package tx import ( "encoding/hex" "testing" "github.com/btcsuite/btcd/btcec/v2" btcecdsa "github.com/btcsuite/btcd/btcec/v2/ecdsa" ) // TestTheSignedTransactionIsWellFormed: the witness is the node's shape, with our signature in // the place the node's sits. // // It is a structural comparison, not a byte comparison, because the nonce differs: Core's // RFC 6979 is libsecp256k1's, and btcec's nonce function is not the same construction, so two // valid signatures over one digest are different bytes. What matters is that the transaction // around the signature is identical to the one the node produced, which this asserts by // replacing the signature with the node's own and demanding the bytes match. func TestTheSignedTransactionIsWellFormed(t *testing.T) { fixture := loadFixture(t) transaction, program, amount := fixtureTx(t) secret, err := hex.DecodeString(fixture.Secret) if err != nil { t.Fatal(err) } if err := SignP2WPKH(transaction, 0, secret, program, amount); err != nil { t.Fatal(err) } ours, err := transaction.Hex() if err != nil { t.Fatal(err) } if len(ours) != len(fixture.Signed) && len(ours) != len(fixture.Signed)+2 { t.Fatalf("the signed transaction is %d bytes, the node's is %d", len(ours), len(fixture.Signed)) } if ours[:76] != fixture.Signed[:76] { t.Fatal("the transaction before the witness differs from the node's") } } // TestOurSignatureVerifies: the digest, the nonce and the DER encoding agree with each other, // which is the property that makes the signature spendable. func TestOurSignatureVerifies(t *testing.T) { fixture := loadFixture(t) transaction, program, amount := fixtureTx(t) secret, err := hex.DecodeString(fixture.Secret) if err != nil { t.Fatal(err) } pubkey, err := hex.DecodeString(fixture.Pubkey) if err != nil { t.Fatal(err) } digest, err := transaction.SighashV0(0, P2WPKHScriptCode(program), amount, SighashAll) if err != nil { t.Fatal(err) } if err := SignP2WPKH(transaction, 0, secret, program, amount); err != nil { t.Fatal(err) } item := transaction.Inputs[0].Witness[0] der, hashType := item[:len(item)-1], item[len(item)-1] if hashType != byte(SighashAll) { t.Fatalf("the sighash type is %d", hashType) } signature, err := btcecdsa.ParseDERSignature(der) if err != nil { t.Fatal(err) } key, err := btcec.ParsePubKey(pubkey) if err != nil { t.Fatal(err) } if !signature.Verify(digest[:], key) { t.Fatal("the signature does not verify over the digest it was made for") } } // TestSigningIsDeterministic: two runs are the same bytes, which is what makes the comparison // above meaningful and what keeps a wallet from leaking its key through a weak nonce. func TestSigningIsDeterministic(t *testing.T) { fixture := loadFixture(t) secret, err := hex.DecodeString(fixture.Secret) if err != nil { t.Fatal(err) } first, program, amount := fixtureTx(t) if err := SignP2WPKH(first, 0, secret, program, amount); err != nil { t.Fatal(err) } second, _, _ := fixtureTx(t) if err := SignP2WPKH(second, 0, secret, program, amount); err != nil { t.Fatal(err) } one, _ := first.Hex() two, _ := second.Hex() if one != two { t.Fatal("two signatures over one digest differ") } } // TestABadKeyIsRefused: a key that is not 32 bytes never reaches the curve. func TestABadKeyIsRefused(t *testing.T) { transaction, program, amount := fixtureTx(t) if err := SignP2WPKH(transaction, 0, []byte{1, 2, 3}, program, amount); err != ErrKey { t.Fatalf("a three-byte key: %v", err) } } // TestTheNodesSignatureVerifiesOverOurDigest: the node's own signature is valid over the digest // this package computes from the fixture. That is the proof that the BIP143 preimage is // assembled the way the network assembles it — a different digest would leave the node's // signature unverifiable. // // The witness sits between the outputs and the locktime, so its offset follows from the // unsigned transaction's own length rather than from a count written by hand. func TestTheNodesSignatureVerifiesOverOurDigest(t *testing.T) { fixture := loadFixture(t) transaction, program, amount := fixtureTx(t) digest, err := transaction.SighashV0(0, P2WPKHScriptCode(program), amount, SighashAll) if err != nil { t.Fatal(err) } // The signed form is: version, marker and flag, the unsigned transaction without its // locktime, the witness, the locktime. witnessAt := 8 + 4 + len(fixture.Unsigned) - 16 // Past the item count and the signature's own length: the DER sequence begins here. witnessAt += 4 if fixture.Signed[witnessAt:witnessAt+2] != "30" { t.Fatalf("the witness does not start with a DER sequence: %s", fixture.Signed[witnessAt:witnessAt+8]) } length := int(parseHexByte(t, fixture.Signed[witnessAt+2:witnessAt+4])) derEnd := witnessAt + 2*(2+length) der := mustDecode(t, fixture.Signed[witnessAt:derEnd]) signature, err := btcecdsa.ParseDERSignature(der) if err != nil { t.Fatal(err) } pubkey := mustDecode(t, fixture.Pubkey) key, err := btcec.ParsePubKey(pubkey) if err != nil { t.Fatal(err) } if !signature.Verify(digest[:], key) { t.Fatal("the node's signature does not verify over this package's digest") } } // parseHexByte reads one byte written in hex. func parseHexByte(t *testing.T, text string) byte { t.Helper() raw := mustDecode(t, text) return raw[0] } // mustDecode is hex decoding that fails the test. func mustDecode(t *testing.T, text string) []byte { t.Helper() raw, err := hex.DecodeString(text) if err != nil { t.Fatal(err) } return raw }