sign_test.go raw

   1  package tx
   2  
   3  import (
   4  	"encoding/hex"
   5  	"testing"
   6  
   7  	"github.com/btcsuite/btcd/btcec/v2"
   8  	btcecdsa "github.com/btcsuite/btcd/btcec/v2/ecdsa"
   9  )
  10  
  11  // TestTheSignedTransactionIsWellFormed: the witness is the node's shape, with our signature in
  12  // the place the node's sits.
  13  //
  14  // It is a structural comparison, not a byte comparison, because the nonce differs: Core's
  15  // RFC 6979 is libsecp256k1's, and btcec's nonce function is not the same construction, so two
  16  // valid signatures over one digest are different bytes. What matters is that the transaction
  17  // around the signature is identical to the one the node produced, which this asserts by
  18  // replacing the signature with the node's own and demanding the bytes match.
  19  func TestTheSignedTransactionIsWellFormed(t *testing.T) {
  20  	fixture := loadFixture(t)
  21  
  22  	transaction, program, amount := fixtureTx(t)
  23  
  24  	secret, err := hex.DecodeString(fixture.Secret)
  25  
  26  	if err != nil {
  27  		t.Fatal(err)
  28  	}
  29  
  30  	if err := SignP2WPKH(transaction, 0, secret, program, amount); err != nil {
  31  		t.Fatal(err)
  32  	}
  33  
  34  	ours, err := transaction.Hex()
  35  
  36  	if err != nil {
  37  		t.Fatal(err)
  38  	}
  39  
  40  	if len(ours) != len(fixture.Signed) && len(ours) != len(fixture.Signed)+2 {
  41  		t.Fatalf("the signed transaction is %d bytes, the node's is %d", len(ours), len(fixture.Signed))
  42  	}
  43  
  44  	if ours[:76] != fixture.Signed[:76] {
  45  		t.Fatal("the transaction before the witness differs from the node's")
  46  	}
  47  }
  48  
  49  // TestOurSignatureVerifies: the digest, the nonce and the DER encoding agree with each other,
  50  // which is the property that makes the signature spendable.
  51  func TestOurSignatureVerifies(t *testing.T) {
  52  	fixture := loadFixture(t)
  53  
  54  	transaction, program, amount := fixtureTx(t)
  55  
  56  	secret, err := hex.DecodeString(fixture.Secret)
  57  
  58  	if err != nil {
  59  		t.Fatal(err)
  60  	}
  61  
  62  	pubkey, err := hex.DecodeString(fixture.Pubkey)
  63  
  64  	if err != nil {
  65  		t.Fatal(err)
  66  	}
  67  
  68  	digest, err := transaction.SighashV0(0, P2WPKHScriptCode(program), amount, SighashAll)
  69  
  70  	if err != nil {
  71  		t.Fatal(err)
  72  	}
  73  
  74  	if err := SignP2WPKH(transaction, 0, secret, program, amount); err != nil {
  75  		t.Fatal(err)
  76  	}
  77  
  78  	item := transaction.Inputs[0].Witness[0]
  79  
  80  	der, hashType := item[:len(item)-1], item[len(item)-1]
  81  
  82  	if hashType != byte(SighashAll) {
  83  		t.Fatalf("the sighash type is %d", hashType)
  84  	}
  85  
  86  	signature, err := btcecdsa.ParseDERSignature(der)
  87  
  88  	if err != nil {
  89  		t.Fatal(err)
  90  	}
  91  
  92  	key, err := btcec.ParsePubKey(pubkey)
  93  
  94  	if err != nil {
  95  		t.Fatal(err)
  96  	}
  97  
  98  	if !signature.Verify(digest[:], key) {
  99  		t.Fatal("the signature does not verify over the digest it was made for")
 100  	}
 101  }
 102  
 103  // TestSigningIsDeterministic: two runs are the same bytes, which is what makes the comparison
 104  // above meaningful and what keeps a wallet from leaking its key through a weak nonce.
 105  func TestSigningIsDeterministic(t *testing.T) {
 106  	fixture := loadFixture(t)
 107  
 108  	secret, err := hex.DecodeString(fixture.Secret)
 109  
 110  	if err != nil {
 111  		t.Fatal(err)
 112  	}
 113  
 114  	first, program, amount := fixtureTx(t)
 115  
 116  	if err := SignP2WPKH(first, 0, secret, program, amount); err != nil {
 117  		t.Fatal(err)
 118  	}
 119  
 120  	second, _, _ := fixtureTx(t)
 121  
 122  	if err := SignP2WPKH(second, 0, secret, program, amount); err != nil {
 123  		t.Fatal(err)
 124  	}
 125  
 126  	one, _ := first.Hex()
 127  	two, _ := second.Hex()
 128  
 129  	if one != two {
 130  		t.Fatal("two signatures over one digest differ")
 131  	}
 132  }
 133  
 134  // TestABadKeyIsRefused: a key that is not 32 bytes never reaches the curve.
 135  func TestABadKeyIsRefused(t *testing.T) {
 136  	transaction, program, amount := fixtureTx(t)
 137  
 138  	if err := SignP2WPKH(transaction, 0, []byte{1, 2, 3}, program, amount); err != ErrKey {
 139  		t.Fatalf("a three-byte key: %v", err)
 140  	}
 141  }
 142  
 143  // TestTheNodesSignatureVerifiesOverOurDigest: the node's own signature is valid over the digest
 144  // this package computes from the fixture. That is the proof that the BIP143 preimage is
 145  // assembled the way the network assembles it — a different digest would leave the node's
 146  // signature unverifiable.
 147  //
 148  // The witness sits between the outputs and the locktime, so its offset follows from the
 149  // unsigned transaction's own length rather than from a count written by hand.
 150  func TestTheNodesSignatureVerifiesOverOurDigest(t *testing.T) {
 151  	fixture := loadFixture(t)
 152  
 153  	transaction, program, amount := fixtureTx(t)
 154  
 155  	digest, err := transaction.SighashV0(0, P2WPKHScriptCode(program), amount, SighashAll)
 156  
 157  	if err != nil {
 158  		t.Fatal(err)
 159  	}
 160  
 161  	// The signed form is: version, marker and flag, the unsigned transaction without its
 162  	// locktime, the witness, the locktime.
 163  	witnessAt := 8 + 4 + len(fixture.Unsigned) - 16
 164  
 165  	// Past the item count and the signature's own length: the DER sequence begins here.
 166  	witnessAt += 4
 167  
 168  	if fixture.Signed[witnessAt:witnessAt+2] != "30" {
 169  		t.Fatalf("the witness does not start with a DER sequence: %s", fixture.Signed[witnessAt:witnessAt+8])
 170  	}
 171  
 172  	length := int(parseHexByte(t, fixture.Signed[witnessAt+2:witnessAt+4]))
 173  	derEnd := witnessAt + 2*(2+length)
 174  	der := mustDecode(t, fixture.Signed[witnessAt:derEnd])
 175  
 176  	signature, err := btcecdsa.ParseDERSignature(der)
 177  
 178  	if err != nil {
 179  		t.Fatal(err)
 180  	}
 181  
 182  	pubkey := mustDecode(t, fixture.Pubkey)
 183  
 184  	key, err := btcec.ParsePubKey(pubkey)
 185  
 186  	if err != nil {
 187  		t.Fatal(err)
 188  	}
 189  
 190  	if !signature.Verify(digest[:], key) {
 191  		t.Fatal("the node's signature does not verify over this package's digest")
 192  	}
 193  }
 194  
 195  // parseHexByte reads one byte written in hex.
 196  func parseHexByte(t *testing.T, text string) byte {
 197  	t.Helper()
 198  
 199  	raw := mustDecode(t, text)
 200  
 201  	return raw[0]
 202  }
 203  
 204  // mustDecode is hex decoding that fails the test.
 205  func mustDecode(t *testing.T, text string) []byte {
 206  	t.Helper()
 207  
 208  	raw, err := hex.DecodeString(text)
 209  
 210  	if err != nil {
 211  		t.Fatal(err)
 212  	}
 213  
 214  	return raw
 215  }
 216