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 };
至此, 这笔操作的交易费用就被计算并设置到了操作的成员变量中.