tricu

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

bootstrap_cross_host_controls.py (4480B)


      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,'execution request 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         request_path=copy/'execution-request.json'
     48         request_bytes=request_path.read_bytes()
     49         request=json.loads(request_bytes)
     50         request['execution_io_version']=1 if request.get('execution_io_version',1)==2 else 2
     51         request_path.write_text(json.dumps(request))
     52         reject('request-io-version','execution request IO version mismatch')
     53         request_path.write_bytes(request_bytes)
     54         status=json.loads((copy/'status.json').read_text())
     55         digest=status['artifacts']['build-stable.arboricx']['tree']
     56         obj=copy/'cas/objects'/digest[:3]/digest
     57         original_object=obj.read_bytes(); obj.write_bytes(b'corrupt')
     58         reject('corrupt-object',None)
     59         obj.write_bytes(original_object)
     60         package=copy/'build-stable.arboricx'; original_package=package.read_bytes()
     61         package.write_bytes(original_package[:-1]+bytes([original_package[-1]^1]))
     62         status['artifacts']['build-stable.arboricx']['sha256']=sha(package)
     63         (copy/'status.json').write_text(json.dumps(status))
     64         reject('different-authenticated-package','cross-host packaged artifact mismatch')
     65         package.write_bytes(original_package)
     66         shutil.copyfile(original/'status.json',copy/'status.json')
     67         extra=indexed_bytes(encode(987654321))
     68         digest=hashlib.sha256(b'arboricx.indexed-tree.v1\0'+extra).hexdigest()
     69         obj=copy/'cas/objects'/digest[:3]/digest
     70         assert not obj.exists(); obj.parent.mkdir(exist_ok=True); obj.write_bytes(extra)
     71         reject('additional-valid-object','complete canonical object/receipt inventory differs')
     72         obj.unlink()
     73         # The restored copy must pass, so failure is not caused merely by relocation.
     74         verify_cross(first,second,control,copy,runtime,library,source)
     75         report.update(complete=True,restored_copy_passes=True)
     76         print('PASS: '+str(len(report['rejected']))+' cross-host rejection controls; restored copy accepted')
     77     finally: (a.directory/'report.json').write_text(json.dumps(report,indent=2)+'\n')
     78 
     79 
     80 if __name__=='__main__': main()