mirror of
https://github.com/kvazar-network/kevacoin.git
synced 2025-01-27 15:24:39 +00:00
Implemented writing to DB.
This commit is contained in:
parent
afa40c41b6
commit
49de399c30
@ -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
|
||||
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
|
||||
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
|
||||
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) {
|
||||
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
|
||||
}
|
||||
}
|
||||
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
11
src/coins.h
@ -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:
|
||||
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:
|
||||
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.");
|
||||
}
|
||||
|
@ -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 ()
|
||||
}
|
||||
|
||||
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)
|
||||
data = cacheIter->second;
|
||||
++cacheIter;
|
||||
}
|
||||
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -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
|
||||
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
|
||||
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)
|
||||
}
|
||||
#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)
|
||||
for (std::map<ExpireEntry, bool>::const_iterator i
|
||||
= cache.expireIndex.begin (); i != cache.expireIndex.end (); ++i)
|
||||
expireIndex[i->first] = i->second;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -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:
|
||||
* 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:
|
||||
#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;
|
||||
|
@ -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,
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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,19 +101,30 @@ 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_NAMESPACE:
|
||||
return true;
|
||||
|
||||
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;
|
||||
|
||||
case OP_KEVA_NAMESPACE:
|
||||
return true;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
CCoinsMap map;
|
||||
InsertCoinsMapEntry(map, value, flags);
|
||||
view.BatchWrite(map, {});
|
||||
view.BatchWrite(map, {}, {});
|
||||
}
|
||||
|
||||
class SingleEntryCacheTest
|
||||
|
133
src/txdb.cpp
133
src/txdb.cpp
@ -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 {
|
||||
|
||||
}
|
||||
|
||||
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 {
|
||||
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) {
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
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);
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
|
||||
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)
|
||||
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…
x
Reference in New Issue
Block a user