From e23c4d544fa368ba7348830a930c5149221be091 Mon Sep 17 00:00:00 2001 From: timemarkovqtum Date: Mon, 11 Mar 2024 15:07:47 +0100 Subject: [PATCH] Port rpc raw transaction --- src/addresstype.cpp | 4 + src/addresstype.h | 13 + src/consensus/consensus.h | 5 - src/consensus/params.h | 6 +- src/net_processing.cpp | 2 +- src/node/miner.cpp | 2 +- src/qt/transactiondesc.cpp | 3 +- src/rpc/blockchain.cpp | 4 +- src/rpc/mempool.cpp | 66 +++- src/rpc/mining.cpp | 8 +- src/rpc/rawtransaction.cpp | 516 ++++++++++++++++++++++++++-- src/test/fuzz/package_eval.cpp | 6 +- src/test/fuzz/utxo_total_supply.cpp | 3 +- 13 files changed, 587 insertions(+), 51 deletions(-) diff --git a/src/addresstype.cpp b/src/addresstype.cpp index 6e50731c86..8e13dfd4c4 100644 --- a/src/addresstype.cpp +++ b/src/addresstype.cpp @@ -172,3 +172,7 @@ PKHash ExtractPublicKeyHash(const CScript& scriptPubKey, bool* OK) { return {}; } + +bool IsValidContractSenderAddress(const CTxDestination& dest) { + return {}; +} diff --git a/src/addresstype.h b/src/addresstype.h index b8c27ceec4..fbdaf3ebc7 100644 --- a/src/addresstype.h +++ b/src/addresstype.h @@ -150,6 +150,19 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet) */ CScript GetScriptForDestination(const CTxDestination& dest); +enum addresstype +{ + PUBKEYHASH = 1, + SCRIPTHASH = 2, + WITNESSSCRIPTHASH = 3, + WITNESSPUBKEYHASH = 4, + WITNESSTAPROOT = 5, + NONSTANDARD = 6 +}; + +/** Check whether a CTxDestination can be used as contract sender address. */ +bool IsValidContractSenderAddress(const CTxDestination& dest); + PKHash ExtractPublicKeyHash(const CScript& scriptPubKey, bool* OK = nullptr); #endif // BITCOIN_ADDRESSTYPE_H diff --git a/src/consensus/consensus.h b/src/consensus/consensus.h index f66b34f8ff..8234d727f5 100644 --- a/src/consensus/consensus.h +++ b/src/consensus/consensus.h @@ -11,17 +11,12 @@ /** The maximum allowed size for a serialized block, in bytes (only for buffer size limits) */ extern unsigned int dgpMaxBlockSerSize; -static const unsigned int MAX_BLOCK_SERIALIZED_SIZE = 4000000; /** The maximum allowed weight for a block, see BIP 141 (network rule) */ extern unsigned int dgpMaxBlockWeight; extern unsigned int dgpMaxBlockSize; // qtum -static const unsigned int MAX_BLOCK_WEIGHT = 4000000; /** The maximum allowed number of signature check operations in a block (network rule) */ -static const int64_t MAX_BLOCK_SIGOPS_COST = 80000; -/** Coinbase transaction outputs can only be spent after this number of new blocks (network rule) */ -static const int COINBASE_MATURITY = 100; extern int64_t dgpMaxBlockSigOps; extern unsigned int dgpMaxProtoMsgLength; diff --git a/src/consensus/params.h b/src/consensus/params.h index a8db942fda..0412077d8d 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -137,11 +137,11 @@ struct Params { int64_t nPowTargetTimespan; int64_t nPowTargetTimespanV2; int64_t nRBTPowTargetTimespan; - std::chrono::seconds PowTargetSpacing() const + std::chrono::seconds TargetSpacingChrono(int height) const { - return std::chrono::seconds{nPowTargetSpacing}; + return std::chrono::seconds{TargetSpacing(height)}; } - int64_t DifficultyAdjustmentInterval(int height=0) const + int64_t DifficultyAdjustmentInterval(int height) const { int64_t targetTimespan = TargetTimespan(height); int64_t targetSpacing = TargetSpacing(height); diff --git a/src/net_processing.cpp b/src/net_processing.cpp index b54a83a3aa..7cbb557c43 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -1307,7 +1307,7 @@ bool PeerManagerImpl::TipMayBeStale() bool PeerManagerImpl::CanDirectFetch() { - return m_chainman.ActiveChain().Tip()->Time() > GetAdjustedTime() - m_chainparams.GetConsensus().PowTargetSpacing() * 20; + return m_chainman.ActiveChain().Tip()->Time() > GetAdjustedTime() - m_chainparams.GetConsensus().TargetSpacingChrono(m_chainman.ActiveChain().Height()) * 20; } static bool PeerHasHeader(CNodeState *state, const CBlockIndex *pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main) diff --git a/src/node/miner.cpp b/src/node/miner.cpp index 68af098328..9e664c8710 100644 --- a/src/node/miner.cpp +++ b/src/node/miner.cpp @@ -202,7 +202,7 @@ bool BlockAssembler::TestPackage(uint64_t packageSize, int64_t packageSigOpsCost if (nBlockWeight + WITNESS_SCALE_FACTOR * packageSize >= m_options.nBlockMaxWeight) { return false; } - if (nBlockSigOpsCost + packageSigOpsCost >= MAX_BLOCK_SIGOPS_COST) { + if (nBlockSigOpsCost + packageSigOpsCost >= (uint64_t)dgpMaxBlockSigOps) { return false; } return true; diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index dae6a2dea9..ddf8253e05 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -333,7 +334,7 @@ QString TransactionDesc::toHTML(interfaces::Node& node, interfaces::Wallet& wall if (wtx.is_coinbase) { - quint32 numBlocksToMaturity = COINBASE_MATURITY + 1; + quint32 numBlocksToMaturity = Params().GetConsensus().CoinbaseMaturity(numBlocks) + 1; strHTML += "
" + tr("Generated coins must mature %1 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to \"not accepted\" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.").arg(QString::number(numBlocksToMaturity)) + "
"; } diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index b8f4806d39..8915b3df96 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1927,7 +1927,7 @@ static RPCHelpMan getblockstats() CAmount totalfee = 0; int64_t inputs = 0; int64_t maxtxsize = 0; - int64_t mintxsize = MAX_BLOCK_SERIALIZED_SIZE; + int64_t mintxsize = dgpMaxBlockSerSize; int64_t outputs = 0; int64_t swtotal_size = 0; int64_t swtotal_weight = 0; @@ -2050,7 +2050,7 @@ static RPCHelpMan getblockstats() ret_all.pushKV("mediantxsize", CalculateTruncatedMedian(txsize_array)); ret_all.pushKV("minfee", (minfee == MAX_MONEY) ? 0 : minfee); ret_all.pushKV("minfeerate", (minfeerate == MAX_MONEY) ? 0 : minfeerate); - ret_all.pushKV("mintxsize", mintxsize == MAX_BLOCK_SERIALIZED_SIZE ? 0 : mintxsize); + ret_all.pushKV("mintxsize", mintxsize == dgpMaxBlockSerSize ? 0 : mintxsize); ret_all.pushKV("outs", outputs); ret_all.pushKV("subsidy", GetBlockSubsidy(pindex.nHeight, chainman.GetParams().GetConsensus())); ret_all.pushKV("swtotal_size", swtotal_size); diff --git a/src/rpc/mempool.cpp b/src/rpc/mempool.cpp index 136969eb87..22d874dee5 100644 --- a/src/rpc/mempool.cpp +++ b/src/rpc/mempool.cpp @@ -49,9 +49,26 @@ static RPCHelpMan sendrawtransaction() "Reject transactions with provably unspendable outputs (e.g. 'datacarrier' outputs that use the OP_RETURN opcode) greater than the specified value, expressed in " + CURRENCY_UNIT + ".\n" "If burning funds through unspendable outputs is desired, increase this value.\n" "This check is based on heuristics and does not guarantee spendability of outputs.\n"}, + {"showcontractdata", RPCArg::Type::BOOL, RPCArg::Default{false}, "Show created contract data, ignored when no contracts created"}, }, - RPCResult{ - RPCResult::Type::STR_HEX, "", "The transaction hash in hex" + { + RPCResult{ + RPCResult::Type::STR_HEX, "", "The transaction hash in hex" + }, + RPCResult{"for create contract with showcontractdata = true", + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"}, + {RPCResult::Type::ARR, "contracts", "", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR_HEX, "address", "The expected contract address"}, + {RPCResult::Type::NUM, "index", "The index of the output"}, + }} + }}, + } + }, }, RPCExamples{ "\nCreate a transaction\n" @@ -73,7 +90,8 @@ static RPCHelpMan sendrawtransaction() } for (const auto& out : mtx.vout) { - if((out.scriptPubKey.IsUnspendable() || !out.scriptPubKey.HasValidOps()) && out.nValue > max_burn_amount) { + bool isContract = out.scriptPubKey.HasOpCreate() || out.scriptPubKey.HasOpCall(); + if(!isContract && (out.scriptPubKey.IsUnspendable() || !out.scriptPubKey.HasValidOps()) && out.nValue > max_burn_amount) { throw JSONRPCTransactionError(TransactionError::MAX_BURN_EXCEEDED); } } @@ -95,7 +113,47 @@ static RPCHelpMan sendrawtransaction() throw JSONRPCTransactionError(err, err_string); } - return tx->GetHash().GetHex(); + std::string txid = tx->GetHash().GetHex(); + + bool showcontractdata = false; + if (!request.params[3].isNull()) showcontractdata = request.params[3].get_bool(); + + if(showcontractdata && tx->HasOpCreate()){ + uint32_t voutNumber=0; + UniValue result(UniValue::VOBJ); + result.pushKV("txid", txid); + + UniValue contracts(UniValue::VARR); + for (const CTxOut& txout : tx->vout) { + if(txout.scriptPubKey.HasOpCreate()){ + std::vector SHA256TxVout(32); + std::vector contractAddress(20); + std::vector txIdAndVout(tx->GetHash().begin(), tx->GetHash().end()); + std::vector voutNumberChrs; + + if (voutNumberChrs.size() < sizeof(voutNumber))voutNumberChrs.resize(sizeof(voutNumber)); + std::memcpy(voutNumberChrs.data(), &voutNumber, sizeof(voutNumber)); + txIdAndVout.insert(txIdAndVout.end(),voutNumberChrs.begin(),voutNumberChrs.end()); + CSHA256().Write(txIdAndVout.data(), txIdAndVout.size()).Finalize(SHA256TxVout.data()); + CRIPEMD160().Write(SHA256TxVout.data(), SHA256TxVout.size()).Finalize(contractAddress.data()); + + UniValue contract(UniValue::VOBJ); + contract.pushKV("address", HexStr(contractAddress)); + contract.pushKV("index", (int64_t)voutNumber); + contracts.push_back(contract); + + SHA256TxVout.clear(); + contractAddress.clear(); + txIdAndVout.clear(); + } + voutNumber++; + } + result.pushKV("contracts", contracts); + + return result; + } + + return txid; }, }; } diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 76170c3201..3b53115534 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -64,7 +64,7 @@ static UniValue GetNetworkHashPS(int lookup, int height, const CChain& active_ch // If lookup is -1, then use blocks since last difficulty change. if (lookup <= 0) - lookup = pb->nHeight % Params().GetConsensus().DifficultyAdjustmentInterval() + 1; + lookup = pb->nHeight % Params().GetConsensus().DifficultyAdjustmentInterval(pb->nHeight) + 1; // If lookup is larger than chain, then set it to chain length. if (lookup > pb->nHeight) @@ -923,8 +923,8 @@ static RPCHelpMan getblocktemplate() result.pushKV("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1); result.pushKV("mutable", aMutable); result.pushKV("noncerange", "00000000ffffffff"); - int64_t nSigOpLimit = MAX_BLOCK_SIGOPS_COST; - int64_t nSizeLimit = MAX_BLOCK_SERIALIZED_SIZE; + int64_t nSigOpLimit = dgpMaxBlockSigOps; + int64_t nSizeLimit = dgpMaxBlockSerSize; if (fPreSegWit) { CHECK_NONFATAL(nSigOpLimit % WITNESS_SCALE_FACTOR == 0); nSigOpLimit /= WITNESS_SCALE_FACTOR; @@ -934,7 +934,7 @@ static RPCHelpMan getblocktemplate() result.pushKV("sigoplimit", nSigOpLimit); result.pushKV("sizelimit", nSizeLimit); if (!fPreSegWit) { - result.pushKV("weightlimit", (int64_t)MAX_BLOCK_WEIGHT); + result.pushKV("weightlimit", (int64_t)dgpMaxBlockWeight); } result.pushKV("curtime", pblock->GetBlockTime()); result.pushKV("bits", strprintf("%08x", pblock->nBits)); diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index b30b0bbfeb..def246f3c2 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -38,8 +38,11 @@ #include #include #include +#include #include #include +#include +#include #include #include @@ -57,10 +60,10 @@ static void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& TxVerbosity verbosity = TxVerbosity::SHOW_DETAILS) { CHECK_NONFATAL(verbosity >= TxVerbosity::SHOW_DETAILS); - // Call into TxToUniv() in bitcoin-common to decode the transaction hex. + // Call into TxToUniv() in qtum-common to decode the transaction hex. // // Blockchain contextual information (confirmations and blocktime) is not - // available to code in bitcoin-common, so we query them here and push the + // available to code in qtum-common, so we query them here and push the // data into the returned UniValue. TxToUniv(tx, /*block_hash=*/uint256(), entry, /*include_hex=*/true, RPCSerializationFlags(), txundo, verbosity); @@ -81,6 +84,184 @@ static void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& } } +void TxToJSONExpanded(const CTransaction& tx, const uint256 hashBlock, UniValue& entry, const CTxMemPool& mempool, node::BlockManager& blockman, + int nHeight = 0, int nConfirmations = 0, int nBlockTime = 0) +{ + + uint256 txid = tx.GetHash(); + entry.pushKV("hex", EncodeHexTx(tx, RPCSerializationFlags())); + entry.pushKV("txid", tx.GetHash().GetHex()); + entry.pushKV("hash", tx.GetWitnessHash().GetHex()); + entry.pushKV("version", tx.nVersion); + entry.pushKV("size", (int)::GetSerializeSize(tx, PROTOCOL_VERSION)); + entry.pushKV("vsize", (GetTransactionWeight(tx) + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR); + entry.pushKV("weight", GetTransactionWeight(tx)); + entry.pushKV("locktime", (int64_t)tx.nLockTime); + + UniValue vin(UniValue::VARR); + for(const CTxIn& txin : tx.vin) { + UniValue in(UniValue::VOBJ); + if (tx.IsCoinBase()) + in.pushKV("coinbase", HexStr(txin.scriptSig)); + else { + in.pushKV("txid", txin.prevout.hash.GetHex()); + in.pushKV("vout", (int64_t)txin.prevout.n); + UniValue o(UniValue::VOBJ); + o.pushKV("asm", ScriptToAsmStr(txin.scriptSig, true)); + o.pushKV("hex", HexStr(txin.scriptSig)); + in.pushKV("scriptSig", o); + if (!txin.scriptWitness.IsNull()) { + UniValue txinwitness(UniValue::VARR); + for (const auto& item : txin.scriptWitness.stack) { + txinwitness.push_back(HexStr(item)); + } + in.pushKV("txinwitness", txinwitness); + } + // Add address and value info if spentindex enabled + CSpentIndexValue spentInfo; + CSpentIndexKey spentKey(txin.prevout.hash, txin.prevout.n); + if (GetSpentIndex(spentKey, spentInfo, mempool, blockman)) { + in.pushKV("value", ValueFromAmount(spentInfo.satoshis)); + in.pushKV("valueSat", spentInfo.satoshis); + if (spentInfo.addressType == 1) { + std::vector addressBytes(spentInfo.addressHash.begin(), spentInfo.addressHash.begin() + 20); + in.pushKV("address", EncodeDestination(CTxDestination(PKHash(uint160(addressBytes))))); + } else if (spentInfo.addressType == 2) { + std::vector addressBytes(spentInfo.addressHash.begin(), spentInfo.addressHash.begin() + 20); + in.pushKV("address", EncodeDestination(CTxDestination(ScriptHash(uint160(addressBytes))))); + } else if (spentInfo.addressType == 3) { + in.pushKV("address", EncodeDestination(CTxDestination(WitnessV0ScriptHash(spentInfo.addressHash)))); + } else if (spentInfo.addressType == 4) { + std::vector addressBytes(spentInfo.addressHash.begin(), spentInfo.addressHash.begin() + 20); + in.pushKV("address", EncodeDestination(CTxDestination(WitnessV0KeyHash(uint160(addressBytes))))); + } + } + } + in.pushKV("sequence", (int64_t)txin.nSequence); + vin.push_back(in); + } + entry.pushKV("vin", vin); + UniValue vout(UniValue::VARR); + for (unsigned int i = 0; i < tx.vout.size(); i++) { + const CTxOut& txout = tx.vout[i]; + UniValue out(UniValue::VOBJ); + out.pushKV("value", ValueFromAmount(txout.nValue)); + out.pushKV("valueSat", txout.nValue); + out.pushKV("n", (int64_t)i); + UniValue o(UniValue::VOBJ); + ScriptToUniv(txout.scriptPubKey, o, true, true); + out.pushKV("scriptPubKey", o); + + // Add spent information if spentindex is enabled + CSpentIndexValue spentInfo; + CSpentIndexKey spentKey(txid, i); + if (GetSpentIndex(spentKey, spentInfo, mempool, blockman)) { + out.pushKV("spentTxId", spentInfo.txid.GetHex()); + out.pushKV("spentIndex", (int)spentInfo.inputIndex); + out.pushKV("spentHeight", spentInfo.blockHeight); + } + + vout.push_back(out); + } + entry.pushKV("vout", vout); + + if (!hashBlock.IsNull()) { + entry.pushKV("blockhash", hashBlock.GetHex()); + + if (nConfirmations > 0) { + entry.pushKV("height", nHeight); + entry.pushKV("confirmations", nConfirmations); + entry.pushKV("time", nBlockTime); + entry.pushKV("blocktime", nBlockTime); + } else { + entry.pushKV("height", -1); + entry.pushKV("confirmations", 0); + } + } +} + +static RPCHelpMan gethexaddress() +{ + return RPCHelpMan{"gethexaddress", + "\nConverts a base58 pubkeyhash address to a hex address for use in smart contracts.\n", + { + {"address", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The base58 address"}, + }, + RPCResult{ + RPCResult::Type::STR_HEX, "hexaddress", "The raw hex pubkeyhash address for use in smart contracts"}, + RPCExamples{ + HelpExampleCli("gethexaddress", "\"address\"") + + HelpExampleRpc("gethexaddress", "\"address\"") + }, + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue +{ + + CTxDestination dest = DecodeDestination(request.params[0].get_str()); + if (!IsValidDestination(dest)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Qtum address"); + } + + if(!std::holds_alternative(dest)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Only pubkeyhash addresses are supported"); + PKHash keyID = std::get(dest); + + return ToKeyID(keyID).GetReverseHex(); +}, + }; +} + +static RPCHelpMan fromhexaddress() +{ + return RPCHelpMan{"fromhexaddress", + "\nConverts a raw hex address to a base58 pubkeyhash address\n", + { + {"hexaddress", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The raw hex address"}, + }, + RPCResult{ + RPCResult::Type::STR, "address", "The base58 pubkeyhash address"}, + RPCExamples{ + HelpExampleCli("fromhexaddress", "\"hexaddress\"") + + HelpExampleRpc("fromhexaddress", "\"hexaddress\"") + }, + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue +{ + + if (request.params[0].get_str().size() != 40) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid pubkeyhash hex size (should be 40 hex characters)"); + CKeyID id; + id.SetReverseHex(request.params[0].get_str()); + PKHash raw(id); + CTxDestination dest(raw); + + return EncodeDestination(dest); +}, + }; +} + +static std::vector DecodeExpanded(bool isExpanded, bool isVin) +{ + if(isExpanded) + { + if(isVin) { + return { + {RPCResult::Type::STR_AMOUNT, "value", /*optional=*/true, "The value in " + CURRENCY_UNIT + " (only if address index is enabled)"}, + {RPCResult::Type::NUM, "valueSat", /*optional=*/true, "The value in Sat (only if address index is enabled)"}, + {RPCResult::Type::STR, "address", /*optional=*/true, "The Qtum address (only if address index is enabled)"}, + }; + } + else { + return { + {RPCResult::Type::NUM, "valueSat", /*optional=*/true, "The value in Sat (only if address index is enabled)"}, + {RPCResult::Type::STR_HEX, "spentTxId", /*optional=*/true, "The spent txid (only if address index is enabled)"}, + {RPCResult::Type::NUM, "spentIndex", /*optional=*/true, "The spent index (only if address index is enabled)"}, + {RPCResult::Type::NUM, "spentHeight", /*optional=*/true, "The spent height (only if address index is enabled)"}, + }; + } + + } + return {}; +} + static std::vector ScriptPubKeyDoc() { return { @@ -92,7 +273,7 @@ static std::vector ScriptPubKeyDoc() { }; } -static std::vector DecodeTxDoc(const std::string& txid_field_doc) +static std::vector DecodeTxDoc(const std::string& txid_field_doc, bool isExpanded = false) { return { {RPCResult::Type::STR_HEX, "txid", txid_field_doc}, @@ -106,28 +287,36 @@ static std::vector DecodeTxDoc(const std::string& txid_field_doc) { {RPCResult::Type::OBJ, "", "", { - {RPCResult::Type::STR_HEX, "coinbase", /*optional=*/true, "The coinbase value (only if coinbase transaction)"}, - {RPCResult::Type::STR_HEX, "txid", /*optional=*/true, "The transaction id (if not coinbase transaction)"}, - {RPCResult::Type::NUM, "vout", /*optional=*/true, "The output number (if not coinbase transaction)"}, - {RPCResult::Type::OBJ, "scriptSig", /*optional=*/true, "The script (if not coinbase transaction)", + Cat>( { - {RPCResult::Type::STR, "asm", "Disassembly of the signature script"}, - {RPCResult::Type::STR_HEX, "hex", "The raw signature script bytes, hex-encoded"}, - }}, - {RPCResult::Type::ARR, "txinwitness", /*optional=*/true, "", - { - {RPCResult::Type::STR_HEX, "hex", "hex-encoded witness data (if any)"}, - }}, - {RPCResult::Type::NUM, "sequence", "The script sequence number"}, + {RPCResult::Type::STR_HEX, "coinbase", /*optional=*/true, "The coinbase value (only if coinbase transaction)"}, + {RPCResult::Type::STR_HEX, "txid", /*optional=*/true, "The transaction id (if not coinbase transaction)"}, + {RPCResult::Type::NUM, "vout", /*optional=*/true, "The output number (if not coinbase transaction)"}, + {RPCResult::Type::OBJ, "scriptSig", /*optional=*/true, "The script (if not coinbase transaction)", + { + {RPCResult::Type::STR, "asm", "Disassembly of the signature script"}, + {RPCResult::Type::STR_HEX, "hex", "The raw signature script bytes, hex-encoded"}, + }}, + {RPCResult::Type::ARR, "txinwitness", /*optional=*/true, "", + { + {RPCResult::Type::STR_HEX, "hex", "hex-encoded witness data (if any)"}, + }}, + {RPCResult::Type::NUM, "sequence", "The script sequence number"}, + }, + DecodeExpanded(isExpanded, true)), }}, }}, {RPCResult::Type::ARR, "vout", "", { {RPCResult::Type::OBJ, "", "", { - {RPCResult::Type::STR_AMOUNT, "value", "The value in " + CURRENCY_UNIT}, - {RPCResult::Type::NUM, "n", "index"}, - {RPCResult::Type::OBJ, "scriptPubKey", "", ScriptPubKeyDoc()}, + Cat>( + { + {RPCResult::Type::STR_AMOUNT, "value", "The value in " + CURRENCY_UNIT}, + {RPCResult::Type::NUM, "n", "index"}, + {RPCResult::Type::OBJ, "scriptPubKey", "", ScriptPubKeyDoc()}, + }, + DecodeExpanded(isExpanded, false)), }}, }}, }; @@ -155,7 +344,7 @@ static std::vector CreateTxDoc() { {"", RPCArg::Type::OBJ_USER_KEYS, RPCArg::Optional::OMITTED, "", { - {"address", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in " + CURRENCY_UNIT}, + {"address", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "A key-value pair. The key (string) is the qtum address, the value (float or string) is the amount in " + CURRENCY_UNIT}, }, }, {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "", @@ -163,6 +352,24 @@ static std::vector CreateTxDoc() {"data", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "A key-value pair. The key must be \"data\", the value is hex-encoded data"}, }, }, + {"contract", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "(send to contract)", + { + {"contractAddress", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "Valid contract address (valid hash160 hex data)"}, + {"data", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "Hex data to add in the call output"}, + {"amount", RPCArg::Type::AMOUNT, RPCArg::Default{0}, "Value in QTUM to send with the call, should be a valid amount, default 0"}, + {"gasLimit", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The gas limit for the transaction"}, + {"gasPrice", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The gas price for the transaction"}, + {"senderAddress", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "The qtum address that will be used to create the contract."}, + }, + }, + {"contract", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "(create contract)", + { + {"bytecode", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "contract bytcode."}, + {"gasLimit", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The gas limit for the transaction"}, + {"gasPrice", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The gas price for the transaction"}, + {"senderAddress", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "The qtum address that will be used to create the contract."}, + }, + }, }, RPCArgOptions{.skip_type_check = true}}, {"locktime", RPCArg::Type::NUM, RPCArg::Default{0}, "Raw locktime. Non-0 value also locktime-activates inputs"}, @@ -291,9 +498,10 @@ static RPCHelpMan getrawtransaction() {RPCResult::Type::NUM, "confirmations", /*optional=*/true, "The confirmations"}, {RPCResult::Type::NUM_TIME, "blocktime", /*optional=*/true, "The block time expressed in " + UNIX_EPOCH_TIME}, {RPCResult::Type::NUM, "time", /*optional=*/true, "Same as \"blocktime\""}, + {RPCResult::Type::NUM, "height", /*optional=*/true, "The block height"}, {RPCResult::Type::STR_HEX, "hex", "The serialized, hex-encoded data for 'txid'"}, }, - DecodeTxDoc(/*txid_field_doc=*/"The transaction id (same as provided)")), + DecodeTxDoc(/*txid_field_doc=*/"The transaction id (same as provided)", true)), }, RPCResult{"for verbosity = 2", RPCResult::Type::OBJ, "", "", @@ -327,6 +535,7 @@ static RPCHelpMan getrawtransaction() [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { const NodeContext& node = EnsureAnyNodeContext(request.context); + const CTxMemPool& mempool = EnsureMemPool(node); ChainstateManager& chainman = EnsureChainman(node); uint256 hash = ParseHashV(request.params[0], "parameter 1"); @@ -386,6 +595,29 @@ static RPCHelpMan getrawtransaction() return EncodeHexTx(*tx, RPCSerializationFlags()); } + //////////////////////////////////////////////////////// // qtum + int nHeight = 0; + int nConfirmations = 0; + int nBlockTime = 0; + if(fAddressIndex) { + LOCK(cs_main); + node::BlockMap::iterator mi = chainman.BlockIndex().find(hash_block); + if (mi != chainman.BlockIndex().end()) { + CBlockIndex* pindex = &((*mi).second); + if (chainman.ActiveChain().Contains(pindex)) { + nHeight = pindex->nHeight; + nConfirmations = 1 + chainman.ActiveChain().Height() - pindex->nHeight; + nBlockTime = pindex->GetBlockTime(); + } else { + nHeight = -1; + nConfirmations = 0; + nBlockTime = pindex->GetBlockTime(); + } + } + } + //////////////////////////////////////////////////////// + + UniValue result(UniValue::VOBJ); if (blockindex) { LOCK(cs_main); @@ -398,6 +630,7 @@ static RPCHelpMan getrawtransaction() } if (verbosity == 1 || !blockindex) { TxToJSON(*tx, hash_block, result, chainman.ActiveChainstate()); + if (fAddressIndex) TxToJSONExpanded(*tx, hash_block, result, mempool, chainman.m_blockman, nHeight, nConfirmations, nBlockTime); return result; } @@ -408,6 +641,7 @@ static RPCHelpMan getrawtransaction() if (tx->IsCoinBase() || is_block_pruned || !(chainman.m_blockman.UndoReadFromDisk(blockUndo, *blockindex) && chainman.m_blockman.ReadBlockFromDisk(block, *blockindex))) { TxToJSON(*tx, hash_block, result, chainman.ActiveChainstate()); + if (fAddressIndex) TxToJSONExpanded(*tx, hash_block, result, mempool, chainman.m_blockman, nHeight, nConfirmations, nBlockTime); return result; } @@ -418,11 +652,143 @@ static RPCHelpMan getrawtransaction() undoTX = &blockUndo.vtxundo.at(it - block.vtx.begin() - 1); } TxToJSON(*tx, hash_block, result, chainman.ActiveChainstate(), undoTX, TxVerbosity::SHOW_DETAILS_AND_PREVOUT); + if (fAddressIndex) TxToJSONExpanded(*tx, hash_block, result, mempool, chainman.m_blockman, nHeight, nConfirmations, nBlockTime); return result; }, }; } +class RawContract : public IRawContract +{ +public: + RawContract(ChainstateManager& _chainman): + chainman(_chainman) + {} + + void addContract(CMutableTransaction& rawTx, const UniValue& Contract) override + { + if(!Contract.isObject()) + throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, need to be object: contract")); + + // Get dgp gas limit and gas price + LOCK(cs_main); + CChain& active_chain = chainman.ActiveChain(); + QtumDGP qtumDGP(globalState.get(), chainman.ActiveChainstate(), fGettingValuesDGP); + uint64_t blockGasLimit = qtumDGP.getBlockGasLimit(active_chain.Height()); + uint64_t minGasPrice = CAmount(qtumDGP.getMinGasPrice(active_chain.Height())); + CAmount nGasPrice = (minGasPrice>DEFAULT_GAS_PRICE)?minGasPrice:DEFAULT_GAS_PRICE; + + bool createContract = Contract.exists("bytecode") && Contract["bytecode"].isStr(); + CScript scriptPubKey; + CAmount nAmount = 0; + + // Get gas limit + uint64_t nGasLimit=createContract ? DEFAULT_GAS_LIMIT_OP_CREATE : DEFAULT_GAS_LIMIT_OP_SEND; + if (Contract.exists("gasLimit")){ + nGasLimit = Contract["gasLimit"].getInt(); + if (nGasLimit > blockGasLimit) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid value for gasLimit (Maximum is: "+i64tostr(blockGasLimit)+")"); + if (nGasLimit < MINIMUM_GAS_LIMIT) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid value for gasLimit (Minimum is: "+i64tostr(MINIMUM_GAS_LIMIT)+")"); + if (nGasLimit <= 0) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid value for gasLimit"); + } + + // Get gas price + if (Contract.exists("gasPrice")){ + UniValue uGasPrice = Contract["gasPrice"]; + std::optional optGasPrice = ParseMoney(uGasPrice.getValStr()); + if(!optGasPrice) + { + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid value for gasPrice"); + } + nGasPrice = (uint64_t)(optGasPrice.value_or(0)); + CAmount maxRpcGasPrice = gArgs.GetIntArg("-rpcmaxgasprice", MAX_RPC_GAS_PRICE); + if (nGasPrice > (int64_t)maxRpcGasPrice) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid value for gasPrice, Maximum allowed in RPC calls is: "+FormatMoney(maxRpcGasPrice)+" (use -rpcmaxgasprice to change it)"); + if (nGasPrice < (int64_t)minGasPrice) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid value for gasPrice (Minimum is: "+FormatMoney(minGasPrice)+")"); + if (nGasPrice <= 0) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid value for gasPrice"); + } + + // Get sender address + bool fHasSender=false; + CTxDestination senderAddress; + if (Contract.exists("senderAddress")){ + senderAddress = DecodeDestination(Contract["senderAddress"].get_str()); + if (!IsValidDestination(senderAddress)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Qtum address to send from"); + if (!IsValidContractSenderAddress(senderAddress)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid contract sender address. Only P2PK and P2PKH allowed"); + else + fHasSender=true; + } + + if(createContract) + { + // Get the new contract bytecode + if(!Contract.exists("bytecode") || !Contract["bytecode"].isStr()) + throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, bytecode is mandatory.")); + + std::string bytecodehex = Contract["bytecode"].get_str(); + if(bytecodehex.size() % 2 != 0 || !CheckHex(bytecodehex)) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid bytecode (bytecode not hex)"); + + // Add create contract output + scriptPubKey = CScript() << CScriptNum(VersionVM::GetEVMDefault().toRaw()) << CScriptNum(nGasLimit) << CScriptNum(nGasPrice) << ParseHex(bytecodehex) <addressInUse(addrAccount)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "contract address does not exist"); + + // Get the contract data + if(!Contract.exists("data") || !Contract["data"].isStr()) + throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, contract data is mandatory.")); + + std::string datahex = Contract["data"].get_str(); + if(datahex.size() % 2 != 0 || !CheckHex(datahex)) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid data (data not hex)"); + + // Get amount + if (Contract.exists("amount")){ + nAmount = AmountFromValue(Contract["amount"]); + if (nAmount < 0) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for call contract"); + } + + // Add call contract output + scriptPubKey = CScript() << CScriptNum(VersionVM::GetEVMDefault().toRaw()) << CScriptNum(nGasLimit) << CScriptNum(nGasPrice) << ParseHex(datahex) << ParseHex(contractaddress) << OP_CALL; + } + + // Build op_sender script + if(fHasSender && active_chain.Height() >= Params().GetConsensus().QIP5Height) + { + if(!std::holds_alternative(senderAddress)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Only pubkeyhash addresses are supported"); + PKHash keyID = std::get(senderAddress); + std::vector scriptSig; + scriptPubKey = (CScript() << CScriptNum(addresstype::PUBKEYHASH) << ToByteVector(keyID) << ToByteVector(scriptSig) << OP_SENDER) + scriptPubKey; + } + + CTxOut out(nAmount, scriptPubKey); + rawTx.vout.push_back(out); + } + +private: + ChainstateManager& chainman; +}; + static RPCHelpMan createrawtransaction() { return RPCHelpMan{"createrawtransaction", @@ -438,8 +804,16 @@ static RPCHelpMan createrawtransaction() RPCExamples{ HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"address\\\":0.01}]\"") + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"data\\\":\\\"00010203\\\"}]\"") + + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"contract\\\":{\\\"contractAddress\\\":\\\"mycontract\\\"," + "\\\"data\\\":\\\"00\\\", \\\"gasLimit\\\":250000, \\\"gasPrice\\\":0.00000040, \\\"amount\\\":0}}]\"") + + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"contract\\\":{\\\"bytecode\\\":\\\"contractbytecode\\\"," + "\\\"gasLimit\\\":2500000, \\\"gasPrice\\\":0.00000040, \\\"senderAddress\\\":\\\"QM72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\\\"}}]\"") + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"[{\\\"address\\\":0.01}]\"") + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"[{\\\"data\\\":\\\"00010203\\\"}]\"") + + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"[{\\\"contract\\\":{\\\"contractAddress\\\":\\\"mycontract\\\"," + "\\\"data\\\":\\\"00\\\", \\\"gasLimit\\\":250000, \\\"gasPrice\\\":0.00000040, \\\"amount\\\":0}}]\"") + + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"contract\\\":{\\\"bytecode\\\":\\\"contractbytecode\\\"," + "\\\"gasLimit\\\":2500000, \\\"gasPrice\\\":0.00000040, \\\"senderAddress\\\":\\\"QM72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\\\"}}]\"") }, [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { @@ -447,7 +821,9 @@ static RPCHelpMan createrawtransaction() if (!request.params[3].isNull()) { rbf = request.params[3].get_bool(); } - CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], rbf); + ChainstateManager& chainman = EnsureAnyChainman(request.context); + RawContract rawContract(chainman); + CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], rbf, &rawContract); return EncodeHexTx(CTransaction(rawTx)); }, @@ -509,7 +885,7 @@ static RPCHelpMan decodescript() {RPCResult::Type::STR, "asm", "Script public key"}, {RPCResult::Type::STR, "desc", "Inferred descriptor for the script"}, {RPCResult::Type::STR, "type", "The output type (e.g. " + GetAllOutputTypes() + ")"}, - {RPCResult::Type::STR, "address", /*optional=*/true, "The Bitcoin address (only if a well-defined address exists)"}, + {RPCResult::Type::STR, "address", /*optional=*/true, "The Qtum address (only if a well-defined address exists)"}, {RPCResult::Type::STR, "p2sh", /*optional=*/true, "address of P2SH script wrapping this redeem script (not returned for types that should not be wrapped)"}, {RPCResult::Type::OBJ, "segwit", /*optional=*/true, @@ -518,7 +894,7 @@ static RPCHelpMan decodescript() {RPCResult::Type::STR, "asm", "String representation of the script public key"}, {RPCResult::Type::STR_HEX, "hex", "Hex string of the script public key"}, {RPCResult::Type::STR, "type", "The type of the script public key (e.g. witness_v0_keyhash or witness_v0_scripthash)"}, - {RPCResult::Type::STR, "address", /*optional=*/true, "The Bitcoin address (only if a well-defined address exists)"}, + {RPCResult::Type::STR, "address", /*optional=*/true, "The Qtum address (only if a well-defined address exists)"}, {RPCResult::Type::STR, "desc", "Inferred descriptor for the script"}, {RPCResult::Type::STR, "p2sh-segwit", "address of the P2SH script wrapping this witness redeem script"}, }}, @@ -557,6 +933,10 @@ static RPCHelpMan decodescript() case TxoutType::SCRIPTHASH: case TxoutType::WITNESS_UNKNOWN: case TxoutType::WITNESS_V1_TAPROOT: + case TxoutType::CREATE_SENDER: + case TxoutType::CALL_SENDER: + case TxoutType::CREATE: + case TxoutType::CALL: // Should not be wrapped return false; } // no default case, so the compiler can warn about missing cases @@ -599,6 +979,10 @@ static RPCHelpMan decodescript() case TxoutType::WITNESS_V0_KEYHASH: case TxoutType::WITNESS_V0_SCRIPTHASH: case TxoutType::WITNESS_V1_TAPROOT: + case TxoutType::CREATE_SENDER: + case TxoutType::CALL_SENDER: + case TxoutType::CREATE: + case TxoutType::CALL: // Should not be wrapped return false; } // no default case, so the compiler can warn about missing cases @@ -809,12 +1193,85 @@ static RPCHelpMan signrawtransactionwithkey() ParsePrevouts(request.params[2], &keystore, coins); UniValue result(UniValue::VOBJ); + CheckSenderSignatures(mtx); SignTransaction(mtx, &keystore, coins, request.params[3], result); return result; }, }; } +static RPCHelpMan signrawsendertransactionwithkey() +{ + return RPCHelpMan{"signrawsendertransactionwithkey", + "\nSign OP_SENDER outputs for raw transaction (serialized, hex-encoded).\n" + "The second argument is an array of base58-encoded private\n" + "keys that will be the only keys used to sign the transaction.\n", + { + {"hexstring", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction hex string"}, + {"privkeys", RPCArg::Type::ARR, RPCArg::Optional::NO, "A json array of base58-encoded private keys for signing", + { + {"privatekey", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "private key in base58-encoding"}, + }, + }, + {"sighashtype", RPCArg::Type::STR, RPCArg::Default{"ALL"}, "The signature hash type. Must be one of:\n" + " \"ALL\"\n" + " \"NONE\"\n" + " \"SINGLE\"\n" + " \"ALL|ANYONECANPAY\"\n" + " \"NONE|ANYONECANPAY\"\n" + " \"SINGLE|ANYONECANPAY\"\n" + }, + }, + RPCResult{ + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR_HEX, "hex", "The hex-encoded raw transaction with signature(s)"}, + {RPCResult::Type::BOOL, "complete", "If the transaction has a complete set of signatures"}, + {RPCResult::Type::ARR, "errors", /*optional=*/true, "Script verification errors (if there are any)", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::NUM, "amount", "The amount of the output"}, + {RPCResult::Type::STR_HEX, "scriptPubKey", "The hex-encoded public key script of the output"}, + {RPCResult::Type::STR, "error", "Verification or signing error related to the output"}, + }}, + }}, + } + }, + RPCExamples{ + HelpExampleCli("signrawsendertransactionwithkey", "\"myhex\" \"[\\\"key1\\\",\\\"key2\\\"]\"") + + HelpExampleRpc("signrawsendertransactionwithkey", "\"myhex\", \"[\\\"key1\\\",\\\"key2\\\"]\"") + }, + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue +{ + + CMutableTransaction mtx; + if (!DecodeHexTx(mtx, request.params[0].get_str(), true)) { + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); + } + + FillableSigningProvider keystore; + const UniValue& keys = request.params[1].get_array(); + for (unsigned int idx = 0; idx < keys.size(); ++idx) { + UniValue k = keys[idx]; + CKey key = DecodeSecret(k.get_str()); + if (!key.IsValid()) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key"); + } + keystore.AddKey(key); + } + + UniValue result(UniValue::VOBJ); + UniValue sigHashType = "ALL"; + if (!request.params[2].isNull()) { + sigHashType = request.params[2]; + } + SignTransactionOutput(mtx, &keystore, sigHashType, result); + return result; +}, + }; +} + const RPCResult decodepsbt_inputs{ RPCResult::Type::ARR, "inputs", "", { @@ -833,7 +1290,7 @@ const RPCResult decodepsbt_inputs{ {RPCResult::Type::STR, "desc", "Inferred descriptor for the output"}, {RPCResult::Type::STR_HEX, "hex", "The raw public key script bytes, hex-encoded"}, {RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"}, - {RPCResult::Type::STR, "address", /*optional=*/true, "The Bitcoin address (only if a well-defined address exists)"}, + {RPCResult::Type::STR, "address", /*optional=*/true, "The Qtum address (only if a well-defined address exists)"}, }}, }}, {RPCResult::Type::OBJ_DYN, "partial_signatures", /*optional=*/true, "", @@ -1013,7 +1470,7 @@ static RPCHelpMan decodepsbt() { return RPCHelpMan{ "decodepsbt", - "Return a JSON object representing the serialized, base64-encoded partially signed Bitcoin transaction.", + "Return a JSON object representing the serialized, base64-encoded partially signed Qtum transaction.", { {"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "The PSBT base64 string"}, }, @@ -1453,7 +1910,7 @@ static RPCHelpMan decodepsbt() static RPCHelpMan combinepsbt() { return RPCHelpMan{"combinepsbt", - "\nCombine multiple partially signed Bitcoin transactions into one transaction.\n" + "\nCombine multiple partially signed Qtum transactions into one transaction.\n" "Implements the Combiner role.\n", { {"txs", RPCArg::Type::ARR, RPCArg::Optional::NO, "The base64 strings of partially signed transactions", @@ -1574,7 +2031,9 @@ static RPCHelpMan createpsbt() if (!request.params[3].isNull()) { rbf = request.params[3].get_bool(); } - CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], rbf); + ChainstateManager& chainman = EnsureAnyChainman(request.context); + RawContract rawContract(chainman); + CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], rbf, &rawContract); // Make a blank psbt PartiallySignedTransaction psbtx; @@ -2011,6 +2470,7 @@ void RegisterRawTransactionRPCCommands(CRPCTable& t) {"rawtransactions", &decodescript}, {"rawtransactions", &combinerawtransaction}, {"rawtransactions", &signrawtransactionwithkey}, + {"rawtransactions", &signrawsendertransactionwithkey}, {"rawtransactions", &decodepsbt}, {"rawtransactions", &combinepsbt}, {"rawtransactions", &finalizepsbt}, @@ -2020,6 +2480,8 @@ void RegisterRawTransactionRPCCommands(CRPCTable& t) {"rawtransactions", &descriptorprocesspsbt}, {"rawtransactions", &joinpsbts}, {"rawtransactions", &analyzepsbt}, + {"rawtransactions", &gethexaddress}, + {"rawtransactions", &fromhexaddress}, }; for (const auto& c : commands) { t.appendCommand(c.name, &c); diff --git a/src/test/fuzz/package_eval.cpp b/src/test/fuzz/package_eval.cpp index 4c81c0b679..82691adc62 100644 --- a/src/test/fuzz/package_eval.cpp +++ b/src/test/fuzz/package_eval.cpp @@ -17,6 +17,7 @@ #include #include #include +#include using node::NodeContext; @@ -39,9 +40,10 @@ void initialize_tx_pool() static const auto testing_setup = MakeNoLogFileContext(); g_setup = testing_setup.get(); - for (int i = 0; i < 2 * COINBASE_MATURITY; ++i) { + int coinbaseMaturity = Params().GetConsensus().CoinbaseMaturity(0); + for (int i = 0; i < 2 * coinbaseMaturity; ++i) { COutPoint prevout{MineBlock(g_setup->m_node, P2WSH_OP_TRUE)}; - if (i < COINBASE_MATURITY) { + if (i < coinbaseMaturity) { // Remember the txids to avoid expensive disk access later on g_outpoints_coinbase_init_mature.push_back(prevout); } diff --git a/src/test/fuzz/utxo_total_supply.cpp b/src/test/fuzz/utxo_total_supply.cpp index a60bb712f0..eb7a111f12 100644 --- a/src/test/fuzz/utxo_total_supply.cpp +++ b/src/test/fuzz/utxo_total_supply.cpp @@ -95,7 +95,8 @@ FUZZ_TARGET(utxo_total_supply) // Get at which height we duplicate the coinbase // Assuming that the fuzzer will mine relatively short chains (less than 200 blocks), we want the duplicate coinbase to be not too high. // Up to 2000 seems reasonable. - int64_t duplicate_coinbase_height = fuzzed_data_provider.ConsumeIntegralInRange(0, 20 * COINBASE_MATURITY); + int coinbaseMaturity = Params().GetConsensus().CoinbaseMaturity(0); + int64_t duplicate_coinbase_height = fuzzed_data_provider.ConsumeIntegralInRange(0, 20 * coinbaseMaturity); // Always pad with OP_0 at the end to avoid bad-cb-length error const CScript duplicate_coinbase_script = CScript() << duplicate_coinbase_height << OP_0; // Mine the first block with this duplicate