Changeset View
Changeset View
Standalone View
Standalone View
src/rpc/mining.cpp
Show All 39 Lines | |||||
#include <cstdint> | #include <cstdint> | ||||
/** | /** | ||||
* Return average network hashes per second based on the last 'lookup' blocks, | * Return average network hashes per second based on the last 'lookup' blocks, | ||||
* or from the last difficulty change if 'lookup' is nonpositive. If 'height' is | * or from the last difficulty change if 'lookup' is nonpositive. If 'height' is | ||||
* nonnegative, compute the estimate at the time when a given block was found. | * nonnegative, compute the estimate at the time when a given block was found. | ||||
*/ | */ | ||||
static UniValue GetNetworkHashPS(int lookup, int height) { | static UniValue GetNetworkHashPS(int lookup, int height, | ||||
CBlockIndex *pb = ::ChainActive().Tip(); | const CChain &active_chain) { | ||||
const CBlockIndex *pb = active_chain.Tip(); | |||||
if (height >= 0 && height < ::ChainActive().Height()) { | if (height >= 0 && height < active_chain.Height()) { | ||||
pb = ::ChainActive()[height]; | pb = active_chain[height]; | ||||
} | } | ||||
if (pb == nullptr || !pb->nHeight) { | if (pb == nullptr || !pb->nHeight) { | ||||
return 0; | return 0; | ||||
} | } | ||||
// If lookup is -1, then use blocks since last difficulty change. | // If lookup is -1, then use blocks since last difficulty change. | ||||
if (lookup <= 0) { | if (lookup <= 0) { | ||||
lookup = pb->nHeight % | lookup = pb->nHeight % | ||||
Params().GetConsensus().DifficultyAdjustmentInterval() + | Params().GetConsensus().DifficultyAdjustmentInterval() + | ||||
1; | 1; | ||||
} | } | ||||
// If lookup is larger than chain, then set it to chain length. | // If lookup is larger than chain, then set it to chain length. | ||||
if (lookup > pb->nHeight) { | if (lookup > pb->nHeight) { | ||||
lookup = pb->nHeight; | lookup = pb->nHeight; | ||||
} | } | ||||
CBlockIndex *pb0 = pb; | const CBlockIndex *pb0 = pb; | ||||
int64_t minTime = pb0->GetBlockTime(); | int64_t minTime = pb0->GetBlockTime(); | ||||
int64_t maxTime = minTime; | int64_t maxTime = minTime; | ||||
for (int i = 0; i < lookup; i++) { | for (int i = 0; i < lookup; i++) { | ||||
pb0 = pb0->pprev; | pb0 = pb0->pprev; | ||||
int64_t time = pb0->GetBlockTime(); | int64_t time = pb0->GetBlockTime(); | ||||
minTime = std::min(time, minTime); | minTime = std::min(time, minTime); | ||||
maxTime = std::max(time, maxTime); | maxTime = std::max(time, maxTime); | ||||
} | } | ||||
Show All 27 Lines | return RPCHelpMan{ | ||||
"To estimate at the time of the given height."}, | "To estimate at the time of the given height."}, | ||||
}, | }, | ||||
RPCResult{RPCResult::Type::NUM, "", "Hashes per second estimated"}, | RPCResult{RPCResult::Type::NUM, "", "Hashes per second estimated"}, | ||||
RPCExamples{HelpExampleCli("getnetworkhashps", "") + | RPCExamples{HelpExampleCli("getnetworkhashps", "") + | ||||
HelpExampleRpc("getnetworkhashps", "")}, | HelpExampleRpc("getnetworkhashps", "")}, | ||||
[&](const RPCHelpMan &self, const Config &config, | [&](const RPCHelpMan &self, const Config &config, | ||||
const JSONRPCRequest &request) -> UniValue { | const JSONRPCRequest &request) -> UniValue { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
const CChain &active_chain = | |||||
EnsureChainman(request.context).ActiveChain(); | |||||
return GetNetworkHashPS( | return GetNetworkHashPS( | ||||
!request.params[0].isNull() ? request.params[0].get_int() : 120, | !request.params[0].isNull() ? request.params[0].get_int() : 120, | ||||
!request.params[1].isNull() ? request.params[1].get_int() : -1); | !request.params[1].isNull() ? request.params[1].get_int() : -1, | ||||
active_chain); | |||||
}, | }, | ||||
}; | }; | ||||
} | } | ||||
static bool GenerateBlock(const Config &config, ChainstateManager &chainman, | static bool GenerateBlock(const Config &config, ChainstateManager &chainman, | ||||
CBlock &block, uint64_t &max_tries, | CBlock &block, uint64_t &max_tries, | ||||
unsigned int &extra_nonce, BlockHash &block_hash) { | unsigned int &extra_nonce, BlockHash &block_hash) { | ||||
block_hash.SetNull(); | block_hash.SetNull(); | ||||
const uint64_t nExcessiveBlockSize = config.GetMaxBlockSize(); | const uint64_t nExcessiveBlockSize = config.GetMaxBlockSize(); | ||||
{ | { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
IncrementExtraNonce(&block, ::ChainActive().Tip(), nExcessiveBlockSize, | CHECK_NONFATAL(std::addressof(::ChainActive()) == | ||||
std::addressof(chainman.ActiveChain())); | |||||
IncrementExtraNonce(&block, chainman.ActiveTip(), nExcessiveBlockSize, | |||||
extra_nonce); | extra_nonce); | ||||
} | } | ||||
const Consensus::Params ¶ms = config.GetChainParams().GetConsensus(); | const Consensus::Params ¶ms = config.GetChainParams().GetConsensus(); | ||||
while (max_tries > 0 && | while (max_tries > 0 && | ||||
block.nNonce < std::numeric_limits<uint32_t>::max() && | block.nNonce < std::numeric_limits<uint32_t>::max() && | ||||
!CheckProofOfWork(block.GetHash(), block.nBits, params) && | !CheckProofOfWork(block.GetHash(), block.nBits, params) && | ||||
Show All 25 Lines | static UniValue generateBlocks(const Config &config, | ||||
const CScript &coinbase_script, int nGenerate, | const CScript &coinbase_script, int nGenerate, | ||||
uint64_t nMaxTries) { | uint64_t nMaxTries) { | ||||
int nHeightEnd = 0; | int nHeightEnd = 0; | ||||
int nHeight = 0; | int nHeight = 0; | ||||
{ | { | ||||
// Don't keep cs_main locked. | // Don't keep cs_main locked. | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
nHeight = ::ChainActive().Height(); | CHECK_NONFATAL(std::addressof(::ChainActive()) == | ||||
std::addressof(chainman.ActiveChain())); | |||||
nHeight = chainman.ActiveHeight(); | |||||
nHeightEnd = nHeight + nGenerate; | nHeightEnd = nHeight + nGenerate; | ||||
} | } | ||||
unsigned int nExtraNonce = 0; | unsigned int nExtraNonce = 0; | ||||
UniValue blockHashes(UniValue::VARR); | UniValue blockHashes(UniValue::VARR); | ||||
while (nHeight < nHeightEnd && !ShutdownRequested()) { | while (nHeight < nHeightEnd && !ShutdownRequested()) { | ||||
std::unique_ptr<CBlockTemplate> pblocktemplate( | std::unique_ptr<CBlockTemplate> pblocktemplate( | ||||
BlockAssembler(config, chainman.ActiveChainstate(), mempool) | BlockAssembler(config, chainman.ActiveChainstate(), mempool) | ||||
▲ Show 20 Lines • Show All 247 Lines • ▼ Show 20 Lines | return RPCHelpMan{ | ||||
throw JSONRPCError( | throw JSONRPCError( | ||||
RPC_DESERIALIZATION_ERROR, | RPC_DESERIALIZATION_ERROR, | ||||
strprintf("Transaction decode failed for %s", str)); | strprintf("Transaction decode failed for %s", str)); | ||||
} | } | ||||
} | } | ||||
CBlock block; | CBlock block; | ||||
ChainstateManager &chainman = EnsureChainman(request.context); | |||||
{ | { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
CTxMemPool empty_mempool; | CTxMemPool empty_mempool; | ||||
std::unique_ptr<CBlockTemplate> blocktemplate( | std::unique_ptr<CBlockTemplate> blocktemplate( | ||||
BlockAssembler(config, ::ChainstateActive(), empty_mempool) | BlockAssembler(config, chainman.ActiveChainstate(), | ||||
empty_mempool) | |||||
.CreateNewBlock(coinbase_script)); | .CreateNewBlock(coinbase_script)); | ||||
if (!blocktemplate) { | if (!blocktemplate) { | ||||
throw JSONRPCError(RPC_INTERNAL_ERROR, | throw JSONRPCError(RPC_INTERNAL_ERROR, | ||||
"Couldn't create new block"); | "Couldn't create new block"); | ||||
} | } | ||||
block = blocktemplate->block; | block = blocktemplate->block; | ||||
} | } | ||||
CHECK_NONFATAL(block.vtx.size() == 1); | CHECK_NONFATAL(block.vtx.size() == 1); | ||||
// Add transactions | // Add transactions | ||||
block.vtx.insert(block.vtx.end(), txs.begin(), txs.end()); | block.vtx.insert(block.vtx.end(), txs.begin(), txs.end()); | ||||
{ | { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
BlockValidationState state; | BlockValidationState state; | ||||
if (!TestBlockValidity(state, chainparams, ::ChainstateActive(), | if (!TestBlockValidity(state, chainparams, | ||||
block, | chainman.ActiveChainstate(), block, | ||||
g_chainman.m_blockman.LookupBlockIndex( | chainman.m_blockman.LookupBlockIndex( | ||||
block.hashPrevBlock), | block.hashPrevBlock), | ||||
BlockValidationOptions(config) | BlockValidationOptions(config) | ||||
.withCheckPoW(false) | .withCheckPoW(false) | ||||
.withCheckMerkleRoot(false))) { | .withCheckMerkleRoot(false))) { | ||||
throw JSONRPCError(RPC_VERIFY_ERROR, | throw JSONRPCError(RPC_VERIFY_ERROR, | ||||
strprintf("TestBlockValidity failed: %s", | strprintf("TestBlockValidity failed: %s", | ||||
state.ToString())); | state.ToString())); | ||||
} | } | ||||
} | } | ||||
BlockHash block_hash; | BlockHash block_hash; | ||||
uint64_t max_tries{DEFAULT_MAX_TRIES}; | uint64_t max_tries{DEFAULT_MAX_TRIES}; | ||||
unsigned int extra_nonce{0}; | unsigned int extra_nonce{0}; | ||||
if (!GenerateBlock(config, EnsureChainman(request.context), block, | if (!GenerateBlock(config, chainman, block, max_tries, extra_nonce, | ||||
max_tries, extra_nonce, block_hash) || | block_hash) || | ||||
block_hash.IsNull()) { | block_hash.IsNull()) { | ||||
throw JSONRPCError(RPC_MISC_ERROR, "Failed to make block."); | throw JSONRPCError(RPC_MISC_ERROR, "Failed to make block."); | ||||
} | } | ||||
UniValue obj(UniValue::VOBJ); | UniValue obj(UniValue::VOBJ); | ||||
obj.pushKV("hash", block_hash.GetHex()); | obj.pushKV("hash", block_hash.GetHex()); | ||||
return obj; | return obj; | ||||
}, | }, | ||||
Show All 28 Lines | return RPCHelpMan{ | ||||
"any network and blockchain warnings"}, | "any network and blockchain warnings"}, | ||||
}}, | }}, | ||||
RPCExamples{HelpExampleCli("getmininginfo", "") + | RPCExamples{HelpExampleCli("getmininginfo", "") + | ||||
HelpExampleRpc("getmininginfo", "")}, | HelpExampleRpc("getmininginfo", "")}, | ||||
[&](const RPCHelpMan &self, const Config &config, | [&](const RPCHelpMan &self, const Config &config, | ||||
const JSONRPCRequest &request) -> UniValue { | const JSONRPCRequest &request) -> UniValue { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
const CTxMemPool &mempool = EnsureMemPool(request.context); | const CTxMemPool &mempool = EnsureMemPool(request.context); | ||||
const CChain &active_chain = | |||||
EnsureChainman(request.context).ActiveChain(); | |||||
UniValue obj(UniValue::VOBJ); | UniValue obj(UniValue::VOBJ); | ||||
obj.pushKV("blocks", int(::ChainActive().Height())); | obj.pushKV("blocks", int(active_chain.Height())); | ||||
if (BlockAssembler::m_last_block_size) { | if (BlockAssembler::m_last_block_size) { | ||||
obj.pushKV("currentblocksize", | obj.pushKV("currentblocksize", | ||||
*BlockAssembler::m_last_block_size); | *BlockAssembler::m_last_block_size); | ||||
} | } | ||||
if (BlockAssembler::m_last_block_num_txs) { | if (BlockAssembler::m_last_block_num_txs) { | ||||
obj.pushKV("currentblocktx", | obj.pushKV("currentblocktx", | ||||
*BlockAssembler::m_last_block_num_txs); | *BlockAssembler::m_last_block_num_txs); | ||||
} | } | ||||
obj.pushKV("difficulty", | obj.pushKV("difficulty", double(GetDifficulty(active_chain.Tip()))); | ||||
double(GetDifficulty(::ChainActive().Tip()))); | |||||
obj.pushKV("networkhashps", | obj.pushKV("networkhashps", | ||||
getnetworkhashps().HandleRequest(config, request)); | getnetworkhashps().HandleRequest(config, request)); | ||||
obj.pushKV("pooledtx", uint64_t(mempool.size())); | obj.pushKV("pooledtx", uint64_t(mempool.size())); | ||||
obj.pushKV("chain", config.GetChainParams().NetworkIDString()); | obj.pushKV("chain", config.GetChainParams().NetworkIDString()); | ||||
obj.pushKV("warnings", GetWarnings(false).original); | obj.pushKV("warnings", GetWarnings(false).original); | ||||
return obj; | return obj; | ||||
}, | }, | ||||
}; | }; | ||||
▲ Show 20 Lines • Show All 232 Lines • ▼ Show 20 Lines | return RPCHelpMan{ | ||||
"The height of the next block"}, | "The height of the next block"}, | ||||
}}, | }}, | ||||
}, | }, | ||||
RPCExamples{HelpExampleCli("getblocktemplate", "") + | RPCExamples{HelpExampleCli("getblocktemplate", "") + | ||||
HelpExampleRpc("getblocktemplate", "")}, | HelpExampleRpc("getblocktemplate", "")}, | ||||
[&](const RPCHelpMan &self, const Config &config, | [&](const RPCHelpMan &self, const Config &config, | ||||
const JSONRPCRequest &request) -> UniValue { | const JSONRPCRequest &request) -> UniValue { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
ChainstateManager &chainman = EnsureChainman(request.context); | |||||
const CChainParams &chainparams = config.GetChainParams(); | const CChainParams &chainparams = config.GetChainParams(); | ||||
std::string strMode = "template"; | std::string strMode = "template"; | ||||
UniValue lpval = NullUniValue; | UniValue lpval = NullUniValue; | ||||
std::set<std::string> setClientRules; | std::set<std::string> setClientRules; | ||||
if (!request.params[0].isNull()) { | if (!request.params[0].isNull()) { | ||||
const UniValue &oparam = request.params[0].get_obj(); | const UniValue &oparam = request.params[0].get_obj(); | ||||
const UniValue &modeval = find_value(oparam, "mode"); | const UniValue &modeval = find_value(oparam, "mode"); | ||||
Show All 17 Lines | return RPCHelpMan{ | ||||
CBlock block; | CBlock block; | ||||
if (!DecodeHexBlk(block, dataval.get_str())) { | if (!DecodeHexBlk(block, dataval.get_str())) { | ||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, | throw JSONRPCError(RPC_DESERIALIZATION_ERROR, | ||||
"Block decode failed"); | "Block decode failed"); | ||||
} | } | ||||
const BlockHash hash = block.GetHash(); | const BlockHash hash = block.GetHash(); | ||||
const CBlockIndex *pindex = | const CBlockIndex *pindex = | ||||
g_chainman.m_blockman.LookupBlockIndex(hash); | chainman.m_blockman.LookupBlockIndex(hash); | ||||
if (pindex) { | if (pindex) { | ||||
if (pindex->IsValid(BlockValidity::SCRIPTS)) { | if (pindex->IsValid(BlockValidity::SCRIPTS)) { | ||||
return "duplicate"; | return "duplicate"; | ||||
} | } | ||||
if (pindex->nStatus.isInvalid()) { | if (pindex->nStatus.isInvalid()) { | ||||
return "duplicate-invalid"; | return "duplicate-invalid"; | ||||
} | } | ||||
return "duplicate-inconclusive"; | return "duplicate-inconclusive"; | ||||
} | } | ||||
CBlockIndex *const pindexPrev = ::ChainActive().Tip(); | CBlockIndex *const pindexPrev = chainman.ActiveTip(); | ||||
// TestBlockValidity only supports blocks built on the | // TestBlockValidity only supports blocks built on the | ||||
// current Tip | // current Tip | ||||
if (block.hashPrevBlock != pindexPrev->GetBlockHash()) { | if (block.hashPrevBlock != pindexPrev->GetBlockHash()) { | ||||
return "inconclusive-not-best-prevblk"; | return "inconclusive-not-best-prevblk"; | ||||
} | } | ||||
BlockValidationState state; | BlockValidationState state; | ||||
TestBlockValidity(state, chainparams, ::ChainstateActive(), | TestBlockValidity(state, chainparams, | ||||
block, pindexPrev, | chainman.ActiveChainstate(), block, | ||||
pindexPrev, | |||||
BlockValidationOptions(config) | BlockValidationOptions(config) | ||||
.withCheckPoW(false) | .withCheckPoW(false) | ||||
.withCheckMerkleRoot(true)); | .withCheckMerkleRoot(true)); | ||||
return BIP22ValidationResult(config, state); | return BIP22ValidationResult(config, state); | ||||
} | } | ||||
} | } | ||||
if (strMode != "template") { | if (strMode != "template") { | ||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode"); | throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode"); | ||||
} | } | ||||
NodeContext &node = EnsureNodeContext(request.context); | NodeContext &node = EnsureNodeContext(request.context); | ||||
if (!node.connman) { | if (!node.connman) { | ||||
throw JSONRPCError( | throw JSONRPCError( | ||||
RPC_CLIENT_P2P_DISABLED, | RPC_CLIENT_P2P_DISABLED, | ||||
"Error: Peer-to-peer functionality missing or disabled"); | "Error: Peer-to-peer functionality missing or disabled"); | ||||
} | } | ||||
if (node.connman->GetNodeCount(CConnman::CONNECTIONS_ALL) == 0) { | if (node.connman->GetNodeCount(CConnman::CONNECTIONS_ALL) == 0) { | ||||
throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, | throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, | ||||
"Bitcoin is not connected!"); | "Bitcoin is not connected!"); | ||||
} | } | ||||
if (::ChainstateActive().IsInitialBlockDownload()) { | if (chainman.ActiveChainstate().IsInitialBlockDownload()) { | ||||
throw JSONRPCError( | throw JSONRPCError( | ||||
RPC_CLIENT_IN_INITIAL_DOWNLOAD, PACKAGE_NAME | RPC_CLIENT_IN_INITIAL_DOWNLOAD, PACKAGE_NAME | ||||
" is in initial sync and waiting for blocks..."); | " is in initial sync and waiting for blocks..."); | ||||
} | } | ||||
static unsigned int nTransactionsUpdatedLast; | static unsigned int nTransactionsUpdatedLast; | ||||
const CTxMemPool &mempool = EnsureMemPool(request.context); | const CTxMemPool &mempool = EnsureMemPool(request.context); | ||||
Show All 9 Lines | return RPCHelpMan{ | ||||
std::string lpstr = lpval.get_str(); | std::string lpstr = lpval.get_str(); | ||||
hashWatchedChain = | hashWatchedChain = | ||||
ParseHashV(lpstr.substr(0, 64), "longpollid"); | ParseHashV(lpstr.substr(0, 64), "longpollid"); | ||||
nTransactionsUpdatedLastLP = atoi64(lpstr.substr(64)); | nTransactionsUpdatedLastLP = atoi64(lpstr.substr(64)); | ||||
} else { | } else { | ||||
// NOTE: Spec does not specify behaviour for non-string | // NOTE: Spec does not specify behaviour for non-string | ||||
// longpollid, but this makes testing easier | // longpollid, but this makes testing easier | ||||
hashWatchedChain = ::ChainActive().Tip()->GetBlockHash(); | hashWatchedChain = chainman.ActiveTip()->GetBlockHash(); | ||||
nTransactionsUpdatedLastLP = nTransactionsUpdatedLast; | nTransactionsUpdatedLastLP = nTransactionsUpdatedLast; | ||||
} | } | ||||
// Release lock while waiting | // Release lock while waiting | ||||
LEAVE_CRITICAL_SECTION(cs_main); | LEAVE_CRITICAL_SECTION(cs_main); | ||||
{ | { | ||||
checktxtime = std::chrono::steady_clock::now() + | checktxtime = std::chrono::steady_clock::now() + | ||||
std::chrono::minutes(1); | std::chrono::minutes(1); | ||||
Show All 22 Lines | return RPCHelpMan{ | ||||
// TODO: Maybe recheck connections/IBD and (if something wrong) | // TODO: Maybe recheck connections/IBD and (if something wrong) | ||||
// send an expires-immediately template to stop miners? | // send an expires-immediately template to stop miners? | ||||
} | } | ||||
// Update block | // Update block | ||||
static CBlockIndex *pindexPrev; | static CBlockIndex *pindexPrev; | ||||
static int64_t nStart; | static int64_t nStart; | ||||
static std::unique_ptr<CBlockTemplate> pblocktemplate; | static std::unique_ptr<CBlockTemplate> pblocktemplate; | ||||
if (pindexPrev != ::ChainActive().Tip() || | if (pindexPrev != chainman.ActiveTip() || | ||||
(mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && | (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && | ||||
GetTime() - nStart > 5)) { | GetTime() - nStart > 5)) { | ||||
// Clear pindexPrev so future calls make a new block, despite | // Clear pindexPrev so future calls make a new block, despite | ||||
// any failures from here on | // any failures from here on | ||||
pindexPrev = nullptr; | pindexPrev = nullptr; | ||||
// Store the pindexBest used before CreateNewBlock, to avoid | // Store the pindexBest used before CreateNewBlock, to avoid | ||||
// races | // races | ||||
nTransactionsUpdatedLast = mempool.GetTransactionsUpdated(); | nTransactionsUpdatedLast = mempool.GetTransactionsUpdated(); | ||||
CBlockIndex *pindexPrevNew = ::ChainActive().Tip(); | CBlockIndex *pindexPrevNew = chainman.ActiveTip(); | ||||
nStart = GetTime(); | nStart = GetTime(); | ||||
// Create new block | // Create new block | ||||
CScript scriptDummy = CScript() << OP_TRUE; | CScript scriptDummy = CScript() << OP_TRUE; | ||||
pblocktemplate = | pblocktemplate = | ||||
BlockAssembler(config, ::ChainstateActive(), mempool) | BlockAssembler(config, chainman.ActiveChainstate(), mempool) | ||||
.CreateNewBlock(scriptDummy); | .CreateNewBlock(scriptDummy); | ||||
if (!pblocktemplate) { | if (!pblocktemplate) { | ||||
throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory"); | throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory"); | ||||
} | } | ||||
// Need to update only after we know CreateNewBlock succeeded | // Need to update only after we know CreateNewBlock succeeded | ||||
pindexPrev = pindexPrevNew; | pindexPrev = pindexPrevNew; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 82 Lines • ▼ Show 20 Lines | return RPCHelpMan{ | ||||
result.pushKV("version", pblock->nVersion); | result.pushKV("version", pblock->nVersion); | ||||
result.pushKV("previousblockhash", pblock->hashPrevBlock.GetHex()); | result.pushKV("previousblockhash", pblock->hashPrevBlock.GetHex()); | ||||
result.pushKV("transactions", transactions); | result.pushKV("transactions", transactions); | ||||
result.pushKV("coinbaseaux", aux); | result.pushKV("coinbaseaux", aux); | ||||
result.pushKV("coinbasetxn", coinbasetxn); | result.pushKV("coinbasetxn", coinbasetxn); | ||||
result.pushKV("coinbasevalue", int64_t(coinbasevalue / SATOSHI)); | result.pushKV("coinbasevalue", int64_t(coinbasevalue / SATOSHI)); | ||||
result.pushKV("longpollid", | result.pushKV("longpollid", | ||||
::ChainActive().Tip()->GetBlockHash().GetHex() + | chainman.ActiveTip()->GetBlockHash().GetHex() + | ||||
ToString(nTransactionsUpdatedLast)); | ToString(nTransactionsUpdatedLast)); | ||||
result.pushKV("target", hashTarget.GetHex()); | result.pushKV("target", hashTarget.GetHex()); | ||||
result.pushKV("mintime", | result.pushKV("mintime", | ||||
int64_t(pindexPrev->GetMedianTimePast()) + 1); | int64_t(pindexPrev->GetMedianTimePast()) + 1); | ||||
result.pushKV("mutable", aMutable); | result.pushKV("mutable", aMutable); | ||||
result.pushKV("noncerange", "00000000ffffffff"); | result.pushKV("noncerange", "00000000ffffffff"); | ||||
result.pushKV("sigoplimit", | result.pushKV("sigoplimit", | ||||
GetMaxBlockSigChecksCount(DEFAULT_MAX_BLOCK_SIZE)); | GetMaxBlockSigChecksCount(DEFAULT_MAX_BLOCK_SIZE)); | ||||
▲ Show 20 Lines • Show All 58 Lines • ▼ Show 20 Lines | return RPCHelpMan{ | ||||
"Block decode failed"); | "Block decode failed"); | ||||
} | } | ||||
if (block.vtx.empty() || !block.vtx[0]->IsCoinBase()) { | if (block.vtx.empty() || !block.vtx[0]->IsCoinBase()) { | ||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, | throw JSONRPCError(RPC_DESERIALIZATION_ERROR, | ||||
"Block does not start with a coinbase"); | "Block does not start with a coinbase"); | ||||
} | } | ||||
ChainstateManager &chainman = EnsureChainman(request.context); | |||||
const BlockHash hash = block.GetHash(); | const BlockHash hash = block.GetHash(); | ||||
{ | { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
const CBlockIndex *pindex = | const CBlockIndex *pindex = | ||||
g_chainman.m_blockman.LookupBlockIndex(hash); | chainman.m_blockman.LookupBlockIndex(hash); | ||||
if (pindex) { | if (pindex) { | ||||
if (pindex->IsValid(BlockValidity::SCRIPTS)) { | if (pindex->IsValid(BlockValidity::SCRIPTS)) { | ||||
return "duplicate"; | return "duplicate"; | ||||
} | } | ||||
if (pindex->nStatus.isInvalid()) { | if (pindex->nStatus.isInvalid()) { | ||||
return "duplicate-invalid"; | return "duplicate-invalid"; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
bool new_block; | bool new_block; | ||||
auto sc = | auto sc = | ||||
std::make_shared<submitblock_StateCatcher>(block.GetHash()); | std::make_shared<submitblock_StateCatcher>(block.GetHash()); | ||||
RegisterSharedValidationInterface(sc); | RegisterSharedValidationInterface(sc); | ||||
bool accepted = EnsureChainman(request.context) | bool accepted = | ||||
.ProcessNewBlock(config, blockptr, | chainman.ProcessNewBlock(config, blockptr, | ||||
/* fForceProcessing */ true, | /* fForceProcessing */ true, | ||||
/* fNewBlock */ &new_block); | /* fNewBlock */ &new_block); | ||||
UnregisterSharedValidationInterface(sc); | UnregisterSharedValidationInterface(sc); | ||||
if (!new_block && accepted) { | if (!new_block && accepted) { | ||||
return "duplicate"; | return "duplicate"; | ||||
} | } | ||||
if (!sc->found) { | if (!sc->found) { | ||||
return "inconclusive"; | return "inconclusive"; | ||||
} | } | ||||
Show All 18 Lines | return RPCHelpMan{ | ||||
HelpExampleRpc("submitheader", "\"aabbcc\"")}, | HelpExampleRpc("submitheader", "\"aabbcc\"")}, | ||||
[&](const RPCHelpMan &self, const Config &config, | [&](const RPCHelpMan &self, const Config &config, | ||||
const JSONRPCRequest &request) -> UniValue { | const JSONRPCRequest &request) -> UniValue { | ||||
CBlockHeader h; | CBlockHeader h; | ||||
if (!DecodeHexBlockHeader(h, request.params[0].get_str())) { | if (!DecodeHexBlockHeader(h, request.params[0].get_str())) { | ||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, | throw JSONRPCError(RPC_DESERIALIZATION_ERROR, | ||||
"Block header decode failed"); | "Block header decode failed"); | ||||
} | } | ||||
ChainstateManager &chainman = EnsureChainman(request.context); | |||||
{ | { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
if (!g_chainman.m_blockman.LookupBlockIndex(h.hashPrevBlock)) { | if (!chainman.m_blockman.LookupBlockIndex(h.hashPrevBlock)) { | ||||
throw JSONRPCError(RPC_VERIFY_ERROR, | throw JSONRPCError(RPC_VERIFY_ERROR, | ||||
"Must submit previous header (" + | "Must submit previous header (" + | ||||
h.hashPrevBlock.GetHex() + | h.hashPrevBlock.GetHex() + | ||||
") first"); | ") first"); | ||||
} | } | ||||
} | } | ||||
BlockValidationState state; | BlockValidationState state; | ||||
EnsureChainman(request.context) | chainman.ProcessNewBlockHeaders(config, {h}, state); | ||||
.ProcessNewBlockHeaders(config, {h}, state); | |||||
if (state.IsValid()) { | if (state.IsValid()) { | ||||
return NullUniValue; | return NullUniValue; | ||||
} | } | ||||
if (state.IsError()) { | if (state.IsError()) { | ||||
throw JSONRPCError(RPC_VERIFY_ERROR, state.ToString()); | throw JSONRPCError(RPC_VERIFY_ERROR, state.ToString()); | ||||
} | } | ||||
throw JSONRPCError(RPC_VERIFY_ERROR, state.GetRejectReason()); | throw JSONRPCError(RPC_VERIFY_ERROR, state.GetRejectReason()); | ||||
}, | }, | ||||
▲ Show 20 Lines • Show All 44 Lines • Show Last 20 Lines |