Skip to content

Commit 2276824

Browse files
committed
support storage variables
1 parent 66afe8d commit 2276824

File tree

9 files changed

+2068
-801
lines changed

9 files changed

+2068
-801
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,4 @@ skip-string-normalization = true
7272

7373
[tool.mypy]
7474
disallow_untyped_defs = true
75-
exclude = "src/tests/integration/test-data"
75+
exclude = "src/tests/integration/test-data"

src/kontrol/prove.py

Lines changed: 53 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
from pyk.proof import ProofStatus
3232
from pyk.proof.proof import Proof
3333
from pyk.proof.reachability import APRFailureInfo, APRProof
34-
from pyk.utils import hash_str, run_process_2, unique
34+
from pyk.utils import hash_str, run_process_2, single, unique
3535
from rich.progress import Progress, SpinnerColumn, TaskID, TextColumn, TimeElapsedColumn
3636

3737
from natspec.natspec import Natspec
@@ -815,6 +815,7 @@ def _method_to_cfg(
815815

816816
for final_node in final_states:
817817
new_init_cterm = _update_cterm_from_node(init_cterm, final_node, config_type, keep_vars)
818+
new_init_cterm = add_natspec_preconditions(new_init_cterm, method, contract, foundry)
818819
new_node = cfg.create_node(new_init_cterm)
819820
if graft_setup_proof:
820821
cfg.create_edge(final_node.id, new_node.id, depth=1)
@@ -824,6 +825,7 @@ def _method_to_cfg(
824825
)
825826
new_node_ids.append(new_node.id)
826827
else:
828+
init_cterm = add_natspec_preconditions(init_cterm, method, contract, foundry)
827829
cfg = KCFG()
828830
init_node = cfg.create_node(init_cterm)
829831
new_node_ids = [init_node.id]
@@ -1068,13 +1070,6 @@ def _init_cterm(
10681070
for constraint in storage_constraints:
10691071
init_cterm = init_cterm.add_constraint(constraint)
10701072

1071-
if type(method) is Contract.Method:
1072-
natspec_preconditions = _create_precondition_constraints(method, init_cterm)
1073-
1074-
for precondition in natspec_preconditions:
1075-
_LOGGER.info(f'Adding NatSpec precondition: {foundry.kevm.pretty_print(mlEqualsTrue(precondition))}')
1076-
init_cterm = init_cterm.add_constraint(mlEqualsTrue(precondition))
1077-
10781073
non_cheatcode_contract_ids = []
10791074
if not (config_type == ConfigType.TEST_CONFIG or active_simbolik):
10801075
non_cheatcode_contract_ids = [Foundry.symbolic_contract_id(contract_name), 'CALLER_ID', 'ORIGIN_ID']
@@ -1498,22 +1493,34 @@ def _interpret_proof_failure(
14981493
print(replace_k_words(line))
14991494

15001495

1501-
def _create_precondition_constraints(method: Contract.Method, init_cterm: CTerm) -> list[KApply]:
1496+
def add_natspec_preconditions(
1497+
cterm: CTerm, method: Contract.Method | Contract.Constructor, contract: Contract, foundry: Foundry
1498+
) -> CTerm:
1499+
if type(method) is Contract.Method:
1500+
natspec_preconditions = _create_precondition_constraints(method, cterm, contract)
1501+
1502+
for precondition in natspec_preconditions:
1503+
_LOGGER.info(f'Adding NatSpec precondition: {foundry.kevm.pretty_print(mlEqualsTrue(precondition))}')
1504+
cterm = cterm.add_constraint(mlEqualsTrue(precondition))
1505+
return cterm
1506+
1507+
1508+
def _create_precondition_constraints(method: Contract.Method, init_cterm: CTerm, contract: Contract) -> list[KApply]:
15021509
precondition_constraints: list[KApply] = []
15031510

15041511
if method.preconditions is not None:
15051512
natspec = Natspec(kdist.get('natspec.llvm'))
15061513
for p in method.preconditions:
15071514
kinner = natspec.decode(input=p.precondition)
15081515
if type(kinner) is KApply:
1509-
kontrol_precondition = _kontrol_kast_from_natspec(kinner, method, init_cterm)
1516+
kontrol_precondition = _kontrol_kast_from_natspec(kinner, method, init_cterm, contract)
15101517
precondition_constraints.append(kontrol_precondition)
15111518

15121519
return precondition_constraints
15131520

15141521

1515-
def _kontrol_kast_from_natspec(term: KApply, method: Contract.Method, init_cterm: CTerm) -> KApply:
1516-
"""Convert a KApply representing a Precondition defined using the Natspec-Grammar a Kontrol KApply.
1522+
def _kontrol_kast_from_natspec(term: KApply, method: Contract.Method, init_cterm: CTerm, contract: Contract) -> KApply:
1523+
"""Convert a KApply representing a Precondition defined using the Natspec-Grammar to a Kontrol KApply.
15171524
15181525
Replace symbol sorts and Id elements with KVariables.
15191526
"""
@@ -1526,22 +1533,33 @@ def _kontrol_kast_from_natspec(term: KApply, method: Contract.Method, init_cterm
15261533
if global_variable:
15271534
args.append(global_variable)
15281535
else:
1529-
args.append(_kontrol_kast_from_natspec(arg, method, init_cterm))
1536+
# TODO: Handle IndexAccess and FieldAccess
1537+
args.append(_kontrol_kast_from_natspec(arg, method, init_cterm, contract))
15301538
if type(arg) is KToken:
15311539
if arg.sort == ID:
1532-
# If it's an ID token, ensure it's either a function parameter or a storage variable
1540+
# If it's an Id token, ensure it's either a function parameter or a storage variable
15331541
function_arg = method.find_arg(arg.token)
15341542
if function_arg:
15351543
args.append(KVariable(function_arg))
15361544
continue
1537-
# TODO: For storage address:
1538-
# - identify storage slot
1539-
# - identify contract address
1545+
# Identify storage slot and offset
1546+
(storage_slot, _slot_offset) = contract.get_storage_slot_by_name(arg.token)
1547+
target_address = init_cterm.cell('ID_CELL')
1548+
if storage_slot:
1549+
storage_map = _get_account_storage_by_address(init_cterm, target_address)
1550+
if storage_map is None:
1551+
continue
1552+
# TODO: Apply the _slot_offset on the Map lookup.
1553+
args.append(KEVM.lookup(storage_map, token(storage_slot)))
1554+
continue
1555+
15401556
_LOGGER.warning(f'NatSpec precondition: Unknown Id sort element: {arg.token}')
15411557
elif arg.sort == HEX_LITERAL:
15421558
args.append(token(int(arg.token, 16)))
1559+
continue
15431560
elif arg.sort in [KSort('Int'), KSort('Bool')]:
15441561
args.append(arg)
1562+
continue
15451563
_LOGGER.warning(f'NatSpec precondition: unknown token: {arg.token}')
15461564
return KApply(symbol, args)
15471565

@@ -1552,3 +1570,21 @@ def get_global_variable(term: KApply, init_cterm: CTerm) -> KInner | None:
15521570
if cell_name is None:
15531571
return None
15541572
return init_cterm.cell(cell_name)
1573+
1574+
1575+
def _get_account_storage_by_address(cterm: CTerm, target_address: KInner) -> KInner | None:
1576+
accounts_cell = flatten_label('_AccountCellMap_', cterm.cell('ACCOUNTS_CELL'))
1577+
for account_wrapped in accounts_cell:
1578+
assert type(account_wrapped) is KApply
1579+
acct_id_cell = account_wrapped.terms[0]
1580+
assert type(acct_id_cell) is KApply and acct_id_cell.label.name == '<acctID>'
1581+
account_address = single(acct_id_cell.terms)
1582+
if target_address == account_address:
1583+
account_cell = account_wrapped.terms[1]
1584+
assert type(account_cell) is KApply and account_cell.label.name == '<account>'
1585+
storage_cell = account_cell.terms[3]
1586+
assert type(storage_cell) is KApply and storage_cell.label.name == '<storage>'
1587+
return single(storage_cell.terms)
1588+
1589+
_LOGGER.warning(f'Account {target_address} not found in the state.')
1590+
return None

src/kontrol/solc_to_k.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -958,6 +958,12 @@ def method_by_name(self) -> dict[str, Contract.Method]:
958958
def method_by_sig(self) -> dict[str, Contract.Method]:
959959
return {method.signature: method for method in self.methods}
960960

961+
def get_storage_slot_by_name(self, target_name: str) -> tuple[int, int] | tuple[None, None]:
962+
for field in self.fields:
963+
if field.label == target_name:
964+
return (field.slot, field.offset)
965+
return (None, None)
966+
961967

962968
# Helpers
963969

src/tests/integration/test-data/end-to-end-prove-all

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ CounterTest.test_Increment()
44
ForgetBranchTest.test_forgetBranch(uint256)
55
PreconditionTest.testPrecondition_globalVariables(uint256,address)
66
PreconditionTest.testPrecondition_hexLiterals(address)
7+
PreconditionTest.testPrecondition_storage()
78
PreconditionTest.testPrecondition(uint256)
89
RandomVarTest.test_custom_names()
910
RandomVarTest.test_randomAddress()

src/tests/integration/test-data/end-to-end-prove-show

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
ForgetBranchTest.test_forgetBranch(uint256)
2+
PreconditionTest.testPrecondition_storage()
3+
PreconditionTest.testPrecondition(uint256)
24
RandomVarTest.test_custom_names()
35
UnitTest.test_checkInitialBalance(uint256)
4-
PreconditionTest.testPrecondition(uint256)

0 commit comments

Comments
 (0)