tricu

An interpreted language for exploring Tree Calculus
Log | Files | Refs | README | LICENSE

bootstrap_cross_host_controls.py (4066B)


      1 #!/usr/bin/env python3
      2 """Check verifier rejection of corrupted copies of a completed experiment.
      3 Restore the copies and verify acceptance again.
      4 """
      5 import argparse
      6 import hashlib
      7 import json
      8 from pathlib import Path
      9 import shutil
     10 import subprocess
     11 from bootstrap_acceptance import sha
     12 from bootstrap_cross_host import verify_cross
     13 from native_indexed_store import indexed_bytes
     14 from base_workloads import encode
     15 
     16 
     17 def main():
     18     p=argparse.ArgumentParser(description=__doc__)
     19     p.add_argument('experiment',type=Path)
     20     p.add_argument('--directory',type=Path,required=True)
     21     a=p.parse_args(); a.directory.mkdir(parents=True,exist_ok=False)
     22     e=a.experiment.resolve(); definition=json.loads((e/'experiment.json').read_text())
     23     runtime=Path(definition['runtime']['path']); library=Path(definition['zig_library']['path'])
     24     first,second,control,original=(e/name/'generation' for name in ('H1','H2','H3','Z3'))
     25     source=e/'source'; copy=a.directory/'zig-copy'
     26     shutil.copytree(original,copy)
     27     report=dict(complete=False,rejected=[],acceptance_sha256=sha(e/'accepted-cross-host.json'))
     28     def reject(label,expected,zig=copy):
     29         try: verify_cross(first,second,control,zig,runtime,library,source)
     30         except (ValueError,AssertionError,subprocess.CalledProcessError) as error:
     31             if expected and expected not in str(error): raise
     32             report['rejected'].append(label)
     33             (a.directory/(label+'.log')).write_text(str(error)+'\n'+str(getattr(error,'output','')))
     34         else: raise AssertionError('invalid experiment accepted: '+label)
     35     try:
     36         reject('same-directory','four distinct',zig=control)
     37         for key,value,message in [('execution_host','haskell','execution host mismatch'),
     38                                   ('runtime_sha256','0'*64,'runtime identity mismatch'),
     39                                   ('fresh_store',False,'fresh-store execution evidence'),
     40                                   ('compiler_bundle_sha256','0'*64,'compiler bundle identity mismatch')]:
     41             saved={name:(copy/name).read_bytes() for name in ('inputs.json','status.json')}
     42             for name,payload in saved.items():
     43                 record=json.loads(payload); record[key]=value
     44                 (copy/name).write_text(json.dumps(record))
     45             reject(key,message)
     46             for name,payload in saved.items(): (copy/name).write_bytes(payload)
     47         status=json.loads((copy/'status.json').read_text())
     48         digest=status['artifacts']['build-stable.arboricx']['tree']
     49         obj=copy/'cas/objects'/digest[:3]/digest
     50         original_object=obj.read_bytes(); obj.write_bytes(b'corrupt')
     51         reject('corrupt-object',None)
     52         obj.write_bytes(original_object)
     53         package=copy/'build-stable.arboricx'; original_package=package.read_bytes()
     54         package.write_bytes(original_package[:-1]+bytes([original_package[-1]^1]))
     55         status['artifacts']['build-stable.arboricx']['sha256']=sha(package)
     56         (copy/'status.json').write_text(json.dumps(status))
     57         reject('different-authenticated-package','cross-host packaged artifact mismatch')
     58         package.write_bytes(original_package)
     59         shutil.copyfile(original/'status.json',copy/'status.json')
     60         extra=indexed_bytes(encode(987654321))
     61         digest=hashlib.sha256(b'arboricx.indexed-tree.v1\0'+extra).hexdigest()
     62         obj=copy/'cas/objects'/digest[:3]/digest
     63         assert not obj.exists(); obj.parent.mkdir(exist_ok=True); obj.write_bytes(extra)
     64         reject('additional-valid-object','complete canonical object/receipt inventory differs')
     65         obj.unlink()
     66         # The restored copy must pass, so failure is not caused merely by relocation.
     67         verify_cross(first,second,control,copy,runtime,library,source)
     68         report.update(complete=True,restored_copy_passes=True)
     69         print('PASS: '+str(len(report['rejected']))+' cross-host rejection controls; restored copy accepted')
     70     finally: (a.directory/'report.json').write_text(json.dumps(report,indent=2)+'\n')
     71 
     72 
     73 if __name__=='__main__': main()