From ce19a5f6cba96e0b578416f131fec53e788536ac Mon Sep 17 00:00:00 2001 From: Gregory Sanders Date: Wed, 6 Mar 2019 09:08:32 -0500 Subject: [PATCH 01/58] Add secure reporting document, link in README --- README.md | 4 + SECURITY.md | 894 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 898 insertions(+) create mode 100644 SECURITY.md diff --git a/README.md b/README.md index 55b85da977abb..a5ebc8441476b 100644 --- a/README.md +++ b/README.md @@ -74,3 +74,7 @@ Translations are periodically pulled from Transifex and merged into the git repo pull from Transifex would automatically overwrite them again. Translators should also subscribe to the [mailing list](https://groups.google.com/forum/#!forum/bitcoin-translators). + +Secure Reporting +------------------ +See [our vulnerability reporting guide](SECURITY.md) \ No newline at end of file diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000000000..b183d770c64df --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,894 @@ +# Security + +The maintainers of Elements take security very seriously and are committed to addressing any disclosed security vulnerabilities quickly and carefully. If you find a security vulnerability, please report it to us following the steps described here. + +## Reporting a Vulnerability + +Privately and confidentially send us a description of the vulnerability that you have discovered using an encrypted and authenticated channel. PGP encrypted email is preferred. Our contact information is given below. + +In your report, please include as much information as you can, including: + +* a description of the vulnerability and how it could be exploited +* its potential impact (e.g. privacy leak, denial of service, theft of funds) +* steps or code for reproducing it +* a proposed patch for remedying it + +Also, provide us with a secure means to contact you with any follow up questions we might have. + +## Considerations + +Please take care not to violate the privacy of users in your report. For example, stack traces or exploit scripts sent to us should never contain private keys or personally identifiable information. + +Give us at least one week to investigate the vulnerability you found and up to 90 days to fix it. Also, please give us reasonable advanced notice if at any point you intend to disclose the vulnerability to anyone else. + +In general, please investigate and report bugs in a way that makes a reasonable, good faith effort not to be disruptive or harmful to us, this software's users, or the users of dependent projects. + +We will take care to inform the maintainers of dependent projects. + +## How to Contact Us + +Greg Sanders +gsanders@blockstream.com + + +``` +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1 + +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 +=bKfQ +-----END PGP PUBLIC KEY BLOCK----- +``` + +## Escalation + +If there is no response from the authors within 3 days, please escalate your vulnerability report by emailing security@blockstream.com using this PGP key: + + +``` +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +lTHPj4G8vH5F/zIFb+uQNSlKzuH+ +=8mAH +-----END PGP PUBLIC KEY BLOCK----- +``` + +## Acknowledgement + +Your help in maintaining and improving the security of this software is deeply appreciated. When the vulnerability you have reported is patched here and in downstream dependencies, we will publicly disclose it. Let us know if you would like to remain anonymous or would like to be acknowledged. We would be happy to acknowledge your contributions to closing the vulnerability you found in the release notes, source code and/or documentation in this repository. From 77ba5eec41c11802e3a227f10676ceadb83f3a88 Mon Sep 17 00:00:00 2001 From: Steven Roose Date: Wed, 20 Mar 2019 11:12:34 +0000 Subject: [PATCH 02/58] RPC params convertion cleanup --- src/rpc/client.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index a907456d9cade..4a0867c68bfb3 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -149,7 +149,6 @@ static const CRPCConvertParam vRPCConvertParams[] = { "logging", 1, "exclude" }, { "disconnectnode", 1, "nodeid" }, { "addwitnessaddress", 1, "p2sh" }, - { "initpegoutwallet", 1, "bip32_counter"}, // Echo with conversion (For testing only) { "echojson", 0, "arg0" }, { "echojson", 1, "arg1" }, @@ -161,10 +160,11 @@ static const CRPCConvertParam vRPCConvertParams[] = { "echojson", 7, "arg7" }, { "echojson", 8, "arg8" }, { "echojson", 9, "arg9" }, - { "rescanblockchain", 0, "start_height"}, - { "rescanblockchain", 1, "stop_height"}, - { "createwallet", 1, "disable_private_keys"}, - { "combineblocksigs", 1, "signatures"}, + // ELEMENTS: + { "rescanblockchain", 0, "start_height" }, + { "rescanblockchain", 1, "stop_height" }, + { "createwallet", 1, "disable_private_keys" }, + { "combineblocksigs", 1, "signatures" }, { "sendtomainchain", 1, "amount" }, { "sendtomainchain", 2, "subtractfeefromamount" }, }; From 36a5d5d0f800007c6f23c4fcf68ac572003abda0 Mon Sep 17 00:00:00 2001 From: Steven Roose Date: Tue, 19 Mar 2019 20:31:58 +0000 Subject: [PATCH 03/58] Fix bug in calculate_contract for Liquid --- src/pegins.cpp | 117 ++++++++++++++++++++++++++----------------------- 1 file changed, 61 insertions(+), 56 deletions(-) diff --git a/src/pegins.cpp b/src/pegins.cpp index b7a457e33a708..133395ac455cc 100644 --- a/src/pegins.cpp +++ b/src/pegins.cpp @@ -75,67 +75,72 @@ CScript calculate_contract(const CScript& federation_script, const CScript& scri std::vector > solutions; unsigned int required; std::vector> keys; + bool is_liquidv1_watchman = MatchLiquidWatchman(federation_script); // Sanity check federation_script only to match 3 templates - if (federation_script != CScript() << OP_TRUE && - !MatchMultisig(federation_script, required, keys) && - !MatchLiquidWatchman(federation_script)) { - assert(false); + if (!is_liquidv1_watchman && + federation_script != CScript() << OP_TRUE && + !MatchMultisig(federation_script, required, keys)) { + assert(false); } + CScript::const_iterator sdpc = federation_script.begin(); + std::vector vch; + opcodetype opcodeTmp; + bool liquid_op_else_found = false; + while (federation_script.GetOp(sdpc, opcodeTmp, vch)) { - CScript::const_iterator sdpc = federation_script.begin(); - std::vector vch; - opcodetype opcodeTmp; - while (federation_script.GetOp(sdpc, opcodeTmp, vch)) + // For liquid watchman template, don't tweak emergency keys + if (is_liquidv1_watchman && opcodeTmp == OP_ELSE) { + liquid_op_else_found = true; + } + + size_t pub_len = 33; + if (vch.size() == pub_len && !liquid_op_else_found) { - size_t pub_len = 33; - if (vch.size() == pub_len) - { - unsigned char tweak[32]; - CHMAC_SHA256(vch.data(), pub_len).Write(scriptPubKey.data(), scriptPubKey.size()).Finalize(tweak); - int ret; - secp256k1_pubkey watchman; - secp256k1_pubkey tweaked; - ret = secp256k1_ec_pubkey_parse(secp256k1_ctx_validation, &watchman, vch.data(), pub_len); - assert(ret == 1); - ret = secp256k1_ec_pubkey_parse(secp256k1_ctx_validation, &tweaked, vch.data(), pub_len); - assert(ret == 1); - // If someone creates a tweak that makes this fail, they broke SHA256 - ret = secp256k1_ec_pubkey_tweak_add(secp256k1_ctx_validation, &tweaked, tweak); - assert(ret == 1); - unsigned char new_pub[33]; - ret = secp256k1_ec_pubkey_serialize(secp256k1_ctx_validation, new_pub, &pub_len, &tweaked, SECP256K1_EC_COMPRESSED); - assert(ret == 1); - assert(pub_len == 33); - - // push tweaked pubkey - std::vector pub_vec(new_pub, new_pub + pub_len); - scriptDestination << pub_vec; - - // Sanity checks to reduce pegin risk. If the tweaked - // value flips a bit, we may lose pegin funds irretrievably. - // We take the tweak, derive its pubkey and check that - // `tweaked - watchman = tweak` to check the computation - // two different ways - secp256k1_pubkey tweaked2; - ret = secp256k1_ec_pubkey_create(secp256k1_ctx_validation, &tweaked2, tweak); - assert(ret); - ret = secp256k1_ec_pubkey_negate(secp256k1_ctx_validation, &watchman); - assert(ret); - secp256k1_pubkey* pubkey_combined[2]; - pubkey_combined[0] = &watchman; - pubkey_combined[1] = &tweaked; - secp256k1_pubkey maybe_tweaked2; - ret = secp256k1_ec_pubkey_combine(secp256k1_ctx_validation, &maybe_tweaked2, pubkey_combined, 2); - assert(ret); - assert(!memcmp(&maybe_tweaked2, &tweaked2, 64)); + unsigned char tweak[32]; + CHMAC_SHA256(vch.data(), pub_len).Write(scriptPubKey.data(), scriptPubKey.size()).Finalize(tweak); + int ret; + secp256k1_pubkey watchman; + secp256k1_pubkey tweaked; + ret = secp256k1_ec_pubkey_parse(secp256k1_ctx_validation, &watchman, vch.data(), pub_len); + assert(ret == 1); + ret = secp256k1_ec_pubkey_parse(secp256k1_ctx_validation, &tweaked, vch.data(), pub_len); + assert(ret == 1); + // If someone creates a tweak that makes this fail, they broke SHA256 + ret = secp256k1_ec_pubkey_tweak_add(secp256k1_ctx_validation, &tweaked, tweak); + assert(ret == 1); + unsigned char new_pub[33]; + ret = secp256k1_ec_pubkey_serialize(secp256k1_ctx_validation, new_pub, &pub_len, &tweaked, SECP256K1_EC_COMPRESSED); + assert(ret == 1); + assert(pub_len == 33); + + // push tweaked pubkey + std::vector pub_vec(new_pub, new_pub + pub_len); + scriptDestination << pub_vec; + + // Sanity checks to reduce pegin risk. If the tweaked + // value flips a bit, we may lose pegin funds irretrievably. + // We take the tweak, derive its pubkey and check that + // `tweaked - watchman = tweak` to check the computation + // two different ways + secp256k1_pubkey tweaked2; + ret = secp256k1_ec_pubkey_create(secp256k1_ctx_validation, &tweaked2, tweak); + assert(ret); + ret = secp256k1_ec_pubkey_negate(secp256k1_ctx_validation, &watchman); + assert(ret); + secp256k1_pubkey* pubkey_combined[2]; + pubkey_combined[0] = &watchman; + pubkey_combined[1] = &tweaked; + secp256k1_pubkey maybe_tweaked2; + ret = secp256k1_ec_pubkey_combine(secp256k1_ctx_validation, &maybe_tweaked2, pubkey_combined, 2); + assert(ret); + assert(!memcmp(&maybe_tweaked2, &tweaked2, 64)); + } else { + // add to script untouched + if (vch.size() > 0) { + scriptDestination << vch; } else { - // add to script untouched - if (vch.size() > 0) { - scriptDestination << vch; - } else { - scriptDestination << opcodeTmp; - } + scriptDestination << opcodeTmp; } } } @@ -441,5 +446,5 @@ bool MatchLiquidWatchman(const CScript& script) return false; } // No more pushes - return (it + 1 == script.end()); + return (it == script.end()); } From 3a0b3b70a53fa2df54ddb4c435962cada842c26d Mon Sep 17 00:00:00 2001 From: Gregory Sanders Date: Tue, 19 Mar 2019 14:13:13 -0400 Subject: [PATCH 04/58] Add tweakfedpeg and compatbility test --- test/functional/rpc_tweakfedpeg.py | 80 ++++++++++++++++++++++++++++++ test/functional/test_runner.py | 1 + 2 files changed, 81 insertions(+) create mode 100755 test/functional/rpc_tweakfedpeg.py diff --git a/test/functional/rpc_tweakfedpeg.py b/test/functional/rpc_tweakfedpeg.py new file mode 100755 index 0000000000000..eccb722173cb5 --- /dev/null +++ b/test/functional/rpc_tweakfedpeg.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 + +import re + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import assert_equal + +# Example script templates that we get proper responses from elements-0.14 based software +OP_TRUE_SCRIPT="51" +OP_CMS_SCRIPT="52210307fd375ed7cced0f50723e3e1a97bbe7ccff7318c815df4e99a59bc94dbcd819210367c4f666f18279009c941e57fab3e42653c6553e5ca092c104d1db279e328a2852ae" +LIQUID_SCRIPT="745c87635b21020e0338c96a8870479f2396c373cc7696ba124e8635d41b0ea581112b678172612102675333a4e4b8fb51d9d4e22fa5a8eaced3fdac8a8cbf9be8c030f75712e6af992102896807d54bc55c24981f24a453c60ad3e8993d693732288068a23df3d9f50d4821029e51a5ef5db3137051de8323b001749932f2ff0d34c82e96a2c2461de96ae56c2102a4e1a9638d46923272c266631d94d36bdb03a64ee0e14c7518e49d2f29bc40102102f8a00b269f8c5e59c67d36db3cdc11b11b21f64b4bffb2815e9100d9aa8daf072103079e252e85abffd3c401a69b087e590a9b86f33f574f08129ccbd3521ecf516b2103111cf405b627e22135b3b3733a4a34aa5723fb0f58379a16d32861bf576b0ec2210318f331b3e5d38156da6633b31929c5b220349859cc9ca3d33fb4e68aa08401742103230dae6b4ac93480aeab26d000841298e3b8f6157028e47b0897c1e025165de121035abff4281ff00660f99ab27bb53e6b33689c2cd8dcd364bc3c90ca5aea0d71a62103bd45cddfacf2083b14310ae4a84e25de61e451637346325222747b157446614c2103cc297026b06c71cbfa52089149157b5ff23de027ac5ab781800a578192d175462103d3bde5d63bdb3a6379b461be64dad45eabff42f758543a9645afd42f6d4248282103ed1e8d5109c9ed66f7941bc53cc71137baa76d50d274bda8d5e8ffbd6e61fe9a5f6702c00fb275522103aab896d53a8e7d6433137bbba940f9c521e085dd07e60994579b64a6d992cf79210291b7d0b1b692f8f524516ed950872e5da10fb1b808b5a526dedc6fed1cf29807210386aa9372fbab374593466bc5451dc59954e90787f08060964d95c87ef34ca5bb5368ae" + +class TweakFedpegTest(BitcoinTestFramework): + + def set_test_params(self): + self.num_nodes = 3 + self.setup_clean_chain = True + self.extra_args = [[ + "-fedpegscript="+OP_TRUE_SCRIPT + ], + [ + "-fedpegscript="+OP_CMS_SCRIPT + ], + [ + "-fedpegscript="+LIQUID_SCRIPT + ]] + + def setup_network(self): + self.setup_nodes() + + def run_test(self): + # Test that OP_TRUE mainchain_addr/claim_script never changes + assert_equal(self.nodes[0].getsidechaininfo()["fedpegscript"], OP_TRUE_SCRIPT) + pegin_addr = self.nodes[0].getpeginaddress() + for _ in range(5): + assert_equal(pegin_addr["mainchain_address"], self.nodes[0].getpeginaddress()["mainchain_address"]) + assert_equal(self.nodes[0].tweakfedpegscript(pegin_addr["claim_script"])["script"], OP_TRUE_SCRIPT) + + # Test that OP_CMS has all keys change and matches elements-0.14 example + pegin_addr = self.nodes[1].getpeginaddress() + assert_equal(self.nodes[1].getsidechaininfo()["fedpegscript"], OP_CMS_SCRIPT) + nontweak_decoded = self.nodes[1].decodescript(OP_CMS_SCRIPT)["asm"] + tweak_decoded = self.nodes[1].decodescript(self.nodes[1].tweakfedpegscript(pegin_addr["claim_script"])["script"])["asm"] + for nontweak, tweak in zip(re.split(" ", nontweak_decoded), re.split(" ", tweak_decoded)): + assert_equal(len(nontweak), len(tweak)) # same opcodes/push sizes + # All pubkeys must be different + if len(nontweak) == 66: + assert(nontweak != tweak) + else: + assert_equal(tweak, nontweak) + + # Elements 0.14 Daemon example + assert_equal(self.nodes[1].tweakfedpegscript("0014008b1fdbaec5a82a9322af1640e871727245bce1")["script"], \ + "522102f5bc6bc407187d06854005c366b84b411534757f4503587cf335645a620f896a2102fd90164e4e7d53417e4eacfa3f86fd39fe40594791758739e8af31eeea4e79c552ae") + + # Test Liquid-style fedpegscript with CSV emergency keys(which don't get tweaked!) + pegin_addr = self.nodes[2].getpeginaddress() + assert_equal(self.nodes[2].getsidechaininfo()["fedpegscript"], LIQUID_SCRIPT) + nontweak_decoded = self.nodes[2].decodescript(LIQUID_SCRIPT)["asm"] + tweak_decoded = self.nodes[2].decodescript(self.nodes[2].tweakfedpegscript(pegin_addr["claim_script"])["script"])["asm"] + assert_equal(len(nontweak_decoded), len(tweak_decoded)) + tweak_match = False # All pubkeys for now must differ + for nontweak, tweak in zip(re.split(" ", nontweak_decoded), re.split(" ", tweak_decoded)): + assert_equal(len(nontweak), len(tweak)) # same opcodes/push sizes + if nontweak == "OP_ELSE": + tweak_match = True + # All pubkeys must be different + if len(nontweak) == 66: + assert(tweak_match == (nontweak == tweak)) + else: + assert_equal(tweak, nontweak) + + + # Liquid Deamon example: + assert_equal(self.nodes[2].tweakfedpegscript("0014008b1fdbaec5a82a9322af1640e871727245bce1")["script"], \ + "745c87635b2103258857ed88024e5296436f851337451d3d68522fbb21dc5a047b965cd2f790932103ab43d77e8706a799cee3c14678574bccaca98c101217369cee987073cd2546492102ee5c8dfdf742bcc2b08386a6a3ec4b6087d39a6fd7e6f0fdeb82ecc30b11d0342102b104b6a469d8b3ccf0a671198e5f7d7010dbf2eb5587733b168f6e0d3be75760210207fc37d4877529fb0d63b1de352689627400a43f18717fa182fe0eea283bdd7921025d8724d82c61708458bc1a08ec9a7fcaf0ab6747ccf0a7d378faea07a261ab3e2102af6859d48d0a4518a4811f12adb974bbd051931e3f96f0b6ae16142c43ae6fd12102458215967f7977effc21b964bd92d870ae7eca98f343801031639150139e63ed2102cdef66bf4b5d26d0be22cabd5761970cfa84be905dc0137ad08ab634f104cb9e210297fb764f808c126f46ce0444124213bdc6a18f2904e75c9144f53bed4c19376e2103c631e77d14a5eb2fc61496ee3e9ea19d1e56e7e65a07aaa786f25ffc2921efde210270addb9011a4b41987b0848a8dc52e5442964fe5188c6a4c9320fbb9016390772103bf9aa75444d0013c46dcf10d70831b75b46a25d901fb7c6360beb2f6cac9c503210278f303dbaad1410a26d99e4dca8925fae4c90532a156c29e8ab1abf5ccaa663d210394cc0983add2dc60aa988c9faedebdc5463106184d72dd430ef24d215daf8b935f6702c00fb275522103aab896d53a8e7d6433137bbba940f9c521e085dd07e60994579b64a6d992cf79210291b7d0b1b692f8f524516ed950872e5da10fb1b808b5a526dedc6fed1cf29807210386aa9372fbab374593466bc5451dc59954e90787f08060964d95c87ef34ca5bb5368ae") + + +if __name__ == '__main__': + TweakFedpegTest().main() diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 8f258a0d8e9a8..4453ee2aa0e40 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -67,6 +67,7 @@ 'feature_blocksign.py', 'rpc_calcfastmerkleroot.py', 'feature_txwitness.py', + 'rpc_tweakfedpeg.py', # Longest test should go first, to favor running tests in parallel 'feature_fee_estimation.py', 'wallet_hd.py', From 3e7cff1e45762c72e13a06abca14bb0aadb34cab Mon Sep 17 00:00:00 2001 From: Steven Roose Date: Wed, 27 Feb 2019 15:42:54 +0000 Subject: [PATCH 05/58] Disable tests that are not relevant for Elements --- test/functional/test_runner.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 4453ee2aa0e40..275a438679f34 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -73,7 +73,8 @@ 'wallet_hd.py', 'wallet_backup.py', # vv Tests less than 5m vv - 'mining_getblocktemplate_longpoll.py', + # ELEMENTS: no longpolling + #'mining_getblocktemplate_longpoll.py', 'feature_maxuploadtarget.py', 'feature_block.py', 'rpc_fundrawtransaction.py', @@ -94,7 +95,8 @@ 'rpc_txoutproof.py', 'wallet_listreceivedby.py', 'wallet_abandonconflict.py', - 'feature_csv_activation.py', + # ELEMENTS: no transitions in Elements + #'feature_csv_activation.py', 'rpc_rawtransaction.py', 'wallet_address_types.py', 'feature_bip68_sequence.py', @@ -145,7 +147,8 @@ 'feature_rbf.py', 'mempool_packages.py', 'rpc_createmultisig.py', - 'feature_versionbits_warning.py', + # ELEMENTS: no versionbits in use + #'feature_versionbits_warning.py', 'rpc_preciousblock.py', 'wallet_importprunedfunds.py', 'rpc_zmq.py', From aa375e66c1a9d2aea23e80e43b86bd5014dec6d2 Mon Sep 17 00:00:00 2001 From: Gregory Sanders Date: Tue, 19 Feb 2019 17:00:45 -0500 Subject: [PATCH 06/58] Replace CScriptID and CKeyID in CTxDestination with dedicated type --- src/bench/ccoins_caching.cpp | 4 +- src/bitcoin-tx.cpp | 6 +-- src/key_io.cpp | 8 ++-- src/keystore.cpp | 9 ++-- src/outputtype.cpp | 18 ++++---- src/pegins.cpp | 2 +- src/rpc/misc.cpp | 8 ++-- src/rpc/rawtransaction.cpp | 4 +- src/rpc/util.cpp | 4 +- src/script/descriptor.cpp | 8 ++-- src/script/ismine.cpp | 2 +- src/script/standard.cpp | 16 ++++--- src/script/standard.h | 22 +++++++-- src/test/coins_tests.cpp | 8 ++-- src/test/denialofservice_tests.cpp | 6 +-- src/test/key_tests.cpp | 8 ++-- src/test/miner_tests.cpp | 2 +- src/test/script_p2sh_tests.cpp | 34 +++++++------- src/test/script_standard_tests.cpp | 68 ++++++++++++++-------------- src/test/script_tests.cpp | 4 +- src/test/sigopcount_tests.cpp | 10 ++-- src/test/transaction_tests.cpp | 30 ++++++------ src/test/txindex_tests.cpp | 2 +- src/test/txvalidationcache_tests.cpp | 4 +- src/wallet/rpcdump.cpp | 26 +++++------ src/wallet/rpcwallet.cpp | 43 +++++++++--------- src/wallet/test/wallet_tests.cpp | 8 ++-- src/wallet/wallet.cpp | 16 ++++--- 28 files changed, 201 insertions(+), 179 deletions(-) diff --git a/src/bench/ccoins_caching.cpp b/src/bench/ccoins_caching.cpp index db303eeead065..a352cf61eca74 100644 --- a/src/bench/ccoins_caching.cpp +++ b/src/bench/ccoins_caching.cpp @@ -39,9 +39,9 @@ SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsViewCache& coinsRet) dummyTransactions[1].vout.resize(2); dummyTransactions[1].vout[0].nValue = 21 * CENT; - dummyTransactions[1].vout[0].scriptPubKey = GetScriptForDestination(key[2].GetPubKey().GetID()); + dummyTransactions[1].vout[0].scriptPubKey = GetScriptForDestination(PKHash(key[2].GetPubKey())); dummyTransactions[1].vout[1].nValue = 22 * CENT; - dummyTransactions[1].vout[1].scriptPubKey = GetScriptForDestination(key[3].GetPubKey().GetID()); + dummyTransactions[1].vout[1].scriptPubKey = GetScriptForDestination(PKHash(key[3].GetPubKey())); AddCoins(coinsRet, dummyTransactions[1], 0); return dummyTransactions; diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index 0783303030119..eccb36a3002e4 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -325,7 +325,7 @@ static void MutateTxAddOutPubKey(CMutableTransaction& tx, const std::string& str } if (bScriptHash) { // Get the ID for the script, and then construct a P2SH destination for it. - scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey)); + scriptPubKey = GetScriptForDestination(ScriptHash(scriptPubKey)); } // construct TxOut, append to transaction output list @@ -399,7 +399,7 @@ static void MutateTxAddOutMultiSig(CMutableTransaction& tx, const std::string& s "redeemScript exceeds size limit: %d > %d", scriptPubKey.size(), MAX_SCRIPT_ELEMENT_SIZE)); } // Get the ID for the script, and then construct a P2SH destination for it. - scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey)); + scriptPubKey = GetScriptForDestination(ScriptHash(scriptPubKey)); } // construct TxOut, append to transaction output list @@ -471,7 +471,7 @@ static void MutateTxAddOutScript(CMutableTransaction& tx, const std::string& str throw std::runtime_error(strprintf( "redeemScript exceeds size limit: %d > %d", scriptPubKey.size(), MAX_SCRIPT_ELEMENT_SIZE)); } - scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey)); + scriptPubKey = GetScriptForDestination(ScriptHash(scriptPubKey)); } // construct TxOut, append to transaction output list diff --git a/src/key_io.cpp b/src/key_io.cpp index 8efcbaf07b279..97ac87c367c16 100644 --- a/src/key_io.cpp +++ b/src/key_io.cpp @@ -29,7 +29,7 @@ class DestinationEncoder : public boost::static_visitor public: explicit DestinationEncoder(const CChainParams& params, const bool for_parent) : m_params(params), for_parent(for_parent) {} - std::string operator()(const CKeyID& id) const + std::string operator()(const PKHash& id) const { CChainParams::Base58Type type = for_parent ? CChainParams::PARENT_PUBKEY_ADDRESS : CChainParams::PUBKEY_ADDRESS; std::vector data = m_params.Base58Prefix(type); @@ -37,7 +37,7 @@ class DestinationEncoder : public boost::static_visitor return EncodeBase58Check(data); } - std::string operator()(const CScriptID& id) const + std::string operator()(const ScriptHash& id) const { CChainParams::Base58Type type = for_parent ? CChainParams::PARENT_SCRIPT_ADDRESS : CChainParams::SCRIPT_ADDRESS; std::vector data = m_params.Base58Prefix(type); @@ -91,7 +91,7 @@ CTxDestination DecodeDestination(const std::string& str, const CChainParams& par const std::vector& pubkey_prefix = params.Base58Prefix(type_pkh); if (data.size() == hash.size() + pubkey_prefix.size() && std::equal(pubkey_prefix.begin(), pubkey_prefix.end(), data.begin())) { std::copy(data.begin() + pubkey_prefix.size(), data.end(), hash.begin()); - return CKeyID(hash); + return PKHash(hash); } // Script-hash-addresses have version 5 (or 196 testnet). // The data vector contains RIPEMD160(SHA256(cscript)), where cscript is the serialized redemption script. @@ -99,7 +99,7 @@ CTxDestination DecodeDestination(const std::string& str, const CChainParams& par const std::vector& script_prefix = params.Base58Prefix(type_sh); if (data.size() == hash.size() + script_prefix.size() && std::equal(script_prefix.begin(), script_prefix.end(), data.begin())) { std::copy(data.begin() + script_prefix.size(), data.end(), hash.begin()); - return CScriptID(hash); + return ScriptHash(hash); } } data.clear(); diff --git a/src/keystore.cpp b/src/keystore.cpp index b2012a04bb7ab..48515c92018c9 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -178,16 +178,17 @@ CKeyID GetKeyForDestination(const CKeyStore& store, const CTxDestination& dest) { // Only supports destinations which map to single public keys, i.e. P2PKH, // P2WPKH, and P2SH-P2WPKH. - if (auto id = boost::get(&dest)) { - return *id; + if (auto id = boost::get(&dest)) { + return CKeyID(*id); } if (auto witness_id = boost::get(&dest)) { return CKeyID(*witness_id); } - if (auto script_id = boost::get(&dest)) { + if (auto script_hash = boost::get(&dest)) { CScript script; + CScriptID script_id(*script_hash); CTxDestination inner_dest; - if (store.GetCScript(*script_id, script) && ExtractDestination(script, inner_dest)) { + if (store.GetCScript(script_id, script) && ExtractDestination(script, inner_dest)) { if (auto inner_witness_id = boost::get(&inner_dest)) { return CKeyID(*inner_witness_id); } diff --git a/src/outputtype.cpp b/src/outputtype.cpp index 7e5690dfc5d4c..df5879101c038 100644 --- a/src/outputtype.cpp +++ b/src/outputtype.cpp @@ -45,14 +45,14 @@ const std::string& FormatOutputType(OutputType type) CTxDestination GetDestinationForKey(const CPubKey& key, OutputType type) { switch (type) { - case OutputType::LEGACY: return key.GetID(); + case OutputType::LEGACY: return PKHash(key); case OutputType::P2SH_SEGWIT: case OutputType::BECH32: { - if (!key.IsCompressed()) return key.GetID(); - CTxDestination witdest = WitnessV0KeyHash(key.GetID()); + if (!key.IsCompressed()) return PKHash(key); + CTxDestination witdest = WitnessV0KeyHash(PKHash(key)); CScript witprog = GetScriptForDestination(witdest); if (type == OutputType::P2SH_SEGWIT) { - return CScriptID(witprog); + return ScriptHash(witprog); } else { return witdest; } @@ -63,10 +63,10 @@ CTxDestination GetDestinationForKey(const CPubKey& key, OutputType type) std::vector GetAllDestinationsForKey(const CPubKey& key) { - CKeyID keyid = key.GetID(); + PKHash keyid(key); if (key.IsCompressed()) { CTxDestination segwit = WitnessV0KeyHash(keyid); - CTxDestination p2sh = CScriptID(GetScriptForDestination(segwit)); + CTxDestination p2sh = ScriptHash(GetScriptForDestination(segwit)); return std::vector{std::move(keyid), std::move(p2sh), std::move(segwit)}; } else { return std::vector{std::move(keyid)}; @@ -80,19 +80,19 @@ CTxDestination AddAndGetDestinationForScript(CKeyStore& keystore, const CScript& // Note that scripts over 520 bytes are not yet supported. switch (type) { case OutputType::LEGACY: - return CScriptID(script); + return ScriptHash(script); case OutputType::P2SH_SEGWIT: case OutputType::BECH32: { CTxDestination witdest = WitnessV0ScriptHash(script); CScript witprog = GetScriptForDestination(witdest); // Check if the resulting program is solvable (i.e. doesn't use an uncompressed key) - if (!IsSolvable(keystore, witprog)) return CScriptID(script); + if (!IsSolvable(keystore, witprog)) return ScriptHash(script); // Add the redeemscript, so that P2WSH and P2SH-P2WSH outputs are recognized as ours. keystore.AddCScript(witprog); if (type == OutputType::BECH32) { return witdest; } else { - return CScriptID(witprog); + return ScriptHash(CScriptID(witprog)); } } default: assert(false); diff --git a/src/pegins.cpp b/src/pegins.cpp index 133395ac455cc..ca1c8eecbd459 100644 --- a/src/pegins.cpp +++ b/src/pegins.cpp @@ -182,7 +182,7 @@ static bool CheckPeginTx(const std::vector& tx_data, T& pegtx, co // Check that the witness program matches the p2ch on the p2sh-p2wsh transaction output CScript tweaked_fedpegscript = calculate_contract(Params().GetConsensus().fedpegScript, claim_script); CScript witness_output(GetScriptForWitness(tweaked_fedpegscript)); - CScript expected_script(CScript() << OP_HASH160 << ToByteVector(CScriptID(witness_output)) << OP_EQUAL); + CScript expected_script(CScript() << OP_HASH160 << ToByteVector(ScriptHash(CScriptID(witness_output))) << OP_EQUAL); if (pegtx->vout[prevout.n].scriptPubKey != expected_script) { return false; } diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 5bf4c4fce4b34..ded78f08797e0 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -177,8 +177,8 @@ static UniValue verifymessage(const JSONRPCRequest& request) throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address"); } - const CKeyID *keyID = boost::get(&destination); - if (!keyID) { + const PKHash *pkhash = boost::get(&destination); + if (!pkhash) { throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key"); } @@ -196,7 +196,7 @@ static UniValue verifymessage(const JSONRPCRequest& request) if (!pubkey.RecoverCompact(ss.GetHash(), vchSig)) return false; - return (pubkey.GetID() == *keyID); + return (pubkey.GetID() == *pkhash); } static UniValue signmessagewithprivkey(const JSONRPCRequest& request) @@ -470,7 +470,7 @@ UniValue tweakfedpegscript(const JSONRPCRequest& request) std::vector scriptData = ParseHex(request.params[0].get_str()); CScript claim_script = CScript(scriptData.begin(), scriptData.end()); CScript tweaked_script = calculate_contract(Params().GetConsensus().fedpegScript, claim_script); - CTxDestination parent_addr(CScriptID(GetScriptForWitness(tweaked_script))); + CTxDestination parent_addr(ScriptHash(GetScriptForWitness(tweaked_script))); UniValue ret(UniValue::VOBJ); ret.pushKV("script", HexStr(tweaked_script)); diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 7fc722e8018d8..214db5e740ee4 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -639,7 +639,7 @@ static UniValue decodescript(const JSONRPCRequest& request) if (type.isStr() && type.get_str() != "scripthash") { // P2SH cannot be wrapped in a P2SH. If this script is already a P2SH, // don't return the address for a P2SH of the P2SH. - r.pushKV("p2sh", EncodeDestination(CScriptID(script))); + r.pushKV("p2sh", EncodeDestination(ScriptHash(CScriptID(script)))); // P2SH and witness programs cannot be wrapped in P2WSH, if this script // is a witness program, don't return addresses for a segwit programs. if (type.get_str() == "pubkey" || type.get_str() == "pubkeyhash" || type.get_str() == "multisig" || type.get_str() == "nonstandard") { @@ -666,7 +666,7 @@ static UniValue decodescript(const JSONRPCRequest& request) segwitScr = GetScriptForDestination(WitnessV0ScriptHash(script)); } ScriptPubKeyToUniv(segwitScr, sr, true); - sr.pushKV("p2sh-segwit", EncodeDestination(CScriptID(segwitScr))); + sr.pushKV("p2sh-segwit", EncodeDestination(ScriptHash(CScriptID(segwitScr)))); r.pushKV("segwit", sr); } } diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp index b4925c9583d1d..ddbdfcdee7458 100644 --- a/src/rpc/util.cpp +++ b/src/rpc/util.cpp @@ -76,7 +76,7 @@ class DescribeAddressVisitor : public boost::static_visitor return UniValue(UniValue::VOBJ); } - UniValue operator()(const CKeyID& keyID) const + UniValue operator()(const PKHash& keyID) const { UniValue obj(UniValue::VOBJ); obj.pushKV("isscript", false); @@ -84,7 +84,7 @@ class DescribeAddressVisitor : public boost::static_visitor return obj; } - UniValue operator()(const CScriptID& scriptID) const + UniValue operator()(const ScriptHash& scriptID) const { UniValue obj(UniValue::VOBJ); obj.pushKV("isscript", true); diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp index 45b097dde6e14..e87981a3f3caf 100644 --- a/src/script/descriptor.cpp +++ b/src/script/descriptor.cpp @@ -228,7 +228,7 @@ class SingleKeyDescriptor final : public Descriptor } }; -CScript P2PKHGetScript(const CPubKey& pubkey) { return GetScriptForDestination(pubkey.GetID()); } +CScript P2PKHGetScript(const CPubKey& pubkey) { return GetScriptForDestination(PKHash(pubkey)); } CScript P2PKGetScript(const CPubKey& pubkey) { return GetScriptForRawPubKey(pubkey); } CScript P2WPKHGetScript(const CPubKey& pubkey) { return GetScriptForDestination(WitnessV0KeyHash(pubkey.GetID())); } @@ -320,7 +320,7 @@ class ConvertorDescriptor : public Descriptor } }; -CScript ConvertP2SH(const CScript& script) { return GetScriptForDestination(CScriptID(script)); } +CScript ConvertP2SH(const CScript& script) { return GetScriptForDestination(ScriptHash(script)); } CScript ConvertP2WSH(const CScript& script) { return GetScriptForDestination(WitnessV0ScriptHash(script)); } /** A parsed combo(P) descriptor. */ @@ -347,14 +347,14 @@ class ComboDescriptor final : public Descriptor CKeyID keyid = key.GetID(); { CScript p2pk = GetScriptForRawPubKey(key); - CScript p2pkh = GetScriptForDestination(keyid); + CScript p2pkh = GetScriptForDestination(PKHash(keyid)); output_scripts = std::vector{std::move(p2pk), std::move(p2pkh)}; out.pubkeys.emplace(keyid, key); } if (key.IsCompressed()) { CScript p2wpkh = GetScriptForDestination(WitnessV0KeyHash(keyid)); CScriptID p2wpkh_id(p2wpkh); - CScript p2sh_p2wpkh = GetScriptForDestination(p2wpkh_id); + CScript p2sh_p2wpkh = GetScriptForDestination(ScriptHash(p2wpkh_id)); out.scripts.emplace(p2wpkh_id, p2wpkh); output_scripts.push_back(std::move(p2wpkh)); output_scripts.push_back(std::move(p2sh_p2wpkh)); diff --git a/src/script/ismine.cpp b/src/script/ismine.cpp index 926d7986bed72..746a4ecb71e58 100644 --- a/src/script/ismine.cpp +++ b/src/script/ismine.cpp @@ -91,7 +91,7 @@ IsMineResult IsMineInner(const CKeyStore& keystore, const CScript& scriptPubKey, // This also applies to the P2WSH case. break; } - ret = std::max(ret, IsMineInner(keystore, GetScriptForDestination(CKeyID(uint160(vSolutions[0]))), IsMineSigVersion::WITNESS_V0)); + ret = std::max(ret, IsMineInner(keystore, GetScriptForDestination(PKHash(uint160(vSolutions[0]))), IsMineSigVersion::WITNESS_V0)); break; } case TX_PUBKEYHASH: diff --git a/src/script/standard.cpp b/src/script/standard.cpp index baf0e6681b717..afedcc240b6a5 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -21,6 +21,10 @@ unsigned nMaxDatacarrierBytes = MAX_OP_RETURN_RELAY; CScriptID::CScriptID(const CScript& in) : uint160(Hash160(in.begin(), in.end())) {} +ScriptHash::ScriptHash(const CScript& in) : uint160(Hash160(in.begin(), in.end())) {} + +PKHash::PKHash(const CPubKey& pubkey) : uint160(pubkey.GetID()) {} + WitnessV0ScriptHash::WitnessV0ScriptHash(const CScript& in) { CSHA256().Write(in.data(), in.size()).Finalize(begin()); @@ -174,17 +178,17 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet) if (!pubKey.IsValid()) return false; - addressRet = pubKey.GetID(); + addressRet = PKHash(pubKey); return true; } else if (whichType == TX_PUBKEYHASH) { - addressRet = CKeyID(uint160(vSolutions[0])); + addressRet = PKHash(uint160(vSolutions[0])); return true; } else if (whichType == TX_SCRIPTHASH) { - addressRet = CScriptID(uint160(vSolutions[0])); + addressRet = ScriptHash(uint160(vSolutions[0])); return true; } else if (whichType == TX_WITNESS_V0_KEYHASH) { WitnessV0KeyHash hash; @@ -229,7 +233,7 @@ bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std:: if (!pubKey.IsValid()) continue; - CTxDestination address = pubKey.GetID(); + CTxDestination address = PKHash(pubKey); addressRet.push_back(address); } @@ -262,13 +266,13 @@ class CScriptVisitor : public boost::static_visitor return false; } - bool operator()(const CKeyID &keyID) const { + bool operator()(const PKHash &keyID) const { script->clear(); *script << OP_DUP << OP_HASH160 << ToByteVector(keyID) << OP_EQUALVERIFY << OP_CHECKSIG; return true; } - bool operator()(const CScriptID &scriptID) const { + bool operator()(const ScriptHash &scriptID) const { script->clear(); *script << OP_HASH160 << ToByteVector(scriptID) << OP_EQUAL; return true; diff --git a/src/script/standard.h b/src/script/standard.h index db1e082a0aae8..67374efbc0b9d 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -74,6 +74,22 @@ class CNoDestination { friend bool operator<(const CNoDestination &a, const CNoDestination &b) { return true; } }; +struct PKHash : public uint160 +{ + PKHash() : uint160() {} + explicit PKHash(const uint160& hash) : uint160(hash) {} + explicit PKHash(const CPubKey& pubkey); + using uint160::uint160; +}; + +struct ScriptHash : public uint160 +{ + ScriptHash() : uint160() {} + explicit ScriptHash(const uint160& hash) : uint160(hash) {} + explicit ScriptHash(const CScript& script); + using uint160::uint160; +}; + struct WitnessV0ScriptHash : public uint256 { WitnessV0ScriptHash() : uint256() {} @@ -129,15 +145,15 @@ class NullData /** * A txout script template with a specific destination. It is either: * * CNoDestination: no destination set - * * CKeyID: TX_PUBKEYHASH destination (P2PKH) - * * CScriptID: TX_SCRIPTHASH destination (P2SH) + * * PKHash: TX_PUBKEYHASH destination (P2PKH) + * * ScriptHash: TX_SCRIPTHASH destination (P2SH) * * WitnessV0ScriptHash: TX_WITNESS_V0_SCRIPTHASH destination (P2WSH) * * WitnessV0KeyHash: TX_WITNESS_V0_KEYHASH destination (P2WPKH) * * WitnessUnknown: TX_WITNESS_UNKNOWN destination (P2W???) * * NullData: TX_NULL_DATA destination (OP_RETURN) * A CTxDestination is the internal data type encoded in a bitcoin address */ -typedef boost::variant CTxDestination; +typedef boost::variant CTxDestination; /** Check whether a CTxDestination is a CNoDestination. */ bool IsValidDestination(const CTxDestination& dest); diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp index 14ab6828f186a..61f325dbf6633 100644 --- a/src/test/coins_tests.cpp +++ b/src/test/coins_tests.cpp @@ -481,8 +481,8 @@ BOOST_AUTO_TEST_CASE(ccoins_serialization) ss1 >> cc1; BOOST_CHECK_EQUAL(cc1.fCoinBase, false); BOOST_CHECK_EQUAL(cc1.nHeight, 203998U); - BOOST_CHECK_EQUAL(cc1.out.nValue, CAmount{60000000000}); - BOOST_CHECK_EQUAL(HexStr(cc1.out.scriptPubKey), HexStr(GetScriptForDestination(CKeyID(uint160(ParseHex("816115944e077fe7c803cfa57f29b36bf87c1d35")))))); + BOOST_CHECK_EQUAL(cc1.out.nValue.GetAmount(), 60000000000); + BOOST_CHECK_EQUAL(HexStr(cc1.out.scriptPubKey), HexStr(GetScriptForDestination(PKHash(uint160(ParseHex("816115944e077fe7c803cfa57f29b36bf87c1d35")))))); // Good example CDataStream ss2(ParseHex("8ddf77bbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa4"), SER_DISK, CLIENT_VERSION); @@ -490,8 +490,8 @@ BOOST_AUTO_TEST_CASE(ccoins_serialization) ss2 >> cc2; BOOST_CHECK_EQUAL(cc2.fCoinBase, true); BOOST_CHECK_EQUAL(cc2.nHeight, 120891U); - BOOST_CHECK_EQUAL(cc2.out.nValue, 110397); - BOOST_CHECK_EQUAL(HexStr(cc2.out.scriptPubKey), HexStr(GetScriptForDestination(CKeyID(uint160(ParseHex("8c988f1a4a4de2161e0f50aac7f17e7f9555caa4")))))); + BOOST_CHECK_EQUAL(cc2.out.nValue.GetAmount(), 110397); + BOOST_CHECK_EQUAL(HexStr(cc2.out.scriptPubKey), HexStr(GetScriptForDestination(PKHash(uint160(ParseHex("8c988f1a4a4de2161e0f50aac7f17e7f9555caa4")))))); // Smallest possible example CDataStream ss3(ParseHex("000006"), SER_DISK, CLIENT_VERSION); diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp index 52bbe96b96e3e..4948fe50e2f3d 100644 --- a/src/test/denialofservice_tests.cpp +++ b/src/test/denialofservice_tests.cpp @@ -349,7 +349,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans) tx.vin[0].scriptSig << OP_1; tx.vout.resize(1); tx.vout[0].nValue = 1*CENT; - tx.vout[0].scriptPubKey = GetScriptForDestination(key.GetPubKey().GetID()); + tx.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey())); AddOrphanTx(MakeTransactionRef(tx), i); } @@ -365,7 +365,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans) tx.vin[0].prevout.hash = txPrev->GetHash(); tx.vout.resize(1); tx.vout[0].nValue = 1*CENT; - tx.vout[0].scriptPubKey = GetScriptForDestination(key.GetPubKey().GetID()); + tx.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey())); SignSignature(keystore, *txPrev, tx, 0, SIGHASH_ALL); AddOrphanTx(MakeTransactionRef(tx), i); @@ -379,7 +379,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans) CMutableTransaction tx; tx.vout.resize(1); tx.vout[0].nValue = 1*CENT; - tx.vout[0].scriptPubKey = GetScriptForDestination(key.GetPubKey().GetID()); + tx.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey())); tx.vin.resize(2777); for (unsigned int j = 0; j < tx.vin.size(); j++) { diff --git a/src/test/key_tests.cpp b/src/test/key_tests.cpp index 61db70decbf28..cae9b43562406 100644 --- a/src/test/key_tests.cpp +++ b/src/test/key_tests.cpp @@ -68,10 +68,10 @@ BOOST_AUTO_TEST_CASE(key_test1) BOOST_CHECK(!key2C.VerifyPubKey(pubkey2)); BOOST_CHECK(key2C.VerifyPubKey(pubkey2C)); - BOOST_CHECK(DecodeDestination(addr1) == CTxDestination(pubkey1.GetID())); - BOOST_CHECK(DecodeDestination(addr2) == CTxDestination(pubkey2.GetID())); - BOOST_CHECK(DecodeDestination(addr1C) == CTxDestination(pubkey1C.GetID())); - BOOST_CHECK(DecodeDestination(addr2C) == CTxDestination(pubkey2C.GetID())); + BOOST_CHECK(DecodeDestination(addr1) == CTxDestination(PKHash(pubkey1))); + BOOST_CHECK(DecodeDestination(addr2) == CTxDestination(PKHash(pubkey2))); + BOOST_CHECK(DecodeDestination(addr1C) == CTxDestination(PKHash(pubkey1C))); + BOOST_CHECK(DecodeDestination(addr2C) == CTxDestination(PKHash(pubkey2C))); for (int n=0; n<16; n++) { diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index 3eb8aa14fd419..eff31b0f0d343 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -399,7 +399,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vin[0].scriptSig = CScript() << OP_1; tx.vout[0].nValue = BLOCKSUBSIDY-LOWFEE; script = CScript() << OP_0; - tx.vout[0].scriptPubKey = GetScriptForDestination(CScriptID(script)); + tx.vout[0].scriptPubKey = GetScriptForDestination(ScriptHash(script)); hash = tx.GetHash(); mempool.addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); tx.vin[0].prevout.hash = hash; diff --git a/src/test/script_p2sh_tests.cpp b/src/test/script_p2sh_tests.cpp index 5b10935302a5f..8ae24645244a0 100644 --- a/src/test/script_p2sh_tests.cpp +++ b/src/test/script_p2sh_tests.cpp @@ -68,14 +68,14 @@ BOOST_AUTO_TEST_CASE(sign) // different keys, straight/P2SH, pubkey/pubkeyhash CScript standardScripts[4]; standardScripts[0] << ToByteVector(key[0].GetPubKey()) << OP_CHECKSIG; - standardScripts[1] = GetScriptForDestination(key[1].GetPubKey().GetID()); + standardScripts[1] = GetScriptForDestination(PKHash(key[1].GetPubKey())); standardScripts[2] << ToByteVector(key[1].GetPubKey()) << OP_CHECKSIG; - standardScripts[3] = GetScriptForDestination(key[2].GetPubKey().GetID()); + standardScripts[3] = GetScriptForDestination(PKHash(key[2].GetPubKey())); CScript evalScripts[4]; for (int i = 0; i < 4; i++) { keystore.AddCScript(standardScripts[i]); - evalScripts[i] = GetScriptForDestination(CScriptID(standardScripts[i])); + evalScripts[i] = GetScriptForDestination(ScriptHash(standardScripts[i])); } CMutableTransaction txFrom; // Funding transaction: @@ -130,7 +130,7 @@ BOOST_AUTO_TEST_CASE(norecurse) CScript invalidAsScript; invalidAsScript << OP_INVALIDOPCODE << OP_INVALIDOPCODE; - CScript p2sh = GetScriptForDestination(CScriptID(invalidAsScript)); + CScript p2sh = GetScriptForDestination(ScriptHash(invalidAsScript)); CScript scriptSig; scriptSig << Serialize(invalidAsScript); @@ -141,7 +141,7 @@ BOOST_AUTO_TEST_CASE(norecurse) // Try to recur, and verification should succeed because // the inner HASH160 <> EQUAL should only check the hash: - CScript p2sh2 = GetScriptForDestination(CScriptID(p2sh)); + CScript p2sh2 = GetScriptForDestination(ScriptHash(p2sh)); CScript scriptSig2; scriptSig2 << Serialize(invalidAsScript) << Serialize(p2sh); @@ -164,7 +164,7 @@ BOOST_AUTO_TEST_CASE(set) } CScript inner[4]; - inner[0] = GetScriptForDestination(key[0].GetPubKey().GetID()); + inner[0] = GetScriptForDestination(PKHash(key[0].GetPubKey())); inner[1] = GetScriptForMultisig(2, std::vector(keys.begin(), keys.begin()+2)); inner[2] = GetScriptForMultisig(1, std::vector(keys.begin(), keys.begin()+2)); inner[3] = GetScriptForMultisig(2, std::vector(keys.begin(), keys.begin()+3)); @@ -172,7 +172,7 @@ BOOST_AUTO_TEST_CASE(set) CScript outer[4]; for (int i = 0; i < 4; i++) { - outer[i] = GetScriptForDestination(CScriptID(inner[i])); + outer[i] = GetScriptForDestination(ScriptHash(inner[i])); keystore.AddCScript(inner[i]); } @@ -244,7 +244,7 @@ BOOST_AUTO_TEST_CASE(switchover) CScript scriptSig; scriptSig << Serialize(notValid); - CScript fund = GetScriptForDestination(CScriptID(notValid)); + CScript fund = GetScriptForDestination(ScriptHash(notValid)); // Validation should succeed under old rules (hash is correct): @@ -275,11 +275,11 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard) txFrom.vout.resize(7); // First three are standard: - CScript pay1 = GetScriptForDestination(key[0].GetPubKey().GetID()); + CScript pay1 = GetScriptForDestination(PKHash(key[0].GetPubKey())); keystore.AddCScript(pay1); CScript pay1of3 = GetScriptForMultisig(1, keys); - txFrom.vout[0].scriptPubKey = GetScriptForDestination(CScriptID(pay1)); // P2SH (OP_CHECKSIG) + txFrom.vout[0].scriptPubKey = GetScriptForDestination(ScriptHash(pay1)); // P2SH (OP_CHECKSIG) txFrom.vout[0].nValue = 1000; txFrom.vout[1].scriptPubKey = pay1; // ordinary OP_CHECKSIG txFrom.vout[1].nValue = 2000; @@ -294,7 +294,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard) oneAndTwo << OP_2 << ToByteVector(key[3].GetPubKey()) << ToByteVector(key[4].GetPubKey()) << ToByteVector(key[5].GetPubKey()); oneAndTwo << OP_3 << OP_CHECKMULTISIG; keystore.AddCScript(oneAndTwo); - txFrom.vout[3].scriptPubKey = GetScriptForDestination(CScriptID(oneAndTwo)); + txFrom.vout[3].scriptPubKey = GetScriptForDestination(ScriptHash(oneAndTwo)); txFrom.vout[3].nValue = 4000; // vout[4] is max sigops: @@ -303,24 +303,24 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard) fifteenSigops << ToByteVector(key[i%3].GetPubKey()); fifteenSigops << OP_15 << OP_CHECKMULTISIG; keystore.AddCScript(fifteenSigops); - txFrom.vout[4].scriptPubKey = GetScriptForDestination(CScriptID(fifteenSigops)); + txFrom.vout[4].scriptPubKey = GetScriptForDestination(ScriptHash(fifteenSigops)); txFrom.vout[4].nValue = 5000; // vout[5/6] are non-standard because they exceed MAX_P2SH_SIGOPS CScript sixteenSigops; sixteenSigops << OP_16 << OP_CHECKMULTISIG; keystore.AddCScript(sixteenSigops); - txFrom.vout[5].scriptPubKey = GetScriptForDestination(CScriptID(sixteenSigops)); + txFrom.vout[5].scriptPubKey = GetScriptForDestination(ScriptHash(sixteenSigops)); txFrom.vout[5].nValue = 5000; CScript twentySigops; twentySigops << OP_CHECKMULTISIG; keystore.AddCScript(twentySigops); - txFrom.vout[6].scriptPubKey = GetScriptForDestination(CScriptID(twentySigops)); + txFrom.vout[6].scriptPubKey = GetScriptForDestination(ScriptHash(twentySigops)); txFrom.vout[6].nValue = 6000; AddCoins(coins, txFrom, 0); CMutableTransaction txTo; txTo.vout.resize(1); - txTo.vout[0].scriptPubKey = GetScriptForDestination(key[1].GetPubKey().GetID()); + txTo.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key[1].GetPubKey())); txTo.vin.resize(5); for (int i = 0; i < 5; i++) @@ -343,7 +343,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard) CMutableTransaction txToNonStd1; txToNonStd1.vout.resize(1); - txToNonStd1.vout[0].scriptPubKey = GetScriptForDestination(key[1].GetPubKey().GetID()); + txToNonStd1.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key[1].GetPubKey())); txToNonStd1.vout[0].nValue = 1000; txToNonStd1.vin.resize(1); txToNonStd1.vin[0].prevout.n = 5; @@ -355,7 +355,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard) CMutableTransaction txToNonStd2; txToNonStd2.vout.resize(1); - txToNonStd2.vout[0].scriptPubKey = GetScriptForDestination(key[1].GetPubKey().GetID()); + txToNonStd2.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key[1].GetPubKey())); txToNonStd2.vout[0].nValue = 1000; txToNonStd2.vin.resize(1); txToNonStd2.vin[0].prevout.n = 6; diff --git a/src/test/script_standard_tests.cpp b/src/test/script_standard_tests.cpp index b3e4b12918437..936284b4cd0e5 100644 --- a/src/test/script_standard_tests.cpp +++ b/src/test/script_standard_tests.cpp @@ -179,23 +179,23 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestination) s.clear(); s << ToByteVector(pubkey) << OP_CHECKSIG; BOOST_CHECK(ExtractDestination(s, address)); - BOOST_CHECK(boost::get(&address) && - *boost::get(&address) == pubkey.GetID()); + BOOST_CHECK(boost::get(&address) && + *boost::get(&address) == PKHash(pubkey)); // TX_PUBKEYHASH s.clear(); s << OP_DUP << OP_HASH160 << ToByteVector(pubkey.GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; BOOST_CHECK(ExtractDestination(s, address)); - BOOST_CHECK(boost::get(&address) && - *boost::get(&address) == pubkey.GetID()); + BOOST_CHECK(boost::get(&address) && + *boost::get(&address) == PKHash(pubkey)); // TX_SCRIPTHASH CScript redeemScript(s); // initialize with leftover P2PKH script s.clear(); s << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL; BOOST_CHECK(ExtractDestination(s, address)); - BOOST_CHECK(boost::get(&address) && - *boost::get(&address) == CScriptID(redeemScript)); + BOOST_CHECK(boost::get(&address) && + *boost::get(&address) == ScriptHash(redeemScript)); // TX_MULTISIG s.clear(); @@ -255,8 +255,8 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations) BOOST_CHECK_EQUAL(whichType, TX_PUBKEY); BOOST_CHECK_EQUAL(addresses.size(), 1U); BOOST_CHECK_EQUAL(nRequired, 1); - BOOST_CHECK(boost::get(&addresses[0]) && - *boost::get(&addresses[0]) == pubkeys[0].GetID()); + BOOST_CHECK(boost::get(&addresses[0]) && + *boost::get(&addresses[0]) == PKHash(pubkeys[0])); // TX_PUBKEYHASH s.clear(); @@ -265,8 +265,8 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations) BOOST_CHECK_EQUAL(whichType, TX_PUBKEYHASH); BOOST_CHECK_EQUAL(addresses.size(), 1U); BOOST_CHECK_EQUAL(nRequired, 1); - BOOST_CHECK(boost::get(&addresses[0]) && - *boost::get(&addresses[0]) == pubkeys[0].GetID()); + BOOST_CHECK(boost::get(&addresses[0]) && + *boost::get(&addresses[0]) == PKHash(pubkeys[0])); // TX_SCRIPTHASH CScript redeemScript(s); // initialize with leftover P2PKH script @@ -276,8 +276,8 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations) BOOST_CHECK_EQUAL(whichType, TX_SCRIPTHASH); BOOST_CHECK_EQUAL(addresses.size(), 1U); BOOST_CHECK_EQUAL(nRequired, 1); - BOOST_CHECK(boost::get(&addresses[0]) && - *boost::get(&addresses[0]) == CScriptID(redeemScript)); + BOOST_CHECK(boost::get(&addresses[0]) && + *boost::get(&addresses[0]) == ScriptHash(redeemScript)); // TX_MULTISIG s.clear(); @@ -289,10 +289,10 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations) BOOST_CHECK_EQUAL(whichType, TX_MULTISIG); BOOST_CHECK_EQUAL(addresses.size(), 2U); BOOST_CHECK_EQUAL(nRequired, 2); - BOOST_CHECK(boost::get(&addresses[0]) && - *boost::get(&addresses[0]) == pubkeys[0].GetID()); - BOOST_CHECK(boost::get(&addresses[1]) && - *boost::get(&addresses[1]) == pubkeys[1].GetID()); + BOOST_CHECK(boost::get(&addresses[0]) && + *boost::get(&addresses[0]) == PKHash(pubkeys[0])); + BOOST_CHECK(boost::get(&addresses[1]) && + *boost::get(&addresses[1]) == PKHash(pubkeys[1])); // TX_NULL_DATA s.clear(); @@ -311,17 +311,17 @@ BOOST_AUTO_TEST_CASE(script_standard_GetScriptFor_) CScript expected, result; - // CKeyID + // PKHash expected.clear(); expected << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; - result = GetScriptForDestination(pubkeys[0].GetID()); + result = GetScriptForDestination(PKHash(pubkeys[0])); BOOST_CHECK(result == expected); // CScriptID CScript redeemScript(result); expected.clear(); expected << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL; - result = GetScriptForDestination(CScriptID(redeemScript)); + result = GetScriptForDestination(ScriptHash(redeemScript)); BOOST_CHECK(result == expected); // CNoDestination @@ -421,7 +421,7 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine) // P2PKH compressed { CBasicKeyStore keystore; - scriptPubKey = GetScriptForDestination(pubkeys[0].GetID()); + scriptPubKey = GetScriptForDestination(PKHash(pubkeys[0])); // Keystore does not have key result = IsMine(keystore, scriptPubKey); @@ -436,7 +436,7 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine) // P2PKH uncompressed { CBasicKeyStore keystore; - scriptPubKey = GetScriptForDestination(uncompressedPubkey.GetID()); + scriptPubKey = GetScriptForDestination(PKHash(uncompressedPubkey)); // Keystore does not have key result = IsMine(keystore, scriptPubKey); @@ -452,8 +452,8 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine) { CBasicKeyStore keystore; - CScript redeemScript = GetScriptForDestination(pubkeys[0].GetID()); - scriptPubKey = GetScriptForDestination(CScriptID(redeemScript)); + CScript redeemScript = GetScriptForDestination(PKHash(pubkeys[0])); + scriptPubKey = GetScriptForDestination(ScriptHash(redeemScript)); // Keystore does not have redeemScript or key result = IsMine(keystore, scriptPubKey); @@ -474,9 +474,9 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine) { CBasicKeyStore keystore; - CScript redeemscript_inner = GetScriptForDestination(pubkeys[0].GetID()); - CScript redeemscript = GetScriptForDestination(CScriptID(redeemscript_inner)); - scriptPubKey = GetScriptForDestination(CScriptID(redeemscript)); + CScript redeemscript_inner = GetScriptForDestination(PKHash(pubkeys[0])); + CScript redeemscript = GetScriptForDestination(ScriptHash(redeemscript_inner)); + scriptPubKey = GetScriptForDestination(ScriptHash(redeemscript)); keystore.AddCScript(redeemscript); keystore.AddCScript(redeemscript_inner); @@ -490,8 +490,8 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine) { CBasicKeyStore keystore; - CScript redeemscript = GetScriptForDestination(pubkeys[0].GetID()); - CScript witnessscript = GetScriptForDestination(CScriptID(redeemscript)); + CScript redeemscript = GetScriptForDestination(ScriptHash(PKHash(pubkeys[0]))); + CScript witnessscript = GetScriptForDestination(ScriptHash(redeemscript)); scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessscript)); keystore.AddCScript(witnessscript); @@ -506,7 +506,7 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine) { CBasicKeyStore keystore; - CScript witnessscript = GetScriptForDestination(WitnessV0KeyHash(pubkeys[0].GetID())); + CScript witnessscript = GetScriptForDestination(WitnessV0KeyHash(PKHash(pubkeys[0]))); scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessscript)); keystore.AddCScript(witnessscript); @@ -520,7 +520,7 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine) { CBasicKeyStore keystore; - CScript witnessscript_inner = GetScriptForDestination(pubkeys[0].GetID()); + CScript witnessscript_inner = GetScriptForDestination(PKHash(pubkeys[0])); CScript witnessscript = GetScriptForDestination(WitnessV0ScriptHash(witnessscript_inner)); scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessscript)); @@ -537,7 +537,7 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine) CBasicKeyStore keystore; keystore.AddKey(keys[0]); - scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(pubkeys[0].GetID())); + scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(PKHash(pubkeys[0]))); // Keystore implicitly has key and P2SH redeemScript keystore.AddCScript(scriptPubKey); @@ -550,7 +550,7 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine) CBasicKeyStore keystore; keystore.AddKey(uncompressedKey); - scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(uncompressedPubkey.GetID())); + scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(PKHash(uncompressedPubkey))); // Keystore has key, but no P2SH redeemScript result = IsMine(keystore, scriptPubKey); @@ -598,7 +598,7 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine) keystore.AddKey(keys[1]); CScript redeemScript = GetScriptForMultisig(2, {uncompressedPubkey, pubkeys[1]}); - scriptPubKey = GetScriptForDestination(CScriptID(redeemScript)); + scriptPubKey = GetScriptForDestination(ScriptHash(CScriptID(redeemScript))); // Keystore has no redeemScript result = IsMine(keystore, scriptPubKey); @@ -664,7 +664,7 @@ BOOST_AUTO_TEST_CASE(script_standard_IsMine) CScript witnessScript = GetScriptForMultisig(2, {pubkeys[0], pubkeys[1]}); CScript redeemScript = GetScriptForDestination(WitnessV0ScriptHash(witnessScript)); - scriptPubKey = GetScriptForDestination(CScriptID(redeemScript)); + scriptPubKey = GetScriptForDestination(ScriptHash(redeemScript)); // Keystore has no witnessScript, P2SH redeemScript, or keys result = IsMine(keystore, scriptPubKey); diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 92b1b2bbc7324..48003ce353675 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -1189,7 +1189,7 @@ BOOST_AUTO_TEST_CASE(script_combineSigs) keystore.AddKey(key); } - CMutableTransaction txFrom = BuildCreditingTransaction(GetScriptForDestination(keys[0].GetPubKey().GetID())); + CMutableTransaction txFrom = BuildCreditingTransaction(GetScriptForDestination(PKHash(keys[0].GetPubKey()))); CMutableTransaction txTo = BuildSpendingTransaction(CScript(), CScriptWitness(), txFrom); CScript& scriptPubKey = txFrom.vout[0].scriptPubKey; SignatureData scriptSig; @@ -1215,7 +1215,7 @@ BOOST_AUTO_TEST_CASE(script_combineSigs) // P2SH, single-signature case: CScript pkSingle; pkSingle << ToByteVector(keys[0].GetPubKey()) << OP_CHECKSIG; keystore.AddCScript(pkSingle); - scriptPubKey = GetScriptForDestination(CScriptID(pkSingle)); + scriptPubKey = GetScriptForDestination(ScriptHash(pkSingle)); SignSignature(keystore, txFrom, txTo, 0, SIGHASH_ALL); scriptSig = DataFromTransaction(txTo, 0, txFrom.vout[0]); combined = CombineSignatures(txFrom.vout[0], txTo, scriptSig, empty); diff --git a/src/test/sigopcount_tests.cpp b/src/test/sigopcount_tests.cpp index d4eec99721f03..d223b4dcce67f 100644 --- a/src/test/sigopcount_tests.cpp +++ b/src/test/sigopcount_tests.cpp @@ -39,7 +39,7 @@ BOOST_AUTO_TEST_CASE(GetSigOpCount) BOOST_CHECK_EQUAL(s1.GetSigOpCount(true), 3U); BOOST_CHECK_EQUAL(s1.GetSigOpCount(false), 21U); - CScript p2sh = GetScriptForDestination(CScriptID(s1)); + CScript p2sh = GetScriptForDestination(ScriptHash(s1)); CScript scriptSig; scriptSig << OP_0 << Serialize(s1); BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(scriptSig), 3U); @@ -55,7 +55,7 @@ BOOST_AUTO_TEST_CASE(GetSigOpCount) BOOST_CHECK_EQUAL(s2.GetSigOpCount(true), 3U); BOOST_CHECK_EQUAL(s2.GetSigOpCount(false), 20U); - p2sh = GetScriptForDestination(CScriptID(s2)); + p2sh = GetScriptForDestination(ScriptHash(s2)); BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(true), 0U); BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(false), 0U); CScript scriptSig2; @@ -152,7 +152,7 @@ BOOST_AUTO_TEST_CASE(GetTxSigOpCost) // Multisig nested in P2SH { CScript redeemScript = CScript() << 1 << ToByteVector(pubkey) << ToByteVector(pubkey) << 2 << OP_CHECKMULTISIGVERIFY; - CScript scriptPubKey = GetScriptForDestination(CScriptID(redeemScript)); + CScript scriptPubKey = GetScriptForDestination(ScriptHash(redeemScript)); CScript scriptSig = CScript() << OP_0 << OP_0 << ToByteVector(redeemScript); BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig, CScriptWitness()); @@ -193,7 +193,7 @@ BOOST_AUTO_TEST_CASE(GetTxSigOpCost) { CScript p2pk = CScript() << ToByteVector(pubkey) << OP_CHECKSIG; CScript scriptSig = GetScriptForWitness(p2pk); - CScript scriptPubKey = GetScriptForDestination(CScriptID(scriptSig)); + CScript scriptPubKey = GetScriptForDestination(ScriptHash(scriptSig)); scriptSig = CScript() << ToByteVector(scriptSig); CScriptWitness scriptWitness; scriptWitness.stack.push_back(std::vector(0)); @@ -224,7 +224,7 @@ BOOST_AUTO_TEST_CASE(GetTxSigOpCost) { CScript witnessScript = CScript() << 1 << ToByteVector(pubkey) << ToByteVector(pubkey) << 2 << OP_CHECKMULTISIGVERIFY; CScript redeemScript = GetScriptForWitness(witnessScript); - CScript scriptPubKey = GetScriptForDestination(CScriptID(redeemScript)); + CScript scriptPubKey = GetScriptForDestination(ScriptHash(redeemScript)); CScript scriptSig = CScript() << ToByteVector(redeemScript); CScriptWitness scriptWitness; scriptWitness.stack.push_back(std::vector(0)); diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 9f4e723c013eb..13ee685e7df66 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -310,9 +310,9 @@ SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsViewCache& coinsRet) dummyTransactions[1].vout.resize(2); dummyTransactions[1].vout[0].nValue = 21*CENT; - dummyTransactions[1].vout[0].scriptPubKey = GetScriptForDestination(key[2].GetPubKey().GetID()); + dummyTransactions[1].vout[0].scriptPubKey = GetScriptForDestination(PKHash(key[2].GetPubKey())); dummyTransactions[1].vout[1].nValue = 22*CENT; - dummyTransactions[1].vout[1].scriptPubKey = GetScriptForDestination(key[3].GetPubKey().GetID()); + dummyTransactions[1].vout[1].scriptPubKey = GetScriptForDestination(PKHash(key[3].GetPubKey())); AddCoins(coinsRet, dummyTransactions[1], 0); return dummyTransactions; @@ -574,8 +574,8 @@ BOOST_AUTO_TEST_CASE(test_witness) CheckWithFlag(output1, input2, STANDARD_SCRIPT_VERIFY_FLAGS, false); // P2SH pay-to-compressed-pubkey. - CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(scriptPubkey1)), output1, input1); - CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(scriptPubkey2)), output2, input2); + CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(scriptPubkey1)), output1, input1); + CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(scriptPubkey2)), output2, input2); ReplaceRedeemScript(input2.vin[0].scriptSig, scriptPubkey1); CheckWithFlag(output1, input1, 0, true); CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true); @@ -599,8 +599,8 @@ BOOST_AUTO_TEST_CASE(test_witness) CheckWithFlag(output1, input2, STANDARD_SCRIPT_VERIFY_FLAGS, false); // P2SH witness pay-to-compressed-pubkey (v0). - CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(GetScriptForWitness(scriptPubkey1))), output1, input1); - CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(GetScriptForWitness(scriptPubkey2))), output2, input2); + CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(GetScriptForWitness(scriptPubkey1))), output1, input1); + CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(GetScriptForWitness(scriptPubkey2))), output2, input2); ReplaceRedeemScript(input2.vin[0].scriptSig, GetScriptForWitness(scriptPubkey1)); CheckWithFlag(output1, input1, 0, true); CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true); @@ -624,8 +624,8 @@ BOOST_AUTO_TEST_CASE(test_witness) CheckWithFlag(output1, input2, STANDARD_SCRIPT_VERIFY_FLAGS, false); // P2SH pay-to-uncompressed-pubkey. - CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(scriptPubkey1L)), output1, input1); - CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(scriptPubkey2L)), output2, input2); + CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(scriptPubkey1L)), output1, input1); + CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(scriptPubkey2L)), output2, input2); ReplaceRedeemScript(input2.vin[0].scriptSig, scriptPubkey1L); CheckWithFlag(output1, input1, 0, true); CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true); @@ -641,8 +641,8 @@ BOOST_AUTO_TEST_CASE(test_witness) CreateCreditAndSpend(keystore, GetScriptForWitness(scriptPubkey2L), output2, input2, false); // Signing disabled for P2SH witness pay-to-uncompressed-pubkey (v1). - CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(GetScriptForWitness(scriptPubkey1L))), output1, input1, false); - CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(GetScriptForWitness(scriptPubkey2L))), output2, input2, false); + CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(GetScriptForWitness(scriptPubkey1L))), output1, input1, false); + CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(GetScriptForWitness(scriptPubkey2L))), output2, input2, false); // Normal 2-of-2 multisig CreateCreditAndSpend(keystore, scriptMulti, output1, input1, false); @@ -655,10 +655,10 @@ BOOST_AUTO_TEST_CASE(test_witness) CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true); // P2SH 2-of-2 multisig - CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(scriptMulti)), output1, input1, false); + CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(scriptMulti)), output1, input1, false); CheckWithFlag(output1, input1, 0, true); CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, false); - CreateCreditAndSpend(keystore2, GetScriptForDestination(CScriptID(scriptMulti)), output2, input2, false); + CreateCreditAndSpend(keystore2, GetScriptForDestination(ScriptHash(scriptMulti)), output2, input2, false); CheckWithFlag(output2, input2, 0, true); CheckWithFlag(output2, input2, SCRIPT_VERIFY_P2SH, false); BOOST_CHECK(*output1 == *output2); @@ -679,10 +679,10 @@ BOOST_AUTO_TEST_CASE(test_witness) CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true); // P2SH witness 2-of-2 multisig - CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(GetScriptForWitness(scriptMulti))), output1, input1, false); + CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(GetScriptForWitness(scriptMulti))), output1, input1, false); CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true); CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false); - CreateCreditAndSpend(keystore2, GetScriptForDestination(CScriptID(GetScriptForWitness(scriptMulti))), output2, input2, false); + CreateCreditAndSpend(keystore2, GetScriptForDestination(ScriptHash(GetScriptForWitness(scriptMulti))), output2, input2, false); CheckWithFlag(output2, input2, SCRIPT_VERIFY_P2SH, true); CheckWithFlag(output2, input2, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false); BOOST_CHECK(*output1 == *output2); @@ -708,7 +708,7 @@ BOOST_AUTO_TEST_CASE(test_IsStandard) t.vout[0].nValue = 90*CENT; CKey key; key.MakeNewKey(true); - t.vout[0].scriptPubKey = GetScriptForDestination(key.GetPubKey().GetID()); + t.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey())); std::string reason; BOOST_CHECK(IsStandardTx(t, reason)); diff --git a/src/test/txindex_tests.cpp b/src/test/txindex_tests.cpp index 2a160b9988c75..0ad831599f00e 100644 --- a/src/test/txindex_tests.cpp +++ b/src/test/txindex_tests.cpp @@ -49,7 +49,7 @@ BOOST_FIXTURE_TEST_CASE(txindex_initial_sync, TestChain100Setup) // Check that new transactions in new blocks make it into the index. for (int i = 0; i < 10; i++) { - CScript coinbase_script_pub_key = GetScriptForDestination(coinbaseKey.GetPubKey().GetID()); + CScript coinbase_script_pub_key = GetScriptForDestination(PKHash(coinbaseKey.GetPubKey())); std::vector no_txns; const CBlock& block = CreateAndProcessBlock(no_txns, coinbase_script_pub_key); const CTransaction& txn = *block.vtx[0]; diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp index b043e9f03c2e5..8a1e4f4f916ab 100644 --- a/src/test/txvalidationcache_tests.cpp +++ b/src/test/txvalidationcache_tests.cpp @@ -151,8 +151,8 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup) } CScript p2pk_scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG; - CScript p2sh_scriptPubKey = GetScriptForDestination(CScriptID(p2pk_scriptPubKey)); - CScript p2pkh_scriptPubKey = GetScriptForDestination(coinbaseKey.GetPubKey().GetID()); + CScript p2sh_scriptPubKey = GetScriptForDestination(ScriptHash(p2pk_scriptPubKey)); + CScript p2pkh_scriptPubKey = GetScriptForDestination(PKHash(coinbaseKey.GetPubKey())); CScript p2wpkh_scriptPubKey = GetScriptForWitness(p2pkh_scriptPubKey); CBasicKeyStore keystore; diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index f19a1bb56958c..76fe4ada93d33 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -235,7 +235,7 @@ static void ImportScript(CWallet* const pwallet, const CScript& script, const st if (!pwallet->HaveCScript(id) && !pwallet->AddCScript(script)) { throw JSONRPCError(RPC_WALLET_ERROR, "Error adding p2sh redeemScript to wallet"); } - ImportAddress(pwallet, id, strLabel); + ImportAddress(pwallet, ScriptHash(id), strLabel); } else { CTxDestination destination; if (ExtractDestination(script, destination)) { @@ -573,7 +573,7 @@ UniValue importwallet(const JSONRPCRequest& request) assert(key.VerifyPubKey(pubkey)); CKeyID keyid = pubkey.GetID(); if (pwallet->HaveKey(keyid)) { - pwallet->WalletLogPrintf("Skipping import of %s (key already present)\n", EncodeDestination(keyid)); + pwallet->WalletLogPrintf("Skipping import of %s (key already present)\n", EncodeDestination(PKHash(keyid))); continue; } int64_t nTime = DecodeDumpTime(vstr[1]); @@ -591,14 +591,14 @@ UniValue importwallet(const JSONRPCRequest& request) fLabel = true; } } - pwallet->WalletLogPrintf("Importing %s...\n", EncodeDestination(keyid)); + pwallet->WalletLogPrintf("Importing %s...\n", EncodeDestination(PKHash(keyid))); if (!pwallet->AddKeyPubKey(key, pubkey)) { fGood = false; continue; } pwallet->mapKeyMetadata[keyid].nCreateTime = nTime; if (fLabel) - pwallet->SetAddressBook(keyid, strLabel, "receive"); + pwallet->SetAddressBook(PKHash(keyid), strLabel, "receive"); nTimeBegin = std::min(nTimeBegin, nTime); } else if(IsHex(vstr[0])) { std::vector vData(ParseHex(vstr[0])); @@ -735,8 +735,8 @@ UniValue dumpwallet(const JSONRPCRequest& request) // sort time/key pairs std::vector > vKeyBirth; for (const auto& entry : mapKeyBirth) { - if (const CKeyID* keyID = boost::get(&entry.first)) { // set and test - vKeyBirth.push_back(std::make_pair(entry.second, *keyID)); + if (const PKHash* keyID = boost::get(&entry.first)) { // set and test + vKeyBirth.push_back(std::make_pair(entry.second, CKeyID(*keyID))); } } mapKeyBirth.clear(); @@ -787,7 +787,7 @@ UniValue dumpwallet(const JSONRPCRequest& request) for (const CScriptID &scriptid : scripts) { CScript script; std::string create_time = "0"; - std::string address = EncodeDestination(scriptid); + std::string address = EncodeDestination(ScriptHash(scriptid)); // get birth times for scripts with metadata auto it = pwallet->m_script_metadata.find(scriptid); if (it != pwallet->m_script_metadata.end()) { @@ -900,7 +900,7 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con throw JSONRPCError(RPC_WALLET_ERROR, "Error adding p2sh redeemScript to wallet"); } - CScript redeemDestination = GetScriptForDestination(redeem_id); + CScript redeemDestination = GetScriptForDestination(ScriptHash(redeem_id)); if (::IsMine(*pwallet, redeemDestination) == ISMINE_SPENDABLE) { throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script"); @@ -933,7 +933,7 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con CKeyID vchAddress = pubkey.GetID(); pwallet->MarkDirty(); - pwallet->SetAddressBook(vchAddress, label, "receive"); + pwallet->SetAddressBook(PKHash(vchAddress), label, "receive"); if (pwallet->HaveKey(vchAddress)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Already have this key"); @@ -966,7 +966,7 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey is not a valid public key"); } - CTxDestination pubkey_dest = pubKey.GetID(); + CTxDestination pubkey_dest = PKHash(pubKey); // Consistency check. if (!(pubkey_dest == dest)) { @@ -1020,7 +1020,7 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con CPubKey pubKey = key.GetPubKey(); assert(key.VerifyPubKey(pubKey)); - CTxDestination pubkey_dest = pubKey.GetID(); + CTxDestination pubkey_dest = PKHash(pubKey); // Consistency check. if (!(pubkey_dest == dest)) { @@ -1029,7 +1029,7 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con CKeyID vchAddress = pubKey.GetID(); pwallet->MarkDirty(); - pwallet->SetAddressBook(vchAddress, label, "receive"); + pwallet->SetAddressBook(PKHash(vchAddress), label, "receive"); if (pwallet->HaveKey(vchAddress)) { throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script"); @@ -1293,7 +1293,7 @@ UniValue getwalletpakinfo(const JSONRPCRequest& request) ret.pushKV("bitcoin_descriptor", desc_str); ret.pushKV("liquid_pak", HexStr(pwallet->online_key)); - ret.pushKV("liquid_pak_address", EncodeDestination((pwallet->online_key.GetID()))); + ret.pushKV("liquid_pak_address", EncodeDestination(PKHash(pwallet->online_key))); UniValue address_list(UniValue::VARR); for (unsigned int i = 0; i < 3; i++) { diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index ea8673264e781..17a87b79f8513 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -533,13 +533,14 @@ static UniValue signmessage(const JSONRPCRequest& request) throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address"); } - const CKeyID *keyID = boost::get(&dest); - if (!keyID) { + const PKHash *pkhash = boost::get(&dest); + if (!pkhash) { throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key"); } CKey key; - if (!pwallet->GetKey(*keyID, key)) { + CKeyID keyID(*pkhash); + if (!pwallet->GetKey(keyID, key)) { throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available"); } @@ -1004,7 +1005,7 @@ class Witnessifier : public boost::static_visitor explicit Witnessifier(CWallet *_pwallet) : pwallet(_pwallet), already_witness(false) {} - bool operator()(const CKeyID &keyID) { + bool operator()(const PKHash &keyID) { if (pwallet) { CScript basescript = GetScriptForDestination(keyID); CScript witscript = GetScriptForWitness(basescript); @@ -1016,9 +1017,9 @@ class Witnessifier : public boost::static_visitor return false; } - bool operator()(const CScriptID &scriptID) { + bool operator()(const ScriptHash &scripthash) { CScript subscript; - if (pwallet && pwallet->GetCScript(scriptID, subscript)) { + if (pwallet && pwallet->GetCScript(CScriptID(scripthash), subscript)) { int witnessversion; std::vector witprog; if (subscript.IsWitnessProgram(witnessversion, witprog)) { @@ -1105,7 +1106,7 @@ static UniValue addwitnessaddress(const JSONRPCRequest& request) CScript witprogram = GetScriptForDestination(w.result); if (p2sh) { - w.result = CScriptID(witprogram); + w.result = ScriptHash(witprogram); } if (w.already_witness) { @@ -2836,7 +2837,7 @@ static UniValue listunspent(const JSONRPCRequest& request) } if (scriptPubKey.IsPayToScriptHash()) { - const CScriptID& hash = boost::get(address); + const CScriptID hash(GetScriptForDestination(address)); CScript redeemScript; if (pwallet->GetCScript(hash, redeemScript)) { entry.pushKV("redeemScript", HexStr(redeemScript.begin(), redeemScript.end())); @@ -3525,7 +3526,7 @@ class DescribeWalletAddressVisitor : public boost::static_visitor UniValue pubkeys(UniValue::VARR); for (size_t i = 1; i < solutions_data.size() - 1; ++i) { CPubKey key(solutions_data[i].begin(), solutions_data[i].end()); - if (include_addresses) a.push_back(EncodeDestination(key.GetID())); + if (include_addresses) a.push_back(EncodeDestination(PKHash(key))); pubkeys.push_back(HexStr(key.begin(), key.end())); } obj.pushKV("pubkeys", std::move(pubkeys)); @@ -3542,8 +3543,9 @@ class DescribeWalletAddressVisitor : public boost::static_visitor UniValue operator()(const CNoDestination& dest) const { return UniValue(UniValue::VOBJ); } - UniValue operator()(const CKeyID& keyID) const + UniValue operator()(const PKHash& pkhash) const { + CKeyID keyID(pkhash); UniValue obj(UniValue::VOBJ); CPubKey vchPubKey; if (pwallet && pwallet->GetPubKey(keyID, vchPubKey)) { @@ -3553,8 +3555,9 @@ class DescribeWalletAddressVisitor : public boost::static_visitor return obj; } - UniValue operator()(const CScriptID& scriptID) const + UniValue operator()(const ScriptHash& scripthash) const { + CScriptID scriptID(scripthash); UniValue obj(UniValue::VOBJ); CScript subscript; if (pwallet && pwallet->GetCScript(scriptID, subscript)) { @@ -4199,7 +4202,7 @@ UniValue getpeginaddress(const JSONRPCRequest& request) pwallet->AddCScript(destScript); //Call contracthashtool, get deposit address on mainchain. - CTxDestination destAddr(CScriptID(GetScriptForWitness(calculate_contract(Params().GetConsensus().fedpegScript, witProg)))); + CTxDestination destAddr(ScriptHash(GetScriptForWitness(calculate_contract(Params().GetConsensus().fedpegScript, witProg)))); UniValue fundinginfo(UniValue::VOBJ); @@ -4293,8 +4296,6 @@ UniValue initpegoutwallet(const JSONRPCRequest& request) } } - CKeyID online_key_id = online_pubkey.GetID(); - // Parse offline counter int counter = 0; if (request.params.size() > 1) { @@ -4389,7 +4390,7 @@ UniValue initpegoutwallet(const JSONRPCRequest& request) UniValue pak(UniValue::VOBJ); pak.pushKV("pakentry", "pak=" + HexStr(negatedpubkeybytes) + ":" + HexStr(online_pubkey)); pak.pushKV("liquid_pak", HexStr(online_pubkey)); - pak.pushKV("liquid_pak_address", EncodeDestination(online_key_id)); + pak.pushKV("liquid_pak_address", EncodeDestination(PKHash(online_pubkey))); pak.pushKV("address_lookahead", address_list); return pak; } @@ -4728,7 +4729,7 @@ unsigned int GetPeginTxnOutputIndex(const T_tx& txn, const CScript& witnessProgr { unsigned int nOut = 0; //Call contracthashtool - CScript mainchain_script = GetScriptForDestination(CScriptID(GetScriptForWitness(calculate_contract(Params().GetConsensus().fedpegScript, witnessProgram)))); + CScript mainchain_script = GetScriptForDestination(ScriptHash(GetScriptForWitness(calculate_contract(Params().GetConsensus().fedpegScript, witnessProgram)))); for (; nOut < txn.vout.size(); nOut++) if (txn.vout[nOut].scriptPubKey == mainchain_script) break; @@ -4871,19 +4872,17 @@ static UniValue createrawpegin(const JSONRPCRequest& request, T_tx_ref& txBTCRef CPubKey newKey; if (!pwallet->GetKeyFromPool(newKey)) throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); - CKeyID keyID = newKey.GetID(); + PKHash pkhash(newKey); - pwallet->SetAddressBook(keyID, "", "receive"); + pwallet->SetAddressBook(pkhash, "", "receive"); // One peg-in input, one wallet output and one fee output CMutableTransaction mtx; mtx.vin.push_back(CTxIn(COutPoint(txHashes[0], nOut), CScript(), ~(uint32_t)0)); // mark as peg-in input mtx.vin[0].m_is_pegin = true; - //TODO(rebase) CA - //mtx.vout.push_back(CTxOut(Params().GetConsensus().pegged_asset, value, GetScriptForDestination(CTxDestination(keyID)))); - //mtx.vout.push_back(CTxOut(Params().GetConsensus().pegged_asset, 0, CScript())); - mtx.vout.push_back(CTxOut(value, GetScriptForDestination(CTxDestination(keyID)))); + mtx.vout.push_back(CTxOut(Params().GetConsensus().pegged_asset, value, GetScriptForDestination(pkhash))); + mtx.vout.push_back(CTxOut(Params().GetConsensus().pegged_asset, 0, CScript())); // Construct pegin proof CScriptWitness pegin_witness; diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index 3a8e6f751ac38..44fe6df846fa5 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -255,7 +255,7 @@ BOOST_AUTO_TEST_CASE(ComputeTimeSmart) BOOST_AUTO_TEST_CASE(LoadReceiveRequests) { - CTxDestination dest = CKeyID(); + CTxDestination dest = PKHash(); LOCK(m_wallet.cs_wallet); m_wallet.AddDestData(dest, "misc", "val_misc"); m_wallet.AddDestData(dest, "rr0", "val_rr0"); @@ -326,7 +326,7 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup) list = wallet->ListCoins(); } BOOST_CHECK_EQUAL(list.size(), 1U); - BOOST_CHECK_EQUAL(boost::get(list.begin()->first).ToString(), coinbaseAddress); + BOOST_CHECK_EQUAL(boost::get(list.begin()->first).ToString(), coinbaseAddress); BOOST_CHECK_EQUAL(list.begin()->second.size(), 1U); // Check initial balance from one mature coinbase transaction. @@ -342,7 +342,7 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup) list = wallet->ListCoins(); } BOOST_CHECK_EQUAL(list.size(), 1U); - BOOST_CHECK_EQUAL(boost::get(list.begin()->first).ToString(), coinbaseAddress); + BOOST_CHECK_EQUAL(boost::get(list.begin()->first).ToString(), coinbaseAddress); BOOST_CHECK_EQUAL(list.begin()->second.size(), 2U); // Lock both coins. Confirm number of available coins drops to 0. @@ -371,7 +371,7 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup) list = wallet->ListCoins(); } BOOST_CHECK_EQUAL(list.size(), 1U); - BOOST_CHECK_EQUAL(boost::get(list.begin()->first).ToString(), coinbaseAddress); + BOOST_CHECK_EQUAL(boost::get(list.begin()->first).ToString(), coinbaseAddress); BOOST_CHECK_EQUAL(list.begin()->second.size(), 2U); } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 6a767156f9947..185b09f808a5f 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -133,13 +133,15 @@ class CAffectedKeysVisitor : public boost::static_visitor { } } - void operator()(const CKeyID &keyId) { + void operator()(const PKHash &pkhash) { + CKeyID keyId(pkhash); if (keystore.HaveKey(keyId)) vKeys.push_back(keyId); } - void operator()(const CScriptID &scriptId) { + void operator()(const ScriptHash &scripthash) { CScript script; + CScriptID scriptId(scripthash); if (keystore.GetCScript(scriptId, script)) Process(script); } @@ -276,7 +278,7 @@ bool CWallet::AddKeyPubKeyWithDB(WalletBatch &batch, const CKey& secret, const C // check if we need to remove from watch-only CScript script; - script = GetScriptForDestination(pubkey.GetID()); + script = GetScriptForDestination(PKHash(pubkey)); if (HaveWatchOnly(script)) { RemoveWatchOnly(script); } @@ -366,7 +368,7 @@ bool CWallet::LoadCScript(const CScript& redeemScript) * these. Do not add them to the wallet and warn. */ if (redeemScript.size() > MAX_SCRIPT_ELEMENT_SIZE) { - std::string strAddr = EncodeDestination(CScriptID(redeemScript)); + std::string strAddr = EncodeDestination(ScriptHash(redeemScript)); WalletLogPrintf("%s: Warning: This wallet contains a redeemScript of size %i which exceeds maximum size %i thus can never be redeemed. Do not use address %s.\n", __func__, redeemScript.size(), MAX_SCRIPT_ELEMENT_SIZE, strAddr); return true; } @@ -3651,7 +3653,7 @@ void CWallet::GetKeyBirthTimes(std::map &mapKeyBirth) c // get birth times for keys with metadata for (const auto& entry : mapKeyMetadata) { if (entry.second.nCreateTime) { - mapKeyBirth[entry.first] = entry.second.nCreateTime; + mapKeyBirth[PKHash(entry.first)] = entry.second.nCreateTime; } } @@ -3659,7 +3661,7 @@ void CWallet::GetKeyBirthTimes(std::map &mapKeyBirth) c CBlockIndex *pindexMax = chainActive[std::max(0, chainActive.Height() - 144)]; // the tip can be reorganized; use a 144-block safety margin std::map mapKeyFirstBlock; for (const CKeyID &keyid : GetKeys()) { - if (mapKeyBirth.count(keyid) == 0) + if (mapKeyBirth.count(PKHash(keyid)) == 0) mapKeyFirstBlock[keyid] = pindexMax; } @@ -3692,7 +3694,7 @@ void CWallet::GetKeyBirthTimes(std::map &mapKeyBirth) c // Extract block timestamps for those keys for (const auto& entry : mapKeyFirstBlock) - mapKeyBirth[entry.first] = entry.second->GetBlockTime() - TIMESTAMP_WINDOW; // block times can be 2h off + mapKeyBirth[PKHash(entry.first)] = entry.second->GetBlockTime() - TIMESTAMP_WINDOW; // block times can be 2h off } /** From 045e61569273769d78d7977aede1f762270624cd Mon Sep 17 00:00:00 2001 From: Steven Roose Date: Tue, 19 Mar 2019 20:28:28 +0000 Subject: [PATCH 07/58] Add CKey::ECDH --- src/key.cpp | 10 ++++++++++ src/key.h | 5 +++++ 2 files changed, 15 insertions(+) diff --git a/src/key.cpp b/src/key.cpp index 0a89ce36e0686..be1249cfc24e3 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -12,6 +12,7 @@ #include #include +#include static secp256k1_context* secp256k1_context_sign = nullptr; @@ -202,6 +203,15 @@ bool SigHasLowR(const secp256k1_ecdsa_signature* sig) return compact_sig[0] < 0x80; } +uint256 CKey::ECDH(const CPubKey& pubkey) const { + assert(fValid); + uint256 result; + secp256k1_pubkey pkey; + assert(secp256k1_ec_pubkey_parse(secp256k1_context_sign, &pkey, pubkey.begin(), pubkey.size())); + assert(secp256k1_ecdh(secp256k1_context_sign, result.begin(), &pkey, begin(), NULL, NULL)); + return result; +} + bool CKey::Sign(const uint256 &hash, std::vector& vchSig, bool grind, uint32_t test_case) const { if (!fValid) return false; diff --git a/src/key.h b/src/key.h index e054d4f4c77e5..3bc8e01737421 100644 --- a/src/key.h +++ b/src/key.h @@ -110,6 +110,11 @@ class CKey */ CPubKey GetPubKey() const; + /** + * Compute the ECDH exchange result using this private key and another public key. + */ + uint256 ECDH(const CPubKey& pubkey) const; + /** * Create a DER-serialized signature. * The test_case parameter tweaks the deterministic nonce. From 00fba7771c5aed926db718d707ae8f9eae98af3b Mon Sep 17 00:00:00 2001 From: Gregory Sanders Date: Fri, 15 Mar 2019 12:18:18 -0400 Subject: [PATCH 08/58] Add liquidv1 chainparams --- src/chainparams.cpp | 141 ++++++++++++++++++++++++++++++++++++++++ src/chainparamsbase.cpp | 1 + src/chainparamsbase.h | 1 + src/chainparamsseeds.h | 11 ++++ 4 files changed, 154 insertions(+) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 3a9533ff1a555..841951f75c034 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -601,6 +601,145 @@ class CCustomParams : public CRegTestParams { } }; +/** + * Liquid v1 + */ +class CLiquidV1Params : public CChainParams { +public: + CLiquidV1Params() + { + + strNetworkID = "liquidv1"; + consensus.nSubsidyHalvingInterval = 150; + consensus.BIP16Exception = uint256(); + consensus.BIP34Height = 0; + consensus.BIP34Hash = uint256(); + consensus.BIP65Height = 0; + consensus.BIP66Height = 0; + consensus.powLimit = uint256S("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks; + consensus.nPowTargetSpacing = 60; // Minute block assumption + consensus.fPowAllowMinDifficultyBlocks = true; + consensus.fPowNoRetargeting = true; + consensus.nRuleChangeActivationThreshold = 108; + consensus.nMinerConfirmationWindow = 144; + + consensus.nMinimumChainWork = uint256(); + consensus.defaultAssumeValid = uint256(); + + nPruneAfterHeight = 1000; + fDefaultConsistencyChecks = false; + fRequireStandard = true; + fMineBlocksOnDemand = false; + m_fallback_fee_enabled = false; // TODO Will this break stuff? + + bech32_hrp = "ex"; // ex(plicit) + blech32_hrp = "lq"; // l(i)q(uid) + parent_bech32_hrp = "bc"; + parent_blech32_hrp = "bc"; // Doesn't exist but... + + base58Prefixes[PUBKEY_ADDRESS] = std::vector(1, 57); + base58Prefixes[SCRIPT_ADDRESS] = std::vector(1, 39); + base58Prefixes[SECRET_KEY] = std::vector(1, 128); + base58Prefixes[BLINDED_ADDRESS]= std::vector(1,12); + + base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x88, 0xB2, 0x1E}; + base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x88, 0xAD, 0xE4}; + + base58Prefixes[PARENT_PUBKEY_ADDRESS] = std::vector(1,0); + base58Prefixes[PARENT_SCRIPT_ADDRESS] = std::vector(1,5); + + pchMessageStart[0] = 0xfa; + pchMessageStart[1] = 0xbf; + pchMessageStart[2] = 0xb5; + pchMessageStart[3] = 0xda; + + nDefaultPort = 7042; + + vSeeds.clear(); + vSeeds.emplace_back("seed.liquidnetwork.io"); + vFixedSeeds = std::vector(pnSeed6_liquidv1, pnSeed6_liquidv1 + ARRAYLEN(pnSeed6_liquidv1)); + + // + // ELEMENTS fields + + consensus.genesis_style = "elements"; // unused here but let's set it anyways + + // Block signing encumberance script, default of 51 aka OP_TRUE + std::vector sign_bytes = ParseHex("5b21026a2a106ec32c8a1e8052e5d02a7b0a150423dbd9b116fc48d46630ff6e6a05b92102791646a8b49c2740352b4495c118d876347bf47d0551c01c4332fdc2df526f1a2102888bda53a424466b0451627df22090143bbf7c060e9eacb1e38426f6b07f2ae12102aee8967150dee220f613de3b239320355a498808084a93eaf39a34dcd62024852102d46e9259d0a0bb2bcbc461a3e68f34adca27b8d08fbe985853992b4b104e27412102e9944e35e5750ab621e098145b8e6cf373c273b7c04747d1aa020be0af40ccd62102f9a9d4b10a6d6c56d8c955c547330c589bb45e774551d46d415e51cd9ad5116321033b421566c124dfde4db9defe4084b7aa4e7f36744758d92806b8f72c2e943309210353dcc6b4cf6ad28aceb7f7b2db92a4bf07ac42d357adf756f3eca790664314b621037f55980af0455e4fb55aad9b85a55068bb6dc4740ea87276dc693f4598db45fa210384001daa88dabd23db878dbb1ce5b4c2a5fa72c3113e3514bf602325d0c37b8e21039056d089f2fe72dbc0a14780b4635b0dc8a1b40b7a59106325dd1bc45cc70493210397ab8ea7b0bf85bc7fc56bb27bf85e75502e94e76a6781c409f3f2ec3d1122192103b00e3b5b77884bf3cae204c4b4eac003601da75f96982ffcb3dcb29c5ee419b92103c1f3c0874cfe34b8131af34699589aacec4093399739ae352e8a46f80a6f68375fae"); + consensus.signblockscript = CScript(sign_bytes.begin(), sign_bytes.end()); + consensus.max_block_signature_size = 12*74; // 11 signatures plus wiggle room + g_signed_blocks = true; + + g_con_blockheightinheader = true; + g_con_elementswitness = true; + + consensus.genesis_subsidy = 0; + + // All non-zero coinbase outputs must go to this scriptPubKey + std::vector man_bytes = ParseHex("76a914fc26751a5025129a2fd006c6fbfa598ddd67f7e188ac"); + consensus.mandatory_coinbase_destination = CScript(man_bytes.begin(), man_bytes.end()); // Blank script allows any coinbase destination + + // Custom chains connect coinbase outputs to db by default + consensus.connect_genesis_outputs = true; + + initialFreeCoins = 0; + + anyonecanspend_aremine = false; + + consensus.has_parent_chain = true; + + enforce_pak = true; + + multi_data_permitted = true; + + parentGenesisBlockHash = uint256S("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"); + const bool parent_genesis_is_null = parentGenesisBlockHash == uint256(); + assert(consensus.has_parent_chain != parent_genesis_is_null); + consensus.parentChainPowLimit = uint256S("0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffff"); + consensus.parent_chain_signblockscript = CScript(); // It has PoW + consensus.pegin_min_depth = 100; + + const CScript default_script(CScript() << OP_TRUE); + consensus.fedpegScript = StrHexToScriptWithDefault("745c87635b21020e0338c96a8870479f2396c373cc7696ba124e8635d41b0ea581112b678172612102675333a4e4b8fb51d9d4e22fa5a8eaced3fdac8a8cbf9be8c030f75712e6af992102896807d54bc55c24981f24a453c60ad3e8993d693732288068a23df3d9f50d4821029e51a5ef5db3137051de8323b001749932f2ff0d34c82e96a2c2461de96ae56c2102a4e1a9638d46923272c266631d94d36bdb03a64ee0e14c7518e49d2f29bc40102102f8a00b269f8c5e59c67d36db3cdc11b11b21f64b4bffb2815e9100d9aa8daf072103079e252e85abffd3c401a69b087e590a9b86f33f574f08129ccbd3521ecf516b2103111cf405b627e22135b3b3733a4a34aa5723fb0f58379a16d32861bf576b0ec2210318f331b3e5d38156da6633b31929c5b220349859cc9ca3d33fb4e68aa08401742103230dae6b4ac93480aeab26d000841298e3b8f6157028e47b0897c1e025165de121035abff4281ff00660f99ab27bb53e6b33689c2cd8dcd364bc3c90ca5aea0d71a62103bd45cddfacf2083b14310ae4a84e25de61e451637346325222747b157446614c2103cc297026b06c71cbfa52089149157b5ff23de027ac5ab781800a578192d175462103d3bde5d63bdb3a6379b461be64dad45eabff42f758543a9645afd42f6d4248282103ed1e8d5109c9ed66f7941bc53cc71137baa76d50d274bda8d5e8ffbd6e61fe9a5f6702c00fb275522103aab896d53a8e7d6433137bbba940f9c521e085dd07e60994579b64a6d992cf79210291b7d0b1b692f8f524516ed950872e5da10fb1b808b5a526dedc6fed1cf29807210386aa9372fbab374593466bc5451dc59954e90787f08060964d95c87ef34ca5bb5368ae", default_script); + + + // Calculate pegged Bitcoin asset + std::vector commit = CommitToArguments(consensus, strNetworkID); + uint256 entropy; + GenerateAssetEntropy(entropy, COutPoint(uint256(commit), 0), parentGenesisBlockHash); + + // Elements serialization uses derivation, bitcoin serialization uses 0x00 + if (g_con_elementswitness) { + CalculateAsset(consensus.pegged_asset, entropy); + } else { + assert(consensus.pegged_asset == CAsset()); + } + + consensus.parent_pegged_asset.SetHex("0x00"); // No parent pegged asset + initial_reissuance_tokens = 0; + + consensus.subsidy_asset = consensus.pegged_asset; + + // CSV always active + consensus.vDeployments[Consensus::DEPLOYMENT_CSV].bit = 0; + consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE; + consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; + // Segwit, likewise + consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].bit = 1; + consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE; + consensus.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; + + + // Finally, create genesis block + genesis = CreateGenesisBlock(consensus, CScript(commit), CScript(OP_RETURN), 1296688602, 2, 0x207fffff, 1, 0); + consensus.hashGenesisBlock = genesis.GetHash(); + assert(consensus.hashGenesisBlock.GetHex() == "1466275836220db2944ca059a3a10ef6fd2ea684b0688d2c379296888a206003"); + } + +}; + + static std::unique_ptr globalChainParams; const CChainParams &Params() { @@ -617,6 +756,8 @@ std::unique_ptr CreateChainParams(const std::string& chain) return std::unique_ptr(new CTestNetParams()); else if (chain == CBaseChainParams::REGTEST) return std::unique_ptr(new CRegTestParams(gArgs)); + else if (chain == CBaseChainParams::LIQUID1) + return std::unique_ptr(new CLiquidV1Params()); return std::unique_ptr(new CCustomParams(chain, gArgs)); } diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp index 9f856be51f794..26510ea95f46d 100644 --- a/src/chainparamsbase.cpp +++ b/src/chainparamsbase.cpp @@ -14,6 +14,7 @@ const std::string CBaseChainParams::MAIN = "main"; const std::string CBaseChainParams::TESTNET = "test"; const std::string CBaseChainParams::REGTEST = "regtest"; +const std::string CBaseChainParams::LIQUID1 = "liquidv1"; void SetupChainParamsBaseOptions() { diff --git a/src/chainparamsbase.h b/src/chainparamsbase.h index 4d3617eb6e7fa..273128178f82c 100644 --- a/src/chainparamsbase.h +++ b/src/chainparamsbase.h @@ -20,6 +20,7 @@ class CBaseChainParams static const std::string MAIN; static const std::string TESTNET; static const std::string REGTEST; + static const std::string LIQUID1; const std::string& DataDir() const { return strDataDir; } int RPCPort() const { return nRPCPort; } diff --git a/src/chainparamsseeds.h b/src/chainparamsseeds.h index 4b982a4715136..6bc7f85d9c1d6 100644 --- a/src/chainparamsseeds.h +++ b/src/chainparamsseeds.h @@ -1287,4 +1287,15 @@ static SeedSpec6 pnSeed6_test[] = { {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x47,0xb1,0xe4,0x55,0xd1,0xb0,0x14,0x3f,0xb6,0xdb}, 18333}, {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xa0,0x60,0x9e,0x46,0x54,0xdb,0x61,0x3b,0xb2,0x6f}, 18333} }; + +static SeedSpec6 pnSeed6_liquidv1[] = { + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x23,0xc4,0x10,0xfe}, 7042}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x23,0xed,0xb0,0x3f}, 7042}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x23,0xed,0x51,0x0e}, 7042}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x23,0xed,0x93,0x15}, 7042}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x23,0xe3,0x5f,0x6d}, 7042}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x23,0xe7,0x8d,0xad}, 7042}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x68,0xc4,0x30,0xb8}, 7042}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x23,0xe7,0x0a,0x93}, 7042} +}; #endif // BITCOIN_CHAINPARAMSSEEDS_H From 17a22f1495f680536dbc50a61a27132d4f9239cf Mon Sep 17 00:00:00 2001 From: Steven Roose Date: Fri, 15 Mar 2019 18:05:43 +0000 Subject: [PATCH 09/58] Add support for blinded addresses --- src/chainparams.cpp | 2 + src/chainparams.h | 1 + src/key_io.cpp | 44 ++++++++++++++++++++++ src/rpc/misc.cpp | 83 +++++++++++++++++++++++++++++++++++++++++ src/script/standard.cpp | 11 ++++++ src/script/standard.h | 13 +++++++ 6 files changed, 154 insertions(+) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 841951f75c034..48a4d98a04b2a 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -566,6 +566,8 @@ class CCustomParams : public CRegTestParams { base58Prefixes[PARENT_SCRIPT_ADDRESS] = std::vector(1, args.GetArg("-parentscriptprefix", 196)); parent_bech32_hrp = args.GetArg("-parent_bech32_hrp", "bcrt"); + base58Prefixes[BLINDED_ADDRESS] = std::vector(1, args.GetArg("-blindedprefix", 4)); + // END ELEMENTS fields // CSV always active by default, unlike regtest diff --git a/src/chainparams.h b/src/chainparams.h index 8a9e760f8b058..1b9e14668ce99 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -54,6 +54,7 @@ class CChainParams EXT_PUBLIC_KEY, EXT_SECRET_KEY, // ELEMENTS + BLINDED_ADDRESS, PARENT_PUBKEY_ADDRESS, PARENT_SCRIPT_ADDRESS, diff --git a/src/key_io.cpp b/src/key_io.cpp index 97ac87c367c16..50e534b26c55d 100644 --- a/src/key_io.cpp +++ b/src/key_io.cpp @@ -31,6 +31,17 @@ class DestinationEncoder : public boost::static_visitor std::string operator()(const PKHash& id) const { + if (id.blinding_pubkey.IsFullyValid()) { + assert(!for_parent); + std::vector data = m_params.Base58Prefix(CChainParams::BLINDED_ADDRESS); + // Blinded addresses have the actual address type prefix inside the payload. + std::vector prefix = m_params.Base58Prefix(CChainParams::PUBKEY_ADDRESS); + data.insert(data.end(), prefix.begin(), prefix.end()); + data.insert(data.end(), id.blinding_pubkey.begin(), id.blinding_pubkey.end()); + data.insert(data.end(), id.begin(), id.end()); + return EncodeBase58Check(data); + } + CChainParams::Base58Type type = for_parent ? CChainParams::PARENT_PUBKEY_ADDRESS : CChainParams::PUBKEY_ADDRESS; std::vector data = m_params.Base58Prefix(type); data.insert(data.end(), id.begin(), id.end()); @@ -39,6 +50,17 @@ class DestinationEncoder : public boost::static_visitor std::string operator()(const ScriptHash& id) const { + if (id.blinding_pubkey.IsFullyValid()) { + assert(!for_parent); + std::vector data = m_params.Base58Prefix(CChainParams::BLINDED_ADDRESS); + // Blinded addresses have the actual address type prefix inside the payload. + std::vector prefix = m_params.Base58Prefix(CChainParams::SCRIPT_ADDRESS); + data.insert(data.end(), prefix.begin(), prefix.end()); + data.insert(data.end(), id.blinding_pubkey.begin(), id.blinding_pubkey.end()); + data.insert(data.end(), id.begin(), id.end()); + return EncodeBase58Check(data); + } + CChainParams::Base58Type type = for_parent ? CChainParams::PARENT_SCRIPT_ADDRESS : CChainParams::SCRIPT_ADDRESS; std::vector data = m_params.Base58Prefix(type); data.insert(data.end(), id.begin(), id.end()); @@ -82,17 +104,31 @@ class DestinationEncoder : public boost::static_visitor CTxDestination DecodeDestination(const std::string& str, const CChainParams& params, const bool for_parent) { std::vector data; + size_t pk_size = CPubKey::COMPRESSED_PUBLIC_KEY_SIZE; uint160 hash; if (DecodeBase58Check(str, data)) { // base58-encoded Bitcoin addresses. // Public-key-hash-addresses have version 0 (or 111 testnet). // The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key. + + // Blinded addresses have two prefixes: first the blinded one, then the traditional one. + const std::vector& blinded_prefix = params.Base58Prefix(CChainParams::BLINDED_ADDRESS); + CChainParams::Base58Type type_pkh = for_parent ? CChainParams::PARENT_PUBKEY_ADDRESS : CChainParams::PUBKEY_ADDRESS; const std::vector& pubkey_prefix = params.Base58Prefix(type_pkh); if (data.size() == hash.size() + pubkey_prefix.size() && std::equal(pubkey_prefix.begin(), pubkey_prefix.end(), data.begin())) { std::copy(data.begin() + pubkey_prefix.size(), data.end(), hash.begin()); return PKHash(hash); + } else if (data.size() == hash.size() + blinded_prefix.size() + pubkey_prefix.size() + pk_size && + std::equal(blinded_prefix.begin(), blinded_prefix.end(), data.begin()) && + std::equal(pubkey_prefix.begin(), pubkey_prefix.end(), data.begin() + blinded_prefix.size())) { + auto payload_start = data.begin() + blinded_prefix.size() + pubkey_prefix.size(); + CPubKey pubkey; + pubkey.Set(payload_start, payload_start + pk_size); + std::copy(payload_start + pk_size, data.end(), hash.begin()); + return PKHash(hash, pubkey); } + // Script-hash-addresses have version 5 (or 196 testnet). // The data vector contains RIPEMD160(SHA256(cscript)), where cscript is the serialized redemption script. CChainParams::Base58Type type_sh = for_parent ? CChainParams::PARENT_SCRIPT_ADDRESS : CChainParams::SCRIPT_ADDRESS; @@ -100,6 +136,14 @@ CTxDestination DecodeDestination(const std::string& str, const CChainParams& par if (data.size() == hash.size() + script_prefix.size() && std::equal(script_prefix.begin(), script_prefix.end(), data.begin())) { std::copy(data.begin() + script_prefix.size(), data.end(), hash.begin()); return ScriptHash(hash); + } else if (data.size() == hash.size() + blinded_prefix.size() + pubkey_prefix.size() + pk_size && + std::equal(blinded_prefix.begin(), blinded_prefix.end(), data.begin()) && + std::equal(script_prefix.begin(), script_prefix.end(), data.begin() + blinded_prefix.size())) { + auto payload_start = data.begin() + blinded_prefix.size() + script_prefix.size(); + CPubKey pubkey; + pubkey.Set(payload_start, payload_start + pk_size); + std::copy(payload_start + pk_size, data.end(), hash.begin()); + return ScriptHash(hash, pubkey); } } data.clear(); diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index ded78f08797e0..5be2b342fb706 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -543,6 +543,88 @@ UniValue calcfastmerkleroot(const JSONRPCRequest& request) } +class BlindingPubkeyAdderVisitor : public boost::static_visitor<> +{ +public: + CPubKey blind_key; + explicit BlindingPubkeyAdderVisitor(const CPubKey& blind_key_in) : blind_key(blind_key_in) {} + + void operator()(const CNoDestination& dest) const {} + + void operator()(PKHash& keyID) const + { + keyID.blinding_pubkey = blind_key; + } + + void operator()(ScriptHash& scriptID) const + { + scriptID.blinding_pubkey = blind_key; + } + + void operator()(WitnessV0KeyHash& id) const + { + id.blinding_pubkey = blind_key; + } + + void operator()(WitnessV0ScriptHash& id) const + { + id.blinding_pubkey = blind_key; + } + + void operator()(WitnessUnknown& id) const + { + id.blinding_pubkey = blind_key; + } + + void operator()(const NullData& id) const {} +}; + + +UniValue createblindedaddress(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() != 2) + { + throw std::runtime_error( + "createblindedaddress address blinding_key\n" + "\nCreates a blinded address using the provided blinding key.\n" + "\nArguments:\n" + "1. \"address\" (string, required) The unblinded address to be blinded.\n" + "2. \"blinding_key\" (string, required) The blinding public key. This can be obtained for a given address using `validateaddress`.\n" + "\nResult:\n" + "\"blinded_address\" (string) The blinded address.\n" + "\nExamples:\n" + "\nCreate a multisig address from 2 addresses\n" + + HelpExampleCli("createblindedaddress", "HEZk3iQi1jC49bxUriTtynnXgWWWdAYx16 ec09811118b6febfa5ebe68642e5091c418fbace07e655da26b4a845a691fc2d") + + "\nAs a json rpc call\n" + + HelpExampleRpc("createblindedaddress", "HEZk3iQi1jC49bxUriTtynnXgWWWdAYx16, ec09811118b6febfa5ebe68642e5091c418fbace07e655da26b4a845a691fc2d") + ); + } + + CTxDestination address = DecodeDestination(request.params[0].get_str()); + if (!IsValidDestination(address)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address or script"); + } + if (IsBlindDestination(address)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Not an unblinded address"); + } + + if (!IsHex(request.params[1].get_str())) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid hexadecimal for key"); + } + std::vector keydata = ParseHex(request.params[1].get_str()); + if (keydata.size() != 33) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid hexadecimal key length, must be length 66."); + } + + CPubKey key; + key.Set(keydata.begin(), keydata.end()); + + // Append blinding key and return + boost::apply_visitor(BlindingPubkeyAdderVisitor(key), address); + return EncodeDestination(address); +} + + // END ELEMENTS CALLS // @@ -560,6 +642,7 @@ static const CRPCCommand commands[] = // ELEMENTS: { "util", "getpakinfo", &getpakinfo, {}}, { "util", "tweakfedpegscript", &tweakfedpegscript, {"claim_script"} }, + { "util", "createblindedaddress", &createblindedaddress, {"address", "blinding_key"}}, { "hidden", "calcfastmerkleroot", &calcfastmerkleroot, {"leaves"} }, /* Not shown in help */ diff --git a/src/script/standard.cpp b/src/script/standard.cpp index afedcc240b6a5..3ac8f1f4fd4f1 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -22,14 +22,25 @@ unsigned nMaxDatacarrierBytes = MAX_OP_RETURN_RELAY; CScriptID::CScriptID(const CScript& in) : uint160(Hash160(in.begin(), in.end())) {} ScriptHash::ScriptHash(const CScript& in) : uint160(Hash160(in.begin(), in.end())) {} +ScriptHash::ScriptHash(const CScript& in, const CPubKey& blinding_pubkey_in) : uint160(Hash160(in.begin(), in.end())), blinding_pubkey(blinding_pubkey_in) {} +ScriptHash::ScriptHash(const uint160& hash, const CPubKey& blinding_pubkey_in) : uint160(hash), blinding_pubkey(blinding_pubkey_in) {} PKHash::PKHash(const CPubKey& pubkey) : uint160(pubkey.GetID()) {} +PKHash::PKHash(const CPubKey& pubkey, const CPubKey& blinding_pubkey_in) : uint160(pubkey.GetID()), blinding_pubkey(blinding_pubkey_in) {} +PKHash::PKHash(const uint160& hash, const CPubKey& blinding_pubkey_in) : uint160(hash), blinding_pubkey(blinding_pubkey_in) {} WitnessV0ScriptHash::WitnessV0ScriptHash(const CScript& in) { CSHA256().Write(in.data(), in.size()).Finalize(begin()); } +WitnessV0ScriptHash::WitnessV0ScriptHash(const CScript& in, const CPubKey& blinding_pubkey_in) +{ + CSHA256().Write(in.data(), in.size()).Finalize(begin()); + blinding_pubkey = blinding_pubkey_in; +} + + const char* GetTxnOutputType(txnouttype t) { switch (t) diff --git a/src/script/standard.h b/src/script/standard.h index 67374efbc0b9d..f5501ae300902 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -13,6 +13,8 @@ #include +#include // blinding_pubkey + static const bool DEFAULT_ACCEPT_DATACARRIER = true; class CKeyID; @@ -79,7 +81,10 @@ struct PKHash : public uint160 PKHash() : uint160() {} explicit PKHash(const uint160& hash) : uint160(hash) {} explicit PKHash(const CPubKey& pubkey); + explicit PKHash(const CPubKey& pubkey, const CPubKey& blinding_pubkey); + explicit PKHash(const uint160& hash, const CPubKey& blinding_pubkey); using uint160::uint160; + CPubKey blinding_pubkey; }; struct ScriptHash : public uint160 @@ -87,7 +92,10 @@ struct ScriptHash : public uint160 ScriptHash() : uint160() {} explicit ScriptHash(const uint160& hash) : uint160(hash) {} explicit ScriptHash(const CScript& script); + explicit ScriptHash(const CScript& script, const CPubKey& blinding_pubkey); + explicit ScriptHash(const uint160& hash, const CPubKey& blinding_pubkey); using uint160::uint160; + CPubKey blinding_pubkey; }; struct WitnessV0ScriptHash : public uint256 @@ -95,14 +103,18 @@ struct WitnessV0ScriptHash : public uint256 WitnessV0ScriptHash() : uint256() {} explicit WitnessV0ScriptHash(const uint256& hash) : uint256(hash) {} explicit WitnessV0ScriptHash(const CScript& script); + explicit WitnessV0ScriptHash(const CScript& script, const CPubKey& blinding_pubkey); using uint256::uint256; + CPubKey blinding_pubkey; }; struct WitnessV0KeyHash : public uint160 { WitnessV0KeyHash() : uint160() {} explicit WitnessV0KeyHash(const uint160& hash) : uint160(hash) {} + explicit WitnessV0KeyHash(const uint160& hash, const CPubKey& blinding_pubkey_in) : uint160(hash), blinding_pubkey(blinding_pubkey_in) {} using uint160::uint160; + CPubKey blinding_pubkey; }; //! CTxDestination subtype to encode any future Witness version @@ -111,6 +123,7 @@ struct WitnessUnknown unsigned int version; unsigned int length; unsigned char program[40]; + CPubKey blinding_pubkey; friend bool operator==(const WitnessUnknown& w1, const WitnessUnknown& w2) { if (w1.version != w2.version) return false; From 9cb2fa051fcbe0fe66f15e6b88d224d1935376f4 Mon Sep 17 00:00:00 2001 From: Gregory Sanders Date: Fri, 1 Mar 2019 09:49:03 -0500 Subject: [PATCH 10/58] Add blech32 python implementation with diff annotation with segwit_addr.py --- test/functional/test_framework/liquid_addr.py | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 test/functional/test_framework/liquid_addr.py diff --git a/test/functional/test_framework/liquid_addr.py b/test/functional/test_framework/liquid_addr.py new file mode 100644 index 0000000000000..c8ae77275b096 --- /dev/null +++ b/test/functional/test_framework/liquid_addr.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python3 +# Copyright (c) 2017 Pieter Wuille +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Reference implementation for Blech32 and segwit addresses.""" + + +CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" + +def blech32_polymod(values): + """Internal function that computes the blech32 checksum.""" + generator = [0x7d52fba40bd886, 0x5e8dbf1a03950c, 0x1c3a3c74072a18, 0x385d72fa0e5139, 0x7093e5a608865b] # new generators, 7 bytes + chk = 1 + for value in values: + top = chk >> 55 # 25->55 + chk = ((chk & 0x7fffffffffffff) << 5) ^^ value # 0x1ffffff->0x7fffffffffffff + for i in range(5): + chk ^= generator[i] if ((top >> i) & 1) else 0 + return chk + + +def blech32_hrp_expand(hrp): + """Expand the HRP into values for checksum computation.""" + return [ord(x) >> 5 for x in hrp] + [0] + [ord(x) & 31 for x in hrp] + + +def blech32_verify_checksum(hrp, data): + """Verify a checksum given HRP and converted data characters.""" + return blech32_polymod(blech32_hrp_expand(hrp) + data) == 1 + + +def blech32_create_checksum(hrp, data): + """Compute the checksum values given HRP and data.""" + values = blech32_hrp_expand(hrp) + data + polymod = blech32_polymod(values + [0]*12) ^^ 1 # 6->12 + return [(polymod >> 5 * (11 - i)) & 31 for i in range(12)] +# ^ 5 ^ 6 + +def blech32_encode(hrp, data): + """Compute a blech32 string given HRP and data values.""" + combined = data + blech32_create_checksum(hrp, data) + return hrp + '1' + ''.join([CHARSET[d] for d in combined]) + + +def blech32_decode(bech): + """Validate a blech32 string, and determine HRP and data.""" + if ((any(ord(x) < 33 or ord(x) > 126 for x in bech)) or + (bech.lower() != bech and bech.upper() != bech)): + return (None, None) + bech = bech.lower() + pos = bech.rfind('1') + if pos < 1 or pos + 13 > len(bech) or len(bech) > 1000: # 7->13 90->1000 + return (None, None) + if not all(x in CHARSET for x in bech[pos+1:]): + return (None, None) + hrp = bech[:pos] + data = [CHARSET.find(x) for x in bech[pos+1:]] + if not blech32_verify_checksum(hrp, data): + return (None, None) + return (hrp, data[:-12]) # 6->12 + + +def convertbits(data, frombits, tobits, pad=True): + """General power-of-2 base conversion.""" + acc = 0 + bits = 0 + ret = [] + maxv = (1 << tobits) - 1 + max_acc = (1 << (frombits + tobits - 1)) - 1 + for value in data: + if value < 0 or (value >> frombits): + return None + acc = ((acc << frombits) | value) & max_acc + bits += frombits + while bits >= tobits: + bits -= tobits + ret.append((acc >> bits) & maxv) + if pad: + if bits: + ret.append((acc << (tobits - bits)) & maxv) + elif bits >= frombits or ((acc << (tobits - bits)) & maxv): + return None + return ret + + +def decode(hrp, addr): + """Decode a segwit address.""" + hrpgot, data = blech32_decode(addr) + if hrpgot != hrp: + return (None, None) + decoded = convertbits(data[1:], 5, 8, False) + if decoded is None or len(decoded) < 2 or len(decoded) > 40: + return (None, None) + if data[0] > 16: + return (None, None) + if data[0] == 0 and len(decoded) != 20 and len(decoded) != 32: + return (None, None) + return (data[0], decoded) + + +def encode(hrp, witver, witprog): + """Encode a segwit address.""" + ret = blech32_encode(hrp, [witver] + convertbits(witprog, 8, 5)) + if decode(hrp, ret) == (None, None): + return None + return ret From 0f2878036427e9b332fea6ef93f76d9898fbfb72 Mon Sep 17 00:00:00 2001 From: Steven Roose Date: Tue, 19 Mar 2019 17:47:57 +0000 Subject: [PATCH 11/58] Add blech32 implementation and API --- src/Makefile.am | 2 + src/blech32.cpp | 199 ++++++++++++++++++++++++++++++++++++++++++++ src/blech32.h | 30 +++++++ src/chainparams.cpp | 5 ++ src/chainparams.h | 4 + src/init.cpp | 2 + src/key_io.cpp | 19 +++++ src/rpc/util.cpp | 137 ++++++++++++++++++++++++++++++ src/rpc/util.h | 3 + 9 files changed, 401 insertions(+) create mode 100644 src/blech32.cpp create mode 100644 src/blech32.h diff --git a/src/Makefile.am b/src/Makefile.am index a2d67258c9b5c..bc3ffd93b7b63 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -94,6 +94,7 @@ BITCOIN_CORE_H = \ addrman.h \ base58.h \ bech32.h \ + blech32.h \ bloom.h \ blockencodings.h \ blockfilter.h \ @@ -403,6 +404,7 @@ libbitcoin_common_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_common_a_SOURCES = \ base58.cpp \ bech32.cpp \ + blech32.cpp \ chainparams.cpp \ coins.cpp \ compressor.cpp \ diff --git a/src/blech32.cpp b/src/blech32.cpp new file mode 100644 index 0000000000000..8acd4f0a1f26a --- /dev/null +++ b/src/blech32.cpp @@ -0,0 +1,199 @@ +// Copyright (c) 2017 Pieter Wuille +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +/* + * IMPORTANT NOTE: Comments below may largely pertain for bech32, not blech32. + * Some of these magic constants have changes. + * See liquid_addr.py for compact difference from bech32 + * TODO: Update comments + */ + +namespace +{ + +typedef std::vector data; + +/** The Blech32 character set for encoding. */ +const char* CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"; + +/** The Blech32 character set for decoding. */ +const int8_t CHARSET_REV[128] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1, + -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1, + 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1, + -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1, + 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1 +}; + +/** Concatenate two byte arrays. */ +data Cat(data x, const data& y) +{ + x.insert(x.end(), y.begin(), y.end()); + return x; +} + +/** This function will compute what 6 5-bit values to XOR into the last 6 input values, in order to + * make the checksum 0. These 6 values are packed together in a single 30-bit integer. The higher + * bits correspond to earlier values. */ +uint32_t PolyMod(const data& v) +{ + // The input is interpreted as a list of coefficients of a polynomial over F = GF(32), with an + // implicit 1 in front. If the input is [v0,v1,v2,v3,v4], that polynomial is v(x) = + // 1*x^5 + v0*x^4 + v1*x^3 + v2*x^2 + v3*x + v4. The implicit 1 guarantees that + // [v0,v1,v2,...] has a distinct checksum from [0,v0,v1,v2,...]. + + // The output is a 30-bit integer whose 5-bit groups are the coefficients of the remainder of + // v(x) mod g(x), where g(x) is the Blech32 generator, + // x^6 + {29}x^5 + {22}x^4 + {20}x^3 + {21}x^2 + {29}x + {18}. g(x) is chosen in such a way + // that the resulting code is a BCH code, guaranteeing detection of up to 3 errors within a + // window of 1023 characters. Among the various possible BCH codes, one was selected to in + // fact guarantee detection of up to 4 errors within a window of 89 characters. + + // Note that the coefficients are elements of GF(32), here represented as decimal numbers + // between {}. In this finite field, addition is just XOR of the corresponding numbers. For + // example, {27} + {13} = {27 ^ 13} = {22}. Multiplication is more complicated, and requires + // treating the bits of values themselves as coefficients of a polynomial over a smaller field, + // GF(2), and multiplying those polynomials mod a^5 + a^3 + 1. For example, {5} * {26} = + // (a^2 + 1) * (a^4 + a^3 + a) = (a^4 + a^3 + a) * a^2 + (a^4 + a^3 + a) = a^6 + a^5 + a^4 + a + // = a^3 + 1 (mod a^5 + a^3 + 1) = {9}. + + // During the course of the loop below, `c` contains the bitpacked coefficients of the + // polynomial constructed from just the values of v that were processed so far, mod g(x). In + // the above example, `c` initially corresponds to 1 mod (x), and after processing 2 inputs of + // v, it corresponds to x^2 + v0*x + v1 mod g(x). As 1 mod g(x) = 1, that is the starting value + // for `c`. + uint64_t c = 1; + for (const auto v_i : v) { + // We want to update `c` to correspond to a polynomial with one extra term. If the initial + // value of `c` consists of the coefficients of c(x) = f(x) mod g(x), we modify it to + // correspond to c'(x) = (f(x) * x + v_i) mod g(x), where v_i is the next input to + // process. Simplifying: + // c'(x) = (f(x) * x + v_i) mod g(x) + // ((f(x) mod g(x)) * x + v_i) mod g(x) + // (c(x) * x + v_i) mod g(x) + // If c(x) = c0*x^5 + c1*x^4 + c2*x^3 + c3*x^2 + c4*x + c5, we want to compute + // c'(x) = (c0*x^5 + c1*x^4 + c2*x^3 + c3*x^2 + c4*x + c5) * x + v_i mod g(x) + // = c0*x^6 + c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i mod g(x) + // = c0*(x^6 mod g(x)) + c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i + // If we call (x^6 mod g(x)) = k(x), this can be written as + // c'(x) = (c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i) + c0*k(x) + + // First, determine the value of c0: + uint8_t c0 = c >> 55; // ELEMENTS: 25->55 + + // Then compute c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i: + c = ((c & 0x7fffffffffffff) << 5) ^ v_i; // ELEMENTS 0x1ffffff->0x7fffffffffffff + + // Finally, for each set bit n in c0, conditionally add {2^n}k(x): + if (c0 & 1) c ^= 0x7d52fba40bd886; // ELEMENTS + if (c0 & 2) c ^= 0x5e8dbf1a03950c; // ELEMENTS + if (c0 & 4) c ^= 0x1c3a3c74072a18; // ELEMENTS + if (c0 & 8) c ^= 0x385d72fa0e5139; // ELEMENTS + if (c0 & 16) c ^= 0x7093e5a608865b; // ELEMENTS + } + return c; +} + +/** Convert to lower case. */ +inline unsigned char LowerCase(unsigned char c) +{ + return (c >= 'A' && c <= 'Z') ? (c - 'A') + 'a' : c; +} + +/** Expand a HRP for use in checksum computation. */ +data ExpandHRP(const std::string& hrp) +{ + data ret; + ret.reserve(hrp.size() + 90); + ret.resize(hrp.size() * 2 + 1); + for (size_t i = 0; i < hrp.size(); ++i) { + unsigned char c = hrp[i]; + ret[i] = c >> 5; + ret[i + hrp.size() + 1] = c & 0x1f; + } + ret[hrp.size()] = 0; + return ret; +} + +/** Verify a checksum. */ +bool VerifyChecksum(const std::string& hrp, const data& values) +{ + // PolyMod computes what value to xor into the final values to make the checksum 0. However, + // if we required that the checksum was 0, it would be the case that appending a 0 to a valid + // list of values would result in a new valid list. For that reason, Blech32 requires the + // resulting checksum to be 1 instead. + return PolyMod(Cat(ExpandHRP(hrp), values)) == 1; +} + +/** Create a checksum. */ +data CreateChecksum(const std::string& hrp, const data& values) +{ + data enc = Cat(ExpandHRP(hrp), values); + enc.resize(enc.size() + 12); // ELEMENTS: Append 6->12 zeroes + uint32_t mod = PolyMod(enc) ^ 1; // Determine what to XOR into those 6 zeroes. + data ret(12); // ELEMENTS: 6->12 + for (size_t i = 0; i < 12; ++i) { // ELEMENTS: 6->12 + // Convert the 5-bit groups in mod to checksum values. + ret[i] = (mod >> (5 * (11 - i))) & 31; // ELEMENTS: 5->11 + } + return ret; +} + +} // namespace + +namespace blech32 +{ + +/** Encode a Blech32 string. */ +std::string Encode(const std::string& hrp, const data& values) { + data checksum = CreateChecksum(hrp, values); + data combined = Cat(values, checksum); + std::string ret = hrp + '1'; + ret.reserve(ret.size() + combined.size()); + for (const auto c : combined) { + ret += CHARSET[c]; + } + return ret; +} + +/** Decode a Blech32 string. */ +std::pair Decode(const std::string& str) { + bool lower = false, upper = false; + for (size_t i = 0; i < str.size(); ++i) { + unsigned char c = str[i]; + if (c >= 'a' && c <= 'z') lower = true; + else if (c >= 'A' && c <= 'Z') upper = true; + else if (c < 33 || c > 126) return {}; + } + if (lower && upper) return {}; + size_t pos = str.rfind('1'); + if (str.size() > 1000 || pos == str.npos || pos == 0 || pos + 13 > str.size()) { // ELEMENTS: 90->1000, 7->13 + return {}; + } + data values(str.size() - 1 - pos); + for (size_t i = 0; i < str.size() - 1 - pos; ++i) { + unsigned char c = str[i + pos + 1]; + int8_t rev = CHARSET_REV[c]; + + if (rev == -1) { + return {}; + } + values[i] = rev; + } + std::string hrp; + for (size_t i = 0; i < pos; ++i) { + hrp += LowerCase(str[i]); + } + if (!VerifyChecksum(hrp, values)) { + return {}; + } + return {hrp, data(values.begin(), values.end() - 12)}; +} + +} // namespace blech32 diff --git a/src/blech32.h b/src/blech32.h new file mode 100644 index 0000000000000..cfe907581e7a6 --- /dev/null +++ b/src/blech32.h @@ -0,0 +1,30 @@ +// Copyright (c) 2017 Pieter Wuille +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +// Bech32 is a string encoding format used in newer address types. +// The output consists of a human-readable part (alphanumeric), a +// separator character (1), and a base32 data section, the last +// 6 characters of which are a checksum. +// +// For more information, see BIP 173. + +#ifndef BITCOIN_BLECH32_H +#define BITCOIN_BLECH32_H + +#include +#include +#include + +namespace blech32 +{ + +/** Encode a Bech32 string. Returns the empty string in case of failure. */ +std::string Encode(const std::string& hrp, const std::vector& values); + +/** Decode a Bech32 string. Returns (hrp, data). Empty hrp means failure. */ +std::pair> Decode(const std::string& str); + +} // namespace blech32 + +#endif // BITCOIN_BLECH32_H diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 48a4d98a04b2a..c8772a59c5249 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -187,6 +187,7 @@ class CMainParams : public CChainParams { base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x88, 0xAD, 0xE4}; bech32_hrp = "bc"; + blech32_hrp = blech32_hrp; vFixedSeeds = std::vector(pnSeed6_main, pnSeed6_main + ARRAYLEN(pnSeed6_main)); @@ -298,6 +299,7 @@ class CTestNetParams : public CChainParams { base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94}; bech32_hrp = "tb"; + blech32_hrp = blech32_hrp; vFixedSeeds = std::vector(pnSeed6_test, pnSeed6_test + ARRAYLEN(pnSeed6_test)); @@ -407,6 +409,7 @@ class CRegTestParams : public CChainParams { base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94}; bech32_hrp = "bcrt"; + blech32_hrp = blech32_hrp; /* enable fallback fee on regtest */ m_fallback_fee_enabled = true; @@ -486,6 +489,7 @@ class CCustomParams : public CRegTestParams { m_fallback_fee_enabled = args.GetBoolArg("-fallback_fee_enabled", m_fallback_fee_enabled); bech32_hrp = args.GetArg("-bech32_hrp", bech32_hrp); + blech32_hrp = args.GetArg("-blech32_hrp", "el"); base58Prefixes[PUBKEY_ADDRESS] = std::vector(1, args.GetArg("-pubkeyprefix", 111)); base58Prefixes[SCRIPT_ADDRESS] = std::vector(1, args.GetArg("-scriptprefix", 196)); base58Prefixes[SECRET_KEY] = std::vector(1, args.GetArg("-secretprefix", 239)); @@ -565,6 +569,7 @@ class CCustomParams : public CRegTestParams { base58Prefixes[PARENT_PUBKEY_ADDRESS] = std::vector(1, args.GetArg("-parentpubkeyprefix", 111)); base58Prefixes[PARENT_SCRIPT_ADDRESS] = std::vector(1, args.GetArg("-parentscriptprefix", 196)); parent_bech32_hrp = args.GetArg("-parent_bech32_hrp", "bcrt"); + parent_blech32_hrp = args.GetArg("-parent_bech32_hrp", "bcrt"); base58Prefixes[BLINDED_ADDRESS] = std::vector(1, args.GetArg("-blindedprefix", 4)); diff --git a/src/chainparams.h b/src/chainparams.h index 1b9e14668ce99..31bd7eaad45e0 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -81,6 +81,7 @@ class CChainParams const std::vector& DNSSeeds() const { return vSeeds; } const std::vector& Base58Prefix(Base58Type type) const { return base58Prefixes[type]; } const std::string& Bech32HRP() const { return bech32_hrp; } + const std::string& Blech32HRP() const { return blech32_hrp; } const std::vector& FixedSeeds() const { return vFixedSeeds; } const CCheckpointData& Checkpoints() const { return checkpointData; } const ChainTxData& TxData() const { return chainTxData; } @@ -88,6 +89,7 @@ class CChainParams const uint256 ParentGenesisBlockHash() const { return parentGenesisBlockHash; } bool anyonecanspend_aremine; const std::string& ParentBech32HRP() const { return parent_bech32_hrp; } + const std::string& ParentBlech32HRP() const { return parent_blech32_hrp; } bool GetEnforcePak() const { return enforce_pak; } bool GetMultiDataPermitted() const { return multi_data_permitted; } @@ -101,6 +103,7 @@ class CChainParams std::vector vSeeds; std::vector base58Prefixes[MAX_BASE58_TYPES]; std::string bech32_hrp; + std::string blech32_hrp; std::string strNetworkID; CBlock genesis; CAmount initialFreeCoins; @@ -114,6 +117,7 @@ class CChainParams // ELEMENTS extra fields: uint256 parentGenesisBlockHash; std::string parent_bech32_hrp; + std::string parent_blech32_hrp; bool enforce_pak; bool multi_data_permitted; }; diff --git a/src/init.cpp b/src/init.cpp index 4e6d376a24e5b..382bdc7840ab4 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -508,6 +508,7 @@ void SetupServerArgs() gArgs.AddArg("-extpubkeyprefix", strprintf("The 4-byte prefix, in hex, of the chain's base58 extended public key encoding. (default: %s)", HexStr(defaultChainParams->Base58Prefix(CChainParams::EXT_PUBLIC_KEY))), false, OptionsCategory::CHAINPARAMS); gArgs.AddArg("-extprvkeyprefix", strprintf("The 4-byte prefix, in hex, of the chain's base58 extended private key encoding. (default: %s)", HexStr(defaultChainParams->Base58Prefix(CChainParams::EXT_SECRET_KEY))), false, OptionsCategory::CHAINPARAMS); gArgs.AddArg("-bech32_hrp", strprintf("The human-readable part of the chain's bech32 encoding. (default: %s)", defaultChainParams->Bech32HRP()), false, OptionsCategory::CHAINPARAMS); + gArgs.AddArg("-blech32_hrp", strprintf("The human-readable part of the chain's blech32 encoding. Used in confidential addresses.(default: %s)", defaultChainParams->Blech32HRP()), false, OptionsCategory::CHAINPARAMS); #if HAVE_DECL_DAEMON gArgs.AddArg("-daemon", "Run in the background as a daemon and accept commands", false, OptionsCategory::OPTIONS); @@ -534,6 +535,7 @@ void SetupServerArgs() gArgs.AddArg("-parentpubkeyprefix", strprintf("The byte prefix, in decimal, of the parent chain's base58 pubkey address. (default: %d)", 111), false, OptionsCategory::CHAINPARAMS); gArgs.AddArg("-parentscriptprefix", strprintf("The byte prefix, in decimal, of the parent chain's base58 script address. (default: %d)", 196), false, OptionsCategory::CHAINPARAMS); gArgs.AddArg("-parent_bech32_hrp", strprintf("The human-readable part of the parent chain's bech32 encoding. (default: %s)", "bc"), false, OptionsCategory::CHAINPARAMS); + gArgs.AddArg("-parent_blech32_hrp", strprintf("The human-readable part of the parent chain's blech32 encoding. (default: %s)", "bc"), false, OptionsCategory::CHAINPARAMS); // Add the hidden options gArgs.AddHiddenArgs(hidden_args); diff --git a/src/key_io.cpp b/src/key_io.cpp index 50e534b26c55d..c11cc76379243 100644 --- a/src/key_io.cpp +++ b/src/key_io.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include