@leofcoin/workers 1.5.36 → 1.5.37

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.
@@ -977,9 +977,17 @@ var bytecodes = {
977
977
 
978
978
  const TRANSACTION_FEE_BYTES = 1024n;
979
979
  const TRANSACTION_FEE_UNIT = 10n;
980
- const FEE_BURN_BASIS_POINTS = 1000n;
980
+ const MAX_TRANSACTION_BYTES = 32 * 1024;
981
+ const MAX_BLOCK_TRANSACTION_BYTES = 128 * 1024;
982
+ const MAX_BLOCK_TRANSACTIONS = 256;
983
+ (BigInt(MAX_TRANSACTION_BYTES) / TRANSACTION_FEE_BYTES) * TRANSACTION_FEE_UNIT;
981
984
  const FEE_BASIS_POINTS = 10000n;
982
985
  const FEE_PROTOCOL_VERSION = '1.10.9';
986
+ const MONETARY_POLICY_PROTOCOL_VERSION = '1.10.10';
987
+ const BLOCKS_PER_YEAR = 5256000n;
988
+ const ANNUAL_ISSUANCE_BASIS_POINTS = 200n;
989
+ const SUPPLY_FLOOR_BASIS_POINTS = 9500n;
990
+ const MONETARY_FEE_BURN_BASIS_POINTS = 1000n;
983
991
  const parseProtocolVersion = (version) => {
984
992
  const match = /^(\d+)\.(\d+)\.(\d+)/.exec(version);
985
993
  return match ? [Number(match[1]), Number(match[2]), Number(match[3])] : undefined;
@@ -995,19 +1003,61 @@ const supportsTransactionFees = (version) => {
995
1003
  }
996
1004
  return true;
997
1005
  };
1006
+ const supportsMonetaryPolicy = (version) => {
1007
+ const actual = parseProtocolVersion(version);
1008
+ const required = parseProtocolVersion(MONETARY_POLICY_PROTOCOL_VERSION);
1009
+ if (!actual)
1010
+ return false;
1011
+ for (let index = 0; index < required.length; index += 1) {
1012
+ if (actual[index] !== required[index])
1013
+ return actual[index] > required[index];
1014
+ }
1015
+ return true;
1016
+ };
1017
+ const calculateMonetaryPolicy = (totalSupply, targetSupply) => {
1018
+ if (totalSupply < 0n || targetSupply <= 0n)
1019
+ throw new Error('invalid monetary policy supply');
1020
+ const floorSupply = (targetSupply * SUPPLY_FLOOR_BASIS_POINTS) / FEE_BASIS_POINTS;
1021
+ if (totalSupply < floorSupply) {
1022
+ const annualIssuance = (targetSupply * ANNUAL_ISSUANCE_BASIS_POINTS) / FEE_BASIS_POINTS;
1023
+ const scheduled = annualIssuance / BLOCKS_PER_YEAR || 1n;
1024
+ return { subsidy: scheduled < floorSupply - totalSupply ? scheduled : floorSupply - totalSupply, burnBasisPoints: 0n, floorSupply };
1025
+ }
1026
+ return {
1027
+ subsidy: 0n,
1028
+ burnBasisPoints: totalSupply >= targetSupply ? MONETARY_FEE_BURN_BASIS_POINTS : 0n,
1029
+ floorSupply
1030
+ };
1031
+ };
1032
+ const distributeAmount = (amount, addresses, rotation = 0) => {
1033
+ const canonical = [...new Set(addresses)].sort();
1034
+ if (canonical.length === 0)
1035
+ throw new Error('cannot distribute without validators');
1036
+ const count = BigInt(canonical.length);
1037
+ const base = amount / count;
1038
+ const remainder = Number(amount % count);
1039
+ const result = new Map(canonical.map((address) => [address, base]));
1040
+ for (let index = 0; index < remainder; index += 1) {
1041
+ const address = canonical[(rotation + index) % canonical.length];
1042
+ result.set(address, result.get(address) + 1n);
1043
+ }
1044
+ return result;
1045
+ };
998
1046
  const feeRotationIndex = (transactionHash, validatorCount) => {
999
1047
  let value = 0n;
1000
1048
  for (const character of transactionHash)
1001
1049
  value = (value * 31n + BigInt(character.charCodeAt(0))) % 4294967291n;
1002
1050
  return Number(value % BigInt(validatorCount));
1003
1051
  };
1004
- const distributeTransactionFee = (fee, transactionHash, validatorAddresses) => {
1052
+ const distributeTransactionFee = (fee, transactionHash, validatorAddresses, burnBasisPoints = 1000n) => {
1005
1053
  const canonicalValidators = [...new Set(validatorAddresses)].sort();
1006
1054
  if (canonicalValidators.length === 0)
1007
1055
  throw new Error('cannot distribute transaction fee without validators');
1008
1056
  if (fee < 0n)
1009
1057
  throw new Error('transaction fee cannot be negative');
1010
- const burned = (fee * FEE_BURN_BASIS_POINTS) / FEE_BASIS_POINTS;
1058
+ if (burnBasisPoints < 0n || burnBasisPoints > FEE_BASIS_POINTS)
1059
+ throw new Error('invalid fee burn rate');
1060
+ const burned = (fee * burnBasisPoints) / FEE_BASIS_POINTS;
1011
1061
  const validatorPool = fee - burned;
1012
1062
  const count = BigInt(canonicalValidators.length);
1013
1063
  const base = validatorPool / count;
@@ -1030,6 +1080,26 @@ const calculateFee = async (transaction, format = false) => {
1030
1080
  const fee = units * TRANSACTION_FEE_UNIT;
1031
1081
  return format ? formatUnits(fee.toString()) : fee;
1032
1082
  };
1083
+ const validateTransactionResourceLimits = async (transaction) => {
1084
+ const message = await new TransactionMessage(transaction);
1085
+ const size = message.encoded.length;
1086
+ if (size > MAX_TRANSACTION_BYTES) {
1087
+ throw new Error(`transaction exceeds ${MAX_TRANSACTION_BYTES} byte protocol limit: ${size}`);
1088
+ }
1089
+ return size;
1090
+ };
1091
+ const validateBlockResourceLimits = async (transactions) => {
1092
+ if (transactions.length > MAX_BLOCK_TRANSACTIONS) {
1093
+ throw new Error(`block exceeds ${MAX_BLOCK_TRANSACTIONS} transaction protocol limit`);
1094
+ }
1095
+ let size = 0;
1096
+ for (const transaction of transactions)
1097
+ size += await validateTransactionResourceLimits(transaction);
1098
+ if (size > MAX_BLOCK_TRANSACTION_BYTES) {
1099
+ throw new Error(`block transactions exceed ${MAX_BLOCK_TRANSACTION_BYTES} byte protocol limit: ${size}`);
1100
+ }
1101
+ return size;
1102
+ };
1033
1103
 
1034
1104
  class LittlePubSub {
1035
1105
  subscribers = new Map();
@@ -1211,7 +1281,7 @@ const runTask = async (id, taskName, input) => {
1211
1281
  });
1212
1282
  }
1213
1283
  };
1214
- const _executeTransaction = async (transaction, validators, feesEnabled) => {
1284
+ const _executeTransaction = async (transaction, validators, feesEnabled, burnBasisPoints) => {
1215
1285
  const hash = await new TransactionMessage(transaction).hash();
1216
1286
  if (latestTransactions.includes(hash)) {
1217
1287
  throw new Error(`double transaction found: ${hash}`);
@@ -1223,7 +1293,7 @@ const _executeTransaction = async (transaction, validators, feesEnabled) => {
1223
1293
  globalThis.state = await createState();
1224
1294
  if (feesEnabled) {
1225
1295
  const fee = BigInt(await calculateFee(transaction));
1226
- const { payments, burned } = distributeTransactionFee(fee, hash, validators);
1296
+ const { payments, burned } = distributeTransactionFee(fee, hash, validators, burnBasisPoints);
1227
1297
  await _.collectFee({ from, payments, burned });
1228
1298
  }
1229
1299
  await _.execute({ contract: to, method, params });
@@ -1327,6 +1397,17 @@ const _ = {
1327
1397
  }
1328
1398
  return total;
1329
1399
  },
1400
+ settleRewards: ({ rewards }) => {
1401
+ globalThis.msg = createMessage(contracts[nativeToken$2].creator, nativeToken$2);
1402
+ let minted = 0n;
1403
+ for (const [validator, rawAmount] of rewards) {
1404
+ const amount = BigInt(rawAmount);
1405
+ if (amount > 0n)
1406
+ contracts[nativeToken$2].mint(validator, amount);
1407
+ minted += amount;
1408
+ }
1409
+ return minted;
1410
+ },
1330
1411
  init: async (message) => {
1331
1412
  let { peerid, fromState, state, info } = message;
1332
1413
  if (info)
@@ -1438,8 +1519,20 @@ const _ = {
1438
1519
  return left < right ? -1 : left > right ? 1 : 0;
1439
1520
  });
1440
1521
  const feesEnabled = supportsTransactionFees(block.protocolVersion);
1441
- for (const transaction of transactions)
1442
- await _executeTransaction(transaction, validators, feesEnabled);
1522
+ const monetaryPolicyEnabled = supportsMonetaryPolicy(block.protocolVersion);
1523
+ if (monetaryPolicyEnabled)
1524
+ await validateBlockResourceLimits(transactions);
1525
+ const policy = monetaryPolicyEnabled
1526
+ ? calculateMonetaryPolicy(BigInt(contracts[nativeToken$2].totalSupply), BigInt(contracts[nativeToken$2].targetSupply))
1527
+ : { subsidy: 0n, burnBasisPoints: 1000n };
1528
+ for (const transaction of transactions) {
1529
+ await _executeTransaction(transaction, validators, feesEnabled, policy.burnBasisPoints);
1530
+ }
1531
+ if (monetaryPolicyEnabled && policy.subsidy > 0n) {
1532
+ _.settleRewards({
1533
+ rewards: [...distributeAmount(policy.subsidy, validators, Number(block.index) % validators.length)]
1534
+ });
1535
+ }
1443
1536
  block.loaded = true;
1444
1537
  worker.postMessage({
1445
1538
  type: 'debug',
@@ -1507,6 +1600,9 @@ worker.onmessage(({ id, type, input }) => {
1507
1600
  case 'collectFee':
1508
1601
  runTask(id, 'collectFee', input);
1509
1602
  break;
1603
+ case 'settleRewards':
1604
+ runTask(id, 'settleRewards', input);
1605
+ break;
1510
1606
  case 'contracts':
1511
1607
  respond(id, contracts);
1512
1608
  break;
@@ -977,9 +977,17 @@ var bytecodes = {
977
977
 
978
978
  const TRANSACTION_FEE_BYTES = 1024n;
979
979
  const TRANSACTION_FEE_UNIT = 10n;
980
- const FEE_BURN_BASIS_POINTS = 1000n;
980
+ const MAX_TRANSACTION_BYTES = 32 * 1024;
981
+ const MAX_BLOCK_TRANSACTION_BYTES = 128 * 1024;
982
+ const MAX_BLOCK_TRANSACTIONS = 256;
983
+ (BigInt(MAX_TRANSACTION_BYTES) / TRANSACTION_FEE_BYTES) * TRANSACTION_FEE_UNIT;
981
984
  const FEE_BASIS_POINTS = 10000n;
982
985
  const FEE_PROTOCOL_VERSION = '1.10.9';
986
+ const MONETARY_POLICY_PROTOCOL_VERSION = '1.10.10';
987
+ const BLOCKS_PER_YEAR = 5256000n;
988
+ const ANNUAL_ISSUANCE_BASIS_POINTS = 200n;
989
+ const SUPPLY_FLOOR_BASIS_POINTS = 9500n;
990
+ const MONETARY_FEE_BURN_BASIS_POINTS = 1000n;
983
991
  const parseProtocolVersion = (version) => {
984
992
  const match = /^(\d+)\.(\d+)\.(\d+)/.exec(version);
985
993
  return match ? [Number(match[1]), Number(match[2]), Number(match[3])] : undefined;
@@ -995,19 +1003,61 @@ const supportsTransactionFees = (version) => {
995
1003
  }
996
1004
  return true;
997
1005
  };
1006
+ const supportsMonetaryPolicy = (version) => {
1007
+ const actual = parseProtocolVersion(version);
1008
+ const required = parseProtocolVersion(MONETARY_POLICY_PROTOCOL_VERSION);
1009
+ if (!actual)
1010
+ return false;
1011
+ for (let index = 0; index < required.length; index += 1) {
1012
+ if (actual[index] !== required[index])
1013
+ return actual[index] > required[index];
1014
+ }
1015
+ return true;
1016
+ };
1017
+ const calculateMonetaryPolicy = (totalSupply, targetSupply) => {
1018
+ if (totalSupply < 0n || targetSupply <= 0n)
1019
+ throw new Error('invalid monetary policy supply');
1020
+ const floorSupply = (targetSupply * SUPPLY_FLOOR_BASIS_POINTS) / FEE_BASIS_POINTS;
1021
+ if (totalSupply < floorSupply) {
1022
+ const annualIssuance = (targetSupply * ANNUAL_ISSUANCE_BASIS_POINTS) / FEE_BASIS_POINTS;
1023
+ const scheduled = annualIssuance / BLOCKS_PER_YEAR || 1n;
1024
+ return { subsidy: scheduled < floorSupply - totalSupply ? scheduled : floorSupply - totalSupply, burnBasisPoints: 0n, floorSupply };
1025
+ }
1026
+ return {
1027
+ subsidy: 0n,
1028
+ burnBasisPoints: totalSupply >= targetSupply ? MONETARY_FEE_BURN_BASIS_POINTS : 0n,
1029
+ floorSupply
1030
+ };
1031
+ };
1032
+ const distributeAmount = (amount, addresses, rotation = 0) => {
1033
+ const canonical = [...new Set(addresses)].sort();
1034
+ if (canonical.length === 0)
1035
+ throw new Error('cannot distribute without validators');
1036
+ const count = BigInt(canonical.length);
1037
+ const base = amount / count;
1038
+ const remainder = Number(amount % count);
1039
+ const result = new Map(canonical.map((address) => [address, base]));
1040
+ for (let index = 0; index < remainder; index += 1) {
1041
+ const address = canonical[(rotation + index) % canonical.length];
1042
+ result.set(address, result.get(address) + 1n);
1043
+ }
1044
+ return result;
1045
+ };
998
1046
  const feeRotationIndex = (transactionHash, validatorCount) => {
999
1047
  let value = 0n;
1000
1048
  for (const character of transactionHash)
1001
1049
  value = (value * 31n + BigInt(character.charCodeAt(0))) % 4294967291n;
1002
1050
  return Number(value % BigInt(validatorCount));
1003
1051
  };
1004
- const distributeTransactionFee = (fee, transactionHash, validatorAddresses) => {
1052
+ const distributeTransactionFee = (fee, transactionHash, validatorAddresses, burnBasisPoints = 1000n) => {
1005
1053
  const canonicalValidators = [...new Set(validatorAddresses)].sort();
1006
1054
  if (canonicalValidators.length === 0)
1007
1055
  throw new Error('cannot distribute transaction fee without validators');
1008
1056
  if (fee < 0n)
1009
1057
  throw new Error('transaction fee cannot be negative');
1010
- const burned = (fee * FEE_BURN_BASIS_POINTS) / FEE_BASIS_POINTS;
1058
+ if (burnBasisPoints < 0n || burnBasisPoints > FEE_BASIS_POINTS)
1059
+ throw new Error('invalid fee burn rate');
1060
+ const burned = (fee * burnBasisPoints) / FEE_BASIS_POINTS;
1011
1061
  const validatorPool = fee - burned;
1012
1062
  const count = BigInt(canonicalValidators.length);
1013
1063
  const base = validatorPool / count;
@@ -1030,6 +1080,26 @@ const calculateFee = async (transaction, format = false) => {
1030
1080
  const fee = units * TRANSACTION_FEE_UNIT;
1031
1081
  return format ? formatUnits(fee.toString()) : fee;
1032
1082
  };
1083
+ const validateTransactionResourceLimits = async (transaction) => {
1084
+ const message = await new TransactionMessage(transaction);
1085
+ const size = message.encoded.length;
1086
+ if (size > MAX_TRANSACTION_BYTES) {
1087
+ throw new Error(`transaction exceeds ${MAX_TRANSACTION_BYTES} byte protocol limit: ${size}`);
1088
+ }
1089
+ return size;
1090
+ };
1091
+ const validateBlockResourceLimits = async (transactions) => {
1092
+ if (transactions.length > MAX_BLOCK_TRANSACTIONS) {
1093
+ throw new Error(`block exceeds ${MAX_BLOCK_TRANSACTIONS} transaction protocol limit`);
1094
+ }
1095
+ let size = 0;
1096
+ for (const transaction of transactions)
1097
+ size += await validateTransactionResourceLimits(transaction);
1098
+ if (size > MAX_BLOCK_TRANSACTION_BYTES) {
1099
+ throw new Error(`block transactions exceed ${MAX_BLOCK_TRANSACTION_BYTES} byte protocol limit: ${size}`);
1100
+ }
1101
+ return size;
1102
+ };
1033
1103
 
1034
1104
  class LittlePubSub {
1035
1105
  subscribers = new Map();
@@ -1211,7 +1281,7 @@ const runTask = async (id, taskName, input) => {
1211
1281
  });
1212
1282
  }
1213
1283
  };
1214
- const _executeTransaction = async (transaction, validators, feesEnabled) => {
1284
+ const _executeTransaction = async (transaction, validators, feesEnabled, burnBasisPoints) => {
1215
1285
  const hash = await new TransactionMessage(transaction).hash();
1216
1286
  if (latestTransactions.includes(hash)) {
1217
1287
  throw new Error(`double transaction found: ${hash}`);
@@ -1223,7 +1293,7 @@ const _executeTransaction = async (transaction, validators, feesEnabled) => {
1223
1293
  globalThis.state = await createState();
1224
1294
  if (feesEnabled) {
1225
1295
  const fee = BigInt(await calculateFee(transaction));
1226
- const { payments, burned } = distributeTransactionFee(fee, hash, validators);
1296
+ const { payments, burned } = distributeTransactionFee(fee, hash, validators, burnBasisPoints);
1227
1297
  await _.collectFee({ from, payments, burned });
1228
1298
  }
1229
1299
  await _.execute({ contract: to, method, params });
@@ -1327,6 +1397,17 @@ const _ = {
1327
1397
  }
1328
1398
  return total;
1329
1399
  },
1400
+ settleRewards: ({ rewards }) => {
1401
+ globalThis.msg = createMessage(contracts[nativeToken$2].creator, nativeToken$2);
1402
+ let minted = 0n;
1403
+ for (const [validator, rawAmount] of rewards) {
1404
+ const amount = BigInt(rawAmount);
1405
+ if (amount > 0n)
1406
+ contracts[nativeToken$2].mint(validator, amount);
1407
+ minted += amount;
1408
+ }
1409
+ return minted;
1410
+ },
1330
1411
  init: async (message) => {
1331
1412
  let { peerid, fromState, state, info } = message;
1332
1413
  if (info)
@@ -1438,8 +1519,20 @@ const _ = {
1438
1519
  return left < right ? -1 : left > right ? 1 : 0;
1439
1520
  });
1440
1521
  const feesEnabled = supportsTransactionFees(block.protocolVersion);
1441
- for (const transaction of transactions)
1442
- await _executeTransaction(transaction, validators, feesEnabled);
1522
+ const monetaryPolicyEnabled = supportsMonetaryPolicy(block.protocolVersion);
1523
+ if (monetaryPolicyEnabled)
1524
+ await validateBlockResourceLimits(transactions);
1525
+ const policy = monetaryPolicyEnabled
1526
+ ? calculateMonetaryPolicy(BigInt(contracts[nativeToken$2].totalSupply), BigInt(contracts[nativeToken$2].targetSupply))
1527
+ : { subsidy: 0n, burnBasisPoints: 1000n };
1528
+ for (const transaction of transactions) {
1529
+ await _executeTransaction(transaction, validators, feesEnabled, policy.burnBasisPoints);
1530
+ }
1531
+ if (monetaryPolicyEnabled && policy.subsidy > 0n) {
1532
+ _.settleRewards({
1533
+ rewards: [...distributeAmount(policy.subsidy, validators, Number(block.index) % validators.length)]
1534
+ });
1535
+ }
1443
1536
  block.loaded = true;
1444
1537
  worker.postMessage({
1445
1538
  type: 'debug',
@@ -1507,6 +1600,9 @@ worker.onmessage(({ id, type, input }) => {
1507
1600
  case 'collectFee':
1508
1601
  runTask(id, 'collectFee', input);
1509
1602
  break;
1603
+ case 'settleRewards':
1604
+ runTask(id, 'settleRewards', input);
1605
+ break;
1510
1606
  case 'contracts':
1511
1607
  respond(id, contracts);
1512
1608
  break;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leofcoin/workers",
3
- "version": "1.5.36",
3
+ "version": "1.5.37",
4
4
  "description": "Workers used in @leofcoin/chain",
5
5
  "type": "module",
6
6
  "private": false,