Уважаемые пользователи Голос!
Сайт доступен в режиме «чтение» до сентября 2020 года. Операции с токенами Golos, Cyber можно проводить, используя альтернативные клиенты или через эксплорер Cyberway. Подробности здесь: https://golos.io/@goloscore/operacii-s-tokenami-golos-cyber-1594822432061
С уважением, команда “Голос”
GOLOS
RU
EN
UA
vik
6 лет назад

🎓 Golos JS - Формирование транзакций с несколькими операциями соблюдая TaPoS, вычисление ref_block_prefix в браузере без NodeJS, сервера и модуля Buffer

Недавно я обновил форму постинга golos.cf/md и добавил в нее возможность добавлять бенефициаров и другие comment_options

В библиотеке golos-js есть два отдельных метода, один для размещения самого поста golos.broadcast.comment(), второй для настройки опций поста, в т.ч. бенефициаров golos.broadcast.commentOptions()

Такое разделение имеет ряд минусов: во-первых - нужно делать две транзакции, во-вторых - если после размещения поста чей-то бот проголосует за пост перед тем как долетит вторая транзакция с опциями поста - опции по-просту не примутся из-за того, что с постом уже есть взаимодействия.
Очевидное решение - это упаковать обе операции в одну транзакцию. Я уже описывал принцип ранее: 🎓 Экономим ресурсы совмещая 100 операций в одну транзакцию , но описанный скрипт работает только в nodeJS
А форма golos.cf/md предполагает возможность простого копирования html и работы локально на вашем устройстве, стало быть nodejs там нет и нужно работать с обычным JavaScript

Проблема возникла только с вычислением ref_block_num и ref_block_prefix - это данные предыдущих блоков которые должны содержаться в каждой транзакции на голосе. Каждая транзакция должна ссылаться на предыдущий блок и создавать целостность цепи.

Подробно можно прочесть тут: Transactions as Proof of Stake - TaPoS

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

Но на самом деле, привязка к последнему (предыдущему) блоку не вполне безопасна, поскольку если транзакция попадет в микрофорк cможет быть отброшена главной цепочкой.
Привязка к последнему неизменяемому блоку last_irreversible_block_num тоже влечет ряд уязвимостей для свежей транзакции ну и линковка на такое количество блоков назад кажется противоречива алгоритму.

Примечательно, что golos-js использует -3 блока от последнего head_block_number в то время как steem-js использует last_irreversible_block_num-1 линковка почти на 20 блоков старее.
Мне больше нравится подход golos, однако я буду рад услышать аргументы, так как у самого нет компетенции и представления теоретические.

Получение предварительных данных (номер и хеш блока)

Первым запросом будет golos.api.getDynamicGlobalProperties

В ответе нам нужен номер последнего блока head_block_number (только мы отнимем от него еще - 2 блока)

Пример ответа:

head_block_number: 15523551

Далее нам нужен хеш блока 15523551 - 3 = 15523548 для вычисления ref_block_prefix но запрос мы сделаем не минус - 3 от последнего блока, а минус 2 или блок 15523549

Запрос golos.api.getBlockHeader(15523549)

Пример ответа:

previous: '00ecdedcfd8699a358c4595f0233db938d6d5705'

Обратите внимание на previous (пер. предыдущий) - это хеш блока 15523548, он как раз и есть в данном случае head_block_number минус 3.

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

Блок 15523548

Хеш 00ecdedcfd8699a358c4595f0233db938d6d5705

Теперь из этих двух переменных нам нужно получить две другие - ref_block_num и ref_block_prefix

ref_block_num получается очень легко в обоих случаях, работает и в nodeJS и в браузере:

var refblocknum = 15523548 & 0xFFFF;

Теперь переменная refblocknum равна 57052, при построении транзакции мы поместим этот номер в параметр ref_block_num

С ref_block_prefix было все несколько сложнее, признаться - я долгое время не знал как обойтись без модуля Buffer в браузере, но оказалось все очень просто. Сначала пример для nodeJS который можно найти в golos-js библиотеке и просто скопировать. Выглядит примерно так:

Как вычислить ref_block_num & ref_block_prefix в NodeJS + модуль Buffer

const BlockPrefix = new Buffer('00ecdedcfd8699a358c4595f0233db938d6d5705', 'hex').readUInt32LE(4);

Теперь в переменной BlockPrefix число 2744747773 его и можно ставить в ref_block_prefix

Теперь транзакция содержит эти важные данные

"ref_block_num":57052,
"ref_block_prefix":2744747773

Как вычислить ref_block_num & ref_block_prefix в браузере простым JS

Проблема в том, что модуль Buffer работает только в NodeJS и пришлось потратить некоторое время, чтобы выполнить обычным JS то, что выполняет этот модуль.

В nodeJS функция new Buffer('00ecdedcfd8699a358c4595f0233db938d6d5705', 'hex') возвратит ответ вида:

<Buffer 00 ec de dc fd 86 99 a3 58 c4 59 5f 02 33 db 93 8d 6d 57 05>

А .readUInt32LE(4) выделит определенные байты из этого массива и переведет их в нужное число.

Сделать это простым JS удалось так:

var blockid = '00ecdedcfd8699a358c4595f0233db938d6d5705';
            n = [];
            for (var i = 0; i < blockid.length; i += 2) {
                n.push(blockid.substr(i, 2));
            }

Выше был создан массив байт похожий на содержания буфера из примера про nodeJS

И теперь в переменной n содержание вида:

[ '00', 'ec', 'de', 'dc', 'fd', '86', '99', 'a3', '58', 'c4', '59', '5f', '02', '33', 'db', '93', '8d', '6d', '57', '05' ]

Из nodeJS нам ясно, что в этой строке нужно как-то найти число 2744747773

И спрятано оно вот в этом куске ... 'fd', '86', '99', 'a3' ... это 4,5,6 и 7 индекс массива байт.

Теперь эти 4 байта нужно инвертировать наоборот вот так a3 99 86 fd

Например просто наполнив переменную:

var hex = n[7] + n[6] + n[5] + n[4];

Теперь в переменной hex у нас значение a39986fd и чтобы оно превратилось в искомое число 2744747773 нужно просто конвертировать hex в число:

var refBlockPrefix = parseInt(hex, 16)

Теперь refBlockPrefix содержит нужные данные для ref_block_prefix и автоматическое формирование транзакции с множеством операций возможно в обычном браузере, на html странице без применения сервера.

Вот так выглядит код:

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

Живой пример скрипта работает на странице https://golos.cf/md где в одной транзакции совмещены две операции: комментарий и опции комментария.

15
108.980 GOLOS
На Golos с January 2017
Комментарии (7)
Сортировать по:
Сначала старые