Merged changes from bitshares PR #1259

This commit is contained in:
Ronak Patel 2019-08-26 15:38:24 +05:30
parent 1d5372d432
commit 58b1872d6c
8 changed files with 1312 additions and 31 deletions

View file

@ -26,6 +26,7 @@
#include <graphene/chain/get_config.hpp>
#include <graphene/chain/tournament_object.hpp>
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/hardfork.hpp>
#include <fc/bloom_filter.hpp>
#include <fc/smart_ref_impl.hpp>
@ -1773,10 +1774,12 @@ set<public_key_type> database_api::get_required_signatures( const signed_transac
set<public_key_type> database_api_impl::get_required_signatures( const signed_transaction& trx, const flat_set<public_key_type>& available_keys )const
{
wdump((trx)(available_keys));
bool allow_non_immediate_owner = ( _db.head_block_time() >= HARDFORK_1002_TIME );
auto result = trx.get_required_signatures( _db.get_chain_id(),
available_keys,
[&]( account_id_type id ){ return &id(_db).active; },
[&]( account_id_type id ){ return &id(_db).owner; },
allow_non_immediate_owner,
_db.get_global_properties().parameters.max_authority_depth );
wdump((result));
return result;
@ -1794,7 +1797,11 @@ set<address> database_api::get_potential_address_signatures( const signed_transa
set<public_key_type> database_api_impl::get_potential_signatures( const signed_transaction& trx )const
{
wdump((trx));
auto chain_time = _db.head_block_time();
bool allow_non_immediate_owner = ( chain_time >= HARDFORK_1002_TIME );
set<public_key_type> result;
trx.get_required_signatures(
_db.get_chain_id(),
flat_set<public_key_type>(),
@ -1812,15 +1819,28 @@ set<public_key_type> database_api_impl::get_potential_signatures( const signed_t
result.insert(k);
return &auth;
},
allow_non_immediate_owner,
_db.get_global_properties().parameters.max_authority_depth
);
// Insert keys in required "other" authories
flat_set<account_id_type> required_active;
flat_set<account_id_type> required_owner;
vector<authority> other;
trx.get_required_authorities( required_active, required_owner, other );
for( const auto& auth : other )
for( const auto& key : auth.get_keys() )
result.insert( key );
wdump((result));
return result;
}
set<address> database_api_impl::get_potential_address_signatures( const signed_transaction& trx )const
{
auto chain_time = _db.head_block_time();
bool allow_non_immediate_owner = ( chain_time >= HARDFORK_1002_TIME );
set<address> result;
trx.get_required_signatures(
_db.get_chain_id(),
@ -1839,6 +1859,7 @@ set<address> database_api_impl::get_potential_address_signatures( const signed_t
result.insert(k);
return &auth;
},
allow_non_immediate_owner,
_db.get_global_properties().parameters.max_authority_depth
);
return result;
@ -1851,9 +1872,11 @@ bool database_api::verify_authority( const signed_transaction& trx )const
bool database_api_impl::verify_authority( const signed_transaction& trx )const
{
bool allow_non_immediate_owner = ( _db.head_block_time() >= HARDFORK_1002_TIME );
trx.verify_authority( _db.get_chain_id(),
[&]( account_id_type id ){ return &id(_db).active; },
[&]( account_id_type id ){ return &id(_db).owner; },
allow_non_immediate_owner,
_db.get_global_properties().parameters.max_authority_depth );
return true;
}

View file

@ -680,9 +680,10 @@ processed_transaction database::_apply_transaction(const signed_transaction& trx
if( !(skip & (skip_transaction_signatures | skip_authority_check) ) )
{
bool allow_non_immediate_owner = ( head_block_time() >= HARDFORK_1002_TIME );
auto get_active = [&]( account_id_type id ) { return &id(*this).active; };
auto get_owner = [&]( account_id_type id ) { return &id(*this).owner; };
trx.verify_authority( chain_id, get_active, get_owner, get_global_properties().parameters.max_authority_depth );
trx.verify_authority( chain_id, get_active, get_owner, allow_non_immediate_owner, get_global_properties().parameters.max_authority_depth );
}
//Skip all manner of expiration and TaPoS checking if we're on block 1; It's impossible that the transaction is

View file

@ -0,0 +1,7 @@
// added transaction size check
// Bug fix of update commitee member update
// bitshares-core issue #584 Owner keys of non-immediately required accounts can not authorize a transaction
// https://github.com/bitshares/bitshares-core/issues/584
#ifndef HARDFORK_1002_TIME
#define HARDFORK_1002_TIME (fc::time_point_sec( 1567488600 )) //Tuesday, 3 September 2019 05:30:00 GMT
#endif

View file

@ -141,13 +141,27 @@ namespace graphene { namespace chain {
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,
uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH
)const;
/**
* Checks whether signatures in this signed transaction are sufficient to authorize the transaction.
* Throws an exception when failed.
*
* @param chain_id the ID of a block chain
* @param get_active callback function to retrieve active authorities of a given account
* @param get_owner callback function to retrieve owner authorities of a given account
* @param allow_non_immediate_owner whether to allow owner authority of non-immediately
* required accounts to authorize operations in the transaction
* @param max_recursion maximum level of recursion when verifying, since an account
* can have another account in active authorities and/or owner authorities
*/
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,
bool allow_non_immediate_owner,
uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH )const;
/**
@ -162,6 +176,7 @@ namespace graphene { namespace chain {
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,
uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH
) const;
@ -171,11 +186,31 @@ namespace graphene { namespace chain {
/// Removes all operations and signatures
void clear() { operations.clear(); signatures.clear(); }
/** Removes all signatures */
void clear_signatures() { signatures.clear(); }
};
/**
* Checks whether given public keys and approvals are sufficient to authorize given operations.
* Throws an exception when failed.
*
* @param ops a vector of operations
* @param sigs a set of public keys
* @param get_active callback function to retrieve active authorities of a given account
* @param get_owner callback function to retrieve owner authorities of a given account
* @param allow_non_immediate_owner whether to allow owner authority of non-immediately
* required accounts to authorize operations
* @param max_recursion maximum level of recursion when verifying, since an account
* can have another account in active authorities and/or owner authorities
* @param allow_committee whether to allow the special "committee account" to authorize the operations
* @param active_approvals accounts that approved the operations with their active authories
* @param owner_approvals accounts that approved the operations with their owner authories
*/
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,
bool allow_non_immediate_owner,
uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH,
bool allow_committe = false,
const flat_set<account_id_type>& active_aprovals = flat_set<account_id_type>(),

View file

@ -24,6 +24,7 @@
#include <graphene/chain/database.hpp>
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/proposal_object.hpp>
#include <graphene/chain/hardfork.hpp>
namespace graphene { namespace chain {
@ -32,10 +33,12 @@ bool proposal_object::is_authorized_to_execute(database& db) const
transaction_evaluation_state dry_run_eval(&db);
try {
bool allow_non_immediate_owner = ( db.head_block_time() >= HARDFORK_1002_TIME );
verify_authority( proposed_transaction.operations,
available_key_approvals,
[&]( account_id_type id ){ return &id(db).active; },
[&]( account_id_type id ){ return &id(db).owner; },
allow_non_immediate_owner,
db.get_global_properties().parameters.max_authority_depth,
true, /* allow committeee */
available_active_approvals,

View file

@ -97,6 +97,8 @@ void transaction::get_required_authorities( flat_set<account_id_type>& active, f
{
for( const auto& op : operations )
operation_get_required_authorities( op, active, owner, other );
for( const auto& account : owner )
active.erase( account );
}
@ -159,7 +161,7 @@ struct sign_state
bool check_authority( account_id_type id )
{
if( approved_by.find(id) != approved_by.end() ) return true;
return check_authority( get_active(id) );
return check_authority( get_active(id) ) || ( allow_non_immediate_owner && check_authority( get_owner(id) ) );
}
/**
@ -194,7 +196,8 @@ struct sign_state
{
if( depth == max_recursion )
continue;
if( check_authority( get_active( a.first ), depth+1 ) )
if( check_authority( get_active( a.first ), depth+1 )
|| ( allow_non_immediate_owner && check_authority( get_owner( a.first ), depth+1 ) ) )
{
approved_by.insert( a.first );
total_weight += a.second;
@ -225,9 +228,16 @@ struct sign_state
}
sign_state( const flat_set<public_key_type>& sigs,
const std::function<const authority*(account_id_type)>& a,
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 = flat_set<public_key_type>() )
:get_active(a),available_keys(keys)
: get_active(active),
get_owner(owner),
allow_non_immediate_owner(allow_owner),
max_recursion(max_recursion_depth),
available_keys(keys)
{
for( const auto& key : sigs )
provided_signatures[ key ] = false;
@ -235,17 +245,20 @@ struct sign_state
}
const std::function<const authority*(account_id_type)>& get_active;
const flat_set<public_key_type>& available_keys;
const std::function<const authority*(account_id_type)>& get_owner;
const bool allow_non_immediate_owner;
const uint32_t max_recursion;
const flat_set<public_key_type>& available_keys;
flat_map<public_key_type,bool> provided_signatures;
flat_set<account_id_type> approved_by;
uint32_t max_recursion = GRAPHENE_MAX_SIG_CHECK_DEPTH;
};
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,
bool allow_non_immediate_owner,
uint32_t max_recursion_depth,
bool allow_committe,
const flat_set<account_id_type>& active_aprovals,
@ -262,8 +275,7 @@ void verify_authority( const vector<operation>& ops, const flat_set<public_key_t
GRAPHENE_ASSERT( required_active.find(GRAPHENE_COMMITTEE_ACCOUNT) == required_active.end(),
invalid_committee_approval, "Committee account may only propose transactions" );
sign_state s(sigs,get_active);
s.max_recursion = max_recursion_depth;
sign_state s( sigs, get_active, get_owner, allow_non_immediate_owner, max_recursion_depth );
for( auto& id : active_aprovals )
s.approved_by.insert( id );
for( auto& id : owner_approvals )
@ -318,6 +330,7 @@ set<public_key_type> signed_transaction::get_required_signatures(
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,
uint32_t max_recursion_depth )const
{
flat_set<account_id_type> required_active;
@ -325,23 +338,23 @@ set<public_key_type> signed_transaction::get_required_signatures(
vector<authority> other;
get_required_authorities( required_active, required_owner, other );
sign_state s(get_signature_keys( chain_id ),get_active,available_keys);
s.max_recursion = max_recursion_depth;
const flat_set<public_key_type>& signature_keys = get_signature_keys(chain_id);
sign_state s( signature_keys, get_active, get_owner, allow_non_immediate_owner, max_recursion_depth, available_keys );
for( const auto& auth : other )
s.check_authority(&auth);
for( auto& owner : required_owner )
s.check_authority( get_owner( owner ) );
for( auto& active : required_active )
s.check_authority( active );
s.check_authority( active ) || s.check_authority( get_owner( active ) );
s.remove_unused_signatures();
set<public_key_type> result;
for( auto& provided_sig : s.provided_signatures )
if( available_keys.find( provided_sig.first ) != available_keys.end() )
if( available_keys.find( provided_sig.first ) != available_keys.end()
&& signature_keys.find( provided_sig.first ) == signature_keys.end() )
result.insert( provided_sig.first );
return result;
@ -352,6 +365,7 @@ set<public_key_type> signed_transaction::minimize_required_signatures(
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,
uint32_t max_recursion
) const
{
@ -363,7 +377,7 @@ set<public_key_type> signed_transaction::minimize_required_signatures(
result.erase( k );
try
{
graphene::chain::verify_authority( operations, result, get_active, get_owner, max_recursion );
graphene::chain::verify_authority( operations, result, get_active, get_owner, allow_non_immediate_owner, max_recursion );
continue; // element stays erased if verify_authority is ok
}
catch( const tx_missing_owner_auth& e ) {}
@ -378,9 +392,15 @@ void signed_transaction::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,
bool allow_non_immediate_owner,
uint32_t max_recursion )const
{ try {
graphene::chain::verify_authority( operations, get_signature_keys( chain_id ), get_active, get_owner, max_recursion );
graphene::chain::verify_authority( operations,
get_signature_keys( chain_id ),
get_active,
get_owner,
allow_non_immediate_owner,
max_recursion );
} FC_CAPTURE_AND_RETHROW( (*this) ) }
} } // graphene::chain

View file

@ -32,6 +32,7 @@
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/committee_member_object.hpp>
#include <graphene/chain/proposal_object.hpp>
#include <graphene/chain/hardfork.hpp>
#include <graphene/db/simple_index.hpp>
@ -79,7 +80,10 @@ BOOST_AUTO_TEST_CASE( any_two_of_three )
try {
account_update_operation op;
op.account = nathan.id;
op.active = authority(2, public_key_type(nathan_key1.get_public_key()), 1, public_key_type(nathan_key2.get_public_key()), 1, public_key_type(nathan_key3.get_public_key()), 1);
op.active = authority(2,
public_key_type(nathan_key1.get_public_key()), 1,
public_key_type(nathan_key2.get_public_key()), 1,
public_key_type(nathan_key3.get_public_key()), 1);
op.owner = *op.active;
trx.operations.push_back(op);
sign(trx, nathan_key1);
@ -1159,10 +1163,12 @@ BOOST_FIXTURE_TEST_CASE( get_required_signatures_test, database_fixture )
set<public_key_type> ref_set
) -> bool
{
//wdump( (tx)(available_keys) );
set<public_key_type> result_set = tx.get_required_signatures( db.get_chain_id(), available_keys, get_active, get_owner );
//wdump( (result_set)(ref_set) );
return result_set == ref_set;
set<public_key_type> result_set = tx.get_required_signatures( db.get_chain_id(), available_keys,
get_active, get_owner, false );
set<public_key_type> result_set2 = tx.get_required_signatures( db.get_chain_id(), available_keys,
get_active, get_owner, true );
//wdump( (result_set)(result_set2)(ref_set) );
return result_set == ref_set && result_set2 == ref_set;
} ;
set_auth( well_id, authority( 60, alice_id, 50, bob_id, 50 ) );
@ -1273,10 +1279,12 @@ BOOST_FIXTURE_TEST_CASE( nonminimal_sig_test, database_fixture )
set<public_key_type> ref_set
) -> bool
{
//wdump( (tx)(available_keys) );
set<public_key_type> result_set = tx.get_required_signatures( db.get_chain_id(), available_keys, get_active, get_owner );
//wdump( (result_set)(ref_set) );
return result_set == ref_set;
set<public_key_type> result_set = tx.get_required_signatures( db.get_chain_id(), available_keys,
get_active, get_owner, false );
set<public_key_type> result_set2 = tx.get_required_signatures( db.get_chain_id(), available_keys,
get_active, get_owner, true );
//wdump( (result_set)(result_set2)(ref_set) );
return result_set == ref_set && result_set2 == ref_set;
} ;
auto chk_min = [&](
@ -1285,10 +1293,12 @@ BOOST_FIXTURE_TEST_CASE( nonminimal_sig_test, database_fixture )
set<public_key_type> ref_set
) -> bool
{
//wdump( (tx)(available_keys) );
set<public_key_type> result_set = tx.minimize_required_signatures( db.get_chain_id(), available_keys, get_active, get_owner );
//wdump( (result_set)(ref_set) );
return result_set == ref_set;
set<public_key_type> result_set = tx.minimize_required_signatures( db.get_chain_id(), available_keys,
get_active, get_owner, false );
set<public_key_type> result_set2 = tx.minimize_required_signatures( db.get_chain_id(), available_keys,
get_active, get_owner, true );
//wdump( (result_set)(result_set2)(ref_set) );
return result_set == ref_set && result_set2 == ref_set;
} ;
set_auth( roco_id, authority( 2, styx_id, 1, thud_id, 2 ) );
@ -1305,9 +1315,510 @@ BOOST_FIXTURE_TEST_CASE( nonminimal_sig_test, database_fixture )
BOOST_CHECK( chk( tx, { alice_public_key, bob_public_key }, { alice_public_key, bob_public_key } ) );
BOOST_CHECK( chk_min( tx, { alice_public_key, bob_public_key }, { alice_public_key } ) );
GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner ), fc::exception );
GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, false ), fc::exception );
GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, true ), fc::exception );
sign( tx, alice_private_key );
tx.verify_authority( db.get_chain_id(), get_active, get_owner );
tx.verify_authority( db.get_chain_id(), get_active, get_owner, false );
tx.verify_authority( db.get_chain_id(), get_active, get_owner, true );
}
catch(fc::exception& e)
{
edump((e.to_detail_string()));
throw;
}
}
/*
* Active vs Owner https://github.com/bitshares/bitshares-core/issues/584
*
* All weights and all thresholds are 1, so every single key should be able to sign if within max_depth
*
* Bob --+--(a)--+-- Alice --+--(a)--+-- Daisy --(a/o)-- Daisy_active_key / Daisy_owner_key
* | | | |
* | | | +-- Alice_active_key
* | | |
* | | +--(o)--+-- Cindy --(a/o)-- Cindy_active_key / Cindy_owner_key
* | | |
* | | +-- Alice_owner_key
* | |
* | +-- Bob_active_key
* |
* +--(o)--+-- Edwin --+--(a)--+-- Gavin --(a/o)-- Gavin_active_key / Gavin_owner_key
* | | |
* | | +-- Edwin_active_key
* | |
* | +--(o)--+-- Frank --(a/o)-- Frank_active_key / Frank_owner_key
* | |
* | +-- Edwin_owner_key
* |
* +-- Bob_owner_key
*/
BOOST_FIXTURE_TEST_CASE( parent_owner_test, database_fixture )
{
try
{
ACTORS(
(alice)(bob)(cindy)(daisy)(edwin)(frank)(gavin)
);
transfer( account_id_type(), bob_id, asset(100000) );
auto set_auth = [&](
account_id_type aid,
const authority& active,
const authority& owner
)
{
signed_transaction tx;
account_update_operation op;
op.account = aid;
op.active = active;
op.owner = owner;
tx.operations.push_back( op );
set_expiration( db, tx );
PUSH_TX( db, tx, database::skip_transaction_signatures );
} ;
auto get_active = [&](
account_id_type aid
) -> const authority*
{
return &(aid(db).active);
} ;
auto get_owner = [&](
account_id_type aid
) -> const authority*
{
return &(aid(db).owner);
} ;
auto chk = [&](
const signed_transaction& tx,
bool after_hf_584,
flat_set<public_key_type> available_keys,
set<public_key_type> ref_set
) -> bool
{
//wdump( (tx)(available_keys) );
set<public_key_type> result_set = tx.get_required_signatures(db.get_chain_id(), available_keys,
get_active, get_owner, after_hf_584);
//wdump( (result_set)(ref_set) );
return result_set == ref_set;
} ;
fc::ecc::private_key alice_active_key = fc::ecc::private_key::regenerate(fc::digest("alice_active"));
fc::ecc::private_key alice_owner_key = fc::ecc::private_key::regenerate(fc::digest("alice_owner"));
fc::ecc::private_key bob_active_key = fc::ecc::private_key::regenerate(fc::digest("bob_active"));
fc::ecc::private_key bob_owner_key = fc::ecc::private_key::regenerate(fc::digest("bob_owner"));
fc::ecc::private_key cindy_active_key = fc::ecc::private_key::regenerate(fc::digest("cindy_active"));
fc::ecc::private_key cindy_owner_key = fc::ecc::private_key::regenerate(fc::digest("cindy_owner"));
fc::ecc::private_key daisy_active_key = fc::ecc::private_key::regenerate(fc::digest("daisy_active"));
fc::ecc::private_key daisy_owner_key = fc::ecc::private_key::regenerate(fc::digest("daisy_owner"));
fc::ecc::private_key edwin_active_key = fc::ecc::private_key::regenerate(fc::digest("edwin_active"));
fc::ecc::private_key edwin_owner_key = fc::ecc::private_key::regenerate(fc::digest("edwin_owner"));
fc::ecc::private_key frank_active_key = fc::ecc::private_key::regenerate(fc::digest("frank_active"));
fc::ecc::private_key frank_owner_key = fc::ecc::private_key::regenerate(fc::digest("frank_owner"));
fc::ecc::private_key gavin_active_key = fc::ecc::private_key::regenerate(fc::digest("gavin_active"));
fc::ecc::private_key gavin_owner_key = fc::ecc::private_key::regenerate(fc::digest("gavin_owner"));
public_key_type alice_active_pub( alice_active_key.get_public_key() );
public_key_type alice_owner_pub( alice_owner_key.get_public_key() );
public_key_type bob_active_pub( bob_active_key.get_public_key() );
public_key_type bob_owner_pub( bob_owner_key.get_public_key() );
public_key_type cindy_active_pub( cindy_active_key.get_public_key() );
public_key_type cindy_owner_pub( cindy_owner_key.get_public_key() );
public_key_type daisy_active_pub( daisy_active_key.get_public_key() );
public_key_type daisy_owner_pub( daisy_owner_key.get_public_key() );
public_key_type edwin_active_pub( edwin_active_key.get_public_key() );
public_key_type edwin_owner_pub( edwin_owner_key.get_public_key() );
public_key_type frank_active_pub( frank_active_key.get_public_key() );
public_key_type frank_owner_pub( frank_owner_key.get_public_key() );
public_key_type gavin_active_pub( gavin_active_key.get_public_key() );
public_key_type gavin_owner_pub( gavin_owner_key.get_public_key() );
set_auth( alice_id, authority( 1, alice_active_pub, 1, daisy_id, 1 ), authority( 1, alice_owner_pub, 1, cindy_id, 1 ) );
set_auth( bob_id, authority( 1, bob_active_pub, 1, alice_id, 1 ), authority( 1, bob_owner_pub, 1, edwin_id, 1 ) );
set_auth( cindy_id, authority( 1, cindy_active_pub, 1 ), authority( 1, cindy_owner_pub, 1 ) );
set_auth( daisy_id, authority( 1, daisy_active_pub, 1 ), authority( 1, daisy_owner_pub, 1 ) );
set_auth( edwin_id, authority( 1, edwin_active_pub, 1, gavin_id, 1 ), authority( 1, edwin_owner_pub, 1, frank_id, 1 ) );
set_auth( frank_id, authority( 1, frank_active_pub, 1 ), authority( 1, frank_owner_pub, 1 ) );
set_auth( gavin_id, authority( 1, gavin_active_pub, 1 ), authority( 1, gavin_owner_pub, 1 ) );
generate_block();
signed_transaction tx;
transfer_operation op;
op.from = bob_id;
op.to = alice_id;
op.amount = asset(1);
tx.operations.push_back( op );
set_expiration( db, tx );
// https://github.com/bitshares/bitshares-core/issues/584
// If not allow non-immediate owner to authorize
BOOST_CHECK( chk( tx, false, { alice_owner_pub }, { } ) );
BOOST_CHECK( chk( tx, false, { alice_active_pub }, { alice_active_pub } ) );
BOOST_CHECK( chk( tx, false, { alice_active_pub, alice_owner_pub }, { alice_active_pub } ) );
BOOST_CHECK( chk( tx, false, { bob_owner_pub }, { bob_owner_pub } ) );
BOOST_CHECK( chk( tx, false, { bob_active_pub }, { bob_active_pub } ) );
BOOST_CHECK( chk( tx, false, { bob_active_pub, bob_owner_pub }, { bob_active_pub } ) );
BOOST_CHECK( chk( tx, false, { cindy_owner_pub }, { } ) );
BOOST_CHECK( chk( tx, false, { cindy_active_pub }, { } ) );
BOOST_CHECK( chk( tx, false, { cindy_active_pub, cindy_owner_pub }, { } ) );
BOOST_CHECK( chk( tx, false, { daisy_owner_pub }, { } ) );
BOOST_CHECK( chk( tx, false, { daisy_active_pub }, { daisy_active_pub } ) );
BOOST_CHECK( chk( tx, false, { daisy_active_pub, daisy_owner_pub }, { daisy_active_pub } ) );
BOOST_CHECK( chk( tx, false, { edwin_owner_pub }, { } ) );
BOOST_CHECK( chk( tx, false, { edwin_active_pub }, { edwin_active_pub } ) );
BOOST_CHECK( chk( tx, false, { edwin_active_pub, edwin_owner_pub }, { edwin_active_pub } ) );
BOOST_CHECK( chk( tx, false, { frank_owner_pub }, { } ) );
BOOST_CHECK( chk( tx, false, { frank_active_pub }, { } ) );
BOOST_CHECK( chk( tx, false, { frank_active_pub, frank_owner_pub }, { } ) );
BOOST_CHECK( chk( tx, false, { gavin_owner_pub }, { } ) );
BOOST_CHECK( chk( tx, false, { gavin_active_pub }, { gavin_active_pub } ) );
BOOST_CHECK( chk( tx, false, { gavin_active_pub, gavin_owner_pub }, { gavin_active_pub } ) );
// If allow non-immediate owner to authorize
BOOST_CHECK( chk( tx, true, { alice_owner_pub }, { alice_owner_pub } ) );
BOOST_CHECK( chk( tx, true, { alice_active_pub }, { alice_active_pub } ) );
BOOST_CHECK( chk( tx, true, { alice_active_pub, alice_owner_pub }, { alice_active_pub } ) );
BOOST_CHECK( chk( tx, true, { bob_owner_pub }, { bob_owner_pub } ) );
BOOST_CHECK( chk( tx, true, { bob_active_pub }, { bob_active_pub } ) );
BOOST_CHECK( chk( tx, true, { bob_active_pub, bob_owner_pub }, { bob_active_pub } ) );
BOOST_CHECK( chk( tx, true, { cindy_owner_pub }, { cindy_owner_pub } ) );
BOOST_CHECK( chk( tx, true, { cindy_active_pub }, { cindy_active_pub } ) );
BOOST_CHECK( chk( tx, true, { cindy_active_pub, cindy_owner_pub }, { cindy_active_pub } ) );
BOOST_CHECK( chk( tx, true, { daisy_owner_pub }, { daisy_owner_pub } ) );
BOOST_CHECK( chk( tx, true, { daisy_active_pub }, { daisy_active_pub } ) );
BOOST_CHECK( chk( tx, true, { daisy_active_pub, daisy_owner_pub }, { daisy_active_pub } ) );
BOOST_CHECK( chk( tx, true, { edwin_owner_pub }, { edwin_owner_pub } ) );
BOOST_CHECK( chk( tx, true, { edwin_active_pub }, { edwin_active_pub } ) );
BOOST_CHECK( chk( tx, true, { edwin_active_pub, edwin_owner_pub }, { edwin_active_pub } ) );
BOOST_CHECK( chk( tx, true, { frank_owner_pub }, { frank_owner_pub } ) );
BOOST_CHECK( chk( tx, true, { frank_active_pub }, { frank_active_pub } ) );
BOOST_CHECK( chk( tx, true, { frank_active_pub, frank_owner_pub }, { frank_active_pub } ) );
BOOST_CHECK( chk( tx, true, { gavin_owner_pub }, { gavin_owner_pub } ) );
BOOST_CHECK( chk( tx, true, { gavin_active_pub }, { gavin_active_pub } ) );
BOOST_CHECK( chk( tx, true, { gavin_active_pub, gavin_owner_pub }, { gavin_active_pub } ) );
sign( tx, alice_owner_key );
GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, false ), fc::exception );
GRAPHENE_REQUIRE_THROW( PUSH_TX( db, tx, database::skip_transaction_dupe_check ), fc::exception );
tx.verify_authority( db.get_chain_id(), get_active, get_owner, true);
tx.clear_signatures();
sign( tx, alice_active_key );
tx.verify_authority( db.get_chain_id(), get_active, get_owner, false);
PUSH_TX( db, tx, database::skip_transaction_dupe_check );
tx.verify_authority( db.get_chain_id(), get_active, get_owner, true);
tx.clear_signatures();
sign( tx, bob_owner_key );
tx.verify_authority( db.get_chain_id(), get_active, get_owner, false);
PUSH_TX( db, tx, database::skip_transaction_dupe_check );
tx.verify_authority( db.get_chain_id(), get_active, get_owner, true);
tx.clear_signatures();
sign( tx, bob_active_key );
tx.verify_authority( db.get_chain_id(), get_active, get_owner, false);
PUSH_TX( db, tx, database::skip_transaction_dupe_check );
tx.verify_authority( db.get_chain_id(), get_active, get_owner, true);
tx.clear_signatures();
sign( tx, cindy_owner_key );
GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, false), fc::exception );
GRAPHENE_REQUIRE_THROW( PUSH_TX( db, tx, database::skip_transaction_dupe_check ), fc::exception );
tx.verify_authority( db.get_chain_id(), get_active, get_owner, true);
tx.clear_signatures();
sign( tx, cindy_active_key );
GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, false), fc::exception );
GRAPHENE_REQUIRE_THROW( PUSH_TX( db, tx, database::skip_transaction_dupe_check ), fc::exception );
tx.verify_authority( db.get_chain_id(), get_active, get_owner, true);
tx.clear_signatures();
sign( tx, daisy_owner_key );
GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, false), fc::exception );
GRAPHENE_REQUIRE_THROW( PUSH_TX( db, tx, database::skip_transaction_dupe_check ), fc::exception );
tx.verify_authority( db.get_chain_id(), get_active, get_owner, true);
tx.clear_signatures();
sign( tx, daisy_active_key );
tx.verify_authority( db.get_chain_id(), get_active, get_owner, false);
PUSH_TX( db, tx, database::skip_transaction_dupe_check );
tx.verify_authority( db.get_chain_id(), get_active, get_owner, true);
tx.clear_signatures();
sign( tx, edwin_owner_key );
GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, false), fc::exception );
GRAPHENE_REQUIRE_THROW( PUSH_TX( db, tx, database::skip_transaction_dupe_check ), fc::exception );
tx.verify_authority( db.get_chain_id(), get_active, get_owner, true);
tx.clear_signatures();
sign( tx, edwin_active_key );
tx.verify_authority( db.get_chain_id(), get_active, get_owner, false);
PUSH_TX( db, tx, database::skip_transaction_dupe_check );
tx.verify_authority( db.get_chain_id(), get_active, get_owner, true);
tx.clear_signatures();
sign( tx, frank_owner_key );
GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, false), fc::exception );
GRAPHENE_REQUIRE_THROW( PUSH_TX( db, tx, database::skip_transaction_dupe_check ), fc::exception );
tx.verify_authority( db.get_chain_id(), get_active, get_owner, true);
tx.clear_signatures();
sign( tx, frank_active_key );
GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, false), fc::exception );
GRAPHENE_REQUIRE_THROW( PUSH_TX( db, tx, database::skip_transaction_dupe_check ), fc::exception );
tx.verify_authority( db.get_chain_id(), get_active, get_owner, true);
tx.clear_signatures();
sign( tx, gavin_owner_key );
GRAPHENE_REQUIRE_THROW( tx.verify_authority( db.get_chain_id(), get_active, get_owner, false), fc::exception );
GRAPHENE_REQUIRE_THROW( PUSH_TX( db, tx, database::skip_transaction_dupe_check ), fc::exception );
tx.verify_authority( db.get_chain_id(), get_active, get_owner, true);
tx.clear_signatures();
sign( tx, gavin_active_key );
tx.verify_authority( db.get_chain_id(), get_active, get_owner, false);
PUSH_TX( db, tx, database::skip_transaction_dupe_check );
tx.verify_authority( db.get_chain_id(), get_active, get_owner, true);
tx.clear_signatures();
// proposal tests
auto new_proposal = [&]() -> proposal_id_type {
signed_transaction ptx;
proposal_create_operation pop;
pop.proposed_ops.emplace_back(op);
pop.fee_paying_account = bob_id;
pop.expiration_time = db.head_block_time() + fc::days(1);
ptx.operations.push_back(pop);
set_expiration( db, ptx );
sign( ptx, bob_active_key );
return PUSH_TX( db, ptx, database::skip_transaction_dupe_check ).operation_results[0].get<object_id_type>();
};
auto approve_proposal = [&](
proposal_id_type proposal,
account_id_type account,
bool approve_with_owner,
fc::ecc::private_key key
)
{
signed_transaction ptx;
proposal_update_operation pup;
pup.fee_paying_account = account;
pup.proposal = proposal;
if( approve_with_owner )
pup.owner_approvals_to_add.insert( account );
else
pup.active_approvals_to_add.insert( account );
ptx.operations.push_back(pup);
set_expiration( db, ptx );
sign( ptx, key );
PUSH_TX( db, ptx, database::skip_transaction_dupe_check );
};
proposal_id_type pid;
pid = new_proposal();
approve_proposal( pid, alice_id, true, alice_owner_key );
BOOST_CHECK( !db.find( pid ) );
pid = new_proposal();
approve_proposal( pid, alice_id, false, alice_active_key );
BOOST_CHECK( !db.find( pid ) );
pid = new_proposal();
approve_proposal( pid, bob_id, true, bob_owner_key );
BOOST_CHECK( !db.find( pid ) );
pid = new_proposal();
approve_proposal( pid, bob_id, false, bob_active_key );
BOOST_CHECK( !db.find( pid ) );
// Cindy's approval doesn't work
pid = new_proposal();
approve_proposal( pid, cindy_id, true, cindy_owner_key );
BOOST_CHECK( db.find( pid ) );
pid = new_proposal();
approve_proposal( pid, cindy_id, false, cindy_active_key );
BOOST_CHECK( db.find( pid ) );
pid = new_proposal();
approve_proposal( pid, daisy_id, true, daisy_owner_key );
BOOST_CHECK( !db.find( pid ) );
pid = new_proposal();
approve_proposal( pid, daisy_id, false, daisy_active_key );
BOOST_CHECK( !db.find( pid ) );
pid = new_proposal();
approve_proposal( pid, edwin_id, true, edwin_owner_key );
BOOST_CHECK( !db.find( pid ) );
pid = new_proposal();
approve_proposal( pid, edwin_id, false, edwin_active_key );
BOOST_CHECK( !db.find( pid ) );
// Frank's approval doesn't work
pid = new_proposal();
approve_proposal( pid, frank_id, true, frank_owner_key );
BOOST_CHECK( db.find( pid ) );
pid = new_proposal();
approve_proposal( pid, frank_id, false, frank_active_key );
BOOST_CHECK( db.find( pid ) );
pid = new_proposal();
approve_proposal( pid, gavin_id, true, gavin_owner_key );
BOOST_CHECK( !db.find( pid ) );
pid = new_proposal();
approve_proposal( pid, gavin_id, false, gavin_active_key );
BOOST_CHECK( !db.find( pid ) );
generate_block( database::skip_transaction_dupe_check );
// pass the hard fork time
generate_blocks( HARDFORK_1002_TIME, true, database::skip_transaction_dupe_check );
set_expiration( db, tx );
// signing tests
sign( tx, alice_owner_key );
PUSH_TX( db, tx, database::skip_transaction_dupe_check );
tx.clear_signatures();
sign( tx, alice_active_key );
PUSH_TX( db, tx, database::skip_transaction_dupe_check );
tx.clear_signatures();
sign( tx, bob_owner_key );
PUSH_TX( db, tx, database::skip_transaction_dupe_check );
tx.clear_signatures();
sign( tx, bob_active_key );
PUSH_TX( db, tx, database::skip_transaction_dupe_check );
tx.clear_signatures();
sign( tx, cindy_owner_key );
PUSH_TX( db, tx, database::skip_transaction_dupe_check );
tx.clear_signatures();
sign( tx, cindy_active_key );
PUSH_TX( db, tx, database::skip_transaction_dupe_check );
tx.clear_signatures();
sign( tx, daisy_owner_key );
PUSH_TX( db, tx, database::skip_transaction_dupe_check );
tx.clear_signatures();
sign( tx, daisy_active_key );
PUSH_TX( db, tx, database::skip_transaction_dupe_check );
tx.clear_signatures();
sign( tx, edwin_owner_key );
PUSH_TX( db, tx, database::skip_transaction_dupe_check );
tx.clear_signatures();
sign( tx, edwin_active_key );
PUSH_TX( db, tx, database::skip_transaction_dupe_check );
tx.clear_signatures();
sign( tx, frank_owner_key );
PUSH_TX( db, tx, database::skip_transaction_dupe_check );
tx.clear_signatures();
sign( tx, frank_active_key );
PUSH_TX( db, tx, database::skip_transaction_dupe_check );
tx.clear_signatures();
sign( tx, gavin_owner_key );
PUSH_TX( db, tx, database::skip_transaction_dupe_check );
tx.clear_signatures();
sign( tx, gavin_active_key );
PUSH_TX( db, tx, database::skip_transaction_dupe_check );
tx.clear_signatures();
// proposal tests
pid = new_proposal();
approve_proposal( pid, alice_id, true, alice_owner_key );
BOOST_CHECK( !db.find( pid ) );
pid = new_proposal();
approve_proposal( pid, alice_id, false, alice_active_key );
BOOST_CHECK( !db.find( pid ) );
pid = new_proposal();
approve_proposal( pid, bob_id, true, bob_owner_key );
BOOST_CHECK( !db.find( pid ) );
pid = new_proposal();
approve_proposal( pid, bob_id, false, bob_active_key );
BOOST_CHECK( !db.find( pid ) );
pid = new_proposal();
approve_proposal( pid, cindy_id, true, cindy_owner_key );
BOOST_CHECK( !db.find( pid ) );
pid = new_proposal();
approve_proposal( pid, cindy_id, false, cindy_active_key );
BOOST_CHECK( !db.find( pid ) );
pid = new_proposal();
approve_proposal( pid, daisy_id, true, daisy_owner_key );
BOOST_CHECK( !db.find( pid ) );
pid = new_proposal();
approve_proposal( pid, daisy_id, false, daisy_active_key );
BOOST_CHECK( !db.find( pid ) );
pid = new_proposal();
approve_proposal( pid, edwin_id, true, edwin_owner_key );
BOOST_CHECK( !db.find( pid ) );
pid = new_proposal();
approve_proposal( pid, edwin_id, false, edwin_active_key );
BOOST_CHECK( !db.find( pid ) );
pid = new_proposal();
approve_proposal( pid, frank_id, true, frank_owner_key );
BOOST_CHECK( !db.find( pid ) );
pid = new_proposal();
approve_proposal( pid, frank_id, false, frank_active_key );
BOOST_CHECK( !db.find( pid ) );
pid = new_proposal();
approve_proposal( pid, gavin_id, true, gavin_owner_key );
BOOST_CHECK( !db.find( pid ) );
pid = new_proposal();
approve_proposal( pid, gavin_id, false, gavin_active_key );
BOOST_CHECK( !db.find( pid ) );
generate_block( database::skip_transaction_dupe_check );
}
catch(fc::exception& e)
{

View file

@ -25,6 +25,9 @@
#include <boost/test/unit_test.hpp>
#include <graphene/app/database_api.hpp>
#include <graphene/chain/hardfork.hpp>
#include <fc/crypto/digest.hpp>
#include "../common/database_fixture.hpp"
@ -68,4 +71,682 @@ BOOST_FIXTURE_TEST_SUITE(database_api_tests, database_fixture)
} FC_LOG_AND_RETHROW()
}
BOOST_AUTO_TEST_CASE( get_potential_signatures_owner_and_active )
{
try {
fc::ecc::private_key nathan_key1 = fc::ecc::private_key::regenerate(fc::digest("key1"));
fc::ecc::private_key nathan_key2 = fc::ecc::private_key::regenerate(fc::digest("key2"));
public_key_type pub_key_active( nathan_key1.get_public_key() );
public_key_type pub_key_owner( nathan_key2.get_public_key() );
const account_object& nathan = create_account("nathan", nathan_key1.get_public_key() );
try {
account_update_operation op;
op.account = nathan.id;
op.active = authority(1, pub_key_active, 1);
op.owner = authority(1, pub_key_owner, 1);
trx.operations.push_back(op);
sign(trx, nathan_key1);
PUSH_TX( db, trx, database::skip_transaction_dupe_check );
trx.clear();
} FC_CAPTURE_AND_RETHROW ((nathan.active))
// this op requires active
transfer_operation op;
op.from = nathan.id;
op.to = account_id_type();
trx.operations.push_back(op);
graphene::app::database_api db_api(db);
set<public_key_type> pub_keys = db_api.get_potential_signatures( trx );
BOOST_CHECK( pub_keys.find( pub_key_active ) != pub_keys.end() );
BOOST_CHECK( pub_keys.find( pub_key_owner ) != pub_keys.end() );
trx.operations.clear();
// this op requires owner
account_update_operation auop;
auop.account = nathan.id;
auop.owner = authority(1, pub_key_owner, 1);
trx.operations.push_back(auop);
pub_keys = db_api.get_potential_signatures( trx );
BOOST_CHECK( pub_keys.find( pub_key_active ) == pub_keys.end() ); // active key doesn't help in this case
BOOST_CHECK( pub_keys.find( pub_key_owner ) != pub_keys.end() );
} FC_LOG_AND_RETHROW()
}
/// Testing get_potential_signatures and get_required_signatures for non-immediate owner authority issue.
/// https://github.com/bitshares/bitshares-core/issues/584
BOOST_AUTO_TEST_CASE( get_signatures_non_immediate_owner )
{
try {
fc::ecc::private_key nathan_key1 = fc::ecc::private_key::regenerate(fc::digest("key1"));
fc::ecc::private_key nathan_key2 = fc::ecc::private_key::regenerate(fc::digest("key2"));
fc::ecc::private_key ashley_key1 = fc::ecc::private_key::regenerate(fc::digest("akey1"));
fc::ecc::private_key ashley_key2 = fc::ecc::private_key::regenerate(fc::digest("akey2"));
fc::ecc::private_key oliver_key1 = fc::ecc::private_key::regenerate(fc::digest("okey1"));
fc::ecc::private_key oliver_key2 = fc::ecc::private_key::regenerate(fc::digest("okey2"));
public_key_type pub_key_active( nathan_key1.get_public_key() );
public_key_type pub_key_owner( nathan_key2.get_public_key() );
public_key_type a_pub_key_active( ashley_key1.get_public_key() );
public_key_type a_pub_key_owner( ashley_key2.get_public_key() );
public_key_type o_pub_key_active( oliver_key1.get_public_key() );
public_key_type o_pub_key_owner( oliver_key2.get_public_key() );
const account_object& nathan = create_account("nathan", nathan_key1.get_public_key() );
const account_object& ashley = create_account("ashley", ashley_key1.get_public_key() );
const account_object& oliver = create_account("oliver", oliver_key1.get_public_key() );
account_id_type nathan_id = nathan.id;
account_id_type ashley_id = ashley.id;
account_id_type oliver_id = oliver.id;
try {
account_update_operation op;
op.account = nathan_id;
op.active = authority(1, pub_key_active, 1, ashley_id, 1);
op.owner = authority(1, pub_key_owner, 1, oliver_id, 1);
trx.operations.push_back(op);
sign(trx, nathan_key1);
PUSH_TX( db, trx, database::skip_transaction_dupe_check );
trx.clear();
op.account = ashley_id;
op.active = authority(1, a_pub_key_active, 1);
op.owner = authority(1, a_pub_key_owner, 1);
trx.operations.push_back(op);
sign(trx, ashley_key1);
PUSH_TX( db, trx, database::skip_transaction_dupe_check );
trx.clear();
op.account = oliver_id;
op.active = authority(1, o_pub_key_active, 1);
op.owner = authority(1, o_pub_key_owner, 1);
trx.operations.push_back(op);
sign(trx, oliver_key1);
PUSH_TX( db, trx, database::skip_transaction_dupe_check );
trx.clear();
} FC_CAPTURE_AND_RETHROW ((nathan.active))
// this transaction requires active
signed_transaction trx_a;
transfer_operation op;
op.from = nathan_id;
op.to = account_id_type();
trx_a.operations.push_back(op);
// get potential signatures
graphene::app::database_api db_api(db);
set<public_key_type> pub_keys = db_api.get_potential_signatures( trx_a );
BOOST_CHECK( pub_keys.find( pub_key_active ) != pub_keys.end() );
BOOST_CHECK( pub_keys.find( pub_key_owner ) != pub_keys.end() );
BOOST_CHECK( pub_keys.find( a_pub_key_active ) != pub_keys.end() );
// doesn't work due to https://github.com/bitshares/bitshares-core/issues/584
BOOST_CHECK( pub_keys.find( a_pub_key_owner ) == pub_keys.end() );
BOOST_CHECK( pub_keys.find( o_pub_key_active ) != pub_keys.end() );
// doesn't work due to https://github.com/bitshares/bitshares-core/issues/584
BOOST_CHECK( pub_keys.find( o_pub_key_owner ) == pub_keys.end() );
// get required signatures
pub_keys = db_api.get_required_signatures( trx_a, { a_pub_key_owner, o_pub_key_owner } );
BOOST_CHECK( pub_keys.empty() );
// this op requires owner
signed_transaction trx_o;
account_update_operation auop;
auop.account = nathan_id;
auop.owner = authority(1, pub_key_owner, 1);
trx_o.operations.push_back(auop);
// get potential signatures
pub_keys = db_api.get_potential_signatures( trx_o );
// active authorities doesn't help in this case
BOOST_CHECK( pub_keys.find( pub_key_active ) == pub_keys.end() );
BOOST_CHECK( pub_keys.find( a_pub_key_active ) == pub_keys.end() );
BOOST_CHECK( pub_keys.find( a_pub_key_owner ) == pub_keys.end() );
// owner authorities should be ok
BOOST_CHECK( pub_keys.find( pub_key_owner ) != pub_keys.end() );
BOOST_CHECK( pub_keys.find( o_pub_key_active ) != pub_keys.end() );
// doesn't work due to https://github.com/bitshares/bitshares-core/issues/584
BOOST_CHECK( pub_keys.find( o_pub_key_owner ) == pub_keys.end() );
// get required signatures
pub_keys = db_api.get_required_signatures( trx_o, { a_pub_key_owner, o_pub_key_owner } );
BOOST_CHECK( pub_keys.empty() );
// go beyond hard fork
generate_blocks( HARDFORK_1002_TIME, true );
// for the transaction that requires active
// get potential signatures
pub_keys = db_api.get_potential_signatures( trx_a );
// all authorities should be ok
BOOST_CHECK( pub_keys.find( pub_key_active ) != pub_keys.end() );
BOOST_CHECK( pub_keys.find( a_pub_key_active ) != pub_keys.end() );
BOOST_CHECK( pub_keys.find( a_pub_key_owner ) != pub_keys.end() );
BOOST_CHECK( pub_keys.find( pub_key_owner ) != pub_keys.end() );
BOOST_CHECK( pub_keys.find( o_pub_key_active ) != pub_keys.end() );
BOOST_CHECK( pub_keys.find( o_pub_key_owner ) != pub_keys.end() );
// get required signatures
pub_keys = db_api.get_required_signatures( trx_a, { a_pub_key_owner } );
BOOST_CHECK( pub_keys.find( a_pub_key_owner ) != pub_keys.end() );
pub_keys = db_api.get_required_signatures( trx_a, { o_pub_key_owner } );
BOOST_CHECK( pub_keys.find( o_pub_key_owner ) != pub_keys.end() );
// for the transaction that requires owner
// get potential signatures
pub_keys = db_api.get_potential_signatures( trx_o );
// active authorities doesn't help in this case
BOOST_CHECK( pub_keys.find( pub_key_active ) == pub_keys.end() );
BOOST_CHECK( pub_keys.find( a_pub_key_active ) == pub_keys.end() );
BOOST_CHECK( pub_keys.find( a_pub_key_owner ) == pub_keys.end() );
// owner authorities should help
BOOST_CHECK( pub_keys.find( pub_key_owner ) != pub_keys.end() );
BOOST_CHECK( pub_keys.find( o_pub_key_active ) != pub_keys.end() );
BOOST_CHECK( pub_keys.find( o_pub_key_owner ) != pub_keys.end() );
// get required signatures
pub_keys = db_api.get_required_signatures( trx_o, { a_pub_key_owner, o_pub_key_owner } );
BOOST_CHECK( pub_keys.find( a_pub_key_owner ) == pub_keys.end() );
BOOST_CHECK( pub_keys.find( o_pub_key_owner ) != pub_keys.end() );
} FC_LOG_AND_RETHROW()
}
BOOST_AUTO_TEST_CASE( get_potential_signatures_other )
{
try {
fc::ecc::private_key priv_key1 = fc::ecc::private_key::regenerate(fc::digest("key1"));
public_key_type pub_key1( priv_key1.get_public_key() );
const account_object& nathan = create_account( "nathan" );
balance_claim_operation op;
op.deposit_to_account = nathan.id;
op.balance_owner_key = pub_key1;
trx.operations.push_back(op);
graphene::app::database_api db_api(db);
set<public_key_type> pub_keys = db_api.get_potential_signatures( trx );
BOOST_CHECK( pub_keys.find( pub_key1 ) != pub_keys.end() );
} FC_LOG_AND_RETHROW()
}
BOOST_AUTO_TEST_CASE( get_required_signatures_owner_or_active )
{
try {
fc::ecc::private_key nathan_key1 = fc::ecc::private_key::regenerate(fc::digest("key1"));
fc::ecc::private_key nathan_key2 = fc::ecc::private_key::regenerate(fc::digest("key2"));
public_key_type pub_key_active( nathan_key1.get_public_key() );
public_key_type pub_key_owner( nathan_key2.get_public_key() );
const account_object& nathan = create_account("nathan", nathan_key1.get_public_key() );
try {
account_update_operation op;
op.account = nathan.id;
op.active = authority(1, pub_key_active, 1);
op.owner = authority(1, pub_key_owner, 1);
trx.operations.push_back(op);
sign(trx, nathan_key1);
PUSH_TX( db, trx, database::skip_transaction_dupe_check );
trx.clear();
} FC_CAPTURE_AND_RETHROW ((nathan.active))
graphene::app::database_api db_api(db);
// prepare available keys sets
flat_set<public_key_type> avail_keys1, avail_keys2, avail_keys3;
avail_keys1.insert( pub_key_active );
avail_keys2.insert( pub_key_owner );
avail_keys3.insert( pub_key_active );
avail_keys3.insert( pub_key_owner );
set<public_key_type> pub_keys;
// this op requires active
transfer_operation op;
op.from = nathan.id;
op.to = account_id_type();
trx.operations.push_back(op);
// provides active, should be ok
pub_keys = db_api.get_required_signatures( trx, avail_keys1 );
BOOST_CHECK( pub_keys.find( pub_key_active ) != pub_keys.end() );
// provides owner, should be ok
pub_keys = db_api.get_required_signatures( trx, avail_keys2 );
BOOST_CHECK( pub_keys.find( pub_key_owner ) != pub_keys.end() );
// provides both active and owner, should return one of them
pub_keys = db_api.get_required_signatures( trx, avail_keys3 );
BOOST_CHECK( pub_keys.size() == 1 );
BOOST_CHECK( pub_keys.find( pub_key_active ) != pub_keys.end() || pub_keys.find( pub_key_owner ) != pub_keys.end() );
trx.operations.clear();
// this op requires owner
account_update_operation auop;
auop.account = nathan.id;
auop.owner = authority(1, pub_key_owner, 1);
trx.operations.push_back(auop);
// provides active, should return an empty set
pub_keys = db_api.get_required_signatures( trx, avail_keys1 );
BOOST_CHECK( pub_keys.size() == 0 );
// provides owner, should return it
pub_keys = db_api.get_required_signatures( trx, avail_keys2 );
BOOST_CHECK( pub_keys.find( pub_key_owner ) != pub_keys.end() );
// provides both active and owner, should return owner only
pub_keys = db_api.get_required_signatures( trx, avail_keys3 );
BOOST_CHECK( pub_keys.find( pub_key_active ) == pub_keys.end() );
BOOST_CHECK( pub_keys.find( pub_key_owner ) != pub_keys.end() );
} FC_LOG_AND_RETHROW()
}
BOOST_AUTO_TEST_CASE( get_required_signatures_partially_signed_or_not )
{
try {
fc::ecc::private_key morgan_key = fc::ecc::private_key::regenerate(fc::digest("morgan_key"));
fc::ecc::private_key nathan_key = fc::ecc::private_key::regenerate(fc::digest("nathan_key"));
fc::ecc::private_key oliver_key = fc::ecc::private_key::regenerate(fc::digest("oliver_key"));
public_key_type pub_key_morgan( morgan_key.get_public_key() );
public_key_type pub_key_nathan( nathan_key.get_public_key() );
public_key_type pub_key_oliver( oliver_key.get_public_key() );
const account_object& morgan = create_account("morgan", morgan_key.get_public_key() );
const account_object& nathan = create_account("nathan", nathan_key.get_public_key() );
const account_object& oliver = create_account("oliver", oliver_key.get_public_key() );
graphene::app::database_api db_api(db);
// prepare available keys sets
flat_set<public_key_type> avail_keys_empty, avail_keys_m, avail_keys_n, avail_keys_o;
flat_set<public_key_type> avail_keys_mn, avail_keys_mo, avail_keys_no, avail_keys_mno;
avail_keys_m.insert( pub_key_morgan );
avail_keys_mn.insert( pub_key_morgan );
avail_keys_mo.insert( pub_key_morgan );
avail_keys_mno.insert( pub_key_morgan );
avail_keys_n.insert( pub_key_nathan );
avail_keys_mn.insert( pub_key_nathan );
avail_keys_no.insert( pub_key_nathan );
avail_keys_mno.insert( pub_key_nathan );
avail_keys_o.insert( pub_key_oliver );
avail_keys_mo.insert( pub_key_oliver );
avail_keys_no.insert( pub_key_oliver );
avail_keys_mno.insert( pub_key_oliver );
// result set
set<public_key_type> pub_keys;
// make a transaction that require 1 signature (m)
transfer_operation op;
op.from = morgan.id;
op.to = oliver.id;
trx.operations.push_back(op);
// provides [], should return []
pub_keys = db_api.get_required_signatures( trx, avail_keys_empty );
BOOST_CHECK( pub_keys.size() == 0 );
// provides [m], should return [m]
pub_keys = db_api.get_required_signatures( trx, avail_keys_m );
BOOST_CHECK( pub_keys.size() == 1 );
BOOST_CHECK( pub_keys.find( pub_key_morgan ) != pub_keys.end() );
// provides [n], should return []
pub_keys = db_api.get_required_signatures( trx, avail_keys_n );
BOOST_CHECK( pub_keys.size() == 0 );
// provides [m,n], should return [m]
pub_keys = db_api.get_required_signatures( trx, avail_keys_mn );
BOOST_CHECK( pub_keys.size() == 1 );
BOOST_CHECK( pub_keys.find( pub_key_morgan ) != pub_keys.end() );
// sign with n, but actually need m
sign(trx, nathan_key);
// provides [], should return []
pub_keys = db_api.get_required_signatures( trx, avail_keys_empty );
BOOST_CHECK( pub_keys.size() == 0 );
// provides [m], should return [m]
pub_keys = db_api.get_required_signatures( trx, avail_keys_m );
BOOST_CHECK( pub_keys.size() == 1 );
BOOST_CHECK( pub_keys.find( pub_key_morgan ) != pub_keys.end() );
// provides [n], should return []
pub_keys = db_api.get_required_signatures( trx, avail_keys_n );
BOOST_CHECK( pub_keys.size() == 0 );
// provides [o], should return []
pub_keys = db_api.get_required_signatures( trx, avail_keys_o );
BOOST_CHECK( pub_keys.size() == 0 );
// provides [m,n], should return [m]
pub_keys = db_api.get_required_signatures( trx, avail_keys_mn );
BOOST_CHECK( pub_keys.size() == 1 );
BOOST_CHECK( pub_keys.find( pub_key_morgan ) != pub_keys.end() );
// provides [m,o], should return [m]
pub_keys = db_api.get_required_signatures( trx, avail_keys_mo );
BOOST_CHECK( pub_keys.size() == 1 );
BOOST_CHECK( pub_keys.find( pub_key_morgan ) != pub_keys.end() );
// provides [n,o], should return []
pub_keys = db_api.get_required_signatures( trx, avail_keys_no );
BOOST_CHECK( pub_keys.size() == 0 );
// provides [m,n,o], should return [m]
pub_keys = db_api.get_required_signatures( trx, avail_keys_mno );
BOOST_CHECK( pub_keys.size() == 1 );
BOOST_CHECK( pub_keys.find( pub_key_morgan ) != pub_keys.end() );
// sign with m, should be enough
trx.clear_signatures();
sign(trx, morgan_key);
// provides [], should return []
pub_keys = db_api.get_required_signatures( trx, avail_keys_empty );
BOOST_CHECK( pub_keys.size() == 0 );
// provides [m], should return []
pub_keys = db_api.get_required_signatures( trx, avail_keys_m );
BOOST_CHECK( pub_keys.size() == 0 );
// provides [n], should return []
pub_keys = db_api.get_required_signatures( trx, avail_keys_n );
BOOST_CHECK( pub_keys.size() == 0 );
// provides [m,n], should return []
pub_keys = db_api.get_required_signatures( trx, avail_keys_mn );
BOOST_CHECK( pub_keys.size() == 0 );
// sign with m+n, although m only should be enough, this API won't complain
sign(trx, nathan_key);
// provides [], should return []
pub_keys = db_api.get_required_signatures( trx, avail_keys_empty );
BOOST_CHECK( pub_keys.size() == 0 );
// provides [m], should return []
pub_keys = db_api.get_required_signatures( trx, avail_keys_m );
BOOST_CHECK( pub_keys.size() == 0 );
// provides [n], should return []
pub_keys = db_api.get_required_signatures( trx, avail_keys_n );
BOOST_CHECK( pub_keys.size() == 0 );
// provides [o], should return []
pub_keys = db_api.get_required_signatures( trx, avail_keys_o );
BOOST_CHECK( pub_keys.size() == 0 );
// provides [m,n], should return []
pub_keys = db_api.get_required_signatures( trx, avail_keys_mn );
BOOST_CHECK( pub_keys.size() == 0 );
// provides [m,o], should return []
pub_keys = db_api.get_required_signatures( trx, avail_keys_mo );
BOOST_CHECK( pub_keys.size() == 0 );
// provides [n,o], should return []
pub_keys = db_api.get_required_signatures( trx, avail_keys_no );
BOOST_CHECK( pub_keys.size() == 0 );
// provides [m,n,o], should return []
pub_keys = db_api.get_required_signatures( trx, avail_keys_mno );
BOOST_CHECK( pub_keys.size() == 0 );
// make a transaction that require 2 signatures (m+n)
trx.clear_signatures();
op.from = nathan.id;
trx.operations.push_back(op);
// provides [], should return []
pub_keys = db_api.get_required_signatures( trx, avail_keys_empty );
BOOST_CHECK( pub_keys.size() == 0 );
// provides [m], should return [m]
pub_keys = db_api.get_required_signatures( trx, avail_keys_m );
BOOST_CHECK( pub_keys.size() == 1 );
BOOST_CHECK( pub_keys.find( pub_key_morgan ) != pub_keys.end() );
// provides [n], should return [n]
pub_keys = db_api.get_required_signatures( trx, avail_keys_n );
BOOST_CHECK( pub_keys.size() == 1 );
BOOST_CHECK( pub_keys.find( pub_key_nathan ) != pub_keys.end() );
// provides [o], should return []
pub_keys = db_api.get_required_signatures( trx, avail_keys_o );
BOOST_CHECK( pub_keys.size() == 0 );
// provides [m,n], should return [m,n]
pub_keys = db_api.get_required_signatures( trx, avail_keys_mn );
BOOST_CHECK( pub_keys.size() == 2 );
BOOST_CHECK( pub_keys.find( pub_key_morgan ) != pub_keys.end() );
BOOST_CHECK( pub_keys.find( pub_key_nathan ) != pub_keys.end() );
// provides [m,o], should return [m]
pub_keys = db_api.get_required_signatures( trx, avail_keys_mo );
BOOST_CHECK( pub_keys.size() == 1 );
BOOST_CHECK( pub_keys.find( pub_key_morgan ) != pub_keys.end() );
// provides [n,o], should return [n]
pub_keys = db_api.get_required_signatures( trx, avail_keys_no );
BOOST_CHECK( pub_keys.size() == 1 );
BOOST_CHECK( pub_keys.find( pub_key_nathan ) != pub_keys.end() );
// provides [m,n,o], should return [m,n]
pub_keys = db_api.get_required_signatures( trx, avail_keys_mno );
BOOST_CHECK( pub_keys.size() == 2 );
BOOST_CHECK( pub_keys.find( pub_key_morgan ) != pub_keys.end() );
BOOST_CHECK( pub_keys.find( pub_key_nathan ) != pub_keys.end() );
// sign with o, but actually need m+n
sign(trx, oliver_key);
// provides [], should return []
pub_keys = db_api.get_required_signatures( trx, avail_keys_empty );
BOOST_CHECK( pub_keys.size() == 0 );
// provides [m], should return [m]
pub_keys = db_api.get_required_signatures( trx, avail_keys_m );
BOOST_CHECK( pub_keys.size() == 1 );
BOOST_CHECK( pub_keys.find( pub_key_morgan ) != pub_keys.end() );
// provides [n], should return [n]
pub_keys = db_api.get_required_signatures( trx, avail_keys_n );
BOOST_CHECK( pub_keys.size() == 1 );
BOOST_CHECK( pub_keys.find( pub_key_nathan ) != pub_keys.end() );
// provides [o], should return []
pub_keys = db_api.get_required_signatures( trx, avail_keys_o );
BOOST_CHECK( pub_keys.size() == 0 );
// provides [m,n], should return [m,n]
pub_keys = db_api.get_required_signatures( trx, avail_keys_mn );
BOOST_CHECK( pub_keys.size() == 2 );
BOOST_CHECK( pub_keys.find( pub_key_morgan ) != pub_keys.end() );
BOOST_CHECK( pub_keys.find( pub_key_nathan ) != pub_keys.end() );
// provides [m,o], should return [m]
pub_keys = db_api.get_required_signatures( trx, avail_keys_mo );
BOOST_CHECK( pub_keys.size() == 1 );
BOOST_CHECK( pub_keys.find( pub_key_morgan ) != pub_keys.end() );
// provides [n,o], should return [n]
pub_keys = db_api.get_required_signatures( trx, avail_keys_no );
BOOST_CHECK( pub_keys.size() == 1 );
BOOST_CHECK( pub_keys.find( pub_key_nathan ) != pub_keys.end() );
// provides [m,n,o], should return [m,n]
pub_keys = db_api.get_required_signatures( trx, avail_keys_mno );
BOOST_CHECK( pub_keys.size() == 2 );
BOOST_CHECK( pub_keys.find( pub_key_morgan ) != pub_keys.end() );
BOOST_CHECK( pub_keys.find( pub_key_nathan ) != pub_keys.end() );
// sign with m+o, but actually need m+n
sign(trx, morgan_key);
// provides [], should return []
pub_keys = db_api.get_required_signatures( trx, avail_keys_empty );
BOOST_CHECK( pub_keys.size() == 0 );
// provides [m], should return []
pub_keys = db_api.get_required_signatures( trx, avail_keys_m );
BOOST_CHECK( pub_keys.size() == 0 );
// provides [n], should return [n]
pub_keys = db_api.get_required_signatures( trx, avail_keys_n );
BOOST_CHECK( pub_keys.size() == 1 );
BOOST_CHECK( pub_keys.find( pub_key_nathan ) != pub_keys.end() );
// provides [o], should return []
pub_keys = db_api.get_required_signatures( trx, avail_keys_o );
BOOST_CHECK( pub_keys.size() == 0 );
// provides [m,n], should return [n]
pub_keys = db_api.get_required_signatures( trx, avail_keys_mn );
BOOST_CHECK( pub_keys.size() == 1 );
BOOST_CHECK( pub_keys.find( pub_key_nathan ) != pub_keys.end() );
// provides [m,o], should return []
pub_keys = db_api.get_required_signatures( trx, avail_keys_mo );
BOOST_CHECK( pub_keys.size() == 0 );
// provides [n,o], should return [n]
pub_keys = db_api.get_required_signatures( trx, avail_keys_no );
BOOST_CHECK( pub_keys.size() == 1 );
BOOST_CHECK( pub_keys.find( pub_key_nathan ) != pub_keys.end() );
// provides [m,n,o], should return [n]
pub_keys = db_api.get_required_signatures( trx, avail_keys_mno );
BOOST_CHECK( pub_keys.size() == 1 );
BOOST_CHECK( pub_keys.find( pub_key_nathan ) != pub_keys.end() );
// sign with m, but actually need m+n
trx.clear_signatures();
sign(trx, morgan_key);
// provides [], should return []
pub_keys = db_api.get_required_signatures( trx, avail_keys_empty );
BOOST_CHECK( pub_keys.size() == 0 );
// provides [m], should return []
pub_keys = db_api.get_required_signatures( trx, avail_keys_m );
BOOST_CHECK( pub_keys.size() == 0 );
// provides [n], should return [n]
pub_keys = db_api.get_required_signatures( trx, avail_keys_n );
BOOST_CHECK( pub_keys.size() == 1 );
BOOST_CHECK( pub_keys.find( pub_key_nathan ) != pub_keys.end() );
// provides [o], should return []
pub_keys = db_api.get_required_signatures( trx, avail_keys_o );
BOOST_CHECK( pub_keys.size() == 0 );
// provides [m,n], should return [n]
pub_keys = db_api.get_required_signatures( trx, avail_keys_mn );
BOOST_CHECK( pub_keys.size() == 1 );
BOOST_CHECK( pub_keys.find( pub_key_nathan ) != pub_keys.end() );
// provides [m,o], should return []
pub_keys = db_api.get_required_signatures( trx, avail_keys_mo );
BOOST_CHECK( pub_keys.size() == 0 );
// provides [n,o], should return [n]
pub_keys = db_api.get_required_signatures( trx, avail_keys_no );
BOOST_CHECK( pub_keys.size() == 1 );
BOOST_CHECK( pub_keys.find( pub_key_nathan ) != pub_keys.end() );
// provides [m,n,o], should return [n]
pub_keys = db_api.get_required_signatures( trx, avail_keys_mno );
BOOST_CHECK( pub_keys.size() == 1 );
BOOST_CHECK( pub_keys.find( pub_key_nathan ) != pub_keys.end() );
// sign with m+n, should be enough
sign(trx, nathan_key);
// provides [], should return []
pub_keys = db_api.get_required_signatures( trx, avail_keys_empty );
BOOST_CHECK( pub_keys.size() == 0 );
// provides [m], should return []
pub_keys = db_api.get_required_signatures( trx, avail_keys_m );
BOOST_CHECK( pub_keys.size() == 0 );
// provides [n], should return []
pub_keys = db_api.get_required_signatures( trx, avail_keys_n );
BOOST_CHECK( pub_keys.size() == 0 );
// provides [o], should return []
pub_keys = db_api.get_required_signatures( trx, avail_keys_o );
BOOST_CHECK( pub_keys.size() == 0 );
// provides [m,n], should return []
pub_keys = db_api.get_required_signatures( trx, avail_keys_mn );
BOOST_CHECK( pub_keys.size() == 0 );
// provides [m,o], should return []
pub_keys = db_api.get_required_signatures( trx, avail_keys_mo );
BOOST_CHECK( pub_keys.size() == 0 );
// provides [n,o], should return []
pub_keys = db_api.get_required_signatures( trx, avail_keys_no );
BOOST_CHECK( pub_keys.size() == 0 );
// provides [m,n,o], should return []
pub_keys = db_api.get_required_signatures( trx, avail_keys_mno );
BOOST_CHECK( pub_keys.size() == 0 );
// sign with m+n+o, should be enough as well
sign(trx, oliver_key);
// provides [], should return []
pub_keys = db_api.get_required_signatures( trx, avail_keys_empty );
BOOST_CHECK( pub_keys.size() == 0 );
// provides [m], should return []
pub_keys = db_api.get_required_signatures( trx, avail_keys_m );
BOOST_CHECK( pub_keys.size() == 0 );
// provides [n], should return []
pub_keys = db_api.get_required_signatures( trx, avail_keys_n );
BOOST_CHECK( pub_keys.size() == 0 );
// provides [o], should return []
pub_keys = db_api.get_required_signatures( trx, avail_keys_o );
BOOST_CHECK( pub_keys.size() == 0 );
// provides [m,n], should return []
pub_keys = db_api.get_required_signatures( trx, avail_keys_mn );
BOOST_CHECK( pub_keys.size() == 0 );
// provides [m,o], should return []
pub_keys = db_api.get_required_signatures( trx, avail_keys_mo );
BOOST_CHECK( pub_keys.size() == 0 );
// provides [n,o], should return []
pub_keys = db_api.get_required_signatures( trx, avail_keys_no );
BOOST_CHECK( pub_keys.size() == 0 );
// provides [m,n,o], should return []
pub_keys = db_api.get_required_signatures( trx, avail_keys_mno );
BOOST_CHECK( pub_keys.size() == 0 );
} FC_LOG_AND_RETHROW()
}
BOOST_AUTO_TEST_SUITE_END()