31
31
from pyk .proof import ProofStatus
32
32
from pyk .proof .proof import Proof
33
33
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
35
35
from rich .progress import Progress , SpinnerColumn , TaskID , TextColumn , TimeElapsedColumn
36
36
37
37
from natspec .natspec import Natspec
@@ -815,6 +815,7 @@ def _method_to_cfg(
815
815
816
816
for final_node in final_states :
817
817
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 )
818
819
new_node = cfg .create_node (new_init_cterm )
819
820
if graft_setup_proof :
820
821
cfg .create_edge (final_node .id , new_node .id , depth = 1 )
@@ -824,6 +825,7 @@ def _method_to_cfg(
824
825
)
825
826
new_node_ids .append (new_node .id )
826
827
else :
828
+ init_cterm = add_natspec_preconditions (init_cterm , method , contract , foundry )
827
829
cfg = KCFG ()
828
830
init_node = cfg .create_node (init_cterm )
829
831
new_node_ids = [init_node .id ]
@@ -1068,13 +1070,6 @@ def _init_cterm(
1068
1070
for constraint in storage_constraints :
1069
1071
init_cterm = init_cterm .add_constraint (constraint )
1070
1072
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
-
1078
1073
non_cheatcode_contract_ids = []
1079
1074
if not (config_type == ConfigType .TEST_CONFIG or active_simbolik ):
1080
1075
non_cheatcode_contract_ids = [Foundry .symbolic_contract_id (contract_name ), 'CALLER_ID' , 'ORIGIN_ID' ]
@@ -1498,22 +1493,34 @@ def _interpret_proof_failure(
1498
1493
print (replace_k_words (line ))
1499
1494
1500
1495
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 ]:
1502
1509
precondition_constraints : list [KApply ] = []
1503
1510
1504
1511
if method .preconditions is not None :
1505
1512
natspec = Natspec (kdist .get ('natspec.llvm' ))
1506
1513
for p in method .preconditions :
1507
1514
kinner = natspec .decode (input = p .precondition )
1508
1515
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 )
1510
1517
precondition_constraints .append (kontrol_precondition )
1511
1518
1512
1519
return precondition_constraints
1513
1520
1514
1521
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.
1517
1524
1518
1525
Replace symbol sorts and Id elements with KVariables.
1519
1526
"""
@@ -1526,22 +1533,33 @@ def _kontrol_kast_from_natspec(term: KApply, method: Contract.Method, init_cterm
1526
1533
if global_variable :
1527
1534
args .append (global_variable )
1528
1535
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 ))
1530
1538
if type (arg ) is KToken :
1531
1539
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
1533
1541
function_arg = method .find_arg (arg .token )
1534
1542
if function_arg :
1535
1543
args .append (KVariable (function_arg ))
1536
1544
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
+
1540
1556
_LOGGER .warning (f'NatSpec precondition: Unknown Id sort element: { arg .token } ' )
1541
1557
elif arg .sort == HEX_LITERAL :
1542
1558
args .append (token (int (arg .token , 16 )))
1559
+ continue
1543
1560
elif arg .sort in [KSort ('Int' ), KSort ('Bool' )]:
1544
1561
args .append (arg )
1562
+ continue
1545
1563
_LOGGER .warning (f'NatSpec precondition: unknown token: { arg .token } ' )
1546
1564
return KApply (symbol , args )
1547
1565
@@ -1552,3 +1570,21 @@ def get_global_variable(term: KApply, init_cterm: CTerm) -> KInner | None:
1552
1570
if cell_name is None :
1553
1571
return None
1554
1572
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
0 commit comments