← Commit history

Add repeatable GLB binding and asset integrity verification

John Lauer ·00cf997e0d ·12d ago ·parent 0f6d815
1 file changed +26
tools/verify_motion.pyadded+26
@@ -0,0 +1,26 @@+"""Check the published motion asset and provenance without a graphics runtime."""+from pathlib import Path+import hashlib,json,struct+root=Path(__file__).resolve().parents[1]+manifest=json.loads((root/'states.json').read_text()); motion=manifest['motion']+blob=(root/motion['model']).read_bytes()+assert hashlib.sha256(blob).hexdigest()==motion['sha256']+magic,version,length=struct.unpack_from('<4sII',blob)+assert (magic,version,length)==(b'glTF',2,len(blob))+n,kind=struct.unpack_from('<II',blob,12);assert kind==0x4e4f534a+model=json.loads(blob[20:20+n]);nodes=model['nodes']+def unique(name):+ found=[i for i,node in enumerate(nodes) if node.get('name')==name]+ assert len(found)==1,(name,found)+ return found[0]+rotor=unique(motion['nodes']['rotor']);housing=unique(motion['nodes']['stationary'])+rootnode=unique(motion['nodes']['assembly'])+assert rotor in nodes[rootnode]['children'] and housing in nodes[rootnode]['children']+assert len(nodes[rotor]['children'])==8+for binding in motion['bindings']:+ animations=[a for a in model['animations'] if a.get('name')==binding['animation']]+ assert len(animations)==1+ assert all(c['target']['node']==rotor and c['target']['path']=='rotation' for c in animations[0]['channels'])+for asset in json.loads((root/'provenance/assets.json').read_text())['assets']:+ assert hashlib.sha256((root/asset['file']).read_bytes()).hexdigest()==asset['sha256'],asset['file']+print('PASS: GLB structure, unique bindings, rotor-only animation, asset hashes')