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:
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.
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.
The VIZ uses the account model. Accounts are part of a common namespace, store tokens and public access keys.
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:
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.
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.
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.
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.
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).
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 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).
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).
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.
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:
You can find out the current values of the parameters by executing an API request
get_chain_properties
to the plugindatabase_api
or via the network browser on the control.viz.world website
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.
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 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.
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.
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
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
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:
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 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.
In the VIZ protocol there is a numbering of operations (starts from zero), there are both normal and virtual operations:
The operation number is needed for low-level formation of transactions and their signature (for more information, see the section Formation of transactions).
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).
Everything related to the protocol is located in the /libraries/protocol directory of the source code of the C++ node of the VIZ 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:
block_summary_object
objects);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).
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):
In VIZ, there are service accounts that have access keys to certain permissions embedded in the configuration file:
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.memo
note 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 anonymous
account. Attention! If the note is not specified, the funds will be burned in the same way as the transfer to the null
account.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.5JabcrvaLnBTCkCVFX5r4rmeGGfuJuVp4NAKRNLTey6pxhRQmf4
, was reset to empty after pre-installing the sale of subaccounts for 10,000 VIZ (the recipient iscommittee
).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.
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:
00a53f658226b2a6f3f75fc8185d884d029f50bf
) — id (hash) of the current block;340282366920938463463374607431768211455
) — used to calculate the percentage of delegates participating in block signing;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;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
.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.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 hexffff
fits 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.
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 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:
DEFINE_API_ARGS
(API method name, return value type);DEFINE_API
(checking the request parameters, CHECK_ARGS_COUNT
, forming a return value of a certain type);plugin_initialize
, which can handle boost::program_options::variables_map
for more fine-tuning of the plugin through the node configuration file.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.
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]]}
A plugin responsible for receiving and sending signed blocks and transactions between network nodes
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
}
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
}
]
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"}
]
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"}
]
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": []
}
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": []
}
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
}
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
}
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
}
]
}
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
}
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"
}
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"
]
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"
]
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
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
]
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"
]
Request example:
{"id":1,"method":"call","jsonrpc":"2.0","params":["account_by_key","get_key_references",[["VIZ5Z2po7K5CoCXw2xLPPt8JJvJLJ3xVNANLgTy9KDfLeZH2urSSd","VIZ1111111111111111111111111111111114T1Anm"]]]}
Answer:
[
[
"on1x"
],
[
"viz"
]
]
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"
}
]
}
]
]
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"
}
]
}
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"
},
...
]
Example:
{"id":1,"method":"call","jsonrpc":"2.0","params":["committee_api","get_committee_requests_list",["2"]]}
Answer:
[
2,
3,
13,
14,
33
]
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
}
Example:
{"id":1,"method":"call","jsonrpc":"2.0","params":["invite_api","get_invites_list",["0"]]}
Answer:
[
6,
8,
15,
20,
34,
...
]
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"
}
]
}
]
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
}
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
}
]
Example:
{"id":1,"method":"call","jsonrpc":"2.0","params":["paid_subscription_api","get_active_paid_subscriptions",["on1x"]]}
Answer:
[
"viz.world"
]
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
}
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
}
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"
]
Example:
{"id":1,"method":"call","jsonrpc":"2.0","params":["witness_api","get_witness_count",[]]}
Answer:
59
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"
}
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"
}
]
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"
}
]
Example:
{"id":1,"method":"call","jsonrpc":"2.0","params":["witness_api","lookup_witness_accounts",["","4"]]}
Answer:
[
"t3",
"lb",
"ae",
"in"
]
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.
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:
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.
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).
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.
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.
The viz-swift-lib library — a library on Swift, which can be installed via Swift Package Manager.
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).
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.
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.
Detailed documentation in English with an indication of all methods and their attributes available at the link.
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 build
console, 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 viz
via the console.
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:
https://viz.lexa.host/
for JSON-RPC requests over HTTPS and wss://viz.lexa.host/ws
for JSON-RPC requests over WebSocket over SSL;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);
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);
}
});
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);
}
});
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);
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);
}
});
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.auth
module.
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.
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);
}
});
}
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);
}
});
For anonymous use of the invite code, there is an invite
account in the system with 5KcfoRuDfkhrLCxVcE9x51J6KN9aM9fpb78tLrvvFckxVV6FyFW
private 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);
}
});
For anonymous use of the voucher, there isinvite
account 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);
}
});
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);
}
});
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);
}
});
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);
}
});
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);
}
});
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:
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 keyVIZ6LWhhUzKmYM5VcxtC2FpEjzr71imfb7DeGA9yodeqnvtP2SYjA
specified 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");
}
});
}
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);
}
});
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);
}
});
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);
}
});
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);
}
});
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);
}
});
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);
}
});
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_percent
of 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);
}
});
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_proxy
operation.
The rules for delegating a share are partially prescribed in the network protocol, partially controlled by delegates:
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_shares
operation 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);
}
}
});
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:
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);
});
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 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_approve
operation (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_ratification
method 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_approve
operation):
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_dispute
operation):
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_release
operation):
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_escrow
API 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);
}
});
When creating an account, the creator is recorded in the recovery
account 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_recovery
operation:
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_account
operation. 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);
});
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_create
operation:
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_transactions
API request to thedatabase_api
plugin (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, tranfer
operations that require active permissions, andaward
operations that require regular permissions). To provide a signature of the desired access type, you can use theproposal_update
operation 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_time
period 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_delete
operation 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)
});
To sign data with a private key (theviz.auth.signature.sign
method, 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-lib
methods:
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));
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",[]]}');
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.
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:
VIZ
: 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 hex56495A
value, as a result, sha256 is calculated from the hex0356495A
value;00
);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.
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:
80
;123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz
;Step-by-step conversion of a public key (consisting of 32 bytes) to a readable format:
123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz
;VIZ
);The vis-js-lib library uses theauth
module (link to GitHub, which allows you to use pre-installed methods to work with keys and sign data.
escrow
account login will correspond to the hex value06657363726f77
in binary representation);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_sequence
field representing uint64_t already consists of 8 bytes, so to transmit the decimal value 377, its hex value0179
in binary value will be hex: 7901000000000000
.2019-02-07T06:19:23
in 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:23
example in unixtime will be 1549520363
(hex value is5C5BCDEB
), which in binary value will be hex:EBCD5B5C
.03
for VIZ,06
for SHARES), a string ticker value of 7 bytes (56495A00000000
for VIZ,53484152455300
for SHARES). For example: 1.002 VIZ
in JSON value inside the operation will have a binary representation in hex:EA030000000000000356495A
.026a1dbaacb805f145f9276025627102152840bb1aa09b7fac580f892d93b572b4
binary value corresponds to a private key with recovery_id equal to 02
and a point with X coordinates6a1dbaacb805f145f927602562710215
in the hex representation and Y2840bb1aa09b7fac580f892d93b572b4
in the hex representation . Which corresponds to the public keyVIZ5hDwvV1PPUTmehSmZecaxo1ameBpCMNVmYHKK2bL1ppLGRvh85
.transfer
operation in binary form will have the02
hex entry, and the create_invite
operation will have the2b
hex value.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":[]}
ffff
(for example, the number 9023 in the hex representation 233F
, according to the integer representation should be inverted, we get 3F23
);get_block_header
API request with the next block number (9024) to the database_api plugin. The response will contain the previous
field with the ID0000233F716D887523BB63AD3E6107C96EDCFD8A
of the required block. We take 716D8875
for the binary representation, flip the bytes — 75886D71
and translate it into decimal format for JSON: 1971875185
.2019-02-07T06:19:23
in unixtime will be 1549520363
(5C5BCDEB
hex value ), which in binary value will be inverted and represent hex:EBCD5B5C
.01
);02
);test1
string length and hex representation: 057465737431
);test2
string length and hex representation: 057465737432
); 1.002 VIZ
in hex: EA030000000000000356495A00000000
);<3
and hex representation: 023C33
);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 signatures
array, 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_api
plugin.
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:
digest_type::encoder
in thetransaction.cpp
protocol 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
.
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_dynamic_global_properties
API request to the database_api
plugin;head_block_number
field minus 3 blocks, set for which ref_block_num block will be formed and its ID will be requested;get_block_header
API 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);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.
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_operation
structure, 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.