@leofcoin/chain 1.10.9 → 1.10.11

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 (44) hide show
  1. package/exports/beacon-envelope.js +68 -0
  2. package/exports/beacon-epoch.js +117 -0
  3. package/exports/beacon-lifecycle.js +155 -0
  4. package/exports/beacon-round.js +77 -0
  5. package/exports/beacon-wire.js +98 -0
  6. package/exports/beacon.js +141 -0
  7. package/exports/browser/beacon-envelope.js +163 -0
  8. package/exports/browser/beacon-epoch.js +116 -0
  9. package/exports/browser/beacon-lifecycle.js +154 -0
  10. package/exports/browser/beacon-round.js +76 -0
  11. package/exports/browser/beacon-wire.js +99 -0
  12. package/exports/browser/beacon.js +1706 -0
  13. package/exports/browser/{browser-D-r0O9Qn-BZDYY6cg.js → browser-CWeoyGUw-BabtHowB.js} +4 -2
  14. package/exports/browser/{browser-_hiyXwPp-DYI1tyUr.js → browser-DvU1xNFS-sdlKNCJc.js} +4 -2
  15. package/exports/browser/chain.js +174 -5008
  16. package/exports/browser/{client-BVhUamQG-D-D1e_x8.js → client-B-jyclOB-CsNYfj4Z.js} +6 -4
  17. package/exports/browser/constants-Cv0p224A.js +130 -0
  18. package/exports/browser/hkdf-DhdhLAAv.js +147 -0
  19. package/exports/browser/{index-BD0Anx7_-Dg4_tCeN.js → index-Bxr5Iztg-C6xBk0rK.js} +4 -2
  20. package/exports/browser/index-CvKt4UDE.js +464 -0
  21. package/exports/browser/index-D7FUx7Dd.js +4996 -0
  22. package/exports/browser/{messages-UKnuelZ7-DJT-98q-.js → messages-FMRAS8QX-CvlZBzy5.js} +4 -2
  23. package/exports/browser/{node-browser-DlzZ5CP_.js → node-browser-xlqSBOaN.js} +12 -5
  24. package/exports/browser/node-browser.js +4 -2
  25. package/exports/browser/{constants-gMYZLHKp.js → proposal.proto-BcUgd885.js} +61 -620
  26. package/exports/browser/quorum-S_qdgiAY.js +8 -0
  27. package/exports/browser/weierstrass-C-jX_jly.js +2156 -0
  28. package/exports/browser/workers/block-worker.js +1 -1
  29. package/exports/browser/workers/machine-worker.js +7142 -72
  30. package/exports/browser/workers/{worker-CZqErLI7-BxofVJAn.js → worker-DMCj1e6z-CTYVa2yX.js} +30 -1
  31. package/exports/chain.js +158 -75
  32. package/exports/{constants-D6gWzJZg.js → constants-CMYKv-Rt.js} +1 -1
  33. package/exports/node.js +1 -1
  34. package/exports/quorum-S_qdgiAY.js +8 -0
  35. package/exports/workers/block-worker.js +1 -1
  36. package/exports/workers/machine-worker.js +7142 -72
  37. package/exports/workers/{worker-CZqErLI7-BxofVJAn.js → worker-DMCj1e6z-CTYVa2yX.js} +30 -1
  38. package/package.json +29 -2
  39. package/types/beacon-envelope.d.ts +27 -0
  40. package/types/beacon-epoch.d.ts +28 -0
  41. package/types/beacon-lifecycle.d.ts +40 -0
  42. package/types/beacon-round.d.ts +18 -0
  43. package/types/beacon-wire.d.ts +31 -0
  44. package/types/beacon.d.ts +24 -0
@@ -7929,6 +7929,21 @@ const blockchainCodecs = [
7929
7929
  }
7930
7930
  ];
7931
7931
  const consensusCodecs = [
7932
+ {
7933
+ name: 'beacon-activation-message',
7934
+ codec: '0x62616d',
7935
+ hashAlg: 'keccak-256'
7936
+ },
7937
+ {
7938
+ name: 'beacon-commitment-message',
7939
+ codec: '0x62636d',
7940
+ hashAlg: 'keccak-256'
7941
+ },
7942
+ {
7943
+ name: 'beacon-share-message',
7944
+ codec: '0x62736d',
7945
+ hashAlg: 'keccak-256'
7946
+ },
7932
7947
  {
7933
7948
  name: 'proposal-message',
7934
7949
  codec: '0x70726d',
@@ -9053,6 +9068,20 @@ var proto$2 = {
9053
9068
  reward: BigInt(0)
9054
9069
  };
9055
9070
 
9071
+ ({
9072
+ epoch: BigInt(0)});
9073
+
9074
+ ({
9075
+ epoch: BigInt(0),
9076
+ threshold: BigInt(0),
9077
+ participant: BigInt(0),
9078
+ commitments: Array()});
9079
+
9080
+ ({
9081
+ epoch: BigInt(0),
9082
+ round: BigInt(0),
9083
+ participant: BigInt(0)});
9084
+
9056
9085
  var proto$1 = {
9057
9086
  creator: String(),
9058
9087
  contract: new Uint8Array(),
@@ -9207,4 +9236,4 @@ class EasyWorker {
9207
9236
  }
9208
9237
  }
9209
9238
 
9210
- export { EasyWorker as E, FormatInterface as F, index$2 as a, proto$2 as b, jsonStringifyBigInt$1 as c, proto$1 as d, proto as e, index$3 as i, jsonParseBigInt$1 as j, proto$3 as p };
9239
+ export { EasyWorker as E, FormatInterface as F, index$2 as a, proto$2 as b, jsonParseBigInt$1 as c, proto$1 as d, proto as e, index$3 as i, jsonStringifyBigInt$1 as j, proto$3 as p };
package/exports/chain.js CHANGED
@@ -2,15 +2,16 @@ import { createDebugger } from '@vandeurenglenn/debug';
2
2
  import { Codec } from '@leofcoin/codec-format-interface';
3
3
  import { jsonStringifyBigInt, jsonParseBigInt, formatBytes, parseUnits, formatUnits } from '@leofcoin/utils';
4
4
  import { TransactionMessage, BlockMessage, ContractMessage, LastBlockMessage, PrevoteMessage, PrecommitMessage, ProposalMessage, BWMessage, StateMessage } from '@leofcoin/messages';
5
- import addresses, { contractFactory } from '@leofcoin/addresses';
6
- import { createTransactionHash, calculateFee, createContractMessage, signTransaction, distributeTransactionFee, supportsTransactionFees, aggregateValidatorFees, contractFactoryMessage, nativeTokenMessage, validatorsMessage, nameServiceMessage } from '@leofcoin/lib';
5
+ import addresses, { contractFactory, nativeToken } from '@leofcoin/addresses';
6
+ import { MAX_TRANSACTION_BYTES, MAX_BLOCK_TRANSACTION_BYTES, MAX_BLOCK_TRANSACTIONS, createTransactionHash, calculateFee, createContractMessage, signTransaction, distributeTransactionFee, supportsTransactionFees, supportsMonetaryPolicy, validateBlockResourceLimits, calculateMonetaryPolicy, distributeAmount, aggregateValidatorFees, MONETARY_POLICY_AUTHORITY, contractFactoryMessage, nativeTokenMessage, validatorsMessage, nameServiceMessage } from '@leofcoin/lib';
7
7
  import MultiWallet from '@leofcoin/multi-wallet';
8
8
  import { fromBase58 } from '@vandeurenglenn/typed-array-utils';
9
9
  import semver from 'semver';
10
10
  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
- import { P as PROTOCOL_VERSION, R as REACHED_ONE_ZERO_ZERO } from './constants-D6gWzJZg.js';
13
+ import { P as PROTOCOL_VERSION, R as REACHED_ONE_ZERO_ZERO } from './constants-CMYKv-Rt.js';
14
+ import { q as quorumThreshold } from './quorum-S_qdgiAY.js';
14
15
  import '@leofcoin/networks';
15
16
 
16
17
  const limit = 1800;
@@ -78,19 +79,14 @@ class Transaction extends Protocol {
78
79
  return new Promise(async (resolve, reject) => {
79
80
  let size = 0;
80
81
  const _transactions = [];
81
- const MAX_BLOCK_TX_BYTES = 786432;
82
- await Promise.all(
83
- transactions.map(async (tx) => {
84
- tx = await new TransactionMessage(tx);
85
- const newSize = size + tx.encoded.length;
86
- if (newSize <= MAX_BLOCK_TX_BYTES) {
87
- size = newSize;
88
- _transactions.push({ ...tx.decoded, hash: await tx.hash() });
89
- } else {
90
- resolve(_transactions);
91
- }
92
- })
93
- );
82
+ for (const rawTransaction of transactions) {
83
+ const tx = await new TransactionMessage(rawTransaction);
84
+ if (tx.encoded.length > MAX_TRANSACTION_BYTES) continue;
85
+ const newSize = size + tx.encoded.length;
86
+ if (newSize > MAX_BLOCK_TRANSACTION_BYTES || _transactions.length >= MAX_BLOCK_TRANSACTIONS) break;
87
+ size = newSize;
88
+ _transactions.push({ ...tx.decoded, hash: await tx.hash() });
89
+ }
94
90
  return resolve(_transactions);
95
91
  });
96
92
  }
@@ -896,7 +892,7 @@ class Machine {
896
892
  * @param {Array} parameters
897
893
  * @returns Promise<message>
898
894
  */
899
- async execute(contract, method, parameters, sender) {
895
+ async execute(contract, method, parameters, sender, execution) {
900
896
  try {
901
897
  if (contract === contractFactory && method === "registerContract") {
902
898
  if (await this.has(parameters[0])) throw new Error(`duplicate contract @${parameters[0]}`);
@@ -938,7 +934,10 @@ ${error.message}`);
938
934
  contract,
939
935
  method,
940
936
  params: parameters,
941
- sender
937
+ sender,
938
+ executionSeed: execution?.seed,
939
+ executionTimestamp: execution?.timestamp,
940
+ executionRandomness: execution?.randomness
942
941
  }
943
942
  });
944
943
  });
@@ -946,6 +945,9 @@ ${error.message}`);
946
945
  collectFee(from, payments, burned) {
947
946
  return this.#askWorker("collectFee", { from, payments, burned });
948
947
  }
948
+ settleRewards(rewards) {
949
+ return this.#askWorker("settleRewards", { rewards });
950
+ }
949
951
  get(contract, method, parameters) {
950
952
  return new Promise((resolve, reject) => {
951
953
  const id = randombytes(20).toString();
@@ -1447,7 +1449,8 @@ class State extends Contract {
1447
1449
  if (!(block instanceof Uint8Array)) block = new Uint8Array(Object.values(block));
1448
1450
  block = await new BlockMessage(block);
1449
1451
  const resolvedHash = await block.hash();
1450
- if (resolvedHash !== hash) throw new ResolveError(`block hash mismatch: requested ${hash}, received ${resolvedHash}`);
1452
+ if (resolvedHash !== hash)
1453
+ throw new ResolveError(`block hash mismatch: requested ${hash}, received ${resolvedHash}`);
1451
1454
  const { index } = block.decoded;
1452
1455
  if (this.#blocks[index] && this.#blocks[index].hash !== resolvedHash) {
1453
1456
  throw new ResolveError(`conflicting block ${resolvedHash} @${index}`);
@@ -1789,19 +1792,20 @@ class State extends Contract {
1789
1792
  #loadBlockTransactions;
1790
1793
  #getLastTransactions;
1791
1794
  // todo throw error
1792
- async #_executeTransaction(transaction, validators, feesEnabled) {
1795
+ async #_executeTransaction(transaction, validators, feesEnabled, burnBasisPoints, blockTimestamp) {
1793
1796
  try {
1794
1797
  const hash = await transaction.hash();
1795
1798
  if (feesEnabled) {
1796
1799
  const fee = BigInt(await calculateFee(transaction.decoded));
1797
- const { payments, burned } = distributeTransactionFee(fee, hash, validators);
1800
+ const { payments, burned } = distributeTransactionFee(fee, hash, validators, burnBasisPoints);
1798
1801
  await this.#machine.collectFee(transaction.decoded.from, payments, burned);
1799
1802
  }
1800
1803
  await this.#machine.execute(
1801
1804
  transaction.decoded.to,
1802
1805
  transaction.decoded.method,
1803
1806
  transaction.decoded.params,
1804
- transaction.decoded.from
1807
+ transaction.decoded.from,
1808
+ { seed: hash, timestamp: Number(blockTimestamp) }
1805
1809
  );
1806
1810
  } catch (error) {
1807
1811
  console.log(error);
@@ -1845,7 +1849,26 @@ class State extends Contract {
1845
1849
  });
1846
1850
  debug$1(`executing ${transactions.length} transactions for block ${block.index}`);
1847
1851
  const feesEnabled = supportsTransactionFees(block.protocolVersion);
1848
- for (const transaction of transactions) await this.#_executeTransaction(transaction, validators, feesEnabled);
1852
+ const monetaryPolicyEnabled = supportsMonetaryPolicy(block.protocolVersion);
1853
+ if (monetaryPolicyEnabled) await validateBlockResourceLimits(transactions);
1854
+ const policy = monetaryPolicyEnabled ? calculateMonetaryPolicy(
1855
+ BigInt(await this.#machine.get(nativeToken, "totalSupply")),
1856
+ BigInt(await this.#machine.get(nativeToken, "targetSupply"))
1857
+ ) : { subsidy: 0n, burnBasisPoints: 1000n };
1858
+ for (const transaction of transactions) {
1859
+ await this.#_executeTransaction(
1860
+ transaction,
1861
+ validators,
1862
+ feesEnabled,
1863
+ policy.burnBasisPoints,
1864
+ block.timestamp
1865
+ );
1866
+ }
1867
+ if (monetaryPolicyEnabled && policy.subsidy > 0n) {
1868
+ await this.#machine.settleRewards([
1869
+ ...distributeAmount(policy.subsidy, validators, Number(block.index) % validators.length)
1870
+ ]);
1871
+ }
1849
1872
  this.#blocks[block.index].loaded = true;
1850
1873
  debug$1(`executed transactions for block ${block.index}`);
1851
1874
  if (Number(block.index) === 0) this.#loaded = true;
@@ -2261,13 +2284,6 @@ class ConnectionMonitor {
2261
2284
  }
2262
2285
  }
2263
2286
 
2264
- const quorumThreshold = (validatorCount) => {
2265
- if (!Number.isSafeInteger(validatorCount) || validatorCount < 1) {
2266
- throw new Error("validator count must be a positive safe integer");
2267
- }
2268
- return Math.floor(2 * validatorCount / 3) + 1;
2269
- };
2270
-
2271
2287
  const validateChainLink = (localTip, incoming) => {
2272
2288
  const localHash = localTip?.hash || "0x0";
2273
2289
  const localIndex = Number(localTip?.index ?? -1);
@@ -2289,7 +2305,6 @@ const validateChainLink = (localTip, incoming) => {
2289
2305
  return "append";
2290
2306
  };
2291
2307
 
2292
- const BLOCK_REWARD = 150n;
2293
2308
  const validateCanonicalValidatorSet = (blockIndex, expectedValidators, validators) => {
2294
2309
  const actualValidators = validators.map(({ address }) => address);
2295
2310
  const canonicalExpected = [...new Set(expectedValidators)].sort();
@@ -2299,20 +2314,18 @@ const validateCanonicalValidatorSet = (blockIndex, expectedValidators, validator
2299
2314
  );
2300
2315
  }
2301
2316
  };
2302
- const validateBlockEconomics = (block, calculatedFees, validatorFees = /* @__PURE__ */ new Map()) => {
2317
+ const validateBlockEconomics = (block, calculatedFees, validatorFees = /* @__PURE__ */ new Map(), subsidyRewards = /* @__PURE__ */ new Map(), expectedSubsidy = [...subsidyRewards.values()].reduce((sum, reward) => sum + reward, 0n)) => {
2303
2318
  if (block.validators.length === 0) {
2304
2319
  throw new Error(`Block ${block.index} cannot distribute rewards without validators`);
2305
2320
  }
2306
- if (block.reward !== BLOCK_REWARD) {
2307
- throw new Error(`Block ${block.index} has invalid base reward: expected ${BLOCK_REWARD}, got ${block.reward}`);
2321
+ if (block.reward !== expectedSubsidy) {
2322
+ throw new Error(`Block ${block.index} has invalid base reward: expected ${expectedSubsidy}, got ${block.reward}`);
2308
2323
  }
2309
2324
  if (block.fees !== calculatedFees) {
2310
2325
  throw new Error(`Block ${block.index} has invalid fees: expected ${calculatedFees}, got ${block.fees}`);
2311
2326
  }
2312
- const validatorCount = BigInt(block.validators.length);
2313
- const baseReward = BLOCK_REWARD / validatorCount;
2314
2327
  for (const validator of block.validators) {
2315
- const expectedReward = baseReward + (validatorFees.get(validator.address) || 0n);
2328
+ const expectedReward = (subsidyRewards.get(validator.address) || 0n) + (validatorFees.get(validator.address) || 0n);
2316
2329
  if (validator.reward !== expectedReward) {
2317
2330
  throw new Error(
2318
2331
  `Block ${block.index} has an invalid reward for validator ${validator.address}: expected ${expectedReward}, got ${validator.reward}`
@@ -2369,6 +2382,14 @@ const proposalDelay = ({
2369
2382
  return Math.max(0, Math.max(nextFromCanonicalBlock, nextFromLocalAttempt) - now);
2370
2383
  };
2371
2384
 
2385
+ const enqueueTransaction = async (transaction, ops) => {
2386
+ const hash = await transaction.hash();
2387
+ if (!await ops.hasInPool(hash) && !await ops.hasInStore(hash)) {
2388
+ await ops.putToPool(hash, transaction.encoded);
2389
+ }
2390
+ ops.addPendingNonce(transaction.decoded.from, transaction.decoded.nonce);
2391
+ return hash;
2392
+ };
2372
2393
  const compareTransactionNonces = (left, right) => {
2373
2394
  const leftNonce = BigInt(left ?? 0);
2374
2395
  const rightNonce = BigInt(right ?? 0);
@@ -2619,9 +2640,7 @@ class Chain extends VersionControl {
2619
2640
  };
2620
2641
  // ─────────────────────────────────────────────────────────────────────────
2621
2642
  this.#addTransaction = async (message) => {
2622
- const transaction = new TransactionMessage(message);
2623
- const hash = await transaction.hash();
2624
- this.addPendingNonce(transaction.decoded.from, transaction.decoded.nonce);
2643
+ const hash = await this.#sendTransaction(message);
2625
2644
  debug(`added ${hash}`);
2626
2645
  };
2627
2646
  this.#init();
@@ -2696,12 +2715,7 @@ class Chain extends VersionControl {
2696
2715
  return signConsensusMessage(addresses.validators, type, message, identity);
2697
2716
  }
2698
2717
  async #verifyConsensusMessage(type, message) {
2699
- return verifyConsensusMessage(
2700
- addresses.validators,
2701
- type,
2702
- message,
2703
- globalThis.peernet?.network || "leofcoin"
2704
- );
2718
+ return verifyConsensusMessage(addresses.validators, type, message, globalThis.peernet?.network || "leofcoin");
2705
2719
  }
2706
2720
  async #getConsensusValidators(nextBlockIndex2) {
2707
2721
  const localBlock = await this.lastBlock;
@@ -2781,8 +2795,14 @@ class Chain extends VersionControl {
2781
2795
  })
2782
2796
  );
2783
2797
  const feesEnabled = supportsTransactionFees(blockMessage.decoded.protocolVersion);
2784
- if (feesEnabled) await this.#assertFeeBurnSupported();
2785
- const calculatedFees = feesEnabled ? (await Promise.all(transactions.map((transaction) => calculateFee(transaction.decoded)))).reduce((total, fee) => total + BigInt(fee), 0n) : 0n;
2798
+ if (feesEnabled) await this.#assertFeeBurnSupported(blockMessage.decoded.protocolVersion);
2799
+ const adaptivePolicy = supportsMonetaryPolicy(blockMessage.decoded.protocolVersion);
2800
+ if (adaptivePolicy) await validateBlockResourceLimits(transactions);
2801
+ const policy = await this.#monetaryPolicy(blockMessage.decoded.protocolVersion);
2802
+ const calculatedFees = feesEnabled ? (await Promise.all(transactions.map((transaction) => calculateFee(transaction.decoded)))).reduce(
2803
+ (total, fee) => total + BigInt(fee),
2804
+ 0n
2805
+ ) : 0n;
2786
2806
  const feeEntries = feesEnabled ? await Promise.all(
2787
2807
  transactions.map(async (transaction) => ({
2788
2808
  fee: BigInt(await calculateFee(transaction.decoded)),
@@ -2790,8 +2810,13 @@ class Chain extends VersionControl {
2790
2810
  }))
2791
2811
  ) : [];
2792
2812
  const validatorAddresses = blockMessage.decoded.validators.map(({ address }) => address);
2793
- const validatorFees = feesEnabled ? aggregateValidatorFees(feeEntries, validatorAddresses) : new Map(validatorAddresses.map((address) => [address, 0n]));
2794
- validateBlockEconomics(blockMessage.decoded, calculatedFees, validatorFees);
2813
+ const validatorFees = feesEnabled ? aggregateValidatorFees(feeEntries, validatorAddresses, policy.burnBasisPoints) : new Map(validatorAddresses.map((address) => [address, 0n]));
2814
+ const subsidyRewards = adaptivePolicy ? distributeAmount(
2815
+ policy.subsidy,
2816
+ validatorAddresses,
2817
+ Number(blockMessage.decoded.index) % validatorAddresses.length
2818
+ ) : new Map(validatorAddresses.map((address) => [address, policy.subsidy / BigInt(validatorAddresses.length)]));
2819
+ validateBlockEconomics(blockMessage.decoded, calculatedFees, validatorFees, subsidyRewards, policy.subsidy);
2795
2820
  if (feesEnabled) {
2796
2821
  const feesBySender = /* @__PURE__ */ new Map();
2797
2822
  for (let index = 0; index < transactions.length; index += 1) {
@@ -2802,20 +2827,32 @@ class Chain extends VersionControl {
2802
2827
  [...feesBySender].map(async ([sender, required]) => {
2803
2828
  const balance = BigInt(await this.balanceOf(sender) || 0n);
2804
2829
  if (balance < required) {
2805
- throw new Error(`insufficient balance for transaction fees from ${sender}: need ${required}, got ${balance}`);
2830
+ throw new Error(
2831
+ `insufficient balance for transaction fees from ${sender}: need ${required}, got ${balance}`
2832
+ );
2806
2833
  }
2807
2834
  })
2808
2835
  );
2809
2836
  }
2810
2837
  return transactions;
2811
2838
  }
2812
- async #assertFeeBurnSupported() {
2813
- const creator = await this.staticCall(addresses.nativeToken, "creator");
2814
- const canBurn = await this.staticCall(addresses.nativeToken, "hasRole", [creator, "BURN"]);
2815
- if (!canBurn) {
2816
- throw new Error("native token genesis does not support protocol fee burning");
2839
+ async #assertFeeBurnSupported(protocolVersion) {
2840
+ const [canBurn, canMint] = await Promise.all([
2841
+ this.staticCall(addresses.nativeToken, "hasRole", [MONETARY_POLICY_AUTHORITY, "BURN"]),
2842
+ this.staticCall(addresses.nativeToken, "hasRole", [MONETARY_POLICY_AUTHORITY, "MINT"])
2843
+ ]);
2844
+ if (!canBurn || supportsMonetaryPolicy(protocolVersion) && !canMint) {
2845
+ throw new Error("native token genesis does not support protocol monetary policy");
2817
2846
  }
2818
2847
  }
2848
+ async #monetaryPolicy(protocolVersion) {
2849
+ if (!supportsMonetaryPolicy(protocolVersion)) return { subsidy: 150n, burnBasisPoints: 1000n, floorSupply: 0n };
2850
+ const [totalSupply, targetSupply] = await Promise.all([
2851
+ this.staticCall(addresses.nativeToken, "totalSupply"),
2852
+ this.staticCall(addresses.nativeToken, "targetSupply")
2853
+ ]);
2854
+ return calculateMonetaryPolicy(BigInt(totalSupply), BigInt(targetSupply));
2855
+ }
2819
2856
  /** Check if the next block will cross an epoch boundary (block-based timing) */
2820
2857
  #isEpochBoundary(blockHeight) {
2821
2858
  return (blockHeight + 1) % this.#epochLength === 0;
@@ -3264,12 +3301,24 @@ class Chain extends VersionControl {
3264
3301
  async #versionHandler() {
3265
3302
  return new globalThis.peernet.protos["peernet-response"]({ response: this.version });
3266
3303
  }
3267
- async #executeTransaction({ hash, from, to, method, params, nonce, feePayments = { payments: [], burned: 0n } }) {
3304
+ async #executeTransaction({
3305
+ hash,
3306
+ from,
3307
+ to,
3308
+ method,
3309
+ params,
3310
+ nonce,
3311
+ executionTimestamp,
3312
+ feePayments = { payments: [], burned: 0n }
3313
+ }) {
3268
3314
  try {
3269
3315
  if (feePayments.payments.length > 0 || feePayments.burned > 0n) {
3270
3316
  await this.machine.collectFee(from, feePayments.payments, feePayments.burned);
3271
3317
  }
3272
- let result = await this.machine.execute(to, method, params, from);
3318
+ let result = await this.machine.execute(to, method, params, from, {
3319
+ seed: hash,
3320
+ timestamp: Number(executionTimestamp)
3321
+ });
3273
3322
  globalThis.pubsub.publish(`transaction.completed.${hash}`, { status: "fulfilled", hash });
3274
3323
  return result || "no state change";
3275
3324
  } catch (error) {
@@ -3338,13 +3387,25 @@ class Chain extends VersionControl {
3338
3387
  if (nonceDiff !== 0) return nonceDiff;
3339
3388
  return 0;
3340
3389
  });
3390
+ const monetaryPolicy = await this.#monetaryPolicy(blockMessage.decoded.protocolVersion);
3341
3391
  for (const transaction of allTransactions) {
3342
3392
  if (!contracts.includes(transaction.decoded.to)) contracts.push(transaction.decoded.to);
3343
3393
  this.removePendingNonce(transaction.decoded.from, transaction.decoded.nonce);
3344
3394
  const transactionHash = await transaction.hash();
3345
3395
  const validatorAddresses = blockMessage.decoded.validators.map(({ address }) => address);
3346
- const feeDistribution = supportsTransactionFees(blockMessage.decoded.protocolVersion) ? distributeTransactionFee(BigInt(await calculateFee(transaction.decoded)), transactionHash, validatorAddresses) : { payments: [], burned: 0n };
3347
- await this.#handleTransaction(transaction, [], void 0, feeDistribution);
3396
+ const feeDistribution = supportsTransactionFees(blockMessage.decoded.protocolVersion) ? distributeTransactionFee(
3397
+ BigInt(await calculateFee(transaction.decoded)),
3398
+ transactionHash,
3399
+ validatorAddresses,
3400
+ monetaryPolicy.burnBasisPoints
3401
+ ) : { payments: [], burned: 0n };
3402
+ await this.#handleTransaction(transaction, [], blockMessage.decoded, feeDistribution);
3403
+ }
3404
+ if (supportsMonetaryPolicy(blockMessage.decoded.protocolVersion) && monetaryPolicy.subsidy > 0n) {
3405
+ const validators = blockMessage.decoded.validators.map(({ address }) => address);
3406
+ await this.machine.settleRewards([
3407
+ ...distributeAmount(monetaryPolicy.subsidy, validators, blockIndex % validators.length)
3408
+ ]);
3348
3409
  }
3349
3410
  try {
3350
3411
  promises = await Promise.allSettled(promises);
@@ -3414,7 +3475,12 @@ class Chain extends VersionControl {
3414
3475
  return;
3415
3476
  }
3416
3477
  try {
3417
- const result = await this.#executeTransaction({ ...transaction.decoded, hash, feePayments });
3478
+ const result = await this.#executeTransaction({
3479
+ ...transaction.decoded,
3480
+ hash,
3481
+ executionTimestamp: block?.timestamp ?? transaction.decoded.timestamp,
3482
+ feePayments
3483
+ });
3418
3484
  if (block) {
3419
3485
  block.transactions.push(hash);
3420
3486
  block.fees = block.fees += await calculateFee(transaction.decoded);
@@ -3445,7 +3511,7 @@ class Chain extends VersionControl {
3445
3511
  fees: BigInt(0),
3446
3512
  timestamp,
3447
3513
  previousHash: "",
3448
- reward: BLOCK_REWARD,
3514
+ reward: 0n,
3449
3515
  index: 0,
3450
3516
  producer: "",
3451
3517
  producerProof: "",
@@ -3467,9 +3533,18 @@ class Chain extends VersionControl {
3467
3533
  if (nonceDiff !== 0) return nonceDiff;
3468
3534
  return 0;
3469
3535
  });
3536
+ let blockTransactionBytes = 0;
3470
3537
  for (const { transaction, hash } of allTransactions) {
3538
+ const transactionBytes = transaction.encoded.length;
3539
+ if (transactionBytes > MAX_TRANSACTION_BYTES) {
3540
+ await globalThis.transactionPoolStore.delete(hash);
3541
+ continue;
3542
+ }
3543
+ if (block.transactions.length >= MAX_BLOCK_TRANSACTIONS || blockTransactionBytes + transactionBytes > MAX_BLOCK_TRANSACTION_BYTES)
3544
+ break;
3471
3545
  await this.validateTransactionSignature(transaction);
3472
3546
  block.transactions.push(hash);
3547
+ blockTransactionBytes += transactionBytes;
3473
3548
  if (supportsTransactionFees(this.version)) block.fees += BigInt(await calculateFee(transaction.decoded));
3474
3549
  await globalThis.peernet.put(hash, transaction.encoded, "transaction");
3475
3550
  }
@@ -3480,17 +3555,20 @@ class Chain extends VersionControl {
3480
3555
  const canonicalValidators = await this.staticCall(addresses.validators, "validators");
3481
3556
  const sortedValidators = [...canonicalValidators].sort();
3482
3557
  if (sortedValidators.length === 0) throw new Error("cannot produce a block without validators");
3483
- if (supportsTransactionFees(this.version)) await this.#assertFeeBurnSupported();
3558
+ if (supportsTransactionFees(this.version)) await this.#assertFeeBurnSupported(this.version);
3559
+ const monetaryPolicy = await this.#monetaryPolicy(this.version);
3560
+ block.reward = monetaryPolicy.subsidy;
3484
3561
  const feeEntries = supportsTransactionFees(this.version) ? await Promise.all(
3485
3562
  allTransactions.map(async ({ transaction, hash }) => ({
3486
3563
  fee: BigInt(await calculateFee(transaction.decoded)),
3487
3564
  transactionHash: hash
3488
3565
  }))
3489
3566
  ) : [];
3490
- const validatorFees = supportsTransactionFees(this.version) ? aggregateValidatorFees(feeEntries, sortedValidators) : new Map(sortedValidators.map((address) => [address, 0n]));
3567
+ const validatorFees = supportsTransactionFees(this.version) ? aggregateValidatorFees(feeEntries, sortedValidators, monetaryPolicy.burnBasisPoints) : new Map(sortedValidators.map((address) => [address, 0n]));
3568
+ const subsidyRewards = supportsMonetaryPolicy(this.version) ? distributeAmount(monetaryPolicy.subsidy, sortedValidators, Number(block.index) % sortedValidators.length) : new Map(sortedValidators.map((address) => [address, monetaryPolicy.subsidy / BigInt(sortedValidators.length)]));
3491
3569
  block.validators = sortedValidators.map((validatorAddress) => ({
3492
3570
  address: validatorAddress,
3493
- reward: (validatorFees.get(validatorAddress) || 0n) + block.reward / BigInt(sortedValidators.length)
3571
+ reward: (validatorFees.get(validatorAddress) || 0n) + (subsidyRewards.get(validatorAddress) || 0n)
3494
3572
  }));
3495
3573
  try {
3496
3574
  block.producer = globalThis.peernet.selectedAccount || "";
@@ -3553,18 +3631,23 @@ class Chain extends VersionControl {
3553
3631
  async #sendTransaction(transaction) {
3554
3632
  transaction = await new TransactionMessage(transaction.encoded || transaction);
3555
3633
  await this.validateTransactionSignature(transaction);
3556
- const hash = await transaction.hash();
3634
+ let hash;
3557
3635
  try {
3558
- const has = await globalThis.transactionPoolStore.has(hash);
3559
- if (!has && !await transactionStore.has(hash)) {
3560
- await globalThis.transactionPoolStore.put(hash, transaction.encoded);
3561
- }
3636
+ hash = await enqueueTransaction(transaction, {
3637
+ putToPool: (txHash, data) => globalThis.transactionPoolStore.put(txHash, data),
3638
+ hasInPool: (txHash) => globalThis.transactionPoolStore.has(txHash),
3639
+ hasInStore: (txHash) => globalThis.transactionStore.has(txHash),
3640
+ addPendingNonce: (address, nonce) => this.addPendingNonce(address, nonce)
3641
+ });
3562
3642
  if (this.#participating && !this.#runningEpoch) this.#runEpoch();
3643
+ return hash;
3563
3644
  } catch (e) {
3564
- try {
3565
- globalThis.peernet.publish("invalid-transaction", hash);
3566
- } catch (publishError) {
3567
- debug("peernet publish failed: invalid-transaction", publishError?.message ?? publishError);
3645
+ if (hash) {
3646
+ try {
3647
+ globalThis.peernet.publish("invalid-transaction", hash);
3648
+ } catch (publishError) {
3649
+ debug("peernet publish failed: invalid-transaction", publishError?.message ?? publishError);
3650
+ }
3568
3651
  }
3569
3652
  throw new Error("invalid transaction");
3570
3653
  }
@@ -3577,7 +3660,7 @@ class Chain extends VersionControl {
3577
3660
  async sendTransaction(transaction) {
3578
3661
  const transactionMessage = await new TransactionMessage({ ...transaction });
3579
3662
  const event = await super.sendTransaction(transactionMessage);
3580
- this.#sendTransaction(transactionMessage.encoded);
3663
+ await this.#sendTransaction(transactionMessage.encoded);
3581
3664
  try {
3582
3665
  globalThis.peernet.publish("send-transaction", transactionMessage.encoded);
3583
3666
  } catch (publishError) {
@@ -1,6 +1,6 @@
1
1
  import networks from '@leofcoin/networks';
2
2
 
3
- const PROTOCOL_VERSION = "0.2.0";
3
+ const PROTOCOL_VERSION = "0.3.0";
4
4
  const REACHED_ONE_ZERO_ZERO = false;
5
5
  const DEFAULT_NODE_OPTIONS = {
6
6
  autoStart: false,
package/exports/node.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import Peernet from '@leofcoin/peernet';
2
2
  import nodeConfig from '@leofcoin/lib/node-config';
3
- import { D as DEFAULT_NODE_OPTIONS } from './constants-D6gWzJZg.js';
3
+ import { D as DEFAULT_NODE_OPTIONS } from './constants-CMYKv-Rt.js';
4
4
  import '@leofcoin/networks';
5
5
 
6
6
  class Node {
@@ -0,0 +1,8 @@
1
+ const quorumThreshold = (validatorCount) => {
2
+ if (!Number.isSafeInteger(validatorCount) || validatorCount < 1) {
3
+ throw new Error("validator count must be a positive safe integer");
4
+ }
5
+ return Math.floor(2 * validatorCount / 3) + 1;
6
+ };
7
+
8
+ export { quorumThreshold as q };
@@ -1,4 +1,4 @@
1
- import { E as EasyWorker, F as FormatInterface, p as proto$3, i as index$3, a as index$2, b as proto$2 } from './worker-CZqErLI7-BxofVJAn.js';
1
+ import { E as EasyWorker, F as FormatInterface, p as proto$3, i as index$3, a as index$2, b as proto$2 } from './worker-DMCj1e6z-CTYVa2yX.js';
2
2
 
3
3
  class ValidatorMessage extends FormatInterface {
4
4
  get messageName() {