From d30f46d83be6da6128961483c06fb861659505df Mon Sep 17 00:00:00 2001 From: timemarkovqtum Date: Mon, 8 Jul 2024 16:33:48 +0200 Subject: [PATCH] Port rpc mempool --- src/rpc/blockchain.cpp | 323 ++++++++++++++++++++++++++++ src/rpc/mempool.cpp | 67 +++++- src/rpc/mining.cpp | 69 +++--- src/rpc/node.cpp | 441 +++++++++++++++++++++++++++++++++++++- src/rpc/output_script.cpp | 90 +------- src/validation.cpp | 37 ++++ src/validation.h | 9 + src/wallet/rpc/mining.cpp | 6 +- 8 files changed, 902 insertions(+), 140 deletions(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 1a4e113917..11b7824564 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -3859,6 +3859,312 @@ return RPCHelpMan{ }; } +static RPCHelpMan qrc20name() +{ + return RPCHelpMan{"qrc20name", + "\nReturns the name of the token\n", + { + {"contractaddress", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The contract address"}, + }, + RPCResult{ + RPCResult::Type::STR, "name", "The name of the token"}, + RPCExamples{ + HelpExampleCli("qrc20name", "\"eb23c0b3e6042821da281a2e2364feb22dd543e3\"") + + HelpExampleRpc("qrc20name", "\"eb23c0b3e6042821da281a2e2364feb22dd543e3\"") + }, + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue +{ + // Set contract address + ChainstateManager& chainman = EnsureAnyChainman(request.context); + CallToken token(chainman); + token.setAddress(request.params[0].get_str()); + + // Get name + std::string result; + if(!token.name(result)) + throw JSONRPCError(RPC_MISC_ERROR, "Fail to get token name"); + + return result; +}, + }; +} + +static RPCHelpMan qrc20symbol() +{ + return RPCHelpMan{"qrc20symbol", + "\nReturns the symbol of the token\n", + { + {"contractaddress", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The contract address"}, + }, + RPCResult{ + RPCResult::Type::STR, "symbol", "The symbol of the token"}, + RPCExamples{ + HelpExampleCli("qrc20symbol", "\"eb23c0b3e6042821da281a2e2364feb22dd543e3\"") + + HelpExampleRpc("qrc20symbol", "\"eb23c0b3e6042821da281a2e2364feb22dd543e3\"") + }, + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue +{ + // Set contract address + ChainstateManager& chainman = EnsureAnyChainman(request.context); + CallToken token(chainman); + token.setAddress(request.params[0].get_str()); + + // Get symbol + std::string result; + if(!token.symbol(result)) + throw JSONRPCError(RPC_MISC_ERROR, "Fail to get symbol"); + + return result; +}, + }; +} + +static RPCHelpMan qrc20totalsupply() +{ + return RPCHelpMan{"qrc20totalsupply", + "\nReturns the total supply of the token\n", + { + {"contractaddress", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The contract address"}, + }, + RPCResult{ + RPCResult::Type::STR, "totalSupply", "The total supply of the token"}, + RPCExamples{ + HelpExampleCli("qrc20totalsupply", "\"eb23c0b3e6042821da281a2e2364feb22dd543e3\"") + + HelpExampleRpc("qrc20totalsupply", "\"eb23c0b3e6042821da281a2e2364feb22dd543e3\"") + }, + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue +{ + // Set contract address + ChainstateManager& chainman = EnsureAnyChainman(request.context); + CallToken token(chainman); + token.setAddress(request.params[0].get_str()); + + // Get total supply + std::string result; + if(!token.totalSupply(result)) + throw JSONRPCError(RPC_MISC_ERROR, "Fail to get total supply"); + + // Get decimals + uint32_t decimals; + if(!token.decimals(decimals)) + throw JSONRPCError(RPC_MISC_ERROR, "Fail to get decimals"); + + // Check value + dev::s256 value(result); + if(value < 0) + throw JSONRPCError(RPC_MISC_ERROR, "Invalid total supply, value must be positive"); + + return FormatToken(decimals, value); +}, + }; +} + +static RPCHelpMan qrc20decimals() +{ + return RPCHelpMan{"qrc20decimals", + "\nReturns the number of decimals of the token\n", + { + {"contractaddress", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The contract address"}, + }, + RPCResult{ + RPCResult::Type::NUM, "decimals", "The number of decimals of the token"}, + RPCExamples{ + HelpExampleCli("qrc20decimals", "\"eb23c0b3e6042821da281a2e2364feb22dd543e3\"") + + HelpExampleRpc("qrc20decimals", "\"eb23c0b3e6042821da281a2e2364feb22dd543e3\"") + }, + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue +{ + // Set contract address + ChainstateManager& chainman = EnsureAnyChainman(request.context); + CallToken token(chainman); + token.setAddress(request.params[0].get_str()); + uint32_t result; + + // Get decimals + if(!token.decimals(result)) + throw JSONRPCError(RPC_MISC_ERROR, "Fail to get decimals"); + + return (int)result; +}, + }; +} + +static RPCHelpMan qrc20balanceof() +{ + return RPCHelpMan{"qrc20balanceof", + "\nReturns the token balance for address\n", + { + {"contractaddress", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The contract address"}, + {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The qtum address to check token balance"}, + }, + RPCResult{ + RPCResult::Type::STR, "balance", "The token balance of the chosen address"}, + RPCExamples{ + HelpExampleCli("qrc20balanceof", "\"eb23c0b3e6042821da281a2e2364feb22dd543e3\" \"QX1GkJdye9WoUnrE2v6ZQhQ72EUVDtGXQX\"") + + HelpExampleRpc("qrc20balanceof", "\"eb23c0b3e6042821da281a2e2364feb22dd543e3\" \"QX1GkJdye9WoUnrE2v6ZQhQ72EUVDtGXQX\"") + }, + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue +{ + // Get parameters + ChainstateManager& chainman = EnsureAnyChainman(request.context); + CallToken token(chainman); + token.setAddress(request.params[0].get_str()); + std::string sender = request.params[1].get_str(); + token.setSender(sender); + + // Get balance of address + std::string result; + if(!token.balanceOf(result)) + throw JSONRPCError(RPC_MISC_ERROR, "Fail to get balance"); + + // Get decimals + uint32_t decimals; + if(!token.decimals(decimals)) + throw JSONRPCError(RPC_MISC_ERROR, "Fail to get decimals"); + + // Check value + dev::s256 value(result); + if(value < 0) + throw JSONRPCError(RPC_MISC_ERROR, "Invalid balance, vout must be positive"); + + return FormatToken(decimals, value); +}, + }; +} + +static RPCHelpMan qrc20allowance() +{ + return RPCHelpMan{"qrc20allowance", + "\nReturns remaining tokens allowed to spend for an address\n", + { + {"contractaddress", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The contract address"}, + {"addressfrom", RPCArg::Type::STR, RPCArg::Optional::NO, "The qtum address of the account owning tokens"}, + {"addressto", RPCArg::Type::STR, RPCArg::Optional::NO, "The qtum address of the account able to transfer the tokens"}, + }, + RPCResult{ + RPCResult::Type::STR, "allowance", "Amount of remaining tokens allowed to spent"}, + RPCExamples{ + HelpExampleCli("qrc20allowance", "\"eb23c0b3e6042821da281a2e2364feb22dd543e3\" \"QX1GkJdye9WoUnrE2v6ZQhQ72EUVDtGXQX\" \"QM72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\"") + + HelpExampleRpc("qrc20allowance", "\"eb23c0b3e6042821da281a2e2364feb22dd543e3\" \"QX1GkJdye9WoUnrE2v6ZQhQ72EUVDtGXQX\" \"QM72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\"") + }, + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue +{ + // Set contract address + ChainstateManager& chainman = EnsureAnyChainman(request.context); + CallToken token(chainman); + token.setAddress(request.params[0].get_str()); + + // Get total supply + std::string result; + if(!token.allowance(request.params[1].get_str(), request.params[2].get_str(), result)) + throw JSONRPCError(RPC_MISC_ERROR, "Fail to get allowance"); + + // Get decimals + uint32_t decimals; + if(!token.decimals(decimals)) + throw JSONRPCError(RPC_MISC_ERROR, "Fail to get decimals"); + + // Check value + dev::s256 value(result); + if(value < 0) + throw JSONRPCError(RPC_MISC_ERROR, "Invalid allowance, value must be positive"); + + return FormatToken(decimals, value); +}, + }; +} + +static RPCHelpMan qrc20listtransactions() +{ + return RPCHelpMan{"qrc20listtransactions", + "\nReturns transactions history for a specific address.\n", + { + {"contractaddress", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The contract address."}, + {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The qtum address to get history for."}, + {"fromblock", RPCArg::Type::NUM, RPCArg::Default{0}, "The number of the earliest block."}, + {"minconf", RPCArg::Type::NUM, RPCArg::Default{6}, "Minimal number of confirmations."}, + }, + RPCResult{ + RPCResult::Type::ARR, "", "", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR, "receiver", "The receiver qtum address"}, + {RPCResult::Type::STR, "sender", "The sender qtum address"}, + {RPCResult::Type::STR, "amount", "The transferred token amount"}, + {RPCResult::Type::NUM, "confirmations", "The number of confirmations of the most recent transaction included"}, + {RPCResult::Type::STR_HEX, "blockHash", "The block hash"}, + {RPCResult::Type::NUM, "blockNumber", "The block number"}, + {RPCResult::Type::NUM_TIME, "blocktime", "The block time expressed in " + UNIX_EPOCH_TIME + "."}, + {RPCResult::Type::STR_HEX, "transactionHash", "The transaction hash"}, + } + }} + }, + RPCExamples{ + HelpExampleCli("qrc20listtransactions", "\"eb23c0b3e6042821da281a2e2364feb22dd543e3\" \"QX1GkJdye9WoUnrE2v6ZQhQ72EUVDtGXQX\"") + + HelpExampleCli("qrc20listtransactions", "\"eb23c0b3e6042821da281a2e2364feb22dd543e3\" \"QX1GkJdye9WoUnrE2v6ZQhQ72EUVDtGXQX\" 0 6") + + HelpExampleRpc("qrc20listtransactions", "\"eb23c0b3e6042821da281a2e2364feb22dd543e3\" \"QX1GkJdye9WoUnrE2v6ZQhQ72EUVDtGXQX\"") + + HelpExampleRpc("qrc20listtransactions", "\"eb23c0b3e6042821da281a2e2364feb22dd543e3\" \"QX1GkJdye9WoUnrE2v6ZQhQ72EUVDtGXQX\" 0 6") + }, + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue +{ + // Get parameters + ChainstateManager& chainman = EnsureAnyChainman(request.context); + CallToken token(chainman); + token.setAddress(request.params[0].get_str()); + std::string sender = request.params[1].get_str(); + token.setSender(sender); + int64_t fromBlock = 0; + int64_t minconf = 6; + if(!request.params[2].isNull()) + fromBlock = request.params[2].getInt(); + if(!request.params[3].isNull()) + minconf = request.params[3].getInt(); + + // Get transaction events + LOCK(cs_main); + std::vector result; + CChain& active_chain = chainman.ActiveChain(); + int64_t toBlock = active_chain.Height(); + if(!token.transferEvents(result, fromBlock, toBlock, minconf)) + throw JSONRPCError(RPC_MISC_ERROR, "Fail to get transfer events"); + if(!token.burnEvents(result, fromBlock, toBlock, minconf)) + throw JSONRPCError(RPC_MISC_ERROR, "Fail to get burn events"); + + // Get decimals + uint32_t decimals; + if(!token.decimals(decimals)) + throw JSONRPCError(RPC_MISC_ERROR, "Fail to get decimals"); + + // Create transaction list + UniValue res(UniValue::VARR); + for(const auto& event : result){ + UniValue obj(UniValue::VOBJ); + + obj.pushKV("receiver", event.receiver); + obj.pushKV("sender", event.sender); + dev::s256 v = uintTou256(event.value); + dev::s256 value; + if(event.sender == event.receiver) + value = 0; + else if(event.receiver == sender) + value = v; + else + value = -v; + obj.pushKV("amount", FormatToken(decimals, value)); + int confirms = toBlock - event.blockNumber + 1; + obj.pushKV("confirmations", confirms); + obj.pushKV("blockHash", event.blockHash.GetHex()); + obj.pushKV("blockNumber", event.blockNumber); + obj.pushKV("blocktime", active_chain[event.blockNumber]->GetBlockTime()); + obj.pushKV("transactionHash", event.transactionHash.GetHex()); + res.push_back(obj); + } + + return res; +}, + }; +} void RegisterBlockchainRPCCommands(CRPCTable& t) { @@ -3879,6 +4185,8 @@ void RegisterBlockchainRPCCommands(CRPCTable& t) {"blockchain", &gettxoutsetinfo}, {"blockchain", &pruneblockchain}, {"blockchain", &verifychain}, + {"blockchain", &getaccountinfo}, + {"blockchain", &getstorage}, {"blockchain", &preciousblock}, {"blockchain", &scantxoutset}, {"blockchain", &scanblocks}, @@ -3886,6 +4194,21 @@ void RegisterBlockchainRPCCommands(CRPCTable& t) {"blockchain", &dumptxoutset}, {"blockchain", &loadtxoutset}, {"blockchain", &getchainstates}, + {"blockchain", &callcontract}, + {"blockchain", &qrc20name}, + {"blockchain", &qrc20symbol}, + {"blockchain", &qrc20totalsupply}, + {"blockchain", &qrc20decimals}, + {"blockchain", &qrc20balanceof}, + {"blockchain", &qrc20allowance}, + {"blockchain", &qrc20listtransactions}, + {"blockchain", &listcontracts}, + {"blockchain", &gettransactionreceipt}, + {"blockchain", &searchlogs}, + {"blockchain", &waitforlogs}, + {"blockchain", &getestimatedannualroi}, + {"blockchain", &getdelegationinfoforaddress}, + {"blockchain", &getdelegationsforstaker}, {"hidden", &invalidateblock}, {"hidden", &reconsiderblock}, {"hidden", &waitfornewblock}, diff --git a/src/rpc/mempool.cpp b/src/rpc/mempool.cpp index 25bfec2d45..3e41f00651 100644 --- a/src/rpc/mempool.cpp +++ b/src/rpc/mempool.cpp @@ -50,9 +50,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" @@ -74,7 +91,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); } } @@ -94,7 +112,48 @@ 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); + const uint256& txHash = tx->GetHash().ToUint256(); + std::vector txIdAndVout(txHash.begin(), txHash.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 aa2b62c332..bcf99ae5b3 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -40,6 +41,8 @@ #include #include #include +#include +#include #include #include @@ -76,7 +79,7 @@ UniValue GetNetworkHashPS(int lookup, int height, const CChain& active_chain) { // If lookup is -1, then use blocks since last difficulty change. if (lookup == -1) - 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) @@ -105,7 +108,7 @@ UniValue GetNetworkHashPS(int lookup, int height, const CChain& active_chain) { static RPCHelpMan getnetworkhashps() { return RPCHelpMan{"getnetworkhashps", - "\nReturns the estimated network hashes per second based on the last n blocks.\n" + "\nReturns the estimated network hashes per second based on the last n blocks (for PoW only).\n" "Pass in [blocks] to override # of blocks, -1 specifies since last difficulty change.\n" "Pass in [height] to estimate the network speed at the time when a certain block was found.\n", { @@ -216,7 +219,7 @@ static RPCHelpMan generatetodescriptor() "Mine to a specified descriptor and return the block hashes.", { {"num_blocks", RPCArg::Type::NUM, RPCArg::Optional::NO, "How many blocks are generated."}, - {"descriptor", RPCArg::Type::STR, RPCArg::Optional::NO, "The descriptor to send the newly generated bitcoin to."}, + {"descriptor", RPCArg::Type::STR, RPCArg::Optional::NO, "The descriptor to send the newly generated qtum to."}, {"maxtries", RPCArg::Type::NUM, RPCArg::Default{DEFAULT_MAX_TRIES}, "How many iterations to try."}, }, RPCResult{ @@ -260,7 +263,7 @@ static RPCHelpMan generatetoaddress() "Mine to a specified address and return the block hashes.", { {"nblocks", RPCArg::Type::NUM, RPCArg::Optional::NO, "How many blocks are generated."}, - {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The address to send the newly generated bitcoin to."}, + {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The address to send the newly generated qtum to."}, {"maxtries", RPCArg::Type::NUM, RPCArg::Default{DEFAULT_MAX_TRIES}, "How many iterations to try."}, }, RPCResult{ @@ -271,7 +274,7 @@ static RPCHelpMan generatetoaddress() RPCExamples{ "\nGenerate 11 blocks to myaddress\n" + HelpExampleCli("generatetoaddress", "11 \"myaddress\"") - + "If you are using the " PACKAGE_NAME " wallet, you can get a new address to send the newly generated bitcoin to with:\n" + + "If you are using the " PACKAGE_NAME " wallet, you can get a new address to send the newly generated qtum to with:\n" + HelpExampleCli("getnewaddress", "") }, [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue @@ -300,7 +303,7 @@ static RPCHelpMan generateblock() return RPCHelpMan{"generateblock", "Mine a set of ordered transactions to a specified address or descriptor and return the block hash.", { - {"output", RPCArg::Type::STR, RPCArg::Optional::NO, "The address or descriptor to send the newly generated bitcoin to."}, + {"output", RPCArg::Type::STR, RPCArg::Optional::NO, "The address or descriptor to send the newly generated qtum to."}, {"transactions", RPCArg::Type::ARR, RPCArg::Optional::NO, "An array of hex strings which are either txids or raw transactions.\n" "Txids must reference transactions currently in the mempool.\n" "All transactions must be valid and in valid order, otherwise the block will be rejected.", @@ -411,50 +414,34 @@ static RPCHelpMan generateblock() }; } -static RPCHelpMan getmininginfo() +static RPCHelpMan getsubsidy() { - return RPCHelpMan{"getmininginfo", - "\nReturns a json object containing mining-related information.", - {}, + return RPCHelpMan{"getsubsidy", + "\nReturns subsidy value for the specified value of target.", + { + {"height", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "Specify block height."}, + }, RPCResult{ - RPCResult::Type::OBJ, "", "", - { - {RPCResult::Type::NUM, "blocks", "The current block"}, - {RPCResult::Type::NUM, "currentblockweight", /*optional=*/true, "The block weight of the last assembled block (only present if a block was ever assembled)"}, - {RPCResult::Type::NUM, "currentblocktx", /*optional=*/true, "The number of block transactions of the last assembled block (only present if a block was ever assembled)"}, - {RPCResult::Type::NUM, "difficulty", "The current difficulty"}, - {RPCResult::Type::NUM, "networkhashps", "The network hashes per second"}, - {RPCResult::Type::NUM, "pooledtx", "The size of the mempool"}, - {RPCResult::Type::STR, "chain", "current network name (main, test, signet, regtest)"}, - {RPCResult::Type::STR, "warnings", "any network and blockchain warnings"}, - }}, + RPCResult::Type::NUM, "subsidy", "Subsidy value for the specified target" + }, RPCExamples{ - HelpExampleCli("getmininginfo", "") - + HelpExampleRpc("getmininginfo", "") + HelpExampleCli("getsubsidy", "") + + HelpExampleRpc("getsubsidy", "") }, [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { NodeContext& node = EnsureAnyNodeContext(request.context); - const CTxMemPool& mempool = EnsureMemPool(node); ChainstateManager& chainman = EnsureChainman(node); - LOCK(cs_main); - const CChain& active_chain = chainman.ActiveChain(); - UniValue obj(UniValue::VOBJ); - obj.pushKV("blocks", active_chain.Height()); - if (BlockAssembler::m_last_block_weight) obj.pushKV("currentblockweight", *BlockAssembler::m_last_block_weight); - if (BlockAssembler::m_last_block_num_txs) obj.pushKV("currentblocktx", *BlockAssembler::m_last_block_num_txs); - obj.pushKV("difficulty", GetDifficulty(*CHECK_NONFATAL(active_chain.Tip()))); - obj.pushKV("networkhashps", getnetworkhashps().HandleRequest(request)); - obj.pushKV("pooledtx", (uint64_t)mempool.size()); - obj.pushKV("chain", chainman.GetParams().GetChainTypeString()); - obj.pushKV("warnings", GetWarnings(false).original); - return obj; + int nTarget = !request.params[0].isNull() ? request.params[0].getInt() : chainman.ActiveChain().Height(); + if (nTarget < 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); + const Consensus::Params& consensusParams = Params().GetConsensus(); + return (uint64_t)GetBlockSubsidy(nTarget, consensusParams); }, }; } - // NOTE: Unlike wallet RPC (which use BTC values), mining RPCs follow GBT (BIP 22) in using satoshi amounts static RPCHelpMan prioritisetransaction() { @@ -939,8 +926,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; @@ -950,7 +937,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)); @@ -1099,12 +1086,12 @@ void RegisterMiningRPCCommands(CRPCTable& t) { static const CRPCCommand commands[]{ {"mining", &getnetworkhashps}, - {"mining", &getmininginfo}, {"mining", &prioritisetransaction}, {"mining", &getprioritisedtransactions}, {"mining", &getblocktemplate}, {"mining", &submitblock}, {"mining", &submitheader}, + {"mining", &getsubsidy}, {"hidden", &generatetoaddress}, {"hidden", &generatetodescriptor}, diff --git a/src/rpc/node.cpp b/src/rpc/node.cpp index 45053f882d..fd4c2a0153 100644 --- a/src/rpc/node.cpp +++ b/src/rpc/node.cpp @@ -26,6 +26,10 @@ #include #include #include +#include +#include +#include +#include #include #ifdef HAVE_MALLOC_INFO @@ -244,15 +248,15 @@ static RPCHelpMan logging() }, [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { - uint32_t original_log_categories = LogInstance().GetCategoryMask(); + uint64_t original_log_categories = LogInstance().GetCategoryMask(); if (request.params[0].isArray()) { EnableOrDisableLogCategories(request.params[0], true); } if (request.params[1].isArray()) { EnableOrDisableLogCategories(request.params[1], false); } - uint32_t updated_log_categories = LogInstance().GetCategoryMask(); - uint32_t changed_log_categories = original_log_categories ^ updated_log_categories; + uint64_t updated_log_categories = LogInstance().GetCategoryMask(); + uint64_t changed_log_categories = original_log_categories ^ updated_log_categories; // Update libevent logging if BCLog::LIBEVENT has changed. if (changed_log_categories & BCLog::LIBEVENT) { @@ -275,7 +279,7 @@ static RPCHelpMan echo(const std::string& name) "\nSimply echo back the input arguments. This command is for testing.\n" "\nIt will return an internal bug report when arg9='trigger_internal_bug' is passed.\n" "\nThe difference between echo and echojson is that echojson has argument conversion enabled in the client-side table in " - "bitcoin-cli and the GUI. There is no server-side difference.", + "qtum-cli and the GUI. There is no server-side difference.", { {"arg0", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "", RPCArgOptions{.skip_type_check = true}}, {"arg1", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "", RPCArgOptions{.skip_type_check = true}}, @@ -326,7 +330,7 @@ static RPCHelpMan echoipc() // and spawn bitcoin-echo below instead of bitcoin-node. But // using bitcoin-node avoids the need to build and install a // new executable just for this one test. - auto init = ipc->spawnProcess("bitcoin-node"); + auto init = ipc->spawnProcess("qtum-node"); echo = init->makeEcho(); ipc->addCleanup(*echo, [init = init.release()] { delete init; }); } else { @@ -400,6 +404,433 @@ static RPCHelpMan getindexinfo() }; } +static RPCHelpMan getdgpinfo() +{ + return RPCHelpMan{"getdgpinfo", + "\nReturns an object containing DGP state info.\n", + {}, + RPCResult{ + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::NUM, "maxblocksize", "Current maximum block size"}, + {RPCResult::Type::NUM, "mingasprice", "Current minimum gas price"}, + {RPCResult::Type::NUM, "blockgaslimit", "Current block gas limit"}, + } + }, + RPCExamples{ + HelpExampleCli("getdgpinfo", "") + + HelpExampleRpc("getdgpinfo", "") + }, + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue +{ + + ChainstateManager& chainman = EnsureAnyChainman(request.context); + LOCK(cs_main); + + CChain& active_chain = chainman.ActiveChain(); + QtumDGP qtumDGP(globalState.get(), chainman.ActiveChainstate()); + + UniValue obj(UniValue::VOBJ); + obj.pushKV("maxblocksize", (uint64_t)qtumDGP.getBlockSize(active_chain.Height())); + obj.pushKV("mingasprice", (uint64_t)qtumDGP.getMinGasPrice(active_chain.Height())); + obj.pushKV("blockgaslimit", (uint64_t)qtumDGP.getBlockGasLimit(active_chain.Height())); + + return obj; +}, + }; +} + +static RPCHelpMan getblockhashes() +{ + return RPCHelpMan{"getblockhashes", + "\nReturns array of hashes of blocks within the timestamp range provided.\n", + { + {"high", RPCArg::Type::NUM, RPCArg::Optional::NO, "The newer block timestamp"}, + {"low", RPCArg::Type::NUM, RPCArg::Optional::NO, "The older block timestamp"}, + {"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "An object with options", + { + {"noOrphans", RPCArg::Type::BOOL, RPCArg::Default{"false"}, "Will only include blocks on the main chain"}, + {"logicalTimes", RPCArg::Type::BOOL, RPCArg::Default{"false"}, "Will include logical timestamps with hashes"}, + }, + }, + }, + { + RPCResult{"if logicalTimes is set to false", + RPCResult::Type::ARR, "", "", + { + {RPCResult::Type::STR_HEX, "", "The block hash"} + }, + }, + RPCResult{"if logicalTimes is set to true", + RPCResult::Type::ARR, "", "", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR_HEX, "blockhash", "The block hash"}, + {RPCResult::Type::NUM, "logicalts", "The logical timestamp"}, + }} + }, + }, + }, + RPCExamples{ + HelpExampleCli("getblockhashes", "1231614698 1231024505") + + HelpExampleCli("getblockhashes", "1231614698 1231024505 '{\"noOrphans\":false, \"logicalTimes\":true}'") + + HelpExampleRpc("getblockhashes", "1231614698, 1231024505") + }, + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue +{ + + ChainstateManager& chainman = EnsureAnyChainman(request.context); + + unsigned int high = request.params[0].getInt(); + unsigned int low = request.params[1].getInt(); + bool fActiveOnly = false; + bool fLogicalTS = false; + + if (!request.params[2].isNull()) { + if (request.params[2].isObject()) { + UniValue noOrphans = request.params[2].get_obj().find_value("noOrphans"); + UniValue returnLogical = request.params[2].get_obj().find_value("logicalTimes"); + + if (noOrphans.isBool()) + fActiveOnly = noOrphans.get_bool(); + + if (returnLogical.isBool()) + fLogicalTS = returnLogical.get_bool(); + } + } + + std::vector > blockHashes; + bool found = false; + + found = GetTimestampIndex(high, low, fActiveOnly, blockHashes, chainman); + + if (!found) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for block hashes"); + } + + UniValue result(UniValue::VARR); + + for (std::vector >::const_iterator it=blockHashes.begin(); it!=blockHashes.end(); it++) { + if (fLogicalTS) { + UniValue item(UniValue::VOBJ); + item.pushKV("blockhash", it->first.GetHex()); + item.pushKV("logicalts", (int)it->second); + result.push_back(item); + } else { + result.push_back(it->first.GetHex()); + } + } + + return result; +}, + }; +} + +bool getAddressesFromParams(const UniValue& params, std::vector > &addresses) +{ + if (params[0].isStr()) { + uint256 hashBytes; + int type = 0; + if (!DecodeIndexKey(params[0].get_str(), hashBytes, type)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); + } + addresses.push_back(std::make_pair(hashBytes, type)); + } else if (params[0].isObject()) { + + UniValue addressValues = params[0].get_obj().find_value("addresses"); + if (!addressValues.isArray()) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Addresses is expected to be an array"); + } + + std::vector values = addressValues.getValues(); + + for (std::vector::iterator it = values.begin(); it != values.end(); ++it) { + + uint256 hashBytes; + int type = 0; + if (!DecodeIndexKey(it->get_str(), hashBytes, type)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); + } + addresses.push_back(std::make_pair(hashBytes, type)); + } + } else { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); + } + + return true; +} + +bool heightSort(std::pair a, + std::pair b) { + return a.second.blockHeight < b.second.blockHeight; +} + +bool timestampSort(std::pair a, + std::pair b) { + return a.second.time < b.second.time; +} + +bool getAddressFromIndex(const int &type, const uint256 &hash, std::string &address) +{ + if (type == 2) { + std::vector addressBytes(hash.begin(), hash.begin() + 20); + address = EncodeDestination(ScriptHash(uint160(addressBytes))); + } else if (type == 1) { + std::vector addressBytes(hash.begin(), hash.begin() + 20); + address = EncodeDestination(PKHash(uint160(addressBytes))); + } else if (type == 3) { + address = EncodeDestination(WitnessV0ScriptHash(hash)); + } else if (type == 4) { + std::vector addressBytes(hash.begin(), hash.begin() + 20); + address = EncodeDestination(WitnessV0KeyHash(uint160(addressBytes))); + } else if (type == 5) { + WitnessV1Taproot tap; + std::copy(hash.begin(), hash.end(), tap.begin()); + address = EncodeDestination(tap); + } else { + return false; + } + return true; +} + +static RPCHelpMan getaddressdeltas() +{ + return RPCHelpMan{"getaddressdeltas", + "\nReturns all changes for an address (requires addressindex to be enabled).\n", + { + {"argument", RPCArg::Type::OBJ, RPCArg::Optional::NO, "Json object", + { + {"addresses", RPCArg::Type::ARR, RPCArg::Optional::NO, "The qtum addresses", + { + {"address", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "The qtum address"}, + } + }, + {"start", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The start block height"}, + {"end", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The end block height"}, + {"chainInfo", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED, "Include chain info in results, only applies if start and end specified"}, + } + } + }, + { + RPCResult{"if chainInfo is set to false", + RPCResult::Type::ARR, "", "", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::NUM, "satoshis", "The difference of satoshis"}, + {RPCResult::Type::STR_HEX, "txid", "The related txid"}, + {RPCResult::Type::NUM, "index", "The related input or output index"}, + {RPCResult::Type::NUM, "blockindex", "The transaction index in block"}, + {RPCResult::Type::NUM, "height", "The block height"}, + {RPCResult::Type::STR, "address", "The qtum address"}, + }} + }, + }, + RPCResult{"if chainInfo is set to true", + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::ARR, "deltas", "List of delta", + { + {RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::NUM, "satoshis", "The difference of satoshis"}, + {RPCResult::Type::STR_HEX, "txid", "The related txid"}, + {RPCResult::Type::NUM, "index", "The related input or output index"}, + {RPCResult::Type::NUM, "blockindex", "The transaction index in block"}, + {RPCResult::Type::NUM, "height", "The block height"}, + {RPCResult::Type::STR, "address", "The qtum address"}, + }} + }}, + {RPCResult::Type::OBJ, "start", "Start block", + { + {RPCResult::Type::STR_HEX, "hash", "The block hash"}, + {RPCResult::Type::NUM, "height", "The block height"}, + }}, + {RPCResult::Type::OBJ, "end", "End block", + { + {RPCResult::Type::STR_HEX, "hash", "The block hash"}, + {RPCResult::Type::NUM, "height", "The block height"}, + }}, + }, + }, + }, + RPCExamples{ + HelpExampleCli("getaddressdeltas", "'{\"addresses\": [\"QD1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\"]}'") + + HelpExampleRpc("getaddressdeltas", "{\"addresses\": [\"QD1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\"]}") + + HelpExampleCli("getaddressdeltas", "'{\"addresses\": [\"QD1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\"], \"start\": 5000, \"end\": 5500, \"chainInfo\": true}'") + + HelpExampleRpc("getaddressdeltas", "{\"addresses\": [\"QD1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\"], \"start\": 5000, \"end\": 5500, \"chainInfo\": true}") + }, + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue +{ + ChainstateManager& chainman = EnsureAnyChainman(request.context); + + UniValue startValue = request.params[0].get_obj().find_value("start"); + UniValue endValue = request.params[0].get_obj().find_value("end"); + + UniValue chainInfo = request.params[0].get_obj().find_value("chainInfo"); + bool includeChainInfo = false; + if (chainInfo.isBool()) { + includeChainInfo = chainInfo.get_bool(); + } + + int start = 0; + int end = 0; + + if (startValue.isNum() && endValue.isNum()) { + start = startValue.getInt(); + end = endValue.getInt(); + if (start <= 0 || end <= 0) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Start and end is expected to be greater than zero"); + } + if (end < start) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "End value is expected to be greater than start"); + } + } + + std::vector > addresses; + + if (!getAddressesFromParams(request.params, addresses)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); + } + + std::vector > addressIndex; + + for (std::vector >::iterator it = addresses.begin(); it != addresses.end(); it++) { + if (start > 0 && end > 0) { + if (!GetAddressIndex((*it).first, (*it).second, addressIndex, chainman.m_blockman, start, end)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address"); + } + } else { + if (!GetAddressIndex((*it).first, (*it).second, addressIndex, chainman.m_blockman)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address"); + } + } + } + + UniValue deltas(UniValue::VARR); + + for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) { + std::string address; + if (!getAddressFromIndex(it->first.type, it->first.hashBytes, address)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unknown address type"); + } + + UniValue delta(UniValue::VOBJ); + delta.pushKV("satoshis", it->second); + delta.pushKV("txid", it->first.txhash.GetHex()); + delta.pushKV("index", (int)it->first.index); + delta.pushKV("blockindex", (int)it->first.txindex); + delta.pushKV("height", it->first.blockHeight); + delta.pushKV("address", address); + deltas.push_back(delta); + } + + UniValue result(UniValue::VOBJ); + + if (includeChainInfo && start > 0 && end > 0) { + ChainstateManager& chainman = EnsureAnyChainman(request.context); + LOCK(cs_main); + + CChain& active_chain = chainman.ActiveChain(); + if (start > active_chain.Height() || end > active_chain.Height()) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Start or end is outside chain range"); + } + + CBlockIndex* startIndex = active_chain[start]; + CBlockIndex* endIndex = active_chain[end]; + + UniValue startInfo(UniValue::VOBJ); + UniValue endInfo(UniValue::VOBJ); + + startInfo.pushKV("hash", startIndex->GetBlockHash().GetHex()); + startInfo.pushKV("height", start); + + endInfo.pushKV("hash", endIndex->GetBlockHash().GetHex()); + endInfo.pushKV("height", end); + + result.pushKV("deltas", deltas); + result.pushKV("start", startInfo); + result.pushKV("end", endInfo); + + return result; + } else { + return deltas; + } +}, + }; +} + +static RPCHelpMan getaddressbalance() +{ + return RPCHelpMan{"getaddressbalance", + "\nReturns the balance for an address(es) (requires addressindex to be enabled).\n", + { + {"argument", RPCArg::Type::OBJ, RPCArg::Optional::NO, "Json object", + { + {"addresses", RPCArg::Type::ARR, RPCArg::Optional::NO, "The qtum addresses", + { + {"address", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "The qtum address"}, + } + }, + } + } + }, + RPCResult{ + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::NUM, "balance", "The current balance in satoshis"}, + {RPCResult::Type::NUM, "received", "The total number of satoshis received (including change)"}, + {RPCResult::Type::NUM, "immature", "The immature balance in satoshis"}, + } + }, + RPCExamples{ + HelpExampleCli("getaddressbalance", "'{\"addresses\": [\"QD1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\"]}'") + + HelpExampleRpc("getaddressbalance", "{\"addresses\": [\"QD1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\"]}") + }, + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue +{ + ChainstateManager& chainman = EnsureAnyChainman(request.context); + + std::vector > addresses; + + if (!getAddressesFromParams(request.params, addresses)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); + } + + std::vector > addressIndex; + + for (std::vector >::iterator it = addresses.begin(); it != addresses.end(); it++) { + if (!GetAddressIndex((*it).first, (*it).second, addressIndex, chainman.m_blockman)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address"); + } + } + + CAmount balance = 0; + CAmount received = 0; + CAmount immature = 0; + + LOCK(cs_main); + CChain& active_chain = chainman.ActiveChain(); + for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) { + if (it->second > 0) { + received += it->second; + } + balance += it->second; + int nHeight = active_chain.Height(); + if (it->first.txindex == 1 && ((nHeight - it->first.blockHeight) < Params().GetConsensus().CoinbaseMaturity(nHeight))) + immature += it->second; //immature stake outputs + } + + UniValue result(UniValue::VOBJ); + result.pushKV("balance", balance); + result.pushKV("received", received); + result.pushKV("immature", immature); + + return result; +}, + }; +} void RegisterNodeRPCCommands(CRPCTable& t) { static const CRPCCommand commands[]{ diff --git a/src/rpc/output_script.cpp b/src/rpc/output_script.cpp index f9343f48a8..8eb023214b 100644 --- a/src/rpc/output_script.cpp +++ b/src/rpc/output_script.cpp @@ -29,15 +29,15 @@ static RPCHelpMan validateaddress() { return RPCHelpMan{ "validateaddress", - "\nReturn information about the given bitcoin address.\n", + "\nReturn information about the given qtum address.\n", { - {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address to validate"}, + {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The qtum address to validate"}, }, RPCResult{ RPCResult::Type::OBJ, "", "", { {RPCResult::Type::BOOL, "isvalid", "If the address is valid or not"}, - {RPCResult::Type::STR, "address", /*optional=*/true, "The bitcoin address validated"}, + {RPCResult::Type::STR, "address", /*optional=*/true, "The qtum address validated"}, {RPCResult::Type::STR_HEX, "scriptPubKey", /*optional=*/true, "The hex-encoded scriptPubKey generated by the address"}, {RPCResult::Type::BOOL, "isscript", /*optional=*/true, "If the key is a script"}, {RPCResult::Type::BOOL, "iswitness", /*optional=*/true, "If the address is a witness address"}, @@ -85,89 +85,6 @@ static RPCHelpMan validateaddress() }; } -static RPCHelpMan createmultisig() -{ - return RPCHelpMan{"createmultisig", - "\nCreates a multi-signature address with n signature of m keys required.\n" - "It returns a json object with the address and redeemScript.\n", - { - {"nrequired", RPCArg::Type::NUM, RPCArg::Optional::NO, "The number of required signatures out of the n keys."}, - {"keys", RPCArg::Type::ARR, RPCArg::Optional::NO, "The hex-encoded public keys.", - { - {"key", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "The hex-encoded public key"}, - }}, - {"address_type", RPCArg::Type::STR, RPCArg::Default{"legacy"}, "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."}, - }, - RPCResult{ - RPCResult::Type::OBJ, "", "", - { - {RPCResult::Type::STR, "address", "The value of the new multisig address."}, - {RPCResult::Type::STR_HEX, "redeemScript", "The string value of the hex-encoded redemption script."}, - {RPCResult::Type::STR, "descriptor", "The descriptor for this multisig"}, - {RPCResult::Type::ARR, "warnings", /*optional=*/true, "Any warnings resulting from the creation of this multisig", - { - {RPCResult::Type::STR, "", ""}, - }}, - } - }, - RPCExamples{ - "\nCreate a multisig address from 2 public keys\n" - + HelpExampleCli("createmultisig", "2 \"[\\\"03789ed0bb717d88f7d321a368d905e7430207ebbd82bd342cf11ae157a7ace5fd\\\",\\\"03dbc6764b8884a92e871274b87583e6d5c2a58819473e17e107ef3f6aa5a61626\\\"]\"") + - "\nAs a JSON-RPC call\n" - + HelpExampleRpc("createmultisig", "2, [\"03789ed0bb717d88f7d321a368d905e7430207ebbd82bd342cf11ae157a7ace5fd\",\"03dbc6764b8884a92e871274b87583e6d5c2a58819473e17e107ef3f6aa5a61626\"]") - }, - [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue - { - int required = request.params[0].getInt(); - - // Get the public keys - const UniValue& keys = request.params[1].get_array(); - std::vector pubkeys; - for (unsigned int i = 0; i < keys.size(); ++i) { - if (IsHex(keys[i].get_str()) && (keys[i].get_str().length() == 66 || keys[i].get_str().length() == 130)) { - pubkeys.push_back(HexToPubKey(keys[i].get_str())); - } else { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Invalid public key: %s\n.", keys[i].get_str())); - } - } - - // Get the output type - OutputType output_type = OutputType::LEGACY; - if (!request.params[2].isNull()) { - std::optional parsed = ParseOutputType(request.params[2].get_str()); - if (!parsed) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[2].get_str())); - } else if (parsed.value() == OutputType::BECH32M) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "createmultisig cannot create bech32m multisig addresses"); - } - output_type = parsed.value(); - } - - // Construct using pay-to-script-hash: - FillableSigningProvider keystore; - CScript inner; - const CTxDestination dest = AddAndGetMultisigDestination(required, pubkeys, output_type, keystore, inner); - - // Make the descriptor - std::unique_ptr descriptor = InferDescriptor(GetScriptForDestination(dest), keystore); - - UniValue result(UniValue::VOBJ); - result.pushKV("address", EncodeDestination(dest)); - result.pushKV("redeemScript", HexStr(inner)); - result.pushKV("descriptor", descriptor->ToString()); - - UniValue warnings(UniValue::VARR); - if (descriptor->GetOutputType() != output_type) { - // Only warns if the user has explicitly chosen an address type we cannot generate - warnings.push_back("Unable to make chosen address type, please ensure no uncompressed public keys are present."); - } - PushWarnings(warnings, result); - - return result; - }, - }; -} - static RPCHelpMan getdescriptorinfo() { const std::string EXAMPLE_DESCRIPTOR = "wpkh([d34db33f/84h/0h/0h]0279be667ef9dcbbac55a06295Ce870b07029Bfcdb2dce28d959f2815b16f81798)"; @@ -306,7 +223,6 @@ void RegisterOutputScriptRPCCommands(CRPCTable& t) { static const CRPCCommand commands[]{ {"util", &validateaddress}, - {"util", &createmultisig}, {"util", &deriveaddresses}, {"util", &getdescriptorinfo}, }; diff --git a/src/validation.cpp b/src/validation.cpp index 13c05c630c..419f7844df 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -6211,11 +6211,48 @@ std::pair ChainstateManager::GetPruneRange(const Chainstate& chainstat return {prune_start, prune_end}; } +////////////////////////////////////////////////////////////////////////////////// // qtum +bool GetAddressIndex(uint256 addressHash, int type, std::vector > &addressIndex, node::BlockManager& blockman, int start, int end) +{ + if (!fAddressIndex) + return error("address index not enabled"); + + if (!blockman.m_block_tree_db->ReadAddressIndex(addressHash, type, addressIndex, start, end)) + return error("unable to get txids for address"); + + return true; +} + +bool GetSpentIndex(CSpentIndexKey &key, CSpentIndexValue &value, const CTxMemPool& mempool, node::BlockManager& blockman) +{ + if (!fAddressIndex) + return false; + + if (mempool.getSpentIndex(key, value)) + return true; + + if (!blockman.m_block_tree_db->ReadSpentIndex(key, value)) + return false; + + return true; +} + bool GetAddressUnspent(uint256 addressHash, int type, std::vector > &unspentOutputs, node::BlockManager& blockman) { return {}; } +bool GetTimestampIndex(const unsigned int &high, const unsigned int &low, const bool fActiveOnly, std::vector > &hashes, ChainstateManager& chainman) +{ + if (!fAddressIndex) + return error("Timestamp index not enabled"); + + if (!chainman.m_blockman.m_block_tree_db->ReadTimestampIndex(high, low, fActiveOnly, hashes, chainman)) + return error("Unable to get hashes for timestamps"); + + return true; +} + bool GetAddressWeight(uint256 addressHash, int type, const std::map& immatureStakes, int32_t nHeight, uint64_t& nWeight, node::BlockManager& blockman) { return {}; diff --git a/src/validation.h b/src/validation.h index ba0ea05389..0f153f9456 100644 --- a/src/validation.h +++ b/src/validation.h @@ -410,9 +410,18 @@ static_assert(std::is_nothrow_destructible_v); /** Initializes the script-execution cache */ [[nodiscard]] bool InitScriptExecutionCache(size_t max_size_bytes); +///////////////////////////////////////////////////////////////// // qtum +bool GetAddressIndex(uint256 addressHash, int type, + std::vector > &addressIndex, node::BlockManager& blockman, + int start = 0, int end = 0); + +bool GetSpentIndex(CSpentIndexKey &key, CSpentIndexValue &value, const CTxMemPool& mempool, node::BlockManager& blockman); + bool GetAddressUnspent(uint256 addressHash, int type, std::vector > &unspentOutputs, node::BlockManager& blockman); +bool GetTimestampIndex(const unsigned int &high, const unsigned int &low, const bool fActiveOnly, std::vector > &hashes, ChainstateManager& chainman); + bool GetAddressWeight(uint256 addressHash, int type, const std::map& immatureStakes, int32_t nHeight, uint64_t& nWeight, node::BlockManager& blockman); std::map GetImmatureStakes(ChainstateManager& chainman); diff --git a/src/wallet/rpc/mining.cpp b/src/wallet/rpc/mining.cpp index 3bbd71ae35..0cc3b3c5a0 100644 --- a/src/wallet/rpc/mining.cpp +++ b/src/wallet/rpc/mining.cpp @@ -86,8 +86,8 @@ RPCHelpMan getmininginfo() if (BlockAssembler::m_last_block_weight) obj.pushKV("currentblockweight", *BlockAssembler::m_last_block_weight); if (BlockAssembler::m_last_block_num_txs) obj.pushKV("currentblocktx", *BlockAssembler::m_last_block_num_txs); - diff.pushKV("proof-of-work", (double)GetDifficulty(*CHECK_NONFATAL(GetLastBlockIndex(chainman.m_best_header, false)))); - diff.pushKV("proof-of-stake", (double)GetDifficulty(*CHECK_NONFATAL(GetLastBlockIndex(chainman.m_best_header, true)))); + diff.pushKV("proof-of-work", GetDifficulty(*CHECK_NONFATAL(GetLastBlockIndex(chainman.m_best_header, false)))); + diff.pushKV("proof-of-stake", GetDifficulty(*CHECK_NONFATAL(GetLastBlockIndex(chainman.m_best_header, true)))); diff.pushKV("search-interval", (int)lastCoinStakeSearchInterval); obj.pushKV("difficulty", diff); @@ -173,7 +173,7 @@ RPCHelpMan getstakinginfo() if (BlockAssembler::m_last_block_num_txs) obj.pushKV("currentblocktx", *BlockAssembler::m_last_block_num_txs); obj.pushKV("pooledtx", (uint64_t)mempool.size()); - obj.pushKV("difficulty", (double)GetDifficulty(*CHECK_NONFATAL(GetLastBlockIndex(chainman.m_best_header, true)))); + obj.pushKV("difficulty", GetDifficulty(*CHECK_NONFATAL(GetLastBlockIndex(chainman.m_best_header, true)))); obj.pushKV("search-interval", (int)lastCoinStakeSearchInterval); obj.pushKV("weight", (uint64_t)nStakerWeight);