@peerbit/trusted-network 6.0.109 → 6.0.111
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/src/v2-policy-anchor.d.ts +12 -23
- package/dist/src/v2-policy-anchor.d.ts.map +1 -1
- package/dist/src/v2-policy-anchor.js +318 -666
- package/dist/src/v2-policy-anchor.js.map +1 -1
- package/dist/src/v2.d.ts +52 -5
- package/dist/src/v2.d.ts.map +1 -1
- package/dist/src/v2.js +225 -6
- package/dist/src/v2.js.map +1 -1
- package/package.json +7 -6
- package/src/v2-policy-anchor.ts +423 -754
- package/src/v2.ts +209 -6
|
@@ -32,7 +32,8 @@ var __runInitializers = (this && this.__runInitializers) || function (thisArg, i
|
|
|
32
32
|
}
|
|
33
33
|
return useValue ? value : void 0;
|
|
34
34
|
};
|
|
35
|
-
import { deserialize, field, fixedArray, serialize, variant, } from "@dao-xyz/borsh";
|
|
35
|
+
import { deserialize, field, fixedArray, serialize, variant, vec, } from "@dao-xyz/borsh";
|
|
36
|
+
import { CrashSafeTwoSlotCheckpoint } from "@peerbit/any-store/checkpoint";
|
|
36
37
|
import { PublicSignKey, sha256Sync } from "@peerbit/crypto";
|
|
37
38
|
import { compare, concat, equals } from "uint8arrays";
|
|
38
39
|
import { TRUSTED_NETWORK_V2_MAX_PENDING_POLICIES, TrustedNetworkV2PolicyReducer, authenticatePolicySnapshotEntryV2, } from "./v2-policy-engine.js";
|
|
@@ -41,40 +42,39 @@ import { NetworkDescriptorV2, PolicySubjectBindingV2, TRUSTED_NETWORK_V2_KNOWN_R
|
|
|
41
42
|
* Internal crash-safe policy-anchor storage format.
|
|
42
43
|
*
|
|
43
44
|
* This module is intentionally absent from the package entry point. Its store
|
|
44
|
-
* must already be open and
|
|
45
|
-
* one wrapper may write that scope:
|
|
46
|
-
* not pretend that
|
|
45
|
+
* must already be open and dedicated to this anchor and descriptor. Exactly
|
|
46
|
+
* one wrapper may write that scope: the two-slot checkpoint deliberately does
|
|
47
|
+
* not pretend that atomic replacement is a multi-process compare-and-swap.
|
|
47
48
|
*/
|
|
49
|
+
/** The namespace owned by the superseded append-only anchor format. */
|
|
48
50
|
export const TRUSTED_NETWORK_V2_POLICY_ANCHOR_STORE_OWNER = "peerbit/trusted-network/v2/policy-anchor/v1";
|
|
49
|
-
export const TRUSTED_NETWORK_V2_MAX_POLICY_ANCHOR_GENERATION_BYTES = TRUSTED_NETWORK_V2_MAX_POLICY_ENTRY_BYTES * 4;
|
|
50
|
-
const GENERATION_KEY_PREFIX = `${TRUSTED_NETWORK_V2_POLICY_ANCHOR_STORE_OWNER}/generation/`;
|
|
51
|
-
const ANCHOR_FORMAT_VERSION = 2;
|
|
52
51
|
const REDUCER_DURABLE_FORMAT_VERSION = 1;
|
|
53
|
-
const MAX_U64 = 0xffffffffffffffffn;
|
|
54
52
|
const MAX_UNAVAILABLE_REASON_LENGTH = 512;
|
|
55
53
|
// The core admits at most 64 pending policies. A fork transition can surface
|
|
56
54
|
// that complete set plus its accepted and triggering children. Once the
|
|
57
55
|
// transition is durable the wrapper stops policy admission entirely.
|
|
58
56
|
const MAX_FORK_OBSERVATIONS_V2 = TRUSTED_NETWORK_V2_MAX_PENDING_POLICIES + 2;
|
|
57
|
+
// Variant/state and length framing for the parent, empty candidate, digest,
|
|
58
|
+
// empty reason, u8 vector count, and every u32-framed observation.
|
|
59
|
+
const MAX_CHECKPOINT_PAYLOAD_FRAMING_BYTES_V2 = 313;
|
|
60
|
+
/** Exact maximum encoded application payload accepted by the checkpoint. */
|
|
61
|
+
export const TRUSTED_NETWORK_V2_MAX_POLICY_ANCHOR_CHECKPOINT_PAYLOAD_BYTES = (MAX_FORK_OBSERVATIONS_V2 + 1) * TRUSTED_NETWORK_V2_MAX_POLICY_ENTRY_BYTES +
|
|
62
|
+
MAX_CHECKPOINT_PAYLOAD_FRAMING_BYTES_V2;
|
|
59
63
|
const MAX_QUEUED_POLICY_ENTRY_BYTES_V2 = TRUSTED_NETWORK_V2_MAX_POLICY_ENTRY_BYTES * 2;
|
|
60
64
|
const ZERO_DIGEST = new Uint8Array(32);
|
|
61
65
|
const textEncoder = new TextEncoder();
|
|
62
|
-
const
|
|
63
|
-
const GENERATION_CHECKSUM_DOMAIN = textEncoder.encode("peerbit/trusted-network/v2/policy-anchor/generation/v1");
|
|
64
|
-
const FORK_OBSERVATION_COMMITMENT_DOMAIN = textEncoder.encode("peerbit/trusted-network/v2/policy-anchor/fork-observations/v1");
|
|
66
|
+
const CHECKPOINT_SCOPE_DOMAIN = textEncoder.encode("peerbit/trusted-network/v2/policy-anchor/checkpoint/v1\0");
|
|
65
67
|
const ANCHOR_STATE = Object.freeze({
|
|
66
68
|
ACTIVE: 1,
|
|
67
69
|
UNAVAILABLE: 2,
|
|
68
70
|
FORKED: 3,
|
|
69
71
|
});
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
let PolicyAnchorCoreStateRecordV2 = (() => {
|
|
77
|
-
let _classDecorators = [variant([2, 16, 0])];
|
|
72
|
+
/**
|
|
73
|
+
* One self-contained application snapshot. The enclosing generic checkpoint
|
|
74
|
+
* supplies generation, scope, predecessor, and checksum authentication.
|
|
75
|
+
*/
|
|
76
|
+
let PolicyAnchorCheckpointPayloadV2 = (() => {
|
|
77
|
+
let _classDecorators = [variant([2, 16, 5])];
|
|
78
78
|
let _classDescriptor;
|
|
79
79
|
let _classExtraInitializers = [];
|
|
80
80
|
let _classThis;
|
|
@@ -93,13 +93,10 @@ let PolicyAnchorCoreStateRecordV2 = (() => {
|
|
|
93
93
|
let _unavailableReason_decorators;
|
|
94
94
|
let _unavailableReason_initializers = [];
|
|
95
95
|
let _unavailableReason_extraInitializers = [];
|
|
96
|
-
let
|
|
97
|
-
let
|
|
98
|
-
let
|
|
99
|
-
|
|
100
|
-
let _forkChildEntryBytes1_initializers = [];
|
|
101
|
-
let _forkChildEntryBytes1_extraInitializers = [];
|
|
102
|
-
var PolicyAnchorCoreStateRecordV2 = class {
|
|
96
|
+
let _forkObservationEntryBytes_decorators;
|
|
97
|
+
let _forkObservationEntryBytes_initializers = [];
|
|
98
|
+
let _forkObservationEntryBytes_extraInitializers = [];
|
|
99
|
+
var PolicyAnchorCheckpointPayloadV2 = class {
|
|
103
100
|
static { _classThis = this; }
|
|
104
101
|
static {
|
|
105
102
|
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
|
|
@@ -108,17 +105,15 @@ let PolicyAnchorCoreStateRecordV2 = (() => {
|
|
|
108
105
|
_comparisonCandidateEntryBytes_decorators = [field({ type: Uint8Array })];
|
|
109
106
|
_acceptedAncestorDigest_decorators = [field({ type: fixedArray("u8", 32) })];
|
|
110
107
|
_unavailableReason_decorators = [field({ type: "string" })];
|
|
111
|
-
|
|
112
|
-
_forkChildEntryBytes1_decorators = [field({ type: Uint8Array })];
|
|
108
|
+
_forkObservationEntryBytes_decorators = [field({ type: vec(Uint8Array, "u8") })];
|
|
113
109
|
__esDecorate(null, null, _state_decorators, { kind: "field", name: "state", static: false, private: false, access: { has: obj => "state" in obj, get: obj => obj.state, set: (obj, value) => { obj.state = value; } }, metadata: _metadata }, _state_initializers, _state_extraInitializers);
|
|
114
110
|
__esDecorate(null, null, _acceptedHeadEntryBytes_decorators, { kind: "field", name: "acceptedHeadEntryBytes", static: false, private: false, access: { has: obj => "acceptedHeadEntryBytes" in obj, get: obj => obj.acceptedHeadEntryBytes, set: (obj, value) => { obj.acceptedHeadEntryBytes = value; } }, metadata: _metadata }, _acceptedHeadEntryBytes_initializers, _acceptedHeadEntryBytes_extraInitializers);
|
|
115
111
|
__esDecorate(null, null, _comparisonCandidateEntryBytes_decorators, { kind: "field", name: "comparisonCandidateEntryBytes", static: false, private: false, access: { has: obj => "comparisonCandidateEntryBytes" in obj, get: obj => obj.comparisonCandidateEntryBytes, set: (obj, value) => { obj.comparisonCandidateEntryBytes = value; } }, metadata: _metadata }, _comparisonCandidateEntryBytes_initializers, _comparisonCandidateEntryBytes_extraInitializers);
|
|
116
112
|
__esDecorate(null, null, _acceptedAncestorDigest_decorators, { kind: "field", name: "acceptedAncestorDigest", static: false, private: false, access: { has: obj => "acceptedAncestorDigest" in obj, get: obj => obj.acceptedAncestorDigest, set: (obj, value) => { obj.acceptedAncestorDigest = value; } }, metadata: _metadata }, _acceptedAncestorDigest_initializers, _acceptedAncestorDigest_extraInitializers);
|
|
117
113
|
__esDecorate(null, null, _unavailableReason_decorators, { kind: "field", name: "unavailableReason", static: false, private: false, access: { has: obj => "unavailableReason" in obj, get: obj => obj.unavailableReason, set: (obj, value) => { obj.unavailableReason = value; } }, metadata: _metadata }, _unavailableReason_initializers, _unavailableReason_extraInitializers);
|
|
118
|
-
__esDecorate(null, null,
|
|
119
|
-
__esDecorate(null, null, _forkChildEntryBytes1_decorators, { kind: "field", name: "forkChildEntryBytes1", static: false, private: false, access: { has: obj => "forkChildEntryBytes1" in obj, get: obj => obj.forkChildEntryBytes1, set: (obj, value) => { obj.forkChildEntryBytes1 = value; } }, metadata: _metadata }, _forkChildEntryBytes1_initializers, _forkChildEntryBytes1_extraInitializers);
|
|
114
|
+
__esDecorate(null, null, _forkObservationEntryBytes_decorators, { kind: "field", name: "forkObservationEntryBytes", static: false, private: false, access: { has: obj => "forkObservationEntryBytes" in obj, get: obj => obj.forkObservationEntryBytes, set: (obj, value) => { obj.forkObservationEntryBytes = value; } }, metadata: _metadata }, _forkObservationEntryBytes_initializers, _forkObservationEntryBytes_extraInitializers);
|
|
120
115
|
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
121
|
-
|
|
116
|
+
PolicyAnchorCheckpointPayloadV2 = _classThis = _classDescriptor.value;
|
|
122
117
|
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
123
118
|
__runInitializers(_classThis, _classExtraInitializers);
|
|
124
119
|
}
|
|
@@ -127,175 +122,14 @@ let PolicyAnchorCoreStateRecordV2 = (() => {
|
|
|
127
122
|
comparisonCandidateEntryBytes = (__runInitializers(this, _acceptedHeadEntryBytes_extraInitializers), __runInitializers(this, _comparisonCandidateEntryBytes_initializers, void 0));
|
|
128
123
|
acceptedAncestorDigest = (__runInitializers(this, _comparisonCandidateEntryBytes_extraInitializers), __runInitializers(this, _acceptedAncestorDigest_initializers, void 0));
|
|
129
124
|
unavailableReason = (__runInitializers(this, _acceptedAncestorDigest_extraInitializers), __runInitializers(this, _unavailableReason_initializers, void 0));
|
|
130
|
-
|
|
131
|
-
forkChildEntryBytes1 = (__runInitializers(this, _forkChildEntryBytes0_extraInitializers), __runInitializers(this, _forkChildEntryBytes1_initializers, void 0));
|
|
132
|
-
constructor(properties) {
|
|
133
|
-
__runInitializers(this, _forkChildEntryBytes1_extraInitializers);
|
|
134
|
-
if (properties)
|
|
135
|
-
Object.assign(this, properties);
|
|
136
|
-
}
|
|
137
|
-
};
|
|
138
|
-
return PolicyAnchorCoreStateRecordV2 = _classThis;
|
|
139
|
-
})();
|
|
140
|
-
let PolicyAnchorStateGenerationPayloadV2 = (() => {
|
|
141
|
-
let _classDecorators = [variant([2, 16, 3])];
|
|
142
|
-
let _classDescriptor;
|
|
143
|
-
let _classExtraInitializers = [];
|
|
144
|
-
let _classThis;
|
|
145
|
-
let _coreState_decorators;
|
|
146
|
-
let _coreState_initializers = [];
|
|
147
|
-
let _coreState_extraInitializers = [];
|
|
148
|
-
let _forkObservationCount_decorators;
|
|
149
|
-
let _forkObservationCount_initializers = [];
|
|
150
|
-
let _forkObservationCount_extraInitializers = [];
|
|
151
|
-
let _forkObservationCommitment_decorators;
|
|
152
|
-
let _forkObservationCommitment_initializers = [];
|
|
153
|
-
let _forkObservationCommitment_extraInitializers = [];
|
|
154
|
-
var PolicyAnchorStateGenerationPayloadV2 = class {
|
|
155
|
-
static { _classThis = this; }
|
|
156
|
-
static {
|
|
157
|
-
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
|
|
158
|
-
_coreState_decorators = [field({ type: PolicyAnchorCoreStateRecordV2 })];
|
|
159
|
-
_forkObservationCount_decorators = [field({ type: "u8" })];
|
|
160
|
-
_forkObservationCommitment_decorators = [field({ type: fixedArray("u8", 32) })];
|
|
161
|
-
__esDecorate(null, null, _coreState_decorators, { kind: "field", name: "coreState", static: false, private: false, access: { has: obj => "coreState" in obj, get: obj => obj.coreState, set: (obj, value) => { obj.coreState = value; } }, metadata: _metadata }, _coreState_initializers, _coreState_extraInitializers);
|
|
162
|
-
__esDecorate(null, null, _forkObservationCount_decorators, { kind: "field", name: "forkObservationCount", static: false, private: false, access: { has: obj => "forkObservationCount" in obj, get: obj => obj.forkObservationCount, set: (obj, value) => { obj.forkObservationCount = value; } }, metadata: _metadata }, _forkObservationCount_initializers, _forkObservationCount_extraInitializers);
|
|
163
|
-
__esDecorate(null, null, _forkObservationCommitment_decorators, { kind: "field", name: "forkObservationCommitment", static: false, private: false, access: { has: obj => "forkObservationCommitment" in obj, get: obj => obj.forkObservationCommitment, set: (obj, value) => { obj.forkObservationCommitment = value; } }, metadata: _metadata }, _forkObservationCommitment_initializers, _forkObservationCommitment_extraInitializers);
|
|
164
|
-
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
165
|
-
PolicyAnchorStateGenerationPayloadV2 = _classThis = _classDescriptor.value;
|
|
166
|
-
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
167
|
-
__runInitializers(_classThis, _classExtraInitializers);
|
|
168
|
-
}
|
|
169
|
-
coreState = __runInitializers(this, _coreState_initializers, void 0);
|
|
170
|
-
forkObservationCount = (__runInitializers(this, _coreState_extraInitializers), __runInitializers(this, _forkObservationCount_initializers, void 0));
|
|
171
|
-
forkObservationCommitment = (__runInitializers(this, _forkObservationCount_extraInitializers), __runInitializers(this, _forkObservationCommitment_initializers, void 0));
|
|
125
|
+
forkObservationEntryBytes = (__runInitializers(this, _unavailableReason_extraInitializers), __runInitializers(this, _forkObservationEntryBytes_initializers, void 0));
|
|
172
126
|
constructor(properties) {
|
|
173
|
-
__runInitializers(this,
|
|
127
|
+
__runInitializers(this, _forkObservationEntryBytes_extraInitializers);
|
|
174
128
|
if (properties)
|
|
175
129
|
Object.assign(this, properties);
|
|
176
130
|
}
|
|
177
131
|
};
|
|
178
|
-
return
|
|
179
|
-
})();
|
|
180
|
-
let PolicyAnchorObservationGenerationPayloadV2 = (() => {
|
|
181
|
-
let _classDecorators = [variant([2, 16, 4])];
|
|
182
|
-
let _classDescriptor;
|
|
183
|
-
let _classExtraInitializers = [];
|
|
184
|
-
let _classThis;
|
|
185
|
-
let _entryBytes_decorators;
|
|
186
|
-
let _entryBytes_initializers = [];
|
|
187
|
-
let _entryBytes_extraInitializers = [];
|
|
188
|
-
var PolicyAnchorObservationGenerationPayloadV2 = class {
|
|
189
|
-
static { _classThis = this; }
|
|
190
|
-
static {
|
|
191
|
-
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
|
|
192
|
-
_entryBytes_decorators = [field({ type: Uint8Array })];
|
|
193
|
-
__esDecorate(null, null, _entryBytes_decorators, { kind: "field", name: "entryBytes", static: false, private: false, access: { has: obj => "entryBytes" in obj, get: obj => obj.entryBytes, set: (obj, value) => { obj.entryBytes = value; } }, metadata: _metadata }, _entryBytes_initializers, _entryBytes_extraInitializers);
|
|
194
|
-
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
195
|
-
PolicyAnchorObservationGenerationPayloadV2 = _classThis = _classDescriptor.value;
|
|
196
|
-
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
197
|
-
__runInitializers(_classThis, _classExtraInitializers);
|
|
198
|
-
}
|
|
199
|
-
entryBytes = __runInitializers(this, _entryBytes_initializers, void 0);
|
|
200
|
-
constructor(properties) {
|
|
201
|
-
__runInitializers(this, _entryBytes_extraInitializers);
|
|
202
|
-
if (properties)
|
|
203
|
-
Object.assign(this, properties);
|
|
204
|
-
}
|
|
205
|
-
};
|
|
206
|
-
return PolicyAnchorObservationGenerationPayloadV2 = _classThis;
|
|
207
|
-
})();
|
|
208
|
-
let PolicyAnchorGenerationBodyV2 = (() => {
|
|
209
|
-
let _classDecorators = [variant([2, 16, 1])];
|
|
210
|
-
let _classDescriptor;
|
|
211
|
-
let _classExtraInitializers = [];
|
|
212
|
-
let _classThis;
|
|
213
|
-
let _formatVersion_decorators;
|
|
214
|
-
let _formatVersion_initializers = [];
|
|
215
|
-
let _formatVersion_extraInitializers = [];
|
|
216
|
-
let _generation_decorators;
|
|
217
|
-
let _generation_initializers = [];
|
|
218
|
-
let _generation_extraInitializers = [];
|
|
219
|
-
let _descriptorDigest_decorators;
|
|
220
|
-
let _descriptorDigest_initializers = [];
|
|
221
|
-
let _descriptorDigest_extraInitializers = [];
|
|
222
|
-
let _previousGenerationChecksum_decorators;
|
|
223
|
-
let _previousGenerationChecksum_initializers = [];
|
|
224
|
-
let _previousGenerationChecksum_extraInitializers = [];
|
|
225
|
-
let _kind_decorators;
|
|
226
|
-
let _kind_initializers = [];
|
|
227
|
-
let _kind_extraInitializers = [];
|
|
228
|
-
let _payloadBytes_decorators;
|
|
229
|
-
let _payloadBytes_initializers = [];
|
|
230
|
-
let _payloadBytes_extraInitializers = [];
|
|
231
|
-
var PolicyAnchorGenerationBodyV2 = class {
|
|
232
|
-
static { _classThis = this; }
|
|
233
|
-
static {
|
|
234
|
-
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
|
|
235
|
-
_formatVersion_decorators = [field({ type: "u8" })];
|
|
236
|
-
_generation_decorators = [field({ type: "u64" })];
|
|
237
|
-
_descriptorDigest_decorators = [field({ type: fixedArray("u8", 32) })];
|
|
238
|
-
_previousGenerationChecksum_decorators = [field({ type: fixedArray("u8", 32) })];
|
|
239
|
-
_kind_decorators = [field({ type: "u8" })];
|
|
240
|
-
_payloadBytes_decorators = [field({ type: Uint8Array })];
|
|
241
|
-
__esDecorate(null, null, _formatVersion_decorators, { kind: "field", name: "formatVersion", static: false, private: false, access: { has: obj => "formatVersion" in obj, get: obj => obj.formatVersion, set: (obj, value) => { obj.formatVersion = value; } }, metadata: _metadata }, _formatVersion_initializers, _formatVersion_extraInitializers);
|
|
242
|
-
__esDecorate(null, null, _generation_decorators, { kind: "field", name: "generation", static: false, private: false, access: { has: obj => "generation" in obj, get: obj => obj.generation, set: (obj, value) => { obj.generation = value; } }, metadata: _metadata }, _generation_initializers, _generation_extraInitializers);
|
|
243
|
-
__esDecorate(null, null, _descriptorDigest_decorators, { kind: "field", name: "descriptorDigest", static: false, private: false, access: { has: obj => "descriptorDigest" in obj, get: obj => obj.descriptorDigest, set: (obj, value) => { obj.descriptorDigest = value; } }, metadata: _metadata }, _descriptorDigest_initializers, _descriptorDigest_extraInitializers);
|
|
244
|
-
__esDecorate(null, null, _previousGenerationChecksum_decorators, { kind: "field", name: "previousGenerationChecksum", static: false, private: false, access: { has: obj => "previousGenerationChecksum" in obj, get: obj => obj.previousGenerationChecksum, set: (obj, value) => { obj.previousGenerationChecksum = value; } }, metadata: _metadata }, _previousGenerationChecksum_initializers, _previousGenerationChecksum_extraInitializers);
|
|
245
|
-
__esDecorate(null, null, _kind_decorators, { kind: "field", name: "kind", static: false, private: false, access: { has: obj => "kind" in obj, get: obj => obj.kind, set: (obj, value) => { obj.kind = value; } }, metadata: _metadata }, _kind_initializers, _kind_extraInitializers);
|
|
246
|
-
__esDecorate(null, null, _payloadBytes_decorators, { kind: "field", name: "payloadBytes", static: false, private: false, access: { has: obj => "payloadBytes" in obj, get: obj => obj.payloadBytes, set: (obj, value) => { obj.payloadBytes = value; } }, metadata: _metadata }, _payloadBytes_initializers, _payloadBytes_extraInitializers);
|
|
247
|
-
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
248
|
-
PolicyAnchorGenerationBodyV2 = _classThis = _classDescriptor.value;
|
|
249
|
-
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
250
|
-
__runInitializers(_classThis, _classExtraInitializers);
|
|
251
|
-
}
|
|
252
|
-
formatVersion = __runInitializers(this, _formatVersion_initializers, void 0);
|
|
253
|
-
generation = (__runInitializers(this, _formatVersion_extraInitializers), __runInitializers(this, _generation_initializers, void 0));
|
|
254
|
-
descriptorDigest = (__runInitializers(this, _generation_extraInitializers), __runInitializers(this, _descriptorDigest_initializers, void 0));
|
|
255
|
-
previousGenerationChecksum = (__runInitializers(this, _descriptorDigest_extraInitializers), __runInitializers(this, _previousGenerationChecksum_initializers, void 0));
|
|
256
|
-
kind = (__runInitializers(this, _previousGenerationChecksum_extraInitializers), __runInitializers(this, _kind_initializers, void 0));
|
|
257
|
-
payloadBytes = (__runInitializers(this, _kind_extraInitializers), __runInitializers(this, _payloadBytes_initializers, void 0));
|
|
258
|
-
constructor(properties) {
|
|
259
|
-
__runInitializers(this, _payloadBytes_extraInitializers);
|
|
260
|
-
if (properties)
|
|
261
|
-
Object.assign(this, properties);
|
|
262
|
-
}
|
|
263
|
-
};
|
|
264
|
-
return PolicyAnchorGenerationBodyV2 = _classThis;
|
|
265
|
-
})();
|
|
266
|
-
let PolicyAnchorGenerationRecordV2 = (() => {
|
|
267
|
-
let _classDecorators = [variant([2, 16, 2])];
|
|
268
|
-
let _classDescriptor;
|
|
269
|
-
let _classExtraInitializers = [];
|
|
270
|
-
let _classThis;
|
|
271
|
-
let _body_decorators;
|
|
272
|
-
let _body_initializers = [];
|
|
273
|
-
let _body_extraInitializers = [];
|
|
274
|
-
let _checksum_decorators;
|
|
275
|
-
let _checksum_initializers = [];
|
|
276
|
-
let _checksum_extraInitializers = [];
|
|
277
|
-
var PolicyAnchorGenerationRecordV2 = class {
|
|
278
|
-
static { _classThis = this; }
|
|
279
|
-
static {
|
|
280
|
-
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
|
|
281
|
-
_body_decorators = [field({ type: PolicyAnchorGenerationBodyV2 })];
|
|
282
|
-
_checksum_decorators = [field({ type: fixedArray("u8", 32) })];
|
|
283
|
-
__esDecorate(null, null, _body_decorators, { kind: "field", name: "body", static: false, private: false, access: { has: obj => "body" in obj, get: obj => obj.body, set: (obj, value) => { obj.body = value; } }, metadata: _metadata }, _body_initializers, _body_extraInitializers);
|
|
284
|
-
__esDecorate(null, null, _checksum_decorators, { kind: "field", name: "checksum", static: false, private: false, access: { has: obj => "checksum" in obj, get: obj => obj.checksum, set: (obj, value) => { obj.checksum = value; } }, metadata: _metadata }, _checksum_initializers, _checksum_extraInitializers);
|
|
285
|
-
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
286
|
-
PolicyAnchorGenerationRecordV2 = _classThis = _classDescriptor.value;
|
|
287
|
-
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
288
|
-
__runInitializers(_classThis, _classExtraInitializers);
|
|
289
|
-
}
|
|
290
|
-
body = __runInitializers(this, _body_initializers, void 0);
|
|
291
|
-
checksum = (__runInitializers(this, _body_extraInitializers), __runInitializers(this, _checksum_initializers, void 0));
|
|
292
|
-
constructor(properties) {
|
|
293
|
-
__runInitializers(this, _checksum_extraInitializers);
|
|
294
|
-
if (properties)
|
|
295
|
-
Object.assign(this, properties);
|
|
296
|
-
}
|
|
297
|
-
};
|
|
298
|
-
return PolicyAnchorGenerationRecordV2 = _classThis;
|
|
132
|
+
return PolicyAnchorCheckpointPayloadV2 = _classThis;
|
|
299
133
|
})();
|
|
300
134
|
const TYPED_ARRAY_PROTOTYPE = Object.getPrototypeOf(Uint8Array.prototype);
|
|
301
135
|
const TYPED_ARRAY_BYTE_LENGTH = Object.getOwnPropertyDescriptor(TYPED_ARRAY_PROTOTYPE, "byteLength").get;
|
|
@@ -309,6 +143,8 @@ const exactUint8ArrayByteLength = (input) => {
|
|
|
309
143
|
}
|
|
310
144
|
const byteLength = TYPED_ARRAY_BYTE_LENGTH.call(input);
|
|
311
145
|
if (byteLength === 0) {
|
|
146
|
+
// The intrinsic getter reports zero for both empty and detached views. The
|
|
147
|
+
// intrinsic set distinguishes them without consulting caller properties.
|
|
312
148
|
UINT8_ARRAY_SET.call(new Uint8Array(0), input);
|
|
313
149
|
}
|
|
314
150
|
return byteLength;
|
|
@@ -362,36 +198,20 @@ const copyForkEvidence = (evidence) => evidence === undefined
|
|
|
362
198
|
],
|
|
363
199
|
};
|
|
364
200
|
const keyId = (key) => bytesKey(serialize(key));
|
|
365
|
-
const descriptorDigest = (descriptor) => sha256Sync(concat([DESCRIPTOR_DIGEST_DOMAIN, serialize(descriptor)]));
|
|
366
|
-
const generationChecksum = (body) => sha256Sync(concat([GENERATION_CHECKSUM_DOMAIN, serialize(body)]));
|
|
367
201
|
const observationContentHash = (entryBytes) => sha256Sync(entryBytes);
|
|
368
202
|
const u32Bytes = (value) => {
|
|
369
203
|
const bytes = new Uint8Array(4);
|
|
370
204
|
new DataView(bytes.buffer).setUint32(0, value, false);
|
|
371
205
|
return bytes;
|
|
372
206
|
};
|
|
373
|
-
const
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
? compare(left.entryBytes, right.entryBytes)
|
|
382
|
-
: hashOrder;
|
|
383
|
-
})
|
|
384
|
-
.map(({ entryBytes }) => entryBytes);
|
|
385
|
-
const forkObservationCommitment = (descriptorHash, canonicalEntries) => sha256Sync(concat([
|
|
386
|
-
FORK_OBSERVATION_COMMITMENT_DOMAIN,
|
|
387
|
-
descriptorHash,
|
|
388
|
-
Uint8Array.of(canonicalEntries.length),
|
|
389
|
-
...canonicalEntries.flatMap((entryBytes) => [
|
|
390
|
-
u32Bytes(entryBytes.byteLength),
|
|
391
|
-
entryBytes,
|
|
392
|
-
]),
|
|
393
|
-
]));
|
|
394
|
-
const generationKey = (generation) => `${GENERATION_KEY_PREFIX}${generation.toString().padStart(20, "0")}`;
|
|
207
|
+
const checkpointScope = (descriptor) => {
|
|
208
|
+
const descriptorBytes = serialize(descriptor);
|
|
209
|
+
return concat([
|
|
210
|
+
CHECKPOINT_SCOPE_DOMAIN,
|
|
211
|
+
u32Bytes(descriptorBytes.byteLength),
|
|
212
|
+
descriptorBytes,
|
|
213
|
+
]);
|
|
214
|
+
};
|
|
395
215
|
const assertOpenNotAborted = (signal) => {
|
|
396
216
|
if (signal?.aborted) {
|
|
397
217
|
throw new Error("TrustedNetwork v2 durable policy open was aborted");
|
|
@@ -410,6 +230,37 @@ const assertEntryBytes = (bytes, label) => {
|
|
|
410
230
|
throw new Error(`${label} must contain 1-${TRUSTED_NETWORK_V2_MAX_POLICY_ENTRY_BYTES} bytes`);
|
|
411
231
|
}
|
|
412
232
|
};
|
|
233
|
+
const canonicalForkObservationEntries = (entries) => {
|
|
234
|
+
const observed = new Map();
|
|
235
|
+
const hashed = [];
|
|
236
|
+
for (const entryBytes of entries) {
|
|
237
|
+
assertEntryBytes(entryBytes, "fork observation entry");
|
|
238
|
+
const hash = observationContentHash(entryBytes);
|
|
239
|
+
const hashKey = bytesKey(hash);
|
|
240
|
+
const existing = observed.get(hashKey);
|
|
241
|
+
if (existing !== undefined) {
|
|
242
|
+
if (equals(existing, entryBytes)) {
|
|
243
|
+
throw new Error("Duplicate policy fork observation");
|
|
244
|
+
}
|
|
245
|
+
throw new Error("Policy fork-observation content hash collision");
|
|
246
|
+
}
|
|
247
|
+
observed.set(hashKey, entryBytes);
|
|
248
|
+
hashed.push({ entryBytes, hash });
|
|
249
|
+
}
|
|
250
|
+
return hashed
|
|
251
|
+
.sort((left, right) => {
|
|
252
|
+
const hashOrder = compare(left.hash, right.hash);
|
|
253
|
+
return hashOrder === 0
|
|
254
|
+
? compare(left.entryBytes, right.entryBytes)
|
|
255
|
+
: hashOrder;
|
|
256
|
+
})
|
|
257
|
+
.map(({ entryBytes }) => entryBytes);
|
|
258
|
+
};
|
|
259
|
+
const assertEmptyBytes = (bytes, label) => {
|
|
260
|
+
if (!hasExactUint8ArrayByteLength(bytes, 0)) {
|
|
261
|
+
throw new Error(`${label} must be empty`);
|
|
262
|
+
}
|
|
263
|
+
};
|
|
413
264
|
const retainCanonicalForkChild = (children, candidate) => {
|
|
414
265
|
const existingIndex = children.findIndex(({ digest }) => equals(digest, candidate.digest));
|
|
415
266
|
if (existingIndex >= 0) {
|
|
@@ -431,173 +282,161 @@ const retainCanonicalForkChild = (children, candidate) => {
|
|
|
431
282
|
if (children.length > 2)
|
|
432
283
|
children.length = 2;
|
|
433
284
|
};
|
|
434
|
-
const
|
|
435
|
-
|
|
436
|
-
|
|
285
|
+
const assertCanonicalObservationOrder = (entries) => {
|
|
286
|
+
const canonical = canonicalForkObservationEntries(entries);
|
|
287
|
+
if (canonical.length !== entries.length ||
|
|
288
|
+
canonical.some((entryBytes, index) => !equals(entryBytes, entries[index]))) {
|
|
289
|
+
throw new Error("Policy fork observations are not in canonical order");
|
|
437
290
|
}
|
|
438
291
|
};
|
|
439
|
-
const
|
|
292
|
+
const checkpointPayloadFromState = (state, forkObservationEntryBytes = []) => {
|
|
440
293
|
if (state.state === "ACTIVE") {
|
|
441
|
-
return new
|
|
294
|
+
return new PolicyAnchorCheckpointPayloadV2({
|
|
442
295
|
state: ANCHOR_STATE.ACTIVE,
|
|
443
296
|
acceptedHeadEntryBytes: copyBytes(state.acceptedHeadEntryBytes),
|
|
444
297
|
comparisonCandidateEntryBytes: new Uint8Array(0),
|
|
445
298
|
acceptedAncestorDigest: copyBytes(ZERO_DIGEST),
|
|
446
299
|
unavailableReason: "",
|
|
447
|
-
|
|
448
|
-
forkChildEntryBytes1: new Uint8Array(0),
|
|
300
|
+
forkObservationEntryBytes: [],
|
|
449
301
|
});
|
|
450
302
|
}
|
|
451
303
|
if (state.state === "UNAVAILABLE") {
|
|
452
|
-
return new
|
|
304
|
+
return new PolicyAnchorCheckpointPayloadV2({
|
|
453
305
|
state: ANCHOR_STATE.UNAVAILABLE,
|
|
454
306
|
acceptedHeadEntryBytes: copyBytes(state.acceptedHeadEntryBytes),
|
|
455
307
|
comparisonCandidateEntryBytes: copyBytes(state.comparisonCandidateEntryBytes),
|
|
456
308
|
acceptedAncestorDigest: copyBytes(state.acceptedAncestorDigest),
|
|
457
309
|
unavailableReason: state.reason,
|
|
458
|
-
|
|
459
|
-
forkChildEntryBytes1: new Uint8Array(0),
|
|
310
|
+
forkObservationEntryBytes: [],
|
|
460
311
|
});
|
|
461
312
|
}
|
|
462
|
-
return new
|
|
313
|
+
return new PolicyAnchorCheckpointPayloadV2({
|
|
463
314
|
state: ANCHOR_STATE.FORKED,
|
|
464
315
|
acceptedHeadEntryBytes: copyBytes(state.commonParentEntryBytes),
|
|
465
316
|
comparisonCandidateEntryBytes: new Uint8Array(0),
|
|
466
317
|
acceptedAncestorDigest: copyBytes(ZERO_DIGEST),
|
|
467
318
|
unavailableReason: "",
|
|
468
|
-
|
|
469
|
-
forkChildEntryBytes1: copyBytes(state.childEntryBytes[1]),
|
|
319
|
+
forkObservationEntryBytes: forkObservationEntryBytes.map(copyBytes),
|
|
470
320
|
});
|
|
471
321
|
};
|
|
472
|
-
const
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
322
|
+
const coreIdentityBytesFromState = (state) =>
|
|
323
|
+
// Fork observations are deliberately excluded from this transient identity.
|
|
324
|
+
// A FORKED identity is only compared with the preceding non-forked snapshot;
|
|
325
|
+
// after publication, the wrapper admits no more work and retains no copy.
|
|
326
|
+
serialize(checkpointPayloadFromState(state));
|
|
327
|
+
const decodeCanonicalCheckpointPayload = (input) => {
|
|
328
|
+
let byteLength;
|
|
329
|
+
try {
|
|
330
|
+
byteLength = exactUint8ArrayByteLength(input);
|
|
331
|
+
}
|
|
332
|
+
catch {
|
|
333
|
+
byteLength = -1;
|
|
334
|
+
}
|
|
335
|
+
if (byteLength < 1 ||
|
|
336
|
+
byteLength > TRUSTED_NETWORK_V2_MAX_POLICY_ANCHOR_CHECKPOINT_PAYLOAD_BYTES) {
|
|
337
|
+
throw new Error("Policy-anchor checkpoint payload exceeds its byte ceiling");
|
|
338
|
+
}
|
|
339
|
+
// CrashSafeTwoSlotCheckpoint.current already returns a fresh genuine exact
|
|
340
|
+
// copy, so retaining that input while deserializing avoids another full-size
|
|
341
|
+
// restore copy without exposing caller-owned bytes.
|
|
342
|
+
const payload = deserialize(input, PolicyAnchorCheckpointPayloadV2);
|
|
343
|
+
if (!equals(input, serialize(payload))) {
|
|
344
|
+
throw new Error("Policy-anchor checkpoint payload is not canonical");
|
|
345
|
+
}
|
|
346
|
+
return payload;
|
|
347
|
+
};
|
|
348
|
+
const durableStateFromCheckpointPayload = async (payload, descriptor, signal) => {
|
|
349
|
+
assertEntryBytes(payload.acceptedHeadEntryBytes, "accepted head entry");
|
|
350
|
+
if (payload.state === ANCHOR_STATE.ACTIVE) {
|
|
351
|
+
assertEmptyBytes(payload.comparisonCandidateEntryBytes, "active comparison candidate");
|
|
352
|
+
if (!equals(payload.acceptedAncestorDigest, ZERO_DIGEST)) {
|
|
477
353
|
throw new Error("Active accepted-ancestor digest must be zero");
|
|
478
354
|
}
|
|
479
|
-
if (
|
|
355
|
+
if (payload.unavailableReason !== "") {
|
|
480
356
|
throw new Error("Active unavailable reason must be empty");
|
|
481
357
|
}
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
358
|
+
if (payload.forkObservationEntryBytes.length !== 0) {
|
|
359
|
+
throw new Error("Active policy-anchor state must not contain fork evidence");
|
|
360
|
+
}
|
|
361
|
+
const durableState = {
|
|
485
362
|
formatVersion: REDUCER_DURABLE_FORMAT_VERSION,
|
|
486
363
|
state: "ACTIVE",
|
|
487
|
-
acceptedHeadEntryBytes: copyBytes(
|
|
364
|
+
acceptedHeadEntryBytes: copyBytes(payload.acceptedHeadEntryBytes),
|
|
365
|
+
};
|
|
366
|
+
return {
|
|
367
|
+
durableState,
|
|
368
|
+
coreIdentityBytes: coreIdentityBytesFromState(durableState),
|
|
488
369
|
};
|
|
489
370
|
}
|
|
490
|
-
if (
|
|
491
|
-
assertEntryBytes(
|
|
492
|
-
if (!hasExactUint8ArrayByteLength(
|
|
371
|
+
if (payload.state === ANCHOR_STATE.UNAVAILABLE) {
|
|
372
|
+
assertEntryBytes(payload.comparisonCandidateEntryBytes, "unavailable comparison candidate");
|
|
373
|
+
if (!hasExactUint8ArrayByteLength(payload.acceptedAncestorDigest, 32)) {
|
|
493
374
|
throw new Error("Unavailable accepted-ancestor digest must be 32 bytes");
|
|
494
375
|
}
|
|
495
|
-
if (
|
|
496
|
-
|
|
376
|
+
if (payload.unavailableReason.length === 0 ||
|
|
377
|
+
payload.unavailableReason.length > MAX_UNAVAILABLE_REASON_LENGTH) {
|
|
497
378
|
throw new Error("Unavailable reason is empty or exceeds its character ceiling");
|
|
498
379
|
}
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
380
|
+
if (payload.forkObservationEntryBytes.length !== 0) {
|
|
381
|
+
throw new Error("Unavailable policy-anchor state must not contain fork evidence");
|
|
382
|
+
}
|
|
383
|
+
const durableState = {
|
|
502
384
|
formatVersion: REDUCER_DURABLE_FORMAT_VERSION,
|
|
503
385
|
state: "UNAVAILABLE",
|
|
504
|
-
acceptedHeadEntryBytes: copyBytes(
|
|
505
|
-
comparisonCandidateEntryBytes: copyBytes(
|
|
506
|
-
acceptedAncestorDigest: copyBytes(
|
|
507
|
-
reason:
|
|
386
|
+
acceptedHeadEntryBytes: copyBytes(payload.acceptedHeadEntryBytes),
|
|
387
|
+
comparisonCandidateEntryBytes: copyBytes(payload.comparisonCandidateEntryBytes),
|
|
388
|
+
acceptedAncestorDigest: copyBytes(payload.acceptedAncestorDigest),
|
|
389
|
+
reason: payload.unavailableReason,
|
|
508
390
|
};
|
|
509
|
-
}
|
|
510
|
-
if (record.state === ANCHOR_STATE.FORKED) {
|
|
511
|
-
assertEmptyBytes(record.comparisonCandidateEntryBytes, "forked comparison candidate");
|
|
512
|
-
if (!equals(record.acceptedAncestorDigest, ZERO_DIGEST)) {
|
|
513
|
-
throw new Error("Forked accepted-ancestor digest must be zero");
|
|
514
|
-
}
|
|
515
|
-
if (record.unavailableReason !== "") {
|
|
516
|
-
throw new Error("Forked unavailable reason must be empty");
|
|
517
|
-
}
|
|
518
|
-
assertEntryBytes(record.forkChildEntryBytes0, "fork child 0");
|
|
519
|
-
assertEntryBytes(record.forkChildEntryBytes1, "fork child 1");
|
|
520
391
|
return {
|
|
521
|
-
formatVersion: REDUCER_DURABLE_FORMAT_VERSION,
|
|
522
|
-
state: "FORKED",
|
|
523
|
-
commonParentEntryBytes: copyBytes(record.acceptedHeadEntryBytes),
|
|
524
|
-
childEntryBytes: [
|
|
525
|
-
copyBytes(record.forkChildEntryBytes0),
|
|
526
|
-
copyBytes(record.forkChildEntryBytes1),
|
|
527
|
-
],
|
|
528
|
-
};
|
|
529
|
-
}
|
|
530
|
-
throw new Error("Unknown durable policy-anchor state");
|
|
531
|
-
};
|
|
532
|
-
const decodeCanonicalPayload = (input, type, maxBytes, label) => {
|
|
533
|
-
let byteLength;
|
|
534
|
-
try {
|
|
535
|
-
byteLength = exactUint8ArrayByteLength(input);
|
|
536
|
-
}
|
|
537
|
-
catch {
|
|
538
|
-
byteLength = -1;
|
|
539
|
-
}
|
|
540
|
-
if (byteLength < 1 || byteLength > maxBytes) {
|
|
541
|
-
throw new Error(`${label} exceeds its byte ceiling`);
|
|
542
|
-
}
|
|
543
|
-
const bytes = copyBytesWithLength(input, byteLength);
|
|
544
|
-
const payload = deserialize(bytes, type);
|
|
545
|
-
if (!equals(bytes, serialize(payload))) {
|
|
546
|
-
throw new Error(`${label} is not canonical`);
|
|
547
|
-
}
|
|
548
|
-
return payload;
|
|
549
|
-
};
|
|
550
|
-
const decodeGenerationPayload = (body) => {
|
|
551
|
-
if (body.kind === GENERATION_KIND.STATE) {
|
|
552
|
-
const payload = decodeCanonicalPayload(body.payloadBytes, PolicyAnchorStateGenerationPayloadV2, MAX_STATE_GENERATION_PAYLOAD_BYTES, "Policy-anchor state payload");
|
|
553
|
-
const durableState = durableStateFromCoreRecord(payload.coreState);
|
|
554
|
-
if (durableState.state === "FORKED") {
|
|
555
|
-
if (payload.forkObservationCount < 2 ||
|
|
556
|
-
payload.forkObservationCount > MAX_FORK_OBSERVATIONS_V2) {
|
|
557
|
-
throw new Error(`Forked policy-anchor state must commit to 2-${MAX_FORK_OBSERVATIONS_V2} observations`);
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
else if (payload.forkObservationCount !== 0 ||
|
|
561
|
-
!equals(payload.forkObservationCommitment, ZERO_DIGEST)) {
|
|
562
|
-
throw new Error("Non-forked policy-anchor state must not contain a fork commitment");
|
|
563
|
-
}
|
|
564
|
-
return {
|
|
565
|
-
kind: "state",
|
|
566
|
-
payload,
|
|
567
392
|
durableState,
|
|
393
|
+
coreIdentityBytes: coreIdentityBytesFromState(durableState),
|
|
568
394
|
};
|
|
569
395
|
}
|
|
570
|
-
if (
|
|
571
|
-
|
|
572
|
-
assertEntryBytes(payload.entryBytes, "fork observation entry");
|
|
573
|
-
return { kind: "fork-observation", payload };
|
|
396
|
+
if (payload.state !== ANCHOR_STATE.FORKED) {
|
|
397
|
+
throw new Error("Unknown durable policy-anchor state");
|
|
574
398
|
}
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
let byteLength;
|
|
579
|
-
try {
|
|
580
|
-
byteLength = exactUint8ArrayByteLength(input);
|
|
581
|
-
}
|
|
582
|
-
catch {
|
|
583
|
-
byteLength = -1;
|
|
399
|
+
assertEmptyBytes(payload.comparisonCandidateEntryBytes, "forked comparison candidate");
|
|
400
|
+
if (!equals(payload.acceptedAncestorDigest, ZERO_DIGEST)) {
|
|
401
|
+
throw new Error("Forked accepted-ancestor digest must be zero");
|
|
584
402
|
}
|
|
585
|
-
if (
|
|
586
|
-
|
|
587
|
-
throw new Error("Policy-anchor generation record exceeds its byte ceiling");
|
|
403
|
+
if (payload.unavailableReason !== "") {
|
|
404
|
+
throw new Error("Forked unavailable reason must be empty");
|
|
588
405
|
}
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
throw new Error("Policy-anchor generation record is not canonical");
|
|
406
|
+
if (payload.forkObservationEntryBytes.length < 2 ||
|
|
407
|
+
payload.forkObservationEntryBytes.length > MAX_FORK_OBSERVATIONS_V2) {
|
|
408
|
+
throw new Error(`Forked policy-anchor state must contain 2-${MAX_FORK_OBSERVATIONS_V2} observations`);
|
|
593
409
|
}
|
|
594
|
-
|
|
595
|
-
|
|
410
|
+
assertCanonicalObservationOrder(payload.forkObservationEntryBytes);
|
|
411
|
+
const commonParent = await authenticatePolicySnapshotEntryV2(payload.acceptedHeadEntryBytes, descriptor);
|
|
412
|
+
assertOpenNotAborted(signal);
|
|
413
|
+
const canonicalChildren = [];
|
|
414
|
+
for (const entryBytes of payload.forkObservationEntryBytes) {
|
|
415
|
+
const authenticated = await authenticatePolicySnapshotEntryV2(entryBytes, descriptor);
|
|
416
|
+
assertOpenNotAborted(signal);
|
|
417
|
+
if (authenticated.body.sequence !== commonParent.body.sequence + 1n ||
|
|
418
|
+
!equals(authenticated.body.previousPolicyDigest, commonParent.digest)) {
|
|
419
|
+
throw new Error("Stored policy fork observation is not a direct child of the common parent");
|
|
420
|
+
}
|
|
421
|
+
retainCanonicalForkChild(canonicalChildren, {
|
|
422
|
+
digest: authenticated.digest,
|
|
423
|
+
entryBytes,
|
|
424
|
+
});
|
|
596
425
|
}
|
|
597
|
-
if (
|
|
598
|
-
throw new Error("
|
|
426
|
+
if (canonicalChildren.length !== 2) {
|
|
427
|
+
throw new Error("Stored policy fork evidence has fewer than two distinct children");
|
|
599
428
|
}
|
|
600
|
-
return {
|
|
429
|
+
return {
|
|
430
|
+
durableState: {
|
|
431
|
+
formatVersion: REDUCER_DURABLE_FORMAT_VERSION,
|
|
432
|
+
state: "FORKED",
|
|
433
|
+
commonParentEntryBytes: copyBytes(payload.acceptedHeadEntryBytes),
|
|
434
|
+
childEntryBytes: [
|
|
435
|
+
copyBytes(canonicalChildren[0].entryBytes),
|
|
436
|
+
copyBytes(canonicalChildren[1].entryBytes),
|
|
437
|
+
],
|
|
438
|
+
},
|
|
439
|
+
};
|
|
601
440
|
};
|
|
602
441
|
const publishedFromReducer = (reducer) => {
|
|
603
442
|
if (reducer.state === "HALTED") {
|
|
@@ -615,51 +454,95 @@ const publishedFromReducer = (reducer) => {
|
|
|
615
454
|
roles,
|
|
616
455
|
};
|
|
617
456
|
};
|
|
618
|
-
const assertCanonicalCoreRestore = (reducer,
|
|
457
|
+
const assertCanonicalCoreRestore = (reducer, expectedState) => {
|
|
619
458
|
const restoredState = reducer.exportDurableState();
|
|
620
|
-
if (restoredState.state === "EMPTY"
|
|
459
|
+
if (restoredState.state === "EMPTY" ||
|
|
460
|
+
restoredState.state !== expectedState.state) {
|
|
621
461
|
throw new Error("Restored policy-anchor state is unexpectedly empty");
|
|
622
462
|
}
|
|
623
|
-
|
|
624
|
-
if (
|
|
463
|
+
let exact = false;
|
|
464
|
+
if (restoredState.state === "ACTIVE" && expectedState.state === "ACTIVE") {
|
|
465
|
+
exact = equals(restoredState.acceptedHeadEntryBytes, expectedState.acceptedHeadEntryBytes);
|
|
466
|
+
}
|
|
467
|
+
else if (restoredState.state === "UNAVAILABLE" &&
|
|
468
|
+
expectedState.state === "UNAVAILABLE") {
|
|
469
|
+
exact =
|
|
470
|
+
equals(restoredState.acceptedHeadEntryBytes, expectedState.acceptedHeadEntryBytes) &&
|
|
471
|
+
equals(restoredState.comparisonCandidateEntryBytes, expectedState.comparisonCandidateEntryBytes) &&
|
|
472
|
+
equals(restoredState.acceptedAncestorDigest, expectedState.acceptedAncestorDigest) &&
|
|
473
|
+
restoredState.reason === expectedState.reason;
|
|
474
|
+
}
|
|
475
|
+
else if (restoredState.state === "FORKED" &&
|
|
476
|
+
expectedState.state === "FORKED") {
|
|
477
|
+
exact =
|
|
478
|
+
equals(restoredState.commonParentEntryBytes, expectedState.commonParentEntryBytes) &&
|
|
479
|
+
equals(restoredState.childEntryBytes[0], expectedState.childEntryBytes[0]) &&
|
|
480
|
+
equals(restoredState.childEntryBytes[1], expectedState.childEntryBytes[1]);
|
|
481
|
+
}
|
|
482
|
+
if (!exact) {
|
|
625
483
|
throw new Error("Restored policy-anchor state is not canonical");
|
|
626
484
|
}
|
|
627
485
|
};
|
|
486
|
+
const canonicalForkEntriesForCommit = (result, durableState, forkEvidence) => {
|
|
487
|
+
if (forkEvidence === undefined) {
|
|
488
|
+
throw new Error("Forked policy reducer has no fork evidence");
|
|
489
|
+
}
|
|
490
|
+
const observations = result.forkObservations ?? [];
|
|
491
|
+
if (observations.length < 2 ||
|
|
492
|
+
observations.length > MAX_FORK_OBSERVATIONS_V2) {
|
|
493
|
+
throw new Error(`A durable fork transition must contain 2-${MAX_FORK_OBSERVATIONS_V2} observations`);
|
|
494
|
+
}
|
|
495
|
+
const observationEntries = [];
|
|
496
|
+
const canonicalChildren = [];
|
|
497
|
+
for (const proof of observations) {
|
|
498
|
+
assertEntryBytes(proof.entryBytes, "fork observation entry");
|
|
499
|
+
if (!hasExactUint8ArrayByteLength(proof.digest, 32)) {
|
|
500
|
+
throw new Error("Policy fork-observation digest must be 32 bytes");
|
|
501
|
+
}
|
|
502
|
+
if (proof.sequence !== forkEvidence.commonParent.sequence + 1n) {
|
|
503
|
+
throw new Error("Policy fork observation is not a direct child of the common parent");
|
|
504
|
+
}
|
|
505
|
+
// The reducer result owns this genuine exact copy and is not exposed until
|
|
506
|
+
// publication settles, so retaining it through this synchronous validation
|
|
507
|
+
// avoids another full fork-evidence copy.
|
|
508
|
+
observationEntries.push(proof.entryBytes);
|
|
509
|
+
retainCanonicalForkChild(canonicalChildren, proof);
|
|
510
|
+
}
|
|
511
|
+
if (canonicalChildren.length !== 2) {
|
|
512
|
+
throw new Error("A durable fork transition requires two distinct policy children");
|
|
513
|
+
}
|
|
514
|
+
for (let index = 0; index < canonicalChildren.length; index++) {
|
|
515
|
+
if (!equals(canonicalChildren[index].entryBytes, durableState.childEntryBytes[index])) {
|
|
516
|
+
throw new Error("Published fork state does not contain the lowest canonical children");
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
return canonicalForkObservationEntries(observationEntries);
|
|
520
|
+
};
|
|
628
521
|
/**
|
|
629
522
|
* Crash-safe publication wrapper for the internal v2 reducer.
|
|
630
523
|
*
|
|
631
524
|
* The mutable reducer is never exposed. Authorization reads use only the last
|
|
632
|
-
* projection published after
|
|
633
|
-
* fail-closed fence around both reducer mutation and
|
|
525
|
+
* projection published after one atomic checkpoint replacement. An admission
|
|
526
|
+
* places a fail-closed fence around both reducer mutation and publication.
|
|
634
527
|
*/
|
|
635
528
|
export class TrustedNetworkV2DurablePolicyReducer {
|
|
636
|
-
|
|
637
|
-
durability;
|
|
638
|
-
descriptor;
|
|
639
|
-
descriptorHash;
|
|
529
|
+
checkpoint;
|
|
640
530
|
core;
|
|
641
531
|
published;
|
|
642
|
-
|
|
643
|
-
previousGenerationChecksum = copyBytes(ZERO_DIGEST);
|
|
644
|
-
durableCoreBytes;
|
|
532
|
+
durableCoreIdentityBytes;
|
|
645
533
|
operationTail = Promise.resolve();
|
|
646
534
|
authorizationFences = 0;
|
|
647
535
|
bufferedAdmissions = 0;
|
|
648
536
|
bufferedAdmissionEntryBytes = 0;
|
|
649
537
|
terminalError;
|
|
650
538
|
constructor(properties) {
|
|
651
|
-
this.
|
|
652
|
-
this.durability = properties.durability;
|
|
653
|
-
this.descriptor = deserialize(serialize(properties.descriptor), NetworkDescriptorV2);
|
|
654
|
-
this.descriptorHash = descriptorDigest(this.descriptor);
|
|
539
|
+
this.checkpoint = properties.checkpoint;
|
|
655
540
|
this.core = properties.core;
|
|
656
541
|
this.published = publishedFromReducer(this.core);
|
|
657
|
-
this.
|
|
658
|
-
|
|
659
|
-
this.durableCoreBytes =
|
|
660
|
-
properties.durableCoreBytes === undefined
|
|
542
|
+
this.durableCoreIdentityBytes =
|
|
543
|
+
properties.durableCoreIdentityBytes === undefined
|
|
661
544
|
? undefined
|
|
662
|
-
: copyBytes(properties.
|
|
545
|
+
: copyBytes(properties.durableCoreIdentityBytes);
|
|
663
546
|
}
|
|
664
547
|
static async open(options) {
|
|
665
548
|
assertNetworkDescriptorV2(options.descriptor);
|
|
@@ -675,214 +558,49 @@ export class TrustedNetworkV2DurablePolicyReducer {
|
|
|
675
558
|
maxPendingPolicyBytes: options.maxPendingPolicyBytes,
|
|
676
559
|
};
|
|
677
560
|
assertOpenNotAborted(signal);
|
|
678
|
-
const
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
}
|
|
683
|
-
// A successful fresh barrier is required on every open, including an empty
|
|
684
|
-
// store; a generic flush acknowledgement is not this physical fence.
|
|
685
|
-
await durability.barrier();
|
|
561
|
+
const checkpoint = await CrashSafeTwoSlotCheckpoint.open({
|
|
562
|
+
store,
|
|
563
|
+
scope: checkpointScope(descriptor),
|
|
564
|
+
maxPayloadBytes: TRUSTED_NETWORK_V2_MAX_POLICY_ANCHOR_CHECKPOINT_PAYLOAD_BYTES,
|
|
565
|
+
});
|
|
686
566
|
assertOpenNotAborted(signal);
|
|
687
|
-
|
|
688
|
-
//
|
|
689
|
-
//
|
|
690
|
-
|
|
691
|
-
const generationKeys = [];
|
|
692
|
-
for await (const [key, input] of store.iterator()) {
|
|
567
|
+
// Never let the old append-only format look like an empty or newer
|
|
568
|
+
// checkpoint. It was internal and never activated, so migration or fallback
|
|
569
|
+
// would add rollback surface without preserving a public compatibility need.
|
|
570
|
+
for await (const [key] of store.iterator()) {
|
|
693
571
|
assertOpenNotAborted(signal);
|
|
694
|
-
if (key.startsWith(GENERATION_KEY_PREFIX)) {
|
|
695
|
-
const suffix = key.slice(GENERATION_KEY_PREFIX.length);
|
|
696
|
-
if (!/^\d{20}$/.test(suffix)) {
|
|
697
|
-
throw new Error("Malformed policy-anchor generation key");
|
|
698
|
-
}
|
|
699
|
-
let byteLength;
|
|
700
|
-
try {
|
|
701
|
-
byteLength = exactUint8ArrayByteLength(input);
|
|
702
|
-
}
|
|
703
|
-
catch {
|
|
704
|
-
byteLength = -1;
|
|
705
|
-
}
|
|
706
|
-
if (byteLength < 1 ||
|
|
707
|
-
byteLength > TRUSTED_NETWORK_V2_MAX_POLICY_ANCHOR_GENERATION_BYTES) {
|
|
708
|
-
throw new Error("Policy-anchor generation record exceeds its byte ceiling");
|
|
709
|
-
}
|
|
710
|
-
const generation = BigInt(suffix);
|
|
711
|
-
if (generation === 0n || generation > MAX_U64) {
|
|
712
|
-
throw new Error("Policy-anchor generation key is outside u64");
|
|
713
|
-
}
|
|
714
|
-
generationKeys.push(key);
|
|
715
|
-
continue;
|
|
716
|
-
}
|
|
717
572
|
if (key === TRUSTED_NETWORK_V2_POLICY_ANCHOR_STORE_OWNER ||
|
|
718
573
|
key.startsWith(`${TRUSTED_NETWORK_V2_POLICY_ANCHOR_STORE_OWNER}/`)) {
|
|
719
|
-
throw new Error("
|
|
574
|
+
throw new Error("Legacy append-only policy-anchor records are not supported; reset the dedicated store");
|
|
720
575
|
}
|
|
721
576
|
}
|
|
722
577
|
assertOpenNotAborted(signal);
|
|
723
|
-
|
|
724
|
-
let previousChecksum = copyBytes(ZERO_DIGEST);
|
|
725
|
-
let latestCoreBytes;
|
|
726
|
-
let latestDurableState;
|
|
578
|
+
const current = checkpoint.current;
|
|
727
579
|
let core;
|
|
728
|
-
let durableCoreBytes;
|
|
729
|
-
let forkEvidence;
|
|
730
|
-
let expectedForkObservationCount;
|
|
731
|
-
let expectedForkObservationCommitment;
|
|
732
|
-
const observedEntries = new Map();
|
|
733
|
-
const canonicalChildren = [];
|
|
734
580
|
try {
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
throw new Error("Policy-anchor generation history is gapped");
|
|
742
|
-
}
|
|
743
|
-
const input = await store.get(key);
|
|
744
|
-
assertOpenNotAborted(signal);
|
|
745
|
-
if (input === undefined) {
|
|
746
|
-
throw new Error("Policy-anchor generation disappeared during restore");
|
|
747
|
-
}
|
|
748
|
-
const { record, payload } = decodeGenerationRecord(input);
|
|
749
|
-
if (record.body.generation !== keyedGeneration ||
|
|
750
|
-
generationKey(record.body.generation) !== key) {
|
|
751
|
-
throw new Error("Policy-anchor generation key does not match record");
|
|
752
|
-
}
|
|
753
|
-
if (!equals(record.body.descriptorDigest, expectedDescriptorHash)) {
|
|
754
|
-
throw new Error("Policy-anchor generation belongs to another descriptor");
|
|
755
|
-
}
|
|
756
|
-
if (!equals(record.body.previousGenerationChecksum, previousChecksum)) {
|
|
757
|
-
throw new Error("Policy-anchor generation checksum chain is broken");
|
|
758
|
-
}
|
|
759
|
-
if (payload.kind === "state") {
|
|
760
|
-
if (latestDurableState?.state === "FORKED") {
|
|
761
|
-
throw new Error("A FORKED policy anchor may only append observation deltas");
|
|
762
|
-
}
|
|
763
|
-
latestCoreBytes = serialize(payload.payload.coreState);
|
|
764
|
-
latestDurableState = payload.durableState;
|
|
765
|
-
if (latestDurableState.state === "FORKED") {
|
|
766
|
-
expectedForkObservationCount = payload.payload.forkObservationCount;
|
|
767
|
-
expectedForkObservationCommitment = copyBytes(payload.payload.forkObservationCommitment);
|
|
768
|
-
core = await TrustedNetworkV2PolicyReducer.restore({
|
|
769
|
-
...coreProperties,
|
|
770
|
-
durableState: latestDurableState,
|
|
771
|
-
});
|
|
772
|
-
assertOpenNotAborted(signal);
|
|
773
|
-
assertCanonicalCoreRestore(core, latestCoreBytes);
|
|
774
|
-
durableCoreBytes = copyBytes(latestCoreBytes);
|
|
775
|
-
forkEvidence = core.forkEvidence;
|
|
776
|
-
if (forkEvidence === undefined) {
|
|
777
|
-
throw new Error("Restored forked policy anchor has no fork evidence");
|
|
778
|
-
}
|
|
779
|
-
for (const child of forkEvidence.children) {
|
|
780
|
-
const hashKey = bytesKey(observationContentHash(child.entryBytes));
|
|
781
|
-
if (observedEntries.has(hashKey)) {
|
|
782
|
-
throw new Error("Duplicate policy fork observation in durable state");
|
|
783
|
-
}
|
|
784
|
-
observedEntries.set(hashKey, copyBytes(child.entryBytes));
|
|
785
|
-
retainCanonicalForkChild(canonicalChildren, child);
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
|
-
}
|
|
789
|
-
else {
|
|
790
|
-
if (latestDurableState?.state !== "FORKED" ||
|
|
791
|
-
core === undefined ||
|
|
792
|
-
forkEvidence === undefined) {
|
|
793
|
-
throw new Error("Policy fork-observation delta requires a preceding FORKED state");
|
|
794
|
-
}
|
|
795
|
-
const entryBytes = payload.payload.entryBytes;
|
|
796
|
-
const hashKey = bytesKey(observationContentHash(entryBytes));
|
|
797
|
-
if (observedEntries.has(hashKey)) {
|
|
798
|
-
throw new Error("Duplicate policy fork-observation generation");
|
|
799
|
-
}
|
|
800
|
-
if (observedEntries.size >= MAX_FORK_OBSERVATIONS_V2) {
|
|
801
|
-
throw new Error(`Policy fork evidence exceeds ${MAX_FORK_OBSERVATIONS_V2} children`);
|
|
802
|
-
}
|
|
803
|
-
const authenticated = await authenticatePolicySnapshotEntryV2(entryBytes, descriptor);
|
|
804
|
-
assertOpenNotAborted(signal);
|
|
805
|
-
if (authenticated.body.sequence !==
|
|
806
|
-
forkEvidence.commonParent.sequence + 1n ||
|
|
807
|
-
!equals(authenticated.body.previousPolicyDigest, forkEvidence.commonParent.digest)) {
|
|
808
|
-
throw new Error("Stored policy fork observation is not a direct child of the common parent");
|
|
809
|
-
}
|
|
810
|
-
observedEntries.set(hashKey, copyBytes(entryBytes));
|
|
811
|
-
retainCanonicalForkChild(canonicalChildren, {
|
|
812
|
-
digest: authenticated.digest,
|
|
813
|
-
entryBytes,
|
|
814
|
-
});
|
|
815
|
-
}
|
|
816
|
-
previousChecksum = copyBytes(record.checksum);
|
|
817
|
-
}
|
|
818
|
-
const highestGeneration = generationKeys.length === 0 ? undefined : BigInt(generationKeys.length);
|
|
819
|
-
if (highestGeneration !== undefined && latestCoreBytes === undefined) {
|
|
820
|
-
throw new Error("Policy-anchor history has no durable state generation");
|
|
821
|
-
}
|
|
822
|
-
if (core === undefined) {
|
|
823
|
-
if (highestGeneration === undefined) {
|
|
824
|
-
core = new TrustedNetworkV2PolicyReducer(coreProperties);
|
|
825
|
-
}
|
|
826
|
-
else {
|
|
827
|
-
core = await TrustedNetworkV2PolicyReducer.restore({
|
|
828
|
-
...coreProperties,
|
|
829
|
-
durableState: latestDurableState,
|
|
830
|
-
});
|
|
831
|
-
assertOpenNotAborted(signal);
|
|
832
|
-
assertCanonicalCoreRestore(core, latestCoreBytes);
|
|
833
|
-
durableCoreBytes = copyBytes(latestCoreBytes);
|
|
834
|
-
}
|
|
835
|
-
}
|
|
836
|
-
if (latestDurableState?.state === "FORKED") {
|
|
837
|
-
if (forkEvidence === undefined ||
|
|
838
|
-
expectedForkObservationCount === undefined ||
|
|
839
|
-
expectedForkObservationCommitment === undefined) {
|
|
840
|
-
throw new Error("Restored forked policy anchor has no fork evidence");
|
|
841
|
-
}
|
|
842
|
-
if (observedEntries.size !== expectedForkObservationCount) {
|
|
843
|
-
throw new Error(`Policy fork evidence is incomplete: expected ${expectedForkObservationCount}, restored ${observedEntries.size}`);
|
|
844
|
-
}
|
|
845
|
-
const restoredCommitment = forkObservationCommitment(expectedDescriptorHash, canonicalForkObservationEntries(observedEntries.values()));
|
|
846
|
-
if (!equals(restoredCommitment, expectedForkObservationCommitment)) {
|
|
847
|
-
throw new Error("Policy fork evidence commitment mismatch");
|
|
848
|
-
}
|
|
849
|
-
if (canonicalChildren.length !== 2) {
|
|
850
|
-
throw new Error("Stored policy fork evidence has fewer than two distinct children");
|
|
851
|
-
}
|
|
852
|
-
const normalizedForkState = {
|
|
853
|
-
formatVersion: REDUCER_DURABLE_FORMAT_VERSION,
|
|
854
|
-
state: "FORKED",
|
|
855
|
-
commonParentEntryBytes: copyBytes(latestDurableState.commonParentEntryBytes),
|
|
856
|
-
childEntryBytes: [
|
|
857
|
-
copyBytes(canonicalChildren[0].entryBytes),
|
|
858
|
-
copyBytes(canonicalChildren[1].entryBytes),
|
|
859
|
-
],
|
|
860
|
-
};
|
|
861
|
-
const pairChanged = canonicalChildren.some((child, index) => !equals(child.digest, forkEvidence.children[index].digest) ||
|
|
862
|
-
!equals(child.entryBytes, forkEvidence.children[index].entryBytes));
|
|
863
|
-
if (pairChanged) {
|
|
864
|
-
core.abort();
|
|
865
|
-
core = await TrustedNetworkV2PolicyReducer.restore({
|
|
866
|
-
...coreProperties,
|
|
867
|
-
durableState: normalizedForkState,
|
|
868
|
-
});
|
|
869
|
-
assertOpenNotAborted(signal);
|
|
870
|
-
assertCanonicalCoreRestore(core, serialize(coreRecordFromState(normalizedForkState)));
|
|
871
|
-
}
|
|
872
|
-
latestDurableState = normalizedForkState;
|
|
873
|
-
durableCoreBytes = serialize(coreRecordFromState(normalizedForkState));
|
|
874
|
-
}
|
|
875
|
-
else if (observedEntries.size !== 0) {
|
|
876
|
-
throw new Error("Non-forked policy anchor has fork observations");
|
|
581
|
+
if (current === undefined) {
|
|
582
|
+
core = new TrustedNetworkV2PolicyReducer(coreProperties);
|
|
583
|
+
return new TrustedNetworkV2DurablePolicyReducer({
|
|
584
|
+
checkpoint,
|
|
585
|
+
core,
|
|
586
|
+
});
|
|
877
587
|
}
|
|
588
|
+
const payload = decodeCanonicalCheckpointPayload(current.payload);
|
|
589
|
+
const decoded = await durableStateFromCheckpointPayload(payload, descriptor, signal);
|
|
590
|
+
assertOpenNotAborted(signal);
|
|
591
|
+
core = await TrustedNetworkV2PolicyReducer.restore({
|
|
592
|
+
...coreProperties,
|
|
593
|
+
durableState: decoded.durableState,
|
|
594
|
+
});
|
|
595
|
+
assertOpenNotAborted(signal);
|
|
596
|
+
assertCanonicalCoreRestore(core, decoded.durableState);
|
|
597
|
+
const forked = decoded.durableState.state === "FORKED";
|
|
878
598
|
return new TrustedNetworkV2DurablePolicyReducer({
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
599
|
+
// The helper retains its latest payload. A terminal fork no longer needs
|
|
600
|
+
// storage, so release that potentially 8.78 MiB snapshot immediately.
|
|
601
|
+
checkpoint: forked ? undefined : checkpoint,
|
|
882
602
|
core,
|
|
883
|
-
|
|
884
|
-
previousGenerationChecksum: previousChecksum,
|
|
885
|
-
durableCoreBytes,
|
|
603
|
+
durableCoreIdentityBytes: decoded.coreIdentityBytes,
|
|
886
604
|
});
|
|
887
605
|
}
|
|
888
606
|
catch (error) {
|
|
@@ -907,13 +625,15 @@ export class TrustedNetworkV2DurablePolicyReducer {
|
|
|
907
625
|
return copyForkEvidence(this.published.forkEvidence);
|
|
908
626
|
}
|
|
909
627
|
get pendingCount() {
|
|
910
|
-
return this.core.pendingCount;
|
|
628
|
+
return this.published.state === "FORKED" ? 0 : this.core.pendingCount;
|
|
911
629
|
}
|
|
912
630
|
get pendingBytes() {
|
|
913
|
-
return this.core.pendingBytes;
|
|
631
|
+
return this.published.state === "FORKED" ? 0 : this.core.pendingBytes;
|
|
914
632
|
}
|
|
915
633
|
get pendingDigests() {
|
|
916
|
-
return this.
|
|
634
|
+
return this.published.state === "FORKED"
|
|
635
|
+
? []
|
|
636
|
+
: this.core.pendingDigests.map(copyBytes);
|
|
917
637
|
}
|
|
918
638
|
/** Internal diagnostics for the fixed pre-publication admission bound. */
|
|
919
639
|
get bufferedAdmissionCount() {
|
|
@@ -1074,120 +794,52 @@ export class TrustedNetworkV2DurablePolicyReducer {
|
|
|
1074
794
|
async persistCorePublication(result) {
|
|
1075
795
|
const durableState = this.core.exportDurableState();
|
|
1076
796
|
if (durableState.state === "EMPTY") {
|
|
1077
|
-
if (this.
|
|
1078
|
-
throw
|
|
1079
|
-
}
|
|
1080
|
-
return;
|
|
1081
|
-
}
|
|
1082
|
-
const coreRecord = coreRecordFromState(durableState);
|
|
1083
|
-
const coreBytes = serialize(coreRecord);
|
|
1084
|
-
const stateChanged = this.durableCoreBytes === undefined ||
|
|
1085
|
-
!equals(this.durableCoreBytes, coreBytes);
|
|
1086
|
-
const suppliedObservations = new Map();
|
|
1087
|
-
for (const proof of result.forkObservations ?? []) {
|
|
1088
|
-
const contentHash = observationContentHash(proof.entryBytes);
|
|
1089
|
-
const hashKey = bytesKey(contentHash);
|
|
1090
|
-
const retained = suppliedObservations.get(hashKey);
|
|
1091
|
-
if (retained !== undefined && !equals(retained, proof.entryBytes)) {
|
|
1092
|
-
throw this.halt("Policy fork-observation content hash collision");
|
|
1093
|
-
}
|
|
1094
|
-
suppliedObservations.set(hashKey, copyBytes(proof.entryBytes));
|
|
1095
|
-
}
|
|
1096
|
-
let committedForkObservationCount = 0;
|
|
1097
|
-
let committedForkObservationDigest = copyBytes(ZERO_DIGEST);
|
|
1098
|
-
if (durableState.state !== "FORKED") {
|
|
1099
|
-
if (suppliedObservations.size !== 0) {
|
|
1100
|
-
throw this.halt("Only a forked policy anchor may contain observations");
|
|
1101
|
-
}
|
|
1102
|
-
}
|
|
1103
|
-
else {
|
|
1104
|
-
if (this.published.state === "FORKED" || !stateChanged) {
|
|
1105
|
-
throw this.halt("A durable fork transition may be published only once");
|
|
1106
|
-
}
|
|
1107
|
-
if (suppliedObservations.size < 2 ||
|
|
1108
|
-
suppliedObservations.size > MAX_FORK_OBSERVATIONS_V2) {
|
|
1109
|
-
throw this.halt(`A durable fork transition must contain 2-${MAX_FORK_OBSERVATIONS_V2} distinct observations`);
|
|
797
|
+
if (this.durableCoreIdentityBytes !== undefined) {
|
|
798
|
+
throw new Error("Durable policy state cannot return to EMPTY");
|
|
1110
799
|
}
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
committedForkObservationDigest = forkObservationCommitment(this.descriptorHash, completeObservationEntries);
|
|
1114
|
-
for (const entryBytes of durableState.childEntryBytes) {
|
|
1115
|
-
const hashKey = bytesKey(observationContentHash(entryBytes));
|
|
1116
|
-
if (!suppliedObservations.delete(hashKey)) {
|
|
1117
|
-
throw this.halt("Published fork state is missing a canonical observation");
|
|
1118
|
-
}
|
|
800
|
+
if ((result.forkObservations?.length ?? 0) !== 0) {
|
|
801
|
+
throw new Error("An empty policy anchor cannot contain fork evidence");
|
|
1119
802
|
}
|
|
1120
|
-
}
|
|
1121
|
-
const observationEntries = canonicalForkObservationEntries(suppliedObservations.values());
|
|
1122
|
-
const recordCount = (stateChanged ? 1 : 0) + observationEntries.length;
|
|
1123
|
-
if (recordCount === 0)
|
|
1124
803
|
return;
|
|
1125
|
-
if (BigInt(recordCount) > MAX_U64 - this.generation) {
|
|
1126
|
-
throw this.halt("Policy-anchor generation exhausted u64");
|
|
1127
804
|
}
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
if (
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
forkObservationCount: committedForkObservationCount,
|
|
1135
|
-
forkObservationCommitment: committedForkObservationDigest,
|
|
1136
|
-
}));
|
|
1137
|
-
if (payloadBytes.byteLength > MAX_STATE_GENERATION_PAYLOAD_BYTES) {
|
|
1138
|
-
throw this.halt("Policy-anchor state payload exceeds its byte ceiling");
|
|
1139
|
-
}
|
|
1140
|
-
drafts.push({ kind: GENERATION_KIND.STATE, payloadBytes });
|
|
1141
|
-
}
|
|
1142
|
-
for (const entryBytes of observationEntries) {
|
|
1143
|
-
const payloadBytes = serialize(new PolicyAnchorObservationGenerationPayloadV2({
|
|
1144
|
-
entryBytes: copyBytes(entryBytes),
|
|
1145
|
-
}));
|
|
1146
|
-
if (payloadBytes.byteLength > MAX_OBSERVATION_GENERATION_PAYLOAD_BYTES) {
|
|
1147
|
-
throw this.halt("Policy fork-observation payload exceeds its byte ceiling");
|
|
1148
|
-
}
|
|
1149
|
-
drafts.push({
|
|
1150
|
-
kind: GENERATION_KIND.FORK_OBSERVATION,
|
|
1151
|
-
payloadBytes,
|
|
1152
|
-
});
|
|
1153
|
-
}
|
|
1154
|
-
for (const draft of drafts) {
|
|
1155
|
-
nextGeneration += 1n;
|
|
1156
|
-
const body = new PolicyAnchorGenerationBodyV2({
|
|
1157
|
-
formatVersion: ANCHOR_FORMAT_VERSION,
|
|
1158
|
-
generation: nextGeneration,
|
|
1159
|
-
descriptorDigest: copyBytes(this.descriptorHash),
|
|
1160
|
-
previousGenerationChecksum: copyBytes(previousChecksum),
|
|
1161
|
-
kind: draft.kind,
|
|
1162
|
-
payloadBytes: copyBytes(draft.payloadBytes),
|
|
1163
|
-
});
|
|
1164
|
-
const record = new PolicyAnchorGenerationRecordV2({
|
|
1165
|
-
body,
|
|
1166
|
-
checksum: generationChecksum(body),
|
|
1167
|
-
});
|
|
1168
|
-
const recordBytes = serialize(record);
|
|
1169
|
-
if (recordBytes.byteLength >
|
|
1170
|
-
TRUSTED_NETWORK_V2_MAX_POLICY_ANCHOR_GENERATION_BYTES) {
|
|
1171
|
-
throw this.halt("Policy-anchor generation record exceeds its byte ceiling");
|
|
1172
|
-
}
|
|
1173
|
-
const key = generationKey(nextGeneration);
|
|
1174
|
-
if ((await this.store.get(key)) !== undefined) {
|
|
1175
|
-
throw this.halt("Policy-anchor generation must never be overwritten");
|
|
805
|
+
const coreIdentityBytes = coreIdentityBytesFromState(durableState);
|
|
806
|
+
const stateChanged = this.durableCoreIdentityBytes === undefined ||
|
|
807
|
+
!equals(this.durableCoreIdentityBytes, coreIdentityBytes);
|
|
808
|
+
if (durableState.state !== "FORKED") {
|
|
809
|
+
if ((result.forkObservations?.length ?? 0) !== 0) {
|
|
810
|
+
throw new Error("Only a forked policy anchor may contain observations");
|
|
1176
811
|
}
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
}
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
throw
|
|
812
|
+
if (!stateChanged)
|
|
813
|
+
return;
|
|
814
|
+
}
|
|
815
|
+
else if (this.published.state === "FORKED" || !stateChanged) {
|
|
816
|
+
throw new Error("A durable fork transition may be published only once");
|
|
817
|
+
}
|
|
818
|
+
const nextPublished = publishedFromReducer(this.core);
|
|
819
|
+
const observationEntries = durableState.state === "FORKED"
|
|
820
|
+
? canonicalForkEntriesForCommit(result, durableState, nextPublished.forkEvidence)
|
|
821
|
+
: [];
|
|
822
|
+
const payloadBytes = serialize(checkpointPayloadFromState(durableState, observationEntries));
|
|
823
|
+
if (payloadBytes.byteLength >
|
|
824
|
+
TRUSTED_NETWORK_V2_MAX_POLICY_ANCHOR_CHECKPOINT_PAYLOAD_BYTES) {
|
|
825
|
+
throw new Error("Policy-anchor checkpoint payload exceeds its byte ceiling");
|
|
826
|
+
}
|
|
827
|
+
// Allocate every post-commit publication value before durable replacement.
|
|
828
|
+
// Once commit resolves, the remaining operations are non-throwing assignments.
|
|
829
|
+
const nextDurableCoreIdentityBytes = durableState.state === "FORKED"
|
|
830
|
+
? undefined
|
|
831
|
+
: copyBytes(coreIdentityBytes);
|
|
832
|
+
const checkpoint = this.checkpoint;
|
|
833
|
+
if (checkpoint === undefined) {
|
|
834
|
+
throw new Error("Policy-anchor checkpoint is unavailable");
|
|
835
|
+
}
|
|
836
|
+
await checkpoint.commit(payloadBytes);
|
|
837
|
+
this.durableCoreIdentityBytes = nextDurableCoreIdentityBytes;
|
|
838
|
+
this.published = nextPublished;
|
|
839
|
+
if (durableState.state === "FORKED") {
|
|
840
|
+
// The terminal projection is self-contained; drop the helper's retained
|
|
841
|
+
// copy of the potentially maximum-sized checkpoint payload.
|
|
842
|
+
this.checkpoint = undefined;
|
|
1191
843
|
}
|
|
1192
844
|
}
|
|
1193
845
|
}
|