diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index 77407e1045..acb90c8af5 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -272,7 +272,6 @@ class database_api_impl : public std::enable_shared_from_this map< pair, std::function > _market_subscriptions; graphene::chain::database& _db; const application_options* _app_options = nullptr; - }; ////////////////////////////////////////////////////////////////////// @@ -2068,17 +2067,27 @@ bool database_api::verify_account_authority( const string& account_name_or_id, c return my->verify_account_authority( account_name_or_id, signers ); } -bool database_api_impl::verify_account_authority( const string& account_name_or_id, const flat_set& keys )const +bool database_api_impl::verify_account_authority( const string& account_name_or_id, + const flat_set& keys )const { - const account_object* account = get_account_from_string(account_name_or_id); - - /// reuse trx.verify_authority by creating a dummy transfer - signed_transaction trx; + // create a dummy transfer transfer_operation op; - op.from = account->id; - trx.operations.emplace_back(op); + op.from = get_account_from_string(account_name_or_id)->id; + std::vector ops; + ops.emplace_back(op); - return verify_authority( trx ); + try + { + graphene::chain::verify_authority(ops, keys, + [this]( account_id_type id ){ return &id(_db).active; }, + [this]( account_id_type id ){ return &id(_db).owner; } ); + } + catch (fc::exception& ex) + { + return false; + } + + return true; } processed_transaction database_api::validate_transaction( const signed_transaction& trx )const diff --git a/libraries/app/include/graphene/app/database_api.hpp b/libraries/app/include/graphene/app/database_api.hpp index e43f5dfc3d..506b427597 100644 --- a/libraries/app/include/graphene/app/database_api.hpp +++ b/libraries/app/include/graphene/app/database_api.hpp @@ -669,9 +669,12 @@ class database_api bool verify_authority( const signed_transaction& trx )const; /** - * @return true if the signers have enough authority to authorize an account + * @brief Verify that the public keys have enough authority to approve an operation for this account + * @param account_name_or_id the account to check + * @param signers the public keys + * @return true if the passed in keys have enough authority to approve an operation for this account */ - bool verify_account_authority( const string& account_name_or_id, const flat_set& signers )const; + bool verify_account_authority( const string& account_name_or_id, const flat_set& signers )const; /** * Validates a transaction against the current state without broadcasting it on the network. diff --git a/tests/tests/database_api_tests.cpp b/tests/tests/database_api_tests.cpp index 2577f57710..1eeb177b42 100644 --- a/tests/tests/database_api_tests.cpp +++ b/tests/tests/database_api_tests.cpp @@ -831,4 +831,114 @@ BOOST_AUTO_TEST_CASE( get_transaction_hex ) } FC_LOG_AND_RETHROW() } +BOOST_AUTO_TEST_CASE(verify_account_authority) +{ + try { + + ACTORS( (nathan) ); + graphene::app::database_api db_api(db); + + // good keys + flat_set public_keys; + public_keys.emplace(nathan_public_key); + BOOST_CHECK(db_api.verify_account_authority( "nathan", public_keys)); + + // bad keys + flat_set bad_public_keys; + bad_public_keys.emplace(public_key_type("BTS6MkMxwBjFWmcDjXRoJ4mW9Hd4LCSPwtv9tKG1qYW5Kgu4AhoZy")); + BOOST_CHECK(!db_api.verify_account_authority( "nathan", bad_public_keys)); + + } 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() ); + fund(nathan); + graphene::app::database_api db_api(db); + + 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.clear(); + } FC_CAPTURE_AND_RETHROW ((nathan.active)) + + // two keys should work + { + flat_set public_keys; + public_keys.emplace(nathan_key1.get_public_key()); + public_keys.emplace(nathan_key2.get_public_key()); + BOOST_CHECK(db_api.verify_account_authority("nathan", public_keys)); + } + + // the other two keys should work + { + flat_set public_keys; + public_keys.emplace(nathan_key2.get_public_key()); + public_keys.emplace(nathan_key3.get_public_key()); + BOOST_CHECK(db_api.verify_account_authority("nathan", public_keys)); + } + + // just one key should not work + { + flat_set public_keys; + public_keys.emplace(nathan_key1.get_public_key()); + BOOST_CHECK(!db_api.verify_account_authority("nathan", public_keys)); + } + } catch (fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} + +BOOST_AUTO_TEST_CASE( verify_authority_multiple_accounts ) +{ + try { + ACTORS( (nathan) (alice) (bob) ); + + graphene::app::database_api db_api(db); + + try { + account_update_operation op; + op.account = nathan.id; + op.active = authority(3, nathan_public_key, 1, alice.id, 1, bob.id, 1); + op.owner = *op.active; + trx.operations.push_back(op); + sign(trx, nathan_private_key); + PUSH_TX( db, trx, database::skip_transaction_dupe_check ); + trx.clear(); + } FC_CAPTURE_AND_RETHROW ((nathan.active)) + + // requires 3 signatures + { + flat_set public_keys; + public_keys.emplace(nathan_public_key); + public_keys.emplace(alice_public_key); + public_keys.emplace(bob_public_key); + BOOST_CHECK(db_api.verify_account_authority("nathan", public_keys)); + } + + // only 2 signatures given + { + flat_set public_keys; + public_keys.emplace(nathan_public_key); + public_keys.emplace(bob_public_key); + BOOST_CHECK(!db_api.verify_account_authority("nathan", public_keys)); + } + } catch (fc::exception& e) { + edump((e.to_detail_string())); + throw; + } +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/tests/wallet_tests.cpp b/tests/tests/wallet_tests.cpp index 554b729703..8601f747d5 100644 --- a/tests/tests/wallet_tests.cpp +++ b/tests/tests/wallet_tests.cpp @@ -26,6 +26,7 @@ #include #include +#include #include @@ -45,7 +46,7 @@ BOOST_FIXTURE_TEST_SUITE(wallet_tests, database_fixture) /*** * Act */ - int nbr_keys_desired = 3; + unsigned int nbr_keys_desired = 3; vector derived_keys = graphene::wallet::utility::derive_owner_keys_from_brain_key("SOME WORDS GO HERE", nbr_keys_desired); @@ -70,10 +71,11 @@ BOOST_FIXTURE_TEST_SUITE(wallet_tests, database_fixture) string expected_prefix = GRAPHENE_ADDRESS_PREFIX; for (auto info : derived_keys) { string description = (string) info.pub_key; - BOOST_CHECK_EQUAL(0, description.find(expected_prefix)); + BOOST_CHECK_EQUAL(0u, description.find(expected_prefix)); } } FC_LOG_AND_RETHROW() } - + BOOST_AUTO_TEST_SUITE_END() +