Skip to content

Commit

Permalink
Merge bitcoin/bitcoin#28154: test: refactor: deduplicate segwitv0 ECD…
Browse files Browse the repository at this point in the history
…SA signing for tx inputs

83d7cfd test: refactor: deduplicate segwitv0 ECDSA signing for tx inputs (Sebastian Falbesoner)

Pull request description:

  This PR is a simple follow-up for #28025. It introduces a `signing_input_segwitv0` helper in order to deduplicate the following steps needed to create a segwitv0 ECDSA signature:
  1. calculate the `SegwitV0SignatureHash` with the desired sighash type
  2. create the actual digital signature by calling ECKey.sign_ecdsa on the signature message hash calculated above
  3. put the DER-encoded result (plus sighash byte) at the bottom of the witness stack

ACKs for top commit:
  achow101:
    ACK 83d7cfd
  pinheadmz:
    code review ACK at 83d7cfd

Tree-SHA512: b8e55409ddc9ddb14cfc06daeb4730d7750a4632f175f88dcac6ec4d216e71fd4a7eee325a64d6ebba3b33be50bcd30c2de7400f834c01abb67e52840d9823b6
  • Loading branch information
achow101 committed Sep 20, 2023
2 parents 99ce836 + 83d7cfd commit 8247a8d
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 11 deletions.
20 changes: 9 additions & 11 deletions test/functional/p2p_segwit.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,9 @@
SIGHASH_ANYONECANPAY,
SIGHASH_NONE,
SIGHASH_SINGLE,
SegwitV0SignatureHash,
hash160,
sign_input_legacy,
sign_input_segwitv0,
)
from test_framework.script_util import (
key_to_p2pk_script,
Expand Down Expand Up @@ -121,10 +121,8 @@ def func_wrapper(self, *args, **kwargs):

def sign_p2pk_witness_input(script, tx_to, in_idx, hashtype, value, key):
"""Add signature for a P2PK witness script."""
tx_hash = SegwitV0SignatureHash(script, tx_to, in_idx, hashtype, value)
signature = key.sign_ecdsa(tx_hash) + chr(hashtype).encode('latin-1')
tx_to.wit.vtxinwit[in_idx].scriptWitness.stack = [signature, script]
tx_to.rehash()
tx_to.wit.vtxinwit[in_idx].scriptWitness.stack = [script]
sign_input_segwitv0(tx_to, in_idx, script, value, key, hashtype)

def test_transaction_acceptance(node, p2p, tx, with_witness, accepted, reason=None):
"""Send a transaction to the node and check that it's accepted to the mempool
Expand Down Expand Up @@ -1476,11 +1474,9 @@ def test_uncompressed_pubkey(self):
tx2.vin.append(CTxIn(COutPoint(tx.sha256, 0), b""))
tx2.vout.append(CTxOut(tx.vout[0].nValue - 1000, script_wsh))
script = keyhash_to_p2pkh_script(pubkeyhash)
sig_hash = SegwitV0SignatureHash(script, tx2, 0, SIGHASH_ALL, tx.vout[0].nValue)
signature = key.sign_ecdsa(sig_hash) + b'\x01' # 0x1 is SIGHASH_ALL
tx2.wit.vtxinwit.append(CTxInWitness())
tx2.wit.vtxinwit[0].scriptWitness.stack = [signature, pubkey]
tx2.rehash()
tx2.wit.vtxinwit[0].scriptWitness.stack = [pubkey]
sign_input_segwitv0(tx2, 0, script, tx.vout[0].nValue, key)

# Should fail policy test.
test_transaction_acceptance(self.nodes[0], self.test_node, tx2, True, False, 'non-mandatory-script-verify-flag (Using non-compressed keys in segwit)')
Expand Down Expand Up @@ -1676,11 +1672,13 @@ def test_signature_version_1(self):
tx2.vout.append(CTxOut(tx.vout[0].nValue, CScript([OP_TRUE])))

script = keyhash_to_p2pkh_script(pubkeyhash)
sig_hash = SegwitV0SignatureHash(script, tx2, 0, SIGHASH_ALL, tx.vout[0].nValue)
signature = key.sign_ecdsa(sig_hash) + b'\x01' # 0x1 is SIGHASH_ALL
tx2.wit.vtxinwit.append(CTxInWitness())
sign_input_segwitv0(tx2, 0, script, tx.vout[0].nValue, key)
signature = tx2.wit.vtxinwit[0].scriptWitness.stack.pop()

# Check that we can't have a scriptSig
tx2.vin[0].scriptSig = CScript([signature, pubkey])
tx2.rehash()
block = self.build_next_block()
self.update_witness_block_with_transactions(block, [tx, tx2])
test_witness_block(self.nodes[0], self.test_node, block, accepted=False,
Expand Down
9 changes: 9 additions & 0 deletions test/functional/test_framework/script.py
Original file line number Diff line number Diff line change
Expand Up @@ -699,6 +699,15 @@ def sign_input_legacy(tx, input_index, input_scriptpubkey, privkey, sighash_type
tx.vin[input_index].scriptSig = bytes(CScript([der_sig + bytes([sighash_type])])) + tx.vin[input_index].scriptSig
tx.rehash()

def sign_input_segwitv0(tx, input_index, input_scriptpubkey, input_amount, privkey, sighash_type=SIGHASH_ALL):
"""Add segwitv0 ECDSA signature for a given transaction input. Note that the signature
is inserted at the bottom of the witness stack, i.e. additional witness data
needed (e.g. pubkey for P2WPKH) can already be set before."""
sighash = SegwitV0SignatureHash(input_scriptpubkey, tx, input_index, sighash_type, input_amount)
der_sig = privkey.sign_ecdsa(sighash)
tx.wit.vtxinwit[input_index].scriptWitness.stack.insert(0, der_sig + bytes([sighash_type]))
tx.rehash()

# TODO: Allow cached hashPrevouts/hashSequence/hashOutputs to be provided.
# Performance optimization probably not necessary for python tests, however.
# Note that this corresponds to sigversion == 1 in EvalScript, which is used
Expand Down

0 comments on commit 8247a8d

Please sign in to comment.