@leofcoin/chain 1.10.21 → 1.11.0

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 (29) hide show
  1. package/README.md +95 -0
  2. package/exports/browser/beacon-wire.js +2 -2
  3. package/exports/browser/{browser-CCfR9KOM-BGMMnVsR.js → browser-CCfR9KOM-CNWKLusu.js} +3 -3
  4. package/exports/browser/{browser-crypto-CH0ua22L.js → browser-crypto-B_UJguhk.js} +453 -35
  5. package/exports/browser/chain.js +1275 -996
  6. package/exports/browser/client-DbZBfpG6-C1cXgvfr.js +5943 -0
  7. package/exports/browser/{connection-monitor-D-krNiRH.js → connection-monitor-BzbeYGEt.js} +153 -24
  8. package/exports/browser/{index-dTQVgkpf-DOL2rIjp.js → index-dTQVgkpf-CkoAiRNX.js} +3 -3
  9. package/exports/browser/{index-DCbLvMKL.js → index-phv5KvaE.js} +238 -234
  10. package/exports/browser/{messages-DCePyqKC-BSvJxDWZ.js → messages-DZYCxoZI-B472zldo.js} +3 -3
  11. package/exports/browser/{node-browser-Ha4Lvntj.js → node-browser-BmyPBkL5.js} +55 -11
  12. package/exports/browser/node-browser.js +3 -3
  13. package/exports/browser/workers/block-worker.js +8 -9
  14. package/exports/browser/workers/machine-worker.js +141 -1195
  15. package/exports/{workers/worker-BDTWOCzS-COrAszCt.js → browser/workers/proposal.proto-8zgMfutf-B3So_tnS.js} +910 -398
  16. package/exports/chain.js +296 -1078
  17. package/exports/connection-monitor-BdFLRh-J.js +465 -0
  18. package/exports/connection-monitor.js +2 -324
  19. package/exports/{constants-Dy9i9S_o.js → constants-BQ6f-_OT.js} +1 -1
  20. package/exports/hash-format-state.js +3 -3
  21. package/exports/machine.js +550 -0
  22. package/exports/node.js +35 -2
  23. package/exports/state-checkpoint-BiQ1EXzo.js +110 -0
  24. package/exports/state-checkpoint.js +3 -0
  25. package/exports/workers/block-worker.js +8 -9
  26. package/exports/workers/machine-worker.js +141 -1195
  27. package/exports/{browser/workers/worker-BDTWOCzS-COrAszCt.js → workers/proposal.proto-8zgMfutf-B3So_tnS.js} +910 -398
  28. package/package.json +9 -7
  29. package/exports/browser/client-DKKzPSlb-C_1ug6ux.js +0 -2401
@@ -0,0 +1,110 @@
1
+ import { BlockMessage } from '@leofcoin/messages';
2
+ import { jsonParseBigInt, jsonStringifyBigInt } from '@leofcoin/utils';
3
+
4
+ const validateChainLink = (localTip, incoming) => {
5
+ const localHash = localTip?.hash || "0x0";
6
+ const localIndex = Number(localTip?.index ?? -1);
7
+ const hasCanonicalTip = localHash !== "0x0";
8
+ if (hasCanonicalTip && incoming.index < localIndex) return "stale";
9
+ if (hasCanonicalTip && incoming.index === localIndex) {
10
+ if (incoming.hash === localHash) return "duplicate";
11
+ throw new Error(`Block conflict detected at index ${incoming.index}`);
12
+ }
13
+ const expectedIndex = hasCanonicalTip ? localIndex + 1 : 0;
14
+ if (incoming.index !== expectedIndex) {
15
+ throw new Error(`Unexpected block index ${incoming.index}; expected ${expectedIndex}`);
16
+ }
17
+ if (incoming.previousHash !== localHash) {
18
+ throw new Error(
19
+ `previousHash mismatch at index ${incoming.index}: expected ${localHash}, got ${incoming.previousHash}`
20
+ );
21
+ }
22
+ return "append";
23
+ };
24
+
25
+ const CHECKPOINT_KEYS = ["checkpoint:0", "checkpoint:1"];
26
+ const counters = [
27
+ "nativeCalls",
28
+ "nativeMints",
29
+ "nativeBurns",
30
+ "nativeTransfers",
31
+ "totalBurnAmount",
32
+ "totalMintAmount",
33
+ "totalTransferAmount",
34
+ "totalTransactions",
35
+ "totalBlocks"
36
+ ];
37
+ const record = (value) => value && typeof value === "object" && !Array.isArray(value);
38
+ const text = (value) => typeof value === "string" ? value : new TextDecoder("utf-8", { fatal: true }).decode(value);
39
+ const digest = async (value) => [...new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(value)))].map((byte) => byte.toString(16).padStart(2, "0")).join("");
40
+ function validateCheckpoint(snapshot, chainId, requiredContracts) {
41
+ if (!record(snapshot) || snapshot.chainId !== chainId || !record(snapshot.states) || !record(snapshot.accounts) || !record(snapshot.info))
42
+ throw new Error("Invalid checkpoint or chain identity");
43
+ const tip = snapshot.lastBlock;
44
+ const index = Number(tip?.index);
45
+ if (!["bigint", "number"].includes(typeof tip?.index) || !Number.isSafeInteger(index) || index < -1 || (index === -1 ? tip.hash !== "0x0" : typeof tip.hash !== "string" || !tip.hash || tip.hash === "0x0"))
46
+ throw new Error("Invalid checkpoint tip");
47
+ for (const contract of requiredContracts) if (!record(snapshot.states[contract])) throw new Error(`Incomplete checkpoint contract: ${contract}`);
48
+ for (const [address, nonce] of Object.entries(snapshot.accounts))
49
+ if (!address || typeof nonce !== "string" || !/^(0|[1-9]\d*)$/.test(nonce)) throw new Error("Invalid checkpoint nonce");
50
+ for (const counter of counters)
51
+ if (typeof snapshot.info[counter] !== "bigint" || snapshot.info[counter] < 0n) throw new Error(`Invalid checkpoint counter: ${counter}`);
52
+ if (snapshot.info.totalBlocks !== BigInt(index) + 1n) throw new Error("Checkpoint block count does not match tip");
53
+ return snapshot;
54
+ }
55
+ async function writeCheckpoint(store, snapshot, generation, requiredContracts) {
56
+ if (!Number.isSafeInteger(generation) || generation < 0) throw new Error("Invalid checkpoint generation");
57
+ validateCheckpoint(snapshot, snapshot.chainId, requiredContracts);
58
+ const payload = JSON.stringify({ generation, snapshot }, jsonStringifyBigInt);
59
+ await store.put(CHECKPOINT_KEYS[generation % 2], new TextEncoder().encode(JSON.stringify({ schema: 1, payload, checksum: await digest(payload) })));
60
+ }
61
+ async function readCheckpoint(store, chainId, requiredContracts) {
62
+ const valid = [];
63
+ let present = 0;
64
+ const keys = new Set(await store.keys());
65
+ for (const key of CHECKPOINT_KEYS) {
66
+ if (!keys.has(key)) continue;
67
+ present++;
68
+ const bytes = await store.get(key);
69
+ try {
70
+ const envelope = JSON.parse(text(bytes));
71
+ if (envelope.schema !== 1 || typeof envelope.payload !== "string" || envelope.checksum !== await digest(envelope.payload)) throw new Error("Invalid checkpoint checksum");
72
+ const result = JSON.parse(envelope.payload, jsonParseBigInt);
73
+ if (!Number.isSafeInteger(result.generation) || result.generation < 0 || result.generation % 2 !== CHECKPOINT_KEYS.indexOf(key)) throw new Error("Invalid checkpoint generation");
74
+ validateCheckpoint(result.snapshot, chainId, requiredContracts);
75
+ valid.push(result);
76
+ } catch {
77
+ }
78
+ }
79
+ if (present && !valid.length) throw new Error("No valid state checkpoint remains; preserve this data root and rebuild in a separate root");
80
+ return valid.sort((left, right) => right.generation - left.generation)[0];
81
+ }
82
+ async function recoveryHistory(hash, readBlock) {
83
+ const reversed = [];
84
+ const seen = /* @__PURE__ */ new Set();
85
+ while (hash !== "0x0") {
86
+ if (!hash || seen.has(hash)) throw new Error("Invalid or cyclic recovery ancestry");
87
+ seen.add(hash);
88
+ const bytes = await readBlock(hash);
89
+ if (!bytes) throw new Error(`Recovery block unavailable: ${hash}`);
90
+ const message = new BlockMessage(bytes);
91
+ if (await message.hash() !== hash) throw new Error(`Recovery block hash mismatch: ${hash}`);
92
+ reversed.push({ ...message.decoded, hash, loaded: false });
93
+ hash = message.decoded.previousHash;
94
+ }
95
+ const history = reversed.reverse();
96
+ let tip = { index: -1, hash: "0x0" };
97
+ for (const block of history) {
98
+ const index = Number(block.index);
99
+ if (!Number.isSafeInteger(index) || index < 0) throw new Error("Invalid recovery block index");
100
+ if (validateChainLink(tip, { index, hash: block.hash, previousHash: block.previousHash }) !== "append") throw new Error("Invalid recovery chain link");
101
+ tip = { index, hash: block.hash };
102
+ }
103
+ return history;
104
+ }
105
+ async function restoreAccountNonces(store, accounts) {
106
+ await store.clear();
107
+ for (const [address, nonce] of Object.entries(accounts)) await store.put(address, String(nonce));
108
+ }
109
+
110
+ export { CHECKPOINT_KEYS as C, recoveryHistory as a, restoreAccountNonces as b, validateCheckpoint as c, readCheckpoint as r, validateChainLink as v, writeCheckpoint as w };
@@ -0,0 +1,3 @@
1
+ import '@leofcoin/messages';
2
+ import '@leofcoin/utils';
3
+ export { C as CHECKPOINT_KEYS, r as readCheckpoint, a as recoveryHistory, b as restoreAccountNonces, c as validateCheckpoint, w as writeCheckpoint } from './state-checkpoint-BiQ1EXzo.js';
@@ -1,6 +1,6 @@
1
- import { E as EasyWorker, F as FormatInterface, p as proto$3, e as encodeFrames, d as decodeFrames, a as proto$2 } from './worker-BDTWOCzS-COrAszCt.js';
1
+ import { B as BinaryWorker, a as BinaryFormat, p as proto$2, e as encodeFrames, d as decodeFrames, b as proto$1 } from './proposal.proto-8zgMfutf-B3So_tnS.js';
2
2
 
3
- class ValidatorMessage extends FormatInterface {
3
+ class ValidatorMessage extends BinaryFormat {
4
4
  get messageName() {
5
5
  return 'ValidatorMessage';
6
6
  }
@@ -8,11 +8,11 @@ class ValidatorMessage extends FormatInterface {
8
8
  if (buffer instanceof ValidatorMessage)
9
9
  return buffer;
10
10
  const name = 'validator-message';
11
- super(buffer, proto$2, { name });
11
+ super(buffer, proto$1, { name });
12
12
  }
13
13
  }
14
14
 
15
- class BlockMessage extends FormatInterface {
15
+ class BlockMessage extends BinaryFormat {
16
16
  get messageName() {
17
17
  return 'BlockMessage';
18
18
  }
@@ -20,7 +20,7 @@ class BlockMessage extends FormatInterface {
20
20
  if (buffer instanceof BlockMessage)
21
21
  return buffer;
22
22
  const name = 'block-message';
23
- super(buffer, proto$3, { name });
23
+ super(buffer, proto$2, { name });
24
24
  }
25
25
  encode(decoded) {
26
26
  decoded = decoded || this.decoded;
@@ -40,14 +40,13 @@ class BlockMessage extends FormatInterface {
40
40
  encoded = encoded || this.encoded;
41
41
  super.decode(encoded);
42
42
  // @ts-ignore
43
- // Preserve historical acceptance of padded length prefixes. Tightening
44
- // canonical protocol validation is a separate, versioned decision.
45
- this.decoded.validators = decodeFrames(this.decoded.validators, 0, { canonical: false }).map((validator) => new ValidatorMessage(validator).decoded);
43
+ // Binary-only protocol also requires canonical validator frame lengths.
44
+ this.decoded.validators = decodeFrames(this.decoded.validators).map((validator) => new ValidatorMessage(validator).decoded);
46
45
  return this.decoded;
47
46
  }
48
47
  }
49
48
 
50
- const worker = new EasyWorker();
49
+ const worker = new BinaryWorker();
51
50
  globalThis.BigNumber = BigNumber;
52
51
  globalThis.contracts = {};
53
52
  const run = async (blocks) => {