@(作者:天晓)
昨天,天晓发了《EOS账号之间有“对眼”?分析eosio.token源码来了解https://bihu.com/article/813854》,引起一些小伙伴的关注,不过现在看来那个解释还不完善、不够深入。
“对眼”现象是什么?方便没看昨天文章的朋友了解,当A账号第一次向B账号发起一笔转账,要消耗一笔大约0.23KB的RAM资源,圊呓语大哥称之为“对眼”现象。
EOS账号“对眼”很奇怪
当时,天晓是通过分析系统自带的代币合约“contracts/eosio.token”,试图解释“对眼”现象。不过到晚上,咱们币乎的技术大神荆凯提出他发现的问题。
第一次a给b转账时候,消耗RAM建立记录;第二次a给b转账,不消耗RAM,只更新记录。这两步,之前的说法还能解释。
从账号c给b第一次转账时候 又会消耗c的RAM;c给b第二次转账,不消耗RAM。后面这两步就无法解释了,但这种现象让天晓想起以前看过一位大神“而耳”的文章。(虽然“而耳”并不是大V,不过他是专门开发EOS游戏的,对EOS技术研究很深入,所以天晓很早也就关注他了,可惜他现在已经不再更新文章。)
“而耳”其中一篇《eos智能合约执行谁来承担内存空间,multi_index参数说明https://bihu.com/article/573139》,提出他的一个观点“eos里谁去修改某个表的一条记录,这条记录所用存储空间就转移到谁的身上。所以在设计数据结构时要重点考虑,否则一个活跃的玩家很快就不能操作了。”
“而耳”从游戏开发的角度,介绍了调取智能合约时可能发生的情况,不过也刚好解释了荆凯提出的问题。
这个问题不是说昨天的那个代码分析完全错了,而是不够深入。咱们先看回昨天那段代码:
void token::add_balance( account_name owner, asset value, account_name RAM_payer )
{
accounts to_acnts( _self, owner );
auto to = to_acnts.find( value.symbol.name() );
if( to == to_acnts.end() ) {
to_acnts.emplace( RAM_payer, [&]( auto& a ){
a.balance = value;
});
} else {
to_acnts.modify( to, 0, [&]( auto& a ) {
a.balance += value;
});
}
}
昨天解释说,如果代币接收方此前没有该代币,则发送方需要消耗RAM用于在“to_acnts”表中创建记录并更新余额;如果此前有记录,那么不需再支付RAM,直接增加余额就行。
其实后面部分modify方法还需要深入分析,那也会对RAM进行操作。据“而耳”介绍,modify方法最终会调用db_update_i64。我们可以通过以下链接找到db_update_i64方法的源码:
https://github.com/EOS-Mainnet/eos/blob/master/libraries/chain/apply_context.cpp
void apply_context::db_update_i64( int iterator, account_name payer, const char* buffer, size_t buffer_size ) {
const key_value_object& obj = keyval_cache.get( iterator );
const auto& table_obj = keyval_cache.get_table( obj.t_id );
FC_ASSERT( table_obj.code == receiver, "db access violation" );
// require_write_lock( table_obj.scope );
const int64_t overhead = config::billable_size_v<key_value_object>;
int64_t old_size = (int64_t)(obj.value.size() + overhead);
int64_t new_size = (int64_t)(buffer_size + overhead);
if( payer == account_name() ) payer = obj.payer;
if( account_name(obj.payer) != payer ) {
// refund the existing payer
update_db_usage( obj.payer, -(old_size) );
// charge the new payer
update_db_usage( payer, (new_size));
} else if(old_size != new_size) {
// charge/refund the existing payer the difference
update_db_usage( obj.payer, new_size - old_size);
}
db.modify( obj, [&]( auto& o ) {
o.value.resize( buffer_size );
memcpy( o.value.data(), buffer, buffer_size );
o.payer = payer;
});
}
在这个函数中可以看到有几个if的判断语句,如果修改记录的账户还是之前的,那么还会判断消耗的RAM大小是否产生变化(记录一个账户余额消耗0.23KB,但其他智能合约的记录未必也是);如果修改记录的账户不是之前的,那么之前用户的RAM会释放,而正在调用合约修改记录的用户将消耗RAM。因此,这就很好地解释了荆凯发现的问题。
这也意味着,现在所有项目空投方虽然需要预先消耗大量的RAM,但随着用户对代币的使用,那么这部分RAM是会逐步释放,然后由用户们承担。
另外,issue(发行代币)、transfer(转账)等操作调用这些代码,不是说只要把数据更新到RAM就行,这些操作也是要上链的,可以查询交易哈希。
参考文章:
eos智能合约执行谁来承担内存空间,multi_index参数说明
https://bihu.com/article/573139
早圊语说-EOS的转账/RAM#市场永远是对的
https://bihu.com/article/810800
欢迎给EOS Cafe Calgary投票
EOS Cafe Calgary是一家领先的节点,致力于提高EOS的使用率和价值。我们的目标是利用EOS的力量,提供教育、基础设施和DAPP(分布式应用),以丰富全球EOS生态系统。大家投票的时候,欢迎给EOS Cafe Calgary投一票,我们的账号名是eoscafeblock。也感谢大家对我们EOS Cafe Calgary的支持!
EOS Cafe Calgary is a leading block producer candidate that is committed to increasing the adoption and value of EOS. Our goal is to harness the power of EOS to provide education, infrastructure and decentralized applications to enrich the global EOS ecosystem.
网友评论