@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.
- package/exports/beacon-envelope.js +68 -0
- package/exports/beacon-epoch.js +117 -0
- package/exports/beacon-lifecycle.js +155 -0
- package/exports/beacon-round.js +77 -0
- package/exports/beacon-wire.js +98 -0
- package/exports/beacon.js +141 -0
- package/exports/browser/beacon-envelope.js +163 -0
- package/exports/browser/beacon-epoch.js +116 -0
- package/exports/browser/beacon-lifecycle.js +154 -0
- package/exports/browser/beacon-round.js +76 -0
- package/exports/browser/beacon-wire.js +99 -0
- package/exports/browser/beacon.js +1706 -0
- package/exports/browser/{browser-D-r0O9Qn-BZDYY6cg.js → browser-CWeoyGUw-BabtHowB.js} +4 -2
- package/exports/browser/{browser-_hiyXwPp-DYI1tyUr.js → browser-DvU1xNFS-sdlKNCJc.js} +4 -2
- package/exports/browser/chain.js +174 -5008
- package/exports/browser/{client-BVhUamQG-D-D1e_x8.js → client-B-jyclOB-CsNYfj4Z.js} +6 -4
- package/exports/browser/constants-Cv0p224A.js +130 -0
- package/exports/browser/hkdf-DhdhLAAv.js +147 -0
- package/exports/browser/{index-BD0Anx7_-Dg4_tCeN.js → index-Bxr5Iztg-C6xBk0rK.js} +4 -2
- package/exports/browser/index-CvKt4UDE.js +464 -0
- package/exports/browser/index-D7FUx7Dd.js +4996 -0
- package/exports/browser/{messages-UKnuelZ7-DJT-98q-.js → messages-FMRAS8QX-CvlZBzy5.js} +4 -2
- package/exports/browser/{node-browser-DlzZ5CP_.js → node-browser-xlqSBOaN.js} +12 -5
- package/exports/browser/node-browser.js +4 -2
- package/exports/browser/{constants-gMYZLHKp.js → proposal.proto-BcUgd885.js} +61 -620
- package/exports/browser/quorum-S_qdgiAY.js +8 -0
- package/exports/browser/weierstrass-C-jX_jly.js +2156 -0
- package/exports/browser/workers/block-worker.js +1 -1
- package/exports/browser/workers/machine-worker.js +7142 -72
- package/exports/browser/workers/{worker-CZqErLI7-BxofVJAn.js → worker-DMCj1e6z-CTYVa2yX.js} +30 -1
- package/exports/chain.js +158 -75
- package/exports/{constants-D6gWzJZg.js → constants-CMYKv-Rt.js} +1 -1
- package/exports/node.js +1 -1
- package/exports/quorum-S_qdgiAY.js +8 -0
- package/exports/workers/block-worker.js +1 -1
- package/exports/workers/machine-worker.js +7142 -72
- package/exports/workers/{worker-CZqErLI7-BxofVJAn.js → worker-DMCj1e6z-CTYVa2yX.js} +30 -1
- package/package.json +29 -2
- package/types/beacon-envelope.d.ts +27 -0
- package/types/beacon-epoch.d.ts +28 -0
- package/types/beacon-lifecycle.d.ts +40 -0
- package/types/beacon-round.d.ts +18 -0
- package/types/beacon-wire.d.ts +31 -0
- 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,
|
|
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-
|
|
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
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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)
|
|
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
|
-
|
|
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 !==
|
|
2307
|
-
throw new Error(`Block ${block.index} has invalid base reward: expected ${
|
|
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 =
|
|
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
|
|
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
|
|
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
|
-
|
|
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(
|
|
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
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
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({
|
|
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(
|
|
3347
|
-
|
|
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({
|
|
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:
|
|
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) +
|
|
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
|
-
|
|
3634
|
+
let hash;
|
|
3557
3635
|
try {
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
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
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
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) {
|
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-
|
|
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-
|
|
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() {
|