diff options
author | JM <jm@dexon.org> | 2019-01-31 15:12:57 +0800 |
---|---|---|
committer | Jhih-Ming Huang <jm.huang@cobinhood.com> | 2019-04-11 10:39:57 +0800 |
commit | 61c57167d691064864bed88928b0cfbeb4da874c (patch) | |
tree | 5f090be2d01c38465b8561ef34be010d8bb89650 | |
parent | 034d4756e7c95af12f88f89fb8bfa72bfa319e2d (diff) | |
download | dexon-61c57167d691064864bed88928b0cfbeb4da874c.tar.gz dexon-61c57167d691064864bed88928b0cfbeb4da874c.tar.zst dexon-61c57167d691064864bed88928b0cfbeb4da874c.zip |
core: vm: vm interface (#164)
-rw-r--r-- | accounts/abi/bind/bind_test.go | 2 | ||||
-rw-r--r-- | accounts/abi/bind/util_test.go | 2 | ||||
-rw-r--r-- | contracts/chequebook/contract/chequebook.go | 4 | ||||
-rw-r--r-- | contracts/ens/contract/ens.go | 4 | ||||
-rw-r--r-- | contracts/ens/contract/fifsregistrar.go | 4 | ||||
-rw-r--r-- | contracts/ens/contract/publicresolver.go | 4 | ||||
-rw-r--r-- | core/blockchain_test.go | 3 | ||||
-rw-r--r-- | core/state_transition.go | 14 | ||||
-rw-r--r-- | core/types/transaction.go | 5 | ||||
-rw-r--r-- | core/vm/evm/evm.go | 81 | ||||
-rw-r--r-- | core/vm/evm/instructions.go | 17 | ||||
-rw-r--r-- | core/vm/evm/interpreter.go | 9 | ||||
-rw-r--r-- | core/vm/evm/runtime/runtime_example_test.go | 6 | ||||
-rw-r--r-- | core/vm/evm/runtime/runtime_test.go | 48 | ||||
-rw-r--r-- | core/vm/sqlvm/sqlvm.go | 45 | ||||
-rw-r--r-- | core/vm/tools/patch.go | 158 | ||||
-rw-r--r-- | core/vm/tools/patch_example_test.go | 26 | ||||
-rw-r--r-- | core/vm/tools/transaction.go | 263 | ||||
-rw-r--r-- | core/vm/vm.go | 105 | ||||
-rw-r--r-- | eth/tracers/tracers_test.go | 59 | ||||
-rw-r--r-- | les/helper_test.go | 11 | ||||
-rw-r--r-- | light/odr_test.go | 2 | ||||
-rw-r--r-- | tests/state_test.go | 17 |
23 files changed, 805 insertions, 84 deletions
diff --git a/accounts/abi/bind/bind_test.go b/accounts/abi/bind/bind_test.go index a197bb638..d7a1d76c1 100644 --- a/accounts/abi/bind/bind_test.go +++ b/accounts/abi/bind/bind_test.go @@ -27,6 +27,7 @@ import ( "testing" "github.com/dexon-foundation/dexon/common" + "github.com/dexon-foundation/dexon/core/vm/tools" ) var bindTests = []struct { @@ -945,6 +946,7 @@ func TestBindings(t *testing.T) { // Generate the test suite for all the contracts for i, tt := range bindTests { // Generate the binding and create a Go source file in the workspace + tt.bytecode = tools.Patch(tt.bytecode) bind, err := Bind([]string{tt.name}, []string{tt.abi}, []string{tt.bytecode}, "bindtest", LangGo) if err != nil { t.Fatalf("test %d: failed to generate binding: %v", i, err) diff --git a/accounts/abi/bind/util_test.go b/accounts/abi/bind/util_test.go index 4226affa3..0c0120a66 100644 --- a/accounts/abi/bind/util_test.go +++ b/accounts/abi/bind/util_test.go @@ -27,6 +27,7 @@ import ( "github.com/dexon-foundation/dexon/common" "github.com/dexon-foundation/dexon/core" "github.com/dexon-foundation/dexon/core/types" + "github.com/dexon-foundation/dexon/core/vm/tools" "github.com/dexon-foundation/dexon/crypto" ) @@ -60,6 +61,7 @@ func TestWaitDeployed(t *testing.T) { ) // Create the transaction. + test.code = tools.Patch(test.code) tx := types.NewContractCreation(0, big.NewInt(0), test.gas, big.NewInt(1), common.FromHex(test.code)) tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey) diff --git a/contracts/chequebook/contract/chequebook.go b/contracts/chequebook/contract/chequebook.go index 4dd0cc03a..bbfba40bc 100644 --- a/contracts/chequebook/contract/chequebook.go +++ b/contracts/chequebook/contract/chequebook.go @@ -12,6 +12,7 @@ import ( "github.com/dexon-foundation/dexon/accounts/abi/bind" "github.com/dexon-foundation/dexon/common" "github.com/dexon-foundation/dexon/core/types" + "github.com/dexon-foundation/dexon/core/vm/tools" "github.com/dexon-foundation/dexon/event" ) @@ -27,7 +28,8 @@ func DeployChequebook(auth *bind.TransactOpts, backend bind.ContractBackend) (co if err != nil { return common.Address{}, nil, nil, err } - address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(ChequebookBin), backend) + newChequebookBin := tools.PatchBinary(common.FromHex(ChequebookBin)) + address, tx, contract, err := bind.DeployContract(auth, parsed, newChequebookBin, backend) if err != nil { return common.Address{}, nil, nil, err } diff --git a/contracts/ens/contract/ens.go b/contracts/ens/contract/ens.go index 203205983..c4dfb738d 100644 --- a/contracts/ens/contract/ens.go +++ b/contracts/ens/contract/ens.go @@ -11,6 +11,7 @@ import ( "github.com/dexon-foundation/dexon/accounts/abi/bind" "github.com/dexon-foundation/dexon/common" "github.com/dexon-foundation/dexon/core/types" + "github.com/dexon-foundation/dexon/core/vm/tools" "github.com/dexon-foundation/dexon/event" ) @@ -26,7 +27,8 @@ func DeployENS(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Ad if err != nil { return common.Address{}, nil, nil, err } - address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(ENSBin), backend) + newENSBin := tools.PatchBinary(common.FromHex(ENSBin)) + address, tx, contract, err := bind.DeployContract(auth, parsed, newENSBin, backend) if err != nil { return common.Address{}, nil, nil, err } diff --git a/contracts/ens/contract/fifsregistrar.go b/contracts/ens/contract/fifsregistrar.go index 82e1cc43e..fa9b22c50 100644 --- a/contracts/ens/contract/fifsregistrar.go +++ b/contracts/ens/contract/fifsregistrar.go @@ -10,6 +10,7 @@ import ( "github.com/dexon-foundation/dexon/accounts/abi/bind" "github.com/dexon-foundation/dexon/common" "github.com/dexon-foundation/dexon/core/types" + "github.com/dexon-foundation/dexon/core/vm/tools" ) // FIFSRegistrarABI is the input ABI used to generate the binding from. @@ -24,7 +25,8 @@ func DeployFIFSRegistrar(auth *bind.TransactOpts, backend bind.ContractBackend, if err != nil { return common.Address{}, nil, nil, err } - address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(FIFSRegistrarBin), backend, ensAddr, node) + newBin := tools.PatchBinary(common.FromHex(FIFSRegistrarBin)) + address, tx, contract, err := bind.DeployContract(auth, parsed, newBin, backend, ensAddr, node) if err != nil { return common.Address{}, nil, nil, err } diff --git a/contracts/ens/contract/publicresolver.go b/contracts/ens/contract/publicresolver.go index 9a6180c21..565e1df02 100644 --- a/contracts/ens/contract/publicresolver.go +++ b/contracts/ens/contract/publicresolver.go @@ -12,6 +12,7 @@ import ( "github.com/dexon-foundation/dexon/accounts/abi/bind" "github.com/dexon-foundation/dexon/common" "github.com/dexon-foundation/dexon/core/types" + "github.com/dexon-foundation/dexon/core/vm/tools" "github.com/dexon-foundation/dexon/event" ) @@ -27,7 +28,8 @@ func DeployPublicResolver(auth *bind.TransactOpts, backend bind.ContractBackend, if err != nil { return common.Address{}, nil, nil, err } - address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(PublicResolverBin), backend, ensAddr) + newBin := tools.PatchBinary(common.FromHex(PublicResolverBin)) + address, tx, contract, err := bind.DeployContract(auth, parsed, newBin, backend, ensAddr) if err != nil { return common.Address{}, nil, nil, err } diff --git a/core/blockchain_test.go b/core/blockchain_test.go index c760252d8..349bf698f 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -35,6 +35,7 @@ import ( "github.com/dexon-foundation/dexon/core/state" "github.com/dexon-foundation/dexon/core/types" vm "github.com/dexon-foundation/dexon/core/vm/evm" + "github.com/dexon-foundation/dexon/core/vm/tools" "github.com/dexon-foundation/dexon/crypto" "github.com/dexon-foundation/dexon/ethdb" "github.com/dexon-foundation/dexon/params" @@ -904,7 +905,7 @@ func TestLogReorgs(t *testing.T) { genesis = gspec.MustCommit(db) signer = types.NewEIP155Signer(gspec.Config.ChainID) ) - + code = tools.PatchBinary(code) blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil) defer blockchain.Stop() diff --git a/core/state_transition.go b/core/state_transition.go index a9a700c2b..32589da90 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -27,7 +27,6 @@ import ( "github.com/dexon-foundation/dexon/common" "github.com/dexon-foundation/dexon/core/vm" "github.com/dexon-foundation/dexon/core/vm/evm" - "github.com/dexon-foundation/dexon/core/vm/sqlvm" "github.com/dexon-foundation/dexon/log" "github.com/dexon-foundation/dexon/params" ) @@ -73,7 +72,6 @@ type StateTransition struct { data []byte state vm.StateDB evm *evm.EVM - sqlvm *sqlvm.SQLVM } // Message represents a message sent to a contract. @@ -254,6 +252,7 @@ func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bo // Pay intrinsic gas gas, err := IntrinsicGas(st.data, contractCreation, homestead) + if err != nil { return nil, 0, false, err } @@ -262,23 +261,18 @@ func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bo } var ( - evm = st.evm - sqlvm = st.sqlvm // vm errors do not effect consensus and are therefor // not assigned to err, except for insufficient balance // error. vmerr error ) + i := st.evm.Interpreter().(*evm.EVMInterpreter) if contractCreation { - if len(st.data) != 0 && st.data[0] == 0xed { - ret, _, st.gas, vmerr = sqlvm.Create(sender, st.data, st.gas, st.value) - } else { - ret, _, st.gas, vmerr = evm.Create(sender, st.data, st.gas, st.value) - } + ret, _, st.gas, vmerr = vm.Create(sender, st.data, st.gas, st.value, i) } else { // Increment the nonce for the next transaction st.state.SetNonce(msg.From(), st.state.GetNonce(sender.Address())+1) - ret, st.gas, vmerr = evm.Call(sender, st.to(), st.data, st.gas, st.value) + ret, st.gas, vmerr = vm.Call(sender, st.to(), st.data, st.gas, st.value, i) } if vmerr != nil { log.Debug("VM returned with error", "err", vmerr) diff --git a/core/types/transaction.go b/core/types/transaction.go index 857ac2137..014ab1025 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -42,7 +42,6 @@ type Transaction struct { size atomic.Value from atomic.Value } - type txdata struct { AccountNonce uint64 `json:"nonce" gencodec:"required"` Price *big.Int `json:"gasPrice" gencodec:"required"` @@ -171,7 +170,9 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error { *tx = Transaction{data: dec} return nil } - +func (tx *Transaction) SetFrom(fromTx *Transaction) { + tx.from.Store(fromTx.from.Load()) +} func (tx *Transaction) Data() []byte { return common.CopyBytes(tx.data.Payload) } func (tx *Transaction) Gas() uint64 { return tx.data.GasLimit } func (tx *Transaction) GasPrice() *big.Int { return new(big.Int).Set(tx.data.Price) } diff --git a/core/vm/evm/evm.go b/core/vm/evm/evm.go index 28fe24574..e1c8c02ed 100644 --- a/core/vm/evm/evm.go +++ b/core/vm/evm/evm.go @@ -31,6 +31,57 @@ import ( // deployed contract addresses (relevant after the account abstraction). var emptyCodeHash = crypto.Keccak256Hash(nil) +func init() { + vm.Register(vm.EVM, &EVMImplement{}) +} + +type EVMImplement struct{} + +func (evmImpl *EVMImplement) Create(caller vm.ContractRef, code []byte, gas uint64, + value *big.Int, interpreter vm.Interpreter) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) { + i := interpreter.(*EVMInterpreter) + return i.evm.Create(caller, code, gas, value) +} + +func (evmImpl *EVMImplement) Create2(caller vm.ContractRef, code []byte, gas uint64, + endowment *big.Int, salt *big.Int, + interpreter vm.Interpreter) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) { + + i := interpreter.(*EVMInterpreter) + return i.evm.Create2(caller, code, gas, endowment, salt) +} + +func (evmImpl *EVMImplement) Call(caller vm.ContractRef, addr common.Address, + input []byte, gas uint64, value *big.Int, + interpreter vm.Interpreter) (ret []byte, leftOverGas uint64, err error) { + i := interpreter.(*EVMInterpreter) + return i.evm.Call(caller, addr, input, gas, value) + +} +func (evmImpl *EVMImplement) CallCode(caller vm.ContractRef, addr common.Address, + input []byte, gas uint64, value *big.Int, + interpreter vm.Interpreter) (ret []byte, leftOverGas uint64, err error) { + + i := interpreter.(*EVMInterpreter) + return i.evm.CallCode(caller, addr, input, gas, value) +} + +func (evmImpl *EVMImplement) DelegateCall(caller vm.ContractRef, addr common.Address, + input []byte, gas uint64, + interpreter vm.Interpreter) (ret []byte, leftOverGas uint64, err error) { + + i := interpreter.(*EVMInterpreter) + return i.evm.DelegateCall(caller, addr, input, gas) +} + +func (evmImpl *EVMImplement) StaticCall(caller vm.ContractRef, addr common.Address, + input []byte, gas uint64, + interpreter vm.Interpreter) (ret []byte, leftovergas uint64, err error) { + + i := interpreter.(*EVMInterpreter) + return i.evm.StaticCall(caller, addr, input, gas) +} + // run runs the given contract and takes care of running precompiles with a fallback to the byte code interpreter. func run(evm *EVM, contract *vm.Contract, input []byte, readOnly bool) ([]byte, error) { if contract.CodeAddr != nil { @@ -187,8 +238,12 @@ func (evm *EVM) Call(caller vm.ContractRef, addr common.Address, input []byte, g // Initialise a new contract and set the code that is to be used by the EVM. // The contract is a scoped environment for this execution context only. contract := vm.NewContract(caller, to, value, gas) - contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) - + code := evm.StateDB.GetCode(addr) + if len(code) > 0 && code[0] == vm.EVM && vm.MULTIVM { + code = code[1:] + } + codeAndHash := vm.CodeAndHash{Code: code} + contract.SetCodeOptionalHash(&addr, &codeAndHash) // Even if the account has no code, we need to continue because it might be a precompile start := time.Now() @@ -243,7 +298,12 @@ func (evm *EVM) CallCode(caller vm.ContractRef, addr common.Address, input []byt // EVM. The contract is a scoped environment for this execution context // only. contract := vm.NewContract(caller, to, value, gas) - contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) + code := evm.StateDB.GetCode(addr) + if len(code) > 0 && vm.MULTIVM { + code = code[1:] + } + codeAndHash := vm.CodeAndHash{Code: code} + contract.SetCodeOptionalHash(&addr, &codeAndHash) ret, err = run(evm, contract, input, false) if err != nil { @@ -276,7 +336,12 @@ func (evm *EVM) DelegateCall(caller vm.ContractRef, addr common.Address, input [ // Initialise a new contract and make initialise the delegate values contract := vm.NewContract(caller, to, nil, gas).AsDelegate() - contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) + code := evm.StateDB.GetCode(addr) + if len(code) > 0 && vm.MULTIVM { + code = code[1:] + } + codeAndHash := vm.CodeAndHash{Code: code} + contract.SetCodeOptionalHash(&addr, &codeAndHash) ret, err = run(evm, contract, input, false) if err != nil { @@ -309,7 +374,12 @@ func (evm *EVM) StaticCall(caller vm.ContractRef, addr common.Address, input []b // EVM. The contract is a scoped environment for this execution context // only. contract := vm.NewContract(caller, to, new(big.Int), gas) - contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) + code := evm.StateDB.GetCode(addr) + if len(code) > 0 && vm.MULTIVM { + code = code[1:] + } + codeAndHash := vm.CodeAndHash{Code: code} + contract.SetCodeOptionalHash(&addr, &codeAndHash) // We do an AddBalance of zero here, just in order to trigger a touch. // This doesn't matter on Mainnet, where all empties are gone at the time of Byzantium, @@ -370,7 +440,6 @@ func (evm *EVM) create(caller vm.ContractRef, codeAndHash *vm.CodeAndHash, gas u evm.vmConfig.Tracer.CaptureStart(caller.Address(), address, true, codeAndHash.Code, gas, value) } start := time.Now() - ret, err := run(evm, contract, nil, false) // check whether the max code size has been exceeded diff --git a/core/vm/evm/instructions.go b/core/vm/evm/instructions.go index 10ff75b69..ce95d2446 100644 --- a/core/vm/evm/instructions.go +++ b/core/vm/evm/instructions.go @@ -491,8 +491,7 @@ func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, contract *vm.Cont memOffset = stack.Pop() dataOffset = stack.Pop() length = stack.Pop() - - end = interpreter.intPool.Get().Add(dataOffset, length) + end = interpreter.intPool.Get().Add(dataOffset, length) ) defer interpreter.intPool.Put(memOffset, dataOffset, length, end) @@ -725,12 +724,12 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, contract *vm.Contract, me input = memory.Get(offset.Int64(), size.Int64()) gas = contract.Gas ) + // size.Add(size, big1) if interpreter.evm.ChainConfig().IsEIP150(interpreter.evm.BlockNumber) { gas -= gas / 64 } - contract.UseGas(gas) - res, addr, returnGas, suberr := interpreter.evm.Create(contract, input, gas, value) + res, addr, returnGas, suberr := vm.Create(contract, input, gas, value, interpreter) // Push item on the stack based on the returned error. If the ruleset is // homestead we must check for CodeStoreOutOfGasError (homestead only // rule) and treat as an error, if the ruleset is frontier we must @@ -763,7 +762,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, contract *vm.Contract, m // Apply EIP150 gas -= gas / 64 contract.UseGas(gas) - res, addr, returnGas, suberr := interpreter.evm.Create2(contract, input, gas, endowment, salt) + res, addr, returnGas, suberr := vm.Create2(contract, input, gas, endowment, salt, interpreter) // Push item on the stack based on the returned error. if suberr != nil { stack.Push(interpreter.intPool.GetZero()) @@ -793,7 +792,7 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, contract *vm.Contract, memo if value.Sign() != 0 { gas += params.CallStipend } - ret, returnGas, err := interpreter.evm.Call(contract, toAddr, args, gas, value) + ret, returnGas, err := vm.Call(contract, toAddr, args, gas, value, interpreter) if err != nil { stack.Push(interpreter.intPool.GetZero()) } else { @@ -822,7 +821,7 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, contract *vm.Contract, if value.Sign() != 0 { gas += params.CallStipend } - ret, returnGas, err := interpreter.evm.CallCode(contract, toAddr, args, gas, value) + ret, returnGas, err := vm.CallCode(contract, toAddr, args, gas, value, interpreter) if err != nil { stack.Push(interpreter.intPool.GetZero()) } else { @@ -847,7 +846,7 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, contract *vm.Contra // Get arguments from the memory. args := memory.Get(inOffset.Int64(), inSize.Int64()) - ret, returnGas, err := interpreter.evm.DelegateCall(contract, toAddr, args, gas) + ret, returnGas, err := vm.DelegateCall(contract, toAddr, args, gas, interpreter) if err != nil { stack.Push(interpreter.intPool.GetZero()) } else { @@ -872,7 +871,7 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, contract *vm.Contract // Get arguments from the memory. args := memory.Get(inOffset.Int64(), inSize.Int64()) - ret, returnGas, err := interpreter.evm.StaticCall(contract, toAddr, args, gas) + ret, returnGas, err := vm.StaticCall(contract, toAddr, args, gas, interpreter) if err != nil { stack.Push(interpreter.intPool.GetZero()) } else { diff --git a/core/vm/evm/interpreter.go b/core/vm/evm/interpreter.go index 867e72ee1..9ed647c00 100644 --- a/core/vm/evm/interpreter.go +++ b/core/vm/evm/interpreter.go @@ -172,7 +172,6 @@ func (in *EVMInterpreter) Run(contract *vm.Contract, input []byte, readOnly bool if len(contract.Code) == 0 { return nil, nil } - var ( op OpCode // current opcode mem = vm.NewMemory() // bound memory @@ -188,7 +187,6 @@ func (in *EVMInterpreter) Run(contract *vm.Contract, input []byte, readOnly bool logged bool // deferred Tracer should ignore already logged steps ) contract.Input = input - // Reclaim the stack as an int pool when the execution stops defer func() { in.intPool.Put(stack.Data...) @@ -251,6 +249,7 @@ func (in *EVMInterpreter) Run(contract *vm.Contract, input []byte, readOnly bool if err != nil || !contract.UseGas(cost) { return nil, vm.ErrOutOfGas } + if memorySize > 0 { mem.Resize(memorySize) } @@ -284,6 +283,7 @@ func (in *EVMInterpreter) Run(contract *vm.Contract, input []byte, readOnly bool pc++ } } + return nil, nil } @@ -292,3 +292,8 @@ func (in *EVMInterpreter) Run(contract *vm.Contract, input []byte, readOnly bool func (in *EVMInterpreter) CanRun(code []byte) bool { return true } + +// StateDB return StateDB stored in evm +func (in *EVMInterpreter) StateDB() vm.StateDB { + return in.evm.StateDB +} diff --git a/core/vm/evm/runtime/runtime_example_test.go b/core/vm/evm/runtime/runtime_example_test.go index c6050a771..dd50bc373 100644 --- a/core/vm/evm/runtime/runtime_example_test.go +++ b/core/vm/evm/runtime/runtime_example_test.go @@ -21,14 +21,16 @@ import ( "github.com/dexon-foundation/dexon/common" "github.com/dexon-foundation/dexon/core/vm/evm/runtime" + "github.com/dexon-foundation/dexon/core/vm/tools" ) func ExampleExecute() { - ret, _, err := runtime.Execute(common.Hex2Bytes("6060604052600a8060106000396000f360606040526008565b00"), nil, nil) + code := tools.PatchBinary(common.Hex2Bytes("6060604052600a8060106000396000f360606040526008565b00")) + ret, _, err := runtime.Execute(code, nil, nil) if err != nil { fmt.Println(err) } fmt.Println(ret) // Output: - // [96 96 96 64 82 96 8 86 91 0] + // [0 96 96 96 64 82 96 8 86 91 0] } diff --git a/core/vm/evm/runtime/runtime_test.go b/core/vm/evm/runtime/runtime_test.go index 43b8da325..762f3601d 100644 --- a/core/vm/evm/runtime/runtime_test.go +++ b/core/vm/evm/runtime/runtime_test.go @@ -24,7 +24,9 @@ import ( "github.com/dexon-foundation/dexon/accounts/abi" "github.com/dexon-foundation/dexon/common" "github.com/dexon-foundation/dexon/core/state" - vm "github.com/dexon-foundation/dexon/core/vm/evm" + "github.com/dexon-foundation/dexon/core/vm" + "github.com/dexon-foundation/dexon/core/vm/evm" + "github.com/dexon-foundation/dexon/core/vm/tools" "github.com/dexon-foundation/dexon/ethdb" "github.com/dexon-foundation/dexon/params" ) @@ -65,24 +67,25 @@ func TestEVM(t *testing.T) { }() Execute([]byte{ - byte(vm.DIFFICULTY), - byte(vm.TIMESTAMP), - byte(vm.GASLIMIT), - byte(vm.PUSH1), - byte(vm.ORIGIN), - byte(vm.BLOCKHASH), - byte(vm.COINBASE), + byte(evm.DIFFICULTY), + byte(evm.TIMESTAMP), + byte(evm.GASLIMIT), + byte(evm.PUSH1), + byte(evm.ORIGIN), + byte(evm.BLOCKHASH), + byte(evm.COINBASE), }, nil, nil) } func TestExecute(t *testing.T) { ret, _, err := Execute([]byte{ - byte(vm.PUSH1), 10, - byte(vm.PUSH1), 0, - byte(vm.MSTORE), - byte(vm.PUSH1), 32, - byte(vm.PUSH1), 0, - byte(vm.RETURN), + byte(vm.EVM), + byte(evm.PUSH1), 10, + byte(evm.PUSH1), 0, + byte(evm.MSTORE), + byte(evm.PUSH1), 32, + byte(evm.PUSH1), 0, + byte(evm.RETURN), }, nil, nil) if err != nil { t.Fatal("didn't expect error", err) @@ -98,12 +101,13 @@ func TestCall(t *testing.T) { state, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) address := common.HexToAddress("0x0a") state.SetCode(address, []byte{ - byte(vm.PUSH1), 10, - byte(vm.PUSH1), 0, - byte(vm.MSTORE), - byte(vm.PUSH1), 32, - byte(vm.PUSH1), 0, - byte(vm.RETURN), + byte(vm.EVM), + byte(evm.PUSH1), 10, + byte(evm.PUSH1), 0, + byte(evm.MSTORE), + byte(evm.PUSH1), 32, + byte(evm.PUSH1), 0, + byte(evm.RETURN), }) ret, _, err := Call(address, nil, &Config{State: state}) @@ -121,7 +125,7 @@ func BenchmarkCall(b *testing.B) { var definition = `[{"constant":true,"inputs":[],"name":"seller","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[],"name":"abort","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"value","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[],"name":"refund","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"buyer","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[],"name":"confirmReceived","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"state","outputs":[{"name":"","type":"uint8"}],"type":"function"},{"constant":false,"inputs":[],"name":"confirmPurchase","outputs":[],"type":"function"},{"inputs":[],"type":"constructor"},{"anonymous":false,"inputs":[],"name":"Aborted","type":"event"},{"anonymous":false,"inputs":[],"name":"PurchaseConfirmed","type":"event"},{"anonymous":false,"inputs":[],"name":"ItemReceived","type":"event"},{"anonymous":false,"inputs":[],"name":"Refunded","type":"event"}]` var code = common.Hex2Bytes("6060604052361561006c5760e060020a600035046308551a53811461007457806335a063b4146100865780633fa4f245146100a6578063590e1ae3146100af5780637150d8ae146100cf57806373fac6f0146100e1578063c19d93fb146100fe578063d696069714610112575b610131610002565b610133600154600160a060020a031681565b610131600154600160a060020a0390811633919091161461015057610002565b61014660005481565b610131600154600160a060020a039081163391909116146102d557610002565b610133600254600160a060020a031681565b610131600254600160a060020a0333811691161461023757610002565b61014660025460ff60a060020a9091041681565b61013160025460009060ff60a060020a9091041681146101cc57610002565b005b600160a060020a03166060908152602090f35b6060908152602090f35b60025460009060a060020a900460ff16811461016b57610002565b600154600160a060020a03908116908290301631606082818181858883f150506002805460a060020a60ff02191660a160020a179055506040517f72c874aeff0b183a56e2b79c71b46e1aed4dee5e09862134b8821ba2fddbf8bf9250a150565b80546002023414806101dd57610002565b6002805460a060020a60ff021973ffffffffffffffffffffffffffffffffffffffff1990911633171660a060020a1790557fd5d55c8a68912e9a110618df8d5e2e83b8d83211c57a8ddd1203df92885dc881826060a15050565b60025460019060a060020a900460ff16811461025257610002565b60025460008054600160a060020a0390921691606082818181858883f150508354604051600160a060020a0391821694503090911631915082818181858883f150506002805460a060020a60ff02191660a160020a179055506040517fe89152acd703c9d8c7d28829d443260b411454d45394e7995815140c8cbcbcf79250a150565b60025460019060a060020a900460ff1681146102f057610002565b6002805460008054600160a060020a0390921692909102606082818181858883f150508354604051600160a060020a0391821694503090911631915082818181858883f150506002805460a060020a60ff02191660a160020a179055506040517f8616bbbbad963e4e65b1366f1d75dfb63f9e9704bbbf91fb01bec70849906cf79250a15056") - + code = tools.PatchBinary(code) abi, err := abi.JSON(strings.NewReader(definition)) if err != nil { b.Fatal(err) @@ -177,7 +181,7 @@ func benchmarkEVM_Create(bench *testing.B, code string) { EIP155Block: new(big.Int), EIP158Block: new(big.Int), }, - EVMConfig: vm.Config{}, + EVMConfig: evm.Config{}, } // Warm up the intpools and stuff bench.ResetTimer() diff --git a/core/vm/sqlvm/sqlvm.go b/core/vm/sqlvm/sqlvm.go index 258a65d01..fe2eee42b 100644 --- a/core/vm/sqlvm/sqlvm.go +++ b/core/vm/sqlvm/sqlvm.go @@ -5,7 +5,6 @@ import ( "github.com/dexon-foundation/dexon/common" "github.com/dexon-foundation/dexon/core/vm" - "github.com/dexon-foundation/dexon/crypto" "github.com/dexon-foundation/dexon/params" ) @@ -30,21 +29,39 @@ type SQLVM struct { callGasTemp uint64 } -func (sqlvm *SQLVM) Create(caller vm.ContractRef, code []byte, gas uint64, - value *big.Int) (ret []byte, contractAddr common.Address, - leftOverGas uint64, err error) { +func init() { + vm.Register(vm.SQLVM, &SQLVM{}) +} - contractAddr = crypto.CreateAddress(caller.Address(), sqlvm.StateDB.GetNonce(caller.Address())) - return sqlvm.create(caller, &vm.CodeAndHash{Code: code}, gas, value, contractAddr) +func (sqlvm *SQLVM) Create(caller vm.ContractRef, code []byte, gas uint64, value *big.Int, + in vm.Interpreter) ([]byte, common.Address, uint64, error) { + // todo (jm) need to implemnt + return nil, common.Address{}, gas, nil } -// create creates a new contract using code as deployment code. -func (sqlvm *SQLVM) create(caller vm.ContractRef, codeAndHash *vm.CodeAndHash, gas uint64, - value *big.Int, address common.Address) ([]byte, common.Address, uint64, error) { - // Depth check execution. Fail if we're trying to execute above the - if sqlvm.depth > int(params.CallCreateDepth) { - return nil, common.Address{}, gas, vm.ErrDepth - } - // TODO (JM) implement create database contract function +func (sqlvm *SQLVM) Create2(caller vm.ContractRef, code []byte, gas uint64, endowment *big.Int, salt *big.Int, + in vm.Interpreter) ([]byte, common.Address, uint64, error) { + // todo (jm) need to implemnt return nil, common.Address{}, gas, nil } +func (sqlvm *SQLVM) Call(caller vm.ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int, + in vm.Interpreter) ([]byte, uint64, error) { + // todo (jm) need to implemnt + return nil, gas, nil +} +func (sqlvm *SQLVM) CallCode(caller vm.ContractRef, addr common.Address, input []byte, gas uint64, + value *big.Int, in vm.Interpreter) ([]byte, uint64, error) { + // todo (jm) need to implemnt + return nil, gas, nil +} + +func (sqlvm *SQLVM) DelegateCall(caller vm.ContractRef, addr common.Address, input []byte, gas uint64, + in vm.Interpreter) ([]byte, uint64, error) { + // todo (jm) need to implemnt + return nil, gas, nil +} +func (sqlvm *SQLVM) StaticCall(caller vm.ContractRef, addr common.Address, input []byte, gas uint64, + in vm.Interpreter) ([]byte, uint64, error) { + // todo (jm) need to implemnt + return nil, gas, nil +} diff --git a/core/vm/tools/patch.go b/core/vm/tools/patch.go new file mode 100644 index 000000000..7d41fcc98 --- /dev/null +++ b/core/vm/tools/patch.go @@ -0,0 +1,158 @@ +package tools + +import ( + "encoding/binary" + "encoding/hex" + "regexp" +) + +func PatchBinary(input []byte) []byte { + if input == nil { + return nil + } + stringCode := hex.EncodeToString(input) + stringCode = Patch(stringCode) + result, _ := hex.DecodeString(stringCode) + return result +} +func patchPattern1(input string) string { + // Case 1 + // PUSH1 v1 DUP1 PUSH1 v2 PUSH1 v3 CODECOPY + // we have to set v1 = v1 + 1 + r, _ := regexp.Compile("60..8060..60..39") + loc := r.FindStringIndex(input) + if len(loc) > 0 { + valLoc := loc[0]/2 + 1 + bString, _ := hex.DecodeString(input) + bString[valLoc]++ + input = hex.EncodeToString(bString) + } + return input +} +func patchPattern2(input string) string { + // Case 2 + // PUSH2 v1 v2 DUP1 PUSH2 v3 PUSH1 v4 CODECOPY + // we have to set BigEndian(v1,v2)++ + r, _ := regexp.Compile("61....8061....60..39") + loc := r.FindStringIndex(input) + if len(loc) > 0 { + valLoc := loc[0]/2 + 1 + bString, _ := hex.DecodeString(input) + val := binary.BigEndian.Uint16(bString[valLoc : valLoc+2]) + val++ + binary.BigEndian.PutUint16(bString[valLoc:], val) + input = hex.EncodeToString(bString) + } + return input +} +func patchPattern3(input string) string { + // Case 3 + // PUSH1 v1 DUP1 PUSH3 v2 v3 v4 DUP4 CODECOPY + // we have to set BigEndian(v2,v3,v4)++ + r, _ := regexp.Compile("60..8062......8339") + loc := r.FindStringIndex(input) + if len(loc) > 0 { + valLoc := loc[0]/2 + 4 + bString, _ := hex.DecodeString(input) + toConvert := append([]byte{0}, bString[valLoc:valLoc+3]...) + tmpBinary := make([]byte, 4) + val := binary.BigEndian.Uint32(toConvert) + val-- + binary.BigEndian.PutUint32(tmpBinary, val) + bString[valLoc] = tmpBinary[1] + bString[valLoc+1] = tmpBinary[2] + bString[valLoc+2] = tmpBinary[3] + input = hex.EncodeToString(bString) + } + return input +} +func patchPattern4(input string) string { + // Case 4 + // PUSH2 v1 v2 DUP1 PUSH2 v3 DUP4 CODECOPY + // we have to set BigEndian(v1,v2)+2 + r, _ := regexp.Compile("61....8061....8339") + loc := r.FindStringIndex(input) + if len(loc) > 0 { + valLoc := loc[0]/2 + 1 + bString, _ := hex.DecodeString(input) + val := binary.BigEndian.Uint16(bString[valLoc : valLoc+2]) + val = val + 2 + binary.BigEndian.PutUint16(bString[valLoc:], val) + input = hex.EncodeToString(bString) + } + return input +} +func patchPattern5(input string) string { + // Case 5 + // PUSH1 v1 DUP1 PUSH2 v2 v3 DUP4 CODECOPY + // we have to set BigEndian(v2,v3)++ + r, _ := regexp.Compile("60..8061....8339") + loc := r.FindStringIndex(input) + if len(loc) > 0 { + valLoc := loc[0]/2 + 4 + bString, _ := hex.DecodeString(input) + val := binary.BigEndian.Uint16(bString[valLoc : valLoc+2]) + val = val + 1 + binary.BigEndian.PutUint16(bString[valLoc:], val) + input = hex.EncodeToString(bString) + } + return input +} +func patchPattern6(input string) string { + // Case 6 + // SUB DUP1 PUSH2 v2 v3 DUP4 CODECOPY + // we have to set BigEndian(v2,v3)++ + r, _ := regexp.Compile("038061....8339") + loc := r.FindStringIndex(input) + if len(loc) > 0 { + valLoc := loc[0]/2 + 3 + bString, _ := hex.DecodeString(input) + val := binary.BigEndian.Uint16(bString[valLoc : valLoc+2]) + val = val + 1 + binary.BigEndian.PutUint16(bString[valLoc:], val) + input = hex.EncodeToString(bString) + } + return input +} +func addPrefix(input string) string { + r, _ := regexp.Compile("6060604052") + loc := r.FindAllStringIndex(input, -1) + if len(loc) > 0 { + for i, offset := range loc { + insertLoc := offset[0] + i*2 + if !isDelegateCall(input, insertLoc) { + input = input[:insertLoc] + "00" + input[insertLoc:] + } + } + if input[0:2] != "00" { + input = "00" + input + } + } + return input +} +func isDelegateCall(input string, codeStartAt int) bool { + delegateCallPrefix := "6504032353da7150" + prefixLen := len(delegateCallPrefix) + newStartAt := codeStartAt - prefixLen + if newStartAt >= 0 { + if input[newStartAt:newStartAt+prefixLen] == delegateCallPrefix { + return true + } + } + return false +} + +// Patch patch lagacy bytecode(hex string) to new vm interface compatible bytecode +func Patch(input string) string { + if len(input) == 0 { + return input + } + input = patchPattern1(input) + input = patchPattern2(input) + input = patchPattern3(input) + input = patchPattern4(input) + input = patchPattern5(input) + input = patchPattern6(input) + result := addPrefix(input) + return result +} diff --git a/core/vm/tools/patch_example_test.go b/core/vm/tools/patch_example_test.go new file mode 100644 index 000000000..7c093e68f --- /dev/null +++ b/core/vm/tools/patch_example_test.go @@ -0,0 +1,26 @@ +package tools + +import ( + "fmt" +) + +func ExamplePatchPUSH1() { + input := "606060405260068060106000396000f3606060405200" + // 07 ^00 + fmt.Println(Patch(input)) + // Output: + // 00606060405260078060106000396000f300606060405200 +} +func ExamplePatchPUSH2() { + input := "6060604052aabb616fff8606060405200" + // 7000 ^00 + fmt.Println(Patch(input)) + // Output: + // 006060604052aabb616fff800606060405200 +} +func ExamplePatchDelegate() { + input := "6504032353da71506060604052" + fmt.Println(Patch(input)) + // Output: + // 006504032353da71506060604052 +} diff --git a/core/vm/tools/transaction.go b/core/vm/tools/transaction.go new file mode 100644 index 000000000..1ff36a36e --- /dev/null +++ b/core/vm/tools/transaction.go @@ -0,0 +1,263 @@ +package tools + +import ( + "encoding/json" + "errors" + "fmt" + "io" + "math/big" + "sync/atomic" + + "github.com/dexon-foundation/dexon/common" + "github.com/dexon-foundation/dexon/common/hexutil" + "github.com/dexon-foundation/dexon/crypto" + "github.com/dexon-foundation/dexon/crypto/sha3" + "github.com/dexon-foundation/dexon/rlp" +) + +//go:generate gencodec -type txdata -field-override txdataMarshaling -out gen_tx_json.go + +var ( + ErrInvalidSig = errors.New("invalid transaction v, r, s values") +) + +type writeCounter common.StorageSize + +func (c *writeCounter) Write(b []byte) (int, error) { + *c += writeCounter(len(b)) + return len(b), nil +} + +type Transaction struct { + Data txdata + // caches + hash atomic.Value + size atomic.Value + from atomic.Value +} + +type txdata struct { + AccountNonce uint64 `json:"nonce" gencodec:"required"` + Price *big.Int `json:"gasPrice" gencodec:"required"` + GasLimit uint64 `json:"gas" gencodec:"required"` + Recipient *common.Address `json:"to" rlp:"nil"` // nil means contract creation + Amount *big.Int `json:"value" gencodec:"required"` + Payload []byte `json:"input" gencodec:"required"` + + // Signature values + V *big.Int `json:"v" gencodec:"required"` + R *big.Int `json:"r" gencodec:"required"` + S *big.Int `json:"s" gencodec:"required"` + + // This is only used when marshaling to JSON. + Hash *common.Hash `json:"hash" rlp:"-"` +} + +type txdataMarshaling struct { + AccountNonce hexutil.Uint64 + Price *hexutil.Big + GasLimit hexutil.Uint64 + Amount *hexutil.Big + Payload hexutil.Bytes + V *hexutil.Big + R *hexutil.Big + S *hexutil.Big +} + +// Protected returns whether the transaction is protected from replay protection. +func (tx *Transaction) Protected() bool { + return isProtectedV(tx.Data.V) +} +func isProtectedV(V *big.Int) bool { + if V.BitLen() <= 8 { + v := V.Uint64() + return v != 27 && v != 28 + } + // anything not 27 or 28 is considered protected + return true +} + +// EncodeRLP implements rlp.Encoder +func (tx *Transaction) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, &tx.Data) +} + +// DecodeRLP implements rlp.Decoder +func (tx *Transaction) DecodeRLP(s *rlp.Stream) error { + _, size, _ := s.Kind() + err := s.Decode(&tx.Data) + if err == nil { + tx.size.Store(common.StorageSize(rlp.ListSize(size))) + } + + return err +} + +func (tx *Transaction) Size() common.StorageSize { + if size := tx.size.Load(); size != nil { + fmt.Println("tools load") + return size.(common.StorageSize) + } + c := writeCounter(0) + rlp.Encode(&c, &tx.Data) + tx.size.Store(common.StorageSize(c)) + return common.StorageSize(c) +} + +// MarshalJSON encodes the web3 RPC transaction format. +func (tx *Transaction) MarshalJSON() ([]byte, error) { + hash := tx.Hash() + data := tx.Data + data.Hash = &hash + return data.MarshalJSON() +} + +// MarshalJSON marshals as JSON. +func (t txdata) MarshalJSON() ([]byte, error) { + type txdata struct { + AccountNonce hexutil.Uint64 `json:"nonce" gencodec:"required"` + Price *hexutil.Big `json:"gasPrice" gencodec:"required"` + GasLimit hexutil.Uint64 `json:"gas" gencodec:"required"` + Recipient *common.Address `json:"to" rlp:"nil"` + Amount *hexutil.Big `json:"value" gencodec:"required"` + Payload hexutil.Bytes `json:"input" gencodec:"required"` + V *hexutil.Big `json:"v" gencodec:"required"` + R *hexutil.Big `json:"r" gencodec:"required"` + S *hexutil.Big `json:"s" gencodec:"required"` + Hash *common.Hash `json:"hash" rlp:"-"` + } + var enc txdata + enc.AccountNonce = hexutil.Uint64(t.AccountNonce) + enc.Price = (*hexutil.Big)(t.Price) + enc.GasLimit = hexutil.Uint64(t.GasLimit) + enc.Recipient = t.Recipient + enc.Amount = (*hexutil.Big)(t.Amount) + enc.Payload = t.Payload + enc.V = (*hexutil.Big)(t.V) + enc.R = (*hexutil.Big)(t.R) + enc.S = (*hexutil.Big)(t.S) + enc.Hash = t.Hash + return json.Marshal(&enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (t *txdata) UnmarshalJSON(input []byte) error { + type txdata struct { + AccountNonce *hexutil.Uint64 `json:"nonce" gencodec:"required"` + Price *hexutil.Big `json:"gasPrice" gencodec:"required"` + GasLimit *hexutil.Uint64 `json:"gas" gencodec:"required"` + Recipient *common.Address `json:"to" rlp:"nil"` + Amount *hexutil.Big `json:"value" gencodec:"required"` + Payload *hexutil.Bytes `json:"input" gencodec:"required"` + V *hexutil.Big `json:"v" gencodec:"required"` + R *hexutil.Big `json:"r" gencodec:"required"` + S *hexutil.Big `json:"s" gencodec:"required"` + Hash *common.Hash `json:"hash" rlp:"-"` + } + var dec txdata + if err := json.Unmarshal(input, &dec); err != nil { + return err + } + if dec.AccountNonce == nil { + return errors.New("missing required field 'nonce' for txdata") + } + t.AccountNonce = uint64(*dec.AccountNonce) + if dec.Price == nil { + return errors.New("missing required field 'gasPrice' for txdata") + } + t.Price = (*big.Int)(dec.Price) + if dec.GasLimit == nil { + return errors.New("missing required field 'gas' for txdata") + } + t.GasLimit = uint64(*dec.GasLimit) + if dec.Recipient != nil { + t.Recipient = dec.Recipient + } + if dec.Amount == nil { + return errors.New("missing required field 'value' for txdata") + } + t.Amount = (*big.Int)(dec.Amount) + if dec.Payload == nil { + return errors.New("missing required field 'input' for txdata") + } + t.Payload = *dec.Payload + if dec.V == nil { + return errors.New("missing required field 'v' for txdata") + } + t.V = (*big.Int)(dec.V) + if dec.R == nil { + return errors.New("missing required field 'r' for txdata") + } + t.R = (*big.Int)(dec.R) + if dec.S == nil { + return errors.New("missing required field 's' for txdata") + } + t.S = (*big.Int)(dec.S) + if dec.Hash != nil { + t.Hash = dec.Hash + } + return nil +} + +// UnmarshalJSON decodes the web3 RPC transaction format. +func (tx *Transaction) UnmarshalJSON(input []byte) error { + var dec txdata + if err := dec.UnmarshalJSON(input); err != nil { + return err + } + + withSignature := dec.V.Sign() != 0 || dec.R.Sign() != 0 || dec.S.Sign() != 0 + if withSignature { + var V byte + if isProtectedV(dec.V) { + chainID := deriveChainId(dec.V).Uint64() + V = byte(dec.V.Uint64() - 35 - 2*chainID) + } else { + V = byte(dec.V.Uint64() - 27) + } + if !crypto.ValidateSignatureValues(V, dec.R, dec.S, false) { + return ErrInvalidSig + } + } + + *tx = Transaction{Data: dec} + return nil +} + +// deriveChainId derives the chain id from the given v parameter +func deriveChainId(v *big.Int) *big.Int { + if v.BitLen() <= 64 { + v := v.Uint64() + if v == 27 || v == 28 { + return new(big.Int) + } + return new(big.Int).SetUint64((v - 35) / 2) + } + v = new(big.Int).Sub(v, big.NewInt(35)) + return v.Div(v, big.NewInt(2)) +} + +// Hash hashes the RLP encoding of tx. +// It uniquely identifies the transaction. +func (tx *Transaction) Hash() common.Hash { + if hash := tx.hash.Load(); hash != nil { + return hash.(common.Hash) + } + v := rlpHash(tx) + tx.hash.Store(v) + return v +} +func rlpHash(x interface{}) (h common.Hash) { + hw := sha3.NewKeccak256() + rlp.Encode(hw, x) + hw.Sum(h[:0]) + return h +} +func (tx *Transaction) SetFrom(from atomic.Value) { + tx.from.Store(from.Load()) +} +func (tx *Transaction) Gas() uint64 { return tx.Data.GasLimit } +func (tx *Transaction) GasPrice() *big.Int { return new(big.Int).Set(tx.Data.Price) } +func (tx *Transaction) Value() *big.Int { return new(big.Int).Set(tx.Data.Amount) } +func (tx *Transaction) Nonce() uint64 { return tx.Data.AccountNonce } +func (tx *Transaction) CheckNonce() bool { return true } diff --git a/core/vm/vm.go b/core/vm/vm.go new file mode 100644 index 000000000..8d4dffd0b --- /dev/null +++ b/core/vm/vm.go @@ -0,0 +1,105 @@ +package vm + +import ( + "fmt" + "math/big" + + "github.com/dexon-foundation/dexon/common" +) + +const ( + EVM = byte(iota) + SQLVM +) + +var ( + MULTIVM = true +) + +type VM interface { + Create(ContractRef, []byte, uint64, *big.Int, + Interpreter) ([]byte, common.Address, uint64, error) + Create2(ContractRef, []byte, uint64, *big.Int, *big.Int, + Interpreter) ([]byte, common.Address, uint64, error) + Call(ContractRef, common.Address, []byte, uint64, *big.Int, + Interpreter) ([]byte, uint64, error) + CallCode(ContractRef, common.Address, []byte, uint64, + *big.Int, Interpreter) ([]byte, uint64, error) + DelegateCall(ContractRef, common.Address, []byte, uint64, + Interpreter) ([]byte, uint64, error) + StaticCall(ContractRef, common.Address, []byte, uint64, + Interpreter) ([]byte, uint64, error) +} + +type Interpreter interface { + StateDB() StateDB +} + +var vmList map[byte]VM + +func init() { + vmList = make(map[byte]VM) +} +func Register(vmType byte, vm VM) { + vmList[vmType] = vm +} +func Create(caller ContractRef, code []byte, gas uint64, value *big.Int, + interpreter Interpreter) (ret []byte, contractAddr common.Address, + leftOverGas uint64, err error) { + + name, code := getVMAndCode(code) + return vmList[name].Create(caller, code, gas, value, interpreter) +} + +func Create2(caller ContractRef, code []byte, gas uint64, endowment *big.Int, + salt *big.Int, interpreter Interpreter) (ret []byte, + contractAddr common.Address, leftOverGas uint64, err error) { + + name, code := getVMAndCode(code) + return vmList[name].Create2(caller, code, gas, endowment, salt, interpreter) +} + +func Call(caller ContractRef, addr common.Address, input []byte, gas uint64, + value *big.Int, interpreter Interpreter) (ret []byte, leftOverGas uint64, err error) { + + code := interpreter.StateDB().GetCode(addr) + name, _ := getVMAndCode(code) + return vmList[name].Call(caller, addr, input, gas, value, interpreter) +} + +func CallCode(caller ContractRef, addr common.Address, input []byte, gas uint64, + value *big.Int, interpreter Interpreter) (ret []byte, leftOverGas uint64, err error) { + + code := interpreter.StateDB().GetCode(addr) + name, _ := getVMAndCode(code) + return vmList[name].CallCode(caller, addr, input, gas, value, interpreter) +} + +func DelegateCall(caller ContractRef, addr common.Address, input []byte, + gas uint64, interpreter Interpreter) (ret []byte, leftOverGas uint64, err error) { + + code := interpreter.StateDB().GetCode(addr) + name, _ := getVMAndCode(code) + return vmList[name].DelegateCall(caller, addr, input, gas, interpreter) +} + +func StaticCall(caller ContractRef, addr common.Address, input []byte, + gas uint64, interpreter Interpreter) (ret []byte, leftOverGas uint64, err error) { + + code := interpreter.StateDB().GetCode(addr) + name, _ := getVMAndCode(code) + return vmList[name].StaticCall(caller, addr, input, gas, interpreter) +} + +func getVMAndCode(code []byte) (byte, []byte) { + if MULTIVM && len(code) > 0 { + switch code[0] { + case EVM, SQLVM: + return code[0], code[1:] + default: + fmt.Printf("Unknow code prefix %x\n", code[0]) + return EVM, code[1:] + } + } + return EVM, code +} diff --git a/eth/tracers/tracers_test.go b/eth/tracers/tracers_test.go index 783c41b7a..1f2f44b5a 100644 --- a/eth/tracers/tracers_test.go +++ b/eth/tracers/tracers_test.go @@ -34,6 +34,7 @@ import ( "github.com/dexon-foundation/dexon/core/types" "github.com/dexon-foundation/dexon/core/vm" "github.com/dexon-foundation/dexon/core/vm/evm" + "github.com/dexon-foundation/dexon/core/vm/tools" "github.com/dexon-foundation/dexon/crypto" "github.com/dexon-foundation/dexon/ethdb" "github.com/dexon-foundation/dexon/params" @@ -90,6 +91,10 @@ var makeTest = function(tx, rewind) { }, null, 2)); } */ +const ( + gasDiffUnit = 204 + createGasDiff = 200 +) // callTrace is the result of a callTracer run. type callTrace struct { @@ -199,6 +204,24 @@ func TestPrestateTracerCreate2(t *testing.T) { } } +func setTx(toolTx *tools.Transaction, tx *types.Transaction, patchPayload bool) (*types.Transaction, int, error) { + rTx := new(types.Transaction) + // Patch bytecode and load to tx + originalLen := len(toolTx.Data.Payload) + if patchPayload { + toolTx.Data.Payload = tools.PatchBinary(toolTx.Data.Payload) + } + afterLen := len(toolTx.Data.Payload) + payloadLenDiff := afterLen - originalLen + toolTx.Data.GasLimit = toolTx.Data.GasLimit + uint64(payloadLenDiff*gasDiffUnit) + encodeByte, _ := rlp.EncodeToBytes(toolTx) + if err := rlp.DecodeBytes(encodeByte, rTx); err != nil { + return nil, 0, err + } + rTx.SetFrom(tx) + return rTx, payloadLenDiff, nil +} + // Iterates over all the input-output datasets in the tracer test harness and // runs the JavaScript tracers against them. func TestCallTracer(t *testing.T) { @@ -213,7 +236,6 @@ func TestCallTracer(t *testing.T) { file := file // capture range variable t.Run(camel(strings.TrimSuffix(strings.TrimPrefix(file.Name(), "call_tracer_"), ".json")), func(t *testing.T) { t.Parallel() - // Call tracer test found, read if from disk blob, err := ioutil.ReadFile(filepath.Join("testdata", file.Name())) if err != nil { @@ -223,13 +245,29 @@ func TestCallTracer(t *testing.T) { if err := json.Unmarshal(blob, test); err != nil { t.Fatalf("failed to parse testcase: %v", err) } + for k, v := range test.Genesis.Alloc { + v.Code = tools.PatchBinary(v.Code) + test.Genesis.Alloc[k] = v + } // Configure a blockchain with the given prestate tx := new(types.Transaction) + toolTx := new(tools.Transaction) + if err := rlp.DecodeBytes(common.FromHex(test.Input), toolTx); err != nil { + t.Fatalf("failed to parse testcase input: %v", err) + } if err := rlp.DecodeBytes(common.FromHex(test.Input), tx); err != nil { t.Fatalf("failed to parse testcase input: %v", err) } signer := types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number))) + + // Get correct sender address origin, _ := signer.Sender(tx) + tx.AsMessage(signer) + + tx, payloadLenDiff, err := setTx(toolTx, tx, test.Result.Type == "CREATE") + if err != nil { + t.Fatalf("failed to set tx: %v", err) + } context := vm.Context{ CanTransfer: core.CanTransfer, @@ -250,7 +288,6 @@ func TestCallTracer(t *testing.T) { t.Fatalf("failed to create call tracer: %v", err) } evm := evm.NewEVM(context, statedb, test.Genesis.Config, evm.Config{Debug: true, Tracer: tracer}) - msg, err := tx.AsMessage(signer) if err != nil { t.Fatalf("failed to prepare transaction for tracing: %v", err) @@ -269,9 +306,27 @@ func TestCallTracer(t *testing.T) { t.Fatalf("failed to unmarshal trace result: %v", err) } + updateResultInput(test.Result, 0) + *test.Result.Gas += hexutil.Uint64(payloadLenDiff * (gasDiffUnit - 4)) + *test.Result.GasUsed += hexutil.Uint64(payloadLenDiff * (gasDiffUnit - 4)) + if test.Result.Type == "CREATE" { + *test.Result.GasUsed -= hexutil.Uint64(createGasDiff) + } if !reflect.DeepEqual(ret, test.Result) { t.Fatalf("trace mismatch: \nhave %+v\nwant %+v", ret, test.Result) } }) } } +func updateResultInput(result *callTrace, depth int) { + for i := range result.Calls { + updateResultInput(&result.Calls[i], depth+1) + } + if result.Type == "CREATE" { + result.Output = tools.PatchBinary(result.Output) + } + result.Input = tools.PatchBinary(result.Input) + if len(result.Input) > 0 && result.Type == "CREATE" && depth == 0 { + result.Input = result.Input[1:] + } +} diff --git a/les/helper_test.go b/les/helper_test.go index 550fb1905..136c4e33e 100644 --- a/les/helper_test.go +++ b/les/helper_test.go @@ -30,7 +30,8 @@ import ( "github.com/dexon-foundation/dexon/consensus/ethash" "github.com/dexon-foundation/dexon/core" "github.com/dexon-foundation/dexon/core/types" - vm "github.com/dexon-foundation/dexon/core/vm/evm" + "github.com/dexon-foundation/dexon/core/vm/evm" + "github.com/dexon-foundation/dexon/core/vm/tools" "github.com/dexon-foundation/dexon/crypto" "github.com/dexon-foundation/dexon/eth" "github.com/dexon-foundation/dexon/ethdb" @@ -77,7 +78,11 @@ contract test { } } */ - +func init() { + testContractCode = tools.PatchBinary(testContractCode) + testEventEmitterCode = tools.PatchBinary(testEventEmitterCode) + testContractCodeDeployed = tools.PatchBinary(testContractCodeDeployed) +} func testChainGen(i int, block *core.BlockGen) { signer := types.HomesteadSigner{} @@ -164,7 +169,7 @@ func newTestProtocolManager(lightSync bool, blocks int, generator func(int, *cor if lightSync { chain, _ = light.NewLightChain(odr, gspec.Config, engine) } else { - blockchain, _ := core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil) + blockchain, _ := core.NewBlockChain(db, nil, gspec.Config, engine, evm.Config{}, nil) gchain, _ := core.GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, blocks, generator) if _, err := blockchain.InsertChain(gchain); err != nil { panic(err) diff --git a/light/odr_test.go b/light/odr_test.go index 08d4bce32..267d1a346 100644 --- a/light/odr_test.go +++ b/light/odr_test.go @@ -32,6 +32,7 @@ import ( "github.com/dexon-foundation/dexon/core/state" "github.com/dexon-foundation/dexon/core/types" vm "github.com/dexon-foundation/dexon/core/vm/evm" + "github.com/dexon-foundation/dexon/core/vm/tools" "github.com/dexon-foundation/dexon/crypto" "github.com/dexon-foundation/dexon/ethdb" "github.com/dexon-foundation/dexon/params" @@ -222,6 +223,7 @@ func testChainGen(i int, block *core.BlockGen) { nonce := block.TxNonce(acc1Addr) tx2, _ := types.SignTx(types.NewTransaction(nonce, acc2Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, acc1Key) nonce++ + testContractCode = tools.PatchBinary(testContractCode) tx3, _ := types.SignTx(types.NewContractCreation(nonce, big.NewInt(0), 1000000, big.NewInt(0), testContractCode), signer, acc1Key) testContractAddr = crypto.CreateAddress(acc1Addr, nonce) block.AddTx(tx1) diff --git a/tests/state_test.go b/tests/state_test.go index b5f887085..78b2853fb 100644 --- a/tests/state_test.go +++ b/tests/state_test.go @@ -25,7 +25,8 @@ import ( "testing" "github.com/dexon-foundation/dexon/cmd/utils" - vm "github.com/dexon-foundation/dexon/core/vm/evm" + "github.com/dexon-foundation/dexon/core/vm" + "github.com/dexon-foundation/dexon/core/vm/evm" ) func TestState(t *testing.T) { @@ -59,28 +60,30 @@ func TestState(t *testing.T) { key := fmt.Sprintf("%s/%d", subtest.Fork, subtest.Index) name := name + "/" + key t.Run(key, func(t *testing.T) { - withTrace(t, test.gasLimit(subtest), func(vmconfig vm.Config) error { + withTrace(t, test.gasLimit(subtest), func(vmconfig evm.Config) error { + vm.MULTIVM = false _, err := test.Run(subtest, vmconfig) return st.checkFailure(t, name, err) }) }) } }) + vm.MULTIVM = true } // Transactions with gasLimit above this value will not get a VM trace on failure. const traceErrorLimit = 400000 // The VM config for state tests that accepts --vm.* command line arguments. -var testVMConfig = func() vm.Config { - vmconfig := vm.Config{} +var testVMConfig = func() evm.Config { + vmconfig := evm.Config{} flag.StringVar(&vmconfig.EVMInterpreter, utils.EVMInterpreterFlag.Name, utils.EVMInterpreterFlag.Value, utils.EVMInterpreterFlag.Usage) flag.StringVar(&vmconfig.EWASMInterpreter, utils.EWASMInterpreterFlag.Name, utils.EWASMInterpreterFlag.Value, utils.EWASMInterpreterFlag.Usage) flag.Parse() return vmconfig }() -func withTrace(t *testing.T, gasLimit uint64, test func(vm.Config) error) { +func withTrace(t *testing.T, gasLimit uint64, test func(evm.Config) error) { err := test(testVMConfig) if err == nil { return @@ -92,8 +95,8 @@ func withTrace(t *testing.T, gasLimit uint64, test func(vm.Config) error) { } buf := new(bytes.Buffer) w := bufio.NewWriter(buf) - tracer := vm.NewJSONLogger(&vm.LogConfig{DisableMemory: true}, w) - err2 := test(vm.Config{Debug: true, Tracer: tracer}) + tracer := vm.NewJSONLogger(&evm.LogConfig{DisableMemory: true}, w) + err2 := test(evm.Config{Debug: true, Tracer: tracer}) if !reflect.DeepEqual(err, err2) { t.Errorf("different error for second run: %v", err2) } |