@leofcoin/chain 1.10.16 → 1.10.18
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 +6 -6
- package/exports/beacon-epoch.js +1 -1
- package/exports/beacon-lifecycle.js +1 -1
- package/exports/beacon-round.js +1 -1
- package/exports/beacon.js +15 -14
- package/exports/browser/_commonjsHelpers-B85MJLTf.js +5 -0
- package/exports/browser/beacon-envelope.js +61 -150
- package/exports/browser/beacon-epoch.js +2 -2
- package/exports/browser/beacon-lifecycle.js +3 -3
- package/exports/browser/beacon-round.js +2 -2
- package/exports/browser/beacon-wire.js +3 -3
- package/exports/browser/beacon.js +2483 -1228
- package/exports/browser/{browser-CCfR9KOM-CvhDZrGu.js → browser-CCfR9KOM-CpStHgTj.js} +4 -4
- package/exports/browser/{browser-crypto-CeE-yId5.js → browser-crypto-CH0ua22L.js} +1 -1
- package/exports/browser/chain.js +151 -81
- package/exports/browser/{client-CAC7RVMf-Dmz_2A_5.js → client-C_pBoi1g-_pfKzCvX.js} +256 -80
- package/exports/browser/{connection-monitor-Cmv00mwl.js → connection-monitor-D-krNiRH.js} +1 -1
- package/exports/browser/{hkdf-CnZvwpZ9.js → hkdf-BUHINPFj.js} +2 -94
- package/exports/browser/{index-BC-KRBgE.js → index-DmuI-29u.js} +1 -1
- package/exports/browser/{index-dTQVgkpf-CuHJWO2l.js → index-dTQVgkpf-BbdshWB0.js} +4 -4
- package/exports/browser/index.browser-CFfJpgKV.js +857 -0
- package/exports/browser/{messages-Dw86z0zq-DrPw_nlZ.js → messages-BGHaihSi-eHZVl0y9.js} +4 -4
- package/exports/browser/{node-browser-DWSdi-XP.js → node-browser-BNe-en1l.js} +5 -5
- package/exports/browser/node-browser.js +4 -4
- package/exports/browser/{sha2-piimb6xL.js → sha2-BKcEJzgd.js} +144 -3
- package/exports/browser/weierstrass-BVfhtHbl.js +3596 -0
- package/exports/browser/workers/machine-worker.js +61 -19
- package/exports/chain.js +146 -73
- package/exports/workers/machine-worker.js +61 -19
- package/package.json +14 -13
- package/exports/browser/weierstrass-Q0QND1ZT.js +0 -2156
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { g as getDefaultExportFromCjs } from './node-browser-
|
|
2
|
-
import './browser-crypto-
|
|
1
|
+
import { g as getDefaultExportFromCjs } from './node-browser-BNe-en1l.js';
|
|
2
|
+
import './browser-crypto-CH0ua22L.js';
|
|
3
3
|
import './index-CvKt4UDE.js';
|
|
4
|
-
import './sha2-
|
|
5
|
-
import './connection-monitor-
|
|
4
|
+
import './sha2-BKcEJzgd.js';
|
|
5
|
+
import './connection-monitor-D-krNiRH.js';
|
|
6
6
|
|
|
7
7
|
function _mergeNamespaces$1(n, m) {
|
|
8
8
|
m.forEach(function (e) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { a as index$3, b as base58$1, i as index$4, f as fromBase58, c as fromHex, d as fromArrayLike, e as toBase32, t as toBase58, g as toHex } from './index-CvKt4UDE.js';
|
|
2
|
-
import { s as sha256 } from './sha2-
|
|
2
|
+
import { s as sha256 } from './sha2-BKcEJzgd.js';
|
|
3
3
|
|
|
4
4
|
var isHex = string => /^[A-F0-9]+$/i.test(
|
|
5
5
|
string.startsWith('0x') ?
|
package/exports/browser/chain.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { F as FormatInterface, p as proto, a as proto$1, b as proto$2, T as TransactionMessage, j as jsonStringifyBigInt, c as jsonParseBigInt, C as ContractMessage, d as Codec } from './browser-crypto-
|
|
2
|
-
import { B as BlockMessage, L as LastBlockMessage, P as PROTOCOL_VERSION, R as REACHED_ONE_ZERO_ZERO, C as ConnectionMonitor, a as BWMessage, S as StateMessage } from './connection-monitor-
|
|
3
|
-
import { M as MAX_TRANSACTION_BYTES, a as MAX_BLOCK_TRANSACTION_BYTES, b as MAX_BLOCK_TRANSACTIONS, c as MultiWallet, d as createTransactionHash, e as calculateFee, f as createContractMessage, s as signTransaction, r as randombytes, g as distributeTransactionFee, h as supportsTransactionFees, i as supportsMonetaryPolicy, v as validateBlockResourceLimits, j as calculateMonetaryPolicy, k as distributeAmount, l as aggregateValidatorFees, m as MONETARY_POLICY_AUTHORITY, n as contractFactoryMessage, o as nativeTokenMessage, p as validatorsMessage, q as nameServiceMessage, t as parseUnits, u as formatUnits } from './index-
|
|
1
|
+
import { F as FormatInterface, p as proto, a as proto$1, b as proto$2, T as TransactionMessage, j as jsonStringifyBigInt, c as jsonParseBigInt, C as ContractMessage, d as Codec } from './browser-crypto-CH0ua22L.js';
|
|
2
|
+
import { B as BlockMessage, L as LastBlockMessage, P as PROTOCOL_VERSION, R as REACHED_ONE_ZERO_ZERO, C as ConnectionMonitor, a as BWMessage, S as StateMessage } from './connection-monitor-D-krNiRH.js';
|
|
3
|
+
import { M as MAX_TRANSACTION_BYTES, a as MAX_BLOCK_TRANSACTION_BYTES, b as MAX_BLOCK_TRANSACTIONS, c as MultiWallet, d as createTransactionHash, e as calculateFee, f as createContractMessage, s as signTransaction, r as randombytes, g as distributeTransactionFee, h as supportsTransactionFees, i as supportsMonetaryPolicy, v as validateBlockResourceLimits, j as calculateMonetaryPolicy, k as distributeAmount, l as aggregateValidatorFees, m as MONETARY_POLICY_AUTHORITY, n as contractFactoryMessage, o as nativeTokenMessage, p as validatorsMessage, q as nameServiceMessage, t as parseUnits, u as formatUnits } from './index-DmuI-29u.js';
|
|
4
4
|
import { f as fromBase58 } from './index-CvKt4UDE.js';
|
|
5
|
+
import { g as getDefaultExportFromCjs } from './_commonjsHelpers-B85MJLTf.js';
|
|
5
6
|
import { q as quorumThreshold } from './quorum-S_qdgiAY.js';
|
|
6
|
-
import './sha2-
|
|
7
|
+
import './sha2-BKcEJzgd.js';
|
|
7
8
|
|
|
8
9
|
const getTargets = () => Array.from([]);
|
|
9
10
|
const isEnabled = (target) => {
|
|
@@ -98,10 +99,6 @@ var addresses = {
|
|
|
98
99
|
validators
|
|
99
100
|
};
|
|
100
101
|
|
|
101
|
-
function getDefaultExportFromCjs (x) {
|
|
102
|
-
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
102
|
var re = {exports: {}};
|
|
106
103
|
|
|
107
104
|
var constants;
|
|
@@ -3699,6 +3696,12 @@ class Machine {
|
|
|
3699
3696
|
pubsub.publish(data.id, { error: data.message });
|
|
3700
3697
|
break;
|
|
3701
3698
|
}
|
|
3699
|
+
case "addLoadedBlockError":
|
|
3700
|
+
case "collectFeeError":
|
|
3701
|
+
case "settleRewardsError": {
|
|
3702
|
+
pubsub.publish(data.id, { error: new Error(data.message) });
|
|
3703
|
+
break;
|
|
3704
|
+
}
|
|
3702
3705
|
case "debug": {
|
|
3703
3706
|
if (data.message.includes("loaded transactions for block:")) {
|
|
3704
3707
|
pubsub.publish("block-loaded", data.message.replace("loaded transactions for block: ", "").split(" @")[0]);
|
|
@@ -3748,9 +3751,9 @@ class Machine {
|
|
|
3748
3751
|
}
|
|
3749
3752
|
}
|
|
3750
3753
|
}
|
|
3751
|
-
async updateState() {
|
|
3754
|
+
async updateState(strict = false) {
|
|
3752
3755
|
try {
|
|
3753
|
-
const lastBlock = await this.lastBlock;
|
|
3756
|
+
const lastBlock = strict ? await this.getCanonicalTip() : await this.lastBlock;
|
|
3754
3757
|
const lastBlockIndex = Number(lastBlock.index);
|
|
3755
3758
|
const statesLastBlockIndex = Number(this.states.lastBlock.index);
|
|
3756
3759
|
if (lastBlockIndex > statesLastBlockIndex || lastBlock.hash !== this.states.lastBlock.hash && Number(lastBlockIndex) === 0) {
|
|
@@ -3809,7 +3812,7 @@ class Machine {
|
|
|
3809
3812
|
return JSON.stringify(sorted, replacer);
|
|
3810
3813
|
};
|
|
3811
3814
|
const tasks = [
|
|
3812
|
-
stateStore.put("lastBlock", sortedStringify(
|
|
3815
|
+
stateStore.put("lastBlock", sortedStringify(lastBlock, jsonStringifyBigInt)),
|
|
3813
3816
|
stateStore.put("states", sortedStringify(state, jsonStringifyBigInt)),
|
|
3814
3817
|
stateStore.put("accounts", sortedStringify(accounts, jsonStringifyBigInt)),
|
|
3815
3818
|
stateStore.put(
|
|
@@ -3836,6 +3839,7 @@ class Machine {
|
|
|
3836
3839
|
}
|
|
3837
3840
|
} catch (error) {
|
|
3838
3841
|
console.error(error);
|
|
3842
|
+
if (strict) throw error;
|
|
3839
3843
|
}
|
|
3840
3844
|
}
|
|
3841
3845
|
async #init(blocks) {
|
|
@@ -4102,6 +4106,9 @@ ${error.message}`);
|
|
|
4102
4106
|
return this.states.lastBlock;
|
|
4103
4107
|
});
|
|
4104
4108
|
}
|
|
4109
|
+
getCanonicalTip() {
|
|
4110
|
+
return this.#askWorker("lastBlock");
|
|
4111
|
+
}
|
|
4105
4112
|
get lastBlockHeight() {
|
|
4106
4113
|
return this.#askWorker("lastBlockHeight").catch((error) => {
|
|
4107
4114
|
debug$2("lastBlockHeight fallback after worker timeout:", error?.message ?? error);
|
|
@@ -4246,6 +4253,56 @@ const decodeResponsePayload = async (result, depth = 0) => {
|
|
|
4246
4253
|
return result;
|
|
4247
4254
|
};
|
|
4248
4255
|
|
|
4256
|
+
class CanonicalStateQueue {
|
|
4257
|
+
#tail = Promise.resolve();
|
|
4258
|
+
#failed = false;
|
|
4259
|
+
#failure;
|
|
4260
|
+
run(operation) {
|
|
4261
|
+
const result = this.#tail.then(async () => {
|
|
4262
|
+
if (this.#failed) {
|
|
4263
|
+
throw new Error("Canonical state application halted after a failed mutation; restart and recover state", {
|
|
4264
|
+
cause: this.#failure
|
|
4265
|
+
});
|
|
4266
|
+
}
|
|
4267
|
+
let mutating = false;
|
|
4268
|
+
try {
|
|
4269
|
+
return await operation({ markMutating: () => {
|
|
4270
|
+
mutating = true;
|
|
4271
|
+
} });
|
|
4272
|
+
} catch (error) {
|
|
4273
|
+
if (mutating) {
|
|
4274
|
+
this.#failed = true;
|
|
4275
|
+
this.#failure = error;
|
|
4276
|
+
}
|
|
4277
|
+
throw error;
|
|
4278
|
+
}
|
|
4279
|
+
});
|
|
4280
|
+
this.#tail = result.then(() => void 0, () => void 0);
|
|
4281
|
+
return result;
|
|
4282
|
+
}
|
|
4283
|
+
}
|
|
4284
|
+
|
|
4285
|
+
const validateChainLink = (localTip, incoming) => {
|
|
4286
|
+
const localHash = localTip?.hash || "0x0";
|
|
4287
|
+
const localIndex = Number(localTip?.index ?? -1);
|
|
4288
|
+
const hasCanonicalTip = localHash !== "0x0";
|
|
4289
|
+
if (hasCanonicalTip && incoming.index < localIndex) return "stale";
|
|
4290
|
+
if (hasCanonicalTip && incoming.index === localIndex) {
|
|
4291
|
+
if (incoming.hash === localHash) return "duplicate";
|
|
4292
|
+
throw new Error(`Block conflict detected at index ${incoming.index}`);
|
|
4293
|
+
}
|
|
4294
|
+
const expectedIndex = hasCanonicalTip ? localIndex + 1 : 0;
|
|
4295
|
+
if (incoming.index !== expectedIndex) {
|
|
4296
|
+
throw new Error(`Unexpected block index ${incoming.index}; expected ${expectedIndex}`);
|
|
4297
|
+
}
|
|
4298
|
+
if (incoming.previousHash !== localHash) {
|
|
4299
|
+
throw new Error(
|
|
4300
|
+
`previousHash mismatch at index ${incoming.index}: expected ${localHash}, got ${incoming.previousHash}`
|
|
4301
|
+
);
|
|
4302
|
+
}
|
|
4303
|
+
return "append";
|
|
4304
|
+
};
|
|
4305
|
+
|
|
4249
4306
|
const debug$1 = createDebugger("leofcoin/state");
|
|
4250
4307
|
class State extends Contract {
|
|
4251
4308
|
constructor(config) {
|
|
@@ -4260,6 +4317,7 @@ class State extends Contract {
|
|
|
4260
4317
|
this.#blocks = [];
|
|
4261
4318
|
this.knownBlocks = [];
|
|
4262
4319
|
this.#totalSize = 0;
|
|
4320
|
+
this.#canonicalStateQueue = new CanonicalStateQueue();
|
|
4263
4321
|
this.#loaded = false;
|
|
4264
4322
|
this.#resolvingHashes = /* @__PURE__ */ new Set();
|
|
4265
4323
|
this._wantList = [];
|
|
@@ -4302,6 +4360,13 @@ class State extends Contract {
|
|
|
4302
4360
|
#blocks;
|
|
4303
4361
|
#totalSize;
|
|
4304
4362
|
#machine;
|
|
4363
|
+
#canonicalStateQueue;
|
|
4364
|
+
withCanonicalState(operation) {
|
|
4365
|
+
return this.#canonicalStateQueue.run(operation);
|
|
4366
|
+
}
|
|
4367
|
+
canonicalTip() {
|
|
4368
|
+
return this.#machine ? this.#machine.getCanonicalTip() : Promise.resolve({ index: -1, hash: "0x0", previousHash: "0x0" });
|
|
4369
|
+
}
|
|
4305
4370
|
#loaded;
|
|
4306
4371
|
#resolvingHashes;
|
|
4307
4372
|
/**
|
|
@@ -4454,7 +4519,7 @@ class State extends Contract {
|
|
|
4454
4519
|
this.#machine = machine;
|
|
4455
4520
|
const lastBlock = await this.#machine.lastBlock;
|
|
4456
4521
|
if (lastBlock.hash !== "0x0") {
|
|
4457
|
-
this.updateState(new BlockMessage(lastBlock));
|
|
4522
|
+
await this.updateState(new BlockMessage(lastBlock));
|
|
4458
4523
|
}
|
|
4459
4524
|
this.#loaded = true;
|
|
4460
4525
|
this.#chainState = "loaded";
|
|
@@ -4477,9 +4542,10 @@ class State extends Contract {
|
|
|
4477
4542
|
await machine.ready;
|
|
4478
4543
|
this.#machine = machine;
|
|
4479
4544
|
}
|
|
4480
|
-
await this.#machine.updateState();
|
|
4545
|
+
await this.#machine.updateState(true);
|
|
4481
4546
|
} catch (error) {
|
|
4482
4547
|
console.error(error);
|
|
4548
|
+
throw error;
|
|
4483
4549
|
}
|
|
4484
4550
|
}
|
|
4485
4551
|
getLatestBlock() {
|
|
@@ -4668,11 +4734,16 @@ class State extends Contract {
|
|
|
4668
4734
|
console.error(error);
|
|
4669
4735
|
}
|
|
4670
4736
|
if (!lastBlock) lastBlock = await this.#getLatestBlock();
|
|
4671
|
-
if (globalThis.peernet.peers.length === 0)
|
|
4737
|
+
if (globalThis.peernet.peers.length === 0) {
|
|
4738
|
+
this.#chainSyncing = false;
|
|
4739
|
+
this.#syncState = "connectionless";
|
|
4740
|
+
return this.#syncState;
|
|
4741
|
+
}
|
|
4672
4742
|
try {
|
|
4673
|
-
await this.#syncChain(lastBlock);
|
|
4743
|
+
await this.withCanonicalState((transition) => this.#syncChain(lastBlock, transition));
|
|
4674
4744
|
} catch (error) {
|
|
4675
4745
|
this.#syncErrorCount += 1;
|
|
4746
|
+
this.#chainSyncing = false;
|
|
4676
4747
|
if (this.#syncErrorCount < 3) return this.syncChain(lastBlock);
|
|
4677
4748
|
this.#syncErrorCount = 0;
|
|
4678
4749
|
this.#chainSyncing = false;
|
|
@@ -4686,9 +4757,9 @@ class State extends Contract {
|
|
|
4686
4757
|
this.#syncState = "synced";
|
|
4687
4758
|
return this.#syncState;
|
|
4688
4759
|
}
|
|
4689
|
-
async #syncChain(lastBlock) {
|
|
4760
|
+
async #syncChain(lastBlock, transition) {
|
|
4690
4761
|
try {
|
|
4691
|
-
const localBlock = await this.
|
|
4762
|
+
const localBlock = await this.canonicalTip();
|
|
4692
4763
|
const localIndex = localBlock ? Number(localBlock.index) : -1;
|
|
4693
4764
|
const remoteIndex = Number(lastBlock.index);
|
|
4694
4765
|
const remoteBlockHash = lastBlock.hash;
|
|
@@ -4710,6 +4781,9 @@ class State extends Contract {
|
|
|
4710
4781
|
debug$1(`Remote block hash is 0x0, skipping sync`);
|
|
4711
4782
|
return;
|
|
4712
4783
|
}
|
|
4784
|
+
if (localBlock?.hash !== "0x0" && localIndex === remoteIndex && localBlock.hash !== remoteBlockHash) {
|
|
4785
|
+
throw new Error(`Block conflict detected at index ${remoteIndex}`);
|
|
4786
|
+
}
|
|
4713
4787
|
if (localIndex > remoteIndex) {
|
|
4714
4788
|
const MAX_REORG_DEPTH = 6;
|
|
4715
4789
|
const reorgDepth = localIndex - remoteIndex;
|
|
@@ -4749,9 +4823,14 @@ class State extends Contract {
|
|
|
4749
4823
|
debug$1(`Loading blocks from index ${localIndex + 1} to ${remoteIndex}`);
|
|
4750
4824
|
const start = localIndex + 1;
|
|
4751
4825
|
if (this.#machine && blocks.length > start) {
|
|
4752
|
-
await this.#loadBlocks(blocks.slice(start));
|
|
4826
|
+
await this.#loadBlocks(blocks.slice(start), transition);
|
|
4753
4827
|
}
|
|
4754
4828
|
if (blocks.length > 0) {
|
|
4829
|
+
const appliedTip = await this.canonicalTip();
|
|
4830
|
+
if (appliedTip.hash !== remoteBlockHash || Number(appliedTip.index) !== remoteIndex) {
|
|
4831
|
+
throw new Error(`Sync did not apply canonical target ${remoteBlockHash} at index ${remoteIndex}`);
|
|
4832
|
+
}
|
|
4833
|
+
transition.markMutating();
|
|
4755
4834
|
await this.updateState(new BlockMessage(blocks[blocks.length - 1]));
|
|
4756
4835
|
}
|
|
4757
4836
|
} else {
|
|
@@ -4871,14 +4950,14 @@ class State extends Contract {
|
|
|
4871
4950
|
console.log(error);
|
|
4872
4951
|
}
|
|
4873
4952
|
console.log(error);
|
|
4874
|
-
|
|
4953
|
+
throw error;
|
|
4875
4954
|
}
|
|
4876
4955
|
}
|
|
4877
4956
|
/**
|
|
4878
4957
|
*
|
|
4879
4958
|
* @param {Block[]} blocks
|
|
4880
4959
|
*/
|
|
4881
|
-
async #loadBlocks(blocks) {
|
|
4960
|
+
async #loadBlocks(blocks, transition) {
|
|
4882
4961
|
this.#chainState = "loading";
|
|
4883
4962
|
if (blocks.some((block) => !block)) {
|
|
4884
4963
|
throw new Error("missing block data during load; chain resolution incomplete");
|
|
@@ -4889,10 +4968,21 @@ class State extends Contract {
|
|
|
4889
4968
|
for (const block of blocks) {
|
|
4890
4969
|
if (block && !block.loaded) {
|
|
4891
4970
|
try {
|
|
4971
|
+
const link = validateChainLink(await this.canonicalTip(), {
|
|
4972
|
+
index: Number(block.index),
|
|
4973
|
+
hash: block.hash,
|
|
4974
|
+
previousHash: String(block.previousHash)
|
|
4975
|
+
});
|
|
4976
|
+
if (link !== "append") {
|
|
4977
|
+
this.#blocks[block.index].loaded = true;
|
|
4978
|
+
debug$1(`Ignoring ${link} loaded block ${block.hash} at height ${block.index}`);
|
|
4979
|
+
continue;
|
|
4980
|
+
}
|
|
4892
4981
|
debug$1(`loading block: ${Number(block.index)} ${block.hash}`);
|
|
4893
4982
|
let transactions = await this.#loadBlockTransactions(block.transactions || []);
|
|
4894
4983
|
const validators = block.validators.map(({ address }) => address);
|
|
4895
4984
|
debug$1(`loading transactions: ${transactions.length} for block ${block.index}`);
|
|
4985
|
+
transition.markMutating();
|
|
4896
4986
|
for (const transaction of transactions) {
|
|
4897
4987
|
const hash = await transaction.hash();
|
|
4898
4988
|
if (poolTransactionKeys.has(hash)) await globalThis.transactionPoolStore.delete(hash);
|
|
@@ -4925,10 +5015,11 @@ class State extends Contract {
|
|
|
4925
5015
|
...distributeAmount(policy.subsidy, validators, Number(block.index) % validators.length)
|
|
4926
5016
|
]);
|
|
4927
5017
|
}
|
|
4928
|
-
this.#blocks[block.index].loaded = true;
|
|
4929
5018
|
debug$1(`executed transactions for block ${block.index}`);
|
|
5019
|
+
const registered = await this.#machine.addLoadedBlock({ ...block, loaded: true });
|
|
5020
|
+
if (registered !== true) throw new Error(`Loaded block registration failed after execution: ${block.hash}`);
|
|
5021
|
+
this.#blocks[block.index].loaded = true;
|
|
4930
5022
|
if (Number(block.index) === 0) this.#loaded = true;
|
|
4931
|
-
await this.#machine.addLoadedBlock(block);
|
|
4932
5023
|
debug$1(`loaded block: ${block.hash} @${Number(block.index)}`);
|
|
4933
5024
|
globalThis.pubsub.publish("block-loaded", { ...block });
|
|
4934
5025
|
} catch (error) {
|
|
@@ -4936,6 +5027,7 @@ class State extends Contract {
|
|
|
4936
5027
|
for (const transaction of block.transactions) {
|
|
4937
5028
|
this.wantList.push(transaction);
|
|
4938
5029
|
}
|
|
5030
|
+
throw error;
|
|
4939
5031
|
}
|
|
4940
5032
|
}
|
|
4941
5033
|
}
|
|
@@ -5049,27 +5141,6 @@ class VersionControl extends State {
|
|
|
5049
5141
|
}
|
|
5050
5142
|
}
|
|
5051
5143
|
|
|
5052
|
-
const validateChainLink = (localTip, incoming) => {
|
|
5053
|
-
const localHash = localTip?.hash || "0x0";
|
|
5054
|
-
const localIndex = Number(localTip?.index ?? -1);
|
|
5055
|
-
const hasCanonicalTip = localHash !== "0x0";
|
|
5056
|
-
if (hasCanonicalTip && incoming.index < localIndex) return "stale";
|
|
5057
|
-
if (hasCanonicalTip && incoming.index === localIndex) {
|
|
5058
|
-
if (incoming.hash === localHash) return "duplicate";
|
|
5059
|
-
throw new Error(`Block conflict detected at index ${incoming.index}`);
|
|
5060
|
-
}
|
|
5061
|
-
const expectedIndex = hasCanonicalTip ? localIndex + 1 : 0;
|
|
5062
|
-
if (incoming.index !== expectedIndex) {
|
|
5063
|
-
throw new Error(`Unexpected block index ${incoming.index}; expected ${expectedIndex}`);
|
|
5064
|
-
}
|
|
5065
|
-
if (incoming.previousHash !== localHash) {
|
|
5066
|
-
throw new Error(
|
|
5067
|
-
`previousHash mismatch at index ${incoming.index}: expected ${localHash}, got ${incoming.previousHash}`
|
|
5068
|
-
);
|
|
5069
|
-
}
|
|
5070
|
-
return "append";
|
|
5071
|
-
};
|
|
5072
|
-
|
|
5073
5144
|
const validateCanonicalValidatorSet = (blockIndex, expectedValidators, validators) => {
|
|
5074
5145
|
const actualValidators = validators.map(({ address }) => address);
|
|
5075
5146
|
const canonicalExpected = [...new Set(expectedValidators)].sort();
|
|
@@ -5645,7 +5716,9 @@ class Chain extends VersionControl {
|
|
|
5645
5716
|
`[consensus] Epoch boundary at block ${blockHeight}: new epoch starts at height ${this.#currentEpochStartHeight}`
|
|
5646
5717
|
);
|
|
5647
5718
|
if (this.#participating && !this.#runningEpoch) {
|
|
5648
|
-
|
|
5719
|
+
void this.#runEpoch().catch((error) => {
|
|
5720
|
+
debug(`epoch scheduling failed: ${error?.message ?? error}`);
|
|
5721
|
+
});
|
|
5649
5722
|
}
|
|
5650
5723
|
}
|
|
5651
5724
|
async #runEpoch() {
|
|
@@ -6114,11 +6187,14 @@ class Chain extends VersionControl {
|
|
|
6114
6187
|
}
|
|
6115
6188
|
}
|
|
6116
6189
|
async #addBlock(block) {
|
|
6190
|
+
return this.withCanonicalState((transition) => this.#applyBlock(block, transition));
|
|
6191
|
+
}
|
|
6192
|
+
async #applyBlock(block, transition) {
|
|
6117
6193
|
const receivedEncoded = block instanceof BlockMessage ? block.encoded : block;
|
|
6118
6194
|
const blockMessage = await new BlockMessage(block);
|
|
6119
6195
|
const hash = await blockMessage.hash();
|
|
6120
6196
|
const blockIndex = Number(blockMessage.decoded.index);
|
|
6121
|
-
const linkResult = validateChainLink(await this.
|
|
6197
|
+
const linkResult = validateChainLink(await this.canonicalTip(), {
|
|
6122
6198
|
index: blockIndex,
|
|
6123
6199
|
hash,
|
|
6124
6200
|
previousHash: String(blockMessage.decoded.previousHash)
|
|
@@ -6147,6 +6223,7 @@ class Chain extends VersionControl {
|
|
|
6147
6223
|
console.log(`[chain] \u2705 Block data integrity verified: ${hash}`);
|
|
6148
6224
|
await this.#validateBlockValidators(blockMessage);
|
|
6149
6225
|
const transactions = await this.#resolveBlockTransactions(blockMessage);
|
|
6226
|
+
transition.markMutating();
|
|
6150
6227
|
await Promise.all(
|
|
6151
6228
|
blockMessage.decoded.transactions.map(async (transactionHash) => {
|
|
6152
6229
|
if (await transactionPoolStore.has(transactionHash)) await transactionPoolStore.delete(transactionHash);
|
|
@@ -6157,9 +6234,6 @@ class Chain extends VersionControl {
|
|
|
6157
6234
|
hash,
|
|
6158
6235
|
...blockMessage.decoded
|
|
6159
6236
|
};
|
|
6160
|
-
debug(`added block: ${hash}`);
|
|
6161
|
-
let promises = [];
|
|
6162
|
-
let contracts = [];
|
|
6163
6237
|
const allTransactions = transactions.sort((a, b) => {
|
|
6164
6238
|
if (a.decoded.priority !== b.decoded.priority) {
|
|
6165
6239
|
return (b.decoded.priority ? 1 : 0) - (a.decoded.priority ? 1 : 0);
|
|
@@ -6170,7 +6244,6 @@ class Chain extends VersionControl {
|
|
|
6170
6244
|
});
|
|
6171
6245
|
const monetaryPolicy = await this.#monetaryPolicy(blockMessage.decoded.protocolVersion);
|
|
6172
6246
|
for (const transaction of allTransactions) {
|
|
6173
|
-
if (!contracts.includes(transaction.decoded.to)) contracts.push(transaction.decoded.to);
|
|
6174
6247
|
this.removePendingNonce(transaction.decoded.from, transaction.decoded.nonce);
|
|
6175
6248
|
const transactionHash = await transaction.hash();
|
|
6176
6249
|
const validatorAddresses = blockMessage.decoded.validators.map(({ address }) => address);
|
|
@@ -6188,36 +6261,36 @@ class Chain extends VersionControl {
|
|
|
6188
6261
|
...distributeAmount(monetaryPolicy.subsidy, validators, blockIndex % validators.length)
|
|
6189
6262
|
]);
|
|
6190
6263
|
}
|
|
6191
|
-
|
|
6192
|
-
|
|
6193
|
-
|
|
6194
|
-
|
|
6195
|
-
globalThis.pubsub.publish("transaction-processed", transaction.encoded);
|
|
6196
|
-
|
|
6197
|
-
|
|
6198
|
-
if (!noncesByAddress[transaction.decoded.from] || noncesByAddress?.[transaction.decoded.from] < transaction.decoded.nonce) {
|
|
6199
|
-
noncesByAddress[transaction.decoded.from] = transaction.decoded.nonce;
|
|
6200
|
-
}
|
|
6201
|
-
}
|
|
6202
|
-
await Promise.all(
|
|
6203
|
-
Object.entries(noncesByAddress).map(([from, nonce]) => globalThis.accountsStore.put(from, String(nonce)))
|
|
6204
|
-
);
|
|
6205
|
-
if ((await this.lastBlock).index < Number(blockMessage.decoded.index)) {
|
|
6206
|
-
await this.machine.addLoadedBlock({ ...blockMessage.decoded, loaded: true, hash: await blockMessage.hash() });
|
|
6207
|
-
try {
|
|
6208
|
-
await this.call(addresses.validators, "recordValidatorSnapshot", [blockMessage.decoded.index]);
|
|
6209
|
-
} catch (snapshotError) {
|
|
6210
|
-
debug(`failed to record validator snapshot: ${snapshotError?.message ?? snapshotError}`);
|
|
6211
|
-
}
|
|
6212
|
-
await this.#handleEpochBoundary(Number(blockMessage.decoded.index));
|
|
6213
|
-
await this.updateState(blockMessage);
|
|
6264
|
+
const noncesByAddress = {};
|
|
6265
|
+
for (let transaction of transactions) {
|
|
6266
|
+
globalThis.pubsub.publish("transaction-processed", transaction.encoded);
|
|
6267
|
+
if (transaction.decoded.to === globalThis.peernet.selectedAccount)
|
|
6268
|
+
globalThis.pubsub.publish("account-transaction-processed", transaction.encoded);
|
|
6269
|
+
if (!noncesByAddress[transaction.decoded.from] || noncesByAddress?.[transaction.decoded.from] < transaction.decoded.nonce) {
|
|
6270
|
+
noncesByAddress[transaction.decoded.from] = transaction.decoded.nonce;
|
|
6214
6271
|
}
|
|
6215
|
-
globalThis.pubsub.publish("block-processed", blockMessage.decoded);
|
|
6216
|
-
} catch (error) {
|
|
6217
|
-
console.log(error.hash);
|
|
6218
|
-
console.log("errrrr");
|
|
6219
|
-
await transactionPoolStore.delete(error.hash);
|
|
6220
6272
|
}
|
|
6273
|
+
await Promise.all(
|
|
6274
|
+
Object.entries(noncesByAddress).map(([from, nonce]) => globalThis.accountsStore.put(from, String(nonce)))
|
|
6275
|
+
);
|
|
6276
|
+
if (validateChainLink(await this.canonicalTip(), {
|
|
6277
|
+
index: blockIndex,
|
|
6278
|
+
hash,
|
|
6279
|
+
previousHash: String(blockMessage.decoded.previousHash)
|
|
6280
|
+
}) !== "append") {
|
|
6281
|
+
throw new Error(`Canonical tip changed during block application: ${hash}`);
|
|
6282
|
+
}
|
|
6283
|
+
const registered = await this.machine.addLoadedBlock({ ...blockMessage.decoded, loaded: true, hash });
|
|
6284
|
+
if (registered !== true) throw new Error(`Block registration failed after execution: ${hash}`);
|
|
6285
|
+
try {
|
|
6286
|
+
await this.call(addresses.validators, "recordValidatorSnapshot", [blockMessage.decoded.index]);
|
|
6287
|
+
} catch (snapshotError) {
|
|
6288
|
+
debug(`failed to record validator snapshot: ${snapshotError?.message ?? snapshotError}`);
|
|
6289
|
+
}
|
|
6290
|
+
await this.updateState(blockMessage);
|
|
6291
|
+
debug(`added block: ${hash}`);
|
|
6292
|
+
globalThis.pubsub.publish("block-processed", blockMessage.decoded);
|
|
6293
|
+
await this.#handleEpochBoundary(blockIndex);
|
|
6221
6294
|
}
|
|
6222
6295
|
async participate(address) {
|
|
6223
6296
|
try {
|
|
@@ -6281,10 +6354,6 @@ class Chain extends VersionControl {
|
|
|
6281
6354
|
executionTimestamp: block?.timestamp ?? transaction.decoded.timestamp,
|
|
6282
6355
|
feePayments
|
|
6283
6356
|
});
|
|
6284
|
-
if (block) {
|
|
6285
|
-
block.transactions.push(hash);
|
|
6286
|
-
block.fees = block.fees += await calculateFee(transaction.decoded);
|
|
6287
|
-
}
|
|
6288
6357
|
await globalThis.accountsStore.put(
|
|
6289
6358
|
transaction.decoded.from,
|
|
6290
6359
|
new TextEncoder().encode(String(transaction.decoded.nonce))
|
|
@@ -6296,6 +6365,7 @@ class Chain extends VersionControl {
|
|
|
6296
6365
|
console.log(hash);
|
|
6297
6366
|
peernet.publish("invalid-transaction", hash);
|
|
6298
6367
|
await globalThis.transactionPoolStore.delete(e.hash);
|
|
6368
|
+
if (block) throw e;
|
|
6299
6369
|
}
|
|
6300
6370
|
}
|
|
6301
6371
|
// todo filter tx that need to wait on prev nonce
|