@@ -100,14 +100,18 @@ def validate_observation(value, run_id, protocol):
100100 following = bisect_right (epoch_ends , row ['start_ns' ])
101101 require (following == len (epochs ) or row ['end_ns' ] <= epochs [following ]['start_ns' ], 'warmup contamination' )
102102 expected = statistics (samples , epochs )
103- require (set (value ['statistics' ]) == set (expected ), 'missing statistics or units' )
104- for key , result in expected .items ():
105- actual = value ['statistics' ][key ]
106- require (type (actual ) in (int , float ) and math .isfinite (actual ) and actual > 0 and
107- math .isclose (actual , result , rel_tol = 1e-12 ), 'statistics disagreement: ' + key )
103+ validate_statistics (value ['statistics' ], expected )
108104 return expected
109105
110106
107+ def validate_statistics (actual , expected ):
108+ require (set (actual ) == set (expected ), 'missing statistics or units' )
109+ for key , result in expected .items ():
110+ number = actual [key ]
111+ require (type (number ) in (int , float ) and math .isfinite (number ) and number > 0 and
112+ math .isclose (number , result , rel_tol = 1e-12 ), 'statistics disagreement: ' + key )
113+
114+
111115def artifact (path ):
112116 path = Path (path ).resolve ()
113117 return dict (path = str (path ), bytes = path .stat ().st_size , sha256 = hashlib .sha256 (path .read_bytes ()).hexdigest ())
@@ -129,18 +133,76 @@ def validate_server(server, observation):
129133 measured = observation ['completed' ], errors = 0 ), 'server/client counts differ' )
130134
131135
136+ def validate_provenance (receipt , expected_identity ):
137+ records = {record ['path' ]: record for record in receipt ['artifacts' ]}
138+ require (len (records ) == len (receipt ['artifacts' ]), 'duplicate artifact paths' )
139+
140+ def bound (path , expected = None ):
141+ path = str (Path (path ).resolve ())
142+ require (path in records , 'unbound provenance artifact: ' + path )
143+ if expected is not None :
144+ require (records [path ]['sha256' ] == expected , 'provenance digest disagreement: ' + path )
145+ return records [path ]
146+
147+ directory = Path (receipt ['consumer_directory' ])
148+ copies = {'consumer.cpp' : 'test/installed/v3_benchmark_consumer.cpp' ,
149+ 'v3_host_poll.hpp' : 'examples/v3_host_poll.hpp' ,
150+ 'client.py' : 'test/installed/v3_benchmark_client.py' }
151+ local = {str (directory / name ): expected_identity ['source' ][source ] for name , source in copies .items ()}
152+ for path , sha256 in local .items (): bound (path , sha256 )
153+ for name , sha256 in expected_identity ['fixtures' ].items (): bound (directory / 'fixtures' / name , sha256 )
154+ require (set (receipt ['consumers' ]) == {'shared' , 'static' }, 'missing consumer provenance' )
155+ for linkage , consumer in receipt ['consumers' ].items ():
156+ compiled = consumer ['compile' ]
157+ bound (directory / linkage , compiled ['artifact_sha256' ])
158+ require (compiled ['private_test_headers' ] is False , 'private consumer headers' )
159+ require (compiled ['sources' ] == {str (directory / 'consumer.cpp' ): local [str (directory / 'consumer.cpp' )]},
160+ 'wrong consumer sources' )
161+ deps = compiled ['dependency_sha256' ]
162+ require (set (compiled ['dependencies' ]) == set (deps ), 'missing dependency digests' )
163+ resolved = {str (Path (path ).resolve ()): sha256 for path , sha256 in deps .items ()}
164+ require (all (path in resolved for path in local if not path .endswith ('client.py' )), 'missing local dependencies' )
165+ require (any (path in expected_identity ['package' ] for path in resolved ), 'missing installed headers' )
166+ for path , sha256 in resolved .items ():
167+ # Installed headers are bound by the independently supplied package identity.
168+ expected = expected_identity ['package' ].get (path , local .get (path ))
169+ require (expected is not None and sha256 == expected , 'unexpected consumer dependency: ' + path )
170+ require (artifact (path )['sha256' ] == sha256 , 'changed consumer dependency: ' + path )
171+ if path in local : bound (path , sha256 )
172+ require (Path (compiled ['link_map' ]).resolve () == (directory / (linkage + '.map' )).resolve (), 'wrong link map' )
173+ bound (compiled ['link_map' ])
174+ output = Path (receipt ['cells' ][0 ]['observation' ]['path' ]).parent .parent
175+ command_record = bound (output / 'commands.json' )
176+ commands = parse_json (Path (command_record ['path' ]).read_text ())
177+ require (len (commands ) >= 4 , 'missing tool commands' )
178+ for command in commands :
179+ require (command ['exit' ] == 0 and 'error' not in command and command ['argv' ], 'failed provenance command' )
180+ bound (command ['log' ], command ['sha256' ])
181+ require (records [str (Path (command ['log' ]).resolve ())]['bytes' ] == command ['bytes' ], 'wrong command length' )
182+ require (set (receipt ['tools' ]) == {'compiler' , 'pkg_config' , 'clients' }, 'missing tool versions' )
183+ for key , command in zip (('compiler' , 'pkg_config' , 'clients' ), commands ):
184+ require (receipt ['tools' ][key ] and receipt ['tools' ][key ] == Path (command ['log' ]).read_text (), 'wrong tool version' )
185+ inputs = receipt ['client_inputs' ]
186+ require (inputs and [item ['path' ] for item in inputs ] == parse_json (Path (commands [3 ]['log' ]).read_text ()),
187+ 'missing client input provenance' )
188+ for record in inputs :
189+ require (bound (record ['path' ]) == record , 'unbound client input' )
190+
191+
132192def validate_receipt (receipt , expected_identity , expected_run_id = None ):
133193 require (receipt ['schema_version' ] == 1 and receipt ['status' ] == 'passed' , 'failed or unknown receipt' )
134194 require (receipt ['identity' ] == expected_identity , 'stale source/package/configuration identity' )
135195 require ('workload' not in receipt or receipt ['workload' ] == expected_identity .get ('workload' ), 'stale workload' )
136196 require (len (receipt ['run_id' ]) == 32 and all (c in '0123456789abcdef' for c in receipt ['run_id' ]), 'invalid run identifier' )
137197 if expected_run_id is not None :
138198 require (receipt ['run_id' ] == expected_run_id , 'reused run identifier' )
139- validate_cells ([tuple (cell [k ] for k in ('linkage' , 'loop' , 'protocol' )) for cell in receipt ['cells' ]], receipt ['tls' ])
199+ require (receipt ['tls' ] == expected_identity ['tls' ], 'contradictory TLS mode' )
200+ validate_cells ([tuple (cell [k ] for k in ('linkage' , 'loop' , 'protocol' )) for cell in receipt ['cells' ]], expected_identity ['tls' ])
140201 require (receipt ['artifacts' ], 'missing immutable artifacts' )
141202 sample_paths = [cell ['observation' ]['path' ] for cell in receipt ['cells' ]]
142203 require (len (sample_paths ) == len (set (sample_paths )), 'reused samples across cells' )
143204 for record in receipt ['artifacts' ]: verify_artifact (record )
205+ validate_provenance (receipt , expected_identity )
144206 for cell in receipt ['cells' ]:
145207 require (cell ['client_exit' ] == 0 and cell ['fresh_execution' ] is True , 'missing live client execution' )
146208 for key in ('observation' , 'server' ):
@@ -154,6 +216,7 @@ def validate_receipt(receipt, expected_identity, expected_run_id=None):
154216 expected_epochs , expected_warmup = epoch_workload (cell ['protocol' ], workload ['requests' ], concurrency , workload ['warmup' ])
155217 require (observation ['warmup_requested' ] == expected_warmup , 'wrong warmup workload' )
156218 require (len (observation ['epochs' ]) == expected_epochs , 'wrong epoch count' )
157- validate_observation (observation , receipt ['run_id' ], cell ['protocol' ])
219+ expected = validate_observation (observation , receipt ['run_id' ], cell ['protocol' ])
220+ validate_statistics (cell ['statistics' ], expected )
158221 validate_server (parse_json (Path (cell ['server' ]['path' ]).read_text ()), observation )
159222 return receipt
0 commit comments