Browse Source

Add mapSerials infrastructure to CCoinsView.

This adds the TXDB/CCoinsViewCache primitives necessary for
writing consensus rules for mapSerials later.
pull/145/head
Sean Bowe 9 years ago
parent
commit
45d6bee945
  1. 63
      src/coins.cpp
  2. 31
      src/coins.h
  3. 50
      src/test/coins_tests.cpp
  4. 30
      src/txdb.cpp
  5. 4
      src/txdb.h

63
src/coins.cpp

@ -41,6 +41,7 @@ bool CCoins::Spend(uint32_t nPos)
return true;
}
bool CCoinsView::GetAnchorAt(const uint256 &rt, libzerocash::IncrementalMerkleTree &tree) const { return false; }
bool CCoinsView::GetSerial(const uint256 &serial) const { return false; }
bool CCoinsView::GetCoins(const uint256 &txid, CCoins &coins) const { return false; }
bool CCoinsView::HaveCoins(const uint256 &txid) const { return false; }
uint256 CCoinsView::GetBestBlock() const { return uint256(); }
@ -48,13 +49,15 @@ uint256 CCoinsView::GetBestAnchor() const { return uint256(); };
bool CCoinsView::BatchWrite(CCoinsMap &mapCoins,
const uint256 &hashBlock,
const uint256 &hashAnchor,
CAnchorsMap &mapAnchors) { return false; }
CAnchorsMap &mapAnchors,
CSerialsMap &mapSerials) { return false; }
bool CCoinsView::GetStats(CCoinsStats &stats) const { return false; }
CCoinsViewBacked::CCoinsViewBacked(CCoinsView *viewIn) : base(viewIn) { }
bool CCoinsViewBacked::GetAnchorAt(const uint256 &rt, libzerocash::IncrementalMerkleTree &tree) const { return base->GetAnchorAt(rt, tree); }
bool CCoinsViewBacked::GetSerial(const uint256 &serial) const { return base->GetSerial(serial); }
bool CCoinsViewBacked::GetCoins(const uint256 &txid, CCoins &coins) const { return base->GetCoins(txid, coins); }
bool CCoinsViewBacked::HaveCoins(const uint256 &txid) const { return base->HaveCoins(txid); }
uint256 CCoinsViewBacked::GetBestBlock() const { return base->GetBestBlock(); }
@ -63,7 +66,8 @@ void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; }
bool CCoinsViewBacked::BatchWrite(CCoinsMap &mapCoins,
const uint256 &hashBlock,
const uint256 &hashAnchor,
CAnchorsMap &mapAnchors) { return base->BatchWrite(mapCoins, hashBlock, hashAnchor, mapAnchors); }
CAnchorsMap &mapAnchors,
CSerialsMap &mapSerials) { return base->BatchWrite(mapCoins, hashBlock, hashAnchor, mapAnchors, mapSerials); }
bool CCoinsViewBacked::GetStats(CCoinsStats &stats) const { return base->GetStats(stats); }
CCoinsKeyHasher::CCoinsKeyHasher() : salt(GetRandHash()) {}
@ -121,6 +125,21 @@ bool CCoinsViewCache::GetAnchorAt(const uint256 &rt, libzerocash::IncrementalMer
return true;
}
bool CCoinsViewCache::GetSerial(const uint256 &serial) const {
CSerialsMap::iterator it = cacheSerials.find(serial);
if (it != cacheSerials.end())
return it->second.entered;
CSerialsCacheEntry entry;
bool tmp = base->GetSerial(serial);
entry.entered = tmp;
cacheSerials.insert(std::make_pair(serial, entry));
// TODO: cache usage
return tmp;
}
void CCoinsViewCache::PushAnchor(const libzerocash::IncrementalMerkleTree &tree) {
std::vector<unsigned char> newrt_v(32);
@ -161,6 +180,12 @@ void CCoinsViewCache::PopAnchor(const uint256 &newrt) {
}
}
void CCoinsViewCache::SetSerial(const uint256 &serial, bool spent) {
std::pair<CSerialsMap::iterator, bool> ret = cacheSerials.insert(std::make_pair(serial, CSerialsCacheEntry()));
ret.first->second.entered = spent;
ret.first->second.flags |= CSerialsCacheEntry::DIRTY;
}
bool CCoinsViewCache::GetCoins(const uint256 &txid, CCoins &coins) const {
CCoinsMap::const_iterator it = FetchCoins(txid);
if (it != cacheCoins.end()) {
@ -229,7 +254,8 @@ void CCoinsViewCache::SetBestBlock(const uint256 &hashBlockIn) {
bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins,
const uint256 &hashBlockIn,
const uint256 &hashAnchorIn,
CAnchorsMap &mapAnchors) {
CAnchorsMap &mapAnchors,
CSerialsMap &mapSerials) {
assert(!hasModifier);
for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) {
if (it->second.flags & CCoinsCacheEntry::DIRTY) { // Ignore non-dirty entries (optimization).
@ -294,15 +320,44 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins,
CAnchorsMap::iterator itOld = child_it++;
mapAnchors.erase(itOld);
}
for (CSerialsMap::iterator child_it = mapSerials.begin(); child_it != mapSerials.end();)
{
if (child_it->second.flags & CSerialsCacheEntry::DIRTY) { // Ignore non-dirty entries (optimization).
CSerialsMap::iterator parent_it = cacheSerials.find(child_it->first);
if (parent_it == cacheSerials.end()) {
if (child_it->second.entered) {
// Parent doesn't have an entry, but child has a SPENT serial.
// Move the spent serial up.
CSerialsCacheEntry& entry = cacheSerials[child_it->first];
entry.entered = true;
entry.flags = CSerialsCacheEntry::DIRTY;
// TODO: cache usage
}
} else {
if (parent_it->second.entered != child_it->second.entered) {
parent_it->second.entered = child_it->second.entered;
parent_it->second.flags |= CSerialsCacheEntry::DIRTY;
}
}
}
CSerialsMap::iterator itOld = child_it++;
mapSerials.erase(itOld);
}
hashAnchor = hashAnchorIn;
hashBlock = hashBlockIn;
return true;
}
bool CCoinsViewCache::Flush() {
bool fOk = base->BatchWrite(cacheCoins, hashBlock, hashAnchor, cacheAnchors);
bool fOk = base->BatchWrite(cacheCoins, hashBlock, hashAnchor, cacheAnchors, cacheSerials);
cacheCoins.clear();
cacheAnchors.clear();
cacheSerials.clear();
cachedCoinsUsage = 0;
return fOk;
}

31
src/coins.h

@ -311,8 +311,21 @@ struct CAnchorsCacheEntry
CAnchorsCacheEntry() : entered(false), flags(0), tree(INCREMENTAL_MERKLE_TREE_DEPTH) {}
};
struct CSerialsCacheEntry
{
bool entered; // If the serial is spent or not
unsigned char flags;
enum Flags {
DIRTY = (1 << 0), // This cache entry is potentially different from the version in the parent view.
};
CSerialsCacheEntry() : entered(false), flags(0) {}
};
typedef boost::unordered_map<uint256, CCoinsCacheEntry, CCoinsKeyHasher> CCoinsMap;
typedef boost::unordered_map<uint256, CAnchorsCacheEntry, CCoinsKeyHasher> CAnchorsMap;
typedef boost::unordered_map<uint256, CSerialsCacheEntry, CCoinsKeyHasher> CSerialsMap;
struct CCoinsStats
{
@ -335,6 +348,9 @@ public:
//! Retrieve the tree at a particular anchored root in the chain
virtual bool GetAnchorAt(const uint256 &rt, libzerocash::IncrementalMerkleTree &tree) const;
//! Determine whether a serial is spent or not
virtual bool GetSerial(const uint256 &serial) const;
//! Retrieve the CCoins (unspent transaction outputs) for a given txid
virtual bool GetCoins(const uint256 &txid, CCoins &coins) const;
@ -353,7 +369,8 @@ public:
virtual bool BatchWrite(CCoinsMap &mapCoins,
const uint256 &hashBlock,
const uint256 &hashAnchor,
CAnchorsMap &mapAnchors);
CAnchorsMap &mapAnchors,
CSerialsMap &mapSerials);
//! Calculate statistics about the unspent transaction output set
virtual bool GetStats(CCoinsStats &stats) const;
@ -372,6 +389,7 @@ protected:
public:
CCoinsViewBacked(CCoinsView *viewIn);
bool GetAnchorAt(const uint256 &rt, libzerocash::IncrementalMerkleTree &tree) const;
bool GetSerial(const uint256 &serial) const;
bool GetCoins(const uint256 &txid, CCoins &coins) const;
bool HaveCoins(const uint256 &txid) const;
uint256 GetBestBlock() const;
@ -380,7 +398,8 @@ public:
bool BatchWrite(CCoinsMap &mapCoins,
const uint256 &hashBlock,
const uint256 &hashAnchor,
CAnchorsMap &mapAnchors);
CAnchorsMap &mapAnchors,
CSerialsMap &mapSerials);
bool GetStats(CCoinsStats &stats) const;
};
@ -423,6 +442,7 @@ protected:
mutable CCoinsMap cacheCoins;
mutable uint256 hashAnchor;
mutable CAnchorsMap cacheAnchors;
mutable CSerialsMap cacheSerials;
/* Cached dynamic memory usage for the inner CCoins objects. */
mutable size_t cachedCoinsUsage;
@ -433,6 +453,7 @@ public:
// Standard CCoinsView methods
bool GetAnchorAt(const uint256 &rt, libzerocash::IncrementalMerkleTree &tree) const;
bool GetSerial(const uint256 &serial) const;
bool GetCoins(const uint256 &txid, CCoins &coins) const;
bool HaveCoins(const uint256 &txid) const;
uint256 GetBestBlock() const;
@ -441,7 +462,8 @@ public:
bool BatchWrite(CCoinsMap &mapCoins,
const uint256 &hashBlock,
const uint256 &hashAnchor,
CAnchorsMap &mapAnchors);
CAnchorsMap &mapAnchors,
CSerialsMap &mapSerials);
// Adds the tree to mapAnchors and sets the current commitment
@ -452,6 +474,9 @@ public:
// the new current root.
void PopAnchor(const uint256 &rt);
// Marks a serial as spent or not.
void SetSerial(const uint256 &serial, bool spent);
/**
* Return a pointer to CCoins in the cache, or NULL if not found. This is
* more efficient than GetCoins. Modifications to other cache entries are

50
src/test/coins_tests.cpp

@ -21,6 +21,7 @@ class CCoinsViewTest : public CCoinsView
uint256 hashBestAnchor_;
std::map<uint256, CCoins> map_;
std::map<uint256, libzerocash::IncrementalMerkleTree> mapAnchors_;
std::map<uint256, bool> mapSerials_;
public:
bool GetAnchorAt(const uint256& rt, libzerocash::IncrementalMerkleTree &tree) const {
@ -39,6 +40,19 @@ public:
}
}
bool GetSerial(const uint256 &serial) const
{
std::map<uint256, bool>::const_iterator it = mapSerials_.find(serial);
if (it == mapSerials_.end()) {
return false;
} else {
// The map shouldn't contain any false entries.
assert(it->second);
return true;
}
}
uint256 GetBestAnchor() const { return hashBestAnchor_; }
bool GetCoins(const uint256& txid, CCoins& coins) const
@ -66,7 +80,8 @@ public:
bool BatchWrite(CCoinsMap& mapCoins,
const uint256& hashBlock,
const uint256& hashAnchor,
CAnchorsMap& mapAnchors)
CAnchorsMap& mapAnchors,
CSerialsMap& mapSerials)
{
for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end(); ) {
map_[it->first] = it->second.coins;
@ -84,8 +99,17 @@ public:
}
mapAnchors.erase(it++);
}
for (CSerialsMap::iterator it = mapSerials.begin(); it != mapSerials.end(); ) {
if (it->second.entered) {
mapSerials_[it->first] = true;
} else {
mapSerials_.erase(it->first);
}
mapSerials.erase(it++);
}
mapCoins.clear();
mapAnchors.clear();
mapSerials.clear();
hashBestBlock_ = hashBlock;
hashBestAnchor_ = hashAnchor;
return true;
@ -113,6 +137,30 @@ public:
}
BOOST_AUTO_TEST_CASE(serials_test)
{
CCoinsViewTest base;
CCoinsViewCacheTest cache(&base);
uint256 myserial = GetRandHash();
BOOST_CHECK(!cache.GetSerial(myserial));
cache.SetSerial(myserial, true);
BOOST_CHECK(cache.GetSerial(myserial));
cache.Flush();
CCoinsViewCacheTest cache2(&base);
BOOST_CHECK(cache2.GetSerial(myserial));
cache2.SetSerial(myserial, false);
BOOST_CHECK(!cache2.GetSerial(myserial));
cache2.Flush();
CCoinsViewCacheTest cache3(&base);
BOOST_CHECK(!cache3.GetSerial(myserial));
}
void appendRandomCommitment(IncrementalMerkleTree &tree)
{
Address addr = Address::CreateNewRandomAddress();

30
src/txdb.cpp

@ -18,6 +18,7 @@
using namespace std;
static const char DB_ANCHOR = 'A';
static const char DB_SERIAL = 's';
static const char DB_COINS = 'c';
static const char DB_BLOCK_FILES = 'f';
static const char DB_TXINDEX = 't';
@ -42,6 +43,13 @@ void static BatchWriteAnchor(CLevelDBBatch &batch,
}
}
void static BatchWriteSerial(CLevelDBBatch &batch, const uint256 &serial, const bool &entered) {
if (!entered)
batch.Erase(make_pair(DB_SERIAL, serial));
else
batch.Write(make_pair(DB_SERIAL, serial), true);
}
void static BatchWriteCoins(CLevelDBBatch &batch, const uint256 &hash, const CCoins &coins) {
if (coins.IsPruned())
batch.Erase(make_pair(DB_COINS, hash));
@ -81,6 +89,16 @@ bool CCoinsViewDB::GetAnchorAt(const uint256 &rt, libzerocash::IncrementalMerkle
return true;
}
bool CCoinsViewDB::GetSerial(const uint256 &serial) const {
bool spent = false;
bool read = db.Read(make_pair(DB_SERIAL, serial), spent);
// We should never read false from the database.
assert(spent != read);
return spent;
}
bool CCoinsViewDB::GetCoins(const uint256 &txid, CCoins &coins) const {
return db.Read(make_pair(DB_COINS, txid), coins);
}
@ -106,7 +124,8 @@ uint256 CCoinsViewDB::GetBestAnchor() const {
bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins,
const uint256 &hashBlock,
const uint256 &hashAnchor,
CAnchorsMap &mapAnchors) {
CAnchorsMap &mapAnchors,
CSerialsMap &mapSerials) {
CLevelDBBatch batch;
size_t count = 0;
size_t changed = 0;
@ -129,6 +148,15 @@ bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins,
mapAnchors.erase(itOld);
}
for (CSerialsMap::iterator it = mapSerials.begin(); it != mapSerials.end();) {
if (it->second.flags & CSerialsCacheEntry::DIRTY) {
BatchWriteSerial(batch, it->first, it->second.entered);
// TODO: changed++?
}
CSerialsMap::iterator itOld = it++;
mapSerials.erase(itOld);
}
if (!hashBlock.IsNull())
BatchWriteHashBestChain(batch, hashBlock);
if (!hashAnchor.IsNull())

4
src/txdb.h

@ -37,6 +37,7 @@ public:
CCoinsViewDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false);
bool GetAnchorAt(const uint256 &rt, libzerocash::IncrementalMerkleTree &tree) const;
bool GetSerial(const uint256 &serial) const;
bool GetCoins(const uint256 &txid, CCoins &coins) const;
bool HaveCoins(const uint256 &txid) const;
uint256 GetBestBlock() const;
@ -44,7 +45,8 @@ public:
bool BatchWrite(CCoinsMap &mapCoins,
const uint256 &hashBlock,
const uint256 &hashAnchor,
CAnchorsMap &mapAnchors);
CAnchorsMap &mapAnchors,
CSerialsMap &mapSerials);
bool GetStats(CCoinsStats &stats) const;
};

Loading…
Cancel
Save