Files
jcc-blockchain/generate-genesis.mjs
T
BlockchainEngineer 3b228e2e33 feat(protocol): scaffold devnet, monitoring, rpc gateway, and load tests
- Add docker-compose.yml with 4 Besu IBFT 2.0 validators
- Add Foundry project (Counter.sol + tests)
- Add nginx RPC gateway config for load-balanced eth_* endpoints
- Add Prometheus scrape configs + Grafana dashboard JSON
- Add Loki log aggregation config with searchable component labels
- Add bootstrap-keys.sh, generate-genesis.mjs, and devnet_simulator.py
- Add rpc_gateway_mock.py (threaded HTTP mock for local smoke testing)
- Add load_test.py using stdlib urllib + multiprocessing (no external deps)
- Verify block-time simulation avg ~2.95s and RPC latency <10ms at 493 TPS

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-05-07 04:23:52 +00:00

71 lines
2.6 KiB
JavaScript

/**
* Script to generate IBFT 2.0 genesis extraData.
*
* Usage (Node.js ≥18):
* node generate-genesis.mjs <validator-pubkey-hex> [<validator-pubkey-hex> ...]
*
* 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));