您正在查看: EOS-新手教程 分类下的文章

ScatterDesktop麒麟网络及账号配置

之前我们演示了scatter 扩展版本,添加测试网络的演示 (《Scatter的使用及添加测试网络相关设置》)
目前scatter官方已经明确废弃了扩展版本,主推桌面版本ScatterDesktop

1.下载ScatterDesktop

请到https://github.com/GetScatter/ScatterDesktop/releases 下载最新版本

2.安装

我们将在mac系统演示,下载下来后,直接运行安装包,将scatter拖到Applications

3.创建scatter

输入密码点击,点击Create new Scatter。如果之前有备份,可以点击Import from Backup

3.1点击Accept

3.2选择一个目录,作为备份的保存位置

3.3进入主界面

3.4进入设置

3.5进入网络设置

3.6输入scatter密码(新建scatter时设置的密码)

3.7新建网络

3.8输入麒麟网某节点的配置
host: api.kylin.eosbeijing.one
port: 8880
chainId: 5fff1dae8dc8e2fc4d5b23b2c7665c97f9e9d8edf2b6485a86ba311c25639191


点击保存。

4添加账号

如果没有麒麟网的账号的话,可以先去注册下,并申请测试代币
访问: https://tools.cryptokylin.io/#/tools/create

4.1进入Vault

4.2点击新建New

4.3点击导入

4.4导入麒麟网账号的私钥

4.5粘贴私钥后,进入账号检测,稍等片刻,即可显示,并自动导入,当前私钥下的所有账号。


至此ScatterDesktop的下载,安装,添加测试网络,以及账号,已经演示完毕。

其他帮助

清除ScatterDesktop本地数据

终端运行

find ~ -iname  "scatter*"

查找本地scatter目录,一般是在/Users/你的当前机器登陆的账户/Library/Application\ Support/scatter/
清空目录

rm -rf /Users/你的当前机器登陆的账户/Library/Application\ Support/scatter/

从零开始,纯净机器上部署EOS测试网 (版本v1.4.1)

系统准备

演示的系统为 Ubuntu 18.04 LTS,内存8g以上,硬盘300g+

clone EOS代码

我们以EOS-Mainnet仓库部署,(EOS-Mainnet是部署在目前EOS主网的版本,等于再次在EOSIO仓库测试过的版本,更稳定。)

开始clone 代码

//如果没有安装git,先安装
$sudo apt install git
$git clone https://github.com/EOS-Mainnet/eos.git

切换到最新分支(当前的EOS最新版本为 v1.4.1)

$git fetch
$git checkout -b mainnet-1.4.1

编译代码

$git submodule update --init --recursive
$./eosio_build.sh -s EOS

漫长等待,编译成功

执行安装

$sudo ./eosio_install.sh

安装成功后查看版本

$ nodeos -v
mainnet-1.4.1

各个节点,分别创建钱包和密钥,准备用户名

创建钱包

cleos wallet create --to-console

记得保存返回的钱包解锁密码,丢失无法找回钱包内数据

创建密钥

cleos wallet create_key

创建的密钥会自动添加到钱包,如需查看,可执行

cleos wallet private_keys

输入钱包解锁密码查看

准备用户名

1-5a-z 字符12位,准备好,后面config配置中producer-name用到。

各个节点机器,分别创建目录

选定一个存储位置,用于存储EOS节点配置和节点数据。选定的磁盘存储一般要在300G以上,防止后期不够。
假设我们选定的磁盘位置为/data
然后在当前目录下,新建两个目录

  • eos-tools 用于存放eos节点的配置
  • data 用于存放eos节点数据

创世节点配置

进入eos-tools文件夹

创建genesis.json文件

直接生成genesis.json文件

nodeos --extract-genesis-json genesis.json

创建config.ini文件

创建config.ini文件,并输入以下信息

//修改本地节点rpc 访问端口,默认为8888,修改后,本地访问方式为`cleos -u http://127.0.0.1:8000 get info`
http-server-address = 0.0.0.0:8000
//bp节点间的访问地址,当前节点的p2p-peer-address为,服务器IP或者域名,加上下面设置的端口
p2p-listen-endpoint = 0.0.0.0:8001

#设置一个自己的bp名字
agent-name = "EOS Shen Si"
//创世节点名字必须为eosio
producer-name = eosio
#创建一对密钥 创世节点不能修改私钥
signature-provider = EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV=KEY:5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
#创世节点才需要,其他bp不需要开启
enable-stale-production = true
#添加一些稳定的其他bp节点
# p2p-peer-address =

#以下为通用设置
wasm-runtime = wabt
pause-on-startup = false
abi-serializer-max-time-ms = 3000
chain-state-db-size-mb = 65535
reversible-blocks-db-size-mb = 2048
contracts-console = false
p2p-max-nodes-per-host = 1
allowed-connection = any
max-clients = 100
network-version-match = 1
sync-fetch-span = 500

#filter-on = *
connection-cleanup-period = 30
max-implicit-request = 1500
http-validate-host = false
access-control-allow-origin = *
access-control-allow-headers = *
access-control-allow-credentials = false
verbose-http-errors = true

#添加插件
plugin = eosio::chain_api_plugin
plugin = eosio::history_plugin
plugin = eosio::history_api_plugin
plugin = eosio::producer_plugin

启动节点

$EOS_TOOLS_DIR为eos-tools的当前目录,本例中路径为/data/eos-tools
$EOS_DATA_DIR为eos data的当前目录,本例中路径为/data/data

  • 第一次启动节点,(仅第一次运行使用)
    nodeos --genesis-json $EOS_TOOLS_DIR/genesis.json --max-irreversible-block-age 108000000 --data-dir $EOS_DATA_DIR --config-dir $EOS_TOOLS_DIR --delete-all-blocks

如果此时是创世节点,此时已开始出块。

创世节点配置

导入创世账户eosio的私钥5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3到钱包

cleos wallet import

创建系统账户

先创建10对密钥,用于创建前期10个系统的内置账户

cleos wallet create_key

执行10次

账户名 公钥 私钥
eosio.token EOS5rNiix2ENyUcvGo5pCr7yUukkhCUHySYfcMpFGNH18XERuf9t5
eosio.bpay EOS7YEYexP3yQtddnQHJGV4CDLUF58RkFTDNUZR5MPmD9jx1xFH8f
eosio.msig EOS5SoN8xuQBCA5vBcRZXiVYA9xjkShaVN3UpsMngv1X2dhHojAS4
eosio.names EOS6Q44xh1p35ZkVkfSAguWLPJiqJVCi2B4AuGsxVwchKrKHKkf9T
eosio.ram EOS81nArYKAitRN1h2GUCCNZWS6zArKzrg1JvcgC5zyhFAumQRgrR
eosio.ramfee EOS8HhotHdovvtC1RR5G7bPRgLaHXkFbMGbSfwKKrtkQspy1rapy3
eosio.saving EOS85QMTp1Xm71jCAYYvVhm3dxmxZuobUEHxe1wLNb8qL52kmrfwX
eosio.stake EOS5USZ8g5p3fU965ByvhL8AgfXfWUbeDHXjySe2QiG1wcz5So9KR
eosio.vpay EOS8Qk7vEPme8huxpqa4mS9jfuDriQrpM6MJjFDvcvJfiPaKfypR6
eosio.sudo EOS8Qk7vEPme8huxpqa4mS9jfuDriQrpM6MJjFDvcvJfiPaKfypR6

部署系统合约

进入eos/build/contracts

部署eosio.bios
cleos -u http://127.0.0.1:8000 set contract eosio eosio.bios/ -p eosio
创建账户
cleos -u http://127.0.0.1:8000 create account eosio eosio.token EOS5rNiix2ENyUcvGo5pCr7yUukkhCUHySYfcMpFGNH18XERuf9t5 EOS5rNiix2ENyUcvGo5pCr7yUukkhCUHySYfcMpFGNH18XERuf9t5
cleos -u http://127.0.0.1:8000 create account eosio eosio.bpay EOS7YEYexP3yQtddnQHJGV4CDLUF58RkFTDNUZR5MPmD9jx1xFH8f EOS7YEYexP3yQtddnQHJGV4CDLUF58RkFTDNUZR5MPmD9jx1xFH8f
cleos -u http://127.0.0.1:8000 create account eosio eosio.msig EOS5SoN8xuQBCA5vBcRZXiVYA9xjkShaVN3UpsMngv1X2dhHojAS4 EOS5SoN8xuQBCA5vBcRZXiVYA9xjkShaVN3UpsMngv1X2dhHojAS4
cleos -u http://127.0.0.1:8000 create account eosio eosio.names EOS6Q44xh1p35ZkVkfSAguWLPJiqJVCi2B4AuGsxVwchKrKHKkf9T EOS6Q44xh1p35ZkVkfSAguWLPJiqJVCi2B4AuGsxVwchKrKHKkf9T
cleos -u http://127.0.0.1:8000 create account eosio eosio.ram EOS81nArYKAitRN1h2GUCCNZWS6zArKzrg1JvcgC5zyhFAumQRgrR EOS81nArYKAitRN1h2GUCCNZWS6zArKzrg1JvcgC5zyhFAumQRgrR
cleos -u http://127.0.0.1:8000 create account eosio eosio.ramfee EOS8HhotHdovvtC1RR5G7bPRgLaHXkFbMGbSfwKKrtkQspy1rapy3 EOS8HhotHdovvtC1RR5G7bPRgLaHXkFbMGbSfwKKrtkQspy1rapy3
cleos -u http://127.0.0.1:8000 create account eosio eosio.saving EOS85QMTp1Xm71jCAYYvVhm3dxmxZuobUEHxe1wLNb8qL52kmrfwX EOS85QMTp1Xm71jCAYYvVhm3dxmxZuobUEHxe1wLNb8qL52kmrfwX
cleos -u http://127.0.0.1:8000 create account eosio eosio.stake EOS5USZ8g5p3fU965ByvhL8AgfXfWUbeDHXjySe2QiG1wcz5So9KR EOS5USZ8g5p3fU965ByvhL8AgfXfWUbeDHXjySe2QiG1wcz5So9KR
cleos -u http://127.0.0.1:8000 create account eosio eosio.upay EOS8Qk7vEPme8huxpqa4mS9jfuDriQrpM6MJjFDvcvJfiPaKfypR6 EOS8Qk7vEPme8huxpqa4mS9jfuDriQrpM6MJjFDvcvJfiPaKfypR6
cleos -u http://127.0.0.1:8000 create account eosio eosio.sudo EOS8Qk7vEPme8huxpqa4mS9jfuDriQrpM6MJjFDvcvJfiPaKfypR6 EOS8Qk7vEPme8huxpqa4mS9jfuDriQrpM6MJjFDvcvJfiPaKfypR6
部署 eosio.token
cleos -u http://127.0.0.1:8000 set contract eosio.token eosio.token
创建代币
cleos -u http://127.0.0.1:8000 push action eosio.token create '["eosio","10000000000.0000 EOS",0,0,0]' -p eosio.token
发布代币
cleos -u http://127.0.0.1:8000 push action eosio.token issue '["eosio","1000000000.0000 EOS","issue"]' -p eosio
issue 中的发行量(supply),要比上边创建的最大量(max_supply),要少一个0
查询账户余额
cleos -u http://127.0.0.1:8000 get currency balance eosio.token eosio

应返回10000000000.0000 EOS

部署 eosio.msig
cleos -u http://127.0.0.1:8000 set contract eosio eosio.msig -p eosio
cleos -u http://127.0.0.1:8000 push action eosio setpriv '{"account": "eosio.msig", "is_priv": 1}' -p eosio
部署eosio.system
cleos -u http://127.0.0.1:8000 set contract eosio eosio.system -p eosio
部署 eosio.sudo
cleos -u http://127.0.0.1:8000 set contract eosio eosio.sudo -p eosio
cleos -u http://127.0.0.1:8000 push action eosio setpriv '{"account": "eosio.sudo", "is_priv": 1}' -p eosio
创建其他三个bp的账户,(账户名和公钥由各自节点自己提供)
cleos system newaccount eosio eosio.bp1 EOS7mcXeVUSLCZxEf8eBPm2MbmiswNovoB6rk71pPqaZoxwSAeK7b EOS7mcXeVUSLCZxEf8eBPm2MbmiswNovoB6rk71pPqaZoxwSAeK7b  --stake-net '50.00 EOS' --stake-cpu '50.00 EOS'  --buy-ram-kbytes 10000
cleos system newaccount eosio eosio.bp2 EOS6wLrhcEQDZJvfZSCgVkFzsKcv34XTsTTjXXQZajWaBbEhvLPMg  EOS6wLrhcEQDZJvfZSCgVkFzsKcv34XTsTTjXXQZajWaBbEhvLPMg   --stake-net '50.00 EOS' --stake-cpu '50.00 EOS'  --buy-ram-kbytes 10000
cleos system newaccount eosio eosio.bp3 EOS6u8i7rknHyAd7AdziWSnEsAbyJHA3U9So4R8p2z58cA1KeEDv2  EOS6u8i7rknHyAd7AdziWSnEsAbyJHA3U9So4R8p2z58cA1KeEDv2   --stake-net '50.00 EOS' --stake-cpu '50.00 EOS'  --buy-ram-kbytes 10000
查看以创建的用户
cleos -u http://127.0.0.1:8000 get account eosio.bp1
cleos -u http://127.0.0.1:8000 get account eosio.bp2
cleos -u http://127.0.0.1:8000 get account eosio.bp3

确认创建ok

其他bp加入

进入eos-tools文件夹

创建genesis.json文件

将创世节点的genesis.json文件,复制到此目录

创建config.ini文件

将创世节点的config.ini复制到此目录,并修改以下信息

agent-name = "节点的名称,用于信息展示"
producer-name = 节点的账户名
signature-provider = 节点的公钥+私钥,(此配置妥善保存,别手误上传)
enable-stale-production = false
p2p-peer-address = 添加其他的节点地址

启动从节点

$EOS_TOOLS_DIR为eos-tools的当前目录,本例中路径为/data/eos-tools
$EOS_DATA_DIR为eos data的当前目录,本例中路径为/data/data

  • 第一次启动节点,(仅第一次运行使用)
    nodeos --genesis-json $EOS_TOOLS_DIR/genesis.json --max-irreversible-block-age 108000000 --data-dir $EOS_DATA_DIR --config-dir $EOS_TOOLS_DIR --delete-all-blocks

    此时从节点已经接收来自eosio创世节点的数据。

开始投票,从普通节点成为出块节点

给从节点分别转代币

创世节点,先把大于15%主网总发行量,分成当前启动主网节点数份数,目前为了好记,直接一人转100000000.0000 EOS

cleos -u http://127.0.0.1:8000 push action eosio.token transfer '["eosio", "eosio.bp1","100000000.0000 EOS","vote"]' -p eosio
cleos -u http://127.0.0.1:8000 push action eosio.token transfer '["eosio", "eosio.bp2","100000000.0000 EOS","vote"]' -p eosio
cleos -u http://127.0.0.1:8000 push action eosio.token transfer '["eosio", "eosio.bp3","100000000.0000 EOS","vote"]' -p eosio

各个从节点,分别抵押自己的代币

cleos -u http://127.0.0.1:8000 system delegatebw  eosio.bp* eosio.bp* '25000000.0000 EOS' '25000000.0000 EOS'

各个节点分别提交注册出块节点申请

cleos -u http://127.0.0.1:8000 system regproducer eosio.bp*  节点公钥  节点的网址

各个节点,开始投票

cleos -u http://127.0.0.1:8000 system voteproducer prods eosio.bp* eosio.bp*

查看出块节点

cleos -u http://127.0.0.1:8000  get schedule

此时 eosio.bp1-3,三个节点应该都已经在出块了。

eosio等系统账号,将权限交给社区

eosio.bpay
cleos push action eosio updateauth '{"account": "'eosio.bpay'", "permission": "active", "parent": "owner", "auth":{"threshold": 1, "keys": [], "waits": [], "accounts": [{"weight": 1, "permission": {"actor": "eosio", "permission": active}}]}}' -p eosio.bpay@active
cleos push action eosio updateauth '{"account": "'eosio.bpay'", "permission": "owner", "parent": "",       "auth":{"threshold": 1, "keys": [], "waits": [], "accounts": [{"weight": 1, "permission": {"actor": "eosio", "permission": active}}]}}' -p eosio.bpay@owner

其他账户eosio.msig eosio.names eosio.ram eosio.ramfee eosio.saving eosio.stake eosio.token eosio.vpay eosio.sudo同上

eosio
cleos push action eosio updateauth '{"account": "eosio", "permission": "active", "parent": "owner", "auth":{"threshold": 1, "keys": [], "waits": [], "accounts": [{"weight": 1, "permission": {"actor": "eosio.prods", "permission": active}}]}}' -p eosio@active
cleos push action eosio updateauth '{"account": "eosio", "permission": "owner", "parent": "",       "auth":{"threshold": 1, "keys": [], "waits": [], "accounts": [{"weight": 1, "permission": {"actor": "eosio.prods", "permission": active}}]}}' -p eosio@owner

其他命令

  • 继续同步数据 (常规退出后,继续执行)
    nodeos --max-transaction-time 1000 --max-irreversible-block-age 108000000 --data-dir $EOS_DATA_DIR --config-dir $EOS_TOOLS_DIR
  • 重建数据,重新接收
    nodeos --hard-replay-blockchain --wasm-runtime wavm --max-irreversible-block-age 108000000 --data-dir $EOS_DATA_DIR --config-dir $EOS_TOOLS_DIR

    其他设置

  • 中文支持:sudo apt-get update && sudo apt-get -y install language-pack-zh-hans
  • 时间同步,sudo apt-get install chrony
    修改ulimit限制
    • ulimit -s 64000
    • sudo vi /etc/security/limits.conf
*               soft    stack            64000
root          soft    stack            64000

[eosiocpp] account_name 与 std:string 互相转换

  • account_name to std:string
auto username = name{from};
std::string from_name = username.to_string();
  • std::string to account_name
account_name from_name = string_to_name(username);

eos系统合约介绍 — 提案合约eosio.msig (下)

简介

本篇将为大家介绍eosio.msig的源码实现,合约代码库详见:eosio.msig。eosio.msig主要有propose、approve、unapprove、cancel、exec、invalidate这几种方法,下面会详细逐一介绍每种方法的功能和实现细节。

主要合约方法

eosio.msig合约,在eosio.msig.hpp头文件中,主要定义了以下六个合约方法:

  • propose:提出提案
  • approve:通过提案
  • unapprove:不通过提案
  • cancel:取消提案
  • exec:执行提案
  • invalidate:撤回对之前所有该账户通过、但未被最终执行的提案的通过授权

eosio.msig合约头文件

namespace eosio {

   class multisig : public contract {
      public:
         multisig( account_name self ):contract(self){}

         void propose();
         void approve( account_name proposer, name proposal_name, permission_level level );
         void unapprove( account_name proposer, name proposal_name, permission_level level );
         void cancel( account_name proposer, name proposal_name, account_name canceler );
         void exec( account_name proposer, name proposal_name, account_name executer );
         void invalidate( account_name account );

propose方法

propose方法主要功能是提出提案,对应上篇提到的 cleos multisig propose 命令,传参如下:

  • proposer:提案账户
  • proposal_name:提案名
  • requested:提案通过所需权限
  • trx:提案具体执行的交易内容

为了节省资源开销,propose方法并不会根据 cleos multisig propose 传入的参数一一做解析,而是直接解析input data

/*
propose function manually parses input data (instead of taking parsed arguments from dispatcher)
because parsing data in the dispatcher uses too much CPU in case if proposed transaction is big

If we use dispatcher the function signature should be:

void multisig::propose( account_name proposer,
                        name proposal_name,
                        vector<permission_level> requested,
                        transaction  trx)
*/

void multisig::propose() {
   constexpr size_t max_stack_buffer_size = 512;
   size_t size = action_data_size();
   char* buffer = (char*)( max_stack_buffer_size < size ? malloc(size) : alloca(size) );
   read_action_data( buffer, size );

之后,会做一系列前置检验工作:proposer提案账户授权是否正确、交易是否超时、propose_name是否存在、提案通过所需权限是否正确等

require_auth( proposer );
   eosio_assert( trx_header.expiration >= eosio::time_point_sec(current_time_point()), "transaction expired" );
   //eosio_assert( trx_header.actions.size() > 0, "transaction must have at least one action" );

   proposals proptable( _self, proposer );
   eosio_assert( proptable.find( proposal_name ) == proptable.end(), "proposal with the same name exists" );

   bytes packed_requested = pack(requested);
   auto res = ::check_transaction_authorization( buffer+trx_pos, size-trx_pos,
                                                 (const char*)0, 0,
                                                 packed_requested.data(), packed_requested.size()
                                               );
   eosio_assert( res > 0, "transaction authorization failed" );

之后,将提案和提案合约的内容存表,将提案通过所需的权限存入requested_approvals表中,我们上篇文章所提到 cleos get table eosio.msig <proposer account> approvals 命令查询的就是这张表

proptable.emplace( proposer, [&]( auto& prop ) {
      prop.proposal_name       = proposal_name;
      prop.packed_transaction  = bytes( buffer+trx_pos, buffer+size );
   });

   approvals apptable(  _self, proposer );
   apptable.emplace( proposer, [&]( auto& a ) {
      a.proposal_name       = proposal_name;
      a.requested_approvals.reserve( requested.size() );
      for ( auto& level : requested ) {
         a.requested_approvals.push_back( approval{ level, time_point{ microseconds{0} } } );
      }
   });

approve方法

approve方法的主要功能是通过提案,对应上篇提到的 cleos multisig approve 命令,传参如下

  • proposer:提案人
  • proposal_name:提案名
  • permissions:使用哪个权限批准这个提案

首先,系统会查找提案合约内容,查找 requested_approvals 表中需要通过的权限中,是否有和传入permission匹配项。若有匹配项,将此权限加入 provided_approvals 表,即表示该权限通过此提案,并从 requested_approvals 表中移除该权限。

void multisig::approve( account_name proposer, name proposal_name, permission_level level ) {
   require_auth( level );

   approvals apptable(  _self, proposer );
   auto apps_it = apptable.find( proposal_name );
   if ( apps_it != apptable.end() ) {
      auto itr = std::find_if( apps_it->requested_approvals.begin(), apps_it->requested_approvals.end(), [&](const approval& a) { return a.level == level; } );
      eosio_assert( itr != apps_it->requested_approvals.end(), "approval is not on the list of requested approvals" );

      apptable.modify( apps_it, proposer, [&]( auto& a ) {
            a.provided_approvals.push_back( approval{ level, current_time_point() } );
            a.requested_approvals.erase( itr );
         });
   } else {
      old_approvals old_apptable(  _self, proposer );
      auto& apps = old_apptable.get( proposal_name, "proposal not found" );

      auto itr = std::find( apps.requested_approvals.begin(), apps.requested_approvals.end(), level );
      eosio_assert( itr != apps.requested_approvals.end(), "approval is not on the list of requested approvals" );

      old_apptable.modify( apps, proposer, [&]( auto& a ) {
            a.provided_approvals.push_back( level );
            a.requested_approvals.erase( itr );
         });
   }
}

unapprove方法

unapprove方法的主要功能是不通过提案,对应上篇提到的 cleos multisig unapprove 命令,传参如下:

  • proposer:提案人
  • proposal_name:提案名
  • permissions:使用哪个权限拒绝这个提案

首先,系统会查找提案合约内容,查找 provided_approvals 表中通过的权限中,是否有和传入permission匹配项。若有匹配项,将此权限加入requested_approvals 表,即表示该权限还没通过此提案,并从 provided_approvals 表中移除该权限。

void multisig::unapprove( account_name proposer, name proposal_name, permission_level level ) {
   require_auth( level );

   approvals apptable(  _self, proposer );
   auto apps_it = apptable.find( proposal_name );
   if ( apps_it != apptable.end() ) {
      auto itr = std::find_if( apps_it->provided_approvals.begin(), apps_it->provided_approvals.end(), [&](const approval& a) { return a.level == level; } );
      eosio_assert( itr != apps_it->provided_approvals.end(), "no approval previously granted" );
      apptable.modify( apps_it, proposer, [&]( auto& a ) {
            a.requested_approvals.push_back( approval{ level, current_time_point() } );
            a.provided_approvals.erase( itr );
         });
   } else {
      old_approvals old_apptable(  _self, proposer );
      auto& apps = old_apptable.get( proposal_name, "proposal not found" );
      auto itr = std::find( apps.provided_approvals.begin(), apps.provided_approvals.end(), level );
      eosio_assert( itr != apps.provided_approvals.end(), "no approval previously granted" );
      old_apptable.modify( apps, proposer, [&]( auto& a ) {
            a.requested_approvals.push_back( level );
            a.provided_approvals.erase( itr );
         });
   }
}

cancel方法

cancel方法的主要功能是取消提案,对应上篇提到的 cleos multisig cancel 命令,传参如下:

  • proposer:提案账户
  • proposal_name:提案名
  • canceler:取消账户

首先,先查找表获取提案内容。如果canceler账户和提案账户不同,则在提案交易过期之前,canceler都不能取消提案。若能取消,将提案从表中移除。

void multisig::cancel( account_name proposer, name proposal_name, account_name canceler ) {
   require_auth( canceler );

   proposals proptable( _self, proposer );
   auto& prop = proptable.get( proposal_name, "proposal not found" );

   if( canceler != proposer ) {
      eosio_assert( unpack<transaction_header>( prop.packed_transaction ).expiration < eosio::time_point_sec(current_time_point()), "cannot cancel until expiration" );
   }
   proptable.erase(prop);

   //remove from new table
   approvals apptable(  _self, proposer );
   auto apps_it = apptable.find( proposal_name );
   if ( apps_it != apptable.end() ) {
      apptable.erase(apps_it);
   } else {
      old_approvals old_apptable(  _self, proposer );
      auto apps_it = old_apptable.find( proposal_name );
      eosio_assert( apps_it != old_apptable.end(), "proposal not found" );
      old_apptable.erase(apps_it);
   }
}

exec方法

exec方法的主要功能是执行提案,对应上篇提到的 cleos multisig exec 命令,传参如下:

  • proposer:提案账户
  • proposal_name:提案名
  • executer:执行账户
void multisig::exec( account_name proposer, name proposal_name, account_name executer ) {
   require_auth( executer );

   proposals proptable( _self, proposer );
   auto& prop = proptable.get( proposal_name, "proposal not found" );
   transaction_header trx_header;
   datastream<const char*> ds( prop.packed_transaction.data(), prop.packed_transaction.size() );
   ds >> trx_header;
   //首先,需要做前置检查,检查交易是否过期
   eosio_assert( trx_header.expiration >= eosio::time_point_sec(current_time_point()), "transaction expired" );

   approvals apptable(  _self, proposer );
   auto apps_it = apptable.find( proposal_name );
   vector<permission_level> approvals;
   //然后,查 provided_approvals 表获取通过提案交易的权限们,对比 inv_table 表,如果权限不在 inv_table 表中或者 last_invalidation_time 
   //已经小于当前时间,代表权限有效,放入approvals表中。inv_table 表的用途在下一个invalidate方法中介绍
   invalidations inv_table( _self, _self );
   if ( apps_it != apptable.end() ) {
      approvals.reserve( apps_it->provided_approvals.size() );
      for ( auto& p : apps_it->provided_approvals ) {
         auto it = inv_table.find( p.level.actor );
         if ( it == inv_table.end() || it->last_invalidation_time < p.time ) {
            approvals.push_back(p.level);
         }
      }
      apptable.erase(apps_it);
   } else {
      old_approvals old_apptable(  _self, proposer );
      auto& apps = old_apptable.get( proposal_name, "proposal not found" );
      for ( auto& level : apps.provided_approvals ) {
         auto it = inv_table.find( level.actor );
         if ( it == inv_table.end() ) {
            approvals.push_back( level );
         }
      }
      old_apptable.erase(apps);
   }
   //最后,执行提案。如果交易执行权限检验无误,会发起一个defer延迟合约,去执行提案交易。如果执行成功,
   //`cleos get actions <executer account>` 会产生两条actions,一条是exec的交易,一条是提案执行的交易。
   bytes packed_provided_approvals = pack(approvals);
   auto res = ::check_transaction_authorization( prop.packed_transaction.data(), prop.packed_transaction.size(),
                                                 (const char*)0, 0,
                                                 packed_provided_approvals.data(), packed_provided_approvals.size()
                                                 );
   eosio_assert( res > 0, "transaction authorization failed" );

   send_deferred( (uint128_t(proposer) << 64) | proposal_name, executer, prop.packed_transaction.data(), prop.packed_transaction.size() );

   proptable.erase(prop);
}

invalidate方法

invalidate方法的主要功能是:如果account之前通过的提案还未执行,就可以使用该方法将提案一键设置为无效。这个方法主要是解决:账户权限变更时,之前通过但未执行的提案一旦执行会盗取账户权限的问题,详见issue。该方法传参如下:

  • account:提案的批准账户

该功能的实现非常简单,首先,inv_table 是用来存放权限的,它的两个字段 account 和last_invalidation_time 分别是账户名和账户权限最近失效时间。last_invalidation_time 时间之前,account的提案批准权限都不可用,在该时间之后account的提案批准权限才能生效。

因此,如果想使account之前审批通过的所有提案都失效的话,就将 last_invalidation_time 设置为当前时间即可。exec方法在执行之前会检查 inv_table,则包含在 inv_table 中的account,即便批准了该提案,该批准也会作废

void multisig::invalidate( account_name account ) {
   require_auth( account );
   invalidations inv_table( _self, _self );
   auto it = inv_table.find( account );
   if ( it == inv_table.end() ) {
      inv_table.emplace( account, [&](auto& i) {
            i.account = account;
            i.last_invalidation_time = current_time_point();
         });
   } else {
      inv_table.modify( it, account, [&](auto& i) {
            i.last_invalidation_time = current_time_point();
         });
   }
}

转载自简书

eos系统合约介绍 — 提案合约eosio.msig (上)

简介

本篇将为大家介绍eos另一个系统合约eos.msig的主要功能和源码实现细节。eos.msig是eos的提案合约,同样也是cleos multisig命令调用的系统合约,可用于提案、通过/不通提案、执行多重签名交易等功能。由于涉及内容较多,介绍将分为上下两篇,上篇将围绕eos账户权限、cleos multisig命令的使用这几个方面进行介绍,下篇则会为大家介绍eosio.msig的源码实现

Eos账户权限

回顾一下之前的文章,一个账户最基本的权限owner和active是由公私钥对控制的,然而,eos丰富的权限控制方式,还允许我们将一个账户的权限,下放给其他的账户。比如下面的经典例子:
账户popo的权限情况

permissio account weight threshold
owner 2
@user1 1
@user2 1
active 1
@user1 1
@user2 1

我们将一步一步,通过命令行操作的方式来为大家介绍eos权限的应用。

1.我们分别创建三个账户popo11111111、zoin11111111、zoin22222222,每个账户使用不同的公私钥对。
分别创建6对公私钥,分别用于三个账号的owner和active权限

cleos create key --to-console

| 账号 | 权限|公钥 |私钥 |
| ------------ | ------------ | ------------ |
| popo11111111 | owner | EOS81WjiHefR6c5VKYjvdSat68RDJ3qGPgCckJqG3pXbDbVjZR5QB | 5JAxr3bM6DwwtcKJ7aVydpoaSA2oyiZ94PjqTpW6uXDZ41VuK22|
| popo11111111 | active | EOS7ob62mdBfiRt1a8tQrQGbhnU1gEAvWyQDFHs99HzYEh8wxeQ9T |5KRCeqsHAx1UNTtz7BH5N4SSMe75XMCj3j93TqQRDrYCwbjccoP |
| zoin11111111 | owner | EOS5N3qMPXjtwZpMyPeiYXSmBQbr7Lt5Ro2W8pLqyAzFYQPknoUvZ |5KWYcdZcJ1E8mWtg1Qoq5s1fYXJxMQkLupozfZEUEiu6vn6QNzx |
| zoin11111111 | active | EOS6BAfK8mZZjMaa74zxkT3evWEtWo4AnRQYmnpGWkNJYKVjcrpao | 5Jwy6VbCwpkmczjYgMNVtr9Nd77aenZcPE8DtvPwtYqztbgMzu4|
| zoin22222222 | owner | EOS6hBy5yioHpcH4SxSe91aH8vCGAhhEifKtp1jZEy7PS2rhJFSER |5Hz3u44Avpf4NCVDQdwZGSVXpbpEdRFczo2Wm9FC14pqcx8mUwM |
| zoin22222222 | active | EOS5TeY2C9bdewrbS58SZnrp5azF5Cq7AXraNJihxaW4hbGeWSaeE |5JFoGdAc6zPG9KdHCHPHFL5XbpEnFmDq61y53Bw3Hm3VuFy7Stk |

2.下面开始创建三个账号

cleos system newaccount 已有账号 将要创建的账号 ownerkey  activekey  --stake-net '0.1 EOS' --stake-cpu '0.1 EOS'  --buy-ram-kbytes 4000

利用我们上面文章《以传统的WEB开发方式,来举例理解Dapp开发》中已经在麒麟测试网创建的账号cryptokylinq,并在麒麟测试网来创建其他账号。

suroudeMacBook-Pro:~ surou$ cleos -u http://kylin.fn.eosbixin.com system newaccount cryptokylinq popo11111111 EOS81WjiHefR6c5VKYjvdSat68RDJ3qGPgCckJqG3pXbDbVjZR5QB  EOS7ob62mdBfiRt1a8tQrQGbhnU1gEAvWyQDFHs99HzYEh8wxeQ9T  --stake-net '0.1 EOS' --stake-cpu '0.1 EOS'  --buy-ram-kbytes 4000
executed transaction: ada14edaa2dc356f02f8f19515f3227669887a5a840601d28f8d59238e1c7890  336 bytes  4284 us
#         eosio <= eosio::newaccount            {"creator":"cryptokylinq","name":"popo11111111","owner":{"threshold":1,"keys":[{"key":"EOS81WjiHefR6...
#         eosio <= eosio::buyrambytes           {"payer":"cryptokylinq","receiver":"popo11111111","bytes":4096000}
#   eosio.token <= eosio.token::transfer        {"from":"cryptokylinq","to":"eosio.ram","quantity":"99.8497 EOS","memo":"buy ram"}
#  cryptokylinq <= eosio.token::transfer        {"from":"cryptokylinq","to":"eosio.ram","quantity":"99.8497 EOS","memo":"buy ram"}
#     eosio.ram <= eosio.token::transfer        {"from":"cryptokylinq","to":"eosio.ram","quantity":"99.8497 EOS","memo":"buy ram"}
#   eosio.token <= eosio.token::transfer        {"from":"cryptokylinq","to":"eosio.ramfee","quantity":"0.5018 EOS","memo":"ram fee"}
#  cryptokylinq <= eosio.token::transfer        {"from":"cryptokylinq","to":"eosio.ramfee","quantity":"0.5018 EOS","memo":"ram fee"}
#  eosio.ramfee <= eosio.token::transfer        {"from":"cryptokylinq","to":"eosio.ramfee","quantity":"0.5018 EOS","memo":"ram fee"}
#         eosio <= eosio::delegatebw            {"from":"cryptokylinq","receiver":"popo11111111","stake_net_quantity":"0.1000 EOS","stake_cpu_quanti...
#   eosio.token <= eosio.token::transfer        {"from":"cryptokylinq","to":"eosio.stake","quantity":"0.2000 EOS","memo":"stake bandwidth"}
#  cryptokylinq <= eosio.token::transfer        {"from":"cryptokylinq","to":"eosio.stake","quantity":"0.2000 EOS","memo":"stake bandwidth"}
#   eosio.stake <= eosio.token::transfer        {"from":"cryptokylinq","to":"eosio.stake","quantity":"0.2000 EOS","memo":"stake bandwidth"}
2018-09-25T04:07:44.999 thread-0   main.cpp:458                  print_result   warning: transaction executed locally, but may not be confirmed by the network yet
suroudeMacBook-Pro:~ surou$ cleos -u http://kylin.fn.eosbixin.com system newaccount cryptokylinq zoin11111111 EOS5N3qMPXjtwZpMyPeiYXSmBQbr7Lt5Ro2W8pLqyAzFYQPknoUvZ  EOS6BAfK8mZZjMaa74zxkT3evWEtWo4AnRQYmnpGWkNJYKVjcrpao  --stake-net '0.1 EOS' --stake-cpu '0.1 EOS'  --buy-ram-kbytes 4000
executed transaction: daf4b102f68d7161022c0ac8635d91137a0d2d24072d49fdad13a608e2e03072  336 bytes  5102 us
#         eosio <= eosio::newaccount            {"creator":"cryptokylinq","name":"zoin11111111","owner":{"threshold":1,"keys":[{"key":"EOS5N3qMPXjtw...
#         eosio <= eosio::buyrambytes           {"payer":"cryptokylinq","receiver":"zoin11111111","bytes":4096000}
#   eosio.token <= eosio.token::transfer        {"from":"cryptokylinq","to":"eosio.ram","quantity":"99.8589 EOS","memo":"buy ram"}
#  cryptokylinq <= eosio.token::transfer        {"from":"cryptokylinq","to":"eosio.ram","quantity":"99.8589 EOS","memo":"buy ram"}
#     eosio.ram <= eosio.token::transfer        {"from":"cryptokylinq","to":"eosio.ram","quantity":"99.8589 EOS","memo":"buy ram"}
#   eosio.token <= eosio.token::transfer        {"from":"cryptokylinq","to":"eosio.ramfee","quantity":"0.5019 EOS","memo":"ram fee"}
#  cryptokylinq <= eosio.token::transfer        {"from":"cryptokylinq","to":"eosio.ramfee","quantity":"0.5019 EOS","memo":"ram fee"}
#  eosio.ramfee <= eosio.token::transfer        {"from":"cryptokylinq","to":"eosio.ramfee","quantity":"0.5019 EOS","memo":"ram fee"}
#         eosio <= eosio::delegatebw            {"from":"cryptokylinq","receiver":"zoin11111111","stake_net_quantity":"0.1000 EOS","stake_cpu_quanti...
#   eosio.token <= eosio.token::transfer        {"from":"cryptokylinq","to":"eosio.stake","quantity":"0.2000 EOS","memo":"stake bandwidth"}
#  cryptokylinq <= eosio.token::transfer        {"from":"cryptokylinq","to":"eosio.stake","quantity":"0.2000 EOS","memo":"stake bandwidth"}
#   eosio.stake <= eosio.token::transfer        {"from":"cryptokylinq","to":"eosio.stake","quantity":"0.2000 EOS","memo":"stake bandwidth"}
2018-09-25T04:11:22.091 thread-0   main.cpp:458                  print_result   warning: transaction executed locally, but may not be confirmed by the network yet
suroudeMacBook-Pro:~ surou$ cleos -u http://kylin.fn.eosbixin.com system newaccount cryptokylinq zoin22222222 EOS6hBy5yioHpcH4SxSe91aH8vCGAhhEifKtp1jZEy7PS2rhJFSER  EOS5TeY2C9bdewrbS58SZnrp5azF5Cq7AXraNJihxaW4hbGeWSaeE  --stake-net '0.1 EOS' --stake-cpu '0.1 EOS'  --buy-ram-kbytes 4000
executed transaction: 7b184ccebaeb2cd2b6cece49ec46abd86b20a1d20b29722d21029dd1abc958f0  336 bytes  2667 us
#         eosio <= eosio::newaccount            {"creator":"cryptokylinq","name":"zoin22222222","owner":{"threshold":1,"keys":[{"key":"EOS6hBy5yioHp...
#         eosio <= eosio::buyrambytes           {"payer":"cryptokylinq","receiver":"zoin22222222","bytes":4096000}
#   eosio.token <= eosio.token::transfer        {"from":"cryptokylinq","to":"eosio.ram","quantity":"99.8723 EOS","memo":"buy ram"}
#  cryptokylinq <= eosio.token::transfer        {"from":"cryptokylinq","to":"eosio.ram","quantity":"99.8723 EOS","memo":"buy ram"}
#     eosio.ram <= eosio.token::transfer        {"from":"cryptokylinq","to":"eosio.ram","quantity":"99.8723 EOS","memo":"buy ram"}
#   eosio.token <= eosio.token::transfer        {"from":"cryptokylinq","to":"eosio.ramfee","quantity":"0.5019 EOS","memo":"ram fee"}
#  cryptokylinq <= eosio.token::transfer        {"from":"cryptokylinq","to":"eosio.ramfee","quantity":"0.5019 EOS","memo":"ram fee"}
#  eosio.ramfee <= eosio.token::transfer        {"from":"cryptokylinq","to":"eosio.ramfee","quantity":"0.5019 EOS","memo":"ram fee"}
#         eosio <= eosio::delegatebw            {"from":"cryptokylinq","receiver":"zoin22222222","stake_net_quantity":"0.1000 EOS","stake_cpu_quanti...
#   eosio.token <= eosio.token::transfer        {"from":"cryptokylinq","to":"eosio.stake","quantity":"0.2000 EOS","memo":"stake bandwidth"}
#  cryptokylinq <= eosio.token::transfer        {"from":"cryptokylinq","to":"eosio.stake","quantity":"0.2000 EOS","memo":"stake bandwidth"}
#   eosio.stake <= eosio.token::transfer        {"from":"cryptokylinq","to":"eosio.stake","quantity":"0.2000 EOS","memo":"stake bandwidth"}
2018-09-25T04:13:52.457 thread-0   main.cpp:458                  print_result   warning: transaction executed locally, but may not be confirmed by the network yet

3.将popo11111111的owner权限交出,由zoin11111111和zoin22222222的owner权限控制。每个权限的权重weight是1,而门槛threshold是2,即使用popo11111111的owner权限发送交易,需要zoin11111111和zoin22222222进行多重签名才行
先将popo11111111的owner私钥导入钱包

suroudeMacBook-Pro:~ surou$ cleos wallet import
private key: imported private key for: EOS81WjiHefR6c5VKYjvdSat68RDJ3qGPgCckJqG3pXbDbVjZR5QB

查看下popo11111111信息

suroudeMacBook-Pro:~ surou$ cleos -u http://kylin.fn.eosbixin.com get account popo11111111
permissions: 
     owner     1:    1 EOS81WjiHefR6c5VKYjvdSat68RDJ3qGPgCckJqG3pXbDbVjZR5QB
        active     1:    1 EOS7ob62mdBfiRt1a8tQrQGbhnU1gEAvWyQDFHs99HzYEh8wxeQ9T

将popo11111111的owner权限交出

suroudeMacBook-Pro:~ surou$ cleos -u http://kylin.fn.eosbixin.com set account permission popo11111111 owner '{"threshold":2, "keys":[{"key": "EOS81WjiHefR6c5VKYjvdSat68RDJ3qGPgCckJqG3pXbDbVjZR5QB"}], "accounts": [{"permission": {"actor": "zoin11111111", "permission": "owner"}, "weight": 1}, {"permission": {"actor": "zoin22222222", "permission": "owner"}, "weight": 1}]}' -p popo11111111@owner
executed transaction: 38317f76ad1eb90601ad9bd2c6720e322196aebbb8bed5afacfd4f7c5098b111  200 bytes  759 us
#         eosio <= eosio::updateauth            {"account":"popo11111111","permission":"owner","parent":"","auth":{"threshold":2,"keys":[{"key":"EOS...

再次查看popo11111111账户

suroudeMacBook-Pro:~ surou$ cleos -u http://kylin.fn.eosbixin.com get account popo11111111
permissions: 
     owner     2:    60450 EOS81WjiHefR6c5VKYjvdSat68RDJ3qGPgCckJqG3pXbDbVjZR5QB1 zoin11111111@owner, 1 zoin22222222@owner, 
        active     1:    1 EOS7ob62mdBfiRt1a8tQrQGbhnU1gEAvWyQDFHs99HzYEh8wxeQ9T

已将权限owner转交给了 zoin11111111@owner, 1 zoin22222222@owner

4.将popo11111111的active权限交出,由zoin11111111和zoin22222222的active权限控制。每个权限的权重weight是1,而门槛threshold是1,即使用popo11111111的active权限发送交易,zoin11111111和zoin22222222任意一个账户签名即可
先将popo11111111的owner私钥导入钱包

suroudeMacBook-Pro:~ surou$ cleos wallet import
private key: imported private key for: EOS7ob62mdBfiRt1a8tQrQGbhnU1gEAvWyQDFHs99HzYEh8wxeQ9T

将popo11111111的active权限交出

suroudeMacBook-Pro:~ surou$ cleos -u http://kylin.fn.eosbixin.com set account permission popo11111111 active '{"threshold":1, "keys":[{"key": "EOS6L7pr3AaKekTs1dbratDq1PutoSdmBWJFwbLcStsnKBbJtNUws"}], "accounts": [{"permission": {"actor": "zoin11111111", "permission": "active"}, "weight": 1}, {"permission": {"actor": "zoin22222222", "permission": "active"}, "weight": 1}]}' -p popo11111111@active
executed transaction: c51dede340204a6561b9ad511efcf1f194075319a97f54e7fce1dbee8b20eb18  200 bytes  748 us
#         eosio <= eosio::updateauth            {"account":"popo11111111","permission":"active","parent":"owner","auth":{"threshold":1,"keys":[{"key...

再次查看popo11111111账户

suroudeMacBook-Pro:~ surou$ cleos -u http://kylin.fn.eosbixin.com get account popo11111111
permissions: 
     owner     2:    60450 EOS81WjiHefR6c5VKYjvdSat68RDJ3qGPgCckJqG3pXbDbVjZR5QB1 zoin11111111@owner, 1 zoin22222222@owner, 
        active     1:    31778 EOS6L7pr3AaKekTs1dbratDq1PutoSdmBWJFwbLcStsnKBbJtNUws1 zoin11111111@active, 1 zoin22222222@active, 

已经权限active转交给1 zoin11111111@active, 1 zoin22222222@active

cleos multisig命令

EOS账户这一节,我们将popo11111111账户的owner和active权限交给了zoin11111111和zoin22222222账户,则popo11111111账户原来的公私钥对就失效了,交易的发送需要zoin11111111和zoin22222222账户授权。

额外提一句,主网启动后,超级节点们会将eosio账户的权限交给eosio.prods账户,而eosio.prods账户是由21个超级节点的账户联合控制的,需要15个节点联合签名才能使用eosio账户,可以避免拿管理员权限作恶的现象

  • 主网eosio账户权限
    suroudeMacBook-Pro:~ surou$ cleos -u http://kylin.fn.eosbixin.com get account eosio
    privileged: true
    permissions: 
       owner     1:    1 eosio.prods@active, 
          active     1:    1 eosio.prods@active,
  • 主网eosio.prods账户权限
    suroudeMacBook-Pro:~ surou$ cleos -u http://kylin.fn.eosbixin.com get account eosio.prods
    permissions: 
       owner     1:    
          active    15:    1 acryptotitan@active, 1 alohaeostest@active, 1 blockmatrix2@active, 1 eosargentina@active, 1 eosasia11111@active, 1 eosbeijingbp@active, 1 eoscanadacom@active, 1 eosecoeoseco@active, 1 eoshuobipool@active, 1 eosiomeetone@active, 1 eosiosg11111@active, 1 eoslaomaocom@active, 1 eosnodeonebp@active, 1 eospaceioeos@active, 1 eosriobrazil@active, 1 eosstorebest@active, 1 eossv12eossv@active, 1 eosswedenorg@active, 1 eoszb1111111@active, 1 helloeoschbp@active, 1 superoneiobp@active, 
             prod.major    11:    1 acryptotitan@active, 1 alohaeostest@active, 1 blockmatrix2@active, 1 eosargentina@active, 1 eosasia11111@active, 1 eosbeijingbp@active, 1 eoscanadacom@active, 1 eosecoeoseco@active, 1 eoshuobipool@active, 1 eosiomeetone@active, 1 eosiosg11111@active, 1 eoslaomaocom@active, 1 eosnodeonebp@active, 1 eospaceioeos@active, 1 eosriobrazil@active, 1 eosstorebest@active, 1 eossv12eossv@active, 1 eosswedenorg@active, 1 eoszb1111111@active, 1 helloeoschbp@active, 1 superoneiobp@active, 
                prod.minor     8:    1 acryptotitan@active, 1 alohaeostest@active, 1 blockmatrix2@active, 1 eosargentina@active, 1 eosasia11111@active, 1 eosbeijingbp@active, 1 eoscanadacom@active, 1 eosecoeoseco@active, 1 eoshuobipool@active, 1 eosiomeetone@active, 1 eosiosg11111@active, 1 eoslaomaocom@active, 1 eosnodeonebp@active, 1 eospaceioeos@active, 1 eosriobrazil@active, 1 eosstorebest@active, 1 eossv12eossv@active, 1 eosswedenorg@active, 1 eoszb1111111@active, 1 helloeoschbp@active, 1 superoneiobp@active, 

下面,我们就要介绍cleos multisig命令如何调用eosio.msig合约进行提案、发送多重签名的交易。

1.zoin11111111发起一个转账提案,提案名是transferpopo,将popo11111111账户中的10EOS转给cryptokylinq账户,这之前需要保证popo11111111账户有币。
先给popo11111111转入点EOS

cleos -u http://kylin.fn.eosbixin.com push action eosio.token transfer '["cryptokylinq", "popo11111111","10.0000 EOS","test"]' -p cryptokylinq

再导入zoin11111111的active的私钥

suroudeMacBook-Pro:~ surou$ cleos wallet import
private key: imported private key for: EOS6BAfK8mZZjMaa74zxkT3evWEtWo4AnRQYmnpGWkNJYKVjcrpao

发起提案

suroudeMacBook-Pro:~ surou$ cleos -u http://kylin.fn.eosbixin.com multisig propose transferpopo '[{"actor": "zoin11111111", "permission": "active"}, {"actor": "zoin22222222", "permission": "active"}]'  '[{"actor": "popo11111111", "permission":"active"}]'  eosio.token transfer '{"from": "popo11111111", "to": "cryptokylinq", "quantity":"10.0000 EOS", "memo": "test multisig"}' -p zoin11111111@active
executed transaction: 83cde06310cd6cd74dfb180aa38fa4f0a5d950b6f39a4f963237a5ea6d2c39aa  240 bytes  810 us
#    eosio.msig <= eosio.msig::propose          {"proposer":"zoin11111111","proposal_name":"transferpopo","requested":[{"actor":"zoin11111111","perm...

详细分析下cleos multisig propose命令参数
- proposal_name:提案名

  • requested_permissions:提案审批通过需要的权限,这里需要zoin11111111和zoin22222222的active权限
  • trx_permission:提案执行需要的权限,需要popo11111111的active权限就能发起转账
  • contract:提案调用的合约账户,转账使用eosio.token账户合约
  • action:提案调用的合约方法,转账使用transfer方法
  • data:具体数据
  • proposer:提案发起人,zoin11111111发起
    - proposal_expiration:提案的有效时间

2.查看提案交易cleos multisig review <proposer> <proposal_name>

suroudeMacBook-Pro:~ surou$ cleos -u http://kylin.fn.eosbixin.com multisig review zoin11111111 transferpopo
{
  "proposal_name": "transferpopo",
  "packed_transaction": "4224ab5b000000000000000000000100a6823403ea3055000000572d3ccdcd011042082184402bad00000000a8ed32322e1042082184402bad60a78b1ed25cfd45a08601000000000004454f53000000000d74657374206d756c746973696700",
  "transaction": {
    "expiration": "2018-09-26T06:16:34",
    "ref_block_num": 0,
    "ref_block_prefix": 0,
    "max_net_usage_words": 0,
    "max_cpu_usage_ms": 0,
    "delay_sec": 0,
    "context_free_actions": [],
    "actions": [{
        "account": "eosio.token",
        "name": "transfer",
        "authorization": [{
            "actor": "popo11111111",
            "permission": "active"
          }
        ],
        "data": {
          "from": "popo11111111",
          "to": "cryptokylinq",
          "quantity": "10.0000 EOS",
          "memo": "test multisig"
        },
        "hex_data": "1042082184402bad60a78b1ed25cfd45a08601000000000004454f53000000000d74657374206d756c7469736967"
      }
    ],
    "transaction_extensions": []
  }
}

3.查看提案审批情况,provided_approvals为空表示尚未审批,request_approvals表示需要哪些权限进行审批
cleos get table eosio.msig <proposer> approvals

suroudeMacBook-Pro:~ surou$ cleos -u http://kylin.fn.eosbixin.com get table eosio.msig zoin11111111 approvals
{
  "rows": [{
      "proposal_name": "transferpopo",
      "requested_approvals": [{
          "actor": "zoin11111111",
          "permission": "active"
        },{
          "actor": "zoin22222222",
          "permission": "active"
        }
      ],
      "provided_approvals": []
    }
  ],
  "more": false
}

4.通过提案 cleos multisig approve 提案人 提案 权限

  • zoin11111111通过提案
    suroudeMacBook-Pro:~ surou$ cleos -u http://kylin.fn.eosbixin.com multisig approve zoin11111111 transferpopo '{"actor": "zoin11111111", "permission": "active"}'  -p zoin11111111@active
    executed transaction: bcc0495b88d7b897853af11ace59029a32f16f1727de04e4c1fa7fc85e7df1dc  128 bytes  1091 us
    #    eosio.msig <= eosio.msig::approve          {"proposer":"zoin11111111","proposal_name":"transferpopo","level":{"actor":"zoin11111111","permissio...

    查看提案审批情况

    suroudeMacBook-Pro:~ surou$ cleos -u http://kylin.fn.eosbixin.com get table eosio.msig zoin11111111 approvals
    {
    "rows": [{
        "proposal_name": "transferpopo",
        "requested_approvals": [{
            "actor": "zoin22222222",
            "permission": "active"
          }
        ],
        "provided_approvals": [{
            "actor": "zoin11111111",
            "permission": "active"
          }
        ]
      }
    ],
    "more": false
    }
  • zoin22222222通过提案
    suroudeMacBook-Pro:~ surou$ cleos -u http://kylin.fn.eosbixin.com multisig approve zoin11111111 transferpopo '{"actor": "zoin22222222", "permission": "active"}'  -p zoin22222222@active
    executed transaction: 62ef04a0ee345ac604ca9fe7be034a0cb69524b1ed146e22c44a806a03337919  128 bytes  633 us
    #    eosio.msig <= eosio.msig::approve          {"proposer":"zoin11111111","proposal_name":"transferpopo","level":{"actor":"zoin22222222","permissio...

    查看提案审批情况

    suroudeMacBook-Pro:~ surou$ cleos -u http://kylin.fn.eosbixin.com get table eosio.msig zoin11111111 approvals
    {
    "rows": [{
        "proposal_name": "transferpopo",
        "requested_approvals": [],
        "provided_approvals": [{
            "actor": "zoin11111111",
            "permission": "active"
          },{
            "actor": "zoin22222222",
            "permission": "active"
          }
        ]
      }
    ],
    "more": false
    }

5.执行提案cleos multisig exec <proposer> <proposal_name> -p 谁想执行都可以
执行前查看账户

  • cryptokylinq 余额4,148.3135 EOS
  • popo11111111 余额 10 EOS
suroudeMacBook-Pro:~ surou$ cleos -u http://kylin.fn.eosbixin.com multisig exec zoin11111111 transferpopo -p zoin11111111
executed transaction: caaa900536463207713f6af031ee315125d993f0af56ea91aafff3dcdf8b1353  160 bytes  1098 us
#    eosio.msig <= eosio.msig::exec             {"proposer":"zoin11111111","proposal_name":"transferpopo","executer":"zoin11111111"}

执行完成后查看账户

  • cryptokylinq 余额4,158.3135 EOS
  • popo11111111 余额 0 EOS

且提案将被自动删除

suroudeMacBook-Pro:~ surou$ cleos -u http://kylin.fn.eosbixin.com get table eosio.msig zoin11111111 approvals
{
  "rows": [],
  "more": false
}

并注意到,该笔转账没有交易记录actions 额,是不是在某些情况,需要隐藏交易记录时,可以通过此方法。。。


使用场景

某个账户发起一个提案后,需事先明确指定哪些账户/权限,必须全部通过审核后,合约动作才能被执行。

常见问题

  • 提案操作很消耗CPU Error 3080004: Transaction exceeded the current CPU usage limit imposed on the transaction

    cleos -u http://kylin.fn.eosbixin.com system delegatebw cryptokylinq zoin11111111 "0 EOS" "10 EOS"
  • 执行提案时,可能出现下面未知错误

    需要部署eosio.system系统合约,并开启提案合约的功能即可解决(具体原理会在介绍eos.system合约的时候介绍)

    cleos push action eosio setpriv '["eosio.msig", 1]' -p eosio

    后记

    本篇为大家介绍了eos的账户权限,以及eosio.msig提案合约如何通过cleos multisig调用使用,如何发起提案、审批提案等。别走开,下一篇将深入源码,分析eosio.msig提案合约是怎么写的。
    参考自简书