sign.go raw

   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