BitShares-Core  4.0.0
BitShares blockchain implementation and command-line interface software
transaction.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2015 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  */
24 
31 
32 #include <fc/io/raw.hpp>
33 
34 namespace graphene { namespace protocol {
35 
37 {
39  fc::raw::pack( enc, *this );
40  return enc.result();
41 }
42 
44 {
46  fc::raw::pack( enc, *this );
47  return enc.result();
48 }
49 
51 {
53  fc::raw::pack( enc, chain_id );
54  fc::raw::pack( enc, *this );
55  return enc.result();
56 }
57 
59 {
60  FC_ASSERT( operations.size() > 0, "A transaction must have at least one operation", ("trx",*this) );
61  for( const auto& op : operations )
63 }
64 
66 {
67  return fc::raw::pack_size(*this);
68 }
69 
71 {
72  auto h = digest();
73  memcpy(_tx_id_buffer._hash, h._hash, std::min(sizeof(_tx_id_buffer), sizeof(h)));
74  return _tx_id_buffer;
75 }
76 
78 {
79  digest_type h = sig_digest( chain_id );
80  signatures.push_back(key.sign_compact(h));
81  return signatures.back();
82 }
83 
85 {
87  fc::raw::pack( enc, chain_id );
88  fc::raw::pack( enc, *this );
89  return key.sign_compact(enc.result());
90 }
91 
93 {
94  expiration = expiration_time;
95 }
96 
97 void transaction::set_reference_block( const block_id_type& reference_block )
98 {
99  ref_block_num = boost::endian::endian_reverse(reference_block._hash[0].value());
100  ref_block_prefix = reference_block._hash[1].value();
101 }
102 
103 void transaction::get_required_authorities( flat_set<account_id_type>& active,
104  flat_set<account_id_type>& owner,
105  vector<authority>& other,
106  bool ignore_custom_operation_required_auths )const
107 {
108  for( const auto& op : operations )
109  operation_get_required_authorities( op, active, owner, other, ignore_custom_operation_required_auths );
110  for( const auto& account : owner )
111  active.erase( account );
112 }
113 
114 
115 const flat_set<public_key_type> empty_keyset;
116 
118 {
122  bool signed_by( const public_key_type& k )
123  {
124  auto itr = provided_signatures.find(k);
125  if( itr == provided_signatures.end() )
126  {
127  auto pk = available_keys.find(k);
128  if( pk != available_keys.end() )
129  return provided_signatures[k] = true;
130  return false;
131  }
132  return itr->second = true;
133  }
134 
137 
138  bool signed_by( const address& a ) {
139  if( !available_address_sigs ) {
140  available_address_sigs = std::map<address,public_key_type>();
141  provided_address_sigs = std::map<address,public_key_type>();
142  for( auto& item : available_keys ) {
143  (*available_address_sigs)[ address(pts_address(item, false, 56) ) ] = item;
144  (*available_address_sigs)[ address(pts_address(item, true, 56) ) ] = item;
145  (*available_address_sigs)[ address(pts_address(item, false, 0) ) ] = item;
146  (*available_address_sigs)[ address(pts_address(item, true, 0) ) ] = item;
147  (*available_address_sigs)[ address(item) ] = item;
148  }
149  for( auto& item : provided_signatures ) {
150  (*provided_address_sigs)[ address(pts_address(item.first, false, 56) ) ] = item.first;
151  (*provided_address_sigs)[ address(pts_address(item.first, true, 56) ) ] = item.first;
152  (*provided_address_sigs)[ address(pts_address(item.first, false, 0) ) ] = item.first;
153  (*provided_address_sigs)[ address(pts_address(item.first, true, 0) ) ] = item.first;
154  (*provided_address_sigs)[ address(item.first) ] = item.first;
155  }
156  }
157  auto itr = provided_address_sigs->find(a);
158  if( itr == provided_address_sigs->end() )
159  {
160  auto aitr = available_address_sigs->find(a);
161  if( aitr != available_address_sigs->end() ) {
162  auto pk = available_keys.find(aitr->second);
163  if( pk != available_keys.end() )
164  return provided_signatures[aitr->second] = true;
165  return false;
166  }
167  }
168  return provided_signatures[itr->second] = true;
169  }
170 
171  bool check_authority( account_id_type id )
172  {
173  if( approved_by.find(id) != approved_by.end() ) return true;
174  return check_authority( get_active(id) ) || ( allow_non_immediate_owner && check_authority( get_owner(id) ) );
175  }
176 
181  bool check_authority( const authority* au, uint32_t depth = 0 )
182  {
183  if( au == nullptr ) return false;
184  const authority& auth = *au;
185 
186  uint32_t total_weight = 0;
187  for( const auto& k : auth.key_auths )
188  if( signed_by( k.first ) )
189  {
190  total_weight += k.second;
191  if( total_weight >= auth.weight_threshold )
192  return true;
193  }
194 
195  for( const auto& k : auth.address_auths )
196  if( signed_by( k.first ) )
197  {
198  total_weight += k.second;
199  if( total_weight >= auth.weight_threshold )
200  return true;
201  }
202 
203  for( const auto& a : auth.account_auths )
204  {
205  if( approved_by.find(a.first) == approved_by.end() )
206  {
207  if( depth == max_recursion )
208  continue;
209  if( check_authority( get_active( a.first ), depth+1 )
210  || ( allow_non_immediate_owner && check_authority( get_owner( a.first ), depth+1 ) ) )
211  {
212  approved_by.insert( a.first );
213  total_weight += a.second;
214  if( total_weight >= auth.weight_threshold )
215  return true;
216  }
217  }
218  else
219  {
220  total_weight += a.second;
221  if( total_weight >= auth.weight_threshold )
222  return true;
223  }
224  }
225  return total_weight >= auth.weight_threshold;
226  }
227 
229  {
230  vector<public_key_type> remove_sigs;
231  for( const auto& sig : provided_signatures )
232  if( !sig.second ) remove_sigs.push_back( sig.first );
233 
234  for( auto& sig : remove_sigs )
235  provided_signatures.erase(sig);
236 
237  return remove_sigs.size() != 0;
238  }
239 
240  sign_state( const flat_set<public_key_type>& sigs,
241  const std::function<const authority*(account_id_type)>& active,
242  const std::function<const authority*(account_id_type)>& owner,
243  bool allow_owner,
244  uint32_t max_recursion_depth = GRAPHENE_MAX_SIG_CHECK_DEPTH,
245  const flat_set<public_key_type>& keys = empty_keyset )
246  : get_active(active),
247  get_owner(owner),
248  allow_non_immediate_owner(allow_owner),
249  max_recursion(max_recursion_depth),
250  available_keys(keys)
251  {
252  for( const auto& key : sigs )
253  provided_signatures[ key ] = false;
254  approved_by.insert( GRAPHENE_TEMP_ACCOUNT );
255  }
256 
257  const std::function<const authority*(account_id_type)>& get_active;
258  const std::function<const authority*(account_id_type)>& get_owner;
259 
261  const uint32_t max_recursion;
262  const flat_set<public_key_type>& available_keys;
263 
264  flat_map<public_key_type,bool> provided_signatures;
265  flat_set<account_id_type> approved_by;
266 };
267 
268 
269 void verify_authority( const vector<operation>& ops, const flat_set<public_key_type>& sigs,
270  const std::function<const authority*(account_id_type)>& get_active,
271  const std::function<const authority*(account_id_type)>& get_owner,
272  const custom_authority_lookup& get_custom,
273  bool allow_non_immediate_owner,
274  bool ignore_custom_operation_required_auths,
275  uint32_t max_recursion_depth,
276  bool allow_committee,
277  const flat_set<account_id_type>& active_aprovals,
278  const flat_set<account_id_type>& owner_approvals )
279 {
280  rejected_predicate_map rejected_custom_auths;
281  try {
282  flat_set<account_id_type> required_active;
283  flat_set<account_id_type> required_owner;
284  vector<authority> other;
285 
286  sign_state s( sigs, get_active, get_owner, allow_non_immediate_owner, max_recursion_depth );
287  for( auto& id : active_aprovals )
288  s.approved_by.insert( id );
289  for( auto& id : owner_approvals )
290  s.approved_by.insert( id );
291 
292  auto approved_by_custom_authority = [&s, &rejected_custom_auths, get_custom = std::move(get_custom)](
293  account_id_type account,
294  operation op ) mutable {
295  auto viable_custom_auths = get_custom( account, op, &rejected_custom_auths );
296  for( const auto& auth : viable_custom_auths )
297  if( s.check_authority( &auth ) ) return true;
298  return false;
299  };
300 
301  for( const auto& op : ops ) {
302  flat_set<account_id_type> operation_required_active;
303  operation_get_required_authorities( op, operation_required_active, required_owner, other,
304  ignore_custom_operation_required_auths );
305 
306  auto itr = operation_required_active.begin();
307  while ( itr != operation_required_active.end() ) {
308  if ( approved_by_custom_authority( *itr, op ) )
309  itr = operation_required_active.erase( itr );
310  else
311  ++itr;
312  }
313 
314  required_active.insert( operation_required_active.begin(), operation_required_active.end() );
315  }
316 
317  if( !allow_committee )
318  GRAPHENE_ASSERT( required_active.find(GRAPHENE_COMMITTEE_ACCOUNT) == required_active.end(),
319  invalid_committee_approval, "Committee account may only propose transactions" );
320 
321  for( const auto& auth : other )
322  {
323  GRAPHENE_ASSERT( s.check_authority(&auth), tx_missing_other_auth, "Missing Authority", ("auth",auth)("sigs",sigs) );
324  }
325 
326  // fetch all of the top level authorities
327  for( auto id : required_owner )
328  {
329  GRAPHENE_ASSERT( owner_approvals.find(id) != owner_approvals.end() ||
330  s.check_authority(get_owner(id)),
331  tx_missing_owner_auth, "Missing Owner Authority ${id}", ("id",id)("auth",*get_owner(id)) );
332  }
333 
334  for( auto id : required_active )
335  {
337  s.check_authority(get_owner(id)),
338  tx_missing_active_auth, "Missing Active Authority ${id}",
339  ("id",id)("auth",*get_active(id))("owner",*get_owner(id)) );
340  }
341 
344  tx_irrelevant_sig,
345  "Unnecessary signature(s) detected"
346  );
347 } FC_CAPTURE_AND_RETHROW( (rejected_custom_auths)(ops)(sigs) ) }
348 
349 
350 const flat_set<public_key_type>& signed_transaction::get_signature_keys( const chain_id_type& chain_id )const
351 { try {
352  auto d = sig_digest( chain_id );
353  flat_set<public_key_type> result;
354  for( const auto& sig : signatures )
355  {
357  result.insert( fc::ecc::public_key(sig,d) ).second,
358  tx_duplicate_sig,
359  "Duplicate Signature detected" );
360  }
361  _signees = std::move( result );
362  return _signees;
364 
365 
366 set<public_key_type> signed_transaction::get_required_signatures( const chain_id_type& chain_id,
367  const flat_set<public_key_type>& available_keys,
368  const std::function<const authority*(account_id_type)>& get_active,
369  const std::function<const authority*(account_id_type)>& get_owner,
370  bool allow_non_immediate_owner,
371  bool ignore_custom_operation_required_authorities,
372  uint32_t max_recursion_depth )const
373 {
374  flat_set<account_id_type> required_active;
375  flat_set<account_id_type> required_owner;
376  vector<authority> other;
377  get_required_authorities( required_active, required_owner, other, ignore_custom_operation_required_authorities );
378 
379  const flat_set<public_key_type>& signature_keys = get_signature_keys(chain_id);
380  sign_state s( signature_keys, get_active, get_owner, allow_non_immediate_owner, max_recursion_depth, available_keys );
381 
382  for( const auto& auth : other )
383  s.check_authority( &auth );
384  for( auto& owner : required_owner )
385  s.check_authority( get_owner( owner ) );
386  for( auto& active : required_active )
387  s.check_authority( active ) || s.check_authority( get_owner( active ) );
388 
390 
391  set<public_key_type> result;
392 
393  for( auto& provided_sig : s.provided_signatures )
394  if( available_keys.find( provided_sig.first ) != available_keys.end()
395  && signature_keys.find( provided_sig.first ) == signature_keys.end() )
396  result.insert( provided_sig.first );
397 
398  return result;
399 }
400 
402  const chain_id_type& chain_id,
403  const flat_set<public_key_type>& available_keys,
404  const std::function<const authority*(account_id_type)>& get_active,
405  const std::function<const authority*(account_id_type)>& get_owner,
406  const custom_authority_lookup &get_custom,
407  bool allow_non_immediate_owner,
408  bool ignore_custom_operation_required_auths,
409  uint32_t max_recursion )const
410 {
411  set< public_key_type > s = get_required_signatures( chain_id, available_keys, get_active, get_owner,
412  allow_non_immediate_owner,
413  ignore_custom_operation_required_auths, max_recursion );
414  flat_set< public_key_type > result( s.begin(), s.end() );
415 
416  for( const public_key_type& k : s )
417  {
418  result.erase( k );
419  try
420  {
421  graphene::protocol::verify_authority( operations, result, get_active, get_owner, get_custom,
422  allow_non_immediate_owner,ignore_custom_operation_required_auths,
423  max_recursion );
424  continue; // element stays erased if verify_authority is ok
425  }
426  catch( const tx_missing_owner_auth& e ) {}
427  catch( const tx_missing_active_auth& e ) {}
428  catch( const tx_missing_other_auth& e ) {}
429  result.insert( k );
430  }
431  return set<public_key_type>( result.begin(), result.end() );
432 }
433 
435 {
436  if( !_tx_id_buffer._hash[0].value() )
437  transaction::id();
438  return _tx_id_buffer;
439 }
440 
442 {
443  if( _validated ) return;
445  _validated = true;
446 }
447 
449 {
450  if( _packed_size == 0 )
452  return _packed_size;
453 }
454 
455 const flat_set<public_key_type>& precomputable_transaction::get_signature_keys( const chain_id_type& chain_id )const
456 {
457  // Strictly we should check whether the given chain ID is same as the one used to initialize the `signees` field.
458  // However, we don't pass in another chain ID so far, for better performance, we skip the check.
459  if( _signees.empty() )
461  return _signees;
462 }
463 
465  const std::function<const authority*(account_id_type)>& get_active,
466  const std::function<const authority*(account_id_type)>& get_owner,
467  const custom_authority_lookup& get_custom,
468  bool allow_non_immediate_owner,
469  bool ignore_custom_operation_required_auths,
470  uint32_t max_recursion )const
471 { try {
472  graphene::protocol::verify_authority( operations, get_signature_keys( chain_id ), get_active, get_owner,
473  get_custom, allow_non_immediate_owner,
474  ignore_custom_operation_required_auths, max_recursion );
475 } FC_CAPTURE_AND_RETHROW( (*this) ) }
476 
477 } } // graphene::protocol
478 
boost::endian::little_uint32_buf_t _hash[5]
Definition: ripemd160.hpp:71
const flat_set< public_key_type > empty_keyset
#define GRAPHENE_TEMP_ACCOUNT
Represents the canonical account with WILDCARD authority (anybody can access funds in temp account) ...
Definition: config.hpp:133
virtual const transaction_id_type & id() const
Definition: transaction.cpp:70
fc::time_point_sec expiration
Definition: transaction.hpp:87
Identifies a weighted set of keys and accounts that must approve operations.
Definition: authority.hpp:34
void pack(Stream &s, const flat_set< T, A... > &value, uint32_t _max_depth)
Definition: flat.hpp:11
map< custom_authority_id_type, rejected_predicate > rejected_predicate_map
Definition: transaction.hpp:31
vector< operation > operations
Definition: transaction.hpp:89
void verify_authority(const chain_id_type &chain_id, const std::function< const authority *(account_id_type)> &get_active, const std::function< const authority *(account_id_type)> &get_owner, const custom_authority_lookup &get_custom, bool allow_non_immediate_owner, bool ignore_custom_operation_required_auths, uint32_t max_recursion=GRAPHENE_MAX_SIG_CHECK_DEPTH) const
bool check_authority(account_id_type id)
virtual void validate() const
Definition: transaction.cpp:58
Definition: api.cpp:56
compact_signature sign_compact(const fc::sha256 &digest, bool require_canonical=true) const
std::function< vector< authority >(account_id_type, const operation &, rejected_predicate_map *)> custom_authority_lookup
Definition: transaction.hpp:33
flat_map< address, weight_type > address_auths
Definition: authority.hpp:123
transaction_id_type _tx_id_buffer
flat_set< public_key_type > _signees
virtual const flat_set< public_key_type > & get_signature_keys(const chain_id_type &chain_id) const
Extract public keys from signatures with given chain ID.
virtual void validate() const override
void get_required_authorities(flat_set< account_id_type > &active, flat_set< account_id_type > &owner, vector< authority > &other, bool ignore_custom_operation_required_auths) const
fc::static_variant< transfer_operation, limit_order_create_operation, limit_order_cancel_operation, call_order_update_operation, fill_order_operation, account_create_operation, account_update_operation, account_whitelist_operation, account_upgrade_operation, account_transfer_operation, asset_create_operation, asset_update_operation, asset_update_bitasset_operation, asset_update_feed_producers_operation, asset_issue_operation, asset_reserve_operation, asset_fund_fee_pool_operation, asset_settle_operation, asset_global_settle_operation, asset_publish_feed_operation, witness_create_operation, witness_update_operation, proposal_create_operation, proposal_update_operation, proposal_delete_operation, withdraw_permission_create_operation, withdraw_permission_update_operation, withdraw_permission_claim_operation, withdraw_permission_delete_operation, committee_member_create_operation, committee_member_update_operation, committee_member_update_global_parameters_operation, vesting_balance_create_operation, vesting_balance_withdraw_operation, worker_create_operation, custom_operation, assert_operation, balance_claim_operation, override_transfer_operation, transfer_to_blind_operation, blind_transfer_operation, transfer_from_blind_operation, asset_settle_cancel_operation, asset_claim_fees_operation, fba_distribute_operation, bid_collateral_operation, execute_bid_operation, asset_claim_pool_operation, asset_update_issuer_operation, htlc_create_operation, htlc_redeem_operation, htlc_redeemed_operation, htlc_extend_operation, htlc_refund_operation, custom_authority_create_operation, custom_authority_update_operation, custom_authority_delete_operation, ticket_create_operation, ticket_update_operation >
bool signed_by(const address &a)
digest_type sig_digest(const chain_id_type &chain_id) const
Definition: transaction.cpp:50
groups operations that should be applied atomically
Definition: transaction.hpp:68
sha256 result()
Definition: sha256.cpp:59
void set_reference_block(const block_id_type &reference_block)
Definition: transaction.cpp:97
set< public_key_type > get_required_signatures(const chain_id_type &chain_id, const flat_set< public_key_type > &available_keys, const std::function< const authority *(account_id_type)> &get_active, const std::function< const authority *(account_id_type)> &get_owner, bool allow_non_immediate_owner, bool ignore_custom_operation_required_authorities, uint32_t max_recursion=GRAPHENE_MAX_SIG_CHECK_DEPTH) const
provides stack-based nullable value similar to boost::optional
Definition: optional.hpp:20
flat_map< public_key_type, weight_type > key_auths
Definition: authority.hpp:121
bool check_authority(const authority *au, uint32_t depth=0)
virtual uint64_t get_packed_size() const
Definition: transaction.cpp:65
size_t pack_size(const T &v)
Definition: raw.hpp:757
void set_expiration(fc::time_point_sec expiration_time)
Definition: transaction.cpp:92
void operation_validate(const operation &op)
Definition: operations.cpp:98
contains only the public point of an elliptic curve key.
Definition: elliptic.hpp:35
void verify_authority(const vector< operation > &ops, const flat_set< public_key_type > &sigs, const std::function< const authority *(account_id_type)> &get_active, const std::function< const authority *(account_id_type)> &get_owner, const custom_authority_lookup &get_custom, bool allow_non_immediate_owner, bool ignore_custom_operation_required_auths, uint32_t max_recursion=GRAPHENE_MAX_SIG_CHECK_DEPTH, bool allow_committee=false, const flat_set< account_id_type > &active_aprovals=flat_set< account_id_type >(), const flat_set< account_id_type > &owner_approvals=flat_set< account_id_type >())
flat_map< account_id_type, weight_type > account_auths
Definition: authority.hpp:120
#define GRAPHENE_COMMITTEE_ACCOUNT
Definition: config.hpp:125
#define FC_CAPTURE_AND_RETHROW(...)
Definition: exception.hpp:478
#define GRAPHENE_MAX_SIG_CHECK_DEPTH
Definition: config.hpp:43
#define FC_ASSERT(TEST,...)
Checks a condition and throws an assert_exception if the test is FALSE.
Definition: exception.hpp:345
adds a signature to a transaction
#define GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION(type)
Definition: types.hpp:74
bool signed_by(const public_key_type &k)
digest_type digest() const
Calculate the digest for a transaction.
Definition: transaction.cpp:43
optional< map< address, public_key_type > > available_address_sigs
const flat_set< public_key_type > & available_keys
virtual const flat_set< public_key_type > & get_signature_keys(const chain_id_type &chain_id) const override
Extract public keys from signatures with given chain ID.
virtual uint64_t get_packed_size() const override
set< public_key_type > minimize_required_signatures(const chain_id_type &chain_id, const flat_set< public_key_type > &available_keys, const std::function< const authority *(account_id_type)> &get_active, const std::function< const authority *(account_id_type)> &get_owner, const custom_authority_lookup &get_custom, bool allow_non_immediate_owner, bool ignore_custom_operation_required_auths, uint32_t max_recursion=GRAPHENE_MAX_SIG_CHECK_DEPTH) const
virtual const transaction_id_type & id() const override
sign_state(const flat_set< public_key_type > &sigs, const std::function< const authority *(account_id_type)> &active, const std::function< const authority *(account_id_type)> &owner, bool allow_owner, uint32_t max_recursion_depth=GRAPHENE_MAX_SIG_CHECK_DEPTH, const flat_set< public_key_type > &keys=empty_keyset)
flat_set< account_id_type > approved_by
#define GRAPHENE_ASSERT(expr, exc_type, FORMAT,...)
Definition: exceptions.hpp:28
a 160 bit hash of a public key
Definition: address.hpp:44
an elliptic curve private key.
Definition: elliptic.hpp:89
const signature_type & sign(const private_key_type &key, const chain_id_type &chain_id)
Definition: transaction.cpp:77
captures the result of evaluating the operations contained in the transaction
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
optional< map< address, public_key_type > > provided_address_sigs
vector< signature_type > signatures
const std::function< const authority *(account_id_type)> & get_active
const std::function< const authority *(account_id_type)> & get_owner
flat_map< public_key_type, bool > provided_signatures