From d1c3c7a698e2595ee3d058f133e3eb5cc3b9f94e Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Tue, 14 Jul 2015 17:28:26 -0400 Subject: [PATCH] Issue #157 - TAPOS Refinements 1. Implement a TaPoS assert operation predicate that offers full block ID validation for transactions that want the added security. This is only required for transactions that are of high value and transfer control of funds to a newly created identifier and where the witnesses cannot be trusted. 2. Remove the full block ID from the transaction digest generation. --- libraries/chain/assert_evaluator.cpp | 5 ++++ libraries/chain/db_block.cpp | 10 ++----- .../graphene/chain/block_summary_object.hpp | 2 +- .../graphene/chain/protocol/assert.hpp | 18 ++++++++++++- .../graphene/chain/protocol/transaction.hpp | 19 ++++++------- libraries/chain/protocol/transaction.cpp | 27 ++----------------- 6 files changed, 37 insertions(+), 44 deletions(-) diff --git a/libraries/chain/assert_evaluator.cpp b/libraries/chain/assert_evaluator.cpp index f7fe1a14..c805280b 100644 --- a/libraries/chain/assert_evaluator.cpp +++ b/libraries/chain/assert_evaluator.cpp @@ -17,6 +17,7 @@ */ #include +#include #include #include @@ -38,6 +39,10 @@ struct predicate_evaluator { FC_ASSERT( p.asset_id(db).symbol == p.symbol ); } + void operator()( const block_id_predicate& p )const + { + FC_ASSERT( block_summary_id_type( block_header::num_from_id( p.id ) )(db).block_id == p.id ); + } }; void_result assert_evaluator::do_evaluate( const assert_operation& o ) diff --git a/libraries/chain/db_block.cpp b/libraries/chain/db_block.cpp index 9cccc9ff..5adba8a5 100644 --- a/libraries/chain/db_block.cpp +++ b/libraries/chain/db_block.cpp @@ -547,11 +547,7 @@ processed_transaction database::_apply_transaction(const signed_transaction& trx ref_block_height = uint32_t( x0 ); } - const block_summary_object& tapos_block_summary = - static_cast( - get_index() - .get(block_summary_id_type(ref_block_height)) - ); + const auto& tapos_block_summary = block_summary_id_type( ref_block_height )(*this); //This is the signature check for transactions with relative expiration. if( !(skip & skip_transaction_signatures) ) @@ -561,9 +557,7 @@ processed_transaction database::_apply_transaction(const signed_transaction& trx for( const auto& sig : trx.signatures ) { FC_ASSERT(eval_state._sigs.insert(std::make_pair( - public_key_type( - fc::ecc::public_key(sig, - trx.digest(tapos_block_summary.block_id))), + public_key_type( fc::ecc::public_key(sig, trx.digest())), false)).second, "Multiple signatures by same key detected"); } diff --git a/libraries/chain/include/graphene/chain/block_summary_object.hpp b/libraries/chain/include/graphene/chain/block_summary_object.hpp index ec8dcd0d..ddf44d6f 100644 --- a/libraries/chain/include/graphene/chain/block_summary_object.hpp +++ b/libraries/chain/include/graphene/chain/block_summary_object.hpp @@ -36,7 +36,7 @@ namespace graphene { namespace chain { static const uint8_t space_id = implementation_ids; static const uint8_t type_id = impl_block_summary_object_type; - block_id_type block_id; + block_id_type block_id; fc::time_point_sec timestamp; }; diff --git a/libraries/chain/include/graphene/chain/protocol/assert.hpp b/libraries/chain/include/graphene/chain/protocol/assert.hpp index 2177df87..48a9db77 100644 --- a/libraries/chain/include/graphene/chain/protocol/assert.hpp +++ b/libraries/chain/include/graphene/chain/protocol/assert.hpp @@ -34,13 +34,27 @@ namespace graphene { namespace chain { }; + /** + * Used to verify that a specific block is part of the + * blockchain history. This helps protect some high-value + * transactions to newly created IDs + * + * The block ID must be within the last 2^16 blocks. + */ + struct block_id_predicate + { + block_id_type id; + bool validate()const{ return true; } + }; + /** * When defining predicates do not make the protocol dependent upon * implementation details. */ typedef static_variant< account_name_eq_lit_predicate, - asset_symbol_eq_lit_predicate + asset_symbol_eq_lit_predicate, + block_id_predicate > predicate; @@ -71,5 +85,7 @@ namespace graphene { namespace chain { FC_REFLECT( graphene::chain::assert_operation::fee_parameters_type, (fee) ) FC_REFLECT( graphene::chain::account_name_eq_lit_predicate, (account_id)(name) ) FC_REFLECT( graphene::chain::asset_symbol_eq_lit_predicate, (asset_id)(symbol) ) +FC_REFLECT( graphene::chain::block_id_predicate, (id) ) FC_REFLECT_TYPENAME( graphene::chain::predicate ) FC_REFLECT( graphene::chain::assert_operation, (fee)(fee_paying_account)(predicates)(required_auths)(extensions) ) + diff --git a/libraries/chain/include/graphene/chain/protocol/transaction.hpp b/libraries/chain/include/graphene/chain/protocol/transaction.hpp index 4575742e..776bf162 100644 --- a/libraries/chain/include/graphene/chain/protocol/transaction.hpp +++ b/libraries/chain/include/graphene/chain/protocol/transaction.hpp @@ -89,13 +89,10 @@ namespace graphene { namespace chain { vector operations; extensions_type extensions; - /// Calculate the digest for a transaction with a reference block - /// @param ref_block_id Full block ID of the reference block - digest_type digest(const block_id_type& ref_block_id)const; /// Calculate the digest for a transaction with an absolute expiration time - digest_type digest()const; + digest_type digest()const; transaction_id_type id()const; - void validate() const; + void validate() const; void set_expiration( fc::time_point_sec expiration_time ); void set_expiration( const block_id_type& reference_block, unsigned_int lifetime_intervals = 3 ); @@ -115,10 +112,6 @@ namespace graphene { namespace chain { } void get_required_authorities( flat_set& active, flat_set& owner, vector& other )const; - - protected: - // Intentionally unreflected: does not go on wire - optional block_id_cache; }; /** @@ -135,6 +128,14 @@ namespace graphene { namespace chain { /** returns signature but does not append */ signature_type sign( const private_key_type& key )const; + /** + * The purpose of this method is to identify the minimal subset of @ref available_keys that are + * required to sign + */ + set get_required_signatures( const set& available_keys, + const map& active_authorities, + const map& owner_authorities )const; + /** * Given a set of private keys sign this transaction with a minimial subset of required keys. * diff --git a/libraries/chain/protocol/transaction.cpp b/libraries/chain/protocol/transaction.cpp index 5dc32798..9a02a98a 100644 --- a/libraries/chain/protocol/transaction.cpp +++ b/libraries/chain/protocol/transaction.cpp @@ -23,13 +23,6 @@ namespace graphene { namespace chain { -digest_type transaction::digest(const block_id_type& ref_block_id) const -{ - digest_type::encoder enc; - fc::raw::pack( enc, ref_block_id ); - fc::raw::pack( enc, *this ); - return enc.result(); -} digest_type processed_transaction::merkle_digest()const { @@ -38,8 +31,6 @@ digest_type processed_transaction::merkle_digest()const digest_type transaction::digest()const { - //Only use this digest() for transactions with absolute expiration times. - assert(relative_expiration == 0); digest_type::encoder enc; fc::raw::pack( enc, *this ); return enc.result(); @@ -65,24 +56,11 @@ graphene::chain::transaction_id_type graphene::chain::transaction::id() const const signature_type& graphene::chain::signed_transaction::sign(const private_key_type& key) { - if( relative_expiration != 0 ) - { - // Relative expiration is set, meaning we must include the block ID in the signature - FC_ASSERT(block_id_cache.valid()); - signatures.push_back(key.sign_compact(digest(*block_id_cache))); - } else { - signatures.push_back(key.sign_compact(digest())); - } + signatures.push_back(key.sign_compact(digest())); return signatures.back(); } signature_type graphene::chain::signed_transaction::sign(const private_key_type& key)const { - if( relative_expiration != 0 ) - { - // Relative expiration is set, meaning we must include the block ID in the signature - FC_ASSERT(block_id_cache.valid()); - return key.sign_compact(digest(*block_id_cache)); - } return key.sign_compact(digest()); } @@ -91,7 +69,6 @@ void transaction::set_expiration( fc::time_point_sec expiration_time ) ref_block_num = 0; relative_expiration = 0; ref_block_prefix = expiration_time.sec_since_epoch(); - block_id_cache.reset(); } void transaction::set_expiration( const block_id_type& reference_block, unsigned_int lifetime_intervals ) @@ -99,8 +76,8 @@ void transaction::set_expiration( const block_id_type& reference_block, unsigned ref_block_num = fc::endian_reverse_u32(reference_block._hash[0]); ref_block_prefix = reference_block._hash[1]; relative_expiration = lifetime_intervals; - block_id_cache = reference_block; } + void transaction::get_required_authorities( flat_set& active, flat_set& owner, vector& other )const { for( const auto& op : operations )