88
99from pyk .cli_utils import dir_path , file_path
1010from pyk .kast import KApply , KDefinition , KFlatModule , KImport , KRequire , KSort
11+ from pyk .kcfg import KCFG
1112from pyk .ktool .krun import _krun
13+ from pyk .prelude import mlTop
1214
1315from .gst_to_kore import gst_to_kore
1416from .kevm import KEVM
1517from .solc_to_k import Contract , contract_to_k , solc_compile
16- from .utils import KPrint_make_unparsing , add_include_arg
18+ from .utils import KCFG_from_claim , KPrint_make_unparsing , add_include_arg , read_kast_flatmodulelist
1719
1820_LOGGER : Final = logging .getLogger (__name__ )
1921_LOG_FORMAT : Final = '%(levelname)s %(asctime)s %(name)s - %(message)s'
@@ -177,6 +179,8 @@ def exec_foundry_kompile(
177179 md_selector : Optional [str ],
178180 regen : bool = False ,
179181 rekompile : bool = False ,
182+ reparse : bool = False ,
183+ reinit : bool = False ,
180184 requires : Iterable [str ] = (),
181185 imports : Iterable [str ] = (),
182186 ** kwargs ,
@@ -189,6 +193,9 @@ def exec_foundry_kompile(
189193 spec_module = 'FOUNDRY-SPEC'
190194 foundry_definition_dir = foundry_out / 'kompiled'
191195 foundry_main_file = foundry_definition_dir / 'foundry.k'
196+ kompiled_timestamp = foundry_definition_dir / 'timestamp'
197+ parsed_spec = foundry_definition_dir / 'spec.json'
198+ kcfgs_file = foundry_definition_dir / 'kcfgs.json'
192199 requires = ['lemmas/lemmas.k' , 'lemmas/int-simplification.k' ] + list (requires )
193200 imports = ['LEMMAS' , 'INT-SIMPLIFICATION' ] + list (imports )
194201 if not foundry_definition_dir .exists ():
@@ -205,7 +212,7 @@ def exec_foundry_kompile(
205212 imports = list (imports ),
206213 output = fmf ,
207214 )
208- if regen or rekompile or not ( foundry_definition_dir / 'timestamp' ) .exists ():
215+ if regen or rekompile or not kompiled_timestamp .exists ():
209216 KEVM .kompile (
210217 foundry_definition_dir ,
211218 foundry_main_file ,
@@ -216,6 +223,26 @@ def exec_foundry_kompile(
216223 md_selector = md_selector ,
217224 profile = profile ,
218225 )
226+ kevm = KEVM (foundry_definition_dir , main_file = foundry_main_file , profile = profile )
227+ if regen or rekompile or reparse or not parsed_spec .exists ():
228+ prove_args = add_include_arg (includes )
229+ kevm .prove (
230+ foundry_main_file ,
231+ spec_module_name = spec_module ,
232+ dry_run = True ,
233+ args = (['--emit-json-spec' , str (parsed_spec )] + prove_args ),
234+ )
235+ if regen or rekompile or reparse or reinit or not kcfgs_file .exists ():
236+ cfgs : Dict [str , Dict ] = {}
237+ for module in read_kast_flatmodulelist (parsed_spec ).modules :
238+ for claim in module .claims :
239+ cfg_label = claim .att ["label" ]
240+ _LOGGER .info (f'Producing KCFG: { cfg_label } ' )
241+ cfgs [cfg_label ] = KCFG_from_claim (kevm .definition , claim ).to_dict ()
242+ with open (kcfgs_file , 'w' ) as kf :
243+ kf .write (json .dumps (cfgs ))
244+ kf .close ()
245+ _LOGGER .info (f'Wrote file: { kcfgs_file } ' )
219246
220247
221248def exec_prove (
@@ -267,23 +294,45 @@ def exec_foundry_prove(
267294 _ignore_arg (kwargs , 'definition_dir' , f'--definition: { kwargs ["definition_dir" ]} ' )
268295 _ignore_arg (kwargs , 'spec_module' , f'--spec-module: { kwargs ["spec_module" ]} ' )
269296 definition_dir = foundry_out / 'kompiled'
270- spec_file = definition_dir / 'foundry.k'
271- spec_module = 'FOUNDRY-SPEC'
272- claims = [Contract .contract_test_to_claim_id (_t ) for _t in tests ]
273- exclude_claims = [Contract .contract_test_to_claim_id (_t ) for _t in exclude_tests ]
274- exec_prove (
275- definition_dir ,
276- profile ,
277- spec_file ,
278- includes = includes ,
279- debug_equations = debug_equations ,
280- bug_report = bug_report ,
281- depth = depth ,
282- claims = claims ,
283- exclude_claims = exclude_claims ,
284- spec_module = spec_module ,
285- ** kwargs ,
286- )
297+ use_directory = foundry_out / 'specs'
298+ use_directory .mkdir (parents = True , exist_ok = True )
299+ kevm = KEVM (definition_dir , profile = profile , use_directory = use_directory )
300+ kcfgs_file = definition_dir / 'kcfgs.json'
301+ kcfgs : Dict [str , KCFG ] = {}
302+ _LOGGER .info (f'Reading file: { kcfgs_file } ' )
303+ with open (kcfgs_file , 'r' ) as kf :
304+ kcfgs = {k : KCFG .from_dict (v ) for k , v in json .loads (kf .read ()).items ()}
305+ tests = [Contract .contract_test_to_claim_id (_t ) for _t in tests ]
306+ exclude_tests = [Contract .contract_test_to_claim_id (_t ) for _t in exclude_tests ]
307+ claims = list (kcfgs .keys ())
308+ _unfound_kcfgs : List [str ] = []
309+ if len (tests ) > 0 :
310+ kcfgs = {k : kcfg for k , kcfg in kcfgs .items () if k in tests }
311+ for _t in tests :
312+ if _t not in claims :
313+ _unfound_kcfgs .append (_t )
314+ for _t in exclude_tests :
315+ if _t not in claims :
316+ _unfound_kcfgs .append (_t )
317+ if _t in kcfgs :
318+ kcfgs .pop (_t )
319+ if _unfound_kcfgs :
320+ _LOGGER .error (f'Missing KCFGs for tests: { _unfound_kcfgs } ' )
321+ sys .exit (1 )
322+ _failed_claims : List [str ] = []
323+ for kcfg_name , kcfg in kcfgs .items ():
324+ _LOGGER .info (f'Proving KCFG: { kcfg_name } ' )
325+ edge = kcfg .create_edge (kcfg .get_unique_init ().id , kcfg .get_unique_target ().id , mlTop (), depth = - 1 )
326+ claim = edge .to_claim ()
327+ result = kevm .prove_claim (claim , kcfg_name .replace ('.' , '-' ))
328+ if type (result ) is KApply and result .label .name == '#Top' :
329+ _LOGGER .info (f'Proved KCFG: { kcfg_name } ' )
330+ else :
331+ _LOGGER .error (f'Failed to prove KCFG: { kcfg_name } ' )
332+ _failed_claims .append (kcfg_name )
333+ if _failed_claims :
334+ _LOGGER .error (f'Failed to prove KCFGs: { _failed_claims } ' )
335+ sys .exit (1 )
287336
288337
289338def exec_run (
@@ -482,6 +531,20 @@ def parse(s):
482531 action = 'store_true' ,
483532 help = 'Rekompile foundry.k even if kompiled definition already exists.' ,
484533 )
534+ foundry_kompile .add_argument (
535+ '--reparse' ,
536+ dest = 'reparse' ,
537+ default = False ,
538+ action = 'store_true' ,
539+ help = 'Reparse K specifications even if the parsed spec already exists.' ,
540+ )
541+ foundry_kompile .add_argument (
542+ '--reinit' ,
543+ dest = 'reinit' ,
544+ default = False ,
545+ action = 'store_true' ,
546+ help = 'Reinitialize kcfgs even if they already exist.' ,
547+ )
485548
486549 foundry_prove_args = command_parser .add_parser (
487550 'foundry-prove' ,
0 commit comments