From acfbad2cd4f66942669c906c1c903051585d446c Mon Sep 17 00:00:00 2001 From: Michael Vandeberg Date: Wed, 6 Jan 2016 11:02:20 -0500 Subject: [PATCH 1/2] Adds a cryptography API. --- libraries/app/api.cpp | 74 +++++++++++++++++++++- libraries/app/include/graphene/app/api.hpp | 55 ++++++++++++++++ 2 files changed, 128 insertions(+), 1 deletion(-) diff --git a/libraries/app/api.cpp b/libraries/app/api.cpp index dfc43420..20e3a058 100644 --- a/libraries/app/api.cpp +++ b/libraries/app/api.cpp @@ -37,7 +37,7 @@ #include namespace graphene { namespace app { - + login_api::login_api(application& a) :_app(a) { @@ -87,6 +87,10 @@ namespace graphene { namespace app { { _network_node_api = std::make_shared< network_node_api >( std::ref(_app) ); } + else if( api_name == "crypto_api" ) + { + _crypto_api = std::make_shared< crypto_api >( std::ref(_app) ); + } return; } @@ -196,6 +200,12 @@ namespace graphene { namespace app { FC_ASSERT(_history_api); return *_history_api; } + + fc::api login_api::crypto() const + { + FC_ASSERT(_crypto_api); + return *_crypto_api; + } vector get_relevant_accounts( const object* obj ) { @@ -405,5 +415,67 @@ namespace graphene { namespace app { } return result; } FC_CAPTURE_AND_RETHROW( (a)(b)(bucket_seconds)(start)(end) ) } + + blind_signature crypto_api::blind_sign( extended_private_key key, const blinded_hash& hash, int i ) const + { + return key.blind_sign( hash, i ); + } + + compact_signature crypto_api::unblind_signature( extended_private_key key, + const extended_public_key& bob, + const blind_signature& sig, + const fc::sha256& hash, + int i ) const + { + return key.unblind_signature( bob, sig, hash, i ); + } + + commitment_type crypto_api::blind( const blind_factor_type& blind, uint64_t value ) + { + return fc::ecc::blind( blind, value ); + } + + blind_factor_type crypto_api::blind_sum( const std::vector& blinds_in, uint32_t non_neg ) + { + return fc::ecc::blind_sum( blinds_in, non_neg ); + } + + bool crypto_api::verify_sum( const std::vector& commits_in, const std::vector& neg_commits_in, int64_t excess ) + { + return fc::ecc::verify_sum( commits_in, neg_commits_in, excess ); + } + + bool crypto_api::verify_range( uint64_t& min_val, uint64_t& max_val, const commitment_type& commit, const std::vector& proof ) + { + return fc::ecc::verify_range( min_val, max_val, commit, proof ); + } + + std::vector crypto_api::range_proof_sign( uint64_t min_value, + const commitment_type& commit, + const blind_factor_type& commit_blind, + const blind_factor_type& nonce, + int8_t base10_exp, + uint8_t min_bits, + uint64_t actual_value ) + { + return fc::ecc::range_proof_sign( min_value, commit, commit_blind, nonce, base10_exp, min_bits, actual_value ); + } + + bool crypto_api::verify_range_proof_rewind( blind_factor_type& blind_out, + uint64_t& value_out, + string& message_out, + const blind_factor_type& nonce, + uint64_t& min_val, + uint64_t& max_val, + commitment_type commit, + const std::vector& proof ) + { + return fc::ecc::verify_range_proof_rewind( blind_out, value_out, message_out, nonce, min_val, max_val, commit, proof ); + } + + range_proof_info crypto_ap::range_get_info( const std::vector& proof ) + { + return fc::ecc::range_get_info( proof ); + } } } // graphene::app diff --git a/libraries/app/include/graphene/app/api.hpp b/libraries/app/include/graphene/app/api.hpp index 30d0d048..a897f99a 100644 --- a/libraries/app/include/graphene/app/api.hpp +++ b/libraries/app/include/graphene/app/api.hpp @@ -30,6 +30,7 @@ #include #include +#include #include #include @@ -42,6 +43,7 @@ namespace graphene { namespace app { using namespace graphene::chain; using namespace graphene::market_history; + using namespace fc::ecc; using namespace std; class application; @@ -173,6 +175,45 @@ namespace graphene { namespace app { private: application& _app; }; + + class crypto_api + { + public: + blind_signature blind_sign( extended_private_key key, const blinded_hash& hash, int i ) const; + + compact_signature unblind_signature( extended_private_key key, + const extended_public_key& bob, + const blind_signature& sig, + const fc::sha256& hash, + int i ) const; + + commitment_type blind( const blind_factor_type& blind, uint64_t value ); + + blind_factor_type blind_sum( const std::vector& blinds_in, uint32_t non_neg ); + + bool verify_sum( const std::vector& commits_in, const std::vector& neg_commits_in, int64_t excess ); + + bool verify_range( uint64_t& min_val, uint64_t& max_val, const commitment_type& commit, const std::vector& proof ); + + std::vector range_proof_sign( uint64_t min_value, + const commitment_type& commit, + const blind_factor_type& commit_blind, + const blind_factor_type& nonce, + int8_t base10_exp, + uint8_t min_bits, + uint64_t actual_value ); + + bool verify_range_proof_rewind( blind_factor_type& blind_out, + uint64_t& value_out, + string& message_out, + const blind_factor_type& nonce, + uint64_t& min_val, + uint64_t& max_val, + commitment_type commit, + const std::vector& proof ); + + range_proof_info range_get_info( const std::vector& proof ); + }; /** * @brief The login_api class implements the bottom layer of the RPC API @@ -203,6 +244,8 @@ namespace graphene { namespace app { fc::api history()const; /// @brief Retrieve the network node API fc::api network_node()const; + /// @brief Retrieve the cryptography API + fc::api crypto()const; private: /// @brief Called to enable an API, not reflected. @@ -213,6 +256,7 @@ namespace graphene { namespace app { optional< fc::api > _network_broadcast_api; optional< fc::api > _network_node_api; optional< fc::api > _history_api; + optional< fc::api _crypto_api; }; }} // graphene::app @@ -239,10 +283,21 @@ FC_API(graphene::app::network_node_api, (get_advanced_node_parameters) (set_advanced_node_parameters) ) +FC_API(graphene::app::crypto_api, + (blind_sign) + (unblind_signature) + (blind) + (blind_sum) + (verify_sum) + (verify_range) + (range_proof_sign) + (verify_range_proof_rewind) + (range_get_info)) FC_API(graphene::app::login_api, (login) (network_broadcast) (database) (history) (network_node) + (crypto) ) From 8df58439b1498071fa3b1fbfce61093295338c5a Mon Sep 17 00:00:00 2001 From: Michael Vandeberg Date: Thu, 7 Jan 2016 11:04:14 -0500 Subject: [PATCH 2/2] Opens up cryptographic methods in their own API with serialized wrappers for public and private keys. Tests for serialization of the wrappers added in serialization_tests. --- libraries/app/api.cpp | 52 ++++--- libraries/app/application.cpp | 1 + libraries/app/include/graphene/app/api.hpp | 63 ++++++--- .../include/graphene/chain/protocol/types.hpp | 51 +++++++ libraries/chain/protocol/types.cpp | 129 +++++++++++++++++- tests/tests/serialization_tests.cpp | 37 +++++ 6 files changed, 290 insertions(+), 43 deletions(-) diff --git a/libraries/app/api.cpp b/libraries/app/api.cpp index 20e3a058..f262cca7 100644 --- a/libraries/app/api.cpp +++ b/libraries/app/api.cpp @@ -89,7 +89,7 @@ namespace graphene { namespace app { } else if( api_name == "crypto_api" ) { - _crypto_api = std::make_shared< crypto_api >( std::ref(_app) ); + _crypto_api = std::make_shared< crypto_api >(); } return; } @@ -416,18 +416,20 @@ namespace graphene { namespace app { return result; } FC_CAPTURE_AND_RETHROW( (a)(b)(bucket_seconds)(start)(end) ) } - blind_signature crypto_api::blind_sign( extended_private_key key, const blinded_hash& hash, int i ) const + crypto_api::crypto_api(){}; + + blind_signature crypto_api::blind_sign( const extended_private_key_type& key, const blinded_hash& hash, int i ) { - return key.blind_sign( hash, i ); + return fc::ecc::extended_private_key( key ).blind_sign( hash, i ); } - compact_signature crypto_api::unblind_signature( extended_private_key key, - const extended_public_key& bob, + signature_type crypto_api::unblind_signature( const extended_private_key_type& key, + const extended_public_key_type& bob, const blind_signature& sig, const fc::sha256& hash, - int i ) const + int i ) { - return key.unblind_signature( bob, sig, hash, i ); + return fc::ecc::extended_private_key( key ).unblind_signature( extended_public_key( bob ), sig, hash, i ); } commitment_type crypto_api::blind( const blind_factor_type& blind, uint64_t value ) @@ -444,12 +446,14 @@ namespace graphene { namespace app { { return fc::ecc::verify_sum( commits_in, neg_commits_in, excess ); } - - bool crypto_api::verify_range( uint64_t& min_val, uint64_t& max_val, const commitment_type& commit, const std::vector& proof ) + + verify_range_result crypto_api::verify_range( const commitment_type& commit, const std::vector& proof ) { - return fc::ecc::verify_range( min_val, max_val, commit, proof ); + verify_range_result result; + result.success = fc::ecc::verify_range( result.min_val, result.max_val, commit, proof ); + return result; } - + std::vector crypto_api::range_proof_sign( uint64_t min_value, const commitment_type& commit, const blind_factor_type& commit_blind, @@ -460,20 +464,24 @@ namespace graphene { namespace app { { return fc::ecc::range_proof_sign( min_value, commit, commit_blind, nonce, base10_exp, min_bits, actual_value ); } - - bool crypto_api::verify_range_proof_rewind( blind_factor_type& blind_out, - uint64_t& value_out, - string& message_out, - const blind_factor_type& nonce, - uint64_t& min_val, - uint64_t& max_val, - commitment_type commit, - const std::vector& proof ) + + verify_range_proof_rewind_result crypto_api::verify_range_proof_rewind( const blind_factor_type& nonce, + const commitment_type& commit, + const std::vector& proof ) { - return fc::ecc::verify_range_proof_rewind( blind_out, value_out, message_out, nonce, min_val, max_val, commit, proof ); + verify_range_proof_rewind_result result; + result.success = fc::ecc::verify_range_proof_rewind( result.blind_out, + result.value_out, + result.message_out, + nonce, + result.min_val, + result.max_val, + const_cast< commitment_type& >( commit ), + proof ); + return result; } - range_proof_info crypto_ap::range_get_info( const std::vector& proof ) + range_proof_info crypto_api::range_get_info( const std::vector& proof ) { return fc::ecc::range_get_info( proof ); } diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index 0245ebcf..8e13d8fa 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -386,6 +386,7 @@ namespace detail { wild_access.allowed_apis.push_back( "database_api" ); wild_access.allowed_apis.push_back( "network_broadcast_api" ); wild_access.allowed_apis.push_back( "history_api" ); + wild_access.allowed_apis.push_back( "crypto_api" ); _apiaccess.permission_map["*"] = wild_access; } diff --git a/libraries/app/include/graphene/app/api.hpp b/libraries/app/include/graphene/app/api.hpp index a897f99a..a6ffe0e9 100644 --- a/libraries/app/include/graphene/app/api.hpp +++ b/libraries/app/include/graphene/app/api.hpp @@ -23,6 +23,7 @@ #include #include +#include #include @@ -48,6 +49,22 @@ namespace graphene { namespace app { class application; + struct verify_range_result + { + bool success; + uint64_t min_val; + uint64_t max_val; + }; + + struct verify_range_proof_rewind_result + { + bool success; + uint64_t min_val; + uint64_t max_val; + uint64_t value_out; + fc::ecc::blind_factor_type blind_out; + string message_out; + }; /** * @brief The history_api class implements the RPC API for account history @@ -179,21 +196,23 @@ namespace graphene { namespace app { class crypto_api { public: - blind_signature blind_sign( extended_private_key key, const blinded_hash& hash, int i ) const; + crypto_api(); - compact_signature unblind_signature( extended_private_key key, - const extended_public_key& bob, - const blind_signature& sig, + fc::ecc::blind_signature blind_sign( const extended_private_key_type& key, const fc::ecc::blinded_hash& hash, int i ); + + signature_type unblind_signature( const extended_private_key_type& key, + const extended_public_key_type& bob, + const fc::ecc::blind_signature& sig, const fc::sha256& hash, - int i ) const; - - commitment_type blind( const blind_factor_type& blind, uint64_t value ); + int i ); + + fc::ecc::commitment_type blind( const fc::ecc::blind_factor_type& blind, uint64_t value ); - blind_factor_type blind_sum( const std::vector& blinds_in, uint32_t non_neg ); + fc::ecc::blind_factor_type blind_sum( const std::vector& blinds_in, uint32_t non_neg ); bool verify_sum( const std::vector& commits_in, const std::vector& neg_commits_in, int64_t excess ); - bool verify_range( uint64_t& min_val, uint64_t& max_val, const commitment_type& commit, const std::vector& proof ); + verify_range_result verify_range( const fc::ecc::commitment_type& commit, const std::vector& proof ); std::vector range_proof_sign( uint64_t min_value, const commitment_type& commit, @@ -203,14 +222,11 @@ namespace graphene { namespace app { uint8_t min_bits, uint64_t actual_value ); - bool verify_range_proof_rewind( blind_factor_type& blind_out, - uint64_t& value_out, - string& message_out, - const blind_factor_type& nonce, - uint64_t& min_val, - uint64_t& max_val, - commitment_type commit, - const std::vector& proof ); + + verify_range_proof_rewind_result verify_range_proof_rewind( const blind_factor_type& nonce, + const fc::ecc::commitment_type& commit, + const std::vector& proof ); + range_proof_info range_get_info( const std::vector& proof ); }; @@ -245,7 +261,7 @@ namespace graphene { namespace app { /// @brief Retrieve the network node API fc::api network_node()const; /// @brief Retrieve the cryptography API - fc::api crypto()const; + fc::api crypto()const; private: /// @brief Called to enable an API, not reflected. @@ -256,13 +272,19 @@ namespace graphene { namespace app { optional< fc::api > _network_broadcast_api; optional< fc::api > _network_node_api; optional< fc::api > _history_api; - optional< fc::api _crypto_api; + optional< fc::api > _crypto_api; }; }} // graphene::app FC_REFLECT( graphene::app::network_broadcast_api::transaction_confirmation, (id)(block_num)(trx_num)(trx) ) +FC_REFLECT( graphene::app::verify_range_result, + (success)(min_val)(max_val) ) +FC_REFLECT( graphene::app::verify_range_proof_rewind_result, + (success)(min_val)(max_val)(value_out)(blind_out)(message_out) ) +//FC_REFLECT_TYPENAME( fc::ecc::compact_signature ); +//FC_REFLECT_TYPENAME( fc::ecc::commitment_type ); FC_API(graphene::app::history_api, (get_account_history) @@ -292,7 +314,8 @@ FC_API(graphene::app::crypto_api, (verify_range) (range_proof_sign) (verify_range_proof_rewind) - (range_get_info)) + (range_get_info) + ) FC_API(graphene::app::login_api, (login) (network_broadcast) diff --git a/libraries/chain/include/graphene/chain/protocol/types.hpp b/libraries/chain/include/graphene/chain/protocol/types.hpp index ec0006b1..f77e183d 100644 --- a/libraries/chain/include/graphene/chain/protocol/types.hpp +++ b/libraries/chain/include/graphene/chain/protocol/types.hpp @@ -248,16 +248,67 @@ namespace graphene { namespace chain { bool is_valid_v1( const std::string& base58str ); }; + struct extended_public_key_type + { + struct binary_key + { + binary_key() {} + uint32_t check = 0; + fc::ecc::extended_key_data data; + }; + + fc::ecc::extended_key_data key_data; + + extended_public_key_type(); + extended_public_key_type( const fc::ecc::extended_key_data& data ); + extended_public_key_type( const fc::ecc::extended_public_key& extpubkey ); + explicit extended_public_key_type( const std::string& base58str ); + operator fc::ecc::extended_public_key() const; + explicit operator std::string() const; + friend bool operator == ( const extended_public_key_type& p1, const fc::ecc::extended_public_key& p2); + friend bool operator == ( const extended_public_key_type& p1, const extended_public_key_type& p2); + friend bool operator != ( const extended_public_key_type& p1, const extended_public_key_type& p2); + }; + + struct extended_private_key_type + { + struct binary_key + { + binary_key() {} + uint32_t check = 0; + fc::ecc::extended_key_data data; + }; + + fc::ecc::extended_key_data key_data; + + extended_private_key_type(); + extended_private_key_type( const fc::ecc::extended_key_data& data ); + extended_private_key_type( const fc::ecc::extended_private_key& extprivkey ); + explicit extended_private_key_type( const std::string& base58str ); + operator fc::ecc::extended_private_key() const; + explicit operator std::string() const; + friend bool operator == ( const extended_private_key_type& p1, const fc::ecc::extended_private_key& p2); + friend bool operator == ( const extended_private_key_type& p1, const extended_private_key_type& p2); + friend bool operator != ( const extended_private_key_type& p1, const extended_private_key_type& p2); + }; } } // graphene::chain namespace fc { void to_variant( const graphene::chain::public_key_type& var, fc::variant& vo ); void from_variant( const fc::variant& var, graphene::chain::public_key_type& vo ); + void to_variant( const graphene::chain::extended_public_key_type& var, fc::variant& vo ); + void from_variant( const fc::variant& var, graphene::chain::extended_public_key_type& vo ); + void to_variant( const graphene::chain::extended_private_key_type& var, fc::variant& vo ); + void from_variant( const fc::variant& var, graphene::chain::extended_private_key_type& vo ); } FC_REFLECT( graphene::chain::public_key_type, (key_data) ) FC_REFLECT( graphene::chain::public_key_type::binary_key, (data)(check) ) +FC_REFLECT( graphene::chain::extended_public_key_type, (key_data) ) +FC_REFLECT( graphene::chain::extended_public_key_type::binary_key, (check)(data) ) +FC_REFLECT( graphene::chain::extended_private_key_type, (key_data) ) +FC_REFLECT( graphene::chain::extended_private_key_type::binary_key, (check)(data) ) FC_REFLECT_ENUM( graphene::chain::object_type, (null_object_type) diff --git a/libraries/chain/protocol/types.cpp b/libraries/chain/protocol/types.cpp index b9d99792..7df2ccf4 100644 --- a/libraries/chain/protocol/types.cpp +++ b/libraries/chain/protocol/types.cpp @@ -108,6 +108,114 @@ namespace graphene { namespace chain { { return p1.key_data != p2.key_data; } + + // extended_public_key_type + + extended_public_key_type::extended_public_key_type():key_data(){}; + + extended_public_key_type::extended_public_key_type( const fc::ecc::extended_key_data& data ) + :key_data( data ){}; + + extended_public_key_type::extended_public_key_type( const fc::ecc::extended_public_key& extpubkey ) + { + key_data = extpubkey.serialize_extended(); + }; + + extended_public_key_type::extended_public_key_type( const std::string& base58str ) + { + std::string prefix( GRAPHENE_ADDRESS_PREFIX ); + + const size_t prefix_len = prefix.size(); + FC_ASSERT( base58str.size() > prefix_len ); + FC_ASSERT( base58str.substr( 0, prefix_len ) == prefix , "", ("base58str", base58str) ); + auto bin = fc::from_base58( base58str.substr( prefix_len ) ); + auto bin_key = fc::raw::unpack(bin); + FC_ASSERT( fc::ripemd160::hash( bin_key.data.data, bin_key.data.size() )._hash[0] == bin_key.check ); + key_data = bin_key.data; + } + + extended_public_key_type::operator fc::ecc::extended_public_key() const + { + return fc::ecc::extended_public_key::deserialize( key_data ); + } + + extended_public_key_type::operator std::string() const + { + binary_key k; + k.data = key_data; + k.check = fc::ripemd160::hash( k.data.data, k.data.size() )._hash[0]; + auto data = fc::raw::pack( k ); + return GRAPHENE_ADDRESS_PREFIX + fc::to_base58( data.data(), data.size() ); + } + + bool operator == ( const extended_public_key_type& p1, const fc::ecc::extended_public_key& p2) + { + return p1.key_data == p2.serialize_extended(); + } + + bool operator == ( const extended_public_key_type& p1, const extended_public_key_type& p2) + { + return p1.key_data == p2.key_data; + } + + bool operator != ( const extended_public_key_type& p1, const extended_public_key_type& p2) + { + return p1.key_data != p2.key_data; + } + + // extended_private_key_type + + extended_private_key_type::extended_private_key_type():key_data(){}; + + extended_private_key_type::extended_private_key_type( const fc::ecc::extended_key_data& data ) + :key_data( data ){}; + + extended_private_key_type::extended_private_key_type( const fc::ecc::extended_private_key& extprivkey ) + { + key_data = extprivkey.serialize_extended(); + }; + + extended_private_key_type::extended_private_key_type( const std::string& base58str ) + { + std::string prefix( GRAPHENE_ADDRESS_PREFIX ); + + const size_t prefix_len = prefix.size(); + FC_ASSERT( base58str.size() > prefix_len ); + FC_ASSERT( base58str.substr( 0, prefix_len ) == prefix , "", ("base58str", base58str) ); + auto bin = fc::from_base58( base58str.substr( prefix_len ) ); + auto bin_key = fc::raw::unpack(bin); + FC_ASSERT( fc::ripemd160::hash( bin_key.data.data, bin_key.data.size() )._hash[0] == bin_key.check ); + key_data = bin_key.data; + } + + extended_private_key_type::operator fc::ecc::extended_private_key() const + { + return fc::ecc::extended_private_key::deserialize( key_data ); + } + + extended_private_key_type::operator std::string() const + { + binary_key k; + k.data = key_data; + k.check = fc::ripemd160::hash( k.data.data, k.data.size() )._hash[0]; + auto data = fc::raw::pack( k ); + return GRAPHENE_ADDRESS_PREFIX + fc::to_base58( data.data(), data.size() ); + } + + bool operator == ( const extended_private_key_type& p1, const fc::ecc::extended_public_key& p2) + { + return p1.key_data == p2.serialize_extended(); + } + + bool operator == ( const extended_private_key_type& p1, const extended_private_key_type& p2) + { + return p1.key_data == p2.key_data; + } + + bool operator != ( const extended_private_key_type& p1, const extended_private_key_type& p2) + { + return p1.key_data != p2.key_data; + } } } // graphene::chain @@ -116,12 +224,31 @@ namespace fc using namespace std; void to_variant( const graphene::chain::public_key_type& var, fc::variant& vo ) { - vo = std::string(var); + vo = std::string( var ); } void from_variant( const fc::variant& var, graphene::chain::public_key_type& vo ) { vo = graphene::chain::public_key_type( var.as_string() ); } + + void to_variant( const graphene::chain::extended_public_key_type& var, fc::variant& vo ) + { + vo = std::string( var ); + } + + void from_variant( const fc::variant& var, graphene::chain::extended_public_key_type& vo ) + { + vo = graphene::chain::extended_public_key_type( var.as_string() ); + } + void to_variant( const graphene::chain::extended_private_key_type& var, fc::variant& vo ) + { + vo = std::string( var ); + } + + void from_variant( const fc::variant& var, graphene::chain::extended_private_key_type& vo ) + { + vo = graphene::chain::extended_private_key_type( var.as_string() ); + } } // fc diff --git a/tests/tests/serialization_tests.cpp b/tests/tests/serialization_tests.cpp index b485cec7..f16c807f 100644 --- a/tests/tests/serialization_tests.cpp +++ b/tests/tests/serialization_tests.cpp @@ -25,6 +25,7 @@ #include +#include #include #include "../common/database_fixture.hpp" @@ -82,4 +83,40 @@ BOOST_AUTO_TEST_CASE( json_tests ) } } +BOOST_AUTO_TEST_CASE( extended_private_key_type_test ) +{ + try + { + fc::ecc::extended_private_key key = fc::ecc::extended_private_key( fc::ecc::private_key::generate(), + fc::sha256(), + 0, 0, 0 ); + extended_private_key_type type = extended_private_key_type( key ); + std::string packed = std::string( type ); + extended_private_key_type unpacked = extended_private_key_type( packed ); + BOOST_CHECK( type == unpacked ); + } catch ( const fc::exception& e ) + { + edump((e.to_detail_string())); + throw; + } +} + +BOOST_AUTO_TEST_CASE( extended_public_key_type_test ) +{ + try + { + fc::ecc::extended_public_key key = fc::ecc::extended_public_key( fc::ecc::private_key::generate().get_public_key(), + fc::sha256(), + 0, 0, 0 ); + extended_public_key_type type = extended_public_key_type( key ); + std::string packed = std::string( type ); + extended_public_key_type unpacked = extended_public_key_type( packed ); + BOOST_CHECK( type == unpacked ); + } catch ( const fc::exception& e ) + { + edump((e.to_detail_string())); + throw; + } +} + BOOST_AUTO_TEST_SUITE_END()