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

in #bitshares6 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    };

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

Sort:  

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

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!

Coin Marketplace

STEEM 0.25
TRX 0.11
JST 0.033
BTC 63036.79
ETH 3067.42
USDT 1.00
SBD 3.82