Недавно я обновил форму постинга 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 где в одной транзакции совмещены две операции: комментарий и опции комментария.
Все правильно и хорошо. Я поддерживаю.
Но боюсь есть маленькая загвоздка. Если вдруг кто-то попробует включить в транзакцию операции которые требуют разные подписи. То боюсь при этом его ждет фиаско.
Вовсе нет.
В посте есть ссылка на мой прошлый пост, там описано решение.
Вы либо подписываете аккаунт несколькими ключами либо используете мультисиг и подписываете операции разных пользователей своим ключом.
С вопросом "к какому блоку линковаться" думаю всё довольно просто. Если отправляешь транзакцию, которая "сама по себе", то линковать к last_irreversible (или чуть раньше).
А линковка к свежим блокам реализует следующую логику: "считать эту транзакцию действительной, только если такой-то блок попал в цепь". Элементарный use-case: мы "слушаем" блокчейн в режиме head block, и допустим нам надо принять некие монеты на наш аккаунт и сразу отправить их кому-то другому. Благодаря TaPoS мы можем принять монеты и сразу же совершить перевод. Если первая транзакция будет потеряна из-за форка цепи, то и наша транзакция автоматически "отменится".
Ваш пост поддержали следующие Инвесторы Сообщества "Добрый кит":
sharker, litrbooh, boddhisattva, neo, narin, andrvik, max-max, dimarss, tinochka, vadbars, amikphoto, arsar, tom123, yourlastwinter, olga-olga, vict0r, semasping, kssenia, ladynazgool, tnam0rken, zivchakh, rubin, aiparnyuk, hyipov, amelina.elena, lushaya, talia, graff0x, bombo, manavendra, mr-nikola, dimas102, lengalenga, lokkie, bag, ksantoprotein, massatela, chirakovalsky, yakubovruslan, astramar, benken, leonid96, brainmechanic, sinilga, valen-tina, anykeycheg, delectat, yurij12
Поэтому я тоже проголосовал за него!
dobryj.kit теперь стал Делегатом! Ваш голос важен для всего сообщества!!!
Поддержите нас:
Благодарю. Задумался: как же это всё понять...
Очень интересует тема блокчейна. JS знаю на уровне начальном: изучил основы, Jquery, ajax.
Благодарю.
Делать broadcast операций
comment
иcomment_options
нужно в одной транзакции иначеcomment_options
могут не пройти.Тип блока абсолютно не важен. В каждой новой транзакции нужно привязаться к прежним блокам. В примере привязка на 3 блока ранее последнего. Можно еще привязываться на минус 21 блок от текущего. Привязка нужна для целостности цепи, принцип блокчейна - связанные друг с другом последовательные блоки.
Адрес ноды, публичной или локальной, аккаунт с ключом, скрипт который сформирует и отправит транзакцию.
Сама транзакция выглядит так:
https://golos.cf/tx/?=16b914989b1b3c0bec170426372a316d1533e311
zoom
1 ,2 и 3 динамические данные - ref префикс и id описываемы в этом посте + срок действия транзакции.
4 - непосредственно операции
5 - подпись.
Можете открыть исходный код html страницы golos.cf/md там как раз JS и Jquery и формирование транзакции c несколькими операциями через golos-js
Благодарю за разъяснения. Посмотрю golos.cf/md