From 420c6a56d6895c612789d459f8ddafbc3ac4073a Mon Sep 17 00:00:00 2001 From: Jianping Wu Date: Thu, 7 Mar 2019 22:37:20 -0800 Subject: [PATCH] WIP: fixed keva block deserialization. --- src/rpc/mining.cpp | 474 +-------------------------------------------- 1 file changed, 7 insertions(+), 467 deletions(-) diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index d8a9c43d1..17c1c07a6 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -299,398 +299,6 @@ std::string gbt_vb_name(const Consensus::DeploymentPos pos) { return s; } -#if 0 -UniValue getblocktemplate(const JSONRPCRequest& request) -{ - if (request.fHelp || request.params.size() > 1) - throw std::runtime_error( - "getblocktemplate ( TemplateRequest )\n" - "\nIf the request parameters include a 'mode' key, that is used to explicitly select between the default 'template' request or a 'proposal'.\n" - "It returns data needed to construct a block to work on.\n" - "For full specification, see BIPs 22, 23, 9, and 145:\n" - " https://github.com/bitcoin/bips/blob/master/bip-0022.mediawiki\n" - " https://github.com/bitcoin/bips/blob/master/bip-0023.mediawiki\n" - " https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki#getblocktemplate_changes\n" - " https://github.com/bitcoin/bips/blob/master/bip-0145.mediawiki\n" - - "\nArguments:\n" - "1. template_request (json object, optional) A json object in the following spec\n" - " {\n" - " \"mode\":\"template\" (string, optional) This must be set to \"template\", \"proposal\" (see BIP 23), or omitted\n" - " \"capabilities\":[ (array, optional) A list of strings\n" - " \"support\" (string) client side supported feature, 'longpoll', 'coinbasetxn', 'coinbasevalue', 'proposal', 'serverlist', 'workid'\n" - " ,...\n" - " ],\n" - " \"rules\":[ (array, optional) A list of strings\n" - " \"support\" (string) client side supported softfork deployment\n" - " ,...\n" - " ]\n" - " }\n" - "\n" - - "\nResult:\n" - "{\n" - " \"version\" : n, (numeric) The preferred block version\n" - " \"rules\" : [ \"rulename\", ... ], (array of strings) specific block rules that are to be enforced\n" - " \"vbavailable\" : { (json object) set of pending, supported versionbit (BIP 9) softfork deployments\n" - " \"rulename\" : bitnumber (numeric) identifies the bit number as indicating acceptance and readiness for the named softfork rule\n" - " ,...\n" - " },\n" - " \"vbrequired\" : n, (numeric) bit mask of versionbits the server requires set in submissions\n" - " \"previousblockhash\" : \"xxxx\", (string) The hash of current highest block\n" - " \"transactions\" : [ (array) contents of non-coinbase transactions that should be included in the next block\n" - " {\n" - " \"data\" : \"xxxx\", (string) transaction data encoded in hexadecimal (byte-for-byte)\n" - " \"txid\" : \"xxxx\", (string) transaction id encoded in little-endian hexadecimal\n" - " \"hash\" : \"xxxx\", (string) hash encoded in little-endian hexadecimal (including witness data)\n" - " \"depends\" : [ (array) array of numbers \n" - " n (numeric) transactions before this one (by 1-based index in 'transactions' list) that must be present in the final block if this one is\n" - " ,...\n" - " ],\n" - " \"fee\": n, (numeric) difference in value between transaction inputs and outputs (in satoshis); for coinbase transactions, this is a negative Number of the total collected block fees (ie, not including the block subsidy); if key is not present, fee is unknown and clients MUST NOT assume there isn't one\n" - " \"sigops\" : n, (numeric) total SigOps cost, as counted for purposes of block limits; if key is not present, sigop cost is unknown and clients MUST NOT assume it is zero\n" - " \"weight\" : n, (numeric) total transaction weight, as counted for purposes of block limits\n" - " \"required\" : true|false (boolean) if provided and true, this transaction must be in the final block\n" - " }\n" - " ,...\n" - " ],\n" - " \"coinbaseaux\" : { (json object) data that should be included in the coinbase's scriptSig content\n" - " \"flags\" : \"xx\" (string) key name is to be ignored, and value included in scriptSig\n" - " },\n" - " \"coinbasevalue\" : n, (numeric) maximum allowable input to coinbase transaction, including the generation award and transaction fees (in satoshis)\n" - " \"coinbasetxn\" : { ... }, (json object) information for coinbase transaction\n" - " \"target\" : \"xxxx\", (string) The hash target\n" - " \"mintime\" : xxx, (numeric) The minimum timestamp appropriate for next block time in seconds since epoch (Jan 1 1970 GMT)\n" - " \"mutable\" : [ (array of string) list of ways the block template may be changed \n" - " \"value\" (string) A way the block template may be changed, e.g. 'time', 'transactions', 'prevblock'\n" - " ,...\n" - " ],\n" - " \"noncerange\" : \"00000000ffffffff\",(string) A range of valid nonces\n" - " \"sigoplimit\" : n, (numeric) limit of sigops in blocks\n" - " \"sizelimit\" : n, (numeric) limit of block size\n" - " \"weightlimit\" : n, (numeric) limit of block weight\n" - " \"curtime\" : ttt, (numeric) current timestamp in seconds since epoch (Jan 1 1970 GMT)\n" - " \"bits\" : \"xxxxxxxx\", (string) compressed target of next block\n" - " \"height\" : n (numeric) The height of the next block\n" - "}\n" - - "\nExamples:\n" - + HelpExampleCli("getblocktemplate", "") - + HelpExampleRpc("getblocktemplate", "") - ); - - LOCK(cs_main); - - std::string strMode = "template"; - UniValue lpval = NullUniValue; - std::set setClientRules; - int64_t nMaxVersionPreVB = -1; - if (!request.params[0].isNull()) - { - const UniValue& oparam = request.params[0].get_obj(); - const UniValue& modeval = find_value(oparam, "mode"); - if (modeval.isStr()) - strMode = modeval.get_str(); - else if (modeval.isNull()) - { - /* Do nothing */ - } - else - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode"); - lpval = find_value(oparam, "longpollid"); - - if (strMode == "proposal") - { - const UniValue& dataval = find_value(oparam, "data"); - if (!dataval.isStr()) - throw JSONRPCError(RPC_TYPE_ERROR, "Missing data String key for proposal"); - - CBlock block; - if (!DecodeHexBlk(block, dataval.get_str())) - throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed"); - - uint256 hash = block.GetHash(); - BlockMap::iterator mi = mapBlockIndex.find(hash); - if (mi != mapBlockIndex.end()) { - CBlockIndex *pindex = mi->second; - if (pindex->IsValid(BLOCK_VALID_SCRIPTS)) - return "duplicate"; - if (pindex->nStatus & BLOCK_FAILED_MASK) - return "duplicate-invalid"; - return "duplicate-inconclusive"; - } - - CBlockIndex* const pindexPrev = chainActive.Tip(); - // TestBlockValidity only supports blocks built on the current Tip - if (block.hashPrevBlock != pindexPrev->GetBlockHash()) - return "inconclusive-not-best-prevblk"; - CValidationState state; - TestBlockValidity(state, Params(), block, pindexPrev, false, true); - return BIP22ValidationResult(state); - } - - const UniValue& aClientRules = find_value(oparam, "rules"); - if (aClientRules.isArray()) { - for (unsigned int i = 0; i < aClientRules.size(); ++i) { - const UniValue& v = aClientRules[i]; - setClientRules.insert(v.get_str()); - } - } else { - // NOTE: It is important that this NOT be read if versionbits is supported - const UniValue& uvMaxVersion = find_value(oparam, "maxversion"); - if (uvMaxVersion.isNum()) { - nMaxVersionPreVB = uvMaxVersion.get_int64(); - } - } - } - - if (strMode != "template") - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode"); - - if(!g_connman) - throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); - - if (g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL) == 0) - throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Kevacoin is not connected!"); - - if (IsInitialBlockDownload()) - throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Kevacoin is downloading blocks..."); - - static unsigned int nTransactionsUpdatedLast; - - if (!lpval.isNull()) - { - // Wait to respond until either the best block changes, OR a minute has passed and there are more transactions - uint256 hashWatchedChain; - std::chrono::steady_clock::time_point checktxtime; - unsigned int nTransactionsUpdatedLastLP; - - if (lpval.isStr()) - { - // Format: - std::string lpstr = lpval.get_str(); - - hashWatchedChain.SetHex(lpstr.substr(0, 64)); - nTransactionsUpdatedLastLP = atoi64(lpstr.substr(64)); - } - else - { - // NOTE: Spec does not specify behaviour for non-string longpollid, but this makes testing easier - hashWatchedChain = chainActive.Tip()->GetBlockHash(); - nTransactionsUpdatedLastLP = nTransactionsUpdatedLast; - } - - // Release the wallet and main lock while waiting - LEAVE_CRITICAL_SECTION(cs_main); - { - checktxtime = std::chrono::steady_clock::now() + std::chrono::minutes(1); - - WaitableLock lock(csBestBlock); - while (hashBestBlock == hashWatchedChain && IsRPCRunning()) - { - if (cvBlockChange.wait_until(lock, checktxtime) == std::cv_status::timeout) - { - // Timeout: Check transactions for update - if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLastLP) - break; - checktxtime += std::chrono::seconds(10); - } - } - } - ENTER_CRITICAL_SECTION(cs_main); - - if (!IsRPCRunning()) - throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Shutting down"); - // TODO: Maybe recheck connections/IBD and (if something wrong) send an expires-immediately template to stop miners? - } - - const struct VBDeploymentInfo& segwit_info = VersionBitsDeploymentInfo[Consensus::DEPLOYMENT_SEGWIT]; - // If the caller is indicating segwit support, then allow CreateNewBlock() - // to select witness transactions, after segwit activates (otherwise - // don't). - bool fSupportsSegwit = setClientRules.find(segwit_info.name) != setClientRules.end(); - - // Update block - static CBlockIndex* pindexPrev; - static int64_t nStart; - static std::unique_ptr pblocktemplate; - // Cache whether the last invocation was with segwit support, to avoid returning - // a segwit-block to a non-segwit caller. - static bool fLastTemplateSupportsSegwit = true; - if (pindexPrev != chainActive.Tip() || - (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 5) || - fLastTemplateSupportsSegwit != fSupportsSegwit) - { - // Clear pindexPrev so future calls make a new block, despite any failures from here on - pindexPrev = nullptr; - - // Store the pindexBest used before CreateNewBlock, to avoid races - nTransactionsUpdatedLast = mempool.GetTransactionsUpdated(); - CBlockIndex* pindexPrevNew = chainActive.Tip(); - nStart = GetTime(); - fLastTemplateSupportsSegwit = fSupportsSegwit; - - // Create new block - CScript scriptDummy = CScript() << OP_TRUE; - pblocktemplate = BlockAssembler(Params()).CreateNewBlock(scriptDummy, fSupportsSegwit); - if (!pblocktemplate) - throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory"); - - // Need to update only after we know CreateNewBlock succeeded - pindexPrev = pindexPrevNew; - } - CBlock* pblock = &pblocktemplate->block; // pointer for convenience - const Consensus::Params& consensusParams = Params().GetConsensus(); - - // Update nTime - UpdateTime(pblock, consensusParams, pindexPrev); - pblock->nNonce = 0; - - // NOTE: If at some point we support pre-segwit miners post-segwit-activation, this needs to take segwit support into consideration - const bool fPreSegWit = (THRESHOLD_ACTIVE != VersionBitsState(pindexPrev, consensusParams, Consensus::DEPLOYMENT_SEGWIT, versionbitscache)); - - UniValue aCaps(UniValue::VARR); aCaps.push_back("proposal"); - - UniValue transactions(UniValue::VARR); - std::map setTxIndex; - int i = 0; - for (const auto& it : pblock->vtx) { - const CTransaction& tx = *it; - uint256 txHash = tx.GetHash(); - setTxIndex[txHash] = i++; - - if (tx.IsCoinBase()) - continue; - - UniValue entry(UniValue::VOBJ); - - entry.push_back(Pair("data", EncodeHexTx(tx))); - entry.push_back(Pair("txid", txHash.GetHex())); - entry.push_back(Pair("hash", tx.GetWitnessHash().GetHex())); - - UniValue deps(UniValue::VARR); - for (const CTxIn &in : tx.vin) - { - if (setTxIndex.count(in.prevout.hash)) - deps.push_back(setTxIndex[in.prevout.hash]); - } - entry.push_back(Pair("depends", deps)); - - int index_in_template = i - 1; - entry.push_back(Pair("fee", pblocktemplate->vTxFees[index_in_template])); - int64_t nTxSigOps = pblocktemplate->vTxSigOpsCost[index_in_template]; - if (fPreSegWit) { - assert(nTxSigOps % WITNESS_SCALE_FACTOR == 0); - nTxSigOps /= WITNESS_SCALE_FACTOR; - } - entry.push_back(Pair("sigops", nTxSigOps)); - entry.push_back(Pair("weight", GetTransactionWeight(tx))); - - transactions.push_back(entry); - } - - UniValue aux(UniValue::VOBJ); - aux.push_back(Pair("flags", HexStr(COINBASE_FLAGS.begin(), COINBASE_FLAGS.end()))); - - arith_uint256 hashTarget = arith_uint256().SetCompact(pblock->nBits); - - UniValue aMutable(UniValue::VARR); - aMutable.push_back("time"); - aMutable.push_back("transactions"); - aMutable.push_back("prevblock"); - - UniValue result(UniValue::VOBJ); - result.push_back(Pair("capabilities", aCaps)); - - UniValue aRules(UniValue::VARR); - UniValue vbavailable(UniValue::VOBJ); - for (int j = 0; j < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++j) { - Consensus::DeploymentPos pos = Consensus::DeploymentPos(j); - ThresholdState state = VersionBitsState(pindexPrev, consensusParams, pos, versionbitscache); - switch (state) { - case THRESHOLD_DEFINED: - case THRESHOLD_FAILED: - // Not exposed to GBT at all - break; - case THRESHOLD_LOCKED_IN: - // Ensure bit is set in block version - pblock->nVersion |= VersionBitsMask(consensusParams, pos); - // FALL THROUGH to get vbavailable set... - case THRESHOLD_STARTED: - { - const struct VBDeploymentInfo& vbinfo = VersionBitsDeploymentInfo[pos]; - vbavailable.push_back(Pair(gbt_vb_name(pos), consensusParams.vDeployments[pos].bit)); - if (setClientRules.find(vbinfo.name) == setClientRules.end()) { - if (!vbinfo.gbt_force) { - // If the client doesn't support this, don't indicate it in the [default] version - pblock->nVersion &= ~VersionBitsMask(consensusParams, pos); - } - } - break; - } - case THRESHOLD_ACTIVE: - { - // Add to rules only - const struct VBDeploymentInfo& vbinfo = VersionBitsDeploymentInfo[pos]; - aRules.push_back(gbt_vb_name(pos)); - if (setClientRules.find(vbinfo.name) == setClientRules.end()) { - // Not supported by the client; make sure it's safe to proceed - if (!vbinfo.gbt_force) { - // If we do anything other than throw an exception here, be sure version/force isn't sent to old clients - throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Support for '%s' rule requires explicit client support", vbinfo.name)); - } - } - break; - } - } - } - result.push_back(Pair("version", pblock->nVersion)); - result.push_back(Pair("rules", aRules)); - result.push_back(Pair("vbavailable", vbavailable)); - result.push_back(Pair("vbrequired", int(0))); - - if (nMaxVersionPreVB >= 2) { - // If VB is supported by the client, nMaxVersionPreVB is -1, so we won't get here - // Because BIP 34 changed how the generation transaction is serialized, we can only use version/force back to v2 blocks - // This is safe to do [otherwise-]unconditionally only because we are throwing an exception above if a non-force deployment gets activated - // Note that this can probably also be removed entirely after the first BIP9 non-force deployment (ie, probably segwit) gets activated - aMutable.push_back("version/force"); - } - - result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex())); - result.push_back(Pair("transactions", transactions)); - result.push_back(Pair("coinbaseaux", aux)); - result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0]->vout[0].nValue)); - result.push_back(Pair("longpollid", chainActive.Tip()->GetBlockHash().GetHex() + i64tostr(nTransactionsUpdatedLast))); - result.push_back(Pair("target", hashTarget.GetHex())); - result.push_back(Pair("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1)); - result.push_back(Pair("mutable", aMutable)); - result.push_back(Pair("noncerange", "00000000ffffffff")); - int64_t nSigOpLimit = MAX_BLOCK_SIGOPS_COST; - int64_t nSizeLimit = MAX_BLOCK_SERIALIZED_SIZE; - if (fPreSegWit) { - assert(nSigOpLimit % WITNESS_SCALE_FACTOR == 0); - nSigOpLimit /= WITNESS_SCALE_FACTOR; - assert(nSizeLimit % WITNESS_SCALE_FACTOR == 0); - nSizeLimit /= WITNESS_SCALE_FACTOR; - } - result.push_back(Pair("sigoplimit", nSigOpLimit)); - result.push_back(Pair("sizelimit", nSizeLimit)); - if (!fPreSegWit) { - result.push_back(Pair("weightlimit", (int64_t)MAX_BLOCK_WEIGHT)); - } - result.push_back(Pair("curtime", pblock->GetBlockTime())); - result.push_back(Pair("bits", strprintf("%08x", pblock->nBits))); - result.push_back(Pair("height", (int64_t)(pindexPrev->nHeight+1))); - - if (!pblocktemplate->vchCoinbaseCommitment.empty() && fSupportsSegwit) { - result.push_back(Pair("default_witness_commitment", HexStr(pblocktemplate->vchCoinbaseCommitment.begin(), pblocktemplate->vchCoinbaseCommitment.end()))); - } - - return result; -} -#endif - UniValue getblocktemplate(const JSONRPCRequest& request) { // JSON-RPC2 request @@ -991,79 +599,6 @@ protected: } }; -#if 0 -UniValue submitblock(const JSONRPCRequest& request) -{ - // We allow 2 arguments for compliance with BIP22. Argument 2 is ignored. - if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { - throw std::runtime_error( - "submitblock \"hexdata\" ( \"dummy\" )\n" - "\nAttempts to submit new block to network.\n" - "See https://en.bitcoin.it/wiki/BIP_0022 for full specification.\n" - - "\nArguments\n" - "1. \"hexdata\" (string, required) the hex-encoded block data to submit\n" - "2. \"dummy\" (optional) dummy value, for compatibility with BIP22. This value is ignored.\n" - "\nResult:\n" - "\nExamples:\n" - + HelpExampleCli("submitblock", "\"mydata\"") - + HelpExampleRpc("submitblock", "\"mydata\"") - ); - } - - std::shared_ptr blockptr = std::make_shared(); - CBlock& block = *blockptr; - if (!DecodeHexBlk(block, request.params[0].get_str())) { - throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed"); - } - - if (block.vtx.empty() || !block.vtx[0]->IsCoinBase()) { - throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block does not start with a coinbase"); - } - - uint256 hash = block.GetHash(); - bool fBlockPresent = false; - { - LOCK(cs_main); - BlockMap::iterator mi = mapBlockIndex.find(hash); - if (mi != mapBlockIndex.end()) { - CBlockIndex *pindex = mi->second; - if (pindex->IsValid(BLOCK_VALID_SCRIPTS)) { - return "duplicate"; - } - if (pindex->nStatus & BLOCK_FAILED_MASK) { - return "duplicate-invalid"; - } - // Otherwise, we might only have the header - process the block before returning - fBlockPresent = true; - } - } - - { - LOCK(cs_main); - BlockMap::iterator mi = mapBlockIndex.find(block.hashPrevBlock); - if (mi != mapBlockIndex.end()) { - UpdateUncommittedBlockStructures(block, mi->second, Params().GetConsensus()); - } - } - - submitblock_StateCatcher sc(block.GetHash()); - RegisterValidationInterface(&sc); - bool fAccepted = ProcessNewBlock(Params(), blockptr, true, nullptr); - UnregisterValidationInterface(&sc); - if (fBlockPresent) { - if (fAccepted && !sc.found) { - return "duplicate-inconclusive"; - } - return "duplicate"; - } - if (!sc.found) { - return "inconclusive"; - } - return BIP22ValidationResult(sc.state); -} -#endif - static uint256 CryptoHashToUint256(const crypto::hash& hash) { std::vector prev_id((unsigned char*)(&hash), (unsigned char*)&(hash) + sizeof(crypto::hash)); @@ -1108,8 +643,13 @@ UniValue submitblock(const JSONRPCRequest& request) std::shared_ptr blockptr = std::make_shared(); CBlock& block = *blockptr; - if (!DecodeHexBlk(block, keva_block_blob.keva_block)) { - throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed"); + const std::vector keva_block(keva_block_blob.keva_block.begin(), keva_block_blob.keva_block.end()); + CDataStream ssBlock(keva_block, SER_NETWORK, PROTOCOL_VERSION); + try { + ssBlock >> block; + } + catch (const std::exception&) { + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Failed to deserialize keva block"); } if (block.vtx.empty() || !block.vtx[0]->IsCoinBase()) {