import type { BlockNumber, CheckpointNumber, SlotNumber } from '@aztec/foundation/branded-types'; import type { SecretValue } from '@aztec/foundation/config'; import { Fr } from '@aztec/foundation/curves/bn254'; import type { EthAddress } from '@aztec/foundation/eth-address'; import type { Signature } from '@aztec/foundation/eth-signature'; import type { SequencerConfig, SlasherConfig } from '@aztec/stdlib/interfaces/server'; import type { BlockProposal, BlockProposalOptions, CheckpointAttestation, CheckpointProposal, CheckpointProposalOptions } from '@aztec/stdlib/p2p'; import type { CheckpointHeader } from '@aztec/stdlib/rollup'; import type { BlockHeader, Tx } from '@aztec/stdlib/tx'; import { type ValidatorHASignerConfig } from '@aztec/validator-ha-signer/config'; import type { PeerId } from '@libp2p/interface'; import { z } from 'zod'; import type { CommitteeAttestationsAndSigners } from '../block/index.js'; /** * Validator client configuration */ export type ValidatorClientConfig = ValidatorHASignerConfig & { /** The private keys of the validators participating in attestation duties */ validatorPrivateKeys?: SecretValue<`0x${string}`[]>; /** The addresses of the validators to use with remote signers */ validatorAddresses?: EthAddress[]; /** Do not run the validator */ disableValidator: boolean; /** Temporarily disable these specific validator addresses */ disabledValidators: EthAddress[]; /** Interval between polling for new attestations from peers */ attestationPollingIntervalMs: number; /** Whether to re-execute transactions in a block proposal before attesting */ validatorReexecute: boolean; /** Whether to always reexecute block proposals, even for non-validator nodes or when out of the currnet committee */ alwaysReexecuteBlockProposals?: boolean; /** Whether to run in fisherman mode: validates all proposals and attestations but does not broadcast attestations or participate in consensus */ fishermanMode?: boolean; /** Skip checkpoint proposal validation and always attest (default: false) */ skipCheckpointProposalValidation?: boolean; /** Skip pushing re-executed blocks to archiver (default: false) */ skipPushProposedBlocksToArchiver?: boolean; /** Agree to attest to equivocated checkpoint proposals (for testing purposes only) */ attestToEquivocatedProposals?: boolean; /** Maximum L2 gas per block for validation. Proposals exceeding this limit are rejected. */ validateMaxL2BlockGas?: number; /** Maximum DA gas per block for validation. Proposals exceeding this limit are rejected. */ validateMaxDABlockGas?: number; /** Maximum transactions per block for validation. Proposals exceeding this limit are rejected. */ validateMaxTxsPerBlock?: number; /** Maximum transactions per checkpoint for validation. Proposals exceeding this limit are rejected. */ validateMaxTxsPerCheckpoint?: number; }; export type ValidatorClientFullConfig = ValidatorClientConfig & Pick & Pick & { /** * Whether transactions are disabled for this node * @remarks This should match the property in P2PConfig. It's not picked from there to avoid circular dependencies. */ disableTransactions?: boolean; }; export declare const ValidatorClientConfigSchema: z.ZodObject<{ haSigningEnabled: z.ZodBoolean; l1Contracts: z.ZodObject<{ rollupAddress: z.ZodType; }, "strip", z.ZodTypeAny, { rollupAddress: EthAddress; }, { rollupAddress: EthAddress; }>; nodeId: z.ZodString; pollingIntervalMs: z.ZodNumber; signingTimeoutMs: z.ZodNumber; maxStuckDutiesAgeMs: z.ZodOptional; cleanupOldDutiesAfterHours: z.ZodOptional; databaseUrl: z.ZodOptional; poolMaxCount: z.ZodOptional; poolMinCount: z.ZodOptional; poolIdleTimeoutMs: z.ZodOptional; poolConnectionTimeoutMs: z.ZodOptional; } & { validatorAddresses: z.ZodOptional, "many">>; disableValidator: z.ZodBoolean; disabledValidators: z.ZodArray, "many">; attestationPollingIntervalMs: z.ZodNumber; validatorReexecute: z.ZodBoolean; alwaysReexecuteBlockProposals: z.ZodOptional; fishermanMode: z.ZodOptional; skipCheckpointProposalValidation: z.ZodOptional; skipPushProposedBlocksToArchiver: z.ZodOptional; attestToEquivocatedProposals: z.ZodOptional; validateMaxL2BlockGas: z.ZodOptional; validateMaxDABlockGas: z.ZodOptional; validateMaxTxsPerBlock: z.ZodOptional; validateMaxTxsPerCheckpoint: z.ZodOptional; }, "strip", z.ZodTypeAny, { haSigningEnabled: boolean; l1Contracts: { rollupAddress: EthAddress; }; nodeId: string; pollingIntervalMs: number; signingTimeoutMs: number; maxStuckDutiesAgeMs?: number | undefined; cleanupOldDutiesAfterHours?: number | undefined; databaseUrl?: string | undefined; poolMaxCount?: number | undefined; poolMinCount?: number | undefined; poolIdleTimeoutMs?: number | undefined; poolConnectionTimeoutMs?: number | undefined; validatorAddresses?: EthAddress[] | undefined; disableValidator: boolean; disabledValidators: EthAddress[]; attestationPollingIntervalMs: number; validatorReexecute: boolean; alwaysReexecuteBlockProposals?: boolean | undefined; fishermanMode?: boolean | undefined; skipCheckpointProposalValidation?: boolean | undefined; skipPushProposedBlocksToArchiver?: boolean | undefined; attestToEquivocatedProposals?: boolean | undefined; validateMaxL2BlockGas?: number | undefined; validateMaxDABlockGas?: number | undefined; validateMaxTxsPerBlock?: number | undefined; validateMaxTxsPerCheckpoint?: number | undefined; }, { haSigningEnabled: boolean; l1Contracts: { rollupAddress: EthAddress; }; nodeId: string; pollingIntervalMs: number; signingTimeoutMs: number; maxStuckDutiesAgeMs?: number | undefined; cleanupOldDutiesAfterHours?: number | undefined; databaseUrl?: string | undefined; poolMaxCount?: number | undefined; poolMinCount?: number | undefined; poolIdleTimeoutMs?: number | undefined; poolConnectionTimeoutMs?: number | undefined; validatorAddresses?: string[] | undefined; disableValidator: boolean; disabledValidators: string[]; attestationPollingIntervalMs: number; validatorReexecute: boolean; alwaysReexecuteBlockProposals?: boolean | undefined; fishermanMode?: boolean | undefined; skipCheckpointProposalValidation?: boolean | undefined; skipPushProposedBlocksToArchiver?: boolean | undefined; attestToEquivocatedProposals?: boolean | undefined; validateMaxL2BlockGas?: number | undefined; validateMaxDABlockGas?: number | undefined; validateMaxTxsPerBlock?: number | undefined; validateMaxTxsPerCheckpoint?: number | undefined; }>; export declare const ValidatorClientFullConfigSchema: z.ZodObject<{ haSigningEnabled: z.ZodBoolean; l1Contracts: z.ZodObject<{ rollupAddress: z.ZodType; }, "strip", z.ZodTypeAny, { rollupAddress: EthAddress; }, { rollupAddress: EthAddress; }>; nodeId: z.ZodString; pollingIntervalMs: z.ZodNumber; signingTimeoutMs: z.ZodNumber; maxStuckDutiesAgeMs: z.ZodOptional; cleanupOldDutiesAfterHours: z.ZodOptional; databaseUrl: z.ZodOptional; poolMaxCount: z.ZodOptional; poolMinCount: z.ZodOptional; poolIdleTimeoutMs: z.ZodOptional; poolConnectionTimeoutMs: z.ZodOptional; } & { validatorAddresses: z.ZodOptional, "many">>; disableValidator: z.ZodBoolean; disabledValidators: z.ZodArray, "many">; attestationPollingIntervalMs: z.ZodNumber; validatorReexecute: z.ZodBoolean; alwaysReexecuteBlockProposals: z.ZodOptional; fishermanMode: z.ZodOptional; skipCheckpointProposalValidation: z.ZodOptional; skipPushProposedBlocksToArchiver: z.ZodOptional; attestToEquivocatedProposals: z.ZodOptional; validateMaxL2BlockGas: z.ZodOptional; validateMaxDABlockGas: z.ZodOptional; validateMaxTxsPerBlock: z.ZodOptional; validateMaxTxsPerCheckpoint: z.ZodOptional; } & { txPublicSetupAllowListExtend: z.ZodOptional; selector: import("@aztec/foundation/schemas").ZodFor; onlySelf: z.ZodOptional; rejectNullMsgSender: z.ZodOptional; calldataLength: z.ZodOptional; }, "strip", z.ZodTypeAny, { address: import("../aztec-address/index.js").AztecAddress; selector: import("../abi/function_selector.js").FunctionSelector; onlySelf?: boolean | undefined; rejectNullMsgSender?: boolean | undefined; calldataLength?: number | undefined; }, { address?: any; selector?: any; onlySelf?: boolean | undefined; rejectNullMsgSender?: boolean | undefined; calldataLength?: number | undefined; }>, z.ZodObject<{ classId: import("@aztec/foundation/schemas").ZodFor; selector: import("@aztec/foundation/schemas").ZodFor; onlySelf: z.ZodOptional; rejectNullMsgSender: z.ZodOptional; calldataLength: z.ZodOptional; }, "strip", z.ZodTypeAny, { classId: Fr; selector: import("../abi/function_selector.js").FunctionSelector; onlySelf?: boolean | undefined; rejectNullMsgSender?: boolean | undefined; calldataLength?: number | undefined; }, { classId?: any; selector?: any; onlySelf?: boolean | undefined; rejectNullMsgSender?: boolean | undefined; calldataLength?: number | undefined; }>]>, "many">>; broadcastInvalidBlockProposal: z.ZodOptional; slashBroadcastedInvalidBlockPenalty: z.ZodPipeline, z.ZodBigInt>; slashDuplicateProposalPenalty: z.ZodPipeline, z.ZodBigInt>; slashDuplicateAttestationPenalty: z.ZodPipeline, z.ZodBigInt>; disableTransactions: z.ZodOptional; }, "strip", z.ZodTypeAny, { haSigningEnabled: boolean; l1Contracts: { rollupAddress: EthAddress; }; nodeId: string; pollingIntervalMs: number; signingTimeoutMs: number; maxStuckDutiesAgeMs?: number | undefined; cleanupOldDutiesAfterHours?: number | undefined; databaseUrl?: string | undefined; poolMaxCount?: number | undefined; poolMinCount?: number | undefined; poolIdleTimeoutMs?: number | undefined; poolConnectionTimeoutMs?: number | undefined; validatorAddresses?: EthAddress[] | undefined; disableValidator: boolean; disabledValidators: EthAddress[]; attestationPollingIntervalMs: number; validatorReexecute: boolean; alwaysReexecuteBlockProposals?: boolean | undefined; fishermanMode?: boolean | undefined; skipCheckpointProposalValidation?: boolean | undefined; skipPushProposedBlocksToArchiver?: boolean | undefined; attestToEquivocatedProposals?: boolean | undefined; validateMaxL2BlockGas?: number | undefined; validateMaxDABlockGas?: number | undefined; validateMaxTxsPerBlock?: number | undefined; validateMaxTxsPerCheckpoint?: number | undefined; txPublicSetupAllowListExtend?: ({ address: import("../aztec-address/index.js").AztecAddress; selector: import("../abi/function_selector.js").FunctionSelector; onlySelf?: boolean | undefined; rejectNullMsgSender?: boolean | undefined; calldataLength?: number | undefined; } | { classId: Fr; selector: import("../abi/function_selector.js").FunctionSelector; onlySelf?: boolean | undefined; rejectNullMsgSender?: boolean | undefined; calldataLength?: number | undefined; })[] | undefined; broadcastInvalidBlockProposal?: boolean | undefined; slashBroadcastedInvalidBlockPenalty: bigint; slashDuplicateProposalPenalty: bigint; slashDuplicateAttestationPenalty: bigint; disableTransactions?: boolean | undefined; }, { haSigningEnabled: boolean; l1Contracts: { rollupAddress: EthAddress; }; nodeId: string; pollingIntervalMs: number; signingTimeoutMs: number; maxStuckDutiesAgeMs?: number | undefined; cleanupOldDutiesAfterHours?: number | undefined; databaseUrl?: string | undefined; poolMaxCount?: number | undefined; poolMinCount?: number | undefined; poolIdleTimeoutMs?: number | undefined; poolConnectionTimeoutMs?: number | undefined; validatorAddresses?: string[] | undefined; disableValidator: boolean; disabledValidators: string[]; attestationPollingIntervalMs: number; validatorReexecute: boolean; alwaysReexecuteBlockProposals?: boolean | undefined; fishermanMode?: boolean | undefined; skipCheckpointProposalValidation?: boolean | undefined; skipPushProposedBlocksToArchiver?: boolean | undefined; attestToEquivocatedProposals?: boolean | undefined; validateMaxL2BlockGas?: number | undefined; validateMaxDABlockGas?: number | undefined; validateMaxTxsPerBlock?: number | undefined; validateMaxTxsPerCheckpoint?: number | undefined; txPublicSetupAllowListExtend?: ({ address?: any; selector?: any; onlySelf?: boolean | undefined; rejectNullMsgSender?: boolean | undefined; calldataLength?: number | undefined; } | { classId?: any; selector?: any; onlySelf?: boolean | undefined; rejectNullMsgSender?: boolean | undefined; calldataLength?: number | undefined; })[] | undefined; broadcastInvalidBlockProposal?: boolean | undefined; slashBroadcastedInvalidBlockPenalty: string | number | bigint; slashDuplicateProposalPenalty: string | number | bigint; slashDuplicateAttestationPenalty: string | number | bigint; disableTransactions?: boolean | undefined; }>; export interface Validator { start(): Promise; updateConfig(config: Partial): void; createBlockProposal(blockHeader: BlockHeader, indexWithinCheckpoint: number, inHash: Fr, archive: Fr, txs: Tx[], proposerAddress: EthAddress | undefined, options: BlockProposalOptions): Promise; /** Creates a checkpoint proposal for the last block in a checkpoint */ createCheckpointProposal(checkpointHeader: CheckpointHeader, archive: Fr, feeAssetPriceModifier: bigint, lastBlockProposal: BlockProposal | undefined, proposerAddress: EthAddress | undefined, options: CheckpointProposalOptions): Promise; /** * Validate a block proposal from a peer. * Note: Validators do NOT attest to individual blocks - attestations are only for checkpoint proposals. * @returns true if the proposal is valid, false otherwise */ validateBlockProposal(proposal: BlockProposal, sender: PeerId): Promise; /** * Validate and attest to a checkpoint proposal from a peer. * @returns Checkpoint attestations if valid, undefined otherwise */ attestToCheckpointProposal(proposal: CheckpointProposal, sender: PeerId): Promise; broadcastBlockProposal(proposal: BlockProposal): Promise; /** Collect own attestations for a checkpoint proposal (used when skipping p2p attestation collection) */ collectOwnAttestations(proposal: CheckpointProposal): Promise; /** Collect attestations from the p2p network for a checkpoint proposal */ collectAttestations(proposal: CheckpointProposal, required: number, deadline: Date): Promise; signAttestationsAndSigners(attestationsAndSigners: CommitteeAttestationsAndSigners, proposer: EthAddress, slot: SlotNumber, blockNumber: BlockNumber | CheckpointNumber): Promise; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmFsaWRhdG9yLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaW50ZXJmYWNlcy92YWxpZGF0b3IudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLEVBQUUsV0FBVyxFQUFFLGdCQUFnQixFQUFFLFVBQVUsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBQ2pHLE9BQU8sS0FBSyxFQUFFLFdBQVcsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQzVELE9BQU8sRUFBRSxFQUFFLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUNwRCxPQUFPLEtBQUssRUFBRSxVQUFVLEVBQUUsTUFBTSwrQkFBK0IsQ0FBQztBQUNoRSxPQUFPLEtBQUssRUFBRSxTQUFTLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUVqRSxPQUFPLEtBQUssRUFBRSxlQUFlLEVBQUUsYUFBYSxFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFDdEYsT0FBTyxLQUFLLEVBQ1YsYUFBYSxFQUNiLG9CQUFvQixFQUNwQixxQkFBcUIsRUFDckIsa0JBQWtCLEVBQ2xCLHlCQUF5QixFQUMxQixNQUFNLG1CQUFtQixDQUFDO0FBQzNCLE9BQU8sS0FBSyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDN0QsT0FBTyxLQUFLLEVBQUUsV0FBVyxFQUFFLEVBQUUsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBQ3hELE9BQU8sRUFBRSxLQUFLLHVCQUF1QixFQUFpQyxNQUFNLG1DQUFtQyxDQUFDO0FBRWhILE9BQU8sS0FBSyxFQUFFLE1BQU0sRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBQ2hELE9BQU8sRUFBRSxDQUFDLEVBQUUsTUFBTSxLQUFLLENBQUM7QUFFeEIsT0FBTyxLQUFLLEVBQUUsK0JBQStCLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQztBQUd6RTs7R0FFRztBQUNILE1BQU0sTUFBTSxxQkFBcUIsR0FBRyx1QkFBdUIsR0FBRztJQUM1RCw2RUFBNkU7SUFDN0Usb0JBQW9CLENBQUMsRUFBRSxXQUFXLENBQUMsS0FBSyxNQUFNLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFFcEQsaUVBQWlFO0lBQ2pFLGtCQUFrQixDQUFDLEVBQUUsVUFBVSxFQUFFLENBQUM7SUFFbEMsK0JBQStCO0lBQy9CLGdCQUFnQixFQUFFLE9BQU8sQ0FBQztJQUUxQiw2REFBNkQ7SUFDN0Qsa0JBQWtCLEVBQUUsVUFBVSxFQUFFLENBQUM7SUFFakMsK0RBQStEO0lBQy9ELDRCQUE0QixFQUFFLE1BQU0sQ0FBQztJQUVyQyw4RUFBOEU7SUFDOUUsa0JBQWtCLEVBQUUsT0FBTyxDQUFDO0lBRTVCLHFIQUFxSDtJQUNySCw2QkFBNkIsQ0FBQyxFQUFFLE9BQU8sQ0FBQztJQUV4QyxpSkFBaUo7SUFDakosYUFBYSxDQUFDLEVBQUUsT0FBTyxDQUFDO0lBRXhCLDZFQUE2RTtJQUM3RSxnQ0FBZ0MsQ0FBQyxFQUFFLE9BQU8sQ0FBQztJQUUzQyxtRUFBbUU7SUFDbkUsZ0NBQWdDLENBQUMsRUFBRSxPQUFPLENBQUM7SUFFM0Msc0ZBQXNGO0lBQ3RGLDRCQUE0QixDQUFDLEVBQUUsT0FBTyxDQUFDO0lBRXZDLDRGQUE0RjtJQUM1RixxQkFBcUIsQ0FBQyxFQUFFLE1BQU0sQ0FBQztJQUUvQiw0RkFBNEY7SUFDNUYscUJBQXFCLENBQUMsRUFBRSxNQUFNLENBQUM7SUFFL0Isa0dBQWtHO0lBQ2xHLHNCQUFzQixDQUFDLEVBQUUsTUFBTSxDQUFDO0lBRWhDLHVHQUF1RztJQUN2RywyQkFBMkIsQ0FBQyxFQUFFLE1BQU0sQ0FBQztDQUN0QyxDQUFDO0FBRUYsTUFBTSxNQUFNLHlCQUF5QixHQUFHLHFCQUFxQixHQUMzRCxJQUFJLENBQUMsZUFBZSxFQUFFLDhCQUE4QixHQUFHLCtCQUErQixDQUFDLEdBQ3ZGLElBQUksQ0FDRixhQUFhLEVBQ2IscUNBQXFDLEdBQUcsK0JBQStCLEdBQUcsa0NBQWtDLENBQzdHLEdBQUc7SUFDRjs7O09BR0c7SUFDSCxtQkFBbUIsQ0FBQyxFQUFFLE9BQU8sQ0FBQztDQUMvQixDQUFDO0FBRUosZUFBTyxNQUFNLDJCQUEyQjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7RUFpQnZDLENBQUM7QUFFRixlQUFPLE1BQU0sK0JBQStCOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7RUFTM0MsQ0FBQztBQUVGLE1BQU0sV0FBVyxTQUFTO0lBQ3hCLEtBQUssSUFBSSxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDdkIsWUFBWSxDQUFDLE1BQU0sRUFBRSxPQUFPLENBQUMseUJBQXlCLENBQUMsR0FBRyxJQUFJLENBQUM7SUFHL0QsbUJBQW1CLENBQ2pCLFdBQVcsRUFBRSxXQUFXLEVBQ3hCLHFCQUFxQixFQUFFLE1BQU0sRUFDN0IsTUFBTSxFQUFFLEVBQUUsRUFDVixPQUFPLEVBQUUsRUFBRSxFQUNYLEdBQUcsRUFBRSxFQUFFLEVBQUUsRUFDVCxlQUFlLEVBQUUsVUFBVSxHQUFHLFNBQVMsRUFDdkMsT0FBTyxFQUFFLG9CQUFvQixHQUM1QixPQUFPLENBQUMsYUFBYSxHQUFHLFNBQVMsQ0FBQyxDQUFDO0lBRXRDLHVFQUF1RTtJQUN2RSx3QkFBd0IsQ0FDdEIsZ0JBQWdCLEVBQUUsZ0JBQWdCLEVBQ2xDLE9BQU8sRUFBRSxFQUFFLEVBQ1gscUJBQXFCLEVBQUUsTUFBTSxFQUM3QixpQkFBaUIsRUFBRSxhQUFhLEdBQUcsU0FBUyxFQUM1QyxlQUFlLEVBQUUsVUFBVSxHQUFHLFNBQVMsRUFDdkMsT0FBTyxFQUFFLHlCQUF5QixHQUNqQyxPQUFPLENBQUMsa0JBQWtCLENBQUMsQ0FBQztJQUUvQjs7OztPQUlHO0lBQ0gscUJBQXFCLENBQUMsUUFBUSxFQUFFLGFBQWEsRUFBRSxNQUFNLEVBQUUsTUFBTSxHQUFHLE9BQU8sQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUVqRjs7O09BR0c7SUFDSCwwQkFBMEIsQ0FDeEIsUUFBUSxFQUFFLGtCQUFrQixFQUM1QixNQUFNLEVBQUUsTUFBTSxHQUNiLE9BQU8sQ0FBQyxxQkFBcUIsRUFBRSxHQUFHLFNBQVMsQ0FBQyxDQUFDO0lBRWhELHNCQUFzQixDQUFDLFFBQVEsRUFBRSxhQUFhLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDO0lBRS9ELHlHQUF5RztJQUN6RyxzQkFBc0IsQ0FBQyxRQUFRLEVBQUUsa0JBQWtCLEdBQUcsT0FBTyxDQUFDLHFCQUFxQixFQUFFLENBQUMsQ0FBQztJQUV2RiwwRUFBMEU7SUFDMUUsbUJBQW1CLENBQUMsUUFBUSxFQUFFLGtCQUFrQixFQUFFLFFBQVEsRUFBRSxNQUFNLEVBQUUsUUFBUSxFQUFFLElBQUksR0FBRyxPQUFPLENBQUMscUJBQXFCLEVBQUUsQ0FBQyxDQUFDO0lBRXRILDBCQUEwQixDQUN4QixzQkFBc0IsRUFBRSwrQkFBK0IsRUFDdkQsUUFBUSxFQUFFLFVBQVUsRUFDcEIsSUFBSSxFQUFFLFVBQVUsRUFDaEIsV0FBVyxFQUFFLFdBQVcsR0FBRyxnQkFBZ0IsR0FDMUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxDQUFDO0NBQ3ZCIn0=