diff --git a/src/coins.cpp b/src/coins.cpp index 8dfb35c2e..fba8e88ea 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -10,6 +10,8 @@ bool CCoinsView::GetCoin(const COutPoint &outpoint, Coin &coin) const { return false; } uint256 CCoinsView::GetBestBlock() const { return uint256(); } std::vector CCoinsView::GetHeadBlocks() const { return std::vector(); } +bool CCoinsView::GetName(const valtype &nameSpace, const valtype &key, CKevaData &data) const { return false; } +bool CCoinsView::GetNamesForHeight(unsigned nHeight, std::set& names) const { return false; } bool CCoinsView::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { return false; } CCoinsViewCursor *CCoinsView::Cursor() const { return nullptr; } @@ -24,6 +26,8 @@ bool CCoinsViewBacked::GetCoin(const COutPoint &outpoint, Coin &coin) const { re bool CCoinsViewBacked::HaveCoin(const COutPoint &outpoint) const { return base->HaveCoin(outpoint); } uint256 CCoinsViewBacked::GetBestBlock() const { return base->GetBestBlock(); } std::vector CCoinsViewBacked::GetHeadBlocks() const { return base->GetHeadBlocks(); } +bool CCoinsViewBacked::GetName(const valtype &nameSpace, const valtype &key, CKevaData &data) const { return false; } +bool CCoinsViewBacked::GetNamesForHeight(unsigned nHeight, std::set& names) const { return false; } void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; } bool CCoinsViewBacked::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { return base->BatchWrite(mapCoins, hashBlock); } CCoinsViewCursor *CCoinsViewBacked::Cursor() const { return base->Cursor(); } @@ -142,6 +146,96 @@ void CCoinsViewCache::SetBestBlock(const uint256 &hashBlockIn) { hashBlock = hashBlockIn; } +bool CCoinsViewCache::GetName(const valtype &nameSpace, const valtype &key, CKevaData &data) const { + if (cacheNames.isDeleted(nameSpace, key)) + return false; + if (cacheNames.get(nameSpace, key, data)) + return true; + + /* Note: This does not attempt to cache name queries. The cache + only keeps track of changes! */ + + return base->GetName(nameSpace, key, data); +} + + +bool CCoinsViewCache::GetNamesForHeight(unsigned nHeight, std::set& names) const { + /* Query the base view first, and then apply the cached changes (if + there are any). */ + + if (!base->GetNamesForHeight(nHeight, names)) + return false; + + cacheNames.updateNamesForHeight(nHeight, names); + return true; +} + +/* 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) { + CKevaData oldData; + if (GetName(nameSpace, key, oldData)) + { +#if 0 + cacheNames.removeExpireIndex(name, oldData.getHeight()); + + /* Update the name history. If we are undoing, we expect that + the top history item matches the data being set now. If we + are not undoing, push the overwritten data onto the history stack. + Note that we only have to do this if the name already existed + in the database. Otherwise, no special action is required + for the name history. */ + if (fNameHistory) + { + CNameHistory history; + if (!GetNameHistory(name, history)) + { + /* Ensure that the history stack is indeed (still) empty + and was not modified by the failing GetNameHistory call. */ + assert(history.empty()); + } + + if (undo) + history.pop(data); + else + history.push(oldData); + + cacheNames.setHistory(name, history); + } +#endif + } else + assert (!undo); + + cacheNames.set(nameSpace, key, data); +#if 0 + cacheNames.addExpireIndex(name, data.getHeight()); +#endif +} + +void CCoinsViewCache::DeleteName(const valtype &nameSpace, const valtype &key) { + CKevaData oldData; + if (GetName(nameSpace, key, oldData)) { +#if 0 + cacheNames.removeExpireIndex(name, oldData.getHeight()); +#endif + } + else + assert(false); + +#if 0 + if (fNameHistory) + { + /* When deleting a name, the history should already be clean. */ + CNameHistory history; + assert (!GetNameHistory(name, history) || history.empty()); + } +#endif + + cacheNames.remove(nameSpace, key); +} + bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn) { for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end(); it = mapCoins.erase(it)) { // Ignore non-dirty entries (optimization). diff --git a/src/coins.h b/src/coins.h index c6850947e..5dee9fe8c 100644 --- a/src/coins.h +++ b/src/coins.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -163,6 +164,12 @@ public: //! the old block hash, in that order. virtual std::vector GetHeadBlocks() const; + // Get a name (if it exists) + virtual bool GetName(const valtype& nameSpace, const valtype& key, CKevaData& data) const; + + // Query for names that were updated at the given height + virtual bool GetNamesForHeight(unsigned nHeight, std::set& names) const; + //! Do a bulk modification (multiple Coin changes + BestBlock change). //! The passed mapCoins can be modified. virtual bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock); @@ -190,6 +197,8 @@ public: bool HaveCoin(const COutPoint &outpoint) const override; uint256 GetBestBlock() const override; std::vector GetHeadBlocks() const override; + bool GetName(const valtype& nameSpace, const valtype& key, CKevaData& data) const override; + bool GetNamesForHeight(unsigned nHeight, std::set& names) const override; void SetBackend(CCoinsView &viewIn); bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) override; CCoinsViewCursor *Cursor() const override; @@ -211,6 +220,9 @@ protected: /* Cached dynamic memory usage for the inner Coin objects. */ mutable size_t cachedCoinsUsage; + /** Name changes cache. */ + CKevaCache cacheNames; + public: CCoinsViewCache(CCoinsView *baseIn); @@ -224,11 +236,17 @@ public: bool HaveCoin(const COutPoint &outpoint) const override; uint256 GetBestBlock() const override; void SetBestBlock(const uint256 &hashBlock); + bool GetName(const valtype &nameSpace, const valtype &key, CKevaData &data) const override; + bool GetNamesForHeight(unsigned nHeight, std::set& names) const override; bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) override; CCoinsViewCursor* Cursor() const override { throw std::logic_error("CCoinsViewCache cursor iteration not supported."); } + /* Changes to the name database. */ + void SetName(const valtype &nameSpace, const valtype &key, const CKevaData &data, bool undo); + void DeleteName(const valtype &nameSpace, const valtype &key); + /** * Check if we have the given utxo already loaded in this cache. * The semantics are the same as HaveCoin(), but no calls to diff --git a/src/keva/common.cpp b/src/keva/common.cpp index df2c2f043..2651c2ee2 100644 --- a/src/keva/common.cpp +++ b/src/keva/common.cpp @@ -38,7 +38,7 @@ CNameIterator::~CNameIterator () } /* ************************************************************************** */ -/* CNameCacheNameIterator. */ +/* CKevaCacheNameIterator. */ class CCacheNameIterator : public CNameIterator { @@ -46,7 +46,7 @@ class CCacheNameIterator : public CNameIterator private: /** Reference to cache object that is used. */ - const CNameCache& cache; + const CKevaCache& cache; /** Base iterator to combine with the cache. */ CNameIterator* base; @@ -59,7 +59,7 @@ private: CKevaData baseData; /** Iterator of the cache's entries. */ - CNameCache::EntryMap::const_iterator cacheIter; + CKevaCache::EntryMap::const_iterator cacheIter; /* Call the base iterator's next() routine to fill in the internal "cache" for the next entry. This already skips entries that are @@ -73,7 +73,7 @@ public: * @param c The cache object to use. * @param b The base iterator. */ - CCacheNameIterator (const CNameCache& c, CNameIterator* b); + CCacheNameIterator (const CKevaCache& c, CNameIterator* b); /* Destruct, this deletes also the base iterator. */ ~CCacheNameIterator (); @@ -84,7 +84,7 @@ public: }; -CCacheNameIterator::CCacheNameIterator (const CNameCache& c, CNameIterator* b) +CCacheNameIterator::CCacheNameIterator (const CKevaCache& c, CNameIterator* b) : cache(c), base(b) { /* Add a seek-to-start to ensure that everything is consistent. This call @@ -147,7 +147,7 @@ CCacheNameIterator::next (valtype& name, CKevaData& data) { assert (baseName != cacheIter->first); - CNameCache::NameComparator cmp; + CKevaCache::NameComparator cmp; useBase = cmp (baseName, cacheIter->first); } } @@ -170,11 +170,12 @@ CCacheNameIterator::next (valtype& name, CKevaData& data) } /* ************************************************************************** */ -/* CNameCache. */ +/* CKevaCache. */ bool -CNameCache::get (const valtype& name, CKevaData& data) const +CKevaCache::get (const valtype& nameSpace, const valtype& key, CKevaData& data) const { + valtype name = nameSpace + key; const EntryMap::const_iterator i = entries.find (name); if (i == entries.end ()) return false; @@ -184,8 +185,9 @@ CNameCache::get (const valtype& name, CKevaData& data) const } void -CNameCache::set (const valtype& name, const CKevaData& data) +CKevaCache::set (const valtype& nameSpace, const valtype& key, const CKevaData& data) { + valtype name = nameSpace + key; const std::set::iterator di = deleted.find (name); if (di != deleted.end ()) deleted.erase (di); @@ -198,8 +200,9 @@ CNameCache::set (const valtype& name, const CKevaData& data) } void -CNameCache::remove (const valtype& name) +CKevaCache::remove (const valtype& nameSpace, const valtype& key) { + valtype name = nameSpace + key; const EntryMap::iterator ei = entries.find (name); if (ei != entries.end ()) entries.erase (ei); @@ -208,13 +211,14 @@ CNameCache::remove (const valtype& name) } CNameIterator* -CNameCache::iterateNames (CNameIterator* base) const +CKevaCache::iterateNames (CNameIterator* base) const { return new CCacheNameIterator (*this, base); } +#if 0 bool -CNameCache::getHistory (const valtype& name, CNameHistory& res) const +CKevaCache::getHistory (const valtype& name, CNameHistory& res) const { assert (fNameHistory); @@ -227,7 +231,7 @@ CNameCache::getHistory (const valtype& name, CNameHistory& res) const } void -CNameCache::setHistory (const valtype& name, const CNameHistory& data) +CKevaCache::setHistory (const valtype& name, const CNameHistory& data) { assert (fNameHistory); @@ -237,9 +241,10 @@ CNameCache::setHistory (const valtype& name, const CNameHistory& data) else history.insert (std::make_pair (name, data)); } +#endif void -CNameCache::updateNamesForHeight (unsigned nHeight, +CKevaCache::updateNamesForHeight (unsigned nHeight, std::set& names) const { /* Seek in the map of cached entries to the first one corresponding @@ -263,21 +268,21 @@ CNameCache::updateNamesForHeight (unsigned nHeight, } void -CNameCache::addExpireIndex (const valtype& name, unsigned height) +CKevaCache::addExpireIndex (const valtype& name, unsigned height) { const ExpireEntry entry(height, name); expireIndex[entry] = true; } void -CNameCache::removeExpireIndex (const valtype& name, unsigned height) +CKevaCache::removeExpireIndex (const valtype& name, unsigned height) { const ExpireEntry entry(height, name); expireIndex[entry] = false; } void -CNameCache::apply (const CNameCache& cache) +CKevaCache::apply (const CKevaCache& cache) { for (EntryMap::const_iterator i = cache.entries.begin (); i != cache.entries.end (); ++i) diff --git a/src/keva/common.h b/src/keva/common.h index 8587de2ff..3b90302df 100644 --- a/src/keva/common.h +++ b/src/keva/common.h @@ -274,7 +274,7 @@ public: * new names (or updates to them), this also keeps track of deleted names * (when rolling back changes). */ -class CNameCache +class CKevaCache { private: @@ -306,6 +306,7 @@ public: * the height is serialised as byte-array with little-endian order, * which does not correspond to the ordering by actual value. */ +#if 0 class ExpireEntry { public: @@ -369,6 +370,7 @@ public: } }; +#endif /** * Type of name entry map. This is public because it is also used @@ -383,17 +385,21 @@ private: /** Deleted names. */ std::set deleted; +#if 0 /** * New or updated history stacks. If they are empty, the corresponding * database entry is deleted instead. */ std::map history; +#endif +#if 0 /** * Changes to be performed to the expire index. The entry is mapped * to either "true" (meaning to add it) or "false" (delete). */ std::map expireIndex; +#endif friend class CCacheNameIterator; @@ -404,8 +410,10 @@ public: { entries.clear (); deleted.clear (); +#if 0 history.clear (); expireIndex.clear (); +#endif } /** @@ -417,38 +425,40 @@ public: inline bool empty () const { - if (entries.empty () && deleted.empty ()) - { - assert (history.empty () && expireIndex.empty ()); - return true; - } + if (entries.empty () && deleted.empty ()) { +#if 0 + assert (history.empty () && expireIndex.empty ()); +#endif + return true; + } return false; } /* See if the given name is marked as deleted. */ inline bool - isDeleted (const valtype& name) const + isDeleted (const valtype& nameSpace, const valtype& key) const { return (deleted.count (name) > 0); } /* Try to get a name's associated data. This looks only in entries, and doesn't care about deleted data. */ - bool get (const valtype& name, CKevaData& data) const; + bool get (const valtype& nameSpace, const valtype& key, CKevaData& data) const; /* Insert (or update) a name. If it is marked as "deleted", this also removes the "deleted" mark. */ - void set (const valtype& name, const CKevaData& data); + void set (const valtype& nameSpace, const valtype& key, const CKevaData& data); /* Delete a name. If it is in the "entries" set also, remove it there. */ - void remove (const valtype& name); + void remove (const valtype& nameSpace, const valtype& key); /* 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; +#if 0 /** * Query for an history entry. * @param name The name to look up. @@ -463,6 +473,7 @@ public: * @param data The new history entry. */ void setHistory (const valtype& name, const CNameHistory& data); +#endif /* Query the cached changes to the expire index. In particular, for a given height and a given set of names that were indexed to @@ -470,14 +481,16 @@ public: are represented by the cached expire index changes. */ void updateNamesForHeight (unsigned nHeight, std::set& names) const; +#if 0 /* Add an expire-index entry. */ void addExpireIndex (const valtype& name, unsigned height); /* Remove an expire-index entry. */ void removeExpireIndex (const valtype& name, unsigned height); +#endif /* Apply all the changes in the passed-in record on top of this one. */ - void apply (const CNameCache& cache); + void apply (const CKevaCache& cache); /* Write all cached changes to a database batch update object. */ void writeBatch (CDBBatch& batch) const; diff --git a/src/keva/main.cpp b/src/keva/main.cpp index 0fd41672c..67d2febc0 100644 --- a/src/keva/main.cpp +++ b/src/keva/main.cpp @@ -26,19 +26,20 @@ /* CKevaTxUndo. */ void -CKevaTxUndo::fromOldState(const valtype& nm, const CCoinsView& view) +CKevaTxUndo::fromOldState(const valtype& nameSpace, const valtype& key, const CCoinsView& view) { - name = nm; - isNew = !view.GetName(name, oldData); + this->nameSpace = nameSpace; + this->key = key; + isNew = !view.GetName(nameSpace, key, oldData); } void -CKevaTxUndo::apply (CCoinsViewCache& view) const +CKevaTxUndo::apply(CCoinsViewCache& view) const { if (isNew) - view.DeleteName(name); + view.DeleteName(nameSpace, key); else - view.SetName(name, oldData, true); + view.SetName(nameSpace, key, oldData, true); } /* ************************************************************************** */ @@ -504,6 +505,7 @@ ApplyNameTransaction (const CTransaction& tx, unsigned nHeight, { 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. */ @@ -521,34 +523,34 @@ ApplyNameTransaction (const CTransaction& tx, unsigned nHeight, } 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.IsNamecoin ()) + 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) - { - const CNameScript op(tx.vout[i].scriptPubKey); - if (op.isNameOp () && op.isAnyUpdate ()) - { - const valtype& name = op.getOpName (); - LogPrint (BCLog::NAMES, "Updating name at height %d: %s\n", - nHeight, ValtypeToString (name).c_str ()); - - CKevaTxUndo opUndo; - opUndo.fromOldState (name, view); - undo.vnameundo.push_back (opUndo); - - CNameData data; - data.fromScript (nHeight, COutPoint (tx.GetHash (), i), op); - view.SetName (name, data, false); - } + for (unsigned i = 0; i < tx.vout.size (); ++i) { + const CKevaScript op(tx.vout[i].scriptPubKey); + if (op.isKevaOp () && op.isAnyUpdate ()) { + const valtype& nameSpace = op.getOpNamespace(); + const valtype& key = op.getOpKey(); + LogPrint (BCLog::KEVA, "Updating name at height %d: %s\n", + nHeight, ValtypeToString (nameSpace).c_str ()); + + 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); } + } } bool diff --git a/src/keva/main.h b/src/keva/main.h index 9c244c118..d99545c5e 100644 --- a/src/keva/main.h +++ b/src/keva/main.h @@ -51,8 +51,11 @@ class CKevaTxUndo private: - /** The name this concerns. */ - valtype name; + /** The namespace this concerns. */ + valtype nameSpace; + + /** The key this concerns. */ + valtype key; /** Whether this was an entirely new name (no update). */ bool isNew; @@ -79,7 +82,7 @@ public: * @param nm The name that is being updated. * @param view The (old!) chain state. */ - void fromOldState (const valtype& nm, const CCoinsView& view); + void fromOldState (const valtype& nameSpace, const valtype& key, const CCoinsView& view); /** * Apply the undo to the chain state given. diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp index 6f463cabf..2d967fd42 100644 --- a/src/primitives/transaction.cpp +++ b/src/primitives/transaction.cpp @@ -62,6 +62,12 @@ uint256 CMutableTransaction::GetHash() const return SerializeHash(*this, SER_GETHASH, SERIALIZE_TRANSACTION_NO_WITNESS); } +void CMutableTransaction::SetKevacoin() +{ + assert (nVersion == CTransaction::CURRENT_VERSION); + nVersion = CTransaction::KEVACOIN_VERSION; +} + uint256 CTransaction::ComputeHash() const { return SerializeHash(*this, SER_GETHASH, SERIALIZE_TRANSACTION_NO_WITNESS); diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index cd348fdbe..a5ddc5ae4 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -266,6 +266,7 @@ class CTransaction public: // Default transaction version. static const int32_t CURRENT_VERSION=2; + static const int32_t KEVACOIN_VERSION=0x7100; // Changing the default transaction version requires a two step process: first // adapting relay policy by bumping MAX_STANDARD_VERSION, and then later date @@ -335,6 +336,11 @@ public: return (vin.size() == 1 && vin[0].prevout.IsNull()); } + bool IsKevacoin() const + { + return nVersion == KEVACOIN_VERSION; + } + friend bool operator==(const CTransaction& a, const CTransaction& b) { return a.hash == b.hash; @@ -404,6 +410,12 @@ struct CMutableTransaction } return false; } + + /** + * Turn this into a Kevacoin version transaction. It is assumed + * that it isn't already. + */ + void SetKevacoin(); }; typedef std::shared_ptr CTransactionRef; diff --git a/src/script/keva.h b/src/script/keva.h index 0a6be3bf7..2383245b3 100644 --- a/src/script/keva.h +++ b/src/script/keva.h @@ -52,7 +52,7 @@ public: * @return True iff this is a name operation. */ inline bool - isNameOp () const + isKevaOp () const { switch (op) { case OP_KEVA_PUT: @@ -80,10 +80,10 @@ public: /** * Return the name operation. This returns OP_NAME_NEW, OP_NAME_FIRSTUPDATE - * or OP_NAME_UPDATE. Do not call if this is not a name script. + * or OP_NAME_UPDATE. Do not call if this is not a keva script. * @return The name operation opcode. */ - inline opcodetype getNameOp() const + inline opcodetype getKevaOp() const { switch (op) { case OP_KEVA_PUT: @@ -119,9 +119,9 @@ public: /** * Return the name operation name. This call is only valid for * OP_KEVA_NAMESPACE or OP_KEVA_PUT. - * @return The name operation's name. + * @return The keva operation's namespace. */ - inline const valtype& getOpName () const + inline const valtype& getOpNamespace() const { switch (op) { case OP_KEVA_PUT: @@ -140,7 +140,7 @@ public: * OP_KEVA_PUT. * @return The name operation's value. */ - inline const valtype& getOpValue () const + inline const valtype& getOpNamespaceValue() const { switch (op) { case OP_KEVA_PUT: @@ -153,15 +153,51 @@ public: } /** - * Check if the given script is a name script. This is a utility method. + * Return the keva operation key. This call is only valid for + * OP_KEVA_PUT. + * @return The keva operation's value. + */ + inline const valtype& getOpKey() const + { + switch (op) { + case OP_KEVA_PUT: + return args[0]; + + case OP_KEVA_NAMESPACE: + return args[0]; + + default: + assert(false); + } + } + + /** + * Return the keva operation value. This call is only valid for + * OP_KEVA_PUT. + * @return The keva operation's value. + */ + inline const valtype& getOpValue() const + { + switch (op) { + case OP_KEVA_PUT: + // args[1] is namespace + return args[2]; + + default: + assert (false); + } + } + + /** + * Check if the given script is a keva script. This is a utility method. * @param script The script to parse. * @return True iff it is a name script. */ static inline bool - isNameScript (const CScript& script) + isKevaScript (const CScript& script) { const CKevaScript op(script); - return op.isNameOp (); + return op.isKevaOp (); } /** diff --git a/src/script/script.h b/src/script/script.h index 331ecfba1..0981310cf 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -38,6 +38,8 @@ static const int MAX_STACK_SIZE = 1000; // otherwise as UNIX timestamp. static const unsigned int LOCKTIME_THRESHOLD = 500000000; // Tue Nov 5 00:53:20 1985 UTC +typedef std::vector valtype; + template std::vector ToByteVector(const T& in) { diff --git a/src/undo.h b/src/undo.h index 1f10c6652..78527d364 100644 --- a/src/undo.h +++ b/src/undo.h @@ -101,6 +101,9 @@ class CBlockUndo public: std::vector vtxundo; // for all but the coinbase + /** Stack of operations done to the keva database. */ + std::vector vkevaundo; + ADD_SERIALIZE_METHODS; template diff --git a/src/util.h b/src/util.h index 288c0437b..4ce441915 100644 --- a/src/util.h +++ b/src/util.h @@ -104,6 +104,7 @@ namespace BCLog { COINDB = (1 << 18), QT = (1 << 19), LEVELDB = (1 << 20), + KEVA = (1 << 21), ALL = ~(uint32_t)0, }; }