见证人选择机制

前面文章bitshares基本概念详解【见证人】聊过见证人的基本概念,当时想着另写文章来聊见证人的切换等,终于找到时间来研究下这方面的内容。

活动见证人打包排序

db_witness_schedule.cpp

void database::update_witness_schedule()

{

  const witness_schedule_object& wso = witness_schedule_id_type()(*this);

  const global_property_object& gpo = get_global_properties();

  if( head_block_num() % gpo.active_witnesses.size() == 0 )

  {

      modify( wso, [&]( witness_schedule_object& _wso )

      {

        _wso.current_shuffled_witnesses.clear();

        _wso.current_shuffled_witnesses.reserve( gpo.active_witnesses.size() );

        for( const witness_id_type& w : gpo.active_witnesses )

            _wso.current_shuffled_witnesses.push_back( w );

        auto now_hi = uint64_t(head_block_time().sec_since_epoch()) << 32;

        for( uint32_t i = 0; i < _wso.current_shuffled_witnesses.size(); ++i )

        {

            /// High performance random generator

            /// http://xorshift.di.unimi.it/

            uint64_t k = now_hi + uint64_t(i)*2685821657736338717ULL;

            k ^= (k >> 12);

            k ^= (k << 25);

            k ^= (k >> 27);

            k *= 2685821657736338717ULL;

            uint32_t jmax = _wso.current_shuffled_witnesses.size() - i;

            uint32_t j = i + k%jmax;

            std::swap( _wso.current_shuffled_witnesses[i],

                      _wso.current_shuffled_witnesses[j] );

        }

      });

  }

}

这个函数做了活动见证人的随机排序,从代码看是用了一个高性能的随机数生成器,然后根据活动见证人个数在for循环中每次执行一个随机换位。

代码中有 head_block_num() % gpo.active_witnesses.size() == 0 的判断,也就是说每个活动见证人都打包过一次后重新排序。

update_witness_schedule这个函数会在

void database::_apply_block( const signed_block& next_block )

中调用,也就是每个块调用一次。

见证人选择

那么上面的 active_witnesses 又是怎么确定的呢?

初始见证人

首先在init_genesis中会对增加指定的初始见证人,并按序加到current_shuffled_witnesses数组中,如下:

db_init.cpp

  // Set active witnesses

  modify(get_global_properties(), [&](global_property_object& p) {

      for( uint32_t i = 1; i <= genesis_state.initial_active_witnesses; ++i )

      {

        p.active_witnesses.insert(witness_id_type(i));

      }

  });

更新见证人

同样的在 _apply_block 中会做维护时间是否到达的判断,如果到达链维护时间则执行维护函数perform_chain_maintenance

  bool maint_needed = (dynamic_global_props.next_maintenance_time <= next_block.timestamp);

  ...

  // Are we at the maintenance interval?

  if( maint_needed )

      perform_chain_maintenance(next_block, global_props);

在这个函数中会调用 update_active_witnesses()函数。

db_maint.cpp

void database::update_active_witnesses()

{ try {

  assert( _witness_count_histogram_buffer.size() > 0 );

  share_type stake_target = (_total_voting_stake-_witness_count_histogram_buffer[0]) / 2;

  /// accounts that vote for 0 or 1 witness do not get to express an opinion on

  /// the number of witnesses to have (they abstain and are non-voting accounts)

  share_type stake_tally = 0;

  size_t witness_count = 0;

  if( stake_target > 0 )

  {

      while( (witness_count < _witness_count_histogram_buffer.size() - 1)

            && (stake_tally <= stake_target) )

      {

        stake_tally += _witness_count_histogram_buffer[++witness_count];

      }

  }

  const auto& all_witnesses = this->get_index_type().indices(); 

  for (const witness_object& witness : all_witnesses)

      database_check_witness(*this,witness);

  const chain_property_object& cpo = get_chain_properties();

  auto wits = sort_votable_objects(std::max(witness_count*2+1, (size_t)cpo.immutable_parameters.min_witness_count)); 

  const global_property_object& gpo = get_global_properties(); 

  for( const witness_object& wit : all_witnesses )

  {

      modify( wit, [&]( witness_object& obj ){

              obj.total_votes = _vote_tally_buffer[wit.vote_id];

              });

  }

  // Update witness authority

  modify( get(GRAPHENE_WITNESS_ACCOUNT), [&]( account_object& a )

  {

      if( head_block_time() < HARDFORK_533_TIME )

      {

        uint64_t total_votes = 0;

        map weights;

        a.active.weight_threshold = 0;

        a.active.clear();

        for( const witness_object& wit : wits )

        {

            weights.emplace(wit.witness_account, _vote_tally_buffer[wit.vote_id]);

            total_votes += _vote_tally_buffer[wit.vote_id];           

        }

        // total_votes is 64 bits. Subtract the number of leading low bits from 64 to get the number of useful bits,

        // then I want to keep the most significant 16 bits of what's left.

        int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(total_votes)) - 15, 0);

        for( const auto& weight : weights )

        {

            // Ensure that everyone has at least one vote. Zero weights aren't allowed.

            uint16_t votes = std::max((weight.second >> bits_to_drop), uint64_t(1) );

            a.active.account_auths[weight.first] += votes;

            a.active.weight_threshold += votes;

        }

        a.active.weight_threshold /= 2;

        a.active.weight_threshold += 1;

      }

      else

      {

        vote_counter vc;

        for( const witness_object& wit : wits )

        {

            vc.add( wit.witness_account, _vote_tally_buffer[wit.vote_id] );

            idump((wit));

        }

        vc.finish( a.active );

      }

  } );

  modify(gpo, [&]( global_property_object& gp ){

      gp.active_witnesses.clear();

      gp.active_witnesses.reserve(wits.size());

      std::transform(wits.begin(), wits.end(),

                    std::inserter(gp.active_witnesses, gp.active_witnesses.end()),

                    [](const witness_object& w) {

        return w.id;

      });

  });

} FC_CAPTURE_AND_RETHROW() }

这个函数比较有意思,代码全放上来吧。

最前面有注释说明了,如果一个帐户不投票见证人或者只投票给一个见证人,则不能对见证人数量有任何影响,因为不投票见证人或者只投票给一个见证人在见证人个数直方图(_witness_count_histogram_buffer)上投票数量都计在第一个数组元素_witness_count_histogram_buffer[0]中,更详细看后面 vote_tally_helper 那部分代码说明。

stake_target是来决定有多少个见证人的数值,等于投两个见证人以上的帐户的投票数总和除以二,如下:

  share_type stake_target = (_total_voting_stake-_witness_count_histogram_buffer[0]) / 2;

从_witness_count_histogram_buffer[1]开始累加投票数,只到累加投票数大于stake_target,累加个数即为活动见证人个数,如下:

      while( (witness_count < _witness_count_histogram_buffer.size() - 1)

            && (stake_tally <= stake_target) )

      {

        stake_tally += _witness_count_histogram_buffer[++witness_count];

      }

假设没有一个帐户投见证人个数大致2人,则会按最小见证人处理。

database_check_witness()会对每个见证人允许状态、丢包数、创世见证人、资产等做了判断。

会根据见证人得到投票数进行排序,至少会有链参数 min_witness_count 个见证人,当前值是11,如下:

  auto wits = sort_votable_objects(std::max(witness_count*2+1, (size_t)cpo.immutable_parameters.min_witness_count)); 

根据 _vote_tally_buffer 的计数,把投票数设到每个见证人对象上,如下:

obj.total_votes = _vote_tally_buffer[wit.vote_id];

db_maint.cpp -> vote_tally_helper

  struct vote_tally_helper {

      database& d;

      const global_property_object& props;

      vote_tally_helper(database& d, const global_property_object& gpo)

        : d(d), props(gpo)

      {

        d._vote_tally_buffer.resize(props.next_available_vote_id);

        d._witness_count_histogram_buffer.resize(props.parameters.maximum_witness_count / 2 + 1);

        d._committee_count_histogram_buffer.resize(props.parameters.maximum_committee_count / 2 + 1);

        d._total_voting_stake = 0;

      }

      void operator()(const account_object& stake_account) {

        if( props.parameters.count_non_member_votes || stake_account.is_member(d.head_block_time()) )

        {

            // There may be a difference between the account whose stake is voting and the one specifying opinions.

            // Usually they're the same, but if the stake account has specified a voting_account, that account is the one

            // specifying the opinions.

            const account_object& opinion_account =

                  (stake_account.options.voting_account ==

                  GRAPHENE_PROXY_TO_SELF_ACCOUNT)? stake_account

                                    : d.get(stake_account.options.voting_account);

            const auto& stats = stake_account.statistics(d);

            uint64_t voting_stake = stats.total_core_in_orders.value

                  + (stake_account.cashback_vb.valid() ? (*stake_account.cashback_vb)(d).balance.amount.value: 0)

                  + d.get_balance(stake_account.get_id(), asset_id_type()).amount.value;

            for( vote_id_type id : opinion_account.options.votes )

            {

              uint32_t offset = id.instance();

              // if they somehow managed to specify an illegal offset, ignore it.

              if( offset < d._vote_tally_buffer.size() )

                  d._vote_tally_buffer[offset] += voting_stake;

            }

            if( opinion_account.options.num_witness <= props.parameters.maximum_witness_count )

            {

              uint16_t offset = std::min(size_t(opinion_account.options.num_witness/2),

                                          d._witness_count_histogram_buffer.size() - 1);

              // votes for a number greater than maximum_witness_count

              // are turned into votes for maximum_witness_count.

              //

              // in particular, this takes care of the case where a

              // member was voting for a high number, then the

              // parameter was lowered.

              d._witness_count_histogram_buffer[offset] += voting_stake;

            }

            if( opinion_account.options.num_committee <= props.parameters.maximum_committee_count )

            {

              uint16_t offset = std::min(size_t(opinion_account.options.num_committee/2),

                                          d._committee_count_histogram_buffer.size() - 1);

              // votes for a number greater than maximum_committee_count

              // are turned into votes for maximum_committee_count.

              //

              // same rationale as for witnesses

              d._committee_count_histogram_buffer[offset] += voting_stake;

            }

            d._total_voting_stake += voting_stake;

        }

      }

  } tally_helper(*this, gpo);

  struct process_fees_helper {

      database& d;

      const global_property_object& props;

      process_fees_helper(database& d, const global_property_object& gpo)

        : d(d), props(gpo) {}

      void operator()(const account_object& a) {

        a.statistics(d).process_fees(a, d);

      }

  } fee_helper(*this, gpo);

  perform_account_maintenance(std::tie(

      tally_helper,

      fee_helper

      ));

在 perform_chain_maintenance()中定义了一个vote_tally_helper类,用来处理投票计数。

构造函数中会设置_witness_count_histogram_buffer大小,按当前数据就是 1001/2+1=501,如下:

d._witness_count_histogram_buffer.resize(props.parameters.maximum_witness_count / 2 + 1);

重载了操作符()参数是account_object&,在这个函数中可以看到投票权重与三个数值有关,如下:

            const auto& stats = stake_account.statistics(d);

            uint64_t voting_stake = stats.total_core_in_orders.value

                  + (stake_account.cashback_vb.valid() ? (*stake_account.cashback_vb)(d).balance.amount.value: 0)

                  + d.get_balance(stake_account.get_id(), asset_id_type()).amount.value;

根据每个帐号投票的见证人列表,会在 _vote_tally_buffer 中把见证人的票数进行统计,如下:

            for( vote_id_type id : opinion_account.options.votes )

            {

              uint32_t offset = id.instance();

              // if they somehow managed to specify an illegal offset, ignore it.

              if( offset < d._vote_tally_buffer.size() )

                  d._vote_tally_buffer[offset] += voting_stake;

            }

同时会根据每个帐号投票的见证人个数不同,在 _witness_count_histogram_buffer 中做统计,如下:

            if( opinion_account.options.num_witness <= props.parameters.maximum_witness_count )

            {

              uint16_t offset = std::min(size_t(opinion_account.options.num_witness/2),

                                          d._witness_count_histogram_buffer.size() - 1);

              // votes for a number greater than maximum_witness_count

              // are turned into votes for maximum_witness_count.

              //

              // in particular, this takes care of the case where a

              // member was voting for a high number, then the

              // parameter was lowered.

              d._witness_count_histogram_buffer[offset] += voting_stake;

            }

从上面可以看出,如果不投票给见证人或者只投票一个见证人,offset为0,所有的票数都会加在_witness_count_histogram_buffer[0]上。

注意后面还有一个vector清除操作,如下:

  struct clear_canary {

      clear_canary(vector& target): target(target){}

      ~clear_canary() { target.clear(); }

  private:

      vector& target;

  };

  clear_canary a(_witness_count_histogram_buffer),

                b(_committee_count_histogram_buffer),

                c(_vote_tally_buffer);

这些vector变量是在析构中clear()的,也就是在perform_chain_maintenance()函数执行完时才会清除,还有 _total_voting_stake 也是在进入时设为0,这些数据每次都会重新统计。

这一段代码比较有意思就是活动见证人个数的决定,如果一个人票数足够多,对活动见证人个数就能起到比较大的作用,他可以少投一些见证人就能让见证人个数减少,而多投一些见证人可能让见证人个数增加。

感谢您阅读 @chaimyu 的帖子,期待您能留言交流!

https://steemit.com/bitshares/@chaimyu/imzcm-bitshares

你可能感兴趣的:(见证人选择机制)