Add StateManager set & getStorage

This commit is contained in:
Karl Floersch 2020-05-13 14:45:46 -04:00
parent 9de0a7a2d6
commit 7f0923b35e
5 changed files with 443 additions and 122 deletions

3
.gitignore vendored
View file

@ -38,6 +38,9 @@ profile.cov
# VS Code
.vscode
# vim
*.swp
# dashboard
/dashboard/assets/flow-typed
/dashboard/assets/node_modules

View file

@ -43,9 +43,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
@ -207,7 +207,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

View file

@ -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
}

398
tests/StateManagerABI.json Normal file
View file

@ -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"
}
]

View file

@ -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),
}
}