diff --git a/src/Makefile.am b/src/Makefile.am index a1a47f839..ee4491195 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -252,15 +252,13 @@ libbitcoin_wallet_a_SOURCES = \ wallet/fees.cpp \ wallet/init.cpp \ wallet/rpcdump.cpp \ + wallet/rpckeva.cpp \ wallet/rpcwallet.cpp \ wallet/wallet.cpp \ wallet/walletdb.cpp \ wallet/walletutil.cpp \ $(BITCOIN_CORE_H) -libbitcoin_wallet_a_SOURCES += \ - wallet/rpckeva.cpp - # crypto primitives library crypto_libbitcoin_crypto_a_CPPFLAGS = $(AM_CPPFLAGS) $(SSL_CFLAGS) crypto_libbitcoin_crypto_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) @@ -328,6 +326,8 @@ libbitcoin_consensus_a_SOURCES = \ script/bitcoinconsensus.cpp \ script/interpreter.cpp \ script/interpreter.h \ + script/keva.h \ + script/keva.cpp \ script/script.cpp \ script/script.h \ script/script_error.cpp \ @@ -338,9 +338,7 @@ libbitcoin_consensus_a_SOURCES = \ uint256.h \ utilstrencodings.cpp \ utilstrencodings.h \ - version.h \ - script/keva.h \ - script/keva.cpp + version.h # common: shared between bitcoind, and bitcoin-qt and non-server tools libbitcoin_common_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include index b48fcea84..7c1ef6a76 100644 --- a/src/Makefile.bench.include +++ b/src/Makefile.bench.include @@ -51,7 +51,7 @@ endif if ENABLE_WALLET bench_bench_litecoin_SOURCES += bench/coin_selection.cpp -bench_bench_litecoin_LDADD += $(LIBBITCOIN_WALLET) $(LIBBITCOIN_CRYPTO) +bench_bench_litecoin_LDADD += $(LIBBITCOIN_WALLET) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) endif bench_bench_litecoin_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) diff --git a/src/coins.cpp b/src/coins.cpp index 49b03be3e..a961000a3 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -10,6 +10,7 @@ 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::HasNamespace(const valtype &nameSpace) const { return false; } bool CCoinsView::GetName(const valtype &nameSpace, const valtype &key, CKevaData &data) const { return false; } bool CCoinsView::GetNamesForHeight(unsigned nHeight, std::set& names) const { return false; } bool CCoinsView::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { return false; } @@ -27,6 +28,7 @@ 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::HasNamespace(const valtype &nameSpace) const { return false; } bool CCoinsViewBacked::GetName(const valtype &nameSpace, const valtype &key, CKevaData &data) const { return false; } bool CCoinsViewBacked::GetNamesForHeight(unsigned nHeight, std::set& names) const { return false; } void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; } @@ -148,6 +150,10 @@ void CCoinsViewCache::SetBestBlock(const uint256 &hashBlockIn) { hashBlock = hashBlockIn; } +bool CCoinsViewCache::HasNamespace(const valtype &nameSpace) const { + return cacheNames.hasNamespace(nameSpace); +} + bool CCoinsViewCache::GetName(const valtype &nameSpace, const valtype &key, CKevaData &data) const { if (cacheNames.isDeleted(nameSpace, key)) return false; @@ -160,7 +166,6 @@ bool CCoinsViewCache::GetName(const valtype &nameSpace, const valtype &key, CKev 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). */ diff --git a/src/coins.h b/src/coins.h index 439dad6a1..1076f1f9b 100644 --- a/src/coins.h +++ b/src/coins.h @@ -164,6 +164,9 @@ public: //! the old block hash, in that order. virtual std::vector GetHeadBlocks() const; + // Check if a namespace exists. + virtual bool HasNamespace(const valtype& nameSpace) const; + // Get a name (if it exists) virtual bool GetName(const valtype& nameSpace, const valtype& key, CKevaData& data) const; @@ -200,6 +203,7 @@ public: bool HaveCoin(const COutPoint &outpoint) const override; uint256 GetBestBlock() const override; std::vector GetHeadBlocks() const override; + bool HasNamespace(const valtype& nameSpace) const override; bool GetName(const valtype& nameSpace, const valtype& key, CKevaData& data) const override; bool GetNamesForHeight(unsigned nHeight, std::set& names) const override; void SetBackend(CCoinsView &viewIn); @@ -240,6 +244,7 @@ public: bool HaveCoin(const COutPoint &outpoint) const override; uint256 GetBestBlock() const override; void SetBestBlock(const uint256 &hashBlock); + bool HasNamespace(const valtype &nameSpace) const override; bool GetName(const valtype &nameSpace, const valtype &key, CKevaData &data) const override; bool GetNamesForHeight(unsigned nHeight, std::set& names) const override; bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) override; diff --git a/src/keva/common.cpp b/src/keva/common.cpp index 95da255f6..c60721ce8 100644 --- a/src/keva/common.cpp +++ b/src/keva/common.cpp @@ -174,7 +174,7 @@ CCacheNameIterator::next (valtype& name, CKevaData& data) /* CKevaCache. */ bool -CKevaCache::get (const valtype& nameSpace, const valtype& key, CKevaData& data) const +CKevaCache::get(const valtype& nameSpace, const valtype& key, CKevaData& data) const { valtype name = nameSpace; name.insert( name.end(), key.begin(), key.end() ); @@ -186,8 +186,14 @@ CKevaCache::get (const valtype& nameSpace, const valtype& key, CKevaData& data) return true; } +bool CKevaCache::hasNamespace(const valtype& nameSpace) const +{ + auto ni = namespaces.find(nameSpace); + return (ni != namespaces.end()); +} + void -CKevaCache::set (const valtype& nameSpace, const valtype& key, const CKevaData& data) +CKevaCache::set(const valtype& nameSpace, const valtype& key, const CKevaData& data) { valtype name = nameSpace; name.insert( name.end(), key.begin(), key.end() ); @@ -200,10 +206,12 @@ CKevaCache::set (const valtype& nameSpace, const valtype& key, const CKevaData& ei->second = data; else entries.insert (std::make_pair (name, data)); + + namespaces.insert(nameSpace); } void -CKevaCache::remove (const valtype& nameSpace, const valtype& key) +CKevaCache::remove(const valtype& nameSpace, const valtype& key) { valtype name = nameSpace; name.insert( name.end(), key.begin(), key.end() ); @@ -215,7 +223,7 @@ CKevaCache::remove (const valtype& nameSpace, const valtype& key) } CNameIterator* -CKevaCache::iterateNames (CNameIterator* base) const +CKevaCache::iterateNames(CNameIterator* base) const { return new CCacheNameIterator (*this, base); } diff --git a/src/keva/common.h b/src/keva/common.h index 83ad51c18..df5a6a3ce 100644 --- a/src/keva/common.h +++ b/src/keva/common.h @@ -377,6 +377,7 @@ public: * by the unit tests. */ typedef std::map EntryMap; + typedef std::set NamespaceSet; private: @@ -384,6 +385,8 @@ private: EntryMap entries; /** Deleted names. */ std::set deleted; + /** Namespaces */ + NamespaceSet namespaces; #if 0 /** @@ -448,14 +451,16 @@ public: /* Try to get a name's associated data. This looks only in entries, and doesn't care about deleted data. */ - bool get (const valtype& nameSpace, const valtype& key, CKevaData& data) const; + bool get(const valtype& nameSpace, const valtype& key, CKevaData& data) const; + + bool hasNamespace(const valtype& nameSpace) const; /* Insert (or update) a name. If it is marked as "deleted", this also removes the "deleted" mark. */ - void set (const valtype& nameSpace, const valtype& key, 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& nameSpace, const valtype& key); + 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 diff --git a/src/txmempool.cpp b/src/txmempool.cpp index d1edde284..7b99f703e 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -284,7 +284,7 @@ void CTxMemPool::UpdateForRemoveFromMempool(const setEntries &entriesToRemove, b // should be a bit faster. // However, if we happen to be in the middle of processing a reorg, then // the mempool can be in an inconsistent state. In this case, the set - // of ancestors reachable via mapLinks will be the same as the set of + // of ancestors reachable via mapLinks will be the same as the set of // ancestors whose packages include this transaction, because when we // add a new transaction to the mempool in addUnchecked(), we assume it // has no children, and in the case of a reorg where that assumption is @@ -329,7 +329,7 @@ void CTxMemPoolEntry::UpdateAncestorState(int64_t modifySize, CAmount modifyFee, } CTxMemPool::CTxMemPool(CBlockPolicyEstimator* estimator) : - nTransactionsUpdated(0), minerPolicyEstimator(estimator) + nTransactionsUpdated(0), minerPolicyEstimator(estimator), kevaMemPool(*this) { _clear(); //lock free clear diff --git a/src/txmempool.h b/src/txmempool.h index 7f25a7c99..314866afa 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -472,6 +473,9 @@ private: mutable bool blockSinceLastRollingFeeBump; mutable double rollingMinimumFeeRate; //!< minimum fee to get into the pool, decreases exponentially + /** Keva-related mempool data. */ + CKevaMemPool kevaMemPool; + void trackPackageRemoved(const CFeeRate& rate); public: @@ -661,6 +665,38 @@ public: return (mapTx.count(hash) != 0); } + inline bool registersNamespace(const valtype& nameSpace) const + { + AssertLockHeld(cs); + return kevaMemPool.registersNamespace(nameSpace); + } + + inline bool updatesKey(const valtype& nameSpace, const valtype& key) const + { + AssertLockHeld(cs); + return kevaMemPool.updatesKey(nameSpace, key); + } + + 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 + * leave it there for as little changes as possible.) + * @param tx The tx that should be added. + * @return True if it doesn't conflict. + */ + inline bool + checkNameOps (const CTransaction& tx) const + { + AssertLockHeld(cs); + return kevaMemPool.checkTx(tx); + } + CTransactionRef get(const uint256& hash) const; TxMempoolInfo info(const uint256& hash) const; std::vector infoAll() const; diff --git a/src/wallet/rpckeva.cpp b/src/wallet/rpckeva.cpp index ebaf09654..4cd8ad7c2 100644 --- a/src/wallet/rpckeva.cpp +++ b/src/wallet/rpckeva.cpp @@ -24,8 +24,7 @@ /* ************************************************************************** */ -UniValue -keva_namespace (const JSONRPCRequest& request) +UniValue keva_namespace(const JSONRPCRequest& request) { CWallet* const pwallet = GetWalletForJSONRPCRequest(request); if (!EnsureWalletIsAvailable (pwallet, request.fHelp)) @@ -50,9 +49,9 @@ keva_namespace (const JSONRPCRequest& request) ObserveSafeMode (); - const std::string displayNameStr = request.params[0].get_str (); + const std::string displayNameStr = request.params[0].get_str(); const valtype displayName = ValtypeFromString (displayNameStr); - if (displayName.size () > MAX_NAME_LENGTH) + if (displayName.size () > MAX_NAMESPACE_LENGTH) throw JSONRPCError (RPC_INVALID_PARAMETER, "the name is too long"); /* No explicit locking should be necessary. CReserveKey takes care @@ -66,7 +65,7 @@ keva_namespace (const JSONRPCRequest& request) const bool ok = keyName.GetReservedKey(pubKey, true); assert (ok); - CKeyID keyId = pubKey.GetID() + CKeyID keyId = pubKey.GetID(); // The namespace is: Hash160(Hash160(keyId) || displayName) valtype toHash = ToByteVector(Hash160(ToByteVector(keyId))); @@ -79,24 +78,23 @@ keva_namespace (const JSONRPCRequest& request) CCoinControl coinControl; CWalletTx wtx; SendMoneyToScript(pwallet, newScript, nullptr, - NAME_LOCKED_AMOUNT, false, wtx, coinControl); + KEVA_LOCKED_AMOUNT, false, wtx, coinControl); keyName.KeepKey(); - const std::string randStr = HexStr(rand); const std::string txid = wtx.GetHash().GetHex(); - LogPrintf ("name_new: name=%s, rand=%s, tx=%s\n", - nameStr.c_str(), randStr.c_str(), txid.c_str()); + LogPrintf("keva_namespace: namespace=%s, displayName=%s, tx=%s\n", + namespaceHash.ToString().c_str(), displayNameStr.c_str(), txid.c_str()); UniValue res(UniValue::VARR); res.push_back(txid); - res.push_back(randStr); + res.push_back(namespaceHash.ToString()); return res; } /* ************************************************************************** */ - +#if 0 UniValue keva_namespace(const JSONRPCRequest& request) { CWallet* const pwallet = GetWalletForJSONRPCRequest(request); @@ -123,20 +121,22 @@ UniValue keva_namespace(const JSONRPCRequest& request) ObserveSafeMode (); - const std::string displayName = request.params[0].get_str(); - const valtype displayNameVal = ValtypeFromString(displayName); - if (displayNameVal.size() > MAX_NAMESPACE_LENGTH) + const valtype displayName = ValtypeFromString(request.params[0].get_str()); + if (displayName.size() > MAX_NAMESPACE_LENGTH) { throw JSONRPCError (RPC_INVALID_PARAMETER, "the display name of the namespace is too long"); + } +#if 0 /* Reject updates to a name for which the mempool already has a pending update. This is not a hard rule enforced by network rules, but it is necessary with the current mempool implementation. */ { LOCK (mempool.cs); - if (mempool.updatesName(name)) + if (mempool.registersNamespace(name)) throw JSONRPCError (RPC_TRANSACTION_ERROR, "there is already a pending update for this name"); } +#endif /* No more locking required, similarly to name_new. */ EnsureWalletIsUnlocked (pwallet); @@ -148,36 +148,31 @@ UniValue keva_namespace(const JSONRPCRequest& request) bool usedKey = false; CScript addrName; - if (request.params.size () == 3) - { - keyName.ReturnKey (); - const CTxDestination dest - = DecodeDestination (request.params[2].get_str ()); - if (!IsValidDestination (dest)) - throw JSONRPCError (RPC_INVALID_ADDRESS_OR_KEY, "invalid address"); - - addrName = GetScriptForDestination (dest); - } - else - { - usedKey = true; - addrName = GetScriptForDestination (pubKeyReserve.GetID ()); + if (request.params.size () == 3) { + keyName.ReturnKey(); + const CTxDestination dest = DecodeDestination (request.params[2].get_str ()); + if (!IsValidDestination (dest)) { + throw JSONRPCError (RPC_INVALID_ADDRESS_OR_KEY, "invalid address"); } + addrName = GetScriptForDestination(dest); + } else { + usedKey = true; + addrName = GetScriptForDestination(pubKeyReserve.GetID ()); + } - const CScript nameScript - = CNameScript::buildNameUpdate (addrName, name, value); + const CScript kevaScript = CKevaScript::buildKevaNamespace(addrName, name, displayName); CCoinControl coinControl; CWalletTx wtx; - SendMoneyToScript (pwallet, nameScript, &txIn, - NAME_LOCKED_AMOUNT, false, wtx, coinControl); + SendMoneyToScript(pwallet, kevaScript, nullptr, KEVA_LOCKED_AMOUNT, false, wtx, coinControl); - if (usedKey) + if (usedKey) { keyName.KeepKey (); + } return wtx.GetHash ().GetHex (); } - +#endif UniValue keva_put(const JSONRPCRequest& request) { @@ -207,8 +202,8 @@ UniValue keva_put(const JSONRPCRequest& request) ObserveSafeMode (); const std::string namespaceStr = request.params[0].get_str (); - const valtype namespaceVal = ValtypeFromString (namespaceStr); - if (namespaceVal.size () > MAX_NAMESPACE_LENGTH) + const valtype nameSpace = ValtypeFromString (namespaceStr); + 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 (); @@ -231,17 +226,19 @@ UniValue keva_put(const JSONRPCRequest& request) rules, but it is necessary with the current mempool implementation. */ { LOCK (mempool.cs); - if (mempool.updatesName (name)) + if (mempool.updatesKey(nameSpace, key)) { throw JSONRPCError (RPC_TRANSACTION_ERROR, "there is already a pending update for this name"); + } } - CNameData oldData; + CKevaData oldData; { LOCK (cs_main); - if (!pcoinsTip->GetName (name, oldData) || oldData.isExpired ()) + if (!pcoinsTip->HasNamespace(nameSpace)) { throw JSONRPCError (RPC_TRANSACTION_ERROR, - "this name can not be updated"); + "this name can not be updated"); + } } const COutPoint outp = oldData.getUpdateOutpoint (); @@ -275,12 +272,12 @@ UniValue keva_put(const JSONRPCRequest& request) } const CScript nameScript - = CNameScript::buildNameUpdate (addrName, name, value); + = CKevaScript::buildKevaPut(addrName, nameSpace, key, value); CCoinControl coinControl; CWalletTx wtx; - SendMoneyToScript (pwallet, nameScript, &txIn, - NAME_LOCKED_AMOUNT, false, wtx, coinControl); + SendMoneyToScript(pwallet, nameScript, &txIn, + KEVA_LOCKED_AMOUNT, false, wtx, coinControl); if (usedKey) keyName.KeepKey (); diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index 7814c31c4..1a9bd046d 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -636,7 +636,7 @@ public: int changePos = -1; std::string error; CCoinControl dummy; - BOOST_CHECK(wallet->CreateTransaction({recipient}, wtx, reservekey, fee, changePos, error, dummy)); + BOOST_CHECK(wallet->CreateTransaction({recipient}, nullptr, wtx, reservekey, fee, changePos, error, dummy)); CValidationState state; BOOST_CHECK(wallet->CommitTransaction(wtx, reservekey, nullptr, state)); CMutableTransaction blocktx;