context.add_ram_usage(
l.account,
(int64_t)(config::billable_size_v<permission_link_object>)
);
EOS 插入数据的RAM消耗
eosio action abi 部分语义化
账号 | 合约 | Action | 语义 | 备注 |
---|---|---|---|---|
eosio | eosio.system | delegatebw | 抵押资源 | |
undelegatebw | 赎回资源 | |||
buyramkbytes | 购买内存 | |||
sellram | 卖出内存 | |||
refund | 赎回退款 | |||
voteproducer | 投票 | |||
claimrewards | 申请奖励 | |||
bidname | 竞拍账户 | |||
bidrefund | 竞拍退还 | |||
newaccount | 创建账户 | |||
updateauth | 更新授权 | |||
deleteauth | 删除授权 | |||
linkauth | 链接授权 | |||
unlinkauth | 取消链接授权 | |||
canceldelay | 取消延迟交易 | |||
regproducer | 注册生产者 | |||
unregprod | 取消生产者 | |||
eosio.token | eosio.token | issue | 代币增发 | |
transfer | 代币转账 | |||
create | 创建代币 |
ref_block_num 和 ref_block_prefix
先简单记一下,后面再做补充
主要作用是判断下前后交易是不是在一个链上,也就是有没有分叉。
参考
https://github.com/EOSIO/eos/issues/4659
https://eos.live/detail/17094
https://github.com/EOSIO/eosjs/issues/151
http://www.zhongruitech.com/955126221.html
一些三方推荐插件
队列插件:
将区块链数据推入队列
kafka_plugin - 卡夫卡
eos_zmq_plugin - ZeroMQ
eos-rabbitmq-plugin - RabbitMQ
数据库插件:
将区块链数据同步到数据库中
eosio_sql_plugin - 基于SOCI的SQL数据库
eos_sql_db_plugin - MySQL数据库
elasticsearch_plugin - ElasticSearch
推送插件:
通过其他协议将区块链数据通知给消费者:
eosio-watcher-plugin - 针对链操作的HTTP POST到端点
eosio_block_subscription_plugin - 用于链操作的推送通知的TCP服务器
调试插件
eosio_all_code_dump_plugin - 将所有合同代码转储到目录
Block Producer插件:
eos-producer-heartbeat-plugin - BP Heartbeat
blacklist_plugin - 与BP黑名单合同整合
源码解析 区块的分叉处理
在区块链上, 分叉是一个老话题了, EOS 也会出现分叉的情况, 下面我们来讲讲在分叉情况它是如何切换到最长链的。
EOS 的生产者是根据 active_schedule 的顺序来生产区块的,但是在决定当前区块生产者的时候是根据 block_time 来的选择的。 假如当前 BP1 生产了一个 block_num 为 100 的块,而BP2和BP3都接受到了,此时根据block_time是 BP2 生产 block_num 为 101 的块, 但 BP3 因为网络原因没收到 BP2 产生的块, 导致它也开始生产 block_num 为 101 的块了, 这时候出现了分叉。 那么哪条链会被最终确认呢,换句话说 2个 block_num 为 101 的块谁会被不可逆,谁会被抛弃, 这要看两条链上的生产者数量, 生产者多的那条链增长速度会比较快, 所以区块更快被确认,比另一条链先不可逆, 这时候就会切换到长的链,而丢弃短的链了。 一般情况下,其他节点接受完BP2的块后都在等待BP3的出块,BP3的出块将BP2的分支覆盖掉了, 基本所有节点都切换到了BP3的链上并开始生产,所以 BP2 的块被丢弃的可能性很大。
先讲下分叉的大致流程,当一个节点收到同个 block_num 的会都 insert 进 fork_multi_index_type 对应的表里,根据 block_state 的 header.previous 形成多叉树(不了解多叉树的读者可以百度一下哈)。每进一个需要分叉的块都会先切换到该块对应的链上。一旦 block_num 最小的确定是哪个块不可逆后,其他分叉下相同 block_num 的块就会被删除。至于为什么不直接把其他分叉全删了。 如果该分叉还有人在生产块,那么该块在接入节点得时候就会出现 unlinkable_block 的错误了,这块不详解,感兴趣的可以自行了解。
下面分析代码,因为分叉只有接受别人的块才会出现,所以从 push_block 开始看起。
void push_block( const signed_block_ptr& b, controller::block_status s ) {
// ...
if ( read_mode != db_read_mode::IRREVERSIBLE ) {
maybe_switch_forks( s );
}
// ...
} FC_LOG_AND_RETHROW( )
}
void maybe_switch_forks( controller::block_status s = controller::block_status::complete ) {
auto new_head = fork_db.head();
// 无分叉情况,正常叠加块
if( new_head->header.previous == head->id ) {
try {
apply_block( new_head->block, s );
fork_db.mark_in_current_chain( new_head, true );
fork_db.set_validity( new_head, true );
head = new_head;
} catch ( const fc::exception& e ) {
fork_db.set_validity( new_head, false ); // Removes new_head from fork_db index, so no need to mark it as not in the current chain.
throw;
}
// 头 id 不同,出现分叉情况
} else if( new_head->id != head->id ) {
ilog("switching forks from ${current_head_id} (block number ${current_head_num}) to ${new_head_id} (block number ${new_head_num})",
("current_head_id", head->id)("current_head_num", head->block_num)("new_head_id", new_head->id)("new_head_num", new_head->block_num) );
// 获取两个分支上对应的块
auto branches = fork_db.fetch_branch_from( new_head->id, head->id );
// 将前一分支的块都标记成不在当前使用分支。
for( auto itr = branches.second.begin(); itr != branches.second.end(); ++itr ) {
fork_db.mark_in_current_chain( *itr , false );
pop_block();
}
EOS_ASSERT( self.head_block_id() == branches.second.back()->header.previous, fork_database_exception,
"loss of sync between fork_db and chainbase during fork switch" ); // _should_ never fail
// 切换分支, apply 当前分支的块
for( auto ritr = branches.first.rbegin(); ritr != branches.first.rend(); ++ritr) {
optional<fc::exception> except;
try {
apply_block( (*ritr)->block, (*ritr)->validated ? controller::block_status::validated : controller::block_status::complete );
head = *ritr;
fork_db.mark_in_current_chain( *ritr, true );
(*ritr)->validated = true;
}
catch (const fc::exception& e) { except = e; }
if (except) {
elog("exception thrown while switching forks ${e}", ("e",except->to_detail_string()));
// ritr currently points to the block that threw
// if we mark it invalid it will automatically remove all forks built off it.
fork_db.set_validity( *ritr, false );
// pop all blocks from the bad fork
// ritr base is a forward itr to the last block successfully applied
auto applied_itr = ritr.base();
for( auto itr = applied_itr; itr != branches.first.end(); ++itr ) {
fork_db.mark_in_current_chain( *itr , false );
pop_block();
}
EOS_ASSERT( self.head_block_id() == branches.second.back()->header.previous, fork_database_exception,
"loss of sync between fork_db and chainbase during fork switch reversal" ); // _should_ never fail
// re-apply good blocks
for( auto ritr = branches.second.rbegin(); ritr != branches.second.rend(); ++ritr ) {
apply_block( (*ritr)->block, controller::block_status::validated /* we previously validated these blocks*/ );
head = *ritr;
fork_db.mark_in_current_chain( *ritr, true );
}
// 抛出一个切换分支的异常。
throw *except;
} // end if exception
} /// end for each block in branch
ilog("successfully switched fork to new head ${new_head_id}", ("new_head_id", new_head->id));
}
} /// push_block
void fork_database::prune( const block_state_ptr& h ) {
auto num = h->block_num;
auto& by_bn = my->index.get<by_block_num>();
auto bni = by_bn.begin();
while( bni != by_bn.end() && (*bni)->block_num < num ) {
prune( *bni );
bni = by_bn.begin();
}
auto itr = my->index.find( h->id );
if( itr != my->index.end() ) {
irreversible(*itr);
my->index.erase(itr);
}
// 当 num 对应的块不可逆化, 其他分支下同等 block_num 就会被删除
auto& numidx = my->index.get<by_block_num>();
auto nitr = numidx.lower_bound( num );
while( nitr != numidx.end() && (*nitr)->block_num == num ) {
auto itr_to_remove = nitr;
++nitr;
auto id = (*itr_to_remove)->id;
remove( id );
}
}
其实分叉处理就是一个塑造出一个多叉树, 当多叉树上有节点成为不可逆的时候就可以裁剪。
转载自:https://eos.live/detail/16153