From 2c280242774221c567cf9a155e27dcc042b33211 Mon Sep 17 00:00:00 2001 From: dimxy Date: Mon, 10 Aug 2020 20:03:10 +0500 Subject: [PATCH] Added unspent cc index and cc txns in mempool improvements (#64) * fixed name conflict (global namespace) * CCtokens_impl.h renamed * more global namespace fix * fixed libcc.so dependencies * all templated to impl.h * fix single def for helpers; restore global namespaces; restore fix for tokentransfer inputs check * fix tokenv2balance evalcode * first ver of Unspent CC Index; SetCCUnspentsInMempool added, AddNormalInpustRemote mempool flag; tokencreate use normals in mempool (temp) * TestBlockValidity call surrounded by crit section * fixed maxOutputs * deb logging * added TokenV2List with unspent cc index support * added if-cc-active condition like in other places * refactored critical section for TestBlockValidity * changed cc index key (txid index added) and value; revuint used for creationid; template GetNonFungible calls fixed; * lost in the prev commit * old token logging refactored * del extra logging * deleted more trace logging * fix mempool index keyType for cryptoconditions * added opt ver IsSpentInMempool using spent index; calls profiling * added mempool cs around TestBlockValidity * try LOCK2 for TestBlockValidity * restored cs_main around TestBlockValidity; formatting (justification) in BitcoinMiner * removed time profiling * restored mempool index in AddNormalInputsRemote * tx param changed to const in myAddToMempool * added mempool.check and a loop to update mempool indexes in CheckBlock (instead of CCTxFix...) * removed txhash and index from unspent cc index key, fixed mempool address and spent index for cc txns type, added unspent cc index for mempool tokentransfer usemempool input * restored txhash and index in the cc index key (needed to allow multiple cc tx for one creationid) * added listccunspents rpc (to browse unspent cc index) --- src/Makefile.am | 24 +- src/cc/CCinclude.h | 45 +- src/cc/CCtokens.cpp | 87 +++- src/cc/CCtokens.h | 1 + src/cc/CCtokens_impl.h | 158 +++--- src/cc/CCtokenutils.cpp | 6 +- src/cc/CCtx.cpp | 163 ++++++- src/cc/CCutils.cpp | 67 ++- src/cc/old/CCtokenutils_v0.cpp | 354 ++++++++++++++ src/init.cpp | 11 +- src/main.cpp | 398 +++++++++++++-- src/main.h | 14 + src/miner.cpp | 263 +++++----- src/rpc/blockchain.cpp | 862 +++++++++++---------------------- src/rpc/ccutilsrpc.cpp | 106 ++++ src/rpc/register.h | 15 +- src/rpc/tokensrpc.cpp | 28 +- src/txdb.cpp | 60 +++ src/txdb.h | 5 + src/txmempool.cpp | 255 ++++++++-- src/txmempool.h | 14 + src/unspentccindex.h | 198 ++++++++ 22 files changed, 2180 insertions(+), 954 deletions(-) create mode 100644 src/cc/old/CCtokenutils_v0.cpp create mode 100644 src/rpc/ccutilsrpc.cpp create mode 100644 src/unspentccindex.h diff --git a/src/Makefile.am b/src/Makefile.am index 10bbd5264d7..5b62fe85813 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -298,17 +298,24 @@ libbitcoin_server_a_SOURCES = \ cc/import.cpp \ cc/importgateway.cpp \ cc/CCassetsCore.cpp \ + cc/old/CCassetsCore_v0.cpp \ + cc/CCassetstx.cpp \ + cc/old/CCassetstx_v0.cpp \ cc/CCcustom.cpp \ cc/CCtx.cpp \ cc/CCutils.cpp \ + cc/CCvalidation.cpp \ cc/CCtokens.cpp \ + cc/old/CCtokens_v0.cpp \ cc/assets.cpp \ + cc/old/assets_v0.cpp \ cc/faucet.cpp \ cc/rewards.cpp \ cc/dice.cpp \ cc/lotto.cpp \ cc/fsm.cpp \ cc/heir.cpp \ + cc/old/heir_v0.cpp \ cc/oracles.cpp \ cc/oraclesV2.cpp \ cc/prices.cpp \ @@ -318,6 +325,9 @@ libbitcoin_server_a_SOURCES = \ cc/channelsV2.cpp \ cc/auction.cpp \ cc/betprotocol.cpp \ + cc/pricesfeed.cpp \ + cc/priceslibs/cjsonpointer.cpp \ + cc/kogs.cpp \ chain.cpp \ checkpoints.cpp \ fs.cpp \ @@ -352,6 +362,10 @@ libbitcoin_server_a_SOURCES = \ rpc/net.cpp \ rpc/rawtransaction.cpp \ rpc/server.cpp \ + rpc/tokensrpc.cpp \ + rpc/pricesrpc.cpp \ + rpc/marmararpc.cpp \ + rpc/ccutilsrpc.cpp \ script/serverchecker.cpp \ script/sigcache.cpp \ timedata.cpp \ @@ -399,9 +413,6 @@ libbitcoin_wallet_a_SOURCES = \ transaction_builder.cpp \ wallet/rpcdisclosure.cpp \ wallet/rpcdump.cpp \ - cc/CCtokens.cpp \ - cc/CCassetsCore.cpp \ - cc/CCassetstx.cpp \ cc/CCtx.cpp \ wallet/rpcwallet.cpp \ wallet/wallet.cpp \ @@ -521,7 +532,9 @@ libbitcoin_common_a_SOURCES = \ script/standard.cpp \ transaction_builder.cpp \ cc/CCtokenutils.cpp \ + cc/old/CCtokenutils_v0.cpp \ cc/CCutilbits.cpp \ + gmp_i64.c \ $(BITCOIN_CORE_H) \ $(LIBZCASH_H) @@ -554,10 +567,6 @@ if GLIBC_BACK_COMPAT libbitcoin_util_a_SOURCES += compat/glibc_compat.cpp endif -if ENABLE_TESTS -libbitcoin_server_a_SOURCES += rpc/testtransactions.cpp -endif - # cli: zcash-cli libbitcoin_cli_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) @@ -787,6 +796,7 @@ clean-local: -$(MAKE) -C secp256k1 clean -$(MAKE) -C snark clean -$(MAKE) -C univalue clean + -$(MAKE) -C cryptoconditions clean rm -f leveldb/*/*.gcno leveldb/helpers/memenv/*.gcno -rm -f config.h diff --git a/src/cc/CCinclude.h b/src/cc/CCinclude.h index 07b98566841..360f13ca3a0 100644 --- a/src/cc/CCinclude.h +++ b/src/cc/CCinclude.h @@ -74,6 +74,7 @@ Details. #include "../komodo_nSPV_defs.h" #include "../komodo_cJSON.h" #include "../init.h" +#include "../unspentccindex.h" #include "rpc/server.h" #define CC_BURNPUBKEY "02deaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddead" //!< 'dead' pubkey in hex for burning tokens (if tokens are sent to it, they become 'burned') @@ -360,7 +361,7 @@ int64_t CCgettxout(uint256 txid,int32_t vout,int32_t mempoolflag,int32_t lockfla /// \cond INTERNAL bool myIsutxo_spentinmempool(uint256 &spenttxid,int32_t &spentvini,uint256 txid,int32_t vout); -bool myAddtomempool(CTransaction &tx, CValidationState *pstate = NULL, bool fSkipExpiry = false); +bool myAddtomempool(const CTransaction &tx, CValidationState *pstate = NULL, bool fSkipExpiry = false); bool mytxid_inmempool(uint256 txid); int32_t myIsutxo_spent(uint256 &spenttxid,uint256 txid,int32_t vout); int32_t myGet_mempool_txs(std::vector &txs,uint8_t evalcode,uint8_t funcid); @@ -769,6 +770,20 @@ int64_t CCduration(int32_t &numblocks,uint256 txid); bool ExactAmounts(Eval* eval, const CTransaction &tx, uint64_t txfee); bool CCOpretCheck(Eval* eval, const CTransaction &tx, bool no_burn, bool no_multi, bool last_vout); +/// decodes cc transaction vout basic data: evalcode, funcid, version, creationId +/// first tries to find cc data in vout's opdrop then in the last vout opreturn +/// cc data must follow the common format: evalcode funcid version creationId +/// if tx does not have evalcode cc vins the function treats the tx as the creation tx and sets creationId to tx.GetHash() +/// @param tx cc transaction +/// @param n vout number to check +/// @param[out] evalcode +/// @param[out] funcid +/// @param[out] version +/// @param[out] creationId uint256 value euther from cc data or tx id itself +/// @returns true if decoded okay +bool CCDecodeTxVout(const CTransaction &tx, int32_t n, uint8_t &evalcode, uint8_t &funcid, uint8_t &version, uint256 &creationId); + + /// @private uint256 CCOraclesReverseScan(char const *logcategory,uint256 &txid,int32_t height,uint256 reforacletxid,uint256 batontxid); /// @private @@ -835,7 +850,31 @@ UniValue FinalizeCCV2Tx(bool remote, uint64_t mask, struct CCcontract_info *cp, /// @param[out] unspentOutputs vector of pairs of address key and amount /// @param coinaddr address where unspent outputs are searched /// @param CCflag if true the function searches for cc outputs, otherwise for normal outputs -void SetCCunspents(std::vector > &unspentOutputs,char *coinaddr,bool CCflag = true); +void SetCCunspents(std::vector > &unspentOutputs, char *coinaddr,bool CCflag = true); + +/// SetCCunspentsWithMempool returns a vector of unspent outputs on an address, including outputs in mempool +/// outputs that spent in mempool are removed +/// @param[out] unspentOutputs vector of pairs of address key and amount +/// @param coinaddr address where unspent outputs are searched +/// @param CCflag if true the function searches for cc outputs, otherwise for normal outputs +void SetCCunspentsWithMempool(std::vector > &unspentOutputs, char *coinaddr, bool ccflag = true); + +/// SetCCunspents returns a vector of unspent outputs for a cc address and creationid of cc instance +/// @param[out] unspentOutputs vector of pairs of objects CAddressUnspentCCKey and CAddressUnspentCCValue +/// @param coinaddr cc address where unspent outputs are searched +/// @param creationid cc instance creationid for which outputs are searched +void SetCCunspentsCCIndex(std::vector > &unspentOutputs, const char *coinaddr, uint256 creationId); + +/// SetCCunspents returns a vector of unspent outputs for a cc address +/// @param[out] unspentOutputs vector of pairs of objects CAddressUnspentCCKey and CAddressUnspentCCValue +/// @param coinaddr cc address where unspent outputs are searched +void SetCCunspentsCCIndex(std::vector > &unspentOutputs, const char *coinaddr); + +/// Adds mempool outputs to a vector of unspent outputs for a cc address +/// @param[out] unspentOutputs vector of pairs of objects CAddressUnspentCCKey and CAddressUnspentCCValue +/// @param coinaddr cc address where unspent outputs are searched +/// @param creationId txid of cc instance creation tx, might be empty to return all txns on coinaddr +void AddCCunspentsCCIndexMempool(std::vector > &unspentOutputs, const char *coinaddr, uint256 creationId); /// SetCCtxids returns a vector of all outputs on an address /// @param[out] addressIndex vector of pairs of address index key and amount @@ -894,7 +933,7 @@ int64_t AddNormalinputs2(CMutableTransaction &mtx,int64_t total,int32_t maxinput /// @param total amount of inputs to add. If total equals to 0 the function does not add inputs but returns amount of all available normal inputs in the wallet /// @param maxinputs maximum number of inputs to add /// @returns amount of added normal inputs or amount of all normal inputs in the wallet -int64_t AddNormalinputsRemote(CMutableTransaction &mtx, CPubKey mypk, int64_t total, int32_t maxinputs); +int64_t AddNormalinputsRemote(CMutableTransaction &mtx, CPubKey mypk, int64_t total, int32_t maxinputs, bool mempool = false); /// CCutxovalue returns amount of an utxo. The function does this without loading the utxo transaction, by using address index only /// @param coinaddr address where the utxo is searched diff --git a/src/cc/CCtokens.cpp b/src/cc/CCtokens.cpp index d7153b452e2..fea15e93a53 100644 --- a/src/cc/CCtokens.cpp +++ b/src/cc/CCtokens.cpp @@ -21,6 +21,7 @@ tokens cc tx creation and validation code */ +#include "CCtokens_impl.h" thread_local uint32_t tokenValIndentSize = 0; // for debug logging @@ -170,7 +171,7 @@ CAmount V1::CheckTokensvout(bool goDeeper, bool checkPubkeys /*<--not used, alwa // get optional nft eval code: vscript_t vopretNonfungible; - GetNonfungibleData(reftokenid, vopretNonfungible); + GetNonfungibleData(reftokenid, vopretNonfungible); if (vopretNonfungible.size() > 0) { // shift evalcodes so the first is NFT evalcode evalCode2 = evalCode1; @@ -324,7 +325,7 @@ CAmount V1::CheckTokensvout(bool goDeeper, bool checkPubkeys /*<--not used, alwa LOGSTREAM(cctokens_log, CCLOG_DEBUG2, stream << "IsTokensvout() vopretExtra=" << HexStr(vopretExtra) << std::endl); // get non-fungible data - GetNonfungibleData(reftokenid, vopretNonfungible); + GetNonfungibleData(reftokenid, vopretNonfungible); std::vector voutPubkeys; FilterOutTokensUnspendablePk(voutPubkeysInOpret, voutPubkeys); // cannot send tokens to token unspendable cc addr (only marker is allowed there) @@ -578,8 +579,6 @@ CAmount V2::CheckTokensvout(bool goDeeper, bool checkPubkeys, struct CCcontract_ } -#include "CCtokens_impl.h" - /*static CPubKey GetTokenOriginatorPubKey(CScript scriptPubKey) { uint8_t funcId; @@ -611,6 +610,10 @@ bool TokensValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction & if (strcmp(ASSETCHAINS_SYMBOL, "ROGUE") == 0 && chainActive.Height() <= 12500) return true; + + // my test chains: + if (strcmp(ASSETCHAINS_SYMBOL, "TOK3") == 0 /*&& eval->GetCurrentHeight() < 100*/) + return true; // check boundaries: if (tx.vout.size() < 1) @@ -709,9 +712,9 @@ bool Tokensv2Validate(struct CCcontract_info *cp, Eval* eval, const CTransaction std::string errorStr; - if (CheckTokensV2CreateTx(cp, eval, tx)) + if (CheckTokensV2CreateTx(cp, eval, tx)) //found create tx and it is valid return true; - if (eval->state.IsInvalid()) + if (eval->state.IsInvalid()) // create tx is invalid return false; // check 't' vouts (could have multiple tokenids) @@ -761,7 +764,77 @@ CAmount GetTokenBalance(CPubKey pk, uint256 tokenid, bool usemempool) return GetTokenBalance(pk, tokenid, usemempool); } UniValue TokenInfo(uint256 tokenid) { return TokenInfo(tokenid); } -UniValue TokenList() { return TokenList(); } + +UniValue TokenList() +{ + UniValue result(UniValue::VARR); + std::vector txids; + std::vector > unspentOutputs; + + struct CCcontract_info *cp, C; + cp = CCinit(&C, EVAL_TOKENS); + + auto addTokenId = [&](uint256 txid) { + CTransaction vintx; + uint256 hashBlock; + std::vector origpubkey; + std::string name, description; + + if (myGetTransaction(txid, vintx, hashBlock) != 0) { + std::vector oprets; + if (vintx.vout.size() > 0 && DecodeTokenCreateOpRetV1(vintx.vout.back().scriptPubKey, origpubkey, name, description, oprets) != 0) { + result.push_back(txid.GetHex()); + } + else { + LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << "DecodeTokenCreateOpRetV1 failed for txid=" << txid.GetHex() <normaladdr, false, cp->evalcode, 0, zeroid, 'c'); // find by old normal addr marker + for (std::vector::const_iterator it = txids.begin(); it != txids.end(); it++) { + addTokenId(*it); + } + + SetCCunspents(unspentOutputs, cp->unspendableCCaddr, true); // find by burnable validated cc addr marker + LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << " unspentOutputs.size()=" << unspentOutputs.size() << std::endl); + for (std::vector >::const_iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); it++) { + addTokenId(it->first.txhash); + } + + return(result); +} + +UniValue TokenV2List() +{ + UniValue result(UniValue::VARR); + std::vector > unspentOutputs; + + struct CCcontract_info *cp, C; + cp = CCinit(&C, EVAL_TOKENSV2); + + auto addTokenId = [&](uint256 tokenid, const CScript &opreturn) { + std::vector origpubkey; + std::string name, description; + std::vector oprets; + + if (DecodeTokenCreateOpRetV2(opreturn, origpubkey, name, description, oprets) != 0) { + result.push_back(tokenid.GetHex()); + } + else { + LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << "DecodeTokenCreateOpRetV2 failed for tokenid=" << tokenid.GetHex() << " opreturn.size=" << opreturn.size() << std::endl); + } + }; + + SetCCunspentsCCIndex(unspentOutputs, cp->unspendableCCaddr, zeroid); // find by burnable validated cc addr marker + LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << "unspentOutputs.size()=" << unspentOutputs.size() << std::endl); + for (std::vector >::const_iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); it++) { + addTokenId(it->first.creationid, it->second.opreturn); + } + + return(result); +} + bool TokensExactAmounts(bool goDeeper, struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, std::string &errorStr) { diff --git a/src/cc/CCtokens.h b/src/cc/CCtokens.h index 5f09ae9484b..9b2530ea6c1 100644 --- a/src/cc/CCtokens.h +++ b/src/cc/CCtokens.h @@ -51,6 +51,7 @@ bool IsTokenMarkerVout(CTxOut vout); int64_t GetTokenBalance(CPubKey pk, uint256 tokenid, bool usemempool = false); UniValue TokenInfo(uint256 tokenid); UniValue TokenList(); +UniValue TokenV2List(); ///template UniValue TokenBeginTransferTx(CMutableTransaction &mtx, struct CCcontract_info *cp, const CPubKey &remotepk, CAmount txfee); ///template UniValue TokenAddTransferVout(CMutableTransaction &mtx, struct CCcontract_info *cp, const CPubKey &remotepk, uint256 tokenid, const char *tokenaddr, std::vector destpubkeys, const std::pair &probecond, CAmount amount, bool useMempool); diff --git a/src/cc/CCtokens_impl.h b/src/cc/CCtokens_impl.h index 3f484357495..af4323488c7 100644 --- a/src/cc/CCtokens_impl.h +++ b/src/cc/CCtokens_impl.h @@ -17,7 +17,34 @@ #include "CCtokens.h" #include "importcoin.h" +#include "base58.h" +// get non-fungible data from 'tokenbase' tx (the data might be empty) +template +void GetNonfungibleData(uint256 tokenid, vscript_t &vopretNonfungible) +{ + CTransaction tokenbasetx; + uint256 hashBlock; + + if (!myGetTransaction(tokenid, tokenbasetx, hashBlock)) { + LOGSTREAMFN(cctokens_log, CCLOG_INFO, stream << "could not load token creation tx=" << tokenid.GetHex() << std::endl); + return; + } + + vopretNonfungible.clear(); + // check if it is non-fungible tx and get its second evalcode from non-fungible payload + if (tokenbasetx.vout.size() > 0) { + std::vector origpubkey; + std::string name, description; + std::vector oprets; + uint8_t funcid; + + if (IsTokenCreateFuncid(V::DecodeTokenCreateOpRet(tokenbasetx.vout.back().scriptPubKey, origpubkey, name, description, oprets))) { + if (oprets.size() > 0) + vopretNonfungible = oprets[0]; + } + } +} // overload, adds inputs from token cc addr and returns non-fungible opret payload if present // also sets evalcode in cp, if needed @@ -26,7 +53,8 @@ CAmount AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, c { CAmount /*threshold, price,*/ totalinputs = 0; int32_t n = 0; - std::vector > unspentOutputs; + //std::vector > unspentOutputs; + std::vector > unspentOutputs; if (cp->evalcode != V::EvalCode()) LOGSTREAMFN(cctokens_log, CCLOG_INFO, stream << "warning: EVAL_TOKENS *cp is needed but used evalcode=" << (int)cp->evalcode << std::endl); @@ -35,54 +63,55 @@ CAmount AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, c { // check if this is a NFT vscript_t vopretNonfungible; - GetNonfungibleData(tokenid, vopretNonfungible); //load NFT data + GetNonfungibleData(tokenid, vopretNonfungible); //load NFT data if (vopretNonfungible.size() > 0) cp->evalcodeNFT = vopretNonfungible.begin()[0]; // set evalcode of NFT, for signing } - //if (!useMempool) // reserved for mempool use - SetCCunspents(unspentOutputs, (char*)tokenaddr, true); - //else - // SetCCunspentsWithMempool(unspentOutputs, (char*)tokenaddr, true); // add tokens in mempool too + + SetCCunspentsCCIndex(unspentOutputs, tokenaddr, tokenid); + std::cerr << __func__ << " non mempool unspentOutputs.size=" << unspentOutputs.size() << std::endl; - if (unspentOutputs.empty()) { - LOGSTREAM(cctokens_log, CCLOG_DEBUG1, stream << "AddTokenCCInputs() no utxos for token dual/three eval addr=" << tokenaddr << " evalcode=" << (int)cp->evalcode << " additionalTokensEvalcode2=" << (int)cp->evalcodeNFT << std::endl); + if (useMempool) { + AddCCunspentsCCIndexMempool(unspentOutputs, tokenaddr, tokenid); + std::cerr << __func__ << " with mempool unspentOutputs.size=" << unspentOutputs.size() << std::endl; } - + // threshold = total / (maxinputs != 0 ? maxinputs : CC_MAXVINS); // let's not use threshold - - for (std::vector >::const_iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); it++) + LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << " found unspentOutputs=" << unspentOutputs.size() << std::endl); + //for (std::vector >::const_iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); it++) + for (std::vector >::const_iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); it++) { - CTransaction vintx; + CTransaction tx; uint256 hashBlock; //if (it->second.satoshis < threshold) // this should work also for non-fungible tokens (there should be only 1 satoshi for non-fungible token issue) // continue; + if (it->second.satoshis == 0) continue; // skip null vins if (std::find_if(mtx.vin.begin(), mtx.vin.end(), [&](const CTxIn &vin){ return vin.prevout.hash == it->first.txhash && vin.prevout.n == it->first.index; }) != mtx.vin.end()) continue; // vin already added - if (myGetTransaction(it->first.txhash, vintx, hashBlock) != 0) + if (myGetTransaction(it->first.txhash, tx, hashBlock) != 0) { char destaddr[KOMODO_ADDRESS_BUFSIZE]; - //std::cerr << __func__ << " scriptPubKey.size()=" << vintx.vout[it->first.index].scriptPubKey.size() << " scriptPubKey=" << vintx.vout[it->first.index].scriptPubKey.ToString() << " scriptPubKey[0]" << (int)vintx.vout[it->first.index].scriptPubKey[0] << std::endl; - Getscriptaddress(destaddr, vintx.vout[it->first.index].scriptPubKey); + Getscriptaddress(destaddr, tx.vout[it->first.index].scriptPubKey); if (strcmp(destaddr, tokenaddr) != 0 /*&& strcmp(destaddr, cp->unspendableCCaddr) != 0 && // TODO: check why this. Should not we add token inputs from unspendable cc addr if mypubkey is used? strcmp(destaddr, cp->unspendableaddr2) != 0*/) // or the logic is to allow to spend all available tokens (what about unspendableaddr3)? continue; - LOGSTREAM(cctokens_log, CCLOG_DEBUG1, stream << "AddTokenCCInputs() check vintx vout destaddress=" << destaddr << " amount=" << vintx.vout[it->first.index].nValue << std::endl); + LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << "checked tx vout destaddress=" << destaddr << " amount=" << tx.vout[it->first.index].nValue << std::endl); - if (IsTokensvout(true, true, cp, NULL, vintx, it->first.index, tokenid) > 0 && !myIsutxo_spentinmempool(ignoretxid,ignorevin,it->first.txhash, it->first.index)) + if (IsTokensvout(true, true, cp, NULL, tx, it->first.index, tokenid) > 0 && !myIsutxo_spentinmempool(ignoretxid, ignorevin, it->first.txhash, it->first.index)) { if (total != 0 && maxinputs != 0) // if it is not just to calc amount... mtx.vin.push_back(CTxIn(it->first.txhash, it->first.index, CScript())); totalinputs += it->second.satoshis; - LOGSTREAM(cctokens_log, CCLOG_DEBUG1, stream << "AddTokenCCInputs() adding input nValue=" << it->second.satoshis << std::endl); + LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << "adding input nValue=" << it->second.satoshis << std::endl); n++; if ((total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs)) @@ -103,7 +132,7 @@ CAmount AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, c // check if this is a NFT vscript_t vopretNonfungible; - GetNonfungibleData(tokenid, vopretNonfungible); + GetNonfungibleData(tokenid, vopretNonfungible); if (vopretNonfungible.size() > 0) cp->evalcodeNFT = vopretNonfungible.begin()[0]; // set evalcode of NFT @@ -264,7 +293,6 @@ UniValue TokenTransferExt(const CPubKey &remotepk, CAmount txfee, uint256 tokeni LOGSTREAMFN(cctokens_log, CCLOG_DEBUG1, stream << CCerror << "=" << total << std::endl); return NullUniValue; } - cp = CCinit(&C, V::EvalCode()); if (txfee == 0) @@ -277,7 +305,9 @@ UniValue TokenTransferExt(const CPubKey &remotepk, CAmount txfee, uint256 tokeni return NullUniValue; } - CAmount normalInputs = AddNormalinputs(mtx, mypk, txfee, 0x10000, isRemote); + // CAmount normalInputs = AddNormalinputs(mtx, mypk, txfee, 0x10000, isRemote); // note: wallet scanning for inputs is slower than index scanning + CAmount normalInputs = AddNormalinputsRemote(mtx, mypk, txfee, 0x10000, useMempool); + if (normalInputs > 0) { if ((inputs = AddTokenCCInputs(cp, mtx, tokenaddr, tokenid, total, CC_MAXVINS, useMempool)) >= total) // NOTE: AddTokenCCInputs might set cp->additionalEvalCode which is used in FinalizeCCtx! @@ -350,16 +380,15 @@ std::string TokenTransfer(CAmount txfee, uint256 tokenid, CPubKey destpubkey, CA cp = CCinit(&C, V::EvalCode()); vscript_t vopretNonfungible; - GetNonfungibleData(tokenid, vopretNonfungible); + GetNonfungibleData(tokenid, vopretNonfungible); if (vopretNonfungible.size() > 0) cp->evalcodeNFT = vopretNonfungible.begin()[0]; // set evalcode of NFT GetTokensCCaddress(cp, tokenaddr, mypk, V::IsMixed()); - UniValue sigData = TokenTransferExt(CPubKey(), txfee, tokenid, tokenaddr, {}, {destpubkey}, total, false); + UniValue sigData = TokenTransferExt(CPubKey(), txfee, tokenid, tokenaddr, {}, {destpubkey}, total, true); return ResultGetTx(sigData); } - // returns token creation signed raw tx // params: txfee amount, token amount, token name and description, optional NFT data, template @@ -404,14 +433,14 @@ UniValue CreateTokenExt(const CPubKey &remotepk, CAmount txfee, CAmount tokensup CAmount totalInputs; // always add inputs only from the mypk passed in the param to prove the token creator has the token originator pubkey // This what the AddNormalinputsRemote does (and it is not necessary that this is done only for nspv calls): - if ((totalInputs = AddNormalinputsRemote(mtx, mypk, tokensupply + txfeeCount * txfee, 0x10000)) > 0) + if ((totalInputs = AddNormalinputsRemote(mtx, mypk, tokensupply + txfeeCount * txfee, 0x10000, addTxInMemory)) > 0) { CAmount mypkInputs = TotalPubkeyNormalInputs(mtx, mypk); if (mypkInputs < tokensupply) { // check that the token amount is really issued with mypk (because in the wallet there may be some other privkeys) CCerror = "some inputs signed not with mypubkey (-pubkey=pk)"; return NullUniValue; } - + uint8_t destEvalCode = V::EvalCode(); if( nonfungibleData.size() > 0 ) destEvalCode = nonfungibleData.begin()[0]; @@ -429,7 +458,6 @@ UniValue CreateTokenExt(const CPubKey &remotepk, CAmount txfee, CAmount tokensup } sigData = V::FinalizeCCTx(isRemote, FINALIZECCTX_NO_CHANGE_WHEN_ZERO, cp, mtx, mypk, txfee, V::EncodeTokenCreateOpRet(vscript_t(mypk.begin(), mypk.end()), name, description, { nonfungibleData })); - if (!ResultHasTx(sigData)) { CCerror = "couldnt finalize token tx"; return NullUniValue; @@ -450,7 +478,7 @@ template std::string CreateTokenLocal(CAmount txfee, CAmount tokensupply, std::string name, std::string description, vscript_t nonfungibleData) { CPubKey nullpk = CPubKey(); - UniValue sigData = CreateTokenExt(nullpk, txfee, tokensupply, name, description, nonfungibleData, 0, false); + UniValue sigData = CreateTokenExt(nullpk, txfee, tokensupply, name, description, nonfungibleData, 0, true); //TODO: temp true, set back to false return sigData[JSON_HEXTX].getValStr(); } @@ -578,46 +606,6 @@ UniValue TokenInfo(uint256 tokenid) return result; } -template -static UniValue TokenList() -{ - UniValue result(UniValue::VARR); - std::vector txids; - std::vector > unspentCCMarker; - - struct CCcontract_info *cp, C; uint256 txid, hashBlock; - CTransaction vintx; std::vector origpubkey; - std::string name, description; - - cp = CCinit(&C, V::EvalCode()); - - auto addTokenId = [&](uint256 txid) { - if (myGetTransaction(txid, vintx, hashBlock) != 0) { - std::vector oprets; - if (vintx.vout.size() > 0 && V::DecodeTokenCreateOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, origpubkey, name, description, oprets) != 0) { - result.push_back(txid.GetHex()); - } - else { - std::cerr << __func__ << " V::DecodeTokenCreateOpRet failed" <normaladdr, false, cp->evalcode, 0, zeroid, 'c'); // find by old normal addr marker - for (std::vector::const_iterator it = txids.begin(); it != txids.end(); it++) { - addTokenId(*it); - } - - SetCCunspents(unspentCCMarker, cp->unspendableCCaddr, true); // find by burnable validated cc addr marker - std::cerr << __func__ << " unspenCCMarker.size()=" << unspentCCMarker.size() << std::endl; - for (std::vector >::const_iterator it = unspentCCMarker.begin(); it != unspentCCMarker.end(); it++) { - addTokenId(it->first.txhash); - } - - return(result); -} - - // extract cc token vins' pubkeys: template @@ -664,10 +652,6 @@ bool ExtractTokensCCVinPubkeys(const CTransaction &tx, std::vector &vin return found; } - - - - // this is just for log messages indentation fur debugging recursive calls: extern thread_local uint32_t tokenValIndentSize; @@ -729,36 +713,6 @@ static uint8_t ValidateTokenOpret(uint256 txid, const CScript &scriptPubKey, uin return (uint8_t)0; } - - -// get non-fungible data from 'tokenbase' tx (the data might be empty) -template -void GetNonfungibleData(uint256 tokenid, vscript_t &vopretNonfungible) -{ - CTransaction tokenbasetx; - uint256 hashBlock; - - if (!myGetTransaction(tokenid, tokenbasetx, hashBlock)) { - LOGSTREAM(cctokens_log, CCLOG_INFO, stream << "GetNonfungibleData() could not load token creation tx=" << tokenid.GetHex() << std::endl); - return; - } - - vopretNonfungible.clear(); - // check if it is non-fungible tx and get its second evalcode from non-fungible payload - if (tokenbasetx.vout.size() > 0) { - std::vector origpubkey; - std::string name, description; - std::vector oprets; - uint8_t funcid; - - if (IsTokenCreateFuncid(V::DecodeTokenCreateOpRet(tokenbasetx.vout.back().scriptPubKey, origpubkey, name, description, oprets))) { - if (oprets.size() > 0) - vopretNonfungible = oprets[0]; - } - } -} - - // checks if any token vouts are sent to 'dead' pubkey template static CAmount HasBurnedTokensvouts(const CTransaction& tx, uint256 reftokenid) diff --git a/src/cc/CCtokenutils.cpp b/src/cc/CCtokenutils.cpp index a23eeeb094e..133887e1299 100644 --- a/src/cc/CCtokenutils.cpp +++ b/src/cc/CCtokenutils.cpp @@ -257,6 +257,7 @@ uint8_t DecodeTokenCreateOpRetV1(const CScript &scriptPubKey, std::vector &origpubkey, std::string &name, std::string &description, std::vector &oprets) { vscript_t vopret, vblob; @@ -359,7 +360,7 @@ uint8_t DecodeTokenOpRetV1(const CScript scriptPubKey, uint256 &tokenid, std::ve return (uint8_t)0; } -// decode token 2 opret: +// decode token v2 (mixed mode cc) opret: // for 't' returns all data from opret, vopretExtra contains other contract's data (currently only assets'). // for 'c' returns only funcid. NOTE: nonfungible data is not returned uint8_t DecodeTokenOpRetV2(const CScript scriptPubKey, uint256 &tokenid, std::vector &oprets) @@ -539,8 +540,6 @@ CC *MakeTokensv2CCcond1(uint8_t evalcode, uint8_t evalcode2, CPubKey pk) std::vector pks; pks.push_back(CCNewSecp256k1(pk)); - //std::cerr << __func__ << " entered for evalcode=" << std::hex << (int)evalcode << " evalcode2=" << std::hex << (int)evalcode2 << std::endl; - std::vector thresholds; thresholds.push_back(CCNewEval(E_MARSHAL(ss << evalcode))); if (evalcode != EVAL_TOKENSV2) // if evalCode == EVAL_TOKENS, it is actually MakeCCcond1()! @@ -559,7 +558,6 @@ CC *MakeTokensv2CCcond1(uint8_t evalcode, CPubKey pk) { // make three-eval (token+evalcode+evalcode2) cc vout: CTxOut MakeTokensCC1voutMixed(uint8_t evalcode, uint8_t evalcode2, CAmount nValue, CPubKey pk, vscript_t* pvData) { - //std::cerr << __func__ << " entered for evalcode=" << std::hex << (int)evalcode << std::endl; CTxOut vout; CCwrapper payoutCond( MakeTokensv2CCcond1(evalcode, evalcode2, pk) ); if (!CCtoAnon(payoutCond.get())) diff --git a/src/cc/CCtx.cpp b/src/cc/CCtx.cpp index 1809449fcf5..0d580606bd6 100644 --- a/src/cc/CCtx.cpp +++ b/src/cc/CCtx.cpp @@ -598,7 +598,94 @@ void NSPV_CCunspents(std::vector > &txids,char *coinaddr,bool ccflag); void NSPV_CCtxids(std::vector &txids,char *coinaddr,bool ccflag, uint8_t evalcode,uint256 filtertxid, uint8_t func); -void SetCCunspents(std::vector > &unspentOutputs,char *coinaddr,bool ccflag) +// set cc or normal unspents from mempool +static void AddCCunspentsInMempool(std::vector > &unspentOutputs, char *destaddr, bool isCC) +{ + uint160 hashBytes; + std::string addrstr(destaddr); + CBitcoinAddress address(addrstr); + int type; + + if (address.GetIndexKey(hashBytes, type, isCC) == false) + return; + + // lock mempool + ENTER_CRITICAL_SECTION(mempool.cs); + + std::vector > memOutputs; + std::vector< std::pair > addresses; + addresses.push_back(std::make_pair(hashBytes, type)); + mempool.getAddressIndex(addresses, memOutputs); + + //std::cerr << __func__ << " total memOutputs.size=" << memOutputs.size() << " hashBytes=" << hashBytes.GetHex() << " addrstr=" << addrstr << std::endl; + + // non indexed impl: + /* + for (CTxMemPool::indexed_transaction_set::iterator mi = mempool.mapTx.begin(); mi != mempool.mapTx.end(); ++mi) { + const CTransaction& memtx = mi->GetTx(); + for (int32_t i = 0; i < memtx.vout.size(); i++) + { + uint256 dummytxid; + int32_t dummyvout; + if (!myIsutxo_spentinmempool(dummytxid, dummyvout, memtx.GetHash(), i)) + { + if (isCC && memtx.vout[i].scriptPubKey.IsPayToCryptoCondition() || !isCC && !memtx.vout[i].scriptPubKey.IsPayToCryptoCondition()) + { + char voutaddr[64]; + Getscriptaddress(voutaddr, memtx.vout[i].scriptPubKey); + if (strcmp(voutaddr, destaddr) == 0) + { + // create unspent output key value pair + CAddressUnspentKey key; + CAddressUnspentValue value; + + key.type = type; + key.hashBytes = hashBytes; + key.txhash = memtx.GetHash(); + key.index = i; + + value.satoshis = memtx.vout[i].nValue; + value.blockHeight = 0; + value.script = memtx.vout[i].scriptPubKey; + unspentOutputs.push_back(std::make_pair(key, value)); + } + } + } + } + } + */ + + // impl using mempool address and spent indexes + for (std::vector >::iterator mo = memOutputs.begin(); mo != memOutputs.end(); mo ++) + { + uint256 dummytxid; + int32_t dummyvout; + + if (mo->first.spending == 0 // the entry is an output + && !myIsutxo_spentinmempool(dummytxid, dummyvout, mo->first.txhash, mo->first.index) && mo->first.type == type) + { + // create unspent output key value pair + CAddressUnspentKey key; + CAddressUnspentValue value; + + key.type = type; + key.hashBytes = hashBytes; + key.txhash = mo->first.txhash; + key.index = mo->first.index; + + value.satoshis = mo->second.amount; + value.blockHeight = 0; + // note: value.script is not set + + //std::cerr << __func__ << " adding txhash=" << mo->first.txhash.GetHex() << " index=" << mo->first.index << " amount=" << mo->second.amount << " spending=" << mo->first.spending << " mo->second.prevhash=" << mo->second.prevhash.GetHex() << " mo->second.prevout=" << mo->second.prevout << std::endl; + unspentOutputs.push_back(std::make_pair(key, value)); + } + } + LEAVE_CRITICAL_SECTION(mempool.cs); +} + + +void SetCCunspents(std::vector > &unspentOutputs, char *coinaddr,bool ccflag) { int32_t type=0,i,n; char *ptr; std::string addrstr; uint160 hashBytes; std::vector > addresses; if ( KOMODO_NSPV_SUPERLITE ) @@ -622,6 +709,61 @@ void SetCCunspents(std::vector > &unspentOutputs, char *coinaddr, bool ccflag) +{ + SetCCunspents(unspentOutputs, coinaddr, ccflag); + + // remove utxos spent in mempool + /* decided not to do this as the caller still needs to check is not spent in mempool + for (std::vector >::iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); ) + { + uint256 dummytxid; + int32_t dummyvout; + if (myIsutxo_spentinmempool(dummytxid, dummyvout, it->first.txhash, it->first.index)) { + //std::cerr << __func__ << " erasing spent in mempool txid=" << it->first.txhash.GetHex() << " index=" << it->first.index << " spenttxid=" << dummytxid.GetHex() << std::endl; + it = unspentOutputs.erase(it); + } + else + it++; + } */ + //std::cerr << __func__ << " unspentOutputs.size=" << unspentOutputs.size() << std::endl; + + AddCCunspentsInMempool(unspentOutputs, coinaddr, ccflag); + //std::cerr << __func__ << " unspentOutputs.size with mempool=" << unspentOutputs.size() << std::endl; + +} + + +// find cc unspent outputs with use unspents cc index +void SetCCunspentsCCIndex(std::vector > &unspentOutputs, const char *coinaddr, uint256 creationId) +{ + int32_t type=0; + uint160 hashBytes; + std::vector > searchKeys; + CBitcoinAddress address(coinaddr); + + if (address.GetIndexKey(hashBytes, type, true) == 0) + return; + searchKeys.push_back(std::make_pair(hashBytes, creationId)); + for (std::vector >::iterator it = searchKeys.begin(); it != searchKeys.end(); it++) + { + if (GetUnspentCCIndex((*it).first, (*it).second, unspentOutputs, -1, -1, 0) == 0) + return; + } +} + +void AddCCunspentsCCIndexMempool(std::vector > &unspentOutputs, const char *coinaddr, uint256 creationId) +{ + CBitcoinAddress address( coinaddr ); + uint160 hashBytes; + int type; + if (address.GetIndexKey(hashBytes, type, true)) { + + mempool.getUnspentCCIndex({ std::make_pair(hashBytes, creationId) }, unspentOutputs); + } +} + void SetCCtxids(std::vector > &addressIndex,char *coinaddr,bool ccflag) { int32_t type=0,i,n; char *ptr; std::string addrstr; uint160 hashBytes; std::vector > addresses; @@ -948,10 +1090,11 @@ int64_t AddNormalinputs2(CMutableTransaction &mtx, int64_t total, int32_t maxinp } // has additional mypk param for nspv calls -int64_t AddNormalinputsRemote(CMutableTransaction &mtx, CPubKey mypk, int64_t total, int32_t maxinputs) +int64_t AddNormalinputsRemote(CMutableTransaction &mtx, CPubKey mypk, int64_t total, int32_t maxinputs, bool useMempool) { int32_t abovei,belowi,ind,vout,i,n = 0; int64_t sum,threshold,above,below; int64_t remains,nValue,totalinputs = 0; char coinaddr[64]; uint256 txid,hashBlock; CTransaction tx; struct CC_utxo *utxos,*up; std::vector > unspentOutputs; + if ( KOMODO_NSPV_SUPERLITE ) return(NSPV_AddNormalinputs(mtx,mypk,total,maxinputs,&NSPV_U)); utxos = (struct CC_utxo *)calloc(CC_MAXVINS,sizeof(*utxos)); @@ -962,15 +1105,20 @@ int64_t AddNormalinputsRemote(CMutableTransaction &mtx, CPubKey mypk, int64_t to else threshold = total; sum = 0; Getscriptaddress(coinaddr,CScript() << vscript_t(mypk.begin(), mypk.end()) << OP_CHECKSIG); - SetCCunspents(unspentOutputs,coinaddr,false); + if (!useMempool) + SetCCunspents(unspentOutputs,coinaddr,false); + else + SetCCunspentsWithMempool(unspentOutputs,coinaddr,false); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; vout = (int32_t)it->first.index; //if ( it->second.satoshis < threshold ) - // continue; - if( it->second.satoshis == 0 ) - continue; + // continue; // do not use threshold + if( it->second.satoshis == 0 ) + continue; //skip null outputs + if ( myGetTransaction(txid,tx,hashBlock) != 0 && tx.vout.size() > 0 && vout < tx.vout.size() && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() == 0 ) { //fprintf(stderr,"check %.8f to vins array.%d of %d %s/v%d\n",(double)tx.vout[vout].nValue/COIN,n,maxinputs,txid.GetHex().c_str(),(int32_t)vout); @@ -990,7 +1138,7 @@ int64_t AddNormalinputsRemote(CMutableTransaction &mtx, CPubKey mypk, int64_t to if ( i != n ) continue; } - if ( myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) + if (myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0) { up = &utxos[n++]; up->txid = txid; @@ -1003,6 +1151,7 @@ int64_t AddNormalinputsRemote(CMutableTransaction &mtx, CPubKey mypk, int64_t to } } } + remains = total; for (i=0; i0; i++) { diff --git a/src/cc/CCutils.cpp b/src/cc/CCutils.cpp index 1968305058b..4f26c3dd456 100644 --- a/src/cc/CCutils.cpp +++ b/src/cc/CCutils.cpp @@ -439,10 +439,10 @@ bool GetCCaddress1of2(struct CCcontract_info *cp,char *destaddr,CPubKey pk,CPubK bool GetTokensCCaddress1of2(struct CCcontract_info *cp, char *destaddr, CPubKey pk1, CPubKey pk2, bool mixed) { CCwrapper payoutCond; - if (mixed) - payoutCond.reset(MakeTokensv2CCcond1of2(cp->evalcode, cp->evalcodeNFT, pk1, pk2)); - else + if (!mixed) payoutCond.reset(MakeTokensCCcond1of2(cp->evalcode, cp->evalcodeNFT, pk1, pk2)); + else + payoutCond.reset(MakeTokensv2CCcond1of2(cp->evalcode, cp->evalcodeNFT, pk1, pk2)); destaddr[0] = 0; if (payoutCond != nullptr) // if additionalTokensEvalcode2 not set then it is dual-eval cc else three-eval cc @@ -1486,7 +1486,9 @@ bool GetCCDropAsOpret(const CScript &scriptPubKey, CScript &opret) return false; } -// get OP_DROP data for mixed cc vouts: +// get OP_DROP data for mixed cc vouts +// the function returns OP_DROP data as OP_RETURN script. This looks strange but all cc modules have DecodeOpReturn-like functions +// that accept OP_RETURN scripts bool GetCCVDataAsOpret(const CScript &scriptPubKey, CScript &opret) { std::vector vParams; @@ -1498,7 +1500,6 @@ bool GetCCVDataAsOpret(const CScript &scriptPubKey, CScript &opret) { //vscript_t vopret(vParams[0].begin() + 6, vParams[0].end()); opret << OP_RETURN << vParams[0]; // return vData[1] as cc opret - std::cerr << __func__ << " ccopret=" << opret.ToString() << std::endl; return true; } } @@ -1576,4 +1577,60 @@ UniValue CCaddress(struct CCcontract_info *cp, const char *name, const std::vect result.push_back(Pair("mypk Normal Balance",ValueFromAmount(CCaddress_balance(destaddr,0)))); } return(result); +} + +// decode cc transaction: +// try to find cc data in vout's opdrop or in the last vout opreturn +// return funcid, version and creationid +bool CCDecodeTxVout(const CTransaction &tx, int32_t n, uint8_t &evalcode, uint8_t &funcid, uint8_t &version, uint256 &creationId) +{ + CScript opdrop; + vscript_t ccdata; + + if (tx.vout.size() > 0) + { + // note: assumes that this is a cc vout (does not check this) + + // first try if OP_DROP data exists + bool usedOpreturn; + if (GetCCVDataAsOpret(tx.vout[n].scriptPubKey, opdrop)) + GetOpReturnData(opdrop, ccdata), usedOpreturn = false; + else + GetOpReturnData(tx.vout.back().scriptPubKey, ccdata), usedOpreturn = true; // use OP_RETURN in the last vout if no OP_DROP data + + // use following algotithm to determine creationId + // get the evalcode from ccdata + // if no cc vins found with this evalcode this is the creation tx and creationId = tx.GetHash() + // else the creationId is after the version field: 'evalcode funcid version creationId' + if (ccdata.size() >= 3) { + struct CCcontract_info *cp, C; + cp = CCinit(&C, ccdata[0]); + int32_t i = 0; + for (; i < tx.vin.size(); i ++) + if (cp->ismyvin(tx.vin[i].scriptSig)) + break; + if (i == tx.vin.size()) + { + creationId = tx.GetHash(); // tx is the creation tx + evalcode = ccdata[0]; + funcid = ccdata[1]; + version = ccdata[2]; + } + else + { + uint256 encodedCrid; + if (ccdata.size() >= 3 + sizeof(uint256)) { // get creationId from the ccdata + bool isEof = true; + if (!E_UNMARSHAL(ccdata, ss >> evalcode; ss >> funcid; ss >> version; ss >> encodedCrid; isEof = ss.eof()) && isEof) { + LOGSTREAMFN("ccutils", CCLOG_DEBUG1, stream << "failed to decode ccdata, isEof=" << isEof << " usedOpreturn=" << usedOpreturn << " tx=" << HexStr(E_MARSHAL(ss << tx)) << std::endl); + return false; + } + } + creationId = revuint256(encodedCrid); + //std::cerr << __func__ << " in opret found creationid=" << creationId.GetHex() << std::endl; + } + } + return true; + } + return false; } \ No newline at end of file diff --git a/src/cc/old/CCtokenutils_v0.cpp b/src/cc/old/CCtokenutils_v0.cpp new file mode 100644 index 00000000000..af74e742dc1 --- /dev/null +++ b/src/cc/old/CCtokenutils_v0.cpp @@ -0,0 +1,354 @@ +/****************************************************************************** +* Copyright � 2014-2019 The SuperNET Developers. * +* * +* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * +* the top-level directory of this distribution for the individual copyright * +* holder information and the developer policies on copyright and licensing. * +* * +* Unless otherwise agreed in a custom licensing agreement, no part of the * +* SuperNET software, including this file may be copied, modified, propagated * +* or distributed except according to the terms contained in the LICENSE file * +* * +* Removal or modification of this copyright notice is prohibited. * +* * +******************************************************************************/ + +// encode decode tokens opret +// make token cryptoconditions and vouts +// This code was moved to a separate source file to enable linking libcommon.so (with importcoin.cpp which depends on some token functions) + +#include "CCtokens_v0.h" + +namespace tokensv0 { + +#ifndef IS_CHARINSTR +#define IS_CHARINSTR(c, str) (std::string(str).find((char)(c)) != std::string::npos) +#endif + +// NOTE: this inital tx won't be used by other contract +// for tokens to be used there should be at least one 't' tx with other contract's custom opret +CScript EncodeTokenCreateOpRet(uint8_t funcid, std::vector origpubkey, std::string name, std::string description, vscript_t vopretNonfungible) +{ + /* CScript opret; + uint8_t evalcode = EVAL_TOKENS; + funcid = 'c'; // override the param + + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << origpubkey << name << description; \ + if (!vopretNonfungible.empty()) { + ss << (uint8_t)OPRETID_NONFUNGIBLEDATA; + ss << vopretNonfungible; + }); */ + + std::vector> oprets; + + if(!vopretNonfungible.empty()) + oprets.push_back(std::make_pair(OPRETID_NONFUNGIBLEDATA, vopretNonfungible)); + return EncodeTokenCreateOpRet(funcid, origpubkey, name, description, oprets); +} + +CScript EncodeTokenCreateOpRet(uint8_t funcid, std::vector origpubkey, std::string name, std::string description, std::vector> oprets) +{ + CScript opret; + uint8_t evalcode = EVAL_TOKENS; + funcid = 'c'; // override the param + + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << origpubkey << name << description; + for (auto o : oprets) { + if (o.first != 0) { + ss << (uint8_t)o.first; + ss << o.second; + } + }); + return(opret); +} + +/* +// opret 'i' for imported tokens +CScript EncodeTokenImportOpRet(std::vector origpubkey, std::string name, std::string description, uint256 srctokenid, std::vector> oprets) +{ + CScript opret; + uint8_t evalcode = EVAL_TOKENS; + uint8_t funcid = 'i'; + + srctokenid = revuint256(srctokenid); // do not forget this + + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << origpubkey << name << description << srctokenid; + for (auto o : oprets) { + if (o.first != 0) { + ss << (uint8_t)o.first; + ss << o.second; + } + }); + return(opret); +} +*/ + + +CScript EncodeTokenOpRet(uint256 tokenid, std::vector voutPubkeys, std::pair opretWithId) +{ + std::vector> oprets; + oprets.push_back(opretWithId); + return EncodeTokenOpRet(tokenid, voutPubkeys, oprets); +} + +CScript EncodeTokenOpRet(uint256 tokenid, std::vector voutPubkeys, std::vector> oprets) +{ + CScript opret; + uint8_t tokenFuncId = 't'; + uint8_t evalCodeInOpret = EVAL_TOKENS; + + tokenid = revuint256(tokenid); + + uint8_t ccType = 0; + if (voutPubkeys.size() >= 0 && voutPubkeys.size() <= 2) + ccType = voutPubkeys.size(); + else { + LOGSTREAMFN("cctokens", CCLOG_DEBUG2, stream << "voutPubkeys.size()=" << voutPubkeys.size() << " not supported" << std::endl); + } + + //vopret_t vpayload; + //GetOpReturnData(payload, vpayload); + + opret << OP_RETURN << E_MARSHAL(ss << evalCodeInOpret << tokenFuncId << tokenid << ccType; + if (ccType >= 1) ss << voutPubkeys[0]; + if (ccType == 2) ss << voutPubkeys[1]; + for (auto o : oprets) { + if (o.first != 0) { + ss << (uint8_t)o.first; + ss << o.second; + } + }); + + // bad opret cases (tries to attach payload without re-serialization): + + // error "64: scriptpubkey": + // if (payload.size() > 0) + // opret += payload; + + // error "64: scriptpubkey": + // CScript opretPayloadNoOpcode(vpayload); + // return opret + opretPayloadNoOpcode; + + // error "sig_aborted": + // opret.resize(opret.size() + vpayload.size()); + // CScript::iterator it = opret.begin() + opret.size(); + // for (int i = 0; i < vpayload.size(); i++, it++) + // *it = vpayload[i]; + + return opret; +} + +// overload for compatibility +//CScript EncodeTokenOpRet(uint8_t tokenFuncId, uint8_t evalCodeInOpret, uint256 tokenid, std::vector voutPubkeys, CScript payload) +//{ +// return EncodeTokenOpRet(tokenid, voutPubkeys, payload); +//} + +// overload for fungible tokens (no additional data in opret): +uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey, std::vector &origpubkey, std::string &name, std::string &description) { + //vopret_t vopretNonfungibleDummy; + std::vector> opretsDummy; + return DecodeTokenCreateOpRet(scriptPubKey, origpubkey, name, description, opretsDummy); +} + +uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey, std::vector &origpubkey, std::string &name, std::string &description, std::vector> &oprets) +{ + vscript_t vopret, vblob; + uint8_t dummyEvalcode, funcid, opretId = 0; + + GetOpReturnData(scriptPubKey, vopret); + oprets.clear(); + + if (vopret.size() > 2 && vopret.begin()[0] == EVAL_TOKENS && vopret.begin()[1] == 'c') + { + if (E_UNMARSHAL(vopret, ss >> dummyEvalcode; ss >> funcid; ss >> origpubkey; ss >> name; ss >> description; + while (!ss.eof()) { + ss >> opretId; + if (!ss.eof()) { + ss >> vblob; + oprets.push_back(std::make_pair(opretId, vblob)); + } + })) + { + return(funcid); + } + } + LOGSTREAMFN("cctokens", CCLOG_DEBUG1, stream << "incorrect token create opret" << std::endl); + return (uint8_t)0; +} + +// decode token opret: +// for 't' returns all data from opret, vopretExtra contains other contract's data (currently only assets'). +// for 'c' returns only funcid. NOTE: nonfungible data is not returned +uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCodeTokens, uint256 &tokenid, std::vector &voutPubkeys, std::vector> &oprets) +{ + vscript_t vopret, vblob, dummyPubkey, vnonfungibleDummy; + uint8_t funcId = 0, *script, dummyEvalCode, dummyFuncId, ccType, opretId = 0; + std::string dummyName; std::string dummyDescription; + uint256 dummySrcTokenId; + CPubKey voutPubkey1, voutPubkey2; + + vscript_t voldstyledata; + bool foundOldstyle = false; + + GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + tokenid = zeroid; + oprets.clear(); + + if (script != NULL && vopret.size() > 2) + { + evalCodeTokens = script[0]; + if (evalCodeTokens != EVAL_TOKENS) { + LOGSTREAMFN("cctokens", CCLOG_DEBUG1, stream << "incorrect evalcode in tokens opret" << std::endl); + return (uint8_t)0; + } + + funcId = script[1]; + LOGSTREAMFN("cctokens", CCLOG_DEBUG2, stream << "decoded funcId=" << (char)(funcId ? funcId : ' ') << std::endl); + + switch (funcId) + { + case 'c': + return DecodeTokenCreateOpRet(scriptPubKey, dummyPubkey, dummyName, dummyDescription, oprets); + + case 't': + + // compatibility with old-style rogue or assets data (with no opretid): + // try to unmarshal old-style rogue or assets data: + foundOldstyle = E_UNMARSHAL(vopret, ss >> dummyEvalCode; ss >> dummyFuncId; ss >> tokenid; ss >> ccType; + if (ccType >= 1) ss >> voutPubkey1; + if (ccType == 2) ss >> voutPubkey2; + if (!ss.eof()) { + ss >> voldstyledata; + }) && voldstyledata.size() >= 2 && + (voldstyledata.begin()[0] == 0x11 /*EVAL_ROGUE*/ && IS_CHARINSTR(voldstyledata.begin()[1], "RHQKG") || + voldstyledata.begin()[0] == EVAL_ASSETS && IS_CHARINSTR(voldstyledata.begin()[1], "sbSBxo")) ; + + if (foundOldstyle || // fix for compatibility with old style data (no opretid) + E_UNMARSHAL(vopret, ss >> dummyEvalCode; ss >> dummyFuncId; ss >> tokenid; ss >> ccType; + if (ccType >= 1) ss >> voutPubkey1; + if (ccType == 2) ss >> voutPubkey2; + while (!ss.eof()) { + ss >> opretId; + if (!ss.eof()) { + ss >> vblob; + oprets.push_back(std::make_pair(opretId, vblob)); + } + })) + { + if (!(ccType >= 0 && ccType <= 2)) { //incorrect ccType + LOGSTREAMFN("cctokens", CCLOG_DEBUG1, stream << "incorrect ccType=" << (int)ccType << " tokenid=" << revuint256(tokenid).GetHex() << std::endl); + return (uint8_t)0; + } + + // add verification pubkeys: + voutPubkeys.clear(); + if (voutPubkey1.IsValid()) + voutPubkeys.push_back(voutPubkey1); + if (voutPubkey2.IsValid()) + voutPubkeys.push_back(voutPubkey2); + + tokenid = revuint256(tokenid); + + if (foundOldstyle) { //patch for old-style opret data with no opretid + LOGSTREAMFN("cctokens", CCLOG_DEBUG1, stream << "found old-style rogue/asset data, evalcode=" << (int)voldstyledata.begin()[0] << " funcid=" << (char)voldstyledata.begin()[1] << " for tokenid=" << revuint256(tokenid).GetHex() << std::endl); + uint8_t opretIdRestored; + if (voldstyledata.begin()[0] == 0x11 /*EVAL_ROGUE*/) + opretIdRestored = OPRETID_ROGUEGAMEDATA; + else // EVAL_ASSETS + opretIdRestored = OPRETID_ASSETSDATA; + + oprets.push_back(std::make_pair(opretIdRestored, voldstyledata)); + } + + return(funcId); + } + LOGSTREAMFN("cctokens", CCLOG_DEBUG1, stream << "bad opret format," << " ccType=" << (int)ccType << " tokenid=" << revuint256(tokenid).GetHex() << std::endl); + return (uint8_t)0; + + default: + LOGSTREAMFN("cctokens", CCLOG_DEBUG1, stream << "illegal funcid=" << (int)funcId << std::endl); + return (uint8_t)0; + } + } + else { + LOGSTREAMFN("cctokens", CCLOG_DEBUG1, stream << "empty opret, could not parse" << std::endl); + } + return (uint8_t)0; +} + + +// make three-eval (token+evalcode+evalcode2) 1of2 cryptocondition: +CC *MakeTokensCCcond1of2(uint8_t evalcode, uint8_t evalcode2, CPubKey pk1, CPubKey pk2) +{ + // make 1of2 sigs cond + std::vector pks; + pks.push_back(CCNewSecp256k1(pk1)); + pks.push_back(CCNewSecp256k1(pk2)); + + std::vector thresholds; + thresholds.push_back(CCNewEval(E_MARSHAL(ss << evalcode))); + if (evalcode != EVAL_TOKENS) // if evalCode == EVAL_TOKENS, it is actually MakeCCcond1of2()! + thresholds.push_back(CCNewEval(E_MARSHAL(ss << (uint8_t)EVAL_TOKENS))); // this is eval token cc + if (evalcode2 != 0) + thresholds.push_back(CCNewEval(E_MARSHAL(ss << evalcode2))); // add optional additional evalcode + thresholds.push_back(CCNewThreshold(1, pks)); // this is 1 of 2 sigs cc + + return CCNewThreshold(thresholds.size(), thresholds); +} +// overload to make two-eval (token+evalcode) 1of2 cryptocondition: +CC *MakeTokensCCcond1of2(uint8_t evalcode, CPubKey pk1, CPubKey pk2) { + return MakeTokensCCcond1of2(evalcode, 0, pk1, pk2); +} + +// make three-eval (token+evalcode+evalcode2) cryptocondition: +CC *MakeTokensCCcond1(uint8_t evalcode, uint8_t evalcode2, CPubKey pk) +{ + std::vector pks; + pks.push_back(CCNewSecp256k1(pk)); + + std::vector thresholds; + thresholds.push_back(CCNewEval(E_MARSHAL(ss << evalcode))); + if (evalcode != EVAL_TOKENS) // if evalCode == EVAL_TOKENS, it is actually MakeCCcond1()! + thresholds.push_back(CCNewEval(E_MARSHAL(ss << (uint8_t)EVAL_TOKENS))); // this is eval token cc + if (evalcode2 != 0) + thresholds.push_back(CCNewEval(E_MARSHAL(ss << evalcode2))); // add optional additional evalcode + thresholds.push_back(CCNewThreshold(1, pks)); // signature + + return CCNewThreshold(thresholds.size(), thresholds); +} +// overload to make two-eval (token+evalcode) cryptocondition: +CC *MakeTokensCCcond1(uint8_t evalcode, CPubKey pk) { + return MakeTokensCCcond1(evalcode, 0, pk); +} + +// make three-eval (token+evalcode+evalcode2) 1of2 cc vout: +CTxOut MakeTokensCC1of2vout(uint8_t evalcode, uint8_t evalcode2, CAmount nValue, CPubKey pk1, CPubKey pk2) +{ + CTxOut vout; + CC *payoutCond = MakeTokensCCcond1of2(evalcode, evalcode2, pk1, pk2); + vout = CTxOut(nValue, CCPubKey(payoutCond)); + cc_free(payoutCond); + return(vout); +} +// overload to make two-eval (token+evalcode) 1of2 cc vout: +CTxOut MakeTokensCC1of2vout(uint8_t evalcode, CAmount nValue, CPubKey pk1, CPubKey pk2) { + return MakeTokensCC1of2vout(evalcode, 0, nValue, pk1, pk2); +} + +// make three-eval (token+evalcode+evalcode2) cc vout: +CTxOut MakeTokensCC1vout(uint8_t evalcode, uint8_t evalcode2, CAmount nValue, CPubKey pk) +{ + CTxOut vout; + CC *payoutCond = MakeTokensCCcond1(evalcode, evalcode2, pk); + vout = CTxOut(nValue, CCPubKey(payoutCond)); + cc_free(payoutCond); + return(vout); +} +// overload to make two-eval (token+evalcode) cc vout: +CTxOut MakeTokensCC1vout(uint8_t evalcode, CAmount nValue, CPubKey pk) { + return MakeTokensCC1vout(evalcode, 0, nValue, pk); +} + +}; \ No newline at end of file diff --git a/src/init.cpp b/src/init.cpp index c601cdf5da0..3a616edc4b5 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1657,7 +1657,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) if ( fReindex == 0 ) { - bool checkval,fAddressIndex,fSpentIndex; + bool checkval, fAddressIndex, fSpentIndex, fUnspentCCIndexTmp; pblocktree = new CBlockTreeDB(nBlockTreeDBCache, false, fReindex, dbCompression, dbMaxOpenFiles); fAddressIndex = GetBoolArg("-addressindex", DEFAULT_ADDRESSINDEX); pblocktree->ReadFlag("addressindex", checkval); @@ -1675,6 +1675,15 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) fprintf(stderr,"set spentindex, will reindex. could take a while.\n"); fReindex = true; } + + fUnspentCCIndexTmp = GetBoolArg("-unspentccindex", DEFAULT_UNSPENTCCINDEX); + pblocktree->ReadFlag("unspentccindex", checkval); + if ( checkval != fUnspentCCIndexTmp && fUnspentCCIndexTmp != 0 ) + { + pblocktree->WriteFlag("unspentccindex", fUnspentCCIndexTmp); + fprintf(stderr,"set unspentccindex, will reindex. could take a while.\n"); + fReindex = true; + } } bool clearWitnessCaches = false; diff --git a/src/main.cpp b/src/main.cpp index b2f2868304d..fa7d9698d1d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -113,6 +113,8 @@ bool fCoinbaseEnforcedProtectionEnabled = true; size_t nCoinCacheUsage = 5000 * 300; uint64_t nPruneTarget = 0; bool fAlerts = DEFAULT_ALERTS; +bool fUnspentCCIndex = false; + /* If the tip is older than this (in seconds), the node is considered to be in initial block download. */ int64_t nMaxTipAge = DEFAULT_MAX_TIP_AGE; @@ -1938,7 +1940,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa { if (pfMissingInputs) *pfMissingInputs = true; - //fprintf(stderr,"missing inputs\n"); + fprintf(stderr,"%s missing inputs for tx %s prevout.hash=%s\n", __func__, tx.GetHash().GetHex().c_str(), txin.prevout.hash.GetHex().c_str()); //TODO: remove return false; /* https://github.com/zcash/zcash/blob/master/src/main.cpp#L1490 @@ -2123,6 +2125,10 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa if (fSpentIndex) { pool.addSpentIndex(entry, view); } + + if (fUnspentCCIndex) { + pool.addUnspentCCIndex(entry, view); // add mempool unspent cc index for cc vin/vouts + } } } } @@ -2190,6 +2196,18 @@ bool GetAddressUnspent(uint160 addressHash, int type, return true; } +bool GetUnspentCCIndex(uint160 addressHash, uint256 creationId, + std::vector > &unspentOutputs, int32_t beginHeight, int32_t endHeight, int64_t maxOutputs) +{ + if (!fUnspentCCIndex) + return error("unspent cc index not enabled"); + + if (!pblocktree->ReadUnspentCCIndex(addressHash, creationId, unspentOutputs, beginHeight, endHeight, maxOutputs)) + return error("unable to get outputs for address from unspent cc index"); + + return true; +} + struct CompareBlocksByHeightMain { bool operator()(const CBlockIndex* a, const CBlockIndex* b) const @@ -2216,7 +2234,7 @@ struct CompareBlocksByHeightMain else return(coins.vout[n].nValue); }*/ -bool myAddtomempool(CTransaction &tx, CValidationState *pstate, bool fSkipExpiry) +bool myAddtomempool(const CTransaction &tx, CValidationState *pstate, bool fSkipExpiry) { CValidationState state; if (!pstate) @@ -3242,13 +3260,14 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex std::vector > addressIndex; std::vector > addressUnspentIndex; std::vector > spentIndex; + std::vector > unspentCCIndex; // index for cc transactions // undo transactions in reverse order for (int i = block.vtx.size() - 1; i >= 0; i--) { const CTransaction &tx = block.vtx[i]; uint256 hash = tx.GetHash(); - if (fAddressIndex) { - + if (fAddressIndex || fUnspentCCIndex) + { for (unsigned int k = tx.vout.size(); k-- > 0;) { const CTxOut &out = tx.vout[k]; @@ -3258,11 +3277,36 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex int keyType = GetAddressType(out.scriptPubKey, vDest, txType, vSols); if ( keyType != 0 ) { - for (auto addr : vSols) + if (fAddressIndex) { + for (auto addr : vSols) + { + uint160 addrHash = addr.size() == 20 ? uint160(addr) : Hash160(addr); + addressIndex.push_back(make_pair(CAddressIndexKey(keyType, addrHash, pindex->GetHeight(), i, hash, k, false), out.nValue)); + addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(keyType, addrHash, hash, k), CAddressUnspentValue())); + } + } + if (fUnspentCCIndex) { - uint160 addrHash = addr.size() == 20 ? uint160(addr) : Hash160(addr); - addressIndex.push_back(make_pair(CAddressIndexKey(keyType, addrHash, pindex->GetHeight(), i, hash, k, false), out.nValue)); - addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(keyType, addrHash, hash, k), CAddressUnspentValue())); + if (keyType == 3) // CC type + { + if (vSols.size() > 0) + { + uint160 addrHash = vSols[0].size() == 20 ? uint160(vSols[0]) : Hash160(vSols[0]); // use first vSol data as the address + uint256 creationId; + uint8_t evalcode, funcid, version; + CScript opreturn; //init as empty + if (tx.vout.back().scriptPubKey.size() > 0 && tx.vout.back().scriptPubKey[0] == OP_RETURN) + opreturn = tx.vout.back().scriptPubKey; + + if (CCDecodeTxVout(tx, k, evalcode, funcid, version, creationId)) { + // set key for delete the current entry from unspent cc index + unspentCCIndex.push_back(make_pair( + CUnspentCCIndexKey(addrHash, creationId, hash, k), + CUnspentCCIndexValue())); + //std::cerr << __func__ << " undoing cc tx=" << hash.GetHex() << " nvout=" << k << " evalcode=" << (int)evalcode << " creationId=" << creationId.GetHex() << " opreturn.size()=" << opreturn.size() << std::endl; + } + } + } } } } @@ -3310,22 +3354,51 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex spentIndex.push_back(make_pair(CSpentIndexKey(input.prevout.hash, input.prevout.n), CSpentIndexValue())); } - if (fAddressIndex) { + if (fAddressIndex || fUnspentCCIndex) { const CTxOut &prevout = view.GetOutputFor(tx.vin[j]); vector> vSols; CTxDestination vDest; txnouttype txType = TX_PUBKEYHASH; int keyType = GetAddressType(prevout.scriptPubKey, vDest, txType, vSols); - if ( keyType != 0 ) + if (keyType != 0) { - for (auto addr : vSols) + if (fAddressIndex) { - uint160 addrHash = addr.size() == 20 ? uint160(addr) : Hash160(addr); - // undo spending activity - addressIndex.push_back(make_pair(CAddressIndexKey(keyType, addrHash, pindex->GetHeight(), i, hash, j, true), prevout.nValue * -1)); - // restore unspent index - addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(keyType, addrHash, input.prevout.hash, input.prevout.n), CAddressUnspentValue(prevout.nValue, prevout.scriptPubKey, undo.nHeight))); + for (auto addr : vSols) + { + uint160 addrHash = addr.size() == 20 ? uint160(addr) : Hash160(addr); + // undo spending activity + addressIndex.push_back(make_pair(CAddressIndexKey(keyType, addrHash, pindex->GetHeight(), i, hash, j, true), prevout.nValue * -1)); + // restore unspent index + addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(keyType, addrHash, input.prevout.hash, input.prevout.n), CAddressUnspentValue(prevout.nValue, prevout.scriptPubKey, undo.nHeight))); + } + } + if (fUnspentCCIndex) // support cc index for cc chains + { + if (keyType == 3) // type CC + { + if (vSols.size() > 0) + { + uint160 addrHash = vSols[0].size() == 20 ? uint160(vSols[0]) : Hash160(vSols[0]); // use first vSol data as the address + CTransaction vintx; + uint256 hashBlock; + + if (myGetTransaction(input.prevout.hash, vintx, hashBlock) && vintx.vout.size() > 0) { // load previous tx to get opreturn + uint256 creationId; + uint8_t evalcode, funcid, version; + CScript prevOpreturn; //init as empty + if (vintx.vout.back().scriptPubKey.size() > 0 && vintx.vout.back().scriptPubKey[0] == OP_RETURN) + prevOpreturn = vintx.vout.back().scriptPubKey; + + // restore prev entry: + if (CCDecodeTxVout(vintx, input.prevout.n, evalcode, funcid, version, creationId)) + unspentCCIndex.push_back(make_pair( + CUnspentCCIndexKey(addrHash, creationId, input.prevout.hash, input.prevout.n), + CUnspentCCIndexValue(prevout.nValue, prevout.scriptPubKey, prevOpreturn, undo.nHeight, evalcode, funcid, version))); + } + } + } } } } @@ -3368,6 +3441,12 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex } } + if (fUnspentCCIndex) { + if (!pblocktree->UpdateUnspentCCIndex(unspentCCIndex)) { + return AbortNode(state, "Failed to write unspent cc index"); + } + } + return fClean; } @@ -3485,7 +3564,8 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin return(true); if ( KOMODO_STOPAT != 0 && pindex->GetHeight() > KOMODO_STOPAT ) return(false); - //fprintf(stderr,"connectblock ht.%d\n",(int32_t)pindex->GetHeight()); + fprintf(stderr,"connectblock ht.%d\n",(int32_t)pindex->GetHeight()); + AssertLockHeld(cs_main); bool fExpensiveChecks = true; if (fCheckpointsEnabled) { @@ -3625,9 +3705,12 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin std::vector > vPos; vPos.reserve(block.vtx.size()); blockundo.vtxundo.reserve(block.vtx.size() - 1); + std::vector > addressIndex; std::vector > addressUnspentIndex; std::vector > spentIndex; + std::vector > unspentCCIndex; // index for cc transactions + // Construct the incremental merkle tree at the current // block position, auto old_sprout_tree_root = view.GetBestAnchor(SPROUT); @@ -3679,7 +3762,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin return state.DoS(100, error("ConnectBlock(): JoinSplit requirements not met"), REJECT_INVALID, "bad-txns-joinsplit-requirements-not-met"); - if (fAddressIndex || fSpentIndex) + if (fAddressIndex || fSpentIndex || fUnspentCCIndex) { for (size_t j = 0; j < tx.vin.size(); j++) { @@ -3692,22 +3775,55 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin txnouttype txType = TX_PUBKEYHASH; uint160 addrHash; int keyType = GetAddressType(prevout.scriptPubKey, vDest, txType, vSols); - if ( keyType != 0 ) + if (fAddressIndex || fSpentIndex) { - for (auto addr : vSols) + if ( keyType != 0 ) { - addrHash = addr.size() == 20 ? uint160(addr) : Hash160(addr); - // record spending activity - addressIndex.push_back(make_pair(CAddressIndexKey(keyType, addrHash, pindex->GetHeight(), i, txhash, j, true), prevout.nValue * -1)); + for (auto addr : vSols) + { + addrHash = addr.size() == 20 ? uint160(addr) : Hash160(addr); + // record spending activity + addressIndex.push_back(make_pair(CAddressIndexKey(keyType, addrHash, pindex->GetHeight(), i, txhash, j, true), prevout.nValue * -1)); - // remove address from unspent index - addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(keyType, addrHash, input.prevout.hash, input.prevout.n), CAddressUnspentValue())); - } + // remove address from unspent index + addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(keyType, addrHash, input.prevout.hash, input.prevout.n), CAddressUnspentValue())); + } - if (fSpentIndex) { - // add the spent index to determine the txid and input that spent an output - // and to find the amount and address from an input - spentIndex.push_back(make_pair(CSpentIndexKey(input.prevout.hash, input.prevout.n), CSpentIndexValue(txhash, j, pindex->GetHeight(), prevout.nValue, keyType, addrHash))); + if (fSpentIndex) { + // add the spent index to determine the txid and input that spent an output + // and to find the amount and address from an input + spentIndex.push_back(make_pair(CSpentIndexKey(input.prevout.hash, input.prevout.n), CSpentIndexValue(txhash, j, pindex->GetHeight(), prevout.nValue, keyType, addrHash))); + } + } + } + if (fUnspentCCIndex) + { + // erase spent cc entry + if (keyType == 3) + { + if (vSols.size() > 0) + { + CTransaction vintx; + uint256 hashBlock; + + if (myGetTransaction(input.prevout.hash, vintx, hashBlock) && vintx.vout.size() > 0) + { + uint160 addrHash = vSols[0].size() == 20 ? uint160(vSols[0]) : Hash160(vSols[0]); // use first vSol data as the address + uint256 creationId; + uint8_t evalcode, funcid, version; + CScript opreturn; //init as empty + if (vintx.vout.back().scriptPubKey.size() > 0 && vintx.vout.back().scriptPubKey[0] == OP_RETURN) + opreturn = tx.vout.back().scriptPubKey; + + if (CCDecodeTxVout(vintx, input.prevout.n, evalcode, funcid, version, creationId)) { + // set key for delete the spent output + unspentCCIndex.push_back(make_pair( + CUnspentCCIndexKey(addrHash, creationId, input.prevout.hash, input.prevout.n), + CUnspentCCIndexValue())); + std::cerr << __func__ << " erasing spent cc output evalcode=" << (int)evalcode << " Hash160(vSols[0])=" << Hash160(vSols[0]).GetHex() << " creationId=" << creationId.GetHex() << " opreturn.size()=" << opreturn.size() << std::endl; + } + } + } } } } @@ -3753,7 +3869,8 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin control.Add(vChecks); } - if (fAddressIndex) { + if (fAddressIndex || fUnspentCCIndex) // update address index, unspent index and cc index + { for (unsigned int k = 0; k < tx.vout.size(); k++) { const CTxOut &out = tx.vout[k]; @@ -3763,16 +3880,42 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin CTxDestination vDest; txnouttype txType = TX_PUBKEYHASH; int keyType = GetAddressType(out.scriptPubKey, vDest, txType, vSols); - if ( keyType != 0 ) + if (keyType != 0) { - for (auto addr : vSols) + if (fAddressIndex) { - addrHash = addr.size() == 20 ? uint160(addr) : Hash160(addr); - // record receiving activity - addressIndex.push_back(make_pair(CAddressIndexKey(keyType, addrHash, pindex->GetHeight(), i, txhash, k, false), out.nValue)); + for (auto addr : vSols) + { + addrHash = addr.size() == 20 ? uint160(addr) : Hash160(addr); + // record receiving activity + addressIndex.push_back(make_pair(CAddressIndexKey(keyType, addrHash, pindex->GetHeight(), i, txhash, k, false), out.nValue)); - // record unspent output - addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(keyType, addrHash, txhash, k), CAddressUnspentValue(out.nValue, out.scriptPubKey, pindex->GetHeight()))); + // record unspent output + addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(keyType, addrHash, txhash, k), CAddressUnspentValue(out.nValue, out.scriptPubKey, pindex->GetHeight()))); + } + } + if (fUnspentCCIndex) // support cc index for cc chains + { + if (keyType == 3) // type CC + { + if (vSols.size() > 0) + { + uint160 addrHash = vSols[0].size() == 20 ? uint160(vSols[0]) : Hash160(vSols[0]); // use first vSol data as the address + uint256 creationId; + uint8_t evalcode, funcid, version; + CScript opreturn; //init as empty + if (tx.vout.back().scriptPubKey.size() > 0 && tx.vout.back().scriptPubKey[0] == OP_RETURN) + opreturn = tx.vout.back().scriptPubKey; + + if (CCDecodeTxVout(tx, k, evalcode, funcid, version, creationId)) { + // record cc index output with spk and opreturn + unspentCCIndex.push_back(make_pair( + CUnspentCCIndexKey(addrHash, creationId, txhash, k), + CUnspentCCIndexValue(tx.vout[k].nValue, tx.vout[k].scriptPubKey, opreturn, pindex->GetHeight(), evalcode, funcid, version))); + //std::cerr << __func__ << " adding to cc index tx=" << txhash.GetHex() << " nvout=" << k << " evalcode=" << (int)evalcode << " creationId=" << creationId.GetHex() << " opreturn.size()=" << opreturn.size() << std::endl; + } + } + } } } } @@ -3915,6 +4058,12 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin } } + if (fUnspentCCIndex) { + if (!pblocktree->UpdateUnspentCCIndex(unspentCCIndex)) { + return AbortNode(state, "Failed to write address unspent cc index"); + } + } + if (fSpentIndex) if (!pblocktree->UpdateSpentIndex(spentIndex)) return AbortNode(state, "Failed to write transaction index"); @@ -4314,6 +4463,83 @@ static int64_t nTimeFlush = 0; static int64_t nTimeChainState = 0; static int64_t nTimePostConnect = 0; + +// class to save mempool state and auto restore it (in destructor) +// this is used for assets chains where we need to restore mempool state to make mempool clean up from the txns of a validated block +// if the block was failed to connect +class CMempoolStateSaver +{ +public: + CMempoolStateSaver(const std::string & _caller) : isAssetChain(false), preventRestore(false), caller(_caller) {} + void Save(bool _isAssetChain) + { + isAssetChain = _isAssetChain; + if (isAssetChain) + { + LOCK2(cs_main, mempool.cs); + // remember all non-z txids currently in mempool + for (auto const & e : mempool.mapTx) { + const CTransaction &tx = e.GetTx(); + const uint256 &hash = tx.GetHash(); + if (tx.vjoinsplit.empty() && tx.vShieldedSpend.empty()) + mempoolState[hash] = true; + } + } + } + + // prevent from restore from mempool state if the block was valid and connected okay + void PreventRestore() + { + if (isAssetChain) { + mempoolState.clear(); + preventRestore = true; + } + } + +private: + void _RestoreState() + { + if (isAssetChain) + { + if (!preventRestore) + { + LOCK2(cs_main, mempool.cs); + + // remove txns that have been added in CheckBlock() for the connected block + std::list txnsToRemove; + for (auto const & e : mempool.mapTx) { + const CTransaction &tx = e.GetTx(); + uint256 hash = tx.GetHash(); + if (tx.vjoinsplit.empty() && tx.vShieldedSpend.empty()) { + if (!mempoolState[hash]) + txnsToRemove.push_back(tx); // + } + } + + // do actual remove + for (auto const & tx : txnsToRemove) { + list removed; + mempool.remove(tx, removed, false); + // std::cerr << __func__ << " mempool.removed=" << tx.GetHash().GetHex() << " " << removed.size() << std::endl; + } + } + } + } +public: + // auto restore the saved mempool state + ~CMempoolStateSaver() + { + _RestoreState(); + } + +private: + bool isAssetChain; + bool preventRestore; + std::map mempoolState; + std::string caller; +}; + + /** * Connect a new block to chainActive. pblock is either NULL or a pointer to a CBlock * corresponding to pindexNew, to bypass loading it again from disk. @@ -4346,6 +4572,12 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * LogPrint("bench", " - Load block from disk: %.2fms [%.2fs]\n", (nTime2 - nTime1) * 0.001, nTimeReadFromDisk * 0.000001); { CCoinsViewCache view(pcoinsTip); + + // save mempool state, for asset chains: + CMempoolStateSaver mempoolState(__func__); + mempoolState.Save(ASSETCHAINS_CC != 0); + // The mempool will be restored to the initial state when mempoolState exits its scope (if any errors) + bool rv = ConnectBlock(*pblock, state, pindexNew, view, false, true); KOMODO_CONNECTING = -1; GetMainSignals().BlockChecked(*pblock, state); @@ -5276,32 +5508,36 @@ bool CheckBlock(int32_t *futureblockp,int32_t height,CBlockIndex *pindex,const C if ( ASSETCHAINS_CC != 0 && !fCheckPOW ) return true; - if ( ASSETCHAINS_CC != 0 ) // CC contracts might refer to transactions in the current block, from a CC spend within the same block and out of order + if (ASSETCHAINS_CC != 0) // CC contracts might refer to transactions in the current block, from a CC spend within the same block and out of order { int32_t i,j,rejects=0,lastrejects=0; //fprintf(stderr,"put block's tx into mempool\n"); // Copy all non Z-txs in mempool to temporary mempool because there can be tx in local mempool that make the block invalid. - LOCK2(cs_main,mempool.cs); + // LOCK2(cs_main,mempool.cs); + ENTER_CRITICAL_SECTION(cs_main); + ENTER_CRITICAL_SECTION(mempool.cs); + //fprintf(stderr, "starting... mempoolsize.%ld\n",mempool.size()); list transactionsToRemove; - BOOST_FOREACH(const CTxMemPoolEntry& e, mempool.mapTx) { + for(const CTxMemPoolEntry& e : mempool.mapTx) { const CTransaction &tx = e.GetTx(); const uint256 &hash = tx.GetHash(); - if ( tx.vjoinsplit.empty() && tx.vShieldedSpend.empty()) { + if (tx.vjoinsplit.empty() && tx.vShieldedSpend.empty()) { transactionsToRemove.push_back(tx); tmpmempool.addUnchecked(hash,e,true); } } - BOOST_FOREACH(const CTransaction& tx, transactionsToRemove) { + for(const CTransaction& tx : transactionsToRemove) { list removed; mempool.remove(tx, removed, false); + // std::cerr << __func__ << " removed to tmpmempool tx=" << tx.GetHash().GetHex() << std::endl; } // add all the txs in the block to the empty mempool. // CC validation shouldnt (cant) depend on the state of mempool! - while ( 1 ) + while( true ) { list removed; - for (i=0; iGetHeight()); { LOCK(cs_main); + + // save mempool state and clear it, for asset chains: + CMempoolStateSaver mempoolState(__func__); + mempoolState.Save(ASSETCHAINS_CC != 0); // The mempool will be restored to the initial state when mempoolState exits its scope + // this mempol state should be restored after the exist of this block as there is no block connect done yet + if ( chainActive.LastTip() != 0 ) komodo_currentheight_set(chainActive.LastTip()->GetHeight()); checked = CheckBlock(&futureblock,height!=0?height:komodo_block2height(pblock),0,*pblock, state, verifier,0); @@ -5896,6 +6181,11 @@ bool TestBlockValidity(CValidationState &state, const CBlock& block, CBlockIndex indexDummy.SetHeight(pindexPrev->GetHeight() + 1); // JoinSplit proofs are verified in ConnectBlock auto verifier = libzcash::ProofVerifier::Disabled(); + + // save mempool state and clear it, for asset chains: + CMempoolStateSaver mempoolState(__func__); + mempoolState.Save(ASSETCHAINS_CC != 0); // the mempool state will be restored when mempoolState exits its scope + // NOTE: CheckBlockHeader is called by CheckBlock if (!ContextualCheckBlockHeader(block, state, pindexPrev)) { @@ -6297,6 +6587,9 @@ bool static LoadBlockIndexDB() pblocktree->ReadFlag("spentindex", fSpentIndex); LogPrintf("%s: spent index %s\n", __func__, fSpentIndex ? "enabled" : "disabled"); + pblocktree->ReadFlag("unspentccindex", fUnspentCCIndex); + LogPrintf("%s: unspent cc index %s\n", __func__, fUnspentCCIndex ? "enabled" : "disabled"); + // Fill in-memory data BOOST_FOREACH(const PAIRTYPE(uint256, CBlockIndex*)& item, mapBlockIndex) { @@ -6667,6 +6960,11 @@ bool InitBlockIndex() { fSpentIndex = GetBoolArg("-spentindex", DEFAULT_SPENTINDEX); pblocktree->WriteFlag("spentindex", fSpentIndex); fprintf(stderr,"fAddressIndex.%d/%d fSpentIndex.%d/%d\n",fAddressIndex,DEFAULT_ADDRESSINDEX,fSpentIndex,DEFAULT_SPENTINDEX); + + fUnspentCCIndex = GetBoolArg("-unspentccindex", DEFAULT_SPENTINDEX); + pblocktree->WriteFlag("unspentccindex", true); + fprintf(stderr,"fUnspentCCIndex.%d\n", true); + LogPrintf("Initializing databases...\n"); } // Only add the genesis block if not reindexing (in which case we reuse the one already on disk) diff --git a/src/main.h b/src/main.h index ba03410714a..e857e2d06f6 100644 --- a/src/main.h +++ b/src/main.h @@ -43,6 +43,7 @@ #include "tinyformat.h" #include "txmempool.h" #include "uint256.h" +#include "unspentccindex.h" #include #include @@ -127,6 +128,8 @@ static const bool DEFAULT_NSPV_PROCESSING = false; //static const bool DEFAULT_SPENTINDEX = false; #define DEFAULT_ADDRESSINDEX (GetArg("-ac_cc",0) != 0 || GetArg("-ac_ccactivate",0) != 0) #define DEFAULT_SPENTINDEX (GetArg("-ac_cc",0) != 0 || GetArg("-ac_ccactivate",0) != 0) +#define DEFAULT_UNSPENTCCINDEX (GetArg("-ac_cc",0) != 0 || GetArg("-ac_ccactivate",0) != 0) + static const bool DEFAULT_TIMESTAMPINDEX = false; static const unsigned int DEFAULT_DB_MAX_OPEN_FILES = 1000; static const bool DEFAULT_DB_COMPRESSION = true; @@ -848,6 +851,10 @@ bool GetAddressIndex(uint160 addressHash, int type, bool GetAddressUnspent(uint160 addressHash, int type, std::vector > &unspentOutputs); +// get utxos from unspet cc index +bool GetUnspentCCIndex(uint160 addressHash, uint256 creationId, + std::vector > &unspentOutputs, int32_t beginHeight, int32_t endHeight, int64_t maxOutputs); + /** Functions for disk access for blocks */ bool WriteBlockToDisk(const CBlock& block, CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& messageStart); bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos,bool checkPOW); @@ -992,4 +999,11 @@ uint64_t CalculateCurrentUsage(); /** Return a CMutableTransaction with contextual default values based on set of consensus rules at height */ CMutableTransaction CreateNewContextualCMutableTransaction(const Consensus::Params& consensusParams, int nHeight); +/** + * Extracts solutions and destination from scriptPubKey + * determines dest txType (pubkey or pubkey hash, script hash or CC) + */ +int8_t GetAddressType(const CScript &scriptPubKey, CTxDestination &vDest, txnouttype &txType, std::vector> &vSols); + + #endif // BITCOIN_MAIN_H diff --git a/src/miner.cpp b/src/miner.cpp index 9c1ed5d0959..281edf69a50 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -2022,161 +2022,162 @@ void static BitcoinMiner() // Found a solution SetThreadPriority(THREAD_PRIORITY_NORMAL); LogPrintf("KomodoMiner:\n"); - LogPrintf("proof-of-work found \n hash: %s \ntarget: %s\n", B.GetHash().GetHex(), HASHTarget.GetHex()); + LogPrintf("proof-of-work found \n hash: %s \ntarget: %s\n", B.GetHash().GetHex().c_str(), HASHTarget.GetHex().c_str()); #ifdef ENABLE_WALLET if (ProcessBlockFound(&B, *pwallet, reservekey)) { #else - if (ProcessBlockFound(&B)) { + if (ProcessBlockFound(&B)) { #endif - // Ignore chain updates caused by us - std::lock_guard lock{m_cs}; - cancelSolver = false; - } - KOMODO_CHOSEN_ONE = 0; - SetThreadPriority(THREAD_PRIORITY_LOWEST); - // In regression test mode, stop mining after a block is found. - if (chainparams.MineBlocksOnDemand()) { - // Increment here because throwing skips the call below - ehSolverRuns.increment(); - throw boost::thread_interrupted(); - } - return true; - }; - std::function cancelled = [&m_cs, &cancelSolver](EhSolverCancelCheck pos) { + // Ignore chain updates caused by us std::lock_guard lock{m_cs}; - return cancelSolver; - }; - // TODO: factor this out into a function with the same API for each solver. - if (solver == "tromp" ) { //&& notaryid >= 0 ) { - // Create solver and initialize it. - equi eq(1); - eq.setstate(&curr_state); - - // Initialization done, start algo driver. - eq.digit0(0); + cancelSolver = false; + } + KOMODO_CHOSEN_ONE = 0; + SetThreadPriority(THREAD_PRIORITY_LOWEST); + // In regression test mode, stop mining after a block is found. + if (chainparams.MineBlocksOnDemand()) { + // Increment here because throwing skips the call below + ehSolverRuns.increment(); + throw boost::thread_interrupted(); + } + return true; + }; + + std::function cancelled = [&m_cs, &cancelSolver](EhSolverCancelCheck pos) { + std::lock_guard lock{m_cs}; + return cancelSolver; + }; + // TODO: factor this out into a function with the same API for each solver. + if (solver == "tromp" ) { //&& notaryid >= 0 ) { + // Create solver and initialize it. + equi eq(1); + eq.setstate(&curr_state); + + // Initialization done, start algo driver. + eq.digit0(0); + eq.xfull = eq.bfull = eq.hfull = 0; + eq.showbsizes(0); + for (u32 r = 1; r < WK; r++) { + (r&1) ? eq.digitodd(r, 0) : eq.digiteven(r, 0); eq.xfull = eq.bfull = eq.hfull = 0; - eq.showbsizes(0); - for (u32 r = 1; r < WK; r++) { - (r&1) ? eq.digitodd(r, 0) : eq.digiteven(r, 0); - eq.xfull = eq.bfull = eq.hfull = 0; - eq.showbsizes(r); + eq.showbsizes(r); + } + eq.digitK(0); + ehSolverRuns.increment(); + + // Convert solution indices to byte array (decompress) and pass it to validBlock method. + for (size_t s = 0; s < eq.nsols; s++) { + LogPrint("pow", "Checking solution %d\n", s+1); + std::vector index_vector(PROOFSIZE); + for (size_t i = 0; i < PROOFSIZE; i++) { + index_vector[i] = eq.sols[s][i]; } - eq.digitK(0); - ehSolverRuns.increment(); - - // Convert solution indices to byte array (decompress) and pass it to validBlock method. - for (size_t s = 0; s < eq.nsols; s++) { - LogPrint("pow", "Checking solution %d\n", s+1); - std::vector index_vector(PROOFSIZE); - for (size_t i = 0; i < PROOFSIZE; i++) { - index_vector[i] = eq.sols[s][i]; - } - std::vector sol_char = GetMinimalFromIndices(index_vector, DIGITBITS); + std::vector sol_char = GetMinimalFromIndices(index_vector, DIGITBITS); - if (validBlock(sol_char)) { - // If we find a POW solution, do not try other solutions - // because they become invalid as we created a new block in blockchain. - break; - } - } - } else { - try { - // If we find a valid block, we rebuild - bool found = EhOptimisedSolve(n, k, curr_state, validBlock, cancelled); - ehSolverRuns.increment(); - if (found) { - int32_t i; uint256 hash = pblock->GetHash(); - //for (i=0; i<32; i++) - // fprintf(stderr,"%02x",((uint8_t *)&hash)[i]); - //fprintf(stderr," <- %s Block found %d\n",ASSETCHAINS_SYMBOL,Mining_height); - //FOUND_BLOCK = 1; - //KOMODO_MAYBEMINED = Mining_height; - break; - } - } catch (EhSolverCancelledException&) { - LogPrint("pow", "Equihash solver cancelled\n"); - std::lock_guard lock{m_cs}; - cancelSolver = false; + if (validBlock(sol_char)) { + // If we find a POW solution, do not try other solutions + // because they become invalid as we created a new block in blockchain. + break; } } - - // Check for stop or if block needs to be rebuilt - boost::this_thread::interruption_point(); - // Regtest mode doesn't require peers - /*if ( FOUND_BLOCK != 0 ) - { - FOUND_BLOCK = 0; - fprintf(stderr,"FOUND_BLOCK!\n"); - //sleep(2000); - } */ - if (vNodes.empty() && chainparams.MiningRequiresPeers()) - { - if ( ASSETCHAINS_SYMBOL[0] == 0 || Mining_height > ASSETCHAINS_MINHEIGHT ) - { - fprintf(stderr,"no nodes, break\n"); + } else { + try { + // If we find a valid block, we rebuild + bool found = EhOptimisedSolve(n, k, curr_state, validBlock, cancelled); + ehSolverRuns.increment(); + if (found) { + int32_t i; uint256 hash = pblock->GetHash(); + //for (i=0; i<32; i++) + // fprintf(stderr,"%02x",((uint8_t *)&hash)[i]); + //fprintf(stderr," <- %s Block found %d\n",ASSETCHAINS_SYMBOL,Mining_height); + //FOUND_BLOCK = 1; + //KOMODO_MAYBEMINED = Mining_height; break; } + } catch (EhSolverCancelledException&) { + LogPrint("pow", "Equihash solver cancelled\n"); + std::lock_guard lock{m_cs}; + cancelSolver = false; } - if ((UintToArith256(pblock->nNonce) & 0xffff) == 0xffff) - { - //if ( 0 && ASSETCHAINS_SYMBOL[0] != 0 ) - fprintf(stderr,"0xffff, break\n"); - break; - } - if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 60) - { - if ( 0 && ASSETCHAINS_SYMBOL[0] != 0 ) - fprintf(stderr,"timeout, break\n"); - break; - } - if ( pindexPrev != chainActive.LastTip() ) + } + + // Check for stop or if block needs to be rebuilt + boost::this_thread::interruption_point(); + // Regtest mode doesn't require peers + /*if ( FOUND_BLOCK != 0 ) + { + FOUND_BLOCK = 0; + fprintf(stderr,"FOUND_BLOCK!\n"); + //sleep(2000); + } */ + if (vNodes.empty() && chainparams.MiningRequiresPeers()) + { + if ( ASSETCHAINS_SYMBOL[0] == 0 || Mining_height > ASSETCHAINS_MINHEIGHT ) { - if ( 0 && ASSETCHAINS_SYMBOL[0] != 0 ) - fprintf(stderr,"Tip advanced, break\n"); + fprintf(stderr,"no nodes, break\n"); break; } - // Update nNonce and nTime - pblock->nNonce = ArithToUint256(UintToArith256(pblock->nNonce) + 1); - pblock->nBits = savebits; - if ( ASSETCHAINS_ADAPTIVEPOW > 0 ) + } + if ((UintToArith256(pblock->nNonce) & 0xffff) == 0xffff) + { + //if ( 0 && ASSETCHAINS_SYMBOL[0] != 0 ) + fprintf(stderr,"0xffff, break\n"); + break; + } + if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 60) + { + if ( 0 && ASSETCHAINS_SYMBOL[0] != 0 ) + fprintf(stderr,"timeout, break\n"); + break; + } + if ( pindexPrev != chainActive.LastTip() ) + { + if ( 0 && ASSETCHAINS_SYMBOL[0] != 0 ) + fprintf(stderr,"Tip advanced, break\n"); + break; + } + // Update nNonce and nTime + pblock->nNonce = ArithToUint256(UintToArith256(pblock->nNonce) + 1); + pblock->nBits = savebits; + if ( ASSETCHAINS_ADAPTIVEPOW > 0 ) + { + UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev); + HASHTarget.SetCompact(pblock->nBits); + hashTarget = HASHTarget; + savebits = pblock->nBits; + //hashTarget = HASHTarget_POW = komodo_adaptivepow_target(Mining_height,HASHTarget,pblock->nTime); + } + /*if ( NOTARY_PUBKEY33[0] == 0 ) + { + int32_t percPoS; + UpdateTime(pblock, consensusParams, pindexPrev); + if (consensusParams.fPowAllowMinDifficultyBlocks) { - UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev); + // Changing pblock->nTime can change work required on testnet: HASHTarget.SetCompact(pblock->nBits); - hashTarget = HASHTarget; - savebits = pblock->nBits; - //hashTarget = HASHTarget_POW = komodo_adaptivepow_target(Mining_height,HASHTarget,pblock->nTime); + HASHTarget_POW = komodo_PoWtarget(&percPoS,HASHTarget,Mining_height,ASSETCHAINS_STAKED); } - /*if ( NOTARY_PUBKEY33[0] == 0 ) - { - int32_t percPoS; - UpdateTime(pblock, consensusParams, pindexPrev); - if (consensusParams.fPowAllowMinDifficultyBlocks) - { - // Changing pblock->nTime can change work required on testnet: - HASHTarget.SetCompact(pblock->nBits); - HASHTarget_POW = komodo_PoWtarget(&percPoS,HASHTarget,Mining_height,ASSETCHAINS_STAKED); - } - }*/ - } + }*/ } } - catch (const boost::thread_interrupted&) - { - miningTimer.stop(); - c.disconnect(); - LogPrintf("KomodoMiner terminated\n"); - throw; - } - catch (const std::runtime_error &e) - { - miningTimer.stop(); - c.disconnect(); - LogPrintf("KomodoMiner runtime error: %s\n", e.what()); - return; - } + } + catch (const boost::thread_interrupted&) + { miningTimer.stop(); c.disconnect(); + LogPrintf("KomodoMiner terminated\n"); + throw; } + catch (const std::runtime_error &e) + { + miningTimer.stop(); + c.disconnect(); + LogPrintf("KomodoMiner runtime error: %s\n", e.what()); + return; + } + miningTimer.stop(); + c.disconnect(); +} #ifdef ENABLE_WALLET void GenerateBitcoins(bool fGenerate, CWallet* pwallet, int nThreads) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 38124042861..565a5bfb92d 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -4,19 +4,19 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. /****************************************************************************** - * Copyright © 2014-2019 The SuperNET Developers. * - * * - * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * - * the top-level directory of this distribution for the individual copyright * - * holder information and the developer policies on copyright and licensing. * - * * - * Unless otherwise agreed in a custom licensing agreement, no part of the * - * SuperNET software, including this file may be copied, modified, propagated * - * or distributed except according to the terms contained in the LICENSE file * - * * - * Removal or modification of this copyright notice is prohibited. * - * * - ******************************************************************************/ +* Copyright © 2014-2019 The SuperNET Developers. * +* * +* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * +* the top-level directory of this distribution for the individual copyright * +* holder information and the developer policies on copyright and licensing. * +* * +* Unless otherwise agreed in a custom licensing agreement, no part of the * +* SuperNET software, including this file may be copied, modified, propagated * +* or distributed except according to the terms contained in the LICENSE file * +* * +* Removal or modification of this copyright notice is prohibited. * +* * +******************************************************************************/ #include "amount.h" #include "chain.h" @@ -51,7 +51,7 @@ using namespace std; extern int32_t KOMODO_INSYNC; extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry); void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex); -int32_t komodo_notarized_height(int32_t *prevMoMheightp,uint256 *hashp,uint256 *txidp); +int32_t komodo_notarized_height(int32_t *prevMoMheightp, uint256 *hashp, uint256 *txidp); #include "komodo_defs.h" #include "komodo_structs.h" @@ -70,7 +70,8 @@ double GetDifficultyINTERNAL(const CBlockIndex* blockindex, bool networkDifficul uint32_t bits; if (networkDifficulty) { bits = GetNextWorkRequired(blockindex, nullptr, Params().GetConsensus()); - } else { + } + else { bits = blockindex->nBits; } @@ -129,20 +130,20 @@ static UniValue ValuePoolDesc( UniValue blockheaderToJSON(const CBlockIndex* blockindex) { UniValue result(UniValue::VOBJ); - if ( blockindex == 0 ) + if (blockindex == 0) { result.push_back(Pair("error", "null blockhash")); return(result); } - uint256 notarized_hash,notarized_desttxid; int32_t prevMoMheight,notarized_height; - notarized_height = komodo_notarized_height(&prevMoMheight,¬arized_hash,¬arized_desttxid); + uint256 notarized_hash, notarized_desttxid; int32_t prevMoMheight, notarized_height; + notarized_height = komodo_notarized_height(&prevMoMheight, ¬arized_hash, ¬arized_desttxid); result.push_back(Pair("last_notarized_height", notarized_height)); result.push_back(Pair("hash", blockindex->GetBlockHash().GetHex())); int confirmations = -1; // Only report confirmations if the block is on the main chain if (chainActive.Contains(blockindex)) confirmations = chainActive.Height() - blockindex->GetHeight() + 1; - result.push_back(Pair("confirmations", komodo_dpowconfs(blockindex->GetHeight(),confirmations))); + result.push_back(Pair("confirmations", komodo_dpowconfs(blockindex->GetHeight(), confirmations))); result.push_back(Pair("rawconfirmations", confirmations)); result.push_back(Pair("height", blockindex->GetHeight())); result.push_back(Pair("version", blockindex->nVersion)); @@ -154,7 +155,7 @@ UniValue blockheaderToJSON(const CBlockIndex* blockindex) result.push_back(Pair("bits", strprintf("%08x", blockindex->nBits))); result.push_back(Pair("difficulty", GetDifficulty(blockindex))); result.push_back(Pair("chainwork", blockindex->chainPower.chainWork.GetHex())); - result.push_back(Pair("segid", (int)komodo_segid(0,blockindex->GetHeight()))); + result.push_back(Pair("segid", (int)komodo_segid(0, blockindex->GetHeight()))); if (blockindex->pprev) result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex())); @@ -172,16 +173,17 @@ UniValue blockToDeltasJSON(const CBlock& block, const CBlockIndex* blockindex) // Only report confirmations if the block is on the main chain if (chainActive.Contains(blockindex)) { confirmations = chainActive.Height() - blockindex->GetHeight() + 1; - } else { + } + else { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block is an orphan"); } - result.push_back(Pair("confirmations", komodo_dpowconfs(blockindex->GetHeight(),confirmations))); + result.push_back(Pair("confirmations", komodo_dpowconfs(blockindex->GetHeight(), confirmations))); result.push_back(Pair("rawconfirmations", confirmations)); result.push_back(Pair("size", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION))); result.push_back(Pair("height", blockindex->GetHeight())); result.push_back(Pair("version", block.nVersion)); result.push_back(Pair("merkleroot", block.hashMerkleRoot.GetHex())); - result.push_back(Pair("segid", (int)komodo_segid(0,blockindex->GetHeight()))); + result.push_back(Pair("segid", (int)komodo_segid(0, blockindex->GetHeight()))); UniValue deltas(UniValue::VARR); @@ -209,7 +211,7 @@ UniValue blockToDeltasJSON(const CBlock& block, const CBlockIndex* blockindex) if (spentInfo.addressType == 1) { delta.push_back(Pair("address", CBitcoinAddress(CKeyID(spentInfo.addressHash)).ToString())); } - else if (spentInfo.addressType == 2) { + else if (spentInfo.addressType == 2) { delta.push_back(Pair("address", CBitcoinAddress(CScriptID(spentInfo.addressHash)).ToString())); } else { @@ -221,7 +223,8 @@ UniValue blockToDeltasJSON(const CBlock& block, const CBlockIndex* blockindex) delta.push_back(Pair("prevout", (int)input.prevout.n)); inputs.push_back(delta); - } else { + } + else { throw JSONRPCError(RPC_INTERNAL_ERROR, "Spent information not available"); } @@ -238,12 +241,12 @@ UniValue blockToDeltasJSON(const CBlock& block, const CBlockIndex* blockindex) UniValue delta(UniValue::VOBJ); if (out.scriptPubKey.IsPayToScriptHash()) { - vector hashBytes(out.scriptPubKey.begin()+2, out.scriptPubKey.begin()+22); + vector hashBytes(out.scriptPubKey.begin() + 2, out.scriptPubKey.begin() + 22); delta.push_back(Pair("address", CBitcoinAddress(CScriptID(uint160(hashBytes))).ToString())); } else if (out.scriptPubKey.IsPayToPublicKeyHash()) { - vector hashBytes(out.scriptPubKey.begin()+3, out.scriptPubKey.begin()+23); + vector hashBytes(out.scriptPubKey.begin() + 3, out.scriptPubKey.begin() + 23); delta.push_back(Pair("address", CBitcoinAddress(CKeyID(uint160(hashBytes))).ToString())); } else if (out.scriptPubKey.IsPayToPublicKey() || out.scriptPubKey.IsPayToCryptoCondition()) { @@ -288,26 +291,26 @@ UniValue blockToDeltasJSON(const CBlock& block, const CBlockIndex* blockindex) UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails = false) { UniValue result(UniValue::VOBJ); - uint256 notarized_hash,notarized_desttxid; int32_t prevMoMheight,notarized_height; - notarized_height = komodo_notarized_height(&prevMoMheight,¬arized_hash,¬arized_desttxid); + uint256 notarized_hash, notarized_desttxid; int32_t prevMoMheight, notarized_height; + notarized_height = komodo_notarized_height(&prevMoMheight, ¬arized_hash, ¬arized_desttxid); result.push_back(Pair("last_notarized_height", notarized_height)); result.push_back(Pair("hash", block.GetHash().GetHex())); int confirmations = -1; // Only report confirmations if the block is on the main chain if (chainActive.Contains(blockindex)) confirmations = chainActive.Height() - blockindex->GetHeight() + 1; - result.push_back(Pair("confirmations", komodo_dpowconfs(blockindex->GetHeight(),confirmations))); + result.push_back(Pair("confirmations", komodo_dpowconfs(blockindex->GetHeight(), confirmations))); result.push_back(Pair("rawconfirmations", confirmations)); result.push_back(Pair("size", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION))); result.push_back(Pair("height", blockindex->GetHeight())); result.push_back(Pair("version", block.nVersion)); result.push_back(Pair("merkleroot", block.hashMerkleRoot.GetHex())); - result.push_back(Pair("segid", (int)komodo_segid(0,blockindex->GetHeight()))); + result.push_back(Pair("segid", (int)komodo_segid(0, blockindex->GetHeight()))); result.push_back(Pair("finalsaplingroot", block.hashFinalSaplingRoot.GetHex())); UniValue txs(UniValue::VARR); BOOST_FOREACH(const CTransaction&tx, block.vtx) { - if(txDetails) + if (txDetails) { UniValue objTx(UniValue::VOBJ); TxToJSON(tx, uint256(), objTx); @@ -390,22 +393,24 @@ UniValue getdifficulty(const UniValue& params, bool fHelp, const CPubKey& mypk) return GetNetworkDifficulty(); } -bool NSPV_spentinmempool(uint256 &spenttxid,int32_t &spentvini,uint256 txid,int32_t vout); +bool NSPV_spentinmempool(uint256 &spenttxid, int32_t &spentvini, uint256 txid, int32_t vout); bool NSPV_inmempool(uint256 txid); -bool myIsutxo_spentinmempool(uint256 &spenttxid,int32_t &spentvini,uint256 txid,int32_t vout) +bool myIsutxo_spentinmempool(uint256 &spenttxid, int32_t &spentvini, uint256 txid, int32_t vout) { - int32_t vini = 0; - if ( KOMODO_NSPV_SUPERLITE ) - return(NSPV_spentinmempool(spenttxid,spentvini,txid,vout)); - BOOST_FOREACH(const CTxMemPoolEntry &e,mempool.mapTx) + if (KOMODO_NSPV_SUPERLITE) + return(NSPV_spentinmempool(spenttxid, spentvini, txid, vout)); + + /* old full search impl: + BOOST_FOREACH(const CTxMemPoolEntry &e, mempool.mapTx) { const CTransaction &tx = e.GetTx(); const uint256 &hash = tx.GetHash(); - BOOST_FOREACH(const CTxIn &txin,tx.vin) + int32_t vini = 0; + BOOST_FOREACH(const CTxIn &txin, tx.vin) { //fprintf(stderr,"%s/v%d ",uint256_str(str,txin.prevout.hash),txin.prevout.n); - if ( txin.prevout.n == vout && txin.prevout.hash == txid ) + if (txin.prevout.n == vout && txin.prevout.hash == txid) { spenttxid = hash; spentvini = vini; @@ -416,19 +421,31 @@ bool myIsutxo_spentinmempool(uint256 &spenttxid,int32_t &spentvini,uint256 txid, //fprintf(stderr,"are vins for %s\n",uint256_str(str,hash)); } return(false); + */ + + // indexed impl: + CSpentIndexKey key { txid, (uint32_t)vout }; + CSpentIndexValue value; + if (mempool.getSpentIndex(key, value)) { + spenttxid = value.txid; + spentvini = value.inputIndex; + return true; + } + else + return false; } bool mytxid_inmempool(uint256 txid) { - if ( KOMODO_NSPV_SUPERLITE ) + if (KOMODO_NSPV_SUPERLITE) { - + } - BOOST_FOREACH(const CTxMemPoolEntry &e,mempool.mapTx) + BOOST_FOREACH(const CTxMemPoolEntry &e, mempool.mapTx) { const CTransaction &tx = e.GetTx(); const uint256 &hash = tx.GetHash(); - if ( txid == hash ) + if (txid == hash) return(true); } return(false); @@ -540,7 +557,7 @@ UniValue getblockdeltas(const UniValue& params, bool fHelp, const CPubKey& mypk) if (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0) throw JSONRPCError(RPC_INTERNAL_ERROR, "Block not available (pruned data)"); - if(!ReadBlockFromDisk(block, pblockindex,1)) + if (!ReadBlockFromDisk(block, pblockindex, 1)) throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk"); return blockToDeltasJSON(block, pblockindex); @@ -574,7 +591,7 @@ UniValue getblockhashes(const UniValue& params, bool fHelp, const CPubKey& mypk) + HelpExampleCli("getblockhashes", "1231614698 1231024505") + HelpExampleRpc("getblockhashes", "1231614698, 1231024505") + HelpExampleCli("getblockhashes", "1231614698 1231024505 '{\"noOrphans\":false, \"logicalTimes\":true}'") - ); + ); unsigned int high = params[0].get_int(); unsigned int low = params[1].get_int(); @@ -605,13 +622,14 @@ UniValue getblockhashes(const UniValue& params, bool fHelp, const CPubKey& mypk) UniValue result(UniValue::VARR); - for (std::vector >::const_iterator it=blockHashes.begin(); it!=blockHashes.end(); it++) { + for (std::vector >::const_iterator it = blockHashes.begin(); it != blockHashes.end(); it++) { if (fLogicalTS) { UniValue item(UniValue::VOBJ); item.push_back(Pair("blockhash", it->first.GetHex())); item.push_back(Pair("logicalts", (int)it->second)); result.push_back(item); - } else { + } + else { result.push_back(it->first.GetHex()); } } @@ -662,62 +680,62 @@ UniValue getlastsegidstakes(const UniValue& params, bool fHelp, const CPubKey& m + HelpExampleRpc("getlastsegidstakes", "1000") ); - if ( ASSETCHAINS_STAKED == 0 ) + if (ASSETCHAINS_STAKED == 0) throw runtime_error("Only applies to ac_staked chains\n"); LOCK(cs_main); int depth = params[0].get_int(); - if ( depth > chainActive.Height() ) + if (depth > chainActive.Height()) throw runtime_error("Not enough blocks to scan back that far.\n"); - - int32_t segids[64] = {0}; + + int32_t segids[64] = { 0 }; int32_t pow = 0; int32_t notset = 0; - for (int64_t i = chainActive.Height(); i > chainActive.Height()-depth; i--) + for (int64_t i = chainActive.Height(); i > chainActive.Height() - depth; i--) { - int8_t segid = komodo_segid(0,i); + int8_t segid = komodo_segid(0, i); //CBlockIndex* pblockindex = chainActive[i]; - if ( segid >= 0 ) + if (segid >= 0) segids[segid] += 1; - else if ( segid == -1 ) + else if (segid == -1) pow++; else notset++; } - - int8_t posperc = 100*(depth-pow)/depth; - + + int8_t posperc = 100 * (depth - pow) / depth; + UniValue ret(UniValue::VOBJ); UniValue objsegids(UniValue::VOBJ); for (int8_t i = 0; i < 64; i++) { char str[4]; sprintf(str, "%d", i); - objsegids.push_back(Pair(str,segids[i])); + objsegids.push_back(Pair(str, segids[i])); } - ret.push_back(Pair("NotSet",notset)); - ret.push_back(Pair("PoW",pow)); - ret.push_back(Pair("PoSPerc",posperc)); - ret.push_back(Pair("SegIds",objsegids)); + ret.push_back(Pair("NotSet", notset)); + ret.push_back(Pair("PoW", pow)); + ret.push_back(Pair("PoSPerc", posperc)); + ret.push_back(Pair("SegIds", objsegids)); return ret; } /*uint256 _komodo_getblockhash(int32_t nHeight) { - uint256 hash; - LOCK(cs_main); - if ( nHeight >= 0 && nHeight <= chainActive.Height() ) - { - CBlockIndex* pblockindex = chainActive[nHeight]; - hash = pblockindex->GetBlockHash(); - int32_t i; - for (i=0; i<32; i++) - printf("%02x",((uint8_t *)&hash)[i]); - printf(" blockhash.%d\n",nHeight); - } else memset(&hash,0,sizeof(hash)); - return(hash); +uint256 hash; +LOCK(cs_main); +if ( nHeight >= 0 && nHeight <= chainActive.Height() ) +{ +CBlockIndex* pblockindex = chainActive[nHeight]; +hash = pblockindex->GetBlockHash(); +int32_t i; +for (i=0; i<32; i++) +printf("%02x",((uint8_t *)&hash)[i]); +printf(" blockhash.%d\n",nHeight); +} else memset(&hash,0,sizeof(hash)); +return(hash); }*/ UniValue getblockheader(const UniValue& params, bool fHelp, const CPubKey& mypk) @@ -857,9 +875,10 @@ UniValue getblock(const UniValue& params, bool fHelp, const CPubKey& mypk) int verbosity = 1; if (params.size() > 1) { - if(params[1].isNum()) { + if (params[1].isNum()) { verbosity = params[1].get_int(); - } else { + } + else { verbosity = params[1].get_bool() ? 1 : 0; } } @@ -877,7 +896,7 @@ UniValue getblock(const UniValue& params, bool fHelp, const CPubKey& mypk) if (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0) throw JSONRPCError(RPC_INTERNAL_ERROR, "Block not available (pruned data)"); - if(!ReadBlockFromDisk(block, pblockindex,1)) + if (!ReadBlockFromDisk(block, pblockindex, 1)) throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk"); if (verbosity == 0) @@ -932,8 +951,8 @@ UniValue gettxoutsetinfo(const UniValue& params, bool fHelp, const CPubKey& mypk UniValue kvsearch(const UniValue& params, bool fHelp, const CPubKey& mypk) { - UniValue ret(UniValue::VOBJ); uint32_t flags; uint8_t value[IGUANA_MAXSCRIPTSIZE*8],key[IGUANA_MAXSCRIPTSIZE*8]; int32_t duration,j,height,valuesize,keylen; uint256 refpubkey; static uint256 zeroes; - if (fHelp || params.size() != 1 ) + UniValue ret(UniValue::VOBJ); uint32_t flags; uint8_t value[IGUANA_MAXSCRIPTSIZE * 8], key[IGUANA_MAXSCRIPTSIZE * 8]; int32_t duration, j, height, valuesize, keylen; uint256 refpubkey; static uint256 zeroes; + if (fHelp || params.size() != 1) throw runtime_error( "kvsearch key\n" "\nSearch for a key stored via the kvupdate command. This feature is only available for asset chains.\n" @@ -957,76 +976,79 @@ UniValue kvsearch(const UniValue& params, bool fHelp, const CPubKey& mypk) + HelpExampleRpc("kvsearch", "\"examplekey\"") ); LOCK(cs_main); - if ( (keylen= (int32_t)strlen(params[0].get_str().c_str())) > 0 ) + if ((keylen = (int32_t)strlen(params[0].get_str().c_str())) > 0) { - ret.push_back(Pair("coin",(char *)(ASSETCHAINS_SYMBOL[0] == 0 ? "KMD" : ASSETCHAINS_SYMBOL))); + ret.push_back(Pair("coin", (char *)(ASSETCHAINS_SYMBOL[0] == 0 ? "KMD" : ASSETCHAINS_SYMBOL))); ret.push_back(Pair("currentheight", (int64_t)chainActive.LastTip()->GetHeight())); - ret.push_back(Pair("key",params[0].get_str())); - ret.push_back(Pair("keylen",keylen)); - if ( keylen < sizeof(key) ) + ret.push_back(Pair("key", params[0].get_str())); + ret.push_back(Pair("keylen", keylen)); + if (keylen < sizeof(key)) { - memcpy(key,params[0].get_str().c_str(),keylen); - if ( (valuesize= komodo_kvsearch(&refpubkey,chainActive.LastTip()->GetHeight(),&flags,&height,value,key,keylen)) >= 0 ) + memcpy(key, params[0].get_str().c_str(), keylen); + if ((valuesize = komodo_kvsearch(&refpubkey, chainActive.LastTip()->GetHeight(), &flags, &height, value, key, keylen)) >= 0) { std::string val; char *valuestr; val.resize(valuesize); valuestr = (char *)val.data(); - memcpy(valuestr,value,valuesize); - if ( memcmp(&zeroes,&refpubkey,sizeof(refpubkey)) != 0 ) - ret.push_back(Pair("owner",refpubkey.GetHex())); - ret.push_back(Pair("height",height)); + memcpy(valuestr, value, valuesize); + if (memcmp(&zeroes, &refpubkey, sizeof(refpubkey)) != 0) + ret.push_back(Pair("owner", refpubkey.GetHex())); + ret.push_back(Pair("height", height)); duration = ((flags >> 2) + 1) * KOMODO_KVDURATION; - ret.push_back(Pair("expiration", (int64_t)(height+duration))); - ret.push_back(Pair("flags",(int64_t)flags)); - ret.push_back(Pair("value",val)); - ret.push_back(Pair("valuesize",valuesize)); - } else ret.push_back(Pair("error",(char *)"cant find key")); - } else ret.push_back(Pair("error",(char *)"key too big")); - } else ret.push_back(Pair("error",(char *)"null key")); + ret.push_back(Pair("expiration", (int64_t)(height + duration))); + ret.push_back(Pair("flags", (int64_t)flags)); + ret.push_back(Pair("value", val)); + ret.push_back(Pair("valuesize", valuesize)); + } + else ret.push_back(Pair("error", (char *)"cant find key")); + } + else ret.push_back(Pair("error", (char *)"key too big")); + } + else ret.push_back(Pair("error", (char *)"null key")); return ret; } UniValue minerids(const UniValue& params, bool fHelp, const CPubKey& mypk) { - uint32_t timestamp = 0; UniValue ret(UniValue::VOBJ); UniValue a(UniValue::VARR); uint8_t minerids[2000],pubkeys[65][33]; int32_t i,j,n,numnotaries,tally[129]; - if ( fHelp || params.size() != 1 ) + uint32_t timestamp = 0; UniValue ret(UniValue::VOBJ); UniValue a(UniValue::VARR); uint8_t minerids[2000], pubkeys[65][33]; int32_t i, j, n, numnotaries, tally[129]; + if (fHelp || params.size() != 1) throw runtime_error("minerids needs height\n"); LOCK(cs_main); int32_t height = atoi(params[0].get_str().c_str()); - if ( height <= 0 ) + if (height <= 0) height = chainActive.LastTip()->GetHeight(); else { CBlockIndex *pblockindex = chainActive[height]; - if ( pblockindex != 0 ) + if (pblockindex != 0) timestamp = pblockindex->GetBlockTime(); } - if ( (n= komodo_minerids(minerids,height,(int32_t)(sizeof(minerids)/sizeof(*minerids)))) > 0 ) + if ((n = komodo_minerids(minerids, height, (int32_t)(sizeof(minerids) / sizeof(*minerids)))) > 0) { - memset(tally,0,sizeof(tally)); - numnotaries = komodo_notaries(pubkeys,height,timestamp); - if ( numnotaries > 0 ) + memset(tally, 0, sizeof(tally)); + numnotaries = komodo_notaries(pubkeys, height, timestamp); + if (numnotaries > 0) { - for (i=0; i= numnotaries ) + if (minerids[i] >= numnotaries) tally[128]++; else tally[minerids[i]]++; } - for (i=0; i<64; i++) + for (i = 0; i<64; i++) { - UniValue item(UniValue::VOBJ); std::string hex,kmdaddress; char *hexstr,kmdaddr[64],*ptr; int32_t m; + UniValue item(UniValue::VOBJ); std::string hex, kmdaddress; char *hexstr, kmdaddr[64], *ptr; int32_t m; hex.resize(66); hexstr = (char *)hex.data(); - for (j=0; j<33; j++) - sprintf(&hexstr[j*2],"%02x",pubkeys[i][j]); + for (j = 0; j<33; j++) + sprintf(&hexstr[j * 2], "%02x", pubkeys[i][j]); item.push_back(Pair("notaryid", i)); - bitcoin_address(kmdaddr,60,pubkeys[i],33); + bitcoin_address(kmdaddr, 60, pubkeys[i], 33); m = (int32_t)strlen(kmdaddr); kmdaddress.resize(m); ptr = (char *)kmdaddress.data(); - memcpy(ptr,kmdaddr,m); + memcpy(ptr, kmdaddr, m); item.push_back(Pair("KMDaddress", kmdaddress)); item.push_back(Pair("pubkey", hex)); @@ -1040,55 +1062,56 @@ UniValue minerids(const UniValue& params, bool fHelp, const CPubKey& mypk) } ret.push_back(Pair("mined", a)); ret.push_back(Pair("numnotaries", numnotaries)); - } else ret.push_back(Pair("error", (char *)"couldnt extract minerids")); + } + else ret.push_back(Pair("error", (char *)"couldnt extract minerids")); return ret; } UniValue notaries(const UniValue& params, bool fHelp, const CPubKey& mypk) { - UniValue a(UniValue::VARR); uint32_t timestamp=0; UniValue ret(UniValue::VOBJ); int32_t i,j,n,m; char *hexstr; uint8_t pubkeys[64][33]; char btcaddr[64],kmdaddr[64],*ptr; - if ( fHelp || (params.size() != 1 && params.size() != 2) ) + UniValue a(UniValue::VARR); uint32_t timestamp = 0; UniValue ret(UniValue::VOBJ); int32_t i, j, n, m; char *hexstr; uint8_t pubkeys[64][33]; char btcaddr[64], kmdaddr[64], *ptr; + if (fHelp || (params.size() != 1 && params.size() != 2)) throw runtime_error("notaries height timestamp\n"); LOCK(cs_main); int32_t height = atoi(params[0].get_str().c_str()); - if ( params.size() == 2 ) + if (params.size() == 2) timestamp = (uint32_t)atol(params[1].get_str().c_str()); else timestamp = (uint32_t)time(NULL); - if ( height < 0 ) + if (height < 0) { height = chainActive.LastTip()->GetHeight(); timestamp = chainActive.LastTip()->GetBlockTime(); } - else if ( params.size() < 2 ) + else if (params.size() < 2) { CBlockIndex *pblockindex = chainActive[height]; - if ( pblockindex != 0 ) + if (pblockindex != 0) timestamp = pblockindex->GetBlockTime(); } - if ( (n= komodo_notaries(pubkeys,height,timestamp)) > 0 ) + if ((n = komodo_notaries(pubkeys, height, timestamp)) > 0) { - for (i=0; i 0 ) + if ((opretlen = komodo_pending_withdraws(opretbuf)) > 0) ret.push_back(Pair("withdraws", opretbuf)); else ret.push_back(Pair("withdraws", (char *)"")); - for (baseid=0; baseid<32; baseid++) + for (baseid = 0; baseid<32; baseid++) { UniValue item(UniValue::VOBJ); UniValue obj(UniValue::VOBJ); - if ( pax_fiatstatus(&available,&deposited,&issued,&withdrawn,&approved,&redeemed,CURRENCIES[baseid]) == 0 ) + if (pax_fiatstatus(&available, &deposited, &issued, &withdrawn, &approved, &redeemed, CURRENCIES[baseid]) == 0) { - if ( deposited != 0 || issued != 0 || withdrawn != 0 || approved != 0 || redeemed != 0 ) + if (deposited != 0 || issued != 0 || withdrawn != 0 || approved != 0 || redeemed != 0) { item.push_back(Pair("available", ValueFromAmount(available))); item.push_back(Pair("deposited", ValueFromAmount(deposited))); @@ -1126,7 +1149,7 @@ UniValue paxpending(const UniValue& params, bool fHelp, const CPubKey& mypk) item.push_back(Pair("withdrawn", ValueFromAmount(withdrawn))); item.push_back(Pair("approved", ValueFromAmount(approved))); item.push_back(Pair("redeemed", ValueFromAmount(redeemed))); - obj.push_back(Pair(CURRENCIES[baseid],item)); + obj.push_back(Pair(CURRENCIES[baseid], item)); a.push_back(obj); } } @@ -1137,354 +1160,44 @@ UniValue paxpending(const UniValue& params, bool fHelp, const CPubKey& mypk) UniValue paxprice(const UniValue& params, bool fHelp, const CPubKey& mypk) { - if ( fHelp || params.size() > 4 || params.size() < 2 ) + if (fHelp || params.size() > 4 || params.size() < 2) throw runtime_error("paxprice \"base\" \"rel\" height\n"); LOCK(cs_main); - UniValue ret(UniValue::VOBJ); uint64_t basevolume=0,relvolume,seed; + UniValue ret(UniValue::VOBJ); uint64_t basevolume = 0, relvolume, seed; std::string base = params[0].get_str(); std::string rel = params[1].get_str(); int32_t height; - if ( params.size() == 2 ) + if (params.size() == 2) height = chainActive.LastTip()->GetHeight(); else height = atoi(params[2].get_str().c_str()); //if ( params.size() == 3 || (basevolume= COIN * atof(params[3].get_str().c_str())) == 0 ) - basevolume = 100000; - relvolume = komodo_paxprice(&seed,height,(char *)base.c_str(),(char *)rel.c_str(),basevolume); + basevolume = 100000; + relvolume = komodo_paxprice(&seed, height, (char *)base.c_str(), (char *)rel.c_str(), basevolume); ret.push_back(Pair("base", base)); ret.push_back(Pair("rel", rel)); ret.push_back(Pair("height", height)); char seedstr[32]; - sprintf(seedstr,"%llu",(long long)seed); + sprintf(seedstr, "%llu", (long long)seed); ret.push_back(Pair("seed", seedstr)); - if ( height < 0 || height > chainActive.Height() ) + if (height < 0 || height > chainActive.Height()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); else { CBlockIndex *pblockindex = chainActive[height]; - if ( pblockindex != 0 ) + if (pblockindex != 0) ret.push_back(Pair("timestamp", (int64_t)pblockindex->nTime)); - if ( basevolume != 0 && relvolume != 0 ) - { - ret.push_back(Pair("price",((double)relvolume / (double)basevolume))); - ret.push_back(Pair("invprice",((double)basevolume / (double)relvolume))); - ret.push_back(Pair("basevolume",ValueFromAmount(basevolume))); - ret.push_back(Pair("relvolume",ValueFromAmount(relvolume))); - } else ret.push_back(Pair("error", "overflow or error in one or more of parameters")); - } - return ret; -} -// fills pricedata with raw price, correlated and smoothed values for numblock -/*int32_t prices_extract(int64_t *pricedata,int32_t firstheight,int32_t numblocks,int32_t ind) -{ - int32_t height,i,n,width,numpricefeeds = -1; uint64_t seed,ignore,rngval; uint32_t rawprices[1440*6],*ptr; int64_t *tmpbuf; - width = numblocks+PRICES_DAYWINDOW*2+PRICES_SMOOTHWIDTH; // need 2*PRICES_DAYWINDOW previous raw price points to calc PRICES_DAYWINDOW correlated points to calc, in turn, smoothed point - komodo_heightpricebits(&seed,rawprices,firstheight + numblocks - 1); - if ( firstheight < width ) - return(-1); - for (i=0; i2; i++,ht--) - { - if ( ht < 0 || ht > chainActive.Height() ) - throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); - else + if (basevolume != 0 && relvolume != 0) { - if ( (n= komodo_heightpricebits(0,rawprices,ht)) > 0 ) - { - if ( n != numpricefeeds ) - throw JSONRPCError(RPC_INVALID_PARAMETER, "numprices != first numprices"); - else - { - for (j=0; j= width ) - { - for (i=0; i= 0 ) - { - if ( checkprices[1] != correlated[i] ) - { - //fprintf(stderr,"ind.%d ht.%d %.8f != %.8f\n",j,nextheight-1-i,(double)checkprices[1]/COIN,(double)correlated[i]/COIN); - correlated[i] = checkprices[1]; - } - } - } - } - tmpbuf = (int64_t *)calloc(sizeof(int64_t),2*PRICES_DAYWINDOW); - for (i=0; i= 0 ) - { - if ( checkprices[2] != smoothed ) - { - fprintf(stderr,"ind.%d ht.%d %.8f != %.8f\n",j,nextheight-1-i,(double)checkprices[2]/COIN,(double)smoothed/COIN); - smoothed = checkprices[2]; - } - } - UniValue parr(UniValue::VARR); - parr.push_back(ValueFromAmount((int64_t)prices[offset] * komodo_pricemult(j))); - parr.push_back(ValueFromAmount(correlated[i])); - parr.push_back(ValueFromAmount(smoothed)); - // compare to alternate method - p.push_back(parr); - } - free(tmpbuf); - } - else - { - for (i=0; i vexpr; - SplitStr(sexpr, vexpr); - - // debug print parsed strings: - std::cerr << "parsed synthetic: "; - for (auto s : vexpr) - std::cerr << s << " "; - std::cerr << std::endl; - - return PricesBet(txfee, amount, leverage, vexpr); -} - -// pricesaddfunding rpc implementation -UniValue pricesaddfunding(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - if (fHelp || params.size() != 2) - throw runtime_error("pricesaddfunding bettxid amount\n" - "where amount is in coins\n"); - LOCK(cs_main); - UniValue ret(UniValue::VOBJ); - - if (ASSETCHAINS_CBOPRET == 0) - throw JSONRPCError(RPC_INVALID_PARAMETER, "only -ac_cbopret chains have prices"); - - CAmount txfee = 10000; - uint256 bettxid = Parseuint256(params[0].get_str().c_str()); - if (bettxid.IsNull()) - throw runtime_error("invalid bettxid\n"); - - CAmount amount = atof(params[1].get_str().c_str()) * COIN; - if (amount <= 0) - throw runtime_error("invalid amount\n"); - - return PricesAddFunding(txfee, bettxid, amount); -} - -// rpc pricessetcostbasis implementation -UniValue pricessetcostbasis(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - if (fHelp || params.size() != 1) - throw runtime_error("pricessetcostbasis bettxid\n"); - LOCK(cs_main); - UniValue ret(UniValue::VOBJ); - - if (ASSETCHAINS_CBOPRET == 0) - throw JSONRPCError(RPC_INVALID_PARAMETER, "only -ac_cbopret chains have prices"); - - uint256 bettxid = Parseuint256(params[0].get_str().c_str()); - if (bettxid.IsNull()) - throw runtime_error("invalid bettxid\n"); - - int64_t txfee = 10000; - - return PricesSetcostbasis(txfee, bettxid); -} - -// pricescashout rpc implementation -UniValue pricescashout(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - if (fHelp || params.size() != 1) - throw runtime_error("pricescashout bettxid\n"); - LOCK(cs_main); - UniValue ret(UniValue::VOBJ); - - if (ASSETCHAINS_CBOPRET == 0) - throw JSONRPCError(RPC_INVALID_PARAMETER, "only -ac_cbopret chains have prices"); - - uint256 bettxid = Parseuint256(params[0].get_str().c_str()); - if (bettxid.IsNull()) - throw runtime_error("invalid bettxid\n"); - - int64_t txfee = 10000; - - return PricesCashout(txfee, bettxid); -} - -// pricesrekt rpc implementation -UniValue pricesrekt(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - if (fHelp || params.size() != 2) - throw runtime_error("pricesrekt bettxid height\n"); - LOCK(cs_main); - UniValue ret(UniValue::VOBJ); - - if (ASSETCHAINS_CBOPRET == 0) - throw JSONRPCError(RPC_INVALID_PARAMETER, "only -ac_cbopret chains have prices"); - - uint256 bettxid = Parseuint256(params[0].get_str().c_str()); - if (bettxid.IsNull()) - throw runtime_error("invalid bettxid\n"); - - int32_t height = atoi(params[0].get_str().c_str()); - - int64_t txfee = 10000; - - return PricesRekt(txfee, bettxid, height); -} - -// pricesrekt rpc implementation -UniValue pricesgetorderbook(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - if (fHelp || params.size() != 0) - throw runtime_error("pricesgetorderbook\n"); - LOCK(cs_main); - UniValue ret(UniValue::VOBJ); - - if (ASSETCHAINS_CBOPRET == 0) - throw JSONRPCError(RPC_INVALID_PARAMETER, "only -ac_cbopret chains have prices"); - - return PricesGetOrderbook(); -} - -// pricesrekt rpc implementation -UniValue pricesrefillfund(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - if (fHelp || params.size() != 1) - throw runtime_error("pricesrefillfund amount\n"); - LOCK(cs_main); - UniValue ret(UniValue::VOBJ); - - if (ASSETCHAINS_CBOPRET == 0) - throw JSONRPCError(RPC_INVALID_PARAMETER, "only -ac_cbopret chains have prices"); - - CAmount amount = atof(params[0].get_str().c_str()) * COIN; - - return PricesRefillFund(amount); -} - - UniValue gettxout(const UniValue& params, bool fHelp, const CPubKey& mypk) { if (fHelp || params.size() < 2 || params.size() > 3) @@ -1542,11 +1255,12 @@ UniValue gettxout(const UniValue& params, bool fHelp, const CPubKey& mypk) if (!view.GetCoins(hash, coins)) return NullUniValue; mempool.pruneSpent(hash, coins); // TODO: this should be done by the CCoinsViewMemPool - } else { + } + else { if (!pcoinsTip->GetCoins(hash, coins)) return NullUniValue; } - if (n<0 || (unsigned int)n>=coins.vout.size() || coins.vout[n].IsNull()) + if (n<0 || (unsigned int)n >= coins.vout.size() || coins.vout[n].IsNull()) return NullUniValue; BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock()); @@ -1555,13 +1269,14 @@ UniValue gettxout(const UniValue& params, bool fHelp, const CPubKey& mypk) if ((unsigned int)coins.nHeight == MEMPOOL_HEIGHT) { ret.push_back(Pair("confirmations", 0)); ret.push_back(Pair("rawconfirmations", 0)); - } else { - ret.push_back(Pair("confirmations", komodo_dpowconfs(coins.nHeight,pindex->GetHeight() - coins.nHeight + 1))); + } + else { + ret.push_back(Pair("confirmations", komodo_dpowconfs(coins.nHeight, pindex->GetHeight() - coins.nHeight + 1))); ret.push_back(Pair("rawconfirmations", pindex->GetHeight() - coins.nHeight + 1)); } ret.push_back(Pair("value", ValueFromAmount(coins.vout[n].nValue))); uint64_t interest; int32_t txheight; uint32_t locktime; - if ( (interest= komodo_accrued_interest(&txheight,&locktime,hash,n,coins.nHeight,coins.vout[n].nValue,(int32_t)pindex->GetHeight())) != 0 ) + if ((interest = komodo_accrued_interest(&txheight, &locktime, hash, n, coins.nHeight, coins.vout[n].nValue, (int32_t)pindex->GetHeight())) != 0) ret.push_back(Pair("interest", ValueFromAmount(interest))); UniValue o(UniValue::VOBJ); ScriptPubKeyToJSON(coins.vout[n].scriptPubKey, o, true); @@ -1637,9 +1352,9 @@ static UniValue NetworkUpgradeDesc(const Consensus::Params& consensusParams, Con rv.push_back(Pair("name", upgrade.strName)); rv.push_back(Pair("activationheight", consensusParams.vUpgrades[idx].nActivationHeight)); switch (NetworkUpgradeState(height, consensusParams, idx)) { - case UPGRADE_DISABLED: rv.push_back(Pair("status", "disabled")); break; - case UPGRADE_PENDING: rv.push_back(Pair("status", "pending")); break; - case UPGRADE_ACTIVE: rv.push_back(Pair("status", "active")); break; + case UPGRADE_DISABLED: rv.push_back(Pair("status", "disabled")); break; + case UPGRADE_PENDING: rv.push_back(Pair("status", "pending")); break; + case UPGRADE_ACTIVE: rv.push_back(Pair("status", "active")); break; } rv.push_back(Pair("info", upgrade.strInfo)); return rv; @@ -1712,43 +1427,45 @@ UniValue getblockchaininfo(const UniValue& params, bool fHelp, const CPubKey& my LOCK(cs_main); double progress; - if ( ASSETCHAINS_SYMBOL[0] == 0 ) { + if (ASSETCHAINS_SYMBOL[0] == 0) { progress = Checkpoints::GuessVerificationProgress(Params().Checkpoints(), chainActive.LastTip()); - } else { + } + else { int32_t longestchain = KOMODO_LONGESTCHAIN;//komodo_longestchain(); - progress = (longestchain > 0 ) ? (double) chainActive.Height() / longestchain : 1.0; + progress = (longestchain > 0) ? (double)chainActive.Height() / longestchain : 1.0; } UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("chain", Params().NetworkIDString())); - obj.push_back(Pair("blocks", (int)chainActive.Height())); - obj.push_back(Pair("synced", KOMODO_INSYNC!=0)); - obj.push_back(Pair("headers", pindexBestHeader ? pindexBestHeader->GetHeight() : -1)); - obj.push_back(Pair("bestblockhash", chainActive.LastTip()->GetBlockHash().GetHex())); - obj.push_back(Pair("difficulty", (double)GetNetworkDifficulty())); - obj.push_back(Pair("verificationprogress", progress)); - obj.push_back(Pair("chainwork", chainActive.LastTip()->chainPower.chainWork.GetHex())); + obj.push_back(Pair("chain", Params().NetworkIDString())); + obj.push_back(Pair("blocks", (int)chainActive.Height())); + obj.push_back(Pair("synced", KOMODO_INSYNC != 0)); + obj.push_back(Pair("headers", pindexBestHeader ? pindexBestHeader->GetHeight() : -1)); + obj.push_back(Pair("bestblockhash", chainActive.LastTip()->GetBlockHash().GetHex())); + obj.push_back(Pair("difficulty", (double)GetNetworkDifficulty())); + obj.push_back(Pair("verificationprogress", progress)); + obj.push_back(Pair("chainwork", chainActive.LastTip()->chainPower.chainWork.GetHex())); if (ASSETCHAINS_LWMAPOS) { - obj.push_back(Pair("chainstake", chainActive.LastTip()->chainPower.chainStake.GetHex())); + obj.push_back(Pair("chainstake", chainActive.LastTip()->chainPower.chainStake.GetHex())); } - obj.push_back(Pair("pruned", fPruneMode)); - - SproutMerkleTree tree; - pcoinsTip->GetSproutAnchorAt(pcoinsTip->GetBestAnchor(SPROUT), tree); - obj.push_back(Pair("commitments", static_cast(tree.size()))); - + obj.push_back(Pair("pruned", fPruneMode)); CBlockIndex* tip = chainActive.LastTip(); - UniValue valuePools(UniValue::VARR); - valuePools.push_back(ValuePoolDesc("sprout", tip->nChainSproutValue, boost::none)); - valuePools.push_back(ValuePoolDesc("sapling", tip->nChainSaplingValue, boost::none)); - obj.push_back(Pair("valuePools", valuePools)); - + if ( KOMODO_NSPV_SUPERLITE == 0 ) + { + SproutMerkleTree tree; + pcoinsTip->GetSproutAnchorAt(pcoinsTip->GetBestAnchor(SPROUT), tree); + obj.push_back(Pair("commitments", static_cast(tree.size()))); + + UniValue valuePools(UniValue::VARR); + valuePools.push_back(ValuePoolDesc("sprout", tip->nChainSproutValue, boost::none)); + valuePools.push_back(ValuePoolDesc("sapling", tip->nChainSaplingValue, boost::none)); + obj.push_back(Pair("valuePools", valuePools)); + } const Consensus::Params& consensusParams = Params().GetConsensus(); UniValue softforks(UniValue::VARR); softforks.push_back(SoftForkDesc("bip34", 2, tip, consensusParams)); softforks.push_back(SoftForkDesc("bip66", 3, tip, consensusParams)); softforks.push_back(SoftForkDesc("bip65", 4, tip, consensusParams)); - obj.push_back(Pair("softforks", softforks)); + obj.push_back(Pair("softforks", softforks)); UniValue upgrades(UniValue::VOBJ); for (int i = Consensus::UPGRADE_OVERWINTER; i < Consensus::MAX_NETWORK_UPGRADES; i++) { @@ -1767,7 +1484,7 @@ UniValue getblockchaininfo(const UniValue& params, bool fHelp, const CPubKey& my while (block && block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA)) block = block->pprev; - obj.push_back(Pair("pruneheight", block->GetHeight())); + obj.push_back(Pair("pruneheight", block->GetHeight())); } return obj; } @@ -1778,10 +1495,10 @@ struct CompareBlocksByHeight bool operator()(const CBlockIndex* a, const CBlockIndex* b) const { /* Make sure that unequal blocks with the same height do not compare - equal. Use the pointers themselves to make a distinction. */ + equal. Use the pointers themselves to make a distinction. */ if (a->GetHeight() != b->GetHeight()) - return (a->GetHeight() > b->GetHeight()); + return (a->GetHeight() > b->GetHeight()); return a < b; } @@ -1825,13 +1542,13 @@ UniValue getchaintips(const UniValue& params, bool fHelp, const CPubKey& mypk) LOCK(cs_main); /* Build up a list of chain tips. We start with the list of all - known blocks, and successively remove blocks that appear as pprev - of another block. */ + known blocks, and successively remove blocks that appear as pprev + of another block. */ /*static pthread_mutex_t mutex; static int32_t didinit; if ( didinit == 0 ) { - pthread_mutex_init(&mutex,NULL); - didinit = 1; + pthread_mutex_init(&mutex,NULL); + didinit = 1; } pthread_mutex_lock(&mutex);*/ std::set setTips; @@ -1841,18 +1558,18 @@ UniValue getchaintips(const UniValue& params, bool fHelp, const CPubKey& mypk) n++; setTips.insert(item.second); } - fprintf(stderr,"iterations getchaintips %d\n",n); + fprintf(stderr, "iterations getchaintips %d\n", n); n = 0; BOOST_FOREACH(const PAIRTYPE(const uint256, CBlockIndex*)& item, mapBlockIndex) { - const CBlockIndex* pprev=0; + const CBlockIndex* pprev = 0; n++; - if ( item.second != 0 ) + if (item.second != 0) pprev = item.second->pprev; if (pprev) setTips.erase(pprev); } - fprintf(stderr,"iterations getchaintips %d\n",n); + fprintf(stderr, "iterations getchaintips %d\n", n); //pthread_mutex_unlock(&mutex); // Always report the currently active tip. @@ -1861,40 +1578,45 @@ UniValue getchaintips(const UniValue& params, bool fHelp, const CPubKey& mypk) /* Construct the output array. */ UniValue res(UniValue::VARR); const CBlockIndex *forked; BOOST_FOREACH(const CBlockIndex* block, setTips) + { + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("height", block->GetHeight())); + obj.push_back(Pair("hash", block->phashBlock->GetHex())); + forked = chainActive.FindFork(block); + if (forked != 0) { - UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("height", block->GetHeight())); - obj.push_back(Pair("hash", block->phashBlock->GetHex())); - forked = chainActive.FindFork(block); - if ( forked != 0 ) - { - const int branchLen = block->GetHeight() - forked->GetHeight(); - obj.push_back(Pair("branchlen", branchLen)); - - string status; - if (chainActive.Contains(block)) { - // This block is part of the currently active chain. - status = "active"; - } else if (block->nStatus & BLOCK_FAILED_MASK) { - // This block or one of its ancestors is invalid. - status = "invalid"; - } else if (block->nChainTx == 0) { - // This block cannot be connected because full block data for it or one of its parents is missing. - status = "headers-only"; - } else if (block->IsValid(BLOCK_VALID_SCRIPTS)) { - // This block is fully validated, but no longer part of the active chain. It was probably the active block once, but was reorganized. - status = "valid-fork"; - } else if (block->IsValid(BLOCK_VALID_TREE)) { - // The headers for this block are valid, but it has not been validated. It was probably never part of the most-work chain. - status = "valid-headers"; - } else { - // No clue. - status = "unknown"; - } - obj.push_back(Pair("status", status)); + const int branchLen = block->GetHeight() - forked->GetHeight(); + obj.push_back(Pair("branchlen", branchLen)); + + string status; + if (chainActive.Contains(block)) { + // This block is part of the currently active chain. + status = "active"; + } + else if (block->nStatus & BLOCK_FAILED_MASK) { + // This block or one of its ancestors is invalid. + status = "invalid"; + } + else if (block->nChainTx == 0) { + // This block cannot be connected because full block data for it or one of its parents is missing. + status = "headers-only"; + } + else if (block->IsValid(BLOCK_VALID_SCRIPTS)) { + // This block is fully validated, but no longer part of the active chain. It was probably the active block once, but was reorganized. + status = "valid-fork"; } - res.push_back(obj); + else if (block->IsValid(BLOCK_VALID_TREE)) { + // The headers for this block are valid, but it has not been validated. It was probably never part of the most-work chain. + status = "valid-headers"; + } + else { + // No clue. + status = "unknown"; + } + obj.push_back(Pair("status", status)); } + res.push_back(obj); + } return res; } @@ -1902,14 +1624,14 @@ UniValue getchaintips(const UniValue& params, bool fHelp, const CPubKey& mypk) UniValue mempoolInfoToJSON() { UniValue ret(UniValue::VOBJ); - ret.push_back(Pair("size", (int64_t) mempool.size())); - ret.push_back(Pair("bytes", (int64_t) mempool.GetTotalTxSize())); - ret.push_back(Pair("usage", (int64_t) mempool.DynamicMemoryUsage())); + ret.push_back(Pair("size", (int64_t)mempool.size())); + ret.push_back(Pair("bytes", (int64_t)mempool.GetTotalTxSize())); + ret.push_back(Pair("usage", (int64_t)mempool.DynamicMemoryUsage())); if (Params().NetworkIDString() == "regtest") { ret.push_back(Pair("fullyNotified", mempool.IsFullyNotified())); } - + return ret; } @@ -1944,12 +1666,12 @@ UniValue getchaintxstats(const UniValue& params, bool fHelp, const CPubKey& mypk { if (fHelp || params.size() > 2) throw runtime_error( - "getchaintxstats\n" - "\nCompute statistics about the total number and rate of transactions in the chain.\n" - "\nArguments:\n" - "1. nblocks (numeric, optional) Number of blocks in averaging window.\n" - "2. blockhash (string, optional) The hash of the block which ends the window.\n" - "\nResult:\n" + "getchaintxstats\n" + "\nCompute statistics about the total number and rate of transactions in the chain.\n" + "\nArguments:\n" + "1. nblocks (numeric, optional) Number of blocks in averaging window.\n" + "2. blockhash (string, optional) The hash of the block which ends the window.\n" + "\nResult:\n" "{\n" " \"time\": xxxxx, (numeric) The timestamp for the final block in the window in UNIX format.\n" " \"txcount\": xxxxx, (numeric) The total number of transactions in the chain up to that point.\n" @@ -1970,7 +1692,8 @@ UniValue getchaintxstats(const UniValue& params, bool fHelp, const CPubKey& mypk if (params[1].isNull()) { LOCK(cs_main); pindex = chainActive.Tip(); - } else { + } + else { uint256 hash(ParseHashV(params[1], "blockhash")); LOCK(cs_main); pindex = LookupBlockIndex(hash); @@ -1986,7 +1709,8 @@ UniValue getchaintxstats(const UniValue& params, bool fHelp, const CPubKey& mypk if (params[0].isNull()) { blockcount = std::max(0, std::min(blockcount, pindex->GetHeight() - 1)); - } else { + } + else { blockcount = params[0].get_int(); if (blockcount < 0 || (blockcount > 0 && blockcount >= pindex->GetHeight())) { @@ -2042,7 +1766,7 @@ UniValue invalidateblock(const UniValue& params, bool fHelp, const CPubKey& mypk } if (state.IsValid()) { - ActivateBestChain(true,state); + ActivateBestChain(true, state); } if (!state.IsValid()) { @@ -2081,7 +1805,7 @@ UniValue reconsiderblock(const UniValue& params, bool fHelp, const CPubKey& mypk } if (state.IsValid()) { - ActivateBestChain(true,state); + ActivateBestChain(true, state); } if (!state.IsValid()) { @@ -2094,24 +1818,24 @@ UniValue reconsiderblock(const UniValue& params, bool fHelp, const CPubKey& mypk static const CRPCCommand commands[] = { // category name actor (function) okSafeMode // --------------------- ------------------------ ----------------------- ---------- - { "blockchain", "getblockchaininfo", &getblockchaininfo, true }, - { "blockchain", "getbestblockhash", &getbestblockhash, true }, - { "blockchain", "getblockcount", &getblockcount, true }, - { "blockchain", "getblock", &getblock, true }, - { "blockchain", "getblockhash", &getblockhash, true }, - { "blockchain", "getblockheader", &getblockheader, true }, - { "blockchain", "getchaintips", &getchaintips, true }, - { "blockchain", "getchaintxstats", &getchaintxstats, true }, - { "blockchain", "getdifficulty", &getdifficulty, true }, - { "blockchain", "getmempoolinfo", &getmempoolinfo, true }, - { "blockchain", "getrawmempool", &getrawmempool, true }, - { "blockchain", "gettxout", &gettxout, true }, - { "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, true }, - { "blockchain", "verifychain", &verifychain, true }, - - /* Not shown in help */ - { "hidden", "invalidateblock", &invalidateblock, true }, - { "hidden", "reconsiderblock", &reconsiderblock, true }, + { "blockchain", "getblockchaininfo", &getblockchaininfo, true }, +{ "blockchain", "getbestblockhash", &getbestblockhash, true }, +{ "blockchain", "getblockcount", &getblockcount, true }, +{ "blockchain", "getblock", &getblock, true }, +{ "blockchain", "getblockhash", &getblockhash, true }, +{ "blockchain", "getblockheader", &getblockheader, true }, +{ "blockchain", "getchaintips", &getchaintips, true }, +{ "blockchain", "getchaintxstats", &getchaintxstats, true }, +{ "blockchain", "getdifficulty", &getdifficulty, true }, +{ "blockchain", "getmempoolinfo", &getmempoolinfo, true }, +{ "blockchain", "getrawmempool", &getrawmempool, true }, +{ "blockchain", "gettxout", &gettxout, true }, +{ "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, true }, +{ "blockchain", "verifychain", &verifychain, true }, + +/* Not shown in help */ +{ "hidden", "invalidateblock", &invalidateblock, true }, +{ "hidden", "reconsiderblock", &reconsiderblock, true }, }; void RegisterBlockchainRPCCommands(CRPCTable &tableRPC) diff --git a/src/rpc/ccutilsrpc.cpp b/src/rpc/ccutilsrpc.cpp new file mode 100644 index 00000000000..13daa023eea --- /dev/null +++ b/src/rpc/ccutilsrpc.cpp @@ -0,0 +1,106 @@ +/****************************************************************************** + * Copyright 2014-2020 The SuperNET Developers. * + * * + * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * + * the top-level directory of this distribution for the individual copyright * + * holder information and the developer policies on copyright and licensing. * + * * + * Unless otherwise agreed in a custom licensing agreement, no part of the * + * SuperNET software, including this file may be copied, modified, propagated * + * or distributed except according to the terms contained in the LICENSE file * + * * + * Removal or modification of this copyright notice is prohibited. * + * * + ******************************************************************************/ + +#include +#include +#include +#include "univalue.h" +#include "amount.h" +#include "rpc/server.h" +#include "rpc/protocol.h" + +//#include "../wallet/crypter.h" +//#include "../wallet/rpcwallet.h" + +#include "../txdb.h" +#include "sync_ext.h" +#include "../main.h" +#include "../cc/CCinclude.h" + +using namespace std; + +UniValue listccunspents(const UniValue& params, bool fHelp, const CPubKey& mypk) +{ + UniValue resarray(UniValue::VARR); + bool fUnspentCCIndexTmp; + + if (fHelp || (params.size() < 1 || params.size() > 2)) + throw runtime_error("listccunspents ccadress [creationid]\n"); + + pblocktree->ReadFlag("unspentccindex", fUnspentCCIndexTmp); + if (!fUnspentCCIndexTmp) + throw runtime_error("unspent cc index not supported\n"); + + std::vector > unspentOutputs, unspentOutputsMem; + + std::string ccaddr = params[0].get_str(); + uint256 creationid; + if (params.size() == 2) + creationid = Parseuint256(params[1].get_str().c_str()); + + auto addUniElem = [&](const std::pair &o, uint256 spenttxid, int32_t spentvin) + { + UniValue elem(UniValue::VOBJ); + elem.push_back(Pair("hashBytes", o.first.hashBytes.GetHex())); + elem.push_back(Pair("creationId", o.first.creationid.GetHex())); + elem.push_back(Pair("txhash", o.first.txhash.GetHex())); + elem.push_back(Pair("index", (int64_t)o.first.index)); + elem.push_back(Pair("satoshis", o.second.satoshis)); + elem.push_back(Pair("blockHeight", o.second.blockHeight)); + elem.push_back(Pair("evalcode", HexStr(std::string(1, o.second.evalcode)))); + elem.push_back(Pair("funcid", std::string(1, o.second.funcid))); + elem.push_back(Pair("version", (int)o.second.version)); + elem.push_back(Pair("scriptPubKey", o.second.scriptPubKey.ToString())); + elem.push_back(Pair("opreturn", o.second.opreturn.ToString())); + if (!spenttxid.IsNull()) { + elem.push_back(Pair("SpentTxid", spenttxid.GetHex())); + elem.push_back(Pair("SpentVin", (int64_t)spentvin)); + } + resarray.push_back(elem); + }; + + SetCCunspentsCCIndex(unspentOutputs, ccaddr.c_str(), creationid); + std::cerr << " non mempool unspentOutputs.size=" << unspentOutputs.size() << std::endl; + for( auto const &o : unspentOutputs) { + uint256 spenttxid; + int32_t spentvin; + myIsutxo_spentinmempool(spenttxid, spentvin, o.first.txhash, o.first.index); // SetCCunspentsCCIndex does not check spent in mempool + addUniElem(o, spenttxid, spentvin); + } + + AddCCunspentsCCIndexMempool(unspentOutputsMem, ccaddr.c_str(), creationid); + std::cerr << __func__ << " with mempool unspentOutputs.size=" << unspentOutputs.size() << std::endl; + + for( auto const &o : unspentOutputsMem) { + addUniElem(o, zeroid, 0); + } + return resarray; +} + + + +static const CRPCCommand commands[] = +{ // category name actor (function) okSafeMode + // -------------- ------------------------ ----------------------- ---------- + // Marmara + // tokens & assets + { "ccutils", "listccunspents", &listccunspents, true } +}; + +void RegisterCCUtilsRPCCommands(CRPCTable &tableRPC) +{ + for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) + tableRPC.appendCommand(commands[vcidx].name, &commands[vcidx]); +} diff --git a/src/rpc/register.h b/src/rpc/register.h index 245f76e2220..69f6566c43e 100644 --- a/src/rpc/register.h +++ b/src/rpc/register.h @@ -34,9 +34,12 @@ void RegisterMiscRPCCommands(CRPCTable &tableRPC); void RegisterMiningRPCCommands(CRPCTable &tableRPC); /** Register raw transaction RPC commands */ void RegisterRawTransactionRPCCommands(CRPCTable &tableRPC); - -/** Register test transaction RPC commands */ -void RegisterTesttransactionsRPCCommands(CRPCTable &tableRPC); +// tokens cc rpcs: +void RegisterTokensRPCCommands(CRPCTable &tableRPC); +// marmara cc rpcs: +void RegisterMarmaraRPCCommands(CRPCTable &tableRPC); +// cc utils rpcs: +void RegisterCCUtilsRPCCommands(CRPCTable &tableRPC); static inline void RegisterAllCoreRPCCommands(CRPCTable &tableRPC) @@ -46,9 +49,9 @@ static inline void RegisterAllCoreRPCCommands(CRPCTable &tableRPC) RegisterMiscRPCCommands(tableRPC); RegisterMiningRPCCommands(tableRPC); RegisterRawTransactionRPCCommands(tableRPC); -#ifdef TESTMODE - RegisterTesttransactionsRPCCommands(tableRPC); -#endif + RegisterTokensRPCCommands(tableRPC); + RegisterMarmaraRPCCommands(tableRPC); + RegisterCCUtilsRPCCommands(tableRPC); } #endif diff --git a/src/rpc/tokensrpc.cpp b/src/rpc/tokensrpc.cpp index 12ca0a5527f..6dd7a8131d1 100644 --- a/src/rpc/tokensrpc.cpp +++ b/src/rpc/tokensrpc.cpp @@ -75,25 +75,27 @@ UniValue tokenv2address(const UniValue& params, bool fHelp, const CPubKey& mypk) return CCaddress(cp, "Tokensv2", pubkey, true); } -template -static UniValue tokenlist(const std::string& name, const UniValue& params, bool fHelp, const CPubKey& remotepk) +UniValue tokenlist(const UniValue& params, bool fHelp, const CPubKey& remotepk) { uint256 tokenid; - if ( fHelp || params.size() > 0 ) - throw runtime_error(name + "\n"); - if ( ensure_CCrequirements(V::EvalCode()) < 0 ) + if (fHelp || params.size() > 0) + throw runtime_error("tokenlist\n"); + + if (ensure_CCrequirements(EVAL_TOKENS) < 0) throw runtime_error(CC_REQUIREMENTS_MSG); - return(TokenList()); -} -UniValue tokenlist(const UniValue& params, bool fHelp, const CPubKey& remotepk) -{ - return tokenlist("tokenlist", params, fHelp, remotepk); + return TokenList(); } UniValue tokenv2list(const UniValue& params, bool fHelp, const CPubKey& remotepk) { - return tokenlist("tokenv2list", params, fHelp, remotepk); -} + uint256 tokenid; + if (fHelp || params.size() > 0) + throw runtime_error("tokenv2list\n"); + + if (ensure_CCrequirements(EVAL_TOKENSV2) < 0) + throw runtime_error(CC_REQUIREMENTS_MSG); + + return TokenV2List();} template static UniValue tokeninfo(const std::string& name, const UniValue& params, bool fHelp, const CPubKey& remotepk) @@ -195,7 +197,7 @@ static UniValue tokenbalance(const std::string& name, const UniValue& params, bo char destaddr[KOMODO_ADDRESS_BUFSIZE]; result.push_back(Pair("result", "success")); - cp = CCinit(&C, EVAL_TOKENS); + cp = CCinit(&C, V::EvalCode()); if (GetCCaddress(cp, destaddr, pubkey2pk(vpubkey), V::IsMixed()) != 0) result.push_back(Pair("CCaddress", destaddr)); diff --git a/src/txdb.cpp b/src/txdb.cpp index 82885044e4e..9e532840826 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -56,6 +56,9 @@ static const char DB_FLAG = 'F'; static const char DB_REINDEX_FLAG = 'R'; static const char DB_LAST_BLOCK = 'l'; +// cc module outputs index with opdrop or opreturn data +static const char DB_ADDRESSUNSPENT_CC_INDEX = 'O'; + CCoinsViewDB::CCoinsViewDB(std::string dbName, size_t nCacheSize, bool fMemory, bool fWipe) : db(GetDataDir() / dbName, nCacheSize, fMemory, fWipe) { } @@ -744,3 +747,60 @@ bool CBlockTreeDB::LoadBlockIndexGuts() return true; } + +// update or erase entry for unspent cc index +bool CBlockTreeDB::UpdateUnspentCCIndex(const std::vector >&vect) { + CDBBatch batch(*this); + for (std::vector >::const_iterator it=vect.begin(); it!=vect.end(); it++) { + if (it->second.IsNull()) { + batch.Erase(make_pair(DB_ADDRESSUNSPENT_CC_INDEX, it->first)); + } else { + batch.Write(make_pair(DB_ADDRESSUNSPENT_CC_INDEX, it->first), it->second); + } + } + return WriteBatch(batch); +} + +// read unspent cc index by address or address+creationid key +bool CBlockTreeDB::ReadUnspentCCIndex(uint160 addressHash, uint256 creationid, + std::vector > &unspentOutputs, int32_t beginHeight, int32_t endHeight, int64_t maxOutputs) { + + boost::scoped_ptr pcursor(NewIterator()); + + if (creationid.IsNull()) + pcursor->Seek(make_pair(DB_ADDRESSUNSPENT_CC_INDEX, CUnspentCCIndexKeyAddr(addressHash))); //search first address + else + pcursor->Seek(make_pair(DB_ADDRESSUNSPENT_CC_INDEX, CUnspentCCIndexKeyCreationId(addressHash, creationid))); // search first address+creationId + + int64_t n = 0; + while (pcursor->Valid() && (maxOutputs <= 0 || n < maxOutputs)) { + boost::this_thread::interruption_point(); + try { + CDataStream ssKey(SER_DISK, CLIENT_VERSION); + pair keyObj; + pcursor->GetKey(keyObj); + char chType = keyObj.first; + CUnspentCCIndexKey indexKey = keyObj.second; + + if (chType == DB_ADDRESSUNSPENT_CC_INDEX && indexKey.hashBytes == addressHash && (creationid.IsNull() || indexKey.creationid == creationid)) { + try { + CUnspentCCIndexValue ccValue; + pcursor->GetValue(ccValue); + if ((beginHeight < 0 || ccValue.blockHeight >= beginHeight) && (endHeight < 0 || ccValue.blockHeight <= endHeight)) { + unspentOutputs.push_back(make_pair(indexKey, ccValue)); + n ++; + } + pcursor->Next(); + } catch (const std::exception& e) { + return error("failed to get unspent cc index value"); + } + } + else { + break; + } + } catch (const std::exception& e) { + break; + } + } + return true; +} diff --git a/src/txdb.h b/src/txdb.h index 195f4c183c6..38f53e64ff8 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -23,6 +23,7 @@ #include "coins.h" #include "dbwrapper.h" +#include "unspentccindex.h" #include #include @@ -117,6 +118,10 @@ class CBlockTreeDB : public CDBWrapper bool blockOnchainActive(const uint256 &hash); UniValue Snapshot(int top); bool Snapshot2(std::map &addressAmounts, UniValue *ret); + + bool UpdateUnspentCCIndex(const std::vector >&vect); + bool ReadUnspentCCIndex(uint160 addressHash, uint256 creationid, + std::vector > &vect, int32_t beginHeight, int32_t endHeight, int64_t maxOutputs); }; #endif // BITCOIN_TXDB_H diff --git a/src/txmempool.cpp b/src/txmempool.cpp index a60a8103a65..540493c4737 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -33,6 +33,8 @@ #include "version.h" #define _COINBASE_MATURITY 100 +#include "cc/CCinclude.h" + using namespace std; CTxMemPoolEntry::CTxMemPoolEntry(): @@ -159,26 +161,14 @@ void CTxMemPool::addAddressIndex(const CTxMemPoolEntry &entry, const CCoinsViewC vector> vSols; txnouttype txType = TX_PUBKEYHASH; - int keyType = 1; - CTxDestination vDest; - if (Solver(prevout.scriptPubKey, txType, vSols) || ExtractDestination(prevout.scriptPubKey, vDest)) + int keyType = GetAddressType(prevout.scriptPubKey, vDest, txType, vSols); + if (keyType != 0) { - if (vDest.which()) - { - uint160 hashBytes; - if (CBitcoinAddress(vDest).GetIndexKey(hashBytes, keyType, prevout.scriptPubKey.IsPayToCryptoCondition())) - { - vSols.push_back(vector(hashBytes.begin(), hashBytes.end())); - } - } - if (txType == TX_SCRIPTHASH) - { - keyType = 2; - } for (auto addr : vSols) { - CMempoolAddressDeltaKey key(keyType, addr.size() == 20 ? uint160(addr) : Hash160(addr), txhash, j, true); + // add index entry for vins: + CMempoolAddressDeltaKey key(keyType, addr.size() == 20 ? uint160(addr) : Hash160(addr), txhash, j, true); CMempoolAddressDelta delta(entry.GetTime(), prevout.nValue * -1, input.prevout.hash, input.prevout.n); mapAddress.insert(make_pair(key, delta)); inserted.push_back(key); @@ -192,24 +182,12 @@ void CTxMemPool::addAddressIndex(const CTxMemPoolEntry &entry, const CCoinsViewC vector> vSols; CTxDestination vDest; txnouttype txType = TX_PUBKEYHASH; - int keyType = 1; - if ((Solver(out.scriptPubKey, txType, vSols) || ExtractDestination(out.scriptPubKey, vDest)) && txType != TX_MULTISIG) + int keyType = GetAddressType(out.scriptPubKey, vDest, txType, vSols); + if (keyType != 0 && txType != TX_MULTISIG) { - // if we failed to solve, and got a vDest, assume P2PKH or P2PK address returned - if (vDest.which()) - { - uint160 hashBytes; - if (CBitcoinAddress(vDest).GetIndexKey(hashBytes, keyType, out.scriptPubKey.IsPayToCryptoCondition())) - { - vSols.push_back(vector(hashBytes.begin(), hashBytes.end())); - } - } - else if (txType == TX_SCRIPTHASH) - { - keyType = 2; - } for (auto addr : vSols) { + // add index entry for vouts: CMempoolAddressDeltaKey key(keyType, addr.size() == 20 ? uint160(addr) : Hash160(addr), txhash, k, 0); mapAddress.insert(make_pair(key, CMempoolAddressDelta(entry.GetTime(), out.nValue))); inserted.push_back(key); @@ -266,28 +244,13 @@ void CTxMemPool::addSpentIndex(const CTxMemPoolEntry &entry, const CCoinsViewCac vector> vSols; CTxDestination vDest; txnouttype txType = TX_PUBKEYHASH; - int keyType = 1; - // some non-standard types, like time lock coinbases, don't solve, but do extract - if ((Solver(prevout.scriptPubKey, txType, vSols) || ExtractDestination(prevout.scriptPubKey, vDest)) && txType != TX_MULTISIG) + int keyType = GetAddressType(prevout.scriptPubKey, vDest, txType, vSols); + if (keyType != 0 && txType != TX_MULTISIG) { - // if we failed to solve, and got a vDest, assume P2PKH or P2PK address returned - if (vDest.which()) - { - CKeyID kid; - if (CBitcoinAddress(vDest).GetKeyID(kid)) - { - vSols.push_back(vector(kid.begin(), kid.end())); - } - } - else if (txType == TX_SCRIPTHASH) - { - keyType = 2; - } for (auto addr : vSols) { CSpentIndexKey key = CSpentIndexKey(input.prevout.hash, input.prevout.n); CSpentIndexValue value = CSpentIndexValue(txhash, j, -1, prevout.nValue, keyType, addr.size() == 20 ? uint160(addr) : Hash160(addr)); - mapSpent.insert(make_pair(key, value)); inserted.push_back(key); } @@ -297,7 +260,6 @@ void CTxMemPool::addSpentIndex(const CTxMemPoolEntry &entry, const CCoinsViewCac // don't know exactly how, but it was spent CSpentIndexKey key = CSpentIndexKey(input.prevout.hash, input.prevout.n); CSpentIndexValue value = CSpentIndexValue(txhash, j, -1, prevout.nValue, 0, uint160()); - mapSpent.insert(make_pair(key, value)); inserted.push_back(key); } @@ -334,6 +296,199 @@ bool CTxMemPool::removeSpentIndex(const uint256 txhash) return true; } +void CTxMemPool::addUnspentCCIndex(const CTxMemPoolEntry &entry, const CCoinsViewCache &view) +{ + LOCK(cs); + const CTransaction& tx = entry.GetTx(); + std::vector inserted; + + uint256 txhash = tx.GetHash(); + for (unsigned int j = 0; j < tx.vin.size(); j++) { + if (tx.IsPegsImport() && j==0) continue; + const CTxIn input = tx.vin[j]; + const CTxOut &prevout = view.GetOutputFor(input); + + vector> vSols; + txnouttype txType = TX_PUBKEYHASH; + CTxDestination vDest; + int keyType = GetAddressType(prevout.scriptPubKey, vDest, txType, vSols); + if (keyType == 3) // cc type + { + if (vSols.size() > 0) + { + uint160 addrHash = vSols[0].size() == 20 ? uint160(vSols[0]) : Hash160(vSols[0]); // use first vSol data as the address + uint256 hashBlock; + + const CTransaction& vintx = mapTx.find(input.prevout.hash)->GetTx(); // load previous mempool tx to get opreturn + // note we add only mempool tx into the unspent cc index + // so non-mempool txns spent by mempool txns will be found in the db cc index as unspent + // that is, the caller should always check that vout is not spent in mempool. + // This limitation is because the coin cache does not store opreturns + if (!vintx.IsNull() && vintx.vout.size() > 0) { + uint256 creationId; + uint8_t evalcode, funcid, version; + CScript prevOpreturn; //init as empty + if (vintx.vout.back().scriptPubKey.size() > 0 && vintx.vout.back().scriptPubKey[0] == OP_RETURN) + prevOpreturn = vintx.vout.back().scriptPubKey; + + if (CCDecodeTxVout(vintx, input.prevout.n, evalcode, funcid, version, creationId)) { + CUnspentCCIndexKey key(addrHash, creationId, input.prevout.hash, input.prevout.n); + mapUnspentCCIndex.erase(key); + std::cerr << __func__ << " removing previous from mempool cc index addrHash=" << addrHash.GetHex() << " tx=" << txhash.GetHex() << " input.prevout.hash=" << input.prevout.hash.GetHex() << " input.prevout.n=" << j << " evalcode=" << (int)evalcode << " creationId=" << creationId.GetHex() << " prevOpreturn.size()=" << prevOpreturn.size() << std::endl; + inserted.push_back(key); + } + } + } + } + } + + for (unsigned int k = 0; k < tx.vout.size(); k++) { + const CTxOut &out = tx.vout[k]; + + vector> vSols; + CTxDestination vDest; + txnouttype txType = TX_PUBKEYHASH; + int keyType = GetAddressType(out.scriptPubKey, vDest, txType, vSols); + if (keyType == 3 && txType != TX_MULTISIG) // cc vout type + { + if (vSols.size() > 0) + { + uint160 addrHash = vSols[0].size() == 20 ? uint160(vSols[0]) : Hash160(vSols[0]); // use first vSol data as the address + uint256 creationId; + uint8_t evalcode, funcid, version; + CScript opreturn; //init as empty + if (tx.vout.back().scriptPubKey.size() > 0 && tx.vout.back().scriptPubKey[0] == OP_RETURN) + opreturn = tx.vout.back().scriptPubKey; + + if (CCDecodeTxVout(tx, k, evalcode, funcid, version, creationId)) { + // record cc index output with spk and opreturn + CUnspentCCIndexKey key(addrHash, creationId, txhash, k); + CUnspentCCIndexValue value(tx.vout[k].nValue, tx.vout[k].scriptPubKey, opreturn, 0, evalcode, funcid, version); + mapUnspentCCIndex.insert(make_pair(key, value)); + std::cerr << __func__ << " adding to mempool cc index addrHash=" << addrHash.GetHex() << " tx=" << txhash.GetHex() << " nvout=" << k << " evalcode=" << (int)evalcode << " creationId=" << creationId.GetHex() << " opreturn.size()=" << opreturn.size() << " mapUnspentCCIndex.size=" << mapUnspentCCIndex.size() << std::endl; + inserted.push_back(key); + } + } + } + } + + mapUnspentCCIndexInserted.insert(make_pair(txhash, inserted)); +} + +// finds outputs by hash160 of a cc address (creationid must by null) +// or by a pair of hash160 of a cc address and creationid +bool CTxMemPool::getUnspentCCIndex(const std::vector > &keys, std::vector > &outputs) +{ + LOCK(cs); + for (std::vector >::const_iterator it = keys.begin(); it != keys.end(); it++) { + mapUnspentCCIndexType::iterator ait = mapUnspentCCIndex.lower_bound(CUnspentCCIndexKey((*it).first, (*it).second, zeroid, 0)); + while (ait != mapUnspentCCIndex.end() && (*ait).first.hashBytes == (*it).first && ((*ait).first.creationid == (*it).second || (*it).second.IsNull())) { + outputs.push_back(*ait); + ait++; + } + + { + std::cerr << __func__ << " (*it).first=" << (*it).first.GetHex() << std::endl; + mapUnspentCCIndexType::iterator ait = mapUnspentCCIndex.lower_bound(CUnspentCCIndexKey((*it).first, (*it).second, zeroid, 0)); + while (ait != mapUnspentCCIndex.end() ) { + std::cerr << __func__ << " (*ait).first.hashBytes=" << (*ait).first.hashBytes.GetHex() << " (*ait).first.creationid=" << (*ait).first.creationid.GetHex() << " txhash=" << (*ait).first.txhash.GetHex() << " index=" << (*ait).first.index << std::endl; + ait++; + } + } + } + return true; +} + +// erase tx unspent entry and restore previous as unspents +bool CTxMemPool::removeUnspentCCIndex(const CTransaction &tx) +{ + LOCK(cs); + /* mapUnspentCCIndexInsertedType::iterator it = mapUnspentCCIndexInserted.find(txhash); + + if (it != mapUnspentCCIndexInserted.end()) { + std::vector keys = (*it).second; + for (std::vector::iterator mit = keys.begin(); mit != keys.end(); mit++) { + mapUnspentCCIndex.erase(*mit); + } + mapUnspentCCIndexInserted.erase(it); + } */ + + uint256 txhash = tx.GetHash(); + // restore previous mempool tx as unspents + for (unsigned int j = 0; j < tx.vin.size(); j++) { + if (tx.IsPegsImport() && j==0) continue; + const CTxIn input = tx.vin[j]; + //const CTxOut &prevout = view.GetOutputFor(input); + + vector> vSols; + txnouttype txType = TX_PUBKEYHASH; + CTxDestination vDest; + //int keyType = GetAddressType(prevout.scriptPubKey, vDest, txType, vSols); + //if (keyType == 3) // cc type + if (IsCCInput(tx.vin[j].scriptSig)) + { + if (vSols.size() > 0) + { + uint160 addrHash = vSols[0].size() == 20 ? uint160(vSols[0]) : Hash160(vSols[0]); // use first vSol data as the address + uint256 hashBlock; + + const CTransaction& vintx = mapTx.find(input.prevout.hash)->GetTx(); // load previous mempool tx to get opreturn + // note we add only mempool tx into the unspent cc index + // so non-mempool txns spent by mempool txns will be found in the db cc index as unspent + // that is, the caller should always check that vout is not spent in mempool. + // This limitation is because the coin cache does not store opreturns + if (!vintx.IsNull() && vintx.vout.size() > 0) { + uint256 creationId; + uint8_t evalcode, funcid, version; + CScript prevOpreturn; //init as empty + if (vintx.vout.back().scriptPubKey.size() > 0 && vintx.vout.back().scriptPubKey[0] == OP_RETURN) + prevOpreturn = vintx.vout.back().scriptPubKey; + + if (CCDecodeTxVout(vintx, input.prevout.n, evalcode, funcid, version, creationId)) { + CUnspentCCIndexKey key(addrHash, creationId, input.prevout.hash, input.prevout.n); + CUnspentCCIndexValue value(vintx.vout[input.prevout.n].nValue, vintx.vout[input.prevout.n].scriptPubKey, prevOpreturn, 0, evalcode, funcid, version); + mapUnspentCCIndex.insert(make_pair(key, value)); + std::cerr << __func__ << " restoring previous to mempool cc index addrHash=" << addrHash.GetHex() << " tx=" << txhash.GetHex() << " input.prevout.hash=" << input.prevout.hash.GetHex() << " input.prevout.n=" << j << " evalcode=" << (int)evalcode << " creationId=" << creationId.GetHex() << " prevOpreturn.size()=" << prevOpreturn.size() << std::endl; + //inserted.push_back(key); + } + } + } + } + } + + // eraase entries for the tx + for (unsigned int k = 0; k < tx.vout.size(); k++) { + const CTxOut &out = tx.vout[k]; + + vector> vSols; + CTxDestination vDest; + txnouttype txType = TX_PUBKEYHASH; + int keyType = GetAddressType(out.scriptPubKey, vDest, txType, vSols); + if (keyType == 3 && txType != TX_MULTISIG) // cc vout type + { + if (vSols.size() > 0) + { + uint160 addrHash = vSols[0].size() == 20 ? uint160(vSols[0]) : Hash160(vSols[0]); // use first vSol data as the address + uint256 creationId; + uint8_t evalcode, funcid, version; + CScript opreturn; //init as empty + if (tx.vout.back().scriptPubKey.size() > 0 && tx.vout.back().scriptPubKey[0] == OP_RETURN) + opreturn = tx.vout.back().scriptPubKey; + + if (CCDecodeTxVout(tx, k, evalcode, funcid, version, creationId)) { + // record cc index output with spk and opreturn + CUnspentCCIndexKey key(addrHash, creationId, txhash, k); + CUnspentCCIndexValue value(tx.vout[k].nValue, tx.vout[k].scriptPubKey, opreturn, 0, evalcode, funcid, version); + mapUnspentCCIndex.erase(key); + std::cerr << __func__ << " removing from mempool cc index addrHash=" << addrHash.GetHex() << " tx=" << txhash.GetHex() << " nvout=" << k << " evalcode=" << (int)evalcode << " creationId=" << creationId.GetHex() << " opreturn.size()=" << opreturn.size() << std::endl; + //inserted.push_back(key); + } + } + } + } + return true; +} + void CTxMemPool::remove(const CTransaction &origTx, std::list& removed, bool fRecursive) { // Remove transaction from memory pool @@ -360,6 +515,7 @@ void CTxMemPool::remove(const CTransaction &origTx, std::list& rem if (!mapTx.count(hash)) continue; const CTransaction& tx = mapTx.find(hash)->GetTx(); + const CTransaction txCopy = tx; // save for cc index clean up if (fRecursive) { for (unsigned int i = 0; i < tx.vout.size(); i++) { std::map::iterator it = mapNextTx.find(COutPoint(hash, i)); @@ -387,6 +543,7 @@ void CTxMemPool::remove(const CTransaction &origTx, std::list& rem minerPolicyEstimator->removeTx(hash); removeAddressIndex(hash); removeSpentIndex(hash); + removeUnspentCCIndex(txCopy); // erase cc index entry if present } } } diff --git a/src/txmempool.h b/src/txmempool.h index b73ff4b3920..bdb9f5bf233 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -30,6 +30,8 @@ #include "primitives/transaction.h" #include "sync.h" +#include "unspentccindex.h" + #undef foreach #include "boost/multi_index_container.hpp" #include "boost/multi_index/ordered_index.hpp" @@ -187,6 +189,12 @@ class CTxMemPool typedef std::map > mapSpentIndexInserted; mapSpentIndexInserted mapSpentInserted; + typedef std::map mapUnspentCCIndexType; + mapUnspentCCIndexType mapUnspentCCIndex; + + typedef std::map > mapUnspentCCIndexInsertedType; + mapUnspentCCIndexInsertedType mapUnspentCCIndexInserted; + public: std::map mapNextTx; std::map > mapDeltas; @@ -212,6 +220,12 @@ class CTxMemPool void addSpentIndex(const CTxMemPoolEntry &entry, const CCoinsViewCache &view); bool getSpentIndex(CSpentIndexKey &key, CSpentIndexValue &value); bool removeSpentIndex(const uint256 txhash); + + // unspent cc index support: + void addUnspentCCIndex(const CTxMemPoolEntry &entry, const CCoinsViewCache &view); + bool getUnspentCCIndex(const std::vector > &keys, std::vector > &outputs); + bool removeUnspentCCIndex(const CTransaction &tx); + void remove(const CTransaction &tx, std::list& removed, bool fRecursive = false); void removeWithAnchor(const uint256 &invalidRoot, ShieldedType type); void removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight, int flags); diff --git a/src/unspentccindex.h b/src/unspentccindex.h new file mode 100644 index 00000000000..daa3e71954d --- /dev/null +++ b/src/unspentccindex.h @@ -0,0 +1,198 @@ +/****************************************************************************** + * Copyright © 2014-2010 The SuperNET Developers. * + * * + * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * + * the top-level directory of this distribution for the individual copyright * + * holder information and the developer policies on copyright and licensing. * + * * + * Unless otherwise agreed in a custom licensing agreement, no part of the * + * SuperNET software, including this file may be copied, modified, propagated * + * or distributed except according to the terms contained in the LICENSE file * + * * + * Removal or modification of this copyright notice is prohibited. * + * * + ******************************************************************************/ + +#ifndef UNSPENTCCINDEX_H +#define UNSPENTCCINDEX_H + +#include "uint256.h" +#include "amount.h" + +// unspent cc index key +struct CUnspentCCIndexKey { + uint160 hashBytes; + uint256 creationid; + uint256 txhash; + uint32_t index; + + size_t GetSerializeSize(int nType, int nVersion) const { + return sizeof(uint160) + sizeof(uint256) + sizeof(uint256) + sizeof(uint32_t) ; + } + template + void Serialize(Stream& s) const { + hashBytes.Serialize(s); + creationid.Serialize(s); + txhash.Serialize(s); + ser_writedata32(s, index); + } + template + void Unserialize(Stream& s) { + hashBytes.Unserialize(s); + creationid.Unserialize(s); + txhash.Unserialize(s); + index = ser_readdata32(s); + } + + CUnspentCCIndexKey(uint160 addressHash, uint256 _creationid, uint256 _txid, uint32_t _index) { + hashBytes = addressHash; + creationid = _creationid; + txhash = _txid; + index = _index; + } + + CUnspentCCIndexKey() { + SetNull(); + } + + void SetNull() { + hashBytes.SetNull(); + creationid.SetNull(); + txhash.SetNull(); + index = 0; + } +}; + +// partial key for cc address only +struct CUnspentCCIndexKeyAddr { + uint160 hashBytes; + + size_t GetSerializeSize(int nType, int nVersion) const { + return sizeof(uint160); + } + template + void Serialize(Stream& s) const { + hashBytes.Serialize(s); + } + template + void Unserialize(Stream& s) { + hashBytes.Unserialize(s); + } + + CUnspentCCIndexKeyAddr(uint160 addressHash) { + hashBytes = addressHash; + } + + CUnspentCCIndexKeyAddr() { + SetNull(); + } + + void SetNull() { + hashBytes.SetNull(); + } +}; + +// partial key for cc address+creationid +struct CUnspentCCIndexKeyCreationId { + uint160 hashBytes; + uint256 creationid; + + size_t GetSerializeSize(int nType, int nVersion) const { + return sizeof(uint160) + sizeof(uint256); + } + template + void Serialize(Stream& s) const { + hashBytes.Serialize(s); + creationid.Serialize(s); + } + template + void Unserialize(Stream& s) { + hashBytes.Unserialize(s); + creationid.Unserialize(s); + } + + CUnspentCCIndexKeyCreationId(uint160 addressHash, uint256 _creationid) { + hashBytes = addressHash; + creationid = _creationid; + } + + CUnspentCCIndexKeyCreationId() { + SetNull(); + } + + void SetNull() { + hashBytes.SetNull(); + creationid.SetNull(); + } +}; + +// unspent cc index value +struct CUnspentCCIndexValue { + CAmount satoshis; + CScript scriptPubKey; + CScript opreturn; + int blockHeight; + uint8_t evalcode; + uint8_t funcid; + uint8_t version; + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(satoshis); + READWRITE(*(CScriptBase*)(&scriptPubKey)); + READWRITE(*(CScriptBase*)(&opreturn)); + READWRITE(blockHeight); + READWRITE(evalcode); + READWRITE(funcid); + READWRITE(version); + } + + CUnspentCCIndexValue(CAmount sats, CScript _scriptPubKey, CScript _opreturn, int32_t _height, uint8_t _evalcode, uint8_t _funcid, uint8_t _version) { + satoshis = sats; + scriptPubKey = _scriptPubKey; + opreturn = _opreturn; + blockHeight = _height; + evalcode = _evalcode; + funcid = _funcid; + version = _version; + } + + CUnspentCCIndexValue() { + SetNull(); + } + + void SetNull() { + satoshis = -1; + scriptPubKey.clear(); + opreturn.clear(); + blockHeight = 0; + evalcode = 0; + funcid = 0; + version = 0; + } + + bool IsNull() const { + return (satoshis == -1); + } +}; + +struct CUnspentCCIndexKeyCompare +{ + bool operator()(const CUnspentCCIndexKey& a, const CUnspentCCIndexKey& b) const + { + if (a.hashBytes == b.hashBytes) + if (a.creationid == b.creationid) + if (a.txhash == b.txhash) + return a.index < b.index; + else + return a.txhash < b.txhash; + else + return a.creationid < b.creationid; + else + return a.hashBytes < b.hashBytes; + } +}; + +#endif // #ifndef UNSPENTCCINDEX_H