diff --git a/.gitignore b/.gitignore index a8722aa593ac3..d5140e07bdc89 100644 --- a/.gitignore +++ b/.gitignore @@ -103,7 +103,8 @@ linux-build win32-build qa/pull-tester/run-bitcoind-for-test.sh qa/pull-tester/tests_config.py -qa/pull-tester/cache/* +qa/pull-tester/cache +qa/pull-tester/cache_bigblock qa/pull-tester/test.*/* qa/tmp cache/ diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index add5612d0d986..7b0808ce1555f 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -127,6 +127,7 @@ testScripts.append('zmq_test.py') testScriptsExt = [ + 'bip100-sizelimit.py', 'bip9-softforks.py', 'bip65-cltv.py', 'bip65-cltv-p2p.py', diff --git a/qa/rpc-tests/.gitignore b/qa/rpc-tests/.gitignore index cb41d94423e46..f7a97fd75f038 100644 --- a/qa/rpc-tests/.gitignore +++ b/qa/rpc-tests/.gitignore @@ -1,2 +1,3 @@ *.pyc cache +cache_bigblock diff --git a/qa/rpc-tests/bip100-sizelimit.py b/qa/rpc-tests/bip100-sizelimit.py new file mode 100755 index 0000000000000..61a8d0d70c826 --- /dev/null +++ b/qa/rpc-tests/bip100-sizelimit.py @@ -0,0 +1,207 @@ +#!/usr/bin/env python3 +# Copyright (c) 2017 The Bitcoin developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +# +# Test mining and broadcast of larger-than-1MB-blocks +# +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * + +from decimal import Decimal + +CACHE_DIR = "cache_bigblock" + +class BigBlockTest(BitcoinTestFramework): + + def setup_chain(self): + print("Initializing test directory "+self.options.tmpdir) + + if not os.path.isdir(os.path.join(CACHE_DIR, "node0")): + print("Creating initial chain. This will be cached for future runs.") + + for i in range(4): + initialize_datadir(CACHE_DIR, i) # Overwrite port/rpcport in bitcoin.conf + + # Node 0 creates 8MB blocks that vote for increase to 8MB + # Node 1 creates empty blocks that vote for 8MB + # Node 2 creates empty blocks that vote for 2MB + # Node 3 creates empty blocks that do not vote for increase + self.nodes = [] + # Use node0 to mine blocks for input splitting + self.nodes.append(start_node(0, CACHE_DIR, ["-blockmaxsize=8000000", "-maxblocksizevote=8", "-limitancestorsize=2000", "-limitdescendantsize=2000"])) + self.nodes.append(start_node(1, CACHE_DIR, ["-blockmaxsize=1000", "-maxblocksizevote=8", "-limitancestorsize=2000", "-limitdescendantsize=2000"])) + self.nodes.append(start_node(2, CACHE_DIR, ["-blockmaxsize=1000", "-maxblocksizevote=1", "-limitancestorsize=2000", "-limitdescendantsize=2000"])) + self.nodes.append(start_node(3, CACHE_DIR, ["-blockmaxsize=1000", "-maxblocksizevote=2", "-limitancestorsize=2000", "-limitdescendantsize=2000"])) + + connect_nodes_bi(self.nodes, 0, 1) + connect_nodes_bi(self.nodes, 1, 2) + connect_nodes_bi(self.nodes, 2, 3) + connect_nodes_bi(self.nodes, 3, 0) + + self.is_network_split = False + + # Create a 2012-block chain in a 75% ratio for increase (genesis block votes for 1MB) + # Make sure they are not already sorted correctly + blocks = [] + blocks.append(self.nodes[1].generate(503)) + assert(self.sync_blocks(self.nodes[1:3])) + blocks.append(self.nodes[2].generate(502)) # <--- genesis is 503rd vote for 1MB + assert(self.sync_blocks(self.nodes[2:4])) + blocks.append(self.nodes[3].generate(503)) + assert(self.sync_blocks(self.nodes[1:4])) + blocks.append(self.nodes[1].generate(503)) + assert(self.sync_blocks(self.nodes)) + + tx_file = open(os.path.join(CACHE_DIR, "txdata"), "w") + + # Create a lot of tansaction data ready to be mined + fee = Decimal('.00005') + used = set() + print("Creating transaction data") + for i in range(0,25): + inputs = [] + outputs = {} + limit = 0 + utxos = self.nodes[3].listunspent(0) + for utxo in utxos: + if not utxo["txid"]+str(utxo["vout"]) in used: + raw_input = {} + raw_input["txid"] = utxo["txid"] + raw_input["vout"] = utxo["vout"] + inputs.append(raw_input) + outputs[self.nodes[3].getnewaddress()] = utxo["amount"] - fee + used.add(utxo["txid"]+str(utxo["vout"])) + limit = limit + 1 + if (limit >= 250): + break + rawtx = self.nodes[3].createrawtransaction(inputs, outputs) + txdata = self.nodes[3].signrawtransaction(rawtx)["hex"] + self.nodes[3].sendrawtransaction(txdata) + tx_file.write(txdata+"\n") + tx_file.close() + + stop_nodes(self.nodes) + wait_bitcoinds() + self.nodes = [] + for i in range(4): + os.remove(log_filename(CACHE_DIR, i, "db.log")) + os.remove(log_filename(CACHE_DIR, i, "peers.dat")) + os.remove(log_filename(CACHE_DIR, i, "fee_estimates.dat")) + + for i in range(4): + from_dir = os.path.join(CACHE_DIR, "node"+str(i)) + to_dir = os.path.join(self.options.tmpdir, "node"+str(i)) + shutil.copytree(from_dir, to_dir) + initialize_datadir(self.options.tmpdir, i) # Overwrite port/rpcport in bitcoin.conf + + def sync_blocks(self, rpc_connections, wait=1, max_wait=60): + """ + Wait until everybody has the same block count + """ + for i in range(0,max_wait): + if i > 0: time.sleep(wait) + counts = [ x.getblockcount() for x in rpc_connections ] + if counts == [ counts[0] ]*len(counts): + return True + return False + + def setup_network(self): + self.nodes = [] + + self.nodes.append(start_node(0, self.options.tmpdir, ["-blockmaxsize=8000000", "-maxblocksizevote=8", "-limitancestorsize=2000", "-limitdescendantsize=2000"], timewait=60)) + self.nodes.append(start_node(1, self.options.tmpdir, ["-blockmaxsize=1000", "-maxblocksizevote=8", "-limitancestorsize=2000", "-limitdescendantsize=2000"], timewait=60)) + self.nodes.append(start_node(2, self.options.tmpdir, ["-blockmaxsize=1000", "-maxblocksizevote=1", "-limitancestorsize=2000", "-limitdescendantsize=2000"], timewait=60)) + # (We don't restart the node with the huge wallet + connect_nodes_bi(self.nodes, 0, 1) + connect_nodes_bi(self.nodes, 1, 2) + connect_nodes_bi(self.nodes, 2, 0) + + self.load_mempool(self.nodes[0]) + + def load_mempool(self, node): + with open(os.path.join(CACHE_DIR, "txdata"), "r") as f: + for line in f: + node.sendrawtransaction(line.rstrip()) + + def TestMineBig(self, expect_big): + # Test if node0 will mine a block bigger than legacy MAX_BLOCK_SIZE + b1hash = self.nodes[0].generate(1)[0] + b1 = self.nodes[0].getblock(b1hash, True) + assert(self.sync_blocks(self.nodes[0:3])) + + if expect_big: + assert(b1['size'] > 1000*1000) + + # Have node1 mine on top of the block, + # to make sure it goes along with the fork + b2hash = self.nodes[1].generate(1)[0] + b2 = self.nodes[1].getblock(b2hash, True) + assert(b2['previousblockhash'] == b1hash) + assert(self.sync_blocks(self.nodes[0:3])) + + else: + assert(b1['size'] < 1000*1000) + + # Reset chain to before b1hash: + for node in self.nodes[0:3]: + node.invalidateblock(b1hash) + assert(self.sync_blocks(self.nodes[0:3])) + + + def run_test(self): + print("Testing consensus blocksize increase conditions") + + assert_equal(self.nodes[0].getblockcount(), 2011) # This is a 0-based height + + # Current nMaxBlockSize is still 1MB + assert_equal(self.nodes[0].getblocktemplate()["sizelimit"], 1000000) + self.TestMineBig(False) + + # Create a situation where the 1512th-highest vote is for 2MB + self.nodes[2].generate(1) + assert(self.sync_blocks(self.nodes[1:3])) + ahash = self.nodes[1].generate(3)[2] + assert_equal(self.nodes[1].getblocktemplate()["sizelimit"], int(1000000 * 1.05)) + assert(self.sync_blocks(self.nodes[0:2])) + self.TestMineBig(True) + + # Shutdown then restart node[0], it should produce a big block. + stop_node(self.nodes[0], 0) + self.nodes[0] = start_node(0, self.options.tmpdir, ["-blockmaxsize=8000000", "-maxblocksizevote=8", "-limitancestorsize=2000", "-limitdescendantsize=2000"], timewait=60) + self.load_mempool(self.nodes[0]) + connect_nodes_bi(self.nodes, 0, 1) + connect_nodes_bi(self.nodes, 0, 2) + assert_equal(self.nodes[0].getblocktemplate()["sizelimit"], int(1000000 * 1.05)) + self.TestMineBig(True) + + # Test re-orgs past the sizechange block + stop_node(self.nodes[0], 0) + self.nodes[2].invalidateblock(ahash) + assert_equal(self.nodes[2].getblocktemplate()["sizelimit"], 1000000) + self.nodes[2].generate(2) + assert_equal(self.nodes[2].getblocktemplate()["sizelimit"], 1000000) + assert(self.sync_blocks(self.nodes[1:3])) + + # Restart node0, it should re-org onto longer chain, + # and refuse to mine a big block: + self.nodes[0] = start_node(0, self.options.tmpdir, ["-blockmaxsize=8000000", "-maxblocksizevote=8", "-limitancestorsize=2000", "-limitdescendantsize=2000"], timewait=60) + self.load_mempool(self.nodes[0]) + connect_nodes_bi(self.nodes, 0, 1) + connect_nodes_bi(self.nodes, 0, 2) + assert(self.sync_blocks(self.nodes[0:3])) + assert_equal(self.nodes[0].getblocktemplate()["sizelimit"], 1000000) + self.TestMineBig(False) + + # Mine 4 blocks voting for 8MB. Bigger block NOT ok, we are in the next voting period + self.nodes[1].generate(4) + assert_equal(self.nodes[1].getblocktemplate()["sizelimit"], 1000000) + assert(self.sync_blocks(self.nodes[0:3])) + self.TestMineBig(False) + + + print("Cached test chain and transactions left in %s"%(CACHE_DIR)) + +if __name__ == '__main__': + BigBlockTest().main() diff --git a/qa/rpc-tests/p2p-fullblocktest.py b/qa/rpc-tests/p2p-fullblocktest.py index 56df8ffd01181..ccf06b15cd1e1 100755 --- a/qa/rpc-tests/p2p-fullblocktest.py +++ b/qa/rpc-tests/p2p-fullblocktest.py @@ -37,12 +37,13 @@ def __init__(self): self.block_time = int(time.time())+1 self.tip = None self.blocks = {} + self.test = None def run_test(self): - test = TestManager(self, self.options.tmpdir) - test.add_all_connections(self.nodes) + self.test = TestManager(self, self.options.tmpdir) + self.test.add_all_connections(self.nodes) NetworkThread().start() # Start up network handling in another thread - test.run() + self.test.run() def add_transactions_to_block(self, block, tx_list): [ tx.rehash() for tx in tx_list ] @@ -348,7 +349,16 @@ def update_block(block_number, new_transactions): tx.vout = [CTxOut(0, script_output)] b24 = update_block(24, [tx]) assert_equal(len(b24.serialize()), MAX_BLOCK_SIZE+1) - yield rejected(RejectResult(16, b'bad-blk-length')) + yield rejected() # Network sanity check will cause disconnect + + # Reconnect + self.test.clear_all_connections() + self.test.wait_for_disconnections() + self.test.add_all_connections(self.nodes) + # Ignore requests for the oversize block + [n.inv_hash_ignore.append(b24.sha256) for n in self.test.test_nodes] + NetworkThread().start() + self.test.wait_for_verack() b25 = block(25, spend=out7) yield rejected() diff --git a/qa/rpc-tests/test_framework/authproxy.py b/qa/rpc-tests/test_framework/authproxy.py index e5f7ab3656167..95b2be658cffe 100644 --- a/qa/rpc-tests/test_framework/authproxy.py +++ b/qa/rpc-tests/test_framework/authproxy.py @@ -124,6 +124,11 @@ def _request(self, method, path, postdata): return self._get_response() else: raise + except BrokenPipeError: + # Python 3.5+ raises this instead of BadStatusLine when the connection was reset + self.__conn.close() + self.__conn.request(method, path, postdata, headers) + return self._get_response() def __call__(self, *args): AuthServiceProxy.__id_count += 1 diff --git a/qa/rpc-tests/test_framework/comptool.py b/qa/rpc-tests/test_framework/comptool.py index 7c92d3f828917..a57a57f1bb655 100755 --- a/qa/rpc-tests/test_framework/comptool.py +++ b/qa/rpc-tests/test_framework/comptool.py @@ -52,6 +52,7 @@ def __init__(self, block_store, tx_store): self.tx_request_map = {} self.block_reject_map = {} self.tx_reject_map = {} + self.inv_hash_ignore = [] # When the pingmap is non-empty we're waiting for # a response @@ -77,15 +78,17 @@ def on_getheaders(self, conn, message): conn.send_message(response) def on_getdata(self, conn, message): - [conn.send_message(r) for r in self.block_store.get_blocks(message.inv)] - [conn.send_message(r) for r in self.tx_store.get_transactions(message.inv)] - - for i in message.inv: - if i.type == 1: + for idx, i in enumerate(message.inv): + if i.hash in self.inv_hash_ignore: + del message.inv[idx] + elif i.type == 1: self.tx_request_map[i.hash] = True elif i.type == 2: self.block_request_map[i.hash] = True + [conn.send_message(r) for r in self.block_store.get_blocks(message.inv)] + [conn.send_message(r) for r in self.tx_store.get_transactions(message.inv)] + def on_inv(self, conn, message): self.lastInv = [x.hash for x in message.inv] @@ -192,10 +195,10 @@ def veracked(): return all(node.verack_received for node in self.test_nodes) return wait_until(veracked, timeout=10) - def wait_for_pings(self, counter): + def wait_for_pings(self, counter, attempts=float('inf')): def received_pongs(): return all(node.received_ping_response(counter) for node in self.test_nodes) - return wait_until(received_pongs) + return wait_until(received_pongs, attempts) # sync_blocks: Wait for all connections to request the blockhash given # then send get_headers to find out the tip of each node, and synchronize @@ -217,7 +220,7 @@ def blocks_requested(): # Send ping and wait for response -- synchronization hack [ c.cb.send_ping(self.ping_counter) for c in self.connections ] - self.wait_for_pings(self.ping_counter) + self.wait_for_pings(self.ping_counter, attempts=20) self.ping_counter += 1 # Analogous to sync_block (see above) diff --git a/src/Makefile.am b/src/Makefile.am index 37ff9bccaf5db..49f230a6bd1f5 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -100,6 +100,7 @@ BITCOIN_CORE_H = \ dbwrapper.h \ limitedmap.h \ main.h \ + maxblocksize.h \ memusage.h \ merkleblock.h \ miner.h \ @@ -181,6 +182,7 @@ libbitcoin_server_a_SOURCES = \ init.cpp \ dbwrapper.cpp \ main.cpp \ + maxblocksize.cpp \ merkleblock.cpp \ miner.cpp \ net.cpp \ diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 226b1ec9c93b9..50c8f5e5bd385 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -55,11 +55,13 @@ BITCOIN_TESTS =\ test/limitedmap_tests.cpp \ test/dbwrapper_tests.cpp \ test/main_tests.cpp \ + test/maxblocksize_tests.cpp \ test/mempool_tests.cpp \ test/merkle_tests.cpp \ test/miner_tests.cpp \ test/multisig_tests.cpp \ test/netbase_tests.cpp \ + test/p2p_protocol_tests.cpp \ test/pmt_tests.cpp \ test/policyestimator_tests.cpp \ test/pow_tests.cpp \ diff --git a/src/alert.cpp b/src/alert.cpp index eb1cd5e7f6ebe..9b79a3d62224c 100644 --- a/src/alert.cpp +++ b/src/alert.cpp @@ -121,7 +121,7 @@ bool CAlert::AppliesTo(int nVersion, const std::string& strSubVerIn) const bool CAlert::AppliesToMe() const { - return AppliesTo(PROTOCOL_VERSION, FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, std::vector())); + return AppliesTo(PROTOCOL_VERSION, FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, std::vector(), 0)); } bool CAlert::RelayTo(CNode* pnode) const diff --git a/src/chain.h b/src/chain.h index ae6c4338d6102..4db6f17086e9f 100644 --- a/src/chain.h +++ b/src/chain.h @@ -14,6 +14,9 @@ #include +static const int BIP100_DBI_VERSION = 0x08000000; +static const int DISK_BLOCK_INDEX_VERSION = BIP100_DBI_VERSION; + struct CDiskBlockPos { int nFile; @@ -67,8 +70,8 @@ enum BlockStatus { /** * Only first tx is coinbase, 2 <= coinbase input script length <= 100, transactions valid, no duplicate txids, - * sigops, size, merkle root. Implies all parents are at least TREE but not necessarily TRANSACTIONS. When all - * parent blocks also have TRANSACTIONS, CBlockIndex::nChainTx will be set. + * merkle root. Implies all parents are at least TREE but not necessarily TRANSACTIONS. When all + * parent blocks also have TRANSACTIONS, CBlockIndex::nChainTx and maxblocksize will be set. */ BLOCK_VALID_TRANSACTIONS = 3, @@ -146,6 +149,15 @@ class CBlockIndex //! (memory only) Sequential id assigned to distinguish order in which blocks are received. uint32_t nSequenceId; + //! Index entry serial format version + int nSerialVersion; + + //! Maximum serialized block size at nHeight + uint64_t nMaxBlockSize; + + //! This block's vote for future maximum serialized block size + uint64_t nMaxBlockSizeVote; + void SetNull() { phashBlock = NULL; @@ -160,6 +172,9 @@ class CBlockIndex nChainTx = 0; nStatus = 0; nSequenceId = 0; + nSerialVersion = 0; + nMaxBlockSize = 0; + nMaxBlockSizeVote = 0; nVersion = 0; hashMerkleRoot = uint256(); @@ -292,6 +307,7 @@ class CDiskBlockIndex : public CBlockIndex explicit CDiskBlockIndex(const CBlockIndex* pindex) : CBlockIndex(*pindex) { hashPrev = (pprev ? pprev->GetBlockHash() : uint256()); + nSerialVersion = DISK_BLOCK_INDEX_VERSION; } ADD_SERIALIZE_METHODS; @@ -299,7 +315,7 @@ class CDiskBlockIndex : public CBlockIndex template inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { if (!(nType & SER_GETHASH)) - READWRITE(VARINT(nVersion)); + READWRITE(VARINT(nSerialVersion)); READWRITE(VARINT(nHeight)); READWRITE(VARINT(nStatus)); @@ -318,6 +334,11 @@ class CDiskBlockIndex : public CBlockIndex READWRITE(nTime); READWRITE(nBits); READWRITE(nNonce); + + if (nSerialVersion >= BIP100_DBI_VERSION) { + READWRITE(VARINT(nMaxBlockSize)); + READWRITE(VARINT(nMaxBlockSizeVote)); + } } uint256 GetBlockHash() const diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 4af6f34c6209a..703a370ad780e 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -92,6 +92,10 @@ class CMainParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nStartTime = 1462060800; // May 1st, 2016 consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nTimeout = 1493596800; // May 1st, 2017 + // BIP100 defined start height and max block size change critical vote position + consensus.bip100ActivationHeight = 449568; + consensus.nMaxBlockSizeChangePosition = 1512; + /** * The message start string is designed to be unlikely to occur in normal data. * The characters are rarely used upper ASCII, not valid as UTF-8, and produce @@ -185,6 +189,10 @@ class CTestNetParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nStartTime = 1456790400; // March 1st, 2016 consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nTimeout = 1493596800; // May 1st, 2017 + // BIP100 + consensus.bip100ActivationHeight = 798336; + consensus.nMaxBlockSizeChangePosition = 1512; + pchMessageStart[0] = 0x0b; pchMessageStart[1] = 0x11; pchMessageStart[2] = 0x09; @@ -257,6 +265,8 @@ class CRegTestParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_CSV].bit = 0; consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nStartTime = 0; consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nTimeout = 999999999999ULL; + consensus.bip100ActivationHeight = 0; + consensus.nMaxBlockSizeChangePosition = 1512; pchMessageStart[0] = 0xfa; pchMessageStart[1] = 0xbf; diff --git a/src/clientversion.cpp b/src/clientversion.cpp index aae0569bba211..9ab691668ed8e 100644 --- a/src/clientversion.cpp +++ b/src/clientversion.cpp @@ -7,6 +7,8 @@ #include "tinyformat.h" #include +#include +#include /** * Name of client reported in the 'version' message. Report the same name @@ -94,8 +96,18 @@ std::string FormatFullVersion() /** * Format the subversion field according to BIP 14 spec (https://github.com/bitcoin/bips/blob/master/bip-0014.mediawiki) */ -std::string FormatSubVersion(const std::string& name, int nClientVersion, const std::vector& comments) +std::string FormatSubVersion(const std::string& name, int nClientVersion, std::vector comments, uint64_t nMaxBlockSize) { + if (nMaxBlockSize) { + // Announce our excessive block acceptence. + comments.insert(comments.end(), std::string("BIP100")); + + std::stringstream ss; + double dMaxBlockSize = static_cast(nMaxBlockSize) / 1000000; + ss << "EB" << std::setprecision(static_cast(std::log10(dMaxBlockSize))+7) << dMaxBlockSize; + comments.insert(comments.end(), ss.str()); + } + std::ostringstream ss; ss << "/"; ss << name << ":" << FormatVersion(nClientVersion); diff --git a/src/clientversion.h b/src/clientversion.h index ab79da9b36b87..b3b648d2ff04f 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -50,6 +50,7 @@ #include #include +#include static const int CLIENT_VERSION = 1000000 * CLIENT_VERSION_MAJOR @@ -63,7 +64,7 @@ extern const std::string CLIENT_DATE; std::string FormatFullVersion(); -std::string FormatSubVersion(const std::string& name, int nClientVersion, const std::vector& comments); +std::string FormatSubVersion(const std::string& name, int nClientVersion, std::vector comments, uint64_t nMaxBlockSize); #endif // WINDRES_PREPROC diff --git a/src/consensus/consensus.h b/src/consensus/consensus.h index ad9cc26175351..4bd72bc7197ae 100644 --- a/src/consensus/consensus.h +++ b/src/consensus/consensus.h @@ -6,10 +6,16 @@ #ifndef BITCOIN_CONSENSUS_CONSENSUS_H #define BITCOIN_CONSENSUS_CONSENSUS_H -/** The maximum allowed size for a serialized block, in bytes (network rule) */ +#include + +/** Legacy maximum block size */ static const unsigned int MAX_BLOCK_SIZE = 1000000; +/** The maximum allowed size for a serialized transaction, in bytes */ +static const unsigned int MAX_TRANSACTION_SIZE = 1000*1000; /** The maximum allowed number of signature check operations in a block (network rule) */ -static const unsigned int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50; +inline uint64_t MaxBlockSigops(uint64_t nBlockSize) { + return ((nBlockSize - 1) / 1000000 + 1) * 1000000 / 50; +} /** Coinbase transaction outputs can only be spent after this number of new blocks (network rule) */ static const int COINBASE_MATURITY = 100; diff --git a/src/consensus/params.h b/src/consensus/params.h index 6c4cc49479ba1..9928d092ab392 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -53,6 +53,13 @@ struct Params { uint32_t nRuleChangeActivationThreshold; uint32_t nMinerConfirmationWindow; BIP9Deployment vDeployments[MAX_VERSION_BITS_DEPLOYMENTS]; + /** + * BIP100: One-based position from beginning (end) of the ascending sorted list of max block size + * votes in a retarget interval, at which the possible new lower (higher) max block size is read. + * 1512 = 75th percentile of 2016 + */ + int bip100ActivationHeight; + uint32_t nMaxBlockSizeChangePosition; /** Proof of work parameters */ uint256 powLimit; bool fPowAllowMinDifficultyBlocks; diff --git a/src/init.cpp b/src/init.cpp index eda9105a7335a..35552f3b6ec1c 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -493,7 +493,8 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageGroup(_("Block creation options:")); strUsage += HelpMessageOpt("-blockminsize=", strprintf(_("Set minimum block size in bytes (default: %u)"), DEFAULT_BLOCK_MIN_SIZE)); - strUsage += HelpMessageOpt("-blockmaxsize=", strprintf(_("Set maximum block size in bytes (default: %d)"), DEFAULT_BLOCK_MAX_SIZE)); + strUsage += HelpMessageOpt("-blockmaxsize=", _("Set maximum block size in bytes (default: network sizelimit)")); + strUsage += HelpMessageOpt("-maxblocksizevote=", _("Set vote for maximum block size in megabytes (default: network sizelimit)")); strUsage += HelpMessageOpt("-blockprioritysize=", strprintf(_("Set maximum size of high-priority/low-fee transactions in bytes (default: %d)"), DEFAULT_BLOCK_PRIORITY_SIZE)); if (showDebug) strUsage += HelpMessageOpt("-blockversion=", "Override block version to test forking scenarios"); @@ -1022,6 +1023,10 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) fAlerts = GetBoolArg("-alerts", DEFAULT_ALERTS); + uint64_t nMaxBlockSizeVote = GetArg("-maxblocksizevote", 0); + if (nMaxBlockSizeVote > 999) + InitWarning(_("Warning: Max block size vote is very large. Units are megabytes.\n")); + // Option to startup with mocktime set (used for regression testing): SetMockTime(GetArg("-mocktime", 0)); // SetMockTime(0) is a no-op @@ -1134,17 +1139,17 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) RegisterNodeSignals(GetNodeSignals()); // sanitize comments per BIP-0014, format user agent and check total size - std::vector uacomments; BOOST_FOREACH(string cmt, mapMultiArgs["-uacomment"]) { if (cmt != SanitizeString(cmt, SAFE_CHARS_UA_COMMENT)) return InitError(strprintf(_("User Agent comment (%s) contains unsafe characters."), cmt)); - uacomments.push_back(SanitizeString(cmt, SAFE_CHARS_UA_COMMENT)); + vUAComments.push_back(SanitizeString(cmt, SAFE_CHARS_UA_COMMENT)); } - strSubVersion = FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, uacomments); - if (strSubVersion.size() > MAX_SUBVERSION_LENGTH) { + uint64_t hugeBlock = static_cast((100000. / 3.) * MAX_BLOCK_SIZE); + size_t userAgentLen = FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, vUAComments, hugeBlock).size(); + if (userAgentLen > MAX_SUBVERSION_LENGTH) { return InitError(strprintf(_("Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments."), - strSubVersion.size(), MAX_SUBVERSION_LENGTH)); + userAgentLen, MAX_SUBVERSION_LENGTH)); } if (mapArgs.count("-onlynet")) { @@ -1335,8 +1340,11 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) CleanupBlockRevFiles(); } - if (!LoadBlockIndex()) { + bool fRebuildRequired = false; + if (!LoadBlockIndex(&fRebuildRequired)) { strLoadError = _("Error loading block database"); + if (fRebuildRequired) + strLoadError += _(". You need to rebuild the database using -reindex."); break; } diff --git a/src/main.cpp b/src/main.cpp index 3dd8f34aebb73..1b642dbc9352b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -16,6 +16,7 @@ #include "consensus/validation.h" #include "hash.h" #include "init.h" +#include "maxblocksize.h" #include "merkleblock.h" #include "net.h" #include "policy/policy.h" @@ -39,6 +40,7 @@ #include #include +#include #include #include #include @@ -91,6 +93,8 @@ map mapOrphanTransactions GUARDED_BY(cs_main);; map > mapOrphanTransactionsByPrev GUARDED_BY(cs_main);; void EraseOrphansFor(NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(cs_main); +static bool SanityCheckMessage(CNode* peer, const CNetMessage& msg); + /** * Returns true if there are nRequired or more blocks of minVersion or above * in the last Consensus::Params::nMajorityWindow blocks, starting at pstart and going backwards. @@ -299,6 +303,12 @@ int GetHeight() return chainActive.Height(); } +int GetMaxBlockSize() +{ + LOCK(cs_main); + return chainActive.Tip()->nMaxBlockSize; +} + void UpdatePreferredDownload(CNode* node, CNodeState* state) { nPreferredDownload -= state->fPreferredDownload; @@ -563,19 +573,23 @@ bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) { void RegisterNodeSignals(CNodeSignals& nodeSignals) { nodeSignals.GetHeight.connect(&GetHeight); + nodeSignals.SanityCheckMessages.connect(&SanityCheckMessage); nodeSignals.ProcessMessages.connect(&ProcessMessages); nodeSignals.SendMessages.connect(&SendMessages); nodeSignals.InitializeNode.connect(&InitializeNode); nodeSignals.FinalizeNode.connect(&FinalizeNode); + nodeSignals.GetMaxBlockSize.connect(&GetMaxBlockSize); } void UnregisterNodeSignals(CNodeSignals& nodeSignals) { nodeSignals.GetHeight.disconnect(&GetHeight); + nodeSignals.SanityCheckMessages.disconnect(&SanityCheckMessage); nodeSignals.ProcessMessages.disconnect(&ProcessMessages); nodeSignals.SendMessages.disconnect(&SendMessages); nodeSignals.InitializeNode.disconnect(&InitializeNode); nodeSignals.FinalizeNode.disconnect(&FinalizeNode); + nodeSignals.GetMaxBlockSize.disconnect(&GetMaxBlockSize); } CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator) @@ -942,7 +956,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state) if (tx.vout.empty()) return state.DoS(10, false, REJECT_INVALID, "bad-txns-vout-empty"); // Size limits - if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) + if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) > MAX_TRANSACTION_SIZE) return state.DoS(100, false, REJECT_INVALID, "bad-txns-oversize"); // Check for negative or overflow output values @@ -1577,8 +1591,12 @@ CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams) return nSubsidy; } +bool fForceInitialBlockDownload = false; bool IsInitialBlockDownload() { + if (fForceInitialBlockDownload) + return false; + const CChainParams& chainParams = Params(); LOCK(cs_main); if (fImporting || fReindex) @@ -2247,6 +2265,13 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin int64_t nTime1 = GetTimeMicros(); nTimeCheck += nTime1 - nTimeStart; LogPrint("bench", " - Sanity checks: %.2fms [%.2fs]\n", 0.001 * (nTime1 - nTimeStart), nTimeCheck * 0.000001); + // Block size limit (BIP100) + if (block.vtx.size() > pindex->nMaxBlockSize) + return state.DoS(100, error("%s: size limits failed", __func__), REJECT_INVALID, "bad-vtx-length"); + uint64_t nBlockSize = ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION); + if (nBlockSize > pindex->nMaxBlockSize) + return state.DoS(100, error("%s: size limits failed", __func__), REJECT_INVALID, "bad-blk-length"); + // Do not allow blocks that contain transactions which 'overwrite' older transactions, // unless those are already completely spent. // If such overwrites are allowed, coinbases and transactions depending upon those @@ -2328,7 +2353,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin nInputs += tx.vin.size(); nSigOps += GetLegacySigOpCount(tx); - if (nSigOps > MAX_BLOCK_SIGOPS) + if (nSigOps > MaxBlockSigops(nBlockSize)) return state.DoS(100, error("ConnectBlock(): too many sigops"), REJECT_INVALID, "bad-blk-sigops"); @@ -2357,7 +2382,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin // this is to prevent a "rogue miner" from creating // an incredibly-expensive-to-validate block. nSigOps += GetP2SHSigOpCount(tx, view); - if (nSigOps > MAX_BLOCK_SIGOPS) + if (nSigOps > MaxBlockSigops(nBlockSize)) return state.DoS(100, error("ConnectBlock(): too many sigops"), REJECT_INVALID, "bad-blk-sigops"); } @@ -3078,6 +3103,7 @@ bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBl pindexNew->nFile = pos.nFile; pindexNew->nDataPos = pos.nPos; pindexNew->nUndoPos = 0; + pindexNew->nMaxBlockSizeVote = GetMaxBlockSizeVote(block.vtx[0].vin[0].scriptSig, pindexNew->nHeight); pindexNew->nStatus |= BLOCK_HAVE_DATA; pindexNew->RaiseValidity(BLOCK_VALID_TRANSACTIONS); setDirtyBlockIndex.insert(pindexNew); @@ -3096,6 +3122,7 @@ bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBl LOCK(cs_nBlockSequenceId); pindex->nSequenceId = nBlockSequenceId++; } + pindex->nMaxBlockSize = GetNextMaxBlockSize(pindex->pprev, Params().GetConsensus()); if (chainActive.Tip() == NULL || !setBlockIndexCandidates.value_comp()(pindex, chainActive.Tip())) { setBlockIndexCandidates.insert(pindex); } @@ -3126,7 +3153,7 @@ bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAdd } if (!fKnown) { - while (vinfoBlockFile[nFile].nSize + nAddSize >= MAX_BLOCKFILE_SIZE) { + while (vinfoBlockFile[nFile].nSize + nAddSize >= GetNextMaxBlockSize(chainActive.Tip(), Params().GetConsensus()) * MIN_BLOCKFILE_BLOCKS) { nFile++; if (vinfoBlockFile.size() <= nFile) { vinfoBlockFile.resize(nFile + 1); @@ -3252,9 +3279,8 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo // because we receive the wrong transactions for it. // Size limits - if (block.vtx.empty() || block.vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) - return state.DoS(100, error("CheckBlock(): size limits failed"), - REJECT_INVALID, "bad-blk-length"); + if (block.vtx.empty()) + return state.DoS(100, error("CheckBlock(): no transactions"), REJECT_INVALID, "bad-blk-length"); // First transaction must be coinbase, the rest must not be if (block.vtx.empty() || !block.vtx[0].IsCoinBase()) @@ -3272,17 +3298,16 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo tx.GetHash().ToString(), FormatStateMessage(state)); + if (fCheckPOW && fCheckMerkleRoot) + block.fChecked = true; + unsigned int nSigOps = 0; BOOST_FOREACH(const CTransaction& tx, block.vtx) { nSigOps += GetLegacySigOpCount(tx); } - if (nSigOps > MAX_BLOCK_SIGOPS) - return state.DoS(100, error("CheckBlock(): out-of-bounds SigOpCount"), - REJECT_INVALID, "bad-blk-sigops"); - - if (fCheckPOW && fCheckMerkleRoot) - block.fChecked = true; + if (nSigOps > MaxBlockSigops(::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION))) + return state.DoS(100, error("CheckBlock(): out-of-bounds SigOpCount"), REJECT_INVALID, "bad-blk-sigops", true); return true; } @@ -3533,6 +3558,7 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams, CBlockIndex indexDummy(block); indexDummy.pprev = pindexPrev; indexDummy.nHeight = pindexPrev->nHeight + 1; + indexDummy.nMaxBlockSize = GetNextMaxBlockSize(pindexPrev, chainparams.GetConsensus()); // NOTE: CheckBlockHeader is called by CheckBlock if (!ContextualCheckBlockHeader(block, state, pindexPrev)) @@ -3720,7 +3746,7 @@ CBlockIndex * InsertBlockIndex(uint256 hash) return pindexNew; } -bool static LoadBlockIndexDB() +bool static LoadBlockIndexDB(bool* fRebuildRequired) { const CChainParams& chainparams = Params(); if (!pblocktree->LoadBlockIndexGuts()) @@ -3737,8 +3763,10 @@ bool static LoadBlockIndexDB() vSortedByHeight.push_back(make_pair(pindex->nHeight, pindex)); } sort(vSortedByHeight.begin(), vSortedByHeight.end()); - BOOST_FOREACH(const PAIRTYPE(int, CBlockIndex*)& item, vSortedByHeight) + vector >::iterator firstBIP100Entry = vSortedByHeight.end(); + for (vector >::iterator iter = vSortedByHeight.begin(); iter != vSortedByHeight.end(); iter++) { + const PAIRTYPE(int, CBlockIndex*)& item = *iter; CBlockIndex* pindex = item.second; pindex->nChainWork = (pindex->pprev ? pindex->pprev->nChainWork : 0) + GetBlockProof(*pindex); // We can link the chain of blocks for which we've received transactions at some point. @@ -3763,6 +3791,11 @@ bool static LoadBlockIndexDB() pindex->BuildSkip(); if (pindex->IsValid(BLOCK_VALID_TREE) && (pindexBestHeader == NULL || CBlockIndexWorkComparator()(pindexBestHeader, pindex))) pindexBestHeader = pindex; + if (item.first < chainparams.GetConsensus().bip100ActivationHeight) { + pindex->nMaxBlockSize = MAX_BLOCK_SIZE; + } else if (firstBIP100Entry == vSortedByHeight.end()) { + firstBIP100Entry = iter; + } } // Load block file info @@ -3810,6 +3843,35 @@ bool static LoadBlockIndexDB() pblocktree->ReadReindexing(fReindexing); fReindex |= fReindexing; + // Set max block size variables in any remaining pre-BIP100 index entries + bool fUpdatedEntries = false; + for (vector >::iterator iter = firstBIP100Entry; iter != vSortedByHeight.end(); iter++) { + const PAIRTYPE(int, CBlockIndex*)& item = *iter; + CBlockIndex* pindex = item.second; + if (pindex->nSerialVersion < BIP100_DBI_VERSION) { + if (!fUpdatedEntries) { + uiInterface.InitMessage(_("Updating block index for BIP100...")); + fUpdatedEntries = true; + } + pindex->nSerialVersion = DISK_BLOCK_INDEX_VERSION; + if (pindex->nChainTx) { + CBlock block; + if (ReadBlockFromDisk(block, pindex, chainparams.GetConsensus())) { + pindex->nMaxBlockSizeVote = GetMaxBlockSizeVote(block.vtx[0].vin[0].scriptSig, pindex->nHeight); + pindex->nMaxBlockSize = GetNextMaxBlockSize(pindex->pprev, chainparams.GetConsensus()); + } else { + // Error: Can't reconstruct vote. Rebuild required. + // This can happen if the blocks were pruned after BIP100 + // activation with pre-BIP100 software. + *fRebuildRequired = true; + return false; + } + } + LogPrint("reindex", "%s: Updating block index entry at height %d for BIP100\n", __func__, pindex->nHeight); + setDirtyBlockIndex.insert(pindex); + } + } + // Check whether we have a transaction index pblocktree->ReadFlag("txindex", fTxIndex); LogPrintf("%s: transaction index %s\n", __func__, fTxIndex ? "enabled" : "disabled"); @@ -3952,15 +4014,16 @@ void UnloadBlockIndex() fHavePruned = false; } -bool LoadBlockIndex() +bool LoadBlockIndex(bool* fRebuildRequired) { + assert(fRebuildRequired != NULL); // Load block index from databases - if (!fReindex && !LoadBlockIndexDB()) + if (!fReindex && !LoadBlockIndexDB(fRebuildRequired)) return false; return true; } -bool InitBlockIndex(const CChainParams& chainparams) +bool InitBlockIndex(const CChainParams& chainparams) { LOCK(cs_main); @@ -4031,7 +4094,7 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB continue; // read size blkdat >> nSize; - if (nSize < 80 || nSize > MAX_BLOCK_SIZE) + if (nSize < 80) continue; } catch (const std::exception&) { // no valid block header found; don't complain @@ -4365,6 +4428,36 @@ std::string GetWarnings(const std::string& strFor) // Messages // +static std::map maxMessageSizes = boost::assign::map_list_of + // values list the max size of each part of the message payload currently defined/used. + // values equate to the max payload size for that respective message type. + ("getaddr", 0) + ("mempool", 0) + ("ping", 8) + ("pong", 8) + ("verack", 0) + ("version", 4 + 8 + 8 + (4 + 8 + 16 + 2) + (4 + 8 + 16 + 2) + 8 + (3 + 256) + 4 + 1) + ("filterclear", 0) + ("reject", (1 + 12) + 1 + (1 + 111) + 32) // this is loose max because the max valid is actually 151 bytes as of BIP 61. see the p2p_protocol_tests unit tests. + ; + +bool static SanityCheckMessage(CNode* peer, const CNetMessage& msg) +{ + const std::string& strCommand = msg.hdr.GetCommand(); + uint64_t nMaxMessageSize = chainActive.Tip()->nMaxBlockSize * 105 / 100; + if (msg.hdr.nMessageSize > nMaxMessageSize || + (maxMessageSizes.count(strCommand) && msg.hdr.nMessageSize > maxMessageSizes[strCommand])) { + LogPrint("net", "Oversized %s message from peer=%i (%d bytes)\n", + SanitizeString(strCommand), peer->GetId(), msg.hdr.nMessageSize); + Misbehaving(peer->GetId(), 20); + return msg.hdr.nMessageSize <= nMaxMessageSize; + } + // This would be a good place for more sophisticated DoS detection/prevention. + // (e.g. disconnect a peer that is flooding us with excessive messages) + + return true; +} + bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { @@ -4538,7 +4631,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam } } -bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int64_t nTimeReceived) +bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int64_t nTimeReceived) { const CChainParams& chainparams = Params(); RandAddSeedPerfmon(); @@ -5477,6 +5570,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } catch (const std::ios_base::failure&) { // Avoid feedback loops by preventing reject messages from triggering a new reject message. LogPrint("net", "Unparseable reject message received\n"); + return false; } } } @@ -5601,7 +5695,7 @@ bool ProcessMessages(CNode* pfrom) } if (!fRet) - LogPrintf("%s(%s, %u bytes) FAILED peer=%d\n", __func__, SanitizeString(strCommand), nMessageSize, pfrom->id); + LogPrint("net", "%s(%s, %u bytes) FAILED peer=%d\n", __func__, SanitizeString(strCommand), nMessageSize, pfrom->id); break; } diff --git a/src/main.h b/src/main.h index 838d766809467..d0ee1a3acc268 100644 --- a/src/main.h +++ b/src/main.h @@ -62,8 +62,8 @@ static const unsigned int DEFAULT_DESCENDANT_LIMIT = 25; static const unsigned int DEFAULT_DESCENDANT_SIZE_LIMIT = 101; /** Default for -mempoolexpiry, expiration time for mempool transactions in hours */ static const unsigned int DEFAULT_MEMPOOL_EXPIRY = 72; -/** The maximum size of a blk?????.dat file (since 0.8) */ -static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB +/** Minimum number of max-sized blocks in blk?????.dat files */ +static const unsigned int MIN_BLOCKFILE_BLOCKS = 128; /** The pre-allocation chunk size for blk?????.dat files (since 0.8) */ static const unsigned int BLOCKFILE_CHUNK_SIZE = 0x1000000; // 16 MiB /** The pre-allocation chunk size for rev?????.dat files (since 0.8) */ @@ -209,11 +209,13 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB /** Initialize a new block tree database + block data on disk */ bool InitBlockIndex(const CChainParams& chainparams); /** Load the block tree and coins database from disk */ -bool LoadBlockIndex(); +bool LoadBlockIndex(bool* fRebuildRequired); /** Unload database information */ void UnloadBlockIndex(); /** Process protocol messages received from a given node */ bool ProcessMessages(CNode* pfrom); +/** Process a single message from a given node */ +bool ProcessMessage(CNode* pfrom, std::string strCommand, CDataStream& vRecv, int64_t nTimeReceived); /** * Send queued protocol messages to be sent to a give node. * @@ -225,6 +227,7 @@ void ThreadScriptCheck(); /** Try to detect Partition (network isolation) attacks against us */ void PartitionCheck(bool (*initialDownloadCheck)(), CCriticalSection& cs, const CBlockIndex *const &bestHeader, int64_t nPowTargetSpacing); /** Check whether we are doing an initial block download (synchronizing from disk or network) */ +extern bool fForceInitialBlockDownload; bool IsInitialBlockDownload(); /** Format a string that describes several potential problems detected by the core. * strFor can have three values: diff --git a/src/maxblocksize.cpp b/src/maxblocksize.cpp new file mode 100644 index 0000000000000..64af2dda8959d --- /dev/null +++ b/src/maxblocksize.cpp @@ -0,0 +1,141 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2017 The Bitcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "maxblocksize.h" + +#include "chain.h" +#include "util.h" +#include +#include +#include + +uint64_t GetNextMaxBlockSize(const CBlockIndex* pindexLast, const Consensus::Params& params) +{ + // BIP100 not active, return legacy max size + if (pindexLast == NULL || pindexLast->nHeight < params.bip100ActivationHeight) + return MAX_BLOCK_SIZE; + + uint64_t nMaxBlockSize = pindexLast->nMaxBlockSize; + + // Only change once per difficulty adjustment interval + if ((pindexLast->nHeight+1) % params.DifficultyAdjustmentInterval() != 0) { + return nMaxBlockSize; + } + + std::vector votes; + const CBlockIndex *pindexWalk = pindexLast; + for (int64_t i = 0; i < params.DifficultyAdjustmentInterval(); i++) { + assert(pindexWalk); + assert(pindexWalk->nMaxBlockSize == nMaxBlockSize); + votes.push_back(pindexWalk->nMaxBlockSizeVote ? pindexWalk->nMaxBlockSizeVote : pindexWalk->nMaxBlockSize); + pindexWalk = pindexWalk->pprev; + } + + std::sort(votes.begin(),votes.end()); + uint64_t lowerValue = votes.at(params.nMaxBlockSizeChangePosition - 1); + uint64_t raiseValue = votes.at(params.DifficultyAdjustmentInterval() - params.nMaxBlockSizeChangePosition); + + assert(lowerValue >= 1000000); // minimal vote supported is 1MB + assert(lowerValue >= raiseValue); // lowerValue comes from a higher sorted position + + uint64_t raiseCap = nMaxBlockSize * 105 / 100; + raiseValue = (raiseValue > raiseCap ? raiseCap : raiseValue); + if (raiseValue > nMaxBlockSize) { + nMaxBlockSize = raiseValue; + } else { + uint64_t lowerFloor = nMaxBlockSize * 100 / 105; + lowerValue = (lowerValue < lowerFloor ? lowerFloor : lowerValue); + if (lowerValue < nMaxBlockSize) + nMaxBlockSize = lowerValue; + } + + if (nMaxBlockSize != pindexLast->nMaxBlockSize) { + LogPrintf("GetNextMaxBlockSize RETARGET\n"); + LogPrintf("Before: %d\n", pindexLast->nMaxBlockSize); + LogPrintf("After: %d\n", nMaxBlockSize); + } + + return nMaxBlockSize; +} + +static uint32_t FindVote(const std::string& coinbase) { + + bool eb_vote = false; + uint32_t ebVoteMB = 0; + std::vector curr; + bool bip100vote = false; + bool started = false; + + BOOST_FOREACH (char s, coinbase) { + if (s == '/') { + started = true; + // End (or beginning) of a potential vote string. + + if (curr.size() < 2) // Minimum vote string length is 2 + { + bip100vote = false; + curr.clear(); + continue; + } + + if (curr.size()==6 && curr[0]=='B' && curr[1]=='I' && curr[2]=='P' && + curr[3]=='1' && curr[4]=='0' && curr[5]=='0') { + bip100vote = true; + curr.clear(); + continue; + } + + // Look for a B vote. + if (bip100vote && curr[0] == 'B') { + try { + return boost::lexical_cast(std::string( + curr.begin() + 1, curr.end())); + } + catch (const std::exception& e) { + LogPrintf("Invalid coinbase B-vote: %s\n", e.what()); + } + } + + // Look for a EB vote. Keep it, but continue to look for a BIP100/B vote. + if (!eb_vote && curr.size() > 2 && curr[0] == 'E' && curr[1] == 'B') { + try { + ebVoteMB = boost::lexical_cast(std::string( + curr.begin() + 2, curr.end())); + eb_vote = true; + } + catch (const std::exception& e) { + LogPrintf("Invalid coinbase EB-vote: %s\n", e.what()); + } + } + + bip100vote = false; + curr.clear(); + continue; + } + else if (!started) + continue; + else + curr.push_back(s); + } + return ebVoteMB; +} + +uint64_t GetMaxBlockSizeVote(const CScript &coinbase, int32_t nHeight) +{ + // Skip encoded height if found at start of coinbase + CScript expect = CScript() << nHeight; + int searchStart = coinbase.size() >= expect.size() && + std::equal(expect.begin(), expect.end(), coinbase.begin()) + ? expect.size() + :0; + + std::string s(coinbase.begin() + searchStart, coinbase.end()); + + if (s.length() < 5) // shortest vote is /EB1/ + return 0; + + + return static_cast(FindVote(s)) * 1000000; +} diff --git a/src/maxblocksize.h b/src/maxblocksize.h new file mode 100644 index 0000000000000..551ff1b3ccb1c --- /dev/null +++ b/src/maxblocksize.h @@ -0,0 +1,22 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2017 The Bitcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_MAXBLOCKSIZE_H +#define BITCOIN_MAXBLOCKSIZE_H + +#include "consensus/consensus.h" +#include "consensus/params.h" +#include "script/script.h" + +#include + +class CBlockHeader; +class CBlockIndex; + +uint64_t GetNextMaxBlockSize(const CBlockIndex* pindexLast, const Consensus::Params&); + +uint64_t GetMaxBlockSizeVote(const CScript &coinbase, int32_t nHeight); + +#endif // BITCOIN_MAXBLOCKSIZE_H diff --git a/src/miner.cpp b/src/miner.cpp index d095d418feac7..dadc5005e2df1 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -14,6 +14,7 @@ #include "consensus/validation.h" #include "hash.h" #include "main.h" +#include "maxblocksize.h" #include "net.h" #include "policy/policy.h" #include "pow.h" @@ -28,6 +29,8 @@ #include #include #include +#include +#include using namespace std; @@ -71,6 +74,23 @@ int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParam return nNewTime - nOldTime; } +// BIP100 string: +// - Adds our block size vote (B) if configured. +// - Adds Excessive Block (EB) string. This announces how big blocks we currently accept. +std::vector BIP100Str(uint64_t hardLimit) { + uint64_t blockVote = GetArg("-maxblocksizevote", 0); + + std::stringstream ss; + ss << "/BIP100/"; + if (blockVote) + ss << "B" << blockVote << "/"; + double dMaxBlockSize = double(hardLimit)/1000000; + ss << "EB" << std::setprecision(int(log10(dMaxBlockSize))+7) << dMaxBlockSize << "/"; + + const std::string s = ss.str(); + return std::vector(s.begin(), s.end()); +} + CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& scriptPubKeyIn) { // Create new block @@ -92,19 +112,20 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& s pblocktemplate->vTxSigOps.push_back(-1); // updated at end // Largest block you're willing to create: - unsigned int nBlockMaxSize = GetArg("-blockmaxsize", DEFAULT_BLOCK_MAX_SIZE); - // Limit to between 1K and MAX_BLOCK_SIZE-1K for sanity: - nBlockMaxSize = std::max((unsigned int)1000, std::min((unsigned int)(MAX_BLOCK_SIZE-1000), nBlockMaxSize)); + uint64_t hardLimit = GetNextMaxBlockSize(chainActive.Tip(), chainparams.GetConsensus()); + uint64_t nBlockMaxSize = GetArg("-blockmaxsize", hardLimit); + // Limit to between 1K and (hard limit - 1K) for sanity: + nBlockMaxSize = std::max((uint64_t)1000, std::min((hardLimit - 1000), nBlockMaxSize)); // How much of the block should be dedicated to high-priority transactions, // included regardless of the fees they pay unsigned int nBlockPrioritySize = GetArg("-blockprioritysize", DEFAULT_BLOCK_PRIORITY_SIZE); - nBlockPrioritySize = std::min(nBlockMaxSize, nBlockPrioritySize); + nBlockPrioritySize = std::min(nBlockMaxSize, (uint64_t)nBlockPrioritySize); // Minimum block size you want to create; block will be filled with free transactions // until there are no more or the block reaches this size: unsigned int nBlockMinSize = GetArg("-blockminsize", DEFAULT_BLOCK_MIN_SIZE); - nBlockMinSize = std::min(nBlockMaxSize, nBlockMinSize); + nBlockMinSize = std::min(nBlockMaxSize, (uint64_t)nBlockMinSize); // Collect memory pool transactions into the block CTxMemPool::setEntries inBlock; @@ -224,9 +245,11 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& s if (!IsFinalTx(tx, nHeight, nLockTimeCutoff)) continue; + // TODO: with more complexity we could make the block bigger when + // sigop-constrained and sigop density in later megabytes is low unsigned int nTxSigOps = iter->GetSigOpCount(); - if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) { - if (nBlockSigOps > MAX_BLOCK_SIGOPS - 2) { + if (nBlockSigOps + nTxSigOps >= MaxBlockSigops(nBlockSize)) { + if (nBlockSigOps > MaxBlockSigops(nBlockSize) - 2) { break; } continue; @@ -278,7 +301,7 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& s // Compute final coinbase transaction. txNew.vout[0].nValue = nFees + GetBlockSubsidy(nHeight, chainparams.GetConsensus()); - txNew.vin[0].scriptSig = CScript() << nHeight << OP_0; + txNew.vin[0].scriptSig = CScript() << nHeight << BIP100Str(hardLimit) << OP_0; pblock->vtx[0] = txNew; pblocktemplate->vTxFees[0] = -nFees; @@ -298,7 +321,7 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& s return pblocktemplate.release(); } -void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned int& nExtraNonce) +void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned int& nExtraNonce, uint64_t nMaxBlockSize) { // Update nExtraNonce static uint256 hashPrevBlock; @@ -310,7 +333,7 @@ void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned ++nExtraNonce; unsigned int nHeight = pindexPrev->nHeight+1; // Height first in coinbase required for block.version=2 CMutableTransaction txCoinbase(pblock->vtx[0]); - txCoinbase.vin[0].scriptSig = (CScript() << nHeight << CScriptNum(nExtraNonce)) + COINBASE_FLAGS; + txCoinbase.vin[0].scriptSig = (CScript() << nHeight << BIP100Str(nMaxBlockSize) << CScriptNum(nExtraNonce)) + COINBASE_FLAGS; assert(txCoinbase.vin[0].scriptSig.size() <= 100); pblock->vtx[0] = txCoinbase; @@ -425,7 +448,8 @@ void static BitcoinMiner(const CChainParams& chainparams) return; } CBlock *pblock = &pblocktemplate->block; - IncrementExtraNonce(pblock, pindexPrev, nExtraNonce); + uint64_t hardLimit = GetNextMaxBlockSize(pindexPrev, chainparams.GetConsensus()); + IncrementExtraNonce(pblock, pindexPrev, nExtraNonce, hardLimit); LogPrintf("Running BitcoinMiner with %u transactions in block (%u bytes)\n", pblock->vtx.size(), ::GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION)); diff --git a/src/miner.h b/src/miner.h index 512494198b7c1..31d1334fb50c3 100644 --- a/src/miner.h +++ b/src/miner.h @@ -34,7 +34,8 @@ void GenerateBitcoins(bool fGenerate, int nThreads, const CChainParams& chainpar /** Generate a new block, without valid proof-of-work */ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& scriptPubKeyIn); /** Modify the extranonce in a block */ -void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned int& nExtraNonce); +void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned int& nExtraNonce, uint64_t nMaxBlockSize); int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev); +std::vector BIP100Str(uint64_t hardlimit); #endif // BITCOIN_MINER_H diff --git a/src/net.cpp b/src/net.cpp index 5386f64822e5b..319f148f60603 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -84,7 +84,7 @@ static std::vector vhListenSocket; CAddrMan addrman; int nMaxConnections = DEFAULT_MAX_PEER_CONNECTIONS; bool fAddressesInitialized = false; -std::string strSubVersion; +std::vector vUAComments; vector vNodes; CCriticalSection cs_vNodes; @@ -442,6 +442,7 @@ void CNode::CloseSocketDisconnect() void CNode::PushVersion() { int nBestHeight = g_signals.GetHeight().get_value_or(0); + int nMaxBlockSize = g_signals.GetMaxBlockSize().get_value_or(0); int64_t nTime = (fInbound ? GetAdjustedTime() : GetTime()); CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService("0.0.0.0",0))); @@ -451,6 +452,7 @@ void CNode::PushVersion() LogPrint("net", "send version message: version %d, blocks=%d, us=%s, them=%s, peer=%d\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString(), addrYou.ToString(), id); else LogPrint("net", "send version message: version %d, blocks=%d, us=%s, peer=%d\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString(), id); + std::string strSubVersion = FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, vUAComments, nMaxBlockSize); PushMessage(NetMsgType::VERSION, PROTOCOL_VERSION, nLocalServices, nTime, addrYou, addrMe, nLocalHostNonce, strSubVersion, nBestHeight, !GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY)); } @@ -663,12 +665,10 @@ bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes) handled = msg.readData(pch, nBytes); if (handled < 0) - return false; + return false; - if (msg.in_data && msg.hdr.nMessageSize > MAX_PROTOCOL_MESSAGE_LENGTH) { - LogPrint("net", "Oversized message from peer=%i, disconnecting\n", GetId()); + if (msg.in_data && !g_signals.SanityCheckMessages(this, boost::ref(msg))) return false; - } pch += handled; nBytes -= handled; @@ -2582,14 +2582,14 @@ bool CBanDB::Read(banmap_t& banSet) // ... verify the network matches ours if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp))) return error("%s: Invalid network magic number", __func__); - + // de-serialize address data into one CAddrMan object ssBanlist >> banSet; } catch (const std::exception& e) { return error("%s: Deserialize or I/O error - %s", __func__, e.what()); } - + return true; } diff --git a/src/net.h b/src/net.h index a7deceb14d927..b0c3d6339a621 100644 --- a/src/net.h +++ b/src/net.h @@ -29,6 +29,7 @@ class CAddrMan; class CScheduler; +class CNetMessage; class CNode; namespace boost { @@ -43,8 +44,6 @@ static const int TIMEOUT_INTERVAL = 20 * 60; static const unsigned int MAX_INV_SZ = 50000; /** The maximum number of new addresses to accumulate before announcing. */ static const unsigned int MAX_ADDR_TO_SEND = 1000; -/** Maximum length of incoming protocol messages (no message over 2 MiB is currently acceptable). */ -static const unsigned int MAX_PROTOCOL_MESSAGE_LENGTH = 2 * 1024 * 1024; /** Maximum length of strSubVer in `version` message */ static const unsigned int MAX_SUBVERSION_LENGTH = 256; /** -listen default */ @@ -108,14 +107,19 @@ struct CombinerAll } }; -// Signals for message handling +// Signals are used to communicate with higher-level code. struct CNodeSignals { boost::signals2::signal GetHeight; + // register a handler for this signal to do sanity checks as the bytes of a message are being + // received. Note that the message may not be completely read (so this can be + // used to prevent DoS attacks using over-size messages). + boost::signals2::signal SanityCheckMessages; boost::signals2::signal ProcessMessages; boost::signals2::signal SendMessages; boost::signals2::signal InitializeNode; boost::signals2::signal FinalizeNode; + boost::signals2::signal GetMaxBlockSize; }; @@ -171,8 +175,8 @@ extern CCriticalSection cs_vAddedNodes; extern NodeId nLastNodeId; extern CCriticalSection cs_nLastNodeId; -/** Subversion as sent to the P2P network in `version` messages */ -extern std::string strSubVersion; +/** Comments in subversion sent to the P2P network in `version` messages */ +extern std::vector vUAComments; struct LocalServiceInfo { int nScore; diff --git a/src/policy/policy.h b/src/policy/policy.h index 4f9354e36fe71..f6dd0f9681dcb 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -14,8 +14,7 @@ class CCoinsViewCache; -/** Default for -blockmaxsize and -blockminsize, which control the range of sizes the mining code will create **/ -static const unsigned int DEFAULT_BLOCK_MAX_SIZE = 750000; +/** Default for -blockminsize, which controls the minimum size the mining code will create **/ static const unsigned int DEFAULT_BLOCK_MIN_SIZE = 0; /** Default for -blockprioritysize, maximum space for zero/low-fee transactions **/ static const unsigned int DEFAULT_BLOCK_PRIORITY_SIZE = 0; @@ -24,7 +23,7 @@ static const unsigned int MAX_STANDARD_TX_SIZE = 100000; /** Maximum number of signature check operations in an IsStandard() P2SH script */ static const unsigned int MAX_P2SH_SIGOPS = 15; /** The maximum number of sigops we're willing to relay/mine in a single tx */ -static const unsigned int MAX_STANDARD_TX_SIGOPS = MAX_BLOCK_SIGOPS/5; +static const unsigned int MAX_STANDARD_TX_SIGOPS = 4000; /** Default for -maxmempool, maximum megabytes of mempool memory usage */ static const unsigned int DEFAULT_MAX_MEMPOOL_SIZE = 300; /** diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index b4ac69639304b..74784e18372fa 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -183,7 +183,7 @@ QString ClientModel::formatFullVersion() const QString ClientModel::formatSubVersion() const { - return QString::fromStdString(strSubVersion); + return QString::fromStdString(FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, vUAComments, 0)); } QString ClientModel::formatBuildDate() const diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index f0bcafafe9500..3574c09803296 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -77,6 +77,8 @@ UniValue blockheaderToJSON(const CBlockIndex* blockindex) result.push_back(Pair("bits", strprintf("%08x", blockindex->nBits))); result.push_back(Pair("difficulty", GetDifficulty(blockindex))); result.push_back(Pair("chainwork", blockindex->nChainWork.GetHex())); + result.push_back(Pair("sizelimit", blockindex->nMaxBlockSize)); + result.push_back(Pair("sizelimitvote", blockindex->nMaxBlockSizeVote)); if (blockindex->pprev) result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex())); @@ -118,6 +120,8 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx result.push_back(Pair("bits", strprintf("%08x", block.nBits))); result.push_back(Pair("difficulty", GetDifficulty(blockindex))); result.push_back(Pair("chainwork", blockindex->nChainWork.GetHex())); + result.push_back(Pair("sizelimit", blockindex->nMaxBlockSize)); + result.push_back(Pair("sizelimitvote", blockindex->nMaxBlockSizeVote)); if (blockindex->pprev) result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex())); @@ -322,6 +326,9 @@ UniValue getblockheader(const UniValue& params, bool fHelp) " \"nonce\" : n, (numeric) The nonce\n" " \"bits\" : \"1d00ffff\", (string) The bits\n" " \"difficulty\" : x.xxx, (numeric) The difficulty\n" + " \"chainwork\" : \"0000...1f3\" (string) Expected number of hashes required to produce the current chain (in hex)\n" + " \"sizelimit\" : n, (numeric) The block size limit as of this block\n" + " \"sizelimitvote\" : n, (numeric) This block's size limit vote\n" " \"previousblockhash\" : \"hash\", (string) The hash of the previous block\n" " \"nextblockhash\" : \"hash\", (string) The hash of the next block\n" " \"chainwork\" : \"0000...1f3\" (string) Expected number of hashes required to produce the current chain (in hex)\n" @@ -386,6 +393,8 @@ UniValue getblock(const UniValue& params, bool fHelp) " \"bits\" : \"1d00ffff\", (string) The bits\n" " \"difficulty\" : x.xxx, (numeric) The difficulty\n" " \"chainwork\" : \"xxxx\", (string) Expected number of hashes required to produce the chain up to this block (in hex)\n" + " \"sizelimit\" : n, (numeric) The block size limit as of this block\n" + " \"sizelimitvote\" : n, (numeric) This block's sizelimit vote\n" " \"previousblockhash\" : \"hash\", (string) The hash of the previous block\n" " \"nextblockhash\" : \"hash\" (string) The hash of the next block\n" "}\n" @@ -636,6 +645,7 @@ UniValue getblockchaininfo(const UniValue& params, bool fHelp) " \"chainwork\": \"xxxx\" (string) total amount of work in active chain, in hexadecimal\n" " \"pruned\": xx, (boolean) if the blocks are subject to pruning\n" " \"pruneheight\": xxxxxx, (numeric) heighest block available\n" + " \"sizelimit\" : n, (numeric) The block size limit as of the last block\n" " \"softforks\": [ (array) status of softforks in progress\n" " {\n" " \"id\": \"xxxx\", (string) name of softfork\n" @@ -673,6 +683,7 @@ UniValue getblockchaininfo(const UniValue& params, bool fHelp) obj.push_back(Pair("verificationprogress", Checkpoints::GuessVerificationProgress(Params().Checkpoints(), chainActive.Tip()))); obj.push_back(Pair("chainwork", chainActive.Tip()->nChainWork.GetHex())); obj.push_back(Pair("pruned", fPruneMode)); + obj.push_back(Pair("sizelimit", chainActive.Tip()->nMaxBlockSize)); const Consensus::Params& consensusParams = Params().GetConsensus(); CBlockIndex* tip = chainActive.Tip(); diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index 18c3b3a511730..02f0c96d56486 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -12,6 +12,7 @@ #include "core_io.h" #include "init.h" #include "main.h" +#include "maxblocksize.h" #include "miner.h" #include "net.h" #include "pow.h" @@ -164,7 +165,8 @@ UniValue generate(const UniValue& params, bool fHelp) CBlock *pblock = &pblocktemplate->block; { LOCK(cs_main); - IncrementExtraNonce(pblock, chainActive.Tip(), nExtraNonce); + uint64_t nMaxBlockSize = GetNextMaxBlockSize(chainActive.Tip(), Params().GetConsensus()); + IncrementExtraNonce(pblock, chainActive.Tip(), nExtraNonce, nMaxBlockSize); } while (!CheckProofOfWork(pblock->GetHash(), pblock->nBits, Params().GetConsensus())) { // Yes, there is a chance every nonce could fail to satisfy the -regtest @@ -245,6 +247,7 @@ UniValue getmininginfo(const UniValue& params, bool fHelp) " \"pooledtx\": n (numeric) The size of the mem pool\n" " \"testnet\": true|false (boolean) If using testnet or not\n" " \"chain\": \"xxxx\", (string) current network name as defined in BIP70 (main, test, regtest)\n" + " \"sizelimit\" : n, (numeric) The block size limit as of the last block\n" "}\n" "\nExamples:\n" + HelpExampleCli("getmininginfo", "") @@ -265,6 +268,7 @@ UniValue getmininginfo(const UniValue& params, bool fHelp) obj.push_back(Pair("pooledtx", (uint64_t)mempool.size())); obj.push_back(Pair("testnet", Params().TestnetToBeDeprecatedFieldRPC())); obj.push_back(Pair("chain", Params().NetworkIDString())); + obj.push_back(Pair("sizelimit", chainActive.Tip()->nMaxBlockSize)); obj.push_back(Pair("generate", getgenerate(params, false))); return obj; } @@ -587,12 +591,14 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) int index_in_template = i - 1; entry.push_back(Pair("fee", pblocktemplate->vTxFees[index_in_template])); entry.push_back(Pair("sigops", pblocktemplate->vTxSigOps[index_in_template])); - transactions.push_back(entry); } + uint64_t nMaxBlockSize = GetNextMaxBlockSize(chainActive.Tip(), Params().GetConsensus()); + CScript flags = CScript() << BIP100Str(nMaxBlockSize); + flags += COINBASE_FLAGS; UniValue aux(UniValue::VOBJ); - aux.push_back(Pair("flags", HexStr(COINBASE_FLAGS.begin(), COINBASE_FLAGS.end()))); + aux.push_back(Pair("flags", HexStr(flags.begin(), flags.end()))); arith_uint256 hashTarget = arith_uint256().SetCompact(pblock->nBits); @@ -668,8 +674,8 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) result.push_back(Pair("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1)); result.push_back(Pair("mutable", aMutable)); result.push_back(Pair("noncerange", "00000000ffffffff")); - result.push_back(Pair("sigoplimit", (int64_t)MAX_BLOCK_SIGOPS)); - result.push_back(Pair("sizelimit", (int64_t)MAX_BLOCK_SIZE)); + result.push_back(Pair("sigoplimit", MaxBlockSigops(nMaxBlockSize))); + result.push_back(Pair("sizelimit", nMaxBlockSize)); 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))); diff --git a/src/rpcmisc.cpp b/src/rpcmisc.cpp index 9871c3fcc9031..1c1f9c8653ff5 100644 --- a/src/rpcmisc.cpp +++ b/src/rpcmisc.cpp @@ -62,6 +62,7 @@ UniValue getinfo(const UniValue& params, bool fHelp) " \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n" " \"paytxfee\": x.xxxx, (numeric) the transaction fee set in " + CURRENCY_UNIT + "/kB\n" " \"relayfee\": x.xxxx, (numeric) minimum relay fee for non-free transactions in " + CURRENCY_UNIT + "/kB\n" + " \"sizelimit\" : n, (numeric) The block size limit as of the last block\n" " \"errors\": \"...\" (string) any error messages\n" "}\n" "\nExamples:\n" @@ -103,6 +104,7 @@ UniValue getinfo(const UniValue& params, bool fHelp) obj.push_back(Pair("paytxfee", ValueFromAmount(payTxFee.GetFeePerK()))); #endif obj.push_back(Pair("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK()))); + obj.push_back(Pair("sizelimit", chainActive.Tip()->nMaxBlockSize)); obj.push_back(Pair("errors", GetWarnings("statusbar"))); return obj; } diff --git a/src/rpcnet.cpp b/src/rpcnet.cpp index 779e7fbc6aa25..22c13a5e2cec6 100644 --- a/src/rpcnet.cpp +++ b/src/rpcnet.cpp @@ -466,7 +466,8 @@ UniValue getnetworkinfo(const UniValue& params, bool fHelp) UniValue obj(UniValue::VOBJ); obj.push_back(Pair("version", CLIENT_VERSION)); - obj.push_back(Pair("subversion", strSubVersion)); + obj.push_back(Pair("subversion", + FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, vUAComments, 0))); obj.push_back(Pair("protocolversion",PROTOCOL_VERSION)); obj.push_back(Pair("localservices", strprintf("%016x", nLocalServices))); obj.push_back(Pair("timeoffset", GetTimeOffset())); diff --git a/src/test/ReceiveMsgBytes_tests.cpp b/src/test/ReceiveMsgBytes_tests.cpp new file mode 100644 index 0000000000000..72a2dd8552d06 --- /dev/null +++ b/src/test/ReceiveMsgBytes_tests.cpp @@ -0,0 +1,139 @@ +// Copyright (c) 2011-2014 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +// +// Unit tests for CNode::ReceiveMsgBytes +// + + +#include "main.h" +#include "net.h" +#include "pow.h" +#include "serialize.h" +#include "util.h" +#include "maxblocksize.h" + +#include "test/test_bitcoin.h" + +#include + +BOOST_FIXTURE_TEST_SUITE(ReceiveMsgBytes_tests, TestingSetup) + +BOOST_AUTO_TEST_CASE(FullMessages) +{ + CNode testNode(INVALID_SOCKET, CAddress(CService("127.0.0.1", 0), NODE_NETWORK)); + testNode.nVersion = 1; + + CDataStream s(SER_NETWORK, PROTOCOL_VERSION); + s << CMessageHeader(Params().MessageStart(), "ping", 0); + s << (uint64_t)11; // ping nonce + CNetMessage::FinalizeHeader(s); + + LOCK(testNode.cs_vRecvMsg); + + // Receive a full 'ping' message + { + BOOST_CHECK(testNode.ReceiveMsgBytes(&s[0], s.size())); + BOOST_CHECK_EQUAL(testNode.vRecvMsg.size(),1UL); + CNetMessage& msg = testNode.vRecvMsg[0]; + BOOST_CHECK(msg.complete()); + BOOST_CHECK_EQUAL(msg.hdr.GetCommand(), "ping"); + uint64_t nonce; + msg.vRecv >> nonce; + BOOST_CHECK_EQUAL(nonce, (uint64_t)11); + } + + + testNode.vRecvMsg.clear(); + + // ...receive it one byte at a time: + { + for (size_t i = 0; i < s.size(); i++) { + BOOST_CHECK(testNode.ReceiveMsgBytes(&s[i], 1)); + } + BOOST_CHECK_EQUAL(testNode.vRecvMsg.size(),1UL); + CNetMessage& msg = testNode.vRecvMsg[0]; + BOOST_CHECK(msg.complete()); + BOOST_CHECK_EQUAL(msg.hdr.GetCommand(), "ping"); + uint64_t nonce; + msg.vRecv >> nonce; + BOOST_CHECK_EQUAL(nonce, (uint64_t)11); + } +} + +BOOST_AUTO_TEST_CASE(TooLargeBlock) +{ + // Random real block (000000000000dab0130bbcc991d3d7ae6b81aa6f50a798888dfe62337458dc45) + // With one tx + CBlock block; + CDataStream stream(ParseHex("0100000079cda856b143d9db2c1caff01d1aecc8630d30625d10e8b4b8b0000000000000b50cc069d6a3e33e3ff84a5c41d9d3febe7c770fdcc96b2c3ff60abe184f196367291b4d4c86041b8fa45d630101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff08044c86041b020a02ffffffff0100f2052a01000000434104ecd3229b0571c3be876feaac0442a9f13c5a572742927af1dc623353ecf8c202225f64868137a18cdd85cbbb4c74fbccfd4f49639cf1bdc94a5672bb15ad5d4cac00000000"), SER_NETWORK, PROTOCOL_VERSION); + stream >> block; + + CNode testNode(INVALID_SOCKET, CAddress(CService("127.0.0.1", 0), NODE_NETWORK)); + testNode.nVersion = 1; + + CDataStream s(SER_NETWORK, PROTOCOL_VERSION); + s << CMessageHeader(Params().MessageStart(), "block", 0); + size_t headerLen = s.size(); + s << block; + + // Test: too large + size_t nMaxMessageSize = chainActive.Tip()->nMaxBlockSize * 105 / 100; + s.resize(nMaxMessageSize + headerLen + 1); + CNetMessage::FinalizeHeader(s); + + BOOST_CHECK(!testNode.ReceiveMsgBytes(&s[0], s.size())); + + testNode.vRecvMsg.clear(); + + // Test: exactly at max: + s.resize(nMaxMessageSize + headerLen); + CNetMessage::FinalizeHeader(s); + + BOOST_CHECK(testNode.ReceiveMsgBytes(&s[0], s.size())); +} + +BOOST_AUTO_TEST_CASE(TooLargeVerack) +{ + CNode testNode(INVALID_SOCKET, CAddress(CService("127.0.0.1", 0), NODE_NETWORK)); + testNode.nVersion = 1; + + CDataStream s(SER_NETWORK, PROTOCOL_VERSION); + s << CMessageHeader(Params().MessageStart(), "verack", 0); + size_t headerLen = s.size(); + + CNetMessage::FinalizeHeader(s); + BOOST_CHECK(testNode.ReceiveMsgBytes(&s[0], s.size())); + + // verack is zero-length, so even one byte bigger is too big: + s.resize(headerLen+1); + CNetMessage::FinalizeHeader(s); + BOOST_CHECK(testNode.ReceiveMsgBytes(&s[0], s.size())); + CNodeStateStats stats; + GetNodeStateStats(testNode.GetId(), stats); + BOOST_CHECK(stats.nMisbehavior > 0); +} + +BOOST_AUTO_TEST_CASE(TooLargePing) +{ + CNode testNode(INVALID_SOCKET, CAddress(CService("127.0.0.1", 0), NODE_NETWORK)); + testNode.nVersion = 1; + + CDataStream s(SER_NETWORK, PROTOCOL_VERSION); + s << CMessageHeader(Params().MessageStart(), "ping", 0); + s << (uint64_t)11; // 8-byte nonce + + CNetMessage::FinalizeHeader(s); + BOOST_CHECK(testNode.ReceiveMsgBytes(&s[0], s.size())); + + // Add another nonce, sanity check should fail + s << (uint64_t)11; // 8-byte nonce + CNetMessage::FinalizeHeader(s); + BOOST_CHECK(testNode.ReceiveMsgBytes(&s[0], s.size())); + CNodeStateStats stats; + GetNodeStateStats(testNode.GetId(), stats); + BOOST_CHECK(stats.nMisbehavior > 0); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/maxblocksize_tests.cpp b/src/test/maxblocksize_tests.cpp new file mode 100644 index 0000000000000..e294952d48c06 --- /dev/null +++ b/src/test/maxblocksize_tests.cpp @@ -0,0 +1,205 @@ +// Copyright (c) 2017 The Bitcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include "maxblocksize.h" +#include "chain.h" +#include "chainparams.h" +#include +#include + +BOOST_AUTO_TEST_SUITE(maxblocksize_tests); + +void fillBlockIndex( + const Consensus::Params& params, std::vector& blockIndexes, + bool addVotes, int64_t currMax) { + + int height = params.bip100ActivationHeight; + CBlockIndex* prev = NULL; + BOOST_FOREACH (CBlockIndex& index, blockIndexes) + { + index.nHeight = height++; + index.nMaxBlockSize = currMax; + + if (addVotes) + index.nMaxBlockSizeVote = std::max( + (index.nHeight - params.bip100ActivationHeight) * 1000000, 1000000); + + index.pprev = prev; + prev = &index; + } +}; + +BOOST_AUTO_TEST_CASE(get_next_max_blocksize) { + const Consensus::Params& params = Params(CBaseChainParams::MAIN).GetConsensus(); + BOOST_CHECK_EQUAL(1512, params.nMaxBlockSizeChangePosition); + + // Genesis block, legacy block size + BOOST_CHECK_EQUAL(MAX_BLOCK_SIZE, GetNextMaxBlockSize(NULL, params)); + + const int64_t interval = params.DifficultyAdjustmentInterval(); + + // Not at a difficulty adjustment interval, + // should not change max block size. + { + uint64_t currMax = 42 * 1000000; + std::vector blockInterval(interval); + fillBlockIndex(params, blockInterval, true, currMax); + CBlockIndex index; + + BOOST_FOREACH (CBlockIndex& b, blockInterval) { + if ((b.nHeight + 1) % interval == 0) + continue; + BOOST_CHECK_EQUAL(currMax, + GetNextMaxBlockSize(&b, params)); + + } + } + + // No block voted. Keep current size. + { + uint64_t currMax = 2000000; + std::vector blockInterval(interval); + fillBlockIndex(params, blockInterval, false, currMax); + + BOOST_CHECK_EQUAL(currMax, + GetNextMaxBlockSize(&blockInterval.back(), params)); + } + + // Everyone votes current size. Keep current size. + { + uint64_t currMax = 2000000; + std::vector blockInterval(interval); + fillBlockIndex(params, blockInterval, false, currMax); + + BOOST_FOREACH (CBlockIndex& b, blockInterval) + b.nMaxBlockSizeVote = currMax; + + BOOST_CHECK_EQUAL(currMax, + GetNextMaxBlockSize(&blockInterval.back(), params)); + } + + // Everyone votes. + // Blocks vote (vote# * 1MB) + { + // Test raise. + uint64_t currMax = 2000000; + std::vector blockInterval(interval); + fillBlockIndex(params, blockInterval, true, currMax); + uint64_t newLimit = GetNextMaxBlockSize(&blockInterval.back(), params); + BOOST_CHECK_EQUAL(int(currMax * 1.05), newLimit); + + // Test lower. + currMax = 1000 * 2000000; + fillBlockIndex(params, blockInterval, true, currMax); + newLimit = GetNextMaxBlockSize(&blockInterval.back(), params); + BOOST_CHECK_EQUAL(int(currMax / 1.05), newLimit); + } +} + +std::vector to_uchar(const std::string& coinbaseStr) { + return std::vector(coinbaseStr.begin(), coinbaseStr.end()); +} + +// If we have an explicit /B/ vote, we read it and ignore /EB/. +BOOST_AUTO_TEST_CASE(get_max_blocksize_vote_b) { + + std::vector vote(to_uchar("/BIP100/B2/EB1/")); + int32_t height = 600000; + + // Coinbase as in the internal miner + CScript coinbase = CScript() << height << vote << OP_0; + BOOST_CHECK_EQUAL(2000000, GetMaxBlockSizeVote(coinbase, height)); + + // Coinbase as created with IncrementExtraNonce + unsigned int nonce = 1; + CScript coinbase_flags; + coinbase = (CScript() << height << vote << CScriptNum(nonce)) + coinbase_flags; + BOOST_CHECK_EQUAL(2000000, GetMaxBlockSizeVote(coinbase, height)); + + // coinbase without height should also work + coinbase = (CScript() << vote << CScriptNum(nonce)) + coinbase_flags; + BOOST_CHECK_EQUAL(2000000, GetMaxBlockSizeVote(coinbase, height)); + + // can't vote twice, only first one counts. + coinbase = (CScript() << to_uchar("/BIP100/B4/EB6/BIP100/B8/")); + BOOST_CHECK_EQUAL(4000000, GetMaxBlockSizeVote(coinbase, height)); + + // B-votes override EB, even though EB is first. + coinbase = (CScript() << to_uchar("/EB6/BIP100/B8/")); + BOOST_CHECK_EQUAL(8000000, GetMaxBlockSizeVote(coinbase, height)); +} + +// If /B/ is not present, we count /EB/ as a vote. +BOOST_AUTO_TEST_CASE(get_max_blocksize_vote_eb) { + std::vector vote(to_uchar("/some data/EB1/")); + int32_t height = 600000; + + CScript coinbase = CScript() << height << vote << OP_0; + BOOST_CHECK_EQUAL(1000000, GetMaxBlockSizeVote(coinbase, height)); + + unsigned int nonce = 1; + CScript coinbase_flags; + coinbase = (CScript() << height << vote << CScriptNum(nonce)) + coinbase_flags; + BOOST_CHECK_EQUAL(1000000, GetMaxBlockSizeVote(coinbase, height)); + + // Example of a Bitcoin Unlimited coinbase string + coinbase = CScript() << height << to_uchar("/EB16/AD12/a miner comment"); + BOOST_CHECK_EQUAL(16000000, GetMaxBlockSizeVote(coinbase, height)); + + // can't vote twice, only first one counts. + coinbase = (CScript() << to_uchar("some data/EB6/EB8/")); + BOOST_CHECK_EQUAL(6000000, GetMaxBlockSizeVote(coinbase, height)); +} + +BOOST_AUTO_TEST_CASE(get_max_blocksize_vote_no_vote) { + int32_t height = 600000; + BOOST_CHECK_EQUAL(0, GetMaxBlockSizeVote(CScript() << height << OP_0, height)); + + // votes must begin and end with / + BOOST_CHECK_EQUAL(0, GetMaxBlockSizeVote(CScript() << height << to_uchar("/EB2"), height)); + BOOST_CHECK_EQUAL(0, GetMaxBlockSizeVote(CScript() << height << to_uchar("EB2/"), height)); + BOOST_CHECK_EQUAL(0, GetMaxBlockSizeVote(CScript() << height << to_uchar("/BIP100/B2"), height)); + BOOST_CHECK_EQUAL(0, GetMaxBlockSizeVote(CScript() << height << to_uchar("BIP100/B2/"), height)); + + // whitespace is not allowed + BOOST_CHECK_EQUAL(0, GetMaxBlockSizeVote(CScript() << height << to_uchar("/ EB2/"), height)); + BOOST_CHECK_EQUAL(0, GetMaxBlockSizeVote(CScript() << height << to_uchar("/EB2 /"), height)); + BOOST_CHECK_EQUAL(0, GetMaxBlockSizeVote(CScript() << height << to_uchar("/EB 2/"), height)); + BOOST_CHECK_EQUAL(0, GetMaxBlockSizeVote(CScript() << height << to_uchar("/BIP100/B2 /"), height)); + BOOST_CHECK_EQUAL(0, GetMaxBlockSizeVote(CScript() << height << to_uchar("/BIP100/ B2/"), height)); + BOOST_CHECK_EQUAL(0, GetMaxBlockSizeVote(CScript() << height << to_uchar("/BIP100/B 2/"), height)); + BOOST_CHECK_EQUAL(0, GetMaxBlockSizeVote(CScript() << height << to_uchar("/BIP100 /B2/"), height)); + BOOST_CHECK_EQUAL(0, GetMaxBlockSizeVote(CScript() << height << to_uchar("/ BIP100/B2/"), height)); + + // decimals not supported + BOOST_CHECK_EQUAL(0, GetMaxBlockSizeVote(CScript() << height << to_uchar("/EB2.2/"), height)); + BOOST_CHECK_EQUAL(0, GetMaxBlockSizeVote(CScript() << height << to_uchar("/BIP100/B2.2/"), height)); + + // missing mb value + BOOST_CHECK_EQUAL(0, GetMaxBlockSizeVote(CScript() << to_uchar("/BIP100/B/"), height)); + BOOST_CHECK_EQUAL(0, GetMaxBlockSizeVote(CScript() << to_uchar("/EB/"), height)); + + // missing BIP100 prefix + BOOST_CHECK_EQUAL(0, GetMaxBlockSizeVote(CScript() << to_uchar("/B2/"), height)); + BOOST_CHECK_EQUAL(0, GetMaxBlockSizeVote(CScript() << to_uchar("/BIP100/B/B8/"), height)); + + //Explicit zeros and garbage + BOOST_CHECK_EQUAL(0, GetMaxBlockSizeVote(CScript() << to_uchar("/BIP100/B0/BIP100/B2"), height)); + BOOST_CHECK_EQUAL(0, GetMaxBlockSizeVote(CScript() << to_uchar("/EB0/EB2/"), height)); + BOOST_CHECK_EQUAL(0, GetMaxBlockSizeVote(CScript() << to_uchar("/BIP100/Bgarbage/B2/"), height)); + BOOST_CHECK_EQUAL(2000000, GetMaxBlockSizeVote(CScript() << to_uchar("/EBgarbage/EB2/"), height)); + + + // Test that height is not a part of the vote string. + // Encoded height in this test ends with /. + // Should not be interpreted as /BIP100/B8/ + CScript coinbase = CScript() << 47; + BOOST_CHECK_EQUAL('/', coinbase.back()); + std::vector vote = to_uchar("BIP100/B8/"); + coinbase.insert(coinbase.end(), vote.begin(), vote.end()); // insert instead of << to avoid size being prepended + BOOST_CHECK_EQUAL(0, GetMaxBlockSizeVote(coinbase, 47)); +} + +BOOST_AUTO_TEST_SUITE_END(); diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index ab6485081ceed..9710ce5ddd9c4 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -256,6 +256,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) next->pprev = prev; next->nHeight = prev->nHeight + 1; next->BuildSkip(); + next->nMaxBlockSize = prev->nMaxBlockSize; chainActive.SetTip(next); } BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); @@ -269,6 +270,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) next->pprev = prev; next->nHeight = prev->nHeight + 1; next->BuildSkip(); + next->nMaxBlockSize = prev->nMaxBlockSize; chainActive.SetTip(next); } BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); @@ -386,4 +388,28 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) fCheckpointsEnabled = true; } +std::string DefaultCoinbaseStr() { + const CChainParams& chainparams = Params(CBaseChainParams::MAIN); + CScript scriptPubKey = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG; + std::auto_ptr tpl(CreateNewBlock(chainparams, scriptPubKey)); + CScript coinbase = tpl->block.vtx.at(0).vin.at(0).scriptSig; + return std::string(coinbase.begin(), coinbase.end()); +} + +BOOST_AUTO_TEST_CASE(CreateNewBlock_bip100str) +{ + LOCK(cs_main); + + // No vote defined. Should only contain EB. + mapArgs.erase("-maxblocksizevote"); + std::string c = DefaultCoinbaseStr(); + BOOST_CHECK(c.find("/BIP100/EB1/") != std::string::npos); + BOOST_CHECK(c.find("/B1/") == std::string::npos); + + // Vote for 16MB blocks + SoftSetArg("-maxblocksizevote", "16"); + c = DefaultCoinbaseStr(); + BOOST_CHECK(c.find("/BIP100/B16/EB1/") != std::string::npos); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/p2p_protocol_tests.cpp b/src/test/p2p_protocol_tests.cpp new file mode 100644 index 0000000000000..b43ebbd6b3ef6 --- /dev/null +++ b/src/test/p2p_protocol_tests.cpp @@ -0,0 +1,111 @@ +// Copyright (c) 2011-2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "main.h" + +#include "test/test_bitcoin.h" + +#include + +BOOST_FIXTURE_TEST_SUITE(p2p_protocol_tests, TestingSetup) + +BOOST_AUTO_TEST_CASE(MaxSizeVersionMessage) +{ + CNode n(INVALID_SOCKET, CAddress(CService("127.0.0.1", 0), NODE_NETWORK)); + CDataStream s(SER_NETWORK, PROTOCOL_VERSION); + nLocalHostNonce = 2; // this trips the version message logic to end shortly after reading the data (which is the focus of this test) + s << PROTOCOL_VERSION; + s << n.nServices; + s << GetTime(); + s << CAddress(CService("0.0.0.0", 0)); + s << CAddress(CService("0.0.0.0", 0)); + s << nLocalHostNonce; + s << std::string(256, 'a'); // 256 is the max allowed length in the Bitcoin Core/XT protocol processing code + s << n.nStartingHeight; + s << n.fRelayTxes; + BOOST_CHECK_EQUAL(352, s.size()); + BOOST_CHECK(ProcessMessage(&n, "version", s, 0)); +} + +BOOST_AUTO_TEST_CASE(OverMaxSizeVersionMessage) +{ + CNode n(INVALID_SOCKET, CAddress(CService("127.0.0.1", 0), NODE_NETWORK)); + CDataStream s(SER_NETWORK, PROTOCOL_VERSION); + nLocalHostNonce = 2; // this trips the version message logic to end shortly after reading the data (which is the focus of this test) + s << PROTOCOL_VERSION; + s << n.nServices; + s << GetTime(); + s << CAddress(CService("0.0.0.0", 0)); + s << CAddress(CService("0.0.0.0", 0)); + s << nLocalHostNonce; + s << std::string(257, 'a'); // invalid, max is 256 + s << n.nStartingHeight; + s << n.fRelayTxes; + BOOST_CHECK_EQUAL(353, s.size()); + BOOST_CHECK_THROW(ProcessMessage(&n, "version", s, 0), std::ios_base::failure); +} + +BOOST_AUTO_TEST_CASE(MaxSizeWeirdRejectMessage) +{ + CNode n(INVALID_SOCKET, CAddress(CService("127.0.0.1", 0), NODE_NETWORK)); + n.nVersion = PROTOCOL_VERSION; + CDataStream s(SER_NETWORK, PROTOCOL_VERSION); + s << std::string(12, 'a'); // not a real command, but it uses the max of 12 here. + s << (uint8_t)0x10; + s << std::string(111, 'a'); + BOOST_CHECK_EQUAL(126, s.size()); + bool temp = fDebug; + fDebug = true; + BOOST_CHECK(ProcessMessage(&n, "reject", s, 0)); + fDebug = temp; +} + +BOOST_AUTO_TEST_CASE(MaxSizeValidRejectMessage) +{ + CNode n(INVALID_SOCKET, CAddress(CService("127.0.0.1", 0), NODE_NETWORK)); + n.nVersion = PROTOCOL_VERSION; + CDataStream s(SER_NETWORK, PROTOCOL_VERSION); + s << std::string("block"); // does not use the max of 12, but "block" is the longest command that has a defined extension of 32 bytes + s << (uint8_t)0x10; + s << std::string(111, 'a'); + s << uint256(); + BOOST_CHECK_EQUAL(151, s.size()); + bool temp = fDebug; + fDebug = true; + BOOST_CHECK(ProcessMessage(&n, "reject", s, 0)); + fDebug = temp; +} + +BOOST_AUTO_TEST_CASE(OverMaxSizeWeirdRejectMessage) +{ + CNode n(INVALID_SOCKET, CAddress(CService("127.0.0.1", 0), NODE_NETWORK)); + n.nVersion = PROTOCOL_VERSION; + CDataStream s(SER_NETWORK, PROTOCOL_VERSION); + s << std::string(13, 'a'); // invalid, max is 12 + s << (uint8_t)0x10; + s << std::string(111, 'a'); + BOOST_CHECK_EQUAL(127, s.size()); + bool temp = fDebug; + fDebug = true; + BOOST_CHECK(!ProcessMessage(&n, "reject", s, 0)); // check this way since the reject message processing swallows the exception + fDebug = temp; +} + +BOOST_AUTO_TEST_CASE(OverMaxSizeValidRejectMessage) +{ + CNode n(INVALID_SOCKET, CAddress(CService("127.0.0.1", 0), NODE_NETWORK)); + n.nVersion = PROTOCOL_VERSION; + CDataStream s(SER_NETWORK, PROTOCOL_VERSION); + s << std::string("block"); + s << (uint8_t)0x10; + s << std::string(112, 'a'); // invalid, max is 111 + s << uint256(); + BOOST_CHECK_EQUAL(152, s.size()); + bool temp = fDebug; + fDebug = true; + BOOST_CHECK(!ProcessMessage(&n, "reject", s, 0)); // check this way since the reject message processing swallows the exception + fDebug = temp; +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp index 9abae69b1ee7a..8755abcb1652b 100644 --- a/src/test/rpc_tests.cpp +++ b/src/test/rpc_tests.cpp @@ -6,7 +6,10 @@ #include "rpcclient.h" #include "base58.h" +#include "main.h" +#include "net.h" #include "netbase.h" +#include "utilstrencodings.h" #include "test/test_bitcoin.h" @@ -228,7 +231,7 @@ BOOST_AUTO_TEST_CASE(json_parse_errors) BOOST_AUTO_TEST_CASE(rpc_ban) { BOOST_CHECK_NO_THROW(CallRPC(string("clearbanned"))); - + UniValue r; BOOST_CHECK_NO_THROW(r = CallRPC(string("setban 127.0.0.0 add"))); BOOST_CHECK_THROW(r = CallRPC(string("setban 127.0.0.0:8334")), runtime_error); //portnumber for setban not allowed @@ -260,7 +263,7 @@ BOOST_AUTO_TEST_CASE(rpc_ban) adr = find_value(o1, "address"); banned_until = find_value(o1, "banned_until"); BOOST_CHECK_EQUAL(adr.get_str(), "127.0.0.0/24"); - int64_t now = GetTime(); + int64_t now = GetTime(); BOOST_CHECK(banned_until.get_int64() > now); BOOST_CHECK(banned_until.get_int64()-now <= 200); @@ -308,4 +311,51 @@ BOOST_AUTO_TEST_CASE(rpc_ban) BOOST_CHECK_EQUAL(adr.get_str(), "2001:4d48:ac57:400:cacf:e9ff:fe1d:9c63/128"); } +static std::string get_coinbaseaux_flags(UniValue blocktpl) { + UniValue aux = find_value(blocktpl.get_obj(), "coinbaseaux").get_obj(); + std::string hexstr = find_value(aux.get_obj(), "flags").get_str(); + std::vector parsed = ParseHex(hexstr); + return std::string(parsed.begin(), parsed.end()); +} + +// Put us in a state where we accept rpc calls. +class RpcMineState { +public: + RpcMineState() + { + // Don't throw "Bitcoin is downloading blocks" + fForceInitialBlockDownload = true; + + // Don't throw "Bitcoin is not connected" + LOCK(cs_vNodes); + assert(vNodes.empty()); + vNodes.push_back(new CNode(INVALID_SOCKET, CAddress())); + + } + ~RpcMineState() { + fForceInitialBlockDownload = false; + + LOCK(cs_vNodes); + delete vNodes[0]; + vNodes.clear(); + } +}; + +BOOST_AUTO_TEST_CASE(rpc_getblocktemplate_vote) +{ + RpcMineState raii; + + mapArgs.erase("-maxblocksizevote"); + UniValue noVote = CallRPC("getblocktemplate"); + std::string f = get_coinbaseaux_flags(noVote); + BOOST_CHECK(f.find("/BIP100/EB1/") != std::string::npos); + BOOST_CHECK(f.find("/B1/") == std::string::npos); + + SoftSetArg("-maxblocksizevote", "16"); + UniValue hasVote = CallRPC("getblocktemplate"); + f = get_coinbaseaux_flags(hasVote); + BOOST_CHECK(f.find("/BIP100/B16/EB1/") != std::string::npos); + +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index f278d7e3999a9..b3471b1dd2326 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -126,7 +126,7 @@ TestChain100Setup::CreateAndProcessBlock(const std::vector& block.vtx.push_back(tx); // IncrementExtraNonce creates a valid coinbase and merkleRoot unsigned int extraNonce = 0; - IncrementExtraNonce(&block, chainActive.Tip(), extraNonce); + IncrementExtraNonce(&block, chainActive.Tip(), extraNonce, MAX_BLOCK_SIZE); while (!CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus())) ++block.nNonce; diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 517e6945cf9bc..4ecf97a6f0c5b 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -11,6 +11,7 @@ #include "utilstrencodings.h" #include "utilmoneystr.h" #include "test/test_bitcoin.h" +#include "consensus/consensus.h" #include #include @@ -419,9 +420,15 @@ BOOST_AUTO_TEST_CASE(test_FormatSubVersion) std::vector comments2; comments2.push_back(std::string("comment1")); comments2.push_back(SanitizeString(std::string("Comment2; .,_?@-; !\"#$%&'()*+/<=>[]\\^`{|}~"), SAFE_CHARS_UA_COMMENT)); // Semicolon is discouraged but not forbidden by BIP-0014 - BOOST_CHECK_EQUAL(FormatSubVersion("Test", 99900, std::vector()),std::string("/Test:0.9.99/")); - BOOST_CHECK_EQUAL(FormatSubVersion("Test", 99900, comments),std::string("/Test:0.9.99(comment1)/")); - BOOST_CHECK_EQUAL(FormatSubVersion("Test", 99900, comments2),std::string("/Test:0.9.99(comment1; Comment2; .,_?@-; )/")); + BOOST_CHECK_EQUAL(FormatSubVersion("Test", 99900, std::vector(), 0),std::string("/Test:0.9.99/")); + BOOST_CHECK_EQUAL(FormatSubVersion("Test", 99900, comments, 0),std::string("/Test:0.9.99(comment1)/")); + BOOST_CHECK_EQUAL(FormatSubVersion("Test", 99900, comments2, 0),std::string("/Test:0.9.99(comment1; Comment2; .,_?@-; )/")); + + // BIP100 + BOOST_CHECK_EQUAL(FormatSubVersion("Test", 99900, std::vector(), MAX_BLOCK_SIZE),std::string("/Test:0.9.99(BIP100; EB1)/")); + BOOST_CHECK_EQUAL(FormatSubVersion("Test", 99900, comments, MAX_BLOCK_SIZE),std::string("/Test:0.9.99(comment1; BIP100; EB1)/")); + BOOST_CHECK_EQUAL(FormatSubVersion("Test", 99900, comments, MAX_BLOCK_SIZE + (MAX_BLOCK_SIZE / 3)), + std::string("/Test:0.9.99(comment1; BIP100; EB1.333333)/")); } BOOST_AUTO_TEST_CASE(test_ParseFixedPoint) diff --git a/src/txdb.cpp b/src/txdb.cpp index f99e11f26e3f4..65f9d32802afe 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -202,6 +202,9 @@ bool CBlockTreeDB::LoadBlockIndexGuts() pindexNew->nNonce = diskindex.nNonce; pindexNew->nStatus = diskindex.nStatus; pindexNew->nTx = diskindex.nTx; + pindexNew->nSerialVersion = diskindex.nSerialVersion; + pindexNew->nMaxBlockSize = diskindex.nMaxBlockSize; + pindexNew->nMaxBlockSizeVote = diskindex.nMaxBlockSizeVote; if (!CheckProofOfWork(pindexNew->GetBlockHash(), pindexNew->nBits, Params().GetConsensus())) return error("LoadBlockIndex(): CheckProofOfWork failed: %s", pindexNew->ToString());