diff --git a/src/keva/main.cpp b/src/keva/main.cpp index 834a7abc5..50a328009 100644 --- a/src/keva/main.cpp +++ b/src/keva/main.cpp @@ -51,15 +51,11 @@ CKevaMemPool::addUnchecked (const uint256& hash, const CTxMemPoolEntry& entry) AssertLockHeld (pool.cs); if (entry.isNamespaceRegistration()) { const valtype& nameSpace = entry.getNamespace(); - assert(mapNamespaceRegs.count(nameSpace) == 0); - mapNamespaceRegs.insert(std::make_pair(nameSpace, hash)); listUnconfirmedNamespaces.push_back(std::make_tuple(hash, nameSpace, entry.getDisplayName())); } if (entry.isNamespaceKeyUpdate ()) { const valtype& nameSpace = entry.getNamespace(); - assert(mapNamespaceUpdates.count(nameSpace) == 0); - mapNamespaceUpdates.insert (std::make_pair(nameSpace, hash)); listUnconfirmedKeyValues.push_back(std::make_tuple(hash, nameSpace, entry.getKey(), entry.getValue())); } } @@ -94,9 +90,6 @@ void CKevaMemPool::remove(const CTxMemPoolEntry& entry) { AssertLockHeld (pool.cs); if (entry.isNamespaceRegistration()) { - const NamespaceTxMap::iterator mit = mapNamespaceRegs.find(entry.getNamespace()); - assert (mit != mapNamespaceRegs.end()); - mapNamespaceRegs.erase(mit); auto hash = entry.GetTx().GetHash(); for (auto iter = listUnconfirmedNamespaces.begin(); iter != listUnconfirmedNamespaces.end(); ++iter) { if (std::get<0>(*iter) == hash) { @@ -107,9 +100,6 @@ void CKevaMemPool::remove(const CTxMemPoolEntry& entry) } if (entry.isNamespaceKeyUpdate()) { - const NamespaceTxMap::iterator mit = mapNamespaceUpdates.find(entry.getNamespace()); - assert (mit != mapNamespaceUpdates.end()); - mapNamespaceUpdates.erase(mit); auto hash = entry.GetTx().GetHash(); for (auto iter = listUnconfirmedKeyValues.begin(); iter != listUnconfirmedKeyValues.end(); ++iter) { if (std::get<0>(*iter) == hash) { @@ -146,79 +136,6 @@ CKevaMemPool::removeConflicts(const CTransaction& tx) #endif } -void -CKevaMemPool::check(const CCoinsView& coins) const -{ - AssertLockHeld (pool.cs); - - const uint256 blockHash = coins.GetBestBlock (); - int nHeight; - if (blockHash.IsNull()) - nHeight = 0; - else - nHeight = mapBlockIndex.find (blockHash)->second->nHeight; - - std::set nameRegs; - std::set namespaceKeyUpdates; - for (const auto& entry : pool.mapTx) { - const uint256 txHash = entry.GetTx ().GetHash (); - if (entry.isNamespaceRegistration()) { - const valtype& nameSpace = entry.getNamespace(); - - const NamespaceTxMap::const_iterator mit = mapNamespaceRegs.find(nameSpace); - assert (mit != mapNamespaceRegs.end()); - assert (mit->second == txHash); - - assert (nameRegs.count(nameSpace) == 0); - nameRegs.insert(nameSpace); -#if 0 - /* The old name should be expired already. Note that we use - nHeight+1 for the check, because that's the height at which - the mempool tx will actually be mined. */ - CKevaData data; - if (coins.GetName(name, data)) - assert (data.isExpired (nHeight + 1)); -#endif - } - - if (entry.isNamespaceKeyUpdate()) { - const valtype& nameSpace = entry.getNamespace(); - const NamespaceTxMap::const_iterator mit = mapNamespaceUpdates.find(nameSpace); - assert (mit != mapNamespaceUpdates.end ()); - assert (mit->second == txHash); - - assert (namespaceKeyUpdates.count(nameSpace) == 0); - namespaceKeyUpdates.insert(nameSpace); - -#if 0 - /* As above, use nHeight+1 for the expiration check. */ - CKevaData data; - if (!coins.GetName(name, data)) { - assert (false); - } - assert (!data.isExpired (nHeight + 1)); -#endif - } - } - - assert(nameRegs.size() == mapNamespaceRegs.size()); - assert(namespaceKeyUpdates.size() == mapNamespaceUpdates.size()); - - /* Check that nameRegs and nameUpdates are disjoint. They must be since - a name can only be in either category, depending on whether it exists - at the moment or not. */ -#if 0 - // Is this neccesary? - for (const auto& nameSpace : nameRegs) { - assert(namespaceKeyUpdates.count(name) == 0); - } -#endif - - for (const auto& nameSpace : namespaceKeyUpdates) { - assert(nameRegs.count(nameSpace) == 0); - } -} - bool CKevaMemPool::validateNamespace(const CTransaction& tx, const valtype& nameSpace) const { valtype kevaNamespace = ToByteVector(Hash160(ToByteVector(tx.vin[0].prevout.hash))); @@ -227,17 +144,14 @@ bool CKevaMemPool::validateNamespace(const CTransaction& tx, const valtype& name } bool -CKevaMemPool::checkTx (const CTransaction& tx) const +CKevaMemPool::checkTx(const CTransaction& tx) const { AssertLockHeld (pool.cs); - if (!tx.IsKevacoin ()) + if (!tx.IsKevacoin ()) { return true; + } - /* In principle, multiple name_updates could be performed within the - mempool at once (building upon each other). This is disallowed, though, - since the current mempool implementation does not like it. (We keep - track of only a single update tx for each name.) */ for (const auto& txout : tx.vout) { const CKevaScript nameOp(txout.scriptPubKey); if (!nameOp.isKevaOp ()) @@ -247,10 +161,6 @@ CKevaMemPool::checkTx (const CTransaction& tx) const { const valtype& nameSpace = nameOp.getOpNamespace(); std::map::const_iterator mi; - mi = mapNamespaceRegs.find(nameSpace); - if (mi != mapNamespaceRegs.end ()) { - return false; - } if (!validateNamespace(tx, nameSpace)) { return false; } @@ -259,11 +169,6 @@ CKevaMemPool::checkTx (const CTransaction& tx) const case OP_KEVA_PUT: { - const valtype& nameSpace = nameOp.getOpNamespace(); - const valtype& key = nameOp.getOpKey(); - if (updatesNamespace(nameSpace)) { - return false; - } break; } @@ -320,33 +225,27 @@ CheckKevaTransaction (const CTransaction& tx, unsigned nHeight, { const std::string strTxid = tx.GetHash ().GetHex (); const char* txid = strTxid.c_str (); - const bool fMempool = (flags & SCRIPT_VERIFY_KEVA_MEMPOOL); - -#if 0 - /* Ignore historic bugs. */ - CChainParams::BugType type; - if (Params ().IsHistoricBug (tx.GetHash (), nHeight, type)) - return true; -#endif - /* As a first step, try to locate inputs and outputs of the transaction - that are keva scripts. At most one input and output should be - a keva operation. */ + /* + As a first step, try to locate inputs and outputs of the transaction + that are keva scripts. At most one input and output should be + a keva operation. + */ int nameIn = -1; CKevaScript nameOpIn; Coin coinIn; - for (unsigned i = 0; i < tx.vin.size (); ++i) { + for (unsigned i = 0; i < tx.vin.size(); ++i) { const COutPoint& prevout = tx.vin[i].prevout; Coin coin; - if (!view.GetCoin (prevout, coin)) { - return error ("%s: failed to fetch input coin for %s", __func__, txid); + if (!view.GetCoin(prevout, coin)) { + return error("%s: failed to fetch input coin for %s", __func__, txid); } const CKevaScript op(coin.out.scriptPubKey); if (op.isKevaOp()) { if (nameIn != -1) { - return state.Invalid (error ("%s: multiple name inputs into transaction %s", __func__, txid)); + return state.Invalid(error("%s: multiple name inputs into transaction %s", __func__, txid)); } nameIn = i; nameOpIn = op; @@ -356,71 +255,43 @@ CheckKevaTransaction (const CTransaction& tx, unsigned nHeight, int nameOut = -1; CKevaScript nameOpOut; - 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()) { if (nameOut != -1) { - return state.Invalid (error ("%s: multiple name outputs from" - " transaction %s", __func__, txid)); + return state.Invalid(error("%s: multiple name outputs from transaction %s", __func__, txid)); } nameOut = i; nameOpOut = op; } } - /* Check that no name inputs/outputs are present for a non-Namecoin tx. - If that's the case, all is fine. For a Namecoin tx instead, there - should be at least an output (for NAME_NEW, no inputs are expected). */ - - if (!tx.IsKevacoin ()) { - if (nameIn != -1) - return state.Invalid (error ("%s: non-Kevacoin tx %s has keva inputs", - __func__, txid)); - if (nameOut != -1) - return state.Invalid (error ("%s: non-Kevacoin tx %s at height %u" - " has keva outputs", - __func__, txid, nHeight)); + /* + Check that no keva inputs/outputs are present for a non-Kevacoin tx. + If that's the case, all is fine. For a kevacoin tx instead, there + should be at least an output. + */ + if (!tx.IsKevacoin()) { + if (nameIn != -1) { + return state.Invalid(error("%s: non-Kevacoin tx %s has keva inputs", __func__, txid)); + } + if (nameOut != -1) { + return state.Invalid (error ("%s: non-Kevacoin tx %s at height %u has keva outputs", + __func__, txid, nHeight)); + } return true; } assert(tx.IsKevacoin ()); - if (nameOut == -1) - return state.Invalid (error ("%s: Kevacoin tx %s has no keva outputs", - __func__, txid)); + if (nameOut == -1) { + return state.Invalid (error ("%s: Kevacoin tx %s has no keva outputs", __func__, txid)); + } /* Reject "greedy names". */ -#if 0 - const Consensus::Params& params = Params ().GetConsensus (); - if (tx.vout[nameOut].nValue < params.rules->MinNameCoinAmount(nHeight)) { - return state.Invalid (error ("%s: greedy name", __func__)); - } -#else if (tx.vout[nameOut].nValue < KEVA_LOCKED_AMOUNT) { return state.Invalid (error ("%s: greedy name", __func__)); } -#endif - -#if 0 - /* Handle NAME_NEW now, since this is easy and different from the other - operations. */ - - if (nameOpOut.getNameOp () == OP_NAME_NEW) - { - if (nameIn != -1) - return state.Invalid (error ("CheckKevaTransaction: NAME_NEW with" - " previous name input")); - - if (nameOpOut.getOpHash ().size () != 20) - return state.Invalid (error ("CheckKevaTransaction: NAME_NEW's hash" - " has wrong size")); - - return true; - } - - /* Now that we have ruled out NAME_NEW, check that we have a previous - name input that is being updated. */ -#endif if (nameOpOut.isNamespaceRegistration()) { if (nameOpOut.getOpNamespaceDisplayName().size () > MAX_VALUE_LENGTH) { @@ -429,7 +300,8 @@ CheckKevaTransaction (const CTransaction& tx, unsigned nHeight, return true; } - assert (nameOpOut.isAnyUpdate()); + assert(nameOpOut.isAnyUpdate()); + if (nameIn == -1) { return state.Invalid(error("CheckKevaTransaction: update without previous keva input")); } @@ -451,82 +323,9 @@ CheckKevaTransaction (const CTransaction& tx, unsigned nHeight, } if (nameSpace != nameOpIn.getOpNamespace()) { - return state.Invalid (error ("%s: KEVA_PUT namespace mismatch to prev tx" - " found in %s", __func__, txid)); - } - - CKevaData oldName; - const valtype& namespaceIn = nameOpIn.getOpNamespace(); - if (!view.GetNamespace(namespaceIn, oldName)) { - return state.Invalid (error ("%s: Namespace not found", __func__)); - } - assert (tx.vin[nameIn].prevout == oldName.getUpdateOutpoint()); - -#if 0 - /* This is actually redundant, since expired names are removed - from the UTXO set and thus not available to be spent anyway. - But it does not hurt to enforce this here, too. It is also - exercised by the unit tests. */ - CKevaData oldName; - const valtype& namespaceIn = nameOpIn.getOpNamespace(); - const valtype& keyIn = nameOpIn.getOpKey(); - if (!view.GetName(namespaceIn, keyIn, oldName)) { - return state.Invalid (error ("%s: KEVA_PUT name does not exist", - __func__)); - } - if (oldName.isExpired (nHeight)) - return state.Invalid (error ("%s: trying to update expired name", - __func__)); - /* This is an internal consistency check. If everything is fine, - the input coins from the UTXO database should match the - name database. */ - assert (static_cast (coinIn.nHeight) == oldName.getHeight()); - assert (tx.vin[nameIn].prevout == oldName.getUpdateOutpoint()); -#endif - return true; - } - -#if 0 - /* Finally, NAME_FIRSTUPDATE. */ - - assert(nameOpOut.getNameOp () == OP_NAME_FIRSTUPDATE); - if (nameOpIn.getNameOp () != OP_NAME_NEW) - return state.Invalid (error ("CheckKevaTransaction: NAME_FIRSTUPDATE" - " with non-NAME_NEW prev tx")); - - /* Maturity of NAME_NEW is checked only if we're not adding - to the mempool. */ - if (!fMempool) - { - assert (static_cast (coinIn.nHeight) != MEMPOOL_HEIGHT); - if (coinIn.nHeight + MIN_FIRSTUPDATE_DEPTH > nHeight) - return state.Invalid (error ("CheckKevaTransaction: NAME_NEW" - " is not mature for FIRST_UPDATE")); + return state.Invalid(error("%s: KEVA_PUT namespace mismatch to prev tx found in %s", __func__, txid)); } - - if (nameOpOut.getOpRand ().size () > 20) - return state.Invalid (error ("CheckKevaTransaction: NAME_FIRSTUPDATE" - " rand too large, %d bytes", - nameOpOut.getOpRand ().size ())); - - { - valtype toHash(nameOpOut.getOpRand ()); - toHash.insert (toHash.end (), name.begin (), name.end ()); - const uint160 hash = Hash160 (toHash); - if (hash != uint160 (nameOpIn.getOpHash ())) - return state.Invalid (error ("CheckKevaTransaction: NAME_FIRSTUPDATE" - " hash mismatch")); } - - CNameData oldName; - if (view.GetName (name, oldName) && !oldName.isExpired (nHeight)) - return state.Invalid (error ("CheckKevaTransaction: NAME_FIRSTUPDATE" - " on an unexpired name")); - - /* We don't have to specifically check that miners don't create blocks with - conflicting NAME_FIRSTUPDATE's, since the mining's CCoinsViewCache - takes care of this with the check above already. */ -#endif return true; } @@ -578,128 +377,6 @@ void ApplyNameTransaction(const CTransaction& tx, unsigned nHeight, } } -#if 0 -bool -ExpireNames (unsigned nHeight, CCoinsViewCache& view, CBlockUndo& undo, - std::set& names) -{ - names.clear (); - - /* The genesis block contains no name expirations. */ - if (nHeight == 0) - return true; - - /* Otherwise, find out at which update heights names have expired - since the last block. If the expiration depth changes, this could - be multiple heights at once. */ - - const Consensus::Params& params = Params ().GetConsensus (); - const unsigned expDepthOld = params.rules->NameExpirationDepth (nHeight - 1); - const unsigned expDepthNow = params.rules->NameExpirationDepth (nHeight); - - if (expDepthNow > nHeight) - return true; - - /* Both are inclusive! The last expireTo was nHeight - 1 - expDepthOld, - now we start at this value + 1. */ - const unsigned expireFrom = nHeight - expDepthOld; - const unsigned expireTo = nHeight - expDepthNow; - - /* It is possible that expireFrom = expireTo + 1, in case that the - expiration period is raised together with the block height. In this - case, no names expire in the current step. This case means that - the absolute expiration height "n - expirationDepth(n)" is - flat -- which is fine. */ - assert (expireFrom <= expireTo + 1); - - /* Find all names that expire at those depths. Note that GetNamesForHeight - clears the output set, to we union all sets here. */ - for (unsigned h = expireFrom; h <= expireTo; ++h) - { - std::set newNames; - view.GetNamesForHeight (h, newNames); - names.insert (newNames.begin (), newNames.end ()); - } - - /* Expire all those names. */ - for (std::set::const_iterator i = names.begin (); - i != names.end (); ++i) - { - const std::string nameStr = ValtypeToString (*i); - - CNameData data; - if (!view.GetName (*i, data)) - return error ("%s : name '%s' not found in the database", - __func__, nameStr.c_str ()); - if (!data.isExpired (nHeight)) - return error ("%s : name '%s' is not actually expired", - __func__, nameStr.c_str ()); - - /* Special rule: When d/postmortem expires (the name used by - libcoin in the name-stealing demonstration), it's coin - is already spent. Ignore. */ - if (nHeight == 175868 && nameStr == "d/postmortem") - continue; - - const COutPoint& out = data.getUpdateOutpoint (); - Coin coin; - if (!view.GetCoin(out, coin)) - return error ("%s : name coin for '%s' is not available", - __func__, nameStr.c_str ()); - const CNameScript nameOp(coin.out.scriptPubKey); - if (!nameOp.isNameOp () || !nameOp.isAnyUpdate () - || nameOp.getOpName () != *i) - return error ("%s : name coin to be expired is wrong script", __func__); - - if (!view.SpendCoin (out, &coin)) - return error ("%s : spending name coin failed", __func__); - undo.vexpired.push_back (coin); - } - - return true; -} - -bool -UnexpireNames (unsigned nHeight, CBlockUndo& undo, CCoinsViewCache& view, - std::set& names) -{ - names.clear (); - - /* The genesis block contains no name expirations. */ - if (nHeight == 0) - return true; - - std::vector::reverse_iterator i; - for (i = undo.vexpired.rbegin (); i != undo.vexpired.rend (); ++i) - { - const CNameScript nameOp(i->out.scriptPubKey); - if (!nameOp.isNameOp () || !nameOp.isAnyUpdate ()) - return error ("%s : wrong script to be unexpired", __func__); - - const valtype& name = nameOp.getOpName (); - if (names.count (name) > 0) - return error ("%s : name '%s' unexpired twice", - __func__, ValtypeToString (name).c_str ()); - names.insert (name); - - CNameData data; - if (!view.GetName (nameOp.getOpName (), data)) - return error ("%s : no data for name '%s' to be unexpired", - __func__, ValtypeToString (name).c_str ()); - if (!data.isExpired (nHeight) || data.isExpired (nHeight - 1)) - return error ("%s : name '%s' to be unexpired is not expired in the DB" - " or it was already expired before the current height", - __func__, ValtypeToString (name).c_str ()); - - if (ApplyTxInUndo (std::move(*i), view, - data.getUpdateOutpoint ()) != DISCONNECT_OK) - return error ("%s : failed to undo name coin spending", __func__); - } - - return true; -} -#endif - void CheckNameDB (bool disconnect) { diff --git a/src/keva/main.h b/src/keva/main.h index 23796b596..87f2d3c43 100644 --- a/src/keva/main.h +++ b/src/keva/main.h @@ -111,31 +111,6 @@ private: /** The parent mempool object. Used to, e. g., remove conflicting tx. */ CTxMemPool& pool; - /** Type used for internal indices. */ - typedef std::map NamespaceTxMap; - - /** Type used for indexing Tx */ - typedef std::tuple NamespaceKeyTuple; - - /** Type used for internal indices. */ - typedef std::map NamespaceKeyTxMap; - - - /** - * Keep track of namespaces that are registered by transactions in the pool. - * Map name to registering transaction. - */ - NamespaceTxMap mapNamespaceRegs; - - /** Map pending name updates to transaction IDs. */ - //NamespaceTxMap mapNamespaceUpdates; - - /** - * Keep track of key that are updated by transactions in the pool. - * Map key to registering transaction. - */ - NamespaceTxMap mapNamespaceUpdates; - /** * Pending/unconfirmed namespaces. * Tuple: txid, namespace, display name @@ -159,52 +134,15 @@ public: * Construct with reference to parent mempool. * @param p The parent pool. */ - explicit inline CKevaMemPool (CTxMemPool& p) - : pool(p), mapNamespaceRegs() - {} - - /** - * Check whether a particular namespace is being registered by - * some transaction in the mempool. Does not lock, this is - * done by the parent mempool (which calls through afterwards). - * @param name The name to check for. - * @return True iff there's a matching namespace registration in the pool. - */ - inline bool - registersNamespace (const valtype& nameSpace) const - { - return mapNamespaceRegs.count(nameSpace) > 0; - } - - /** - * Check whether a particular namespace has a pending update. Does not lock. - * @param name The namespace to check for. - * @return True iff there's a matching namespace update in the pool. - */ - inline bool - updatesNamespace(const valtype& nameSpace) const - { - return mapNamespaceUpdates.count(nameSpace) > 0; - } - - /** - * Return txid of transaction registering or updating a name. The returned - * txid is null if no such tx exists. - * @param name The name to check for. - * @return The txid that registers/updates it. Null if none. - */ - uint256 getTxForNamespace(const valtype& nameSpace) const; - - uint256 getTxForNamespaceKey(const valtype& nameSpace, const valtype& key) const; + explicit inline CKevaMemPool (CTxMemPool& p) : pool(p) {} /** * Clear all data. */ - inline void - clear () + inline void clear () { - mapNamespaceRegs.clear(); - mapNamespaceUpdates.clear(); + listUnconfirmedNamespaces.clear(); + listUnconfirmedKeyValues.clear(); } /** @@ -230,12 +168,6 @@ public: */ void removeConflicts (const CTransaction& tx); - /** - * Perform sanity checks. Throws if it fails. - * @param coins The coins view this represents. - */ - void check (const CCoinsView& coins) const; - /** * Check if a tx can be added (based on name criteria) without * causing a conflict. diff --git a/src/txmempool.h b/src/txmempool.h index 2423b8e16..1805b96b7 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -675,24 +675,6 @@ public: return (mapTx.count(hash) != 0); } - inline bool registersNamespace(const valtype& nameSpace) const - { - AssertLockHeld(cs); - return kevaMemPool.registersNamespace(nameSpace); - } - - inline bool updatesNamespace(const valtype& nameSpace) const - { - AssertLockHeld(cs); - return kevaMemPool.updatesNamespace(nameSpace); - } - - inline uint256 getTxForNamespace(const valtype& name) const - { - AssertLockHeld(cs); - return kevaMemPool.getTxForNamespace(name); - } - /** * Check if a tx can be added to it according to name criteria. * (The non-name criteria are checked in main.cpp and not here, we diff --git a/src/wallet/rpckeva.cpp b/src/wallet/rpckeva.cpp index dc92a7f0e..ae8ab2136 100644 --- a/src/wallet/rpckeva.cpp +++ b/src/wallet/rpckeva.cpp @@ -244,33 +244,16 @@ UniValue keva_put(const JSONRPCRequest& request) if (value.size () > MAX_VALUE_LENGTH) throw JSONRPCError (RPC_INVALID_PARAMETER, "the value is too long"); - /* 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.updatesNamespace(nameSpace)) { - throw JSONRPCError (RPC_TRANSACTION_ERROR, - "there is already a pending update for this name"); - } - } + EnsureWalletIsUnlocked(pwallet); - CKevaData oldData; - { - LOCK (cs_main); - if (!pcoinsTip->GetNamespace(nameSpace, oldData)) { - throw JSONRPCError (RPC_TRANSACTION_ERROR, - "this name can not be updated"); - } + COutput output; + std::string kevaNamespce = namespaceStr; + if (!pwallet->FindKevaCoin(output, kevaNamespce)) { + throw JSONRPCError (RPC_TRANSACTION_ERROR, "this name can not be updated"); } - - const COutPoint outp = oldData.getUpdateOutpoint(); + const COutPoint outp(output.tx->GetHash(), output.i); const CTxIn txIn(outp); - /* No more locking required, similarly to name_new. */ - - EnsureWalletIsUnlocked(pwallet); - CReserveKey keyName(pwallet); CPubKey pubKeyReserve; const bool ok = keyName.GetReservedKey(pubKeyReserve, true);