@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/src/v2.ts CHANGED
@@ -19,17 +19,19 @@ import { compare, concat, equals } from "uint8arrays";
19
19
  * Decode-only TrustedNetwork v2 codec scaffold.
20
20
  *
21
21
  * This module is intentionally absent from the package entry point. It pins
22
- * the policy wire contract needed by the next slice without exposing a usable
23
- * controller, policy engine, resource fence, or encryption path.
22
+ * the policy, resource-fence-body, and operation-proof wire contracts without
23
+ * exposing a usable controller, resource validator, or encryption path.
24
24
  */
25
25
 
26
26
  export const TRUSTED_NETWORK_V2_PROTOCOL_VERSION = 2;
27
27
  export const TRUSTED_NETWORK_V2_POLICY_HASH_SHA256 = 1;
28
28
 
29
29
  /**
30
- * Profile 1 accepts only an EntryV0 whose normal toSignable() bytes (including
31
- * causal metadata) have exactly one successfully verified signature. The
32
- * signer's canonical public-key bytes must equal the descriptor authority.
30
+ * Authority-record profile 1 accepts only a policy-snapshot or resource-fence
31
+ * EntryV0 whose normal toSignable() bytes (including causal metadata) have
32
+ * exactly one successfully verified signature. The signer's canonical
33
+ * public-key bytes must equal the descriptor authority. Protected-resource
34
+ * adapters must define a separate bounded profile for operation entries.
33
35
  */
34
36
  export const TRUSTED_NETWORK_V2_ENTRY_V0_AUTHORITY_ONLY_SIGNATURE_PROFILE = 1;
35
37
 
@@ -45,6 +47,10 @@ export const TRUSTED_NETWORK_V2_NETWORK_ID_DOMAIN =
45
47
  export const TRUSTED_NETWORK_V2_POLICY_DIGEST_DOMAIN =
46
48
  "peerbit/trusted-network/v2/policy-body/v1";
47
49
 
50
+ /** Fixed first-profile record lengths. Changing either requires a new variant. */
51
+ export const TRUSTED_NETWORK_V2_RESOURCE_FENCE_BODY_BYTES = 186;
52
+ export const TRUSTED_NETWORK_V2_OPERATION_POLICY_PROOF_BYTES = 146;
53
+
48
54
  export const TrustedNetworkRole = Object.freeze({
49
55
  ADMIN: 0x01,
50
56
  WRITER: 0x02,
@@ -60,6 +66,29 @@ export const TRUSTED_NETWORK_V2_KNOWN_ROLE_BITS =
60
66
 
61
67
  const ZERO_DIGEST = new Uint8Array(32);
62
68
  const textEncoder = new TextEncoder();
69
+ const TYPED_ARRAY_PROTOTYPE = Object.getPrototypeOf(Uint8Array.prototype);
70
+ const TYPED_ARRAY_BYTE_LENGTH = Object.getOwnPropertyDescriptor(
71
+ TYPED_ARRAY_PROTOTYPE,
72
+ "byteLength",
73
+ )!.get!;
74
+ const TYPED_ARRAY_TAG = Object.getOwnPropertyDescriptor(
75
+ TYPED_ARRAY_PROTOTYPE,
76
+ Symbol.toStringTag,
77
+ )!.get!;
78
+
79
+ const exactUint8ArrayByteLength = (value: unknown): number => {
80
+ if (
81
+ !ArrayBuffer.isView(value) ||
82
+ TYPED_ARRAY_TAG.call(value) !== "Uint8Array"
83
+ ) {
84
+ throw new TypeError("Expected a genuine Uint8Array");
85
+ }
86
+ const byteLength = TYPED_ARRAY_BYTE_LENGTH.call(value) as number;
87
+ if (byteLength === 0) {
88
+ Uint8Array.prototype.set.call(new Uint8Array(0), value as Uint8Array);
89
+ }
90
+ return byteLength;
91
+ };
63
92
 
64
93
  const assertByte = (value: number, label: string): void => {
65
94
  if (!Number.isInteger(value) || value < 0 || value > 0xff) {
@@ -80,7 +109,13 @@ const assertU64 = (value: bigint, label: string): void => {
80
109
  };
81
110
 
82
111
  const assertBytes32 = (value: Uint8Array, label: string): void => {
83
- if (!(value instanceof Uint8Array) || value.byteLength !== 32) {
112
+ let byteLength: number;
113
+ try {
114
+ byteLength = exactUint8ArrayByteLength(value);
115
+ } catch {
116
+ throw new Error(`${label} must contain exactly 32 bytes`);
117
+ }
118
+ if (byteLength !== 32) {
84
119
  throw new Error(`${label} must contain exactly 32 bytes`);
85
120
  }
86
121
  };
@@ -190,6 +225,80 @@ export class PolicySnapshotBodyV2 {
190
225
  }
191
226
  }
192
227
 
228
+ /** Canonical unsigned payload intended for an authenticated resource fence. */
229
+ @variant([2, 2])
230
+ export class ResourceFenceV2 {
231
+ @field({ type: fixedArray("u8", 32) })
232
+ networkId: Uint8Array;
233
+
234
+ @field({ type: fixedArray("u8", 32) })
235
+ resourceId: Uint8Array;
236
+
237
+ @field({ type: "u64" })
238
+ fenceSequence: bigint;
239
+
240
+ @field({ type: fixedArray("u8", 32) })
241
+ previousFenceDigest: Uint8Array;
242
+
243
+ @field({ type: "u64" })
244
+ policySequence: bigint;
245
+
246
+ @field({ type: fixedArray("u8", 32) })
247
+ policyDigest: Uint8Array;
248
+
249
+ @field({ type: "u64" })
250
+ contentEpoch: bigint;
251
+
252
+ @field({ type: fixedArray("u8", 32) })
253
+ epochManifestDigest: Uint8Array;
254
+
255
+ constructor(properties?: {
256
+ networkId: Uint8Array;
257
+ resourceId: Uint8Array;
258
+ fenceSequence: bigint;
259
+ previousFenceDigest: Uint8Array;
260
+ policySequence: bigint;
261
+ policyDigest: Uint8Array;
262
+ contentEpoch: bigint;
263
+ epochManifestDigest: Uint8Array;
264
+ }) {
265
+ if (properties) Object.assign(this, properties);
266
+ }
267
+ }
268
+
269
+ /** Constant-size policy commitment intended for a signed resource operation. */
270
+ @variant([2, 3])
271
+ export class OperationPolicyProofV2 {
272
+ @field({ type: fixedArray("u8", 32) })
273
+ networkId: Uint8Array;
274
+
275
+ @field({ type: fixedArray("u8", 32) })
276
+ resourceId: Uint8Array;
277
+
278
+ @field({ type: "u64" })
279
+ policySequence: bigint;
280
+
281
+ @field({ type: fixedArray("u8", 32) })
282
+ policyDigest: Uint8Array;
283
+
284
+ @field({ type: fixedArray("u8", 32) })
285
+ fenceDigest: Uint8Array;
286
+
287
+ @field({ type: "u64" })
288
+ contentEpoch: bigint;
289
+
290
+ constructor(properties?: {
291
+ networkId: Uint8Array;
292
+ resourceId: Uint8Array;
293
+ policySequence: bigint;
294
+ policyDigest: Uint8Array;
295
+ fenceDigest: Uint8Array;
296
+ contentEpoch: bigint;
297
+ }) {
298
+ if (properties) Object.assign(this, properties);
299
+ }
300
+ }
301
+
193
302
  @variant("trusted_network_v2")
194
303
  export class TrustedNetworkV2 extends Program<never> {
195
304
  @field({ type: NetworkDescriptorV2 })
@@ -353,3 +462,97 @@ export const decodePolicySnapshotBodyV2 = (
353
462
  assertPolicySnapshotBodyV2(body, descriptor);
354
463
  return body;
355
464
  };
465
+
466
+ export const assertResourceFenceV2 = (
467
+ fence: ResourceFenceV2,
468
+ descriptor: NetworkDescriptorV2,
469
+ ): void => {
470
+ assertNetworkDescriptorV2(descriptor);
471
+ assertBytes32(fence.networkId, "networkId");
472
+ if (!equals(fence.networkId, deriveNetworkIdV2(descriptor))) {
473
+ throw new Error("Resource fence belongs to another network");
474
+ }
475
+ assertBytes32(fence.resourceId, "resourceId");
476
+ assertU64(fence.fenceSequence, "fenceSequence");
477
+ assertBytes32(fence.previousFenceDigest, "previousFenceDigest");
478
+ if (fence.fenceSequence === 0n) {
479
+ if (!equals(fence.previousFenceDigest, ZERO_DIGEST)) {
480
+ throw new Error(
481
+ "Initial resource fence must use the zero previous digest",
482
+ );
483
+ }
484
+ } else if (equals(fence.previousFenceDigest, ZERO_DIGEST)) {
485
+ throw new Error("A non-initial resource fence must name its predecessor");
486
+ }
487
+ assertU64(fence.policySequence, "policySequence");
488
+ assertBytes32(fence.policyDigest, "policyDigest");
489
+ assertU64(fence.contentEpoch, "contentEpoch");
490
+ assertBytes32(fence.epochManifestDigest, "epochManifestDigest");
491
+ };
492
+
493
+ export const assertOperationPolicyProofV2 = (
494
+ proof: OperationPolicyProofV2,
495
+ descriptor: NetworkDescriptorV2,
496
+ ): void => {
497
+ assertNetworkDescriptorV2(descriptor);
498
+ assertBytes32(proof.networkId, "networkId");
499
+ if (!equals(proof.networkId, deriveNetworkIdV2(descriptor))) {
500
+ throw new Error("Operation policy proof belongs to another network");
501
+ }
502
+ assertBytes32(proof.resourceId, "resourceId");
503
+ assertU64(proof.policySequence, "policySequence");
504
+ assertBytes32(proof.policyDigest, "policyDigest");
505
+ assertBytes32(proof.fenceDigest, "fenceDigest");
506
+ assertU64(proof.contentEpoch, "contentEpoch");
507
+ };
508
+
509
+ const decodeExactCanonicalV2 = <T>(
510
+ bytes: Uint8Array,
511
+ exactLength: number,
512
+ type: new () => T,
513
+ ): T => {
514
+ let byteLength: number;
515
+ try {
516
+ byteLength = exactUint8ArrayByteLength(bytes);
517
+ } catch {
518
+ throw new Error(
519
+ `TrustedNetwork v2 record must contain ${exactLength} bytes`,
520
+ );
521
+ }
522
+ if (byteLength !== exactLength) {
523
+ throw new Error(
524
+ `TrustedNetwork v2 record must contain ${exactLength} bytes`,
525
+ );
526
+ }
527
+ const captured = new Uint8Array(byteLength);
528
+ Uint8Array.prototype.set.call(captured, bytes);
529
+ const value = deserialize(captured, type);
530
+ assertCanonicalEncoding(captured, value);
531
+ return value;
532
+ };
533
+
534
+ export const decodeResourceFenceV2 = (
535
+ bytes: Uint8Array,
536
+ descriptor: NetworkDescriptorV2,
537
+ ): ResourceFenceV2 => {
538
+ const fence = decodeExactCanonicalV2(
539
+ bytes,
540
+ TRUSTED_NETWORK_V2_RESOURCE_FENCE_BODY_BYTES,
541
+ ResourceFenceV2,
542
+ );
543
+ assertResourceFenceV2(fence, descriptor);
544
+ return fence;
545
+ };
546
+
547
+ export const decodeOperationPolicyProofV2 = (
548
+ bytes: Uint8Array,
549
+ descriptor: NetworkDescriptorV2,
550
+ ): OperationPolicyProofV2 => {
551
+ const proof = decodeExactCanonicalV2(
552
+ bytes,
553
+ TRUSTED_NETWORK_V2_OPERATION_POLICY_PROOF_BYTES,
554
+ OperationPolicyProofV2,
555
+ );
556
+ assertOperationPolicyProofV2(proof, descriptor);
557
+ return proof;
558
+ };