mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-16 13:01:19 +02:00
Merge 5bf65ec66e into merged_master (Bitcoin PR bitcoin/bitcoin#22558)
This commit is contained in:
commit
2d07ef8fc1
17 changed files with 710 additions and 31 deletions
71
src/psbt.cpp
71
src/psbt.cpp
|
|
@ -386,6 +386,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)
|
||||
|
|
@ -415,6 +433,24 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
bool PSBTInput::Merge(const PSBTInput& input)
|
||||
|
|
@ -424,7 +460,6 @@ bool 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;
|
||||
}
|
||||
|
||||
|
|
@ -435,6 +470,9 @@ bool 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;
|
||||
|
|
@ -470,6 +508,10 @@ bool PSBTInput::Merge(const PSBTInput& input)
|
|||
|
||||
if (m_utxo_rangeproof.empty() && !input.m_utxo_rangeproof.empty()) m_utxo_rangeproof = input.m_utxo_rangeproof;
|
||||
|
||||
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;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -484,6 +526,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)
|
||||
|
|
@ -497,6 +548,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
|
||||
|
|
@ -512,6 +572,7 @@ bool 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;
|
||||
|
|
@ -542,6 +603,9 @@ bool PSBTOutput::Merge(const PSBTOutput& output)
|
|||
m_blind_asset_proof = output.m_blind_asset_proof;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -702,10 +766,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
|
|
@ -60,6 +60,12 @@ 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;
|
||||
// Elements proprietary types
|
||||
static constexpr uint8_t PSBT_ELEMENTS_IN_ISSUANCE_VALUE = 0x00;
|
||||
|
|
@ -91,6 +97,9 @@ static constexpr uint8_t PSBT_OUT_WITNESSSCRIPT = 0x01;
|
|||
static constexpr uint8_t PSBT_OUT_BIP32_DERIVATION = 0x02;
|
||||
static constexpr uint8_t PSBT_OUT_AMOUNT = 0x03;
|
||||
static constexpr uint8_t PSBT_OUT_SCRIPT = 0x04;
|
||||
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;
|
||||
// Elements proprietary types
|
||||
static constexpr uint8_t PSBT_ELEMENTS_OUT_VALUE_COMMITMENT = 0x01;
|
||||
|
|
@ -159,22 +168,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
|
||||
|
|
@ -201,17 +218,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)
|
||||
|
|
@ -245,6 +269,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;
|
||||
|
|
@ -352,6 +385,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
|
||||
|
|
@ -835,6 +915,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:
|
||||
{
|
||||
bool known = false;
|
||||
|
|
@ -1168,6 +1345,9 @@ struct PSBTOutput
|
|||
std::map<CPubKey, KeyOriginInfo> hd_keypaths;
|
||||
std::optional<CAmount> amount{std::nullopt};
|
||||
std::optional<CScript> script{std::nullopt};
|
||||
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;
|
||||
|
||||
|
|
@ -1289,6 +1469,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;
|
||||
|
|
@ -1373,6 +1587,59 @@ struct PSBTOutput
|
|||
script = sc;
|
||||
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:
|
||||
{
|
||||
bool known = false;
|
||||
|
|
@ -1520,6 +1787,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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -293,6 +293,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 {
|
||||
|
|
|
|||
|
|
@ -909,6 +909,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"},
|
||||
|
|
@ -964,7 +1001,30 @@ static RPCHelpMan decodepsbt()
|
|||
{RPCResult::Type::STR_HEX, "blind_value_proof", "Explicit value rangeproof that proves the value commitment matches the value"},
|
||||
{RPCResult::Type::STR_HEX, "blind_asset_proof", "Assert surjection proof that proves the assert commitment matches the asset"},
|
||||
{RPCResult::Type::STR, "status", "information about how the output has been blinded, if available"},
|
||||
{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"},
|
||||
}},
|
||||
|
|
@ -1366,6 +1426,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);
|
||||
|
|
@ -1506,6 +1632,47 @@ static RPCHelpMan decodepsbt()
|
|||
break;
|
||||
}
|
||||
|
||||
// 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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -154,6 +154,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;
|
||||
|
|
@ -173,6 +174,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));
|
||||
|
|
@ -209,17 +221,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);
|
||||
|
|
|
|||
|
|
@ -519,6 +519,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;
|
||||
|
|
@ -676,3 +677,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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -385,6 +385,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.
|
||||
|
|
|
|||
|
|
@ -2188,6 +2188,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);
|
||||
|
|
|
|||
|
|
@ -2183,6 +2183,35 @@ TransactionError CWallet::SignPSBT(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",
|
||||
"cHNidP8BAHECAAAAASd0Srq/MCf+DWzyOpbu4u+xiO9SMBlUWFiD5ptmJLJCAAAAAAD/////Anh8AQAAAAAAFgAUg6fjS9mf8DpJYu+KGhAbspVGHs5gawQqAQAAABYAFHrDad8bIOAz1hFmI5V7CsSfPFLoAAAAAAABASsA8gUqAQAAACJRIFosLPW1LPMfg60ujaY/8DGD7Nj2CcdRCuikjgORCgdXIhYC/jSQZMmNbiqFP6PJsSvYswShnBlcYO+n7iOTBG0/ojIZAHcrLadWAACAAQAAgAAAAIABAAAAAAAAAAAAAA==",
|
||||
"cHNidP8BAH0CAAAAASd0Srq/MCf+DWzyOpbu4u+xiO9SMBlUWFiD5ptmJLJCAAAAAAD/////Aoh7AQAAAAAAFgAUI4KHHH6EIaAAk/dU2RKB5nWHS59gawQqAQAAACJRIFosLPW1LPMfg60ujaY/8DGD7Nj2CcdRCuikjgORCgdXAAAAAAABASsA8gUqAQAAACJRIFosLPW1LPMfg60ujaY/8DGD7Nj2CcdRCuikjgORCgdXAAABBSEC/jSQZMmNbiqFP6PJsSvYswShnBlcYO+n7iOTBG0/ojIA",
|
||||
"cHNidP8BAH0CAAAAASd0Srq/MCf+DWzyOpbu4u+xiO9SMBlUWFiD5ptmJLJCAAAAAAD/////Aoh7AQAAAAAAFgAUI4KHHH6EIaAAk/dU2RKB5nWHS59gawQqAQAAACJRIFosLPW1LPMfg60ujaY/8DGD7Nj2CcdRCuikjgORCgdXAAAAAAABASsA8gUqAQAAACJRIFosLPW1LPMfg60ujaY/8DGD7Nj2CcdRCuikjgORCgdXAAAiBwL+NJBkyY1uKoU/o8mxK9izBKGcGVxg76fuI5MEbT+iMhkAdystp1YAAIABAACAAAAAgAEAAAAAAAAAAA==",
|
||||
"cHNidP8BAF4CAAAAAZvUh2UjC/mnLmYgAflyVW5U8Mb5f+tWvLVgDYF/aZUmAQAAAAD/////AUjmBSoBAAAAIlEgAw2k/OT32yjCyylRYx4ANxOFZZf+ljiCy1AOaBEsymMAAAAAAAEBKwDyBSoBAAAAIlEgwiR++/2SrEf29AuNQtFpF1oZ+p+hDkol1/NetN2FtpJCFAIssTrGgkjegGqmo2Wc88A+toIdCcgRSk6Gj+vehlu20s2XDhX1P8DIL5UP1WD/qRm3YXK+AXNoqJkTrwdPQAsJQIl1aqNznMxonsD886NgvjLMC1mxbpOh6LtGBXJrLKej/3BsQXZkljKyzGjh+RK4pXjjcZzncQiFx6lm9JvNQ8sAAA==",
|
||||
"cHNidP8BAF4CAAAAAZvUh2UjC/mnLmYgAflyVW5U8Mb5f+tWvLVgDYF/aZUmAQAAAAD/////AUjmBSoBAAAAIlEgAw2k/OT32yjCyylRYx4ANxOFZZf+ljiCy1AOaBEsymMAAAAAAAEBKwDyBSoBAAAAIlEgwiR++/2SrEf29AuNQtFpF1oZ+p+hDkol1/NetN2FtpJBFCyxOsaCSN6AaqajZZzzwD62gh0JyBFKToaP696GW7bSzZcOFfU/wMgvlQ/VYP+pGbdhcr4Bc2iomROvB09ACwlCiXVqo3OczGiewPzzo2C+MswLWbFuk6Hou0YFcmssp6P/cGxBdmSWMrLMaOH5ErileONxnOdxCIXHqWb0m81DywEBAAA=",
|
||||
"cHNidP8BAF4CAAAAAZvUh2UjC/mnLmYgAflyVW5U8Mb5f+tWvLVgDYF/aZUmAQAAAAD/////AUjmBSoBAAAAIlEgAw2k/OT32yjCyylRYx4ANxOFZZf+ljiCy1AOaBEsymMAAAAAAAEBKwDyBSoBAAAAIlEgwiR++/2SrEf29AuNQtFpF1oZ+p+hDkol1/NetN2FtpJBFCyxOsaCSN6AaqajZZzzwD62gh0JyBFKToaP696GW7bSzZcOFfU/wMgvlQ/VYP+pGbdhcr4Bc2iomROvB09ACwk5iXVqo3OczGiewPzzo2C+MswLWbFuk6Hou0YFcmssp6P/cGxBdmSWMrLMaOH5ErileONxnOdxCIXHqWb0m81DywAA",
|
||||
"cHNidP8BAF4CAAAAAZvUh2UjC/mnLmYgAflyVW5U8Mb5f+tWvLVgDYF/aZUmAQAAAAD/////AUjmBSoBAAAAIlEgAw2k/OT32yjCyylRYx4ANxOFZZf+ljiCy1AOaBEsymMAAAAAAAEBKwDyBSoBAAAAIlEgwiR++/2SrEf29AuNQtFpF1oZ+p+hDkol1/NetN2FtpJjFcFQkpt0waBJVLeLS2A16XpeB4paDyjsltVHv+6azoA6wG99YgWelJehpKJnVp2YdtpgEBr/OONSm5uTnOf5GulwEV8uSQr3zEXE94UR82BXzlxaXFYyWin7RN/CA/NW4fgAIyAssTrGgkjegGqmo2Wc88A+toIdCcgRSk6Gj+vehlu20qzAAAA=",
|
||||
"cHNidP8BAF4CAAAAAZvUh2UjC/mnLmYgAflyVW5U8Mb5f+tWvLVgDYF/aZUmAQAAAAD/////AUjmBSoBAAAAIlEgAw2k/OT32yjCyylRYx4ANxOFZZf+ljiCy1AOaBEsymMAAAAAAAEBKwDyBSoBAAAAIlEgwiR++/2SrEf29AuNQtFpF1oZ+p+hDkol1/NetN2FtpJhFcFQkpt0waBJVLeLS2A16XpeB4paDyjsltVHv+6azoA6wG99YgWelJehpKJnVp2YdtpgEBr/OONSm5uTnOf5GulwEV8uSQr3zEXE94UR82BXzlxaXFYyWin7RN/CA/NW4SMgLLE6xoJI3oBqpqNlnPPAPraCHQnIEUpOho/r3oZbttKswAAA"
|
||||
],
|
||||
"valid" : [
|
||||
"cHNidP8BAHUCAAAAASaBcTce3/KF6Tet7qSze3gADAVmy7OtZGQXE8pCFxv2AAAAAAD+////AtPf9QUAAAAAGXapFNDFmQPFusKGh2DpD9UhpGZap2UgiKwA4fUFAAAAABepFDVF5uM7gyxHBQ8k0+65PJwDlIvHh7MuEwAAAQD9pQEBAAAAAAECiaPHHqtNIOA3G7ukzGmPopXJRjr6Ljl/hTPMti+VZ+UBAAAAFxYAFL4Y0VKpsBIDna89p95PUzSe7LmF/////4b4qkOnHf8USIk6UwpyN+9rRgi7st0tAXHmOuxqSJC0AQAAABcWABT+Pp7xp0XpdNkCxDVZQ6vLNL1TU/////8CAMLrCwAAAAAZdqkUhc/xCX/Z4Ai7NK9wnGIZeziXikiIrHL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAkcwRAIgJxK+IuAnDzlPVoMR3HyppolwuAJf3TskAinwf4pfOiQCIAGLONfc0xTnNMkna9b7QPZzMlvEuqFEyADS8vAtsnZcASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUCSDBFAiEA0SuFLYXc2WHS9fSrZgZU327tzHlMDDPOXMMJ/7X85Y0CIGczio4OFyXBl/saiK9Z9R5E5CVbIBZ8hoQDHAXR8lkqASECI7cr7vCWXRC+B3jv7NYfysb3mk6haTkzgHNEZPhPKrMAAAAAAAAA",
|
||||
|
|
@ -44,7 +55,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,
|
||||
|
|
@ -1231,5 +1231,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()['bitcoin'] * 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.walletcreatefundedpsbt([], [{self.boring.getnewaddress(): self.psbt_online.getbalance()['bitcoin']}], None, {"subtractFeeFromOutputs": [0]})['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
Add a link
Reference in a new issue