From d870596cc72f74a5762cce2400802e2b351e903e Mon Sep 17 00:00:00 2001 From: codex Date: Fri, 16 Oct 2026 10:48:21 +0200 Subject: [PATCH] Verify exact task metadata baseline and readback Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a140c4-354a-7823-8f0d-8f717397f786 --- docs/task-execution-mediator-seam.md | 14 +- docs/task-execution-native-fixture.md | 34 ++++- scripts/task_execution_mediator.py | 8 +- scripts/task_execution_native_fixture.py | 66 ++++++++- tests/test_task_execution_mediator.py | 4 +- tests/test_task_execution_native_fixture.py | 22 ++- tests/test_task_execution_readback.py | 127 ++++++++++++++++++ ...-WP-0035-credential-lane-implementation.md | 13 ++ 8 files changed, 274 insertions(+), 14 deletions(-) create mode 100644 tests/test_task_execution_readback.py diff --git a/docs/task-execution-mediator-seam.md b/docs/task-execution-mediator-seam.md index 964688b..463c056 100644 --- a/docs/task-execution-mediator-seam.md +++ b/docs/task-execution-mediator-seam.md @@ -54,9 +54,10 @@ with a preceding durable CAS/confirmation sequence. native calls continue at2. Pending/unknown/cleanup-closed state cannot supply a next ordinal. Each native request retains its own durable intent and outcome. 8. Cleanup stays under the original distinct obligations even if business work - fails. Returned evidence explicitly says native apply is unverified: six successful - native calls do not establish total approved baseline/readback normalization, - atomic CAS, service-login provenance or native production owner acceptance. + fails. Wave 5 requires exact committed before/after snapshots before reporting + native apply verified; six HTTP successes alone remain insufficient. This is + sequential fixture acceptance, without atomic CAS, service-login provenance or + native production owner acceptance. The owner configuration selects provider route/read credential and the existing Secrets configuration. These are independently installed runtime inputs, never @@ -105,3 +106,10 @@ per-call served-time/use enforcement, audit/outbox and protected uncertain/crash recovery. Then obtain and execute the exact founder-scoped attended trial. Approval replay prevention and uncertain native-effect reconciliation remain separate guarantees. T12, Secrets WP13 and native adoption stay open. + +Wave 5 adds owner-installed exact metadata snapshots committed through the +existing expected_before/expected_after descriptor fields, per-target baseline +and complete role/policy readback comparison, durable readback/verified evidence, +and a separate cleanup-confirmed transition. The sequence remains sequential, +without native CAS or independently recoverable cleanup handles. See +[fixture acceptance](task-execution-native-fixture.md#exact-disposable-metadata-acceptance-2026-10-16-wave-5). diff --git a/docs/task-execution-native-fixture.md b/docs/task-execution-native-fixture.md index 7096f1a..1dd6b91 100644 --- a/docs/task-execution-native-fixture.md +++ b/docs/task-execution-native-fixture.md @@ -33,7 +33,7 @@ metadata calls use independently recompiled source/method/path/body. A task use reserved for revoke; native token limits also enforce the ceiling. Business calls check wall and monotonic deadlines before effect, with a five-second cleanup reserve. No retry/remint, caller HCL/JSON/path, mount/AppRole/KV/SecretID or shell API exists. -The adapter cannot claim complete baseline CAS or normalized readback acceptance. +Wave 5 below adds exact sequential snapshot acceptance; native baseline CAS remains absent. Every business/grant request records durable intent and closed status evidence. Native cleanup has its own durable per-request intents beneath the original @@ -89,3 +89,35 @@ native token, then performs explicit synthetic owner recovery. No blind retry oc No umbrella broker, general executor, new credential catalogue or standing service admin entitlement is introduced. Approval CAS prevents approval replay; it cannot make uncertain native effects exactly-once. RPF T12 and Secrets WP13 remain open. + +### Exact disposable metadata acceptance (2026-10-16, wave 5) + +The owner-installed `FixtureAdmission` now carries canonical JSON before/after +snapshot bytes. Their SHA-256 values must equal the existing descriptor's +`expected_before` and `expected_after`, and both snapshots must name exactly the +two compiled metadata paths. Explicit JSON null means absence. Only the actual +OpenBao empty-errors 404 is accepted as absence; denied/malformed reads fail. +These inputs contain metadata, never KV values or private token handles. + +Policy comparison retains name, HCL, version and cas_required. Only the +modification timestamp is excluded, after requiring an aware timestamp and the +closed observed policy field set. Role comparison retains every returned field, +including aliases, token defaults and unexpected additions, using canonical JSON +bytes (therefore false cannot equal zero). The disposable Kubernetes fixture pins +the complete OpenBao 2.5.5 response shape. Another plugin/version/default requires +an independently reviewed owner snapshot; nothing infers new accepted defaults. + +Each baseline gates its corresponding write. Policy readback occurs before role +baseline/write/readback. A role conflict can therefore leave the policy applied; +that operation remains unknown. This sequence has no native CAS, aggregate +baseline gate, concurrent-writer exclusion, or simultaneous final-state proof. +After all six calls match the committed snapshots, the same-live adapter records +readback and verified with a value-free evidence digest. The mediator reports +native_apply verified; cleanup-confirmed requires all separate cleanup receipts. +Readback success with unconfirmed cleanup cannot reach cleanup-confirmed. + +Reopening SQLite cannot dispatch, reconsume, restore token/accessor handles or +retry cleanup. Native effects can survive process loss. Cleanup handles remain +memory-only: no independently authorized recovery mechanism is implemented here. +This closes disposable sequential apply acceptance only, not TSA03 or native +production recovery/admission. diff --git a/scripts/task_execution_mediator.py b/scripts/task_execution_mediator.py index d42b55f..6614974 100644 --- a/scripts/task_execution_mediator.py +++ b/scripts/task_execution_mediator.py @@ -169,12 +169,16 @@ def run_native_fixture_task(*,compiler:Compiler,journal:Journal,plan_request:dic _fresh(secrets_cfg,handoff,descriptor) native.metadata_call(ordinal) result['native_calls_completed']+=1 + result['readback_evidence_digest']=native.verify_metadata() + result['native_apply']='verified' except Exception: _unknown(journal,binding) result['native_apply']='unknown' finally: result['cleanup_confirmed']=run_cleanup(journal,binding,native)['confirmed'] - # Native total expected-state normalization/acceptance remains explicitly - # absent. Six successful calls cannot be relabelled a verified owner trial. + if result['native_apply']=='verified' and result['cleanup_confirmed']: + journal.transition(binding,'verified','cleanup-confirmed',result['readback_evidence_digest']) + # Readback and cleanup are independent outcomes; no recovery authority + # or native compare-and-swap is implied by either fixture result. result['business_state']=journal.observe(binding)['state'] return result diff --git a/scripts/task_execution_native_fixture.py b/scripts/task_execution_native_fixture.py index c9ee244..caa7da1 100644 --- a/scripts/task_execution_native_fixture.py +++ b/scripts/task_execution_native_fixture.py @@ -32,6 +32,8 @@ class PrivateSession: class FixtureAdmission: """Independently installed owner fixture inputs, not a request schema.""" descriptor: bytes + expected_before: bytes + expected_after: bytes owner_token: str = field(repr=False) identity_session: PrivateSession = field(repr=False) server_process: subprocess.Popen = field(repr=False) @@ -78,6 +80,22 @@ class NativeFixtureAdapter: self.hcl+='path "auth/token/revoke-self" {\n capabilities = ["update"]\n}\n' if self.meta['execution_grant']['openbao']['policy']!={'name':self.policy_name,'hcl':self.hcl}: raise Refused('native grant body differs from bound owner projection') + # Installed owner snapshots use the existing descriptor commitments. No + # request-supplied expectation or response may authorize itself. + self.expected={} + for phase in ('before','after'): + raw=getattr(admission,'expected_'+phase) + if type(raw) is not bytes or 'sha256:'+content_hash(raw)!=descriptor['expected_'+phase]: + raise Refused('owner expected-state digest mismatch') + value=json.loads(raw) + if type(value) is not dict or set(value)!=set(paths) or encoded(value)!=raw: + raise Refused('canonical exact target snapshot required') + if any(item is not None and type(item) is not dict for item in value.values()): + raise Refused('metadata snapshot object or explicit absence required') + if phase=='after' and any(item is None for item in value.values()): + raise Refused('present final metadata required') + self.expected[phase]=value + self._observed={'before':{},'after':{}} self.cleanup=tuple(Cleanup(item['kind'],item['call_digest']) for item in cleanup_specs(binding.operation_id)) if self.meta['cleanup'] != cleanup_specs(binding.operation_id): raise Refused('cleanup plan mismatch') self.task=None; self._mint_attempted=False; self._policy_attempted=False; self._ordinal=0; self._next_metadata=0 @@ -183,11 +201,53 @@ class NativeFixtureAdapter: call=self.compiler.before_call(self.plan,ordinal) status,data=self._business(call.method,call.path,self.task.token,call.payload,task=True) self._next_metadata+=1 - self._ok(status,data,{200,204,404} if ordinal in {0,3} else {200,204}) - # Metadata only. The trusted owner caller must compare native normalized - # baseline/readback to separately admitted expected state. Not KV values. + try: + self._ok(status,data,{200,204,404} if ordinal in {0,3} else {200,204}) + if ordinal in {0,2,3,5}: + phase='before' if ordinal in {0,3} else 'after' + value=self._metadata_snapshot(call.path,status,data) + if encoded(value)!=encoded(self.expected[phase][call.path]): + raise Refused('native exact expected-state mismatch') + self._observed[phase][call.path]=value + except Exception: + state=self.journal.observe(self.binding)['state'] + if state!='unknown': self.journal.transition(self.binding,state,'unknown') + raise Refused('native baseline or readback unconfirmed') from None return status,data + @staticmethod + def _metadata_snapshot(path,status,response): + # HTTP errors/denials are never absence. OpenBao's exact empty 404 + # metadata response is the only absent representation admitted here. + if status==404 and response=={'errors':[]}: + return None + if status!=200 or response.get('errors') or type(response.get('data')) is not dict: + raise Refused('metadata read not confirmed') + value=dict(response['data']) + if path.startswith('sys/policies/acl/'): + if set(value)!={'cas_required','modified','name','policy','version'}: + raise Refused('unrecognized policy metadata fields') + modified=value.pop('modified') + if type(modified) is not str or datetime.fromisoformat(modified.replace('Z','+00:00')).tzinfo is None: + raise Refused('invalid policy modification timestamp') + # All role fields, aliases, defaults and unknown additions participate in + # exact canonical JSON comparison. True never equals integer 1 here. + return value + + def verify_metadata(self): + self._fresh() + if self._next_metadata!=6 or self.journal.observe(self.binding)['state']!='applying': + raise Refused('complete same-live metadata sequence required') + for phase in ('before','after'): + if encoded(self._observed[phase])!=encoded(self.expected[phase]): + raise Refused('complete expected metadata evidence required') + evidence=content_hash(encoded({'expected_before':'sha256:'+content_hash(self.admission.expected_before), + 'expected_after':'sha256:'+content_hash(self.admission.expected_after), + 'scope':'per-target-sequential-readback-no-native-cas'})) + self.journal.transition(self.binding,'applying','readback',evidence) + self.journal.transition(self.binding,'readback','verified',evidence) + return evidence + def _cleanup_admitted(self, kind, digest): self._live() expected=next(item for item in self.cleanup if item.kind==kind) diff --git a/tests/test_task_execution_mediator.py b/tests/test_task_execution_mediator.py index 8ad9045..52024f1 100644 --- a/tests/test_task_execution_mediator.py +++ b/tests/test_task_execution_mediator.py @@ -99,7 +99,9 @@ def test_whole_strict_consume_confirmation_precedes_real_native_effect(owner,tmp with whole_fixture(owner,tmp_path) as fixture: result=invoke(fixture) assert result['native_calls_completed']==6 and result['cleanup_confirmed'] - assert result['native_apply']=='unverified' # total owner baseline/readback acceptance remains separate + assert result['native_apply']=='verified' + assert result['business_state']=='cleanup-confirmed' + assert len(result['readback_evidence_digest'])==64 obj=fixture.approval.engine.get(fixture.approval_id) assert obj.status=='consumed' assert obj.consumed_digest==result['request_digest'] diff --git a/tests/test_task_execution_native_fixture.py b/tests/test_task_execution_native_fixture.py index 2a8e419..4c54313 100644 --- a/tests/test_task_execution_native_fixture.py +++ b/tests/test_task_execution_native_fixture.py @@ -78,6 +78,19 @@ def owner(compiler,bao_dev,tmp_path): 'backend_origin':origin,'cluster_uid':'disposable-fixture-only', 'operations':req['operations'],'operation_deadline':(datetime.now(timezone.utc)+timedelta(seconds=240)).isoformat(), 'backend_budget':{'ttl_seconds':120,'uses':12},'expected_before':'sha256:'+'0'*64,'expected_after':'sha256:'+'1'*64} + # Independently installed synthetic expected state, before any native write. + policy_call=compiler.before_call(plan,1);role_call=compiler.before_call(plan,4) + body=json.loads(role_call.payload) + before=m.encoded({policy_call.path:None,role_call.path:None}) + after=m.encoded({policy_call.path:{'cas_required':False,'name':policy_call.path.rsplit('/',1)[1], + 'policy':json.loads(policy_call.payload)['policy'],'version':1},role_call.path:{ + 'alias_name_source':'serviceaccount_uid','bound_service_account_names':body['bound_service_account_names'], + 'bound_service_account_namespace_selector':'','bound_service_account_namespaces':body['bound_service_account_namespaces'], + 'policies':body['policies'].split(','),'token_bound_cidrs':[],'token_explicit_max_ttl':0, + 'token_max_ttl':0,'token_no_default_policy':False,'token_num_uses':0,'token_period':0, + 'token_policies':body['policies'].split(','),'token_strictly_bind_ip':False, + 'token_ttl':900,'token_type':'default','ttl':900}}) + descriptor.update(expected_before=m.tagged(before),expected_after=m.tagged(after)) # Exercise the actual consumer closed descriptor parser as well; source pins, # grant/expected-state digests remain deliberately synthetic owner fixtures. from secrets_engine.execution_contract import ExecutionContract @@ -107,7 +120,7 @@ def owner(compiler,bao_dev,tmp_path): plan=compiler.bind_execution(compiler.compile(req),descriptor,installed) binding=Binding('SYNTHETIC-APPROVAL-NOT-PRODUCTION',req['request_digest'],req['operation_id'],plan.content_digest) state=tmp_path/'private-journal';state.mkdir(mode=0o700);journal=Journal(state) - admission=FixtureAdmission(installed,root,identity,process) + admission=FixtureAdmission(installed,before,after,root,identity,process) native=NativeFixtureAdapter(compiler,plan,journal,binding,admission) try: yield native,journal,binding,call,compiler,descriptor finally: journal.close() @@ -140,6 +153,7 @@ def test_native_fixed_grant_metadata_denials_and_confirmed_cleanup(owner): assert role[1]['data']['bound_service_account_names']==expected['bound_service_account_names'] assert role[1]['data']['bound_service_account_namespaces']==expected['bound_service_account_namespaces'] assert role[1]['data']['token_policies']==expected['policies'].split(',') + assert len(native.verify_metadata())==64 # Root probes native capabilities without spending the limited task token. for path in ['sys/policies/acl/sibling','auth/kubernetes/role/sibling','secret/data/sibling','auth/token/create','sys/mounts/secret','auth/approle/role/sibling/secret-id']: status,data=call('POST','sys/capabilities',{'token':native.task.token,'paths':[path]}) @@ -152,7 +166,7 @@ def test_native_fixed_grant_metadata_denials_and_confirmed_cleanup(owner): assert status==400 and data.get('errors') in (['invalid accessor'],['1 error occurred:\n\t* invalid accessor\n\n']) assert result['confirmed'] assert call('GET','sys/policies/acl/'+native.policy_name)[0]==404 - assert journal.observe(binding)['state']=='applying' # native success is not full owner verification + assert journal.observe(binding)['state']=='verified' with pytest.raises(Refused): native.metadata_call(6) with pytest.raises(Refused): run_cleanup(journal,binding,native) @@ -261,7 +275,7 @@ def test_nonloopback_estate_origin_is_refused_before_native_transport(owner): installed=m.encoded(changed) plan=changed_compiler.bind_execution(changed_compiler.compile(native.meta['request']),changed,installed) changed_binding=Binding(binding.approval_id,binding.request_digest,binding.operation_id,plan.content_digest) - admission=FixtureAdmission(installed,native.admission.owner_token,native.admission.identity_session,native.admission.server_process) + admission=replace(native.admission,descriptor=installed) with pytest.raises(Refused):NativeFixtureAdapter(changed_compiler,plan,journal,changed_binding,admission) @@ -294,7 +308,7 @@ def test_proposed_source_grant_never_materializes_even_with_matching_digest(owne installed=m.encoded(changed_descriptor) plan=changed_compiler.bind_execution(changed_compiler.compile(native.meta['request']),changed_descriptor,installed) changed_binding=Binding(binding.approval_id,binding.request_digest,binding.operation_id,plan.content_digest) - admission=FixtureAdmission(installed,native.admission.owner_token,native.admission.identity_session,native.admission.server_process) + admission=replace(native.admission,descriptor=installed) with pytest.raises(Refused):NativeFixtureAdapter(changed_compiler,plan,journal,changed_binding,admission) assert call('GET','sys/policies/acl/'+native.policy_name)[0]==404 diff --git a/tests/test_task_execution_readback.py b/tests/test_task_execution_readback.py new file mode 100644 index 0000000..5bc0b5b --- /dev/null +++ b/tests/test_task_execution_readback.py @@ -0,0 +1,127 @@ +"""Exact disposable native acceptance, partial effects and restart refusal.""" +import json +from dataclasses import replace +import pytest +from test_task_execution_native_fixture import owner,bao_dev,compiler,admit +from task_execution_native_fixture import NativeFixtureAdapter +from task_execution_journal import Refused,Journal +from task_execution_cleanup import run_cleanup + + +def start(native,journal,binding): + admit(native,journal,binding);native.materialize_grant() + journal.transition(binding,'grant-issued','applying') + + +def test_expectation_digest_pin_rejected_before_reserve(owner): + n,j,b,c,compiler,*_=owner + with pytest.raises(Refused,match='expected-state digest'): + NativeFixtureAdapter(compiler,n.plan,j,b,replace(n.admission,expected_after=b'{}')) + assert c('GET','sys/policies/acl/'+n.policy_name)[0]==404 + + +@pytest.mark.parametrize('target',[0,3]) +def test_actual_conflicting_baseline_gates_its_target_write(owner,target): + n,j,b,c,compiler,*_=owner + call=compiler.before_call(n.plan,target+1) + existing={'policy':'path "unrelated" { capabilities = ["read"] }'} if target==0 else json.loads(call.payload) + assert c('PUT',call.path,existing)[0] in {200,204} + with j.locked(): + start(n,j,b) + for i in range(target): n.metadata_call(i) + with pytest.raises(Refused,match='baseline or readback'):n.metadata_call(target) + assert j.observe(b)['state']=='unknown' + if target==3: + # Policy effect already happened: this is deliberately no aggregate CAS. + assert c('GET',compiler.before_call(n.plan,1).path)[1]['data']['policy']==json.loads(compiler.before_call(n.plan,1).payload)['policy'] + assert run_cleanup(j,b,n)['confirmed'] + assert j.observe(b)['state']=='unknown' + + +@pytest.mark.parametrize('target',[2,5]) +def test_actual_readback_drift_never_verifies(owner,target): + n,j,b,c,compiler,*_=owner + with j.locked(): + start(n,j,b) + for i in range(target):n.metadata_call(i) + call=compiler.before_call(n.plan,target-1) + body=json.loads(call.payload) + if target==2:body['policy']+='\npath "sibling/*" { capabilities = ["read"] }' + else:body['policies']+=',unexpected-policy' + assert c('PUT',call.path,body)[0] in {200,204} + with pytest.raises(Refused,match='baseline or readback'):n.metadata_call(target) + with pytest.raises(Refused):n.verify_metadata() + assert run_cleanup(j,b,n)['confirmed'] + assert j.observe(b)['state']=='unknown' + + +@pytest.mark.parametrize('status,response',[(403,{'errors':[]}), (404,{}),(404,{'errors':['permission denied']}), (200,{'data':None})]) +def test_only_exact_backend_absence_counts(status,response): + with pytest.raises(Refused):NativeFixtureAdapter._metadata_snapshot('auth/kubernetes/role/x',status,response) + + +def test_json_types_and_unknown_role_fields_remain_significant(owner): + n,j,b,c,compiler,*_=owner + original=n._http + def changed(method,path,token,body=b''): + status,response=original(method,path,token,body) + if method=='GET' and path==compiler.before_call(n.plan,5).path and status==200: + response['data']['token_num_uses']=False # False == 0 in Python; never canonical JSON. + return status,response + n._http=changed + with j.locked(): + start(n,j,b) + for i in range(5):n.metadata_call(i) + with pytest.raises(Refused):n.metadata_call(5) + assert run_cleanup(j,b,n)['confirmed'] + assert j.observe(b)['state']=='unknown' + + +def test_reopened_sqlite_cannot_resume_or_recover_memory_handles(owner): + n,j,b,c,compiler,*_=owner + with j.locked(): + start(n,j,b) + n.metadata_call(0);n.metadata_call(1) + # Model loss of the live admission; handles deliberately are not reconstructed. + # The still-running disposable server proves native effect survives reopening. + location=j.directory + assert c('GET',compiler.before_call(n.plan,1).path)[0]==200 + j.db.close() + # Owner journal retains the exact DB path; this is a real reopened SQLite. + reopened=Journal(location) + try: + with reopened.locked(): + assert reopened.observe(b)['dispatch_allowed'] is False + with pytest.raises(Refused):reopened.reserve(b,n.cleanup) + with pytest.raises(Refused):reopened.next_call_ordinal(b) + finally:reopened.close() + # No recovery/cleanup claim: only same-process fixture teardown retains handles. + + +def test_verified_readback_with_real_cleanup_denial_is_not_complete(owner): + n,j,b,c,compiler,*_=owner + with j.locked(): + start(n,j,b) + for i in range(6):n.metadata_call(i) + n.verify_metadata() + # Actual backend removal denies the task's self-revoke capability. + assert c('DELETE','sys/policies/acl/'+n.policy_name)[0] in {200,204} + result=run_cleanup(j,b,n) + assert result['confirmed'] is False + assert j.observe(b)['state']=='verified' + with pytest.raises(Refused):j.transition(b,'verified','cleanup-confirmed','0'*64) + assert c('POST','auth/token/lookup-accessor',{'accessor':n.task.accessor})[0]==200 + + +def test_actual_denied_baseline_cannot_be_caught_then_write_retried(owner): + n,j,b,c,compiler,*_=owner + with j.locked(): + start(n,j,b) + # Task still has update but loses read: real 403 is not absent baseline. + denied=n.hcl.replace('["read", "create", "update"]','["create", "update"]') + assert c('PUT','sys/policies/acl/'+n.policy_name,{'policy':denied})[0] in {200,204} + with pytest.raises(Refused):n.metadata_call(0) + assert j.observe(b)['state']=='unknown' + with pytest.raises(Refused):n.metadata_call(1) + assert c('GET',compiler.before_call(n.plan,1).path)[0]==404 + assert run_cleanup(j,b,n)['confirmed'] diff --git a/workplans/RPF-WP-0035-credential-lane-implementation.md b/workplans/RPF-WP-0035-credential-lane-implementation.md index 4ea8015..8b09305 100644 --- a/workplans/RPF-WP-0035-credential-lane-implementation.md +++ b/workplans/RPF-WP-0035-credential-lane-implementation.md @@ -970,6 +970,19 @@ and defaults stay unchanged. T02 still owns login/claim/TTL/use/MASON acceptance Secrets T06 separately owns dispatcher adoption and steady-state default migration. +2026-10-16 T12 wave 5 source preparation: disposable adapter now binds owner +canonical before/after metadata snapshots to existing descriptor hashes, rejects +per-target baseline/readback mismatch, and records readback/verified evidence. +Mediator reports cleanup-confirmed only after independent fixed cleanup receipts. +Complete role metadata and policy name/HCL/version/cas are compared; validated +modification timestamp alone is excluded. This is sequential fixture acceptance, +not aggregate CAS, concurrent-writer exclusion or native production completion. +Role conflict may follow a policy write. Restart refuses dispatch and cannot +recover memory-only token/accessor cleanup handles. T12 remains progress; actual +fresh CCR/catalog/grant, source/trust/caller/policy admission, isolated owner custody, +scoped recovery and attended native acceptance remain explicit prerequisites. + + 2026-10-11 T11 refinement (same source-review trial): the exact Secrets Engine v3 manifest `6bb05bac74e4939c3e42db2ae95c5f9fb4616bb20cc285811a315c26669ebce1` and all named bytes/proof were verified and independently re-reviewed. The three