From aa97d9938c04c9a758588398470628f431df2ffb Mon Sep 17 00:00:00 2001 From: Jianping Wu Date: Mon, 4 Mar 2019 22:13:48 -0800 Subject: [PATCH] WIP: getblocktemplate for cn style return. --- src/chainparams.cpp | 16 +++- src/cn_utils/cnutils.cpp | 18 ++++ src/cn_utils/cnutils.h | 6 +- src/cn_utils/cryptonote_core/tx_extra.h | 3 + src/consensus/consensus.h | 3 +- src/rpc/mining.cpp | 121 ++++++++++++++++++++++-- 6 files changed, 155 insertions(+), 12 deletions(-) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index b28f5c6d8..db16202f2 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -203,16 +203,25 @@ public: pchMessageStart[3] = 0xe4; nDefaultPort = 19335; nPruneAfterHeight = 1000; - genesis = CreateGenesisBlock(0x5c4fd388, 0x80003898, 0x1e0fffff, 1, 500 * COIN); +#if 1 + genesis = CreateGenesisBlock(1543789622, 1592, 0x1f0ffff0, 1, 500 * COIN); consensus.hashGenesisBlock = genesis.GetHash(); - assert(consensus.hashGenesisBlock == uint256S("7dc62041b90482eafe70fb1e1f6c8973a8c46b7f9f12b966e101c2bae126b5d4")); + assert(consensus.hashGenesisBlock == uint256S("860764b471430b96f15c145a44fd854f89d3be6f9ae054ef46e4c8473259bae3")); assert(genesis.hashMerkleRoot == uint256S("3cf6c3b6da3f4058853ee70369ee43d473aca91ae8fc8f44a645beb21c392d80")); +#else + genesis = CreateGenesisBlock(1550900037, 3033, 0x1f0fffff, 1, 500 * COIN); + consensus.hashGenesisBlock = genesis.GetHash(); + assert(consensus.hashGenesisBlock == uint256S("584a067cf7ea6daf30c351a46c6b7403d6fdc0e6e953cf7531d11eb7d834546c")); + assert(genesis.hashMerkleRoot == uint256S("3cf6c3b6da3f4058853ee70369ee43d473aca91ae8fc8f44a645beb21c392d80")); +#endif vFixedSeeds.clear(); vSeeds.clear(); // nodes with support for servicebits filtering should be at the top +#if 0 vSeeds.emplace_back("testnet-seed.kevacoin.org"); vSeeds.emplace_back("testnet-seed.honourchat.com"); +#endif base58Prefixes[PUBKEY_ADDRESS] = std::vector(1,55); // P base58Prefixes[SCRIPT_ADDRESS] = std::vector(1,5); @@ -223,8 +232,9 @@ public: base58Prefixes[KEVA_NAMESPACE] = std::vector(1,53); // N bech32_hrp = "tkva"; - +#if 0 vFixedSeeds = std::vector(pnSeed6_test, pnSeed6_test + ARRAYLEN(pnSeed6_test)); +#endif fDefaultConsistencyChecks = false; fRequireStandard = false; diff --git a/src/cn_utils/cnutils.cpp b/src/cn_utils/cnutils.cpp index 4df7cba4e..0f1b10a9f 100644 --- a/src/cn_utils/cnutils.cpp +++ b/src/cn_utils/cnutils.cpp @@ -34,4 +34,22 @@ bool validate_address(const char *addr, size_t len) { std::string output = ""; uint64_t prefix; return tools::base58::decode_addr(addr, prefix, output); +} + +//------------------------------------------------------------------------------------------------------------------------------ +// equivalent of strstr, but with arbitrary bytes (ie, NULs) +// This does not differentiate between "not found" and "found at offset 0" +uint64_t slow_memmem(const void* start_buff, size_t buflen,const void* pat,size_t patlen) +{ + const void* buf = start_buff; + const void* end=(const char*)buf+buflen; + if (patlen > buflen || patlen == 0) return 0; + while(buflen>0 && (buf=memchr(buf,((const char*)pat)[0],buflen-patlen+1))) + { + if(memcmp(buf,pat,patlen)==0) + return (const char*)buf - (const char*)start_buff; + buf=(const char*)buf+1; + buflen = (const char*)end - (const char*)buf; + } + return 0; } \ No newline at end of file diff --git a/src/cn_utils/cnutils.h b/src/cn_utils/cnutils.h index 91fa1a2bb..20d5d2e68 100644 --- a/src/cn_utils/cnutils.h +++ b/src/cn_utils/cnutils.h @@ -1,4 +1,8 @@ #pragma once +#include +#include + uint32_t convert_blob(const char *blob, size_t len, char *out); -bool validate_address(const char *addr, size_t len); \ No newline at end of file +bool validate_address(const char *addr, size_t len); +uint64_t slow_memmem(const void* start_buff, size_t buflen,const void* pat,size_t patlen); \ No newline at end of file diff --git a/src/cn_utils/cryptonote_core/tx_extra.h b/src/cn_utils/cryptonote_core/tx_extra.h index 37a04a41e..bf06e98fd 100644 --- a/src/cn_utils/cryptonote_core/tx_extra.h +++ b/src/cn_utils/cryptonote_core/tx_extra.h @@ -13,6 +13,9 @@ #define TX_EXTRA_NONCE 0x02 #define TX_EXTRA_MERGE_MINING_TAG 0x03 +#define TX_EXTRA_KEVA_BLOCKHASH_TAG 0xc1 +#define TX_EXTRA_KEVA_TX_LIST_TAG 0xc2 + #define TX_EXTRA_NONCE_PAYMENT_ID 0x00 namespace cryptonote diff --git a/src/consensus/consensus.h b/src/consensus/consensus.h index 1df5b7f34..0f45d757e 100644 --- a/src/consensus/consensus.h +++ b/src/consensus/consensus.h @@ -16,7 +16,8 @@ static const unsigned int MAX_BLOCK_WEIGHT = 6000000; /** The maximum allowed number of signature check operations in a block (network rule) */ static const int64_t MAX_BLOCK_SIGOPS_COST = 80000; /** Coinbase transaction outputs can only be spent after this number of new blocks (network rule) */ -static const int COINBASE_MATURITY = 100; +//static const int COINBASE_MATURITY = 100; +static const int COINBASE_MATURITY = 7; static const int WITNESS_SCALE_FACTOR = 4; diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index e1d418f39..d9efaed0a 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -29,9 +30,11 @@ #include #include -#include -#include #include +#include +#define MAX_RESERVE_SIZE 16 + +const std::string CN_DUMMY_ADDRESS = "44234234234"; unsigned int ParseConfirmTarget(const UniValue& value) { @@ -689,7 +692,19 @@ UniValue getblocktemplate(const JSONRPCRequest& request) UniValue getblocktemplate(const JSONRPCRequest& request) { - if (request.fHelp || request.params.size() > 1) + // JSON-RPC2 request + // {reserve_size: 8, wallet_address: config.poolServer.poolAddress} + std::map kv; + request.params.getObjMap(kv); + int reserve_size = kv["reserve_size"].get_int(); + bool fValidReserveSize = reserve_size > 0 && reserve_size <= MAX_RESERVE_SIZE; + std::vector data; + std::string wallet_address = kv["wallet_address"].get_str(); + CTxDestination walletDest = DecodeDestination(wallet_address); + bool fValidWalletAddress = walletDest.which() == 4; // Expect WitnessV0KeyHash + bool fInvalidInput = !fValidReserveSize || !fValidWalletAddress; + + if (request.fHelp || fInvalidInput) 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" @@ -795,8 +810,8 @@ UniValue getblocktemplate(const JSONRPCRequest& request) nStart = GetTime(); // Create new block - CScript scriptDummy = CScript() << OP_TRUE; - pblocktemplate = BlockAssembler(Params()).CreateNewBlock(scriptDummy, fSupportsSegwit); + CScript scriptPubKey = GetScriptForDestination(walletDest); + pblocktemplate = BlockAssembler(Params()).CreateNewBlock(scriptPubKey, fSupportsSegwit); if (!pblocktemplate) throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory"); @@ -854,8 +869,6 @@ UniValue getblocktemplate(const JSONRPCRequest& request) aMutable.push_back("transactions"); aMutable.push_back("prevblock"); - UniValue result(UniValue::VOBJ); - UniValue aRules(UniValue::VARR); UniValue vbavailable(UniValue::VOBJ); for (int j = 0; j < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++j) { @@ -885,6 +898,13 @@ UniValue getblocktemplate(const JSONRPCRequest& request) } } } + + // Update witness commitment after adding the transactions. + pblocktemplate->vchCoinbaseCommitment = GenerateCoinbaseCommitment(*pblock, pindexPrev, consensusParams); + BlockMerkleRoot(*pblock); + uint256 blockHash = pblock->GetHash(); + +#if 0 result.push_back(Pair("version", pblock->nVersion)); result.push_back(Pair("rules", aRules)); result.push_back(Pair("vbavailable", vbavailable)); @@ -911,6 +931,93 @@ UniValue getblocktemplate(const JSONRPCRequest& request) if (!pblocktemplate->vchCoinbaseCommitment.empty() && fSupportsSegwit) { result.push_back(Pair("default_witness_commitment", HexStr(pblocktemplate->vchCoinbaseCommitment.begin(), pblocktemplate->vchCoinbaseCommitment.end()))); } +#endif + + cryptonote::block cn_block; + // block_header + // const int cn_variant = b.major_version >= 7 ? b.major_version - 6 : 0; + cn_block.major_version = 8; // cn variant 2 + cn_block.minor_version = 0; + cn_block.timestamp = pblock->GetBlockTime(); + memcpy(&(cn_block.prev_id), pblock->hashPrevBlock.begin(), sizeof(pblock->hashPrevBlock)); + cn_block.nonce = 0; + + // block + // Coinbase transaction. + const uint32_t height = pindexPrev->nHeight+1; + const size_t miner_height = 10000; + const size_t median_size = 20000; + const uint64_t already_generated_coins = 10000; + const size_t current_block_size = 20000; + const uint64_t fee = 0; + const size_t max_outs = 1; + cryptonote::account_public_address miner_address; + cryptonote::transaction miner_tx; + cryptonote::blobdata extra_nonce; + // The reserve_offset is the offset for extra_nonce + extra_nonce.resize(reserve_size, 0); + + if(!cryptonote::get_account_address_from_str(miner_address, CN_DUMMY_ADDRESS)) { + throw JSONRPCError(RPC_INTERNAL_ERROR, "Internal error: failed to parse wallet address"); + } + + if (!construct_miner_tx(miner_height, median_size, already_generated_coins, current_block_size, + fee, miner_address, miner_tx, extra_nonce, max_outs)) { + throw JSONRPCError(RPC_INTERNAL_ERROR, "Internal error: failed to construct miner tx"); + } + cn_block.miner_tx = miner_tx; + + cryptonote::blobdata block_blob = cryptonote::t_serializable_object_to_blob(cn_block); + crypto::public_key tx_pub_key = cryptonote::get_tx_pub_key_from_extra(cn_block.miner_tx); + if(tx_pub_key == cryptonote::null_pkey) { + throw JSONRPCError(RPC_INTERNAL_ERROR, "Internal error: failed to tx pub key in coinbase extra"); + } + uint32_t reserved_offset = slow_memmem((void*)block_blob.data(), block_blob.size(), &tx_pub_key, sizeof(tx_pub_key)); + if(!reserved_offset) { + throw JSONRPCError(RPC_INTERNAL_ERROR, "Internal error: Failed to find tx pub key in blockblob"); + } + reserved_offset += sizeof(tx_pub_key) + 2; //2 bytes: tag for TX_EXTRA_NONCE(1 byte), counter in TX_EXTRA_NONCE(1 byte) + if(reserved_offset + reserve_size > block_blob.size()) + { + throw JSONRPCError(RPC_INTERNAL_ERROR, "Internal error: Failed to calculate offset"); + } + + // No transactions other than base one. + cn_block.tx_hashes.clear(); + + // Copy kevacoin block hash to the extra field, as a proof that the work has + // been done for the block. + // Copy all the txs to extra so that later we can use them to + // reconstruct the block. + const uint32_t blockHashSize = blockHash.size(); + const uint32_t txTotalSize = 1 + blockHashSize + 1 + 4 + pblock->vtx.size() * sizeof(CTransaction); + cn_block.miner_tx.extra.resize(cn_block.miner_tx.extra.size() + txTotalSize, 0); + size_t lastOffset = cn_block.miner_tx.extra.size(); + uint8_t miningTag = TX_EXTRA_KEVA_BLOCKHASH_TAG; + memcpy(cn_block.miner_tx.extra.data() + lastOffset, &miningTag, 1); + lastOffset ++; + memcpy(cn_block.miner_tx.extra.data() + lastOffset, blockHash.begin(), blockHashSize); + lastOffset += blockHashSize; + uint8_t kevaTxsTag = TX_EXTRA_KEVA_TX_LIST_TAG; + memcpy(cn_block.miner_tx.extra.data() + lastOffset, &kevaTxsTag, 1); + lastOffset ++; + uint32_t txsSize = ; + // This is not right! Need to serialize the transactions. + // Maybe using SerializationOp of CBlock to serialize the all block instead? + memcpy(cn_block.miner_tx.extra.data() + lastOffset, pblock->vtx.data(), pblock->vtx.size() * sizeof(CTransaction)); + + UniValue result(UniValue::VOBJ); + cryptonote::blobdata hashing_blob; + cryptonote::get_block_hashing_blob(cn_block, hashing_blob); + + //res.prev_hash = epee::string_tools::pod_to_hex(cn_block.prev_id); + //res.blocktemplate_blob = epee::string_tools::buff_to_hex_nodelimer(block_blob); + //res.blockhashing_blob = epee::string_tools::buff_to_hex_nodelimer(hashing_blob); + + result.push_back(Pair("blocktemplate_blob", pblock->hashPrevBlock.GetHex())); + result.push_back(Pair("difficulty", pblock->hashPrevBlock.GetHex())); + result.push_back(Pair("height", std::to_string(height))); + result.push_back(Pair("reserved_offset", std::to_string(reserved_offset))); return result; }