# API part 4

Автор: [@asuleymanov](https://golos.id/@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': 'author',
    'permlink': 'permlink',
    'voter': 'voter',
    'weight': weight}]
```

Тут можно четко определить:

* тип операции `vote`(голосование)
* собственно саму публикацию за которую производиться голосование (параметры `author` и `permlink`)
* кто голосует (параметр `voter`)
* и соответственно вес голоса который он готов отдать (параметр `weight`)

### Транзакция

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

```
trx = {'ref_block_num': 36029,
    'ref_block_prefix': 1164960351,
    'expiration': '2017-06-13T12:24:17',
    'operations': [['vote',
                     {'author': 'xeroc',
                      'permlink': 'piston',
                      'voter': 'xeroc',
                      'weight': 10000}]],
    'extensions': [],
    'signatures': [],
}
```

Можно заметить что наша операция является частью массива`operations`.\
Давайте разберем те поля в транзакции которые у нас появились, но нам неизвестны.

* Параметр

  `ref_block_num`

  указывает на номер предыдущего блока.
* Параметр

  `ref_block_prefix`

  получаем из идентификатора блока, а именно последние 4 байта.
* Параметр

  `expiration`

  проставляет время истечения действия транзакции. Если до этого времени не произошло включение транзакции в блок, то она считается недействительной.
* Параметр

  `extensions`

  расширение.
* Параметр

  `signatures`

  собственно подпись. Данный параметр проставляется в момент подписания.

Примет получения параметров`ref_block_num`и`ref_block_prefix`на python:

```
DGP = get_dynamic_global_properties()
ref_block_num = DGP["head_block_number"] 
&0xFFFF
ref_block_prefix = struct.unpack_from("<I", unhexlify(DGP["head_block_id"]), 4)[0]
```

Цель этих двух параметров для предотвращения повторения запросов.

### Сериализация (приведение к жесткой структуре)

Прежде чем переходить непосредственно к процессу подписи. Надо сериализовать (привести к жесткой структуре) транзакцию. Это необходимо чтобы на стороне ноды было проще проверить правильность подписания транзакции. Т.е. после такой обработки мы будем точно знать порядок в каком располагались поля при подписании.\
При этом из процесса сериализации исключается параметр`signatures`и`extensions`.

И так на примере нашей транзакции порядок будет таким (Все примеры будут на языке python):

1. Создание буфера

   `buf = b""`
2. Добавление в буфер параметра

   `ref_block_num`

   `buf += struct.pack("<H", trx["ref_block_num"])`
3. Добавление в буфер параметра

   `ref_block_prefix`

   `buf += struct.pack("<I", trx["ref_block_prefix"])`
4. Добавление в буфер параметра

   `expiration`

   . Сам параметр при этом преобразуется в целое число (uint32)

```
timeformat = '%Y-%m-%dT%H:%M:%S%Z'
buf += struct.pack("<I", timegm(time.strptime((trx["expiration"] + "UTC"), timeformat)))
```

1. Добавление в буфер количество операций в нашей транзакции

   `buf += bytes(varint(len(trx["operations"])))`
2. Добавление непосредственно полей операции. Первое что добавляем это код операции. Он определяется из массива начинающегося с 0.

   **Список операций из исходников**

```
                vote_operation,
                comment_operation,
                transfer_operation,
                transfer_to_vesting_operation,
                withdraw_vesting_operation,
                limit_order_create_operation,
                limit_order_cancel_operation,
                feed_publish_operation,
                convert_operation,
                account_create_operation,
                account_update_operation,
                witness_update_operation,
                account_witness_vote_operation,
                account_witness_proxy_operation,
                pow_operation,
                custom_operation,
                report_over_production_operation,
                delete_comment_operation,
                custom_json_operation,
                comment_options_operation,
                set_withdraw_vesting_route_operation,
                limit_order_create2_operation,
                challenge_authority_operation,
                prove_authority_operation,
                request_account_recovery_operation,
                recover_account_operation,
                change_recovery_account_operation,
                escrow_transfer_operation,
                escrow_dispute_operation,
                escrow_release_operation,
                pow2_operation,
                escrow_approve_operation,
                transfer_to_savings_operation,
                transfer_from_savings_operation,
                cancel_transfer_from_savings_operation,
                custom_binary_operation,
                decline_voting_rights_operation,
                reset_account_operation,
                set_reset_account_operation,

                /// virtual operations below this point
                fill_convert_request_operation,
                author_reward_operation,
                curation_reward_operation,
                comment_reward_operation,
                liquidity_reward_operation,
                interest_operation,
                fill_vesting_withdraw_operation,
                fill_order_operation,
                shutdown_witness_operation,
                fill_transfer_from_savings_operation,
                hardfork_operation,
                comment_payout_update_operation
```

Далее идут поля непосредственно из операции в определенном порядке. На данный момент я смог выявить порядок только для 3-х операций.

> Операция`vote`
>
> ```
> (voter)
> (author)
> (permlink)
> (weight)
> ```
>
> Операция`comment`(эта одна операция но может выполнять два действия. Непосредственно комментировать и создавать новую публикацию)
>
> ```
> (parent_author) // если этот параметр пустой то это считается созданием новой публикации
> (parent_permlink)
> (author)
> (permlink)
> (title)
> (body)
> (json_metadata)
> ```

В нашем случае это выглядит в таком виде :

```
if op[0] == "vote":
    opdata = op[1]
    buf += (varint(len(opdata["voter"])) + bytes(opdata["voter"], "utf-8"))
    buf += (varint(len(opdata["author"])) + bytes(opdata["author"], "utf-8"))
    buf += (varint(len(opdata["permlink"])) + bytes(opdata["permlink"], "utf-8"))
    buf += struct.pack("<h", int(opdata["weight"]))
```

#### Результат

Приблизительный разбор результата:\
Во- первых , параметр 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 хэш, так называемый дайджест сообщения. Получить его можно таким способом:

```
message = unhexlify(chainid) + buf
digest = hashlib.sha256(message).digest()
```

Теперь мы используя секретные ключи подписываем нашу транзакцию. Каждый секретный ключ приведет к одной подписи , которая должна быть добавлена в параметр signatures первоначальной транзакции.\
В нашем случае, мы просто работаем с одним закрытым ключом, представленным в WIF. Получим фактический двоичный закрытый ключ от WIF (для простоты используется класс PrivateKey от steembase.account)

```
wifs = ["5JLw5dgQAx6rhZEgNN5C2ds1V47RweGshynFSWFbaMohsYsBvE8"]
sigs = []
for wif in wifs:
    p = bytes(PrivateKey(wif))  # binary representation of private key
    sk = ecdsa.SigningKey.from_string(p, curve=ecdsa.SECP256k1)
```

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

```
cnt = 0
i = 0
while 1 :
    cnt += 1
    # Deterministic k
    #
    k = ecdsa.rfc6979.generate_k(
        sk.curve.generator.order(),
        sk.privkey.secret_multiplier,
        hashlib.sha256,
        hashlib.sha256(digest + bytes([cnt])).digest())
```

Подпись генерируется с помощью соответствующего вызова подписи ECDSA:

```
# Sign message

sigder = sk.sign_digest(
    digest,
    sigencode=ecdsa.util.sigencode_der,
    k=k)
```

Теперь создаем подпись,и проверяем является ли она канонической. Если это так, то мы разрываем цикл и продолжаем:

```
# Reformating of signature

r, s = ecdsa.util.sigdecode_der(sigder, sk.curve.generator.order())
signature = ecdsa.util.sigencode_string(r, s, sk.curve.generator.order())

# Make sure signature is canonical!

lenR = sigder[3]
lenS = sigder[5 + lenR]
if lenR is 32 and lenS is 32 :
    # ........
```

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

```
# Derive the recovery parameter

i = recoverPubkeyParameter(digest, signature, sk.get_verifying_key())
i += 4   \# compressed
i += 27  \# compact
break
```

После получения канонической подписи, мы форматируем её в шестнадцатеричном представлении и добавляем к нашей транзакции в параметр`signatures`. Этот вид подписи , называется компактной подписью.

```
trx["signatures"].append(
    hexlify(
        struct.pack("<B", i) +
        signature
    ).decode("ascii")
)
```

### Отправка

После довольно сложной процедуры подписания, отправка кажется элементарным действием.\
Для того чтобы быть уверенными что проблем при отправке нету лучше всего использовать команду`broadcast_transaction_synchronous`(так как при её использовании система возвращает ответ), а параметр для команды будет являться непосредственно`trx`.\
В ответе будет вот такая структура:

```
    ID       string `json:"id"`
    BlockNum uint32 `json:"block_num"`
    TrxNum   uint32 `json:"trx_num"`
    Expired  bool   `json:"expired"`
```

Этим постом я завершаю цикл по разбору GOLOS API

**Историческая справка**

* [Статья № 1](https://golos.id/ru--otkrytyij-kod/@asuleymanov/opisanie-api-golos-chast-1)
  * Начало разбора команд из раздела Database\_Api
* [Статья № 2](https://golos.id/ru--otkrytyij-kod/@asuleymanov/opisani-golosapi-chast-2)
  * Окончание разбора команд из раздела Database\_Api
* [Статья № 3](https://golos.id/ru--otkrytyij-kod/@asuleymanov/opisanie-golosapi-chast-3)
  * Разбор команд из разделов Market\_History\_API и Follow\_API


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://wiki.golos.id/developers/api/api-golos-ch4.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
