@series-inc/rundot-syncplay 5.23.0-beta.7

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.
Files changed (264) hide show
  1. package/README.md +610 -0
  2. package/dist/animation.d.ts +119 -0
  3. package/dist/animation.js +488 -0
  4. package/dist/asset-cooking.d.ts +82 -0
  5. package/dist/asset-cooking.js +364 -0
  6. package/dist/authority-room.d.ts +217 -0
  7. package/dist/authority-room.js +701 -0
  8. package/dist/bot-documents.d.ts +115 -0
  9. package/dist/bot-documents.js +549 -0
  10. package/dist/bot-primitives.d.ts +152 -0
  11. package/dist/bot-primitives.js +472 -0
  12. package/dist/browser.d.ts +57 -0
  13. package/dist/browser.js +53 -0
  14. package/dist/canonical.d.ts +12 -0
  15. package/dist/canonical.js +288 -0
  16. package/dist/certification.d.ts +455 -0
  17. package/dist/certification.js +1855 -0
  18. package/dist/chaos-certification.d.ts +56 -0
  19. package/dist/chaos-certification.js +1397 -0
  20. package/dist/cjs/animation.js +495 -0
  21. package/dist/cjs/asset-cooking.js +372 -0
  22. package/dist/cjs/authority-room.js +704 -0
  23. package/dist/cjs/bot-documents.js +556 -0
  24. package/dist/cjs/bot-primitives.js +482 -0
  25. package/dist/cjs/browser.js +96 -0
  26. package/dist/cjs/canonical.js +299 -0
  27. package/dist/cjs/certification.js +1860 -0
  28. package/dist/cjs/chaos-certification.js +1402 -0
  29. package/dist/cjs/collections.js +36 -0
  30. package/dist/cjs/collision.js +96 -0
  31. package/dist/cjs/commands.js +987 -0
  32. package/dist/cjs/config-bundle.js +491 -0
  33. package/dist/cjs/creator.js +30 -0
  34. package/dist/cjs/deterministic-primitives.js +134 -0
  35. package/dist/cjs/deterministic-session.js +1410 -0
  36. package/dist/cjs/ecs-lite.js +155 -0
  37. package/dist/cjs/effects-adapter.js +97 -0
  38. package/dist/cjs/engine-complete.js +2297 -0
  39. package/dist/cjs/engine-core-proof.js +70 -0
  40. package/dist/cjs/engine-parity-matrix.js +874 -0
  41. package/dist/cjs/engine-parity.js +3183 -0
  42. package/dist/cjs/engine-runtime.js +3141 -0
  43. package/dist/cjs/errors.js +22 -0
  44. package/dist/cjs/events.js +247 -0
  45. package/dist/cjs/examples/counter-duel.js +157 -0
  46. package/dist/cjs/examples/fixed-arena.js +368 -0
  47. package/dist/cjs/index.js +229 -0
  48. package/dist/cjs/input-authority.js +150 -0
  49. package/dist/cjs/input-button.js +256 -0
  50. package/dist/cjs/input-timeline.js +418 -0
  51. package/dist/cjs/instant-replay.js +66 -0
  52. package/dist/cjs/lag-compensation3d.js +837 -0
  53. package/dist/cjs/local-authority-server-runner.js +1121 -0
  54. package/dist/cjs/local-authority-wire.js +1497 -0
  55. package/dist/cjs/local-authority.js +817 -0
  56. package/dist/cjs/local-ws-adapter.js +150 -0
  57. package/dist/cjs/match-log.js +61 -0
  58. package/dist/cjs/math.js +157 -0
  59. package/dist/cjs/mock-network-adapter.js +26 -0
  60. package/dist/cjs/movement.js +703 -0
  61. package/dist/cjs/movement3d.js +1246 -0
  62. package/dist/cjs/multiclient-authority.js +308 -0
  63. package/dist/cjs/multiplayer-service.js +434 -0
  64. package/dist/cjs/navigation.js +347 -0
  65. package/dist/cjs/navmesh.js +1437 -0
  66. package/dist/cjs/networked-client.js +691 -0
  67. package/dist/cjs/networked-input-authority.js +443 -0
  68. package/dist/cjs/node.js +47 -0
  69. package/dist/cjs/number-lab.js +78 -0
  70. package/dist/cjs/offline-session.js +370 -0
  71. package/dist/cjs/package.json +1 -0
  72. package/dist/cjs/pause-resume.js +43 -0
  73. package/dist/cjs/physics-cert.js +426 -0
  74. package/dist/cjs/physics2d.js +751 -0
  75. package/dist/cjs/physics3d.js +1996 -0
  76. package/dist/cjs/prediction-culling.js +203 -0
  77. package/dist/cjs/protocol-lab.js +1299 -0
  78. package/dist/cjs/r3f-render-adapter.js +209 -0
  79. package/dist/cjs/random.js +59 -0
  80. package/dist/cjs/replay-bundle.js +1598 -0
  81. package/dist/cjs/replay-inspect.js +236 -0
  82. package/dist/cjs/replay-scoring.js +68 -0
  83. package/dist/cjs/replay.js +356 -0
  84. package/dist/cjs/reports.js +181 -0
  85. package/dist/cjs/rollback-history.js +251 -0
  86. package/dist/cjs/runtime-guards.js +111 -0
  87. package/dist/cjs/sample-lab.js +145 -0
  88. package/dist/cjs/sample-scenes.js +752 -0
  89. package/dist/cjs/schema-artifacts.js +359 -0
  90. package/dist/cjs/schema-codegen.js +544 -0
  91. package/dist/cjs/schema-dsl-binary.js +655 -0
  92. package/dist/cjs/schema.js +319 -0
  93. package/dist/cjs/sdk-offline.js +99 -0
  94. package/dist/cjs/session-promotion.js +74 -0
  95. package/dist/cjs/session-snapshot.js +265 -0
  96. package/dist/cjs/session-wire.js +216 -0
  97. package/dist/cjs/signals.js +103 -0
  98. package/dist/cjs/sparse-set.js +169 -0
  99. package/dist/cjs/synctest.js +136 -0
  100. package/dist/cjs/system-lifecycle.js +286 -0
  101. package/dist/cjs/timers.js +61 -0
  102. package/dist/cjs/tools/static-checker.js +1468 -0
  103. package/dist/cjs/types.js +2 -0
  104. package/dist/cjs/venus-mp-budget.js +125 -0
  105. package/dist/cjs/ws-frame.js +255 -0
  106. package/dist/cli.d.ts +2 -0
  107. package/dist/cli.js +11379 -0
  108. package/dist/collections.d.ts +9 -0
  109. package/dist/collections.js +30 -0
  110. package/dist/collision.d.ts +25 -0
  111. package/dist/collision.js +85 -0
  112. package/dist/commands.d.ts +137 -0
  113. package/dist/commands.js +977 -0
  114. package/dist/config-bundle.d.ts +89 -0
  115. package/dist/config-bundle.js +483 -0
  116. package/dist/creator.d.ts +20 -0
  117. package/dist/creator.js +11 -0
  118. package/dist/deterministic-primitives.d.ts +59 -0
  119. package/dist/deterministic-primitives.js +126 -0
  120. package/dist/deterministic-session.d.ts +197 -0
  121. package/dist/deterministic-session.js +1401 -0
  122. package/dist/ecs-lite.d.ts +26 -0
  123. package/dist/ecs-lite.js +152 -0
  124. package/dist/effects-adapter.d.ts +21 -0
  125. package/dist/effects-adapter.js +94 -0
  126. package/dist/engine-complete.d.ts +45 -0
  127. package/dist/engine-complete.js +2292 -0
  128. package/dist/engine-core-proof.d.ts +26 -0
  129. package/dist/engine-core-proof.js +66 -0
  130. package/dist/engine-parity-matrix.d.ts +300 -0
  131. package/dist/engine-parity-matrix.js +869 -0
  132. package/dist/engine-parity.d.ts +74 -0
  133. package/dist/engine-parity.js +3178 -0
  134. package/dist/engine-runtime.d.ts +351 -0
  135. package/dist/engine-runtime.js +3123 -0
  136. package/dist/errors.d.ts +11 -0
  137. package/dist/errors.js +17 -0
  138. package/dist/events.d.ts +45 -0
  139. package/dist/events.js +242 -0
  140. package/dist/examples/counter-duel.d.ts +43 -0
  141. package/dist/examples/counter-duel.js +150 -0
  142. package/dist/examples/fixed-arena.d.ts +110 -0
  143. package/dist/examples/fixed-arena.js +356 -0
  144. package/dist/index.d.ts +101 -0
  145. package/dist/index.js +66 -0
  146. package/dist/input-authority.d.ts +82 -0
  147. package/dist/input-authority.js +147 -0
  148. package/dist/input-button.d.ts +36 -0
  149. package/dist/input-button.js +249 -0
  150. package/dist/input-timeline.d.ts +65 -0
  151. package/dist/input-timeline.js +412 -0
  152. package/dist/instant-replay.d.ts +59 -0
  153. package/dist/instant-replay.js +62 -0
  154. package/dist/lag-compensation3d.d.ts +146 -0
  155. package/dist/lag-compensation3d.js +827 -0
  156. package/dist/local-authority-server-runner.d.ts +79 -0
  157. package/dist/local-authority-server-runner.js +1118 -0
  158. package/dist/local-authority-wire.d.ts +142 -0
  159. package/dist/local-authority-wire.js +1477 -0
  160. package/dist/local-authority.d.ts +160 -0
  161. package/dist/local-authority.js +810 -0
  162. package/dist/local-ws-adapter.d.ts +14 -0
  163. package/dist/local-ws-adapter.js +146 -0
  164. package/dist/match-log.d.ts +74 -0
  165. package/dist/match-log.js +57 -0
  166. package/dist/math.d.ts +41 -0
  167. package/dist/math.js +154 -0
  168. package/dist/mock-network-adapter.d.ts +13 -0
  169. package/dist/mock-network-adapter.js +23 -0
  170. package/dist/movement.d.ts +155 -0
  171. package/dist/movement.js +694 -0
  172. package/dist/movement3d.d.ts +195 -0
  173. package/dist/movement3d.js +1238 -0
  174. package/dist/multiclient-authority.d.ts +30 -0
  175. package/dist/multiclient-authority.js +305 -0
  176. package/dist/multiplayer-service.d.ts +153 -0
  177. package/dist/multiplayer-service.js +429 -0
  178. package/dist/navigation.d.ts +91 -0
  179. package/dist/navigation.js +338 -0
  180. package/dist/navmesh.d.ts +236 -0
  181. package/dist/navmesh.js +1423 -0
  182. package/dist/networked-client.d.ts +124 -0
  183. package/dist/networked-client.js +688 -0
  184. package/dist/networked-input-authority.d.ts +51 -0
  185. package/dist/networked-input-authority.js +439 -0
  186. package/dist/node.d.ts +22 -0
  187. package/dist/node.js +19 -0
  188. package/dist/number-lab.d.ts +15 -0
  189. package/dist/number-lab.js +75 -0
  190. package/dist/offline-session.d.ts +2 -0
  191. package/dist/offline-session.js +367 -0
  192. package/dist/pause-resume.d.ts +48 -0
  193. package/dist/pause-resume.js +40 -0
  194. package/dist/physics-cert.d.ts +40 -0
  195. package/dist/physics-cert.js +389 -0
  196. package/dist/physics2d.d.ts +142 -0
  197. package/dist/physics2d.js +741 -0
  198. package/dist/physics3d.d.ts +285 -0
  199. package/dist/physics3d.js +1979 -0
  200. package/dist/prediction-culling.d.ts +34 -0
  201. package/dist/prediction-culling.js +200 -0
  202. package/dist/protocol-lab.d.ts +115 -0
  203. package/dist/protocol-lab.js +1295 -0
  204. package/dist/r3f-render-adapter.d.ts +61 -0
  205. package/dist/r3f-render-adapter.js +203 -0
  206. package/dist/random.d.ts +13 -0
  207. package/dist/random.js +55 -0
  208. package/dist/replay-bundle.d.ts +150 -0
  209. package/dist/replay-bundle.js +1586 -0
  210. package/dist/replay-inspect.d.ts +60 -0
  211. package/dist/replay-inspect.js +228 -0
  212. package/dist/replay-scoring.d.ts +28 -0
  213. package/dist/replay-scoring.js +65 -0
  214. package/dist/replay.d.ts +17 -0
  215. package/dist/replay.js +351 -0
  216. package/dist/reports.d.ts +176 -0
  217. package/dist/reports.js +174 -0
  218. package/dist/rollback-history.d.ts +46 -0
  219. package/dist/rollback-history.js +247 -0
  220. package/dist/runtime-guards.d.ts +1 -0
  221. package/dist/runtime-guards.js +108 -0
  222. package/dist/sample-lab.d.ts +32 -0
  223. package/dist/sample-lab.js +134 -0
  224. package/dist/sample-scenes.d.ts +86 -0
  225. package/dist/sample-scenes.js +745 -0
  226. package/dist/schema-artifacts.d.ts +49 -0
  227. package/dist/schema-artifacts.js +356 -0
  228. package/dist/schema-codegen.d.ts +21 -0
  229. package/dist/schema-codegen.js +538 -0
  230. package/dist/schema-dsl-binary.d.ts +75 -0
  231. package/dist/schema-dsl-binary.js +648 -0
  232. package/dist/schema.d.ts +87 -0
  233. package/dist/schema.js +315 -0
  234. package/dist/sdk-offline.d.ts +34 -0
  235. package/dist/sdk-offline.js +96 -0
  236. package/dist/session-promotion.d.ts +26 -0
  237. package/dist/session-promotion.js +70 -0
  238. package/dist/session-snapshot.d.ts +95 -0
  239. package/dist/session-snapshot.js +255 -0
  240. package/dist/session-wire.d.ts +89 -0
  241. package/dist/session-wire.js +211 -0
  242. package/dist/signals.d.ts +31 -0
  243. package/dist/signals.js +99 -0
  244. package/dist/sparse-set.d.ts +34 -0
  245. package/dist/sparse-set.js +165 -0
  246. package/dist/synctest.d.ts +31 -0
  247. package/dist/synctest.js +133 -0
  248. package/dist/system-lifecycle.d.ts +72 -0
  249. package/dist/system-lifecycle.js +281 -0
  250. package/dist/timers.d.ts +17 -0
  251. package/dist/timers.js +53 -0
  252. package/dist/tools/index.d.ts +4 -0
  253. package/dist/tools/index.js +4 -0
  254. package/dist/tools/static-checker.d.ts +31 -0
  255. package/dist/tools/static-checker.js +1462 -0
  256. package/dist/tools/vite-plugin.d.ts +69 -0
  257. package/dist/tools/vite-plugin.js +203 -0
  258. package/dist/types.d.ts +148 -0
  259. package/dist/types.js +1 -0
  260. package/dist/venus-mp-budget.d.ts +29 -0
  261. package/dist/venus-mp-budget.js +122 -0
  262. package/dist/ws-frame.d.ts +49 -0
  263. package/dist/ws-frame.js +247 -0
  264. package/package.json +169 -0
@@ -0,0 +1,1497 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.deterministicLocalAuthorityWireProtocol = void 0;
4
+ exports.encodeDeterministicLocalAuthorityWireEnvelope = encodeDeterministicLocalAuthorityWireEnvelope;
5
+ exports.decodeDeterministicLocalAuthorityWireEnvelope = decodeDeterministicLocalAuthorityWireEnvelope;
6
+ exports.createDeterministicLocalAuthorityWireAuth = createDeterministicLocalAuthorityWireAuth;
7
+ exports.createDeterministicLocalAuthorityInputBundleEnvelope = createDeterministicLocalAuthorityInputBundleEnvelope;
8
+ exports.createDeterministicLocalAuthorityCommandBundleEnvelope = createDeterministicLocalAuthorityCommandBundleEnvelope;
9
+ exports.createDeterministicLocalAuthorityAckEnvelope = createDeterministicLocalAuthorityAckEnvelope;
10
+ exports.createDeterministicLocalAuthorityCorrectionEnvelope = createDeterministicLocalAuthorityCorrectionEnvelope;
11
+ exports.createDeterministicLocalAuthoritySnapshotEnvelope = createDeterministicLocalAuthoritySnapshotEnvelope;
12
+ exports.createDeterministicLocalAuthoritySnapshotTransfer = createDeterministicLocalAuthoritySnapshotTransfer;
13
+ exports.createDeterministicLocalAuthoritySnapshotRequestEnvelope = createDeterministicLocalAuthoritySnapshotRequestEnvelope;
14
+ exports.createDeterministicLocalAuthoritySnapshotOfferEnvelope = createDeterministicLocalAuthoritySnapshotOfferEnvelope;
15
+ exports.createDeterministicLocalAuthoritySnapshotChunkEnvelope = createDeterministicLocalAuthoritySnapshotChunkEnvelope;
16
+ exports.createDeterministicLocalAuthoritySnapshotCompleteEnvelope = createDeterministicLocalAuthoritySnapshotCompleteEnvelope;
17
+ exports.applyDeterministicLocalAuthorityInputBundleEnvelope = applyDeterministicLocalAuthorityInputBundleEnvelope;
18
+ exports.applyDeterministicLocalAuthorityCommandBundleEnvelope = applyDeterministicLocalAuthorityCommandBundleEnvelope;
19
+ exports.createDeterministicLocalAuthorityWireReceiver = createDeterministicLocalAuthorityWireReceiver;
20
+ exports.runDeterministicLocalAuthorityWireFixture = runDeterministicLocalAuthorityWireFixture;
21
+ const canonical_js_1 = require("./canonical.js");
22
+ const commands_js_1 = require("./commands.js");
23
+ const local_authority_js_1 = require("./local-authority.js");
24
+ exports.deterministicLocalAuthorityWireProtocol = 'rdm-local-authority/v1';
25
+ function encodeDeterministicLocalAuthorityWireEnvelope(envelope, sign) {
26
+ assertWireKind(envelope.kind);
27
+ assertWireSequence(envelope.sequence);
28
+ if (!isRecord(envelope.payload)) {
29
+ throw new Error('Wire envelope payload must be an object');
30
+ }
31
+ const withoutChecksum = {
32
+ schemaVersion: 1,
33
+ protocol: exports.deterministicLocalAuthorityWireProtocol,
34
+ kind: envelope.kind,
35
+ sequence: envelope.sequence,
36
+ payload: (0, canonical_js_1.cloneCanonical)(envelope.payload),
37
+ };
38
+ const auth = sign === undefined ? undefined : createDeterministicLocalAuthorityWireAuth(withoutChecksum, sign);
39
+ const signedEnvelope = auth === undefined ? withoutChecksum : { ...withoutChecksum, auth };
40
+ return (0, canonical_js_1.canonicalStringify)({
41
+ ...signedEnvelope,
42
+ checksum: (0, canonical_js_1.defaultChecksum)(signedEnvelope),
43
+ });
44
+ }
45
+ function decodeDeterministicLocalAuthorityWireEnvelope(encoded) {
46
+ // Bound parse cost BEFORE JSON.parse. The kind-specific budget is only known
47
+ // post-parse, so pre-cap at the largest budget: `.length` counts UTF-16
48
+ // units and UTF-8 bytes are always >= that, so length > cap already implies
49
+ // the byte budget is blown without touching the bytes. The exact UTF-8
50
+ // measurement below then only ever runs on a bounded string.
51
+ if (encoded.length > absoluteMaxWireEnvelopeBytes) {
52
+ throw new Error(`Wire envelope exceeds ${absoluteMaxWireEnvelopeBytes} byte payload budget`);
53
+ }
54
+ const encodedByteLength = (0, canonical_js_1.encodeUtf8)(encoded).byteLength;
55
+ if (encodedByteLength > absoluteMaxWireEnvelopeBytes) {
56
+ throw new Error(`Wire envelope exceeds ${absoluteMaxWireEnvelopeBytes} byte payload budget`);
57
+ }
58
+ let parsed;
59
+ try {
60
+ parsed = JSON.parse(encoded);
61
+ }
62
+ catch {
63
+ throw new Error('Wire envelope must be valid JSON');
64
+ }
65
+ if (!isRecord(parsed)) {
66
+ throw new Error('Wire envelope must be an object');
67
+ }
68
+ if (parsed.schemaVersion !== 1) {
69
+ throw new Error('Wire envelope schemaVersion must be 1');
70
+ }
71
+ if (parsed.protocol !== exports.deterministicLocalAuthorityWireProtocol) {
72
+ throw new Error(`Wire envelope protocol must be ${exports.deterministicLocalAuthorityWireProtocol}`);
73
+ }
74
+ if (typeof parsed.kind !== 'string') {
75
+ throw new Error('Wire envelope kind must be a string');
76
+ }
77
+ assertWireKind(parsed.kind);
78
+ const maxBytes = maxWireEnvelopeBytes(parsed.kind);
79
+ if (encodedByteLength > maxBytes) {
80
+ throw new Error(`Wire envelope exceeds ${maxBytes} byte payload budget`);
81
+ }
82
+ if (typeof parsed.sequence !== 'number') {
83
+ throw new Error('Wire envelope sequence must be an integer');
84
+ }
85
+ assertWireSequence(parsed.sequence);
86
+ if (!isRecord(parsed.payload)) {
87
+ throw new Error('Wire envelope payload must be an object');
88
+ }
89
+ if (typeof parsed.checksum !== 'string') {
90
+ throw new Error('Wire envelope checksum must be a string');
91
+ }
92
+ // Symmetry with encode: the encoder checksummed the LIVE payload and then
93
+ // canonical-encoded it, so sentinel values (-0, NaN, ±Infinity, undefined,
94
+ // bigint, Map/Set/typed arrays) sit on the wire as literal `{"$rdm":…}`
95
+ // objects. Decode them back to real values BEFORE recomputing the checksum —
96
+ // hashing the raw parse output trips canonicalize's reserved-key guard, which
97
+ // made every legitimately-produced snapshot containing a non-finite number
98
+ // undecodable on a frozen wire.
99
+ const payload = (0, canonical_js_1.decodeCanonicalValue)(parsed.payload);
100
+ if (!isRecord(payload)) {
101
+ throw new Error('Wire envelope payload must be an object');
102
+ }
103
+ const baseEnvelope = {
104
+ schemaVersion: 1,
105
+ protocol: exports.deterministicLocalAuthorityWireProtocol,
106
+ kind: parsed.kind,
107
+ sequence: parsed.sequence,
108
+ payload,
109
+ };
110
+ const auth = parsed.auth === undefined ? undefined : parseWireAuth(parsed.auth);
111
+ const expectedChecksum = (0, canonical_js_1.defaultChecksum)(auth === undefined ? baseEnvelope : { ...baseEnvelope, auth });
112
+ if (parsed.checksum !== expectedChecksum) {
113
+ throw new Error('Wire envelope checksum mismatch');
114
+ }
115
+ // The decoded payload is already a fresh deep structure (decodeCanonical
116
+ // rebuilds it), so the envelope is assembled directly — re-encoding the raw
117
+ // parse output through cloneCanonical would re-trip the reserved-key guard.
118
+ return (auth === undefined
119
+ ? { ...baseEnvelope, checksum: parsed.checksum }
120
+ : { ...baseEnvelope, auth, checksum: parsed.checksum });
121
+ }
122
+ function createDeterministicLocalAuthorityWireAuth(envelope, sign) {
123
+ assertWireSenderId(sign.senderId);
124
+ if (typeof sign.key !== 'string' || sign.key.length === 0) {
125
+ throw new Error('Wire auth key must be a non-empty string');
126
+ }
127
+ return {
128
+ senderId: sign.senderId,
129
+ signature: hmacSha256Hex(sign.key, (0, canonical_js_1.canonicalStringify)({ senderId: sign.senderId, envelope })),
130
+ };
131
+ }
132
+ function createDeterministicLocalAuthorityInputBundleEnvelope(sequence, inputs, sign) {
133
+ return encodeDeterministicLocalAuthorityWireEnvelope({
134
+ kind: 'input-bundle',
135
+ sequence,
136
+ payload: { inputs: inputs.map((input) => (0, canonical_js_1.cloneCanonical)(input)) },
137
+ }, sign);
138
+ }
139
+ function createDeterministicLocalAuthorityCommandBundleEnvelope(sequence, commands, sign) {
140
+ return encodeDeterministicLocalAuthorityWireEnvelope({
141
+ kind: 'command-bundle',
142
+ sequence,
143
+ payload: { commands: commands.map((command) => (0, canonical_js_1.cloneCanonical)(command)) },
144
+ }, sign);
145
+ }
146
+ function createDeterministicLocalAuthorityAckEnvelope(sequence, runtime, sign) {
147
+ return encodeDeterministicLocalAuthorityWireEnvelope({
148
+ kind: 'ack',
149
+ sequence,
150
+ payload: { checksums: runtime.checksums(), frame: runtime.state.frame },
151
+ }, sign);
152
+ }
153
+ function createDeterministicLocalAuthorityCorrectionEnvelope(sequence, runtime, identityOrSign, sign) {
154
+ const snapshot = runtime.snapshot();
155
+ const identity = isSnapshotIdentityLike(identityOrSign) ? (0, canonical_js_1.cloneCanonical)(identityOrSign) : undefined;
156
+ const signer = isSnapshotIdentityLike(identityOrSign) ? sign : identityOrSign;
157
+ return encodeDeterministicLocalAuthorityWireEnvelope({
158
+ kind: 'correction',
159
+ sequence,
160
+ payload: {
161
+ ...(identity === undefined ? {} : { identity }),
162
+ frame: snapshot.state.frame,
163
+ snapshotChecksum: (0, canonical_js_1.defaultChecksum)(snapshot),
164
+ snapshot,
165
+ state: snapshot.state,
166
+ },
167
+ }, signer);
168
+ }
169
+ function createDeterministicLocalAuthoritySnapshotEnvelope(sequence, runtime, identityOrSign, sign) {
170
+ const snapshot = runtime.snapshot();
171
+ const identity = isSnapshotIdentityLike(identityOrSign) ? (0, canonical_js_1.cloneCanonical)(identityOrSign) : undefined;
172
+ const signer = isSnapshotIdentityLike(identityOrSign) ? sign : identityOrSign;
173
+ return encodeDeterministicLocalAuthorityWireEnvelope({
174
+ kind: 'snapshot',
175
+ sequence,
176
+ payload: {
177
+ ...(identity === undefined ? {} : { identity }),
178
+ frame: snapshot.state.frame,
179
+ snapshotChecksum: (0, canonical_js_1.defaultChecksum)(snapshot),
180
+ snapshot,
181
+ },
182
+ }, signer);
183
+ }
184
+ function createDeterministicLocalAuthoritySnapshotTransfer(runtime, identity, transferId, chunkSize = 1024) {
185
+ if (!isValidTransferId(transferId)) {
186
+ throw new Error('Snapshot transferId is invalid');
187
+ }
188
+ if (!Number.isInteger(chunkSize) || chunkSize <= 0) {
189
+ throw new Error('Snapshot chunkSize must be a positive integer');
190
+ }
191
+ assertSnapshotIdentity(identity);
192
+ const snapshot = runtime.snapshot();
193
+ const snapshotBytes = (0, canonical_js_1.canonicalStringify)(snapshot);
194
+ const chunks = [];
195
+ for (let offset = 0; offset < snapshotBytes.length; offset += chunkSize) {
196
+ chunks.push(snapshotBytes.slice(offset, offset + chunkSize));
197
+ }
198
+ return {
199
+ transferId,
200
+ identity: (0, canonical_js_1.cloneCanonical)(identity),
201
+ frame: snapshot.state.frame,
202
+ snapshotChecksum: (0, canonical_js_1.defaultChecksum)(snapshot),
203
+ snapshotBytes,
204
+ chunkSize,
205
+ chunks,
206
+ chunkChecksums: chunks.map((chunk) => (0, canonical_js_1.defaultChecksum)(chunk)),
207
+ };
208
+ }
209
+ function createDeterministicLocalAuthoritySnapshotRequestEnvelope(sequence, identity, minFrame, sign) {
210
+ assertSnapshotIdentity(identity);
211
+ return encodeDeterministicLocalAuthorityWireEnvelope({
212
+ kind: 'snapshot-request',
213
+ sequence,
214
+ payload: { identity: (0, canonical_js_1.cloneCanonical)(identity), minFrame },
215
+ }, sign);
216
+ }
217
+ function createDeterministicLocalAuthoritySnapshotOfferEnvelope(sequence, transfer, sign) {
218
+ return encodeDeterministicLocalAuthorityWireEnvelope({
219
+ kind: 'snapshot-offer',
220
+ sequence,
221
+ payload: {
222
+ transferId: transfer.transferId,
223
+ identity: (0, canonical_js_1.cloneCanonical)(transfer.identity),
224
+ frame: transfer.frame,
225
+ snapshotChecksum: transfer.snapshotChecksum,
226
+ chunkCount: transfer.chunks.length,
227
+ totalBytes: transfer.snapshotBytes.length,
228
+ chunkChecksums: [...transfer.chunkChecksums],
229
+ },
230
+ }, sign);
231
+ }
232
+ function createDeterministicLocalAuthoritySnapshotChunkEnvelope(sequence, transfer, chunkIndex, sign) {
233
+ if (!Number.isInteger(chunkIndex) || chunkIndex < 0 || chunkIndex >= transfer.chunks.length) {
234
+ throw new Error('Snapshot chunkIndex is out of range');
235
+ }
236
+ const chunk = transfer.chunks[chunkIndex];
237
+ return encodeDeterministicLocalAuthorityWireEnvelope({
238
+ kind: 'snapshot-chunk',
239
+ sequence,
240
+ payload: {
241
+ transferId: transfer.transferId,
242
+ chunkIndex,
243
+ chunk,
244
+ chunkChecksum: (0, canonical_js_1.defaultChecksum)(chunk),
245
+ },
246
+ }, sign);
247
+ }
248
+ function createDeterministicLocalAuthoritySnapshotCompleteEnvelope(sequence, transferId, sign) {
249
+ if (!isValidTransferId(transferId)) {
250
+ throw new Error('Snapshot transferId is invalid');
251
+ }
252
+ return encodeDeterministicLocalAuthorityWireEnvelope({
253
+ kind: 'snapshot-complete',
254
+ sequence,
255
+ payload: { transferId },
256
+ }, sign);
257
+ }
258
+ function applyDeterministicLocalAuthorityInputBundleEnvelope(runtime, senderSlot, envelope) {
259
+ if (envelope.kind !== 'input-bundle') {
260
+ throw new Error(`Expected input-bundle envelope, received ${envelope.kind}`);
261
+ }
262
+ const inputs = envelope.payload.inputs;
263
+ if (!Array.isArray(inputs)) {
264
+ throw new Error('Input bundle payload requires inputs array');
265
+ }
266
+ let accepted = 0;
267
+ let rejected = 0;
268
+ const rejectionReasons = [];
269
+ for (const input of inputs) {
270
+ const message = parseInputMessage(input);
271
+ if (message.slot !== senderSlot) {
272
+ rejected += 1;
273
+ rejectionReasons.push('forged-slot');
274
+ continue;
275
+ }
276
+ const beforeRejected = runtime.state.rejectedMessages;
277
+ if (runtime.submitInput(message)) {
278
+ accepted += 1;
279
+ }
280
+ else {
281
+ rejected += 1;
282
+ rejectionReasons.push(classifyRuntimeInputRejection(runtime, beforeRejected, message));
283
+ }
284
+ }
285
+ return { accepted, rejected, rejectionReasons };
286
+ }
287
+ function applyDeterministicLocalAuthorityCommandBundleEnvelope(timeline, senderSlot, envelope) {
288
+ if (envelope.kind !== 'command-bundle') {
289
+ throw new Error(`Expected command-bundle envelope, received ${envelope.kind}`);
290
+ }
291
+ const commands = envelope.payload.commands;
292
+ if (!Array.isArray(commands)) {
293
+ throw new Error('Command bundle payload requires commands array');
294
+ }
295
+ let accepted = 0;
296
+ let rejected = 0;
297
+ const rejectionReasons = [];
298
+ for (const command of commands) {
299
+ const message = parseCommandMessage(command);
300
+ const rejection = timeline.recordFromSlot(senderSlot, message);
301
+ if (rejection === undefined) {
302
+ accepted += 1;
303
+ }
304
+ else {
305
+ rejected += 1;
306
+ rejectionReasons.push(rejection.reason);
307
+ }
308
+ }
309
+ return { accepted, rejected, rejectionReasons };
310
+ }
311
+ function createDeterministicLocalAuthorityWireReceiver(runtime, options = {}) {
312
+ const seenSequenceKeys = new Set();
313
+ const highestSequenceBySender = new Map();
314
+ const sequenceReplayWindow = readSequenceReplayWindow(options.sequenceReplayWindow);
315
+ const pendingSnapshotTransfers = new Map();
316
+ let lastAckFrame = -1;
317
+ let lastCorrectionFrame = -1;
318
+ let lastSnapshotFrame = -1;
319
+ function reject(kind, reason) {
320
+ return { kind, accepted: 0, rejected: 1, rejectionReasons: [reason], appliedAck: false, appliedCorrection: false, appliedSnapshot: false };
321
+ }
322
+ function sequenceKey(sender, sequence) {
323
+ return `${sender}:${sequence}`;
324
+ }
325
+ function rejectOrReserveSequence(envelope, sender) {
326
+ const key = sequenceKey(sender, envelope.sequence);
327
+ if (seenSequenceKeys.has(key)) {
328
+ return 'replayed-envelope';
329
+ }
330
+ const highestSequence = highestSequenceBySender.get(sender) ?? -1;
331
+ if (envelope.sequence < highestSequence - sequenceReplayWindow) {
332
+ return 'duplicate-sequence';
333
+ }
334
+ return undefined;
335
+ }
336
+ function markSequence(sender, sequence) {
337
+ seenSequenceKeys.add(sequenceKey(sender, sequence));
338
+ const highest = Math.max(highestSequenceBySender.get(sender) ?? -1, sequence);
339
+ highestSequenceBySender.set(sender, highest);
340
+ // Prune this sender's keys below the replay window: anything older is
341
+ // already rejected as duplicate-sequence by the window rule, so retaining
342
+ // it only grows memory without bound (an adversary can advance sequence
343
+ // numbers arbitrarily). Pruning keeps the set at O(window × senders).
344
+ const floor = highest - sequenceReplayWindow;
345
+ const senderPrefix = `${sender}:`;
346
+ for (const key of seenSequenceKeys) {
347
+ if (!key.startsWith(senderPrefix))
348
+ continue;
349
+ if (Number(key.slice(senderPrefix.length)) < floor) {
350
+ seenSequenceKeys.delete(key);
351
+ }
352
+ }
353
+ }
354
+ function rejectAuth(envelope, expectedSenderId) {
355
+ return verifyWireEnvelopeAuth(envelope, expectedSenderId, options.auth);
356
+ }
357
+ return {
358
+ receive(envelope, senderSlot) {
359
+ if (envelope.kind === 'input-bundle') {
360
+ if (senderSlot === undefined) {
361
+ return reject(envelope.kind, 'missing-sender-slot');
362
+ }
363
+ const sender = `slot:${senderSlot}`;
364
+ const authRejection = rejectAuth(envelope, sender);
365
+ if (authRejection !== undefined) {
366
+ return reject(envelope.kind, authRejection);
367
+ }
368
+ const sequenceRejection = rejectOrReserveSequence(envelope, sender);
369
+ if (sequenceRejection !== undefined) {
370
+ return reject(envelope.kind, sequenceRejection);
371
+ }
372
+ const applied = applyDeterministicLocalAuthorityInputBundleEnvelope(runtime, senderSlot, envelope);
373
+ markSequence(sender, envelope.sequence);
374
+ return { ...applied, kind: envelope.kind, appliedAck: false, appliedCorrection: false, appliedSnapshot: false };
375
+ }
376
+ if (envelope.kind === 'command-bundle') {
377
+ if (senderSlot === undefined) {
378
+ return reject(envelope.kind, 'missing-sender-slot');
379
+ }
380
+ if (options.commandTimeline === undefined) {
381
+ return reject(envelope.kind, 'missing-command-timeline');
382
+ }
383
+ const sender = `slot:${senderSlot}`;
384
+ const authRejection = rejectAuth(envelope, sender);
385
+ if (authRejection !== undefined) {
386
+ return reject(envelope.kind, authRejection);
387
+ }
388
+ const sequenceRejection = rejectOrReserveSequence(envelope, sender);
389
+ if (sequenceRejection !== undefined) {
390
+ return reject(envelope.kind, sequenceRejection);
391
+ }
392
+ if (runtime.state.clients[senderSlot]?.spectator === true) {
393
+ markSequence(sender, envelope.sequence);
394
+ return { kind: envelope.kind, accepted: 0, rejected: 1, rejectionReasons: ['spectator-command'], appliedAck: false, appliedCorrection: false, appliedSnapshot: false };
395
+ }
396
+ const applied = applyDeterministicLocalAuthorityCommandBundleEnvelope(options.commandTimeline, senderSlot, envelope);
397
+ markSequence(sender, envelope.sequence);
398
+ return { ...applied, kind: envelope.kind, appliedAck: false, appliedCorrection: false, appliedSnapshot: false };
399
+ }
400
+ if (envelope.kind === 'snapshot-request') {
401
+ if (senderSlot === undefined) {
402
+ return reject(envelope.kind, 'missing-sender-slot');
403
+ }
404
+ const sender = `slot:${senderSlot}`;
405
+ const authRejection = rejectAuth(envelope, sender);
406
+ if (authRejection !== undefined) {
407
+ return reject(envelope.kind, authRejection);
408
+ }
409
+ const sequenceRejection = rejectOrReserveSequence(envelope, sender);
410
+ if (sequenceRejection !== undefined) {
411
+ return reject(envelope.kind, sequenceRejection);
412
+ }
413
+ if (options.snapshotIdentity === undefined) {
414
+ return reject(envelope.kind, 'snapshot-identity-required');
415
+ }
416
+ const requestIdentity = parseSnapshotIdentityPayload(envelope);
417
+ if (!snapshotIdentityMatches(options.snapshotIdentity, requestIdentity)) {
418
+ return reject(envelope.kind, 'snapshot-descriptor-mismatch');
419
+ }
420
+ const minFrame = integerPayloadField(envelope, 'minFrame');
421
+ if (minFrame > runtime.state.frame) {
422
+ return reject(envelope.kind, 'snapshot-min-frame-unavailable');
423
+ }
424
+ markSequence(sender, envelope.sequence);
425
+ return { kind: envelope.kind, accepted: 1, rejected: 0, rejectionReasons: [], appliedAck: false, appliedCorrection: false, appliedSnapshot: false };
426
+ }
427
+ const sender = 'authority';
428
+ const authRejection = rejectAuth(envelope, sender);
429
+ if (authRejection !== undefined) {
430
+ return reject(envelope.kind, authRejection);
431
+ }
432
+ const sequenceRejection = rejectOrReserveSequence(envelope, sender);
433
+ if (sequenceRejection !== undefined) {
434
+ return reject(envelope.kind, sequenceRejection);
435
+ }
436
+ if (envelope.kind === 'ack') {
437
+ const frame = integerPayloadField(envelope, 'frame');
438
+ if (frame < lastAckFrame) {
439
+ return reject(envelope.kind, 'stale-ack');
440
+ }
441
+ markSequence(sender, envelope.sequence);
442
+ lastAckFrame = frame;
443
+ return { kind: envelope.kind, accepted: 1, rejected: 0, rejectionReasons: [], appliedAck: true, appliedCorrection: false, appliedSnapshot: false };
444
+ }
445
+ if (envelope.kind === 'correction') {
446
+ const frame = integerPayloadField(envelope, 'frame');
447
+ if (frame < lastCorrectionFrame) {
448
+ return reject(envelope.kind, 'stale-correction');
449
+ }
450
+ const identityRejection = directSnapshotIdentityRejection(envelope, options.snapshotIdentity);
451
+ if (identityRejection !== undefined) {
452
+ return reject(envelope.kind, identityRejection);
453
+ }
454
+ const snapshot = parseRuntimeSnapshotPayload(envelope, 'snapshot');
455
+ assertSnapshotChecksum(envelope, snapshot);
456
+ runtime.restore(snapshot);
457
+ markSequence(sender, envelope.sequence);
458
+ lastCorrectionFrame = frame;
459
+ return { kind: envelope.kind, accepted: 1, rejected: 0, rejectionReasons: [], appliedAck: false, appliedCorrection: true, appliedSnapshot: false };
460
+ }
461
+ if (envelope.kind === 'snapshot') {
462
+ const frame = integerPayloadField(envelope, 'frame');
463
+ if (frame < lastSnapshotFrame) {
464
+ return reject(envelope.kind, 'stale-snapshot');
465
+ }
466
+ const identityRejection = directSnapshotIdentityRejection(envelope, options.snapshotIdentity);
467
+ if (identityRejection !== undefined) {
468
+ return reject(envelope.kind, identityRejection);
469
+ }
470
+ const snapshot = parseRuntimeSnapshotPayload(envelope, 'snapshot');
471
+ assertSnapshotChecksum(envelope, snapshot);
472
+ runtime.restore(snapshot);
473
+ markSequence(sender, envelope.sequence);
474
+ lastSnapshotFrame = frame;
475
+ return { kind: envelope.kind, accepted: 1, rejected: 0, rejectionReasons: [], appliedAck: false, appliedCorrection: false, appliedSnapshot: true };
476
+ }
477
+ if (envelope.kind === 'snapshot-offer') {
478
+ const offer = parseSnapshotOffer(envelope);
479
+ if (options.snapshotIdentity === undefined) {
480
+ return reject(envelope.kind, 'snapshot-identity-required');
481
+ }
482
+ if (!snapshotIdentityMatches(options.snapshotIdentity, offer.identity)) {
483
+ return reject(envelope.kind, 'snapshot-descriptor-mismatch');
484
+ }
485
+ const maxAge = options.maxSnapshotAgeFrames ?? 256;
486
+ if (offer.frame < runtime.state.frame - maxAge) {
487
+ return reject(envelope.kind, 'stale-snapshot-offer');
488
+ }
489
+ pendingSnapshotTransfers.set(offer.transferId, {
490
+ ...offer,
491
+ chunks: new Map(),
492
+ });
493
+ markSequence(sender, envelope.sequence);
494
+ return { kind: envelope.kind, accepted: 1, rejected: 0, rejectionReasons: [], appliedAck: false, appliedCorrection: false, appliedSnapshot: false };
495
+ }
496
+ if (envelope.kind === 'snapshot-chunk') {
497
+ const transferId = stringPayloadField(envelope, 'transferId');
498
+ const transfer = pendingSnapshotTransfers.get(transferId);
499
+ if (transfer === undefined) {
500
+ return reject(envelope.kind, 'unknown-snapshot-transfer');
501
+ }
502
+ const chunkIndex = integerPayloadField(envelope, 'chunkIndex');
503
+ if (chunkIndex < 0 || chunkIndex >= transfer.chunkCount) {
504
+ return reject(envelope.kind, 'snapshot-chunk-index-out-of-range');
505
+ }
506
+ if (transfer.chunks.has(chunkIndex)) {
507
+ return reject(envelope.kind, 'duplicate-snapshot-chunk');
508
+ }
509
+ const chunk = stringPayloadField(envelope, 'chunk');
510
+ const chunkChecksum = stringPayloadField(envelope, 'chunkChecksum');
511
+ if (transfer.chunkChecksums[chunkIndex] !== chunkChecksum || (0, canonical_js_1.defaultChecksum)(chunk) !== chunkChecksum) {
512
+ return reject(envelope.kind, 'snapshot-chunk-checksum-mismatch');
513
+ }
514
+ transfer.chunks.set(chunkIndex, chunk);
515
+ markSequence(sender, envelope.sequence);
516
+ return { kind: envelope.kind, accepted: 1, rejected: 0, rejectionReasons: [], appliedAck: false, appliedCorrection: false, appliedSnapshot: false };
517
+ }
518
+ const transferId = stringPayloadField(envelope, 'transferId');
519
+ const transfer = pendingSnapshotTransfers.get(transferId);
520
+ if (transfer === undefined) {
521
+ return reject(envelope.kind, 'unknown-snapshot-transfer');
522
+ }
523
+ if (transfer.chunks.size !== transfer.chunkCount) {
524
+ return reject(envelope.kind, 'missing-snapshot-chunk');
525
+ }
526
+ const snapshotBytes = Array.from({ length: transfer.chunkCount }, (_, index) => transfer.chunks.get(index)).join('');
527
+ if (snapshotBytes.length !== transfer.totalBytes) {
528
+ return reject(envelope.kind, 'snapshot-byte-length-mismatch');
529
+ }
530
+ const parsed = parseSnapshotBytes(snapshotBytes);
531
+ if ((0, canonical_js_1.defaultChecksum)(parsed) !== transfer.snapshotChecksum) {
532
+ return reject(envelope.kind, 'snapshot-transfer-checksum-mismatch');
533
+ }
534
+ runtime.restore(parsed);
535
+ pendingSnapshotTransfers.delete(transferId);
536
+ markSequence(sender, envelope.sequence);
537
+ lastSnapshotFrame = Math.max(lastSnapshotFrame, transfer.frame);
538
+ return { kind: envelope.kind, accepted: 1, rejected: 0, rejectionReasons: [], appliedAck: false, appliedCorrection: false, appliedSnapshot: true };
539
+ },
540
+ snapshot() {
541
+ const perSenderEntries = [...highestSequenceBySender.entries()].sort((left, right) => compareStrings(left[0], right[0]));
542
+ const perSenderHighestSequences = Object.fromEntries(perSenderEntries);
543
+ const seenSequences = [...new Set([...seenSequenceKeys].map((key) => Number(key.slice(key.lastIndexOf(':') + 1))))].sort(compareNumbers);
544
+ const state = {
545
+ highestSequence: perSenderEntries.reduce((highest, entry) => Math.max(highest, entry[1]), -1),
546
+ seenSequences,
547
+ perSenderHighestSequences,
548
+ lastAckFrame,
549
+ lastCorrectionFrame,
550
+ lastSnapshotFrame,
551
+ };
552
+ return { ...state, checksum: (0, canonical_js_1.defaultChecksum)(state) };
553
+ },
554
+ };
555
+ }
556
+ function runDeterministicLocalAuthorityWireFixture(players, frames, seed) {
557
+ const runtime = (0, local_authority_js_1.createDeterministicLocalAuthorityRuntime)(players, { maxFrameLead: 16 });
558
+ const commandChannels = [
559
+ { id: 'ability', reliable: true, allowPrediction: true, maxPayloadBytes: 128, maxFrameLead: 16 },
560
+ { id: 'emote', reliable: false, allowPrediction: false, maxPayloadBytes: 64, minFrameLead: 1, maxFrameLead: 8 },
561
+ ];
562
+ const commandTimeline = (0, commands_js_1.createDeterministicCommandTimeline)({
563
+ playerCount: players,
564
+ currentFrame: 0,
565
+ channels: commandChannels,
566
+ validAssetRefs: ['ability.dash', 'emote.wave'],
567
+ validatePayload(command) {
568
+ return typeof command.payload === 'object' && command.payload !== null && !Array.isArray(command.payload);
569
+ },
570
+ });
571
+ const snapshotIdentity = {
572
+ descriptorHash: (0, canonical_js_1.defaultChecksum)({ kind: 'local-authority-wire-fixture', players, seed }),
573
+ schemaHash: 'local-authority-wire-schema@1',
574
+ assetDbHash: 'local-authority-wire-assets@1',
575
+ configHash: 'local-authority-wire-config@1',
576
+ };
577
+ const auth = createWireFixtureAuth(players, seed);
578
+ const receiver = createDeterministicLocalAuthorityWireReceiver(runtime, {
579
+ commandTimeline,
580
+ snapshotIdentity,
581
+ maxSnapshotAgeFrames: Math.max(1, Math.min(64, Math.floor(frames / 2))),
582
+ auth: { requireAuth: true, authorityKey: auth.authorityKey, slotKeys: auth.slotKeys },
583
+ });
584
+ let sequence = 0;
585
+ let encodedEnvelopes = 0;
586
+ let decodedEnvelopes = 0;
587
+ let wireAuthSignedEnvelopes = 0;
588
+ let acceptedInputs = 0;
589
+ let rejectedInputs = 0;
590
+ let acceptedCommands = 0;
591
+ let rejectedCommands = 0;
592
+ let ackEnvelopes = 0;
593
+ let commandEnvelopes = 0;
594
+ let correctionEnvelopes = 0;
595
+ let snapshotEnvelopes = 0;
596
+ let snapshotRequestEnvelopes = 0;
597
+ let snapshotOfferEnvelopes = 0;
598
+ let snapshotChunkEnvelopes = 0;
599
+ let snapshotCompleteEnvelopes = 0;
600
+ let correctionsApplied = 0;
601
+ let snapshotsApplied = 0;
602
+ let snapshotNegotiationCompletesApplied = 0;
603
+ const rejectionReasons = [];
604
+ for (let frame = 0; frame < frames; frame += 1) {
605
+ commandTimeline.setCurrentFrame(frame);
606
+ for (let slot = 0; slot < players; slot += 1) {
607
+ const encoded = createDeterministicLocalAuthorityInputBundleEnvelope(sequence, [{
608
+ slot,
609
+ frame,
610
+ value: seed + frame * 31 + slot,
611
+ delay: slot % 4,
612
+ }], wireFixtureSlotSign(auth, slot));
613
+ sequence += 1;
614
+ encodedEnvelopes += 1;
615
+ const decoded = decodeDeterministicLocalAuthorityWireEnvelope(encoded);
616
+ wireAuthSignedEnvelopes += 1;
617
+ decodedEnvelopes += 1;
618
+ const applied = receiver.receive(decoded, slot);
619
+ acceptedInputs += applied.accepted;
620
+ rejectedInputs += applied.rejected;
621
+ rejectionReasons.push(...applied.rejectionReasons);
622
+ if ((frame + slot + seed) % 19 === 0) {
623
+ const commandFrame = frame + 1;
624
+ const command = {
625
+ id: `wire:${slot}:${frame}:ability`,
626
+ slot,
627
+ frame: commandFrame,
628
+ channelId: 'ability',
629
+ payload: { assetRef: 'ability.dash', power: (slot + frame) % 7 },
630
+ predicted: frame % 2 === 0,
631
+ };
632
+ const commandEnvelope = createDeterministicLocalAuthorityCommandBundleEnvelope(sequence, [command], wireFixtureSlotSign(auth, slot));
633
+ sequence += 1;
634
+ encodedEnvelopes += 1;
635
+ const decodedCommand = decodeDeterministicLocalAuthorityWireEnvelope(commandEnvelope);
636
+ wireAuthSignedEnvelopes += 1;
637
+ decodedEnvelopes += 1;
638
+ commandEnvelopes += 1;
639
+ const appliedCommand = receiver.receive(decodedCommand, slot);
640
+ acceptedCommands += appliedCommand.accepted;
641
+ rejectedCommands += appliedCommand.rejected;
642
+ rejectionReasons.push(...appliedCommand.rejectionReasons);
643
+ }
644
+ }
645
+ if (frame % 8 === 0) {
646
+ const ack = decodeDeterministicLocalAuthorityWireEnvelope(createDeterministicLocalAuthorityAckEnvelope(sequence, runtime, wireFixtureAuthoritySign(auth)));
647
+ wireAuthSignedEnvelopes += 1;
648
+ const appliedAck = receiver.receive(ack);
649
+ rejectionReasons.push(...appliedAck.rejectionReasons);
650
+ sequence += 1;
651
+ encodedEnvelopes += 1;
652
+ decodedEnvelopes += 1;
653
+ ackEnvelopes += 1;
654
+ }
655
+ if (frame % 16 === 0) {
656
+ const correction = decodeDeterministicLocalAuthorityWireEnvelope(createDeterministicLocalAuthorityCorrectionEnvelope(sequence, runtime, snapshotIdentity, wireFixtureAuthoritySign(auth)));
657
+ wireAuthSignedEnvelopes += 1;
658
+ const appliedCorrection = receiver.receive(correction);
659
+ sequence += 1;
660
+ encodedEnvelopes += 1;
661
+ decodedEnvelopes += 1;
662
+ correctionEnvelopes += 1;
663
+ correctionsApplied += Number(appliedCorrection.appliedCorrection);
664
+ rejectionReasons.push(...appliedCorrection.rejectionReasons);
665
+ }
666
+ runtime.step();
667
+ }
668
+ const finalSnapshot = decodeDeterministicLocalAuthorityWireEnvelope(createDeterministicLocalAuthoritySnapshotEnvelope(sequence, runtime, snapshotIdentity, wireFixtureAuthoritySign(auth)));
669
+ wireAuthSignedEnvelopes += 1;
670
+ const appliedSnapshot = receiver.receive(finalSnapshot);
671
+ encodedEnvelopes += 1;
672
+ decodedEnvelopes += 1;
673
+ snapshotEnvelopes += 1;
674
+ snapshotsApplied += Number(appliedSnapshot.appliedSnapshot);
675
+ rejectionReasons.push(...appliedSnapshot.rejectionReasons);
676
+ const transfer = createDeterministicLocalAuthoritySnapshotTransfer(runtime, snapshotIdentity, `transfer:${seed}:${frames}`, 512);
677
+ const snapshotRequest = receiver.receive(decodeDeterministicLocalAuthorityWireEnvelope(createDeterministicLocalAuthoritySnapshotRequestEnvelope(sequence + 1, snapshotIdentity, runtime.state.frame, wireFixtureSlotSign(auth, 0))), 0);
678
+ wireAuthSignedEnvelopes += 1;
679
+ encodedEnvelopes += 1;
680
+ decodedEnvelopes += 1;
681
+ snapshotRequestEnvelopes += 1;
682
+ rejectionReasons.push(...snapshotRequest.rejectionReasons);
683
+ sequence += 1;
684
+ const snapshotOffer = receiver.receive(decodeDeterministicLocalAuthorityWireEnvelope(createDeterministicLocalAuthoritySnapshotOfferEnvelope(sequence + 1, transfer, wireFixtureAuthoritySign(auth))));
685
+ wireAuthSignedEnvelopes += 1;
686
+ encodedEnvelopes += 1;
687
+ decodedEnvelopes += 1;
688
+ snapshotOfferEnvelopes += 1;
689
+ rejectionReasons.push(...snapshotOffer.rejectionReasons);
690
+ sequence += 1;
691
+ for (let chunkIndex = 0; chunkIndex < transfer.chunks.length; chunkIndex += 1) {
692
+ const chunk = receiver.receive(decodeDeterministicLocalAuthorityWireEnvelope(createDeterministicLocalAuthoritySnapshotChunkEnvelope(sequence + 1, transfer, chunkIndex, wireFixtureAuthoritySign(auth))));
693
+ wireAuthSignedEnvelopes += 1;
694
+ encodedEnvelopes += 1;
695
+ decodedEnvelopes += 1;
696
+ snapshotChunkEnvelopes += 1;
697
+ rejectionReasons.push(...chunk.rejectionReasons);
698
+ sequence += 1;
699
+ }
700
+ const snapshotComplete = receiver.receive(decodeDeterministicLocalAuthorityWireEnvelope(createDeterministicLocalAuthoritySnapshotCompleteEnvelope(sequence + 1, transfer.transferId, wireFixtureAuthoritySign(auth))));
701
+ wireAuthSignedEnvelopes += 1;
702
+ encodedEnvelopes += 1;
703
+ decodedEnvelopes += 1;
704
+ snapshotCompleteEnvelopes += 1;
705
+ snapshotsApplied += Number(snapshotComplete.appliedSnapshot);
706
+ snapshotNegotiationCompletesApplied += Number(snapshotComplete.appliedSnapshot);
707
+ rejectionReasons.push(...snapshotComplete.rejectionReasons);
708
+ sequence += 1;
709
+ const hostile = runHostileWireCases(runtime, receiver, players, frames, sequence + 1, snapshotIdentity, auth);
710
+ const sequenceWindow = runWireSequenceWindowFixture(auth);
711
+ rejectedInputs += hostile.rejectedInputs;
712
+ rejectedCommands += hostile.rejectedCommands;
713
+ wireAuthSignedEnvelopes += hostile.signedEnvelopes;
714
+ rejectionReasons.push(...hostile.rejectionReasons);
715
+ rejectionReasons.push(...sequenceWindow.rejectionReasons);
716
+ const convergenceDrainFrames = 4;
717
+ for (let drain = 0; drain < convergenceDrainFrames; drain += 1) {
718
+ runtime.step();
719
+ }
720
+ const checksums = runtime.checksums();
721
+ const metrics = {
722
+ players,
723
+ frames,
724
+ encodedEnvelopes,
725
+ decodedEnvelopes,
726
+ acceptedInputs,
727
+ rejectedInputs,
728
+ acceptedCommands,
729
+ rejectedCommands,
730
+ malformedRejected: hostile.malformedRejected,
731
+ forgedSlotRejected: rejectionReasons.filter((reason) => reason === 'forged-slot').length,
732
+ staleFrameRejected: rejectionReasons.filter((reason) => reason === 'stale-frame').length,
733
+ futureFrameRejected: rejectionReasons.filter((reason) => reason === 'future-frame').length,
734
+ oversizedRejected: hostile.oversizedRejected,
735
+ duplicateInputRejected: rejectionReasons.filter((reason) => reason === 'duplicate-input').length,
736
+ invalidInputIdRejected: rejectionReasons.filter((reason) => reason === 'invalid-input-id').length,
737
+ ackEnvelopes,
738
+ commandEnvelopes,
739
+ correctionEnvelopes,
740
+ snapshotEnvelopes,
741
+ snapshotRequestEnvelopes,
742
+ snapshotOfferEnvelopes,
743
+ snapshotChunkEnvelopes,
744
+ snapshotCompleteEnvelopes,
745
+ snapshotNegotiationCompletesApplied,
746
+ snapshotNegotiationRejected: rejectionReasons.filter((reason) => reason.startsWith('snapshot-') || reason === 'missing-snapshot-chunk' || reason === 'stale-snapshot-offer').length,
747
+ snapshotDescriptorMismatchRejected: rejectionReasons.filter((reason) => reason === 'snapshot-descriptor-mismatch').length,
748
+ snapshotChunkChecksumRejected: rejectionReasons.filter((reason) => reason === 'snapshot-chunk-checksum-mismatch').length,
749
+ snapshotMissingChunkRejected: rejectionReasons.filter((reason) => reason === 'missing-snapshot-chunk').length,
750
+ staleSnapshotOfferRejected: rejectionReasons.filter((reason) => reason === 'stale-snapshot-offer').length,
751
+ reorderedSequenceAccepted: sequenceWindow.reorderedSequenceAccepted,
752
+ duplicateSequenceRejected: rejectionReasons.filter((reason) => reason === 'duplicate-sequence').length,
753
+ replayedEnvelopeRejected: rejectionReasons.filter((reason) => reason === 'replayed-envelope').length,
754
+ staleAckRejected: rejectionReasons.filter((reason) => reason === 'stale-ack').length,
755
+ staleCorrectionRejected: rejectionReasons.filter((reason) => reason === 'stale-correction').length,
756
+ staleSnapshotRejected: rejectionReasons.filter((reason) => reason === 'stale-snapshot').length,
757
+ wireAuthSignedEnvelopes,
758
+ wireAuthMissingRejected: rejectionReasons.filter((reason) => reason === 'missing-wire-auth').length,
759
+ wireAuthSenderMismatchRejected: rejectionReasons.filter((reason) => reason === 'wire-auth-sender-mismatch').length,
760
+ wireAuthSignatureRejected: rejectionReasons.filter((reason) => reason === 'wire-auth-signature-mismatch').length,
761
+ wireAuthHmacSha256Covered: isKnownHmacSha256VectorCovered(),
762
+ forgedAuthorityCorrectionRejected: hostile.forgedAuthorityCorrectionRejected,
763
+ forgedAuthoritySnapshotRejected: hostile.forgedAuthoritySnapshotRejected,
764
+ snapshotIdentityRequiredRejected: hostile.snapshotIdentityRequiredRejected,
765
+ correctionsApplied,
766
+ snapshotsApplied,
767
+ convergenceDrainFrames,
768
+ authorityProjectionChecksum: checksums.authorityProjectionChecksum,
769
+ clientProjectionChecksum: checksums.clientProjectionChecksum,
770
+ convergence: checksums.convergence,
771
+ };
772
+ return { ...metrics, checksum: (0, canonical_js_1.defaultChecksum)(metrics) };
773
+ }
774
+ function runHostileWireCases(runtime, receiver, players, frames, sequence, snapshotIdentity, auth) {
775
+ let malformedRejected = 0;
776
+ let oversizedRejected = 0;
777
+ let rejectedInputs = 0;
778
+ let rejectedCommands = 0;
779
+ let signedEnvelopes = 0;
780
+ let forgedAuthorityCorrectionRejected = 0;
781
+ let forgedAuthoritySnapshotRejected = 0;
782
+ let snapshotIdentityRequiredRejected = 0;
783
+ const rejectionReasons = [];
784
+ const malformedCases = [
785
+ '{bad-json',
786
+ (0, canonical_js_1.canonicalStringify)({ schemaVersion: 2, protocol: exports.deterministicLocalAuthorityWireProtocol, kind: 'input-bundle', sequence, payload: {}, checksum: 'x' }),
787
+ (0, canonical_js_1.canonicalStringify)({ schemaVersion: 1, protocol: exports.deterministicLocalAuthorityWireProtocol, kind: 'input-bundle', sequence, payload: {}, checksum: 'x' }),
788
+ ];
789
+ for (const encoded of malformedCases) {
790
+ try {
791
+ decodeDeterministicLocalAuthorityWireEnvelope(encoded);
792
+ }
793
+ catch {
794
+ malformedRejected += 1;
795
+ }
796
+ }
797
+ const hostileBundles = [
798
+ { senderSlot: 0, inputs: [{ slot: 1, frame: runtime.state.frame, value: 1 }] },
799
+ { senderSlot: 0, inputs: [{ slot: 0, frame: Math.max(0, runtime.state.frame - 2), value: 1 }] },
800
+ { senderSlot: 0, inputs: [{ slot: 0, frame: runtime.state.frame + frames + 128, value: 1 }] },
801
+ { senderSlot: 0, inputs: [{ slot: 0, frame: Math.max(0, runtime.state.frame - 1), value: 1 }] },
802
+ { senderSlot: 0, inputs: [{ slot: 0, frame: runtime.state.frame, value: 1, inputId: 'invalid input id' }] },
803
+ ];
804
+ let nextSequence = sequence + malformedCases.length;
805
+ const missingAuth = decodeDeterministicLocalAuthorityWireEnvelope(createDeterministicLocalAuthorityInputBundleEnvelope(nextSequence, [
806
+ { slot: 0, frame: runtime.state.frame, value: 101 },
807
+ ]));
808
+ nextSequence += 1;
809
+ const missingAuthResult = receiver.receive(missingAuth, 0);
810
+ rejectedInputs += missingAuthResult.rejected;
811
+ rejectionReasons.push(...missingAuthResult.rejectionReasons);
812
+ const senderMismatch = decodeDeterministicLocalAuthorityWireEnvelope(createDeterministicLocalAuthorityInputBundleEnvelope(nextSequence, [
813
+ { slot: 0, frame: runtime.state.frame, value: 102 },
814
+ ], wireFixtureSlotSign(auth, 1)));
815
+ signedEnvelopes += 1;
816
+ nextSequence += 1;
817
+ const senderMismatchResult = receiver.receive(senderMismatch, 0);
818
+ rejectedInputs += senderMismatchResult.rejected;
819
+ rejectionReasons.push(...senderMismatchResult.rejectionReasons);
820
+ const badSignature = decodeDeterministicLocalAuthorityWireEnvelope(createDeterministicLocalAuthorityInputBundleEnvelope(nextSequence, [
821
+ { slot: 0, frame: runtime.state.frame, value: 103 },
822
+ ], { senderId: 'slot:0', key: `${wireFixtureSlotSign(auth, 0).key}:wrong` }));
823
+ signedEnvelopes += 1;
824
+ nextSequence += 1;
825
+ const badSignatureResult = receiver.receive(badSignature, 0);
826
+ rejectedInputs += badSignatureResult.rejected;
827
+ rejectionReasons.push(...badSignatureResult.rejectionReasons);
828
+ for (const hostile of hostileBundles) {
829
+ const decoded = decodeDeterministicLocalAuthorityWireEnvelope(createDeterministicLocalAuthorityInputBundleEnvelope(nextSequence, hostile.inputs, wireFixtureSlotSign(auth, hostile.senderSlot)));
830
+ signedEnvelopes += 1;
831
+ nextSequence += 1;
832
+ const applied = receiver.receive(decoded, hostile.senderSlot);
833
+ rejectedInputs += applied.rejected;
834
+ rejectionReasons.push(...applied.rejectionReasons);
835
+ }
836
+ const gapAck = decodeDeterministicLocalAuthorityWireEnvelope(createDeterministicLocalAuthorityAckEnvelope(nextSequence + 1, runtime, wireFixtureAuthoritySign(auth)));
837
+ signedEnvelopes += 1;
838
+ const gapAckResult = receiver.receive(gapAck);
839
+ rejectedInputs += gapAckResult.rejected;
840
+ rejectionReasons.push(...gapAckResult.rejectionReasons);
841
+ const gapInput = decodeDeterministicLocalAuthorityWireEnvelope(createDeterministicLocalAuthorityInputBundleEnvelope(nextSequence + 1, [
842
+ { slot: 0, frame: runtime.state.frame, value: 98 },
843
+ ], wireFixtureSlotSign(auth, 0)));
844
+ signedEnvelopes += 1;
845
+ const gapInputResult = receiver.receive(gapInput, 0);
846
+ rejectedInputs += gapInputResult.rejected;
847
+ rejectionReasons.push(...gapInputResult.rejectionReasons);
848
+ const duplicateSequence = decodeDeterministicLocalAuthorityWireEnvelope(createDeterministicLocalAuthorityInputBundleEnvelope(nextSequence, [
849
+ { slot: 0, frame: runtime.state.frame, value: 99 },
850
+ ], wireFixtureSlotSign(auth, 0)));
851
+ signedEnvelopes += 1;
852
+ nextSequence += 2;
853
+ const duplicateSequenceResult = receiver.receive(duplicateSequence, 0);
854
+ rejectedInputs += duplicateSequenceResult.rejected;
855
+ rejectionReasons.push(...duplicateSequenceResult.rejectionReasons);
856
+ const currentAck = decodeDeterministicLocalAuthorityWireEnvelope(createDeterministicLocalAuthorityAckEnvelope(nextSequence, runtime, wireFixtureAuthoritySign(auth)));
857
+ signedEnvelopes += 1;
858
+ nextSequence += 1;
859
+ const currentAckResult = receiver.receive(currentAck);
860
+ const replayedAckResult = receiver.receive(currentAck);
861
+ const staleAck = decodeDeterministicLocalAuthorityWireEnvelope(encodeDeterministicLocalAuthorityWireEnvelope({
862
+ kind: 'ack',
863
+ sequence: nextSequence,
864
+ payload: { checksums: runtime.checksums(), frame: Math.max(0, runtime.state.frame - 1) },
865
+ }, wireFixtureAuthoritySign(auth)));
866
+ signedEnvelopes += 1;
867
+ nextSequence += 1;
868
+ const staleAckResult = receiver.receive(staleAck);
869
+ rejectedInputs += currentAckResult.rejected + replayedAckResult.rejected + staleAckResult.rejected;
870
+ rejectionReasons.push(...currentAckResult.rejectionReasons, ...replayedAckResult.rejectionReasons, ...staleAckResult.rejectionReasons);
871
+ const currentCorrection = decodeDeterministicLocalAuthorityWireEnvelope(createDeterministicLocalAuthorityCorrectionEnvelope(nextSequence, runtime, snapshotIdentity, wireFixtureAuthoritySign(auth)));
872
+ signedEnvelopes += 1;
873
+ nextSequence += 1;
874
+ const forgedCorrection = decodeDeterministicLocalAuthorityWireEnvelope(createDeterministicLocalAuthorityCorrectionEnvelope(nextSequence, runtime, snapshotIdentity, wireFixtureSlotSign(auth, 0)));
875
+ signedEnvelopes += 1;
876
+ nextSequence += 1;
877
+ const staleCorrection = decodeDeterministicLocalAuthorityWireEnvelope(encodeDeterministicLocalAuthorityWireEnvelope({
878
+ kind: 'correction',
879
+ sequence: nextSequence,
880
+ payload: { ...currentCorrection.payload, frame: Math.max(0, Number(currentCorrection.payload.frame) - 1) },
881
+ }, wireFixtureAuthoritySign(auth)));
882
+ signedEnvelopes += 1;
883
+ nextSequence += 1;
884
+ const currentCorrectionResult = receiver.receive(currentCorrection);
885
+ const forgedCorrectionResult = receiver.receive(forgedCorrection);
886
+ const staleCorrectionResult = receiver.receive(staleCorrection);
887
+ forgedAuthorityCorrectionRejected += Number(forgedCorrectionResult.rejectionReasons.includes('wire-auth-sender-mismatch'));
888
+ rejectedInputs += currentCorrectionResult.rejected + forgedCorrectionResult.rejected + staleCorrectionResult.rejected;
889
+ rejectionReasons.push(...currentCorrectionResult.rejectionReasons, ...forgedCorrectionResult.rejectionReasons, ...staleCorrectionResult.rejectionReasons);
890
+ const currentSnapshot = decodeDeterministicLocalAuthorityWireEnvelope(createDeterministicLocalAuthoritySnapshotEnvelope(nextSequence, runtime, snapshotIdentity, wireFixtureAuthoritySign(auth)));
891
+ signedEnvelopes += 1;
892
+ nextSequence += 1;
893
+ const forgedSnapshot = decodeDeterministicLocalAuthorityWireEnvelope(createDeterministicLocalAuthoritySnapshotEnvelope(nextSequence, runtime, snapshotIdentity, wireFixtureSlotSign(auth, 0)));
894
+ signedEnvelopes += 1;
895
+ nextSequence += 1;
896
+ const staleSnapshot = decodeDeterministicLocalAuthorityWireEnvelope(encodeDeterministicLocalAuthorityWireEnvelope({
897
+ kind: 'snapshot',
898
+ sequence: nextSequence,
899
+ payload: { ...currentSnapshot.payload, frame: Math.max(0, Number(currentSnapshot.payload.frame) - 1) },
900
+ }, wireFixtureAuthoritySign(auth)));
901
+ signedEnvelopes += 1;
902
+ nextSequence += 1;
903
+ const currentSnapshotResult = receiver.receive(currentSnapshot);
904
+ const forgedSnapshotResult = receiver.receive(forgedSnapshot);
905
+ const staleSnapshotResult = receiver.receive(staleSnapshot);
906
+ forgedAuthoritySnapshotRejected += Number(forgedSnapshotResult.rejectionReasons.includes('wire-auth-sender-mismatch'));
907
+ rejectedInputs += currentSnapshotResult.rejected + forgedSnapshotResult.rejected + staleSnapshotResult.rejected;
908
+ rejectionReasons.push(...currentSnapshotResult.rejectionReasons, ...forgedSnapshotResult.rejectionReasons, ...staleSnapshotResult.rejectionReasons);
909
+ const hostileCommands = [
910
+ { senderSlot: 0, commands: [{ id: 'wire:hostile:forged', slot: 1, frame: runtime.state.frame, channelId: 'ability', payload: { assetRef: 'ability.dash' } }] },
911
+ { senderSlot: 0, commands: [{ id: 'wire:hostile:bad-asset', slot: 0, frame: runtime.state.frame, channelId: 'ability', payload: { assetRef: 'missing.asset' } }] },
912
+ { senderSlot: 0, commands: [{ id: 'wire:hostile:bad-schema', slot: 0, frame: runtime.state.frame, channelId: 'ability', payload: 'bad' }] },
913
+ { senderSlot: 0, commands: [{ id: 'wire:hostile:future', slot: 0, frame: runtime.state.frame + frames + 128, channelId: 'ability', payload: { assetRef: 'ability.dash' } }] },
914
+ ];
915
+ for (const hostile of hostileCommands) {
916
+ const decoded = decodeDeterministicLocalAuthorityWireEnvelope(createDeterministicLocalAuthorityCommandBundleEnvelope(nextSequence, hostile.commands, wireFixtureSlotSign(auth, hostile.senderSlot)));
917
+ signedEnvelopes += 1;
918
+ nextSequence += 1;
919
+ const applied = receiver.receive(decoded, hostile.senderSlot);
920
+ rejectedCommands += applied.rejected;
921
+ rejectionReasons.push(...applied.rejectionReasons);
922
+ }
923
+ const wrongIdentity = { ...snapshotIdentity, descriptorHash: `${snapshotIdentity.descriptorHash}:wrong` };
924
+ const descriptorMismatch = receiver.receive(decodeDeterministicLocalAuthorityWireEnvelope(createDeterministicLocalAuthoritySnapshotRequestEnvelope(nextSequence, wrongIdentity, runtime.state.frame, wireFixtureSlotSign(auth, 0))), 0);
925
+ signedEnvelopes += 1;
926
+ nextSequence += 1;
927
+ rejectedInputs += descriptorMismatch.rejected;
928
+ rejectionReasons.push(...descriptorMismatch.rejectionReasons);
929
+ const staleTransfer = createDeterministicLocalAuthoritySnapshotTransfer(runtime, snapshotIdentity, `hostile-stale:${nextSequence}`, 512);
930
+ const staleOffer = receiver.receive(decodeDeterministicLocalAuthorityWireEnvelope(encodeDeterministicLocalAuthorityWireEnvelope({
931
+ kind: 'snapshot-offer',
932
+ sequence: nextSequence,
933
+ payload: {
934
+ transferId: staleTransfer.transferId,
935
+ identity: staleTransfer.identity,
936
+ frame: Math.max(0, runtime.state.frame - frames - 128),
937
+ snapshotChecksum: staleTransfer.snapshotChecksum,
938
+ chunkCount: staleTransfer.chunks.length,
939
+ totalBytes: staleTransfer.snapshotBytes.length,
940
+ chunkChecksums: staleTransfer.chunkChecksums,
941
+ },
942
+ }, wireFixtureAuthoritySign(auth))));
943
+ signedEnvelopes += 1;
944
+ nextSequence += 1;
945
+ rejectedInputs += staleOffer.rejected;
946
+ rejectionReasons.push(...staleOffer.rejectionReasons);
947
+ const missingChunkTransfer = createDeterministicLocalAuthoritySnapshotTransfer(runtime, snapshotIdentity, `hostile-missing:${nextSequence}`, 64);
948
+ const missingOffer = receiver.receive(decodeDeterministicLocalAuthorityWireEnvelope(createDeterministicLocalAuthoritySnapshotOfferEnvelope(nextSequence, missingChunkTransfer, wireFixtureAuthoritySign(auth))));
949
+ signedEnvelopes += 1;
950
+ nextSequence += 1;
951
+ const missingComplete = receiver.receive(decodeDeterministicLocalAuthorityWireEnvelope(createDeterministicLocalAuthoritySnapshotCompleteEnvelope(nextSequence, missingChunkTransfer.transferId, wireFixtureAuthoritySign(auth))));
952
+ signedEnvelopes += 1;
953
+ nextSequence += 1;
954
+ rejectedInputs += missingOffer.rejected + missingComplete.rejected;
955
+ rejectionReasons.push(...missingOffer.rejectionReasons, ...missingComplete.rejectionReasons);
956
+ const badChunkTransfer = createDeterministicLocalAuthoritySnapshotTransfer(runtime, snapshotIdentity, `hostile-bad-chunk:${nextSequence}`, 512);
957
+ const badOffer = receiver.receive(decodeDeterministicLocalAuthorityWireEnvelope(createDeterministicLocalAuthoritySnapshotOfferEnvelope(nextSequence, badChunkTransfer, wireFixtureAuthoritySign(auth))));
958
+ signedEnvelopes += 1;
959
+ nextSequence += 1;
960
+ const badChunk = receiver.receive(decodeDeterministicLocalAuthorityWireEnvelope(encodeDeterministicLocalAuthorityWireEnvelope({
961
+ kind: 'snapshot-chunk',
962
+ sequence: nextSequence,
963
+ payload: {
964
+ transferId: badChunkTransfer.transferId,
965
+ chunkIndex: 0,
966
+ chunk: `${badChunkTransfer.chunks[0]}x`,
967
+ chunkChecksum: badChunkTransfer.chunkChecksums[0],
968
+ },
969
+ }, wireFixtureAuthoritySign(auth))));
970
+ signedEnvelopes += 1;
971
+ nextSequence += 1;
972
+ rejectedInputs += badOffer.rejected + badChunk.rejected;
973
+ rejectionReasons.push(...badOffer.rejectionReasons, ...badChunk.rejectionReasons);
974
+ const identitylessRuntime = (0, local_authority_js_1.createDeterministicLocalAuthorityRuntime)(players, { maxFrameLead: 16 });
975
+ const identitylessReceiver = createDeterministicLocalAuthorityWireReceiver(identitylessRuntime, {
976
+ auth: { requireAuth: true, authorityKey: auth.authorityKey, slotKeys: auth.slotKeys },
977
+ });
978
+ const identitylessTransfer = createDeterministicLocalAuthoritySnapshotTransfer(identitylessRuntime, snapshotIdentity, `hostile-no-identity:${nextSequence}`, 512);
979
+ const identitylessOffer = identitylessReceiver.receive(decodeDeterministicLocalAuthorityWireEnvelope(createDeterministicLocalAuthoritySnapshotOfferEnvelope(nextSequence, identitylessTransfer, wireFixtureAuthoritySign(auth))));
980
+ signedEnvelopes += 1;
981
+ nextSequence += 1;
982
+ snapshotIdentityRequiredRejected += Number(identitylessOffer.rejectionReasons.includes('snapshot-identity-required'));
983
+ rejectedInputs += identitylessOffer.rejected;
984
+ rejectionReasons.push(...identitylessOffer.rejectionReasons);
985
+ try {
986
+ decodeDeterministicLocalAuthorityWireEnvelope(encodeDeterministicLocalAuthorityWireEnvelope({
987
+ kind: 'input-bundle',
988
+ sequence: nextSequence,
989
+ payload: { inputs: [{ slot: 0, frame: runtime.state.frame, value: 1, blob: 'x'.repeat(70_000) }] },
990
+ }));
991
+ }
992
+ catch {
993
+ oversizedRejected += 1;
994
+ }
995
+ return {
996
+ rejectedInputs,
997
+ rejectedCommands,
998
+ malformedRejected,
999
+ oversizedRejected,
1000
+ signedEnvelopes,
1001
+ forgedAuthorityCorrectionRejected,
1002
+ forgedAuthoritySnapshotRejected,
1003
+ snapshotIdentityRequiredRejected,
1004
+ rejectionReasons,
1005
+ };
1006
+ }
1007
+ function runWireSequenceWindowFixture(auth) {
1008
+ const runtime = (0, local_authority_js_1.createDeterministicLocalAuthorityRuntime)(1, { maxFrameLead: 8 });
1009
+ const receiver = createDeterministicLocalAuthorityWireReceiver(runtime, {
1010
+ sequenceReplayWindow: 2,
1011
+ auth: { requireAuth: true, authorityKey: auth.authorityKey, slotKeys: auth.slotKeys },
1012
+ });
1013
+ const high = decodeDeterministicLocalAuthorityWireEnvelope(createDeterministicLocalAuthorityInputBundleEnvelope(10, [
1014
+ { slot: 0, frame: 0, value: 10, delay: 0, inputId: 'sequence-window-high' },
1015
+ ], wireFixtureSlotSign(auth, 0)));
1016
+ const delayed = decodeDeterministicLocalAuthorityWireEnvelope(createDeterministicLocalAuthorityInputBundleEnvelope(9, [
1017
+ { slot: 0, frame: 1, value: 9, delay: 0, inputId: 'sequence-window-delayed' },
1018
+ ], wireFixtureSlotSign(auth, 0)));
1019
+ const replayedDelayed = decodeDeterministicLocalAuthorityWireEnvelope(createDeterministicLocalAuthorityInputBundleEnvelope(9, [
1020
+ { slot: 0, frame: 2, value: 99, delay: 0, inputId: 'sequence-window-replayed' },
1021
+ ], wireFixtureSlotSign(auth, 0)));
1022
+ const tooOld = decodeDeterministicLocalAuthorityWireEnvelope(createDeterministicLocalAuthorityInputBundleEnvelope(7, [
1023
+ { slot: 0, frame: 2, value: 7, delay: 0, inputId: 'sequence-window-too-old' },
1024
+ ], wireFixtureSlotSign(auth, 0)));
1025
+ const highResult = receiver.receive(high, 0);
1026
+ const delayedResult = receiver.receive(delayed, 0);
1027
+ const replayedDelayedResult = receiver.receive(replayedDelayed, 0);
1028
+ const tooOldResult = receiver.receive(tooOld, 0);
1029
+ return {
1030
+ reorderedSequenceAccepted: Number(highResult.accepted === 1) * Number(delayedResult.accepted === 1),
1031
+ rejectionReasons: [
1032
+ ...highResult.rejectionReasons,
1033
+ ...delayedResult.rejectionReasons,
1034
+ ...replayedDelayedResult.rejectionReasons,
1035
+ ...tooOldResult.rejectionReasons,
1036
+ ],
1037
+ };
1038
+ }
1039
+ function isKnownHmacSha256VectorCovered() {
1040
+ return createDeterministicLocalAuthorityWireAuth({
1041
+ schemaVersion: 1,
1042
+ protocol: exports.deterministicLocalAuthorityWireProtocol,
1043
+ kind: 'input-bundle',
1044
+ sequence: 42,
1045
+ payload: { inputs: [{ slot: 0, frame: 1, value: 7 }] },
1046
+ }, { senderId: 'slot:0', key: 'test-secret' }).signature === '945ddd63621adbcbe77e1a02eb18fa63ff500ee9bf34f1870fd359ca08cd5bcf';
1047
+ }
1048
+ function integerPayloadField(envelope, key) {
1049
+ const value = envelope.payload[key];
1050
+ if (typeof value !== 'number' || !Number.isInteger(value)) {
1051
+ throw new Error(`Wire ${envelope.kind} payload ${key} must be an integer`);
1052
+ }
1053
+ return value;
1054
+ }
1055
+ function parseRuntimeSnapshotPayload(envelope, key) {
1056
+ const value = envelope.payload[key];
1057
+ if (!isRecord(value)) {
1058
+ throw new Error(`Wire ${envelope.kind} payload ${key} must be a runtime snapshot`);
1059
+ }
1060
+ return (0, canonical_js_1.cloneCanonical)(value);
1061
+ }
1062
+ function assertSnapshotChecksum(envelope, snapshot) {
1063
+ const expected = envelope.payload.snapshotChecksum;
1064
+ if (typeof expected !== 'string') {
1065
+ throw new Error(`Wire ${envelope.kind} payload snapshotChecksum must be a string`);
1066
+ }
1067
+ const actual = (0, canonical_js_1.defaultChecksum)(snapshot);
1068
+ if (expected !== actual) {
1069
+ throw new Error(`Wire ${envelope.kind} snapshot checksum mismatch`);
1070
+ }
1071
+ }
1072
+ function parseSnapshotIdentityPayload(envelope) {
1073
+ const value = envelope.payload.identity;
1074
+ if (!isRecord(value)) {
1075
+ throw new Error(`Wire ${envelope.kind} payload identity must be an object`);
1076
+ }
1077
+ const identity = (0, canonical_js_1.cloneCanonical)(value);
1078
+ assertSnapshotIdentity(identity);
1079
+ return identity;
1080
+ }
1081
+ function directSnapshotIdentityRejection(envelope, expected) {
1082
+ if (expected === undefined) {
1083
+ return undefined;
1084
+ }
1085
+ if (!isRecord(envelope.payload.identity)) {
1086
+ return 'snapshot-identity-required';
1087
+ }
1088
+ const actual = parseSnapshotIdentityPayload(envelope);
1089
+ return snapshotIdentityMatches(expected, actual) ? undefined : 'snapshot-descriptor-mismatch';
1090
+ }
1091
+ function parseSnapshotOffer(envelope) {
1092
+ const transferId = stringPayloadField(envelope, 'transferId');
1093
+ if (!isValidTransferId(transferId)) {
1094
+ throw new Error('Snapshot transferId is invalid');
1095
+ }
1096
+ const identity = parseSnapshotIdentityPayload(envelope);
1097
+ const frame = integerPayloadField(envelope, 'frame');
1098
+ const chunkCount = integerPayloadField(envelope, 'chunkCount');
1099
+ const totalBytes = integerPayloadField(envelope, 'totalBytes');
1100
+ const snapshotChecksum = stringPayloadField(envelope, 'snapshotChecksum');
1101
+ const rawChunkChecksums = envelope.payload.chunkChecksums;
1102
+ if (!Array.isArray(rawChunkChecksums) || rawChunkChecksums.some((checksum) => typeof checksum !== 'string')) {
1103
+ throw new Error('Wire snapshot-offer payload chunkChecksums must be a string array');
1104
+ }
1105
+ if (chunkCount <= 0 || rawChunkChecksums.length !== chunkCount) {
1106
+ throw new Error('Wire snapshot-offer chunkCount must match chunkChecksums');
1107
+ }
1108
+ if (totalBytes < 0) {
1109
+ throw new Error('Wire snapshot-offer totalBytes must be non-negative');
1110
+ }
1111
+ return {
1112
+ transferId,
1113
+ identity,
1114
+ frame,
1115
+ snapshotChecksum,
1116
+ chunkCount,
1117
+ totalBytes,
1118
+ chunkChecksums: [...rawChunkChecksums],
1119
+ };
1120
+ }
1121
+ function parseSnapshotBytes(snapshotBytes) {
1122
+ let parsed;
1123
+ try {
1124
+ parsed = JSON.parse(snapshotBytes);
1125
+ }
1126
+ catch {
1127
+ throw new Error('Snapshot transfer bytes must be valid JSON');
1128
+ }
1129
+ if (!isRecord(parsed)) {
1130
+ throw new Error('Snapshot transfer bytes must encode an object');
1131
+ }
1132
+ return (0, canonical_js_1.cloneCanonical)(parsed);
1133
+ }
1134
+ function assertSnapshotIdentity(identity) {
1135
+ if (typeof identity.descriptorHash !== 'string' || identity.descriptorHash.length === 0) {
1136
+ throw new Error('Snapshot identity descriptorHash is required');
1137
+ }
1138
+ for (const key of ['schemaHash', 'assetDbHash', 'configHash']) {
1139
+ if (identity[key] !== undefined && (typeof identity[key] !== 'string' || identity[key].length === 0)) {
1140
+ throw new Error(`Snapshot identity ${key} must be a non-empty string`);
1141
+ }
1142
+ }
1143
+ }
1144
+ function isSnapshotIdentityLike(value) {
1145
+ return value !== undefined && isRecord(value) && typeof value.descriptorHash === 'string';
1146
+ }
1147
+ function snapshotIdentityMatches(expected, actual) {
1148
+ return expected.descriptorHash === actual.descriptorHash
1149
+ && (expected.schemaHash ?? '') === (actual.schemaHash ?? '')
1150
+ && (expected.assetDbHash ?? '') === (actual.assetDbHash ?? '')
1151
+ && (expected.configHash ?? '') === (actual.configHash ?? '');
1152
+ }
1153
+ function isValidTransferId(transferId) {
1154
+ return /^[A-Za-z0-9:._-]{1,128}$/.test(transferId);
1155
+ }
1156
+ function createWireFixtureAuth(players, seed) {
1157
+ return {
1158
+ authorityKey: (0, canonical_js_1.defaultChecksum)({ kind: 'wire-fixture-authority-key', players, seed }),
1159
+ slotKeys: Object.fromEntries(Array.from({ length: players }, (_, slot) => [
1160
+ `slot:${slot}`,
1161
+ (0, canonical_js_1.defaultChecksum)({ kind: 'wire-fixture-slot-key', players, seed, slot }),
1162
+ ])),
1163
+ };
1164
+ }
1165
+ function wireFixtureAuthoritySign(auth) {
1166
+ return { senderId: 'authority', key: auth.authorityKey };
1167
+ }
1168
+ function wireFixtureSlotSign(auth, slot) {
1169
+ const senderId = `slot:${slot}`;
1170
+ const key = auth.slotKeys[senderId];
1171
+ if (key === undefined) {
1172
+ throw new Error(`Missing wire fixture auth key for ${senderId}`);
1173
+ }
1174
+ return { senderId, key };
1175
+ }
1176
+ function parseInputMessage(value) {
1177
+ if (!isRecord(value)) {
1178
+ throw new Error('Wire input must be an object');
1179
+ }
1180
+ const slot = integerField(value, 'slot');
1181
+ const frame = integerField(value, 'frame');
1182
+ const inputValue = integerField(value, 'value');
1183
+ const delay = value.delay === undefined ? undefined : integerField(value, 'delay');
1184
+ const inputId = value.inputId === undefined ? undefined : stringField(value, 'inputId');
1185
+ const data = value.data === undefined ? undefined : canonicalField(value, 'data');
1186
+ // Conditional spreads, not explicit `undefined` properties: a present-but-
1187
+ // undefined field re-encodes as a literal `{"$rdm":"undefined"}` on the wire,
1188
+ // so a benign decode→re-encode relay would emit frames its peers reject.
1189
+ return {
1190
+ slot,
1191
+ frame,
1192
+ value: inputValue,
1193
+ ...(data === undefined ? {} : { data }),
1194
+ ...(delay === undefined ? {} : { delay }),
1195
+ ...(inputId === undefined ? {} : { inputId }),
1196
+ };
1197
+ }
1198
+ function parseCommandMessage(value) {
1199
+ if (!isRecord(value)) {
1200
+ throw new Error('Wire command must be an object');
1201
+ }
1202
+ const id = stringField(value, 'id');
1203
+ const slot = integerField(value, 'slot');
1204
+ const frame = integerField(value, 'frame');
1205
+ const channelId = stringField(value, 'channelId');
1206
+ const payload = canonicalField(value, 'payload');
1207
+ const predicted = value.predicted === undefined ? undefined : booleanField(value, 'predicted');
1208
+ const submittedFrame = value.submittedFrame === undefined ? undefined : integerField(value, 'submittedFrame');
1209
+ return {
1210
+ id,
1211
+ slot,
1212
+ frame,
1213
+ channelId,
1214
+ payload,
1215
+ ...(predicted === undefined ? {} : { predicted }),
1216
+ ...(submittedFrame === undefined ? {} : { submittedFrame }),
1217
+ };
1218
+ }
1219
+ function classifyRuntimeInputRejection(runtime, beforeRejected, message) {
1220
+ if (runtime.state.rejectedMessages === beforeRejected) {
1221
+ return 'unknown';
1222
+ }
1223
+ if (message.inputId !== undefined && !isValidWireInputId(message.inputId)) {
1224
+ return 'invalid-input-id';
1225
+ }
1226
+ if (message.frame === runtime.state.frame - 1) {
1227
+ return 'duplicate-input';
1228
+ }
1229
+ if (message.frame < runtime.state.frame) {
1230
+ return 'stale-frame';
1231
+ }
1232
+ if (message.frame > runtime.state.frame + 16) {
1233
+ return 'future-frame';
1234
+ }
1235
+ const client = runtime.state.clients[message.slot];
1236
+ if (client?.connected === true && client.spectator !== true) {
1237
+ return 'duplicate-input';
1238
+ }
1239
+ return 'runtime-policy';
1240
+ }
1241
+ function isValidWireInputId(inputId) {
1242
+ return /^[A-Za-z0-9:._-]{1,128}$/.test(inputId);
1243
+ }
1244
+ function assertWireKind(kind) {
1245
+ if (kind !== 'input-bundle' &&
1246
+ kind !== 'command-bundle' &&
1247
+ kind !== 'ack' &&
1248
+ kind !== 'correction' &&
1249
+ kind !== 'snapshot' &&
1250
+ kind !== 'snapshot-request' &&
1251
+ kind !== 'snapshot-offer' &&
1252
+ kind !== 'snapshot-chunk' &&
1253
+ kind !== 'snapshot-complete') {
1254
+ throw new Error(`Unknown local authority wire kind ${kind}`);
1255
+ }
1256
+ }
1257
+ /**
1258
+ * Pre-parse cap: the largest of the per-kind budgets below. Applied to the raw
1259
+ * string before JSON.parse so a hostile blob can't buy unbounded parse cost.
1260
+ */
1261
+ const absoluteMaxWireEnvelopeBytes = 2_097_152;
1262
+ // NOTE on transports: the engine's local ws-frame codec caps a single frame at
1263
+ // 65535 bytes, so a monolithic `snapshot`/`correction` envelope near the 2 MB
1264
+ // budget cannot traverse that harness transport — use the chunked
1265
+ // snapshot-offer/chunk/complete path there (1 KB chunks). The 2 MB budget
1266
+ // exists for host transports (mp-room envelopes) without that frame limit.
1267
+ function maxWireEnvelopeBytes(kind) {
1268
+ return kind === 'input-bundle' || kind === 'command-bundle' ? 65_536 : 2_097_152;
1269
+ }
1270
+ function parseWireAuth(value) {
1271
+ if (!isRecord(value)) {
1272
+ throw new Error('Wire envelope auth must be an object');
1273
+ }
1274
+ const senderId = value.senderId;
1275
+ const signature = value.signature;
1276
+ if (typeof senderId !== 'string') {
1277
+ throw new Error('Wire envelope auth senderId must be a string');
1278
+ }
1279
+ assertWireSenderId(senderId);
1280
+ if (typeof signature !== 'string' || signature.length === 0) {
1281
+ throw new Error('Wire envelope auth signature must be a non-empty string');
1282
+ }
1283
+ return { senderId, signature };
1284
+ }
1285
+ function assertWireSenderId(senderId) {
1286
+ if (!/^[A-Za-z0-9:._-]{1,128}$/.test(senderId)) {
1287
+ throw new Error('Wire auth senderId is invalid');
1288
+ }
1289
+ }
1290
+ function verifyWireEnvelopeAuth(envelope, expectedSenderId, options) {
1291
+ const auth = envelope.auth;
1292
+ if (auth === undefined) {
1293
+ return options?.requireAuth === true ? 'missing-wire-auth' : undefined;
1294
+ }
1295
+ if (auth.senderId !== expectedSenderId) {
1296
+ return 'wire-auth-sender-mismatch';
1297
+ }
1298
+ const key = expectedSenderId === 'authority'
1299
+ ? options?.authorityKey
1300
+ : options?.slotKeys?.[expectedSenderId];
1301
+ if (key === undefined) {
1302
+ return 'missing-wire-auth-key';
1303
+ }
1304
+ const baseEnvelope = {
1305
+ schemaVersion: envelope.schemaVersion,
1306
+ protocol: envelope.protocol,
1307
+ kind: envelope.kind,
1308
+ sequence: envelope.sequence,
1309
+ payload: envelope.payload,
1310
+ };
1311
+ const expectedAuth = createDeterministicLocalAuthorityWireAuth(baseEnvelope, { senderId: expectedSenderId, key });
1312
+ return constantTimeEqualHex(auth.signature, expectedAuth.signature) ? undefined : 'wire-auth-signature-mismatch';
1313
+ }
1314
+ function assertWireSequence(sequence) {
1315
+ if (!Number.isInteger(sequence) || sequence < 0) {
1316
+ throw new Error('Wire envelope sequence must be a non-negative integer');
1317
+ }
1318
+ }
1319
+ function hmacSha256Hex(key, message) {
1320
+ const blockSize = 64;
1321
+ const rawKey = (0, canonical_js_1.encodeUtf8)(key);
1322
+ const normalizedKey = rawKey.length > blockSize ? sha256(rawKey) : rawKey;
1323
+ const keyBlock = new Uint8Array(blockSize);
1324
+ keyBlock.set(normalizedKey);
1325
+ const outer = new Uint8Array(blockSize);
1326
+ const inner = new Uint8Array(blockSize);
1327
+ for (let index = 0; index < blockSize; index += 1) {
1328
+ outer[index] = keyBlock[index] ^ 0x5c;
1329
+ inner[index] = keyBlock[index] ^ 0x36;
1330
+ }
1331
+ return toHex(sha256(concatBytes(outer, sha256(concatBytes(inner, (0, canonical_js_1.encodeUtf8)(message))))));
1332
+ }
1333
+ function sha256(bytes) {
1334
+ const hash = [
1335
+ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
1336
+ 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
1337
+ ];
1338
+ const constants = [
1339
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
1340
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
1341
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
1342
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
1343
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
1344
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
1345
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
1346
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
1347
+ ];
1348
+ const bitLengthHigh = Math.floor((bytes.length * 8) / 0x100000000);
1349
+ const bitLengthLow = (bytes.length * 8) >>> 0;
1350
+ const paddedLength = Math.ceil((bytes.length + 9) / 64) * 64;
1351
+ const padded = new Uint8Array(paddedLength);
1352
+ padded.set(bytes);
1353
+ padded[bytes.length] = 0x80;
1354
+ writeUint32(padded, paddedLength - 8, bitLengthHigh);
1355
+ writeUint32(padded, paddedLength - 4, bitLengthLow);
1356
+ const words = new Array(64).fill(0);
1357
+ for (let offset = 0; offset < paddedLength; offset += 64) {
1358
+ for (let index = 0; index < 16; index += 1) {
1359
+ words[index] = readUint32(padded, offset + index * 4);
1360
+ }
1361
+ for (let index = 16; index < 64; index += 1) {
1362
+ const s0 = rotateRight(words[index - 15], 7) ^ rotateRight(words[index - 15], 18) ^ (words[index - 15] >>> 3);
1363
+ const s1 = rotateRight(words[index - 2], 17) ^ rotateRight(words[index - 2], 19) ^ (words[index - 2] >>> 10);
1364
+ words[index] = add32(words[index - 16], s0, words[index - 7], s1);
1365
+ }
1366
+ let a = hash[0];
1367
+ let b = hash[1];
1368
+ let c = hash[2];
1369
+ let d = hash[3];
1370
+ let e = hash[4];
1371
+ let f = hash[5];
1372
+ let g = hash[6];
1373
+ let h = hash[7];
1374
+ for (let index = 0; index < 64; index += 1) {
1375
+ const s1 = rotateRight(e, 6) ^ rotateRight(e, 11) ^ rotateRight(e, 25);
1376
+ const ch = (e & f) ^ (~e & g);
1377
+ const temp1 = add32(h, s1, ch, constants[index], words[index]);
1378
+ const s0 = rotateRight(a, 2) ^ rotateRight(a, 13) ^ rotateRight(a, 22);
1379
+ const maj = (a & b) ^ (a & c) ^ (b & c);
1380
+ const temp2 = add32(s0, maj);
1381
+ h = g;
1382
+ g = f;
1383
+ f = e;
1384
+ e = add32(d, temp1);
1385
+ d = c;
1386
+ c = b;
1387
+ b = a;
1388
+ a = add32(temp1, temp2);
1389
+ }
1390
+ hash[0] = add32(hash[0], a);
1391
+ hash[1] = add32(hash[1], b);
1392
+ hash[2] = add32(hash[2], c);
1393
+ hash[3] = add32(hash[3], d);
1394
+ hash[4] = add32(hash[4], e);
1395
+ hash[5] = add32(hash[5], f);
1396
+ hash[6] = add32(hash[6], g);
1397
+ hash[7] = add32(hash[7], h);
1398
+ }
1399
+ const digest = new Uint8Array(32);
1400
+ for (let index = 0; index < hash.length; index += 1) {
1401
+ writeUint32(digest, index * 4, hash[index]);
1402
+ }
1403
+ return digest;
1404
+ }
1405
+ function constantTimeEqualHex(left, right) {
1406
+ const leftBytes = hexToBytes(left);
1407
+ const rightBytes = hexToBytes(right);
1408
+ const length = Math.max(leftBytes.length, rightBytes.length);
1409
+ let diff = leftBytes.length ^ rightBytes.length;
1410
+ for (let index = 0; index < length; index += 1) {
1411
+ diff |= leftBytes[index] ^ rightBytes[index];
1412
+ }
1413
+ return diff === 0;
1414
+ }
1415
+ function hexToBytes(hex) {
1416
+ if (!/^[0-9a-f]*$/i.test(hex) || hex.length % 2 !== 0) {
1417
+ return new Uint8Array(0);
1418
+ }
1419
+ const bytes = new Uint8Array(hex.length / 2);
1420
+ for (let index = 0; index < bytes.length; index += 1) {
1421
+ bytes[index] = Number.parseInt(hex.slice(index * 2, index * 2 + 2), 16);
1422
+ }
1423
+ return bytes;
1424
+ }
1425
+ function concatBytes(left, right) {
1426
+ const bytes = new Uint8Array(left.length + right.length);
1427
+ bytes.set(left);
1428
+ bytes.set(right, left.length);
1429
+ return bytes;
1430
+ }
1431
+ function add32(...values) {
1432
+ return values.reduce((sum, value) => (sum + value) >>> 0, 0);
1433
+ }
1434
+ function rotateRight(value, bits) {
1435
+ return (value >>> bits) | (value << (32 - bits));
1436
+ }
1437
+ function readUint32(bytes, offset) {
1438
+ return ((bytes[offset] << 24) | (bytes[offset + 1] << 16) | (bytes[offset + 2] << 8) | bytes[offset + 3]) >>> 0;
1439
+ }
1440
+ function writeUint32(bytes, offset, value) {
1441
+ bytes[offset] = (value >>> 24) & 0xff;
1442
+ bytes[offset + 1] = (value >>> 16) & 0xff;
1443
+ bytes[offset + 2] = (value >>> 8) & 0xff;
1444
+ bytes[offset + 3] = value & 0xff;
1445
+ }
1446
+ function toHex(bytes) {
1447
+ return [...bytes].map((byte) => byte.toString(16).padStart(2, '0')).join('');
1448
+ }
1449
+ function readSequenceReplayWindow(window) {
1450
+ if (window === undefined) {
1451
+ return 64;
1452
+ }
1453
+ if (!Number.isInteger(window) || window < 0) {
1454
+ throw new Error('Wire sequence replay window must be a non-negative integer');
1455
+ }
1456
+ return window;
1457
+ }
1458
+ function compareNumbers(left, right) {
1459
+ return left - right;
1460
+ }
1461
+ function compareStrings(left, right) {
1462
+ return Number(left > right) - Number(left < right);
1463
+ }
1464
+ function integerField(record, key) {
1465
+ const value = record[key];
1466
+ if (typeof value !== 'number' || !Number.isInteger(value)) {
1467
+ throw new Error(`Wire input ${key} must be an integer`);
1468
+ }
1469
+ return value;
1470
+ }
1471
+ function stringField(record, key) {
1472
+ const value = record[key];
1473
+ if (typeof value !== 'string') {
1474
+ throw new Error(`Wire input ${key} must be a string`);
1475
+ }
1476
+ return value;
1477
+ }
1478
+ function stringPayloadField(envelope, key) {
1479
+ const value = envelope.payload[key];
1480
+ if (typeof value !== 'string') {
1481
+ throw new Error(`Wire ${envelope.kind} payload ${key} must be a string`);
1482
+ }
1483
+ return value;
1484
+ }
1485
+ function booleanField(record, key) {
1486
+ const value = record[key];
1487
+ if (typeof value !== 'boolean') {
1488
+ throw new Error(`Wire input ${key} must be a boolean`);
1489
+ }
1490
+ return value;
1491
+ }
1492
+ function canonicalField(record, key) {
1493
+ return (0, canonical_js_1.cloneCanonical)(record[key]);
1494
+ }
1495
+ function isRecord(value) {
1496
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
1497
+ }