@ -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
e xpected string
type TwoOperandTestcase struct {
X string
Y string
E xpected 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 . E xpected) )
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 ) {