--- /dev/null
+package vm
+
+import (
+ "testing"
+
+ "github.com/davecgh/go-spew/spew"
+
+ "github.com/bytom/errors"
+ "github.com/bytom/testutil"
+)
+
+func TestOutputIDAndNonceOp(t *testing.T) {
+ // arbitrary
+ outputID := mustDecodeHex("0a60f9b12950c84c221012a808ef7782823b7e16b71fe2ba01811cda96a217df")
+
+ prog := []byte{uint8(OP_OUTPUTID)}
+ vm := &virtualMachine{
+ runLimit: 50000,
+ program: prog,
+ context: &Context{SpentOutputID: &outputID},
+ }
+ err := vm.step()
+ if err != nil {
+ t.Fatal(err)
+ }
+ gotVM := vm
+
+ expectedStack := [][]byte{outputID}
+ if !testutil.DeepEqual(gotVM.dataStack, expectedStack) {
+ t.Errorf("expected stack %v, got %v; vm is:\n%s", expectedStack, gotVM.dataStack, spew.Sdump(vm))
+ }
+
+ prog = []byte{uint8(OP_OUTPUTID)}
+ vm = &virtualMachine{
+ runLimit: 50000,
+ program: prog,
+ context: &Context{SpentOutputID: nil},
+ }
+ err = vm.step()
+ if err != ErrContext {
+ t.Errorf("expected ErrContext, got %v", err)
+ }
+}
+
+func TestBlockHeight(t *testing.T) {
+ var blockHeight uint64 = 6666
+
+ prog, err := Assemble("BLOCKHEIGHT 6666 NUMEQUAL")
+ if err != nil {
+ t.Fatal(err)
+ }
+ vm := &virtualMachine{
+ runLimit: 50000,
+ program: prog,
+ context: &Context{BlockHeight: &blockHeight},
+ }
+ err = vm.run()
+ if err != nil {
+ t.Errorf("got error %s, expected none", err)
+ }
+ if vm.falseResult() {
+ t.Error("result is false, want success")
+ }
+
+ prog, err = Assemble("BLOCKHEIGHT 7777 NUMEQUAL")
+ if err != nil {
+ t.Fatal(err)
+ }
+ vm = &virtualMachine{
+ runLimit: 50000,
+ program: prog,
+ context: &Context{BlockHeight: &blockHeight},
+ }
+ err = vm.run()
+ if err == nil && vm.falseResult() {
+ err = ErrFalseVMResult
+ }
+ switch err {
+ case nil:
+ t.Error("got ok result, expected failure")
+ case ErrFalseVMResult:
+ // ok
+ default:
+ t.Errorf("got error %s, expected ErrFalseVMResult", err)
+ }
+}
+
+func TestIntrospectionOps(t *testing.T) {
+ // arbitrary
+ entryID := mustDecodeHex("2e68d78cdeaa98944c12512cf9c719eb4881e9afb61e4b766df5f369aee6392c")
+ assetID := mustDecodeHex("0100000000000000000000000000000000000000000000000000000000000000")
+
+ type testStruct struct {
+ op Op
+ startVM *virtualMachine
+ wantErr error
+ wantVM *virtualMachine
+ }
+ cases := []testStruct{{
+ op: OP_CHECKOUTPUT,
+ startVM: &virtualMachine{
+ dataStack: [][]byte{
+ {0},
+ {1},
+ append([]byte{9}, make([]byte, 31)...),
+ {1},
+ []byte("missingprog"),
+ },
+ context: &Context{
+ CheckOutput: func(uint64, uint64, []byte, uint64, []byte, bool) (bool, error) {
+ return false, nil
+ },
+ },
+ },
+ wantVM: &virtualMachine{
+ runLimit: 50062,
+ deferredCost: -78,
+ dataStack: [][]byte{{}},
+ },
+ }, {
+ op: OP_CHECKOUTPUT,
+ startVM: &virtualMachine{
+ dataStack: [][]byte{
+ {4},
+ mustDecodeHex("1f2a05f881ed9fa0c9068a84823677409f863891a2196eb55dbfbb677a566374"),
+ {7},
+ append([]byte{2}, make([]byte, 31)...),
+ Int64Bytes(-1),
+ []byte("controlprog"),
+ },
+ context: &Context{},
+ },
+ wantErr: ErrBadValue,
+ }, {
+ op: OP_CHECKOUTPUT,
+ startVM: &virtualMachine{
+ dataStack: [][]byte{
+ {4},
+ mustDecodeHex("1f2a05f881ed9fa0c9068a84823677409f863891a2196eb55dbfbb677a566374"),
+ Int64Bytes(-1),
+ append([]byte{2}, make([]byte, 31)...),
+ {1},
+ []byte("controlprog"),
+ },
+ context: &Context{},
+ },
+ wantErr: ErrBadValue,
+ }, {
+ op: OP_CHECKOUTPUT,
+ startVM: &virtualMachine{
+ dataStack: [][]byte{
+ Int64Bytes(-1),
+ mustDecodeHex("1f2a05f881ed9fa0c9068a84823677409f863891a2196eb55dbfbb677a566374"),
+ {7},
+ append([]byte{2}, make([]byte, 31)...),
+ {1},
+ []byte("controlprog"),
+ },
+ context: &Context{},
+ },
+ wantErr: ErrBadValue,
+ }, {
+ op: OP_CHECKOUTPUT,
+ startVM: &virtualMachine{
+ dataStack: [][]byte{
+ {5},
+ mustDecodeHex("1f2a05f881ed9fa0c9068a84823677409f863891a2196eb55dbfbb677a566374"),
+ {7},
+ append([]byte{2}, make([]byte, 31)...),
+ {1},
+ []byte("controlprog"),
+ },
+ context: &Context{
+ CheckOutput: func(uint64, uint64, []byte, uint64, []byte, bool) (bool, error) {
+ return false, ErrBadValue
+ },
+ },
+ },
+ wantErr: ErrBadValue,
+ }, {
+ op: OP_CHECKOUTPUT,
+ startVM: &virtualMachine{
+ runLimit: 0,
+ dataStack: [][]byte{
+ {4},
+ mustDecodeHex("1f2a05f881ed9fa0c9068a84823677409f863891a2196eb55dbfbb677a566374"),
+ {7},
+ append([]byte{2}, make([]byte, 31)...),
+ {1},
+ []byte("controlprog"),
+ },
+ context: &Context{},
+ },
+ wantErr: ErrRunLimitExceeded,
+ }, {
+ op: OP_ASSET,
+ startVM: &virtualMachine{
+ context: &Context{AssetID: &assetID},
+ },
+ wantVM: &virtualMachine{
+ runLimit: 49959,
+ deferredCost: 40,
+ dataStack: [][]byte{assetID},
+ },
+ }, {
+ op: OP_AMOUNT,
+ startVM: &virtualMachine{
+ context: &Context{Amount: uint64ptr(5)},
+ },
+ wantVM: &virtualMachine{
+ runLimit: 49990,
+ deferredCost: 9,
+ dataStack: [][]byte{{5}},
+ },
+ }, {
+ op: OP_PROGRAM,
+ startVM: &virtualMachine{
+ program: []byte("spendprog"),
+ context: &Context{Code: []byte("spendprog")},
+ },
+ wantVM: &virtualMachine{
+ runLimit: 49982,
+ deferredCost: 17,
+ dataStack: [][]byte{[]byte("spendprog")},
+ },
+ }, {
+ op: OP_PROGRAM,
+ startVM: &virtualMachine{
+ program: []byte("issueprog"),
+ runLimit: 50000,
+ context: &Context{Code: []byte("issueprog")},
+ },
+ wantVM: &virtualMachine{
+ runLimit: 49982,
+ deferredCost: 17,
+ dataStack: [][]byte{[]byte("issueprog")},
+ },
+ }, {
+ op: OP_INDEX,
+ startVM: &virtualMachine{
+ context: &Context{DestPos: new(uint64)},
+ },
+ wantVM: &virtualMachine{
+ runLimit: 49991,
+ deferredCost: 8,
+ dataStack: [][]byte{[]byte{}},
+ },
+ }, {
+ op: OP_ENTRYID,
+ startVM: &virtualMachine{
+ context: &Context{EntryID: entryID},
+ },
+ wantVM: &virtualMachine{
+ runLimit: 49959,
+ deferredCost: 40,
+ dataStack: [][]byte{entryID},
+ },
+ }}
+
+ txops := []Op{
+ OP_CHECKOUTPUT, OP_ASSET, OP_AMOUNT, OP_PROGRAM,
+ OP_INDEX, OP_OUTPUTID,
+ }
+
+ for _, op := range txops {
+ cases = append(cases, testStruct{
+ op: op,
+ startVM: &virtualMachine{
+ runLimit: 0,
+ context: &Context{},
+ },
+ wantErr: ErrRunLimitExceeded,
+ })
+ }
+
+ for i, c := range cases {
+ t.Logf("case %d", i)
+ prog := []byte{byte(c.op)}
+ vm := c.startVM
+ if c.wantErr != ErrRunLimitExceeded {
+ vm.runLimit = 50000
+ }
+ vm.program = prog
+ err := vm.run()
+ switch errors.Root(err) {
+ case c.wantErr:
+ // ok
+ case nil:
+ t.Errorf("case %d, op %s: got no error, want %v", i, ops[c.op].name, c.wantErr)
+ default:
+ t.Errorf("case %d, op %s: got err = %v want %v", i, ops[c.op].name, err, c.wantErr)
+ }
+ if c.wantErr != nil {
+ continue
+ }
+ gotVM := vm
+
+ c.wantVM.program = prog
+ c.wantVM.pc = 1
+ c.wantVM.nextPC = 1
+ c.wantVM.context = gotVM.context
+
+ if !testutil.DeepEqual(gotVM, c.wantVM) {
+ t.Errorf("case %d, op %s: unexpected vm result\n\tgot: %+v\n\twant: %+v\nstartVM is:\n%s", i, ops[c.op].name, gotVM, c.wantVM, spew.Sdump(c.startVM))
+ }
+ }
+}
+
+func uint64ptr(n uint64) *uint64 { return &n }