Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions .github/workflows/qa.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ on:
closed,
]
pull_request_review:
types: [submitted, dismissed]
types: [ submitted, dismissed ]

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}-${{ github.event_name }}-${{ github.event.action || 'default' }}
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref
}}-${{ github.event_name }}-${{ github.event.action || 'default' }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
Expand Down Expand Up @@ -88,13 +89,13 @@ jobs:
slack_bot_token: ${{ env.SLACK_BOT_TOKEN }}
slack_channel_id: ${{ env.SLACK_CHANNEL_ID }}

# Setup dependencies for QA (skip for draft PRs)
- name: Setup dependencies
uses: settlemint/shared-actions/.github/actions/setup-dependencies@e6f1bf8860111910c2ec9538e9edc137f39e8ef1 # main
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
npm_token: ${{ env.NPM_TOKEN }}
disable_node: "true"
bun-version: latest

- name: Install dependencies
run: bun install --frozen-lockfile

- name: Login to GitHub Container Registry
if: |
Expand Down Expand Up @@ -213,9 +214,8 @@ jobs:
uses: settlemint/shared-actions/.github/actions/build-status-labeler@e6f1bf8860111910c2ec9538e9edc137f39e8ef1 # main
with:
pr_number: ${{ github.event.pull_request.number }}
workflow_status:
${{ steps.secret-scan.outcome == 'success' && 'success' || 'failure'
}}
workflow_status: ${{ steps.secret-scan.outcome == 'success' && 'success' ||
'failure' }}

# Check PR review status (PR and PR review events only)
- name: Check PR review status
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,13 @@ Options:
--static-node-namespace <name> Namespace segment inserted between service name and domain for static-nodes entries.
--static-node-service-name <name> Headless Service name used when constructing static-nodes hostnames.
--static-node-pod-prefix <prefix> StatefulSet prefix used when constructing validator pod hostnames.
--rpc-node-service-name <name> Headless Service name used when constructing RPC static-nodes hostnames.
--rpc-node-pod-prefix <prefix> StatefulSet prefix used when constructing RPC pod hostnames.
--genesis-configmap-name <name> ConfigMap name that stores the generated genesis.json payload.
--static-nodes-configmap-name <name> ConfigMap name that stores the generated static-nodes.json payload.
--faucet-artifact-prefix <prefix> Prefix applied to faucet ConfigMaps and Secrets.
-v, --validators <count> Number of validator nodes to generate. (default: 4)
-r, --rpc-nodes <count> Number of RPC nodes to generate. (default: 2)
-a, --allocations <file> Path to a genesis allocations JSON file. (default: none)
--abi-directory <path> Directory containing ABI JSON files to publish as ConfigMaps.
--subgraph-hash-file <path> Path to a file containing the subgraph IPFS hash.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "network-bootstrapper",
"module": "src/index.ts",
"type": "module",
"version": "1.3.4",
"version": "1.3.5",
"private": false,
"license": "FSL-1.1-MIT",
"author": {
Expand Down
100 changes: 89 additions & 11 deletions src/cli/commands/bootstrap/bootstrap.command.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,20 @@ import type { OutputPayload, OutputType } from "./bootstrap.output.ts";
import { outputResult as realOutputResult } from "./bootstrap.output.ts";

const VALIDATOR_LABEL = "validator nodes";
const RPC_LABEL = "RPC nodes";
const VALIDATOR_RETURN = 2;
const RPC_RETURN = 2;
const GENESIS_MARKER = '"extraData": "0xextra"';
const EXPECTED_DEFAULT_VALIDATOR = 4;
const EXPECTED_DEFAULT_RPC = 2;
const DEFAULT_STATIC_NODE_PORT = 30_303;
const CUSTOM_STATIC_NODE_PORT = 40_000;
const LEADING_DOT_REGEX = /^\./u;
const {
staticNodeServiceName: DEFAULT_SERVICE_NAME,
staticNodePodPrefix: DEFAULT_POD_PREFIX,
rpcNodeServiceName: DEFAULT_RPC_SERVICE_NAME,
rpcNodePodPrefix: DEFAULT_RPC_POD_PREFIX,
genesisConfigMapName: DEFAULT_GENESIS_CONFIGMAP_NAME,
staticNodesConfigMapName: DEFAULT_STATIC_NODES_CONFIGMAP_NAME,
faucetArtifactPrefix: DEFAULT_FAUCET_PREFIX,
Expand All @@ -35,7 +40,9 @@ const PRIVATE_KEY_REPEAT = 32;
const PUBLIC_KEY_REPEAT = 64;
const FIRST_VALIDATOR_INDEX = 1;
const SECOND_VALIDATOR_INDEX = 2;
const FAUCET_INDEX = VALIDATOR_RETURN + 1;
const FIRST_RPC_FACTORY_INDEX = VALIDATOR_RETURN + 1;
const SECOND_RPC_FACTORY_INDEX = VALIDATOR_RETURN + 2;
const FAUCET_INDEX = VALIDATOR_RETURN + RPC_RETURN + 1;
const SAMPLE_SUBGRAPH_HASH =
"bafybeigdyrztzd4gufq2bdsd6we3jh7uzulnd2ipkyli5sto6f5j6rlude";
const createFactoryStub = () => {
Expand Down Expand Up @@ -71,14 +78,15 @@ const expectedPublicKey = (index: number) => {
return `0x04${pattern.repeat(PUBLIC_KEY_REPEAT)}` as const;
};

const expectedStaticNodeUri = (
index: number,
domain?: string,
port: number = DEFAULT_STATIC_NODE_PORT,
discoveryPort: number = DEFAULT_STATIC_NODE_PORT,
namespace?: string,
serviceName: string = DEFAULT_SERVICE_NAME,
podPrefix: string = DEFAULT_POD_PREFIX
const buildStaticNodeUri = (
publicKeyIndex: number,
ordinal: number,
domain: string | undefined,
port: number,
discoveryPort: number,
namespace: string | undefined,
serviceName: string,
podPrefix: string
): string => {
const normalizedDomain =
domain === undefined || domain.trim().length === 0
Expand All @@ -88,7 +96,6 @@ const expectedStaticNodeUri = (
namespace === undefined || namespace.trim().length === 0
? undefined
: namespace.trim();
const ordinal = index - 1;
const podName = `${podPrefix}-${ordinal}`;
const segments = [podName, serviceName];
if (normalizedNamespace) {
Expand All @@ -98,7 +105,7 @@ const expectedStaticNodeUri = (
segments.push(normalizedDomain);
}
const host = segments.join(".");
const publicKey = expectedPublicKey(index).slice(2);
const publicKey = expectedPublicKey(publicKeyIndex).slice(2);
const nodeId =
publicKey.startsWith(UNCOMPRESSED_PUBLIC_KEY_PREFIX) &&
publicKey.length === UNCOMPRESSED_PUBLIC_KEY_LENGTH
Expand All @@ -107,6 +114,47 @@ const expectedStaticNodeUri = (
return `enode://${nodeId}@${host}:${port}?discport=${discoveryPort}`;
};

const expectedStaticNodeUri = (
index: number,
domain?: string,
port: number = DEFAULT_STATIC_NODE_PORT,
discoveryPort: number = DEFAULT_STATIC_NODE_PORT,
namespace?: string,
serviceName: string = DEFAULT_SERVICE_NAME,
podPrefix: string = DEFAULT_POD_PREFIX
): string =>
buildStaticNodeUri(
index,
index - 1,
domain,
port,
discoveryPort,
namespace,
serviceName,
podPrefix
);

const expectedRpcStaticNodeUri = (
publicKeyIndex: number,
ordinal: number,
domain?: string,
port: number = DEFAULT_STATIC_NODE_PORT,
discoveryPort: number = DEFAULT_STATIC_NODE_PORT,
namespace?: string,
serviceName: string = DEFAULT_RPC_SERVICE_NAME,
podPrefix: string = DEFAULT_RPC_POD_PREFIX
): string =>
buildStaticNodeUri(
publicKeyIndex,
ordinal,
domain,
port,
discoveryPort,
namespace,
serviceName,
podPrefix
);

const captureStdout = () => {
let captured = "";
const original = process.stdout.write;
Expand Down Expand Up @@ -220,17 +268,21 @@ describe("CLI command bootstrap", () => {

expect(promptCalls).toEqual([
[VALIDATOR_LABEL, undefined, EXPECTED_DEFAULT_VALIDATOR],
[RPC_LABEL, undefined, EXPECTED_DEFAULT_RPC],
]);
expect(textPromptCalls).toEqual([
["Static node service name", DEFAULT_SERVICE_NAME],
["Static node pod prefix", DEFAULT_POD_PREFIX],
["RPC node service name", DEFAULT_RPC_SERVICE_NAME],
["RPC node pod prefix", DEFAULT_RPC_POD_PREFIX],
["Genesis ConfigMap name", DEFAULT_GENESIS_CONFIGMAP_NAME],
["Static nodes ConfigMap name", DEFAULT_STATIC_NODES_CONFIGMAP_NAME],
["Faucet artifact prefix", DEFAULT_FAUCET_PREFIX],
]);
const output = stdout.read();
expect(output).toContain("Genesis");
expect(output).toContain("Validator Nodes");
expect(output).toContain("RPC Nodes");
expect(output).toContain("Static Nodes");
expect(output).toContain(GENESIS_MARKER);
expect(loadAllocationsPath).toBe("/tmp/alloc.json");
Expand All @@ -239,11 +291,14 @@ describe("CLI command bootstrap", () => {
expect(outputInvocation?.payload.staticNodes).toEqual([
expectedStaticNodeUri(FIRST_VALIDATOR_INDEX),
expectedStaticNodeUri(SECOND_VALIDATOR_INDEX),
expectedRpcStaticNodeUri(FIRST_RPC_FACTORY_INDEX, 0),
expectedRpcStaticNodeUri(SECOND_RPC_FACTORY_INDEX, 1),
]);
expect(outputInvocation?.payload.abiArtifacts).toEqual([]);
expect(outputInvocation?.payload.artifactNames).toEqual({
faucetPrefix: DEFAULT_FAUCET_PREFIX,
validatorPrefix: DEFAULT_POD_PREFIX,
rpcPrefix: DEFAULT_RPC_POD_PREFIX,
genesisConfigMapName: DEFAULT_GENESIS_CONFIGMAP_NAME,
staticNodesConfigMapName: DEFAULT_STATIC_NODES_CONFIGMAP_NAME,
subgraphConfigMapName: DEFAULT_SUBGRAPH_CONFIGMAP_NAME,
Expand Down Expand Up @@ -407,6 +462,8 @@ describe("CLI command bootstrap", () => {
"generate",
"--validators",
"2",
"--rpc-nodes",
"2",
"--allocations",
"/tmp/mock.json",
"--consensus",
Expand Down Expand Up @@ -489,6 +546,12 @@ describe("CLI command bootstrap", () => {
"custom-service",
"--static-node-pod-prefix",
"custom-validator",
"--rpc-nodes",
"1",
"--rpc-node-service-name",
"custom-rpc-service",
"--rpc-node-pod-prefix",
"custom-rpc",
"--genesis-configmap-name",
"custom-genesis",
"--static-nodes-configmap-name",
Expand All @@ -509,10 +572,21 @@ describe("CLI command bootstrap", () => {
"custom-service",
"custom-validator"
),
expectedRpcStaticNodeUri(
2,
0,
"svc.cluster.local",
CUSTOM_STATIC_NODE_PORT,
0,
"network",
"custom-rpc-service",
"custom-rpc"
),
]);
expect(capturedPayload?.artifactNames).toEqual({
faucetPrefix: "custom-faucet",
validatorPrefix: "custom-validator",
rpcPrefix: "custom-rpc",
genesisConfigMapName: "custom-genesis",
staticNodesConfigMapName: "custom-static-nodes",
subgraphConfigMapName: DEFAULT_SUBGRAPH_CONFIGMAP_NAME,
Expand Down Expand Up @@ -556,6 +630,7 @@ describe("CLI command bootstrap", () => {
await runBootstrap(
{
validators: 1,
rpcNodes: 0,
staticNodeDomain: "svc.cluster.local",
staticNodeNamespace: "network",
staticNodePort: CUSTOM_STATIC_NODE_PORT,
Expand Down Expand Up @@ -624,6 +699,7 @@ describe("CLI command bootstrap", () => {

const options: CliOptions = {
validators: validatorOverride,
rpcNodes: 0,
consensus: ALGORITHM.ibftV2,
chainId: 1234,
secondsPerBlock: 6,
Expand Down Expand Up @@ -795,9 +871,11 @@ describe("CLI command bootstrap", () => {
loadSubgraphHash: () => Promise.resolve(SAMPLE_SUBGRAPH_HASH),
outputResult: (_type, payload) => {
expect(payload.validators).toHaveLength(EXPECTED_DEFAULT_VALIDATOR);
expect(payload.rpcNodes).toHaveLength(EXPECTED_DEFAULT_RPC);
expect(payload.artifactNames).toEqual({
faucetPrefix: DEFAULT_FAUCET_PREFIX,
validatorPrefix: DEFAULT_POD_PREFIX,
rpcPrefix: DEFAULT_RPC_POD_PREFIX,
genesisConfigMapName: DEFAULT_GENESIS_CONFIGMAP_NAME,
staticNodesConfigMapName: DEFAULT_STATIC_NODES_CONFIGMAP_NAME,
subgraphConfigMapName: DEFAULT_SUBGRAPH_CONFIGMAP_NAME,
Expand Down
Loading
Loading