From 3e0a21bde76d8871ba036a5aba936f9d0c5999b1 Mon Sep 17 00:00:00 2001 From: Davor Hirunda Date: Mon, 5 Sep 2022 13:27:26 +0000 Subject: [PATCH] Add support for P2SH-P2WSH address format --- .../bitcoin/bitcoin_address.cpp | 58 +++++++++++++++---- .../bitcoin/bitcoin_address.hpp | 15 +++-- .../sidechain_net_handler.hpp | 1 + .../peerplays_sidechain_plugin.cpp | 1 + .../sidechain_net_handler_bitcoin.cpp | 45 ++++++++++++-- 5 files changed, 100 insertions(+), 20 deletions(-) diff --git a/libraries/plugins/peerplays_sidechain/bitcoin/bitcoin_address.cpp b/libraries/plugins/peerplays_sidechain/bitcoin/bitcoin_address.cpp index 69b28ee1..4eb9803d 100644 --- a/libraries/plugins/peerplays_sidechain/bitcoin/bitcoin_address.cpp +++ b/libraries/plugins/peerplays_sidechain/bitcoin/bitcoin_address.cpp @@ -242,12 +242,12 @@ bytes btc_multisig_segwit_address::get_address_bytes(const bytes &script_hash) { } btc_weighted_multisig_address::btc_weighted_multisig_address(const std::vector> &keys_data, - network ntype) { + network ntype, payment_type type) { network_type = ntype; + this->type = type; create_redeem_script(keys_data); create_witness_script(); create_segwit_address(); - type = payment_type::P2WSH; } void btc_weighted_multisig_address::create_redeem_script(const std::vector> &keys_data) { @@ -278,26 +278,43 @@ void btc_weighted_multisig_address::create_witness_script() { script_builder builder; builder << op::_0; builder << fc::sha256::hash(redeem_script_.data(), redeem_script_.size()); - witness_script_ = builder; } void btc_weighted_multisig_address::create_segwit_address() { std::string hrp; + address_types byte_version; switch (network_type) { case (network::mainnet): hrp = "bc"; + byte_version = address_types::MAINNET_SCRIPT; break; case (network::testnet): hrp = "tb"; + byte_version = address_types::TESTNET_SCRIPT; break; case (network::regtest): hrp = "bcrt"; + byte_version = address_types::TESTNET_SCRIPT; break; } - fc::sha256 sh = fc::sha256::hash(&redeem_script_[0], redeem_script_.size()); - std::vector hash_data(sh.data(), sh.data() + sh.data_size()); - address = segwit_addr::encode(hrp, 0, hash_data); + + if (type == payment_type::P2WSH) { + fc::sha256 sh = fc::sha256::hash(&redeem_script_[0], redeem_script_.size()); + std::vector hash_data(sh.data(), sh.data() + sh.data_size()); + address = segwit_addr::encode(hrp, 0, hash_data); + } else if (type == payment_type::P2SH_WSH) { + fc::sha256 hash256 = fc::sha256::hash(&witness_script_[0], witness_script_.size()); + fc::ripemd160 hash160 = fc::ripemd160::hash(hash256.data(), hash256.data_size()); + raw_address = bytes(hash160.data(), hash160.data() + hash160.data_size()); + bytes address_bytes(1, byte_version); // 1 byte version + address_bytes.insert(address_bytes.end(), raw_address.begin(), raw_address.end()); + fc::sha256 hash256_1 = fc::sha256::hash(fc::sha256::hash(address_bytes.data(), address_bytes.size())); + address_bytes.insert(address_bytes.end(), hash256_1.data(), hash256_1.data() + 4); // 4 byte checksum + address = fc::to_base58(address_bytes); + } else { + wlog("Unsupported payment type of address"); + } } btc_one_or_m_of_n_multisig_address::btc_one_or_m_of_n_multisig_address(const fc::ecc::public_key &user_key_data, @@ -353,12 +370,12 @@ void btc_one_or_m_of_n_multisig_address::create_segwit_address() { btc_one_or_weighted_multisig_address::btc_one_or_weighted_multisig_address(const fc::ecc::public_key &user_key_data, const std::vector> &keys_data, - bitcoin_address::network ntype) { + bitcoin_address::network ntype, payment_type type) { network_type = ntype; + this->type = type; create_redeem_script(user_key_data, keys_data); create_witness_script(); create_segwit_address(); - type = payment_type::P2WSH; } void btc_one_or_weighted_multisig_address::create_redeem_script(const fc::ecc::public_key &user_key_data, const std::vector> &keys_data) { @@ -398,20 +415,39 @@ void btc_one_or_weighted_multisig_address::create_witness_script() { void btc_one_or_weighted_multisig_address::create_segwit_address() { std::string hrp; + address_types byte_version; switch (network_type) { case (network::mainnet): + byte_version = address_types::MAINNET_SCRIPT; hrp = "bc"; break; case (network::testnet): + byte_version = address_types::TESTNET_SCRIPT; hrp = "tb"; break; case (network::regtest): + byte_version = address_types::TESTNET_SCRIPT; hrp = "bcrt"; break; } - fc::sha256 sh = fc::sha256::hash(&redeem_script_[0], redeem_script_.size()); - std::vector hash_data(sh.data(), sh.data() + sh.data_size()); - address = segwit_addr::encode(hrp, 0, hash_data); + + if (type == payment_type::P2WSH) { + fc::sha256 sh = fc::sha256::hash(&redeem_script_[0], redeem_script_.size()); + std::vector hash_data(sh.data(), sh.data() + sh.data_size()); + address = segwit_addr::encode(hrp, 0, hash_data); + } else if (type == payment_type::P2SH_WSH) { + fc::sha256 hash256 = fc::sha256::hash(&witness_script_[0], witness_script_.size()); + fc::ripemd160 hash160 = fc::ripemd160::hash(hash256.data(), hash256.data_size()); + raw_address = bytes(hash160.data(), hash160.data() + hash160.data_size()); + + bytes address_bytes(1, byte_version); // 1 byte version test net + address_bytes.insert(address_bytes.end(), raw_address.begin(), raw_address.end()); + fc::sha256 hash256_1 = fc::sha256::hash(fc::sha256::hash(address_bytes.data(), address_bytes.size())); + address_bytes.insert(address_bytes.end(), hash256_1.data(), hash256_1.data() + 4); // 4 byte checksum + address = fc::to_base58(address_bytes); + } else { + elog("Unsupported payment type of address"); + } } btc_timelocked_one_or_weighted_multisig_address::btc_timelocked_one_or_weighted_multisig_address(const fc::ecc::public_key &user_key_data, uint32_t latency, const std::vector> &keys_data, bitcoin_address::network ntype) : diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/bitcoin_address.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/bitcoin_address.hpp index b9467e2f..04762352 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/bitcoin_address.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/bitcoin/bitcoin_address.hpp @@ -8,6 +8,14 @@ using namespace graphene::chain; namespace graphene { namespace peerplays_sidechain { namespace bitcoin { const bytes op_num = {0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f}; // OP_1 - OP_15 +enum address_types { MAINNET_SCRIPT = 5, + TESTNET_SCRIPT = 196 }; + +enum script_op { + OP_0 = 0x00, + OP_PUSH = 0x20, + OP_SIZE_34 = 0x22 +}; class bitcoin_address { @@ -96,9 +104,6 @@ private: void create_address(); public: - enum address_types { MAINNET_SCRIPT = 5, - TESTNET_SCRIPT = 196 }; - enum { OP_0 = 0x00, OP_EQUAL = 0x87, OP_HASH160 = 0xa9, @@ -145,7 +150,7 @@ public: btc_weighted_multisig_address() = default; btc_weighted_multisig_address(const std::vector> &keys_data, - network network_type = network::regtest); + network network_type = network::regtest, payment_type type = payment_type::P2SH_WSH); bytes get_redeem_script() const { return redeem_script_; @@ -190,7 +195,7 @@ class btc_one_or_weighted_multisig_address : public bitcoin_address { public: btc_one_or_weighted_multisig_address() = default; btc_one_or_weighted_multisig_address(const fc::ecc::public_key &user_key_data, const std::vector> &keys_data, - network network_type = network::regtest); + network network_type = network::regtest, payment_type type = payment_type::P2SH_WSH); bytes get_redeem_script() const { return redeem_script_; } diff --git a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp index a9257e54..7e6030ed 100644 --- a/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp +++ b/libraries/plugins/peerplays_sidechain/include/graphene/peerplays_sidechain/sidechain_net_handler.hpp @@ -57,6 +57,7 @@ protected: sidechain_type sidechain; bool debug_rpc_calls; + bool use_p2wsh_address; std::map private_keys; diff --git a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp index 461b1a92..3592dac0 100644 --- a/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp +++ b/libraries/plugins/peerplays_sidechain/peerplays_sidechain_plugin.cpp @@ -165,6 +165,7 @@ void peerplays_sidechain_plugin_impl::plugin_set_program_options( cli.add_options()("sidechain-retry-threshold", bpo::value()->default_value(150), "Sidechain retry throttling threshold"); cli.add_options()("debug-rpc-calls", bpo::value()->default_value(false), "Outputs RPC calls to console"); + cli.add_options()("use-p2wsh-address", bpo::value()->default_value(false), "Use p2wsh address instead of p2sh address"); cli.add_options()("bitcoin-sidechain-enabled", bpo::value()->default_value(false), "Bitcoin sidechain handler enabled"); cli.add_options()("bitcoin-node-ip", bpo::value()->default_value("127.0.0.1"), "IP address of Bitcoin node"); diff --git a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp index b48ccbb6..fd3909bf 100644 --- a/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp +++ b/libraries/plugins/peerplays_sidechain/sidechain_net_handler_bitcoin.cpp @@ -329,6 +329,10 @@ sidechain_net_handler_bitcoin::sidechain_net_handler_bitcoin(peerplays_sidechain debug_rpc_calls = options.at("debug-rpc-calls").as(); } + if (options.count("use-p2wsh-address")) { + use_p2wsh_address = options.at("use-p2wsh-address").as(); + } + ip = options.at("bitcoin-node-ip").as(); zmq_port = options.at("bitcoin-node-zmq-port").as(); rpc_port = options.at("bitcoin-node-rpc-port").as(); @@ -793,7 +797,12 @@ void sidechain_net_handler_bitcoin::process_sidechain_addresses() { if (sao.expires == time_point_sec::maximum()) { auto usr_pubkey = fc::ecc::public_key(create_public_key_data(parse_hex(sao.deposit_public_key))); - btc_one_or_weighted_multisig_address addr(usr_pubkey, pubkeys, network_type); + payment_type payment_type_address = payment_type::P2SH_WSH; + if (use_p2wsh_address) { + payment_type_address = payment_type::P2WSH; + } + + btc_one_or_weighted_multisig_address addr(usr_pubkey, pubkeys, network_type, payment_type_address); std::string address_data = "{ \"redeemScript\": \"" + fc::to_hex(addr.get_redeem_script()) + "\", \"witnessScript\": \"" + fc::to_hex(addr.get_witness_script()) + "\" }"; @@ -953,7 +962,13 @@ bool sidechain_net_handler_bitcoin::settle_sidechain_transaction(const sidechain auto pub_key = fc::ecc::public_key(create_public_key_data(parse_hex(si.public_key))); pubkey_weights.push_back(std::make_pair(pub_key, si.weight)); } - btc_weighted_multisig_address addr(pubkey_weights, network_type); + + payment_type payment_type_address = payment_type::P2SH_WSH; + if (use_p2wsh_address) { + payment_type_address = payment_type::P2WSH; + } + + btc_weighted_multisig_address addr(pubkey_weights, network_type, payment_type_address); std::string tx_txid = tx_json.get("result.txid"); uint32_t tx_confirmations = tx_json.get("result.confirmations"); @@ -1003,7 +1018,11 @@ std::string sidechain_net_handler_bitcoin::create_primary_wallet_address(const s pubkey_weights.push_back(std::make_pair(pub_key, son.weight)); } - btc_weighted_multisig_address addr(pubkey_weights, network_type); + payment_type payment_type_address = payment_type::P2SH_WSH; + if (use_p2wsh_address) { + payment_type_address = payment_type::P2WSH; + } + btc_weighted_multisig_address addr(pubkey_weights, network_type, payment_type_address); std::stringstream ss; @@ -1239,6 +1258,18 @@ std::string sidechain_net_handler_bitcoin::send_transaction(const sidechain_tran } // Add redeemscripts to vins and make tx ready for sending sign_witness_transaction_finalize(tx, redeem_scripts, false); + + if (!use_p2wsh_address) { + // get witness script from redeem script + bitcoin::bytes redeem_bytes = parse_hex(redeem_script); + fc::sha256 sha = fc::sha256::hash(&redeem_bytes[0], redeem_bytes.size()); + std::string witness_script(sha.str()); + witness_script.insert(0, std::string("220020")); + for (size_t i = 0; i < tx.vin.size(); i++) { + tx.vin[i].scriptSig = parse_hex(witness_script); + } + } + std::string final_tx_hex = fc::to_hex(pack(tx)); std::string res = bitcoin_client->sendrawtransaction(final_tx_hex); @@ -1309,7 +1340,13 @@ std::string sidechain_net_handler_bitcoin::get_redeemscript_for_userdeposit(cons pubkey_weights.push_back(std::make_pair(pub_key, son.weight)); } auto user_pub_key = fc::ecc::public_key(create_public_key_data(parse_hex(addr_itr->deposit_public_key))); - btc_one_or_weighted_multisig_address deposit_addr(user_pub_key, pubkey_weights, network_type); + + payment_type payment_type_address = payment_type::P2SH_WSH; + if (use_p2wsh_address) { + payment_type_address = payment_type::P2WSH; + } + btc_one_or_weighted_multisig_address deposit_addr(user_pub_key, pubkey_weights, network_type, payment_type_address); + return fc::to_hex(deposit_addr.get_redeem_script()); }