@leofcoin/chain 1.9.3 → 1.9.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/exports/chain.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { createDebugger } from '@vandeurenglenn/debug';
2
2
  import { formatBytes, jsonStringifyBigInt, jsonParseBigInt, parseUnits, formatUnits } from '@leofcoin/utils';
3
- import { TransactionMessage, BlockMessage, ContractMessage, LastBlockMessage, BWMessage, BWRequestMessage } from '@leofcoin/messages';
3
+ import { TransactionMessage, BlockMessage, ContractMessage, LastBlockMessage, PrevoteMessage, PrecommitMessage, ProposalMessage, BWMessage, StateMessage, BWRequestMessage } from '@leofcoin/messages';
4
4
  import addresses, { contractFactory } from '@leofcoin/addresses';
5
5
  import { calculateFee, createContractMessage, signTransaction, contractFactoryMessage, nativeTokenMessage, validatorsMessage, nameServiceMessage } from '@leofcoin/lib';
6
6
  import semver from 'semver';
@@ -8,7 +8,6 @@ import { randombytes } from '@leofcoin/crypto';
8
8
  import EasyWorker from '@vandeurenglenn/easy-worker';
9
9
  import { ContractDeploymentError, ExecutionError, isResolveError, ResolveError, isExecutionError } from '@leofcoin/errors';
10
10
  import { log } from 'console';
11
- import codecs from '@leofcoin/codecs/utils';
12
11
  import { P as PROTOCOL_VERSION, R as REACHED_ONE_ZERO_ZERO } from './constants-eo0U5-D_.js';
13
12
  import { log as log$1 } from 'node:console';
14
13
  import '@leofcoin/networks';
@@ -1157,11 +1156,6 @@ class Jobber {
1157
1156
  }
1158
1157
  }
1159
1158
 
1160
- codecs.addCodec({
1161
- name: 'last-block-message',
1162
- codec: 0x6c626d,
1163
- hashAlg: 'keccak-256'
1164
- });
1165
1159
  const debug$1 = createDebugger('leofcoin/state');
1166
1160
  class State extends Contract {
1167
1161
  #resolveErrored;
@@ -2348,16 +2342,6 @@ class ConnectionMonitor {
2348
2342
  }
2349
2343
  }
2350
2344
 
2351
- codecs.addCodec({
2352
- name: 'last-block-message',
2353
- codec: 0x6c626d,
2354
- hashAlg: 'keccak-256'
2355
- });
2356
- codecs.addCodec({
2357
- name: 'last-block-request-message',
2358
- codec: 0x6c62726d,
2359
- hashAlg: 'keccak-256'
2360
- });
2361
2345
  const debug = createDebugger('leofcoin/chain');
2362
2346
  // check if browser or local
2363
2347
  class Chain extends VersionControl {
@@ -2451,7 +2435,10 @@ class Chain extends VersionControl {
2451
2435
  return;
2452
2436
  this.#castedVotes.add(voteKey);
2453
2437
  const from = peernet.selectedAccount;
2454
- const payload = new TextEncoder().encode(JSON.stringify({ blockHash, index, round, from }));
2438
+ const voteData = { blockHash, index: BigInt(index), round: BigInt(round), from };
2439
+ const Message = type === 'prevote' ? PrevoteMessage : PrecommitMessage;
2440
+ const message = new Message(voteData);
2441
+ const payload = message.encoded;
2455
2442
  try {
2456
2443
  globalThis.peernet.publish(`consensus:${type}`, payload);
2457
2444
  }
@@ -2466,16 +2453,17 @@ class Chain extends VersionControl {
2466
2453
  */
2467
2454
  this.#handleProposal = async (payload) => {
2468
2455
  try {
2469
- const msg = JSON.parse(new TextDecoder().decode(payload));
2456
+ const message = new ProposalMessage(payload);
2457
+ const msg = message.decoded;
2470
2458
  const { blockHash, index, round, from } = msg;
2471
- const validators = await this.#getConsensusValidators(index);
2472
- const expectedProposerIdx = (index + round) % validators.length;
2459
+ const validators = await this.#getConsensusValidators(Number(index));
2460
+ const expectedProposerIdx = Number((index + round) % BigInt(validators.length));
2473
2461
  if (!validators[expectedProposerIdx] || validators[expectedProposerIdx] !== from) {
2474
2462
  debug(`[consensus] Proposal from wrong proposer at height ${index} round ${round}`);
2475
2463
  return;
2476
2464
  }
2477
2465
  const localBlock = await this.lastBlock;
2478
- const localIndex = localBlock?.index !== undefined ? Number(localBlock.index) : -1;
2466
+ const localIndex = localBlock?.index !== undefined ? localBlock.index : -1n;
2479
2467
  if (index <= localIndex) {
2480
2468
  debug(`[consensus] Ignoring stale proposal at height ${index} (local: ${localIndex})`);
2481
2469
  return;
@@ -2494,13 +2482,13 @@ class Chain extends VersionControl {
2494
2482
  debug(`[consensus] Cannot fetch proposed block ${blockHash}:`, e?.message);
2495
2483
  return;
2496
2484
  }
2497
- this.#consensusRound = round;
2485
+ this.#consensusRound = Number(round);
2498
2486
  if (this.#roundTimer) {
2499
2487
  clearTimeout(this.#roundTimer);
2500
2488
  this.#roundTimer = null;
2501
2489
  }
2502
2490
  if (validators.includes(peernet.selectedAccount) && !this.#isJailed(peernet.selectedAccount)) {
2503
- await this.#castVote('prevote', blockHash, index, round);
2491
+ await this.#castVote('prevote', blockHash, Number(index), Number(round));
2504
2492
  }
2505
2493
  }
2506
2494
  catch (e) {
@@ -2513,13 +2501,14 @@ class Chain extends VersionControl {
2513
2501
  */
2514
2502
  this.#handlePrevote = async (payload) => {
2515
2503
  try {
2516
- const msg = JSON.parse(new TextDecoder().decode(payload));
2504
+ const message = new PrevoteMessage(payload);
2505
+ const msg = message.decoded;
2517
2506
  const { blockHash, index, round, from } = msg;
2518
- const validators = await this.#getConsensusValidators(index);
2507
+ const validators = await this.#getConsensusValidators(Number(index));
2519
2508
  if (!validators.includes(from))
2520
2509
  return;
2521
2510
  const localBlock = await this.lastBlock;
2522
- const localIndex = localBlock?.index !== undefined ? Number(localBlock.index) : -1;
2511
+ const localIndex = localBlock?.index !== undefined ? localBlock.index : -1n;
2523
2512
  if (index <= localIndex)
2524
2513
  return;
2525
2514
  const voteKey = `${index}:${round}:${blockHash}`;
@@ -2532,7 +2521,7 @@ class Chain extends VersionControl {
2532
2521
  if (voteCount >= threshold &&
2533
2522
  validators.includes(peernet.selectedAccount) &&
2534
2523
  !this.#isJailed(peernet.selectedAccount)) {
2535
- await this.#castVote('precommit', blockHash, index, round);
2524
+ await this.#castVote('precommit', blockHash, Number(index), Number(round));
2536
2525
  }
2537
2526
  }
2538
2527
  catch (e) {
@@ -2546,12 +2535,13 @@ class Chain extends VersionControl {
2546
2535
  */
2547
2536
  this.#handlePrecommit = async (payload) => {
2548
2537
  try {
2549
- const msg = JSON.parse(new TextDecoder().decode(payload));
2538
+ const message = new PrecommitMessage(payload);
2539
+ const msg = message.decoded;
2550
2540
  const { blockHash, index, round, from } = msg;
2551
- const validators = await this.#getConsensusValidators(index);
2541
+ const validators = await this.#getConsensusValidators(Number(index));
2552
2542
  if (!validators.includes(from))
2553
2543
  return;
2554
- if (index <= this.#committedHeight)
2544
+ if (index <= BigInt(this.#committedHeight))
2555
2545
  return;
2556
2546
  const voteKey = `${index}:${round}:${blockHash}`;
2557
2547
  if (!this.#precommits.has(voteKey))
@@ -2560,27 +2550,27 @@ class Chain extends VersionControl {
2560
2550
  const threshold = Math.ceil((2 * validators.length) / 3);
2561
2551
  const voteCount = this.#precommits.get(voteKey).size;
2562
2552
  debug(`[consensus] Precommits ${voteKey}: ${voteCount}/${validators.length} (need ${threshold})`);
2563
- if (voteCount >= threshold && index > this.#committedHeight) {
2564
- this.#committedHeight = index;
2553
+ if (voteCount >= threshold && index > BigInt(this.#committedHeight)) {
2554
+ this.#committedHeight = Number(index);
2565
2555
  this.#consensusRound = 0;
2566
2556
  // Prune vote state for committed and older heights
2567
2557
  for (const key of [...this.#prevotes.keys()]) {
2568
- if (Number(key.split(':')[0]) <= index)
2558
+ if (BigInt(key.split(':')[0]) <= index)
2569
2559
  this.#prevotes.delete(key);
2570
2560
  }
2571
2561
  for (const key of [...this.#precommits.keys()]) {
2572
- if (Number(key.split(':')[0]) <= index)
2562
+ if (BigInt(key.split(':')[0]) <= index)
2573
2563
  this.#precommits.delete(key);
2574
2564
  }
2575
2565
  for (const key of [...this.#castedVotes]) {
2576
- if (Number(key.split(':')[1]) <= index)
2566
+ if (BigInt(key.split(':')[1]) <= index)
2577
2567
  this.#castedVotes.delete(key);
2578
2568
  }
2579
2569
  // Non-proposers add the block to local state now.
2580
2570
  // Proposers already committed state in #createBlock() and their
2581
2571
  // lastBlock.index === index, so the guard below skips them.
2582
2572
  const currentBlock = await this.lastBlock;
2583
- const currentIndex = currentBlock?.index !== undefined ? Number(currentBlock.index) : -1;
2573
+ const currentIndex = currentBlock?.index !== undefined ? currentBlock.index : -1n;
2584
2574
  if (index > currentIndex) {
2585
2575
  debug(`[consensus] ✅ Committing block ${blockHash} at height ${index}`);
2586
2576
  try {
@@ -2853,7 +2843,9 @@ class Chain extends VersionControl {
2853
2843
  await globalThis.peernet.addRequestHandler('transactionPool', this.#transactionPoolHandler.bind(this));
2854
2844
  await globalThis.peernet.addRequestHandler('version', this.#versionHandler.bind(this));
2855
2845
  await globalThis.peernet.addRequestHandler('stateInfo', () => {
2856
- return new globalThis.peernet.protos['peernet-response']({ response: this.machine.states.info });
2846
+ return new globalThis.peernet.protos['peernet-response']({
2847
+ response: new StateMessage(this.machine.states.info).encoded
2848
+ });
2857
2849
  });
2858
2850
  globalThis.peernet.subscribe('add-block', this.#addBlock.bind(this));
2859
2851
  globalThis.peernet.subscribe('invalid-transaction', this.#invalidTransaction.bind(this));
@@ -2941,13 +2933,8 @@ class Chain extends VersionControl {
2941
2933
  async getPeerTransactionPool(peer) {
2942
2934
  let transactionsInPool = await this.#makeRequest(peer, 'transactionPool');
2943
2935
  if (transactionsInPool instanceof Uint8Array) {
2944
- try {
2945
- const text = new TextDecoder().decode(transactionsInPool);
2946
- transactionsInPool = JSON.parse(text);
2947
- }
2948
- catch (e) {
2949
- return [];
2950
- }
2936
+ debug('transactionPool response must be decoded array payload');
2937
+ return [];
2951
2938
  }
2952
2939
  if (!Array.isArray(transactionsInPool))
2953
2940
  return [];
@@ -2979,13 +2966,7 @@ class Chain extends VersionControl {
2979
2966
  try {
2980
2967
  let versionResponse = await this.#makeRequest(peer, 'version');
2981
2968
  if (versionResponse instanceof Uint8Array) {
2982
- const decoded = new TextDecoder().decode(versionResponse);
2983
- try {
2984
- versionResponse = JSON.parse(decoded);
2985
- }
2986
- catch {
2987
- versionResponse = decoded;
2988
- }
2969
+ versionResponse = new TextDecoder().decode(versionResponse);
2989
2970
  }
2990
2971
  if (typeof versionResponse === 'string') {
2991
2972
  peer.version = versionResponse;
@@ -2995,6 +2976,12 @@ class Chain extends VersionControl {
2995
2976
  typeof versionResponse.version === 'string') {
2996
2977
  peer.version = versionResponse.version;
2997
2978
  }
2979
+ if (!peer.version || typeof peer.version !== 'string') {
2980
+ const reason = `invalid version response from peer ${peerId}`;
2981
+ debug(reason);
2982
+ await this.#recordPeerFailure(peerId, reason);
2983
+ return;
2984
+ }
2998
2985
  }
2999
2986
  catch (error) {
3000
2987
  debug(`failed to request version from peer ${peerId}:`, error?.message ?? error);
@@ -3003,7 +2990,9 @@ class Chain extends VersionControl {
3003
2990
  }
3004
2991
  debug(`peer connected with version ${peer.version}`);
3005
2992
  if (!this.isVersionCompatible(peer.version)) {
3006
- debug(`versions don't match`);
2993
+ const mismatchReason = `incompatible peer version ${peer.version} (local: ${this.version})`;
2994
+ console.error(`[chain] ${mismatchReason}`);
2995
+ await this.#recordPeerFailure(peerId, mismatchReason);
3007
2996
  return;
3008
2997
  }
3009
2998
  let lastBlock;
@@ -3023,7 +3012,7 @@ class Chain extends VersionControl {
3023
3012
  // This prevents Byzantine nodes from claiming a fake chain length to steer our sync
3024
3013
  const localBlock = await this.lastBlock;
3025
3014
  const MAX_SYNC_AHEAD = 100_000;
3026
- if (lastBlock?.index > (localBlock?.index ?? 0) + MAX_SYNC_AHEAD) {
3015
+ if (lastBlock?.index > BigInt(localBlock?.index ?? 0) + BigInt(MAX_SYNC_AHEAD)) {
3027
3016
  const peerName = peer?.peerId || peer?.id || peer?.address || peerId || 'unknown';
3028
3017
  debug(`Peer ${peerName} claims unreasonable block height ${lastBlock.index} (local: ${localBlock?.index ?? 0})`);
3029
3018
  await this.#recordPeerFailure(peerId, `unreasonable lastBlock index: ${lastBlock.index}`);
@@ -3039,15 +3028,13 @@ class Chain extends VersionControl {
3039
3028
  try {
3040
3029
  let knownBlocksResponse = await this.#makeRequest(peer, 'knownBlocks');
3041
3030
  if (knownBlocksResponse instanceof Uint8Array) {
3042
- try {
3043
- knownBlocksResponse = JSON.parse(new TextDecoder().decode(knownBlocksResponse));
3044
- }
3045
- catch (e) {
3046
- console.log(e);
3047
- }
3031
+ const reason = `knownBlocks must be object response, got raw bytes from ${peerId}`;
3032
+ debug(reason);
3033
+ await this.#recordPeerFailure(peerId, reason);
3034
+ return;
3048
3035
  }
3049
3036
  const MAX_WANTLIST_SIZE = 1000;
3050
- if (knownBlocksResponse.blocks) {
3037
+ if (knownBlocksResponse && Array.isArray(knownBlocksResponse.blocks)) {
3051
3038
  const remaining = MAX_WANTLIST_SIZE - this.wantList.length;
3052
3039
  if (remaining > 0) {
3053
3040
  for (const hash of knownBlocksResponse.blocks.slice(0, remaining)) {
@@ -3089,12 +3076,7 @@ class Chain extends VersionControl {
3089
3076
  try {
3090
3077
  let stateInfo = await this.#makeRequest(peer, 'stateInfo');
3091
3078
  if (stateInfo instanceof Uint8Array) {
3092
- try {
3093
- stateInfo = JSON.parse(new TextDecoder().decode(stateInfo));
3094
- }
3095
- catch (e) {
3096
- console.log(e);
3097
- }
3079
+ stateInfo = new StateMessage(stateInfo).decoded;
3098
3080
  }
3099
3081
  await this.syncChain(lastBlock);
3100
3082
  this.machine.states.info = stateInfo;
@@ -3112,7 +3094,7 @@ class Chain extends VersionControl {
3112
3094
  return new globalThis.peernet.protos['peernet-response']({ response: pool });
3113
3095
  }
3114
3096
  async #versionHandler() {
3115
- return new globalThis.peernet.protos['peernet-response']({ response: { version: this.version } });
3097
+ return new globalThis.peernet.protos['peernet-response']({ response: this.version });
3116
3098
  }
3117
3099
  async #executeTransaction({ hash, from, to, method, params, nonce }) {
3118
3100
  try {
@@ -3464,12 +3446,14 @@ class Chain extends VersionControl {
3464
3446
  debug(`created block: ${hash} @${block.index}`);
3465
3447
  // Phase 2: announce proposal for consensus voting instead of direct add-block
3466
3448
  console.log(`[consensus] 📤 Proposing block #${block.index} | hash: ${hash} | round: ${this.#consensusRound}`);
3467
- const proposalPayload = new TextEncoder().encode(JSON.stringify({
3449
+ const proposalData = {
3468
3450
  blockHash: hash,
3469
- index: block.index,
3470
- round: this.#consensusRound,
3451
+ index: BigInt(block.index),
3452
+ round: BigInt(this.#consensusRound),
3471
3453
  from: peernet.selectedAccount
3472
- }));
3454
+ };
3455
+ const proposalMessage = new ProposalMessage(proposalData);
3456
+ const proposalPayload = proposalMessage.encoded;
3473
3457
  try {
3474
3458
  globalThis.peernet.publish('consensus:propose', proposalPayload);
3475
3459
  }
@@ -1,4 +1,4 @@
1
- import { E as EasyWorker, B as BlockMessage } from './worker-BrtyXRJ7-BrtyXRJ7.js';
1
+ import { E as EasyWorker, B as BlockMessage } from './worker-Bsi6vKgF-Bsi6vKgF.js';
2
2
 
3
3
  const worker = new EasyWorker();
4
4
  globalThis.BigNumber = BigNumber;
@@ -1,4 +1,4 @@
1
- import { E as EasyWorker, C as ContractMessage, T as TransactionMessage } from './worker-BrtyXRJ7-BrtyXRJ7.js';
1
+ import { E as EasyWorker, C as ContractMessage, T as TransactionMessage } from './worker-Bsi6vKgF-Bsi6vKgF.js';
2
2
 
3
3
  /* Do NOT modify this file; see /src.ts/_admin/update-version.ts */
4
4
  /**