@leofcoin/chain 1.9.3 → 1.9.5

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.
@@ -1,4 +1,4 @@
1
- import { t as toBase58, T as TransactionMessage, C as ContractMessage, R as RawTransactionMessage, B as BlockMessage, u as utils, L as LastBlockMessage, P as PROTOCOL_VERSION, a as REACHED_ONE_ZERO_ZERO, b as BWMessage, c as BWRequestMessage } from './constants-BTdMMS4w.js';
1
+ import { t as toBase58, T as TransactionMessage, C as ContractMessage, R as RawTransactionMessage, B as BlockMessage, L as LastBlockMessage, P as PROTOCOL_VERSION, a as REACHED_ONE_ZERO_ZERO, u as utils, b as PrevoteMessage, c as PrecommitMessage, d as ProposalMessage, e as BWMessage, S as StateMessage, f as BWRequestMessage } from './constants-M3qbPwZp.js';
2
2
  import { log } from 'console';
3
3
  import { log as log$1 } from 'node:console';
4
4
 
@@ -5055,11 +5055,6 @@ class Jobber {
5055
5055
  }
5056
5056
  }
5057
5057
 
5058
- utils.addCodec({
5059
- name: 'last-block-message',
5060
- codec: 0x6c626d,
5061
- hashAlg: 'keccak-256'
5062
- });
5063
5058
  const debug$1 = createDebugger('leofcoin/state');
5064
5059
  class State extends Contract {
5065
5060
  #resolveErrored;
@@ -6154,7 +6149,7 @@ class ConnectionMonitor {
6154
6149
  const networkName = globalThis.peernet?.network;
6155
6150
  if (networkName && typeof networkName === 'string') {
6156
6151
  // Try to import network config
6157
- const { default: networks } = await import('./constants-BTdMMS4w.js').then(function (n) { return n.n; });
6152
+ const { default: networks } = await import('./constants-M3qbPwZp.js').then(function (n) { return n.n; });
6158
6153
  const [mainKey, subKey] = networkName.split(':');
6159
6154
  const networkConfig = networks?.[mainKey]?.[subKey];
6160
6155
  if (networkConfig?.stars && Array.isArray(networkConfig.stars)) {
@@ -6246,16 +6241,20 @@ class ConnectionMonitor {
6246
6241
  }
6247
6242
  }
6248
6243
 
6249
- utils.addCodec({
6250
- name: 'last-block-message',
6251
- codec: 0x6c626d,
6252
- hashAlg: 'keccak-256'
6253
- });
6254
- utils.addCodec({
6255
- name: 'last-block-request-message',
6256
- codec: 0x6c62726d,
6257
- hashAlg: 'keccak-256'
6258
- });
6244
+ const ensureCodec = (name, codec, hashAlg = 'keccak-256') => {
6245
+ if (!utils.codecs[name]) {
6246
+ utils.addCodec({ name, codec, hashAlg });
6247
+ }
6248
+ };
6249
+ // Backward compatibility for mixed deployments where some nodes still resolve
6250
+ // older @leofcoin/codecs versions that do not include these protocol codecs.
6251
+ ensureCodec('last-block-message', 0x6c626d);
6252
+ ensureCodec('last-block-request-message', 0x6c62726d);
6253
+ ensureCodec('state-message', 0x73746d);
6254
+ ensureCodec('publish-message', 0x70626d);
6255
+ ensureCodec('proposal-message', 0x70726d);
6256
+ ensureCodec('prevote-message', 0x70766d);
6257
+ ensureCodec('precommit-message', 0x7063636d);
6259
6258
  const debug = createDebugger('leofcoin/chain');
6260
6259
  // check if browser or local
6261
6260
  class Chain extends VersionControl {
@@ -6349,7 +6348,10 @@ class Chain extends VersionControl {
6349
6348
  return;
6350
6349
  this.#castedVotes.add(voteKey);
6351
6350
  const from = peernet.selectedAccount;
6352
- const payload = new TextEncoder().encode(JSON.stringify({ blockHash, index, round, from }));
6351
+ const voteData = { blockHash, index: BigInt(index), round: BigInt(round), from };
6352
+ const Message = type === 'prevote' ? PrevoteMessage : PrecommitMessage;
6353
+ const message = new Message(voteData);
6354
+ const payload = message.encoded;
6353
6355
  try {
6354
6356
  globalThis.peernet.publish(`consensus:${type}`, payload);
6355
6357
  }
@@ -6364,16 +6366,17 @@ class Chain extends VersionControl {
6364
6366
  */
6365
6367
  this.#handleProposal = async (payload) => {
6366
6368
  try {
6367
- const msg = JSON.parse(new TextDecoder().decode(payload));
6369
+ const message = new ProposalMessage(payload);
6370
+ const msg = message.decoded;
6368
6371
  const { blockHash, index, round, from } = msg;
6369
- const validators = await this.#getConsensusValidators(index);
6370
- const expectedProposerIdx = (index + round) % validators.length;
6372
+ const validators = await this.#getConsensusValidators(Number(index));
6373
+ const expectedProposerIdx = Number((index + round) % BigInt(validators.length));
6371
6374
  if (!validators[expectedProposerIdx] || validators[expectedProposerIdx] !== from) {
6372
6375
  debug(`[consensus] Proposal from wrong proposer at height ${index} round ${round}`);
6373
6376
  return;
6374
6377
  }
6375
6378
  const localBlock = await this.lastBlock;
6376
- const localIndex = localBlock?.index !== undefined ? Number(localBlock.index) : -1;
6379
+ const localIndex = localBlock?.index !== undefined ? localBlock.index : -1n;
6377
6380
  if (index <= localIndex) {
6378
6381
  debug(`[consensus] Ignoring stale proposal at height ${index} (local: ${localIndex})`);
6379
6382
  return;
@@ -6392,13 +6395,13 @@ class Chain extends VersionControl {
6392
6395
  debug(`[consensus] Cannot fetch proposed block ${blockHash}:`, e?.message);
6393
6396
  return;
6394
6397
  }
6395
- this.#consensusRound = round;
6398
+ this.#consensusRound = Number(round);
6396
6399
  if (this.#roundTimer) {
6397
6400
  clearTimeout(this.#roundTimer);
6398
6401
  this.#roundTimer = null;
6399
6402
  }
6400
6403
  if (validators.includes(peernet.selectedAccount) && !this.#isJailed(peernet.selectedAccount)) {
6401
- await this.#castVote('prevote', blockHash, index, round);
6404
+ await this.#castVote('prevote', blockHash, Number(index), Number(round));
6402
6405
  }
6403
6406
  }
6404
6407
  catch (e) {
@@ -6411,13 +6414,14 @@ class Chain extends VersionControl {
6411
6414
  */
6412
6415
  this.#handlePrevote = async (payload) => {
6413
6416
  try {
6414
- const msg = JSON.parse(new TextDecoder().decode(payload));
6417
+ const message = new PrevoteMessage(payload);
6418
+ const msg = message.decoded;
6415
6419
  const { blockHash, index, round, from } = msg;
6416
- const validators = await this.#getConsensusValidators(index);
6420
+ const validators = await this.#getConsensusValidators(Number(index));
6417
6421
  if (!validators.includes(from))
6418
6422
  return;
6419
6423
  const localBlock = await this.lastBlock;
6420
- const localIndex = localBlock?.index !== undefined ? Number(localBlock.index) : -1;
6424
+ const localIndex = localBlock?.index !== undefined ? localBlock.index : -1n;
6421
6425
  if (index <= localIndex)
6422
6426
  return;
6423
6427
  const voteKey = `${index}:${round}:${blockHash}`;
@@ -6430,7 +6434,7 @@ class Chain extends VersionControl {
6430
6434
  if (voteCount >= threshold &&
6431
6435
  validators.includes(peernet.selectedAccount) &&
6432
6436
  !this.#isJailed(peernet.selectedAccount)) {
6433
- await this.#castVote('precommit', blockHash, index, round);
6437
+ await this.#castVote('precommit', blockHash, Number(index), Number(round));
6434
6438
  }
6435
6439
  }
6436
6440
  catch (e) {
@@ -6444,12 +6448,13 @@ class Chain extends VersionControl {
6444
6448
  */
6445
6449
  this.#handlePrecommit = async (payload) => {
6446
6450
  try {
6447
- const msg = JSON.parse(new TextDecoder().decode(payload));
6451
+ const message = new PrecommitMessage(payload);
6452
+ const msg = message.decoded;
6448
6453
  const { blockHash, index, round, from } = msg;
6449
- const validators = await this.#getConsensusValidators(index);
6454
+ const validators = await this.#getConsensusValidators(Number(index));
6450
6455
  if (!validators.includes(from))
6451
6456
  return;
6452
- if (index <= this.#committedHeight)
6457
+ if (index <= BigInt(this.#committedHeight))
6453
6458
  return;
6454
6459
  const voteKey = `${index}:${round}:${blockHash}`;
6455
6460
  if (!this.#precommits.has(voteKey))
@@ -6458,27 +6463,27 @@ class Chain extends VersionControl {
6458
6463
  const threshold = Math.ceil((2 * validators.length) / 3);
6459
6464
  const voteCount = this.#precommits.get(voteKey).size;
6460
6465
  debug(`[consensus] Precommits ${voteKey}: ${voteCount}/${validators.length} (need ${threshold})`);
6461
- if (voteCount >= threshold && index > this.#committedHeight) {
6462
- this.#committedHeight = index;
6466
+ if (voteCount >= threshold && index > BigInt(this.#committedHeight)) {
6467
+ this.#committedHeight = Number(index);
6463
6468
  this.#consensusRound = 0;
6464
6469
  // Prune vote state for committed and older heights
6465
6470
  for (const key of [...this.#prevotes.keys()]) {
6466
- if (Number(key.split(':')[0]) <= index)
6471
+ if (BigInt(key.split(':')[0]) <= index)
6467
6472
  this.#prevotes.delete(key);
6468
6473
  }
6469
6474
  for (const key of [...this.#precommits.keys()]) {
6470
- if (Number(key.split(':')[0]) <= index)
6475
+ if (BigInt(key.split(':')[0]) <= index)
6471
6476
  this.#precommits.delete(key);
6472
6477
  }
6473
6478
  for (const key of [...this.#castedVotes]) {
6474
- if (Number(key.split(':')[1]) <= index)
6479
+ if (BigInt(key.split(':')[1]) <= index)
6475
6480
  this.#castedVotes.delete(key);
6476
6481
  }
6477
6482
  // Non-proposers add the block to local state now.
6478
6483
  // Proposers already committed state in #createBlock() and their
6479
6484
  // lastBlock.index === index, so the guard below skips them.
6480
6485
  const currentBlock = await this.lastBlock;
6481
- const currentIndex = currentBlock?.index !== undefined ? Number(currentBlock.index) : -1;
6486
+ const currentIndex = currentBlock?.index !== undefined ? currentBlock.index : -1n;
6482
6487
  if (index > currentIndex) {
6483
6488
  debug(`[consensus] ✅ Committing block ${blockHash} at height ${index}`);
6484
6489
  try {
@@ -6751,7 +6756,9 @@ class Chain extends VersionControl {
6751
6756
  await globalThis.peernet.addRequestHandler('transactionPool', this.#transactionPoolHandler.bind(this));
6752
6757
  await globalThis.peernet.addRequestHandler('version', this.#versionHandler.bind(this));
6753
6758
  await globalThis.peernet.addRequestHandler('stateInfo', () => {
6754
- return new globalThis.peernet.protos['peernet-response']({ response: this.machine.states.info });
6759
+ return new globalThis.peernet.protos['peernet-response']({
6760
+ response: new StateMessage(this.machine.states.info).encoded
6761
+ });
6755
6762
  });
6756
6763
  globalThis.peernet.subscribe('add-block', this.#addBlock.bind(this));
6757
6764
  globalThis.peernet.subscribe('invalid-transaction', this.#invalidTransaction.bind(this));
@@ -6839,13 +6846,8 @@ class Chain extends VersionControl {
6839
6846
  async getPeerTransactionPool(peer) {
6840
6847
  let transactionsInPool = await this.#makeRequest(peer, 'transactionPool');
6841
6848
  if (transactionsInPool instanceof Uint8Array) {
6842
- try {
6843
- const text = new TextDecoder().decode(transactionsInPool);
6844
- transactionsInPool = JSON.parse(text);
6845
- }
6846
- catch (e) {
6847
- return [];
6848
- }
6849
+ debug('transactionPool response must be decoded array payload');
6850
+ return [];
6849
6851
  }
6850
6852
  if (!Array.isArray(transactionsInPool))
6851
6853
  return [];
@@ -6877,13 +6879,7 @@ class Chain extends VersionControl {
6877
6879
  try {
6878
6880
  let versionResponse = await this.#makeRequest(peer, 'version');
6879
6881
  if (versionResponse instanceof Uint8Array) {
6880
- const decoded = new TextDecoder().decode(versionResponse);
6881
- try {
6882
- versionResponse = JSON.parse(decoded);
6883
- }
6884
- catch {
6885
- versionResponse = decoded;
6886
- }
6882
+ versionResponse = new TextDecoder().decode(versionResponse);
6887
6883
  }
6888
6884
  if (typeof versionResponse === 'string') {
6889
6885
  peer.version = versionResponse;
@@ -6893,6 +6889,12 @@ class Chain extends VersionControl {
6893
6889
  typeof versionResponse.version === 'string') {
6894
6890
  peer.version = versionResponse.version;
6895
6891
  }
6892
+ if (!peer.version || typeof peer.version !== 'string') {
6893
+ const reason = `invalid version response from peer ${peerId}`;
6894
+ debug(reason);
6895
+ await this.#recordPeerFailure(peerId, reason);
6896
+ return;
6897
+ }
6896
6898
  }
6897
6899
  catch (error) {
6898
6900
  debug(`failed to request version from peer ${peerId}:`, error?.message ?? error);
@@ -6901,7 +6903,9 @@ class Chain extends VersionControl {
6901
6903
  }
6902
6904
  debug(`peer connected with version ${peer.version}`);
6903
6905
  if (!this.isVersionCompatible(peer.version)) {
6904
- debug(`versions don't match`);
6906
+ const mismatchReason = `incompatible peer version ${peer.version} (local: ${this.version})`;
6907
+ console.error(`[chain] ${mismatchReason}`);
6908
+ await this.#recordPeerFailure(peerId, mismatchReason);
6905
6909
  return;
6906
6910
  }
6907
6911
  let lastBlock;
@@ -6921,7 +6925,7 @@ class Chain extends VersionControl {
6921
6925
  // This prevents Byzantine nodes from claiming a fake chain length to steer our sync
6922
6926
  const localBlock = await this.lastBlock;
6923
6927
  const MAX_SYNC_AHEAD = 100_000;
6924
- if (lastBlock?.index > (localBlock?.index ?? 0) + MAX_SYNC_AHEAD) {
6928
+ if (lastBlock?.index > BigInt(localBlock?.index ?? 0) + BigInt(MAX_SYNC_AHEAD)) {
6925
6929
  const peerName = peer?.peerId || peer?.id || peer?.address || peerId || 'unknown';
6926
6930
  debug(`Peer ${peerName} claims unreasonable block height ${lastBlock.index} (local: ${localBlock?.index ?? 0})`);
6927
6931
  await this.#recordPeerFailure(peerId, `unreasonable lastBlock index: ${lastBlock.index}`);
@@ -6937,15 +6941,13 @@ class Chain extends VersionControl {
6937
6941
  try {
6938
6942
  let knownBlocksResponse = await this.#makeRequest(peer, 'knownBlocks');
6939
6943
  if (knownBlocksResponse instanceof Uint8Array) {
6940
- try {
6941
- knownBlocksResponse = JSON.parse(new TextDecoder().decode(knownBlocksResponse));
6942
- }
6943
- catch (e) {
6944
- console.log(e);
6945
- }
6944
+ const reason = `knownBlocks must be object response, got raw bytes from ${peerId}`;
6945
+ debug(reason);
6946
+ await this.#recordPeerFailure(peerId, reason);
6947
+ return;
6946
6948
  }
6947
6949
  const MAX_WANTLIST_SIZE = 1000;
6948
- if (knownBlocksResponse.blocks) {
6950
+ if (knownBlocksResponse && Array.isArray(knownBlocksResponse.blocks)) {
6949
6951
  const remaining = MAX_WANTLIST_SIZE - this.wantList.length;
6950
6952
  if (remaining > 0) {
6951
6953
  for (const hash of knownBlocksResponse.blocks.slice(0, remaining)) {
@@ -6987,12 +6989,7 @@ class Chain extends VersionControl {
6987
6989
  try {
6988
6990
  let stateInfo = await this.#makeRequest(peer, 'stateInfo');
6989
6991
  if (stateInfo instanceof Uint8Array) {
6990
- try {
6991
- stateInfo = JSON.parse(new TextDecoder().decode(stateInfo));
6992
- }
6993
- catch (e) {
6994
- console.log(e);
6995
- }
6992
+ stateInfo = new StateMessage(stateInfo).decoded;
6996
6993
  }
6997
6994
  await this.syncChain(lastBlock);
6998
6995
  this.machine.states.info = stateInfo;
@@ -7010,7 +7007,7 @@ class Chain extends VersionControl {
7010
7007
  return new globalThis.peernet.protos['peernet-response']({ response: pool });
7011
7008
  }
7012
7009
  async #versionHandler() {
7013
- return new globalThis.peernet.protos['peernet-response']({ response: { version: this.version } });
7010
+ return new globalThis.peernet.protos['peernet-response']({ response: this.version });
7014
7011
  }
7015
7012
  async #executeTransaction({ hash, from, to, method, params, nonce }) {
7016
7013
  try {
@@ -7362,12 +7359,14 @@ class Chain extends VersionControl {
7362
7359
  debug(`created block: ${hash} @${block.index}`);
7363
7360
  // Phase 2: announce proposal for consensus voting instead of direct add-block
7364
7361
  console.log(`[consensus] 📤 Proposing block #${block.index} | hash: ${hash} | round: ${this.#consensusRound}`);
7365
- const proposalPayload = new TextEncoder().encode(JSON.stringify({
7362
+ const proposalData = {
7366
7363
  blockHash: hash,
7367
- index: block.index,
7368
- round: this.#consensusRound,
7364
+ index: BigInt(block.index),
7365
+ round: BigInt(this.#consensusRound),
7369
7366
  from: peernet.selectedAccount
7370
- }));
7367
+ };
7368
+ const proposalMessage = new ProposalMessage(proposalData);
7369
+ const proposalPayload = proposalMessage.encoded;
7371
7370
  try {
7372
7371
  globalThis.peernet.publish('consensus:propose', proposalPayload);
7373
7372
  }