-
-
Notifications
You must be signed in to change notification settings - Fork 479
Expand file tree
/
Copy pathattestationByTargetRepository.ts
More file actions
89 lines (79 loc) 路 3.42 KB
/
Copy pathattestationByTargetRepository.ts
File metadata and controls
89 lines (79 loc) 路 3.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import {ContainerType, Type} from "@chainsafe/ssz";
import {DB_PREFIX_LENGTH, DbReqOpts, encodeKey, uintLen} from "@lodestar/db";
import {BLSPubkey, Epoch, ssz} from "@lodestar/types";
import {bytesToInt, intToBytes} from "@lodestar/utils";
import {Bucket, getBucketNameByValue} from "../../buckets.js";
import {LodestarValidatorDatabaseController} from "../../types.js";
import {SlashingProtectionAttestation} from "../types.js";
import {blsPubkeyLen, uniqueVectorArr} from "../utils.js";
/**
* Manages validator db storage of attestations.
* Entries in the db are indexed by an encoded key which combines the validator's public key and the
* attestation's target epoch.
*/
export class AttestationByTargetRepository {
protected type: Type<SlashingProtectionAttestation>;
protected bucket = Bucket.slashingProtectionAttestationByTarget;
private readonly bucketId = getBucketNameByValue(this.bucket);
private readonly dbReqOpts: DbReqOpts = {bucketId: this.bucketId};
private readonly minKey: Uint8Array;
private readonly maxKey: Uint8Array;
constructor(protected db: LodestarValidatorDatabaseController) {
this.type = new ContainerType({
sourceEpoch: ssz.Epoch,
targetEpoch: ssz.Epoch,
signingRoot: ssz.Root,
}); // casing doesn't matter
this.minKey = encodeKey(this.bucket, Buffer.alloc(0));
this.maxKey = encodeKey(this.bucket + 1, Buffer.alloc(0));
}
async getAll(pubkey: BLSPubkey, limit?: number): Promise<SlashingProtectionAttestation[]> {
const attestations = await this.db.values({
limit,
gte: this.encodeKey(pubkey, 0),
lt: this.encodeKey(pubkey, Number.MAX_SAFE_INTEGER),
bucketId: this.bucketId,
});
return attestations.map((attestation) => this.type.deserialize(attestation));
}
/** Attestation with the highest target epoch recorded for `pubkey`, or null if none */
async getLatest(pubkey: BLSPubkey): Promise<SlashingProtectionAttestation | null> {
const [att] = await this.db.values({
gte: this.encodeKey(pubkey, 0),
lt: this.encodeKey(pubkey, Number.MAX_SAFE_INTEGER),
reverse: true,
limit: 1,
bucketId: this.bucketId,
});
return att ? this.type.deserialize(att) : null;
}
async get(pubkey: BLSPubkey, targetEpoch: Epoch): Promise<SlashingProtectionAttestation | null> {
const att = await this.db.get(this.encodeKey(pubkey, targetEpoch), this.dbReqOpts);
return att && this.type.deserialize(att);
}
async set(pubkey: BLSPubkey, atts: SlashingProtectionAttestation[]): Promise<void> {
await this.db.batchPut(
atts.map((att) => ({
key: this.encodeKey(pubkey, att.targetEpoch),
value: this.type.serialize(att),
})),
this.dbReqOpts
);
}
async listPubkeys(): Promise<BLSPubkey[]> {
const keys = await this.db.keys({gte: this.minKey, lt: this.maxKey, bucketId: this.bucketId});
return uniqueVectorArr(keys.map((key) => this.decodeKey(key).pubkey));
}
private encodeKey(pubkey: BLSPubkey, targetEpoch: Epoch): Uint8Array {
return encodeKey(this.bucket, Buffer.concat([pubkey, intToBytes(BigInt(targetEpoch), uintLen, "be")]));
}
private decodeKey(key: Uint8Array): {pubkey: BLSPubkey; targetEpoch: Epoch} {
return {
pubkey: key.slice(DB_PREFIX_LENGTH, DB_PREFIX_LENGTH + blsPubkeyLen),
targetEpoch: bytesToInt(
key.slice(DB_PREFIX_LENGTH + blsPubkeyLen, DB_PREFIX_LENGTH + blsPubkeyLen + uintLen),
"be"
),
};
}
}