Skip to content

Commit b620b2d

Browse files
author
MarcoFalke
committed
Merge bitcoin#22378: test: remove confusing MAX_BLOCK_BASE_SIZE
607076d test: remove confusing `MAX_BLOCK_BASE_SIZE` (Sebastian Falbesoner) 4af97c7 test: introduce `get_weight()` helper for CBlock (Sebastian Falbesoner) a084ebe test: introduce `get_weight()` helper for CTransaction (Sebastian Falbesoner) Pull request description: This is a very late follow-up PR to bitcoin#10618, which removed the constant `MAX_BLOCK_BASE_SIZE` from the core implementation about four years ago (see also bitcoin#10608 in why it was considered confusing and superfluous). Since there is also no point in still keeping it in the functional test framework, the PR switches to weight-based accounting on the relevant test code parts and use `MAX_BLOCK_WEIGHT` instead for the block limit checks. To prepare that, the first two commits introduce `get_weight()` helpers for the classes CTransaction and CBlock, respectively. ACKs for top commit: MarcoFalke: review ACK 607076d 🚴 Tree-SHA512: d59aa0b6b3dfd0a849b8063e66de275d252f705f99e25cd3bf6daec028b47d946777ee5b42a060f5283cb18e917ac073119c2c0e11bbc21211f69ef0a6ed335a
2 parents dcd1169 + 607076d commit b620b2d

File tree

6 files changed

+57
-62
lines changed

6 files changed

+57
-62
lines changed

test/functional/feature_block.py

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
CTransaction,
2323
CTxIn,
2424
CTxOut,
25-
MAX_BLOCK_BASE_SIZE,
25+
MAX_BLOCK_WEIGHT,
2626
uint256_from_compact,
2727
uint256_from_str,
2828
)
@@ -307,33 +307,33 @@ def run_test(self):
307307
b22 = self.next_block(22, spend=out[5])
308308
self.send_blocks([b22], success=False, reject_reason='bad-txns-premature-spend-of-coinbase', reconnect=True)
309309

310-
# Create a block on either side of MAX_BLOCK_BASE_SIZE and make sure its accepted/rejected
310+
# Create a block on either side of MAX_BLOCK_WEIGHT and make sure its accepted/rejected
311311
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
312312
# \-> b12 (3) -> b13 (4) -> b15 (5) -> b23 (6)
313313
# \-> b24 (6) -> b25 (7)
314314
# \-> b3 (1) -> b4 (2)
315-
self.log.info("Accept a block of size MAX_BLOCK_BASE_SIZE")
315+
self.log.info("Accept a block of weight MAX_BLOCK_WEIGHT")
316316
self.move_tip(15)
317317
b23 = self.next_block(23, spend=out[6])
318318
tx = CTransaction()
319-
script_length = MAX_BLOCK_BASE_SIZE - len(b23.serialize()) - 69
319+
script_length = (MAX_BLOCK_WEIGHT - b23.get_weight() - 276) // 4
320320
script_output = CScript([b'\x00' * script_length])
321321
tx.vout.append(CTxOut(0, script_output))
322322
tx.vin.append(CTxIn(COutPoint(b23.vtx[1].sha256, 0)))
323323
b23 = self.update_block(23, [tx])
324-
# Make sure the math above worked out to produce a max-sized block
325-
assert_equal(len(b23.serialize()), MAX_BLOCK_BASE_SIZE)
324+
# Make sure the math above worked out to produce a max-weighted block
325+
assert_equal(b23.get_weight(), MAX_BLOCK_WEIGHT)
326326
self.send_blocks([b23], True)
327327
self.save_spendable_output()
328328

329-
self.log.info("Reject a block of size MAX_BLOCK_BASE_SIZE + 1")
329+
self.log.info("Reject a block of weight MAX_BLOCK_WEIGHT + 4")
330330
self.move_tip(15)
331331
b24 = self.next_block(24, spend=out[6])
332-
script_length = MAX_BLOCK_BASE_SIZE - len(b24.serialize()) - 69
332+
script_length = (MAX_BLOCK_WEIGHT - b24.get_weight() - 276) // 4
333333
script_output = CScript([b'\x00' * (script_length + 1)])
334334
tx.vout = [CTxOut(0, script_output)]
335335
b24 = self.update_block(24, [tx])
336-
assert_equal(len(b24.serialize()), MAX_BLOCK_BASE_SIZE + 1)
336+
assert_equal(b24.get_weight(), MAX_BLOCK_WEIGHT + 1 * 4)
337337
self.send_blocks([b24], success=False, reject_reason='bad-blk-length', reconnect=True)
338338

339339
b25 = self.next_block(25, spend=out[7])
@@ -484,13 +484,13 @@ def run_test(self):
484484
# Until block is full, add tx's with 1 satoshi to p2sh_script, the rest to OP_TRUE
485485
tx_new = None
486486
tx_last = tx
487-
total_size = len(b39.serialize())
488-
while(total_size < MAX_BLOCK_BASE_SIZE):
487+
total_weight = b39.get_weight()
488+
while total_weight < MAX_BLOCK_WEIGHT:
489489
tx_new = self.create_tx(tx_last, 1, 1, p2sh_script)
490490
tx_new.vout.append(CTxOut(tx_last.vout[1].nValue - 1, CScript([OP_TRUE])))
491491
tx_new.rehash()
492-
total_size += len(tx_new.serialize())
493-
if total_size >= MAX_BLOCK_BASE_SIZE:
492+
total_weight += tx_new.get_weight()
493+
if total_weight >= MAX_BLOCK_WEIGHT:
494494
break
495495
b39.vtx.append(tx_new) # add tx to block
496496
tx_last = tx_new
@@ -501,7 +501,7 @@ def run_test(self):
501501
# Make sure we didn't accidentally make too big a block. Note that the
502502
# size of the block has non-determinism due to the ECDSA signature in
503503
# the first transaction.
504-
while (len(b39.serialize()) >= MAX_BLOCK_BASE_SIZE):
504+
while b39.get_weight() >= MAX_BLOCK_WEIGHT:
505505
del b39.vtx[-1]
506506

507507
b39 = self.update_block(39, [])
@@ -891,7 +891,7 @@ def run_test(self):
891891
self.send_blocks([b63], success=False, reject_reason='bad-txns-nonfinal', reconnect=True)
892892

893893
# This checks that a block with a bloated VARINT between the block_header and the array of tx such that
894-
# the block is > MAX_BLOCK_BASE_SIZE with the bloated varint, but <= MAX_BLOCK_BASE_SIZE without the bloated varint,
894+
# the block is > MAX_BLOCK_WEIGHT with the bloated varint, but <= MAX_BLOCK_WEIGHT without the bloated varint,
895895
# does not cause a subsequent, identical block with canonical encoding to be rejected. The test does not
896896
# care whether the bloated block is accepted or rejected; it only cares that the second block is accepted.
897897
#
@@ -916,12 +916,12 @@ def run_test(self):
916916
tx = CTransaction()
917917

918918
# use canonical serialization to calculate size
919-
script_length = MAX_BLOCK_BASE_SIZE - len(b64a.normal_serialize()) - 69
919+
script_length = (MAX_BLOCK_WEIGHT - 4 * len(b64a.normal_serialize()) - 276) // 4
920920
script_output = CScript([b'\x00' * script_length])
921921
tx.vout.append(CTxOut(0, script_output))
922922
tx.vin.append(CTxIn(COutPoint(b64a.vtx[1].sha256, 0)))
923923
b64a = self.update_block("64a", [tx])
924-
assert_equal(len(b64a.serialize()), MAX_BLOCK_BASE_SIZE + 8)
924+
assert_equal(b64a.get_weight(), MAX_BLOCK_WEIGHT + 8 * 4)
925925
self.send_blocks([b64a], success=False, reject_reason='non-canonical ReadCompactSize()')
926926

927927
# bitcoind doesn't disconnect us for sending a bloated block, but if we subsequently
@@ -935,7 +935,7 @@ def run_test(self):
935935
b64 = CBlock(b64a)
936936
b64.vtx = copy.deepcopy(b64a.vtx)
937937
assert_equal(b64.hash, b64a.hash)
938-
assert_equal(len(b64.serialize()), MAX_BLOCK_BASE_SIZE)
938+
assert_equal(b64.get_weight(), MAX_BLOCK_WEIGHT)
939939
self.blocks[64] = b64
940940
b64 = self.update_block(64, [])
941941
self.send_blocks([b64], True)
@@ -1269,12 +1269,12 @@ def run_test(self):
12691269
for i in range(89, LARGE_REORG_SIZE + 89):
12701270
b = self.next_block(i, spend)
12711271
tx = CTransaction()
1272-
script_length = MAX_BLOCK_BASE_SIZE - len(b.serialize()) - 69
1272+
script_length = (MAX_BLOCK_WEIGHT - b.get_weight() - 276) // 4
12731273
script_output = CScript([b'\x00' * script_length])
12741274
tx.vout.append(CTxOut(0, script_output))
12751275
tx.vin.append(CTxIn(COutPoint(b.vtx[1].sha256, 0)))
12761276
b = self.update_block(i, [tx])
1277-
assert_equal(len(b.serialize()), MAX_BLOCK_BASE_SIZE)
1277+
assert_equal(b.get_weight(), MAX_BLOCK_WEIGHT)
12781278
blocks.append(b)
12791279
self.save_spendable_output()
12801280
spend = self.get_spendable_output()

test/functional/feature_segwit.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -259,8 +259,8 @@ def run_test(self):
259259
assert_equal(int(self.nodes[0].getmempoolentry(txid1)["wtxid"], 16), tx1.calc_sha256(True))
260260

261261
# Check that weight and vsize are properly reported in mempool entry (txid1)
262-
assert_equal(self.nodes[0].getmempoolentry(txid1)["vsize"], (self.nodes[0].getmempoolentry(txid1)["weight"] + 3) // 4)
263-
assert_equal(self.nodes[0].getmempoolentry(txid1)["weight"], len(tx1.serialize_without_witness())*3 + len(tx1.serialize_with_witness()))
262+
assert_equal(self.nodes[0].getmempoolentry(txid1)["vsize"], tx1.get_vsize())
263+
assert_equal(self.nodes[0].getmempoolentry(txid1)["weight"], tx1.get_weight())
264264

265265
# Now create tx2, which will spend from txid1.
266266
tx = CTransaction()
@@ -275,8 +275,8 @@ def run_test(self):
275275
assert_equal(int(self.nodes[0].getmempoolentry(txid2)["wtxid"], 16), tx.calc_sha256(True))
276276

277277
# Check that weight and vsize are properly reported in mempool entry (txid2)
278-
assert_equal(self.nodes[0].getmempoolentry(txid2)["vsize"], (self.nodes[0].getmempoolentry(txid2)["weight"] + 3) // 4)
279-
assert_equal(self.nodes[0].getmempoolentry(txid2)["weight"], len(tx.serialize_without_witness())*3 + len(tx.serialize_with_witness()))
278+
assert_equal(self.nodes[0].getmempoolentry(txid2)["vsize"], tx.get_vsize())
279+
assert_equal(self.nodes[0].getmempoolentry(txid2)["weight"], tx.get_weight())
280280

281281
# Now create tx3, which will spend from txid2
282282
tx = CTransaction()
@@ -298,8 +298,8 @@ def run_test(self):
298298
assert_equal(int(self.nodes[0].getmempoolentry(txid3)["wtxid"], 16), tx.calc_sha256(True))
299299

300300
# Check that weight and vsize are properly reported in mempool entry (txid3)
301-
assert_equal(self.nodes[0].getmempoolentry(txid3)["vsize"], (self.nodes[0].getmempoolentry(txid3)["weight"] + 3) // 4)
302-
assert_equal(self.nodes[0].getmempoolentry(txid3)["weight"], len(tx.serialize_without_witness())*3 + len(tx.serialize_with_witness()))
301+
assert_equal(self.nodes[0].getmempoolentry(txid3)["vsize"], tx.get_vsize())
302+
assert_equal(self.nodes[0].getmempoolentry(txid3)["weight"], tx.get_weight())
303303

304304
# Mine a block to clear the gbt cache again.
305305
self.nodes[0].generate(1)

test/functional/mempool_accept.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
COutPoint,
1616
CTxIn,
1717
CTxOut,
18-
MAX_BLOCK_BASE_SIZE,
18+
MAX_BLOCK_WEIGHT,
1919
MAX_MONEY,
2020
tx_from_hex,
2121
)
@@ -209,7 +209,7 @@ def run_test(self):
209209

210210
self.log.info('A really large transaction')
211211
tx = tx_from_hex(raw_tx_reference)
212-
tx.vin = [tx.vin[0]] * math.ceil(MAX_BLOCK_BASE_SIZE / len(tx.vin[0].serialize()))
212+
tx.vin = [tx.vin[0]] * math.ceil(MAX_BLOCK_WEIGHT // 4 / len(tx.vin[0].serialize()))
213213
self.check_mempool_result(
214214
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'bad-txns-oversize'}],
215215
rawtxs=[tx.serialize().hex()],

test/functional/mining_prioritisetransaction.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import time
88

9-
from test_framework.messages import COIN, MAX_BLOCK_BASE_SIZE
9+
from test_framework.messages import COIN, MAX_BLOCK_WEIGHT
1010
from test_framework.test_framework import BitcoinTestFramework
1111
from test_framework.util import assert_equal, assert_raises_rpc_error, create_confirmed_utxos, create_lots_of_big_transactions, gen_return_txouts
1212

@@ -61,15 +61,15 @@ def run_test(self):
6161
txids[i] = create_lots_of_big_transactions(self.nodes[0], self.txouts, utxos[start_range:end_range], end_range - start_range, (i+1)*base_fee)
6262

6363
# Make sure that the size of each group of transactions exceeds
64-
# MAX_BLOCK_BASE_SIZE -- otherwise the test needs to be revised to create
65-
# more transactions.
64+
# MAX_BLOCK_WEIGHT // 4 -- otherwise the test needs to be revised to
65+
# create more transactions.
6666
mempool = self.nodes[0].getrawmempool(True)
6767
sizes = [0, 0, 0]
6868
for i in range(3):
6969
for j in txids[i]:
7070
assert j in mempool
7171
sizes[i] += mempool[j]['vsize']
72-
assert sizes[i] > MAX_BLOCK_BASE_SIZE # Fail => raise utxo_count
72+
assert sizes[i] > MAX_BLOCK_WEIGHT // 4 # Fail => raise utxo_count
7373

7474
# add a fee delta to something in the cheapest bucket and make sure it gets mined
7575
# also check that a different entry in the cheapest bucket is NOT mined

test/functional/p2p_segwit.py

Lines changed: 11 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
55
"""Test segwit transactions and blocks on P2P network."""
66
from decimal import Decimal
7-
import math
87
import random
98
import struct
109
import time
@@ -21,7 +20,7 @@
2120
CTxInWitness,
2221
CTxOut,
2322
CTxWitness,
24-
MAX_BLOCK_BASE_SIZE,
23+
MAX_BLOCK_WEIGHT,
2524
MSG_BLOCK,
2625
MSG_TX,
2726
MSG_WITNESS_FLAG,
@@ -106,16 +105,6 @@ def sign_p2pk_witness_input(script, tx_to, in_idx, hashtype, value, key):
106105
tx_to.wit.vtxinwit[in_idx].scriptWitness.stack = [signature, script]
107106
tx_to.rehash()
108107

109-
def get_virtual_size(witness_block):
110-
"""Calculate the virtual size of a witness block.
111-
112-
Virtual size is base + witness/4."""
113-
base_size = len(witness_block.serialize(with_witness=False))
114-
total_size = len(witness_block.serialize())
115-
# the "+3" is so we round up
116-
vsize = int((3 * base_size + total_size + 3) / 4)
117-
return vsize
118-
119108
def test_transaction_acceptance(node, p2p, tx, with_witness, accepted, reason=None):
120109
"""Send a transaction to the node and check that it's accepted to the mempool
121110
@@ -437,8 +426,7 @@ def test_block_relay(self):
437426
rpc_details = self.nodes[0].getblock(block.hash, True)
438427
assert_equal(rpc_details["size"], len(block.serialize()))
439428
assert_equal(rpc_details["strippedsize"], len(block.serialize(False)))
440-
weight = 3 * len(block.serialize(False)) + len(block.serialize())
441-
assert_equal(rpc_details["weight"], weight)
429+
assert_equal(rpc_details["weight"], block.get_weight())
442430

443431
# Upgraded node should not ask for blocks from unupgraded
444432
block4 = self.build_next_block(version=4)
@@ -849,7 +837,7 @@ def test_block_malleability(self):
849837
block.solve()
850838

851839
block.vtx[0].wit.vtxinwit[0].scriptWitness.stack.append(b'a' * 5000000)
852-
assert get_virtual_size(block) > MAX_BLOCK_BASE_SIZE
840+
assert block.get_weight() > MAX_BLOCK_WEIGHT
853841

854842
# We can't send over the p2p network, because this is too big to relay
855843
# TODO: repeat this test with a block that can be relayed
@@ -858,7 +846,7 @@ def test_block_malleability(self):
858846
assert self.nodes[0].getbestblockhash() != block.hash
859847

860848
block.vtx[0].wit.vtxinwit[0].scriptWitness.stack.pop()
861-
assert get_virtual_size(block) < MAX_BLOCK_BASE_SIZE
849+
assert block.get_weight() < MAX_BLOCK_WEIGHT
862850
assert_equal(None, self.nodes[0].submitblock(block.serialize().hex()))
863851

864852
assert self.nodes[0].getbestblockhash() == block.hash
@@ -920,11 +908,10 @@ def test_witness_block_size(self):
920908
child_tx.rehash()
921909
self.update_witness_block_with_transactions(block, [parent_tx, child_tx])
922910

923-
vsize = get_virtual_size(block)
924-
additional_bytes = (MAX_BLOCK_BASE_SIZE - vsize) * 4
911+
additional_bytes = MAX_BLOCK_WEIGHT - block.get_weight()
925912
i = 0
926913
while additional_bytes > 0:
927-
# Add some more bytes to each input until we hit MAX_BLOCK_BASE_SIZE+1
914+
# Add some more bytes to each input until we hit MAX_BLOCK_WEIGHT+1
928915
extra_bytes = min(additional_bytes + 1, 55)
929916
block.vtx[-1].wit.vtxinwit[int(i / (2 * NUM_DROPS))].scriptWitness.stack[i % (2 * NUM_DROPS)] = b'a' * (195 + extra_bytes)
930917
additional_bytes -= extra_bytes
@@ -933,8 +920,7 @@ def test_witness_block_size(self):
933920
block.vtx[0].vout.pop() # Remove old commitment
934921
add_witness_commitment(block)
935922
block.solve()
936-
vsize = get_virtual_size(block)
937-
assert_equal(vsize, MAX_BLOCK_BASE_SIZE + 1)
923+
assert_equal(block.get_weight(), MAX_BLOCK_WEIGHT + 1)
938924
# Make sure that our test case would exceed the old max-network-message
939925
# limit
940926
assert len(block.serialize()) > 2 * 1024 * 1024
@@ -947,7 +933,7 @@ def test_witness_block_size(self):
947933
block.vtx[0].vout.pop()
948934
add_witness_commitment(block)
949935
block.solve()
950-
assert get_virtual_size(block) == MAX_BLOCK_BASE_SIZE
936+
assert block.get_weight() == MAX_BLOCK_WEIGHT
951937

952938
test_witness_block(self.nodes[0], self.test_node, block, accepted=True)
953939

@@ -1304,10 +1290,9 @@ def test_tx_relay_after_segwit_activation(self):
13041290
raw_tx = self.nodes[0].getrawtransaction(tx3.hash, 1)
13051291
assert_equal(int(raw_tx["hash"], 16), tx3.calc_sha256(True))
13061292
assert_equal(raw_tx["size"], len(tx3.serialize_with_witness()))
1307-
weight = len(tx3.serialize_with_witness()) + 3 * len(tx3.serialize_without_witness())
1308-
vsize = math.ceil(weight / 4)
1293+
vsize = tx3.get_vsize()
13091294
assert_equal(raw_tx["vsize"], vsize)
1310-
assert_equal(raw_tx["weight"], weight)
1295+
assert_equal(raw_tx["weight"], tx3.get_weight())
13111296
assert_equal(len(raw_tx["vin"][0]["txinwitness"]), 1)
13121297
assert_equal(raw_tx["vin"][0]["txinwitness"][0], witness_script.hex())
13131298
assert vsize != raw_tx["size"]
@@ -1663,7 +1648,7 @@ def test_signature_version_1(self):
16631648
block.vtx.append(tx)
16641649

16651650
# Test the block periodically, if we're close to maxblocksize
1666-
if (get_virtual_size(block) > MAX_BLOCK_BASE_SIZE - 1000):
1651+
if block.get_weight() > MAX_BLOCK_WEIGHT - 4000:
16671652
self.update_witness_block_with_transactions(block, [])
16681653
test_witness_block(self.nodes[0], self.test_node, block, accepted=True)
16691654
block = self.build_next_block()

test/functional/test_framework/messages.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
from test_framework.util import assert_equal
3434

3535
MAX_LOCATOR_SZ = 101
36-
MAX_BLOCK_BASE_SIZE = 1000000
36+
MAX_BLOCK_WEIGHT = 4000000
3737
MAX_BLOOM_FILTER_SIZE = 36000
3838
MAX_BLOOM_HASH_FUNCS = 50
3939

@@ -608,12 +608,15 @@ def is_valid(self):
608608
return False
609609
return True
610610

611-
# Calculate the virtual transaction size using witness and non-witness
611+
# Calculate the transaction weight using witness and non-witness
612612
# serialization size (does NOT use sigops).
613-
def get_vsize(self):
613+
def get_weight(self):
614614
with_witness_size = len(self.serialize_with_witness())
615615
without_witness_size = len(self.serialize_without_witness())
616-
return math.ceil(((WITNESS_SCALE_FACTOR - 1) * without_witness_size + with_witness_size) / WITNESS_SCALE_FACTOR)
616+
return (WITNESS_SCALE_FACTOR - 1) * without_witness_size + with_witness_size
617+
618+
def get_vsize(self):
619+
return math.ceil(self.get_weight() / WITNESS_SCALE_FACTOR)
617620

618621
def __repr__(self):
619622
return "CTransaction(nVersion=%i vin=%s vout=%s wit=%s nLockTime=%i)" \
@@ -761,6 +764,13 @@ def solve(self):
761764
self.nNonce += 1
762765
self.rehash()
763766

767+
# Calculate the block weight using witness and non-witness
768+
# serialization size (does NOT use sigops).
769+
def get_weight(self):
770+
with_witness_size = len(self.serialize(with_witness=True))
771+
without_witness_size = len(self.serialize(with_witness=False))
772+
return (WITNESS_SCALE_FACTOR - 1) * without_witness_size + with_witness_size
773+
764774
def __repr__(self):
765775
return "CBlock(nVersion=%i hashPrevBlock=%064x hashMerkleRoot=%064x nTime=%s nBits=%08x nNonce=%08x vtx=%s)" \
766776
% (self.nVersion, self.hashPrevBlock, self.hashMerkleRoot,

0 commit comments

Comments
 (0)