3b228e2e33
- 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>
90 lines
2.4 KiB
JavaScript
90 lines
2.4 KiB
JavaScript
#!/usr/bin/env node
|
|
// populate-devnet.js — runs the deterministic key generator and writes files to the repo.
|
|
const { spawnSync } = require('child_process');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const workspace = path.resolve(__dirname, '..');
|
|
const devnetDir = path.join(workspace, 'devnet');
|
|
|
|
// Run the generator in /tmp where deps are installed
|
|
const result = spawnSync('node', ['generate-static-keys.js'], {
|
|
cwd: '/tmp/opencode/keygen',
|
|
encoding: 'utf8',
|
|
maxBuffer: 10 * 1024 * 1024,
|
|
});
|
|
|
|
if (result.status !== 0) {
|
|
console.error('generate-static-keys.js failed:', result.stderr);
|
|
process.exit(1);
|
|
}
|
|
|
|
const data = JSON.parse(result.stdout);
|
|
|
|
// Write validator keys
|
|
for (const v of data.validators) {
|
|
const dir = path.join(devnetDir, 'validator-keys', v.name);
|
|
fs.mkdirSync(dir, { recursive: true });
|
|
fs.writeFileSync(path.join(dir, 'key'), v.privateKey.slice(2), 'utf8');
|
|
fs.writeFileSync(path.join(dir, 'address'), v.address, 'utf8');
|
|
}
|
|
|
|
// Write genesis with deterministic extraData
|
|
const genesis = {
|
|
config: {
|
|
chainId: 1337,
|
|
homesteadBlock: 0,
|
|
eip150Block: 0,
|
|
eip150Hash: '0x' + '00'.repeat(32),
|
|
eip155Block: 0,
|
|
eip158Block: 0,
|
|
byzantiumBlock: 0,
|
|
constantinopleBlock: 0,
|
|
petersburgBlock: 0,
|
|
istanbulBlock: 0,
|
|
muirGlacierBlock: 0,
|
|
berlinBlock: 0,
|
|
londonBlock: 0,
|
|
zeroBaseFee: true,
|
|
ibft2: {
|
|
blockperiodseconds: 2,
|
|
epochlength: 30000,
|
|
requesttimeoutseconds: 4,
|
|
blockreward: '0x0',
|
|
miningbeneficiary: '0x' + '00'.repeat(20)
|
|
}
|
|
},
|
|
nonce: '0x0',
|
|
timestamp: '0x' + Math.floor(Date.now() / 1000).toString(16),
|
|
gasLimit: '0x1fffffffffffff',
|
|
difficulty: '0x1',
|
|
mixHash: '0x63746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365',
|
|
coinbase: '0x' + '00'.repeat(20),
|
|
alloc: {},
|
|
extraData: data.extraData
|
|
};
|
|
|
|
for (const v of data.validators) {
|
|
genesis.alloc[v.address.slice(2)] = {
|
|
comment: v.name,
|
|
balance: '0xad78ebc5ac6200000'
|
|
};
|
|
}
|
|
// Anvil default #0
|
|
const anvilDefault = 'f39Fd6e51aad88F6F4ce6aB8827279cffFb92266';
|
|
genesis.alloc[anvilDefault] = { comment: 'Anvil default #0', balance: '0x56bc75e2d63100000' };
|
|
|
|
fs.writeFileSync(
|
|
path.join(devnetDir, 'besu-config', 'genesis.json'),
|
|
JSON.stringify(genesis, null, 2),
|
|
'utf8'
|
|
);
|
|
|
|
fs.writeFileSync(
|
|
path.join(devnetDir, 'besu-config', 'nodes-summary.json'),
|
|
JSON.stringify(data, null, 2),
|
|
'utf8'
|
|
);
|
|
|
|
console.log('Done. Populated:', devnetDir);
|