Skip to content

Commit 2f58994

Browse files
authored
feat: add rpcNodes keys generation (#278)
1 parent 96eb57b commit 2f58994

9 files changed

Lines changed: 210 additions & 47 deletions

File tree

.github/workflows/qa.yml

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@ on:
1717
closed,
1818
]
1919
pull_request_review:
20-
types: [submitted, dismissed]
20+
types: [ submitted, dismissed ]
2121

2222
concurrency:
23-
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}-${{ github.event_name }}-${{ github.event.action || 'default' }}
23+
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref
24+
}}-${{ github.event_name }}-${{ github.event.action || 'default' }}
2425
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
2526

2627
jobs:
@@ -88,13 +89,13 @@ jobs:
8889
slack_bot_token: ${{ env.SLACK_BOT_TOKEN }}
8990
slack_channel_id: ${{ env.SLACK_CHANNEL_ID }}
9091

91-
# Setup dependencies for QA (skip for draft PRs)
92-
- name: Setup dependencies
93-
uses: settlemint/shared-actions/.github/actions/setup-dependencies@e6f1bf8860111910c2ec9538e9edc137f39e8ef1 # main
92+
- name: Setup Bun
93+
uses: oven-sh/setup-bun@v2
9494
with:
95-
github_token: ${{ secrets.GITHUB_TOKEN }}
96-
npm_token: ${{ env.NPM_TOKEN }}
97-
disable_node: "true"
95+
bun-version: latest
96+
97+
- name: Install dependencies
98+
run: bun install --frozen-lockfile
9899

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

220220
# Check PR review status (PR and PR review events only)
221221
- name: Check PR review status

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,13 @@ Options:
3636
--static-node-namespace <name> Namespace segment inserted between service name and domain for static-nodes entries.
3737
--static-node-service-name <name> Headless Service name used when constructing static-nodes hostnames.
3838
--static-node-pod-prefix <prefix> StatefulSet prefix used when constructing validator pod hostnames.
39+
--rpc-node-service-name <name> Headless Service name used when constructing RPC static-nodes hostnames.
40+
--rpc-node-pod-prefix <prefix> StatefulSet prefix used when constructing RPC pod hostnames.
3941
--genesis-configmap-name <name> ConfigMap name that stores the generated genesis.json payload.
4042
--static-nodes-configmap-name <name> ConfigMap name that stores the generated static-nodes.json payload.
4143
--faucet-artifact-prefix <prefix> Prefix applied to faucet ConfigMaps and Secrets.
4244
-v, --validators <count> Number of validator nodes to generate. (default: 4)
45+
-r, --rpc-nodes <count> Number of RPC nodes to generate. (default: 2)
4346
-a, --allocations <file> Path to a genesis allocations JSON file. (default: none)
4447
--abi-directory <path> Directory containing ABI JSON files to publish as ConfigMaps.
4548
--subgraph-hash-file <path> Path to a file containing the subgraph IPFS hash.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "network-bootstrapper",
33
"module": "src/index.ts",
44
"type": "module",
5-
"version": "1.3.4",
5+
"version": "1.3.5",
66
"private": false,
77
"license": "FSL-1.1-MIT",
88
"author": {

src/cli/commands/bootstrap/bootstrap.command.test.ts

Lines changed: 89 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,20 @@ import type { OutputPayload, OutputType } from "./bootstrap.output.ts";
1111
import { outputResult as realOutputResult } from "./bootstrap.output.ts";
1212

1313
const VALIDATOR_LABEL = "validator nodes";
14+
const RPC_LABEL = "RPC nodes";
1415
const VALIDATOR_RETURN = 2;
16+
const RPC_RETURN = 2;
1517
const GENESIS_MARKER = '"extraData": "0xextra"';
1618
const EXPECTED_DEFAULT_VALIDATOR = 4;
19+
const EXPECTED_DEFAULT_RPC = 2;
1720
const DEFAULT_STATIC_NODE_PORT = 30_303;
1821
const CUSTOM_STATIC_NODE_PORT = 40_000;
1922
const LEADING_DOT_REGEX = /^\./u;
2023
const {
2124
staticNodeServiceName: DEFAULT_SERVICE_NAME,
2225
staticNodePodPrefix: DEFAULT_POD_PREFIX,
26+
rpcNodeServiceName: DEFAULT_RPC_SERVICE_NAME,
27+
rpcNodePodPrefix: DEFAULT_RPC_POD_PREFIX,
2328
genesisConfigMapName: DEFAULT_GENESIS_CONFIGMAP_NAME,
2429
staticNodesConfigMapName: DEFAULT_STATIC_NODES_CONFIGMAP_NAME,
2530
faucetArtifactPrefix: DEFAULT_FAUCET_PREFIX,
@@ -35,7 +40,9 @@ const PRIVATE_KEY_REPEAT = 32;
3540
const PUBLIC_KEY_REPEAT = 64;
3641
const FIRST_VALIDATOR_INDEX = 1;
3742
const SECOND_VALIDATOR_INDEX = 2;
38-
const FAUCET_INDEX = VALIDATOR_RETURN + 1;
43+
const FIRST_RPC_FACTORY_INDEX = VALIDATOR_RETURN + 1;
44+
const SECOND_RPC_FACTORY_INDEX = VALIDATOR_RETURN + 2;
45+
const FAUCET_INDEX = VALIDATOR_RETURN + RPC_RETURN + 1;
3946
const SAMPLE_SUBGRAPH_HASH =
4047
"bafybeigdyrztzd4gufq2bdsd6we3jh7uzulnd2ipkyli5sto6f5j6rlude";
4148
const createFactoryStub = () => {
@@ -71,14 +78,15 @@ const expectedPublicKey = (index: number) => {
7178
return `0x04${pattern.repeat(PUBLIC_KEY_REPEAT)}` as const;
7279
};
7380

74-
const expectedStaticNodeUri = (
75-
index: number,
76-
domain?: string,
77-
port: number = DEFAULT_STATIC_NODE_PORT,
78-
discoveryPort: number = DEFAULT_STATIC_NODE_PORT,
79-
namespace?: string,
80-
serviceName: string = DEFAULT_SERVICE_NAME,
81-
podPrefix: string = DEFAULT_POD_PREFIX
81+
const buildStaticNodeUri = (
82+
publicKeyIndex: number,
83+
ordinal: number,
84+
domain: string | undefined,
85+
port: number,
86+
discoveryPort: number,
87+
namespace: string | undefined,
88+
serviceName: string,
89+
podPrefix: string
8290
): string => {
8391
const normalizedDomain =
8492
domain === undefined || domain.trim().length === 0
@@ -88,7 +96,6 @@ const expectedStaticNodeUri = (
8896
namespace === undefined || namespace.trim().length === 0
8997
? undefined
9098
: namespace.trim();
91-
const ordinal = index - 1;
9299
const podName = `${podPrefix}-${ordinal}`;
93100
const segments = [podName, serviceName];
94101
if (normalizedNamespace) {
@@ -98,7 +105,7 @@ const expectedStaticNodeUri = (
98105
segments.push(normalizedDomain);
99106
}
100107
const host = segments.join(".");
101-
const publicKey = expectedPublicKey(index).slice(2);
108+
const publicKey = expectedPublicKey(publicKeyIndex).slice(2);
102109
const nodeId =
103110
publicKey.startsWith(UNCOMPRESSED_PUBLIC_KEY_PREFIX) &&
104111
publicKey.length === UNCOMPRESSED_PUBLIC_KEY_LENGTH
@@ -107,6 +114,47 @@ const expectedStaticNodeUri = (
107114
return `enode://${nodeId}@${host}:${port}?discport=${discoveryPort}`;
108115
};
109116

117+
const expectedStaticNodeUri = (
118+
index: number,
119+
domain?: string,
120+
port: number = DEFAULT_STATIC_NODE_PORT,
121+
discoveryPort: number = DEFAULT_STATIC_NODE_PORT,
122+
namespace?: string,
123+
serviceName: string = DEFAULT_SERVICE_NAME,
124+
podPrefix: string = DEFAULT_POD_PREFIX
125+
): string =>
126+
buildStaticNodeUri(
127+
index,
128+
index - 1,
129+
domain,
130+
port,
131+
discoveryPort,
132+
namespace,
133+
serviceName,
134+
podPrefix
135+
);
136+
137+
const expectedRpcStaticNodeUri = (
138+
publicKeyIndex: number,
139+
ordinal: number,
140+
domain?: string,
141+
port: number = DEFAULT_STATIC_NODE_PORT,
142+
discoveryPort: number = DEFAULT_STATIC_NODE_PORT,
143+
namespace?: string,
144+
serviceName: string = DEFAULT_RPC_SERVICE_NAME,
145+
podPrefix: string = DEFAULT_RPC_POD_PREFIX
146+
): string =>
147+
buildStaticNodeUri(
148+
publicKeyIndex,
149+
ordinal,
150+
domain,
151+
port,
152+
discoveryPort,
153+
namespace,
154+
serviceName,
155+
podPrefix
156+
);
157+
110158
const captureStdout = () => {
111159
let captured = "";
112160
const original = process.stdout.write;
@@ -220,17 +268,21 @@ describe("CLI command bootstrap", () => {
220268

221269
expect(promptCalls).toEqual([
222270
[VALIDATOR_LABEL, undefined, EXPECTED_DEFAULT_VALIDATOR],
271+
[RPC_LABEL, undefined, EXPECTED_DEFAULT_RPC],
223272
]);
224273
expect(textPromptCalls).toEqual([
225274
["Static node service name", DEFAULT_SERVICE_NAME],
226275
["Static node pod prefix", DEFAULT_POD_PREFIX],
276+
["RPC node service name", DEFAULT_RPC_SERVICE_NAME],
277+
["RPC node pod prefix", DEFAULT_RPC_POD_PREFIX],
227278
["Genesis ConfigMap name", DEFAULT_GENESIS_CONFIGMAP_NAME],
228279
["Static nodes ConfigMap name", DEFAULT_STATIC_NODES_CONFIGMAP_NAME],
229280
["Faucet artifact prefix", DEFAULT_FAUCET_PREFIX],
230281
]);
231282
const output = stdout.read();
232283
expect(output).toContain("Genesis");
233284
expect(output).toContain("Validator Nodes");
285+
expect(output).toContain("RPC Nodes");
234286
expect(output).toContain("Static Nodes");
235287
expect(output).toContain(GENESIS_MARKER);
236288
expect(loadAllocationsPath).toBe("/tmp/alloc.json");
@@ -239,11 +291,14 @@ describe("CLI command bootstrap", () => {
239291
expect(outputInvocation?.payload.staticNodes).toEqual([
240292
expectedStaticNodeUri(FIRST_VALIDATOR_INDEX),
241293
expectedStaticNodeUri(SECOND_VALIDATOR_INDEX),
294+
expectedRpcStaticNodeUri(FIRST_RPC_FACTORY_INDEX, 0),
295+
expectedRpcStaticNodeUri(SECOND_RPC_FACTORY_INDEX, 1),
242296
]);
243297
expect(outputInvocation?.payload.abiArtifacts).toEqual([]);
244298
expect(outputInvocation?.payload.artifactNames).toEqual({
245299
faucetPrefix: DEFAULT_FAUCET_PREFIX,
246300
validatorPrefix: DEFAULT_POD_PREFIX,
301+
rpcPrefix: DEFAULT_RPC_POD_PREFIX,
247302
genesisConfigMapName: DEFAULT_GENESIS_CONFIGMAP_NAME,
248303
staticNodesConfigMapName: DEFAULT_STATIC_NODES_CONFIGMAP_NAME,
249304
subgraphConfigMapName: DEFAULT_SUBGRAPH_CONFIGMAP_NAME,
@@ -407,6 +462,8 @@ describe("CLI command bootstrap", () => {
407462
"generate",
408463
"--validators",
409464
"2",
465+
"--rpc-nodes",
466+
"2",
410467
"--allocations",
411468
"/tmp/mock.json",
412469
"--consensus",
@@ -489,6 +546,12 @@ describe("CLI command bootstrap", () => {
489546
"custom-service",
490547
"--static-node-pod-prefix",
491548
"custom-validator",
549+
"--rpc-nodes",
550+
"1",
551+
"--rpc-node-service-name",
552+
"custom-rpc-service",
553+
"--rpc-node-pod-prefix",
554+
"custom-rpc",
492555
"--genesis-configmap-name",
493556
"custom-genesis",
494557
"--static-nodes-configmap-name",
@@ -509,10 +572,21 @@ describe("CLI command bootstrap", () => {
509572
"custom-service",
510573
"custom-validator"
511574
),
575+
expectedRpcStaticNodeUri(
576+
2,
577+
0,
578+
"svc.cluster.local",
579+
CUSTOM_STATIC_NODE_PORT,
580+
0,
581+
"network",
582+
"custom-rpc-service",
583+
"custom-rpc"
584+
),
512585
]);
513586
expect(capturedPayload?.artifactNames).toEqual({
514587
faucetPrefix: "custom-faucet",
515588
validatorPrefix: "custom-validator",
589+
rpcPrefix: "custom-rpc",
516590
genesisConfigMapName: "custom-genesis",
517591
staticNodesConfigMapName: "custom-static-nodes",
518592
subgraphConfigMapName: DEFAULT_SUBGRAPH_CONFIGMAP_NAME,
@@ -556,6 +630,7 @@ describe("CLI command bootstrap", () => {
556630
await runBootstrap(
557631
{
558632
validators: 1,
633+
rpcNodes: 0,
559634
staticNodeDomain: "svc.cluster.local",
560635
staticNodeNamespace: "network",
561636
staticNodePort: CUSTOM_STATIC_NODE_PORT,
@@ -624,6 +699,7 @@ describe("CLI command bootstrap", () => {
624699

625700
const options: CliOptions = {
626701
validators: validatorOverride,
702+
rpcNodes: 0,
627703
consensus: ALGORITHM.ibftV2,
628704
chainId: 1234,
629705
secondsPerBlock: 6,
@@ -795,9 +871,11 @@ describe("CLI command bootstrap", () => {
795871
loadSubgraphHash: () => Promise.resolve(SAMPLE_SUBGRAPH_HASH),
796872
outputResult: (_type, payload) => {
797873
expect(payload.validators).toHaveLength(EXPECTED_DEFAULT_VALIDATOR);
874+
expect(payload.rpcNodes).toHaveLength(EXPECTED_DEFAULT_RPC);
798875
expect(payload.artifactNames).toEqual({
799876
faucetPrefix: DEFAULT_FAUCET_PREFIX,
800877
validatorPrefix: DEFAULT_POD_PREFIX,
878+
rpcPrefix: DEFAULT_RPC_POD_PREFIX,
801879
genesisConfigMapName: DEFAULT_GENESIS_CONFIGMAP_NAME,
802880
staticNodesConfigMapName: DEFAULT_STATIC_NODES_CONFIGMAP_NAME,
803881
subgraphConfigMapName: DEFAULT_SUBGRAPH_CONFIGMAP_NAME,

0 commit comments

Comments
 (0)