Skip to content
This repository has been archived by the owner on Jan 9, 2024. It is now read-only.

GasCost calculation mismatch for opCode CALL/STATICCALL with RPCdaemon #296

Open
lupin012 opened this issue Jun 4, 2022 · 0 comments
Open
Labels
bug Something isn't working

Comments

@lupin012
Copy link
Collaborator

lupin012 commented Jun 4, 2022

Executing the debug_traceTransaction call using the following input:

{
  "jsonrpc":"2.0",
  "method":"debug_traceTransaction",
  "params":[
     "0xeb3803f052e817e41e381b5ac150d0967e37b2e298d9a1dc079750abeeafbead",
     {
       "disableStorage": false,
       "disableMemory": false,
       "disableStack": false
     }
  ],
  "id":1
}

we see a mismatch on gasCost calculation between RPCDaemon/erigon and Silkrpc/Silkworm/Evmone

Erigon:
See core/vm/interpreter.go

//Static portion of gas
cost = operation.constantGas // For tracing (ad esempio per la CALL e 100)

// Dynamic portion of gas
// consume the gas and return an error if not enough gas is available.
// cost is explicitly set so that the capture state defer method can get the proper cost
if operation.dynamicGas != nil {
var dynamicCost uint64
dynamicCost, err = operation.dynamicGas(in.evm, contract, locStack, mem, memorySize)

 cost += dynamicCost // total cost, for debug tracing
 if err != nil || !contract.UseGas(dynamicCost) {
    return nil, ErrOutOfGas
 }

}

So the gasCost is the sum between static part and dynamica one using the gas returned by operation.dynamicGas
Then is called the Tracer:
in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, mem, locStack, in.returnData, contract, in.evm.depth, err)

The routine operation.dynamicGas() in our case is' :
makeCallVariantGasCallEIP2929() in core/vm/operations_acl.go

In the routine contract.Gas (is the gasLeft) (GasLeft - static cost opcode)
if !warmAccess
decrease by coldCost (2500)

gas, err := oldCalculator() // decrease by 1/64 of the gas-left

if warmAccess
return

// In case of a cold access, we temporarily add the cold charge back, and also
// add it to the returned gas. By adding it to the return, it will be charged
// outside of this function, as part of the dynamic gas, and that will make it
// also become correctly reported to tracers.

Using the transaction reported below:

"pc":15411,"op":"DUP7","gas":77277,"gasCost":3,"depth":2
"pc":15412,"op":"GAS","gas":77274,"gasCost":2,"depth":2
"pc":15413,"op":"CALL","gas":77272,"gasCost":76106,"depth":2
==============
"pc":0,"op":"PUSH1","gas":73506,"gasCost":3,"depth":3
"pc":2,"op":"PUSH1","gas":73503,"gasCost":3,"depth":3
"pc":4,"op":"MSTORE","gas":73500,"gasCost":12,"depth":3

in makeCallVariantGasCallEIP2929: 77172 (contract.Gas is already decreased by opcode cost)

if !warmAccess
77172-2500 = 74672 (coldCost)
74672-(74672/64) = 73506 (old-calculator)

73506+2500=76006
After the routine it is summed the static parte (100) = 76106

Seems the gasCost calculation for CALL and any other opcode different. Is it correct


In silkrpc (tracer) with current interface using gas-left and nsg.gas it is not possible calculate gasCost of the CALL in the same way of ERIGON.

call_impl() in silkworm/third_party/evmone/lib/evmone/instructions_calls.cpp

in input we have:

  • gas =
  • gasLeft =

if (state.rev >= EVMC_BERLIN && state.host.access_account(dst) == EVMC_ACCESS_COLD)
decraese gas-left by 2500 (coldCost)

// assign msg.gas = gas

msg.gas = std::min(msg.gas, state.gas_left - state.gas_left / 64);

then it is executed the execute() where the tracer is called
const auto result = state.host.call(msg);

the tracer is called with gas-left and msg.gas with same value. Because during ExecutionState the reset() is caled

using transaction data:

"depth":2,"gas":77277,"gasCost":3,"op":"DUP7","pc":15411
"depth":2,"gas":77274,"gasCost":2,"op":"GAS","pc":15412
"depth":2,"gas":77272,"gasCost":73506,"op":"CALL","pc":15413
--------------
"depth":3,"gas":73506,"gasCost":3,"op":"PUSH1","pc":0
"depth":3,"gas":73503,"gasCost":3,"op":"PUSH1","pc":2
"depth":3,"gas":73500,"gasCost":12,"op":"MSTORE","pc":4

in call_impl() 77172 ( OPcode cost is already decreased)
gas = 77272
gasLeft = 77172

if (state.rev >= EVMC_BERLIN && state.host.access_account(dst) == EVMC_ACCESS_COLD)
state.gas_left -= 2500 (74672)

msg.gas = min(74672, 74672-(74672/64)) // 73506

msg.gas = 73506
gas-left = 74672

const auto result = state.host.call(msg);

the tracer receives:
msg.gas = 73506
gas-left = 73506

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

1 participant