forked from celo-org/celo-blockchain
-
Notifications
You must be signed in to change notification settings - Fork 0
/
contract_call.go
121 lines (101 loc) · 3.97 KB
/
contract_call.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
package contracts
import (
"math/big"
"github.com/aaronwinter/celo-blockchain/accounts/abi"
"github.com/aaronwinter/celo-blockchain/common"
"github.com/aaronwinter/celo-blockchain/common/hexutil"
"github.com/aaronwinter/celo-blockchain/core/vm"
"github.com/aaronwinter/celo-blockchain/log"
)
// Method represents a contract's method
type Method struct {
abi *abi.ABI
method string
maxGas uint64
}
// NewMethod creates a new Method
func NewMethod(abi *abi.ABI, method string, maxGas uint64) Method {
return Method{
abi: abi,
method: method,
maxGas: maxGas,
}
}
// Bind returns a BoundMethod instance which can be used to call the contract method represented by am
// and residing at contracAddress.
func (am Method) Bind(contractAddress common.Address) *BoundMethod {
return &BoundMethod{
Method: am,
resolveAddress: noopResolver(contractAddress),
}
}
// encodeCall will encodes the msg into []byte format for EVM consumption
func (am Method) encodeCall(args ...interface{}) ([]byte, error) {
return am.abi.Pack(am.method, args...)
}
// decodeResult will decode the result of msg execution into the result parameter
func (am Method) decodeResult(result interface{}, output []byte) error {
if result == nil {
return nil
}
return am.abi.Unpack(result, am.method, output)
}
// NewBoundMethod constructs a new bound method instance bound to the given address.
func NewBoundMethod(contractAddress common.Address, abi *abi.ABI, methodName string, maxGas uint64) *BoundMethod {
return NewMethod(abi, methodName, maxGas).Bind(contractAddress)
}
func NewRegisteredContractMethod(registryId common.Hash, abi *abi.ABI, methodName string, maxGas uint64) *BoundMethod {
return &BoundMethod{
Method: NewMethod(abi, methodName, maxGas),
resolveAddress: func(vmRunner vm.EVMRunner) (common.Address, error) {
return resolveAddressForCall(vmRunner, registryId, methodName)
},
}
}
// BoundMethod represents a Method that is bounded to an address
// In particular, instead of address we use an address resolver to cope the fact
// that addresses need to be obtained from the Registry before making a call
type BoundMethod struct {
Method
resolveAddress func(vm.EVMRunner) (common.Address, error)
}
// Query executes the method with the given EVMRunner as a read only action, the returned
// value is unpacked into result.
func (bm *BoundMethod) Query(vmRunner vm.EVMRunner, result interface{}, args ...interface{}) error {
return bm.run(vmRunner, result, true, nil, args...)
}
// Execute executes the method with the given EVMRunner and unpacks the return value into result.
// If the method does not return a value then result should be nil.
func (bm *BoundMethod) Execute(vmRunner vm.EVMRunner, result interface{}, value *big.Int, args ...interface{}) error {
return bm.run(vmRunner, result, false, value, args...)
}
func (bm *BoundMethod) run(vmRunner vm.EVMRunner, result interface{}, readOnly bool, value *big.Int, args ...interface{}) error {
defer meterExecutionTime(bm.method)()
contractAddress, err := bm.resolveAddress(vmRunner)
if err != nil {
return err
}
logger := log.New("to", contractAddress, "method", bm.method)
input, err := bm.encodeCall(args...)
if err != nil {
logger.Error("Error invoking evm function: can't encode method arguments", "args", args, "err", err)
return err
}
var output []byte
if readOnly {
output, err = vmRunner.Query(contractAddress, input, bm.maxGas)
} else {
output, err = vmRunner.Execute(contractAddress, input, bm.maxGas, value)
}
if err != nil {
message, _ := unpackError(output)
logger.Error("Error invoking evm function: EVM call failure", "input", hexutil.Encode(input), "maxgas", bm.maxGas, "err", err, "message", message)
return err
}
if err := bm.decodeResult(result, output); err != nil {
logger.Error("Error invoking evm function: can't unpack result", "err", err, "maxgas", bm.maxGas)
return err
}
logger.Trace("EVM call successful", "input", hexutil.Encode(input), "output", hexutil.Encode(output))
return nil
}