aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/index/base.cpp3
-rw-r--r--src/net.cpp1
-rw-r--r--src/psbt.cpp19
-rw-r--r--src/psbt.h406
-rw-r--r--src/pubkey.cpp12
-rw-r--r--src/pubkey.h19
-rw-r--r--src/rpc/misc.cpp11
-rw-r--r--src/rpc/rawtransaction.cpp162
-rw-r--r--src/script/keyorigin.h19
-rw-r--r--src/script/sign.cpp1
-rw-r--r--src/script/sign.h76
-rw-r--r--src/script/signingprovider.h3
-rw-r--r--src/serialize.h13
-rw-r--r--src/test/fuzz/integer.cpp4
-rw-r--r--src/test/fuzz/parse_iso8601.cpp1
-rw-r--r--src/test/fuzz/script_sign.cpp5
-rw-r--r--src/wallet/rpc/addresses.cpp12
17 files changed, 661 insertions, 106 deletions
diff --git a/src/index/base.cpp b/src/index/base.cpp
index fc6dd77a72..8525dcbfa0 100644
--- a/src/index/base.cpp
+++ b/src/index/base.cpp
@@ -91,11 +91,12 @@ bool BaseIndex::Init()
const CBlockIndex* block = active_chain.Tip();
prune_violation = true;
// check backwards from the tip if we have all block data until we reach the indexes bestblock
- while (block_to_test && block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA)) {
+ while (block_to_test && block && (block->nStatus & BLOCK_HAVE_DATA)) {
if (block_to_test == block) {
prune_violation = false;
break;
}
+ assert(block->pprev);
block = block->pprev;
}
}
diff --git a/src/net.cpp b/src/net.cpp
index 2adccce11e..2e2aa9dcef 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -2899,7 +2899,6 @@ void CConnman::RecordBytesSent(uint64_t bytes)
nMaxOutboundTotalBytesSentInCycle = 0;
}
- // TODO, exclude peers with download permission
nMaxOutboundTotalBytesSentInCycle += bytes;
}
diff --git a/src/psbt.cpp b/src/psbt.cpp
index 6585766807..203e0a0bd3 100644
--- a/src/psbt.cpp
+++ b/src/psbt.cpp
@@ -32,6 +32,13 @@ bool PartiallySignedTransaction::Merge(const PartiallySignedTransaction& psbt)
for (unsigned int i = 0; i < outputs.size(); ++i) {
outputs[i].Merge(psbt.outputs[i]);
}
+ for (auto& xpub_pair : psbt.m_xpubs) {
+ if (m_xpubs.count(xpub_pair.first) == 0) {
+ m_xpubs[xpub_pair.first] = xpub_pair.second;
+ } else {
+ m_xpubs[xpub_pair.first].insert(xpub_pair.second.begin(), xpub_pair.second.end());
+ }
+ }
unknown.insert(psbt.unknown.begin(), psbt.unknown.end());
return true;
@@ -146,6 +153,10 @@ void PSBTInput::Merge(const PSBTInput& input)
}
partial_sigs.insert(input.partial_sigs.begin(), input.partial_sigs.end());
+ ripemd160_preimages.insert(input.ripemd160_preimages.begin(), input.ripemd160_preimages.end());
+ sha256_preimages.insert(input.sha256_preimages.begin(), input.sha256_preimages.end());
+ hash160_preimages.insert(input.hash160_preimages.begin(), input.hash160_preimages.end());
+ 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());
@@ -401,3 +412,11 @@ bool DecodeRawPSBT(PartiallySignedTransaction& psbt, const std::string& tx_data,
}
return true;
}
+
+uint32_t PartiallySignedTransaction::GetVersion() const
+{
+ if (m_version != std::nullopt) {
+ return *m_version;
+ }
+ return 0;
+}
diff --git a/src/psbt.h b/src/psbt.h
index 21daa050ea..43b1b249c5 100644
--- a/src/psbt.h
+++ b/src/psbt.h
@@ -10,8 +10,11 @@
#include <policy/feerate.h>
#include <primitives/transaction.h>
#include <pubkey.h>
+#include <script/keyorigin.h>
#include <script/sign.h>
#include <script/signingprovider.h>
+#include <span.h>
+#include <streams.h>
#include <optional>
@@ -20,6 +23,9 @@ static constexpr uint8_t PSBT_MAGIC_BYTES[5] = {'p', 's', 'b', 't', 0xff};
// Global types
static constexpr uint8_t PSBT_GLOBAL_UNSIGNED_TX = 0x00;
+static constexpr uint8_t PSBT_GLOBAL_XPUB = 0x01;
+static constexpr uint8_t PSBT_GLOBAL_VERSION = 0xFB;
+static constexpr uint8_t PSBT_GLOBAL_PROPRIETARY = 0xFC;
// Input types
static constexpr uint8_t PSBT_IN_NON_WITNESS_UTXO = 0x00;
@@ -31,11 +37,17 @@ static constexpr uint8_t PSBT_IN_WITNESSSCRIPT = 0x05;
static constexpr uint8_t PSBT_IN_BIP32_DERIVATION = 0x06;
static constexpr uint8_t PSBT_IN_SCRIPTSIG = 0x07;
static constexpr uint8_t PSBT_IN_SCRIPTWITNESS = 0x08;
+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_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_PROPRIETARY = 0xFC;
// The separator is 0x00. Reading this in means that the unserializer can interpret it
// as a 0 length key which indicates that this is the separator. The separator has no value.
@@ -45,6 +57,113 @@ static constexpr uint8_t PSBT_SEPARATOR = 0x00;
// to prevent reading a stream indefinitely and running out of memory.
const std::streamsize MAX_FILE_SIZE_PSBT = 100000000; // 100 MiB
+// PSBT version number
+static constexpr uint32_t PSBT_HIGHEST_VERSION = 0;
+
+/** A structure for PSBT proprietary types */
+struct PSBTProprietary
+{
+ uint64_t subtype;
+ std::vector<unsigned char> identifier;
+ std::vector<unsigned char> key;
+ std::vector<unsigned char> value;
+
+ bool operator<(const PSBTProprietary &b) const {
+ return key < b.key;
+ }
+ bool operator==(const PSBTProprietary &b) const {
+ return key == b.key;
+ }
+};
+
+// Takes a stream and multiple arguments and serializes them as if first serialized into a vector and then into the stream
+// The resulting output into the stream has the total serialized length of all of the objects followed by all objects concatenated with each other.
+template<typename Stream, typename... X>
+void SerializeToVector(Stream& s, const X&... args)
+{
+ WriteCompactSize(s, GetSerializeSizeMany(s.GetVersion(), args...));
+ SerializeMany(s, args...);
+}
+
+// Takes a stream and multiple arguments and unserializes them first as a vector then each object individually in the order provided in the arguments
+template<typename Stream, typename... X>
+void UnserializeFromVector(Stream& s, X&... args)
+{
+ size_t expected_size = ReadCompactSize(s);
+ size_t remaining_before = s.size();
+ UnserializeMany(s, args...);
+ size_t remaining_after = s.size();
+ if (remaining_after + expected_size != remaining_before) {
+ throw std::ios_base::failure("Size of value was not the stated size");
+ }
+}
+
+// Deserialize an individual HD keypath to a stream
+template<typename Stream>
+void DeserializeHDKeypath(Stream& s, KeyOriginInfo& hd_keypath)
+{
+ // Read in key path
+ uint64_t value_len = ReadCompactSize(s);
+ if (value_len % 4 || value_len == 0) {
+ throw std::ios_base::failure("Invalid length for HD key path");
+ }
+
+ s >> hd_keypath.fingerprint;
+ for (unsigned int i = 4; i < value_len; i += sizeof(uint32_t)) {
+ uint32_t index;
+ s >> index;
+ hd_keypath.path.push_back(index);
+ }
+}
+
+// Deserialize HD keypaths into a map
+template<typename Stream>
+void DeserializeHDKeypaths(Stream& s, const std::vector<unsigned char>& key, std::map<CPubKey, KeyOriginInfo>& hd_keypaths)
+{
+ // Make sure that the key is the size of pubkey + 1
+ if (key.size() != CPubKey::SIZE + 1 && key.size() != CPubKey::COMPRESSED_SIZE + 1) {
+ throw std::ios_base::failure("Size of key was not the expected size for the type BIP32 keypath");
+ }
+ // Read in the pubkey from key
+ CPubKey pubkey(key.begin() + 1, key.end());
+ if (!pubkey.IsFullyValid()) {
+ throw std::ios_base::failure("Invalid pubkey");
+ }
+ if (hd_keypaths.count(pubkey) > 0) {
+ throw std::ios_base::failure("Duplicate Key, pubkey derivation path already provided");
+ }
+
+ KeyOriginInfo keypath;
+ DeserializeHDKeypath(s, keypath);
+
+ // Add to map
+ hd_keypaths.emplace(pubkey, std::move(keypath));
+}
+
+// Serialize an individual HD keypath to a stream
+template<typename Stream>
+void SerializeHDKeypath(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 HD keypaths to a stream from a map
+template<typename Stream>
+void SerializeHDKeypaths(Stream& s, const std::map<CPubKey, KeyOriginInfo>& hd_keypaths, CompactSizeWriter type)
+{
+ for (auto keypath_pair : hd_keypaths) {
+ if (!keypath_pair.first.IsValid()) {
+ throw std::ios_base::failure("Invalid CPubKey being serialized");
+ }
+ SerializeToVector(s, type, Span{keypath_pair.first});
+ SerializeHDKeypath(s, keypath_pair.second);
+ }
+}
+
/** A structure for PSBTs which contain per-input information */
struct PSBTInput
{
@@ -56,7 +175,12 @@ struct PSBTInput
CScriptWitness final_script_witness;
std::map<CPubKey, KeyOriginInfo> hd_keypaths;
std::map<CKeyID, SigPair> partial_sigs;
+ std::map<uint160, std::vector<unsigned char>> ripemd160_preimages;
+ 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;
std::map<std::vector<unsigned char>, std::vector<unsigned char>> unknown;
+ std::set<PSBTProprietary> m_proprietary;
std::optional<int> sighash_type;
bool IsNull() const;
@@ -69,55 +193,85 @@ struct PSBTInput
inline void Serialize(Stream& s) const {
// Write the utxo
if (non_witness_utxo) {
- SerializeToVector(s, PSBT_IN_NON_WITNESS_UTXO);
+ SerializeToVector(s, CompactSizeWriter(PSBT_IN_NON_WITNESS_UTXO));
OverrideStream<Stream> os(&s, s.GetType(), s.GetVersion() | SERIALIZE_TRANSACTION_NO_WITNESS);
SerializeToVector(os, non_witness_utxo);
}
if (!witness_utxo.IsNull()) {
- SerializeToVector(s, PSBT_IN_WITNESS_UTXO);
+ SerializeToVector(s, CompactSizeWriter(PSBT_IN_WITNESS_UTXO));
SerializeToVector(s, witness_utxo);
}
if (final_script_sig.empty() && final_script_witness.IsNull()) {
// Write any partial signatures
for (auto sig_pair : partial_sigs) {
- SerializeToVector(s, PSBT_IN_PARTIAL_SIG, Span{sig_pair.second.first});
+ SerializeToVector(s, CompactSizeWriter(PSBT_IN_PARTIAL_SIG), Span{sig_pair.second.first});
s << sig_pair.second.second;
}
// Write the sighash type
if (sighash_type != std::nullopt) {
- SerializeToVector(s, PSBT_IN_SIGHASH);
+ SerializeToVector(s, CompactSizeWriter(PSBT_IN_SIGHASH));
SerializeToVector(s, *sighash_type);
}
// Write the redeem script
if (!redeem_script.empty()) {
- SerializeToVector(s, PSBT_IN_REDEEMSCRIPT);
+ SerializeToVector(s, CompactSizeWriter(PSBT_IN_REDEEMSCRIPT));
s << redeem_script;
}
// Write the witness script
if (!witness_script.empty()) {
- SerializeToVector(s, PSBT_IN_WITNESSSCRIPT);
+ SerializeToVector(s, CompactSizeWriter(PSBT_IN_WITNESSSCRIPT));
s << witness_script;
}
// Write any hd keypaths
- SerializeHDKeypaths(s, hd_keypaths, PSBT_IN_BIP32_DERIVATION);
+ SerializeHDKeypaths(s, hd_keypaths, CompactSizeWriter(PSBT_IN_BIP32_DERIVATION));
+
+ // Write any ripemd160 preimage
+ for (const auto& [hash, preimage] : ripemd160_preimages) {
+ SerializeToVector(s, CompactSizeWriter(PSBT_IN_RIPEMD160), Span{hash});
+ s << preimage;
+ }
+
+ // Write any sha256 preimage
+ for (const auto& [hash, preimage] : sha256_preimages) {
+ SerializeToVector(s, CompactSizeWriter(PSBT_IN_SHA256), Span{hash});
+ s << preimage;
+ }
+
+ // Write any hash160 preimage
+ for (const auto& [hash, preimage] : hash160_preimages) {
+ SerializeToVector(s, CompactSizeWriter(PSBT_IN_HASH160), Span{hash});
+ s << preimage;
+ }
+
+ // Write any hash256 preimage
+ for (const auto& [hash, preimage] : hash256_preimages) {
+ SerializeToVector(s, CompactSizeWriter(PSBT_IN_HASH256), Span{hash});
+ s << preimage;
+ }
}
// Write script sig
if (!final_script_sig.empty()) {
- SerializeToVector(s, PSBT_IN_SCRIPTSIG);
+ SerializeToVector(s, CompactSizeWriter(PSBT_IN_SCRIPTSIG));
s << final_script_sig;
}
// write script witness
if (!final_script_witness.IsNull()) {
- SerializeToVector(s, PSBT_IN_SCRIPTWITNESS);
+ SerializeToVector(s, CompactSizeWriter(PSBT_IN_SCRIPTWITNESS));
SerializeToVector(s, final_script_witness.stack);
}
+ // Write proprietary things
+ for (const auto& entry : m_proprietary) {
+ s << entry.key;
+ s << entry.value;
+ }
+
// Write unknown things
for (auto& entry : unknown) {
s << entry.first;
@@ -147,8 +301,9 @@ struct PSBTInput
break;
}
- // First byte of key is the type
- unsigned char type = key[0];
+ // Type is compact size uint at beginning of key
+ SpanReader skey(s.GetType(), s.GetVersion(), key);
+ uint64_t type = ReadCompactSize(skey);
// Do stuff based on type
switch(type) {
@@ -250,6 +405,104 @@ struct PSBTInput
UnserializeFromVector(s, final_script_witness.stack);
break;
}
+ case PSBT_IN_RIPEMD160:
+ {
+ // Make sure that the key is the size of a ripemd160 hash + 1
+ if (key.size() != CRIPEMD160::OUTPUT_SIZE + 1) {
+ throw std::ios_base::failure("Size of key was not the expected size for the type ripemd160 preimage");
+ }
+ // Read in the hash from key
+ std::vector<unsigned char> hash_vec(key.begin() + 1, key.end());
+ uint160 hash(hash_vec);
+ if (ripemd160_preimages.count(hash) > 0) {
+ throw std::ios_base::failure("Duplicate Key, input ripemd160 preimage already provided");
+ }
+
+ // Read in the preimage from value
+ std::vector<unsigned char> preimage;
+ s >> preimage;
+
+ // Add to preimages list
+ ripemd160_preimages.emplace(hash, std::move(preimage));
+ break;
+ }
+ case PSBT_IN_SHA256:
+ {
+ // Make sure that the key is the size of a sha256 hash + 1
+ if (key.size() != CSHA256::OUTPUT_SIZE + 1) {
+ throw std::ios_base::failure("Size of key was not the expected size for the type sha256 preimage");
+ }
+ // Read in the hash from key
+ std::vector<unsigned char> hash_vec(key.begin() + 1, key.end());
+ uint256 hash(hash_vec);
+ if (sha256_preimages.count(hash) > 0) {
+ throw std::ios_base::failure("Duplicate Key, input sha256 preimage already provided");
+ }
+
+ // Read in the preimage from value
+ std::vector<unsigned char> preimage;
+ s >> preimage;
+
+ // Add to preimages list
+ sha256_preimages.emplace(hash, std::move(preimage));
+ break;
+ }
+ case PSBT_IN_HASH160:
+ {
+ // Make sure that the key is the size of a hash160 hash + 1
+ if (key.size() != CHash160::OUTPUT_SIZE + 1) {
+ throw std::ios_base::failure("Size of key was not the expected size for the type hash160 preimage");
+ }
+ // Read in the hash from key
+ std::vector<unsigned char> hash_vec(key.begin() + 1, key.end());
+ uint160 hash(hash_vec);
+ if (hash160_preimages.count(hash) > 0) {
+ throw std::ios_base::failure("Duplicate Key, input hash160 preimage already provided");
+ }
+
+ // Read in the preimage from value
+ std::vector<unsigned char> preimage;
+ s >> preimage;
+
+ // Add to preimages list
+ hash160_preimages.emplace(hash, std::move(preimage));
+ break;
+ }
+ case PSBT_IN_HASH256:
+ {
+ // Make sure that the key is the size of a hash256 hash + 1
+ if (key.size() != CHash256::OUTPUT_SIZE + 1) {
+ throw std::ios_base::failure("Size of key was not the expected size for the type hash256 preimage");
+ }
+ // Read in the hash from key
+ std::vector<unsigned char> hash_vec(key.begin() + 1, key.end());
+ uint256 hash(hash_vec);
+ if (hash256_preimages.count(hash) > 0) {
+ throw std::ios_base::failure("Duplicate Key, input hash256 preimage already provided");
+ }
+
+ // Read in the preimage from value
+ std::vector<unsigned char> preimage;
+ s >> preimage;
+
+ // Add to preimages list
+ hash256_preimages.emplace(hash, std::move(preimage));
+ break;
+ }
+ case PSBT_IN_PROPRIETARY:
+ {
+ PSBTProprietary this_prop;
+ skey >> this_prop.identifier;
+ this_prop.subtype = ReadCompactSize(skey);
+ this_prop.key = key;
+
+ if (m_proprietary.count(this_prop) > 0) {
+ throw std::ios_base::failure("Duplicate Key, proprietary key already found");
+ }
+ s >> this_prop.value;
+ m_proprietary.insert(this_prop);
+ break;
+ }
// Unknown stuff
default:
if (unknown.count(key) > 0) {
@@ -281,6 +534,7 @@ struct PSBTOutput
CScript witness_script;
std::map<CPubKey, KeyOriginInfo> hd_keypaths;
std::map<std::vector<unsigned char>, std::vector<unsigned char>> unknown;
+ std::set<PSBTProprietary> m_proprietary;
bool IsNull() const;
void FillSignatureData(SignatureData& sigdata) const;
@@ -292,18 +546,24 @@ struct PSBTOutput
inline void Serialize(Stream& s) const {
// Write the redeem script
if (!redeem_script.empty()) {
- SerializeToVector(s, PSBT_OUT_REDEEMSCRIPT);
+ SerializeToVector(s, CompactSizeWriter(PSBT_OUT_REDEEMSCRIPT));
s << redeem_script;
}
// Write the witness script
if (!witness_script.empty()) {
- SerializeToVector(s, PSBT_OUT_WITNESSSCRIPT);
+ SerializeToVector(s, CompactSizeWriter(PSBT_OUT_WITNESSSCRIPT));
s << witness_script;
}
// Write any hd keypaths
- SerializeHDKeypaths(s, hd_keypaths, PSBT_OUT_BIP32_DERIVATION);
+ SerializeHDKeypaths(s, hd_keypaths, CompactSizeWriter(PSBT_OUT_BIP32_DERIVATION));
+
+ // Write proprietary things
+ for (const auto& entry : m_proprietary) {
+ s << entry.key;
+ s << entry.value;
+ }
// Write unknown things
for (auto& entry : unknown) {
@@ -334,8 +594,9 @@ struct PSBTOutput
break;
}
- // First byte of key is the type
- unsigned char type = key[0];
+ // Type is compact size uint at beginning of key
+ SpanReader skey(s.GetType(), s.GetVersion(), key);
+ uint64_t type = ReadCompactSize(skey);
// Do stuff based on type
switch(type) {
@@ -364,6 +625,20 @@ struct PSBTOutput
DeserializeHDKeypaths(s, key, hd_keypaths);
break;
}
+ case PSBT_OUT_PROPRIETARY:
+ {
+ PSBTProprietary this_prop;
+ skey >> this_prop.identifier;
+ this_prop.subtype = ReadCompactSize(skey);
+ this_prop.key = key;
+
+ if (m_proprietary.count(this_prop) > 0) {
+ throw std::ios_base::failure("Duplicate Key, proprietary key already found");
+ }
+ s >> this_prop.value;
+ m_proprietary.insert(this_prop);
+ break;
+ }
// Unknown stuff
default: {
if (unknown.count(key) > 0) {
@@ -393,11 +668,17 @@ struct PSBTOutput
struct PartiallySignedTransaction
{
std::optional<CMutableTransaction> tx;
+ // We use a vector of CExtPubKey in the event that there happens to be the same KeyOriginInfos for different CExtPubKeys
+ // Note that this map swaps the key and values from the serialization
+ std::map<KeyOriginInfo, std::set<CExtPubKey>> m_xpubs;
std::vector<PSBTInput> inputs;
std::vector<PSBTOutput> outputs;
std::map<std::vector<unsigned char>, std::vector<unsigned char>> unknown;
+ std::optional<uint32_t> m_version;
+ std::set<PSBTProprietary> m_proprietary;
bool IsNull() const;
+ uint32_t GetVersion() const;
/** Merge psbt into this. The two psbts must have the same underlying CTransaction (i.e. the
* same actual Bitcoin transaction.) Returns true if the merge succeeded, false otherwise. */
@@ -422,12 +703,36 @@ struct PartiallySignedTransaction
s << PSBT_MAGIC_BYTES;
// unsigned tx flag
- SerializeToVector(s, PSBT_GLOBAL_UNSIGNED_TX);
+ SerializeToVector(s, CompactSizeWriter(PSBT_GLOBAL_UNSIGNED_TX));
// Write serialized tx to a stream
OverrideStream<Stream> os(&s, s.GetType(), s.GetVersion() | SERIALIZE_TRANSACTION_NO_WITNESS);
SerializeToVector(os, *tx);
+ // Write xpubs
+ for (const auto& xpub_pair : m_xpubs) {
+ for (const auto& xpub : xpub_pair.second) {
+ unsigned char ser_xpub[BIP32_EXTKEY_WITH_VERSION_SIZE];
+ xpub.EncodeWithVersion(ser_xpub);
+ // Note that the serialization swaps the key and value
+ // The xpub is the key (for uniqueness) while the path is the value
+ SerializeToVector(s, PSBT_GLOBAL_XPUB, ser_xpub);
+ SerializeHDKeypath(s, xpub_pair.first);
+ }
+ }
+
+ // PSBT version
+ if (GetVersion() > 0) {
+ SerializeToVector(s, CompactSizeWriter(PSBT_GLOBAL_VERSION));
+ SerializeToVector(s, *m_version);
+ }
+
+ // Write proprietary things
+ for (const auto& entry : m_proprietary) {
+ s << entry.key;
+ s << entry.value;
+ }
+
// Write the unknown things
for (auto& entry : unknown) {
s << entry.first;
@@ -460,6 +765,9 @@ struct PartiallySignedTransaction
// Used for duplicate key detection
std::set<std::vector<unsigned char>> key_lookup;
+ // Track the global xpubs we have already seen. Just for sanity checking
+ std::set<CExtPubKey> global_xpubs;
+
// Read global data
bool found_sep = false;
while(!s.empty()) {
@@ -474,8 +782,9 @@ struct PartiallySignedTransaction
break;
}
- // First byte of key is the type
- unsigned char type = key[0];
+ // Type is compact size uint at beginning of key
+ SpanReader skey(s.GetType(), s.GetVersion(), key);
+ uint64_t type = ReadCompactSize(skey);
// Do stuff based on type
switch(type) {
@@ -499,6 +808,65 @@ struct PartiallySignedTransaction
}
break;
}
+ case PSBT_GLOBAL_XPUB:
+ {
+ if (key.size() != BIP32_EXTKEY_WITH_VERSION_SIZE + 1) {
+ throw std::ios_base::failure("Size of key was not the expected size for the type global xpub");
+ }
+ // Read in the xpub from key
+ CExtPubKey xpub;
+ xpub.DecodeWithVersion(&key.data()[1]);
+ if (!xpub.pubkey.IsFullyValid()) {
+ throw std::ios_base::failure("Invalid pubkey");
+ }
+ if (global_xpubs.count(xpub) > 0) {
+ throw std::ios_base::failure("Duplicate key, global xpub already provided");
+ }
+ global_xpubs.insert(xpub);
+ // Read in the keypath from stream
+ KeyOriginInfo keypath;
+ DeserializeHDKeypath(s, keypath);
+
+ // Note that we store these swapped to make searches faster.
+ // Serialization uses xpub -> keypath to enqure key uniqueness
+ if (m_xpubs.count(keypath) == 0) {
+ // Make a new set to put the xpub in
+ m_xpubs[keypath] = {xpub};
+ } else {
+ // Insert xpub into existing set
+ m_xpubs[keypath].insert(xpub);
+ }
+ break;
+ }
+ case PSBT_GLOBAL_VERSION:
+ {
+ if (m_version) {
+ throw std::ios_base::failure("Duplicate Key, version already provided");
+ } else if (key.size() != 1) {
+ throw std::ios_base::failure("Global version key is more than one byte type");
+ }
+ uint32_t v;
+ UnserializeFromVector(s, v);
+ m_version = v;
+ if (*m_version > PSBT_HIGHEST_VERSION) {
+ throw std::ios_base::failure("Unsupported version number");
+ }
+ break;
+ }
+ case PSBT_GLOBAL_PROPRIETARY:
+ {
+ PSBTProprietary this_prop;
+ skey >> this_prop.identifier;
+ this_prop.subtype = ReadCompactSize(skey);
+ this_prop.key = key;
+
+ if (m_proprietary.count(this_prop) > 0) {
+ throw std::ios_base::failure("Duplicate Key, proprietary key already found");
+ }
+ s >> this_prop.value;
+ m_proprietary.insert(this_prop);
+ break;
+ }
// Unknown stuff
default: {
if (unknown.count(key) > 0) {
diff --git a/src/pubkey.cpp b/src/pubkey.cpp
index 956ff2b34a..b7dfb6d83f 100644
--- a/src/pubkey.cpp
+++ b/src/pubkey.cpp
@@ -352,6 +352,18 @@ void CExtPubKey::Decode(const unsigned char code[BIP32_EXTKEY_SIZE]) {
if ((nDepth == 0 && (nChild != 0 || ReadLE32(vchFingerprint) != 0)) || !pubkey.IsFullyValid()) pubkey = CPubKey();
}
+void CExtPubKey::EncodeWithVersion(unsigned char code[BIP32_EXTKEY_WITH_VERSION_SIZE]) const
+{
+ memcpy(code, version, 4);
+ Encode(&code[4]);
+}
+
+void CExtPubKey::DecodeWithVersion(const unsigned char code[BIP32_EXTKEY_WITH_VERSION_SIZE])
+{
+ memcpy(version, code, 4);
+ Decode(&code[4]);
+}
+
bool CExtPubKey::Derive(CExtPubKey &out, unsigned int _nChild) const {
out.nDepth = nDepth + 1;
CKeyID id = pubkey.GetID();
diff --git a/src/pubkey.h b/src/pubkey.h
index 2453c92d92..204e96f49e 100644
--- a/src/pubkey.h
+++ b/src/pubkey.h
@@ -17,6 +17,7 @@
#include <vector>
const unsigned int BIP32_EXTKEY_SIZE = 74;
+const unsigned int BIP32_EXTKEY_WITH_VERSION_SIZE = 78;
/** A reference to a CKey: the Hash160 of its serialized public key */
class CKeyID : public uint160
@@ -129,6 +130,11 @@ public:
return a.vch[0] < b.vch[0] ||
(a.vch[0] == b.vch[0] && memcmp(a.vch, b.vch, a.size()) < 0);
}
+ friend bool operator>(const CPubKey& a, const CPubKey& b)
+ {
+ return a.vch[0] > b.vch[0] ||
+ (a.vch[0] == b.vch[0] && memcmp(a.vch, b.vch, a.size()) > 0);
+ }
//! Implement serialization, as if this was a byte vector.
template <typename Stream>
@@ -283,6 +289,7 @@ public:
};
struct CExtPubKey {
+ unsigned char version[4];
unsigned char nDepth;
unsigned char vchFingerprint[4];
unsigned int nChild;
@@ -303,8 +310,20 @@ struct CExtPubKey {
return !(a == b);
}
+ friend bool operator<(const CExtPubKey &a, const CExtPubKey &b)
+ {
+ if (a.pubkey < b.pubkey) {
+ return true;
+ } else if (a.pubkey > b.pubkey) {
+ return false;
+ }
+ return a.chaincode < b.chaincode;
+ }
+
void Encode(unsigned char code[BIP32_EXTKEY_SIZE]) const;
void Decode(const unsigned char code[BIP32_EXTKEY_SIZE]);
+ void EncodeWithVersion(unsigned char code[BIP32_EXTKEY_WITH_VERSION_SIZE]) const;
+ void DecodeWithVersion(const unsigned char code[BIP32_EXTKEY_WITH_VERSION_SIZE]);
bool Derive(CExtPubKey& out, unsigned int nChild) const;
};
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index a5ee538f67..2761c69167 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -114,6 +114,10 @@ static RPCHelpMan createmultisig()
{RPCResult::Type::STR, "address", "The value of the new multisig address."},
{RPCResult::Type::STR_HEX, "redeemScript", "The string value of the hex-encoded redemption script."},
{RPCResult::Type::STR, "descriptor", "The descriptor for this multisig"},
+ {RPCResult::Type::ARR, "warnings", /* optional */ true, "Any warnings resulting from the creation of this multisig",
+ {
+ {RPCResult::Type::STR, "", ""},
+ }},
}
},
RPCExamples{
@@ -162,6 +166,13 @@ static RPCHelpMan createmultisig()
result.pushKV("redeemScript", HexStr(inner));
result.pushKV("descriptor", descriptor->ToString());
+ UniValue warnings(UniValue::VARR);
+ if (!request.params[2].isNull() && OutputTypeFromDestination(dest) != output_type) {
+ // Only warns if the user has explicitly chosen an address type we cannot generate
+ warnings.push_back("Unable to make chosen address type, please ensure no uncompressed public keys are present.");
+ }
+ if (warnings.size()) result.pushKV("warnings", warnings);
+
return result;
},
};
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 78c1596219..f2e8104e14 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -3,6 +3,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <base58.h>
#include <chain.h>
#include <coins.h>
#include <consensus/amount.h>
@@ -1075,6 +1076,26 @@ static RPCHelpMan decodepsbt()
{
{RPCResult::Type::ELISION, "", "The layout is the same as the output of decoderawtransaction."},
}},
+ {RPCResult::Type::ARR, "global_xpubs", "",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR, "xpub", "The extended public key this path corresponds to"},
+ {RPCResult::Type::STR_HEX, "master_fingerprint", "The fingerprint of the master key"},
+ {RPCResult::Type::STR, "path", "The path"},
+ }},
+ }},
+ {RPCResult::Type::NUM, "psbt_version", "The PSBT version number. Not to be confused with the unsigned transaction version"},
+ {RPCResult::Type::ARR, "proprietary", "The global proprietary map",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR_HEX, "identifier", "The hex string for the proprietary identifier"},
+ {RPCResult::Type::NUM, "subtype", "The number for the subtype"},
+ {RPCResult::Type::STR_HEX, "key", "The hex for the key"},
+ {RPCResult::Type::STR_HEX, "value", "The hex for the value"},
+ }},
+ }},
{RPCResult::Type::OBJ_DYN, "unknown", "The unknown global fields",
{
{RPCResult::Type::STR_HEX, "key", "(key-value pair) An unknown key-value pair"},
@@ -1133,10 +1154,36 @@ static RPCHelpMan decodepsbt()
{
{RPCResult::Type::STR_HEX, "", "hex-encoded witness data (if any)"},
}},
- {RPCResult::Type::OBJ_DYN, "unknown", /*optional=*/true, "The unknown global fields",
+ {RPCResult::Type::OBJ_DYN, "ripemd160_preimages", /*optional=*/ true, "",
+ {
+ {RPCResult::Type::STR, "hash", "The hash and preimage that corresponds to it."},
+ }},
+ {RPCResult::Type::OBJ_DYN, "sha256_preimages", /*optional=*/ true, "",
+ {
+ {RPCResult::Type::STR, "hash", "The hash and preimage that corresponds to it."},
+ }},
+ {RPCResult::Type::OBJ_DYN, "hash160_preimages", /*optional=*/ true, "",
+ {
+ {RPCResult::Type::STR, "hash", "The hash and preimage that corresponds to it."},
+ }},
+ {RPCResult::Type::OBJ_DYN, "hash256_preimages", /*optional=*/ true, "",
+ {
+ {RPCResult::Type::STR, "hash", "The hash and preimage that corresponds to it."},
+ }},
+ {RPCResult::Type::OBJ_DYN, "unknown", /*optional=*/ true, "The unknown input fields",
{
{RPCResult::Type::STR_HEX, "key", "(key-value pair) An unknown key-value pair"},
}},
+ {RPCResult::Type::ARR, "proprietary", "The input proprietary map",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR_HEX, "identifier", "The hex string for the proprietary identifier"},
+ {RPCResult::Type::NUM, "subtype", "The number for the subtype"},
+ {RPCResult::Type::STR_HEX, "key", "The hex for the key"},
+ {RPCResult::Type::STR_HEX, "value", "The hex for the value"},
+ }},
+ }},
}},
}},
{RPCResult::Type::ARR, "outputs", "",
@@ -1168,6 +1215,16 @@ static RPCHelpMan decodepsbt()
{
{RPCResult::Type::STR_HEX, "key", "(key-value pair) An unknown key-value pair"},
}},
+ {RPCResult::Type::ARR, "proprietary", "The output proprietary map",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR_HEX, "identifier", "The hex string for the proprietary identifier"},
+ {RPCResult::Type::NUM, "subtype", "The number for the subtype"},
+ {RPCResult::Type::STR_HEX, "key", "The hex for the key"},
+ {RPCResult::Type::STR_HEX, "value", "The hex for the value"},
+ }},
+ }},
}},
}},
{RPCResult::Type::STR_AMOUNT, "fee", /*optional=*/true, "The transaction fee paid if all UTXOs slots in the PSBT have been filled."},
@@ -1194,6 +1251,38 @@ static RPCHelpMan decodepsbt()
TxToUniv(CTransaction(*psbtx.tx), uint256(), tx_univ, false);
result.pushKV("tx", tx_univ);
+ // Add the global xpubs
+ UniValue global_xpubs(UniValue::VARR);
+ for (std::pair<KeyOriginInfo, std::set<CExtPubKey>> xpub_pair : psbtx.m_xpubs) {
+ for (auto& xpub : xpub_pair.second) {
+ std::vector<unsigned char> ser_xpub;
+ ser_xpub.assign(BIP32_EXTKEY_WITH_VERSION_SIZE, 0);
+ xpub.EncodeWithVersion(ser_xpub.data());
+
+ UniValue keypath(UniValue::VOBJ);
+ keypath.pushKV("xpub", EncodeBase58Check(ser_xpub));
+ keypath.pushKV("master_fingerprint", HexStr(Span<unsigned char>(xpub_pair.first.fingerprint, xpub_pair.first.fingerprint + 4)));
+ keypath.pushKV("path", WriteHDKeypath(xpub_pair.first.path));
+ global_xpubs.push_back(keypath);
+ }
+ }
+ result.pushKV("global_xpubs", global_xpubs);
+
+ // PSBT version
+ result.pushKV("psbt_version", static_cast<uint64_t>(psbtx.GetVersion()));
+
+ // Proprietary
+ UniValue proprietary(UniValue::VARR);
+ for (const auto& entry : psbtx.m_proprietary) {
+ UniValue this_prop(UniValue::VOBJ);
+ this_prop.pushKV("identifier", HexStr(entry.identifier));
+ this_prop.pushKV("subtype", entry.subtype);
+ this_prop.pushKV("key", HexStr(entry.key));
+ this_prop.pushKV("value", HexStr(entry.value));
+ proprietary.push_back(this_prop);
+ }
+ result.pushKV("proprietary", proprietary);
+
// Unknown data
UniValue unknowns(UniValue::VOBJ);
for (auto entry : psbtx.unknown) {
@@ -1300,6 +1389,56 @@ static RPCHelpMan decodepsbt()
in.pushKV("final_scriptwitness", txinwitness);
}
+ // Ripemd160 hash preimages
+ if (!input.ripemd160_preimages.empty()) {
+ UniValue ripemd160_preimages(UniValue::VOBJ);
+ for (const auto& [hash, preimage] : input.ripemd160_preimages) {
+ ripemd160_preimages.pushKV(HexStr(hash), HexStr(preimage));
+ }
+ in.pushKV("ripemd160_preimages", ripemd160_preimages);
+ }
+
+ // Sha256 hash preimages
+ if (!input.sha256_preimages.empty()) {
+ UniValue sha256_preimages(UniValue::VOBJ);
+ for (const auto& [hash, preimage] : input.sha256_preimages) {
+ sha256_preimages.pushKV(HexStr(hash), HexStr(preimage));
+ }
+ in.pushKV("sha256_preimages", sha256_preimages);
+ }
+
+ // Hash160 hash preimages
+ if (!input.hash160_preimages.empty()) {
+ UniValue hash160_preimages(UniValue::VOBJ);
+ for (const auto& [hash, preimage] : input.hash160_preimages) {
+ hash160_preimages.pushKV(HexStr(hash), HexStr(preimage));
+ }
+ in.pushKV("hash160_preimages", hash160_preimages);
+ }
+
+ // Hash256 hash preimages
+ if (!input.hash256_preimages.empty()) {
+ UniValue hash256_preimages(UniValue::VOBJ);
+ for (const auto& [hash, preimage] : input.hash256_preimages) {
+ hash256_preimages.pushKV(HexStr(hash), HexStr(preimage));
+ }
+ in.pushKV("hash256_preimages", hash256_preimages);
+ }
+
+ // Proprietary
+ if (!input.m_proprietary.empty()) {
+ UniValue proprietary(UniValue::VARR);
+ for (const auto& entry : input.m_proprietary) {
+ UniValue this_prop(UniValue::VOBJ);
+ this_prop.pushKV("identifier", HexStr(entry.identifier));
+ this_prop.pushKV("subtype", entry.subtype);
+ this_prop.pushKV("key", HexStr(entry.key));
+ this_prop.pushKV("value", HexStr(entry.value));
+ proprietary.push_back(this_prop);
+ }
+ in.pushKV("proprietary", proprietary);
+ }
+
// Unknown data
if (input.unknown.size() > 0) {
UniValue unknowns(UniValue::VOBJ);
@@ -1344,6 +1483,20 @@ static RPCHelpMan decodepsbt()
out.pushKV("bip32_derivs", keypaths);
}
+ // Proprietary
+ if (!output.m_proprietary.empty()) {
+ UniValue proprietary(UniValue::VARR);
+ for (const auto& entry : output.m_proprietary) {
+ UniValue this_prop(UniValue::VOBJ);
+ this_prop.pushKV("identifier", HexStr(entry.identifier));
+ this_prop.pushKV("subtype", entry.subtype);
+ this_prop.pushKV("key", HexStr(entry.key));
+ this_prop.pushKV("value", HexStr(entry.value));
+ proprietary.push_back(this_prop);
+ }
+ out.pushKV("proprietary", proprietary);
+ }
+
// Unknown data
if (output.unknown.size() > 0) {
UniValue unknowns(UniValue::VOBJ);
@@ -1757,6 +1910,13 @@ static RPCHelpMan joinpsbts()
for (unsigned int i = 0; i < psbt.tx->vout.size(); ++i) {
merged_psbt.AddOutput(psbt.tx->vout[i], psbt.outputs[i]);
}
+ for (auto& xpub_pair : psbt.m_xpubs) {
+ if (merged_psbt.m_xpubs.count(xpub_pair.first) == 0) {
+ merged_psbt.m_xpubs[xpub_pair.first] = xpub_pair.second;
+ } else {
+ merged_psbt.m_xpubs[xpub_pair.first].insert(xpub_pair.second.begin(), xpub_pair.second.end());
+ }
+ }
merged_psbt.unknown.insert(psbt.unknown.begin(), psbt.unknown.end());
}
diff --git a/src/script/keyorigin.h b/src/script/keyorigin.h
index 210395d177..c779872be2 100644
--- a/src/script/keyorigin.h
+++ b/src/script/keyorigin.h
@@ -18,6 +18,25 @@ struct KeyOriginInfo
return std::equal(std::begin(a.fingerprint), std::end(a.fingerprint), std::begin(b.fingerprint)) && a.path == b.path;
}
+ friend bool operator<(const KeyOriginInfo& a, const KeyOriginInfo& b)
+ {
+ // Compare the fingerprints lexicographically
+ int fpr_cmp = memcmp(a.fingerprint, b.fingerprint, 4);
+ if (fpr_cmp < 0) {
+ return true;
+ } else if (fpr_cmp > 0) {
+ return false;
+ }
+ // Compare the sizes of the paths, shorter is "less than"
+ if (a.path.size() < b.path.size()) {
+ return true;
+ } else if (a.path.size() > b.path.size()) {
+ return false;
+ }
+ // Paths same length, compare them lexicographically
+ return a.path < b.path;
+ }
+
SERIALIZE_METHODS(KeyOriginInfo, obj) { READWRITE(obj.fingerprint, obj.path); }
void clear()
diff --git a/src/script/sign.cpp b/src/script/sign.cpp
index 3f7060879c..d33c847d98 100644
--- a/src/script/sign.cpp
+++ b/src/script/sign.cpp
@@ -9,6 +9,7 @@
#include <key.h>
#include <policy/policy.h>
#include <primitives/transaction.h>
+#include <script/keyorigin.h>
#include <script/signingprovider.h>
#include <script/standard.h>
#include <uint256.h>
diff --git a/src/script/sign.h b/src/script/sign.h
index 50525af332..7e3d5e80e4 100644
--- a/src/script/sign.h
+++ b/src/script/sign.h
@@ -12,8 +12,6 @@
#include <script/interpreter.h>
#include <script/keyorigin.h>
#include <script/standard.h>
-#include <span.h>
-#include <streams.h>
class CKey;
class CKeyID;
@@ -84,80 +82,6 @@ struct SignatureData {
void MergeSignatureData(SignatureData sigdata);
};
-// Takes a stream and multiple arguments and serializes them as if first serialized into a vector and then into the stream
-// The resulting output into the stream has the total serialized length of all of the objects followed by all objects concatenated with each other.
-template<typename Stream, typename... X>
-void SerializeToVector(Stream& s, const X&... args)
-{
- WriteCompactSize(s, GetSerializeSizeMany(s.GetVersion(), args...));
- SerializeMany(s, args...);
-}
-
-// Takes a stream and multiple arguments and unserializes them first as a vector then each object individually in the order provided in the arguments
-template<typename Stream, typename... X>
-void UnserializeFromVector(Stream& s, X&... args)
-{
- size_t expected_size = ReadCompactSize(s);
- size_t remaining_before = s.size();
- UnserializeMany(s, args...);
- size_t remaining_after = s.size();
- if (remaining_after + expected_size != remaining_before) {
- throw std::ios_base::failure("Size of value was not the stated size");
- }
-}
-
-// Deserialize HD keypaths into a map
-template<typename Stream>
-void DeserializeHDKeypaths(Stream& s, const std::vector<unsigned char>& key, std::map<CPubKey, KeyOriginInfo>& hd_keypaths)
-{
- // Make sure that the key is the size of pubkey + 1
- if (key.size() != CPubKey::SIZE + 1 && key.size() != CPubKey::COMPRESSED_SIZE + 1) {
- throw std::ios_base::failure("Size of key was not the expected size for the type BIP32 keypath");
- }
- // Read in the pubkey from key
- CPubKey pubkey(key.begin() + 1, key.end());
- if (!pubkey.IsFullyValid()) {
- throw std::ios_base::failure("Invalid pubkey");
- }
- if (hd_keypaths.count(pubkey) > 0) {
- throw std::ios_base::failure("Duplicate Key, pubkey derivation path already provided");
- }
-
- // Read in key path
- uint64_t value_len = ReadCompactSize(s);
- if (value_len % 4 || value_len == 0) {
- throw std::ios_base::failure("Invalid length for HD key path");
- }
-
- KeyOriginInfo keypath;
- s >> keypath.fingerprint;
- for (unsigned int i = 4; i < value_len; i += sizeof(uint32_t)) {
- uint32_t index;
- s >> index;
- keypath.path.push_back(index);
- }
-
- // Add to map
- hd_keypaths.emplace(pubkey, std::move(keypath));
-}
-
-// Serialize HD keypaths to a stream from a map
-template<typename Stream>
-void SerializeHDKeypaths(Stream& s, const std::map<CPubKey, KeyOriginInfo>& hd_keypaths, uint8_t type)
-{
- for (auto keypath_pair : hd_keypaths) {
- if (!keypath_pair.first.IsValid()) {
- throw std::ios_base::failure("Invalid CPubKey being serialized");
- }
- SerializeToVector(s, type, Span{keypath_pair.first});
- WriteCompactSize(s, (keypath_pair.second.path.size() + 1) * sizeof(uint32_t));
- s << keypath_pair.second.fingerprint;
- for (const auto& path : keypath_pair.second.path) {
- s << path;
- }
- }
-}
-
/** Produce a script signature using a generic signature creator. */
bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreator& creator, const CScript& scriptPubKey, SignatureData& sigdata);
diff --git a/src/script/signingprovider.h b/src/script/signingprovider.h
index fbce61c6a9..b8b3e03dd3 100644
--- a/src/script/signingprovider.h
+++ b/src/script/signingprovider.h
@@ -8,12 +8,11 @@
#include <key.h>
#include <pubkey.h>
+#include <script/keyorigin.h>
#include <script/script.h>
#include <script/standard.h>
#include <sync.h>
-struct KeyOriginInfo;
-
/** An interface to be implemented by keystores that support signing. */
class SigningProvider
{
diff --git a/src/serialize.h b/src/serialize.h
index edf10440c6..873361fe9e 100644
--- a/src/serialize.h
+++ b/src/serialize.h
@@ -527,6 +527,19 @@ struct CompactSizeFormatter
}
};
+class CompactSizeWriter
+{
+protected:
+ uint64_t n;
+public:
+ explicit CompactSizeWriter(uint64_t n_in) : n(n_in) { }
+
+ template<typename Stream>
+ void Serialize(Stream &s) const {
+ WriteCompactSize<Stream>(s, n);
+ }
+};
+
template<size_t Limit>
struct LimitedStringFormatter
{
diff --git a/src/test/fuzz/integer.cpp b/src/test/fuzz/integer.cpp
index b6c40809e3..ce424c443e 100644
--- a/src/test/fuzz/integer.cpp
+++ b/src/test/fuzz/integer.cpp
@@ -29,12 +29,10 @@
#include <util/strencodings.h>
#include <util/string.h>
#include <util/system.h>
-#include <util/time.h>
#include <version.h>
#include <cassert>
#include <chrono>
-#include <ctime>
#include <limits>
#include <set>
#include <vector>
@@ -81,8 +79,6 @@ FUZZ_TARGET_INIT(integer, initialize_integer)
(void)ComputeMerkleRoot(v256);
(void)CountBits(u64);
(void)DecompressAmount(u64);
- (void)FormatISO8601Date(i64);
- (void)FormatISO8601DateTime(i64);
{
if (std::optional<CAmount> parsed = ParseMoney(FormatMoney(i64))) {
assert(parsed.value() == i64);
diff --git a/src/test/fuzz/parse_iso8601.cpp b/src/test/fuzz/parse_iso8601.cpp
index a56f2aa48d..3c56fa49ee 100644
--- a/src/test/fuzz/parse_iso8601.cpp
+++ b/src/test/fuzz/parse_iso8601.cpp
@@ -19,6 +19,7 @@ FUZZ_TARGET(parse_iso8601)
const std::string random_string = fuzzed_data_provider.ConsumeRemainingBytesAsString();
const std::string iso8601_datetime = FormatISO8601DateTime(random_time);
+ (void)FormatISO8601Date(random_time);
const int64_t parsed_time_1 = ParseISO8601DateTime(iso8601_datetime);
if (random_time >= 0) {
assert(parsed_time_1 >= 0);
diff --git a/src/test/fuzz/script_sign.cpp b/src/test/fuzz/script_sign.cpp
index 79380bd9c9..1a42179724 100644
--- a/src/test/fuzz/script_sign.cpp
+++ b/src/test/fuzz/script_sign.cpp
@@ -5,6 +5,7 @@
#include <chainparams.h>
#include <chainparamsbase.h>
#include <key.h>
+#include <psbt.h>
#include <pubkey.h>
#include <script/keyorigin.h>
#include <script/sign.h>
@@ -43,7 +44,7 @@ FUZZ_TARGET_INIT(script_sign, initialize_script_sign)
} catch (const std::ios_base::failure&) {
}
CDataStream serialized{SER_NETWORK, PROTOCOL_VERSION};
- SerializeHDKeypaths(serialized, hd_keypaths, fuzzed_data_provider.ConsumeIntegral<uint8_t>());
+ SerializeHDKeypaths(serialized, hd_keypaths, CompactSizeWriter(fuzzed_data_provider.ConsumeIntegral<uint8_t>()));
}
{
@@ -61,7 +62,7 @@ FUZZ_TARGET_INIT(script_sign, initialize_script_sign)
}
CDataStream serialized{SER_NETWORK, PROTOCOL_VERSION};
try {
- SerializeHDKeypaths(serialized, hd_keypaths, fuzzed_data_provider.ConsumeIntegral<uint8_t>());
+ SerializeHDKeypaths(serialized, hd_keypaths, CompactSizeWriter(fuzzed_data_provider.ConsumeIntegral<uint8_t>()));
} catch (const std::ios_base::failure&) {
}
std::map<CPubKey, KeyOriginInfo> deserialized_hd_keypaths;
diff --git a/src/wallet/rpc/addresses.cpp b/src/wallet/rpc/addresses.cpp
index e570c18099..c7400cafe7 100644
--- a/src/wallet/rpc/addresses.cpp
+++ b/src/wallet/rpc/addresses.cpp
@@ -238,6 +238,10 @@ RPCHelpMan addmultisigaddress()
{RPCResult::Type::STR, "address", "The value of the new multisig address"},
{RPCResult::Type::STR_HEX, "redeemScript", "The string value of the hex-encoded redemption script"},
{RPCResult::Type::STR, "descriptor", "The descriptor for this multisig"},
+ {RPCResult::Type::ARR, "warnings", /* optional */ true, "Any warnings resulting from the creation of this multisig",
+ {
+ {RPCResult::Type::STR, "", ""},
+ }},
}
},
RPCExamples{
@@ -295,6 +299,14 @@ RPCHelpMan addmultisigaddress()
result.pushKV("address", EncodeDestination(dest));
result.pushKV("redeemScript", HexStr(inner));
result.pushKV("descriptor", descriptor->ToString());
+
+ UniValue warnings(UniValue::VARR);
+ if (!request.params[3].isNull() && OutputTypeFromDestination(dest) != output_type) {
+ // Only warns if the user has explicitly chosen an address type we cannot generate
+ warnings.push_back("Unable to make chosen address type, please ensure no uncompressed public keys are present.");
+ }
+ if (warnings.size()) result.pushKV("warnings", warnings);
+
return result;
},
};