Skip to content

Commit

Permalink
[qa] Test that v0 segwit outputs can't be spent pre-activation
Browse files Browse the repository at this point in the history
Also updates the comments for an existing test, that now should be rewritten.
Includes changes suggested by John Newbery.
  • Loading branch information
sdaftuar committed Apr 13, 2018
1 parent ccb8ca4 commit 8b56fc0
Showing 1 changed file with 84 additions and 4 deletions.
88 changes: 84 additions & 4 deletions test/functional/p2p_segwit.py
Expand Up @@ -47,7 +47,7 @@ def test_transaction_acceptance(rpc, p2p, tx, with_witness, accepted, reason=Non
with mininode_lock:
assert_equal(p2p.last_message["reject"].reason, reason)

def test_witness_block(rpc, p2p, block, accepted, with_witness=True):
def test_witness_block(rpc, p2p, block, accepted, with_witness=True, reason=None):
"""Send a block to the node and check that it's accepted
- Submit the block over the p2p interface
Expand All @@ -58,6 +58,10 @@ def test_witness_block(rpc, p2p, block, accepted, with_witness=True):
p2p.send_message(msg_block(block))
p2p.sync_with_ping()
assert_equal(rpc.getbestblockhash() == block.hash, accepted)
if (reason != None and not accepted):
# Check the rejection reason as well.
with mininode_lock:
assert_equal(p2p.last_message["reject"].reason, reason)

class TestP2PConn(P2PInterface):
def __init__(self):
Expand Down Expand Up @@ -271,6 +275,80 @@ def test_unnecessary_witness_before_segwit_activation(self):
self.utxo.pop(0)
self.utxo.append(UTXO(tx4.sha256, 0, tx4.vout[0].nValue))

# ~6 months after segwit activation, the SCRIPT_VERIFY_WITNESS flag was
# backdated so that it applies to all blocks, going back to the genesis
# block.
#
# Consequently, version 0 witness outputs are never spendable without
# witness, and so can't be spent before segwit activation (the point at which
# blocks are permitted to contain witnesses).
def test_v0_outputs_arent_spendable(self):
self.log.info("Testing that v0 witness program outputs aren't spendable before activation")

assert len(self.utxo), "self.utxo is empty"

# Create two outputs, a p2wsh and p2sh-p2wsh
witness_program = CScript([OP_TRUE])
witness_hash = sha256(witness_program)
scriptPubKey = CScript([OP_0, witness_hash])

p2sh_pubkey = hash160(scriptPubKey)
p2sh_scriptPubKey = CScript([OP_HASH160, p2sh_pubkey, OP_EQUAL])

value = self.utxo[0].nValue // 3

tx = CTransaction()
tx.vin = [CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b'')]
tx.vout = [CTxOut(value, scriptPubKey), CTxOut(value, p2sh_scriptPubKey)]
tx.vout.append(CTxOut(value, CScript([OP_TRUE])))
tx.rehash()
txid = tx.sha256

# Add it to a block
block = self.build_next_block()
self.update_witness_block_with_transactions(block, [tx])
# Verify that segwit isn't activated. A block serialized with witness
# should be rejected prior to activation.
test_witness_block(self.nodes[0], self.test_node, block, accepted=False, with_witness=True, reason = b'unexpected-witness')
# Now send the block without witness. It should be accepted
test_witness_block(self.nodes[0], self.test_node, block, accepted=True, with_witness=False)

# Now try to spend the outputs. This should fail since SCRIPT_VERIFY_WITNESS is always enabled.
p2wsh_tx = CTransaction()
p2wsh_tx.vin = [CTxIn(COutPoint(txid, 0), b'')]
p2wsh_tx.vout = [CTxOut(value, CScript([OP_TRUE]))]
p2wsh_tx.wit.vtxinwit.append(CTxInWitness())
p2wsh_tx.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE])]
p2wsh_tx.rehash()

p2sh_p2wsh_tx = CTransaction()
p2sh_p2wsh_tx.vin = [CTxIn(COutPoint(txid, 1), CScript([scriptPubKey]))]
p2sh_p2wsh_tx.vout = [CTxOut(value, CScript([OP_TRUE]))]
p2sh_p2wsh_tx.wit.vtxinwit.append(CTxInWitness())
p2sh_p2wsh_tx.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE])]
p2sh_p2wsh_tx.rehash()

for tx in [p2wsh_tx, p2sh_p2wsh_tx]:

block = self.build_next_block()
self.update_witness_block_with_transactions(block, [tx])

# When the block is serialized with a witness, the block will be rejected because witness
# data isn't allowed in blocks that don't commit to witness data.
test_witness_block(self.nodes[0], self.test_node, block, accepted=False, with_witness=True, reason=b'unexpected-witness')

# When the block is serialized without witness, validation fails because the transaction is
# invalid (transactions are always validated with SCRIPT_VERIFY_WITNESS so a segwit v0 transaction
# without a witness is invalid).
# Note: The reject reason for this failure could be
# 'block-validation-failed' (if script check threads > 1) or
# 'non-mandatory-script-verify-flag (Witness program was passed an
# empty witness)' (otherwise).
# TODO: support multiple acceptable reject reasons.
test_witness_block(self.nodes[0], self.test_node, block, accepted=False, with_witness=False)

self.utxo.pop(0)
self.utxo.append(UTXO(txid, 2, value))

# Mine enough blocks for segwit's vb state to be 'started'.
def advance_to_segwit_started(self):
Expand Down Expand Up @@ -1476,9 +1554,10 @@ def test_p2sh_witness(self, segwit_activated):
block = self.build_next_block()
self.update_witness_block_with_transactions(block, [spend_tx])

# If we're before activation, then sending this without witnesses
# should be valid. If we're after activation, then sending this with
# witnesses should be valid.
# If we're after activation, then sending this with witnesses should be valid.
# This no longer works before activation, because SCRIPT_VERIFY_WITNESS
# is always set.
# TODO: rewrite this test to make clear that it only works after activation.
if segwit_activated:
test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True)
else:
Expand Down Expand Up @@ -1897,6 +1976,7 @@ def run_test(self):
self.test_witness_services() # Verifies NODE_WITNESS
self.test_non_witness_transaction() # non-witness tx's are accepted
self.test_unnecessary_witness_before_segwit_activation()
self.test_v0_outputs_arent_spendable()
self.test_block_relay(segwit_activated=False)

# Advance to segwit being 'started'
Expand Down

0 comments on commit 8b56fc0

Please sign in to comment.