Changeset View
Changeset View
Standalone View
Standalone View
test/functional/abc-p2p-compactblocks.py
- This file was copied from test/functional/abc-p2p-fullblocktest.py.
Show All 10 Lines | |||||
(e.g. sigops limits). | (e.g. sigops limits). | ||||
""" | """ | ||||
from test_framework.test_framework import ComparisonTestFramework | from test_framework.test_framework import ComparisonTestFramework | ||||
from test_framework.util import * | from test_framework.util import * | ||||
from test_framework.comptool import TestManager, TestInstance, RejectResult | from test_framework.comptool import TestManager, TestInstance, RejectResult | ||||
from test_framework.blocktools import * | from test_framework.blocktools import * | ||||
import time | import time | ||||
from test_framework.key import CECKey | |||||
from test_framework.script import * | from test_framework.script import * | ||||
from test_framework.cdefs import (ONE_MEGABYTE, LEGACY_MAX_BLOCK_SIZE, | from test_framework.cdefs import (ONE_MEGABYTE, LEGACY_MAX_BLOCK_SIZE, | ||||
MAX_BLOCK_SIGOPS_PER_MB, MAX_TX_SIGOPS_COUNT) | MAX_BLOCK_SIGOPS_PER_MB, MAX_TX_SIGOPS_COUNT) | ||||
from collections import deque | from collections import deque | ||||
class PreviousSpendableOutput(): | class PreviousSpendableOutput(): | ||||
▲ Show 20 Lines • Show All 238 Lines • ▼ Show 20 Lines | def get_tests(self): | ||||
save_spendable_output() | save_spendable_output() | ||||
yield test | yield test | ||||
# collect spendable outputs now to avoid cluttering the code later on | # collect spendable outputs now to avoid cluttering the code later on | ||||
out = [] | out = [] | ||||
for i in range(100): | for i in range(100): | ||||
out.append(get_spendable_output()) | out.append(get_spendable_output()) | ||||
# Let's build some blocks and test them. | |||||
for i in range(16): | |||||
n = i + 1 | |||||
block(n, spend=out[i], block_size=n * ONE_MEGABYTE) | |||||
yield accepted() | |||||
# block of maximal size | |||||
block(17, spend=out[16], block_size=self.excessive_block_size) | |||||
yield accepted() | |||||
# Reject oversized blocks with bad-blk-length error | |||||
block(18, spend=out[17], block_size=self.excessive_block_size + 1) | |||||
yield rejected(RejectResult(16, b'bad-blk-length')) | |||||
# Rewind bad block. | |||||
tip(17) | |||||
# Accept many sigops | |||||
lots_of_checksigs = CScript( | |||||
[OP_CHECKSIG] * MAX_BLOCK_SIGOPS_PER_MB) | |||||
block(19, spend=out[17], script=lots_of_checksigs, | |||||
block_size=ONE_MEGABYTE) | |||||
yield accepted() | |||||
block(20, spend=out[18], script=lots_of_checksigs, | |||||
block_size=ONE_MEGABYTE, extra_sigops=1) | |||||
yield rejected(RejectResult(16, b'bad-blk-sigops')) | |||||
# Rewind bad block | |||||
tip(19) | |||||
# Accept 40k sigops per block > 1MB and <= 2MB | |||||
block(21, spend=out[18], script=lots_of_checksigs, | |||||
extra_sigops=MAX_BLOCK_SIGOPS_PER_MB, block_size=ONE_MEGABYTE + 1) | |||||
yield accepted() | |||||
# Accept 40k sigops per block > 1MB and <= 2MB | |||||
block(22, spend=out[19], script=lots_of_checksigs, | |||||
extra_sigops=MAX_BLOCK_SIGOPS_PER_MB, block_size=2 * ONE_MEGABYTE) | |||||
yield accepted() | |||||
# Reject more than 40k sigops per block > 1MB and <= 2MB. | |||||
block(23, spend=out[20], script=lots_of_checksigs, | |||||
extra_sigops=MAX_BLOCK_SIGOPS_PER_MB + 1, block_size=ONE_MEGABYTE + 1) | |||||
yield rejected(RejectResult(16, b'bad-blk-sigops')) | |||||
# Rewind bad block | |||||
tip(22) | |||||
# Reject more than 40k sigops per block > 1MB and <= 2MB. | |||||
block(24, spend=out[20], script=lots_of_checksigs, | |||||
extra_sigops=MAX_BLOCK_SIGOPS_PER_MB + 1, block_size=2 * ONE_MEGABYTE) | |||||
yield rejected(RejectResult(16, b'bad-blk-sigops')) | |||||
# Rewind bad block | |||||
tip(22) | |||||
# Accept 60k sigops per block > 2MB and <= 3MB | |||||
block(25, spend=out[20], script=lots_of_checksigs, extra_sigops=2 * | |||||
MAX_BLOCK_SIGOPS_PER_MB, block_size=2 * ONE_MEGABYTE + 1) | |||||
yield accepted() | |||||
# Accept 60k sigops per block > 2MB and <= 3MB | |||||
block(26, spend=out[21], script=lots_of_checksigs, | |||||
extra_sigops=2 * MAX_BLOCK_SIGOPS_PER_MB, block_size=3 * ONE_MEGABYTE) | |||||
yield accepted() | |||||
# Reject more than 40k sigops per block > 1MB and <= 2MB. | |||||
block(27, spend=out[22], script=lots_of_checksigs, extra_sigops=2 * | |||||
MAX_BLOCK_SIGOPS_PER_MB + 1, block_size=2 * ONE_MEGABYTE + 1) | |||||
yield rejected(RejectResult(16, b'bad-blk-sigops')) | |||||
# Rewind bad block | |||||
tip(26) | |||||
# Reject more than 40k sigops per block > 1MB and <= 2MB. | |||||
block(28, spend=out[22], script=lots_of_checksigs, extra_sigops=2 * | |||||
MAX_BLOCK_SIGOPS_PER_MB + 1, block_size=3 * ONE_MEGABYTE) | |||||
yield rejected(RejectResult(16, b'bad-blk-sigops')) | |||||
# Rewind bad block | |||||
tip(26) | |||||
# Too many sigops in one txn | |||||
too_many_tx_checksigs = CScript( | |||||
[OP_CHECKSIG] * (MAX_BLOCK_SIGOPS_PER_MB + 1)) | |||||
block( | |||||
29, spend=out[22], script=too_many_tx_checksigs, block_size=ONE_MEGABYTE + 1) | |||||
yield rejected(RejectResult(16, b'bad-txn-sigops')) | |||||
# Rewind bad block | |||||
tip(26) | |||||
# Generate a key pair to test P2SH sigops count | |||||
private_key = CECKey() | |||||
private_key.set_secretbytes(b"fatstacks") | |||||
public_key = private_key.get_pubkey() | |||||
# P2SH | |||||
# Build the redeem script, hash it, use hash to create the p2sh script | |||||
redeem_script = CScript( | |||||
[public_key] + [OP_2DUP, OP_CHECKSIGVERIFY] * 5 + [OP_CHECKSIG]) | |||||
redeem_script_hash = hash160(redeem_script) | |||||
p2sh_script = CScript([OP_HASH160, redeem_script_hash, OP_EQUAL]) | |||||
# Create a p2sh transaction | |||||
p2sh_tx = self.create_tx(out[22].tx, out[22].n, 1, p2sh_script) | |||||
# Add the transaction to the block | |||||
block(30) | |||||
update_block(30, [p2sh_tx]) | |||||
yield accepted() | |||||
# Creates a new transaction using the p2sh transaction included in the | |||||
# last block | |||||
def spend_p2sh_tx(output_script=CScript([OP_TRUE])): | |||||
# Create the transaction | |||||
spent_p2sh_tx = CTransaction() | |||||
spent_p2sh_tx.vin.append(CTxIn(COutPoint(p2sh_tx.sha256, 0), b'')) | |||||
spent_p2sh_tx.vout.append(CTxOut(1, output_script)) | |||||
# Sign the transaction using the redeem script | |||||
sighash = SignatureHashForkId( | |||||
redeem_script, spent_p2sh_tx, 0, SIGHASH_ALL | SIGHASH_FORKID, p2sh_tx.vout[0].nValue) | |||||
sig = private_key.sign(sighash) + \ | |||||
bytes(bytearray([SIGHASH_ALL | SIGHASH_FORKID])) | |||||
spent_p2sh_tx.vin[0].scriptSig = CScript([sig, redeem_script]) | |||||
spent_p2sh_tx.rehash() | |||||
return spent_p2sh_tx | |||||
# Sigops p2sh limit | |||||
p2sh_sigops_limit = MAX_BLOCK_SIGOPS_PER_MB - \ | |||||
redeem_script.GetSigOpCount(True) | |||||
# Too many sigops in one p2sh txn | |||||
too_many_p2sh_sigops = CScript([OP_CHECKSIG] * (p2sh_sigops_limit + 1)) | |||||
block(31, spend=out[23], block_size=ONE_MEGABYTE + 1) | |||||
update_block(31, [spend_p2sh_tx(too_many_p2sh_sigops)]) | |||||
yield rejected(RejectResult(16, b'bad-txn-sigops')) | |||||
# Rewind bad block | |||||
tip(30) | |||||
# Max sigops in one p2sh txn | |||||
max_p2sh_sigops = CScript([OP_CHECKSIG] * (p2sh_sigops_limit)) | |||||
block(32, spend=out[23], block_size=ONE_MEGABYTE + 1) | |||||
update_block(32, [spend_p2sh_tx(max_p2sh_sigops)]) | |||||
yield accepted() | |||||
# Check that compact block also work for big blocks | # Check that compact block also work for big blocks | ||||
node = self.nodes[0] | node = self.nodes[0] | ||||
peer = TestNode() | peer = TestNode() | ||||
peer.add_connection(NodeConn('127.0.0.1', p2p_port(0), node, peer)) | peer.add_connection(NodeConn('127.0.0.1', p2p_port(0), node, peer)) | ||||
# Start up network handling in another thread and wait for connection | # Start up network handling in another thread and wait for connection | ||||
# to be etablished | # to be etablished | ||||
NetworkThread().start() | NetworkThread().start() | ||||
Show All 21 Lines | def get_tests(self): | ||||
def received_headers(): | def received_headers(): | ||||
return (peer.last_headers != None) | return (peer.last_headers != None) | ||||
wait_until(received_headers, timeout=30) | wait_until(received_headers, timeout=30) | ||||
# It's like we know about the same headers ! | # It's like we know about the same headers ! | ||||
peer.send_message(peer.last_headers) | peer.send_message(peer.last_headers) | ||||
# Send a block | # Send a block | ||||
b33 = block(33, spend=out[24], block_size=ONE_MEGABYTE + 1) | b1 = block(1, spend=out[0], block_size=ONE_MEGABYTE + 1) | ||||
yield accepted() | yield accepted() | ||||
# Checks the node to forward it via compact block | # Checks the node to forward it via compact block | ||||
def received_block(): | def received_block(): | ||||
return (peer.last_cmpctblock != None) | return (peer.last_cmpctblock != None) | ||||
wait_until(received_block, timeout=30) | wait_until(received_block, timeout=30) | ||||
# Was it our block ? | # Was it our block ? | ||||
cmpctblk_header = peer.last_cmpctblock.header_and_shortids.header | cmpctblk_header = peer.last_cmpctblock.header_and_shortids.header | ||||
cmpctblk_header.calc_sha256() | cmpctblk_header.calc_sha256() | ||||
assert(cmpctblk_header.sha256 == b33.sha256) | assert(cmpctblk_header.sha256 == b1.sha256) | ||||
# Send a bigger block | # Send a bigger block | ||||
peer.clear_block_data() | peer.clear_block_data() | ||||
b34 = block(34, spend=out[25], block_size=8 * ONE_MEGABYTE) | b2 = block(2, spend=out[1], block_size=self.excessive_block_size) | ||||
yield accepted() | yield accepted() | ||||
# Checks the node forwards it via compact block | # Checks the node forwards it via compact block | ||||
wait_until(received_block, timeout=30) | wait_until(received_block, timeout=30) | ||||
# Was it our block ? | # Was it our block ? | ||||
cmpctblk_header = peer.last_cmpctblock.header_and_shortids.header | cmpctblk_header = peer.last_cmpctblock.header_and_shortids.header | ||||
cmpctblk_header.calc_sha256() | cmpctblk_header.calc_sha256() | ||||
assert(cmpctblk_header.sha256 == b34.sha256) | assert(cmpctblk_header.sha256 == b2.sha256) | ||||
# Let's send a compact block and see if the node accepts it. | # Let's send a compact block and see if the node accepts it. | ||||
# First, we generate the block and send all transaction to the mempool | # First, we generate the block and send all transaction to the mempool | ||||
b35 = block(35, spend=out[26], block_size=8 * ONE_MEGABYTE) | b3 = block(3, spend=out[2], block_size=8 * ONE_MEGABYTE) | ||||
for i in range(1, len(b35.vtx)): | for i in range(1, len(b3.vtx)): | ||||
node.sendrawtransaction(ToHex(b35.vtx[i]), True) | node.sendrawtransaction(ToHex(b3.vtx[i]), True) | ||||
# Now we create the compact block and send it | # Now we create the compact block and send it | ||||
comp_block = HeaderAndShortIDs() | comp_block = HeaderAndShortIDs() | ||||
comp_block.initialize_from_block(b35) | comp_block.initialize_from_block(b3) | ||||
peer.send_and_ping(msg_cmpctblock(comp_block.to_p2p())) | peer.send_and_ping(msg_cmpctblock(comp_block.to_p2p())) | ||||
# Check that compact block is received properly | # Check that compact block is received properly | ||||
assert(int(node.getbestblockhash(), 16) == b35.sha256) | assert(int(node.getbestblockhash(), 16) == b3.sha256) | ||||
if __name__ == '__main__': | if __name__ == '__main__': | ||||
FullBlockTest().main() | FullBlockTest().main() |