Merge pull request #235 from TheTaconator/issue196
Proposed fix for Issue #196: create_account_with_brain_key doesn't save owner key
This commit is contained in:
commit
a6a730bfa7
7 changed files with 270 additions and 1 deletions
|
|
@ -75,6 +75,7 @@ class database_api_impl : public std::enable_shared_from_this<database_api_impl>
|
||||||
|
|
||||||
// Keys
|
// Keys
|
||||||
vector<vector<account_id_type>> get_key_references( vector<public_key_type> key )const;
|
vector<vector<account_id_type>> get_key_references( vector<public_key_type> key )const;
|
||||||
|
bool is_public_key_registered(string public_key) const;
|
||||||
|
|
||||||
// Accounts
|
// Accounts
|
||||||
vector<optional<account_object>> get_accounts(const vector<account_id_type>& account_ids)const;
|
vector<optional<account_object>> get_accounts(const vector<account_id_type>& account_ids)const;
|
||||||
|
|
@ -485,6 +486,35 @@ vector<vector<account_id_type>> database_api_impl::get_key_references( vector<pu
|
||||||
return final_result;
|
return final_result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool database_api::is_public_key_registered(string public_key) const
|
||||||
|
{
|
||||||
|
return my->is_public_key_registered(public_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool database_api_impl::is_public_key_registered(string public_key) const
|
||||||
|
{
|
||||||
|
// Short-circuit
|
||||||
|
if (public_key.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search among all keys using an existing map of *current* account keys
|
||||||
|
public_key_type key;
|
||||||
|
try {
|
||||||
|
key = public_key_type(public_key);
|
||||||
|
} catch ( ... ) {
|
||||||
|
// An invalid public key was detected
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto& idx = _db.get_index_type<account_index>();
|
||||||
|
const auto& aidx = dynamic_cast<const primary_index<account_index>&>(idx);
|
||||||
|
const auto& refs = aidx.get_secondary_index<graphene::chain::account_member_index>();
|
||||||
|
auto itr = refs.account_to_key_memberships.find(key);
|
||||||
|
bool is_known = itr != refs.account_to_key_memberships.end();
|
||||||
|
|
||||||
|
return is_known;
|
||||||
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// //
|
// //
|
||||||
// Accounts //
|
// Accounts //
|
||||||
|
|
|
||||||
|
|
@ -212,6 +212,15 @@ class database_api
|
||||||
|
|
||||||
vector<vector<account_id_type>> get_key_references( vector<public_key_type> key )const;
|
vector<vector<account_id_type>> get_key_references( vector<public_key_type> key )const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether a textual representation of a public key
|
||||||
|
* (in Base-58 format) is *currently* linked
|
||||||
|
* to any *registered* (i.e. non-stealth) account on the blockchain
|
||||||
|
* @param public_key Public key
|
||||||
|
* @return Whether a public key is known
|
||||||
|
*/
|
||||||
|
bool is_public_key_registered(string public_key) const;
|
||||||
|
|
||||||
//////////////
|
//////////////
|
||||||
// Accounts //
|
// Accounts //
|
||||||
//////////////
|
//////////////
|
||||||
|
|
@ -592,6 +601,7 @@ FC_API(graphene::app::database_api,
|
||||||
|
|
||||||
// Keys
|
// Keys
|
||||||
(get_key_references)
|
(get_key_references)
|
||||||
|
(is_public_key_registered)
|
||||||
|
|
||||||
// Accounts
|
// Accounts
|
||||||
(get_accounts)
|
(get_accounts)
|
||||||
|
|
|
||||||
|
|
@ -256,6 +256,26 @@ namespace detail {
|
||||||
class wallet_api_impl;
|
class wallet_api_impl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* A utility class for performing various state-less actions that are related to wallets
|
||||||
|
*/
|
||||||
|
class utility {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Derive any number of *possible* owner keys from a given brain key.
|
||||||
|
*
|
||||||
|
* NOTE: These keys may or may not match with the owner keys of any account.
|
||||||
|
* This function is merely intended to assist with account or key recovery.
|
||||||
|
*
|
||||||
|
* @see suggest_brain_key()
|
||||||
|
*
|
||||||
|
* @param brain_key Brain key
|
||||||
|
* @param number_of_desired_keys Number of desired keys
|
||||||
|
* @return A list of keys that are deterministically derived from the brainkey
|
||||||
|
*/
|
||||||
|
static vector<brain_key_info> derive_owner_keys_from_brain_key(string brain_key, int number_of_desired_keys = 1);
|
||||||
|
};
|
||||||
|
|
||||||
struct operation_detail {
|
struct operation_detail {
|
||||||
string memo;
|
string memo;
|
||||||
string description;
|
string description;
|
||||||
|
|
@ -576,6 +596,29 @@ class wallet_api
|
||||||
*/
|
*/
|
||||||
brain_key_info suggest_brain_key()const;
|
brain_key_info suggest_brain_key()const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Derive any number of *possible* owner keys from a given brain key.
|
||||||
|
*
|
||||||
|
* NOTE: These keys may or may not match with the owner keys of any account.
|
||||||
|
* This function is merely intended to assist with account or key recovery.
|
||||||
|
*
|
||||||
|
* @see suggest_brain_key()
|
||||||
|
*
|
||||||
|
* @param brain_key Brain key
|
||||||
|
* @param numberOfDesiredKeys Number of desired keys
|
||||||
|
* @return A list of keys that are deterministically derived from the brainkey
|
||||||
|
*/
|
||||||
|
vector<brain_key_info> derive_owner_keys_from_brain_key(string brain_key, int number_of_desired_keys = 1) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether a textual representation of a public key
|
||||||
|
* (in Base-58 format) is *currently* linked
|
||||||
|
* to any *registered* (i.e. non-stealth) account on the blockchain
|
||||||
|
* @param public_key Public key
|
||||||
|
* @return Whether a public key is known
|
||||||
|
*/
|
||||||
|
bool is_public_key_registered(string public_key) const;
|
||||||
|
|
||||||
/** Converts a signed_transaction in JSON form to its binary representation.
|
/** Converts a signed_transaction in JSON form to its binary representation.
|
||||||
*
|
*
|
||||||
* TODO: I don't see a broadcast_transaction() function, do we need one?
|
* TODO: I don't see a broadcast_transaction() function, do we need one?
|
||||||
|
|
@ -1565,6 +1608,7 @@ FC_API( graphene::wallet::wallet_api,
|
||||||
(import_account_keys)
|
(import_account_keys)
|
||||||
(import_balance)
|
(import_balance)
|
||||||
(suggest_brain_key)
|
(suggest_brain_key)
|
||||||
|
(derive_owner_keys_from_brain_key)
|
||||||
(register_account)
|
(register_account)
|
||||||
(upgrade_account)
|
(upgrade_account)
|
||||||
(create_account_with_brain_key)
|
(create_account_with_brain_key)
|
||||||
|
|
@ -1609,6 +1653,7 @@ FC_API( graphene::wallet::wallet_api,
|
||||||
(get_block)
|
(get_block)
|
||||||
(get_account_count)
|
(get_account_count)
|
||||||
(get_account_history)
|
(get_account_history)
|
||||||
|
(is_public_key_registered)
|
||||||
(get_market_history)
|
(get_market_history)
|
||||||
(get_global_properties)
|
(get_global_properties)
|
||||||
(get_dynamic_global_properties)
|
(get_dynamic_global_properties)
|
||||||
|
|
|
||||||
|
|
@ -2717,7 +2717,29 @@ std::string operation_result_printer::operator()(const asset& a)
|
||||||
|
|
||||||
}}}
|
}}}
|
||||||
|
|
||||||
|
namespace graphene { namespace wallet {
|
||||||
|
vector<brain_key_info> utility::derive_owner_keys_from_brain_key(string brain_key, int number_of_desired_keys)
|
||||||
|
{
|
||||||
|
// Safety-check
|
||||||
|
FC_ASSERT( number_of_desired_keys >= 1 );
|
||||||
|
|
||||||
|
// Create as many derived owner keys as requested
|
||||||
|
vector<brain_key_info> results;
|
||||||
|
brain_key = graphene::wallet::detail::normalize_brain_key(brain_key);
|
||||||
|
for (int i = 0; i < number_of_desired_keys; ++i) {
|
||||||
|
fc::ecc::private_key priv_key = graphene::wallet::detail::derive_private_key( brain_key, i );
|
||||||
|
|
||||||
|
brain_key_info result;
|
||||||
|
result.brain_priv_key = brain_key;
|
||||||
|
result.wif_priv_key = key_to_wif( priv_key );
|
||||||
|
result.pub_key = priv_key.get_public_key();
|
||||||
|
|
||||||
|
results.push_back(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
|
||||||
namespace graphene { namespace wallet {
|
namespace graphene { namespace wallet {
|
||||||
|
|
||||||
|
|
@ -2847,6 +2869,18 @@ brain_key_info wallet_api::suggest_brain_key()const
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vector<brain_key_info> wallet_api::derive_owner_keys_from_brain_key(string brain_key, int number_of_desired_keys) const
|
||||||
|
{
|
||||||
|
return graphene::wallet::utility::derive_owner_keys_from_brain_key(brain_key, number_of_desired_keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wallet_api::is_public_key_registered(string public_key) const
|
||||||
|
{
|
||||||
|
bool is_known = my->_remote_db->is_public_key_registered(public_key);
|
||||||
|
return is_known;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
string wallet_api::serialize_transaction( signed_transaction tx )const
|
string wallet_api::serialize_transaction( signed_transaction tx )const
|
||||||
{
|
{
|
||||||
return fc::to_hex(fc::raw::pack(tx));
|
return fc::to_hex(fc::raw::pack(tx));
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ endif()
|
||||||
|
|
||||||
file(GLOB UNIT_TESTS "tests/*.cpp")
|
file(GLOB UNIT_TESTS "tests/*.cpp")
|
||||||
add_executable( chain_test ${UNIT_TESTS} ${COMMON_SOURCES} )
|
add_executable( chain_test ${UNIT_TESTS} ${COMMON_SOURCES} )
|
||||||
target_link_libraries( chain_test graphene_chain graphene_app graphene_account_history graphene_egenesis_none fc ${PLATFORM_SPECIFIC_LIBS} )
|
target_link_libraries( chain_test graphene_chain graphene_app graphene_account_history graphene_egenesis_none fc graphene_wallet ${PLATFORM_SPECIFIC_LIBS} )
|
||||||
if(MSVC)
|
if(MSVC)
|
||||||
set_source_files_properties( tests/serialization_tests.cpp PROPERTIES COMPILE_FLAGS "/bigobj" )
|
set_source_files_properties( tests/serialization_tests.cpp PROPERTIES COMPILE_FLAGS "/bigobj" )
|
||||||
endif(MSVC)
|
endif(MSVC)
|
||||||
|
|
|
||||||
71
tests/tests/database_api_tests.cpp
Normal file
71
tests/tests/database_api_tests.cpp
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2017 Cryptonomex, Inc., and contributors.
|
||||||
|
*
|
||||||
|
* The MIT License
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <boost/test/unit_test.hpp>
|
||||||
|
|
||||||
|
#include <graphene/app/database_api.hpp>
|
||||||
|
|
||||||
|
#include "../common/database_fixture.hpp"
|
||||||
|
|
||||||
|
using namespace graphene::chain;
|
||||||
|
using namespace graphene::chain::test;
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_SUITE(database_api_tests, database_fixture)
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(is_registered) {
|
||||||
|
try {
|
||||||
|
/***
|
||||||
|
* Arrange
|
||||||
|
*/
|
||||||
|
auto nathan_private_key = generate_private_key("nathan");
|
||||||
|
public_key_type nathan_public = nathan_private_key.get_public_key();
|
||||||
|
|
||||||
|
auto dan_private_key = generate_private_key("dan");
|
||||||
|
public_key_type dan_public = dan_private_key.get_public_key();
|
||||||
|
|
||||||
|
auto unregistered_private_key = generate_private_key("unregistered");
|
||||||
|
public_key_type unregistered_public = unregistered_private_key.get_public_key();
|
||||||
|
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Act
|
||||||
|
*/
|
||||||
|
create_account("dan", dan_private_key.get_public_key()).id;
|
||||||
|
create_account("nathan", nathan_private_key.get_public_key()).id;
|
||||||
|
// Unregistered key will not be registered with any account
|
||||||
|
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Assert
|
||||||
|
*/
|
||||||
|
graphene::app::database_api db_api(db);
|
||||||
|
|
||||||
|
BOOST_CHECK(db_api.is_public_key_registered((string) nathan_public));
|
||||||
|
BOOST_CHECK(db_api.is_public_key_registered((string) dan_public));
|
||||||
|
BOOST_CHECK(!db_api.is_public_key_registered((string) unregistered_public));
|
||||||
|
|
||||||
|
} FC_LOG_AND_RETHROW()
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
79
tests/tests/wallet_tests.cpp
Normal file
79
tests/tests/wallet_tests.cpp
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2017 Cryptonomex, Inc., and contributors.
|
||||||
|
*
|
||||||
|
* The MIT License
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <boost/test/unit_test.hpp>
|
||||||
|
|
||||||
|
#include <graphene/app/database_api.hpp>
|
||||||
|
#include <graphene/wallet/wallet.hpp>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "../common/database_fixture.hpp"
|
||||||
|
|
||||||
|
using namespace graphene::chain;
|
||||||
|
using namespace graphene::chain::test;
|
||||||
|
using namespace graphene::wallet;
|
||||||
|
|
||||||
|
BOOST_FIXTURE_TEST_SUITE(wallet_tests, database_fixture)
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Check the basic behavior of deriving potential owner keys from a brain key
|
||||||
|
*/
|
||||||
|
BOOST_AUTO_TEST_CASE(derive_owner_keys_from_brain_key) {
|
||||||
|
try {
|
||||||
|
/***
|
||||||
|
* Act
|
||||||
|
*/
|
||||||
|
int nbr_keys_desired = 3;
|
||||||
|
vector<brain_key_info> derived_keys = graphene::wallet::utility::derive_owner_keys_from_brain_key("SOME WORDS GO HERE", nbr_keys_desired);
|
||||||
|
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Assert: Check the number of derived keys
|
||||||
|
*/
|
||||||
|
BOOST_CHECK_EQUAL(nbr_keys_desired, derived_keys.size());
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Assert: Check that each derived key is unique
|
||||||
|
*/
|
||||||
|
set<string> set_derived_public_keys;
|
||||||
|
for (auto info : derived_keys) {
|
||||||
|
string description = (string) info.pub_key;
|
||||||
|
set_derived_public_keys.emplace(description);
|
||||||
|
}
|
||||||
|
BOOST_CHECK_EQUAL(nbr_keys_desired, set_derived_public_keys.size());
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Assert: Check whether every public key begins with the expected prefix
|
||||||
|
*/
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
} FC_LOG_AND_RETHROW()
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
Loading…
Reference in a new issue