scriptbuilder_test.go raw
1 package txscript
2
3 import (
4 "bytes"
5 "testing"
6 )
7
8 // TestScriptBuilderAddOp tests that pushing opcodes to a script via the ScriptBuilder API works as expected.
9 func TestScriptBuilderAddOp(t *testing.T) {
10 t.Parallel()
11 tests := []struct {
12 name string
13 opcodes []byte
14 expected []byte
15 }{
16 {
17 name: "push OP_0",
18 opcodes: []byte{OP_0},
19 expected: []byte{OP_0},
20 },
21 {
22 name: "push OP_1 OP_2",
23 opcodes: []byte{OP_1, OP_2},
24 expected: []byte{OP_1, OP_2},
25 },
26 {
27 name: "push OP_HASH160 OP_EQUAL",
28 opcodes: []byte{OP_HASH160, OP_EQUAL},
29 expected: []byte{OP_HASH160, OP_EQUAL},
30 },
31 }
32 // Run tests and individually add each op via AddOp.
33 builder := NewScriptBuilder()
34 t.Logf("Running %d tests", len(tests))
35 for i, test := range tests {
36 builder.Reset()
37 for _, opcode := range test.opcodes {
38 builder.AddOp(opcode)
39 }
40 result, e := builder.Script()
41 if e != nil {
42 t.Errorf("ScriptBuilder.AddOp #%d (%s) unexpected "+
43 "error: %v", i, test.name, e,
44 )
45 continue
46 }
47 if !bytes.Equal(result, test.expected) {
48 t.Errorf("ScriptBuilder.AddOp #%d (%s) wrong result\n"+
49 "got: %x\nwant: %x", i, test.name, result,
50 test.expected,
51 )
52 continue
53 }
54 }
55 // Run tests and bulk add ops via AddOps.
56 t.Logf("Running %d tests", len(tests))
57 for i, test := range tests {
58 builder.Reset()
59 result, e := builder.AddOps(test.opcodes).Script()
60 if e != nil {
61 t.Errorf("ScriptBuilder.AddOps #%d (%s) unexpected "+
62 "error: %v", i, test.name, e,
63 )
64 continue
65 }
66 if !bytes.Equal(result, test.expected) {
67 t.Errorf("ScriptBuilder.AddOps #%d (%s) wrong result\n"+
68 "got: %x\nwant: %x", i, test.name, result,
69 test.expected,
70 )
71 continue
72 }
73 }
74 }
75
76 // TestScriptBuilderAddInt64 tests that pushing signed integers to a script via the ScriptBuilder API works as expected.
77 func TestScriptBuilderAddInt64(t *testing.T) {
78 t.Parallel()
79 tests := []struct {
80 name string
81 val int64
82 expected []byte
83 }{
84 {name: "push -1", val: -1, expected: []byte{OP_1NEGATE}},
85 {name: "push small int 0", val: 0, expected: []byte{OP_0}},
86 {name: "push small int 1", val: 1, expected: []byte{OP_1}},
87 {name: "push small int 2", val: 2, expected: []byte{OP_2}},
88 {name: "push small int 3", val: 3, expected: []byte{OP_3}},
89 {name: "push small int 4", val: 4, expected: []byte{OP_4}},
90 {name: "push small int 5", val: 5, expected: []byte{OP_5}},
91 {name: "push small int 6", val: 6, expected: []byte{OP_6}},
92 {name: "push small int 7", val: 7, expected: []byte{OP_7}},
93 {name: "push small int 8", val: 8, expected: []byte{OP_8}},
94 {name: "push small int 9", val: 9, expected: []byte{OP_9}},
95 {name: "push small int 10", val: 10, expected: []byte{OP_10}},
96 {name: "push small int 11", val: 11, expected: []byte{OP_11}},
97 {name: "push small int 12", val: 12, expected: []byte{OP_12}},
98 {name: "push small int 13", val: 13, expected: []byte{OP_13}},
99 {name: "push small int 14", val: 14, expected: []byte{OP_14}},
100 {name: "push small int 15", val: 15, expected: []byte{OP_15}},
101 {name: "push small int 16", val: 16, expected: []byte{OP_16}},
102 {name: "push 17", val: 17, expected: []byte{OP_DATA_1, 0x11}},
103 {name: "push 65", val: 65, expected: []byte{OP_DATA_1, 0x41}},
104 {name: "push 127", val: 127, expected: []byte{OP_DATA_1, 0x7f}},
105 {name: "push 128", val: 128, expected: []byte{OP_DATA_2, 0x80, 0}},
106 {name: "push 255", val: 255, expected: []byte{OP_DATA_2, 0xff, 0}},
107 {name: "push 256", val: 256, expected: []byte{OP_DATA_2, 0, 0x01}},
108 {name: "push 32767", val: 32767, expected: []byte{OP_DATA_2, 0xff, 0x7f}},
109 {name: "push 32768", val: 32768, expected: []byte{OP_DATA_3, 0, 0x80, 0}},
110 {name: "push -2", val: -2, expected: []byte{OP_DATA_1, 0x82}},
111 {name: "push -3", val: -3, expected: []byte{OP_DATA_1, 0x83}},
112 {name: "push -4", val: -4, expected: []byte{OP_DATA_1, 0x84}},
113 {name: "push -5", val: -5, expected: []byte{OP_DATA_1, 0x85}},
114 {name: "push -17", val: -17, expected: []byte{OP_DATA_1, 0x91}},
115 {name: "push -65", val: -65, expected: []byte{OP_DATA_1, 0xc1}},
116 {name: "push -127", val: -127, expected: []byte{OP_DATA_1, 0xff}},
117 {name: "push -128", val: -128, expected: []byte{OP_DATA_2, 0x80, 0x80}},
118 {name: "push -255", val: -255, expected: []byte{OP_DATA_2, 0xff, 0x80}},
119 {name: "push -256", val: -256, expected: []byte{OP_DATA_2, 0x00, 0x81}},
120 {name: "push -32767", val: -32767, expected: []byte{OP_DATA_2, 0xff, 0xff}},
121 {name: "push -32768", val: -32768, expected: []byte{OP_DATA_3, 0x00, 0x80, 0x80}},
122 }
123 builder := NewScriptBuilder()
124 t.Logf("Running %d tests", len(tests))
125 for i, test := range tests {
126 builder.Reset().AddInt64(test.val)
127 result, e := builder.Script()
128 if e != nil {
129 t.Errorf("ScriptBuilder.AddInt64 #%d (%s) unexpected "+
130 "error: %v", i, test.name, e,
131 )
132 continue
133 }
134 if !bytes.Equal(result, test.expected) {
135 t.Errorf("ScriptBuilder.AddInt64 #%d (%s) wrong result\n"+
136 "got: %x\nwant: %x", i, test.name, result,
137 test.expected,
138 )
139 continue
140 }
141 }
142 }
143
144 // TestScriptBuilderAddData tests that pushing data to a script via the ScriptBuilder API works as expected and conforms
145 // to BIP0062.
146 func TestScriptBuilderAddData(t *testing.T) {
147 t.Parallel()
148 tests := []struct {
149 name string
150 data []byte
151 expected []byte
152 useFull bool // use AddFullData instead of AddData.
153 }{
154 // BIP0062: Pushing an empty byte sequence must use OP_0.
155 {name: "push empty byte sequence", data: nil, expected: []byte{OP_0}},
156 {name: "push 1 byte 0x00", data: []byte{0x00}, expected: []byte{OP_0}},
157 // BIP0062: Pushing a 1-byte sequence of byte 0x01 through 0x10 must use OP_n.
158 {name: "push 1 byte 0x01", data: []byte{0x01}, expected: []byte{OP_1}},
159 {name: "push 1 byte 0x02", data: []byte{0x02}, expected: []byte{OP_2}},
160 {name: "push 1 byte 0x03", data: []byte{0x03}, expected: []byte{OP_3}},
161 {name: "push 1 byte 0x04", data: []byte{0x04}, expected: []byte{OP_4}},
162 {name: "push 1 byte 0x05", data: []byte{0x05}, expected: []byte{OP_5}},
163 {name: "push 1 byte 0x06", data: []byte{0x06}, expected: []byte{OP_6}},
164 {name: "push 1 byte 0x07", data: []byte{0x07}, expected: []byte{OP_7}},
165 {name: "push 1 byte 0x08", data: []byte{0x08}, expected: []byte{OP_8}},
166 {name: "push 1 byte 0x09", data: []byte{0x09}, expected: []byte{OP_9}},
167 {name: "push 1 byte 0x0a", data: []byte{0x0a}, expected: []byte{OP_10}},
168 {name: "push 1 byte 0x0b", data: []byte{0x0b}, expected: []byte{OP_11}},
169 {name: "push 1 byte 0x0c", data: []byte{0x0c}, expected: []byte{OP_12}},
170 {name: "push 1 byte 0x0d", data: []byte{0x0d}, expected: []byte{OP_13}},
171 {name: "push 1 byte 0x0e", data: []byte{0x0e}, expected: []byte{OP_14}},
172 {name: "push 1 byte 0x0f", data: []byte{0x0f}, expected: []byte{OP_15}},
173 {name: "push 1 byte 0x10", data: []byte{0x10}, expected: []byte{OP_16}},
174 // BIP0062: Pushing the byte 0x81 must use OP_1NEGATE.
175 {name: "push 1 byte 0x81", data: []byte{0x81}, expected: []byte{OP_1NEGATE}},
176 // BIP0062: Pushing any other byte sequence up to 75 bytes must use the normal data push (opcode byte n, with n
177 // the number of bytes, followed n bytes of data being pushed).
178 {name: "push 1 byte 0x11", data: []byte{0x11}, expected: []byte{OP_DATA_1, 0x11}},
179 {name: "push 1 byte 0x80", data: []byte{0x80}, expected: []byte{OP_DATA_1, 0x80}},
180 {name: "push 1 byte 0x82", data: []byte{0x82}, expected: []byte{OP_DATA_1, 0x82}},
181 {name: "push 1 byte 0xff", data: []byte{0xff}, expected: []byte{OP_DATA_1, 0xff}},
182 {
183 name: "push data len 17",
184 data: bytes.Repeat([]byte{0x49}, 17),
185 expected: append([]byte{OP_DATA_17}, bytes.Repeat([]byte{0x49}, 17)...),
186 },
187 {
188 name: "push data len 75",
189 data: bytes.Repeat([]byte{0x49}, 75),
190 expected: append([]byte{OP_DATA_75}, bytes.Repeat([]byte{0x49}, 75)...),
191 },
192 // BIP0062: Pushing 76 to 255 bytes must use OP_PUSHDATA1.
193 {
194 name: "push data len 76",
195 data: bytes.Repeat([]byte{0x49}, 76),
196 expected: append([]byte{OP_PUSHDATA1, 76}, bytes.Repeat([]byte{0x49}, 76)...),
197 },
198 {
199 name: "push data len 255",
200 data: bytes.Repeat([]byte{0x49}, 255),
201 expected: append([]byte{OP_PUSHDATA1, 255}, bytes.Repeat([]byte{0x49}, 255)...),
202 },
203 // BIP0062: Pushing 256 to 520 bytes must use OP_PUSHDATA2.
204 {
205 name: "push data len 256",
206 data: bytes.Repeat([]byte{0x49}, 256),
207 expected: append([]byte{OP_PUSHDATA2, 0, 1}, bytes.Repeat([]byte{0x49}, 256)...),
208 },
209 {
210 name: "push data len 520",
211 data: bytes.Repeat([]byte{0x49}, 520),
212 expected: append([]byte{OP_PUSHDATA2, 0x08, 0x02}, bytes.Repeat([]byte{0x49}, 520)...),
213 },
214 // BIP0062: OP_PUSHDATA4 can never be used, as pushes over 520 bytes are not allowed, and those below can be
215 // done using other operators.
216 {
217 name: "push data len 521",
218 data: bytes.Repeat([]byte{0x49}, 521),
219 expected: nil,
220 },
221 {
222 name: "push data len 32767 (canonical)",
223 data: bytes.Repeat([]byte{0x49}, 32767),
224 expected: nil,
225 },
226 {
227 name: "push data len 65536 (canonical)",
228 data: bytes.Repeat([]byte{0x49}, 65536),
229 expected: nil,
230 },
231 // Additional tests for the PushFullData function that intentionally allows data pushes to exceed the limit for
232 // regression testing purposes. 3-byte data push via OP_PUSHDATA_2.
233 {
234 name: "push data len 32767 (non-canonical)",
235 data: bytes.Repeat([]byte{0x49}, 32767),
236 expected: append([]byte{OP_PUSHDATA2, 255, 127}, bytes.Repeat([]byte{0x49}, 32767)...),
237 useFull: true,
238 },
239 // 5-byte data push via OP_PUSHDATA_4.
240 {
241 name: "push data len 65536 (non-canonical)",
242 data: bytes.Repeat([]byte{0x49}, 65536),
243 expected: append([]byte{OP_PUSHDATA4, 0, 0, 1, 0}, bytes.Repeat([]byte{0x49}, 65536)...),
244 useFull: true,
245 },
246 }
247 builder := NewScriptBuilder()
248 t.Logf("Running %d tests", len(tests))
249 for i, test := range tests {
250 if !test.useFull {
251 builder.Reset().AddData(test.data)
252 } else {
253 builder.Reset().AddFullData(test.data)
254 }
255 result, _ := builder.Script()
256 if !bytes.Equal(result, test.expected) {
257 t.Errorf("ScriptBuilder.AddData #%d (%s) wrong result\n"+
258 "got: %x\nwant: %x", i, test.name, result,
259 test.expected,
260 )
261 continue
262 }
263 }
264 }
265
266 // TestExceedMaxScriptSize ensures that all of the functions that can be used to add data to a script don't allow the
267 // script to exceed the max allowed size.
268 func TestExceedMaxScriptSize(t *testing.T) {
269 t.Parallel()
270 // Start off by constructing a max size script.
271 builder := NewScriptBuilder()
272 builder.Reset().AddFullData(make([]byte, MaxScriptSize-3))
273 origScript, e := builder.Script()
274 if e != nil {
275 t.Fatalf("Unexpected error for max size script: %v", e)
276 }
277 // Ensure adding data that would exceed the maximum size of the script does not add the data.
278 script, e := builder.AddData([]byte{0x00}).Script()
279 if _, ok := e.(ErrScriptNotCanonical); !ok || e == nil {
280 t.Fatalf("ScriptBuilder.AddData allowed exceeding max script "+
281 "size: %v", len(script),
282 )
283 }
284 if !bytes.Equal(script, origScript) {
285 t.Fatalf("ScriptBuilder.AddData unexpected modified script - "+
286 "got len %d, want len %d", len(script), len(origScript),
287 )
288 }
289 // Ensure adding an opcode that would exceed the maximum size of the script does not add the data.
290 builder.Reset().AddFullData(make([]byte, MaxScriptSize-3))
291 script, e = builder.AddOp(OP_0).Script()
292 if _, ok := e.(ErrScriptNotCanonical); !ok || e == nil {
293 t.Fatalf("ScriptBuilder.AddOp unexpected modified script - "+
294 "got len %d, want len %d", len(script), len(origScript),
295 )
296 }
297 if !bytes.Equal(script, origScript) {
298 t.Fatalf("ScriptBuilder.AddOp unexpected modified script - "+
299 "got len %d, want len %d", len(script), len(origScript),
300 )
301 }
302 // Ensure adding an integer that would exceed the maximum size of the script does not add the data.
303 builder.Reset().AddFullData(make([]byte, MaxScriptSize-3))
304 script, e = builder.AddInt64(0).Script()
305 if _, ok := e.(ErrScriptNotCanonical); !ok || e == nil {
306 t.Fatalf("ScriptBuilder.AddInt64 unexpected modified script - "+
307 "got len %d, want len %d", len(script), len(origScript),
308 )
309 }
310 if !bytes.Equal(script, origScript) {
311 t.Fatalf("ScriptBuilder.AddInt64 unexpected modified script - "+
312 "got len %d, want len %d", len(script), len(origScript),
313 )
314 }
315 }
316
317 // TestErroredScript ensures that all of the functions that can be used to add data to a script don't modify the script
318 // once an error has happened.
319 func TestErroredScript(t *testing.T) {
320 t.Parallel()
321 // Start off by constructing a near max size script that has enough space left to add each data type without an
322 // error and force an initial error condition.
323 builder := NewScriptBuilder()
324 builder.Reset().AddFullData(make([]byte, MaxScriptSize-8))
325 origScript, e := builder.Script()
326 if e != nil {
327 t.Fatalf("ScriptBuilder.AddFullData unexpected error: %v", e)
328 }
329 script, e := builder.AddData([]byte{0x00, 0x00, 0x00, 0x00, 0x00}).Script()
330 if _, ok := e.(ErrScriptNotCanonical); !ok || e == nil {
331 t.Fatalf("ScriptBuilder.AddData allowed exceeding max script "+
332 "size: %v", len(script),
333 )
334 }
335 if !bytes.Equal(script, origScript) {
336 t.Fatalf("ScriptBuilder.AddData unexpected modified script - "+
337 "got len %d, want len %d", len(script), len(origScript),
338 )
339 }
340 // Ensure adding data, even using the non-canonical path, to a script that has errored doesn't succeed.
341 script, e = builder.AddFullData([]byte{0x00}).Script()
342 if _, ok := e.(ErrScriptNotCanonical); !ok || e == nil {
343 t.Fatal("ScriptBuilder.AddFullData succeeded on errored script")
344 }
345 if !bytes.Equal(script, origScript) {
346 t.Fatalf("ScriptBuilder.AddFullData unexpected modified "+
347 "script - got len %d, want len %d", len(script),
348 len(origScript),
349 )
350 }
351 // Ensure adding data to a script that has errored doesn't succeed.
352 script, e = builder.AddData([]byte{0x00}).Script()
353 if _, ok := e.(ErrScriptNotCanonical); !ok || e == nil {
354 t.Fatal("ScriptBuilder.AddData succeeded on errored script")
355 }
356 if !bytes.Equal(script, origScript) {
357 t.Fatalf("ScriptBuilder.AddData unexpected modified "+
358 "script - got len %d, want len %d", len(script),
359 len(origScript),
360 )
361 }
362 // Ensure adding an opcode to a script that has errored doesn't succeed.
363 script, e = builder.AddOp(OP_0).Script()
364 if _, ok := e.(ErrScriptNotCanonical); !ok || e == nil {
365 t.Fatal("ScriptBuilder.AddOp succeeded on errored script")
366 }
367 if !bytes.Equal(script, origScript) {
368 t.Fatalf("ScriptBuilder.AddOp unexpected modified script - "+
369 "got len %d, want len %d", len(script), len(origScript),
370 )
371 }
372 // Ensure adding an integer to a script that has errored doesn't succeed.
373 script, e = builder.AddInt64(0).Script()
374 if _, ok := e.(ErrScriptNotCanonical); !ok || e == nil {
375 t.Fatal("ScriptBuilder.AddInt64 succeeded on errored script")
376 }
377 if !bytes.Equal(script, origScript) {
378 t.Fatalf("ScriptBuilder.AddInt64 unexpected modified script - "+
379 "got len %d, want len %d", len(script), len(origScript),
380 )
381 }
382 // Ensure the error has a message set.
383 if e.Error() == "" {
384 t.Fatal("ErrScriptNotCanonical.ScriptError does not have any text")
385 }
386 }
387