API part 4
Автор: @asuleymanov
В предыдущей части описания я обещал приоткрыть завесу тайны над тем как именно можно самому добавлять записи в блокчейн. Собственно я опишу команды из раздела Network_Brodcast_API и Login_API. И попробую рассказать о процедуре генерации транзакции, её подписи и отправки в блокчейн.
Команды раздела Network_Brodcast_API
broadcast_block
Параметры:"method":"broadcast_block", "params":["signed_block"], "id":0
Описание: Скорее всего данная команда должна загружать в блокчейн собранный и подписанный блок. Но проверить его мне не удалось.
broadcast_transaction
Параметры:"method":"broadcast_transaction", "params":["trx"], "id":1
Описание: Транзакция будет проверяться на достоверность в локальной базе данных до начала трансляции. Если он не может применяться локально, будет вызвана ошибка, и транзакция не будет транслироваться.
broadcast_transaction_synchronous
Параметры:"method":"broadcast_transaction_synchronous", "params":["trx"], "id":2
Описание: Этот вызов не будет возвращен до тех пор, пока транзакция не будет включена в блок.
broadcast_transaction_with_callback
Параметры:"method":"broadcast_transaction_with_callback", "params":["confirmationCallback","trx"], "id":3
Описание: Эта версия широковещательной транзакции регистрирует метод обратного вызова, который будет вызываться, когда транзакция включена в блок. Метод обратного вызова включает идентификатор транзакции, номер блока и номер транзакции в блоке.
Команды раздела Login_API
login
Параметры:"method":"login", "params":["username","password"], "id":0
Описание: Позволяет подключаться к учетным записям в сети GOLOS.
get_api_by_name
Параметры:"method":"get_api_by_name", "params":["apiname"], "id":1
Описание: Возвращает уникальный идентификатор API по его имени.Пример идентификатора "login_api" или "follow_api".
get_version
Параметры:"method":"get_version", "params":[], "id":2
Описание: Возвращает данные о версии компонентов блокчейн
Процедура подписания и отправки
Для примера я возьму самую простую операцию голосования. С её помощью я разберу как происходит генерация транзакции и её подписание.
Операция
Первым шагом мы создаем операцию. Если посмотреть на саму операцию то она имеет вид
Тут можно четко определить:
тип операции
vote
(голосование)собственно саму публикацию за которую производиться голосование (параметры
author
иpermlink
)кто голосует (параметр
voter
)и соответственно вес голоса который он готов отдать (параметр
weight
)
Транзакция
Следующим шагом мы создаем непосредственно транзакцию с необходимой нам операцией. Транзакция может содержать от одной до нескольких операций. В нашем случае это 1 операция голосования и сгенерированная транзакция выглядит так:
Можно заметить что наша операция является частью массиваoperations
.
Давайте разберем те поля в транзакции которые у нас появились, но нам неизвестны.
Параметр
ref_block_num
указывает на номер предыдущего блока.
Параметр
ref_block_prefix
получаем из идентификатора блока, а именно последние 4 байта.
Параметр
expiration
проставляет время истечения действия транзакции. Если до этого времени не произошло включение транзакции в блок, то она считается недействительной.
Параметр
extensions
расширение.
Параметр
signatures
собственно подпись. Данный параметр проставляется в момент подписания.
Примет получения параметровref_block_num
иref_block_prefix
на python:
Цель этих двух параметров для предотвращения повторения запросов.
Сериализация (приведение к жесткой структуре)
Прежде чем переходить непосредственно к процессу подписи. Надо сериализовать (привести к жесткой структуре) транзакцию. Это необходимо чтобы на стороне ноды было проще проверить правильность подписания транзакции. Т.е. после такой обработки мы будем точно знать порядок в каком располагались поля при подписании.
При этом из процесса сериализации исключается параметрsignatures
иextensions
.
И так на примере нашей транзакции порядок будет таким (Все примеры будут на языке python):
Создание буфера
buf = b""
Добавление в буфер параметра
ref_block_num
buf += struct.pack("<H", trx["ref_block_num"])
Добавление в буфер параметра
ref_block_prefix
buf += struct.pack("<I", trx["ref_block_prefix"])
Добавление в буфер параметра
expiration
. Сам параметр при этом преобразуется в целое число (uint32)
Добавление в буфер количество операций в нашей транзакции
buf += bytes(varint(len(trx["operations"])))
Добавление непосредственно полей операции. Первое что добавляем это код операции. Он определяется из массива начинающегося с 0.
Список операций из исходников
Далее идут поля непосредственно из операции в определенном порядке. На данный момент я смог выявить порядок только для 3-х операций.
Операция
vote
Операция
comment
(эта одна операция но может выполнять два действия. Непосредственно комментировать и создавать новую публикацию)
В нашем случае это выглядит в таком виде :
Результат
Приблизительный разбор результата:
Во- первых , параметр ref_block_num( 36029)
bd8c..............................................................
и параметр ref_block_prefix( 1164960351)
....5fe26f45......................................................
Затем мы добавим время истечения транзакцииexpiration
2016-08-08T12:24:17
............f179a857..............................................
После этого нам нужно добавить количество операций (01)
....................01............................................
И непосредственно саму операцию:
Идентификатор операции (00)
......................00..........................................
Голосующийvoter
........................057865726f63..............................
Автор публикацииauthor
....................................057865726f63..................
Ссылка на публикациюpermlink
................................................06706973746f6e....
и вес голоса10000
..............................................................1027
Результат сериализации предстает приблизительно в таком виде:
bd8c5fe26f45f179a8570100057865726f63057865726f6306706973746f6e1027
Подписание
И вот самая тяжелая часть всего процесса. Для меня это пока тоже темный лес, но попробую описать хоть как то. Для подписания транзакции нам понадобится:
Сериализованный буфер
Идентификатор цепи chainid (STEEM:
0000000000000000000000000000000000000000000000000000000000000000
; GOLOS
782a3039b478c839e4cb0c941ff4eaeb7df40bdd68bd441afd444b9da763de12
)
Секретный ключ (в нашем случае posting key)
Вместо того чтобы подписывать непосредственно саму транзакцию мы подпишем 256SHA хэш, так называемый дайджест сообщения. Получить его можно таким способом:
Теперь мы используя секретные ключи подписываем нашу транзакцию. Каждый секретный ключ приведет к одной подписи , которая должна быть добавлена в параметр signatures первоначальной транзакции. В нашем случае, мы просто работаем с одним закрытым ключом, представленным в WIF. Получим фактический двоичный закрытый ключ от WIF (для простоты используется класс PrivateKey от steembase.account)
Теперь реализовываем цикл , который имеет решающее значение , поскольку система принимает только канонические подписи , и мы не имеем никакого способа узнать является ли подпись канонической. При этом мы получаем детерминированный параметр для подписания ECDSA и при создании этого параметра будем добавлять наш счетчик цикла в дайджест перед хэшированием.
Подпись генерируется с помощью соответствующего вызова подписи ECDSA:
Теперь создаем подпись,и проверяем является ли она канонической. Если это так, то мы разрываем цикл и продолжаем:
После того, как мы обеспечили каноничность, мы добавляем дополнительные параметры. Это упрощает проверку подписи , так как она привязывает подпись к одному уникальному открытому ключу. Без этих параметров системе необходимо будет проверить несколько открытых ключей вместо одного. Мы добавляем 4 и 27 , чтобы осталась совместимость с другими протоколами и получаем нашу подпись.
После получения канонической подписи, мы форматируем её в шестнадцатеричном представлении и добавляем к нашей транзакции в параметрsignatures
. Этот вид подписи , называется компактной подписью.
Отправка
После довольно сложной процедуры подписания, отправка кажется элементарным действием.
Для того чтобы быть уверенными что проблем при отправке нету лучше всего использовать командуbroadcast_transaction_synchronous
(так как при её использовании система возвращает ответ), а параметр для команды будет являться непосредственноtrx
.
В ответе будет вот такая структура:
Этим постом я завершаю цикл по разбору GOLOS API
Историческая справка
Начало разбора команд из раздела Database_Api
Окончание разбора команд из раздела Database_Api
Разбор команд из разделов Market_History_API и Follow_API
Last updated