--- /dev/null
+package vm
+
+import (
+ "fmt"
+ "reflect"
+ "testing"
+
+ "github.com/bytom/errors"
+ "github.com/bytom/math/checked"
+ "github.com/bytom/testutil"
+)
+
+func TestParseOp(t *testing.T) {
+ cases := []struct {
+ prog []byte
+ pc uint32
+ want Instruction
+ wantErr error
+ }{{
+ prog: []byte{byte(OP_ADD)},
+ want: Instruction{Op: OP_ADD, Len: 1},
+ }, {
+ prog: []byte{byte(OP_16)},
+ want: Instruction{Op: OP_16, Data: []byte{16}, Len: 1},
+ }, {
+ prog: []byte{byte(OP_DATA_5), 1, 1, 1, 1, 1},
+ want: Instruction{Op: OP_DATA_5, Data: []byte{1, 1, 1, 1, 1}, Len: 6},
+ }, {
+ prog: []byte{byte(OP_DATA_5), 1, 1, 1, 1, 1, 255},
+ want: Instruction{Op: OP_DATA_5, Data: []byte{1, 1, 1, 1, 1}, Len: 6},
+ }, {
+ prog: []byte{byte(OP_PUSHDATA1), 1, 1},
+ want: Instruction{Op: OP_PUSHDATA1, Data: []byte{1}, Len: 3},
+ }, {
+ prog: []byte{byte(OP_PUSHDATA1), 1, 1, 255},
+ want: Instruction{Op: OP_PUSHDATA1, Data: []byte{1}, Len: 3},
+ }, {
+ prog: []byte{byte(OP_PUSHDATA2), 1, 0, 1},
+ want: Instruction{Op: OP_PUSHDATA2, Data: []byte{1}, Len: 4},
+ }, {
+ prog: []byte{byte(OP_PUSHDATA2), 1, 0, 1, 255},
+ want: Instruction{Op: OP_PUSHDATA2, Data: []byte{1}, Len: 4},
+ }, {
+ prog: []byte{byte(OP_PUSHDATA4), 1, 0, 0, 0, 1},
+ want: Instruction{Op: OP_PUSHDATA4, Data: []byte{1}, Len: 6},
+ }, {
+ prog: []byte{byte(OP_PUSHDATA4), 1, 0, 0, 0, 1, 255},
+ want: Instruction{Op: OP_PUSHDATA4, Data: []byte{1}, Len: 6},
+ }, {
+ prog: []byte{},
+ wantErr: ErrShortProgram,
+ }, {
+ prog: []byte{byte(OP_0)},
+ pc: 1,
+ wantErr: ErrShortProgram,
+ }, {
+ prog: []byte{byte(OP_DATA_1)},
+ wantErr: ErrShortProgram,
+ }, {
+ prog: []byte{byte(OP_PUSHDATA1)},
+ wantErr: ErrShortProgram,
+ }, {
+ prog: []byte{byte(OP_PUSHDATA1), 1},
+ wantErr: ErrShortProgram,
+ }, {
+ prog: []byte{byte(OP_PUSHDATA2)},
+ wantErr: ErrShortProgram,
+ }, {
+ prog: []byte{byte(OP_PUSHDATA2), 1, 0},
+ wantErr: ErrShortProgram,
+ }, {
+ prog: []byte{byte(OP_PUSHDATA4)},
+ wantErr: ErrShortProgram,
+ }, {
+ prog: []byte{byte(OP_PUSHDATA4), 1, 0, 0, 0},
+ wantErr: ErrShortProgram,
+ }, {
+ pc: 71,
+ prog: []byte{0x6d, 0x6b, 0xaa, 0x20, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x20, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x0, 0x0, 0x4e, 0xff, 0xff, 0xff, 0xff, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30},
+ wantErr: checked.ErrOverflow,
+ }}
+
+ for _, c := range cases {
+ t.Run(fmt.Sprintf("%d: %x", c.pc, c.prog), func(t *testing.T) {
+ got, gotErr := ParseOp(c.prog, c.pc)
+
+ if errors.Root(gotErr) != c.wantErr {
+ t.Errorf("ParseOp(%x, %d) error = %v want %v", c.prog, c.pc, gotErr, c.wantErr)
+ }
+
+ if c.wantErr != nil {
+ return
+ }
+
+ if !testutil.DeepEqual(got, c.want) {
+ t.Errorf("ParseOp(%x, %d) = %+v want %+v", c.prog, c.pc, got, c.want)
+ }
+ })
+ }
+}
+
+func TestParseProgram(t *testing.T) {
+ cases := []struct {
+ prog []byte
+ want []Instruction
+ wantErr error
+ }{
+ {
+ prog: []byte{byte(OP_2), byte(OP_3), byte(OP_ADD), byte(OP_5), byte(OP_NUMEQUAL)},
+ want: []Instruction{
+ {Op: OP_2, Data: []byte{0x02}, Len: 1},
+ {Op: OP_3, Data: []byte{0x03}, Len: 1},
+ {Op: OP_ADD, Len: 1},
+ {Op: OP_5, Data: []byte{0x05}, Len: 1},
+ {Op: OP_NUMEQUAL, Len: 1},
+ },
+ },
+ {
+ prog: []byte{255},
+ want: []Instruction{
+ {Op: 255, Len: 1},
+ },
+ },
+ }
+
+ for _, c := range cases {
+ got, gotErr := ParseProgram(c.prog)
+
+ if errors.Root(gotErr) != c.wantErr {
+ t.Errorf("ParseProgram(%x) error = %v want %v", c.prog, gotErr, c.wantErr)
+ }
+
+ if c.wantErr != nil {
+ continue
+ }
+
+ if !testutil.DeepEqual(got, c.want) {
+ t.Errorf("ParseProgram(%x) = %+v want %+v", c.prog, got, c.want)
+ }
+ }
+}
+
+func TestIsPushData(t *testing.T) {
+ cases := []struct {
+ want Instruction
+ wantErr error
+ }{
+ {
+ want: Instruction{Op: OP_16, Data: []byte{16}, Len: 1},
+ },
+ {
+ want: Instruction{Op: OP_DATA_32, Data: []byte{16}, Len: 1},
+ },
+ {
+ want: Instruction{Op: OP_FALSE, Data: []byte{}, Len: 1},
+ },
+ {
+ want: Instruction{Op: OP_TRUE, Data: []byte{1}, Len: 1},
+ },
+ {
+ want: Instruction{Op: OP_JUMP, Data: []byte{0x00000000}, Len: 1},
+ wantErr: ErrShortProgram,
+ },
+ {
+ want: Instruction{Op: OP_ADD, Data: []byte{0x12, 0x56}, Len: 2},
+ wantErr: ErrShortProgram,
+ },
+ }
+
+ for _, c := range cases {
+ if c.want.IsPushdata() {
+ t.Logf("check success")
+ } else if c.wantErr != nil {
+ t.Logf("check err success")
+ } else {
+ t.Errorf("check false: %v -- %v", reflect.ValueOf(ops[OP_1].fn), reflect.ValueOf(ops[c.want.Op].fn))
+ }
+ }
+}