diff --git a/src/keva/common.cpp b/src/keva/common.cpp index 576808eda..0d4406e37 100644 --- a/src/keva/common.cpp +++ b/src/keva/common.cpp @@ -226,8 +226,9 @@ CKevaCache::remove(const valtype& nameSpace, const valtype& key) { auto name = std::make_tuple(nameSpace, key); const EntryMap::iterator ei = entries.find(name); - if (ei != entries.end()) + if (ei != entries.end()) { entries.erase(ei); + } deleted.insert(name); } diff --git a/src/keva/main.cpp b/src/keva/main.cpp index 30b3d44dd..d39863920 100644 --- a/src/keva/main.cpp +++ b/src/keva/main.cpp @@ -36,33 +36,35 @@ CKevaTxUndo::fromOldState(const valtype& nameSpace, const valtype& key, const CC void CKevaTxUndo::apply(CCoinsViewCache& view) const { - if (isNew) + if (isNew) { view.DeleteName(nameSpace, key); - else + } + else { view.SetName(nameSpace, key, oldData, true); + } } /* ************************************************************************** */ /* CKevaMemPool. */ void -CKevaMemPool::addUnchecked (const uint256& hash, const CTxMemPoolEntry& entry) +CKevaMemPool::addUnchecked(const uint256& hash, const CKevaScript& kevaOp) { AssertLockHeld (pool.cs); - if (entry.isNamespaceRegistration()) { - const valtype& nameSpace = entry.getNamespace(); - listUnconfirmedNamespaces.push_back(std::make_tuple(hash, nameSpace, entry.getDisplayName())); + if (kevaOp.isNamespaceRegistration()) { + const valtype& nameSpace = kevaOp.getOpNamespace(); + listUnconfirmedNamespaces.push_back(std::make_tuple(hash, nameSpace, kevaOp.getOpNamespaceDisplayName())); } - if (entry.isKeyUpdate()) { - const valtype& nameSpace = entry.getNamespace(); - listUnconfirmedKeyValues.push_back(std::make_tuple(hash, nameSpace, entry.getKey(), entry.getValue())); + if (kevaOp.getKevaOp() == OP_KEVA_PUT) { + const valtype& nameSpace = kevaOp.getOpNamespace(); + listUnconfirmedKeyValues.push_back(std::make_tuple(hash, nameSpace, kevaOp.getOpKey(), kevaOp.getOpValue())); } - if (entry.isKeyDelete()) { - const valtype& nameSpace = entry.getNamespace(); + if (kevaOp.getKevaOp() == OP_KEVA_DELETE) { + const valtype& nameSpace = kevaOp.getOpNamespace(); const valtype& empty = ValtypeFromString(""); - listUnconfirmedKeyValues.push_back(std::make_tuple(hash, nameSpace, entry.getKey(), empty)); + listUnconfirmedKeyValues.push_back(std::make_tuple(hash, nameSpace, kevaOp.getOpKey(), empty)); } } @@ -375,8 +377,8 @@ void ApplyKevaTransaction(const CTransaction& tx, unsigned nHeight, } else if (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 ()); + LogPrint (BCLog::KEVA, "Updating key at height %d: %s %s\n", + nHeight, ValtypeToString(nameSpace).c_str(), ValtypeToString(key).c_str()); CKevaTxUndo opUndo; opUndo.fromOldState(nameSpace, key, view); diff --git a/src/keva/main.h b/src/keva/main.h index 73518de41..0de11816f 100644 --- a/src/keva/main.h +++ b/src/keva/main.h @@ -147,18 +147,17 @@ public: } /** - * Add an entry without checking it. It should have been checked - * already. If this conflicts with the mempool, it may throw. + * Added unconfirmed keva values. * @param hash The tx hash. - * @param entry The new mempool entry. + * @param entry The new keva entry. */ - void addUnchecked (const uint256& hash, const CTxMemPoolEntry& entry); + void addUnchecked(const uint256& hash, const CKevaScript& kevaOp); /** * Remove the given mempool entry. It is assumed that it is present. * @param entry The entry to remove. */ - void remove (const CTxMemPoolEntry& entry); + void remove(const CTxMemPoolEntry& entry); /** * Remove conflicts for the given tx, based on name operations. I. e., @@ -167,7 +166,7 @@ public: * @param tx The transaction for which we look for conflicts. * @param removed Put removed tx here. */ - void removeConflicts (const CTransaction& tx); + void removeConflicts(const CTransaction& tx); /** * Check if a tx can be added (based on name criteria) without @@ -175,7 +174,7 @@ public: * @param tx The transaction to check. * @return True if it doesn't conflict. */ - bool checkTx (const CTransaction& tx) const; + bool checkTx(const CTransaction& tx) const; /** Keva get unconfirmed namespaces. */ void getUnconfirmedNamespaceList(std::vector>& nameSpaces) const; diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 0dbde7ec7..f7a86dd00 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -423,7 +423,6 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, nTransactionsUpdated++; totalTxSize += entry.GetTxSize(); if (minerPolicyEstimator) {minerPolicyEstimator->processTransaction(entry, validFeeEstimate);} - kevaMemPool.addUnchecked (hash, entry); vTxHashes.emplace_back(tx.GetWitnessHash(), newit); newit->vTxHashesIdx = vTxHashes.size() - 1; diff --git a/src/txmempool.h b/src/txmempool.h index 5642a82bf..e92feaa0f 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -693,6 +693,12 @@ public: return kevaMemPool.checkTx(tx); } + inline void addKevaUnchecked(const uint256& hash, const CKevaScript& kevaOp) + { + AssertLockHeld(cs); + kevaMemPool.addUnchecked(hash, kevaOp); + } + /** Keva get unconfirmed key values. */ bool getUnconfirmedKeyValue(const valtype& nameSpace, const valtype& key, valtype& value) const; diff --git a/src/undo.h b/src/undo.h index 2d32eb0d5..23da8833a 100644 --- a/src/undo.h +++ b/src/undo.h @@ -110,6 +110,7 @@ public: template inline void SerializationOp(Stream& s, Operation ser_action) { READWRITE(vtxundo); + READWRITE(vkevaundo); } }; diff --git a/src/wallet/rpckeva.cpp b/src/wallet/rpckeva.cpp index 91c4a1ce7..fe4510cfa 100644 --- a/src/wallet/rpckeva.cpp +++ b/src/wallet/rpckeva.cpp @@ -184,7 +184,8 @@ UniValue keva_list_namespaces(const JSONRPCRequest& request) UniValue res(UniValue::VARR); for (const auto& item : mapObjects) { UniValue obj(UniValue::VOBJ); - obj.pushKV(item.first, item.second); + obj.pushKV("namespaceId", item.first); + obj.pushKV("displayName", item.second); res.push_back(obj); } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 99f28f1f7..66ca777c7 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3176,6 +3176,20 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CCon return false; } } else { + CKevaScript kevaOp; + auto _tx = wtx.tx; + if (_tx->IsKevacoin()) { + for (const auto& txOut : _tx->vout) { + const CKevaScript curKevaOp(txOut.scriptPubKey); + if (!curKevaOp.isKevaOp()) { + continue; + } + assert(!kevaOp.isKevaOp()); + kevaOp = curKevaOp; + } + assert(kevaOp.isKevaOp()); + mempool.addKevaUnchecked(wtx.GetHash(), kevaOp); + } wtx.RelayWalletTransaction(connman); } } diff --git a/test/functional/feature_keva.py b/test/functional/feature_keva.py index 2b228a6f5..ba3124098 100755 --- a/test/functional/feature_keva.py +++ b/test/functional/feature_keva.py @@ -16,15 +16,91 @@ import re class KevaTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True - self.num_nodes = 3 + self.num_nodes = 2 def setup_network(self): super().setup_network() - connect_nodes(self.nodes[0], 2) + connect_nodes_bi(self.nodes, 0, 1) self.sync_all() + def run_unconfirmed_namespaces(self): + self.sync_all() + response = self.nodes[1].keva_namespace('second_namespace') + namespaceId = response['namespaceId'] + response = self.nodes[1].keva_list_namespaces() + # Node 0's unconfirmed namespace should not be here + assert(len(response) == 1) + + # Node 0's unconfirmed operations should not be here + self.nodes[1].keva_put(namespaceId, 'second_key', 'second_value') + response = self.nodes[1].keva_pending() + # Pending namespace and put operation. + assert(len(response) == 2) + self.sync_all() + + def run_test_disconnect_block(self, namespaceId): + key = 'This is the test key' + value = 'This is the test value' + self.nodes[0].keva_put(namespaceId, key, value) + self.nodes[0].generate(1) + response = self.nodes[0].keva_get(namespaceId, key) + assert(response['value'] == value) + # Disconnect the block + self.sync_all() + self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash()) + self.nodes[1].invalidateblock(self.nodes[1].getbestblockhash()) + self.sync_all() + response = self.nodes[0].keva_get(namespaceId, key) + assert(response['value'] == '') + + self.log.info("Test undeleting after disconnecting blocks") + keyToDelete = 'This is the test key to delete' + valueToDelete = 'This is the test value of the key' + self.nodes[0].keva_put(namespaceId, keyToDelete, valueToDelete) + self.nodes[0].generate(1) + response = self.nodes[0].keva_get(namespaceId, keyToDelete) + assert(response['value'] == valueToDelete) + # Now delete the key + self.nodes[0].keva_delete(namespaceId, keyToDelete) + self.nodes[0].generate(1) + response = self.nodes[0].keva_get(namespaceId, keyToDelete) + assert(response['value'] == '') + # Disconnect the block + self.sync_all() + self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash()) + self.nodes[1].invalidateblock(self.nodes[1].getbestblockhash()) + self.sync_all() + response = self.nodes[0].keva_get(namespaceId, keyToDelete) + # The value should be undeleted. + assert(response['value'] == valueToDelete) + + self.log.info("Test namespace after disconnecting blocks") + displayName = 'A new namspace' + response = self.nodes[0].keva_namespace(displayName) + newNamespaceId = response['namespaceId'] + self.nodes[0].generate(1) + response = self.nodes[0].keva_list_namespaces() + found = False + print(response) + for ns in response: + if (ns['namespaceId'] == newNamespaceId): + found = True + assert(found) + # Now disconnect the block and the namespace should be gone. + self.sync_all() + self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash()) + self.nodes[1].invalidateblock(self.nodes[1].getbestblockhash()) + self.sync_all() + found = False + for ns in response: + if (ns['namespaceId'] == newNamespaceId): + found = True + assert(not found) + + def run_test(self): - self.nodes[0].generate(161) #block 161 + self.nodes[0].generate(105) + self.nodes[1].generate(105) response = self.nodes[0].keva_namespace('first_namespace') namespaceId = response['namespaceId'] @@ -38,6 +114,9 @@ class KevaTest(BitcoinTestFramework): response = self.nodes[0].keva_get(namespaceId, key) assert(response['value'] == value) + self.log.info("Test other wallet's unconfirmed namespaces do not show up in ours") + self.run_unconfirmed_namespaces() + self.nodes[0].generate(1) response = self.nodes[0].keva_pending() assert(len(response) == 0) @@ -99,5 +178,9 @@ class KevaTest(BitcoinTestFramework): response = self.nodes[0].keva_get(namespaceId, keyToDelete) assert(response['value'] == newValue) + self.log.info("Test disconnecting blocks") + self.run_test_disconnect_block(namespaceId) + + if __name__ == '__main__': KevaTest().main()