From 4b1cc08f9f94a1e6e1ecba6b97f99b73fb513872 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sun, 23 May 2021 17:38:18 -0700 Subject: Make XOnlyPubKey act like byte container --- src/pubkey.h | 15 ++++++++++++++- src/uint256.h | 2 +- 2 files changed, 15 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/pubkey.h b/src/pubkey.h index 1af1187006..7d09faa9c1 100644 --- a/src/pubkey.h +++ b/src/pubkey.h @@ -222,6 +222,12 @@ private: uint256 m_keydata; public: + /** Construct an empty x-only pubkey. */ + XOnlyPubKey() = default; + + XOnlyPubKey(const XOnlyPubKey&) = default; + XOnlyPubKey& operator=(const XOnlyPubKey&) = default; + /** Construct an x-only pubkey from exactly 32 bytes. */ explicit XOnlyPubKey(Span bytes); @@ -234,7 +240,14 @@ public: const unsigned char& operator[](int pos) const { return *(m_keydata.begin() + pos); } const unsigned char* data() const { return m_keydata.begin(); } - size_t size() const { return m_keydata.size(); } + static constexpr size_t size() { return decltype(m_keydata)::size(); } + const unsigned char* begin() const { return m_keydata.begin(); } + const unsigned char* end() const { return m_keydata.end(); } + unsigned char* begin() { return m_keydata.begin(); } + unsigned char* end() { return m_keydata.end(); } + bool operator==(const XOnlyPubKey& other) const { return m_keydata == other.m_keydata; } + bool operator!=(const XOnlyPubKey& other) const { return m_keydata != other.m_keydata; } + bool operator<(const XOnlyPubKey& other) const { return m_keydata < other.m_keydata; } }; struct CExtPubKey { diff --git a/src/uint256.h b/src/uint256.h index fadf2320af..d4917d0eac 100644 --- a/src/uint256.h +++ b/src/uint256.h @@ -75,7 +75,7 @@ public: return &m_data[WIDTH]; } - unsigned int size() const + static constexpr unsigned int size() { return sizeof(m_data); } -- cgit v1.2.3 From 31df02a07091dbd5e0b315c8e5695e808f3a5505 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sun, 23 May 2021 18:00:18 -0700 Subject: Change Solver() output for WITNESS_V1_TAPROOT This is just a small simplification to prepare for the follow-up instruction of a CTxDestination variant for taproot outputs. In the old code, WITNESS_V1_TAPROOT and WITNESS_UNKNOWN both produced {version, program} as Solver() output. Change this so that WITNESS_V1_TAPROOT produces just {program}, like WITNESS_V0_* do. --- src/script/standard.cpp | 16 ++++++++++++---- src/test/script_standard_tests.cpp | 5 ++--- 2 files changed, 14 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/script/standard.cpp b/src/script/standard.cpp index 364fac3c84..540aa0f2d9 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -155,15 +155,14 @@ TxoutType Solver(const CScript& scriptPubKey, std::vector witnessprogram; if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) { if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_KEYHASH_SIZE) { - vSolutionsRet.push_back(witnessprogram); + vSolutionsRet.push_back(std::move(witnessprogram)); return TxoutType::WITNESS_V0_KEYHASH; } if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_SCRIPTHASH_SIZE) { - vSolutionsRet.push_back(witnessprogram); + vSolutionsRet.push_back(std::move(witnessprogram)); return TxoutType::WITNESS_V0_SCRIPTHASH; } if (witnessversion == 1 && witnessprogram.size() == WITNESS_V1_TAPROOT_SIZE) { - vSolutionsRet.push_back(std::vector{(unsigned char)witnessversion}); vSolutionsRet.push_back(std::move(witnessprogram)); return TxoutType::WITNESS_V1_TAPROOT; } @@ -242,8 +241,17 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet) addressRet = hash; return true; } - case TxoutType::WITNESS_UNKNOWN: case TxoutType::WITNESS_V1_TAPROOT: { + /* For now, no WitnessV1Taproot variant in CTxDestination exists, so map + * this to WitnessUnknown. */ + WitnessUnknown unk; + unk.version = 1; + std::copy(vSolutions[0].begin(), vSolutions[0].end(), unk.program); + unk.length = vSolutions[0].size(); + addressRet = unk; + return true; + } + case TxoutType::WITNESS_UNKNOWN: { WitnessUnknown unk; unk.version = vSolutions[0][0]; std::copy(vSolutions[1].begin(), vSolutions[1].end(), unk.program); diff --git a/src/test/script_standard_tests.cpp b/src/test/script_standard_tests.cpp index 44fbfa5970..950f3b970a 100644 --- a/src/test/script_standard_tests.cpp +++ b/src/test/script_standard_tests.cpp @@ -111,9 +111,8 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_success) s.clear(); s << OP_1 << ToByteVector(uint256::ZERO); BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::WITNESS_V1_TAPROOT); - BOOST_CHECK_EQUAL(solutions.size(), 2U); - BOOST_CHECK(solutions[0] == std::vector{1}); - BOOST_CHECK(solutions[1] == ToByteVector(uint256::ZERO)); + BOOST_CHECK_EQUAL(solutions.size(), 1U); + BOOST_CHECK(solutions[0] == ToByteVector(uint256::ZERO)); // TxoutType::WITNESS_UNKNOWN s.clear(); -- cgit v1.2.3 From 41839bdb89b3777ece2318877b9c7921ecca2472 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sun, 23 May 2021 18:23:58 -0700 Subject: Avoid dependence on CTxDestination index order --- src/script/descriptor.cpp | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp index b54ba204f0..19fad1009e 100644 --- a/src/script/descriptor.cpp +++ b/src/script/descriptor.cpp @@ -638,6 +638,19 @@ public: std::optional GetOutputType() const override { return std::nullopt; } }; +static std::optional OutputTypeFromDestination(const CTxDestination& dest) { + if (std::holds_alternative(dest) || + std::holds_alternative(dest)) { + return OutputType::LEGACY; + } + if (std::holds_alternative(dest) || + std::holds_alternative(dest) || + std::holds_alternative(dest)) { + return OutputType::BECH32; + } + return std::nullopt; +} + /** A parsed addr(A) descriptor. */ class AddressDescriptor final : public DescriptorImpl { @@ -651,15 +664,7 @@ public: std::optional GetOutputType() const override { - switch (m_destination.index()) { - case 1 /* PKHash */: - case 2 /* ScriptHash */: return OutputType::LEGACY; - case 3 /* WitnessV0ScriptHash */: - case 4 /* WitnessV0KeyHash */: - case 5 /* WitnessUnknown */: return OutputType::BECH32; - case 0 /* CNoDestination */: - default: return std::nullopt; - } + return OutputTypeFromDestination(m_destination); } bool IsSingleType() const final { return true; } }; @@ -679,15 +684,7 @@ public: { CTxDestination dest; ExtractDestination(m_script, dest); - switch (dest.index()) { - case 1 /* PKHash */: - case 2 /* ScriptHash */: return OutputType::LEGACY; - case 3 /* WitnessV0ScriptHash */: - case 4 /* WitnessV0KeyHash */: - case 5 /* WitnessUnknown */: return OutputType::BECH32; - case 0 /* CNoDestination */: - default: return std::nullopt; - } + return OutputTypeFromDestination(dest); } bool IsSingleType() const final { return true; } }; -- cgit v1.2.3 From a4bf84039c00b196b87f969acf6369d72c56ab46 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sun, 23 May 2021 17:38:40 -0700 Subject: Separate WitnessV1Taproot variant in CTxDestination --- src/key_io.cpp | 15 +++++++++++++++ src/rpc/util.cpp | 10 ++++++++++ src/script/descriptor.cpp | 1 + src/script/standard.cpp | 15 ++++++++------- src/script/standard.h | 13 ++++++++++--- src/wallet/rpcwallet.cpp | 1 + 6 files changed, 45 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/key_io.cpp b/src/key_io.cpp index dbcbfa1f29..615f4c9312 100644 --- a/src/key_io.cpp +++ b/src/key_io.cpp @@ -54,6 +54,14 @@ public: return bech32::Encode(bech32::Encoding::BECH32, m_params.Bech32HRP(), data); } + std::string operator()(const WitnessV1Taproot& tap) const + { + std::vector data = {1}; + data.reserve(53); + ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, tap.begin(), tap.end()); + return bech32::Encode(bech32::Encoding::BECH32M, m_params.Bech32HRP(), data); + } + std::string operator()(const WitnessUnknown& id) const { if (id.version < 1 || id.version > 16 || id.length < 2 || id.length > 40) { @@ -135,6 +143,13 @@ CTxDestination DecodeDestination(const std::string& str, const CChainParams& par return CNoDestination(); } + if (version == 1 && data.size() == WITNESS_V1_TAPROOT_SIZE) { + static_assert(WITNESS_V1_TAPROOT_SIZE == WitnessV1Taproot::size()); + WitnessV1Taproot tap; + std::copy(data.begin(), data.end(), tap.begin()); + return tap; + } + if (version > 16) { error_str = "Invalid Bech32 address witness version"; return CNoDestination(); diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp index 7cf25e0c82..2059628b54 100644 --- a/src/rpc/util.cpp +++ b/src/rpc/util.cpp @@ -301,6 +301,16 @@ public: return obj; } + UniValue operator()(const WitnessV1Taproot& tap) const + { + UniValue obj(UniValue::VOBJ); + obj.pushKV("isscript", true); + obj.pushKV("iswitness", true); + obj.pushKV("witness_version", 1); + obj.pushKV("witness_program", HexStr(tap)); + return obj; + } + UniValue operator()(const WitnessUnknown& id) const { UniValue obj(UniValue::VOBJ); diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp index 19fad1009e..d4b43932b9 100644 --- a/src/script/descriptor.cpp +++ b/src/script/descriptor.cpp @@ -645,6 +645,7 @@ static std::optional OutputTypeFromDestination(const CTxDestination& } if (std::holds_alternative(dest) || std::holds_alternative(dest) || + std::holds_alternative(dest) || std::holds_alternative(dest)) { return OutputType::BECH32; } diff --git a/src/script/standard.cpp b/src/script/standard.cpp index 540aa0f2d9..0a233b550a 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -242,13 +242,9 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet) return true; } case TxoutType::WITNESS_V1_TAPROOT: { - /* For now, no WitnessV1Taproot variant in CTxDestination exists, so map - * this to WitnessUnknown. */ - WitnessUnknown unk; - unk.version = 1; - std::copy(vSolutions[0].begin(), vSolutions[0].end(), unk.program); - unk.length = vSolutions[0].size(); - addressRet = unk; + WitnessV1Taproot tap; + std::copy(vSolutions[0].begin(), vSolutions[0].end(), tap.begin()); + addressRet = tap; return true; } case TxoutType::WITNESS_UNKNOWN: { @@ -337,6 +333,11 @@ public: return CScript() << OP_0 << ToByteVector(id); } + CScript operator()(const WitnessV1Taproot& tap) const + { + return CScript() << OP_1 << ToByteVector(tap); + } + CScript operator()(const WitnessUnknown& id) const { return CScript() << CScript::EncodeOP_N(id.version) << std::vector(id.program, id.program + id.length); diff --git a/src/script/standard.h b/src/script/standard.h index 12ab9979a8..a96b096fa7 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -6,6 +6,7 @@ #ifndef BITCOIN_SCRIPT_STANDARD_H #define BITCOIN_SCRIPT_STANDARD_H +#include #include