Поваренная книга VIZ представляет разработчикам описание API/объектов/структур в блокчейне VIZ, примеры кода для популярных вариантов использования, гайд для низкоуровневого формирования транзакций.
Содержание:
Блокчейн VIZ относится к семейству Graphene (блокчейн-технология написанная на C++, открытый код). Исходный код Graphene доступен во множестве вариаций, так как он форкался (копировался) и видоизменялся множество раз (например: BitShares, Steem, VIZ, служил основой для EOS).
Несмотря на общие элементы, в каждой системе есть достаточно много различий в объектах, структурах данных и внутренних механизмах, а также в экономике. Graphene - своего рода каркас, движок для блокчейн решений. Его структуру можно долго изучать, но для разработчиков приложений на том или ином блокчейне нет необходимости досконально знать, как устроен обмен данных между нодами, как происходит синхронизация и хранение данных в памяти. Главное - разобраться в основах.
Программное обеспечение, которое связывается с другими нодами сети, принимает и транслирует транзакции другим нодам, ведет учет и обработку блоков (подробнее в разделе Типы нод).
В VIZ используется модель аккаунтов. Аккаунты входят в общее пространство имен, хранят в себе токены и публичные ключи доступа.
Ключи бывают приватные и публичные. С помощью приватного ключа можно подписать сообщение. С помощью публичного ключа можно доказать факт подписи сообщения. Каждому приватному ключу соответствует единственный публичный ключ. Именно публичные ключи хранятся в блокчейн-системе. Используя их, ноды могут удостовериться, что то или иное действие инициировал владелец приватного ключа (соответствующего публичному ключу).
Аккаунт в блокчейне VIZ содержит три типа полномочий:
Каждый тип полномочий может содержать список доверенных аккаунтов, которые могут подписать и выполнить операцию данного типа доступа от лица исходного аккаунта. Также каждый тип полномочий может содержать один или несколько публичных ключей разного веса (для возможности управления аккаунтом через multisig, когда аккаунтом владеют несколько пользователей).
Обычно аккаунт содержит либо единый ключ для всех типов полномочий, либо по одному ключу на каждый тип полномочий. Аккаунт дополнительно содержит memo-ключ, который используют для кодирования сообщений между участниками сети.
Токен VIZ является передаваемым (переносимым), он не участвует в управлении, но может быть переведен в социальный капитал (SHARES). Данную операцию в других блокчейнах часто называют стейкинг (staking). Аккаунт, владеющий социальным капиталом, может принимать участие в управлении.
Аккаунт может заявить о своем намерении быть делегатом. Делегат (witness) избирается участниками сети (через процедуру голосования) и участвует в очереди делегатов для подписания блоков.
Участник сети может проголосовать как за одного, так и за нескольких делегатов, которые разделят между собой вес его голоса поровну. Чем больше социального капитала проголосует за делегата, тем выше он будет в очереди делегатов для подписания блоков.
Очередь делегатов состоит из 21 слота: 11 мест зафиксированы за делегатами, набравшими наибольшее количество голосов, остальные 10 мест занимают делегаты в плавающей конкурирующей очереди согласно набранным голосам.
Пользователи создают операции (действия аккаунта в сети, подробнее в разделе Операции и их типы), формируют из них транзакцию, которую подписывают приватным ключом аккаунта нужного типа полномочий.
Ноды делегатов получают от всех узлов сети транзакции, которые отправили в сеть пользователи. Делегат, чья очередь подошла подписывать блок, формирует из очереди транзакций блок и отправляет его другим узлам сети. Новый блок формируется каждые 3 секунды. Все узлы сети проверяют подписи делегата, подписи пользователей в транзакциях и применяют операции по очереди. Таким образом формируется состояние системы (подробнее в разделе Состояние (стэйт) системы).
VIZ использует консенсус Fair DPoS (Delegated Proof of Stake). Ноды хранят два состояния системы: необратимое и обратимое. Необратимость (irreversible) наступает тогда, когда в самой длинной цепочке блоков подтвердят свое участие в ней примерно 15 делегатов (75% от количества делегатов в очереди). После этого нода не может откатить более ранние транзакции, таким образом наступает финальность состояния сети (finality).
В обычном состоянии необратимость наступает за 15 блоков (около 45 секунд). Поэтому все важные операции, требующие проверки, могут получить подтверждение только достигнув необратимости.
Плагины расширяют возможности ноды, могут обрабатывать отдельные операции и управлять собственными структурами данных. Есть как обязательные плагины (отвечающие за соединение между узлами сети), так и необязатальные (например, история операций аккаунтов, подробнее в разделе Плагины и их API).
В блокчейне VIZ были созданы условия для так называемой прогнозируемой экономики. Если в других системах заложены принципы с затухающей инфляцией, что ставит в неравные условия участников, подключившихся к системе в разное время, то в VIZ запрограммирована фиксированная инфляция с раундами в 1 год (10512000 блоков).
При запуске цепи были распределены 50 млн viz. Каждый раунд закладывается фиксированная инфляция в 10%. Через год после старта в сети было 55 млн viz, а значит, инфляция в следующий раунд считалась уже от 55 миллионов viz, что привело к эмитированию 5,5 млн viz за второй год.
За счет фиксированной инфляции появляется возможность прогнозировать эмиссию viz каждый блок. Во второй год, например, каждый блок эмитируется 5500000 / 10512000 = 0.523 viz
. И это число не меняется, пока не начнется следующий раунд.
Существующая модель VIZ предусматривает управление экономикой делегатами сети. Избранные участниками сети делегаты голосуют за параметры сети, среди которых есть параметры по распределению эмиссии токенов:
Актуальные значения параметров можно узнать выполнив API запрос
get_chain_properties
к плагинуdatabase_api
или через обозреватель сети на сайте control.viz.world
Фонд ДАО копит токены viz для финансирования инициатив по развитию экосистемы. Если участник сети решил провести конкурс, разработать приложение, принести пользу всей сети — он может подать заявку на финансирование из Фонда. Любой участник сети может проголосовать как против, так и за полное или частичное исполнение заявки. Когда подойдет время рассмотрения заявки, будут подсчитаны доли всех участников сети, принявших участие в голосовании, и вынесено решение.
У каждого участника сети есть возобновляемый со временем ресурс — энергия. Израсходованная энергия восполняется со скоростью 100 процентных пунктов за 5 суток (20 п.п. в сутки, 0,83 п.п. в час и т.д.). Энергия аккаунта не может превышать 100%.
Когда участник Виза решает наградить кого-то, он указывает процент энергии аккаунта, который желает потратить на награду. Блокчейн учитывает размер социального капитала участника и затраченную на награду энергию и соотносит их с суммой наград всех участников за последние 5 суток. В результате, целевой аккаунт получает награду в социальный капитал из эмиссии пропорционально социальному капиталу и потраченной энергии награждающего. Таким образом обеспечивается равноправный конкурентный доступ к Фонду наград. Участник сети сам решает, кого наградить и за что, открывая возможность свободного выбора и стимулирования любых действий и инициатив.
Например:
Вся экономика VIZ находится под управлением владельцев социального капитала. Именно они голосуют за делегатов, если согласны с их виденьем и моделью управления системой (если не за кого голосовать, каждый может сам стать делегатом и получить голоса других участников). Фонд наград и Фонд ДАО работают по справедливой равноправно долевой модели управления.
Нода VIZ — сердце блокчейна, программное обеспечение, которое обрабатывает блок за блоком, исполняет все операции и хранит состояние системы. Владелец настраивает ноду, выбирает используемые плагины. От включенных плагинов и их настроек зависит то, какие возможности может предоставлять нода.
За формирование блоков отвечают делегаты, поэтому важнейшими нодами являются как раз ноды делегатов. Они обмениваются данными с другими нодами, собирают транзакции и когда подходит очередь делегата (владельца сервера) сформировать блок — происходит подпись собранного блока и его трансляция другим узлам. Блок должен быть подписан и доставлен за 3 секунды, выделенные на это делегату. Пропуск блока приводит к задержке выполнения транзакций и штрафу делегата на какое-то время (штраф накладывается на суммарный вес голосов, отданных за делегата другими пользователями). Это позволяет системе временно понижать в очереди делегатов тех, у кого случились какие-то неполадки на сервере или дата-центре, защищая таким образом надежность сети.
Зачастую делегаты держат две ноды, основную и запасную (резерв). Часто они находятся в разных дата-центрах и не зависят друг от друга. Если с основной нодой происходит неполадка, то делегат меняет ключ подписи блоков на резервный и формированием блоков будет заниматься запасная нода.
Ключевые плагины: chain p2p json_rpc webserver witness network_broadcast_api database_api witness_api
Основа для функционирования любой блокчейн системы — peer-to-peer (p2p) соединение и обмен данными. Сид-ноды отличаются тем, что выполняют важную роль — принимают и раздают блоки, разгружая таким образом пропускную способность всей сети и снижая отклик для близких территориально подключений. Нет экономической выгоды держать сид-ноду, так как сервер стоит денег, но не приносит своему владельцу ничего. Поэтому большинство сид-нод запускают делегаты (witnesses), если они могут позволить себе это финансово.
Ключевые плагины: chain p2p
Часть плагинов занимаются предоставляем API для разработчиков и их пользователей. Такие ноды часто называют полными, если у них включены все доступные плагины и хранят историю с первого блока. Так как плагины дают доступ не только к состоянию системы, но и формируют свои структуры данных, у них повышенные требования к ресурсам сервера (особенно к оперативной памяти, так как нода хранит базу данных ChainBase в RAM). Именно через API ноды происходят запросы на актуальную информацию, статус аккаунтов, историю операций или заявки в комитете.
Примеры таких плагинов:
API ноды могут быть как приватными (когда в настройках указаны параметры доступа по логину и паролю), так и публичными (когда обращение к API доступно всем и публично известен адрес ноды). Часто публичные API ноды называют просто публичными нодами. Подробнее читайте в разделе Плагины и их API.
Операции в блокчейне VIZ делятся на обычные, устаревшие, виртуальные. Обычные операции попадают в блокчейн через подписанную транзакцию участника сети. Устаревшие операции отключены в следствии развития сети и изменения внутренних механик.
Часть операций также относятся к тяжелым (data операции), на них накладывается дополнительный коэфициент пропускной способности (голосуемый параметр сети data_operations_cost_additional_bandwidth).
Виртуальные операции генерируются нодой когда срабатывают определенные условия в коде и носят больше информационный характер для участников сети. Например, операция награждения (award) от участника сети содержит цель награждения и затрачиваемую аккаунтом энергию. Там не хранится объем награждения, который достанется получателю из фонда наград. Именно для таких случаев и нужны виртуальные операции. В случае награждения будет сформирована виртуальная операция receive_award, содержащая полученную награду в поле shares.
В протоколе VIZ есть нумерация операций (начинается с нуля), там находятся как обычные, так и виртуальные операции:
Номер операции нужен для низко-уровневого формирования транзакций и их подписи (подробнее в разделе Формирование транзакций).
Рассматривая VIZ необходимо разделять объекты и структуры протокола (операция, транзакция, блок, ассет, версия, полномочия) от объектов и структур которые существуют непосредственно в блокчейне (на которые влияют те или иные операции).
Все, что касается протокола находится в каталоге /libraries/protocol исходного кода C++ ноды блокчейна VIZ.
Именно из объектов и структур самого блокчейна состоит состояния системы (стэйт). Каждый блок, содержащий операции, обрабатывается основным модулем database, который просчитывает все изменения и принимает решения по отложенным действиям. В каталоге /libraries/chain/include/graphene/chain содержатся как объекты и структуры данных, так и внутреннее устройство блокчейна (evaluator, block_log, dynamic_global_property_object, типы объектов).
Состояние системы состоит из объектов:
block_summary_object
);Плагины, предоставляющие API, могут возвращать объекты как из блокчейна, так и собственные. Простые запросы с получением объекта по id отдают данные как есть, часто пропуская объект из блокчейна через конструктор аналогичного для API, чтобы скопировать состояние и отдать пользователю его, например: плагин witness_api
использует отдельный объект witness_api_object
. А плагин database_api
использует account_api_object
, который дополняет стандартный объект блокчейна аккаунт типами доступа копируя туда актуальные полномочия из индекса.
Если плагин расширяет стандартные таблицы индексов и объекты, то он создает новую структуру, отдельно ведет учет операций и заполняет индекс. Например, так поступает плагин private_message
, обрабатывая custom операции (создавая объекты message_object
, наполняющие индекс message_index
).
Делегаты транслируют свою позицию по голосуемым параметрам сети. Блокчейн система каждый цикл очереди делегатов (21 блок) вычисляет медианные значения голосуемых параметров и фиксирует их на этот цикл. Описание параметров (в скобках указаны медианные значения на момент написания данного раздела):
В VIZ существуют служебные аккаунты, которые имеют заложенные в конфигурационном файле ключи доступа к определенным полномочиям:
committee
, viz
, sign
, что соответствует 5Hw9YPABaFxa2LooiANLrhUK5TPryy8f7v9Y1rk923PuYqbYdfC
. После запуска сети и подключения к ней других делегатов аноним может прекратить подпись аккаунтом committee
и ключ подписи обнуляется до значения VIZ1111111111111111111111111111111114T1Anm
.memo
для регистрации анонимного аккаунта: login:public_key
, где login
- это желаемый логин для нового аккаунта, а public_key
— единый публичный ключ для всех типов полномочий. Если логин не указан, а в заметке только public_key
, то создается анонимный сабаккаунт формата nX.anonymous
, где X — инкрементация номера, указанного в json_metadata
аккаунта anonymous
. Внимание! Если заметка не указана, средства будут сожжены аналогично переводу на аккаунт null
.invite
, viz
, active
, что соответствует 5KcfoRuDfkhrLCxVcE9x51J6KN9aM9fpb78tLrvvFckxVV6FyFW
. Изначально не имеет какой-либо доли для осуществления транзакций, что может быть исправлено путем делегирования или включения системы резервной пропускной способности делегатами.5JabcrvaLnBTCkCVFX5r4rmeGGfuJuVp4NAKRNLTey6pxhRQmf4
, был сброшен на пустой после предустановки продажи субаккаунтов за 10 000 VIZ (получатель committee
).Состояние системы — необходимый минимум данных, для функцонирования ноды. Каркас Graphene, на котором построен VIZ, исполняет операции из транзакций в каждом блоке, который соответствует консенсусу (очереди делегатов, соответствие подписей). Каждый блок происходит обработка данных, отложенных действий, что приводит к иттерационной сущности состояния системы. Часть данных хранятся там постоянно, что накладывает определенные требования на серверное оборудование, на котором запущена нода.
В разделе Объекты и структуры в блокчейне описана большая часть объектов, которые составляют состояние системы.
Данный объект хранит основные свойства сети, содержит информацию о токенах в обращении и другую важную информацию. В исходном коде он часто представлен в виде переменной dgp, нода модифицирует его состояние с каждой иттерацией, поэтому dynamic global property можно с уверенностью считать самой важной частью состояния системы. Рассмотрим его публичные свойства, доступные через метод get_dynamic_global_properties при обращении к плагину database_api:
current_witness (пример значения: "solox") — делегат актуального блока;
head_block_number (пример значения: 10829669) — номер актуального блока;
head_block_id (пример значения: 00a53f658226b2a6f3f75fc8185d884d029f50bf
) — идентификатор (он же хэш) актуального блока;
time (пример значения: "2019-10-11T08:49:21") — время генерации актуального блока;
last_irreversible_block_num (пример значения: 10829651) — номер последнего необратимого блока;
genesis_time (пример значения: "2018-09-29T10:23:24") — время генерации первого блока сети;
current_supply (пример значения: "55159321.957 VIZ") — общее количество токенов VIZ в системе;
total_vesting_fund (пример значения: "27924855.425 VIZ") — количество токенов VIZ переведенных в долю сети (SHARES);
total_vesting_shares (пример значения: "27924847.935329 SHARES") — общая количественная мера доли сети в SHARES;
committee_fund (пример значения: "1423813.837 VIZ") — баланс фонда комитета;
committee_requests (пример значения: 39) — общее количество заявок в комитет;
total_reward_fund (пример значения: "28216.906 VIZ") — баланс фонда наград;
total_reward_shares (пример значения: "6019776735774") — количественная мера конкуренции за фонд наград;
current_aslot (пример значения: 10855719) — текущий номер слота делегата на подпись (содержит в себе нумерацию слота от старта в сети, включая пропущенные делегатами блоки);
recent_slots_filled (пример значения: 340282366920938463463374607431768211455
) — используется для вычисления процента делегатов участвующих в подписи блоков;
participation_count (пример значения: 128) — необходимо разделить на 128, чтобы получить процент делегатов участвующих в подписи блоков;
maximum_block_size (пример значения: 65536) — максимальный размер блока в байтах (голосуемый параметр сети);
average_block_size (пример значения: 114) — средний размер блока, рассчитывается по формуле average_block_size = (99 * average_block_size + new_block_size) / 100
, используется для обновления current_reserve_ratio для поддержания около 50% или меньше в пропускной способности сети;
max_virtual_bandwidth (пример значения: 5986734968066277376
) — максимальная пропускная способность сети рассчитывается по формуле maximum_block_size * CHAIN_BANDWIDTH_AVERAGE_WINDOW_SECONDS / CHAIN_BLOCK_INTERVAL
, максимальная виртуальная пропускная способность сети по формуле max_bandwidth * current_reserve_ratio
current_reserve_ratio (пример значения: 20000) — Раз в 20 блоков (1 минута) происходит проверка average_block_size <= 25% maximum_block_size
. Если оно выполняется, то данное значение увеличивается на 1 (линейно, каждый блок), но не более CHAIN_MAX_RESERVE_RATIO (20000). Если условие не выполнено, то current_reserve_ratio делится пополам, что должно сразу снизить нагрузку на сеть, защищая ее от участников, использующих объемные транзакции. Другими словами уменьшение вдвое резервного соотношения не уменьшит вдвое использование сети, но ограничит пользователей которые уже попытаются превысить более 50% от их пропускной способности. Когда резервное соотношение падает вдвое от максимального значения (10000 вместо 20000), восстановление общей виртуальной пропускной способности займет около 7 суток.
bandwidth_reserve_candidates (пример значения: 1) — количество кандидатов на резерв пропускной способности (плюс 1 кандидат по умолчанию);
inflation_calc_block_num (пример значения: 10315901) — номер блока последнего расчета распределения инфляции;
inflation_witness_percent (пример значения: 2000) — процент от эмиссии получаемый делегатами за подпись блоков;
inflation_ratio (пример значения: 5000) — процент соотношения от эмиссии направляемый в фонд комитета против фонда наград;
vote_regeneration_per_day (пример значения: 1) — устаревшее свойство.
Нода проверяет все входящие транзакции на уникальность в пуле транзакций. После того как наступает expiration (ограничено в настройках константой CHAIN_MAX_TIME_UNTIL_EXPIRATION в один час), транзакция удаляется из пула.
Все транзакции в VIZ должны соответствовать концепции TaPoS, то-есть ссылаться на один из прошлых блоков (ref_block_num в 2 байтовом представлении (бинарное «и» десятичного представления номера блока с hex ffff
) и ref_block_prefix состоящий из десятичного представления 5, 6, 7, 8 байтов от бинарного состояния хэша в обратном порядке), что позволяет иницатору транзакции опираться на актуальное для него состояние системы не беспокоясь о необратимом блоке. В случае, если он опирался на состояние системы в случайном минорном форке, то транзакция не попадет в основную цепочку. Таким образом участники сети могут контролировать исполнение очереди транзакций и строить взаимодействие не дожидаясь необратимости блока. Это, в свою очередь, накладывает ограничение на финальный учет подобных действий, поэтому большинство важных транзакций должны находиться уже в необратимом состоянии для проверяющей стороны.
Нода выделяет пространство block_summary_object с размерностью в 2 байта (чтобы номер блока прошедший через операцию бинарного «и» с hex ffff
умещался в диапазоне от 0 до 65536) и принимая новые блоки перезаписывает по кругу идентификаторы (хэши) в этом пространстве (и индексе block_summary_index). 65537 блоков охватывают временной промежуток 196611 секунд (примерно 2.27 суток). Соответственно новые транзакции могут ссылаться только на блоки из этого пространства, чтобы нода могла сверить соответствие идентификатора блока (из ref_block_num) с контрольной суммой из ref_block_prefix.
Если в первом поколении блокчейн-систем основанных на Proof of Work требовалось хранить в состоянии системы все идентификаторы блоков и транзакций, то с ростом количества данных многие разработчики начали искать способ снизить издержки на объем хранимых данных. И основной объем данных хранится как раз в блоках и содержащихся в них транзакциях. В современных DLT уже решена эта проблема за счет согласования необратимого состояния и переносимого состояния системы. Часть блокчейн-систем только начинают двигаться в этом направлении, например в Steem предложено решение в виде Platform Independent State Files – PISF. Новые блокчейн-системы (и часть старых первопроходцев, например нода к XRP Ledger — rippled) уже созданы с учетом переносимого состояния системы, их архитектура позволяет запросить у доверенных нод актуальное состояние системы, пропуская длительную синхронизацию, скачивание всей истории блокчейна и самостоятельную обработку всех транзакций.
VIZ не вносил значительных изменений в архитектуру состояния системы Graphene, поэтому в индексе block_summary_index состоящий из структур block_summary_object хранится вся информация о блоках (а именно block_id_type конкретного блока, который уже содержит всю информацию). Это затрудняет создание переносимого состояния системы, так как объем данных для такого состояния будет значительным. Единственная возможность модернизировать это — перейти к консенсусу доверенных нод.
Плагины представляют собой универсальный инструмент расширения ноды и её возможностей. Часть из них лишь отдают данные, подготавливают индексы, отвечают на сложные запросы пользователей с фильтрацией данных, часть из них обрабатывают custom операции и могут предоставлять совершенно отдельный сервис. Например, можно написать плагин, который после платной подписки первого уровня будет формировать уведомления о важных действиях в блокчейне или предоставлять сервис личных сообщений. Публичный плагин не всегда означает «бесплатный». И «бесплатный» не всегда значит открытый (в плане исходного кода).
Если обратиться к логическому изучению цепочки нода-сервисы-API, то мы увидим неприятную ситуацию, когда публичные API могут создавать проблемы для функционирования самой ноды. Такое может возникнуть при большом потоке запросов к API сервиса, тем более в том случае, если плагин, предоставляющий сервис, работает как раз с нодой блокчейна. Пропускная способность от пользовательских запросов (или запросов злоумышленника при желании произвести DDOS сервиса) может помешать самой ноде обмениваться данными с другими узлами. Если API запросы нагружают CPU или используют большие выборки по индексам, долго подготавливают данные для ответа — возникает проблема не только с сервисом, который начинает тормозить, но и с функционированием ноды.
Именно поэтому рекомендуется избегать пересечения публичной API ноды с требовательными плагинами и работы делегата на том же сервере. Более правильной архитектурой сервиса будет являться отдельный независимый плагин, имеющий доступ к обработке блоков на приватной ноде, сам обрабатывает данные, хранит их в базе данных и позволяет кэшировать запросы. При росте нагрузки всегда можно расширить сервис применяя технологии кластеризации как данных, так и отвечающих узлов (с помощью load balancing).
В данном разделе описаны все доступные плагины VIZ, предоставляющие пользователям доступ через API. Вы можете сами изучить API того или иного плагин, если будете следовать следующей инструкции:
DEFINE_API_ARGS
(название API метода, тип возвращаемого значения);DEFINE_API
(проверка параметров запроса, CHECK_ARGS_COUNT
, формирование возвращаемого значения определенного типа);plugin_initialize
, который может обрабатывать boost::program_options::variables_map
для более тонкой настройки плагина через конфигурационный файл ноды.Все запросы должны быть сформированы в JSON и выполнены через RPC. Транспортный протокол зависит от настройки ноды, возможны варианты как JSON-RPC через стандартные HTTP запросы, так и через WebSocket.
Для этого в конфигурационном файле ноды должны быть подключены плагины: json_rpc, webserver. Чтобы принимать транзакции от пользователей также должен быть включен плагин network_broadcast_api. Настройки для портов:
# Количество потоков для клиентов rpc. Оптимальное значение *количество ядер минус 1*
webserver-thread-pool-size = 2
# IP:PORT для HTTP подключений
webserver-http-endpoint = 0.0.0.0:8090
# IP:PORT для WebSocket подключений
webserver-ws-endpoint = 0.0.0.0:8091
# IP:PORT для HTTP и WebSocket соединений (одновременная обработка двух типов подключений)
rpc-endpoint = 0.0.0.0:8081
Чтобы обрабатывать запросы с поддержкой SSL, необходимо пробросить используемые порты через проксирующий сервер (например, nginx или apache), тогда станут возможными запросы через https/wss.
Правила формирования запросов к публичной ноде довольно простые:
{"id":REQUEST_ID,"jsonrpc":"2.0","method":"call","params":["PLUGIN_NAME","PLUGIN_API_METHOD",[ARGS]]}
Плагин, отвечающий за прием и рассылку между узлами сети подписанных блоков и транзакций
Пример:
{"id":1,"method":"call","jsonrpc":"2.0","params":["custom_protocol_api","get_account",[["readdle","V"]]]}
Ответ:
{
"id": 116,
"name": "readdle",
"master_authority": {
"weight_threshold": 1,
"account_auths": [],
"key_auths": [
[
"VIZ7PZqJj3UvV3kymCWHnwn9PxRGgt9z6MDzyEeXCFVr6X9XmoBCY",
1
]
]
},
"active_authority": {
"weight_threshold": 1,
"account_auths": [],
"key_auths": [
[
"VIZ6CmEqBAdWu1MGrSeTPwSG9yKwLwBPEuVBhzbKBMFP2aYTomCCN",
1
]
]
},
"regular_authority": {
"weight_threshold": 1,
"account_auths": [],
"key_auths": [
[
"VIZ8PqkhFxxifidH6688oSZHYPGsXMhRVE9xhvh6N65XkYRg74pMR",
1
]
]
},
"memo_key": "VIZ7Lo593wA3SwFwpiHYfm3kWw4BwgrcBsYt1UDyZW4J3dd5GWuab",
"json_metadata": "{\"profile\":{\"nickname\":\"Readdle.me\",\"about\":\"RU: Децентрализованная Социальная Сеть, где пользователь выступает в роли Оракула, обрабатывающего социальную активность интересных ему аккаунтов. Работает на блокчейне VIZ с использованием системы награждений Социальным Капиталом VIZ (Ƶ).\",\"avatar\":\"https://readdle.me/readdle-avatar.png\",\"services\":{\"telegram\":\"readdle_me\"},\"interests\":[\"ru\",\"welcome\"],\"pinned\":\"viz://@readdle/22099872/\"}}",
"proxy": "",
"referrer": "",
"last_master_update": "1970-01-01T00:00:00",
"last_account_update": "2020-11-05T19:23:33",
"created": "2018-09-29T18:25:27",
"recovery_account": "in",
"last_account_recovery": "1970-01-01T00:00:00",
"subcontent_count": 0,
"vote_count": 0,
"content_count": 0,
"awarded_rshares": 0,
"custom_sequence": 4,
"custom_sequence_block_num": 22897215,
"energy": 10000,
"last_vote_time": "2018-09-29T18:25:27",
"balance": "0.000 VIZ",
"vesting_shares": "24.102958 SHARES",
"delegated_vesting_shares": "0.000000 SHARES",
"received_vesting_shares": "10.056015 SHARES",
"vesting_withdraw_rate": "0.000000 SHARES",
"next_vesting_withdrawal": "1969-12-31T23:59:59",
"withdrawn": 0,
"to_withdraw": 0,
"withdraw_routes": 0,
"curation_rewards": 0,
"posting_rewards": 0,
"receiver_awards": 24103,
"benefactor_awards": 0,
"proxied_vsf_votes": [
0,
0,
0,
0
],
"witnesses_voted_for": 0,
"witnesses_vote_weight": 0,
"last_post": "1970-01-01T00:00:00",
"last_root_post": "1970-01-01T00:00:00",
"average_bandwidth": "16919020937",
"lifetime_bandwidth": "52767000000",
"last_bandwidth_update": "2020-12-03T11:55:45",
"witness_votes": [],
"valid": true,
"account_seller": "",
"account_offer_price": "0.000 VIZ",
"account_on_sale": false,
"account_on_sale_start_time": "1970-01-01T00:00:00",
"subaccount_seller": "",
"subaccount_offer_price": "0.000 VIZ",
"subaccount_on_sale": false
}
Пример:
{"id":1,"method":"call","jsonrpc":"2.0","params":["database_api","get_accounts",[["wildviz","zozo"]]]}
Ответ:
[
{
"id": 3276,
"name": "wildviz",
"master_authority": {
"weight_threshold": 1,
"account_auths": [],
"key_auths": [
[
"VIZ7TAJxC1ibwYEgWon3YWXJdjKaTPS3eVy9zSKq2cRFECMuJvHcq",
1
]
]
},
"active_authority": {
"weight_threshold": 1,
"account_auths": [],
"key_auths": [
[
"VIZ8dJxeKPXe5wf6bnqaMsj3EW8EbMURoKxR4RqPCQH8xA89b6RpC",
1
]
]
},
"regular_authority": {
"weight_threshold": 1,
"account_auths": [],
"key_auths": [
[
"VIZ8iKb38H1JD7PZqEYpoLX3nMt1mgfoh2LwngexvywusE7fgrd5G",
1
]
]
},
"memo_key": "VIZ8mvAu9beH6gDtBrdy3oh8eTGP4tKpNXHvY7wtGm7EXLuwFaRi7",
"json_metadata": "",
"proxy": "",
"referrer": "",
"last_master_update": "1970-01-01T00:00:00",
"last_account_update": "1970-01-01T00:00:00",
"created": "2019-03-14T08:33:21",
"recovery_account": "xchng",
"last_account_recovery": "1970-01-01T00:00:00",
"subcontent_count": 0,
"vote_count": 10,
"content_count": 0,
"awarded_rshares": 0,
"custom_sequence": 2,
"custom_sequence_block_num": 10319967,
"energy": 9995,
"last_vote_time": "2019-10-14T08:08:48",
"balance": "0.000 VIZ",
"vesting_shares": "19159.343040 SHARES",
"delegated_vesting_shares": "250.000000 SHARES",
"received_vesting_shares": "0.000000 SHARES",
"vesting_withdraw_rate": "0.000000 SHARES",
"next_vesting_withdrawal": "1969-12-31T23:59:59",
"withdrawn": 1500000000,
"to_withdraw": 1500000000,
"withdraw_routes": 0,
"curation_rewards": 0,
"posting_rewards": 0,
"receiver_awards": 255786,
"benefactor_awards": 2958047,
"proxied_vsf_votes": [
0,
0,
0,
0
],
"witnesses_voted_for": 1,
"witnesses_vote_weight": "19159343040",
"last_post": "1970-01-01T00:00:00",
"last_root_post": "1970-01-01T00:00:00",
"average_bandwidth": 1089649559,
"lifetime_bandwidth": "24196000000",
"last_bandwidth_update": "2019-10-14T08:08:48",
"witness_votes": [
"wildviz"
],
"valid": true,
"account_seller": "",
"account_offer_price": "0.000 VIZ",
"account_on_sale": false,
"account_on_sale_start_time": "1970-01-01T00:00:00",
"subaccount_seller": "",
"subaccount_offer_price": "0.000 VIZ",
"subaccount_on_sale": false
}
]
Пример:
{"id":1,"method":"call","jsonrpc":"2.0","params":["database_api","get_accounts_on_sale",[0,1000]]}
Ответ:
[
{"account":"btc","account_seller":"ae","account_offer_price":"5000.000 VIZ"},
{"account":"press","account_seller":"on1x","account_offer_price":"10000.000 VIZ"}
]
Пример:
{"id":1,"method":"call","jsonrpc":"2.0","params":["database_api","get_subaccounts_on_sale",[0,1000]]}
Ответ:
[
{"account":"com","subaccount_seller":"ae","subaccount_offer_price":"10.000 VIZ"},
{"account":"digital","subaccount_seller":"on1x","subaccount_offer_price":"100.000 VIZ"},
{"account":"blog","subaccount_seller":"on1x","subaccount_offer_price":"100.000 VIZ"},
{"account":"new.romankr","subaccount_seller":"romankr","subaccount_offer_price":"10.000 VIZ"},
{"account":"romankr1","subaccount_seller":"romankr","subaccount_offer_price":"10.000 VIZ"},
{"account":"viz","subaccount_seller":"committee","subaccount_offer_price":"10000.000 VIZ"}
]
Пример:
{"id":1,"method":"call","jsonrpc":"2.0","params":["database_api","get_block",["1"]]}
Ответ:
{
"previous": "0000000000000000000000000000000000000000",
"timestamp": "2018-09-29T10:23:27",
"witness": "committee",
"transaction_merkle_root": "0000000000000000000000000000000000000000",
"extensions": [
[
1,
"1.0.0"
]
],
"witness_signature": "2003120d1f1d8bb8e8325036838e5269332fbec7c88cd3cc76e4517d27772856ce43ef6bf0a7fde9987cec9467b944e7626db100b70867e7ec82308879a021e97a",
"transactions": []
}
Пример:
{"id":1,"method":"call","jsonrpc":"2.0","params":["database_api","get_block_header",["2"]]}
Ответ:
{
"previous": "000000010496d4414ddcee5b76f9a6b950da6fe9",
"timestamp": "2018-09-29T10:23:30",
"witness": "committee",
"transaction_merkle_root": "0000000000000000000000000000000000000000",
"extensions": []
}
Пример:
{"id":1,"method":"call","jsonrpc":"2.0","params":["database_api","get_chain_properties",[]]}
Ответ:
{
"account_creation_fee": "1.000 VIZ",
"maximum_block_size": 65536,
"create_account_delegation_ratio": 10,
"create_account_delegation_time": 2592000,
"min_delegation": "1.000 VIZ",
"min_curation_percent": 0,
"max_curation_percent": 10000,
"bandwidth_reserve_percent": 0,
"bandwidth_reserve_below": "0.000000 SHARES",
"flag_energy_additional_cost": 0,
"vote_accounting_min_rshares": 50000,
"committee_request_approve_min_percent": 1000,
"inflation_witness_percent": 2000,
"inflation_ratio_committee_vs_reward_fund": 7500,
"inflation_recalc_period": 806400,
"data_operations_cost_additional_bandwidth": 0,
"witness_miss_penalty_percent": 100,
"witness_miss_penalty_duration": 86400
}
Пример:
{"id":1,"method":"call","jsonrpc":"2.0","params":["database_api","get_config",[]]}
Ответ:
{
"CHAIN_100_PERCENT": 10000,
"CHAIN_1_PERCENT": 100,
"CHAIN_ADDRESS_PREFIX": "VIZ",
"CHAIN_BANDWIDTH_AVERAGE_WINDOW_SECONDS": 604800,
"CHAIN_BANDWIDTH_PRECISION": 1000000,
"CONSENSUS_BANDWIDTH_RESERVE_PERCENT": 1000,
"CONSENSUS_BANDWIDTH_RESERVE_BELOW": 500000000,
"CHAIN_HARDFORK_VERSION": "2.4.0",
"CHAIN_VERSION": "2.4.0",
"CHAIN_BLOCK_INTERVAL": 3,
"CHAIN_BLOCKS_PER_DAY": 28800,
"CHAIN_BLOCKS_PER_YEAR": 10512000,
"CHAIN_CASHOUT_WINDOW_SECONDS": 86400,
"CHAIN_ID": "2040effda178d4fffff5eab7a915d4019879f5205cc5392e4bcced2b6edda0cd",
"CHAIN_HARDFORK_REQUIRED_WITNESSES": 17,
"CHAIN_INITIATOR_NAME": "viz",
"CHAIN_INITIATOR_PUBLIC_KEY_STR": "VIZ6MyX5QiXAXRZk7SYCiqpi6Mtm8UbHWDFSV8HPpt7FJyahCnc2T",
"CHAIN_INIT_SUPPLY": "50000000000",
"CHAIN_COMMITTEE_ACCOUNT": "committee",
"CHAIN_COMMITTEE_PUBLIC_KEY_STR": "VIZ6Yt7d6LsngBoXQr47aLv97bJVs7jyr7esZTM4UUSpLUf3nbRKS",
"CHAIN_IRREVERSIBLE_THRESHOLD": 7500,
"CHAIN_IRREVERSIBLE_SUPPORT_MIN_RUN": 2,
"CHAIN_MAX_ACCOUNT_NAME_LENGTH": 25,
"CHAIN_MAX_ACCOUNT_WITNESS_VOTES": 100,
"CHAIN_BLOCK_SIZE": 6291456,
"CHAIN_MAX_COMMENT_DEPTH": 65520,
"CHAIN_MAX_MEMO_LENGTH": 2048,
"CHAIN_MAX_WITNESSES": 21,
"CHAIN_MAX_PROXY_RECURSION_DEPTH": 4,
"CHAIN_MAX_RESERVE_RATIO": 20000,
"CHAIN_MAX_SUPPORT_WITNESSES": 10,
"CHAIN_MAX_SHARE_SUPPLY": "1000000000000000",
"CHAIN_MAX_SIG_CHECK_DEPTH": 2,
"CHAIN_MAX_TIME_UNTIL_EXPIRATION": 3600,
"CHAIN_MAX_TRANSACTION_SIZE": 65536,
"CHAIN_MAX_UNDO_HISTORY": 10000,
"CHAIN_MAX_VOTE_CHANGES": 5,
"CHAIN_MAX_TOP_WITNESSES": 11,
"CHAIN_MAX_WITHDRAW_ROUTES": 10,
"CHAIN_MAX_WITNESS_URL_LENGTH": 2048,
"CHAIN_MIN_ACCOUNT_CREATION_FEE": 1000,
"CHAIN_MIN_ACCOUNT_NAME_LENGTH": 2,
"CHAIN_MIN_BLOCK_SIZE_LIMIT": 65536,
"CHAIN_MAX_BLOCK_SIZE_LIMIT": 2097152,
"CHAIN_NULL_ACCOUNT": "null",
"CHAIN_NUM_INITIATORS": 0,
"CHAIN_PROXY_TO_SELF_ACCOUNT": "",
"CHAIN_SECONDS_PER_YEAR": 31536000,
"CHAIN_VESTING_WITHDRAW_INTERVALS": 28,
"CHAIN_VESTING_WITHDRAW_INTERVAL_SECONDS": 86400,
"CHAIN_ENERGY_REGENERATION_SECONDS": 432000,
"TOKEN_SYMBOL": 1514755587,
"SHARES_SYMBOL": "23438642651878150",
"CHAIN_NAME": "VIZ",
"CHAIN_BLOCK_GENERATION_POSTPONED_TX_LIMIT": 5,
"CHAIN_PENDING_TRANSACTION_EXECUTION_LIMIT": 200000
}
Пример:
{"id":1,"method":"call","jsonrpc":"2.0","params":["database_api","get_database_info",[]]}
Ответ:
{
"total_size": "6442450944",
"free_size": 1828375424,
"reserved_size": 0,
"used_size": "4614075520",
"index_list": [
{
"name": "graphene::chain::dynamic_global_property_object",
"record_count": 1
},
{
"name": "graphene::chain::account_object",
"record_count": 3700
},
{
"name": "graphene::chain::account_authority_object",
"record_count": 3700
},
{
"name": "graphene::chain::witness_object",
"record_count": 59
},
{
"name": "graphene::chain::transaction_object",
"record_count": 4
},
{
"name": "graphene::chain::block_summary_object",
"record_count": 65536
},
{
"name": "graphene::chain::witness_schedule_object",
"record_count": 1
},
{
"name": "graphene::chain::content_object",
"record_count": 0
},
{
"name": "graphene::chain::content_type_object",
"record_count": 0
},
{
"name": "graphene::chain::content_vote_object",
"record_count": 0
},
{
"name": "graphene::chain::witness_vote_object",
"record_count": 239
},
{
"name": "graphene::chain::hardfork_property_object",
"record_count": 1
},
{
"name": "graphene::chain::withdraw_vesting_route_object",
"record_count": 7
},
{
"name": "graphene::chain::master_authority_history_object",
"record_count": 4
},
{
"name": "graphene::chain::account_recovery_request_object",
"record_count": 0
},
{
"name": "graphene::chain::change_recovery_account_request_object",
"record_count": 0
},
{
"name": "graphene::chain::escrow_object",
"record_count": 0
},
{
"name": "graphene::chain::vesting_delegation_object",
"record_count": 286
},
{
"name": "graphene::chain::fix_vesting_delegation_object",
"record_count": 0
},
{
"name": "graphene::chain::vesting_delegation_expiration_object",
"record_count": 12
},
{
"name": "graphene::chain::account_metadata_object",
"record_count": 3700
},
{
"name": "graphene::chain::proposal_object",
"record_count": 0
},
{
"name": "graphene::chain::required_approval_object",
"record_count": 0
},
{
"name": "graphene::chain::committee_request_object",
"record_count": 39
},
{
"name": "graphene::chain::committee_vote_object",
"record_count": 453
},
{
"name": "graphene::chain::invite_object",
"record_count": 364
},
{
"name": "graphene::chain::award_shares_expire_object",
"record_count": 2463
},
{
"name": "graphene::chain::paid_subscription_object",
"record_count": 4
},
{
"name": "graphene::chain::paid_subscribe_object",
"record_count": 26
},
{
"name": "graphene::chain::witness_penalty_expire_object",
"record_count": 4
},
{
"name": "graphene::plugins::private_message::message_object",
"record_count": 0
},
{
"name": "graphene::plugins::operation_history::operation_object",
"record_count": 11967735
},
{
"name": "graphene::plugins::account_history::account_history_object",
"record_count": 12014504
}
]
}
Пример:
{"id":1,"method":"call","jsonrpc":"2.0","params":["database_api","get_dynamic_global_properties",[]]}
Ответ:
{
"id": 0,
"head_block_number": 10920301,
"head_block_id": "00a6a16d8a63db35fa727c62a49c00bf9d963819",
"genesis_time": "2018-09-29T10:23:24",
"time": "2019-10-14T12:22:54",
"current_witness": "ae.witness",
"committee_fund": "1442755.925 VIZ",
"committee_requests": 39,
"current_supply": "55206722.493 VIZ",
"total_vesting_fund": "27501277.264 VIZ",
"total_vesting_shares": "27501269.766153 SHARES",
"total_reward_fund": "28351.836 VIZ",
"total_reward_shares": "6610384216022",
"average_block_size": 120,
"maximum_block_size": 65536,
"current_aslot": 10946390,
"recent_slots_filled": "340282366920938463463374607431768211455",
"participation_count": 128,
"last_irreversible_block_num": 10920284,
"max_virtual_bandwidth": "5986734968066277376",
"current_reserve_ratio": 20000,
"vote_regeneration_per_day": 1,
"bandwidth_reserve_candidates": 1,
"inflation_calc_block_num": 10315901,
"inflation_witness_percent": 2000,
"inflation_ratio": 5000
}
Пример:
{"id":1,"method":"call","jsonrpc":"2.0","params":["database_api","get_next_scheduled_hardfork",[]]}
Ответ:
{
"hf_version": "2.4.0",
"live_time": "2019-04-30T05:00:00"
}
Пример:
{"id":1,"method":"call","jsonrpc":"2.0","params":["database_api","get_potential_signatures",[{"ref_block_num":41097,"ref_block_prefix":1234018187,"expiration":"2019-10-14T12:21:46","operations":[["award",{"initiator":"social","receiver":"social","energy":15,"custom_sequence":0,"memo":"ubi","beneficiaries":[]}]],"extensions":[]}]]}
Ответ:
[
"VIZ5iCdUDKypJU3iCp1vbkTk5P7hEW1GgK6E75V1yZZznGU7NuV32"
]
Пример:
{"id":1,"method":"call","jsonrpc":"2.0","params":["database_api","get_required_signatures",[{"ref_block_num":41097,"ref_block_prefix":1234018187,"expiration":"2019-10-14T12:21:46","operations":[["award",{"initiator":"social","receiver":"social","energy":15,"custom_sequence":0,"memo":"ubi","beneficiaries":[]}]],"extensions":[]},["VIZ7TAJxC1ibwYEgWon3YWXJdjKaTPS3eVy9zSKq2cRFECMuJvHcq","VIZ5iCdUDKypJU3iCp1vbkTk5P7hEW1GgK6E75V1yZZznGU7NuV32"]]]}
Ответ:
[
"VIZ5iCdUDKypJU3iCp1vbkTk5P7hEW1GgK6E75V1yZZznGU7NuV32"
]
Пример:
{"id":1,"method":"call","jsonrpc":"2.0","params":["database_api","get_transaction_hex",[{"ref_block_num":41097,"ref_block_prefix":1234018187,"expiration":"2019-10-14T12:21:46","operations":[["award",{"initiator":"social","receiver":"social","energy":15,"custom_sequence":0,"memo":"ubi","beneficiaries":[]}]],"extensions":[],"signatures":["1f64cb23ba686126f9a00d904840e472de44e0e2291eca5c6b380083ee7a0b9f9934bbe9f03404a33a6df0630a020ff4750b886b65f4af8cd9877df8128d45da05"]}]]}
Ответ:
89a08b9f8d495a68a45d012f06736f6369616c06736f6369616c0f000000000000000000037562690000011f64cb23ba686126f9a00d904840e472de44e0e2291eca5c6b380083ee7a0b9f9934bbe9f03404a33a6df0630a020ff4750b886b65f4af8cd9877df8128d45da05
Пример:
{"id":1,"method":"call","jsonrpc":"2.0","params":["database_api","lookup_account_names",[["wildviz","zozo"]]]}
Ответ:
[
{
"id": 3276,
"name": "wildviz",
"master_authority": {
"weight_threshold": 1,
"account_auths": [],
"key_auths": [
[
"VIZ7TAJxC1ibwYEgWon3YWXJdjKaTPS3eVy9zSKq2cRFECMuJvHcq",
1
]
]
},
"active_authority": {
"weight_threshold": 1,
"account_auths": [],
"key_auths": [
[
"VIZ8dJxeKPXe5wf6bnqaMsj3EW8EbMURoKxR4RqPCQH8xA89b6RpC",
1
]
]
},
"regular_authority": {
"weight_threshold": 1,
"account_auths": [],
"key_auths": [
[
"VIZ8iKb38H1JD7PZqEYpoLX3nMt1mgfoh2LwngexvywusE7fgrd5G",
1
]
]
},
"memo_key": "VIZ8mvAu9beH6gDtBrdy3oh8eTGP4tKpNXHvY7wtGm7EXLuwFaRi7",
"json_metadata": "",
"proxy": "",
"referrer": "",
"last_master_update": "1970-01-01T00:00:00",
"last_account_update": "1970-01-01T00:00:00",
"created": "2019-03-14T08:33:21",
"recovery_account": "xchng",
"last_account_recovery": "1970-01-01T00:00:00",
"subcontent_count": 0,
"vote_count": 10,
"content_count": 0,
"awarded_rshares": 0,
"custom_sequence": 2,
"custom_sequence_block_num": 10319967,
"energy": 9995,
"last_vote_time": "2019-10-14T08:08:48",
"balance": "0.000 VIZ",
"vesting_shares": "19157.679056 SHARES",
"delegated_vesting_shares": "250.000000 SHARES",
"received_vesting_shares": "0.000000 SHARES",
"vesting_withdraw_rate": "0.000000 SHARES",
"next_vesting_withdrawal": "1969-12-31T23:59:59",
"withdrawn": 1500000000,
"to_withdraw": 1500000000,
"withdraw_routes": 0,
"curation_rewards": 0,
"posting_rewards": 0,
"receiver_awards": 255786,
"benefactor_awards": 2958047,
"proxied_vsf_votes": [
0,
0,
0,
0
],
"witnesses_voted_for": 1,
"witnesses_vote_weight": "19157679056",
"last_post": "1970-01-01T00:00:00",
"last_root_post": "1970-01-01T00:00:00",
"average_bandwidth": 1089649559,
"lifetime_bandwidth": "24196000000",
"last_bandwidth_update": "2019-10-14T08:08:48",
"witness_votes": [],
"valid": true,
"account_seller": "",
"account_offer_price": "0.000 VIZ",
"account_on_sale": false,
"account_on_sale_start_time": "1970-01-01T00:00:00",
"subaccount_seller": "",
"subaccount_offer_price": "0.000 VIZ",
"subaccount_on_sale": false
},
null
]
Пример:
{"id":1,"method":"call","jsonrpc":"2.0","params":["database_api","lookup_accounts",["ae","10"]]}
Ответ:
[
"ae",
"ae-witness",
"ae.witness",
"ae0",
"ae1",
"ae10",
"ae100",
"ae101",
"ae102",
"ae103"
]
Пример запроса:
{"id":1,"method":"call","jsonrpc":"2.0","params":["account_by_key","get_key_references",[["VIZ5Z2po7K5CoCXw2xLPPt8JJvJLJ3xVNANLgTy9KDfLeZH2urSSd","VIZ1111111111111111111111111111111114T1Anm"]]]}
Ответ:
[
[
"on1x"
],
[
"viz"
]
]
track-account-range
в виде json строки: ["from","to"]
.Пример запроса:
{"id":1,"method":"call","jsonrpc":"2.0","params":["account_history","get_account_history",["on1x","-1","5"]]}
Ответ:
[
[
3057,
{
"trx_id": "d5f53163bc237b17c8fd6f3278d3ca1b4ae21691",
"block": 10913871,
"trx_in_block": 0,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2019-10-14T07:01:18",
"op": [
"transfer",
{
"from": "viz-social-bot",
"to": "on1x",
"amount": "7.725 VIZ",
"memo": "withdraw:3353"
}
]
}
],
[
3058,
{
"trx_id": "ac8c4c225334dc59ec604dfa3d56b55dfc0647d3",
"block": 10916119,
"trx_in_block": 0,
"op_in_trx": 0,
"virtual_op": 1,
"timestamp": "2019-10-14T08:53:42",
"op": [
"receive_award",
{
"initiator": "dignity",
"receiver": "on1x",
"custom_sequence": 0,
"memo": "telegram:151842302",
"shares": "58.246984 SHARES"
}
]
}
]
]
Пример:
{"id":1,"method":"call","jsonrpc":"2.0","params":["committee_api","get_committee_request",["39","1"]]}
Ответ:
{
"id": 38,
"request_id": 39,
"url": "https://control.viz.world/media/@wildviz/committee-1/",
"creator": "wildviz",
"worker": "wildviz",
"required_amount_min": "0.000 VIZ",
"required_amount_max": "10000.000 VIZ",
"start_time": "2019-09-23T15:49:39",
"duration": 432000,
"end_time": "2019-09-28T15:49:39",
"status": 5,
"votes_count": 23,
"conclusion_time": "2019-09-28T15:49:39",
"conclusion_payout_amount": "10000.000 VIZ",
"payout_amount": "10000.000 VIZ",
"remain_payout_amount": "0.000 VIZ",
"last_payout_time": "2019-09-28T15:56:42",
"payout_time": "2019-09-28T15:56:42",
"votes": [
{
"voter": "wildviz",
"vote_percent": 10000,
"last_update": "2019-09-23T15:52:09"
}
]
}
Пример:
{"id":1,"method":"call","jsonrpc":"2.0","params":["committee_api","get_committee_request_votes",["39"]]}
Ответ:
[
{
"voter": "wildviz",
"vote_percent": 10000,
"last_update": "2019-09-23T15:52:09"
},
{
"voter": "denis-golub",
"vote_percent": 10000,
"last_update": "2019-09-23T15:54:51"
},
{
"voter": "lex",
"vote_percent": 10000,
"last_update": "2019-09-23T15:54:51"
},
...
]
Пример:
{"id":1,"method":"call","jsonrpc":"2.0","params":["committee_api","get_committee_requests_list",["2"]]}
Ответ:
[
2,
3,
13,
14,
33
]
Пример:
{"id":1,"method":"call","jsonrpc":"2.0","params":["invite_api","get_invite_by_id",["0"]]}
Ответ:
{
"id": 0,
"creator": "denis-skripnik",
"receiver": "liveblogs",
"invite_key": "VIZ5bqU5UEig5o8gESpJCy662LLXQuZWHX49puiZtGhv3ZxmN6e3t",
"invite_secret": "5JopL2TpywM2WKx83aTLnwLZjZ58ZEnK7aBJ3L2V2KjaVA1Neko",
"balance": "0.000 VIZ",
"claimed_balance": "100.000 VIZ",
"create_time": "2018-10-05T02:12:57",
"claim_time": "2018-10-05T11:55:36",
"status": 2
}
Пример:
{"id":1,"method":"call","jsonrpc":"2.0","params":["invite_api","get_invites_list",["0"]]}
Ответ:
[
6,
8,
15,
20,
34,
...
]
Пример:
{"id":1,"method":"call","jsonrpc":"2.0","params":["operation_history","get_ops_in_block",["10913871","false"]]}
Ответ:
[
{
"trx_id": "d5f53163bc237b17c8fd6f3278d3ca1b4ae21691",
"block": 10913871,
"trx_in_block": 0,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2019-10-14T07:01:18",
"op": [
"transfer",
{
"from": "viz-social-bot",
"to": "on1x",
"amount": "7.725 VIZ",
"memo": "withdraw:3353"
}
]
},
{
"trx_id": "0122149bdfdbae80a6c3f4cabe36c78e0e20c12d",
"block": 10913871,
"trx_in_block": 1,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2019-10-14T07:01:18",
"op": [
"transfer",
{
"from": "viz-social-bot",
"to": "dance",
"amount": "6.827 VIZ",
"memo": "withdraw:3354"
}
]
},
{
"trx_id": "e8d8da85004234afd048979860d88cd78297ac1e",
"block": 10913871,
"trx_in_block": 2,
"op_in_trx": 0,
"virtual_op": 0,
"timestamp": "2019-10-14T07:01:18",
"op": [
"transfer",
{
"from": "viz-social-bot",
"to": "hypno",
"amount": "6.976 VIZ",
"memo": "withdraw:3355"
}
]
},
{
"trx_id": "0000000000000000000000000000000000000000",
"block": 10913871,
"trx_in_block": 65535,
"op_in_trx": 0,
"virtual_op": 1,
"timestamp": "2019-10-14T07:01:21",
"op": [
"witness_reward",
{
"witness": "wildviz",
"shares": "0.103999 SHARES"
}
]
}
]
Пример:
{"id":1,"method":"call","jsonrpc":"2.0","params":["operation_history","get_transaction",["d5f53163bc237b17c8fd6f3278d3ca1b4ae21691"]]}
Ответ:
{
"ref_block_num": 34889,
"ref_block_prefix": 453694329,
"expiration": "2019-10-14T07:11:20",
"operations": [
[
"transfer",
{
"from": "viz-social-bot",
"to": "on1x",
"amount": "7.725 VIZ",
"memo": "withdraw:3353"
}
]
],
"extensions": [],
"signatures": [
"203e778ae54b5fe51367bef34a7001eca7257732075723e99f07fd8c4cd0cc8040021925c811fef0ff2151a28193403af6af05f05dadc1427bb0daf604df53ee0c"
],
"transaction_id": "d5f53163bc237b17c8fd6f3278d3ca1b4ae21691",
"block_num": 10913871,
"transaction_num": 0
}
{"id":1,"method":"call","jsonrpc":"2.0","params":["paid_subscription_api","get_paid_subscriptions",[0,1]]}
Ответ:
[
{
"update_time" : "2019-06-05T15:07:57",
"creator" : "tratata",
"id" : 3,
"period" : 30,
"levels" : 10,
"url" : "",
"amount" : 500000
}
]
Пример:
{"id":1,"method":"call","jsonrpc":"2.0","params":["paid_subscription_api","get_active_paid_subscriptions",["on1x"]]}
Ответ:
[
"viz.world"
]
Пример:
{"id":1,"method":"call","jsonrpc":"2.0","params":["paid_subscription_api","get_paid_subscription_options",["viz.world"]]}
Ответ:
{
"creator": "viz.world",
"url": "https://control.viz.world/media/@viz.world/ru-service/",
"levels": 2,
"amount": 5000,
"period": 30,
"update_time": "2019-05-21T07:14:45",
"active_subscribers": [
"on1x",
"muz",
"litrbooh",
"antonkostroma",
"xchng",
"liveblogs",
"wildviz",
"id1club"
],
"active_subscribers_count": 8,
"active_subscribers_summary_amount": 65000,
"active_subscribers_with_auto_renewal": [
"on1x",
"muz",
"litrbooh",
"antonkostroma",
"liveblogs"
],
"active_subscribers_with_auto_renewal_count": 5,
"active_subscribers_with_auto_renewal_summary_amount": 35000
}
Пример:
{"id":1,"method":"call","jsonrpc":"2.0","params":["paid_subscription_api","get_paid_subscription_status",["on1x","viz.world"]]}
Ответ:
{
"subscriber": "on1x",
"creator": "viz.world",
"level": 2,
"amount": 5000,
"period": 30,
"start_time": "2019-09-18T07:51:39",
"next_time": "2019-10-18T07:51:39",
"end_time": "1969-12-31T23:59:59",
"active": true,
"auto_renewal": true
}
Пример:
{"id":1,"method":"call","jsonrpc":"2.0","params":["witness_api","get_active_witnesses",[]]}
Ответ:
[
"solox",
"lexai",
"xchng",
"creativity",
"jackvote",
"pom-vjfru0njnme",
"retroscope",
"wildviz",
"lex",
"dordoy",
"denis-skripnik",
"dmilash",
"id1club",
"lb",
"ae.witness",
"denis-golub",
"litrbooh",
"mad-max",
"t3",
"web3",
"charity"
]
Пример:
{"id":1,"method":"call","jsonrpc":"2.0","params":["witness_api","get_witness_count",[]]}
Ответ:
59
Пример:
{"id":1,"method":"call","jsonrpc":"2.0","params":["witness_api","get_witness_schedule",[]]}
Ответ:
{
"id": 0,
"current_virtual_time": "23461032266075892861574835409469",
"next_shuffle_block_num": 10916661,
"current_shuffled_witnesses": "736f6c6f780000000000000000000000000000000000000000000000000000006c657861690000000000000000000000000000000000000000000000000000007863686e6700000000000000000000000000000000000000000000000000000063726561746976697479000000000000000000000000000000000000000000006a61636b766f7465000000000000000000000000000000000000000000000000706f6d2d766a667275306e6a6e6d650000000000000000000000000000000000726574726f73636f70650000000000000000000000000000000000000000000077696c6476697a000000000000000000000000000000000000000000000000006c65780000000000000000000000000000000000000000000000000000000000646f72646f79000000000000000000000000000000000000000000000000000064656e69732d736b7269706e696b000000000000000000000000000000000000646d696c61736800000000000000000000000000000000000000000000000000696431636c7562000000000000000000000000000000000000000000000000006c6200000000000000000000000000000000000000000000000000000000000061652e7769746e657373000000000000000000000000000000000000000000006d61642d6d6178000000000000000000000000000000000000000000000000006c697472626f6f6800000000000000000000000000000000000000000000000070686f746f636c75620000000000000000000000000000000000000000000000743300000000000000000000000000000000000000000000000000000000000064656e69732d676f6c75620000000000000000000000000000000000000000006368617269747900000000000000000000000000000000000000000000000000",
"num_scheduled_witnesses": 21,
"median_props": {
"account_creation_fee": "1.000 VIZ",
"maximum_block_size": 65536,
"create_account_delegation_ratio": 10,
"create_account_delegation_time": 2592000,
"min_delegation": "1.000 VIZ",
"min_curation_percent": 0,
"max_curation_percent": 10000,
"bandwidth_reserve_percent": 0,
"bandwidth_reserve_below": "0.000000 SHARES",
"flag_energy_additional_cost": 0,
"vote_accounting_min_rshares": 50000,
"committee_request_approve_min_percent": 1000,
"inflation_witness_percent": 2000,
"inflation_ratio_committee_vs_reward_fund": 7500,
"inflation_recalc_period": 806400,
"data_operations_cost_additional_bandwidth": 0,
"witness_miss_penalty_percent": 100,
"witness_miss_penalty_duration": 86400
},
"majority_version": "2.4.0"
}
Пример:
{"id":1,"method":"call","jsonrpc":"2.0","params":["witness_api","get_witnesses",[["0"]]]}
Ответ:
[
{
"id": 0,
"owner": "committee",
"created": "1970-01-01T00:00:00",
"url": "",
"votes": 0,
"penalty_percent": 0,
"counted_votes": 0,
"virtual_last_update": "0",
"virtual_position": "0",
"virtual_scheduled_time": "340282366920938463463374607431768211455",
"total_missed": 15,
"last_aslot": 144669,
"last_confirmed_block_num": 132749,
"signing_key": "VIZ1111111111111111111111111111111114T1Anm",
"props": {
"account_creation_fee": "1.000 VIZ",
"maximum_block_size": 131072,
"create_account_delegation_ratio": 10,
"create_account_delegation_time": 2592000,
"min_delegation": "0.001 VIZ",
"min_curation_percent": 1600,
"max_curation_percent": 1600,
"bandwidth_reserve_percent": 1000,
"bandwidth_reserve_below": "500.000000 SHARES",
"flag_energy_additional_cost": 0,
"vote_accounting_min_rshares": 5000000,
"committee_request_approve_min_percent": 1000,
"inflation_witness_percent": 2000,
"inflation_ratio_committee_vs_reward_fund": 5000,
"inflation_recalc_period": 806400,
"data_operations_cost_additional_bandwidth": 0,
"witness_miss_penalty_percent": 100,
"witness_miss_penalty_duration": 86400
},
"last_work": "0000000000000000000000000000000000000000000000000000000000000000",
"running_version": "1.0.1",
"hardfork_version_vote": "0.0.0",
"hardfork_time_vote": "1970-01-01T00:00:00"
}
]
Пример:
{"id":1,"method":"call","jsonrpc":"2.0","params":["witness_api","get_witnesses_by_vote",["","10000"]]}
Ответ:
[
{
"id": 42,
"owner": "solox",
"created": "2018-12-22T19:09:51",
"url": "http://viz.world/@solox/witness/",
"votes": "2265575784725",
"penalty_percent": 0,
"counted_votes": "2265575784725",
"virtual_last_update": "23453249838245982754942708691144",
"virtual_position": "0",
"virtual_scheduled_time": "23453400035105083671049497423272",
"total_missed": 273,
"last_aslot": 10942638,
"last_confirmed_block_num": 10916550,
"signing_key": "VIZ8MzGnSUeqbFaFr8g297XNDT7iWQZ8ktBgeBDYj1moWCHQ8a5PA",
"props": {
"account_creation_fee": "1.000 VIZ",
"maximum_block_size": 65536,
"create_account_delegation_ratio": 10,
"create_account_delegation_time": 2592000,
"min_delegation": "1.000 VIZ",
"min_curation_percent": 0,
"max_curation_percent": 10000,
"bandwidth_reserve_percent": 100,
"bandwidth_reserve_below": "1.000000 SHARES",
"flag_energy_additional_cost": 0,
"vote_accounting_min_rshares": 50000,
"committee_request_approve_min_percent": 1000,
"inflation_witness_percent": 3000,
"inflation_ratio_committee_vs_reward_fund": 7500,
"inflation_recalc_period": 806400,
"data_operations_cost_additional_bandwidth": 0,
"witness_miss_penalty_percent": 100,
"witness_miss_penalty_duration": 86400
},
"last_work": "0000000000000000000000000000000000000000000000000000000000000000",
"running_version": "2.4.0",
"hardfork_version_vote": "2.4.0",
"hardfork_time_vote": "2019-04-30T05:00:00"
}
]
Пример:
{"id":1,"method":"call","jsonrpc":"2.0","params":["witness_api","lookup_witness_accounts",["","4"]]}
Ответ:
[
"t3",
"lb",
"ae",
"in"
]
Для разработки приложений зачастую используют уже готовые библиотеки. Наличие библиотеки для конкретного языка программирования зависит от наличия заготовок для работы с криптографией, большими числами и траспортными протоколами (http/ws). Так как VIZ исторически эволюционировал из Graphene, то большинство библиотек для таких блокчейн систем, как EOS/Steem/Golos — подходят и для VIZ. Отличительной особенностью является формат общения с нодой (структура json-rpc), порядок и наименование параметров при описании операции, формат сложных данных в бинарном виде (например формат ассетов VIZ и SHARES отличается от нового формата SMT в Steem).
Несмотря на разный синтаксис — основа для взаимодействия с VIZ одна: криптография для ключей и подписи сообщений, проверка подписи по данным и публичному ключу, формирование транзакций, взаимодейстие с нодой.
Каждый разработчик может поднять свою ноду для взаимодействия с VIZ, но для начинающих разбираться существуют публичные ноды:
Ниже перечислины основные библиотеки VIZ, которые поддерживают большинство API запросов к ноде и формирование транзакций.
Фаворит для разработки приложений библиотека viz-js-lib. В нем есть поддержка всего что нужно как для серверного (nodejs), так и для пользовательского (js в браузерах) взаимодействия с VIZ:
Документация viz-js-lib на английском языке доступна на GitHub. Примеры для часто использумых операций смотрите в разделе Примеры кода.
Библиотека thallid-viz от ksantoprotein поддерживает как API запросы, так и формирование транзакций. В наличии множество примеров с разными операциями.
Библиотека viz-python-lib поддерживает большинство необходимого, но отсутствуют примеры и документация (библиотека пока недоделана).
После перехода на адаптированные библиотеки BigNumber и Elliptic Curve стало доступным использовать криптографию без сборки secp256k1 для PHP и включения поддержки GMP.
Библиотека viz-php-lib поддерживает JsonRPC, работу с ключами, формирование транзакций, шифрование сообщение через shared key (совместимое с viz-js-lib), присутствуют примеры, поддержка PSR-4 и установка без дополнительных зависимостей (all-in-one).
Библиотека php-graphene-node-client с поддержкой VIZ, установка которого возможна через Composer.
Библиотека viz-go-lib отлично подходит для API запросов и изучения формирования транзакций. К сожалению документации по библиотеке нет, как и примеров с отдельными операциями.
Библиотека viz-swift-lib — библиотека на Swift, установка которой возможна через Swift Package Manager.
Криптографическая библиотека viz_dart_ecc — позволяет формировать публичный ключ из приватного, подписывать данные, верифицировать подпись.
Библиотека viz-transaction — позволяет формировать и подписывать транзакции для блокчейна VIZ (библиотека не содержит методов для трансляции транзакции блокчейн-ноде, для этого нужно использовать любые другие библиотеки для http/ws протокола).
Если вы не нашли требуемый язык программирования, то можно обратить внимание на существующие библиотеки для EOS и Steem. Чтобы модифицировать их и получить поддержку VIZ достаточно проверить формат json-rpc запросов, поменять chain_id (в VIZ он равен 2040effda178d4fffff5eab7a915d4019879f5205cc5392e4bcced2b6edda0cd
— это префикс для подписи сырых транзакций) и настроить конструктор операций.
Начинающим разработчикам всегда рекомендуется прочесть документации по той или иной библиотеке. Это помогает как понять работу библиотеки, так и запомнить возможности, которые можно использовать при разработке приложений. В данном разделе описаны наиболее популярные запросы в виде примеров. Наиболее используемая библиотека для приложений VIZ — viz-js-lib, поэтому примеры будут с её использованием.
Подробная документация на английском с указанием всех методов и их аттрибутов доступно по ссылке.
В зависимости от серверного (nodejs) или браузерного (js) использования библиотеку нужно подключать разными способами.
Для nodejs актуальной инструкцией будет установка библиотеки через npm install viz-js-lib --save
и подключением её в js файле через var viz = require('viz-js-lib');
.
Для js подключения можно либо самому собрать webpack библиотеки через консоль npm build
, либо воспользоваться уже собранной библиотекой от jsDelivr CND или Unpkg CDN. Просто добавьте к html файлу тэг script и укажите url библиотеки: <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/viz-js-lib@latest/dist/viz.min.js"></script>
, после чего у вас будет доступ через консоль к глобальной переменной viz
.
Пока у вашего приложения нет большого потока пользователей, разумно использовать доступную публичную ноду. На момент написания статьи в VIZ доступно две публичные ноды:
https://viz.lexa.host/
для JSON-RPC запросов через HTTPS и wss://viz.lexa.host/ws
для JSON-RPC запросов через WebSocket over SSL;https://solox.world/
для JSON-RPC запросов через HTTPS и wss://solox.world/ws
для JSON-RPC запросов через WebSocket over SSL.Пример настройки viz для работы с нодой https://viz.lexa.host/
:
var api_gate='https://viz.lexa.host/';
viz.config.set('websocket',api_gate);
В разделе Плагины и их API были перечислены основные плагины и запросы к ним — все они доступны в библиотеке viz-js-lib. Для того, чтобы выполнить тот или иной запрос, достаточно перевести его название в CamelCase.
Например, если вы решили выполнить запрос get_database_info к плагину database_api, то вам необходимо выполнить код:
viz.api.getDatabaseInfo(function(err,response){
if(!err){
//получен ответ
console.log(response);
}
else{
//ошибка
console.log(err);
}
});
В случае, если запрос требует входных данных, то вы добавляете их в начало вызова. Например, для запроса get_active_paid_subscriptions к плагину paid_subscription_api, необходимо указать пользователя, для которого будет произведен поиск активных платных подписок:
var subscriber='on1x';
viz.api.getActivePaidSubscriptions(subscriber,function(err,response){
if(!err){
//получен ответ
console.log(response);
}
else{
//ошибка
console.log(err);
}
});
Для каждой операции из протокола VIZ существует отдельный метод в библиотеке viz-js-lib, который принимает приватный ключ (для подписи транзакции) и параметры операции. Название операции, аналогично API методам, должно быть переведено в формат CamelCase. Пример кода для трансляции (broadcast) операции account_metadata (запись в блокчейн мета-данных аккаунта):
var regular_key='5K...';//приватный ключ
var user_login='test';//логин аккаунта
var metadata={'name':'Тестовый аккаунт','photo':'https://cdn.pixabay.com/photo/2015/12/06/14/14/tokyo-1079524_960_720.jpg'};
viz.broadcast.accountMetadata(regular_key,user_login,JSON.stringify(metadata),function(err,result){
if(!err){
//транзакция принята публичной нодой
console.log(result);
}
else{
//нода не приняла транзакцию
console.log(err);
}
});
Часть новичков хотят периодически опрашивать ноду и получать актуальные данные о DGP (Dynamic Global Properties), чтобы на основе этого показывать новые блоки, исполнять условия по необратимому блоку или подсвечивать в списке делегатов последнего, кто подписал блок. Для этого достаточно запрашивать данные по таймеру каждые 3 секунды (время между блоками):
var dgp={}
function update_dgp(auto=false){
viz.api.getDynamicGlobalProperties(function(err,response){
if(!err){
dgp=response;
}
});
if(auto){
setTimeout("update_dgp(true)",3000);
}
}
update_dgp(true);
Пример кода, для получения информации об аккаунте и рассчета актуального значения энергии аккаунта (учитывая скорость её восстановления):
var current_user='on1x';
viz.api.getAccounts([current_user],function(err,response){
if(!err){
//получен ответ
if(typeof response[0] !== 'undefined'){
//мы запросили массив аккаунтов, смотрим нулевой элемент, соответствующий current_user
let last_vote_time=Date.parse(response[0].last_vote_time);
//учитываем временную зону пользователя
let delta_time=parseInt((new Date().getTime() - last_vote_time + (new Date().getTimezoneOffset()*60000))/1000);
let energy=response[0].energy;
//рассчитываем востановленную энергию
//скорость восстановления энергии от 0% до 100% CHAIN_ENERGY_REGENERATION_SECONDS 5 дней (432000 секунд)
let new_energy=parseInt(energy+(delta_time*10000/432000));
//энергии не может быть больше 100%
if(new_energy>10000){
new_energy=10000;
}
console.log('актуальная энергия аккаунта',new_energy);
}
else{
console.log('аккаунт не найден',current_user);
}
}
else{
//ошибка
console.log(err);
}
});
Криптографические ключи представляют собой координаты X и Y которые записаны в общепринятом формате DER на эллептической кривой secp256k1 (в качестве хеш-функции служит SHA-256). В библиотеке viz-js-lib преобразования и работа с ключами относятся к модулю viz.auth
.
В Graphene экосистеме придумали механизм человекочитаемых паролей. По причинам перебора использовать их не рекомендуется, поэтому, чтобы усложнить множественный перебор приватных ключей к публичному пришли к определенным правилам формирования ключей в виде конкатенации строк: логин аккаунта, пароль (сложный), тип доступа.
Часть приложений условились использовать эти правила, таким образом упрощая доступ пользователем к разным возможностям аккаунта по общему паролю. Например, пользователь test зарегистрирован используя общий пароль PK3452JENDK332. При авторизации в приложении используя эти логин и пароль, приложение может самостоятельно сформировать ключи нужного типа доступа, просто используя конкатенацию строк. Пользователь хочет перевести токены? Приложение формирует налету приватный активный ключ по строке testPK3452JENDK332active. Пользователь награждает кого-то? Приложение формирует приватный регулярный ключ по строке testPK3452JENDK332regular. Это упрощает доступ для пользователя по общему паролю, но лишает гибкости и подвергает опасности аккаунт. Типы доступа имеют разные полномочия и при компрометации доверенного окружения доверенного окружения пользователя или сайта доступ к аккаунту может быть перехвачен. Поэтому часть приложений отказываются от правил или договоренностей ради безопасности пользователей и не поддерживают общий пароль.
Обычно, при регистрации пользователя, приложение генерирует пароль самостоятельно. Но бывает исключения, когда приложение позволяет использовать свой пароль для генерации ключей. Библиотека позволяет самостоятельно указать строки для генерации ключа в методе viz.auth.toWif(account_login,general_pass,auth_type);
. В примере ниже представлена функция для генерации случайного пароля заданной длины и генерации ключа по нему без привязки к пользователю и типу доступа:
function pass_gen(length=100,to_wif=true){
let charset='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+-=_:;.,@!^&*$';
let ret='';
for (var i=0,n=charset.length;i<length;++i){
ret+=charset.charAt(Math.floor(Math.random()*n));
}
if(!to_wif){
return ret;
}
let wif=viz.auth.toWif('',ret,'');
return wif;
}
Получить публичный ключ по заданному приватному можно методом viz.auth.wifToPublic(wif)
. Для тех приложений, которые хотят формировать ключи по кокатенации логина, пароля и типа доступа, существует метод viz.auth.getPrivateKeys(account_login,general_pass,auth_types);
. Метод возвращает массив по шаблону result.type для приватных ключей (которые нужно передать пользователю) и result.typePubkey для публичных ключей (которые нужно транслировать в блокчейн для сохранения за аккаунтом пользователя). Код функции для регистрации нового аккаунта в VIZ по главному паролю:
var user_login='test';//аккаунт регистратор
var active_key='5K...';//приватный активный ключ
function create_account_with_general_pass(account_login,token_amount,shares_amount,general_pass){
let fixed_token_amount=''+parseFloat(token_amount).toFixed(3)+' VIZ';//токены, которые будут переведены в долю новому аккаунту
let fixed_shares_amount=''+parseFloat(shares_amount).toFixed(6)+' SHARES';//доля, которая будет делегирована новому аккаунту
if(''==token_amount){
fixed_token_amount='0.000 VIZ';
}
if(''==shares_amount){
fixed_shares_amount='0.000000 SHARES';
}
let auth_types = ['regular','active','master','memo'];//типы доступов
let keys=viz.auth.getPrivateKeys(account_login,general_pass,auth_types);
//типы доступов содержат публичные ключи с весом, достаточным для совершения операций
let master = {
'weight_threshold': 1,
'account_auths': [],
'key_auths': [
[keys.masterPubkey, 1]
]
};
let active = {
'weight_threshold': 1,
'account_auths': [],
'key_auths': [
[keys.activePubkey, 1]
]
};
let regular = {
"weight_threshold": 1,
"account_auths": [],
"key_auths": [
[keys.regularPubkey, 1]
]
};
let memo_key=keys.memoPubkey;
let json_metadata='';
let referrer='';
viz.broadcast.accountCreate(active_key, fixed_token_amount, fixed_shares_amount, user_login, account_login, master, active, regular, memo_key, json_metadata, referrer, [],function(err,result){
if(!err){
console.log('VIZ Account: '+account_login+'\r\nGeneral pass (for private keys): '+general_pass+'\r\nPrivate master key: '+keys.master+'\r\nPrivate active key: '+keys.active+'\r\nPrivate regular key: '+keys.regular+'\r\nPrivate memo key: '+keys.memo+'');
}
else{
console.log(err);
}
});
}
В блокчейне VIZ есть механика ваучеров. Их может создать кто угодно, переведя в него токены VIZ. Ваучеры можно погасить или использовать в качестве инвайт-кода для упрощенной регистрации нового аккаунта. В первом случае токены поступают на счет аккаунта предъявителя, во втором случае токены конвертируются в долю сети для нового аккаунта (с единым ключом доступа для всех типов доступа).
Пример кода для создания ваучера:
var user_login='test';
var active_key='5K...';//приватный активный ключ
var fixed_amount='100.000 VIZ';//количество токенов затраченные на ваучер
var private_key=pass_gen();//генерируем приватный ключ
var public_key=viz.auth.wifToPublic(private_key);//получаем публичный ключ из приватного
viz.broadcast.createInvite(active_key,user_login,fixed_amount,public_key,function(err,result){
if(!err){
console.log('Ваучер создан, публичный ключ для проверки: '+public_key+', приватный ключ для использования: '+private_key);
}
else{
console.log(err);
}
});
Для анонимного использования инвайт-кода в системе существует аккаунт invite
с приватным активным ключом 5KcfoRuDfkhrLCxVcE9x51J6KN9aM9fpb78tLrvvFckxVV6FyFW
, пример кода:
var receiver='newtestaccount';//логин нового аккаунта
var secret_key='5K...';//приватный ключ инвайт-кода
var private_key=pass_gen();//генерируем приватный ключ
var public_key=viz.auth.wifToPublic(private_key);//получаем публичный ключ из приватного
viz.broadcast.inviteRegistration('5KcfoRuDfkhrLCxVcE9x51J6KN9aM9fpb78tLrvvFckxVV6FyFW','invite',receiver,secret_key,public_key,function(err,result){
if(!err){
console.log('Аккаунт '+receiver+' зарегистрирован, общий приватный ключ для всех типов доступа: '+private_key);
}
else{
console.log(err);
}
});
Для анонимного использования ваучера в системе существует аккаунт invite
с приватным активным ключом 5KcfoRuDfkhrLCxVcE9x51J6KN9aM9fpb78tLrvvFckxVV6FyFW
, пример кода:
var receiver='test';//логин аккаунта
var secret_key='5K...';//приватный ключ инвайт-кода
var public_key=viz.auth.wifToPublic(secret_key);//получаем публичный ключ ваучера
viz.broadcast.claimInviteBalance('5KcfoRuDfkhrLCxVcE9x51J6KN9aM9fpb78tLrvvFckxVV6FyFW','invite',receiver,secret_key,function(err,result){
if(!err){
console.log('Аккаунт '+receiver+' успешно погасил ваучер с публичным ключом '+public_key);
}
else{
console.log(err);
}
});
Для автоматической конвертации всех доступных токенов VIZ в долю сети SHARES необходимо запросить информацию об аккаунте и конвертировать их себе же в долю операцией transfer_to_vesting:
var current_user='test';
var active_key='5K...';//приватный активный ключ
viz.api.getAccounts([current_user],function(err,response){
if(!err){
//получен ответ
if(typeof response[0] !== 'undefined'){
if('0.000 VIZ'!=response[0].balance){
viz.broadcast.transferToVesting(active_key,current_user,current_user,response[0].balance,function(err,result){
if(!err){
console.log('конвертация в долю сети',response[0].balance);
console.log(result);
}
else{
console.log(err);
}
});
}
else{
console.log('баланс на нуле');
}
}
else{
console.log('аккаунт не найден',current_user);
}
}
else{
//ошибка
console.log(err);
}
});
Часто новые разработчики сталкиваются с проблемой, что пользователь делегировал часть токенов другому аккаунту и нужно рассчитать доступную долю для конвертации SHARES в VIZ. Пример кода, который автоматически ставит доступные для конвертации SHARES на вывод из доли:
var current_user='test';
var active_key='5K...';//приватный активный ключ
viz.api.getAccounts([current_user],function(err,response){
if(!err){
//получен ответ
if(typeof response[0] !== 'undefined'){
vesting_shares=parseFloat(response[0].vesting_shares);
delegated_vesting_shares=parseFloat(response[0].delegated_vesting_shares);
shares=vesting_shares - delegated_vesting_shares;
let fixed_shares=''+shares.toFixed(6)+' SHARES';
console.log('доступные SHARES для конвертации',fixed_shares);
viz.broadcast.withdrawVesting(active_key,current_user,fixed_shares,function(err,result){
if(!err){
console.log('запуск конвертации доли сети в токены VIZ',fixed_shares);
console.log(result);
}
else{
console.log(err);
}
});
}
else{
console.log('аккаунт не найден',current_user);
}
}
else{
//ошибка
console.log(err);
}
});
Пример перевода 1.000 VIZ из баланса аккаунта в комитет:
var current_user='test';
var active_key='5K...';//приватный активный ключ
var target='committee';
var fixed_amount='1.000 VIZ';
var memo='Заметка';//utf-8 включая emoji
viz.broadcast.transfer(active_key,current_user,target,fixed_amount,memo,function(err,result){
if(!err){
//получен ответ
console.log(result);
}
else{
//ошибка
console.log(err);
}
});
Аккаунт может наградить другого участника сети используя операцию award. Можно указать цель награды target, причину (номер custom_sequence или заметку memo), а также бенефициаров (аккаунты, которые разделят награду цели). Пример:
var current_user='test';//аккаунт награждающего
var regular_key='5K...';//приватный обычный ключ награждающего
var target='viz.plus';//цель награды - заказчик статьи
var energy='1000';//10.00% будут потрачены из актуальной энергии аккаунта
var custom_sequence=0;//номер custom операции
var memo='спасибо за viz cookbook';//utf-8 включая emoji
var beneficiaries_list=[{"account":"on1x","weight":2000}];//20% автору статьи
viz.broadcast.award(regular_key,current_user,target,energy,custom_sequence,memo,beneficiaries_list,function(err,result){
if(!err){
//получен ответ
console.log(result);
}
else{
//ошибка
console.log(err);
}
});
Бывает, что есть необходимость сменить доступы у аккаунта. Это может быть по причине добавления нового ключа, делегирование управления или создание условий для мульти-подписного управления аккаунтом (когда для выполнения операций требуется подпись несколькими ключами).
Полномочия для выполнения операций имеют следующую структуру:
Система проверяет транзакцию, операции в ней, проверяет наличие подписей задейственных аккаунтов и достаточным ли весом они обладают, для совершения действия положенного типа.
Пример полномочий с одним ключом:
{
"weight_threshold": 1,
"account_auths": [],
"key_auths": [
["VIZ6LWhhUzKmYM5VcxtC2FpEjzr71imfb7DeGA9yodeqnvtP2SYjA", 1]
]
}
Если у аккаунта будет записаны эти полномочия в тип доступа regular, то для совершения операции награждения блокчейн будет требовать подпись транзакции ключом 5KRLZitDd5c9uZzDgTMF4se4eVewENtZ29GbCuKwbT3msRbtLgi
(которому соответствует указанный в полномочиях публичный ключ VIZ6LWhhUzKmYM5VcxtC2FpEjzr71imfb7DeGA9yodeqnvtP2SYjA
).
При делегировании управления другому аккаунту, например test
, необходимо изменить полномочия на:
{
"weight_threshold": 1,
"account_auths": [
["test", 1]
],
"key_auths": [
["VIZ6LWhhUzKmYM5VcxtC2FpEjzr71imfb7DeGA9yodeqnvtP2SYjA", 1]
]
}
После этого блокчейн будет требовать подпись либо указанным ключом, либо ключом аналогичного типа доступа аккаунта test
.
Мульти-подписное управление предполагает усложнение полномочий, например для управления 2 из 3 можно использовать полномочие:
{
"weight_threshold": 4,
"account_auths": [],
"key_auths": [
["VIZ6LWhhUzKmYM5VcxtC2FpEjzr71imfb7DeGA9yodeqnvtP2SYjA", 2],
["VIZ5mK1zLnYHy7PbnsxRpS4NbKjEoH2J9eBmgSjVKJ5BKQpLLj9T4", 2],
["VIZ4uiqeDPsoteSFVbTWPBUbmfzxYkJyXYmA6B1pAFWZ59n4iBuUK", 2]
]
}
Для того чтобы транзакция была принята блокчейном, необходимо добавить подписи как минимум 2 из 3 указанных ключей. В этом примере публичному ключу VIZ4uiqeDPsoteSFVbTWPBUbmfzxYkJyXYmA6B1pAFWZ59n4iBuUK
соответствует приватный ключ 5KMBKopgd56MZvV8FYhp5AP7AWFyLKiybqRnZYgjXukw34VRE78
.
Рассмотрим пример сброса доступов к аккаунту (смену всех ключей и полномочий):
//функция требует приватный мастер ключ от аккаунта
function reset_account_with_general_pass(account_login,master_key,general_pass){
if(''==general_pass){
//если не указан общий пароль, сгенерируем его
general_pass=pass_gen(50,false);
}
let auth_types = ['regular','active','master','memo'];
let keys=viz.auth.getPrivateKeys(account_login,general_pass,auth_types);
let master = {
'weight_threshold': 1,
'account_auths': [],
'key_auths': [
[keys.masterPubkey, 1]
]
};
let active = {
'weight_threshold': 1,
'account_auths': [],
'key_auths': [
[keys.activePubkey, 1]
]
};
let regular = {
'weight_threshold': 1,
'account_auths': [],
'key_auths': [
[keys.regularPubkey, 1]
]
};
let memo_key=keys.memoPubkey;
viz.api.getAccounts([account_login],function(err,response){
if(0==response.length){
err=true;
}
if(!err){
let json_metadata=response[0].json_metadata;
viz.broadcast.accountUpdate(master_key,account_login,master,active,regular,memo_key,json_metadata,function(err,result){
if(!err){
console.log('Reset Account: '+account_login+'\r\nGeneral pass (for private keys): '+general_pass+'\r\nPrivate master key: '+keys.master+'\r\nPrivate active key: '+keys.active+'\r\nPrivate regular key: '+keys.regular+'\r\nPrivate memo key: '+keys.memo+'');
}
else{
console.log(err);
}
});
}
else{
console.log("Пользователь не найден");
}
});
}
У каждого делегата есть ключ подписи блоков (signing_key
), который отвечает за верификацию блокчейном подписи блока. Делегатская нода конфигурируется с приватным ключом подписи (лучше сформировать его заранее), а в блокчейн отправляется публичный ключ, для проверки подписей. В случае если делегат долго отсутствовал блокчейн отметит его как отключенного обнулив ключ подписи. Отключиться можно самостоятельно выставив пустой ключ подписи VIZ1111111111111111111111111111111114T1Anm
. Пример кода для объявления аккаунта делегатом:
var account_login='test';
var active_key='5K...';
var url='https://...';//ссылка на заявление о намерении быть делегатом
var private_key=pass_gen();//генерируем приватный ключ
var signing_key=viz.auth.wifToPublic(private_key);//публичный ключ
viz.broadcast.witnessUpdate(active_key,account_login,url,signing_key,function(err,result){
if(!err){
console.log('Делегат '+account_login+' заявил о желании быть делегатом, приватный ключ подписи блоков: '+private_key);
}
else{
console.log(err);
}
});
Для того чтобы делегат начал подписывать блоки, у него должен быть не нулевой вес вес голосов. Доля аккаунта при голосовании за нескольких делегатов делится между ними. Пример кода для голосования за делегата:
var account_login='test';
var active_key='5K...';
var witness_login='witness';
var value=true;//булево значение голоса (true - проголосовать за делегата, false - снять голос)
viz.broadcast.accountWitnessVote(active_key,account_login,witness_login,value,function(err,result){
if(!err){
console.log(result);
}
else{
console.log(err);
}
});
Если пользователь не принимает участие в выборе делегатов, он может делегировать право распоряжаться его долей другому аккаунту. Для этого существует операция account_witness_proxy
, ей может воспользоваться приложение регистратор, чтобы не загружать своего пользователя лишней информацией об устройстве блокчейн-системы и не терять своё влияние (так как для регистрации аккаунта могут затрачиваться токены VIZ, то приложение вкладывает в пользователей потенциал аналогиной доли сети).
Если пользователь решит самостоятельно участвовать в выборе делегатов, то первый же голос за делегата отменит прокси.
var account_login='test';
var active_key='5K...';
var proxy_login='proxy';
viz.broadcast.accountWitnessProxy(active_key,account_login,proxy_login,function(err,result){
if(!err){
console.log(result);
}
else{
console.log(err);
}
});
Делегаты транслируют свою позицию по голосуемым параметрам сети. Блокчейн система каждый цикл очереди делегатов (21 блок) вычисляет медианные значения голосуемых параметров и фиксирует их на этот цикл. Подробнее про голосуемые параметры сети можно прочесть в разделе Объекты и структуры в VIZ. Пример операции versioned_chain_properties_update
для трансляции делегатом голосуемых параметров сети:
var account_login='test';
var active_key='5K...';//приватный активный ключ
var props={};
props.account_creation_fee='1.000 VIZ';
props.create_account_delegation_ratio=10;
props.create_account_delegation_time=2592000 ;
props.bandwidth_reserve_percent=1;
props.bandwidth_reserve_below='1.000000 SHARES';
props.committee_request_approve_min_percent=1000;
props.flag_energy_additional_cost=1000;//устаревший параметр
props.min_curation_percent=0;//устаревший параметр
props.max_curation_percent=10000;//устаревший параметр
props.min_delegation='1.000 VIZ';
props.vote_accounting_min_rshares=5000000 ;
props.maximum_block_size=65536;
props.inflation_witness_percent=2000;
props.inflation_ratio_committee_vs_reward_fund=7500;
props.inflation_recalc_period=806400;
props.data_operations_cost_additional_bandwidth=0;
props.witness_miss_penalty_percent=100;
props.witness_miss_penalty_duration=86400;
viz.broadcast.versionedChainPropertiesUpdate(active_key,account_login,[2,props],function(err,result){
if(!err){
console.log(result);
}
else{
console.log(err);
}
});
var account_login='test';
var regular_key='5K...';//приватный регулярный ключ
var url='https://...';//URL с описанием заявки
var worker='test';//логин аккаунта, который в случае одобрения будет получать средства их фонда комитета
var min_amount='0.000 VIZ';//минимальное количество токенов VIZ для удовлетворения заявки
var max_amount='10000.000 VIZ';//максимальное количество токенов VIZ
var duration=5*24*3600;//длительность заявки в секундах (должно быть между COMMITTEE_MIN_DURATION (5 дней) и COMMITTEE_MAX_DURATION (30 дней))
viz.broadcast.committeeWorkerCreateRequest(regular_key,account_login,url,worker,min_amount,max_amount,duration,function(err,result){
if(!err){
console.log(result);
}
else{
console.log(err);
}
});
Отменить заявку может только её создатель.
var account_login='test';
var regular_key='5K...';//приватный регулярный ключ
var request_id=14;//номер отменяемой заявки комитета
viz.broadcast.committeeWorkerCancelRequest(regular_key,account_login,request_id,function(err,result){
if(!err){
console.log(result);
}
else{
console.log(err);
}
});
Проголосовать за заявку может любой участник сети. При завершении время действия заявки происходит расчет удовлетворенной суммы выплаты из комитета учитывающий долю голоса от общего веса проголосовавших и выставленный ими процент согласия с условиями заявки (от -100% до +100%). Если сумма доли сети проголосовавших превышает голосуемый параметр сети committee_request_approve_min_percent
и расчетная сумма входит в заложенные заявкой рамки, то заявка удовлетворяется комитетом и ставится в очередь на выплаты. Полную механику можно изучить в файле database.cpp метод committee_processing(). Пример кода с голосованием за заявку:
var account_login='test';
var regular_key='5K...';//приватный регулярный ключ
var request_id=15;//номер заявки комитета
var percent=8000;//80% процент от максимальной суммы заявки, на который считает правильным удовлетворить заявку голосующий
viz.broadcast.committeeVoteRequest(regular_key,account_login,request_id,percent,function(err,result){
if(!err){
console.log(result);
}
else{
console.log(err);
}
});
Система платных подписок в VIZ позволяет любому аккаунту задать условия соглашения, после подписания которого от подписчика будут перечисляться токены VIZ на баланс провайдера соглашения. Система позволяет изменять условия соглашения провайдеру и автоматически пытается согласовать их в момент экспирации и продления активных подписок без ущерба подписчику. Подписчик может указать единоразово он платит провайдеру по соглашению или согласен автоматически продливать подписку при наличии нужной суммы токенов VIZ на своем балансе. Публикация условий соглашения по платной подписке:
var account_login='test';
var active_key='5K...';//приватный активный ключ
var url='https://...';//URL с описанием платной подписки и соглашения
var levels=3;//количество уровней платной подписки (провайдер сам решает количество уровней и что будет предоставлять за каждый), если указать 0, то новые подписки не смогут быть оформлены
var amount='100.000 VIZ';//количество токенов VIZ за каждый уровень платной подписки
var period=30;//период действия подписки (количество дней)
viz.broadcast.setPaidSubscription(active_key,account_login,url,levels,amount,period,function(err,result){
if(!err){
console.log(result);
}
else{
console.log(err);
}
});
Аккаунт, желающий заключить соглашение о платной подписке должен подтвердить системе условия соглашения, желаемый уровень подписки и необходимость автоматического продления. Также можно изменить уровень подписки, система автоматически сделает пересчет и либо снимет необходимую сумму, либо продлит время для экспирации соглашения:
var account_login='subscriber';
var active_key='5K...';//приватный активный ключ
var provider_account='test';//логин аккаунта провайдера платной подписки
var level=2;//желаемый уровень платной подписки
var amount='100.000 VIZ';//количество токенов VIZ за каждый уровень по соглашению
var period=30;//период действия подписки по соглашению
var auto_renewal=true;//необходимость автоматического продления
viz.broadcast.paidSubscribe(active_key,account_login,provider_account,level,amount,period,auto_renewal,function(err,result){
if(!err){
console.log(result);
}
else{
console.log(err);
}
});
Получить информацию об условиях соглашения по платной подписке:
var provider_account='test';
viz.api.getPaidSubscriptionOptions(provider_account,function(err,response){
if(!err){
console.log(response);
}
else{
console.log(err);
}
});
Получить список активных или инактивных платных подписок можно API запросом:
var account_login='subscriber';
viz.api.getActivePaidSubscriptions(account_login,function(err,response){
for(let i in response){
console.log('Действует соглашение по платной подписке с аккаунтом '+response[i]);
}
}
viz.api.getInactivePaidSubscriptions(account_login,function(err,response){
for(let i in response){
console.log('Действует соглашение по платной подписке с аккаунтом '+response[i]);
}
}
Проверить действующее соглашение можно через API запрос:
var account_login='subscriber';
var provider_account='test';
viz.api.getPaidSubscriptionStatus(account_login,provider_account,function(err,response){
if(!err){
console.log('Соглашение с аккаунтом '+response.creator);
console.log('Статус соглашения: '+(response.active?'активное':'инактивное'));
console.log('Автопродление: '+(response.auto_renewal?'включено':'отключено'));
console.log('Уровень подписки: '+response.level);
console.log('Стоимость уровеня подписки: '+(response.amount/1000)+' VIZ');
console.log('Период подписки в днях: '+response.period);
console.log('Дата начала соглашения: '+response.start_time);
if(response.active){
console.log('Дата экспирации соглашения: '+response.next_time);
}
}
else{
console.log(err);
}
});
В блокчейне VIZ есть возможность делегировать долю (SHARES) другому аккаунту, что позволяет передать управление долей связанное с наградами, голосованием в комитете и получением пропускной способности сети (bandwidth). Делегирование не распространяется на голосование за делегатов, так как там присутствует отдельная операция в виде передачи права голоса всей своей доли операцией account_witness_proxy
.
Правила делегирования доли частично прописаны в протоколе сети, частично управляются делегатами:
При делегировании срабатывает защитный механизм от абуза двойной траты энергии. Если делегатор передает 50% от своей доли, то энергия будет уменьшена на 50%. Энергия в таком случае может стать отрицательной (до -100%). Стоит учитывать, что операция delegate_vesting_shares
задает фактическое значение делегированной доли. Если вы захотите отменить делегирование, значит надо задать значение делегированной доли 0.000000 SHARES
. Если вы хотите увеличить делегирование с 1000.000000 SHARES
до 3000.000000 SHARES
, то вам нужно просто задать значение делегирования 3000.000000 SHARES
.
Пример кода для делегирования доли другому аккаунту:
var account_login='test';
var active_key='5K...';//приватный активный ключ
var delegatee='recipient';//цель делегирования
var shares='100.000000 SHARES';
viz.broadcast.delegateVestingShares(active_key,account_login,delegatee,shares,function(err,result){
if(!err){
console.log(result);
}
else{
console.log(err);
}
});
Получение информации о делегировании:
var account_login='test';
var start_from=0;
var count=1000;
var type=0;//0 - исходящее делегирование, 1 - входящее делегирование
viz.api.getVestingDelegations(account_login,start_from,count,type,function(err,response){
if(!err){
if(0==response.length){
console.log('Нет записей о делегированной доле');
}
for(delegation in response){
console.log('Делегировано аккаунту '+response[delegation].delegatee+', '+response[delegation].vesting_shares+', можно отозвать после '+response[delegation].min_delegation_time);
}
}
});
Получение информации о возвращении делегированной доли после отмены делегирования:
var account_login='test';
var start_from=new Date().toISOString().substr(0,19);//поиск возврата делегированной доли после даты оформленной в формате ISO
var count=1000;
viz.api.getExpiringVestingDelegations(account_login,start_from,count,function(err,response){
if(!err){
if(0==response.length){
console.log('Нет записей о возвращаемой делегированной доле');
}
for(delegation in response){
console.log(response[delegation].vesting_shares+' вернется '+response[delegation].expiration);
}
}
});
Когда разработчикам нужно ввести свою структуру в блокчейн, сделать децентрализированное приложение (dApp), которое будет мониторить блоки и учитывать операции в сети — они могут использовать custom операции. Custom операция имеет гибкую структуру:
Разработчики могут сами придумать структуры данных, протокол команд и их учет через custom операции. Например, это может быть карточная игра, медиа-блоги, комментарии, каталог товаров или работа с рекламными блоками.
Пример использования custom операции:
var account_login='test';
var required_active_auths=[];
var required_regular_auths=[account_login];
var private_key='5K...';//приватный ключ нужного типа доступа (в данном случае regular)
var custom_name='file_app';
var custom_json='{"directory":"/photos/2020/viz_conf/","filename":"moscow_camp.jpg","url":"https://..."}';
viz.broadcast.custom(private_key,required_active_auths,required_regular_auths,custom_name,custom_json,function(err,result){
console.log(err,result);
});
Владелец аккаунта может выставить его на продажу используя master доступ:
var account_login='test';
var master_key='5K...';
var seller_login='reseller';//логин аккаунта который получит токены при успешной продаже аккаунта
var fixed_price_amount='10000.000 VIZ';//цена аккаунта
var on_sale=true;//выставить аккаунт на продажу (если false, то снять с продажи)
viz.broadcast.setAccountPrice(master_key,account_login,seller_login,fixed_price_amount,on_sale,function(err,result){
console.log(err,result);
});
Также можно выставить на продажу сабаккаунты (*.login
):
var account_login='test';
var master_key='5K...';
var seller_login='test';//логин аккаунта который получит токены при успешной продаже аккаунта
var fixed_price_amount='1000.000 VIZ';//цена сабаккаунта
var on_sale=true;//выставить сабаккаунты на продажу (если false, то снять с продажи)
viz.broadcast.setSubaccountPrice(master_key,account_login,seller_login,fixed_price_amount,on_sale,function(err,result){
console.log(err,result);
});
Купить аккаунт или сабаккаунт можно операцией buy_account
, система проверит возможность покупки и проведет сделку если это возможно:
var account_login='buyer';
var active_key='5K...';
var subaccount_login='enjoy.test';//покупка сабаккаунта у аккаунта test
var account_offer_price='1000.000 VIZ';//согласованная цена предложения (если цена изменится, блокчейн откажет в операции)
var private_key=pass_gen();//генерируем приватный ключ
var public_key=viz.auth.wifToPublic(private_key);//получаем публичный ключ из приватного
var token_to_shares='5.000 VIZ';//дополнительно потратить токены для конвертации в долю нового аккаунта
viz.broadcast.buyAccount(active_key,account_login,subaccount_login,account_offer_price,public_key,token_to_shares,function(err,result){
if(!err){
console.log('Покупка аккаунта '+account_login+' прошла успешно, приватный общий ключ '+private_key);
console.log(result);
}
else{
console.log(err);
}
});
Трехсторонние сделки работают по принципу проверки выполнения условий гарантом (agent
). Получатель и гарант должны подтвердить начало сделки операцией escrow_approve
(гарант получает комиссию на этом этапе), иначе при достижении даты дедлайна ратификации (ratification_deadline
) происходит возврат всех токенов инициатору сделки (метод expire_escrow_ratification
в файле database.cpp).
Если наступает момент спора, то отправитель или получатель могут инициировать разбирательство операцией escrow_dispute
, после чего принятие решения по сделке передается гаранту (именно он решает кто и сколько токенов получит). Если сделка повисла и долгое время не разрешается — договор истекает (escrow_expiration
) и всеми токенами управляет либо агент (если был открыт диспут), либо любая из сторон сделки.
Создание escrow перевода:
var account_login='test';
var active_key='5K...';
var receiver_login='receiver';
var token_amount='100.000 VIZ';//количество передаваемых токенов
var escrow_id=1;//номер escrow назначается вручную для согласования заявки (uint32)
var agent_login='agent';
var fee='10.000 VIZ';//комиссия гаранта
var json_metadata='{}';//дополнительные мета-данные в формате json
var ratification_deadline=new Date().toISOString().substr(0,19);//дата принудительной окончания сделки и возврата средств если гарант и получатель не подтвердили участие в сделке (дедлайн в формате ISO вида 2019-10-17T13:30:18)
var escrow_expiration=new Date().toISOString().substr(0,19);//дата окончания принятия решения по сделке, после чего невозможно инициировать спор (дедлайн в формате ISO вида 2019-10-17T13:30:18)
viz.broadcast.escrowTransfer(active_key,account_login,receiver_login,token_amount,escrow_id,agent_login,fee,json_metadata,ratification_deadline,escrow_expiration,function(err,result){
if(!err){
console.log(result);
}
else{
console.log(err);
}
});
Подтверждение участие в сделке по предложенным условиям (свое участие должны подтвердить гарант и получатель операцией escrow_approve
):
var account_login='test';
var receiver_login='receiver';
var agent_login='agent';
var escrow_id=1;//номер escrow назначается вручную для согласования заявки (uint32)
var who=agent_login;//кто подтверждает свое участие
var active_key='5K...';//ключ подтверждающий стороны (who)
var approve=true;//false в случае отказа от участия в сделке
viz.broadcast.escrowApprove(active_key,account_login,receiver_login,agent_login,who,escrow_id,approve,function(err,result){
if(!err){
console.log(result);
}
else{
console.log(err);
}
});
Требование диспута (открыть спор по сделке может отправитель или получатель операцией escrow_dispute
):
var account_login='test';
var receiver_login='receiver';
var agent_login='agent';
var escrow_id=1;//номер escrow назначается вручную для согласования заявки (uint32)
var who=receiver_login;//кто подтверждает свое участие
var active_key='5K...';//ключ подтверждающий стороны (who)
viz.broadcast.escrowDispute(active_key,account_login,receiver_login,agent_login,who,escrow_id,function(err,result){
if(!err){
console.log(result);
}
else{
console.log(err);
}
});
Отпустить средства (операция escrow_release
):
var account_login='test';
var receiver_login='receiver';
var agent_login='agent';
var escrow_id=1;//номер escrow назначается вручную для согласования заявки (uint32)
var token_amount='100.000 VIZ';//количество передаваемых токенов
var who=receiver_login;//кто решил отпустить средства (если открыт диспут, то только гарант решает кому и сколько перевести)
var active_key='5K...';//ключ инициатора операции (who)
var receiver=account_login;//получателем средств может быть только другая сторона сделки до истечения срока сделки, иначе — любая из сторон сделки
viz.broadcast.escrowRelease(active_key,account_login,receiver_login,agent_login,who,receiver,escrow_id,token_amount,function(err,result){
if(!err){
console.log(result);
}
else{
console.log(err);
}
});
Чтобы получить информацию об escrow, необходимо вызвать API запрос get_escrow
плагина database_api
:
var account_login='test';
var escrow_id=1;
viz.api.getEscrow(account_login,escrow_id,function(err,response){
if(!err){
//получен ответ
console.log(response);
}
else{
//ошибка
console.log(err);
}
});
При создании аккаунта создатель записывается в поле аккаунта recovery
как доверенное лицо для восстановления доступа к аккаунта, в случае его взлома и смены ключей доступа. Блокчейн ведет записи по смене мастер полномочий и хранит их 30 дней. Именно в этот период в 30 дней доверенный аккаунт может создать запрос на восстановление доступа операцией request_account_recovery
:
var recovery_account='escrow';
var active_key='5K...';
var account_to_recover='test';
var private_key=pass_gen();//генерируем приватный ключ (передаем владельцу аккаунта или согласовываем с ним публичный ключ для мастер полномочий)
var public_key=viz.auth.wifToPublic(private_key);//получаем публичный ключ из приватного
var new_master_authority={
'weight_threshold': 1,
'account_auths': [],
'key_auths': [
[public_key, 1]
]
};//новые мастер полномочия
var extensions=[];//дополнительное поле, не используется
viz.broadcast.requestAccountRecovery(active_key,recovery_account,account_to_recover,new_master_authority,extensions,function(err,result) {
console.log(err,result);
});
После того как запрос на смену доступов создан его должен подтвердить аккаунт операцией recover_account
. Важный момент заключается в том, что транзакцию надо подписать сразу двумя ключами — старым и новым из заявки от доверительного аккаунта, поэтому нужно сформировать транзакцию используя viz.broadcast.send
:
var account_login='test';
var new_master_key='5K...';//новый приватный мастер ключ
var new_master_public_key=viz.auth.wifToPublic(new_master_key);//публичный ключ из приватного мастер ключа (согласован с доверенным аккаунтом)
var new_master_authority={
'weight_threshold': 1,
'account_auths': [],
'key_auths': [
[new_master_public_key, 1]
]
};//новые мастер полномочия
var recent_master_key='5K...';//старый приватный мастер ключ для доказательства идентификации
var recent_master_public_key=viz.auth.wifToPublic(recent_master_key);//старый публичный мастер ключ
var recent_master_authority={
'weight_threshold': 1,
'account_auths': [],
'key_auths': [
[recent_master_public_key, 1]
]
};//старые мастер полномочия как доказательство идентификации
var extensions=[];//дополнительное поле, не используется
var operations=['recover_account',['account_to_recover':account_login,'new_master_authority':new_master_authority,'recent_master_authority':recent_master_authority,'extensions':extensions]];
var tx={'extensions':[],operations};
viz.broadcast.send(tx,[recent_master_key,new_master_key],function(err,result) {
console.log(err,result);
});
Поменять доверенный аккаунт для восстановления доступа можно операцией change_recovery_account
, изменения вступят через 30 дней (чтобы исключить абуз):
var account_login='test';
var master_key='5K...';//мастер ключ
var new_recovery_account='escrow';//новый доверенный аккаунт
var extensions=[];//дополнительное поле, не используется
viz.broadcast.changeRecoveryAccount(master_key,account_login,new_recovery_account,extensions,function(err,result) {
console.log(err,result);
});
Для управления системой предложенных операций существует 3 операции: создания предложения, предоставление подписи (обновление), удаление предложения. После создания предложения блокчейн система будет ожидать все необходимые подписи для осуществления операций заложенных внутрь предложения, после чего выполнит их. Если подошел срок истечения, то предложение не будет выполнено. Системой предложенных операций пользуются для управления мультиподписными аккаунтами. Для создания предложения используйте операцию proposal_create
:
var account_login='test';
var active_key='5K...';//активный приватный ключ
var title='payments-14';//наименование предложения (играет роль идентификатора, должно быть уникальным)
var memo='Платежи по договору №14';
var expiration_date=new Date();//дата экспирации, когда предложенные операции будут отклонены или выполнены при получении всех необходимых подписей
expiration_date.setDate(expiration_date.getDate() + 10);//плюс десять дней от текущего момента
var expiration_time=expiration_date.toISOString().substr(0,19);//дата истечения предложения в формате ISO
console.log('expiration_time',expiration_time);
var proposed_operations=[];
proposed_operations.push({'op':['transfer',{'from':'escrow','to':'test','amount':'10.000 VIZ','memo':memo}]});
proposed_operations.push({'op':['transfer',{'from':'escrow2','to':'test','amount':'10.000 VIZ','memo':memo}]});
var review_period_date=new Date(expiration_date.getTime() - 10);//дата прекращения приема подписей (минус 10 секунд до даты экспирации)
var review_period_time=review_period_date.toISOString().substr(0,19);//дата истечения предложения в формате ISO
console.log('review_period_time',review_period_time);
var extensions=[];
viz.broadcast.proposalCreate(active_key,account_login,title,memo,expiration_time,proposed_operations,review_period_time,extensions,function(err,result){
console.log(err,result);
});
Чтобы получить информацию о предложениях сделанных пользователем, нужно выполнить API запрос get_proposed_transactions
к плагину database_api
(будет возвращен массив предложений):
var looking_account='test';
var from=0;
var limit=100;
viz.api.getProposedTransactions(looking_account,from,limit,function(err,response){
console.log(err,response);
});
Система предложений поддерживает все существующие операции в блокчейне, но не позволяет смешивать операции требующие разных полномочий (например, операции tranfer
, требующие active полномочия, и операции award
, требующие regular полномочия). Предоставить подпись нужного типа доступа можно операцией proposal_update
указав логин подписавшего транзакцию в массиве на добавление или удаления из списка подтвердивших предложение (для этого есть 4 типа массивов: active, master, regular и key для использования одиночных ключей). Как только предоставлены все необходимые подписи — операции из предложения будут исполнены (при условии, что не указан период review_period_time
), в случае ошибки выполнения повторная попытка будет предпринята при экспирации. Пример:
var account_login='escrow';
var active_key='5K...';//активный приватный ключ
var proposal_author='test';//автор предложения
var proposal_title='payments-14';//идентификатор предложения
var active_approvals_to_add=[];//список аккаунтов подписавших данную транзакцию активным типом доступа для подтверждения предложения
var active_approvals_to_remove=[];//список аккаунтов для удаления из списка подтвердивших предложение
var master_approvals_to_add=[];
var master_approvals_to_remove=[];
var regular_approvals_to_add=[];
var regular_approvals_to_remove=[];
var key_approvals_to_add=[];
var key_approvals_to_remove=[];
var extensions=[];
active_approvals_to_add.push(account_login);
viz.broadcast.proposalUpdate(active_key,proposal_author,proposal_title,active_approvals_to_add,active_approvals_to_remove,master_approvals_to_add,master_approvals_to_remove,regular_approvals_to_add,regular_approvals_to_remove,key_approvals_to_add,key_approvals_to_remove,extensions,function(err,result){
console.log(err,result);
});
Предложение может удалить заявитель или любой участник, чья подпись требуется. Для этого достаточно выполнить операцию proposal_delete
подписав её активным ключом:
var account_login='escrow2';//участник предложения
var active_key='5K...';//активный приватный ключ
var proposal_author='test';//автор предложения
var proposal_title='payments-14';//идентификатор предложения
var extensions=[];
viz.broadcast.proposalDelete(active_key,proposal_author,proposal_title,account_login,extensions,function(err,result){
console.log(err,result);
});
Пример реализации отложенной операции награды созданной тем же аккаунтом в одной транзакции и подписавший транзакцию активным и регулярным ключом:
var expiration_date=new Date(new Date().getTime() + 20000);//+20 секунд от текущего момента
var expiration_time=expiration_date.toISOString().substr(0,19);//дата истечения предложения в формате ISO
var review_period_date=new Date(expiration_date.getTime() - 10000);//дата прекращения приема подписей (минус 10 секунд от даты экспирации)
var review_period_time=review_period_date.toISOString().substr(0,19);//дата истечения предложения в формате ISO
var login='test';
var active_wif='5K...';
var regular_wif='5J...';
var target='committee';//цель операции награждения
var energy=200;//2%
var memo='отложенного награждения через proposal';
var regular_public_wif = viz.auth.wifToPublic(regular_wif);//для добавление в key_approvals_to_add через операцию proposal_update
const operations = [
["proposal_create",{
"author": login,
"title": 'proposal-award',
"memo": login + '-award',
"expiration_time": expiration_time,
"proposed_operations": [
{"op":['award', {'initiator':login,'receiver':target,'energy':200,'custom_sequence':0,'memo':memo}]}],
"review_period_time": review_period_time,
"extensions": []
}],
["proposal_update",{
"author": login,
"title": 'proposal-award',
"active_approvals_to_add": [],
"active_approvals_to_remove": [],
"owner_approvals_to_add": [],
"owner_approvals_to_remove": [],
"posting_approvals_to_add": [],
"posting_approvals_to_remove": [],
"key_approvals_to_add": [regular_public_wif],
"key_approvals_to_remove": [],
"extensions": []
}]
];
viz.broadcast.send({extensions:[],operations},[active_wif,regular_wif],function(err,result){
console.log(err,result)
});
Чтобы подписать данные приватным ключом (метод viz.auth.signature.sign
, для поиска каноничной подписи необходимо добавить nonce, который будет находиться в данных на подпись) и проверить подпись публичным ключом (метод viz.auth.signature.verifyData
), можно воспользоваться стандартными методами viz-js-lib
:
var private_key='5KRLZitDd5c9uZzDgTMF4se4eVewENtZ29GbCuKwbT3msRbtLgi';
var public_key='VIZ6LWhhUzKmYM5VcxtC2FpEjzr71imfb7DeGA9yodeqnvtP2SYjA';
var invalid_public_key='VIZ65kiW3JsxsF7NCabAuSJUk8Efhx5PW6cbgSS5uuZpbkSTpSjn6';
var data={data:"data signature check!",nonce:0};
var nonce=0;
var data_with_nonce='';
var signature='';
function auth_signature_check(hex){//проверка на каноничность подписи
if('1f'==hex.substring(0,2)){
return true;
}
return false;
}
while(!auth_signature_check(signature)){
data.nonce=nonce;
data_with_nonce=JSON.stringify(data);
signature=viz.auth.signature.sign(data_with_nonce,private_key).toHex();
nonce++;
}
console.log('data with nonce',data_with_nonce);
console.log('signature',signature);
console.log('check by public_key',viz.auth.signature.verifyData(data_with_nonce,viz.auth.signature.fromHex(signature),public_key));
console.log('check by invalid_public_key',viz.auth.signature.verifyData(data_with_nonce,viz.auth.signature.fromHex(signature),invalid_public_key));
Если вашему приложению не требуется криптография и подпись транзакций, то вы можете использовать нативные средства для json-rpc запросов через js.
Пример для WebSocket соединения:
var api_gate='wss://solox.world/ws';
var latency_start=new Date().getTime();
var latency=-1;
var socket = new WebSocket(api_gate);
socket.onmessage=function(event){
latency=new Date().getTime() - latency_start;
let json=JSON.parse(event.data);
if(json.result){
console.log(json.result);
}
else{
console.log(json.error);
}
socket.close();
}
socket.onopen=function(){
socket.send('{"id":1,"method":"call","jsonrpc":"2.0","params":["database_api","get_dynamic_global_properties",[]]}');
};
Пример для HTTP соединения:
var api_gate='https://viz.lexa.host/';
var latency_start=new Date().getTime();
var latency=-1;
var xhr = new XMLHttpRequest();
xhr.overrideMimeType('text/plain');
xhr.open('POST',api_gate);
xhr.setRequestHeader('accept','application/json, text/plain, */*');
xhr.setRequestHeader('content-type','application/json');
xhr.onreadystatechange=function(){
if(4==xhr.readyState && 200==xhr.status){
latency=new Date().getTime() - latency_start;
console.log(xhr);
let json=JSON.parse(xhr.response);
if(json.result){
console.log(json.result);
}
else{
console.log(json.error);
}
}
}
xhr.send('{"id":1,"method":"call","jsonrpc":"2.0","params":["database_api","get_dynamic_global_properties",[]]}');
Есть правила формирования транзакций, заложенные в протокол VIZ (использующий каркас Graphene). Данные, подписываемые криптографическим ключом, должны соответствовать всем структурам и правилам бинарного представления, заложенным в код. В данном разделе вы узнаете как кодируются ключи, как происходит бинарное представление данных или их подготовка.
Структура данных, бинарное представление которой и нужно подписывать используя ключи соответствующих типов доступа используемых в аккаунтах вложенных операций, содержит следующие поля:
VIZ
: 2040effda178d4fffff5eab7a915d4019879f5205cc5392e4bcced2b6edda0cd
. Стоит отметить, что fc::sha256::hash преобразует строку в c_str, добавляя в начало ее hex значения 56495A
длину строки, в итоге sha256 рассчитывается от hex значения 0356495A
;00
);Открытый и свободный код блокчейн систем основанных на Graphene позволяют запускать новые цепочки, как без изменений, так и полностью переработанные с собственными механиками и экономикой. Более того, множество проектов запускают публичные тестовые цепочки для проверки изменений. Чтобы ноды не путались и транзакции из одной сети нельзя было применять в форке (или цепи с аналогичными аккаунтами и ключами) — существует идентификатор цепи, который присутствует как метка в каждой транзакции и операции подписи.
Приватные и публичные ключи в VIZ находятся по алгоритму ESDCA (теория), и используют криптографию для проверки подписей набора данных. Многие разработчики не являясь специалистами в криптографии просто используют специализированные библиотеки, не вдаваясь в подробности.
Рассмотрим этапность преобразования приватного ключа (состоящего из 32 байт) в читаемый WIF формат:
80
;123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz
;Этапность преобразование публичного ключа (состоящего из 32 байт) в читаемый формат:
123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz
;VIZ
);В библиотеке viz-js-lib используется модуль auth
(ссылка на GitHub), который позволяет предустановленными методами работать с ключами и подписывать данные.
escrow
будет соответствовать hex значению 06657363726f77
в бинарном представлении);award
представляет собой uint16_t, для передачи которого достаточно 2 байта. Если необходимо передать значением 10.00%, то в целочисленном значении это будет 1000, в hex представлении 03EB
, перевернутое значение которого будет EB03
. Поле custom_sequence
представляющее собой uint64_t уже состоит из 8 байтов, поэтому для передачи десятичного значения 377, его hex значение 0179
в бинарном значении будет представлять собой hex: 7901000000000000
.2019-02-07T06:19:23
в часовом поясе UTC+0, он же GMT). Для их бинарного значения записывается в десятичном формате unixtime по правилам представления integer. Пример 2019-02-07T06:19:23
в unixtime будет 1549520363
(hex значение 5C5BCDEB
), который в бинарном значении будет представлять собой hex: EBCD5B5C
.03
для VIZ, 06
для SHARES), строковое значение тикера в 7 байт (56495A00000000
для VIZ, 53484152455300
для SHARES). Например: 1.002 VIZ
в JSON значении внутри операции будет иметь бинарное представление в hex: EA030000000000000356495A
.026a1dbaacb805f145f9276025627102152840bb1aa09b7fac580f892d93b572b4
соответствует приватному ключу с recovery_id равным 02
и точке с координатами X в hex представлении 6a1dbaacb805f145f927602562710215
и Y в hex представлении 2840bb1aa09b7fac580f892d93b572b4
. Что соответствует публичному ключу VIZ5hDwvV1PPUTmehSmZecaxo1ameBpCMNVmYHKK2bL1ppLGRvh85
.transfer
в бинарном виде будет иметь запись в hex 02
, а операция create_invite
hex значение 2b
.Разберем пример транзакции в формате JSON и её представление в бинарном виде:
{"ref_block_num":9023,"ref_block_prefix":1971875185,"expiration":"2019-02-07T06:19:23","operations":[["transfer",{"from":"test1","to":"test2","amount":"1.002 VIZ","memo":"<3"}]],"extensions":[]}
ffff
(например, число 9023 в hex представлении 233F
, согласно представлению integer должно быть перевернуто, получаем 3F23
);get_block_header
с номером следующего блока (9024) к плагину database_api. Ответ будет содержать поле previous
с идентификатором 0000233F716D887523BB63AD3E6107C96EDCFD8A
искомого блока. Берем 716D8875
для бинарного представления, переворачиваем байты — 75886D71
и переводим в десятичный формат для JSON: 1971875185
.2019-02-07T06:19:23
в unixtime будет 1549520363
(hex значение 5C5BCDEB
), который в бинарном значении будет перевернут и представлять собой hex: EBCD5B5C
.01
);02
);test1
и hex представление: 057465737431
);test2
и hex представление: 057465737432
);1.002 VIZ
в hex: EA030000000000000356495A00000000
);<3
и hex представление: 023C33
);00
).Итоговое бинарное представление данных транзакции в hex: 3F23716D8875EBCD5B5C0102057465737431057465737432EA030000000000000356495A00000000023C3300
;
Чтобы отправить транзакцию в блокчейн, необходимо дополнить данное представление chain_id в начале и подписать приватным ключом. Полученную подпись необходимо добавить в JSON поле массив signatures
, например:
{"ref_block_num":9023,"ref_block_prefix":1971875185,"expiration":"2019-02-07T06:19:23","operations":[["transfer",{"from":"test1","to":"test2","amount":"1.002 VIZ","memo":"<3"}]],"extensions":[],"signatures":["1f500f2a5d721e45c53e76fca786d690c7c0556f1923aa07c944e26614b50481d353e88f82e731be74c18e3fb8d117dc992a475991974b6e1364a66f5ccb618f83"]}
И передать этот JSON через API запрос broadcast_transaction
плагину network_broadcast_api
.
В исходном коде ноды указан тип подписанной транзакции как transaction_id_type, который в протоколе Graphene записан как fc::ripemd160. Но практика показывает, что идентификатор транзакции не является ripemd160 (размер хэша 20 байт), а является частью sha256 хэша (размер хэша 32 байта). Доподлинно неизвестно, почему так произошло, но можно сделать 2 предположения:
digest_type::encoder
в файле протокола transaction.cpp
);Стоит отметить, что данную ошибку исправили в протоколе EOS, там тип транзакции fc::sha256, что наводит на мысли, что это все таки баг. Как итог идентификатором транзакции в Graphene проектах до EOS является хэш sha256, но обрезанный — 20 первых байт (вместо полных 32 байт).
Например, транзакция в VIZ из блока 11142739 имеет идентификатор c84f9e8255859b2083be720cf9b64b3542e4360f
. Сырая транзакция {"ref_block_num":1612,"ref_block_prefix":2641357798,"expiration":"2019-10-22T05:59:27","operations":[["award",{"initiator":"on1x","receiver":"viz-social-bot","energy":20,"custom_sequence":0,"memo":"telegram:262632819","beneficiaries":[]}]],"extensions":[]}
в hex: 4c06e6eb6f9dbf9aae5d012f046f6e31780e76697a2d736f6369616c2d626f74140000000000000000001274656c656772616d3a3236323633323831390000
.
Хэш sha256 от сырой транзакции: c84f9e8255859b2083be720cf9b64b3542e4360f0a62e33363bca5d984ee608a
, первые 20 байт которого и являются её идентификатором в блокчейне: c84f9e8255859b2083be720cf9b64b3542e4360f
.
Нода блокчейна хранит идентификаторы последних 65537 блоков (подробнее читайте про концепцию TaPoS в VIZ). Обычно разработчики ссылаются на один из последних блоков, обычно, выполняя очередь действий:
get_dynamic_global_properties
к плагину database_api
;head_block_number
минус 3 блока устанавливают для какого блока будут формировать ref_block_num и запрашивать его идентификатор;get_block_header
к плагину database_api
, запрашивая искомый блок плюс один блок (так как заголовок каждого блока содержит ссылку на идентификатор прошлого блока, искомый идентификатор находится в следующем);Большинство библиотек которые содержат абстракции для упрощения вызовов и трансляции транзакции в блокчейн делают это самостоятельно.
Пример ручного получения ref_block_num
и ref_block_prefix
на viz-js-lib можете посмотреть в исходном коде абстракции подготовки транзакции в самой библиотеке.
Пример аналогичного получения ref_block_num
и ref_block_prefix
на PHP в исходном коде библиотеки php-graphene-node-client.
Все операции и их параметры записаны в протоколе VIZ и находятся в файле chain_operations.hpp.
Именно там можно изучить типы параметров и их требуемый порядок в операции. Внимание! Порядок параматров в структуре операции не совпадает с порядком параметров в самой операции. Рассмотрим пример на операции escrow_transfer_operation
, структура операции (часто перед операцией присутствует комментарий её описывающий, ):
/**
* The purpose of this operation is to enable someone to send money contingently to
* another individual. The funds leave the *from* account and go into a temporary balance
* where they are held until *from* releases it to *to* or *to* refunds it to *from*.
*
* In the event of a dispute the *agent* can divide the funds between the to/from account.
* Disputes can be raised any time before or on the dispute deadline time, after the escrow
* has been approved by all parties.
*
* This operation only creates a proposed escrow transfer. Both the *agent* and *to* must
* agree to the terms of the arrangement by approving the escrow.
*
* The escrow agent is paid the fee on approval of all parties. It is up to the escrow agent
* to determine the fee.
*
* Escrow transactions are uniquely identified by 'from' and 'escrow_id', the 'escrow_id' is defined
* by the sender.
*/
struct escrow_transfer_operation : public base_operation {
account_name_type from;
account_name_type to;
account_name_type agent;
uint32_t escrow_id = 30;
asset token_amount = asset(0, TOKEN_SYMBOL);
asset fee;
time_point_sec ratification_deadline;
time_point_sec escrow_expiration;
string json_metadata;
void validate() const;
void get_required_active_authorities(flat_set<account_name_type> &a) const {
a.insert(from);
}
};
Порядок параметров в операции задается уже в конце файла методом:
FC_REFLECT((graphene::protocol::escrow_transfer_operation), (from)(to)(token_amount)(escrow_id)(agent)(fee)(json_metadata)(ratification_deadline)(escrow_expiration));
Кроме описания структуры операции есть еще обработка параметров в методе validate
, найти который можно в файле chain_operations.cpp:
void escrow_transfer_operation::validate() const {
validate_account_name(from);
validate_account_name(to);
validate_account_name(agent);
FC_ASSERT(fee.amount >= 0, "fee cannot be negative");
FC_ASSERT(token_amount.amount >=
0, "tokens amount cannot be negative");
FC_ASSERT(from != agent &&
to != agent, "agent must be a third party");
FC_ASSERT(fee.symbol == TOKEN_SYMBOL, "fee must be TOKEN_SYMBOL");
FC_ASSERT(token_amount.symbol ==
TOKEN_SYMBOL, "amount must be TOKEN_SYMBOL");
FC_ASSERT(ratification_deadline <
escrow_expiration, "ratification deadline must be before escrow expiration");
validate_json_metadata(json_metadata);
}
Большинство операций проверяют наличие подписи соответствующих полномочий, например в структуре escrow_transfer_operation
присутствует проверка подписи инициатора операции (поле from
) активных полномочий в методе get_required_active_authorities
.