@lodestar/beacon-node
Version:
A Typescript implementation of the beacon chain
129 lines • 5.16 kB
JavaScript
import { Repository } from "@lodestar/db";
import { ssz } from "@lodestar/types";
import { bytesToInt } from "@lodestar/utils";
import all from "it-all";
import { getSignedBlockTypeFromBytes } from "../../util/multifork.js";
import { Bucket, getBucketNameByValue } from "../buckets.js";
import { getParentRootIndexKey, getRootIndexKey } from "./blockArchiveIndex.js";
import { deleteParentRootIndex, deleteRootIndex, storeParentRootIndex, storeRootIndex } from "./blockArchiveIndex.js";
/**
* Stores finalized blocks. Block slot is identifier.
*/
export class BlockArchiveRepository extends Repository {
constructor(config, db) {
const bucket = Bucket.allForks_blockArchive;
const type = ssz.phase0.SignedBeaconBlock; // Pick some type but won't be used
super(config, db, bucket, type, getBucketNameByValue(bucket));
}
// Overrides for multi-fork
encodeValue(value) {
return this.config.getForkTypes(value.message.slot).SignedBeaconBlock.serialize(value);
}
decodeValue(data) {
return getSignedBlockTypeFromBytes(this.config, data).deserialize(data);
}
// Handle key as slot
getId(value) {
return value.message.slot;
}
decodeKey(data) {
return bytesToInt(super.decodeKey(data), "be");
}
// Overrides to index
async put(key, value) {
const blockRoot = this.config.getForkTypes(value.message.slot).BeaconBlock.hashTreeRoot(value.message);
const slot = value.message.slot;
await Promise.all([
super.put(key, value),
storeRootIndex(this.db, slot, blockRoot),
storeParentRootIndex(this.db, slot, value.message.parentRoot),
]);
}
async batchPut(items) {
await Promise.all([
super.batchPut(items),
Array.from(items).map((item) => {
const slot = item.value.message.slot;
const blockRoot = this.config.getForkTypes(slot).BeaconBlock.hashTreeRoot(item.value.message);
return storeRootIndex(this.db, slot, blockRoot);
}),
Array.from(items).map((item) => {
const slot = item.value.message.slot;
const parentRoot = item.value.message.parentRoot;
return storeParentRootIndex(this.db, slot, parentRoot);
}),
]);
}
async batchPutBinary(items) {
await Promise.all([
super.batchPutBinary(items),
Array.from(items).map((item) => storeRootIndex(this.db, item.slot, item.blockRoot)),
Array.from(items).map((item) => storeParentRootIndex(this.db, item.slot, item.parentRoot)),
]);
}
async remove(value) {
await Promise.all([
super.remove(value),
deleteRootIndex(this.db, this.config.getForkTypes(value.message.slot).SignedBeaconBlock, value),
deleteParentRootIndex(this.db, value),
]);
}
async batchRemove(values) {
await Promise.all([
super.batchRemove(values),
Array.from(values).map((value) => deleteRootIndex(this.db, this.config.getForkTypes(value.message.slot).SignedBeaconBlock, value)),
Array.from(values).map((value) => deleteParentRootIndex(this.db, value)),
]);
}
async *valuesStream(opts) {
const firstSlot = this.getFirstSlot(opts);
const valuesStream = super.valuesStream(opts);
const step = opts?.step ?? 1;
for await (const value of valuesStream) {
if ((value.message.slot - firstSlot) % step === 0) {
yield value;
}
}
}
async values(opts) {
return all(this.valuesStream(opts));
}
// INDEX
async getByRoot(root) {
const slot = await this.getSlotByRoot(root);
return slot !== null ? this.get(slot) : null;
}
async getBinaryEntryByRoot(root) {
const slot = await this.getSlotByRoot(root);
return slot !== null ? { key: slot, value: await this.getBinary(slot) } : null;
}
async getByParentRoot(root) {
const slot = await this.getSlotByParentRoot(root);
return slot !== null ? this.get(slot) : null;
}
async getSlotByRoot(root) {
return this.parseSlot(await this.db.get(getRootIndexKey(root)));
}
async getSlotByParentRoot(root) {
return this.parseSlot(await this.db.get(getParentRootIndexKey(root)));
}
parseSlot(slotBytes) {
if (!slotBytes)
return null;
const slot = bytesToInt(slotBytes, "be");
// TODO: Is this necessary? How can bytesToInt return a non-integer?
return Number.isInteger(slot) ? slot : null;
}
getFirstSlot(opts) {
const dbFilterOpts = this.dbFilterOptions(opts);
const firstSlot = dbFilterOpts.gt
? this.decodeKey(dbFilterOpts.gt) + 1
: dbFilterOpts.gte
? this.decodeKey(dbFilterOpts.gte)
: null;
if (firstSlot === null)
throw Error("specify opts.gt or opts.gte");
return firstSlot;
}
}
//# sourceMappingURL=blockArchive.js.map