@aztec/slasher 0.0.1-commit.21caa21
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 +218 -0
- package/dest/config.d.ts +6 -0
- package/dest/config.d.ts.map +1 -0
- package/dest/config.js +134 -0
- package/dest/empire_slasher_client.d.ts +190 -0
- package/dest/empire_slasher_client.d.ts.map +1 -0
- package/dest/empire_slasher_client.js +572 -0
- package/dest/factory/create_facade.d.ts +15 -0
- package/dest/factory/create_facade.d.ts.map +1 -0
- package/dest/factory/create_facade.js +23 -0
- package/dest/factory/create_implementation.d.ts +17 -0
- package/dest/factory/create_implementation.d.ts.map +1 -0
- package/dest/factory/create_implementation.js +73 -0
- package/dest/factory/get_settings.d.ts +4 -0
- package/dest/factory/get_settings.d.ts.map +1 -0
- package/dest/factory/get_settings.js +36 -0
- package/dest/factory/index.d.ts +3 -0
- package/dest/factory/index.d.ts.map +1 -0
- package/dest/factory/index.js +2 -0
- package/dest/index.d.ts +11 -0
- package/dest/index.d.ts.map +1 -0
- package/dest/index.js +10 -0
- package/dest/null_slasher_client.d.ts +17 -0
- package/dest/null_slasher_client.d.ts.map +1 -0
- package/dest/null_slasher_client.js +33 -0
- package/dest/slash_offenses_collector.d.ts +45 -0
- package/dest/slash_offenses_collector.d.ts.map +1 -0
- package/dest/slash_offenses_collector.js +94 -0
- package/dest/slash_round_monitor.d.ts +30 -0
- package/dest/slash_round_monitor.d.ts.map +1 -0
- package/dest/slash_round_monitor.js +52 -0
- package/dest/slasher_client_facade.d.ts +44 -0
- package/dest/slasher_client_facade.d.ts.map +1 -0
- package/dest/slasher_client_facade.js +76 -0
- package/dest/slasher_client_interface.d.ts +39 -0
- package/dest/slasher_client_interface.d.ts.map +1 -0
- package/dest/slasher_client_interface.js +4 -0
- package/dest/stores/offenses_store.d.ts +37 -0
- package/dest/stores/offenses_store.d.ts.map +1 -0
- package/dest/stores/offenses_store.js +105 -0
- package/dest/stores/payloads_store.d.ts +29 -0
- package/dest/stores/payloads_store.d.ts.map +1 -0
- package/dest/stores/payloads_store.js +125 -0
- package/dest/stores/schema_version.d.ts +2 -0
- package/dest/stores/schema_version.d.ts.map +1 -0
- package/dest/stores/schema_version.js +1 -0
- package/dest/tally_slasher_client.d.ts +125 -0
- package/dest/tally_slasher_client.d.ts.map +1 -0
- package/dest/tally_slasher_client.js +349 -0
- package/dest/test/dummy_watcher.d.ts +11 -0
- package/dest/test/dummy_watcher.d.ts.map +1 -0
- package/dest/test/dummy_watcher.js +14 -0
- package/dest/watcher.d.ts +21 -0
- package/dest/watcher.d.ts.map +1 -0
- package/dest/watcher.js +1 -0
- package/dest/watchers/attestations_block_watcher.d.ts +33 -0
- package/dest/watchers/attestations_block_watcher.d.ts.map +1 -0
- package/dest/watchers/attestations_block_watcher.js +136 -0
- package/dest/watchers/epoch_prune_watcher.d.ts +37 -0
- package/dest/watchers/epoch_prune_watcher.d.ts.map +1 -0
- package/dest/watchers/epoch_prune_watcher.js +135 -0
- package/package.json +90 -0
- package/src/config.ts +157 -0
- package/src/empire_slasher_client.ts +657 -0
- package/src/factory/create_facade.ts +52 -0
- package/src/factory/create_implementation.ts +159 -0
- package/src/factory/get_settings.ts +58 -0
- package/src/factory/index.ts +2 -0
- package/src/index.ts +10 -0
- package/src/null_slasher_client.ts +41 -0
- package/src/slash_offenses_collector.ts +118 -0
- package/src/slash_round_monitor.ts +62 -0
- package/src/slasher_client_facade.ts +101 -0
- package/src/slasher_client_interface.ts +46 -0
- package/src/stores/offenses_store.ts +145 -0
- package/src/stores/payloads_store.ts +146 -0
- package/src/stores/schema_version.ts +1 -0
- package/src/tally_slasher_client.ts +442 -0
- package/src/test/dummy_watcher.ts +21 -0
- package/src/watcher.ts +27 -0
- package/src/watchers/attestations_block_watcher.ts +181 -0
- package/src/watchers/epoch_prune_watcher.ts +193 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { AztecAsyncKVStore } from '@aztec/kv-store';
|
|
2
|
+
import { type Offense, type OffenseIdentifier } from '@aztec/stdlib/slashing';
|
|
3
|
+
export declare const SCHEMA_VERSION = 1;
|
|
4
|
+
export declare class SlasherOffensesStore {
|
|
5
|
+
private kvStore;
|
|
6
|
+
private settings;
|
|
7
|
+
/** Map from offense key to offense data */
|
|
8
|
+
private offenses;
|
|
9
|
+
/** Map from offense key to whether the offense has been executed (only used for empire based slashing) */
|
|
10
|
+
private offensesSlashed;
|
|
11
|
+
/** Multimap from round to offense keys (only used for consensus based slashing) */
|
|
12
|
+
private roundsOffenses;
|
|
13
|
+
private log;
|
|
14
|
+
constructor(kvStore: AztecAsyncKVStore, settings: {
|
|
15
|
+
slashingRoundSize: number;
|
|
16
|
+
epochDuration: number;
|
|
17
|
+
slashOffenseExpirationRounds?: number;
|
|
18
|
+
});
|
|
19
|
+
/** Returns all offenses not marked as slashed */
|
|
20
|
+
getPendingOffenses(): Promise<Offense[]>;
|
|
21
|
+
/** Returns all offenses tracked for the given round */
|
|
22
|
+
getOffensesForRound(round: bigint): Promise<Offense[]>;
|
|
23
|
+
/** Returns whether an offense is pending (ie not marked as slashed) */
|
|
24
|
+
hasPendingOffense(offense: OffenseIdentifier): Promise<boolean>;
|
|
25
|
+
/** Returns whether we have seen this offense */
|
|
26
|
+
hasOffense(offense: OffenseIdentifier): Promise<boolean>;
|
|
27
|
+
/** Adds a new offense (defaults to pending, but will be slashed if markAsSlashed had been called for it) */
|
|
28
|
+
addPendingOffense(offense: Offense): Promise<void>;
|
|
29
|
+
/** Marks the given offenses as slashed (regardless of whether they are known or not) */
|
|
30
|
+
markAsSlashed(offenses: OffenseIdentifier[]): Promise<void>;
|
|
31
|
+
/** Prunes all offenses expired from the store */
|
|
32
|
+
clearExpiredOffenses(currentRound: bigint): Promise<number>;
|
|
33
|
+
/** Generate a unique key for an offense */
|
|
34
|
+
private getOffenseKey;
|
|
35
|
+
private getRoundKey;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib2ZmZW5zZXNfc3RvcmUuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zdG9yZXMvb2ZmZW5zZXNfc3RvcmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQ0EsT0FBTyxLQUFLLEVBQUUsaUJBQWlCLEVBQW9ELE1BQU0saUJBQWlCLENBQUM7QUFDM0csT0FBTyxFQUNMLEtBQUssT0FBTyxFQUNaLEtBQUssaUJBQWlCLEVBSXZCLE1BQU0sd0JBQXdCLENBQUM7QUFFaEMsZUFBTyxNQUFNLGNBQWMsSUFBSSxDQUFDO0FBRWhDLHFCQUFhLG9CQUFvQjtJQWE3QixPQUFPLENBQUMsT0FBTztJQUNmLE9BQU8sQ0FBQyxRQUFRO0lBYmxCLDJDQUEyQztJQUMzQyxPQUFPLENBQUMsUUFBUSxDQUFnQztJQUVoRCwwR0FBMEc7SUFDMUcsT0FBTyxDQUFDLGVBQWUsQ0FBd0I7SUFFL0MsbUZBQW1GO0lBQ25GLE9BQU8sQ0FBQyxjQUFjLENBQXFDO0lBRTNELE9BQU8sQ0FBQyxHQUFHLENBQTBDO0lBRXJELFlBQ1UsT0FBTyxFQUFFLGlCQUFpQixFQUMxQixRQUFRLEVBQUU7UUFDaEIsaUJBQWlCLEVBQUUsTUFBTSxDQUFDO1FBQzFCLGFBQWEsRUFBRSxNQUFNLENBQUM7UUFDdEIsNEJBQTRCLENBQUMsRUFBRSxNQUFNLENBQUM7S0FDdkMsRUFLRjtJQUVELGlEQUFpRDtJQUNwQyxrQkFBa0IsSUFBSSxPQUFPLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FVcEQ7SUFFRCx1REFBdUQ7SUFDMUMsbUJBQW1CLENBQUMsS0FBSyxFQUFFLE1BQU0sR0FBRyxPQUFPLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FVbEU7SUFFRCx1RUFBdUU7SUFDMUQsaUJBQWlCLENBQUMsT0FBTyxFQUFFLGlCQUFpQixHQUFHLE9BQU8sQ0FBQyxPQUFPLENBQUMsQ0FHM0U7SUFFRCxnREFBZ0Q7SUFDbkMsVUFBVSxDQUFDLE9BQU8sRUFBRSxpQkFBaUIsR0FBRyxPQUFPLENBQUMsT0FBTyxDQUFDLENBR3BFO0lBRUQsNEdBQTRHO0lBQy9GLGlCQUFpQixDQUFDLE9BQU8sRUFBRSxPQUFPLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxDQU05RDtJQUVELHlGQUF5RjtJQUM1RSxhQUFhLENBQUMsUUFBUSxFQUFFLGlCQUFpQixFQUFFLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxDQU92RTtJQUVELGlEQUFpRDtJQUNwQyxvQkFBb0IsQ0FBQyxZQUFZLEVBQUUsTUFBTSxHQUFHLE9BQU8sQ0FBQyxNQUFNLENBQUMsQ0F1Q3ZFO0lBRUQsMkNBQTJDO0lBQzNDLE9BQU8sQ0FBQyxhQUFhO0lBSXJCLE9BQU8sQ0FBQyxXQUFXO0NBR3BCIn0=
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"offenses_store.d.ts","sourceRoot":"","sources":["../../src/stores/offenses_store.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAoD,MAAM,iBAAiB,CAAC;AAC3G,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,iBAAiB,EAIvB,MAAM,wBAAwB,CAAC;AAEhC,eAAO,MAAM,cAAc,IAAI,CAAC;AAEhC,qBAAa,oBAAoB;IAa7B,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,QAAQ;IAblB,2CAA2C;IAC3C,OAAO,CAAC,QAAQ,CAAgC;IAEhD,0GAA0G;IAC1G,OAAO,CAAC,eAAe,CAAwB;IAE/C,mFAAmF;IACnF,OAAO,CAAC,cAAc,CAAqC;IAE3D,OAAO,CAAC,GAAG,CAA0C;IAErD,YACU,OAAO,EAAE,iBAAiB,EAC1B,QAAQ,EAAE;QAChB,iBAAiB,EAAE,MAAM,CAAC;QAC1B,aAAa,EAAE,MAAM,CAAC;QACtB,4BAA4B,CAAC,EAAE,MAAM,CAAC;KACvC,EAKF;IAED,iDAAiD;IACpC,kBAAkB,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC,CAUpD;IAED,uDAAuD;IAC1C,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAUlE;IAED,uEAAuE;IAC1D,iBAAiB,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,OAAO,CAAC,CAG3E;IAED,gDAAgD;IACnC,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,OAAO,CAAC,CAGpE;IAED,4GAA4G;IAC/F,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAM9D;IAED,yFAAyF;IAC5E,aAAa,CAAC,QAAQ,EAAE,iBAAiB,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAOvE;IAED,iDAAiD;IACpC,oBAAoB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAuCvE;IAED,2CAA2C;IAC3C,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,WAAW;CAGpB"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { createLogger } from '@aztec/aztec.js/log';
|
|
2
|
+
import { deserializeOffense, getRoundForOffense, serializeOffense } from '@aztec/stdlib/slashing';
|
|
3
|
+
export const SCHEMA_VERSION = 1;
|
|
4
|
+
export class SlasherOffensesStore {
|
|
5
|
+
kvStore;
|
|
6
|
+
settings;
|
|
7
|
+
/** Map from offense key to offense data */ offenses;
|
|
8
|
+
/** Map from offense key to whether the offense has been executed (only used for empire based slashing) */ offensesSlashed;
|
|
9
|
+
/** Multimap from round to offense keys (only used for consensus based slashing) */ roundsOffenses;
|
|
10
|
+
log;
|
|
11
|
+
constructor(kvStore, settings){
|
|
12
|
+
this.kvStore = kvStore;
|
|
13
|
+
this.settings = settings;
|
|
14
|
+
this.log = createLogger('slasher:store:offenses');
|
|
15
|
+
this.offenses = kvStore.openMap('offenses');
|
|
16
|
+
this.roundsOffenses = kvStore.openMultiMap('rounds-offenses');
|
|
17
|
+
this.offensesSlashed = kvStore.openSet('offenses-slashed');
|
|
18
|
+
}
|
|
19
|
+
/** Returns all offenses not marked as slashed */ async getPendingOffenses() {
|
|
20
|
+
const offenses = [];
|
|
21
|
+
for await (const [key, buffer] of this.offenses.entriesAsync()){
|
|
22
|
+
if (await this.offensesSlashed.hasAsync(key)) {
|
|
23
|
+
continue; // Skip executed offenses
|
|
24
|
+
}
|
|
25
|
+
const offense = deserializeOffense(buffer);
|
|
26
|
+
offenses.push(offense);
|
|
27
|
+
}
|
|
28
|
+
return offenses;
|
|
29
|
+
}
|
|
30
|
+
/** Returns all offenses tracked for the given round */ async getOffensesForRound(round) {
|
|
31
|
+
const offenses = [];
|
|
32
|
+
for await (const key of this.roundsOffenses.getValuesAsync(this.getRoundKey(round))){
|
|
33
|
+
const buffer = await this.offenses.getAsync(key);
|
|
34
|
+
if (buffer) {
|
|
35
|
+
const offense = deserializeOffense(buffer);
|
|
36
|
+
offenses.push(offense);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return offenses;
|
|
40
|
+
}
|
|
41
|
+
/** Returns whether an offense is pending (ie not marked as slashed) */ async hasPendingOffense(offense) {
|
|
42
|
+
const key = this.getOffenseKey(offense);
|
|
43
|
+
return await this.offenses.getAsync(key) !== undefined && !await this.offensesSlashed.hasAsync(key);
|
|
44
|
+
}
|
|
45
|
+
/** Returns whether we have seen this offense */ async hasOffense(offense) {
|
|
46
|
+
const key = this.getOffenseKey(offense);
|
|
47
|
+
return await this.offenses.getAsync(key) !== undefined;
|
|
48
|
+
}
|
|
49
|
+
/** Adds a new offense (defaults to pending, but will be slashed if markAsSlashed had been called for it) */ async addPendingOffense(offense) {
|
|
50
|
+
const key = this.getOffenseKey(offense);
|
|
51
|
+
await this.offenses.set(key, serializeOffense(offense));
|
|
52
|
+
const round = getRoundForOffense(offense, this.settings);
|
|
53
|
+
await this.roundsOffenses.set(this.getRoundKey(round), key);
|
|
54
|
+
this.log.trace(`Adding pending offense ${key} for round ${round}`);
|
|
55
|
+
}
|
|
56
|
+
/** Marks the given offenses as slashed (regardless of whether they are known or not) */ async markAsSlashed(offenses) {
|
|
57
|
+
await this.kvStore.transactionAsync(async ()=>{
|
|
58
|
+
for (const offense of offenses){
|
|
59
|
+
const key = this.getOffenseKey(offense);
|
|
60
|
+
await this.offensesSlashed.add(key);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
/** Prunes all offenses expired from the store */ async clearExpiredOffenses(currentRound) {
|
|
65
|
+
const expirationRounds = this.settings.slashOffenseExpirationRounds ?? 0;
|
|
66
|
+
if (expirationRounds <= 0) {
|
|
67
|
+
return 0; // No expiration configured
|
|
68
|
+
}
|
|
69
|
+
const expiredBefore = currentRound - BigInt(expirationRounds);
|
|
70
|
+
if (expiredBefore < 0) {
|
|
71
|
+
return 0; // Not enough rounds have passed to expire anything
|
|
72
|
+
}
|
|
73
|
+
// Collect expired offenses and rounds
|
|
74
|
+
const expiredRoundKeys = new Set();
|
|
75
|
+
const expiredOffenseKeys = new Set();
|
|
76
|
+
for await (const [roundKey, offenseKey] of this.roundsOffenses.entriesAsync({
|
|
77
|
+
end: this.getRoundKey(expiredBefore)
|
|
78
|
+
})){
|
|
79
|
+
expiredOffenseKeys.add(offenseKey);
|
|
80
|
+
expiredRoundKeys.add(roundKey);
|
|
81
|
+
}
|
|
82
|
+
if (expiredOffenseKeys.size === 0 && expiredRoundKeys.size === 0) {
|
|
83
|
+
return 0; // Nothing to clean up
|
|
84
|
+
}
|
|
85
|
+
// Remove expired stuff in a transaction
|
|
86
|
+
await this.kvStore.transactionAsync(async ()=>{
|
|
87
|
+
for (const key of expiredOffenseKeys){
|
|
88
|
+
this.log.trace(`Deleting offense ${key}`);
|
|
89
|
+
await this.offenses.delete(key);
|
|
90
|
+
await this.offensesSlashed.delete(key);
|
|
91
|
+
}
|
|
92
|
+
for (const roundKey of expiredRoundKeys){
|
|
93
|
+
this.log.trace(`Deleting round info for ${roundKey}`);
|
|
94
|
+
await this.roundsOffenses.delete(roundKey);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
return expiredOffenseKeys.size;
|
|
98
|
+
}
|
|
99
|
+
/** Generate a unique key for an offense */ getOffenseKey(offense) {
|
|
100
|
+
return `${offense.validator.toString()}:${offense.offenseType}:${offense.epochOrSlot}`;
|
|
101
|
+
}
|
|
102
|
+
getRoundKey(round) {
|
|
103
|
+
return round.toString().padStart(16, '0');
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
2
|
+
import type { AztecAsyncKVStore } from '@aztec/kv-store';
|
|
3
|
+
import { type SlashPayload, type SlashPayloadRound } from '@aztec/stdlib/slashing';
|
|
4
|
+
export declare class SlasherPayloadsStore {
|
|
5
|
+
private kvStore;
|
|
6
|
+
private settings?;
|
|
7
|
+
/** Map from payload address to payload data */
|
|
8
|
+
private payloads;
|
|
9
|
+
/** Map from `round:payload` to votes */
|
|
10
|
+
private roundPayloadVotes;
|
|
11
|
+
constructor(kvStore: AztecAsyncKVStore, settings?: {
|
|
12
|
+
slashingPayloadLifetimeInRounds?: number | undefined;
|
|
13
|
+
} | undefined);
|
|
14
|
+
getPayloadsForRound(round: bigint): Promise<SlashPayloadRound[]>;
|
|
15
|
+
getPayloadAtRound(payloadAddress: EthAddress, round: bigint): Promise<SlashPayloadRound | undefined>;
|
|
16
|
+
private getVotesForRound;
|
|
17
|
+
private getRoundKey;
|
|
18
|
+
private getPayloadVotesKey;
|
|
19
|
+
private getPayloadVotesKeyRangeForRound;
|
|
20
|
+
/**
|
|
21
|
+
* Purge vote payload data for expired rounds. Does not delete actual payload data.
|
|
22
|
+
*/
|
|
23
|
+
clearExpiredPayloads(currentRound: bigint): Promise<void>;
|
|
24
|
+
incrementPayloadVotes(payloadAddress: EthAddress, round: bigint): Promise<bigint>;
|
|
25
|
+
addPayload(payload: SlashPayloadRound): Promise<void>;
|
|
26
|
+
getPayload(payloadAddress: EthAddress | string): Promise<SlashPayload | undefined>;
|
|
27
|
+
hasPayload(payload: EthAddress): Promise<boolean>;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGF5bG9hZHNfc3RvcmUuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zdG9yZXMvcGF5bG9hZHNfc3RvcmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLCtCQUErQixDQUFDO0FBQzNELE9BQU8sS0FBSyxFQUFFLGlCQUFpQixFQUFpQixNQUFNLGlCQUFpQixDQUFDO0FBQ3hFLE9BQU8sRUFDTCxLQUFLLFlBQVksRUFDakIsS0FBSyxpQkFBaUIsRUFHdkIsTUFBTSx3QkFBd0IsQ0FBQztBQUVoQyxxQkFBYSxvQkFBb0I7SUFRN0IsT0FBTyxDQUFDLE9BQU87SUFDZixPQUFPLENBQUMsUUFBUSxDQUFDO0lBUm5CLCtDQUErQztJQUMvQyxPQUFPLENBQUMsUUFBUSxDQUFnQztJQUVoRCx3Q0FBd0M7SUFDeEMsT0FBTyxDQUFDLGlCQUFpQixDQUFnQztJQUV6RCxZQUNVLE9BQU8sRUFBRSxpQkFBaUIsRUFDMUIsUUFBUSxDQUFDOztpQkFFaEIsRUFJRjtJQUVZLG1CQUFtQixDQUFDLEtBQUssRUFBRSxNQUFNLEdBQUcsT0FBTyxDQUFDLGlCQUFpQixFQUFFLENBQUMsQ0FVNUU7SUFFWSxpQkFBaUIsQ0FBQyxjQUFjLEVBQUUsVUFBVSxFQUFFLEtBQUssRUFBRSxNQUFNLEdBQUcsT0FBTyxDQUFDLGlCQUFpQixHQUFHLFNBQVMsQ0FBQyxDQVdoSDtZQUVhLGdCQUFnQjtJQVk5QixPQUFPLENBQUMsV0FBVztJQUluQixPQUFPLENBQUMsa0JBQWtCO0lBSTFCLE9BQU8sQ0FBQywrQkFBK0I7SUFNdkM7O09BRUc7SUFDVSxvQkFBb0IsQ0FBQyxZQUFZLEVBQUUsTUFBTSxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FvQ3JFO0lBRVkscUJBQXFCLENBQUMsY0FBYyxFQUFFLFVBQVUsRUFBRSxLQUFLLEVBQUUsTUFBTSxHQUFHLE9BQU8sQ0FBQyxNQUFNLENBQUMsQ0FNN0Y7SUFFWSxVQUFVLENBQUMsT0FBTyxFQUFFLGlCQUFpQixHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FPakU7SUFFWSxVQUFVLENBQUMsY0FBYyxFQUFFLFVBQVUsR0FBRyxNQUFNLEdBQUcsT0FBTyxDQUFDLFlBQVksR0FBRyxTQUFTLENBQUMsQ0FJOUY7SUFFWSxVQUFVLENBQUMsT0FBTyxFQUFFLFVBQVUsR0FBRyxPQUFPLENBQUMsT0FBTyxDQUFDLENBRzdEO0NBQ0YifQ==
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payloads_store.d.ts","sourceRoot":"","sources":["../../src/stores/payloads_store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAC3D,OAAO,KAAK,EAAE,iBAAiB,EAAiB,MAAM,iBAAiB,CAAC;AACxE,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,iBAAiB,EAGvB,MAAM,wBAAwB,CAAC;AAEhC,qBAAa,oBAAoB;IAQ7B,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,QAAQ,CAAC;IARnB,+CAA+C;IAC/C,OAAO,CAAC,QAAQ,CAAgC;IAEhD,wCAAwC;IACxC,OAAO,CAAC,iBAAiB,CAAgC;IAEzD,YACU,OAAO,EAAE,iBAAiB,EAC1B,QAAQ,CAAC;;iBAEhB,EAIF;IAEY,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAU5E;IAEY,iBAAiB,CAAC,cAAc,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC,CAWhH;YAEa,gBAAgB;IAY9B,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,kBAAkB;IAI1B,OAAO,CAAC,+BAA+B;IAMvC;;OAEG;IACU,oBAAoB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAoCrE;IAEY,qBAAqB,CAAC,cAAc,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAM7F;IAEY,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAOjE;IAEY,UAAU,CAAC,cAAc,EAAE,UAAU,GAAG,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,CAI9F;IAEY,UAAU,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,CAG7D;CACF"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { deserializeSlashPayload, serializeSlashPayload } from '@aztec/stdlib/slashing';
|
|
2
|
+
export class SlasherPayloadsStore {
|
|
3
|
+
kvStore;
|
|
4
|
+
settings;
|
|
5
|
+
/** Map from payload address to payload data */ payloads;
|
|
6
|
+
/** Map from `round:payload` to votes */ roundPayloadVotes;
|
|
7
|
+
constructor(kvStore, settings){
|
|
8
|
+
this.kvStore = kvStore;
|
|
9
|
+
this.settings = settings;
|
|
10
|
+
this.payloads = kvStore.openMap('slash-payloads');
|
|
11
|
+
this.roundPayloadVotes = kvStore.openMap('round-payload-votes');
|
|
12
|
+
}
|
|
13
|
+
async getPayloadsForRound(round) {
|
|
14
|
+
const payloads = [];
|
|
15
|
+
const votes = await this.getVotesForRound(round);
|
|
16
|
+
for (const [address, votesCount] of votes){
|
|
17
|
+
const payload = await this.getPayload(address);
|
|
18
|
+
if (payload) {
|
|
19
|
+
payloads.push({
|
|
20
|
+
...payload,
|
|
21
|
+
votes: votesCount,
|
|
22
|
+
round
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return payloads;
|
|
27
|
+
}
|
|
28
|
+
async getPayloadAtRound(payloadAddress, round) {
|
|
29
|
+
const address = payloadAddress.toString();
|
|
30
|
+
const buffer = await this.payloads.getAsync(address);
|
|
31
|
+
if (!buffer) {
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
const data = deserializeSlashPayload(buffer);
|
|
35
|
+
const votes = await this.roundPayloadVotes.getAsync(this.getPayloadVotesKey(round, address)) ?? 0n;
|
|
36
|
+
return {
|
|
37
|
+
...data,
|
|
38
|
+
votes,
|
|
39
|
+
round
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
async getVotesForRound(round) {
|
|
43
|
+
const votes = [];
|
|
44
|
+
for await (const [fullKey, roundVotes] of this.roundPayloadVotes.entriesAsync(this.getPayloadVotesKeyRangeForRound(round))){
|
|
45
|
+
// Extract just the address part from the key (remove "round:" prefix)
|
|
46
|
+
const address = fullKey.split(':')[1];
|
|
47
|
+
votes.push([
|
|
48
|
+
address,
|
|
49
|
+
roundVotes
|
|
50
|
+
]);
|
|
51
|
+
}
|
|
52
|
+
return votes;
|
|
53
|
+
}
|
|
54
|
+
getRoundKey(round) {
|
|
55
|
+
return round.toString().padStart(16, '0');
|
|
56
|
+
}
|
|
57
|
+
getPayloadVotesKey(round, payloadAddress) {
|
|
58
|
+
return `${this.getRoundKey(round)}:${payloadAddress.toString()}`;
|
|
59
|
+
}
|
|
60
|
+
getPayloadVotesKeyRangeForRound(round) {
|
|
61
|
+
const start = `${this.getRoundKey(round)}:`;
|
|
62
|
+
const end = `${this.getRoundKey(round)}:Z`; // 'Z' sorts after any hex address, 0x-prefixed or not
|
|
63
|
+
return {
|
|
64
|
+
start,
|
|
65
|
+
end
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Purge vote payload data for expired rounds. Does not delete actual payload data.
|
|
70
|
+
*/ async clearExpiredPayloads(currentRound) {
|
|
71
|
+
const lifetimeInRounds = this.settings?.slashingPayloadLifetimeInRounds ?? 0;
|
|
72
|
+
if (lifetimeInRounds <= 0) {
|
|
73
|
+
return; // No lifetime configured
|
|
74
|
+
}
|
|
75
|
+
const expiredBefore = currentRound - BigInt(lifetimeInRounds);
|
|
76
|
+
if (expiredBefore < 0) {
|
|
77
|
+
return; // Not enough rounds have passed to expire anything
|
|
78
|
+
}
|
|
79
|
+
// Collect expired payload votes by scanning round-payload keys
|
|
80
|
+
const expiredPayloads = [];
|
|
81
|
+
const expiredVoteKeys = [];
|
|
82
|
+
for await (const key of this.roundPayloadVotes.keysAsync({
|
|
83
|
+
end: `${this.getRoundKey(expiredBefore)}:Z`
|
|
84
|
+
})){
|
|
85
|
+
const [roundStr, payloadAddress] = key.split(':');
|
|
86
|
+
if (BigInt(roundStr) <= expiredBefore) {
|
|
87
|
+
expiredVoteKeys.push(key);
|
|
88
|
+
expiredPayloads.push(payloadAddress);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (expiredVoteKeys.length === 0) {
|
|
92
|
+
return; // No expired payloads to clean up
|
|
93
|
+
}
|
|
94
|
+
// Remove expired payload vote records
|
|
95
|
+
// Note that we do not delete payload data since these could be repurposed in future votes
|
|
96
|
+
await this.kvStore.transactionAsync(async ()=>{
|
|
97
|
+
for (const key of expiredVoteKeys){
|
|
98
|
+
await this.roundPayloadVotes.delete(key);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
async incrementPayloadVotes(payloadAddress, round) {
|
|
103
|
+
const key = this.getPayloadVotesKey(round, payloadAddress);
|
|
104
|
+
const currentVotes = await this.roundPayloadVotes.getAsync(key) || 0n;
|
|
105
|
+
const newVotes = currentVotes + 1n;
|
|
106
|
+
await this.roundPayloadVotes.set(key, newVotes);
|
|
107
|
+
return newVotes;
|
|
108
|
+
}
|
|
109
|
+
async addPayload(payload) {
|
|
110
|
+
const address = payload.address.toString();
|
|
111
|
+
await this.kvStore.transactionAsync(async ()=>{
|
|
112
|
+
await this.payloads.set(address, serializeSlashPayload(payload));
|
|
113
|
+
await this.roundPayloadVotes.set(this.getPayloadVotesKey(payload.round, address), payload.votes);
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
async getPayload(payloadAddress) {
|
|
117
|
+
const address = payloadAddress.toString();
|
|
118
|
+
const buffer = await this.payloads.getAsync(address);
|
|
119
|
+
return buffer ? deserializeSlashPayload(buffer) : undefined;
|
|
120
|
+
}
|
|
121
|
+
async hasPayload(payload) {
|
|
122
|
+
const address = payload.toString();
|
|
123
|
+
return await this.payloads.getAsync(address) !== undefined;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export declare const SCHEMA_VERSION = 1;
|
|
2
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2NoZW1hX3ZlcnNpb24uZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zdG9yZXMvc2NoZW1hX3ZlcnNpb24udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsZUFBTyxNQUFNLGNBQWMsSUFBSSxDQUFDIn0=
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema_version.d.ts","sourceRoot":"","sources":["../../src/stores/schema_version.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,cAAc,IAAI,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const SCHEMA_VERSION = 1;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import type { EpochCache } from '@aztec/epoch-cache';
|
|
2
|
+
import { RollupContract, SlasherContract, TallySlashingProposerContract } from '@aztec/ethereum/contracts';
|
|
3
|
+
import { SlotNumber } from '@aztec/foundation/branded-types';
|
|
4
|
+
import type { DateProvider } from '@aztec/foundation/timer';
|
|
5
|
+
import type { Prettify } from '@aztec/foundation/types';
|
|
6
|
+
import type { SlasherConfig } from '@aztec/stdlib/interfaces/server';
|
|
7
|
+
import { type Offense, type ProposerSlashAction, type ProposerSlashActionProvider, type SlashPayloadRound } from '@aztec/stdlib/slashing';
|
|
8
|
+
import type { Hex } from 'viem';
|
|
9
|
+
import { SlashOffensesCollector, type SlashOffensesCollectorConfig, type SlashOffensesCollectorSettings } from './slash_offenses_collector.js';
|
|
10
|
+
import { SlashRoundMonitor, type SlashRoundMonitorSettings } from './slash_round_monitor.js';
|
|
11
|
+
import type { SlasherClientInterface } from './slasher_client_interface.js';
|
|
12
|
+
import type { SlasherOffensesStore } from './stores/offenses_store.js';
|
|
13
|
+
import type { Watcher } from './watcher.js';
|
|
14
|
+
/** Settings used in the tally slasher client, loaded from the L1 contracts during initialization */
|
|
15
|
+
export type TallySlasherSettings = Prettify<SlashRoundMonitorSettings & SlashOffensesCollectorSettings & {
|
|
16
|
+
slashingLifetimeInRounds: number;
|
|
17
|
+
slashingExecutionDelayInRounds: number;
|
|
18
|
+
slashingRoundSizeInEpochs: number;
|
|
19
|
+
slashingOffsetInRounds: number;
|
|
20
|
+
slashingQuorumSize: number;
|
|
21
|
+
slashingAmounts: [bigint, bigint, bigint];
|
|
22
|
+
/** Committee size for block proposal */
|
|
23
|
+
targetCommitteeSize: number;
|
|
24
|
+
}>;
|
|
25
|
+
export type TallySlasherClientConfig = SlashOffensesCollectorConfig & Pick<SlasherConfig, 'slashValidatorsAlways' | 'slashValidatorsNever' | 'slashExecuteRoundsLookBack'>;
|
|
26
|
+
/**
|
|
27
|
+
* The Tally Slasher client is responsible for managing slashable offenses using
|
|
28
|
+
* the consensus-based slashing model where proposers vote on individual validator offenses.
|
|
29
|
+
*
|
|
30
|
+
* The client subscribes to several slash watchers that emit offenses and tracks them. When the slasher is the
|
|
31
|
+
* proposer, it votes for which validators from past epochs should be slashed based on collected offenses.
|
|
32
|
+
* Voting is handled by the sequencer publisher, the slasher client does not interact with L1 directly.
|
|
33
|
+
* The client also monitors rounds and executes slashing when rounds become executable after reaching quorum.
|
|
34
|
+
*
|
|
35
|
+
* Voting and offense collection
|
|
36
|
+
* - Time is divided into rounds (ROUND_SIZE slots each). During each round, block proposers can submit votes
|
|
37
|
+
* indicating which validators from SLASH_OFFSET_IN_ROUNDS rounds ago should be slashed.
|
|
38
|
+
* - Votes are encoded as bytes where each validator's vote is represented by 2 bits indicating the slash amount (0-3 slash units)
|
|
39
|
+
* for the validator in the committee being slashed.
|
|
40
|
+
* - When gathering offenses for round N, the system looks at offenses from round N-2 (where 2 is the hardcoded
|
|
41
|
+
* offset), giving time to detect offenses and vote on them in a later round.
|
|
42
|
+
* - Each offense carries an epoch or block identifier to differentiate multiple offenses by the same validator.
|
|
43
|
+
*
|
|
44
|
+
* Quorum and execution
|
|
45
|
+
* - After a round ends, there is an execution delay period for review so the VETOER in the Slasher can veto
|
|
46
|
+
* if needed.
|
|
47
|
+
* - Once the delay passes, anyone can call executeRound() to tally votes and execute slashing.
|
|
48
|
+
* - Validators that reach the quorum threshold are slashed. A vote for slashing N units is also considered
|
|
49
|
+
* a vote for slashing N-1, N-2, ..., 1 units. The system slashes for the largest amount that reaches quorum.
|
|
50
|
+
* - The client monitors executable rounds and triggers execution when appropriate.
|
|
51
|
+
*
|
|
52
|
+
* Differences from Empire model
|
|
53
|
+
* - No fixed slash payloads - votes are for individual validator offenses encoded in bytes
|
|
54
|
+
* - The L1 contract determines which offenses reach quorum rather than nodes agreeing on a payload
|
|
55
|
+
* - Proposers vote directly on which validators to slash and by how much
|
|
56
|
+
* - Uses a slash offset to vote on validators from past rounds (e.g., round N votes on round N-2)
|
|
57
|
+
*/
|
|
58
|
+
export declare class TallySlasherClient implements ProposerSlashActionProvider, SlasherClientInterface {
|
|
59
|
+
private config;
|
|
60
|
+
private settings;
|
|
61
|
+
private tallySlashingProposer;
|
|
62
|
+
private slasher;
|
|
63
|
+
private rollup;
|
|
64
|
+
private epochCache;
|
|
65
|
+
private dateProvider;
|
|
66
|
+
private offensesStore;
|
|
67
|
+
private log;
|
|
68
|
+
protected unwatchCallbacks: (() => void)[];
|
|
69
|
+
protected roundMonitor: SlashRoundMonitor;
|
|
70
|
+
protected offensesCollector: SlashOffensesCollector;
|
|
71
|
+
constructor(config: TallySlasherClientConfig, settings: TallySlasherSettings, tallySlashingProposer: TallySlashingProposerContract, slasher: SlasherContract, rollup: RollupContract, watchers: Watcher[], epochCache: EpochCache, dateProvider: DateProvider, offensesStore: SlasherOffensesStore, log?: import("@aztec/foundation/log").Logger);
|
|
72
|
+
start(): Promise<void>;
|
|
73
|
+
/**
|
|
74
|
+
* Stop the tally slasher client
|
|
75
|
+
*/
|
|
76
|
+
stop(): Promise<void>;
|
|
77
|
+
/** Returns the current config */
|
|
78
|
+
getConfig(): SlasherConfig;
|
|
79
|
+
/** Update the config of the slasher client */
|
|
80
|
+
updateConfig(config: Partial<SlasherConfig>): void;
|
|
81
|
+
/** Triggered on a time basis when we enter a new slashing round. Clears expired offenses. */
|
|
82
|
+
protected handleNewRound(round: bigint): Promise<void>;
|
|
83
|
+
/** Called when we see a RoundExecuted event on the TallySlashingProposer (just for logging). */
|
|
84
|
+
protected handleRoundExecuted(round: bigint, slashCount: bigint, l1BlockHash: Hex): Promise<void>;
|
|
85
|
+
/**
|
|
86
|
+
* Get the actions the proposer should take for slashing
|
|
87
|
+
* @param slotNumber - The current slot number
|
|
88
|
+
* @returns The actions to take
|
|
89
|
+
*/
|
|
90
|
+
getProposerActions(slotNumber: SlotNumber): Promise<ProposerSlashAction[]>;
|
|
91
|
+
/**
|
|
92
|
+
* Returns an execute slash action if there are any rounds ready to be executed.
|
|
93
|
+
* Returns the oldest slash action if there are multiple rounds pending execution.
|
|
94
|
+
*/
|
|
95
|
+
protected getExecuteSlashAction(slotNumber: SlotNumber): Promise<ProposerSlashAction | undefined>;
|
|
96
|
+
private tryGetRoundExecuteAction;
|
|
97
|
+
/** Returns a vote action based on offenses from the target round (with offset applied) */
|
|
98
|
+
protected getVoteOffensesAction(slotNumber: SlotNumber): Promise<ProposerSlashAction | undefined>;
|
|
99
|
+
/** Returns the committees that were active during the timespan of a given round */
|
|
100
|
+
private collectCommitteesActiveDuringRound;
|
|
101
|
+
/**
|
|
102
|
+
* Get slash payloads is NOT SUPPORTED in tally model
|
|
103
|
+
* @throws Error indicating this operation is not supported
|
|
104
|
+
*/
|
|
105
|
+
getSlashPayloads(): Promise<SlashPayloadRound[]>;
|
|
106
|
+
/**
|
|
107
|
+
* Gather offenses to be slashed on a given round.
|
|
108
|
+
* In tally slashing, round N slashes validators from round N - slashOffsetInRounds.
|
|
109
|
+
* @param round - The round to get offenses for, defaults to current round
|
|
110
|
+
* @returns Array of pending offenses for the round with offset applied
|
|
111
|
+
*/
|
|
112
|
+
gatherOffensesForRound(round?: bigint): Promise<Offense[]>;
|
|
113
|
+
/** Returns all pending offenses stored */
|
|
114
|
+
getPendingOffenses(): Promise<Offense[]>;
|
|
115
|
+
/**
|
|
116
|
+
* Returns the round to be slashed given the current round by applying the slash offset.
|
|
117
|
+
* During round N, we cannot slash the validators from the epochs of the same round, since the round is not over,
|
|
118
|
+
* and besides we would be asking the current validators to vote to slash themselves. So during round N we look at the
|
|
119
|
+
* epochs spanned during round N - SLASH_OFFSET_IN_ROUNDS. This offset means that the epochs we slash are complete,
|
|
120
|
+
* and also gives nodes time to detect any misbehavior (eg slashing for prunes requires the proof submission window to
|
|
121
|
+
* pass).
|
|
122
|
+
*/
|
|
123
|
+
private getSlashedRound;
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGFsbHlfc2xhc2hlcl9jbGllbnQuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy90YWxseV9zbGFzaGVyX2NsaWVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLEtBQUssRUFBRSxVQUFVLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUNyRCxPQUFPLEVBQUUsY0FBYyxFQUFFLGVBQWUsRUFBRSw2QkFBNkIsRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBRTNHLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUk3RCxPQUFPLEtBQUssRUFBRSxZQUFZLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUM1RCxPQUFPLEtBQUssRUFBRSxRQUFRLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUN4RCxPQUFPLEtBQUssRUFBRSxhQUFhLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUNyRSxPQUFPLEVBQ0wsS0FBSyxPQUFPLEVBRVosS0FBSyxtQkFBbUIsRUFDeEIsS0FBSywyQkFBMkIsRUFDaEMsS0FBSyxpQkFBaUIsRUFHdkIsTUFBTSx3QkFBd0IsQ0FBQztBQUVoQyxPQUFPLEtBQUssRUFBRSxHQUFHLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFFaEMsT0FBTyxFQUNMLHNCQUFzQixFQUN0QixLQUFLLDRCQUE0QixFQUNqQyxLQUFLLDhCQUE4QixFQUNwQyxNQUFNLCtCQUErQixDQUFDO0FBQ3ZDLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxLQUFLLHlCQUF5QixFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDN0YsT0FBTyxLQUFLLEVBQUUsc0JBQXNCLEVBQUUsTUFBTSwrQkFBK0IsQ0FBQztBQUM1RSxPQUFPLEtBQUssRUFBRSxvQkFBb0IsRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBQ3ZFLE9BQU8sS0FBSyxFQUFFLE9BQU8sRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUU1QyxvR0FBb0c7QUFDcEcsTUFBTSxNQUFNLG9C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
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tally_slasher_client.d.ts","sourceRoot":"","sources":["../src/tally_slasher_client.ts"],"names":[],"mappings":"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"}
|