【精】EOS智能合约:system系统合约源码分析 (7)

prototalvote索引的定义在multi_index状态表producers_table。

1. typedef eosio::multi_index< N(producers), producer_info, 2. indexed_by<N(prototalvote), const_mem_fun<producer_info, double, &producer_info::by_votes> > 3. > producers_table;

producers_table状态表的声明中,定义了表名为producers,数据结构为producer_info,然后定义了二级索引,名称为prototalvote,该索引的提取器是操作数据结构produer_info对象,提取类型为double,提取规则是produer_info的by_votes方法。
N是一个宏,可以把base32编码后的字符串转换为uint64。

最新版本的eosio.contracts已经改为"useraaaaaaaa"_n的方式代替了N("useraaaaaaaa")。

1. /** 2. * @brief 用于从X的base32编码字符串解释生成编译的uint64 t 3. * 4. * @param X - 代表名称的字符串 5. * @return constexpr uint64_t - 64位无符整型值,可代表一个名称 6. */ 7. #define N(X) ::eosio::string_to_name(#X)

下面研究multi_index的二级索引indexed_by的定义源码。

1. template<uint64_t IndexName, typename Extractor> 2. struct indexed_by { 3. enum constants { index_name = IndexName }; 4. typedef Extractor secondary_extractor_type; 5. };

结构体indexed_by是用来为multi_index状态表创建索引实例的。EOS中支持指定最多16个二级索引。接收两个参数,一个是索引名称,另一个是提取器。提取器采用了const_mem_fun模板,该模板有效定义了提取器的数据范围,数据类型以及提取规则(方法)。回到producers_table表的数据结构produer_info结构体中。

1. struct producer_info { 2. account_name owner; // producer账户名 3. double total_votes = 0; // 当前producer的总投票数 4. eosio::public_key producer_key; // 当前producer的公钥 5. bool is_active = true; // 当前producer是否有效 6. std::string url; // 当前producer的介绍url,可以是官网 7. uint32_t unpaid_blocks = 0; // 未领奖励的区块数量 8. uint64_t last_claim_time = 0; // 上一次认领奖励的时间 9. uint16_t location = 0; // 当前producer的位置 10. 11. uint64_t primary_key()const { return owner; }// producer_info结构体的主键,将被状态表producer_table作为第一索引。 12. double by_votes()const { return is_active ? -total_votes : total_votes; } // 按投票(排序),注意排序并不是在此实现,此方法只是为了区分,将失效producer的总票数置为其相反数 13. bool active()const { return is_active; } // 判断是否有效 14. void deactivate() { producer_key = public_key(); is_active = false; } // 将当前生产者设置为失效的动作。 15. 16. // 注意:明确序列化宏不是必要的,用在此处是为了提高编译效率 17. EOSLIB_SERIALIZE( producer_info, (owner)(total_votes)(producer_key)(is_active)(url) 18. (unpaid_blocks)(last_claim_time)(location) ) 19. }; 系统合约管理出块

eosio.system的onblock可以管理生产者的出块动作,参与每0.5秒的出块工作。下面通过注释分析该动作的源码。

1. /** 2. * @brief system合约的出块动作 3. * 4. * @param timestamp 时间戳 5. * @param producer 生产者 6. */ 7. void system_contract::onblock( block_timestamp timestamp, account_name producer ) { 8. using namespace eosio; 9. // 该动作是由eosio创世账户执行,要先校验是否有该账户权限。 10. require_auth(N(eosio)); 11. // 当总激活抵押数小于最低激活抵押额时,停止动作。 12. if( _gstate.total_activated_stake < min_activated_stake ) 13. return; 14. 15. // 当预投票开始时,更新时间为当前时间。 16. if( _gstate.last_pervote_bucket_fill == 0 ) /// start the presses 17. _gstate.last_pervote_bucket_fill = current_time(); 18. 19. // 在生产者集合中查询传入的生产者账号 20. auto prod = _producers.find(producer); 21. if ( prod != _producers.end() ) { // 成功查到结果 22. _gstate.total_unpaid_blocks++; // 全局未结算区块数加一 23. _producers.modify( prod, 0, [&](auto& p ) { // 当前生产者未结算数加一 24. p.unpaid_blocks++; 25. }); 26. } 27. // 注意:每分钟只更新区块生产者一次,0.5秒更新一次区块时间。 28. if( timestamp.slot - _gstate.last_producer_schedule_update.slot > 120 ){ update_elected_producers( timestamp ); // 更新已入选生产节点,见上小节 29. // 账户名称拍卖工作的更新操作,注意:每天只能交易一次。 30. if( (timestamp.slot - _gstate.last_name_close.slot) > blocks_per_day ) { 31. name_bid_table bids(_self,_self); // 拍卖账户名的状态表实例。 32. auto idx = bids.get_index<N(highbid)>(); // 得到二级索引的结果集,按照出价高低排序。 33. auto highest = idx.begin(); 34. /** 35. * @brief 判断是否符合拍卖结束条件。 36. * 条件包括: 37. * 1,状态表不为空 38. * 2,出价大于0 39. * 3,出价的时间在一秒钟之内 40. * 4,抵押激活时间大于0 41. * 5,当前时间至少超过抵押激活14天的时间 42. */ 43. if( highest != idx.end() && 44. highest->high_bid > 0 && 45. highest->last_bid_time < (current_time() - useconds_per_day) && _gstate.thresh_activated_stake_time > 0 && 46. (current_time() - _gstate.thresh_activated_stake_time) > 14 * useconds_per_day ) { 47. _gstate.last_name_close = timestamp;// 记录成功交易时间 48. idx.modify( highest, 0, [&]( auto& b ){ 49. b.high_bid = -b.high_bid; // 该笔拍卖报价已兑现,则置相反数,可作为记录的同时不参与其他有效报价。 50. }); 51. } 52. } 53. } 54. }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wsspgz.html