Merge branch 'fix_flush_to_not_corrupt'

This commit is contained in:
Brannon King 2020-02-19 12:38:59 -07:00
commit 38f913317b
31 changed files with 4070 additions and 273 deletions

View file

@ -14,7 +14,7 @@ jobs:
- &build-template
stage: build
name: linux
env: NAME=linux EXT=
env: NAME=linux DOCKER_IMAGE=lbry/build_lbrycrd_gcc EXT=
os: linux
dist: xenial
language: minimal
@ -22,7 +22,7 @@ jobs:
- docker
install:
- mkdir -p ${HOME}/ccache
- docker pull $DOCKER_BUILD_IMAGE
- docker pull $DOCKER_IMAGE
script:
- echo "build..."
- docker run -v "$(pwd):/lbrycrd" -v "${HOME}/ccache:/ccache" -w /lbrycrd -e CCACHE_DIR=/ccache ${DOCKER_IMAGE} packaging/build_${NAME}_64bit.sh
@ -56,11 +56,11 @@ jobs:
- <<: *build-template
name: windows
env: NAME=windows EXT=.exe
env: NAME=windows DOCKER_IMAGE=lbry/build_lbrycrd EXT=.exe
- <<: *build-template
name: osx
env: NAME=darwin EXT=
env: NAME=darwin DOCKER_IMAGE=lbry/build_lbrycrd EXT=
before_install:
- mkdir -p ./depends/SDKs && pushd depends/SDKs && curl -C - ${MAC_OS_SDK} | tar --skip-old-files -xJ && popd
@ -86,8 +86,8 @@ jobs:
services:
- docker
script:
- docker pull $DOCKER_WINE_IMAGE
- docker run -v "$(pwd):/test" -e "WINEDEBUG=-all" -e "TRIEHASH_FUZZER_BLOCKS=1000" -it $DOCKER_WINE_IMAGE wine "/test/test_lbrycrd.exe"
- docker pull lbry/wine
- docker run -v "$(pwd):/test" -e "WINEDEBUG=-all" -e "TRIEHASH_FUZZER_BLOCKS=1000" -it lbry/wine wine "/test/test_lbrycrd.exe"
- <<: *test-template
os: osx

View file

@ -3,7 +3,7 @@ AC_PREREQ([2.60])
define(_CLIENT_VERSION_MAJOR, 0)
define(_CLIENT_VERSION_MINOR, 17)
define(_CLIENT_VERSION_REVISION, 4)
define(_CLIENT_VERSION_BUILD, 2)
define(_CLIENT_VERSION_BUILD, 3)
define(_CLIENT_VERSION_IS_RELEASE, true)
define(_COPYRIGHT_YEAR, 2019)
define(_COPYRIGHT_HOLDERS,[The %s developers])

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
linux_CFLAGS=-pipe
linux_CXXFLAGS=$(linux_CFLAGS) -std=c++11
linux_release_CFLAGS=-O2 -g
linux_release_CFLAGS=-O3 -g -include $(BASEDIR)/glibc_version_header/force_link_glibc_2.19.h
linux_release_CXXFLAGS=$(linux_release_CFLAGS)
linux_debug_CFLAGS=-O1 -g

View file

@ -30,8 +30,8 @@ define $(package)_config_cmds
PKG_CONFIG_SYSROOT_DIR=/ \
PKG_CONFIG_LIBDIR=$(host_prefix)/lib/pkgconfig \
PKG_CONFIG_PATH=$(host_prefix)/share/pkgconfig \
sed -i.old 's/^GEN_DEPS.c=.*/& $($(package)_cflags)/' config/mh-mingw* && \
sed -i.old 's/^GEN_DEPS.cc=.*/& $($(package)_cxxflags)/' config/mh-mingw* && \
sed -i.old 's|^GEN_DEPS.c=.*|& $($(package)_cflags)|' config/mh-mingw* && \
sed -i.old 's|^GEN_DEPS.cc=.*|& $($(package)_cxxflags)|' config/mh-mingw* && \
$($(package)_autoconf)
endef

View file

@ -5,7 +5,7 @@ $(package)_file_name=$(package)-$($(package)_version).tar.gz
$(package)_sha256_hash=8f9faeaebad088e772f4ef5e38252d472be4d878c6b3a2718c10a4fcebe7a41c
define $(package)_set_vars
$(package)_config_env=AR="$($(package)_ar)" RANLIB="$($(package)_ranlib)" CC="$($(package)_cc)"
$(package)_config_env=AR="$($(package)_ar)" RANLIB="$($(package)_ranlib)" CC="$($(package)_cc) $($(package)_cflags) $($(package)_cppflags)"
$(package)_config_opts=--prefix=$(host_prefix) --openssldir=$(host_prefix)/etc/openssl
$(package)_config_opts+=no-camellia
$(package)_config_opts+=no-capieng
@ -42,7 +42,6 @@ $(package)_config_opts+=no-weak-ssl-ciphers
$(package)_config_opts+=no-whirlpool
$(package)_config_opts+=no-zlib
$(package)_config_opts+=no-zlib-dynamic
$(package)_config_opts+=$($(package)_cflags) $($(package)_cppflags)
$(package)_config_opts_linux=-fPIC -Wa,--noexecstack
$(package)_config_opts_x86_64_linux=linux-x86_64
$(package)_config_opts_i686_linux=linux-generic32

View file

@ -1,17 +1,12 @@
FROM ubuntu:16.04
FROM ubuntu:18.04
ENV LANG C.UTF-8
RUN set -xe; \
apt-get update; \
apt-get install --no-install-recommends -y build-essential libtool autotools-dev automake pkg-config git wget apt-utils \
librsvg2-bin libtiff-tools cmake imagemagick libcap-dev libz-dev libbz2-dev python-setuptools xz-utils ccache g++-multilib \
g++-mingw-w64-i686 mingw-w64-i686-dev bsdmainutils curl ca-certificates g++-mingw-w64-x86-64 mingw-w64-x86-64-dev; \
rm -rf /var/lib/apt/lists/*;
RUN wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -; \
echo 'deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-8 main' >> /etc/apt/sources.list; \
apt-get update; \
apt-get install --no-install-recommends -y clang-8 lldb-8 lld-8 libc++-8-dev; \
librsvg2-bin libtiff-tools cmake imagemagick libcap-dev libz-dev libbz2-dev python-setuptools python3-setuptools xz-utils ccache g++-multilib \
g++-mingw-w64-i686 mingw-w64-i686-dev bsdmainutils curl ca-certificates g++-mingw-w64-x86-64 mingw-w64-x86-64-dev \
clang-8 lldb-8 lld-8 libc++-8-dev; \
rm -rf /var/lib/apt/lists/*;
RUN update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang-cpp-8 80; \
@ -28,7 +23,7 @@ ARG VCS_REF
ARG BUILD_DATE
LABEL maintainer="blockchain@lbry.com" \
decription="build_lbrycrd" \
version="1.1" \
version="1.2" \
org.label-schema.name="build_lbrycrd" \
org.label-schema.description="Use this to generate a reproducible build of LBRYcrd" \
org.label-schema.build-date=$BUILD_DATE \

View file

@ -0,0 +1,28 @@
FROM ubuntu:18.04
ENV LANG C.UTF-8
RUN set -xe; \
apt-get update; \
apt-get install --no-install-recommends -y build-essential libtool autotools-dev automake pkg-config git wget apt-utils \
librsvg2-bin cmake libcap-dev libz-dev libbz2-dev python-setuptools python3-setuptools xz-utils ccache \
bsdmainutils curl ca-certificates; \
rm -rf /var/lib/apt/lists/*; \
/usr/sbin/update-ccache-symlinks;
ARG VCS_REF
ARG BUILD_DATE
LABEL maintainer="blockchain@lbry.com" \
decription="build_lbrycrd_gcc" \
version="1.2" \
org.label-schema.name="build_lbrycrd_gcc" \
org.label-schema.description="Use this to generate a reproducible build of LBRYcrd for Linux" \
org.label-schema.build-date=$BUILD_DATE \
org.label-schema.vcs-ref=$VCS_REF \
org.label-schema.vcs-url="https://github.com/lbryio/lbrycrd" \
org.label-schema.schema-version="1.0.0-rc1" \
org.label-schema.vendor="LBRY" \
org.label-schema.docker.cmd="docker build --build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` --build-arg VCS_REF=`git rev-parse --short HEAD` -t lbry/build_lbrycrd_gcc packaging"
ENV PATH "/usr/lib/ccache:$PATH"
WORKDIR /home
CMD ["/bin/bash"]

View file

@ -171,7 +171,7 @@ class CBlockIndex
{
public:
//! pointer to the hash of the block, if any. Memory is owned by this CBlockIndex
const uint256* phashBlock;
const uint256 hash;
//! pointer to the index of the predecessor of this block
CBlockIndex* pprev;
@ -222,7 +222,6 @@ public:
void SetNull()
{
phashBlock = nullptr;
pprev = nullptr;
pskip = nullptr;
nHeight = 0;
@ -244,12 +243,12 @@ public:
nNonce = 0;
}
CBlockIndex()
CBlockIndex(const uint256& blockHash) : hash(blockHash)
{
SetNull();
}
explicit CBlockIndex(const CBlockHeader& block)
explicit CBlockIndex(const CBlockHeader& block) : hash(block.GetHash())
{
SetNull();
@ -295,7 +294,7 @@ public:
uint256 GetBlockHash() const
{
return *phashBlock;
return hash;
}
uint256 GetBlockPoWHash() const

View file

@ -10,14 +10,9 @@
bool CCoinsView::GetCoin(const COutPoint &outpoint, Coin &coin) const { return false; }
uint256 CCoinsView::GetBestBlock() const { return uint256(); }
std::vector<uint256> CCoinsView::GetHeadBlocks() const { return std::vector<uint256>(); }
bool CCoinsView::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, bool sync) { return false; }
bool CCoinsView::BatchWrite(const CCoinsMap &mapCoins, const uint256 &hashBlock, bool sync) { return false; }
CCoinsViewCursor *CCoinsView::Cursor() const { return nullptr; }
bool CCoinsView::HaveCoin(const COutPoint &outpoint) const
{
Coin coin;
return GetCoin(outpoint, coin);
}
bool CCoinsView::HaveCoin(const COutPoint &outpoint) const { return false; }
CCoinsViewBacked::CCoinsViewBacked(CCoinsView *viewIn) : base(viewIn) { }
bool CCoinsViewBacked::GetCoin(const COutPoint &outpoint, Coin &coin) const { return base->GetCoin(outpoint, coin); }
@ -25,7 +20,7 @@ bool CCoinsViewBacked::HaveCoin(const COutPoint &outpoint) const { return base->
uint256 CCoinsViewBacked::GetBestBlock() const { return base->GetBestBlock(); }
std::vector<uint256> CCoinsViewBacked::GetHeadBlocks() const { return base->GetHeadBlocks(); }
void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; }
bool CCoinsViewBacked::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, bool sync) { return base->BatchWrite(mapCoins, hashBlock, sync); }
bool CCoinsViewBacked::BatchWrite(const CCoinsMap &mapCoins, const uint256 &hashBlock, bool sync) { return base->BatchWrite(mapCoins, hashBlock, sync); }
CCoinsViewCursor *CCoinsViewBacked::Cursor() const { return base->Cursor(); }
size_t CCoinsViewBacked::EstimateSize() const { return base->EstimateSize(); }
@ -127,11 +122,6 @@ bool CCoinsViewCache::HaveCoin(const COutPoint &outpoint) const {
return (it != cacheCoins.end() && !it->second.coin.IsSpent());
}
bool CCoinsViewCache::HaveCoinInCache(const COutPoint &outpoint) const {
CCoinsMap::const_iterator it = cacheCoins.find(outpoint);
return (it != cacheCoins.end() && !it->second.coin.IsSpent());
}
uint256 CCoinsViewCache::GetBestBlock() const {
if (hashBlock.IsNull())
hashBlock = base->GetBestBlock();
@ -142,8 +132,8 @@ void CCoinsViewCache::SetBestBlock(const uint256 &hashBlockIn) {
hashBlock = hashBlockIn;
}
bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn, bool sync) {
for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end(); it = mapCoins.erase(it)) {
bool CCoinsViewCache::BatchWrite(const CCoinsMap &mapCoins, const uint256 &hashBlockIn, bool sync) {
for (auto it = mapCoins.begin(); it != mapCoins.end(); ++it) {
// Ignore non-dirty entries (optimization).
if (!(it->second.flags & CCoinsCacheEntry::DIRTY)) {
continue;

View file

@ -163,7 +163,7 @@ public:
//! Do a bulk modification (multiple Coin changes + BestBlock change).
//! The passed mapCoins can be modified.
virtual bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, bool sync);
virtual bool BatchWrite(const CCoinsMap &mapCoins, const uint256 &hashBlock, bool sync);
//! Get a cursor to iterate over the whole state
virtual CCoinsViewCursor *Cursor() const;
@ -189,7 +189,7 @@ public:
uint256 GetBestBlock() const override;
std::vector<uint256> GetHeadBlocks() const override;
void SetBackend(CCoinsView &viewIn);
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, bool sync) override;
bool BatchWrite(const CCoinsMap &mapCoins, const uint256 &hashBlock, bool sync) override;
CCoinsViewCursor *Cursor() const override;
size_t EstimateSize() const override;
};
@ -222,18 +222,11 @@ public:
bool HaveCoin(const COutPoint &outpoint) const override;
uint256 GetBestBlock() const override;
void SetBestBlock(const uint256 &hashBlock);
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, bool sync) override;
bool BatchWrite(const CCoinsMap &mapCoins, const uint256 &hashBlock, bool sync) override;
CCoinsViewCursor* Cursor() const override {
throw std::logic_error("CCoinsViewCache cursor iteration not supported.");
}
/**
* Check if we have the given utxo already loaded in this cache.
* The semantics are the same as HaveCoin(), but no calls to
* the backing CCoinsView are made.
*/
bool HaveCoinInCache(const COutPoint &outpoint) const;
/**
* Return a reference to Coin in the cache, or a pruned one if not found. This is
* more efficient than GetCoin.

View file

@ -241,7 +241,7 @@ void Shutdown()
// FlushStateToDisk generates a ChainStateFlushed callback, which we should avoid missing
if (pcoinsTip != nullptr) {
FlushStateToDisk();
FlushStateToDisk(true);
}
// After there are no more peers/RPC left to give us new data which may generate
@ -257,7 +257,7 @@ void Shutdown()
{
LOCK(cs_main);
if (pcoinsTip != nullptr) {
FlushStateToDisk();
FlushStateToDisk(true);
}
pcoinsTip.reset();
pcoinscatcher.reset();

View file

@ -119,10 +119,57 @@ bool DecodeClaimScript(const CScript& scriptIn, int& op, std::vector<std::vector
vvchParams.push_back(std::move(vchParam2));
if (last_drop == OP_2DROP)
vvchParams.push_back(std::move(vchParam3));
return true;
}
// this overload exists because optimizer wouldn't inline GetScriptOp:
bool DecodeClaimScript(const CScript& scriptIn, int& op, CScript::const_iterator& pc, bool allowSupportMetadata)
{
op = -1;
opcodetype opcode;
if (!scriptIn.GetOp(pc, opcode))
return false;
if (opcode != OP_CLAIM_NAME && opcode != OP_SUPPORT_CLAIM && opcode != OP_UPDATE_CLAIM)
return false;
op = opcode;
// Valid formats:
// OP_CLAIM_NAME vchName vchValue OP_2DROP OP_DROP pubkeyscript
// OP_UPDATE_CLAIM vchName vchClaimId vchValue OP_2DROP OP_2DROP pubkeyscript
// OP_SUPPORT_CLAIM vchName vchClaimId OP_2DROP OP_DROP pubkeyscript
// OP_SUPPORT_CLAIM vchName vchClaimId vchValue OP_2DROP OP_2DROP pubkeyscript
// All others are invalid.
if (!scriptIn.GetOp(pc, opcode) || opcode < 0 || opcode > OP_PUSHDATA4)
return false;
if (!scriptIn.GetOp(pc, opcode) || opcode < 0 || opcode > OP_PUSHDATA4)
return false;
if (!scriptIn.GetOp(pc, opcode))
return false;
auto last_drop = OP_DROP;
if (opcode >= 0 && opcode <= OP_PUSHDATA4 && op != OP_CLAIM_NAME) {
if (!scriptIn.GetOp(pc, opcode))
return false;
last_drop = OP_2DROP;
} else if (op == OP_UPDATE_CLAIM)
return false;
if (opcode != OP_2DROP)
return false;
if (!scriptIn.GetOp(pc, opcode) || opcode != last_drop)
return false;
return !(op == OP_SUPPORT_CLAIM && last_drop == OP_2DROP && !allowSupportMetadata);
}
uint160 ClaimIdHash(const uint256& txhash, uint32_t nOut)
{
std::vector<unsigned char> claimToHash(txhash.begin(), txhash.end());
@ -139,10 +186,10 @@ CScript StripClaimScriptPrefix(const CScript& scriptIn)
CScript StripClaimScriptPrefix(const CScript& scriptIn, int& op)
{
std::vector<std::vector<unsigned char> > vvchParams;
CScript::const_iterator pc = scriptIn.begin();
if (!DecodeClaimScript(scriptIn, op, vvchParams, pc))
auto isClaim = DecodeClaimScript(scriptIn, op, pc);
if (!isClaim)
return scriptIn;
return CScript(pc, scriptIn.end());

View file

@ -30,6 +30,7 @@ CScript SupportClaimScript(std::string name, uint160 claimId, std::string value=
CScript UpdateClaimScript(std::string name, uint160 claimId, std::string value);
bool DecodeClaimScript(const CScript& scriptIn, int& op, std::vector<std::vector<unsigned char> >& vvchParams, bool allowSupportMetadata=true);
bool DecodeClaimScript(const CScript& scriptIn, int& op, std::vector<std::vector<unsigned char> >& vvchParams, CScript::const_iterator& pc, bool allowSupportMetadata=true);
bool DecodeClaimScript(const CScript& scriptIn, int& op, CScript::const_iterator& pc, bool allowSupportMetadata=true);
CScript StripClaimScriptPrefix(const CScript& scriptIn);
CScript StripClaimScriptPrefix(const CScript& scriptIn, int& op);
uint160 ClaimIdHash(const uint256& txhash, uint32_t nOut);

View file

@ -1049,8 +1049,8 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
return recentRejects->contains(inv.hash) ||
mempool.exists(inv.hash) ||
pcoinsTip->HaveCoinInCache(COutPoint(inv.hash, 0)) || // Best effort: only try output 0 and 1
pcoinsTip->HaveCoinInCache(COutPoint(inv.hash, 1));
pcoinsTip->HaveCoin(COutPoint(inv.hash, 0)) || // Best effort: only try output 0 and 1
pcoinsTip->HaveCoin(COutPoint(inv.hash, 1));
}
case MSG_BLOCK:
case MSG_WITNESS_BLOCK:

View file

@ -1351,9 +1351,9 @@ static UniValue getchaintips(const JSONRPCRequest& request)
for (const auto& item : g_chainstate.mapBlockIndex)
{
if (!chainActive.Contains(item.second)) {
setOrphans.insert(item.second);
setPrevs.insert(item.second->pprev);
if (!chainActive.Contains(item)) {
setOrphans.insert(item);
setPrevs.insert(item->pprev);
}
}
@ -1373,7 +1373,7 @@ static UniValue getchaintips(const JSONRPCRequest& request)
{
UniValue obj(UniValue::VOBJ);
obj.pushKV("height", block->nHeight);
obj.pushKV("hash", block->phashBlock->GetHex());
obj.pushKV("hash", block->hash.GetHex());
// not use ForkAt method because we need the previous one as well
const CBlockIndex *forkAt = block, *forkPrev = block;
@ -1385,8 +1385,8 @@ static UniValue getchaintips(const JSONRPCRequest& request)
const int branchLen = block->nHeight - forkAt->nHeight;
obj.pushKV("branchlen", branchLen);
if (forkAt != forkPrev) {
obj.pushKV("branchhash", forkAt->phashBlock->GetHex());
obj.pushKV("branchhashNext", forkPrev->phashBlock->GetHex());
obj.pushKV("branchhash", forkAt->hash.GetHex());
obj.pushKV("branchhashNext", forkPrev->hash.GetHex());
}
std::string status;

View file

@ -1,7 +1,5 @@
#include <boost/test/unit_test.hpp>
#include <stdlib.h>
#include <rpc/blockchain.h>
#include <test/test_bitcoin.h>
#include <txdb.h>
@ -16,7 +14,7 @@ static bool DoubleEquals(double a, double b, double epsilon)
static CBlockIndex* CreateBlockIndexWithNbits(uint32_t nbits)
{
CBlockIndex* block_index = new CBlockIndex();
CBlockIndex* block_index = new CBlockIndex((uint256()));
block_index->nHeight = 46367;
block_index->nTime = 1269211443;
block_index->nBits = nbits;

View file

@ -54,9 +54,9 @@ public:
uint256 GetBestBlock() const override { return hashBestBlock_; }
bool BatchWrite(CCoinsMap& mapCoins, const uint256& hashBlock, bool sync) override
bool BatchWrite(const CCoinsMap& mapCoins, const uint256& hashBlock, bool sync) override
{
for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end(); ) {
for (auto it = mapCoins.begin(); it != mapCoins.end(); ++it) {
if (it->second.flags & CCoinsCacheEntry::DIRTY) {
// Same optimization used in CCoinsViewDB is to only write dirty entries.
map_[it->first] = it->second.coin;
@ -65,7 +65,6 @@ public:
map_.erase(it->first);
}
}
mapCoins.erase(it++);
}
if (!hashBlock.IsNull())
hashBestBlock_ = hashBlock;
@ -186,7 +185,7 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test)
COutPoint out(txids[InsecureRand32() % txids.size()], 0);
int cacheid = InsecureRand32() % stack.size();
stack[cacheid]->Uncache(out);
uncached_an_entry |= !stack[cacheid]->HaveCoinInCache(out);
uncached_an_entry |= !stack[cacheid]->HaveCoin(out);
}
// Once every 1000 iterations and at the end, verify the full cache.
@ -199,7 +198,7 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test)
if (coin.IsSpent()) {
missed_an_entry = true;
} else {
BOOST_CHECK(stack.back()->HaveCoinInCache(entry.first));
BOOST_CHECK(stack.back()->HaveCoin(entry.first));
found_an_entry = true;
}
}

View file

@ -76,7 +76,7 @@ const unsigned int nonces[] = {
static CBlockIndex CreateBlockIndex(int nHeight)
{
CBlockIndex index;
CBlockIndex index((uint256()));
index.nHeight = nHeight;
index.pprev = chainActive.Tip();
return index;
@ -376,8 +376,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
// Create an actual 209999-long block chain (without valid blocks).
while (chainActive.Tip()->nHeight < 209999) {
CBlockIndex* prev = chainActive.Tip();
CBlockIndex* next = new CBlockIndex();
next->phashBlock = new uint256(InsecureRand256());
CBlockIndex* next = new CBlockIndex(InsecureRand256());
next->hashClaimTrie = pblocktemplate->block.hashClaimTrie;
pcoinsTip->SetBestBlock(next->GetBlockHash());
next->pprev = prev;
@ -389,8 +388,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
// Extend to a 210000-long block chain.
while (chainActive.Tip()->nHeight < 210000) {
CBlockIndex* prev = chainActive.Tip();
CBlockIndex* next = new CBlockIndex();
next->phashBlock = new uint256(InsecureRand256());
CBlockIndex* next = new CBlockIndex(InsecureRand256());
next->hashClaimTrie = pblocktemplate->block.hashClaimTrie;
pcoinsTip->SetBestBlock(next->GetBlockHash());
next->pprev = prev;
@ -423,7 +421,6 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
CBlockIndex* del = chainActive.Tip();
chainActive.SetTip(del->pprev);
pcoinsTip->SetBestBlock(del->pprev->GetBlockHash());
delete del->phashBlock;
delete del;
}

View file

@ -18,7 +18,7 @@ BOOST_AUTO_TEST_CASE(get_next_work)
{
const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
int64_t nLastRetargetTime = 1261130161; // Block #30240
CBlockIndex pindexLast;
CBlockIndex pindexLast((uint256()));
pindexLast.nHeight = 32255;
pindexLast.nTime = 1262152739; // Block #32255
pindexLast.nBits = 0x1d00ffff;
@ -30,7 +30,7 @@ BOOST_AUTO_TEST_CASE(get_next_work_pow_limit)
{
const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
int64_t nLastRetargetTime = 1231006505; // Block #0
CBlockIndex pindexLast;
CBlockIndex pindexLast((uint256()));
pindexLast.nHeight = 2015;
pindexLast.nTime = 1233061996; // Block #2015
pindexLast.nBits = 0x1d00ffff;
@ -42,7 +42,7 @@ BOOST_AUTO_TEST_CASE(get_next_work_lower_limit_actual)
{
const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
int64_t nLastRetargetTime = 1279008237; // Block #66528
CBlockIndex pindexLast;
CBlockIndex pindexLast((uint256()));
pindexLast.nHeight = 68543;
pindexLast.nTime = 1279297671; // Block #68543
pindexLast.nBits = 0x1c05a3f4;
@ -54,7 +54,7 @@ BOOST_AUTO_TEST_CASE(get_next_work_upper_limit_actual)
{
const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
int64_t nLastRetargetTime = 1263163443; // NOTE: Not an actual block time
CBlockIndex pindexLast;
CBlockIndex pindexLast((uint256()));
pindexLast.nHeight = 46367;
pindexLast.nTime = 1269211443; // Block #46367
pindexLast.nBits = 0x1c387f6f;
@ -64,8 +64,10 @@ BOOST_AUTO_TEST_CASE(get_next_work_upper_limit_actual)
BOOST_AUTO_TEST_CASE(GetBlockProofEquivalentTime_test)
{
const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
std::vector<CBlockIndex> blocks(10000);
std::vector<CBlockIndex> blocks;
blocks.reserve(10000);
for (int i = 0; i < 10000; i++) {
blocks.emplace_back(uint256());
blocks[i].pprev = i ? &blocks[i - 1] : nullptr;
blocks[i].nHeight = i;
blocks[i].nTime = 1269211443 + i * chainParams->GetConsensus().nPowTargetSpacing;

View file

@ -16,9 +16,11 @@ BOOST_FIXTURE_TEST_SUITE(skiplist_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(skiplist_test)
{
std::vector<CBlockIndex> vIndex(SKIPLIST_LENGTH);
std::vector<CBlockIndex> vIndex;
vIndex.reserve(SKIPLIST_LENGTH);
for (int i=0; i<SKIPLIST_LENGTH; i++) {
vIndex.emplace_back(uint256());
vIndex[i].nHeight = i;
vIndex[i].pprev = (i == 0) ? nullptr : &vIndex[i - 1];
vIndex[i].BuildSkip();
@ -47,12 +49,13 @@ BOOST_AUTO_TEST_CASE(getlocator_test)
{
// Build a main chain 100000 blocks long.
std::vector<uint256> vHashMain(100000);
std::vector<CBlockIndex> vBlocksMain(100000);
for (unsigned int i=0; i<vBlocksMain.size(); i++) {
std::vector<CBlockIndex> vBlocksMain;
vBlocksMain.reserve(100000);
for (unsigned int i=0; i<vHashMain.size(); i++) {
vHashMain[i] = ArithToUint256(i); // Set the hash equal to the height, so we can quickly check the distances.
vBlocksMain.emplace_back(vHashMain[i]);
vBlocksMain[i].nHeight = i;
vBlocksMain[i].pprev = i ? &vBlocksMain[i - 1] : nullptr;
vBlocksMain[i].phashBlock = &vHashMain[i];
vBlocksMain[i].BuildSkip();
BOOST_CHECK_EQUAL((int)UintToArith256(vBlocksMain[i].GetBlockHash()).GetLow64(), vBlocksMain[i].nHeight);
BOOST_CHECK(vBlocksMain[i].pprev == nullptr || vBlocksMain[i].nHeight == vBlocksMain[i].pprev->nHeight + 1);
@ -60,12 +63,13 @@ BOOST_AUTO_TEST_CASE(getlocator_test)
// Build a branch that splits off at block 49999, 50000 blocks long.
std::vector<uint256> vHashSide(50000);
std::vector<CBlockIndex> vBlocksSide(50000);
for (unsigned int i=0; i<vBlocksSide.size(); i++) {
std::vector<CBlockIndex> vBlocksSide;
vBlocksSide.reserve(50000);
for (unsigned int i=0; i<vHashSide.size(); i++) {
vHashSide[i] = ArithToUint256(i + 50000 + (arith_uint256(1) << 128)); // Add 1<<128 to the hashes, so GetLow64() still returns the height.
vBlocksSide.emplace_back(vHashSide[i]);
vBlocksSide[i].nHeight = i + 50000;
vBlocksSide[i].pprev = i ? &vBlocksSide[i - 1] : (vBlocksMain.data()+49999);
vBlocksSide[i].phashBlock = &vHashSide[i];
vBlocksSide[i].BuildSkip();
BOOST_CHECK_EQUAL((int)UintToArith256(vBlocksSide[i].GetBlockHash()).GetLow64(), vBlocksSide[i].nHeight);
BOOST_CHECK(vBlocksSide[i].pprev == nullptr || vBlocksSide[i].nHeight == vBlocksSide[i].pprev->nHeight + 1);
@ -102,12 +106,13 @@ BOOST_AUTO_TEST_CASE(getlocator_test)
BOOST_AUTO_TEST_CASE(findearliestatleast_test)
{
std::vector<uint256> vHashMain(100000);
std::vector<CBlockIndex> vBlocksMain(100000);
for (unsigned int i=0; i<vBlocksMain.size(); i++) {
std::vector<CBlockIndex> vBlocksMain;
vBlocksMain.reserve(100000);
for (unsigned int i=0; i<vHashMain.size(); i++) {
vHashMain[i] = ArithToUint256(i); // Set the hash equal to the height
vBlocksMain.emplace_back(vHashMain[i]);
vBlocksMain[i].nHeight = i;
vBlocksMain[i].pprev = i ? &vBlocksMain[i - 1] : nullptr;
vBlocksMain[i].phashBlock = &vHashMain[i];
vBlocksMain[i].BuildSkip();
if (i < 10) {
vBlocksMain[i].nTime = i;
@ -148,7 +153,7 @@ BOOST_AUTO_TEST_CASE(findearliestatleast_edge_test)
std::list<CBlockIndex> blocks;
for (unsigned int timeMax : {100, 100, 100, 200, 200, 200, 300, 300, 300}) {
CBlockIndex* prev = blocks.empty() ? nullptr : &blocks.back();
blocks.emplace_back();
blocks.emplace_back(uint256());
blocks.back().nHeight = prev ? prev->nHeight + 1 : 0;
blocks.back().pprev = prev;
blocks.back().BuildSkip();

View file

@ -76,7 +76,7 @@ public:
VersionBitsTester& Mine(unsigned int height, int32_t nTime, int32_t nVersion) {
while (vpblock.size() < height) {
CBlockIndex* pindex = new CBlockIndex();
CBlockIndex* pindex = new CBlockIndex((uint256()));
pindex->nHeight = vpblock.size();
pindex->pprev = vpblock.size() > 0 ? vpblock.back() : nullptr;
pindex->nTime = nTime;

View file

@ -93,74 +93,61 @@ std::vector<uint256> CCoinsViewDB::GetHeadBlocks() const {
return vhashHeadBlocks;
}
bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, bool sync) {
bool CCoinsViewDB::BatchWrite(const CCoinsMap &mapCoins, const uint256 &hashBlock, bool sync) {
size_t count = 0;
size_t changed = 0;
int crash_simulate = gArgs.GetArg("-dbcrashratio", 0);
assert(!hashBlock.IsNull());
uint256 old_tip = GetBestBlock();
if (old_tip.IsNull()) {
// We may be in the middle of replaying.
std::vector<uint256> old_heads = GetHeadBlocks();
if (old_heads.size() == 2) {
assert(old_heads[0] == hashBlock);
old_tip = old_heads[1];
}
}
db << "BEGIN";
db << "INSERT OR REPLACE INTO marker VALUES('head_block', ?)" << hashBlock;
auto dbd = db << "DELETE FROM unspent WHERE txID = ? AND txN = ?";
auto dbi = db << "INSERT OR REPLACE INTO unspent VALUES(?,?,?,?,?,?,?)";
for (auto it = mapCoins.begin(); it != mapCoins.end();) {
if (it->second.flags & CCoinsCacheEntry::DIRTY) {
if (it->second.coin.IsSpent()) {
// at present the "IsSpent" flag is used for both "spent" and "block going backwards"
dbd << it->first.hash << it->first.n;
dbd++;
if (!mapCoins.empty()) {
db << "INSERT OR REPLACE INTO marker VALUES('head_block', ?)" << hashBlock;
auto dbd = db << "DELETE FROM unspent WHERE txID = ? AND txN = ?";
auto dbi = db << "INSERT OR REPLACE INTO unspent VALUES(?,?,?,?,?,?,?)";
for (auto it = mapCoins.begin(); it != mapCoins.end(); ++it) {
if (it->second.flags & CCoinsCacheEntry::DIRTY) {
if (it->second.coin.IsSpent()) {
// at present the "IsSpent" flag is used for both "spent" and "block going backwards"
dbd << it->first.hash << it->first.n;
dbd++;
} else {
CTxDestination address;
std::string destination;
if (ExtractDestination(it->second.coin.out.scriptPubKey, address))
destination = EncodeDestination(address);
uint32_t isCoinBase = it->second.coin.fCoinBase; // bit-field
uint32_t coinHeight = it->second.coin.nHeight; // bit-field
dbi << it->first.hash << it->first.n << isCoinBase << coinHeight
<< it->second.coin.out.nValue << it->second.coin.out.scriptPubKey << destination;
dbi++;
}
changed++;
}
else {
CTxDestination address;
std::string destination;
if (ExtractDestination(it->second.coin.out.scriptPubKey, address))
destination = EncodeDestination(address);
uint32_t isCoinBase = it->second.coin.fCoinBase; // bit-field
uint32_t coinHeight = it->second.coin.nHeight; // bit-field
dbi << it->first.hash << it->first.n << isCoinBase << coinHeight
<< it->second.coin.out.nValue << it->second.coin.out.scriptPubKey << destination;
dbi++;
}
changed++;
}
count++;
auto itOld = it++;
mapCoins.erase(itOld);
if (crash_simulate && count % 200000 == 0) {
static FastRandomContext rng;
if (rng.randrange(crash_simulate) == 0) {
LogPrintf("Simulating a crash. Goodbye.\n");
_Exit(0);
if (crash_simulate && ++count % 200000 == 0) {
static FastRandomContext rng;
if (rng.randrange(crash_simulate) == 0) {
LogPrintf("Simulating a crash. Goodbye.\n");
_Exit(0);
}
}
}
dbd.used(true);
dbi.used(true);
db << "DELETE FROM marker WHERE name = 'head_block'";
}
dbd.used(true);
dbi.used(true);
db << "INSERT OR REPLACE INTO marker VALUES('best_block', ?)" << hashBlock;
db << "DELETE FROM marker WHERE name = 'head_block'";
auto code = sqlite::commit(db);
if (code != SQLITE_OK) {
LogPrint(BCLog::COINDB, "Error committing transaction outputs changes to coin database. SQLite error: %d\n", code);
LogPrintf("%s: Error committing coins info to database. SQLite error: %d\n", __func__, code);
return false;
}
LogPrint(BCLog::COINDB, "Committed %u changed transaction outputs (out of %u) to coin database...\n", (unsigned int)changed, (unsigned int)count);
LogPrint(BCLog::COINDB, "Committed %zu changed transaction outputs (out of %zu) to coin database...\n", changed, count);
if (sync) {
auto code = sqlite::sync(db);
code = sqlite::sync(db);
if (code != SQLITE_OK) {
LogPrint(BCLog::COINDB, "Error syncing coin database. SQLite error: %d\n", code);
LogPrintf("%s: Error syncing coin database. SQLite error: %d\n", __func__, code);
return false;
}
}
@ -312,27 +299,33 @@ void CCoinsViewDBCursor::Next()
bool CBlockTreeDB::BatchWrite(const std::vector<std::pair<int, const CBlockFileInfo*> >& fileInfo,
int nLastFile, const std::vector<const CBlockIndex*>& blockInfo, bool sync) {
db << "BEGIN";
auto ibf = db << "INSERT OR REPLACE INTO block_file(file, blocks, size, undoSize, heightFirst, heightLast, timeFirst, timeLast) "
"VALUES(?,?,?,?,?,?,?,?)";
for (auto& kvp: fileInfo) {
ibf << kvp.first << kvp.second->nBlocks << kvp.second->nSize << kvp.second->nUndoSize
<< kvp.second->nHeightFirst << kvp.second->nHeightLast << kvp.second->nTimeFirst << kvp.second->nTimeLast;
ibf++;
if (!fileInfo.empty()) {
auto ibf = db << "INSERT OR REPLACE INTO block_file(file, blocks, size, undoSize, heightFirst, "
"heightLast, timeFirst, timeLast) VALUES(?,?,?,?,?,?,?,?)";
for (auto &kvp: fileInfo) {
ibf << kvp.first << kvp.second->nBlocks << kvp.second->nSize << kvp.second->nUndoSize
<< kvp.second->nHeightFirst << kvp.second->nHeightLast << kvp.second->nTimeFirst
<< kvp.second->nTimeLast;
ibf++;
}
ibf.used(true);
}
ibf.used(true);
db << "INSERT OR REPLACE INTO flag VALUES('last_block', ?)" << nLastFile; // TODO: is this always max(file column)?
auto ibi = db << "INSERT OR REPLACE INTO block_info(hash, prevHash, height, file, dataPos, undoPos, "
"txCount, status, version, rootTxHash, rootTrieHash, time, bits, nonce) "
"VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
for (auto& bi: blockInfo) {
ibi << bi->GetBlockHash() << (bi->pprev ? bi->pprev->GetBlockHash() : uint256())
<< bi->nHeight << bi->nFile << bi->nDataPos << bi->nUndoPos << bi->nTx
<< bi->nStatus << bi->nVersion << bi->hashMerkleRoot << bi->hashClaimTrie
<< bi->nTime << bi->nBits << bi->nNonce;
ibi++;
if(!blockInfo.empty()) {
auto ibi = db << "INSERT OR REPLACE INTO block_info(hash, prevHash, height, file, dataPos, undoPos, "
"txCount, status, version, rootTxHash, rootTrieHash, time, bits, nonce) "
"VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
for (auto &bi: blockInfo) {
ibi << bi->GetBlockHash() << (bi->pprev ? bi->pprev->GetBlockHash() : uint256())
<< bi->nHeight << bi->nFile << bi->nDataPos << bi->nUndoPos << bi->nTx
<< bi->nStatus << bi->nVersion << bi->hashMerkleRoot << bi->hashClaimTrie
<< bi->nTime << bi->nBits << bi->nNonce;
ibi++;
}
ibi.used(true);
}
ibi.used(true);
auto code = sqlite::commit(db);
if (code != SQLITE_OK) {
LogPrintf("%s: Error committing block info to database. SQLite error: %d\n", __func__, code);
@ -340,7 +333,7 @@ bool CBlockTreeDB::BatchWrite(const std::vector<std::pair<int, const CBlockFileI
}
// by Sync they mean disk sync:
if (sync) {
auto code = sqlite::sync(db);
code = sqlite::sync(db);
if (code != SQLITE_OK) {
LogPrintf("%s: Error syncing block database. SQLite error: %d\n", __func__, code);
return false;

View file

@ -101,7 +101,7 @@ public:
bool HaveCoin(const COutPoint &outpoint) const override;
uint256 GetBestBlock() const override;
std::vector<uint256> GetHeadBlocks() const override;
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, bool sync) override;
bool BatchWrite(const CCoinsMap &mapCoins, const uint256 &hashBlock, bool sync) override;
CCoinsViewCursor *Cursor() const override;
size_t EstimateSize() const override;
};

View file

@ -156,7 +156,7 @@ enum class FlushStateMode {
};
// See definition for documentation
static bool FlushStateToDisk(const CChainParams& chainParams, CValidationState &state, FlushStateMode mode, int nManualPruneHeight=0);
static bool FlushStateToDisk(const CChainParams& chainParams, CValidationState &state, FlushStateMode mode, int nManualPruneHeight=0, bool syncToDisk=false);
static void FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight);
static void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight);
bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks = nullptr);
@ -220,7 +220,7 @@ bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints* lp, bool
CBlockIndex* tip = chainActive.Tip();
assert(tip != nullptr);
CBlockIndex index;
CBlockIndex index((uint256()));
index.pprev = tip;
// CheckSequenceLocks() uses chainActive.Height()+1 to evaluate
// height based locks because when SequenceLocks() is called within
@ -505,14 +505,14 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
// do all inputs exist?
for (const CTxIn& txin : tx.vin) {
if (!pcoinsTip->HaveCoinInCache(txin.prevout)) {
if (!pcoinsTip->HaveCoin(txin.prevout)) {
coins_to_uncache.push_back(txin.prevout);
}
if (!view.HaveCoin(txin.prevout)) {
// Are inputs missing because we already have the tx?
for (size_t out = 0; out < tx.vout.size(); out++) {
// Optimistically just do efficient check of cache for outputs
if (pcoinsTip->HaveCoinInCache(COutPoint(hash, out))) {
if (pcoinsTip->HaveCoin(COutPoint(hash, out))) {
return state.Invalid(false, REJECT_DUPLICATE, "txn-already-known");
}
}
@ -1151,14 +1151,14 @@ static void CheckForkWarningConditions()
if (!GetfLargeWorkForkFound() && pindexBestForkBase)
{
std::string warning = std::string("'Warning: Large-work fork detected, forking after block ") +
pindexBestForkBase->phashBlock->ToString() + std::string("'");
pindexBestForkBase->hash.ToString() + std::string("'");
AlertNotify(warning);
}
if (pindexBestForkTip && pindexBestForkBase)
{
LogPrintf("%s: Warning: Large valid fork found\n forking the chain at height %d (%s)\n lasting to height %d (%s).\nChain state database corruption likely.\n", __func__,
pindexBestForkBase->nHeight, pindexBestForkBase->phashBlock->ToString(),
pindexBestForkTip->nHeight, pindexBestForkTip->phashBlock->ToString());
pindexBestForkBase->nHeight, pindexBestForkBase->hash.ToString(),
pindexBestForkTip->nHeight, pindexBestForkTip->hash.ToString());
SetfLargeWorkForkFound(true);
}
else
@ -1713,8 +1713,8 @@ static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consens
// mainnet and testnet), so for simplicity, always leave P2SH
// on except for the one violating block.
if (consensusparams.BIP16Exception.IsNull() || // no bip16 exception on this chain
pindex->phashBlock == nullptr || // this is a new candidate block, eg from TestBlockValidity()
*pindex->phashBlock != consensusparams.BIP16Exception) // this block isn't the historical exception
pindex->hash.IsNull() || // this is a new candidate block, eg from TestBlockValidity()
pindex->hash != consensusparams.BIP16Exception) // this block isn't the historical exception
{
flags |= SCRIPT_VERIFY_P2SH;
}
@ -1766,7 +1766,7 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
{
AssertLockHeld(cs_main);
assert(pindex);
assert(*pindex->phashBlock == block.GetHash());
assert(pindex->hash == block.GetHash());
int64_t nTimeStart = GetTimeMicros();
// Check it again in case a previous version let a bad block in
@ -1822,9 +1822,9 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
// relative to a piece of software is an objective fact these defaults can be easily reviewed.
// This setting doesn't force the selection of any particular chain but makes validating some faster by
// effectively caching the result of part of the verification.
BlockMap::const_iterator it = mapBlockIndex.find(hashAssumeValid);
auto it = mapBlockIndex.find(hashAssumeValid);
if (it != mapBlockIndex.end()) {
if (it->second->GetAncestor(pindex->nHeight) == pindex &&
if ((*it)->GetAncestor(pindex->nHeight) == pindex &&
pindexBestHeader->GetAncestor(pindex->nHeight) == pindex &&
pindexBestHeader->nChainWork >= nMinimumChainWork) {
// This block is a member of the assumed verified chain and an ancestor of the best header.
@ -2095,7 +2095,7 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
setDirtyBlockIndex.insert(pindex);
}
assert(pindex->phashBlock);
assert(!pindex->hash.IsNull());
// add this block to the view's block chain
view.SetBestBlock(pindex->GetBlockHash());
@ -2117,7 +2117,8 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
* If FlushStateMode::NONE is used, then FlushStateToDisk(...) won't do anything
* besides checking if we need to prune.
*/
bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &state, FlushStateMode mode, int nManualPruneHeight) {
bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &state,
FlushStateMode mode, int nManualPruneHeight, bool syncToDisk) {
int64_t nMempoolUsage = mempool.DynamicMemoryUsage();
LOCK(cs_main);
static int64_t nLastWrite = 0;
@ -2186,7 +2187,7 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &
vBlocks.push_back(*it);
setDirtyBlockIndex.erase(it++);
}
if (!pblocktree->BatchWrite(vFiles, nLastBlockFile, vBlocks, mode == FlushStateMode::ALWAYS)) {
if (!pblocktree->BatchWrite(vFiles, nLastBlockFile, vBlocks, syncToDisk)) {
return AbortNode(state, "Failed to write to block index database");
}
}
@ -2204,10 +2205,10 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &
// overwrite one. Still, use a conservative safety factor of 2.
if (!CheckDiskSpace(48 * 2 * 2 * pcoinsTip->GetCacheSize()))
return state.Error("out of disk space");
if (mode == FlushStateMode::ALWAYS && !pclaimTrie->SyncToDisk())
if (syncToDisk && !pclaimTrie->SyncToDisk())
return state.Error("Failed to write to claim trie database");
// Flush the chainstate (which may refer to block index entries).
if (!pcoinsTip->Flush(mode == FlushStateMode::ALWAYS))
if (!pcoinsTip->Flush(syncToDisk))
return AbortNode(state, "Failed to write to coin database");
nLastFlush = nNow;
full_flush_completed = true;
@ -2224,10 +2225,10 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &
return true;
}
void FlushStateToDisk() {
void FlushStateToDisk(bool diskSync) {
CValidationState state;
const CChainParams& chainparams = Params();
if (!FlushStateToDisk(chainparams, state, FlushStateMode::ALWAYS)) {
if (!FlushStateToDisk(chainparams, state, FlushStateMode::ALWAYS, 0, diskSync)) {
LogPrintf("%s: failed to flush state (%s)\n", __func__, FormatStateMessage(state));
}
}
@ -2355,7 +2356,7 @@ bool CChainState::DisconnectTip(CValidationState& state, const CChainParams& cha
}
LogPrint(BCLog::BENCH, "- Disconnect block: %.2fms\n", (GetTimeMicros() - nStart) * MILLI);
// Write the chain state to disk, if necessary.
if (!FlushStateToDisk(chainparams, state, FlushStateMode::IF_NEEDED))
if (!IsInitialBlockDownload() && !FlushStateToDisk(chainparams, state, FlushStateMode::ALWAYS))
return false;
if (disconnectpool) {
@ -2502,7 +2503,7 @@ bool CChainState::ConnectTip(CValidationState& state, const CChainParams& chainp
int64_t nTime4 = GetTimeMicros(); nTimeFlush += nTime4 - nTime3;
LogPrint(BCLog::BENCH, " - Flush: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime4 - nTime3) * MILLI, nTimeFlush * MICRO, nTimeFlush * MILLI / nBlocksTotal);
// Write the chain state to disk, if necessary.
if (!FlushStateToDisk(chainparams, state, FlushStateMode::IF_NEEDED))
if (!IsInitialBlockDownload() && !FlushStateToDisk(chainparams, state, FlushStateMode::ALWAYS))
return false;
int64_t nTime5 = GetTimeMicros(); nTimeChainState += nTime5 - nTime4;
LogPrint(BCLog::BENCH, " - Writing chainstate: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime5 - nTime4) * MILLI, nTimeChainState * MICRO, nTimeChainState * MILLI / nBlocksTotal);
@ -2775,11 +2776,11 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams&
if (!blocks_connected) return true;
const CBlockIndex* pindexFork = chainActive.FindFork(starting_tip);
bool fInitialDownload = IsInitialBlockDownload();
// Notify external listeners about the new tip.
// Enqueue while holding cs_main to ensure that UpdatedBlockTip is called in the order in which blocks are connected
if (pindexFork != pindexNewTip) {
bool fInitialDownload = IsInitialBlockDownload();
// Notify ValidationInterface subscribers
GetMainSignals().UpdatedBlockTip(pindexNewTip, pindexFork, fInitialDownload);
@ -2802,14 +2803,10 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams&
auto& consensus = chainparams.GetConsensus();
CheckBlockIndex(consensus);
auto flushMode = FlushStateMode::PERIODIC;
if (pindexNewTip && chainparams.NetworkIDString() != CBaseChainParams::REGTEST
&& pindexNewTip->nTime + consensus.nPowTargetSpacing > GetAdjustedTime()) {
// trying to ensure that we flush to disk after new blocks when we're caught up to the chain
// they're technically allowed to be two hours late, but experience says one minute is more likely
flushMode = FlushStateMode::ALWAYS;
}
return FlushStateToDisk(chainparams, state, flushMode);
auto flushMode = IsInitialBlockDownload() ? FlushStateMode::IF_NEEDED : FlushStateMode::ALWAYS;
auto diskSync = chainparams.NetworkIDString() != CBaseChainParams::REGTEST
&& flushMode == FlushStateMode::ALWAYS;
return FlushStateToDisk(chainparams, state, flushMode, 0, diskSync);
}
bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock) {
@ -2895,12 +2892,12 @@ bool CChainState::InvalidateBlock(CValidationState& state, const CChainParams& c
// The resulting new best tip may not be in setBlockIndexCandidates anymore, so
// add it again.
BlockMap::iterator it = mapBlockIndex.begin();
auto it = mapBlockIndex.begin();
while (it != mapBlockIndex.end()) {
if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->nChainTx && !setBlockIndexCandidates.value_comp()(it->second, chainActive.Tip())) {
setBlockIndexCandidates.insert(it->second);
if ((*it)->IsValid(BLOCK_VALID_TRANSACTIONS) && (*it)->nChainTx && !setBlockIndexCandidates.value_comp()(*it, chainActive.Tip())) {
setBlockIndexCandidates.insert(*it);
}
it++;
++it;
}
InvalidChainFound(pindex);
@ -2921,21 +2918,21 @@ void CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) {
int nHeight = pindex->nHeight;
// Remove the invalidity flag from this block and all its descendants.
BlockMap::iterator it = mapBlockIndex.begin();
auto it = mapBlockIndex.begin();
while (it != mapBlockIndex.end()) {
if (!it->second->IsValid() && it->second->GetAncestor(nHeight) == pindex) {
it->second->nStatus &= ~BLOCK_FAILED_MASK;
setDirtyBlockIndex.insert(it->second);
if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->nChainTx && setBlockIndexCandidates.value_comp()(chainActive.Tip(), it->second)) {
setBlockIndexCandidates.insert(it->second);
if (!(*it)->IsValid() && (*it)->GetAncestor(nHeight) == pindex) {
(*it)->nStatus &= ~BLOCK_FAILED_MASK;
setDirtyBlockIndex.insert(*it);
if ((*it)->IsValid(BLOCK_VALID_TRANSACTIONS) && (*it)->nChainTx && setBlockIndexCandidates.value_comp()(chainActive.Tip(), *it)) {
setBlockIndexCandidates.insert(*it);
}
if (it->second == pindexBestInvalid) {
if (*it == pindexBestInvalid) {
// Reset invalid block marker if it was pointing to one of those.
pindexBestInvalid = nullptr;
}
m_failed_blocks.erase(it->second);
m_failed_blocks.erase(*it);
}
it++;
++it;
}
// Remove the invalidity flag from all ancestors too.
@ -2959,9 +2956,9 @@ CBlockIndex* CChainState::AddToBlockIndex(const CBlockHeader& block)
// Check for duplicate
uint256 hash = block.GetHash();
BlockMap::iterator it = mapBlockIndex.find(hash);
auto it = mapBlockIndex.find(hash);
if (it != mapBlockIndex.end())
return it->second;
return *it;
// Construct new block index object
CBlockIndex* pindexNew = new CBlockIndex(block);
@ -2969,12 +2966,11 @@ CBlockIndex* CChainState::AddToBlockIndex(const CBlockHeader& block)
// to avoid miners withholding blocks but broadcasting headers, to get a
// competitive advantage.
pindexNew->nSequenceId = 0;
BlockMap::iterator mi = mapBlockIndex.insert(std::make_pair(hash, pindexNew)).first;
pindexNew->phashBlock = &((*mi).first);
mapBlockIndex.insert(pindexNew);
BlockMap::iterator miPrev = mapBlockIndex.find(block.hashPrevBlock);
if (miPrev != mapBlockIndex.end())
{
pindexNew->pprev = (*miPrev).second;
pindexNew->pprev = *miPrev;
pindexNew->nHeight = pindexNew->pprev->nHeight + 1;
pindexNew->BuildSkip();
}
@ -3404,12 +3400,12 @@ bool CChainState::AcceptBlockHeader(const CBlockHeader& block, CValidationState&
AssertLockHeld(cs_main);
// Check for duplicate
uint256 hash = block.GetHash();
BlockMap::iterator miSelf = mapBlockIndex.find(hash);
auto miSelf = mapBlockIndex.find(hash);
CBlockIndex *pindex = nullptr;
if (hash != chainparams.GetConsensus().hashGenesisBlock) {
if (miSelf != mapBlockIndex.end()) {
// Block header is already known.
pindex = miSelf->second;
pindex = *miSelf;
if (ppindex)
*ppindex = pindex;
if (pindex->nStatus & BLOCK_FAILED_MASK)
@ -3422,10 +3418,10 @@ bool CChainState::AcceptBlockHeader(const CBlockHeader& block, CValidationState&
// Get prev block index
CBlockIndex* pindexPrev = nullptr;
BlockMap::iterator mi = mapBlockIndex.find(block.hashPrevBlock);
auto mi = mapBlockIndex.find(block.hashPrevBlock);
if (mi == mapBlockIndex.end())
return state.DoS(10, error("%s: prev block not found", __func__), 0, "prev-blk-not-found");
pindexPrev = (*mi).second;
pindexPrev = *mi;
if (pindexPrev->nStatus & BLOCK_FAILED_MASK)
return state.DoS(100, error("%s: prev block invalid", __func__), REJECT_INVALID, "bad-prevblk");
if (!ContextualCheckBlockHeader(block, state, chainparams, pindexPrev, GetAdjustedTime()))
@ -3620,11 +3616,9 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams,
assert(pindexPrev && pindexPrev == chainActive.Tip());
CCoinsViewCache viewNew(pcoinsTip.get());
CClaimTrieCache trieCache(pclaimTrie);
uint256 block_hash(block.GetHash());
CBlockIndex indexDummy(block);
indexDummy.pprev = pindexPrev;
indexDummy.nHeight = pindexPrev->nHeight + 1;
indexDummy.phashBlock = &block_hash;
// NOTE: CheckBlockHeader is called by CheckBlock
if (!ContextualCheckBlockHeader(block, state, chainparams, pindexPrev, GetAdjustedTime()))
@ -3662,7 +3656,7 @@ void PruneOneBlockFile(const int fileNumber)
LOCK(cs_LastBlockFile);
for (const auto& entry : g_chainstate.mapBlockIndex) {
CBlockIndex* pindex = entry.second;
CBlockIndex* pindex = entry;
if (pindex->nFile == fileNumber) {
pindex->nStatus &= ~BLOCK_HAVE_DATA;
pindex->nStatus &= ~BLOCK_HAVE_UNDO;
@ -3859,15 +3853,14 @@ CBlockIndex * CChainState::InsertBlockIndex(const uint256& hash)
// Return existing
auto mi = mapBlockIndex.find(hash);
if (mi != mapBlockIndex.end())
return mi->second;
return *mi;
if (hash.IsNull())
return nullptr;
// Create new
CBlockIndex* pindexNew = new CBlockIndex();
mi = mapBlockIndex.insert(std::make_pair(hash, pindexNew)).first;
pindexNew->phashBlock = &(mi->first);
CBlockIndex* pindexNew = new CBlockIndex(hash);
mapBlockIndex.insert(pindexNew);
return pindexNew;
}
@ -3882,9 +3875,8 @@ bool CChainState::LoadBlockIndex(const Consensus::Params& consensus_params, CBlo
// Calculate nChainWork
std::vector<std::pair<int, CBlockIndex*> > vSortedByHeight;
vSortedByHeight.reserve(mapBlockIndex.size());
for (const std::pair<const uint256, CBlockIndex*>& item : mapBlockIndex)
for (const auto& pindex : mapBlockIndex)
{
CBlockIndex* pindex = item.second;
vSortedByHeight.push_back(std::make_pair(pindex->nHeight, pindex));
}
sort(vSortedByHeight.begin(), vSortedByHeight.end());
@ -3949,9 +3941,8 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams) EXCLUSIVE_LOCKS_RE
// Check presence of blk files
LogPrintf("Checking all blk files are present...\n");
std::set<int> setBlkDataFiles;
for (const std::pair<const uint256, CBlockIndex*>& item : g_chainstate.mapBlockIndex)
for (const auto& pindex : g_chainstate.mapBlockIndex)
{
CBlockIndex* pindex = item.second;
if (pindex->nStatus & BLOCK_HAVE_DATA) {
setBlkDataFiles.insert(pindex->nFile);
}
@ -4138,10 +4129,7 @@ bool CChainState::RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& i
bool CChainState::ReplayBlocks(const CChainParams& params, CCoinsView* view)
{
LOCK(cs_main);
CCoinsViewCache cache(view);
CClaimTrieCache trieCache(pclaimTrie);
AssertLockHeld(cs_main);
std::vector<uint256> hashHeads = view->GetHeadBlocks();
if (hashHeads.empty()) return true; // We're already in a consistent state.
@ -4154,20 +4142,25 @@ bool CChainState::ReplayBlocks(const CChainParams& params, CCoinsView* view)
const CBlockIndex* pindexNew; // New tip during the interrupted flush.
const CBlockIndex* pindexFork = nullptr; // Latest block common to both the old and the new tip.
if (mapBlockIndex.count(hashHeads[0]) == 0) {
auto it = mapBlockIndex.find(hashHeads[0]);
if (it == mapBlockIndex.end()) {
return error("ReplayBlocks(): reorganization to unknown block requested");
}
pindexNew = mapBlockIndex[hashHeads[0]];
pindexNew = *it;
if (!hashHeads[1].IsNull()) { // The old tip is allowed to be 0, indicating it's the first flush.
if (mapBlockIndex.count(hashHeads[1]) == 0) {
it = mapBlockIndex.find(hashHeads[1]);
if (it == mapBlockIndex.end()) {
return error("ReplayBlocks(): reorganization from unknown block requested");
}
pindexOld = mapBlockIndex[hashHeads[1]];
pindexOld = *it;
pindexFork = LastCommonAncestor(pindexOld, pindexNew);
assert(pindexFork != nullptr);
}
CCoinsViewCache cache(view);
CClaimTrieCache trieCache(pclaimTrie);
// Rollback along the old branch.
while (pindexOld != pindexFork) {
if (pindexOld->nHeight > 0) { // Never disconnect the genesis block.
@ -4249,8 +4242,7 @@ bool CChainState::RewindBlockIndex(const CChainParams& params)
// Reduce validity flag and have-data flags.
// We do this after actual disconnecting, otherwise we'll end up writing the lack of data
// to disk before writing the chainstate, resulting in a failure to continue if interrupted.
for (const auto& entry : mapBlockIndex) {
CBlockIndex* pindexIter = entry.second;
for (const auto& pindexIter : mapBlockIndex) {
// Note: If we encounter an insufficiently validated block that
// is on chainActive, it must be because we are a pruning node, and
@ -4343,8 +4335,8 @@ void UnloadBlockIndex()
warningcache[b].clear();
}
for (BlockMap::value_type& entry : g_chainstate.mapBlockIndex) {
delete entry.second;
for (auto& entry : g_chainstate.mapBlockIndex) {
delete entry;
}
g_chainstate.mapBlockIndex.clear();
fHavePruned = false;
@ -4392,7 +4384,7 @@ bool CChainState::LoadGenesisBlock(const CChainParams& chainparams)
// mapBlockIndex. Note that we can't use chainActive here, since it is
// set based on the coins db, not the block index db, which is the only
// thing loaded at this point.
if (mapBlockIndex.count(chainparams.GenesisBlock().GetHash()))
if (mapBlockIndex.find(chainparams.GenesisBlock().GetHash()) != mapBlockIndex.end())
return true;
try {
@ -4555,7 +4547,7 @@ void CChainState::CheckBlockIndex(const Consensus::Params& consensusParams)
// Build forward-pointing map of the entire block tree.
std::multimap<CBlockIndex*,CBlockIndex*> forward;
for (auto& entry : mapBlockIndex) {
forward.insert(std::make_pair(entry.second->pprev, entry.second));
forward.insert(std::make_pair(entry->pprev, entry));
}
assert(forward.size() == mapBlockIndex.size());
@ -4901,7 +4893,7 @@ double GuessVerificationProgress(const ChainTxData& data, const CBlockIndex *pin
CBlockIndex *LookupBlockIndex(const uint256 &hash) {
AssertLockHeld(cs_main);
auto it = g_chainstate.mapBlockIndex.find(hash);
return it == g_chainstate.mapBlockIndex.end() ? nullptr : it->second;
return it == g_chainstate.mapBlockIndex.end() ? nullptr : *it;
}
class CMainCleanup
@ -4912,7 +4904,7 @@ public:
// block headers
auto it1 = g_chainstate.mapBlockIndex.begin();
for (; it1 != g_chainstate.mapBlockIndex.end(); ++it1)
delete (*it1).second;
delete *it1;
g_chainstate.mapBlockIndex.clear();
}
} instance_of_cmaincleanup;

View file

@ -144,7 +144,25 @@ extern CCriticalSection cs_main;
extern CBlockPolicyEstimator feeEstimator;
extern CTxMemPool mempool;
extern std::atomic_bool g_is_mempool_loaded;
typedef std::map<uint256, CBlockIndex*> BlockMap;
struct BlockIndexPointerCompare {
inline bool operator() (const CBlockIndex* lhs, const CBlockIndex* rhs) const {
return lhs->hash < rhs->hash;
}
};
struct BlockMap : public std::set<CBlockIndex*, BlockIndexPointerCompare> {
inline iterator find(const uint256& blockHash) {
CBlockIndex temp(blockHash);
return std::set<CBlockIndex*, BlockIndexPointerCompare>::find(&temp);
}
inline const_iterator find(const uint256& blockHash) const {
CBlockIndex temp(blockHash);
return std::set<CBlockIndex*, BlockIndexPointerCompare>::find(&temp);
}
};
extern uint64_t nLastBlockTx;
extern uint64_t nLastBlockWeight;
extern const std::string strMessageMagic;
@ -291,7 +309,7 @@ void PruneOneBlockFile(const int fileNumber);
void UnlinkPrunedFiles(const std::set<int>& setFilesToPrune);
/** Flush all state, indexes and buffers to disk. */
void FlushStateToDisk();
void FlushStateToDisk(bool diskSync = false);
/** Prune block files and flush state to disk. */
void PruneAndFlush();
/** Prune block files up to a given height */

View file

@ -3995,7 +3995,8 @@ static UniValue listunspent(const JSONRPCRequest& request)
std::vector<COutput> vecOutputs;
{
LOCK2(cs_main, pwallet->cs_wallet);
pwallet->AvailableCoins(vecOutputs, !include_unsafe, nullptr, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount, nMinDepth, nMaxDepth);
pwallet->AvailableCoins(vecOutputs, !include_unsafe, nullptr,
nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount, nMinDepth, nMaxDepth, true);
}
LOCK(pwallet->cs_wallet);

View file

@ -207,12 +207,9 @@ static int64_t AddTx(CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64
CBlockIndex* block = nullptr;
if (blockTime > 0) {
LOCK(cs_main);
auto inserted = g_chainstate.mapBlockIndex.emplace(GetRandHash(), new CBlockIndex);
assert(inserted.second);
const uint256& hash = inserted.first->first;
block = inserted.first->second;
auto inserted = g_chainstate.mapBlockIndex.insert(new CBlockIndex(GetRandHash()));
block = *inserted.first;
block->nTime = blockTime;
block->phashBlock = &hash;
}
CWalletTx wtx(&wallet, MakeTransactionRef(tx));

View file

@ -1030,7 +1030,8 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
}
//// debug print
WalletLogPrintf("AddToWallet %s %s%s\n", wtxIn.GetHash().ToString(), (fInsertedNew ? "new" : ""), (fUpdated ? "update" : ""));
if (LogAcceptCategory(BCLog::DB))
WalletLogPrintf("AddToWallet %s%s%s\n", wtxIn.GetHash().ToString(), (fInsertedNew ? " new" : ""), (fUpdated ? " update" : ""));
// Write to disk
if (fInsertedNew || fUpdated)
@ -1876,7 +1877,8 @@ bool CWalletTx::RelayWalletTransaction(CConnman* connman)
CValidationState state;
/* GetDepthInMainChain already catches known conflicts. */
if (InMempool() || AcceptToMemoryPool(maxTxFee, state)) {
pwallet->WalletLogPrintf("Relaying wtx %s\n", GetHash().ToString());
if (LogAcceptCategory(BCLog::DB))
pwallet->WalletLogPrintf("Relaying wtx %s\n", GetHash().ToString());
if (connman) {
CInv inv(MSG_TX, GetHash());
connman->ForEachNode([&inv](CNode* pnode)
@ -2285,7 +2287,9 @@ CAmount CWallet::GetAvailableBalance(const CCoinControl* coinControl) const
return balance;
}
void CWallet::AvailableCoins(std::vector<COutput> &vCoins, bool fOnlySafe, const CCoinControl *coinControl, const CAmount &nMinimumAmount, const CAmount &nMaximumAmount, const CAmount &nMinimumSumAmount, const uint64_t nMaximumCount, const int nMinDepth, const int nMaxDepth) const
void CWallet::AvailableCoins(std::vector<COutput> &vCoins, bool fOnlySafe, const CCoinControl *coinControl,
const CAmount &nMinimumAmount, const CAmount &nMaximumAmount, const CAmount &nMinimumSumAmount,
const uint64_t nMaximumCount, const int nMinDepth, const int nMaxDepth, bool computeSolvable) const
{
AssertLockHeld(cs_main);
AssertLockHeld(cs_wallet);
@ -2372,17 +2376,23 @@ void CWallet::AvailableCoins(std::vector<COutput> &vCoins, bool fOnlySafe, const
continue;
}
bool solvable = IsSolvable(*this, pcoin->tx->vout[i].scriptPubKey);
bool spendable = (mine & ISMINE_SPENDABLE) || (((mine & ISMINE_WATCH_ONLY) != ISMINE_NO) && (coinControl && coinControl->fAllowWatchOnly && solvable));
// spending claims or supports requires specific selection:
auto isClaimCoin = (mine & ISMINE_CLAIM) || (mine & ISMINE_SUPPORT);
auto isClaimCoin = bool(mine & ISMINE_STAKE);
auto claimSpendRequested = isClaimCoin && coinControl && coinControl->IsSelected(COutPoint(entry.first, i));
if (spendable && isClaimCoin && !claimSpendRequested)
if (isClaimCoin && !claimSpendRequested)
continue;
vCoins.push_back(COutput(pcoin, i, nDepth, spendable, solvable, safeTx, (coinControl && coinControl->fAllowWatchOnly)));
bool solvable = false, computedSolvable = false;
bool spendable = bool(mine & ISMINE_SPENDABLE);
if (!spendable && bool(mine & ISMINE_WATCH_ONLY) && coinControl && coinControl->fAllowWatchOnly) {
solvable = IsSolvable(*this, pcoin->tx->vout[i].scriptPubKey); // this is a slow call
spendable = solvable;
computedSolvable = true;
};
if (computeSolvable && !computedSolvable)
solvable = IsSolvable(*this, pcoin->tx->vout[i].scriptPubKey);
vCoins.emplace_back(pcoin, i, nDepth, spendable, solvable, safeTx, (coinControl && coinControl->fAllowWatchOnly));
// Checks the sum amount of all UTXO's.
if (nMinimumSumAmount != MAX_MONEY) {
@ -3494,7 +3504,8 @@ bool CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRe
}
m_pool_key_to_index.erase(keypool.vchPubKey.GetID());
WalletLogPrintf("keypool reserve %d\n", nIndex);
if (LogAcceptCategory(BCLog::DB))
WalletLogPrintf("keypool reserve %d\n", nIndex);
}
return true;
}
@ -3504,7 +3515,8 @@ void CWallet::KeepKey(int64_t nIndex)
// Remove from key pool
WalletBatch batch(*database);
batch.ErasePool(nIndex);
WalletLogPrintf("keypool keep %d\n", nIndex);
if (LogAcceptCategory(BCLog::DB))
WalletLogPrintf("keypool keep %d\n", nIndex);
}
void CWallet::ReturnKey(int64_t nIndex, bool fInternal, const CPubKey& pubkey)

View file

@ -508,8 +508,8 @@ public:
int i;
int nDepth;
/** Pre-computed estimated size of this output as a fully-signed input in a transaction. Can be -1 if it could not be calculated */
int nInputBytes;
/** estimated size of this output as a fully-signed input in a transaction. Can be -1 */
mutable int nInputBytes;
/** Whether we have the private keys to spend this output */
bool fSpendable;
@ -527,20 +527,22 @@ public:
*/
bool fSafe;
COutput(const CWalletTx *txIn, int iIn, int nDepthIn, bool fSpendableIn, bool fSolvableIn, bool fSafeIn, bool use_max_sig_in = false)
COutput(const CWalletTx *txIn, int iIn, int nDepthIn, bool fSpendableIn,
bool fSolvableIn, bool fSafeIn, bool use_max_sig_in = false)
: tx(txIn), i(iIn), nDepth(nDepthIn), fSpendable(fSpendableIn), fSolvable(fSolvableIn),
fSafe(fSafeIn), nInputBytes(-1), use_max_sig(use_max_sig_in)
{
tx = txIn; i = iIn; nDepth = nDepthIn; fSpendable = fSpendableIn; fSolvable = fSolvableIn; fSafe = fSafeIn; nInputBytes = -1; use_max_sig = use_max_sig_in;
// If known and signable by the given wallet, compute nInputBytes
// Failure will keep this value -1
if (fSpendable && tx) {
nInputBytes = tx->GetSpendSize(i, use_max_sig);
}
}
std::string ToString() const;
inline CInputCoin GetInputCoin() const
{
// If known and signable by the given wallet, compute nInputBytes
// Failure will keep this value -1
if (nInputBytes < 0 && fSpendable && tx) {
nInputBytes = tx->GetSpendSize(i, use_max_sig);
}
return CInputCoin(tx->tx, i, nInputBytes);
}
};
@ -850,7 +852,7 @@ public:
/**
* populate vCoins with vector of available COutputs.
*/
void AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe=true, const CCoinControl *coinControl = nullptr, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0, const int nMinDepth = 0, const int nMaxDepth = 9999999) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe=true, const CCoinControl *coinControl = nullptr, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0, const int nMinDepth = 0, const int nMaxDepth = 9999999, bool computeSolvable=false) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/**
* Return list of available coins and locked coins grouped by non-change output address.

View file

@ -70,6 +70,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
))['hex']
txid_in_block = node.sendrawtransaction(hexstring=raw_tx_in_block, allowhighfees=True)
node.generate(1)
self.mempool_size = 0
self.check_mempool_result(
result_expected=[{'txid': txid_in_block, 'allowed': False, 'reject-reason': '18: txn-already-known'}],
rawtxs=[raw_tx_in_block],