The VIZ Cookbook

The VIZ Cookbook provides developers with a description of the API/objects/structures in the VIZ blockchain, code examples for popular use cases, a guide for low-level transaction generation.


Content:

Basic concepts

The VIZ blockchain belongs to the Graphene family (a blockchain technology written in C++, open source). The Graphene source code is available in many variations, as it was photographed (copied) and modified many times (for example: BitShares, Steem, VIZ, served as the basis for EOS).

Despite the common elements, each system has quite a lot of differences in objects, data structures and internal mechanisms, as well as in the economy. Graphene is a kind of framework, an engine for blockchain solutions. Its structure can be studied for a long time, but for application developers on a particular blockchain there is no need to thoroughly know how data exchange between nodes works, how data is synchronized and stored in memory. The main thing is to understand the basics.


The node of the blockchain system

Software that communicates with other nodes of the network, accepts and transmits transactions to other nodes, keeps records and processes blocks (for more information, see the Node types) section.

Accounts

The VIZ uses the account model. Accounts are part of a common namespace, store tokens and public access keys.

Keys and types of permissions

There are private and public keys. You can use a private key to sign a message. Using a public key, you can prove the fact of signing a message. Each private key corresponds to a single public key. And these are the public keys that are stored in the blockchain system. Using them, nodes can make sure that this or that action was initiated by the owner of the private key (corresponding to the public key).

An account in the VIZ blockchain contains three types of permissions:

  • master - responsible for the ownership of the account;
  • active - responsible for managing account tokens;
  • regular - responsible for normal operations (for example, rewarding).

Each type of authority can contain a list of trusted accounts that can sign and perform an operation of this type of access on behalf of the source account. Also, each type of authority can contain one or more public keys of different weights (for the ability to manage an account via multisig, when several users own an account).

Usually, an account contains either a single key for all types of permissions, or one key for each type of permissions. The account additionally contains a memo key, which is used to encode messages between network participants.

VIZ tokens and share of the SHARES network

The VIZ token is transferable, it does not participate in management, but can be transferred to social capital (SHARES). This operation is often called stacking in other blockchains. An account that owns social capital can participate in the management.

Delegates

An account can declare its intention to be a delegate. The delegate (witness) is elected by the network participants (through the voting procedure) and participates in the queue of delegates for signing blocks.

A member of the network can vote for either one or several delegates, who will share the weight of his vote equally among themselves. The more social capital votes for a delegate, the higher he will be in the queue of delegates for signing blocks.

The queue of delegates consists of 21 slots: 11 of them are fixed for the delegates who received the most votes, the remaining 10 seats are occupied by delegates in a floating competing queue according to the votes collected.

Transactions

Users

Users create operations (account actions in the network, for more information, see the section Operations and their types), form a transaction from them, which is signed with the private key of the account of the desired type of authority.

Blocks

Delegate nodes receive transactions from all nodes of the network that users have sent to the network. The delegate whose turn it is to sign the block forms a block from the transaction queue and sends it to other network nodes. A new block is formed every 3 seconds. All nodes of the network check the delegate signatures, user signatures in transactions and apply operations in turn. This is how the state of the system is formed (for more information, see the section State of the system).

Consensus

The VIZ uses the Fair DPoS (Delegated Proof of Stake) consensus. Nodes store two states of the system: irreversible and reversible. Irreversibility occurs when approximately 15 delegates (75% of the number of delegates in the queue) confirm their participation in the longest chain of blocks. After that, the node can't roll back earlier transactions, so the finality of the network state occurs.

In the normal state, irreversibility occurs in 15 blocks (about 45 seconds). Therefore, all important operations that require verification can be confirmed only after reaching irreversibility.

Plugins

Plugins extend the capabilities of the node, can process individual operations and manage their own data structures. There are both mandatory plugins (responsible for connecting between network nodes) and optional ones (for example, the history of account operations, for more information, see the section Plugins and their API).

Economy

The conditions for the so-called predictable economy were created in the VIZ blockchain. If other systems have principles with decaying inflation, which puts participants who joined the system at different times in unequal conditions, then VIZ has a fixed inflation with rounds of 1 year (10512000 blocks).


Inflation model

At the start of the chain, 50 million viz were distributed. Each round is based on a fixed inflation rate of 10%. A year after the start, there were 55 million viz in the network, which means that the inflation for the next round was already considered from 55 million viz, which led to the issuance of 5.5 million viz for the second year.

Due to fixed inflation, it becomes possible to predict the issue of viz for each block. In the second year, for example, each block is issued 5500000 / 10512000 = 0.523 viz. And this number does not change until the next round begins.

Token issue distribution

The existing VIZ model provides for the management of the economy by network delegates. The delegates elected by the network participants vote for the network parameters, among which there are parameters for the distribution of the token issue:

  • inflation_witness_percent — - an economic parameter that sets the percentage of the issue directed to the remuneration of delegates for maintaining the infrastructure of the blockchain system;
  • inflation_ratio_committee_vs_reward_fund — an economic parameter that determines the ratio of the issue sent to the DAO Fund and the Awards Fund.

You can find out the current values of the parameters by executing an API request get_chain_propertiesto the plugin database_api or via the network browser on the control.viz.world website

DAO Fund

The DAO Foundation collects viz tokens to finance initiatives for the development of the ecosystem. If a network participant has decided to hold a competition, develop an application, benefit the entire network — he can apply for funding from the Fund. Any member of the network can vote both against and for the full or partial execution of the application. When the time comes for considering the application, the shares of all network participants who took part in the voting will be calculated and a decision will be made.

Awards Fund

Each participant of the network has a resource that is renewable over time — energy. The spent energy is replenished at a rate of 100 percentage points in 5 days (20 p. p. per day, 0.83 p. p.per hour, etc.). The energy of the account cannot exceed 100%.

When a VIZ participant decides to award someone, he indicates the percentage of the account's energy that he wants to spend on the award. The blockchain considers the size of the participant's social capital and the energy spent on the award and correlates them with the sum of the awards of all participants for the last 5 days. As a result, the target account receives a reward in social capital from the issue in proportion to the social capital and the energy spent by the recipient.This ensures equal competitive access to the Awards Fund. The network participant decides for himself who to reward and for what, opening up the possibility of free choice and stimulating any actions and initiatives.

For example:

  • The author of the story and his readers can reward the illustrator who published the drawings of the characters;
  • On the website of questions and answers, participants can reward the one who helped solve and understand the question;
  • Viewers can reward a video blogger for an interesting topic or a training lesson.

Selfgovernment

The entire economy of the VIZ is managed by the owners of social capital. They are the ones who vote for the delegates if they agree with their vision and model of managing the system (if there is no one to vote for, everyone can become a delegate himself and get the votes of other participants). The Awards Fund and the DAO Fund work according to a fair, equally shared management model.

Node types

The VIZ node is the heart of the blockchain, the software that processes block by block, performs all operations and stores the state of the system. The owner configures the node, selects the plugins used. The included plugins and their settings depend on what features the node can provide.


Witness node (delegate node)

Delegates are responsible for the formation of blocks, so the most important nodes are the delegate nodes. They exchange data with other nodes, collect transactions, and when it is the turn of the delegate (the server owner) to form a block, the collected block is signed and transmitted to other nodes. The block must be signed and delivered within 3 seconds allocated to the delegate. Skipping a block leads to a delay in the execution of transactions and a delegate penalty for some time (the penalty is imposed on the total weight of votes cast for the delegate by other users). This allows the system to temporarily lower in the delegate queue those who have some problems on the server or data center, thus protecting the reliability of the network.

Often, delegates hold two nodes, the main and the spare (reserve). They are often located in different data centers and do not depend on each other. If there is a problem with the main node, the delegate changes the block signing key to a backup one and the spare node will be engaged in the formation of blocks.

Key plugins: chain p2p json_rpc webserver witness network_broadcast_api database_api witness_api

Seed node

The basis for the functioning of any blockchain system is peer-to-peer (p2p) connection and data exchange. Seed nodes differ in that they perform an important role-they accept and distribute blocks, thus unloading the bandwidth of the entire network and reducing the response for geographically close connections. There is no economic benefit to keep a seed node, since the server costs money, but does not bring anything to its owner. Therefore, most of the seed nodes run delegates (witnesses), if they can afford it financially. It is through the API of the node that requests for up-to-date information, the status of accounts, the history of operations or applications in the committee occur.

Key plugins: chain p2p

API node

Some of the plugins we provide are APIs for developers and their users. Such nodes are often called complete if they have all the available plugins enabled and store the history from the first block. Since plugins give access not only to the state of the system, but also form their own data structures, they have increased requirements for server resources (especially RAM, since the node stores the ChainBase database in RAM). It is through the API of the node that requests for up-to-date information, the status of accounts, the history of operations or applications in the committee occur.

Examples of such plugins:

  • network_broadcast_api - sending a transaction to the network;
  • database_api — the main plugin that provides access to the state of the system, getting data about accounts, getting information about the block, getting network parameters;
  • custom_protocol_api - plugin for recording the counter and the block height for the last custom operations for the account (the number of stored ids of custom operations for the account is set by the custom-protocol-store-size parameter in the configuration file);
  • account_history - getting a list of operations related to the account;
  • committee_api - getting a list of applications by status, getting information about the application, getting a list of votes on the application;
  • invite_api - getting a list of invites by status, requesting information about the invite by ID or key;
  • operation_history - getting information about operations in the block;
  • paid_subscription_api - getting information about paid subscriptions, agreements concluded between accounts;
  • witness_api - getting a list of delegates, a queue of delegates, information about a specific delegate and his voted network parameters.

The API nodes can be either private (when the login and password access parameters are specified in the settings), or public (when the API access is available to everyone and the node address is publicly known). Often, public API nodes are simply called public nodes. For more information, see Plugins and their APIs.

Operations and their types

Operations in the VIZ blockchain are divided into ordinary, outdated, virtual. Ordinary operations get into the blockchain through a signed transaction of a network participant. Outdated operations are disabled due to the development of the network and changes in internal mechanics.

Some of the operations are also heavy (data operations), an additional bandwidth factor is imposed on them (voting parameter of network data_operations_cost_additional_bandwidth).

Virtual operations are generated by the node when certain conditions in the code are triggered and are more informative for network participants. For example, an award operation from a network member contains the purpose of the award and the energy spent by the account. It does not store the amount of awards that will go to the recipient from the awards fund. It is for such cases that virtual operations are needed. In case of awarding, a virtual operation receive_award will be generated, containing the received award in the shares field.


Numbering of operations

In the VIZ protocol there is a numbering of operations (starts from zero), there are both normal and virtual operations:

  • 0, vote, outdated operation;
  • 1, content, outdated operation;
  • 2, transfer, transfer of VIZ tokens;
  • 3, transfer_to_vesting, transfer of VIZ tokens to a share in the SHARES network;
  • 4, withdraw_vesting, conversion of SHARES to VIZ (withdrawal within 28 days in equal parts of 1/28 of the total amount of SHARES);
  • 5, account_update, updating account access;
  • 6, witness_update, setting a key for a delegate (or a statement of intent to be one);
  • 7, account_witness_vote, voting for a delegate;
  • 8, account_witness_proxy, transfer of the right to vote (with share) for the delegates;
  • 9, delete_content, outdated operation;
  • 10, custom, send a public string with the contents in JSON format to the VIZ blockchain system;
  • 11, set_withdraw_vesting_route, setting the share withdrawal direction when converting SHARES to VIZ tokens;
  • 12, request_account_recovery, request to restore access through a trusted account;
  • 13, recover_account, satisfaction of a request to restore access by a trusted account;
  • 14, change_recovery_account, - change of the trusted account to restore the account in case of loss of access;
  • 15, escrow_transfer, create a deal through an intermediary;
  • 16, escrow_dispute, request a resolution of a dispute between the sides to the transaction from an intermediary;
  • 17, escrow_release, release tokens from the transaction;
  • 18, escrow_approve, confirm the completion of the transaction;
  • 19, delegate_vesting_shares, delegating a share to another participant;
  • 20, account_create, creating a new account;
  • 21, account_metadata, updating public account metadata;
  • 22, proposal_create, creating an offer for signature;
  • 23, proposal_update, updating the offer (providing a signature);
  • 24, proposal_delete, deleting an offer;
  • 25, chain_properties_update, outdated operation, replaced by versioned_chain_properties_update;
  • 26, author_reward, outdated virtual operation;
  • 27, curation_reward, outdated virtual operation;
  • 28, content_reward, outdated virtual operation;
  • 29, fill_vesting_withdraw, virtual operation, contains information about converting SHARES to VIZ;
  • 30, shutdown_witness, virtual operation, called when the delegate is disabled due to a large number of missed blocks;
  • 31, hardfork, virtual operation, called when a hardfork occurs (updating the blockchain version);
  • 32, content_payout_update, outdated virtual operation;
  • 33, content_benefactor_reward, outdated virtual operation;
  • 34, return_vesting_delegation, virtual operation, occurs when the delegated share is returned;
  • 35, committee_worker_create_request, creating an application to the committee;
  • 36, committee_worker_cancel_request, cancellation of the application in the committee;
  • 37, committee_vote_request, voting for the application in the committee;
  • 38, committee_cancel_request, virtual operation, the application was rejected by the committee;
  • 39, committee_approve_request, virtual operation, the application was approved by the committee;
  • 40, committee_payout_request, virtual operation, the application received a full payment from the committee;
  • 41, committee_pay_request, virtual operation, the application received a payment from the committee;
  • 42, witness_reward, виртуальная операция, award to the delegate for signing the block;
  • 43, create_invite, creating an invite code;
  • 44, claim_invite_balance, repayment of the invite code to the account balance;
  • 45, invite_registration, registration using an invite code;
  • 46, versioned_chain_properties_update, setting voting parameters of the network by the delegate;
  • 47, award, awarding of network participants;
  • 48, receive_award, virtual operation, receiving a reward;
  • 49, benefactor_award, virtual operation, receiving a beneficiary award;
  • 50, set_paid_subscription, setting the terms of the agreement for periodic payments;
  • 51, paid_subscribe, signing the terms of the agreement;
  • 52, paid_subscription_action, virtual operation, payment of periodic payments;
  • 53, cancel_paid_subscription, virtual operation, termination of periodic payments;
  • 54, set_account_price, setting the price for an account;
  • 55, set_subaccount_price, setting the price for a subaccount;
  • 56, buy_account, buying an account;
  • 57, account_sale, virtual operation, account purchase record;
  • 58, use_invite_balance, using the balance of the invite code to transfer to the account's network share;
  • 59, expire_escrow_ratification, virtual operation, expiration of the transaction ratification through an intermediary;

The operation number is needed for low-level formation of transactions and their signature (for more information, see the section Formation of transactions).

Objects and structures in VIZ

Considering VIZ, it is necessary to separate the objects and structures of the protocol (operation, transaction, block, cassette, version, authority) from the objects and structures that exist directly in the blockchain (which are affected by certain operations).


List of protocol objects and structures

Everything related to the protocol is located in the /libraries/protocol directory of the source code of the C++ node of the VIZ blockchain.

  • types — data types in the protocol
  • operations / proposal_operations / chain_operations / chain_virtual_operations — everything related to operations and their processing;
  • transaction — everything related to the transaction (id, list of operations, which block it refers to);
  • block_header / block — contains transactions, refers to the previous block, contains extensions that a delegate can use to initiate a vote for switching to a new version of the hardfork;
  • asset — a structure of tokens in VIZ (VIZ and SHARES, the ratio of assets of different categories to each other);
  • base / version — the structure describing the version of the network protocol, the voting and the time for switching to the new version;
  • authority — a structure describing the keyset for a certain type of account access;
  • sign_state — an assistant for verifying signatures (or the presence of a key that can generate it).

Objects and structures in the blockchain

It is from the objects and structures of the blockchain itself that the state of the system consists. Each block containing operations is processed by the main database module, which calculates all changes and makes decisions on deferred actions. In the /libraries/chain/include/graphene/chain catalog contains both objects and data structures, as well as the internal structure of the blockchain (evaluator, block_log, dynamic_global_property_object, object types).

The state of the system consists of objects:

  • dynamic_global_property_object — the main object containing data about the current state of the economy and the state of the node (for example, the number of an irreversible block);
  • account_object — account entries;
  • account_authority_object — entries of permissions for accounts;
  • witness_object — entries of delegates;
  • transaction_object — used for transactions in the queue (this allows you to check if there are no duplicates for new transactions and delete transactions from the queue if it was not executed before the expiration date expire);
  • block_summary_object — it is used for indexing blocks and their hash, for checking TaPoS (the transaction must refer to the previous block, the check takes place just by the index constructed from block_summary_object objects);
  • witness_schedule_object — status of the delegate queue;
  • witness_vote_object — records of voting for delegates
  • hardfork_property_object — records about the current network hard fork;
  • withdraw_vesting_route_object — records about the token distribution route when converting a share;
  • master_authority_history_object — records about changes of the master permissions;
  • account_recovery_request_object — account recovery requests;
  • change_recovery_account_request_object — requests to change a trusted account to restore access;
  • escrow_object — records of three-way transactions;
  • vesting_delegation_object — records about the delegated share;
  • vesting_delegation_expiration_object — records about the delegated share to be returned after the delegation is canceled;
  • account_metadata_object — separate records with account metadata;
  • proposal_object — records of proposal operations;
  • required_approval_object — records of required confirmations for proposal operations;
  • committee_request_object — records of requests to the committee;
  • committee_vote_object — records of votes on applications in the committee;
  • invite_object — records of all invites;
  • award_shares_expire_object — records of awards that should lower the competition at the end of the term;
  • paid_subscription_object — information about paid subscriptions;
  • paid_subscribe_object — records of paid subscriptions issued;
  • witness_penalty_expire_object — records of penalties for delegates who missed the block.

Objects and structures in API plugins

Plugins that provide the API can return objects both from the blockchain and their own. Simple requests with getting an object by id return the data as is, often passing the object from the blockchain through the constructor of a similar to API to copy the state and give it to the user, for example: the witness_api plugin uses a separate witness_api_object object. And the database_api plugin uses account_api_object , which complements the standard blockchain object account with access types by copying the current permissions from the index there.

If the plugin extends the standard index tables and objects, it creates a new structure, separately keeps records of operations and fills in the index. For example, the private_message plugin does this by processing custom operations (creating message_object objects, that fill the message_index index).

Voting parameters of the network

Delegates broadcast their position on the network's voting parameters. The blockchain system calculates the median values of the voted parameters every cycle of the delegate queue (21 blocks) and fixes them for this cycle. Description of the parameters (the median values are indicated in parentheses at the time of writing this section):

  • account_creation_fee — transferred commission when creating an account (1.000 VIZ);
  • create_account_delegation_ratio — delegation margin factor when creating an account (x10);
  • create_account_delegation_time — delegation time when creating an account (2592000 seconds);
  • bandwidth_reserve_percent — the share of the network allocated for backup bandwidth (0.01%);
  • bandwidth_reserve_below — backup bandwidth, valid for accounts with a network share up to the threshold (1.000000 SHARES);
  • committee_request_approve_min_percent — the minimum percentage of the voting network required for making a decision on the application to the committee (10%);
  • min_delegation — minimum number of tokens for delegation (1,000 VIZ);
  • vote_accounting_min_rshares — minimum vote weight to be taken into account when awarding (5000000 rushares);
  • maximum_block_size — maximum block size in the network (65536 bytes);
  • inflation_witness_percent — the share of inflation for the award to delegates (20%);
  • inflation_ratio_committee_vs_reward_fund — the ratio of the division of the balance of inflation between the committee and the awards fund (75%);
  • inflation_recalc_period — the number of blocks between the recalculation of the inflation model (806400);
  • data_operations_cost_additional_bandwidth — additional bandwidth markup for each data operation in a transaction (0% of the transaction size);
  • witness_miss_penalty_percent — penalty to a delegate for skipping a block as a percentage of the total weight of votes (1%);
  • witness_miss_penalty_duration — the duration of the penalty to the delegate for skipping a block in seconds (86400 seconds);
  • create_invite_min_balance — minimum check amount (10,000 VIZ);
  • committee_create_request_fee — fee for creating an application to the DAO Fund (100.000 VIZ);
  • create_paid_subscription_fee — fee for creating a paid subscription (100.000 VIZ);
  • account_on_sale_fee — fee for placing an account for sale (10.000 VIZ);
  • subaccount_on_sale_fee — fee for placing subaccounts for sale (100.000 VIZ);
  • witness_declaration_fee — fee for declaring an account as a delegate (10.000 VIZ);
  • withdraw_intervals — the number of periods (days) of capital reduction (28).

Service accounts

In VIZ, there are service accounts that have access keys to certain permissions embedded in the configuration file:

  • null — a specialized account that burns the received tokens. It has empty powers, the mechanism for burning tokens is embedded in the source code of the blockchain.
  • committee — a specialized account that transfers all received tokens to the committee's fund, thus allowing you to donate tokens for the development of the ecosystem. Has empty permissions. It also serves as the initiator of the network, for the anonymous genesis of the blockchain (when the block signing key from the committee account is available to everyone in the configuration file). The signature key is formed from the string concatenation of the strings commitee, viz, sign, which corresponds to5Hw9YPABaFxa2LooiANLrhUK5TPryy8f7v9Y1rk923PuYqbYdfC. After starting the network and connecting other delegates to it, an anonymous user can stop signing with the committee account and the signature key is reset to the VIZ111111111111111111111111111114T1Anm value.
  • anonymous — a specialized account that, when receiving tokens with the specified key (and login, if desired), creates an anonymous account (anonymity is provided by a possible transfer from the gateways of both exchange and social, custody services). Has empty permissions. The format of the memonote for registering an anonymous account is login:public_key, where login is the desired login for the new account, and public_key is a single public key for all types of permissions. If the login is not specified, and only public_key is specified in the note, an anonymous subaccount of the format nX.anonymous is created, where X is the increment of the number specified in the json_metadata of the anonymousaccount. Attention! If the note is not specified, the funds will be burned in the same way as the transfer to the null account.
  • invite — a specialized account for the ability to activate invite codes anonymously, without having an account in the blockchain. The active key is available to everyone in the configuration file, formed from the string concatenation of the strings invite,viz,active, which corresponds to5KcfoRuDfkhrLCxVcE9x51J6KN9aM9fpb78tLrvvFckxVV6FyFW. Initially, it does not have any share for making transactions, which can be corrected by delegating or enabling the backup bandwidth system by delegates.
  • viz — the account-initiator of the chain in the genesis block, the private key 5JabcrvaLnBTCkCVFX5r4rmeGGfuJuVp4NAKRNLTey6pxhRQmf4, was reset to empty after pre-installing the sale of subaccounts for 10,000 VIZ (the recipient iscommittee).

State of the system

The state of the system is the necessary minimum of data for the functioning of the node. The Graphene codebase on which VIZ is built executes operations from transactions in each block that corresponds to a consensus (delegate queues, signature matching). Each block there is a processing of data, pending actions, which leads to an iterative essence of the system state. Some of the data is stored there permanently, which imposes certain requirements on the server equipment on which the node is running.

In the Objects and structures in the blockchain section describes most of the objects that make up the state of the system.


Dynamic global property object (dgpo)

This object stores the main properties of the network, contains information about tokens in circulation and other important information. In the source code, it is often represented as a dgp variable, which modifies its state with each iteration, so dynamic global property can be confidently considered the most important part of the system state. Let's consider its public properties, accessible via the get_dynamic_global_properties method when accessing the database_api plugin:

  • current_witness (example value: "solox") - delegate of the current block;
  • head_block_number (example value: 10829669) — the number of the current block;
  • head_block_id (example value: 00a53f658226b2a6f3f75fc8185d884d029f50bf) — id (hash) of the current block;
  • time (example value: "2019-10-11T08:49:21") — time of generation of the current block;
  • last_irreversible_block_num (example value: 10829651) — the number of the last irreversible block;
  • genesis_time (example value: "2018-09-29T10:23:24") — generation time of the first network block;
  • current_supply (example value: "55159321.957 VIZ") — the total number of VIZ tokens in the system;
  • total_vesting_fund (example value: "27924855.425 VIZ") — the number of VIZ tokens transferred to the network share (SHARES);
  • total_vesting_shares (example value: "27924847.935329 SHARES") — a general quantitative measure of the network's share in SHARES;
  • committee_fund (example value: "1423813.837 VIZ") — balance of the committee's fund;
  • committee_requests (example value: 39) — total number of applications to the committee;
  • total_reward_fund (example value: "28216.906 VIZ") — balance of the awards fund;
  • total_reward_shares (example value: "6019776735774") — quantitative measure of competition for the award fund;
  • current_aslot (example value: 10855719) — the current number of the delegate slot for signature (contains the numbering of the slot from the start in the network, including the blocks skipped by the delegates);
  • recent_slots_filled (example value: 340282366920938463463374607431768211455) — used to calculate the percentage of delegates participating in block signing;
  • participation_count (example value: 128) — it is necessary to divide by 128 to get the percentage of delegates participating in the block signing;
  • maximum_block_size (example value: 65536) — maximum block size in bytes (a network parameter to be voted on);
  • average_block_size (example value: 114) — the average block size is calculated using the formula: average_block_size = (99 * average_block_size + new_block_size) / 100, used to update current_reserve_ratio to maintain about 50% or less in network bandwidth;
  • max_virtual_bandwidth (example value: 5986734968066277376) — the maximum network bandwidth is calculated using the formula maximum_block_size * CHAIN_BANDWIDTH_AVERAGE_WINDOW_SECONDS / CHAIN_BLOCK_INTERVAL, the maximum virtual network bandwidth according to the formula max_bandwidth * current_reserve_ratio.
  • current_reserve_ratio (example value: 20000) — Once every 20 blocks (1 minute), a check occurs: average_block_size <= 25% maximum_block_size. If it is executed, then this value increases by 1 (linearly, each block), but not more than CHAIN_MAX_RESERVE_RATIO (20000). If the condition is not met, then current_reserve_ratio is divided in half, which should immediately reduce the load on the network, protecting it from participants using bulk transactions. In other words, halving the backup ratio will not halve the network usage, but will limit users who will already try to exceed more than 50% of their bandwidth. When the backup ratio drops by half from the maximum value (10000 instead of 20000), it will take about 7 days to restore the total virtual bandwidth.
  • bandwidth_reserve_candidates (example value: 1) — the number of candidates for the bandwidth reserve (plus 1 candidate by default);
  • inflation_calc_block_num (example value: 10315901) — the block number of the last calculation of the inflation distribution;
  • inflation_witness_percent (example value: 2000) — the percentage of the issue received by delegates for signing blocks;
  • inflation_ratio (example value: 5000) — the percentage of the ratio of the issue sent to the committee's fund against the awards fund;
  • vote_regeneration_per_day (example value: 1) — deprecated property.

Uniqueness of transactions and TaPoS (Transactions as Proof of Stake)

The node checks all incoming transactions for uniqueness in the transaction pool. After expiration occurs (limited in the settings by the CHAIN_MAX_TIME_UNTIL_EXPIRATION constant in one hour), the transaction is deleted from the pool.

All transactions in VIZ must [comply with the concept of TaPoS](https://github.com/super 3 / invictus. io/blob/master/assets/pdf/TransactionsAsProofOfStake10. pdf), that is, refer to one of the previous blocks (ref_block_num in 2 byte representation (binary «and» of decimal representation of the block number with hex ffff) and ref_block_prefix consisting of a decimal representation of 5, 6, 7, 8 bytes from the binary hash state in reverse order), which allows the initiator of the transaction to rely on the current state of the system for him without worrying about an irreversible block. If it relied on the state of the system in a random minor fork, then the transaction will not fall into the main chain. Thus, network participants can control the execution of the transaction queue and build interaction without waiting for the block to be irreversible. This, in turn, imposes a restriction on the final accounting of such actions, so most important transactions should already be in an irreversible state for the verifying side.

The node allocates space for the block_summary_object with a dimension of 2 bytes (so that the block number that passed through the binary «and» operation with hexfffffits in the range from 0 to 65536) and accepting new blocks overwrites the identifiers (hashes) in this space (and the block_summary_index index) in a circle. 65537 blocks cover a time interval of 196611 seconds (approximately 2.27 days). Accordingly, new transactions can only refer to blocks from this space, so that the node can verify the correspondence of the block identifier (from ref_block_num) with the checksum from ref_block_prefix.

Portable system state

If in the first generation of blockchain systems based on Proof of Work it was required to store all the identifiers of blocks and transactions in the system state, then with the increase in the amount of data, many developers began to look for a way to reduce the costs of the amount of stored data. And large part of the data is stored just in the blocks and the transactions contained in them. In current DLTs this problem has already been solved by matching the irreversible state and the portable state of the system. Some blockchain systems are just beginning to move in this direction, for example, Steem has proposed a solution in the form of Platform Independent State Files – PISF. New blockchain systems (and some of the old pioneers, for example, the XRP Ledger node — rippled) have already been created taking into account the portable state of the system, their architecture allows you to request the current state of the system from trusted nodes, skipping long synchronization, downloading the entire history of the blockchain and independently processing all transactions.

VIZ did not make significant changes to the architecture of the Graphene system state, so the block_summary_index index consisting of block_summary_object structures stores all information about blocks (namely, the block_id_type of a specific block that already contains all the information). This makes it difficult to create a portable system state, because the amount of data for such state will be significant. The only way to upgrade this is to switch to consensus of trusted nodes.

Plugins and their API

Plugins are a universal tool for expanding the node and its capabilities. Some of them only give data, prepare indexes, respond to complex user requests with data filtering, some of them process custom operations and can provide a completely separate service. For example, you can write a plugin that, after a paid subscription of the first level, will generate notifications about important actions in the blockchain or provide a personal message service. A public plugin does not always mean "free". And "free" does not always mean open (in terms of source code).

If we turn to the logical study of the node-services-API chain, we will see an unpleasant situation when public APIs can create problems for the functioning of the node itself. This can occur with a large flow of requests to the service API, especially if the plugin that provides the service works just with the blockchain node. The bandwidth from user requests (or requests from an attacker if he wants to perform a DDOS attack on the service) can prevent the node itself from exchanging data with other nodes. If API requests load the CPU or use large samples of indexes, take a long time to prepare data for a response — there is a problem not only with the service, which begins to slow down, but also with the functioning of the node.

That is why it is recommended to avoid the intersection of the public API node with demanding plugins and the work of the delegate on the same server. A more correct architecture of the service will be a separate independent plugin that has access to processing blocks on a private node, processes the data itself, stores it in a database and allows caching requests. When the load increases, you can always expand the service using clustering technologies for both data and responding nodes (using load balancing).

This section describes all the available VIZ plugins that provide users with access via the API. You can learn the API of a particular plugin yourself if you follow the following instructions:

  • Open the main header file of the plugin (example for database_api), examine DEFINE_API_ARGS (API method name, return value type);
  • Open the main plugin file (example for database_api), examine the DEFINE_API (checking the request parameters, CHECK_ARGS_COUNT, forming a return value of a certain type);
  • Examine plugin_initialize, which can handle boost::program_options::variables_map for more fine-tuning of the plugin through the node configuration file.

Request protocol

All requests must be generated in JSON and executed via RPC. The transport protocol depends on the configuration of the node, there are options for both JSON-RPC via standard HTTP requests, and via WebSocket.

To do this, the following plugins must be connected in the node's configuration file: json_rpc, web server. To accept transactions from users, the network_broadcast_api plugin must also be enabled. Port settings:

# The number of threads for rpc clients. Optimal value *number of cores minus 1*
webserver-thread-pool-size = 2

# IP:PORT for HTTP connections
webserver-http-endpoint = 0.0.0.0:8090

# IP:PORT for WebSocket connects
webserver-ws-endpoint = 0.0.0.0:8091

# IP:PORT for HTTP and WebSocket connections (simultaneous processing of two types of connections)
rpc-endpoint = 0.0.0.0:8081

To process requests with SSL support, it is necessary to forward the ports used through a proxy server (for example, nginx or apache), then requests via https/wss will become possible.

Creating an API request

The rules for generating requests to a public node are quite simple:

{"id":REQUEST_ID,"jsonrpc":"2.0","method":"call","params":["PLUGIN_NAME","PLUGIN_API_METHOD",[ARGS]]}
  • REQUEST_ID — the request number, it is optional (you can number all requests with 1), but when connecting via web sockets (ws), it allows you to associate responses to requests with a similar id;
  • PLUGIN_NAME — the name of the plugin to which the request is being made (for example: database_api, committee_api);
  • PLUGIN_API_METHOD — the name of the method that processes the request (for example: get_accounts from database_api);
  • ARGS — an array of ordered parameters passed to the plugin method.

network_broadcast_api

A plugin responsible for receiving and sending signed blocks and transactions between network nodes

  • broadcast_block — transfer of the signed block (signed_block) to other network nodes;
  • broadcast_transaction — transfer of a signed transaction (signed_transaction) to other network nodes;
  • broadcast_transaction_synchronous — transfer of the signed transaction (signed_transaction) to other network nodes (waits for the block to enter and returns the hash of the transaction, the block number and the transaction number in the block, or returns false if the transaction expires);
  • broadcast_transaction_with_callback — the same as broadcast_transaction_synchronous, except for checking the validity of the transaction before transferring it to the transaction pool and registering the callback method.

custom_protocol_api

  • get_account — returns an account by login with the optional ability to request custom_protocol_id (optionally, overwrites custom_sequence and custom_sequence_block_num if the requested custom protocol id is found in the node history);

Example:

{"id":1,"method":"call","jsonrpc":"2.0","params":["custom_protocol_api","get_account",[["readdle","V"]]]}

Answer:

{
  "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
}

database_api

  • get_account_count — returns the number of accounts in the network;
  • get_accounts — returns an array of accounts by the requested logins (differs from lookup_account_names with additional information);

Example:

{"id":1,"method":"call","jsonrpc":"2.0","params":["database_api","get_accounts",[["wildviz","zozo"]]]}

Answer:

[
  {
    "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
  }
]
  • get_accounts_on_sale — returns a list of accounts for sale, has two parameters: from (offset in the resulting list) and limit (the number of records, can not be more than 1000);

Example:

{"id":1,"method":"call","jsonrpc":"2.0","params":["database_api","get_accounts_on_sale",[0,1000]]}

Answer:

[
  {"account":"btc","account_seller":"ae","account_offer_price":"5000.000 VIZ"},
  {"account":"press","account_seller":"on1x","account_offer_price":"10000.000 VIZ"}
]
  • get_subaccounts_on_sale — returns a list of subaccounts for sale, has two parameters: from (the offset in the resulting list) and limit (the number of records, can not be more than 1000);

Example:

{"id":1,"method":"call","jsonrpc":"2.0","params":["database_api","get_subaccounts_on_sale",[0,1000]]}

Answer:

[
  {"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"}
]
  • get_block — returns information about a block by its number;

Example:

{"id":1,"method":"call","jsonrpc":"2.0","params":["database_api","get_block",["1"]]}

Answer:

{
  "previous": "0000000000000000000000000000000000000000",
  "timestamp": "2018-09-29T10:23:27",
  "witness": "committee",
  "transaction_merkle_root": "0000000000000000000000000000000000000000",
  "extensions": [
    [
      1,
      "1.0.0"
    ]
  ],
  "witness_signature": "2003120d1f1d8bb8e8325036838e5269332fbec7c88cd3cc76e4517d27772856ce43ef6bf0a7fde9987cec9467b944e7626db100b70867e7ec82308879a021e97a",
  "transactions": []
}
  • get_block_header — returns the block header by its number;

Example:

{"id":1,"method":"call","jsonrpc":"2.0","params":["database_api","get_block_header",["2"]]}

Answer:

{
  "previous": "000000010496d4414ddcee5b76f9a6b950da6fe9",
  "timestamp": "2018-09-29T10:23:30",
  "witness": "committee",
  "transaction_merkle_root": "0000000000000000000000000000000000000000",
  "extensions": []
}
  • get_chain_properties — returns the median values of the voted network parameters;

Example:

{"id":1,"method":"call","jsonrpc":"2.0","params":["database_api","get_chain_properties",[]]}

Answer:

{
  "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
}
  • get_config — returns the presets in the VIZ protocol configuration file;

Example:

{"id":1,"method":"call","jsonrpc":"2.0","params":["database_api","get_config",[]]}

Answer:

{
  "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
}
  • get_database_info — returns information on the use of the database (by object type);

Example:

{"id":1,"method":"call","jsonrpc":"2.0","params":["database_api","get_database_info",[]]}

Answer:

{
  "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
    }
  ]
}
  • get_dynamic_global_properties — returns data about dynamic global network properties;

Example:

{"id":1,"method":"call","jsonrpc":"2.0","params":["database_api","get_dynamic_global_properties",[]]}

Answer:

{
  "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
}
  • get_escrow — returns information about a three-way transaction by account login and transaction id (id);
  • get_expiring_vesting_delegations — returns a list of the delegated share to be returned (by the expiration time of the return);
  • get_hardfork_version — returns the version of the current hardfork;
  • get_master_history — returns a list of the history of changes of the master privileges for the specified account (in the form of master_authority_history_api_object);
  • get_next_scheduled_hardfork — returns the next scheduled hardfork;

Example:

{"id":1,"method":"call","jsonrpc":"2.0","params":["database_api","get_next_scheduled_hardfork",[]]}

Answer:

{
  "hf_version": "2.4.0",
  "live_time": "2019-04-30T05:00:00"
}
  • get_potential_signatures — returns potential public keys for signing a transaction;

Example:

{"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":[]}]]}

Answer:

[
  "VIZ5iCdUDKypJU3iCp1vbkTk5P7hEW1GgK6E75V1yZZznGU7NuV32"
]
  • get_proposed_transactions — returns all proposed transactions to the account (attributes: account, from — the initial transaction number, limit — the threshold value); returns all proposed transactions to the account (attributes: account, from - the initial transaction number, limit - the threshold value)
  • get_recovery_request — returns data on the request to restore access to the account;
  • get_required_signatures —returns the public keys from the suggested ones that are necessary for signing the transaction (you need to send the transaction without a signature and provide the public keys);

Example:

{"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"]]]}

Answer:

[
  "VIZ5iCdUDKypJU3iCp1vbkTk5P7hEW1GgK6E75V1yZZznGU7NuV32"
]
  • get_transaction_hex —returns the hex value of the raw transaction;

Example:

{"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"]}]]}

Answer:

89a08b9f8d495a68a45d012f06736f6369616c06736f6369616c0f000000000000000000037562690000011f64cb23ba686126f9a00d904840e472de44e0e2291eca5c6b380083ee7a0b9f9934bbe9f03404a33a6df0630a020ff4750b886b65f4af8cd9877df8128d45da05
  • get_vesting_delegations — returns a list of the delegated account share;
  • get_withdraw_routes — returns an array of ways to reduce the share for the account;
  • lookup_account_names — returns an array of accounts by the requested logins;

Example:

{"id":1,"method":"call","jsonrpc":"2.0","params":["database_api","lookup_account_names",[["wildviz","zozo"]]]}

Answer:

[
  {
    "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
]
  • lookup_accounts — returns an array of account logins along the lower border with an indication of the number of returned elements (no more than 1000);

Example:

{"id":1,"method":"call","jsonrpc":"2.0","params":["database_api","lookup_accounts",["ae","10"]]}

Answer:

[
  "ae",
  "ae-witness",
  "ae.witness",
  "ae0",
  "ae1",
  "ae10",
  "ae100",
  "ae101",
  "ae102",
  "ae103"
]
  • verify_account_authority — verifies the signature of the transaction by a separate account with the indication of the public key;
  • verify_authority — accepts a signed transaction and returns TRUE if the transaction is signed with all the necessary keys;

account_by_key

  • get_key_references — returns an array of account logins that contain the specified public key.

Request example:

{"id":1,"method":"call","jsonrpc":"2.0","params":["account_by_key","get_key_references",[["VIZ5Z2po7K5CoCXw2xLPPt8JJvJLJ3xVNANLgTy9KDfLeZH2urSSd","VIZ1111111111111111111111111111111114T1Anm"]]]}

Answer:

[
  [
    "on1x"
  ],
  [
    "viz"
  ]
]

account_history

  • get_account_history — returns the history of operations (including virtual ones) associated with a specific account. The track-account-range can be repeatedly specified in the configuration file as a json string: ["from","to"].

Request example:

{"id":1,"method":"call","jsonrpc":"2.0","params":["account_history","get_account_history",["on1x","-1","5"]]}

Answer:

[
  [
    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"
        }
      ]
    }
  ]
]

committee_api

  • get_committee_request — returns information about the application (you can specify the number of votes returned for the application);

Example:

{"id":1,"method":"call","jsonrpc":"2.0","params":["committee_api","get_committee_request",["39","1"]]}

Answer:

{
  "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"
    }
  ]
}
  • get_committee_request_votes — returns an array of all votes on the application;

Example:

{"id":1,"method":"call","jsonrpc":"2.0","params":["committee_api","get_committee_request_votes",["39"]]}

Answer:

[
  {
    "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"
  },
  ...
]
  • get_committee_requests_list — returns an array of application IDs to the committee, depending on the status (0 - pending review, 1 - canceled by the creator, 2 - insufficient votes, 3 - insufficient payment for the application, 4 — the application is approved and waiting for payments, 5 - payments are completed);

Example:

{"id":1,"method":"call","jsonrpc":"2.0","params":["committee_api","get_committee_requests_list",["2"]]}

Answer:

[
  2,
  3,
  13,
  14,
  33
]

invite_api

  • get_invite_by_id — returns an invite by ID;

Example:

{"id":1,"method":"call","jsonrpc":"2.0","params":["invite_api","get_invite_by_id",["0"]]}

Answer:

{
  "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
}
  • get_invite_by_key — returns an invite using a public key;
  • get_invites_list — returns an array of invite IDs by status (0 - not activated, 1 - redeemed to the account, 2 - used for account registration);

Example:

{"id":1,"method":"call","jsonrpc":"2.0","params":["invite_api","get_invites_list",["0"]]}

Answer:

[
  6,
  8,
  15,
  20,
  34,
  ...
]

operation_history

  • get_ops_in_block — returns an array of operations by block number (you can specify whether to show only virtual operations);

Example:

{"id":1,"method":"call","jsonrpc":"2.0","params":["operation_history","get_ops_in_block",["10913871","false"]]}

Answer:

[
  {
    "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"
      }
    ]
  }
]
  • get_transaction — returns transaction data by its hash (id);

Example:

{"id":1,"method":"call","jsonrpc":"2.0","params":["operation_history","get_transaction",["d5f53163bc237b17c8fd6f3278d3ca1b4ae21691"]]}

Answer:

{
  "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
}
  • get_paid_subscriptions — returns a list of paid subscriptions sorted by the initiator account (accepts 2 parameters: from — the number of records to indent from the beginning, limit — the limit of the number of results, can not exceed 1000);

Example:

{"id":1,"method":"call","jsonrpc":"2.0","params":["paid_subscription_api","get_paid_subscriptions",[0,1]]}

Answer:

[
  {
    "update_time" : "2019-06-05T15:07:57",
    "creator" : "tratata",
    "id" : 3,
    "period" : 30,
    "levels" : 10,
    "url" : "",
    "amount" : 500000
  }
]
  • get_active_paid_subscriptions — returns an array of active paid subscription agreements for the account;

Example:

{"id":1,"method":"call","jsonrpc":"2.0","params":["paid_subscription_api","get_active_paid_subscriptions",["on1x"]]}

Answer:

[
  "viz.world"
]
  • get_inactive_paid_subscriptions — returns an array of canceled paid subscription agreements for the account;
  • get_paid_subscription_options — returns data about the agreement for a paid subscription to the account;

Example:

{"id":1,"method":"call","jsonrpc":"2.0","params":["paid_subscription_api","get_paid_subscription_options",["viz.world"]]}

Answer:

{
  "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
}
  • get_paid_subscription_status — returns data about the subscriber's agreement for a paid subscription to a specific account;

Example:

{"id":1,"method":"call","jsonrpc":"2.0","params":["paid_subscription_api","get_paid_subscription_status",["on1x","viz.world"]]}

Answer:

{
  "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
}

witness_api

  • get_active_witnesses — returns an array of delegates in the current round (out of 21 slots, if there is this number of delegates);

Example:

{"id":1,"method":"call","jsonrpc":"2.0","params":["witness_api","get_active_witnesses",[]]}

Answer:

[
  "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"
]
  • get_witness_by_account — returns the delegate in accordance with its username;
  • get_witness_count — returns the number of accounts that have declared their desire to be a delegate;

Example:

{"id":1,"method":"call","jsonrpc":"2.0","params":["witness_api","get_witness_count",[]]}

Answer:

59
  • get_witness_schedule — returns the delegate queue supplemented with the calculated median values of the network parameters and the majority version of the hard fork;

Example:

{"id":1,"method":"call","jsonrpc":"2.0","params":["witness_api","get_witness_schedule",[]]}

Answer:

{
  "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"
}
  • get_witnesses — returns an array of delegates according to their id (you can request several in the array);

Example:

{"id":1,"method":"call","jsonrpc":"2.0","params":["witness_api","get_witnesses",[["0"]]]}

Answer:

[
  {
    "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"
  }
]
  • get_witnesses_by_vote — returns an array of delegates sorted by the potential of the received votes (the left border of the list and the number of returned values in the array are specified, but not more than 100);

Example:

{"id":1,"method":"call","jsonrpc":"2.0","params":["witness_api","get_witnesses_by_vote",["","10000"]]}

Answer:

[
 {
   "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"
 }
]
  • get_witnesses_by_counted_vote — returns an array of delegates sorted by the potential of the received votes received, taking into account the penalty imposed for skipping blocks (the left border of the list and the number of returned values in the array are specified, but not more than 100);
  • lookup_witness_accounts — returns an array of logins of accounts that declared themselves as delegates (the left border of the list and the number of returned values in the array are specified, but not more than 1000).

Example:

 {"id":1,"method":"call","jsonrpc":"2.0","params":["witness_api","lookup_witness_accounts",["","4"]]}

Answer:

 [
  "t3",
  "lb",
  "ae",
  "in"
]

Libraries for working with VIZ

Ready-made libraries are often used for application development. The availability of a library for a specific programming language depends on the availability of blanks for working with cryptography, large numbers and transport protocols (http/ws). Because VIZ has historically evolved from Graphene, most libraries for such blockchain systems as EOS/Steem/Golos are also suitable for VIZ. A distinctive feature is the format of communication with the node (json-rpc structure), the order and name of the parameters when describing the operation, the format of complex data in binary form (for example, the format of VIZ and SHARES assets differs from the new SMT format in Steem).

Despite the different syntax, the basis for interaction with VIZ is the same: cryptography for keys and message signatures, signature verification using data and a public key, transaction formation, interaction with the node.

Each developer can raise his own node to interact with VIZ, but there are public nodes for beginners to understand:

The main VIZ libraries that support most of the node request API and transaction generation are listed below.


JavaScript

The best tool for application development is the vis-js-lib library. It has support for everything you need for both server (nodejs) and user (js in browsers) interaction with VIZ:

  • Creating and encoding keys;
  • API requests;
  • Formation of transactions;
  • Simplified transaction constructor for operations;
  • Callback functions for requests;

Viz-js-lib documentation in English is available on GitHub. For examples of frequently used operations, see [Code examples](code-examples. md#viz-js-lib) section.

Python

The thallid-viz library from ksantoprotein supports both API requests and transaction formation. Available many examples with different operations.

The viz-python-lib library supports most of the necessary, but there are no examples and documentation (the library is still unfinished).

PHP

After switching to the adapted BigNumber and Elliptic Curve libraries, it became available to use cryptography without building secp256k1 for PHP and enabling support for GMP.

The viz-php-lib library supports JsonRPC, key handling, transaction generation, message encryption via shared key (compatible with viz-js-lib), there are examples, PSR-4 support and installation without additional dependencies (all-in-one).

The php-graphene-node-client library with VIZ support, which can be installed via Composer.

GO

The viz-go-lib library is great for API requests and studying the formation of transactions. Unfortunately, there is no documentation for the library, as well as examples with individual operations.

Swift

The viz-swift-lib library — a library on Swift, which can be installed via Swift Package Manager.

Dart

The viz_dart_ecc cryptographic library — allows you to generate a public key from a private one, sign data, verify the signature.

The viz-transaction library — allows you to form and sign transactions for the VIZ blockchain (the library does not contain methods for translating a transaction to the blockchain node, for this you need to use any other libraries for the http/ws protocol).

Other

If you haven't found the required programming language, then you can pay attention to the existing libraries for EOS and Steem. To modify them and get VIZ support, it is enough to check the format of json-rpc requests, change the chain_id (in VIZ, it is equal to2040effda178d4fffff5eab7a915d4019879f5205cc5392e4bcced2b6edda0cd — this is the prefix for signing raw transactions) and configure the operation constructor.

Code examples

Novice developers are always recommended to read the documentation for a particular library. This helps both to understand how the library works and to remember the features that can be used when developing applications. This section describes the most popular queries in the form of examples. The most used library for VIZ applications is viz-js-lib, so the examples will be with its use.


viz-js-lib

Detailed documentation in English with an indication of all methods and their attributes available at the link.

Connecting the library

Depending on the server (nodejs) or browser (js) usage, the library needs to be connected in different ways.

For nodejs, the current instruction is to install the library vianpm install viz-js-lib --save and connect it in a js file viavar viz = require('viz-js-lib');.

For js connection, you can either build the webpack libraries yourself via thenpm buildconsole, or use the already built library from jsDelivr CND or Unpkg CDN. Just add the script tag to the html file and specify the library url: <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/viz-js-lib@latest/dist/viz.min.js"></script>after which you will have access to the global variable vizvia the console.

Using a public node

As long as your application does not have a large flow of users, it is reasonable to use an available public node. At the time of writing, there are two public nodes available in VIZ:

  • Public node from the lex delegate: https://viz.lexa.host/ for JSON-RPC requests over HTTPS and wss://viz.lexa.host/ws for JSON-RPC requests over WebSocket over SSL;
  • Public node from the solox delegate: https://solox.world/ for JSON-RPC requests over HTTPS and wss://solox.world/ws for JSON-RPC requests over WebSocket over SSL.

Example of configuring viz to work with the https://viz.lexa.host/node:

var api_gate='https://viz.lexa.host/';
viz.config.set('websocket',api_gate);

API-requests

In the Plugins and their API section the main plugins and their requests were listed — all of them are available in the viz-js-lib library. In order to execute a particular request, it is enough to translate its name to camelCase.

For example, if you decide to make a get_database_info request to the database_api plugin, then you need to run the code:

viz.api.getDatabaseInfo(function(err,response){
    if(!err){
        //response received
        console.log(response);
    }
    else{
        //error
        console.log(err);
    }
});

If the request requires input data, then you add them to the beginning of the call. For example, to request get_active_paid_subscriptions to the paid_subscription_api plugin, you must specify the user for whom the search for active paid subscriptions will be performed:

var subscriber='on1x';
viz.api.getActivePaidSubscriptions(subscriber,function(err,response){
    if(!err){
        //response received
        console.log(response);
    }
    else{
        //error
        console.log(err);
    }
});

Transaction broadcasting

For each operation from the VIZ protocol, there is a separate method in the vis-js-lib library that accepts a private key (for signing the transaction) and operation parameters. The name of the operation, similar to the API methods, should be translated to CamelCase format. Sample code for broadcasting the accounts_metadata operation (writing account meta data to the blockchain):

var regular_key='5K...';//private key
var user_login='test';//account login
var metadata={'name':'Test account','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){
        //the transaction was accepted by the public node
        console.log(result);
    }
    else{
        //the node did not accept the transaction
        console.log(err);
    }
});

Dynamic global properties of network

Some newcomers want to periodically send requests to the node and get up-to-date data about DGP (Dynamic Global Properties) in order to show new blocks based on this, fulfill the conditions for an irreversible block, or highlight the last one who signed the block in the list of delegates. To do this, it is enough to request data on the timer every 3 seconds (the time between blocks):

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);

Getting account information

Sample code for getting information about the account and calculating the current value of the account's energy (taking into account the speed of its recovery):

var current_user='on1x';
viz.api.getAccounts([current_user],function(err,response){
    if(!err){
        //response received
        if(typeof response[0] !== 'undefined'){
            //we have requested an array of accounts, we are looking at the zero element corresponding to current_user
            let last_vote_time=Date.parse(response[0].last_vote_time);
            //we take into account the user's time zone
            let delta_time=parseInt((new Date().getTime() - last_vote_time + (new Date().getTimezoneOffset()*60000))/1000);
            let energy=response[0].energy;
            //calculating the recovered energy
            //energy recovery rate from 0% to 100% CHAIN_ENERGY_REGENERATION_SECONDS 5 days (432000 seconds)
            let new_energy=parseInt(energy+(delta_time*10000/432000));
            //the energy can not be more than 100%
            if(new_energy>10000){
                new_energy=10000;
            }
            console.log('current energy of account',new_energy);
        }
        else{
            console.log('the account was not found',current_user);
        }
    }
    else{
        //error
        console.log(err);
    }
});

Working with keys

Cryptographic keys are the X and Y coordinates that are written in the generally accepted DER format on the secp256k1 elliptic curve (SHA-256 serves as a hash function). In the viz-js-lib library transformations and working with keys belong to theviz.authmodule.

The Graphene ecosystem has come up with a mechanism for human-readable passwords. Due to the brute force, it is not recommended to use them, therefore, in order to complicate the multiple search of private keys to the public, we came to certain rules for generating keys in the form of concatenation of strings: account login, password (complex), access type.

Some applications have agreed to use these rules, thus simplifying the user's access to various account features using a common password. For example, the user with name test is registered using the general password PK3452JENDK332. When logging in to the application using this username and password, the application can independently generate the keys of the desired access type, simply using string concatenation. Does the user want to transfer tokens? The application generates a private active key on the fly by the testPK3452JENDK332active line. Is the user rewarding someone? The application generates a private regular key using the string testPK3452JENDK332regular. This makes it easier for the user to access using a shared password, but it deprives the flexibility and puts the account at risk. Access types have different permissions and if the trusted environment of the user's trusted environment or the site is compromised, access to the account can be intercepted. Therefore, some applications abandon the rules or agreements for the sake of user security and do not support a common password.

Account registration

Usually, when registering a user, the application generates a password on its own. But there are exceptions when the application allows you to use your password to generate keys. The library allows you to independently specify the strings for generating the key in the viz.auth.toWif(account_login,general_pass,auth_type);method. The example below shows a function for generating a random password of a given length and generating a key using it without binding to the user and access 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;
}

You can get a public key using the specified private key using the viz.auth.wifToPublic(wif)method. For those applications that want to generate keys by concatenation of login, password and access type, there is the viz.auth.getPrivateKeys(account_login,general_pass,auth_types)method. The method returns an array using the result.type template for private keys (to be passed to the user) and result.typePubkey for public keys (which need to be transferred to the blockchain to be saved for the user's account). The function code for registering a new account in VIZ using the main password:

var user_login='test';//registrator account
var active_key='5K...';//private active key

function create_account_with_general_pass(account_login,token_amount,shares_amount,general_pass){
    let fixed_token_amount=''+parseFloat(token_amount).toFixed(3)+' VIZ';//tokens that will be transferred to the share of the new account
    let fixed_shares_amount=''+parseFloat(shares_amount).toFixed(6)+' SHARES';//the share that will be delegated to the new account
    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'];//access types
    let keys=viz.auth.getPrivateKeys(account_login,general_pass,auth_types);
    //access types contain public keys with a weight sufficient for performing operations
    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);
        }
    });
}

Creating a voucher (invite code)

There is a voucher mechanism in the VIZ blockchain. Anyone can create them by transferring VIZ tokens to it. Vouchers can be redeemed or used as an invite code for simplified registration of a new account. In the first case, the tokens are transferred to the bearer's account, in the second case, the tokens are converted into a network share for a new account (with a single access key for all access types).

Sample code for creating a voucher:

var user_login='test';
var active_key='5K...';//private active key
var fixed_amount='100.000 VIZ';//the number of tokens spent on the voucher
var private_key=pass_gen();//generating a private key
var public_key=viz.auth.wifToPublic(private_key);//getting a public key from a private one
viz.broadcast.createInvite(active_key,user_login,fixed_amount,public_key,function(err,result){
    if(!err){
        console.log('The voucher has been created, the public key for verification: '+public_key+', private key to use: '+private_key);
    }
    else{
        console.log(err);
    }
});

Registration via an invite code

For anonymous use of the invite code, there is an inviteaccount in the system with 5KcfoRuDfkhrLCxVcE9x51J6KN9aM9fpb78tLrvvFckxVV6FyFWprivate active key, code example:

var receiver='newtestaccount';//new account login
var secret_key='5K...';//private key of the invite code
var private_key=pass_gen();//generating a private key
var public_key=viz.auth.wifToPublic(private_key);//getting a public key from a private one
viz.broadcast.inviteRegistration('5KcfoRuDfkhrLCxVcE9x51J6KN9aM9fpb78tLrvvFckxVV6FyFW','invite',receiver,secret_key,public_key,function(err,result){
    if(!err){
        console.log('Account '+receiver+' registered, shared private key for all access types: '+private_key);
    }
    else{
        console.log(err);
    }
});

Repayment of the voucher

For anonymous use of the voucher, there isinviteaccount in the system with a private active key 5KcfoRuDfkhrLCxVcE9x51J6KN9aM9fpb78tLrvvFckxVV6FyFW, code example:

var receiver='test';//account login
var secret_key='5K...';//private key of the invite code
var public_key=viz.auth.wifToPublic(secret_key);//getting the public key of the voucher
viz.broadcast.claimInviteBalance('5KcfoRuDfkhrLCxVcE9x51J6KN9aM9fpb78tLrvvFckxVV6FyFW','invite',receiver,secret_key,function(err,result){
    if(!err){
        console.log('Account '+receiver+' successfully redeemed a voucher with a public key '+public_key);
    }
    else{
        console.log(err);
    }
});

Converting the VIZ tokens into a share

For automatically converting all available VIZ tokens into a share of the SHARES network, you need to request account information and convert them to your own share using the transfer_to_vesting operation:

var current_user='test';
var active_key='5K...';//private active key
viz.api.getAccounts([current_user],function(err,response){
    if(!err){
        //response received
        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('convertation to a network share',response[0].balance);
                        console.log(result);
                    }
                    else{
                        console.log(err);
                    }
                });
            }
            else{
                console.log('the balance is at zero');
            }
        }
        else{
            console.log('the account was not found',current_user);
        }
    }
    else{
        //error
        console.log(err);
    }
});

Converting a share into VIZ tokens

New developers often face the problem that the user has delegated part of the tokens to another account and they need to calculate the available share for converting SHARES to VIZ. An example of the code that automatically sets the SHARES available for conversion to output from the share:

var current_user='test';
var active_key='5K...';//private active key
viz.api.getAccounts([current_user],function(err,response){
    if(!err){
        //response received
        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 available for convertation',fixed_shares);
            viz.broadcast.withdrawVesting(active_key,current_user,fixed_shares,function(err,result){
                if(!err){
                    console.log('launching the convertation of the network share into VIZ tokens',fixed_shares);
                    console.log(result);
                }
                else{
                    console.log(err);
                }
            });
        }
        else{
            console.log('account was not found',current_user);
        }
    }
    else{
        //error
        console.log(err);
    }
});

Tokens transfering

Example of transferring 1.000 VIZ from the account balance to the committee:

var current_user='test';
var active_key='5K...';//private active key
var target='committee';
var fixed_amount='1.000 VIZ';
var memo='Заметка';//utf-8 including emoji
viz.broadcast.transfer(active_key,current_user,target,fixed_amount,memo,function(err,result){
    if(!err){
        //response received
        console.log(result);
    }
    else{
        //error
        console.log(err);
    }
});

Awarding of network member

An account can award another network member using the award operation. You can specify the purpose of the target award, the reason (the custom_sequence number or memo note), as well as the beneficiaries (accounts that will share the award of target). Example:

var current_user='test';//the awarder account
var regular_key='5K...';//the private regular key of the awarder

var target='viz.plus';//the award recipient - the customer of the article
var energy='1000';//10.00% will be spent from the actual energy of the account
var custom_sequence=0;//the number of the custom operation
var memo='спасибо за viz cookbook';//utf-8 including emoji
var beneficiaries_list=[{"account":"on1x","weight":2000}];//20% to the author of the article
viz.broadcast.award(regular_key,current_user,target,energy,custom_sequence,memo,beneficiaries_list,function(err,result){
    if(!err){
        //response received
        console.log(result);
    }
    else{
        //error
        console.log(err);
    }
});

Changing account keys

Sometimes there is a need to change the access rights of the account. This may be due to adding a new key, delegating management, or creating conditions for multi-signature account management (when operations require signing with multiple keys).

The permissions for performing operations have the following structure:

  • weight_threshold — the required weight for approving a transaction with the necessary type of operations;
  • account_auths — an array of accounts and their weights. Accounts can add the weight of a transaction when adding a signature to it with a key;
  • key_auths — an array of public keys and their weights.

The system checks the transaction, the operations in it, checks for the signatures of the relevant accounts and whether they have enough weight to perform the required type of action.

Example of permissions with a single key:

{
    "weight_threshold": 1,
    "account_auths": [],
    "key_auths": [
        ["VIZ6LWhhUzKmYM5VcxtC2FpEjzr71imfb7DeGA9yodeqnvtP2SYjA", 1]
    ]
}

If the account has these permissions written to the regular access type, then to perform the award operation, the blockchain will require the signature of the transaction with the key 5KRLZitDd5c9uZzDgTMF4se4eVewENtZ29GbCuKwbT3msRbtLgi(which corresponds to the public keyVIZ6LWhhUzKmYM5VcxtC2FpEjzr71imfb7DeGA9yodeqnvtP2SYjAspecified in the permissions).

When delegating control to another account, for example,test, you need to change the permissions to:

{
    "weight_threshold": 1,
    "account_auths": [
        ["test", 1]
    ],
    "key_auths": [
        ["VIZ6LWhhUzKmYM5VcxtC2FpEjzr71imfb7DeGA9yodeqnvtP2SYjA", 1]
    ]
}

After that, the blockchain will require a signature either with the specified key, or with a key of a similar type of access to the test account.

Multi-sign management suggest the complication of permissions, for example, to manage 2 out of 3, you can use the permission:

{
    "weight_threshold": 4,
    "account_auths": [],
    "key_auths": [
        ["VIZ6LWhhUzKmYM5VcxtC2FpEjzr71imfb7DeGA9yodeqnvtP2SYjA", 2],
        ["VIZ5mK1zLnYHy7PbnsxRpS4NbKjEoH2J9eBmgSjVKJ5BKQpLLj9T4", 2],
        ["VIZ4uiqeDPsoteSFVbTWPBUbmfzxYkJyXYmA6B1pAFWZ59n4iBuUK", 2]
    ]
}

In order for the transaction to be accepted by the blockchain, it is necessary to add signatures of at least 2 of the 3 specified keys. In this example, the public key VIZ4uiqeDPsoteSFVbTWPBUbmfzxYkJyXYmA6B1pAFWZ59n4iBuUK corresponds to the private key 5KMBKopgd56MZvV8FYhp5AP7AWFyLKiybqRnZYgjXukw34VRE78.

Let's consider an example of resetting account access (changing all keys and permissions):

//the function requires a private master key from the account
function reset_account_with_general_pass(account_login,master_key,general_pass){
    if(''==general_pass){
        //if a general password is not specified, we will generate it
        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("The user was not found");
        }
    });
}

Declaring an account as a delegate

Each delegate has a block signing key, which is responsible for verifying the block signature by the blockchain. The delegate node is configured with a private signature key (it is better to form it in advance), and a public key is sent to the blockchain to verify signatures. If the delegate has been absent for a long time, the blockchain will mark it as disabled by zeroing the signature key. You can disconnect yourself by setting an empty signature key viz111111111111111111111111111111111114t1anm. Sample code for declaring an account as a delegate:

var account_login='test';
var active_key='5K...';
var url='https://...';//link to the statement of intent to be a delegate 
var private_key=pass_gen();//generating a private key
var signing_key=viz.auth.wifToPublic(private_key);//public key
viz.broadcast.witnessUpdate(active_key,account_login,url,signing_key,function(err,result){
    if(!err){
        console.log('The delegate '+account_login+' declared a desire to be a delegate, a private key for signing blocks: '+private_key);
    }
    else{
        console.log(err);
    }
});

Voting for a delegate

In order for a delegate to start signing blocks, he must have a non-zero weight of the weight of votes. The share of the account when voting for several delegates is divided between them. Sample code for voting for a delegate:

var account_login='test';
var active_key='5K...';
var witness_login='witness';
var value=true;//boolean value of the voice (true - vote for a delegate, false - remove the vote)
viz.broadcast.accountWitnessVote(active_key,account_login,witness_login,value,function(err,result){
    if(!err){
        console.log(result);
    }
    else{
        console.log(err);
    }
});

Transfer of voting rights to a proxy

If the user does not participate in the selection of delegates, he can delegate the right to dispose of his share to another account. To do this, there is an account_witness_proxy operation, the registrator application can use it in order not to confuse its user with unnecessary information about the device of the blockchain system and not lose its influence (because VIZ tokens can be spent for account registration, the application invests the potential of a similar share of the network in users).

If the user decides to participate independently in the selection of delegates, the first vote for the delegate will cancel the proxy.

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);
    }
});

Setting the voting parameters of the network

Delegates broadcast their position on the network's voting parameters. The blockchain system calculates the median values of the voted parameters every cycle of the delegate queue (21 blocks) and fixes them for this cycle. For more information about the network parameters to be voted, see the Objects and structures in the VIZ section. Example of the ersioned_chain_properties_update operation for broadcasting the voting parameters of network by a delegate:

var account_login='test';
var active_key='5K...';//private active key

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;//outdated parameter
props.min_curation_percent=0;//outdated parameter
props.max_curation_percent=10000;//outdated parameter
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);
    }
});

Application to the Committee

var account_login='test';
var regular_key='5K...';//private regular key
var url='https://...';//URL with the application description
var worker='test';//the login of the account that, if approved, will receive funds from their committee fund
var min_amount='0.000 VIZ';//the minimum number of VIZ tokens to satisfy the application
var max_amount='10000.000 VIZ';//maximum number of VIZ tokens
var duration=5*24*3600;//the duration of the request in seconds (must be between COMMITTEE_MIN_DURATION (5 days) and COMMITTEE_MAX_DURATION (30 days))

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);
    }
});

Canceling the application in the Committee

Only the creator of the application can cancel the application.

var account_login='test';
var regular_key='5K...';//private regular key
var request_id=14;//number of the canceled application of the committee

viz.broadcast.committeeWorkerCancelRequest(regular_key,account_login,request_id,function(err,result){
    if(!err){
        console.log(result);
    }
    else{
        console.log(err);
    }
});

Voting on the application in the committee

Any member of the network can vote for the application. At the end of the application validity period, the satisfied payment amount is calculated from the committee, taking into account the share of votes from the total weight of those who voted and the percentage of agreement with the terms of the application set by them (from -100% to +100%). If the amount of the share of the network of those who voted exceeds the voting parametercommittee_request_approved_min_percentof the network and the estimated amount is included in the framework laid down by the application, then the application is satisfied by the committee and put in the queue for payments. The whole principle of operation can be explored in the database.cpp committee_processing() method file. Sample code with voting for the application:

var account_login='test';
var regular_key='5K...';//private regular key
var request_id=15;//application number of the committee
var percent=8000;//80% percentage of the maximum amount of the application, for which the voting person considers it correct to satisfy the application
viz.broadcast.committeeVoteRequest(regular_key,account_login,request_id,percent,function(err,result){
    if(!err){
        console.log(result);
    }
    else{
        console.log(err);
    }
});

The system of paid subscriptions in VIZ allows any account to set the terms of the agreement, after signing which VIZ tokens will be transferred from the subscriber to the balance of the agreement provider. The system allows you to change the terms of the agreement to the provider and automatically tries to agree on them at the time of expiration and renewal of active subscriptions without prejudice to the subscriber. The subscriber can specify whether he pays the provider once under the agreement or agrees to automatically renew the subscription if the required amount of VIZ tokens is available on his balance. Publication of the terms of the paid subscription agreement:

var account_login='test';
var active_key='5K...';//private active key
var url='https://...';//URL with a description of the paid subscription and agreement
var levels=3;//the number of paid subscription levels (the provider decides the number of levels and what it will provide for each), if you specify 0, then new subscriptions will not be able to be issued
var amount='100.000 VIZ';//the number of VIZ tokens for each paid subscription level
var period=30;//subscription validity period (number of days)
viz.broadcast.setPaidSubscription(active_key,account_login,url,levels,amount,period,function(err,result){
    if(!err){
        console.log(result);
    }
    else{
        console.log(err);
    }
});

An account wishing to conclude a paid subscription agreement must confirm to the system the terms of the agreement, the desired subscription level and the need for automatic renewal. You can also change the subscription level, the system will automatically recalculate and either withdraw the required amount, or extend the time for the expiration of the agreement:

var account_login='subscriber';
var active_key='5K...';//private active key
var provider_account='test';//login of the paid subscription provider's account
var level=2;//the desired level of paid subscription
var amount='100.000 VIZ';//the number of VIZ tokens for each level according to the agreement
var period=30;//subscription validity period under the agreement
var auto_renewal=true;//the need for automatic renewal
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);
    }
});

Getting information about the terms of the paid subscription agreement:

var provider_account='test';
viz.api.getPaidSubscriptionOptions(provider_account,function(err,response){
    if(!err){
        console.log(response);
    }
    else{
        console.log(err);
    }
});

You can get a list of active or inactive paid subscriptions using an API request:

var account_login='subscriber';
viz.api.getActivePaidSubscriptions(account_login,function(err,response){
    for(let i in response){
        console.log('A paid subscription agreement with the account is valid '+response[i]);
    }
}
viz.api.getInactivePaidSubscriptions(account_login,function(err,response){
    for(let i in response){
        console.log('A paid subscription agreement with the account is valid'+response[i]);
    }
}

You can check the current agreement via an API request:

var account_login='subscriber';
var provider_account='test';
viz.api.getPaidSubscriptionStatus(account_login,provider_account,function(err,response){
    if(!err){
        console.log('Agreement with the account '+response.creator);
        console.log('Status of the agreement: '+(response.active?'active':'inactive'));
        console.log('Auto-renewal: '+(response.auto_renewal?'enabled':'disabled'));
        console.log('Subscription level: '+response.level);
        console.log('The cost of the subscription level: '+(response.amount/1000)+' VIZ');
        console.log('Subscription period in days: '+response.period);
        console.log('Agreement start date: '+response.start_time);
        if(response.active){
            console.log('Expiration date of the agreement: '+response.next_time);
        }
    }
    else{
        console.log(err);
    }
});

Delegating a share

In the VIZ blockchain, it is possible to delegate a share to another account, which allows you to transfer share management related to awards, voting in the committee and obtaining network bandwidth. Delegation does not apply to voting for delegates, since there is a separate operation in the form of transferring the voting right of its entire share by the account_witness_proxyoperation.

The rules for delegating a share are partially prescribed in the network protocol, partially controlled by delegates:

  • The minimum amount of delegation is set by delegates via the voting parameter min_delegation;
  • The delegation margin coefficient when creating a new account is set by delegates via the voting parameter create_account_delegation_ratio;
  • The duration of delegation (the inability to cancel it) when creating an account is set by delegates via the voted parameter create_account_delegation_time;
  • The minimum duration of delegation is set in the protocol (the CHAIN_CASHOUT_WINDOW_SECONDS constant is equal to one day);

When delegating, a protective mechanism is triggered against an abusing of double waste of energy. If the delegate transfer 50% of their share, then the energy will be reduced by 50%. In this case, the energy can become negative (up to -100%). It is worth considering that the delegate_vesting_sharesoperation sets the actual value of the delegated share. If you want to cancel delegation, then you need to set the value of the delegated share to 0.000000 SHARES. If you want to increase the delegation from 1000.000000 SHARES to3000.000000 SHARES, then you just need to set the delegation value to3000.000000 SHARES.

Sample code for delegating a share to another account:

var account_login='test';
var active_key='5K...';//private active key
var delegatee='recipient';//target of delegation
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);
    }
});

Getting information about delegation:

var account_login='test';
var start_from=0;
var count=1000;
var type=0;//0 - outgoing delegation, 1 - incoming delegation
viz.api.getVestingDelegations(account_login,start_from,count,type,function(err,response){
    if(!err){
        if(0==response.length){
            console.log('There are no records of the delegated share');
        }
        for(delegation in response){
            console.log('Delegated to the account '+response[delegation].delegatee+', '+response[delegation].vesting_shares+', can be revoked after '+response[delegation].min_delegation_time);
        }
    }
});

Getting information about the return of the delegated share after the delegation is canceled:

var account_login='test';
var start_from=new Date().toISOString().substr(0,19);//searching for the return of the delegated share after the date issued in ISO format
var count=1000;
viz.api.getExpiringVestingDelegations(account_login,start_from,count,function(err,response){
    if(!err){
        if(0==response.length){
            console.log('There are no records of the delegated share being returned');
        }
        for(delegation in response){
            console.log(response[delegation].vesting_shares+' will return '+response[delegation].expiration);
        }
    }
});

Custom operations

When developers need to introduce their structure into the blockchain, make a decentralized application (dApp) that will monitor blocks and take into account operations in the network — they can use custom operations. The custom operation has a flexible structure:

  • required_active_auths — an array of accounts, whose signatures with active keys a transaction should contain;
  • required_regular_auths — an array of accounts, whose signatures with regular keys a transaction should contain;
  • custom_name — the name of the category of custom operations (the developers themselves decide which name to use for their application);
  • custom_json — any structure in JSON format;

Developers can come up with their own data structures, the protocol of commands and their accounting through custom operations. For example, it can be a card game, media blogs, comments, a product catalog, or working with ad blocks.

Example of using a custom operation:

var account_login='test';
var required_active_auths=[];
var required_regular_auths=[account_login];
var private_key='5K...';//the private key of the desired access type (in this case, 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);
});

Selling an account

The account owner can put it up for sale using master access:

var account_login='test';
var master_key='5K...';
var seller_login='reseller';//the login of the account that will receive tokens upon successful sale of the account
var fixed_price_amount='10000.000 VIZ';//account price
var on_sale=true;//put the account up for sale (if false, then remove it from sale)
viz.broadcast.setAccountPrice(master_key,account_login,seller_login,fixed_price_amount,on_sale,function(err,result){
    console.log(err,result);
});

You can also put up subaccounts (*.login) for sale:

var account_login='test';
var master_key='5K...';
var seller_login='test';//the login of the account that will receive tokens upon successful sale of the account
var fixed_price_amount='1000.000 VIZ';//subaccount price
var on_sale=true;//put up subaccounts for sale (if false, then remove them from sale)
viz.broadcast.setSubaccountPrice(master_key,account_login,seller_login,fixed_price_amount,on_sale,function(err,result){
    console.log(err,result);
});

You can buy an account or a subaccount using thebuy_account' operation, the system will check the possibility of buying and conduct a transaction if possible:

var account_login='buyer';
var active_key='5K...';
var subaccount_login='enjoy.test';//purchasing of a subaccount from an account named "test"
var account_offer_price='1000.000 VIZ';//the agreed offer price (if the price changes, the blockchain will refuse the operation)
var private_key=pass_gen();//generating a private key
var public_key=viz.auth.wifToPublic(private_key);//getting a public key from a private key
var token_to_shares='5.000 VIZ';//additionally spending tokens to converting to a share of a new account
viz.broadcast.buyAccount(active_key,account_login,subaccount_login,account_offer_price,public_key,token_to_shares,function(err,result){
    if(!err){
        console.log('Buying an account '+account_login+' passed successfully, private shared key is '+private_key);
        console.log(result);
    }
    else{
        console.log(err);
    }
});

Three-way Escrow transactions

Three-way transactions work on the principle of checking the fulfillment of the conditions by the guarantor (agent). The recipient and the guarantor must confirm the start of the transaction with the escrow_approveoperation (the guarantor receives a commission at this stage), otherwise, when the ratification deadline date (ratification_deadline) is reached, all tokens are returned to the initiator of the transaction (theexpire_escrow_ratificationmethod in the file database.cpp).

If there is a moment of dispute, the sender or recipient can initiate the proceedings with the operationescrow_dispute, after which the decision on the transaction is transferred to the guarantor (it is he who decides who and how many tokens will receive). If the transaction is suspended and is not resolved for a long time — the contract expires (escrow_expiration) and all tokens are managed either by an agent (if a dispute was opened), or by any of the sides of the transaction.

Creating an escrow transfer:

var account_login='test';
var active_key='5K...';
var receiver_login='receiver';
var token_amount='100.000 VIZ';//number of tokens to be transferred
var escrow_id=1;//the escrow number, assigned manually for the approval of the application (uint32)
var agent_login='agent';
var fee='10.000 VIZ';//the guarantor's commission
var json_metadata='{}';//additional metadata in json format
var ratification_deadline=new Date().toISOString().substr(0,19);//the date of the forced completion of the transaction and the refund of funds if the guarantor and the recipient have not confirmed their participation in the transaction (deadline in the ISO format of the form 2019-10-17T13:30:18)
var escrow_expiration=new Date().toISOString().substr(0,19);//the deadline for making a decision on the transaction, after which it is impossible to initiate a dispute (deadline in the ISO format of the form 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);
    }
});

Confirmation of participation in the transaction under the proposed conditions (the guarantor and the recipient must confirm their participation with theescrow_approveoperation):

var account_login='test';
var receiver_login='receiver';
var agent_login='agent';
var escrow_id=1;//the escrow number is assigned manually for the approval of the application (uint32)

var who=agent_login;//who confirms their participation
var active_key='5K...';//the key confirming the sides (who)

var approve=true;//false in case of refusal to participate in the transaction
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);
    }
});

Dispute request (the sender or recipient can open a dispute on a transaction using theescrow_disputeoperation):

var account_login='test';
var receiver_login='receiver';
var agent_login='agent';
var escrow_id=1;//the escrow number is assigned manually for the approval of the application (uint32)


var who=receiver_login;//who confirms their participation
var active_key='5K...';//the key confirming the sides (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);
    }
});

Releasing funds (escrow_releaseoperation):

var account_login='test';
var receiver_login='receiver';
var agent_login='agent';
var escrow_id=1;//the escrow number is assigned manually for the approval of the application (uint32)
var token_amount='100.000 VIZ';//number of tokens to be transferred

var who=receiver_login;//who decided to release the funds (if a dispute is open, then only the guarantor decides to whom and how much to transfer)
var active_key='5K...';//key of the initiator of the operation (who)
var receiver=account_login;//the recipient of funds can only be the other side of the transaction before the expiration of the transaction, otherwise-any of the sides of the transaction

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);
    }
});

To get information about escrow, you need to call theget_escrowAPI request of thedatabase_api plugin:

var account_login='test';
var escrow_id=1;
viz.api.getEscrow(account_login,escrow_id,function(err,response){
    if(!err){
        //response received
        console.log(response);
    }
    else{
        //error
        console.log(err);
    }
});

Account restoration

When creating an account, the creator is recorded in the recoveryaccount field as a trusted person to restore access to the account, in case of its hacking and change of access keys. The blockchain keeps records of the change of master powers and stores them for 30 days. It is during this period of 30 days that a trusted account can create a request to restore access with therequest_account_recoveryoperation:

var recovery_account='escrow';
var active_key='5K...';

var account_to_recover='test';
var private_key=pass_gen();//we generate a private key (we transfer the account owner or agree with him the public key for master permissions) 
var public_key=viz.auth.wifToPublic(private_key);//getting a public key from a private one

var new_master_authority={
    'weight_threshold': 1,
    'account_auths': [],
    'key_auths': [
        [public_key, 1]
    ]
};//new master permissions
var extensions=[];//additional field, not used
viz.broadcast.requestAccountRecovery(active_key,recovery_account,account_to_recover,new_master_authority,extensions,function(err,result) {
    console.log(err,result);
});

After the request for changing access is created, the account must confirm it with therecovery_accountoperation. An important point is that the transaction must be signed with two keys at once — the old and the new one from the application from the trust account, so you need to form a transaction usingviz.broadcast.send:

var account_login='test';

var new_master_key='5K...';//new private master key
var new_master_public_key=viz.auth.wifToPublic(new_master_key);//public key from the private master key (agreed with trusted account)
var new_master_authority={
    'weight_threshold': 1,
    'account_auths': [],
    'key_auths': [
        [new_master_public_key, 1]
    ]
};//new master permissions

var recent_master_key='5K...';//old private master key for proof of identification
var recent_master_public_key=viz.auth.wifToPublic(recent_master_key);//old public master key
var recent_master_authority={
    'weight_threshold': 1,
    'account_auths': [],
    'key_auths': [
        [recent_master_public_key, 1]
    ]
};//old master credentials as proof of identification
var extensions=[];//additional field, not used

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);
});

To change a trusted account to restore access, you can use thechange_recovery_account operation, the changes will take effect in 30 days (to exclude abuse):

var account_login='test';
var master_key='5K...';//master key
var new_recovery_account='escrow';//new trusted account
var extensions=[];//additional field, not used
viz.broadcast.changeRecoveryAccount(master_key,account_login,new_recovery_account,extensions,function(err,result) {
    console.log(err,result);
});

The system of proposed operations

To manage the system of proposed operations, there are 3 operations: creating an offer, providing a signature (updating), deleting an offer. After creating the offer, the blockchain system will wait for all the necessary signatures to perform the operations embedded inside the offer, after which it will execute them. If the expiration date has come, the offer will not be fulfilled. The system of proposed operations is used to manage multi-subscription accounts. To create an offer, use theproposal_createoperation:

var account_login='test';
var active_key='5K...';//active private key
var title='payments-14';//the name of the offer (plays the role of an identifier, must be unique)
var memo='Платежи по договору №14';

var expiration_date=new Date();//expiration date when the proposed operations will be rejected or executed upon receipt of all necessary signatures
expiration_date.setDate(expiration_date.getDate() + 10);//plus ten days from the current moment
var expiration_time=expiration_date.toISOString().substr(0,19);//the expiration date of the offer in ISO format
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);//signature acceptance termination date (minus 10 seconds before expiration date)
var review_period_time=review_period_date.toISOString().substr(0,19);//the expiration date of the offer in ISO format
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);
});

To get information about the offers made by the user, you need to execute theget_proposed_transactionsAPI request to thedatabase_apiplugin (an array of offers will be returned):

var looking_account='test';
var from=0;
var limit=100;
viz.api.getProposedTransactions(looking_account,from,limit,function(err,response){
    console.log(err,response);
});

The offer system supports all existing operations in the blockchain, but does not allow mixing operations that require different permissions (for example, tranferoperations that require active permissions, andawardoperations that require regular permissions). To provide a signature of the desired access type, you can use theproposal_updateoperation by specifying the login of the signatory of the transaction in the array to add or remove from the list of those who confirmed the offer (there are 4 types of arrays for this: active, master, regular and key for using single keys). As soon as all the necessary signatures are provided, the operations from the offer will be executed (provided that the review_period_timeperiod is not specified). In case of an execution error, a second attempt will be made at expiration. Example:

var account_login='escrow';
var active_key='5K...';//active private key

var proposal_author='test';//the author of the offer
var proposal_title='payments-14';//offer ID

var active_approvals_to_add=[];//the list of accounts that signed this transaction with the active access type for confirming the offer
var active_approvals_to_remove=[];//list of accounts to delete from the list of those who confirmed the offer
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);
});

The proposal can be deleted by the applicant or any participant whose signature is required. To do this, it is enough to perform theproposal_deleteoperation by signing it with the active key:

var account_login='escrow2';//participant of the offer
var active_key='5K...';//active private key

var proposal_author='test';//the author of the offer
var proposal_title='payments-14';//offer ID

var extensions=[];

viz.broadcast.proposalDelete(active_key,proposal_author,proposal_title,account_login,extensions,function(err,result){
        console.log(err,result);
});

An example of the implementation of a deferred reward operation created by the same account in the same transaction and signed the transaction with an active and regular key:

var expiration_date=new Date(new Date().getTime() + 20000);//+20 seconds from the current moment
var expiration_time=expiration_date.toISOString().substr(0,19);//the expiration date of the offer in ISO format
var review_period_date=new Date(expiration_date.getTime() - 10000);//signature acceptance termination date (minus 10 seconds from the expiration date)
var review_period_time=review_period_date.toISOString().substr(0,19);//the expiration date of the offer in ISO format

var login='test';
var active_wif='5K...';
var regular_wif='5J...';

var target='committee';//the target of the award operation
var energy=200;//2%
var memo='отложенного награждения через proposal';

var regular_public_wif = viz.auth.wifToPublic(regular_wif);//to add to key_approvals_to_add via the proposal_update operation

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)
});

Signing data with a private key and verifying the signature with a public key

To sign data with a private key (theviz.auth.signature.signmethod, to search for a canonical signature, you need to add a nonce that will be in the data for signature) and verify the signature with a public key (theviz.auth.signature.verifyData method), you can use the standardviz-js-libmethods:

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){//checking for the canonicity of the signature
    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));

js requests to a VIZ public node without a library

If your application does not require cryptography and transaction signing, then you can use native tools for json-rpc requests via js.

Example for a WebSocket connection:

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",[]]}');
};

Example for an HTTP connection:

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",[]]}');

Generating transactions

There are rules for forming transactions embedded in the VIZ protocol (using the Graphene codebase). The data signed with a cryptographic key must comply with all the structures and rules of binary representation embedded in the code. This section describes how keys are encoded, how the binary representation of data or its preparation occurs.


Transaction structure for signing

The data structure, the binary representation of which needs to be signed using the keys of the corresponding access types used in the accounts of nested operations, contains the following fields:

  • chain_id — the chain id, in VIZ is fc::sha256::hash from the stringVIZ: 2040effda178d4fffff5eab7a915d4019879f5205cc5392e4bcced2b6edda0cd. It is worth noting that fc::sha256::hash converts a string to c_str by adding the string lengrh to the beginning of its hex56495Avalue, as a result, sha256 is calculated from the hex0356495Avalue;
  • tapos_link — the Transactions as Proof of Stake conception is that each transaction refers to a specific block that must be in the chain for it to work. The binary representation is a representation of the ref_block_num and ref_block_prefix transaction parameters.
  • expiration — unixtime of the transaction expiration (it must be included in the chain before the expiration time);
  • operations — an array of operations that are in a transaction, the binary representation of each operation consists of all the attributes of the operation according to the protocol;
  • extensions — an array of service extensions of the transaction (not used, so in binary format it is the hex value of the array without elements:00);

Why is chain_id needed

The open and free code of blockchain systems based on Graphene allows you to launch new chains, both unchanged and completely redesigned with their own mechanics and economy. Moreover, many projects run public test chains to check for changes. To prevent nodes from being confused and transactions from the same network cannot be used in a fork (or a chain with similar accounts and keys), there is a chain identifier that is present as a label in each transaction and signature operation.

Key format

Private and public keys in VIZ are located according to the DSA algorithm, and use cryptography to verify the signatures of a data set. Many developers who are not experts in cryptography simply use specialized libraries, without going into details.

Let's consider the stages of converting a private key (consisting of 32 bytes) into a readable WIF format:

  • add a binary prefix to the key in the hex representation80;
  • calculate the sha256 hash from the sha256 hash of the binary representation of the key for the checksum;
  • add the first 8 bytes of the checksum to the end of the key;
  • encode the resulting binary result using the base58 algorithm using the alphabet:123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz;

Step-by-step conversion of a public key (consisting of 32 bytes) to a readable format:

  • get the checksum by hashing the binary representation of the key ripemd 160 algorithm;
  • add the first 8 bytes of the checksum to the end of the key;
  • encode the resulting binary result using the base58 algorithm using the alphabet:123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz;
  • add the network prefix (string value VIZ);

The vis-js-lib library uses theauthmodule (link to GitHub, which allows you to use pre-installed methods to work with keys and sign data.

Representation of different data types in binary form

  • string — the string value in binary form is a byte containing the length of the string and the string itself (for example, the string value of theescrowaccount login will correspond to the hex value06657363726f77in binary representation);
  • integer — the numeric value is flipped in the binary representation and the empty dimension is filled with zeros (if the value type is specified). For example, the energy specified in the award operation is a uint16_t, for which 2 bytes are enough to transfer. If it is necessary to pass the value of 10.00%, then in the integer value it will be 1000, in the hex representation 03EB, the inverted value of which will beEB03. The custom_sequencefield representing uint64_t already consists of 8 bytes, so to transmit the decimal value 377, its hex value0179in binary value will be hex: 7901000000000000.
  • date — the date fields from the JSON representation are written in ISO format (for example 2019-02-07T06:19:23in the UTC+0 time zone, also known as GMT). For their binary value, it is written in the decimal unixtime format according to the rules of integer representation.2019-02-07T06:19:23example in unixtime will be 1549520363 (hex value is5C5BCDEB), which in binary value will be hex:EBCD5B5C.
  • asset — the binary value of the VIZ or SHARES tokens is a sequence of values: an integer with a dimension of 8 bytes without precision (0.012 will represent 12, 1.002 will represent 1002), 1 byte with precision of the token (03 for VIZ,06for SHARES), a string ticker value of 7 bytes (56495A00000000 for VIZ,53484152455300for SHARES). For example: 1.002 VIZ in JSON value inside the operation will have a binary representation in hex:EA030000000000000356495A.
  • public_key — the value of the public key in the binary representation contains 33 bytes, the first byte is the value for restoring the public key (recovery id в ECDSA), 16 bytes — coordinates of the public key point on the X axis, the last 16 bytes — on the Y axis. For example, the026a1dbaacb805f145f9276025627102152840bb1aa09b7fac580f892d93b572b4 binary value corresponds to a private key with recovery_id equal to 02 and a point with X coordinates6a1dbaacb805f145f927602562710215in the hex representation and Y2840bb1aa09b7fac580f892d93b572b4 in the hex representation . Which corresponds to the public keyVIZ5hDwvV1PPUTmehSmZecaxo1ameBpCMNVmYHKK2bL1ppLGRvh85.
  • operation_type — the operation type is an integer value of the operation number according to the VIZ protocol written in 1 byte (for more information, see the section Operations and their types). For example, the transfer operation in binary form will have the02hex entry, and the create_inviteoperation will have the2b hex value.

Example of a transaction structure

Let's analyze an example of a transaction in JSON format and its representation in binary form:

{"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":[]}
  • ref_block_num — a reference to the block number after the bitwise «and» with hex ffff(for example, the number 9023 in the hex representation 233F, according to the integer representation should be inverted, we get 3F23);
  • ref_block_prefix — 4 bytes (5, 6, 7, 8) from the binary state of the block ID in decimal format, which can be obtained by theget_block_headerAPI request with the next block number (9024) to the database_api plugin. The response will contain the previousfield with the ID0000233F716D887523BB63AD3E6107C96EDCFD8Aof the required block. We take 716D8875 for the binary representation, flip the bytes — 75886D71 and translate it into decimal format for JSON: 1971875185.
  • expiration — unixtime of the transaction expiration. 2019-02-07T06:19:23 in unixtime will be 1549520363 (5C5BCDEBhex value ), which in binary value will be inverted and represent hex:EBCD5B5C.
  • operations — array of operations (because there is one element in the array, hex:01);
    • transfer — token transfer operation (according to the numbering of the operation in the hex protocol:02);
      • from — sender's account login ( test1string length and hex representation: 057465737431);
      • to — recipient's account login (test2string length and hex representation: 057465737432);
      • amount — the transmitted number of VIZ tokens (1.002 VIZ in hex: EA030000000000000356495A00000000);
      • memo — note to the recipient (string length <3 and hex representation: 023C33);
  • extensions — an array of service extensions of the transaction (because it is not used and has no elements, it has the representation 00).

The final binary representation of the transaction data in hex: 3F23716D8875EBCD5B5C0102057465737431057465737432EA030000000000000356495A00000000023C3300;

To send a transaction to the blockchain, it is necessary to supplement this representation with chain_id at the beginning and sign with a private key. The resulting signature must be added to the JSON field signaturesarray, for example:

{"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"]}

And pass this JSON via thebroadcast_transaction API request to the network_broadcast_apiplugin.

Transaction ID

The source code of the node specifies the type of signed transaction as transaction_id_type, which is written in the Graphene protocol as fc::ripemd160. But practice shows that the transaction ID is not ripemd160 (hash size is 20 bytes) and is a part of the sha256 hash (the hash size is 32 bytes). It is not known for sure why this happened, but we can make 2 assumptions:

  • This is done intentionally to reduce the size of the transaction hash (20 bytes per 32 place), but increases the risk of a collision;
  • This is an unintentional bug that was not found before launching Graphene chains, and later decided not to fix it (presumably, the bug occurs at the time of converting the transaction via digest_type::encoder in thetransaction.cppprotocol file);

It is worth noting that this error was fixed in the EOS protocol, there is a fc::sha256 transaction typewhich suggests that this is still a bug. As a result, the transaction ID in Graphene projects before EOS is the sha256 hash, but the truncated one is the first 20 bytes (instead of the full 32 bytes).

For example, a transaction in VIZ from block 11142739 has the id c84f9e8255859b2083be720cf9b64b3542e4360f. Raw transaction {"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":[]}in hex:4c06e6eb6f9dbf9aae5d012f046f6e31780e76697a2d736f6369616c2d626f74140000000000000000001274656c656772616d3a3236323633323831390000.

The sha256 hash from the raw transaction: c84f9e8255859b2083be720cf9b64b3542e4360f0a62e33363bca5d984ee608a, the first 20 bytes of which are its identifier in the blockchain: c84f9e8255859b2083be720cf9b64b3542e4360f.

Getting ref_block_num and ref_block_prefix to form a transaction

The blockchain node stores the IDs of the last 65537 blocks (for more information, read [about the TaPoS concept in VIZ](state. md#Uniqueness-of-transactions-and-tapos-transactions-as-proof-of-stake)). In most cases developers refer to one of the last blocks, usually by executing a queue of actions:

  • Get data about the system status via theget_dynamic_global_properties API request to the database_apiplugin;
  • Using the value of the head_block_number field minus 3 blocks, set for which ref_block_num block will be formed and its ID will be requested;
  • Get the ID of the block being used by executing the get_block_headerAPI request to the database_api plugin, requesting the required block plus one block (because the header of each block contains a reference to the ID of the previous block, the required ID is located in the next one);
  • form theref_block_prefix from the identifier.

Most libraries that contain abstractions to simplify calls and transfer transactions to the blockchain do this on their own.

An example of manually getting ref_block_num and ref_block_prefix on viz-js-lib can be found in the source code of abstractions of transaction preparation in the library itself.

An example of a similar receipt of ref_block_num and ref_block_prefix in PHP in the source code of the php-graphene-node-client library.

The order of data serialization in the operation

All operations and their parameters are recorded in the VIZ protocol and are in the chain_operations.hpp file.

It is there that you can study the types of parameters and their required order in the operation. Attention! The order of parameters in the operation structure does not coincide with the order of parameters in the operation itself. Let's consider an example of the escrow_transfer_operation, the structure of the operation (often there is a comment describing it before the 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);
    }
};

The order of parameters in the operation is set already at the end of the file by the method:

FC_REFLECT((graphene::protocol::escrow_transfer_operation), (from)(to)(token_amount)(escrow_id)(agent)(fee)(json_metadata)(ratification_deadline)(escrow_expiration));

In addition to the description of the operation structure, there is also parameter processing in the validate method, which can be found in the chain_operations.cpp file:

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);
}

Most operations check for the signature of the corresponding permissions, for example, in the escrow_transfer_operationstructure, there is a check for the signature of the initiator of the operation (the from field) of the active permissions in the get_required_active_authorities method.