@leofcoin/chain 1.10.7 → 1.10.9
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/browser/chain.js +213 -44
- package/exports/browser/workers/machine-worker.js +998 -12
- package/exports/chain.js +156 -41
- package/exports/workers/machine-worker.js +998 -12
- package/package.json +1 -1
package/exports/browser/chain.js
CHANGED
|
@@ -1056,6 +1056,61 @@ 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 FEE_BURN_BASIS_POINTS = 1000n;
|
|
1062
|
+
const FEE_BASIS_POINTS = 10000n;
|
|
1063
|
+
const FEE_PROTOCOL_VERSION = "1.10.9";
|
|
1064
|
+
const parseProtocolVersion = (version) => {
|
|
1065
|
+
const match = /^(\d+)\.(\d+)\.(\d+)/.exec(version);
|
|
1066
|
+
return match ? [Number(match[1]), Number(match[2]), Number(match[3])] : void 0;
|
|
1067
|
+
};
|
|
1068
|
+
const supportsTransactionFees = (version) => {
|
|
1069
|
+
const actual = parseProtocolVersion(version);
|
|
1070
|
+
const required = parseProtocolVersion(FEE_PROTOCOL_VERSION);
|
|
1071
|
+
if (!actual)
|
|
1072
|
+
return false;
|
|
1073
|
+
for (let index = 0; index < required.length; index += 1) {
|
|
1074
|
+
if (actual[index] !== required[index])
|
|
1075
|
+
return actual[index] > required[index];
|
|
1076
|
+
}
|
|
1077
|
+
return true;
|
|
1078
|
+
};
|
|
1079
|
+
const feeRotationIndex = (transactionHash, validatorCount) => {
|
|
1080
|
+
let value = 0n;
|
|
1081
|
+
for (const character of transactionHash)
|
|
1082
|
+
value = (value * 31n + BigInt(character.charCodeAt(0))) % 4294967291n;
|
|
1083
|
+
return Number(value % BigInt(validatorCount));
|
|
1084
|
+
};
|
|
1085
|
+
const distributeTransactionFee = (fee, transactionHash, validatorAddresses) => {
|
|
1086
|
+
const canonicalValidators = [...new Set(validatorAddresses)].sort();
|
|
1087
|
+
if (canonicalValidators.length === 0)
|
|
1088
|
+
throw new Error("cannot distribute transaction fee without validators");
|
|
1089
|
+
if (fee < 0n)
|
|
1090
|
+
throw new Error("transaction fee cannot be negative");
|
|
1091
|
+
const burned = fee * FEE_BURN_BASIS_POINTS / FEE_BASIS_POINTS;
|
|
1092
|
+
const validatorPool = fee - burned;
|
|
1093
|
+
const count = BigInt(canonicalValidators.length);
|
|
1094
|
+
const base = validatorPool / count;
|
|
1095
|
+
const remainder = Number(validatorPool % count);
|
|
1096
|
+
const start = feeRotationIndex(transactionHash, canonicalValidators.length);
|
|
1097
|
+
const validatorFees = new Map(canonicalValidators.map((validator) => [validator, base]));
|
|
1098
|
+
for (let index = 0; index < remainder; index += 1) {
|
|
1099
|
+
const validator = canonicalValidators[(start + index) % canonicalValidators.length];
|
|
1100
|
+
validatorFees.set(validator, validatorFees.get(validator) + 1n);
|
|
1101
|
+
}
|
|
1102
|
+
const payments = [...validatorFees.entries()].filter(([, amount]) => amount > 0n).map(([to, amount]) => ({ to, amount }));
|
|
1103
|
+
return { burned, validatorFees, payments };
|
|
1104
|
+
};
|
|
1105
|
+
const aggregateValidatorFees = (fees, validatorAddresses) => {
|
|
1106
|
+
const totals = new Map([...new Set(validatorAddresses)].sort().map((validator) => [validator, 0n]));
|
|
1107
|
+
for (const entry of fees) {
|
|
1108
|
+
const { validatorFees } = distributeTransactionFee(entry.fee, entry.transactionHash, validatorAddresses);
|
|
1109
|
+
for (const [validator, amount] of validatorFees)
|
|
1110
|
+
totals.set(validator, totals.get(validator) + amount);
|
|
1111
|
+
}
|
|
1112
|
+
return totals;
|
|
1113
|
+
};
|
|
1059
1114
|
const createContractMessage = async (creator, contract, constructorParameters = []) => {
|
|
1060
1115
|
return new ContractMessage({
|
|
1061
1116
|
creator,
|
|
@@ -1064,11 +1119,10 @@ const createContractMessage = async (creator, contract, constructorParameters =
|
|
|
1064
1119
|
});
|
|
1065
1120
|
};
|
|
1066
1121
|
const calculateFee = async (transaction, format = false) => {
|
|
1067
|
-
if (transaction.to === validators$2)
|
|
1068
|
-
return 0;
|
|
1069
1122
|
transaction = await new TransactionMessage(transaction);
|
|
1070
|
-
|
|
1071
|
-
|
|
1123
|
+
const encodedBytes = BigInt(transaction.encoded.length);
|
|
1124
|
+
const units = (encodedBytes + TRANSACTION_FEE_BYTES - 1n) / TRANSACTION_FEE_BYTES;
|
|
1125
|
+
const fee = units * TRANSACTION_FEE_UNIT;
|
|
1072
1126
|
return format ? formatUnits(fee.toString()) : fee;
|
|
1073
1127
|
};
|
|
1074
1128
|
const createTransactionHash = async (transaction) => {
|
|
@@ -8801,7 +8855,7 @@ class Machine {
|
|
|
8801
8855
|
* @param {Array} parameters
|
|
8802
8856
|
* @returns Promise<message>
|
|
8803
8857
|
*/
|
|
8804
|
-
async execute(contract, method, parameters) {
|
|
8858
|
+
async execute(contract, method, parameters, sender) {
|
|
8805
8859
|
try {
|
|
8806
8860
|
if (contract === contractFactory$2 && method === "registerContract") {
|
|
8807
8861
|
if (await this.has(parameters[0])) throw new Error(`duplicate contract @${parameters[0]}`);
|
|
@@ -8842,11 +8896,15 @@ ${error.message}`);
|
|
|
8842
8896
|
to: contract,
|
|
8843
8897
|
contract,
|
|
8844
8898
|
method,
|
|
8845
|
-
params: parameters
|
|
8899
|
+
params: parameters,
|
|
8900
|
+
sender
|
|
8846
8901
|
}
|
|
8847
8902
|
});
|
|
8848
8903
|
});
|
|
8849
8904
|
}
|
|
8905
|
+
collectFee(from, payments, burned) {
|
|
8906
|
+
return this.#askWorker("collectFee", { from, payments, burned });
|
|
8907
|
+
}
|
|
8850
8908
|
get(contract, method, parameters) {
|
|
8851
8909
|
return new Promise((resolve, reject) => {
|
|
8852
8910
|
const id = randombytes(20).toString();
|
|
@@ -9690,9 +9748,20 @@ class State extends Contract {
|
|
|
9690
9748
|
#loadBlockTransactions;
|
|
9691
9749
|
#getLastTransactions;
|
|
9692
9750
|
// todo throw error
|
|
9693
|
-
async #_executeTransaction(transaction) {
|
|
9751
|
+
async #_executeTransaction(transaction, validators, feesEnabled) {
|
|
9694
9752
|
try {
|
|
9695
|
-
|
|
9753
|
+
const hash = await transaction.hash();
|
|
9754
|
+
if (feesEnabled) {
|
|
9755
|
+
const fee = BigInt(await calculateFee(transaction.decoded));
|
|
9756
|
+
const { payments, burned } = distributeTransactionFee(fee, hash, validators);
|
|
9757
|
+
await this.#machine.collectFee(transaction.decoded.from, payments, burned);
|
|
9758
|
+
}
|
|
9759
|
+
await this.#machine.execute(
|
|
9760
|
+
transaction.decoded.to,
|
|
9761
|
+
transaction.decoded.method,
|
|
9762
|
+
transaction.decoded.params,
|
|
9763
|
+
transaction.decoded.from
|
|
9764
|
+
);
|
|
9696
9765
|
} catch (error) {
|
|
9697
9766
|
console.log(error);
|
|
9698
9767
|
await globalThis.transactionPoolStore.delete(await transaction.hash());
|
|
@@ -9721,23 +9790,21 @@ class State extends Contract {
|
|
|
9721
9790
|
try {
|
|
9722
9791
|
debug$1(`loading block: ${Number(block.index)} ${block.hash}`);
|
|
9723
9792
|
let transactions = await this.#loadBlockTransactions(block.transactions || []);
|
|
9793
|
+
const validators = block.validators.map(({ address }) => address);
|
|
9724
9794
|
debug$1(`loading transactions: ${transactions.length} for block ${block.index}`);
|
|
9725
|
-
let priority = [];
|
|
9726
9795
|
for (const transaction of transactions) {
|
|
9727
9796
|
const hash = await transaction.hash();
|
|
9728
|
-
if (transaction.decoded.priority) priority.push(transaction);
|
|
9729
9797
|
if (poolTransactionKeys.has(hash)) await globalThis.transactionPoolStore.delete(hash);
|
|
9730
9798
|
}
|
|
9731
|
-
|
|
9732
|
-
|
|
9733
|
-
|
|
9734
|
-
|
|
9735
|
-
|
|
9736
|
-
|
|
9737
|
-
}
|
|
9738
|
-
transactions = transactions.filter((transaction) => !transaction.decoded.priority);
|
|
9799
|
+
transactions = transactions.sort((a, b) => {
|
|
9800
|
+
if (a.decoded.priority !== b.decoded.priority) return a.decoded.priority ? -1 : 1;
|
|
9801
|
+
const left = BigInt(a.decoded.nonce);
|
|
9802
|
+
const right = BigInt(b.decoded.nonce);
|
|
9803
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
9804
|
+
});
|
|
9739
9805
|
debug$1(`executing ${transactions.length} transactions for block ${block.index}`);
|
|
9740
|
-
|
|
9806
|
+
const feesEnabled = supportsTransactionFees(block.protocolVersion);
|
|
9807
|
+
for (const transaction of transactions) await this.#_executeTransaction(transaction, validators, feesEnabled);
|
|
9741
9808
|
this.#blocks[block.index].loaded = true;
|
|
9742
9809
|
debug$1(`executed transactions for block ${block.index}`);
|
|
9743
9810
|
if (Number(block.index) === 0) this.#loaded = true;
|
|
@@ -10181,6 +10248,47 @@ const validateChainLink = (localTip, incoming) => {
|
|
|
10181
10248
|
return "append";
|
|
10182
10249
|
};
|
|
10183
10250
|
|
|
10251
|
+
const BLOCK_REWARD = 150n;
|
|
10252
|
+
const validateCanonicalValidatorSet = (blockIndex, expectedValidators, validators) => {
|
|
10253
|
+
const actualValidators = validators.map(({ address }) => address);
|
|
10254
|
+
const canonicalExpected = [...new Set(expectedValidators)].sort();
|
|
10255
|
+
if (actualValidators.length !== canonicalExpected.length || actualValidators.some((address, index) => address !== canonicalExpected[index])) {
|
|
10256
|
+
throw new Error(
|
|
10257
|
+
`Block ${blockIndex} validator set mismatch: expected ${canonicalExpected.join(",")}, got ${actualValidators.join(",")}`
|
|
10258
|
+
);
|
|
10259
|
+
}
|
|
10260
|
+
};
|
|
10261
|
+
const validateBlockEconomics = (block, calculatedFees, validatorFees = /* @__PURE__ */ new Map()) => {
|
|
10262
|
+
if (block.validators.length === 0) {
|
|
10263
|
+
throw new Error(`Block ${block.index} cannot distribute rewards without validators`);
|
|
10264
|
+
}
|
|
10265
|
+
if (block.reward !== BLOCK_REWARD) {
|
|
10266
|
+
throw new Error(`Block ${block.index} has invalid base reward: expected ${BLOCK_REWARD}, got ${block.reward}`);
|
|
10267
|
+
}
|
|
10268
|
+
if (block.fees !== calculatedFees) {
|
|
10269
|
+
throw new Error(`Block ${block.index} has invalid fees: expected ${calculatedFees}, got ${block.fees}`);
|
|
10270
|
+
}
|
|
10271
|
+
const validatorCount = BigInt(block.validators.length);
|
|
10272
|
+
const baseReward = BLOCK_REWARD / validatorCount;
|
|
10273
|
+
for (const validator of block.validators) {
|
|
10274
|
+
const expectedReward = baseReward + (validatorFees.get(validator.address) || 0n);
|
|
10275
|
+
if (validator.reward !== expectedReward) {
|
|
10276
|
+
throw new Error(
|
|
10277
|
+
`Block ${block.index} has an invalid reward for validator ${validator.address}: expected ${expectedReward}, got ${validator.reward}`
|
|
10278
|
+
);
|
|
10279
|
+
}
|
|
10280
|
+
}
|
|
10281
|
+
};
|
|
10282
|
+
|
|
10283
|
+
const resolveTransactionReference = async (expectedHash, transactionData) => {
|
|
10284
|
+
const transaction = transactionData instanceof TransactionMessage ? transactionData : new TransactionMessage(transactionData);
|
|
10285
|
+
const actualHash = await transaction.hash();
|
|
10286
|
+
if (actualHash !== expectedHash) {
|
|
10287
|
+
throw new Error(`Transaction hash mismatch: expected ${expectedHash}, got ${actualHash}`);
|
|
10288
|
+
}
|
|
10289
|
+
return transaction;
|
|
10290
|
+
};
|
|
10291
|
+
|
|
10184
10292
|
const consensusSignableData = (validatorsAddress, type, message) => ({
|
|
10185
10293
|
from: String(message.from),
|
|
10186
10294
|
to: validatorsAddress,
|
|
@@ -10348,8 +10456,23 @@ class Chain extends VersionControl {
|
|
|
10348
10456
|
debug(`[consensus] Block hash mismatch in proposal: expected ${blockHash}, got ${actualHash}`);
|
|
10349
10457
|
return;
|
|
10350
10458
|
}
|
|
10459
|
+
if (BigInt(blockMessage.decoded.index) !== index) {
|
|
10460
|
+
debug(`[consensus] Proposal height ${index} does not match block height ${blockMessage.decoded.index}`);
|
|
10461
|
+
return;
|
|
10462
|
+
}
|
|
10463
|
+
if (blockMessage.decoded.producer !== from) {
|
|
10464
|
+
debug(`[consensus] Proposal sender ${from} does not match block producer ${blockMessage.decoded.producer}`);
|
|
10465
|
+
return;
|
|
10466
|
+
}
|
|
10467
|
+
validateChainLink(localBlock, {
|
|
10468
|
+
index: Number(blockMessage.decoded.index),
|
|
10469
|
+
hash: actualHash,
|
|
10470
|
+
previousHash: String(blockMessage.decoded.previousHash)
|
|
10471
|
+
});
|
|
10472
|
+
await this.#validateBlockValidators(blockMessage);
|
|
10473
|
+
await this.#resolveBlockTransactions(blockMessage);
|
|
10351
10474
|
} catch (e) {
|
|
10352
|
-
debug(`[consensus]
|
|
10475
|
+
debug(`[consensus] Invalid proposed block ${blockHash}:`, e?.message);
|
|
10353
10476
|
return;
|
|
10354
10477
|
}
|
|
10355
10478
|
this.#consensusRound = Number(round);
|
|
@@ -10604,14 +10727,52 @@ class Chain extends VersionControl {
|
|
|
10604
10727
|
if (new Set(validatorAddresses).size !== validatorAddresses.length) {
|
|
10605
10728
|
throw new Error(`Block ${blockMessage.decoded.index} validators contain duplicates`);
|
|
10606
10729
|
}
|
|
10607
|
-
const
|
|
10608
|
-
|
|
10609
|
-
|
|
10610
|
-
|
|
10611
|
-
|
|
10612
|
-
|
|
10613
|
-
);
|
|
10730
|
+
const expectedValidators = await this.staticCall(addresses.validators, "validators");
|
|
10731
|
+
validateCanonicalValidatorSet(blockMessage.decoded.index, expectedValidators, validators);
|
|
10732
|
+
}
|
|
10733
|
+
async #resolveBlockTransactions(blockMessage) {
|
|
10734
|
+
const transactions = await Promise.all(
|
|
10735
|
+
blockMessage.decoded.transactions.map(async (expectedHash) => {
|
|
10736
|
+
const data = await globalThis.peernet.get(expectedHash, "transaction");
|
|
10737
|
+
const transaction = await resolveTransactionReference(expectedHash, data);
|
|
10738
|
+
await this.validateTransactionSignature(transaction);
|
|
10739
|
+
return transaction;
|
|
10740
|
+
})
|
|
10741
|
+
);
|
|
10742
|
+
const feesEnabled = supportsTransactionFees(blockMessage.decoded.protocolVersion);
|
|
10743
|
+
if (feesEnabled) await this.#assertFeeBurnSupported();
|
|
10744
|
+
const calculatedFees = feesEnabled ? (await Promise.all(transactions.map((transaction) => calculateFee(transaction.decoded)))).reduce((total, fee) => total + BigInt(fee), 0n) : 0n;
|
|
10745
|
+
const feeEntries = feesEnabled ? await Promise.all(
|
|
10746
|
+
transactions.map(async (transaction) => ({
|
|
10747
|
+
fee: BigInt(await calculateFee(transaction.decoded)),
|
|
10748
|
+
transactionHash: await transaction.hash()
|
|
10749
|
+
}))
|
|
10750
|
+
) : [];
|
|
10751
|
+
const validatorAddresses = blockMessage.decoded.validators.map(({ address }) => address);
|
|
10752
|
+
const validatorFees = feesEnabled ? aggregateValidatorFees(feeEntries, validatorAddresses) : new Map(validatorAddresses.map((address) => [address, 0n]));
|
|
10753
|
+
validateBlockEconomics(blockMessage.decoded, calculatedFees, validatorFees);
|
|
10754
|
+
if (feesEnabled) {
|
|
10755
|
+
const feesBySender = /* @__PURE__ */ new Map();
|
|
10756
|
+
for (let index = 0; index < transactions.length; index += 1) {
|
|
10757
|
+
const sender = transactions[index].decoded.from;
|
|
10758
|
+
feesBySender.set(sender, (feesBySender.get(sender) || 0n) + feeEntries[index].fee);
|
|
10614
10759
|
}
|
|
10760
|
+
await Promise.all(
|
|
10761
|
+
[...feesBySender].map(async ([sender, required]) => {
|
|
10762
|
+
const balance = BigInt(await this.balanceOf(sender) || 0n);
|
|
10763
|
+
if (balance < required) {
|
|
10764
|
+
throw new Error(`insufficient balance for transaction fees from ${sender}: need ${required}, got ${balance}`);
|
|
10765
|
+
}
|
|
10766
|
+
})
|
|
10767
|
+
);
|
|
10768
|
+
}
|
|
10769
|
+
return transactions;
|
|
10770
|
+
}
|
|
10771
|
+
async #assertFeeBurnSupported() {
|
|
10772
|
+
const creator = await this.staticCall(addresses.nativeToken, "creator");
|
|
10773
|
+
const canBurn = await this.staticCall(addresses.nativeToken, "hasRole", [creator, "BURN"]);
|
|
10774
|
+
if (!canBurn) {
|
|
10775
|
+
throw new Error("native token genesis does not support protocol fee burning");
|
|
10615
10776
|
}
|
|
10616
10777
|
}
|
|
10617
10778
|
/** Check if the next block will cross an epoch boundary (block-based timing) */
|
|
@@ -11062,9 +11223,12 @@ class Chain extends VersionControl {
|
|
|
11062
11223
|
async #versionHandler() {
|
|
11063
11224
|
return new globalThis.peernet.protos["peernet-response"]({ response: this.version });
|
|
11064
11225
|
}
|
|
11065
|
-
async #executeTransaction({ hash, from, to, method, params, nonce }) {
|
|
11226
|
+
async #executeTransaction({ hash, from, to, method, params, nonce, feePayments = { payments: [], burned: 0n } }) {
|
|
11066
11227
|
try {
|
|
11067
|
-
|
|
11228
|
+
if (feePayments.payments.length > 0 || feePayments.burned > 0n) {
|
|
11229
|
+
await this.machine.collectFee(from, feePayments.payments, feePayments.burned);
|
|
11230
|
+
}
|
|
11231
|
+
let result = await this.machine.execute(to, method, params, from);
|
|
11068
11232
|
globalThis.pubsub.publish(`transaction.completed.${hash}`, { status: "fulfilled", hash });
|
|
11069
11233
|
return result || "no state change";
|
|
11070
11234
|
} catch (error) {
|
|
@@ -11111,13 +11275,7 @@ class Chain extends VersionControl {
|
|
|
11111
11275
|
}
|
|
11112
11276
|
console.log(`[chain] \u2705 Block data integrity verified: ${hash}`);
|
|
11113
11277
|
await this.#validateBlockValidators(blockMessage);
|
|
11114
|
-
const transactions = await
|
|
11115
|
-
blockMessage.decoded.transactions.map(async (hash2) => {
|
|
11116
|
-
const data = await peernet.get(hash2, "transaction");
|
|
11117
|
-
return new TransactionMessage(data);
|
|
11118
|
-
})
|
|
11119
|
-
);
|
|
11120
|
-
await Promise.all(transactions.map((transaction) => this.validateTransactionSignature(transaction)));
|
|
11278
|
+
const transactions = await this.#resolveBlockTransactions(blockMessage);
|
|
11121
11279
|
await Promise.all(
|
|
11122
11280
|
blockMessage.decoded.transactions.map(async (transactionHash) => {
|
|
11123
11281
|
if (await transactionPoolStore.has(transactionHash)) await transactionPoolStore.delete(transactionHash);
|
|
@@ -11142,7 +11300,10 @@ class Chain extends VersionControl {
|
|
|
11142
11300
|
for (const transaction of allTransactions) {
|
|
11143
11301
|
if (!contracts.includes(transaction.decoded.to)) contracts.push(transaction.decoded.to);
|
|
11144
11302
|
this.removePendingNonce(transaction.decoded.from, transaction.decoded.nonce);
|
|
11145
|
-
await
|
|
11303
|
+
const transactionHash = await transaction.hash();
|
|
11304
|
+
const validatorAddresses = blockMessage.decoded.validators.map(({ address }) => address);
|
|
11305
|
+
const feeDistribution = supportsTransactionFees(blockMessage.decoded.protocolVersion) ? distributeTransactionFee(BigInt(await calculateFee(transaction.decoded)), transactionHash, validatorAddresses) : { payments: [], burned: 0n };
|
|
11306
|
+
await this.#handleTransaction(transaction, [], void 0, feeDistribution);
|
|
11146
11307
|
}
|
|
11147
11308
|
try {
|
|
11148
11309
|
promises = await Promise.allSettled(promises);
|
|
@@ -11199,7 +11360,7 @@ class Chain extends VersionControl {
|
|
|
11199
11360
|
if (await this.hasTransactionToHandle() && !this.#runningEpoch && this.#participating) await this.#runEpoch();
|
|
11200
11361
|
return true;
|
|
11201
11362
|
}
|
|
11202
|
-
async #handleTransaction(transaction, latestTransactions, block) {
|
|
11363
|
+
async #handleTransaction(transaction, latestTransactions, block, feePayments = { payments: [], burned: 0n }) {
|
|
11203
11364
|
await this.validateTransactionSignature(transaction);
|
|
11204
11365
|
const hash = await transaction.hash();
|
|
11205
11366
|
const doubleTransactions = [];
|
|
@@ -11212,7 +11373,7 @@ class Chain extends VersionControl {
|
|
|
11212
11373
|
return;
|
|
11213
11374
|
}
|
|
11214
11375
|
try {
|
|
11215
|
-
const result = await this.#executeTransaction({ ...transaction.decoded, hash });
|
|
11376
|
+
const result = await this.#executeTransaction({ ...transaction.decoded, hash, feePayments });
|
|
11216
11377
|
if (block) {
|
|
11217
11378
|
block.transactions.push(hash);
|
|
11218
11379
|
block.fees = block.fees += await calculateFee(transaction.decoded);
|
|
@@ -11243,7 +11404,7 @@ class Chain extends VersionControl {
|
|
|
11243
11404
|
fees: BigInt(0),
|
|
11244
11405
|
timestamp,
|
|
11245
11406
|
previousHash: "",
|
|
11246
|
-
reward:
|
|
11407
|
+
reward: BLOCK_REWARD,
|
|
11247
11408
|
index: 0,
|
|
11248
11409
|
producer: "",
|
|
11249
11410
|
producerProof: "",
|
|
@@ -11268,7 +11429,7 @@ class Chain extends VersionControl {
|
|
|
11268
11429
|
for (const { transaction, hash } of allTransactions) {
|
|
11269
11430
|
await this.validateTransactionSignature(transaction);
|
|
11270
11431
|
block.transactions.push(hash);
|
|
11271
|
-
block.fees += BigInt(await calculateFee(transaction.decoded));
|
|
11432
|
+
if (supportsTransactionFees(this.version)) block.fees += BigInt(await calculateFee(transaction.decoded));
|
|
11272
11433
|
await globalThis.peernet.put(hash, transaction.encoded, "transaction");
|
|
11273
11434
|
}
|
|
11274
11435
|
if (block.transactions.length === 0) return;
|
|
@@ -11278,9 +11439,17 @@ class Chain extends VersionControl {
|
|
|
11278
11439
|
const canonicalValidators = await this.staticCall(addresses.validators, "validators");
|
|
11279
11440
|
const sortedValidators = [...canonicalValidators].sort();
|
|
11280
11441
|
if (sortedValidators.length === 0) throw new Error("cannot produce a block without validators");
|
|
11442
|
+
if (supportsTransactionFees(this.version)) await this.#assertFeeBurnSupported();
|
|
11443
|
+
const feeEntries = supportsTransactionFees(this.version) ? await Promise.all(
|
|
11444
|
+
allTransactions.map(async ({ transaction, hash }) => ({
|
|
11445
|
+
fee: BigInt(await calculateFee(transaction.decoded)),
|
|
11446
|
+
transactionHash: hash
|
|
11447
|
+
}))
|
|
11448
|
+
) : [];
|
|
11449
|
+
const validatorFees = supportsTransactionFees(this.version) ? aggregateValidatorFees(feeEntries, sortedValidators) : new Map(sortedValidators.map((address) => [address, 0n]));
|
|
11281
11450
|
block.validators = sortedValidators.map((validatorAddress) => ({
|
|
11282
11451
|
address: validatorAddress,
|
|
11283
|
-
reward:
|
|
11452
|
+
reward: (validatorFees.get(validatorAddress) || 0n) + block.reward / BigInt(sortedValidators.length)
|
|
11284
11453
|
}));
|
|
11285
11454
|
try {
|
|
11286
11455
|
block.producer = globalThis.peernet.selectedAccount || "";
|
|
@@ -11405,7 +11574,7 @@ class Chain extends VersionControl {
|
|
|
11405
11574
|
* @returns
|
|
11406
11575
|
*/
|
|
11407
11576
|
internalCall(sender, contract, method, parameters) {
|
|
11408
|
-
return this.machine.execute(contract, method, parameters);
|
|
11577
|
+
return this.machine.execute(contract, method, parameters, sender);
|
|
11409
11578
|
}
|
|
11410
11579
|
/**
|
|
11411
11580
|
*
|
|
@@ -11415,7 +11584,7 @@ class Chain extends VersionControl {
|
|
|
11415
11584
|
* @returns
|
|
11416
11585
|
*/
|
|
11417
11586
|
call(contract, method, parameters) {
|
|
11418
|
-
return this.machine.execute(contract, method, parameters);
|
|
11587
|
+
return this.machine.execute(contract, method, parameters, globalThis.peernet.selectedAccount);
|
|
11419
11588
|
}
|
|
11420
11589
|
staticCall(contract, method, parameters) {
|
|
11421
11590
|
return this.machine.get(contract, method, parameters);
|