@leofcoin/chain 1.10.8 → 1.10.10

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.
@@ -1056,6 +1056,111 @@ const contractFactoryMessage = bytecodes.contractFactory;
1056
1056
  const nativeTokenMessage = bytecodes.nativeToken;
1057
1057
  const nameServiceMessage = bytecodes.nameService;
1058
1058
  const validatorsMessage = bytecodes.validators;
1059
+ const TRANSACTION_FEE_BYTES = 1024n;
1060
+ const TRANSACTION_FEE_UNIT = 10n;
1061
+ const MAX_TRANSACTION_BYTES = 32 * 1024;
1062
+ const MAX_BLOCK_TRANSACTION_BYTES = 128 * 1024;
1063
+ const MAX_BLOCK_TRANSACTIONS = 256;
1064
+ BigInt(MAX_TRANSACTION_BYTES) / TRANSACTION_FEE_BYTES * TRANSACTION_FEE_UNIT;
1065
+ const FEE_BASIS_POINTS = 10000n;
1066
+ const FEE_PROTOCOL_VERSION = "1.10.9";
1067
+ const MONETARY_POLICY_PROTOCOL_VERSION = "1.10.10";
1068
+ const BLOCKS_PER_YEAR = 5256000n;
1069
+ const ANNUAL_ISSUANCE_BASIS_POINTS = 200n;
1070
+ const SUPPLY_FLOOR_BASIS_POINTS = 9500n;
1071
+ const MONETARY_FEE_BURN_BASIS_POINTS = 1000n;
1072
+ const parseProtocolVersion = (version) => {
1073
+ const match = /^(\d+)\.(\d+)\.(\d+)/.exec(version);
1074
+ return match ? [Number(match[1]), Number(match[2]), Number(match[3])] : void 0;
1075
+ };
1076
+ const supportsTransactionFees = (version) => {
1077
+ const actual = parseProtocolVersion(version);
1078
+ const required = parseProtocolVersion(FEE_PROTOCOL_VERSION);
1079
+ if (!actual)
1080
+ return false;
1081
+ for (let index = 0; index < required.length; index += 1) {
1082
+ if (actual[index] !== required[index])
1083
+ return actual[index] > required[index];
1084
+ }
1085
+ return true;
1086
+ };
1087
+ const supportsMonetaryPolicy = (version) => {
1088
+ const actual = parseProtocolVersion(version);
1089
+ const required = parseProtocolVersion(MONETARY_POLICY_PROTOCOL_VERSION);
1090
+ if (!actual)
1091
+ return false;
1092
+ for (let index = 0; index < required.length; index += 1) {
1093
+ if (actual[index] !== required[index])
1094
+ return actual[index] > required[index];
1095
+ }
1096
+ return true;
1097
+ };
1098
+ const calculateMonetaryPolicy = (totalSupply, targetSupply) => {
1099
+ if (totalSupply < 0n || targetSupply <= 0n)
1100
+ throw new Error("invalid monetary policy supply");
1101
+ const floorSupply = targetSupply * SUPPLY_FLOOR_BASIS_POINTS / FEE_BASIS_POINTS;
1102
+ if (totalSupply < floorSupply) {
1103
+ const annualIssuance = targetSupply * ANNUAL_ISSUANCE_BASIS_POINTS / FEE_BASIS_POINTS;
1104
+ const scheduled = annualIssuance / BLOCKS_PER_YEAR || 1n;
1105
+ return { subsidy: scheduled < floorSupply - totalSupply ? scheduled : floorSupply - totalSupply, burnBasisPoints: 0n, floorSupply };
1106
+ }
1107
+ return {
1108
+ subsidy: 0n,
1109
+ burnBasisPoints: totalSupply >= targetSupply ? MONETARY_FEE_BURN_BASIS_POINTS : 0n,
1110
+ floorSupply
1111
+ };
1112
+ };
1113
+ const distributeAmount = (amount, addresses, rotation = 0) => {
1114
+ const canonical = [...new Set(addresses)].sort();
1115
+ if (canonical.length === 0)
1116
+ throw new Error("cannot distribute without validators");
1117
+ const count = BigInt(canonical.length);
1118
+ const base = amount / count;
1119
+ const remainder = Number(amount % count);
1120
+ const result = new Map(canonical.map((address) => [address, base]));
1121
+ for (let index = 0; index < remainder; index += 1) {
1122
+ const address = canonical[(rotation + index) % canonical.length];
1123
+ result.set(address, result.get(address) + 1n);
1124
+ }
1125
+ return result;
1126
+ };
1127
+ const feeRotationIndex = (transactionHash, validatorCount) => {
1128
+ let value = 0n;
1129
+ for (const character of transactionHash)
1130
+ value = (value * 31n + BigInt(character.charCodeAt(0))) % 4294967291n;
1131
+ return Number(value % BigInt(validatorCount));
1132
+ };
1133
+ const distributeTransactionFee = (fee, transactionHash, validatorAddresses, burnBasisPoints = 1000n) => {
1134
+ const canonicalValidators = [...new Set(validatorAddresses)].sort();
1135
+ if (canonicalValidators.length === 0)
1136
+ throw new Error("cannot distribute transaction fee without validators");
1137
+ if (fee < 0n)
1138
+ throw new Error("transaction fee cannot be negative");
1139
+ if (burnBasisPoints < 0n || burnBasisPoints > FEE_BASIS_POINTS)
1140
+ throw new Error("invalid fee burn rate");
1141
+ const burned = fee * burnBasisPoints / FEE_BASIS_POINTS;
1142
+ const validatorPool = fee - burned;
1143
+ const count = BigInt(canonicalValidators.length);
1144
+ const base = validatorPool / count;
1145
+ const remainder = Number(validatorPool % count);
1146
+ const start = feeRotationIndex(transactionHash, canonicalValidators.length);
1147
+ const validatorFees = new Map(canonicalValidators.map((validator) => [validator, base]));
1148
+ for (let index = 0; index < remainder; index += 1) {
1149
+ const validator = canonicalValidators[(start + index) % canonicalValidators.length];
1150
+ validatorFees.set(validator, validatorFees.get(validator) + 1n);
1151
+ }
1152
+ const payments = [...validatorFees.entries()].filter(([, amount]) => amount > 0n).map(([to, amount]) => ({ to, amount }));
1153
+ return { burned, validatorFees, payments };
1154
+ };
1155
+ const aggregateValidatorFees = (fees, validatorAddresses, burnBasisPoints = 1000n) => {
1156
+ const totals = new Map([...new Set(validatorAddresses)].sort().map((validator) => [validator, 0n]));
1157
+ for (const entry of fees) {
1158
+ const { validatorFees } = distributeTransactionFee(entry.fee, entry.transactionHash, validatorAddresses, burnBasisPoints);
1159
+ for (const [validator, amount] of validatorFees)
1160
+ totals.set(validator, totals.get(validator) + amount);
1161
+ }
1162
+ return totals;
1163
+ };
1059
1164
  const createContractMessage = async (creator, contract, constructorParameters = []) => {
1060
1165
  return new ContractMessage({
1061
1166
  creator,
@@ -1064,13 +1169,32 @@ const createContractMessage = async (creator, contract, constructorParameters =
1064
1169
  });
1065
1170
  };
1066
1171
  const calculateFee = async (transaction, format = false) => {
1067
- if (transaction.to === validators$2)
1068
- return 0;
1069
1172
  transaction = await new TransactionMessage(transaction);
1070
- let fee = toBigInt(String(transaction.encoded.length));
1071
- fee /= 1073741824n;
1173
+ const encodedBytes = BigInt(transaction.encoded.length);
1174
+ const units = (encodedBytes + TRANSACTION_FEE_BYTES - 1n) / TRANSACTION_FEE_BYTES;
1175
+ const fee = units * TRANSACTION_FEE_UNIT;
1072
1176
  return format ? formatUnits(fee.toString()) : fee;
1073
1177
  };
1178
+ const validateTransactionResourceLimits = async (transaction) => {
1179
+ const message = await new TransactionMessage(transaction);
1180
+ const size = message.encoded.length;
1181
+ if (size > MAX_TRANSACTION_BYTES) {
1182
+ throw new Error(`transaction exceeds ${MAX_TRANSACTION_BYTES} byte protocol limit: ${size}`);
1183
+ }
1184
+ return size;
1185
+ };
1186
+ const validateBlockResourceLimits = async (transactions) => {
1187
+ if (transactions.length > MAX_BLOCK_TRANSACTIONS) {
1188
+ throw new Error(`block exceeds ${MAX_BLOCK_TRANSACTIONS} transaction protocol limit`);
1189
+ }
1190
+ let size = 0;
1191
+ for (const transaction of transactions)
1192
+ size += await validateTransactionResourceLimits(transaction);
1193
+ if (size > MAX_BLOCK_TRANSACTION_BYTES) {
1194
+ throw new Error(`block transactions exceed ${MAX_BLOCK_TRANSACTION_BYTES} byte protocol limit: ${size}`);
1195
+ }
1196
+ return size;
1197
+ };
1074
1198
  const createTransactionHash = async (transaction) => {
1075
1199
  const isRawTransactionMessage = transaction instanceof RawTransactionMessage;
1076
1200
  let message;
@@ -7845,19 +7969,14 @@ class Transaction extends Protocol {
7845
7969
  return new Promise(async (resolve, reject) => {
7846
7970
  let size = 0;
7847
7971
  const _transactions = [];
7848
- const MAX_BLOCK_TX_BYTES = 786432;
7849
- await Promise.all(
7850
- transactions.map(async (tx) => {
7851
- tx = await new TransactionMessage(tx);
7852
- const newSize = size + tx.encoded.length;
7853
- if (newSize <= MAX_BLOCK_TX_BYTES) {
7854
- size = newSize;
7855
- _transactions.push({ ...tx.decoded, hash: await tx.hash() });
7856
- } else {
7857
- resolve(_transactions);
7858
- }
7859
- })
7860
- );
7972
+ for (const rawTransaction of transactions) {
7973
+ const tx = await new TransactionMessage(rawTransaction);
7974
+ if (tx.encoded.length > MAX_TRANSACTION_BYTES) continue;
7975
+ const newSize = size + tx.encoded.length;
7976
+ if (newSize > MAX_BLOCK_TRANSACTION_BYTES || _transactions.length >= MAX_BLOCK_TRANSACTIONS) break;
7977
+ size = newSize;
7978
+ _transactions.push({ ...tx.decoded, hash: await tx.hash() });
7979
+ }
7861
7980
  return resolve(_transactions);
7862
7981
  });
7863
7982
  }
@@ -8801,7 +8920,7 @@ class Machine {
8801
8920
  * @param {Array} parameters
8802
8921
  * @returns Promise<message>
8803
8922
  */
8804
- async execute(contract, method, parameters) {
8923
+ async execute(contract, method, parameters, sender) {
8805
8924
  try {
8806
8925
  if (contract === contractFactory$2 && method === "registerContract") {
8807
8926
  if (await this.has(parameters[0])) throw new Error(`duplicate contract @${parameters[0]}`);
@@ -8842,11 +8961,18 @@ ${error.message}`);
8842
8961
  to: contract,
8843
8962
  contract,
8844
8963
  method,
8845
- params: parameters
8964
+ params: parameters,
8965
+ sender
8846
8966
  }
8847
8967
  });
8848
8968
  });
8849
8969
  }
8970
+ collectFee(from, payments, burned) {
8971
+ return this.#askWorker("collectFee", { from, payments, burned });
8972
+ }
8973
+ settleRewards(rewards) {
8974
+ return this.#askWorker("settleRewards", { rewards });
8975
+ }
8850
8976
  get(contract, method, parameters) {
8851
8977
  return new Promise((resolve, reject) => {
8852
8978
  const id = randombytes(20).toString();
@@ -9690,9 +9816,20 @@ class State extends Contract {
9690
9816
  #loadBlockTransactions;
9691
9817
  #getLastTransactions;
9692
9818
  // todo throw error
9693
- async #_executeTransaction(transaction) {
9819
+ async #_executeTransaction(transaction, validators, feesEnabled, burnBasisPoints) {
9694
9820
  try {
9695
- await this.#machine.execute(transaction.decoded.to, transaction.decoded.method, transaction.decoded.params);
9821
+ const hash = await transaction.hash();
9822
+ if (feesEnabled) {
9823
+ const fee = BigInt(await calculateFee(transaction.decoded));
9824
+ const { payments, burned } = distributeTransactionFee(fee, hash, validators, burnBasisPoints);
9825
+ await this.#machine.collectFee(transaction.decoded.from, payments, burned);
9826
+ }
9827
+ await this.#machine.execute(
9828
+ transaction.decoded.to,
9829
+ transaction.decoded.method,
9830
+ transaction.decoded.params,
9831
+ transaction.decoded.from
9832
+ );
9696
9833
  } catch (error) {
9697
9834
  console.log(error);
9698
9835
  await globalThis.transactionPoolStore.delete(await transaction.hash());
@@ -9721,23 +9858,34 @@ class State extends Contract {
9721
9858
  try {
9722
9859
  debug$1(`loading block: ${Number(block.index)} ${block.hash}`);
9723
9860
  let transactions = await this.#loadBlockTransactions(block.transactions || []);
9861
+ const validators = block.validators.map(({ address }) => address);
9724
9862
  debug$1(`loading transactions: ${transactions.length} for block ${block.index}`);
9725
- let priority = [];
9726
9863
  for (const transaction of transactions) {
9727
9864
  const hash = await transaction.hash();
9728
- if (transaction.decoded.priority) priority.push(transaction);
9729
9865
  if (poolTransactionKeys.has(hash)) await globalThis.transactionPoolStore.delete(hash);
9730
9866
  }
9731
- if (priority.length > 0) {
9732
- debug$1(`executing ${priority.length} priority transactions for block ${block.index}`);
9733
- priority = priority.sort((a, b) => a.nonce - b.nonce);
9734
- for (const transaction of priority) {
9735
- await this.#_executeTransaction(transaction);
9736
- }
9737
- }
9738
- transactions = transactions.filter((transaction) => !transaction.decoded.priority);
9867
+ transactions = transactions.sort((a, b) => {
9868
+ if (a.decoded.priority !== b.decoded.priority) return a.decoded.priority ? -1 : 1;
9869
+ const left = BigInt(a.decoded.nonce);
9870
+ const right = BigInt(b.decoded.nonce);
9871
+ return left < right ? -1 : left > right ? 1 : 0;
9872
+ });
9739
9873
  debug$1(`executing ${transactions.length} transactions for block ${block.index}`);
9740
- await Promise.all(transactions.map((transaction) => this.#_executeTransaction(transaction)));
9874
+ const feesEnabled = supportsTransactionFees(block.protocolVersion);
9875
+ const monetaryPolicyEnabled = supportsMonetaryPolicy(block.protocolVersion);
9876
+ if (monetaryPolicyEnabled) await validateBlockResourceLimits(transactions);
9877
+ const policy = monetaryPolicyEnabled ? calculateMonetaryPolicy(
9878
+ BigInt(await this.#machine.get(nativeToken$2, "totalSupply")),
9879
+ BigInt(await this.#machine.get(nativeToken$2, "targetSupply"))
9880
+ ) : { subsidy: 0n, burnBasisPoints: 1000n };
9881
+ for (const transaction of transactions) {
9882
+ await this.#_executeTransaction(transaction, validators, feesEnabled, policy.burnBasisPoints);
9883
+ }
9884
+ if (monetaryPolicyEnabled && policy.subsidy > 0n) {
9885
+ await this.#machine.settleRewards([
9886
+ ...distributeAmount(policy.subsidy, validators, Number(block.index) % validators.length)
9887
+ ]);
9888
+ }
9741
9889
  this.#blocks[block.index].loaded = true;
9742
9890
  debug$1(`executed transactions for block ${block.index}`);
9743
9891
  if (Number(block.index) === 0) this.#loaded = true;
@@ -10181,7 +10329,6 @@ const validateChainLink = (localTip, incoming) => {
10181
10329
  return "append";
10182
10330
  };
10183
10331
 
10184
- const BLOCK_REWARD = 150n;
10185
10332
  const validateCanonicalValidatorSet = (blockIndex, expectedValidators, validators) => {
10186
10333
  const actualValidators = validators.map(({ address }) => address);
10187
10334
  const canonicalExpected = [...new Set(expectedValidators)].sort();
@@ -10191,19 +10338,18 @@ const validateCanonicalValidatorSet = (blockIndex, expectedValidators, validator
10191
10338
  );
10192
10339
  }
10193
10340
  };
10194
- const validateBlockEconomics = (block, calculatedFees) => {
10341
+ const validateBlockEconomics = (block, calculatedFees, validatorFees = /* @__PURE__ */ new Map(), subsidyRewards = /* @__PURE__ */ new Map(), expectedSubsidy = [...subsidyRewards.values()].reduce((sum, reward) => sum + reward, 0n)) => {
10195
10342
  if (block.validators.length === 0) {
10196
10343
  throw new Error(`Block ${block.index} cannot distribute rewards without validators`);
10197
10344
  }
10198
- if (block.reward !== BLOCK_REWARD) {
10199
- throw new Error(`Block ${block.index} has invalid base reward: expected ${BLOCK_REWARD}, got ${block.reward}`);
10345
+ if (block.reward !== expectedSubsidy) {
10346
+ throw new Error(`Block ${block.index} has invalid base reward: expected ${expectedSubsidy}, got ${block.reward}`);
10200
10347
  }
10201
10348
  if (block.fees !== calculatedFees) {
10202
10349
  throw new Error(`Block ${block.index} has invalid fees: expected ${calculatedFees}, got ${block.fees}`);
10203
10350
  }
10204
- const validatorCount = BigInt(block.validators.length);
10205
- const expectedReward = calculatedFees / validatorCount + BLOCK_REWARD / validatorCount;
10206
10351
  for (const validator of block.validators) {
10352
+ const expectedReward = (subsidyRewards.get(validator.address) || 0n) + (validatorFees.get(validator.address) || 0n);
10207
10353
  if (validator.reward !== expectedReward) {
10208
10354
  throw new Error(
10209
10355
  `Block ${block.index} has an invalid reward for validator ${validator.address}: expected ${expectedReward}, got ${validator.reward}`
@@ -10671,10 +10817,57 @@ class Chain extends VersionControl {
10671
10817
  return transaction;
10672
10818
  })
10673
10819
  );
10674
- const calculatedFees = (await Promise.all(transactions.map((transaction) => calculateFee(transaction.decoded)))).reduce((total, fee) => total + BigInt(fee), 0n);
10675
- validateBlockEconomics(blockMessage.decoded, calculatedFees);
10820
+ const feesEnabled = supportsTransactionFees(blockMessage.decoded.protocolVersion);
10821
+ if (feesEnabled) await this.#assertFeeBurnSupported(blockMessage.decoded.protocolVersion);
10822
+ const adaptivePolicy = supportsMonetaryPolicy(blockMessage.decoded.protocolVersion);
10823
+ if (adaptivePolicy) await validateBlockResourceLimits(transactions);
10824
+ const policy = await this.#monetaryPolicy(blockMessage.decoded.protocolVersion);
10825
+ const calculatedFees = feesEnabled ? (await Promise.all(transactions.map((transaction) => calculateFee(transaction.decoded)))).reduce((total, fee) => total + BigInt(fee), 0n) : 0n;
10826
+ const feeEntries = feesEnabled ? await Promise.all(
10827
+ transactions.map(async (transaction) => ({
10828
+ fee: BigInt(await calculateFee(transaction.decoded)),
10829
+ transactionHash: await transaction.hash()
10830
+ }))
10831
+ ) : [];
10832
+ const validatorAddresses = blockMessage.decoded.validators.map(({ address }) => address);
10833
+ const validatorFees = feesEnabled ? aggregateValidatorFees(feeEntries, validatorAddresses, policy.burnBasisPoints) : new Map(validatorAddresses.map((address) => [address, 0n]));
10834
+ const subsidyRewards = adaptivePolicy ? distributeAmount(policy.subsidy, validatorAddresses, Number(blockMessage.decoded.index) % validatorAddresses.length) : new Map(validatorAddresses.map((address) => [address, policy.subsidy / BigInt(validatorAddresses.length)]));
10835
+ validateBlockEconomics(blockMessage.decoded, calculatedFees, validatorFees, subsidyRewards, policy.subsidy);
10836
+ if (feesEnabled) {
10837
+ const feesBySender = /* @__PURE__ */ new Map();
10838
+ for (let index = 0; index < transactions.length; index += 1) {
10839
+ const sender = transactions[index].decoded.from;
10840
+ feesBySender.set(sender, (feesBySender.get(sender) || 0n) + feeEntries[index].fee);
10841
+ }
10842
+ await Promise.all(
10843
+ [...feesBySender].map(async ([sender, required]) => {
10844
+ const balance = BigInt(await this.balanceOf(sender) || 0n);
10845
+ if (balance < required) {
10846
+ throw new Error(`insufficient balance for transaction fees from ${sender}: need ${required}, got ${balance}`);
10847
+ }
10848
+ })
10849
+ );
10850
+ }
10676
10851
  return transactions;
10677
10852
  }
10853
+ async #assertFeeBurnSupported(protocolVersion) {
10854
+ const creator = await this.staticCall(addresses.nativeToken, "creator");
10855
+ const [canBurn, canMint] = await Promise.all([
10856
+ this.staticCall(addresses.nativeToken, "hasRole", [creator, "BURN"]),
10857
+ this.staticCall(addresses.nativeToken, "hasRole", [creator, "MINT"])
10858
+ ]);
10859
+ if (!canBurn || supportsMonetaryPolicy(protocolVersion) && !canMint) {
10860
+ throw new Error("native token genesis does not support protocol monetary policy");
10861
+ }
10862
+ }
10863
+ async #monetaryPolicy(protocolVersion) {
10864
+ if (!supportsMonetaryPolicy(protocolVersion)) return { subsidy: 150n, burnBasisPoints: 1000n, floorSupply: 0n };
10865
+ const [totalSupply, targetSupply] = await Promise.all([
10866
+ this.staticCall(addresses.nativeToken, "totalSupply"),
10867
+ this.staticCall(addresses.nativeToken, "targetSupply")
10868
+ ]);
10869
+ return calculateMonetaryPolicy(BigInt(totalSupply), BigInt(targetSupply));
10870
+ }
10678
10871
  /** Check if the next block will cross an epoch boundary (block-based timing) */
10679
10872
  #isEpochBoundary(blockHeight) {
10680
10873
  return (blockHeight + 1) % this.#epochLength === 0;
@@ -11123,9 +11316,12 @@ class Chain extends VersionControl {
11123
11316
  async #versionHandler() {
11124
11317
  return new globalThis.peernet.protos["peernet-response"]({ response: this.version });
11125
11318
  }
11126
- async #executeTransaction({ hash, from, to, method, params, nonce }) {
11319
+ async #executeTransaction({ hash, from, to, method, params, nonce, feePayments = { payments: [], burned: 0n } }) {
11127
11320
  try {
11128
- let result = await this.machine.execute(to, method, params);
11321
+ if (feePayments.payments.length > 0 || feePayments.burned > 0n) {
11322
+ await this.machine.collectFee(from, feePayments.payments, feePayments.burned);
11323
+ }
11324
+ let result = await this.machine.execute(to, method, params, from);
11129
11325
  globalThis.pubsub.publish(`transaction.completed.${hash}`, { status: "fulfilled", hash });
11130
11326
  return result || "no state change";
11131
11327
  } catch (error) {
@@ -11194,10 +11390,18 @@ class Chain extends VersionControl {
11194
11390
  if (nonceDiff !== 0) return nonceDiff;
11195
11391
  return 0;
11196
11392
  });
11393
+ const monetaryPolicy = await this.#monetaryPolicy(blockMessage.decoded.protocolVersion);
11197
11394
  for (const transaction of allTransactions) {
11198
11395
  if (!contracts.includes(transaction.decoded.to)) contracts.push(transaction.decoded.to);
11199
11396
  this.removePendingNonce(transaction.decoded.from, transaction.decoded.nonce);
11200
- await this.#handleTransaction(transaction, []);
11397
+ const transactionHash = await transaction.hash();
11398
+ const validatorAddresses = blockMessage.decoded.validators.map(({ address }) => address);
11399
+ const feeDistribution = supportsTransactionFees(blockMessage.decoded.protocolVersion) ? distributeTransactionFee(BigInt(await calculateFee(transaction.decoded)), transactionHash, validatorAddresses, monetaryPolicy.burnBasisPoints) : { payments: [], burned: 0n };
11400
+ await this.#handleTransaction(transaction, [], void 0, feeDistribution);
11401
+ }
11402
+ if (supportsMonetaryPolicy(blockMessage.decoded.protocolVersion) && monetaryPolicy.subsidy > 0n) {
11403
+ const validators = blockMessage.decoded.validators.map(({ address }) => address);
11404
+ await this.machine.settleRewards([...distributeAmount(monetaryPolicy.subsidy, validators, blockIndex % validators.length)]);
11201
11405
  }
11202
11406
  try {
11203
11407
  promises = await Promise.allSettled(promises);
@@ -11254,7 +11458,7 @@ class Chain extends VersionControl {
11254
11458
  if (await this.hasTransactionToHandle() && !this.#runningEpoch && this.#participating) await this.#runEpoch();
11255
11459
  return true;
11256
11460
  }
11257
- async #handleTransaction(transaction, latestTransactions, block) {
11461
+ async #handleTransaction(transaction, latestTransactions, block, feePayments = { payments: [], burned: 0n }) {
11258
11462
  await this.validateTransactionSignature(transaction);
11259
11463
  const hash = await transaction.hash();
11260
11464
  const doubleTransactions = [];
@@ -11267,7 +11471,7 @@ class Chain extends VersionControl {
11267
11471
  return;
11268
11472
  }
11269
11473
  try {
11270
- const result = await this.#executeTransaction({ ...transaction.decoded, hash });
11474
+ const result = await this.#executeTransaction({ ...transaction.decoded, hash, feePayments });
11271
11475
  if (block) {
11272
11476
  block.transactions.push(hash);
11273
11477
  block.fees = block.fees += await calculateFee(transaction.decoded);
@@ -11298,7 +11502,7 @@ class Chain extends VersionControl {
11298
11502
  fees: BigInt(0),
11299
11503
  timestamp,
11300
11504
  previousHash: "",
11301
- reward: BLOCK_REWARD,
11505
+ reward: 0n,
11302
11506
  index: 0,
11303
11507
  producer: "",
11304
11508
  producerProof: "",
@@ -11320,10 +11524,18 @@ class Chain extends VersionControl {
11320
11524
  if (nonceDiff !== 0) return nonceDiff;
11321
11525
  return 0;
11322
11526
  });
11527
+ let blockTransactionBytes = 0;
11323
11528
  for (const { transaction, hash } of allTransactions) {
11529
+ const transactionBytes = transaction.encoded.length;
11530
+ if (transactionBytes > MAX_TRANSACTION_BYTES) {
11531
+ await globalThis.transactionPoolStore.delete(hash);
11532
+ continue;
11533
+ }
11534
+ if (block.transactions.length >= MAX_BLOCK_TRANSACTIONS || blockTransactionBytes + transactionBytes > MAX_BLOCK_TRANSACTION_BYTES) break;
11324
11535
  await this.validateTransactionSignature(transaction);
11325
11536
  block.transactions.push(hash);
11326
- block.fees += BigInt(await calculateFee(transaction.decoded));
11537
+ blockTransactionBytes += transactionBytes;
11538
+ if (supportsTransactionFees(this.version)) block.fees += BigInt(await calculateFee(transaction.decoded));
11327
11539
  await globalThis.peernet.put(hash, transaction.encoded, "transaction");
11328
11540
  }
11329
11541
  if (block.transactions.length === 0) return;
@@ -11333,9 +11545,20 @@ class Chain extends VersionControl {
11333
11545
  const canonicalValidators = await this.staticCall(addresses.validators, "validators");
11334
11546
  const sortedValidators = [...canonicalValidators].sort();
11335
11547
  if (sortedValidators.length === 0) throw new Error("cannot produce a block without validators");
11548
+ if (supportsTransactionFees(this.version)) await this.#assertFeeBurnSupported(this.version);
11549
+ const monetaryPolicy = await this.#monetaryPolicy(this.version);
11550
+ block.reward = monetaryPolicy.subsidy;
11551
+ const feeEntries = supportsTransactionFees(this.version) ? await Promise.all(
11552
+ allTransactions.map(async ({ transaction, hash }) => ({
11553
+ fee: BigInt(await calculateFee(transaction.decoded)),
11554
+ transactionHash: hash
11555
+ }))
11556
+ ) : [];
11557
+ const validatorFees = supportsTransactionFees(this.version) ? aggregateValidatorFees(feeEntries, sortedValidators, monetaryPolicy.burnBasisPoints) : new Map(sortedValidators.map((address) => [address, 0n]));
11558
+ 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)]));
11336
11559
  block.validators = sortedValidators.map((validatorAddress) => ({
11337
11560
  address: validatorAddress,
11338
- reward: block.fees / BigInt(sortedValidators.length) + block.reward / BigInt(sortedValidators.length)
11561
+ reward: (validatorFees.get(validatorAddress) || 0n) + (subsidyRewards.get(validatorAddress) || 0n)
11339
11562
  }));
11340
11563
  try {
11341
11564
  block.producer = globalThis.peernet.selectedAccount || "";
@@ -11460,7 +11683,7 @@ class Chain extends VersionControl {
11460
11683
  * @returns
11461
11684
  */
11462
11685
  internalCall(sender, contract, method, parameters) {
11463
- return this.machine.execute(contract, method, parameters);
11686
+ return this.machine.execute(contract, method, parameters, sender);
11464
11687
  }
11465
11688
  /**
11466
11689
  *
@@ -11470,7 +11693,7 @@ class Chain extends VersionControl {
11470
11693
  * @returns
11471
11694
  */
11472
11695
  call(contract, method, parameters) {
11473
- return this.machine.execute(contract, method, parameters);
11696
+ return this.machine.execute(contract, method, parameters, globalThis.peernet.selectedAccount);
11474
11697
  }
11475
11698
  staticCall(contract, method, parameters) {
11476
11699
  return this.machine.get(contract, method, parameters);