Благодаря ви Google: Как да изкопая биткойн в BigQuery на Google

Използване на силата на SQL за добив на криптовалута в облака

Обичам Google BigQuery. Това е управлявана, много мащабируема платформа за данни, което означава, че можете да заявявате огромни количества данни много бързо (например 4 Терабайта за по-малко от 30 секунди!). Редовно го използвам в моите проекти (като испански урок за Google Assistant) и винаги съм удивен от високото представяне.

Миналата седмица организирахме Dev.IL Meetup за Blockchain, технологията зад биткойн и бях поразена от едно от разговорите на Асаф Надлер, обяснявайки механизмите зад технологията, която кара биткойн да цъка (не се колебайте да гледате разговора тук , но честно предупреждение, това е на иврит :-). Когато се прибрах вкъщи след срещата, отворих конзолата си BigQuery с някои аналитични заявки, които написах предишния ден, и получих тази идея: Ами ако мога да използвам BigQuery за добив на биткойн? Възможно ли е дори? Може ли това да е рентабилно? Предвид впечатляващата мащабируемост на BigQuery, изглеждаше, че това може да е добър мач.

(Спойлер: беше! До 25 гига хеша / секунда, безплатно и много забавно! Прочетете, за да разберете как ...)

Бях много заинтригуван и затова реших да го опитам. От известно време се интересувах да експериментирам с технологията Blockchain и това беше чудесна възможност да го направя. Това отне четене на много статии и няколко часа работа, но имах известен успех! (Или по-скоро доказателство за успех на концепцията ;-)

Мислех, че може да е интересно, ако споделя пътуването си с вас в превръщането на машина за анализ на данни в машина за добив на биткойн. Бях доста изненадан от резултатите. и се обзалагам, че и вие ще бъдете!

Но първо първо: нека направим супер бърз преглед на това как работи добивът на биткойн.

Добив на биткойни в ядка

Вероятно сте чували, че извличането на биткойн включва решаване на трудно изчислим математически проблем и това е вярно: Bitcoin системата се състои от транзакции (тоест прехвърляне на пари между потребители) и тези транзакции се регистрират в публична книга, наречена blockchain. Както подсказва името, блокчейнът е свързан списък на блокове с данни за транзакции.

Извличането на биткойни по същество включва намиране на валиден следващ блок, който от своя страна ви дава, миньора, награда - в момента 12.5BTC за всеки блок, който намерите.

Всеки блок се състои от две части: заглавие от 80 байта, последвано от списък на транзакциите. Заглавката включва идентификатора на предишния блок (т.е. хеш на заглавката на предишния блок), както и хеш на SHA256 на списъка с транзакции, както и някаква друга информация. Като миньор, вие трябва по някакъв начин да сте сигурни, че заглавката на блока, когато е хеширана два пъти, използвайки хеш функцията SHA256, е по-малка от дадено число, наричано също "трудност" - или колко е трудно да намерите целта номер (т.е. валиден следващ блок).

Блоковете на биткойн се добиват със скорост около 1 блок на всеки 10 минути. За да се гарантира, че скоростта остава постоянна, трудността се коригира автоматично на всеки блокове през 2016 г. (приблизително на всеки две седмици), така че е повече или по-малко пропорционална на общите изчислители на мощността на изчислителната мощност, въведени в процеса.

Майнингът основно включва опит за различни варианти за заглавката, главно в полето nonce (последните 4 байта на заглавката), докато в крайна сметка не намерите заглавка, чийто хеш започва с определен брой нули (или, казано по-различно - по-малък от някои номер, както казах преди).

Ако искате по-подробно обяснение, можете да разгледате публикацията в блога на Ken Shirriff за добив на биткойн или просто да следвате и да събирате информацията, която споменавам в цялата публикация.

Минно дело с BigQuery

Поканени сте да следвате моите стъпки и да пускате примерите в този блог. Някои от запитванията, представени тук, могат да приличат на части от процеса на добив на биткойн. Ако имате пробен акаунт за безплатна облачна платформа на Google, техните условия ви забраняват да се занимавате с добив на криптовалута. Въпреки че никой от примерите, представени в тази публикация, всъщност няма да изкопая криптовалута, аз все пак ви съветвам да я играете на сигурно място и да имате платен акаунт в Google Cloud Platform, който, доколкото знам, не забранява извличането на криптовалути по никакъв начин.

Първи неща: Първо: Поглед в заглавката на блока

Нека започнем с това как да разгледаме как се извършва добивът на практика. Ще разгледаме заглавката на някакъв блок от биткойн блокчейна и ще се опитаме сами да изчислим хеша, за да видим как е направено (и след това също да проверим дали можем да направим хеширащата част с BigQuery).

Но къде да намерим блок?

Оказва се, можете да намерите целия Blockchain в BigQuery. Но за нашите цели ще използваме различен източник, който също предоставя начин да получим суровите двоични данни на блока: уебсайт, наречен blockchain.info. На случаен принцип избрах един от последните блокове, номер 514868:

Можете да получите двоичните данни за този блок (кодиран шестнадесетичен), като щракнете върху хеша на блока и след това добавите? Format = шестнадесетичен към URL, което води до тази връзка. Блокът показва също пълния списък на транзакциите и други интересни данни и ви каня да проучите сами.

Засега обаче нека се фокусираме върху заглавката. Ще копираме първите 160 знака от данните на блока (това ще бъдат първите 80 байта):

000000204a4ef98461ee26898076e6a2cfc7c764d02b5f8d670832000000000000000000f99f5c4d5025979fcb33d245536a55b628d4564c075c0210cbbc941ad79fdbc5e491b55a494a5117ac997500

Тази страница на Bitcoin Wiki обяснява как работи алгоритъмът за хеширане: ние по принцип трябва да вземем това заглавие и да изпълним функцията SHA256 върху него, след което да го стартираме отново при резултата от първото изпълнение. Това трябва да доведе до хеш на блока.

Така че първо нещо, ако искахме да направим това в BigQuery, ще ни трябва функция SHA256. За щастие, той беше представен във версия 2 на BigQuery (a.k.a. Standard SQ). Имаме нужда и от някакъв метод за преобразуване на тези шестнадесетични стойности в действителни байтове. За щастие функция, наречена FROM_HEX, ни обхвана. Дотук добре.

Сега можем да опитаме да напишем действителната заявка (като един ред):

ИЗБЕРЕТЕ TO_HEX (SHA256 (SHA256 (FROM_HEX) (
"000000204a4ef98461ee26898076e6a2cfc7c764d02b5f8d670832000000000000000000f99f5c4d5025979fcb33d245536a55b628d4564c075c0210cbbc941ad79fdbc5e491b55a494a5117ac997500"))))

(Ако искате да продължите по-нататък, можете да опитате да стартирате тази заявка на конзолата BigQuery. Също така ще трябва да премахнете отметката от опциите за заявка „Използване на наследствен SQL“. Ако получите грешка, която гласи: „Неразпозната функция до_хекс“, тя означава, че не сте премахнали отметката от това поле.)

Изпълнявайки горната заявка, получаваме следния резултат:

Очаквах да видя „000000000000000000082fac02f1bf91ad4e024e6a5a1537936e9d518f827a53“

Когато направих това за първи път, бях доста разочарован, защото изглежда, че хешът е различен от първоначалния хеш на блока. Бързо разбрах обаче, че току-що е обърнато! (Очевидно биткойн хешовете се съхраняват с малко ендиан).

Добавянето на REVERSE функция точно преди да се обадите на TO_HEX направи трика:

ИЗБЕРЕТЕ TO_HEX (НАЗАД (SHA256 (SHA256 (FROM_HEX) (
"000000204a4ef98461ee26898076e6a2cfc7c764d02b5f8d670832000000000000000000f99f5c4d5025979fcb33d245536a55b628d4564c075c0210cbbc941ad79fdbc5e491b55a494a5117ac997500")))))
Сега изглежда законно!

И така, това вече е доста постижение: сега знаем, че можем да проверим хешовете за биткойн блокове в BigQuery. Ако искахме да използваме BigQuery за мина, бихме искали да използваме един и същ набор от функции, с изключение на това, че нямаше да имаме пълната заглавка: ще трябва да търсим валиден заглав с достатъчно малка стойност на хеш.

Валидно заглавие се състои от 6 полета:

  1. версия - стойности от 2, 3 и 4 са общи (добавят се нови стойности)
  2. hashPrevBlock - Хешът на предишния блок във веригата
  3. hashMerkleRoot - Хеш на всички транзакции в блока (обяснител)
  4. време - Времева маркировка кога е създаден блокът
  5. трудност - Трудността на блока, съхраняван като число с плаваща запетая
  6. без значение - 4 байта, които можем да варираме, за да повлияем на стойността на заглавката

Майнингът в общи линии опитва всички възможни комбинации, а след това проверява хеша за всеки, докато хешът не е под целевата стойност. Целевата стойност може лесно да се извлече от трудността на блока:

target = (0xffff * 2 ** (256-48)) / трудност

Минималната трудност за всеки блок е 1. Следователно, всяка цел ще има поне 48 водещи нули. И тъй като номерът на трудността за нашия блок беше 3462542391191.563, целта беше: 000000000000000000514a490000000000000000000000000000000000000000

Можете да намерите текущата трудност и целева стойност тук (връзката също ще ви даде малко повече обяснение за връзката между трудността и целевата стойност).

Така че, по принцип, ако искахме да възпроизведем процеса на майнинг за този блок, просто ще трябва да опитаме всички различни комбинации за не, последните 4 байта на заглавката, докато не намерим една, чийто хеш е по-малък от горния номер.

Преизграждане на блока

Реших да започна с малки, като просто потърся последния байт, тъй като вече имаме отговора в случая. Първоначално мислех да кача малка таблица с всички числа между 0 и 255 (валидните стойности за този байт), но след това установих, че тази таблица всъщност може да бъде имитирана чрез комбиниране на две SQL функции: UNNEST () и GENERATE_ARRAY ():

ИЗБЕРЕТЕ * ОТ НАЙ-ДОБРОТО (GENERATE_ARRAY (0, 255)) число;

Въоръжен с тези знания, създадох първата си заявка, която се опита да възпроизведе процеса на добив в BigQuery:

SELECT
  TO_HEX (CODE_POINTS_TO_BYTES ([0xac, 0x99, 0x75, число]))
ОТ
  UNNEST (GENERATE_ARRAY (0, 255)) число
КЪДЕТО
  TO_HEX (REVERSE (SHA256 (SHA256 (CONCAT (FROM_HEX (
"000000204a4ef98461ee26898076e6a2cfc7c764d02b5f8d670832000000000000000000f99f5c4d5025979fcb33d245536a55b628d4564c075c0210cbbc941ad79fdbc5e491b55a494a5117"), CODE_POINTS_TO_BYTES ([0xac, 0x99, 0x75, бр])))))) като "000000000000000000%"

Добре, нека да го дразним малко!

По принцип, тъй като ние търсим само корекцията, не премахнах тази част от заглавката (можете да проверите - дългия шестнадесетичен низ в заявката е дълъг само 152 знака, което представлява 76 байта, тоест заглавката без nВеднага щом). Това, което търсенето ни търси, е 4-байтова стойност, която веднъж добавена към заглавката, ще доведе до хеш, по-малък от целевия номер.

Тъй като това беше първоначалното ми изпитание, използвах стойностите, които вече знам от блока, за първите 3 байта в нонсенс и бях търсил само BigQuery за крайния байт. Това работи като чар и бързо намери правилната стойност на nonce:

Може би се чудите защо използвах LIKE вътре в клаузата WHERE, за да филтрирам правилния резултат. Правя това, защото просто търсим хешове, по-малки от целевата стойност. Тъй като целевата стойност започва с 18 нули, ние просто филтрираме всяко число, което не започва с 18 нули (тъй като очевидно е по-голямо от целевата стойност). Това означава, че можем да получим някои фалшиви положителни резултати (например число, което започва с 18 нули и след това всяка цифра, по-голяма от 5), но можем бързо да ги филтрираме ръчно.

Така че сега, когато знаем как да търсим подъл нон, можем бързо да разширим търсенето до повече байтове:

SELECT
  TO_HEX (CODE_POINTS_TO_BYTES ([0xac, num2, num3, num4]))
ОТ
  НАЙ-НЕЩО (GENERATE_ARRAY (0, 255)) num2,
  НАЙ-НЕЩО (GENERATE_ARRAY (0, 255)) num3,
  UNNEST (GENERATE_ARRAY (0, 255)) num4
КЪДЕТО
  TO_HEX (REVERSE (SHA256 (SHA256 (CONCAT (FROM_HEX (
"000000204a4ef98461ee26898076e6a2cfc7c764d02b5f8d670832000000000000000000f99f5c4d5025979fcb33d245536a55b628d4564c075c0210cbbc941ad79fdbc5e491b55a494a5117"), CODE_POINTS_TO_BYTES ([0xac, num2, NUM3, num4])))))) като "000000000000000000%"

Докато тази заявка прави трика и намира 3 байта, имаше един проблем: той работи бавно! Завършването на тази заявка отне около 30 секунди. Ако търсехме всичките 4 байта, това ще отнеме около два часа с тази скорост. Споменах по-горе, че нов блок се добива на всеки 10 минути, така че до момента, в който намерим резултат, ние вече бяхме назад и това няма да е от значение.

Това беше малко разочароващо (какво се случи с обещанието на BigQuery за „много мащабируемо“?), Затова реших да проуча по-нататък и да се опитам дали мога да направя нещо, за да подобря това.

От предишния ми опит с BigQuery си спомних, че присъединяването към различни таблици беше скъпа операция, която доведе до това, че заявките се изпълняват много по-дълго. Опитах се да премахна стъпката на присъединяване, като генерирах по-голяма таблица (т.е. казах на GENERATE_ARRAY да генерира повече числа), но след известен опит и грешка, изглежда, че максималният размер на таблицата все пак ще бъде около 1 милион редове, което означава, че аз ' ще трябва да се присъединявате към таблици или да търсите само 1 милион хеша всеки път (което е нещо безсмислено, тъй като процесорът на компютъра ми може да направи това за по-малко от секунда).

Така че изглеждаше, че би било възможно да изкопавам Bitcoin с BigQuery, но едва ли е практично.

Все пак не обичам да се отказвам, затова пробвах още няколко подхода. И един от тях всъщност работи!

Минно дело по-бързо

Спомних си, че BigQuery имаше няколко много големи публични набора от данни, съдържащи огромни таблици. След сканиране на няколко от тях открих един, който съдържаше 5,3 милиарда редове, което е малко над 4,294,967,296, числото на различния брой комбинации. Ако можех да конструирам заявка, която да премине над тази таблица и да изпробвам различна комбинация за всеки ред в таблицата, бих могъл да покрия всички възможни комбинации.

Опитах се да използвам SQL функцията ROW_NUMBER () OVER (), която трябва да върне текущия номер на реда (тогава бих могъл да извлека различен набор от байтове за всеки ред според номера на реда), но той бързо умря с следната грешка:

Ресурсите са надвишени по време на изпълнение на заявката: Заявката не може да бъде изпълнена в заделената памет ..

Кофти! Но тогава започнах да мисля - ами ако просто опитах случайни числа? в крайна сметка има много добър шанс, ако има съвпадение на хеш, ще го намеря, тъй като броят на опитите е по-голям от броя на комбинациите.

И така, аз дойдох с това запитване:

ИЗБЕРЕТЕ TO_HEX (без значение)
ОТ (
  SELECT
    CODE_POINTS_TO_BYTES ([
      CAST (TRUNC (256 * RAND ()) AS INT64),
      CAST (TRUNC (256 * RAND ()) AS INT64),
      CAST (TRUNC (256 * RAND ()) AS INT64),
      CAST (TRUNC (256 * RAND ()) AS INT64)
    ]) КАК НЯМА
  ОТ
    `FH-bigquery.wikipedia.wikipedia_views_201308`
)
КЪДЕТО
  TO_HEX (REVERSE (SHA256 (SHA256 (CONCAT (FROM_HEX (
"000000204a4ef98461ee26898076e6a2cfc7c764d02b5f8d670832000000000000000000f99f5c4d5025979fcb33d245536a55b628d4564c075c0210cbbc941ad79fdbc5e491b55a494a5117"), nВеднага щом))))) като "000000000000000000%"

CAST (TRUNC (256 * RAND ()) AS INT64) генерира произволно число между 0 и 255, а след това вътрешната SELECT част просто генерира таблица с ~ 5.3 милиарда редове от четири случайни стойности. Тогава външната заявка гарантира, че получаваме само стойности, които всъщност водят до достатъчно нисък хеш - това е, което търсим!

За моя приятна изненада, тази заявка се върна само за 20,9 секунди - и наистина намери правилната стойност без знаци (дори я намери два пъти!):

Това означава, че успях да проверя близо 4 милиарда хеша за 20 секунди или около 200 мега-хеша в секунда. Това не е много лошо - като се има предвид, че графичните процесори ще ви получат само около 30–60 мега хеша / секунда (CPU още по-малко, да не говорим за майнинг с молив и хартия).

Така че, очевидно, това ни достига никъде близо до специализиран скоростен хардуерен хардуер, но все пак имаше още един трик, който имах под ръкава си ...

Concurrency

Ако сте стрелец в комбинаториката, може би сте забелязали, че проверката на всички възможни стойности за сега не гарантира много добър шанс да намерите достатъчно малък хеш (както казах в началото, това е труден проблем!).

По време на писането целта беше приблизително 2¹⁸² (вижте как изчисляваме целта от трудността по-горе), което означава, че има 2¹⁸² валидни хеш комбинации от общо 2² възможни, или с други думи - шансът произволен хеш да бъде по-нисък отколкото целта е 1 на 2⁷⁴.

Това всъщност означава, че ако се различаваме само по никакъв начин, ще проверим 2³² възможности и шансът ни действително да намерим желания хеш е супер малък: така че ни трябват повече байтове.

Ако хвърлим друг поглед върху структурата на заглавката, ще забележите, че можем леко да променим полето за времето (други възли все пак ще приемат блока, ако времето е леко изключено) или можем да променим нещо в списъка на транзакциите на нашия блок, което ще доведе до напълно различна стойност за хеш полето на корен на merkle.

Един от начините за промяна на списъка с транзакции (и по този начин генериране на различен хеш хек) е чрез добавяне на допълнителен полезен товар към първата транзакция, наречена extraNonce, която може да има до 100 байта. Друг начин би бил просто да изберете различен набор от транзакции, които да бъдат включени в блока.

Въпросът е, че за да бъдеш успешен миньор, трябва да се пробват различни комбинации от списък на транзакции и / или да се променя часовото поле, в допълнение към нонсе. Това означава, че можем да изпълним заявката, която намерихме многократно, паралелно, всеки път с различна заглавна част на блока. Но колко паралелни заявки позволява BigQuery?

Според страницата с квоти можете да прочетете до 50 едновременни заявки, които теоретично могат да доведат до 50 пъти по-бързо хеширане. Наистина ли работи? Не знам, не съм опитвал. В момента най-добрият ми резултат с една заявка беше 500 мега хеша в секунда (използвайки този набор от данни за 106 милиарда редове уикипедия като изходна таблица), което означава, че с ограничение от 50 едновременни заявки можем теоретично да стигнем до 25 Giga- хешове / секунда. Като умерен специализиран хардуерен хардуер - което не е много лошо, като се има предвид, че е по същество безплатен.

И така, колко струва?

BigQuery твърди, че е рентабилно решение. Досега вече бях убеден с твърдението им за мащабируемост, но тяхната ценова структура също беше изненада за мен. Много добра изненада.

Моделът на ценообразуване за BigQuery се основава единствено на количеството данни, които питате: в общи линии се таксувате от байта. Работата е там, че не таксувате за мощта за обработка или за междинните данни, генерирани от заявката ви - само за байтове, които четете от таблицата на източника.

В нашия случай използваме огромни изходни таблици, но просто ги използваме за генериране на голям брой комбинации от произволни числа - всъщност не четем никакви данни за таблиците. BigQuery има приятна функция, където показва прогнозната цена за заявка (като броя на байтите, за които ще бъдете таксувани). Първият 1 Terabyte е безплатен всеки месец и плащате 5 $ за всеки допълнителен Tetabyte, който запитате след това. Не е зле!

Така че, ако проверим какво оценява BigQuery за нашето голямо запитване, това намираме:

Така че в основата си можем да изпълняваме толкова много заявки, колкото искаме, безплатно! Те със сигурност измерват до претенцията си за ефективност на разходите (в случая поне).

Обобщение и заключения

Това започна като забавно упражнение, с цел да научите по-добре как работи блокчейн. Когато започнах, бях почти сигурен, че ще ударя някакъв твърд лимит (може би някаква квота за време на процесора или нещо подобно) и трябва да кажа, че все още съм доста изненадан, че мога да получа цялата тази изчислителна мощност безплатно.

Въпреки че моите изследвания показват, че би трябвало да е възможно да превърна BigQuery в машина за добив на теория, все още има значително количество работа, която би трябвало да се свърши, ако искам да автоматизирам процеса на добив. Грубо изчисление показва, че дори и да се изгради такъв миньор, когато се състезаваше с целия специализиран рудник за минно дело, който ще работи по света, това би ми изкарало приблизително 5 долара годишно. Въпреки това, сега знам, че биткойн може да се добива със SQL, което е безценно ;-)

Но най-важното е, че този експеримент ми даде изцяло нова гледна точка за огромната изчислителна мощ, която се изразходва за добив на биткойн. Говорим за 26 милиарда гига-хешове в секунда - това е 2,6 * 10¹⁹, което е повече от броя на пясъчните зърна на земята. Всяка секунда. Трябва да се чудите какво можем да постигнем, ако вместо това използваме дори част от тази изчислителна способност за медицински изследвания ...