11 "github.com/bytom/errors"
12 "github.com/bytom/testutil"
15 type tracebuf struct {
19 func (t tracebuf) dump() {
20 os.Stdout.Write(t.Bytes())
23 // Programs that run without error.
24 func TestProgramOK(t *testing.T) {
28 // Programs that return an ErrFalseVMResult.
29 func TestProgramNotOK(t *testing.T) {
33 func doOKNotOK(t *testing.T, expectOK bool) {
41 {"INVERT 0xfef0 EQUAL", [][]byte{{0x01, 0x0f}}},
43 {"AND 0x02 EQUAL", [][]byte{{0x03}, {0x06}}},
44 {"AND 0x02 EQUAL", [][]byte{{0x03, 0xff}, {0x06}}},
46 {"OR 0x07 EQUAL", [][]byte{{0x03}, {0x06}}},
47 {"OR 0x07ff EQUAL", [][]byte{{0x03, 0xff}, {0x06}}},
49 {"XOR 0x05 EQUAL", [][]byte{{0x03}, {0x06}}},
50 {"XOR 0x05ff EQUAL", [][]byte{{0x03, 0xff}, {0x06}}},
52 // numeric and logical ops
53 {"1ADD 2 NUMEQUAL", [][]byte{Int64Bytes(1)}},
54 {"1ADD 0 NUMEQUAL", [][]byte{Int64Bytes(-1)}},
56 {"1SUB 1 NUMEQUAL", [][]byte{Int64Bytes(2)}},
57 {"1SUB -1 NUMEQUAL", [][]byte{Int64Bytes(0)}},
59 {"2MUL 2 NUMEQUAL", [][]byte{Int64Bytes(1)}},
60 {"2MUL 0 NUMEQUAL", [][]byte{Int64Bytes(0)}},
61 {"2MUL -2 NUMEQUAL", [][]byte{Int64Bytes(-1)}},
63 {"2DIV 1 NUMEQUAL", [][]byte{Int64Bytes(2)}},
64 {"2DIV 0 NUMEQUAL", [][]byte{Int64Bytes(1)}},
65 {"2DIV 0 NUMEQUAL", [][]byte{Int64Bytes(0)}},
66 {"2DIV -1 NUMEQUAL", [][]byte{Int64Bytes(-1)}},
67 {"2DIV -1 NUMEQUAL", [][]byte{Int64Bytes(-2)}},
69 {"NEGATE -1 NUMEQUAL", [][]byte{Int64Bytes(1)}},
70 {"NEGATE 1 NUMEQUAL", [][]byte{Int64Bytes(-1)}},
71 {"NEGATE 0 NUMEQUAL", [][]byte{Int64Bytes(0)}},
73 {"ABS 1 NUMEQUAL", [][]byte{Int64Bytes(1)}},
74 {"ABS 1 NUMEQUAL", [][]byte{Int64Bytes(-1)}},
75 {"ABS 0 NUMEQUAL", [][]byte{Int64Bytes(0)}},
77 {"0NOTEQUAL", [][]byte{Int64Bytes(1)}},
78 {"0NOTEQUAL NOT", [][]byte{Int64Bytes(0)}},
80 {"ADD 5 NUMEQUAL", [][]byte{Int64Bytes(2), Int64Bytes(3)}},
82 {"SUB 2 NUMEQUAL", [][]byte{Int64Bytes(5), Int64Bytes(3)}},
84 {"MUL 6 NUMEQUAL", [][]byte{Int64Bytes(2), Int64Bytes(3)}},
86 {"DIV 2 NUMEQUAL", [][]byte{Int64Bytes(6), Int64Bytes(3)}},
88 {"MOD 0 NUMEQUAL", [][]byte{Int64Bytes(6), Int64Bytes(2)}},
89 {"MOD 0 NUMEQUAL", [][]byte{Int64Bytes(-6), Int64Bytes(2)}},
90 {"MOD 0 NUMEQUAL", [][]byte{Int64Bytes(6), Int64Bytes(-2)}},
91 {"MOD 0 NUMEQUAL", [][]byte{Int64Bytes(-6), Int64Bytes(-2)}},
92 {"MOD 2 NUMEQUAL", [][]byte{Int64Bytes(12), Int64Bytes(10)}},
93 {"MOD 8 NUMEQUAL", [][]byte{Int64Bytes(-12), Int64Bytes(10)}},
94 {"MOD -8 NUMEQUAL", [][]byte{Int64Bytes(12), Int64Bytes(-10)}},
95 {"MOD -2 NUMEQUAL", [][]byte{Int64Bytes(-12), Int64Bytes(-10)}},
97 {"LSHIFT 2 NUMEQUAL", [][]byte{Int64Bytes(1), Int64Bytes(1)}},
98 {"LSHIFT 4 NUMEQUAL", [][]byte{Int64Bytes(1), Int64Bytes(2)}},
99 {"LSHIFT -2 NUMEQUAL", [][]byte{Int64Bytes(-1), Int64Bytes(1)}},
100 {"LSHIFT -4 NUMEQUAL", [][]byte{Int64Bytes(-1), Int64Bytes(2)}},
102 {"1 1 BOOLAND", nil},
103 {"1 0 BOOLAND NOT", nil},
104 {"0 1 BOOLAND NOT", nil},
105 {"0 0 BOOLAND NOT", nil},
110 {"0 0 BOOLOR NOT", nil},
112 {"1 2 OR 3 EQUAL", nil},
115 {"0 CATPUSHDATA 0x0000 EQUAL", [][]byte{{0x00}}},
116 {"0 0xff CATPUSHDATA 0x01ff EQUAL", nil},
117 {"CATPUSHDATA 0x050105 EQUAL", [][]byte{{0x05}, {0x05}}},
118 {"CATPUSHDATA 0xff01ff EQUAL", [][]byte{{0xff}, {0xff}}},
119 {"0 0xcccccc CATPUSHDATA 0x03cccccc EQUAL", nil},
120 {"0x05 0x05 SWAP 0xdeadbeef CATPUSHDATA DROP 0x05 EQUAL", nil},
121 {"0x05 0x05 SWAP 0xdeadbeef CATPUSHDATA DROP 0x05 EQUAL", nil},
123 // // control flow ops
124 {"1 JUMP:7 0 1 EQUAL", nil}, // jumps over 0
125 {"1 JUMP:$target 0 $target 1 EQUAL", nil}, // jumps over 0
126 {"1 1 JUMPIF:8 0 1 EQUAL", nil}, // jumps over 0
127 {"1 1 JUMPIF:$target 0 $target 1 EQUAL", nil}, // jumps over 0
128 {"1 0 JUMPIF:8 0 1 EQUAL NOT", nil}, // doesn't jump over 0
129 {"1 0 JUMPIF:$target 0 $target 1 EQUAL NOT", nil}, // doesn't jump over 0
130 {"1 0 JUMPIF:1", nil}, // doesn't jump, so no infinite loop
131 {"1 $target 0 JUMPIF:$target", nil}, // doesn't jump, so no infinite loop
132 {"4 1 JUMPIF:14 5 EQUAL JUMP:16 4 EQUAL", nil}, // if (true) { return x == 4; } else { return x == 5; }
133 {"4 1 JUMPIF:$true 5 EQUAL JUMP:$end $true 4 EQUAL $end", nil}, // if (true) { return x == 4; } else { return x == 5; }
134 {"5 0 JUMPIF:14 5 EQUAL JUMP:16 4 EQUAL", nil}, // if (false) { return x == 4; } else { return x == 5; }
135 {"5 0 JUMPIF:$true 5 EQUAL JUMP:$end $true 4 $test EQUAL $end", nil}, // if (false) { return x == 4; } else { return x == 5; }
136 {"0 1 2 3 4 5 6 JUMP:13 DROP DUP 0 NUMNOTEQUAL JUMPIF:12 1", nil}, // same as "0 1 2 3 4 5 6 WHILE DROP ENDWHILE 1"
137 {"0 1 2 3 4 5 6 JUMP:$dup $drop DROP $dup DUP 0 NUMNOTEQUAL JUMPIF:$drop 1", nil}, // same as "0 1 2 3 4 5 6 WHILE DROP ENDWHILE 1"
138 {"0 JUMP:7 1ADD DUP 10 LESSTHAN JUMPIF:6 10 NUMEQUAL", nil}, // fixed version of "0 1 WHILE DROP 1ADD DUP 10 LESSTHAN ENDWHILE 10 NUMEQUAL"
139 {"0 JUMP:$dup $add 1ADD $dup DUP 10 LESSTHAN JUMPIF:$add 10 NUMEQUAL", nil}, // fixed version of "0 1 WHILE DROP 1ADD DUP 10 LESSTHAN ENDWHILE 10 NUMEQUAL"
142 for i, c := range cases {
147 prog, err := Assemble(progSrc)
151 fmt.Printf("* case %d, prog [%s] [%x]\n", i, progSrc, prog)
152 trace := new(tracebuf)
154 vm := &virtualMachine{
156 runLimit: int64(10000),
157 dataStack: append([][]byte{}, c.args...),
160 if err == nil && vm.falseResult() {
161 err = ErrFalseVMResult
163 if expectOK && err != nil {
165 t.Errorf("case %d [%s]: expected success, got error %s", i, progSrc, err)
166 } else if !expectOK && err != ErrFalseVMResult {
168 t.Errorf("case %d [%s]: expected ErrFalseVMResult, got %s", i, progSrc, err)
173 func TestVerifyTxInput(t *testing.T) {
182 Code: []byte{byte(OP_ADD), byte(OP_5), byte(OP_NUMEQUAL)},
183 Arguments: [][]byte{{2}, {3}},
188 vctx: &Context{VMVersion: 2},
189 wantErr: ErrUnsupportedVM,
195 Code: []byte{byte(OP_ADD), byte(OP_5), byte(OP_NUMEQUAL)},
196 Arguments: [][]byte{make([]byte, 50001)},
198 wantErr: ErrRunLimitExceeded,
203 for _, c := range cases {
204 gasLeft, gotErr := Verify(c.vctx, 10000)
205 if errors.Root(gotErr) != c.wantErr {
206 t.Errorf("VerifyTxInput(%+v) err = %v want %v", c.vctx, gotErr, c.wantErr)
208 if gasLeft != c.gasLeft {
209 t.Errorf("VerifyTxInput(%+v) err = gasLeft doesn't match", c.vctx)
214 func TestRun(t *testing.T) {
219 vm: &virtualMachine{runLimit: 50000, program: []byte{byte(OP_TRUE)}},
221 vm: &virtualMachine{runLimit: 50000, program: []byte{byte(OP_ADD)}},
222 wantErr: ErrDataStackUnderflow,
225 for i, c := range cases {
228 if gotErr != c.wantErr {
229 t.Errorf("run test %d: got err = %v want %v", i, gotErr, c.wantErr)
233 if c.wantErr != nil {
239 func TestStep(t *testing.T) {
240 txVMContext := &Context{DestPos: new(uint64)}
242 startVM *virtualMachine
243 wantVM *virtualMachine
246 startVM: &virtualMachine{
247 program: []byte{byte(OP_TRUE)},
250 wantVM: &virtualMachine{
251 program: []byte{byte(OP_TRUE)},
253 dataStack: [][]byte{{1}},
259 startVM: &virtualMachine{
260 program: []byte{byte(OP_TRUE), byte(OP_JUMP), byte(0xff), byte(0x00), byte(0x00), byte(0x00)},
262 dataStack: [][]byte{},
265 wantVM: &virtualMachine{
266 program: []byte{byte(OP_TRUE), byte(OP_JUMP), byte(0xff), byte(0x00), byte(0x00), byte(0x00)},
268 dataStack: [][]byte{},
269 data: []byte{byte(0xff), byte(0x00), byte(0x00), byte(0x00)},
275 startVM: &virtualMachine{
276 program: []byte{byte(OP_TRUE), byte(OP_JUMPIF), byte(0x00), byte(0x00), byte(0x00), byte(0x00)},
278 dataStack: [][]byte{{1}},
281 wantVM: &virtualMachine{
282 program: []byte{byte(OP_TRUE), byte(OP_JUMPIF), byte(0x00), byte(0x00), byte(0x00), byte(0x00)},
284 dataStack: [][]byte{},
287 data: []byte{byte(0x00), byte(0x00), byte(0x00), byte(0x00)},
291 startVM: &virtualMachine{
292 program: []byte{byte(OP_FALSE), byte(OP_JUMPIF), byte(0x00), byte(0x00), byte(0x00), byte(0x00)},
294 dataStack: [][]byte{{}},
297 wantVM: &virtualMachine{
298 program: []byte{byte(OP_FALSE), byte(OP_JUMPIF), byte(0x00), byte(0x00), byte(0x00), byte(0x00)},
300 dataStack: [][]byte{},
303 data: []byte{byte(0x00), byte(0x00), byte(0x00), byte(0x00)},
307 startVM: &virtualMachine{
308 program: []byte{255},
310 dataStack: [][]byte{},
312 wantVM: &virtualMachine{
313 program: []byte{255},
317 dataStack: [][]byte{},
320 startVM: &virtualMachine{
321 program: []byte{byte(OP_ADD)},
324 wantErr: ErrDataStackUnderflow,
326 startVM: &virtualMachine{
327 program: []byte{byte(OP_INDEX)},
329 context: txVMContext,
331 wantErr: ErrRunLimitExceeded,
333 startVM: &virtualMachine{
334 program: []byte{255},
336 expansionReserved: true,
338 wantErr: ErrDisallowedOpcode,
340 startVM: &virtualMachine{
341 program: []byte{255},
344 wantVM: &virtualMachine{
345 program: []byte{255},
352 for i, c := range cases {
353 gotErr := c.startVM.step()
356 if gotErr != c.wantErr {
357 t.Errorf("step test %d: got err = %v want %v", i, gotErr, c.wantErr)
361 if c.wantErr != nil {
365 if !testutil.DeepEqual(gotVM, c.wantVM) {
366 t.Errorf("step test %d:\n\tgot vm: %+v\n\twant vm: %+v", i, gotVM, c.wantVM)
371 func decompile(prog []byte) string {
373 for i := uint32(0); i < uint32(len(prog)); { // update i inside the loop
374 inst, err := ParseOp(prog, i)
376 strs = append(strs, fmt.Sprintf("<%x>", prog[i]))
381 if len(inst.Data) > 0 {
382 str = fmt.Sprintf("0x%x", inst.Data)
384 str = inst.Op.String()
386 strs = append(strs, str)
389 return strings.Join(strs, " ")
392 func TestVerifyTxInputQuickCheck(t *testing.T) {
393 f := func(program []byte, witnesses [][]byte) (ok bool) {
395 if err := recover(); err != nil {
396 t.Log(decompile(program))
397 for i := range witnesses {
398 t.Logf("witness %d: %x\n", i, witnesses[i])
408 Arguments: witnesses,
414 if err := quick.Check(f, nil); err != nil {
419 func TestVerifyBlockHeaderQuickCheck(t *testing.T) {
420 f := func(program []byte, witnesses [][]byte) (ok bool) {
422 if err := recover(); err != nil {
423 t.Log(decompile(program))
424 for i := range witnesses {
425 t.Logf("witness %d: %x\n", i, witnesses[i])
434 Arguments: witnesses,
436 Verify(context, 10000)
439 if err := quick.Check(f, nil); err != nil {