Merge bitcoin/bitcoin#28154: test: refactor: deduplicate segwitv0 ECDSA signing for tx inputs

83d7cfd542 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 83d7cfd542
  pinheadmz:
    code review ACK at 83d7cfd542

Tree-SHA512: b8e55409ddc9ddb14cfc06daeb4730d7750a4632f175f88dcac6ec4d216e71fd4a7eee325a64d6ebba3b33be50bcd30c2de7400f834c01abb67e52840d9823b6
This commit is contained in:
Andrew Chow 2023-09-20 13:43:37 -04:00
commit 8247a8db69
No known key found for this signature in database
GPG key ID: 17565732E08E5E41
2 changed files with 18 additions and 11 deletions

View file

@ -70,9 +70,9 @@ from test_framework.script import (
SIGHASH_ANYONECANPAY, SIGHASH_ANYONECANPAY,
SIGHASH_NONE, SIGHASH_NONE,
SIGHASH_SINGLE, SIGHASH_SINGLE,
SegwitV0SignatureHash,
hash160, hash160,
sign_input_legacy, sign_input_legacy,
sign_input_segwitv0,
) )
from test_framework.script_util import ( from test_framework.script_util import (
key_to_p2pk_script, key_to_p2pk_script,
@ -121,10 +121,8 @@ def subtest(func):
def sign_p2pk_witness_input(script, tx_to, in_idx, hashtype, value, key): def sign_p2pk_witness_input(script, tx_to, in_idx, hashtype, value, key):
"""Add signature for a P2PK witness script.""" """Add signature for a P2PK witness script."""
tx_hash = SegwitV0SignatureHash(script, tx_to, in_idx, hashtype, value) tx_to.wit.vtxinwit[in_idx].scriptWitness.stack = [script]
signature = key.sign_ecdsa(tx_hash) + chr(hashtype).encode('latin-1') sign_input_segwitv0(tx_to, in_idx, script, value, key, hashtype)
tx_to.wit.vtxinwit[in_idx].scriptWitness.stack = [signature, script]
tx_to.rehash()
def test_transaction_acceptance(node, p2p, tx, with_witness, accepted, reason=None): 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 """Send a transaction to the node and check that it's accepted to the mempool
@ -1476,11 +1474,9 @@ class SegWitTest(BitcoinTestFramework):
tx2.vin.append(CTxIn(COutPoint(tx.sha256, 0), b"")) tx2.vin.append(CTxIn(COutPoint(tx.sha256, 0), b""))
tx2.vout.append(CTxOut(tx.vout[0].nValue - 1000, script_wsh)) tx2.vout.append(CTxOut(tx.vout[0].nValue - 1000, script_wsh))
script = keyhash_to_p2pkh_script(pubkeyhash) 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.append(CTxInWitness())
tx2.wit.vtxinwit[0].scriptWitness.stack = [signature, pubkey] tx2.wit.vtxinwit[0].scriptWitness.stack = [pubkey]
tx2.rehash() sign_input_segwitv0(tx2, 0, script, tx.vout[0].nValue, key)
# Should fail policy test. # 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)') test_transaction_acceptance(self.nodes[0], self.test_node, tx2, True, False, 'non-mandatory-script-verify-flag (Using non-compressed keys in segwit)')
@ -1676,11 +1672,13 @@ class SegWitTest(BitcoinTestFramework):
tx2.vout.append(CTxOut(tx.vout[0].nValue, CScript([OP_TRUE]))) tx2.vout.append(CTxOut(tx.vout[0].nValue, CScript([OP_TRUE])))
script = keyhash_to_p2pkh_script(pubkeyhash) script = keyhash_to_p2pkh_script(pubkeyhash)
sig_hash = SegwitV0SignatureHash(script, tx2, 0, SIGHASH_ALL, tx.vout[0].nValue) tx2.wit.vtxinwit.append(CTxInWitness())
signature = key.sign_ecdsa(sig_hash) + b'\x01' # 0x1 is SIGHASH_ALL 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 # Check that we can't have a scriptSig
tx2.vin[0].scriptSig = CScript([signature, pubkey]) tx2.vin[0].scriptSig = CScript([signature, pubkey])
tx2.rehash()
block = self.build_next_block() block = self.build_next_block()
self.update_witness_block_with_transactions(block, [tx, tx2]) self.update_witness_block_with_transactions(block, [tx, tx2])
test_witness_block(self.nodes[0], self.test_node, block, accepted=False, test_witness_block(self.nodes[0], self.test_node, block, accepted=False,

View file

@ -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.vin[input_index].scriptSig = bytes(CScript([der_sig + bytes([sighash_type])])) + tx.vin[input_index].scriptSig
tx.rehash() 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. # TODO: Allow cached hashPrevouts/hashSequence/hashOutputs to be provided.
# Performance optimization probably not necessary for python tests, however. # Performance optimization probably not necessary for python tests, however.
# Note that this corresponds to sigversion == 1 in EvalScript, which is used # Note that this corresponds to sigversion == 1 in EvalScript, which is used