peerplays_migrated/tests/tests/authority_tests.cpp
2015-09-17 12:11:40 -04:00

1310 lines
50 KiB
C++

/*
* Copyright (c) 2015, Cryptonomex, Inc.
* All rights reserved.
*
* This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and
* the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification,
* are permitted until September 8, 2015, provided that the following conditions are met:
*
* 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <boost/test/unit_test.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/protocol/protocol.hpp>
#include <graphene/chain/exceptions.hpp>
#include <graphene/chain/account_object.hpp>
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/committee_member_object.hpp>
#include <graphene/chain/proposal_object.hpp>
#include <graphene/db/simple_index.hpp>
#include <fc/crypto/digest.hpp>
#include "../common/database_fixture.hpp"
using namespace graphene::chain;
using namespace graphene::chain::test;
BOOST_FIXTURE_TEST_SUITE( authority_tests, database_fixture )
BOOST_AUTO_TEST_CASE( simple_single_signature )
{ try {
try {
fc::ecc::private_key nathan_key = fc::ecc::private_key::generate();
const account_object& nathan = create_account("nathan", nathan_key.get_public_key());
const asset_object& core = asset_id_type()(db);
auto old_balance = fund(nathan);
transfer_operation op;
op.from = nathan.id;
op.to = account_id_type();
op.amount = core.amount(500);
trx.operations.push_back(op);
sign(trx, nathan_key);
PUSH_TX( db, trx, database::skip_transaction_dupe_check );
BOOST_CHECK_EQUAL(get_balance(nathan, core), old_balance - 500);
} catch (fc::exception& e) {
edump((e.to_detail_string()));
throw;
}
} FC_LOG_AND_RETHROW() }
BOOST_AUTO_TEST_CASE( any_two_of_three )
{
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 nathan_key3 = fc::ecc::private_key::regenerate(fc::digest("key3"));
const account_object& nathan = create_account("nathan", nathan_key1.get_public_key() );
const asset_object& core = asset_id_type()(db);
auto old_balance = fund(nathan);
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.owner = *op.active;
trx.operations.push_back(op);
sign(trx, nathan_key1);
PUSH_TX( db, trx, database::skip_transaction_dupe_check );
trx.operations.clear();
trx.signatures.clear();
} FC_CAPTURE_AND_RETHROW ((nathan.active))
transfer_operation op;
op.from = nathan.id;
op.to = account_id_type();
op.amount = core.amount(500);
trx.operations.push_back(op);
sign(trx, nathan_key1);
GRAPHENE_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception);
sign(trx, nathan_key2);
PUSH_TX( db, trx, database::skip_transaction_dupe_check );
BOOST_CHECK_EQUAL(get_balance(nathan, core), old_balance - 500);
trx.signatures.clear();
sign(trx, nathan_key2);
sign(trx, nathan_key3);
PUSH_TX( db, trx, database::skip_transaction_dupe_check );
BOOST_CHECK_EQUAL(get_balance(nathan, core), old_balance - 1000);
trx.signatures.clear();
sign(trx, nathan_key1);
sign(trx, nathan_key3);
PUSH_TX( db, trx, database::skip_transaction_dupe_check );
BOOST_CHECK_EQUAL(get_balance(nathan, core), old_balance - 1500);
trx.signatures.clear();
//sign(trx, fc::ecc::private_key::generate());
sign(trx,nathan_key3);
GRAPHENE_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception);
BOOST_CHECK_EQUAL(get_balance(nathan, core), old_balance - 1500);
} catch (fc::exception& e) {
edump((e.to_detail_string()));
throw;
}
}
BOOST_AUTO_TEST_CASE( recursive_accounts )
{
try {
fc::ecc::private_key parent1_key = fc::ecc::private_key::generate();
fc::ecc::private_key parent2_key = fc::ecc::private_key::generate();
const auto& core = asset_id_type()(db);
BOOST_TEST_MESSAGE( "Creating parent1 and parent2 accounts" );
const account_object& parent1 = create_account("parent1", parent1_key.get_public_key());
const account_object& parent2 = create_account("parent2", parent2_key.get_public_key());
BOOST_TEST_MESSAGE( "Creating child account that requires both parent1 and parent2 to approve" );
{
auto make_child_op = make_account("child");
make_child_op.owner = authority(2, account_id_type(parent1.id), 1, account_id_type(parent2.id), 1);
make_child_op.active = authority(2, account_id_type(parent1.id), 1, account_id_type(parent2.id), 1);
trx.operations.push_back(make_child_op);
PUSH_TX( db, trx, ~0 );
trx.operations.clear();
}
const account_object& child = get_account("child");
auto old_balance = fund(child);
BOOST_TEST_MESSAGE( "Attempting to transfer with no signatures, should fail" );
transfer_operation op;
op.from = child.id;
op.amount = core.amount(500);
trx.operations.push_back(op);
GRAPHENE_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception);
BOOST_TEST_MESSAGE( "Attempting to transfer with parent1 signature, should fail" );
sign(trx,parent1_key);
GRAPHENE_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception);
trx.signatures.clear();
BOOST_TEST_MESSAGE( "Attempting to transfer with parent2 signature, should fail" );
sign(trx,parent2_key);
GRAPHENE_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception);
BOOST_TEST_MESSAGE( "Attempting to transfer with parent1 and parent2 signature, should succeed" );
sign(trx,parent1_key);
PUSH_TX( db, trx, database::skip_transaction_dupe_check );
BOOST_CHECK_EQUAL(get_balance(child, core), old_balance - 500);
trx.operations.clear();
trx.signatures.clear();
BOOST_TEST_MESSAGE( "Adding a key for the child that can override parents" );
fc::ecc::private_key child_key = fc::ecc::private_key::generate();
{
account_update_operation op;
op.account = child.id;
op.active = authority(2, account_id_type(parent1.id), 1, account_id_type(parent2.id), 1,
public_key_type(child_key.get_public_key()), 2);
trx.operations.push_back(op);
sign(trx,parent1_key);
sign(trx,parent2_key);
PUSH_TX( db, trx, database::skip_transaction_dupe_check );
BOOST_REQUIRE_EQUAL(child.active.num_auths(), 3);
trx.operations.clear();
trx.signatures.clear();
}
op.from = child.id;
op.to = account_id_type();
op.amount = core.amount(500);
trx.operations.push_back(op);
BOOST_TEST_MESSAGE( "Attempting transfer with no signatures, should fail" );
GRAPHENE_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception);
BOOST_TEST_MESSAGE( "Attempting transfer just parent1, should fail" );
sign(trx, parent1_key);
GRAPHENE_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception);
trx.signatures.clear();
BOOST_TEST_MESSAGE( "Attempting transfer just parent2, should fail" );
sign(trx, parent2_key);
GRAPHENE_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception);
BOOST_TEST_MESSAGE( "Attempting transfer both parents, should succeed" );
sign(trx, parent1_key);
PUSH_TX( db, trx, database::skip_transaction_dupe_check );
BOOST_CHECK_EQUAL(get_balance(child, core), old_balance - 1000);
trx.signatures.clear();
BOOST_TEST_MESSAGE( "Attempting transfer with just child key, should succeed" );
sign(trx, child_key);
PUSH_TX( db, trx, database::skip_transaction_dupe_check );
BOOST_CHECK_EQUAL(get_balance(child, core), old_balance - 1500);
trx.operations.clear();
trx.signatures.clear();
BOOST_TEST_MESSAGE( "Creating grandparent account, parent1 now requires authority of grandparent" );
auto grandparent = create_account("grandparent");
fc::ecc::private_key grandparent_key = fc::ecc::private_key::generate();
{
account_update_operation op;
op.account = parent1.id;
op.active = authority(1, account_id_type(grandparent.id), 1);
op.owner = *op.active;
trx.operations.push_back(op);
op.account = grandparent.id;
op.active = authority(1, public_key_type(grandparent_key.get_public_key()), 1);
op.owner = *op.active;
trx.operations.push_back(op);
PUSH_TX( db, trx, ~0 );
trx.operations.clear();
trx.signatures.clear();
}
BOOST_TEST_MESSAGE( "Attempt to transfer using old parent keys, should fail" );
trx.operations.push_back(op);
sign(trx, parent1_key);
sign(trx, parent2_key);
GRAPHENE_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception);
trx.signatures.clear();
sign( trx, parent2_key );
sign( trx, grandparent_key );
BOOST_TEST_MESSAGE( "Attempt to transfer using parent2_key and grandparent_key" );
PUSH_TX( db, trx, database::skip_transaction_dupe_check );
BOOST_CHECK_EQUAL(get_balance(child, core), old_balance - 2000);
trx.clear();
BOOST_TEST_MESSAGE( "Update grandparent account authority to be committee account" );
{
account_update_operation op;
op.account = grandparent.id;
op.active = authority(1, account_id_type(), 1);
op.owner = *op.active;
trx.operations.push_back(op);
PUSH_TX( db, trx, ~0 );
trx.operations.clear();
trx.signatures.clear();
}
BOOST_TEST_MESSAGE( "Create recursion depth failure" );
trx.operations.push_back(op);
sign(trx, parent2_key);
sign(trx, grandparent_key);
sign(trx, init_account_priv_key);
//Fails due to recursion depth.
GRAPHENE_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception);
BOOST_TEST_MESSAGE( "verify child key can override recursion checks" );
trx.signatures.clear();
sign(trx, child_key);
PUSH_TX( db, trx, database::skip_transaction_dupe_check );
BOOST_CHECK_EQUAL(get_balance(child, core), old_balance - 2500);
trx.operations.clear();
trx.signatures.clear();
BOOST_TEST_MESSAGE( "Verify a cycle fails" );
{
account_update_operation op;
op.account = parent1.id;
op.active = authority(1, account_id_type(child.id), 1);
op.owner = *op.active;
trx.operations.push_back(op);
PUSH_TX( db, trx, ~0 );
trx.operations.clear();
trx.signatures.clear();
}
trx.operations.push_back(op);
sign(trx, parent2_key);
//Fails due to recursion depth.
GRAPHENE_CHECK_THROW(PUSH_TX( db, trx, database::skip_transaction_dupe_check ), fc::exception);
} catch (fc::exception& e) {
edump((e.to_detail_string()));
throw;
}
}
BOOST_AUTO_TEST_CASE( proposed_single_account )
{
using namespace graphene::chain;
try {
INVOKE(any_two_of_three);
fc::ecc::private_key committee_key = init_account_priv_key;
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 nathan_key3 = fc::ecc::private_key::regenerate(fc::digest("key3"));
const account_object& moneyman = create_account("moneyman", init_account_pub_key);
const account_object& nathan = get_account("nathan");
const asset_object& core = asset_id_type()(db);
transfer(account_id_type()(db), moneyman, core.amount(1000000));
//Following any_two_of_three, nathan's active authority is satisfied by any two of {key1,key2,key3}
BOOST_TEST_MESSAGE( "moneyman is creating proposal for nathan to transfer 100 CORE to moneyman" );
transfer_operation transfer_op;
transfer_op.from = nathan.id;
transfer_op.to = moneyman.get_id();
transfer_op.amount = core.amount(100);
proposal_create_operation op;
op.fee_paying_account = moneyman.id;
op.proposed_ops.emplace_back( transfer_op );
op.expiration_time = db.head_block_time() + fc::days(1);
asset nathan_start_balance = db.get_balance(nathan.get_id(), core.get_id());
{
vector<authority> other;
flat_set<account_id_type> active_set, owner_set;
operation_get_required_authorities(op,active_set,owner_set,other);
BOOST_CHECK_EQUAL(active_set.size(), 1);
BOOST_CHECK_EQUAL(owner_set.size(), 0);
BOOST_CHECK_EQUAL(other.size(), 0);
BOOST_CHECK(*active_set.begin() == moneyman.get_id());
active_set.clear();
other.clear();
operation_get_required_authorities(op.proposed_ops.front().op,active_set,owner_set,other);
BOOST_CHECK_EQUAL(active_set.size(), 1);
BOOST_CHECK_EQUAL(owner_set.size(), 0);
BOOST_CHECK_EQUAL(other.size(), 0);
BOOST_CHECK(*active_set.begin() == nathan.id);
}
trx.operations.push_back(op);
set_expiration( db, trx );
sign( trx, init_account_priv_key );
const proposal_object& proposal = db.get<proposal_object>(PUSH_TX( db, trx ).operation_results.front().get<object_id_type>());
BOOST_CHECK_EQUAL(proposal.required_active_approvals.size(), 1);
BOOST_CHECK_EQUAL(proposal.available_active_approvals.size(), 0);
BOOST_CHECK_EQUAL(proposal.required_owner_approvals.size(), 0);
BOOST_CHECK_EQUAL(proposal.available_owner_approvals.size(), 0);
BOOST_CHECK(*proposal.required_active_approvals.begin() == nathan.id);
proposal_update_operation pup;
pup.proposal = proposal.id;
pup.fee_paying_account = nathan.id;
BOOST_TEST_MESSAGE( "Updating the proposal to have nathan's authority" );
pup.active_approvals_to_add.insert(nathan.id);
trx.operations = {pup};
sign( trx, committee_key );
//committee may not add nathan's approval.
GRAPHENE_CHECK_THROW(PUSH_TX( db, trx ), fc::exception);
pup.active_approvals_to_add.clear();
pup.active_approvals_to_add.insert(account_id_type());
trx.operations = {pup};
sign( trx, committee_key );
//committee has no stake in the transaction.
GRAPHENE_CHECK_THROW(PUSH_TX( db, trx ), fc::exception);
trx.signatures.clear();
pup.active_approvals_to_add.clear();
pup.active_approvals_to_add.insert(nathan.id);
trx.operations = {pup};
sign( trx, nathan_key3 );
sign( trx, nathan_key2 );
BOOST_CHECK_EQUAL(get_balance(nathan, core), nathan_start_balance.amount.value);
PUSH_TX( db, trx );
BOOST_CHECK_EQUAL(get_balance(nathan, core), nathan_start_balance.amount.value - 100);
} catch (fc::exception& e) {
edump((e.to_detail_string()));
throw;
}
}
/// Verify that committee authority cannot be invoked in a normal transaction
BOOST_AUTO_TEST_CASE( committee_authority )
{ try {
fc::ecc::private_key nathan_key = fc::ecc::private_key::generate();
fc::ecc::private_key committee_key = init_account_priv_key;
const account_object nathan = create_account("nathan", nathan_key.get_public_key());
const auto& global_params = db.get_global_properties().parameters;
generate_block();
// Signatures are for suckers.
db.modify(db.get_global_properties(), [](global_property_object& p) {
// Turn the review period WAY down, so it doesn't take long to produce blocks to that point in simulated time.
p.parameters.committee_proposal_review_period = fc::days(1).to_seconds();
});
BOOST_TEST_MESSAGE( "transfering 100000 CORE to nathan, signing with committee key should fail because this requires it to be part of a proposal" );
transfer_operation top;
top.to = nathan.id;
top.amount = asset(100000);
trx.operations.push_back(top);
sign(trx, committee_key);
GRAPHENE_CHECK_THROW(PUSH_TX( db, trx ), graphene::chain::invalid_committee_approval );
auto _sign = [&] { trx.signatures.clear(); sign( trx, nathan_key ); };
proposal_create_operation pop;
pop.proposed_ops.push_back({trx.operations.front()});
pop.expiration_time = db.head_block_time() + global_params.committee_proposal_review_period*2;
pop.fee_paying_account = nathan.id;
trx.operations = {pop};
_sign();
// The review period isn't set yet. Make sure it throws.
GRAPHENE_REQUIRE_THROW( PUSH_TX( db, trx ), proposal_create_review_period_required );
pop.review_period_seconds = global_params.committee_proposal_review_period / 2;
trx.operations.back() = pop;
_sign();
// The review period is too short. Make sure it throws.
GRAPHENE_REQUIRE_THROW( PUSH_TX( db, trx ), proposal_create_review_period_insufficient );
pop.review_period_seconds = global_params.committee_proposal_review_period;
trx.operations.back() = pop;
_sign();
proposal_object prop = db.get<proposal_object>(PUSH_TX( db, trx ).operation_results.front().get<object_id_type>());
BOOST_REQUIRE(db.find_object(prop.id));
BOOST_CHECK(prop.expiration_time == pop.expiration_time);
BOOST_CHECK(prop.review_period_time && *prop.review_period_time == pop.expiration_time - *pop.review_period_seconds);
BOOST_CHECK(prop.proposed_transaction.operations.size() == 1);
BOOST_CHECK_EQUAL(get_balance(nathan, asset_id_type()(db)), 0);
BOOST_CHECK(!db.get<proposal_object>(prop.id).is_authorized_to_execute(db));
generate_block();
BOOST_REQUIRE(db.find_object(prop.id));
BOOST_CHECK_EQUAL(get_balance(nathan, asset_id_type()(db)), 0);
BOOST_TEST_MESSAGE( "Checking that the proposal is not authorized to execute" );
BOOST_REQUIRE(!db.get<proposal_object>(prop.id).is_authorized_to_execute(db));
trx.operations.clear();
trx.signatures.clear();
proposal_update_operation uop;
uop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT;
uop.proposal = prop.id;
uop.key_approvals_to_add.emplace(committee_key.get_public_key());
/*
uop.key_approvals_to_add.emplace(1);
uop.key_approvals_to_add.emplace(2);
uop.key_approvals_to_add.emplace(3);
uop.key_approvals_to_add.emplace(4);
uop.key_approvals_to_add.emplace(5);
uop.key_approvals_to_add.emplace(6);
*/
trx.operations.push_back(uop);
sign( trx, committee_key );
db.push_transaction(trx);
BOOST_CHECK_EQUAL(get_balance(nathan, asset_id_type()(db)), 0);
BOOST_CHECK(db.get<proposal_object>(prop.id).is_authorized_to_execute(db));
trx.signatures.clear();
generate_blocks(*prop.review_period_time);
uop.key_approvals_to_add.clear();
uop.key_approvals_to_add.insert(committee_key.get_public_key()); // was 7
trx.operations.back() = uop;
sign( trx, committee_key );
// Should throw because the transaction is now in review.
GRAPHENE_CHECK_THROW(PUSH_TX( db, trx ), fc::exception);
generate_blocks(prop.expiration_time);
BOOST_CHECK_EQUAL(get_balance(nathan, asset_id_type()(db)), 100000);
} FC_LOG_AND_RETHROW() }
BOOST_FIXTURE_TEST_CASE( fired_committee_members, database_fixture )
{ try {
generate_block();
fc::ecc::private_key committee_key = init_account_priv_key;
fc::ecc::private_key committee_member_key = fc::ecc::private_key::generate();
//Meet nathan. He has a little money.
const account_object* nathan = &create_account("nathan");
transfer(account_id_type()(db), *nathan, asset(5000));
generate_block();
nathan = &get_account("nathan");
flat_set<vote_id_type> committee_members;
/*
db.modify(db.get_global_properties(), [](global_property_object& p) {
// Turn the review period WAY down, so it doesn't take long to produce blocks to that point in simulated time.
p.parameters.committee_proposal_review_period = fc::days(1).to_seconds();
});
*/
for( int i = 0; i < 15; ++i )
{
const auto& account = create_account("committee-member" + fc::to_string(i+1), committee_member_key.get_public_key());
upgrade_to_lifetime_member(account);
committee_members.insert(create_committee_member(account).vote_id);
}
BOOST_REQUIRE_EQUAL(get_balance(*nathan, asset_id_type()(db)), 5000);
//A proposal is created to give nathan lots more money.
proposal_create_operation pop = proposal_create_operation::committee_proposal(db.get_global_properties().parameters, db.head_block_time());
pop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT;
pop.expiration_time = db.head_block_time() + *pop.review_period_seconds + fc::days(1).to_seconds();
ilog( "Creating proposal to give nathan money that expires: ${e}", ("e", pop.expiration_time ) );
ilog( "The proposal has a review period of: ${r} sec", ("r",*pop.review_period_seconds) );
transfer_operation top;
top.to = nathan->id;
top.amount = asset(100000);
pop.proposed_ops.emplace_back(top);
trx.operations.push_back(pop);
const proposal_object& prop = db.get<proposal_object>(PUSH_TX( db, trx ).operation_results.front().get<object_id_type>());
proposal_id_type pid = prop.id;
BOOST_CHECK(!pid(db).is_authorized_to_execute(db));
ilog( "commitee member approves proposal" );
//committee key approves of the proposal.
proposal_update_operation uop;
uop.fee_paying_account = GRAPHENE_TEMP_ACCOUNT;
uop.proposal = pid;
uop.key_approvals_to_add.emplace(init_account_pub_key);
trx.operations.back() = uop;
sign( trx, committee_key );
PUSH_TX( db, trx );
BOOST_CHECK(pid(db).is_authorized_to_execute(db));
ilog( "Generating blocks for 2 days" );
generate_block();
BOOST_REQUIRE_EQUAL(get_balance(*nathan, asset_id_type()(db)), 5000);
generate_block();
BOOST_REQUIRE_EQUAL(get_balance(*nathan, asset_id_type()(db)), 5000);
//Time passes... the proposal is now in its review period.
//generate_blocks(*pid(db).review_period_time);
generate_blocks(db.head_block_time() + fc::days(2) );
ilog( "head block time: ${t}", ("t",db.head_block_time()));
fc::time_point_sec maintenance_time = db.get_dynamic_global_properties().next_maintenance_time;
BOOST_CHECK_LT(maintenance_time.sec_since_epoch(), pid(db).expiration_time.sec_since_epoch());
//Yay! The proposal to give nathan more money is authorized.
BOOST_REQUIRE(pid(db).is_authorized_to_execute(db));
nathan = &get_account("nathan");
// no money yet
BOOST_REQUIRE_EQUAL(get_balance(*nathan, asset_id_type()(db)), 5000);
{
//Oh noes! Nathan votes for a whole new slate of committee_members!
account_update_operation op;
op.account = nathan->id;
op.new_options = nathan->options;
op.new_options->votes = committee_members;
trx.operations.push_back(op);
set_expiration( db, trx );
PUSH_TX( db, trx, ~0 );
trx.operations.clear();
}
idump((get_balance(*nathan, asset_id_type()(db))));
// still no money
BOOST_CHECK_EQUAL(get_balance(*nathan, asset_id_type()(db)), 5000);
//Time passes... the set of active committee_members gets updated.
generate_blocks(maintenance_time);
//The proposal is no longer authorized, because the active committee_members got changed.
BOOST_CHECK(!pid(db).is_authorized_to_execute(db));
// still no money
BOOST_CHECK_EQUAL(get_balance(*nathan, asset_id_type()(db)), 5000);
//Time passes... the proposal has now expired.
generate_blocks(pid(db).expiration_time);
BOOST_CHECK(db.find(pid) == nullptr);
//Nathan never got any more money because the proposal was rejected.
BOOST_CHECK_EQUAL(get_balance(*nathan, asset_id_type()(db)), 5000);
} FC_LOG_AND_RETHROW() }
BOOST_FIXTURE_TEST_CASE( proposal_two_accounts, database_fixture )
{ try {
generate_block();
auto nathan_key = generate_private_key("nathan");
auto dan_key = generate_private_key("dan");
const account_object& nathan = create_account("nathan", nathan_key.get_public_key() );
const account_object& dan = create_account("dan", dan_key.get_public_key() );
transfer(account_id_type()(db), nathan, asset(100000));
transfer(account_id_type()(db), dan, asset(100000));
{
transfer_operation top;
top.from = dan.get_id();
top.to = nathan.get_id();
top.amount = asset(500);
proposal_create_operation pop;
pop.proposed_ops.emplace_back(top);
std::swap(top.from, top.to);
pop.proposed_ops.emplace_back(top);
pop.fee_paying_account = nathan.get_id();
pop.expiration_time = db.head_block_time() + fc::days(1);
trx.operations.push_back(pop);
sign( trx, nathan_key );
PUSH_TX( db, trx );
trx.clear();
}
const proposal_object& prop = *db.get_index_type<proposal_index>().indices().begin();
BOOST_CHECK(prop.required_active_approvals.size() == 2);
BOOST_CHECK(prop.required_owner_approvals.size() == 0);
BOOST_CHECK(!prop.is_authorized_to_execute(db));
{
proposal_id_type pid = prop.id;
proposal_update_operation uop;
uop.proposal = prop.id;
uop.active_approvals_to_add.insert(nathan.get_id());
uop.fee_paying_account = nathan.get_id();
trx.operations.push_back(uop);
sign( trx, nathan_key );
PUSH_TX( db, trx );
trx.clear();
BOOST_CHECK(db.find_object(pid) != nullptr);
BOOST_CHECK(!prop.is_authorized_to_execute(db));
uop.active_approvals_to_add = {dan.get_id()};
trx.operations.push_back(uop);
sign( trx, nathan_key );
GRAPHENE_REQUIRE_THROW(PUSH_TX( db, trx ), fc::exception);
sign( trx, dan_key );
PUSH_TX( db, trx );
BOOST_CHECK(db.find_object(pid) == nullptr);
}
} FC_LOG_AND_RETHROW() }
BOOST_FIXTURE_TEST_CASE( proposal_delete, database_fixture )
{ try {
generate_block();
auto nathan_key = generate_private_key("nathan");
auto dan_key = generate_private_key("dan");
const account_object& nathan = create_account("nathan", nathan_key.get_public_key() );
const account_object& dan = create_account("dan", dan_key.get_public_key() );
transfer(account_id_type()(db), nathan, asset(100000));
transfer(account_id_type()(db), dan, asset(100000));
{
transfer_operation top;
top.from = dan.get_id();
top.to = nathan.get_id();
top.amount = asset(500);
proposal_create_operation pop;
pop.proposed_ops.emplace_back(top);
std::swap(top.from, top.to);
top.amount = asset(6000);
pop.proposed_ops.emplace_back(top);
pop.fee_paying_account = nathan.get_id();
pop.expiration_time = db.head_block_time() + fc::days(1);
trx.operations.push_back(pop);
sign( trx, nathan_key );
PUSH_TX( db, trx );
trx.clear();
}
const proposal_object& prop = *db.get_index_type<proposal_index>().indices().begin();
BOOST_CHECK(prop.required_active_approvals.size() == 2);
BOOST_CHECK(prop.required_owner_approvals.size() == 0);
BOOST_CHECK(!prop.is_authorized_to_execute(db));
{
proposal_update_operation uop;
uop.fee_paying_account = nathan.get_id();
uop.proposal = prop.id;
uop.active_approvals_to_add.insert(nathan.get_id());
trx.operations.push_back(uop);
sign( trx, nathan_key );
PUSH_TX( db, trx );
trx.clear();
BOOST_CHECK(!prop.is_authorized_to_execute(db));
BOOST_CHECK_EQUAL(prop.available_active_approvals.size(), 1);
std::swap(uop.active_approvals_to_add, uop.active_approvals_to_remove);
trx.operations.push_back(uop);
sign( trx, nathan_key );
PUSH_TX( db, trx );
trx.clear();
BOOST_CHECK(!prop.is_authorized_to_execute(db));
BOOST_CHECK_EQUAL(prop.available_active_approvals.size(), 0);
}
{
proposal_id_type pid = prop.id;
proposal_delete_operation dop;
dop.fee_paying_account = nathan.get_id();
dop.proposal = pid;
trx.operations.push_back(dop);
sign( trx, nathan_key );
PUSH_TX( db, trx );
BOOST_CHECK(db.find_object(pid) == nullptr);
BOOST_CHECK_EQUAL(get_balance(nathan, asset_id_type()(db)), 100000);
}
} FC_LOG_AND_RETHROW() }
BOOST_FIXTURE_TEST_CASE( proposal_owner_authority_delete, database_fixture )
{ try {
generate_block();
auto nathan_key = generate_private_key("nathan");
auto dan_key = generate_private_key("dan");
const account_object& nathan = create_account("nathan", nathan_key.get_public_key() );
const account_object& dan = create_account("dan", dan_key.get_public_key() );
transfer(account_id_type()(db), nathan, asset(100000));
transfer(account_id_type()(db), dan, asset(100000));
{
transfer_operation top;
top.from = dan.get_id();
top.to = nathan.get_id();
top.amount = asset(500);
account_update_operation uop;
uop.account = nathan.get_id();
uop.owner = authority(1, public_key_type(generate_private_key("nathan2").get_public_key()), 1);
proposal_create_operation pop;
pop.proposed_ops.emplace_back(top);
pop.proposed_ops.emplace_back(uop);
std::swap(top.from, top.to);
top.amount = asset(6000);
pop.proposed_ops.emplace_back(top);
pop.fee_paying_account = nathan.get_id();
pop.expiration_time = db.head_block_time() + fc::days(1);
trx.operations.push_back(pop);
sign( trx, nathan_key );
PUSH_TX( db, trx );
trx.clear();
}
const proposal_object& prop = *db.get_index_type<proposal_index>().indices().begin();
BOOST_CHECK_EQUAL(prop.required_active_approvals.size(), 1);
BOOST_CHECK_EQUAL(prop.required_owner_approvals.size(), 1);
BOOST_CHECK(!prop.is_authorized_to_execute(db));
{
proposal_update_operation uop;
uop.fee_paying_account = nathan.get_id();
uop.proposal = prop.id;
uop.owner_approvals_to_add.insert(nathan.get_id());
trx.operations.push_back(uop);
sign( trx, nathan_key );
PUSH_TX( db, trx );
trx.clear();
BOOST_CHECK(!prop.is_authorized_to_execute(db));
BOOST_CHECK_EQUAL(prop.available_owner_approvals.size(), 1);
std::swap(uop.owner_approvals_to_add, uop.owner_approvals_to_remove);
trx.operations.push_back(uop);
sign( trx, nathan_key );
PUSH_TX( db, trx );
trx.clear();
BOOST_CHECK(!prop.is_authorized_to_execute(db));
BOOST_CHECK_EQUAL(prop.available_owner_approvals.size(), 0);
}
{
proposal_id_type pid = prop.id;
proposal_delete_operation dop;
dop.fee_paying_account = nathan.get_id();
dop.proposal = pid;
dop.using_owner_authority = true;
trx.operations.push_back(dop);
sign( trx, nathan_key );
PUSH_TX( db, trx );
BOOST_CHECK(db.find_object(pid) == nullptr);
BOOST_CHECK_EQUAL(get_balance(nathan, asset_id_type()(db)), 100000);
}
} FC_LOG_AND_RETHROW() }
BOOST_FIXTURE_TEST_CASE( proposal_owner_authority_complete, database_fixture )
{ try {
generate_block();
auto nathan_key = generate_private_key("nathan");
auto dan_key = generate_private_key("dan");
const account_object& nathan = create_account("nathan", nathan_key.get_public_key() );
const account_object& dan = create_account("dan", dan_key.get_public_key() );
transfer(account_id_type()(db), nathan, asset(100000));
transfer(account_id_type()(db), dan, asset(100000));
{
transfer_operation top;
top.from = dan.get_id();
top.to = nathan.get_id();
top.amount = asset(500);
account_update_operation uop;
uop.account = nathan.get_id();
uop.owner = authority(1, public_key_type(generate_private_key("nathan2").get_public_key()), 1);
proposal_create_operation pop;
pop.proposed_ops.emplace_back(top);
pop.proposed_ops.emplace_back(uop);
std::swap(top.from, top.to);
top.amount = asset(6000);
pop.proposed_ops.emplace_back(top);
pop.fee_paying_account = nathan.get_id();
pop.expiration_time = db.head_block_time() + fc::days(1);
trx.operations.push_back(pop);
sign( trx, nathan_key );
PUSH_TX( db, trx );
trx.clear();
}
const proposal_object& prop = *db.get_index_type<proposal_index>().indices().begin();
BOOST_CHECK_EQUAL(prop.required_active_approvals.size(), 1);
BOOST_CHECK_EQUAL(prop.required_owner_approvals.size(), 1);
BOOST_CHECK(!prop.is_authorized_to_execute(db));
{
proposal_id_type pid = prop.id;
proposal_update_operation uop;
uop.fee_paying_account = nathan.get_id();
uop.proposal = prop.id;
uop.key_approvals_to_add.insert(dan.active.key_auths.begin()->first);
trx.operations.push_back(uop);
set_expiration( db, trx );
sign( trx, nathan_key );
sign( trx, dan_key );
PUSH_TX( db, trx );
trx.clear();
BOOST_CHECK(!prop.is_authorized_to_execute(db));
BOOST_CHECK_EQUAL(prop.available_key_approvals.size(), 1);
std::swap(uop.key_approvals_to_add, uop.key_approvals_to_remove);
trx.operations.push_back(uop);
trx.expiration += fc::seconds(1); // Survive trx dupe check
sign( trx, nathan_key );
sign( trx, dan_key );
PUSH_TX( db, trx );
trx.clear();
BOOST_CHECK(!prop.is_authorized_to_execute(db));
BOOST_CHECK_EQUAL(prop.available_key_approvals.size(), 0);
std::swap(uop.key_approvals_to_add, uop.key_approvals_to_remove);
trx.operations.push_back(uop);
trx.expiration += fc::seconds(1); // Survive trx dupe check
sign( trx, nathan_key );
sign( trx, dan_key );
PUSH_TX( db, trx );
trx.clear();
BOOST_CHECK(!prop.is_authorized_to_execute(db));
BOOST_CHECK_EQUAL(prop.available_key_approvals.size(), 1);
uop.key_approvals_to_add.clear();
uop.owner_approvals_to_add.insert(nathan.get_id());
trx.operations.push_back(uop);
trx.expiration += fc::seconds(1); // Survive trx dupe check
sign( trx, nathan_key );
PUSH_TX( db, trx );
trx.clear();
BOOST_CHECK(db.find_object(pid) == nullptr);
}
} FC_LOG_AND_RETHROW() }
BOOST_FIXTURE_TEST_CASE( max_authority_membership, database_fixture )
{
try
{
//Get a sane head block time
generate_block();
db.modify(db.get_global_properties(), [](global_property_object& p) {
p.parameters.committee_proposal_review_period = fc::hours(1).to_seconds();
});
transaction tx;
processed_transaction ptx;
private_key_type committee_key = init_account_priv_key;
// Sam is the creator of accounts
private_key_type sam_key = generate_private_key("sam");
account_object sam_account_object = create_account( "sam", sam_key );
upgrade_to_lifetime_member(sam_account_object);
account_object committee_account_object = committee_account(db);
const asset_object& core = asset_id_type()(db);
transfer(committee_account_object, sam_account_object, core.amount(100000));
// have Sam create some keys
int keys_to_create = 2*GRAPHENE_DEFAULT_MAX_AUTHORITY_MEMBERSHIP;
vector<private_key_type> private_keys;
private_keys.reserve( keys_to_create );
for( int i=0; i<keys_to_create; i++ )
{
string seed = "this_is_a_key_" + std::to_string(i);
private_key_type privkey = generate_private_key( seed );
private_keys.push_back( privkey );
}
set_expiration( db, tx );
vector<public_key_type> key_ids;
key_ids.reserve( keys_to_create );
for( int i=0; i<keys_to_create; i++ )
key_ids.push_back( private_keys[i].get_public_key() );
// now try registering accounts with n keys, 0 < n < 20
// TODO: Make sure it throws / accepts properly when
// max_account_authority is changed in global parameteres
for( int num_keys=1; num_keys<=keys_to_create; num_keys++ )
{
// try registering account with n keys
authority test_authority;
test_authority.weight_threshold = num_keys;
for( int i=0; i<num_keys; i++ )
test_authority.key_auths[ key_ids[i] ] = 1;
auto check_tx = [&]( const authority& owner_auth,
const authority& active_auth )
{
const uint16_t max_authority_membership = GRAPHENE_DEFAULT_MAX_AUTHORITY_MEMBERSHIP;
account_create_operation anon_create_op;
transaction tx;
anon_create_op.owner = owner_auth;
anon_create_op.active = active_auth;
anon_create_op.registrar = sam_account_object.id;
anon_create_op.options.memo_key = sam_account_object.options.memo_key;
anon_create_op.name = generate_anon_acct_name();
tx.operations.push_back( anon_create_op );
set_expiration( db, tx );
if( num_keys > max_authority_membership )
{
GRAPHENE_REQUIRE_THROW( PUSH_TX( db, tx, ~0 ), account_create_max_auth_exceeded );
}
else
{
PUSH_TX( db, tx, ~0 );
}
return;
};
check_tx( sam_account_object.owner, test_authority );
check_tx( test_authority, sam_account_object.active );
check_tx( test_authority, test_authority );
}
}
FC_LOG_AND_RETHROW()
}
BOOST_FIXTURE_TEST_CASE( bogus_signature, database_fixture )
{
try
{
private_key_type committee_key = init_account_priv_key;
// Sam is the creator of accounts
private_key_type alice_key = generate_private_key("alice");
private_key_type bob_key = generate_private_key("bob");
private_key_type charlie_key = generate_private_key("charlie");
account_object committee_account_object = committee_account(db);
account_object alice_account_object = create_account( "alice", alice_key );
account_object bob_account_object = create_account( "bob", bob_key );
account_object charlie_account_object = create_account( "charlie", charlie_key );
// unneeded, comment it out to silence compiler warning
//key_id_type bob_key_id = bob_account_object.memo_key;
uint32_t skip = database::skip_transaction_dupe_check;
// send from Sam -> Alice, signed by Sam
const asset_object& core = asset_id_type()(db);
transfer(committee_account_object, alice_account_object, core.amount(100000));
transfer_operation xfer_op;
xfer_op.from = alice_account_object.id;
xfer_op.to = bob_account_object.id;
xfer_op.amount = core.amount(5000);
xfer_op.fee = db.current_fee_schedule().calculate_fee( xfer_op );
trx.clear();
trx.operations.push_back( xfer_op );
BOOST_TEST_MESSAGE( "Transfer signed by alice" );
sign( trx, alice_key );
flat_set<account_id_type> active_set, owner_set;
vector<authority> others;
trx.get_required_authorities( active_set, owner_set, others );
PUSH_TX( db, trx, skip );
trx.operations.push_back( xfer_op );
BOOST_TEST_MESSAGE( "Invalidating Alices Signature" );
// Alice's signature is now invalid
GRAPHENE_REQUIRE_THROW( PUSH_TX( db, trx, skip ), fc::exception );
// Re-sign, now OK (sig is replaced)
BOOST_TEST_MESSAGE( "Resign with Alice's Signature" );
trx.signatures.clear();
sign( trx, alice_key );
PUSH_TX( db, trx, skip );
trx.signatures.clear();
trx.operations.pop_back();
sign( trx, alice_key );
sign( trx, charlie_key );
// Signed by third-party Charlie (irrelevant key, not in authority)
GRAPHENE_REQUIRE_THROW( PUSH_TX( db, trx, skip ), tx_irrelevant_sig );
}
FC_LOG_AND_RETHROW()
}
BOOST_FIXTURE_TEST_CASE( voting_account, database_fixture )
{ try {
ACTORS((nathan)(vikram));
upgrade_to_lifetime_member(nathan_id);
upgrade_to_lifetime_member(vikram_id);
committee_member_id_type nathan_committee_member = create_committee_member(nathan_id(db)).id;
committee_member_id_type vikram_committee_member = create_committee_member(vikram_id(db)).id;
//wdump((db.get_balance(account_id_type(), asset_id_type())));
generate_block();
//wdump((db.get_balance(account_id_type(), asset_id_type())));
transfer(account_id_type(), nathan_id, asset(1000000));
transfer(account_id_type(), vikram_id, asset(100));
{
account_update_operation op;
op.account = nathan_id;
op.new_options = nathan_id(db).options;
op.new_options->voting_account = vikram_id;
op.new_options->votes = flat_set<vote_id_type>{nathan_committee_member(db).vote_id};
op.new_options->num_committee = 1;
trx.operations.push_back(op);
sign( trx, nathan_private_key );
PUSH_TX( db, trx );
trx.clear();
}
{
account_update_operation op;
op.account = vikram_id;
op.new_options = vikram_id(db).options;
op.new_options->votes.insert(vikram_committee_member(db).vote_id);
op.new_options->num_committee = 11;
trx.operations.push_back(op);
sign( trx, vikram_private_key );
// Fails because num_committee is larger than the cardinality of committee members being voted for
GRAPHENE_CHECK_THROW(PUSH_TX( db, trx ), fc::exception);
op.new_options->num_committee = 3;
trx.operations = {op};
trx.signatures.clear();
sign( trx, vikram_private_key );
PUSH_TX( db, trx );
trx.clear();
}
generate_blocks(db.get_dynamic_global_properties().next_maintenance_time + GRAPHENE_DEFAULT_BLOCK_INTERVAL);
BOOST_CHECK(std::find(db.get_global_properties().active_committee_members.begin(),
db.get_global_properties().active_committee_members.end(),
nathan_committee_member) == db.get_global_properties().active_committee_members.end());
BOOST_CHECK(std::find(db.get_global_properties().active_committee_members.begin(),
db.get_global_properties().active_committee_members.end(),
vikram_committee_member) != db.get_global_properties().active_committee_members.end());
} FC_LOG_AND_RETHROW() }
/*
* Simple corporate accounts:
*
* Well Corp. Alice 50, Bob 50 T=60
* Xylo Company Alice 30, Cindy 50 T=40
* Yaya Inc. Bob 10, Dan 10, Edy 10 T=20
* Zyzz Co. Dan 50 T=40
*
* Complex corporate accounts:
*
* Mega Corp. Well 30, Yes 30 T=40
* Nova Ltd. Alice 10, Well 10 T=20
* Odle Intl. Dan 10, Yes 10, Zyzz 10 T=20
* Poxx LLC Well 10, Xylo 10, Yes 20, Zyzz 20 T=40
*/
BOOST_FIXTURE_TEST_CASE( get_required_signatures_test, database_fixture )
{
try
{
ACTORS(
(alice)(bob)(cindy)(dan)(edy)
(mega)(nova)(odle)(poxx)
(well)(xylo)(yaya)(zyzz)
);
auto set_auth = [&](
account_id_type aid,
const authority& auth
)
{
signed_transaction tx;
account_update_operation op;
op.account = aid;
op.active = auth;
op.owner = auth;
tx.operations.push_back( op );
set_expiration( db, tx );
PUSH_TX( db, tx, database::skip_transaction_signatures | database::skip_authority_check );
} ;
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,
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 );
//wdump( (result_set)(ref_set) );
return result_set == ref_set;
} ;
set_auth( well_id, authority( 60, alice_id, 50, bob_id, 50 ) );
set_auth( xylo_id, authority( 40, alice_id, 30, cindy_id, 50 ) );
set_auth( yaya_id, authority( 20, bob_id, 10, dan_id, 10, edy_id, 10 ) );
set_auth( zyzz_id, authority( 40, dan_id, 50 ) );
set_auth( mega_id, authority( 40, well_id, 30, yaya_id, 30 ) );
set_auth( nova_id, authority( 20, alice_id, 10, well_id, 10 ) );
set_auth( odle_id, authority( 20, dan_id, 10, yaya_id, 10, zyzz_id, 10 ) );
set_auth( poxx_id, authority( 40, well_id, 10, xylo_id, 10, yaya_id, 20, zyzz_id, 20 ) );
signed_transaction tx;
flat_set< public_key_type > all_keys
{ alice_public_key, bob_public_key, cindy_public_key, dan_public_key, edy_public_key };
tx.operations.push_back( transfer_operation() );
transfer_operation& op = tx.operations.back().get<transfer_operation>();
op.to = edy_id;
op.amount = asset(1);
op.from = alice_id;
BOOST_CHECK( chk( tx, all_keys, { alice_public_key } ) );
op.from = bob_id;
BOOST_CHECK( chk( tx, all_keys, { bob_public_key } ) );
op.from = well_id;
BOOST_CHECK( chk( tx, all_keys, { alice_public_key, bob_public_key } ) );
op.from = xylo_id;
BOOST_CHECK( chk( tx, all_keys, { alice_public_key, cindy_public_key } ) );
op.from = yaya_id;
BOOST_CHECK( chk( tx, all_keys, { bob_public_key, dan_public_key } ) );
op.from = zyzz_id;
BOOST_CHECK( chk( tx, all_keys, { dan_public_key } ) );
op.from = mega_id;
BOOST_CHECK( chk( tx, all_keys, { alice_public_key, bob_public_key, dan_public_key } ) );
op.from = nova_id;
BOOST_CHECK( chk( tx, all_keys, { alice_public_key, bob_public_key } ) );
op.from = odle_id;
BOOST_CHECK( chk( tx, all_keys, { bob_public_key, dan_public_key } ) );
op.from = poxx_id;
BOOST_CHECK( chk( tx, all_keys, { alice_public_key, bob_public_key, cindy_public_key, dan_public_key } ) );
// TODO: Add sigs to tx, then check
// TODO: Check removing sigs
// TODO: Accounts with mix of keys and accounts in their authority
// TODO: Tx with multiple ops requiring different sigs
}
catch(fc::exception& e)
{
edump((e.to_detail_string()));
throw;
}
}
/*
* Pathological case
*
* Roco(T=2)
* 1/ \2
* Styx(T=2) Thud(T=1)
* 1/ \1 |1
* Alice Bob Alice
*/
BOOST_FIXTURE_TEST_CASE( nonminimal_sig_test, database_fixture )
{
try
{
ACTORS(
(alice)(bob)
(roco)
(styx)(thud)
);
auto set_auth = [&](
account_id_type aid,
const authority& auth
)
{
signed_transaction tx;
account_update_operation op;
op.account = aid;
op.active = auth;
op.owner = auth;
tx.operations.push_back( op );
set_expiration( db, tx );
PUSH_TX( db, tx, database::skip_transaction_signatures | database::skip_authority_check );
} ;
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,
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 );
//wdump( (result_set)(ref_set) );
return result_set == ref_set;
} ;
auto chk_min = [&](
const signed_transaction& tx,
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.minimize_required_signatures( db.get_chain_id(), available_keys, get_active, get_owner );
//wdump( (result_set)(ref_set) );
return result_set == ref_set;
} ;
set_auth( roco_id, authority( 2, styx_id, 1, thud_id, 2 ) );
set_auth( styx_id, authority( 2, alice_id, 1, bob_id, 1 ) );
set_auth( thud_id, authority( 1, alice_id, 1 ) );
signed_transaction tx;
transfer_operation op;
op.from = roco_id;
op.to = bob_id;
op.amount = asset(1);
tx.operations.push_back( op );
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 );
sign( tx, alice_private_key );
tx.verify_authority( db.get_chain_id(), get_active, get_owner );
}
catch(fc::exception& e)
{
edump((e.to_detail_string()));
throw;
}
}
BOOST_AUTO_TEST_SUITE_END()