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. 56
      src/keva/main.cpp
  6. 2
      src/script/keva.cpp
  7. 29
      src/script/keva.h
  8. 4
      src/test/coins_tests.cpp
  9. 133
      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 @@ -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::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::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; }
bool CCoinsView::ValidateNameDB() const { return false; }
@ -31,8 +32,11 @@ std::vector<uint256> CCoinsViewBacked::GetHeadBlocks() const { return base->GetH @@ -31,8 +32,11 @@ std::vector<uint256> CCoinsViewBacked::GetHeadBlocks() const { return base->GetH
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::GetNamesForHeight(unsigned nHeight, std::set<valtype>& names) const { return false; }
CNameIterator* CCoinsViewBacked::IterateNames() const { return base->IterateNames(); }
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(); }
size_t CCoinsViewBacked::EstimateSize() const { return base->EstimateSize(); }
bool CCoinsViewBacked::ValidateNameDB() const { return base->ValidateNameDB(); }
@ -177,14 +181,18 @@ bool CCoinsViewCache::GetNamesForHeight(unsigned nHeight, std::set<valtype>& nam @@ -177,14 +181,18 @@ bool CCoinsViewCache::GetNamesForHeight(unsigned nHeight, std::set<valtype>& nam
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
time. The ordinary case (!undo) means that we update the name normally,
going forward in time. This is important for keeping track of the
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;
if (GetName(nameSpace, key, oldData))
{
if (GetName(nameSpace, key, oldData)) {
#if 0
cacheNames.removeExpireIndex(name, oldData.getHeight());
@ -212,8 +220,9 @@ void CCoinsViewCache::SetName(const valtype &nameSpace, const valtype &key, cons @@ -212,8 +220,9 @@ void CCoinsViewCache::SetName(const valtype &nameSpace, const valtype &key, cons
cacheNames.setHistory(name, history);
}
#endif
} else
} else {
assert (!undo);
}
cacheNames.set(nameSpace, key, data);
#if 0
@ -243,7 +252,7 @@ void CCoinsViewCache::DeleteName(const valtype &nameSpace, const valtype &key) { @@ -243,7 +252,7 @@ void CCoinsViewCache::DeleteName(const valtype &nameSpace, const valtype &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)) {
// Ignore non-dirty entries (optimization).
if (!(it->second.flags & CCoinsCacheEntry::DIRTY)) {
@ -298,13 +307,21 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn @@ -298,13 +307,21 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn
}
}
hashBlock = hashBlockIn;
cacheNames.apply(names);
return true;
}
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();
cachedCoinsUsage = 0;
cacheNames.clear();
return fOk;
}

11
src/coins.h

@ -173,9 +173,12 @@ public: @@ -173,9 +173,12 @@ public:
// Query for names that were updated at the given height
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).
//! 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
virtual CCoinsViewCursor *Cursor() const;
@ -206,8 +209,9 @@ public: @@ -206,8 +209,9 @@ public:
bool HasNamespace(const valtype& nameSpace) const 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;
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;
size_t EstimateSize() const override;
bool ValidateNameDB() const;
@ -247,7 +251,8 @@ public: @@ -247,7 +251,8 @@ public:
bool HasNamespace(const valtype &nameSpace) 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 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 {
throw std::logic_error("CCoinsViewCache cursor iteration not supported.");
}

68
src/keva/common.cpp

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

16
src/keva/common.h

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

56
src/keva/main.cpp

@ -494,44 +494,38 @@ CheckNameTransaction (const CTransaction& tx, unsigned nHeight, @@ -494,44 +494,38 @@ CheckNameTransaction (const CTransaction& tx, unsigned nHeight,
return true;
}
void
ApplyNameTransaction (const CTransaction& tx, unsigned nHeight,
CCoinsViewCache& view, CBlockUndo& undo)
void ApplyNameTransaction(const CTransaction& tx, unsigned nHeight,
CCoinsViewCache& view, CBlockUndo& undo)
{
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 ())
return;
/* Changes are encoded in the outputs. We don't have to do any checks,
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);
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& key = op.getOpKey();
LogPrint (BCLog::KEVA, "Updating name at height %d: %s\n",
@ -542,8 +536,8 @@ ApplyNameTransaction (const CTransaction& tx, unsigned nHeight, @@ -542,8 +536,8 @@ ApplyNameTransaction (const CTransaction& tx, unsigned nHeight,
undo.vkevaundo.push_back(opUndo);
CKevaData data;
data.fromScript (nHeight, COutPoint (tx.GetHash (), i), op);
view.SetName (nameSpace, key, data, false);
data.fromScript(nHeight, COutPoint(tx.GetHash(), i), op);
view.SetName(nameSpace, key, data, false);
}
}
}

2
src/script/keva.cpp

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

29
src/script/keva.h

@ -38,6 +38,12 @@ public: @@ -38,6 +38,12 @@ public:
: 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
* the member variables representing the "picked apart" name script.
@ -95,17 +101,28 @@ public: @@ -95,17 +101,28 @@ public:
}
/**
* Return whether this is a name update (including first updates). I. e.,
* whether this creates a name index update/entry.
* @return True iff this is NAME_FIRSTUPDATE or NAME_UPDATE.
* Return whether this is a namespace registration.
* @return True iff this is OP_KEVA_NAMESPACE.
*/
inline bool isAnyUpdate () const
inline bool isNamespaceRegistration() const
{
switch (op) {
case OP_KEVA_PUT:
case OP_KEVA_NAMESPACE:
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;
default:

4
src/test/coins_tests.cpp

@ -52,7 +52,7 @@ public: @@ -52,7 +52,7 @@ public:
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(); ) {
if (it->second.flags & CCoinsCacheEntry::DIRTY) {
@ -588,7 +588,7 @@ void WriteCoinsViewEntry(CCoinsView& view, CAmount value, char flags) @@ -588,7 +588,7 @@ void WriteCoinsViewEntry(CCoinsView& view, CAmount value, char flags)
{
CCoinsMap map;
InsertCoinsMapEntry(map, value, flags);
view.BatchWrite(map, {});
view.BatchWrite(map, {}, {});
}
class SingleEntryCacheTest

133
src/txdb.cpp

@ -24,6 +24,8 @@ static const char DB_BLOCK_FILES = 'f'; @@ -24,6 +24,8 @@ static const char DB_BLOCK_FILES = 'f';
static const char DB_TXINDEX = 't';
static const char DB_BLOCK_INDEX = 'b';
static const char DB_NAME = 'n';
static const char DB_BEST_BLOCK = 'B';
static const char DB_HEAD_BLOCKS = 'H';
static const char DB_FLAG = 'F';
@ -54,7 +56,7 @@ struct CoinEntry { @@ -54,7 +56,7 @@ struct CoinEntry {
}
CCoinsViewDB::CCoinsViewDB(size_t nCacheSize, bool fMemory, bool fWipe) : db(GetDataDir() / "chainstate", nCacheSize, fMemory, fWipe, true)
CCoinsViewDB::CCoinsViewDB(size_t nCacheSize, bool fMemory, bool fWipe) : db(GetDataDir() / "chainstate", nCacheSize, fMemory, fWipe, true)
{
}
@ -81,7 +83,103 @@ std::vector<uint256> CCoinsViewDB::GetHeadBlocks() const { @@ -81,7 +83,103 @@ std::vector<uint256> CCoinsViewDB::GetHeadBlocks() const {
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);
size_t count = 0;
size_t changed = 0;
@ -132,6 +230,8 @@ bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { @@ -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.
batch.Erase(DB_HEAD_BLOCKS);
batch.Write(DB_BEST_BLOCK, hashBlock);
@ -236,6 +336,35 @@ bool CBlockTreeDB::WriteBatchSync(const std::vector<std::pair<int, const CBlockF @@ -236,6 +336,35 @@ bool CBlockTreeDB::WriteBatchSync(const std::vector<std::pair<int, const CBlockF
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) {
return Read(std::make_pair(DB_TXINDEX, txid), pos);
}

5
src/txdb.h

@ -76,7 +76,10 @@ public: @@ -76,7 +76,10 @@ public:
bool HaveCoin(const COutPoint &outpoint) const override;
uint256 GetBestBlock() 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;
//! 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) @@ -51,8 +51,9 @@ UniValue keva_namespace(const JSONRPCRequest& request)
const std::string displayNameStr = request.params[0].get_str();
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");
}
/* No explicit locking should be necessary. CReserveKey takes care
of locking the wallet, and CommitTransaction (called when sending
@ -67,7 +68,7 @@ UniValue keva_namespace(const JSONRPCRequest& request) @@ -67,7 +68,7 @@ UniValue keva_namespace(const JSONRPCRequest& request)
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)));
toHash.insert(toHash.end(), displayName.begin(), displayName.end());
const uint160 namespaceHash = Hash160(toHash);
@ -93,87 +94,6 @@ UniValue keva_namespace(const JSONRPCRequest& request) @@ -93,87 +94,6 @@ UniValue keva_namespace(const JSONRPCRequest& request)
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)
{
CWallet* const pwallet = GetWalletForJSONRPCRequest(request);

Loading…
Cancel
Save