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

通过源码分析,onblock动作不仅管理了生产者的结算动作,还管理了链上账户名拍卖工作。与上面producers_table中的二级索引prototalvote的功能相同,name_bid_table状态表的二级索引highbid也是用来对结果集进行排序的,具体声明如下:

1. typedef eosio::multi_index< N(namebids), name_bid, 2. indexed_by<N(highbid), const_mem_fun<name_bid, uint64_t, &name_bid::by_high_bid > > > name_bid_table;

二级索引highbid同样使用了const_mem_fun模板定义了提取器内容,其中提取规则也就是排序依赖为name_bid结构体的by_high_bid函数。

1. struct name_bid { 2. account_name newname; 3. account_name high_bidder; 4. int64_t high_bid = 0; // 若该项值为负数,则证明已获得拍卖名字,等待认领。 5. uint64_t last_bid_time = 0; 6. auto primary_key()const { return newname; } // 主键 7. uint64_t by_high_bid()const { return static_cast<uint64_t>(-high_bid); } // 返回报价字段的值 8. }; 初始化主币

EOSIO在将合约迁移到一个新创建的repo 以后,为系统合约加入了主币初始化init的操作。下面仍旧通过源码分析该操作的内容。

1. /** 2. * @brief 初始化主币 3. * 4. * @param version 版本号 5. * @param core 初始化的主币对象 6. */ 7. void system_contract::init( unsigned_int version, symbol core ) { 8. require_auth( _self ); // 判断是否拥有合约主人身份。 9. eosio_assert( version.value == 0, "unsupported version for init action" ); // 对于初始化动作,版本号只能为0 10. auto itr = _rammarket.find(ramcore_symbol.raw()); // 在内存表中查找主币对象,如果已查到说明初始化操作已完成,则推出当前进程。 11. eosio_assert( itr == _rammarket.end(), "system contract has already been initialized" ); 12. // 此处调用了token的get_supply函数,获得主币供应量 13. auto system_token_supply = eosio::token::get_supply(token_account, core.code() ); 14. // 校验token的符号以及小数点精确位数是否一致。 15. eosio_assert( system_token_supply.symbol == core, "specified core symbol does not exist (precision mismatch)" ); 16. // 校验主币的供应量是否大于0 17. eosio_assert( system_token_supply.amount > 0, "system token supply must be greater than 0" ); 18. _rammarket.emplace( _self, [&]( auto& m ) { // 内存市场状态表新增数据 19. m.supply.amount = 100000000000000ll; 20. m.supply.symbol = ramcore_symbol; 21. m.base.balance.amount = int64_t(_gstate.free_ram()); 22. m.base.balance.symbol = ram_symbol; 23. m.quote.balance.amount = system_token_supply.amount / 1000; 24. m.quote.balance.symbol = core; 25. }); 26. }

初始化主币的操作在节点启动时会被调用到,这个操作一般被执行成功一次就不会再被调用。初始化主币的命令时:

$ cleos push action eosio init '["0", "4,SYS"]' -p eosio@active

传入了两个参数,第一个参数时0,上面介绍了是版本的含义。第二个参数的值为“4,SYS”,是token符号对象。SYS定义了主币的符号名称,4是主币的小数点精度,这个值可以是0到18。前面在token转账的过程中,校验了token的符号对象,校验工作就包含了对符号名称以及小数点精度位数的校验。

非常规账户竞拍

前面介绍system合约的onblock动作以及init动作都涉及到了账户竞拍的逻辑。在EOS中,常规账户的名称要求为必须12个字符同时中间不能包含点,而非常规账户名则可以少于12个字符并且可包含点,加入后缀。这种非常规账户的名称显然是稀有且具备个性的,因此EOS加入了这一部分的竞拍市场机制。该动作是由system系统合约的bidname完成。下面仍旧分析其源码实现。

1. /** 2. * @brief 账户名拍卖 3. * 4. * @param bidder 竞拍者 5. * @param newname 标的账户名 6. * @param bid 报价 7. */ 8. void system_contract::bidname( name bidder, name newname, asset bid ) { 9. require_auth( bidder ); // 校验竞拍者是否本人操作 10. // 校验标的账户名是否符合高级后缀。 11. eosio_assert( newname.suffix() == newname, "you can only bid on top-level suffix" ); 12. eosio_assert( (bool)newname, "the empty name is not a valid account name to bid on" );//校验标的是否为空 13. eosio_assert( (newname.value & 0xFull) == 0, "13 character names are not valid account names to bid on" );//13个字符长度的标的不允许竞拍 14. // 常规账户长度为12位且不包含点,只有非常规账户才可以参与竞拍,即小于12个字符的,或者包含点的。 15. eosio_assert( (newname.value & 0x1F0ull) == 0, "accounts with 12 character names and no dots can be created without bidding required" ); 16. eosio_assert( !is_account( newname ), "account already exists" );// 校验标的账户是否已存在。 17. eosio_assert( bid.symbol == core_symbol(), "asset must be system token" );// 校验报价资产必须是主币 18. eosio_assert( bid.amount > 0, "insufficient bid" );// 校验报价必须正数 19. // 经过以上重重校验,可以进行实际拍卖环节。首先发起转账,将竞拍报价从竞拍者手中转账到eosio.names账户(该账户主管名称拍卖) 20. INLINE_ACTION_SENDER(eosio::token, transfer)( 21. token_account, { {bidder, active_permission} }, 22. { bidder, names_account, bid, std::string("bid name ")+ newname.to_string() } 23. ); 24. // 创建当前合约的name_bid_table状态表的实例bids 25. name_bid_table bids(_self, _self.value); 26. print( name{bidder}, " bid ", bid, " on ", name{newname}, "\n" ); 27. auto current = bids.find( newname.value ); // 先查询是否已存在该标的的历史报价数据 28. if( current == bids.end() ) { // 如果不存在历史报价数据,则新建 29. bids.emplace( bidder, [&]( auto& b ) { // 添加该标的的首单竞拍相关字段到状态表。 30. b.newname = newname; 31. b.high_bidder = bidder; 32. b.high_bid = bid.amount; 33. b.last_bid_time = current_time_point(); 34. }); 35. } else { // 如果已经存在历史报价数据,则处理该标的的最高报价 36. // 历史最高报价high_bid已被置为负数,则说明该已成功交易,竞拍关闭。 37. eosio_assert( current->high_bid > 0, "this auction has already closed" ); 38. // 此次新的报价必须高于该标的的历史最高报价的10%,这是竞拍规则。 39. eosio_assert( bid.amount - current->high_bid > (current->high_bid / 10), "must increase bid by 10%" ); 40. // 如果该标的的当前最高报价已经是当前竞拍者本人,则不需要执行下面的逻辑。 41. eosio_assert( current->high_bidder != bidder, "account is already highest bidder" ); 42. // 获得竞拍退款状态表big_refund_table的实例refunds_table,传入当前竞拍动作。 43. bid_refund_table refunds_table(_self, newname.value); 44. auto it = refunds_table.find( current->high_bidder.value ); 45. if ( it != refunds_table.end() ) { 46. // 如果在竞拍退款表中找到当前竞拍价格相同的,则更新该条数据对象,增加退款金额为最高报价,以主币形式结算。 47. refunds_table.modify( it, same_payer, [&](auto& r) { 48. r.amount += asset( current->high_bid, core_symbol() ); 49. }); 50. } else { 51. // 如果未找到相同最高报价的,则新增一条数据对象,插入当前最高报价者以及报价价格。 52. refunds_table.emplace( bidder, [&](auto& r) { 53. r.bidder = current->high_bidder; 54. r.amount = asset( current->high_bid, core_symbol() ); 55. }); 56. } 57. // 打包交易,插入bidrefund动作,传入最高报价者以及标的。 58. transaction t; 59. t.actions.emplace_back( permission_level{_self, active_permission}, 60. _self, "bidrefund"_n, 61. std::make_tuple( current->high_bidder, newname ) 62. ); 63. t.delay_sec = 0;// 定义延迟时间 64. // 定义延迟id 65. uint128_t deferred_id = (uint128_t(newname.value) << 64) | current->high_bidder.value; 66. cancel_deferred( deferred_id ); // 按延迟id取消延迟交易 67. t.send( deferred_id, bidder ); // 发送延迟交易 68. // 最后修改name_bid_table状态表的实例bids,将当前竞拍动作更新到该标的对象,包括最高报价者、最高报价以及时间。 69. bids.modify( current, bidder, [&]( auto& b ) { 70. b.high_bidder = bidder; 71. b.high_bid = bid.amount; 72. b.last_bid_time = current_time_point(); 73. }); 74. } 75. } 创建账户

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

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