From 37c62ff042ed4967dc6f143ab825ebf032647533 Mon Sep 17 00:00:00 2001 From: Karl Floersch Date: Wed, 13 May 2020 14:45:46 -0400 Subject: [PATCH] Add StateManager set & getStorage --- .gitignore | 3 + core/vm/evm.go | 8 +- core/vm/state_manager.go | 39 ++-- tests/StateManagerABI.json | 398 +++++++++++++++++++++++++++++++++++++ tests/ovm_test.go | 117 ++--------- 5 files changed, 443 insertions(+), 122 deletions(-) create mode 100644 tests/StateManagerABI.json diff --git a/.gitignore b/.gitignore index 1ee8b83022..b40b40e46e 100644 --- a/.gitignore +++ b/.gitignore @@ -38,6 +38,9 @@ profile.cov # VS Code .vscode +# vim +*.swp + # dashboard /dashboard/assets/flow-typed /dashboard/assets/node_modules diff --git a/core/vm/evm.go b/core/vm/evm.go index ffda158919..5a668d6818 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -42,9 +42,9 @@ type ( // run runs the given contract and takes care of running precompiles with a fallback to the byte code interpreter. func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, error) { - if contract.Address() == StateManagerAddress { - return callStateManager(input, evm, contract) - } + if contract.Address() == StateManagerAddress { + return callStateManager(input, evm, contract) + } if contract.CodeAddr != nil { precompiles := PrecompiledContractsHomestead @@ -208,7 +208,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas to = AccountRef(addr) snapshot = evm.StateDB.Snapshot() ) - if !evm.StateDB.Exist(addr) && addr != StateManagerAddress { + if !evm.StateDB.Exist(addr) && addr != StateManagerAddress { precompiles := PrecompiledContractsHomestead if evm.chainRules.IsByzantium { precompiles = PrecompiledContractsByzantium diff --git a/core/vm/state_manager.go b/core/vm/state_manager.go index 8634200ba7..86854ff3ad 100644 --- a/core/vm/state_manager.go +++ b/core/vm/state_manager.go @@ -1,42 +1,39 @@ package vm import ( - "os" + "os" - "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" ) var ( - StateManagerAddress = common.HexToAddress(os.Getenv("STATE_MANAGER_ADDRESS")) + StateManagerAddress = common.HexToAddress(os.Getenv("STATE_MANAGER_ADDRESS")) ) -type ovmOperation func(*EVM, *Contract, []byte) ([]byte, error) +type stateManagerFunction func(*EVM, *Contract, []byte) ([]byte, error) type methodId [4]byte -var funcs = map[string]ovmOperation{ - "getStorage(address,bytes32)": getStorage, - "setStorage(address,bytes32,bytes32)": setStorage, +var funcs = map[string]stateManagerFunction{ + "getStorage(address,bytes32)": getStorage, + "setStorage(address,bytes32,bytes32)": setStorage, } -var methodIds map[[4]byte]ovmOperation +var methodIds map[[4]byte]stateManagerFunction var executionMangerBytecode []byte - func init() { - methodIds = make(map[[4]byte]ovmOperation, len(funcs)) + methodIds = make(map[[4]byte]stateManagerFunction, len(funcs)) for methodSignature, f := range funcs { - methodIds[MethodSignatureToMethodId(methodSignature)] = f + methodIds[MethodSignatureToMethodId(methodSignature)] = f } } - func MethodSignatureToMethodId(methodSignature string) [4]byte { var methodId [4]byte copy(methodId[:], crypto.Keccak256([]byte(methodSignature))) return methodId } - func callStateManager(input []byte, evm *EVM, contract *Contract) (ret []byte, err error) { var methodId [4]byte copy(methodId[:], input[:4]) @@ -45,14 +42,16 @@ func callStateManager(input []byte, evm *EVM, contract *Contract) (ret []byte, e } func setStorage(evm *EVM, contract *Contract, input []byte) (ret []byte, err error) { - key := common.BytesToHash(input[4:36]) - val := common.BytesToHash(input[36:68]) - evm.StateDB.SetState(contract.Address(), key, val) - return nil, nil + address := common.BytesToAddress(input[4:36]) + key := common.BytesToHash(input[36:68]) + val := common.BytesToHash(input[68:100]) + evm.StateDB.SetState(address, key, val) + return nil, nil } + func getStorage(evm *EVM, contract *Contract, input []byte) (ret []byte, err error) { - key := common.BytesToHash(input[4:36]) - val := evm.StateDB.GetState(contract.Address(), key) + address := common.BytesToAddress(input[4:36]) + key := common.BytesToHash(input[36:68]) + val := evm.StateDB.GetState(address, key) return val.Bytes(), nil - return []byte{}, nil } diff --git a/tests/StateManagerABI.json b/tests/StateManagerABI.json new file mode 100644 index 0000000000..02d159f9db --- /dev/null +++ b/tests/StateManagerABI.json @@ -0,0 +1,398 @@ +[ + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "_ovmContractAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "_codeContractAddress", + "type": "address" + } + ], + "name": "associateCodeContract", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "_ovmContractAddress", + "type": "address" + } + ], + "name": "getCodeContractAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "_codeContractAddress", + "type": "address" + } + ], + "name": "getCodeContractBytecode", + "outputs": [ + { + "internalType": "bytes", + "name": "codeContractBytecode", + "type": "bytes" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "_codeContractAddress", + "type": "address" + } + ], + "name": "getCodeContractHash", + "outputs": [ + { + "internalType": "bytes32", + "name": "_codeContractHash", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "_ovmContractAddress", + "type": "address" + } + ], + "name": "getOvmContractNonce", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "_ovmContractAddress", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "_slot", + "type": "bytes32" + } + ], + "name": "getStorage", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "_ovmContractAddress", + "type": "address" + } + ], + "name": "incrementOvmContractNonce", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "_ovmContractAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_value", + "type": "uint256" + } + ], + "name": "setOvmContractNonce", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "_ovmContractAddress", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "_slot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_value", + "type": "bytes32" + } + ], + "name": "setStorage", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } +], +"evm": { + "bytecode": { + "linkReferences": {}, + "object": "", + "opcodes": "", + "sourceMap": "" + }, + "deployedBytecode": { + "linkReferences": {}, + "object": "", + "opcodes": "", + "sourceMap": "" + } +}, +"interface": [ + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "_ovmContractAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "_codeContractAddress", + "type": "address" + } + ], + "name": "associateCodeContract", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "_ovmContractAddress", + "type": "address" + } + ], + "name": "getCodeContractAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "_codeContractAddress", + "type": "address" + } + ], + "name": "getCodeContractBytecode", + "outputs": [ + { + "internalType": "bytes", + "name": "codeContractBytecode", + "type": "bytes" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "_codeContractAddress", + "type": "address" + } + ], + "name": "getCodeContractHash", + "outputs": [ + { + "internalType": "bytes32", + "name": "_codeContractHash", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "_ovmContractAddress", + "type": "address" + } + ], + "name": "getOvmContractNonce", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "_ovmContractAddress", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "_slot", + "type": "bytes32" + } + ], + "name": "getStorage", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "_ovmContractAddress", + "type": "address" + } + ], + "name": "incrementOvmContractNonce", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "_ovmContractAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_value", + "type": "uint256" + } + ], + "name": "setOvmContractNonce", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "_ovmContractAddress", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "_slot", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_value", + "type": "bytes32" + } + ], + "name": "setStorage", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/tests/ovm_test.go b/tests/ovm_test.go index ae04aa76fb..19ff866b9c 100644 --- a/tests/ovm_test.go +++ b/tests/ovm_test.go @@ -2,9 +2,12 @@ package tests import ( "bytes" + "io/ioutil" "math/big" + "strings" "testing" + "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" @@ -13,8 +16,6 @@ import ( "github.com/ethereum/go-ethereum/params" ) -var KEY = common.FromHex("0102030000000000000000000000000000000000000000000000000000000000") -var VALUE = common.FromHex("0405060000000000000000000000000000000000000000000000000000000000") var chainConfig params.ChainConfig func init() { @@ -32,72 +33,33 @@ func init() { } func TestSloadAndStore(t *testing.T) { - vm.StateManagerAddress = common.HexToAddress("42") + rawStateManagerAbi, _ := ioutil.ReadFile("./StateManagerABI.json") + stateManagerAbi, _ := abi.JSON(strings.NewReader(string(rawStateManagerAbi))) state := newState() - setStorageMethodId := vm.MethodSignatureToMethodId("setStorage(address,bytes32,bytes32)") - storeCode := setStorageMethodId[:] - storeCode = append(storeCode, KEY...) - storeCode = append(storeCode, VALUE...) - getStorageMethodId := vm.MethodSignatureToMethodId("getStorage(address,bytes32)") - loadCode := getStorageMethodId[:] - loadCode = append(loadCode, KEY...) - call(t, state, vm.StateManagerAddress, storeCode) - codeReturnValue, _ := call(t, state, vm.StateManagerAddress, loadCode) + address := common.HexToAddress("9999999999999999999999999999999999999999") + key := [32]byte{} + value := [32]byte{} + copy(key[:], []byte("hello")) + copy(value[:], []byte("world")) - if !bytes.Equal(VALUE, codeReturnValue) { - t.Errorf("Expected %020x; got %020x", VALUE, codeReturnValue) + storeCalldata, _ := stateManagerAbi.Pack("setStorage", address, key, value) + getCalldata, _ := stateManagerAbi.Pack("getStorage", address, key) + + call(t, state, vm.StateManagerAddress, storeCalldata) + getStorageReturnValue, _ := call(t, state, vm.StateManagerAddress, getCalldata) + + if !bytes.Equal(value[:], getStorageReturnValue) { + t.Errorf("Expected %020x; got %020x", value[:], getStorageReturnValue) } } -/* - callCode generates EVM bytecode which makes a single CALL with call data as - it's input. -*/ -func callCode(addr common.Address) []byte { - output := []byte{} - output = append(output, []byte{ - byte(vm.CALLDATASIZE), - byte(vm.PUSH1), 0, - byte(vm.PUSH1), 0, - byte(vm.CALLDATACOPY), - }...) - output = append(output, pushN(0)) - output = append(output, int64ToBytes(0)...) - output = append(output, pushN(0)) - output = append(output, int64ToBytes(0)...) - output = append(output, pushN(0)) - output = append(output, int64ToBytes(0)...) - output = append(output, pushN(0)) - output = append(output, int64ToBytes(0)...) - output = append(output, byte(vm.CALLDATASIZE)) - output = append(output, pushN(0)) - output = append(output, int64ToBytes(0)...) - output = append(output, pushN(0)) - output = append(output, int64ToBytes(0)...) - output = append(output, []byte{ - byte(vm.PUSH20)}...) - output = append(output, addr.Bytes()...) - output = append(output, []byte{ - byte(vm.GAS), - byte(vm.CALL), - byte(vm.POP), - byte(vm.RETURNDATASIZE), - byte(vm.PUSH1), 0, - byte(vm.PUSH1), 0, - byte(vm.RETURNDATACOPY), - byte(vm.RETURNDATASIZE), - byte(vm.PUSH1), 0, - byte(vm.RETURN), - }...) - return output -} - func newState() *state.StateDB { db := state.NewDatabase(rawdb.NewMemoryDatabase()) state, _ := state.New(common.Hash{}, db, nil) return state } + func call(t *testing.T, state *state.StateDB, address common.Address, callData []byte) ([]byte, error) { returnValue, _, err := runtime.Call(address, callData, &runtime.Config{ State: state, @@ -106,44 +68,3 @@ func call(t *testing.T, state *state.StateDB, address common.Address, callData [ return returnValue, err } - -func int64ToBytes(n int64) []byte { - if bytes.Equal(big.NewInt(n).Bytes(), []byte{}) { - return []byte{0} - } else { - return big.NewInt(n).Bytes() - } -} -func pushN(n int64) byte { - return byte(int(vm.PUSH1) + byteLength(n) - 1) -} -func byteLength(n int64) int { - if bytes.Equal(big.NewInt(n).Bytes(), []byte{}) { - return 1 - } else { - return len(big.NewInt(n).Bytes()) - } -} - -func mockPurityChecker(pure bool) []byte { - var pureByte byte - - if pure { - pureByte = 1 - } else { - pureByte = 0 - } - - return []byte{ - byte(vm.PUSH1), - pureByte, - byte(vm.PUSH1), - 0, - byte(vm.MSTORE8), - byte(vm.PUSH1), - 1, - byte(vm.PUSH1), - 0, - byte(vm.RETURN), - } -}