@aztec/slasher 0.87.2-nightly.20250524

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 ADDED
@@ -0,0 +1,5 @@
1
+ # Slasher
2
+
3
+ We slashin.
4
+
5
+ See [slasher_client.ts](./src/slasher_client.ts) for more details.
@@ -0,0 +1,40 @@
1
+ import type { ConfigMappingsType } from '@aztec/foundation/config';
2
+ import { EthAddress } from '@aztec/foundation/eth-address';
3
+ import type { TypedEventEmitter } from '@aztec/foundation/types';
4
+ export declare enum Offence {
5
+ UNKNOWN = 0,
6
+ EPOCH_PRUNE = 1,
7
+ INACTIVITY = 2
8
+ }
9
+ export declare const OffenceToBigInt: Record<Offence, bigint>;
10
+ export declare function bigIntToOffence(offense: bigint): Offence;
11
+ export declare const WANT_TO_SLASH_EVENT: "wantToSlash";
12
+ export interface WantToSlashArgs {
13
+ validators: `0x${string}`[] | readonly `0x${string}`[];
14
+ amounts: bigint[];
15
+ offenses: Offence[];
16
+ }
17
+ export interface WatcherEventMap {
18
+ [WANT_TO_SLASH_EVENT]: (args: WantToSlashArgs) => void;
19
+ }
20
+ export type WatcherEmitter = TypedEventEmitter<WatcherEventMap>;
21
+ export type CheckSlashFn = (validator: `0x${string}`, amount: bigint, offense: Offence) => Promise<boolean>;
22
+ export type Watcher = WatcherEmitter & {
23
+ shouldSlash: CheckSlashFn;
24
+ start?: () => Promise<void>;
25
+ stop?: () => Promise<void>;
26
+ };
27
+ export interface SlasherConfig {
28
+ slashOverridePayload?: EthAddress;
29
+ slashPayloadTtlSeconds: number;
30
+ slashPruneCreate: boolean;
31
+ slashPrunePenalty: bigint;
32
+ slashPruneSignal: boolean;
33
+ slashInactivityCreateTargetPercentage: number;
34
+ slashInactivityCreatePenalty: bigint;
35
+ slashInactivitySignalTargetPercentage: number;
36
+ slashProposerRoundPollingIntervalSeconds: number;
37
+ }
38
+ export declare const DefaultSlasherConfig: SlasherConfig;
39
+ export declare const slasherConfigMappings: ConfigMappingsType<SlasherConfig>;
40
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAEnE,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAC3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAEjE,oBAAY,OAAO;IACjB,OAAO,IAAI;IACX,WAAW,IAAI;IACf,UAAU,IAAI;CACf;AAED,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,CAInD,CAAC;AAEF,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAWxD;AAED,eAAO,MAAM,mBAAmB,EAAG,aAAsB,CAAC;AAE1D,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,KAAK,MAAM,EAAE,EAAE,GAAG,SAAS,KAAK,MAAM,EAAE,EAAE,CAAC;IACvD,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB;AAGD,MAAM,WAAW,eAAe;IAC9B,CAAC,mBAAmB,CAAC,EAAE,CAAC,IAAI,EAAE,eAAe,KAAK,IAAI,CAAC;CACxD;AAED,MAAM,MAAM,cAAc,GAAG,iBAAiB,CAAC,eAAe,CAAC,CAAC;AAEhE,MAAM,MAAM,YAAY,GAAG,CAAC,SAAS,EAAE,KAAK,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAE5G,MAAM,MAAM,OAAO,GAAG,cAAc,GAAG;IACrC,WAAW,EAAE,YAAY,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,IAAI,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B,CAAC;AAEF,MAAM,WAAW,aAAa;IAE5B,oBAAoB,CAAC,EAAE,UAAU,CAAC;IAClC,sBAAsB,EAAE,MAAM,CAAC;IAC/B,gBAAgB,EAAE,OAAO,CAAC;IAC1B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gBAAgB,EAAE,OAAO,CAAC;IAC1B,qCAAqC,EAAE,MAAM,CAAC;IAC9C,4BAA4B,EAAE,MAAM,CAAC;IACrC,qCAAqC,EAAE,MAAM,CAAC;IAC9C,wCAAwC,EAAE,MAAM,CAAC;CAElD;AAED,eAAO,MAAM,oBAAoB,EAAE,aAUlC,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,kBAAkB,CAAC,aAAa,CAsCnE,CAAC"}
package/dest/config.js ADDED
@@ -0,0 +1,76 @@
1
+ import { bigintConfigHelper, booleanConfigHelper, numberConfigHelper } from '@aztec/foundation/config';
2
+ import { EthAddress } from '@aztec/foundation/eth-address';
3
+ export var Offence = /*#__PURE__*/ function(Offence) {
4
+ Offence[Offence["UNKNOWN"] = 0] = "UNKNOWN";
5
+ Offence[Offence["EPOCH_PRUNE"] = 1] = "EPOCH_PRUNE";
6
+ Offence[Offence["INACTIVITY"] = 2] = "INACTIVITY";
7
+ return Offence;
8
+ }({});
9
+ export const OffenceToBigInt = {
10
+ [0]: 0n,
11
+ [1]: 1n,
12
+ [2]: 2n
13
+ };
14
+ export function bigIntToOffence(offense) {
15
+ switch(offense){
16
+ case 0n:
17
+ return 0;
18
+ case 1n:
19
+ return 1;
20
+ case 2n:
21
+ return 2;
22
+ default:
23
+ throw new Error(`Unknown offence: ${offense}`);
24
+ }
25
+ }
26
+ export const WANT_TO_SLASH_EVENT = 'wantToSlash';
27
+ export const DefaultSlasherConfig = {
28
+ slashInactivityCreatePenalty: 1n,
29
+ slashInactivityCreateTargetPercentage: 0.9,
30
+ slashInactivitySignalTargetPercentage: 0.6,
31
+ slashPayloadTtlSeconds: 60 * 60 * 24,
32
+ slashPruneCreate: false,
33
+ slashPrunePenalty: 1n,
34
+ slashPruneSignal: true,
35
+ slashOverridePayload: undefined,
36
+ slashProposerRoundPollingIntervalSeconds: 12
37
+ };
38
+ export const slasherConfigMappings = {
39
+ slashOverridePayload: {
40
+ description: 'An Ethereum address for a slash payload to vote for unconditionally.',
41
+ parseEnv: (val)=>val ? EthAddress.fromString(val) : undefined,
42
+ defaultValue: DefaultSlasherConfig.slashOverridePayload
43
+ },
44
+ slashPayloadTtlSeconds: {
45
+ description: 'Time-to-live for slash payloads in seconds.',
46
+ ...numberConfigHelper(DefaultSlasherConfig.slashPayloadTtlSeconds)
47
+ },
48
+ slashPruneCreate: {
49
+ description: 'Enable creation of slash payloads for pruned epochs.',
50
+ ...booleanConfigHelper(DefaultSlasherConfig.slashPruneCreate)
51
+ },
52
+ slashPrunePenalty: {
53
+ description: 'Penalty amount for slashing validators of a pruned epoch.',
54
+ ...bigintConfigHelper(DefaultSlasherConfig.slashPrunePenalty)
55
+ },
56
+ slashPruneSignal: {
57
+ description: 'Enable voting for slash payloads for pruned epochs.',
58
+ ...booleanConfigHelper(DefaultSlasherConfig.slashPruneSignal)
59
+ },
60
+ slashInactivityCreateTargetPercentage: {
61
+ description: 'Missed attestation percentage to trigger creation of inactivity slash payload (0-100).',
62
+ ...numberConfigHelper(DefaultSlasherConfig.slashInactivityCreateTargetPercentage)
63
+ },
64
+ slashInactivityCreatePenalty: {
65
+ description: 'Penalty amount for slashing an inactive validator.',
66
+ ...bigintConfigHelper(DefaultSlasherConfig.slashInactivityCreatePenalty)
67
+ },
68
+ slashInactivitySignalTargetPercentage: {
69
+ description: 'Missed attestation percentage to trigger voting for an inactivity slash payload (0-100).',
70
+ ...numberConfigHelper(DefaultSlasherConfig.slashInactivitySignalTargetPercentage)
71
+ },
72
+ slashProposerRoundPollingIntervalSeconds: {
73
+ description: 'Polling interval for slashing proposer round in seconds.',
74
+ ...numberConfigHelper(DefaultSlasherConfig.slashProposerRoundPollingIntervalSeconds)
75
+ }
76
+ };
@@ -0,0 +1,23 @@
1
+ import { EpochCache } from '@aztec/epoch-cache';
2
+ import { type L2BlockSourceEventEmitter } from '@aztec/stdlib/block';
3
+ import { Offence, type Watcher, type WatcherEmitter } from './config.js';
4
+ declare const EpochPruneWatcher_base: new () => WatcherEmitter;
5
+ export declare class EpochPruneWatcher extends EpochPruneWatcher_base implements Watcher {
6
+ private l2BlockSource;
7
+ private epochCache;
8
+ private penalty;
9
+ private log;
10
+ private prunedEpochs;
11
+ private maxPrunedEpochs;
12
+ constructor(l2BlockSource: L2BlockSourceEventEmitter, epochCache: EpochCache, penalty: bigint);
13
+ start(): Promise<void>;
14
+ stop(): Promise<void>;
15
+ private handlePruneL2Blocks;
16
+ private addToPrunedEpochs;
17
+ private getValidatorsForEpoch;
18
+ private validatorsToSlashingArgs;
19
+ private wantToSlashForEpoch;
20
+ shouldSlash(validator: `0x${string}`, amount: bigint, _offense: Offence): Promise<boolean>;
21
+ }
22
+ export {};
23
+ //# sourceMappingURL=epoch_prune_watcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"epoch_prune_watcher.d.ts","sourceRoot":"","sources":["../src/epoch_prune_watcher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAEhD,OAAO,EAA2B,KAAK,yBAAyB,EAAuB,MAAM,qBAAqB,CAAC;AAKnH,OAAO,EAAE,OAAO,EAA6C,KAAK,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;sCAE5D,UAAU,cAAc;AAAhF,qBAAa,iBAAkB,SAAQ,sBAA2C,YAAW,OAAO;IAShG,OAAO,CAAC,aAAa;IACrB,OAAO,CAAC,UAAU;IAClB,OAAO,CAAC,OAAO;IAVjB,OAAO,CAAC,GAAG,CAA+C;IAG1D,OAAO,CAAC,YAAY,CAA2C;IAE/D,OAAO,CAAC,eAAe,CAAO;gBAGpB,aAAa,EAAE,yBAAyB,EACxC,UAAU,EAAE,UAAU,EACtB,OAAO,EAAE,MAAM;IAMlB,KAAK;IAKL,IAAI;IASX,OAAO,CAAC,mBAAmB;IAkB3B,OAAO,CAAC,iBAAiB;YAOX,qBAAqB;IAKnC,OAAO,CAAC,wBAAwB;IAUhC,OAAO,CAAC,mBAAmB;IAIpB,WAAW,CAAC,SAAS,EAAE,KAAK,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;CASlG"}
@@ -0,0 +1,77 @@
1
+ import { createLogger } from '@aztec/foundation/log';
2
+ import { L2BlockSourceEvents } from '@aztec/stdlib/block';
3
+ import EventEmitter from 'node:events';
4
+ import { Offence, WANT_TO_SLASH_EVENT } from './config.js';
5
+ export class EpochPruneWatcher extends EventEmitter {
6
+ l2BlockSource;
7
+ epochCache;
8
+ penalty;
9
+ log;
10
+ // Keep track of pruned epochs we've seen to their committee
11
+ prunedEpochs;
12
+ // Only keep track of the last N pruned epochs
13
+ maxPrunedEpochs;
14
+ constructor(l2BlockSource, epochCache, penalty){
15
+ super(), this.l2BlockSource = l2BlockSource, this.epochCache = epochCache, this.penalty = penalty, this.log = createLogger('epoch-prune-watcher'), this.prunedEpochs = new Map(), this.maxPrunedEpochs = 100;
16
+ this.log.info('EpochPruneWatcher initialized');
17
+ }
18
+ start() {
19
+ this.l2BlockSource.on(L2BlockSourceEvents.L2PruneDetected, this.handlePruneL2Blocks.bind(this));
20
+ return Promise.resolve();
21
+ }
22
+ stop() {
23
+ this.l2BlockSource.removeListener(L2BlockSourceEvents.L2PruneDetected, this.handlePruneL2Blocks.bind(this));
24
+ return Promise.resolve();
25
+ }
26
+ // TODO(#14407), TODO(#14408)
27
+ // We should only be slashing due to prune if:
28
+ // - the data was not available (#14407)
29
+ // - OR the data was available and the epoch could have been proven (#14408)
30
+ handlePruneL2Blocks(event) {
31
+ const { epochNumber } = event;
32
+ this.log.info(`Detected chain prune. Attempting to create slash for epoch ${epochNumber}`, event);
33
+ this.getValidatorsForEpoch(epochNumber).then((validators)=>{
34
+ const args = this.validatorsToSlashingArgs(validators);
35
+ if (args) {
36
+ this.addToPrunedEpochs(epochNumber, validators);
37
+ this.emit(WANT_TO_SLASH_EVENT, args);
38
+ }
39
+ }).catch((error)=>{
40
+ this.log.error('Error getting validators for epoch', error);
41
+ });
42
+ }
43
+ addToPrunedEpochs(epochNumber, validators) {
44
+ this.prunedEpochs.set(epochNumber, validators);
45
+ if (this.prunedEpochs.size > this.maxPrunedEpochs) {
46
+ this.prunedEpochs.delete(this.prunedEpochs.keys().next().value);
47
+ }
48
+ }
49
+ async getValidatorsForEpoch(epochNumber) {
50
+ const { committee } = await this.epochCache.getCommitteeForEpoch(epochNumber);
51
+ return committee.map((v)=>v.toString());
52
+ }
53
+ validatorsToSlashingArgs(validators) {
54
+ if (validators.length === 0) {
55
+ this.log.debug('No validators found for epoch, skipping slash creation.');
56
+ return undefined;
57
+ }
58
+ const amounts = Array(validators.length).fill(this.penalty);
59
+ const offenses = Array(validators.length).fill(Offence.EPOCH_PRUNE);
60
+ return {
61
+ validators,
62
+ amounts,
63
+ offenses
64
+ };
65
+ }
66
+ wantToSlashForEpoch(validator, amount, epochNumber) {
67
+ return this.prunedEpochs.get(epochNumber)?.includes(validator) ?? false;
68
+ }
69
+ shouldSlash(validator, amount, _offense) {
70
+ for (const epoch of this.prunedEpochs.keys()){
71
+ if (this.wantToSlashForEpoch(validator, amount, epoch)) {
72
+ return Promise.resolve(true);
73
+ }
74
+ }
75
+ return Promise.resolve(false);
76
+ }
77
+ }
@@ -0,0 +1,4 @@
1
+ export * from './config.js';
2
+ export * from './epoch_prune_watcher.js';
3
+ export * from './slasher_client.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,0BAA0B,CAAC;AACzC,cAAc,qBAAqB,CAAC"}
package/dest/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export * from './config.js';
2
+ export * from './epoch_prune_watcher.js';
3
+ export * from './slasher_client.js';
@@ -0,0 +1,141 @@
1
+ import { type ExtendedViemWalletClient, type L1ReaderConfig, L1TxUtils, SlashingProposerContract } from '@aztec/ethereum';
2
+ import { EthAddress } from '@aztec/foundation/eth-address';
3
+ import type { DateProvider } from '@aztec/foundation/timer';
4
+ import { SlashFactoryAbi } from '@aztec/l1-artifacts';
5
+ import { type GetContractReturnType } from 'viem';
6
+ import { Offence, type SlasherConfig, type Watcher } from './config.js';
7
+ type MonitoredSlashPayload = {
8
+ payloadAddress: EthAddress;
9
+ validators: readonly EthAddress[];
10
+ amounts: readonly bigint[];
11
+ offenses: readonly Offence[];
12
+ observedAtSeconds: number;
13
+ totalAmount: bigint;
14
+ };
15
+ /**
16
+ * A Spartiate slasher client implementation
17
+ *
18
+ * Spartiates: a full citizen of the ancient polis of Sparta, member of an elite warrior class.
19
+ *
20
+ * How it works:
21
+ *
22
+ * The constructor accepts instances of Watcher classes that correspond to specific offences. These "watchers" do two things:
23
+ * - watch for their offence conditions and emit an event when they are detected
24
+ * - confirm/deny whether they agree with a proposed offence
25
+ *
26
+ * The SlasherClient class is responsible for:
27
+ * - listening for events from the watchers and creating a corresponding payload
28
+ * - listening for the payloads from L1 filtering them through the watchers
29
+ * - ordering the payloads and discarding stale payloads
30
+ * - presenting the payload that ought to be currently voted for
31
+ * - detecting when it wants to execute a round
32
+ * - executing a round
33
+ * - listening for the round to be executed
34
+ * - removing the executed round from the list of monitored payloads
35
+ *
36
+ * A few improvements:
37
+ * - TODO(#14421): Only vote on the proposal if it is possible to reach quorum, e.g., if 6 votes are needed and only 4 slots are left don't vote.
38
+ */
39
+ export declare class SlasherClient {
40
+ config: SlasherConfig;
41
+ protected slashFactoryContract: GetContractReturnType<typeof SlashFactoryAbi, ExtendedViemWalletClient>;
42
+ private slashingProposer;
43
+ private l1TxUtils;
44
+ private watchers;
45
+ private dateProvider;
46
+ private log;
47
+ private monitoredPayloads;
48
+ private unwatchCallbacks;
49
+ static new(config: SlasherConfig, l1Contracts: Pick<L1ReaderConfig['l1Contracts'], 'rollupAddress' | 'slashFactoryAddress'>, l1TxUtils: L1TxUtils, watchers: Watcher[], dateProvider: DateProvider): Promise<SlasherClient>;
50
+ constructor(config: SlasherConfig, slashFactoryContract: GetContractReturnType<typeof SlashFactoryAbi, ExtendedViemWalletClient>, slashingProposer: SlashingProposerContract, l1TxUtils: L1TxUtils, watchers: Watcher[], dateProvider: DateProvider, log?: import("@aztec/foundation/log").Logger);
51
+ start(): Promise<void>;
52
+ /**
53
+ * Allows consumers to stop the instance of the slasher client.
54
+ * 'ready' will now return 'false' and the running promise that keeps the client synced is interrupted.
55
+ */
56
+ stop(): Promise<void>;
57
+ /**
58
+ * Get the payload to slash
59
+ *
60
+ * @param _slotNumber the current slot number (unused)
61
+ * @returns the payload to slash or undefined if there is no payload to slash
62
+ */
63
+ getSlashPayload(_slotNumber: bigint): Promise<EthAddress | undefined>;
64
+ /**
65
+ * Get the list of monitored payloads
66
+ *
67
+ * Useful for tests.
68
+ *
69
+ * @returns the list of monitored payloads
70
+ */
71
+ getMonitoredPayloads(): MonitoredSlashPayload[];
72
+ /**
73
+ * This is called when a watcher emits WANT_TO_SLASH_EVENT.
74
+ *
75
+ * @param args - the arguments from the watcher, including the validators, amounts, and offenses
76
+ */
77
+ private wantToSlash;
78
+ /**
79
+ * Watch for new payloads created by the slash factory
80
+ *
81
+ * Whenever a log has events, we iterate over them and convert them to MonitoredSlashPayloads
82
+ *
83
+ * We then add the payloads to the list of monitored payloads if we agree with them
84
+ *
85
+ * @returns a callback to remove the watcher
86
+ */
87
+ private watchSlashFactoryEvents;
88
+ /**
89
+ * Convert a list of factory events to an iterable of monitored payloads
90
+ *
91
+ * @param args
92
+ * @returns the list of monitored payloads
93
+ */
94
+ private factoryEventsToMonitoredPayloads;
95
+ /**
96
+ * Add a payload to the list of monitored payloads if we agree with it
97
+ *
98
+ * @param payload
99
+ */
100
+ private addMonitoredPayload;
101
+ /**
102
+ * Check if we agree with a payload
103
+ *
104
+ * We check each offense and validator pair against the watchers
105
+ *
106
+ * @param payload
107
+ * @returns true if any watcher agrees with the payload, false otherwise
108
+ */
109
+ private doIAgreeWithPayload;
110
+ /**
111
+ * Sort the monitored payloads by total amount in descending order
112
+ */
113
+ private sortMonitoredPayloads;
114
+ /**
115
+ * Filter out payloads that have expired
116
+ *
117
+ * @param currentTimeSeconds
118
+ * @param payloadTtlSeconds
119
+ */
120
+ private filterExpiredPayloads;
121
+ /**
122
+ * Execute a round if we agree with the proposal.
123
+ *
124
+ * Bound to the slashing proposer contract's listenToExecutableProposals method in the constructor.
125
+ *
126
+ * @param {proposal: `0x${string}`; round: bigint} param0
127
+ */
128
+ private executeRoundIfAgree;
129
+ /**
130
+ * Handler for when a proposal is executed.
131
+ *
132
+ * Removes the first matching payload from the list of monitored payloads.
133
+ *
134
+ * Bound to the slashing proposer contract's listenToProposalExecuted method in the constructor.
135
+ *
136
+ * @param {round: bigint; proposal: `0x${string}`} param0
137
+ */
138
+ private proposalExecuted;
139
+ }
140
+ export {};
141
+ //# sourceMappingURL=slasher_client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slasher_client.d.ts","sourceRoot":"","sources":["../src/slasher_client.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,wBAAwB,EAC7B,KAAK,cAAc,EACnB,SAAS,EAGT,wBAAwB,EACzB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAE3D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEtD,OAAO,EAEL,KAAK,qBAAqB,EAI3B,MAAM,MAAM,CAAC;AAEd,OAAO,EACL,OAAO,EACP,KAAK,aAAa,EAGlB,KAAK,OAAO,EAEb,MAAM,aAAa,CAAC;AAErB,KAAK,qBAAqB,GAAG;IAC3B,cAAc,EAAE,UAAU,CAAC;IAC3B,UAAU,EAAE,SAAS,UAAU,EAAE,CAAC;IAClC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,QAAQ,EAAE,SAAS,OAAO,EAAE,CAAC;IAC7B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBAAa,aAAa;IA6Bf,MAAM,EAAE,aAAa;IAC5B,SAAS,CAAC,oBAAoB,EAAE,qBAAqB,CAAC,OAAO,eAAe,EAAE,wBAAwB,CAAC;IACvG,OAAO,CAAC,gBAAgB;IACxB,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,YAAY;IACpB,OAAO,CAAC,GAAG;IAlCb,OAAO,CAAC,iBAAiB,CAA+B;IACxD,OAAO,CAAC,gBAAgB,CAAsB;WAEjC,GAAG,CACd,MAAM,EAAE,aAAa,EACrB,WAAW,EAAE,IAAI,CAAC,cAAc,CAAC,aAAa,CAAC,EAAE,eAAe,GAAG,qBAAqB,CAAC,EACzF,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,OAAO,EAAE,EACnB,YAAY,EAAE,YAAY;gBAoBnB,MAAM,EAAE,aAAa,EAClB,oBAAoB,EAAE,qBAAqB,CAAC,OAAO,eAAe,EAAE,wBAAwB,CAAC,EAC/F,gBAAgB,EAAE,wBAAwB,EAC1C,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,OAAO,EAAE,EACnB,YAAY,EAAE,YAAY,EAC1B,GAAG,yCAA0B;IAK1B,KAAK;IAuBlB;;;OAGG;IACU,IAAI;IAajB;;;;;OAKG;IACI,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;IAoB5E;;;;;;OAMG;IACI,oBAAoB,IAAI,qBAAqB,EAAE;IAMtD;;;;OAIG;IACH,OAAO,CAAC,WAAW;IAmBnB;;;;;;;;OAQG;IACH,OAAO,CAAC,uBAAuB;IAc/B;;;;;OAKG;IACH,OAAO,CAAE,gCAAgC;IAuBzC;;;;OAIG;YACW,mBAAmB;IASjC;;;;;;;OAOG;YACW,mBAAmB;IA2BjC;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAO7B;;;;;OAKG;IACH,OAAO,CAAC,qBAAqB;IAM7B;;;;;;OAMG;YACW,mBAAmB;IA0BjC;;;;;;;;OAQG;IACH,OAAO,CAAC,gBAAgB;CAQzB"}