Added bitcoin address (btc_multisig_address and btc_multisig_segwit_address).
This commit is contained in:
parent
24c6963ef3
commit
5ebb987b8e
7 changed files with 279 additions and 2 deletions
|
|
@ -117,7 +117,7 @@ add_library( graphene_chain
|
||||||
)
|
)
|
||||||
|
|
||||||
add_dependencies( graphene_chain build_hardfork_hpp )
|
add_dependencies( graphene_chain build_hardfork_hpp )
|
||||||
#target_link_libraries( graphene_chain fc graphene_db )
|
|
||||||
target_link_libraries( graphene_chain fc graphene_db sidechain )
|
target_link_libraries( graphene_chain fc graphene_db sidechain )
|
||||||
target_include_directories( graphene_chain
|
target_include_directories( graphene_chain
|
||||||
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_BINARY_DIR}/include" )
|
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_BINARY_DIR}/include" )
|
||||||
|
|
|
||||||
|
|
@ -5,5 +5,5 @@ add_subdirectory( network )
|
||||||
|
|
||||||
add_library( sidechain STATIC ${SOURCES} ${HEADERS} )
|
add_library( sidechain STATIC ${SOURCES} ${HEADERS} )
|
||||||
|
|
||||||
target_link_libraries( sidechain ${Boost_LIBRARIES} fc graphene_chain )
|
target_link_libraries( sidechain fc graphene_chain )
|
||||||
target_include_directories( sidechain PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" )
|
target_include_directories( sidechain PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" )
|
||||||
|
|
|
||||||
105
libraries/sidechain/btc_multisig_address.cpp
Normal file
105
libraries/sidechain/btc_multisig_address.cpp
Normal file
|
|
@ -0,0 +1,105 @@
|
||||||
|
#include <sidechain/btc_multisig_address.hpp>
|
||||||
|
#include <sstream>
|
||||||
|
#include <fc/crypto/base58.hpp>
|
||||||
|
|
||||||
|
namespace sidechain {
|
||||||
|
|
||||||
|
btc_multisig_address::btc_multisig_address( const size_t n_required, const std::map< account_id_type, public_key_type >& keys ) :
|
||||||
|
keys_required ( n_required ), witnesses_keys( keys )
|
||||||
|
{
|
||||||
|
create_redeem_script();
|
||||||
|
create_address();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t btc_multisig_address::count_intersection( const std::map< account_id_type, public_key_type >& keys ) const
|
||||||
|
{
|
||||||
|
FC_ASSERT( keys.size() > 0 );
|
||||||
|
|
||||||
|
int intersections_count = 0;
|
||||||
|
for( auto& key : keys ) {
|
||||||
|
auto witness_key = witnesses_keys.find( key.first );
|
||||||
|
if( witness_key == witnesses_keys.end() ) continue;
|
||||||
|
if( key.second == witness_key->second )
|
||||||
|
intersections_count++;
|
||||||
|
}
|
||||||
|
return intersections_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void btc_multisig_address::create_redeem_script()
|
||||||
|
{
|
||||||
|
FC_ASSERT( keys_required > 0 );
|
||||||
|
FC_ASSERT( keys_required < witnesses_keys.size() );
|
||||||
|
redeem_script.clear();
|
||||||
|
redeem_script.push_back( op[keys_required - 1] );
|
||||||
|
for( auto& key : witnesses_keys ) {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << std::hex << key.second.key_data.size();
|
||||||
|
auto key_size_hex = sidechain::parse_hex( ss.str() );
|
||||||
|
redeem_script.insert( redeem_script.end(), key_size_hex.begin(), key_size_hex.end() );
|
||||||
|
redeem_script.insert( redeem_script.end(), key.second.key_data.begin(), key.second.key_data.end() );
|
||||||
|
}
|
||||||
|
redeem_script.push_back( op[witnesses_keys.size() - 1] );
|
||||||
|
redeem_script.push_back( OP_CHECKMULTISIG );
|
||||||
|
}
|
||||||
|
|
||||||
|
void btc_multisig_address::create_address()
|
||||||
|
{
|
||||||
|
FC_ASSERT( redeem_script.size() > 0 );
|
||||||
|
address.clear();
|
||||||
|
fc::sha256 hash256 = fc::sha256::hash( redeem_script.data(), redeem_script.size() );
|
||||||
|
fc::ripemd160 hash160 = fc::ripemd160::hash( hash256.data(), hash256.data_size() );
|
||||||
|
std::vector<char> temp_addr_hash( sidechain::parse_hex( hash160.str() ) );
|
||||||
|
|
||||||
|
address.push_back( OP_HASH160 );
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << std::hex << temp_addr_hash.size();
|
||||||
|
auto address_size_hex = sidechain::parse_hex( ss.str() );
|
||||||
|
address.insert( address.end(), address_size_hex.begin(), address_size_hex.end() );
|
||||||
|
address.insert( address.end(), temp_addr_hash.begin(), temp_addr_hash.end() );
|
||||||
|
address.push_back( OP_EQUAL );
|
||||||
|
}
|
||||||
|
|
||||||
|
btc_multisig_segwit_address::btc_multisig_segwit_address( const size_t n_required, const std::map< account_id_type, public_key_type >& keys ) :
|
||||||
|
btc_multisig_address( n_required, keys )
|
||||||
|
{
|
||||||
|
create_witness_script();
|
||||||
|
create_segwit_address();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool btc_multisig_segwit_address::operator==( const btc_multisig_segwit_address& addr ) const
|
||||||
|
{
|
||||||
|
if( address != addr.address || redeem_script != addr.redeem_script ||
|
||||||
|
witnesses_keys != addr.witnesses_keys || witness_script != addr.witness_script ||
|
||||||
|
segwit_address != addr.segwit_address || base58_address != addr.base58_address )
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void btc_multisig_segwit_address::create_witness_script()
|
||||||
|
{
|
||||||
|
const auto redeem_sha256 = fc::sha256::hash( redeem_script.data(), redeem_script.size() );
|
||||||
|
witness_script.push_back( OP_0 );
|
||||||
|
witness_script.push_back( 0x20 ); // PUSH_32
|
||||||
|
witness_script.insert( witness_script.end(), redeem_sha256.data(), redeem_sha256.data() + redeem_sha256.data_size() );
|
||||||
|
}
|
||||||
|
|
||||||
|
void btc_multisig_segwit_address::create_segwit_address()
|
||||||
|
{
|
||||||
|
fc::sha256 hash256 = fc::sha256::hash( witness_script.data(), witness_script.size() );
|
||||||
|
fc::ripemd160 hash160 = fc::ripemd160::hash( hash256.data(), hash256.data_size() );
|
||||||
|
|
||||||
|
segwit_address = std::vector<char>(hash160.data(), hash160.data() + hash160.data_size() );
|
||||||
|
base58_address = fc::to_base58( get_address_bytes( segwit_address ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<char> btc_multisig_segwit_address::get_address_bytes( const std::vector<char>& script_hash )
|
||||||
|
{
|
||||||
|
std::vector<char> address_bytes( 1, TESTNET_SCRIPT ); // 1 byte version
|
||||||
|
address_bytes.insert( address_bytes.end(), script_hash.begin(), script_hash.end() );
|
||||||
|
fc::sha256 hash256 = fc::sha256::hash( fc::sha256::hash( address_bytes.data(), address_bytes.size() ) );
|
||||||
|
address_bytes.insert( address_bytes.end(), hash256.data(), hash256.data() + 4 ); // 4 byte checksum
|
||||||
|
|
||||||
|
return address_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,94 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include <graphene/chain/protocol/types.hpp>
|
||||||
|
#include <sidechain/utils.hpp>
|
||||||
|
|
||||||
|
using namespace graphene::chain;
|
||||||
|
|
||||||
|
namespace sidechain {
|
||||||
|
|
||||||
|
const std::vector<char> op = {0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f}; // OP_1 - OP_15
|
||||||
|
|
||||||
|
class btc_multisig_address
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
btc_multisig_address() = default;
|
||||||
|
|
||||||
|
btc_multisig_address( const size_t n_required, const std::map< account_id_type, public_key_type >& keys );
|
||||||
|
|
||||||
|
size_t count_intersection( const std::map< account_id_type, public_key_type >& keys ) const;
|
||||||
|
|
||||||
|
virtual std::vector< char > get_hex_address() { return address; }
|
||||||
|
|
||||||
|
std::vector< char > get_redeem_script() { return redeem_script; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void create_redeem_script();
|
||||||
|
|
||||||
|
void create_address();
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
enum address_types { MAINNET_SCRIPT = 5, TESTNET_SCRIPT = 196 };
|
||||||
|
|
||||||
|
enum { OP_0 = 0x00, OP_EQUAL = 0x87, OP_HASH160 = 0xa9, OP_CHECKMULTISIG = 0xae };
|
||||||
|
|
||||||
|
std::vector< char > address;
|
||||||
|
|
||||||
|
std::vector< char > redeem_script;
|
||||||
|
|
||||||
|
size_t keys_required = 0;
|
||||||
|
|
||||||
|
const std::map< account_id_type, public_key_type > witnesses_keys;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// multisig segwit address (P2WSH)
|
||||||
|
// https://0bin.net/paste/nfnSf0HcBqBUGDto#7zJMRUhGEBkyh-eASQPEwKfNHgQ4D5KrUJRsk8MTPSa
|
||||||
|
class btc_multisig_segwit_address : public btc_multisig_address
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
btc_multisig_segwit_address() = default;
|
||||||
|
|
||||||
|
btc_multisig_segwit_address( const size_t n_required, const std::map< account_id_type, public_key_type >& keys );
|
||||||
|
|
||||||
|
bool operator==( const btc_multisig_segwit_address& addr ) const;
|
||||||
|
|
||||||
|
std::vector< char > get_hex_address() override { return segwit_address; }
|
||||||
|
|
||||||
|
std::string get_base58_address() { return base58_address; }
|
||||||
|
|
||||||
|
std::vector< char > get_witness_script() { return witness_script; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void create_witness_script();
|
||||||
|
|
||||||
|
void create_segwit_address();
|
||||||
|
|
||||||
|
std::vector<char> get_address_bytes( const std::vector<char>& script_hash );
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
std::vector< char > segwit_address;
|
||||||
|
|
||||||
|
std::vector< char > witness_script;
|
||||||
|
|
||||||
|
std::string base58_address;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
FC_REFLECT( sidechain::btc_multisig_address, (address)(redeem_script)(keys_required)(witnesses_keys) );
|
||||||
|
|
||||||
|
FC_REFLECT_DERIVED( sidechain::btc_multisig_segwit_address, (sidechain::btc_multisig_address),
|
||||||
|
(segwit_address)(witness_script)(base58_address) );
|
||||||
11
libraries/sidechain/include/sidechain/utils.hpp
Normal file
11
libraries/sidechain/include/sidechain/utils.hpp
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <fc/crypto/hex.hpp>
|
||||||
|
|
||||||
|
namespace sidechain {
|
||||||
|
|
||||||
|
std::vector<char> parse_hex( const std::string& str );
|
||||||
|
|
||||||
|
}
|
||||||
12
libraries/sidechain/utils.cpp
Normal file
12
libraries/sidechain/utils.cpp
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
#include <sidechain/utils.hpp>
|
||||||
|
|
||||||
|
namespace sidechain {
|
||||||
|
|
||||||
|
std::vector<char> parse_hex( const std::string& str )
|
||||||
|
{
|
||||||
|
std::vector<char> vec( str.size() / 2 );
|
||||||
|
fc::from_hex( str, vec.data(), vec.size() );
|
||||||
|
return vec;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
55
tests/tests/btc_multisig_address_tests.cpp
Normal file
55
tests/tests/btc_multisig_address_tests.cpp
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
#include <boost/test/unit_test.hpp>
|
||||||
|
#include <fc/crypto/digest.hpp>
|
||||||
|
#include <sidechain/btc_multisig_address.hpp>
|
||||||
|
|
||||||
|
using namespace sidechain;
|
||||||
|
|
||||||
|
fc::ecc::public_key_data create_public_key_data( const std::vector<char>& public_key )
|
||||||
|
{
|
||||||
|
FC_ASSERT( public_key.size() == 33 );
|
||||||
|
fc::ecc::public_key_data key;
|
||||||
|
for(size_t i = 0; i < 33; i++) {
|
||||||
|
key.at(i) = public_key[i];
|
||||||
|
}
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE( btc_multisig_address_tests )
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE( create_multisig_address_test ) {
|
||||||
|
|
||||||
|
std::vector<char> public_key1 = parse_hex( "03db643710666b862e0a97f7edbe8ef40ec2c4a29ef995c431c21ca85e35000010" );
|
||||||
|
std::vector<char> public_key2 = parse_hex( "0320000d982c156a6f09df8c7674abddc2bb326533268ed03572916221b4417983" );
|
||||||
|
std::vector<char> public_key3 = parse_hex( "033619e682149aef0c3e2dee3dc5107dd78cb2c14bf0bd25b59056259fbb37ec3f" );
|
||||||
|
|
||||||
|
std::vector<char> address = parse_hex( "a91460cb986f0926e7c4ca1984ca9f56767da2af031e87" );
|
||||||
|
std::vector<char> redeem_script = parse_hex( "522103db643710666b862e0a97f7edbe8ef40ec2c4a29ef995c431c21ca85e35000010210320000d982c156a6f09df8c7674abddc2bb326533268ed03572916221b441798321033619e682149aef0c3e2dee3dc5107dd78cb2c14bf0bd25b59056259fbb37ec3f53ae" );
|
||||||
|
|
||||||
|
fc::ecc::public_key_data key1 = create_public_key_data( public_key1 );
|
||||||
|
fc::ecc::public_key_data key2 = create_public_key_data( public_key2 );
|
||||||
|
fc::ecc::public_key_data key3 = create_public_key_data( public_key3 );
|
||||||
|
|
||||||
|
sidechain::btc_multisig_segwit_address cma(2, { { account_id_type(1), public_key_type(key1) }, { account_id_type(2), public_key_type(key2) }, { account_id_type(3), public_key_type(key3) } });
|
||||||
|
|
||||||
|
BOOST_CHECK( address == cma.address );
|
||||||
|
BOOST_CHECK( redeem_script == cma.redeem_script );
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE( create_segwit_address_test ) {
|
||||||
|
// https://0bin.net/paste/nfnSf0HcBqBUGDto#7zJMRUhGEBkyh-eASQPEwKfNHgQ4D5KrUJRsk8MTPSa
|
||||||
|
std::vector<char> public_key1 = parse_hex( "03b3623117e988b76aaabe3d63f56a4fc88b228a71e64c4cc551d1204822fe85cb" );
|
||||||
|
std::vector<char> public_key2 = parse_hex( "03dd823066e096f72ed617a41d3ca56717db335b1ea47a1b4c5c9dbdd0963acba6" );
|
||||||
|
std::vector<char> public_key3 = parse_hex( "033d7c89bd9da29fa8d44db7906a9778b53121f72191184a9fee785c39180e4be1" );
|
||||||
|
|
||||||
|
std::vector<char> witness_script = parse_hex("0020b6744de4f6ec63cc92f7c220cdefeeb1b1bed2b66c8e5706d80ec247d37e65a1");
|
||||||
|
|
||||||
|
fc::ecc::public_key_data key1 = create_public_key_data( public_key1 );
|
||||||
|
fc::ecc::public_key_data key2 = create_public_key_data( public_key2 );
|
||||||
|
fc::ecc::public_key_data key3 = create_public_key_data( public_key3 );
|
||||||
|
|
||||||
|
sidechain::btc_multisig_segwit_address address(2, { { account_id_type(1), public_key_type(key1) }, { account_id_type(2), public_key_type(key2) }, { account_id_type(3), public_key_type(key3) } });
|
||||||
|
BOOST_CHECK( address.get_witness_script() == witness_script );
|
||||||
|
BOOST_CHECK( address.get_base58_address() == "2NGU4ogScHEHEpReUzi9RB2ha58KAFnkFyk" );
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
Loading…
Reference in a new issue