/** * Script to generate IBFT 2.0 genesis extraData. * * Usage (Node.js ≥18): * node generate-genesis.mjs [ ...] * * Example: * node generate-genesis.mjs 09b... 12a... * * Prints the full genesis JSON to stdout with extraData populated. */ import fs from "fs"; import path from "path"; import { fileURLToPath } from "url"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const VANITY = "0x".padEnd(66, "0"); // 32 bytes of zeros const ROUND = "0x0000000000000000"; // 8 bytes round (big-endian uint64) const SEAL = "0x".padEnd(194, "0"); // 96 bytes proposer seal (empty) function encodeRlpListOfAddresses(pubkeys) { // RLP list of 64-byte addresses (we use pubkey as address placeholder for IBFT 2.0 extraData) // For Besu IBFT 2.0 the extraData is: vanity (32) | RLP([validators...]) | round (8) | seal (0 or 96) // The addresses inside RLP are 20-byte Ethereum addresses derived from pubkeys. // For simplicity, we require 20-byte hex addresses as input here. const items = pubkeys.map((pk) => { const hex = pk.startsWith("0x") ? pk : "0x" + pk; if (hex.length !== 42) { throw new Error(`Expected 20-byte address (42 hex chars), got ${hex.length} for ${pk}`); } // RLP encode: 0x80 + len for short strings const buf = Buffer.from(hex.slice(2), "hex"); if (buf.length <= 55) { return Buffer.from([0x80 + buf.length, ...buf]); } throw new Error("RLP long string not implemented"); }); const payload = Buffer.concat(items); if (payload.length <= 55) { return Buffer.concat([Buffer.from([0xc0 + payload.length]), payload]); } throw new Error("RLP long list not implemented"); } function encodeExtraData(addresses) { const vanity = Buffer.from(VANITY.slice(2), "hex"); const validators = encodeRlpListOfAddresses(addresses); const round = Buffer.from(ROUND.slice(2), "hex"); const seal = Buffer.from(SEAL.slice(2), "hex"); const full = Buffer.concat([vanity, validators, round, seal]); return "0x" + full.toString("hex"); } const addresses = process.argv.slice(2); if (addresses.length < 4) { console.error("Usage: node generate-genesis.mjs <20-byte-address> ... (provide at least 4 for IBFT)"); process.exit(1); } const templatePath = path.join(__dirname, "besu-config", "ibftGenesisTemplate.json"); const template = JSON.parse(fs.readFileSync(templatePath, "utf8")); template.extraData = encodeExtraData(addresses); // Add alloc for a faucet / deployer account if needed (placeholder) template.alloc = {}; console.log(JSON.stringify(template, null, 2));