Skip to content

Commit

Permalink
Implement EIP-7702 part 3: Delegation Designation and Gas Costs
Browse files Browse the repository at this point in the history
  • Loading branch information
jangko committed Sep 18, 2024
1 parent 6c4bf7b commit acaf447
Show file tree
Hide file tree
Showing 13 changed files with 262 additions and 78 deletions.
66 changes: 66 additions & 0 deletions nimbus/core/eip7702.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Nimbus
# Copyright (c) 2024 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
# http://www.apache.org/licenses/LICENSE-2.0)
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or
# http://opensource.org/licenses/MIT)
# at your option. This file may not be copied, modified, or distributed except
# according to those terms.

{.push raises: [].}

import
../evm/code_bytes,
results,
stew/assign2,
eth/common/eth_types,
eth/common/eth_types_rlp,
eth/keys

const
DelegationPrefix = [0xef.byte, 0x01, 0x00]
Magic = 0x05

func authority*(auth: Authorization): Opt[EthAddress] =
var w = initRlpWriter()
w.appendRawBytes([Magic.byte])
w.append(auth.chainId.uint64)
w.append(auth.address)
w.append(auth.nonce)
let sigHash = keccakHash(w.finish())

var bytes: array[65, byte]
assign(bytes.toOpenArray(0, 31), auth.R.toBytesBE())
assign(bytes.toOpenArray(32, 63), auth.S.toBytesBE())
bytes[64] = auth.y_parity.byte

let sig = Signature.fromRaw(bytes).valueOr:
return Opt.none(EthAddress)

let pubkey = recover(sig, SkMessage(sigHash.data)).valueOr:
return Opt.none(EthAddress)

ok(pubkey.toCanonicalAddress())

func parseDelegation*(code: CodeBytesRef): bool =
if code.len != 23:
return false

if not code.hasPrefix(DelegationPrefix):
return false

true

func addressToDelegation*(auth: EthAddress): array[23, byte] =
assign(result.toOpenArray(0, 2), DelegationPrefix)
assign(result.toOpenArray(3, 22), auth)

func parseDelegationAddress*(code: CodeBytesRef): Opt[EthAddress] =
if code.len != 23:
return Opt.none(EthAddress)

if not code.hasPrefix(DelegationPrefix):
return Opt.none(EthAddress)

Opt.some(slice[20](code, 3, 22))
21 changes: 20 additions & 1 deletion nimbus/db/ledger/backend/accounts_ledger.nim
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ import
"../../.."/[constants, utils/utils],
../../access_list as ac_access_list,
"../.."/[core_db, storage_types, transient_storage],
../../aristo/aristo_blobify
../../aristo/aristo_blobify,
../../../core/eip7702

const
debugAccountsLedgerRef = false
Expand Down Expand Up @@ -491,6 +492,24 @@ proc getCodeSize*(ac: AccountsLedgerRef, address: EthAddress): int =

acc.code.len()

proc resolveCodeHash*(ac: AccountsLedgerRef, address: EthAddress): Hash256 =
let code = ac.getCode(address)
let delegateTo = parseDelegationAddress(code).valueOr:
return emptyEthAccount.codeHash
ac.getCodeHash(delegateTo)

proc resolveCode*(ac: AccountsLedgerRef, address: EthAddress): CodeBytesRef =
let code = ac.getCode(address)
let delegateTo = parseDelegationAddress(code).valueOr:
return CodeBytesRef()
ac.getCode(delegateTo)

proc resolveCodeSize*(ac: AccountsLedgerRef, address: EthAddress): int =
let code = ac.getCode(address)
let delegateTo = parseDelegationAddress(code).valueOr:
return 0
ac.getCodeSize(delegateTo)

proc getCommittedStorage*(ac: AccountsLedgerRef, address: EthAddress, slot: UInt256): UInt256 =
let acc = ac.getAccount(address, false)
if acc.isNil:
Expand Down
12 changes: 12 additions & 0 deletions nimbus/db/ledger/base.nim
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,15 @@ proc getAccountProof*(ldg: LedgerRef, eAddr: EthAddress): seq[seq[byte]] =
proc getStorageProof*(ldg: LedgerRef, eAddr: EthAddress, slots: openArray[UInt256]): seq[seq[seq[byte]]] =
result = ldg.ac.getStorageProof(eAddr, slots)

proc resolveCode*(ldg: LedgerRef, eAddr: EthAddress): CodeBytesRef =
ldg.ac.resolveCode(eAddr)

proc resolveCodeHash*(ldg: LedgerRef, eAddr: EthAddress): Hash256 =
ldg.ac.resolveCodeHash(eAddr)

proc resolveCodeSize*(ldg: LedgerRef, eAddr: EthAddress): int =
ldg.ac.resolveCodeSize(eAddr)

# ------------------------------------------------------------------------------
# Public virtual read-only methods
# ------------------------------------------------------------------------------
Expand All @@ -324,6 +333,9 @@ func inAccessList*(db: ReadOnlyStateDB, eAddr: EthAddress, slot: UInt256): bool
func getTransientStorage*(db: ReadOnlyStateDB, eAddr: EthAddress, slot: UInt256): UInt256 {.borrow.}
func getAccountProof*(db: ReadOnlyStateDB, eAddr: EthAddress): seq[seq[byte]] {.borrow.}
func getStorageProof*(db: ReadOnlyStateDB, eAddr: EthAddress, slots: openArray[UInt256]): seq[seq[seq[byte]]] {.borrow.}
proc resolveCodeHash*(db: ReadOnlyStateDB, eAddr: EthAddress): Hash256 {.borrow.}
proc resolveCode*(db: ReadOnlyStateDB, eAddr: EthAddress): CodeBytesRef {.borrow.}
proc resolveCodeSize*(db: ReadOnlyStateDB, eAddr: EthAddress): int {.borrow.}

# ------------------------------------------------------------------------------
# End
Expand Down
9 changes: 8 additions & 1 deletion nimbus/evm/code_bytes.nim
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
# at your option. This file may not be copied, modified, or distributed except according to those terms.

import stew/byteutils, results, ./interpreter/op_codes
import
stew/byteutils,
stew/assign2,
results,
./interpreter/op_codes

export results

Expand Down Expand Up @@ -83,3 +87,6 @@ func hasPrefix*(a: CodeBytesRef, b: openArray[byte]): bool =
if a.bytes[i] != b[i]:
return false
true

func slice*[N: static[int]](a: CodeBytesRef, b, c: int): array[N, byte] =
assign(result, a.bytes.toOpenArray(b, c))
31 changes: 29 additions & 2 deletions nimbus/evm/computation.nim
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,29 @@ template getTransientStorage*(c: Computation, slot: UInt256): UInt256 =
c.vmState.readOnlyStateDB.
getTransientStorage(c.msg.contractAddress, slot)

template resolveCodeSize*(c: Computation, address: EthAddress): uint =
when evmc_enabled:
c.host.getCodeSize(address)
else:
uint(c.vmState.readOnlyStateDB.resolveCodeSize(address))

template resolveCodeHash*(c: Computation, address: EthAddress): Hash256 =
when evmc_enabled:
c.host.getCodeHash(address)
else:
let
db = c.vmState.readOnlyStateDB
if not db.accountExists(address) or db.isEmptyAccount(address):
default(Hash256)
else:
db.resolveCodeHash(address)

template resolveCode*(c: Computation, address: EthAddress): CodeBytesRef =
when evmc_enabled:
CodeBytesRef.init(c.host.copyCode(address))
else:
c.vmState.readOnlyStateDB.resolveCode(address)

proc newComputation*(vmState: BaseVMState, sysCall: bool, message: Message,
salt: ContractSalt = ZERO_CONTRACTSALT,
authorizationList: openArray[Authorization] = []): Computation =
Expand All @@ -242,8 +265,12 @@ proc newComputation*(vmState: BaseVMState, sysCall: bool, message: Message,
result.code = CodeStream.init(message.data)
message.data = @[]
else:
result.code = CodeStream.init(
vmState.readOnlyStateDB.getCode(message.codeAddress))
if vmState.fork >= FkPrague:
result.code = CodeStream.init(
vmState.readOnlyStateDB.resolveCode(message.codeAddress))
else:
result.code = CodeStream.init(
vmState.readOnlyStateDB.getCode(message.codeAddress))
assign(result.authorizationList, authorizationList)

func newComputation*(vmState: BaseVMState, sysCall: bool,
Expand Down
10 changes: 5 additions & 5 deletions nimbus/evm/interpreter/gas_costs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ type
kind*: Op
isNewAccount*: bool
gasLeft*: GasInt
gasCallEIP2929*: GasInt
gasCallEIPs*: GasInt
contractGas*: UInt256
currentMemSize*: GasNatural
memOffset*: GasNatural
Expand Down Expand Up @@ -375,9 +375,9 @@ template gasCosts(fork: EVMFork, prefix, ResultGasCostsName: untyped) =
# Both gasCost and childGasLimit are always on positive side

var gasLeft = params.gasLeft
if gasLeft < params.gasCallEIP2929:
if gasLeft < params.gasCallEIPs:
return err(opErr(OutOfGas))
gasLeft -= params.gasCallEIP2929
gasLeft -= params.gasCallEIPs

var gasCost: GasInt = `prefix gasMemoryExpansion`(
params.currentMemSize,
Expand Down Expand Up @@ -422,11 +422,11 @@ template gasCosts(fork: EVMFork, prefix, ResultGasCostsName: untyped) =
return err(gasErr(GasIntOverflow))
childGasLimit = params.contractGas.truncate(GasInt)

if gasCost.u256 + childGasLimit.u256 + params.gasCallEIP2929.u256 > high(GasInt).u256:
if gasCost.u256 + childGasLimit.u256 + params.gasCallEIPs.u256 > high(GasInt).u256:
return err(gasErr(GasIntOverflow))

gasCost += childGasLimit
gasCost += params.gasCallEIP2929
gasCost += params.gasCallEIPs

# Ccallgas - Gas sent to the child message
if not value.isZero and params.kind in {Call, CallCode}:
Expand Down
40 changes: 24 additions & 16 deletions nimbus/evm/interpreter/op_handlers/oph_call.nim
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import
../../../constants,
../../evm_errors,
../../../common/evmforks,
../../../core/eip7702,
../../computation,
../../memory,
../../stack,
Expand Down Expand Up @@ -59,8 +60,20 @@ type
memOffset: int
memLength: int
contractAddress: EthAddress
gasCallEIP2929: GasInt
gasCallEIPs: GasInt

proc gasCallEIP2929(c: Computation, address: EthAddress): GasInt =
when evmc_enabled:
if c.host.accessAccount(address) == EVMC_ACCESS_COLD:
return ColdAccountAccessCost - WarmStorageReadCost
else:
c.vmState.mutateStateDB:
if not db.inAccessList(address):
db.accessList(address)

# The WarmStorageReadCostEIP2929 (100) is already deducted in
# the form of a constant `gasCall`
return ColdAccountAccessCost - WarmStorageReadCost

proc updateStackAndParams(q: var LocalParams; c: Computation) =
c.stack.lsTop(0)
Expand All @@ -81,17 +94,12 @@ proc updateStackAndParams(q: var LocalParams; c: Computation) =
# because it will affect `c.gasMeter.gasRemaining`
# and further `childGasLimit`
if FkBerlin <= c.fork:
when evmc_enabled:
if c.host.accessAccount(q.codeAddress) == EVMC_ACCESS_COLD:
q.gasCallEIP2929 = ColdAccountAccessCost - WarmStorageReadCost
else:
c.vmState.mutateStateDB:
if not db.inAccessList(q.codeAddress):
db.accessList(q.codeAddress)

# The WarmStorageReadCostEIP2929 (100) is already deducted in
# the form of a constant `gasCall`
q.gasCallEIP2929 = ColdAccountAccessCost - WarmStorageReadCost
q.gasCallEIPs = gasCallEIP2929(c, q.codeAddress)

if FkPrague <= c.fork:
let delegateTo = parseDelegationAddress(c.getCode(q.codeAddress))
if delegateTo.isSome:
q.gasCallEIPs += gasCallEIP2929(c, delegateTo[])

proc callParams(c: Computation): EvmResult[LocalParams] =
## Helper for callOp()
Expand Down Expand Up @@ -228,7 +236,7 @@ proc callOp(cpt: VmCpt): EvmResultVoid =
kind: Call,
isNewAccount: not cpt.accountExists(p.contractAddress),
gasLeft: cpt.gasMeter.gasRemaining,
gasCallEIP2929: p.gasCallEIP2929,
gasCallEIPs: p.gasCallEIPs,
contractGas: p.gas,
currentMemSize: cpt.memory.len,
memOffset: p.memOffset,
Expand Down Expand Up @@ -300,7 +308,7 @@ proc callCodeOp(cpt: VmCpt): EvmResultVoid =
kind: CallCode,
isNewAccount: not cpt.accountExists(p.contractAddress),
gasLeft: cpt.gasMeter.gasRemaining,
gasCallEIP2929: p.gasCallEIP2929,
gasCallEIPs: p.gasCallEIPs,
contractGas: p.gas,
currentMemSize: cpt.memory.len,
memOffset: p.memOffset,
Expand Down Expand Up @@ -373,7 +381,7 @@ proc delegateCallOp(cpt: VmCpt): EvmResultVoid =
kind: DelegateCall,
isNewAccount: not cpt.accountExists(p.contractAddress),
gasLeft: cpt.gasMeter.gasRemaining,
gasCallEIP2929: p.gasCallEIP2929,
gasCallEIPs: p.gasCallEIPs,
contractGas: p.gas,
currentMemSize: cpt.memory.len,
memOffset: p.memOffset,
Expand Down Expand Up @@ -440,7 +448,7 @@ proc staticCallOp(cpt: VmCpt): EvmResultVoid =
kind: StaticCall,
isNewAccount: not cpt.accountExists(p.contractAddress),
gasLeft: cpt.gasMeter.gasRemaining,
gasCallEIP2929: p.gasCallEIP2929,
gasCallEIPs: p.gasCallEIPs,
contractGas: p.gas,
currentMemSize: cpt.memory.len,
memOffset: p.memOffset,
Expand Down
3 changes: 3 additions & 0 deletions nimbus/evm/interpreter/op_handlers/oph_defs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ const
VmOpCancunAndLater* =
VmOpShanghaiAndLater - {FkShanghai}

VmOpPragueAndLater* =
VmOpCancunAndLater - {FkCancun}

# ------------------------------------------------------------------------------
# End
# ------------------------------------------------------------------------------
Loading

0 comments on commit acaf447

Please sign in to comment.