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.
This commit is contained in:
parent
2f97a84cb2
commit
d1c3c7a698
6 changed files with 37 additions and 44 deletions
|
|
@ -17,6 +17,7 @@
|
|||
*/
|
||||
|
||||
#include <graphene/chain/assert_evaluator.hpp>
|
||||
#include <graphene/chain/block_summary_object.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
|
||||
#include <sstream>
|
||||
|
|
@ -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 )
|
||||
|
|
|
|||
|
|
@ -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<const block_summary_object&>(
|
||||
get_index<block_summary_object>()
|
||||
.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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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) )
|
||||
|
||||
|
|
|
|||
|
|
@ -89,13 +89,10 @@ namespace graphene { namespace chain {
|
|||
vector<operation> 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<account_id_type>& active, flat_set<account_id_type>& owner, vector<authority>& other )const;
|
||||
|
||||
protected:
|
||||
// Intentionally unreflected: does not go on wire
|
||||
optional<block_id_type> 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<public_key_type> get_required_signatures( const set<public_key_type>& available_keys,
|
||||
const map<account_id_type,authority>& active_authorities,
|
||||
const map<account_id_type,authority>& owner_authorities )const;
|
||||
|
||||
/**
|
||||
* Given a set of private keys sign this transaction with a minimial subset of required keys.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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<account_id_type>& active, flat_set<account_id_type>& owner, vector<authority>& other )const
|
||||
{
|
||||
for( const auto& op : operations )
|
||||
|
|
|
|||
Loading…
Reference in a new issue