美文网首页比特股教程实时更细
Graphene 源码阅读 - 交易篇 - 交易费用

Graphene 源码阅读 - 交易篇 - 交易费用

作者: 聂怀远 | 来源:发表于2018-05-31 22:24 被阅读5次

    https://steemit.com/bitshares/@cifer/6ai6f6-graphene

    交易费用

    操作类型不同, 所需费用也不同. 各项操作的费用记录在 global_property_object::chain_parameters::fee_schedule 中.

    石墨烯代码将创世信息中的 inital_parameters::current_fees, global_property_object::chain_parameters::fee_schdule, 以及各项操作中的 struct fee_parameters_type {} 结构关联了起来.

    节点启动之前, 一般我们会使用 —create-genesis-json 选项创建创世文件, 创世文件中的 inital_parameters::current_fees 信息会使用各个操作的 struct fee_parameters_type {} 结构写入, 参见:

    // 代码 1.1

    //  libraries/app/application.cpp

    79 namespace detail {

      80

      81    graphene::chain::genesis_state_type create_example_genesis() {

      82      auto nathan_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("nathan")));

      83      dlog("Allocating all stake to ${key}", ("key", utilities::key_to_wif(nathan_key)));

      84      graphene::chain::genesis_state_type initial_state;

      85      initial_state.initial_parameters.current_fees = fee_schedule::get_default();//->set_all_fees(GRAPHENE_BLOCKCHAIN_PRECISION);

      86      initial_state.initial_active_witnesses = GRAPHENE_DEFAULT_MIN_WITNESS_COUNT;

      87      initial_state.initial_timestamp = time_point_sec(time_point::now().sec_since_epoch() /

      88            initial_state.initial_parameters.block_interval *

      89            initial_state.initial_parameters.block_interval);

      90      for( uint64_t i = 0; i < initial_state.initial_active_witnesses; ++i )

      91      {

      92          auto name = "init"+fc::to_string(i);

      93          initial_state.initial_accounts.emplace_back(name,

      94                                                      nathan_key.get_public_key(),

      95                                                      nathan_key.get_public_key(),

      96                                                      true);

      97          initial_state.initial_committee_candidates.push_back({name});

      98          initial_state.initial_witness_candidates.push_back({name, nathan_key.get_public_key()});

      99      }

    100

    101      initial_state.initial_accounts.emplace_back("nathan", nathan_key.get_public_key());

    102      initial_state.initial_balances.push_back({nathan_key.get_public_key(),

    103                                                GRAPHENE_SYMBOL,

    104                                                GRAPHENE_MAX_SHARE_SUPPLY});

    105      initial_state.initial_chain_id = fc::sha256::hash( "BOGUS" );

    106

    107      return initial_state;

    108    }

    然后在启动时, global_property_object::chain_parameters::fee_schdule 会用创世信息中的 inital_parameters::current_fees 初始化自己; 后续创建打包交易使用的费用信息都是从 global_property_object::chain_parameters::fee_schdule 获得, 各个操作自己的 struct fee_parameters_type {} 不再被使用.

    交易费用的设置

    设置交易费用一般发生在交易签名之前, 如果交易中包含多个操作, 每个操作的费用都会被计算并设置:

    // 代码 1.2

    // libraries/wallet/wallet.cpp

    501    void set_operation_fees( signed_transaction& tx, const fee_schedule& s  )

    502    {

    503      for( auto& op : tx.operations )

    504          s.set_fee(op);

    505    }

    fee_schedule::set_fee(op) 方法以操作为参数, 负责设置每个操作的费用. set_fee() 首先调用 calculate_fee() 设置计算操作的费用, calculate_fee() 这里用到了一个 calc_fee_visitor, 这个 visitor 以 fee_schedule 和 op 为参数, 就是用 op 的计费方法以及 fee_schedule 的计费参数计算费用. calc_fee_visitor 里有一个 try … catch (代码 1.4) 可能不好理解, 这里的 try … catch 是因为 fee_schedule 这块代码有点问题, 除了 op 是 account_create_operation 之外, 其它情况下 param.get() 都会抛异常, 这点感兴趣可以看一下 fee_schedule 的源码便知原因.

    // 代码 1.3

    // libraries/chain/protocol/fee_schedule.cpp

    133    asset fee_schedule::calculate_fee( const operation& op, const price& core_exchange_rate )const

    134    {

    135      auto base_value = op.visit( calc_fee_visitor( *this, op ) );

    136      auto scaled = fc::uint128(base_value) * scale;

    137      scaled /= GRAPHENE_100_PERCENT;

    138      FC_ASSERT( scaled <= GRAPHENE_MAX_SHARE_SUPPLY );

    139      //idump( (base_value)(scaled)(core_exchange_rate) );

    140      auto result = asset( scaled.to_uint64(), asset_id_type(0) ) * core_exchange_rate;

    141      //FC_ASSERT( result * core_exchange_rate >= asset( scaled.to_uint64()) );

    142

    143      while( result * core_exchange_rate < asset( scaled.to_uint64()) )

    144        result.amount++;

    145

    146      FC_ASSERT( result.amount <= GRAPHENE_MAX_SHARE_SUPPLY );

    147      return result;

    148    }

    150    asset fee_schedule::set_fee( operation& op, const price& core_exchange_rate )const

    151    {

    152      auto f = calculate_fee( op, core_exchange_rate );

    153      auto f_max = f;

    154      for( int i=0; i

    155      {

    156          op.visit( set_fee_visitor( f_max ) );

    157          auto f2 = calculate_fee( op, core_exchange_rate );

    158          if( f == f2 )

    159            break;

    160          f_max = std::max( f_max, f2 );

    161          f = f2;

    162          if( i == 0 )

    163          {

    164            // no need for warnings on later iterations

    165            wlog( "set_fee requires multiple iterations to stabilize with core_exchange_rate ${p} on operation ${op}",

    166                ("p", core_exchange_rate) ("op", op) );

    167          }

    168      }

    169      return f_max;

    170    }

    // 代码 1.4

    libraries/chain/protocol/fee_schedule.cpp

    78    struct calc_fee_visitor

    79    {

    80      typedef uint64_t result_type;

    81

    82      const fee_schedule& param;

    83      const int current_op;

    84      calc_fee_visitor( const fee_schedule& p, const operation& op ):param(p),current_op(op.which()){}

    85

    86      template

    87      result_type operator()( const OpType& op )const

    88      {

    89          try {

    90            return op.calculate_fee( param.get() ).value;

    91          } catch (fc::assert_exception e) {

    92              fee_parameters params; params.set_which(current_op);

    93              auto itr = param.parameters.find(params);

    94              if( itr != param.parameters.end() ) params = *itr;

    95              return op.calculate_fee( params.get() ).value;

    96          }

    97      }

    98    };

    calculate_fee 算出费用后, 便会调用 op.visit(set_fee_visitor(f_max)) 将具体费用设置到操作中, set_fee_visitor() 很简单, 就是将 f_max 赋值给操作的 fee 成员, 是的, 每个操作都有一个 fee 成员.

    另外在 fee_schedule::set_fee 代码中还考虑到 core_exchange_rate 的变动而多循环执行了几次费用计算, 以达到费用更精确的目的.

    // 代码 1.5

    // libraries/chain/protocol/fee_schedule.cpp

    100    struct set_fee_visitor

    101    {

    102      typedef void result_type;

    103      asset _fee;

    104

    105      set_fee_visitor( asset f ):_fee(f){}

    106

    107      template

    108      void operator()( OpType& op )const

    109      {

    110          op.fee = _fee;

    111      }

    112    };

    至此, 这笔操作的交易费用就被计算并设置到了操作的成员变量中.

    相关文章

      网友评论

        本文标题:Graphene 源码阅读 - 交易篇 - 交易费用

        本文链接:https://www.haomeiwen.com/subject/lgdajftx.html