forked from celo-org/celo-blockchain
/
evm.go
162 lines (138 loc) · 6.44 KB
/
evm.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// Copyright 2017 The Celo Authors
// This file is part of the celo library.
//
// The celo library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The celo library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the celo library. If not, see <http://www.gnu.org/licenses/>.
package contract_comm
import (
"math/big"
"reflect"
"time"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/contract_comm/errors"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
)
var (
emptyMessage = types.NewMessage(common.HexToAddress("0x0"), nil, 0, common.Big0, 0, common.Big0, nil, nil, common.Big0, []byte{}, false)
internalEvmHandlerSingleton *InternalEVMHandler
// Metrics timers to track the execution time of calls made from the system to core contracts.
systemCallTimers = make(map[string]metrics.Timer)
)
// An EVM handler to make calls to smart contracts from within geth
type InternalEVMHandler struct {
chain vm.ChainContext
}
func MakeStaticCall(registryId [32]byte, abi abi.ABI, funcName string, args []interface{}, returnObj interface{}, gas uint64, header *types.Header, state vm.StateDB) (uint64, error) {
return makeCallWithContractId(registryId, abi, funcName, args, returnObj, gas, nil, header, state, true)
}
func MakeCall(registryId [32]byte, abi abi.ABI, funcName string, args []interface{}, returnObj interface{}, gas uint64, value *big.Int, header *types.Header, state vm.StateDB, finaliseState bool) (uint64, error) {
gasLeft, err := makeCallWithContractId(registryId, abi, funcName, args, returnObj, gas, value, header, state, false)
if err == nil && finaliseState {
state.Finalise(true)
}
return gasLeft, err
}
func MakeStaticCallWithAddress(scAddress common.Address, abi abi.ABI, funcName string, args []interface{}, returnObj interface{}, gas uint64, header *types.Header, state vm.StateDB) (uint64, error) {
return makeCallFromSystem(scAddress, abi, funcName, args, returnObj, gas, nil, header, state, true)
}
func GetRegisteredAddress(registryId [32]byte, header *types.Header, state vm.StateDB) (*common.Address, error) {
vmevm, err := createEVM(header, state)
if err != nil {
return nil, err
}
return vm.GetRegisteredAddressWithEvm(registryId, vmevm)
}
func createEVM(header *types.Header, state vm.StateDB) (*vm.EVM, error) {
// Normally, when making an evm call, we should use the current block's state. However,
// there are times (e.g. retrieving the set of validators when an epoch ends) that we need
// to call the evm using the currently mined block. In that case, the header and state params
// will be non nil.
if internalEvmHandlerSingleton == nil {
return nil, errors.ErrNoInternalEvmHandlerSingleton
}
if header == nil {
header = internalEvmHandlerSingleton.chain.CurrentHeader()
}
if state == nil || reflect.ValueOf(state).IsNil() {
var err error
state, err = internalEvmHandlerSingleton.chain.State()
if err != nil {
log.Error("Error in retrieving the state from the blockchain", "err", err)
return nil, err
}
}
// The EVM Context requires a msg, but the actual field values don't really matter for this case.
// Putting in zero values.
context := vm.NewEVMContext(emptyMessage, header, internalEvmHandlerSingleton.chain, nil)
evm := vm.NewEVM(context, state, internalEvmHandlerSingleton.chain.Config(), *internalEvmHandlerSingleton.chain.GetVMConfig())
return evm, nil
}
func makeCallFromSystem(scAddress common.Address, abi abi.ABI, funcName string, args []interface{}, returnObj interface{}, gas uint64, value *big.Int, header *types.Header, state vm.StateDB, static bool) (uint64, error) {
// Record a metrics data point about execution time.
start := time.Now()
timer, ok := systemCallTimers[funcName]
if !ok {
timer = metrics.NewRegisteredTimer("contract_comm/systemcall/"+funcName, nil)
systemCallTimers[funcName] = timer
}
defer timer.UpdateSince(start)
vmevm, err := createEVM(header, state)
if err != nil {
return 0, err
}
var gasLeft uint64
if static {
gasLeft, err = vmevm.StaticCallFromSystem(scAddress, abi, funcName, args, returnObj, gas)
} else {
gasLeft, err = vmevm.CallFromSystem(scAddress, abi, funcName, args, returnObj, gas, value)
}
if err != nil {
log.Error("Error when invoking evm function", "err", err, "funcName", funcName, "static", static, "address", scAddress, "args", args, "gas", gas, "gasLeft", gasLeft, "value", value)
return gasLeft, err
}
return gasLeft, nil
}
func SetInternalEVMHandler(chain vm.ChainContext) {
if internalEvmHandlerSingleton == nil {
log.Trace("Setting the InternalEVMHandler Singleton")
internalEvmHandler := InternalEVMHandler{
chain: chain,
}
internalEvmHandlerSingleton = &internalEvmHandler
}
}
func makeCallWithContractId(registryId [32]byte, abi abi.ABI, funcName string, args []interface{}, returnObj interface{}, gas uint64, value *big.Int, header *types.Header, state vm.StateDB, static bool) (uint64, error) {
scAddress, err := GetRegisteredAddress(registryId, header, state)
if err != nil {
if err == errors.ErrSmartContractNotDeployed {
log.Debug("Contract not yet registered", "function", funcName, "registryId", hexutil.Encode(registryId[:]))
return 0, err
} else if err == errors.ErrRegistryContractNotDeployed {
log.Debug("Registry contract not yet deployed", "function", funcName, "registryId", hexutil.Encode(registryId[:]))
return 0, err
} else {
log.Error("Error in getting registered address", "function", funcName, "registryId", hexutil.Encode(registryId[:]), "err", err)
return 0, err
}
}
gasLeft, err := makeCallFromSystem(*scAddress, abi, funcName, args, returnObj, gas, value, header, state, static)
if err != nil {
log.Error("Error in executing function on registered contract", "function", funcName, "registryId", hexutil.Encode(registryId[:]), "err", err)
}
return gasLeft, err
}