tx_test.go raw
1 package tx
2
3 import (
4 "encoding/hex"
5 "errors"
6 "strings"
7 "testing"
8 )
9
10 // The vector is a transaction the rig node built with createrawtransaction: one input spending
11 // output 3 of 11..11, one output paying 1000 sats to a P2WSH script, sequence fffffffd.
12 var nodeHex = "02000000" + "01" + strings.Repeat("11", 32) + "03000000" + "00" +
13 "fdffffff" + "01" + "e803000000000000" + "22" + nodeScript + "00000000"
14
15 const (
16 // nodeScript is the P2WSH scriptPubKey the node paid.
17 nodeScript = "0020640dce64eb09d134b9ea24c4665270ae70109ed5b4866f67ac7a80c8c91d6f96"
18 )
19
20 // nodeTx builds the vector.
21 func nodeTx(t *testing.T) *Tx {
22 t.Helper()
23
24 script, err := hex.DecodeString(nodeScript)
25
26 if err != nil {
27 t.Fatal(err)
28 }
29
30 transaction := New()
31 transaction.Version = 2
32
33 var hash [32]byte
34
35 for index := range hash {
36 hash[index] = 0x11
37 }
38
39 transaction.AddInput(Outpoint{Hash: hash, Index: 3}, 0xfffffffd)
40 transaction.AddOutput(1000, script)
41
42 return transaction
43 }
44
45 // TestSerializeMatchesTheNode: the serialiser produces the bytes the node produced.
46 func TestSerializeMatchesTheNode(t *testing.T) {
47 raw, err := nodeTx(t).Serialize()
48
49 if err != nil {
50 t.Fatal(err)
51 }
52
53 if got := hex.EncodeToString(raw); got != nodeHex {
54 t.Fatalf("the transaction is\n%s\nwant\n%s", got, nodeHex)
55 }
56 }
57
58 // TestHexIsTheSameBeforeSigning: with no witnesses, the wire form is the base form.
59 func TestHexIsTheSameBeforeSigning(t *testing.T) {
60 hexText, err := nodeTx(t).Hex()
61
62 if err != nil {
63 t.Fatal(err)
64 }
65
66 if hexText != nodeHex {
67 t.Fatalf("the hex is\n%s\nwant\n%s", hexText, nodeHex)
68 }
69 }
70
71 // TestWitnessSerialisationAddsTheMarker: one witness changes the wire form and not the id.
72 func TestWitnessSerialisationAddsTheMarker(t *testing.T) {
73 transaction := nodeTx(t)
74
75 before, err := transaction.TxIDHex()
76
77 if err != nil {
78 t.Fatal(err)
79 }
80
81 transaction.Inputs[0].Witness = [][]byte{{0x01}, {0x02, 0x03}}
82
83 after, err := transaction.TxIDHex()
84
85 if err != nil {
86 t.Fatal(err)
87 }
88
89 if before != after {
90 t.Fatalf("the id changed from %s to %s", before, after)
91 }
92
93 raw, err := transaction.SerializeWitness()
94
95 if err != nil {
96 t.Fatal(err)
97 }
98
99 text := hex.EncodeToString(raw)
100
101 if !strings.HasPrefix(text, "020000000001") {
102 t.Fatalf("the witness form starts %s", text[:12])
103 }
104
105 // count 2, item 0x01, item 0x0203, then the locktime.
106 if !strings.Contains(text, "020101020203"+"00000000") {
107 t.Fatalf("the witness section is not in %s", text)
108 }
109 }
110
111 // TestTxIDIsReversed: the id a person sees is the hash written backwards.
112 func TestTxIDIsReversed(t *testing.T) {
113 transaction := nodeTx(t)
114
115 id, err := transaction.TxID()
116
117 if err != nil {
118 t.Fatal(err)
119 }
120
121 text, err := transaction.TxIDHex()
122
123 if err != nil {
124 t.Fatal(err)
125 }
126
127 if text[:2] != hex.EncodeToString(id[31:32]) {
128 t.Fatalf("the id %s does not start with the last byte of the hash", text)
129 }
130 }
131
132 // TestEmptyTransactionsAreRefused: nothing to spend or nothing to pay is not a transaction.
133 func TestEmptyTransactionsAreRefused(t *testing.T) {
134 transaction := New()
135
136 if _, err := transaction.Serialize(); !errors.Is(err, ErrNoInputs) {
137 t.Fatalf("an input-less transaction serialised: %v", err)
138 }
139
140 transaction.AddInput(Outpoint{}, SequenceFinal)
141
142 if _, err := transaction.Serialize(); !errors.Is(err, ErrNoOutputs) {
143 t.Fatalf("an output-less transaction serialised: %v", err)
144 }
145 }
146