Skip to content

Commit

Permalink
Merge bf97301 into 5c1833d
Browse files Browse the repository at this point in the history
  • Loading branch information
dapplion authored May 13, 2022
2 parents 5c1833d + bf97301 commit 87a9cec
Show file tree
Hide file tree
Showing 18 changed files with 319 additions and 103 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/benchmark.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ jobs:
- name: Run benchmarks
run: yarn benchmark
env:
# To download content for tests
INFURA_ETH2_CREDENTIALS: ${{ secrets.INFURA_ETH2_CREDENTIALS }}
# To write to PRs and commits
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# benchmark options
BENCHMARK_S3: true
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ docs/reference/cli.md
.lodestar
*.ssz
.pyrmont
packages/lodestar/.tmpdb/
.tmpdb

# Wallet CLI artifacts
.pass
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
CachedBeaconStateAltair,
beforeProcessEpoch,
} from "../../../../src";
import {beforeValue, getNetworkCachedState, LazyValue} from "../../util";
import {getNetworkCachedState, beforeValue, LazyValue} from "../../../utils";
import {StateEpoch} from "../../types";
import {altairState} from "../../params";

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {getClient} from "@chainsafe/lodestar-api";
import {config} from "@chainsafe/lodestar-config/default";
import {getInfuraBeaconUrl} from "./infura";
import {getInfuraBeaconUrl} from "../utils/infura";

// Analyze how Ethereum Consensus blocks are in a target network to prepare accurate performance states and blocks

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
} from "../../src";
import {Validator} from "../../lib/phase0";
import {csvAppend, readCsv} from "./csv";
import {getInfuraBeaconUrl} from "./infura";
import {getInfuraBeaconUrl} from "../utils/infura";
import {createCachedBeaconStateTest} from "../utils/state";

// Understand the real network characteristics regarding epoch transitions to accurately produce performance test data.
Expand Down
6 changes: 6 additions & 0 deletions packages/beacon-state-transition/test/perf/params.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,9 @@ export const altairState = {
network: "mainnet" as const,
epoch: 81889, // Post altair fork
};

export const rangeSyncTest = {
network: "mainnet" as const,
startSlot: 3766816, // Post altair, first slot in epoch 117713
endSlot: 3766847, // 3766816 + 31, all blocks in same epoch
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
CachedBeaconStatePhase0,
beforeProcessEpoch,
} from "../../../../src";
import {beforeValue, getNetworkCachedState, LazyValue} from "../../util";
import {getNetworkCachedState, beforeValue, LazyValue} from "../../../utils";
import {processParticipationRecordUpdates} from "../../../../src/phase0/epoch/processParticipationRecordUpdates";
import {StateEpoch} from "../../types";
import {phase0State} from "../../params";
Expand Down
82 changes: 1 addition & 81 deletions packages/beacon-state-transition/test/perf/util.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import fs from "node:fs";
import path from "node:path";
import {config} from "@chainsafe/lodestar-config/default";
import {phase0, ssz, Slot, altair} from "@chainsafe/lodestar-types";
import bls, {CoordType, PublicKey, SecretKey} from "@chainsafe/bls";
Expand All @@ -14,7 +12,7 @@ import {
createCachedBeaconState,
computeCommitteeCount,
} from "../../src";
import {createIBeaconConfig, createIChainForkConfig, IChainForkConfig} from "@chainsafe/lodestar-config";
import {createIBeaconConfig, createIChainForkConfig} from "@chainsafe/lodestar-config";
import {
CachedBeaconStateAllForks,
CachedBeaconStatePhase0,
Expand All @@ -32,12 +30,7 @@ import {
SLOTS_PER_EPOCH,
SLOTS_PER_HISTORICAL_ROOT,
} from "@chainsafe/lodestar-params";
import {NetworkName, networksChainConfig} from "@chainsafe/lodestar-config/networks";
import {getClient} from "@chainsafe/lodestar-api";
import {getInfuraBeaconUrl} from "./infura";
import {testCachePath} from "../cache";
import {getNextSyncCommittee} from "../../src/util/syncCommittee";
import {createCachedBeaconStateTest} from "../utils/state";
import {getEffectiveBalanceIncrements} from "../../src/cache/effectiveBalanceIncrements";

let phase0State: BeaconStatePhase0 | null = null;
Expand Down Expand Up @@ -446,76 +439,3 @@ export function generateTestCachedBeaconStateOnlyValidators({
index2pubkey,
});
}

const initialValue = null;
export type LazyValue<T> = {value: T};

/**
* Register a callback to compute a value in the before() block of mocha tests
* ```ts
* const state = beforeValue(() => getState())
* it("test", () => {
* doTest(state.value)
* })
* ```
*/
export function beforeValue<T>(fn: () => T | Promise<T>, timeout?: number): LazyValue<T> {
let value: T = (initialValue as unknown) as T;

before(async function () {
this.timeout(timeout ?? 300_000);
value = await fn();
});

return new Proxy<{value: T}>(
{value},
{
get: function (target, prop) {
if (prop === "value") {
if (value === initialValue) {
throw Error("beforeValue has not yet run the before() block");
} else {
return value;
}
} else {
return undefined;
}
},
}
);
}

/**
* Create a network config from known network params
*/
export function getNetworkConfig(network: NetworkName): IChainForkConfig {
const configNetwork = networksChainConfig[network];
return createIChainForkConfig(configNetwork);
}

/**
* Download a state from Infura. Caches states in local fs by network and slot to only download once.
*/
export async function getNetworkCachedState(
network: NetworkName,
slot: number,
timeout?: number
): Promise<CachedBeaconStateAllForks> {
const config = getNetworkConfig(network);

const filepath = path.join(testCachePath, `state_${network}_${slot}.ssz`);
let stateSsz: Uint8Array;
if (fs.existsSync(filepath)) {
stateSsz = fs.readFileSync(filepath);
} else {
const client = getClient({baseUrl: getInfuraBeaconUrl(network), timeoutMs: timeout ?? 300_000}, {config});
stateSsz =
computeEpochAtSlot(slot) < config.ALTAIR_FORK_EPOCH
? await client.debug.getState(String(slot), "ssz")
: await client.debug.getStateV2(String(slot), "ssz");
fs.writeFileSync(filepath, stateSsz);
}

const stateView = config.getForkTypes(slot).BeaconState.deserializeToViewDU(stateSsz);
return createCachedBeaconStateTest(stateView, config);
}
36 changes: 36 additions & 0 deletions packages/beacon-state-transition/test/utils/beforeValue.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
export type LazyValue<T> = {value: T};

/**
* Register a callback to compute a value in the before() block of mocha tests
* ```ts
* const state = beforeValue(() => getState())
* it("test", () => {
* doTest(state.value)
* })
* ```
*/
export function beforeValue<T>(fn: () => T | Promise<T>, timeout?: number): LazyValue<T> {
let value: T = (null as unknown) as T;

before(async function () {
this.timeout(timeout ?? 300_000);
value = await fn();
});

return new Proxy<{value: T}>(
{value},
{
get: function (target, prop) {
if (prop === "value") {
if (value === null) {
throw Error("beforeValue has not yet run the before() block");
} else {
return value;
}
} else {
return undefined;
}
},
}
);
}
2 changes: 2 additions & 0 deletions packages/beacon-state-transition/test/utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./beforeValue";
export * from "./testFileCache";
116 changes: 116 additions & 0 deletions packages/beacon-state-transition/test/utils/testFileCache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import fs from "node:fs";
import path from "node:path";
import got from "got";
import {getClient} from "@chainsafe/lodestar-api";
import {NetworkName, networksChainConfig} from "@chainsafe/lodestar-config/networks";
import {createIChainForkConfig, IChainForkConfig} from "@chainsafe/lodestar-config";
import {CachedBeaconStateAllForks, computeEpochAtSlot} from "../../src";
import {getInfuraBeaconUrl} from "./infura";
import {testCachePath} from "../cache";
import {createCachedBeaconStateTest} from "../utils/state";
import {allForks} from "@chainsafe/lodestar-types";

/**
* Full link example:
* ```
* https://github.com/dapplion/ethereum-consensus-test-data/releases/download/v0.1.0/block_mainnet_3766821.ssz
* ``` */
const TEST_FILES_BASE_URL = "https://github.com/dapplion/ethereum-consensus-test-data/releases/download/v0.1.0";

/**
* Create a network config from known network params
*/
export function getNetworkConfig(network: NetworkName): IChainForkConfig {
const configNetwork = networksChainConfig[network];
return createIChainForkConfig(configNetwork);
}

/**
* Download a state from Infura. Caches states in local fs by network and slot to only download once.
*/
export async function getNetworkCachedState(
network: NetworkName,
slot: number,
timeout?: number
): Promise<CachedBeaconStateAllForks> {
const config = getNetworkConfig(network);
const fileId = `state_${network}_${slot}.ssz`;

const filepath = path.join(testCachePath, fileId);

if (fs.existsSync(filepath)) {
const stateSsz = fs.readFileSync(filepath);
return createCachedBeaconStateTest(config.getForkTypes(slot).BeaconState.deserializeToViewDU(stateSsz), config);
} else {
const stateSsz = await tryEach([
() => downloadTestFile(fileId),
() => {
const client = getClient({baseUrl: getInfuraBeaconUrl(network), timeoutMs: timeout ?? 300_000}, {config});
return computeEpochAtSlot(slot) < config.ALTAIR_FORK_EPOCH
? client.debug.getState(String(slot), "ssz")
: client.debug.getStateV2(String(slot), "ssz");
},
]);

fs.writeFileSync(filepath, stateSsz);
return createCachedBeaconStateTest(config.getForkTypes(slot).BeaconState.deserializeToViewDU(stateSsz), config);
}
}

/**
* Download a state from Infura. Caches states in local fs by network and slot to only download once.
*/
export async function getNetworkCachedBlock(
network: NetworkName,
slot: number,
timeout?: number
): Promise<allForks.SignedBeaconBlock> {
const config = getNetworkConfig(network);
const fileId = `block_${network}_${slot}.ssz`;

const filepath = path.join(testCachePath, fileId);

if (fs.existsSync(filepath)) {
const blockSsz = fs.readFileSync(filepath);
return config.getForkTypes(slot).SignedBeaconBlock.deserialize(blockSsz);
} else {
const blockSsz = await tryEach([
() => downloadTestFile(fileId),
async () => {
const client = getClient({baseUrl: getInfuraBeaconUrl(network), timeoutMs: timeout ?? 300_000}, {config});

const res =
computeEpochAtSlot(slot) < config.ALTAIR_FORK_EPOCH
? await client.beacon.getBlock(String(slot))
: await client.beacon.getBlockV2(String(slot));
return config.getForkTypes(slot).SignedBeaconBlock.serialize(res.data);
},
]);

fs.writeFileSync(filepath, blockSsz);
return config.getForkTypes(slot).SignedBeaconBlock.deserialize(blockSsz);
}
}

async function downloadTestFile(fileId: string): Promise<Buffer> {
const fileUrl = `${TEST_FILES_BASE_URL}/${fileId}`;
// eslint-disable-next-line no-console
console.log(`Downloading file ${fileUrl}`);

const res = await got(fileUrl, {responseType: "buffer"});
return res.body;
}

async function tryEach<T>(promises: (() => Promise<T>)[]): Promise<T> {
const errors: Error[] = [];

for (let i = 0; i < promises.length; i++) {
try {
return promises[i]();
} catch (e) {
errors.push(e as Error);
}
}

throw Error(errors.map((e, i) => `Error[${i}] ${e.message}`).join("\n"));
}
10 changes: 9 additions & 1 deletion packages/light-client/test/getGenesisData.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import {getClient} from "@chainsafe/lodestar-api";
import {config} from "@chainsafe/lodestar-config/default";
import {getInfuraBeaconUrl} from "@chainsafe/lodestar-beacon-state-transition/test/perf/infura";
import {NetworkName} from "@chainsafe/lodestar-config/networks";

// To populate packages/light-client/src/networks.ts
Expand All @@ -25,6 +24,15 @@ async function getGenesisData(): Promise<void> {
}
}

function getInfuraBeaconUrl(network: NetworkName): string {
const INFURA_ETH2_CREDENTIALS = process.env.INFURA_ETH2_CREDENTIALS;
if (!INFURA_ETH2_CREDENTIALS) {
throw Error("Must set ENV INFURA_ETH2_CREDENTIALS");
}

return `https://${INFURA_ETH2_CREDENTIALS}@eth2-beacon-${network}.infura.io`;
}

getGenesisData().catch((e) => {
console.error(e);
process.exit(1);
Expand Down
Loading

0 comments on commit 87a9cec

Please sign in to comment.