@@ -4,14 +4,23 @@ import { join } from "node:path";
44
55import { type CoreV1Api , KubeConfig } from "@kubernetes/client-node" ;
66
7- import { ARTIFACT_DEFAULTS } from "../constants/artifact-defaults.ts" ;
8- import type { IndexedNode , OutputPayload , OutputType } from "./output.ts" ;
7+ import { ARTIFACT_DEFAULTS } from "../../../constants/artifact-defaults.ts" ;
8+ import {
9+ ALGORITHM ,
10+ type BesuAllocAccount ,
11+ BesuGenesisService ,
12+ } from "../../../genesis/besu-genesis.service.ts" ;
13+ import type {
14+ IndexedNode ,
15+ OutputPayload ,
16+ OutputType ,
17+ } from "./bootstrap.output.ts" ;
918import {
1019 outputResult ,
1120 printFaucet ,
1221 printGenesis ,
1322 printGroup ,
14- } from "./output.ts" ;
23+ } from "./bootstrap. output.ts" ;
1524
1625let output = "" ;
1726let originalWrite : typeof process . stdout . write ;
@@ -77,7 +86,7 @@ const PUBLIC_KEY_HEX_LENGTH = 128;
7786const HEX_RADIX = 16 ;
7887const SAMPLE_VALIDATOR_INDEX = 1 ;
7988const SAMPLE_FAUCET_INDEX = 99 ;
80- const EXPECTED_CONFIGMAP_COUNT = 7 ;
89+ const EXPECTED_CONFIGMAP_COUNT = 8 ;
8190const EXPECTED_SECRET_COUNT = 2 ;
8291const HEX_PREFIX_PATTERN = / ^ 0 x / ;
8392const TEST_CHAIN_ID = 1 ;
@@ -217,10 +226,37 @@ const staticNodeUri = (
217226
218227const sampleValidator = sampleNode ( SAMPLE_VALIDATOR_INDEX ) ;
219228const sampleFaucet = sampleNode ( SAMPLE_FAUCET_INDEX ) ;
229+ const allocationTarget = sampleNode ( SAMPLE_FAUCET_INDEX + 1 ) ;
230+
231+ const SAMPLE_EXTRA_ALLOCATION : BesuAllocAccount = {
232+ balance : "0x1234" ,
233+ code : "0x6000" ,
234+ storage : {
235+ "0x1" : "0x2" ,
236+ } ,
237+ } ;
238+
239+ const toAllocationConfigMapName = ( address : string ) : string =>
240+ `alloc-${ address . slice ( 2 ) . toLowerCase ( ) } ` ;
241+
242+ const genesisService = new BesuGenesisService ( TEST_CHAIN_ID ) ;
243+ const sampleGenesis = genesisService . generate (
244+ ALGORITHM . QBFT ,
245+ {
246+ chainId : TEST_CHAIN_ID ,
247+ faucetWalletAddress : sampleFaucet . address ,
248+ gasLimit : "0x1" ,
249+ gasPrice : 0 ,
250+ secondsPerBlock : 2 ,
251+ } ,
252+ {
253+ [ allocationTarget . address ] : SAMPLE_EXTRA_ALLOCATION ,
254+ }
255+ ) ;
220256
221257const samplePayload : OutputPayload = {
222258 faucet : sampleFaucet ,
223- genesis : { config : { chainId : TEST_CHAIN_ID } , extraData : "0xabc" } ,
259+ genesis : sampleGenesis ,
224260 validators : [ sampleValidator ] ,
225261 staticNodes : [
226262 staticNodeUri (
@@ -253,6 +289,9 @@ describe("outputResult", () => {
253289
254290 await outputResult ( "file" , samplePayload ) ;
255291
292+ expect ( output ) . toContain ( "[bootstrap] Output mode: file" ) ;
293+ expect ( output ) . toContain ( "[bootstrap] Writing besu-genesis.json" ) ;
294+
256295 const directories = await readdir ( "out" ) ;
257296 expect ( directories . length ) . toBe ( 1 ) ;
258297 const targetDir = directories [ 0 ] ;
@@ -300,6 +339,7 @@ describe("outputResult", () => {
300339 namespace : string ;
301340 name : string ;
302341 data : Record < string , string > ;
342+ immutable ?: boolean ;
303343 } > = [ ] ;
304344 const createdSecrets : Array < {
305345 namespace : string ;
@@ -336,6 +376,7 @@ describe("outputResult", () => {
336376 namespace,
337377 name : body ?. metadata ?. name ?? "" ,
338378 data : body ?. data ?? { } ,
379+ immutable : body ?. immutable ,
339380 } ) ;
340381 return Promise . resolve ( ) ;
341382 } ,
@@ -364,6 +405,10 @@ describe("outputResult", () => {
364405
365406 await outputResult ( "kubernetes" , samplePayload ) ;
366407
408+ expect ( output ) . toContain ( "[bootstrap] Output mode: kubernetes" ) ;
409+ expect ( output ) . toContain ( "[bootstrap] ConfigMap → besu-genesis" ) ;
410+ expect ( output ) . toContain ( "[bootstrap] Secret → besu-faucet-private-key" ) ;
411+
367412 expect ( listedConfigNamespaces ) . toEqual ( [ "test-namespace" ] ) ;
368413 expect ( listedSecretNamespaces ) . toEqual ( [ "test-namespace" ] ) ;
369414 expect ( createdConfigMaps ) . toHaveLength ( EXPECTED_CONFIGMAP_COUNT ) ;
@@ -374,6 +419,9 @@ describe("outputResult", () => {
374419 expect ( mapNames ) . toContain ( "besu-faucet-address" ) ;
375420 expect ( mapNames ) . toContain ( "besu-faucet-pubkey" ) ;
376421 expect ( mapNames ) . toContain ( "besu-static-nodes" ) ;
422+ expect ( mapNames ) . toContain (
423+ toAllocationConfigMapName ( allocationTarget . address )
424+ ) ;
377425 expect ( mapNames ) . not . toContain ( "besu-faucet-enode" ) ;
378426 const secretNames = createdSecrets . map ( ( entry ) => entry . name ) . sort ( ) ;
379427 expect ( secretNames ) . toEqual ( [
@@ -391,6 +439,44 @@ describe("outputResult", () => {
391439 expect (
392440 JSON . parse ( staticNodesConfig ?. data ?. [ "static-nodes.json" ] ?? "[]" )
393441 ) . toEqual ( samplePayload . staticNodes ) ;
442+ const genesisConfig = createdConfigMaps . find (
443+ ( entry ) => entry . name === "besu-genesis"
444+ ) ;
445+ expect ( genesisConfig ?. immutable ) . toBe ( true ) ;
446+ const parsedGenesis = JSON . parse (
447+ genesisConfig ?. data ?. [ "genesis.json" ] ?? "{}"
448+ ) as {
449+ config : { chainId : number } ;
450+ alloc ?: Record < string , BesuAllocAccount > ;
451+ } ;
452+ expect ( parsedGenesis . config . chainId ) . toBe ( TEST_CHAIN_ID ) ;
453+ const allocations = parsedGenesis . alloc ;
454+ if ( ! allocations ) {
455+ throw new Error ( "expected allocations payload" ) ;
456+ }
457+ const faucetAlloc = allocations [ sampleFaucet . address ] ;
458+ if ( ! faucetAlloc ) {
459+ throw new Error ( "expected faucet allocation entry" ) ;
460+ }
461+ const expectedFaucetAlloc = sampleGenesis . alloc [ sampleFaucet . address ] ;
462+ if ( ! expectedFaucetAlloc ) {
463+ throw new Error ( "sample genesis missing faucet entry" ) ;
464+ }
465+ expect ( faucetAlloc . balance ) . toBe ( expectedFaucetAlloc . balance ) ;
466+ const placeholderAlloc = allocations [ allocationTarget . address ] ;
467+ if ( ! placeholderAlloc ) {
468+ throw new Error ( "expected placeholder allocation entry" ) ;
469+ }
470+ expect ( placeholderAlloc . balance ) . toBe ( "0x0" ) ;
471+ const allocationConfig = createdConfigMaps . find (
472+ ( entry ) =>
473+ entry . name === toAllocationConfigMapName ( allocationTarget . address )
474+ ) ;
475+ expect ( allocationConfig ?. immutable ) . toBe ( true ) ;
476+ const parsedAllocation = JSON . parse (
477+ allocationConfig ?. data ?. [ "alloc.json" ] ?? "{}"
478+ ) as BesuAllocAccount ;
479+ expect ( parsedAllocation ) . toEqual ( SAMPLE_EXTRA_ALLOCATION ) ;
394480 } finally {
395481 ( KubeConfig . prototype as any ) . loadFromCluster = originalLoad ;
396482 ( KubeConfig . prototype as any ) . makeApiClient = originalMake ;
@@ -429,7 +515,11 @@ describe("outputResult", () => {
429515 "custom-genesis" ,
430516 "custom-static" ,
431517 "custom-validator-0-address" ,
518+ "custom-validator-0-enode" ,
519+ "custom-validator-0-pubkey" ,
432520 "custom-faucet-address" ,
521+ "custom-faucet-pubkey" ,
522+ toAllocationConfigMapName ( allocationTarget . address ) ,
433523 ] ;
434524 const expectedSecrets = [
435525 "custom-faucet-private-key" ,
0 commit comments