Skip to content

Commit 44f8fa6

Browse files
committed
feat: untested vm.PrecompileEnvironment.Create() and Create2()
1 parent d2697b1 commit 44f8fa6

File tree

3 files changed

+94
-32
lines changed

3 files changed

+94
-32
lines changed

core/vm/contracts.libevm.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@ const (
6161
CallCode = CallType(CALLCODE)
6262
DelegateCall = CallType(DELEGATECALL)
6363
StaticCall = CallType(STATICCALL)
64+
65+
// Although not technically calls, the CREATE OpCodes also enter a new call
66+
// frame and therefore have similar setup to _outgoing_ *CALL* types. They
67+
// are only used internally, by [environment].
68+
create = CallType(CREATE)
69+
create2 = CallType(CREATE2)
6470
)
6571

6672
func (t CallType) isValid() bool {
@@ -162,6 +168,9 @@ type PrecompileEnvironment interface {
162168
// removed and automatically determined according to the type of call that
163169
// invoked the precompile.
164170
Call(addr common.Address, input []byte, gas uint64, value *uint256.Int, _ ...CallOption) (ret []byte, _ error)
171+
172+
Create(code []byte, gas uint64, value *uint256.Int) ([]byte, common.Address, error)
173+
Create2(code []byte, gas uint64, value, salt *uint256.Int) ([]byte, common.Address, error)
165174
}
166175

167176
func (args *evmCallArgs) env() *environment {

core/vm/environment.libevm.go

Lines changed: 67 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -100,49 +100,63 @@ func (e *environment) BlockHeader() (types.Header, error) {
100100
return *hdr, nil
101101
}
102102

103-
func (e *environment) Call(addr common.Address, input []byte, gas uint64, value *uint256.Int, opts ...CallOption) ([]byte, error) {
104-
return e.callContract(Call, addr, input, gas, value, opts...)
105-
}
106-
107-
func (e *environment) callContract(typ CallType, addr common.Address, input []byte, gas uint64, value *uint256.Int, opts ...CallOption) (retData []byte, retErr error) {
103+
func (e *environment) beforeNewCallFrame(typ CallType, gas uint64, value *uint256.Int, opts ...CallOption) (ContractRef, func(), error) {
108104
// Depth and read-only setting are handled by [EVMInterpreter.Run], which
109105
// isn't used for precompiles, so we need to do it ourselves to maintain the
110106
// expected invariants.
111107
in := e.evm.interpreter
108+
var deferred []func()
112109

113110
in.evm.depth++
114-
defer func() { in.evm.depth-- }()
111+
deferred = append(deferred, func() { in.evm.depth-- })
115112

116113
if e.ReadOnly() && !in.readOnly { // i.e. the precompile was StaticCall()ed
117114
in.readOnly = true
118-
defer func() { in.readOnly = false }()
115+
deferred = append(deferred, func() { in.readOnly = false })
119116
}
120117

121-
var caller ContractRef = e.self
122-
for _, o := range opts {
123-
switch o := o.(type) {
124-
case callOptUNSAFECallerAddressProxy:
125-
// Note that, in addition to being unsafe, this breaks an EVM
126-
// assumption that the caller ContractRef is always a *Contract.
127-
caller = AccountRef(e.self.CallerAddress)
128-
if e.callType == DelegateCall {
129-
// self was created with AsDelegate(), which means that
130-
// CallerAddress was inherited.
131-
caller = AccountRef(e.self.Address())
132-
}
133-
case nil:
134-
default:
135-
return nil, fmt.Errorf("unsupported option %T", o)
118+
var (
119+
caller ContractRef = e.self
120+
config libevmCallConfig
121+
)
122+
config.apply(opts...)
123+
if config.proxyCallerAddressUNSAFE {
124+
// Note that, in addition to being unsafe, this breaks an EVM
125+
// assumption that the caller ContractRef is always a *Contract.
126+
caller = AccountRef(e.self.CallerAddress)
127+
if e.callType == DelegateCall {
128+
// self was created with AsDelegate(), which means that
129+
// CallerAddress was inherited.
130+
caller = AccountRef(e.self.Address())
136131
}
137132
}
138133

139-
if in.readOnly && value != nil && !value.IsZero() {
140-
return nil, ErrWriteProtection
134+
writes := (value != nil && !value.IsZero()) || typ == create || typ == create2
135+
if in.readOnly && writes {
136+
return nil, nil, ErrWriteProtection
141137
}
142138
if !e.UseGas(gas) {
143-
return nil, ErrOutOfGas
139+
return nil, nil, ErrOutOfGas
144140
}
145141

142+
return caller, func() {
143+
for _, fn := range deferred {
144+
fn()
145+
}
146+
}, nil
147+
}
148+
149+
func (e *environment) Call(addr common.Address, input []byte, gas uint64, value *uint256.Int, opts ...CallOption) ([]byte, error) {
150+
return e.callContract(Call, addr, input, gas, value, opts...)
151+
}
152+
153+
func (e *environment) callContract(typ CallType, addr common.Address, input []byte, gas uint64, value *uint256.Int, opts ...CallOption) (retData []byte, retErr error) {
154+
caller, cleanup, err := e.beforeNewCallFrame(typ, gas, value, opts...)
155+
if err != nil {
156+
return nil, err
157+
}
158+
defer cleanup()
159+
146160
if t := e.evm.Config.Tracer; t != nil {
147161
var bigVal *big.Int
148162
if value != nil {
@@ -174,3 +188,31 @@ func (e *environment) callContract(typ CallType, addr common.Address, input []by
174188
return nil, fmt.Errorf("unimplemented precompile call type %v", typ)
175189
}
176190
}
191+
192+
func (e *environment) Create(code []byte, gas uint64, value *uint256.Int) ([]byte, common.Address, error) {
193+
return e.create(create, gas, value, func(caller ContractRef) ([]byte, common.Address, uint64, error) {
194+
return e.evm.Create(caller, code, gas, value)
195+
})
196+
}
197+
198+
func (e *environment) Create2(code []byte, gas uint64, value, salt *uint256.Int) ([]byte, common.Address, error) {
199+
return e.create(create2, gas, value, func(caller ContractRef) ([]byte, common.Address, uint64, error) {
200+
return e.evm.Create2(caller, code, gas, value, salt)
201+
})
202+
}
203+
204+
type creator func(ContractRef) ([]byte, common.Address, uint64, error)
205+
206+
func (e *environment) create(typ CallType, gas uint64, value *uint256.Int, do creator) ([]byte, common.Address, error) {
207+
caller, cleanup, err := e.beforeNewCallFrame(typ, gas, value)
208+
if err != nil {
209+
return nil, common.Address{}, err
210+
}
211+
defer cleanup()
212+
213+
ret, contract, returnGas, err := do(caller)
214+
if err := e.refundGas(returnGas); err != nil {
215+
return nil, common.Address{}, err
216+
}
217+
return ret, contract, err
218+
}

core/vm/options.libevm.go

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,23 @@ package vm
1818

1919
// A CallOption modifies the default behaviour of a contract call.
2020
type CallOption interface {
21-
libevmCallOption() // noop to only allow internally defined options
21+
apply(*libevmCallConfig)
2222
}
2323

24+
type libevmCallConfig struct {
25+
proxyCallerAddressUNSAFE bool
26+
}
27+
28+
func (c *libevmCallConfig) apply(opts ...CallOption) {
29+
for _, o := range opts {
30+
o.apply(c)
31+
}
32+
}
33+
34+
type libevmFuncOpt func(*libevmCallConfig)
35+
36+
func (f libevmFuncOpt) apply(c *libevmCallConfig) { f(c) }
37+
2438
// WithUNSAFECallerAddressProxying results in precompiles making contract calls
2539
// specifying their own caller's address as the caller. This is NOT SAFE for
2640
// regular use as callers of the precompile may not understand that they are
@@ -29,10 +43,7 @@ type CallOption interface {
2943
// Deprecated: this option MUST NOT be used other than to allow migration to
3044
// libevm when backwards compatibility is required.
3145
func WithUNSAFECallerAddressProxying() CallOption {
32-
return callOptUNSAFECallerAddressProxy{}
46+
return libevmFuncOpt(func(c *libevmCallConfig) {
47+
c.proxyCallerAddressUNSAFE = true
48+
})
3349
}
34-
35-
// Deprecated: see [WithUNSAFECallerAddressProxying].
36-
type callOptUNSAFECallerAddressProxy struct{}
37-
38-
func (callOptUNSAFECallerAddressProxy) libevmCallOption() {}

0 commit comments

Comments
 (0)