aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/banman.cpp20
-rw-r--r--src/bench/bench.cpp4
-rw-r--r--src/bench/checkblock.cpp8
-rw-r--r--src/bench/rpc_blockchain.cpp4
-rw-r--r--src/bitcoin-tx.cpp10
-rw-r--r--src/chain.h29
-rw-r--r--src/common/bloom.cpp4
-rw-r--r--src/core_write.cpp2
-rw-r--r--src/crypto/chacha_poly_aead.cpp2
-rw-r--r--src/dbwrapper.h6
-rw-r--r--src/fs.cpp4
-rw-r--r--src/hash.h15
-rw-r--r--src/index/txindex.cpp4
-rw-r--r--src/interfaces/chain.h3
-rw-r--r--src/interfaces/wallet.h1
-rw-r--r--src/net.cpp4
-rw-r--r--src/net_processing.cpp38
-rw-r--r--src/net_processing.h9
-rw-r--r--src/node/blockstorage.cpp18
-rw-r--r--src/node/blockstorage.h9
-rw-r--r--src/node/interfaces.cpp5
-rw-r--r--src/psbt.cpp2
-rw-r--r--src/pubkey.h4
-rw-r--r--src/qt/forms/debugwindow.ui82
-rw-r--r--src/qt/guiconstants.h2
-rw-r--r--src/qt/psbtoperationsdialog.cpp2
-rw-r--r--src/qt/rpcconsole.cpp3
-rw-r--r--src/qt/sendcoinsdialog.cpp2
-rw-r--r--src/qt/transactiondesc.cpp9
-rw-r--r--src/qt/transactionrecord.cpp15
-rw-r--r--src/qt/transactionrecord.h2
-rw-r--r--src/qt/transactiontablemodel.cpp9
-rw-r--r--src/qt/walletframe.cpp2
-rw-r--r--src/random.h2
-rw-r--r--src/rpc/blockchain.cpp237
-rw-r--r--src/rpc/client.cpp2
-rw-r--r--src/rpc/rawtransaction.cpp7
-rw-r--r--src/rpc/util.cpp16
-rw-r--r--src/rpc/util.h9
-rw-r--r--src/script/bitcoinconsensus.cpp17
-rw-r--r--src/script/interpreter.cpp4
-rw-r--r--src/serialize.h77
-rw-r--r--src/span.h9
-rw-r--r--src/streams.h86
-rw-r--r--src/support/allocators/zeroafterfree.h2
-rw-r--r--src/test/bloom_tests.cpp12
-rw-r--r--src/test/fuzz/autofile.cpp8
-rw-r--r--src/test/fuzz/buffered_file.cpp6
-rw-r--r--src/test/fuzz/chain.cpp23
-rw-r--r--src/test/fuzz/fuzz.cpp2
-rw-r--r--src/test/fuzz/integer.cpp5
-rw-r--r--src/test/fuzz/rpc.cpp3
-rw-r--r--src/test/fuzz/script.cpp19
-rw-r--r--src/test/fuzz/util.h6
-rw-r--r--src/test/fuzz/versionbits.cpp35
-rw-r--r--src/test/interfaces_tests.cpp1
-rw-r--r--src/test/script_tests.cpp20
-rw-r--r--src/test/serialize_tests.cpp52
-rw-r--r--src/test/streams_tests.cpp10
-rw-r--r--src/test/util/blockfilter.cpp2
-rw-r--r--src/test/validation_chainstatemanager_tests.cpp3
-rw-r--r--src/txdb.cpp5
-rw-r--r--src/txdb.h3
-rw-r--r--src/uint256.h6
-rw-r--r--src/util/system.cpp2
-rw-r--r--src/validation.cpp2
-rw-r--r--src/versionbits.cpp31
-rw-r--r--src/versionbits.h12
-rw-r--r--src/wallet/bdb.cpp6
-rw-r--r--src/wallet/coincontrol.h19
-rw-r--r--src/wallet/dump.cpp12
-rw-r--r--src/wallet/interfaces.cpp1
-rw-r--r--src/wallet/receive.cpp2
-rw-r--r--src/wallet/rpc/coins.cpp4
-rw-r--r--src/wallet/rpc/spend.cpp79
-rw-r--r--src/wallet/rpc/transactions.cpp4
-rw-r--r--src/wallet/spend.cpp14
-rw-r--r--src/wallet/sqlite.cpp18
-rw-r--r--src/wallet/test/spend_tests.cpp51
-rw-r--r--src/wallet/test/wallet_tests.cpp17
-rw-r--r--src/wallet/wallet.cpp51
-rw-r--r--src/wallet/wallet.h2
82 files changed, 878 insertions, 471 deletions
diff --git a/src/banman.cpp b/src/banman.cpp
index 95b927c1ff..b28e3f7f7c 100644
--- a/src/banman.cpp
+++ b/src/banman.cpp
@@ -7,6 +7,7 @@
#include <netaddress.h>
#include <node/ui_interface.h>
+#include <sync.h>
#include <util/system.h>
#include <util/time.h>
#include <util/translation.h>
@@ -39,18 +40,23 @@ BanMan::~BanMan()
void BanMan::DumpBanlist()
{
- SweepBanned(); // clean unused entries (if bantime has expired)
-
- if (!BannedSetIsDirty()) return;
-
- int64_t n_start = GetTimeMillis();
+ static Mutex dump_mutex;
+ LOCK(dump_mutex);
banmap_t banmap;
- GetBanned(banmap);
- if (m_ban_db.Write(banmap)) {
+ {
+ LOCK(m_cs_banned);
+ SweepBanned();
+ if (!BannedSetIsDirty()) return;
+ banmap = m_banned;
SetBannedSetDirty(false);
}
+ int64_t n_start = GetTimeMillis();
+ if (!m_ban_db.Write(banmap)) {
+ SetBannedSetDirty(true);
+ }
+
LogPrint(BCLog::NET, "Flushed %d banned node addresses/subnets to disk %dms\n", banmap.size(),
GetTimeMillis() - n_start);
}
diff --git a/src/bench/bench.cpp b/src/bench/bench.cpp
index d7b4228566..9bd176f0a0 100644
--- a/src/bench/bench.cpp
+++ b/src/bench/bench.cpp
@@ -4,10 +4,10 @@
#include <bench/bench.h>
+#include <fs.h>
#include <test/util/setup_common.h>
#include <chrono>
-#include <fstream>
#include <functional>
#include <iostream>
#include <map>
@@ -29,7 +29,7 @@ void GenerateTemplateResults(const std::vector<ankerl::nanobench::Result>& bench
// nothing to write, bail out
return;
}
- std::ofstream fout(filename);
+ fsbridge::ofstream fout{fs::PathFromString(filename)};
if (fout.is_open()) {
ankerl::nanobench::render(tpl, benchmarkResults, fout);
} else {
diff --git a/src/bench/checkblock.cpp b/src/bench/checkblock.cpp
index a9f3f5f84d..52e5cb743f 100644
--- a/src/bench/checkblock.cpp
+++ b/src/bench/checkblock.cpp
@@ -17,8 +17,8 @@
static void DeserializeBlockTest(benchmark::Bench& bench)
{
CDataStream stream(benchmark::data::block413567, SER_NETWORK, PROTOCOL_VERSION);
- char a = '\0';
- stream.write(&a, 1); // Prevent compaction
+ std::byte a{0};
+ stream.write({&a, 1}); // Prevent compaction
bench.unit("block").run([&] {
CBlock block;
@@ -31,8 +31,8 @@ static void DeserializeBlockTest(benchmark::Bench& bench)
static void DeserializeAndCheckBlockTest(benchmark::Bench& bench)
{
CDataStream stream(benchmark::data::block413567, SER_NETWORK, PROTOCOL_VERSION);
- char a = '\0';
- stream.write(&a, 1); // Prevent compaction
+ std::byte a{0};
+ stream.write({&a, 1}); // Prevent compaction
ArgsManager bench_args;
const auto chainParams = CreateChainParams(bench_args, CBaseChainParams::MAIN);
diff --git a/src/bench/rpc_blockchain.cpp b/src/bench/rpc_blockchain.cpp
index 9bc31461d2..2143bcf950 100644
--- a/src/bench/rpc_blockchain.cpp
+++ b/src/bench/rpc_blockchain.cpp
@@ -23,8 +23,8 @@ struct TestBlockAndIndex {
TestBlockAndIndex()
{
CDataStream stream(benchmark::data::block413567, SER_NETWORK, PROTOCOL_VERSION);
- char a = '\0';
- stream.write(&a, 1); // Prevent compaction
+ std::byte a{0};
+ stream.write({&a, 1}); // Prevent compaction
stream >> block;
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
index edec883264..ec07114d6e 100644
--- a/src/bitcoin-tx.cpp
+++ b/src/bitcoin-tx.cpp
@@ -12,6 +12,7 @@
#include <consensus/consensus.h>
#include <core_io.h>
#include <key_io.h>
+#include <fs.h>
#include <policy/policy.h>
#include <policy/rbf.h>
#include <primitives/transaction.h>
@@ -158,7 +159,7 @@ static void RegisterLoad(const std::string& strInput)
std::string key = strInput.substr(0, pos);
std::string filename = strInput.substr(pos + 1, std::string::npos);
- FILE *f = fopen(filename.c_str(), "r");
+ FILE *f = fsbridge::fopen(filename.c_str(), "r");
if (!f) {
std::string strErr = "Cannot open file " + filename;
throw std::runtime_error(strErr);
@@ -433,13 +434,16 @@ static void MutateTxAddOutData(CMutableTransaction& tx, const std::string& strIn
if (pos==0)
throw std::runtime_error("TX output value not specified");
- if (pos != std::string::npos) {
+ if (pos == std::string::npos) {
+ pos = 0;
+ } else {
// Extract and validate VALUE
value = ExtractAndValidateValue(strInput.substr(0, pos));
+ ++pos;
}
// extract and validate DATA
- std::string strData = strInput.substr(pos + 1, std::string::npos);
+ const std::string strData{strInput.substr(pos, std::string::npos)};
if (!IsHex(strData))
throw std::runtime_error("invalid TX output data");
diff --git a/src/chain.h b/src/chain.h
index 7d5164a7ff..24b5026aba 100644
--- a/src/chain.h
+++ b/src/chain.h
@@ -10,6 +10,7 @@
#include <consensus/params.h>
#include <flatfile.h>
#include <primitives/block.h>
+#include <sync.h>
#include <tinyformat.h>
#include <uint256.h>
@@ -37,6 +38,8 @@ static constexpr int64_t TIMESTAMP_WINDOW = MAX_FUTURE_BLOCK_TIME;
*/
static constexpr int64_t MAX_BLOCK_TIME_GAP = 90 * 60;
+extern RecursiveMutex cs_main;
+
class CBlockFileInfo
{
public:
@@ -161,13 +164,13 @@ public:
int nHeight{0};
//! Which # file this block is stored in (blk?????.dat)
- int nFile{0};
+ int nFile GUARDED_BY(::cs_main){0};
//! Byte offset within blk?????.dat where this block's data is stored
- unsigned int nDataPos{0};
+ unsigned int nDataPos GUARDED_BY(::cs_main){0};
//! Byte offset within rev?????.dat where this block's undo data is stored
- unsigned int nUndoPos{0};
+ unsigned int nUndoPos GUARDED_BY(::cs_main){0};
//! (memory only) Total amount of work (expected number of hashes) in the chain up to and including this block
arith_uint256 nChainWork{};
@@ -195,7 +198,7 @@ public:
//! load to avoid the block index being spuriously rewound.
//! @sa NeedsRedownload
//! @sa ActivateSnapshot
- uint32_t nStatus{0};
+ uint32_t nStatus GUARDED_BY(::cs_main){0};
//! block header
int32_t nVersion{0};
@@ -223,8 +226,9 @@ public:
{
}
- FlatFilePos GetBlockPos() const
+ FlatFilePos GetBlockPos() const EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
{
+ AssertLockHeld(::cs_main);
FlatFilePos ret;
if (nStatus & BLOCK_HAVE_DATA) {
ret.nFile = nFile;
@@ -233,8 +237,9 @@ public:
return ret;
}
- FlatFilePos GetUndoPos() const
+ FlatFilePos GetUndoPos() const EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
{
+ AssertLockHeld(::cs_main);
FlatFilePos ret;
if (nStatus & BLOCK_HAVE_UNDO) {
ret.nFile = nFile;
@@ -306,7 +311,9 @@ public:
//! Check whether this block index entry is valid up to the passed validity level.
bool IsValid(enum BlockStatus nUpTo = BLOCK_VALID_TRANSACTIONS) const
+ EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
{
+ AssertLockHeld(::cs_main);
assert(!(nUpTo & ~BLOCK_VALID_MASK)); // Only validity flags allowed.
if (nStatus & BLOCK_FAILED_MASK)
return false;
@@ -315,12 +322,17 @@ public:
//! @returns true if the block is assumed-valid; this means it is queued to be
//! validated by a background chainstate.
- bool IsAssumedValid() const { return nStatus & BLOCK_ASSUMED_VALID; }
+ bool IsAssumedValid() const EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
+ {
+ AssertLockHeld(::cs_main);
+ return nStatus & BLOCK_ASSUMED_VALID;
+ }
//! Raise the validity level of this block index entry.
//! Returns true if the validity was changed.
- bool RaiseValidity(enum BlockStatus nUpTo)
+ bool RaiseValidity(enum BlockStatus nUpTo) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
{
+ AssertLockHeld(::cs_main);
assert(!(nUpTo & ~BLOCK_VALID_MASK)); // Only validity flags allowed.
if (nStatus & BLOCK_FAILED_MASK) return false;
@@ -370,6 +382,7 @@ public:
SERIALIZE_METHODS(CDiskBlockIndex, obj)
{
+ LOCK(::cs_main);
int _nVersion = s.GetVersion();
if (!(s.GetType() & SER_GETHASH)) READWRITE(VARINT_MODE(_nVersion, VarIntMode::NONNEGATIVE_SIGNED));
diff --git a/src/common/bloom.cpp b/src/common/bloom.cpp
index 0bb72dbcbb..c744d05a0e 100644
--- a/src/common/bloom.cpp
+++ b/src/common/bloom.cpp
@@ -62,7 +62,7 @@ void CBloomFilter::insert(const COutPoint& outpoint)
{
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
stream << outpoint;
- insert(stream);
+ insert(MakeUCharSpan(stream));
}
bool CBloomFilter::contains(Span<const unsigned char> vKey) const
@@ -83,7 +83,7 @@ bool CBloomFilter::contains(const COutPoint& outpoint) const
{
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
stream << outpoint;
- return contains(stream);
+ return contains(MakeUCharSpan(stream));
}
bool CBloomFilter::IsWithinSizeConstraints() const
diff --git a/src/core_write.cpp b/src/core_write.cpp
index 067f1e4f4e..5ea62cf3ed 100644
--- a/src/core_write.cpp
+++ b/src/core_write.cpp
@@ -8,6 +8,7 @@
#include <consensus/consensus.h>
#include <consensus/validation.h>
#include <key_io.h>
+#include <script/descriptor.h>
#include <script/script.h>
#include <script/standard.h>
#include <serialize.h>
@@ -152,6 +153,7 @@ void ScriptPubKeyToUniv(const CScript& scriptPubKey, UniValue& out, bool include
CTxDestination address;
out.pushKV("asm", ScriptToAsmStr(scriptPubKey));
+ out.pushKV("desc", InferDescriptor(scriptPubKey, DUMMY_SIGNING_PROVIDER)->ToString());
if (include_hex) out.pushKV("hex", HexStr(scriptPubKey));
std::vector<std::vector<unsigned char>> solns;
diff --git a/src/crypto/chacha_poly_aead.cpp b/src/crypto/chacha_poly_aead.cpp
index 19087b7d75..4f3e6f7fa3 100644
--- a/src/crypto/chacha_poly_aead.cpp
+++ b/src/crypto/chacha_poly_aead.cpp
@@ -73,7 +73,7 @@ bool ChaCha20Poly1305AEAD::Crypt(uint64_t seqnr_payload, uint64_t seqnr_aad, int
return false;
}
memory_cleanse(expected_tag, sizeof(expected_tag));
- // MAC has been successfully verified, make sure we don't covert it in decryption
+ // MAC has been successfully verified, make sure we don't convert it in decryption
src_len -= POLY1305_TAGLEN;
}
diff --git a/src/dbwrapper.h b/src/dbwrapper.h
index 12db0fffcc..1109cb5888 100644
--- a/src/dbwrapper.h
+++ b/src/dbwrapper.h
@@ -147,7 +147,7 @@ public:
template<typename K> bool GetKey(K& key) {
leveldb::Slice slKey = piter->key();
try {
- CDataStream ssKey(MakeUCharSpan(slKey), SER_DISK, CLIENT_VERSION);
+ CDataStream ssKey{MakeByteSpan(slKey), SER_DISK, CLIENT_VERSION};
ssKey >> key;
} catch (const std::exception&) {
return false;
@@ -158,7 +158,7 @@ public:
template<typename V> bool GetValue(V& value) {
leveldb::Slice slValue = piter->value();
try {
- CDataStream ssValue(MakeUCharSpan(slValue), SER_DISK, CLIENT_VERSION);
+ CDataStream ssValue{MakeByteSpan(slValue), SER_DISK, CLIENT_VERSION};
ssValue.Xor(dbwrapper_private::GetObfuscateKey(parent));
ssValue >> value;
} catch (const std::exception&) {
@@ -244,7 +244,7 @@ public:
dbwrapper_private::HandleError(status);
}
try {
- CDataStream ssValue(MakeUCharSpan(strValue), SER_DISK, CLIENT_VERSION);
+ CDataStream ssValue{MakeByteSpan(strValue), SER_DISK, CLIENT_VERSION};
ssValue.Xor(obfuscate_key);
ssValue >> value;
} catch (const std::exception&) {
diff --git a/src/fs.cpp b/src/fs.cpp
index 34a0348578..8fcadcb3ef 100644
--- a/src/fs.cpp
+++ b/src/fs.cpp
@@ -7,7 +7,6 @@
#ifndef WIN32
#include <cstring>
#include <fcntl.h>
-#include <string>
#include <sys/file.h>
#include <sys/utsname.h>
#include <unistd.h>
@@ -20,6 +19,9 @@
#include <windows.h>
#endif
+#include <cassert>
+#include <string>
+
namespace fsbridge {
FILE *fopen(const fs::path& p, const char *mode)
diff --git a/src/hash.h b/src/hash.h
index 1456a899d8..9f582842c1 100644
--- a/src/hash.h
+++ b/src/hash.h
@@ -111,8 +111,9 @@ public:
int GetType() const { return nType; }
int GetVersion() const { return nVersion; }
- void write(const char *pch, size_t size) {
- ctx.Write((const unsigned char*)pch, size);
+ void write(Span<const std::byte> src)
+ {
+ ctx.Write(UCharCast(src.data()), src.size());
}
/** Compute the double-SHA256 hash of all data written to this object.
@@ -162,18 +163,18 @@ private:
public:
explicit CHashVerifier(Source* source_) : CHashWriter(source_->GetType(), source_->GetVersion()), source(source_) {}
- void read(char* pch, size_t nSize)
+ void read(Span<std::byte> dst)
{
- source->read(pch, nSize);
- this->write(pch, nSize);
+ source->read(dst);
+ this->write(dst);
}
void ignore(size_t nSize)
{
- char data[1024];
+ std::byte data[1024];
while (nSize > 0) {
size_t now = std::min<size_t>(nSize, 1024);
- read(data, now);
+ read({data, now});
nSize -= now;
}
}
diff --git a/src/index/txindex.cpp b/src/index/txindex.cpp
index e9aeb58194..e1d807f39a 100644
--- a/src/index/txindex.cpp
+++ b/src/index/txindex.cpp
@@ -59,7 +59,9 @@ bool TxIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex)
// Exclude genesis block transaction because outputs are not spendable.
if (pindex->nHeight == 0) return true;
- CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(block.vtx.size()));
+ CDiskTxPos pos{
+ WITH_LOCK(::cs_main, return pindex->GetBlockPos()),
+ GetSizeOfCompactSize(block.vtx.size())};
std::vector<std::pair<uint256, CDiskTxPos>> vPos;
vPos.reserve(block.vtx.size());
for (const auto& tx : block.vtx) {
diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h
index 4f5105a5c1..ddfb4bda95 100644
--- a/src/interfaces/chain.h
+++ b/src/interfaces/chain.h
@@ -116,9 +116,6 @@ public:
//! or one of its ancestors.
virtual std::optional<int> findLocatorFork(const CBlockLocator& locator) = 0;
- //! Check if transaction will be final given chain height current time.
- virtual bool checkFinalTx(const CTransaction& tx) = 0;
-
//! Return whether node has the block and optionally return block metadata
//! or contents.
virtual bool findBlock(const uint256& hash, const FoundBlock& block={}) = 0;
diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h
index aa33a3c951..f26ac866dc 100644
--- a/src/interfaces/wallet.h
+++ b/src/interfaces/wallet.h
@@ -405,7 +405,6 @@ struct WalletTxStatus
int depth_in_main_chain;
unsigned int time_received;
uint32_t lock_time;
- bool is_final;
bool is_trusted;
bool is_abandoned;
bool is_coinbase;
diff --git a/src/net.cpp b/src/net.cpp
index 0260e14da7..be56d1e2d2 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -3091,11 +3091,11 @@ void CaptureMessage(const CAddress& addr, const std::string& msg_type, const Spa
CAutoFile f(fsbridge::fopen(path, "ab"), SER_DISK, CLIENT_VERSION);
ser_writedata64(f, now.count());
- f.write(msg_type.data(), msg_type.length());
+ f.write(MakeByteSpan(msg_type));
for (auto i = msg_type.length(); i < CMessageHeader::COMMAND_SIZE; ++i) {
f << uint8_t{'\0'};
}
uint32_t size = data.size();
ser_writedata32(f, size);
- f.write((const char*)data.data(), data.size());
+ f.write(AsBytes(data));
}
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index cc23876af5..3cebca1a77 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -320,7 +320,7 @@ public:
/** Implement PeerManager */
void StartScheduledTasks(CScheduler& scheduler) override;
void CheckForStaleTipAndEvictPeers() override;
- bool FetchBlock(NodeId id, const uint256& hash, const CBlockIndex& index) override;
+ std::optional<std::string> FetchBlock(NodeId peer_id, const CBlockIndex& block_index) override;
bool GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) const override;
bool IgnoresIncomingTxs() override { return m_ignore_incoming_txs; }
void SendPings() override;
@@ -1460,39 +1460,39 @@ bool PeerManagerImpl::BlockRequestAllowed(const CBlockIndex* pindex)
(GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, m_chainparams.GetConsensus()) < STALE_RELAY_AGE_LIMIT);
}
-bool PeerManagerImpl::FetchBlock(NodeId id, const uint256& hash, const CBlockIndex& index)
+std::optional<std::string> PeerManagerImpl::FetchBlock(NodeId peer_id, const CBlockIndex& block_index)
{
- if (fImporting || fReindex) return false;
+ if (fImporting) return "Importing...";
+ if (fReindex) return "Reindexing...";
LOCK(cs_main);
// Ensure this peer exists and hasn't been disconnected
- CNodeState* state = State(id);
- if (state == nullptr) return false;
+ CNodeState* state = State(peer_id);
+ if (state == nullptr) return "Peer does not exist";
// Ignore pre-segwit peers
- if (!state->fHaveWitness) return false;
+ if (!state->fHaveWitness) return "Pre-SegWit peer";
- // Mark block as in-flight unless it already is
- if (!BlockRequested(id, index)) return false;
+ // Mark block as in-flight unless it already is (for this peer).
+ // If a block was already in-flight for a different peer, its BLOCKTXN
+ // response will be dropped.
+ if (!BlockRequested(peer_id, block_index)) return "Already requested from this peer";
// Construct message to request the block
+ const uint256& hash{block_index.GetBlockHash()};
std::vector<CInv> invs{CInv(MSG_BLOCK | MSG_WITNESS_FLAG, hash)};
// Send block request message to the peer
- bool success = m_connman.ForNode(id, [this, &invs](CNode* node) {
+ bool success = m_connman.ForNode(peer_id, [this, &invs](CNode* node) {
const CNetMsgMaker msgMaker(node->GetCommonVersion());
this->m_connman.PushMessage(node, msgMaker.Make(NetMsgType::GETDATA, invs));
return true;
});
- if (success) {
- LogPrint(BCLog::NET, "Requesting block %s from peer=%d\n",
- hash.ToString(), id);
- } else {
- RemoveBlockRequest(hash);
- LogPrint(BCLog::NET, "Failed to request block %s from peer=%d\n",
- hash.ToString(), id);
- }
- return success;
+ if (!success) return "Peer not fully connected";
+
+ LogPrint(BCLog::NET, "Requesting block %s from peer=%d\n",
+ hash.ToString(), peer_id);
+ return std::nullopt;
}
std::unique_ptr<PeerManager> PeerManager::make(const CChainParams& chainparams, CConnman& connman, AddrMan& addrman,
@@ -1880,7 +1880,7 @@ void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv&
// Fast-path: in this case it is possible to serve the block directly from disk,
// as the network format matches the format on disk
std::vector<uint8_t> block_data;
- if (!ReadRawBlockFromDisk(block_data, pindex, m_chainparams.MessageStart())) {
+ if (!ReadRawBlockFromDisk(block_data, pindex->GetBlockPos(), m_chainparams.MessageStart())) {
assert(!"cannot load block from disk");
}
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::BLOCK, Span{block_data}));
diff --git a/src/net_processing.h b/src/net_processing.h
index 27775cea97..e30f9f516c 100644
--- a/src/net_processing.h
+++ b/src/net_processing.h
@@ -45,12 +45,11 @@ public:
/**
* Attempt to manually fetch block from a given peer. We must already have the header.
*
- * @param[in] id The peer id
- * @param[in] hash The block hash
- * @param[in] pindex The blockindex
- * @returns Whether a request was successfully made
+ * @param[in] peer_id The peer id
+ * @param[in] block_index The blockindex
+ * @returns std::nullopt if a request was successfully made, otherwise an error message
*/
- virtual bool FetchBlock(NodeId id, const uint256& hash, const CBlockIndex& pindex) = 0;
+ virtual std::optional<std::string> FetchBlock(NodeId peer_id, const CBlockIndex& block_index) = 0;
/** Begin running background tasks, should only be called once */
virtual void StartScheduledTasks(CScheduler& scheduler) = 0;
diff --git a/src/node/blockstorage.cpp b/src/node/blockstorage.cpp
index cbfdcb6f11..7691c9a5ce 100644
--- a/src/node/blockstorage.cpp
+++ b/src/node/blockstorage.cpp
@@ -429,6 +429,7 @@ CBlockIndex* BlockManager::GetLastCheckpoint(const CCheckpointData& data)
bool IsBlockPruned(const CBlockIndex* pblockindex)
{
+ AssertLockHeld(::cs_main);
return (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0);
}
@@ -513,7 +514,8 @@ static bool UndoWriteToDisk(const CBlockUndo& blockundo, FlatFilePos& pos, const
bool UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex* pindex)
{
- FlatFilePos pos = pindex->GetUndoPos();
+ const FlatFilePos pos{WITH_LOCK(::cs_main, return pindex->GetUndoPos())};
+
if (pos.IsNull()) {
return error("%s: no undo data available", __func__);
}
@@ -712,6 +714,7 @@ static bool WriteBlockToDisk(const CBlock& block, FlatFilePos& pos, const CMessa
bool BlockManager::WriteUndoDataForBlock(const CBlockUndo& blockundo, BlockValidationState& state, CBlockIndex* pindex, const CChainParams& chainparams)
{
+ AssertLockHeld(::cs_main);
// Write undo information to disk
if (pindex->GetUndoPos().IsNull()) {
FlatFilePos _pos;
@@ -810,7 +813,7 @@ bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const FlatFilePos& pos, c
}
block.resize(blk_size); // Zeroing of memory is intentional here
- filein.read((char*)block.data(), blk_size);
+ filein.read(MakeWritableByteSpan(block));
} catch (const std::exception& e) {
return error("%s: Read from block file failed: %s for %s", __func__, e.what(), pos.ToString());
}
@@ -818,17 +821,6 @@ bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const FlatFilePos& pos, c
return true;
}
-bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const CBlockIndex* pindex, const CMessageHeader::MessageStartChars& message_start)
-{
- FlatFilePos block_pos;
- {
- LOCK(cs_main);
- block_pos = pindex->GetBlockPos();
- }
-
- return ReadRawBlockFromDisk(block, block_pos, message_start);
-}
-
/** Store block on disk. If dbp is non-nullptr, the file is known to already reside on disk */
FlatFilePos BlockManager::SaveBlockToDisk(const CBlock& block, int nHeight, CChain& active_chain, const CChainParams& chainparams, const FlatFilePos* dbp)
{
diff --git a/src/node/blockstorage.h b/src/node/blockstorage.h
index 78c9210892..42e46797d2 100644
--- a/src/node/blockstorage.h
+++ b/src/node/blockstorage.h
@@ -7,12 +7,15 @@
#include <fs.h>
#include <protocol.h> // For CMessageHeader::MessageStartChars
+#include <sync.h>
#include <txdb.h>
#include <atomic>
#include <cstdint>
#include <vector>
+extern RecursiveMutex cs_main;
+
class ArgsManager;
class BlockValidationState;
class CBlock;
@@ -146,7 +149,8 @@ public:
/** Get block file info entry for one block file */
CBlockFileInfo* GetBlockFileInfo(size_t n);
- bool WriteUndoDataForBlock(const CBlockUndo& blockundo, BlockValidationState& state, CBlockIndex* pindex, const CChainParams& chainparams);
+ bool WriteUndoDataForBlock(const CBlockUndo& blockundo, BlockValidationState& state, CBlockIndex* pindex, const CChainParams& chainparams)
+ EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
FlatFilePos SaveBlockToDisk(const CBlock& block, int nHeight, CChain& active_chain, const CChainParams& chainparams, const FlatFilePos* dbp);
@@ -163,7 +167,7 @@ public:
};
//! Check whether the block associated with this index entry is pruned or not.
-bool IsBlockPruned(const CBlockIndex* pblockindex);
+bool IsBlockPruned(const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
void CleanupBlockRevFiles();
@@ -181,7 +185,6 @@ void UnlinkPrunedFiles(const std::set<int>& setFilesToPrune);
bool ReadBlockFromDisk(CBlock& block, const FlatFilePos& pos, const Consensus::Params& consensusParams);
bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams);
bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const FlatFilePos& pos, const CMessageHeader::MessageStartChars& message_start);
-bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const CBlockIndex* pindex, const CMessageHeader::MessageStartChars& message_start);
bool UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex* pindex);
diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp
index 1a48957f0f..ffad289fa9 100644
--- a/src/node/interfaces.cpp
+++ b/src/node/interfaces.cpp
@@ -486,11 +486,6 @@ public:
const CChain& active = Assert(m_node.chainman)->ActiveChain();
return active.GetLocator();
}
- bool checkFinalTx(const CTransaction& tx) override
- {
- LOCK(cs_main);
- return CheckFinalTx(chainman().ActiveChain().Tip(), tx);
- }
std::optional<int> findLocatorFork(const CBlockLocator& locator) override
{
LOCK(cs_main);
diff --git a/src/psbt.cpp b/src/psbt.cpp
index 8248609ba6..c8c73e130b 100644
--- a/src/psbt.cpp
+++ b/src/psbt.cpp
@@ -399,7 +399,7 @@ bool DecodeBase64PSBT(PartiallySignedTransaction& psbt, const std::string& base6
bool DecodeRawPSBT(PartiallySignedTransaction& psbt, const std::string& tx_data, std::string& error)
{
- CDataStream ss_data(MakeUCharSpan(tx_data), SER_NETWORK, PROTOCOL_VERSION);
+ CDataStream ss_data(MakeByteSpan(tx_data), SER_NETWORK, PROTOCOL_VERSION);
try {
ss_data >> psbt;
if (!ss_data.empty()) {
diff --git a/src/pubkey.h b/src/pubkey.h
index 349081205b..dfe06f834c 100644
--- a/src/pubkey.h
+++ b/src/pubkey.h
@@ -142,14 +142,14 @@ public:
{
unsigned int len = size();
::WriteCompactSize(s, len);
- s.write((char*)vch, len);
+ s.write(AsBytes(Span{vch, len}));
}
template <typename Stream>
void Unserialize(Stream& s)
{
const unsigned int len(::ReadCompactSize(s));
if (len <= SIZE) {
- s.read((char*)vch, len);
+ s.read(AsWritableBytes(Span{vch, len}));
if (len != size()) {
Invalidate();
}
diff --git a/src/qt/forms/debugwindow.ui b/src/qt/forms/debugwindow.ui
index 15e0d3fad9..2196801023 100644
--- a/src/qt/forms/debugwindow.ui
+++ b/src/qt/forms/debugwindow.ui
@@ -1355,10 +1355,10 @@
<item row="13" column="0">
<widget class="QLabel" name="peerLastTxLabel">
<property name="toolTip">
- <string>Elapsed time since a novel transaction accepted into our mempool was received from this peer.</string>
+ <string extracomment="Tooltip text for the Last Transaction field in the peer details area.">Elapsed time since a novel transaction accepted into our mempool was received from this peer.</string>
</property>
<property name="text">
- <string>Last Tx</string>
+ <string>Last Transaction</string>
</property>
</widget>
</item>
@@ -1592,6 +1592,84 @@
</widget>
</item>
<item row="23" column="0">
+ <widget class="QLabel" name="peerAddrRelayEnabledLabel">
+ <property name="toolTip">
+ <string extracomment="Tooltip text for the Address Relay field in the peer details area.">Whether we relay addresses to this peer.</string>
+ </property>
+ <property name="text">
+ <string>Address Relay</string>
+ </property>
+ </widget>
+ </item>
+ <item row="23" column="1">
+ <widget class="QLabel" name="peerAddrRelayEnabled">
+ <property name="cursor">
+ <cursorShape>IBeamCursor</cursorShape>
+ </property>
+ <property name="text">
+ <string>N/A</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::PlainText</enum>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ <item row="24" column="0">
+ <widget class="QLabel" name="peerAddrProcessedLabel">
+ <property name="toolTip">
+ <string extracomment="Tooltip text for the Addresses Processed field in the peer details area.">Total number of addresses processed, excluding those dropped due to rate-limiting.</string>
+ </property>
+ <property name="text">
+ <string>Addresses Processed</string>
+ </property>
+ </widget>
+ </item>
+ <item row="24" column="1">
+ <widget class="QLabel" name="peerAddrProcessed">
+ <property name="cursor">
+ <cursorShape>IBeamCursor</cursorShape>
+ </property>
+ <property name="text">
+ <string>N/A</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::PlainText</enum>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ <item row="25" column="0">
+ <widget class="QLabel" name="peerAddrRateLimitedLabel">
+ <property name="toolTip">
+ <string extracomment="Tooltip text for the Addresses Rate-Limited field in the peer details area.">Total number of addresses dropped due to rate-limiting.</string>
+ </property>
+ <property name="text">
+ <string>Addresses Rate-Limited</string>
+ </property>
+ </widget>
+ </item>
+ <item row="25" column="1">
+ <widget class="QLabel" name="peerAddrRateLimited">
+ <property name="cursor">
+ <cursorShape>IBeamCursor</cursorShape>
+ </property>
+ <property name="text">
+ <string>N/A</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::PlainText</enum>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ <item row="26" column="0">
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h
index 1adcd5b6b9..fcdf6056c9 100644
--- a/src/qt/guiconstants.h
+++ b/src/qt/guiconstants.h
@@ -33,8 +33,6 @@ static const bool DEFAULT_SPLASHSCREEN = true;
#define COLOR_NEGATIVE QColor(255, 0, 0)
/* Transaction list -- bare address (without label) */
#define COLOR_BAREADDRESS QColor(140, 140, 140)
-/* Transaction list -- TX status decoration - open until date */
-#define COLOR_TX_STATUS_OPENUNTILDATE QColor(64, 64, 255)
/* Transaction list -- TX status decoration - danger, tx needs attention */
#define COLOR_TX_STATUS_DANGER QColor(200, 100, 100)
/* Transaction list -- TX status decoration - default color */
diff --git a/src/qt/psbtoperationsdialog.cpp b/src/qt/psbtoperationsdialog.cpp
index 0962dfe9db..d328290cbc 100644
--- a/src/qt/psbtoperationsdialog.cpp
+++ b/src/qt/psbtoperationsdialog.cpp
@@ -158,7 +158,7 @@ void PSBTOperationsDialog::saveTransaction() {
if (filename.isEmpty()) {
return;
}
- std::ofstream out(filename.toLocal8Bit().data(), std::ofstream::out | std::ofstream::binary);
+ fsbridge::ofstream out{filename.toLocal8Bit().data(), fsbridge::ofstream::out | fsbridge::ofstream::binary};
out << ssTx.str();
out.close();
showStatus(tr("PSBT saved to disk."), StatusLevel::INFO);
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index 1cadfaeeb9..08729a7722 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -1215,6 +1215,9 @@ void RPCConsole::updateDetailWidget()
}
ui->peerHeight->setText(QString::number(stats->nodeStateStats.m_starting_height));
ui->peerPingWait->setText(GUIUtil::formatPingTime(stats->nodeStateStats.m_ping_wait));
+ ui->peerAddrRelayEnabled->setText(stats->nodeStateStats.m_addr_relay_enabled ? ts.yes : ts.no);
+ ui->peerAddrProcessed->setText(QString::number(stats->nodeStateStats.m_addr_processed));
+ ui->peerAddrRateLimited->setText(QString::number(stats->nodeStateStats.m_addr_rate_limited));
}
ui->peersTabRightPanel->show();
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index 50436a46d8..1206f610cd 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -509,7 +509,7 @@ void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked)
if (filename.isEmpty()) {
return;
}
- std::ofstream out(filename.toLocal8Bit().data(), std::ofstream::out | std::ofstream::binary);
+ fsbridge::ofstream out{filename.toLocal8Bit().data(), fsbridge::ofstream::out | fsbridge::ofstream::binary};
out << ssTx.str();
out.close();
Q_EMIT message(tr("PSBT saved"), "PSBT saved to disk", CClientUIInterface::MSG_INFORMATION);
diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp
index 0504639cde..be5851d627 100644
--- a/src/qt/transactiondesc.cpp
+++ b/src/qt/transactiondesc.cpp
@@ -18,7 +18,6 @@
#include <interfaces/wallet.h>
#include <key_io.h>
#include <policy/policy.h>
-#include <script/script.h>
#include <util/system.h>
#include <validation.h>
#include <wallet/ismine.h>
@@ -35,14 +34,6 @@ using wallet::isminetype;
QString TransactionDesc::FormatTxStatus(const interfaces::WalletTx& wtx, const interfaces::WalletTxStatus& status, bool inMempool, int numBlocks)
{
- if (!status.is_final)
- {
- if (wtx.tx->nLockTime < LOCKTIME_THRESHOLD)
- return tr("Open for %n more block(s)", "", wtx.tx->nLockTime - numBlocks);
- else
- return tr("Open until %1").arg(GUIUtil::dateTimeStr(wtx.tx->nLockTime));
- }
- else
{
int nDepth = status.depth_in_main_chain;
if (nDepth < 0) {
diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp
index 5386569973..26144ba197 100644
--- a/src/qt/transactionrecord.cpp
+++ b/src/qt/transactionrecord.cpp
@@ -179,21 +179,8 @@ void TransactionRecord::updateStatus(const interfaces::WalletTxStatus& wtx, cons
status.depth = wtx.depth_in_main_chain;
status.m_cur_block_hash = block_hash;
- const bool up_to_date = ((int64_t)QDateTime::currentMSecsSinceEpoch() / 1000 - block_time < MAX_BLOCK_TIME_GAP);
- if (up_to_date && !wtx.is_final) {
- if (wtx.lock_time < LOCKTIME_THRESHOLD) {
- status.status = TransactionStatus::OpenUntilBlock;
- status.open_for = wtx.lock_time - numBlocks;
- }
- else
- {
- status.status = TransactionStatus::OpenUntilDate;
- status.open_for = wtx.lock_time;
- }
- }
// For generated transactions, determine maturity
- else if(type == TransactionRecord::Generated)
- {
+ if (type == TransactionRecord::Generated) {
if (wtx.blocks_to_maturity > 0)
{
status.status = TransactionStatus::Immature;
diff --git a/src/qt/transactionrecord.h b/src/qt/transactionrecord.h
index 1c139efabc..dd34656d5f 100644
--- a/src/qt/transactionrecord.h
+++ b/src/qt/transactionrecord.h
@@ -30,8 +30,6 @@ public:
enum Status {
Confirmed, /**< Have 6 or more confirmations (normal tx) or fully mature (mined tx) **/
/// Normal (sent/received) transactions
- OpenUntilDate, /**< Transaction not yet final, waiting for date */
- OpenUntilBlock, /**< Transaction not yet final, waiting for block */
Unconfirmed, /**< Not yet mined into a block **/
Confirming, /**< Confirmed, but waiting for the recommended number of confirmations **/
Conflicted, /**< Conflicts with other transaction or mempool **/
diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp
index b42c3f8c24..44b4fee2e7 100644
--- a/src/qt/transactiontablemodel.cpp
+++ b/src/qt/transactiontablemodel.cpp
@@ -316,12 +316,6 @@ QString TransactionTableModel::formatTxStatus(const TransactionRecord *wtx) cons
switch(wtx->status.status)
{
- case TransactionStatus::OpenUntilBlock:
- status = tr("Open for %n more block(s)","",wtx->status.open_for);
- break;
- case TransactionStatus::OpenUntilDate:
- status = tr("Open until %1").arg(GUIUtil::dateTimeStr(wtx->status.open_for));
- break;
case TransactionStatus::Unconfirmed:
status = tr("Unconfirmed");
break;
@@ -475,9 +469,6 @@ QVariant TransactionTableModel::txStatusDecoration(const TransactionRecord *wtx)
{
switch(wtx->status.status)
{
- case TransactionStatus::OpenUntilBlock:
- case TransactionStatus::OpenUntilDate:
- return COLOR_TX_STATUS_OPENUNTILDATE;
case TransactionStatus::Unconfirmed:
return QIcon(":/icons/transaction_0");
case TransactionStatus::Abandoned:
diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp
index 98f5ebce99..fba83dd510 100644
--- a/src/qt/walletframe.cpp
+++ b/src/qt/walletframe.cpp
@@ -210,7 +210,7 @@ void WalletFrame::gotoLoadPSBT(bool from_clipboard)
Q_EMIT message(tr("Error"), tr("PSBT file must be smaller than 100 MiB"), CClientUIInterface::MSG_ERROR);
return;
}
- std::ifstream in(filename.toLocal8Bit().data(), std::ios::binary);
+ fsbridge::ifstream in{filename.toLocal8Bit().data(), std::ios::binary};
data = std::string(std::istreambuf_iterator<char>{in}, {});
}
diff --git a/src/random.h b/src/random.h
index 5174c553fb..97302d61ab 100644
--- a/src/random.h
+++ b/src/random.h
@@ -89,7 +89,7 @@ constexpr auto GetRandMillis = GetRandomDuration<std::chrono::milliseconds>;
* is memoryless and should be used for repeated network events (e.g. sending a
* certain type of message) to minimize leaking information to observers.
*
- * The probability of an event occuring before time x is 1 - e^-(x/a) where a
+ * The probability of an event occurring before time x is 1 - e^-(x/a) where a
* is the average interval between events.
* */
std::chrono::microseconds GetExponentialRand(std::chrono::microseconds now, std::chrono::seconds average_interval);
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index ccc859619d..7cbe7e6159 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -185,7 +185,7 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIn
case TxVerbosity::SHOW_DETAILS:
case TxVerbosity::SHOW_DETAILS_AND_PREVOUT:
CBlockUndo blockUndo;
- const bool have_undo = !IsBlockPruned(blockindex) && UndoReadFromDisk(blockUndo, blockindex);
+ const bool have_undo{WITH_LOCK(::cs_main, return !IsBlockPruned(blockindex) && UndoReadFromDisk(blockUndo, blockindex))};
for (size_t i = 0; i < block.vtx.size(); ++i) {
const CTransactionRef& tx = block.vtx.at(i);
@@ -790,17 +790,15 @@ static RPCHelpMan getblockfrompeer()
{
return RPCHelpMan{
"getblockfrompeer",
- "\nAttempt to fetch block from a given peer.\n"
- "\nWe must have the header for this block, e.g. using submitheader.\n"
- "\nReturns {} if a block-request was successfully scheduled\n",
+ "Attempt to fetch block from a given peer.\n\n"
+ "We must have the header for this block, e.g. using submitheader.\n"
+ "Subsequent calls for the same block and a new peer will cause the response from the previous peer to be ignored.\n\n"
+ "Returns an empty JSON object if the request was successfully scheduled.",
{
- {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash"},
- {"nodeid", RPCArg::Type::NUM, RPCArg::Optional::NO, "The node ID (see getpeerinfo for node IDs)"},
+ {"block_hash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash to try to fetch"},
+ {"peer_id", RPCArg::Type::NUM, RPCArg::Optional::NO, "The peer to fetch it from (see getpeerinfo for peer IDs)"},
},
- RPCResult{RPCResult::Type::OBJ, "", "",
- {
- {RPCResult::Type::STR, "warnings", /*optional=*/true, "any warnings"},
- }},
+ RPCResult{RPCResult::Type::OBJ, "", /*optional=*/false, "", {}},
RPCExamples{
HelpExampleCli("getblockfrompeer", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\" 0")
+ HelpExampleRpc("getblockfrompeer", "\"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09\" 0")
@@ -810,31 +808,25 @@ static RPCHelpMan getblockfrompeer()
const NodeContext& node = EnsureAnyNodeContext(request.context);
ChainstateManager& chainman = EnsureChainman(node);
PeerManager& peerman = EnsurePeerman(node);
- CConnman& connman = EnsureConnman(node);
- uint256 hash(ParseHashV(request.params[0], "hash"));
+ const uint256& block_hash{ParseHashV(request.params[0], "block_hash")};
+ const NodeId peer_id{request.params[1].get_int64()};
- const NodeId nodeid = static_cast<NodeId>(request.params[1].get_int64());
-
- // Check that the peer with nodeid exists
- if (!connman.ForNode(nodeid, [](CNode* node) {return true;})) {
- throw JSONRPCError(RPC_MISC_ERROR, strprintf("Peer nodeid %d does not exist", nodeid));
- }
-
- const CBlockIndex* const index = WITH_LOCK(cs_main, return chainman.m_blockman.LookupBlockIndex(hash););
+ const CBlockIndex* const index = WITH_LOCK(cs_main, return chainman.m_blockman.LookupBlockIndex(block_hash););
if (!index) {
throw JSONRPCError(RPC_MISC_ERROR, "Block header missing");
}
- UniValue result = UniValue::VOBJ;
+ const bool block_has_data = WITH_LOCK(::cs_main, return index->nStatus & BLOCK_HAVE_DATA);
+ if (block_has_data) {
+ throw JSONRPCError(RPC_MISC_ERROR, "Block already downloaded");
+ }
- if (index->nStatus & BLOCK_HAVE_DATA) {
- result.pushKV("warnings", "Block already downloaded");
- } else if (!peerman.FetchBlock(nodeid, hash, *index)) {
- throw JSONRPCError(RPC_MISC_ERROR, "Failed to fetch block from peer");
+ if (const auto err{peerman.FetchBlock(peer_id, *index)}) {
+ throw JSONRPCError(RPC_MISC_ERROR, err.value());
}
- return result;
+ return UniValue::VOBJ;
},
};
}
@@ -938,8 +930,9 @@ static RPCHelpMan getblockheader()
};
}
-static CBlock GetBlockChecked(const CBlockIndex* pblockindex)
+static CBlock GetBlockChecked(const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
{
+ AssertLockHeld(::cs_main);
CBlock block;
if (IsBlockPruned(pblockindex)) {
throw JSONRPCError(RPC_MISC_ERROR, "Block not available (pruned data)");
@@ -955,8 +948,9 @@ static CBlock GetBlockChecked(const CBlockIndex* pblockindex)
return block;
}
-static CBlockUndo GetUndoChecked(const CBlockIndex* pblockindex)
+static CBlockUndo GetUndoChecked(const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
+ AssertLockHeld(::cs_main);
CBlockUndo blockUndo;
if (IsBlockPruned(pblockindex)) {
throw JSONRPCError(RPC_MISC_ERROR, "Undo data not available (pruned data)");
@@ -1340,6 +1334,7 @@ static RPCHelpMan gettxout()
{RPCResult::Type::STR_AMOUNT, "value", "The transaction value in " + CURRENCY_UNIT},
{RPCResult::Type::OBJ, "scriptPubKey", "", {
{RPCResult::Type::STR, "asm", ""},
+ {RPCResult::Type::STR, "desc", "Inferred descriptor for the output"},
{RPCResult::Type::STR_HEX, "hex", ""},
{RPCResult::Type::STR, "type", "The type, eg pubkeyhash"},
{RPCResult::Type::STR, "address", /*optional=*/true, "The Bitcoin address (only if a well-defined address exists)"},
@@ -1443,7 +1438,7 @@ static void SoftForkDescPushBack(const CBlockIndex* active_chain_tip, UniValue&
UniValue rv(UniValue::VOBJ);
rv.pushKV("type", "buried");
- // getblockchaininfo reports the softfork as active from when the chain height is
+ // getdeploymentinfo reports the softfork as active from when the chain height is
// one below the activation height
rv.pushKV("active", DeploymentActiveAfter(active_chain_tip, params, dep));
rv.pushKV("height", params.DeploymentHeight(dep));
@@ -1455,51 +1450,82 @@ static void SoftForkDescPushBack(const CBlockIndex* active_chain_tip, UniValue&
// For BIP9 deployments.
if (!DeploymentEnabled(consensusParams, id)) return;
+ if (active_chain_tip == nullptr) return;
+
+ auto get_state_name = [](const ThresholdState state) -> std::string {
+ switch (state) {
+ case ThresholdState::DEFINED: return "defined";
+ case ThresholdState::STARTED: return "started";
+ case ThresholdState::LOCKED_IN: return "locked_in";
+ case ThresholdState::ACTIVE: return "active";
+ case ThresholdState::FAILED: return "failed";
+ }
+ return "invalid";
+ };
UniValue bip9(UniValue::VOBJ);
- const ThresholdState thresholdState = g_versionbitscache.State(active_chain_tip, consensusParams, id);
- switch (thresholdState) {
- case ThresholdState::DEFINED: bip9.pushKV("status", "defined"); break;
- case ThresholdState::STARTED: bip9.pushKV("status", "started"); break;
- case ThresholdState::LOCKED_IN: bip9.pushKV("status", "locked_in"); break;
- case ThresholdState::ACTIVE: bip9.pushKV("status", "active"); break;
- case ThresholdState::FAILED: bip9.pushKV("status", "failed"); break;
- }
- const bool has_signal = (ThresholdState::STARTED == thresholdState || ThresholdState::LOCKED_IN == thresholdState);
+
+ const ThresholdState next_state = g_versionbitscache.State(active_chain_tip, consensusParams, id);
+ const ThresholdState current_state = g_versionbitscache.State(active_chain_tip->pprev, consensusParams, id);
+
+ const bool has_signal = (ThresholdState::STARTED == current_state || ThresholdState::LOCKED_IN == current_state);
+
+ // BIP9 parameters
if (has_signal) {
bip9.pushKV("bit", consensusParams.vDeployments[id].bit);
}
bip9.pushKV("start_time", consensusParams.vDeployments[id].nStartTime);
bip9.pushKV("timeout", consensusParams.vDeployments[id].nTimeout);
- int64_t since_height = g_versionbitscache.StateSinceHeight(active_chain_tip, consensusParams, id);
- bip9.pushKV("since", since_height);
+ bip9.pushKV("min_activation_height", consensusParams.vDeployments[id].min_activation_height);
+
+ // BIP9 status
+ bip9.pushKV("status", get_state_name(current_state));
+ bip9.pushKV("since", g_versionbitscache.StateSinceHeight(active_chain_tip->pprev, consensusParams, id));
+ bip9.pushKV("status-next", get_state_name(next_state));
+
+ // BIP9 signalling status, if applicable
if (has_signal) {
UniValue statsUV(UniValue::VOBJ);
- BIP9Stats statsStruct = g_versionbitscache.Statistics(active_chain_tip, consensusParams, id);
+ std::vector<bool> signals;
+ BIP9Stats statsStruct = g_versionbitscache.Statistics(active_chain_tip, consensusParams, id, &signals);
statsUV.pushKV("period", statsStruct.period);
statsUV.pushKV("elapsed", statsStruct.elapsed);
statsUV.pushKV("count", statsStruct.count);
- if (ThresholdState::LOCKED_IN != thresholdState) {
+ if (ThresholdState::LOCKED_IN != current_state) {
statsUV.pushKV("threshold", statsStruct.threshold);
statsUV.pushKV("possible", statsStruct.possible);
}
bip9.pushKV("statistics", statsUV);
+
+ std::string sig;
+ sig.reserve(signals.size());
+ for (const bool s : signals) {
+ sig.push_back(s ? '#' : '-');
+ }
+ bip9.pushKV("signalling", sig);
}
- bip9.pushKV("min_activation_height", consensusParams.vDeployments[id].min_activation_height);
UniValue rv(UniValue::VOBJ);
rv.pushKV("type", "bip9");
- rv.pushKV("bip9", bip9);
- if (ThresholdState::ACTIVE == thresholdState) {
- rv.pushKV("height", since_height);
+ if (ThresholdState::ACTIVE == next_state) {
+ rv.pushKV("height", g_versionbitscache.StateSinceHeight(active_chain_tip, consensusParams, id));
}
- rv.pushKV("active", ThresholdState::ACTIVE == thresholdState);
+ rv.pushKV("active", ThresholdState::ACTIVE == next_state);
+ rv.pushKV("bip9", bip9);
softforks.pushKV(DeploymentName(id), rv);
}
+namespace {
+/* TODO: when -dprecatedrpc=softforks is removed, drop these */
+UniValue DeploymentInfo(const CBlockIndex* tip, const Consensus::Params& consensusParams);
+extern const std::vector<RPCResult> RPCHelpForDeployment;
+}
+
+// used by rest.cpp:rest_chaininfo, so cannot be static
RPCHelpMan getblockchaininfo()
{
+ /* TODO: from v24, remove -deprecatedrpc=softforks */
return RPCHelpMan{"getblockchaininfo",
"Returns an object containing various state info regarding blockchain processing.\n",
{},
@@ -1521,31 +1547,11 @@ RPCHelpMan getblockchaininfo()
{RPCResult::Type::NUM, "pruneheight", /*optional=*/true, "lowest-height complete block stored (only present if pruning is enabled)"},
{RPCResult::Type::BOOL, "automatic_pruning", /*optional=*/true, "whether automatic pruning is enabled (only present if pruning is enabled)"},
{RPCResult::Type::NUM, "prune_target_size", /*optional=*/true, "the target size used by pruning (only present if automatic pruning is enabled)"},
- {RPCResult::Type::OBJ_DYN, "softforks", "status of softforks",
+ {RPCResult::Type::OBJ_DYN, "softforks", "(DEPRECATED, returned only if config option -deprecatedrpc=softforks is passed) status of softforks",
{
{RPCResult::Type::OBJ, "xxxx", "name of the softfork",
- {
- {RPCResult::Type::STR, "type", "one of \"buried\", \"bip9\""},
- {RPCResult::Type::OBJ, "bip9", /*optional=*/true, "status of bip9 softforks (only for \"bip9\" type)",
- {
- {RPCResult::Type::STR, "status", "one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\""},
- {RPCResult::Type::NUM, "bit", /*optional=*/true, "the bit (0-28) in the block version field used to signal this softfork (only for \"started\" and \"locked_in\" status)"},
- {RPCResult::Type::NUM_TIME, "start_time", "the minimum median time past of a block at which the bit gains its meaning"},
- {RPCResult::Type::NUM_TIME, "timeout", "the median time past of a block at which the deployment is considered failed if not yet locked in"},
- {RPCResult::Type::NUM, "since", "height of the first block to which the status applies"},
- {RPCResult::Type::NUM, "min_activation_height", "minimum height of blocks for which the rules may be enforced"},
- {RPCResult::Type::OBJ, "statistics", /*optional=*/true, "numeric statistics about signalling for a softfork (only for \"started\" and \"locked_in\" status)",
- {
- {RPCResult::Type::NUM, "period", "the length in blocks of the signalling period"},
- {RPCResult::Type::NUM, "threshold", /*optional=*/true, "the number of blocks with the version bit set required to activate the feature (only for \"started\" status)"},
- {RPCResult::Type::NUM, "elapsed", "the number of blocks elapsed since the beginning of the current period"},
- {RPCResult::Type::NUM, "count", "the number of blocks with the version bit set in the current period"},
- {RPCResult::Type::BOOL, "possible", /*optional=*/true, "returns false if there are not enough blocks left in this period to pass activation threshold (only for \"started\" status)"},
- }},
- }},
- {RPCResult::Type::NUM, "height", /*optional=*/true, "height of the first block which the rules are or will be enforced (only for \"buried\" type, or \"bip9\" type with \"active\" status)"},
- {RPCResult::Type::BOOL, "active", "true if the rules are enforced for the mempool and the next block"},
- }},
+ RPCHelpForDeployment
+ },
}},
{RPCResult::Type::STR, "warnings", "any network and blockchain warnings"},
}},
@@ -1593,7 +1599,45 @@ RPCHelpMan getblockchaininfo()
}
}
- const Consensus::Params& consensusParams = Params().GetConsensus();
+ if (IsDeprecatedRPCEnabled("softforks")) {
+ const Consensus::Params& consensusParams = Params().GetConsensus();
+ obj.pushKV("softforks", DeploymentInfo(tip, consensusParams));
+ }
+
+ obj.pushKV("warnings", GetWarnings(false).original);
+ return obj;
+},
+ };
+}
+
+namespace {
+const std::vector<RPCResult> RPCHelpForDeployment{
+ {RPCResult::Type::STR, "type", "one of \"buried\", \"bip9\""},
+ {RPCResult::Type::NUM, "height", /*optional=*/true, "height of the first block which the rules are or will be enforced (only for \"buried\" type, or \"bip9\" type with \"active\" status)"},
+ {RPCResult::Type::BOOL, "active", "true if the rules are enforced for the mempool and the next block"},
+ {RPCResult::Type::OBJ, "bip9", /*optional=*/true, "status of bip9 softforks (only for \"bip9\" type)",
+ {
+ {RPCResult::Type::NUM, "bit", /*optional=*/true, "the bit (0-28) in the block version field used to signal this softfork (only for \"started\" and \"locked_in\" status)"},
+ {RPCResult::Type::NUM_TIME, "start_time", "the minimum median time past of a block at which the bit gains its meaning"},
+ {RPCResult::Type::NUM_TIME, "timeout", "the median time past of a block at which the deployment is considered failed if not yet locked in"},
+ {RPCResult::Type::NUM, "min_activation_height", "minimum height of blocks for which the rules may be enforced"},
+ {RPCResult::Type::STR, "status", "bip9 status of specified block (one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\")"},
+ {RPCResult::Type::NUM, "since", "height of the first block to which the status applies"},
+ {RPCResult::Type::STR, "status-next", "bip9 status of next block"},
+ {RPCResult::Type::OBJ, "statistics", /*optional=*/true, "numeric statistics about signalling for a softfork (only for \"started\" and \"locked_in\" status)",
+ {
+ {RPCResult::Type::NUM, "period", "the length in blocks of the signalling period"},
+ {RPCResult::Type::NUM, "threshold", /*optional=*/true, "the number of blocks with the version bit set required to activate the feature (only for \"started\" status)"},
+ {RPCResult::Type::NUM, "elapsed", "the number of blocks elapsed since the beginning of the current period"},
+ {RPCResult::Type::NUM, "count", "the number of blocks with the version bit set in the current period"},
+ {RPCResult::Type::BOOL, "possible", /*optional=*/true, "returns false if there are not enough blocks left in this period to pass activation threshold (only for \"started\" status)"},
+ }},
+ {RPCResult::Type::STR, "signalling", "indicates blocks that signalled with a # and blocks that did not with a -"},
+ }},
+};
+
+UniValue DeploymentInfo(const CBlockIndex* tip, const Consensus::Params& consensusParams)
+{
UniValue softforks(UniValue::VOBJ);
SoftForkDescPushBack(tip, softforks, consensusParams, Consensus::DEPLOYMENT_HEIGHTINCB);
SoftForkDescPushBack(tip, softforks, consensusParams, Consensus::DEPLOYMENT_DERSIG);
@@ -1602,11 +1646,53 @@ RPCHelpMan getblockchaininfo()
SoftForkDescPushBack(tip, softforks, consensusParams, Consensus::DEPLOYMENT_SEGWIT);
SoftForkDescPushBack(tip, softforks, consensusParams, Consensus::DEPLOYMENT_TESTDUMMY);
SoftForkDescPushBack(tip, softforks, consensusParams, Consensus::DEPLOYMENT_TAPROOT);
- obj.pushKV("softforks", softforks);
+ return softforks;
+}
+} // anon namespace
- obj.pushKV("warnings", GetWarnings(false).original);
- return obj;
-},
+static RPCHelpMan getdeploymentinfo()
+{
+ return RPCHelpMan{"getdeploymentinfo",
+ "Returns an object containing various state info regarding soft-forks.",
+ {
+ {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Default{"chain tip"}, "The block hash at which to query fork state"},
+ },
+ RPCResult{
+ RPCResult::Type::OBJ, "", "", {
+ {RPCResult::Type::STR, "hash", "requested block hash (or tip)"},
+ {RPCResult::Type::NUM, "height", "requested block height (or tip)"},
+ {RPCResult::Type::OBJ, "deployments", "", {
+ {RPCResult::Type::OBJ, "xxxx", "name of the deployment", RPCHelpForDeployment}
+ }},
+ }
+ },
+ RPCExamples{ HelpExampleCli("getdeploymentinfo", "") + HelpExampleRpc("getdeploymentinfo", "") },
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+ {
+ ChainstateManager& chainman = EnsureAnyChainman(request.context);
+ LOCK(cs_main);
+ CChainState& active_chainstate = chainman.ActiveChainstate();
+
+ const CBlockIndex* tip;
+ if (request.params[0].isNull()) {
+ tip = active_chainstate.m_chain.Tip();
+ CHECK_NONFATAL(tip);
+ } else {
+ uint256 hash(ParseHashV(request.params[0], "blockhash"));
+ tip = chainman.m_blockman.LookupBlockIndex(hash);
+ if (!tip) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
+ }
+ }
+
+ const Consensus::Params& consensusParams = Params().GetConsensus();
+
+ UniValue deploymentinfo(UniValue::VOBJ);
+ deploymentinfo.pushKV("hash", tip->GetBlockHash().ToString());
+ deploymentinfo.pushKV("height", tip->nHeight);
+ deploymentinfo.pushKV("deployments", DeploymentInfo(tip, consensusParams));
+ return deploymentinfo;
+ },
};
}
@@ -2756,6 +2842,7 @@ static const CRPCCommand commands[] =
{ "blockchain", &getblockheader, },
{ "blockchain", &getchaintips, },
{ "blockchain", &getdifficulty, },
+ { "blockchain", &getdeploymentinfo, },
{ "blockchain", &getmempoolancestors, },
{ "blockchain", &getmempooldescendants, },
{ "blockchain", &getmempoolentry, },
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index 003ba8bb20..c480a093a4 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -60,7 +60,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "getbalance", 1, "minconf" },
{ "getbalance", 2, "include_watchonly" },
{ "getbalance", 3, "avoid_reuse" },
- { "getblockfrompeer", 1, "nodeid" },
+ { "getblockfrompeer", 1, "peer_id" },
{ "getblockhash", 0, "height" },
{ "waitforblockheight", 0, "height" },
{ "waitforblockheight", 1, "timeout" },
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index f227fde0f7..ff0d8a4e0f 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -179,6 +179,7 @@ static RPCHelpMan getrawtransaction()
{RPCResult::Type::OBJ, "scriptPubKey", "",
{
{RPCResult::Type::STR, "asm", "the asm"},
+ {RPCResult::Type::STR, "desc", "Inferred descriptor for the output"},
{RPCResult::Type::STR, "hex", "the hex"},
{RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"},
{RPCResult::Type::STR, "address", /*optional=*/true, "The Bitcoin address (only if a well-defined address exists)"},
@@ -240,7 +241,8 @@ static RPCHelpMan getrawtransaction()
if (!tx) {
std::string errmsg;
if (blockindex) {
- if (!(blockindex->nStatus & BLOCK_HAVE_DATA)) {
+ const bool block_has_data = WITH_LOCK(::cs_main, return blockindex->nStatus & BLOCK_HAVE_DATA);
+ if (!block_has_data) {
throw JSONRPCError(RPC_MISC_ERROR, "Block not available");
}
errmsg = "No such transaction found in the provided block";
@@ -506,6 +508,7 @@ static RPCHelpMan decoderawtransaction()
{RPCResult::Type::OBJ, "scriptPubKey", "",
{
{RPCResult::Type::STR, "asm", "the asm"},
+ {RPCResult::Type::STR, "desc", "Inferred descriptor for the output"},
{RPCResult::Type::STR_HEX, "hex", "the hex"},
{RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"},
{RPCResult::Type::STR, "address", /*optional=*/true, "The Bitcoin address (only if a well-defined address exists)"},
@@ -561,6 +564,7 @@ static RPCHelpMan decodescript()
RPCResult::Type::OBJ, "", "",
{
{RPCResult::Type::STR, "asm", "Script public key"},
+ {RPCResult::Type::STR, "desc", "Inferred descriptor for the script"},
{RPCResult::Type::STR, "type", "The output type (e.g. " + GetAllOutputTypes() + ")"},
{RPCResult::Type::STR, "address", /*optional=*/true, "The Bitcoin address (only if a well-defined address exists)"},
{RPCResult::Type::STR, "p2sh", /*optional=*/true,
@@ -572,6 +576,7 @@ static RPCHelpMan decodescript()
{RPCResult::Type::STR_HEX, "hex", "Hex string of the script public key"},
{RPCResult::Type::STR, "type", "The type of the script public key (e.g. witness_v0_keyhash or witness_v0_scripthash)"},
{RPCResult::Type::STR, "address", /*optional=*/true, "The Bitcoin address (only if a well-defined address exists)"},
+ {RPCResult::Type::STR, "desc", "Inferred descriptor for the script"},
{RPCResult::Type::STR, "p2sh-segwit", "address of the P2SH script wrapping this witness redeem script"},
}},
},
diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp
index 57e3da0351..5ef7e26ce8 100644
--- a/src/rpc/util.cpp
+++ b/src/rpc/util.cpp
@@ -831,11 +831,14 @@ void RPCResult::ToSections(Sections& sections, const OuterType outer_type, const
}
case Type::OBJ_DYN:
case Type::OBJ: {
+ if (m_inner.empty()) {
+ sections.PushSection({indent + maybe_key + "{}", Description("empty JSON object")});
+ return;
+ }
sections.PushSection({indent + maybe_key + "{", Description("json object")});
for (const auto& i : m_inner) {
i.ToSections(sections, OuterType::OBJ, current_indent + 2);
}
- CHECK_NONFATAL(!m_inner.empty());
if (m_type == Type::OBJ_DYN && m_inner.back().m_type != Type::ELISION) {
// If the dictionary keys are dynamic, use three dots for continuation
sections.PushSection({indent_next + "...", ""});
@@ -886,6 +889,17 @@ bool RPCResult::MatchesType(const UniValue& result) const
CHECK_NONFATAL(false);
}
+void RPCResult::CheckInnerDoc() const
+{
+ if (m_type == Type::OBJ) {
+ // May or may not be empty
+ return;
+ }
+ // Everything else must either be empty or not
+ const bool inner_needed{m_type == Type::ARR || m_type == Type::ARR_FIXED || m_type == Type::OBJ_DYN};
+ CHECK_NONFATAL(inner_needed != m_inner.empty());
+}
+
std::string RPCArg::ToStringObj(const bool oneline) const
{
std::string res;
diff --git a/src/rpc/util.h b/src/rpc/util.h
index d43ee33b0f..25ebf78fa1 100644
--- a/src/rpc/util.h
+++ b/src/rpc/util.h
@@ -267,8 +267,7 @@ struct RPCResult {
m_cond{std::move(cond)}
{
CHECK_NONFATAL(!m_cond.empty());
- const bool inner_needed{type == Type::ARR || type == Type::ARR_FIXED || type == Type::OBJ || type == Type::OBJ_DYN};
- CHECK_NONFATAL(inner_needed != inner.empty());
+ CheckInnerDoc();
}
RPCResult(
@@ -292,8 +291,7 @@ struct RPCResult {
m_description{std::move(description)},
m_cond{}
{
- const bool inner_needed{type == Type::ARR || type == Type::ARR_FIXED || type == Type::OBJ || type == Type::OBJ_DYN};
- CHECK_NONFATAL(inner_needed != inner.empty());
+ CheckInnerDoc();
}
RPCResult(
@@ -311,6 +309,9 @@ struct RPCResult {
std::string ToDescriptionString() const;
/** Check whether the result JSON type matches. */
bool MatchesType(const UniValue& result) const;
+
+private:
+ void CheckInnerDoc() const;
};
struct RPCResults {
diff --git a/src/script/bitcoinconsensus.cpp b/src/script/bitcoinconsensus.cpp
index dd15e6104c..f7f9dfc262 100644
--- a/src/script/bitcoinconsensus.cpp
+++ b/src/script/bitcoinconsensus.cpp
@@ -22,20 +22,23 @@ public:
m_remaining(txToLen)
{}
- void read(char* pch, size_t nSize)
+ void read(Span<std::byte> dst)
{
- if (nSize > m_remaining)
+ if (dst.size() > m_remaining) {
throw std::ios_base::failure(std::string(__func__) + ": end of data");
+ }
- if (pch == nullptr)
+ if (dst.data() == nullptr) {
throw std::ios_base::failure(std::string(__func__) + ": bad destination buffer");
+ }
- if (m_data == nullptr)
+ if (m_data == nullptr) {
throw std::ios_base::failure(std::string(__func__) + ": bad source buffer");
+ }
- memcpy(pch, m_data, nSize);
- m_remaining -= nSize;
- m_data += nSize;
+ memcpy(dst.data(), m_data, dst.size());
+ m_remaining -= dst.size();
+ m_data += dst.size();
}
template<typename T>
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
index 07b44971b7..11b1a1c887 100644
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -1303,12 +1303,12 @@ public:
it = itBegin;
while (scriptCode.GetOp(it, opcode)) {
if (opcode == OP_CODESEPARATOR) {
- s.write((char*)&itBegin[0], it-itBegin-1);
+ s.write(AsBytes(Span{&itBegin[0], size_t(it - itBegin - 1)}));
itBegin = it;
}
}
if (itBegin != scriptCode.end())
- s.write((char*)&itBegin[0], it-itBegin);
+ s.write(AsBytes(Span{&itBegin[0], size_t(it - itBegin)}));
}
/** Serialize an input of txTo */
diff --git a/src/serialize.h b/src/serialize.h
index 4cc4b0338c..44bb471f25 100644
--- a/src/serialize.h
+++ b/src/serialize.h
@@ -47,79 +47,72 @@ static const unsigned int MAX_VECTOR_ALLOCATE = 5000000;
struct deserialize_type {};
constexpr deserialize_type deserialize {};
-//! Safely convert odd char pointer types to standard ones.
-inline char* CharCast(char* c) { return c; }
-inline char* CharCast(unsigned char* c) { return (char*)c; }
-inline const char* CharCast(const char* c) { return c; }
-inline const char* CharCast(const unsigned char* c) { return (const char*)c; }
-
/*
* Lowest-level serialization and conversion.
- * @note Sizes of these types are verified in the tests
*/
template<typename Stream> inline void ser_writedata8(Stream &s, uint8_t obj)
{
- s.write((char*)&obj, 1);
+ s.write(AsBytes(Span{&obj, 1}));
}
template<typename Stream> inline void ser_writedata16(Stream &s, uint16_t obj)
{
obj = htole16(obj);
- s.write((char*)&obj, 2);
+ s.write(AsBytes(Span{&obj, 1}));
}
template<typename Stream> inline void ser_writedata16be(Stream &s, uint16_t obj)
{
obj = htobe16(obj);
- s.write((char*)&obj, 2);
+ s.write(AsBytes(Span{&obj, 1}));
}
template<typename Stream> inline void ser_writedata32(Stream &s, uint32_t obj)
{
obj = htole32(obj);
- s.write((char*)&obj, 4);
+ s.write(AsBytes(Span{&obj, 1}));
}
template<typename Stream> inline void ser_writedata32be(Stream &s, uint32_t obj)
{
obj = htobe32(obj);
- s.write((char*)&obj, 4);
+ s.write(AsBytes(Span{&obj, 1}));
}
template<typename Stream> inline void ser_writedata64(Stream &s, uint64_t obj)
{
obj = htole64(obj);
- s.write((char*)&obj, 8);
+ s.write(AsBytes(Span{&obj, 1}));
}
template<typename Stream> inline uint8_t ser_readdata8(Stream &s)
{
uint8_t obj;
- s.read((char*)&obj, 1);
+ s.read(AsWritableBytes(Span{&obj, 1}));
return obj;
}
template<typename Stream> inline uint16_t ser_readdata16(Stream &s)
{
uint16_t obj;
- s.read((char*)&obj, 2);
+ s.read(AsWritableBytes(Span{&obj, 1}));
return le16toh(obj);
}
template<typename Stream> inline uint16_t ser_readdata16be(Stream &s)
{
uint16_t obj;
- s.read((char*)&obj, 2);
+ s.read(AsWritableBytes(Span{&obj, 1}));
return be16toh(obj);
}
template<typename Stream> inline uint32_t ser_readdata32(Stream &s)
{
uint32_t obj;
- s.read((char*)&obj, 4);
+ s.read(AsWritableBytes(Span{&obj, 1}));
return le32toh(obj);
}
template<typename Stream> inline uint32_t ser_readdata32be(Stream &s)
{
uint32_t obj;
- s.read((char*)&obj, 4);
+ s.read(AsWritableBytes(Span{&obj, 1}));
return be32toh(obj);
}
template<typename Stream> inline uint64_t ser_readdata64(Stream &s)
{
uint64_t obj;
- s.read((char*)&obj, 8);
+ s.read(AsWritableBytes(Span{&obj, 1}));
return le64toh(obj);
}
@@ -127,7 +120,7 @@ template<typename Stream> inline uint64_t ser_readdata64(Stream &s)
/////////////////////////////////////////////////////////////////
//
// Templates for serializing to anything that looks like a stream,
-// i.e. anything that supports .read(char*, size_t) and .write(char*, size_t)
+// i.e. anything that supports .read(Span<std::byte>) and .write(Span<const std::byte>)
//
class CSizeComputer;
@@ -196,7 +189,7 @@ template<typename X> const X& ReadWriteAsHelper(const X& x) { return x; }
FORMATTER_METHODS(cls, obj)
#ifndef CHAR_EQUALS_INT8
-template<typename Stream> inline void Serialize(Stream& s, char a ) { ser_writedata8(s, a); } // TODO Get rid of bare char
+template <typename Stream> void Serialize(Stream&, char) = delete; // char serialization forbidden. Use uint8_t or int8_t
#endif
template<typename Stream> inline void Serialize(Stream& s, int8_t a ) { ser_writedata8(s, a); }
template<typename Stream> inline void Serialize(Stream& s, uint8_t a ) { ser_writedata8(s, a); }
@@ -206,13 +199,13 @@ template<typename Stream> inline void Serialize(Stream& s, int32_t a ) { ser_wri
template<typename Stream> inline void Serialize(Stream& s, uint32_t a) { ser_writedata32(s, a); }
template<typename Stream> inline void Serialize(Stream& s, int64_t a ) { ser_writedata64(s, a); }
template<typename Stream> inline void Serialize(Stream& s, uint64_t a) { ser_writedata64(s, a); }
-template<typename Stream, int N> inline void Serialize(Stream& s, const char (&a)[N]) { s.write(a, N); }
-template<typename Stream, int N> inline void Serialize(Stream& s, const unsigned char (&a)[N]) { s.write(CharCast(a), N); }
-template<typename Stream> inline void Serialize(Stream& s, const Span<const unsigned char>& span) { s.write(CharCast(span.data()), span.size()); }
-template<typename Stream> inline void Serialize(Stream& s, const Span<unsigned char>& span) { s.write(CharCast(span.data()), span.size()); }
+template<typename Stream, int N> inline void Serialize(Stream& s, const char (&a)[N]) { s.write(MakeByteSpan(a)); }
+template<typename Stream, int N> inline void Serialize(Stream& s, const unsigned char (&a)[N]) { s.write(MakeByteSpan(a)); }
+template<typename Stream> inline void Serialize(Stream& s, const Span<const unsigned char>& span) { s.write(AsBytes(span)); }
+template<typename Stream> inline void Serialize(Stream& s, const Span<unsigned char>& span) { s.write(AsBytes(span)); }
#ifndef CHAR_EQUALS_INT8
-template<typename Stream> inline void Unserialize(Stream& s, char& a ) { a = ser_readdata8(s); } // TODO Get rid of bare char
+template <typename Stream> void Unserialize(Stream&, char) = delete; // char serialization forbidden. Use uint8_t or int8_t
#endif
template<typename Stream> inline void Unserialize(Stream& s, int8_t& a ) { a = ser_readdata8(s); }
template<typename Stream> inline void Unserialize(Stream& s, uint8_t& a ) { a = ser_readdata8(s); }
@@ -222,9 +215,9 @@ template<typename Stream> inline void Unserialize(Stream& s, int32_t& a ) { a =
template<typename Stream> inline void Unserialize(Stream& s, uint32_t& a) { a = ser_readdata32(s); }
template<typename Stream> inline void Unserialize(Stream& s, int64_t& a ) { a = ser_readdata64(s); }
template<typename Stream> inline void Unserialize(Stream& s, uint64_t& a) { a = ser_readdata64(s); }
-template<typename Stream, int N> inline void Unserialize(Stream& s, char (&a)[N]) { s.read(a, N); }
-template<typename Stream, int N> inline void Unserialize(Stream& s, unsigned char (&a)[N]) { s.read(CharCast(a), N); }
-template<typename Stream> inline void Unserialize(Stream& s, Span<unsigned char>& span) { s.read(CharCast(span.data()), span.size()); }
+template<typename Stream, int N> inline void Unserialize(Stream& s, char (&a)[N]) { s.read(MakeWritableByteSpan(a)); }
+template<typename Stream, int N> inline void Unserialize(Stream& s, unsigned char (&a)[N]) { s.read(MakeWritableByteSpan(a)); }
+template<typename Stream> inline void Unserialize(Stream& s, Span<unsigned char>& span) { s.read(AsWritableBytes(span)); }
template <typename Stream> inline void Serialize(Stream& s, bool a) { uint8_t f = a; ser_writedata8(s, f); }
template <typename Stream> inline void Unserialize(Stream& s, bool& a) { uint8_t f = ser_readdata8(s); a = f; }
@@ -479,10 +472,10 @@ struct CustomUintFormatter
if (v < 0 || v > MAX) throw std::ios_base::failure("CustomUintFormatter value out of range");
if (BigEndian) {
uint64_t raw = htobe64(v);
- s.write(((const char*)&raw) + 8 - Bytes, Bytes);
+ s.write({BytePtr(&raw) + 8 - Bytes, Bytes});
} else {
uint64_t raw = htole64(v);
- s.write((const char*)&raw, Bytes);
+ s.write({BytePtr(&raw), Bytes});
}
}
@@ -492,10 +485,10 @@ struct CustomUintFormatter
static_assert(std::numeric_limits<U>::max() >= MAX && std::numeric_limits<U>::min() <= 0, "Assigned type too small");
uint64_t raw = 0;
if (BigEndian) {
- s.read(((char*)&raw) + 8 - Bytes, Bytes);
+ s.read({BytePtr(&raw) + 8 - Bytes, Bytes});
v = static_cast<I>(be64toh(raw));
} else {
- s.read((char*)&raw, Bytes);
+ s.read({BytePtr(&raw), Bytes});
v = static_cast<I>(le64toh(raw));
}
}
@@ -551,7 +544,7 @@ struct LimitedStringFormatter
throw std::ios_base::failure("String length limit exceeded");
}
v.resize(size);
- if (size != 0) s.read((char*)v.data(), size);
+ if (size != 0) s.read(MakeWritableByteSpan(v));
}
template<typename Stream>
@@ -715,7 +708,7 @@ void Serialize(Stream& os, const std::basic_string<C>& str)
{
WriteCompactSize(os, str.size());
if (!str.empty())
- os.write((char*)str.data(), str.size() * sizeof(C));
+ os.write(MakeByteSpan(str));
}
template<typename Stream, typename C>
@@ -724,7 +717,7 @@ void Unserialize(Stream& is, std::basic_string<C>& str)
unsigned int nSize = ReadCompactSize(is);
str.resize(nSize);
if (nSize != 0)
- is.read((char*)str.data(), nSize * sizeof(C));
+ is.read(MakeWritableByteSpan(str));
}
@@ -737,7 +730,7 @@ void Serialize_impl(Stream& os, const prevector<N, T>& v, const unsigned char&)
{
WriteCompactSize(os, v.size());
if (!v.empty())
- os.write((char*)v.data(), v.size() * sizeof(T));
+ os.write(MakeByteSpan(v));
}
template<typename Stream, unsigned int N, typename T, typename V>
@@ -764,7 +757,7 @@ void Unserialize_impl(Stream& is, prevector<N, T>& v, const unsigned char&)
{
unsigned int blk = std::min(nSize - i, (unsigned int)(1 + 4999999 / sizeof(T)));
v.resize_uninitialized(i + blk);
- is.read((char*)&v[i], blk * sizeof(T));
+ is.read(AsWritableBytes(Span{&v[i], blk}));
i += blk;
}
}
@@ -791,7 +784,7 @@ void Serialize_impl(Stream& os, const std::vector<T, A>& v, const unsigned char&
{
WriteCompactSize(os, v.size());
if (!v.empty())
- os.write((char*)v.data(), v.size() * sizeof(T));
+ os.write(MakeByteSpan(v));
}
template<typename Stream, typename T, typename A>
@@ -830,7 +823,7 @@ void Unserialize_impl(Stream& is, std::vector<T, A>& v, const unsigned char&)
{
unsigned int blk = std::min(nSize - i, (unsigned int)(1 + 4999999 / sizeof(T)));
v.resize(i + blk);
- is.read((char*)&v[i], blk * sizeof(T));
+ is.read(AsWritableBytes(Span{&v[i], blk}));
i += blk;
}
}
@@ -995,9 +988,9 @@ protected:
public:
explicit CSizeComputer(int nVersionIn) : nSize(0), nVersion(nVersionIn) {}
- void write(const char *psz, size_t _nSize)
+ void write(Span<const std::byte> src)
{
- this->nSize += _nSize;
+ this->nSize += src.size();
}
/** Pretend _nSize bytes are written, without specifying them. */
diff --git a/src/span.h b/src/span.h
index 47390a5bb2..b627b993c2 100644
--- a/src/span.h
+++ b/src/span.h
@@ -243,16 +243,21 @@ T& SpanPopBack(Span<T>& span)
return back;
}
+//! Convert a data pointer to a std::byte data pointer.
+//! Where possible, please use the safer AsBytes helpers.
+inline const std::byte* BytePtr(const void* data) { return reinterpret_cast<const std::byte*>(data); }
+inline std::byte* BytePtr(void* data) { return reinterpret_cast<std::byte*>(data); }
+
// From C++20 as_bytes and as_writeable_bytes
template <typename T>
Span<const std::byte> AsBytes(Span<T> s) noexcept
{
- return {reinterpret_cast<const std::byte*>(s.data()), s.size_bytes()};
+ return {BytePtr(s.data()), s.size_bytes()};
}
template <typename T>
Span<std::byte> AsWritableBytes(Span<T> s) noexcept
{
- return {reinterpret_cast<std::byte*>(s.data()), s.size_bytes()};
+ return {BytePtr(s.data()), s.size_bytes()};
}
template <typename V>
diff --git a/src/streams.h b/src/streams.h
index 384bb632b0..2f26be6dd8 100644
--- a/src/streams.h
+++ b/src/streams.h
@@ -49,14 +49,14 @@ public:
return (*this);
}
- void write(const char* pch, size_t nSize)
+ void write(Span<const std::byte> src)
{
- stream->write(pch, nSize);
+ stream->write(src);
}
- void read(char* pch, size_t nSize)
+ void read(Span<std::byte> dst)
{
- stream->read(pch, nSize);
+ stream->read(dst);
}
int GetVersion() const { return nVersion; }
@@ -94,17 +94,17 @@ class CVectorWriter
{
::SerializeMany(*this, std::forward<Args>(args)...);
}
- void write(const char* pch, size_t nSize)
+ void write(Span<const std::byte> src)
{
assert(nPos <= vchData.size());
- size_t nOverwrite = std::min(nSize, vchData.size() - nPos);
+ size_t nOverwrite = std::min(src.size(), vchData.size() - nPos);
if (nOverwrite) {
- memcpy(vchData.data() + nPos, reinterpret_cast<const unsigned char*>(pch), nOverwrite);
+ memcpy(vchData.data() + nPos, src.data(), nOverwrite);
}
- if (nOverwrite < nSize) {
- vchData.insert(vchData.end(), reinterpret_cast<const unsigned char*>(pch) + nOverwrite, reinterpret_cast<const unsigned char*>(pch) + nSize);
+ if (nOverwrite < src.size()) {
+ vchData.insert(vchData.end(), UCharCast(src.data()) + nOverwrite, UCharCast(src.end()));
}
- nPos += nSize;
+ nPos += src.size();
}
template<typename T>
CVectorWriter& operator<<(const T& obj)
@@ -161,18 +161,18 @@ public:
size_t size() const { return m_data.size(); }
bool empty() const { return m_data.empty(); }
- void read(char* dst, size_t n)
+ void read(Span<std::byte> dst)
{
- if (n == 0) {
+ if (dst.size() == 0) {
return;
}
// Read from the beginning of the buffer
- if (n > m_data.size()) {
+ if (dst.size() > m_data.size()) {
throw std::ios_base::failure("SpanReader::read(): end of data");
}
- memcpy(dst, m_data.data(), n);
- m_data = m_data.subspan(n);
+ memcpy(dst.data(), m_data.data(), dst.size());
+ m_data = m_data.subspan(dst.size());
}
};
@@ -206,6 +206,7 @@ public:
: nType{nTypeIn},
nVersion{nVersionIn} {}
+ explicit CDataStream(Span<const uint8_t> sp, int type, int version) : CDataStream{AsBytes(sp), type, version} {}
explicit CDataStream(Span<const value_type> sp, int nTypeIn, int nVersionIn)
: vch(sp.data(), sp.data() + sp.size()),
nType{nTypeIn},
@@ -221,7 +222,7 @@ public:
std::string str() const
{
- return (std::string(begin(), end()));
+ return std::string{UCharCast(data()), UCharCast(data() + size())};
}
@@ -342,16 +343,16 @@ public:
void SetVersion(int n) { nVersion = n; }
int GetVersion() const { return nVersion; }
- void read(char* pch, size_t nSize)
+ void read(Span<value_type> dst)
{
- if (nSize == 0) return;
+ if (dst.size() == 0) return;
// Read from the beginning of the buffer
- unsigned int nReadPosNext = nReadPos + nSize;
+ unsigned int nReadPosNext = nReadPos + dst.size();
if (nReadPosNext > vch.size()) {
throw std::ios_base::failure("CDataStream::read(): end of data");
}
- memcpy(pch, &vch[nReadPos], nSize);
+ memcpy(dst.data(), &vch[nReadPos], dst.size());
if (nReadPosNext == vch.size())
{
nReadPos = 0;
@@ -379,10 +380,10 @@ public:
nReadPos = nReadPosNext;
}
- void write(const char* pch, size_t nSize)
+ void write(Span<const value_type> src)
{
// Write to the end of the buffer
- vch.insert(vch.end(), pch, pch + nSize);
+ vch.insert(vch.end(), src.begin(), src.end());
}
template<typename Stream>
@@ -390,7 +391,7 @@ public:
{
// Special case: stream << stream concatenates like stream += stream
if (!vch.empty())
- s.write((char*)vch.data(), vch.size() * sizeof(value_type));
+ s.write(MakeByteSpan(vch));
}
template<typename T>
@@ -421,7 +422,7 @@ public:
}
for (size_type i = 0, j = 0; i != size(); i++) {
- vch[i] ^= key[j++];
+ vch[i] ^= std::byte{key[j++]};
// This potentially acts on very many bytes of data, so it's
// important that we calculate `j`, i.e. the `key` index in this
@@ -594,12 +595,13 @@ public:
int GetType() const { return nType; }
int GetVersion() const { return nVersion; }
- void read(char* pch, size_t nSize)
+ void read(Span<std::byte> dst)
{
if (!file)
throw std::ios_base::failure("CAutoFile::read: file handle is nullptr");
- if (fread(pch, 1, nSize, file) != nSize)
+ if (fread(dst.data(), 1, dst.size(), file) != dst.size()) {
throw std::ios_base::failure(feof(file) ? "CAutoFile::read: end of file" : "CAutoFile::read: fread failed");
+ }
}
void ignore(size_t nSize)
@@ -615,12 +617,13 @@ public:
}
}
- void write(const char* pch, size_t nSize)
+ void write(Span<const std::byte> src)
{
if (!file)
throw std::ios_base::failure("CAutoFile::write: file handle is nullptr");
- if (fwrite(pch, 1, nSize, file) != nSize)
+ if (fwrite(src.data(), 1, src.size(), file) != src.size()) {
throw std::ios_base::failure("CAutoFile::write: write failed");
+ }
}
template<typename T>
@@ -661,7 +664,7 @@ private:
uint64_t nReadPos; //!< how many bytes have been read from this
uint64_t nReadLimit; //!< up to which position we're allowed to read
uint64_t nRewind; //!< how many bytes we guarantee to rewind
- std::vector<char> vchBuf; //!< the buffer
+ std::vector<std::byte> vchBuf; //!< the buffer
protected:
//! read data from the source to fill the buffer
@@ -682,8 +685,8 @@ protected:
}
public:
- CBufferedFile(FILE *fileIn, uint64_t nBufSize, uint64_t nRewindIn, int nTypeIn, int nVersionIn) :
- nType(nTypeIn), nVersion(nVersionIn), nSrcPos(0), nReadPos(0), nReadLimit(std::numeric_limits<uint64_t>::max()), nRewind(nRewindIn), vchBuf(nBufSize, 0)
+ CBufferedFile(FILE* fileIn, uint64_t nBufSize, uint64_t nRewindIn, int nTypeIn, int nVersionIn)
+ : nType(nTypeIn), nVersion(nVersionIn), nSrcPos(0), nReadPos(0), nReadLimit(std::numeric_limits<uint64_t>::max()), nRewind(nRewindIn), vchBuf(nBufSize, std::byte{0})
{
if (nRewindIn >= nBufSize)
throw std::ios_base::failure("Rewind limit must be less than buffer size");
@@ -716,22 +719,23 @@ public:
}
//! read a number of bytes
- void read(char *pch, size_t nSize) {
- if (nSize + nReadPos > nReadLimit)
+ void read(Span<std::byte> dst)
+ {
+ if (dst.size() + nReadPos > nReadLimit) {
throw std::ios_base::failure("Read attempted past buffer limit");
- while (nSize > 0) {
+ }
+ while (dst.size() > 0) {
if (nReadPos == nSrcPos)
Fill();
unsigned int pos = nReadPos % vchBuf.size();
- size_t nNow = nSize;
+ size_t nNow = dst.size();
if (nNow + pos > vchBuf.size())
nNow = vchBuf.size() - pos;
if (nNow + nReadPos > nSrcPos)
nNow = nSrcPos - nReadPos;
- memcpy(pch, &vchBuf[pos], nNow);
+ memcpy(dst.data(), &vchBuf[pos], nNow);
nReadPos += nNow;
- pch += nNow;
- nSize -= nNow;
+ dst = dst.subspan(nNow);
}
}
@@ -774,12 +778,14 @@ public:
}
//! search for a given byte in the stream, and remain positioned on it
- void FindByte(char ch) {
+ void FindByte(uint8_t ch)
+ {
while (true) {
if (nReadPos == nSrcPos)
Fill();
- if (vchBuf[nReadPos % vchBuf.size()] == ch)
+ if (vchBuf[nReadPos % vchBuf.size()] == std::byte{ch}) {
break;
+ }
nReadPos++;
}
}
diff --git a/src/support/allocators/zeroafterfree.h b/src/support/allocators/zeroafterfree.h
index bc9c95eb53..0befe0ffcd 100644
--- a/src/support/allocators/zeroafterfree.h
+++ b/src/support/allocators/zeroafterfree.h
@@ -41,6 +41,6 @@ struct zero_after_free_allocator : public std::allocator<T> {
};
/** Byte-vector that clears its contents before deletion. */
-using SerializeData = std::vector<uint8_t, zero_after_free_allocator<uint8_t>>;
+using SerializeData = std::vector<std::byte, zero_after_free_allocator<std::byte>>;
#endif // BITCOIN_SUPPORT_ALLOCATORS_ZEROAFTERFREE_H
diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp
index bd579db205..35c4108caa 100644
--- a/src/test/bloom_tests.cpp
+++ b/src/test/bloom_tests.cpp
@@ -43,8 +43,9 @@ BOOST_AUTO_TEST_CASE(bloom_create_insert_serialize)
stream << filter;
std::vector<uint8_t> expected = ParseHex("03614e9b050000000000000001");
+ auto result{MakeUCharSpan(stream)};
- BOOST_CHECK_EQUAL_COLLECTIONS(stream.begin(), stream.end(), expected.begin(), expected.end());
+ BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), expected.begin(), expected.end());
BOOST_CHECK_MESSAGE( filter.contains(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")), "Bloom filter doesn't contain just-inserted object!");
}
@@ -69,8 +70,9 @@ BOOST_AUTO_TEST_CASE(bloom_create_insert_serialize_with_tweak)
stream << filter;
std::vector<uint8_t> expected = ParseHex("03ce4299050000000100008001");
+ auto result{MakeUCharSpan(stream)};
- BOOST_CHECK_EQUAL_COLLECTIONS(stream.begin(), stream.end(), expected.begin(), expected.end());
+ BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), expected.begin(), expected.end());
}
BOOST_AUTO_TEST_CASE(bloom_create_insert_key)
@@ -89,8 +91,9 @@ BOOST_AUTO_TEST_CASE(bloom_create_insert_key)
stream << filter;
std::vector<unsigned char> expected = ParseHex("038fc16b080000000000000001");
+ auto result{MakeUCharSpan(stream)};
- BOOST_CHECK_EQUAL_COLLECTIONS(stream.begin(), stream.end(), expected.begin(), expected.end());
+ BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), expected.begin(), expected.end());
}
BOOST_AUTO_TEST_CASE(bloom_match)
@@ -341,8 +344,9 @@ BOOST_AUTO_TEST_CASE(merkle_block_3_and_serialize)
merkleStream << merkleBlock;
std::vector<uint8_t> expected = ParseHex("0100000079cda856b143d9db2c1caff01d1aecc8630d30625d10e8b4b8b0000000000000b50cc069d6a3e33e3ff84a5c41d9d3febe7c770fdcc96b2c3ff60abe184f196367291b4d4c86041b8fa45d630100000001b50cc069d6a3e33e3ff84a5c41d9d3febe7c770fdcc96b2c3ff60abe184f19630101");
+ auto result{MakeUCharSpan(merkleStream)};
- BOOST_CHECK_EQUAL_COLLECTIONS(expected.begin(), expected.end(), merkleStream.begin(), merkleStream.end());
+ BOOST_CHECK_EQUAL_COLLECTIONS(expected.begin(), expected.end(), result.begin(), result.end());
}
BOOST_AUTO_TEST_CASE(merkle_block_4)
diff --git a/src/test/fuzz/autofile.cpp b/src/test/fuzz/autofile.cpp
index 0cc2d12d29..3b410930ed 100644
--- a/src/test/fuzz/autofile.cpp
+++ b/src/test/fuzz/autofile.cpp
@@ -23,16 +23,16 @@ FUZZ_TARGET(autofile)
CallOneOf(
fuzzed_data_provider,
[&] {
- std::array<uint8_t, 4096> arr{};
+ std::array<std::byte, 4096> arr{};
try {
- auto_file.read((char*)arr.data(), fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096));
+ auto_file.read({arr.data(), fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096)});
} catch (const std::ios_base::failure&) {
}
},
[&] {
- const std::array<uint8_t, 4096> arr{};
+ const std::array<std::byte, 4096> arr{};
try {
- auto_file.write((const char*)arr.data(), fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096));
+ auto_file.write({arr.data(), fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096)});
} catch (const std::ios_base::failure&) {
}
},
diff --git a/src/test/fuzz/buffered_file.cpp b/src/test/fuzz/buffered_file.cpp
index c3c2e4050f..a8c3318629 100644
--- a/src/test/fuzz/buffered_file.cpp
+++ b/src/test/fuzz/buffered_file.cpp
@@ -33,9 +33,9 @@ FUZZ_TARGET(buffered_file)
CallOneOf(
fuzzed_data_provider,
[&] {
- std::array<uint8_t, 4096> arr{};
+ std::array<std::byte, 4096> arr{};
try {
- opt_buffered_file->read((char*)arr.data(), fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096));
+ opt_buffered_file->read({arr.data(), fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096)});
} catch (const std::ios_base::failure&) {
}
},
@@ -53,7 +53,7 @@ FUZZ_TARGET(buffered_file)
return;
}
try {
- opt_buffered_file->FindByte(fuzzed_data_provider.ConsumeIntegral<char>());
+ opt_buffered_file->FindByte(fuzzed_data_provider.ConsumeIntegral<uint8_t>());
} catch (const std::ios_base::failure&) {
}
},
diff --git a/src/test/fuzz/chain.cpp b/src/test/fuzz/chain.cpp
index 326904a811..8c0ed32d51 100644
--- a/src/test/fuzz/chain.cpp
+++ b/src/test/fuzz/chain.cpp
@@ -21,15 +21,18 @@ FUZZ_TARGET(chain)
const uint256 zero{};
disk_block_index->phashBlock = &zero;
- (void)disk_block_index->GetBlockHash();
- (void)disk_block_index->GetBlockPos();
- (void)disk_block_index->GetBlockTime();
- (void)disk_block_index->GetBlockTimeMax();
- (void)disk_block_index->GetMedianTimePast();
- (void)disk_block_index->GetUndoPos();
- (void)disk_block_index->HaveTxsDownloaded();
- (void)disk_block_index->IsValid();
- (void)disk_block_index->ToString();
+ {
+ LOCK(::cs_main);
+ (void)disk_block_index->GetBlockHash();
+ (void)disk_block_index->GetBlockPos();
+ (void)disk_block_index->GetBlockTime();
+ (void)disk_block_index->GetBlockTimeMax();
+ (void)disk_block_index->GetMedianTimePast();
+ (void)disk_block_index->GetUndoPos();
+ (void)disk_block_index->HaveTxsDownloaded();
+ (void)disk_block_index->IsValid();
+ (void)disk_block_index->ToString();
+ }
const CBlockHeader block_header = disk_block_index->GetBlockHeader();
(void)CDiskBlockIndex{*disk_block_index};
@@ -55,7 +58,7 @@ FUZZ_TARGET(chain)
if (block_status & ~BLOCK_VALID_MASK) {
continue;
}
- (void)disk_block_index->RaiseValidity(block_status);
+ WITH_LOCK(::cs_main, (void)disk_block_index->RaiseValidity(block_status));
}
CBlockIndex block_index{block_header};
diff --git a/src/test/fuzz/fuzz.cpp b/src/test/fuzz/fuzz.cpp
index e9debd8c45..60c48e7c22 100644
--- a/src/test/fuzz/fuzz.cpp
+++ b/src/test/fuzz/fuzz.cpp
@@ -80,7 +80,7 @@ void initialize()
}
if (const char* out_path = std::getenv("WRITE_ALL_FUZZ_TARGETS_AND_ABORT")) {
std::cout << "Writing all fuzz target names to '" << out_path << "'." << std::endl;
- std::ofstream out_stream(out_path, std::ios::binary);
+ fsbridge::ofstream out_stream{out_path, std::ios::binary};
for (const auto& t : FuzzTargets()) {
if (std::get<2>(t.second)) continue;
out_stream << t.first << std::endl;
diff --git a/src/test/fuzz/integer.cpp b/src/test/fuzz/integer.cpp
index 3087f11771..72574612a2 100644
--- a/src/test/fuzz/integer.cpp
+++ b/src/test/fuzz/integer.cpp
@@ -206,11 +206,6 @@ FUZZ_TARGET_INIT(integer, initialize_integer)
stream >> deserialized_i8;
assert(i8 == deserialized_i8 && stream.empty());
- char deserialized_ch;
- stream << ch;
- stream >> deserialized_ch;
- assert(ch == deserialized_ch && stream.empty());
-
bool deserialized_b;
stream << b;
stream >> deserialized_b;
diff --git a/src/test/fuzz/rpc.cpp b/src/test/fuzz/rpc.cpp
index b6ecf1c492..03a84b697d 100644
--- a/src/test/fuzz/rpc.cpp
+++ b/src/test/fuzz/rpc.cpp
@@ -120,6 +120,7 @@ const std::vector<std::string> RPC_COMMANDS_SAFE_FOR_FUZZING{
"getchaintips",
"getchaintxstats",
"getconnectioncount",
+ "getdeploymentinfo",
"getdescriptorinfo",
"getdifficulty",
"getindexinfo",
@@ -271,7 +272,7 @@ std::string ConsumeScalarRPCArgument(FuzzedDataProvider& fuzzed_data_provider)
}
CDataStream data_stream{SER_NETWORK, PROTOCOL_VERSION};
data_stream << *opt_psbt;
- r = EncodeBase64({data_stream.begin(), data_stream.end()});
+ r = EncodeBase64(data_stream);
},
[&] {
// base58 encoded key
diff --git a/src/test/fuzz/script.cpp b/src/test/fuzz/script.cpp
index eb170aab76..14a59912db 100644
--- a/src/test/fuzz/script.cpp
+++ b/src/test/fuzz/script.cpp
@@ -102,17 +102,6 @@ FUZZ_TARGET_INIT(script, initialize_script)
(void)script.IsPushOnly();
(void)script.GetSigOpCount(/* fAccurate= */ false);
- (void)FormatScript(script);
- (void)ScriptToAsmStr(script, false);
- (void)ScriptToAsmStr(script, true);
-
- UniValue o1(UniValue::VOBJ);
- ScriptPubKeyToUniv(script, o1, true);
- UniValue o2(UniValue::VOBJ);
- ScriptPubKeyToUniv(script, o2, false);
- UniValue o3(UniValue::VOBJ);
- ScriptToUniv(script, o3);
-
{
const std::vector<uint8_t> bytes = ConsumeRandomLengthByteVector(fuzzed_data_provider);
CompressedScript compressed_script;
@@ -178,4 +167,12 @@ FUZZ_TARGET_INIT(script, initialize_script)
Assert(dest == GetScriptForDestination(tx_destination_2));
}
}
+
+ (void)FormatScript(script);
+ (void)ScriptToAsmStr(script, /*fAttemptSighashDecode=*/fuzzed_data_provider.ConsumeBool());
+
+ UniValue o1(UniValue::VOBJ);
+ ScriptPubKeyToUniv(script, o1, /*include_hex=*/fuzzed_data_provider.ConsumeBool());
+ UniValue o3(UniValue::VOBJ);
+ ScriptToUniv(script, o3);
}
diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h
index fd7f40c01d..3bc62878bd 100644
--- a/src/test/fuzz/util.h
+++ b/src/test/fuzz/util.h
@@ -328,7 +328,6 @@ void WriteToStream(FuzzedDataProvider& fuzzed_data_provider, Stream& stream) noe
CallOneOf(
fuzzed_data_provider,
WRITE_TO_STREAM_CASE(bool, fuzzed_data_provider.ConsumeBool()),
- WRITE_TO_STREAM_CASE(char, fuzzed_data_provider.ConsumeIntegral<char>()),
WRITE_TO_STREAM_CASE(int8_t, fuzzed_data_provider.ConsumeIntegral<int8_t>()),
WRITE_TO_STREAM_CASE(uint8_t, fuzzed_data_provider.ConsumeIntegral<uint8_t>()),
WRITE_TO_STREAM_CASE(int16_t, fuzzed_data_provider.ConsumeIntegral<int16_t>()),
@@ -338,7 +337,7 @@ void WriteToStream(FuzzedDataProvider& fuzzed_data_provider, Stream& stream) noe
WRITE_TO_STREAM_CASE(int64_t, fuzzed_data_provider.ConsumeIntegral<int64_t>()),
WRITE_TO_STREAM_CASE(uint64_t, fuzzed_data_provider.ConsumeIntegral<uint64_t>()),
WRITE_TO_STREAM_CASE(std::string, fuzzed_data_provider.ConsumeRandomLengthString(32)),
- WRITE_TO_STREAM_CASE(std::vector<char>, ConsumeRandomLengthIntegralVector<char>(fuzzed_data_provider)));
+ WRITE_TO_STREAM_CASE(std::vector<uint8_t>, ConsumeRandomLengthIntegralVector<uint8_t>(fuzzed_data_provider)));
} catch (const std::ios_base::failure&) {
break;
}
@@ -358,7 +357,6 @@ void ReadFromStream(FuzzedDataProvider& fuzzed_data_provider, Stream& stream) no
CallOneOf(
fuzzed_data_provider,
READ_FROM_STREAM_CASE(bool),
- READ_FROM_STREAM_CASE(char),
READ_FROM_STREAM_CASE(int8_t),
READ_FROM_STREAM_CASE(uint8_t),
READ_FROM_STREAM_CASE(int16_t),
@@ -368,7 +366,7 @@ void ReadFromStream(FuzzedDataProvider& fuzzed_data_provider, Stream& stream) no
READ_FROM_STREAM_CASE(int64_t),
READ_FROM_STREAM_CASE(uint64_t),
READ_FROM_STREAM_CASE(std::string),
- READ_FROM_STREAM_CASE(std::vector<char>));
+ READ_FROM_STREAM_CASE(std::vector<uint8_t>));
} catch (const std::ios_base::failure&) {
break;
}
diff --git a/src/test/fuzz/versionbits.cpp b/src/test/fuzz/versionbits.cpp
index cf95c0b9bf..95eb71099d 100644
--- a/src/test/fuzz/versionbits.cpp
+++ b/src/test/fuzz/versionbits.cpp
@@ -51,7 +51,7 @@ public:
ThresholdState GetStateFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateFor(pindexPrev, dummy_params, m_cache); }
int GetStateSinceHeightFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateSinceHeightFor(pindexPrev, dummy_params, m_cache); }
- BIP9Stats GetStateStatisticsFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateStatisticsFor(pindexPrev, dummy_params); }
+ BIP9Stats GetStateStatisticsFor(const CBlockIndex* pindex, std::vector<bool>* signals=nullptr) const { return AbstractThresholdConditionChecker::GetStateStatisticsFor(pindex, dummy_params, signals); }
bool Condition(int32_t version) const
{
@@ -220,7 +220,14 @@ FUZZ_TARGET_INIT(versionbits, initialize)
CBlockIndex* prev = blocks.tip();
const int exp_since = checker.GetStateSinceHeightFor(prev);
const ThresholdState exp_state = checker.GetStateFor(prev);
- BIP9Stats last_stats = checker.GetStateStatisticsFor(prev);
+
+ // get statistics from end of previous period, then reset
+ BIP9Stats last_stats;
+ last_stats.period = period;
+ last_stats.threshold = threshold;
+ last_stats.count = last_stats.elapsed = 0;
+ last_stats.possible = (period >= threshold);
+ std::vector<bool> last_signals{};
int prev_next_height = (prev == nullptr ? 0 : prev->nHeight + 1);
assert(exp_since <= prev_next_height);
@@ -241,17 +248,25 @@ FUZZ_TARGET_INIT(versionbits, initialize)
assert(state == exp_state);
assert(since == exp_since);
- // GetStateStatistics may crash when state is not STARTED
- if (state != ThresholdState::STARTED) continue;
-
// check that after mining this block stats change as expected
- const BIP9Stats stats = checker.GetStateStatisticsFor(current_block);
+ std::vector<bool> signals;
+ const BIP9Stats stats = checker.GetStateStatisticsFor(current_block, &signals);
+ const BIP9Stats stats_no_signals = checker.GetStateStatisticsFor(current_block);
+ assert(stats.period == stats_no_signals.period && stats.threshold == stats_no_signals.threshold
+ && stats.elapsed == stats_no_signals.elapsed && stats.count == stats_no_signals.count
+ && stats.possible == stats_no_signals.possible);
+
assert(stats.period == period);
assert(stats.threshold == threshold);
assert(stats.elapsed == b);
assert(stats.count == last_stats.count + (signal ? 1 : 0));
assert(stats.possible == (stats.count + period >= stats.elapsed + threshold));
last_stats = stats;
+
+ assert(signals.size() == last_signals.size() + 1);
+ assert(signals.back() == signal);
+ last_signals.push_back(signal);
+ assert(signals == last_signals);
}
if (exp_state == ThresholdState::STARTED) {
@@ -265,14 +280,12 @@ FUZZ_TARGET_INIT(versionbits, initialize)
CBlockIndex* current_block = blocks.mine_block(signal);
assert(checker.Condition(current_block) == signal);
- // GetStateStatistics is safe on a period boundary
- // and has progressed to a new period
const BIP9Stats stats = checker.GetStateStatisticsFor(current_block);
assert(stats.period == period);
assert(stats.threshold == threshold);
- assert(stats.elapsed == 0);
- assert(stats.count == 0);
- assert(stats.possible == true);
+ assert(stats.elapsed == period);
+ assert(stats.count == blocks_sig);
+ assert(stats.possible == (stats.count + period >= stats.elapsed + threshold));
// More interesting is whether the state changed.
const ThresholdState state = checker.GetStateFor(current_block);
diff --git a/src/test/interfaces_tests.cpp b/src/test/interfaces_tests.cpp
index f4bf6ff8c9..49b7d2003b 100644
--- a/src/test/interfaces_tests.cpp
+++ b/src/test/interfaces_tests.cpp
@@ -123,6 +123,7 @@ BOOST_AUTO_TEST_CASE(findCommonAncestor)
BOOST_AUTO_TEST_CASE(hasBlocks)
{
+ LOCK(::cs_main);
auto& chain = m_node.chain;
const CChain& active = Assert(m_node.chainman)->ActiveChain();
diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp
index eacd7ae894..4906bd2386 100644
--- a/src/test/script_tests.cpp
+++ b/src/test/script_tests.cpp
@@ -155,10 +155,10 @@ void DoTest(const CScript& scriptPubKey, const CScript& scriptSig, const CScript
if (libconsensus_flags == flags) {
int expectedSuccessCode = expect ? 1 : 0;
if (flags & bitcoinconsensus_SCRIPT_FLAGS_VERIFY_WITNESS) {
- BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script_with_amount(scriptPubKey.data(), scriptPubKey.size(), txCredit.vout[0].nValue, stream.data(), stream.size(), 0, libconsensus_flags, nullptr) == expectedSuccessCode, message);
+ BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script_with_amount(scriptPubKey.data(), scriptPubKey.size(), txCredit.vout[0].nValue, UCharCast(stream.data()), stream.size(), 0, libconsensus_flags, nullptr) == expectedSuccessCode, message);
} else {
- BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script_with_amount(scriptPubKey.data(), scriptPubKey.size(), 0, stream.data(), stream.size(), 0, libconsensus_flags, nullptr) == expectedSuccessCode, message);
- BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), stream.data(), stream.size(), 0, libconsensus_flags, nullptr) == expectedSuccessCode, message);
+ BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script_with_amount(scriptPubKey.data(), scriptPubKey.size(), 0, UCharCast(stream.data()), stream.size(), 0, libconsensus_flags, nullptr) == expectedSuccessCode, message);
+ BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), UCharCast(stream.data()), stream.size(), 0, libconsensus_flags, nullptr) == expectedSuccessCode, message);
}
}
#endif
@@ -923,7 +923,7 @@ BOOST_AUTO_TEST_CASE(script_build)
}
#ifdef UPDATE_JSON_TESTS
- FILE* file = fopen("script_tests.json.gen", "w");
+ FILE* file = fsbridge::fopen("script_tests.json.gen", "w");
fputs(strGen.c_str(), file);
fclose(file);
#endif
@@ -1520,7 +1520,7 @@ BOOST_AUTO_TEST_CASE(bitcoinconsensus_verify_script_returns_true)
stream << spendTx;
bitcoinconsensus_error err;
- int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), stream.data(), stream.size(), nIn, libconsensus_flags, &err);
+ int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), UCharCast(stream.data()), stream.size(), nIn, libconsensus_flags, &err);
BOOST_CHECK_EQUAL(result, 1);
BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_OK);
}
@@ -1543,7 +1543,7 @@ BOOST_AUTO_TEST_CASE(bitcoinconsensus_verify_script_tx_index_err)
stream << spendTx;
bitcoinconsensus_error err;
- int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), stream.data(), stream.size(), nIn, libconsensus_flags, &err);
+ int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), UCharCast(stream.data()), stream.size(), nIn, libconsensus_flags, &err);
BOOST_CHECK_EQUAL(result, 0);
BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_TX_INDEX);
}
@@ -1566,7 +1566,7 @@ BOOST_AUTO_TEST_CASE(bitcoinconsensus_verify_script_tx_size)
stream << spendTx;
bitcoinconsensus_error err;
- int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), stream.data(), stream.size() * 2, nIn, libconsensus_flags, &err);
+ int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), UCharCast(stream.data()), stream.size() * 2, nIn, libconsensus_flags, &err);
BOOST_CHECK_EQUAL(result, 0);
BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_TX_SIZE_MISMATCH);
}
@@ -1589,7 +1589,7 @@ BOOST_AUTO_TEST_CASE(bitcoinconsensus_verify_script_tx_serialization)
stream << 0xffffffff;
bitcoinconsensus_error err;
- int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), stream.data(), stream.size(), nIn, libconsensus_flags, &err);
+ int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), UCharCast(stream.data()), stream.size(), nIn, libconsensus_flags, &err);
BOOST_CHECK_EQUAL(result, 0);
BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_TX_DESERIALIZE);
}
@@ -1612,7 +1612,7 @@ BOOST_AUTO_TEST_CASE(bitcoinconsensus_verify_script_amount_required_err)
stream << spendTx;
bitcoinconsensus_error err;
- int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), stream.data(), stream.size(), nIn, libconsensus_flags, &err);
+ int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), UCharCast(stream.data()), stream.size(), nIn, libconsensus_flags, &err);
BOOST_CHECK_EQUAL(result, 0);
BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_AMOUNT_REQUIRED);
}
@@ -1635,7 +1635,7 @@ BOOST_AUTO_TEST_CASE(bitcoinconsensus_verify_script_invalid_flags)
stream << spendTx;
bitcoinconsensus_error err;
- int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), stream.data(), stream.size(), nIn, libconsensus_flags, &err);
+ int result = bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), UCharCast(stream.data()), stream.size(), nIn, libconsensus_flags, &err);
BOOST_CHECK_EQUAL(result, 0);
BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_INVALID_FLAGS);
}
diff --git a/src/test/serialize_tests.cpp b/src/test/serialize_tests.cpp
index e91c203c26..8b8133b689 100644
--- a/src/test/serialize_tests.cpp
+++ b/src/test/serialize_tests.cpp
@@ -61,7 +61,7 @@ public:
BOOST_AUTO_TEST_CASE(sizes)
{
- BOOST_CHECK_EQUAL(sizeof(char), GetSerializeSize(char(0), 0));
+ BOOST_CHECK_EQUAL(sizeof(unsigned char), GetSerializeSize((unsigned char)0, 0));
BOOST_CHECK_EQUAL(sizeof(int8_t), GetSerializeSize(int8_t(0), 0));
BOOST_CHECK_EQUAL(sizeof(uint8_t), GetSerializeSize(uint8_t(0), 0));
BOOST_CHECK_EQUAL(sizeof(int16_t), GetSerializeSize(int16_t(0), 0));
@@ -74,7 +74,7 @@ BOOST_AUTO_TEST_CASE(sizes)
BOOST_CHECK_EQUAL(sizeof(uint8_t), GetSerializeSize(bool(0), 0));
// Sanity-check GetSerializeSize and c++ type matching
- BOOST_CHECK_EQUAL(GetSerializeSize(char(0), 0), 1U);
+ BOOST_CHECK_EQUAL(GetSerializeSize((unsigned char)0, 0), 1U);
BOOST_CHECK_EQUAL(GetSerializeSize(int8_t(0), 0), 1U);
BOOST_CHECK_EQUAL(GetSerializeSize(uint8_t(0), 0), 1U);
BOOST_CHECK_EQUAL(GetSerializeSize(int16_t(0), 0), 2U);
@@ -186,76 +186,78 @@ BOOST_AUTO_TEST_CASE(noncanonical)
std::vector<char>::size_type n;
// zero encoded with three bytes:
- ss.write("\xfd\x00\x00", 3);
+ ss.write(MakeByteSpan("\xfd\x00\x00").first(3));
BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException);
// 0xfc encoded with three bytes:
- ss.write("\xfd\xfc\x00", 3);
+ ss.write(MakeByteSpan("\xfd\xfc\x00").first(3));
BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException);
// 0xfd encoded with three bytes is OK:
- ss.write("\xfd\xfd\x00", 3);
+ ss.write(MakeByteSpan("\xfd\xfd\x00").first(3));
n = ReadCompactSize(ss);
BOOST_CHECK(n == 0xfd);
// zero encoded with five bytes:
- ss.write("\xfe\x00\x00\x00\x00", 5);
+ ss.write(MakeByteSpan("\xfe\x00\x00\x00\x00").first(5));
BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException);
// 0xffff encoded with five bytes:
- ss.write("\xfe\xff\xff\x00\x00", 5);
+ ss.write(MakeByteSpan("\xfe\xff\xff\x00\x00").first(5));
BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException);
// zero encoded with nine bytes:
- ss.write("\xff\x00\x00\x00\x00\x00\x00\x00\x00", 9);
+ ss.write(MakeByteSpan("\xff\x00\x00\x00\x00\x00\x00\x00\x00").first(9));
BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException);
// 0x01ffffff encoded with nine bytes:
- ss.write("\xff\xff\xff\xff\x01\x00\x00\x00\x00", 9);
+ ss.write(MakeByteSpan("\xff\xff\xff\xff\x01\x00\x00\x00\x00").first(9));
BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException);
}
BOOST_AUTO_TEST_CASE(insert_delete)
{
+ constexpr auto B2I{[](std::byte b) { return std::to_integer<uint8_t>(b); }};
+
// Test inserting/deleting bytes.
CDataStream ss(SER_DISK, 0);
BOOST_CHECK_EQUAL(ss.size(), 0U);
- ss.write("\x00\x01\x02\xff", 4);
+ ss.write(MakeByteSpan("\x00\x01\x02\xff").first(4));
BOOST_CHECK_EQUAL(ss.size(), 4U);
- char c = (char)11;
+ uint8_t c{11};
// Inserting at beginning/end/middle:
- ss.insert(ss.begin(), c);
+ ss.insert(ss.begin(), std::byte{c});
BOOST_CHECK_EQUAL(ss.size(), 5U);
- BOOST_CHECK_EQUAL(ss[0], c);
- BOOST_CHECK_EQUAL(ss[1], 0);
+ BOOST_CHECK_EQUAL(B2I(ss[0]), c);
+ BOOST_CHECK_EQUAL(B2I(ss[1]), 0);
- ss.insert(ss.end(), c);
+ ss.insert(ss.end(), std::byte{c});
BOOST_CHECK_EQUAL(ss.size(), 6U);
- BOOST_CHECK_EQUAL(ss[4], 0xff);
- BOOST_CHECK_EQUAL(ss[5], c);
+ BOOST_CHECK_EQUAL(B2I(ss[4]), 0xff);
+ BOOST_CHECK_EQUAL(B2I(ss[5]), c);
- ss.insert(ss.begin()+2, c);
+ ss.insert(ss.begin() + 2, std::byte{c});
BOOST_CHECK_EQUAL(ss.size(), 7U);
- BOOST_CHECK_EQUAL(ss[2], c);
+ BOOST_CHECK_EQUAL(B2I(ss[2]), c);
// Delete at beginning/end/middle
ss.erase(ss.begin());
BOOST_CHECK_EQUAL(ss.size(), 6U);
- BOOST_CHECK_EQUAL(ss[0], 0);
+ BOOST_CHECK_EQUAL(B2I(ss[0]), 0);
ss.erase(ss.begin()+ss.size()-1);
BOOST_CHECK_EQUAL(ss.size(), 5U);
- BOOST_CHECK_EQUAL(ss[4], 0xff);
+ BOOST_CHECK_EQUAL(B2I(ss[4]), 0xff);
ss.erase(ss.begin()+1);
BOOST_CHECK_EQUAL(ss.size(), 4U);
- BOOST_CHECK_EQUAL(ss[0], 0);
- BOOST_CHECK_EQUAL(ss[1], 1);
- BOOST_CHECK_EQUAL(ss[2], 2);
- BOOST_CHECK_EQUAL(ss[3], 0xff);
+ BOOST_CHECK_EQUAL(B2I(ss[0]), 0);
+ BOOST_CHECK_EQUAL(B2I(ss[1]), 1);
+ BOOST_CHECK_EQUAL(B2I(ss[2]), 2);
+ BOOST_CHECK_EQUAL(B2I(ss[3]), 0xff);
}
BOOST_AUTO_TEST_CASE(class_methods)
diff --git a/src/test/streams_tests.cpp b/src/test/streams_tests.cpp
index 3571927397..a8f6cdd4a0 100644
--- a/src/test/streams_tests.cpp
+++ b/src/test/streams_tests.cpp
@@ -160,7 +160,7 @@ BOOST_AUTO_TEST_CASE(bitstream_reader_writer)
BOOST_AUTO_TEST_CASE(streams_serializedata_xor)
{
- std::vector<uint8_t> in;
+ std::vector<std::byte> in;
std::vector<char> expected_xor;
std::vector<unsigned char> key;
CDataStream ds(in, 0, 0);
@@ -174,8 +174,8 @@ BOOST_AUTO_TEST_CASE(streams_serializedata_xor)
std::string(expected_xor.begin(), expected_xor.end()),
ds.str());
- in.push_back('\x0f');
- in.push_back('\xf0');
+ in.push_back(std::byte{0x0f});
+ in.push_back(std::byte{0xf0});
expected_xor.push_back('\xf0');
expected_xor.push_back('\x0f');
@@ -195,8 +195,8 @@ BOOST_AUTO_TEST_CASE(streams_serializedata_xor)
in.clear();
expected_xor.clear();
- in.push_back('\xf0');
- in.push_back('\x0f');
+ in.push_back(std::byte{0xf0});
+ in.push_back(std::byte{0x0f});
expected_xor.push_back('\x0f');
expected_xor.push_back('\x00');
diff --git a/src/test/util/blockfilter.cpp b/src/test/util/blockfilter.cpp
index 538981ce36..3ae22921b9 100644
--- a/src/test/util/blockfilter.cpp
+++ b/src/test/util/blockfilter.cpp
@@ -13,6 +13,8 @@ using node::UndoReadFromDisk;
bool ComputeFilter(BlockFilterType filter_type, const CBlockIndex* block_index, BlockFilter& filter)
{
+ LOCK(::cs_main);
+
CBlock block;
if (!ReadBlockFromDisk(block, block_index->GetBlockPos(), Params().GetConsensus())) {
return false;
diff --git a/src/test/validation_chainstatemanager_tests.cpp b/src/test/validation_chainstatemanager_tests.cpp
index f5742b65a1..26392e690d 100644
--- a/src/test/validation_chainstatemanager_tests.cpp
+++ b/src/test/validation_chainstatemanager_tests.cpp
@@ -235,7 +235,7 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_activate_snapshot, TestChain100Setup)
*chainman.SnapshotBlockhash());
// Ensure that the genesis block was not marked assumed-valid.
- BOOST_CHECK(!chainman.ActiveChain().Genesis()->IsAssumedValid());
+ BOOST_CHECK(WITH_LOCK(::cs_main, return !chainman.ActiveChain().Genesis()->IsAssumedValid()));
const AssumeutxoData& au_data = *ExpectedAssumeutxo(snapshot_height, ::Params());
const CBlockIndex* tip = chainman.ActiveTip();
@@ -356,6 +356,7 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_loadblockindex, TestChain100Setup)
// Mark some region of the chain assumed-valid.
for (int i = 0; i <= cs1.m_chain.Height(); ++i) {
+ LOCK(::cs_main);
auto index = cs1.m_chain[i];
if (i < last_assumed_valid_idx && i >= assumed_valid_start_idx) {
diff --git a/src/txdb.cpp b/src/txdb.cpp
index 85eea888cc..5e4379a60a 100644
--- a/src/txdb.cpp
+++ b/src/txdb.cpp
@@ -296,8 +296,8 @@ bool CBlockTreeDB::ReadFlag(const std::string &name, bool &fValue) {
bool CBlockTreeDB::LoadBlockIndexGuts(const Consensus::Params& consensusParams, std::function<CBlockIndex*(const uint256&)> insertBlockIndex)
{
+ AssertLockHeld(::cs_main);
std::unique_ptr<CDBIterator> pcursor(NewIterator());
-
pcursor->Seek(std::make_pair(DB_BLOCK_INDEX, uint256()));
// Load m_block_index
@@ -322,8 +322,9 @@ bool CBlockTreeDB::LoadBlockIndexGuts(const Consensus::Params& consensusParams,
pindexNew->nStatus = diskindex.nStatus;
pindexNew->nTx = diskindex.nTx;
- if (!CheckProofOfWork(pindexNew->GetBlockHash(), pindexNew->nBits, consensusParams))
+ if (!CheckProofOfWork(pindexNew->GetBlockHash(), pindexNew->nBits, consensusParams)) {
return error("%s: CheckProofOfWork failed: %s", __func__, pindexNew->ToString());
+ }
pcursor->Next();
} else {
diff --git a/src/txdb.h b/src/txdb.h
index d1f47719c4..e70f3cd1f2 100644
--- a/src/txdb.h
+++ b/src/txdb.h
@@ -86,7 +86,8 @@ public:
void ReadReindexing(bool &fReindexing);
bool WriteFlag(const std::string &name, bool fValue);
bool ReadFlag(const std::string &name, bool &fValue);
- bool LoadBlockIndexGuts(const Consensus::Params& consensusParams, std::function<CBlockIndex*(const uint256&)> insertBlockIndex);
+ bool LoadBlockIndexGuts(const Consensus::Params& consensusParams, std::function<CBlockIndex*(const uint256&)> insertBlockIndex)
+ EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
};
std::optional<bilingual_str> CheckLegacyTxindex(CBlockTreeDB& block_tree_db);
diff --git a/src/uint256.h b/src/uint256.h
index 72681d09c9..5c3a2f5409 100644
--- a/src/uint256.h
+++ b/src/uint256.h
@@ -6,6 +6,8 @@
#ifndef BITCOIN_UINT256_H
#define BITCOIN_UINT256_H
+#include <span.h>
+
#include <assert.h>
#include <cstring>
#include <stdint.h>
@@ -96,13 +98,13 @@ public:
template<typename Stream>
void Serialize(Stream& s) const
{
- s.write((char*)m_data, sizeof(m_data));
+ s.write(MakeByteSpan(m_data));
}
template<typename Stream>
void Unserialize(Stream& s)
{
- s.read((char*)m_data, sizeof(m_data));
+ s.read(MakeWritableByteSpan(m_data));
}
};
diff --git a/src/util/system.cpp b/src/util/system.cpp
index e34cdc7fb9..19de08d1ea 100644
--- a/src/util/system.cpp
+++ b/src/util/system.cpp
@@ -146,7 +146,7 @@ bool CheckDiskSpace(const fs::path& dir, uint64_t additional_bytes)
}
std::streampos GetFileSize(const char* path, std::streamsize max) {
- std::ifstream file(path, std::ios::binary);
+ fsbridge::ifstream file{path, std::ios::binary};
file.ignore(max);
return file.gcount();
}
diff --git a/src/validation.cpp b/src/validation.cpp
index 1b1d01a4c2..5fca1551c8 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -4104,7 +4104,7 @@ void CChainState::LoadExternalBlockFile(FILE* fileIn, FlatFilePos* dbp)
try {
// locate a header
unsigned char buf[CMessageHeader::MESSAGE_START_SIZE];
- blkdat.FindByte(char(m_params.MessageStart()[0]));
+ blkdat.FindByte(m_params.MessageStart()[0]);
nRewind = blkdat.GetPos() + 1;
blkdat >> buf;
if (memcmp(buf, m_params.MessageStart(), CMessageHeader::MESSAGE_START_SIZE)) {
diff --git a/src/versionbits.cpp b/src/versionbits.cpp
index a9264e6116..36815fba17 100644
--- a/src/versionbits.cpp
+++ b/src/versionbits.cpp
@@ -98,29 +98,38 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex*
return state;
}
-BIP9Stats AbstractThresholdConditionChecker::GetStateStatisticsFor(const CBlockIndex* pindex, const Consensus::Params& params) const
+BIP9Stats AbstractThresholdConditionChecker::GetStateStatisticsFor(const CBlockIndex* pindex, const Consensus::Params& params, std::vector<bool>* signalling_blocks) const
{
BIP9Stats stats = {};
stats.period = Period(params);
stats.threshold = Threshold(params);
- if (pindex == nullptr)
- return stats;
+ if (pindex == nullptr) return stats;
// Find beginning of period
- const CBlockIndex* pindexEndOfPrevPeriod = pindex->GetAncestor(pindex->nHeight - ((pindex->nHeight + 1) % stats.period));
- stats.elapsed = pindex->nHeight - pindexEndOfPrevPeriod->nHeight;
+ int blocks_in_period = 1 + (pindex->nHeight % stats.period);
+
+ // Reset signalling_blocks
+ if (signalling_blocks) {
+ signalling_blocks->assign(blocks_in_period, false);
+ }
// Count from current block to beginning of period
+ int elapsed = 0;
int count = 0;
const CBlockIndex* currentIndex = pindex;
- while (pindexEndOfPrevPeriod->nHeight != currentIndex->nHeight){
- if (Condition(currentIndex, params))
- count++;
+ do {
+ ++elapsed;
+ --blocks_in_period;
+ if (Condition(currentIndex, params)) {
+ ++count;
+ if (signalling_blocks) signalling_blocks->at(blocks_in_period) = true;
+ }
currentIndex = currentIndex->pprev;
- }
+ } while(blocks_in_period > 0);
+ stats.elapsed = elapsed;
stats.count = count;
stats.possible = (stats.period - stats.threshold ) >= (stats.elapsed - count);
@@ -196,9 +205,9 @@ ThresholdState VersionBitsCache::State(const CBlockIndex* pindexPrev, const Cons
return VersionBitsConditionChecker(pos).GetStateFor(pindexPrev, params, m_caches[pos]);
}
-BIP9Stats VersionBitsCache::Statistics(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos)
+BIP9Stats VersionBitsCache::Statistics(const CBlockIndex* pindex, const Consensus::Params& params, Consensus::DeploymentPos pos, std::vector<bool>* signalling_blocks)
{
- return VersionBitsConditionChecker(pos).GetStateStatisticsFor(pindexPrev, params);
+ return VersionBitsConditionChecker(pos).GetStateStatisticsFor(pindex, params, signalling_blocks);
}
int VersionBitsCache::StateSinceHeight(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos)
diff --git a/src/versionbits.h b/src/versionbits.h
index 25ddd6fa5d..1b3fa11e61 100644
--- a/src/versionbits.h
+++ b/src/versionbits.h
@@ -64,8 +64,10 @@ protected:
virtual int Threshold(const Consensus::Params& params) const =0;
public:
- /** Returns the numerical statistics of an in-progress BIP9 softfork in the current period */
- BIP9Stats GetStateStatisticsFor(const CBlockIndex* pindex, const Consensus::Params& params) const;
+ /** Returns the numerical statistics of an in-progress BIP9 softfork in the period including pindex
+ * If provided, signalling_blocks is set to true/false based on whether each block in the period signalled
+ */
+ BIP9Stats GetStateStatisticsFor(const CBlockIndex* pindex, const Consensus::Params& params, std::vector<bool>* signalling_blocks = nullptr) const;
/** Returns the state for pindex A based on parent pindexPrev B. Applies any state transition if conditions are present.
* Caches state from first block of period. */
ThresholdState GetStateFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const;
@@ -82,8 +84,10 @@ private:
ThresholdConditionCache m_caches[Consensus::MAX_VERSION_BITS_DEPLOYMENTS] GUARDED_BY(m_mutex);
public:
- /** Get the numerical statistics for a given deployment for the signalling period that includes the block after pindexPrev. */
- static BIP9Stats Statistics(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos);
+ /** Get the numerical statistics for a given deployment for the signalling period that includes pindex.
+ * If provided, signalling_blocks is set to true/false based on whether each block in the period signalled
+ */
+ static BIP9Stats Statistics(const CBlockIndex* pindex, const Consensus::Params& params, Consensus::DeploymentPos pos, std::vector<bool>* signalling_blocks = nullptr);
static uint32_t Mask(const Consensus::Params& params, Consensus::DeploymentPos pos);
diff --git a/src/wallet/bdb.cpp b/src/wallet/bdb.cpp
index e0be914a2b..cea120a81e 100644
--- a/src/wallet/bdb.cpp
+++ b/src/wallet/bdb.cpp
@@ -681,10 +681,10 @@ bool BerkeleyBatch::ReadAtCursor(CDataStream& ssKey, CDataStream& ssValue, bool&
// Convert to streams
ssKey.SetType(SER_DISK);
ssKey.clear();
- ssKey.write((char*)datKey.get_data(), datKey.get_size());
+ ssKey.write({BytePtr(datKey.get_data()), datKey.get_size()});
ssValue.SetType(SER_DISK);
ssValue.clear();
- ssValue.write((char*)datValue.get_data(), datValue.get_size());
+ ssValue.write({BytePtr(datValue.get_data()), datValue.get_size()});
return true;
}
@@ -756,7 +756,7 @@ bool BerkeleyBatch::ReadKey(CDataStream&& key, CDataStream& value)
SafeDbt datValue;
int ret = pdb->get(activeTxn, datKey, datValue, 0);
if (ret == 0 && datValue.get_data() != nullptr) {
- value.write((char*)datValue.get_data(), datValue.get_size());
+ value.write({BytePtr(datValue.get_data()), datValue.get_size()});
return true;
}
return false;
diff --git a/src/wallet/coincontrol.h b/src/wallet/coincontrol.h
index 5ef2295c88..65a5c83366 100644
--- a/src/wallet/coincontrol.h
+++ b/src/wallet/coincontrol.h
@@ -115,9 +115,28 @@ public:
vOutpoints.assign(setSelected.begin(), setSelected.end());
}
+ void SetInputWeight(const COutPoint& outpoint, int64_t weight)
+ {
+ m_input_weights[outpoint] = weight;
+ }
+
+ bool HasInputWeight(const COutPoint& outpoint) const
+ {
+ return m_input_weights.count(outpoint) > 0;
+ }
+
+ int64_t GetInputWeight(const COutPoint& outpoint) const
+ {
+ auto it = m_input_weights.find(outpoint);
+ assert(it != m_input_weights.end());
+ return it->second;
+ }
+
private:
std::set<COutPoint> setSelected;
std::map<COutPoint, CTxOut> m_external_txouts;
+ //! Map of COutPoints to the maximum weight for that input
+ std::map<COutPoint, int64_t> m_input_weights;
};
} // namespace wallet
diff --git a/src/wallet/dump.cpp b/src/wallet/dump.cpp
index 7dfb1d8839..3e34a2f776 100644
--- a/src/wallet/dump.cpp
+++ b/src/wallet/dump.cpp
@@ -47,12 +47,12 @@ bool DumpWallet(CWallet& wallet, bilingual_str& error)
// Write out a magic string with version
std::string line = strprintf("%s,%u\n", DUMP_MAGIC, DUMP_VERSION);
dump_file.write(line.data(), line.size());
- hasher.write(line.data(), line.size());
+ hasher.write(MakeByteSpan(line));
// Write out the file format
line = strprintf("%s,%s\n", "format", db.Format());
dump_file.write(line.data(), line.size());
- hasher.write(line.data(), line.size());
+ hasher.write(MakeByteSpan(line));
if (ret) {
@@ -73,7 +73,7 @@ bool DumpWallet(CWallet& wallet, bilingual_str& error)
std::string value_str = HexStr(ss_value);
line = strprintf("%s,%s\n", key_str, value_str);
dump_file.write(line.data(), line.size());
- hasher.write(line.data(), line.size());
+ hasher.write(MakeByteSpan(line));
}
}
@@ -150,7 +150,7 @@ bool CreateFromDump(const std::string& name, const fs::path& wallet_path, biling
return false;
}
std::string magic_hasher_line = strprintf("%s,%s\n", magic_key, version_value);
- hasher.write(magic_hasher_line.data(), magic_hasher_line.size());
+ hasher.write(MakeByteSpan(magic_hasher_line));
// Get the stored file format
std::string format_key;
@@ -181,7 +181,7 @@ bool CreateFromDump(const std::string& name, const fs::path& wallet_path, biling
warnings.push_back(strprintf(_("Warning: Dumpfile wallet format \"%s\" does not match command line specified format \"%s\"."), format_value, file_format));
}
std::string format_hasher_line = strprintf("%s,%s\n", format_key, format_value);
- hasher.write(format_hasher_line.data(), format_hasher_line.size());
+ hasher.write(MakeByteSpan(format_hasher_line));
DatabaseOptions options;
DatabaseStatus status;
@@ -225,7 +225,7 @@ bool CreateFromDump(const std::string& name, const fs::path& wallet_path, biling
}
std::string line = strprintf("%s,%s\n", key, value);
- hasher.write(line.data(), line.size());
+ hasher.write(MakeByteSpan(line));
if (key.empty() || value.empty()) {
continue;
diff --git a/src/wallet/interfaces.cpp b/src/wallet/interfaces.cpp
index b1466869b9..9083c304b2 100644
--- a/src/wallet/interfaces.cpp
+++ b/src/wallet/interfaces.cpp
@@ -90,7 +90,6 @@ WalletTxStatus MakeWalletTxStatus(const CWallet& wallet, const CWalletTx& wtx)
result.depth_in_main_chain = wallet.GetTxDepthInMainChain(wtx);
result.time_received = wtx.nTimeReceived;
result.lock_time = wtx.tx->nLockTime;
- result.is_final = wallet.chain().checkFinalTx(*wtx.tx);
result.is_trusted = CachedTxIsTrusted(wallet, wtx);
result.is_abandoned = wtx.isAbandoned();
result.is_coinbase = wtx.IsCoinBase();
diff --git a/src/wallet/receive.cpp b/src/wallet/receive.cpp
index e598d6f979..1a6f06213c 100644
--- a/src/wallet/receive.cpp
+++ b/src/wallet/receive.cpp
@@ -279,8 +279,6 @@ bool CachedTxIsFromMe(const CWallet& wallet, const CWalletTx& wtx, const isminef
bool CachedTxIsTrusted(const CWallet& wallet, const CWalletTx& wtx, std::set<uint256>& trusted_parents)
{
AssertLockHeld(wallet.cs_wallet);
- // Quick answer in most cases
- if (!wallet.chain().checkFinalTx(*wtx.tx)) return false;
int nDepth = wallet.GetTxDepthInMainChain(wtx);
if (nDepth >= 1) return true;
if (nDepth < 0) return false;
diff --git a/src/wallet/rpc/coins.cpp b/src/wallet/rpc/coins.cpp
index f10de11662..035541babd 100644
--- a/src/wallet/rpc/coins.cpp
+++ b/src/wallet/rpc/coins.cpp
@@ -60,8 +60,8 @@ static CAmount GetReceived(const CWallet& wallet, const UniValue& params, bool b
if (depth < min_depth
// Coinbase with less than 1 confirmation is no longer in the main chain
|| (wtx.IsCoinBase() && (depth < 1 || !include_coinbase))
- || (wallet.IsTxImmatureCoinBase(wtx) && !include_immature_coinbase)
- || !wallet.chain().checkFinalTx(*wtx.tx)) {
+ || (wallet.IsTxImmatureCoinBase(wtx) && !include_immature_coinbase))
+ {
continue;
}
diff --git a/src/wallet/rpc/spend.cpp b/src/wallet/rpc/spend.cpp
index cae3542a5e..433b5a1815 100644
--- a/src/wallet/rpc/spend.cpp
+++ b/src/wallet/rpc/spend.cpp
@@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <consensus/validation.h>
#include <core_io.h>
#include <key_io.h>
#include <policy/policy.h>
@@ -429,6 +430,7 @@ void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out,
{"replaceable", UniValueType(UniValue::VBOOL)},
{"conf_target", UniValueType(UniValue::VNUM)},
{"estimate_mode", UniValueType(UniValue::VSTR)},
+ {"input_weights", UniValueType(UniValue::VARR)},
},
true, true);
@@ -548,6 +550,37 @@ void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out,
}
}
+ if (options.exists("input_weights")) {
+ for (const UniValue& input : options["input_weights"].get_array().getValues()) {
+ uint256 txid = ParseHashO(input, "txid");
+
+ const UniValue& vout_v = find_value(input, "vout");
+ if (!vout_v.isNum()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key");
+ }
+ int vout = vout_v.get_int();
+ if (vout < 0) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout cannot be negative");
+ }
+
+ const UniValue& weight_v = find_value(input, "weight");
+ if (!weight_v.isNum()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing weight key");
+ }
+ int64_t weight = weight_v.get_int64();
+ const int64_t min_input_weight = GetTransactionInputWeight(CTxIn());
+ CHECK_NONFATAL(min_input_weight == 165);
+ if (weight < min_input_weight) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, weight cannot be less than 165 (41 bytes (size of outpoint + sequence + empty scriptSig) * 4 (witness scaling factor)) + 1 (empty witness)");
+ }
+ if (weight > MAX_STANDARD_TX_WEIGHT) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, weight cannot be greater than the maximum standard tx weight of %d", MAX_STANDARD_TX_WEIGHT));
+ }
+
+ coinControl.SetInputWeight(COutPoint(txid, vout), weight);
+ }
+ }
+
if (tx.vout.size() == 0)
throw JSONRPCError(RPC_INVALID_PARAMETER, "TX must have at least one output");
@@ -585,6 +618,23 @@ void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out,
}
}
+static void SetOptionsInputWeights(const UniValue& inputs, UniValue& options)
+{
+ if (options.exists("input_weights")) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Input weights should be specified in inputs rather than in options.");
+ }
+ if (inputs.size() == 0) {
+ return;
+ }
+ UniValue weights(UniValue::VARR);
+ for (const UniValue& input : inputs.getValues()) {
+ if (input.exists("weight")) {
+ weights.push_back(input);
+ }
+ }
+ options.pushKV("input_weights", weights);
+}
+
RPCHelpMan fundrawtransaction()
{
return RPCHelpMan{"fundrawtransaction",
@@ -626,6 +676,17 @@ RPCHelpMan fundrawtransaction()
{"vout_index", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The zero-based output index, before a change output is added."},
},
},
+ {"input_weights", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "Inputs and their corresponding weights",
+ {
+ {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
+ {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output index"},
+ {"weight", RPCArg::Type::NUM, RPCArg::Optional::NO, "The maximum weight for this input, "
+ "including the weight of the outpoint and sequence number. "
+ "Note that serialized signature sizes are not guaranteed to be consistent, "
+ "so the maximum DER signatures size of 73 bytes should be used when considering ECDSA signatures."
+ "Remember to convert serialized sizes to weight units when necessary."},
+ },
+ },
},
FundTxDoc()),
"options"},
@@ -1007,6 +1068,11 @@ RPCHelpMan send()
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
{"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
{"sequence", RPCArg::Type::NUM, RPCArg::Optional::NO, "The sequence number"},
+ {"weight", RPCArg::Type::NUM, RPCArg::DefaultHint{"Calculated from wallet and solving data"}, "The maximum weight for this input, "
+ "including the weight of the outpoint and sequence number. "
+ "Note that signature sizes are not guaranteed to be consistent, "
+ "so the maximum DER signatures size of 73 bytes should be used when considering ECDSA signatures."
+ "Remember to convert serialized sizes to weight units when necessary."},
},
},
{"locktime", RPCArg::Type::NUM, RPCArg::Default{0}, "Raw locktime. Non-0 value also locktime-activates inputs"},
@@ -1110,6 +1176,7 @@ RPCHelpMan send()
// Automatically select coins, unless at least one is manually selected. Can
// be overridden by options.add_inputs.
coin_control.m_add_inputs = rawTx.vin.size() == 0;
+ SetOptionsInputWeights(options["inputs"], options);
FundTransaction(*pwallet, rawTx, fee, change_position, options, coin_control, /* override_min_fee */ false);
bool add_to_wallet = true;
@@ -1250,6 +1317,11 @@ RPCHelpMan walletcreatefundedpsbt()
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
{"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
{"sequence", RPCArg::Type::NUM, RPCArg::DefaultHint{"depends on the value of the 'locktime' and 'options.replaceable' arguments"}, "The sequence number"},
+ {"weight", RPCArg::Type::NUM, RPCArg::DefaultHint{"Calculated from wallet and solving data"}, "The maximum weight for this input, "
+ "including the weight of the outpoint and sequence number. "
+ "Note that signature sizes are not guaranteed to be consistent, "
+ "so the maximum DER signatures size of 73 bytes should be used when considering ECDSA signatures."
+ "Remember to convert serialized sizes to weight units when necessary."},
},
},
},
@@ -1330,10 +1402,12 @@ RPCHelpMan walletcreatefundedpsbt()
}, true
);
+ UniValue options = request.params[3];
+
CAmount fee;
int change_position;
bool rbf{wallet.m_signal_rbf};
- const UniValue &replaceable_arg = request.params[3]["replaceable"];
+ const UniValue &replaceable_arg = options["replaceable"];
if (!replaceable_arg.isNull()) {
RPCTypeCheckArgument(replaceable_arg, UniValue::VBOOL);
rbf = replaceable_arg.isTrue();
@@ -1343,7 +1417,8 @@ RPCHelpMan walletcreatefundedpsbt()
// Automatically select coins, unless at least one is manually selected. Can
// be overridden by options.add_inputs.
coin_control.m_add_inputs = rawTx.vin.size() == 0;
- FundTransaction(wallet, rawTx, fee, change_position, request.params[3], coin_control, /* override_min_fee */ true);
+ SetOptionsInputWeights(request.params[0], options);
+ FundTransaction(wallet, rawTx, fee, change_position, options, coin_control, /* override_min_fee */ true);
// Make a blank psbt
PartiallySignedTransaction psbtx(rawTx);
diff --git a/src/wallet/rpc/transactions.cpp b/src/wallet/rpc/transactions.cpp
index d9034808f4..eef2c13ee1 100644
--- a/src/wallet/rpc/transactions.cpp
+++ b/src/wallet/rpc/transactions.cpp
@@ -114,8 +114,8 @@ static UniValue ListReceived(const CWallet& wallet, const UniValue& params, cons
// Coinbase with less than 1 confirmation is no longer in the main chain
if ((wtx.IsCoinBase() && (nDepth < 1 || !include_coinbase))
- || (wallet.IsTxImmatureCoinBase(wtx) && !include_immature_coinbase)
- || !wallet.chain().checkFinalTx(*wtx.tx)) {
+ || (wallet.IsTxImmatureCoinBase(wtx) && !include_immature_coinbase))
+ {
continue;
}
diff --git a/src/wallet/spend.cpp b/src/wallet/spend.cpp
index d87bdc8679..a42df8262c 100644
--- a/src/wallet/spend.cpp
+++ b/src/wallet/spend.cpp
@@ -105,10 +105,6 @@ void AvailableCoins(const CWallet& wallet, std::vector<COutput>& vCoins, const C
const uint256& wtxid = entry.first;
const CWalletTx& wtx = entry.second;
- if (!wallet.chain().checkFinalTx(*wtx.tx)) {
- continue;
- }
-
if (wallet.IsTxImmatureCoinBase(wtx))
continue;
@@ -455,15 +451,17 @@ std::optional<SelectionResult> SelectCoins(const CWallet& wallet, const std::vec
}
input_bytes = GetTxSpendSize(wallet, wtx, outpoint.n, false);
txout = wtx.tx->vout.at(outpoint.n);
- }
- if (input_bytes == -1) {
- // The input is external. We either did not find the tx in mapWallet, or we did but couldn't compute the input size with wallet data
+ } else {
+ // The input is external. We did not find the tx in mapWallet.
if (!coin_control.GetExternalOutput(outpoint, txout)) {
- // Not ours, and we don't have solving data.
return std::nullopt;
}
input_bytes = CalculateMaximumSignedInputSize(txout, &coin_control.m_external_provider, /* use_max_sig */ true);
}
+ // If available, override calculated size with coin control specified size
+ if (coin_control.HasInputWeight(outpoint)) {
+ input_bytes = GetVirtualTransactionSize(coin_control.GetInputWeight(outpoint), 0, 0);
+ }
CInputCoin coin(outpoint, txout, input_bytes);
if (coin.m_input_bytes == -1) {
diff --git a/src/wallet/sqlite.cpp b/src/wallet/sqlite.cpp
index 07e387f177..2b2181e70b 100644
--- a/src/wallet/sqlite.cpp
+++ b/src/wallet/sqlite.cpp
@@ -395,9 +395,9 @@ bool SQLiteBatch::ReadKey(CDataStream&& key, CDataStream& value)
return false;
}
// Leftmost column in result is index 0
- const char* data = reinterpret_cast<const char*>(sqlite3_column_blob(m_read_stmt, 0));
- int data_size = sqlite3_column_bytes(m_read_stmt, 0);
- value.write(data, data_size);
+ const std::byte* data{BytePtr(sqlite3_column_blob(m_read_stmt, 0))};
+ size_t data_size(sqlite3_column_bytes(m_read_stmt, 0));
+ value.write({data, data_size});
sqlite3_clear_bindings(m_read_stmt);
sqlite3_reset(m_read_stmt);
@@ -512,12 +512,12 @@ bool SQLiteBatch::ReadAtCursor(CDataStream& key, CDataStream& value, bool& compl
}
// Leftmost column in result is index 0
- const char* key_data = reinterpret_cast<const char*>(sqlite3_column_blob(m_cursor_stmt, 0));
- int key_data_size = sqlite3_column_bytes(m_cursor_stmt, 0);
- key.write(key_data, key_data_size);
- const char* value_data = reinterpret_cast<const char*>(sqlite3_column_blob(m_cursor_stmt, 1));
- int value_data_size = sqlite3_column_bytes(m_cursor_stmt, 1);
- value.write(value_data, value_data_size);
+ const std::byte* key_data{BytePtr(sqlite3_column_blob(m_cursor_stmt, 0))};
+ size_t key_data_size(sqlite3_column_bytes(m_cursor_stmt, 0));
+ key.write({key_data, key_data_size});
+ const std::byte* value_data{BytePtr(sqlite3_column_blob(m_cursor_stmt, 1))};
+ size_t value_data_size(sqlite3_column_bytes(m_cursor_stmt, 1));
+ value.write({value_data, value_data_size});
return true;
}
diff --git a/src/wallet/test/spend_tests.cpp b/src/wallet/test/spend_tests.cpp
index b2a0697c21..334bd5b8bc 100644
--- a/src/wallet/test/spend_tests.cpp
+++ b/src/wallet/test/spend_tests.cpp
@@ -63,5 +63,56 @@ BOOST_FIXTURE_TEST_CASE(SubtractFee, TestChain100Setup)
BOOST_CHECK_EQUAL(fee, check_tx(fee + 123));
}
+static void TestFillInputToWeight(int64_t additional_weight, std::vector<int64_t> expected_stack_sizes)
+{
+ static const int64_t EMPTY_INPUT_WEIGHT = GetTransactionInputWeight(CTxIn());
+
+ CTxIn input;
+ int64_t target_weight = EMPTY_INPUT_WEIGHT + additional_weight;
+ BOOST_CHECK(FillInputToWeight(input, target_weight));
+ BOOST_CHECK_EQUAL(GetTransactionInputWeight(input), target_weight);
+ BOOST_CHECK_EQUAL(input.scriptWitness.stack.size(), expected_stack_sizes.size());
+ for (unsigned int i = 0; i < expected_stack_sizes.size(); ++i) {
+ BOOST_CHECK_EQUAL(input.scriptWitness.stack[i].size(), expected_stack_sizes[i]);
+ }
+}
+
+BOOST_FIXTURE_TEST_CASE(FillInputToWeightTest, BasicTestingSetup)
+{
+ {
+ // Less than or equal minimum of 165 should not add any witness data
+ CTxIn input;
+ BOOST_CHECK(!FillInputToWeight(input, -1));
+ BOOST_CHECK_EQUAL(GetTransactionInputWeight(input), 165);
+ BOOST_CHECK_EQUAL(input.scriptWitness.stack.size(), 0);
+ BOOST_CHECK(!FillInputToWeight(input, 0));
+ BOOST_CHECK_EQUAL(GetTransactionInputWeight(input), 165);
+ BOOST_CHECK_EQUAL(input.scriptWitness.stack.size(), 0);
+ BOOST_CHECK(!FillInputToWeight(input, 164));
+ BOOST_CHECK_EQUAL(GetTransactionInputWeight(input), 165);
+ BOOST_CHECK_EQUAL(input.scriptWitness.stack.size(), 0);
+ BOOST_CHECK(FillInputToWeight(input, 165));
+ BOOST_CHECK_EQUAL(GetTransactionInputWeight(input), 165);
+ BOOST_CHECK_EQUAL(input.scriptWitness.stack.size(), 0);
+ }
+
+ // Make sure we can add at least one weight
+ TestFillInputToWeight(1, {0});
+
+ // 1 byte compact size uint boundary
+ TestFillInputToWeight(252, {251});
+ TestFillInputToWeight(253, {83, 168});
+ TestFillInputToWeight(262, {86, 174});
+ TestFillInputToWeight(263, {260});
+
+ // 3 byte compact size uint boundary
+ TestFillInputToWeight(65535, {65532});
+ TestFillInputToWeight(65536, {21842, 43688});
+ TestFillInputToWeight(65545, {21845, 43694});
+ TestFillInputToWeight(65546, {65541});
+
+ // Note: We don't test the next boundary because of memory allocation constraints.
+}
+
BOOST_AUTO_TEST_SUITE_END()
} // namespace wallet
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index bb6021b857..9a74545fb5 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -140,11 +140,13 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
}
// Prune the older block file.
+ int file_number;
{
LOCK(cs_main);
- Assert(m_node.chainman)->m_blockman.PruneOneBlockFile(oldTip->GetBlockPos().nFile);
+ file_number = oldTip->GetBlockPos().nFile;
+ Assert(m_node.chainman)->m_blockman.PruneOneBlockFile(file_number);
}
- UnlinkPrunedFiles({oldTip->GetBlockPos().nFile});
+ UnlinkPrunedFiles({file_number});
// Verify ScanForWalletTransactions only picks transactions in the new block
// file.
@@ -169,9 +171,10 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
// Prune the remaining block file.
{
LOCK(cs_main);
- Assert(m_node.chainman)->m_blockman.PruneOneBlockFile(newTip->GetBlockPos().nFile);
+ file_number = newTip->GetBlockPos().nFile;
+ Assert(m_node.chainman)->m_blockman.PruneOneBlockFile(file_number);
}
- UnlinkPrunedFiles({newTip->GetBlockPos().nFile});
+ UnlinkPrunedFiles({file_number});
// Verify ScanForWalletTransactions scans no blocks.
{
@@ -202,11 +205,13 @@ BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup)
CBlockIndex* newTip = m_node.chainman->ActiveChain().Tip();
// Prune the older block file.
+ int file_number;
{
LOCK(cs_main);
- Assert(m_node.chainman)->m_blockman.PruneOneBlockFile(oldTip->GetBlockPos().nFile);
+ file_number = oldTip->GetBlockPos().nFile;
+ Assert(m_node.chainman)->m_blockman.PruneOneBlockFile(file_number);
}
- UnlinkPrunedFiles({oldTip->GetBlockPos().nFile});
+ UnlinkPrunedFiles({file_number});
// Verify importmulti RPC returns failure for a key whose creation time is
// before the missing block, and success for a key whose creation time is
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 3fcb086d2d..337a5139e1 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -1507,6 +1507,49 @@ bool DummySignInput(const SigningProvider& provider, CTxIn &tx_in, const CTxOut
return true;
}
+bool FillInputToWeight(CTxIn& txin, int64_t target_weight)
+{
+ assert(txin.scriptSig.empty());
+ assert(txin.scriptWitness.IsNull());
+
+ int64_t txin_weight = GetTransactionInputWeight(txin);
+
+ // Do nothing if the weight that should be added is less than the weight that already exists
+ if (target_weight < txin_weight) {
+ return false;
+ }
+ if (target_weight == txin_weight) {
+ return true;
+ }
+
+ // Subtract current txin weight, which should include empty witness stack
+ int64_t add_weight = target_weight - txin_weight;
+ assert(add_weight > 0);
+
+ // We will want to subtract the size of the Compact Size UInt that will also be serialized.
+ // However doing so when the size is near a boundary can result in a problem where it is not
+ // possible to have a stack element size and combination to exactly equal a target.
+ // To avoid this possibility, if the weight to add is less than 10 bytes greater than
+ // a boundary, the size will be split so that 2/3rds will be in one stack element, and
+ // the remaining 1/3rd in another. Using 3rds allows us to avoid additional boundaries.
+ // 10 bytes is used because that accounts for the maximum size. This does not need to be super precise.
+ if ((add_weight >= 253 && add_weight < 263)
+ || (add_weight > std::numeric_limits<uint16_t>::max() && add_weight <= std::numeric_limits<uint16_t>::max() + 10)
+ || (add_weight > std::numeric_limits<uint32_t>::max() && add_weight <= std::numeric_limits<uint32_t>::max() + 10)) {
+ int64_t first_weight = add_weight / 3;
+ add_weight -= first_weight;
+
+ first_weight -= GetSizeOfCompactSize(first_weight);
+ txin.scriptWitness.stack.emplace(txin.scriptWitness.stack.end(), first_weight, 0);
+ }
+
+ add_weight -= GetSizeOfCompactSize(add_weight);
+ txin.scriptWitness.stack.emplace(txin.scriptWitness.stack.end(), add_weight, 0);
+ assert(GetTransactionInputWeight(txin) == target_weight);
+
+ return true;
+}
+
// Helper for producing a bunch of max-sized low-S low-R signatures (eg 71 bytes)
bool CWallet::DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOut> &txouts, const CCoinControl* coin_control) const
{
@@ -1515,6 +1558,14 @@ bool CWallet::DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOut>
for (const auto& txout : txouts)
{
CTxIn& txin = txNew.vin[nIn];
+ // If weight was provided, fill the input to that weight
+ if (coin_control && coin_control->HasInputWeight(txin.prevout)) {
+ if (!FillInputToWeight(txin, coin_control->GetInputWeight(txin.prevout))) {
+ return false;
+ }
+ nIn++;
+ continue;
+ }
// Use max sig if watch only inputs were used or if this particular input is an external input
// to ensure a sufficient fee is attained for the requested feerate.
const bool use_max_sig = coin_control && (coin_control->fAllowWatchOnly || coin_control->IsExternalSelected(txin.prevout));
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 00a1865a0e..e2c5c69c91 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -939,6 +939,8 @@ bool AddWalletSetting(interfaces::Chain& chain, const std::string& wallet_name);
bool RemoveWalletSetting(interfaces::Chain& chain, const std::string& wallet_name);
bool DummySignInput(const SigningProvider& provider, CTxIn &tx_in, const CTxOut &txout, bool use_max_sig);
+
+bool FillInputToWeight(CTxIn& txin, int64_t target_weight);
} // namespace wallet
#endif // BITCOIN_WALLET_WALLET_H