package vm import ( "fmt" "math" "testing" "github.com/bytom/vapor/testutil" ) func TestNumericOps(t *testing.T) { type testStruct struct { op Op startVM *virtualMachine wantErr error wantVM *virtualMachine } cases := []testStruct{{ op: OP_1ADD, startVM: &virtualMachine{ runLimit: 50000, dataStack: [][]byte{{2}}, }, wantVM: &virtualMachine{ runLimit: 49998, dataStack: [][]byte{{3}}, }, }, { op: OP_1SUB, startVM: &virtualMachine{ runLimit: 50000, dataStack: [][]byte{{2}}, }, wantVM: &virtualMachine{ runLimit: 49998, dataStack: [][]byte{{1}}, }, }, { op: OP_2MUL, startVM: &virtualMachine{ runLimit: 50000, dataStack: [][]byte{{2}}, }, wantVM: &virtualMachine{ runLimit: 49998, dataStack: [][]byte{{4}}, }, }, { op: OP_2DIV, startVM: &virtualMachine{ runLimit: 50000, dataStack: [][]byte{{2}}, }, wantVM: &virtualMachine{ runLimit: 49998, dataStack: [][]byte{{1}}, }, }, { op: OP_2DIV, startVM: &virtualMachine{ runLimit: 50000, dataStack: [][]byte{Int64Bytes(-2)}, }, wantVM: &virtualMachine{ runLimit: 49998, dataStack: [][]byte{Int64Bytes(-1)}, }, }, { op: OP_2DIV, startVM: &virtualMachine{ runLimit: 50000, dataStack: [][]byte{Int64Bytes(-1)}, }, wantVM: &virtualMachine{ runLimit: 49998, dataStack: [][]byte{Int64Bytes(-1)}, }, }, { op: OP_NEGATE, startVM: &virtualMachine{ runLimit: 50000, dataStack: [][]byte{{2}}, }, wantVM: &virtualMachine{ runLimit: 49998, deferredCost: 7, dataStack: [][]byte{Int64Bytes(-2)}, }, }, { op: OP_ABS, startVM: &virtualMachine{ runLimit: 50000, dataStack: [][]byte{{2}}, }, wantVM: &virtualMachine{ runLimit: 49998, dataStack: [][]byte{{2}}, }, }, { op: OP_ABS, startVM: &virtualMachine{ runLimit: 50000, dataStack: [][]byte{Int64Bytes(-2)}, }, wantVM: &virtualMachine{ runLimit: 49998, deferredCost: -7, dataStack: [][]byte{{2}}, }, }, { op: OP_NOT, startVM: &virtualMachine{ runLimit: 50000, dataStack: [][]byte{{2}}, }, wantVM: &virtualMachine{ runLimit: 49998, deferredCost: -1, dataStack: [][]byte{{}}, }, }, { op: OP_0NOTEQUAL, startVM: &virtualMachine{ runLimit: 50000, dataStack: [][]byte{{2}}, }, wantVM: &virtualMachine{ runLimit: 49998, dataStack: [][]byte{{1}}, }, }, { op: OP_ADD, startVM: &virtualMachine{ runLimit: 50000, dataStack: [][]byte{{2}, {1}}, }, wantVM: &virtualMachine{ runLimit: 49998, deferredCost: -9, dataStack: [][]byte{{3}}, }, }, { op: OP_SUB, startVM: &virtualMachine{ runLimit: 50000, dataStack: [][]byte{{2}, {1}}, }, wantVM: &virtualMachine{ runLimit: 49998, deferredCost: -9, dataStack: [][]byte{{1}}, }, }, { op: OP_MUL, startVM: &virtualMachine{ runLimit: 50000, dataStack: [][]byte{{2}, {1}}, }, wantVM: &virtualMachine{ runLimit: 49992, deferredCost: -9, dataStack: [][]byte{{2}}, }, }, { op: OP_DIV, startVM: &virtualMachine{ runLimit: 50000, dataStack: [][]byte{{2}, {1}}, }, wantVM: &virtualMachine{ runLimit: 49992, deferredCost: -9, dataStack: [][]byte{{2}}, }, }, { op: OP_DIV, startVM: &virtualMachine{ runLimit: 50000, dataStack: [][]byte{Int64Bytes(-2), {1}}, }, wantVM: &virtualMachine{ runLimit: 49992, deferredCost: -9, dataStack: [][]byte{Int64Bytes(-2)}, }, }, { op: OP_DIV, startVM: &virtualMachine{ runLimit: 50000, dataStack: [][]byte{Int64Bytes(-2), Int64Bytes(-1)}, }, wantVM: &virtualMachine{ runLimit: 49992, deferredCost: -23, dataStack: [][]byte{{2}}, }, }, { op: OP_DIV, startVM: &virtualMachine{ runLimit: 50000, dataStack: [][]byte{Int64Bytes(-3), Int64Bytes(2)}, }, wantVM: &virtualMachine{ runLimit: 49992, deferredCost: -9, dataStack: [][]byte{Int64Bytes(-1)}, }, }, { op: OP_DIV, startVM: &virtualMachine{ runLimit: 50000, dataStack: [][]byte{{2}, {}}, }, wantErr: ErrDivZero, }, { op: OP_MOD, startVM: &virtualMachine{ runLimit: 50000, dataStack: [][]byte{{2}, {1}}, }, wantVM: &virtualMachine{ runLimit: 49992, deferredCost: -10, dataStack: [][]byte{{}}, }, }, { op: OP_MOD, startVM: &virtualMachine{ runLimit: 50000, dataStack: [][]byte{Int64Bytes(-12), {10}}, }, wantVM: &virtualMachine{ runLimit: 49992, deferredCost: -16, dataStack: [][]byte{{8}}, }, }, { op: OP_MOD, startVM: &virtualMachine{ runLimit: 50000, dataStack: [][]byte{{2}, {0}}, }, wantErr: ErrDivZero, }, { op: OP_LSHIFT, startVM: &virtualMachine{ runLimit: 50000, dataStack: [][]byte{{2}, {1}}, }, wantVM: &virtualMachine{ runLimit: 49992, deferredCost: -9, dataStack: [][]byte{{4}}, }, }, { op: OP_LSHIFT, startVM: &virtualMachine{ runLimit: 50000, dataStack: [][]byte{Int64Bytes(-2), {1}}, }, wantVM: &virtualMachine{ runLimit: 49992, deferredCost: -9, dataStack: [][]byte{Int64Bytes(-4)}, }, }, { op: OP_RSHIFT, startVM: &virtualMachine{ runLimit: 50000, dataStack: [][]byte{{2}, {1}}, }, wantVM: &virtualMachine{ runLimit: 49992, deferredCost: -9, dataStack: [][]byte{{1}}, }, }, { op: OP_RSHIFT, startVM: &virtualMachine{ runLimit: 50000, dataStack: [][]byte{Int64Bytes(-2), {1}}, }, wantVM: &virtualMachine{ runLimit: 49992, deferredCost: -9, dataStack: [][]byte{Int64Bytes(-1)}, }, }, { op: OP_BOOLAND, startVM: &virtualMachine{ runLimit: 50000, dataStack: [][]byte{{2}, {1}}, }, wantVM: &virtualMachine{ runLimit: 49998, deferredCost: -9, dataStack: [][]byte{{1}}, }, }, { op: OP_BOOLOR, startVM: &virtualMachine{ runLimit: 50000, dataStack: [][]byte{{2}, {1}}, }, wantVM: &virtualMachine{ runLimit: 49998, deferredCost: -9, dataStack: [][]byte{{1}}, }, }, { op: OP_NUMEQUAL, startVM: &virtualMachine{ runLimit: 50000, dataStack: [][]byte{{2}, {1}}, }, wantVM: &virtualMachine{ runLimit: 49998, deferredCost: -10, dataStack: [][]byte{{}}, }, }, { op: OP_NUMEQUALVERIFY, startVM: &virtualMachine{ runLimit: 50000, dataStack: [][]byte{{2}, {2}}, }, wantVM: &virtualMachine{ runLimit: 49998, deferredCost: -18, dataStack: [][]byte{}, }, }, { op: OP_NUMEQUALVERIFY, startVM: &virtualMachine{ runLimit: 50000, dataStack: [][]byte{{1}, {2}}, }, wantErr: ErrVerifyFailed, }, { op: OP_NUMNOTEQUAL, startVM: &virtualMachine{ runLimit: 50000, dataStack: [][]byte{{2}, {1}}, }, wantVM: &virtualMachine{ runLimit: 49998, deferredCost: -9, dataStack: [][]byte{{1}}, }, }, { op: OP_LESSTHAN, startVM: &virtualMachine{ runLimit: 50000, dataStack: [][]byte{{2}, {1}}, }, wantVM: &virtualMachine{ runLimit: 49998, deferredCost: -10, dataStack: [][]byte{{}}, }, }, { op: OP_LESSTHANOREQUAL, startVM: &virtualMachine{ runLimit: 50000, dataStack: [][]byte{{2}, {1}}, }, wantVM: &virtualMachine{ runLimit: 49998, deferredCost: -10, dataStack: [][]byte{{}}, }, }, { op: OP_GREATERTHAN, startVM: &virtualMachine{ runLimit: 50000, dataStack: [][]byte{{2}, {1}}, }, wantVM: &virtualMachine{ runLimit: 49998, deferredCost: -9, dataStack: [][]byte{{1}}, }, }, { op: OP_GREATERTHANOREQUAL, startVM: &virtualMachine{ runLimit: 50000, dataStack: [][]byte{{2}, {1}}, }, wantVM: &virtualMachine{ runLimit: 49998, deferredCost: -9, dataStack: [][]byte{{1}}, }, }, { op: OP_MIN, startVM: &virtualMachine{ runLimit: 50000, dataStack: [][]byte{{2}, {1}}, }, wantVM: &virtualMachine{ runLimit: 49998, deferredCost: -9, dataStack: [][]byte{{1}}, }, }, { op: OP_MIN, startVM: &virtualMachine{ runLimit: 50000, dataStack: [][]byte{{1}, {2}}, }, wantVM: &virtualMachine{ runLimit: 49998, deferredCost: -9, dataStack: [][]byte{{1}}, }, }, { op: OP_MAX, startVM: &virtualMachine{ runLimit: 50000, dataStack: [][]byte{{2}, {1}}, }, wantVM: &virtualMachine{ runLimit: 49998, deferredCost: -9, dataStack: [][]byte{{2}}, }, }, { op: OP_MAX, startVM: &virtualMachine{ runLimit: 50000, dataStack: [][]byte{{1}, {2}}, }, wantVM: &virtualMachine{ runLimit: 49998, deferredCost: -9, dataStack: [][]byte{{2}}, }, }, { op: OP_WITHIN, startVM: &virtualMachine{ runLimit: 50000, dataStack: [][]byte{{1}, {1}, {2}}, }, wantVM: &virtualMachine{ runLimit: 49996, deferredCost: -18, dataStack: [][]byte{{1}}, }, }} numops := []Op{ OP_1ADD, OP_1SUB, OP_2MUL, OP_2DIV, OP_NEGATE, OP_ABS, OP_NOT, OP_0NOTEQUAL, OP_ADD, OP_SUB, OP_MUL, OP_DIV, OP_MOD, OP_LSHIFT, OP_RSHIFT, OP_BOOLAND, OP_BOOLOR, OP_NUMEQUAL, OP_NUMEQUALVERIFY, OP_NUMNOTEQUAL, OP_LESSTHAN, OP_LESSTHANOREQUAL, OP_GREATERTHAN, OP_GREATERTHANOREQUAL, OP_MIN, OP_MAX, OP_WITHIN, } for _, op := range numops { cases = append(cases, testStruct{ op: op, startVM: &virtualMachine{ runLimit: 0, dataStack: [][]byte{{2}, {2}, {2}}, }, wantErr: ErrRunLimitExceeded, }) } for i, c := range cases { err := ops[c.op].fn(c.startVM) if err != c.wantErr { t.Errorf("case %d, op %s: got err = %v want %v", i, ops[c.op].name, err, c.wantErr) continue } if c.wantErr != nil { continue } if !testutil.DeepEqual(c.startVM, c.wantVM) { t.Errorf("case %d, op %s: unexpected vm result\n\tgot: %+v\n\twant: %+v\n", i, ops[c.op].name, c.startVM, c.wantVM) } } } func TestRangeErrs(t *testing.T) { cases := []struct { prog string expectRangeErr bool }{ {"0 1ADD", false}, {fmt.Sprintf("%d 1ADD", int64(math.MinInt64)), false}, {fmt.Sprintf("%d 1ADD", int64(math.MaxInt64)-1), false}, {fmt.Sprintf("%d 1ADD", int64(math.MaxInt64)), true}, {"0 1SUB", false}, {fmt.Sprintf("%d 1SUB", int64(math.MaxInt64)), false}, {fmt.Sprintf("%d 1SUB", int64(math.MinInt64)+1), false}, {fmt.Sprintf("%d 1SUB", int64(math.MinInt64)), true}, {"1 2MUL", false}, {fmt.Sprintf("%d 2MUL", int64(math.MaxInt64)/2-1), false}, {fmt.Sprintf("%d 2MUL", int64(math.MaxInt64)/2+1), true}, {fmt.Sprintf("%d 2MUL", int64(math.MinInt64)/2+1), false}, {fmt.Sprintf("%d 2MUL", int64(math.MinInt64)/2-1), true}, {"1 NEGATE", false}, {"-1 NEGATE", false}, {fmt.Sprintf("%d NEGATE", int64(math.MaxInt64)), false}, {fmt.Sprintf("%d NEGATE", int64(math.MinInt64)), true}, {"1 ABS", false}, {"-1 ABS", false}, {fmt.Sprintf("%d ABS", int64(math.MaxInt64)), false}, {fmt.Sprintf("%d ABS", int64(math.MinInt64)), true}, {"2 3 ADD", false}, {fmt.Sprintf("%d %d ADD", int64(math.MinInt64), int64(math.MaxInt64)), false}, {fmt.Sprintf("%d %d ADD", int64(math.MaxInt64)/2-1, int64(math.MaxInt64)/2-2), false}, {fmt.Sprintf("%d %d ADD", int64(math.MaxInt64)/2+1, int64(math.MaxInt64)/2+2), true}, {fmt.Sprintf("%d %d ADD", int64(math.MinInt64)/2+1, int64(math.MinInt64)/2+2), false}, {fmt.Sprintf("%d %d ADD", int64(math.MinInt64)/2-1, int64(math.MinInt64)/2-2), true}, {"2 3 SUB", false}, {fmt.Sprintf("1 %d SUB", int64(math.MaxInt64)), false}, {fmt.Sprintf("-1 %d SUB", int64(math.MinInt64)), false}, {fmt.Sprintf("1 %d SUB", int64(math.MinInt64)), true}, {fmt.Sprintf("-1 %d SUB", int64(math.MaxInt64)), false}, {fmt.Sprintf("-2 %d SUB", int64(math.MaxInt64)), true}, {"1 2 LSHIFT", false}, {"-1 2 LSHIFT", false}, {"-1 63 LSHIFT", false}, {"-1 64 LSHIFT", true}, {"0 64 LSHIFT", false}, {"1 62 LSHIFT", false}, {"1 63 LSHIFT", true}, {fmt.Sprintf("%d 0 LSHIFT", int64(math.MaxInt64)), false}, {fmt.Sprintf("%d 1 LSHIFT", int64(math.MaxInt64)), true}, {fmt.Sprintf("%d 1 LSHIFT", int64(math.MaxInt64)/2), false}, {fmt.Sprintf("%d 2 LSHIFT", int64(math.MaxInt64)/2), true}, {fmt.Sprintf("%d 0 LSHIFT", int64(math.MinInt64)), false}, {fmt.Sprintf("%d 1 LSHIFT", int64(math.MinInt64)), true}, {fmt.Sprintf("%d 1 LSHIFT", int64(math.MinInt64)/2), false}, {fmt.Sprintf("%d 2 LSHIFT", int64(math.MinInt64)/2), true}, } for i, c := range cases { prog, _ := Assemble(c.prog) vm := &virtualMachine{ program: prog, runLimit: 50000, } err := vm.run() switch err { case nil: if c.expectRangeErr { t.Errorf("case %d (%s): expected range error, got none", i, c.prog) } case ErrRange: if !c.expectRangeErr { t.Errorf("case %d (%s): got unexpected range error", i, c.prog) } default: if c.expectRangeErr { t.Errorf("case %d (%s): expected range error, got %s", i, c.prog, err) } else { t.Errorf("case %d (%s): got unexpected error %s", i, c.prog, err) } } } }