Skip to content

Commit

Permalink
LFVM: move addressInAccessList to getAccessCost (#730)
Browse files Browse the repository at this point in the history
* rerwork addressInAccessList into calculateAccessCost

* remove calculateAccessCost function and move it into genericCall

* add tests for account access cost in generic call

* move access cost calculation to a function

* add comments, move revision check before checking access status

* adjust berlin stati costs to not account for warm access

* add test ensuring genericCall charges for warm/cold access

* fix call ops static costs
  • Loading branch information
facuMH authored Sep 16, 2024
1 parent f941629 commit dd9c7d6
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 41 deletions.
36 changes: 7 additions & 29 deletions go/interpreter/lfvm/gas.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,13 @@ func getBerlinGasPriceInternal(op OpCode) tosca.Gas {
case BALANCE:
gp = 100
case CALL:
gp = 100
gp = 0
case CALLCODE:
gp = 100
gp = 0
case STATICCALL:
gp = 100
gp = 0
case DELEGATECALL:
gp = 100
gp = 0
case SELFDESTRUCT:
gp = 5000
}
Expand Down Expand Up @@ -223,11 +223,11 @@ func getStaticGasPriceInternal(op OpCode) tosca.Gas {
case CREATE2:
return 32000
case CALL:
return 700 // Should be 100 according to evm.code
return 700
case CALLCODE:
return 700
case STATICCALL:
return 700 // Should be 100 according to evm.code
return 700
case RETURN:
return 0
case STOP:
Expand All @@ -237,7 +237,7 @@ func getStaticGasPriceInternal(op OpCode) tosca.Gas {
case INVALID:
return 0
case DELEGATECALL:
return 700 // Should be 100 according to evm.code
return 700
case SELFDESTRUCT:
return 0 // should be 5000 according to evm.code
}
Expand Down Expand Up @@ -343,28 +343,6 @@ func gasEip2929AccountCheck(c *context, address tosca.Address) error {
return nil
}

func addressInAccessList(c *context) (warmAccess bool, coldCost tosca.Gas, err error) {
warmAccess = true
if c.isAtLeast(tosca.R09_Berlin) {
addr := tosca.Address(c.stack.peekN(1).Bytes20())
// Check slot presence in the access list
//lint:ignore SA1019 deprecated functions to be migrated in #616
warmAccess = c.context.IsAddressInAccessList(addr)
// The WarmStorageReadCostEIP2929 (100) is already deducted in the form of a constant cost, so
// the cost to charge for cold access, if any, is Cold - Warm
coldCost = ColdAccountAccessCostEIP2929 - WarmStorageReadCostEIP2929
if !warmAccess {
c.context.AccessAccount(addr)
// Charge the remaining difference here already, to correctly calculate available
// gas for call
if !c.useGas(coldCost) {
return false, 0, errOutOfGas
}
}
}
return warmAccess, coldCost, nil
}

func gasSelfdestruct(c *context) tosca.Gas {
gas := SelfdestructGasEIP150
var address = tosca.Address(c.stack.peekN(0).Bytes20())
Expand Down
25 changes: 13 additions & 12 deletions go/interpreter/lfvm/instructions.go
Original file line number Diff line number Diff line change
Expand Up @@ -1082,11 +1082,16 @@ func neededMemorySize(c *context, offset, size *uint256.Int) (uint64, error) {
return offset.Uint64() + size.Uint64(), nil
}

func genericCall(c *context, kind tosca.CallKind) {
warmAccess, coldCost, err := addressInAccessList(c)
if err != nil {
return
func getAccessCost(accessStatus tosca.AccessStatus) tosca.Gas {
// EIP-2929 says that cold access cost is 2600 and warm is 100.
// (https://eips.ethereum.org/EIPS/eip-2929)
if accessStatus == tosca.ColdAccess {
return tosca.Gas(2600)
}
return tosca.Gas(100)
}

func genericCall(c *context, kind tosca.CallKind) {
stack := c.stack
value := uint256.NewInt(0)

Expand Down Expand Up @@ -1119,6 +1124,10 @@ func genericCall(c *context, kind tosca.CallKind) {
}

baseGas := c.memory.getExpansionCosts(needed_memory_size)
// from berlin onwards access cost changes depending on warm/cold access.
if c.isAtLeast(tosca.R09_Berlin) {
baseGas += getAccessCost(c.context.AccessAccount(toAddr))
}
checkGas := func(cost tosca.Gas) bool {
return 0 <= cost && cost <= c.gas
}
Expand Down Expand Up @@ -1148,14 +1157,6 @@ func genericCall(c *context, kind tosca.CallKind) {
}

cost := callGas(c.gas, baseGas, provided_gas)
if !warmAccess {
// 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.
c.gas += coldCost
baseGas += coldCost
}
if !c.useGas(baseGas + cost) {
return
}
Expand Down
76 changes: 76 additions & 0 deletions go/interpreter/lfvm/instructions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -882,3 +882,79 @@ func TestExpansionCostOverflow(t *testing.T) {
}
}
}

func TestGetAccessCost_RespondsWithProperGasPrice(t *testing.T) {
if want, got := tosca.Gas(100), getAccessCost(tosca.WarmAccess); want != got {
t.Errorf("unexpected gas cost, wanted %d, got %d", want, got)
}
if want, got := tosca.Gas(2600), getAccessCost(tosca.ColdAccess); want != got {
t.Errorf("unexpected gas cost, wanted %d, got %d", want, got)
}
}

func TestCall_ChargesNothingForColdAccessBeforeBerlin(t *testing.T) {

ctrl := gomock.NewController(t)
runContext := tosca.NewMockRunContext(ctrl)
runContext.EXPECT().Call(tosca.Call, gomock.Any()).Return(tosca.CallResult{}, nil)
ctxt := context{
params: tosca.Parameters{
BlockParameters: tosca.BlockParameters{
Revision: tosca.R07_Istanbul,
},
},
stack: NewStack(),
memory: NewMemory(),
context: runContext,
gas: 0,
}

ctxt.stack.stackPointer = 8

genericCall(&ctxt, tosca.Call)

if ctxt.gas != 0 {
t.Errorf("unexpected gas cost, wanted 0, got %v", ctxt.gas)
}
if ctxt.status != statusRunning {
t.Errorf("unexpected status, wanted RUNNING, got %v", ctxt.status)
}
}

func TestCall_ChargesForAccessAfterBerlin(t *testing.T) {

for _, accessStatus := range []tosca.AccessStatus{tosca.WarmAccess, tosca.ColdAccess} {

ctrl := gomock.NewController(t)
runContext := tosca.NewMockRunContext(ctrl)
runContext.EXPECT().AccessAccount(gomock.Any()).Return(accessStatus)
runContext.EXPECT().Call(tosca.Call, gomock.Any()).Return(tosca.CallResult{}, nil)
delta := tosca.Gas(1)
ctxt := context{
params: tosca.Parameters{
BlockParameters: tosca.BlockParameters{
Revision: tosca.R09_Berlin,
},
},
stack: NewStack(),
memory: NewMemory(),
context: runContext,
gas: 2600 + delta,
}
ctxt.stack.stackPointer = 8

genericCall(&ctxt, tosca.Call)

want := tosca.Gas(delta)
if accessStatus == tosca.WarmAccess {
want = 2500 + delta
}
if ctxt.gas != want {
t.Errorf("unexpected gas cost, wanted %v, got %v", want, ctxt.gas)
}

if ctxt.status != statusRunning {
t.Errorf("unexpected status, wanted RUNNING, got %v", ctxt.status)
}
}
}

0 comments on commit dd9c7d6

Please sign in to comment.