mirror of
https://github.com/kvazar-network/kevacoin.git
synced 2025-01-14 17:17:58 +00:00
Implemented keva_filter.
This commit is contained in:
parent
33bb11cdda
commit
9a025be151
@ -13,7 +13,7 @@ std::vector<uint256> CCoinsView::GetHeadBlocks() const { return std::vector<uint
|
||||
bool CCoinsView::GetNamespace(const valtype &nameSpace, 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; }
|
||||
CNameIterator* CCoinsView::IterateNames() const { assert (false); }
|
||||
CKevaIterator* CCoinsView::IterateKeys(const valtype& nameSpace) 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; }
|
||||
@ -38,7 +38,7 @@ bool CCoinsViewBacked::GetName(const valtype &nameSpace, const valtype &key, CKe
|
||||
bool CCoinsViewBacked::GetNamesForHeight(unsigned nHeight, std::set<valtype>& names) const {
|
||||
return base->GetNamesForHeight(nHeight, names);
|
||||
}
|
||||
CNameIterator* CCoinsViewBacked::IterateNames() const { return base->IterateNames(); }
|
||||
CKevaIterator* CCoinsViewBacked::IterateKeys(const valtype& nameSpace) const { return base->IterateKeys(nameSpace); }
|
||||
void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; }
|
||||
bool CCoinsViewBacked::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, const CKevaCache &names) {
|
||||
return base->BatchWrite(mapCoins, hashBlock, names);
|
||||
@ -190,8 +190,8 @@ bool CCoinsViewCache::GetNamesForHeight(unsigned nHeight, std::set<valtype>& nam
|
||||
return true;
|
||||
}
|
||||
|
||||
CNameIterator* CCoinsViewCache::IterateNames() const {
|
||||
return cacheNames.iterateNames(base->IterateNames());
|
||||
CKevaIterator* CCoinsViewCache::IterateKeys(const valtype& nameSpace) const {
|
||||
return cacheNames.iterateKeys(base->IterateKeys(nameSpace));
|
||||
}
|
||||
|
||||
/* undo is set if the change is due to disconnecting blocks / going back in
|
||||
|
@ -173,8 +173,8 @@ 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;
|
||||
// Get a key iterator.
|
||||
virtual CKevaIterator* IterateKeys(const valtype& nameSpace) const;
|
||||
|
||||
//! Do a bulk modification (multiple Coin changes + BestBlock change).
|
||||
//! The passed mapCoins can be modified.
|
||||
@ -209,7 +209,7 @@ public:
|
||||
bool GetNamespace(const valtype& nameSpace, 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;
|
||||
CNameIterator* IterateNames() const override;
|
||||
CKevaIterator* IterateKeys(const valtype& nameSpace) const override;
|
||||
void SetBackend(CCoinsView &viewIn);
|
||||
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, const CKevaCache &names) override;
|
||||
CCoinsViewCursor *Cursor() const override;
|
||||
@ -251,7 +251,7 @@ public:
|
||||
bool GetNamespace(const valtype &nameSpace, 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;
|
||||
CNameIterator* IterateNames() const override;
|
||||
CKevaIterator* IterateKeys(const valtype& nameSpace) 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.");
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
// Copyright (c) 2018 Jianping Wu
|
||||
// Copyright (c) 2018 the Kevacoin Core Developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
@ -10,9 +10,6 @@
|
||||
|
||||
#include <script/keva.h>
|
||||
|
||||
typedef std::vector<unsigned char> valtype;
|
||||
|
||||
bool fNameHistory = false;
|
||||
|
||||
/* ************************************************************************** */
|
||||
/* CKevaData. */
|
||||
@ -36,9 +33,9 @@ CKevaData::fromScript (unsigned h, const COutPoint& out,
|
||||
}
|
||||
|
||||
/* ************************************************************************** */
|
||||
/* CNameIterator. */
|
||||
/* CKevaIterator. */
|
||||
|
||||
CNameIterator::~CNameIterator ()
|
||||
CKevaIterator::~CKevaIterator ()
|
||||
{
|
||||
/* Nothing to be done here. This may be overwritten by
|
||||
subclasses if they need a destructor. */
|
||||
@ -47,8 +44,7 @@ CNameIterator::~CNameIterator ()
|
||||
/* ************************************************************************** */
|
||||
/* CKevaCacheNameIterator. */
|
||||
|
||||
// JWU TODO: this doesn't work at all!!!!
|
||||
class CCacheNameIterator : public CNameIterator
|
||||
class CCacheKeyIterator : public CKevaIterator
|
||||
{
|
||||
|
||||
private:
|
||||
@ -57,12 +53,14 @@ private:
|
||||
const CKevaCache& cache;
|
||||
|
||||
/** Base iterator to combine with the cache. */
|
||||
CNameIterator* base;
|
||||
CKevaIterator* base;
|
||||
|
||||
/** Whether or not the base iterator has more entries. */
|
||||
bool baseHasMore;
|
||||
/** "Next" name of the base iterator. */
|
||||
valtype baseName;
|
||||
|
||||
/** "Next" key of the base iterator. */
|
||||
valtype baseKey;
|
||||
|
||||
/** "Next" data of the base iterator. */
|
||||
CKevaData baseData;
|
||||
|
||||
@ -72,7 +70,7 @@ private:
|
||||
/* Call the base iterator's next() routine to fill in the internal
|
||||
"cache" for the next entry. This already skips entries that are
|
||||
marked as deleted in the cache. */
|
||||
void advanceBaseIterator ();
|
||||
void advanceBaseIterator();
|
||||
|
||||
public:
|
||||
|
||||
@ -81,104 +79,92 @@ public:
|
||||
* @param c The cache object to use.
|
||||
* @param b The base iterator.
|
||||
*/
|
||||
CCacheNameIterator (const CKevaCache& c, CNameIterator* b);
|
||||
CCacheKeyIterator(const CKevaCache& c, CKevaIterator* b);
|
||||
|
||||
/* Destruct, this deletes also the base iterator. */
|
||||
~CCacheNameIterator ();
|
||||
~CCacheKeyIterator();
|
||||
|
||||
/* Implement iterator methods. */
|
||||
void seek (const valtype& name);
|
||||
bool next (valtype& name, CKevaData& data);
|
||||
void seek(const valtype& key);
|
||||
bool next(valtype& key, CKevaData& data);
|
||||
|
||||
};
|
||||
|
||||
CCacheNameIterator::CCacheNameIterator (const CKevaCache& c, CNameIterator* b)
|
||||
: cache(c), base(b)
|
||||
CCacheKeyIterator::CCacheKeyIterator(const CKevaCache& c, CKevaIterator* b)
|
||||
: cache(c), base(b), CKevaIterator(b->getNamespace())
|
||||
{
|
||||
/* Add a seek-to-start to ensure that everything is consistent. This call
|
||||
may be superfluous if we seek to another position afterwards anyway,
|
||||
but it should also not hurt too much. */
|
||||
seek (valtype ());
|
||||
seek(valtype());
|
||||
}
|
||||
|
||||
CCacheNameIterator::~CCacheNameIterator ()
|
||||
CCacheKeyIterator::~CCacheKeyIterator()
|
||||
{
|
||||
delete base;
|
||||
}
|
||||
|
||||
void
|
||||
CCacheNameIterator::advanceBaseIterator ()
|
||||
CCacheKeyIterator::advanceBaseIterator()
|
||||
{
|
||||
assert (baseHasMore);
|
||||
do
|
||||
baseHasMore = base->next (baseName, baseData);
|
||||
while (baseHasMore && cache.isDeleted(baseName, baseName));
|
||||
do {
|
||||
baseHasMore = base->next(baseKey, baseData);
|
||||
} while (baseHasMore && cache.isDeleted(nameSpace, baseKey));
|
||||
}
|
||||
|
||||
void
|
||||
CCacheNameIterator::seek(const valtype& start)
|
||||
CCacheKeyIterator::seek(const valtype& start)
|
||||
{
|
||||
// JWU TODO: fix this!
|
||||
#if 0
|
||||
cacheIter = cache.entries.lower_bound(start);
|
||||
#endif
|
||||
cacheIter = cache.entries.lower_bound(std::make_tuple(nameSpace, start));
|
||||
base->seek(start);
|
||||
|
||||
baseHasMore = true;
|
||||
advanceBaseIterator();
|
||||
}
|
||||
|
||||
bool CCacheNameIterator::next (valtype& name, CKevaData& data)
|
||||
bool CCacheKeyIterator::next(valtype& key, 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 ())
|
||||
if (!baseHasMore && cacheIter == cache.entries.end())
|
||||
return false;
|
||||
|
||||
/* Determine which source to use for the next. */
|
||||
bool useBase;
|
||||
if (!baseHasMore)
|
||||
useBase = false;
|
||||
else if (cacheIter == cache.entries.end ())
|
||||
else if (cacheIter == cache.entries.end())
|
||||
useBase = true;
|
||||
else
|
||||
{
|
||||
/* A special case is when both iterators are equal. In this case,
|
||||
we want to use the cached version. We also have to advance
|
||||
the base iterator. */
|
||||
if (baseName == cacheIter->first)
|
||||
advanceBaseIterator ();
|
||||
else {
|
||||
/* A special case is when both iterators are equal. In this case,
|
||||
we want to use the cached version. We also have to advance
|
||||
the base iterator. */
|
||||
if (baseKey == std::get<1>(cacheIter->first))
|
||||
advanceBaseIterator ();
|
||||
|
||||
/* Due to advancing the base iterator above, it may happen that
|
||||
no more base entries are present. Handle this gracefully. */
|
||||
if (!baseHasMore)
|
||||
useBase = false;
|
||||
else
|
||||
{
|
||||
assert (baseName != cacheIter->first);
|
||||
/* Due to advancing the base iterator above, it may happen that
|
||||
no more base entries are present. Handle this gracefully. */
|
||||
if (!baseHasMore)
|
||||
useBase = false;
|
||||
else {
|
||||
assert(baseKey != std::get<1>(cacheIter->first));
|
||||
|
||||
CKevaCache::NameComparator cmp;
|
||||
useBase = cmp (baseName, cacheIter->first);
|
||||
}
|
||||
CKevaCache::KeyComparator cmp;
|
||||
useBase = cmp(std::make_tuple(nameSpace, baseKey), cacheIter->first);
|
||||
}
|
||||
}
|
||||
|
||||
/* Use the correct source now and advance it. */
|
||||
if (useBase)
|
||||
{
|
||||
name = baseName;
|
||||
data = baseData;
|
||||
advanceBaseIterator ();
|
||||
}
|
||||
else
|
||||
{
|
||||
name = cacheIter->first;
|
||||
data = cacheIter->second;
|
||||
++cacheIter;
|
||||
}
|
||||
#endif
|
||||
if (useBase) {
|
||||
key = baseKey;
|
||||
data = baseData;
|
||||
advanceBaseIterator();
|
||||
} else {
|
||||
key = std::get<1>(cacheIter->first);
|
||||
data = cacheIter->second;
|
||||
++cacheIter;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -235,10 +221,10 @@ CKevaCache::remove(const valtype& nameSpace, const valtype& key)
|
||||
deleted.insert(name);
|
||||
}
|
||||
|
||||
CNameIterator*
|
||||
CKevaCache::iterateNames(CNameIterator* base) const
|
||||
CKevaIterator*
|
||||
CKevaCache::iterateKeys(CKevaIterator* base) const
|
||||
{
|
||||
return new CCacheNameIterator (*this, base);
|
||||
return new CCacheKeyIterator(*this, base);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
// Copyright (c) 2018 Jianping Wu
|
||||
// Copyright (c) 2018
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
@ -246,37 +246,47 @@ public:
|
||||
};
|
||||
|
||||
/* ************************************************************************** */
|
||||
/* CNameIterator. */
|
||||
/* CKevaIterator. */
|
||||
|
||||
/**
|
||||
* Interface for iterators over the name database.
|
||||
*/
|
||||
class CNameIterator
|
||||
class CKevaIterator
|
||||
{
|
||||
protected:
|
||||
const valtype& nameSpace;
|
||||
|
||||
public:
|
||||
|
||||
CKevaIterator(const valtype& ns) :nameSpace(ns)
|
||||
{}
|
||||
|
||||
// Virtual destructor in case subclasses need them.
|
||||
virtual ~CNameIterator ();
|
||||
virtual ~CKevaIterator();
|
||||
|
||||
const valtype& getNamespace() {
|
||||
return nameSpace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Seek to a given lower bound.
|
||||
* @param start The name to seek to.
|
||||
* @param start The key to seek to.
|
||||
*/
|
||||
virtual void seek (const valtype& name) = 0;
|
||||
virtual void seek(const valtype& start) = 0;
|
||||
|
||||
/**
|
||||
* Get the next name. Returns false if no more names are available.
|
||||
* @param name Put the name here.
|
||||
* @param data Put the name's data here.
|
||||
* @return True if successful, false if no more names.
|
||||
* Get the next key. Returns false if no more keys are available.
|
||||
* @param nameSpace Put the namespace here.
|
||||
* @param key Put the key here.
|
||||
* @param data Put the key's data here.
|
||||
* @return True if successful, false if no more keys.
|
||||
*/
|
||||
virtual bool next (valtype& name, CKevaData& data) = 0;
|
||||
virtual bool next(valtype& key, CKevaData& data) = 0;
|
||||
|
||||
};
|
||||
|
||||
/* ************************************************************************** */
|
||||
/* CNameCache. */
|
||||
/* CKevaCache. */
|
||||
|
||||
/**
|
||||
* Cache / record of updates to the name database. In addition to
|
||||
@ -293,7 +303,7 @@ private:
|
||||
* This is used to sort the cache entry map in the same way as the
|
||||
* database is sorted.
|
||||
*/
|
||||
class NameComparator
|
||||
class KeyComparator
|
||||
{
|
||||
public:
|
||||
inline bool operator() (const std::tuple<valtype, valtype> a,
|
||||
@ -314,7 +324,7 @@ public:
|
||||
* Type of name entry map. This is public because it is also used
|
||||
* by the unit tests.
|
||||
*/
|
||||
typedef std::map<std::tuple<valtype, valtype>, CKevaData, NameComparator> EntryMap;
|
||||
typedef std::map<std::tuple<valtype, valtype>, CKevaData, KeyComparator> EntryMap;
|
||||
|
||||
typedef std::tuple<valtype, valtype> NamespaceKeyType;
|
||||
|
||||
@ -326,7 +336,7 @@ private:
|
||||
/** Deleted names. */
|
||||
std::set<NamespaceKeyType> deleted;
|
||||
|
||||
friend class CCacheNameIterator;
|
||||
friend class CCacheKeyIterator;
|
||||
|
||||
public:
|
||||
|
||||
@ -379,7 +389,7 @@ public:
|
||||
/* Return a name iterator that combines a "base" iterator with the changes
|
||||
made to it according to the cache. The base iterator is taken
|
||||
ownership of. */
|
||||
CNameIterator* iterateNames (CNameIterator* base) const;
|
||||
CKevaIterator* iterateKeys(CKevaIterator* base) const;
|
||||
|
||||
/* Query the cached changes to the expire index. In particular,
|
||||
for a given height and a given set of names that were indexed to
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
// Copyright (c) 2018 Jianping Wu
|
||||
// Copyright (c) 2018 the Kevacoin Core Developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
@ -133,27 +133,6 @@ void CKevaMemPool::remove(const CTxMemPoolEntry& entry)
|
||||
void
|
||||
CKevaMemPool::removeConflicts(const CTransaction& tx)
|
||||
{
|
||||
// JWU TODO: is this required at all?
|
||||
#if 0
|
||||
AssertLockHeld (pool.cs);
|
||||
|
||||
if (!tx.IsKevacoin ())
|
||||
return;
|
||||
|
||||
for (const auto& txout : tx.vout) {
|
||||
const CKevaScript nameOp(txout.scriptPubKey);
|
||||
if (nameOp.isKevaOp() && nameOp.getKevaOp() == OP_KEVA_PUT) {
|
||||
const valtype& nameSpace = nameOp.getOpNamespace();
|
||||
const NamespaceTxMap::const_iterator mit = mapNamespaceRegs.find(nameSpace);
|
||||
if (mit != mapNamespaceRegs.end()) {
|
||||
const CTxMemPool::txiter mit2 = pool.mapTx.find(mit->second);
|
||||
assert(mit2 != pool.mapTx.end());
|
||||
pool.removeRecursive (mit2->GetTx(),
|
||||
MemPoolRemovalReason::KEVA_CONFLICT);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CKevaMemPool::validateNamespace(const CTransaction& tx, const valtype& nameSpace) const
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
// Copyright (c) 2018 Jianping Wu
|
||||
// Copyright (c) 2018 the Kevacoin Core Developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2018 Jianping Wu
|
||||
// Copyright (c) 2018 the Kevacoin Core Developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
|
@ -180,7 +180,7 @@ private:
|
||||
/**
|
||||
* "Fake" name iterator returned.
|
||||
*/
|
||||
class Iterator : public CNameIterator
|
||||
class Iterator : public CKevaIterator
|
||||
{
|
||||
public:
|
||||
|
||||
@ -198,8 +198,8 @@ private:
|
||||
|
||||
public:
|
||||
|
||||
CNameIterator*
|
||||
IterateNames() const
|
||||
CKevaIterator*
|
||||
IterateKeys() const
|
||||
{
|
||||
return new Iterator();
|
||||
}
|
||||
@ -271,7 +271,7 @@ private:
|
||||
* @param iter The iterator to use.
|
||||
* @return The resulting entry list.
|
||||
*/
|
||||
static EntryList getNamesFromIterator (CNameIterator& iter);
|
||||
static EntryList getNamesFromIterator (CKevaIterator& iter);
|
||||
|
||||
public:
|
||||
|
||||
@ -340,7 +340,7 @@ NameIterationTester::verify (const CCoinsView& view) const
|
||||
|
||||
/* Seek the iterator to the end first for "maximum confusion". This ensures
|
||||
that seeking to valtype() works. */
|
||||
std::unique_ptr<CNameIterator> iter(view.IterateNames());
|
||||
std::unique_ptr<CKevaIterator> iter(view.IterateKeys());
|
||||
const valtype end = ValtypeFromString ("zzzzzzzzzzzzzzzz");
|
||||
{
|
||||
valtype name;
|
||||
@ -392,14 +392,14 @@ NameIterationTester::EntryList
|
||||
NameIterationTester::getNamesFromView (const CCoinsView& view,
|
||||
const valtype& start)
|
||||
{
|
||||
std::unique_ptr<CNameIterator> iter(view.IterateNames());
|
||||
std::unique_ptr<CKevaIterator> iter(view.IterateKeys());
|
||||
iter->seek (start);
|
||||
|
||||
return getNamesFromIterator (*iter);
|
||||
}
|
||||
|
||||
NameIterationTester::EntryList
|
||||
NameIterationTester::getNamesFromIterator (CNameIterator& iter)
|
||||
NameIterationTester::getNamesFromIterator (CKevaIterator& iter)
|
||||
{
|
||||
EntryList res;
|
||||
|
||||
@ -702,9 +702,6 @@ BOOST_AUTO_TEST_CASE (name_tx_verification)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(keva_updates_undo)
|
||||
{
|
||||
/* Enable name history to test this on the go. */
|
||||
fNameHistory = true;
|
||||
|
||||
const valtype nameSpace = ValtypeFromString ("database-test-namespace");
|
||||
const valtype displayName = ValtypeFromString ("display name");
|
||||
const valtype key1 = ValtypeFromString ("key1");
|
||||
|
31
src/txdb.cpp
31
src/txdb.cpp
@ -84,7 +84,7 @@ std::vector<uint256> CCoinsViewDB::GetHeadBlocks() const {
|
||||
return vhashHeadBlocks;
|
||||
}
|
||||
|
||||
class CDbNameIterator : public CNameIterator
|
||||
class CDbKeyIterator : public CKevaIterator
|
||||
{
|
||||
|
||||
private:
|
||||
@ -94,42 +94,43 @@ private:
|
||||
|
||||
public:
|
||||
|
||||
~CDbNameIterator();
|
||||
~CDbKeyIterator();
|
||||
|
||||
/**
|
||||
* Construct a new name iterator for the database.
|
||||
* @param db The database to create the iterator for.
|
||||
*/
|
||||
CDbNameIterator(const CDBWrapper& db);
|
||||
CDbKeyIterator(const CDBWrapper& db, const valtype& nameSpace);
|
||||
|
||||
/* Implement iterator methods. */
|
||||
void seek(const valtype& start);
|
||||
bool next(valtype& name, CKevaData& data);
|
||||
bool next(valtype& key, CKevaData& data);
|
||||
|
||||
};
|
||||
|
||||
CDbNameIterator::~CDbNameIterator() {
|
||||
CDbKeyIterator::~CDbKeyIterator() {
|
||||
delete iter;
|
||||
}
|
||||
|
||||
CDbNameIterator::CDbNameIterator(const CDBWrapper& db)
|
||||
: iter(const_cast<CDBWrapper*>(&db)->NewIterator())
|
||||
CDbKeyIterator::CDbKeyIterator(const CDBWrapper& db, const valtype& ns)
|
||||
: iter(const_cast<CDBWrapper*>(&db)->NewIterator()), CKevaIterator(ns)
|
||||
{
|
||||
seek(valtype());
|
||||
}
|
||||
|
||||
void CDbNameIterator::seek(const valtype& start) {
|
||||
iter->Seek(std::make_pair(DB_NAME, start));
|
||||
void CDbKeyIterator::seek(const valtype& start) {
|
||||
iter->Seek(std::make_pair(DB_NAME, std::make_pair(nameSpace, start)));
|
||||
}
|
||||
|
||||
bool CDbNameIterator::next(valtype& name, CKevaData& data) {
|
||||
bool CDbKeyIterator::next(valtype& key, CKevaData& data) {
|
||||
if (!iter->Valid())
|
||||
return false;
|
||||
|
||||
std::pair<char, valtype> key;
|
||||
if (!iter->GetKey(key) || key.first != DB_NAME)
|
||||
std::pair<char, std::pair<valtype, valtype>> curKey;
|
||||
if (!iter->GetKey(curKey) || curKey.first != DB_NAME)
|
||||
return false;
|
||||
name = key.second;
|
||||
|
||||
key = std::get<1>(curKey.second);
|
||||
|
||||
if (!iter->GetValue(data)) {
|
||||
return error("%s : failed to read data from iterator", __func__);
|
||||
@ -139,8 +140,8 @@ bool CDbNameIterator::next(valtype& name, CKevaData& data) {
|
||||
return true;
|
||||
}
|
||||
|
||||
CNameIterator* CCoinsViewDB::IterateNames() const {
|
||||
return new CDbNameIterator(db);
|
||||
CKevaIterator* CCoinsViewDB::IterateKeys(const valtype& nameSpace) const {
|
||||
return new CDbKeyIterator(db, nameSpace);
|
||||
}
|
||||
|
||||
bool CCoinsViewDB::GetNamespace(const valtype &nameSpace, CKevaData &data) const {
|
||||
|
@ -79,7 +79,7 @@ public:
|
||||
bool GetNamespace(const valtype &nameSpace, 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;
|
||||
CNameIterator* IterateNames() const override;
|
||||
CKevaIterator* IterateKeys(const valtype& nameSpace) const override;
|
||||
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, const CKevaCache &names) override;
|
||||
CCoinsViewCursor *Cursor() const override;
|
||||
|
||||
|
@ -1,4 +1,8 @@
|
||||
// Copyright (c) 2018 Jianping Wu
|
||||
// Copyright (c) 2014-2017 Daniel Kraft
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
// Copyright (c) 2018 the Kevacoin Core Developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
@ -20,6 +24,7 @@
|
||||
#include "wallet/wallet.h"
|
||||
|
||||
#include <univalue.h>
|
||||
#include <boost/xpressive/xpressive_dynamic.hpp>
|
||||
|
||||
const int NAMESPACE_LENGTH = 21;
|
||||
const std::string DUMMY_NAMESPACE = "___DUMMY_NAMESPACE___";
|
||||
@ -33,7 +38,7 @@ UniValue keva_namespace(const JSONRPCRequest& request)
|
||||
if (!EnsureWalletIsAvailable (pwallet, request.fHelp))
|
||||
return NullUniValue;
|
||||
|
||||
if (request.fHelp || request.params.size () != 1)
|
||||
if (request.fHelp || request.params.size() != 1)
|
||||
throw std::runtime_error (
|
||||
"keva_namespace \"display_name\"\n"
|
||||
"\nRegister a namespace with the given display name.\n"
|
||||
@ -55,7 +60,7 @@ 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");
|
||||
}
|
||||
|
||||
@ -140,7 +145,7 @@ UniValue keva_list_namespaces(const JSONRPCRequest& request)
|
||||
|
||||
CKevaScript kevaOp;
|
||||
int nOut = -1;
|
||||
for (unsigned i = 0; i < tx.tx->vout.size (); ++i)
|
||||
for (unsigned i = 0; i < tx.tx->vout.size(); ++i)
|
||||
{
|
||||
const CKevaScript cur(tx.tx->vout[i].scriptPubKey);
|
||||
if (cur.isKevaOp ())
|
||||
@ -236,17 +241,17 @@ UniValue keva_put(const JSONRPCRequest& request)
|
||||
if (!DecodeKevaNamespace(namespaceStr, Params(), nameSpace)) {
|
||||
throw JSONRPCError (RPC_INVALID_PARAMETER, "invalid namespace id");
|
||||
}
|
||||
if (nameSpace.size () > MAX_NAMESPACE_LENGTH)
|
||||
if (nameSpace.size() > MAX_NAMESPACE_LENGTH)
|
||||
throw JSONRPCError (RPC_INVALID_PARAMETER, "the namespace is too long");
|
||||
|
||||
const std::string keyStr = request.params[1].get_str ();
|
||||
const valtype key = ValtypeFromString (keyStr);
|
||||
if (key.size () > MAX_KEY_LENGTH)
|
||||
if (key.size() > MAX_KEY_LENGTH)
|
||||
throw JSONRPCError (RPC_INVALID_PARAMETER, "the key is too long");
|
||||
|
||||
const std::string valueStr = request.params[2].get_str ();
|
||||
const valtype value = ValtypeFromString (valueStr);
|
||||
if (value.size () > MAX_VALUE_LENGTH)
|
||||
if (value.size() > MAX_VALUE_LENGTH)
|
||||
throw JSONRPCError (RPC_INVALID_PARAMETER, "the value is too long");
|
||||
|
||||
EnsureWalletIsUnlocked(pwallet);
|
||||
@ -317,13 +322,13 @@ UniValue keva_delete(const JSONRPCRequest& request)
|
||||
if (!DecodeKevaNamespace(namespaceStr, Params(), nameSpace)) {
|
||||
throw JSONRPCError (RPC_INVALID_PARAMETER, "invalid namespace id");
|
||||
}
|
||||
if (nameSpace.size () > MAX_NAMESPACE_LENGTH) {
|
||||
if (nameSpace.size() > MAX_NAMESPACE_LENGTH) {
|
||||
throw JSONRPCError (RPC_INVALID_PARAMETER, "the namespace is too long");
|
||||
}
|
||||
|
||||
const std::string keyStr = request.params[1].get_str ();
|
||||
const valtype key = ValtypeFromString (keyStr);
|
||||
if (key.size () > MAX_KEY_LENGTH) {
|
||||
if (key.size() > MAX_KEY_LENGTH) {
|
||||
throw JSONRPCError (RPC_INVALID_PARAMETER, "the key is too long");
|
||||
}
|
||||
|
||||
@ -414,12 +419,12 @@ UniValue keva_get(const JSONRPCRequest& request)
|
||||
if (!DecodeKevaNamespace(namespaceStr, Params(), nameSpace)) {
|
||||
throw JSONRPCError (RPC_INVALID_PARAMETER, "invalid namespace id");
|
||||
}
|
||||
if (nameSpace.size () > MAX_NAMESPACE_LENGTH)
|
||||
if (nameSpace.size() > MAX_NAMESPACE_LENGTH)
|
||||
throw JSONRPCError (RPC_INVALID_PARAMETER, "the namespace is too long");
|
||||
|
||||
const std::string keyStr = request.params[1].get_str ();
|
||||
const valtype key = ValtypeFromString (keyStr);
|
||||
if (key.size () > MAX_KEY_LENGTH)
|
||||
if (key.size() > MAX_KEY_LENGTH)
|
||||
throw JSONRPCError (RPC_INVALID_PARAMETER, "the key is too long");
|
||||
|
||||
CKevaData data;
|
||||
@ -445,7 +450,7 @@ UniValue keva_get(const JSONRPCRequest& request)
|
||||
|
||||
UniValue keva_pending(const JSONRPCRequest& request)
|
||||
{
|
||||
if (request.fHelp || request.params.size () > 1)
|
||||
if (request.fHelp || request.params.size() > 1)
|
||||
throw std::runtime_error (
|
||||
"keva_pending (\"namespace\")\n"
|
||||
"\nList unconfirmed keva operations in the mempool.\n"
|
||||
@ -523,3 +528,221 @@ UniValue keva_pending(const JSONRPCRequest& request)
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the help string description to use for keva info objects.
|
||||
* @param indent Indentation at the line starts.
|
||||
* @param trailing Trailing string (e. g., comma for an array of these objects).
|
||||
* @return The description string.
|
||||
*/
|
||||
std::string getKevaInfoHelp (const std::string& indent, const std::string& trailing)
|
||||
{
|
||||
std::ostringstream res;
|
||||
|
||||
res << indent << "{" << std::endl;
|
||||
res << indent << " \"name\": xxxxx, "
|
||||
<< "(string) the requested name" << std::endl;
|
||||
res << indent << " \"value\": xxxxx, "
|
||||
<< "(string) the name's current value" << std::endl;
|
||||
res << indent << " \"txid\": xxxxx, "
|
||||
<< "(string) the name's last update tx" << std::endl;
|
||||
res << indent << " \"address\": xxxxx, "
|
||||
<< "(string) the address holding the name" << std::endl;
|
||||
res << indent << " \"height\": xxxxx, "
|
||||
<< "(numeric) the name's last update height" << std::endl;
|
||||
res << indent << "}" << trailing << std::endl;
|
||||
|
||||
return res.str ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility routine to construct a "name info" object to return. This is used
|
||||
* for name_show and also name_list.
|
||||
* @param name The name.
|
||||
* @param value The name's value.
|
||||
* @param outp The last update's outpoint.
|
||||
* @param addr The name's address script.
|
||||
* @param height The name's last update height.
|
||||
* @return A JSON object to return.
|
||||
*/
|
||||
UniValue
|
||||
getKevaInfo (const valtype& name, const valtype& value, const COutPoint& outp,
|
||||
const CScript& addr, int height)
|
||||
{
|
||||
UniValue obj(UniValue::VOBJ);
|
||||
obj.pushKV("name", ValtypeToString(name));
|
||||
obj.pushKV("value", ValtypeToString(value));
|
||||
obj.pushKV("txid", outp.hash.GetHex());
|
||||
obj.pushKV("vout", static_cast<int>(outp.n));
|
||||
|
||||
/* Try to extract the address. May fail if we can't parse the script
|
||||
as a "standard" script. */
|
||||
CTxDestination dest;
|
||||
std::string addrStr;
|
||||
if (ExtractDestination(addr, dest))
|
||||
addrStr = EncodeDestination(dest);
|
||||
else
|
||||
addrStr = "<nonstandard>";
|
||||
obj.pushKV("address", addrStr);
|
||||
|
||||
/* Calculate expiration data. */
|
||||
const int curHeight = chainActive.Height ();
|
||||
const Consensus::Params& params = Params ().GetConsensus ();
|
||||
obj.pushKV("height", height);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return name info object for a CNameData object.
|
||||
* @param name The name.
|
||||
* @param data The name's data.
|
||||
* @return A JSON object to return.
|
||||
*/
|
||||
UniValue
|
||||
getKevaInfo (const valtype& name, const CKevaData& data)
|
||||
{
|
||||
return getKevaInfo(name, data.getValue (), data.getUpdateOutpoint (),
|
||||
data.getAddress (), data.getHeight ());
|
||||
}
|
||||
|
||||
|
||||
UniValue keva_filter(const JSONRPCRequest& request)
|
||||
{
|
||||
if (request.fHelp || request.params.size() > 6 || request.params.size() == 0)
|
||||
throw std::runtime_error(
|
||||
"keva_filter (\"namespaceId\" (\"regexp\" (\"from\" (\"nb\" (\"stat\")))))\n"
|
||||
"\nScan and list keys matching a regular expression.\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"namespace\" (string) namespace Id\n"
|
||||
"2. \"regexp\" (string, optional) filter keys with this regexp\n"
|
||||
"3. \"maxage\" (numeric, optional, default=36000) only consider names updated in the last \"maxage\" blocks; 0 means all names\n"
|
||||
"4. \"from\" (numeric, optional, default=0) return from this position onward; index starts at 0\n"
|
||||
"5. \"nb\" (numeric, optional, default=0) return only \"nb\" entries; 0 means all\n"
|
||||
"6. \"stat\" (string, optional) if set to the string \"stat\", print statistics instead of returning the names\n"
|
||||
"\nResult:\n"
|
||||
"[\n"
|
||||
+ getKevaInfoHelp (" ", ",") +
|
||||
" ...\n"
|
||||
"]\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli ("keva_filter", "\"\" 5")
|
||||
+ HelpExampleCli ("keva_filter", "\"^id/\"")
|
||||
+ HelpExampleCli ("keva_filter", "\"^id/\" 36000 0 0 \"stat\"")
|
||||
+ HelpExampleRpc ("keva_scan", "\"^d/\"")
|
||||
);
|
||||
|
||||
RPCTypeCheck(request.params, {
|
||||
UniValue::VSTR, UniValue::VSTR, UniValue::VNUM,
|
||||
UniValue::VNUM, UniValue::VNUM, UniValue::VSTR
|
||||
});
|
||||
|
||||
if (IsInitialBlockDownload()) {
|
||||
throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD,
|
||||
"Kevacoin is downloading blocks...");
|
||||
}
|
||||
|
||||
ObserveSafeMode();
|
||||
|
||||
/* ********************** */
|
||||
/* Interpret parameters. */
|
||||
|
||||
bool haveRegexp(false);
|
||||
boost::xpressive::sregex regexp;
|
||||
|
||||
valtype nameSpace;
|
||||
int maxage(36000), from(0), nb(0);
|
||||
bool stats(false);
|
||||
|
||||
if (request.params.size() >= 1) {
|
||||
const std::string namespaceStr = request.params[0].get_str();
|
||||
valtype nameSpace;
|
||||
if (!DecodeKevaNamespace(namespaceStr, Params(), nameSpace)) {
|
||||
throw JSONRPCError (RPC_INVALID_PARAMETER, "invalid namespace id");
|
||||
}
|
||||
}
|
||||
|
||||
if (request.params.size() >= 2) {
|
||||
haveRegexp = true;
|
||||
regexp = boost::xpressive::sregex::compile (request.params[1].get_str());
|
||||
}
|
||||
|
||||
if (request.params.size() >= 3)
|
||||
maxage = request.params[2].get_int();
|
||||
if (maxage < 0)
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER,
|
||||
"'maxage' should be non-negative");
|
||||
|
||||
if (request.params.size() >= 4)
|
||||
from = request.params[3].get_int ();
|
||||
|
||||
if (from < 0)
|
||||
throw JSONRPCError (RPC_INVALID_PARAMETER, "'from' should be non-negative");
|
||||
|
||||
if (request.params.size() >= 5)
|
||||
nb = request.params[4].get_int ();
|
||||
|
||||
if (nb < 0)
|
||||
throw JSONRPCError (RPC_INVALID_PARAMETER, "'nb' should be non-negative");
|
||||
|
||||
if (request.params.size() >= 6) {
|
||||
if (request.params[5].get_str() != "stat")
|
||||
throw JSONRPCError (RPC_INVALID_PARAMETER,
|
||||
"fifth argument must be the literal string 'stat'");
|
||||
stats = true;
|
||||
}
|
||||
|
||||
/* ******************************************* */
|
||||
/* Iterate over names to build up the result. */
|
||||
|
||||
UniValue keys(UniValue::VARR);
|
||||
unsigned count(0);
|
||||
|
||||
LOCK (cs_main);
|
||||
|
||||
valtype key;
|
||||
CKevaData data;
|
||||
std::unique_ptr<CKevaIterator> iter(pcoinsTip->IterateKeys(nameSpace));
|
||||
while (iter->next(key, data)) {
|
||||
const int age = chainActive.Height() - data.getHeight();
|
||||
assert(age >= 0);
|
||||
if (maxage != 0 && age >= maxage)
|
||||
continue;
|
||||
|
||||
if (haveRegexp) {
|
||||
const std::string keyStr = ValtypeToString(key);
|
||||
boost::xpressive::smatch matches;
|
||||
if (!boost::xpressive::regex_search(keyStr, matches, regexp))
|
||||
continue;
|
||||
}
|
||||
|
||||
if (from > 0) {
|
||||
--from;
|
||||
continue;
|
||||
}
|
||||
assert(from == 0);
|
||||
|
||||
if (stats)
|
||||
++count;
|
||||
else
|
||||
keys.push_back(getKevaInfo(key, data));
|
||||
|
||||
if (nb > 0) {
|
||||
--nb;
|
||||
if (nb == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* ********************************************************** */
|
||||
/* Return the correct result (take stats mode into account). */
|
||||
|
||||
if (stats) {
|
||||
UniValue res(UniValue::VOBJ);
|
||||
res.pushKV("blocks", chainActive.Height());
|
||||
res.pushKV("count", static_cast<int>(count));
|
||||
return res;
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
@ -3597,6 +3597,7 @@ extern UniValue keva_delete(const JSONRPCRequest& request);
|
||||
extern UniValue keva_get(const JSONRPCRequest& request);
|
||||
extern UniValue keva_list_namespaces(const JSONRPCRequest& request);
|
||||
extern UniValue keva_pending(const JSONRPCRequest& request);
|
||||
extern UniValue keva_filter(const JSONRPCRequest& request);
|
||||
|
||||
static const CRPCCommand commands[] =
|
||||
{ // category name actor (function) argNames
|
||||
@ -3661,7 +3662,8 @@ static const CRPCCommand commands[] =
|
||||
{ "kevacoin", "keva_put", &keva_put, {"namespace", "key", "value"} },
|
||||
{ "kevacoin", "keva_delete", &keva_delete, {"namespace", "key"} },
|
||||
{ "kevacoin", "keva_get", &keva_get, {"namespace", "key"} },
|
||||
{ "kevacoin", "keva_pending", &keva_pending, {"namespace"} }
|
||||
{ "kevacoin", "keva_pending", &keva_pending, {"namespace"} },
|
||||
{ "kevacoin", "keva_filter", &keva_filter, {"namespace", "regexp", "from", "nb", "stat"} }
|
||||
};
|
||||
|
||||
void RegisterWalletRPCCommands(CRPCTable &t)
|
||||
|
Loading…
Reference in New Issue
Block a user