aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorlaanwj <126646+laanwj@users.noreply.github.com>2022-06-28 16:22:23 +0200
committerlaanwj <126646+laanwj@users.noreply.github.com>2022-06-28 16:44:03 +0200
commit5bf65ec66e5986c9188e3f6234f1c5c0f8dc7f90 (patch)
treed852b2f78562cfd8ae83d25af2bc1adb1ec2d3cf /src
parent55c9e2d790fa2e137ccd0d91e6cf3e2d0bff4813 (diff)
parentb80de4c505bf6377f2e476133dce6f2a803f1fa1 (diff)
downloadbitcoin-5bf65ec66e5986c9188e3f6234f1c5c0f8dc7f90.tar.xz
Merge bitcoin/bitcoin#22558: psbt: Taproot fields for PSBT
b80de4c505bf6377f2e476133dce6f2a803f1fa1 test: Test signing psbts without explicitly having scripts (Andrew Chow) a73b56888a1562d9fe46b7b1d2eea08802d98dfe wallet: also search taproot pubkeys in FillPSBT (Andrew Chow) 6cff82722f47b589a6a2cb264bfce20f4d45426a sign: Use sigdata taproot spenddata when signing (Andrew Chow) 5f12fe3f36bc8a9ad2733986d9493354265a525c psbt: Implement merge for Taproot fields (Andrew Chow) 1ece9a371510d887ed9612f2d219f8dfae278658 psbt, test: Check for taproot fields in taproot psbt test (Andrew Chow) 496a1bbe5e442ffc0948f626cca1b85a46ef58db taproot: Use pre-existing signatures if available (Andrew Chow) 0ad21e7c558da47f50d6b39974d0d2713e829d25 tests: Test taproot fields for PSBT (Andrew Chow) 103c6fd2791f7e73eeab7f3900fbedd5b550211d psbt: Remove non_witness_utxo for segwit v1+ (Andrew Chow) 7dccdd3157a87f55f5398316b98f909d6d6f1feb Implement decodepsbt for Taproot fields (Andrew Chow) ac7747585fb0629be502a089c9c9be876bd7107d Fill PSBT Taproot output data to/from SignatureData (Andrew Chow) 25b6ae46e7249a1b363ef4fb12375f368903c58e Assert that TaprootBuilder is Finalized during GetSpendData (Andrew Chow) 3ae5b6af21cf45b3da5e341e84f50e0717eaf589 Store TaprootBuilder in SigningProviders instead of TaprootSpendData (Andrew Chow) 4d1223e5123e60be93b5ad42ba0aee72d0612ea7 Fetch key origins for Taproot keys (Andrew Chow) 52e3f2f88ef1ac7062e905bf2d745b70463ee3e9 Fill PSBT Taproot input data to/from SignatureData (Andrew Chow) 05e2cc9a302ba7f14fc65ba255594c047cb44559 Implement de/ser of PSBT's Taproot fields (Andrew Chow) d557eff2add151781537978e27d6f1aff1b83ef7 Add serialization methods to XOnlyPubKey (Andrew Chow) d43923c38155fdadad3837d79c19a84c9d2d7f50 Add TaprootBuilder::GetTreeTuples (Andrew Chow) ce911204e42b8653cad791d1727aa625de9d0079 Move individual KeyOriginInfo de/ser to separate function (Andrew Chow) Pull request description: Implements the Taproot fields for PSBT described in [BIP 371](https://github.com/bitcoin/bips/blob/master/bip-0371.mediawiki). ACKs for top commit: laanwj: Code review ACK b80de4c505bf6377f2e476133dce6f2a803f1fa1 Tree-SHA512: 50b79bb44f353c9ec2ef4c98aac08a81eba560987e5264a5684caa370e9c4e7a8255c06747fc47749511be45b32d01492e015f92b82be8d22bc8bf192073bd26
Diffstat (limited to 'src')
-rw-r--r--src/psbt.cpp69
-rw-r--r--src/psbt.h288
-rw-r--r--src/pubkey.h3
-rw-r--r--src/rpc/rawtransaction.cpp169
-rw-r--r--src/script/descriptor.cpp2
-rw-r--r--src/script/sign.cpp26
-rw-r--r--src/script/sign.h2
-rw-r--r--src/script/signingprovider.cpp21
-rw-r--r--src/script/signingprovider.h5
-rw-r--r--src/script/standard.cpp17
-rw-r--r--src/script/standard.h2
-rw-r--r--src/wallet/scriptpubkeyman.cpp13
-rw-r--r--src/wallet/wallet.cpp29
13 files changed, 626 insertions, 20 deletions
diff --git a/src/psbt.cpp b/src/psbt.cpp
index c1c8a385cc..36fec74bc9 100644
--- a/src/psbt.cpp
+++ b/src/psbt.cpp
@@ -113,6 +113,24 @@ void PSBTInput::FillSignatureData(SignatureData& sigdata) const
for (const auto& key_pair : hd_keypaths) {
sigdata.misc_pubkeys.emplace(key_pair.first.GetID(), key_pair);
}
+ if (!m_tap_key_sig.empty()) {
+ sigdata.taproot_key_path_sig = m_tap_key_sig;
+ }
+ for (const auto& [pubkey_leaf, sig] : m_tap_script_sigs) {
+ sigdata.taproot_script_sigs.emplace(pubkey_leaf, sig);
+ }
+ if (!m_tap_internal_key.IsNull()) {
+ sigdata.tr_spenddata.internal_key = m_tap_internal_key;
+ }
+ if (!m_tap_merkle_root.IsNull()) {
+ sigdata.tr_spenddata.merkle_root = m_tap_merkle_root;
+ }
+ for (const auto& [leaf_script, control_block] : m_tap_scripts) {
+ sigdata.tr_spenddata.scripts.emplace(leaf_script, control_block);
+ }
+ for (const auto& [pubkey, leaf_origin] : m_tap_bip32_paths) {
+ sigdata.taproot_misc_pubkeys.emplace(pubkey, leaf_origin);
+ }
}
void PSBTInput::FromSignatureData(const SignatureData& sigdata)
@@ -142,13 +160,30 @@ void PSBTInput::FromSignatureData(const SignatureData& sigdata)
for (const auto& entry : sigdata.misc_pubkeys) {
hd_keypaths.emplace(entry.second);
}
+ if (!sigdata.taproot_key_path_sig.empty()) {
+ m_tap_key_sig = sigdata.taproot_key_path_sig;
+ }
+ for (const auto& [pubkey_leaf, sig] : sigdata.taproot_script_sigs) {
+ m_tap_script_sigs.emplace(pubkey_leaf, sig);
+ }
+ if (!sigdata.tr_spenddata.internal_key.IsNull()) {
+ m_tap_internal_key = sigdata.tr_spenddata.internal_key;
+ }
+ if (!sigdata.tr_spenddata.merkle_root.IsNull()) {
+ m_tap_merkle_root = sigdata.tr_spenddata.merkle_root;
+ }
+ for (const auto& [leaf_script, control_block] : sigdata.tr_spenddata.scripts) {
+ m_tap_scripts.emplace(leaf_script, control_block);
+ }
+ for (const auto& [pubkey, leaf_origin] : sigdata.taproot_misc_pubkeys) {
+ m_tap_bip32_paths.emplace(pubkey, leaf_origin);
+ }
}
void PSBTInput::Merge(const PSBTInput& input)
{
if (!non_witness_utxo && input.non_witness_utxo) non_witness_utxo = input.non_witness_utxo;
if (witness_utxo.IsNull() && !input.witness_utxo.IsNull()) {
- // TODO: For segwit v1, we will want to clear out the non-witness utxo when setting a witness one. For v0 and non-segwit, this is not safe
witness_utxo = input.witness_utxo;
}
@@ -159,11 +194,17 @@ void PSBTInput::Merge(const PSBTInput& input)
hash256_preimages.insert(input.hash256_preimages.begin(), input.hash256_preimages.end());
hd_keypaths.insert(input.hd_keypaths.begin(), input.hd_keypaths.end());
unknown.insert(input.unknown.begin(), input.unknown.end());
+ m_tap_script_sigs.insert(input.m_tap_script_sigs.begin(), input.m_tap_script_sigs.end());
+ m_tap_scripts.insert(input.m_tap_scripts.begin(), input.m_tap_scripts.end());
+ m_tap_bip32_paths.insert(input.m_tap_bip32_paths.begin(), input.m_tap_bip32_paths.end());
if (redeem_script.empty() && !input.redeem_script.empty()) redeem_script = input.redeem_script;
if (witness_script.empty() && !input.witness_script.empty()) witness_script = input.witness_script;
if (final_script_sig.empty() && !input.final_script_sig.empty()) final_script_sig = input.final_script_sig;
if (final_script_witness.IsNull() && !input.final_script_witness.IsNull()) final_script_witness = input.final_script_witness;
+ if (m_tap_key_sig.empty() && !input.m_tap_key_sig.empty()) m_tap_key_sig = input.m_tap_key_sig;
+ if (m_tap_internal_key.IsNull() && !input.m_tap_internal_key.IsNull()) m_tap_internal_key = input.m_tap_internal_key;
+ if (m_tap_merkle_root.IsNull() && !input.m_tap_merkle_root.IsNull()) m_tap_merkle_root = input.m_tap_merkle_root;
}
void PSBTOutput::FillSignatureData(SignatureData& sigdata) const
@@ -177,6 +218,15 @@ void PSBTOutput::FillSignatureData(SignatureData& sigdata) const
for (const auto& key_pair : hd_keypaths) {
sigdata.misc_pubkeys.emplace(key_pair.first.GetID(), key_pair);
}
+ if (m_tap_tree.has_value() && m_tap_internal_key.IsFullyValid()) {
+ TaprootSpendData spenddata = m_tap_tree->GetSpendData();
+
+ sigdata.tr_spenddata.internal_key = m_tap_internal_key;
+ sigdata.tr_spenddata.Merge(spenddata);
+ }
+ for (const auto& [pubkey, leaf_origin] : m_tap_bip32_paths) {
+ sigdata.taproot_misc_pubkeys.emplace(pubkey, leaf_origin);
+ }
}
void PSBTOutput::FromSignatureData(const SignatureData& sigdata)
@@ -190,6 +240,15 @@ void PSBTOutput::FromSignatureData(const SignatureData& sigdata)
for (const auto& entry : sigdata.misc_pubkeys) {
hd_keypaths.emplace(entry.second);
}
+ if (!sigdata.tr_spenddata.internal_key.IsNull()) {
+ m_tap_internal_key = sigdata.tr_spenddata.internal_key;
+ }
+ if (sigdata.tr_builder.has_value()) {
+ m_tap_tree = sigdata.tr_builder;
+ }
+ for (const auto& [pubkey, leaf_origin] : sigdata.taproot_misc_pubkeys) {
+ m_tap_bip32_paths.emplace(pubkey, leaf_origin);
+ }
}
bool PSBTOutput::IsNull() const
@@ -201,9 +260,12 @@ void PSBTOutput::Merge(const PSBTOutput& output)
{
hd_keypaths.insert(output.hd_keypaths.begin(), output.hd_keypaths.end());
unknown.insert(output.unknown.begin(), output.unknown.end());
+ m_tap_bip32_paths.insert(output.m_tap_bip32_paths.begin(), output.m_tap_bip32_paths.end());
if (redeem_script.empty() && !output.redeem_script.empty()) redeem_script = output.redeem_script;
if (witness_script.empty() && !output.witness_script.empty()) witness_script = output.witness_script;
+ if (m_tap_internal_key.IsNull() && !output.m_tap_internal_key.IsNull()) m_tap_internal_key = output.m_tap_internal_key;
+ if (m_tap_tree.has_value() && !output.m_tap_tree.has_value()) m_tap_tree = output.m_tap_tree;
}
bool PSBTInputSigned(const PSBTInput& input)
{
@@ -313,10 +375,11 @@ bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction&
input.FromSignatureData(sigdata);
// If we have a witness signature, put a witness UTXO.
- // TODO: For segwit v1, we should remove the non_witness_utxo
if (sigdata.witness) {
input.witness_utxo = utxo;
- // input.non_witness_utxo = nullptr;
+ // We can remove the non_witness_utxo if and only if there are no non-segwit or segwit v0
+ // inputs in this transaction. Since this requires inspecting the entire transaction, this
+ // is something for the caller to deal with (i.e. FillPSBT).
}
// Fill in the missing info
diff --git a/src/psbt.h b/src/psbt.h
index 4a6d41076f..a143a99988 100644
--- a/src/psbt.h
+++ b/src/psbt.h
@@ -40,12 +40,21 @@ static constexpr uint8_t PSBT_IN_RIPEMD160 = 0x0A;
static constexpr uint8_t PSBT_IN_SHA256 = 0x0B;
static constexpr uint8_t PSBT_IN_HASH160 = 0x0C;
static constexpr uint8_t PSBT_IN_HASH256 = 0x0D;
+static constexpr uint8_t PSBT_IN_TAP_KEY_SIG = 0x13;
+static constexpr uint8_t PSBT_IN_TAP_SCRIPT_SIG = 0x14;
+static constexpr uint8_t PSBT_IN_TAP_LEAF_SCRIPT = 0x15;
+static constexpr uint8_t PSBT_IN_TAP_BIP32_DERIVATION = 0x16;
+static constexpr uint8_t PSBT_IN_TAP_INTERNAL_KEY = 0x17;
+static constexpr uint8_t PSBT_IN_TAP_MERKLE_ROOT = 0x18;
static constexpr uint8_t PSBT_IN_PROPRIETARY = 0xFC;
// Output types
static constexpr uint8_t PSBT_OUT_REDEEMSCRIPT = 0x00;
static constexpr uint8_t PSBT_OUT_WITNESSSCRIPT = 0x01;
static constexpr uint8_t PSBT_OUT_BIP32_DERIVATION = 0x02;
+static constexpr uint8_t PSBT_OUT_TAP_INTERNAL_KEY = 0x05;
+static constexpr uint8_t PSBT_OUT_TAP_TREE = 0x06;
+static constexpr uint8_t PSBT_OUT_TAP_BIP32_DERIVATION = 0x07;
static constexpr uint8_t PSBT_OUT_PROPRIETARY = 0xFC;
// The separator is 0x00. Reading this in means that the unserializer can interpret it
@@ -97,22 +106,30 @@ void UnserializeFromVector(Stream& s, X&... args)
}
}
-// Deserialize an individual HD keypath to a stream
+// Deserialize bytes of given length from the stream as a KeyOriginInfo
template<typename Stream>
-void DeserializeHDKeypath(Stream& s, KeyOriginInfo& hd_keypath)
+KeyOriginInfo DeserializeKeyOrigin(Stream& s, uint64_t length)
{
// Read in key path
- uint64_t value_len = ReadCompactSize(s);
- if (value_len % 4 || value_len == 0) {
+ if (length % 4 || length == 0) {
throw std::ios_base::failure("Invalid length for HD key path");
}
+ KeyOriginInfo hd_keypath;
s >> hd_keypath.fingerprint;
- for (unsigned int i = 4; i < value_len; i += sizeof(uint32_t)) {
+ for (unsigned int i = 4; i < length; i += sizeof(uint32_t)) {
uint32_t index;
s >> index;
hd_keypath.path.push_back(index);
}
+ return hd_keypath;
+}
+
+// Deserialize a length prefixed KeyOriginInfo from a stream
+template<typename Stream>
+void DeserializeHDKeypath(Stream& s, KeyOriginInfo& hd_keypath)
+{
+ hd_keypath = DeserializeKeyOrigin(s, ReadCompactSize(s));
}
// Deserialize HD keypaths into a map
@@ -139,17 +156,24 @@ void DeserializeHDKeypaths(Stream& s, const std::vector<unsigned char>& key, std
hd_keypaths.emplace(pubkey, std::move(keypath));
}
-// Serialize an individual HD keypath to a stream
+// Serialize a KeyOriginInfo to a stream
template<typename Stream>
-void SerializeHDKeypath(Stream& s, KeyOriginInfo hd_keypath)
+void SerializeKeyOrigin(Stream& s, KeyOriginInfo hd_keypath)
{
- WriteCompactSize(s, (hd_keypath.path.size() + 1) * sizeof(uint32_t));
s << hd_keypath.fingerprint;
for (const auto& path : hd_keypath.path) {
s << path;
}
}
+// Serialize a length prefixed KeyOriginInfo to a stream
+template<typename Stream>
+void SerializeHDKeypath(Stream& s, KeyOriginInfo hd_keypath)
+{
+ WriteCompactSize(s, (hd_keypath.path.size() + 1) * sizeof(uint32_t));
+ SerializeKeyOrigin(s, hd_keypath);
+}
+
// Serialize HD keypaths to a stream from a map
template<typename Stream>
void SerializeHDKeypaths(Stream& s, const std::map<CPubKey, KeyOriginInfo>& hd_keypaths, CompactSizeWriter type)
@@ -178,6 +202,15 @@ struct PSBTInput
std::map<uint256, std::vector<unsigned char>> sha256_preimages;
std::map<uint160, std::vector<unsigned char>> hash160_preimages;
std::map<uint256, std::vector<unsigned char>> hash256_preimages;
+
+ // Taproot fields
+ std::vector<unsigned char> m_tap_key_sig;
+ std::map<std::pair<XOnlyPubKey, uint256>, std::vector<unsigned char>> m_tap_script_sigs;
+ std::map<std::pair<CScript, int>, std::set<std::vector<unsigned char>, ShortestVectorFirstComparator>> m_tap_scripts;
+ std::map<XOnlyPubKey, std::pair<std::set<uint256>, KeyOriginInfo>> m_tap_bip32_paths;
+ XOnlyPubKey m_tap_internal_key;
+ uint256 m_tap_merkle_root;
+
std::map<std::vector<unsigned char>, std::vector<unsigned char>> unknown;
std::set<PSBTProprietary> m_proprietary;
std::optional<int> sighash_type;
@@ -252,6 +285,53 @@ struct PSBTInput
SerializeToVector(s, CompactSizeWriter(PSBT_IN_HASH256), Span{hash});
s << preimage;
}
+
+ // Write taproot key sig
+ if (!m_tap_key_sig.empty()) {
+ SerializeToVector(s, PSBT_IN_TAP_KEY_SIG);
+ s << m_tap_key_sig;
+ }
+
+ // Write taproot script sigs
+ for (const auto& [pubkey_leaf, sig] : m_tap_script_sigs) {
+ const auto& [xonly, leaf_hash] = pubkey_leaf;
+ SerializeToVector(s, PSBT_IN_TAP_SCRIPT_SIG, xonly, leaf_hash);
+ s << sig;
+ }
+
+ // Write taproot leaf scripts
+ for (const auto& [leaf, control_blocks] : m_tap_scripts) {
+ const auto& [script, leaf_ver] = leaf;
+ for (const auto& control_block : control_blocks) {
+ SerializeToVector(s, PSBT_IN_TAP_LEAF_SCRIPT, Span{control_block});
+ std::vector<unsigned char> value_v(script.begin(), script.end());
+ value_v.push_back((uint8_t)leaf_ver);
+ s << value_v;
+ }
+ }
+
+ // Write taproot bip32 keypaths
+ for (const auto& [xonly, leaf_origin] : m_tap_bip32_paths) {
+ const auto& [leaf_hashes, origin] = leaf_origin;
+ SerializeToVector(s, PSBT_IN_TAP_BIP32_DERIVATION, xonly);
+ std::vector<unsigned char> value;
+ CVectorWriter s_value(s.GetType(), s.GetVersion(), value, 0);
+ s_value << leaf_hashes;
+ SerializeKeyOrigin(s_value, origin);
+ s << value;
+ }
+
+ // Write taproot internal key
+ if (!m_tap_internal_key.IsNull()) {
+ SerializeToVector(s, PSBT_IN_TAP_INTERNAL_KEY);
+ s << ToByteVector(m_tap_internal_key);
+ }
+
+ // Write taproot merkle root
+ if (!m_tap_merkle_root.IsNull()) {
+ SerializeToVector(s, PSBT_IN_TAP_MERKLE_ROOT);
+ SerializeToVector(s, m_tap_merkle_root);
+ }
}
// Write script sig
@@ -488,6 +568,103 @@ struct PSBTInput
hash256_preimages.emplace(hash, std::move(preimage));
break;
}
+ case PSBT_IN_TAP_KEY_SIG:
+ {
+ if (!key_lookup.emplace(key).second) {
+ throw std::ios_base::failure("Duplicate Key, input Taproot key signature already provided");
+ } else if (key.size() != 1) {
+ throw std::ios_base::failure("Input Taproot key signature key is more than one byte type");
+ }
+ s >> m_tap_key_sig;
+ if (m_tap_key_sig.size() < 64) {
+ throw std::ios_base::failure("Input Taproot key path signature is shorter than 64 bytes");
+ } else if (m_tap_key_sig.size() > 65) {
+ throw std::ios_base::failure("Input Taproot key path signature is longer than 65 bytes");
+ }
+ break;
+ }
+ case PSBT_IN_TAP_SCRIPT_SIG:
+ {
+ if (!key_lookup.emplace(key).second) {
+ throw std::ios_base::failure("Duplicate Key, input Taproot script signature already provided");
+ } else if (key.size() != 65) {
+ throw std::ios_base::failure("Input Taproot script signature key is not 65 bytes");
+ }
+ SpanReader s_key(s.GetType(), s.GetVersion(), Span{key}.subspan(1));
+ XOnlyPubKey xonly;
+ uint256 hash;
+ s_key >> xonly;
+ s_key >> hash;
+ std::vector<unsigned char> sig;
+ s >> sig;
+ if (sig.size() < 64) {
+ throw std::ios_base::failure("Input Taproot script path signature is shorter than 64 bytes");
+ } else if (sig.size() > 65) {
+ throw std::ios_base::failure("Input Taproot script path signature is longer than 65 bytes");
+ }
+ m_tap_script_sigs.emplace(std::make_pair(xonly, hash), sig);
+ break;
+ }
+ case PSBT_IN_TAP_LEAF_SCRIPT:
+ {
+ if (!key_lookup.emplace(key).second) {
+ throw std::ios_base::failure("Duplicate Key, input Taproot leaf script already provided");
+ } else if (key.size() < 34) {
+ throw std::ios_base::failure("Taproot leaf script key is not at least 34 bytes");
+ } else if ((key.size() - 2) % 32 != 0) {
+ throw std::ios_base::failure("Input Taproot leaf script key's control block size is not valid");
+ }
+ std::vector<unsigned char> script_v;
+ s >> script_v;
+ if (script_v.empty()) {
+ throw std::ios_base::failure("Input Taproot leaf script must be at least 1 byte");
+ }
+ uint8_t leaf_ver = script_v.back();
+ script_v.pop_back();
+ const auto leaf_script = std::make_pair(CScript(script_v.begin(), script_v.end()), (int)leaf_ver);
+ m_tap_scripts[leaf_script].insert(std::vector<unsigned char>(key.begin() + 1, key.end()));
+ break;
+ }
+ case PSBT_IN_TAP_BIP32_DERIVATION:
+ {
+ if (!key_lookup.emplace(key).second) {
+ throw std::ios_base::failure("Duplicate Key, input Taproot BIP32 keypath already provided");
+ } else if (key.size() != 33) {
+ throw std::ios_base::failure("Input Taproot BIP32 keypath key is not at 33 bytes");
+ }
+ SpanReader s_key(s.GetType(), s.GetVersion(), Span{key}.subspan(1));
+ XOnlyPubKey xonly;
+ s_key >> xonly;
+ std::set<uint256> leaf_hashes;
+ uint64_t value_len = ReadCompactSize(s);
+ size_t before_hashes = s.size();
+ s >> leaf_hashes;
+ size_t after_hashes = s.size();
+ size_t hashes_len = before_hashes - after_hashes;
+ size_t origin_len = value_len - hashes_len;
+ m_tap_bip32_paths.emplace(xonly, std::make_pair(leaf_hashes, DeserializeKeyOrigin(s, origin_len)));
+ break;
+ }
+ case PSBT_IN_TAP_INTERNAL_KEY:
+ {
+ if (!key_lookup.emplace(key).second) {
+ throw std::ios_base::failure("Duplicate Key, input Taproot internal key already provided");
+ } else if (key.size() != 1) {
+ throw std::ios_base::failure("Input Taproot internal key key is more than one byte type");
+ }
+ UnserializeFromVector(s, m_tap_internal_key);
+ break;
+ }
+ case PSBT_IN_TAP_MERKLE_ROOT:
+ {
+ if (!key_lookup.emplace(key).second) {
+ throw std::ios_base::failure("Duplicate Key, input Taproot merkle root already provided");
+ } else if (key.size() != 1) {
+ throw std::ios_base::failure("Input Taproot merkle root key is more than one byte type");
+ }
+ UnserializeFromVector(s, m_tap_merkle_root);
+ break;
+ }
case PSBT_IN_PROPRIETARY:
{
PSBTProprietary this_prop;
@@ -532,6 +709,9 @@ struct PSBTOutput
CScript redeem_script;
CScript witness_script;
std::map<CPubKey, KeyOriginInfo> hd_keypaths;
+ XOnlyPubKey m_tap_internal_key;
+ std::optional<TaprootBuilder> m_tap_tree;
+ std::map<XOnlyPubKey, std::pair<std::set<uint256>, KeyOriginInfo>> m_tap_bip32_paths;
std::map<std::vector<unsigned char>, std::vector<unsigned char>> unknown;
std::set<PSBTProprietary> m_proprietary;
@@ -564,6 +744,40 @@ struct PSBTOutput
s << entry.value;
}
+ // Write taproot internal key
+ if (!m_tap_internal_key.IsNull()) {
+ SerializeToVector(s, PSBT_OUT_TAP_INTERNAL_KEY);
+ s << ToByteVector(m_tap_internal_key);
+ }
+
+ // Write taproot tree
+ if (m_tap_tree.has_value()) {
+ SerializeToVector(s, PSBT_OUT_TAP_TREE);
+ std::vector<unsigned char> value;
+ CVectorWriter s_value(s.GetType(), s.GetVersion(), value, 0);
+ const auto& tuples = m_tap_tree->GetTreeTuples();
+ for (const auto& tuple : tuples) {
+ uint8_t depth = std::get<0>(tuple);
+ uint8_t leaf_ver = std::get<1>(tuple);
+ CScript script = std::get<2>(tuple);
+ s_value << depth;
+ s_value << leaf_ver;
+ s_value << script;
+ }
+ s << value;
+ }
+
+ // Write taproot bip32 keypaths
+ for (const auto& [xonly, leaf] : m_tap_bip32_paths) {
+ const auto& [leaf_hashes, origin] = leaf;
+ SerializeToVector(s, PSBT_OUT_TAP_BIP32_DERIVATION, xonly);
+ std::vector<unsigned char> value;
+ CVectorWriter s_value(s.GetType(), s.GetVersion(), value, 0);
+ s_value << leaf_hashes;
+ SerializeKeyOrigin(s_value, origin);
+ s << value;
+ }
+
// Write unknown things
for (auto& entry : unknown) {
s << entry.first;
@@ -624,6 +838,59 @@ struct PSBTOutput
DeserializeHDKeypaths(s, key, hd_keypaths);
break;
}
+ case PSBT_OUT_TAP_INTERNAL_KEY:
+ {
+ if (!key_lookup.emplace(key).second) {
+ throw std::ios_base::failure("Duplicate Key, output Taproot internal key already provided");
+ } else if (key.size() != 1) {
+ throw std::ios_base::failure("Output Taproot internal key key is more than one byte type");
+ }
+ UnserializeFromVector(s, m_tap_internal_key);
+ break;
+ }
+ case PSBT_OUT_TAP_TREE:
+ {
+ if (!key_lookup.emplace(key).second) {
+ throw std::ios_base::failure("Duplicate Key, output Taproot tree already provided");
+ } else if (key.size() != 1) {
+ throw std::ios_base::failure("Output Taproot tree key is more than one byte type");
+ }
+ m_tap_tree.emplace();
+ std::vector<unsigned char> tree_v;
+ s >> tree_v;
+ SpanReader s_tree(s.GetType(), s.GetVersion(), tree_v);
+ while (!s_tree.empty()) {
+ uint8_t depth;
+ uint8_t leaf_ver;
+ CScript script;
+ s_tree >> depth;
+ s_tree >> leaf_ver;
+ s_tree >> script;
+ m_tap_tree->Add((int)depth, script, (int)leaf_ver, true /* track */);
+ }
+ if (!m_tap_tree->IsComplete()) {
+ throw std::ios_base::failure("Output Taproot tree is malformed");
+ }
+ break;
+ }
+ case PSBT_OUT_TAP_BIP32_DERIVATION:
+ {
+ if (!key_lookup.emplace(key).second) {
+ throw std::ios_base::failure("Duplicate Key, output Taproot BIP32 keypath already provided");
+ } else if (key.size() != 33) {
+ throw std::ios_base::failure("Output Taproot BIP32 keypath key is not at 33 bytes");
+ }
+ XOnlyPubKey xonly(uint256({key.begin() + 1, key.begin() + 33}));
+ std::set<uint256> leaf_hashes;
+ uint64_t value_len = ReadCompactSize(s);
+ size_t before_hashes = s.size();
+ s >> leaf_hashes;
+ size_t after_hashes = s.size();
+ size_t hashes_len = before_hashes - after_hashes;
+ size_t origin_len = value_len - hashes_len;
+ m_tap_bip32_paths.emplace(xonly, std::make_pair(leaf_hashes, DeserializeKeyOrigin(s, origin_len)));
+ break;
+ }
case PSBT_OUT_PROPRIETARY:
{
PSBTProprietary this_prop;
@@ -652,6 +919,11 @@ struct PSBTOutput
}
}
+ // Finalize m_tap_tree so that all of the computed things are computed
+ if (m_tap_tree.has_value() && m_tap_tree->IsComplete() && m_tap_internal_key.IsFullyValid()) {
+ m_tap_tree->Finalize(m_tap_internal_key);
+ }
+
if (!found_sep) {
throw std::ios_base::failure("Separator is missing at the end of an output map");
}
diff --git a/src/pubkey.h b/src/pubkey.h
index dfe06f834c..463efe1b00 100644
--- a/src/pubkey.h
+++ b/src/pubkey.h
@@ -286,6 +286,9 @@ public:
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; }
+
+ //! Implement serialization without length prefixes since it is a fixed length
+ SERIALIZE_METHODS(XOnlyPubKey, obj) { READWRITE(obj.m_keydata); }
};
struct CExtPubKey {
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index b9b8c36bb3..792a1e13b0 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -790,6 +790,43 @@ static RPCHelpMan decodepsbt()
{
{RPCResult::Type::STR, "hash", "The hash and preimage that corresponds to it."},
}},
+ {RPCResult::Type::STR_HEX, "taproot_key_path_sig", /*optional=*/ true, "hex-encoded signature for the Taproot key path spend"},
+ {RPCResult::Type::ARR, "taproot_script_path_sigs", /*optional=*/ true, "",
+ {
+ {RPCResult::Type::OBJ, "signature", /*optional=*/ true, "The signature for the pubkey and leaf hash combination",
+ {
+ {RPCResult::Type::STR, "pubkey", "The x-only pubkey for this signature"},
+ {RPCResult::Type::STR, "leaf_hash", "The leaf hash for this signature"},
+ {RPCResult::Type::STR, "sig", "The signature itself"},
+ }},
+ }},
+ {RPCResult::Type::ARR, "taproot_scripts", /*optional=*/ true, "",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR_HEX, "script", "A leaf script"},
+ {RPCResult::Type::NUM, "leaf_ver", "The version number for the leaf script"},
+ {RPCResult::Type::ARR, "control_blocks", "The control blocks for this script",
+ {
+ {RPCResult::Type::STR_HEX, "control_block", "A hex-encoded control block for this script"},
+ }},
+ }},
+ }},
+ {RPCResult::Type::ARR, "taproot_bip32_derivs", /*optional=*/ true, "",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR, "pubkey", "The x-only public key this path corresponds to"},
+ {RPCResult::Type::STR, "master_fingerprint", "The fingerprint of the master key"},
+ {RPCResult::Type::STR, "path", "The path"},
+ {RPCResult::Type::ARR, "leaf_hashes", "The hashes of the leaves this pubkey appears in",
+ {
+ {RPCResult::Type::STR_HEX, "hash", "The hash of a leaf this pubkey appears in"},
+ }},
+ }},
+ }},
+ {RPCResult::Type::STR_HEX, "taproot_internal_key", /*optional=*/ true, "The hex-encoded Taproot x-only internal key"},
+ {RPCResult::Type::STR_HEX, "taproot_merkle_root", /*optional=*/ true, "The hex-encoded Taproot merkle root"},
{RPCResult::Type::OBJ_DYN, "unknown", /*optional=*/ true, "The unknown input fields",
{
{RPCResult::Type::STR_HEX, "key", "(key-value pair) An unknown key-value pair"},
@@ -831,7 +868,30 @@ static RPCHelpMan decodepsbt()
{RPCResult::Type::STR, "path", "The path"},
}},
}},
- {RPCResult::Type::OBJ_DYN, "unknown", /*optional=*/true, "The unknown global fields",
+ {RPCResult::Type::STR_HEX, "taproot_internal_key", /*optional=*/ true, "The hex-encoded Taproot x-only internal key"},
+ {RPCResult::Type::ARR, "taproot_tree", /*optional=*/ true, "The tuples that make up the Taproot tree, in depth first search order",
+ {
+ {RPCResult::Type::OBJ, "tuple", /*optional=*/ true, "A single leaf script in the taproot tree",
+ {
+ {RPCResult::Type::NUM, "depth", "The depth of this element in the tree"},
+ {RPCResult::Type::NUM, "leaf_ver", "The version of this leaf"},
+ {RPCResult::Type::STR, "script", "The hex-encoded script itself"},
+ }},
+ }},
+ {RPCResult::Type::ARR, "taproot_bip32_derivs", /*optional=*/ true, "",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR, "pubkey", "The x-only public key this path corresponds to"},
+ {RPCResult::Type::STR, "master_fingerprint", "The fingerprint of the master key"},
+ {RPCResult::Type::STR, "path", "The path"},
+ {RPCResult::Type::ARR, "leaf_hashes", "The hashes of the leaves this pubkey appears in",
+ {
+ {RPCResult::Type::STR_HEX, "hash", "The hash of a leaf this pubkey appears in"},
+ }},
+ }},
+ }},
+ {RPCResult::Type::OBJ_DYN, "unknown", /*optional=*/true, "The unknown output fields",
{
{RPCResult::Type::STR_HEX, "key", "(key-value pair) An unknown key-value pair"},
}},
@@ -1045,6 +1105,72 @@ static RPCHelpMan decodepsbt()
in.pushKV("hash256_preimages", hash256_preimages);
}
+ // Taproot key path signature
+ if (!input.m_tap_key_sig.empty()) {
+ in.pushKV("taproot_key_path_sig", HexStr(input.m_tap_key_sig));
+ }
+
+ // Taproot script path signatures
+ if (!input.m_tap_script_sigs.empty()) {
+ UniValue script_sigs(UniValue::VARR);
+ for (const auto& [pubkey_leaf, sig] : input.m_tap_script_sigs) {
+ const auto& [xonly, leaf_hash] = pubkey_leaf;
+ UniValue sigobj(UniValue::VOBJ);
+ sigobj.pushKV("pubkey", HexStr(xonly));
+ sigobj.pushKV("leaf_hash", HexStr(leaf_hash));
+ sigobj.pushKV("sig", HexStr(sig));
+ script_sigs.push_back(sigobj);
+ }
+ in.pushKV("taproot_script_path_sigs", script_sigs);
+ }
+
+ // Taproot leaf scripts
+ if (!input.m_tap_scripts.empty()) {
+ UniValue tap_scripts(UniValue::VARR);
+ for (const auto& [leaf, control_blocks] : input.m_tap_scripts) {
+ const auto& [script, leaf_ver] = leaf;
+ UniValue script_info(UniValue::VOBJ);
+ script_info.pushKV("script", HexStr(script));
+ script_info.pushKV("leaf_ver", leaf_ver);
+ UniValue control_blocks_univ(UniValue::VARR);
+ for (const auto& control_block : control_blocks) {
+ control_blocks_univ.push_back(HexStr(control_block));
+ }
+ script_info.pushKV("control_blocks", control_blocks_univ);
+ tap_scripts.push_back(script_info);
+ }
+ in.pushKV("taproot_scripts", tap_scripts);
+ }
+
+ // Taproot bip32 keypaths
+ if (!input.m_tap_bip32_paths.empty()) {
+ UniValue keypaths(UniValue::VARR);
+ for (const auto& [xonly, leaf_origin] : input.m_tap_bip32_paths) {
+ const auto& [leaf_hashes, origin] = leaf_origin;
+ UniValue path_obj(UniValue::VOBJ);
+ path_obj.pushKV("pubkey", HexStr(xonly));
+ path_obj.pushKV("master_fingerprint", strprintf("%08x", ReadBE32(origin.fingerprint)));
+ path_obj.pushKV("path", WriteHDKeypath(origin.path));
+ UniValue leaf_hashes_arr(UniValue::VARR);
+ for (const auto& leaf_hash : leaf_hashes) {
+ leaf_hashes_arr.push_back(HexStr(leaf_hash));
+ }
+ path_obj.pushKV("leaf_hashes", leaf_hashes_arr);
+ keypaths.push_back(path_obj);
+ }
+ in.pushKV("taproot_bip32_derivs", keypaths);
+ }
+
+ // Taproot internal key
+ if (!input.m_tap_internal_key.IsNull()) {
+ in.pushKV("taproot_internal_key", HexStr(input.m_tap_internal_key));
+ }
+
+ // Write taproot merkle root
+ if (!input.m_tap_merkle_root.IsNull()) {
+ in.pushKV("taproot_merkle_root", HexStr(input.m_tap_merkle_root));
+ }
+
// Proprietary
if (!input.m_proprietary.empty()) {
UniValue proprietary(UniValue::VARR);
@@ -1103,6 +1229,47 @@ static RPCHelpMan decodepsbt()
out.pushKV("bip32_derivs", keypaths);
}
+ // Taproot internal key
+ if (!output.m_tap_internal_key.IsNull()) {
+ out.pushKV("taproot_internal_key", HexStr(output.m_tap_internal_key));
+ }
+
+ // Taproot tree
+ if (output.m_tap_tree.has_value()) {
+ UniValue tree(UniValue::VARR);
+ const auto& tuples = output.m_tap_tree->GetTreeTuples();
+ for (const auto& tuple : tuples) {
+ uint8_t depth = std::get<0>(tuple);
+ uint8_t leaf_ver = std::get<1>(tuple);
+ CScript script = std::get<2>(tuple);
+ UniValue elem(UniValue::VOBJ);
+ elem.pushKV("depth", (int)depth);
+ elem.pushKV("leaf_ver", (int)leaf_ver);
+ elem.pushKV("script", HexStr(script));
+ tree.push_back(elem);
+ }
+ out.pushKV("taproot_tree", tree);
+ }
+
+ // Taproot bip32 keypaths
+ if (!output.m_tap_bip32_paths.empty()) {
+ UniValue keypaths(UniValue::VARR);
+ for (const auto& [xonly, leaf_origin] : output.m_tap_bip32_paths) {
+ const auto& [leaf_hashes, origin] = leaf_origin;
+ UniValue path_obj(UniValue::VOBJ);
+ path_obj.pushKV("pubkey", HexStr(xonly));
+ path_obj.pushKV("master_fingerprint", strprintf("%08x", ReadBE32(origin.fingerprint)));
+ path_obj.pushKV("path", WriteHDKeypath(origin.path));
+ UniValue leaf_hashes_arr(UniValue::VARR);
+ for (const auto& leaf_hash : leaf_hashes) {
+ leaf_hashes_arr.push_back(HexStr(leaf_hash));
+ }
+ path_obj.pushKV("leaf_hashes", leaf_hashes_arr);
+ keypaths.push_back(path_obj);
+ }
+ out.pushKV("taproot_bip32_derivs", keypaths);
+ }
+
// Proprietary
if (!output.m_proprietary.empty()) {
UniValue proprietary(UniValue::VARR);
diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp
index cece0b60ce..ca0170c84b 100644
--- a/src/script/descriptor.cpp
+++ b/src/script/descriptor.cpp
@@ -882,7 +882,7 @@ protected:
if (!xpk.IsFullyValid()) return {};
builder.Finalize(xpk);
WitnessV1Taproot output = builder.GetOutput();
- out.tr_spenddata[output].Merge(builder.GetSpendData());
+ out.tr_trees[output] = builder;
out.pubkeys.emplace(keys[0].GetID(), keys[0]);
return Vector(GetScriptForDestination(output));
}
diff --git a/src/script/sign.cpp b/src/script/sign.cpp
index 2d569d674a..a3681d26cc 100644
--- a/src/script/sign.cpp
+++ b/src/script/sign.cpp
@@ -150,6 +150,7 @@ static bool CreateTaprootScriptSig(const BaseSignatureCreator& creator, Signatur
auto it = sigdata.taproot_script_sigs.find(lookup_key);
if (it != sigdata.taproot_script_sigs.end()) {
sig_out = it->second;
+ return true;
}
if (creator.CreateSchnorrSig(provider, sig_out, pubkey, &leaf_hash, nullptr, sigversion)) {
sigdata.taproot_script_sigs[lookup_key] = sig_out;
@@ -169,6 +170,17 @@ static bool SignTaprootScript(const SigningProvider& provider, const BaseSignatu
// <xonly pubkey> OP_CHECKSIG
if (script.size() == 34 && script[33] == OP_CHECKSIG && script[0] == 0x20) {
XOnlyPubKey pubkey{Span{script}.subspan(1, 32)};
+
+ KeyOriginInfo info;
+ if (provider.GetKeyOriginByXOnly(pubkey, info)) {
+ auto it = sigdata.taproot_misc_pubkeys.find(pubkey);
+ if (it == sigdata.taproot_misc_pubkeys.end()) {
+ sigdata.taproot_misc_pubkeys.emplace(pubkey, std::make_pair(std::set<uint256>({leaf_hash}), info));
+ } else {
+ it->second.first.insert(leaf_hash);
+ }
+ }
+
std::vector<unsigned char> sig;
if (CreateTaprootScriptSig(creator, sigdata, provider, sig, pubkey, leaf_hash, sigversion)) {
result = Vector(std::move(sig));
@@ -205,17 +217,29 @@ static bool SignTaprootScript(const SigningProvider& provider, const BaseSignatu
static bool SignTaproot(const SigningProvider& provider, const BaseSignatureCreator& creator, const WitnessV1Taproot& output, SignatureData& sigdata, std::vector<valtype>& result)
{
TaprootSpendData spenddata;
+ TaprootBuilder builder;
// Gather information about this output.
if (provider.GetTaprootSpendData(output, spenddata)) {
sigdata.tr_spenddata.Merge(spenddata);
}
+ if (provider.GetTaprootBuilder(output, builder)) {
+ sigdata.tr_builder = builder;
+ }
// Try key path spending.
{
+ KeyOriginInfo info;
+ if (provider.GetKeyOriginByXOnly(sigdata.tr_spenddata.internal_key, info)) {
+ auto it = sigdata.taproot_misc_pubkeys.find(sigdata.tr_spenddata.internal_key);
+ if (it == sigdata.taproot_misc_pubkeys.end()) {
+ sigdata.taproot_misc_pubkeys.emplace(sigdata.tr_spenddata.internal_key, std::make_pair(std::set<uint256>(), info));
+ }
+ }
+
std::vector<unsigned char> sig;
if (sigdata.taproot_key_path_sig.size() == 0) {
- if (creator.CreateSchnorrSig(provider, sig, spenddata.internal_key, nullptr, &spenddata.merkle_root, SigVersion::TAPROOT)) {
+ if (creator.CreateSchnorrSig(provider, sig, sigdata.tr_spenddata.internal_key, nullptr, &sigdata.tr_spenddata.merkle_root, SigVersion::TAPROOT)) {
sigdata.taproot_key_path_sig = sig;
}
}
diff --git a/src/script/sign.h b/src/script/sign.h
index 71203d08ec..5e58272154 100644
--- a/src/script/sign.h
+++ b/src/script/sign.h
@@ -70,10 +70,12 @@ struct SignatureData {
CScript witness_script; ///< The witnessScript (if any) for the input. witnessScripts are used in P2WSH outputs.
CScriptWitness scriptWitness; ///< The scriptWitness of an input. Contains complete signatures or the traditional partial signatures format. scriptWitness is part of a transaction input per BIP 144.
TaprootSpendData tr_spenddata; ///< Taproot spending data.
+ std::optional<TaprootBuilder> tr_builder; ///< Taproot tree used to build tr_spenddata.
std::map<CKeyID, SigPair> signatures; ///< BIP 174 style partial signatures for the input. May contain all signatures necessary for producing a final scriptSig or scriptWitness.
std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>> misc_pubkeys;
std::vector<unsigned char> taproot_key_path_sig; /// Schnorr signature for key path spending
std::map<std::pair<XOnlyPubKey, uint256>, std::vector<unsigned char>> taproot_script_sigs; ///< (Partial) schnorr signatures, indexed by XOnlyPubKey and leaf_hash.
+ std::map<XOnlyPubKey, std::pair<std::set<uint256>, KeyOriginInfo>> taproot_misc_pubkeys; ///< Miscellaneous Taproot pubkeys involved in this input along with their leaf script hashes and key origin data. Also includes the Taproot internal key (may have no leaf script hashes).
std::vector<CKeyID> missing_pubkeys; ///< KeyIDs of pubkeys which could not be found
std::vector<CKeyID> missing_sigs; ///< KeyIDs of pubkeys for signatures which could not be found
uint160 missing_redeem_script; ///< ScriptID of the missing redeemScript (if any)
diff --git a/src/script/signingprovider.cpp b/src/script/signingprovider.cpp
index 552934e0eb..c624a17628 100644
--- a/src/script/signingprovider.cpp
+++ b/src/script/signingprovider.cpp
@@ -48,6 +48,10 @@ bool HidingSigningProvider::GetTaprootSpendData(const XOnlyPubKey& output_key, T
{
return m_provider->GetTaprootSpendData(output_key, spenddata);
}
+bool HidingSigningProvider::GetTaprootBuilder(const XOnlyPubKey& output_key, TaprootBuilder& builder) const
+{
+ return m_provider->GetTaprootBuilder(output_key, builder);
+}
bool FlatSigningProvider::GetCScript(const CScriptID& scriptid, CScript& script) const { return LookupHelper(scripts, scriptid, script); }
bool FlatSigningProvider::GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const { return LookupHelper(pubkeys, keyid, pubkey); }
@@ -61,7 +65,16 @@ bool FlatSigningProvider::GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info)
bool FlatSigningProvider::GetKey(const CKeyID& keyid, CKey& key) const { return LookupHelper(keys, keyid, key); }
bool FlatSigningProvider::GetTaprootSpendData(const XOnlyPubKey& output_key, TaprootSpendData& spenddata) const
{
- return LookupHelper(tr_spenddata, output_key, spenddata);
+ TaprootBuilder builder;
+ if (LookupHelper(tr_trees, output_key, builder)) {
+ spenddata = builder.GetSpendData();
+ return true;
+ }
+ return false;
+}
+bool FlatSigningProvider::GetTaprootBuilder(const XOnlyPubKey& output_key, TaprootBuilder& builder) const
+{
+ return LookupHelper(tr_trees, output_key, builder);
}
FlatSigningProvider Merge(const FlatSigningProvider& a, const FlatSigningProvider& b)
@@ -75,10 +88,8 @@ FlatSigningProvider Merge(const FlatSigningProvider& a, const FlatSigningProvide
ret.keys.insert(b.keys.begin(), b.keys.end());
ret.origins = a.origins;
ret.origins.insert(b.origins.begin(), b.origins.end());
- ret.tr_spenddata = a.tr_spenddata;
- for (const auto& [output_key, spenddata] : b.tr_spenddata) {
- ret.tr_spenddata[output_key].Merge(spenddata);
- }
+ ret.tr_trees = a.tr_trees;
+ ret.tr_trees.insert(b.tr_trees.begin(), b.tr_trees.end());
return ret;
}
diff --git a/src/script/signingprovider.h b/src/script/signingprovider.h
index f1bded1a8c..792cc903f2 100644
--- a/src/script/signingprovider.h
+++ b/src/script/signingprovider.h
@@ -25,6 +25,7 @@ public:
virtual bool HaveKey(const CKeyID &address) const { return false; }
virtual bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const { return false; }
virtual bool GetTaprootSpendData(const XOnlyPubKey& output_key, TaprootSpendData& spenddata) const { return false; }
+ virtual bool GetTaprootBuilder(const XOnlyPubKey& output_key, TaprootBuilder& builder) const { return false; }
bool GetKeyByXOnly(const XOnlyPubKey& pubkey, CKey& key) const
{
@@ -67,6 +68,7 @@ public:
bool GetKey(const CKeyID& keyid, CKey& key) const override;
bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override;
bool GetTaprootSpendData(const XOnlyPubKey& output_key, TaprootSpendData& spenddata) const override;
+ bool GetTaprootBuilder(const XOnlyPubKey& output_key, TaprootBuilder& builder) const override;
};
struct FlatSigningProvider final : public SigningProvider
@@ -75,13 +77,14 @@ struct FlatSigningProvider final : public SigningProvider
std::map<CKeyID, CPubKey> pubkeys;
std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>> origins;
std::map<CKeyID, CKey> keys;
- std::map<XOnlyPubKey, TaprootSpendData> tr_spenddata; /** Map from output key to spend data. */
+ std::map<XOnlyPubKey, TaprootBuilder> tr_trees; /** Map from output key to Taproot tree (which can then make the TaprootSpendData */
bool GetCScript(const CScriptID& scriptid, CScript& script) const override;
bool GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const override;
bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override;
bool GetKey(const CKeyID& keyid, CKey& key) const override;
bool GetTaprootSpendData(const XOnlyPubKey& output_key, TaprootSpendData& spenddata) const override;
+ bool GetTaprootBuilder(const XOnlyPubKey& output_key, TaprootBuilder& builder) const override;
};
FlatSigningProvider Merge(const FlatSigningProvider& a, const FlatSigningProvider& b);
diff --git a/src/script/standard.cpp b/src/script/standard.cpp
index e25155d3dd..5d80891485 100644
--- a/src/script/standard.cpp
+++ b/src/script/standard.cpp
@@ -485,6 +485,7 @@ WitnessV1Taproot TaprootBuilder::GetOutput() { return WitnessV1Taproot{m_output_
TaprootSpendData TaprootBuilder::GetSpendData() const
{
assert(IsComplete());
+ assert(m_output_key.IsFullyValid());
TaprootSpendData spd;
spd.merkle_root = m_branch.size() == 0 ? uint256() : m_branch[0]->hash;
spd.internal_key = m_internal_key;
@@ -642,3 +643,19 @@ std::optional<std::vector<std::tuple<int, CScript, int>>> InferTaprootTree(const
return ret;
}
+
+std::vector<std::tuple<uint8_t, uint8_t, CScript>> TaprootBuilder::GetTreeTuples() const
+{
+ assert(IsComplete());
+ std::vector<std::tuple<uint8_t, uint8_t, CScript>> tuples;
+ if (m_branch.size()) {
+ const auto& leaves = m_branch[0]->leaves;
+ for (const auto& leaf : leaves) {
+ assert(leaf.merkle_branch.size() <= TAPROOT_CONTROL_MAX_NODE_COUNT);
+ uint8_t depth = (uint8_t)leaf.merkle_branch.size();
+ uint8_t leaf_ver = (uint8_t)leaf.leaf_version;
+ tuples.push_back(std::make_tuple(depth, leaf_ver, leaf.script));
+ }
+ }
+ return tuples;
+}
diff --git a/src/script/standard.h b/src/script/standard.h
index 6a15ba4e3d..448fdff010 100644
--- a/src/script/standard.h
+++ b/src/script/standard.h
@@ -322,6 +322,8 @@ public:
static bool ValidDepths(const std::vector<int>& depths);
/** Compute spending data (after Finalize()). */
TaprootSpendData GetSpendData() const;
+ /** Returns a vector of tuples representing the depth, leaf version, and script */
+ std::vector<std::tuple<uint8_t, uint8_t, CScript>> GetTreeTuples() const;
};
/** Given a TaprootSpendData and the output key, reconstruct its script tree.
diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp
index 8633e7c62c..1fec82a485 100644
--- a/src/wallet/scriptpubkeyman.cpp
+++ b/src/wallet/scriptpubkeyman.cpp
@@ -2180,6 +2180,19 @@ TransactionError DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTransaction&
*keys = Merge(*keys, *pk_keys);
}
}
+ for (const auto& pk_pair : input.m_tap_bip32_paths) {
+ const XOnlyPubKey& pubkey = pk_pair.first;
+ for (unsigned char prefix : {0x02, 0x03}) {
+ unsigned char b[33] = {prefix};
+ std::copy(pubkey.begin(), pubkey.end(), b + 1);
+ CPubKey fullpubkey;
+ fullpubkey.Set(b, b + 33);
+ std::unique_ptr<FlatSigningProvider> pk_keys = GetSigningProvider(fullpubkey);
+ if (pk_keys) {
+ *keys = Merge(*keys, *pk_keys);
+ }
+ }
+ }
}
SignPSBTInput(HidingSigningProvider(keys.get(), !sign, !bip32derivs), psbtx, i, &txdata, sighash_type, nullptr, finalize);
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index d0b093bbb7..041481559b 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -2006,6 +2006,35 @@ TransactionError CWallet::FillPSBT(PartiallySignedTransaction& psbtx, bool& comp
}
}
+ // Only drop non_witness_utxos if sighash_type != SIGHASH_ANYONECANPAY
+ if ((sighash_type & 0x80) != SIGHASH_ANYONECANPAY) {
+ // Figure out if any non_witness_utxos should be dropped
+ std::vector<unsigned int> to_drop;
+ for (unsigned int i = 0; i < psbtx.inputs.size(); ++i) {
+ const auto& input = psbtx.inputs.at(i);
+ int wit_ver;
+ std::vector<unsigned char> wit_prog;
+ if (input.witness_utxo.IsNull() || !input.witness_utxo.scriptPubKey.IsWitnessProgram(wit_ver, wit_prog)) {
+ // There's a non-segwit input or Segwit v0, so we cannot drop any witness_utxos
+ to_drop.clear();
+ break;
+ }
+ if (wit_ver == 0) {
+ // Segwit v0, so we cannot drop any non_witness_utxos
+ to_drop.clear();
+ break;
+ }
+ if (input.non_witness_utxo) {
+ to_drop.push_back(i);
+ }
+ }
+
+ // Drop the non_witness_utxos that we can drop
+ for (unsigned int i : to_drop) {
+ psbtx.inputs.at(i).non_witness_utxo = nullptr;
+ }
+ }
+
// Complete if every input is now signed
complete = true;
for (const auto& input : psbtx.inputs) {