Блокчейн используется не только в криптовалютах, а также в тех областях где нужно сопровождать доказанную последовательность определенных событий или состояний. Об этом буквально и говорит само слово blockchain: цепочка блоков. Таким образом, с системной точки зрения мы имеем блоки и связи между ними. Наша задача — препарировать блок таким образом, чтобы установить наличие данной связи (проверка блокчейна). Или создать новую связь — это уже майнинг, слово приводящее в трепет публику которая вращается в мире криптовалют.
Поработаем с блоками и связями такой популярной криптовалюты, как BitCoin.
Блоки и связи / Block & Chains
Типичный блок выглядит следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
{ "hash":"0000000000000bae09a7a393a8acded75aa67e46cb81f7acaa5ad94f9eacd103", "ver":1, "prev_block":"00000000000007d0f98d9edca880a6c124e25095712df8952e0439ac7409738a", "mrkl_root":"935aa0ed2e29a4b81e0c995c39e06995ecce7ddbebb26ed32d550a72e8200bf5", "time":1322131230, "bits":437129626, "nonce":2964215930, "n_tx":22, "size":9195, "block_index":818044, "main_chain":true, "height":154595, "received_time":1322131301, "relayed_by":"108.60.208.156", "tx":[--Array of Transactions--] { |
Блоки хорошо представлять в виде вагончиков, которые сцепляются друг с другом. Эта аналогия обладает еще одним ценным соответствием: самый первый вагончик, то есть паровозик, не сцеплен спереди ни с кем. Это самый верхний блок в системе, и он будет самым верхним до тех пор, пока кто-нибудь не намайнит следующий блок. Тогда новый блок становится паровозиком во главе состава и к нему подцепляется остальная цепочка.
Все! В блокчейне больше ничего нет, кроме сцепленных вагончиков — блоков. Теперь нужно понять, что выполняет роль этой сцепки. Вот тут в этом месте наше сравнение с вагончиками не сработает, потому что в пассажирском или грузовом составе каждый вагон может быть сцеплен с любым другим вагоном или локомотивом, а в случае блокчейна для того чтобы вагончики сцепились, их сцепной механизм должен иметь одинаковый код.
В блоках роль этого кода выполняют значения ключей hash и prev_block. В блокчейне значение prev_block каждого блока в точности совпадает со значением hash предыдущего блока, и аналогично hash каждого блока совпадает со значением prev_block последующего блока. При этом при вычислении значения hash используется хэш предыдущего блока, что делает цепочку хешей зависимой друг от друга.
Поскольку hash — это фактически хэш блока, то такой механизм обеспечивает не только однозначность последовательности блоков, но и защиту содержимого блока. Попытка изменить последовательность блоков, удалить блоки или вставить новые, а также поменять содержимое самих блоков ни к чему не приведет: нарушится соответствие последовательности хэшей. Поэтому с блокчейном ничего сделать нелья: можно только добавить новый блок спереди. Это уже майнинг!
Block Parsing
Распарсим один из блоков блокчейна. Я взял для примера блок #528340, хэш которого имеет значение
0000000000000000002740c2167e7dcea59e11362587ea9ed348022701f5a73d.
Вы наверное обратили внимание на то, что хэши имеют вначале большое количество нулей, что вообще-то говоря очень странно для хэш-функции, которая должна выглядеть как стопроцентно случайная последовательность. В этом и есть соль майнинга: блок получает право возглавить блокчейн только в том случае, если его хеш будет иметь в начале определенное количество нулей. В этом и заключается вычислительная трудность, на которую тратят время майнеры.
Заканчиваем с лирическими отступлениями и приступаем к парсингу. Блок будем брать прямо с сайта по его хэшу:
1 2 3 4 5 6 7 8 |
# block #528340 hash block_hash = '0000000000000000002740c2167e7dcea59e11362587ea9ed348022701f5a73d' url = 'https://blockchain.info/rawblock/%s' r = requests.get(url % block_hash) print(r.status_code) data = json.loads(r.text) |
Если код статуса равен 200, значит на запрос получен ответ с нужными данными в формате JSON, которые преобразуются в словарь с парами ключ — значение (переменная data).
Поскольку изобретатели алгоритма решили работать с данными справа налево, в режиме реверса последовательности байтов в строке, я написал вспомогательную функцию которая это делает:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
def sformat(data, arg): s = data[arg] l = list(s) i=0 for ch in reversed(s): if i%2 != 0: l[i-1]=ch l[i]=ch0 else: ch0=ch i+=1 ret = ''.join(l) print('%s: %s' % (arg,ret)) return(ret) |
Переформатируем все переменные, которые будут принимать участие в вычислении хэша:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
bl=sformat(data, 'hash') pb=sformat(data, 'prev_block') mr=sformat(data, 'mrkl_root') # new dict to unify call function sformat() data2 = {'ver':'','time':'','bits':'','nonce':''} data2['ver']='%x' % data['ver'] ve=sformat(data2, 'ver') data2['time']='%x' % data['time'] ti=sformat(data2, 'time') data2['bits']='%x' % data['bits'] bi=sformat(data2, 'bits') data2['nonce']='%x' % data['nonce'] no=sformat(data2, 'nonce') |
Теперь все это нужно смешать в одну кучу
1 |
header=ve+pb+mr+ti+bi+no |
и найти значение хэша нашего блока:
1 2 3 |
header_bin = header.decode('hex') pass1=sha256(header_bin).digest() new_block=sha256(pass1).digest()[::-1].encode('hex') |
Не забываем, что в этой операции участвовал хэш предыдущего блока prev_block. В результате получаем заветную строчку со многими нулями вначале — это и есть хэш нашего блока, все соответствует.
Полезная нагрузка: транзакции
За повествованием мы забыли про полезное наполнение блока, ради чего все собственно затевалось: про транзацкции, кто кому сколько перечислил. Транзакций в блоке великое множество, они проходят под ключом tx. Gосмотрим только одну из, например самую верхнюю n=0:
1 |
print(json.dumps(data['tx'][n], ensure_ascii=False, sort_keys=True, indent=4, separators=(',', ': '))) |
Информацию дает строчка json.dumps(data[‘tx’][n]), остальное — красивости для удобочитаемого вывода. В результате увидим:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
{ "hash": "eaa674093137d79c025f2a86660c25af3c9ba492542c0f92daa5b58c1e5a15b9", "inputs": [ { "script": "03d40f08046d1b2a5b2f4254504f4f4c2ffabe6d6d4a6c034e5c60e2b4ff7ae75f8a2160ece386d768b82cf03f745694e8b2a911d9010000000000000002019a460996010000000000", "sequence": 4294967295, "witness": "01200000000000000000000000000000000000000000000000000000000000000000" } ], "lock_time": 0, "out": [ { "addr": "1MsEbXvTs8mHy5vS9hgyMPq5Xx4umiuYrX", "n": 0, "script": "76a914e4e2a9a247f46c81fa2cc33523ccfac977a370c588ac", "spent": false, "tx_index": 355525333, "type": 0, "value": 1430000492 }, { "n": 1, "script": "6a24aa21a9edb744306372d97b47e5f81708eb8a2e3ef1790c0a128345008e2eb299400e68b1", "spent": false, "tx_index": 355525333, "type": 0, "value": 0 } ], "relayed_by": "0.0.0.0", "size": 241, "time": 1529486188, "tx_index": 355525333, "ver": 1, "vin_sz": 1, "vout_sz": 2, "weight": 856 } |
Видно, что некто перечислил 14.3 BTC кому-то. Именно так: кошельки отправителя и получателя представлены в виде хэшей, и кто это такие — мы не знаем. Собственно это и есть краеугольный камень БитКойна.
Блокчейн хранит абсолютно все транзакции, которые были в системе. По этой причине совершенно четко видно, как появляются монеты и к кому они переходят, и какое общее количество их находится в системе.
На этом завершим наш небольшой анализ, ведь главное мы уже сделали: показали как вагончики сцепляются друг с другом и уникальность этой сцепки.
Ответить