2014-09-16 16:27:06 -07:00
|
|
|
// Copyright (c) 2014 The Bitcoin Core developers
|
2023-01-23 10:31:54 -08:00
|
|
|
// Copyright (c) 2016-2023 The Zcash developers
|
2014-09-16 16:27:06 -07:00
|
|
|
// Distributed under the MIT software license, see the accompanying
|
2019-07-18 07:16:09 -07:00
|
|
|
// file COPYING or https://www.opensource.org/licenses/mit-license.php .
|
2014-09-16 16:27:06 -07:00
|
|
|
|
|
|
|
#include "coins.h"
|
2016-04-25 05:05:36 -07:00
|
|
|
#include "script/standard.h"
|
2014-09-16 16:27:06 -07:00
|
|
|
#include "uint256.h"
|
2018-10-22 15:51:11 -07:00
|
|
|
#include "util/strencodings.h"
|
2015-03-12 01:34:42 -07:00
|
|
|
#include "test/test_bitcoin.h"
|
2016-06-13 10:58:10 -07:00
|
|
|
#include "consensus/validation.h"
|
|
|
|
#include "main.h"
|
|
|
|
#include "undo.h"
|
2018-04-18 12:18:57 -07:00
|
|
|
#include "primitives/transaction.h"
|
2016-10-20 23:50:19 -07:00
|
|
|
#include "pubkey.h"
|
2022-02-17 18:14:37 -08:00
|
|
|
#include "transaction_builder.h"
|
2020-07-09 01:20:06 -07:00
|
|
|
#include "zcash/Note.hpp"
|
2022-02-17 18:14:37 -08:00
|
|
|
#include "zcash/address/mnemonic.h"
|
2014-09-16 16:27:06 -07:00
|
|
|
|
|
|
|
#include <vector>
|
|
|
|
#include <map>
|
|
|
|
|
|
|
|
#include <boost/test/unit_test.hpp>
|
2016-04-02 22:31:26 -07:00
|
|
|
#include "zcash/IncrementalMerkleTree.hpp"
|
2014-09-16 16:27:06 -07:00
|
|
|
|
|
|
|
namespace
|
|
|
|
{
|
|
|
|
class CCoinsViewTest : public CCoinsView
|
|
|
|
{
|
|
|
|
uint256 hashBestBlock_;
|
2018-04-25 18:15:30 -07:00
|
|
|
uint256 hashBestSproutAnchor_;
|
2018-04-27 15:00:21 -07:00
|
|
|
uint256 hashBestSaplingAnchor_;
|
2021-06-28 19:17:35 -07:00
|
|
|
uint256 hashBestOrchardAnchor_;
|
2014-09-16 16:27:06 -07:00
|
|
|
std::map<uint256, CCoins> map_;
|
2018-08-01 09:31:09 -07:00
|
|
|
std::map<uint256, SproutMerkleTree> mapSproutAnchors_;
|
|
|
|
std::map<uint256, SaplingMerkleTree> mapSaplingAnchors_;
|
2021-07-19 18:59:21 -07:00
|
|
|
std::map<uint256, OrchardMerkleFrontier> mapOrchardAnchors_;
|
2018-04-25 10:26:38 -07:00
|
|
|
std::map<uint256, bool> mapSproutNullifiers_;
|
2018-04-18 12:18:57 -07:00
|
|
|
std::map<uint256, bool> mapSaplingNullifiers_;
|
2021-06-28 19:17:35 -07:00
|
|
|
std::map<uint256, bool> mapOrchardNullifiers_;
|
2014-09-16 16:27:06 -07:00
|
|
|
|
|
|
|
public:
|
2016-05-26 12:58:58 -07:00
|
|
|
CCoinsViewTest() {
|
2018-08-01 09:31:09 -07:00
|
|
|
hashBestSproutAnchor_ = SproutMerkleTree::empty_root();
|
|
|
|
hashBestSaplingAnchor_ = SaplingMerkleTree::empty_root();
|
2021-07-19 18:59:21 -07:00
|
|
|
hashBestOrchardAnchor_ = OrchardMerkleFrontier::empty_root();
|
2016-05-26 12:58:58 -07:00
|
|
|
}
|
2021-06-28 19:39:00 -07:00
|
|
|
~CCoinsViewTest() {}
|
2016-05-26 12:58:58 -07:00
|
|
|
|
2018-08-01 09:31:09 -07:00
|
|
|
bool GetSproutAnchorAt(const uint256& rt, SproutMerkleTree &tree) const {
|
|
|
|
if (rt == SproutMerkleTree::empty_root()) {
|
|
|
|
SproutMerkleTree new_tree;
|
2016-04-02 22:31:26 -07:00
|
|
|
tree = new_tree;
|
2016-01-04 01:54:47 -08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-08-01 09:31:09 -07:00
|
|
|
std::map<uint256, SproutMerkleTree>::const_iterator it = mapSproutAnchors_.find(rt);
|
2018-04-25 17:38:34 -07:00
|
|
|
if (it == mapSproutAnchors_.end()) {
|
2016-01-04 01:54:47 -08:00
|
|
|
return false;
|
|
|
|
} else {
|
2016-04-02 22:31:26 -07:00
|
|
|
tree = it->second;
|
2016-01-04 01:54:47 -08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-01 09:31:09 -07:00
|
|
|
bool GetSaplingAnchorAt(const uint256& rt, SaplingMerkleTree &tree) const {
|
|
|
|
if (rt == SaplingMerkleTree::empty_root()) {
|
|
|
|
SaplingMerkleTree new_tree;
|
2018-05-06 23:00:39 -07:00
|
|
|
tree = new_tree;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-08-01 09:31:09 -07:00
|
|
|
std::map<uint256, SaplingMerkleTree>::const_iterator it = mapSaplingAnchors_.find(rt);
|
2018-05-06 23:00:39 -07:00
|
|
|
if (it == mapSaplingAnchors_.end()) {
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
tree = it->second;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-19 18:59:21 -07:00
|
|
|
bool GetOrchardAnchorAt(const uint256& rt, OrchardMerkleFrontier &tree) const {
|
|
|
|
if (rt == OrchardMerkleFrontier::empty_root()) {
|
|
|
|
OrchardMerkleFrontier new_tree;
|
2021-06-28 19:17:35 -07:00
|
|
|
tree = new_tree;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-07-19 18:59:21 -07:00
|
|
|
std::map<uint256, OrchardMerkleFrontier>::const_iterator it = mapOrchardAnchors_.find(rt);
|
2021-06-28 19:17:35 -07:00
|
|
|
if (it == mapOrchardAnchors_.end()) {
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
tree = it->second;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-25 16:55:33 -07:00
|
|
|
bool GetNullifier(const uint256 &nf, ShieldedType type) const
|
2016-01-04 02:34:58 -08:00
|
|
|
{
|
2018-04-23 13:37:17 -07:00
|
|
|
const std::map<uint256, bool>* mapToUse;
|
|
|
|
switch (type) {
|
2018-04-25 16:55:33 -07:00
|
|
|
case SPROUT:
|
2018-04-25 10:26:38 -07:00
|
|
|
mapToUse = &mapSproutNullifiers_;
|
2018-04-23 13:37:17 -07:00
|
|
|
break;
|
2018-04-25 16:55:33 -07:00
|
|
|
case SAPLING:
|
2018-04-23 13:37:17 -07:00
|
|
|
mapToUse = &mapSaplingNullifiers_;
|
|
|
|
break;
|
2021-06-28 19:17:35 -07:00
|
|
|
case ORCHARD:
|
|
|
|
mapToUse = &mapOrchardNullifiers_;
|
|
|
|
break;
|
2018-04-23 13:37:17 -07:00
|
|
|
default:
|
2018-05-07 12:49:17 -07:00
|
|
|
throw std::runtime_error("Unknown shielded type");
|
2018-04-23 13:37:17 -07:00
|
|
|
}
|
2018-04-18 12:18:57 -07:00
|
|
|
std::map<uint256, bool>::const_iterator it = mapToUse->find(nf);
|
|
|
|
if (it == mapToUse->end()) {
|
2016-01-04 02:34:58 -08:00
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
// The map shouldn't contain any false entries.
|
|
|
|
assert(it->second);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-27 15:00:21 -07:00
|
|
|
uint256 GetBestAnchor(ShieldedType type) const {
|
|
|
|
switch (type) {
|
|
|
|
case SPROUT:
|
|
|
|
return hashBestSproutAnchor_;
|
|
|
|
break;
|
|
|
|
case SAPLING:
|
|
|
|
return hashBestSaplingAnchor_;
|
|
|
|
break;
|
2021-06-28 19:17:35 -07:00
|
|
|
case ORCHARD:
|
|
|
|
return hashBestOrchardAnchor_;
|
|
|
|
break;
|
2018-04-27 15:00:21 -07:00
|
|
|
default:
|
2018-05-07 12:49:17 -07:00
|
|
|
throw std::runtime_error("Unknown shielded type");
|
2018-04-27 15:00:21 -07:00
|
|
|
}
|
|
|
|
}
|
2016-01-04 01:54:47 -08:00
|
|
|
|
2014-09-16 16:27:06 -07:00
|
|
|
bool GetCoins(const uint256& txid, CCoins& coins) const
|
|
|
|
{
|
|
|
|
std::map<uint256, CCoins>::const_iterator it = map_.find(txid);
|
|
|
|
if (it == map_.end()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
coins = it->second;
|
2017-06-07 12:03:17 -07:00
|
|
|
if (coins.IsPruned() && InsecureRandBool() == 0) {
|
2014-09-16 16:27:06 -07:00
|
|
|
// Randomly return false in case of an empty entry.
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HaveCoins(const uint256& txid) const
|
|
|
|
{
|
|
|
|
CCoins coins;
|
|
|
|
return GetCoins(txid, coins);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint256 GetBestBlock() const { return hashBestBlock_; }
|
|
|
|
|
2018-04-18 12:18:57 -07:00
|
|
|
void BatchWriteNullifiers(CNullifiersMap& mapNullifiers, std::map<uint256, bool>& cacheNullifiers)
|
|
|
|
{
|
|
|
|
for (CNullifiersMap::iterator it = mapNullifiers.begin(); it != mapNullifiers.end(); ) {
|
2015-11-11 10:02:02 -08:00
|
|
|
if (it->second.flags & CNullifiersCacheEntry::DIRTY) {
|
|
|
|
// Same optimization used in CCoinsViewDB is to only write dirty entries.
|
|
|
|
if (it->second.entered) {
|
|
|
|
cacheNullifiers[it->first] = true;
|
|
|
|
} else {
|
|
|
|
cacheNullifiers.erase(it->first);
|
|
|
|
}
|
2018-04-18 12:18:57 -07:00
|
|
|
}
|
2020-07-10 10:02:32 -07:00
|
|
|
it = mapNullifiers.erase(it);
|
2018-04-18 12:18:57 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-11 10:02:02 -08:00
|
|
|
template<typename Tree, typename Map, typename MapEntry>
|
2018-06-19 09:07:00 -07:00
|
|
|
void BatchWriteAnchors(Map& mapAnchors, std::map<uint256, Tree>& cacheAnchors)
|
2018-05-16 09:31:53 -07:00
|
|
|
{
|
|
|
|
for (auto it = mapAnchors.begin(); it != mapAnchors.end(); ) {
|
2015-11-11 10:02:02 -08:00
|
|
|
if (it->second.flags & MapEntry::DIRTY) {
|
|
|
|
// Same optimization used in CCoinsViewDB is to only write dirty entries.
|
|
|
|
if (it->second.entered) {
|
|
|
|
if (it->first != Tree::empty_root()) {
|
|
|
|
auto ret = cacheAnchors.insert(std::make_pair(it->first, Tree())).first;
|
|
|
|
ret->second = it->second.tree;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
cacheAnchors.erase(it->first);
|
|
|
|
}
|
2018-05-16 09:31:53 -07:00
|
|
|
}
|
2020-07-10 10:02:32 -07:00
|
|
|
it = mapAnchors.erase(it);
|
2018-05-16 09:31:53 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-28 19:39:00 -07:00
|
|
|
HistoryIndex GetHistoryLength(uint32_t epochId) const { return 0; }
|
|
|
|
HistoryNode GetHistoryAt(uint32_t epochId, HistoryIndex index) const { return HistoryNode(); }
|
|
|
|
uint256 GetHistoryRoot(uint32_t epochId) const { return uint256(); }
|
|
|
|
|
2016-01-04 01:54:47 -08:00
|
|
|
bool BatchWrite(CCoinsMap& mapCoins,
|
|
|
|
const uint256& hashBlock,
|
2018-04-25 18:15:30 -07:00
|
|
|
const uint256& hashSproutAnchor,
|
2018-04-27 15:00:21 -07:00
|
|
|
const uint256& hashSaplingAnchor,
|
2021-06-28 19:17:35 -07:00
|
|
|
const uint256& hashOrchardAnchor,
|
2018-04-25 17:38:34 -07:00
|
|
|
CAnchorsSproutMap& mapSproutAnchors,
|
2018-05-06 23:00:39 -07:00
|
|
|
CAnchorsSaplingMap& mapSaplingAnchors,
|
2021-06-28 19:17:35 -07:00
|
|
|
CAnchorsOrchardMap& mapOrchardAnchors,
|
2018-04-25 10:26:38 -07:00
|
|
|
CNullifiersMap& mapSproutNullifiers,
|
2019-11-22 00:25:12 -08:00
|
|
|
CNullifiersMap& mapSaplingNullifiers,
|
2021-06-28 19:17:35 -07:00
|
|
|
CNullifiersMap& mapOrchardNullifiers,
|
2019-11-22 00:25:12 -08:00
|
|
|
CHistoryCacheMap &historyCacheMap)
|
2014-09-16 16:27:06 -07:00
|
|
|
{
|
|
|
|
for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end(); ) {
|
2015-11-11 10:02:02 -08:00
|
|
|
if (it->second.flags & CCoinsCacheEntry::DIRTY) {
|
|
|
|
// Same optimization used in CCoinsViewDB is to only write dirty entries.
|
|
|
|
map_[it->first] = it->second.coins;
|
2017-06-07 12:03:17 -07:00
|
|
|
if (it->second.coins.IsPruned() && InsecureRandRange(3) == 0) {
|
2015-11-11 10:02:02 -08:00
|
|
|
// Randomly delete empty entries on write.
|
|
|
|
map_.erase(it->first);
|
|
|
|
}
|
2014-09-16 16:27:06 -07:00
|
|
|
}
|
2020-07-10 10:02:32 -07:00
|
|
|
it = mapCoins.erase(it);
|
2014-09-16 16:27:06 -07:00
|
|
|
}
|
2016-01-14 14:56:33 -08:00
|
|
|
|
2015-11-11 10:02:02 -08:00
|
|
|
BatchWriteAnchors<SproutMerkleTree, CAnchorsSproutMap, CAnchorsSproutCacheEntry>(mapSproutAnchors, mapSproutAnchors_);
|
|
|
|
BatchWriteAnchors<SaplingMerkleTree, CAnchorsSaplingMap, CAnchorsSaplingCacheEntry>(mapSaplingAnchors, mapSaplingAnchors_);
|
2021-07-19 18:59:21 -07:00
|
|
|
BatchWriteAnchors<OrchardMerkleFrontier, CAnchorsOrchardMap, CAnchorsOrchardCacheEntry>(mapOrchardAnchors, mapOrchardAnchors_);
|
2018-04-18 12:18:57 -07:00
|
|
|
|
2018-04-25 10:26:38 -07:00
|
|
|
BatchWriteNullifiers(mapSproutNullifiers, mapSproutNullifiers_);
|
2018-04-18 12:18:57 -07:00
|
|
|
BatchWriteNullifiers(mapSaplingNullifiers, mapSaplingNullifiers_);
|
2021-06-28 19:17:35 -07:00
|
|
|
BatchWriteNullifiers(mapOrchardNullifiers, mapOrchardNullifiers_);
|
2018-04-18 12:18:57 -07:00
|
|
|
|
2015-11-11 10:02:02 -08:00
|
|
|
if (!hashBlock.IsNull())
|
|
|
|
hashBestBlock_ = hashBlock;
|
|
|
|
if (!hashSproutAnchor.IsNull())
|
|
|
|
hashBestSproutAnchor_ = hashSproutAnchor;
|
|
|
|
if (!hashSaplingAnchor.IsNull())
|
|
|
|
hashBestSaplingAnchor_ = hashSaplingAnchor;
|
2021-06-28 19:17:35 -07:00
|
|
|
if (!hashOrchardAnchor.IsNull())
|
|
|
|
hashBestOrchardAnchor_ = hashOrchardAnchor;
|
2014-09-16 16:27:06 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool GetStats(CCoinsStats& stats) const { return false; }
|
|
|
|
};
|
2015-05-03 16:31:11 -07:00
|
|
|
|
|
|
|
class CCoinsViewCacheTest : public CCoinsViewCache
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
CCoinsViewCacheTest(CCoinsView* base) : CCoinsViewCache(base) {}
|
2021-06-28 19:39:00 -07:00
|
|
|
~CCoinsViewCacheTest() {}
|
2015-05-03 16:31:11 -07:00
|
|
|
|
|
|
|
void SelfTest() const
|
|
|
|
{
|
|
|
|
// Manually recompute the dynamic usage of the whole data, and compare it.
|
2016-07-10 19:53:33 -07:00
|
|
|
size_t ret = memusage::DynamicUsage(cacheCoins) +
|
2018-04-25 17:38:34 -07:00
|
|
|
memusage::DynamicUsage(cacheSproutAnchors) +
|
2018-05-06 23:00:39 -07:00
|
|
|
memusage::DynamicUsage(cacheSaplingAnchors) +
|
2021-06-28 19:17:35 -07:00
|
|
|
memusage::DynamicUsage(cacheOrchardAnchors) +
|
2018-04-25 10:26:38 -07:00
|
|
|
memusage::DynamicUsage(cacheSproutNullifiers) +
|
2019-11-22 00:25:12 -08:00
|
|
|
memusage::DynamicUsage(cacheSaplingNullifiers) +
|
2021-06-28 19:17:35 -07:00
|
|
|
memusage::DynamicUsage(cacheOrchardNullifiers) +
|
2019-11-22 00:25:12 -08:00
|
|
|
memusage::DynamicUsage(historyCacheMap);
|
2015-05-03 16:31:11 -07:00
|
|
|
for (CCoinsMap::iterator it = cacheCoins.begin(); it != cacheCoins.end(); it++) {
|
2015-07-17 10:46:18 -07:00
|
|
|
ret += it->second.coins.DynamicMemoryUsage();
|
2015-05-03 16:31:11 -07:00
|
|
|
}
|
2015-07-17 10:46:18 -07:00
|
|
|
BOOST_CHECK_EQUAL(DynamicMemoryUsage(), ret);
|
2015-05-03 16:31:11 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
2018-04-24 16:16:43 -07:00
|
|
|
class TxWithNullifiers
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
CTransaction tx;
|
|
|
|
uint256 sproutNullifier;
|
|
|
|
uint256 saplingNullifier;
|
2022-02-17 18:14:37 -08:00
|
|
|
uint256 orchardNullifier;
|
2018-04-24 16:16:43 -07:00
|
|
|
|
|
|
|
TxWithNullifiers()
|
|
|
|
{
|
|
|
|
CMutableTransaction mutableTx;
|
|
|
|
|
2017-06-07 12:03:17 -07:00
|
|
|
sproutNullifier = InsecureRand256();
|
2018-04-24 16:16:43 -07:00
|
|
|
JSDescription jsd;
|
|
|
|
jsd.nullifiers[0] = sproutNullifier;
|
2019-06-16 04:39:05 -07:00
|
|
|
mutableTx.vJoinSplit.emplace_back(jsd);
|
2021-06-28 19:39:00 -07:00
|
|
|
|
2017-06-07 12:03:17 -07:00
|
|
|
saplingNullifier = InsecureRand256();
|
2018-04-24 16:16:43 -07:00
|
|
|
SpendDescription sd;
|
|
|
|
sd.nullifier = saplingNullifier;
|
|
|
|
mutableTx.vShieldedSpend.push_back(sd);
|
|
|
|
|
2022-02-17 18:14:37 -08:00
|
|
|
// The Orchard bundle builder always pads to two Actions, so we can just
|
|
|
|
// use an empty builder to create a dummy Orchard bundle.
|
|
|
|
uint256 orchardAnchor;
|
|
|
|
uint256 dataToBeSigned;
|
|
|
|
auto builder = orchard::Builder(true, true, orchardAnchor);
|
2022-03-08 11:19:04 -08:00
|
|
|
mutableTx.orchardBundle = builder.Build().value().ProveAndSign({}, dataToBeSigned).value();
|
2022-02-17 18:14:37 -08:00
|
|
|
orchardNullifier = mutableTx.orchardBundle.GetNullifiers()[0];
|
2021-06-28 19:17:35 -07:00
|
|
|
|
2018-04-24 16:16:43 -07:00
|
|
|
tx = CTransaction(mutableTx);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-09-16 16:27:06 -07:00
|
|
|
}
|
|
|
|
|
2018-08-01 09:31:09 -07:00
|
|
|
uint256 appendRandomSproutCommitment(SproutMerkleTree &tree)
|
2016-05-26 12:56:44 -07:00
|
|
|
{
|
2018-04-25 18:51:17 -07:00
|
|
|
libzcash::SproutSpendingKey k = libzcash::SproutSpendingKey::random();
|
|
|
|
libzcash::SproutPaymentAddress addr = k.address();
|
2016-05-26 12:56:44 -07:00
|
|
|
|
2018-04-24 21:20:01 -07:00
|
|
|
libzcash::SproutNote note(addr.a_pk, 0, uint256(), uint256());
|
2016-05-26 12:56:44 -07:00
|
|
|
|
2016-06-08 08:15:44 -07:00
|
|
|
auto cm = note.cm();
|
|
|
|
tree.append(cm);
|
|
|
|
return cm;
|
2016-05-26 12:56:44 -07:00
|
|
|
}
|
|
|
|
|
2022-03-09 06:52:39 -08:00
|
|
|
template<typename Tree> void AppendRandomLeaf(Tree &tree);
|
2017-06-07 12:03:17 -07:00
|
|
|
template<> void AppendRandomLeaf(SproutMerkleTree &tree) { tree.append(InsecureRand256()); }
|
|
|
|
template<> void AppendRandomLeaf(SaplingMerkleTree &tree) { tree.append(InsecureRand256()); }
|
2022-03-09 06:52:39 -08:00
|
|
|
template<> void AppendRandomLeaf(OrchardMerkleFrontier &tree) {
|
|
|
|
// OrchardMerkleFrontier only has APIs to append entire bundles, but
|
|
|
|
// fortunately the tests only require that the tree root change.
|
|
|
|
// TODO: Remove the need to create proofs by having a testing-only way to
|
|
|
|
// append a random leaf to OrchardMerkleFrontier.
|
|
|
|
uint256 orchardAnchor;
|
|
|
|
uint256 dataToBeSigned;
|
|
|
|
auto builder = orchard::Builder(true, true, orchardAnchor);
|
2022-03-08 11:19:04 -08:00
|
|
|
auto bundle = builder.Build().value().ProveAndSign({}, dataToBeSigned).value();
|
2022-03-09 06:52:39 -08:00
|
|
|
tree.AppendBundle(bundle);
|
|
|
|
}
|
|
|
|
|
2018-05-16 09:31:53 -07:00
|
|
|
template<typename Tree> bool GetAnchorAt(const CCoinsViewCacheTest &cache, const uint256 &rt, Tree &tree);
|
2018-08-01 09:31:09 -07:00
|
|
|
template<> bool GetAnchorAt(const CCoinsViewCacheTest &cache, const uint256 &rt, SproutMerkleTree &tree) { return cache.GetSproutAnchorAt(rt, tree); }
|
|
|
|
template<> bool GetAnchorAt(const CCoinsViewCacheTest &cache, const uint256 &rt, SaplingMerkleTree &tree) { return cache.GetSaplingAnchorAt(rt, tree); }
|
2022-03-09 06:52:39 -08:00
|
|
|
template<> bool GetAnchorAt(const CCoinsViewCacheTest &cache, const uint256 &rt, OrchardMerkleFrontier &tree) { return cache.GetOrchardAnchorAt(rt, tree); }
|
2018-05-16 09:31:53 -07:00
|
|
|
|
2022-04-25 20:59:33 -07:00
|
|
|
BOOST_FIXTURE_TEST_SUITE(coins_tests, BasicTestingSetup)
|
2016-05-26 12:56:44 -07:00
|
|
|
|
2018-04-24 16:16:43 -07:00
|
|
|
void checkNullifierCache(const CCoinsViewCacheTest &cache, const TxWithNullifiers &txWithNullifiers, bool shouldBeInCache) {
|
|
|
|
// Make sure the nullifiers have not gotten mixed up
|
2018-04-25 16:55:33 -07:00
|
|
|
BOOST_CHECK(!cache.GetNullifier(txWithNullifiers.sproutNullifier, SAPLING));
|
2022-02-17 18:14:37 -08:00
|
|
|
BOOST_CHECK(!cache.GetNullifier(txWithNullifiers.sproutNullifier, ORCHARD));
|
2018-04-25 16:55:33 -07:00
|
|
|
BOOST_CHECK(!cache.GetNullifier(txWithNullifiers.saplingNullifier, SPROUT));
|
2022-02-17 18:14:37 -08:00
|
|
|
BOOST_CHECK(!cache.GetNullifier(txWithNullifiers.saplingNullifier, ORCHARD));
|
|
|
|
BOOST_CHECK(!cache.GetNullifier(txWithNullifiers.orchardNullifier, SPROUT));
|
|
|
|
BOOST_CHECK(!cache.GetNullifier(txWithNullifiers.orchardNullifier, SAPLING));
|
2018-04-24 16:16:43 -07:00
|
|
|
// Check if the nullifiers either are or are not in the cache
|
2018-04-25 16:55:33 -07:00
|
|
|
bool containsSproutNullifier = cache.GetNullifier(txWithNullifiers.sproutNullifier, SPROUT);
|
|
|
|
bool containsSaplingNullifier = cache.GetNullifier(txWithNullifiers.saplingNullifier, SAPLING);
|
2022-02-17 18:14:37 -08:00
|
|
|
bool containsOrchardNullifier = cache.GetNullifier(txWithNullifiers.orchardNullifier, ORCHARD);
|
2018-04-24 16:16:43 -07:00
|
|
|
BOOST_CHECK(containsSproutNullifier == shouldBeInCache);
|
|
|
|
BOOST_CHECK(containsSaplingNullifier == shouldBeInCache);
|
2022-02-17 18:14:37 -08:00
|
|
|
BOOST_CHECK(containsOrchardNullifier == shouldBeInCache);
|
2018-04-24 16:16:43 -07:00
|
|
|
}
|
|
|
|
|
2016-07-16 15:45:41 -07:00
|
|
|
BOOST_AUTO_TEST_CASE(chained_joinsplits)
|
2016-05-26 13:12:45 -07:00
|
|
|
{
|
2018-05-16 09:31:53 -07:00
|
|
|
// TODO update this or add a similar test when the SaplingNote class exist
|
2016-05-26 13:12:45 -07:00
|
|
|
CCoinsViewTest base;
|
|
|
|
CCoinsViewCacheTest cache(&base);
|
|
|
|
|
2018-08-01 09:31:09 -07:00
|
|
|
SproutMerkleTree tree;
|
2016-05-26 13:12:45 -07:00
|
|
|
|
2016-07-16 15:45:41 -07:00
|
|
|
JSDescription js1;
|
|
|
|
js1.anchor = tree.root();
|
2018-05-16 09:31:53 -07:00
|
|
|
js1.commitments[0] = appendRandomSproutCommitment(tree);
|
|
|
|
js1.commitments[1] = appendRandomSproutCommitment(tree);
|
2016-05-26 13:12:45 -07:00
|
|
|
|
|
|
|
// Although it's not possible given our assumptions, if
|
2016-07-16 15:45:41 -07:00
|
|
|
// two joinsplits create the same treestate twice, we should
|
2016-05-26 13:12:45 -07:00
|
|
|
// still be able to anchor to it.
|
2016-07-16 15:45:41 -07:00
|
|
|
JSDescription js1b;
|
|
|
|
js1b.anchor = tree.root();
|
|
|
|
js1b.commitments[0] = js1.commitments[0];
|
|
|
|
js1b.commitments[1] = js1.commitments[1];
|
2016-05-26 13:12:45 -07:00
|
|
|
|
2016-07-16 15:45:41 -07:00
|
|
|
JSDescription js2;
|
|
|
|
JSDescription js3;
|
2016-05-26 13:12:45 -07:00
|
|
|
|
2016-07-16 15:45:41 -07:00
|
|
|
js2.anchor = tree.root();
|
|
|
|
js3.anchor = tree.root();
|
2016-05-26 13:12:45 -07:00
|
|
|
|
2018-05-16 09:31:53 -07:00
|
|
|
js2.commitments[0] = appendRandomSproutCommitment(tree);
|
|
|
|
js2.commitments[1] = appendRandomSproutCommitment(tree);
|
2016-05-26 13:12:45 -07:00
|
|
|
|
2018-05-16 09:31:53 -07:00
|
|
|
js3.commitments[0] = appendRandomSproutCommitment(tree);
|
|
|
|
js3.commitments[1] = appendRandomSproutCommitment(tree);
|
2016-05-26 13:12:45 -07:00
|
|
|
|
|
|
|
{
|
|
|
|
CMutableTransaction mtx;
|
2019-06-16 04:39:05 -07:00
|
|
|
mtx.vJoinSplit.push_back(js2);
|
2016-05-26 13:12:45 -07:00
|
|
|
|
2022-08-25 12:34:14 -07:00
|
|
|
BOOST_CHECK(cache.CheckShieldedRequirements(mtx) == tl::unexpected(UnsatisfiedShieldedReq::SproutUnknownAnchor));
|
2016-05-26 13:12:45 -07:00
|
|
|
}
|
|
|
|
|
2016-06-07 20:05:25 -07:00
|
|
|
{
|
2016-07-16 15:45:41 -07:00
|
|
|
// js2 is trying to anchor to js1 but js1
|
2016-06-08 08:15:44 -07:00
|
|
|
// appears afterwards -- not a permitted ordering
|
2016-06-07 20:05:25 -07:00
|
|
|
CMutableTransaction mtx;
|
2019-06-16 04:39:05 -07:00
|
|
|
mtx.vJoinSplit.push_back(js2);
|
|
|
|
mtx.vJoinSplit.push_back(js1);
|
2016-06-07 20:05:25 -07:00
|
|
|
|
2022-08-25 12:34:14 -07:00
|
|
|
BOOST_CHECK(cache.CheckShieldedRequirements(mtx) == tl::unexpected(UnsatisfiedShieldedReq::SproutUnknownAnchor));
|
2016-06-07 20:05:25 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
CMutableTransaction mtx;
|
2019-06-16 04:39:05 -07:00
|
|
|
mtx.vJoinSplit.push_back(js1);
|
|
|
|
mtx.vJoinSplit.push_back(js2);
|
2016-06-07 20:05:25 -07:00
|
|
|
|
2022-08-25 12:34:14 -07:00
|
|
|
BOOST_CHECK(cache.CheckShieldedRequirements(mtx).has_value());
|
2016-06-07 20:05:25 -07:00
|
|
|
}
|
|
|
|
|
2016-05-26 13:12:45 -07:00
|
|
|
{
|
|
|
|
CMutableTransaction mtx;
|
2019-06-16 04:39:05 -07:00
|
|
|
mtx.vJoinSplit.push_back(js1);
|
|
|
|
mtx.vJoinSplit.push_back(js2);
|
|
|
|
mtx.vJoinSplit.push_back(js3);
|
2016-05-26 13:12:45 -07:00
|
|
|
|
2022-08-25 12:34:14 -07:00
|
|
|
BOOST_CHECK(cache.CheckShieldedRequirements(mtx).has_value());
|
2016-05-26 13:12:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
CMutableTransaction mtx;
|
2019-06-16 04:39:05 -07:00
|
|
|
mtx.vJoinSplit.push_back(js1);
|
|
|
|
mtx.vJoinSplit.push_back(js1b);
|
|
|
|
mtx.vJoinSplit.push_back(js2);
|
|
|
|
mtx.vJoinSplit.push_back(js3);
|
2016-05-26 13:12:45 -07:00
|
|
|
|
2022-08-25 12:34:14 -07:00
|
|
|
BOOST_CHECK(cache.CheckShieldedRequirements(mtx).has_value());
|
2016-05-26 13:12:45 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-16 16:27:06 -07:00
|
|
|
static const unsigned int NUM_SIMULATION_ITERATIONS = 40000;
|
|
|
|
|
|
|
|
// This is a large randomized insert/remove simulation test on a variable-size
|
|
|
|
// stack of caches on top of CCoinsViewTest.
|
|
|
|
//
|
|
|
|
// It will randomly create/update/delete CCoins entries to a tip of caches, with
|
|
|
|
// txids picked from a limited list of random 256-bit hashes. Occasionally, a
|
|
|
|
// new tip is added to the stack of caches, or the tip is flushed and removed.
|
|
|
|
//
|
|
|
|
// During the process, booleans are kept to make sure that the randomized
|
|
|
|
// operation hits all branches.
|
|
|
|
BOOST_AUTO_TEST_CASE(coins_cache_simulation_test)
|
|
|
|
{
|
|
|
|
// Various coverage trackers.
|
|
|
|
bool removed_all_caches = false;
|
|
|
|
bool reached_4_caches = false;
|
|
|
|
bool added_an_entry = false;
|
|
|
|
bool removed_an_entry = false;
|
|
|
|
bool updated_an_entry = false;
|
|
|
|
bool found_an_entry = false;
|
|
|
|
bool missed_an_entry = false;
|
|
|
|
|
|
|
|
// A simple map to track what we expect the cache stack to represent.
|
|
|
|
std::map<uint256, CCoins> result;
|
|
|
|
|
|
|
|
// The cache stack.
|
|
|
|
CCoinsViewTest base; // A CCoinsViewTest at the bottom.
|
2015-05-03 16:31:11 -07:00
|
|
|
std::vector<CCoinsViewCacheTest*> stack; // A stack of CCoinsViewCaches on top.
|
|
|
|
stack.push_back(new CCoinsViewCacheTest(&base)); // Start with one cache.
|
2014-09-16 16:27:06 -07:00
|
|
|
|
|
|
|
// Use a limited set of random transaction ids, so we do test overwriting entries.
|
|
|
|
std::vector<uint256> txids;
|
|
|
|
txids.resize(NUM_SIMULATION_ITERATIONS / 8);
|
|
|
|
for (unsigned int i = 0; i < txids.size(); i++) {
|
2017-06-07 12:03:17 -07:00
|
|
|
txids[i] = InsecureRand256();
|
2014-09-16 16:27:06 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < NUM_SIMULATION_ITERATIONS; i++) {
|
|
|
|
// Do a random modification.
|
|
|
|
{
|
2017-06-07 12:03:17 -07:00
|
|
|
uint256 txid = txids[InsecureRandRange(txids.size())]; // txid we're going to modify in this iteration.
|
2014-09-16 16:27:06 -07:00
|
|
|
CCoins& coins = result[txid];
|
|
|
|
CCoinsModifier entry = stack.back()->ModifyCoins(txid);
|
|
|
|
BOOST_CHECK(coins == *entry);
|
2017-06-07 12:03:17 -07:00
|
|
|
if (InsecureRandRange(5) == 0 || coins.IsPruned()) {
|
2014-09-16 16:27:06 -07:00
|
|
|
if (coins.IsPruned()) {
|
|
|
|
added_an_entry = true;
|
|
|
|
} else {
|
|
|
|
updated_an_entry = true;
|
|
|
|
}
|
2017-06-07 12:03:17 -07:00
|
|
|
coins.nVersion = InsecureRand32();
|
2014-09-16 16:27:06 -07:00
|
|
|
coins.vout.resize(1);
|
2017-06-07 12:03:17 -07:00
|
|
|
coins.vout[0].nValue = InsecureRand32();
|
2014-09-16 16:27:06 -07:00
|
|
|
*entry = coins;
|
|
|
|
} else {
|
|
|
|
coins.Clear();
|
|
|
|
entry->Clear();
|
|
|
|
removed_an_entry = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Once every 1000 iterations and at the end, verify the full cache.
|
2017-06-07 12:03:17 -07:00
|
|
|
if (InsecureRandRange(1000) == 1 || i == NUM_SIMULATION_ITERATIONS - 1) {
|
2014-09-16 16:27:06 -07:00
|
|
|
for (std::map<uint256, CCoins>::iterator it = result.begin(); it != result.end(); it++) {
|
|
|
|
const CCoins* coins = stack.back()->AccessCoins(it->first);
|
|
|
|
if (coins) {
|
|
|
|
BOOST_CHECK(*coins == it->second);
|
|
|
|
found_an_entry = true;
|
|
|
|
} else {
|
|
|
|
BOOST_CHECK(it->second.IsPruned());
|
|
|
|
missed_an_entry = true;
|
|
|
|
}
|
|
|
|
}
|
2017-06-01 18:18:57 -07:00
|
|
|
for (const CCoinsViewCacheTest *test : stack) {
|
2015-05-03 16:31:11 -07:00
|
|
|
test->SelfTest();
|
|
|
|
}
|
2014-09-16 16:27:06 -07:00
|
|
|
}
|
|
|
|
|
2017-06-07 12:03:17 -07:00
|
|
|
if (InsecureRandRange(100) == 0) {
|
2014-09-16 16:27:06 -07:00
|
|
|
// Every 100 iterations, change the cache stack.
|
2017-06-07 12:03:17 -07:00
|
|
|
if (stack.size() > 0 && InsecureRandBool() == 0) {
|
2014-09-16 16:27:06 -07:00
|
|
|
stack.back()->Flush();
|
|
|
|
delete stack.back();
|
|
|
|
stack.pop_back();
|
|
|
|
}
|
2017-06-07 12:03:17 -07:00
|
|
|
if (stack.size() == 0 || (stack.size() < 4 && InsecureRandBool())) {
|
2014-09-16 16:27:06 -07:00
|
|
|
CCoinsView* tip = &base;
|
|
|
|
if (stack.size() > 0) {
|
|
|
|
tip = stack.back();
|
|
|
|
} else {
|
|
|
|
removed_all_caches = true;
|
|
|
|
}
|
2015-05-03 16:31:11 -07:00
|
|
|
stack.push_back(new CCoinsViewCacheTest(tip));
|
2014-09-16 16:27:06 -07:00
|
|
|
if (stack.size() == 4) {
|
|
|
|
reached_4_caches = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clean up the stack.
|
|
|
|
while (stack.size() > 0) {
|
|
|
|
delete stack.back();
|
|
|
|
stack.pop_back();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify coverage.
|
|
|
|
BOOST_CHECK(removed_all_caches);
|
|
|
|
BOOST_CHECK(reached_4_caches);
|
|
|
|
BOOST_CHECK(added_an_entry);
|
|
|
|
BOOST_CHECK(removed_an_entry);
|
|
|
|
BOOST_CHECK(updated_an_entry);
|
|
|
|
BOOST_CHECK(found_an_entry);
|
|
|
|
BOOST_CHECK(missed_an_entry);
|
|
|
|
}
|
|
|
|
|
2016-06-13 10:58:10 -07:00
|
|
|
BOOST_AUTO_TEST_CASE(coins_coinbase_spends)
|
|
|
|
{
|
|
|
|
CCoinsViewTest base;
|
|
|
|
CCoinsViewCacheTest cache(&base);
|
|
|
|
|
|
|
|
// Create coinbase transaction
|
|
|
|
CMutableTransaction mtx;
|
|
|
|
mtx.vin.resize(1);
|
|
|
|
mtx.vin[0].scriptSig = CScript() << OP_1;
|
|
|
|
mtx.vin[0].nSequence = 0;
|
|
|
|
mtx.vout.resize(1);
|
|
|
|
mtx.vout[0].nValue = 500;
|
|
|
|
mtx.vout[0].scriptPubKey = CScript() << OP_1;
|
|
|
|
|
|
|
|
CTransaction tx(mtx);
|
|
|
|
|
|
|
|
BOOST_CHECK(tx.IsCoinBase());
|
|
|
|
|
|
|
|
CValidationState state;
|
2016-04-29 20:45:20 -07:00
|
|
|
UpdateCoins(tx, cache, 100);
|
2016-06-13 10:58:10 -07:00
|
|
|
|
|
|
|
// Create coinbase spend
|
|
|
|
CMutableTransaction mtx2;
|
|
|
|
mtx2.vin.resize(1);
|
2016-08-30 12:49:38 -07:00
|
|
|
mtx2.vin[0].prevout = COutPoint(tx.GetHash(), 0);
|
2016-06-13 10:58:10 -07:00
|
|
|
mtx2.vin[0].scriptSig = CScript() << OP_1;
|
|
|
|
mtx2.vin[0].nSequence = 0;
|
|
|
|
|
|
|
|
{
|
|
|
|
CTransaction tx2(mtx2);
|
2015-04-24 07:45:16 -07:00
|
|
|
BOOST_CHECK(Consensus::CheckTxInputs(tx2, state, cache, 100+COINBASE_MATURITY, Params().GetConsensus()));
|
2016-06-13 10:58:10 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
mtx2.vout.resize(1);
|
|
|
|
mtx2.vout[0].nValue = 500;
|
|
|
|
mtx2.vout[0].scriptPubKey = CScript() << OP_1;
|
|
|
|
|
|
|
|
{
|
|
|
|
CTransaction tx2(mtx2);
|
2015-04-24 07:45:16 -07:00
|
|
|
BOOST_CHECK(!Consensus::CheckTxInputs(tx2, state, cache, 100+COINBASE_MATURITY, Params().GetConsensus()));
|
2016-06-13 10:58:10 -07:00
|
|
|
BOOST_CHECK(state.GetRejectReason() == "bad-txns-coinbase-spend-has-transparent-outputs");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-11 17:36:16 -08:00
|
|
|
// This test is similar to the previous test
|
|
|
|
// except the emphasis is on testing the functionality of UpdateCoins
|
|
|
|
// random txs are created and UpdateCoins is used to update the cache stack
|
|
|
|
BOOST_AUTO_TEST_CASE(updatecoins_simulation_test)
|
|
|
|
{
|
|
|
|
// A simple map to track what we expect the cache stack to represent.
|
|
|
|
std::map<uint256, CCoins> result;
|
|
|
|
|
|
|
|
// The cache stack.
|
|
|
|
CCoinsViewTest base; // A CCoinsViewTest at the bottom.
|
|
|
|
std::vector<CCoinsViewCacheTest*> stack; // A stack of CCoinsViewCaches on top.
|
|
|
|
stack.push_back(new CCoinsViewCacheTest(&base)); // Start with one cache.
|
|
|
|
|
|
|
|
// Track the txids we've used and whether they have been spent or not
|
|
|
|
std::map<uint256, CAmount> coinbaseids;
|
|
|
|
std::set<uint256> alltxids;
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < NUM_SIMULATION_ITERATIONS; i++) {
|
|
|
|
{
|
|
|
|
CMutableTransaction tx;
|
|
|
|
tx.vin.resize(1);
|
|
|
|
tx.vout.resize(1);
|
2018-05-30 20:54:55 -07:00
|
|
|
tx.vout[0].nValue = i; //Keep txs unique
|
2017-06-07 12:03:17 -07:00
|
|
|
unsigned int height = InsecureRand32();
|
2015-11-11 17:36:16 -08:00
|
|
|
|
|
|
|
// 1/10 times create a coinbase
|
2017-06-07 12:03:17 -07:00
|
|
|
if (InsecureRandRange(10) == 0 || coinbaseids.size() < 10) {
|
2018-05-30 20:54:55 -07:00
|
|
|
coinbaseids[tx.GetHash()] = tx.vout[0].nValue;
|
2015-11-11 17:36:16 -08:00
|
|
|
assert(CTransaction(tx).IsCoinBase());
|
|
|
|
}
|
|
|
|
// 9/10 times create a regular tx
|
|
|
|
else {
|
|
|
|
uint256 prevouthash;
|
|
|
|
// equally likely to spend coinbase or non coinbase
|
2017-06-07 12:03:17 -07:00
|
|
|
std::set<uint256>::iterator txIt = alltxids.lower_bound(InsecureRand256());
|
2015-11-11 17:36:16 -08:00
|
|
|
if (txIt == alltxids.end()) {
|
|
|
|
txIt = alltxids.begin();
|
|
|
|
}
|
|
|
|
prevouthash = *txIt;
|
|
|
|
|
|
|
|
// Construct the tx to spend the coins of prevouthash
|
|
|
|
tx.vin[0].prevout.hash = prevouthash;
|
|
|
|
tx.vin[0].prevout.n = 0;
|
|
|
|
|
|
|
|
// Update the expected result of prevouthash to know these coins are spent
|
|
|
|
CCoins& oldcoins = result[prevouthash];
|
|
|
|
oldcoins.Clear();
|
|
|
|
|
|
|
|
alltxids.erase(prevouthash);
|
|
|
|
coinbaseids.erase(prevouthash);
|
|
|
|
|
|
|
|
assert(!CTransaction(tx).IsCoinBase());
|
|
|
|
}
|
|
|
|
// Track this tx to possibly spend later
|
|
|
|
alltxids.insert(tx.GetHash());
|
|
|
|
|
|
|
|
// Update the expected result to know about the new output coins
|
|
|
|
CCoins &coins = result[tx.GetHash()];
|
|
|
|
coins.FromTx(tx, height);
|
|
|
|
|
|
|
|
UpdateCoins(tx, *(stack.back()), height);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Once every 1000 iterations and at the end, verify the full cache.
|
2017-06-07 12:03:17 -07:00
|
|
|
if (InsecureRandRange(1000) == 1 || i == NUM_SIMULATION_ITERATIONS - 1) {
|
2015-11-11 17:36:16 -08:00
|
|
|
for (std::map<uint256, CCoins>::iterator it = result.begin(); it != result.end(); it++) {
|
|
|
|
const CCoins* coins = stack.back()->AccessCoins(it->first);
|
|
|
|
if (coins) {
|
|
|
|
BOOST_CHECK(*coins == it->second);
|
|
|
|
} else {
|
|
|
|
BOOST_CHECK(it->second.IsPruned());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-07 12:03:17 -07:00
|
|
|
if (InsecureRandRange(100) == 0) {
|
2015-11-11 17:36:16 -08:00
|
|
|
// Every 100 iterations, change the cache stack.
|
2017-06-07 12:03:17 -07:00
|
|
|
if (stack.size() > 0 && InsecureRandBool() == 0) {
|
2015-11-11 17:36:16 -08:00
|
|
|
stack.back()->Flush();
|
|
|
|
delete stack.back();
|
|
|
|
stack.pop_back();
|
|
|
|
}
|
2017-06-07 12:03:17 -07:00
|
|
|
if (stack.size() == 0 || (stack.size() < 4 && InsecureRandBool())) {
|
2015-11-11 17:36:16 -08:00
|
|
|
CCoinsView* tip = &base;
|
|
|
|
if (stack.size() > 0) {
|
|
|
|
tip = stack.back();
|
|
|
|
}
|
|
|
|
stack.push_back(new CCoinsViewCacheTest(tip));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clean up the stack.
|
|
|
|
while (stack.size() > 0) {
|
|
|
|
delete stack.back();
|
|
|
|
stack.pop_back();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-25 05:05:36 -07:00
|
|
|
BOOST_AUTO_TEST_CASE(ccoins_serialization)
|
|
|
|
{
|
|
|
|
// Good example
|
|
|
|
CDataStream ss1(ParseHex("0104835800816115944e077fe7c803cfa57f29b36bf87c1d358bb85e"), SER_DISK, CLIENT_VERSION);
|
|
|
|
CCoins cc1;
|
|
|
|
ss1 >> cc1;
|
|
|
|
BOOST_CHECK_EQUAL(cc1.nVersion, 1);
|
|
|
|
BOOST_CHECK_EQUAL(cc1.fCoinBase, false);
|
|
|
|
BOOST_CHECK_EQUAL(cc1.nHeight, 203998);
|
|
|
|
BOOST_CHECK_EQUAL(cc1.vout.size(), 2);
|
|
|
|
BOOST_CHECK_EQUAL(cc1.IsAvailable(0), false);
|
|
|
|
BOOST_CHECK_EQUAL(cc1.IsAvailable(1), true);
|
|
|
|
BOOST_CHECK_EQUAL(cc1.vout[1].nValue, 60000000000ULL);
|
|
|
|
BOOST_CHECK_EQUAL(HexStr(cc1.vout[1].scriptPubKey), HexStr(GetScriptForDestination(CKeyID(uint160(ParseHex("816115944e077fe7c803cfa57f29b36bf87c1d35"))))));
|
|
|
|
|
|
|
|
// Good example
|
|
|
|
CDataStream ss2(ParseHex("0109044086ef97d5790061b01caab50f1b8e9c50a5057eb43c2d9563a4eebbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa486af3b"), SER_DISK, CLIENT_VERSION);
|
|
|
|
CCoins cc2;
|
|
|
|
ss2 >> cc2;
|
|
|
|
BOOST_CHECK_EQUAL(cc2.nVersion, 1);
|
|
|
|
BOOST_CHECK_EQUAL(cc2.fCoinBase, true);
|
|
|
|
BOOST_CHECK_EQUAL(cc2.nHeight, 120891);
|
|
|
|
BOOST_CHECK_EQUAL(cc2.vout.size(), 17);
|
|
|
|
for (int i = 0; i < 17; i++) {
|
|
|
|
BOOST_CHECK_EQUAL(cc2.IsAvailable(i), i == 4 || i == 16);
|
|
|
|
}
|
|
|
|
BOOST_CHECK_EQUAL(cc2.vout[4].nValue, 234925952);
|
|
|
|
BOOST_CHECK_EQUAL(HexStr(cc2.vout[4].scriptPubKey), HexStr(GetScriptForDestination(CKeyID(uint160(ParseHex("61b01caab50f1b8e9c50a5057eb43c2d9563a4ee"))))));
|
|
|
|
BOOST_CHECK_EQUAL(cc2.vout[16].nValue, 110397);
|
|
|
|
BOOST_CHECK_EQUAL(HexStr(cc2.vout[16].scriptPubKey), HexStr(GetScriptForDestination(CKeyID(uint160(ParseHex("8c988f1a4a4de2161e0f50aac7f17e7f9555caa4"))))));
|
|
|
|
|
|
|
|
// Smallest possible example
|
|
|
|
CDataStream ssx(SER_DISK, CLIENT_VERSION);
|
|
|
|
BOOST_CHECK_EQUAL(HexStr(ssx.begin(), ssx.end()), "");
|
|
|
|
|
|
|
|
CDataStream ss3(ParseHex("0002000600"), SER_DISK, CLIENT_VERSION);
|
|
|
|
CCoins cc3;
|
|
|
|
ss3 >> cc3;
|
|
|
|
BOOST_CHECK_EQUAL(cc3.nVersion, 0);
|
|
|
|
BOOST_CHECK_EQUAL(cc3.fCoinBase, false);
|
|
|
|
BOOST_CHECK_EQUAL(cc3.nHeight, 0);
|
|
|
|
BOOST_CHECK_EQUAL(cc3.vout.size(), 1);
|
|
|
|
BOOST_CHECK_EQUAL(cc3.IsAvailable(0), true);
|
|
|
|
BOOST_CHECK_EQUAL(cc3.vout[0].nValue, 0);
|
|
|
|
BOOST_CHECK_EQUAL(cc3.vout[0].scriptPubKey.size(), 0);
|
|
|
|
|
|
|
|
// scriptPubKey that ends beyond the end of the stream
|
|
|
|
CDataStream ss4(ParseHex("0002000800"), SER_DISK, CLIENT_VERSION);
|
|
|
|
try {
|
|
|
|
CCoins cc4;
|
|
|
|
ss4 >> cc4;
|
|
|
|
BOOST_CHECK_MESSAGE(false, "We should have thrown");
|
|
|
|
} catch (const std::ios_base::failure& e) {
|
|
|
|
}
|
|
|
|
|
|
|
|
// Very large scriptPubKey (3*10^9 bytes) past the end of the stream
|
|
|
|
CDataStream tmp(SER_DISK, CLIENT_VERSION);
|
|
|
|
uint64_t x = 3000000000ULL;
|
|
|
|
tmp << VARINT(x);
|
|
|
|
BOOST_CHECK_EQUAL(HexStr(tmp.begin(), tmp.end()), "8a95c0bb00");
|
|
|
|
CDataStream ss5(ParseHex("0002008a95c0bb0000"), SER_DISK, CLIENT_VERSION);
|
|
|
|
try {
|
|
|
|
CCoins cc5;
|
|
|
|
ss5 >> cc5;
|
|
|
|
BOOST_CHECK_MESSAGE(false, "We should have thrown");
|
|
|
|
} catch (const std::ios_base::failure& e) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-16 16:27:06 -07:00
|
|
|
BOOST_AUTO_TEST_SUITE_END()
|