Files
lodestar/packages/beacon-node/test/e2e/sync/finalizedSync.test.ts
Nazar Hussain 0c6f50771f chore: use latest TS module resolution (#8419)
**Motivation**

Use latest `module` and `moduleResolution` for TS.

**Description**

- To use [subpath
imports](https://nodejs.org/api/packages.html#subpath-imports) in the PR
#8320 we need to update the module solution strategy for TS.
- That requires to change the `module` for the TS as well. 
- Earlier tried to stay with `node18` or `node20`, but the `ts-node`
does not work with that.
- Maintaining different tsconfig for ts-node is more of hassle on wrong
run.
- So decided to stick with `nodenext` strategy for `moduleResolution` 

**Steps to test or reproduce**

Run all tests

---------

Co-authored-by: Cayman <caymannava@gmail.com>
2025-09-18 11:36:48 -04:00

148 lines
4.9 KiB
TypeScript

import {fromHexString} from "@chainsafe/ssz";
import {routes} from "@lodestar/api";
import {ChainConfig} from "@lodestar/config";
import {TimestampFormatCode} from "@lodestar/logger";
import {SLOTS_PER_EPOCH} from "@lodestar/params";
import {phase0} from "@lodestar/types";
import {afterEach, describe, expect, it, vi} from "vitest";
import {ChainEvent} from "../../../src/chain/index.js";
import {waitForEvent} from "../../utils/events/resolver.js";
import {LogLevel, TestLoggerOpts, testLogger} from "../../utils/logger.js";
import {connect, onPeerConnect} from "../../utils/network.js";
import {getDevBeaconNode} from "../../utils/node/beacon.js";
import {getAndInitDevValidators} from "../../utils/node/validator.js";
describe("sync / finalized sync for fulu", () => {
// chain is finalized at slot 32, plus 4 slots for genesis delay => ~72s it should sync pretty fast
vi.setConfig({testTimeout: 90_000});
const validatorCount = 8;
const ELECTRA_FORK_EPOCH = 0;
const FULU_FORK_EPOCH = 1;
const SECONDS_PER_SLOT = 2;
const testParams: Partial<ChainConfig> = {
SECONDS_PER_SLOT,
ALTAIR_FORK_EPOCH: ELECTRA_FORK_EPOCH,
BELLATRIX_FORK_EPOCH: ELECTRA_FORK_EPOCH,
CAPELLA_FORK_EPOCH: ELECTRA_FORK_EPOCH,
DENEB_FORK_EPOCH: ELECTRA_FORK_EPOCH,
ELECTRA_FORK_EPOCH: ELECTRA_FORK_EPOCH,
FULU_FORK_EPOCH: FULU_FORK_EPOCH,
BLOB_SCHEDULE: [
{
EPOCH: 1,
MAX_BLOBS_PER_BLOCK: 3,
},
],
};
const afterEachCallbacks: (() => Promise<unknown> | void)[] = [];
afterEach(async () => {
while (afterEachCallbacks.length > 0) {
const callback = afterEachCallbacks.pop();
if (callback) await callback();
}
});
it("should do a finalized sync from another BN", async () => {
// single node at beginning, use main thread to verify bls
const genesisSlotsDelay = 4;
const genesisTime = Math.floor(Date.now() / 1000) + genesisSlotsDelay * SECONDS_PER_SLOT;
const testLoggerOpts: TestLoggerOpts = {
level: LogLevel.info,
timestampFormat: {
format: TimestampFormatCode.EpochSlot,
genesisTime,
slotsPerEpoch: SLOTS_PER_EPOCH,
secondsPerSlot: SECONDS_PER_SLOT,
},
};
const loggerNodeA = testLogger("FinalizedSync-Node-A", testLoggerOpts);
const loggerNodeB = testLogger("FinalizedSync-Node-B", testLoggerOpts);
const bn = await getDevBeaconNode({
params: testParams,
options: {
sync: {isSingleNode: true},
network: {allowPublishToZeroPeers: true, useWorker: false},
chain: {blsVerifyAllMainThread: true},
},
validatorCount,
genesisTime,
logger: loggerNodeA,
});
afterEachCallbacks.push(() => bn.close());
const {validators} = await getAndInitDevValidators({
node: bn,
logPrefix: "FinalizedSyncVc",
validatorsPerClient: validatorCount,
validatorClientCount: 1,
startIndex: 0,
useRestApi: false,
testLoggerOpts,
});
afterEachCallbacks.push(() => Promise.all(validators.map((validator) => validator.close())));
// stop beacon node after validators
afterEachCallbacks.push(() => bn.close());
await Promise.all([
waitForEvent<phase0.Checkpoint>(
bn.chain.emitter,
ChainEvent.forkChoiceFinalized,
240000,
(finalized) => finalized.epoch >= FULU_FORK_EPOCH
),
waitForEvent<routes.events.EventData[routes.events.EventType.head]>(
bn.chain.emitter,
routes.events.EventType.head,
100000,
// at block slot 32 imported, finalized checkpoint epoch 2 is processed
({slot}) => slot === 32
),
]);
loggerNodeA.info("Node A emitted finalized checkpoint event for fulu");
const bn2 = await getDevBeaconNode({
params: testParams,
options: {
api: {rest: {enabled: false}},
network: {useWorker: false},
chain: {blsVerifyAllMainThread: true},
},
validatorCount,
genesisTime,
logger: loggerNodeB,
});
loggerNodeA.info("Node B created");
afterEachCallbacks.push(() => bn2.close());
afterEachCallbacks.push(() => bn2.close());
const headSummary = bn.chain.forkChoice.getHead();
const head = await bn.db.block.get(fromHexString(headSummary.blockRoot));
if (!head) throw Error("First beacon node has no head block");
const waitForSynced = waitForEvent<routes.events.EventData[routes.events.EventType.head]>(
bn2.chain.emitter,
routes.events.EventType.head,
100000,
({block}) => block === headSummary.blockRoot
);
await Promise.all([connect(bn2.network, bn.network), onPeerConnect(bn2.network), onPeerConnect(bn.network)]);
loggerNodeA.info("Node A connected to Node B");
try {
await waitForSynced;
loggerNodeB.info("Node B synced to Node A, received fulu head block", {slot: head.message.slot});
} catch (_e) {
expect.fail("Failed to sync to other node in time");
}
});
});