From cd74fbbe4d464e7d87caaabecba6c9ddd009a26d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CMariusz=20Trela=E2=80=9D?= Date: Fri, 3 Nov 2023 09:08:01 +0100 Subject: [PATCH] Add `has_matching_private_key` endpoint into beekeeper --- .../beekeeper/beekeeper_wallet_api.cpp | 9 ++++ .../beekeeper/beekeeper_wallet_api.hpp | 1 + .../__tests__/assets/run_node_helper.js | 7 +++ .../__tests__/detailed/beekeeper_api.spec.ts | 18 +++++++ .../beekeeper_wasm/beekeeper_wasm_api.cpp | 10 ++++ .../beekeeper_wasm/beekeeper_wasm_api.hpp | 2 + programs/beekeeper/beekeeper_wasm/main.cpp | 12 +++++ programs/beekeeper/core/beekeeper_wallet.cpp | 5 ++ .../core/beekeeper_wallet_manager.cpp | 6 +++ .../core/include/core/beekeeper_wallet.hpp | 10 +++- .../include/core/beekeeper_wallet_base.hpp | 11 ++++- .../include/core/beekeeper_wallet_manager.hpp | 7 +++ .../beekeeper/core/include/core/utilities.hpp | 15 +++++- .../core/include/core/wallet_manager_impl.hpp | 1 + programs/beekeeper/core/utilities.cpp | 5 ++ .../beekeeper/core/wallet_manager_impl.cpp | 10 ++++ tests/unit/tests/beekeeper_tests.cpp | 47 +++++++++++++++++++ 17 files changed, 173 insertions(+), 3 deletions(-) diff --git a/programs/beekeeper/beekeeper/beekeeper_wallet_api.cpp b/programs/beekeeper/beekeeper/beekeeper_wallet_api.cpp index 3b66a2d797..aab268582e 100644 --- a/programs/beekeeper/beekeeper/beekeeper_wallet_api.cpp +++ b/programs/beekeeper/beekeeper/beekeeper_wallet_api.cpp @@ -44,6 +44,7 @@ class beekeeper_api_impl (get_info) (create_session) (close_session) + (has_matching_private_key) ) std::shared_ptr _wallet_mgr; @@ -175,6 +176,13 @@ DEFINE_API_IMPL( beekeeper_api_impl, close_session ) return close_session_return(); } +DEFINE_API_IMPL( beekeeper_api_impl, has_matching_private_key ) +{ + std::lock_guard guard( mtx ); + + return { _wallet_mgr->has_matching_private_key( args.token, args.wallet_name, args.public_key ) }; +} + } // detail beekeeper_wallet_api::beekeeper_wallet_api( std::shared_ptr wallet_mgr, appbase::application& app ): my( new detail::beekeeper_api_impl( wallet_mgr ) ) @@ -201,6 +209,7 @@ DEFINE_LOCKLESS_APIS( beekeeper_wallet_api, (get_info) (create_session) (close_session) + (has_matching_private_key) ) } // beekeeper diff --git a/programs/beekeeper/beekeeper/include/beekeeper/beekeeper_wallet_api.hpp b/programs/beekeeper/beekeeper/include/beekeeper/beekeeper_wallet_api.hpp index cc74bf48de..346a783b53 100644 --- a/programs/beekeeper/beekeeper/include/beekeeper/beekeeper_wallet_api.hpp +++ b/programs/beekeeper/beekeeper/include/beekeeper/beekeeper_wallet_api.hpp @@ -44,6 +44,7 @@ class beekeeper_wallet_api (get_info) (create_session) (close_session) + (has_matching_private_key) ) private: diff --git a/programs/beekeeper/beekeeper_wasm/__tests__/assets/run_node_helper.js b/programs/beekeeper/beekeeper_wasm/__tests__/assets/run_node_helper.js index 0415a5ecac..d0a9ff88ac 100644 --- a/programs/beekeeper/beekeeper_wasm/__tests__/assets/run_node_helper.js +++ b/programs/beekeeper/beekeeper_wasm/__tests__/assets/run_node_helper.js @@ -115,6 +115,13 @@ class BeekeeperInstanceHelper { return this.#extract(returnedValue); } + hasMatchingPrivateKey(token, walletName, publicKey) { + const returnedValue = this.instance.has_matching_private_key(token, walletName, publicKey); + const value = this.#extract(returnedValue); + + return value.exists; + } + create(sessionToken, walletName) { const returnedValue = this.instance.create(sessionToken, walletName); diff --git a/programs/beekeeper/beekeeper_wasm/__tests__/detailed/beekeeper_api.spec.ts b/programs/beekeeper/beekeeper_wasm/__tests__/detailed/beekeeper_api.spec.ts index a47e47b268..4b9912a5d9 100644 --- a/programs/beekeeper/beekeeper_wasm/__tests__/detailed/beekeeper_api.spec.ts +++ b/programs/beekeeper/beekeeper_wasm/__tests__/detailed/beekeeper_api.spec.ts @@ -956,6 +956,24 @@ test.describe('WASM beekeeper_api tests', () => { } } + { + console.log(); + console.log("**************************************************************************************"); + console.log("Check `has_matching_private_key` endpoint."); + console.log("**************************************************************************************"); + + /** @type {BeekeeperInstanceHelper} */ + const api = new beekeper(args); + + { + const walletNo = 1; + api.unlock(api.implicitSessionToken, walletNames[walletNo]); + assert.equal(api.hasMatchingPrivateKey(api.implicitSessionToken, walletNames[walletNo], keys[0][1]), false); + api.importKey(api.implicitSessionToken, walletNames[walletNo], keys[0][0]); + assert.equal(api.hasMatchingPrivateKey(api.implicitSessionToken, walletNames[walletNo], keys[0][1]), true); + } + } + console.log('##########################################################################################'); console.log('## ALL TESTS PASSED ##'); console.log('##########################################################################################'); diff --git a/programs/beekeeper/beekeeper_wasm/beekeeper_wasm_api.cpp b/programs/beekeeper/beekeeper_wasm/beekeeper_wasm_api.cpp index c720c124d5..cb4ec227bb 100644 --- a/programs/beekeeper/beekeeper_wasm/beekeeper_wasm_api.cpp +++ b/programs/beekeeper/beekeeper_wasm/beekeeper_wasm_api.cpp @@ -287,4 +287,14 @@ namespace beekeeper { }; return exception_handler( _method ); } + + std::string beekeeper_api::has_matching_private_key( const std::string& token, const std::string& wallet_name, const std::string& public_key ) + { + auto _method = [&, this]() + { + has_matching_private_key_return _result{ _impl->app.get_wallet_manager()->has_matching_private_key( token, wallet_name, public_key ) }; + return to_string( _result ); + }; + return exception_handler( _method ); + } }; diff --git a/programs/beekeeper/beekeeper_wasm/include/beekeeper_wasm/beekeeper_wasm_api.hpp b/programs/beekeeper/beekeeper_wasm/include/beekeeper_wasm/beekeeper_wasm_api.hpp index fd75f6249d..c5a41174e1 100644 --- a/programs/beekeeper/beekeeper_wasm/include/beekeeper_wasm/beekeeper_wasm_api.hpp +++ b/programs/beekeeper/beekeeper_wasm/include/beekeeper_wasm/beekeeper_wasm_api.hpp @@ -70,6 +70,8 @@ class beekeeper_api final std::string sign_digest( const std::string& token, const std::string& sig_digest, const std::string& public_key, const std::string& wallet_name ); std::string get_info( const std::string& token ); + + std::string has_matching_private_key( const std::string& token, const std::string& wallet_name, const std::string& public_key ); }; } diff --git a/programs/beekeeper/beekeeper_wasm/main.cpp b/programs/beekeeper/beekeeper_wasm/main.cpp index 54f3b49f7d..bf7fdf2f31 100644 --- a/programs/beekeeper/beekeeper_wasm/main.cpp +++ b/programs/beekeeper/beekeeper_wasm/main.cpp @@ -212,6 +212,18 @@ EMSCRIPTEN_BINDINGS(beekeeper_api_instance) { timeout_time: time when wallets will be automatically closed */ .function("get_info(token)", &beekeeper_api::get_info) + + /* + ****testing if a private key corresponding to a public key exists in a wallet**** + PARAMS: + token: a token representing a session + wallet_name: a name of wallet + public_key: a public key corresponding to a private key that is stored in a wallet + RESULT: + {"exists":true} + exists: true if a private key exists otherwise false + */ + .function("has_matching_private_key(token, wallet_name, public_key)", &beekeeper_api::has_matching_private_key) ; } diff --git a/programs/beekeeper/core/beekeeper_wallet.cpp b/programs/beekeeper/core/beekeeper_wallet.cpp index 411f3d54bf..3dcaab0b45 100644 --- a/programs/beekeeper/core/beekeeper_wallet.cpp +++ b/programs/beekeeper/core/beekeeper_wallet.cpp @@ -384,6 +384,11 @@ void beekeeper_wallet::set_wallet_filename(string wallet_filename) my->_wallet_filename = wallet_filename; } +bool beekeeper_wallet::has_matching_private_key( const public_key_type& public_key ) +{ + return my->try_get_private_key( public_key ).has_value(); +} + } //beekeeper_wallet namespace fc diff --git a/programs/beekeeper/core/beekeeper_wallet_manager.cpp b/programs/beekeeper/core/beekeeper_wallet_manager.cpp index 7ba54659c4..c1a2d3086a 100644 --- a/programs/beekeeper/core/beekeeper_wallet_manager.cpp +++ b/programs/beekeeper/core/beekeeper_wallet_manager.cpp @@ -136,6 +136,12 @@ void beekeeper_wallet_manager::close_session( const string& token, bool allow_cl --session_cnt; } +bool beekeeper_wallet_manager::has_matching_private_key( const std::string& token, const std::string& name, const std::string& public_key ) +{ + sessions->check_timeout( token ); + return sessions->get_wallet_manager( token )->has_matching_private_key( name, create_public_key( public_key ) ); +} + public_key_type beekeeper_wallet_manager::create_public_key( const std::string& public_key ) { FC_ASSERT( public_key_size == public_key.size(), "Incorrect size of public key. Expected ${public_key_size}, but got ${public_key_size_2}", diff --git a/programs/beekeeper/core/include/core/beekeeper_wallet.hpp b/programs/beekeeper/core/include/core/beekeeper_wallet.hpp index 4a31ccf392..2ce9d7a0c3 100644 --- a/programs/beekeeper/core/include/core/beekeeper_wallet.hpp +++ b/programs/beekeeper/core/include/core/beekeeper_wallet.hpp @@ -156,7 +156,7 @@ class beekeeper_wallet final : public beekeeper_wallet_base /** Removes a key from the wallet. * - * example: remove_key EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV + * example: remove_key 6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV * * @param key the Public Key to remove */ @@ -168,6 +168,14 @@ class beekeeper_wallet final : public beekeeper_wallet_base std::shared_ptr my; void encrypt_keys(); + + /** Tests if a private key corresponding to a public key exists in a wallet + * + * example: has_matching_private_key 6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV + * + * @param key the Public Key to remove + */ + bool has_matching_private_key( const public_key_type& public_key ) override; }; struct plain_keys { diff --git a/programs/beekeeper/core/include/core/beekeeper_wallet_base.hpp b/programs/beekeeper/core/include/core/beekeeper_wallet_base.hpp index 3a6874ffa7..144eafacdc 100644 --- a/programs/beekeeper/core/include/core/beekeeper_wallet_base.hpp +++ b/programs/beekeeper/core/include/core/beekeeper_wallet_base.hpp @@ -79,7 +79,7 @@ class beekeeper_wallet_base /** Removes a key from the wallet. * - * example: remove_key EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV + * example: remove_key 6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV * * @param key the Public Key to remove */ @@ -88,6 +88,15 @@ class beekeeper_wallet_base /** Returns a signature given the digest and public_key, if this wallet can sign via that public key */ virtual std::optional try_sign_digest( const digest_type& sig_digest, const public_key_type& public_key ) = 0; + + /** Tests if a private key corresponding to a public key exists in a wallet + * + * example: has_matching_private_key 6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV + * + * @param a public key to test + * @returns informatio if a private key exists + */ + virtual bool has_matching_private_key( const public_key_type& public_key ) = 0; }; } diff --git a/programs/beekeeper/core/include/core/beekeeper_wallet_manager.hpp b/programs/beekeeper/core/include/core/beekeeper_wallet_manager.hpp index cd7e46b06d..510edaf3a0 100644 --- a/programs/beekeeper/core/include/core/beekeeper_wallet_manager.hpp +++ b/programs/beekeeper/core/include/core/beekeeper_wallet_manager.hpp @@ -133,6 +133,13 @@ class beekeeper_wallet_manager */ void close_session( const string& token, bool allow_close_all_sessions_action = true ); + /// Tests if a private key corresponding to a public key exists in a wallet + /// The wallet must be opened and unlocked. + /// @param name the name of the wallet to test a private key. + /// @param public_key a public key corresponding to a private key that is stored in the wallet + /// @returns true if a private key exists otherwise false + bool has_matching_private_key( const std::string& token, const std::string& name, const std::string& public_key ); + private: seconds_type unlock_timeout = 900; diff --git a/programs/beekeeper/core/include/core/utilities.hpp b/programs/beekeeper/core/include/core/utilities.hpp index cc63805a02..633421e903 100644 --- a/programs/beekeeper/core/include/core/utilities.hpp +++ b/programs/beekeeper/core/include/core/utilities.hpp @@ -192,6 +192,16 @@ using create_session_return = session_token_type; using close_session_args = session_token_type; using close_session_return = void_type; +struct has_matching_private_key_args : public wallet_args +{ + std::string public_key; +}; + +struct has_matching_private_key_return +{ + bool exists = false; +}; + struct exception { static std::pair exception_handler( std::function&& method, const std::string& additional_message = "" ) @@ -239,6 +249,7 @@ namespace fc void to_variant( const beekeeper::public_key_details& var, fc::variant& vo ); void to_variant( const beekeeper::signature_return& var, fc::variant& vo ); void to_variant( const beekeeper::init_data& var, fc::variant& vo ); + void to_variant( const beekeeper::has_matching_private_key_return& var, fc::variant& vo ); } FC_REFLECT( beekeeper::init_data, (status)(version) ) @@ -262,4 +273,6 @@ FC_REFLECT( beekeeper::get_public_keys_return, (keys) ) FC_REFLECT_DERIVED( beekeeper::sign_digest_args, (beekeeper::session_token_type), (sig_digest)(public_key)(wallet_name) ) FC_REFLECT( beekeeper::signature_return, (signature) ) FC_REFLECT( beekeeper::create_session_args, (salt)(notifications_endpoint) ) -FC_REFLECT_DERIVED( beekeeper::get_public_keys_args, (beekeeper::session_token_type), (wallet_name) ) \ No newline at end of file +FC_REFLECT_DERIVED( beekeeper::get_public_keys_args, (beekeeper::session_token_type), (wallet_name) ) +FC_REFLECT_DERIVED( beekeeper::has_matching_private_key_args, (beekeeper::wallet_args), (public_key) ) +FC_REFLECT( beekeeper::has_matching_private_key_return, (exists) ) diff --git a/programs/beekeeper/core/include/core/wallet_manager_impl.hpp b/programs/beekeeper/core/include/core/wallet_manager_impl.hpp index e8e27938f5..c1ac80fcc2 100644 --- a/programs/beekeeper/core/include/core/wallet_manager_impl.hpp +++ b/programs/beekeeper/core/include/core/wallet_manager_impl.hpp @@ -28,6 +28,7 @@ class wallet_manager_impl { string import_key( const std::string& name, const std::string& wif_key ); void remove_key( const std::string& name, const std::string& password, const std::string& public_key ); signature_type sign_digest( const digest_type& sig_digest, const public_key_type& public_key, const std::optional& wallet_name ); + bool has_matching_private_key( const std::string& name, const public_key_type& public_key ); private: diff --git a/programs/beekeeper/core/utilities.cpp b/programs/beekeeper/core/utilities.cpp index f916ab166b..086cb29eff 100644 --- a/programs/beekeeper/core/utilities.cpp +++ b/programs/beekeeper/core/utilities.cpp @@ -98,4 +98,9 @@ namespace fc vo = v; } + void to_variant( const beekeeper::has_matching_private_key_return& var, fc::variant& vo ) + { + variant v = mutable_variant_object( "exists", var.exists ); + vo = v; + } } // fc diff --git a/programs/beekeeper/core/wallet_manager_impl.cpp b/programs/beekeeper/core/wallet_manager_impl.cpp index 7d99f13b6b..22c564f2f9 100644 --- a/programs/beekeeper/core/wallet_manager_impl.cpp +++ b/programs/beekeeper/core/wallet_manager_impl.cpp @@ -264,4 +264,14 @@ signature_type wallet_manager_impl::sign_digest( const digest_type& sig_digest, return sign( [&]( const std::unique_ptr& wallet ){ return wallet->try_sign_digest( sig_digest, public_key ); }, public_key, wallet_name ); } +bool wallet_manager_impl::has_matching_private_key( const std::string& name, const public_key_type& public_key ) +{ + FC_ASSERT( wallets.count(name), "Wallet not found: ${w}", ("w", name)); + + auto& w = wallets.at(name); + FC_ASSERT( !w->is_locked(), "Wallet is locked: ${w}", ("w", name)); + + return w->has_matching_private_key( public_key ); +} + } //beekeeper diff --git a/tests/unit/tests/beekeeper_tests.cpp b/tests/unit/tests/beekeeper_tests.cpp index a56a3acbaf..f7c1d9beb2 100644 --- a/tests/unit/tests/beekeeper_tests.cpp +++ b/tests/unit/tests/beekeeper_tests.cpp @@ -1231,6 +1231,53 @@ BOOST_AUTO_TEST_CASE(beekeeper_refresh_timeout) } FC_LOG_AND_RETHROW() } +BOOST_AUTO_TEST_CASE(has_matching_private_key_endpoint_test) +{ + try { + beekeeper_mgr b_mgr; + b_mgr.remove_wallets(); + + hive::protocol::serialization_mode_controller::pack_guard guard( hive::protocol::pack_type::hf26 ); + + auto _private_key_str = "5JNHfZYKGaomSFvd4NUdQ9qMcEAC43kujbfjueTHpVapX1Kzq2n"; + auto _public_key_str = "6LLegbAgLAy28EHrffBVuANFWcFgmqRMW13wBmTExqFE9SCkg4"; + + auto _private_key_str_2 = "5J8C7BMfvMFXFkvPhHNk2NHGk4zy3jF4Mrpf5k5EzAecuuzqDnn"; + auto _public_key_str_2 = "6Pg5jd1w8rXgGoqvpZXy1tHPdz43itPW6L2AGJuw8kgSAbtsxm"; + + const std::string _host = "127.0.0.1:666"; + const uint64_t _timeout = 90; + const uint32_t _session_limit = 64; + + appbase::application app; + + beekeeper_wallet_manager wm = b_mgr.create_wallet( app, _timeout, _session_limit, [](){} ); + BOOST_REQUIRE( wm.start() ); + + auto _token = wm.create_session( "salt", _host ); + auto _password = wm.create( _token, "0", std::optional() ); + + wm.import_key( _token, "0", _private_key_str ); + + BOOST_REQUIRE_THROW( wm.has_matching_private_key( _token, "pear", _public_key_str ), fc::exception ); + BOOST_REQUIRE_THROW( wm.has_matching_private_key( "_token", "0", _public_key_str ), fc::exception ); + + BOOST_REQUIRE_EQUAL( wm.has_matching_private_key( _token, "0", _public_key_str ), true ); + BOOST_REQUIRE_EQUAL( wm.has_matching_private_key( _token, "0", _public_key_str_2 ), false ); + + wm.import_key( _token, "0", _private_key_str_2 ); + + BOOST_REQUIRE_EQUAL( wm.has_matching_private_key( _token, "0", _public_key_str ), true ); + BOOST_REQUIRE_EQUAL( wm.has_matching_private_key( _token, "0", _public_key_str_2 ), true ); + + wm.close( _token, "0" ); + + BOOST_REQUIRE_THROW( wm.has_matching_private_key( _token, "0", _public_key_str ), fc::exception ); + BOOST_REQUIRE_THROW( wm.has_matching_private_key( _token, "0", _public_key_str_2 ), fc::exception ); + + } FC_LOG_AND_RETHROW() +} + BOOST_AUTO_TEST_SUITE_END() #endif