Skip to content

Commit 01d1ab4

Browse files
committed
feat(core/vm): add dynamic precompile contract capabilities where gas cost is evaluated at runtime
1 parent e58ec5d commit 01d1ab4

File tree

1 file changed

+49
-9
lines changed

1 file changed

+49
-9
lines changed

core/vm/contracts.go

Lines changed: 49 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,22 @@ type PrecompiledContract interface {
5252
Address() common.Address
5353
}
5454

55+
var _ PrecompiledContract = (*ecrecover)(nil)
56+
57+
type DynamicPrecompile interface {
58+
// DynamicRun, similar to `Run`, executes the precompiled contract, except it
59+
// calculates the contract gas usage at runtime (dynamically) rather than
60+
// based on the value from `RequiredGas`.
61+
DynamicRun(
62+
evmObj *EVM,
63+
sender common.Address,
64+
contract *Contract,
65+
readonly bool,
66+
calledFromDelegatedCall bool,
67+
) (outBz []byte, gasCost uint64, err error)
68+
PrecompiledContract
69+
}
70+
5571
// PrecompiledContracts contains the precompiled contracts supported at the given fork.
5672
type PrecompiledContracts map[common.Address]PrecompiledContract
5773

@@ -236,14 +252,29 @@ func RunPrecompiledContract(
236252
calledFromDelegatedCall bool,
237253
) (ret []byte, remainingGas uint64, err error) {
238254
logger := evm.Config.Tracer
239-
gasCost := p.RequiredGas(input)
240-
if suppliedGas < gasCost {
241-
return nil, 0, ErrOutOfGas
242-
}
243-
if logger != nil && logger.OnGasChange != nil {
244-
logger.OnGasChange(suppliedGas, suppliedGas-gasCost, tracing.GasChangeCallPrecompiledContract)
255+
256+
pDyn, isDynamic := p.(DynamicPrecompile)
257+
if !isDynamic {
258+
gasCost := p.RequiredGas(input)
259+
if suppliedGas < gasCost {
260+
return nil, 0, ErrOutOfGas
261+
}
262+
if logger != nil && logger.OnGasChange != nil {
263+
logger.OnGasChange(suppliedGas, suppliedGas-gasCost, tracing.GasChangeCallPrecompiledContract)
264+
}
265+
suppliedGas -= gasCost
266+
contract := &Contract{
267+
CallerAddress: caller.Address(),
268+
caller: caller,
269+
self: AccountRef(p.Address()),
270+
Gas: suppliedGas,
271+
value: value,
272+
Input: input,
273+
}
274+
output, err := p.Run(evm, sender, contract, readOnly, calledFromDelegatedCall)
275+
return output, suppliedGas, err
245276
}
246-
suppliedGas -= gasCost
277+
247278
contract := &Contract{
248279
CallerAddress: caller.Address(),
249280
caller: caller,
@@ -252,8 +283,17 @@ func RunPrecompiledContract(
252283
value: value,
253284
Input: input,
254285
}
255-
output, err := p.Run(evm, sender, contract, readOnly, calledFromDelegatedCall)
256-
return output, suppliedGas, err
286+
output, gasCost, err := pDyn.DynamicRun(evm, sender, contract, readOnly, calledFromDelegatedCall)
287+
if err != nil {
288+
return output, 0, err
289+
}
290+
if gasCost > suppliedGas {
291+
return output, 0, ErrOutOfGas
292+
}
293+
if logger != nil && logger.OnGasChange != nil {
294+
logger.OnGasChange(suppliedGas, suppliedGas-gasCost, tracing.GasChangeCallPrecompiledContract)
295+
}
296+
return output, suppliedGas - gasCost, nil
257297
}
258298

259299
// ecrecover implemented as a native contract.

0 commit comments

Comments
 (0)