1 package tx
2 3 import (
4 "errors"
5 6 "github.com/btcsuite/btcd/btcec/v2"
7 btcecdsa "github.com/btcsuite/btcd/btcec/v2/ecdsa"
8 )
9 10 // ErrKey is a secret key that is not a valid scalar.
11 var ErrKey = errors.New("tx: not a usable secp256k1 key")
12 13 // SignP2WPKH signs one input with a key and assembles its witness.
14 //
15 // The signature is deterministic (RFC 6979), which is what makes it comparable: sign the same
16 // digest with the same key and the bytes are the same every time, on this machine and on the
17 // node that signed the fixture. Low-S is btcec's default and the network's rule, and the
18 // sighash type is appended to the DER encoding because that is where a verifier reads it.
19 func SignP2WPKH(transaction *Tx, index int, secret []byte, program [20]byte, amount uint64) error {
20 if len(secret) != 32 {
21 return ErrKey
22 }
23 24 key, _ := btcec.PrivKeyFromBytes(secret)
25 26 if key == nil {
27 return ErrKey
28 }
29 30 digest, err := transaction.SighashV0(index, P2WPKHScriptCode(program), amount, SighashAll)
31 32 if err != nil {
33 return err
34 }
35 36 signature := btcecdsa.Sign(key, digest[:])
37 38 der := append(signature.Serialize(), byte(SighashAll))
39 40 transaction.Inputs[index].Witness = [][]byte{der, key.PubKey().SerializeCompressed()}
41 42 return nil
43 }
44 45 // SignP2WSH signs one P2WSH input with a key and the script it is spending, and leaves the
46 // witness's last item for the caller.
47 //
48 // The scriptCode of a P2WSH input is the witness script itself, which is why the script is an
49 // argument here and a program is not: the signature commits to the script that will run, so a
50 // different HTLC — a different key, hash or locktime — is a different digest.
51 func SignP2WSH(transaction *Tx, index int, secret, witnessScript []byte, amount uint64) ([]byte, error) {
52 if len(secret) != 32 {
53 return nil, ErrKey
54 }
55 56 if len(witnessScript) == 0 {
57 return nil, ErrScriptTooLong
58 }
59 60 key, _ := btcec.PrivKeyFromBytes(secret)
61 62 if key == nil {
63 return nil, ErrKey
64 }
65 66 digest, err := transaction.SighashV0(index, witnessScript, amount, SighashAll)
67 68 if err != nil {
69 return nil, err
70 }
71 72 signature := btcecdsa.Sign(key, digest[:])
73 74 return append(signature.Serialize(), byte(SighashAll)), nil
75 }
76 77 // SignHTLCClaim spends the claim branch: the preimage, then the signature, then a true
78 // selector, then the script.
79 //
80 // That order is the stack as the script reads it, not a convention: OP_IF pops the selector,
81 // OP_CHECKSIGVERIFY pops the signature, and OP_SHA256 pops the preimage.
82 func SignHTLCClaim(transaction *Tx, index int, secret, witnessScript, preimage []byte, amount uint64) error {
83 signature, err := SignP2WSH(transaction, index, secret, witnessScript, amount)
84 85 if err != nil {
86 return err
87 }
88 89 transaction.Inputs[index].Witness = [][]byte{preimage, signature, {0x01}, witnessScript}
90 91 return nil
92 }
93 94 // SignHTLCRefund spends the refund branch: the signature, an empty selector, then the script.
95 func SignHTLCRefund(transaction *Tx, index int, secret, witnessScript []byte, amount uint64) error {
96 signature, err := SignP2WSH(transaction, index, secret, witnessScript, amount)
97 98 if err != nil {
99 return err
100 }
101 102 transaction.Inputs[index].Witness = [][]byte{signature, {}, witnessScript}
103 104 return nil
105 }
106