@@ -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.
5672type 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