您正在查看: Surou 发布的文章

EOS 插入数据的RAM消耗

context.add_ram_usage(
    l.account,
    (int64_t)(config::billable_size_v<permission_link_object>)
);

https://github.com/EOSIO/eos/blob/3fddb727b8f3615917707281dfd3dd3cc5d3d66d/libraries/chain/eosio_contract.cpp#L340

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黑名单合同整合

转载自:https://github.com/firesWu/awesome-eosio-plugins

源码解析 区块的分叉处理

在区块链上, 分叉是一个老话题了, 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