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(initialRunLimit),
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) {
181 Code: []byte{byte(OP_ADD), byte(OP_5), byte(OP_NUMEQUAL)},
182 Arguments: [][]byte{{2}, {3}},
186 vctx: &Context{VMVersion: 2},
187 wantErr: ErrUnsupportedVM,
192 Code: []byte{byte(OP_ADD), byte(OP_5), byte(OP_NUMEQUAL)},
193 Arguments: [][]byte{make([]byte, 50001)},
195 wantErr: ErrRunLimitExceeded,
199 for _, c := range cases {
200 gotErr := Verify(c.vctx)
201 if errors.Root(gotErr) != c.wantErr {
202 t.Errorf("VerifyTxInput(%+v) err = %v want %v", c.vctx, gotErr, c.wantErr)
207 func TestVerifyBlockHeader(t *testing.T) {
208 consensusProg := []byte{byte(OP_ADD), byte(OP_5), byte(OP_NUMEQUAL)}
212 Arguments: [][]byte{{2}, {3}},
214 gotErr := Verify(context)
216 t.Errorf("unexpected error: %v", gotErr)
221 Arguments: [][]byte{make([]byte, 50000)},
223 gotErr = Verify(context)
224 if errors.Root(gotErr) != ErrRunLimitExceeded {
225 t.Error("expected block to exceed run limit")
229 func TestRun(t *testing.T) {
234 vm: &virtualMachine{runLimit: 50000, program: []byte{byte(OP_TRUE)}},
236 vm: &virtualMachine{runLimit: 50000, program: []byte{byte(OP_ADD)}},
237 wantErr: ErrDataStackUnderflow,
240 for i, c := range cases {
243 if gotErr != c.wantErr {
244 t.Errorf("run test %d: got err = %v want %v", i, gotErr, c.wantErr)
248 if c.wantErr != nil {
254 func TestStep(t *testing.T) {
255 txVMContext := &Context{DestPos: new(uint64)}
257 startVM *virtualMachine
258 wantVM *virtualMachine
261 startVM: &virtualMachine{
262 program: []byte{byte(OP_TRUE)},
265 wantVM: &virtualMachine{
266 program: []byte{byte(OP_TRUE)},
268 dataStack: [][]byte{{1}},
274 startVM: &virtualMachine{
275 program: []byte{byte(OP_TRUE), byte(OP_JUMP), byte(0xff), byte(0x00), byte(0x00), byte(0x00)},
277 dataStack: [][]byte{},
280 wantVM: &virtualMachine{
281 program: []byte{byte(OP_TRUE), byte(OP_JUMP), byte(0xff), byte(0x00), byte(0x00), byte(0x00)},
283 dataStack: [][]byte{},
284 data: []byte{byte(0xff), byte(0x00), byte(0x00), byte(0x00)},
290 startVM: &virtualMachine{
291 program: []byte{byte(OP_TRUE), byte(OP_JUMPIF), byte(0x00), byte(0x00), byte(0x00), byte(0x00)},
293 dataStack: [][]byte{{1}},
296 wantVM: &virtualMachine{
297 program: []byte{byte(OP_TRUE), byte(OP_JUMPIF), byte(0x00), byte(0x00), byte(0x00), byte(0x00)},
299 dataStack: [][]byte{},
302 data: []byte{byte(0x00), byte(0x00), byte(0x00), byte(0x00)},
306 startVM: &virtualMachine{
307 program: []byte{byte(OP_FALSE), byte(OP_JUMPIF), byte(0x00), byte(0x00), byte(0x00), byte(0x00)},
309 dataStack: [][]byte{{}},
312 wantVM: &virtualMachine{
313 program: []byte{byte(OP_FALSE), byte(OP_JUMPIF), byte(0x00), byte(0x00), byte(0x00), byte(0x00)},
315 dataStack: [][]byte{},
318 data: []byte{byte(0x00), byte(0x00), byte(0x00), byte(0x00)},
322 startVM: &virtualMachine{
323 program: []byte{255},
325 dataStack: [][]byte{},
327 wantVM: &virtualMachine{
328 program: []byte{255},
332 dataStack: [][]byte{},
335 startVM: &virtualMachine{
336 program: []byte{byte(OP_ADD)},
339 wantErr: ErrDataStackUnderflow,
341 startVM: &virtualMachine{
342 program: []byte{byte(OP_INDEX)},
344 context: txVMContext,
346 wantErr: ErrRunLimitExceeded,
348 startVM: &virtualMachine{
349 program: []byte{255},
351 expansionReserved: true,
353 wantErr: ErrDisallowedOpcode,
355 startVM: &virtualMachine{
356 program: []byte{255},
359 wantVM: &virtualMachine{
360 program: []byte{255},
367 for i, c := range cases {
368 gotErr := c.startVM.step()
371 if gotErr != c.wantErr {
372 t.Errorf("step test %d: got err = %v want %v", i, gotErr, c.wantErr)
376 if c.wantErr != nil {
380 if !testutil.DeepEqual(gotVM, c.wantVM) {
381 t.Errorf("step test %d:\n\tgot vm: %+v\n\twant vm: %+v", i, gotVM, c.wantVM)
386 func decompile(prog []byte) string {
388 for i := uint32(0); i < uint32(len(prog)); { // update i inside the loop
389 inst, err := ParseOp(prog, i)
391 strs = append(strs, fmt.Sprintf("<%x>", prog[i]))
396 if len(inst.Data) > 0 {
397 str = fmt.Sprintf("0x%x", inst.Data)
399 str = inst.Op.String()
401 strs = append(strs, str)
404 return strings.Join(strs, " ")
407 func TestVerifyTxInputQuickCheck(t *testing.T) {
408 f := func(program []byte, witnesses [][]byte) (ok bool) {
410 if err := recover(); err != nil {
411 t.Log(decompile(program))
412 for i := range witnesses {
413 t.Logf("witness %d: %x\n", i, witnesses[i])
423 Arguments: witnesses,
425 // Leaving this out reduces coverage.
426 // TODO(kr): figure out why and convert that
427 // to a normal unit test.
428 MaxTimeMS: new(uint64),
434 if err := quick.Check(f, nil); err != nil {
439 func TestVerifyBlockHeaderQuickCheck(t *testing.T) {
440 f := func(program []byte, witnesses [][]byte) (ok bool) {
442 if err := recover(); err != nil {
443 t.Log(decompile(program))
444 for i := range witnesses {
445 t.Logf("witness %d: %x\n", i, witnesses[i])
454 Arguments: witnesses,
455 BlockHash: new([]byte),
456 BlockTimeMS: new(uint64),
457 NextConsensusProgram: &[]byte{},
462 if err := quick.Check(f, nil); err != nil {