@leofcoin/chain 1.10.12 → 1.10.14

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 (52) hide show
  1. package/exports/beacon-envelope.js +2 -2
  2. package/exports/beacon-epoch.js +1 -1
  3. package/exports/beacon-lifecycle.js +2 -2
  4. package/exports/beacon-round.js +1 -1
  5. package/exports/beacon.js +1 -1
  6. package/exports/browser/beacon-envelope.js +79 -3
  7. package/exports/browser/beacon-epoch.js +2 -1
  8. package/exports/browser/beacon-lifecycle.js +3 -2
  9. package/exports/browser/beacon-round.js +2 -1
  10. package/exports/browser/beacon-wire.js +3 -3
  11. package/exports/browser/beacon.js +3 -2
  12. package/exports/browser/{browser-DvU1xNFS-sdlKNCJc.js → browser-DvU1xNFS-4-PGqr4c.js} +4 -4
  13. package/exports/browser/browser-DvU1xNFS-BJCQsckZ.js +151 -0
  14. package/exports/browser/browser-DvU1xNFS-D9eSzJGJ.js +151 -0
  15. package/exports/browser/browser-crypto-BHeOz9rj.js +8893 -0
  16. package/exports/browser/browser-crypto-CTP3Yk4M.js +8893 -0
  17. package/exports/browser/{proposal.proto-BcUgd885.js → browser-crypto-Yk_e6OHw.js} +35 -6
  18. package/exports/browser/chain.js +63 -321
  19. package/exports/browser/{client-B-jyclOB-CsNYfj4Z.js → client-DDC8C510-CUi3_3uT.js} +36 -12
  20. package/exports/browser/client-DDC8C510-DbIdQjhy.js +2225 -0
  21. package/exports/browser/client-DDC8C510-ZN_MLo1Y.js +2225 -0
  22. package/exports/browser/connection-monitor-BjXI0nBP.js +453 -0
  23. package/exports/browser/connection-monitor-Cf0vNDCA.js +453 -0
  24. package/exports/browser/connection-monitor-pdopeM1I.js +453 -0
  25. package/exports/browser/hkdf-B9AXp_7K.js +222 -0
  26. package/exports/browser/hkdf-CnZvwpZ9.js +222 -0
  27. package/exports/browser/{index-Bxr5Iztg-C6xBk0rK.js → index-Bxr5Iztg-AmkW0EHS.js} +4 -4
  28. package/exports/browser/index-Bxr5Iztg-Bdpw_E83.js +40 -0
  29. package/exports/browser/index-Bxr5Iztg-CxXVYm3t.js +40 -0
  30. package/exports/browser/{index-D7FUx7Dd.js → index-CPQ5yvZ2.js} +5 -6
  31. package/exports/browser/index-C_RakUAS.js +4995 -0
  32. package/exports/browser/index-uFaocBqT.js +4995 -0
  33. package/exports/browser/{messages-FMRAS8QX-CvlZBzy5.js → messages-DH0tgNM9-BRp0gcJh.js} +4 -4
  34. package/exports/browser/messages-DH0tgNM9-CZwrcL37.js +208 -0
  35. package/exports/browser/messages-DH0tgNM9-DkNnpePH.js +208 -0
  36. package/exports/browser/{node-browser-xlqSBOaN.js → node-browser-1VSuCeew.js} +6 -6
  37. package/exports/browser/node-browser-CQugE1WZ.js +17880 -0
  38. package/exports/browser/node-browser-wDmRaOEo.js +17880 -0
  39. package/exports/browser/node-browser.js +4 -4
  40. package/exports/browser/sha2-Dkoic3vb.js +602 -0
  41. package/exports/browser/sha2-piimb6xL.js +614 -0
  42. package/exports/browser/weierstrass-HGdA_prK.js +2156 -0
  43. package/exports/browser/{weierstrass-C-jX_jly.js → weierstrass-Q0QND1ZT.js} +1 -1
  44. package/exports/browser/workers/machine-worker.js +419 -147
  45. package/exports/chain.js +56 -313
  46. package/exports/connection-monitor.js +324 -0
  47. package/exports/node.js +2 -0
  48. package/exports/workers/machine-worker.js +419 -147
  49. package/package.json +4 -4
  50. package/exports/browser/browser-CWeoyGUw-BabtHowB.js +0 -57
  51. package/exports/browser/constants-Cv0p224A.js +0 -130
  52. package/exports/browser/hkdf-DhdhLAAv.js +0 -147
package/exports/chain.js CHANGED
@@ -11,6 +11,7 @@ import { randombytes } from '@leofcoin/crypto';
11
11
  import EasyWorker from '@vandeurenglenn/easy-worker';
12
12
  import { ContractDeploymentError, ExecutionError, isResolveError, ResolveError, isExecutionError } from '@leofcoin/errors';
13
13
  import { P as PROTOCOL_VERSION, R as REACHED_ONE_ZERO_ZERO } from './constants-CMYKv-Rt.js';
14
+ import ConnectionMonitor from './connection-monitor.js';
14
15
  import { q as quorumThreshold } from './quorum-S_qdgiAY.js';
15
16
  import '@leofcoin/networks';
16
17
 
@@ -37,6 +38,27 @@ class Protocol {
37
38
  }
38
39
  }
39
40
 
41
+ const waitForCanonicalTransaction = (pubsub, transactionPool, hash) => new Promise((resolve, reject) => {
42
+ const completionTopic = `transaction.completed.${hash}`;
43
+ const cleanup = () => {
44
+ pubsub.unsubscribe("block-processed", blockProcessed);
45
+ pubsub.unsubscribe(completionTopic, transactionCompleted);
46
+ };
47
+ const blockProcessed = (block) => {
48
+ if (!Array.isArray(block?.transactions) || !block.transactions.includes(hash)) return;
49
+ cleanup();
50
+ resolve(hash);
51
+ };
52
+ const transactionCompleted = async (result) => {
53
+ if (result?.status === "fulfilled") return;
54
+ await transactionPool.delete(hash);
55
+ cleanup();
56
+ reject({ hash, error: result?.error });
57
+ };
58
+ pubsub.subscribe("block-processed", blockProcessed);
59
+ pubsub.subscribe(completionTopic, transactionCompleted);
60
+ });
61
+
40
62
  class Transaction extends Protocol {
41
63
  #pendingNonces = /* @__PURE__ */ new Map();
42
64
  #maxPendingNonce = /* @__PURE__ */ new Map();
@@ -238,26 +260,7 @@ class Transaction extends Protocol {
238
260
  const hash = await message.hash();
239
261
  try {
240
262
  let data;
241
- const wait = new Promise(async (resolve, reject) => {
242
- if (pubsub.hasSubscribers(`transaction.completed.${hash}`)) {
243
- const result = pubsub.getValue(`transaction.completed.${hash}`);
244
- if (result.status !== "fulfilled") {
245
- await transactionPoolStore.delete(hash);
246
- }
247
- result.status === "fulfilled" ? resolve(result.hash) : reject({ hash: result.hash, error: result.error });
248
- } else {
249
- const completed = async (result) => {
250
- if (result.status !== "fulfilled") {
251
- await transactionPoolStore.delete(hash);
252
- }
253
- result.status === "fulfilled" ? resolve(result.hash) : reject({ hash: result.hash, error: result.error });
254
- setTimeout(async () => {
255
- pubsub.unsubscribe(`transaction.completed.${hash}`, completed);
256
- }, 1e4);
257
- };
258
- pubsub.subscribe(`transaction.completed.${hash}`, completed);
259
- }
260
- });
263
+ const wait = waitForCanonicalTransaction(pubsub, transactionPoolStore, hash);
261
264
  await globalThis.transactionPoolStore.put(hash, message.encoded);
262
265
  this.addPendingNonce(message.decoded.from, message.decoded.nonce);
263
266
  try {
@@ -1648,6 +1651,12 @@ class State extends Contract {
1648
1651
  const localIndex = localBlock ? Number(localBlock.index) : -1;
1649
1652
  const remoteIndex = Number(lastBlock.index);
1650
1653
  const remoteBlockHash = lastBlock.hash;
1654
+ globalThis.pubsub.publish("sync-progress", {
1655
+ phase: "syncing",
1656
+ startIndex: localIndex,
1657
+ localIndex,
1658
+ targetIndex: remoteIndex
1659
+ });
1651
1660
  let localStateHash = "0x0";
1652
1661
  try {
1653
1662
  localStateHash = new TextDecoder().decode(await globalThis.chainStore.get("lastBlock"));
@@ -1707,6 +1716,12 @@ class State extends Contract {
1707
1716
  } else {
1708
1717
  debug$1(`Block already in local state. Remote hash: ${remoteBlockHash} matches local state`);
1709
1718
  }
1719
+ globalThis.pubsub.publish("sync-progress", {
1720
+ phase: "synced",
1721
+ startIndex: localIndex,
1722
+ localIndex: remoteIndex,
1723
+ targetIndex: remoteIndex
1724
+ });
1710
1725
  } catch (error) {
1711
1726
  console.log(error);
1712
1727
  throw error;
@@ -1993,297 +2008,6 @@ class VersionControl extends State {
1993
2008
  }
1994
2009
  }
1995
2010
 
1996
- class ConnectionMonitor {
1997
- #isMonitoring = false;
1998
- #checkInterval = null;
1999
- #reconnectDelay = 5e3;
2000
- #reconnectTimer = null;
2001
- #healthCheckInterval = 6e4;
2002
- #version;
2003
- #lastHealthCheckAt = 0;
2004
- #reconnecting = false;
2005
- // event handlers to remove later
2006
- #onOnline = null;
2007
- #onVisibilityChange = null;
2008
- #onSigcont = null;
2009
- #onPeerConnected = null;
2010
- get isMonitoring() {
2011
- return this.#isMonitoring;
2012
- }
2013
- get connectedPeers() {
2014
- return Object.values(globalThis.peernet?.connections || {}).filter((peer) => peer.connected);
2015
- }
2016
- get compatiblePeers() {
2017
- return this.connectedPeers.filter((peer) => {
2018
- if (!peer.version || !this.#version) return false;
2019
- const [peerMajor, peerMinor] = peer.version.split(".");
2020
- const [localMajor, localMinor] = this.#version.split(".");
2021
- return peerMajor === localMajor && peerMinor === localMinor;
2022
- });
2023
- }
2024
- get disconnectedPeers() {
2025
- return Object.values(globalThis.peernet?.connections || {}).filter((peer) => !peer.connected);
2026
- }
2027
- start(version) {
2028
- this.#version = version;
2029
- console.log(`\u{1F517} Connection Monitor initialized for version: ${this.#version}`);
2030
- if (this.#isMonitoring) return;
2031
- this.#isMonitoring = true;
2032
- console.log("\u{1F504} Starting connection monitor...");
2033
- if (typeof window !== "undefined" && typeof document !== "undefined") {
2034
- this.#onOnline = () => {
2035
- console.log("\u{1F310} Network online \u2014 attempting restore");
2036
- void this.#restoreNetwork();
2037
- };
2038
- window.addEventListener("online", this.#onOnline);
2039
- this.#onVisibilityChange = () => {
2040
- if (document.visibilityState === "visible") {
2041
- console.log("\u{1F4A1} Visibility regained");
2042
- if (this.connectedPeers.length === 0) {
2043
- console.log("\u{1F4A1} Visibility regained \u2014 attempting restore");
2044
- void this.#restoreNetwork();
2045
- }
2046
- }
2047
- };
2048
- document.addEventListener("visibilitychange", this.#onVisibilityChange);
2049
- }
2050
- if (typeof process !== "undefined" && typeof process.on === "function") {
2051
- this.#onSigcont = () => {
2052
- console.log("\u{1F514} Process resumed (SIGCONT) \u2014 attempting restore");
2053
- void this.#restoreNetwork();
2054
- };
2055
- try {
2056
- process.on("SIGCONT", this.#onSigcont);
2057
- } catch (e) {
2058
- }
2059
- }
2060
- this.#lastHealthCheckAt = Date.now();
2061
- this.#checkInterval = setInterval(() => {
2062
- this.#healthCheck();
2063
- }, this.#healthCheckInterval);
2064
- this.#onPeerConnected = () => {
2065
- if (this.#reconnectTimer) {
2066
- clearTimeout(this.#reconnectTimer);
2067
- this.#reconnectTimer = null;
2068
- }
2069
- this.#reconnecting = false;
2070
- this.#reconnectDelay = 5e3;
2071
- };
2072
- globalThis.pubsub?.subscribe("peer:connected", this.#onPeerConnected);
2073
- }
2074
- stop() {
2075
- if (!this.#isMonitoring) return;
2076
- this.#isMonitoring = false;
2077
- if (this.#checkInterval) {
2078
- clearInterval(this.#checkInterval);
2079
- this.#checkInterval = null;
2080
- }
2081
- if (typeof window !== "undefined") {
2082
- if (this.#onOnline) {
2083
- window.removeEventListener("online", this.#onOnline);
2084
- this.#onOnline = null;
2085
- }
2086
- if (this.#onVisibilityChange) {
2087
- document.removeEventListener("visibilitychange", this.#onVisibilityChange);
2088
- this.#onVisibilityChange = null;
2089
- }
2090
- }
2091
- if (typeof process !== "undefined" && typeof process.removeListener === "function" && this.#onSigcont) {
2092
- try {
2093
- process.removeListener("SIGCONT", this.#onSigcont);
2094
- } catch (e) {
2095
- }
2096
- this.#onSigcont = null;
2097
- }
2098
- if (this.#onPeerConnected) {
2099
- globalThis.pubsub?.unsubscribe("peer:connected", this.#onPeerConnected);
2100
- this.#onPeerConnected = null;
2101
- }
2102
- if (this.#reconnectTimer) {
2103
- clearTimeout(this.#reconnectTimer);
2104
- this.#reconnectTimer = null;
2105
- }
2106
- console.log("\u23F9\uFE0F Connection monitor stopped");
2107
- }
2108
- async #healthCheck() {
2109
- const now = Date.now();
2110
- const expectedNext = this.#lastHealthCheckAt + this.#healthCheckInterval;
2111
- const drift = now - expectedNext;
2112
- this.#lastHealthCheckAt = now;
2113
- if (drift > 5e3) {
2114
- console.log(`\u23F0 Detected timer drift of ${drift}ms (likely system sleep) \u2014 attempting restore`);
2115
- void this.#restoreNetwork();
2116
- }
2117
- const connectedPeers = this.connectedPeers;
2118
- const compatiblePeers = this.compatiblePeers;
2119
- const disconnectedPeers = this.disconnectedPeers;
2120
- console.log(
2121
- `\u{1F50D} Health check: ${connectedPeers.length} connected, ${compatiblePeers.length} compatible, ${disconnectedPeers.length} negotiating`
2122
- );
2123
- if (connectedPeers.length === 0 && disconnectedPeers.length === 0) {
2124
- console.warn("\u26A0\uFE0F No peer connections detected \u2014 attempting reconnection");
2125
- await this.#attemptReconnection();
2126
- } else if (compatiblePeers.length === 0 && connectedPeers.length > 0) {
2127
- console.warn("\u26A0\uFE0F No compatible peers found \u2014 attempting reconnection");
2128
- await this.#attemptReconnection();
2129
- }
2130
- globalThis.pubsub?.publish("connection-status", {
2131
- connected: connectedPeers.length,
2132
- compatible: compatiblePeers.length,
2133
- healthy: compatiblePeers.length > 0
2134
- });
2135
- }
2136
- // lightweight TCP probe to detect internet connectivity in Node.js
2137
- async #isOnLine(timeout = 1500) {
2138
- if (navigator?.onLine !== void 0) {
2139
- return navigator.onLine;
2140
- }
2141
- return new Promise(async (resolve) => {
2142
- try {
2143
- const net = await import('net');
2144
- const socket = new net.Socket();
2145
- let finished = false;
2146
- const finish = (val) => {
2147
- if (finished) return;
2148
- finished = true;
2149
- try {
2150
- socket.destroy();
2151
- } catch (e) {
2152
- }
2153
- resolve(val);
2154
- };
2155
- socket.setTimeout(timeout);
2156
- socket.once("connect", () => finish(true));
2157
- socket.once("error", () => finish(false));
2158
- socket.once("timeout", () => finish(false));
2159
- socket.connect(53, "1.1.1.1");
2160
- } catch (e) {
2161
- resolve(false);
2162
- }
2163
- });
2164
- }
2165
- // Called on visibility/online/resume events
2166
- async #restoreNetwork() {
2167
- if (this.#reconnecting) {
2168
- console.log("\u{1F501} Reconnection already in progress, skipping");
2169
- return;
2170
- }
2171
- this.#reconnecting = true;
2172
- if (this.connectedPeers.length > 0) {
2173
- console.log("\u2705 Already connected to peers, skipping restoration");
2174
- this.#reconnecting = false;
2175
- return;
2176
- }
2177
- console.log("\u{1F501} Restoring network");
2178
- try {
2179
- const online = await this.#isOnLine(1500);
2180
- if (!online) {
2181
- console.warn("\u26A0\uFE0F No internet detected, skipping restore");
2182
- this.#reconnecting = false;
2183
- return;
2184
- }
2185
- } catch (e) {
2186
- console.warn("\u26A0\uFE0F Online probe failed, proceeding with restore", e?.message || e);
2187
- }
2188
- try {
2189
- console.log("\u{1F504} Attempting network restoration...");
2190
- if (globalThis.peernet?.client?.reinit) {
2191
- console.log(" \u2192 Trying client.reinit()");
2192
- try {
2193
- await globalThis.peernet.client.reinit();
2194
- console.log(" \u2705 client.reinit() succeeded");
2195
- } catch (e) {
2196
- console.warn(" \u26A0\uFE0F client.reinit() failed:", e?.message || e);
2197
- }
2198
- }
2199
- if (globalThis.peernet?.start) {
2200
- console.log(" \u2192 Trying peernet.start()");
2201
- try {
2202
- await globalThis.peernet.start();
2203
- console.log(" \u2705 peernet.start() succeeded");
2204
- } catch (e) {
2205
- console.warn(" \u26A0\uFE0F peernet.start() failed:", e?.message || e);
2206
- }
2207
- }
2208
- try {
2209
- const networkName = globalThis.peernet?.network;
2210
- if (networkName && typeof networkName === "string") {
2211
- const { default: networks } = await import('@leofcoin/networks');
2212
- const [mainKey, subKey] = networkName.split(":");
2213
- const networkConfig = networks?.[mainKey]?.[subKey];
2214
- if (networkConfig?.stars && Array.isArray(networkConfig.stars)) {
2215
- console.log(" \u2192 Attempting to dial star servers:", networkConfig.stars.join(", "));
2216
- for (const star of networkConfig.stars) {
2217
- try {
2218
- if (globalThis.peernet?.client && "dial" in globalThis.peernet.client) {
2219
- await globalThis.peernet.client.dial(star);
2220
- console.log(` \u2705 Connected to star server: ${star}`);
2221
- }
2222
- } catch (e) {
2223
- console.warn(` \u26A0\uFE0F Failed to dial ${star}:`, e?.message || e);
2224
- }
2225
- }
2226
- }
2227
- }
2228
- } catch (e) {
2229
- console.warn(" \u26A0\uFE0F Could not load or dial star servers:", e?.message || e);
2230
- }
2231
- await new Promise((resolve) => setTimeout(resolve, 2e3));
2232
- const connectedAfter = this.connectedPeers.length;
2233
- console.log(`\u{1F504} Restoration complete. Connected peers: ${connectedAfter}`);
2234
- } catch (error) {
2235
- console.error("\u274C Network restoration failed:", error?.message || error);
2236
- } finally {
2237
- this.#reconnecting = false;
2238
- }
2239
- }
2240
- async waitForPeers(timeoutMs = 3e4) {
2241
- return new Promise((resolve) => {
2242
- const startTime = Date.now();
2243
- const checkPeers = () => {
2244
- if (this.compatiblePeers.length > 0) {
2245
- resolve(true);
2246
- return;
2247
- }
2248
- if (Date.now() - startTime >= timeoutMs) {
2249
- resolve(false);
2250
- return;
2251
- }
2252
- setTimeout(checkPeers, 1e3);
2253
- };
2254
- checkPeers();
2255
- });
2256
- }
2257
- async #attemptReconnection() {
2258
- try {
2259
- await this.#restoreNetwork();
2260
- const hasConnections = this.connectedPeers.length > 0 || this.disconnectedPeers.length > 0;
2261
- if (hasConnections) {
2262
- console.log("\u2705 Reconnection successful, resetting backoff delay");
2263
- this.#reconnectDelay = 5e3;
2264
- } else {
2265
- console.warn("\u26A0\uFE0F Reconnection attempt completed but no peers connected");
2266
- if (this.#reconnectDelay >= 3e4) {
2267
- console.warn("\u26A0\uFE0F Reconnection delay reached maximum, resetting to 5 seconds");
2268
- this.#reconnectDelay = 5e3;
2269
- } else {
2270
- this.#reconnectDelay = Math.min(this.#reconnectDelay * 1.5, 3e4);
2271
- console.warn(`\u26A0\uFE0F Increasing reconnection delay to ${this.#reconnectDelay} ms`);
2272
- }
2273
- this.#reconnectTimer = setTimeout(() => this.#attemptReconnection(), this.#reconnectDelay);
2274
- }
2275
- } catch (error) {
2276
- console.error("\u274C Reconnection failed:", error?.message || error);
2277
- if (this.#reconnectDelay >= 3e4) {
2278
- this.#reconnectDelay = 5e3;
2279
- } else {
2280
- this.#reconnectDelay = Math.min(this.#reconnectDelay * 1.5, 3e4);
2281
- }
2282
- this.#reconnectTimer = setTimeout(() => this.#attemptReconnection(), this.#reconnectDelay);
2283
- }
2284
- }
2285
- }
2286
-
2287
2011
  const validateChainLink = (localTip, incoming) => {
2288
2012
  const localHash = localTip?.hash || "0x0";
2289
2013
  const localIndex = Number(localTip?.index ?? -1);
@@ -3319,7 +3043,6 @@ class Chain extends VersionControl {
3319
3043
  seed: hash,
3320
3044
  timestamp: Number(executionTimestamp)
3321
3045
  });
3322
- globalThis.pubsub.publish(`transaction.completed.${hash}`, { status: "fulfilled", hash });
3323
3046
  return result || "no state change";
3324
3047
  } catch (error) {
3325
3048
  await transactionPoolStore.delete(hash);
@@ -3441,6 +3164,25 @@ class Chain extends VersionControl {
3441
3164
  async participate(address) {
3442
3165
  try {
3443
3166
  if (!await this.staticCall(addresses.validators, "has", [address])) {
3167
+ const minimumBalance = BigInt(await this.staticCall(addresses.validators, "minimumBalance"));
3168
+ const allowance = BigInt(
3169
+ await this.staticCall(addresses.nativeToken, "allowance", [address, addresses.validators]) || 0n
3170
+ );
3171
+ if (allowance < minimumBalance) {
3172
+ const approval = await signTransaction(
3173
+ {
3174
+ from: address,
3175
+ to: addresses.nativeToken,
3176
+ method: "approve",
3177
+ params: [addresses.validators, minimumBalance],
3178
+ nonce: await this.getNonce(address) + 1,
3179
+ timestamp: Date.now()
3180
+ },
3181
+ globalThis.peernet.identity
3182
+ );
3183
+ const pendingApproval = await this.sendTransaction(approval);
3184
+ await pendingApproval.wait;
3185
+ }
3444
3186
  const rawTransaction = {
3445
3187
  from: address,
3446
3188
  to: addresses.validators,
@@ -3717,7 +3459,8 @@ class Chain extends VersionControl {
3717
3459
  return this.call(addresses.nativeToken, "mint", [to, amount]);
3718
3460
  }
3719
3461
  transfer(from, to, amount) {
3720
- return this.call(addresses.nativeToken, "transfer", [from, to, amount]);
3462
+ if (from !== globalThis.peernet.selectedAccount) throw new Error("transfer sender must be the selected account");
3463
+ return this.call(addresses.nativeToken, "transfer", [to, amount]);
3721
3464
  }
3722
3465
  balanceOf(address) {
3723
3466
  return this.staticCall(addresses.nativeToken, "balanceOf", [address]);