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

in bitshares •  7 years ago 

交易费用

操作类型不同, 所需费用也不同. 各项操作的费用记录在 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_scheduleop 为参数, 就是用 op 的计费方法以及 fee_schedule 的计费参数计算费用. calc_fee_visitor 里有一个 try … catch (代码 1.4) 可能不好理解, 这里的 try … catch 是因为 fee_schedule 这块代码有点问题, 除了 opaccount_create_operation 之外, 其它情况下 param.get<OpType>() 都会抛异常, 这点感兴趣可以看一下 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<MAX_FEE_STABILIZATION_ITERATION; 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<typename OpType>
 87       result_type operator()( const OpType& op )const
 88       {
 89          try {
 90             return op.calculate_fee( param.get<OpType>() ).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<typename OpType::fee_parameters_type>() ).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<typename OpType>
108       void operator()( OpType& op )const
109       {
110          op.fee = _fee;
111       }
112    };

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

Authors get paid when people like you upvote their post.
If you enjoyed what you read here, create your account today and start earning FREE STEEM!
Sort Order:  

@cifer, 这就是文章该有的气质!

谢谢

我希望我知道更好的中文。
我讀得很好。

I wish I could write better English :)

Congratulations @cifer! You have received a personal award!

1 Year on Steemit
Click on the badge to view your Board of Honor.

Do not miss the last post from @steemitboard:
SteemitBoard World Cup Contest - The results, the winners and the prizes

Do you like SteemitBoard's project? Then Vote for its witness and get one more award!