@openzeppelin/miden-multisig-client 0.17.0-rc.2 → 0.17.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/README.md +172 -47
- package/dist/account/builder.d.ts +2 -2
- package/dist/account/builder.d.ts.map +1 -1
- package/dist/account/builder.js +49 -24
- package/dist/account/builder.js.map +1 -1
- package/dist/connectivity.d.ts +2 -0
- package/dist/connectivity.d.ts.map +1 -1
- package/dist/connectivity.js +7 -4
- package/dist/connectivity.js.map +1 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/multisig/authArgErrors.d.ts +59 -0
- package/dist/multisig/authArgErrors.d.ts.map +1 -0
- package/dist/multisig/authArgErrors.js +115 -0
- package/dist/multisig/authArgErrors.js.map +1 -0
- package/dist/multisig/helpers.d.ts +5 -0
- package/dist/multisig/helpers.d.ts.map +1 -1
- package/dist/multisig/helpers.js +7 -2
- package/dist/multisig/helpers.js.map +1 -1
- package/dist/multisig.d.ts +140 -0
- package/dist/multisig.d.ts.map +1 -1
- package/dist/multisig.js +419 -12
- package/dist/multisig.js.map +1 -1
- package/dist/procedures.d.ts +1 -1
- package/dist/procedures.js +1 -1
- package/dist/recovery/proposalNoteImport.d.ts +173 -0
- package/dist/recovery/proposalNoteImport.d.ts.map +1 -0
- package/dist/recovery/proposalNoteImport.js +411 -0
- package/dist/recovery/proposalNoteImport.js.map +1 -0
- package/dist/recovery/publicNoteBackfill.d.ts +168 -0
- package/dist/recovery/publicNoteBackfill.d.ts.map +1 -0
- package/dist/recovery/publicNoteBackfill.js +368 -0
- package/dist/recovery/publicNoteBackfill.js.map +1 -0
- package/dist/recovery/recoverNotes.d.ts +147 -0
- package/dist/recovery/recoverNotes.d.ts.map +1 -0
- package/dist/recovery/recoverNotes.js +163 -0
- package/dist/recovery/recoverNotes.js.map +1 -0
- package/dist/recovery/transportDrain.d.ts +124 -0
- package/dist/recovery/transportDrain.d.ts.map +1 -0
- package/dist/recovery/transportDrain.js +262 -0
- package/dist/recovery/transportDrain.js.map +1 -0
- package/dist/transaction/consumeNotes.d.ts.map +1 -1
- package/dist/transaction/consumeNotes.js +4 -1
- package/dist/transaction/consumeNotes.js.map +1 -1
- package/dist/transaction/p2id.d.ts.map +1 -1
- package/dist/transaction/p2id.js +4 -1
- package/dist/transaction/p2id.js.map +1 -1
- package/dist/transaction/summary.d.ts +10 -4
- package/dist/transaction/summary.d.ts.map +1 -1
- package/dist/transaction/summary.js +13 -7
- package/dist/transaction/summary.js.map +1 -1
- package/dist/transaction/updateGuardian.js +4 -1
- package/dist/transaction/updateGuardian.js.map +1 -1
- package/dist/transaction/updateProcedureThreshold.js +4 -1
- package/dist/transaction/updateProcedureThreshold.js.map +1 -1
- package/dist/transaction/updateSigners.js +4 -1
- package/dist/transaction/updateSigners.js.map +1 -1
- package/dist/transaction.d.ts +1 -1
- package/dist/transaction.d.ts.map +1 -1
- package/dist/transaction.js +1 -1
- package/dist/transaction.js.map +1 -1
- package/package.json +14 -10
- package/src/account/builder.ts +87 -35
- package/src/connectivity.ts +9 -4
- package/src/index.ts +34 -1
- package/src/multisig/authArgErrors.ts +138 -0
- package/src/multisig/helpers.ts +7 -2
- package/src/multisig.ts +513 -20
- package/src/procedures.ts +1 -1
- package/src/recovery/proposalNoteImport.ts +548 -0
- package/src/recovery/publicNoteBackfill.ts +502 -0
- package/src/recovery/recoverNotes.ts +288 -0
- package/src/recovery/transportDrain.ts +330 -0
- package/src/transaction/consumeNotes.ts +4 -1
- package/src/transaction/p2id.ts +4 -1
- package/src/transaction/summary.ts +13 -7
- package/src/transaction/updateGuardian.ts +4 -1
- package/src/transaction/updateProcedureThreshold.ts +4 -1
- package/src/transaction/updateSigners.ts +4 -1
- package/src/transaction.ts +1 -1
- package/dist/account/builder.test.d.ts +0 -2
- package/dist/account/builder.test.d.ts.map +0 -1
- package/dist/account/builder.test.js +0 -166
- package/dist/account/builder.test.js.map +0 -1
- package/dist/account/masm/account-components/auth.d.ts +0 -2
- package/dist/account/masm/account-components/auth.d.ts.map +0 -1
- package/dist/account/masm/account-components/auth.js +0 -46
- package/dist/account/masm/account-components/auth.js.map +0 -1
- package/dist/account/masm/index.d.ts +0 -2
- package/dist/account/masm/index.d.ts.map +0 -1
- package/dist/account/masm/index.js +0 -4
- package/dist/account/masm/index.js.map +0 -1
- package/dist/account/masm.d.ts +0 -2
- package/dist/account/masm.d.ts.map +0 -1
- package/dist/account/masm.js +0 -4
- package/dist/account/masm.js.map +0 -1
- package/dist/account/storage.test.d.ts +0 -2
- package/dist/account/storage.test.d.ts.map +0 -1
- package/dist/account/storage.test.js +0 -73
- package/dist/account/storage.test.js.map +0 -1
- package/dist/client.test.d.ts +0 -2
- package/dist/client.test.d.ts.map +0 -1
- package/dist/client.test.js +0 -380
- package/dist/client.test.js.map +0 -1
- package/dist/connectivity.test.d.ts +0 -2
- package/dist/connectivity.test.d.ts.map +0 -1
- package/dist/connectivity.test.js +0 -61
- package/dist/connectivity.test.js.map +0 -1
- package/dist/inspector.test.d.ts +0 -2
- package/dist/inspector.test.d.ts.map +0 -1
- package/dist/inspector.test.js +0 -425
- package/dist/inspector.test.js.map +0 -1
- package/dist/lookupAuth.test.d.ts +0 -2
- package/dist/lookupAuth.test.d.ts.map +0 -1
- package/dist/lookupAuth.test.js +0 -138
- package/dist/lookupAuth.test.js.map +0 -1
- package/dist/multisig/consumeNotesErrors.test.d.ts +0 -2
- package/dist/multisig/consumeNotesErrors.test.d.ts.map +0 -1
- package/dist/multisig/consumeNotesErrors.test.js +0 -28
- package/dist/multisig/consumeNotesErrors.test.js.map +0 -1
- package/dist/multisig/helpers.test.d.ts +0 -2
- package/dist/multisig/helpers.test.d.ts.map +0 -1
- package/dist/multisig/helpers.test.js +0 -94
- package/dist/multisig/helpers.test.js.map +0 -1
- package/dist/multisig.test.d.ts +0 -2
- package/dist/multisig.test.d.ts.map +0 -1
- package/dist/multisig.test.js +0 -4112
- package/dist/multisig.test.js.map +0 -1
- package/dist/proposal/factory.test.d.ts +0 -2
- package/dist/proposal/factory.test.d.ts.map +0 -1
- package/dist/proposal/factory.test.js +0 -32
- package/dist/proposal/factory.test.js.map +0 -1
- package/dist/proposal/metadata.test.d.ts +0 -2
- package/dist/proposal/metadata.test.d.ts.map +0 -1
- package/dist/proposal/metadata.test.js +0 -193
- package/dist/proposal/metadata.test.js.map +0 -1
- package/dist/prover/config.test.d.ts +0 -2
- package/dist/prover/config.test.d.ts.map +0 -1
- package/dist/prover/config.test.js +0 -54
- package/dist/prover/config.test.js.map +0 -1
- package/dist/prover/errors.test.d.ts +0 -2
- package/dist/prover/errors.test.d.ts.map +0 -1
- package/dist/prover/errors.test.js +0 -29
- package/dist/prover/errors.test.js.map +0 -1
- package/dist/prover/retry.test.d.ts +0 -2
- package/dist/prover/retry.test.d.ts.map +0 -1
- package/dist/prover/retry.test.js +0 -20
- package/dist/prover/retry.test.js.map +0 -1
- package/dist/prover/workflow.test.d.ts +0 -2
- package/dist/prover/workflow.test.d.ts.map +0 -1
- package/dist/prover/workflow.test.js +0 -105
- package/dist/prover/workflow.test.js.map +0 -1
- package/dist/raw-client.test.d.ts +0 -2
- package/dist/raw-client.test.d.ts.map +0 -1
- package/dist/raw-client.test.js +0 -111
- package/dist/raw-client.test.js.map +0 -1
- package/dist/rpc/config.test.d.ts +0 -2
- package/dist/rpc/config.test.d.ts.map +0 -1
- package/dist/rpc/config.test.js +0 -24
- package/dist/rpc/config.test.js.map +0 -1
- package/dist/rpc/errors.test.d.ts +0 -2
- package/dist/rpc/errors.test.d.ts.map +0 -1
- package/dist/rpc/errors.test.js +0 -34
- package/dist/rpc/errors.test.js.map +0 -1
- package/dist/rpc/retry.test.d.ts +0 -2
- package/dist/rpc/retry.test.d.ts.map +0 -1
- package/dist/rpc/retry.test.js +0 -98
- package/dist/rpc/retry.test.js.map +0 -1
- package/dist/signers/ecdsa.test.d.ts +0 -2
- package/dist/signers/ecdsa.test.d.ts.map +0 -1
- package/dist/signers/ecdsa.test.js +0 -88
- package/dist/signers/ecdsa.test.js.map +0 -1
- package/dist/signers/falcon.test.d.ts +0 -2
- package/dist/signers/falcon.test.d.ts.map +0 -1
- package/dist/signers/falcon.test.js +0 -161
- package/dist/signers/falcon.test.js.map +0 -1
- package/dist/signers/miden-wallet.ecdsa-recovery.test.d.ts +0 -2
- package/dist/signers/miden-wallet.ecdsa-recovery.test.d.ts.map +0 -1
- package/dist/signers/miden-wallet.ecdsa-recovery.test.js +0 -148
- package/dist/signers/miden-wallet.ecdsa-recovery.test.js.map +0 -1
- package/dist/signers/miden-wallet.test.d.ts +0 -2
- package/dist/signers/miden-wallet.test.d.ts.map +0 -1
- package/dist/signers/miden-wallet.test.js +0 -164
- package/dist/signers/miden-wallet.test.js.map +0 -1
- package/dist/signers/para.test.d.ts +0 -2
- package/dist/signers/para.test.d.ts.map +0 -1
- package/dist/signers/para.test.js +0 -146
- package/dist/signers/para.test.js.map +0 -1
- package/dist/transaction/index.d.ts +0 -8
- package/dist/transaction/index.d.ts.map +0 -1
- package/dist/transaction/index.js +0 -7
- package/dist/transaction/index.js.map +0 -1
- package/dist/transaction/p2id.test.d.ts +0 -2
- package/dist/transaction/p2id.test.d.ts.map +0 -1
- package/dist/transaction/p2id.test.js +0 -246
- package/dist/transaction/p2id.test.js.map +0 -1
- package/dist/transaction/rpoRandomCoin.test.d.ts +0 -2
- package/dist/transaction/rpoRandomCoin.test.d.ts.map +0 -1
- package/dist/transaction/rpoRandomCoin.test.js +0 -52
- package/dist/transaction/rpoRandomCoin.test.js.map +0 -1
- package/dist/transaction/summary.test.d.ts +0 -2
- package/dist/transaction/summary.test.d.ts.map +0 -1
- package/dist/transaction/summary.test.js +0 -26
- package/dist/transaction/summary.test.js.map +0 -1
- package/dist/transaction.test.d.ts +0 -2
- package/dist/transaction.test.d.ts.map +0 -1
- package/dist/transaction.test.js +0 -122
- package/dist/transaction.test.js.map +0 -1
- package/dist/types/proposal.test.d.ts +0 -2
- package/dist/types/proposal.test.d.ts.map +0 -1
- package/dist/types/proposal.test.js +0 -56
- package/dist/types/proposal.test.js.map +0 -1
- package/dist/utils/digest.test.d.ts +0 -2
- package/dist/utils/digest.test.d.ts.map +0 -1
- package/dist/utils/digest.test.js +0 -48
- package/dist/utils/digest.test.js.map +0 -1
- package/dist/utils/ecdsa.test.d.ts +0 -2
- package/dist/utils/ecdsa.test.d.ts.map +0 -1
- package/dist/utils/ecdsa.test.js +0 -131
- package/dist/utils/ecdsa.test.js.map +0 -1
- package/dist/utils/encoding.test.d.ts +0 -2
- package/dist/utils/encoding.test.d.ts.map +0 -1
- package/dist/utils/encoding.test.js +0 -185
- package/dist/utils/encoding.test.js.map +0 -1
- package/dist/utils/key.test.d.ts +0 -2
- package/dist/utils/key.test.d.ts.map +0 -1
- package/dist/utils/key.test.js +0 -82
- package/dist/utils/key.test.js.map +0 -1
- package/dist/utils/signature.test.d.ts +0 -2
- package/dist/utils/signature.test.d.ts.map +0 -1
- package/dist/utils/signature.test.js +0 -164
- package/dist/utils/signature.test.js.map +0 -1
- package/dist/utils/word.test.d.ts +0 -2
- package/dist/utils/word.test.d.ts.map +0 -1
- package/dist/utils/word.test.js +0 -54
- package/dist/utils/word.test.js.map +0 -1
- package/masm/account_components/auth/guarded_multisig.masm +0 -42
- package/src/account/builder.test.ts +0 -238
- package/src/account/masm/account-components/auth.ts +0 -46
- package/src/account/masm/index.ts +0 -4
- package/src/account/masm.ts +0 -4
- package/src/account/storage.test.ts +0 -90
- package/src/client.test.ts +0 -473
- package/src/connectivity.test.ts +0 -67
- package/src/inspector.test.ts +0 -542
- package/src/lookupAuth.test.ts +0 -185
- package/src/multisig/consumeNotesErrors.test.ts +0 -43
- package/src/multisig/helpers.test.ts +0 -114
- package/src/multisig.test.ts +0 -5008
- package/src/proposal/factory.test.ts +0 -40
- package/src/proposal/metadata.test.ts +0 -235
- package/src/prover/config.test.ts +0 -90
- package/src/prover/errors.test.ts +0 -61
- package/src/prover/retry.test.ts +0 -38
- package/src/prover/workflow.test.ts +0 -145
- package/src/raw-client.test.ts +0 -171
- package/src/rpc/config.test.ts +0 -45
- package/src/rpc/errors.test.ts +0 -69
- package/src/rpc/retry.test.ts +0 -144
- package/src/signers/ecdsa.test.ts +0 -111
- package/src/signers/falcon.test.ts +0 -197
- package/src/signers/miden-wallet.ecdsa-recovery.test.ts +0 -201
- package/src/signers/miden-wallet.test.ts +0 -206
- package/src/signers/para.test.ts +0 -186
- package/src/transaction/index.ts +0 -13
- package/src/transaction/p2id.test.ts +0 -371
- package/src/transaction/rpoRandomCoin.test.ts +0 -64
- package/src/transaction/summary.test.ts +0 -32
- package/src/transaction.test.ts +0 -142
- package/src/types/proposal.test.ts +0 -68
- package/src/utils/digest.test.ts +0 -55
- package/src/utils/ecdsa.test.ts +0 -164
- package/src/utils/encoding.test.ts +0 -233
- package/src/utils/key.test.ts +0 -91
- package/src/utils/signature.test.ts +0 -210
- package/src/utils/word.test.ts +0 -64
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Historical public-note backfill by tag.
|
|
3
|
+
*
|
|
4
|
+
* Public notes addressed to an account are on chain, but normal forward sync
|
|
5
|
+
* starts from the store's **global** cursor: in a shared dirty store the
|
|
6
|
+
* cursor may already be past blocks containing a recovered account's notes,
|
|
7
|
+
* and a fresh store has no efficient path to them at all. This module
|
|
8
|
+
* rescans a historical block range with the account's standard note tag and
|
|
9
|
+
* imports what it finds with on-chain inclusion proofs, without ever
|
|
10
|
+
* touching the global sync height.
|
|
11
|
+
*/
|
|
12
|
+
import { AccountId, Endpoint, NoteScript, NoteTag, NoteType, RpcClient, } from '@miden-sdk/miden-sdk';
|
|
13
|
+
import { errorMessage } from '../connectivity.js';
|
|
14
|
+
import { collectExistingRecords, detailsKeyOf, errorDetail, importNoteWithProof, reclassifyConsumedImports, } from './proposalNoteImport.js';
|
|
15
|
+
import { getRawMidenClient, requireMidenRpcEndpoint } from '../raw-client.js';
|
|
16
|
+
import { resolveRpcConfig } from '../rpc/config.js';
|
|
17
|
+
import { isTransientRpcError } from '../rpc/errors.js';
|
|
18
|
+
import { retryRpcRead } from '../rpc/retry.js';
|
|
19
|
+
import { normalizeHexWord } from '../utils/encoding.js';
|
|
20
|
+
/**
|
|
21
|
+
* `RpcError::PaginationError`'s Display prefix in the WASM error chain: the
|
|
22
|
+
* node caps internal pagination per `syncNotes` request, and the scan splits
|
|
23
|
+
* the range client-side when it trips. Exported for the drift-guard test,
|
|
24
|
+
* which pins it against the shipped WASM binary.
|
|
25
|
+
*/
|
|
26
|
+
export const RPC_PAGINATION_FRAGMENT = 'rpc pagination error';
|
|
27
|
+
/**
|
|
28
|
+
* Upper bound on `syncNotes` requests per backfill. Splitting around the
|
|
29
|
+
* node's pagination cap halves ranges, so the budget is only approachable
|
|
30
|
+
* when nearly every sub-range is dense enough to trip the cap; exhausting it
|
|
31
|
+
* reports the remaining ranges as uncovered instead of scanning forever.
|
|
32
|
+
*/
|
|
33
|
+
const MAX_SCAN_REQUESTS = 128;
|
|
34
|
+
/** Block numbers are u32 on chain; out-of-range JS numbers would silently
|
|
35
|
+
* wrap modulo 2^32 at the WASM boundary and scan the wrong range. */
|
|
36
|
+
const MAX_BLOCK_NUMBER = 4_294_967_295;
|
|
37
|
+
/**
|
|
38
|
+
* The requested scan range is invalid (out-of-range bound, or inverted
|
|
39
|
+
* against the resolved chain tip) — a caller error that retrying cannot fix,
|
|
40
|
+
* as opposed to the transient chain-tip-lookup failures this function also
|
|
41
|
+
* throws. `Multisig.recoverNotes` keys its problem retryability on this,
|
|
42
|
+
* mirroring the Rust orchestrator's `InvalidConfig` check.
|
|
43
|
+
*/
|
|
44
|
+
export class BackfillRangeError extends Error {
|
|
45
|
+
}
|
|
46
|
+
function requireBlockNumber(name, value) {
|
|
47
|
+
if (!Number.isInteger(value) || value < 0 || value > MAX_BLOCK_NUMBER) {
|
|
48
|
+
throw new BackfillRangeError(`${name} must be an integer in [0, ${MAX_BLOCK_NUMBER}], got ${value}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Static relevance screen for a discovered public note. The Rust SDK screens
|
|
53
|
+
* with the execution-based `NoteScreener` normal sync uses; the WASM surface
|
|
54
|
+
* does not expose it, so this mirrors its verdict for the well-known note
|
|
55
|
+
* scripts: a note is `relevant` when it is a P2ID/P2IDE note whose target
|
|
56
|
+
* (or P2IDE reclaimer) is the scanned account, and `irrelevant` when it is
|
|
57
|
+
* one of those scripts addressed at someone else. Notes with other scripts
|
|
58
|
+
* are `unscreenable` — this screen cannot judge them, and they are
|
|
59
|
+
* conservatively not imported (tags are shared, truncated filters, and
|
|
60
|
+
* importing unscreened tag matches would let anyone pollute the store), but
|
|
61
|
+
* the report counts them separately so "screened out" and "not screenable"
|
|
62
|
+
* stay distinguishable. Exported for the drift-guard test, which pins the
|
|
63
|
+
* root and storage-layout assumptions against real WASM-built notes.
|
|
64
|
+
*/
|
|
65
|
+
export function screenNoteForAccount(note, account) {
|
|
66
|
+
const root = normalizeHexWord(note.script().root().toHex());
|
|
67
|
+
const items = note.recipient().storage().items();
|
|
68
|
+
const prefix = account.prefix().asInt();
|
|
69
|
+
const suffix = account.suffix().asInt();
|
|
70
|
+
const accountAt = (index) => items.length > index + 1 &&
|
|
71
|
+
items[index].asInt() === suffix &&
|
|
72
|
+
items[index + 1].asInt() === prefix;
|
|
73
|
+
if (root === normalizeHexWord(NoteScript.p2id().root().toHex())) {
|
|
74
|
+
// P2ID note storage: [target.suffix, target.prefix].
|
|
75
|
+
return accountAt(0) ? 'relevant' : 'irrelevant';
|
|
76
|
+
}
|
|
77
|
+
if (root === normalizeHexWord(NoteScript.p2ide().root().toHex())) {
|
|
78
|
+
// P2IDE note storage: [reclaimer.suffix, reclaimer.prefix,
|
|
79
|
+
// target.suffix, target.prefix, reclaim, timelock].
|
|
80
|
+
return accountAt(2) || accountAt(0) ? 'relevant' : 'irrelevant';
|
|
81
|
+
}
|
|
82
|
+
return 'unscreenable';
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Scans a historical block range for public notes addressed at an account's
|
|
86
|
+
* standard note tag and imports what it finds with their on-chain inclusion
|
|
87
|
+
* proofs. Counterpart of
|
|
88
|
+
* `MultisigClient::backfill_public_notes_by_tag` in the Rust SDK.
|
|
89
|
+
*
|
|
90
|
+
* Use after account recovery: normal forward sync starts from the store's
|
|
91
|
+
* **global** cursor, so in a shared dirty store the cursor may already be
|
|
92
|
+
* past blocks containing the recovered account's notes, and a fresh store
|
|
93
|
+
* would need to replay the whole chain state to see them. The scan is
|
|
94
|
+
* tag-scoped and its cost grows with the number of matching notes, not the
|
|
95
|
+
* range length, which makes genesis an acceptable default lower
|
|
96
|
+
* bound. The global sync height is never touched — run normal sync
|
|
97
|
+
* afterwards to verify the imported notes. The store must have synced at
|
|
98
|
+
* least once (`Multisig.recoverNotes` syncs the chain before this strategy
|
|
99
|
+
* runs): importing a proof into a store that has never seen the chain
|
|
100
|
+
* fails, and such failures surface as `failed` outcomes.
|
|
101
|
+
*
|
|
102
|
+
* Notes are discovered by tag only — a best-effort filter: notes sent with
|
|
103
|
+
* unrelated custom tags are outside this scan's guarantee, and, like normal
|
|
104
|
+
* sync, every new discovery is screened for relevance before import —
|
|
105
|
+
* tag-colliding notes the account cannot consume are counted as
|
|
106
|
+
* `skippedIrrelevant` instead of polluting the store. This SDK screens
|
|
107
|
+
* statically against the well-known P2ID/P2IDE scripts (the WASM surface
|
|
108
|
+
* does not expose the execution-based screener the Rust SDK uses), so notes
|
|
109
|
+
* with custom scripts are conservatively not imported and counted as
|
|
110
|
+
* `skippedUnscreenable`. Only public notes can be
|
|
111
|
+
* rebuilt from chain data; private matches are counted as `skippedPrivate`
|
|
112
|
+
* and are covered by the transport drain and proposal-import primitives
|
|
113
|
+
* instead.
|
|
114
|
+
*
|
|
115
|
+
* A range dense enough to trip the node's internal pagination cap is split
|
|
116
|
+
* client-side and rescanned as narrower requests; ranges that still cannot
|
|
117
|
+
* be covered are reported in {@link PublicBackfillReport.uncovered} rather
|
|
118
|
+
* than failing the recovery flow. This function throws only when the scan
|
|
119
|
+
* range itself cannot be established (chain-tip lookup failed, an invalid
|
|
120
|
+
* account ID, a block bound that is not a u32 integer, or
|
|
121
|
+
* `fromBlock > toBlock`).
|
|
122
|
+
*
|
|
123
|
+
* Prefer the `Multisig.backfillPublicNotesByTag` convenience method, which
|
|
124
|
+
* reuses the client's endpoint and retry configuration.
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* ```typescript
|
|
128
|
+
* const report = await multisig.backfillPublicNotesByTag();
|
|
129
|
+
* console.log(report.discovered, 'discovered,', report.outcomes.length, 'public');
|
|
130
|
+
* await multisig.syncState(); // verifies the imported notes
|
|
131
|
+
* ```
|
|
132
|
+
*/
|
|
133
|
+
export async function backfillPublicNotesByTag(midenClient, options) {
|
|
134
|
+
const midenRpcEndpoint = requireMidenRpcEndpoint(options.midenRpcEndpoint);
|
|
135
|
+
const rpcConfig = resolveRpcConfig(options.rpc);
|
|
136
|
+
const webClient = await getRawMidenClient(midenClient, midenRpcEndpoint);
|
|
137
|
+
const rpcClient = new RpcClient(new Endpoint(midenRpcEndpoint));
|
|
138
|
+
// Parse eagerly so a malformed account ID throws before any network work.
|
|
139
|
+
AccountId.fromHex(options.accountId);
|
|
140
|
+
const from = options.fromBlock ?? 0;
|
|
141
|
+
requireBlockNumber('fromBlock', from);
|
|
142
|
+
let to;
|
|
143
|
+
if (options.toBlock !== undefined) {
|
|
144
|
+
requireBlockNumber('toBlock', options.toBlock);
|
|
145
|
+
to = options.toBlock;
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
try {
|
|
149
|
+
const tip = await retryRpcRead(() => rpcClient.getBlockHeaderByNumber(), rpcConfig);
|
|
150
|
+
to = tip.blockNum();
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
throw new Error(`failed to resolve the chain tip for the backfill scan: ${errorDetail(error)}`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (from > to) {
|
|
157
|
+
throw new BackfillRangeError(`backfill range is inverted: fromBlock ${from} > toBlock ${to}`);
|
|
158
|
+
}
|
|
159
|
+
// Work queue of inclusive sub-ranges, split in half whenever the node
|
|
160
|
+
// reports its pagination cap for one of them. WASM call arguments are
|
|
161
|
+
// consumed by the bridge, so the tag is rebuilt per request.
|
|
162
|
+
const scanTag = () => NoteTag.withAccountTarget(AccountId.fromHex(options.accountId));
|
|
163
|
+
const queue = [[from, to]];
|
|
164
|
+
const discovered = new Map();
|
|
165
|
+
const uncovered = [];
|
|
166
|
+
const scanReasons = [];
|
|
167
|
+
let retryable = false;
|
|
168
|
+
let requests = 0;
|
|
169
|
+
let budgetExhausted = false;
|
|
170
|
+
while (queue.length > 0) {
|
|
171
|
+
const [lo, hi] = queue.shift();
|
|
172
|
+
if (requests >= MAX_SCAN_REQUESTS) {
|
|
173
|
+
budgetExhausted = true;
|
|
174
|
+
uncovered.push({ from: lo, to: hi });
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
requests += 1;
|
|
178
|
+
try {
|
|
179
|
+
const info = await retryRpcRead(() => rpcClient.syncNotes(lo, hi, [scanTag()]), rpcConfig);
|
|
180
|
+
for (const committed of info.notes()) {
|
|
181
|
+
const idHex = normalizeHexWord(committed.noteId().toString());
|
|
182
|
+
if (!discovered.has(idHex)) {
|
|
183
|
+
// The wrapper is kept (not a one-shot accessor result) so fresh
|
|
184
|
+
// NoteId handles can be minted per body-fetch attempt below.
|
|
185
|
+
discovered.set(idHex, committed);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
catch (error) {
|
|
190
|
+
// The node caps internal pagination per request rather than
|
|
191
|
+
// truncating; a single-block range cannot be split further (and
|
|
192
|
+
// cannot realistically hold that many pages), so only splittable
|
|
193
|
+
// ranges take this branch.
|
|
194
|
+
if (errorMessage(error).toLowerCase().includes(RPC_PAGINATION_FRAGMENT) && lo < hi) {
|
|
195
|
+
const mid = lo + Math.floor((hi - lo) / 2);
|
|
196
|
+
queue.unshift([lo, mid], [mid + 1, hi]);
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
retryable ||= isTransientRpcError(error);
|
|
200
|
+
scanReasons.push(`blocks [${lo}, ${hi}]: ${errorDetail(error)}`);
|
|
201
|
+
uncovered.push({ from: lo, to: hi });
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
if (budgetExhausted) {
|
|
205
|
+
retryable = true;
|
|
206
|
+
scanReasons.push(`scan budget of ${MAX_SCAN_REQUESTS} requests exhausted while splitting around the node's pagination cap; rerun the backfill over the uncovered ranges`);
|
|
207
|
+
}
|
|
208
|
+
const publicNotes = [];
|
|
209
|
+
for (const [idHex, committed] of discovered) {
|
|
210
|
+
if (committed.noteType() === NoteType.Public) {
|
|
211
|
+
publicNotes.push({ idHex, committed });
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
const skippedPrivate = discovered.size - publicNotes.length;
|
|
215
|
+
let skippedIrrelevant = 0;
|
|
216
|
+
let skippedUnscreenable = 0;
|
|
217
|
+
const outcomes = [];
|
|
218
|
+
const buildReport = () => {
|
|
219
|
+
let reason;
|
|
220
|
+
if (scanReasons.length > 0) {
|
|
221
|
+
reason =
|
|
222
|
+
scanReasons.length <= 3
|
|
223
|
+
? scanReasons.join('; ')
|
|
224
|
+
: `${scanReasons.slice(0, 3).join('; ')}; …and ${scanReasons.length - 3} more`;
|
|
225
|
+
}
|
|
226
|
+
return {
|
|
227
|
+
scannedFrom: from,
|
|
228
|
+
scannedTo: to,
|
|
229
|
+
discovered: discovered.size,
|
|
230
|
+
skippedPrivate,
|
|
231
|
+
skippedIrrelevant,
|
|
232
|
+
skippedUnscreenable,
|
|
233
|
+
outcomes,
|
|
234
|
+
uncovered,
|
|
235
|
+
// Rerunning can help when scan ranges were left uncovered OR when any
|
|
236
|
+
// per-note outcome is itself retryable — surface both at report level
|
|
237
|
+
// so orchestration keyed on the report alone reruns when it should.
|
|
238
|
+
retryable: retryable || outcomes.some((outcome) => outcome.retryable === true),
|
|
239
|
+
...(reason === undefined ? {} : { reason }),
|
|
240
|
+
};
|
|
241
|
+
};
|
|
242
|
+
const pending = [];
|
|
243
|
+
if (publicNotes.length > 0) {
|
|
244
|
+
try {
|
|
245
|
+
// One batched body fetch — the upstream client chunks internally by
|
|
246
|
+
// the node's negotiated note-ids limit, and the node returns full
|
|
247
|
+
// bodies for public notes, so the scan's ID + proof is all this path
|
|
248
|
+
// needs. The WASM bridge consumes call arguments, so fresh NoteId
|
|
249
|
+
// handles are minted from the kept wrappers on every retry attempt.
|
|
250
|
+
const fetchedNotes = await retryRpcRead(() => rpcClient.getNotesById(publicNotes.map((candidate) => candidate.committed.noteId())), rpcConfig);
|
|
251
|
+
const bodies = new Map();
|
|
252
|
+
for (const fetched of fetchedNotes) {
|
|
253
|
+
if (fetched.note) {
|
|
254
|
+
bodies.set(normalizeHexWord(fetched.noteId.toString()), {
|
|
255
|
+
note: fetched.note,
|
|
256
|
+
proof: fetched.inclusionProof,
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
for (const { idHex } of publicNotes) {
|
|
261
|
+
const body = bodies.get(idHex);
|
|
262
|
+
if (body) {
|
|
263
|
+
pending.push({
|
|
264
|
+
idHex,
|
|
265
|
+
note: body.note,
|
|
266
|
+
proof: body.proof,
|
|
267
|
+
detailsKey: detailsKeyOf(normalizeHexWord(body.note.recipient().digest().toHex()), body.note.assets()),
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
// Discovered as public by the scan but returned without a body —
|
|
272
|
+
// not expected for a committed public note.
|
|
273
|
+
outcomes.push({
|
|
274
|
+
identifier: idHex,
|
|
275
|
+
source: 'backfill',
|
|
276
|
+
status: 'failed',
|
|
277
|
+
retryable: true,
|
|
278
|
+
reason: 'the node did not return a body for this public note',
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
catch (error) {
|
|
284
|
+
const fetchRetryable = isTransientRpcError(error);
|
|
285
|
+
const reason = `failed to fetch note bodies: ${errorDetail(error)}`;
|
|
286
|
+
for (const { idHex } of publicNotes) {
|
|
287
|
+
outcomes.push({
|
|
288
|
+
identifier: idHex,
|
|
289
|
+
source: 'backfill',
|
|
290
|
+
status: 'failed',
|
|
291
|
+
retryable: fetchRetryable,
|
|
292
|
+
reason,
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
if (pending.length === 0) {
|
|
298
|
+
return buildReport();
|
|
299
|
+
}
|
|
300
|
+
let existing;
|
|
301
|
+
try {
|
|
302
|
+
existing = await collectExistingRecords(webClient);
|
|
303
|
+
}
|
|
304
|
+
catch (error) {
|
|
305
|
+
const reason = `failed to read local store: ${errorDetail(error)}`;
|
|
306
|
+
for (const candidate of pending) {
|
|
307
|
+
outcomes.push({
|
|
308
|
+
identifier: candidate.idHex,
|
|
309
|
+
source: 'backfill',
|
|
310
|
+
status: 'failed',
|
|
311
|
+
reason,
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
return buildReport();
|
|
315
|
+
}
|
|
316
|
+
// Provisionally `imported` outcomes, re-classified in one batched
|
|
317
|
+
// post-import state check below.
|
|
318
|
+
const screenAccount = AccountId.fromHex(options.accountId);
|
|
319
|
+
const imported = [];
|
|
320
|
+
for (const candidate of pending) {
|
|
321
|
+
// Skip decisions key on an exact note-ID match only: a details-key
|
|
322
|
+
// match is a lossy approximation (see {@link detailsKeyOf} in the
|
|
323
|
+
// proposal-import module), and the upstream import dedupes exactly by
|
|
324
|
+
// the real details commitment, so importing "again" is safe while
|
|
325
|
+
// pre-skipping on the approximation could silently drop a genuinely
|
|
326
|
+
// new note. Unlike the proposal import, a proof-less (expected) record
|
|
327
|
+
// is NOT skipped here even on an ID match: this primitive exists
|
|
328
|
+
// because forward sync will never revisit the note's block, so the
|
|
329
|
+
// freshly fetched proof is applied to upgrade the record in place (the
|
|
330
|
+
// WASM import handles existing records).
|
|
331
|
+
const record = existing.get(candidate.idHex);
|
|
332
|
+
if (record && (record.isConsumed() || record.inclusionProof() !== undefined)) {
|
|
333
|
+
outcomes.push({
|
|
334
|
+
identifier: candidate.idHex,
|
|
335
|
+
source: 'backfill',
|
|
336
|
+
status: record.isConsumed() ? 'already-consumed' : 'already-present',
|
|
337
|
+
});
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
// Screen genuinely new discoveries for relevance, exactly like normal
|
|
341
|
+
// sync does before it stores a tag match. Records the store already
|
|
342
|
+
// tracks (by ID, or a metadata-less record matching on details) are
|
|
343
|
+
// material the user chose to track and skip the screen.
|
|
344
|
+
if (!record && !existing.has(candidate.detailsKey)) {
|
|
345
|
+
const verdict = screenNoteForAccount(candidate.note, screenAccount);
|
|
346
|
+
if (verdict === 'irrelevant') {
|
|
347
|
+
skippedIrrelevant += 1;
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
if (verdict === 'unscreenable') {
|
|
351
|
+
skippedUnscreenable += 1;
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
const { outcome, wasImported } = await importNoteWithProof(webClient, 'backfill', candidate.idHex, candidate.note, candidate.proof);
|
|
356
|
+
if (wasImported) {
|
|
357
|
+
imported.push({
|
|
358
|
+
index: outcomes.length,
|
|
359
|
+
idHex: candidate.idHex,
|
|
360
|
+
detailsKey: candidate.detailsKey,
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
outcomes.push(outcome);
|
|
364
|
+
}
|
|
365
|
+
await reclassifyConsumedImports(webClient, imported, outcomes);
|
|
366
|
+
return buildReport();
|
|
367
|
+
}
|
|
368
|
+
//# sourceMappingURL=publicNoteBackfill.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publicNoteBackfill.js","sourceRoot":"","sources":["../../src/recovery/publicNoteBackfill.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wallet-facing orchestration of the note-recovery primitives.
|
|
3
|
+
*
|
|
4
|
+
* After key-based recovery the local Miden store starts empty (or, in a
|
|
5
|
+
* shared store, its global cursors may already be past this account's
|
|
6
|
+
* notes). Each recovery primitive rescans one source normal forward sync
|
|
7
|
+
* would skip: the private-note transport backlog, the notes embedded in
|
|
8
|
+
* pending consume-notes proposals, and the chain's historical public notes
|
|
9
|
+
* for the account's tag. `Multisig.recoverNotes` runs them as a single flow
|
|
10
|
+
* and finishes with a normal sync so recovered notes are verified and ready
|
|
11
|
+
* to consume.
|
|
12
|
+
*
|
|
13
|
+
* The TS counterpart of the Rust SDK's `MultisigClient::recover_notes`,
|
|
14
|
+
* with matching semantics and report shape.
|
|
15
|
+
*/
|
|
16
|
+
import type { NoteImportOutcome } from './proposalNoteImport.js';
|
|
17
|
+
import { type PublicBackfillReport } from './publicNoteBackfill.js';
|
|
18
|
+
import type { TransportRecoveryReport } from './transportDrain.js';
|
|
19
|
+
/** One step of the note-recovery flow, used to attribute problems. */
|
|
20
|
+
export type RecoveryStep = 'transport-drain' | 'proposal-import' | 'public-backfill' | 'sync';
|
|
21
|
+
/**
|
|
22
|
+
* A step of the recovery flow that could not run (or, for the final sync,
|
|
23
|
+
* did not finish). The flow always continues with the remaining steps; a
|
|
24
|
+
* problem here means the corresponding report field is absent (or `synced`
|
|
25
|
+
* is `false`) and rerunning the flow may recover more.
|
|
26
|
+
*/
|
|
27
|
+
export interface RecoveryStepProblem {
|
|
28
|
+
/** The step that failed. */
|
|
29
|
+
step: RecoveryStep;
|
|
30
|
+
/** Human-readable cause. */
|
|
31
|
+
reason: string;
|
|
32
|
+
/**
|
|
33
|
+
* Whether rerunning the flow can plausibly make this step succeed.
|
|
34
|
+
* Failures reaching this level are I/O failures against GUARDIAN, the
|
|
35
|
+
* node, or the local store; all but local-store failures are marked
|
|
36
|
+
* retryable. The flow is idempotent, so retrying is always safe.
|
|
37
|
+
*/
|
|
38
|
+
retryable: boolean;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Result of `Multisig.recoverNotes`.
|
|
42
|
+
*
|
|
43
|
+
* Each strategy's field holds its primitive's own report and is present
|
|
44
|
+
* exactly when the strategy was enabled and ran; an enabled strategy that
|
|
45
|
+
* could not run at all is a {@link RecoveryStepProblem} instead. Step
|
|
46
|
+
* problems never abort the flow.
|
|
47
|
+
*/
|
|
48
|
+
export interface NoteRecoveryReport {
|
|
49
|
+
/** Report of the transport backlog drain, when that strategy ran. */
|
|
50
|
+
transport?: TransportRecoveryReport;
|
|
51
|
+
/**
|
|
52
|
+
* Per-note outcomes of the proposal-embedded import, when that strategy
|
|
53
|
+
* ran.
|
|
54
|
+
*/
|
|
55
|
+
proposalImport?: NoteImportOutcome[];
|
|
56
|
+
/** Report of the historical public-note backfill, when that strategy ran. */
|
|
57
|
+
backfill?: PublicBackfillReport;
|
|
58
|
+
/** Steps that could not run or finish. Empty on a fully clean flow. */
|
|
59
|
+
problems: RecoveryStepProblem[];
|
|
60
|
+
/** Whether the final verifying sync completed. */
|
|
61
|
+
synced: boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Total note records newly added to the local store by this flow: the
|
|
64
|
+
* drain's imports plus every `imported` outcome from the proposal import
|
|
65
|
+
* and the backfill.
|
|
66
|
+
*/
|
|
67
|
+
imported: number;
|
|
68
|
+
/**
|
|
69
|
+
* Whether rerunning the flow can plausibly recover more: any step
|
|
70
|
+
* problem, per-note outcome, or strategy report marked retryable.
|
|
71
|
+
*/
|
|
72
|
+
retryable: boolean;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Options for `Multisig.recoverNotes`. The default runs every strategy over
|
|
76
|
+
* the full chain history and syncs afterwards.
|
|
77
|
+
*/
|
|
78
|
+
export interface RecoverNotesOptions {
|
|
79
|
+
/**
|
|
80
|
+
* Rescan the private-note transport backlog (the standalone
|
|
81
|
+
* `drainPrivateNoteBacklog`). Default `true`.
|
|
82
|
+
*/
|
|
83
|
+
transportDrain?: boolean;
|
|
84
|
+
/**
|
|
85
|
+
* Import the notes embedded in pending consume-notes proposals
|
|
86
|
+
* (`Multisig.importNotesFromProposals`). Default `true`.
|
|
87
|
+
*/
|
|
88
|
+
proposalImport?: boolean;
|
|
89
|
+
/**
|
|
90
|
+
* Scan chain history for public notes addressed at the account's tag
|
|
91
|
+
* (`Multisig.backfillPublicNotesByTag`). Default `true`.
|
|
92
|
+
*/
|
|
93
|
+
publicBackfill?: boolean;
|
|
94
|
+
/** First block of the backfill scan; defaults to genesis. */
|
|
95
|
+
fromBlock?: number;
|
|
96
|
+
/** Last block of the backfill scan; defaults to the current chain tip. */
|
|
97
|
+
toBlock?: number;
|
|
98
|
+
/**
|
|
99
|
+
* Run a normal sync after the strategies so imported notes are verified
|
|
100
|
+
* and show up as consumable. Default `true`.
|
|
101
|
+
*/
|
|
102
|
+
syncAfter?: boolean;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* The guardian-switch slice of {@link RecoverNotesOptions}: only the
|
|
106
|
+
* proposal-embedded note import runs. Internal — the public entry point is
|
|
107
|
+
* `Multisig.preservePreSwitchProposalNotes`, which adds the switch-specific
|
|
108
|
+
* safety contract (timeout, cancellation, warnings) around this slice. The
|
|
109
|
+
* `satisfies` clause forces every non-range option to be listed, so adding
|
|
110
|
+
* a recovery strategy fails to compile until the switch path decides on it.
|
|
111
|
+
*/
|
|
112
|
+
export declare const GUARDIAN_SWITCH_RECOVERY_OPTIONS: {
|
|
113
|
+
transportDrain: false;
|
|
114
|
+
proposalImport: true;
|
|
115
|
+
publicBackfill: false;
|
|
116
|
+
syncAfter: false;
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* The strategy implementations `runNoteRecovery` orchestrates. `Multisig`
|
|
120
|
+
* wires these to the SDK primitives; tests can substitute stubs.
|
|
121
|
+
*/
|
|
122
|
+
export interface NoteRecoverySteps {
|
|
123
|
+
/** Drain the private-note transport backlog. */
|
|
124
|
+
transportDrain: () => Promise<TransportRecoveryReport>;
|
|
125
|
+
/** Import notes embedded in pending consume-notes proposals. */
|
|
126
|
+
proposalImport: () => Promise<NoteImportOutcome[]>;
|
|
127
|
+
/** Backfill historical public notes by tag. */
|
|
128
|
+
publicBackfill: () => Promise<PublicBackfillReport>;
|
|
129
|
+
/** Run the final verifying sync. */
|
|
130
|
+
sync: () => Promise<void>;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Runs the enabled recovery strategies in order — transport drain, proposal
|
|
134
|
+
* import, public backfill, final sync — folding each strategy-level throw
|
|
135
|
+
* into a {@link RecoveryStepProblem} so no step failure aborts the flow.
|
|
136
|
+
*
|
|
137
|
+
* The optional `cancelled` token makes the run cooperatively cancellable:
|
|
138
|
+
* the orchestrator checks it before each step, and a
|
|
139
|
+
* {@link RecoveryCancelledError} from inside a step is recorded as one
|
|
140
|
+
* non-retryable problem on the interrupted step, after which no further
|
|
141
|
+
* step runs — cancellation never masquerades as a GUARDIAN or node outage.
|
|
142
|
+
*
|
|
143
|
+
* Throws only for an inverted backfill range (a caller error). See
|
|
144
|
+
* `Multisig.recoverNotes` for the wallet-facing entry point.
|
|
145
|
+
*/
|
|
146
|
+
export declare function runNoteRecovery(options: RecoverNotesOptions, steps: NoteRecoverySteps, cancelled?: () => boolean): Promise<NoteRecoveryReport>;
|
|
147
|
+
//# sourceMappingURL=recoverNotes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recoverNotes.d.ts","sourceRoot":"","sources":["../../src/recovery/recoverNotes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,EAAsB,KAAK,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AACxF,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAEnE,sEAAsE;AACtE,MAAM,MAAM,YAAY,GACpB,iBAAiB,GACjB,iBAAiB,GACjB,iBAAiB,GACjB,MAAM,CAAC;AAEX;;;;;GAKG;AACH,MAAM,WAAW,mBAAmB;IAClC,4BAA4B;IAC5B,IAAI,EAAE,YAAY,CAAC;IACnB,4BAA4B;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,kBAAkB;IACjC,qEAAqE;IACrE,SAAS,CAAC,EAAE,uBAAuB,CAAC;IACpC;;;OAGG;IACH,cAAc,CAAC,EAAE,iBAAiB,EAAE,CAAC;IACrC,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,oBAAoB,CAAC;IAChC,uEAAuE;IACvE,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAChC,kDAAkD;IAClD,MAAM,EAAE,OAAO,CAAC;IAChB;;;;OAIG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,6DAA6D;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0EAA0E;IAC1E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,gCAAgC;;;;;CAK2B,CAAC;AAEzE;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,gDAAgD;IAChD,cAAc,EAAE,MAAM,OAAO,CAAC,uBAAuB,CAAC,CAAC;IACvD,gEAAgE;IAChE,cAAc,EAAE,MAAM,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAAC;IACnD,+CAA+C;IAC/C,cAAc,EAAE,MAAM,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACpD,oCAAoC;IACpC,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAMD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,mBAAmB,EAC5B,KAAK,EAAE,iBAAiB,EACxB,SAAS,CAAC,EAAE,MAAM,OAAO,GACxB,OAAO,CAAC,kBAAkB,CAAC,CAwH7B"}
|