Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F12945055
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
52 KB
Subscribers
None
View Options
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
Details
Attached
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)
Attached To
rABC Bitcoin ABC
Event Timeline
Log In to Comment