From dea50be56ba6321431320e98a9bb0f9d35c80a05 Mon Sep 17 00:00:00 2001 From: Jianping Wu Date: Tue, 26 Mar 2019 23:13:36 -0700 Subject: [PATCH] Added support for cryptonote header in functional test python code. --- src/rpc/mining.cpp | 73 +++++++++++++ test/functional/test_framework/authproxy.py | 3 + test/functional/test_framework/blocktools.py | 13 +++ test/functional/test_framework/messages.py | 102 ++++++++++++++++++- 4 files changed, 186 insertions(+), 5 deletions(-) diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index f9496afba..2d54bde9e 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -632,6 +632,78 @@ static uint256 CryptoHashToUint256(const crypto::hash& hash) return uint256(prev_id); } +UniValue submitblock_original(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); +} + + // Cryptonote RPC call UniValue submitblock(const JSONRPCRequest& request) { @@ -947,6 +1019,7 @@ static const CRPCCommand commands[] = { "mining", "prioritisetransaction", &prioritisetransaction, {"txid","dummy","fee_delta"} }, { "mining", "getblocktemplate", &getblocktemplate, {"reserve_size", "wallet_address"} }, { "mining", "submitblock", &submitblock, {"hexdata","dummy"} }, + { "mining", "submitblock_original", &submitblock_original, {"hexdata","dummy"} }, { "generating", "generatetoaddress", &generatetoaddress, {"nblocks","address","maxtries"} }, diff --git a/test/functional/test_framework/authproxy.py b/test/functional/test_framework/authproxy.py index bd3a3b3fa..90204148f 100644 --- a/test/functional/test_framework/authproxy.py +++ b/test/functional/test_framework/authproxy.py @@ -133,6 +133,9 @@ class AuthServiceProxy(): def __call__(self, *args, **argsn): postdata = json.dumps(self.get_request(*args, **argsn), default=EncodeDecimal, ensure_ascii=self.ensure_ascii) + # Kevacoin: submitblock expects a cryptonote wrapper around the block, + # while submitblock_original is the original implementation. + postdata = postdata.replace('submitblock', 'submitblock_original') response = self._request('POST', self.__url.path, postdata.encode('utf-8')) if response['error'] is not None: raise JSONRPCException(response['error']) diff --git a/test/functional/test_framework/blocktools.py b/test/functional/test_framework/blocktools.py index 97cb2de00..3ef5ce928 100644 --- a/test/functional/test_framework/blocktools.py +++ b/test/functional/test_framework/blocktools.py @@ -35,6 +35,14 @@ def create_block(hashprev, coinbase, nTime=None): block.nBits = 0x207fffff # Will break after a difficulty adjustment... block.vtx.append(coinbase) block.hashMerkleRoot = block.calc_merkle_root() + + # In kevacoin, nNonce is used to store the height of the block, which will + # be used in cn_slow_hash computation. + block.nNonce = coinbase.height + assert block.nNonce > 0 + block.major_version = 10 # CN variant 4 + block.timestamp = block.nTime + block.calc_sha256() return block @@ -98,6 +106,11 @@ def create_coinbase(height, pubkey = None): coinbaseoutput.scriptPubKey = CScript([OP_TRUE]) coinbase.vout = [ coinbaseoutput ] coinbase.calc_sha256() + + # Kevacoin needs to know the height, because it is used as a parameter of + # cn_slow_hash. + coinbase.height = height + return coinbase # Create a transaction. diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py index c511e8597..951d2483d 100644 --- a/test/functional/test_framework/messages.py +++ b/test/functional/test_framework/messages.py @@ -100,6 +100,32 @@ def ser_uint256(u): return rs +def deser_varint(f): + shift = 0 + result = 0 + while True: + i = ord(f.read(1)) + result |= (i & 0x7f) << shift + shift += 7 + if not (i & 0x80): + break + + return result + + +def ser_varint(u): + buf = b'' + while True: + towrite = u & 0x7f + u >>= 7 + if u: + buf += bytes((towrite | 0x80, )) + else: + buf += bytes((towrite, )) + break + return buf + + def uint256_from_str(s): r = 0 t = struct.unpack(" target: - self.nNonce += 1 + self.nonce += 1 self.rehash() def __repr__(self):