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:
Vikram Rajkumar 2017-02-20 13:51:15 -06:00 committed by GitHub
commit a6a730bfa7
7 changed files with 270 additions and 1 deletions

View file

@ -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 //

View file

@ -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)

View file

@ -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)

View file

@ -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));

View file

@ -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)

View 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()

View 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()