@leofcoin/chain 1.10.20 → 1.10.22
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/README.md +38 -0
- package/exports/browser/beacon-wire.js +1 -1
- package/exports/browser/{browser-CCfR9KOM-BGMMnVsR.js → browser-CCfR9KOM-UO9mzy-j.js} +2 -2
- package/exports/browser/chain.js +98 -165
- package/exports/browser/client-DbZBfpG6-CYDyUGUC.js +5943 -0
- package/exports/browser/{connection-monitor-D-krNiRH.js → connection-monitor-DMGAhcC0.js} +111 -2
- package/exports/browser/{index-dTQVgkpf-DOL2rIjp.js → index-dTQVgkpf-C6SJ0ZYW.js} +2 -2
- package/exports/browser/{index-DCbLvMKL.js → index-dxD17Fom.js} +231 -227
- package/exports/browser/{messages-DCePyqKC-BSvJxDWZ.js → messages-DZYCxoZI-D6kIiLKF.js} +2 -2
- package/exports/browser/{node-browser-Ha4Lvntj.js → node-browser-BMfelxXV.js} +23 -7
- package/exports/browser/node-browser.js +2 -2
- package/exports/chain.js +97 -164
- package/exports/connection-monitor-h2L8vsS7.js +435 -0
- package/exports/connection-monitor.js +2 -324
- package/exports/node.js +3 -1
- package/package.json +6 -6
- package/exports/browser/client-DKKzPSlb-C_1ug6ux.js +0 -2401
package/README.md
CHANGED
|
@@ -6,4 +6,42 @@
|
|
|
6
6
|
|
|
7
7
|
- reachedOneZeroZero
|
|
8
8
|
|
|
9
|
+
## Cold-sync reliability
|
|
10
|
+
|
|
11
|
+
Both server and browser nodes guard provider discovery against fast negative
|
|
12
|
+
responses. A walk succeeds only after a connected peer confirms it provides
|
|
13
|
+
the requested hash; individual discovery requests retain the protocol request
|
|
14
|
+
timeout.
|
|
15
|
+
|
|
16
|
+
Chain content requests probe the exact requested store before requesting bytes
|
|
17
|
+
from a peer. A pending-only copy cannot act as a provider for the canonical
|
|
18
|
+
transaction store. Responses must use the expected codec and advertised hash;
|
|
19
|
+
the first valid response wins, and unavailable content fails explicitly.
|
|
20
|
+
|
|
21
|
+
Historical ancestry is resolved iteratively, finishing transaction dependencies
|
|
22
|
+
before requesting the next parent. Duplicate callers await the same resolution,
|
|
23
|
+
completed blocks survive retries, and missing or cyclic ancestry fails explicitly.
|
|
24
|
+
Only dependencies of the selected target tip are resolved; unrelated hashes in
|
|
25
|
+
a peer's `knownBlocks` inventory do not gate canonical catch-up.
|
|
26
|
+
Transaction bodies are hash-checked before entering the canonical transaction
|
|
27
|
+
store. Resolving data never applies it: canonical loading still uses the
|
|
28
|
+
serialized state writer and chain-link validation.
|
|
29
|
+
|
|
30
|
+
## Pending transaction propagation
|
|
31
|
+
|
|
32
|
+
Transactions enter the local pending pool before canonical confirmation. Peers
|
|
33
|
+
relay newly accepted transaction bytes using the existing `add-transaction`
|
|
34
|
+
topic; duplicate and already-confirmed entries are not relayed again.
|
|
35
|
+
|
|
36
|
+
On compatible peer reconnection, pending pools are reconciled even at equal
|
|
37
|
+
block heights. Missing bodies are fetched from `transactionPool`, checked against
|
|
38
|
+
their advertised hash, and admitted through the normal signature-validation and
|
|
39
|
+
pending-nonce path. Unavailable or invalid entries are logged without preventing
|
|
40
|
+
recovery of the remaining entries.
|
|
41
|
+
|
|
42
|
+
Pool admission and pubsub publication are not validator acknowledgements.
|
|
43
|
+
`sendTransaction().wait` resolves only after canonical block inclusion. Deploy
|
|
44
|
+
the updated chain runtime to intermediate nodes and validators as well as the
|
|
45
|
+
browser to enable relay and reconnect recovery throughout the network.
|
|
46
|
+
|
|
9
47
|
<a rel="license" href="http://creativecommons.org/licenses/by-nc/4.0/"><img alt="Creative Commons Licence" style="border-width:0" src="https://i.creativecommons.org/l/by-nc/4.0/88x31.png" /></a><br />This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-nc/4.0/">Creative Commons Attribution-NonCommercial 4.0 International License</a>.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { s as signTransaction, c as MultiWallet, d as createTransactionHash } from './index-
|
|
1
|
+
import { s as signTransaction, c as MultiWallet, d as createTransactionHash } from './index-dxD17Fom.js';
|
|
2
2
|
import { f as fromBase58 } from './index-CvKt4UDE.js';
|
|
3
3
|
import './browser-crypto-CH0ua22L.js';
|
|
4
4
|
import './sha2-BKcEJzgd.js';
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { g as getDefaultExportFromCjs } from './node-browser-
|
|
1
|
+
import { g as getDefaultExportFromCjs } from './node-browser-BMfelxXV.js';
|
|
2
2
|
import './browser-crypto-CH0ua22L.js';
|
|
3
3
|
import './index-CvKt4UDE.js';
|
|
4
4
|
import './sha2-BKcEJzgd.js';
|
|
5
|
-
import './connection-monitor-
|
|
5
|
+
import './connection-monitor-DMGAhcC0.js';
|
|
6
6
|
|
|
7
7
|
function _mergeNamespaces$1(n, m) {
|
|
8
8
|
m.forEach(function (e) {
|
package/exports/browser/chain.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
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,
|
|
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-
|
|
2
|
+
import { P as Protocol, B as BlockMessage, L as LastBlockMessage, a as PROTOCOL_VERSION, R as REACHED_ONE_ZERO_ZERO, C as ConnectionMonitor, b as BWMessage, S as StateMessage } from './connection-monitor-DMGAhcC0.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-dxD17Fom.js';
|
|
4
4
|
import { f as fromBase58 } from './index-CvKt4UDE.js';
|
|
5
5
|
import { g as getDefaultExportFromCjs } from './_commonjsHelpers-B85MJLTf.js';
|
|
6
6
|
import { q as quorumThreshold } from './quorum-S_qdgiAY.js';
|
|
@@ -2915,29 +2915,6 @@ function requireSemver () {
|
|
|
2915
2915
|
var semverExports = requireSemver();
|
|
2916
2916
|
var semver = /*@__PURE__*/getDefaultExportFromCjs(semverExports);
|
|
2917
2917
|
|
|
2918
|
-
const limit = 1800;
|
|
2919
|
-
const transactionLimit = 2500;
|
|
2920
|
-
const requestTimeout = 3e4;
|
|
2921
|
-
const syncTimeout = 3e4;
|
|
2922
|
-
class Protocol {
|
|
2923
|
-
constructor(config) {
|
|
2924
|
-
this.resolveTimeout = 1e4;
|
|
2925
|
-
if (config?.resolveTimeout) this.resolveTimeout = config.resolveTimeout;
|
|
2926
|
-
}
|
|
2927
|
-
get limit() {
|
|
2928
|
-
return limit;
|
|
2929
|
-
}
|
|
2930
|
-
get transactionLimit() {
|
|
2931
|
-
return transactionLimit;
|
|
2932
|
-
}
|
|
2933
|
-
get requestTimeout() {
|
|
2934
|
-
return requestTimeout;
|
|
2935
|
-
}
|
|
2936
|
-
get syncTimeout() {
|
|
2937
|
-
return syncTimeout;
|
|
2938
|
-
}
|
|
2939
|
-
}
|
|
2940
|
-
|
|
2941
2918
|
const waitForCanonicalTransaction = (pubsub, transactionPool, hash) => new Promise((resolve, reject) => {
|
|
2942
2919
|
const completionTopic = `transaction.completed.${hash}`;
|
|
2943
2920
|
const cleanup = () => {
|
|
@@ -3164,7 +3141,7 @@ class Transaction extends Protocol {
|
|
|
3164
3141
|
await globalThis.transactionPoolStore.put(hash, message.encoded);
|
|
3165
3142
|
this.addPendingNonce(message.decoded.from, message.decoded.nonce);
|
|
3166
3143
|
try {
|
|
3167
|
-
peernet.publish("add-transaction", message.encoded);
|
|
3144
|
+
await peernet.publish("add-transaction", message.encoded);
|
|
3168
3145
|
} catch (publishError) {
|
|
3169
3146
|
console.warn("peernet publish failed: add-transaction", publishError?.message ?? publishError);
|
|
3170
3147
|
}
|
|
@@ -4304,6 +4281,15 @@ const validateChainLink = (localTip, incoming) => {
|
|
|
4304
4281
|
return "append";
|
|
4305
4282
|
};
|
|
4306
4283
|
|
|
4284
|
+
const resolveTransactionReference = async (expectedHash, transactionData) => {
|
|
4285
|
+
const transaction = transactionData instanceof TransactionMessage ? transactionData : new TransactionMessage(transactionData);
|
|
4286
|
+
const actualHash = await transaction.hash();
|
|
4287
|
+
if (actualHash !== expectedHash) {
|
|
4288
|
+
throw new Error(`Transaction hash mismatch: expected ${expectedHash}, got ${actualHash}`);
|
|
4289
|
+
}
|
|
4290
|
+
return transaction;
|
|
4291
|
+
};
|
|
4292
|
+
|
|
4307
4293
|
const debug$1 = createDebugger("leofcoin/state");
|
|
4308
4294
|
class State extends Contract {
|
|
4309
4295
|
constructor(config) {
|
|
@@ -4320,7 +4306,7 @@ class State extends Contract {
|
|
|
4320
4306
|
this.#totalSize = 0;
|
|
4321
4307
|
this.#canonicalStateQueue = new CanonicalStateQueue();
|
|
4322
4308
|
this.#loaded = false;
|
|
4323
|
-
this.#resolvingHashes = /* @__PURE__ */ new
|
|
4309
|
+
this.#resolvingHashes = /* @__PURE__ */ new Map();
|
|
4324
4310
|
this._wantList = [];
|
|
4325
4311
|
this.#chainStateHandler = () => {
|
|
4326
4312
|
return new globalThis.peernet.protos["peernet-response"]({
|
|
@@ -4576,7 +4562,8 @@ class State extends Contract {
|
|
|
4576
4562
|
if (!exists) {
|
|
4577
4563
|
const data = await peernet.get(hash, "transaction");
|
|
4578
4564
|
if (!data) throw new Error(`missing transaction data for ${hash}`);
|
|
4579
|
-
await
|
|
4565
|
+
const transaction = await resolveTransactionReference(hash, data);
|
|
4566
|
+
await transactionStore.put(hash, transaction.encoded);
|
|
4580
4567
|
}
|
|
4581
4568
|
const inPool = await transactionPoolStore.has(hash);
|
|
4582
4569
|
if (inPool) await transactionPoolStore.delete(hash);
|
|
@@ -4584,84 +4571,61 @@ class State extends Contract {
|
|
|
4584
4571
|
);
|
|
4585
4572
|
}
|
|
4586
4573
|
async #resolveBlock(hash) {
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
}
|
|
4598
|
-
if (this.#blocks[index]) {
|
|
4599
|
-
const previousHash = this.#blocks[index].previousHash;
|
|
4600
|
-
if (previousHash === localHash) return;
|
|
4601
|
-
if (previousHash !== "0x0") {
|
|
4602
|
-
return this.resolveBlock(previousHash);
|
|
4603
|
-
} else {
|
|
4604
|
-
return;
|
|
4574
|
+
const localHash = (await this.canonicalTip()).hash;
|
|
4575
|
+
const visited = /* @__PURE__ */ new Set();
|
|
4576
|
+
while (hash !== "0x0" && hash !== localHash) {
|
|
4577
|
+
if (visited.has(hash)) throw new ResolveError(`cyclic block ancestry at ${hash}`);
|
|
4578
|
+
visited.add(hash);
|
|
4579
|
+
const cachedIndex = this.#blockHashMap.get(hash);
|
|
4580
|
+
const cached = this.#blocks[cachedIndex];
|
|
4581
|
+
if (cached) {
|
|
4582
|
+
hash = cached.previousHash;
|
|
4583
|
+
continue;
|
|
4605
4584
|
}
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4585
|
+
try {
|
|
4586
|
+
const block = await this.getAndPutBlock(hash);
|
|
4587
|
+
await this.#resolveTransactions(block.decoded.transactions);
|
|
4588
|
+
const index = block.decoded.index;
|
|
4589
|
+
const size = block.encoded.byteLength;
|
|
4590
|
+
this.#totalSize += size;
|
|
4591
|
+
this.#blocks[index] = { hash, ...block.decoded };
|
|
4592
|
+
this.#blockHashMap.set(hash, index);
|
|
4593
|
+
debug$1(`resolved block: ${hash} @${index} ${formatBytes(size)}`);
|
|
4594
|
+
globalThis.pubsub.publish("block-resolved", { hash, index });
|
|
4595
|
+
this.#lastResolved = this.#blocks[index];
|
|
4596
|
+
this.#lastResolvedTime = Date.now();
|
|
4597
|
+
hash = block.decoded.previousHash;
|
|
4598
|
+
} catch (error) {
|
|
4599
|
+
throw new ResolveError(`block: ${hash}`, { cause: error });
|
|
4612
4600
|
}
|
|
4613
|
-
promises.push(this.#resolveTransactions(block.decoded.transactions));
|
|
4614
|
-
await Promise.all(promises);
|
|
4615
|
-
index = block.decoded.index;
|
|
4616
|
-
const size = block.encoded.length > 0 ? block.encoded.length : block.encoded.byteLength;
|
|
4617
|
-
this.#totalSize += size;
|
|
4618
|
-
this.#blocks[index] = { hash, ...block.decoded };
|
|
4619
|
-
this.#blockHashMap.set(hash, index);
|
|
4620
|
-
debug$1(`resolved block: ${hash} @${index} ${formatBytes(size)}`);
|
|
4621
|
-
globalThis.pubsub.publish("block-resolved", { hash, index });
|
|
4622
|
-
this.#lastResolved = this.#blocks[index];
|
|
4623
|
-
this.#lastResolvedTime = Date.now();
|
|
4624
|
-
} catch (error) {
|
|
4625
|
-
throw new ResolveError(`block: ${hash}@${index}`, { cause: error });
|
|
4626
4601
|
}
|
|
4627
|
-
return;
|
|
4628
4602
|
}
|
|
4629
4603
|
async resolveBlock(hash) {
|
|
4630
4604
|
if (!hash) throw new Error(`expected hash, got: ${hash}`);
|
|
4631
4605
|
if (hash === "0x0") return;
|
|
4632
|
-
|
|
4633
|
-
|
|
4634
|
-
const isEntering = this.#resolvingHashes.size === 1;
|
|
4606
|
+
const existing = this.#resolvingHashes.get(hash);
|
|
4607
|
+
if (existing) return existing;
|
|
4635
4608
|
this.#resolving = true;
|
|
4636
|
-
|
|
4637
|
-
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
} else {
|
|
4641
|
-
await this.#resolveBlock(hash);
|
|
4642
|
-
}
|
|
4643
|
-
try {
|
|
4644
|
-
const lastBlockHash = await globalThis.stateStore.get("lastBlock");
|
|
4645
|
-
if (lastBlockHash === hash) {
|
|
4609
|
+
const resolution = Promise.resolve().then(async () => {
|
|
4610
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
4611
|
+
try {
|
|
4612
|
+
await this.#resolveBlock(hash);
|
|
4646
4613
|
this.#resolveErrored = false;
|
|
4647
4614
|
return;
|
|
4615
|
+
} catch (error) {
|
|
4616
|
+
console.error(`block resolution attempt ${attempt + 1} failed for ${hash}`, error);
|
|
4617
|
+
if (attempt === 2) {
|
|
4618
|
+
if (!this.wantList.includes(hash)) this.wantList.push(hash);
|
|
4619
|
+
throw new ResolveError(`block: ${hash}`, { cause: error });
|
|
4620
|
+
}
|
|
4648
4621
|
}
|
|
4649
|
-
} catch (error) {
|
|
4650
|
-
}
|
|
4651
|
-
} catch (error) {
|
|
4652
|
-
console.log({ error });
|
|
4653
|
-
this.#resolveErrorCount += 1;
|
|
4654
|
-
if (this.#resolveErrorCount < 3) {
|
|
4655
|
-
this.#resolvingHashes.delete(hash);
|
|
4656
|
-
return this.resolveBlock(hash);
|
|
4657
4622
|
}
|
|
4658
|
-
|
|
4659
|
-
this.wantList.push(hash);
|
|
4660
|
-
throw new ResolveError(`block: ${hash}`, { cause: error });
|
|
4661
|
-
} finally {
|
|
4623
|
+
}).finally(() => {
|
|
4662
4624
|
this.#resolvingHashes.delete(hash);
|
|
4663
4625
|
if (this.#resolvingHashes.size === 0) this.#resolving = false;
|
|
4664
|
-
}
|
|
4626
|
+
});
|
|
4627
|
+
this.#resolvingHashes.set(hash, resolution);
|
|
4628
|
+
return resolution;
|
|
4665
4629
|
}
|
|
4666
4630
|
async resolveBlocks() {
|
|
4667
4631
|
if (this.#chainSyncing || this.#resolving) {
|
|
@@ -4798,24 +4762,6 @@ class State extends Contract {
|
|
|
4798
4762
|
return;
|
|
4799
4763
|
}
|
|
4800
4764
|
if (localStateHash !== remoteBlockHash) {
|
|
4801
|
-
if (this.wantList.length > 0) {
|
|
4802
|
-
debug$1(`Fetching ${this.wantList.length} blocks before resolving`);
|
|
4803
|
-
const getBatch = async (batch) => {
|
|
4804
|
-
const blocks2 = await Promise.all(
|
|
4805
|
-
batch.map(
|
|
4806
|
-
(hash) => this.getAndPutBlock(hash).catch((e) => {
|
|
4807
|
-
console.warn(`failed to fetch block ${hash}`, e);
|
|
4808
|
-
})
|
|
4809
|
-
)
|
|
4810
|
-
);
|
|
4811
|
-
const transactions = blocks2.filter((block) => Boolean(block)).flatMap((block) => block.decoded.transactions);
|
|
4812
|
-
return this.#resolveTransactions(transactions);
|
|
4813
|
-
};
|
|
4814
|
-
for (let i = 0; i < this.wantList.length; i += 50) {
|
|
4815
|
-
const batch = this.wantList.slice(i, i + 50);
|
|
4816
|
-
await getBatch(batch);
|
|
4817
|
-
}
|
|
4818
|
-
}
|
|
4819
4765
|
debug$1(`Resolving remote block: ${remoteBlockHash} @${remoteIndex} (differs from local state)`);
|
|
4820
4766
|
await this.resolveBlock(remoteBlockHash);
|
|
4821
4767
|
const blocksSynced = remoteIndex - localIndex;
|
|
@@ -5171,15 +5117,6 @@ const validateBlockEconomics = (block, calculatedFees, validatorFees = /* @__PUR
|
|
|
5171
5117
|
}
|
|
5172
5118
|
};
|
|
5173
5119
|
|
|
5174
|
-
const resolveTransactionReference = async (expectedHash, transactionData) => {
|
|
5175
|
-
const transaction = transactionData instanceof TransactionMessage ? transactionData : new TransactionMessage(transactionData);
|
|
5176
|
-
const actualHash = await transaction.hash();
|
|
5177
|
-
if (actualHash !== expectedHash) {
|
|
5178
|
-
throw new Error(`Transaction hash mismatch: expected ${expectedHash}, got ${actualHash}`);
|
|
5179
|
-
}
|
|
5180
|
-
return transaction;
|
|
5181
|
-
};
|
|
5182
|
-
|
|
5183
5120
|
const consensusSignableData = (validatorsAddress, type, message) => ({
|
|
5184
5121
|
from: String(message.from),
|
|
5185
5122
|
to: validatorsAddress,
|
|
@@ -6077,39 +6014,29 @@ class Chain extends VersionControl {
|
|
|
6077
6014
|
}
|
|
6078
6015
|
async getPeerTransactionPool(peer) {
|
|
6079
6016
|
let transactionsInPool = await this.#makeRequest(peer, "transactionPool");
|
|
6080
|
-
console.log("raw response for request transactionPool:", transactionsInPool);
|
|
6081
6017
|
if (transactionsInPool instanceof Uint8Array) {
|
|
6082
|
-
|
|
6083
|
-
transactionsInPool = JSON.parse(new TextDecoder().decode(transactionsInPool));
|
|
6084
|
-
} catch {
|
|
6085
|
-
debug("transactionPool response must be decoded array payload");
|
|
6086
|
-
return [];
|
|
6087
|
-
}
|
|
6018
|
+
transactionsInPool = new TextDecoder().decode(transactionsInPool);
|
|
6088
6019
|
}
|
|
6089
6020
|
if (typeof transactionsInPool === "string") {
|
|
6090
|
-
|
|
6091
|
-
transactionsInPool = JSON.parse(transactionsInPool);
|
|
6092
|
-
} catch {
|
|
6093
|
-
return [];
|
|
6094
|
-
}
|
|
6021
|
+
transactionsInPool = JSON.parse(transactionsInPool);
|
|
6095
6022
|
}
|
|
6096
|
-
if (!Array.isArray(transactionsInPool))
|
|
6097
|
-
|
|
6098
|
-
|
|
6099
|
-
|
|
6100
|
-
|
|
6101
|
-
|
|
6102
|
-
|
|
6103
|
-
|
|
6104
|
-
|
|
6105
|
-
|
|
6106
|
-
|
|
6107
|
-
|
|
6108
|
-
|
|
6109
|
-
}
|
|
6023
|
+
if (!Array.isArray(transactionsInPool) || transactionsInPool.some((hash) => typeof hash !== "string")) {
|
|
6024
|
+
throw new Error("transactionPool response must be an array of transaction hashes");
|
|
6025
|
+
}
|
|
6026
|
+
const recovered = [];
|
|
6027
|
+
for (const hash of new Set(transactionsInPool)) {
|
|
6028
|
+
if (await globalThis.transactionPoolStore.has(hash)) continue;
|
|
6029
|
+
if (await globalThis.transactionStore.has(hash)) continue;
|
|
6030
|
+
try {
|
|
6031
|
+
const txData = await globalThis.peernet.get(hash, "transactionPool");
|
|
6032
|
+
if (txData === void 0) throw new Error("pending transaction body unavailable");
|
|
6033
|
+
const transaction = await resolveTransactionReference(hash, txData);
|
|
6034
|
+
recovered.push(await this.#sendTransaction(transaction));
|
|
6035
|
+
} catch (error) {
|
|
6036
|
+
debug(`Failed to recover transaction ${hash}:`, error?.message ?? error);
|
|
6110
6037
|
}
|
|
6111
6038
|
}
|
|
6112
|
-
return
|
|
6039
|
+
return recovered;
|
|
6113
6040
|
}
|
|
6114
6041
|
async #peerConnected(peerId) {
|
|
6115
6042
|
if (typeof peerId !== "string" || !peerId.trim() || peerId === "undefined") {
|
|
@@ -6177,6 +6104,14 @@ class Chain extends VersionControl {
|
|
|
6177
6104
|
await this.#recordPeerFailure(peerId, `unreasonable lastBlock index: ${lastBlock.index}`);
|
|
6178
6105
|
return;
|
|
6179
6106
|
}
|
|
6107
|
+
setTimeout(async () => {
|
|
6108
|
+
try {
|
|
6109
|
+
await this.getPeerTransactionPool(peer);
|
|
6110
|
+
} catch (error) {
|
|
6111
|
+
const peerName = peer?.peerId || peer?.id || peer?.address || peerId || "unknown";
|
|
6112
|
+
debug(`transactionPool request failed: ${peerName}:`, error?.message ?? error);
|
|
6113
|
+
}
|
|
6114
|
+
}, 3e3);
|
|
6180
6115
|
if (!lastBlock || !lastBlock.hash || lastBlock.hash === "0x0") {
|
|
6181
6116
|
debug(`peer has no lastBlock: ${peerId}`);
|
|
6182
6117
|
return;
|
|
@@ -6209,22 +6144,6 @@ class Chain extends VersionControl {
|
|
|
6209
6144
|
}
|
|
6210
6145
|
}
|
|
6211
6146
|
}
|
|
6212
|
-
if (this.wantList.length > 0) {
|
|
6213
|
-
const promises = await Promise.allSettled(this.wantList.map((hash) => peernet.get(hash, "block")));
|
|
6214
|
-
for (let i = promises.length - 1; i >= 0; i -= 1) {
|
|
6215
|
-
if (promises[i].status === "fulfilled") this.wantList.splice(i, 1);
|
|
6216
|
-
}
|
|
6217
|
-
if (this.wantList.length === 0) await this.triggerSync();
|
|
6218
|
-
}
|
|
6219
|
-
setTimeout(async () => {
|
|
6220
|
-
try {
|
|
6221
|
-
const peerTransactionPool = higherThenCurrentLocal && await this.getPeerTransactionPool(peer) || [];
|
|
6222
|
-
if (this.#participating && peerTransactionPool.length > 0) return this.#runEpoch();
|
|
6223
|
-
} catch (error) {
|
|
6224
|
-
const peerName = peer?.peerId || peer?.id || peer?.address || peerId || "unknown";
|
|
6225
|
-
debug(`transactionPool request failed: ${peerName}:`, error?.message ?? error);
|
|
6226
|
-
}
|
|
6227
|
-
}, 3e3);
|
|
6228
6147
|
try {
|
|
6229
6148
|
let stateInfo = await this.#makeRequest(peer, "stateInfo");
|
|
6230
6149
|
if (stateInfo instanceof Uint8Array) {
|
|
@@ -6601,18 +6520,32 @@ class Chain extends VersionControl {
|
|
|
6601
6520
|
await this.validateTransactionSignature(transaction);
|
|
6602
6521
|
let hash;
|
|
6603
6522
|
try {
|
|
6523
|
+
hash = await transaction.hash();
|
|
6524
|
+
if (await globalThis.transactionStore.has(hash)) return hash;
|
|
6525
|
+
const alreadyPending = await globalThis.transactionPoolStore.has(hash);
|
|
6604
6526
|
hash = await enqueueTransaction(transaction, {
|
|
6605
6527
|
putToPool: (txHash, data) => globalThis.transactionPoolStore.put(txHash, data),
|
|
6606
6528
|
hasInPool: (txHash) => globalThis.transactionPoolStore.has(txHash),
|
|
6607
6529
|
hasInStore: (txHash) => globalThis.transactionStore.has(txHash),
|
|
6608
6530
|
addPendingNonce: (address, nonce) => this.addPendingNonce(address, nonce)
|
|
6609
6531
|
});
|
|
6610
|
-
if (
|
|
6532
|
+
if (!alreadyPending) {
|
|
6533
|
+
try {
|
|
6534
|
+
await globalThis.peernet.publish("add-transaction", transaction.encoded);
|
|
6535
|
+
} catch (publishError) {
|
|
6536
|
+
debug("peernet publish failed: add-transaction relay", publishError?.message ?? publishError);
|
|
6537
|
+
}
|
|
6538
|
+
}
|
|
6539
|
+
if (this.#participating && !this.#runningEpoch) {
|
|
6540
|
+
void this.#runEpoch().catch((error) => {
|
|
6541
|
+
debug("transaction epoch scheduling failed:", error?.message ?? error);
|
|
6542
|
+
});
|
|
6543
|
+
}
|
|
6611
6544
|
return hash;
|
|
6612
6545
|
} catch (e) {
|
|
6613
6546
|
if (hash) {
|
|
6614
6547
|
try {
|
|
6615
|
-
globalThis.peernet.publish("invalid-transaction", hash);
|
|
6548
|
+
await globalThis.peernet.publish("invalid-transaction", hash);
|
|
6616
6549
|
} catch (publishError) {
|
|
6617
6550
|
debug("peernet publish failed: invalid-transaction", publishError?.message ?? publishError);
|
|
6618
6551
|
}
|
|
@@ -6630,7 +6563,7 @@ class Chain extends VersionControl {
|
|
|
6630
6563
|
const event = await super.sendTransaction(transactionMessage);
|
|
6631
6564
|
await this.#sendTransaction(transactionMessage.encoded);
|
|
6632
6565
|
try {
|
|
6633
|
-
globalThis.peernet.publish("send-transaction", transactionMessage.encoded);
|
|
6566
|
+
await globalThis.peernet.publish("send-transaction", transactionMessage.encoded);
|
|
6634
6567
|
} catch (publishError) {
|
|
6635
6568
|
debug("peernet publish failed: send-transaction", publishError?.message ?? publishError);
|
|
6636
6569
|
}
|