1 package tx
2 3 import (
4 "encoding/hex"
5 "encoding/json"
6 "os"
7 "strings"
8 "testing"
9 )
10 11 // bip143Fixture is a spend the rig node signed: an unsigned transaction, the prevout it
12 // spends, and the finished signed transaction. It is the end-to-end anchor for this package —
13 // the serialiser has to rebuild the unsigned bytes exactly, and the signed form has to be the
14 // one the node produced for those bytes.
15 type bip143Fixture struct {
16 Secret string `json:"secret"`
17 Pubkey string `json:"pubkey"`
18 ScriptPubKey string `json:"scriptPubKey"`
19 Sequence uint32 `json:"sequence"`
20 Prevout struct {
21 TxID string `json:"txid"`
22 Vout uint32 `json:"vout"`
23 Amount uint64 `json:"amount"`
24 } `json:"prevout"`
25 Unsigned string `json:"unsigned"`
26 Signed string `json:"signed"`
27 Complete bool `json:"complete"`
28 }
29 30 // loadFixture reads it.
31 func loadFixture(t *testing.T) bip143Fixture {
32 t.Helper()
33 34 raw, err := os.ReadFile("testdata/bip143.json")
35 36 if err != nil {
37 t.Fatal(err)
38 }
39 40 var fixture bip143Fixture
41 42 if err := json.Unmarshal(raw, &fixture); err != nil {
43 t.Fatal(err)
44 }
45 46 return fixture
47 }
48 49 // TestTheFixtureRebuilds: the package rebuilds the node's unsigned transaction byte for byte
50 // from the fields a wallet holds, which is what makes the fixture usable for the sighash.
51 func TestTheFixtureRebuilds(t *testing.T) {
52 fixture := loadFixture(t)
53 54 hash, err := hex.DecodeString(fixture.Prevout.TxID)
55 56 if err != nil {
57 t.Fatal(err)
58 }
59 60 var outpoint [32]byte
61 copy(outpoint[:], hash)
62 63 payScript, err := hex.DecodeString(fixture.PayScript(t))
64 65 if err != nil {
66 t.Fatal(err)
67 }
68 69 transaction := New()
70 transaction.Version = 2
71 transaction.AddInput(Outpoint{Hash: outpoint, Index: fixture.Prevout.Vout}, fixture.Sequence)
72 transaction.AddOutput(50000, payScript)
73 74 raw, err := transaction.Serialize()
75 76 if err != nil {
77 t.Fatal(err)
78 }
79 80 if got := hex.EncodeToString(raw); got != fixture.Unsigned {
81 t.Fatalf("rebuilt\n%s\nwant\n%s", got, fixture.Unsigned)
82 }
83 }
84 85 // PayScript reads the output script out of the fixture's own unsigned transaction, so the test
86 // does not need a second copy of it.
87 func (f bip143Fixture) PayScript(t *testing.T) string {
88 t.Helper()
89 90 raw, err := hex.DecodeString(f.Unsigned)
91 92 if err != nil {
93 t.Fatal(err)
94 }
95 96 // The output script is the last varint-prefixed field before the locktime; for this
97 // fixture it is a 22-byte P2WPKH script, so it is the 22 bytes that follow the 0x16 length
98 // in the output section.
99 index := strings.LastIndex(f.Unsigned, "160014")
100 101 if index < 0 {
102 t.Fatal("the fixture has no P2WPKH output")
103 }
104 105 start := index / 2
106 107 return hex.EncodeToString(raw[start+1 : start+23])
108 }
109 110 // TestTheSignedFormIsPinned: the node's signed transaction has the marker, the flag, and this
111 // key in its witness.
112 func TestTheSignedFormIsPinned(t *testing.T) {
113 fixture := loadFixture(t)
114 115 if !fixture.Complete {
116 t.Fatal("the fixture is not a complete signature")
117 }
118 119 if !strings.HasPrefix(fixture.Signed, "020000000001") {
120 t.Fatalf("the signed form starts %s", fixture.Signed[:12])
121 }
122 123 if !strings.Contains(fixture.Signed, fixture.Pubkey) {
124 t.Fatal("the witness does not carry the signing key")
125 }
126 }
127