Browse Source

Implemented writing to DB.

cn
Jianping Wu 6 years ago
parent
commit
49de399c30
  1. 33
      src/coins.cpp
  2. 11
      src/coins.h
  3. 68
      src/keva/common.cpp
  4. 16
      src/keva/common.h
  5. 54
      src/keva/main.cpp
  6. 2
      src/script/keva.cpp
  7. 29
      src/script/keva.h
  8. 4
      src/test/coins_tests.cpp
  9. 131
      src/txdb.cpp
  10. 5
      src/txdb.h
  11. 86
      src/wallet/rpckeva.cpp

33
src/coins.cpp

@ -13,7 +13,8 @@ std::vector<uint256> CCoinsView::GetHeadBlocks() const { return std::vector<uint
bool CCoinsView::HasNamespace(const valtype &nameSpace) const { return false; } bool CCoinsView::HasNamespace(const valtype &nameSpace) const { return false; }
bool CCoinsView::GetName(const valtype &nameSpace, const valtype &key, CKevaData &data) const { return false; } bool CCoinsView::GetName(const valtype &nameSpace, const valtype &key, CKevaData &data) const { return false; }
bool CCoinsView::GetNamesForHeight(unsigned nHeight, std::set<valtype>& names) const { return false; } bool CCoinsView::GetNamesForHeight(unsigned nHeight, std::set<valtype>& names) const { return false; }
bool CCoinsView::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { return false; } CNameIterator* CCoinsView::IterateNames() const { assert (false); }
bool CCoinsView::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, const CKevaCache &names) { return false; }
CCoinsViewCursor *CCoinsView::Cursor() const { return nullptr; } CCoinsViewCursor *CCoinsView::Cursor() const { return nullptr; }
bool CCoinsView::ValidateNameDB() const { return false; } bool CCoinsView::ValidateNameDB() const { return false; }
@ -31,8 +32,11 @@ std::vector<uint256> CCoinsViewBacked::GetHeadBlocks() const { return base->GetH
bool CCoinsViewBacked::HasNamespace(const valtype &nameSpace) const { return false; } bool CCoinsViewBacked::HasNamespace(const valtype &nameSpace) const { return false; }
bool CCoinsViewBacked::GetName(const valtype &nameSpace, const valtype &key, CKevaData &data) const { return false; } bool CCoinsViewBacked::GetName(const valtype &nameSpace, const valtype &key, CKevaData &data) const { return false; }
bool CCoinsViewBacked::GetNamesForHeight(unsigned nHeight, std::set<valtype>& names) const { return false; } bool CCoinsViewBacked::GetNamesForHeight(unsigned nHeight, std::set<valtype>& names) const { return false; }
CNameIterator* CCoinsViewBacked::IterateNames() const { return base->IterateNames(); }
void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; } void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; }
bool CCoinsViewBacked::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { return base->BatchWrite(mapCoins, hashBlock); } bool CCoinsViewBacked::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, const CKevaCache &names) {
return base->BatchWrite(mapCoins, hashBlock, names);
}
CCoinsViewCursor *CCoinsViewBacked::Cursor() const { return base->Cursor(); } CCoinsViewCursor *CCoinsViewBacked::Cursor() const { return base->Cursor(); }
size_t CCoinsViewBacked::EstimateSize() const { return base->EstimateSize(); } size_t CCoinsViewBacked::EstimateSize() const { return base->EstimateSize(); }
bool CCoinsViewBacked::ValidateNameDB() const { return base->ValidateNameDB(); } bool CCoinsViewBacked::ValidateNameDB() const { return base->ValidateNameDB(); }
@ -177,14 +181,18 @@ bool CCoinsViewCache::GetNamesForHeight(unsigned nHeight, std::set<valtype>& nam
return true; return true;
} }
CNameIterator* CCoinsViewCache::IterateNames() const {
return cacheNames.iterateNames(base->IterateNames());
}
/* undo is set if the change is due to disconnecting blocks / going back in /* undo is set if the change is due to disconnecting blocks / going back in
time. The ordinary case (!undo) means that we update the name normally, time. The ordinary case (!undo) means that we update the name normally,
going forward in time. This is important for keeping track of the going forward in time. This is important for keeping track of the
name history. */ name history. */
void CCoinsViewCache::SetName(const valtype &nameSpace, const valtype &key, const CKevaData& data, bool undo) { void CCoinsViewCache::SetName(const valtype &nameSpace, const valtype &key, const CKevaData& data, bool undo)
{
CKevaData oldData; CKevaData oldData;
if (GetName(nameSpace, key, oldData)) if (GetName(nameSpace, key, oldData)) {
{
#if 0 #if 0
cacheNames.removeExpireIndex(name, oldData.getHeight()); cacheNames.removeExpireIndex(name, oldData.getHeight());
@ -212,8 +220,9 @@ void CCoinsViewCache::SetName(const valtype &nameSpace, const valtype &key, cons
cacheNames.setHistory(name, history); cacheNames.setHistory(name, history);
} }
#endif #endif
} else } else {
assert (!undo); assert (!undo);
}
cacheNames.set(nameSpace, key, data); cacheNames.set(nameSpace, key, data);
#if 0 #if 0
@ -243,7 +252,7 @@ void CCoinsViewCache::DeleteName(const valtype &nameSpace, const valtype &key) {
cacheNames.remove(nameSpace, key); cacheNames.remove(nameSpace, key);
} }
bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn) { bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn, const CKevaCache &names) {
for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end(); it = mapCoins.erase(it)) { for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end(); it = mapCoins.erase(it)) {
// Ignore non-dirty entries (optimization). // Ignore non-dirty entries (optimization).
if (!(it->second.flags & CCoinsCacheEntry::DIRTY)) { if (!(it->second.flags & CCoinsCacheEntry::DIRTY)) {
@ -298,13 +307,21 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn
} }
} }
hashBlock = hashBlockIn; hashBlock = hashBlockIn;
cacheNames.apply(names);
return true; return true;
} }
bool CCoinsViewCache::Flush() { bool CCoinsViewCache::Flush() {
bool fOk = base->BatchWrite(cacheCoins, hashBlock); /* This function is called when validating the name mempool, and BatchWrite
actually fails if hashBlock is not set. Thus we have to make sure here
that it is a valid no-op when nothing is cached. */
if (hashBlock.IsNull() && cacheCoins.empty() && cacheNames.empty())
return true;
bool fOk = base->BatchWrite(cacheCoins, hashBlock, cacheNames);
cacheCoins.clear(); cacheCoins.clear();
cachedCoinsUsage = 0; cachedCoinsUsage = 0;
cacheNames.clear();
return fOk; return fOk;
} }

11
src/coins.h

@ -173,9 +173,12 @@ public:
// Query for names that were updated at the given height // Query for names that were updated at the given height
virtual bool GetNamesForHeight(unsigned nHeight, std::set<valtype>& names) const; virtual bool GetNamesForHeight(unsigned nHeight, std::set<valtype>& names) const;
// Get a name iterator.
virtual CNameIterator* IterateNames() const;
//! Do a bulk modification (multiple Coin changes + BestBlock change). //! Do a bulk modification (multiple Coin changes + BestBlock change).
//! The passed mapCoins can be modified. //! The passed mapCoins can be modified.
virtual bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock); virtual bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, const CKevaCache &names);
//! Get a cursor to iterate over the whole state //! Get a cursor to iterate over the whole state
virtual CCoinsViewCursor *Cursor() const; virtual CCoinsViewCursor *Cursor() const;
@ -206,8 +209,9 @@ public:
bool HasNamespace(const valtype& nameSpace) const override; bool HasNamespace(const valtype& nameSpace) const override;
bool GetName(const valtype& nameSpace, const valtype& key, CKevaData& data) const override; bool GetName(const valtype& nameSpace, const valtype& key, CKevaData& data) const override;
bool GetNamesForHeight(unsigned nHeight, std::set<valtype>& names) const override; bool GetNamesForHeight(unsigned nHeight, std::set<valtype>& names) const override;
CNameIterator* IterateNames() const override;
void SetBackend(CCoinsView &viewIn); void SetBackend(CCoinsView &viewIn);
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) override; bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, const CKevaCache &names) override;
CCoinsViewCursor *Cursor() const override; CCoinsViewCursor *Cursor() const override;
size_t EstimateSize() const override; size_t EstimateSize() const override;
bool ValidateNameDB() const; bool ValidateNameDB() const;
@ -247,7 +251,8 @@ public:
bool HasNamespace(const valtype &nameSpace) const override; bool HasNamespace(const valtype &nameSpace) const override;
bool GetName(const valtype &nameSpace, const valtype &key, CKevaData &data) const override; bool GetName(const valtype &nameSpace, const valtype &key, CKevaData &data) const override;
bool GetNamesForHeight(unsigned nHeight, std::set<valtype>& names) const override; bool GetNamesForHeight(unsigned nHeight, std::set<valtype>& names) const override;
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) override; CNameIterator* IterateNames() const override;
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, const CKevaCache &names) override;
CCoinsViewCursor* Cursor() const override { CCoinsViewCursor* Cursor() const override {
throw std::logic_error("CCoinsViewCache cursor iteration not supported."); throw std::logic_error("CCoinsViewCache cursor iteration not supported.");
} }

68
src/keva/common.cpp

@ -21,11 +21,16 @@ void
CKevaData::fromScript (unsigned h, const COutPoint& out, CKevaData::fromScript (unsigned h, const COutPoint& out,
const CKevaScript& script) const CKevaScript& script)
{ {
assert (script.isAnyUpdate ()); if (script.isAnyUpdate()) {
value = script.getOpValue (); value = script.getOpValue();
} else if (script.isNamespaceRegistration()) {
value = script.getOpNamespaceDisplayName();
} else {
assert(false);
}
nHeight = h; nHeight = h;
prevout = out; prevout = out;
addr = script.getAddress (); addr = script.getAddress();
} }
/* ************************************************************************** */ /* ************************************************************************** */
@ -109,18 +114,23 @@ CCacheNameIterator::advanceBaseIterator ()
} }
void void
CCacheNameIterator::seek (const valtype& start) CCacheNameIterator::seek(const valtype& start)
{ {
cacheIter = cache.entries.lower_bound (start); // JWU TODO: fix this!
base->seek (start); #if 0
cacheIter = cache.entries.lower_bound(start);
#endif
base->seek(start);
baseHasMore = true; baseHasMore = true;
advanceBaseIterator (); advanceBaseIterator();
} }
bool bool CCacheNameIterator::next (valtype& name, CKevaData& data)
CCacheNameIterator::next (valtype& name, CKevaData& data)
{ {
#if 0
// JWU TODO: fix this!
/* Exit early if no more data is available in either the cache /* Exit early if no more data is available in either the cache
nor the base iterator. */ nor the base iterator. */
if (!baseHasMore && cacheIter == cache.entries.end ()) if (!baseHasMore && cacheIter == cache.entries.end ())
@ -166,7 +176,7 @@ CCacheNameIterator::next (valtype& name, CKevaData& data)
data = cacheIter->second; data = cacheIter->second;
++cacheIter; ++cacheIter;
} }
#endif
return true; return true;
} }
@ -176,9 +186,7 @@ CCacheNameIterator::next (valtype& name, CKevaData& data)
bool bool
CKevaCache::get(const valtype& nameSpace, const valtype& key, CKevaData& data) const CKevaCache::get(const valtype& nameSpace, const valtype& key, CKevaData& data) const
{ {
valtype name = nameSpace; const EntryMap::const_iterator i = entries.find(std::make_tuple(nameSpace, key));
name.insert( name.end(), key.begin(), key.end() );
const EntryMap::const_iterator i = entries.find (name);
if (i == entries.end ()) if (i == entries.end ())
return false; return false;
@ -195,17 +203,18 @@ bool CKevaCache::hasNamespace(const valtype& nameSpace) const
void void
CKevaCache::set(const valtype& nameSpace, const valtype& key, const CKevaData& data) CKevaCache::set(const valtype& nameSpace, const valtype& key, const CKevaData& data)
{ {
valtype name = nameSpace; auto name = std::make_tuple(nameSpace, key);
name.insert( name.end(), key.begin(), key.end() ); #if 0
const std::set<valtype>::iterator di = deleted.find (name); const std::set<valtype>::iterator di = deleted.find(name);
if (di != deleted.end ()) if (di != deleted.end ())
deleted.erase (di); deleted.erase (di);
#endif
const EntryMap::iterator ei = entries.find (name); const EntryMap::iterator ei = entries.find(name);
if (ei != entries.end ()) if (ei != entries.end ())
ei->second = data; ei->second = data;
else else
entries.insert (std::make_pair (name, data)); entries.insert (std::make_pair(name, data));
namespaces.insert(nameSpace); namespaces.insert(nameSpace);
} }
@ -213,13 +222,14 @@ CKevaCache::set(const valtype& nameSpace, const valtype& key, const CKevaData& d
void void
CKevaCache::remove(const valtype& nameSpace, const valtype& key) CKevaCache::remove(const valtype& nameSpace, const valtype& key)
{ {
valtype name = nameSpace; auto name = std::make_tuple(nameSpace, key);
name.insert( name.end(), key.begin(), key.end() );
const EntryMap::iterator ei = entries.find (name); const EntryMap::iterator ei = entries.find (name);
if (ei != entries.end ()) if (ei != entries.end ())
entries.erase (ei); entries.erase (ei);
deleted.insert (name); #if 0
deleted.insert(name);
#endif
} }
CNameIterator* CNameIterator*
@ -297,16 +307,14 @@ CKevaCache::removeExpireIndex (const valtype& name, unsigned height)
} }
#endif #endif
#if 0 void CKevaCache::apply(const CKevaCache& cache)
void
CKevaCache::apply(const CKevaCache& cache)
{ {
for (EntryMap::const_iterator i = cache.entries.begin (); i != cache.entries.end (); ++i) { for (EntryMap::const_iterator i = cache.entries.begin(); i != cache.entries.end(); ++i) {
set (i->first, i->second); set(std::get<0>(i->first), std::get<1>(i->first), i->second);
} }
#if 0
for (std::set<valtype>::const_iterator i = cache.deleted.begin (); i != cache.deleted.end (); ++i) { for (std::set<valtype>::const_iterator i = cache.deleted.begin(); i != cache.deleted.end(); ++i) {
remove (*i); remove(*i);
} }
for (std::map<valtype, CNameHistory>::const_iterator i for (std::map<valtype, CNameHistory>::const_iterator i
@ -316,5 +324,5 @@ CKevaCache::apply(const CKevaCache& cache)
for (std::map<ExpireEntry, bool>::const_iterator i for (std::map<ExpireEntry, bool>::const_iterator i
= cache.expireIndex.begin (); i != cache.expireIndex.end (); ++i) = cache.expireIndex.begin (); i != cache.expireIndex.end (); ++i)
expireIndex[i->first] = i->second; expireIndex[i->first] = i->second;
}
#endif #endif
}

16
src/keva/common.h

@ -287,12 +287,14 @@ private:
class NameComparator class NameComparator
{ {
public: public:
inline bool inline bool operator() (const std::tuple<const valtype&, const valtype&> a,
operator() (const valtype& a, const valtype& b) const const std::tuple<const valtype&, const valtype&> b) const
{ {
if (a.size () != b.size ()) unsigned int aSize = std::get<0>(a).size() + std::get<1>(a).size();
return a.size () < b.size (); unsigned int bSize = std::get<0>(b).size() + std::get<1>(b).size();
if (aSize != bSize) {
return aSize < bSize;
}
return a < b; return a < b;
} }
}; };
@ -376,7 +378,7 @@ public:
* Type of name entry map. This is public because it is also used * Type of name entry map. This is public because it is also used
* by the unit tests. * by the unit tests.
*/ */
typedef std::map<valtype, CKevaData, NameComparator> EntryMap; typedef std::map<std::tuple<const valtype&, const valtype&>, CKevaData, NameComparator> EntryMap;
typedef std::set<valtype> NamespaceSet; typedef std::set<valtype> NamespaceSet;
private: private:
@ -499,9 +501,7 @@ public:
#endif #endif
/* Apply all the changes in the passed-in record on top of this one. */ /* Apply all the changes in the passed-in record on top of this one. */
#if 0
void apply (const CKevaCache& cache); void apply (const CKevaCache& cache);
#endif
/* Write all cached changes to a database batch update object. */ /* Write all cached changes to a database batch update object. */
void writeBatch (CDBBatch& batch) const; void writeBatch (CDBBatch& batch) const;

54
src/keva/main.cpp

@ -494,44 +494,38 @@ CheckNameTransaction (const CTransaction& tx, unsigned nHeight,
return true; return true;
} }
void void ApplyNameTransaction(const CTransaction& tx, unsigned nHeight,
ApplyNameTransaction (const CTransaction& tx, unsigned nHeight,
CCoinsViewCache& view, CBlockUndo& undo) CCoinsViewCache& view, CBlockUndo& undo)
{ {
assert (nHeight != MEMPOOL_HEIGHT); assert (nHeight != MEMPOOL_HEIGHT);
#if 0
/* Handle historic bugs that should *not* be applied. Names that are
outputs should be marked as unspendable in this case. Otherwise,
we get an inconsistency between the UTXO set and the name database. */
CChainParams::BugType type;
const uint256 txHash = tx.GetHash ();
if (Params ().IsHistoricBug (txHash, nHeight, type)
&& type != CChainParams::BUG_FULLY_APPLY)
{
if (type == CChainParams::BUG_FULLY_IGNORE)
for (unsigned i = 0; i < tx.vout.size (); ++i)
{
const CNameScript op(tx.vout[i].scriptPubKey);
if (op.isNameOp () && op.isAnyUpdate ())
view.SpendCoin (COutPoint (txHash, i));
}
return;
}
#endif
/* This check must be done *after* the historic bug fixing above! Some
of the names that must be handled above are actually produced by
transactions *not* marked as Namecoin tx. */
if (!tx.IsKevacoin ()) if (!tx.IsKevacoin ())
return; return;
/* Changes are encoded in the outputs. We don't have to do any checks, /* Changes are encoded in the outputs. We don't have to do any checks,
so simply apply all these. */ so simply apply all these. */
for (unsigned i = 0; i < tx.vout.size (); ++i) { for (unsigned i = 0; i < tx.vout.size(); ++i) {
const CKevaScript op(tx.vout[i].scriptPubKey); const CKevaScript op(tx.vout[i].scriptPubKey);
if (op.isKevaOp () && op.isAnyUpdate ()) { if (!op.isKevaOp()) {
continue;
}
if (op.isNamespaceRegistration()) {
const valtype& nameSpace = op.getOpNamespace();
const valtype& displayName = op.getOpNamespaceDisplayName();
LogPrint (BCLog::KEVA, "Register name at height %d: %s, display name: %s\n",
nHeight, ValtypeToString(nameSpace).c_str(),
ValtypeToString(displayName).c_str());
const valtype& key = ValtypeFromString(CKevaScript::KEVA_DISPLAY_NAME_KEY);
CKevaTxUndo opUndo;
opUndo.fromOldState(nameSpace, key, view);
undo.vkevaundo.push_back(opUndo);
CKevaData data;
data.fromScript(nHeight, COutPoint(tx.GetHash(), i), op);
view.SetName(nameSpace, key, data, false);
} else if (op.isAnyUpdate()) {
const valtype& nameSpace = op.getOpNamespace(); const valtype& nameSpace = op.getOpNamespace();
const valtype& key = op.getOpKey(); const valtype& key = op.getOpKey();
LogPrint (BCLog::KEVA, "Updating name at height %d: %s\n", LogPrint (BCLog::KEVA, "Updating name at height %d: %s\n",
@ -542,8 +536,8 @@ ApplyNameTransaction (const CTransaction& tx, unsigned nHeight,
undo.vkevaundo.push_back(opUndo); undo.vkevaundo.push_back(opUndo);
CKevaData data; CKevaData data;
data.fromScript (nHeight, COutPoint (tx.GetHash (), i), op); data.fromScript(nHeight, COutPoint(tx.GetHash(), i), op);
view.SetName (nameSpace, key, data, false); view.SetName(nameSpace, key, data, false);
} }
} }
} }

2
src/script/keva.cpp

@ -6,6 +6,8 @@
#include <uint256.h> #include <uint256.h>
std::string CKevaScript::KEVA_DISPLAY_NAME_KEY = "_KEVA_NS_";
CKevaScript::CKevaScript (const CScript& script) CKevaScript::CKevaScript (const CScript& script)
: op(OP_NOP), address(script) : op(OP_NOP), address(script)
{ {

29
src/script/keva.h

@ -38,6 +38,12 @@ public:
: op(OP_NOP) : op(OP_NOP)
{} {}
/**
* The key pointing to the internal display name. This is the first
* key created when a namespace is registered.
*/
static std::string KEVA_DISPLAY_NAME_KEY;
/** /**
* Parse a script and determine whether it is a valid name script. Sets * Parse a script and determine whether it is a valid name script. Sets
* the member variables representing the "picked apart" name script. * the member variables representing the "picked apart" name script.
@ -95,17 +101,28 @@ public:
} }
/** /**
* Return whether this is a name update (including first updates). I. e., * Return whether this is a namespace registration.
* whether this creates a name index update/entry. * @return True iff this is OP_KEVA_NAMESPACE.
* @return True iff this is NAME_FIRSTUPDATE or NAME_UPDATE.
*/ */
inline bool isAnyUpdate () const inline bool isNamespaceRegistration() const
{ {
switch (op) { switch (op) {
case OP_KEVA_PUT: case OP_KEVA_NAMESPACE:
return true; return true;
case OP_KEVA_NAMESPACE: default:
assert(false);
}
}
/**
* Return whether this is a key/value update.
* @return True iff this is OP_KEVA_PUT.
*/
inline bool isAnyUpdate() const
{
switch (op) {
case OP_KEVA_PUT:
return true; return true;
default: default:

4
src/test/coins_tests.cpp

@ -52,7 +52,7 @@ public:
uint256 GetBestBlock() const override { return hashBestBlock_; } uint256 GetBestBlock() const override { return hashBestBlock_; }
bool BatchWrite(CCoinsMap& mapCoins, const uint256& hashBlock) override bool BatchWrite(CCoinsMap& mapCoins, const uint256& hashBlock, const CKevaCache &names) override
{ {
for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end(); ) { for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end(); ) {
if (it->second.flags & CCoinsCacheEntry::DIRTY) { if (it->second.flags & CCoinsCacheEntry::DIRTY) {
@ -588,7 +588,7 @@ void WriteCoinsViewEntry(CCoinsView& view, CAmount value, char flags)
{ {
CCoinsMap map; CCoinsMap map;
InsertCoinsMapEntry(map, value, flags); InsertCoinsMapEntry(map, value, flags);
view.BatchWrite(map, {}); view.BatchWrite(map, {}, {});
} }
class SingleEntryCacheTest class SingleEntryCacheTest

131
src/txdb.cpp

@ -24,6 +24,8 @@ static const char DB_BLOCK_FILES = 'f';
static const char DB_TXINDEX = 't'; static const char DB_TXINDEX = 't';
static const char DB_BLOCK_INDEX = 'b'; static const char DB_BLOCK_INDEX = 'b';
static const char DB_NAME = 'n';
static const char DB_BEST_BLOCK = 'B'; static const char DB_BEST_BLOCK = 'B';
static const char DB_HEAD_BLOCKS = 'H'; static const char DB_HEAD_BLOCKS = 'H';
static const char DB_FLAG = 'F'; static const char DB_FLAG = 'F';
@ -81,7 +83,103 @@ std::vector<uint256> CCoinsViewDB::GetHeadBlocks() const {
return vhashHeadBlocks; return vhashHeadBlocks;
} }
bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { class CDbNameIterator : public CNameIterator
{
private:
/* The backing LevelDB iterator. */
CDBIterator* iter;
public:
~CDbNameIterator();
/**
* Construct a new name iterator for the database.
* @param db The database to create the iterator for.
*/
CDbNameIterator(const CDBWrapper& db);
/* Implement iterator methods. */
void seek(const valtype& start);
bool next(valtype& name, CKevaData& data);
};
CDbNameIterator::~CDbNameIterator() {
delete iter;
}
CDbNameIterator::CDbNameIterator(const CDBWrapper& db)
: iter(const_cast<CDBWrapper*>(&db)->NewIterator())
{
seek(valtype());
}
void CDbNameIterator::seek(const valtype& start) {
iter->Seek(std::make_pair(DB_NAME, start));
}
bool CDbNameIterator::next(valtype& name, CKevaData& data) {
if (!iter->Valid())
return false;
std::pair<char, valtype> key;
if (!iter->GetKey(key) || key.first != DB_NAME)
return false;
name = key.second;
if (!iter->GetValue(data)) {
return error("%s : failed to read data from iterator", __func__);
}
iter->Next();
return true;
}
CNameIterator* CCoinsViewDB::IterateNames() const {
return new CDbNameIterator(db);
}
bool CCoinsViewDB::GetName(const valtype &nameSpace, const valtype &key, CKevaData &data) const {
return db.Read(std::make_pair(DB_NAME, std::make_pair(nameSpace, key)), data);
}
bool CCoinsViewDB::GetNamesForHeight(unsigned nHeight, std::set<valtype>& names) const {
names.clear();
#if 0
/* It seems that there are no "const iterators" for LevelDB. Since we
only need read operations on it, use a const-cast to get around
that restriction. */
boost::scoped_ptr<CDBIterator> pcursor(const_cast<CDBWrapper*>(&db)->NewIterator());
const CKevaCache::ExpireEntry seekEntry(nHeight, valtype ());
pcursor->Seek(std::make_pair(DB_NAME_EXPIRY, seekEntry));
for (; pcursor->Valid(); pcursor->Next())
{
std::pair<char, CKevaCache::ExpireEntry> key;
if (!pcursor->GetKey(key) || key.first != DB_NAME_EXPIRY)
break;
const CKevaCache::ExpireEntry& entry = key.second;
assert (entry.nHeight >= nHeight);
if (entry.nHeight > nHeight)
break;
const valtype& name = entry.name;
if (names.count(name) > 0) {
return error("%s : duplicate name '%s' in expire index",
__func__, ValtypeToString(name).c_str());
}
names.insert(name);
}
#endif
return true;
}
bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, const CKevaCache &names) {
CDBBatch batch(db); CDBBatch batch(db);
size_t count = 0; size_t count = 0;
size_t changed = 0; size_t changed = 0;
@ -132,6 +230,8 @@ bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) {
} }
} }
names.writeBatch(batch);
// In the last batch, mark the database as consistent with hashBlock again. // In the last batch, mark the database as consistent with hashBlock again.
batch.Erase(DB_HEAD_BLOCKS); batch.Erase(DB_HEAD_BLOCKS);
batch.Write(DB_BEST_BLOCK, hashBlock); batch.Write(DB_BEST_BLOCK, hashBlock);
@ -236,6 +336,35 @@ bool CBlockTreeDB::WriteBatchSync(const std::vector<std::pair<int, const CBlockF
return WriteBatch(batch, true); return WriteBatch(batch, true);
} }
void CKevaCache::writeBatch (CDBBatch& batch) const
{
for (EntryMap::const_iterator i = entries.begin(); i != entries.end(); ++i) {
auto name = std::make_pair(std::get<0>(i->first), std::get<1>(i->first));
batch.Write(std::make_pair(DB_NAME, name), i->second);
}
#if 0
for (std::set<valtype>::const_iterator i = deleted.begin(); i != deleted.end(); ++i) {
batch.Erase(std::make_pair (DB_NAME, *i));
}
assert (fNameHistory || history.empty ());
for (std::map<valtype, CNameHistory>::const_iterator i = history.begin ();
i != history.end (); ++i)
if (i->second.empty ())
batch.Erase (std::make_pair (DB_NAME_HISTORY, i->first));
else
batch.Write (std::make_pair (DB_NAME_HISTORY, i->first), i->second);
for (std::map<ExpireEntry, bool>::const_iterator i = expireIndex.begin ();
i != expireIndex.end (); ++i)
if (i->second)
batch.Write (std::make_pair (DB_NAME_EXPIRY, i->first));
else
batch.Erase (std::make_pair (DB_NAME_EXPIRY, i->first));
#endif
}
bool CBlockTreeDB::ReadTxIndex(const uint256 &txid, CDiskTxPos &pos) { bool CBlockTreeDB::ReadTxIndex(const uint256 &txid, CDiskTxPos &pos) {
return Read(std::make_pair(DB_TXINDEX, txid), pos); return Read(std::make_pair(DB_TXINDEX, txid), pos);
} }

5
src/txdb.h

@ -76,7 +76,10 @@ public:
bool HaveCoin(const COutPoint &outpoint) const override; bool HaveCoin(const COutPoint &outpoint) const override;
uint256 GetBestBlock() const override; uint256 GetBestBlock() const override;
std::vector<uint256> GetHeadBlocks() const override; std::vector<uint256> GetHeadBlocks() const override;
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) override; bool GetName(const valtype &nameSpace, const valtype &key, CKevaData &data) const override;
bool GetNamesForHeight(unsigned nHeight, std::set<valtype>& names) const override;
CNameIterator* IterateNames() const override;
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, const CKevaCache &names) override;
CCoinsViewCursor *Cursor() const override; CCoinsViewCursor *Cursor() const override;
//! Attempt to update from an older database format. Returns whether an error occurred. //! Attempt to update from an older database format. Returns whether an error occurred.

86
src/wallet/rpckeva.cpp

@ -51,8 +51,9 @@ UniValue keva_namespace(const JSONRPCRequest& request)
const std::string displayNameStr = request.params[0].get_str(); const std::string displayNameStr = request.params[0].get_str();
const valtype displayName = ValtypeFromString (displayNameStr); const valtype displayName = ValtypeFromString (displayNameStr);
if (displayName.size () > MAX_NAMESPACE_LENGTH) if (displayName.size () > MAX_NAMESPACE_LENGTH) {
throw JSONRPCError (RPC_INVALID_PARAMETER, "the name is too long"); throw JSONRPCError (RPC_INVALID_PARAMETER, "the name is too long");
}
/* No explicit locking should be necessary. CReserveKey takes care /* No explicit locking should be necessary. CReserveKey takes care
of locking the wallet, and CommitTransaction (called when sending of locking the wallet, and CommitTransaction (called when sending
@ -67,7 +68,7 @@ UniValue keva_namespace(const JSONRPCRequest& request)
CKeyID keyId = pubKey.GetID(); CKeyID keyId = pubKey.GetID();
// The namespace is: Hash160(Hash160(keyId) || displayName) // The namespace name is: Hash160(Hash160(keyId) || displayName)
valtype toHash = ToByteVector(Hash160(ToByteVector(keyId))); valtype toHash = ToByteVector(Hash160(ToByteVector(keyId)));
toHash.insert(toHash.end(), displayName.begin(), displayName.end()); toHash.insert(toHash.end(), displayName.begin(), displayName.end());
const uint160 namespaceHash = Hash160(toHash); const uint160 namespaceHash = Hash160(toHash);
@ -93,87 +94,6 @@ UniValue keva_namespace(const JSONRPCRequest& request)
return res; return res;
} }
/* ************************************************************************** */
#if 0
UniValue keva_namespace(const JSONRPCRequest& request)
{
CWallet* const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
return NullUniValue;
if (request.fHelp
|| (request.params.size() != 3))
throw std::runtime_error (
"keva_namespace \"display_name\" (\"keva_namespace\")\n"
"\nCreate a namespace.\n"
+ HelpRequiringPassphrase(pwallet) +
"\nArguments:\n"
"1. \"display_name\" (string, required) the display name of the namespace\n"
"\nResult:\n"
"\"txid\" (string) the keva_namespace's txid\n"
"\"internal_id\" (string) the internal id of the namespace\n"
"\nExamples:\n"
+ HelpExampleCli("keva_namespace", "\"display_name\"")
);
RPCTypeCheck (request.params,
{UniValue::VSTR});
ObserveSafeMode ();
const valtype displayName = ValtypeFromString(request.params[0].get_str());
if (displayName.size() > MAX_NAMESPACE_LENGTH) {
throw JSONRPCError (RPC_INVALID_PARAMETER, "the display name of the namespace is too long");
}
#if 0
/* Reject updates to a name for which the mempool already has
a pending update. This is not a hard rule enforced by network
rules, but it is necessary with the current mempool implementation. */
{
LOCK (mempool.cs);
if (mempool.registersNamespace(name))
throw JSONRPCError (RPC_TRANSACTION_ERROR,
"there is already a pending update for this name");
}
#endif
/* No more locking required, similarly to name_new. */
EnsureWalletIsUnlocked (pwallet);
CReserveKey keyName(pwallet);
CPubKey pubKeyReserve;
const bool ok = keyName.GetReservedKey(pubKeyReserve, true);
assert (ok);
bool usedKey = false;
CScript addrName;
if (request.params.size () == 3) {
keyName.ReturnKey();
const CTxDestination dest = DecodeDestination (request.params[2].get_str ());
if (!IsValidDestination (dest)) {
throw JSONRPCError (RPC_INVALID_ADDRESS_OR_KEY, "invalid address");
}
addrName = GetScriptForDestination(dest);
} else {
usedKey = true;
addrName = GetScriptForDestination(pubKeyReserve.GetID ());
}
const CScript kevaScript = CKevaScript::buildKevaNamespace(addrName, name, displayName);
CCoinControl coinControl;
CWalletTx wtx;
SendMoneyToScript(pwallet, kevaScript, nullptr, KEVA_LOCKED_AMOUNT, false, wtx, coinControl);
if (usedKey) {
keyName.KeepKey ();
}
return wtx.GetHash ().GetHex ();
}
#endif
UniValue keva_put(const JSONRPCRequest& request) UniValue keva_put(const JSONRPCRequest& request)
{ {
CWallet* const pwallet = GetWalletForJSONRPCRequest(request); CWallet* const pwallet = GetWalletForJSONRPCRequest(request);

Loading…
Cancel
Save