Page MenuHomePhabricator

No OneTemporary

diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp
index cd60bbea2..f03c33ee1 100644
--- a/src/test/test_bitcoin.cpp
+++ b/src/test/test_bitcoin.cpp
@@ -1,291 +1,291 @@
// Copyright (c) 2011-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <test/test_bitcoin.h>
#include <chain.h>
#include <chainparams.h>
#include <config.h>
#include <consensus/consensus.h>
#include <consensus/validation.h>
#include <crypto/sha256.h>
#include <fs.h>
#include <key.h>
#include <logging.h>
#include <miner.h>
#include <net_processing.h>
#include <pubkey.h>
#include <random.h>
#include <rpc/register.h>
#include <rpc/server.h>
#include <script/scriptcache.h>
#include <script/sigcache.h>
#include <txdb.h>
#include <txmempool.h>
#include <ui_interface.h>
#include <validation.h>
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <cstdio>
#include <functional>
#include <list>
#include <memory>
#include <thread>
void CConnmanTest::AddNode(CNode &node) {
LOCK(g_connman->cs_vNodes);
g_connman->vNodes.push_back(&node);
}
void CConnmanTest::ClearNodes() {
LOCK(g_connman->cs_vNodes);
g_connman->vNodes.clear();
}
uint256 insecure_rand_seed = GetRandHash();
FastRandomContext insecure_rand_ctx(insecure_rand_seed);
extern void noui_connect();
BasicTestingSetup::BasicTestingSetup(const std::string &chainName) {
SHA256AutoDetect();
RandomInit();
ECC_Start();
SetupEnvironment();
SetupNetworking();
InitSignatureCache();
InitScriptExecutionCache();
// Don't want to write to debug.log file.
GetLogger().m_print_to_file = false;
fCheckBlockIndex = true;
SelectParams(chainName);
noui_connect();
// Set config parameters to default.
GlobalConfig config;
config.SetMaxBlockSize(DEFAULT_MAX_BLOCK_SIZE);
}
BasicTestingSetup::~BasicTestingSetup() {
ECC_Stop();
}
TestingSetup::TestingSetup(const std::string &chainName)
: BasicTestingSetup(chainName) {
const Config &config = GetConfig();
const CChainParams &chainparams = config.GetChainParams();
// Ideally we'd move all the RPC tests to the functional testing framework
// instead of unit tests, but for now we need these here.
RPCServer rpcServer;
RegisterAllRPCCommands(config, rpcServer, tableRPC);
/**
* RPC does not come out of the warmup state on its own. Normally, this is
* handled in bitcoind's init path, but unit tests do not trigger this
* codepath, so we call it explicitly as part of setup.
*/
std::string rpcWarmupStatus;
if (RPCIsInWarmup(&rpcWarmupStatus)) {
SetRPCWarmupFinished();
}
ClearDatadirCache();
pathTemp = fs::temp_directory_path() /
strprintf("test_bitcoin_%lu_%i", (unsigned long)GetTime(),
(int)(InsecureRandRange(100000)));
fs::create_directories(pathTemp);
gArgs.ForceSetArg("-datadir", pathTemp.string());
// We have to run a scheduler thread to prevent ActivateBestChain
// from blocking due to queue overrun.
threadGroup.create_thread(
boost::bind(&CScheduler::serviceQueue, &scheduler));
GetMainSignals().RegisterBackgroundSignalScheduler(scheduler);
g_mempool.setSanityCheck(1.0);
pblocktree.reset(new CBlockTreeDB(1 << 20, true));
pcoinsdbview.reset(new CCoinsViewDB(1 << 23, true));
pcoinsTip.reset(new CCoinsViewCache(pcoinsdbview.get()));
if (!LoadGenesisBlock(chainparams)) {
throw std::runtime_error("LoadGenesisBlock failed.");
}
{
CValidationState state;
if (!ActivateBestChain(config, state)) {
throw std::runtime_error("ActivateBestChain failed.");
}
}
nScriptCheckThreads = 3;
for (int i = 0; i < nScriptCheckThreads - 1; i++) {
threadGroup.create_thread(&ThreadScriptCheck);
}
// Deterministic randomness for tests.
g_connman = std::unique_ptr<CConnman>(new CConnman(config, 0x1337, 0x1337));
connman = g_connman.get();
peerLogic.reset(new PeerLogicValidation(connman, scheduler));
}
TestingSetup::~TestingSetup() {
threadGroup.interrupt_all();
threadGroup.join_all();
GetMainSignals().FlushBackgroundCallbacks();
GetMainSignals().UnregisterBackgroundSignalScheduler();
g_connman.reset();
peerLogic.reset();
UnloadBlockIndex();
pcoinsTip.reset();
pcoinsdbview.reset();
pblocktree.reset();
fs::remove_all(pathTemp);
}
TestChain100Setup::TestChain100Setup()
: TestingSetup(CBaseChainParams::REGTEST) {
// Generate a 100-block chain:
coinbaseKey.MakeNewKey(true);
CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey())
<< OP_CHECKSIG;
for (int i = 0; i < COINBASE_MATURITY; i++) {
std::vector<CMutableTransaction> noTxns;
CBlock b = CreateAndProcessBlock(noTxns, scriptPubKey);
- coinbaseTxns.push_back(*b.vtx[0]);
+ m_coinbase_txns.push_back(*b.vtx[0]);
}
}
//
// Create a new block with just given transactions, coinbase paying to
// scriptPubKey, and try to add it to the current chain.
//
CBlock TestChain100Setup::CreateAndProcessBlock(
const std::vector<CMutableTransaction> &txns, const CScript &scriptPubKey) {
const Config &config = GetConfig();
std::unique_ptr<CBlockTemplate> pblocktemplate =
BlockAssembler(config, g_mempool).CreateNewBlock(scriptPubKey);
CBlock &block = pblocktemplate->block;
// Replace mempool-selected txns with just coinbase plus passed-in txns:
block.vtx.resize(1);
for (const CMutableTransaction &tx : txns) {
block.vtx.push_back(MakeTransactionRef(tx));
}
// Order transactions by canonical order
std::sort(std::begin(block.vtx) + 1, std::end(block.vtx),
[](const std::shared_ptr<const CTransaction> &txa,
const std::shared_ptr<const CTransaction> &txb) -> bool {
return txa->GetId() < txb->GetId();
});
// IncrementExtraNonce creates a valid coinbase and merkleRoot
unsigned int extraNonce = 0;
{
LOCK(cs_main);
IncrementExtraNonce(config, &block, chainActive.Tip(), extraNonce);
}
while (!CheckProofOfWork(block.GetHash(), block.nBits, config)) {
++block.nNonce;
}
std::shared_ptr<const CBlock> shared_pblock =
std::make_shared<const CBlock>(block);
ProcessNewBlock(GetConfig(), shared_pblock, true, nullptr);
CBlock result = block;
return result;
}
TestChain100Setup::~TestChain100Setup() {}
CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CMutableTransaction &tx,
CTxMemPool *pool) {
CTransaction txn(tx);
return FromTx(txn, pool);
}
CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CTransaction &txn,
CTxMemPool *pool) {
// Hack to assume either it's completely dependent on other mempool txs or
// not at all.
Amount inChainValue =
pool && pool->HasNoInputsOf(txn) ? txn.GetValueOut() : Amount::zero();
return CTxMemPoolEntry(MakeTransactionRef(txn), nFee, nTime, dPriority,
nHeight, inChainValue, spendsCoinbase, sigOpCost,
lp);
}
namespace {
// A place to put misc. setup code eg "the travis workaround" that needs to run
// at program startup and exit
struct Init {
Init();
~Init();
std::list<std::function<void(void)>> cleanup;
};
Init init;
Init::Init() {
if (getenv("TRAVIS_NOHANG_WORKAROUND")) {
// This is a workaround for MinGW/Win32 builds on Travis sometimes
// hanging due to no output received by Travis after a 10-minute
// timeout.
// The strategy here is to let the jobs finish however long they take
// on Travis, by feeding Travis output. We start a parallel thread
// that just prints out '.' once per second.
struct Private {
Private() : stop(false) {}
std::atomic_bool stop;
std::thread thr;
std::condition_variable cond;
std::mutex mut;
} *p = new Private;
p->thr = std::thread([p] {
// thread func.. print dots
std::unique_lock<std::mutex> lock(p->mut);
unsigned ctr = 0;
while (!p->stop) {
if (ctr) {
// skip first period to allow app to print first
std::cerr << "." << std::flush;
}
if (!(++ctr % 79)) {
// newline once in a while to keep travis happy
std::cerr << std::endl;
}
p->cond.wait_for(lock, std::chrono::milliseconds(1000));
}
});
cleanup.emplace_back([p]() {
// cleanup function to kill the thread and delete the struct
p->mut.lock();
p->stop = true;
p->cond.notify_all();
p->mut.unlock();
if (p->thr.joinable()) {
p->thr.join();
}
delete p;
});
}
}
Init::~Init() {
for (auto &f : cleanup) {
if (f) {
f();
}
}
}
} // end anonymous namespace
diff --git a/src/test/test_bitcoin.h b/src/test/test_bitcoin.h
index 65517be7e..4e358e9ce 100644
--- a/src/test/test_bitcoin.h
+++ b/src/test/test_bitcoin.h
@@ -1,159 +1,159 @@
// Copyright (c) 2015-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_TEST_TEST_BITCOIN_H
#define BITCOIN_TEST_TEST_BITCOIN_H
#include <chainparamsbase.h>
#include <fs.h>
#include <key.h>
#include <pubkey.h>
#include <random.h>
#include <scheduler.h>
#include <txmempool.h>
/**
* Version of Boost::test prior to 1.64 have issues when dealing with nullptr_t.
* In order to work around this, we ensure that the null pointers are typed in a
* way that Boost will like better.
*
* TODO: Use nullptr directly once the minimum version of boost is 1.64 or more.
*/
#define NULLPTR(T) static_cast<T *>(nullptr)
extern uint256 insecure_rand_seed;
extern FastRandomContext insecure_rand_ctx;
static inline void SeedInsecureRand(bool fDeterministic = false) {
if (fDeterministic) {
insecure_rand_seed = uint256();
} else {
insecure_rand_seed = GetRandHash();
}
insecure_rand_ctx = FastRandomContext(insecure_rand_seed);
}
static inline uint32_t insecure_rand() {
return insecure_rand_ctx.rand32();
}
static inline uint256 InsecureRand256() {
return insecure_rand_ctx.rand256();
}
static inline uint64_t InsecureRandBits(int bits) {
return insecure_rand_ctx.randbits(bits);
}
static inline uint64_t InsecureRandRange(uint64_t range) {
return insecure_rand_ctx.randrange(range);
}
static inline bool InsecureRandBool() {
return insecure_rand_ctx.randbool();
}
/**
* Basic testing setup.
* This just configures logging and chain parameters.
*/
struct BasicTestingSetup {
ECCVerifyHandle globalVerifyHandle;
explicit BasicTestingSetup(
const std::string &chainName = CBaseChainParams::MAIN);
~BasicTestingSetup();
};
/** Testing setup that configures a complete environment.
* Included are data directory, coins database, script check threads setup.
*/
class CConnman;
class CNode;
struct CConnmanTest {
static void AddNode(CNode &node);
static void ClearNodes();
};
class PeerLogicValidation;
struct TestingSetup : public BasicTestingSetup {
fs::path pathTemp;
boost::thread_group threadGroup;
CConnman *connman;
CScheduler scheduler;
std::unique_ptr<PeerLogicValidation> peerLogic;
explicit TestingSetup(
const std::string &chainName = CBaseChainParams::MAIN);
~TestingSetup();
};
class CBlock;
class CMutableTransaction;
class CScript;
//
// Testing fixture that pre-creates a
// 100-block REGTEST-mode block chain
//
struct TestChain100Setup : public TestingSetup {
TestChain100Setup();
// Create a new block with just given transactions, coinbase paying to
// scriptPubKey, and try to add it to the current chain.
CBlock CreateAndProcessBlock(const std::vector<CMutableTransaction> &txns,
const CScript &scriptPubKey);
~TestChain100Setup();
// For convenience, coinbase transactions.
- std::vector<CTransaction> coinbaseTxns;
+ std::vector<CTransaction> m_coinbase_txns;
// private/public key needed to spend coinbase transactions.
CKey coinbaseKey;
};
class CTxMemPoolEntry;
class CTxMemPool;
struct TestMemPoolEntryHelper {
// Default values
Amount nFee;
int64_t nTime;
double dPriority;
unsigned int nHeight;
bool spendsCoinbase;
unsigned int sigOpCost;
LockPoints lp;
TestMemPoolEntryHelper()
: nFee(), nTime(0), dPriority(0.0), nHeight(1), spendsCoinbase(false),
sigOpCost(4) {}
CTxMemPoolEntry FromTx(const CMutableTransaction &tx,
CTxMemPool *pool = nullptr);
CTxMemPoolEntry FromTx(const CTransaction &tx, CTxMemPool *pool = nullptr);
// Change the default value
TestMemPoolEntryHelper &Fee(Amount _fee) {
nFee = _fee;
return *this;
}
TestMemPoolEntryHelper &Time(int64_t _time) {
nTime = _time;
return *this;
}
TestMemPoolEntryHelper &Priority(double _priority) {
dPriority = _priority;
return *this;
}
TestMemPoolEntryHelper &Height(unsigned int _height) {
nHeight = _height;
return *this;
}
TestMemPoolEntryHelper &SpendsCoinbase(bool _flag) {
spendsCoinbase = _flag;
return *this;
}
TestMemPoolEntryHelper &SigOpsCost(unsigned int _sigopsCost) {
sigOpCost = _sigopsCost;
return *this;
}
};
#endif
diff --git a/src/test/txindex_tests.cpp b/src/test/txindex_tests.cpp
index c0594674e..270838a10 100644
--- a/src/test/txindex_tests.cpp
+++ b/src/test/txindex_tests.cpp
@@ -1,76 +1,76 @@
// Copyright (c) 2017-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <index/txindex.h>
#include <script/standard.h>
#include <test/test_bitcoin.h>
#include <util.h>
#include <utiltime.h>
#include <validation.h>
#include <boost/test/unit_test.hpp>
BOOST_AUTO_TEST_SUITE(txindex_tests)
BOOST_FIXTURE_TEST_CASE(txindex_initial_sync, TestChain100Setup) {
TxIndex txindex(1 << 20, true);
CTransactionRef tx_disk;
uint256 block_hash;
// Transaction should not be found in the index before it is started.
- for (const auto &txn : coinbaseTxns) {
+ for (const auto &txn : m_coinbase_txns) {
BOOST_CHECK(!txindex.FindTx(txn.GetHash(), block_hash, tx_disk));
}
// BlockUntilSyncedToCurrentChain should return false before txindex is
// started.
BOOST_CHECK(!txindex.BlockUntilSyncedToCurrentChain());
txindex.Start();
// Allow tx index to catch up with the block index.
constexpr int64_t timeout_ms = 10 * 1000;
int64_t time_start = GetTimeMillis();
while (!txindex.BlockUntilSyncedToCurrentChain()) {
BOOST_REQUIRE(time_start + timeout_ms > GetTimeMillis());
MilliSleep(100);
}
// Check that txindex has all txs that were in the chain before it started.
- for (const auto &txn : coinbaseTxns) {
+ for (const auto &txn : m_coinbase_txns) {
if (!txindex.FindTx(txn.GetHash(), block_hash, tx_disk)) {
BOOST_ERROR("FindTx failed");
} else if (tx_disk->GetHash() != txn.GetHash()) {
BOOST_ERROR("Read incorrect tx");
}
}
// Check that new transactions in new blocks make it into the index.
for (int i = 0; i < 10; i++) {
CScript coinbase_script_pub_key =
GetScriptForDestination(coinbaseKey.GetPubKey().GetID());
std::vector<CMutableTransaction> no_txns;
const CBlock &block =
CreateAndProcessBlock(no_txns, coinbase_script_pub_key);
const CTransaction &txn = *block.vtx[0];
BOOST_CHECK(txindex.BlockUntilSyncedToCurrentChain());
if (!txindex.FindTx(txn.GetHash(), block_hash, tx_disk)) {
BOOST_ERROR("FindTx failed");
} else if (tx_disk->GetHash() != txn.GetHash()) {
BOOST_ERROR("Read incorrect tx");
}
}
// shutdown sequence (c.f. Shutdown() in init.cpp)
txindex.Stop();
threadGroup.interrupt_all();
threadGroup.join_all();
// Rest of shutdown sequence and destructors happen in ~TestingSetup()
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp
index aa65e1556..0f017a84f 100644
--- a/src/test/txvalidationcache_tests.cpp
+++ b/src/test/txvalidationcache_tests.cpp
@@ -1,439 +1,439 @@
// Copyright (c) 2011-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <chain.h>
#include <config.h>
#include <consensus/validation.h>
#include <key.h>
#include <keystore.h>
#include <miner.h>
#include <pubkey.h>
#include <random.h>
#include <script/scriptcache.h>
#include <script/sighashtype.h>
#include <script/sign.h>
#include <script/standard.h>
#include <txmempool.h>
#include <utiltime.h>
#include <validation.h>
#include <test/sigutil.h>
#include <test/test_bitcoin.h>
#include <boost/test/unit_test.hpp>
BOOST_AUTO_TEST_SUITE(txvalidationcache_tests)
static bool ToMemPool(const CMutableTransaction &tx) {
LOCK(cs_main);
CValidationState state;
return AcceptToMemoryPool(GetConfig(), g_mempool, state,
MakeTransactionRef(tx), false, nullptr, true,
Amount::zero());
}
BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup) {
// Make sure skipping validation of transctions that were validated going
// into the memory pool does not allow double-spends in blocks to pass
// validation when they should not.
CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey())
<< OP_CHECKSIG;
// Create a double-spend of mature coinbase txn:
std::vector<CMutableTransaction> spends;
spends.resize(2);
for (int i = 0; i < 2; i++) {
spends[i].nVersion = 1;
spends[i].vin.resize(1);
- spends[i].vin[0].prevout = COutPoint(coinbaseTxns[0].GetId(), 0);
+ spends[i].vin[0].prevout = COutPoint(m_coinbase_txns[0].GetId(), 0);
spends[i].vout.resize(1);
spends[i].vout[0].nValue = 11 * CENT;
spends[i].vout[0].scriptPubKey = scriptPubKey;
// Sign:
std::vector<uint8_t> vchSig;
uint256 hash = SignatureHash(scriptPubKey, CTransaction(spends[i]), 0,
SigHashType().withForkId(),
- coinbaseTxns[0].vout[0].nValue);
+ m_coinbase_txns[0].vout[0].nValue);
BOOST_CHECK(coinbaseKey.SignECDSA(hash, vchSig));
vchSig.push_back(uint8_t(SIGHASH_ALL | SIGHASH_FORKID));
spends[i].vin[0].scriptSig << vchSig;
}
CBlock block;
// Test 1: block with both of those transactions should be rejected.
block = CreateAndProcessBlock(spends, scriptPubKey);
BOOST_CHECK(chainActive.Tip()->GetBlockHash() != block.GetHash());
// Test 2: ... and should be rejected if spend1 is in the memory pool
BOOST_CHECK(ToMemPool(spends[0]));
block = CreateAndProcessBlock(spends, scriptPubKey);
BOOST_CHECK(chainActive.Tip()->GetBlockHash() != block.GetHash());
g_mempool.clear();
// Test 3: ... and should be rejected if spend2 is in the memory pool
BOOST_CHECK(ToMemPool(spends[1]));
block = CreateAndProcessBlock(spends, scriptPubKey);
BOOST_CHECK(chainActive.Tip()->GetBlockHash() != block.GetHash());
g_mempool.clear();
// Final sanity test: first spend in mempool, second in block, that's OK:
std::vector<CMutableTransaction> oneSpend;
oneSpend.push_back(spends[0]);
BOOST_CHECK(ToMemPool(spends[1]));
block = CreateAndProcessBlock(oneSpend, scriptPubKey);
BOOST_CHECK(chainActive.Tip()->GetBlockHash() == block.GetHash());
// spends[1] should have been removed from the mempool when the block with
// spends[0] is accepted:
BOOST_CHECK_EQUAL(g_mempool.size(), 0);
}
// Run CheckInputs (using pcoinsTip) on the given transaction, for all script
// flags. Test that CheckInputs passes for all flags that don't overlap with the
// failing_flags argument, but otherwise fails.
// CHECKLOCKTIMEVERIFY and CHECKSEQUENCEVERIFY (and future NOP codes that may
// get reassigned) have an interaction with DISCOURAGE_UPGRADABLE_NOPS: if the
// script flags used contain DISCOURAGE_UPGRADABLE_NOPS but don't contain
// CHECKLOCKTIMEVERIFY (or CHECKSEQUENCEVERIFY), but the script does contain
// OP_CHECKLOCKTIMEVERIFY (or OP_CHECKSEQUENCEVERIFY), then script execution
// should fail.
// Capture this interaction with the upgraded_nop argument: set it when
// evaluating any script flag that is implemented as an upgraded NOP code.
void ValidateCheckInputsForAllFlags(const CMutableTransaction &mutableTx,
uint32_t failing_flags, bool add_to_cache,
bool upgraded_nop) {
const CTransaction tx(mutableTx);
PrecomputedTransactionData txdata(tx);
// If we add many more flags, this loop can get too expensive, but we can
// rewrite in the future to randomly pick a set of flags to evaluate.
for (uint32_t test_flags = 0; test_flags < (1U << 17); test_flags += 1) {
CValidationState state;
// Make sure the mandatory flags are enabled.
test_flags |= MANDATORY_SCRIPT_VERIFY_FLAGS;
bool ret = CheckInputs(tx, state, pcoinsTip.get(), true, test_flags,
true, add_to_cache, txdata, nullptr);
// CheckInputs should succeed iff test_flags doesn't intersect with
// failing_flags
bool expected_return_value = !(test_flags & failing_flags);
if (expected_return_value && upgraded_nop) {
// If the script flag being tested corresponds to an upgraded NOP,
// then script execution should fail if DISCOURAGE_UPGRADABLE_NOPS
// is set.
expected_return_value =
!(test_flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS);
}
BOOST_CHECK_EQUAL(ret, expected_return_value);
// Test the caching
if (ret && add_to_cache) {
// Check that we get a cache hit if the tx was valid
std::vector<CScriptCheck> scriptchecks;
BOOST_CHECK(CheckInputs(tx, state, pcoinsTip.get(), true,
test_flags, true, add_to_cache, txdata,
&scriptchecks));
BOOST_CHECK(scriptchecks.empty());
} else {
// Check that we get script executions to check, if the transaction
// was invalid, or we didn't add to cache.
std::vector<CScriptCheck> scriptchecks;
BOOST_CHECK(CheckInputs(tx, state, pcoinsTip.get(), true,
test_flags, true, add_to_cache, txdata,
&scriptchecks));
BOOST_CHECK_EQUAL(scriptchecks.size(), tx.vin.size());
}
}
}
BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup) {
// Test that passing CheckInputs with one set of script flags doesn't imply
// that we would pass again with a different set of flags.
{
LOCK(cs_main);
InitScriptExecutionCache();
}
CScript p2pk_scriptPubKey =
CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
CScript p2sh_scriptPubKey =
GetScriptForDestination(CScriptID(p2pk_scriptPubKey));
CScript p2pkh_scriptPubKey =
GetScriptForDestination(coinbaseKey.GetPubKey().GetID());
CBasicKeyStore keystore;
keystore.AddKey(coinbaseKey);
keystore.AddCScript(p2pk_scriptPubKey);
CMutableTransaction mutableFunding_tx;
// Needed when spending the output of this transaction
CScript nulldummyPubKeyScript;
// Create a funding transaction that can fail NULLDUMMY checks. This is for
// testing consensus vs non-standard rules in `checkinputs_test`.
{
mutableFunding_tx.nVersion = 1;
mutableFunding_tx.vin.resize(1);
mutableFunding_tx.vin[0].prevout =
- COutPoint(coinbaseTxns[0].GetId(), 0);
+ COutPoint(m_coinbase_txns[0].GetId(), 0);
mutableFunding_tx.vout.resize(1);
mutableFunding_tx.vout[0].nValue = 50 * COIN;
CKey dummyKey;
dummyKey.MakeNewKey(true);
nulldummyPubKeyScript << OP_1 << ToByteVector(coinbaseKey.GetPubKey())
<< ToByteVector(dummyKey.GetPubKey()) << OP_2
<< OP_CHECKMULTISIG;
mutableFunding_tx.vout[0].scriptPubKey = nulldummyPubKeyScript;
std::vector<uint8_t> nullDummyVchSig;
uint256 nulldummySigHash = SignatureHash(
p2pk_scriptPubKey, CTransaction(mutableFunding_tx), 0,
- SigHashType().withForkId(), coinbaseTxns[0].vout[0].nValue);
+ SigHashType().withForkId(), m_coinbase_txns[0].vout[0].nValue);
BOOST_CHECK(coinbaseKey.SignECDSA(nulldummySigHash, nullDummyVchSig));
nullDummyVchSig.push_back(uint8_t(SIGHASH_ALL | SIGHASH_FORKID));
mutableFunding_tx.vin[0].scriptSig << nullDummyVchSig;
}
const CTransaction funding_tx = CTransaction(mutableFunding_tx);
// Spend the funding transaction by mining it into a block
{
CBlock block = CreateAndProcessBlock({funding_tx}, p2pk_scriptPubKey);
BOOST_CHECK(chainActive.Tip()->GetBlockHash() == block.GetHash());
BOOST_CHECK(pcoinsTip->GetBestBlock() == block.GetHash());
}
// flags to test: SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY,
// SCRIPT_VERIFY_CHECKSEQUENCE_VERIFY, SCRIPT_VERIFY_NULLDUMMY, uncompressed
// pubkey thing
// Create 2 outputs that match the three scripts above, spending the first
// coinbase tx.
CMutableTransaction mutableSpend_tx;
mutableSpend_tx.nVersion = 1;
mutableSpend_tx.vin.resize(1);
mutableSpend_tx.vin[0].prevout = COutPoint(funding_tx.GetId(), 0);
mutableSpend_tx.vout.resize(4);
mutableSpend_tx.vout[0].nValue = 11 * CENT;
mutableSpend_tx.vout[0].scriptPubKey = p2sh_scriptPubKey;
mutableSpend_tx.vout[1].nValue = 11 * CENT;
mutableSpend_tx.vout[1].scriptPubKey =
CScript() << OP_CHECKLOCKTIMEVERIFY << OP_DROP
<< ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
mutableSpend_tx.vout[2].nValue = 11 * CENT;
mutableSpend_tx.vout[2].scriptPubKey =
CScript() << OP_CHECKSEQUENCEVERIFY << OP_DROP
<< ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
mutableSpend_tx.vout[3].nValue = 11 * CENT;
mutableSpend_tx.vout[3].scriptPubKey = p2sh_scriptPubKey;
// Sign the main transaction that we spend from.
{
std::vector<uint8_t> vchSig;
uint256 hash = SignatureHash(
nulldummyPubKeyScript, CTransaction(mutableSpend_tx), 0,
SigHashType().withForkId(), funding_tx.vout[0].nValue);
coinbaseKey.SignECDSA(hash, vchSig);
vchSig.push_back(uint8_t(SIGHASH_ALL | SIGHASH_FORKID));
// The last item on the stack will be dropped by CHECKMULTISIG This is
// to check nulldummy enforcement. It is OP_1 instead of OP_0.
mutableSpend_tx.vin[0].scriptSig << OP_1 << vchSig;
}
const CTransaction spend_tx(mutableSpend_tx);
// Test that invalidity under a set of flags doesn't preclude validity under
// other (eg consensus) flags.
// spend_tx is invalid according to NULLDUMMY
{
LOCK(cs_main);
CValidationState state;
PrecomputedTransactionData ptd_spend_tx(spend_tx);
BOOST_CHECK(!CheckInputs(spend_tx, state, pcoinsTip.get(), true,
MANDATORY_SCRIPT_VERIFY_FLAGS |
SCRIPT_VERIFY_NULLDUMMY,
true, true, ptd_spend_tx, nullptr));
// If we call again asking for scriptchecks (as happens in
// ConnectBlock), we should add a script check object for this -- we're
// not caching invalidity (if that changes, delete this test case).
std::vector<CScriptCheck> scriptchecks;
BOOST_CHECK(
CheckInputs(spend_tx, state, pcoinsTip.get(), true,
MANDATORY_SCRIPT_VERIFY_FLAGS | SCRIPT_VERIFY_NULLDUMMY,
true, true, ptd_spend_tx, &scriptchecks));
BOOST_CHECK_EQUAL(scriptchecks.size(), 1);
// Test that CheckInputs returns true iff cleanstack-enforcing flags are
// not present. Don't add these checks to the cache, so that we can test
// later that block validation works fine in the absence of cached
// successes.
ValidateCheckInputsForAllFlags(spend_tx, SCRIPT_VERIFY_NULLDUMMY, false,
false);
}
// And if we produce a block with this tx, it should be valid (LOW_S not
// enabled yet), even though there's no cache entry.
CBlock block;
block = CreateAndProcessBlock({spend_tx}, p2pk_scriptPubKey);
BOOST_CHECK(chainActive.Tip()->GetBlockHash() == block.GetHash());
BOOST_CHECK(pcoinsTip->GetBestBlock() == block.GetHash());
LOCK(cs_main);
// Test P2SH: construct a transaction that is valid without P2SH, and then
// test validity with P2SH.
{
CMutableTransaction invalid_under_p2sh_tx;
invalid_under_p2sh_tx.nVersion = 1;
invalid_under_p2sh_tx.vin.resize(1);
invalid_under_p2sh_tx.vin[0].prevout = COutPoint(spend_tx.GetId(), 0);
invalid_under_p2sh_tx.vout.resize(1);
invalid_under_p2sh_tx.vout[0].nValue = 11 * CENT;
invalid_under_p2sh_tx.vout[0].scriptPubKey = p2pk_scriptPubKey;
std::vector<uint8_t> vchSig2(p2pk_scriptPubKey.begin(),
p2pk_scriptPubKey.end());
invalid_under_p2sh_tx.vin[0].scriptSig << vchSig2;
ValidateCheckInputsForAllFlags(invalid_under_p2sh_tx,
SCRIPT_VERIFY_P2SH, true, false);
}
// Test CHECKLOCKTIMEVERIFY
{
CMutableTransaction invalid_with_cltv_tx;
invalid_with_cltv_tx.nVersion = 1;
invalid_with_cltv_tx.nLockTime = 100;
invalid_with_cltv_tx.vin.resize(1);
invalid_with_cltv_tx.vin[0].prevout = COutPoint(spend_tx.GetId(), 1);
invalid_with_cltv_tx.vin[0].nSequence = 0;
invalid_with_cltv_tx.vout.resize(1);
invalid_with_cltv_tx.vout[0].nValue = 11 * CENT;
invalid_with_cltv_tx.vout[0].scriptPubKey = p2pk_scriptPubKey;
// Sign
std::vector<uint8_t> vchSig;
uint256 hash = SignatureHash(
spend_tx.vout[1].scriptPubKey, CTransaction(invalid_with_cltv_tx),
0, SigHashType().withForkId(), spend_tx.vout[1].nValue);
BOOST_CHECK(coinbaseKey.SignECDSA(hash, vchSig));
vchSig.push_back(uint8_t(SIGHASH_ALL | SIGHASH_FORKID));
invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 101;
ValidateCheckInputsForAllFlags(invalid_with_cltv_tx,
SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true,
true);
// Make it valid, and check again
invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 100;
CValidationState state;
CTransaction transaction(invalid_with_cltv_tx);
PrecomputedTransactionData txdata(transaction);
BOOST_CHECK(CheckInputs(transaction, state, pcoinsTip.get(), true,
MANDATORY_SCRIPT_VERIFY_FLAGS |
SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY,
true, true, txdata, nullptr));
}
// TEST CHECKSEQUENCEVERIFY
{
CMutableTransaction invalid_with_csv_tx;
invalid_with_csv_tx.nVersion = 2;
invalid_with_csv_tx.vin.resize(1);
invalid_with_csv_tx.vin[0].prevout = COutPoint(spend_tx.GetId(), 2);
invalid_with_csv_tx.vin[0].nSequence = 100;
invalid_with_csv_tx.vout.resize(1);
invalid_with_csv_tx.vout[0].nValue = 11 * CENT;
invalid_with_csv_tx.vout[0].scriptPubKey = p2pk_scriptPubKey;
// Sign
std::vector<uint8_t> vchSig;
uint256 hash = SignatureHash(
spend_tx.vout[2].scriptPubKey, CTransaction(invalid_with_csv_tx), 0,
SigHashType().withForkId(), spend_tx.vout[2].nValue);
BOOST_CHECK(coinbaseKey.SignECDSA(hash, vchSig));
vchSig.push_back(uint8_t(SIGHASH_ALL | SIGHASH_FORKID));
invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 101;
ValidateCheckInputsForAllFlags(
invalid_with_csv_tx, SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, true);
// Make it valid, and check again
invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 100;
CValidationState state;
CTransaction transaction(invalid_with_csv_tx);
PrecomputedTransactionData txdata(transaction);
BOOST_CHECK(CheckInputs(transaction, state, pcoinsTip.get(), true,
MANDATORY_SCRIPT_VERIFY_FLAGS |
SCRIPT_VERIFY_CHECKSEQUENCEVERIFY,
true, true, txdata, nullptr));
}
// TODO: add tests for remaining script flags
{
// Test a transaction with multiple inputs.
CMutableTransaction tx;
tx.nVersion = 1;
tx.vin.resize(2);
tx.vin[0].prevout = COutPoint(spend_tx.GetId(), 0);
tx.vin[1].prevout = COutPoint(spend_tx.GetId(), 3);
tx.vout.resize(1);
tx.vout[0].nValue = 22 * CENT;
tx.vout[0].scriptPubKey = p2pk_scriptPubKey;
// Sign
SignatureData sigdata;
ProduceSignature(
MutableTransactionSignatureCreator(&keystore, &tx, 0, 11 * CENT,
SigHashType().withForkId()),
spend_tx.vout[0].scriptPubKey, sigdata);
UpdateTransaction(tx, 0, sigdata);
ProduceSignature(
MutableTransactionSignatureCreator(&keystore, &tx, 1, 11 * CENT,
SigHashType().withForkId()),
spend_tx.vout[3].scriptPubKey, sigdata);
UpdateTransaction(tx, 1, sigdata);
// This should be valid under all script flags
ValidateCheckInputsForAllFlags(tx, 0, true, false);
// Check that if the second input is invalid, but the first input is
// valid, the transaction is not cached.
// Invalidate vin[1]
tx.vin[1].scriptSig = CScript();
CValidationState state;
CTransaction transaction(tx);
PrecomputedTransactionData txdata(transaction);
// This transaction is now invalid because the second signature is
// missing.
BOOST_CHECK(!CheckInputs(transaction, state, pcoinsTip.get(), true,
MANDATORY_SCRIPT_VERIFY_FLAGS, true, true,
txdata, nullptr));
// Make sure this transaction was not cached (ie becausethe first input
// was valid)
std::vector<CScriptCheck> scriptchecks;
BOOST_CHECK(CheckInputs(transaction, state, pcoinsTip.get(), true,
MANDATORY_SCRIPT_VERIFY_FLAGS, true, true,
txdata, &scriptchecks));
// Should get 2 script checks back -- caching is on a whole-transaction
// basis.
BOOST_CHECK_EQUAL(scriptchecks.size(), 2);
}
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index d221297b8..dbe6f2bc3 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -1,383 +1,383 @@
// Copyright (c) 2012-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <chain.h>
#include <chainparams.h>
#include <config.h>
#include <consensus/validation.h>
#include <rpc/server.h>
#include <validation.h>
#include <wallet/coincontrol.h>
#include <wallet/rpcdump.h>
#include <wallet/wallet.h>
#include <test/test_bitcoin.h>
#include <wallet/test/wallet_test_fixture.h>
#include <boost/test/unit_test.hpp>
#include <univalue.h>
#include <cstdint>
#include <set>
#include <utility>
#include <vector>
BOOST_FIXTURE_TEST_SUITE(wallet_tests, WalletTestingSetup)
static void AddKey(CWallet &wallet, const CKey &key) {
LOCK(wallet.cs_wallet);
wallet.AddKeyPubKey(key, key.GetPubKey());
}
BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup) {
// Cap last block file size, and mine new block in a new block file.
CBlockIndex *const nullBlock = nullptr;
CBlockIndex *oldTip = chainActive.Tip();
GetBlockFileInfo(oldTip->GetBlockPos().nFile)->nSize = MAX_BLOCKFILE_SIZE;
CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
CBlockIndex *newTip = chainActive.Tip();
LOCK(cs_main);
// Verify ScanForWalletTransactions picks up transactions in both the old
// and new block files.
{
CWallet wallet(Params(), "dummy", CWalletDBWrapper::CreateDummy());
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(&wallet);
reserver.reserve();
BOOST_CHECK_EQUAL(nullBlock, wallet.ScanForWalletTransactions(
oldTip, nullptr, reserver));
BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 100 * COIN);
}
// Prune the older block file.
PruneOneBlockFile(oldTip->GetBlockPos().nFile);
UnlinkPrunedFiles({oldTip->GetBlockPos().nFile});
// Verify ScanForWalletTransactions only picks transactions in the new block
// file.
{
CWallet wallet(Params(), "dummy", CWalletDBWrapper::CreateDummy());
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(&wallet);
reserver.reserve();
BOOST_CHECK_EQUAL(oldTip, wallet.ScanForWalletTransactions(
oldTip, nullptr, reserver));
BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 50 * COIN);
}
// 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
// after.
{
CWallet wallet(Params(), "dummy", CWalletDBWrapper::CreateDummy());
vpwallets.insert(vpwallets.begin(), &wallet);
UniValue keys;
keys.setArray();
UniValue key;
key.setObject();
key.pushKV("scriptPubKey",
HexStr(GetScriptForRawPubKey(coinbaseKey.GetPubKey())));
key.pushKV("timestamp", 0);
key.pushKV("internal", UniValue(true));
keys.push_back(key);
key.clear();
key.setObject();
CKey futureKey;
futureKey.MakeNewKey(true);
key.pushKV("scriptPubKey",
HexStr(GetScriptForRawPubKey(futureKey.GetPubKey())));
key.pushKV("timestamp",
newTip->GetBlockTimeMax() + TIMESTAMP_WINDOW + 1);
key.pushKV("internal", UniValue(true));
keys.push_back(key);
JSONRPCRequest request;
request.params.setArray();
request.params.push_back(keys);
UniValue response = importmulti(GetConfig(), request);
BOOST_CHECK_EQUAL(
response.write(),
strprintf("[{\"success\":false,\"error\":{\"code\":-1,\"message\":"
"\"Rescan failed for key with creation timestamp %d. "
"There was an error reading a block from time %d, which "
"is after or within %d seconds of key creation, and "
"could contain transactions pertaining to the key. As a "
"result, transactions and coins using this key may not "
"appear in the wallet. This error could be caused by "
"pruning or data corruption (see bitcoind log for "
"details) and could be dealt with by downloading and "
"rescanning the relevant blocks (see -reindex and "
"-rescan options).\"}},{\"success\":true}]",
0, oldTip->GetBlockTimeMax(), TIMESTAMP_WINDOW));
vpwallets.erase(vpwallets.begin());
}
}
// Verify importwallet RPC starts rescan at earliest block with timestamp
// greater or equal than key birthday. Previously there was a bug where
// importwallet RPC would start the scan at the latest block with timestamp less
// than or equal to key birthday.
BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup) {
// Create two blocks with same timestamp to verify that importwallet rescan
// will pick up both blocks, not just the first.
const int64_t BLOCK_TIME = chainActive.Tip()->GetBlockTimeMax() + 5;
SetMockTime(BLOCK_TIME);
- coinbaseTxns.emplace_back(
+ m_coinbase_txns.emplace_back(
*CreateAndProcessBlock({},
GetScriptForRawPubKey(coinbaseKey.GetPubKey()))
.vtx[0]);
- coinbaseTxns.emplace_back(
+ m_coinbase_txns.emplace_back(
*CreateAndProcessBlock({},
GetScriptForRawPubKey(coinbaseKey.GetPubKey()))
.vtx[0]);
// Set key birthday to block time increased by the timestamp window, so
// rescan will start at the block time.
const int64_t KEY_TIME = BLOCK_TIME + TIMESTAMP_WINDOW;
SetMockTime(KEY_TIME);
- coinbaseTxns.emplace_back(
+ m_coinbase_txns.emplace_back(
*CreateAndProcessBlock({},
GetScriptForRawPubKey(coinbaseKey.GetPubKey()))
.vtx[0]);
LOCK(cs_main);
// Import key into wallet and call dumpwallet to create backup file.
{
CWallet wallet(Params(), "dummy", CWalletDBWrapper::CreateDummy());
LOCK(wallet.cs_wallet);
wallet.mapKeyMetadata[coinbaseKey.GetPubKey().GetID()].nCreateTime =
KEY_TIME;
wallet.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey());
JSONRPCRequest request;
request.params.setArray();
request.params.push_back((pathTemp / "wallet.backup").string());
vpwallets.insert(vpwallets.begin(), &wallet);
::dumpwallet(GetConfig(), request);
}
// Call importwallet RPC and verify all blocks with timestamps >= BLOCK_TIME
// were scanned, and no prior blocks were scanned.
{
CWallet wallet(Params(), "dummy", CWalletDBWrapper::CreateDummy());
JSONRPCRequest request;
request.params.setArray();
request.params.push_back((pathTemp / "wallet.backup").string());
vpwallets[0] = &wallet;
::importwallet(GetConfig(), request);
LOCK(wallet.cs_wallet);
BOOST_CHECK_EQUAL(wallet.mapWallet.size(), 3);
- BOOST_CHECK_EQUAL(coinbaseTxns.size(), 103);
- for (size_t i = 0; i < coinbaseTxns.size(); ++i) {
- bool found = wallet.GetWalletTx(coinbaseTxns[i].GetId());
+ BOOST_CHECK_EQUAL(m_coinbase_txns.size(), 103);
+ for (size_t i = 0; i < m_coinbase_txns.size(); ++i) {
+ bool found = wallet.GetWalletTx(m_coinbase_txns[i].GetId());
bool expected = i >= 100;
BOOST_CHECK_EQUAL(found, expected);
}
}
SetMockTime(0);
vpwallets.erase(vpwallets.begin());
}
// Check that GetImmatureCredit() returns a newly calculated value instead of
// the cached value after a MarkDirty() call.
//
// This is a regression test written to verify a bugfix for the immature credit
// function. Similar tests probably should be written for the other credit and
// debit functions.
BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup) {
CWallet wallet(Params(), "dummy", CWalletDBWrapper::CreateDummy());
- CWalletTx wtx(&wallet, MakeTransactionRef(coinbaseTxns.back()));
+ CWalletTx wtx(&wallet, MakeTransactionRef(m_coinbase_txns.back()));
LOCK2(cs_main, wallet.cs_wallet);
wtx.hashBlock = chainActive.Tip()->GetBlockHash();
wtx.nIndex = 0;
// Call GetImmatureCredit() once before adding the key to the wallet to
// cache the current immature credit amount, which is 0.
BOOST_CHECK_EQUAL(wtx.GetImmatureCredit(), Amount::zero());
// Invalidate the cached value, add the key, and make sure a new immature
// credit amount is calculated.
wtx.MarkDirty();
wallet.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey());
BOOST_CHECK_EQUAL(wtx.GetImmatureCredit(), 50 * COIN);
}
static int64_t AddTx(CWallet &wallet, uint32_t lockTime, int64_t mockTime,
int64_t blockTime) {
CMutableTransaction tx;
tx.nLockTime = lockTime;
SetMockTime(mockTime);
CBlockIndex *block = nullptr;
if (blockTime > 0) {
LOCK(cs_main);
auto inserted = mapBlockIndex.emplace(GetRandHash(), new CBlockIndex);
assert(inserted.second);
const uint256 &hash = inserted.first->first;
block = inserted.first->second;
block->nTime = blockTime;
block->phashBlock = &hash;
}
CWalletTx wtx(&wallet, MakeTransactionRef(tx));
if (block) {
wtx.SetMerkleBranch(block, 0);
}
{
LOCK(cs_main);
wallet.AddToWallet(wtx);
}
LOCK(wallet.cs_wallet);
return wallet.mapWallet.at(wtx.GetId()).nTimeSmart;
}
// Simple test to verify assignment of CWalletTx::nSmartTime value. Could be
// expanded to cover more corner cases of smart time logic.
BOOST_AUTO_TEST_CASE(ComputeTimeSmart) {
// New transaction should use clock time if lower than block time.
BOOST_CHECK_EQUAL(AddTx(m_wallet, 1, 100, 120), 100);
// Test that updating existing transaction does not change smart time.
BOOST_CHECK_EQUAL(AddTx(m_wallet, 1, 200, 220), 100);
// New transaction should use clock time if there's no block time.
BOOST_CHECK_EQUAL(AddTx(m_wallet, 2, 300, 0), 300);
// New transaction should use block time if lower than clock time.
BOOST_CHECK_EQUAL(AddTx(m_wallet, 3, 420, 400), 400);
// New transaction should use latest entry time if higher than
// min(block time, clock time).
BOOST_CHECK_EQUAL(AddTx(m_wallet, 4, 500, 390), 400);
// If there are future entries, new transaction should use time of the
// newest entry that is no more than 300 seconds ahead of the clock time.
BOOST_CHECK_EQUAL(AddTx(m_wallet, 5, 50, 600), 300);
// Reset mock time for other tests.
SetMockTime(0);
}
BOOST_AUTO_TEST_CASE(LoadReceiveRequests) {
CTxDestination dest = CKeyID();
LOCK(m_wallet.cs_wallet);
m_wallet.AddDestData(dest, "misc", "val_misc");
m_wallet.AddDestData(dest, "rr0", "val_rr0");
m_wallet.AddDestData(dest, "rr1", "val_rr1");
auto values = m_wallet.GetDestValues("rr");
BOOST_CHECK_EQUAL(values.size(), 2);
BOOST_CHECK_EQUAL(values[0], "val_rr0");
BOOST_CHECK_EQUAL(values[1], "val_rr1");
}
class ListCoinsTestingSetup : public TestChain100Setup {
public:
ListCoinsTestingSetup() {
CreateAndProcessBlock({},
GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
wallet = std::make_unique<CWallet>(Params(), "mock",
CWalletDBWrapper::CreateMock());
bool firstRun;
wallet->LoadWallet(firstRun);
AddKey(*wallet, coinbaseKey);
WalletRescanReserver reserver(wallet.get());
reserver.reserve();
wallet->ScanForWalletTransactions(chainActive.Genesis(), nullptr,
reserver);
}
~ListCoinsTestingSetup() { wallet.reset(); }
CWalletTx &AddTx(CRecipient recipient) {
CTransactionRef tx;
CReserveKey reservekey(wallet.get());
Amount fee;
int changePos = -1;
std::string error;
CCoinControl dummy;
BOOST_CHECK(wallet->CreateTransaction({recipient}, tx, reservekey, fee,
changePos, error, dummy));
CValidationState state;
BOOST_CHECK(wallet->CommitTransaction(tx, {}, {}, {}, reservekey,
nullptr, state));
CMutableTransaction blocktx;
{
LOCK(wallet->cs_wallet);
blocktx =
CMutableTransaction(*wallet->mapWallet.at(tx->GetId()).tx);
}
CreateAndProcessBlock({CMutableTransaction(blocktx)},
GetScriptForRawPubKey(coinbaseKey.GetPubKey()));
LOCK(wallet->cs_wallet);
auto it = wallet->mapWallet.find(tx->GetId());
BOOST_CHECK(it != wallet->mapWallet.end());
it->second.SetMerkleBranch(chainActive.Tip(), 1);
return it->second;
}
std::unique_ptr<CWallet> wallet;
};
BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup) {
std::string coinbaseAddress = coinbaseKey.GetPubKey().GetID().ToString();
// Confirm ListCoins initially returns 1 coin grouped under coinbaseKey
// address.
auto list = wallet->ListCoins();
BOOST_CHECK_EQUAL(list.size(), 1);
BOOST_CHECK_EQUAL(boost::get<CKeyID>(list.begin()->first).ToString(),
coinbaseAddress);
BOOST_CHECK_EQUAL(list.begin()->second.size(), 1);
// Check initial balance from one mature coinbase transaction.
BOOST_CHECK_EQUAL(50 * COIN, wallet->GetAvailableBalance());
// Add a transaction creating a change address, and confirm ListCoins still
// returns the coin associated with the change address underneath the
// coinbaseKey pubkey, even though the change address has a different
// pubkey.
AddTx(CRecipient{GetScriptForRawPubKey({}), 1 * COIN,
false /* subtract fee */});
list = wallet->ListCoins();
BOOST_CHECK_EQUAL(list.size(), 1);
BOOST_CHECK_EQUAL(boost::get<CKeyID>(list.begin()->first).ToString(),
coinbaseAddress);
BOOST_CHECK_EQUAL(list.begin()->second.size(), 2);
// Lock both coins. Confirm number of available coins drops to 0.
{
LOCK2(cs_main, wallet->cs_wallet);
std::vector<COutput> available;
wallet->AvailableCoins(available);
BOOST_CHECK_EQUAL(available.size(), 2);
}
for (const auto &group : list) {
for (const auto &coin : group.second) {
LOCK(wallet->cs_wallet);
wallet->LockCoin(COutPoint(coin.tx->GetId(), coin.i));
}
}
{
LOCK2(cs_main, wallet->cs_wallet);
std::vector<COutput> available;
wallet->AvailableCoins(available);
BOOST_CHECK_EQUAL(available.size(), 0);
}
// Confirm ListCoins still returns same result as before, despite coins
// being locked.
list = wallet->ListCoins();
BOOST_CHECK_EQUAL(list.size(), 1);
BOOST_CHECK_EQUAL(boost::get<CKeyID>(list.begin()->first).ToString(),
coinbaseAddress);
BOOST_CHECK_EQUAL(list.begin()->second.size(), 2);
}
BOOST_AUTO_TEST_SUITE_END()

File Metadata

Mime Type
text/x-diff
Expires
Fri, Feb 7, 17:34 (1 d, 21 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5082785
Default Alt Text
(52 KB)

Event Timeline