VIZ 开发手册为开发者提供了 VIZ 区块链中 API/对象/结构的描述、常用场景的代码示例以及低级交易构建指南。
目录:
VIZ节点是区块链的核心,是逐块处理、执行所有操作并存储系统状态的软件。所有者配置节点,选择使用的插件。包含的插件及其设置取决于节点能够提供哪些功能。
委托者负责区块的生成,因此最重要的节点是委托节点。它们与其他节点交换数据、收集交易,当轮到委托者(服务器所有者)生成区块时,将收集到的区块进行签名并传输给其他节点。区块必须在分配给委托者的3秒内完成签名和传递。错过区块会导致交易执行延迟,并在一段时间内对委托者进行惩罚(惩罚会影响其他用户为该委托者投票的总权重)。这使得系统能够暂时将服务器或数据中心出现问题的委托者在队列中降级,从而保护网络的可靠性。
通常,委托者会运行两个节点,一个主节点和一个备用节点。它们通常位于不同的数据中心,彼此独立。如果主节点出现问题,委托者会将区块签名密钥更改为备用密钥,备用节点将负责生成区块。
关键插件:chain p2p json_rpc webserver witness network_broadcast_api database_api witness_api
任何区块链系统运行的基础都是点对点(p2p)连接和数据交换。种子节点的特殊之处在于它们承担着重要角色——接收和分发区块,从而减轻整个网络的带宽负担,并降低地理位置上邻近连接的延迟。运行种子节点没有经济利益,因为服务器需要成本,但不会给所有者带来任何收益。因此,如果财务上允许,大多数种子节点由委托者(见证人)运行。正是通过节点的API来请求最新信息、账户状态、操作历史或委员会中的申请。
关键插件:chain p2p
我们提供的一部分插件是面向开发者及其用户的API。如果节点启用了所有可用插件并存储从第一个区块开始的历史记录,则通常称为完整节点。由于插件不仅提供对系统状态的访问,还会形成自己的数据结构,因此对服务器资源(尤其是RAM,因为节点将ChainBase数据库存储在RAM中)有更高要求。正是通过节点的API来请求最新信息、账户状态、操作历史或委员会中的申请。
此类插件的示例:
API节点可以是私有的(在设置中指定了登录名和密码访问参数),也可以是公共的(当API访问对所有人开放且节点地址公开已知时)。通常,公共API节点简称为公共节点。更多信息请参阅插件及其API。
VIZ区块链创建了所谓的可预测经济条件。如果其他系统采用递减通胀的原则,这使得在不同时间加入系统的参与者处于不平等条件,那么VIZ则采用固定通胀和1年为一轮的模式(10512000个区块)。
在链启动时,分配了5000万viz。每轮基于10%的固定通胀率。启动一年后,网络中有5500万viz,这意味着下一轮的通胀已经从5500万viz计算,导致第二年发行550万viz。
由于固定通胀,预测每个区块的viz发行量成为可能。例如在第二年,每个区块发行 5500000 / 10512000 = 0.523 viz。这个数字直到下一轮开始都不会改变。
2023年经济模型发生了变化:改为每个区块固定发行1 viz。
现有的VIZ模型规定由网络委托者管理经济。由网络参与者选举的委托者对网络参数进行投票,其中包括代币发行分配参数:
您可以通过向
database_api插件执行API请求get_chain_properties或通过control.viz.world网站的网络浏览器了解参数的当前值。
DAO基金收集viz代币,为生态系统发展倡议提供资金。如果网络参与者决定举办竞赛、开发应用程序、为整个网络谋福利——他可以申请从基金获得资金。任何网络成员都可以投票反对或支持申请的全部或部分执行。当申请审议时间到来时,将计算所有参与投票的网络成员的份额并做出决定。
每个网络参与者都有一个随时间可再生的资源——能量。消耗的能量以每5天100个百分点的速度补充(每天20个百分点,每小时0.83个百分点等)。账户的能量不能超过100%。
当VIZ参与者决定奖励某人时,他指明想要用于奖励的账户能量百分比。区块链会考虑参与者的社会资本规模和用于奖励的能量,并将它们与最近5天内所有参与者的奖励总额相关联。最终,目标账户按接收者的社会资本和消耗能量比例从发行中获得社会资本奖励。这确保了平等竞争地访问奖励基金。网络参与者自行决定奖励谁以及为何奖励,开启了自由选择的可能性并激励任何行动和倡议。
例如:
整个VIZ经济由社会资本所有者管理。正是他们,如果同意委托者的愿景和系统管理模型,就会为委托者投票(如果没有可投票的人,任何人都可以自己成为委托者并获得其他参与者的投票)。奖励基金和DAO基金按照公平、共享的管理模式运作。
VIZ节点是区块链的核心,是逐块处理、执行所有操作并存储系统状态的软件。所有者配置节点,选择使用的插件。包含的插件及其设置取决于节点能够提供哪些功能。
委托者负责区块的生成,因此最重要的节点是委托节点。它们与其他节点交换数据、收集交易,当轮到委托者(服务器所有者)生成区块时,将收集到的区块进行签名并传输给其他节点。区块必须在分配给委托者的3秒内完成签名和传递。错过区块会导致交易执行延迟,并在一段时间内对委托者进行惩罚(惩罚会影响其他用户为该委托者投票的总权重)。这使得系统能够暂时将服务器或数据中心出现问题的委托者在队列中降级,从而保护网络的可靠性。
通常,委托者会运行两个节点,一个主节点和一个备用节点。它们通常位于不同的数据中心,彼此独立。如果主节点出现问题,委托者会将区块签名密钥更改为备用密钥,备用节点将负责生成区块。
关键插件:chain p2p json_rpc webserver witness network_broadcast_api database_api witness_api
任何区块链系统运行的基础都是点对点(p2p)连接和数据交换。种子节点的特殊之处在于它们承担着重要角色——接收和分发区块,从而减轻整个网络的带宽负担,并降低地理位置上邻近连接的延迟。运行种子节点没有经济利益,因为服务器需要成本,但不会给所有者带来任何收益。因此,如果财务上允许,大多数种子节点由委托者(见证人)运行。正是通过节点的API来请求最新信息、账户状态、操作历史或委员会中的申请。
关键插件:chain p2p
我们提供的一部分插件是面向开发者及其用户的API。如果节点启用了所有可用插件并存储从第一个区块开始的历史记录,则通常称为完整节点。由于插件不仅提供对系统状态的访问,还会形成自己的数据结构,因此对服务器资源(尤其是RAM,因为节点将ChainBase数据库存储在RAM中)有更高要求。正是通过节点的API来请求最新信息、账户状态、操作历史或委员会中的申请。
此类插件的示例:
API节点可以是私有的(在设置中指定了登录名和密码访问参数),也可以是公共的(当API访问对所有人开放且节点地址公开已知时)。通常,公共API节点简称为公共节点。更多信息请参阅插件及其API。
VIZ区块链中的操作分为普通操作、已废弃操作和虚拟操作。普通操作通过网络参与者签名的交易进入区块链。已废弃操作由于网络发展和内部机制变化而被禁用。
部分操作属于重型操作(数据操作),它们会受到额外的带宽因子影响(网络参数data_operations_cost_additional_bandwidth的投票参数)。
虚拟操作在代码中特定条件触发时由节点生成,主要为网络参与者提供信息。例如,来自网络成员的奖励操作包含奖励的目的和账户消耗的能量。它不存储接收者将从奖励基金获得的奖励金额。正是针对这种情况需要虚拟操作。在奖励情况下,将生成虚拟操作receive_award,在shares字段中包含收到的奖励。
在VIZ协议中存在操作编号(从零开始),包括普通操作和虚拟操作:
操作编号用于低层级交易形成及其签名(更多信息请参阅交易形成部分)。
考虑VIZ时,需要将协议的对象和结构(操作、交易、区块、硬分叉、版本、权限)与直接存在于区块链中的对象和结构(受特定操作影响的对象)区分开来。
所有与协议相关的内容都位于VIZ区块链C++节点的源代码的/libraries/protocol目录中。
系统状态正是由区块链本身的对象和结构组成。每个包含操作的区块都由主数据库模块处理,该模块计算所有更改并就延迟操作做出决策。在/libraries/chain/include/graphene/chain目录中,既包含对象和数据结构,也包含区块链的内部结构(评估器、block_log、dynamic_global_property_object、对象类型)。
系统状态由以下对象组成:
block_summary_object对象构建的索引进行);提供API的插件可以返回来自区块链的对象,也可以返回自己的对象。通过id获取对象的简单请求按原样返回数据,通常通过类似API的构造函数传递来自区块链的对象,以复制状态并将其提供给用户,例如:witness_api插件使用单独的witness_api_object对象。而database_api插件使用account_api_object,它通过从索引中复制当前权限来补充标准的区块链对象account。
如果插件扩展了标准的索引表和对象,它会创建一个新结构,单独保存操作记录并填充索引。例如,private_message插件通过处理custom操作(创建填充message_index索引的message_object对象)来实现这一点。
委托者广播他们对网络投票参数的立场。区块链系统每经过一个委托者队列周期(21个区块)计算一次投票参数的中位数值,并在该周期内固定这些值。参数描述(括号中为撰写本节时的中位数值):
在VIZ中,存在服务账户,这些账户具有嵌入配置文件中的特定权限的访问密钥:
commitee、viz、sign的串联形成,对应5Hw9YPABaFxa2LooiANLrhUK5TPryy8f7v9Y1rk923PuYqbYdfC。启动网络并连接其他委托者后,匿名用户可以停止使用committee账户签名,签名密钥重置为VIZ111111111111111111111111111114T1Anm值。memo备注格式为login:public_key,其中login是新账户所需的登录名,public_key是用于所有权限类型的单一公钥。如果未指定登录名,且备注中仅指定了public_key,则会创建一个格式为nX.anonymous的匿名子账户,其中X是anonymous账户json_metadata中指定的数字增量。注意! 如果未指定备注,资金将被销毁,类似于向null账户转账。invite、viz、active的串联形成,对应5KcfoRuDfkhrLCxVcE9x51J6KN9aM9fpb78tLrvvFckxVV6FyFW。最初,它没有任何用于进行交易的份额,可以通过委托或由委托者启用备用带宽系统来纠正。5JabcrvaLnBTCkCVFX5r4rmeGGfuJuVp4NAKRNLTey6pxhRQmf4,在以10,000 VIZ预安装子账户销售(接收者为committee)后重置为空。系统状态是节点运行所需的最小数据量。VIZ所基于的Graphene代码库在每个符合共识的区块中执行来自交易的操作(委托队列、签名匹配)。每个区块都会处理数据和待处理操作,这导致了系统状态的迭代本质。部分数据会永久存储,这对运行节点的服务器设备提出了特定要求。
在区块链中的对象和结构部分描述了构成系统状态的大部分对象。
该对象存储网络的主要属性,包含流通代币信息和其他重要信息。在源代码中,它通常表示为dgp变量,随着每次迭代修改其状态,因此动态全局属性可以确信地被视为系统状态中最重要的部分。让我们看看其公共属性,可通过访问database_api插件的get_dynamic_global_properties方法获取:
00a53f658226b2a6f3f75fc8185d884d029f50bf)- 当前区块的ID(哈希);340282366920938463463374607431768211455)- 用于计算参与区块签名的委托者百分比;average_block_size = (99 * average_block_size + new_block_size) / 100,用于更新current_reserve_ratio以维持网络带宽使用率在约50%或以下;5986734968066277376)- 最大网络带宽使用公式maximum_block_size * CHAIN_BANDWIDTH_AVERAGE_WINDOW_SECONDS / CHAIN_BLOCK_INTERVAL计算,最大虚拟网络带宽根据公式max_bandwidth * current_reserve_ratio计算;average_block_size <= 25% maximum_block_size。如果条件满足,则该值增加1(线性增加,每个区块),但不超过CHAIN_MAX_RESERVE_RATIO(20000)。如果条件不满足,则current_reserve_ratio减半,这应立即减少网络负载,保护其免受使用大量交易的参与者的影响。换句话说,备用比率减半不会使网络使用量减半,但会限制那些已经尝试超过其带宽50%的用户。当备用比率从最大值减半(从20000变为10000)时,恢复总虚拟带宽大约需要7天时间;节点在交易池中检查所有传入交易的唯一性。在expiration发生后(设置中由CHAIN_MAX_TIME_UNTIL_EXPIRATION常量限制为一小时),交易将从池中删除。
VIZ中的所有交易必须符合TaPoS概念,即引用先前的某个区块(ref_block_num为2字节表示(区块号十进制表示与十六进制ffff的二进制"与"操作)和ref_block_prefix由二进制哈希状态的第5、6、7、8字节的十进制表示按反向顺序组成),这使得交易发起者可以依赖其当前的系统状态,而无需担心不可逆区块。如果它依赖于随机小分叉中的系统状态,则交易不会进入主链。因此,网络参与者可以控制交易队列的执行并构建交互,而无需等待区块变为不可逆。这反过来又对此类操作的最终核算施加了限制,因此大多数重要交易对于验证方而言应已处于不可逆状态。
节点为block_summary_object分配了2字节维度的空间(以便通过二进制"与"操作与十六进制ffff处理的区块号适合0到65536的范围),并在接受新区块时循环覆盖此空间(以及block_summary_index索引)中的标识符(哈希)。65537个区块覆盖196611秒的时间间隔(约2.27天)。因此,新交易只能引用此空间中的区块,以便节点可以验证区块标识符(来自ref_block_num)与ref_block_prefix中的校验和是否对应。
如果在基于工作量证明的第一代区块链系统中需要存储所有区块和交易的标识符,那么随着数据量的增加,许多开发人员开始寻找减少存储数据成本的方法。而大部分数据恰好存储在区块及其包含的交易中。在当前分布式账本技术中,这个问题已经通过匹配不可逆状态和系统可移植状态得到解决。一些区块链系统正开始朝这个方向发展,例如,Steem提出了平台无关状态文件 – PISF的解决方案。新的区块链系统(以及一些早期的先驱,例如XRP Ledger节点 - rippled)在设计时已经考虑了系统的可移植状态,其架构允许从受信任的节点请求当前系统状态,跳过漫长的同步过程,无需下载整个区块链历史并独立处理所有交易。
VIZ未对Graphene系统状态的架构进行重大更改,因此由block_summary_object结构组成的block_summary_index索引存储了所有关于区块的信息(即特定区块的block_id_type,该类型已包含所有信息)。这使得创建可移植系统状态变得困难,因为此类状态的数据量将非常庞大。升级的唯一途径是转向受信任节点的共识。
插件是扩展节点及其功能的通用工具。其中一些仅提供数据、准备索引、响应带有数据过滤的复杂用户请求,另一些处理自定义操作,并能提供完全独立的服务。例如,您可以编写一个插件,在首次付费订阅后,生成关于区块链中重要操作的通知,或提供个人消息服务。公共插件并不总是意味着"免费"。而"免费"也不总是意味着开放(就源代码而言)。
如果我们逻辑性地研究节点-服务-API链,会发现一种不理想的情况:公共API可能给节点本身的运行带来问题。当有大量请求涌向服务API时,尤其当提供服务的插件恰好与区块链节点协同工作时,这种情况可能发生。来自用户请求(或攻击者意图对服务进行DDOS攻击的请求)的带宽可能会阻碍节点本身与其他节点交换数据。如果API请求占用CPU,或使用大量索引样本,或需要长时间准备响应数据——不仅服务开始变慢,节点的运行也会出现问题。
这就是为什么建议避免将公共API节点与高要求插件以及委托者的工作放在同一服务器上。更正确的服务架构是使用一个独立的插件,该插件可以访问私有节点上的区块处理,自行处理数据,将其存储在数据库中,并允许缓存请求。当负载增加时,您始终可以使用数据和响应节点(利用负载均衡)的集群技术来扩展服务。
本节描述了所有可用的VIZ插件,这些插件通过API为用户提供访问。如果您按照以下说明操作,可以自行了解特定插件的API:
DEFINE_API_ARGS(API方法名称,返回值类型);DEFINE_API(检查请求参数,CHECK_ARGS_COUNT,形成特定类型的返回值);plugin_initialize,它可以处理boost::program_options::variables_map,以便通过节点配置文件进行更精细的插件调整。所有请求必须生成JSON格式并通过RPC执行。传输协议取决于节点的配置,有通过标准HTTP请求的JSON-RPC选项,也有通过WebSocket的选项。
为此,必须在节点的配置文件中连接以下插件:json_rpc、webserver。为了接受来自用户的交易,还必须启用network_broadcast_api插件。端口设置:
# 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要处理支持SSL的请求,需要通过代理服务器(例如nginx或apache)转发所使用的端口,这样就能实现通过https/wss进行请求。
向公共节点生成请求的规则相当简单:
{"id":REQUEST_ID,"jsonrpc":"2.0","method":"call","params":["PLUGIN_NAME","PLUGIN_API_METHOD",[ARGS]]}一个负责在网络节点之间接收和发送已签名区块及交易的插件
示例:
{"id":1,"method":"call","jsonrpc":"2.0","params":["custom_protocol_api","get_account",[["readdle","V"]]]}回答:
{
  "id": 116,
  "name": "readdle",
  "master_authority": {
    "weight_threshold": 1,
    "account_auths": [],
    "key_auths": [
      [
        "VIZ7PZqJj3UvV3kymCWHnwn9PxRGgt9z6MDzyEeXCFVr6X9XmoBCY",
        1
      ]
    ]
  },
  "active_authority": {
    "weight_threshold": 1,
    "account_auths": [],
    "key_auths": [
      [
        "VIZ6CmEqBAdWu1MGrSeTPwSG9yKwLwBPEuVBhzbKBMFP2aYTomCCN",
        1
      ]
    ]
  },
  "regular_authority": {
    "weight_threshold": 1,
    "account_auths": [],
    "key_auths": [
      [
        "VIZ8PqkhFxxifidH6688oSZHYPGsXMhRVE9xhvh6N65XkYRg74pMR",
        1
      ]
    ]
  },
  "memo_key": "VIZ7Lo593wA3SwFwpiHYfm3kWw4BwgrcBsYt1UDyZW4J3dd5GWuab",
  "json_metadata": "{\"profile\":{\"nickname\":\"Readdle.me\",\"about\":\"RU: Децентрализованная Социальная Сеть, где пользователь выступает в роли Оракула, обрабатывающего социальную активность интересных ему аккаунтов. Работает на блокчейне VIZ с использованием системы награждений Социальным Капиталом VIZ (Ƶ).\",\"avatar\":\"https://readdle.me/readdle-avatar.png\",\"services\":{\"telegram\":\"readdle_me\"},\"interests\":[\"ru\",\"welcome\"],\"pinned\":\"viz://@readdle/22099872/\"}}",
  "proxy": "",
  "referrer": "",
  "last_master_update": "1970-01-01T00:00:00",
  "last_account_update": "2020-11-05T19:23:33",
  "created": "2018-09-29T18:25:27",
  "recovery_account": "in",
  "last_account_recovery": "1970-01-01T00:00:00",
  "subcontent_count": 0,
  "vote_count": 0,
  "content_count": 0,
  "awarded_rshares": 0,
  "custom_sequence": 4,
  "custom_sequence_block_num": 22897215,
  "energy": 10000,
  "last_vote_time": "2018-09-29T18:25:27",
  "balance": "0.000 VIZ",
  "vesting_shares": "24.102958 SHARES",
  "delegated_vesting_shares": "0.000000 SHARES",
  "received_vesting_shares": "10.056015 SHARES",
  "vesting_withdraw_rate": "0.000000 SHARES",
  "next_vesting_withdrawal": "1969-12-31T23:59:59",
  "withdrawn": 0,
  "to_withdraw": 0,
  "withdraw_routes": 0,
  "curation_rewards": 0,
  "posting_rewards": 0,
  "receiver_awards": 24103,
  "benefactor_awards": 0,
  "proxied_vsf_votes": [
    0,
    0,
    0,
    0
  ],
  "witnesses_voted_for": 0,
  "witnesses_vote_weight": 0,
  "last_post": "1970-01-01T00:00:00",
  "last_root_post": "1970-01-01T00:00:00",
  "average_bandwidth": "16919020937",
  "lifetime_bandwidth": "52767000000",
  "last_bandwidth_update": "2020-12-03T11:55:45",
  "witness_votes": [],
  "valid": true,
  "account_seller": "",
  "account_offer_price": "0.000 VIZ",
  "account_on_sale": false,
  "account_on_sale_start_time": "1970-01-01T00:00:00",
  "subaccount_seller": "",
  "subaccount_offer_price": "0.000 VIZ",
  "subaccount_on_sale": false
}示例:
{"id":1,"method":"call","jsonrpc":"2.0","params":["database_api","get_accounts",[["wildviz","zozo"]]]}回答:
[
  {
    "id": 3276,
    "name": "wildviz",
    "master_authority": {
      "weight_threshold": 1,
      "account_auths": [],
      "key_auths": [
        [
          "VIZ7TAJxC1ibwYEgWon3YWXJdjKaTPS3eVy9zSKq2cRFECMuJvHcq",
          1
        ]
      ]
    },
    "active_authority": {
      "weight_threshold": 1,
      "account_auths": [],
      "key_auths": [
        [
          "VIZ8dJxeKPXe5wf6bnqaMsj3EW8EbMURoKxR4RqPCQH8xA89b6RpC",
          1
        ]
      ]
    },
    "regular_authority": {
      "weight_threshold": 1,
      "account_auths": [],
      "key_auths": [
        [
          "VIZ8iKb38H1JD7PZqEYpoLX3nMt1mgfoh2LwngexvywusE7fgrd5G",
          1
        ]
      ]
    },
    "memo_key": "VIZ8mvAu9beH6gDtBrdy3oh8eTGP4tKpNXHvY7wtGm7EXLuwFaRi7",
    "json_metadata": "",
    "proxy": "",
    "referrer": "",
    "last_master_update": "1970-01-01T00:00:00",
    "last_account_update": "1970-01-01T00:00:00",
    "created": "2019-03-14T08:33:21",
    "recovery_account": "xchng",
    "last_account_recovery": "1970-01-01T00:00:00",
    "subcontent_count": 0,
    "vote_count": 10,
    "content_count": 0,
    "awarded_rshares": 0,
    "custom_sequence": 2,
    "custom_sequence_block_num": 10319967,
    "energy": 9995,
    "last_vote_time": "2019-10-14T08:08:48",
    "balance": "0.000 VIZ",
    "vesting_shares": "19159.343040 SHARES",
    "delegated_vesting_shares": "250.000000 SHARES",
    "received_vesting_shares": "0.000000 SHARES",
    "vesting_withdraw_rate": "0.000000 SHARES",
    "next_vesting_withdrawal": "1969-12-31T23:59:59",
    "withdrawn": 1500000000,
    "to_withdraw": 1500000000,
    "withdraw_routes": 0,
    "curation_rewards": 0,
    "posting_rewards": 0,
    "receiver_awards": 255786,
    "benefactor_awards": 2958047,
    "proxied_vsf_votes": [
      0,
      0,
      0,
      0
    ],
    "witnesses_voted_for": 1,
    "witnesses_vote_weight": "19159343040",
    "last_post": "1970-01-01T00:00:00",
    "last_root_post": "1970-01-01T00:00:00",
    "average_bandwidth": 1089649559,
    "lifetime_bandwidth": "24196000000",
    "last_bandwidth_update": "2019-10-14T08:08:48",
    "witness_votes": [
      "wildviz"
    ],
    "valid": true,
    "account_seller": "",
    "account_offer_price": "0.000 VIZ",
    "account_on_sale": false,
    "account_on_sale_start_time": "1970-01-01T00:00:00",
    "subaccount_seller": "",
    "subaccount_offer_price": "0.000 VIZ",
    "subaccount_on_sale": false
  }
]示例:
{"id":1,"method":"call","jsonrpc":"2.0","params":["database_api","get_accounts_on_sale",[0,1000]]}回答:
[
  {"account":"btc","account_seller":"ae","account_offer_price":"5000.000 VIZ"},
  {"account":"press","account_seller":"on1x","account_offer_price":"10000.000 VIZ"}
]示例:
{"id":1,"method":"call","jsonrpc":"2.0","params":["database_api","get_subaccounts_on_sale",[0,1000]]}回答:
[
  {"account":"com","subaccount_seller":"ae","subaccount_offer_price":"10.000 VIZ"},
  {"account":"digital","subaccount_seller":"on1x","subaccount_offer_price":"100.000 VIZ"},
  {"account":"blog","subaccount_seller":"on1x","subaccount_offer_price":"100.000 VIZ"},
  {"account":"new.romankr","subaccount_seller":"romankr","subaccount_offer_price":"10.000 VIZ"},
  {"account":"romankr1","subaccount_seller":"romankr","subaccount_offer_price":"10.000 VIZ"},
  {"account":"viz","subaccount_seller":"committee","subaccount_offer_price":"10000.000 VIZ"}
]示例:
{"id":1,"method":"call","jsonrpc":"2.0","params":["database_api","get_block",["1"]]}回答:
{
  "previous": "0000000000000000000000000000000000000000",
  "timestamp": "2018-09-29T10:23:27",
  "witness": "committee",
  "transaction_merkle_root": "0000000000000000000000000000000000000000",
  "extensions": [
    [
      1,
      "1.0.0"
    ]
  ],
  "witness_signature": "2003120d1f1d8bb8e8325036838e5269332fbec7c88cd3cc76e4517d27772856ce43ef6bf0a7fde9987cec9467b944e7626db100b70867e7ec82308879a021e97a",
  "transactions": []
}示例:
{"id":1,"method":"call","jsonrpc":"2.0","params":["database_api","get_block_header",["2"]]}回答:
{
  "previous": "000000010496d4414ddcee5b76f9a6b950da6fe9",
  "timestamp": "2018-09-29T10:23:30",
  "witness": "committee",
  "transaction_merkle_root": "0000000000000000000000000000000000000000",
  "extensions": []
}示例:
{"id":1,"method":"call","jsonrpc":"2.0","params":["database_api","get_chain_properties",[]]}回答:
{
  "account_creation_fee": "1.000 VIZ",
  "maximum_block_size": 65536,
  "create_account_delegation_ratio": 10,
  "create_account_delegation_time": 2592000,
  "min_delegation": "1.000 VIZ",
  "min_curation_percent": 0,
  "max_curation_percent": 10000,
  "bandwidth_reserve_percent": 0,
  "bandwidth_reserve_below": "0.000000 SHARES",
  "flag_energy_additional_cost": 0,
  "vote_accounting_min_rshares": 50000,
  "committee_request_approve_min_percent": 1000,
  "inflation_witness_percent": 2000,
  "inflation_ratio_committee_vs_reward_fund": 7500,
  "inflation_recalc_period": 806400,
  "data_operations_cost_additional_bandwidth": 0,
  "witness_miss_penalty_percent": 100,
  "witness_miss_penalty_duration": 86400
}示例:
{"id":1,"method":"call","jsonrpc":"2.0","params":["database_api","get_config",[]]}回答:
{
  "CHAIN_100_PERCENT": 10000,
  "CHAIN_1_PERCENT": 100,
  "CHAIN_ADDRESS_PREFIX": "VIZ",
  "CHAIN_BANDWIDTH_AVERAGE_WINDOW_SECONDS": 604800,
  "CHAIN_BANDWIDTH_PRECISION": 1000000,
  "CONSENSUS_BANDWIDTH_RESERVE_PERCENT": 1000,
  "CONSENSUS_BANDWIDTH_RESERVE_BELOW": 500000000,
  "CHAIN_HARDFORK_VERSION": "2.4.0",
  "CHAIN_VERSION": "2.4.0",
  "CHAIN_BLOCK_INTERVAL": 3,
  "CHAIN_BLOCKS_PER_DAY": 28800,
  "CHAIN_BLOCKS_PER_YEAR": 10512000,
  "CHAIN_CASHOUT_WINDOW_SECONDS": 86400,
  "CHAIN_ID": "2040effda178d4fffff5eab7a915d4019879f5205cc5392e4bcced2b6edda0cd",
  "CHAIN_HARDFORK_REQUIRED_WITNESSES": 17,
  "CHAIN_INITIATOR_NAME": "viz",
  "CHAIN_INITIATOR_PUBLIC_KEY_STR": "VIZ6MyX5QiXAXRZk7SYCiqpi6Mtm8UbHWDFSV8HPpt7FJyahCnc2T",
  "CHAIN_INIT_SUPPLY": "50000000000",
  "CHAIN_COMMITTEE_ACCOUNT": "committee",
  "CHAIN_COMMITTEE_PUBLIC_KEY_STR": "VIZ6Yt7d6LsngBoXQr47aLv97bJVs7jyr7esZTM4UUSpLUf3nbRKS",
  "CHAIN_IRREVERSIBLE_THRESHOLD": 7500,
  "CHAIN_IRREVERSIBLE_SUPPORT_MIN_RUN": 2,
  "CHAIN_MAX_ACCOUNT_NAME_LENGTH": 25,
  "CHAIN_MAX_ACCOUNT_WITNESS_VOTES": 100,
  "CHAIN_BLOCK_SIZE": 6291456,
  "CHAIN_MAX_COMMENT_DEPTH": 65520,
  "CHAIN_MAX_MEMO_LENGTH": 2048,
  "CHAIN_MAX_WITNESSES": 21,
  "CHAIN_MAX_PROXY_RECURSION_DEPTH": 4,
  "CHAIN_MAX_RESERVE_RATIO": 20000,
  "CHAIN_MAX_SUPPORT_WITNESSES": 10,
  "CHAIN_MAX_SHARE_SUPPLY": "1000000000000000",
  "CHAIN_MAX_SIG_CHECK_DEPTH": 2,
  "CHAIN_MAX_TIME_UNTIL_EXPIRATION": 3600,
  "CHAIN_MAX_TRANSACTION_SIZE": 65536,
  "CHAIN_MAX_UNDO_HISTORY": 10000,
  "CHAIN_MAX_VOTE_CHANGES": 5,
  "CHAIN_MAX_TOP_WITNESSES": 11,
  "CHAIN_MAX_WITHDRAW_ROUTES": 10,
  "CHAIN_MAX_WITNESS_URL_LENGTH": 2048,
  "CHAIN_MIN_ACCOUNT_CREATION_FEE": 1000,
  "CHAIN_MIN_ACCOUNT_NAME_LENGTH": 2,
  "CHAIN_MIN_BLOCK_SIZE_LIMIT": 65536,
  "CHAIN_MAX_BLOCK_SIZE_LIMIT": 2097152,
  "CHAIN_NULL_ACCOUNT": "null",
  "CHAIN_NUM_INITIATORS": 0,
  "CHAIN_PROXY_TO_SELF_ACCOUNT": "",
  "CHAIN_SECONDS_PER_YEAR": 31536000,
  "CHAIN_VESTING_WITHDRAW_INTERVALS": 28,
  "CHAIN_VESTING_WITHDRAW_INTERVAL_SECONDS": 86400,
  "CHAIN_ENERGY_REGENERATION_SECONDS": 432000,
  "TOKEN_SYMBOL": 1514755587,
  "SHARES_SYMBOL": "23438642651878150",
  "CHAIN_NAME": "VIZ",
  "CHAIN_BLOCK_GENERATION_POSTPONED_TX_LIMIT": 5,
  "CHAIN_PENDING_TRANSACTION_EXECUTION_LIMIT": 200000
}示例:
{"id":1,"method":"call","jsonrpc":"2.0","params":["database_api","get_database_info",[]]}回答:
{
  "total_size": "6442450944",
  "free_size": 1828375424,
  "reserved_size": 0,
  "used_size": "4614075520",
  "index_list": [
    {
      "name": "graphene::chain::dynamic_global_property_object",
      "record_count": 1
    },
    {
      "name": "graphene::chain::account_object",
      "record_count": 3700
    },
    {
      "name": "graphene::chain::account_authority_object",
      "record_count": 3700
    },
    {
      "name": "graphene::chain::witness_object",
      "record_count": 59
    },
    {
      "name": "graphene::chain::transaction_object",
      "record_count": 4
    },
    {
      "name": "graphene::chain::block_summary_object",
      "record_count": 65536
    },
    {
      "name": "graphene::chain::witness_schedule_object",
      "record_count": 1
    },
    {
      "name": "graphene::chain::content_object",
      "record_count": 0
    },
    {
      "name": "graphene::chain::content_type_object",
      "record_count": 0
    },
    {
      "name": "graphene::chain::content_vote_object",
      "record_count": 0
    },
    {
      "name": "graphene::chain::witness_vote_object",
      "record_count": 239
    },
    {
      "name": "graphene::chain::hardfork_property_object",
      "record_count": 1
    },
    {
      "name": "graphene::chain::withdraw_vesting_route_object",
      "record_count": 7
    },
    {
      "name": "graphene::chain::master_authority_history_object",
      "record_count": 4
    },
    {
      "name": "graphene::chain::account_recovery_request_object",
      "record_count": 0
    },
    {
      "name": "graphene::chain::change_recovery_account_request_object",
      "record_count": 0
    },
    {
      "name": "graphene::chain::escrow_object",
      "record_count": 0
    },
    {
      "name": "graphene::chain::vesting_delegation_object",
      "record_count": 286
    },
    {
      "name": "graphene::chain::fix_vesting_delegation_object",
      "record_count": 0
    },
    {
      "name": "graphene::chain::vesting_delegation_expiration_object",
      "record_count": 12
    },
    {
      "name": "graphene::chain::account_metadata_object",
      "record_count": 3700
    },
    {
      "name": "graphene::chain::proposal_object",
      "record_count": 0
    },
    {
      "name": "graphene::chain::required_approval_object",
      "record_count": 0
    },
    {
      "name": "graphene::chain::committee_request_object",
      "record_count": 39
    },
    {
      "name": "graphene::chain::committee_vote_object",
      "record_count": 453
    },
    {
      "name": "graphene::chain::invite_object",
      "record_count": 364
    },
    {
      "name": "graphene::chain::award_shares_expire_object",
      "record_count": 2463
    },
    {
      "name": "graphene::chain::paid_subscription_object",
      "record_count": 4
    },
    {
      "name": "graphene::chain::paid_subscribe_object",
      "record_count": 26
    },
    {
      "name": "graphene::chain::witness_penalty_expire_object",
      "record_count": 4
    },
    {
      "name": "graphene::plugins::private_message::message_object",
      "record_count": 0
    },
    {
      "name": "graphene::plugins::operation_history::operation_object",
      "record_count": 11967735
    },
    {
      "name": "graphene::plugins::account_history::account_history_object",
      "record_count": 12014504
    }
  ]
}示例:
{"id":1,"method":"call","jsonrpc":"2.0","params":["database_api","get_dynamic_global_properties",[]]}回答:
{
  "id": 0,
  "head_block_number": 10920301,
  "head_block_id": "00a6a16d8a63db35fa727c62a49c00bf9d963819",
  "genesis_time": "2018-09-29T10:23:24",
  "time": "2019-10-14T12:22:54",
  "current_witness": "ae.witness",
  "committee_fund": "1442755.925 VIZ",
  "committee_requests": 39,
  "current_supply": "55206722.493 VIZ",
  "total_vesting_fund": "27501277.264 VIZ",
  "total_vesting_shares": "27501269.766153 SHARES",
  "total_reward_fund": "28351.836 VIZ",
  "total_reward_shares": "6610384216022",
  "average_block_size": 120,
  "maximum_block_size": 65536,
  "current_aslot": 10946390,
  "recent_slots_filled": "340282366920938463463374607431768211455",
  "participation_count": 128,
  "last_irreversible_block_num": 10920284,
  "max_virtual_bandwidth": "5986734968066277376",
  "current_reserve_ratio": 20000,
  "vote_regeneration_per_day": 1,
  "bandwidth_reserve_candidates": 1,
  "inflation_calc_block_num": 10315901,
  "inflation_witness_percent": 2000,
  "inflation_ratio": 5000
}示例:
{"id":1,"method":"call","jsonrpc":"2.0","params":["database_api","get_next_scheduled_hardfork",[]]}回答:
{
  "hf_version": "2.4.0",
  "live_time": "2019-04-30T05:00:00"
}示例:
{"id":1,"method":"call","jsonrpc":"2.0","params":["database_api","get_potential_signatures",[{"ref_block_num":41097,"ref_block_prefix":1234018187,"expiration":"2019-10-14T12:21:46","operations":[["award",{"initiator":"social","receiver":"social","energy":15,"custom_sequence":0,"memo":"ubi","beneficiaries":[]}]],"extensions":[]}]]}回答:
[
  "VIZ5iCdUDKypJU3iCp1vbkTk5P7hEW1GgK6E75V1yZZznGU7NuV32"
]示例:
{"id":1,"method":"call","jsonrpc":"2.0","params":["database_api","get_required_signatures",[{"ref_block_num":41097,"ref_block_prefix":1234018187,"expiration":"2019-10-14T12:21:46","operations":[["award",{"initiator":"social","receiver":"social","energy":15,"custom_sequence":0,"memo":"ubi","beneficiaries":[]}]],"extensions":[]},["VIZ7TAJxC1ibwYEgWon3YWXJdjKaTPS3eVy9zSKq2cRFECMuJvHcq","VIZ5iCdUDKypJU3iCp1vbkTk5P7hEW1GgK6E75V1yZZznGU7NuV32"]]]}回答:
[
  "VIZ5iCdUDKypJU3iCp1vbkTk5P7hEW1GgK6E75V1yZZznGU7NuV32"
]示例:
{"id":1,"method":"call","jsonrpc":"2.0","params":["database_api","get_transaction_hex",[{"ref_block_num":41097,"ref_block_prefix":1234018187,"expiration":"2019-10-14T12:21:46","operations":[["award",{"initiator":"social","receiver":"social","energy":15,"custom_sequence":0,"memo":"ubi","beneficiaries":[]}]],"extensions":[],"signatures":["1f64cb23ba686126f9a00d904840e472de44e0e2291eca5c6b380083ee7a0b9f9934bbe9f03404a33a6df0630a020ff4750b886b65f4af8cd9877df8128d45da05"]}]]}回答:
89a08b9f8d495a68a45d012f06736f6369616c06736f6369616c0f000000000000000000037562690000011f64cb23ba686126f9a00d904840e472de44e0e2291eca5c6b380083ee7a0b9f9934bbe9f03404a33a6df0630a020ff4750b886b65f4af8cd9877df8128d45da05示例:
{"id":1,"method":"call","jsonrpc":"2.0","params":["database_api","lookup_account_names",[["wildviz","zozo"]]]}回答:
[
  {
    "id": 3276,
    "name": "wildviz",
    "master_authority": {
      "weight_threshold": 1,
      "account_auths": [],
      "key_auths": [
        [
          "VIZ7TAJxC1ibwYEgWon3YWXJdjKaTPS3eVy9zSKq2cRFECMuJvHcq",
          1
        ]
      ]
    },
    "active_authority": {
      "weight_threshold": 1,
      "account_auths": [],
      "key_auths": [
        [
          "VIZ8dJxeKPXe5wf6bnqaMsj3EW8EbMURoKxR4RqPCQH8xA89b6RpC",
          1
        ]
      ]
    },
    "regular_authority": {
      "weight_threshold": 1,
      "account_auths": [],
      "key_auths": [
        [
          "VIZ8iKb38H1JD7PZqEYpoLX3nMt1mgfoh2LwngexvywusE7fgrd5G",
          1
        ]
      ]
    },
    "memo_key": "VIZ8mvAu9beH6gDtBrdy3oh8eTGP4tKpNXHvY7wtGm7EXLuwFaRi7",
    "json_metadata": "",
    "proxy": "",
    "referrer": "",
    "last_master_update": "1970-01-01T00:00:00",
    "last_account_update": "1970-01-01T00:00:00",
    "created": "2019-03-14T08:33:21",
    "recovery_account": "xchng",
    "last_account_recovery": "1970-01-01T00:00:00",
    "subcontent_count": 0,
    "vote_count": 10,
    "content_count": 0,
    "awarded_rshares": 0,
    "custom_sequence": 2,
    "custom_sequence_block_num": 10319967,
    "energy": 9995,
    "last_vote_time": "2019-10-14T08:08:48",
    "balance": "0.000 VIZ",
    "vesting_shares": "19157.679056 SHARES",
    "delegated_vesting_shares": "250.000000 SHARES",
    "received_vesting_shares": "0.000000 SHARES",
    "vesting_withdraw_rate": "0.000000 SHARES",
    "next_vesting_withdrawal": "1969-12-31T23:59:59",
    "withdrawn": 1500000000,
    "to_withdraw": 1500000000,
    "withdraw_routes": 0,
    "curation_rewards": 0,
    "posting_rewards": 0,
    "receiver_awards": 255786,
    "benefactor_awards": 2958047,
    "proxied_vsf_votes": [
      0,
      0,
      0,
      0
    ],
    "witnesses_voted_for": 1,
    "witnesses_vote_weight": "19157679056",
    "last_post": "1970-01-01T00:00:00",
    "last_root_post": "1970-01-01T00:00:00",
    "average_bandwidth": 1089649559,
    "lifetime_bandwidth": "24196000000",
    "last_bandwidth_update": "2019-10-14T08:08:48",
    "witness_votes": [],
    "valid": true,
    "account_seller": "",
    "account_offer_price": "0.000 VIZ",
    "account_on_sale": false,
    "account_on_sale_start_time": "1970-01-01T00:00:00",
    "subaccount_seller": "",
    "subaccount_offer_price": "0.000 VIZ",
    "subaccount_on_sale": false
  },
  null
]示例:
{"id":1,"method":"call","jsonrpc":"2.0","params":["database_api","lookup_accounts",["ae","10"]]}回答:
[
  "ae",
  "ae-witness",
  "ae.witness",
  "ae0",
  "ae1",
  "ae10",
  "ae100",
  "ae101",
  "ae102",
  "ae103"
]示例:
{"id":1,"method":"call","jsonrpc":"2.0","params":["account_by_key","get_key_references",[["VIZ5Z2po7K5CoCXw2xLPPt8JJvJLJ3xVNANLgTy9KDfLeZH2urSSd","VIZ1111111111111111111111111111111114T1Anm"]]]}回答:
[
  [
    "on1x"
  ],
  [
    "viz"
  ]
]track-account-range 作为json字符串:["from","to"]。示例:
{"id":1,"method":"call","jsonrpc":"2.0","params":["account_history","get_account_history",["on1x","-1","5"]]}回答:
[
  [
    3057,
    {
      "trx_id": "d5f53163bc237b17c8fd6f3278d3ca1b4ae21691",
      "block": 10913871,
      "trx_in_block": 0,
      "op_in_trx": 0,
      "virtual_op": 0,
      "timestamp": "2019-10-14T07:01:18",
      "op": [
        "transfer",
        {
          "from": "viz-social-bot",
          "to": "on1x",
          "amount": "7.725 VIZ",
          "memo": "withdraw:3353"
        }
      ]
    }
  ],
  [
    3058,
    {
      "trx_id": "ac8c4c225334dc59ec604dfa3d56b55dfc0647d3",
      "block": 10916119,
      "trx_in_block": 0,
      "op_in_trx": 0,
      "virtual_op": 1,
      "timestamp": "2019-10-14T08:53:42",
      "op": [
        "receive_award",
        {
          "initiator": "dignity",
          "receiver": "on1x",
          "custom_sequence": 0,
          "memo": "telegram:151842302",
          "shares": "58.246984 SHARES"
        }
      ]
    }
  ]
]示例:
{"id":1,"method":"call","jsonrpc":"2.0","params":["committee_api","get_committee_request",["39","1"]]}回答:
{
  "id": 38,
  "request_id": 39,
  "url": "https://control.viz.world/media/@wildviz/committee-1/",
  "creator": "wildviz",
  "worker": "wildviz",
  "required_amount_min": "0.000 VIZ",
  "required_amount_max": "10000.000 VIZ",
  "start_time": "2019-09-23T15:49:39",
  "duration": 432000,
  "end_time": "2019-09-28T15:49:39",
  "status": 5,
  "votes_count": 23,
  "conclusion_time": "2019-09-28T15:49:39",
  "conclusion_payout_amount": "10000.000 VIZ",
  "payout_amount": "10000.000 VIZ",
  "remain_payout_amount": "0.000 VIZ",
  "last_payout_time": "2019-09-28T15:56:42",
  "payout_time": "2019-09-28T15:56:42",
  "votes": [
    {
      "voter": "wildviz",
      "vote_percent": 10000,
      "last_update": "2019-09-23T15:52:09"
    }
  ]
}示例:
{"id":1,"method":"call","jsonrpc":"2.0","params":["committee_api","get_committee_request_votes",["39"]]}回答:
[
  {
    "voter": "wildviz",
    "vote_percent": 10000,
    "last_update": "2019-09-23T15:52:09"
  },
  {
    "voter": "denis-golub",
    "vote_percent": 10000,
    "last_update": "2019-09-23T15:54:51"
  },
  {
    "voter": "lex",
    "vote_percent": 10000,
    "last_update": "2019-09-23T15:54:51"
  },
  ...
]示例:
{"id":1,"method":"call","jsonrpc":"2.0","params":["committee_api","get_committee_requests_list",["2"]]}回答:
[
  2,
  3,
  13,
  14,
  33
]示例:
{"id":1,"method":"call","jsonrpc":"2.0","params":["invite_api","get_invite_by_id",["0"]]}回答:
{
  "id": 0,
  "creator": "denis-skripnik",
  "receiver": "liveblogs",
  "invite_key": "VIZ5bqU5UEig5o8gESpJCy662LLXQuZWHX49puiZtGhv3ZxmN6e3t",
  "invite_secret": "5JopL2TpywM2WKx83aTLnwLZjZ58ZEnK7aBJ3L2V2KjaVA1Neko",
  "balance": "0.000 VIZ",
  "claimed_balance": "100.000 VIZ",
  "create_time": "2018-10-05T02:12:57",
  "claim_time": "2018-10-05T11:55:36",
  "status": 2
}示例:
{"id":1,"method":"call","jsonrpc":"2.0","params":["invite_api","get_invites_list",["0"]]}回答:
[
  6,
  8,
  15,
  20,
  34,
  ...
]示例:
{"id":1,"method":"call","jsonrpc":"2.0","params":["operation_history","get_ops_in_block",["10913871","false"]]}回答:
[
  {
    "trx_id": "d5f53163bc237b17c8fd6f3278d3ca1b4ae21691",
    "block": 10913871,
    "trx_in_block": 0,
    "op_in_trx": 0,
    "virtual_op": 0,
    "timestamp": "2019-10-14T07:01:18",
    "op": [
      "transfer",
      {
        "from": "viz-social-bot",
        "to": "on1x",
        "amount": "7.725 VIZ",
        "memo": "withdraw:3353"
      }
    ]
  },
  {
    "trx_id": "0122149bdfdbae80a6c3f4cabe36c78e0e20c12d",
    "block": 10913871,
    "trx_in_block": 1,
    "op_in_trx": 0,
    "virtual_op": 0,
    "timestamp": "2019-10-14T07:01:18",
    "op": [
      "transfer",
      {
        "from": "viz-social-bot",
        "to": "dance",
        "amount": "6.827 VIZ",
        "memo": "withdraw:3354"
      }
    ]
  },
  {
    "trx_id": "e8d8da85004234afd048979860d88cd78297ac1e",
    "block": 10913871,
    "trx_in_block": 2,
    "op_in_trx": 0,
    "virtual_op": 0,
    "timestamp": "2019-10-14T07:01:18",
    "op": [
      "transfer",
      {
        "from": "viz-social-bot",
        "to": "hypno",
        "amount": "6.976 VIZ",
        "memo": "withdraw:3355"
      }
    ]
  },
  {
    "trx_id": "0000000000000000000000000000000000000000",
    "block": 10913871,
    "trx_in_block": 65535,
    "op_in_trx": 0,
    "virtual_op": 1,
    "timestamp": "2019-10-14T07:01:21",
    "op": [
      "witness_reward",
      {
        "witness": "wildviz",
        "shares": "0.103999 SHARES"
      }
    ]
  }
]示例:
{"id":1,"method":"call","jsonrpc":"2.0","params":["operation_history","get_transaction",["d5f53163bc237b17c8fd6f3278d3ca1b4ae21691"]]}回答:
{
  "ref_block_num": 34889,
  "ref_block_prefix": 453694329,
  "expiration": "2019-10-14T07:11:20",
  "operations": [
    [
      "transfer",
      {
        "from": "viz-social-bot",
        "to": "on1x",
        "amount": "7.725 VIZ",
        "memo": "withdraw:3353"
      }
    ]
  ],
  "extensions": [],
  "signatures": [
    "203e778ae54b5fe51367bef34a7001eca7257732075723e99f07fd8c4cd0cc8040021925c811fef0ff2151a28193403af6af05f05dadc1427bb0daf604df53ee0c"
  ],
  "transaction_id": "d5f53163bc237b17c8fd6f3278d3ca1b4ae21691",
  "block_num": 10913871,
  "transaction_num": 0
}示例:
{"id":1,"method":"call","jsonrpc":"2.0","params":["paid_subscription_api","get_paid_subscriptions",[0,1]]}回答:
[
  {
    "update_time" : "2019-06-05T15:07:57",
    "creator" : "tratata",
    "id" : 3,
    "period" : 30,
    "levels" : 10,
    "url" : "",
    "amount" : 500000
  }
]示例:
{"id":1,"method":"call","jsonrpc":"2.0","params":["paid_subscription_api","get_active_paid_subscriptions",["on1x"]]}回答:
[
  "viz.world"
]示例:
{"id":1,"method":"call","jsonrpc":"2.0","params":["paid_subscription_api","get_paid_subscription_options",["viz.world"]]}回答:
{
  "creator": "viz.world",
  "url": "https://control.viz.world/media/@viz.world/ru-service/",
  "levels": 2,
  "amount": 5000,
  "period": 30,
  "update_time": "2019-05-21T07:14:45",
  "active_subscribers": [
    "on1x",
    "muz",
    "litrbooh",
    "antonkostroma",
    "xchng",
    "liveblogs",
    "wildviz",
    "id1club"
  ],
  "active_subscribers_count": 8,
  "active_subscribers_summary_amount": 65000,
  "active_subscribers_with_auto_renewal": [
    "on1x",
    "muz",
    "litrbooh",
    "antonkostroma",
    "liveblogs"
  ],
  "active_subscribers_with_auto_renewal_count": 5,
  "active_subscribers_with_auto_renewal_summary_amount": 35000
}示例:
{"id":1,"method":"call","jsonrpc":"2.0","params":["paid_subscription_api","get_paid_subscription_status",["on1x","viz.world"]]}回答:
{
  "subscriber": "on1x",
  "creator": "viz.world",
  "level": 2,
  "amount": 5000,
  "period": 30,
  "start_time": "2019-09-18T07:51:39",
  "next_time": "2019-10-18T07:51:39",
  "end_time": "1969-12-31T23:59:59",
  "active": true,
  "auto_renewal": true
}示例:
{"id":1,"method":"call","jsonrpc":"2.0","params":["witness_api","get_active_witnesses",[]]}回答:
[
  "solox",
  "lexai",
  "xchng",
  "creativity",
  "jackvote",
  "pom-vjfru0njnme",
  "retroscope",
  "wildviz",
  "lex",
  "dordoy",
  "denis-skripnik",
  "dmilash",
  "id1club",
  "lb",
  "ae.witness",
  "denis-golub",
  "litrbooh",
  "mad-max",
  "t3",
  "web3",
  "charity"
]示例:
{"id":1,"method":"call","jsonrpc":"2.0","params":["witness_api","get_witness_count",[]]}回答:
59示例:
{"id":1,"method":"call","jsonrpc":"2.0","params":["witness_api","get_witness_schedule",[]]}回答:
{
  "id": 0,
  "current_virtual_time": "23461032266075892861574835409469",
  "next_shuffle_block_num": 10916661,
  "current_shuffled_witnesses": "736f6c6f780000000000000000000000000000000000000000000000000000006c657861690000000000000000000000000000000000000000000000000000007863686e6700000000000000000000000000000000000000000000000000000063726561746976697479000000000000000000000000000000000000000000006a61636b766f7465000000000000000000000000000000000000000000000000706f6d2d766a667275306e6a6e6d650000000000000000000000000000000000726574726f73636f70650000000000000000000000000000000000000000000077696c6476697a000000000000000000000000000000000000000000000000006c65780000000000000000000000000000000000000000000000000000000000646f72646f79000000000000000000000000000000000000000000000000000064656e69732d736b7269706e696b000000000000000000000000000000000000646d696c61736800000000000000000000000000000000000000000000000000696431636c7562000000000000000000000000000000000000000000000000006c6200000000000000000000000000000000000000000000000000000000000061652e7769746e657373000000000000000000000000000000000000000000006d61642d6d6178000000000000000000000000000000000000000000000000006c697472626f6f6800000000000000000000000000000000000000000000000070686f746f636c75620000000000000000000000000000000000000000000000743300000000000000000000000000000000000000000000000000000000000064656e69732d676f6c75620000000000000000000000000000000000000000006368617269747900000000000000000000000000000000000000000000000000",
  "num_scheduled_witnesses": 21,
  "median_props": {
    "account_creation_fee": "1.000 VIZ",
    "maximum_block_size": 65536,
    "create_account_delegation_ratio": 10,
    "create_account_delegation_time": 2592000,
    "min_delegation": "1.000 VIZ",
    "min_curation_percent": 0,
    "max_curation_percent": 10000,
    "bandwidth_reserve_percent": 0,
    "bandwidth_reserve_below": "0.000000 SHARES",
    "flag_energy_additional_cost": 0,
    "vote_accounting_min_rshares": 50000,
    "committee_request_approve_min_percent": 1000,
    "inflation_witness_percent": 2000,
    "inflation_ratio_committee_vs_reward_fund": 7500,
    "inflation_recalc_period": 806400,
    "data_operations_cost_additional_bandwidth": 0,
    "witness_miss_penalty_percent": 100,
    "witness_miss_penalty_duration": 86400
  },
  "majority_version": "2.4.0"
}示例:
{"id":1,"method":"call","jsonrpc":"2.0","params":["witness_api","get_witnesses",[["0"]]]}回答:
[
  {
    "id": 0,
    "owner": "committee",
    "created": "1970-01-01T00:00:00",
    "url": "",
    "votes": 0,
    "penalty_percent": 0,
    "counted_votes": 0,
    "virtual_last_update": "0",
    "virtual_position": "0",
    "virtual_scheduled_time": "340282366920938463463374607431768211455",
    "total_missed": 15,
    "last_aslot": 144669,
    "last_confirmed_block_num": 132749,
    "signing_key": "VIZ1111111111111111111111111111111114T1Anm",
    "props": {
      "account_creation_fee": "1.000 VIZ",
      "maximum_block_size": 131072,
      "create_account_delegation_ratio": 10,
      "create_account_delegation_time": 2592000,
      "min_delegation": "0.001 VIZ",
      "min_curation_percent": 1600,
      "max_curation_percent": 1600,
      "bandwidth_reserve_percent": 1000,
      "bandwidth_reserve_below": "500.000000 SHARES",
      "flag_energy_additional_cost": 0,
      "vote_accounting_min_rshares": 5000000,
      "committee_request_approve_min_percent": 1000,
      "inflation_witness_percent": 2000,
      "inflation_ratio_committee_vs_reward_fund": 5000,
      "inflation_recalc_period": 806400,
      "data_operations_cost_additional_bandwidth": 0,
      "witness_miss_penalty_percent": 100,
      "witness_miss_penalty_duration": 86400
    },
    "last_work": "0000000000000000000000000000000000000000000000000000000000000000",
    "running_version": "1.0.1",
    "hardfork_version_vote": "0.0.0",
    "hardfork_time_vote": "1970-01-01T00:00:00"
  }
]示例:
{"id":1,"method":"call","jsonrpc":"2.0","params":["witness_api","get_witnesses_by_vote",["","10000"]]}回答:
[
 {
   "id": 42,
   "owner": "solox",
   "created": "2018-12-22T19:09:51",
   "url": "http://viz.world/@solox/witness/",
   "votes": "2265575784725",
   "penalty_percent": 0,
   "counted_votes": "2265575784725",
   "virtual_last_update": "23453249838245982754942708691144",
   "virtual_position": "0",
   "virtual_scheduled_time": "23453400035105083671049497423272",
   "total_missed": 273,
   "last_aslot": 10942638,
   "last_confirmed_block_num": 10916550,
   "signing_key": "VIZ8MzGnSUeqbFaFr8g297XNDT7iWQZ8ktBgeBDYj1moWCHQ8a5PA",
   "props": {
     "account_creation_fee": "1.000 VIZ",
     "maximum_block_size": 65536,
     "create_account_delegation_ratio": 10,
     "create_account_delegation_time": 2592000,
     "min_delegation": "1.000 VIZ",
     "min_curation_percent": 0,
     "max_curation_percent": 10000,
     "bandwidth_reserve_percent": 100,
     "bandwidth_reserve_below": "1.000000 SHARES",
     "flag_energy_additional_cost": 0,
     "vote_accounting_min_rshares": 50000,
     "committee_request_approve_min_percent": 1000,
     "inflation_witness_percent": 3000,
     "inflation_ratio_committee_vs_reward_fund": 7500,
     "inflation_recalc_period": 806400,
     "data_operations_cost_additional_bandwidth": 0,
     "witness_miss_penalty_percent": 100,
     "witness_miss_penalty_duration": 86400
   },
   "last_work": "0000000000000000000000000000000000000000000000000000000000000000",
   "running_version": "2.4.0",
   "hardfork_version_vote": "2.4.0",
   "hardfork_time_vote": "2019-04-30T05:00:00"
 }
]示例:
 {"id":1,"method":"call","jsonrpc":"2.0","params":["witness_api","lookup_witness_accounts",["","4"]]}回答:
 [
  "t3",
  "lb",
  "ae",
  "in"
]应用程序开发通常使用现成的库。特定编程语言库的可用性取决于是否有用于处理密码学、大数和传输协议(http/ws)的模板。由于VIZ历史上是从Graphene发展而来的,大多数适用于EOS/Steem/Golos等区块链系统的库也适用于VIZ。一个显著的区别是与节点的通信格式(json-rpc结构)、描述操作时参数的顺序和名称、二进制形式中复杂数据的格式(例如,VIZ和SHARES资产的格式与Steem中的新SMT格式不同)。
尽管语法不同,但与VIZ交互的基础是相同的:用于密钥和消息签名的密码学、使用数据和公钥验证签名、交易形成、与节点的交互。
每个开发者都可以搭建自己的节点来与VIZ交互,但为初学者理解提供了公共节点:
下面列出了支持大部分节点请求API和交易生成的主要VIZ库。
应用程序开发的最佳工具是vis-js-lib库。它支持服务器端(nodejs)和用户端(浏览器中的js)与VIZ交互所需的一切:
英文版Viz-js-lib文档可在GitHub上获取。关于常用操作的示例,请参阅代码示例部分。
来自ksantoprotein的thallid-viz库支持API请求和交易形成。提供了许多示例涵盖不同操作。
viz-python-lib库支持大部分必要功能,但没有示例和文档(该库尚未完成)。
在切换到适配的BigNumber和椭圆曲线库之后,无需为PHP构建secp256k1并启用GMP支持即可使用密码学功能。
viz-php-lib库支持JsonRPC、密钥处理、交易生成、通过共享密钥进行消息加密(与viz-js-lib兼容),提供示例、PSR-4支持且无需额外依赖即可安装(一体化)。
支持VIZ的php-graphene-node-client库,可通过Composer安装。
viz-go-lib库非常适合API请求和研究交易形成。遗憾的是,该库没有文档,也没有针对单个操作的示例。
viz-swift-lib库 — 一个Swift库,可通过Swift Package Manager安装。
viz_dart_ecc密码学库 — 允许从私钥生成公钥、签名数据、验证签名。
viz-transaction库 — 允许为VIZ区块链形成和签名交易(该库不包含将交易发送到区块链节点的方法,为此需要使用任何其他用于http/ws协议的库)。
如果您没有找到所需的编程语言,可以关注现有的EOS和Steem库。要修改它们以获得VIZ支持,只需检查json-rpc请求的格式,更改chain_id(在VIZ中,它等于2040effda178d4fffff5eab7a915d4019879f5205cc5392e4bcced2b6edda0cd — 这是签名原始交易的前缀)并配置操作构造器。
始终建议新手开发者阅读特定库的文档。这既有助于理解库的工作原理,也能记住在开发应用程序时可以使用的特性。本节以示例形式描述了最常用的查询。VIZ应用程序最常用的库是viz-js-lib,因此示例将使用该库。
详细的英文文档,包含所有方法及其属性的说明,可通过此链接获取。
根据服务器(nodejs)或浏览器(js)的使用场景,库需要以不同的方式连接。
对于nodejs,当前的安装指令是通过npm install viz-js-lib --save安装库,并在js文件中通过var viz = require('viz-js-lib');连接。
对于js连接,您可以通过控制台npm build自行构建webpack库,或使用已构建的库,来自jsDelivr CND或Unpkg CDN。只需将script标签添加到html文件并指定库的URL:<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/viz-js-lib@latest/dist/viz.min.js"></script>,之后您将通过控制台访问全局变量viz。
只要您的应用程序没有大量用户流,使用可用的公共节点是合理的。在撰写本文时,VIZ有两个可用的公共节点:
https://viz.lexa.host/用于通过HTTPS的JSON-RPC请求,wss://viz.lexa.host/ws用于通过SSL的WebSocket的JSON-RPC请求;https://api.viz.world/ 用于通过 HTTPS 的 JSON-RPC 请求。配置 viz 以使用 https://api.viz.world/ 节点的示例:
var api_gate='https://api.viz.world/';
viz.config.set('websocket',api_gate);在插件及其API部分列出了主要插件及其请求——所有这些在viz-js-lib库中都是可用的。要执行特定请求,只需将其名称转换为camelCase即可。
例如,如果您决定向database_api插件发出get_database_info请求,那么您需要运行以下代码:
viz.api.getDatabaseInfo(function(err,response){
    if(!err){
        //response received
        console.log(response);
    }
    else{
        //error
        console.log(err);
    }
});如果请求需要输入数据,您需要将它们添加到调用的开头。例如,要向paid_subscription_api插件请求get_active_paid_subscriptions,您必须指定将为其搜索活跃付费订阅的用户:
var subscriber='on1x';
viz.api.getActivePaidSubscriptions(subscriber,function(err,response){
    if(!err){
        //response received
        console.log(response);
    }
    else{
        //error
        console.log(err);
    }
});对于VIZ协议中的每个操作,vis-js-lib库中都有一个单独的方法,该方法接受私钥(用于签名交易)和操作参数。操作名称,类似于API方法,应转换为驼峰命名法格式。广播accounts_metadata操作(将账户元数据写入区块链)的示例代码:
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);
    }
});一些新手希望定期向节点发送请求并获取关于DGP(动态全局属性)的最新数据,以便基于此显示新区块、满足不可逆区块的条件或在委托者列表中高亮显示最后一个签名区块的委托者。为此,只需每3秒(区块之间的时间间隔)在计时器上请求数据:
var dgp={}
function update_dgp(auto=false){
    viz.api.getDynamicGlobalProperties(function(err,response){
        if(!err){
            dgp=response;
        }
    });
    if(auto){
        setTimeout("update_dgp(true)",3000);
    }
}
update_dgp(true);
获取账户信息并计算账户当前能量值(考虑其恢复速度)的示例代码:
var current_user='on1x';
viz.api.getAccounts([current_user],function(err,response){
    if(!err){
        //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);
    }
});加密密钥是写在secp256k1椭圆曲线上通用DER格式的X和Y坐标(SHA-256作为哈希函数)。在viz-js-lib库中,密钥的转换和操作属于viz.auth模块。
Graphene生态系统发明了一种人类可读密码的机制。由于暴力破解的风险,不建议使用这些密码,因此,为了增加对公钥的私钥多重搜索的难度,我们制定了特定的密钥生成规则,即字符串连接形式:账户登录名、密码(复杂)、访问类型。
一些应用程序已约定使用这些规则,从而通过通用密码简化用户访问各种账户功能。例如,名为test的用户使用通用密码PK3452JENDK332注册。当使用此用户名和密码登录应用程序时,应用程序可以独立生成所需访问类型的密钥,只需使用字符串连接。用户想要转账代币吗?应用程序通过testPK3452JENDK332active字符串动态生成私有的活跃密钥。用户要奖励某人吗?应用程序通过testPK3452JENDK332regular字符串生成私有的常规密钥。这使用户通过共享密码访问更简便,但牺牲了灵活性并使账户面临风险。访问类型具有不同的权限,如果用户可信环境的信任链或网站被攻破,账户访问可能被截获。因此,为了用户安全,一些应用程序放弃这些规则或约定,并不支持通用密码。
通常,在注册用户时,应用程序会自行生成密码。但也有例外,当应用程序允许使用自己的密码生成密钥时。该库允许在viz.auth.toWif(account_login,general_pass,auth_type);方法中独立指定用于生成密钥的字符串。以下示例显示了一个生成给定长度的随机密码并使用它生成密钥的函数,无需绑定到用户和访问类型:
function pass_gen(length=100,to_wif=true){
    let charset='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+-=_:;.,@!^&*$';
    let ret='';
    for (var i=0,n=charset.length;i<length;++i){
        ret+=charset.charAt(Math.floor(Math.random()*n));
    }
    if(!to_wif){
        return ret;
    }
    let wif=viz.auth.toWif('',ret,'');
    return wif;
}您可以使用viz.auth.wifToPublic(wif)方法通过指定的私钥获取公钥。对于那些希望通过连接登录名、密码和访问类型来生成密钥的应用程序,可以使用viz.auth.getPrivateKeys(account_login,general_pass,auth_types)方法。该方法使用result.type模板返回私钥数组(将传递给用户)和result.typePubkey模板返回公钥数组(需要传输到区块链以保存到用户账户)。使用主密码在VIZ中注册新账户的函数代码:
var user_login='test';//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);
        }
    });
}VIZ区块链中存在凭证机制。任何人都可以通过向其中转移VIZ代币来创建它们。凭证可以兑换或用作邀请码以简化新账户的注册。在第一种情况下,代币转移给持有者账户;在第二种情况下,代币转换为新账户的网络份额(所有访问类型使用单一访问密钥)。
创建凭证的示例代码:
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);
    }
});为了匿名使用邀请码,系统中存在一个invite账户,其私密活跃密钥为5KcfoRuDfkhrLCxVcE9x51J6KN9aM9fpb78tLrvvFckxVV6FyFW,代码示例如下:
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);
    }
});为了匿名使用凭证,系统中存在一个invite账户,其私密活跃密钥为5KcfoRuDfkhrLCxVcE9x51J6KN9aM9fpb78tLrvvFckxVV6FyFW,代码示例如下:
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);
    }
});为了自动将所有可用的VIZ代币转换为SHARES网络的份额,您需要请求账户信息并使用transfer_to_vesting操作将它们转换为自己的份额:
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);
    }
});新开发者常常面临这样的问题:用户已将部分代币委托给另一个账户,他们需要计算可用于将SHARES转换为VIZ的可用份额。以下代码示例自动设置可用于从份额中提取转换的SHARES:
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);
    }
});从账户余额向委员会转账1.000 VIZ的示例:
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);
    }
});账户可以使用award操作奖励另一个网络成员。您可以指定目标奖励的目的、原因(custom_sequence编号或memo备注)以及受益人(将分享目标奖励的账户)。 示例:
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);
    }
});有时需要更改账户的访问权限。这可能涉及添加新密钥、委托管理或创建多重签名账户管理条件(当操作需要多个密钥签名时)。
执行操作的权限具有以下结构:
系统检查交易及其中的操作,验证相关账户的签名以及它们是否具有足够权重来执行所需类型的操作。
单密钥权限示例:
{
    "weight_threshold": 1,
    "account_auths": [],
    "key_auths": [
        ["VIZ6LWhhUzKmYM5VcxtC2FpEjzr71imfb7DeGA9yodeqnvtP2SYjA", 1]
    ]
}如果账户将这些权限写入常规访问类型,那么要执行奖励操作,区块链将需要使用密钥5KRLZitDd5c9uZzDgTMF4se4eVewENtZ29GbCuKwbT3msRbtLgi(对应权限中指定的公钥VIZ6LWhhUzKmYM5VcxtC2FpEjzr71imfb7DeGA9yodeqnvtP2SYjA)对交易进行签名。
当将控制权委托给另一个账户时,例如test,需要将权限更改为:
{
    "weight_threshold": 1,
    "account_auths": [
        ["test", 1]
    ],
    "key_auths": [
        ["VIZ6LWhhUzKmYM5VcxtC2FpEjzr71imfb7DeGA9yodeqnvtP2SYjA", 1]
    ]
}此后,区块链将要求使用指定密钥或test账户类似访问类型的密钥进行签名。
多重签名管理意味着权限的复杂化,例如,要实现3个密钥中需要2个签名才能管理,可以使用以下权限:
{
    "weight_threshold": 4,
    "account_auths": [],
    "key_auths": [
        ["VIZ6LWhhUzKmYM5VcxtC2FpEjzr71imfb7DeGA9yodeqnvtP2SYjA", 2],
        ["VIZ5mK1zLnYHy7PbnsxRpS4NbKjEoH2J9eBmgSjVKJ5BKQpLLj9T4", 2],
        ["VIZ4uiqeDPsoteSFVbTWPBUbmfzxYkJyXYmA6B1pAFWZ59n4iBuUK", 2]
    ]
}为了让交易被区块链接受,必须添加3个指定密钥中至少2个的签名。在此示例中,公钥 VIZ4uiqeDPsoteSFVbTWPBUbmfzxYkJyXYmA6B1pAFWZ59n4iBuUK 对应私钥 5KMBKopgd56MZvV8FYhp7AWFyLKiybqRnZYgjXukw34VRE78。
让我们考虑一个重置账户访问权限(更改所有密钥和权限)的示例:
//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");
        }
    });
}每个委托者都有一个区块签名密钥,负责区块链对区块签名的验证。委托者节点配置有私有的签名密钥(最好提前生成),并将公钥发送到区块链以验证签名。如果委托者长时间缺席,区块链会通过将签名密钥清零来将其标记为禁用。您可以通过设置空的签名密钥viz111111111111111111111111111111111114t1anm来断开连接。声明账户为委托者的示例代码:
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);
    }
});为了使委托者开始签名区块,他必须拥有非零的投票权重。账户的份额在为多个委托者投票时会按比例分配。为委托者投票的示例代码:
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);
    }
});如果用户不参与委托者的选择,他可以将自己的份额处置权委托给另一个账户。为此,有一个account_witness_proxy操作,注册器应用程序可以使用它,以免让用户被区块链系统的复杂信息所困扰,同时不失去自身的影响力(因为VIZ代币可以用于账户注册,应用程序将类似的网络份额潜力投资于用户)。
如果用户决定独立参与委托者的选择,第一次为委托者投票将取消代理。
var account_login='test';
var active_key='5K...';
var proxy_login='proxy';
viz.broadcast.accountWitnessProxy(active_key,account_login,proxy_login,function(err,result){
    if(!err){
        console.log(result);
    }
    else{
        console.log(err);
    }
});委托者广播他们对网络投票参数的立场。区块链系统每经过一个委托者队列周期(21个区块)计算一次投票参数的中位数值,并在该周期内固定这些值。有关需要投票的网络参数的更多信息,请参阅VIZ中的对象和结构部分。委托者广播网络投票参数的versioned_chain_properties_update操作示例:
var account_login='test';
var active_key='5K...';//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);
    }
});只有申请的创建者才能取消申请。
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);
    }
});任何网络成员都可以对申请进行投票。在申请有效期结束时,根据投票者总权重中的份额以及他们与申请设定条款的同意程度(从-100%到+100%)从委员会计算满足的支付金额。如果投票者的网络份额金额超过网络的投票参数committee_request_approve_min_percent,并且估算金额在申请设定的框架内,则申请被委员会批准并进入支付队列。整个操作原理可以在database.cpp committee_processing() 方法文件中探索。为申请投票的示例代码:
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);
    }
});VIZ中的付费订阅系统允许任何账户设置协议条款,签署后VIZ代币将从订阅者转移到协议提供者的余额中。该系统允许提供者更改协议条款,并在活动订阅到期和续订时自动尝试与订阅者协商达成一致,且不损害订阅者利益。订阅者可以指定他是根据协议一次性向提供者付款,还是同意在其余额中有足够VIZ代币的情况下自动续订订阅。发布付费订阅协议条款:
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);
    }
});希望签订付费订阅协议的账户必须向系统确认协议条款、所需的订阅级别以及自动续订的需求。您也可以更改订阅级别,系统将自动重新计算并扣除所需金额,或延长协议到期时间:
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);
    }
});获取付费订阅协议条款的信息:
var provider_account='test';
viz.api.getPaidSubscriptionOptions(provider_account,function(err,response){
    if(!err){
        console.log(response);
    }
    else{
        console.log(err);
    }
});您可以通过API请求获取活跃或非活跃的付费订阅列表:
var account_login='subscriber';
viz.api.getActivePaidSubscriptions(account_login,function(err,response){
    for(let i in response){
        console.log('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]);
    }
}您可以通过API请求检查当前协议:
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);
    }
});在VIZ区块链中,可以将份额委托给另一个账户,这允许您转移与奖励、委员会投票和获取网络带宽相关的份额管理权。委托不适用于为委托者投票,因为存在通过account_witness_proxy操作转移其全部份额投票权的单独操作。
份额委托的规则部分在网络协议中规定,部分由委托者控制:
委托时会触发防止滥用双重消耗能量的保护机制。如果委托者转移其50%的份额,则能量将减少50%。在这种情况下,能量可能变为负值(最多-100%)。值得注意的是,delegate_vesting_shares操作设置的是委托份额的实际值。如果您想取消委托,则需要将委托份额的值设置为0.000000 SHARES。如果您想将委托从1000.000000 SHARES增加到3000.000000 SHARES,则只需将委托值设置为3000.000000 SHARES。
将份额委托给另一个账户的示例代码:
var account_login='test';
var active_key='5K...';//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);
    }
});获取委托相关信息:
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);
        }
    }
});获取委托取消后委托份额返还的相关信息:
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);
        }
    }
});当开发者需要在区块链中引入自己的结构,构建一个能够监控区块并记录网络中操作的去中心化应用(dApp)时,他们可以使用自定义操作。自定义操作具有灵活的结构:
开发者可以通过自定义操作设计自己的数据结构、命令协议及其记录方式。例如,可以用于卡牌游戏、媒体博客、评论、产品目录或广告区块处理。
使用自定义操作的示例:
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);
});账户所有者可以使用主访问权限将其挂牌出售:
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);
});您也可以将子账户(*.登录名)挂牌出售:
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);
});您可以使用buy_account操作购买账户或子账户,系统将检查购买的可能性并在条件允许时完成交易:
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);
    }
});三方交易基于担保人(agent)检查条件履行的原则运作。接收者和担保人必须通过escrow_approve操作确认交易开始(担保人在此阶段获得佣金),否则,当批准截止日期(ratification_deadline)到达时,所有代币将返还给交易发起者(database.cpp文件中的expire_escrow_ratification方法)。
如果出现争议时刻,发送方或接收方可以通过escrow_dispute操作启动争议处理程序,之后交易的决定权转移给担保人(由他决定谁将获得多少代币)。如果交易被暂停且长时间未解决——合约将到期(escrow_expiration),所有代币由担保人管理(如果争议已开启),或由交易任何一方管理。
创建托管转账:
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);
    }
});确认在提议条件下参与交易(担保人和接收者必须通过escrow_approve操作确认他们的参与):
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);
    }
});争议请求(发送方或接收方可以通过escrow_dispute操作对交易提出争议):
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);
    }
});释放资金(escrow_release操作):
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);
    }
});要获取托管交易信息,您需要调用database_api插件的get_escrow API请求:
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);
    }
});创建账户时,创建者会被记录在recovery账户字段中,作为恢复账户访问权限的受信任人,以防账户被黑客攻击和访问密钥被更改。区块链会记录主权限的更改并将其保存30天。正是在这30天内,受信任账户可以通过request_account_recovery操作创建恢复访问权限的请求:
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);
});创建更改访问权限的请求后,账户必须通过recover_account操作确认它。一个重要点是,交易必须同时使用两个密钥签名——来自信任账户申请中的旧密钥和新密钥,因此您需要使用viz.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);
});要更改用于恢复访问的受信任账户,您可以使用change_recovery_account操作,更改将在30天后生效(以防止滥用):
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);
});为了管理提议操作系统,有3种操作:创建提议、提供签名(更新)、删除提议。创建提议后,区块链系统将等待所有必要的签名以执行嵌入在提议内部的操作,之后将执行这些操作。如果到期日期已到,提议将不会被执行。提议操作系统用于管理多重签名账户。要创建提议,请使用 proposal_create 操作:
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);
});要获取用户提出的提议信息,您需要向database_api插件执行get_proposed_transactions API请求(将返回一个提议数组):
var looking_account='test';
var from=0;
var limit=100;
viz.api.getProposedTransactions(looking_account,from,limit,function(err,response){
    console.log(err,response);
});提议系统支持区块链中所有现有的操作,但不允许混合需要不同权限的操作(例如,需要活跃权限的transfer操作和需要常规权限的award操作)。要提供所需访问类型的签名,您可以使用proposal_update操作,通过在数组中指定交易签名者的登录名来添加或移除确认提议的签名者列表(为此有4种数组类型:活跃、主权限、常规和用于单个密钥的密钥数组)。一旦提供了所有必需的签名,提议中的操作将被执行(前提是未指定review_period_time周期)。如果执行出错,将在到期时进行第二次尝试。 示例:
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);
});提议可以由申请人或任何需要其签名的参与者删除。为此,只需执行proposal_delete操作并使用活跃密钥进行签名:
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);
});在同一交易中由同一账户创建延迟奖励操作并使用活跃密钥和常规密钥对交易进行签名的实现示例:
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='通过提案实现延迟奖励';
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)
});要使用私钥对数据进行签名(viz.auth.signature.sign方法,为了寻找规范签名,需要添加一个将在签名数据中的随机数),并使用公钥验证签名(viz.auth.signature.verifyData方法),您可以使用标准的viz-js-lib方法:
var private_key='5KRLZitDd5c9uZzDgTMF4se4eVewENtZ29GbCuKwbT3msRbtLgi';
var public_key='VIZ6LWhhUzKmYM5VcxtC2FpEjzr71imfb7DeGA9yodeqnvtP2SYjA';
var invalid_public_key='VIZ65kiW3JsxsF7NCabAuSJUk8Efhx5PW6cbgSS5uuZpbkSTpSjn6';
var data={data:"data signature check!",nonce:0};
var nonce=0;
var data_with_nonce='';
var signature='';
function auth_signature_check(hex){//checking for the canonicity of the signature
    if('1f'==hex.substring(0,2)){
        return true;
    }
    return false;
}
while(!auth_signature_check(signature)){
    data.nonce=nonce;
    data_with_nonce=JSON.stringify(data);
    signature=viz.auth.signature.sign(data_with_nonce,private_key).toHex();
    nonce++;
}
console.log('data with nonce',data_with_nonce);
console.log('signature',signature);
console.log('check by public_key',viz.auth.signature.verifyData(data_with_nonce,viz.auth.signature.fromHex(signature),public_key));
console.log('check by invalid_public_key',viz.auth.signature.verifyData(data_with_nonce,viz.auth.signature.fromHex(signature),invalid_public_key));如果您的应用程序不需要密码学和交易签名,那么您可以使用原生工具通过js进行json-rpc请求。
WebSocket连接示例:
var api_gate='wss://solox.world/ws';
var latency_start=new Date().getTime();
var latency=-1;
var socket = new WebSocket(api_gate);
socket.onmessage=function(event){
    latency=new Date().getTime() - latency_start;
    let json=JSON.parse(event.data);
    if(json.result){
        console.log(json.result);
    }
    else{
        console.log(json.error);
    }
    socket.close();
}
socket.onopen=function(){
    socket.send('{"id":1,"method":"call","jsonrpc":"2.0","params":["database_api","get_dynamic_global_properties",[]]}');
};HTTP连接示例:
var api_gate='https://viz.lexa.host/';
var latency_start=new Date().getTime();
var latency=-1;
var xhr = new XMLHttpRequest();
xhr.overrideMimeType('text/plain');
xhr.open('POST',api_gate);
xhr.setRequestHeader('accept','application/json, text/plain, */*');
xhr.setRequestHeader('content-type','application/json');
xhr.onreadystatechange=function(){
    if(4==xhr.readyState && 200==xhr.status){
        latency=new Date().getTime() - latency_start;
        console.log(xhr);
        let json=JSON.parse(xhr.response);
        if(json.result){
            console.log(json.result);
        }
        else{
            console.log(json.error);
        }
    }
}
xhr.send('{"id":1,"method":"call","jsonrpc":"2.0","params":["database_api","get_dynamic_global_properties",[]]}');VIZ协议(使用Graphene代码库)中嵌入了形成交易的规则。使用加密密钥签名的数据必须符合代码中嵌入的所有二进制表示结构和规则。本节描述密钥的编码方式、数据的二进制表示或其准备过程。
需要根据嵌套操作账户中使用的相应访问类型密钥进行签名的数据结构,其二进制表示包含以下字段:
VIZ进行fc::sha256::hash得到的:2040effda178d4fffff5eab7a915d4019879f5205cc5392e4bcced2b6edda0cd。值得注意的是,fc::sha256::hash将字符串转换为c_str,并在其十六进制值56495A的开头添加字符串长度,最终是对十六进制值0356495A计算sha256;00);基于Graphene的区块链系统代码是开放和免费的,允许您启动新的链,既可以保持不变,也可以完全重新设计,拥有自己的机制和经济体系。此外,许多项目运行公共测试链以检查更改。为了防止节点混淆,防止同一网络中的交易不能在分叉(或具有类似账户和密钥的链)中使用,存在一个链标识符,它在每笔交易和签名操作中作为标签存在。
VIZ中的私钥和公钥基于DSA算法定位,并使用密码学来验证数据集的签名。许多非密码学专家的开发者只是使用专门的库,而不深入细节。
让我们考虑将私钥(由32字节组成)转换为可读的WIF格式的步骤:
80;123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz对得到的二进制结果进行编码;将公钥(由32字节组成)逐步转换为可读格式:
123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz对得到的二进制结果进行编码;VIZ);vis-js-lib库使用auth模块(GitHub链接),允许使用预安装的方法来处理密钥和签名数据。
escrow的字符串值在二进制表示中对应十六进制值06657363726f77);award操作中指定的energy是uint16_t类型,传输只需2字节。如果需要传递10.00%的值,则整数值为1000,十六进制表示为03EB,其翻转值将为EB03。代表uint64_t的custom_sequence字段由8字节组成,因此要传输十进制值377,其十六进制值0179在二进制值中将是十六进制:7901000000000000。2019-02-07T06:19:23,也称为GMT)。它们的二进制值以十进制unixtime格式写入,遵循整数表示规则。例如2019-02-07T06:19:23在unixtime中为1549520363(十六进制值为5C5BCDEB),其在二进制值中将是十六进制:EBCD5B5C。03,SHARES为06),7字节的字符串代码值(VIZ为56495A00000000,SHARES为53484152455300)。例如:JSON操作值中的1.002 VIZ在十六进制中的二进制表示为:EA030000000000000356495A。026a1dbaacb805f145f9276025627102152840bb1aa09b7fac580f892d93b572b4二进制值对应一个恢复ID为02、X坐标为6a1dbaacb805f145f927602562710215(十六进制表示)、Y坐标为2840bb1aa09b7fac580f892d93b572b4(十六进制表示)的私钥。这对应于公钥VIZ5hDwvV1PPUTmehSmZecaxo1ameBpCMNVmYHKK2bL1ppLGRvh85。transfer操作在二进制形式中将有十六进制条目02,而create_invite操作将有十六进制值2b。让我们分析一个JSON格式的交易示例及其二进制表示:
{"ref_block_num":9023,"ref_block_prefix":1971875185,"expiration":"2019-02-07T06:19:23","operations":[["transfer",{"from":"test1","to":"test2","amount":"1.002 VIZ","memo":"<3"}]],"extensions":[]}ffff 进行按位与后的值(例如,数字9023的十六进制表示为 233F,根据整数表示规则应进行翻转,得到 3F23);get_block_header API请求(指定下一个区块号9024)来获取。响应中将包含所需区块的 previous 字段,其ID为 0000233F716D887523BB63AD3E6107C96EDCFD8A。我们取 716D8875 作为二进制表示,翻转字节得到 75886D71,然后转换为JSON所需的十进制格式:1971875185。2019-02-07T06:19:23 转换为unixtime为 1549520363(十六进制值为 5C5BCDEB),在二进制值中将被翻转并以十六进制表示为:EBCD5B5C。01);02);test1 的字符串长度和十六进制表示:057465737431);test2 的字符串长度和十六进制表示:057465737432);1.002 VIZ 的十六进制表示:EA030000000000000356495A00000000);<3 的字符串长度和十六进制表示:023C33);00)。交易数据的最终二进制表示(十六进制):3F23716D8875EBCD5B5C0102057465737431057465737432EA030000000000000356495A00000000023C3300;
要将交易发送到区块链,需要在此表示的开头补充 chain_id 并用私钥进行签名。得到的签名必须添加到JSON的 signatures 数组字段中,例如:
{"ref_block_num":9023,"ref_block_prefix":1971875185,"expiration":"2019-02-07T06:19:23","operations":[["transfer",{"from":"test1","to":"test2","amount":"1.002 VIZ","memo":"<3"}]],"extensions":[],"signatures":["1f500f2a5d721e45c53e76fca786d690c7c0556f1923aa07c944e26614b50481d353e88f82e731be74c18e3fb8d117dc992a475991974b6e1364a66f5ccb618f83"]}并通过 network_broadcast_api 插件的 broadcast_transaction API 接口传递此 JSON 数据。
节点的源代码将已签名交易的类型定义为 transaction_id_type,这在Graphene 协议中被写为 fc::ripemd160。但实践表明,交易ID并非ripemd160(哈希大小为20字节),而是sha256哈希的一部分(哈希大小为32字节)。不确定为什么会发生这种情况,但我们可以做出两种假设:
digest_type::encoder 转换交易时,位于协议文件 transaction.cpp中);值得注意的是,这个错误在EOS协议中得到了修复,EOS使用了fc::sha256交易类型,这表明这确实是一个错误。因此,在EOS之前的Graphene项目中,交易ID是sha256哈希,但是被截断的前20字节(而不是完整的32字节)。
例如,VIZ中的一笔交易来自区块11142739的ID是c84f9e8255859b2083be720cf9b64b3542e4360f。原始交易{"ref_block_num": 1612, "ref_block_prefix":2641357798,"expiration": "2019-10-22T05:59:27","operations":[["award",{"initiator":"on1x","receiver":"viz-social-bot","energy":20,"custom_sequence":0,"memo":"telegram:262632819","beneficiaries":[]}]],"extensions":[]}的十六进制表示为:4c06e6eb6f9dbf9aae5d012f046f6e31780e76697a2d736f6369616c2d626f74140000000000000000001274656c656772616d3a3236323633323831390000。
原始交易的sha256哈希为:c84f9e8255859b2083be720cf9b64b3542e4360f0a62e33363bca5d984ee608a,其前20字节即为它在区块链中的标识符:c84f9e8255859b2083be720cf9b64b3542e4360f。
区块链节点存储最后65537个区块的ID(更多信息,请阅读关于VIZ中的TaPoS概念)。在大多数情况下,开发者会引用最近的一个区块,通常通过执行一系列操作来完成:
database_api 插件发送 get_dynamic_global_properties API请求来获取系统状态数据;head_block_number 字段的值减去3个区块,设定将基于哪个区块形成ref_block_num并请求其ID;database_api 插件执行 get_block_header API请求,获取所使用的区块ID,请求的区块号是所需区块号加一(因为每个区块的头部包含指向前一个区块ID的引用,所需的ID位于下一个区块中);大多数包含抽象层以简化调用和向区块链传输交易的库都会自动完成此操作。
在viz-js-lib中手动获取 ref_block_num 和 ref_block_prefix 的示例可以在库本身的交易准备抽象层的源代码中找到。
在PHP中类似获取 ref_block_num 和 ref_block_prefix 的示例位于php-graphene-node-client库的源代码中。
所有操作及其参数都记录在VIZ协议中,并位于 chain_operations.hpp 文件中。
正是在那里,您可以研究参数的类型及其在操作中的必需顺序。注意! 操作结构中的参数顺序与操作本身中的参数顺序并不一致。让我们以 escrow_transfer_operation 为例,该操作的结构(通常在操作之前有描述它的注释):
/**
 *  The purpose of this operation is to enable someone to send money contingently to
 *  another individual. The funds leave the *from* account and go into a temporary balance
 *  where they are held until *from* releases it to *to* or *to* refunds it to *from*.
 *
 *  In the event of a dispute the *agent* can divide the funds between the to/from account.
 *  Disputes can be raised any time before or on the dispute deadline time, after the escrow
 *  has been approved by all parties.
 *
 *  This operation only creates a proposed escrow transfer. Both the *agent* and *to* must
 *  agree to the terms of the arrangement by approving the escrow.
 *
 *  The escrow agent is paid the fee on approval of all parties. It is up to the escrow agent
 *  to determine the fee.
 *
 *  Escrow transactions are uniquely identified by 'from' and 'escrow_id', the 'escrow_id' is defined
 *  by the sender.
 */
struct escrow_transfer_operation : public base_operation {
    account_name_type from;
    account_name_type to;
    account_name_type agent;
    uint32_t escrow_id = 30;
    asset token_amount = asset(0, TOKEN_SYMBOL);
    asset fee;
    time_point_sec ratification_deadline;
    time_point_sec escrow_expiration;
    string json_metadata;
    void validate() const;
    void get_required_active_authorities(flat_set<account_name_type> &a) const {
        a.insert(from);
    }
};操作中参数的顺序已在文件末尾通过以下方法定义:
FC_REFLECT((graphene::protocol::escrow_transfer_operation), (from)(to)(token_amount)(escrow_id)(agent)(fee)(json_metadata)(ratification_deadline)(escrow_expiration));除了操作结构的描述外,validate 方法中还有参数处理逻辑,可以在 chain_operations.cpp 文件 中找到:
void escrow_transfer_operation::validate() const {
    validate_account_name(from);
    validate_account_name(to);
    validate_account_name(agent);
    FC_ASSERT(fee.amount >= 0, "fee cannot be negative");
    FC_ASSERT(token_amount.amount >=
              0, "tokens amount cannot be negative");
    FC_ASSERT(from != agent &&
              to != agent, "agent must be a third party");
    FC_ASSERT(fee.symbol == TOKEN_SYMBOL, "fee must be TOKEN_SYMBOL");
    FC_ASSERT(token_amount.symbol ==
              TOKEN_SYMBOL, "amount must be TOKEN_SYMBOL");
    FC_ASSERT(ratification_deadline <
              escrow_expiration, "ratification deadline must be before escrow expiration");
    validate_json_metadata(json_metadata);
}大多数操作会检查相应权限的签名,例如,在 escrow_transfer_operation 结构中,get_required_active_authorities 方法会检查操作发起者(from 字段)的活跃权限签名。