BCSkill (Block chain skill )
区块链中文技术社区

只讨论区块链底层技术
遵守一切相关法律政策!

EOS源码分析之一整体介绍

EOS整体介绍


一、EOS的插件式设计



EOS中,虽然编程的复杂度和设计较比特币大幅提高,但其核心的思想其实并没有多大改变,目前来看,仍然以BOOST的signal,boost::asio的信号消息机制来完成模块间的解耦。相比比特币来言,做得更优雅,封装也更良好。


先看一下插件设计的整体类图:




从上面的类图可以清楚的看到,整个插件的依赖和传导机制。然后在下面的流程分析中会详细说明一下它的具体的应用。

二、EOS的整体流程



EOS的版本做了一次比较大的更迭,至少从形式上看是,它的生成路径下,完成了以下几个目标:


cleos:客户端,用来处理和区块链通信。帐户钱包等的管理。


eosio-abigen:二进制ABI的生成程序。


eosio-launcher:简化了eosd节点跨局域网或者跨更宽泛的网络的分布。


keosd:钱包和帐户的实现控制程序


nodeos:核心的节点程序,这个和老版本不一样了,至少名字不一样了。



一般情况下会启动cleos调用keosd来创建帐户和钱包。然后通过启动nodeos来产生节点,进行通信并根据配置生成区块和验证。进入重点,直接看一下 nodeos的创建代码:

int main(int argc, char** argv)
{
   try {
      app().set_version(eosio::nodeos::config::version);
      auto root = fc::app_path();
      app().set_default_data_dir(root / "eosio/nodeos/data" );
      app().set_default_config_dir(root / "eosio/nodeos/config" );
      //这里直接初始化了四个插件
      if(!app().initialize<chain_plugin, http_plugin, net_plugin, producer_plugin>(argc, argv))
         return -1;
      initialize_logging();
      ilog("nodeos version ${ver}", ("ver", eosio::nodeos::config::itoh(static_cast<uint32_t>(app().version()))));
      ilog("eosio root is ${root}", ("root", root.string()));
      app().startup();
      app().exec();
   } catch (const fc::exception& e) {
      elog("${e}", ("e",e.to_detail_string()));
   } catch (const boost::exception& e) {
      elog("${e}", ("e",boost::diagnostic_information(e)));
   } catch (const std::exception& e) {
      elog("${e}", ("e",e.what()));
   } catch (...) {
      elog("unknown exception");
   }
   return 0;
}


代码看上去并不多,当然,比之比特币最新中的几行代码来看,还是要稍有复杂的感觉,但是还可以承受,不过,随后可能c++技能的消耗水平会极剧增加。忽略开前几行的相关文件配置直接进行初始化代码看看去。


template<typename... Plugin>
bool                 initialize(int argc, char** argv) {
   return initialize_impl(argc, argv, {find_plugin<Plugin>()...});
}


没啥,一个向量的初始化。不过有一个变参模板,如果想深入学习的得去看看相关资料。

bool application::initialize_impl(int argc, char** argv, vector<abstract_plugin*> autostart_plugins) {
   set_program_options();//设置命令选项

   bpo::variables_map options;//声明保存结果变量
   bpo::store(bpo::parse_command_line(argc, argv, my->_app_options), options);//分析参数并保存

   if( options.count( "help" ) ) {
      cout << my->_app_options << std::endl;
      return false;
   }

   ......

   //分析配置文件
   bpo::store(bpo::parse_config_file<char>(config_file_name.make_preferred().string().c_str(),
                                           my->_cfg_options, true), options);

   if(options.count("plugin") > 0)
   {
      auto plugins = options.at("plugin").as<std::vector<std::string>>();
      for(auto& arg : plugins)
      {
         vector<string> names;
         boost::split(names, arg, boost::is_any_of(" \t,"));
         for(const std::string& name : names)
            get_plugin(name).initialize(options);//分步初始化第一步,获取指定名插件并初始化,其它类同
      }
   }
   //下面是注册插件,并查寻依赖的相关插件,然后调用,并初始化
   for (auto plugin : autostart_plugins)
      if (plugin != nullptr && plugin->get_state() == abstract_plugin::registered)
         plugin->initialize(options);//分步初始化第一步,获取指定名插件并初始化,其它类同

   bpo::notify(options);//更新最新参数至options

   return true;
}


里面反复的参数控制代码略过了。里面主要是使用了BOOST的参数解析和更新机制



这里的调用很简单,其实就是从map里查找相关的插件,用类名和字符串,这里面用到了BOOST中的一些库boost::core::demangle(typeid(Plugin).name()),用来返回类型的名字。然后再用名字的字符串查找出插件。这里面有一个问题,为什么从plugins这个map中可以查找出对象,仔细看一下有些插件的CPP文件中会有类似的代码:

static appbase::abstract_plugin& _net_plugin = app().register_plugin<net_plugin>();



静态注册了啊。但是有一些插件里没有啊,怎么回事儿?其实接着看代码就发现了问题所在。如下:

virtual void initialize(const variables_map& options) override {
   if(\_state == registered) {
      \_state = initialized;
      //分步初始化,第二步
      static_cast<Impl*>(this)->plugin_requires([&](auto& plug){ plug.initialize(options); });//初始化此插件依赖的插件,并递归调用依赖插件
      static_cast<Impl*>(this)->plugin_initialize(options);  //初始化插件
      //ilog( "initializing plugin ${name}", ("name",name()) );
      app().plugin_initialized(*this);//保存启动的插件
   }
   assert(\_state == initialized); /// if initial state was not registered, final state cannot be initiaized
}



plugin_requires,这个函数的定义就通过宏来产生了。

//先看一个调用实现
class chain_plugin : public plugin<chain_plugin> {
public:
   APPBASE_PLUGIN_REQUIRES()
......
};
#define APPBASE_PLUGIN_REQUIRES_VISIT( r, visitor, elem ) \
  visitor( appbase::app().register_plugin<elem>() );

#define APPBASE_PLUGIN_REQUIRES( PLUGINS )                               \
   template<typename Lambda>                                           \
   void plugin_requires( Lambda&& l ) {                                \
      BOOST_PP_SEQ_FOR_EACH( APPBASE_PLUGIN_REQUIRES_VISIT, l, PLUGINS ) \
   }
//再看另外一个调用实现
class producer_plugin : public appbase::plugin<producer_plugin> {
public:
   APPBASE_PLUGIN_REQUIRES((chain_plugin))
......
};



就这样,基础的插件和基础插件依赖的插件,就这么被一一加载初始化。


三、EOS的程序技术特点


1、使用了较多的宏,并配合BOOST库。


在EOS的代码中,可以隐约看到类似MFC的代码实现机制,举一个例子:

#define FC_CAPTURE_AND_RETHROW( ... ) \
   catch( fc::exception& er ) { \
      FC_RETHROW_EXCEPTION( er, warn, "", FC_FORMAT_ARG_PARAMS(__VA_ARGS__) ); \
   } catch( const std::exception& e ) {  \
      fc::exception fce( \
                FC_LOG_MESSAGE( warn, "${what}: ",FC_FORMAT_ARG_PARAMS(__VA_ARGS__)("what",e.what())), \
                fc::std_exception_code,\
                BOOST_CORE_TYPEID(decltype(e)).name(), \
                e.what() ) ; throw fce;\
   } catch( ... ) {  \
      throw fc::unhandled_exception( \
                FC_LOG_MESSAGE( warn, "",FC_FORMAT_ARG_PARAMS(__VA_ARGS__)), \
                std::current_exception() ); \
   }

FC_CAPTURE_AND_RETHROW( (t) )


包括前面提到的递归调用插件化的宏定义,再通过上面的调用实现对比,基本上是以动态生成代码为主,在比特币也有类似的实现,但规模和应用要小得多。

2、模板的使用普及化


在工程代码上广泛使用了模板,看一下插件的例子:

template<typename Impl>
class plugin : public abstract_plugin {
   public:
      plugin():\_name(boost::core::demangle(typeid(Impl).name())){}
      virtual ~plugin(){}

      virtual state get_state()const override         { ... }
      virtual const std::string& name()const override { ... }

      virtual void register_dependencies() {
.......
      }

      virtual void initialize(const variables_map& options) override {
......
      }

      virtual void startup() override {
......
      }

      virtual void shutdown() override {
......
      }

......
};


3、更深入的绑定使用了c++1X和BOOST



这个就非常明显了,试举一个简单的例子:

//c++11语法糖
for (const auto& at: trx_trace.action_traces) {
   for (const auto& auth: at.act.authorization) {
      result.emplace_back(auth.actor);
   }

   result.emplace_back(at.receiver);
}
//BOOST的网络通信
using boost::asio::ip::tcp;
unique_ptr<tcp::acceptor>        acceptor;
std::unique_ptr<class net_plugin_impl> my;
void net_plugin::plugin_startup() {
   if( my->acceptor ) {
      my->acceptor->open(my->listen_endpoint.protocol());
      my->acceptor->set_option(tcp::acceptor::reuse_address(true));
      my->acceptor->bind(my->listen_endpoint);
      my->acceptor->listen();
      ilog("starting listener, max clients is ${mc}",("mc",my->max_client_count));
      my->start_listen_loop();
   }

   my->chain_plug->chain().on_pending_transaction.connect( &net_plugin_impl::transaction_ready);
   my->start_monitors();

   for( auto seed_node : my->supplied_peers ) {
      connect( seed_node );
   }
}


目前初步看来,EOS对BOOST和c++14的依赖更深。

转载自:https://github.com/XChainLab/documentation/blob/master/eos/eos%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%E4%B9%8B%E4%B8%80%E6%95%B4%E4%BD%93%E4%BB%8B%E7%BB%8D.md

EOS Block Data Structure

EOS Block Data Structure

整体数据结构定义图

我们先来看一张EOS整体的数据结构定义图

从这个图上可以看出来,EOS在区块数据结构的定义上并不是特别复杂.

Block 定义

// ..../eos/libraries/chain/include/eosio/chain/block.hpp
struct signed_block : public signed_block_header {
   using signed_block_header::signed_block_header;
   signed_block() = default;
   signed_block( const signed_block_header& h ):signed_block_header(h){}

   // 交易集合
   vector<transaction_receipt>   transactions;
   extensions_type               block_extensions;
};
using signed_block_ptr = std::shared_ptr<signed_block>;

// ..../eos/libraries/chain/include/eosio/chain/types.hpp
typedef vector<std::pair<uint16_t,vector<char>>> extensions_type;

Block的定义使用的是signed_block struct,这个struct是从signed_block_header继承而来的,在这个结构体中的关键部分是包含了Transaction的vector。区块是由按顺序组织的交易来构成的集合。

block_extensions则定义了一系列的扩展信息,这些信息都由一个整数类型的code来定义,需要的时候,都可以根据这个整数code来解析相应的信息。

在这个结构体中包含的transaction都是使用transaction_receipt结构体,这个结构体又是从transaction_receipt_header继承而来,下面我们看看这个两个struct的定义。

// ..../eos/libraries/chain/include/eosio/chain/block.hpp
struct transaction_receipt_header {
   // 定义交易状态的枚举类型
   enum status_enum {
      // 这个表示执行成功(所以不需要执行出错逻辑)
      executed  = 0,
      // 客观的来说,执行失败了(或者没有执行),某一个出错逻辑执行了
      soft_fail = 1,
      // 执行失败了,并且执行的出错逻辑也失败了,所以并没有状态改变
      hard_fail = 2,
      // 交易被延迟了,计划到未来的某个时间执行
      delayed   = 3,
      // 交易过期了,并且存储空间返还给用户
      expired   = 4  ///< transaction expired and storage space refuned to user
   };

   // 状态数据
   fc::enum_type<uint8_t,status_enum>   status;
   // CPU使用情况
   uint32_t                             cpu_usage_us;
   // 网络使用情况
   fc::unsigned_int                     net_usage_words;
};

struct transaction_receipt : public transaction_receipt_header {
   fc::static_variant<transaction_id_type, packed_transaction> trx;
};

transaction_receipt结构体主要包含了一个打包过的交易以及其对应的交易类型。其parent struct transaction_receipt_header则主要是记录了这个交易的状态信息,以及CPU和网络的使用情况。当一笔交易被某个区块引用时,区块生产者针对这笔交易会做出相应的操作,而操作的不同结果会导致这笔交易的不同状态.

packed_transaction,顾名思义,就是把交易数据打包了,这个结构体里面还定义了,打包数据是否经过了压缩的标识信息,

// ..../eos/libraries/chain/include/eosio/chain/transaction.hpp
struct packed_transaction {
   // 定义打包数据是否压缩的枚举类型
   enum compression_type {
      // 没有压缩
      none = 0,
      // 使用zlib压缩
      zlib = 1,
   };

   // 签名信息
   vector<signature_type>                  signatures;
   // 是否压缩的标识信息
   fc::enum_type<uint8_t,compression_type> compression;
   // 上下文无关的信息
   bytes                                   packed_context_free_data;
   // 打包后的交易数据
   bytes                                   packed_trx;
}

packed_transaction中打包的数据来自于signed_transaction结构体,这个结构体的主要作用就是对交易做签名。

signed_transaction又是从transaction结构体继承而来,一个transaction结构体的实例包含一系列的action,这些action要么全部成功,要么全部失败。

交易ID是通过对交易内容本身经过Hash运算得出,所以每个交易的ID是与其内容一一对应的。交易的主体是由操作构成的。一个交易在纳入区块之前必须含有签名,用以验证交易的合法性。

延迟型交易

交易分为两种类型:一种是账户发起的普通交易,一种是由代码生成的自动交易,自动交易可以设置一个延迟时间,这样的交易叫延迟型交易,这种交易不会立即被执行,而是等到设定时间到时才会被执行。

// ..../eos/libraries/chain/include/eosio/chain/transaction.hpp
struct signed_transaction : public transaction
{
    // 签名信息
     vector<signature_type>    signatures;
     // 上下文无关的数据
     vector<bytes>             context_free_data;
};

struct transaction : public transaction_header {
     // 上下文无关的action
     vector<action>         context_free_actions;
     // 交易操作
     vector<action>         actions;
     // 交易扩展类型
     extensions_type        transaction_extensions;
}

transaction_header结构体包含了与每一个交易相关联的固定大小的数据,这些数据从具体的交易数据中分离出来,可以在需要的时候,帮助解析交易数据,而不再需要更多的动态内存分配。

所有的交易都有一个期限,这个期限限定了一个交易必须在规定时间内被纳入区块链,如果我们发现一个交易的时限已经过去,就可以放心的放弃这个交易,因为所有生产者都不会将它纳入任何区块。

// ..../eos/libraries/chain/include/eosio/chain/transaction.hpp
struct transaction_header {
     // 这个交易的过期时间
     time_point_sec         expiration;
     // 在最后的2^16 blocks中指定一个具体的block number
     uint16_t               ref_block_num       = 0U;
     // 在指定的get_ref_blocknum的blockid中取低位的32bit
     uint32_t               ref_block_prefix    = 0UL;
     // 最大的网络带块
     fc::unsigned_int       max_net_usage_words = 0UL;
     // 最大的CPU使用
     uint8_t                max_cpu_usage_ms    = 0;
     // 这个交易的延期时间
     fc::unsigned_int       delay_sec           = 0UL;
}

signed_block结构体是从signed_block_header继承而来的,这个signed_block_header结构体只是包含了一条数据,那就是producer的签名。

signed_block_header结构体又是从block_header继承而来的,这个结构体就包含了一个block中很多重要的数据,包括,时间戳,producer的名字,所有交易的merkle root,所有action的root等信息。

// ..../eos/libraries/chain/include/eosio/chain/block_header.hpp
struct block_header
{
     block_timestamp_type             timestamp;
     account_name                     producer;
     uint16_t                         confirmed = 1;  

     block_id_type                    previous;

     checksum256_type                 transaction_mroot;
     checksum256_type                 action_mroot;

     uint32_t                          schedule_version = 0;
     optional<producer_schedule_type>  new_producers;
     extensions_type                   header_extensions;
};

struct signed_block_header : public block_header
{
     signature_type    producer_signature;
};

Action(操作)

在前面的transaction结构体中,我们看到包含了有action,这里我们对action做一下说明。

我们先看看action的数据结构定义:

// ..../eos/contracts/eosiolib/action.hpp
struct action {
   // 账户:操作的来源
   account_name               account;
   // 名称:操作的标识
   action_name                name;
   // 授权:执行操作的许可列表
   vector<permission_level>   authorization;
   // 数据:执行操作需要用到的信息
   bytes                      data;
}

EOS区块链中的交易是由一个个操作(action)组成的,操作可以理解成一个能够更改区块链全局状态的方法,操作的顺序是确定的,一个交易内的操作要么全部执行成功,要么都不执行,这与交易的本意是一致的。操作是区块链的最底层逻辑,相当于区块链这个大脑的神经元,区块链的智能最终也是通过一个个操作的组合来实现的。

操作的设计原则

  • 独立原则 操作本身须包含足以解释操作含义的信息,而不需要依赖区块链提供的上下文信息来帮助解释。所以,即便一个操作的当前状态可以通过区块链上的数据推导得出,我们也需要将状态信息纳入操作数据中,以便每个操作是容易理解的。这个原则体现的是区块的可解释性,这一点非常重要,这个底层的设计原则将影响整个区块链的使用效率
  • 余额可计算原则 一个账户当前的余额计算,仅仅依赖于与这个账户相关的信息便可得出,而不需要解析整个区块链才能获得。这个原则针对的是比特币的设计,由于比特币的余额计算需要扫描区块链中的所有交易才能精准的计算出一个账户的余额,这使得一个非常基础的计算落地起来都变得相当繁琐,EOS的这个设计目的在于提升运算效率。
  • 明确费用原则 区块链的交易费用随时间变化而变化,所以,一个签名过的交易须明确的认同这个交易所需要支付的费用,这个费用是在交易形成之前就已经设定并且明确好了的,这一点也非常重要,因为明确的费用协议才能保证余额的正确计算。
  • 明确授权原则 每个操作须包含足够的授权信息以标明是哪一个账户拥有授权这个操作的权力,这种明确授权的设计思路带来几个好处:
    • 便于集中管理
    • 可以优化授权管理
    • 便于并行处理
  • 关联账户原则 每个操作须包含足够的关联账户信息,以保证这个操作能够遍历所有相关联的账户,也就是这个操作能够影响的所有账户,这个原则的目的同样是为了确保账户的余额能够得到及时和准确的运算

操作的来源

一个操作可以通过两种途径产生:

  • 由一个账号产生,通过签名来授权,即显性方式。
  • 由代码生成,即隐形方式。

操作的设计遵循React Flux设计模式,就是每一个操作将会被赋予一个名称,然后被分发给一个或者多个handler。在EOS环境中,每个操作对应的handler是通过scope和name来定义的,默认的handler也可以再次将操作分发给下一级的多个handler。所以,每个EOS应用可以实现自己的handler,当操作被分发到这个应用时,相应的handler的代码就会被执行。

操作的设计思路中另一重要概念是授权。每一个操作的执行都必须确保具备了指定账户的授权。授权通过许可(permission)的方式声明,对于一个授权动作,账户可以要求任意数量的许可,许可的验证是独立于代码执行的,只有所有规定的许可被成功验证之后,对应的代码才能够被执行。安全特性深深的嵌入了区块链的底层设计逻辑,同时又不让安全机制成为性能和结构的累赘,让它自成体系,独立管理。

区块的存储 - 区块日志

区块日志是存储区块的二进制文件,区块日志的特性是只能从末尾追加(append only),区块日志包含两类文件:

区块文件,结构如下:

+---------+----------------+---------+----------------+-----+------------+-------------------+

| Block 1 | Pos of Block 1 | Block 2 | Pos of Block 2 | ... | Head Block | Pos of Head Block |

+---------+----------------+---------+----------------+-----+------------+-------------------+

区块文件包含区块的内容以及每个区块的位置信息。区块位置信息是固定的8字节宽度,这样便于在连续读取区块的时候,按照读一个区块,向后跳跃8个字节,读一个区块,向后跳跃8个字节的模式快速加载区块内容。

索引文件,结构如下:

+----------------+----------------+-----+-------------------+

| Pos of Block 1 | Pos of Block 2 | ... | Pos of Head Block |

+----------------+----------------+-----+-------------------+

区块索引的目的在于提供一个基于区块序号的快速随机搜索方法,使用索引文件可以快速定位目标区块在区块文件中的具体位置。索引文件不是必须的,没有索引文件区块链仍然可以运行,索引文件的主要作用是通过少量空间换取速度提升。索引文件可以通过顺序读取区块文件来重新构建。

转载自:https://github.com/XChainLab/documentation/blob/master/eos/eos.block.data.structure.md

get_controlled_accounts

功能:获得控制的账户
参数:controlling_account

测试

curl --request POST  --url https://api.eoslaomao.com/v1/history/get_controlled_accounts  --data '{"controlling_account":"eosio"}'

返回

{"controlled_accounts":["decentwitter","eoshappyvote","eosio.bpay","eosio.forum","eosio.lost","eosio.msig","eosio.names","eosio.ram","eosio.ramfee","eosio.regram","eosio.rex","eosio.saving","eosio.stake","eosio.token","eosio.unregd","eosio.vpay","eosio.wrap","eoslocktoken","eosvrmarkets","eosvrrewards","eosvrtokenss","evdadvancer1","evdadvancers","evdpopulariz","evradvancer1","evradvancers","evrportraits","freewithdraw","frogfrogcoin","gm2dimrqgige","nvoyiooiyovn","ssssssss1115","stoponnopots","testtodelete","toohottohoot","unusedaccnts"]}

查询其中任意一个账号

cleos -u https://api.eoslaomao.com get account unusedaccnts

返回账号信息如下

permissions:
     owner     1:    1 eosio@active
     active     1:    1 eosio@active
     modify     1:    1 eosio.prods@prod.minor

再次查询交集账号eosio.prods

curl --request POST  --url https://api.eoslaomao.com/v1/history/get_controlled_accounts  --data '{"controlling_account":"eosio.prods"}'

返回账号信息如下

{"controlled_accounts":["chaingame123","eos3dio12345","eosdayeosday","eosio","paranthropus","studcontract","unusedaccnts"]}

其中也包含了unusedaccnts

结论

所以get_controlled_accounts返回的是已经将权限(含部分)移交给查询账号的所有账户。

备注

nodeos时需要启用history_api_plugin插件,并且设置filter-on选项

eosio.wrap 多签使用

Test eosio.wrap on Kylin Testnet

准备测试账户

Kylin 测试网络新建了两个账户用于 wrap 合约测试,分别为 updatemyauthtransmytoken,两个账户的 active 和 owner key 均为 EOS1111111111111111111111111111111114T1Anm,如下所示:

cleos -u https://api-kylin.eoslaomao.com get account updatemyauth
permissions:
     owner     1:    1 EOS1111111111111111111111111111111114T1Anm
        active     1:    1 EOS1111111111111111111111111111111114T1Anm
memory:
     quota:     40.91 KiB    used:     2.926 KiB

net bandwidth:
     delegated:       1.0000 EOS           (total staked delegated to account from others)
     used:                 0 bytes
     available:        835.2 KiB
     limit:            835.2 KiB

cpu bandwidth:
     delegated:       1.0000 EOS           (total staked delegated to account from others)
     used:                 0 us
     available:        121.9 ms
     limit:            121.9 ms

cleos -u https://api-kylin.eoslaomao.com get account transmytoken
permissions:
     owner     1:    1 EOS1111111111111111111111111111111114T1Anm
        active     1:    1 EOS1111111111111111111111111111111114T1Anm
memory:
     quota:     40.91 KiB    used:     2.926 KiB

net bandwidth:
     delegated:       1.0000 EOS           (total staked delegated to account from others)
     used:                 0 bytes
     available:        835.2 KiB
     limit:            835.2 KiB

cpu bandwidth:
     delegated:       1.0000 EOS           (total staked delegated to account from others)
     used:                 0 us
     available:        121.9 ms
     limit:            121.9 ms

EOS balances:
     liquid:          100.0000 EOS
     staked:            0.0000 EOS
     unstaking:         0.0000 EOS
     total:           100.0000 EOS

其中 transmytoken 账户有 100 EOS 的余额,用于 CASE 3 测试。

在线查看 Kylin 网络中的上述账户,请访问:

https://tools.cryptokylin.io/#/tx/updatemyauth

https://tools.cryptokylin.io/#/tx/transmytoken

CASE 1. wrap 变更 updatemyauth 账户的 active key

第一步,生成变更 active key 的 transaction 数据

我们将 updatemyauth 账户的 active key 改为 EOS6GR1JqeNmp5d7FGRt3Rx1BGQ1QxUk65nkfcnuprAs1BVNNWqSD

首先,我们生成 updateauth 数据:

cleos set account permission -s -j -d updatemyauth active EOS6GR1JqeNmp5d7FGRt3Rx1BGQ1QxUk65nkfcnuprAs1BVNNWqSD owner > updatemyauth.json

cat updatemyauth.json

{
  "expiration": "2018-08-09T09:40:33",
  "ref_block_num": 8064,
  "ref_block_prefix": 90673647,
  "max_net_usage_words": 0,
  "max_cpu_usage_ms": 0,
  "delay_sec": 0,
  "context_free_actions": [],
  "actions": [{
      "account": "eosio",
      "name": "updateauth",
      "authorization": [{
          "actor": "updatemyauth",
          "permission": "active"
        }
      ],
      "data": "d0b2365eaa6c52d500000000a8ed32320000000080ab26a701000000010002b57ac63750eca19d2e7a36ba46057e4d8a6a981849f883b5c731c33b86fdc82a01000000"
    }
  ],
  "transaction_extensions": [],
  "signatures": [],
  "context_free_data": []
}

将上述文件中的 ref_block_num 和 ref_block_prefix 改为 0,将 expiration 改为 1970-01-01T00:00:00

第二步,调用 wrap 合约执行上述命令,生成最终的 transaction data

生成 sudo_update_updatemyauth_active_trx.json 文件:

cleos wrap exec -s -j -d BP_ACCOUNT updatemyauth.json > sudo_update_updatemyauth_active_trx.json

cat sudo_update_updatemyauth_active_trx.json

{
  "expiration": "2018-08-09T09:43:33",
  "ref_block_num": 8424,
  "ref_block_prefix": 3790583261,
  "max_net_usage_words": 0,
  "max_cpu_usage_ms": 0,
  "delay_sec": 0,
  "context_free_actions": [],
  "actions": [{
      "account": "eosio.wrap",
      "name": "exec",
      "authorization": [{
          "actor": "eoslaomaocom",
          "permission": "active"
        },{
          "actor": "eosio.sudo",
          "permission": "active"
        }
      ],
      "data": "2029a246521331550000000000000000000000000000010000000000ea30550040cbdaa86c52d501d0b2365eaa6c52d500000000a8ed323243d0b2365eaa6c52d500000000a8ed32320000000080ab26a701000000010002b57ac63750eca19d2e7a36ba46057e4d8a6a981849f883b5c731c33b86fdc82a0100000000"
    }
  ],
  "transaction_extensions": [],
  "signatures": [],
  "context_free_data": []
}

将上述文件中的 ref_block_num 和 ref_block_prefix 设置为 0,将 expiration 的日期向后推迟一定的时间(比如 1 天后),这个时间表示 transaction 的过期时间。由于这个 transaction 最终需要等待 BP 多签生效,因此过期时间不能太短。

第三步,生成 producer_permissions.json 文件

假设网络中有 21 个 BP,名字分别为,blkproducera,blkproducerb,......,blkproduceru。则 producer_permissions.json 的文件内容如下所示:

$ cat producer_permissions.json
[
   {"actor": "blkproducera", "permission": "active"},
   {"actor": "blkproducerb", "permission": "active"},
   {"actor": "blkproducerc", "permission": "active"},
   {"actor": "blkproducerd", "permission": "active"},
   {"actor": "blkproducere", "permission": "active"},
   {"actor": "blkproducerf", "permission": "active"},
   {"actor": "blkproducerg", "permission": "active"},
   {"actor": "blkproducerh", "permission": "active"},
   {"actor": "blkproduceri", "permission": "active"},
   {"actor": "blkproducerj", "permission": "active"},
   {"actor": "blkproducerk", "permission": "active"},
   {"actor": "blkproducerl", "permission": "active"},
   {"actor": "blkproducerm", "permission": "active"},
   {"actor": "blkproducern", "permission": "active"},
   {"actor": "blkproducero", "permission": "active"},
   {"actor": "blkproducerp", "permission": "active"},
   {"actor": "blkproducerq", "permission": "active"},
   {"actor": "blkproducerr", "permission": "active"},
   {"actor": "blkproducers", "permission": "active"},
   {"actor": "blkproducert", "permission": "active"},
   {"actor": "blkproduceru", "permission": "active"}
]

第四步,发起多签 proposal,approve 并执行

发起 proposal:

$ cleos multisig propose_trx updateactive producer_permissions.json sudo_update_updatemyauth_active_trx.json PROPOSER_ACCOUNT

BP 分别 approve

cleos multisig approve PROPOSER_ACCOUNT updateactive '{"actor": "BP_ACCOUNT", "permission": "active"}' -p BP_ACCOUNT

approval 达到 15/21 之后,执行该 proposal:

cleos multisig exec PROPOSER_ACCOUNT updateactive ANY_ACCOUNT

*注意,任何账户都可以执行 approval 达到 15/21 的 proposal,即使该账户不是 BP。

Kylin 测试网络中已经提交 updateactive proposal:https://tools.cryptokylin.io/#/msig?proposer=eoslaomaocom&proposal=updateactive

CASE 2. wrap 变更 updatemyauth 账户的 owner key

我们将 updatemyauth 账户的 owner key 改为 EOS6GR1JqeNmp5d7FGRt3Rx1BGQ1QxUk65nkfcnuprAs1BVNNWqSD

CASE 2 和 CASE 1 类似,唯一的不同在于第一步骤的时候,生成 updatemyauth.json 的命令稍有不同。

更改 owner key 的命令不需要指定 parent permission:

cleos set account permission -s -j -d updatemyauth owner EOS6GR1JqeNmp5d7FGRt3Rx1BGQ1QxUk65nkfcnuprAs1BVNNWqSD > updatemyauth.json

其余步骤请参考 CASE1。

Kylin 测试网络中已经提交 updateowner proposal:https://tools.cryptokylin.io/#/msig?proposer=eoslaomaocom&proposal=updateowner

CASE 3. wrap 转移 transmytoken 账户中的 EOS

第一步,生成 transfer data

我们将 transmytoken 账户的 1 EOS 转移给 eosio 账户,命令如下:

cleos transfer -s -j -d transmytoken eosio "1 EOS" "steal 1 EOS from you!" > transmytoken.json

cat transmytoken.json
{
  "expiration": "2018-08-09T09:52:27",
  "ref_block_num": 25799,
  "ref_block_prefix": 1477572235,
  "max_net_usage_words": 0,
  "max_cpu_usage_ms": 0,
  "delay_sec": 0,
  "context_free_actions": [],
  "actions": [{
      "account": "eosio.token",
      "name": "transfer",
      "authorization": [{
          "actor": "transmytoken",
          "permission": "active"
        }
      ],
      "data": "3015a4d94b3ccdcd0000000000ea3055102700000000000004454f530000000015737465616c203120454f532066726f6d20796f7521"
    }
  ],
  "transaction_extensions": [],
  "signatures": [],
  "context_free_data": []
}

将上述文件中的 ref_block_num 和 ref_block_prefix 改为 0,将 expiration 改为 1970-01-01T00:00:00

其余步骤请参考 CASE1。

Kylin 测试网络中已经提交 transtoken proposal:https://tools.cryptokylin.io/#/msig?proposer=eoslaomaocom&proposal=transtoken

转载自:https://github.com/cryptokylin/contract-upgrade-manual/blob/master/test-sudo-contract.md

多签创建系统账号,并部署合约(multisig eosio.sudo)

Deploy eosio.sudo on Kylin Testnet

1. 使用 multisig 创建 eosio.sudo 账号

第一步,生成创建账户的 transaction 文件。

cleos system newaccount -s -j -d --transfer --stake-net "1.000 EOS" --stake-cpu "1.000 EOS" --buy-ram-kbytes 50 eosio eosio.sudo EOS8MMUW11TAdTDxqdSwSqJodefSoZbFhcprndomgLi9MeR2o8MT4 > generated_account_creation_trx.json

上述命令中的公钥可随意指定,因为最终创建 eosio.sudo 账号的 transaction 中使用的是 eosio@active 权限。

上述命令生成的 generated_account_creation_trx.json 文件内容如下:

$ cat generated_account_creation_trx.json

{
  "expiration": "2018-06-29T17:11:36",
  "ref_block_num": 16349,
  "ref_block_prefix": 3248946195,
  "max_net_usage_words": 0,
  "max_cpu_usage_ms": 0,
  "delay_sec": 0,
  "context_free_actions": [],
  "actions": [{
      "account": "eosio",
      "name": "newaccount",
      "authorization": [{
          "actor": "eosio",
          "permission": "active"
        }
      ],
      "data": "0000000000ea305500004d1a03ea305501000000010003c8162ea04fed738bfd5470527fd1ae7454c2e9ad1acbadec9f9e35bab2f33c660100000001000000010003c8162ea04fed738bfd5470527fd1ae7454c2e9ad1acbadec9f9e35bab2f33c6601000000"
    },{
      "account": "eosio",
      "name": "buyrambytes",
      "authorization": [{
          "actor": "eosio",
          "permission": "active"
        }
      ],
      "data": "0000000000ea305500004d1a03ea305500c80000"
    },{
      "account": "eosio",
      "name": "delegatebw",
      "authorization": [{
          "actor": "eosio",
          "permission": "active"
        }
      ],
      "data": "0000000000ea305500004d1a03ea3055102700000000000004535953000000001027000000000000045359530000000001"
    }
  ],
  "transaction_extensions": [],
  "signatures": [],
  "context_free_data": []
}

第二步,创建一个名为 newaccount_payload.json 的文件,指定 eosio.sudo 的账户权限为 eosio@active,文件内容如下:

$ cat newaccount_payload.json

{
   "creator": "eosio",
   "name": "eosio.sudo",
   "owner": {
      "threshold": 1,
      "keys": [],
      "accounts": [{
         "permission": {"actor": "eosio", "permission": "active"},
         "weight": 1
      }],
      "waits": []
   },
   "active": {
      "threshold": 1,
      "keys": [],
      "accounts": [{
         "permission": {"actor": "eosio", "permission": "active"},
         "weight": 1
      }],
      "waits": []
   }
}

第三步,使用 newaccount_payload.json 生成最终创建 eosio.sudo 账户的 transaction 数据:

使用 newaccount_payload.json 作为 newaccount 的 action data,生成最终创建账户的 transaction 数据

$ cleos push action -s -j -d eosio newaccount newaccount_payload.json -p eosio > generated_newaccount_trx.json
$ cat generated_newaccount_trx.json
{
  "expiration": "2018-06-29T17:11:36",
  "ref_block_num": 16349,
  "ref_block_prefix": 3248946195,
  "max_net_usage_words": 0,
  "max_cpu_usage_ms": 0,
  "delay_sec": 0,
  "context_free_actions": [],
  "actions": [{
      "account": "eosio",
      "name": "newaccount",
      "authorization": [{
          "actor": "eosio",
          "permission": "active"
        }
      ],
      "data": "0000000000ea305500004d1a03ea30550100000000010000000000ea305500000000a8ed32320100000100000000010000000000ea305500000000a8ed3232010000"
    }
  ],
  "transaction_extensions": [],
  "signatures": [],
  "context_free_data": []
}

第四步,生成 setpriv 的 action data

生成 setpriv 的 action data,用于将 eosio.sudo 账户设置为 privileged account:

$ cleos push action -s -j -d eosio setpriv '{"account": "eosio.sudo", "is_priv": 1}' -p eosio > generated_setpriv_trx.json
$ cat generated_setpriv_trx.json
{
  "expiration": "2018-06-29T17:11:36",
  "ref_block_num": 16349,
  "ref_block_prefix": 3248946195,
  "max_net_usage_words": 0,
  "max_cpu_usage_ms": 0,
  "delay_sec": 0,
  "context_free_actions": [],
  "actions": [{
      "account": "eosio",
      "name": "setpriv",
      "authorization": [{
          "actor": "eosio",
          "permission": "active"
        }
      ],
      "data": "00004d1a03ea305501"
    }
  ],
  "transaction_extensions": [],
  "signatures": [],
  "context_free_data": []
}

第五步,准备最终的 transaction data。

将 generated_newaccount_trx.json 复制一份,命名为 create_sudo_account_trx.json

编辑 create_sudo_account_trx.json 文件内容,将 ref_block_num 和 ref_block_prefix 设置为 0,将 expiration 的日期向后推迟一定的时间(比如 1 天后),这个时间表示 transaction 的过期时间。由于这个 transaction 最终需要等待 BP 多签生效,因此过期时间不能太短。

将 generated_account_creation_trx.json 中除了第一个 action 之外其余的 action(buyrambytes 和 delegatebw),追加到 create_sudo_account_trx.json 文件中 newaccount 这个 action 的后面。将 generated_setpriv_trx.json 文件中的 setpriv action 追加到 create_sudo_account_trx.json 文件中 action 列表的最后。

最终得到一个类似下面这一结构的 create_sudo_account_trx.json 文件,该文件可以被用于通过 multisig 提起 proposal 供 BP 多签进行升级。


$ cat create_sudo_account_trx.json
{
  "expiration": "2018-07-06T12:00:00",
  "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",
      "name": "newaccount",
      "authorization": [{
          "actor": "eosio",
          "permission": "active"
        }
      ],
      "data": "0000000000ea305500004d1a03ea30550100000000010000000000ea305500000000a8ed32320100000100000000010000000000ea305500000000a8ed3232010000"
    },{
      "account": "eosio",
      "name": "buyrambytes",
      "authorization": [{
          "actor": "eosio",
          "permission": "active"
        }
      ],
      "data": "0000000000ea305500004d1a03ea305500c80000"
    },{
      "account": "eosio",
      "name": "delegatebw",
      "authorization": [{
          "actor": "eosio",
          "permission": "active"
        }
      ],
      "data": "0000000000ea305500004d1a03ea3055102700000000000004535953000000001027000000000000045359530000000001"
    },{
      "account": "eosio",
      "name": "setpriv",
      "authorization": [{
          "actor": "eosio",
          "permission": "active"
        }
      ],
      "data": "00004d1a03ea305501"
    }
  ],
  "transaction_extensions": [],
  "signatures": [],
  "context_free_data": []
}

第六步,生成 producer_permissions.json 文件

假设网络中有 21 个 BP,名字分别为,blkproducera,blkproducerb,......,blkproduceru。则 producer_permissions.json 的文件内容如下所示:

$ cat producer_permissions.json
[
   {"actor": "blkproducera", "permission": "active"},
   {"actor": "blkproducerb", "permission": "active"},
   {"actor": "blkproducerc", "permission": "active"},
   {"actor": "blkproducerd", "permission": "active"},
   {"actor": "blkproducere", "permission": "active"},
   {"actor": "blkproducerf", "permission": "active"},
   {"actor": "blkproducerg", "permission": "active"},
   {"actor": "blkproducerh", "permission": "active"},
   {"actor": "blkproduceri", "permission": "active"},
   {"actor": "blkproducerj", "permission": "active"},
   {"actor": "blkproducerk", "permission": "active"},
   {"actor": "blkproducerl", "permission": "active"},
   {"actor": "blkproducerm", "permission": "active"},
   {"actor": "blkproducern", "permission": "active"},
   {"actor": "blkproducero", "permission": "active"},
   {"actor": "blkproducerp", "permission": "active"},
   {"actor": "blkproducerq", "permission": "active"},
   {"actor": "blkproducerr", "permission": "active"},
   {"actor": "blkproducers", "permission": "active"},
   {"actor": "blkproducert", "permission": "active"},
   {"actor": "blkproduceru", "permission": "active"}
]

第七步,发起多签 proposal,approve 并执行

发起 proposal:

$ cleos multisig propose_trx createsudo producer_permissions.json create_sudo_account_trx.json PROPOSER_ACCOUNT

BP 分别 approve

cleos multisig approve PROPOSER_ACCOUNT createsudo '{"actor": "BP_ACCOUNT", "permission": "active"}' -p BP_ACCOUNT

approval 达到 15/21 之后,执行该 proposal:

cleos multisig exec PROPOSER_ACCOUNT createsudo ANY_ACCOUNT

*注意,任何账户都可以执行 approval 达到 15/21 的 proposal,即使该账户不是 BP。

Proposal 执行成功后,我们可以查看 eosio.sudo 这个账户,应该可以看到类似下面的输出:

$ cleos get account eosio.sudo
privileged: true
permissions:
     owner     1:    1 eosio@active,
        active     1:    1 eosio@active,
memory:
     quota:     49.74 KiB    used:     3.33 KiB  

net bandwidth:
     staked:          1.0000 EOS           (total stake delegated from account to self)
     delegated:       0.0000 EOS           (total staked delegated to account from others)
     used:                 0 bytes
     available:        2.304 MiB  
     limit:            2.304 MiB  

cpu bandwidth:
     staked:          1.0000 EOS           (total stake delegated from account to self)
     delegated:       0.0000 EOS           (total staked delegated to account from others)
     used:                 0 us   
     available:        460.8 ms   
     limit:            460.8 ms   

producers:     <not voted>

至此,eosio.sudo 账户就创建成功了,接下来就是通过 multisig 部署 sudo 合约到该账户。

2. 使用 multisig 部署 eosio.sudo 合约

该 repo 已经包含 eosio.sudo 官方合约的编译版本,位于 eosio.sudo 目录,源码请参见:https://github.com/EOSIO/eos/tree/v1.1.4/contracts/eosio.sudo

第一步,生成部署 eosio.sudo 合约的 transaction 数据

$ cleos set contract -s -j -d eosio.sudo ./eosio.sudo/ > deploy_sudo_contract_trx.json
Reading WAST/WASM from contracts/eosio.sudo/eosio.sudo.wasm...
Using already assembled WASM...
Publishing contract...
$ cat deploy_sudo_contract_trx.json
{
  "expiration": "2018-06-29T19:55:26",
  "ref_block_num": 18544,
  "ref_block_prefix": 562790588,
  "max_net_usage_words": 0,
  "max_cpu_usage_ms": 0,
  "delay_sec": 0,
  "context_free_actions": [],
  "actions": [{
      "account": "eosio",
      "name": "setcode",
      "authorization": [{
          "actor": "eosio.sudo",
          "permission": "active"
        }
      ],
      "data": "00004d1a03ea30550000c8180061736d01000000013e0c60017f006000017e60027e7e0060017e006000017f60027f7f017f60027f7f0060037f7f7f017f60057f7e7f7f7f0060000060037e7e7e0060017f017f029d010803656e7610616374696f6e5f646174615f73697a65000403656e760c63757272656e745f74696d65000103656e760c656f73696f5f617373657274000603656e76066d656d637079000703656e7610726561645f616374696f6e5f64617461000503656e760c726571756972655f61757468000303656e760d726571756972655f6175746832000203656e760d73656e645f64656665727265640008030f0e0505050400000a05070b050b000904050170010202050301000107c7010b066d656d6f72790200165f5a6571524b3131636865636b73756d32353653315f0008165f5a6571524b3131636865636b73756d31363053315f0009165f5a6e65524b3131636865636b73756d31363053315f000a036e6f77000b305f5a4e35656f73696f3132726571756972655f6175746845524b4e535f31367065726d697373696f6e5f6c6576656c45000c155f5a4e35656f73696f347375646f34657865634576000d056170706c79000e066d656d636d700010066d616c6c6f630011046672656500140908010041000b02150d0a9a130e0b002000200141201010450b0b002000200141201010450b0d0020002001412010104100470b0a00100142c0843d80a70b0e002000290300200029030810060b9e0102017e027f410028020441206b2202210341002002360204200029030010050240024010002200418104490d002000101121020c010b410020022000410f6a4170716b22023602040b2002200010041a200041074b41101002200341186a2002410810031a2003290318100520032903182101200310013703002003200137030820032003290318200241086a2000410010074100200341206a3602040bfd0403027f047e017f4100410028020441206b220936020442002106423b2105412021044200210703400240024002400240024020064206560d0020042c00002203419f7f6a41ff017141194b0d01200341a5016a21030c020b420021082006420b580d020c030b200341d0016a41002003414f6a41ff01714105491b21030b2003ad42388642388721080b2008421f83200542ffffffff0f838621080b200441016a2104200642017c2106200820078421072005427b7c2205427a520d000b024020072002520d0042002106423b2105413021044200210703400240024002400240024020064204560d0020042c00002203419f7f6a41ff017141194b0d01200341a5016a21030c020b420021082006420b580d020c030b200341d0016a41002003414f6a41ff01714105491b21030b2003ad42388642388721080b2008421f83200542ffffffff0f838621080b200441016a2104200642017c2106200820078421072005427b7c2205427a520d000b200720015141c00010020b0240024020012000510d0042002106423b2105412021044200210703400240024002400240024020064206560d0020042c00002203419f7f6a41ff017141194b0d01200341a5016a21030c020b420021082006420b580d020c030b200341d0016a41002003414f6a41ff01714105491b21030b2003ad42388642388721080b2008421f83200542ffffffff0f838621080b200441016a2104200642017c2106200820078421072005427b7c2205427a520d000b20072002520d010b20092000370318200242808080808080a0aad700520d00200941003602142009410136021020092009290310370208200941186a200941086a100f1a0b4100200941206a3602040b8c0101047f4100280204220521042001280204210220012802002101024010002203450d00024020034180044d0d00200310112205200310041a200510140c010b410020052003410f6a4170716b22053602042005200310041a0b200020024101756a210302402002410171450d00200328020020016a28020021010b200320011100004100200436020441010b4901037f4100210502402002450d000240034020002d0000220320012d00002204470d01200141016a2101200041016a21002002417f6a22020d000c020b0b200320046b21050b20050b0900418001200010120bcd04010c7f02402001450d00024020002802c041220d0d004110210d200041c0c1006a41103602000b200141086a200141046a41077122026b200120021b210202400240024020002802c441220a200d4f0d002000200a410c6c6a4180c0006a21010240200a0d0020004184c0006a220d2802000d0020014180c000360200200d20003602000b200241046a210a034002402001280208220d200a6a20012802004b0d002001280204200d6a220d200d28020041808080807871200272360200200141086a22012001280200200a6a360200200d200d28020041808080807872360200200d41046a22010d030b2000101322010d000b0b41fcffffff0720026b2104200041c8c1006a210b200041c0c1006a210c20002802c8412203210d03402000200d410c6c6a22014188c0006a28020020014180c0006a22052802004641d0c200100220014184c0006a280200220641046a210d0340200620052802006a2107200d417c6a2208280200220941ffffffff07712101024020094100480d000240200120024f0d000340200d20016a220a20074f0d01200a280200220a4100480d012001200a41ffffffff07716a41046a22012002490d000b0b20082001200220012002491b200941808080807871723602000240200120024d0d00200d20026a200420016a41ffffffff07713602000b200120024f0d040b200d20016a41046a220d2007490d000b41002101200b4100200b28020041016a220d200d200c280200461b220d360200200d2003470d000b0b20010f0b2008200828020041808080807872360200200d0f0b41000b870501087f20002802c44121010240024041002d00a643450d0041002802a84321070c010b3f002107410041013a00a6434100200741107422073602a8430b200721030240024002400240200741ffff036a41107622023f0022084d0d00200220086b40001a4100210820023f00470d0141002802a84321030b41002108410020033602a84320074100480d0020002001410c6c6a210220074180800441808008200741ffff037122084181f8034922061b6a2008200741ffff077120061b6b20076b2107024041002d00a6430d003f002103410041013a00a6434100200341107422033602a8430b20024180c0006a210220074100480d01200321060240200741076a417871220520036a41ffff036a41107622083f0022044d0d00200820046b40001a20083f00470d0241002802a84321060b4100200620056a3602a8432003417f460d0120002001410c6c6a22014184c0006a2802002206200228020022086a2003460d020240200820014188c0006a22052802002201460d00200620016a2206200628020041808080807871417c20016b20086a72360200200520022802003602002006200628020041ffffffff07713602000b200041c4c1006a2202200228020041016a220236020020002002410c6c6a22004184c0006a200336020020004180c0006a220820073602000b20080f0b02402002280200220820002001410c6c6a22034188c0006a22012802002207460d0020034184c0006a28020020076a2203200328020041808080807871417c20076b20086a72360200200120022802003602002003200328020041ffffffff07713602000b2000200041c4c1006a220728020041016a22033602c0412007200336020041000f0b2002200820076a36020020020b7b01037f024002402000450d0041002802c04222024101480d004180c10021032002410c6c4180c1006a21010340200341046a2802002202450d010240200241046a20004b0d00200220032802006a20004b0d030b2003410c6a22032001490d000b0b0f0b2000417c6a2203200328020041ffffffff07713602000b0300000b0bcf01060041040b04b04900000041100b0572656164000041200b086f6e6572726f72000041300b06656f73696f000041c0000b406f6e6572726f7220616374696f6e277320617265206f6e6c792076616c69642066726f6d207468652022656f73696f222073797374656d206163636f756e74000041d0c2000b566d616c6c6f635f66726f6d5f6672656564207761732064657369676e656420746f206f6e6c792062652063616c6c6564206166746572205f686561702077617320636f6d706c6574656c7920616c6c6f636174656400"
    },{
      "account": "eosio",
      "name": "setabi",
      "authorization": [{
          "actor": "eosio.sudo",
          "permission": "active"
        }
      ],
      "data": "00004d1a03ea3055df040e656f73696f3a3a6162692f312e30030c6163636f756e745f6e616d65046e616d650f7065726d697373696f6e5f6e616d65046e616d650b616374696f6e5f6e616d65046e616d6506107065726d697373696f6e5f6c6576656c0002056163746f720c6163636f756e745f6e616d650a7065726d697373696f6e0f7065726d697373696f6e5f6e616d6506616374696f6e0004076163636f756e740c6163636f756e745f6e616d65046e616d650b616374696f6e5f6e616d650d617574686f72697a6174696f6e127065726d697373696f6e5f6c6576656c5b5d0464617461056279746573127472616e73616374696f6e5f68656164657200060a65787069726174696f6e0e74696d655f706f696e745f7365630d7265665f626c6f636b5f6e756d0675696e743136107265665f626c6f636b5f7072656669780675696e743332136d61785f6e65745f75736167655f776f7264730976617275696e743332106d61785f6370755f75736167655f6d730575696e74380964656c61795f7365630976617275696e74333209657874656e73696f6e000204747970650675696e74313604646174610562797465730b7472616e73616374696f6e127472616e73616374696f6e5f6865616465720314636f6e746578745f667265655f616374696f6e7308616374696f6e5b5d07616374696f6e7308616374696f6e5b5d167472616e73616374696f6e5f657874656e73696f6e730b657874656e73696f6e5b5d046578656300020865786563757465720c6163636f756e745f6e616d65037472780b7472616e73616374696f6e01000000000080545704657865630000000000"
    }
  ],
  "transaction_extensions": [],
  "signatures": [],
  "context_free_data": []
}

将上述文件中的 ref_block_num 和 ref_block_prefix 改为 0,将 expiration 的日期向后推迟一定的时间(比如 1 天后),这个时间表示 transaction 的过期时间。由于这个 transaction 最终需要等待 BP 多签生效,因此过期时间不能太短。

第二步,Propose, Approve and exec

假设 producer 依然是创建账户时候的情况,我们依然沿用 producer_permissions.json 文件。

发起 proposal:

$ cleos multisig propose_trx deploysudo producer_permissions.json deploy_sudo_contract_trx.json PROPOSER_ACCOUNT

BP 分别 approve

cleos multisig approve PROPOSER_ACCOUNT deploysudo '{"actor": "BP_ACCOUNT", "permission": "active"}' -p BP_ACCOUNT

approval 达到 15/21 之后,执行该 proposal:

cleos multisig exec PROPOSER_ACCOUNT deploysudo BP_ACCOUNT

*注意,任何账户都可以执行 approval 达到 15/21 的 proposal,即使该账户不是 BP。

Proposal 执行成功后,我们可以查看 eosio.sudo 的 code,应该可以看到类似下面的输出:

cleos get code eosio.sudo
1b3456a5eca28bcaca7a2a3360fbb2a72b9772a416c8e11a303bcb26bfe3263c

至此,eosio.sudo 合约就通过多签的方式部署成功了。

接下来,我们可以试着使用多签调用 sudo 合约,执行一些诸如更改 key,转账等操作,验证 sudo 合约可以正确执行。

转载自:https://github.com/cryptokylin/contract-upgrade-manual/blob/master/deploy-sudo-contract.md