BitShares-Core  4.0.0
BitShares blockchain implementation and command-line interface software
proposal_evaluator.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2015-2018 Cryptonomex, Inc., and contributors.
3  *
4  * The MIT License
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  */
27 #include <graphene/chain/hardfork.hpp>
28 
29 namespace graphene { namespace chain {
30 
31 namespace detail {
32  void check_asset_options_hf_1774(const fc::time_point_sec& block_time, const asset_options& options);
33 
34  void check_asset_options_hf_bsip_48_75(const fc::time_point_sec& block_time, const asset_options& options);
35  void check_bitasset_options_hf_bsip_48_75(const fc::time_point_sec& block_time, const bitasset_options& options);
37  const asset_update_operation::ext& extensions );
38 
40  const asset_publish_feed_operation::ext& extensions );
41  void check_bitasset_options_hf_bsip77(const fc::time_point_sec& block_time, const bitasset_options& options);
42 
44  const bitasset_options& options); // HF_REMOVABLE
45 
46  void check_asset_options_hf_bsip81(const fc::time_point_sec& block_time, const asset_options& options);
47 
49  const bitasset_options& options); // HF_REMOVABLE
50 
52  const asset_claim_fees_operation& op); // HF_REMOVABLE
53 }
54 
56 {
57  typedef void result_type;
58  const database& db;
61 
63  : db( _db ), block_time(bt), next_maintenance_time( db.get_dynamic_global_properties().next_maintenance_time ) {}
64 
65  template<typename T>
66  void operator()(const T &v) const {}
67 
72  if( v.bitasset_opts.valid() ) {
74  detail::check_bitasset_options_hf_bsip74( block_time, *v.bitasset_opts ); // HF_REMOVABLE
75  detail::check_bitasset_options_hf_bsip77( block_time, *v.bitasset_opts ); // HF_REMOVABLE
76  detail::check_bitasset_options_hf_bsip87( block_time, *v.bitasset_opts ); // HF_REMOVABLE
77  }
78 
79  // TODO move as many validations as possible to validate() if not triggered before hardfork
80  if( HARDFORK_BSIP_48_75_PASSED( block_time ) )
81  {
83  }
84  }
85 
90 
92 
93  // TODO move as many validations as possible to validate() if not triggered before hardfork
94  if( HARDFORK_BSIP_48_75_PASSED( block_time ) )
95  {
96  v.new_options.validate_flags( true );
97  }
98 
99  }
100 
103  detail::check_bitasset_options_hf_bsip74( block_time, v.new_options ); // HF_REMOVABLE
104  detail::check_bitasset_options_hf_bsip77( block_time, v.new_options ); // HF_REMOVABLE
105  detail::check_bitasset_options_hf_bsip87( block_time, v.new_options ); // HF_REMOVABLE
106  }
107 
109  detail::check_asset_claim_fees_hardfork_87_74_collatfee(block_time, v); // HF_REMOVABLE
110  }
111 
113 
115 
116  }
117 
119  if (block_time < HARDFORK_CORE_1468_TIME) {
120  FC_ASSERT(!op.new_parameters.extensions.value.updatable_htlc_options.valid(),
121  "Unable to set HTLC options before hardfork 1468");
125  }
126  if (!HARDFORK_BSIP_40_PASSED(block_time)) {
127  FC_ASSERT(!op.new_parameters.extensions.value.custom_authority_options.valid(),
128  "Unable to set Custom Authority Options before hardfork BSIP 40");
130  "Unable to define fees for custom authority operations prior to hardfork BSIP 40");
132  "Unable to define fees for custom authority operations prior to hardfork BSIP 40");
134  "Unable to define fees for custom authority operations prior to hardfork BSIP 40");
135  }
136  if (!HARDFORK_BSIP_85_PASSED(block_time)) {
137  FC_ASSERT(!op.new_parameters.extensions.value.maker_fee_discount_percent.valid(),
138  "Unable to set maker_fee_discount_percent before hardfork BSIP 85");
139  }
140  if (!HARDFORK_BSIP_86_PASSED(block_time)) {
141  FC_ASSERT(!op.new_parameters.extensions.value.market_fee_network_percent.valid(),
142  "Unable to set market_fee_network_percent before hardfork BSIP 86");
143  }
144  if (!HARDFORK_CORE_2103_PASSED(block_time)) {
146  "Unable to define fees for ticket operations prior to hardfork 2103");
148  "Unable to define fees for ticket operations prior to hardfork 2103");
149  }
150  }
152  FC_ASSERT( block_time >= HARDFORK_CORE_1468_TIME, "Not allowed until hardfork 1468" );
153  if (block_time < HARDFORK_CORE_BSIP64_TIME)
154  {
155  // memo field added at harfork BSIP64
156  // NOTE: both of these checks can be removed after hardfork time
157  FC_ASSERT( !op.extensions.value.memo.valid(),
158  "Memo unavailable until after HARDFORK BSIP64");
159  // HASH160 added at hardfork BSIP64
161  "HASH160 unavailable until after HARDFORK BSIP64" );
162  }
163  }
165  FC_ASSERT( block_time >= HARDFORK_CORE_1468_TIME, "Not allowed until hardfork 1468" );
166  }
168  FC_ASSERT( block_time >= HARDFORK_CORE_1468_TIME, "Not allowed until hardfork 1468" );
169  }
171  FC_ASSERT( HARDFORK_BSIP_40_PASSED(block_time), "Not allowed until hardfork BSIP 40" );
172  }
174  FC_ASSERT( HARDFORK_BSIP_40_PASSED(block_time), "Not allowed until hardfork BSIP 40" );
175  }
177  FC_ASSERT( HARDFORK_BSIP_40_PASSED(block_time), "Not allowed until hardfork BSIP 40" );
178  }
180  FC_ASSERT( HARDFORK_CORE_2103_PASSED(block_time), "Not allowed until hardfork 2103" );
181  }
183  FC_ASSERT( HARDFORK_CORE_2103_PASSED(block_time), "Not allowed until hardfork 2103" );
184  }
185 
186  // loop and self visit in proposals
188  bool already_contains_proposal_update = false;
189 
190  for (const op_wrapper &op : v.proposed_ops)
191  {
192  op.op.visit(*this);
193  // Do not allow more than 1 proposal_update in a proposal
195  {
196  FC_ASSERT( !already_contains_proposal_update,
197  "At most one proposal update can be nested in a proposal!" );
198  already_contains_proposal_update = true;
199  }
200  }
201  }
202 };
203 
204 struct hardfork_visitor_214 // non-recursive proposal visitor
205 {
206  typedef void result_type;
207 
208  template<typename T>
209  void operator()(const T &v) const {}
210 
211  void operator()(const proposal_update_operation &v) const {
212  FC_ASSERT(false, "Not allowed until hardfork 214");
213  }
214 };
215 
217 {
218  if( nested_update_count == 0 || v.proposal.instance.value > max_update_instance )
219  max_update_instance = v.proposal.instance.value;
220  nested_update_count++;
221 }
222 
224 {
225  if( nested_update_count == 0 || v.proposal.instance.value > max_update_instance )
226  max_update_instance = v.proposal.instance.value;
227  nested_update_count++;
228 }
229 
230 // loop and self visit in proposals
232 {
233  for (const op_wrapper &op : v.proposed_ops)
234  op.op.visit(*this);
235 }
236 
238 { try {
239  const database& d = db();
240 
241  // Calling the proposal hardfork visitor
242  const fc::time_point_sec block_time = d.head_block_time();
243  proposal_operation_hardfork_visitor vtor( d, block_time );
244  vtor( o );
245  if( block_time < HARDFORK_CORE_214_TIME )
246  {
247  // cannot be removed after hf, unfortunately
248  hardfork_visitor_214 hf214;
249  for( const op_wrapper &op : o.proposed_ops )
250  op.op.visit( hf214 );
251  }
252  vtor_1479( o );
253 
254  const auto& global_parameters = d.get_global_properties().parameters;
255 
256  FC_ASSERT( o.expiration_time > block_time, "Proposal has already expired on creation." );
257  FC_ASSERT( o.expiration_time <= block_time + global_parameters.maximum_proposal_lifetime,
258  "Proposal expiration time is too far in the future." );
260  fc::seconds( *o.review_period_seconds ) < ( o.expiration_time - block_time ),
261  "Proposal review period must be less than its overall lifetime." );
262 
263  // Find all authorities required by the proposed operations
264  flat_set<account_id_type> tmp_required_active_auths;
265  vector<authority> other;
266  for( auto& op : o.proposed_ops )
267  {
268  operation_get_required_authorities( op.op, tmp_required_active_auths, _required_owner_auths, other,
269  MUST_IGNORE_CUSTOM_OP_REQD_AUTHS( block_time ) );
270  }
271  // All accounts which must provide both owner and active authority should be omitted from the
272  // active authority set; owner authority approval implies active authority approval.
273  std::set_difference( tmp_required_active_auths.begin(), tmp_required_active_auths.end(),
274  _required_owner_auths.begin(), _required_owner_auths.end(),
275  std::inserter( _required_active_auths, _required_active_auths.begin() ) );
276 
277  // TODO: what about other???
278  FC_ASSERT ( other.empty(),
279  "Proposals containing operations requiring non-account authorities are not yet implemented." );
280 
281  // If we're dealing with the committee authority, make sure this transaction has a sufficient review period.
282  if( _required_active_auths.count( GRAPHENE_COMMITTEE_ACCOUNT ) ||
283  _required_owner_auths.count( GRAPHENE_COMMITTEE_ACCOUNT ) )
284  {
286  proposal_create_review_period_required,
287  "Review period not given, but at least ${min} required",
288  ("min", global_parameters.committee_proposal_review_period) );
289  GRAPHENE_ASSERT( *o.review_period_seconds >= global_parameters.committee_proposal_review_period,
290  proposal_create_review_period_insufficient,
291  "Review period of ${t} specified, but at least ${min} required",
292  ("t", *o.review_period_seconds)
293  ("min", global_parameters.committee_proposal_review_period) );
294  }
295 
296  for( const op_wrapper& op : o.proposed_ops )
297  _proposed_trx.operations.push_back( op.op );
298 
299  _proposed_trx.validate();
300 
301  return void_result();
302 } FC_CAPTURE_AND_RETHROW( (o) ) }
303 
305 { try {
306  database& d = db();
307  auto chain_time = d.head_block_time();
308 
309  const proposal_object& proposal = d.create<proposal_object>( [&o, this, chain_time](proposal_object& proposal) {
310  _proposed_trx.expiration = o.expiration_time;
311  proposal.proposed_transaction = _proposed_trx;
312  proposal.expiration_time = o.expiration_time;
313  proposal.proposer = o.fee_paying_account;
314  if( o.review_period_seconds )
315  proposal.review_period_time = o.expiration_time - *o.review_period_seconds;
316 
317  //Populate the required approval sets
318  proposal.required_owner_approvals.insert( _required_owner_auths.begin(), _required_owner_auths.end() );
319  proposal.required_active_approvals.insert( _required_active_auths.begin(), _required_active_auths.end() );
320 
321  if( chain_time > HARDFORK_CORE_1479_TIME )
322  FC_ASSERT( vtor_1479.nested_update_count == 0 || proposal.id.instance() > vtor_1479.max_update_instance,
323  "Cannot update/delete a proposal with a future id!" );
324  else if( vtor_1479.nested_update_count > 0 && proposal.id.instance() <= vtor_1479.max_update_instance )
325  {
326  // Note: This happened on mainnet, proposal 1.10.17503
327  // prevent approval
328  transfer_operation top;
332  proposal.proposed_transaction.operations.emplace_back( top );
333  }
334  });
335 
336  return proposal.id;
337 } FC_CAPTURE_AND_RETHROW( (o) ) }
338 
340 { try {
341  database& d = db();
342 
343  _proposal = &o.proposal(d);
344 
345  if( _proposal->review_period_time && d.head_block_time() >= *_proposal->review_period_time )
347  "This proposal is in its review period. No new approvals may be added." );
348 
349  for( account_id_type id : o.active_approvals_to_remove )
350  {
351  FC_ASSERT( _proposal->available_active_approvals.find(id) != _proposal->available_active_approvals.end(),
352  "", ("id", id)("available", _proposal->available_active_approvals) );
353  }
354  for( account_id_type id : o.owner_approvals_to_remove )
355  {
356  FC_ASSERT( _proposal->available_owner_approvals.find(id) != _proposal->available_owner_approvals.end(),
357  "", ("id", id)("available", _proposal->available_owner_approvals) );
358  }
359 
360  return void_result();
361 } FC_CAPTURE_AND_RETHROW( (o) ) }
362 
364 { try {
365  database& d = db();
366 
367  // Potential optimization: if _executed_proposal is true, we can skip the modify step and make push_proposal
368  // skip signature checks. This isn't done now because I just wrote all the proposals code, and I'm not yet
369  // 100% sure the required approvals are sufficient to authorize the transaction.
370  d.modify(*_proposal, [&o](proposal_object& p) {
373  for( account_id_type id : o.active_approvals_to_remove )
374  p.available_active_approvals.erase(id);
375  for( account_id_type id : o.owner_approvals_to_remove )
376  p.available_owner_approvals.erase(id);
377  for( const auto& id : o.key_approvals_to_add )
378  p.available_key_approvals.insert(id);
379  for( const auto& id : o.key_approvals_to_remove )
380  p.available_key_approvals.erase(id);
381  });
382 
383  // If the proposal has a review period, don't bother attempting to authorize/execute it.
384  // Proposals with a review period may never be executed except at their expiration.
385  if( _proposal->review_period_time )
386  return void_result();
387 
388  if( _proposal->is_authorized_to_execute(d) )
389  {
390  // All required approvals are satisfied. Execute!
391  _executed_proposal = true;
392  try {
393  _processed_transaction = d.push_proposal(*_proposal);
394  } catch(fc::exception& e) {
395  d.modify(*_proposal, [&e](proposal_object& p) {
397  });
398  wlog("Proposed transaction ${id} failed to apply once approved with exception:\n----\n${reason}\n----\nWill try again when it expires.",
399  ("id", o.proposal)("reason", e.to_detail_string()));
400  _proposal_failed = true;
401  }
402  }
403 
404  return void_result();
405 } FC_CAPTURE_AND_RETHROW( (o) ) }
406 
408 { try {
409  database& d = db();
410 
411  _proposal = &o.proposal(d);
412 
413  auto required_approvals = o.using_owner_authority? &_proposal->required_owner_approvals
414  : &_proposal->required_active_approvals;
415  FC_ASSERT( required_approvals->find(o.fee_paying_account) != required_approvals->end(),
416  "Provided authority is not authoritative for this proposal.",
417  ("provided", o.fee_paying_account)("required", *required_approvals));
418 
419  return void_result();
420 } FC_CAPTURE_AND_RETHROW( (o) ) }
421 
423 { try {
424  db().remove(*_proposal);
425 
426  return void_result();
427 } FC_CAPTURE_AND_RETHROW( (o) ) }
428 
429 
430 } } // graphene::chain
bool is_type() const
void modify(const T &obj, const Lambda &m)
proposal_operation_hardfork_visitor(const database &_db, const fc::time_point_sec bt)
void check_asset_options_hf_bsip81(const fc::time_point_sec &block_time, const asset_options &options)
void operator()(const graphene::chain::asset_update_bitasset_operation &v) const
void check_bitasset_options_hf_bsip74(const fc::time_point_sec &block_time, const bitasset_options &options)
necessary to support nested operations inside the proposal_create_operation
Definition: operations.hpp:133
void check_bitasset_options_hf_bsip87(const fc::time_point_sec &block_time, const bitasset_options &options)
vector< operation > operations
Definition: transaction.hpp:89
void operator()(const graphene::chain::committee_member_update_global_parameters_operation &op) const
void operator()(const graphene::chain::asset_create_operation &v) const
flat_set< account_id_type > available_active_approvals
tracks the blockchain state in an extensible manner
Definition: database.hpp:70
Definition: api.cpp:56
used to transfer accumulated fees back to the issuer&#39;s balance.
Definition: asset_ops.hpp:482
The proposal_delete_operation deletes an existing transaction proposalThis operation allows the early...
Definition: proposal.hpp:156
void_result do_evaluate(const proposal_update_operation &o)
void check_bitasset_options_hf_bsip_48_75(const fc::time_point_sec &block_time, const bitasset_options &options)
processed_transaction push_proposal(const proposal_object &proposal)
Definition: db_block.cpp:328
Used to generate a useful error report when an exception is thrown.At each level in the stack where t...
Definition: exception.hpp:56
std::string to_detail_string(log_level ll=log_level::all) const
Definition: exception.cpp:183
bool valid() const
Definition: optional.hpp:186
void operator()(const graphene::chain::htlc_redeem_operation &op) const
flat_set< account_id_type > required_owner_approvals
void check_asset_publish_feed_extensions_hf_bsip77(const fc::time_point_sec &block_time, const asset_publish_feed_operation::ext &extensions)
void operator()(const graphene::chain::custom_authority_update_operation &) const
visitor::result_type visit(visitor &v)
Updates an existing ticket.
Definition: ticket.hpp:66
#define wlog(FORMAT,...)
Definition: logger.hpp:123
void_result do_apply(const proposal_update_operation &o)
void operator()(const graphene::chain::proposal_create_operation &v) const
Publish price feeds for market-issued assetsPrice feed providers use this operation to publish their ...
Definition: asset_ops.hpp:415
void operator()(const graphene::chain::htlc_extend_operation &op) const
flat_set< account_id_type > available_owner_approvals
const global_property_object & get_global_properties() const
Definition: db_getter.cpp:44
#define GRAPHENE_MAX_SHARE_SUPPLY
Definition: config.hpp:38
The proposal_create_operation creates a transaction proposal, for use in multi-sig scenariosCreates a...
Definition: proposal.hpp:70
extension< additional_options_type > extensions
Definition: htlc.hpp:72
uint64_t instance() const
Definition: object_id.hpp:50
void operator()(const graphene::chain::custom_authority_create_operation &) const
flat_set< public_key_type > key_approvals_to_remove
Definition: proposal.hpp:134
void operator()(const graphene::chain::asset_update_operation &v) const
void operator()(const graphene::chain::htlc_create_operation &op) const
std::string to_string(log_level ll=log_level::info) const
Definition: exception.cpp:227
Transfers an amount of one asset from one account to another.
Definition: transfer.hpp:45
object_id_type id
Definition: object.hpp:73
flat_set< account_id_type > active_approvals_to_remove
Definition: proposal.hpp:130
microseconds seconds(int64_t s)
Definition: time.hpp:34
void operator()(const graphene::chain::asset_publish_feed_operation &v) const
flat_set< account_id_type > owner_approvals_to_remove
Definition: proposal.hpp:132
time_point_sec head_block_time() const
Definition: db_getter.cpp:64
#define GRAPHENE_COMMITTEE_ACCOUNT
Definition: config.hpp:125
std::shared_ptr< const fee_schedule > current_fees
current schedule of fees
#define FC_CAPTURE_AND_RETHROW(...)
Definition: exception.hpp:478
Update options specific to BitAssetsBitAssets have some options which are not relevant to other asset...
Definition: asset_ops.hpp:353
#define FC_ASSERT(TEST,...)
Checks a condition and throws an assert_exception if the test is FALSE.
Definition: exception.hpp:345
void_result do_evaluate(const proposal_create_operation &o)
object_id_type do_apply(const proposal_create_operation &o)
void operator()(const proposal_update_operation &v) const
void operator()(const graphene::chain::ticket_update_operation &op) const
#define GRAPHENE_NULL_ACCOUNT
Represents the canonical account with NO authority (nobody can access funds in null account) ...
Definition: config.hpp:131
Update options common to all assetsThere are a number of options which all assets in the network use...
Definition: asset_ops.hpp:307
void operator()(const graphene::chain::asset_claim_fees_operation &v) const
optional< time_point_sec > review_period_time
void operator()(const graphene::chain::ticket_create_operation &op) const
void_result do_apply(const proposal_delete_operation &)
flat_set< account_id_type > required_active_approvals
#define GRAPHENE_RELAXED_COMMITTEE_ACCOUNT
Represents the current committee members.
Definition: config.hpp:129
flat_set< public_key_type > key_approvals_to_add
Definition: proposal.hpp:133
void check_asset_update_extensions_hf_bsip_48_75(const fc::time_point_sec &block_time, const asset_update_operation::ext &extensions)
void check_asset_claim_fees_hardfork_87_74_collatfee(const fc::time_point_sec &block_time, const asset_claim_fees_operation &op)
void check_bitasset_options_hf_bsip77(const fc::time_point_sec &block_time, const bitasset_options &options)
asset amount
The amount of asset to transfer from from to to.
Definition: transfer.hpp:58
void check_asset_options_hf_bsip_48_75(const fc::time_point_sec &block_time, const asset_options &options)
The proposal_update_operation updates an existing transaction proposalThis operation allows accounts ...
Definition: proposal.hpp:119
optional< bitasset_options > bitasset_opts
Definition: asset_ops.hpp:179
account_id_type from
Account to transfer asset from.
Definition: transfer.hpp:54
void validate_flags(bool is_market_issued) const
Definition: asset_ops.cpp:301
#define GRAPHENE_ASSERT(expr, exc_type, FORMAT,...)
Definition: exceptions.hpp:28
Used by committee_members to update the global parameters of the blockchain.This operation allows the...
flat_set< account_id_type > owner_approvals_to_add
Definition: proposal.hpp:131
flat_set< public_key_type > available_key_approvals
void check_asset_options_hf_1774(const fc::time_point_sec &block_time, const asset_options &options)
void operation_get_required_authorities(const operation &op, flat_set< account_id_type > &active, flat_set< account_id_type > &owner, vector< authority > &other, bool ignore_custom_operation_required_auths)
Definition: operations.cpp:103
flat_set< account_id_type > active_approvals_to_add
Definition: proposal.hpp:129
account_id_type to
Account to transfer asset to.
Definition: transfer.hpp:56
void_result do_evaluate(const proposal_delete_operation &o)
const T & create(F &&constructor)
void operator()(const graphene::chain::custom_authority_delete_operation &) const
tracks the approval of a partially approved transaction