mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-03 09:56:38 -05:00
Merge bitcoin/bitcoin#22558: psbt: Taproot fields for PSBT
b80de4c505
test: Test signing psbts without explicitly having scripts (Andrew Chow)a73b56888a
wallet: also search taproot pubkeys in FillPSBT (Andrew Chow)6cff82722f
sign: Use sigdata taproot spenddata when signing (Andrew Chow)5f12fe3f36
psbt: Implement merge for Taproot fields (Andrew Chow)1ece9a3715
psbt, test: Check for taproot fields in taproot psbt test (Andrew Chow)496a1bbe5e
taproot: Use pre-existing signatures if available (Andrew Chow)0ad21e7c55
tests: Test taproot fields for PSBT (Andrew Chow)103c6fd279
psbt: Remove non_witness_utxo for segwit v1+ (Andrew Chow)7dccdd3157
Implement decodepsbt for Taproot fields (Andrew Chow)ac7747585f
Fill PSBT Taproot output data to/from SignatureData (Andrew Chow)25b6ae46e7
Assert that TaprootBuilder is Finalized during GetSpendData (Andrew Chow)3ae5b6af21
Store TaprootBuilder in SigningProviders instead of TaprootSpendData (Andrew Chow)4d1223e512
Fetch key origins for Taproot keys (Andrew Chow)52e3f2f88e
Fill PSBT Taproot input data to/from SignatureData (Andrew Chow)05e2cc9a30
Implement de/ser of PSBT's Taproot fields (Andrew Chow)d557eff2ad
Add serialization methods to XOnlyPubKey (Andrew Chow)d43923c381
Add TaprootBuilder::GetTreeTuples (Andrew Chow)ce911204e4
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 ACKb80de4c505
Tree-SHA512: 50b79bb44f353c9ec2ef4c98aac08a81eba560987e5264a5684caa370e9c4e7a8255c06747fc47749511be45b32d01492e015f92b82be8d22bc8bf192073bd26
This commit is contained in:
commit
5bf65ec66e
17 changed files with 708 additions and 31 deletions
69
src/psbt.cpp
69
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
|
||||
|
|
288
src/psbt.h
288
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");
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -27,7 +27,18 @@
|
|||
"cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQQAAQQBagA=",
|
||||
"cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQEJAOH1BQAAAAAAAQUAAQUBUQA=",
|
||||
"cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQcAAQcBUQA=",
|
||||
"cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQEJAOH1BQAAAAAAAQgBAAEIAwEBUQA="
|
||||
"cHNidP8BADMBAAAAAREREREREREREREREREREREREfrK3hERERERERERERERfwAAAAD/////AAAAAAAAAQEJAOH1BQAAAAAAAQgBAAEIAwEBUQA=",
|
||||
"cHNidP8BAHECAAAAASd0Srq/MCf+DWzyOpbu4u+xiO9SMBlUWFiD5ptmJLJCAAAAAAD/////Anh8AQAAAAAAFgAUg6fjS9mf8DpJYu+KGhAbspVGHs5gawQqAQAAABYAFHrDad8bIOAz1hFmI5V7CsSfPFLoAAAAAAABASsA8gUqAQAAACJRIFosLPW1LPMfg60ujaY/8DGD7Nj2CcdRCuikjgORCgdXARchAv40kGTJjW4qhT+jybEr2LMEoZwZXGDvp+4jkwRtP6IyAAAA",
|
||||
"cHNidP8BAHECAAAAASd0Srq/MCf+DWzyOpbu4u+xiO9SMBlUWFiD5ptmJLJCAAAAAAD/////Anh8AQAAAAAAFgAUg6fjS9mf8DpJYu+KGhAbspVGHs5gawQqAQAAABYAFHrDad8bIOAz1hFmI5V7CsSfPFLoAAAAAAABASsA8gUqAQAAACJRIFosLPW1LPMfg60ujaY/8DGD7Nj2CcdRCuikjgORCgdXARM/Fzuz02wHSvtxb+xjB6BpouRQuZXzyCeFlFq43w4kJg3NcDsMvzTeOZGEqUgawrNYbbZgHwJqd/fkk4SBvDR1AAAA",
|
||||
"cHNidP8BAHECAAAAASd0Srq/MCf+DWzyOpbu4u+xiO9SMBlUWFiD5ptmJLJCAAAAAAD/////Anh8AQAAAAAAFgAUg6fjS9mf8DpJYu+KGhAbspVGHs5gawQqAQAAABYAFHrDad8bIOAz1hFmI5V7CsSfPFLoAAAAAAABASsA8gUqAQAAACJRIFosLPW1LPMfg60ujaY/8DGD7Nj2CcdRCuikjgORCgdXARNCFzuz02wHSvtxb+xjB6BpouRQuZXzyCeFlFq43w4kJg3NcDsMvzTeOZGEqUgawrNYbbZgHwJqd/fkk4SBvDR1FwGqAAAA",
|
||||
"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"
|
||||
],
|
||||
"valid" : [
|
||||
"cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAAAA",
|
||||
|
@ -43,7 +54,13 @@
|
|||
"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"
|
||||
],
|
||||
"creator" : [
|
||||
{
|
||||
|
|
|
@ -9,7 +9,7 @@ from decimal import Decimal
|
|||
from itertools import product
|
||||
|
||||
from test_framework.descriptors import descsum_create
|
||||
from test_framework.key import ECKey
|
||||
from test_framework.key import ECKey, H_POINT
|
||||
from test_framework.messages import (
|
||||
ser_compact_size,
|
||||
WITNESS_SCALE_FACTOR,
|
||||
|
@ -723,5 +723,46 @@ class PSBTTest(BitcoinTestFramework):
|
|||
)
|
||||
assert_equal(psbt2["fee"], psbt3["fee"])
|
||||
|
||||
self.log.info("Test signing inputs that the wallet has keys for but is not watching the scripts")
|
||||
self.nodes[1].createwallet(wallet_name="scriptwatchonly", disable_private_keys=True)
|
||||
watchonly = self.nodes[1].get_wallet_rpc("scriptwatchonly")
|
||||
|
||||
eckey = ECKey()
|
||||
eckey.generate()
|
||||
privkey = bytes_to_wif(eckey.get_bytes())
|
||||
|
||||
desc = descsum_create("wsh(pkh({}))".format(eckey.get_pubkey().get_bytes().hex()))
|
||||
if self.options.descriptors:
|
||||
res = watchonly.importdescriptors([{"desc": desc, "timestamp": "now"}])
|
||||
else:
|
||||
res = watchonly.importmulti([{"desc": desc, "timestamp": "now"}])
|
||||
assert res[0]["success"]
|
||||
addr = self.nodes[0].deriveaddresses(desc)[0]
|
||||
self.nodes[0].sendtoaddress(addr, 10)
|
||||
self.generate(self.nodes[0], 1)
|
||||
self.nodes[0].importprivkey(privkey)
|
||||
|
||||
psbt = watchonly.sendall([wallet.getnewaddress()])["psbt"]
|
||||
psbt = self.nodes[0].walletprocesspsbt(psbt)["psbt"]
|
||||
self.nodes[0].sendrawtransaction(self.nodes[0].finalizepsbt(psbt)["hex"])
|
||||
|
||||
# Same test but for taproot
|
||||
if self.options.descriptors:
|
||||
eckey = ECKey()
|
||||
eckey.generate()
|
||||
privkey = bytes_to_wif(eckey.get_bytes())
|
||||
|
||||
desc = descsum_create("tr({},pk({}))".format(H_POINT, eckey.get_pubkey().get_bytes().hex()))
|
||||
res = watchonly.importdescriptors([{"desc": desc, "timestamp": "now"}])
|
||||
assert res[0]["success"]
|
||||
addr = self.nodes[0].deriveaddresses(desc)[0]
|
||||
self.nodes[0].sendtoaddress(addr, 10)
|
||||
self.generate(self.nodes[0], 1)
|
||||
self.nodes[0].importdescriptors([{"desc": descsum_create("tr({})".format(privkey)), "timestamp":"now"}])
|
||||
|
||||
psbt = watchonly.sendall([wallet.getnewaddress()])["psbt"]
|
||||
psbt = self.nodes[0].walletprocesspsbt(psbt)["psbt"]
|
||||
self.nodes[0].sendrawtransaction(self.nodes[0].finalizepsbt(psbt)["hex"])
|
||||
|
||||
if __name__ == '__main__':
|
||||
PSBTTest().main()
|
||||
|
|
|
@ -15,6 +15,10 @@ import unittest
|
|||
|
||||
from .util import modinv
|
||||
|
||||
# Point with no known discrete log.
|
||||
H_POINT = "50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0"
|
||||
|
||||
|
||||
def TaggedHash(tag, data):
|
||||
ss = hashlib.sha256(tag.encode('utf-8')).digest()
|
||||
ss += ss
|
||||
|
|
|
@ -8,6 +8,7 @@ import random
|
|||
|
||||
from decimal import Decimal
|
||||
from test_framework.address import output_key_to_p2tr
|
||||
from test_framework.key import H_POINT
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import assert_equal
|
||||
from test_framework.descriptors import descsum_create
|
||||
|
@ -157,9 +158,6 @@ KEYS = [
|
|||
CHANGE_XPRV = "tprv8ZgxMBicQKsPcyDrWwiecVnTtFmfRwbfFqEfR4ZGWvq5aTTwLBWmAm5zrbMcYtb9gQNFfhRfqhhrBG37U3nhmXxEgeEPBJGHAPrHCrAd1WX"
|
||||
CHANGE_XPUB = "tpubD6NzVbkrYhZ4WSFeQbPF1uSaTHHbbGnZq8qShabZwCdUQwihxaLMMFhs2kidGF2qrRKiQVqw8VoyuTHj1bZqmMXMeciaU1gBjWA1sim2zUB"
|
||||
|
||||
# Point with no known discrete log.
|
||||
H_POINT = "50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0"
|
||||
|
||||
|
||||
def key(hex_key):
|
||||
"""Construct an x-only pubkey from its hex representation."""
|
||||
|
@ -301,9 +299,21 @@ class WalletTaprootTest(BitcoinTestFramework):
|
|||
test_balance = int(self.psbt_online.getbalance() * 100000000)
|
||||
ret_amnt = random.randrange(100000, test_balance)
|
||||
# Increase fee_rate to compensate for the wallet's inability to estimate fees for script path spends.
|
||||
psbt = self.psbt_online.walletcreatefundedpsbt([], [{self.boring.getnewaddress(): Decimal(ret_amnt) / 100000000}], None, {"subtractFeeFromOutputs":[0], "fee_rate": 200})['psbt']
|
||||
res = self.psbt_offline.walletprocesspsbt(psbt)
|
||||
assert(res['complete'])
|
||||
psbt = self.psbt_online.walletcreatefundedpsbt([], [{self.boring.getnewaddress(): Decimal(ret_amnt) / 100000000}], None, {"subtractFeeFromOutputs":[0], "fee_rate": 200, "change_type": "bech32m"})['psbt']
|
||||
res = self.psbt_offline.walletprocesspsbt(psbt=psbt, finalize=False)
|
||||
|
||||
decoded = self.psbt_offline.decodepsbt(res["psbt"])
|
||||
if pattern.startswith("tr("):
|
||||
for psbtin in decoded["inputs"]:
|
||||
assert "non_witness_utxo" not in psbtin
|
||||
assert "witness_utxo" in psbtin
|
||||
assert "taproot_internal_key" in psbtin
|
||||
assert "taproot_bip32_derivs" in psbtin
|
||||
assert "taproot_key_path_sig" in psbtin or "taproot_script_path_sigs" in psbtin
|
||||
if "taproot_script_path_sigs" in psbtin:
|
||||
assert "taproot_merkle_root" in psbtin
|
||||
assert "taproot_scripts" in psbtin
|
||||
|
||||
rawtx = self.nodes[0].finalizepsbt(res['psbt'])['hex']
|
||||
txid = self.nodes[0].sendrawtransaction(rawtx)
|
||||
self.generatetoaddress(self.nodes[0], 1, self.boring.getnewaddress(), sync_fun=self.no_op)
|
||||
|
@ -442,8 +452,7 @@ class WalletTaprootTest(BitcoinTestFramework):
|
|||
assert(self.rpc_online.gettransaction(txid)["confirmations"] > 0)
|
||||
|
||||
psbt = self.psbt_online.sendall(recipients=[self.boring.getnewaddress()], options={"psbt": True})["psbt"]
|
||||
res = self.psbt_offline.walletprocesspsbt(psbt)
|
||||
assert(res['complete'])
|
||||
res = self.psbt_offline.walletprocesspsbt(psbt=psbt, finalize=False)
|
||||
rawtx = self.nodes[0].finalizepsbt(res['psbt'])['hex']
|
||||
txid = self.nodes[0].sendrawtransaction(rawtx)
|
||||
self.generatetoaddress(self.nodes[0], 1, self.boring.getnewaddress(), sync_fun=self.no_op)
|
||||
|
|
Loading…
Add table
Reference in a new issue