Looked at the latest version here and I think this is still not addressed, but my comment was not really helpful. The interesting case in my eyes is that on the final block, a UINT32_MAX - 1 timelocked transaction is considered standard and enters the mempool, but can't ever be mined. How about adding this:
diff --git a/test/functional/feature_csv_activation.py b/test/functional/feature_csv_activation.py
index 410278f407b..943890d81bc 100755
--- a/test/functional/feature_csv_activation.py
+++ b/test/functional/feature_csv_activation.py
@@ -61,7 +61,7 @@ from test_framework.wallet import (
)
TESTING_TX_COUNT = 83 # Number of testing transactions: 1 BIP113 tx, 16 BIP68 txs, 66 BIP112 txs (see comments above)
-MTP_EDGE_CASE_INPUT_COUNT = 2 # Extra inputs for the "median-time-past can never reach UINT32_MAX" BIP113 test
+MTP_EDGE_CASE_INPUT_COUNT = 3 # Extra inputs for the "median-time-past can never reach UINT32_MAX" BIP113 test
COINBASE_BLOCK_COUNT = TESTING_TX_COUNT + MTP_EDGE_CASE_INPUT_COUNT # Number of coinbase blocks we need to generate as inputs for our txs
BASE_RELATIVE_LOCKTIME = 10
SEQ_DISABLE_FLAG = 1 << 31
@@ -520,6 +520,10 @@ class BIP68_112_113Test(BitcoinTestFramework):
block.solve()
self.send_blocks([block], success=False, reject_reason='bad-txns-nonfinal')
+ # A transaction locked to UINT32_MAX - 1 is not final until the last block is mined
+ max_locktime_standard_tx = mtp_edge_case_tx(mtp_edge_case_inputs[2], UINT32_MAX - 1)
+ assert_raises_rpc_error(-26, 'non-final', self.nodes[0].sendrawtransaction, max_locktime_standard_tx.serialize().hex())
+
# max out MTP on the network to UINT32_MAX, so that the next block's nTime can't exceed it (and thus any block will be rejected for time-too-old)
while self.nodes[0].getblockheader(self.nodes[0].getbestblockhash())['mediantime'] < UINT32_MAX:
block = create_block(self.tip, height=self.tipheight + 1, ntime=UINT32_MAX)
@@ -530,6 +534,10 @@ class BIP68_112_113Test(BitcoinTestFramework):
assert_equal(self.nodes[0].getblockheader(self.nodes[0].getbestblockhash())['mediantime'], UINT32_MAX)
+ # The transaction locked to UINT32_MAX - 1 is now final and enters the mempool, but there can't be another block, so it won't be mined
+ self.nodes[0].sendrawtransaction(max_locktime_standard_tx.serialize().hex())
+ assert max_locktime_standard_tx.txid_hex in self.nodes[0].getrawmempool()
+
# submitting a transaction to the mempool with nLockTime at UINT32_MAX fails
assert_raises_rpc_error(-26, 'non-final', self.nodes[0].sendrawtransaction, never_final_tx.serialize().hex())