Препарируем Blockchain

Написано

в

Блокчейн используется не только в криптовалютах, а также в тех областях где нужно сопровождать доказанную последовательность определенных событий или состояний. Об этом буквально и говорит само слово blockchain: цепочка блоков. Таким образом, с системной точки зрения мы имеем блоки и связи между ними. Наша задача — препарировать блок таким образом, чтобы установить наличие данной связи (проверка блокчейна). Или создать новую связь — это уже майнинг, слово приводящее в трепет публику которая вращается в мире криптовалют.

Поработаем с блоками и связями такой популярной криптовалюты, как BitCoin.

Блоки и связи / Block & Chains

Типичный блок выглядит следующим образом:

{
  "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.

Вы наверное обратили внимание на то, что хэши имеют вначале большое количество нулей, что вообще-то говоря очень странно для хэш-функции, которая должна выглядеть как стопроцентно случайная последовательность. В этом и есть соль майнинга: блок получает право возглавить блокчейн только в том случае, если его хеш будет иметь в начале определенное количество нулей. В этом и заключается вычислительная трудность, на которую тратят время майнеры.

Заканчиваем с лирическими отступлениями и приступаем к парсингу. Блок будем брать прямо с сайта по его хэшу:

# 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).

Поскольку изобретатели алгоритма решили работать с данными справа налево, в режиме реверса последовательности байтов в строке, я написал вспомогательную функцию которая это делает:

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)

Переформатируем все переменные, которые будут принимать участие в вычислении хэша:

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')

Теперь все это нужно смешать в одну кучу

header=ve+pb+mr+ti+bi+no

и найти значение хэша нашего блока:

header_bin = header.decode('hex')
pass1=sha256(header_bin).digest()
new_block=sha256(pass1).digest()[::-1].encode('hex')

Не забываем, что в этой операции участвовал хэш предыдущего блока prev_block. В результате получаем заветную строчку со многими нулями вначале — это и есть хэш нашего блока, все соответствует.

Полезная нагрузка: транзакции

За повествованием мы забыли про полезное наполнение блока, ради чего все собственно затевалось: про транзацкции, кто кому сколько перечислил. Транзакций в блоке великое множество, они проходят под ключом tx. Gосмотрим только одну из, например самую верхнюю n=0:

print(json.dumps(data['tx'][n], ensure_ascii=False, sort_keys=True, indent=4, separators=(',', ': ')))

Информацию дает строчка json.dumps(data[‘tx’][n]), остальное — красивости для удобочитаемого вывода. В результате увидим:

{
    "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 кому-то. Именно так: кошельки отправителя и получателя представлены в виде хэшей, и кто это такие — мы не знаем. Собственно это и есть краеугольный камень БитКойна.

Блокчейн хранит абсолютно все транзакции, которые были в системе. По этой причине совершенно четко видно, как появляются монеты и к кому они переходят, и какое общее количество их находится в системе.

На этом завершим наш небольшой анализ, ведь главное мы уже сделали: показали как вагончики сцепляются друг с другом и уникальность этой сцепки.

Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *