Skip to content

Commit e5753fa

Browse files
author
MarcoFalke
committed
Merge #17921: test: test OP_CSV empty stack fail in feature_csv_activation.py
5ffaf88 test: eliminiated magic numbers in feature_csv_activation.py (Sebastian Falbesoner) 09f706a test: check for OP_CSV empty stack fail reject reason in feature_csv_activation.py (Sebastian Falbesoner) cbd345a test: test OP_CSV empty stack fail in feature_csv_activation.py (Sebastian Falbesoner) Pull request description: Adds an empty stack failure check for OP_CSV (BIP112) to the functional test `feature_csv_activation.py` by prepending a valid scriptSig with `OP_CHECKSEQUENCEVERIFY`. If BIP112 is inactive, the operator just behaves as a NOP (for both tx versions 1 and 2) and the transaction remains valid -- if it is active, the tx is invalid due to an empty stack (for both tx versions 1 and 2, as well). Top commit has no ACKs. Tree-SHA512: 81102aaead5be11e02b894867fa9a9cc17358ec0eb2f21ce2d3db845b87691d305e6ed7c525f9c7e5bcb3c5c609eb28deca0fbaa3d5e9ff928cecd3b91ff129a
2 parents eae48ec + 5ffaf88 commit e5753fa

File tree

1 file changed

+33
-12
lines changed

1 file changed

+33
-12
lines changed

test/functional/feature_csv_activation.py

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
bip112txs_vary_OP_CSV - 16 txs with nSequence = 10 evaluated against varying {relative_locktimes of 10} OP_CSV OP_DROP
3636
bip112txs_vary_OP_CSV_9 - 16 txs with nSequence = 9 evaluated against varying {relative_locktimes of 10} OP_CSV OP_DROP
3737
bip112tx_special - test negative argument to OP_CSV
38+
bip112tx_emptystack - test empty stack (= no argument) OP_CSV
3839
"""
3940
from decimal import Decimal
4041
from itertools import product
@@ -56,6 +57,8 @@
5657
softfork_active,
5758
)
5859

60+
TESTING_TX_COUNT = 83 # Number of testing transactions: 1 BIP113 tx, 16 BIP68 txs, 66 BIP112 txs (see comments above)
61+
COINBASE_BLOCK_COUNT = TESTING_TX_COUNT # Number of coinbase blocks we need to generate as inputs for our txs
5962
BASE_RELATIVE_LOCKTIME = 10
6063
CSV_ACTIVATION_HEIGHT = 432
6164
SEQ_DISABLE_FLAG = 1 << 31
@@ -95,6 +98,13 @@ def create_bip112special(node, input, txversion, address):
9598
signtx.vin[0].scriptSig = CScript([-1, OP_CHECKSEQUENCEVERIFY, OP_DROP] + list(CScript(signtx.vin[0].scriptSig)))
9699
return signtx
97100

101+
def create_bip112emptystack(node, input, txversion, address):
102+
tx = create_transaction(node, input, address, amount=Decimal("49.98"))
103+
tx.nVersion = txversion
104+
signtx = sign_transaction(node, tx)
105+
signtx.vin[0].scriptSig = CScript([OP_CHECKSEQUENCEVERIFY] + list(CScript(signtx.vin[0].scriptSig)))
106+
return signtx
107+
98108
def send_generic_input_tx(node, coinbases, address):
99109
return node.sendrawtransaction(ToHex(sign_transaction(node, create_transaction(node, node.getblock(coinbases.pop())['tx'][0], address, amount=Decimal("49.99")))))
100110

@@ -142,6 +152,7 @@ def set_test_params(self):
142152
143153
'-blockversion=4',
144154
'-addresstype=legacy',
155+
'-par=1', # Use only one script thread to get the exact reject reason for testing
145156
]]
146157
self.supports_cli = False
147158

@@ -167,27 +178,28 @@ def create_test_block(self, txs):
167178
block.solve()
168179
return block
169180

170-
def send_blocks(self, blocks, success=True):
181+
def send_blocks(self, blocks, success=True, reject_reason=None):
171182
"""Sends blocks to test node. Syncs and verifies that tip has advanced to most recent block.
172183
173184
Call with success = False if the tip shouldn't advance to the most recent block."""
174-
self.nodes[0].p2p.send_blocks_and_test(blocks, self.nodes[0], success=success)
185+
self.nodes[0].p2p.send_blocks_and_test(blocks, self.nodes[0], success=success, reject_reason=reject_reason)
175186

176187
def run_test(self):
177188
self.nodes[0].add_p2p_connection(P2PDataStore())
178189

179190
self.log.info("Generate blocks in the past for coinbase outputs.")
180191
long_past_time = int(time.time()) - 600 * 1000 # enough to build up to 1000 blocks 10 minutes apart without worrying about getting into the future
181192
self.nodes[0].setmocktime(long_past_time - 100) # enough so that the generated blocks will still all be before long_past_time
182-
self.coinbase_blocks = self.nodes[0].generate(1 + 16 + 2 * 32 + 1) # 82 blocks generated for inputs
193+
self.coinbase_blocks = self.nodes[0].generate(COINBASE_BLOCK_COUNT) # blocks generated for inputs
183194
self.nodes[0].setmocktime(0) # set time back to present so yielded blocks aren't in the future as we advance last_block_time
184-
self.tipheight = 82 # height of the next block to build
195+
self.tipheight = COINBASE_BLOCK_COUNT # height of the next block to build
185196
self.last_block_time = long_past_time
186197
self.tip = int(self.nodes[0].getbestblockhash(), 16)
187198
self.nodeaddress = self.nodes[0].getnewaddress()
188199

189200
# Activation height is hardcoded
190-
test_blocks = self.generate_blocks(345)
201+
# We advance to block height five below BIP112 activation for the following tests
202+
test_blocks = self.generate_blocks(CSV_ACTIVATION_HEIGHT-5 - COINBASE_BLOCK_COUNT)
191203
self.send_blocks(test_blocks)
192204
assert not softfork_active(self.nodes[0], 'csv')
193205

@@ -218,6 +230,8 @@ def run_test(self):
218230

219231
# 1 special input with -1 OP_CSV OP_DROP (actually will be prepended to spending scriptSig)
220232
bip112specialinput = send_generic_input_tx(self.nodes[0], self.coinbase_blocks, self.nodeaddress)
233+
# 1 special input with (empty stack) OP_CSV (actually will be prepended to spending scriptSig)
234+
bip112emptystackinput = send_generic_input_tx(self.nodes[0],self.coinbase_blocks, self.nodeaddress)
221235

222236
# 1 normal input
223237
bip113input = send_generic_input_tx(self.nodes[0], self.coinbase_blocks, self.nodeaddress)
@@ -228,7 +242,7 @@ def run_test(self):
228242
self.tip = int(inputblockhash, 16)
229243
self.tipheight += 1
230244
self.last_block_time += 600
231-
assert_equal(len(self.nodes[0].getblock(inputblockhash, True)["tx"]), 82 + 1)
245+
assert_equal(len(self.nodes[0].getblock(inputblockhash, True)["tx"]), TESTING_TX_COUNT + 1)
232246

233247
# 2 more version 4 blocks
234248
test_blocks = self.generate_blocks(2)
@@ -267,18 +281,22 @@ def run_test(self):
267281
# -1 OP_CSV OP_DROP input
268282
bip112tx_special_v1 = create_bip112special(self.nodes[0], bip112specialinput, 1, self.nodeaddress)
269283
bip112tx_special_v2 = create_bip112special(self.nodes[0], bip112specialinput, 2, self.nodeaddress)
284+
# (empty stack) OP_CSV input
285+
bip112tx_emptystack_v1 = create_bip112emptystack(self.nodes[0], bip112emptystackinput, 1, self.nodeaddress)
286+
bip112tx_emptystack_v2 = create_bip112emptystack(self.nodes[0], bip112emptystackinput, 2, self.nodeaddress)
270287

271288
self.log.info("TESTING")
272289

273290
self.log.info("Pre-Soft Fork Tests. All txs should pass.")
274291
self.log.info("Test version 1 txs")
275292

276293
success_txs = []
277-
# add BIP113 tx and -1 CSV tx
294+
# BIP113 tx, -1 CSV tx and empty stack CSV tx should succeed
278295
bip113tx_v1.nLockTime = self.last_block_time - 600 * 5 # = MTP of prior block (not <) but < time put on current block
279296
bip113signed1 = sign_transaction(self.nodes[0], bip113tx_v1)
280297
success_txs.append(bip113signed1)
281298
success_txs.append(bip112tx_special_v1)
299+
success_txs.append(bip112tx_emptystack_v1)
282300
# add BIP 68 txs
283301
success_txs.extend(all_rlt_txs(bip68txs_v1))
284302
# add BIP 112 with seq=10 txs
@@ -293,11 +311,12 @@ def run_test(self):
293311
self.log.info("Test version 2 txs")
294312

295313
success_txs = []
296-
# add BIP113 tx and -1 CSV tx
314+
# BIP113 tx, -1 CSV tx and empty stack CSV tx should succeed
297315
bip113tx_v2.nLockTime = self.last_block_time - 600 * 5 # = MTP of prior block (not <) but < time put on current block
298316
bip113signed2 = sign_transaction(self.nodes[0], bip113tx_v2)
299317
success_txs.append(bip113signed2)
300318
success_txs.append(bip112tx_special_v2)
319+
success_txs.append(bip112tx_emptystack_v2)
301320
# add BIP 68 txs
302321
success_txs.extend(all_rlt_txs(bip68txs_v2))
303322
# add BIP 112 with seq=10 txs
@@ -385,8 +404,10 @@ def run_test(self):
385404
self.log.info("BIP 112 tests")
386405
self.log.info("Test version 1 txs")
387406

388-
# -1 OP_CSV tx should fail
407+
# -1 OP_CSV tx and (empty stack) OP_CSV tx should fail
389408
self.send_blocks([self.create_test_block([bip112tx_special_v1])], success=False)
409+
self.send_blocks([self.create_test_block([bip112tx_emptystack_v1])], success=False,
410+
reject_reason='non-mandatory-script-verify-flag (Operation not valid with the current stack size)')
390411
# If SEQUENCE_LOCKTIME_DISABLE_FLAG is set in argument to OP_CSV, version 1 txs should still pass
391412

392413
success_txs = [tx['tx'] for tx in bip112txs_vary_OP_CSV_v1 if tx['sdf']]
@@ -404,8 +425,10 @@ def run_test(self):
404425

405426
self.log.info("Test version 2 txs")
406427

407-
# -1 OP_CSV tx should fail
428+
# -1 OP_CSV tx and (empty stack) OP_CSV tx should fail
408429
self.send_blocks([self.create_test_block([bip112tx_special_v2])], success=False)
430+
self.send_blocks([self.create_test_block([bip112tx_emptystack_v2])], success=False,
431+
reject_reason='non-mandatory-script-verify-flag (Operation not valid with the current stack size)')
409432

410433
# If SEQUENCE_LOCKTIME_DISABLE_FLAG is set in argument to OP_CSV, version 2 txs should pass (all sequence locks are met)
411434
success_txs = [tx['tx'] for tx in bip112txs_vary_OP_CSV_v2 if tx['sdf']]
@@ -449,7 +472,5 @@ def run_test(self):
449472
self.send_blocks([self.create_test_block(time_txs)])
450473
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
451474

452-
# TODO: Test empty stack fails
453-
454475
if __name__ == '__main__':
455476
BIP68_112_113Test().main()

0 commit comments

Comments
 (0)