diff options
Diffstat (limited to 'core/vm/instructions_test.go')
-rw-r--r-- | core/vm/instructions_test.go | 225 |
1 files changed, 138 insertions, 87 deletions
diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index 8a48d765d..50d0a9dda 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -18,6 +18,9 @@ package vm import ( "bytes" + "encoding/json" + "fmt" + "io/ioutil" "math/big" "testing" @@ -26,32 +29,91 @@ import ( "github.com/ethereum/go-ethereum/params" ) -type twoOperandTest struct { - x string - y string - expected string +type TwoOperandTestcase struct { + X string + Y string + Expected string } -func testTwoOperandOp(t *testing.T, tests []twoOperandTest, opFn func(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error)) { +type twoOperandParams struct { + x string + y string +} + +var commonParams []*twoOperandParams +var twoOpMethods map[string]executionFunc + +func init() { + + // Params is a list of common edgecases that should be used for some common tests + params := []string{ + "0000000000000000000000000000000000000000000000000000000000000000", // 0 + "0000000000000000000000000000000000000000000000000000000000000001", // +1 + "0000000000000000000000000000000000000000000000000000000000000005", // +5 + "7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe", // + max -1 + "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", // + max + "8000000000000000000000000000000000000000000000000000000000000000", // - max + "8000000000000000000000000000000000000000000000000000000000000001", // - max+1 + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb", // - 5 + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", // - 1 + } + // Params are combined so each param is used on each 'side' + commonParams = make([]*twoOperandParams, len(params)*len(params)) + for i, x := range params { + for j, y := range params { + commonParams[i*len(params)+j] = &twoOperandParams{x, y} + } + } + twoOpMethods = map[string]executionFunc{ + "add": opAdd, + "sub": opSub, + "mul": opMul, + "div": opDiv, + "sdiv": opSdiv, + "mod": opMod, + "smod": opSmod, + "exp": opExp, + "signext": opSignExtend, + "lt": opLt, + "gt": opGt, + "slt": opSlt, + "sgt": opSgt, + "eq": opEq, + "and": opAnd, + "or": opOr, + "xor": opXor, + "byte": opByte, + "shl": opSHL, + "shr": opSHR, + "sar": opSAR, + } +} + +func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFunc, name string) { + var ( env = NewEVM(Context{}, nil, params.TestChainConfig, Config{}) stack = newstack() pc = uint64(0) - evmInterpreter = NewEVMInterpreter(env, env.vmConfig) + evmInterpreter = env.interpreter.(*EVMInterpreter) ) - - env.interpreter = evmInterpreter + // Stuff a couple of nonzero bigints into pool, to ensure that ops do not rely on pooled integers to be zero evmInterpreter.intPool = poolOfIntPools.get() + evmInterpreter.intPool.put(big.NewInt(-1337)) + evmInterpreter.intPool.put(big.NewInt(-1337)) + evmInterpreter.intPool.put(big.NewInt(-1337)) + for i, test := range tests { - x := new(big.Int).SetBytes(common.Hex2Bytes(test.x)) - shift := new(big.Int).SetBytes(common.Hex2Bytes(test.y)) - expected := new(big.Int).SetBytes(common.Hex2Bytes(test.expected)) + x := new(big.Int).SetBytes(common.Hex2Bytes(test.X)) + y := new(big.Int).SetBytes(common.Hex2Bytes(test.Y)) + expected := new(big.Int).SetBytes(common.Hex2Bytes(test.Expected)) stack.push(x) - stack.push(shift) + stack.push(y) opFn(&pc, evmInterpreter, nil, nil, stack) actual := stack.pop() + if actual.Cmp(expected) != 0 { - t.Errorf("Testcase %d, expected %v, got %v", i, expected, actual) + t.Errorf("Testcase %v %d, %v(%x, %x): expected %x, got %x", name, i, name, x, y, expected, actual) } // Check pool usage // 1.pool is not allowed to contain anything on the stack @@ -64,7 +126,7 @@ func testTwoOperandOp(t *testing.T, tests []twoOperandTest, opFn func(pc *uint64 for evmInterpreter.intPool.pool.len() > 0 { key := evmInterpreter.intPool.get() if _, exist := poolvals[key]; exist { - t.Errorf("Testcase %d, pool contains double-entry", i) + t.Errorf("Testcase %v %d, pool contains double-entry", name, i) } poolvals[key] = struct{}{} } @@ -74,47 +136,22 @@ func testTwoOperandOp(t *testing.T, tests []twoOperandTest, opFn func(pc *uint64 } func TestByteOp(t *testing.T) { - var ( - env = NewEVM(Context{}, nil, params.TestChainConfig, Config{}) - stack = newstack() - evmInterpreter = NewEVMInterpreter(env, env.vmConfig) - ) - - env.interpreter = evmInterpreter - evmInterpreter.intPool = poolOfIntPools.get() - tests := []struct { - v string - th uint64 - expected *big.Int - }{ - {"ABCDEF0908070605040302010000000000000000000000000000000000000000", 0, big.NewInt(0xAB)}, - {"ABCDEF0908070605040302010000000000000000000000000000000000000000", 1, big.NewInt(0xCD)}, - {"00CDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff", 0, big.NewInt(0x00)}, - {"00CDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff", 1, big.NewInt(0xCD)}, - {"0000000000000000000000000000000000000000000000000000000000102030", 31, big.NewInt(0x30)}, - {"0000000000000000000000000000000000000000000000000000000000102030", 30, big.NewInt(0x20)}, - {"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 32, big.NewInt(0x0)}, - {"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 0xFFFFFFFFFFFFFFFF, big.NewInt(0x0)}, - } - pc := uint64(0) - for _, test := range tests { - val := new(big.Int).SetBytes(common.Hex2Bytes(test.v)) - th := new(big.Int).SetUint64(test.th) - stack.push(val) - stack.push(th) - opByte(&pc, evmInterpreter, nil, nil, stack) - actual := stack.pop() - if actual.Cmp(test.expected) != 0 { - t.Fatalf("Expected [%v] %v:th byte to be %v, was %v.", test.v, test.th, test.expected, actual) - } + tests := []TwoOperandTestcase{ + {"ABCDEF0908070605040302010000000000000000000000000000000000000000", "00", "AB"}, + {"ABCDEF0908070605040302010000000000000000000000000000000000000000", "01", "CD"}, + {"00CDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff", "00", "00"}, + {"00CDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff", "01", "CD"}, + {"0000000000000000000000000000000000000000000000000000000000102030", "1F", "30"}, + {"0000000000000000000000000000000000000000000000000000000000102030", "1E", "20"}, + {"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "20", "00"}, + {"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "FFFFFFFFFFFFFFFF", "00"}, } - poolOfIntPools.put(evmInterpreter.intPool) + testTwoOperandOp(t, tests, opByte, "byte") } func TestSHL(t *testing.T) { // Testcases from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-145.md#shl-shift-left - tests := []twoOperandTest{ - {"0000000000000000000000000000000000000000000000000000000000000001", "00", "0000000000000000000000000000000000000000000000000000000000000001"}, + tests := []TwoOperandTestcase{ {"0000000000000000000000000000000000000000000000000000000000000001", "01", "0000000000000000000000000000000000000000000000000000000000000002"}, {"0000000000000000000000000000000000000000000000000000000000000001", "ff", "8000000000000000000000000000000000000000000000000000000000000000"}, {"0000000000000000000000000000000000000000000000000000000000000001", "0100", "0000000000000000000000000000000000000000000000000000000000000000"}, @@ -126,12 +163,12 @@ func TestSHL(t *testing.T) { {"0000000000000000000000000000000000000000000000000000000000000000", "01", "0000000000000000000000000000000000000000000000000000000000000000"}, {"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "01", "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"}, } - testTwoOperandOp(t, tests, opSHL) + testTwoOperandOp(t, tests, opSHL, "shl") } func TestSHR(t *testing.T) { // Testcases from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-145.md#shr-logical-shift-right - tests := []twoOperandTest{ + tests := []TwoOperandTestcase{ {"0000000000000000000000000000000000000000000000000000000000000001", "00", "0000000000000000000000000000000000000000000000000000000000000001"}, {"0000000000000000000000000000000000000000000000000000000000000001", "01", "0000000000000000000000000000000000000000000000000000000000000000"}, {"8000000000000000000000000000000000000000000000000000000000000000", "01", "4000000000000000000000000000000000000000000000000000000000000000"}, @@ -144,12 +181,12 @@ func TestSHR(t *testing.T) { {"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0100", "0000000000000000000000000000000000000000000000000000000000000000"}, {"0000000000000000000000000000000000000000000000000000000000000000", "01", "0000000000000000000000000000000000000000000000000000000000000000"}, } - testTwoOperandOp(t, tests, opSHR) + testTwoOperandOp(t, tests, opSHR, "shr") } func TestSAR(t *testing.T) { // Testcases from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-145.md#sar-arithmetic-shift-right - tests := []twoOperandTest{ + tests := []TwoOperandTestcase{ {"0000000000000000000000000000000000000000000000000000000000000001", "00", "0000000000000000000000000000000000000000000000000000000000000001"}, {"0000000000000000000000000000000000000000000000000000000000000001", "01", "0000000000000000000000000000000000000000000000000000000000000000"}, {"8000000000000000000000000000000000000000000000000000000000000000", "01", "c000000000000000000000000000000000000000000000000000000000000000"}, @@ -168,44 +205,58 @@ func TestSAR(t *testing.T) { {"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0100", "0000000000000000000000000000000000000000000000000000000000000000"}, } - testTwoOperandOp(t, tests, opSAR) + testTwoOperandOp(t, tests, opSAR, "sar") } -func TestSGT(t *testing.T) { - tests := []twoOperandTest{ +// getResult is a convenience function to generate the expected values +func getResult(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcase { + var ( + env = NewEVM(Context{}, nil, params.TestChainConfig, Config{}) + stack = newstack() + pc = uint64(0) + interpreter = env.interpreter.(*EVMInterpreter) + ) + interpreter.intPool = poolOfIntPools.get() + result := make([]TwoOperandTestcase, len(args)) + for i, param := range args { + x := new(big.Int).SetBytes(common.Hex2Bytes(param.x)) + y := new(big.Int).SetBytes(common.Hex2Bytes(param.y)) + stack.push(x) + stack.push(y) + opFn(&pc, interpreter, nil, nil, stack) + actual := stack.pop() + result[i] = TwoOperandTestcase{param.x, param.y, fmt.Sprintf("%064x", actual)} + } + return result +} - {"0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000000"}, - {"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000000"}, - {"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000000"}, - {"0000000000000000000000000000000000000000000000000000000000000001", "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000001"}, - {"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000000"}, - {"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000001"}, - {"0000000000000000000000000000000000000000000000000000000000000001", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000000"}, - {"8000000000000000000000000000000000000000000000000000000000000001", "8000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000000"}, - {"8000000000000000000000000000000000000000000000000000000000000001", "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000001"}, - {"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "8000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000000"}, - {"fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb", "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd", "0000000000000000000000000000000000000000000000000000000000000001"}, - {"fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd", "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb", "0000000000000000000000000000000000000000000000000000000000000000"}, +// utility function to fill the json-file with testcases +// Enable this test to generate the 'testcases_xx.json' files +func xTestWriteExpectedValues(t *testing.T) { + for name, method := range twoOpMethods { + data, err := json.Marshal(getResult(commonParams, method)) + if err != nil { + t.Fatal(err) + } + _ = ioutil.WriteFile(fmt.Sprintf("testdata/testcases_%v.json", name), data, 0644) + if err != nil { + t.Fatal(err) + } } - testTwoOperandOp(t, tests, opSgt) -} - -func TestSLT(t *testing.T) { - tests := []twoOperandTest{ - {"0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000000"}, - {"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000000"}, - {"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000000"}, - {"0000000000000000000000000000000000000000000000000000000000000001", "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000000"}, - {"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000001"}, - {"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000000"}, - {"0000000000000000000000000000000000000000000000000000000000000001", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000001"}, - {"8000000000000000000000000000000000000000000000000000000000000001", "8000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000000"}, - {"8000000000000000000000000000000000000000000000000000000000000001", "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000000"}, - {"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "8000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000001"}, - {"fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb", "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd", "0000000000000000000000000000000000000000000000000000000000000000"}, - {"fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd", "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb", "0000000000000000000000000000000000000000000000000000000000000001"}, + t.Fatal("This test should not be activated") +} + +// TestJsonTestcases runs through all the testcases defined as json-files +func TestJsonTestcases(t *testing.T) { + for name := range twoOpMethods { + data, err := ioutil.ReadFile(fmt.Sprintf("testdata/testcases_%v.json", name)) + if err != nil { + t.Fatal("Failed to read file", err) + } + var testcases []TwoOperandTestcase + json.Unmarshal(data, &testcases) + testTwoOperandOp(t, testcases, twoOpMethods[name], name) } - testTwoOperandOp(t, tests, opSlt) } func opBenchmark(bench *testing.B, op func(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error), args ...string) { |