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:
Daniel Larimer 2015-07-14 17:28:26 -04:00
parent 2f97a84cb2
commit d1c3c7a698
6 changed files with 37 additions and 44 deletions

View file

@ -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 )

View file

@ -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");
}

View file

@ -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;
};

View file

@ -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) )

View file

@ -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.
*

View file

@ -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 )