diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 4b8bcb1e3c..a814baba1f 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -265,6 +265,8 @@ static const CRPCConvertParam vRPCConvertParams[] = { "echojson", 9, "arg9" }, { "rescanblockchain", 0, "start_height"}, { "rescanblockchain", 1, "stop_height"}, + { "checkkernel", 0, "inputs" }, + { "checkkernel", 1, "createblocktemplate" }, { "createwallet", 1, "disable_private_keys"}, { "createwallet", 2, "blank"}, { "createwallet", 4, "avoid_reuse"}, diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 6dfd08f8e4..7875be3d7a 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -1021,157 +1021,6 @@ static RPCHelpMan submitheader() }; } -// Blackcoin: checkkernel RPC -// Blackcoin ToDo: check and fix if needed -/* -static RPCHelpMan checkkernel() -{ - return RPCHelpMan{"checkkernel", - "\nCheck if one of given inputs is a kernel input at the moment.\n", - { - {"inputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "The inputs", - { - {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "", - { - {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"}, - {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"}, - {"sequence", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "depends on the value of the 'locktime' argument", "The sequence number"}, - }}, - }, - }, - {"createblocktemplate", RPCArg::Type::BOOL, RPCArg::Default{false}, "Create block template?"}, - }, - RPCResult{ - RPCResult::Type::OBJ, "", "", - { - {RPCResult::Type::BOOL, "found", "?"}, - {RPCResult::Type::OBJ, "kernel", "", - { - {RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"}, - {RPCResult::Type::NUM, "vout", "?"}, - {RPCResult::Type::NUM, "time", "?"}, - }}, - {RPCResult::Type::STR_HEX, "blocktemplate", "?"}, - {RPCResult::Type::NUM, "blocktemplatefees", "?"}, - }, - }, - RPCExamples{ - HelpExampleCli("checkkernel", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"false\"") - + HelpExampleCli("checkkernel", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"true\"") - }, - [&](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(); - Chainstate& active_chainstate = chainman.ActiveChainstate(); - - UniValue inputs = request.params[0].get_array(); - bool fCreateBlockTemplate = request.params.size() > 1 ? request.params[1].get_bool() : false; - - if (!Params().IsTestChain()) { - const CConnman& connman = EnsureConnman(node); - if (connman.GetNodeCount(ConnectionDirection::Both) == 0) { - throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, PACKAGE_NAME " is not connected!"); - } - - if (active_chainstate.IsInitialBlockDownload()) { - throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, PACKAGE_NAME " is in initial sync and waiting for blocks..."); - } - } - - COutPoint kernel; - CBlockIndex* pindexPrev = active_chain.Tip(); - unsigned int nBits = GetNextTargetRequired(pindexPrev, Params().GetConsensus(), true); - int64_t nTime = GetAdjustedTimeSeconds(); - nTime &= ~Params().GetConsensus().nStakeTimestampMask; - - for (unsigned int idx = 0; idx < inputs.size(); idx++) { - const UniValue& input = inputs[idx]; - const UniValue& o = input.get_obj(); - - const UniValue& txid_v = find_value(o, "txid"); - if (!txid_v.isStr()) - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing txid key"); - string txid = txid_v.get_str(); - if (!IsHex(txid)) - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected hex txid"); - - const UniValue& vout_v = find_value(o, "vout"); - if (!vout_v.isNum()) - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key"); - int nOutput = vout_v.getInt(); - if (nOutput < 0) - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive"); - - COutPoint cInput(uint256S(txid), nOutput); - if (CheckKernel(pindexPrev, nBits, nTime, cInput, active_chainstate.CoinsTip())) - { - kernel = cInput; - break; - } - } - - UniValue result(UniValue::VOBJ); - result.pushKV("found", !kernel.IsNull()); - - if (kernel.IsNull()) - return result; - - UniValue oKernel(UniValue::VOBJ); - oKernel.pushKV("txid", kernel.hash.GetHex()); - oKernel.pushKV("vout", (int64_t)kernel.n); - oKernel.pushKV("time", nTime); - result.pushKV("kernel", oKernel); - - if (!fCreateBlockTemplate) - return result; - -#ifdef ENABLE_WALLET - std::shared_ptr const wallet = wallet::GetWalletForJSONRPCRequest(request); - CWallet* const pwallet = wallet.get(); - - if (!pwallet) - return result; - - if (!pwallet->IsLocked()) - pwallet->TopUpKeyPool(); - - std::unique_ptr pblocktemplate; - bool fPoSCancel = false; - int64_t nFees; - pblocktemplate = BlockAssembler{active_chainstate, &mempool}.CreateNewBlock(CScript(), pwallet, &fPoSCancel, &nFees); - - if (!pblocktemplate) - throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block"); - - CBlock *pblock = &pblocktemplate->block; - CMutableTransaction coinstakeTx(*pblock->vtx[0]); - pblock->nTime = coinstakeTx.nTime = nTime; - pblock->vtx[0] = MakeTransactionRef(std::move(coinstakeTx)); - - CDataStream ss(SER_DISK, PROTOCOL_VERSION); - ss << *pblock; - - result.pushKV("blocktemplate", HexStr(ss)); - result.pushKV("blocktemplatefees", nFees); - - // Blackcoin: the reserved key concept is not used in modern Bitcoin Core - CPubKey pubkey; - if (!pMiningKey->GetReservedKey(pubkey)) - throw JSONRPCError(RPC_MISC_ERROR, "GetReservedKey failed"); - - result.push_back(Pair("blocktemplatesignkey", HexStr(pubkey))); - -#endif - return result; -}, - }; -} -*/ - void RegisterMiningRPCCommands(CRPCTable& t) { static const CRPCCommand commands[]{ @@ -1185,10 +1034,6 @@ void RegisterMiningRPCCommands(CRPCTable& t) {"hidden", &generatetodescriptor}, {"hidden", &generateblock}, {"hidden", &generate}, - - /* - {"staking", &checkkernel}, - */ }; for (const auto& c : commands) { t.appendCommand(c.name, &c); diff --git a/src/wallet/rpc/staking.cpp b/src/wallet/rpc/staking.cpp index 44058744e0..1efe17ca34 100644 --- a/src/wallet/rpc/staking.cpp +++ b/src/wallet/rpc/staking.cpp @@ -11,6 +11,8 @@ #include #include #include +#include // For EncodeDestination +#include // For GetNextTargetRequired #include #include @@ -203,6 +205,161 @@ static RPCHelpMan reservebalance() }; } +static RPCHelpMan checkkernel() +{ + return RPCHelpMan{"checkkernel", + "\nCheck if one of given inputs is a kernel input at the moment.\n", + { + {"inputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "The inputs", + { + {"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "", + { + {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"}, + {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"}, + {"sequence", RPCArg::Type::NUM, RPCArg::DefaultHint{"depends on the value of the 'locktime' argument"}, "The sequence number"}, + }, + }, + }, + }, + {"createblocktemplate", RPCArg::Type::BOOL, RPCArg::Default{false}, "Create block template?"}, + }, + RPCResult{ + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::BOOL, "found", "?"}, + {RPCResult::Type::OBJ, "kernel", "", + { + {RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"}, + {RPCResult::Type::NUM, "vout", "?"}, + {RPCResult::Type::NUM, "time", "?"}, + }}, + {RPCResult::Type::STR_HEX, "blocktemplate", "?"}, + {RPCResult::Type::NUM, "blocktemplatefees", "?"}, + }, + }, + RPCExamples{ + HelpExampleCli("checkkernel", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"false\"") + + HelpExampleCli("checkkernel", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"true\"") + }, + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue +{ + std::shared_ptr const pwallet = GetWalletForJSONRPCRequest(request); + if (!pwallet) return NullUniValue; + + const CTxMemPool& mempool = pwallet->chain().mempool(); + ChainstateManager& chainman = pwallet->chain().chainman(); + LOCK(cs_main); + const CChain& active_chain = chainman.ActiveChain(); + Chainstate& active_chainstate = chainman.ActiveChainstate(); + + UniValue inputs = request.params[0].get_array(); + bool fCreateBlockTemplate = request.params.size() > 1 ? request.params[1].get_bool() : false; + + if (!Params().IsTestChain()) { + if (pwallet->chain().getNodeCount(ConnectionDirection::Both) == 0) { + throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, PACKAGE_NAME " is not connected!"); + } + + if (chainman.IsInitialBlockDownload()) { + throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, PACKAGE_NAME " is in initial sync and waiting for blocks..."); + } + } + + COutPoint kernel; + CBlockIndex* pindexPrev = active_chain.Tip(); + unsigned int nBits = GetNextTargetRequired(pindexPrev, Params().GetConsensus(), true); + int64_t nTime = GetAdjustedTimeSeconds(); + nTime &= ~Params().GetConsensus().nStakeTimestampMask; + + for (unsigned int idx = 0; idx < inputs.size(); idx++) { + const UniValue& o = inputs[idx].get_obj(); + + const UniValue& txid_v = o.find_value("txid"); + if (!txid_v.isStr()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing txid key"); + string txid = txid_v.get_str(); + if (!IsHex(txid)) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected hex txid"); + + const UniValue& vout_v = o.find_value("vout"); + if (!vout_v.isNum()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key"); + int nOutput = vout_v.getInt(); + if (nOutput < 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive"); + + COutPoint cInput(uint256S(txid), nOutput); + if (CheckKernel(pindexPrev, nBits, nTime, cInput, active_chainstate.CoinsTip())) + { + kernel = cInput; + break; + } + } + + UniValue result(UniValue::VOBJ); + result.pushKV("found", !kernel.IsNull()); + + if (kernel.IsNull()) + return result; + + UniValue oKernel(UniValue::VOBJ); + oKernel.pushKV("txid", kernel.hash.GetHex()); + oKernel.pushKV("vout", (int64_t)kernel.n); + oKernel.pushKV("time", nTime); + result.pushKV("kernel", oKernel); + + if (!fCreateBlockTemplate) + return result; + + if (!pwallet->IsLocked()) + pwallet->TopUpKeyPool(); + + bool fPoSCancel = false; + int64_t nFees; + std::unique_ptr pblocktemplate(BlockAssembler{active_chainstate, &mempool}.CreateNewBlock(CScript(), nullptr, &fPoSCancel, &nFees)); + if (!pblocktemplate.get()) + throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block"); + + CBlock *pblock = &pblocktemplate->block; + CMutableTransaction coinstakeTx(*pblock->vtx[0]); + pblock->nTime = coinstakeTx.nTime = nTime; + pblock->vtx[0] = MakeTransactionRef(std::move(coinstakeTx)); + + CDataStream ss(SER_DISK, PROTOCOL_VERSION); + ss << *pblock; + + result.pushKV("blocktemplate", HexStr(ss)); + result.pushKV("blocktemplatefees", nFees); + + if (!pwallet->CanGetAddresses(true)) { + throw JSONRPCError(RPC_WALLET_ERROR, "Error: This wallet has no available keys"); + } + + // Prepare reserve destination + OutputType output_type = pwallet->m_default_change_type ? *pwallet->m_default_change_type : pwallet->m_default_address_type; + auto op_dest = pwallet->GetNewChangeDestination(output_type); + if (!op_dest) { + throw JSONRPCError(RPC_WALLET_ERROR, "Error: Keypool ran out, please call keypoolrefill first"); + } + std::vector vSolutionsTmp; + CScript scriptPubKeyTmp = GetScriptForDestination(*op_dest); + Solver(scriptPubKeyTmp, vSolutionsTmp); + std::unique_ptr provider = pwallet->GetSolvingProvider(scriptPubKeyTmp); + if (!provider) { + throw JSONRPCError(RPC_WALLET_ERROR, "Error: failed to get signing provider"); + } + CKeyID ckey = CKeyID(uint160(vSolutionsTmp[0])); + CPubKey pkey; + if (!provider.get()->GetPubKey(ckey, pkey)) { + throw JSONRPCError(RPC_WALLET_ERROR, "Error: failed to get key"); + } + result.pushKV("blocktemplatesignkey", HexStr(pkey)); + + return result; +}, + }; +} + Span GetStakingRPCCommands() { // clang-format off @@ -212,6 +369,7 @@ static const CRPCCommand commands[] = { "staking", &getstakinginfo, }, { "staking", &reservebalance, }, { "staking", &staking, }, + { "staking", &checkkernel, }, }; // clang-format on return commands;