diff options
Diffstat (limited to 'core/vm')
-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 |
10 files changed, 703 insertions, 55 deletions
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 +} |