@dusk-network/app-core 0.3.2 → 0.4.0
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/CHANGELOG.md +26 -0
- package/README.md +12 -8
- package/package.json +1 -1
- package/src/activity.d.ts +5 -1
- package/src/activity.js +7 -1
- package/src/bridge.d.ts +17 -5
- package/src/bridge.js +169 -91
- package/src/duskevm-withdrawals.d.ts +18 -4
- package/src/duskevm-withdrawals.js +95 -17
- package/src/history.d.ts +34 -10
- package/src/history.js +80 -18
- package/src/shielded.d.ts +21 -6
- package/src/shielded.js +155 -42
package/src/bridge.js
CHANGED
|
@@ -76,14 +76,16 @@ const MAX_U64 = (1n << 64n) - 1n;
|
|
|
76
76
|
const TIMEOUT_MS = 8_000;
|
|
77
77
|
// About one Dusk block every 10 seconds: how far back a sender's history is read.
|
|
78
78
|
const DUSK_BLOCK_MS = 10_000;
|
|
79
|
-
// The blocks the chain's pace is read over,
|
|
80
|
-
//
|
|
81
|
-
//
|
|
79
|
+
// The blocks the chain's pace is read over, from a block rounded down to a
|
|
80
|
+
// multiple of `PACE_STEP` so that the app's cache keeps its time, and how
|
|
81
|
+
// long before the transfer a search starts as the pace says: the block is
|
|
82
|
+
// then checked by its time, and stepped back while it's still later.
|
|
82
83
|
const PACE_SAMPLE = 100_000n;
|
|
83
|
-
const
|
|
84
|
-
const
|
|
85
|
-
const START_CHECKS = 4;
|
|
84
|
+
const PACE_STEP = 10_000n;
|
|
85
|
+
const START_MS = 10 * 60_000;
|
|
86
86
|
const MIN_WINDOW = 50n;
|
|
87
|
+
// The widest a search's windows grow, while the RPC answers them whole.
|
|
88
|
+
const MAX_WINDOW = 100_000n;
|
|
87
89
|
// How far back from the head a search's last window reaches: those blocks
|
|
88
90
|
// are read again on each call.
|
|
89
91
|
const OVERLAP = 1_000n;
|
|
@@ -245,9 +247,9 @@ function historyScanOf(value) {
|
|
|
245
247
|
}
|
|
246
248
|
|
|
247
249
|
/**
|
|
248
|
-
* Where a search stopped, what it searched for,
|
|
249
|
-
* before, and how far the transfer's history is
|
|
250
|
-
* them; null for anything else.
|
|
250
|
+
* Where a search stopped, what it searched for, the credits it found
|
|
251
|
+
* before, the width of its windows, and how far the transfer's history is
|
|
252
|
+
* read, as a result gave them; null for anything else.
|
|
251
253
|
*
|
|
252
254
|
* @param {unknown} value
|
|
253
255
|
* @returns {Scan | null}
|
|
@@ -274,10 +276,16 @@ function scanOf(value) {
|
|
|
274
276
|
// as a search kept by an earlier version.
|
|
275
277
|
const start = quantityOf(scan.start);
|
|
276
278
|
const recheck = start === null ? null : recheckOf(scan.recheck);
|
|
279
|
+
// The width of its windows, as an earlier version kept it for its passes
|
|
280
|
+
// where it isn't said; and the narrowest the RPC refused.
|
|
281
|
+
const size = Number.isSafeInteger(scan.size) && scan.size >= Number(MIN_WINDOW) && scan.size <= Number(MAX_WINDOW) ? scan.size : (recheck?.size ?? null);
|
|
282
|
+
const refused = Number.isSafeInteger(scan.refused) && scan.refused > 0 ? scan.refused : null;
|
|
277
283
|
|
|
278
284
|
Object.assign(out, { basis: scan.basis, from: from.toString(), search: scan.search, seen });
|
|
279
285
|
if (start !== null) out.start = start.toString();
|
|
280
286
|
if (recheck) out.recheck = recheckIn(recheck);
|
|
287
|
+
if (size !== null) out.size = size;
|
|
288
|
+
if (refused !== null) out.refused = refused;
|
|
281
289
|
} else if (!history) {
|
|
282
290
|
return null;
|
|
283
291
|
}
|
|
@@ -395,6 +403,10 @@ function reader(fetchFn, timeoutMs) {
|
|
|
395
403
|
|
|
396
404
|
return {
|
|
397
405
|
counter,
|
|
406
|
+
// What a call reads once for all its searches, by what and where: a
|
|
407
|
+
// chain's head and pace.
|
|
408
|
+
/** @type {Map<string, Promise<any>>} */
|
|
409
|
+
once: new Map(),
|
|
398
410
|
|
|
399
411
|
/**
|
|
400
412
|
* @param {string} url
|
|
@@ -693,80 +705,125 @@ async function blockTime(read, url, block, cache) {
|
|
|
693
705
|
|
|
694
706
|
if (typeof kept === "number" && Number.isFinite(kept)) return kept;
|
|
695
707
|
|
|
696
|
-
|
|
708
|
+
return once(read, `time ${url} ${block}`, async () => {
|
|
709
|
+
const seconds = quantityOf((await read.rpc(url, "eth_getBlockByNumber", [hex(block), false]))?.timestamp);
|
|
697
710
|
|
|
698
|
-
|
|
711
|
+
if (seconds === null) throw new Error(`The chain gave no time for block ${block}`);
|
|
699
712
|
|
|
700
|
-
|
|
713
|
+
const at = Number(seconds) * 1000;
|
|
701
714
|
|
|
702
|
-
|
|
715
|
+
cache?.set(Number(block), at);
|
|
703
716
|
|
|
704
|
-
|
|
717
|
+
return at;
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
/**
|
|
722
|
+
* What a call reads once for all its searches on a chain.
|
|
723
|
+
*
|
|
724
|
+
* @template T
|
|
725
|
+
* @param {Reader} read
|
|
726
|
+
* @param {string} key
|
|
727
|
+
* @param {() => Promise<T>} get
|
|
728
|
+
* @returns {Promise<T>}
|
|
729
|
+
*/
|
|
730
|
+
function once(read, key, get) {
|
|
731
|
+
const kept = read.once.get(key);
|
|
732
|
+
|
|
733
|
+
if (kept) return kept;
|
|
734
|
+
|
|
735
|
+
const got = get();
|
|
736
|
+
|
|
737
|
+
read.once.set(key, got);
|
|
738
|
+
// One that failed is read again by the next search.
|
|
739
|
+
got.catch(() => read.once.delete(key));
|
|
740
|
+
|
|
741
|
+
return got;
|
|
705
742
|
}
|
|
706
743
|
|
|
707
744
|
/**
|
|
745
|
+
* The chain's head, read once a call: the head its pace was read with, where
|
|
746
|
+
* the call read that.
|
|
747
|
+
*
|
|
708
748
|
* @param {Reader} read
|
|
709
749
|
* @param {string} url
|
|
710
750
|
* @returns {Promise<bigint>}
|
|
711
751
|
*/
|
|
712
752
|
async function headOf(read, url) {
|
|
713
|
-
const
|
|
753
|
+
const paced = read.once.get(`pace ${url}`);
|
|
754
|
+
|
|
755
|
+
if (paced) return (await paced).head;
|
|
714
756
|
|
|
715
|
-
|
|
757
|
+
return once(read, `head ${url}`, async () => {
|
|
758
|
+
const head = quantityOf(await read.rpc(url, "eth_blockNumber", []));
|
|
716
759
|
|
|
717
|
-
|
|
760
|
+
if (head === null) throw new Error("The chain gave no head");
|
|
761
|
+
|
|
762
|
+
return head;
|
|
763
|
+
});
|
|
718
764
|
}
|
|
719
765
|
|
|
720
766
|
/**
|
|
721
|
-
* The chain's head, and
|
|
722
|
-
*
|
|
723
|
-
* stepped back while its own time is still later, twice as far each time,
|
|
724
|
-
* as the chain's pace may have changed.
|
|
767
|
+
* The chain's head, its time, and its pace (blocks a second) over its last
|
|
768
|
+
* 100,000 blocks or so, read once a call.
|
|
725
769
|
*
|
|
726
770
|
* @param {Reader} read
|
|
727
771
|
* @param {string} url
|
|
728
|
-
* @param {number} atMs
|
|
729
772
|
* @param {BlockTimes | undefined} cache
|
|
730
|
-
* @returns {Promise<{ head: bigint,
|
|
773
|
+
* @returns {Promise<{ head: bigint, seconds: number, pace: number }>}
|
|
731
774
|
*/
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
775
|
+
function paceOf(read, url, cache) {
|
|
776
|
+
return once(read, `pace ${url}`, async () => {
|
|
777
|
+
const latest = await read.rpc(url, "eth_getBlockByNumber", ["latest", false]);
|
|
778
|
+
const head = quantityOf(latest?.number);
|
|
779
|
+
const headSeconds = quantityOf(latest?.timestamp);
|
|
736
780
|
|
|
737
|
-
|
|
781
|
+
if (head === null || headSeconds === null) throw new Error("The chain gave no head");
|
|
738
782
|
|
|
739
|
-
|
|
783
|
+
cache?.set(Number(head), Number(headSeconds) * 1000);
|
|
784
|
+
read.once.set(`time ${url} ${head}`, Promise.resolve(Number(headSeconds) * 1000));
|
|
740
785
|
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
let pace = 1;
|
|
786
|
+
const back = head < PACE_SAMPLE ? 0n : ((head - PACE_SAMPLE) / PACE_STEP) * PACE_STEP;
|
|
787
|
+
let pace = 1;
|
|
744
788
|
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
const span = Number(headSeconds) - sample / 1000;
|
|
789
|
+
if (back < head) {
|
|
790
|
+
const span = Number(headSeconds) - (await blockTime(read, url, back, cache)) / 1000;
|
|
748
791
|
|
|
749
|
-
|
|
750
|
-
|
|
792
|
+
if (span > 0) pace = Number(head - back) / span;
|
|
793
|
+
}
|
|
751
794
|
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
795
|
+
return { head, pace, seconds: Number(headSeconds) };
|
|
796
|
+
});
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
/**
|
|
800
|
+
* The chain's head, and a block from before `atMs`: the block 10 minutes
|
|
801
|
+
* before it, as the chain's pace says, checked by its time, and stepped back
|
|
802
|
+
* while that's still later, twice as far each time, as the chain's pace may
|
|
803
|
+
* have changed.
|
|
804
|
+
*
|
|
805
|
+
* @param {Reader} read
|
|
806
|
+
* @param {string} url
|
|
807
|
+
* @param {number} atMs
|
|
808
|
+
* @param {BlockTimes | undefined} cache
|
|
809
|
+
* @returns {Promise<{ head: bigint, start: bigint }>}
|
|
810
|
+
*/
|
|
811
|
+
async function startBefore(read, url, atMs, cache) {
|
|
812
|
+
const { head, pace, seconds } = await paceOf(read, url, cache);
|
|
813
|
+
/** @param {bigint} block @param {number} ms @param {number} times */
|
|
814
|
+
const back = (block, ms, times) => {
|
|
815
|
+
const step = BigInt(Math.ceil((Math.max(0, ms) / 1000) * pace * times));
|
|
759
816
|
|
|
760
817
|
return block > step ? block - step : 0n;
|
|
761
818
|
};
|
|
762
|
-
let start =
|
|
819
|
+
let start = back(head, seconds * 1000 - atMs + START_MS, 1);
|
|
763
820
|
|
|
764
|
-
for (let
|
|
821
|
+
for (let times = 2; start > 0n; times *= 2) {
|
|
765
822
|
const at = await blockTime(read, url, start, cache);
|
|
766
823
|
|
|
767
824
|
if (at <= atMs) break;
|
|
768
825
|
|
|
769
|
-
start =
|
|
826
|
+
start = back(start, at - atMs + START_MS, times);
|
|
770
827
|
}
|
|
771
828
|
|
|
772
829
|
return { head, start };
|
|
@@ -775,37 +832,43 @@ async function startBefore(read, url, atMs, cache) {
|
|
|
775
832
|
/**
|
|
776
833
|
* The credits asked for from block `start`, a window of blocks at a time, at
|
|
777
834
|
* most `maxWindows`, until `head`, `enough`, or a window ending after
|
|
778
|
-
* `until` (`covered
|
|
779
|
-
*
|
|
780
|
-
*
|
|
781
|
-
*
|
|
835
|
+
* `until` (`covered`, as the block before `start` may already be, where a
|
|
836
|
+
* search before stopped there): each checked against what was asked, with
|
|
837
|
+
* its block's time. Each window is `size` blocks wide, twice as wide after
|
|
838
|
+
* one the RPC answered whole, up to `MAX_WINDOW`, and narrower than any it
|
|
839
|
+
* refused (`refused`, the narrowest). One it won't search at once, or one
|
|
840
|
+
* wider than `LOG_WINDOW` that isn't answered, is asked again a quarter as
|
|
841
|
+
* wide, or as wide as the widest it answered where that's wider: `size` and
|
|
842
|
+
* `refused` are as the search leaves them. The window ending at `head`
|
|
782
843
|
* reaches back up to `overlap` blocks from it, no wider than `size`, though
|
|
783
844
|
* not under `floor`: those blocks are read again (from `low`). `asked`: the
|
|
784
845
|
* windows asked.
|
|
785
846
|
*
|
|
786
847
|
* @param {Reader} read
|
|
787
|
-
* @param {{ url: string, address: string, topics: ReadonlyArray<string | null>, wei: bigint, amountOf: (data: string) => bigint | null, start: bigint, floor: bigint, head: bigint, size
|
|
788
|
-
* @returns {Promise<{ asked: number, complete: boolean, covered: boolean, credits: Credit[], low: bigint | null, next: bigint, refused: bigint | null, size: bigint
|
|
848
|
+
* @param {{ url: string, address: string, topics: ReadonlyArray<string | null>, wei: bigint, amountOf: (data: string) => bigint | null, start: bigint, floor: bigint, head: bigint, size: bigint, refused: bigint | null, overlap?: bigint, maxWindows: number, blockTimes: BlockTimes | undefined, enough: (credits: Credit[]) => boolean, until: number | null, known: Credit[] }} search
|
|
849
|
+
* @returns {Promise<{ asked: number, complete: boolean, covered: boolean, credits: Credit[], low: bigint | null, next: bigint, refused: bigint | null, size: bigint }>}
|
|
789
850
|
*/
|
|
790
|
-
async function searchLogs(read, { address, amountOf, blockTimes, enough, floor, head, known, maxWindows, overlap = OVERLAP, size: width
|
|
851
|
+
async function searchLogs(read, { address, amountOf, blockTimes, enough, floor, head, known, maxWindows, overlap = OVERLAP, refused: kept, size: width, start, topics, until, url, wei }) {
|
|
791
852
|
// What an earlier search found before `start`.
|
|
792
853
|
/** @type {Credit[]} */
|
|
793
854
|
const credits = [...known];
|
|
794
855
|
let from = start;
|
|
795
856
|
let size = width;
|
|
857
|
+
let refused = kept;
|
|
796
858
|
let covered = false;
|
|
797
859
|
let asked = 0;
|
|
798
860
|
/** @type {bigint | null} */
|
|
799
861
|
let low = null;
|
|
800
862
|
/** @type {bigint | null} */
|
|
801
|
-
let refused = null;
|
|
802
|
-
/** @type {bigint | null} */
|
|
803
863
|
let took = null;
|
|
804
864
|
|
|
805
|
-
|
|
865
|
+
if (until !== null && start > floor && start <= head) covered = (await blockTime(read, url, start - 1n, blockTimes)) > until;
|
|
866
|
+
|
|
867
|
+
while (!covered && asked < maxWindows && from <= head) {
|
|
806
868
|
const to = from + size - 1n < head ? from + size - 1n : head;
|
|
807
869
|
const back = head + 1n - (size < overlap ? size : overlap);
|
|
808
870
|
const lo = to === head && back < from ? (back > floor ? back : floor) : from;
|
|
871
|
+
const blocks = to - lo + 1n;
|
|
809
872
|
/** @type {any} */
|
|
810
873
|
let logs;
|
|
811
874
|
|
|
@@ -814,9 +877,11 @@ async function searchLogs(read, { address, amountOf, blockTimes, enough, floor,
|
|
|
814
877
|
try {
|
|
815
878
|
logs = await read.rpc(url, "eth_getLogs", [{ address, fromBlock: hex(lo), toBlock: hex(to), topics }]);
|
|
816
879
|
} catch (error) {
|
|
817
|
-
if (
|
|
818
|
-
|
|
819
|
-
|
|
880
|
+
if (blocks > MIN_WINDOW && (blocks > BigInt(LOG_WINDOW) || RANGE_REFUSED.test(messageOf(error)))) {
|
|
881
|
+
const fits = took !== null && took < blocks && took > blocks / 4n ? took : blocks / 4n;
|
|
882
|
+
|
|
883
|
+
if (refused === null || blocks < refused) refused = blocks;
|
|
884
|
+
size = fits > MIN_WINDOW ? fits : MIN_WINDOW;
|
|
820
885
|
continue;
|
|
821
886
|
}
|
|
822
887
|
|
|
@@ -824,7 +889,14 @@ async function searchLogs(read, { address, amountOf, blockTimes, enough, floor,
|
|
|
824
889
|
}
|
|
825
890
|
|
|
826
891
|
if (to === head) low = lo;
|
|
827
|
-
if (took === null ||
|
|
892
|
+
if (took === null || blocks > took) took = blocks;
|
|
893
|
+
|
|
894
|
+
// Answered whole: the next one twice as wide, while narrower than any refused.
|
|
895
|
+
if (blocks === size) {
|
|
896
|
+
const wider = size * 2n < MAX_WINDOW ? size * 2n : MAX_WINDOW;
|
|
897
|
+
|
|
898
|
+
if (refused === null || wider < refused) size = wider;
|
|
899
|
+
}
|
|
828
900
|
|
|
829
901
|
for (const log of Array.isArray(logs) ? logs : []) {
|
|
830
902
|
const found = creditLog(log, { address, amountOf, from: lo, to, topics, wei });
|
|
@@ -844,14 +916,15 @@ async function searchLogs(read, { address, amountOf, blockTimes, enough, floor,
|
|
|
844
916
|
}
|
|
845
917
|
}
|
|
846
918
|
|
|
847
|
-
return { asked, complete: from > head, covered, credits, low, next: from, refused, size
|
|
919
|
+
return { asked, complete: from > head, covered, credits, low, next: from, refused, size };
|
|
848
920
|
}
|
|
849
921
|
|
|
850
922
|
/**
|
|
851
923
|
* A search's passes over its blocks again, as a result gave them: where the
|
|
852
924
|
* one under way is (null between two), when the next begins, how many are
|
|
853
|
-
* done, when the search first reached the head, and the
|
|
854
|
-
*
|
|
925
|
+
* done, when the search first reached the head, and the width of the
|
|
926
|
+
* windows it read them in, as an earlier version kept it; null for anything
|
|
927
|
+
* else.
|
|
855
928
|
*
|
|
856
929
|
* @param {unknown} value
|
|
857
930
|
* @returns {{ at: number, from: bigint | null, passes: number, since: number, size: number | null } | null}
|
|
@@ -875,8 +948,8 @@ function recheckOf(value) {
|
|
|
875
948
|
* @param {NonNullable<ReturnType<typeof recheckOf>>} recheck
|
|
876
949
|
* @returns {NonNullable<Scan["recheck"]>}
|
|
877
950
|
*/
|
|
878
|
-
function recheckIn({ at, from, passes, since
|
|
879
|
-
return { at, from: from === null ? null : from.toString(), passes, since
|
|
951
|
+
function recheckIn({ at, from, passes, since }) {
|
|
952
|
+
return { at, from: from === null ? null : from.toString(), passes, since };
|
|
880
953
|
}
|
|
881
954
|
|
|
882
955
|
/**
|
|
@@ -892,9 +965,10 @@ function recheckIn({ at, from, passes, since, size }) {
|
|
|
892
965
|
* time: the first a minute later, each after as long again as it has been
|
|
893
966
|
* since then, and one after the other, as far as its credit, while an
|
|
894
967
|
* arrival is held back. A pass under way takes what the search ahead leaves
|
|
895
|
-
* of a call's windows, one at least
|
|
896
|
-
*
|
|
897
|
-
*
|
|
968
|
+
* of a call's windows, one at least. A pass counts once it read the blocks
|
|
969
|
+
* again, as does a call whose window at the head reached back to where the
|
|
970
|
+
* search began. Both go on in windows as wide as the searches before left
|
|
971
|
+
* them, narrower than the narrowest the RPC refused.
|
|
898
972
|
*
|
|
899
973
|
* @param {Reader} read
|
|
900
974
|
* @param {{ given: Queued[], queue: Queued[], guarded: boolean, unsure: ReadonlySet<string>, rivals?: ReadonlySet<string>, filter: { address: string, topics: ReadonlyArray<string | null>, wei: bigint, amountOf: (data: string) => bigint | null } }} group
|
|
@@ -922,10 +996,10 @@ async function followLogs(read, { filter, given, guarded, queue, rivals, unsure
|
|
|
922
996
|
// The logs searched for: a search for others, as for another sender or
|
|
923
997
|
// amount, isn't carried on.
|
|
924
998
|
const search = [filter.address, ...filter.topics.map((topic) => topic ?? "*"), filter.wei.toString()].join(":");
|
|
925
|
-
/** @type {{ basis: number, from: string, seen?: Scan["seen"], start?: string, recheck?: Scan["recheck"] }[]} */
|
|
999
|
+
/** @type {{ basis: number, from: string, seen?: Scan["seen"], start?: string, recheck?: Scan["recheck"], size?: number, refused?: number }[]} */
|
|
926
1000
|
const resumed = live.flatMap(({ scan }) =>
|
|
927
1001
|
scan?.from !== undefined && scan.search === search && scan.basis !== undefined && scan.basis <= first.at
|
|
928
|
-
? [{ basis: scan.basis, from: scan.from, recheck: scan.recheck, seen: scan.seen, start: scan.start }]
|
|
1002
|
+
? [{ basis: scan.basis, from: scan.from, recheck: scan.recheck, refused: scan.refused, seen: scan.seen, size: scan.size, start: scan.start }]
|
|
929
1003
|
: []
|
|
930
1004
|
);
|
|
931
1005
|
/** @type {Map<string, Credit>} */
|
|
@@ -990,9 +1064,11 @@ async function followLogs(read, { filter, given, guarded, queue, rivals, unsure
|
|
|
990
1064
|
// No later than a credit found before: the blocks are read again from it.
|
|
991
1065
|
for (const credit of carried.values()) if (credit.block !== null && credit.block < floor) floor = credit.block;
|
|
992
1066
|
|
|
993
|
-
// The
|
|
994
|
-
//
|
|
995
|
-
const
|
|
1067
|
+
// The width of the windows, and the narrowest the RPC refused, as the
|
|
1068
|
+
// searches before left them: the narrowest of each.
|
|
1069
|
+
const sizes = resumed.flatMap((scan) => (scan.size === undefined ? [] : [BigInt(scan.size)]));
|
|
1070
|
+
const refusals = resumed.flatMap((scan) => (scan.refused === undefined ? [] : [BigInt(scan.refused)]));
|
|
1071
|
+
const width = sizes.length ? minOf(sizes) : BigInt(LOG_WINDOW);
|
|
996
1072
|
|
|
997
1073
|
/**
|
|
998
1074
|
* The given transfers' credits with `found`: those that arrived, by their
|
|
@@ -1060,7 +1136,8 @@ async function followLogs(read, { filter, given, guarded, queue, rivals, unsure
|
|
|
1060
1136
|
head,
|
|
1061
1137
|
known: [...carried.values()],
|
|
1062
1138
|
maxWindows,
|
|
1063
|
-
|
|
1139
|
+
refused: refusals.length ? minOf(refusals) : null,
|
|
1140
|
+
size: width,
|
|
1064
1141
|
start,
|
|
1065
1142
|
until,
|
|
1066
1143
|
url,
|
|
@@ -1075,7 +1152,7 @@ async function followLogs(read, { filter, given, guarded, queue, rivals, unsure
|
|
|
1075
1152
|
|
|
1076
1153
|
let credits = mark(ahead.credits);
|
|
1077
1154
|
const before = settled(credits, seen);
|
|
1078
|
-
let { refused,
|
|
1155
|
+
let { refused, size } = ahead;
|
|
1079
1156
|
|
|
1080
1157
|
if ((seen || before.held.size) && recheck === null) recheck = { at: before.held.size ? now : now + RECHECK_MS, from: null, passes: 0, since: now, size: null };
|
|
1081
1158
|
|
|
@@ -1096,7 +1173,8 @@ async function followLogs(read, { filter, given, guarded, queue, rivals, unsure
|
|
|
1096
1173
|
head: before.top !== null && before.top < below ? before.top : below - 1n,
|
|
1097
1174
|
known: credits,
|
|
1098
1175
|
maxWindows: maxWindows < 1 ? 0 : Math.max(1, maxWindows - ahead.asked),
|
|
1099
|
-
|
|
1176
|
+
refused,
|
|
1177
|
+
size,
|
|
1100
1178
|
start: from,
|
|
1101
1179
|
until,
|
|
1102
1180
|
url,
|
|
@@ -1108,18 +1186,7 @@ async function followLogs(read, { filter, given, guarded, queue, rivals, unsure
|
|
|
1108
1186
|
|
|
1109
1187
|
recheck = done ? { ...recheck, at: now + Math.max(RECHECK_MS, now - recheck.since), from: null, passes: recheck.passes + (counted ? 1 : 0) } : { ...recheck, from: pass.next };
|
|
1110
1188
|
credits = mark(pass.credits);
|
|
1111
|
-
|
|
1112
|
-
if (pass.took !== null && (took === null || pass.took > took)) took = pass.took;
|
|
1113
|
-
}
|
|
1114
|
-
|
|
1115
|
-
// A window refused: from the next call on, the blocks are read again in
|
|
1116
|
-
// ones as wide as the widest it took narrower than that, else a quarter
|
|
1117
|
-
// as wide.
|
|
1118
|
-
if (recheck !== null && refused !== null) {
|
|
1119
|
-
const fits = took !== null && took < refused ? took : refused / 4n;
|
|
1120
|
-
const narrower = fits > MIN_WINDOW ? fits : MIN_WINDOW;
|
|
1121
|
-
|
|
1122
|
-
if (narrower < width) recheck = { ...recheck, size: Number(narrower) };
|
|
1189
|
+
({ refused, size } = pass);
|
|
1123
1190
|
}
|
|
1124
1191
|
|
|
1125
1192
|
const { arrived, assigned, held, verdicts } = settled(credits, seen);
|
|
@@ -1138,8 +1205,10 @@ async function followLogs(read, { filter, given, guarded, queue, rivals, unsure
|
|
|
1138
1205
|
basis: first.at,
|
|
1139
1206
|
from: next.toString(),
|
|
1140
1207
|
...(recheck ? { recheck: recheckIn(recheck) } : {}),
|
|
1208
|
+
...(refused === null ? {} : { refused: Number(refused) }),
|
|
1141
1209
|
search,
|
|
1142
1210
|
seen: credits.flatMap((credit) => (credit.at >= reach && credit.block !== null ? [{ at: credit.at, block: credit.block.toString(), hash: credit.hash, pass: credit.pass ?? 0, sender: credit.from }] : [])),
|
|
1211
|
+
size: Number(size),
|
|
1143
1212
|
start: floor.toString(),
|
|
1144
1213
|
};
|
|
1145
1214
|
|
|
@@ -1486,7 +1555,8 @@ async function depositPeers(archive, net, found, { amount, bounds, calls, suppli
|
|
|
1486
1555
|
}
|
|
1487
1556
|
|
|
1488
1557
|
/**
|
|
1489
|
-
* Where a scan's search stopped, without its
|
|
1558
|
+
* Where a scan's search stopped, with the width of its windows, without its
|
|
1559
|
+
* history.
|
|
1490
1560
|
*
|
|
1491
1561
|
* @param {Scan | null | undefined} scan
|
|
1492
1562
|
* @returns {Scan}
|
|
@@ -1499,6 +1569,8 @@ function searchOf(scan) {
|
|
|
1499
1569
|
|
|
1500
1570
|
if (scan.start !== undefined) out.start = scan.start;
|
|
1501
1571
|
if (scan.recheck !== undefined) out.recheck = scan.recheck;
|
|
1572
|
+
if (scan.size !== undefined) out.size = scan.size;
|
|
1573
|
+
if (scan.refused !== undefined) out.refused = scan.refused;
|
|
1502
1574
|
|
|
1503
1575
|
return out;
|
|
1504
1576
|
}
|
|
@@ -2072,6 +2144,12 @@ export async function serviceArrivals({
|
|
|
2072
2144
|
}
|
|
2073
2145
|
|
|
2074
2146
|
const live = triage(known, results, { now, waitMs });
|
|
2147
|
+
// The block times the app keeps for a route's chain: by its chain ID, or
|
|
2148
|
+
// the one cache given, where the network's service routes are all on one
|
|
2149
|
+
// chain.
|
|
2150
|
+
const oneChain = new Set(services.map((route) => route.chain?.chainId)).size === 1;
|
|
2151
|
+
/** @param {Route} route @returns {BlockTimes | undefined} */
|
|
2152
|
+
const timesOf = (route) => (typeof blockTimes === "function" ? (blockTimes(route.chain.chainId) ?? undefined) : oneChain ? blockTimes : undefined);
|
|
2075
2153
|
// The fee of each transfer the app gave, those no longer looked for too,
|
|
2076
2154
|
// and the block its payout can't be before.
|
|
2077
2155
|
const feeOf = new Map(routed.map((item) => [item.hash, item.fee ?? /** @type {Route & { kind: "service" }} */ (item.route).feeLux]));
|
|
@@ -2144,7 +2222,7 @@ export async function serviceArrivals({
|
|
|
2144
2222
|
queue: history.queue,
|
|
2145
2223
|
unsure,
|
|
2146
2224
|
},
|
|
2147
|
-
{ blockTimes, maxWindows, now, slackMs: CLOCK_SLACK_MS, url: String(rpcUrl ?? route.chain.rpcUrl), used, windowMs: CREDIT_WINDOW_MS }
|
|
2225
|
+
{ blockTimes: timesOf(route), maxWindows, now, slackMs: CLOCK_SLACK_MS, url: String(rpcUrl ?? route.chain.rpcUrl), used, windowMs: CREDIT_WINDOW_MS }
|
|
2148
2226
|
);
|
|
2149
2227
|
|
|
2150
2228
|
for (const [hash, arrival] of found) results.set(hash, withHistory(hash, arrival, given, history.states));
|
|
@@ -458,6 +458,13 @@ export type WithdrawalScan = {
|
|
|
458
458
|
target: number | null;
|
|
459
459
|
/** The blocks one search asks for. */
|
|
460
460
|
window: number;
|
|
461
|
+
/**
|
|
462
|
+
* Its passes over the final blocks, read again once they're all covered:
|
|
463
|
+
* the highest block the one under way reads next, downwards (`from`, null
|
|
464
|
+
* between two), when the next one begins (`at`), and when the blocks were
|
|
465
|
+
* first all covered (`since`), in milliseconds.
|
|
466
|
+
*/
|
|
467
|
+
recheck?: { from: number | null; at: number; since: number };
|
|
461
468
|
updatedAt?: number;
|
|
462
469
|
};
|
|
463
470
|
|
|
@@ -465,10 +472,17 @@ export type WithdrawalScan = {
|
|
|
465
472
|
* One run over an address's withdrawals, from the bridge's own event
|
|
466
473
|
* (`ETHBridgeInitiated`, by its sender), the newest final blocks first, a
|
|
467
474
|
* window at a time, narrower where the RPC asks for it, within
|
|
468
|
-
* `maxRequests
|
|
469
|
-
* be reorged away.
|
|
470
|
-
*
|
|
471
|
-
*
|
|
475
|
+
* `maxRequests`, the head's requests among them. Only final blocks count as
|
|
476
|
+
* covered: those above can still be reorged away. Each run reads the
|
|
477
|
+
* newest 50,000 final blocks, the new ones first, in as many windows as they
|
|
478
|
+
* take though in half its requests at most (while older blocks are still to
|
|
479
|
+
* be read, only once the final head has moved), and carries on below; under
|
|
480
|
+
* a final head lower than the last run's, what was covered and found above
|
|
481
|
+
* it is dropped, and read again. Once all the final blocks are
|
|
482
|
+
* covered, they're read again from the newest down, a pass at a time with
|
|
483
|
+
* the requests a run has left: the first an hour later, each after as long
|
|
484
|
+
* again as it has been since. A withdrawal is found once: one read again
|
|
485
|
+
* isn't found again.
|
|
472
486
|
*/
|
|
473
487
|
export declare function syncWithdrawals(options: {
|
|
474
488
|
/** The DuskEVM address that sent them. */
|