@origintrail-official/dkg-agent 10.0.19 → 10.0.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/approved-private-replica.d.ts +54 -0
- package/dist/approved-private-replica.d.ts.map +1 -0
- package/dist/approved-private-replica.js +148 -0
- package/dist/approved-private-replica.js.map +1 -0
- package/dist/bounded-operation.d.ts +2 -0
- package/dist/bounded-operation.d.ts.map +1 -1
- package/dist/bounded-operation.js +5 -0
- package/dist/bounded-operation.js.map +1 -1
- package/dist/chain-finality-confirmations-v1.d.ts +7 -0
- package/dist/chain-finality-confirmations-v1.d.ts.map +1 -0
- package/dist/chain-finality-confirmations-v1.js +9 -0
- package/dist/chain-finality-confirmations-v1.js.map +1 -0
- package/dist/chain-reconciler.d.ts +23 -0
- package/dist/chain-reconciler.d.ts.map +1 -1
- package/dist/chain-reconciler.js +35 -0
- package/dist/chain-reconciler.js.map +1 -1
- package/dist/confirmed-graph-scoped-vm-resolver.d.ts +7 -1
- package/dist/confirmed-graph-scoped-vm-resolver.d.ts.map +1 -1
- package/dist/confirmed-graph-scoped-vm-resolver.js +5 -3
- package/dist/confirmed-graph-scoped-vm-resolver.js.map +1 -1
- package/dist/context-graph-authority-dependency.d.ts +11 -0
- package/dist/context-graph-authority-dependency.d.ts.map +1 -0
- package/dist/context-graph-authority-dependency.js +50 -0
- package/dist/context-graph-authority-dependency.js.map +1 -0
- package/dist/context-graph-binding-state.d.ts +6 -0
- package/dist/context-graph-binding-state.d.ts.map +1 -1
- package/dist/context-graph-binding-state.js +17 -10
- package/dist/context-graph-binding-state.js.map +1 -1
- package/dist/context-graph-claim-proof.d.ts +74 -0
- package/dist/context-graph-claim-proof.d.ts.map +1 -0
- package/dist/context-graph-claim-proof.js +137 -0
- package/dist/context-graph-claim-proof.js.map +1 -0
- package/dist/context-graph-member-proof.d.ts +33 -0
- package/dist/context-graph-member-proof.d.ts.map +1 -0
- package/dist/context-graph-member-proof.js +142 -0
- package/dist/context-graph-member-proof.js.map +1 -0
- package/dist/context-graph-membership-persist-scheduler.d.ts +7 -23
- package/dist/context-graph-membership-persist-scheduler.d.ts.map +1 -1
- package/dist/context-graph-membership-persist-scheduler.js +12 -88
- package/dist/context-graph-membership-persist-scheduler.js.map +1 -1
- package/dist/context-graph-meta-confirmation.d.ts +22 -2
- package/dist/context-graph-meta-confirmation.d.ts.map +1 -1
- package/dist/context-graph-meta-confirmation.js +54 -23
- package/dist/context-graph-meta-confirmation.js.map +1 -1
- package/dist/context-graph-meta-projection.d.ts +13 -2
- package/dist/context-graph-meta-projection.d.ts.map +1 -1
- package/dist/context-graph-meta-projection.js +61 -24
- package/dist/context-graph-meta-projection.js.map +1 -1
- package/dist/context-graph-metadata-relocation.d.ts +18 -30
- package/dist/context-graph-metadata-relocation.d.ts.map +1 -1
- package/dist/context-graph-metadata-relocation.js +32 -2
- package/dist/context-graph-metadata-relocation.js.map +1 -1
- package/dist/context-graph-private-meta-proof.d.ts +8 -9
- package/dist/context-graph-private-meta-proof.d.ts.map +1 -1
- package/dist/context-graph-private-meta-proof.js +25 -89
- package/dist/context-graph-private-meta-proof.js.map +1 -1
- package/dist/context-graph-public-meta-proof.d.ts +11 -1
- package/dist/context-graph-public-meta-proof.d.ts.map +1 -1
- package/dist/context-graph-public-meta-proof.js +23 -3
- package/dist/context-graph-public-meta-proof.js.map +1 -1
- package/dist/context-graph-read-authority.d.ts +30 -3
- package/dist/context-graph-read-authority.d.ts.map +1 -1
- package/dist/context-graph-read-authority.js +62 -18
- package/dist/context-graph-read-authority.js.map +1 -1
- package/dist/context-graph-storage-discovery.d.ts +14 -4
- package/dist/context-graph-storage-discovery.d.ts.map +1 -1
- package/dist/context-graph-storage-discovery.js +27 -7
- package/dist/context-graph-storage-discovery.js.map +1 -1
- package/dist/context-graph-subscription-authority-recovery.d.ts +7 -0
- package/dist/context-graph-subscription-authority-recovery.d.ts.map +1 -1
- package/dist/context-graph-subscription-authority-recovery.js +1 -0
- package/dist/context-graph-subscription-authority-recovery.js.map +1 -1
- package/dist/context-graph-subscription-persist-scheduler.d.ts +52 -0
- package/dist/context-graph-subscription-persist-scheduler.d.ts.map +1 -0
- package/dist/context-graph-subscription-persist-scheduler.js +78 -0
- package/dist/context-graph-subscription-persist-scheduler.js.map +1 -0
- package/dist/curator-dial-address.d.ts +16 -0
- package/dist/curator-dial-address.d.ts.map +1 -0
- package/dist/curator-dial-address.js +58 -0
- package/dist/curator-dial-address.js.map +1 -0
- package/dist/curator-meta-refresh.d.ts +18 -4
- package/dist/curator-meta-refresh.d.ts.map +1 -1
- package/dist/curator-meta-refresh.js +61 -58
- package/dist/curator-meta-refresh.js.map +1 -1
- package/dist/curator-peer-connection.d.ts +21 -0
- package/dist/curator-peer-connection.d.ts.map +1 -0
- package/dist/curator-peer-connection.js +41 -0
- package/dist/curator-peer-connection.js.map +1 -0
- package/dist/dkg-agent-agents-phonebook.d.ts +41 -0
- package/dist/dkg-agent-agents-phonebook.d.ts.map +1 -0
- package/dist/dkg-agent-agents-phonebook.js +155 -0
- package/dist/dkg-agent-agents-phonebook.js.map +1 -0
- package/dist/dkg-agent-base.d.ts +162 -24
- package/dist/dkg-agent-base.d.ts.map +1 -1
- package/dist/dkg-agent-base.js +175 -147
- package/dist/dkg-agent-base.js.map +1 -1
- package/dist/dkg-agent-cg-chain-observation.d.ts +6 -1
- package/dist/dkg-agent-cg-chain-observation.d.ts.map +1 -1
- package/dist/dkg-agent-cg-chain-observation.js +20 -5
- package/dist/dkg-agent-cg-chain-observation.js.map +1 -1
- package/dist/dkg-agent-cg-name-resolution.d.ts +38 -2
- package/dist/dkg-agent-cg-name-resolution.d.ts.map +1 -1
- package/dist/dkg-agent-cg-name-resolution.js +100 -8
- package/dist/dkg-agent-cg-name-resolution.js.map +1 -1
- package/dist/dkg-agent-cg-registry.d.ts +43 -3
- package/dist/dkg-agent-cg-registry.d.ts.map +1 -1
- package/dist/dkg-agent-cg-registry.js +267 -129
- package/dist/dkg-agent-cg-registry.js.map +1 -1
- package/dist/dkg-agent-cg-resolve.d.ts +6 -4
- package/dist/dkg-agent-cg-resolve.d.ts.map +1 -1
- package/dist/dkg-agent-cg-resolve.js +40 -18
- package/dist/dkg-agent-cg-resolve.js.map +1 -1
- package/dist/dkg-agent-constants.d.ts +10 -2
- package/dist/dkg-agent-constants.d.ts.map +1 -1
- package/dist/dkg-agent-constants.js +10 -2
- package/dist/dkg-agent-constants.js.map +1 -1
- package/dist/dkg-agent-context-graph.d.ts.map +1 -1
- package/dist/dkg-agent-context-graph.js +1 -5
- package/dist/dkg-agent-context-graph.js.map +1 -1
- package/dist/dkg-agent-crypto.d.ts +128 -21
- package/dist/dkg-agent-crypto.d.ts.map +1 -1
- package/dist/dkg-agent-crypto.js +824 -176
- package/dist/dkg-agent-crypto.js.map +1 -1
- package/dist/dkg-agent-diagnostics.d.ts.map +1 -1
- package/dist/dkg-agent-diagnostics.js +4 -3
- package/dist/dkg-agent-diagnostics.js.map +1 -1
- package/dist/dkg-agent-helpers.d.ts +2 -2
- package/dist/dkg-agent-helpers.d.ts.map +1 -1
- package/dist/dkg-agent-helpers.js +3 -3
- package/dist/dkg-agent-helpers.js.map +1 -1
- package/dist/dkg-agent-join.d.ts +12 -1
- package/dist/dkg-agent-join.d.ts.map +1 -1
- package/dist/dkg-agent-join.js +80 -92
- package/dist/dkg-agent-join.js.map +1 -1
- package/dist/dkg-agent-lifecycle.d.ts +129 -22
- package/dist/dkg-agent-lifecycle.d.ts.map +1 -1
- package/dist/dkg-agent-lifecycle.js +690 -211
- package/dist/dkg-agent-lifecycle.js.map +1 -1
- package/dist/dkg-agent-publish.d.ts +33 -40
- package/dist/dkg-agent-publish.d.ts.map +1 -1
- package/dist/dkg-agent-publish.js +194 -281
- package/dist/dkg-agent-publish.js.map +1 -1
- package/dist/dkg-agent-query.d.ts +7 -0
- package/dist/dkg-agent-query.d.ts.map +1 -1
- package/dist/dkg-agent-query.js +102 -86
- package/dist/dkg-agent-query.js.map +1 -1
- package/dist/dkg-agent-registered-private-empty-vm.d.ts +70 -0
- package/dist/dkg-agent-registered-private-empty-vm.d.ts.map +1 -0
- package/dist/dkg-agent-registered-private-empty-vm.js +133 -0
- package/dist/dkg-agent-registered-private-empty-vm.js.map +1 -0
- package/dist/dkg-agent-rfc64-catalog-auto-publish.d.ts +8 -3
- package/dist/dkg-agent-rfc64-catalog-auto-publish.d.ts.map +1 -1
- package/dist/dkg-agent-rfc64-catalog-auto-publish.js +45 -51
- package/dist/dkg-agent-rfc64-catalog-auto-publish.js.map +1 -1
- package/dist/dkg-agent-rfc64-catalog.d.ts +87 -80
- package/dist/dkg-agent-rfc64-catalog.d.ts.map +1 -1
- package/dist/dkg-agent-rfc64-catalog.js +726 -476
- package/dist/dkg-agent-rfc64-catalog.js.map +1 -1
- package/dist/dkg-agent-rfc64-meta-bootstrap.d.ts +6 -1
- package/dist/dkg-agent-rfc64-meta-bootstrap.d.ts.map +1 -1
- package/dist/dkg-agent-rfc64-meta-bootstrap.js +29 -13
- package/dist/dkg-agent-rfc64-meta-bootstrap.js.map +1 -1
- package/dist/dkg-agent-rfc64-swm-catalog-projection-supervisor.d.ts +8 -0
- package/dist/dkg-agent-rfc64-swm-catalog-projection-supervisor.d.ts.map +1 -1
- package/dist/dkg-agent-rfc64-swm-catalog-projection-supervisor.js +249 -40
- package/dist/dkg-agent-rfc64-swm-catalog-projection-supervisor.js.map +1 -1
- package/dist/dkg-agent-rfc64-swm-catalog-projection.d.ts.map +1 -1
- package/dist/dkg-agent-rfc64-swm-catalog-projection.js +3 -2
- package/dist/dkg-agent-rfc64-swm-catalog-projection.js.map +1 -1
- package/dist/dkg-agent-swm-host.d.ts +217 -52
- package/dist/dkg-agent-swm-host.d.ts.map +1 -1
- package/dist/dkg-agent-swm-host.js +1305 -858
- package/dist/dkg-agent-swm-host.js.map +1 -1
- package/dist/dkg-agent-swm-state.d.ts +15 -0
- package/dist/dkg-agent-swm-state.d.ts.map +1 -1
- package/dist/dkg-agent-swm-state.js +43 -0
- package/dist/dkg-agent-swm-state.js.map +1 -1
- package/dist/dkg-agent-swm-substrate.d.ts +49 -8
- package/dist/dkg-agent-swm-substrate.d.ts.map +1 -1
- package/dist/dkg-agent-swm-substrate.js +219 -46
- package/dist/dkg-agent-swm-substrate.js.map +1 -1
- package/dist/dkg-agent-types.d.ts +86 -27
- package/dist/dkg-agent-types.d.ts.map +1 -1
- package/dist/dkg-agent-types.js.map +1 -1
- package/dist/dkg-agent-vm-promotion.d.ts +4 -0
- package/dist/dkg-agent-vm-promotion.d.ts.map +1 -1
- package/dist/dkg-agent-vm-promotion.js +40 -5
- package/dist/dkg-agent-vm-promotion.js.map +1 -1
- package/dist/dkg-agent-vm-reconcile-scheduling.d.ts +7 -0
- package/dist/dkg-agent-vm-reconcile-scheduling.d.ts.map +1 -1
- package/dist/dkg-agent-vm-reconcile-scheduling.js +18 -0
- package/dist/dkg-agent-vm-reconcile-scheduling.js.map +1 -1
- package/dist/dkg-agent.d.ts +31 -2
- package/dist/dkg-agent.d.ts.map +1 -1
- package/dist/dkg-agent.js +188 -72
- package/dist/dkg-agent.js.map +1 -1
- package/dist/exact-graph-content-verifier.d.ts.map +1 -1
- package/dist/exact-graph-content-verifier.js +3 -2
- package/dist/exact-graph-content-verifier.js.map +1 -1
- package/dist/finalization-handler.d.ts +26 -211
- package/dist/finalization-handler.d.ts.map +1 -1
- package/dist/finalization-handler.js +94 -1652
- package/dist/finalization-handler.js.map +1 -1
- package/dist/finalization-recovery-sqlite-displacement.d.ts +37 -0
- package/dist/finalization-recovery-sqlite-displacement.d.ts.map +1 -0
- package/dist/finalization-recovery-sqlite-displacement.js +102 -0
- package/dist/finalization-recovery-sqlite-displacement.js.map +1 -0
- package/dist/finalization-recovery-sqlite-policy.d.ts +20 -4
- package/dist/finalization-recovery-sqlite-policy.d.ts.map +1 -1
- package/dist/finalization-recovery-sqlite-policy.js +21 -5
- package/dist/finalization-recovery-sqlite-policy.js.map +1 -1
- package/dist/finalization-recovery-sqlite-rows.d.ts +53 -0
- package/dist/finalization-recovery-sqlite-rows.d.ts.map +1 -0
- package/dist/finalization-recovery-sqlite-rows.js +77 -0
- package/dist/finalization-recovery-sqlite-rows.js.map +1 -0
- package/dist/finalization-recovery-sqlite-store.d.ts +10 -4
- package/dist/finalization-recovery-sqlite-store.d.ts.map +1 -1
- package/dist/finalization-recovery-sqlite-store.js +49 -50
- package/dist/finalization-recovery-sqlite-store.js.map +1 -1
- package/dist/finalization-recovery-store.d.ts +18 -0
- package/dist/finalization-recovery-store.d.ts.map +1 -1
- package/dist/finalization-recovery-store.js +24 -0
- package/dist/finalization-recovery-store.js.map +1 -1
- package/dist/finalization-recovery-worker.js +1 -1
- package/dist/finalization-recovery-worker.js.map +1 -1
- package/dist/finalization-recovery.d.ts +2 -3
- package/dist/finalization-recovery.d.ts.map +1 -1
- package/dist/finalization-recovery.js +12 -9
- package/dist/finalization-recovery.js.map +1 -1
- package/dist/finalized-authority-cold-resolution.d.ts +6 -1
- package/dist/finalized-authority-cold-resolution.d.ts.map +1 -1
- package/dist/finalized-authority-cold-resolution.js +22 -3
- package/dist/finalized-authority-cold-resolution.js.map +1 -1
- package/dist/gossip-publish-handler.d.ts.map +1 -1
- package/dist/gossip-publish-handler.js +4 -2
- package/dist/gossip-publish-handler.js.map +1 -1
- package/dist/gossip-session.d.ts +29 -0
- package/dist/gossip-session.d.ts.map +1 -0
- package/dist/gossip-session.js +48 -0
- package/dist/gossip-session.js.map +1 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/internal/context-graph-authority/approved-member-acceptance.d.ts +61 -0
- package/dist/internal/context-graph-authority/approved-member-acceptance.d.ts.map +1 -0
- package/dist/internal/context-graph-authority/approved-member-acceptance.js +75 -0
- package/dist/internal/context-graph-authority/approved-member-acceptance.js.map +1 -0
- package/dist/internal/context-graph-authority/context-graph-agent-gate-authority.d.ts +5 -5
- package/dist/internal/context-graph-authority/context-graph-agent-gate-authority.d.ts.map +1 -1
- package/dist/internal/context-graph-authority/context-graph-agent-gate-authority.js +59 -49
- package/dist/internal/context-graph-authority/context-graph-agent-gate-authority.js.map +1 -1
- package/dist/internal/context-graph-authority/context-graph-authority.d.ts +1 -0
- package/dist/internal/context-graph-authority/context-graph-authority.d.ts.map +1 -1
- package/dist/internal/context-graph-authority/context-graph-authority.js +25 -0
- package/dist/internal/context-graph-authority/context-graph-authority.js.map +1 -1
- package/dist/internal/context-graph-authority/finalized-target-projection.d.ts +17 -0
- package/dist/internal/context-graph-authority/finalized-target-projection.d.ts.map +1 -0
- package/dist/internal/context-graph-authority/finalized-target-projection.js +36 -0
- package/dist/internal/context-graph-authority/finalized-target-projection.js.map +1 -0
- package/dist/internal/context-graph-authority/swm-transport-authority.d.ts +84 -0
- package/dist/internal/context-graph-authority/swm-transport-authority.d.ts.map +1 -0
- package/dist/internal/context-graph-authority/swm-transport-authority.js +175 -0
- package/dist/internal/context-graph-authority/swm-transport-authority.js.map +1 -0
- package/dist/internal/context-graph-binding-abort.d.ts +3 -0
- package/dist/internal/context-graph-binding-abort.d.ts.map +1 -0
- package/dist/internal/context-graph-binding-abort.js +32 -0
- package/dist/internal/context-graph-binding-abort.js.map +1 -0
- package/dist/internal/context-graph-cache-invalidating-store.d.ts +3 -0
- package/dist/internal/context-graph-cache-invalidating-store.d.ts.map +1 -0
- package/dist/internal/context-graph-cache-invalidating-store.js +167 -0
- package/dist/internal/context-graph-cache-invalidating-store.js.map +1 -0
- package/dist/internal/context-graph-meta-record-copy.d.ts +3 -0
- package/dist/internal/context-graph-meta-record-copy.d.ts.map +1 -0
- package/dist/internal/context-graph-meta-record-copy.js +21 -0
- package/dist/internal/context-graph-meta-record-copy.js.map +1 -0
- package/dist/internal/gossip/host-mode-handler.d.ts +11 -0
- package/dist/internal/gossip/host-mode-handler.d.ts.map +1 -0
- package/dist/internal/gossip/host-mode-handler.js +41 -0
- package/dist/internal/gossip/host-mode-handler.js.map +1 -0
- package/dist/internal/join-encryption-key-bundle.d.ts +10 -0
- package/dist/internal/join-encryption-key-bundle.d.ts.map +1 -0
- package/dist/internal/join-encryption-key-bundle.js +72 -0
- package/dist/internal/join-encryption-key-bundle.js.map +1 -0
- package/dist/internal/knowledge-asset-vm-publish-request.d.ts +31 -0
- package/dist/internal/knowledge-asset-vm-publish-request.d.ts.map +1 -0
- package/dist/internal/knowledge-asset-vm-publish-request.js +91 -0
- package/dist/internal/knowledge-asset-vm-publish-request.js.map +1 -0
- package/dist/internal/lifecycle-sync-policy.d.ts +5 -0
- package/dist/internal/lifecycle-sync-policy.d.ts.map +1 -0
- package/dist/internal/lifecycle-sync-policy.js +12 -0
- package/dist/internal/lifecycle-sync-policy.js.map +1 -0
- package/dist/internal/local-private-member.d.ts +14 -0
- package/dist/internal/local-private-member.d.ts.map +1 -0
- package/dist/internal/local-private-member.js +21 -0
- package/dist/internal/local-private-member.js.map +1 -0
- package/dist/internal/promote/assertion-promote-precommit.d.ts +41 -1
- package/dist/internal/promote/assertion-promote-precommit.d.ts.map +1 -1
- package/dist/internal/promote/assertion-promote-precommit.js +79 -22
- package/dist/internal/promote/assertion-promote-precommit.js.map +1 -1
- package/dist/internal/storage-ack-owned-request.d.ts +16 -0
- package/dist/internal/storage-ack-owned-request.d.ts.map +1 -0
- package/dist/internal/storage-ack-owned-request.js +16 -0
- package/dist/internal/storage-ack-owned-request.js.map +1 -0
- package/dist/internal/vm-reconcile-local-admission-wait.d.ts +96 -0
- package/dist/internal/vm-reconcile-local-admission-wait.d.ts.map +1 -0
- package/dist/internal/vm-reconcile-local-admission-wait.js +226 -0
- package/dist/internal/vm-reconcile-local-admission-wait.js.map +1 -0
- package/dist/internal/vm-reconcile-read-authority.d.ts +27 -0
- package/dist/internal/vm-reconcile-read-authority.d.ts.map +1 -0
- package/dist/internal/vm-reconcile-read-authority.js +33 -0
- package/dist/internal/vm-reconcile-read-authority.js.map +1 -0
- package/dist/internal/vm-reconcile-sweep-admission.d.ts +2 -0
- package/dist/internal/vm-reconcile-sweep-admission.d.ts.map +1 -1
- package/dist/internal/vm-reconcile-sweep.d.ts +16 -4
- package/dist/internal/vm-reconcile-sweep.d.ts.map +1 -1
- package/dist/internal/vm-reconcile-sweep.js +91 -50
- package/dist/internal/vm-reconcile-sweep.js.map +1 -1
- package/dist/internal/workspace-projected-delegatees.d.ts +3 -0
- package/dist/internal/workspace-projected-delegatees.d.ts.map +1 -0
- package/dist/internal/workspace-projected-delegatees.js +34 -0
- package/dist/internal/workspace-projected-delegatees.js.map +1 -0
- package/dist/join-approval-restart-projection.d.ts +14 -0
- package/dist/join-approval-restart-projection.d.ts.map +1 -0
- package/dist/join-approval-restart-projection.js +32 -0
- package/dist/join-approval-restart-projection.js.map +1 -0
- package/dist/keyed-persist-scheduler.d.ts +72 -0
- package/dist/keyed-persist-scheduler.d.ts.map +1 -0
- package/dist/keyed-persist-scheduler.js +140 -0
- package/dist/keyed-persist-scheduler.js.map +1 -0
- package/dist/map-with-concurrency.d.ts +7 -0
- package/dist/map-with-concurrency.d.ts.map +1 -1
- package/dist/map-with-concurrency.js +36 -0
- package/dist/map-with-concurrency.js.map +1 -1
- package/dist/p2p/network-admission-coordinator.d.ts +15 -0
- package/dist/p2p/network-admission-coordinator.d.ts.map +1 -1
- package/dist/p2p/network-admission-coordinator.js +20 -2
- package/dist/p2p/network-admission-coordinator.js.map +1 -1
- package/dist/p2p/peer-connect.d.ts +10 -1
- package/dist/p2p/peer-connect.d.ts.map +1 -1
- package/dist/p2p/peer-connect.js +11 -2
- package/dist/p2p/peer-connect.js.map +1 -1
- package/dist/p2p/peer-selection.d.ts +8 -0
- package/dist/p2p/peer-selection.d.ts.map +1 -1
- package/dist/p2p/peer-selection.js +21 -12
- package/dist/p2p/peer-selection.js.map +1 -1
- package/dist/p2p/protocol-readiness.d.ts +3 -3
- package/dist/p2p/protocol-readiness.d.ts.map +1 -1
- package/dist/p2p/protocol-readiness.js +4 -3
- package/dist/p2p/protocol-readiness.js.map +1 -1
- package/dist/prepare-unscoped-context-graph-read-checks.d.ts.map +1 -1
- package/dist/prepare-unscoped-context-graph-read-checks.js +12 -0
- package/dist/prepare-unscoped-context-graph-read-checks.js.map +1 -1
- package/dist/registered-context-graph-authority.d.ts +19 -0
- package/dist/registered-context-graph-authority.d.ts.map +1 -1
- package/dist/registered-private-empty-vm-attempt-v1.d.ts +34 -0
- package/dist/registered-private-empty-vm-attempt-v1.d.ts.map +1 -0
- package/dist/registered-private-empty-vm-attempt-v1.js +120 -0
- package/dist/registered-private-empty-vm-attempt-v1.js.map +1 -0
- package/dist/reserved-ka-id-allocation.d.ts +12 -0
- package/dist/reserved-ka-id-allocation.d.ts.map +1 -0
- package/dist/reserved-ka-id-allocation.js +25 -0
- package/dist/reserved-ka-id-allocation.js.map +1 -0
- package/dist/rfc64/applied-catalog-authority-transition-v1.d.ts.map +1 -1
- package/dist/rfc64/applied-catalog-authority-transition-v1.js +15 -17
- package/dist/rfc64/applied-catalog-authority-transition-v1.js.map +1 -1
- package/dist/rfc64/authority-rpc-circuit-breaker-v1.d.ts +5 -3
- package/dist/rfc64/authority-rpc-circuit-breaker-v1.d.ts.map +1 -1
- package/dist/rfc64/authority-rpc-circuit-breaker-v1.js +9 -0
- package/dist/rfc64/authority-rpc-circuit-breaker-v1.js.map +1 -1
- package/dist/rfc64/catalog-completion-evidence-v1.d.ts +38 -0
- package/dist/rfc64/catalog-completion-evidence-v1.d.ts.map +1 -0
- package/dist/rfc64/catalog-completion-evidence-v1.js +49 -0
- package/dist/rfc64/catalog-completion-evidence-v1.js.map +1 -0
- package/dist/rfc64/catalog-operational-applied-heads-v1.d.ts +12 -13
- package/dist/rfc64/catalog-operational-applied-heads-v1.d.ts.map +1 -1
- package/dist/rfc64/catalog-operational-applied-heads-v1.js +17 -52
- package/dist/rfc64/catalog-operational-applied-heads-v1.js.map +1 -1
- package/dist/rfc64/catalog-operational-reads-v1.d.ts +13 -0
- package/dist/rfc64/catalog-operational-reads-v1.d.ts.map +1 -0
- package/dist/rfc64/catalog-operational-reads-v1.js +56 -0
- package/dist/rfc64/catalog-operational-reads-v1.js.map +1 -0
- package/dist/rfc64/catalog-operational-targets-v1.d.ts +55 -0
- package/dist/rfc64/catalog-operational-targets-v1.d.ts.map +1 -0
- package/dist/rfc64/catalog-operational-targets-v1.js +379 -0
- package/dist/rfc64/catalog-operational-targets-v1.js.map +1 -0
- package/dist/rfc64/catalog-repair-diagnostics-v1.d.ts +28 -0
- package/dist/rfc64/catalog-repair-diagnostics-v1.d.ts.map +1 -0
- package/dist/rfc64/catalog-repair-diagnostics-v1.js +93 -0
- package/dist/rfc64/catalog-repair-diagnostics-v1.js.map +1 -0
- package/dist/rfc64/catalog-repair-retry-v1.d.ts +13 -0
- package/dist/rfc64/catalog-repair-retry-v1.d.ts.map +1 -0
- package/dist/rfc64/catalog-repair-retry-v1.js +39 -0
- package/dist/rfc64/catalog-repair-retry-v1.js.map +1 -0
- package/dist/rfc64/catalog-replay-recovery-runtime-v1.d.ts +52 -0
- package/dist/rfc64/catalog-replay-recovery-runtime-v1.d.ts.map +1 -1
- package/dist/rfc64/catalog-replay-recovery-runtime-v1.js +188 -32
- package/dist/rfc64/catalog-replay-recovery-runtime-v1.js.map +1 -1
- package/dist/rfc64/catalog-replay-snapshot-runtime-v1.d.ts +2 -8
- package/dist/rfc64/catalog-replay-snapshot-runtime-v1.d.ts.map +1 -1
- package/dist/rfc64/catalog-replay-snapshot-runtime-v1.js +10 -37
- package/dist/rfc64/catalog-replay-snapshot-runtime-v1.js.map +1 -1
- package/dist/rfc64/catalog-rollout-authority-v1.d.ts +13 -0
- package/dist/rfc64/catalog-rollout-authority-v1.d.ts.map +1 -1
- package/dist/rfc64/catalog-rollout-authority-v1.js +11 -0
- package/dist/rfc64/catalog-rollout-authority-v1.js.map +1 -1
- package/dist/rfc64/catalog-row-authorship.d.ts +12 -0
- package/dist/rfc64/catalog-row-authorship.d.ts.map +1 -1
- package/dist/rfc64/catalog-row-authorship.js +110 -57
- package/dist/rfc64/catalog-row-authorship.js.map +1 -1
- package/dist/rfc64/inventory-v1/candidate.d.ts +31 -10
- package/dist/rfc64/inventory-v1/candidate.d.ts.map +1 -1
- package/dist/rfc64/inventory-v1/candidate.js +51 -16
- package/dist/rfc64/inventory-v1/candidate.js.map +1 -1
- package/dist/rfc64/inventory-v1/index.d.ts +1 -1
- package/dist/rfc64/inventory-v1/index.d.ts.map +1 -1
- package/dist/rfc64/inventory-v1/index.js +1 -1
- package/dist/rfc64/inventory-v1/index.js.map +1 -1
- package/dist/rfc64/inventory-v1/open.d.ts.map +1 -1
- package/dist/rfc64/inventory-v1/open.js +7 -4
- package/dist/rfc64/inventory-v1/open.js.map +1 -1
- package/dist/rfc64/inventory-v1/swm-author-inventory-persistence.d.ts +3 -1
- package/dist/rfc64/inventory-v1/swm-author-inventory-persistence.d.ts.map +1 -1
- package/dist/rfc64/inventory-v1/swm-author-inventory-persistence.js +15 -0
- package/dist/rfc64/inventory-v1/swm-author-inventory-persistence.js.map +1 -1
- package/dist/rfc64/legacy-swm-boundary-codec-v1.d.ts +25 -0
- package/dist/rfc64/legacy-swm-boundary-codec-v1.d.ts.map +1 -0
- package/dist/rfc64/legacy-swm-boundary-codec-v1.js +127 -0
- package/dist/rfc64/legacy-swm-boundary-codec-v1.js.map +1 -0
- package/dist/rfc64/legacy-swm-boundary-v1.d.ts +12 -0
- package/dist/rfc64/legacy-swm-boundary-v1.d.ts.map +1 -1
- package/dist/rfc64/legacy-swm-boundary-v1.js +83 -144
- package/dist/rfc64/legacy-swm-boundary-v1.js.map +1 -1
- package/dist/rfc64/private-catalog-subscription-readiness-v1.d.ts +33 -0
- package/dist/rfc64/private-catalog-subscription-readiness-v1.d.ts.map +1 -0
- package/dist/rfc64/private-catalog-subscription-readiness-v1.js +97 -0
- package/dist/rfc64/private-catalog-subscription-readiness-v1.js.map +1 -0
- package/dist/rfc64/private-read-roster-v1.d.ts +14 -0
- package/dist/rfc64/private-read-roster-v1.d.ts.map +1 -0
- package/dist/rfc64/private-read-roster-v1.js +57 -0
- package/dist/rfc64/private-read-roster-v1.js.map +1 -0
- package/dist/rfc64/public-catalog-native-receiver-v1.d.ts.map +1 -1
- package/dist/rfc64/public-catalog-native-receiver-v1.js +26 -21
- package/dist/rfc64/public-catalog-native-receiver-v1.js.map +1 -1
- package/dist/rfc64/public-catalog-native-reconciler-v1.d.ts +17 -11
- package/dist/rfc64/public-catalog-native-reconciler-v1.d.ts.map +1 -1
- package/dist/rfc64/public-catalog-native-reconciler-v1.js +10 -8
- package/dist/rfc64/public-catalog-native-reconciler-v1.js.map +1 -1
- package/dist/rfc64/public-catalog-receiver-v1.d.ts +9 -0
- package/dist/rfc64/public-catalog-receiver-v1.d.ts.map +1 -1
- package/dist/rfc64/public-catalog-receiver-v1.js +11 -2
- package/dist/rfc64/public-catalog-receiver-v1.js.map +1 -1
- package/dist/rfc64/public-catalog-service-v1.d.ts.map +1 -1
- package/dist/rfc64/public-catalog-service-v1.js +21 -22
- package/dist/rfc64/public-catalog-service-v1.js.map +1 -1
- package/dist/rfc64/public-catalog-successor-producer-v1.d.ts.map +1 -1
- package/dist/rfc64/public-catalog-successor-producer-v1.js +5 -4
- package/dist/rfc64/public-catalog-successor-producer-v1.js.map +1 -1
- package/dist/rfc64/registered-private-empty-vm-proof-v1.d.ts +20 -0
- package/dist/rfc64/registered-private-empty-vm-proof-v1.d.ts.map +1 -0
- package/dist/rfc64/registered-private-empty-vm-proof-v1.js +26 -0
- package/dist/rfc64/registered-private-empty-vm-proof-v1.js.map +1 -0
- package/dist/rfc64/swm-catalog-durable-asset-resolver-v1.d.ts.map +1 -1
- package/dist/rfc64/swm-catalog-durable-asset-resolver-v1.js +60 -53
- package/dist/rfc64/swm-catalog-durable-asset-resolver-v1.js.map +1 -1
- package/dist/rfc64/swm-inventory-catalog-reconciler-v1.d.ts +2 -1
- package/dist/rfc64/swm-inventory-catalog-reconciler-v1.d.ts.map +1 -1
- package/dist/rfc64/swm-inventory-catalog-reconciler-v1.js +44 -4
- package/dist/rfc64/swm-inventory-catalog-reconciler-v1.js.map +1 -1
- package/dist/rfc64/swm-inventory-shadow-runtime-v1.d.ts +3 -1
- package/dist/rfc64/swm-inventory-shadow-runtime-v1.d.ts.map +1 -1
- package/dist/rfc64/swm-inventory-shadow-runtime-v1.js +34 -0
- package/dist/rfc64/swm-inventory-shadow-runtime-v1.js.map +1 -1
- package/dist/sync/auth/pipe-request-tail.d.ts +5 -1
- package/dist/sync/auth/pipe-request-tail.d.ts.map +1 -1
- package/dist/sync/auth/pipe-request-tail.js +13 -3
- package/dist/sync/auth/pipe-request-tail.js.map +1 -1
- package/dist/sync/auth/request-build.d.ts +3 -0
- package/dist/sync/auth/request-build.d.ts.map +1 -1
- package/dist/sync/auth/request-build.js +11 -2
- package/dist/sync/auth/request-build.js.map +1 -1
- package/dist/sync/backpressure.d.ts +27 -16
- package/dist/sync/backpressure.d.ts.map +1 -1
- package/dist/sync/backpressure.js +66 -40
- package/dist/sync/backpressure.js.map +1 -1
- package/dist/sync/catchup-policy.d.ts +2 -0
- package/dist/sync/catchup-policy.d.ts.map +1 -1
- package/dist/sync/catchup-policy.js +2 -0
- package/dist/sync/catchup-policy.js.map +1 -1
- package/dist/sync/exact-asset-fetch.d.ts +22 -0
- package/dist/sync/exact-asset-fetch.d.ts.map +1 -1
- package/dist/sync/exact-asset-fetch.js +30 -3
- package/dist/sync/exact-asset-fetch.js.map +1 -1
- package/dist/sync/exact-assets.d.ts +7 -5
- package/dist/sync/exact-assets.d.ts.map +1 -1
- package/dist/sync/exact-assets.js +13 -8
- package/dist/sync/exact-assets.js.map +1 -1
- package/dist/sync/exact-batch-observation.d.ts +3 -0
- package/dist/sync/exact-batch-observation.d.ts.map +1 -0
- package/dist/sync/exact-batch-observation.js +13 -0
- package/dist/sync/exact-batch-observation.js.map +1 -0
- package/dist/sync/exact-batch-stream-capability.d.ts +24 -0
- package/dist/sync/exact-batch-stream-capability.d.ts.map +1 -0
- package/dist/sync/exact-batch-stream-capability.js +88 -0
- package/dist/sync/exact-batch-stream-capability.js.map +1 -0
- package/dist/sync/exact-batch-stream-contract.d.ts +78 -0
- package/dist/sync/exact-batch-stream-contract.d.ts.map +1 -0
- package/dist/sync/exact-batch-stream-contract.js +206 -0
- package/dist/sync/exact-batch-stream-contract.js.map +1 -0
- package/dist/sync/on-connect/sync-on-connect.d.ts +1 -1
- package/dist/sync/on-connect/sync-on-connect.d.ts.map +1 -1
- package/dist/sync/on-connect/sync-on-connect.js +3 -3
- package/dist/sync/on-connect/sync-on-connect.js.map +1 -1
- package/dist/sync/on-demand-agents-phonebook.d.ts +162 -0
- package/dist/sync/on-demand-agents-phonebook.d.ts.map +1 -0
- package/dist/sync/on-demand-agents-phonebook.js +571 -0
- package/dist/sync/on-demand-agents-phonebook.js.map +1 -0
- package/dist/sync/peer-connection.d.ts.map +1 -1
- package/dist/sync/peer-connection.js +11 -2
- package/dist/sync/peer-connection.js.map +1 -1
- package/dist/sync/priority-admission-queue.d.ts +17 -0
- package/dist/sync/priority-admission-queue.d.ts.map +1 -1
- package/dist/sync/priority-admission-queue.js +58 -37
- package/dist/sync/priority-admission-queue.js.map +1 -1
- package/dist/sync/requester/admission-boundary.d.ts +12 -0
- package/dist/sync/requester/admission-boundary.d.ts.map +1 -0
- package/dist/sync/requester/admission-boundary.js +22 -0
- package/dist/sync/requester/admission-boundary.js.map +1 -0
- package/dist/sync/requester/durable-sync.d.ts +2 -1
- package/dist/sync/requester/durable-sync.d.ts.map +1 -1
- package/dist/sync/requester/durable-sync.js +3 -188
- package/dist/sync/requester/durable-sync.js.map +1 -1
- package/dist/sync/requester/exact-batch-stream-driver.d.ts +83 -0
- package/dist/sync/requester/exact-batch-stream-driver.d.ts.map +1 -0
- package/dist/sync/requester/exact-batch-stream-driver.js +299 -0
- package/dist/sync/requester/exact-batch-stream-driver.js.map +1 -0
- package/dist/sync/requester/exact-batch-stream.d.ts +72 -0
- package/dist/sync/requester/exact-batch-stream.d.ts.map +1 -0
- package/dist/sync/requester/exact-batch-stream.js +267 -0
- package/dist/sync/requester/exact-batch-stream.js.map +1 -0
- package/dist/sync/requester/exact-recovery-transport.d.ts +19 -0
- package/dist/sync/requester/exact-recovery-transport.d.ts.map +1 -0
- package/dist/sync/requester/exact-recovery-transport.js +3 -0
- package/dist/sync/requester/exact-recovery-transport.js.map +1 -0
- package/dist/sync/requester/finalized-swm-twin-reconciliation.js +12 -3
- package/dist/sync/requester/finalized-swm-twin-reconciliation.js.map +1 -1
- package/dist/sync/requester/graph-scoped-materialization.d.ts.map +1 -1
- package/dist/sync/requester/graph-scoped-materialization.js +61 -27
- package/dist/sync/requester/graph-scoped-materialization.js.map +1 -1
- package/dist/sync/requester/page-fetch.d.ts +7 -0
- package/dist/sync/requester/page-fetch.d.ts.map +1 -1
- package/dist/sync/requester/page-fetch.js +103 -49
- package/dist/sync/requester/page-fetch.js.map +1 -1
- package/dist/sync/requester/shared-memory-sync.d.ts +10 -5
- package/dist/sync/requester/shared-memory-sync.d.ts.map +1 -1
- package/dist/sync/requester/shared-memory-sync.js +149 -150
- package/dist/sync/requester/shared-memory-sync.js.map +1 -1
- package/dist/sync/requester/swm-materialization-memo.d.ts +12 -0
- package/dist/sync/requester/swm-materialization-memo.d.ts.map +1 -0
- package/dist/sync/requester/swm-materialization-memo.js +54 -0
- package/dist/sync/requester/swm-materialization-memo.js.map +1 -0
- package/dist/sync/requester/swm-recovery.d.ts +3 -29
- package/dist/sync/requester/swm-recovery.d.ts.map +1 -1
- package/dist/sync/requester/swm-recovery.js +11 -10
- package/dist/sync/requester/swm-recovery.js.map +1 -1
- package/dist/sync/requester/swm-snapshot-materializer.d.ts.map +1 -1
- package/dist/sync/requester/swm-snapshot-materializer.js +18 -0
- package/dist/sync/requester/swm-snapshot-materializer.js.map +1 -1
- package/dist/sync/requester/swm-target-executor.d.ts +6 -0
- package/dist/sync/requester/swm-target-executor.d.ts.map +1 -1
- package/dist/sync/requester/swm-target-executor.js +24 -3
- package/dist/sync/requester/swm-target-executor.js.map +1 -1
- package/dist/sync/requester/verified-asset-preparation.d.ts +9 -0
- package/dist/sync/requester/verified-asset-preparation.d.ts.map +1 -0
- package/dist/sync/requester/verified-asset-preparation.js +190 -0
- package/dist/sync/requester/verified-asset-preparation.js.map +1 -0
- package/dist/sync/responder/asset-metadata.d.ts +18 -0
- package/dist/sync/responder/asset-metadata.d.ts.map +1 -0
- package/dist/sync/responder/asset-metadata.js +27 -0
- package/dist/sync/responder/asset-metadata.js.map +1 -0
- package/dist/sync/responder/durable-data-request-policy.d.ts +2 -16
- package/dist/sync/responder/durable-data-request-policy.d.ts.map +1 -1
- package/dist/sync/responder/durable-data-request-policy.js +1 -31
- package/dist/sync/responder/durable-data-request-policy.js.map +1 -1
- package/dist/sync/responder/exact-asset-export-cache.d.ts +89 -0
- package/dist/sync/responder/exact-asset-export-cache.d.ts.map +1 -0
- package/dist/sync/responder/exact-asset-export-cache.js +550 -0
- package/dist/sync/responder/exact-asset-export-cache.js.map +1 -0
- package/dist/sync/responder/exact-batch-stream.d.ts +47 -0
- package/dist/sync/responder/exact-batch-stream.d.ts.map +1 -0
- package/dist/sync/responder/exact-batch-stream.js +151 -0
- package/dist/sync/responder/exact-batch-stream.js.map +1 -0
- package/dist/sync/responder/exact-data-session.d.ts +67 -0
- package/dist/sync/responder/exact-data-session.d.ts.map +1 -0
- package/dist/sync/responder/exact-data-session.js +130 -0
- package/dist/sync/responder/exact-data-session.js.map +1 -0
- package/dist/sync/responder/exact-graph-cursor-filter.d.ts +9 -0
- package/dist/sync/responder/exact-graph-cursor-filter.d.ts.map +1 -0
- package/dist/sync/responder/exact-graph-cursor-filter.js +101 -0
- package/dist/sync/responder/exact-graph-cursor-filter.js.map +1 -0
- package/dist/sync/responder/exact-graph-reader.d.ts +95 -0
- package/dist/sync/responder/exact-graph-reader.d.ts.map +1 -0
- package/dist/sync/responder/exact-graph-reader.js +623 -0
- package/dist/sync/responder/exact-graph-reader.js.map +1 -0
- package/dist/sync/responder/exact-page-reader.d.ts +37 -0
- package/dist/sync/responder/exact-page-reader.d.ts.map +1 -0
- package/dist/sync/responder/exact-page-reader.js +130 -0
- package/dist/sync/responder/exact-page-reader.js.map +1 -0
- package/dist/sync/responder/graph-plan.d.ts +19 -85
- package/dist/sync/responder/graph-plan.d.ts.map +1 -1
- package/dist/sync/responder/graph-plan.js +51 -988
- package/dist/sync/responder/graph-plan.js.map +1 -1
- package/dist/sync/responder/page-framing-policy.d.ts +31 -0
- package/dist/sync/responder/page-framing-policy.d.ts.map +1 -0
- package/dist/sync/responder/page-framing-policy.js +41 -0
- package/dist/sync/responder/page-framing-policy.js.map +1 -0
- package/dist/sync/responder/responder-row-page.d.ts +85 -0
- package/dist/sync/responder/responder-row-page.d.ts.map +1 -0
- package/dist/sync/responder/responder-row-page.js +181 -0
- package/dist/sync/responder/responder-row-page.js.map +1 -0
- package/dist/sync/responder/row-serialization.d.ts +34 -0
- package/dist/sync/responder/row-serialization.d.ts.map +1 -0
- package/dist/sync/responder/row-serialization.js +69 -0
- package/dist/sync/responder/row-serialization.js.map +1 -0
- package/dist/sync/responder/session-plan-memo.d.ts +20 -0
- package/dist/sync/responder/session-plan-memo.d.ts.map +1 -0
- package/dist/sync/responder/session-plan-memo.js +135 -0
- package/dist/sync/responder/session-plan-memo.js.map +1 -0
- package/dist/sync/responder/sync-handler.d.ts +26 -1
- package/dist/sync/responder/sync-handler.d.ts.map +1 -1
- package/dist/sync/responder/sync-handler.js +107 -58
- package/dist/sync/responder/sync-handler.js.map +1 -1
- package/dist/sync/shared-memory-completion.d.ts +30 -0
- package/dist/sync/shared-memory-completion.d.ts.map +1 -1
- package/dist/sync/shared-memory-completion.js +13 -0
- package/dist/sync/shared-memory-completion.js.map +1 -1
- package/dist/sync/system-context-graph-policy.d.ts +10 -0
- package/dist/sync/system-context-graph-policy.d.ts.map +1 -1
- package/dist/sync/system-context-graph-policy.js +11 -0
- package/dist/sync/system-context-graph-policy.js.map +1 -1
- package/dist/sync/wire-compression.d.ts +47 -0
- package/dist/sync/wire-compression.d.ts.map +1 -0
- package/dist/sync/wire-compression.js +111 -0
- package/dist/sync/wire-compression.js.map +1 -0
- package/dist/unscoped-query-consistency.d.ts +12 -0
- package/dist/unscoped-query-consistency.d.ts.map +1 -1
- package/dist/unscoped-query-consistency.js +17 -1
- package/dist/unscoped-query-consistency.js.map +1 -1
- package/dist/vm-promotion-audit.d.ts +2 -2
- package/dist/vm-promotion-audit.d.ts.map +1 -1
- package/dist/vm-promotion-audit.js +26 -9
- package/dist/vm-promotion-audit.js.map +1 -1
- package/dist/vm-publish-snapshot-preflight.d.ts +13 -0
- package/dist/vm-publish-snapshot-preflight.d.ts.map +1 -0
- package/dist/vm-publish-snapshot-preflight.js +94 -0
- package/dist/vm-publish-snapshot-preflight.js.map +1 -0
- package/dist/vm-reconcile-dispatcher.d.ts +42 -1
- package/dist/vm-reconcile-dispatcher.d.ts.map +1 -1
- package/dist/vm-reconcile-dispatcher.js +103 -4
- package/dist/vm-reconcile-dispatcher.js.map +1 -1
- package/dist/vm-reconcile-peer-topology.d.ts +7 -9
- package/dist/vm-reconcile-peer-topology.d.ts.map +1 -1
- package/dist/vm-reconcile-peer-topology.js +1 -37
- package/dist/vm-reconcile-peer-topology.js.map +1 -1
- package/dist/vm-recovery-candidate-order.d.ts +16 -0
- package/dist/vm-recovery-candidate-order.d.ts.map +1 -0
- package/dist/vm-recovery-candidate-order.js +24 -0
- package/dist/vm-recovery-candidate-order.js.map +1 -0
- package/dist/vm-recovery-core-transport-preference.d.ts +69 -0
- package/dist/vm-recovery-core-transport-preference.d.ts.map +1 -0
- package/dist/vm-recovery-core-transport-preference.js +131 -0
- package/dist/vm-recovery-core-transport-preference.js.map +1 -0
- package/dist/vm-recovery-experiment-policy.d.ts +24 -0
- package/dist/vm-recovery-experiment-policy.d.ts.map +1 -0
- package/dist/vm-recovery-experiment-policy.js +18 -0
- package/dist/vm-recovery-experiment-policy.js.map +1 -0
- package/dist/vm-recovery-footprint.d.ts +54 -0
- package/dist/vm-recovery-footprint.d.ts.map +1 -1
- package/dist/vm-recovery-footprint.js +88 -24
- package/dist/vm-recovery-footprint.js.map +1 -1
- package/dist/vm-recovery-microbatch.d.ts +2 -0
- package/dist/vm-recovery-microbatch.d.ts.map +1 -1
- package/dist/vm-recovery-microbatch.js +7 -3
- package/dist/vm-recovery-microbatch.js.map +1 -1
- package/dist/vm-recovery-pass-authority.d.ts +84 -0
- package/dist/vm-recovery-pass-authority.d.ts.map +1 -0
- package/dist/vm-recovery-pass-authority.js +126 -0
- package/dist/vm-recovery-pass-authority.js.map +1 -0
- package/dist/vm-recovery-phase-timing.d.ts +75 -0
- package/dist/vm-recovery-phase-timing.d.ts.map +1 -0
- package/dist/vm-recovery-phase-timing.js +236 -0
- package/dist/vm-recovery-phase-timing.js.map +1 -0
- package/dist/vm-recovery-preparation.d.ts +143 -0
- package/dist/vm-recovery-preparation.d.ts.map +1 -0
- package/dist/vm-recovery-preparation.js +433 -0
- package/dist/vm-recovery-preparation.js.map +1 -0
- package/dist/vm-recovery-provider-policy.d.ts +13 -1
- package/dist/vm-recovery-provider-policy.d.ts.map +1 -1
- package/dist/vm-recovery-provider-policy.js +37 -5
- package/dist/vm-recovery-provider-policy.js.map +1 -1
- package/dist/vm-recovery-stream-profile.d.ts +15 -0
- package/dist/vm-recovery-stream-profile.d.ts.map +1 -0
- package/dist/vm-recovery-stream-profile.js +53 -0
- package/dist/vm-recovery-stream-profile.js.map +1 -0
- package/dist/vm-recovery-stream-setback-policy.d.ts +54 -0
- package/dist/vm-recovery-stream-setback-policy.d.ts.map +1 -0
- package/dist/vm-recovery-stream-setback-policy.js +85 -0
- package/dist/vm-recovery-stream-setback-policy.js.map +1 -0
- package/dist/vm-recovery-timing-observer.d.ts +27 -0
- package/dist/vm-recovery-timing-observer.d.ts.map +1 -0
- package/dist/vm-recovery-timing-observer.js +49 -0
- package/dist/vm-recovery-timing-observer.js.map +1 -0
- package/dist/vm-recovery-transport-budget-policy.d.ts +32 -0
- package/dist/vm-recovery-transport-budget-policy.d.ts.map +1 -0
- package/dist/vm-recovery-transport-budget-policy.js +55 -0
- package/dist/vm-recovery-transport-budget-policy.js.map +1 -0
- package/dist/vm-recovery-transport-plan.d.ts +83 -0
- package/dist/vm-recovery-transport-plan.d.ts.map +1 -0
- package/dist/vm-recovery-transport-plan.js +117 -0
- package/dist/vm-recovery-transport-plan.js.map +1 -0
- package/dist/vm-refresh.d.ts +138 -0
- package/dist/vm-refresh.d.ts.map +1 -0
- package/dist/vm-refresh.js +201 -0
- package/dist/vm-refresh.js.map +1 -0
- package/package.json +19 -9
- package/dist/finalization-annotations.d.ts +0 -5
- package/dist/finalization-annotations.d.ts.map +0 -1
- package/dist/finalization-annotations.js +0 -6
- package/dist/finalization-annotations.js.map +0 -1
- package/dist/swm-ka-bound.d.ts +0 -21
- package/dist/swm-ka-bound.d.ts.map +0 -1
- package/dist/swm-ka-bound.js +0 -36
- package/dist/swm-ka-bound.js.map +0 -1
|
@@ -1,68 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { deleteByPatternWithoutCount, GraphManager, loadSelectedSharedMemoryQuads, loadSharedMemorySliceWithKaBoundFallback, asGraphWriteRevisionSource, resolveGraphScopedOrLegacyMetadata, resolveSharedMemoryScopeGraphs, tryReplaceGraphAtomically, tryReplaceGraphAndSubjectAtomically, StoreSchedulerBusyError, } from '@origintrail-official/dkg-storage';
|
|
1
|
+
import { decodeFinalizationMessage, contextGraphMetaUri, validateSubGraphName, validateContextGraphId, DKGEvent, Logger, createOperationContext, assertSafeIri, GRAPH_KA_CONTENT_SCOPE_VERSION, MemoryLayer, createGraphKnowledgeAssetScope, knowledgeAssetLayerGraphUri, sparqlString, } from '@origintrail-official/dkg-core';
|
|
2
|
+
import { GraphManager, resolveGraphScopedOrLegacyMetadata, resolveSharedMemoryScopeGraphs, tryReplaceGraphAtomically, tryReplaceGraphAndSubjectAtomically, StoreSchedulerBusyError, } from '@origintrail-official/dkg-storage';
|
|
4
3
|
import { resolvePublicFinalizedMaterializationAuthority, } from '@origintrail-official/dkg-chain';
|
|
5
|
-
import {
|
|
4
|
+
import { generateGraphKnowledgeAssetMetadata, compareMaterializedVersion, readMaterializedVersion, writeMaterializedVersion, materializedVersionQuad, withMaterializationLock, workspacePublicQuadsDigest, KnowledgeAssetWorkspaceHeadCorruptError, isKnowledgeAssetWorkspaceHeadCorruptError, resolveKnowledgeAssetWorkspaceHead, } from '@origintrail-official/dkg-publisher';
|
|
6
5
|
const DKG_NS = 'http://dkg.io/ontology/';
|
|
7
6
|
const PROV_NS = 'http://www.w3.org/ns/prov#';
|
|
8
7
|
const CHAIN_FINALIZED_RECONCILE_PEER_ID = 'chain-finalized-reconcile-v1';
|
|
9
|
-
// Slow-query / canary tags for the finalization SWM slice (#1549). A healthy fleet
|
|
10
|
-
// sees `.fallbackUnbounded` at ~0 relative to `.bounded`; a spike means the bound is
|
|
11
|
-
// mis-derived or recurrence is common, and `DKG_DISABLE_SWM_KA_BOUND=1` is the lever.
|
|
12
|
-
const SWM_SLICE_SOURCE = 'agent.finalization.sharedMemorySlice';
|
|
13
|
-
const SWM_SLICE_SOURCE_BOUNDED = `${SWM_SLICE_SOURCE}.bounded`;
|
|
14
|
-
const SWM_SLICE_SOURCE_WIDENED = `${SWM_SLICE_SOURCE}.fallbackUnbounded`;
|
|
15
8
|
import { ethers } from 'ethers';
|
|
16
|
-
import { createHash } from 'node:crypto';
|
|
17
|
-
import { deriveSwmKaGraphBound } from './swm-ka-bound.js';
|
|
18
9
|
import { FinalizationLifecycleLogger, finalizationLifecycleDecision, } from './finalization-lifecycle-logger.js';
|
|
19
10
|
import { FinalizationRecoveryCapacityError, FinalizationRecovery, } from './finalization-recovery.js';
|
|
20
11
|
import { FinalizationRecoveryWorker, } from './finalization-recovery-worker.js';
|
|
21
12
|
import { recoverReceiptBackedGraphScopedEvidence } from './receipt-backed-graph-scoped-evidence.js';
|
|
22
13
|
import { resolveConfirmedGraphScopedVm, resolveLocallyConfirmedGraphScopedVm, } from './confirmed-graph-scoped-vm-resolver.js';
|
|
23
14
|
import { verifyExactGraphContent, } from './exact-graph-content-verifier.js';
|
|
24
|
-
import { protobufScalarToBigInt, protobufScalarToNumber } from './protobuf-scalars.js';
|
|
25
15
|
import { createDurableFinalizationRecoveryEligibility, } from './finalization-recovery-eligibility.js';
|
|
26
|
-
/**
|
|
27
|
-
* Predicate for the durable per-root keep-root-copy signal the publisher
|
|
28
|
-
* persists into SWM workspace meta at publish time (the chain-driven
|
|
29
|
-
* reconcile path's equivalent of the gossip envelope's `keepRootCopyOnLabel`).
|
|
30
|
-
* Shared with `DKGAgent` so the write and read sites can't drift.
|
|
31
|
-
*/
|
|
32
|
-
export const KEEP_ROOT_COPY_PREDICATE = `${DKG_NS}keepRootCopyOnLabel`;
|
|
33
|
-
/**
|
|
34
|
-
* Reader-maintained memo (#1609): the flat-KC merkle root a WorkspaceOperation's
|
|
35
|
-
* SWM snapshot hashes to, paired with `SWM_SNAPSHOT_CONTENT_DIGEST_PREDICATE` (a
|
|
36
|
-
* cheap digest of the exact content that produced it), stamped onto the op subject
|
|
37
|
-
* (`urn:dkg:share:<cg>:<id>`) the first time `findSwmSnapshotInNamespace` computes
|
|
38
|
-
* it. Lets a chain-reconcile lookup (a) resolve a *present* KA's op by root
|
|
39
|
-
* directly (fast path), and (b) skip the expensive `computeFlatKCRoot` recompute
|
|
40
|
-
* for an op whose content is unchanged (the digest still matches) — the recompute
|
|
41
|
-
* that dominates beacon reconcile load when a KA published elsewhere forces a full
|
|
42
|
-
* O(#WorkspaceOperations) scan to conclude "not here".
|
|
43
|
-
*
|
|
44
|
-
* This memo is a bridge, NOT the durable fix (see OT-RFC-60): a WorkspaceOperation
|
|
45
|
-
* is assembled incrementally (data quads and private roots arrive over time via
|
|
46
|
-
* *entity-keyed* writes that do NOT rewrite the op subject), so the stamp can go
|
|
47
|
-
* stale without a structural invalidation. Correctness therefore does NOT rely on
|
|
48
|
-
* the stamp being fresh:
|
|
49
|
-
* - `verifyMerkleMatch` stays authoritative on the fast path — a stale stamp can
|
|
50
|
-
* only ever cause a *missed* promotion, never a wrong one.
|
|
51
|
-
* - the fallback scan re-reads EVERY op (it never excludes on stamp-presence) and
|
|
52
|
-
* trusts the memoized root only when the content digest still matches; any
|
|
53
|
-
* content change flips the digest → full recompute → the op is re-evaluated and
|
|
54
|
-
* re-stamped. An op can never be stranded by a stale stamp.
|
|
55
|
-
* The durable fix (OT-RFC-60) makes the root a write-maintained, indexed property
|
|
56
|
-
* stamped once when an op becomes complete-and-immutable, removing both the
|
|
57
|
-
* recompute AND the staleness by construction.
|
|
58
|
-
*
|
|
59
|
-
* Deliberately invisible to the sibling VM-reconcile negative cache: its
|
|
60
|
-
* `readVmReconcileSwmGen` / `vmReconcileWorkspaceOperationPattern` fingerprints
|
|
61
|
-
* select only `rootEntity`/`publishedAt` on the op subject (plus the separate data
|
|
62
|
-
* graph), so stamping these predicates into the meta graph does not perturb the
|
|
63
|
-
* generation signal the negative cache keys on — the two mechanisms don't fight.
|
|
64
|
-
*/
|
|
65
|
-
export { SWM_SNAPSHOT_CONTENT_DIGEST_PREDICATE, SWM_SNAPSHOT_MERKLE_ROOT_PREDICATE, } from './finalization-annotations.js';
|
|
66
16
|
function stripOptionalLiteral(value) {
|
|
67
17
|
if (!value)
|
|
68
18
|
return undefined;
|
|
@@ -101,45 +51,6 @@ function resolveGraphScopedAccessEnvelope(head, requestedAccess) {
|
|
|
101
51
|
function normalizedHex(value) {
|
|
102
52
|
return value.replace(/^0x/i, '').toLowerCase();
|
|
103
53
|
}
|
|
104
|
-
/**
|
|
105
|
-
* Ops kill-switch for the #1549 bounded SWM read. Set `DKG_DISABLE_SWM_KA_BOUND=1`
|
|
106
|
-
* to fall back to the unbounded read with no redeploy. Read here at the
|
|
107
|
-
* orchestration boundary so `deriveSwmKaGraphBound` (in `swm-ka-bound.ts`) stays a
|
|
108
|
-
* deterministic identity transform.
|
|
109
|
-
*/
|
|
110
|
-
function swmKaBoundDisabled() {
|
|
111
|
-
return process.env.DKG_DISABLE_SWM_KA_BOUND === '1';
|
|
112
|
-
}
|
|
113
|
-
/**
|
|
114
|
-
* TTL cap on the in-memory negative reconcile memo (#1609) — the longest a
|
|
115
|
-
* "no local SWM snapshot" verdict may be trusted without a fresh scan even
|
|
116
|
-
* when no write-generation change was observed. Bounds the exposure to any
|
|
117
|
-
* SWM writer invisible to the adapter's write-generation counter (e.g. a
|
|
118
|
-
* second process mutating a shared oxigraph-server) to "≤ TTL late", never
|
|
119
|
-
* "never". Read per call (like `swmKaBoundDisabled`) so ops and tests can
|
|
120
|
-
* retune it without a redeploy.
|
|
121
|
-
*/
|
|
122
|
-
const VM_RECONCILE_NEGATIVE_TTL_MS_DEFAULT = 600_000;
|
|
123
|
-
function vmReconcileNegativeTtlMs() {
|
|
124
|
-
const raw = Number(process.env['DKG_VM_RECONCILE_NEGATIVE_TTL_MS']);
|
|
125
|
-
return Number.isFinite(raw) && raw > 0 ? raw : VM_RECONCILE_NEGATIVE_TTL_MS_DEFAULT;
|
|
126
|
-
}
|
|
127
|
-
/** LRU cap for the negative reconcile memo — bounds memory across CGs × roots. */
|
|
128
|
-
const VM_RECONCILE_NEGATIVE_MEMO_MAX_ENTRIES = 4096;
|
|
129
|
-
const FINALIZATION_SWM_PAGE_ROWS_DEFAULT = 1_000;
|
|
130
|
-
const FINALIZATION_SWM_MAX_ROWS_DEFAULT = 250_000;
|
|
131
|
-
const FINALIZATION_SWM_MAX_BYTES_DEFAULT = 128 * 1024 * 1024;
|
|
132
|
-
function positiveIntegerEnv(name, fallback) {
|
|
133
|
-
const parsed = Number(process.env[name]);
|
|
134
|
-
return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback;
|
|
135
|
-
}
|
|
136
|
-
function finalizationSwmResultBudget() {
|
|
137
|
-
return {
|
|
138
|
-
pageRows: positiveIntegerEnv('DKG_FINALIZATION_SWM_PAGE_ROWS', FINALIZATION_SWM_PAGE_ROWS_DEFAULT),
|
|
139
|
-
maxRows: positiveIntegerEnv('DKG_FINALIZATION_SWM_MAX_ROWS', FINALIZATION_SWM_MAX_ROWS_DEFAULT),
|
|
140
|
-
maxBytesEstimate: positiveIntegerEnv('DKG_FINALIZATION_SWM_MAX_BYTES', FINALIZATION_SWM_MAX_BYTES_DEFAULT),
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
54
|
function isLegacyFinalizationEventBus(value) {
|
|
144
55
|
return value !== undefined
|
|
145
56
|
&& typeof value.emit === 'function';
|
|
@@ -172,32 +83,15 @@ export class FinalizationHandler {
|
|
|
172
83
|
lifecycle;
|
|
173
84
|
processedUals = new Set();
|
|
174
85
|
// Forward-prevention for the cgId-resolution race (RS heal): chain-authoritative
|
|
175
|
-
//
|
|
176
|
-
//
|
|
177
|
-
//
|
|
86
|
+
// KA-id -> cgId bindings, cached POSITIVE-ONLY. A 0/miss is NEVER cached:
|
|
87
|
+
// caching a miss before the on-chain KA->CG binding lands would pin
|
|
88
|
+
// finalization to the local fallback forever.
|
|
178
89
|
chainCgIdByLookupId = new Map();
|
|
179
|
-
// #1609 (2026-07-11/12 testnet incident): the write-generation source backing
|
|
180
|
-
// the negative reconcile memo below. `null` when the store's adapter doesn't
|
|
181
|
-
// track write generations — the memo is then DISABLED and every reconcile
|
|
182
|
-
// scans (fail-open), matching pre-memo behavior.
|
|
183
|
-
graphWriteGen;
|
|
184
|
-
// Negative memo for `findSwmSnapshotForMerkleRoot`: "this (cg, namespace,
|
|
185
|
-
// root) had NO matching local SWM snapshot at write generation G". Unlike
|
|
186
|
-
// `chainCgIdByLookupId` above, caching the negative here is sound BECAUSE it
|
|
187
|
-
// is generation-gated: the verdict is only replayed while the store proves
|
|
188
|
-
// no local write has touched the CG since the scan. LRU, in-memory only —
|
|
189
|
-
// a restart clears it (fail-open).
|
|
190
|
-
negativeSnapshotMemo = new Map();
|
|
191
|
-
/** Legacy WorkspaceOperation presence per (cg, namespace), gated the same way. */
|
|
192
|
-
legacySwmOperationsMemo = new Map();
|
|
193
|
-
/** Equivalent finalization/reconcile reads share one promise until it settles. */
|
|
194
|
-
scanSingleFlights = new Map();
|
|
195
90
|
recoveryWorker;
|
|
196
91
|
finalizationRecoveryEligibility;
|
|
197
92
|
constructor(store, chain, optionsOrEventBus, legacyResolveContextGraphOnChainId, legacyMarkContextGraphMetaDirtyFromQuads, legacyLifecycleLogOptions) {
|
|
198
93
|
const options = normalizeFinalizationHandlerOptions(optionsOrEventBus, legacyResolveContextGraphOnChainId, legacyMarkContextGraphMetaDirtyFromQuads, legacyLifecycleLogOptions);
|
|
199
94
|
this.store = store;
|
|
200
|
-
this.graphWriteGen = asGraphWriteRevisionSource(store);
|
|
201
95
|
this.chain = chain;
|
|
202
96
|
this.eventBus = options.eventBus;
|
|
203
97
|
this.resolveContextGraphOnChainId = options.resolveContextGraphOnChainId;
|
|
@@ -256,7 +150,6 @@ export class FinalizationHandler {
|
|
|
256
150
|
});
|
|
257
151
|
if (liveAdmission.status === 'invalid')
|
|
258
152
|
return;
|
|
259
|
-
let envelope;
|
|
260
153
|
let candidate;
|
|
261
154
|
if (liveAdmission.status === 'admitted') {
|
|
262
155
|
const liveCandidate = liveAdmission.input.candidate;
|
|
@@ -317,22 +210,16 @@ export class FinalizationHandler {
|
|
|
317
210
|
throw new Error(`Unhandled finalization recovery result: ${JSON.stringify(exhaustive)}`);
|
|
318
211
|
}
|
|
319
212
|
}
|
|
320
|
-
envelope = {
|
|
321
|
-
rawMessage: data,
|
|
322
|
-
msg: liveAdmission.message,
|
|
323
|
-
graphAdmission: { ok: true, value: candidate },
|
|
324
|
-
};
|
|
325
213
|
}
|
|
326
214
|
else {
|
|
327
|
-
|
|
328
|
-
}
|
|
329
|
-
if (!envelope)
|
|
215
|
+
this.ignoreUnscopedFinalization(data, contextGraphId);
|
|
330
216
|
return;
|
|
331
|
-
|
|
332
|
-
// durable recovery inbox
|
|
217
|
+
}
|
|
218
|
+
// A deployment without the durable recovery inbox applies the envelope
|
|
219
|
+
// unjournaled.
|
|
333
220
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
334
221
|
try {
|
|
335
|
-
await this.
|
|
222
|
+
await this.processUnjournaledFinalization(liveAdmission.input);
|
|
336
223
|
return;
|
|
337
224
|
}
|
|
338
225
|
catch (error) {
|
|
@@ -350,13 +237,9 @@ export class FinalizationHandler {
|
|
|
350
237
|
}
|
|
351
238
|
}
|
|
352
239
|
}
|
|
353
|
-
|
|
240
|
+
decodeFinalizationMessageOrWarn(rawMessage) {
|
|
354
241
|
try {
|
|
355
|
-
|
|
356
|
-
return {
|
|
357
|
-
rawMessage,
|
|
358
|
-
msg,
|
|
359
|
-
};
|
|
242
|
+
return decodeFinalizationMessage(rawMessage);
|
|
360
243
|
}
|
|
361
244
|
catch (error) {
|
|
362
245
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -443,276 +326,14 @@ export class FinalizationHandler {
|
|
|
443
326
|
return undefined;
|
|
444
327
|
}
|
|
445
328
|
}
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
const
|
|
329
|
+
/** Apply a graph-scoped envelope on a deployment without the durable recovery inbox. */
|
|
330
|
+
async processUnjournaledFinalization(input) {
|
|
331
|
+
const { msg } = input.candidate;
|
|
332
|
+
const ctx = msg.operationId
|
|
333
|
+
? createOperationContext('gossip', msg.operationId)
|
|
334
|
+
: createOperationContext('gossip');
|
|
450
335
|
try {
|
|
451
|
-
|
|
452
|
-
if (msg.operationId) {
|
|
453
|
-
ctx = createOperationContext('gossip', msg.operationId);
|
|
454
|
-
}
|
|
455
|
-
const isGraphScoped = msg.contentScopeVersion === GRAPH_KA_CONTENT_SCOPE_VERSION;
|
|
456
|
-
if (isGraphScoped) {
|
|
457
|
-
if (!graphAdmission?.ok) {
|
|
458
|
-
this.log.warn(ctx, `Finalization: invalid graph-scoped envelope for ${msg.ual || '(missing UAL)'}: `
|
|
459
|
-
+ `${graphAdmission?.reason ?? 'decode-failed'}`);
|
|
460
|
-
return 'deferred';
|
|
461
|
-
}
|
|
462
|
-
return await this.recovery.processUnjournaled({
|
|
463
|
-
rawMessage,
|
|
464
|
-
contextGraphId,
|
|
465
|
-
...(sourcePeerId ? { sourcePeerId } : {}),
|
|
466
|
-
candidate: graphAdmission.value,
|
|
467
|
-
});
|
|
468
|
-
}
|
|
469
|
-
if (msg.contextGraphId && msg.contextGraphId !== contextGraphId) {
|
|
470
|
-
// #1100: same guard as GossipPublishHandler — frames of other gossip
|
|
471
|
-
// message types decode "successfully" with garbage in this field, so
|
|
472
|
-
// only WARN when the mismatched value is a plausible CG id.
|
|
473
|
-
if (!validateContextGraphId(msg.contextGraphId).valid)
|
|
474
|
-
return 'deferred';
|
|
475
|
-
this.log.warn(ctx, `Finalization: contextGraphId "${msg.contextGraphId.slice(0, 120)}" does not match topic "${contextGraphId}", ignoring`);
|
|
476
|
-
return 'deferred';
|
|
477
|
-
}
|
|
478
|
-
// Deduplicate: skip if we already successfully processed this UAL
|
|
479
|
-
const dedupeKey = `${msg.ual}:${msg.txHash}`;
|
|
480
|
-
if (this.processedUals.has(dedupeKey)) {
|
|
481
|
-
this.log.info(ctx, `Finalization: already processed ${msg.ual}, skipping duplicate`);
|
|
482
|
-
return 'already-confirmed';
|
|
483
|
-
}
|
|
484
|
-
if (msg.contentScopeVersion !== undefined
|
|
485
|
-
&& msg.contentScopeVersion !== 0) {
|
|
486
|
-
this.log.warn(ctx, `Finalization: unsupported content scope version ${msg.contentScopeVersion}, ignoring`);
|
|
487
|
-
return 'deferred';
|
|
488
|
-
}
|
|
489
|
-
if (!msg.ual || !msg.txHash || msg.rootEntities.length === 0) {
|
|
490
|
-
this.log.warn(ctx, `Finalization: incomplete message (ual=${msg.ual}, txHash=${msg.txHash}, roots=${msg.rootEntities.length}, scope=${msg.contentScopeVersion ?? 0}), ignoring`);
|
|
491
|
-
return 'deferred';
|
|
492
|
-
}
|
|
493
|
-
const blockNumber = protobufScalarToNumber(msg.blockNumber);
|
|
494
|
-
const startKAId = protobufScalarToBigInt(msg.startKAId);
|
|
495
|
-
const endKAId = protobufScalarToBigInt(msg.endKAId);
|
|
496
|
-
let batchIdForResolve = 0n;
|
|
497
|
-
try {
|
|
498
|
-
batchIdForResolve = protobufScalarToBigInt(msg.batchId);
|
|
499
|
-
}
|
|
500
|
-
catch {
|
|
501
|
-
batchIdForResolve = 0n;
|
|
502
|
-
}
|
|
503
|
-
const ctxGraphId = await this.resolveFinalizationContextGraphId(contextGraphId, msg.targetContextGraphId || undefined, batchIdForResolve, ctx);
|
|
504
|
-
resolvedTargetContextGraphId = ctxGraphId;
|
|
505
|
-
// Validate sub-graph name from gossip — reject invalid names entirely
|
|
506
|
-
let subGraphName;
|
|
507
|
-
if (msg.subGraphName) {
|
|
508
|
-
const sgVal = validateSubGraphName(msg.subGraphName);
|
|
509
|
-
if (sgVal.valid) {
|
|
510
|
-
subGraphName = msg.subGraphName;
|
|
511
|
-
}
|
|
512
|
-
else {
|
|
513
|
-
this.log.warn(ctx, `Finalization: rejected message with invalid subGraphName "${msg.subGraphName}": ${sgVal.reason}`);
|
|
514
|
-
return 'deferred';
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
// Dedup guard: skip if this batch was already promoted (e.g. by ChainEventPoller).
|
|
518
|
-
// Read-both (review F5): also ASK the label `_meta` — the minimal
|
|
519
|
-
// per-cgId partition shape carries no `dkg:status` row.
|
|
520
|
-
const targetMetaGraph = ctxGraphId
|
|
521
|
-
? contextGraphMetaUri(contextGraphId, ctxGraphId)
|
|
522
|
-
: `did:dkg:context-graph:${contextGraphId}/_meta`;
|
|
523
|
-
const alreadyPromoted = await this.isAlreadyConfirmed(msg.ual, targetMetaGraph, `did:dkg:context-graph:${contextGraphId}/_meta`);
|
|
524
|
-
if (alreadyPromoted) {
|
|
525
|
-
this.markProcessed(dedupeKey);
|
|
526
|
-
this.lifecycle.record(ctx, finalizationLifecycleDecision('finalization_already_confirmed', {
|
|
527
|
-
...msg,
|
|
528
|
-
targetContextGraphId: ctxGraphId ?? msg.targetContextGraphId,
|
|
529
|
-
}));
|
|
530
|
-
this.log.info(ctx, `Finalization: ${msg.ual} already confirmed in ${ctxGraphId ? `context graph ${ctxGraphId}` : 'context graph'}, skipping`);
|
|
531
|
-
return 'already-confirmed';
|
|
532
|
-
}
|
|
533
|
-
// #1549: read this KA's per-author SWM under-graphs first, widening to today's
|
|
534
|
-
// unbounded read on empty-or-mismatch. All of the bound/widen policy — and the
|
|
535
|
-
// reasoning for why it is safe — lives in storage's
|
|
536
|
-
// `loadSharedMemorySliceWithKaBoundFallback`, which owns the widen.
|
|
537
|
-
const { quads: sharedMemoryQuads, matched: merkleMatchedQuads } = await this.loadFinalizationSwmSlice(contextGraphId, msg.rootEntities, subGraphName, swmKaBoundDisabled() ? undefined : deriveSwmKaGraphBound(startKAId, endKAId), msg.kcMerkleRoot, async () => {
|
|
538
|
-
const privateRoots = await this.getPrivateRootsFromMeta(contextGraphId, msg.rootEntities, subGraphName);
|
|
539
|
-
const allowGeneratedCatalogFloor = await this.allowsGeneratedCatalogFloor(contextGraphId, ctxGraphId);
|
|
540
|
-
return (quads) => this.sharedMemoryQuadsMatchingMerkle(contextGraphId, quads, privateRoots, msg.kcMerkleRoot, allowGeneratedCatalogFloor);
|
|
541
|
-
});
|
|
542
|
-
if (sharedMemoryQuads.length > 0) {
|
|
543
|
-
if (merkleMatchedQuads) {
|
|
544
|
-
const batchId = protobufScalarToBigInt(msg.batchId);
|
|
545
|
-
// PR #845 review #9: derive `txIndex` from the verified receipt
|
|
546
|
-
// (via `verifyOnChain`), NOT from gossip-supplied `msg.txIndex`.
|
|
547
|
-
// The latter is trust-based; a peer can forge an inflated index
|
|
548
|
-
// for a real publish `txHash` and lock out a legitimate
|
|
549
|
-
// same-block update on the receiver. The verified value comes
|
|
550
|
-
// from the on-chain log we matched (`log.transactionIndex`).
|
|
551
|
-
const { verified, authorAddress, txIndex: verifiedTxIndex } = await this.verifyOnChain(msg.txHash, blockNumber, msg.kcMerkleRoot, msg.publisherAddress, startKAId, endKAId, ctx, ctxGraphId, batchId);
|
|
552
|
-
if (verified) {
|
|
553
|
-
// Codex r5b — drop the rolling-upgrade legacy-publisher
|
|
554
|
-
// fallback. Earlier rounds inferred same-graph intent from
|
|
555
|
-
// `targetContextGraphId === local-on-chain-id-for(contextGraphId)`,
|
|
556
|
-
// but Codex r5b correctly observed that signal is ambiguous:
|
|
557
|
-
// it ALSO matches an explicit-remap-to-self publish (one where
|
|
558
|
-
// the legacy publisher passed `subContextGraphId === ownCG's
|
|
559
|
-
// own on-chain id` to deliberately drop the root copy). Both
|
|
560
|
-
// shapes hit `targetContextGraphId === local id` on the wire,
|
|
561
|
-
// so the fallback would re-add a root copy that the publisher
|
|
562
|
-
// had intentionally removed — a data-isolation regression.
|
|
563
|
-
//
|
|
564
|
-
// The cure is worse than the disease: trading a hard
|
|
565
|
-
// data-isolation bug for a soft query-discoverability gap is
|
|
566
|
-
// unacceptable. Without an unambiguous version/intent signal
|
|
567
|
-
// on the wire, a legacy publisher's same-graph publish stays
|
|
568
|
-
// queryable on receivers via per-cgId partitions but not via
|
|
569
|
-
// the bare `<cg>` label until the publisher upgrades to a
|
|
570
|
-
// tristate-emitting build and re-emits. New publishers always
|
|
571
|
-
// set the tristate (encoded with explicit KEEP/DROP) so the
|
|
572
|
-
// gap is bounded by the upgrade window. PR #779 has not
|
|
573
|
-
// shipped to any production peer yet, so this is the right
|
|
574
|
-
// moment to harden the contract.
|
|
575
|
-
const requestedKeepRootCopyOnLabel = msg.keepRootCopyOnLabel === true;
|
|
576
|
-
// PR #845 review #9: tiebreaker comes from chain-truth.
|
|
577
|
-
// verifyOnChain may not yield a txIndex if the matched event
|
|
578
|
-
// shape didn't carry it (e.g. mocks). Fall back to 0 in that
|
|
579
|
-
// case — matches pre-#845 ordering.
|
|
580
|
-
const finalizationVersion = {
|
|
581
|
-
blockNumber,
|
|
582
|
-
txIndex: typeof verifiedTxIndex === 'number' ? verifiedTxIndex : 0,
|
|
583
|
-
};
|
|
584
|
-
// PR #845 review #10: when same-graph dual-write is requested,
|
|
585
|
-
// BOTH the per-cgId target meta and the root label meta
|
|
586
|
-
// (`<cg>/_meta`) get rewritten. The pre-#845 code only guarded
|
|
587
|
-
// the per-cgId target, so a stale finalization could pass that
|
|
588
|
-
// check while an update had already stamped a newer version in
|
|
589
|
-
// the root label meta — the dual-write then re-inserted the
|
|
590
|
-
// old root-label data + meta on top of the update. Acquire the
|
|
591
|
-
// label-meta lock too (when dual-writing), and downgrade to
|
|
592
|
-
// per-cgId-only if the label projection is newer.
|
|
593
|
-
const isDualWrite = requestedKeepRootCopyOnLabel
|
|
594
|
-
&& !!ctxGraphId
|
|
595
|
-
&& !subGraphName;
|
|
596
|
-
const defaultMeta = `did:dkg:context-graph:${contextGraphId}/_meta`;
|
|
597
|
-
// PR #845 review #7: TOCTOU — serialise check + promotion +
|
|
598
|
-
// version stamp under the per-KA materialization lock so a
|
|
599
|
-
// concurrent stale writer cannot interleave between our
|
|
600
|
-
// `shouldApplyMaterialization` and `writeMaterializedVersion`.
|
|
601
|
-
const outcome = await this.applyVerifiedFinalization({
|
|
602
|
-
contextGraphId,
|
|
603
|
-
sharedMemoryQuads: merkleMatchedQuads,
|
|
604
|
-
ual: msg.ual,
|
|
605
|
-
rootEntities: msg.rootEntities,
|
|
606
|
-
publisherAddress: msg.publisherAddress,
|
|
607
|
-
txHash: msg.txHash,
|
|
608
|
-
blockNumber,
|
|
609
|
-
startKAId,
|
|
610
|
-
endKAId,
|
|
611
|
-
batchId: protobufScalarToBigInt(msg.batchId),
|
|
612
|
-
ctxGraphId,
|
|
613
|
-
subGraphName,
|
|
614
|
-
authorAddress,
|
|
615
|
-
finalizationVersion,
|
|
616
|
-
targetMetaGraph,
|
|
617
|
-
defaultMeta,
|
|
618
|
-
isDualWrite,
|
|
619
|
-
ctx,
|
|
620
|
-
});
|
|
621
|
-
if (outcome === 'stale-target') {
|
|
622
|
-
this.markProcessed(dedupeKey);
|
|
623
|
-
this.lifecycle.record(ctx, finalizationLifecycleDecision('finalization_stale_target', {
|
|
624
|
-
...msg,
|
|
625
|
-
targetContextGraphId: ctxGraphId ?? msg.targetContextGraphId,
|
|
626
|
-
rootEntityCount: msg.rootEntities.length,
|
|
627
|
-
swmStatementCount: merkleMatchedQuads.length,
|
|
628
|
-
subGraphName,
|
|
629
|
-
blockNumber,
|
|
630
|
-
batchId,
|
|
631
|
-
outcome,
|
|
632
|
-
reason: 'newer update already materialized',
|
|
633
|
-
}));
|
|
634
|
-
this.log.info(ctx, `Finalization: a newer update is already materialised for ${msg.ual}, skipping stale publish promotion`);
|
|
635
|
-
return 'already-confirmed';
|
|
636
|
-
}
|
|
637
|
-
this.markProcessed(dedupeKey);
|
|
638
|
-
this.lifecycle.record(ctx, finalizationLifecycleDecision('finalization_applied', {
|
|
639
|
-
...msg,
|
|
640
|
-
targetContextGraphId: ctxGraphId ?? msg.targetContextGraphId,
|
|
641
|
-
rootEntityCount: msg.rootEntities.length,
|
|
642
|
-
swmStatementCount: merkleMatchedQuads.length,
|
|
643
|
-
subGraphName,
|
|
644
|
-
blockNumber,
|
|
645
|
-
batchId,
|
|
646
|
-
outcome,
|
|
647
|
-
retryable: false,
|
|
648
|
-
}));
|
|
649
|
-
this.log.info(ctx, `Finalization: promoted SWM snapshot to ${ctxGraphId ? `context graph ${ctxGraphId}` : 'canonical'} for ${msg.ual} (tx=${msg.txHash.slice(0, 10)}…)`);
|
|
650
|
-
return 'applied';
|
|
651
|
-
}
|
|
652
|
-
this.lifecycle.record(ctx, finalizationLifecycleDecision('finalization_verification_failed', {
|
|
653
|
-
...msg,
|
|
654
|
-
targetContextGraphId: ctxGraphId ?? msg.targetContextGraphId,
|
|
655
|
-
rootEntityCount: msg.rootEntities.length,
|
|
656
|
-
swmStatementCount: merkleMatchedQuads.length,
|
|
657
|
-
subGraphName,
|
|
658
|
-
blockNumber,
|
|
659
|
-
batchId,
|
|
660
|
-
outcome: 'deferred',
|
|
661
|
-
retryable: true,
|
|
662
|
-
reason: 'on-chain verification failed',
|
|
663
|
-
level: 'warn',
|
|
664
|
-
}));
|
|
665
|
-
this.log.info(ctx, `Finalization: on-chain verification failed for ${msg.ual}, will retry via ChainEventPoller`);
|
|
666
|
-
return 'deferred';
|
|
667
|
-
}
|
|
668
|
-
this.lifecycle.record(ctx, finalizationLifecycleDecision('finalization_merkle_mismatch', {
|
|
669
|
-
...msg,
|
|
670
|
-
targetContextGraphId: ctxGraphId ?? msg.targetContextGraphId,
|
|
671
|
-
rootEntityCount: msg.rootEntities.length,
|
|
672
|
-
swmStatementCount: sharedMemoryQuads.length,
|
|
673
|
-
subGraphName,
|
|
674
|
-
blockNumber,
|
|
675
|
-
batchId: protobufScalarToBigInt(msg.batchId),
|
|
676
|
-
outcome: 'deferred',
|
|
677
|
-
retryable: true,
|
|
678
|
-
reason: 'shared memory merkle root mismatch',
|
|
679
|
-
level: 'warn',
|
|
680
|
-
}));
|
|
681
|
-
this.log.info(ctx, `Finalization: merkle mismatch for ${msg.ual}, shared memory data differs from published`);
|
|
682
|
-
}
|
|
683
|
-
else {
|
|
684
|
-
this.lifecycle.record(ctx, finalizationLifecycleDecision('finalization_no_data', {
|
|
685
|
-
...msg,
|
|
686
|
-
targetContextGraphId: ctxGraphId ?? msg.targetContextGraphId,
|
|
687
|
-
rootEntityCount: msg.rootEntities.length,
|
|
688
|
-
swmStatementCount: 0,
|
|
689
|
-
subGraphName,
|
|
690
|
-
blockNumber,
|
|
691
|
-
batchId: protobufScalarToBigInt(msg.batchId),
|
|
692
|
-
outcome: 'deferred',
|
|
693
|
-
retryable: true,
|
|
694
|
-
reason: 'no shared memory data',
|
|
695
|
-
level: 'warn',
|
|
696
|
-
}));
|
|
697
|
-
this.log.info(ctx, `Finalization: no shared memory data for ${msg.ual}, peer missed SWM sharing`);
|
|
698
|
-
}
|
|
699
|
-
// Fallback: no matching shared memory data. The data will arrive via
|
|
700
|
-
// the regular publish topic broadcast or ChainEventPoller sync.
|
|
701
|
-
this.lifecycle.record(ctx, finalizationLifecycleDecision('finalization_payload_sync_required', {
|
|
702
|
-
...msg,
|
|
703
|
-
targetContextGraphId: ctxGraphId ?? msg.targetContextGraphId,
|
|
704
|
-
rootEntityCount: msg.rootEntities.length,
|
|
705
|
-
swmStatementCount: sharedMemoryQuads.length,
|
|
706
|
-
subGraphName,
|
|
707
|
-
blockNumber,
|
|
708
|
-
batchId: protobufScalarToBigInt(msg.batchId),
|
|
709
|
-
outcome: 'deferred',
|
|
710
|
-
retryable: true,
|
|
711
|
-
reason: 'no matching SWM snapshot',
|
|
712
|
-
level: 'warn',
|
|
713
|
-
}));
|
|
714
|
-
this.log.info(ctx, `Finalization: ${msg.ual} requires full payload sync (no matching SWM snapshot)`);
|
|
715
|
-
return 'deferred';
|
|
336
|
+
return await this.recovery.processUnjournaled(input);
|
|
716
337
|
}
|
|
717
338
|
catch (err) {
|
|
718
339
|
if (err instanceof StoreSchedulerBusyError)
|
|
@@ -720,8 +341,7 @@ export class FinalizationHandler {
|
|
|
720
341
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
721
342
|
this.lifecycle.record(ctx, finalizationLifecycleDecision('finalization_failed', {
|
|
722
343
|
...msg,
|
|
723
|
-
contextGraphId: msg.contextGraphId || contextGraphId,
|
|
724
|
-
targetContextGraphId: resolvedTargetContextGraphId ?? msg.targetContextGraphId,
|
|
344
|
+
contextGraphId: msg.contextGraphId || input.contextGraphId,
|
|
725
345
|
rootEntityCount: msg.rootEntities.length,
|
|
726
346
|
outcome: 'failed',
|
|
727
347
|
retryable: true,
|
|
@@ -732,6 +352,36 @@ export class FinalizationHandler {
|
|
|
732
352
|
return 'deferred';
|
|
733
353
|
}
|
|
734
354
|
}
|
|
355
|
+
/**
|
|
356
|
+
* Only graph-scoped finalization is materialized. A message without that
|
|
357
|
+
* scope names root entities instead of the KA's own graph, so this node
|
|
358
|
+
* holds no exact copy to verify it against. Its KA reaches Verified Memory
|
|
359
|
+
* through chain reconcile, which fetches it from peers.
|
|
360
|
+
*/
|
|
361
|
+
ignoreUnscopedFinalization(data, contextGraphId) {
|
|
362
|
+
const msg = this.decodeFinalizationMessageOrWarn(data);
|
|
363
|
+
// Frames of other gossip message types decode "successfully" with garbage
|
|
364
|
+
// fields (#1100), so only report what plausibly is a finalization.
|
|
365
|
+
if (!msg
|
|
366
|
+
|| !msg.ual
|
|
367
|
+
|| !msg.txHash
|
|
368
|
+
|| (msg.contextGraphId && !validateContextGraphId(msg.contextGraphId).valid)) {
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
const ctx = msg.operationId
|
|
372
|
+
? createOperationContext('gossip', msg.operationId)
|
|
373
|
+
: createOperationContext('gossip');
|
|
374
|
+
const reason = `content scope ${msg.contentScopeVersion ?? 0} is not graph-scoped`;
|
|
375
|
+
this.lifecycle.record(ctx, finalizationLifecycleDecision('finalization_unsupported_scope', {
|
|
376
|
+
...msg,
|
|
377
|
+
contextGraphId: msg.contextGraphId || contextGraphId,
|
|
378
|
+
rootEntityCount: msg.rootEntities.length,
|
|
379
|
+
outcome: 'ignored',
|
|
380
|
+
retryable: false,
|
|
381
|
+
reason,
|
|
382
|
+
}));
|
|
383
|
+
this.log.info(ctx, `Finalization: ignoring ${msg.ual}: ${reason}`);
|
|
384
|
+
}
|
|
735
385
|
/** Resolve and verify the exact RDF state that a graph-scoped command may apply. */
|
|
736
386
|
async prepareGraphScopedMaterialization(input) {
|
|
737
387
|
const { candidate: parsed, contextGraphId, sourcePeerId } = input;
|
|
@@ -1017,8 +667,14 @@ export class FinalizationHandler {
|
|
|
1017
667
|
if (resolution.status === 'absent')
|
|
1018
668
|
return undefined;
|
|
1019
669
|
if (resolution.status === 'invalid') {
|
|
1020
|
-
|
|
1021
|
-
|
|
670
|
+
if (resolution.reason === 'not-current') {
|
|
671
|
+
// An intact copy of an earlier version: the caller fetches the current one.
|
|
672
|
+
this.log.info(ctx, `Chain-reconcile: confirmed graph-scoped VM for ${input.ual} is not the current version`);
|
|
673
|
+
}
|
|
674
|
+
else {
|
|
675
|
+
this.log.warn(ctx, `Chain-reconcile: confirmed graph-scoped VM is invalid for ${input.ual} `
|
|
676
|
+
+ `(${resolution.reason})`);
|
|
677
|
+
}
|
|
1022
678
|
return 'no-swm';
|
|
1023
679
|
}
|
|
1024
680
|
await this.advanceExactGraphScopedVersion({
|
|
@@ -1124,14 +780,13 @@ export class FinalizationHandler {
|
|
|
1124
780
|
}
|
|
1125
781
|
/**
|
|
1126
782
|
* Resolve and promote the exact graph-scoped SWM assertion for a chain-known
|
|
1127
|
-
* KA. `undefined` means no V2 head exists and the caller
|
|
1128
|
-
*
|
|
783
|
+
* KA. `undefined` means no V2 head exists and the caller answers `no-swm`;
|
|
784
|
+
* every other result is authoritative for V2.
|
|
1129
785
|
*/
|
|
1130
786
|
async reconcileGraphScopedKC(input, ctx) {
|
|
1131
787
|
const { contextGraphId, onChainCgId, ual, assertionVersion, merkleRoot, publisherAddress, kaId, batchId, versionBlock, authorAddress, versionSnapshot, signal, subGraphName, trustedAssertionEvidence, } = input;
|
|
1132
|
-
// Historical UAL shapes
|
|
1133
|
-
//
|
|
1134
|
-
// V2 parser turn those into a terminal "corrupt head" result.
|
|
788
|
+
// Historical UAL shapes cannot name a V2 per-KA graph. Do not let the
|
|
789
|
+
// strict V2 parser turn those into a terminal "corrupt head" result.
|
|
1135
790
|
try {
|
|
1136
791
|
createGraphKnowledgeAssetScope(ual, 1);
|
|
1137
792
|
}
|
|
@@ -1160,8 +815,7 @@ export class FinalizationHandler {
|
|
|
1160
815
|
// post-fetch check runs. Re-read the immutable confirmed envelope and
|
|
1161
816
|
// verify the exact VM graph against current chain truth. This remains
|
|
1162
817
|
// fail-closed: absent, invalid, stale, or root-mismatched VM state is
|
|
1163
|
-
// still reported as no-swm
|
|
1164
|
-
// disabled for exact fetch callers.
|
|
818
|
+
// still reported as no-swm.
|
|
1165
819
|
return (await this.reconcileConfirmedGraphScopedVmWithoutWorkspaceHead({
|
|
1166
820
|
contextGraphId,
|
|
1167
821
|
ual,
|
|
@@ -2044,353 +1698,6 @@ export class FinalizationHandler {
|
|
|
2044
1698
|
return false;
|
|
2045
1699
|
}
|
|
2046
1700
|
}
|
|
2047
|
-
finalizationSwmBucketUri(contextGraphId, subGraphName) {
|
|
2048
|
-
return subGraphName
|
|
2049
|
-
? new GraphManager(this.store).sharedMemoryUri(contextGraphId, subGraphName)
|
|
2050
|
-
: contextGraphWorkspaceGraphUri(contextGraphId);
|
|
2051
|
-
}
|
|
2052
|
-
/**
|
|
2053
|
-
* Read the finalization SWM slice, preferring this KA's per-author under-graphs
|
|
2054
|
-
* and widening to the complete read on empty-or-mismatch (#1549). The widen — and
|
|
2055
|
-
* the reasoning for why the bound is a safe pure accelerator (INV-1 is refuted
|
|
2056
|
-
* under root recurrence, so the bounded read may miss and must widen before the
|
|
2057
|
-
* caller records anything) — lives in storage's
|
|
2058
|
-
* `loadSharedMemorySliceWithKaBoundFallback`. This method only resolves the bucket
|
|
2059
|
-
* URI and the accept predicate; it is a thin finalization-specific adapter.
|
|
2060
|
-
*
|
|
2061
|
-
* #1098/#1099: replicas store gossiped SWM shares in the PER-KA graphs
|
|
2062
|
-
* `…/_shared_memory/{author}/{number}`, not the bare bucket, so the read must span
|
|
2063
|
-
* bucket + per-KA graphs or a replica reports "no shared memory data" and never
|
|
2064
|
-
* materialises the KA into VM.
|
|
2065
|
-
*/
|
|
2066
|
-
async loadFinalizationSwmSlice(contextGraphId, rootEntities, subGraphName, kaGraphBound, expectedMerkleRoot, createAccept) {
|
|
2067
|
-
const safeRoots = rootEntities.filter(isSafeIri);
|
|
2068
|
-
if (safeRoots.length === 0)
|
|
2069
|
-
return { quads: [], matched: null };
|
|
2070
|
-
const writeRevision = this.graphWriteGen?.getWriteRevision(`${contextGraphDataUri(contextGraphId)}/`);
|
|
2071
|
-
const key = [
|
|
2072
|
-
'finalization',
|
|
2073
|
-
contextGraphId,
|
|
2074
|
-
subGraphName ?? '',
|
|
2075
|
-
safeRoots.slice().sort().join('\u0001'),
|
|
2076
|
-
kaGraphBound ? `${kaGraphBound.agentAddress}:${kaGraphBound.startNumber}:${kaGraphBound.endNumber}` : '*',
|
|
2077
|
-
ethers.hexlify(expectedMerkleRoot),
|
|
2078
|
-
writeRevision
|
|
2079
|
-
? `${writeRevision.generation}:${writeRevision.stable ? 'stable' : 'unstable'}`
|
|
2080
|
-
: 'unknown',
|
|
2081
|
-
].join('\u0000');
|
|
2082
|
-
return this.runScanSingleFlight(key, async () => {
|
|
2083
|
-
const { quads, accepted } = await loadSharedMemorySliceWithKaBoundFallback(this.store, this.finalizationSwmBucketUri(contextGraphId, subGraphName), { rootEntities: safeRoots }, kaGraphBound, {
|
|
2084
|
-
sources: {
|
|
2085
|
-
bounded: SWM_SLICE_SOURCE_BOUNDED,
|
|
2086
|
-
widened: SWM_SLICE_SOURCE_WIDENED,
|
|
2087
|
-
unbounded: SWM_SLICE_SOURCE,
|
|
2088
|
-
},
|
|
2089
|
-
createAccept,
|
|
2090
|
-
queryOptions: { priority: 'background' },
|
|
2091
|
-
resultBudget: finalizationSwmResultBudget(),
|
|
2092
|
-
});
|
|
2093
|
-
return { quads, matched: accepted };
|
|
2094
|
-
});
|
|
2095
|
-
}
|
|
2096
|
-
/** Complete (unbounded) SWM read for the chain-reconcile backstop. */
|
|
2097
|
-
async getSharedMemoryQuadsForRoots(contextGraphId, rootEntities, subGraphName) {
|
|
2098
|
-
const safeRoots = rootEntities.filter(isSafeIri);
|
|
2099
|
-
if (safeRoots.length === 0)
|
|
2100
|
-
return [];
|
|
2101
|
-
return loadSelectedSharedMemoryQuads(this.store, this.finalizationSwmBucketUri(contextGraphId, subGraphName), { rootEntities: safeRoots }, {
|
|
2102
|
-
querySource: SWM_SLICE_SOURCE,
|
|
2103
|
-
queryOptions: { priority: 'background' },
|
|
2104
|
-
resultBudget: finalizationSwmResultBudget(),
|
|
2105
|
-
});
|
|
2106
|
-
}
|
|
2107
|
-
runScanSingleFlight(key, work) {
|
|
2108
|
-
const scope = key.startsWith('finalization\u0000') ? 'finalization' : 'reconcile';
|
|
2109
|
-
const existing = this.scanSingleFlights.get(key);
|
|
2110
|
-
if (existing) {
|
|
2111
|
-
getMetrics().storeScanSingleFlightJoinsTotal.add(1, { scope });
|
|
2112
|
-
return existing;
|
|
2113
|
-
}
|
|
2114
|
-
getMetrics().storeScanSingleFlightActive.add(1, { scope });
|
|
2115
|
-
const running = work().finally(() => {
|
|
2116
|
-
if (this.scanSingleFlights.get(key) === running)
|
|
2117
|
-
this.scanSingleFlights.delete(key);
|
|
2118
|
-
getMetrics().storeScanSingleFlightActive.add(-1, { scope });
|
|
2119
|
-
});
|
|
2120
|
-
this.scanSingleFlights.set(key, running);
|
|
2121
|
-
return running;
|
|
2122
|
-
}
|
|
2123
|
-
verifyMerkleMatch(sharedMemoryQuads, privateRoots, expectedMerkleRoot) {
|
|
2124
|
-
const computedRoot = computeFlatKCRoot(sharedMemoryQuads, privateRoots);
|
|
2125
|
-
return ethers.hexlify(computedRoot) === ethers.hexlify(expectedMerkleRoot);
|
|
2126
|
-
}
|
|
2127
|
-
sharedMemoryQuadsMatchingMerkle(contextGraphId, sharedMemoryQuads, privateRoots, expectedMerkleRoot, allowGeneratedCatalogFloor) {
|
|
2128
|
-
if (this.verifyMerkleMatch(sharedMemoryQuads, privateRoots, expectedMerkleRoot)) {
|
|
2129
|
-
return sharedMemoryQuads;
|
|
2130
|
-
}
|
|
2131
|
-
if (!allowGeneratedCatalogFloor)
|
|
2132
|
-
return null;
|
|
2133
|
-
const withGeneratedCatalog = [
|
|
2134
|
-
...sharedMemoryQuads,
|
|
2135
|
-
...generatedPrivateCatalogFloorQuads(contextGraphId),
|
|
2136
|
-
];
|
|
2137
|
-
if (this.verifyMerkleMatch(withGeneratedCatalog, privateRoots, expectedMerkleRoot)) {
|
|
2138
|
-
return withGeneratedCatalog;
|
|
2139
|
-
}
|
|
2140
|
-
return null;
|
|
2141
|
-
}
|
|
2142
|
-
async storedOnChainContextGraphId(contextGraphId) {
|
|
2143
|
-
const result = await this.store.query(contextGraphOnChainIdBindingQuery(contextGraphId), { source: 'agent.finalization.contextGraphOnChainId' });
|
|
2144
|
-
if (result.type !== 'bindings' || result.bindings.length === 0)
|
|
2145
|
-
return undefined;
|
|
2146
|
-
return stripOptionalLiteral(result.bindings[0]?.['id'])?.trim();
|
|
2147
|
-
}
|
|
2148
|
-
async onChainContextGraphMatchesLocalId(contextGraphId, onChainCgId) {
|
|
2149
|
-
if (onChainCgId === undefined || onChainCgId === null)
|
|
2150
|
-
return false;
|
|
2151
|
-
const normalizedOnChainId = String(onChainCgId).trim();
|
|
2152
|
-
const normalizedContextGraphId = contextGraphId.trim();
|
|
2153
|
-
if (/^\d+$/.test(normalizedContextGraphId) && normalizedContextGraphId === normalizedOnChainId)
|
|
2154
|
-
return true;
|
|
2155
|
-
const liveNameHashMatches = async () => {
|
|
2156
|
-
if (typeof this.chain?.getContextGraphNameHash !== 'function')
|
|
2157
|
-
return false;
|
|
2158
|
-
try {
|
|
2159
|
-
const nameHash = await this.chain.getContextGraphNameHash(BigInt(normalizedOnChainId));
|
|
2160
|
-
return typeof nameHash === 'string' &&
|
|
2161
|
-
nameHash.toLowerCase() === ethers.keccak256(ethers.toUtf8Bytes(normalizedContextGraphId)).toLowerCase();
|
|
2162
|
-
}
|
|
2163
|
-
catch {
|
|
2164
|
-
return false;
|
|
2165
|
-
}
|
|
2166
|
-
};
|
|
2167
|
-
let resolvedOnChainId;
|
|
2168
|
-
try {
|
|
2169
|
-
resolvedOnChainId = await this.resolveContextGraphOnChainId?.(contextGraphId);
|
|
2170
|
-
}
|
|
2171
|
-
catch {
|
|
2172
|
-
resolvedOnChainId = undefined;
|
|
2173
|
-
}
|
|
2174
|
-
if (sameBigIntLiteral(resolvedOnChainId, normalizedOnChainId)) {
|
|
2175
|
-
return typeof this.chain?.getContextGraphNameHash === 'function'
|
|
2176
|
-
? liveNameHashMatches()
|
|
2177
|
-
: true;
|
|
2178
|
-
}
|
|
2179
|
-
const storedOnChainId = await this.storedOnChainContextGraphId(contextGraphId);
|
|
2180
|
-
if (sameBigIntLiteral(storedOnChainId, normalizedOnChainId)) {
|
|
2181
|
-
return typeof this.chain?.getContextGraphNameHash === 'function'
|
|
2182
|
-
? liveNameHashMatches()
|
|
2183
|
-
: true;
|
|
2184
|
-
}
|
|
2185
|
-
return liveNameHashMatches();
|
|
2186
|
-
}
|
|
2187
|
-
async allowsGeneratedCatalogFloor(contextGraphId, onChainCgId) {
|
|
2188
|
-
if (onChainCgId === undefined || onChainCgId === null)
|
|
2189
|
-
return false;
|
|
2190
|
-
if (!this.chain || this.chain.chainId === 'none')
|
|
2191
|
-
return false;
|
|
2192
|
-
const normalizedContextGraphId = contextGraphId.trim();
|
|
2193
|
-
const normalizedOnChainId = String(onChainCgId).trim();
|
|
2194
|
-
const readFinalizedCreation = this.chain.getContextGraphFinalizedCreation;
|
|
2195
|
-
if (typeof readFinalizedCreation === 'function') {
|
|
2196
|
-
try {
|
|
2197
|
-
const creation = await readFinalizedCreation.call(this.chain, BigInt(normalizedOnChainId));
|
|
2198
|
-
if (creation !== undefined) {
|
|
2199
|
-
const idMatches = /^\d+$/.test(normalizedContextGraphId)
|
|
2200
|
-
? normalizedContextGraphId === normalizedOnChainId
|
|
2201
|
-
: creation.nameHash.toLowerCase() === ethers.keccak256(ethers.toUtf8Bytes(normalizedContextGraphId)).toLowerCase();
|
|
2202
|
-
return idMatches && creation.accessPolicy === 1;
|
|
2203
|
-
}
|
|
2204
|
-
}
|
|
2205
|
-
catch {
|
|
2206
|
-
// Never mix a partial/failing finalized creation proof with a latest
|
|
2207
|
-
// point read: that can splice two forks into a false-private result.
|
|
2208
|
-
return false;
|
|
2209
|
-
}
|
|
2210
|
-
}
|
|
2211
|
-
if (typeof this.chain.getContextGraphAccessPolicy !== 'function')
|
|
2212
|
-
return false;
|
|
2213
|
-
if (!await this.onChainContextGraphMatchesLocalId(contextGraphId, onChainCgId))
|
|
2214
|
-
return false;
|
|
2215
|
-
try {
|
|
2216
|
-
return Number(await this.chain.getContextGraphAccessPolicy(BigInt(onChainCgId))) === 1;
|
|
2217
|
-
}
|
|
2218
|
-
catch {
|
|
2219
|
-
return false;
|
|
2220
|
-
}
|
|
2221
|
-
}
|
|
2222
|
-
async getPrivateRootsFromMeta(contextGraphId, rootEntities, subGraphName) {
|
|
2223
|
-
const graphManager = new GraphManager(this.store);
|
|
2224
|
-
const wsMetaGraph = subGraphName
|
|
2225
|
-
? graphManager.sharedMemoryMetaUri(contextGraphId, subGraphName)
|
|
2226
|
-
: contextGraphWorkspaceMetaGraphUri(contextGraphId);
|
|
2227
|
-
const safeRoots = rootEntities.filter(isSafeIri);
|
|
2228
|
-
if (safeRoots.length === 0)
|
|
2229
|
-
return [];
|
|
2230
|
-
const values = safeRoots.map(r => `<${r}>`).join(' ');
|
|
2231
|
-
const sparql = `SELECT ?entity ?root WHERE {
|
|
2232
|
-
GRAPH <${wsMetaGraph}> {
|
|
2233
|
-
VALUES ?entity { ${values} }
|
|
2234
|
-
?entity <${DKG_NS}privateMerkleRoot> ?root .
|
|
2235
|
-
}
|
|
2236
|
-
}`;
|
|
2237
|
-
const roots = [];
|
|
2238
|
-
try {
|
|
2239
|
-
const result = await this.store.query(sparql, { source: 'agent.finalization.privateRoots' });
|
|
2240
|
-
if (result.type === 'bindings') {
|
|
2241
|
-
for (const row of result.bindings) {
|
|
2242
|
-
const hex = row['root'].replace(/^"(.*)".*$/, '$1').replace(/^0x/, '');
|
|
2243
|
-
if (hex.length === 64) {
|
|
2244
|
-
const bytes = new Uint8Array(32);
|
|
2245
|
-
for (let i = 0; i < 32; i++)
|
|
2246
|
-
bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
2247
|
-
roots.push(bytes);
|
|
2248
|
-
}
|
|
2249
|
-
}
|
|
2250
|
-
}
|
|
2251
|
-
}
|
|
2252
|
-
catch { /* metadata may not exist */ }
|
|
2253
|
-
return roots;
|
|
2254
|
-
}
|
|
2255
|
-
/**
|
|
2256
|
-
* Recover the publisher's `keepRootCopyOnLabel` decision for these roots from
|
|
2257
|
-
* SWM workspace meta. The publisher persists `<root> dkg:keepRootCopyOnLabel
|
|
2258
|
-
* "true"|"false"` at publish time — the durable equivalent of the gossip
|
|
2259
|
-
* envelope flag — and it replicates to subscribers alongside the per-root
|
|
2260
|
-
* `privateMerkleRoot`. Returns:
|
|
2261
|
-
* - `true` — a matched root explicitly kept the root-label copy,
|
|
2262
|
-
* - `false` — explicitly dropped (remap / explicit-subCG publish),
|
|
2263
|
-
* - `undefined` — no signal persisted (legacy publish); the caller defaults
|
|
2264
|
-
* to per-cgId-only so a dropped root copy is never re-added.
|
|
2265
|
-
* An explicit `true` wins over `false` across the matched roots: a same-graph
|
|
2266
|
-
* publish demands the root copy exist.
|
|
2267
|
-
*/
|
|
2268
|
-
async getKeepRootCopySignal(contextGraphId, rootEntities, subGraphName) {
|
|
2269
|
-
const graphManager = new GraphManager(this.store);
|
|
2270
|
-
const wsMetaGraph = subGraphName
|
|
2271
|
-
? graphManager.sharedMemoryMetaUri(contextGraphId, subGraphName)
|
|
2272
|
-
: contextGraphWorkspaceMetaGraphUri(contextGraphId);
|
|
2273
|
-
const safeRoots = rootEntities.filter(isSafeIri);
|
|
2274
|
-
if (safeRoots.length === 0)
|
|
2275
|
-
return undefined;
|
|
2276
|
-
const values = safeRoots.map(r => `<${r}>`).join(' ');
|
|
2277
|
-
const sparql = `SELECT ?v WHERE {
|
|
2278
|
-
GRAPH <${assertSafeIri(wsMetaGraph)}> {
|
|
2279
|
-
VALUES ?root { ${values} }
|
|
2280
|
-
?root <${KEEP_ROOT_COPY_PREDICATE}> ?v .
|
|
2281
|
-
}
|
|
2282
|
-
}`;
|
|
2283
|
-
try {
|
|
2284
|
-
const result = await this.store.query(sparql, { source: 'agent.finalization.keepRootCopySignal' });
|
|
2285
|
-
if (result.type !== 'bindings' || result.bindings.length === 0)
|
|
2286
|
-
return undefined;
|
|
2287
|
-
let sawFalse = false;
|
|
2288
|
-
for (const row of result.bindings) {
|
|
2289
|
-
const v = String(row['v']).replace(/^"(.*)".*$/, '$1');
|
|
2290
|
-
if (v === 'true')
|
|
2291
|
-
return true;
|
|
2292
|
-
if (v === 'false')
|
|
2293
|
-
sawFalse = true;
|
|
2294
|
-
}
|
|
2295
|
-
return sawFalse ? false : undefined;
|
|
2296
|
-
}
|
|
2297
|
-
catch {
|
|
2298
|
-
return undefined;
|
|
2299
|
-
}
|
|
2300
|
-
}
|
|
2301
|
-
async getPublisherPeerIdFromMeta(contextGraphId, rootEntities, subGraphName) {
|
|
2302
|
-
const graphManager = new GraphManager(this.store);
|
|
2303
|
-
const wsMetaGraph = subGraphName
|
|
2304
|
-
? graphManager.sharedMemoryMetaUri(contextGraphId, subGraphName)
|
|
2305
|
-
: contextGraphWorkspaceMetaGraphUri(contextGraphId);
|
|
2306
|
-
const safeRoots = rootEntities.filter(isSafeIri);
|
|
2307
|
-
if (safeRoots.length === 0)
|
|
2308
|
-
return undefined;
|
|
2309
|
-
const values = safeRoots.map(r => `<${r}>`).join(' ');
|
|
2310
|
-
const PROV = 'http://www.w3.org/ns/prov#';
|
|
2311
|
-
// GH #748: prefer the dedicated `dkg:publisherPeerId` field (peer-ID
|
|
2312
|
-
// literal); fall back to literal-form `prov:wasAttributedTo` for legacy
|
|
2313
|
-
// un-migrated SWM rows. Skip URI form — that's an agent DID, not a
|
|
2314
|
-
// peer ID, and downstream dials a libp2p peer with this value.
|
|
2315
|
-
//
|
|
2316
|
-
// `FILTER(BOUND(?peerId))` guards the LIMIT 1: without it, an op with
|
|
2317
|
-
// `rootEntity` but neither peer-ID source produces an unbound `?peerId`
|
|
2318
|
-
// that LIMIT could pick. The caller's `wsPeerId || publisherAddress`
|
|
2319
|
-
// fallback would then store an EVM address where a libp2p peer ID is
|
|
2320
|
-
// expected. With BOUND, only ops carrying a real peer-ID match.
|
|
2321
|
-
const sparql = `SELECT ?peerId WHERE {
|
|
2322
|
-
GRAPH <${wsMetaGraph}> {
|
|
2323
|
-
VALUES ?root { ${values} }
|
|
2324
|
-
?op <${DKG_NS}rootEntity> ?root .
|
|
2325
|
-
OPTIONAL { ?op <${DKG_NS}publisherPeerId> ?pidField }
|
|
2326
|
-
OPTIONAL { ?op <${PROV}wasAttributedTo> ?attrField . FILTER(isLiteral(?attrField)) }
|
|
2327
|
-
BIND(COALESCE(?pidField, ?attrField) AS ?peerId)
|
|
2328
|
-
FILTER(BOUND(?peerId))
|
|
2329
|
-
}
|
|
2330
|
-
} LIMIT 1`;
|
|
2331
|
-
try {
|
|
2332
|
-
const result = await this.store.query(sparql, { source: 'agent.finalization.publisherPeerId' });
|
|
2333
|
-
if (result.type === 'bindings' && result.bindings.length > 0) {
|
|
2334
|
-
const raw = result.bindings[0]['peerId'];
|
|
2335
|
-
const peerId = raw.replace(/^"(.*)".*$/, '$1');
|
|
2336
|
-
if (peerId && peerId !== 'unknown')
|
|
2337
|
-
return peerId;
|
|
2338
|
-
}
|
|
2339
|
-
}
|
|
2340
|
-
catch { /* shared memory metadata may not exist */ }
|
|
2341
|
-
return undefined;
|
|
2342
|
-
}
|
|
2343
|
-
/**
|
|
2344
|
-
* Shared SWM→VM promotion under the per-KA materialization lock(s).
|
|
2345
|
-
*
|
|
2346
|
-
* Extracted verbatim from the former `promoteUnderLocks`/`runUnderLocks`
|
|
2347
|
-
* inline closure in `handleFinalizationMessage` (PR #845's TOCTOU-safe
|
|
2348
|
-
* promotion) so BOTH the gossip path and the chain-driven reconciliation
|
|
2349
|
-
* path (`handleChainReconciledKC`) share one implementation. Behavior is
|
|
2350
|
-
* identical to the gossip path; the only change is that the captured locals
|
|
2351
|
-
* are now explicit params.
|
|
2352
|
-
*
|
|
2353
|
-
* Serialises check + promotion + version stamp under the per-KA lock so a
|
|
2354
|
-
* concurrent stale writer cannot interleave between `shouldApplyMaterialization`
|
|
2355
|
-
* and `writeMaterializedVersion`. When dual-writing (same-graph keep-root),
|
|
2356
|
-
* both the per-cgId target meta and the root-label meta are guarded; if the
|
|
2357
|
-
* root label has a newer projection (an applied update), the dual-write
|
|
2358
|
-
* portion is skipped so we don't clobber it.
|
|
2359
|
-
*/
|
|
2360
|
-
async applyVerifiedFinalization(input) {
|
|
2361
|
-
const { contextGraphId, sharedMemoryQuads, ual, rootEntities, publisherAddress, txHash, blockNumber, startKAId, endKAId, batchId, ctxGraphId, subGraphName, authorAddress, finalizationVersion, targetMetaGraph, defaultMeta, isDualWrite, ctx, } = input;
|
|
2362
|
-
const promoteUnderLocks = async () => {
|
|
2363
|
-
if (!(await shouldApplyMaterialization(this.store, targetMetaGraph, ual, finalizationVersion))) {
|
|
2364
|
-
return 'stale-target';
|
|
2365
|
-
}
|
|
2366
|
-
let effectiveKeepRoot = isDualWrite;
|
|
2367
|
-
if (isDualWrite) {
|
|
2368
|
-
if (!(await shouldApplyMaterialization(this.store, defaultMeta, ual, finalizationVersion))) {
|
|
2369
|
-
// Per-cgId is stale-or-equal but ROOT label has a
|
|
2370
|
-
// newer projection (an applied update). Skip the
|
|
2371
|
-
// dual-write portion so we don't clobber it.
|
|
2372
|
-
effectiveKeepRoot = false;
|
|
2373
|
-
this.log.info(ctx, `Finalization: root-label projection is newer for ${ual}; downgrading to per-cgId-only promotion`);
|
|
2374
|
-
}
|
|
2375
|
-
}
|
|
2376
|
-
await this.promoteSharedMemoryToCanonical(contextGraphId, sharedMemoryQuads, ual, rootEntities, publisherAddress, txHash, blockNumber, startKAId, endKAId, batchId, ctx, ctxGraphId, subGraphName, authorAddress, effectiveKeepRoot);
|
|
2377
|
-
await writeMaterializedVersion(this.store, targetMetaGraph, ual, finalizationVersion);
|
|
2378
|
-
if (effectiveKeepRoot) {
|
|
2379
|
-
await writeMaterializedVersion(this.store, defaultMeta, ual, finalizationVersion);
|
|
2380
|
-
}
|
|
2381
|
-
return 'promoted';
|
|
2382
|
-
};
|
|
2383
|
-
// Nested per-graph locks. Acquired in sorted order to prevent
|
|
2384
|
-
// cross-deadlock with any other call site that might one day
|
|
2385
|
-
// also lock multiple metas.
|
|
2386
|
-
const lockOrder = isDualWrite
|
|
2387
|
-
? [defaultMeta, targetMetaGraph].sort()
|
|
2388
|
-
: [targetMetaGraph];
|
|
2389
|
-
if (lockOrder.length === 1) {
|
|
2390
|
-
return withMaterializationLock(lockOrder[0], ual, promoteUnderLocks);
|
|
2391
|
-
}
|
|
2392
|
-
return withMaterializationLock(lockOrder[0], ual, () => withMaterializationLock(lockOrder[1], ual, promoteUnderLocks));
|
|
2393
|
-
}
|
|
2394
1701
|
/**
|
|
2395
1702
|
* Resolve the exact graph namespace from canonical graph-scoped metadata.
|
|
2396
1703
|
* A named assertion stores its namespace in the root metadata graph, so this
|
|
@@ -2448,30 +1755,27 @@ export class FinalizationHandler {
|
|
|
2448
1755
|
return null;
|
|
2449
1756
|
}
|
|
2450
1757
|
}
|
|
2451
|
-
/**
|
|
2452
|
-
* Constant-time exact graph-scoped reconciliation. This method never enters
|
|
2453
|
-
* the legacy workspace-operation scan.
|
|
2454
|
-
*/
|
|
1758
|
+
/** Constant-time exact graph-scoped reconciliation. */
|
|
2455
1759
|
async reconcileExactChainRegisteredKC(rawInput, ctx) {
|
|
2456
1760
|
const resolvedInput = await this.resolveExactChainReconcileInput(rawInput, ctx);
|
|
2457
1761
|
if (!resolvedInput) {
|
|
2458
|
-
return { outcome: 'no-swm',
|
|
1762
|
+
return { outcome: 'no-swm', legacyMarkerEligible: false, input: rawInput };
|
|
2459
1763
|
}
|
|
2460
1764
|
const input = resolvedInput;
|
|
2461
1765
|
const { contextGraphId, onChainCgId, ual, merkleRoot, publisherAddress, kaId, batchId, versionBlock, authorAddress, subGraphName, trustedAssertionEvidence, assertionVersion, versionSnapshot, signal, } = input;
|
|
2462
1766
|
if (!(await this.verifyChainCgBinding(kaId, onChainCgId, ctx))) {
|
|
2463
1767
|
this.log.info(ctx, `Chain-reconcile: chain CG binding for ${ual} (ka=${kaId}) not confirmed against cg ${onChainCgId}; deferring to sweep retry`);
|
|
2464
|
-
return { outcome: 'unverified',
|
|
1768
|
+
return { outcome: 'unverified', legacyMarkerEligible: false, input };
|
|
2465
1769
|
}
|
|
2466
1770
|
const journalReplay = await this.replayMatchingRecoveryEntries(input, ctx);
|
|
2467
1771
|
// The recovery index predates exact chain-rootCount evidence. Exact asset
|
|
2468
1772
|
// inspection must therefore verify the resulting local assertion version
|
|
2469
1773
|
// below instead of treating a same-root replay as sufficient on its own.
|
|
2470
1774
|
if (journalReplay === 'recovered' && assertionVersion === undefined) {
|
|
2471
|
-
return { outcome: 'already-confirmed',
|
|
1775
|
+
return { outcome: 'already-confirmed', legacyMarkerEligible: false, input };
|
|
2472
1776
|
}
|
|
2473
1777
|
if (journalReplay === 'retry-pending' && assertionVersion === undefined) {
|
|
2474
|
-
return { outcome: 'receipt-revalidation-pending',
|
|
1778
|
+
return { outcome: 'receipt-revalidation-pending', legacyMarkerEligible: false, input };
|
|
2475
1779
|
}
|
|
2476
1780
|
if (journalReplay === 'invalidated') {
|
|
2477
1781
|
this.log.info(ctx, `Chain-reconcile: stale finalization evidence for ${ual} was invalidated; `
|
|
@@ -2494,14 +1798,14 @@ export class FinalizationHandler {
|
|
|
2494
1798
|
trustedAssertionEvidence,
|
|
2495
1799
|
}, ctx);
|
|
2496
1800
|
if (graphScopedOutcome !== undefined) {
|
|
2497
|
-
return { outcome: graphScopedOutcome,
|
|
1801
|
+
return { outcome: graphScopedOutcome, legacyMarkerEligible: false, input };
|
|
2498
1802
|
}
|
|
2499
1803
|
if (await this.hasGraphScopedMetadata(contextGraphId, ual)) {
|
|
2500
1804
|
this.log.info(ctx, `Chain-reconcile: graph-scoped metadata exists for ${ual} but its durable workspace head is missing`);
|
|
2501
|
-
return { outcome: 'no-swm',
|
|
1805
|
+
return { outcome: 'no-swm', legacyMarkerEligible: false, input };
|
|
2502
1806
|
}
|
|
2503
|
-
this.log.info(ctx, `Chain-reconcile:
|
|
2504
|
-
return { outcome: 'no-swm',
|
|
1807
|
+
this.log.info(ctx, `Chain-reconcile: no local graph-scoped state for ${ual}`);
|
|
1808
|
+
return { outcome: 'no-swm', legacyMarkerEligible: true, input };
|
|
2505
1809
|
}
|
|
2506
1810
|
/**
|
|
2507
1811
|
* Reconcile only the exact graph-scoped state named by one UAL. This is the
|
|
@@ -2511,64 +1815,28 @@ export class FinalizationHandler {
|
|
|
2511
1815
|
return (await this.reconcileExactChainRegisteredKC(input, ctx)).outcome;
|
|
2512
1816
|
}
|
|
2513
1817
|
/**
|
|
2514
|
-
* Normal chain reconciliation
|
|
2515
|
-
*
|
|
1818
|
+
* Normal chain reconciliation: the exact operation, then the historical
|
|
1819
|
+
* confirmed marker of a KA materialized before per-KA graphs. A KA with no
|
|
1820
|
+
* local graph-scoped state answers `no-swm` and the caller fetches it from
|
|
1821
|
+
* peers; historical workspace operations are never searched for it.
|
|
2516
1822
|
*/
|
|
2517
1823
|
async handleChainReconciledKC(rawInput, ctx) {
|
|
2518
1824
|
const exact = await this.reconcileExactChainRegisteredKC(rawInput, ctx);
|
|
2519
|
-
if (!exact.
|
|
1825
|
+
if (!exact.legacyMarkerEligible)
|
|
2520
1826
|
return exact.outcome;
|
|
2521
|
-
const { contextGraphId, onChainCgId, ual
|
|
2522
|
-
const
|
|
2523
|
-
const targetMetaGraph =
|
|
2524
|
-
? contextGraphMetaUri(contextGraphId,
|
|
2525
|
-
:
|
|
1827
|
+
const { contextGraphId, onChainCgId, ual } = exact.input;
|
|
1828
|
+
const rootMetaGraph = `did:dkg:context-graph:${contextGraphId}/_meta`;
|
|
1829
|
+
const targetMetaGraph = onChainCgId.length > 0
|
|
1830
|
+
? contextGraphMetaUri(contextGraphId, onChainCgId)
|
|
1831
|
+
: rootMetaGraph;
|
|
2526
1832
|
// The ordinary compatibility path may retain the historical confirmed
|
|
2527
1833
|
// marker shortcut. Exact RFC-64 fetch cannot use this marker because it
|
|
2528
1834
|
// proves neither the current assertion version nor the current chain root.
|
|
2529
|
-
if (await this.isAlreadyConfirmed(ual, targetMetaGraph,
|
|
1835
|
+
if (await this.isAlreadyConfirmed(ual, targetMetaGraph, rootMetaGraph)) {
|
|
2530
1836
|
this.log.info(ctx, `Chain-reconcile: legacy ${ual} already confirmed in VM, skipping`);
|
|
2531
1837
|
return 'already-confirmed';
|
|
2532
1838
|
}
|
|
2533
|
-
|
|
2534
|
-
// the only operation that may scan historical workspace operations.
|
|
2535
|
-
const snapshot = await this.findSwmSnapshotForMerkleRoot(contextGraphId, merkleRoot, subGraphName, onChainCgId);
|
|
2536
|
-
if (!snapshot) {
|
|
2537
|
-
this.log.info(ctx, `Chain-reconcile: no local SWM snapshot matches the published merkleRoot for ${ual}; deferring to sweep retry`);
|
|
2538
|
-
return 'no-swm';
|
|
2539
|
-
}
|
|
2540
|
-
const { rootEntities, sharedMemoryQuads } = snapshot;
|
|
2541
|
-
const resolvedSubGraphName = snapshot.subGraphName ?? subGraphName;
|
|
2542
|
-
const finalizationVersion = { blockNumber: versionBlock, txIndex: 0 };
|
|
2543
|
-
const keepRootCopyOnLabel = await this.getKeepRootCopySignal(contextGraphId, rootEntities, resolvedSubGraphName);
|
|
2544
|
-
const isDualWrite = keepRootCopyOnLabel === true && !!ctxGraphId && !resolvedSubGraphName;
|
|
2545
|
-
const defaultMeta = `did:dkg:context-graph:${contextGraphId}/_meta`;
|
|
2546
|
-
const outcome = await this.applyVerifiedFinalization({
|
|
2547
|
-
contextGraphId,
|
|
2548
|
-
sharedMemoryQuads,
|
|
2549
|
-
ual,
|
|
2550
|
-
rootEntities,
|
|
2551
|
-
publisherAddress,
|
|
2552
|
-
txHash: '',
|
|
2553
|
-
blockNumber: versionBlock,
|
|
2554
|
-
startKAId: kaId,
|
|
2555
|
-
endKAId: kaId,
|
|
2556
|
-
batchId,
|
|
2557
|
-
ctxGraphId,
|
|
2558
|
-
subGraphName: resolvedSubGraphName,
|
|
2559
|
-
authorAddress,
|
|
2560
|
-
finalizationVersion,
|
|
2561
|
-
targetMetaGraph,
|
|
2562
|
-
defaultMeta,
|
|
2563
|
-
isDualWrite,
|
|
2564
|
-
ctx,
|
|
2565
|
-
});
|
|
2566
|
-
if (outcome === 'stale-target') {
|
|
2567
|
-
this.log.info(ctx, `Chain-reconcile: a newer update is already materialised for ${ual}, skipping`);
|
|
2568
|
-
return 'stale-target';
|
|
2569
|
-
}
|
|
2570
|
-
this.log.info(ctx, `Chain-reconcile: promoted SWM snapshot to VM for ${ual} (ka=${kaId}, cg=${onChainCgId})`);
|
|
2571
|
-
return 'promoted';
|
|
1839
|
+
return exact.outcome;
|
|
2572
1840
|
}
|
|
2573
1841
|
/**
|
|
2574
1842
|
* Decide from local state alone whether an ordinary chain reconcile of one
|
|
@@ -2587,14 +1855,16 @@ export class FinalizationHandler {
|
|
|
2587
1855
|
* 4. graph-scoped metadata without a head or confirmed copy;
|
|
2588
1856
|
* 5. the legacy confirmed marker, which that path answers
|
|
2589
1857
|
* `already-confirmed` without reading the root;
|
|
2590
|
-
* 6.
|
|
2591
|
-
* namespaces it searches, nothing local can match any root, so `none`.
|
|
1858
|
+
* 6. nothing else is searched, so everything left is `none`.
|
|
2592
1859
|
*
|
|
2593
1860
|
* `confirmed-vm` trusts the root the local copy was confirmed at; a newer
|
|
2594
|
-
* on-chain version is not looked for here
|
|
2595
|
-
*
|
|
2596
|
-
*
|
|
2597
|
-
* the chain-backed path this
|
|
1861
|
+
* on-chain version is not looked for here, which keeps the walk free of
|
|
1862
|
+
* chain reads for copies it already holds. The walk never revisits a settled
|
|
1863
|
+
* ordinal either, so an update reaches a held copy through finalization
|
|
1864
|
+
* gossip, the StorageACK pending-update lane, the chain-backed path this
|
|
1865
|
+
* method routes a local SWM head to (`present`) while its ordinal is still
|
|
1866
|
+
* walked, and, for any confirmed copy, the `KnowledgeAssetUpdated` refresh
|
|
1867
|
+
* (`handleKAUpdatedNudge` in the agent's SWM host).
|
|
2598
1868
|
*
|
|
2599
1869
|
* Any read failure answers `present`, keeping the caller on its chain path.
|
|
2600
1870
|
*/
|
|
@@ -2662,9 +1932,7 @@ export class FinalizationHandler {
|
|
|
2662
1932
|
if (await this.isAlreadyConfirmed(ual, targetMetaGraph, rootMetaGraph)) {
|
|
2663
1933
|
return { kind: 'confirmed-vm', layout: 'legacy' };
|
|
2664
1934
|
}
|
|
2665
|
-
return
|
|
2666
|
-
? LOCAL_CANDIDATE_PRESENT
|
|
2667
|
-
: { kind: 'none' };
|
|
1935
|
+
return { kind: 'none' };
|
|
2668
1936
|
}
|
|
2669
1937
|
/**
|
|
2670
1938
|
* Whether the headless-VM retirement in the chain-backed path would find an
|
|
@@ -2681,53 +1949,6 @@ export class FinalizationHandler {
|
|
|
2681
1949
|
const result = await this.store.query(`ASK { ${graphs.map((graph) => `{ GRAPH <${assertSafeIri(graph)}> { ?s ?p ?o } }`).join(' UNION ')} }`, { source: 'agent.finalization.localCandidate.swmTwin' });
|
|
2682
1950
|
return result.type !== 'boolean' || result.value;
|
|
2683
1951
|
}
|
|
2684
|
-
/**
|
|
2685
|
-
* Whether the root-matched legacy scan (`findSwmSnapshotForMerkleRoot`) has
|
|
2686
|
-
* any WorkspaceOperation to look at: the given namespace, or the root and
|
|
2687
|
-
* every listed sub-graph when none is known. Memoized per CG write
|
|
2688
|
-
* generation, like that scan's negative memo, so a quiet graph answers from
|
|
2689
|
-
* memory for the whole sweep.
|
|
2690
|
-
*/
|
|
2691
|
-
async hasLegacySwmOperations(contextGraphId, subGraphName) {
|
|
2692
|
-
const memoKey = `${contextGraphId}\0${subGraphName ?? ''}`;
|
|
2693
|
-
const revision = this.graphWriteGen?.getWriteRevision(`${contextGraphDataUri(contextGraphId)}/`);
|
|
2694
|
-
if (revision?.stable) {
|
|
2695
|
-
const memo = this.legacySwmOperationsMemo.get(memoKey);
|
|
2696
|
-
if (memo
|
|
2697
|
-
&& memo.writeGen === revision.generation
|
|
2698
|
-
&& Date.now() - memo.recordedAt < vmReconcileNegativeTtlMs()) {
|
|
2699
|
-
return memo.present;
|
|
2700
|
-
}
|
|
2701
|
-
}
|
|
2702
|
-
const graphManager = new GraphManager(this.store);
|
|
2703
|
-
const metaGraphs = subGraphName
|
|
2704
|
-
? [graphManager.sharedMemoryMetaUri(contextGraphId, subGraphName)]
|
|
2705
|
-
: [
|
|
2706
|
-
contextGraphWorkspaceMetaGraphUri(contextGraphId),
|
|
2707
|
-
...(await graphManager.listSubGraphs(contextGraphId))
|
|
2708
|
-
.map((name) => graphManager.sharedMemoryMetaUri(contextGraphId, name)),
|
|
2709
|
-
];
|
|
2710
|
-
const result = await this.store.query(`ASK { ${metaGraphs
|
|
2711
|
-
.map((graph) => `{ GRAPH <${assertSafeIri(graph)}> { ?op <${DKG_NS}rootEntity> ?root } }`)
|
|
2712
|
-
.join(' UNION ')} }`, { source: 'agent.finalization.localCandidate.legacySwmOperations' });
|
|
2713
|
-
const present = result.type !== 'boolean' || result.value;
|
|
2714
|
-
if (revision?.stable) {
|
|
2715
|
-
// Recorded at the pre-probe generation: a racing write flips the gate.
|
|
2716
|
-
this.legacySwmOperationsMemo.delete(memoKey);
|
|
2717
|
-
this.legacySwmOperationsMemo.set(memoKey, {
|
|
2718
|
-
writeGen: revision.generation,
|
|
2719
|
-
recordedAt: Date.now(),
|
|
2720
|
-
present,
|
|
2721
|
-
});
|
|
2722
|
-
while (this.legacySwmOperationsMemo.size > VM_RECONCILE_NEGATIVE_MEMO_MAX_ENTRIES) {
|
|
2723
|
-
const oldest = this.legacySwmOperationsMemo.keys().next().value;
|
|
2724
|
-
if (oldest === undefined)
|
|
2725
|
-
break;
|
|
2726
|
-
this.legacySwmOperationsMemo.delete(oldest);
|
|
2727
|
-
}
|
|
2728
|
-
}
|
|
2729
|
-
return present;
|
|
2730
|
-
}
|
|
2731
1952
|
async verifyChainCgBinding(kaId, onChainCgId, ctx) {
|
|
2732
1953
|
if (!this.chain || this.chain.chainId === 'none' || typeof this.chain.getKAContextGraphId !== 'function') {
|
|
2733
1954
|
return false;
|
|
@@ -2741,784 +1962,5 @@ export class FinalizationHandler {
|
|
|
2741
1962
|
return false;
|
|
2742
1963
|
}
|
|
2743
1964
|
}
|
|
2744
|
-
/**
|
|
2745
|
-
* Find the local SWM snapshot whose KC merkle root matches the chain
|
|
2746
|
-
* `merkleRoot`, returning its root entities + shared-memory quads.
|
|
2747
|
-
*
|
|
2748
|
-
* SWM workspace meta is written at share-time (before publish), so it carries
|
|
2749
|
-
* no merkle root to look up by — only `?op a dkg:WorkspaceOperation ;
|
|
2750
|
-
* dkg:rootEntity <root>`. We therefore enumerate the candidate operations,
|
|
2751
|
-
* gather each one's SWM quads + private roots, recompute the flat-KC root
|
|
2752
|
-
* (`computeFlatKCRoot`, the same function the gossip path verifies against),
|
|
2753
|
-
* and return the first operation whose computed root equals the chain root.
|
|
2754
|
-
* A match is an authoritative merkle verification.
|
|
2755
|
-
*
|
|
2756
|
-
* Returns `null` when no local operation matches — either the snapshot hasn't
|
|
2757
|
-
* been synced yet (the caller's active-fetch missed / is in flight) or this
|
|
2758
|
-
* KA belongs to a publish this node never shared. Either way the B.2 sweep
|
|
2759
|
-
* retries later.
|
|
2760
|
-
*
|
|
2761
|
-
* Cost note (#1609): the recompute is memoized (`SWM_SNAPSHOT_MERKLE_ROOT_PREDICATE`
|
|
2762
|
-
* + content digest). A *present* KA is resolved by its stamped root directly (fast
|
|
2763
|
-
* path, no scan); the fallback still enumerates every op — it must, because ops
|
|
2764
|
-
* assemble incrementally — but reuses each op's memoized root whenever its content
|
|
2765
|
-
* digest is unchanged, skipping the expensive `computeFlatKCRoot`. So steady-state
|
|
2766
|
-
* reconcile pays one bounded read + a cheap digest per op instead of a full merkle
|
|
2767
|
-
* recompute per op. This is a bridge; OT-RFC-60 makes the root a write-maintained,
|
|
2768
|
-
* indexed property (stamped once at op completion) so the fallback disappears
|
|
2769
|
-
* entirely. The generated-catalog-floor variant keeps the exhaustive recompute
|
|
2770
|
-
* (its match is over quads+floor, not the op's intrinsic stamped root).
|
|
2771
|
-
*
|
|
2772
|
-
* READ memo (#1609, 2026-07-11/12 testnet incident): the #1612 digest memo skips
|
|
2773
|
-
* only the merkle recompute — a never-match KA (a publish this node never shared,
|
|
2774
|
-
* exactly the beacon shape) still paid O(#ops) unbounded SWM CONSTRUCTs on EVERY
|
|
2775
|
-
* sweep tick, forever, stalling the event loop and dropping storage-ACK streams
|
|
2776
|
-
* on big stores. This wrapper adds a write-generation-gated NEGATIVE memo over
|
|
2777
|
-
* the whole scan (floor retry included): a "no match" verdict is replayed
|
|
2778
|
-
* without touching the store while (a) the adapter's write generation for the
|
|
2779
|
-
* CG's graph prefix is unchanged — new SWM only arrives via local writes
|
|
2780
|
-
* (gossip receive, publish share, active fetch), all of which pass the adapter
|
|
2781
|
-
* choke points and bump the generation, so the next call rescans — (b) the
|
|
2782
|
-
* catalog-floor eligibility is unchanged, and (c) the entry is younger than
|
|
2783
|
-
* `vmReconcileNegativeTtlMs()`. A MATCH is never memoized. Stores without the
|
|
2784
|
-
* write-generation capability disable the memo entirely (always scan), and a
|
|
2785
|
-
* restart clears it — every failure mode degrades to a rescan, never a miss.
|
|
2786
|
-
*/
|
|
2787
|
-
async findSwmSnapshotForMerkleRoot(contextGraphId, merkleRoot, subGraphName, onChainCgId) {
|
|
2788
|
-
const allowGeneratedCatalogFloor = await this.allowsGeneratedCatalogFloor(contextGraphId, onChainCgId);
|
|
2789
|
-
// Every graph the scan reads — root/sub-graph SWM buckets, their per-KA
|
|
2790
|
-
// under-graphs and `_shared_memory_meta` (op rows, `privateMerkleRoot`,
|
|
2791
|
-
// stamps) — lives under the CG's URI subtree, so one prefix covers all
|
|
2792
|
-
// namespaces of this call. Broader than strictly needed (any CG-local
|
|
2793
|
-
// write invalidates) — that only costs an extra rescan.
|
|
2794
|
-
const memoKey = `${contextGraphId}\0${subGraphName ?? ''}\0${ethers.hexlify(merkleRoot)}`;
|
|
2795
|
-
const swmWritePrefix = `${contextGraphDataUri(contextGraphId)}/`;
|
|
2796
|
-
const preScanRevision = this.graphWriteGen?.getWriteRevision(swmWritePrefix);
|
|
2797
|
-
const preScanGen = preScanRevision?.generation;
|
|
2798
|
-
if (preScanRevision?.stable) {
|
|
2799
|
-
const memo = this.negativeSnapshotMemo.get(memoKey);
|
|
2800
|
-
if (memo) {
|
|
2801
|
-
if (memo.writeGen === preScanGen &&
|
|
2802
|
-
memo.allowGeneratedCatalogFloor === allowGeneratedCatalogFloor &&
|
|
2803
|
-
Date.now() - memo.recordedAt < vmReconcileNegativeTtlMs()) {
|
|
2804
|
-
// Refresh LRU recency; recordedAt stays — the TTL runs from the scan.
|
|
2805
|
-
this.negativeSnapshotMemo.delete(memoKey);
|
|
2806
|
-
this.negativeSnapshotMemo.set(memoKey, memo);
|
|
2807
|
-
return null;
|
|
2808
|
-
}
|
|
2809
|
-
this.negativeSnapshotMemo.delete(memoKey);
|
|
2810
|
-
}
|
|
2811
|
-
}
|
|
2812
|
-
const hit = await this.runScanSingleFlight(['reconcile', memoKey, String(allowGeneratedCatalogFloor), String(preScanGen ?? 'unknown')].join('\u0000'), () => this.scanForSwmSnapshot(contextGraphId, merkleRoot, subGraphName, allowGeneratedCatalogFloor));
|
|
2813
|
-
if (hit)
|
|
2814
|
-
return hit;
|
|
2815
|
-
if (preScanRevision?.stable) {
|
|
2816
|
-
// Record at the PRE-scan generation: any write that raced the scan (or
|
|
2817
|
-
// the scan's own best-effort restamps) flips the gate above, so the next
|
|
2818
|
-
// call rescans rather than replaying a verdict that may predate the write.
|
|
2819
|
-
this.negativeSnapshotMemo.set(memoKey, {
|
|
2820
|
-
writeGen: preScanRevision.generation,
|
|
2821
|
-
recordedAt: Date.now(),
|
|
2822
|
-
allowGeneratedCatalogFloor,
|
|
2823
|
-
});
|
|
2824
|
-
while (this.negativeSnapshotMemo.size > VM_RECONCILE_NEGATIVE_MEMO_MAX_ENTRIES) {
|
|
2825
|
-
const oldest = this.negativeSnapshotMemo.keys().next().value;
|
|
2826
|
-
if (oldest === undefined)
|
|
2827
|
-
break;
|
|
2828
|
-
this.negativeSnapshotMemo.delete(oldest);
|
|
2829
|
-
}
|
|
2830
|
-
}
|
|
2831
|
-
return null;
|
|
2832
|
-
}
|
|
2833
|
-
/** The authoritative full scan behind {@link findSwmSnapshotForMerkleRoot}. */
|
|
2834
|
-
async scanForSwmSnapshot(contextGraphId, merkleRoot, subGraphName, allowGeneratedCatalogFloor) {
|
|
2835
|
-
// Caller knows the exact namespace → search only that one.
|
|
2836
|
-
if (subGraphName) {
|
|
2837
|
-
const hit = await this.findSwmSnapshotInNamespace(contextGraphId, merkleRoot, subGraphName, allowGeneratedCatalogFloor);
|
|
2838
|
-
return hit ? { ...hit, subGraphName } : null;
|
|
2839
|
-
}
|
|
2840
|
-
// No namespace supplied (the chain-driven path never knows it). Try the
|
|
2841
|
-
// root workspace first, then fall back to every registered sub-graph —
|
|
2842
|
-
// otherwise a KA published into a named sub-graph would stay `no-swm`
|
|
2843
|
-
// forever because its SWM snapshot lives under a sub-graph meta graph,
|
|
2844
|
-
// not the root workspace meta. Return the namespace we matched in so the
|
|
2845
|
-
// caller promotes into the correct data graph.
|
|
2846
|
-
const rootHit = await this.findSwmSnapshotInNamespace(contextGraphId, merkleRoot, undefined, allowGeneratedCatalogFloor);
|
|
2847
|
-
if (rootHit)
|
|
2848
|
-
return { ...rootHit, subGraphName: undefined };
|
|
2849
|
-
let subGraphNames = [];
|
|
2850
|
-
try {
|
|
2851
|
-
subGraphNames = await new GraphManager(this.store).listSubGraphs(contextGraphId);
|
|
2852
|
-
}
|
|
2853
|
-
catch { /* no sub-graphs / store can't enumerate */ }
|
|
2854
|
-
for (const sg of subGraphNames) {
|
|
2855
|
-
const hit = await this.findSwmSnapshotInNamespace(contextGraphId, merkleRoot, sg, allowGeneratedCatalogFloor);
|
|
2856
|
-
if (hit)
|
|
2857
|
-
return { ...hit, subGraphName: sg };
|
|
2858
|
-
}
|
|
2859
|
-
return null;
|
|
2860
|
-
}
|
|
2861
|
-
/**
|
|
2862
|
-
* Search a single SWM namespace (root workspace when `subGraphName` is
|
|
2863
|
-
* undefined, otherwise the named sub-graph's shared-memory meta) for a
|
|
2864
|
-
* WorkspaceOperation whose recomputed flat-KC root matches `merkleRoot`.
|
|
2865
|
-
*/
|
|
2866
|
-
async findSwmSnapshotInNamespace(contextGraphId, merkleRoot, subGraphName, allowGeneratedCatalogFloor = false) {
|
|
2867
|
-
const graphManager = new GraphManager(this.store);
|
|
2868
|
-
const wsMetaGraph = subGraphName
|
|
2869
|
-
? graphManager.sharedMemoryMetaUri(contextGraphId, subGraphName)
|
|
2870
|
-
: contextGraphWorkspaceMetaGraphUri(contextGraphId);
|
|
2871
|
-
// The generated-catalog-floor variant matches over quads+floor rather than an
|
|
2872
|
-
// op's intrinsic root, so it cannot be resolved by the stamped intrinsic root —
|
|
2873
|
-
// those CGs (a stable per-CG access policy) keep the exhaustive recompute scan.
|
|
2874
|
-
const useStampIndex = !allowGeneratedCatalogFloor;
|
|
2875
|
-
// FAST PATH — resolve a *present* KA whose op is stamped with the target root via
|
|
2876
|
-
// one indexed lookup (verified authoritatively), instead of recomputing every op's
|
|
2877
|
-
// root. On a miss it falls through to the memoized fallback scan below.
|
|
2878
|
-
if (useStampIndex) {
|
|
2879
|
-
const stamped = await this.findStampedSwmSnapshot(contextGraphId, wsMetaGraph, merkleRoot, subGraphName);
|
|
2880
|
-
if (stamped)
|
|
2881
|
-
return stamped;
|
|
2882
|
-
}
|
|
2883
|
-
const opsBySubject = new Map();
|
|
2884
|
-
try {
|
|
2885
|
-
const memoPatterns = useStampIndex
|
|
2886
|
-
? `OPTIONAL { ?op <${SWM_SNAPSHOT_MERKLE_ROOT_PREDICATE}> ?memoRoot . }
|
|
2887
|
-
OPTIONAL { ?op <${SWM_SNAPSHOT_CONTENT_DIGEST_PREDICATE}> ?memoDigest . }`
|
|
2888
|
-
: '';
|
|
2889
|
-
const result = await this.store.query(`SELECT ?op ?root ?memoRoot ?memoDigest WHERE {
|
|
2890
|
-
GRAPH <${assertSafeIri(wsMetaGraph)}> {
|
|
2891
|
-
?op <${DKG_NS}rootEntity> ?root .
|
|
2892
|
-
${memoPatterns}
|
|
2893
|
-
}
|
|
2894
|
-
}`, { source: 'agent.finalization.swmSnapshotCandidates' });
|
|
2895
|
-
if (result.type === 'bindings') {
|
|
2896
|
-
for (const row of result.bindings) {
|
|
2897
|
-
const op = typeof row['op'] === 'string' ? row['op'].replace(/^<(.*)>$/, '$1') : '';
|
|
2898
|
-
const root = typeof row['root'] === 'string' ? row['root'].replace(/^<(.*)>$/, '$1') : '';
|
|
2899
|
-
if (!op || !isSafeIri(root))
|
|
2900
|
-
continue;
|
|
2901
|
-
const memo = opsBySubject.get(op) ?? { roots: [] };
|
|
2902
|
-
if (!memo.roots.includes(root))
|
|
2903
|
-
memo.roots.push(root);
|
|
2904
|
-
if (memo.memoRoot === undefined && typeof row['memoRoot'] === 'string') {
|
|
2905
|
-
memo.memoRoot = row['memoRoot'].replace(/^"(.*)".*$/, '$1');
|
|
2906
|
-
}
|
|
2907
|
-
if (memo.memoDigest === undefined && typeof row['memoDigest'] === 'string') {
|
|
2908
|
-
memo.memoDigest = row['memoDigest'].replace(/^"(.*)".*$/, '$1');
|
|
2909
|
-
}
|
|
2910
|
-
opsBySubject.set(op, memo);
|
|
2911
|
-
}
|
|
2912
|
-
}
|
|
2913
|
-
}
|
|
2914
|
-
catch { /* SWM meta may not exist yet */ }
|
|
2915
|
-
if (opsBySubject.size === 0)
|
|
2916
|
-
return null;
|
|
2917
|
-
const opsSorted = [...opsBySubject.entries()].sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
|
|
2918
|
-
// Ops whose content changed (or were never stamped) get a fresh (root, digest)
|
|
2919
|
-
// memo written after the loop — best-effort: a store hiccup on the memo must
|
|
2920
|
-
// never fail reconcile (correctness comes from the recompute here).
|
|
2921
|
-
const restamp = new Map();
|
|
2922
|
-
const targetHex = ethers.hexlify(merkleRoot);
|
|
2923
|
-
let hit = null;
|
|
2924
|
-
for (const [op, memo] of opsSorted) {
|
|
2925
|
-
const roots = memo.roots;
|
|
2926
|
-
const sharedMemoryQuads = await this.getSharedMemoryQuadsForRoots(contextGraphId, roots, subGraphName);
|
|
2927
|
-
if (sharedMemoryQuads.length === 0)
|
|
2928
|
-
continue;
|
|
2929
|
-
const privateRoots = await this.getPrivateRootsFromMeta(contextGraphId, roots, subGraphName);
|
|
2930
|
-
if (useStampIndex) {
|
|
2931
|
-
const digest = this.swmContentDigest(sharedMemoryQuads, privateRoots);
|
|
2932
|
-
let computedHex;
|
|
2933
|
-
if (memo.memoRoot !== undefined && memo.memoDigest === digest) {
|
|
2934
|
-
// Content unchanged since the memo was written → reuse the root and skip
|
|
2935
|
-
// the expensive computeFlatKCRoot. The digest is a pure function of the
|
|
2936
|
-
// same (quads, privateRoots) computeFlatKCRoot consumes, so digest-equal
|
|
2937
|
-
// ⇒ root-equal.
|
|
2938
|
-
computedHex = memo.memoRoot;
|
|
2939
|
-
}
|
|
2940
|
-
else {
|
|
2941
|
-
computedHex = this.computeOpMerkleRoot(sharedMemoryQuads, privateRoots);
|
|
2942
|
-
restamp.set(op, { root: computedHex, digest });
|
|
2943
|
-
}
|
|
2944
|
-
if (computedHex === targetHex) {
|
|
2945
|
-
hit = { rootEntities: roots, sharedMemoryQuads };
|
|
2946
|
-
break;
|
|
2947
|
-
}
|
|
2948
|
-
}
|
|
2949
|
-
else {
|
|
2950
|
-
const merkleMatchedQuads = this.sharedMemoryQuadsMatchingMerkle(contextGraphId, sharedMemoryQuads, privateRoots, merkleRoot, allowGeneratedCatalogFloor);
|
|
2951
|
-
if (merkleMatchedQuads) {
|
|
2952
|
-
return { rootEntities: roots, sharedMemoryQuads: merkleMatchedQuads };
|
|
2953
|
-
}
|
|
2954
|
-
}
|
|
2955
|
-
}
|
|
2956
|
-
await this.persistSwmStamps(wsMetaGraph, restamp);
|
|
2957
|
-
return hit;
|
|
2958
|
-
}
|
|
2959
|
-
/**
|
|
2960
|
-
* Cheap content fingerprint of an op's SWM snapshot — a pure function of the same
|
|
2961
|
-
* `(sharedMemoryQuads, privateRoots)` that `computeFlatKCRoot` consumes, but a
|
|
2962
|
-
* plain sorted-sha256 rather than the full skolemize + merkle construction. Used
|
|
2963
|
-
* only to decide whether a memoized root is still valid (digest-equal ⇒ the
|
|
2964
|
-
* content, hence its flat-KC root, is unchanged), never as an authoritative root.
|
|
2965
|
-
*/
|
|
2966
|
-
/**
|
|
2967
|
-
* The op's intrinsic flat-KC merkle root (hex) — the expensive recompute the
|
|
2968
|
-
* content-digest memo lets us skip for unchanged ops. A named seam so callers and
|
|
2969
|
-
* tests can attribute the cost.
|
|
2970
|
-
*/
|
|
2971
|
-
computeOpMerkleRoot(sharedMemoryQuads, privateRoots) {
|
|
2972
|
-
return ethers.hexlify(computeFlatKCRoot(sharedMemoryQuads, privateRoots));
|
|
2973
|
-
}
|
|
2974
|
-
swmContentDigest(sharedMemoryQuads, privateRoots) {
|
|
2975
|
-
// Unambiguous encoding (NUL field-sep, NL row-sep) matching the SWM-generation
|
|
2976
|
-
// fingerprint convention in `readVmReconcileSwmGen`, so distinct contents cannot
|
|
2977
|
-
// collide to the same digest and cause a wrong memoized-root reuse.
|
|
2978
|
-
const quadLines = sharedMemoryQuads
|
|
2979
|
-
.map((q) => [q.subject, q.predicate, typeof q.object === 'string' ? q.object : String(q.object), q.graph ?? ''].join('\0'))
|
|
2980
|
-
.sort();
|
|
2981
|
-
const privateLines = privateRoots.map((r) => ethers.hexlify(r)).sort();
|
|
2982
|
-
const payload = `${quadLines.join('\n')}\0\0private\0\0${privateLines.join('\n')}`;
|
|
2983
|
-
return createHash('sha256').update(payload, 'utf8').digest('hex');
|
|
2984
|
-
}
|
|
2985
|
-
/**
|
|
2986
|
-
* Replace the (root, content-digest) memo for each op — delete-then-insert so an
|
|
2987
|
-
* op never accumulates duplicate/stale stamp triples. Best-effort; a failure here
|
|
2988
|
-
* leaves the memo absent (next pass recomputes), never wrong.
|
|
2989
|
-
*/
|
|
2990
|
-
async persistSwmStamps(wsMetaGraph, restamp) {
|
|
2991
|
-
if (restamp.size === 0)
|
|
2992
|
-
return;
|
|
2993
|
-
try {
|
|
2994
|
-
const quads = [];
|
|
2995
|
-
for (const [op, { root, digest }] of restamp) {
|
|
2996
|
-
await deleteByPatternWithoutCount(this.store, { graph: wsMetaGraph, subject: op, predicate: SWM_SNAPSHOT_MERKLE_ROOT_PREDICATE });
|
|
2997
|
-
await deleteByPatternWithoutCount(this.store, { graph: wsMetaGraph, subject: op, predicate: SWM_SNAPSHOT_CONTENT_DIGEST_PREDICATE });
|
|
2998
|
-
quads.push({ subject: op, predicate: SWM_SNAPSHOT_MERKLE_ROOT_PREDICATE, object: `"${root}"`, graph: wsMetaGraph }, { subject: op, predicate: SWM_SNAPSHOT_CONTENT_DIGEST_PREDICATE, object: `"${digest}"`, graph: wsMetaGraph });
|
|
2999
|
-
}
|
|
3000
|
-
await this.store.insert(quads);
|
|
3001
|
-
}
|
|
3002
|
-
catch { /* memo is best-effort; the recompute is the source of truth */ }
|
|
3003
|
-
}
|
|
3004
|
-
/**
|
|
3005
|
-
* Fast path for `findSwmSnapshotInNamespace`: resolve the WorkspaceOperation
|
|
3006
|
-
* whose stamped intrinsic root equals `merkleRoot` without recomputing every
|
|
3007
|
-
* op's root. Re-verifies with `verifyMerkleMatch` (authoritative) so a stale
|
|
3008
|
-
* stamp can never promote the wrong snapshot; a stamp that no longer verifies is
|
|
3009
|
-
* dropped so the op falls back into the recompute scan this same pass.
|
|
3010
|
-
*/
|
|
3011
|
-
async findStampedSwmSnapshot(contextGraphId, wsMetaGraph, merkleRoot, subGraphName) {
|
|
3012
|
-
const targetHex = ethers.hexlify(merkleRoot);
|
|
3013
|
-
const rootsByOp = new Map();
|
|
3014
|
-
try {
|
|
3015
|
-
const result = await this.store.query(`SELECT ?op ?root WHERE {
|
|
3016
|
-
GRAPH <${assertSafeIri(wsMetaGraph)}> {
|
|
3017
|
-
?op <${SWM_SNAPSHOT_MERKLE_ROOT_PREDICATE}> "${targetHex}" .
|
|
3018
|
-
?op <${DKG_NS}rootEntity> ?root .
|
|
3019
|
-
}
|
|
3020
|
-
}`, { source: 'agent.finalization.stampedSwmSnapshot' });
|
|
3021
|
-
if (result.type === 'bindings') {
|
|
3022
|
-
for (const row of result.bindings) {
|
|
3023
|
-
const op = typeof row['op'] === 'string' ? row['op'].replace(/^<(.*)>$/, '$1') : '';
|
|
3024
|
-
const root = typeof row['root'] === 'string' ? row['root'].replace(/^<(.*)>$/, '$1') : '';
|
|
3025
|
-
if (!op || !isSafeIri(root))
|
|
3026
|
-
continue;
|
|
3027
|
-
const list = rootsByOp.get(op) ?? [];
|
|
3028
|
-
list.push(root);
|
|
3029
|
-
rootsByOp.set(op, list);
|
|
3030
|
-
}
|
|
3031
|
-
}
|
|
3032
|
-
}
|
|
3033
|
-
catch {
|
|
3034
|
-
return null;
|
|
3035
|
-
}
|
|
3036
|
-
for (const [op, roots] of rootsByOp) {
|
|
3037
|
-
const sharedMemoryQuads = await this.getSharedMemoryQuadsForRoots(contextGraphId, roots, subGraphName);
|
|
3038
|
-
if (sharedMemoryQuads.length > 0) {
|
|
3039
|
-
const privateRoots = await this.getPrivateRootsFromMeta(contextGraphId, roots, subGraphName);
|
|
3040
|
-
if (this.verifyMerkleMatch(sharedMemoryQuads, privateRoots, merkleRoot)) {
|
|
3041
|
-
return { rootEntities: roots, sharedMemoryQuads };
|
|
3042
|
-
}
|
|
3043
|
-
}
|
|
3044
|
-
// The stamp no longer reflects the op's content — drop the whole (root, digest)
|
|
3045
|
-
// memo so the recompute scan re-evaluates and re-stamps it this pass.
|
|
3046
|
-
try {
|
|
3047
|
-
await deleteByPatternWithoutCount(this.store, { graph: wsMetaGraph, subject: op, predicate: SWM_SNAPSHOT_MERKLE_ROOT_PREDICATE });
|
|
3048
|
-
await deleteByPatternWithoutCount(this.store, { graph: wsMetaGraph, subject: op, predicate: SWM_SNAPSHOT_CONTENT_DIGEST_PREDICATE });
|
|
3049
|
-
}
|
|
3050
|
-
catch { /* best-effort self-heal */ }
|
|
3051
|
-
}
|
|
3052
|
-
return null;
|
|
3053
|
-
}
|
|
3054
|
-
async verifyOnChain(txHash, blockNumber, expectedMerkleRoot, expectedPublisher, expectedStartKAId, expectedEndKAId, ctx, ctxGraphId, expectedBatchId) {
|
|
3055
|
-
if (!this.chain || this.chain.chainId === 'none')
|
|
3056
|
-
return { verified: false };
|
|
3057
|
-
if (blockNumber <= 0)
|
|
3058
|
-
return { verified: false };
|
|
3059
|
-
try {
|
|
3060
|
-
// Verify KnowledgeBatchCreated or KCCreated (V10) at the specific block
|
|
3061
|
-
const batchFilter = {
|
|
3062
|
-
eventTypes: ['KnowledgeBatchCreated', 'KCCreated'],
|
|
3063
|
-
fromBlock: blockNumber,
|
|
3064
|
-
toBlock: blockNumber,
|
|
3065
|
-
};
|
|
3066
|
-
let batchVerified = false;
|
|
3067
|
-
// Round 5 review §10 — capture the indexed `author` from the matched
|
|
3068
|
-
// KCCreated event so the caller can populate `dkg:Publication` /
|
|
3069
|
-
// `dkg:authoredBy` provenance on the replica side, mirroring the
|
|
3070
|
-
// originator. `address(0)` here is the unattributed-publish sentinel
|
|
3071
|
-
// (RFC-001 §3.6) and is correctly preserved.
|
|
3072
|
-
let authorAddress;
|
|
3073
|
-
// PR #845 review #9: capture the chain-truth `transactionIndex` from
|
|
3074
|
-
// the matched event so the materialization-version guard does not
|
|
3075
|
-
// have to trust the gossip-supplied `msg.txIndex` (which a peer can
|
|
3076
|
-
// inflate to lock out a legitimate same-block update).
|
|
3077
|
-
let verifiedTxIndex;
|
|
3078
|
-
for await (const event of this.chain.listenForEvents(batchFilter)) {
|
|
3079
|
-
if (event.blockNumber !== blockNumber)
|
|
3080
|
-
continue;
|
|
3081
|
-
if (txHash && (!event.data['txHash'] || event.data['txHash'].toLowerCase() !== txHash.toLowerCase())) {
|
|
3082
|
-
continue;
|
|
3083
|
-
}
|
|
3084
|
-
const eventMerkle = typeof event.data['merkleRoot'] === 'string'
|
|
3085
|
-
? ethers.getBytes(event.data['merkleRoot'])
|
|
3086
|
-
: event.data['merkleRoot'];
|
|
3087
|
-
const eventPublisher = event.data['publisherAddress'] ?? '';
|
|
3088
|
-
const eventStartKAId = BigInt(event.data['startKAId'] ?? '0');
|
|
3089
|
-
const eventEndKAId = BigInt(event.data['endKAId'] ?? '0');
|
|
3090
|
-
const merkleMatch = ethers.hexlify(eventMerkle) === ethers.hexlify(expectedMerkleRoot);
|
|
3091
|
-
const publisherMatch = eventPublisher.toLowerCase() === expectedPublisher.toLowerCase();
|
|
3092
|
-
const rangeMatch = eventStartKAId === expectedStartKAId && eventEndKAId === expectedEndKAId;
|
|
3093
|
-
if (merkleMatch && publisherMatch && rangeMatch) {
|
|
3094
|
-
batchVerified = true;
|
|
3095
|
-
const eventAuthor = event.data['author'] ?? '';
|
|
3096
|
-
if (eventAuthor)
|
|
3097
|
-
authorAddress = eventAuthor;
|
|
3098
|
-
const rawTxIdx = event.data['txIndex'];
|
|
3099
|
-
if (typeof rawTxIdx === 'number' && Number.isFinite(rawTxIdx) && rawTxIdx >= 0) {
|
|
3100
|
-
verifiedTxIndex = rawTxIdx;
|
|
3101
|
-
}
|
|
3102
|
-
break;
|
|
3103
|
-
}
|
|
3104
|
-
}
|
|
3105
|
-
if (!batchVerified)
|
|
3106
|
-
return { verified: false };
|
|
3107
|
-
// V10 publish registers the KC to the context graph internally
|
|
3108
|
-
// (no separate addBatchToContextGraph tx / ContextGraphExpanded event).
|
|
3109
|
-
// Skip the legacy ContextGraphExpanded check — the batch verification
|
|
3110
|
-
// above is sufficient for V10.
|
|
3111
|
-
if (ctxGraphId) {
|
|
3112
|
-
if (typeof this.chain.isV10Ready === 'function' && this.chain.isV10Ready()) {
|
|
3113
|
-
return { verified: true, authorAddress, txIndex: verifiedTxIndex };
|
|
3114
|
-
}
|
|
3115
|
-
try {
|
|
3116
|
-
const scanWindow = 256;
|
|
3117
|
-
const headBlock = typeof this.chain.getBlockNumber === 'function'
|
|
3118
|
-
? await this.chain.getBlockNumber()
|
|
3119
|
-
: blockNumber + scanWindow;
|
|
3120
|
-
const cgFilter = {
|
|
3121
|
-
eventTypes: ['ContextGraphExpanded'],
|
|
3122
|
-
fromBlock: blockNumber,
|
|
3123
|
-
toBlock: Math.min(blockNumber + scanWindow, headBlock),
|
|
3124
|
-
};
|
|
3125
|
-
for await (const event of this.chain.listenForEvents(cgFilter)) {
|
|
3126
|
-
const eventCGId = String(event.data['contextGraphId'] ?? '');
|
|
3127
|
-
const eventBatchId = BigInt(event.data['batchId'] ?? '0');
|
|
3128
|
-
if (eventCGId === ctxGraphId && (expectedBatchId === undefined || eventBatchId === expectedBatchId)) {
|
|
3129
|
-
return { verified: true, authorAddress, txIndex: verifiedTxIndex };
|
|
3130
|
-
}
|
|
3131
|
-
}
|
|
3132
|
-
return { verified: false };
|
|
3133
|
-
}
|
|
3134
|
-
catch {
|
|
3135
|
-
return { verified: true, authorAddress, txIndex: verifiedTxIndex };
|
|
3136
|
-
}
|
|
3137
|
-
}
|
|
3138
|
-
return { verified: true, authorAddress, txIndex: verifiedTxIndex };
|
|
3139
|
-
}
|
|
3140
|
-
catch (err) {
|
|
3141
|
-
this.log.info(ctx, `Finalization on-chain verification pending (RPC may be lagging): ${err instanceof Error ? err.message : String(err)}`);
|
|
3142
|
-
}
|
|
3143
|
-
return { verified: false };
|
|
3144
|
-
}
|
|
3145
|
-
async promoteSharedMemoryToCanonical(contextGraphId, sharedMemoryQuads, ual, msgRootEntities, publisherAddress, txHash, blockNumber, startKAId, endKAId, batchId, ctx, ctxGraphId, subGraphName,
|
|
3146
|
-
/**
|
|
3147
|
-
* EIP-712-attested author recovered from `KnowledgeAssetCreated.author`.
|
|
3148
|
-
* Round 5 review §10 — when set, the replica emits matching
|
|
3149
|
-
* `dkg:Publication` / `dkg:authoredBy` triples so agent-provenance via the
|
|
3150
|
-
* `_meta` triplestore is consistent across the originator and every replica.
|
|
3151
|
-
* `address(0)` and missing/empty values are skipped (preserves the
|
|
3152
|
-
* unattributed-publish path's no-author behaviour from RFC-001 §3.6).
|
|
3153
|
-
*/
|
|
3154
|
-
authorAddress,
|
|
3155
|
-
/**
|
|
3156
|
-
* PR #779 same-graph signal: when `true` the publisher kept a root-graph
|
|
3157
|
-
* copy of the canonical quads, so receivers mirror the dual-write so
|
|
3158
|
-
* label-scoped queries resolve. When `false` (or omitted on older
|
|
3159
|
-
* publishers) the publisher used the explicit-`subContextGraphId` /
|
|
3160
|
-
* remap path and deleted its own root copy on purpose — receivers
|
|
3161
|
-
* MUST NOT dual-write or they re-expose the KC under the source CG
|
|
3162
|
-
* label and double-count it in unscoped queries.
|
|
3163
|
-
*/
|
|
3164
|
-
keepRootCopyOnLabel) {
|
|
3165
|
-
const graphManager = new GraphManager(this.store);
|
|
3166
|
-
await graphManager.ensureContextGraph(contextGraphId);
|
|
3167
|
-
if (subGraphName) {
|
|
3168
|
-
await graphManager.ensureSubGraph(contextGraphId, subGraphName);
|
|
3169
|
-
const sgUri = contextGraphSubGraphUri(contextGraphId, subGraphName);
|
|
3170
|
-
const metaGraph = `did:dkg:context-graph:${assertSafeIri(contextGraphId)}/_meta`;
|
|
3171
|
-
const alreadyRegistered = await this.store.query(`ASK { GRAPH <${metaGraph}> {
|
|
3172
|
-
<${assertSafeIri(sgUri)}> a <http://dkg.io/ontology/SubGraph> ;
|
|
3173
|
-
<http://schema.org/name> ${JSON.stringify(subGraphName)} ;
|
|
3174
|
-
<http://dkg.io/ontology/createdBy> ?createdBy .
|
|
3175
|
-
} }`, { source: 'agent.finalization.subGraphRegistration' });
|
|
3176
|
-
if (alreadyRegistered.type !== 'boolean' || !alreadyRegistered.value) {
|
|
3177
|
-
const regQuads = generateSubGraphRegistration({
|
|
3178
|
-
contextGraphId,
|
|
3179
|
-
subGraphName,
|
|
3180
|
-
createdBy: publisherAddress || 'finalization-discovery',
|
|
3181
|
-
timestamp: new Date(),
|
|
3182
|
-
});
|
|
3183
|
-
await this.store.insert(regQuads);
|
|
3184
|
-
this.markContextGraphMetaDirtyFromQuads?.(regQuads);
|
|
3185
|
-
this.log.info(ctx, `Finalization: auto-registered sub-graph "${subGraphName}" in context graph "${contextGraphId}"`);
|
|
3186
|
-
}
|
|
3187
|
-
}
|
|
3188
|
-
const dataGraph = subGraphName
|
|
3189
|
-
? contextGraphSubGraphUri(contextGraphId, subGraphName)
|
|
3190
|
-
: ctxGraphId
|
|
3191
|
-
? contextGraphDataUri(contextGraphId, ctxGraphId)
|
|
3192
|
-
: graphManager.dataGraphUri(contextGraphId);
|
|
3193
|
-
// Devnet test #774-followup (v10-rc-validation §5 gossip replication):
|
|
3194
|
-
// when `ctxGraphId` is set on a non-sub-graph publish, the canonical
|
|
3195
|
-
// data lands in the per-on-chain-id partition
|
|
3196
|
-
// `<cg>/context/<ctxGraphId>` only. The publisher path
|
|
3197
|
-
// (`dkg-publisher.ts` ~line 1382) intentionally ALSO writes the same
|
|
3198
|
-
// quads to the root `<cg>` graph "so `agent.query(label)` (which
|
|
3199
|
-
// resolves to `did:dkg:context-graph:<label>` without a
|
|
3200
|
-
// `/context/<id>` suffix) still finds the just-published triples"
|
|
3201
|
-
// (commit c2abbc9a). Replicas were never updated to mirror that
|
|
3202
|
-
// dual-write — so a CG-scoped query against a label on a recipient
|
|
3203
|
-
// node finds 0 bindings even though the data is local in
|
|
3204
|
-
// `<cg>/context/<ctxGraphId>`. The query engine cannot widen its
|
|
3205
|
-
// allow set to `<cg>/context/<num>` without a CG-registry lookup
|
|
3206
|
-
// (id-prefix collisions, see PR #776 r6 in dkg-query-engine.ts).
|
|
3207
|
-
// Mirroring the publisher's same-graph dual-write fixes the
|
|
3208
|
-
// visibility asymmetry without re-introducing that ambiguity.
|
|
3209
|
-
//
|
|
3210
|
-
// Critical scoping (Codex review on PR #779): the recipient dual-write
|
|
3211
|
-
// MUST only fire for same-graph publishes (the publisher kept the
|
|
3212
|
-
// root copy too). Explicit-`subContextGraphId` / remap publishes
|
|
3213
|
-
// delete the root copy on purpose (`dkg-publisher.ts` ~line 1393),
|
|
3214
|
-
// and a recipient that re-adds it would re-expose the KC under the
|
|
3215
|
-
// source CG's label on every replica — leaking remap intent and
|
|
3216
|
-
// double-counting the same triples in unscoped queries. The
|
|
3217
|
-
// publisher signals same-graph vs remap on the wire via
|
|
3218
|
-
// `keepRootCopyOnLabel`; missing/false (older publishers, or any
|
|
3219
|
-
// remap publish) → no dual-write.
|
|
3220
|
-
const rootDataGraphForLabel = (!subGraphName && ctxGraphId && keepRootCopyOnLabel === true)
|
|
3221
|
-
? graphManager.dataGraphUri(contextGraphId)
|
|
3222
|
-
: null;
|
|
3223
|
-
// Compute canonical quads now, but defer the `store.insert` until AFTER
|
|
3224
|
-
// the confirmed-meta write and SWM cleanup (see bottom of this method).
|
|
3225
|
-
//
|
|
3226
|
-
// Rationale: on a replica the three store mutations (canonical insert,
|
|
3227
|
-
// confirmed-meta insert, SWM cleanup) happen as three sequential awaits.
|
|
3228
|
-
// Readers polling the canonical graph (e.g. downstream consumers waiting
|
|
3229
|
-
// for "KC landed") can observe the canonical insert before the meta flip
|
|
3230
|
-
// to `confirmed` or before SWM has been drained, producing a visible
|
|
3231
|
-
// "data-without-confirmed-meta" intermediate state that is wrong from a
|
|
3232
|
-
// causal-consistency standpoint (the whole point of the finalization
|
|
3233
|
-
// gossip is that by the time data is canonical on a replica, the chain
|
|
3234
|
-
// proof is durable too). On fast dev machines the window is sub-
|
|
3235
|
-
// millisecond and invisible; on slower CI runners it widens to ~70 ms
|
|
3236
|
-
// and a poll that happens to land there sees `tentative` instead of
|
|
3237
|
-
// `confirmed` (flipped the `Tornado EVM integration: agent`
|
|
3238
|
-
// e2e-finalization suite red on every PR-224 run since the
|
|
3239
|
-
// sync-refactor merge at `bc65c3ae`).
|
|
3240
|
-
//
|
|
3241
|
-
// Ordering meta+cleanup BEFORE canonical insert makes the public state
|
|
3242
|
-
// transition single-stepped from an observer's point of view: either
|
|
3243
|
-
// you don't see the data yet, or you see data + confirmed meta + empty
|
|
3244
|
-
// SWM. The only user-visible partial state that remains (confirmed
|
|
3245
|
-
// meta + no data) is naturally recovered by `isAlreadyConfirmed` on the
|
|
3246
|
-
// next retry path, and it's strictly less broken than the converse.
|
|
3247
|
-
const canonicalQuads = sharedMemoryQuads.map(q => ({ ...q, graph: dataGraph }));
|
|
3248
|
-
const privateRoots = await this.getPrivateRootsFromMeta(contextGraphId, msgRootEntities, subGraphName);
|
|
3249
|
-
const merkleRoot = computeFlatKCRoot(canonicalQuads, privateRoots);
|
|
3250
|
-
const partitioned = skolemizeByEntity(canonicalQuads);
|
|
3251
|
-
const localRootSet = new Set(partitioned.keys());
|
|
3252
|
-
const rootEntities = msgRootEntities.length > 0
|
|
3253
|
-
? msgRootEntities
|
|
3254
|
-
: [...partitioned.keys()];
|
|
3255
|
-
if (msgRootEntities.length > 0) {
|
|
3256
|
-
const msgSet = new Set(msgRootEntities);
|
|
3257
|
-
const extraInMsg = msgRootEntities.filter(r => !localRootSet.has(r));
|
|
3258
|
-
let generatedCatalogRootSet = new Set();
|
|
3259
|
-
try {
|
|
3260
|
-
generatedCatalogRootSet = new Set(splitTrustedGeneratedCatalogRootMap(partitioned, generatedPrivateCatalogTripleKeys(contextGraphId)).generatedCatalogRootEntities);
|
|
3261
|
-
}
|
|
3262
|
-
catch {
|
|
3263
|
-
generatedCatalogRootSet = new Set();
|
|
3264
|
-
}
|
|
3265
|
-
const missingInMsg = [...localRootSet].filter(r => !msgSet.has(r) && !generatedCatalogRootSet.has(r));
|
|
3266
|
-
if (extraInMsg.length > 0 || missingInMsg.length > 0) {
|
|
3267
|
-
this.log.warn(ctx, `Finalization: root entity set mismatch — extra in msg: [${extraInMsg.join(', ')}], missing: [${missingInMsg.join(', ')}]`);
|
|
3268
|
-
}
|
|
3269
|
-
}
|
|
3270
|
-
const kaMetadata = [];
|
|
3271
|
-
// GH #936 — assign per-root tokenIds over a CANONICAL (lexicographic) root
|
|
3272
|
-
// order, NOT the SPARQL/gossip binding order. oxigraph binding order is
|
|
3273
|
-
// store-history-dependent, so two replicas reconciling the same KC from
|
|
3274
|
-
// chain would otherwise mint divergent root→tokenId maps. These tokenIds are
|
|
3275
|
-
// local compatibility labels (the on-chain KA count is 1, no on-chain
|
|
3276
|
-
// dependency — see dkg-publisher.ts), so a content-derived sort makes the
|
|
3277
|
-
// map a pure function of the root SET: identical on every replica and on
|
|
3278
|
-
// both the gossip and chain-reconcile promotion paths.
|
|
3279
|
-
const orderedRoots = [...rootEntities].sort(compareRootIris);
|
|
3280
|
-
for (let tokenIdx = 0; tokenIdx < orderedRoots.length; tokenIdx++) {
|
|
3281
|
-
const rootEntity = orderedRoots[tokenIdx];
|
|
3282
|
-
const entityQuads = partitioned.get(rootEntity) ?? [];
|
|
3283
|
-
if (entityQuads.length === 0)
|
|
3284
|
-
continue;
|
|
3285
|
-
kaMetadata.push({
|
|
3286
|
-
rootEntity,
|
|
3287
|
-
kcUal: ual,
|
|
3288
|
-
tokenId: BigInt(tokenIdx + 1),
|
|
3289
|
-
publicTripleCount: entityQuads.length,
|
|
3290
|
-
privateTripleCount: 0,
|
|
3291
|
-
privateMerkleRoot: undefined,
|
|
3292
|
-
});
|
|
3293
|
-
}
|
|
3294
|
-
const wsPeerId = await this.getPublisherPeerIdFromMeta(contextGraphId, msgRootEntities, subGraphName);
|
|
3295
|
-
// Round 5 review §10 — propagate the on-chain-attested author into the
|
|
3296
|
-
// confirmed `_meta` block so replicas emit a `prov:wasAttributedTo`
|
|
3297
|
-
// matching the originator's. We treat `address(0)` (the
|
|
3298
|
-
// unattributed-publish sentinel) as "no author" by skipping the field,
|
|
3299
|
-
// so the legacy no-author behaviour is preserved verbatim. (The former
|
|
3300
|
-
// `dkg:Publication` / `dkg:authoredBy` mirror was dropped — RFC
|
|
3301
|
-
// ka-metadata-trim Phase 1, zero readers.)
|
|
3302
|
-
const isUnattributed = !authorAddress
|
|
3303
|
-
|| authorAddress === '0x0000000000000000000000000000000000000000'
|
|
3304
|
-
|| authorAddress.toLowerCase() === '0x0000000000000000000000000000000000000000';
|
|
3305
|
-
const kcMeta = {
|
|
3306
|
-
ual,
|
|
3307
|
-
contextGraphId,
|
|
3308
|
-
merkleRoot,
|
|
3309
|
-
publisherPeerId: wsPeerId || publisherAddress,
|
|
3310
|
-
timestamp: new Date(),
|
|
3311
|
-
subGraphName,
|
|
3312
|
-
...(isUnattributed
|
|
3313
|
-
? {}
|
|
3314
|
-
: { authorAddress }),
|
|
3315
|
-
};
|
|
3316
|
-
let blockTimestamp = Math.floor(Date.now() / 1000);
|
|
3317
|
-
if (this.chain && typeof this.chain.getBlockTimestamp === 'function') {
|
|
3318
|
-
try {
|
|
3319
|
-
blockTimestamp = await this.chain.getBlockTimestamp(blockNumber);
|
|
3320
|
-
}
|
|
3321
|
-
catch {
|
|
3322
|
-
this.log.info(ctx, `Could not fetch block timestamp for block ${blockNumber}, using local time`);
|
|
3323
|
-
}
|
|
3324
|
-
}
|
|
3325
|
-
const provenance = {
|
|
3326
|
-
txHash,
|
|
3327
|
-
blockNumber,
|
|
3328
|
-
blockTimestamp,
|
|
3329
|
-
publisherAddress,
|
|
3330
|
-
batchId,
|
|
3331
|
-
chainId: this.chain?.chainId ?? 'unknown',
|
|
3332
|
-
};
|
|
3333
|
-
// Remove any existing tentative status for this UAL before inserting
|
|
3334
|
-
// confirmed metadata. Two graph locations can carry it on this replica:
|
|
3335
|
-
// 1. Root `<cg>/_meta` — gossip-publish-handler ALWAYS writes
|
|
3336
|
-
// `generateTentativeMetadata(...)` here (via `getTentativeStatusQuad`,
|
|
3337
|
-
// which hardcodes the root `_meta` graph). This applies to every
|
|
3338
|
-
// gossip-replicated KC regardless of `ctxGraphId` / dual-write mode.
|
|
3339
|
-
// 2. Per-cgId `<cg>/context/<id>/_meta` — older code paths (and any
|
|
3340
|
-
// future writer that respects the same partition split as canonical
|
|
3341
|
-
// data) may park a tentative quad here when `ctxGraphId` is set.
|
|
3342
|
-
//
|
|
3343
|
-
// Codex r5 on PR #779: the previous form mutated `tentativeQuad.graph`
|
|
3344
|
-
// to the per-cgId URI when `ctxGraphId` was set and deleted ONLY that
|
|
3345
|
-
// copy. The root tentative survived. With the same-graph dual-write
|
|
3346
|
-
// path (`keepRootCopyOnLabel === true`) we then re-inserted confirmed
|
|
3347
|
-
// `_meta` into root `<cg>/_meta`, leaving `tentative` AND `confirmed`
|
|
3348
|
-
// status quads coexisting on the same UAL in the root meta graph —
|
|
3349
|
-
// label-scoped status reads were non-deterministic. Even on the
|
|
3350
|
-
// `keepRootCopyOnLabel === false` path the leftover root tentative was
|
|
3351
|
-
// wrong (the publisher had moved/dropped its root copy on remap).
|
|
3352
|
-
//
|
|
3353
|
-
// Fix: always queue the root tentative for deletion AND, when ctxGraphId
|
|
3354
|
-
// is set, also queue the per-cgId variant. Single `store.delete` call so
|
|
3355
|
-
// all stale tentative copies are reaped in one shot — `delete` no-ops on
|
|
3356
|
-
// missing quads, so it's safe to enumerate both regardless of which
|
|
3357
|
-
// writer actually populated them.
|
|
3358
|
-
const rootTentativeQuad = getTentativeStatusQuad(ual, contextGraphId);
|
|
3359
|
-
const tentativesToDelete = [rootTentativeQuad];
|
|
3360
|
-
if (ctxGraphId) {
|
|
3361
|
-
tentativesToDelete.push({
|
|
3362
|
-
...rootTentativeQuad,
|
|
3363
|
-
graph: contextGraphMetaUri(contextGraphId, ctxGraphId),
|
|
3364
|
-
});
|
|
3365
|
-
}
|
|
3366
|
-
try {
|
|
3367
|
-
await this.store.delete(tentativesToDelete);
|
|
3368
|
-
}
|
|
3369
|
-
catch { /* tentative status may not exist */ }
|
|
3370
|
-
let metaQuads = generateConfirmedFullMetadata(kcMeta, kaMetadata, provenance);
|
|
3371
|
-
// GH #936 — append the SHARED deterministic per-root token rows (no-op for
|
|
3372
|
-
// single-root). This is the SAME helper the publisher uses on the originator
|
|
3373
|
-
// path, so a locally-published and a chain-reconciled multi-root KC expose
|
|
3374
|
-
// an identical, queryable rootEntity→tokenId map. graph = the default
|
|
3375
|
-
// `<cg>/_meta` so the ctxGraphId remap below routes them to the per-cgId
|
|
3376
|
-
// `_meta` (and dual-writes a root copy when keepRootCopyOnLabel).
|
|
3377
|
-
metaQuads.push(...buildDeterministicTokenRows(ual, kaMetadata, `did:dkg:context-graph:${contextGraphId}/_meta`));
|
|
3378
|
-
if (ctxGraphId) {
|
|
3379
|
-
const defaultMeta = `did:dkg:context-graph:${contextGraphId}/_meta`;
|
|
3380
|
-
const targetMeta = contextGraphMetaUri(contextGraphId, ctxGraphId);
|
|
3381
|
-
// Codex r3 on PR #779: the publisher's same-graph path keeps the
|
|
3382
|
-
// confirmed `_meta` triples in BOTH the root `<cg>/_meta` and
|
|
3383
|
-
// the per-cgId `<cg>/context/<cgId>/_meta` graphs (see the
|
|
3384
|
-
// matching dual-write in `dkg-publisher.ts` ~line 1419, comment
|
|
3385
|
-
// "on remap publishes the original copy at `<NAME>/_meta` is
|
|
3386
|
-
// also moved; on same-graph publishes we leave the default copy
|
|
3387
|
-
// in place"). Replicas were only writing the per-cgId copy, so
|
|
3388
|
-
// label-only `_meta` reads (status / UAL / authoredBy lookups
|
|
3389
|
-
// that don't know the on-chain id) diverged between publisher
|
|
3390
|
-
// and recipients. Mirror the publisher's same-graph dual-write
|
|
3391
|
-
// here too — gated on the same `keepRootCopyOnLabel` signal as
|
|
3392
|
-
// the data-graph dual-write below, and folded into a single
|
|
3393
|
-
// `store.insert` for the same retry-safety reason (`_meta`
|
|
3394
|
-
// tentative→confirmed flip is supposed to be the durable point
|
|
3395
|
-
// post-this-call; splitting the writes would re-introduce a
|
|
3396
|
-
// partial-state window).
|
|
3397
|
-
if (keepRootCopyOnLabel === true) {
|
|
3398
|
-
const rootCopies = metaQuads
|
|
3399
|
-
.filter((q) => q.graph === defaultMeta)
|
|
3400
|
-
.map((q) => ({ ...q }));
|
|
3401
|
-
const perCgIdCopies = metaQuads.map((q) => q.graph === defaultMeta ? { ...q, graph: targetMeta } : q);
|
|
3402
|
-
metaQuads = [...perCgIdCopies, ...rootCopies];
|
|
3403
|
-
}
|
|
3404
|
-
else {
|
|
3405
|
-
metaQuads = metaQuads.map((q) => q.graph === defaultMeta ? { ...q, graph: targetMeta } : q);
|
|
3406
|
-
}
|
|
3407
|
-
}
|
|
3408
|
-
// #1233 follow-up — bound agents/_meta: this confirmed-metadata restatement
|
|
3409
|
-
// is load-bearing (prior-root cleanup + the tentative→confirmed flip), so it
|
|
3410
|
-
// cannot be skipped for the agents CG. INSERT then PRUNE (insert-first) via
|
|
3411
|
-
// the helper: the just-inserted UAL is `recordUal` so the prune protects it,
|
|
3412
|
-
// and a post-insert prune failure is swallowed (warned) inside the helper so
|
|
3413
|
-
// it can never abort this promotion. agents/_meta
|
|
3414
|
-
// stays O(agents), not O(agents × heartbeats); no-op prune for every other CG
|
|
3415
|
-
// (so it just inserts). For the agents CG `ctxGraphId` is always undefined
|
|
3416
|
-
// (never on-chain), so `metaQuads` land in the default `<cg>/_meta` graph —
|
|
3417
|
-
// the graph bounded here.
|
|
3418
|
-
// NOTE: the agents CG is always-tentative and never confirms on-chain, so in
|
|
3419
|
-
// practice this promotion is not reached for it (see report); this is a
|
|
3420
|
-
// defensive, lifecycle-preserving bound that keeps the invariant robust.
|
|
3421
|
-
// The bound derives its lock + prune roots from `metaQuads` itself, so the
|
|
3422
|
-
// dropped zero-public-triple roots (the `continue` above) are naturally excluded
|
|
3423
|
-
// from the prune — it can only evict superseded records for roots THIS record
|
|
3424
|
-
// covers, never zero a root the record omits. (Previously this call passed a
|
|
3425
|
-
// separate rootEntities list, which risked exactly that drift.)
|
|
3426
|
-
await insertBoundedAgentRegistryMeta({
|
|
3427
|
-
store: this.store,
|
|
3428
|
-
contextGraphId,
|
|
3429
|
-
metaGraph: `did:dkg:context-graph:${contextGraphId}/_meta`,
|
|
3430
|
-
recordUal: ual,
|
|
3431
|
-
metadataQuads: metaQuads,
|
|
3432
|
-
});
|
|
3433
|
-
// Clean up promoted shared memory entries
|
|
3434
|
-
const sharedMemoryGraph = subGraphName
|
|
3435
|
-
? graphManager.sharedMemoryUri(contextGraphId, subGraphName)
|
|
3436
|
-
: contextGraphWorkspaceGraphUri(contextGraphId);
|
|
3437
|
-
const swmMetaGraph = subGraphName
|
|
3438
|
-
? graphManager.sharedMemoryMetaUri(contextGraphId, subGraphName)
|
|
3439
|
-
: contextGraphWorkspaceMetaGraphUri(contextGraphId);
|
|
3440
|
-
// #1099: replicas hold the gossiped SWM copy in PER-KA graphs
|
|
3441
|
-
// `…/_shared_memory/{author}/{number}` (workspace-handler.ts ~line 987),
|
|
3442
|
-
// but this cleanup only drained the bare bucket. The stale per-KA copy
|
|
3443
|
-
// survived every publish, kept being served to late subscribers via the
|
|
3444
|
-
// PROTOCOL_SYNC SWM responder (which reads the whole `…/_shared_memory/`
|
|
3445
|
-
// prefix), and re-poisoned even the publisher's own SWM on resync —
|
|
3446
|
-
// publisher and replicas permanently disagreed about SWM content.
|
|
3447
|
-
// Mirror DKGPublisher's `swmGraphsUnder`: drain the bucket AND every
|
|
3448
|
-
// graph under its `/` prefix.
|
|
3449
|
-
const allGraphs = await this.store.listGraphs();
|
|
3450
|
-
const swmGraphsForClear = allGraphs.filter((g) => g === sharedMemoryGraph || g.startsWith(`${sharedMemoryGraph}/`));
|
|
3451
|
-
for (const rootEntity of rootEntities) {
|
|
3452
|
-
for (const g of swmGraphsForClear) {
|
|
3453
|
-
await deleteByPatternWithoutCount(this.store, { graph: g, subject: rootEntity });
|
|
3454
|
-
await this.store.deleteBySubjectPrefix(g, rootEntity + '/.well-known/genid/');
|
|
3455
|
-
await deleteByPatternWithoutCount(this.store, {
|
|
3456
|
-
graph: g, subject: rootEntity, predicate: 'http://dkg.io/ontology/workspaceOwner',
|
|
3457
|
-
});
|
|
3458
|
-
}
|
|
3459
|
-
await deleteByPatternWithoutCount(this.store, {
|
|
3460
|
-
graph: swmMetaGraph, subject: rootEntity, predicate: 'http://dkg.io/ontology/workspaceOwner',
|
|
3461
|
-
});
|
|
3462
|
-
await this.deleteMetaForRoot(swmMetaGraph, rootEntity);
|
|
3463
|
-
}
|
|
3464
|
-
// Canonical insert LAST — see the comment above `const canonicalQuads`.
|
|
3465
|
-
// By the time any reader observes these quads in the canonical graph,
|
|
3466
|
-
// `_meta` already carries `confirmed` status + chain provenance and the
|
|
3467
|
-
// matching SWM entries have been drained.
|
|
3468
|
-
//
|
|
3469
|
-
// Codex r2 on PR #779: when same-graph dual-write is in play, both
|
|
3470
|
-
// copies (per-cgId partition + root label graph) MUST land in a single
|
|
3471
|
-
// `store.insert` so a crash or store-write failure between them
|
|
3472
|
-
// cannot leave the replica with the per-cgId copy but no root copy.
|
|
3473
|
-
// The previous split form would be skipped on retry by
|
|
3474
|
-
// `isAlreadyConfirmed()` (`_meta` already confirmed) and the root
|
|
3475
|
-
// copy would never be back-filled — a permanent label-scoped query
|
|
3476
|
-
// miss on that replica. Folding both into one insert call ties their
|
|
3477
|
-
// durability to the same store-write transaction (Oxigraph's `load`
|
|
3478
|
-
// and SPARQL-backend bulk insert are both atomic at the call level).
|
|
3479
|
-
const allCanonicalQuads = rootDataGraphForLabel
|
|
3480
|
-
? [
|
|
3481
|
-
...canonicalQuads,
|
|
3482
|
-
...canonicalQuads.map(q => ({ ...q, graph: rootDataGraphForLabel })),
|
|
3483
|
-
]
|
|
3484
|
-
: canonicalQuads;
|
|
3485
|
-
await this.store.insert(allCanonicalQuads);
|
|
3486
|
-
this.log.info(ctx, `Promoted ${canonicalQuads.length} quads from shared memory to canonical for ${ual}`);
|
|
3487
|
-
this.eventBus?.emit(DKGEvent.MEMORY_GRAPH_CHANGED, {
|
|
3488
|
-
contextGraphId,
|
|
3489
|
-
layers: ['swm', 'vm'],
|
|
3490
|
-
subGraphName,
|
|
3491
|
-
operation: 'verifiable_memory_finalized',
|
|
3492
|
-
source: 'finalization',
|
|
3493
|
-
counts: {
|
|
3494
|
-
roots: rootEntities.length,
|
|
3495
|
-
triples: canonicalQuads.length,
|
|
3496
|
-
},
|
|
3497
|
-
});
|
|
3498
|
-
}
|
|
3499
|
-
async deleteMetaForRoot(metaGraph, rootEntity) {
|
|
3500
|
-
const result = await this.store.query(`SELECT DISTINCT ?op WHERE { GRAPH <${assertSafeIri(metaGraph)}> { ?op ${ENTITY_PRED_ALT} <${assertSafeIri(rootEntity)}> } }`, { source: 'agent.finalization.rootMetadataOperations' });
|
|
3501
|
-
if (result.type !== 'bindings')
|
|
3502
|
-
return;
|
|
3503
|
-
for (const row of result.bindings) {
|
|
3504
|
-
const op = row['op'];
|
|
3505
|
-
if (!op)
|
|
3506
|
-
continue;
|
|
3507
|
-
// OT-RFC-43 §10.1 — dual-write migration: remove BOTH the legacy
|
|
3508
|
-
// dkg:rootEntity and the new dkg:entity for this op/entity pair.
|
|
3509
|
-
await this.store.delete([
|
|
3510
|
-
{ subject: op, predicate: DKG_ROOT_ENTITY_LEGACY, object: rootEntity, graph: metaGraph },
|
|
3511
|
-
{ subject: op, predicate: DKG_ENTITY, object: rootEntity, graph: metaGraph },
|
|
3512
|
-
]);
|
|
3513
|
-
const remaining = await this.store.query(`SELECT (COUNT(DISTINCT ?r) AS ?c) WHERE { GRAPH <${assertSafeIri(metaGraph)}> { <${assertSafeIri(op)}> ${ENTITY_PRED_ALT} ?r } }`, { source: 'agent.finalization.remainingOperationRoots' });
|
|
3514
|
-
const rawCount = remaining.type === 'bindings' && remaining.bindings[0]?.['c'];
|
|
3515
|
-
const countVal = typeof rawCount === 'string'
|
|
3516
|
-
? Number(rawCount.replace(/^"/, '').replace(/"(\^\^<[^>]+>)?$/, ''))
|
|
3517
|
-
: NaN;
|
|
3518
|
-
if (countVal === 0) {
|
|
3519
|
-
await deleteByPatternWithoutCount(this.store, { graph: metaGraph, subject: op });
|
|
3520
|
-
}
|
|
3521
|
-
}
|
|
3522
|
-
}
|
|
3523
1965
|
}
|
|
3524
1966
|
//# sourceMappingURL=finalization-handler.js.map
|