Skip to content

Commit 0e958f9

Browse files
committed
Merge remote-tracking branch 'origin/master' into release
2 parents 32e824e + c48628f commit 0e958f9

File tree

2 files changed

+67
-40
lines changed

2 files changed

+67
-40
lines changed

kevm-pyk/src/kevm_pyk/kevm.py

Lines changed: 49 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
from __future__ import annotations
22

33
import logging
4-
from typing import TYPE_CHECKING
4+
from collections.abc import Callable
5+
from typing import TYPE_CHECKING, NamedTuple
56

6-
from pyk.cterm import CTerm
7+
from pyk.cterm import CTerm, CTermSymbolic
78
from pyk.kast import KInner
89
from pyk.kast.inner import (
910
KApply,
@@ -12,14 +13,15 @@
1213
KSort,
1314
KToken,
1415
KVariable,
16+
Subst,
1517
bottom_up,
1618
build_assoc,
1719
build_cons,
1820
top_down,
1921
)
2022
from pyk.kast.manip import abstract_term_safely, flatten_label, set_cell
2123
from pyk.kast.pretty import paren
22-
from pyk.kcfg.kcfg import Step
24+
from pyk.kcfg.kcfg import KCFGExtendResult, Step
2325
from pyk.kcfg.semantics import DefaultSemantics
2426
from pyk.kcfg.show import NodePrinter
2527
from pyk.ktool.kprove import KProve
@@ -37,28 +39,55 @@
3739
from pathlib import Path
3840
from typing import Final
3941

40-
from pyk.cterm import CTermSymbolic
41-
from pyk.kast.inner import KAst, Subst
42+
from pyk.kast.inner import KAst
4243
from pyk.kast.outer import KFlatModule
4344
from pyk.kcfg import KCFG
44-
from pyk.kcfg.semantics import KCFGExtendResult
4545
from pyk.ktool.kprint import SymbolTable
4646
from pyk.utils import BugReport
4747

4848
_LOGGER: Final = logging.getLogger(__name__)
4949

50+
CustomStepImpl = Callable[[Subst, CTerm, CTermSymbolic], KCFGExtendResult | None]
51+
52+
53+
class CustomStep(NamedTuple):
54+
"""Encapsulates a custom step definition consisting of an abstract pattern and its execution function."""
55+
56+
pattern: KSequence
57+
exec_fn: CustomStepImpl
58+
59+
def check_pattern_match(self, cterm: CTerm) -> bool:
60+
return self.pattern.match(cterm.cell('K_CELL')) is not None
61+
62+
def try_execute(self, cterm: CTerm, cterm_symbolic: CTermSymbolic) -> KCFGExtendResult | None:
63+
subst = self.pattern.match(cterm.cell('K_CELL'))
64+
if subst is not None:
65+
return self.exec_fn(subst, cterm, cterm_symbolic)
66+
return None
67+
68+
5069
# KEVM class
5170

5271

5372
class KEVMSemantics(DefaultSemantics):
5473
auto_abstract_gas: bool
5574
allow_symbolic_program: bool
56-
_cached_subst: Subst | None
75+
_custom_steps: tuple[CustomStep, ...]
5776

58-
def __init__(self, auto_abstract_gas: bool = False, allow_symbolic_program: bool = False) -> None:
77+
def __init__(
78+
self,
79+
auto_abstract_gas: bool = False,
80+
allow_symbolic_program: bool = False,
81+
custom_step_definitions: tuple[CustomStep, ...] = (),
82+
) -> None:
5983
self.auto_abstract_gas = auto_abstract_gas
6084
self.allow_symbolic_program = allow_symbolic_program
61-
self._cached_subst = None
85+
if custom_step_definitions:
86+
self._custom_steps = (
87+
CustomStep(self._load_pattern, self._exec_load_custom_step),
88+
) + custom_step_definitions
89+
else:
90+
self._custom_steps = (CustomStep(self._load_pattern, self._exec_load_custom_step),)
6291

6392
@staticmethod
6493
def is_functional(term: KInner) -> bool:
@@ -145,11 +174,12 @@ def _replace(term: KInner) -> KInner:
145174

146175
return CTerm(config=bottom_up(_replace, cterm.config), constraints=cterm.constraints)
147176

148-
def custom_step(self, cterm: CTerm, _cterm_symbolic: CTermSymbolic) -> KCFGExtendResult | None:
149-
if self._check_load_pattern(cterm):
150-
return self._exec_load_custom_step(cterm)
151-
else:
152-
return None
177+
def custom_step(self, cterm: CTerm, cterm_symbolic: CTermSymbolic) -> KCFGExtendResult | None:
178+
for c_step in self._custom_steps:
179+
result = c_step.try_execute(cterm, cterm_symbolic)
180+
if result is not None:
181+
return result
182+
return None
153183

154184
@staticmethod
155185
def cut_point_rules(
@@ -197,26 +227,16 @@ def terminal_rules(break_every_step: bool) -> list[str]:
197227
terminal_rules.append('EVM.step')
198228
return terminal_rules
199229

200-
def _check_load_pattern(self, cterm: CTerm) -> bool:
201-
"""Given a CTerm, check if the rule 'EVM.program.load' is at the top of the K_CELL.
202-
203-
This method checks if the `EVM.program.load` rule is at the top of the `K_CELL` in the given `cterm`.
204-
If the rule matches, the resulting substitution is cached in `_cached_subst` for later use in `custom_step`
205-
:param cterm: The CTerm representing the current state of the proof node.
206-
:return: `True` if the pattern matches and a custom step can be made; `False` otherwise.
207-
"""
208-
load_pattern = KSequence([KApply('loadProgram', KVariable('###BYTECODE')), KVariable('###CONTINUATION')])
209-
self._cached_subst = load_pattern.match(cterm.cell('K_CELL'))
210-
return self._cached_subst is not None
230+
@property
231+
def _load_pattern(self) -> KSequence:
232+
return KSequence([KApply('loadProgram', KVariable('###BYTECODE')), KVariable('###CONTINUATION')])
211233

212-
def _exec_load_custom_step(self, cterm: CTerm) -> KCFGExtendResult:
234+
def _exec_load_custom_step(self, subst: Subst, cterm: CTerm, _c: CTermSymbolic) -> KCFGExtendResult:
213235
"""Given a CTerm, update the JUMPDESTS_CELL and PROGRAM_CELL if the rule 'EVM.program.load' is at the top of the K_CELL.
214236
215237
:param cterm: CTerm of a proof node.
216238
:return: If the K_CELL matches the load_pattern, a Step with depth 1 is returned together with the new configuration, also registering that the `EVM.program.load` rule has been applied.
217239
"""
218-
subst = self._cached_subst
219-
assert subst is not None
220240
bytecode_sections = flatten_label('_+Bytes__BYTES-HOOKED_Bytes_Bytes_Bytes', subst['###BYTECODE'])
221241
jumpdests_set = compute_jumpdests(bytecode_sections)
222242
new_cterm = CTerm.from_kast(set_cell(cterm.kast, 'JUMPDESTS_CELL', jumpdests_set))
@@ -225,7 +245,7 @@ def _exec_load_custom_step(self, cterm: CTerm) -> KCFGExtendResult:
225245
return Step(new_cterm, 1, (), ['EVM.program.load'], cut=True)
226246

227247
def can_make_custom_step(self, cterm: CTerm) -> bool:
228-
return self._check_load_pattern(cterm)
248+
return any(c_step.check_pattern_match(cterm) for c_step in self._custom_steps)
229249

230250
def is_mergeable(self, ct1: CTerm, ct2: CTerm) -> bool:
231251
"""Given two CTerms of Edges' targets, check if they are mergeable.

kevm-pyk/src/kevm_pyk/kproj/evm-semantics/evm.md

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1274,29 +1274,32 @@ Operators that require access to the rest of the Ethereum network world-state ca
12741274
```k
12751275
syntax UnStackOp ::= "BALANCE"
12761276
// ------------------------------
1277-
rule <k> BALANCE ACCT => #accessAccounts ACCT ~> BAL ~> #push ... </k>
1277+
rule [balance.true]:
1278+
<k> BALANCE ACCT => #accessAccounts ACCT ~> BAL ~> #push ... </k>
12781279
<account>
12791280
<acctID> ACCT </acctID>
12801281
<balance> BAL </balance>
12811282
...
12821283
</account>
12831284
1284-
rule <k> BALANCE ACCT => #accessAccounts ACCT ~> 0 ~> #push ... </k> [owise]
1285+
rule [balance.false]: <k> BALANCE ACCT => #accessAccounts ACCT ~> 0 ~> #push ... </k> [owise]
12851286
12861287
syntax UnStackOp ::= "EXTCODESIZE"
12871288
// ----------------------------------
1288-
rule <k> EXTCODESIZE ACCT => #accessAccounts ACCT ~> lengthBytes(CODE) ~> #push ... </k>
1289+
rule [extcodesize.true]:
1290+
<k> EXTCODESIZE ACCT => #accessAccounts ACCT ~> lengthBytes(CODE) ~> #push ... </k>
12891291
<account>
12901292
<acctID> ACCT </acctID>
12911293
<code> CODE </code>
12921294
...
12931295
</account>
12941296
1295-
rule <k> EXTCODESIZE ACCT => #accessAccounts ACCT ~> 0 ~> #push ... </k> [owise]
1297+
rule [extcodesize.false]: <k> EXTCODESIZE ACCT => #accessAccounts ACCT ~> 0 ~> #push ... </k> [owise]
12961298
12971299
syntax UnStackOp ::= "EXTCODEHASH"
12981300
// ----------------------------------
1299-
rule <k> EXTCODEHASH ACCT => #accessAccounts ACCT ~> keccak(CODE) ~> #push ... </k>
1301+
rule [extcodehash.true]:
1302+
<k> EXTCODEHASH ACCT => #accessAccounts ACCT ~> keccak(CODE) ~> #push ... </k>
13001303
<account>
13011304
<acctID> ACCT </acctID>
13021305
<code> CODE:Bytes </code>
@@ -1306,19 +1309,21 @@ Operators that require access to the rest of the Ethereum network world-state ca
13061309
</account>
13071310
requires notBool #accountEmpty(CODE, NONCE, BAL)
13081311
1309-
rule <k> EXTCODEHASH ACCT => #accessAccounts ACCT ~> 0 ~> #push ... </k> [owise]
1312+
rule [extcodehash.false]: <k> EXTCODEHASH ACCT => #accessAccounts ACCT ~> 0 ~> #push ... </k> [owise]
13101313
13111314
syntax QuadStackOp ::= "EXTCODECOPY"
13121315
// ------------------------------------
1313-
rule <k> EXTCODECOPY ACCT MEMSTART PGMSTART WIDTH => #accessAccounts ACCT ... </k>
1316+
rule [extcodecopy.true]:
1317+
<k> EXTCODECOPY ACCT MEMSTART PGMSTART WIDTH => #accessAccounts ACCT ... </k>
13141318
<localMem> LM => LM [ MEMSTART := #range(PGM, PGMSTART, WIDTH) ] </localMem>
13151319
<account>
13161320
<acctID> ACCT </acctID>
13171321
<code> PGM </code>
13181322
...
13191323
</account>
13201324
1321-
rule <k> EXTCODECOPY ACCT MEMSTART _ WIDTH => #accessAccounts ACCT ... </k>
1325+
rule [extcodecopy.false]:
1326+
<k> EXTCODECOPY ACCT MEMSTART _ WIDTH => #accessAccounts ACCT ... </k>
13221327
<localMem> LM => LM [ MEMSTART := #padToWidth(WIDTH, .Bytes) ] </localMem> [owise]
13231328
```
13241329

@@ -1392,7 +1397,7 @@ The various `CALL*` (and other inter-contract control flow) operations will be d
13921397
| "#checkNonceExceeded" Int
13931398
| "#checkDepthExceeded"
13941399
| "#call" Int Int Int Int Int Bytes Bool
1395-
| "#callWithCode" Int Int Int Bytes Int Int Bytes Bool
1400+
| "#callWithCode" Int Int Int Bytes Int Int Bytes Bool [symbol(callwithcode_check_fork)]
13961401
| "#mkCall" Int Int Int Bytes Int Bytes Bool
13971402
// -----------------------------------------------------------------------------------
13981403
rule <k> #checkBalanceUnderflow ACCT VALUE => #refund GCALL ~> #pushCallStack ~> #pushWorldState ~> #end EVMC_BALANCE_UNDERFLOW ... </k>
@@ -1443,7 +1448,8 @@ The various `CALL*` (and other inter-contract control flow) operations will be d
14431448
14441449
rule <k> #checkCall ACCT VALUE => #checkBalanceUnderflow ACCT VALUE ~> #checkDepthExceeded ... </k>
14451450
1446-
rule <k> #call ACCTFROM ACCTTO ACCTCODE VALUE APPVALUE ARGS STATIC
1451+
rule [call.true]:
1452+
<k> #call ACCTFROM ACCTTO ACCTCODE VALUE APPVALUE ARGS STATIC
14471453
=> #callWithCode ACCTFROM ACCTTO ACCTCODE CODE VALUE APPVALUE ARGS STATIC
14481454
...
14491455
</k>
@@ -1453,7 +1459,8 @@ The various `CALL*` (and other inter-contract control flow) operations will be d
14531459
...
14541460
</account>
14551461
1456-
rule <k> #call ACCTFROM ACCTTO ACCTCODE VALUE APPVALUE ARGS STATIC
1462+
rule [call.false]:
1463+
<k> #call ACCTFROM ACCTTO ACCTCODE VALUE APPVALUE ARGS STATIC
14571464
=> #callWithCode ACCTFROM ACCTTO ACCTCODE .Bytes VALUE APPVALUE ARGS STATIC
14581465
...
14591466
</k> [owise]

0 commit comments

Comments
 (0)