The route table lives in configs/gateway/*.json, where each endpoint names both the public path and the url_pattern sent upstream. Both moved: luxd answers under /v1/chain and nothing translates between them. The operational scripts went with it — Makefiles, the roll and validator helpers, the e2e specs and the market-maker config all composed /v1/bc addresses that now reach nothing. A test that dials a dead path fails in a way that looks like the chain is down. Lines that record an observation keep their original spelling. Co-authored-by: Hanzo Dev <[email protected]>
167 lines
6.0 KiB
JavaScript
167 lines
6.0 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Add a new validator to the Lux Primary Network.
|
|
*
|
|
* Uses direct P-Chain transaction signing (bypasses CLI ledger requirement).
|
|
* For our POA mainnet where we control the treasury key.
|
|
*
|
|
* Steps:
|
|
* 1. Export LUX from C-Chain to P-Chain via atomic tx
|
|
* 2. Import on P-Chain
|
|
* 3. Issue AddPermissionlessValidatorTx
|
|
*
|
|
* Usage:
|
|
* node scripts/add-validator.js
|
|
*/
|
|
|
|
const MNEMONIC = process.env.LUX_MNEMONIC
|
|
if (!MNEMONIC) {
|
|
console.error('ERROR: LUX_MNEMONIC is required (source from KMS, never hardcode)')
|
|
process.exit(1)
|
|
}
|
|
const RPC = process.env.LUX_RPC || 'https://api.lux.network'
|
|
const STAKE_NLUX = 2_500_000_000n // 2.5 LUX
|
|
|
|
// Validator-8 BLS keys (from `lux key show validator-8`)
|
|
const V8 = {
|
|
nodeID: 'NodeID-73af7ae93528c8d66abbeba4f4cf40d631259994',
|
|
blsPub: 'ac6a513feea1c1f6de2aea8f38f6623fdab6d92a14d2fba89bbb670c966c7867a78f6c86670fb90a8292ce7e641021eb',
|
|
blsPop: '978a7b47d688993e6bbc9f70ca54ac501bd532197220cb7cc8630228e92db08f6938357059061fdf020009ae6eb333b5103598851406392d754113ba6deb32e706d0661bb597fcd52f10da806e8d79746952f87c37f4f8aba84d2c9d047fe58d',
|
|
}
|
|
|
|
async function rpc(endpoint, method, params = {}) {
|
|
const r = await fetch(`${RPC}${endpoint}`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ jsonrpc: '2.0', method, params, id: 1 }),
|
|
})
|
|
const j = await r.json()
|
|
if (j.error) throw new Error(`${method}: ${j.error.message}`)
|
|
return j.result
|
|
}
|
|
|
|
async function main() {
|
|
console.log('=== Add Validator-8 to Lux Primary Network ===\n')
|
|
|
|
// 1. Check current state
|
|
const { validators } = await rpc('/v1/chain/P', 'platform.getCurrentValidators', { subnetID: '' })
|
|
console.log(`Current validators: ${validators.length}`)
|
|
|
|
const exists = validators.find(v => v.nodeID === V8.nodeID)
|
|
if (exists) {
|
|
console.log(`\n${V8.nodeID} is ALREADY a validator!`)
|
|
console.log(` weight: ${exists.weight}, uptime: ${exists.uptime}`)
|
|
console.log(`\nTotal validators: ${validators.length}`)
|
|
return
|
|
}
|
|
|
|
// 2. Check if keystore API is available
|
|
let keystoreAvailable = false
|
|
try {
|
|
await rpc('/v1/keystore', 'keystore.listUsers', {})
|
|
keystoreAvailable = true
|
|
} catch { }
|
|
|
|
if (!keystoreAvailable) {
|
|
// Keystore API not available on production nodes
|
|
// Need to use the wallet API or direct P-Chain transaction signing
|
|
console.log('\nKeystore API not available (production mode)')
|
|
console.log('Using P-Chain wallet transaction signing...\n')
|
|
}
|
|
|
|
// 3. Check P-Chain user balance
|
|
const { networkID } = await rpc('/v1/info', 'info.getNetworkID', {})
|
|
console.log(`Network ID: ${networkID}`)
|
|
|
|
// 4. Check if platform.spend is available (newer API)
|
|
try {
|
|
const txBytes = await buildAddValidatorTx(networkID)
|
|
if (txBytes) {
|
|
console.log('\nSubmitting AddPermissionlessValidator transaction...')
|
|
const { txID } = await rpc('/v1/chain/P', 'platform.issueTx', { tx: txBytes })
|
|
console.log(`TX submitted: ${txID}`)
|
|
console.log('Waiting for acceptance...')
|
|
|
|
// Poll for acceptance
|
|
for (let i = 0; i < 30; i++) {
|
|
await new Promise(r => setTimeout(r, 2000))
|
|
try {
|
|
const { status } = await rpc('/v1/chain/P', 'platform.getTxStatus', { txID })
|
|
console.log(` Status: ${status}`)
|
|
if (status === 'Accepted' || status === 'Committed') {
|
|
console.log('\nValidator added successfully!')
|
|
break
|
|
}
|
|
if (status === 'Rejected' || status === 'Dropped') {
|
|
console.log('\nTransaction rejected!')
|
|
break
|
|
}
|
|
} catch (e) {
|
|
// May not be available yet
|
|
}
|
|
}
|
|
|
|
// Verify
|
|
const { validators: updated } = await rpc('/v1/chain/P', 'platform.getCurrentValidators', { subnetID: '' })
|
|
console.log(`\nValidators after: ${updated.length}`)
|
|
return
|
|
}
|
|
} catch (e) {
|
|
console.log(`Direct TX approach not available: ${e.message}`)
|
|
}
|
|
|
|
// Fallback: use platform.addPermissionlessValidator if available
|
|
try {
|
|
const startTime = Math.floor(Date.now() / 1000) + 120 // 2 min from now
|
|
const endTime = startTime + 365 * 24 * 60 * 60 // 1 year
|
|
|
|
const result = await rpc('/v1/chain/P', 'platform.addPermissionlessValidator', {
|
|
nodeID: V8.nodeID,
|
|
subnetID: '11111111111111111111111111111111LpoYY', // primary network
|
|
startTime: String(startTime),
|
|
endTime: String(endTime),
|
|
weight: String(STAKE_NLUX),
|
|
signer: {
|
|
publicKey: `0x${V8.blsPub}`,
|
|
proofOfPossession: `0x${V8.blsPop}`,
|
|
},
|
|
delegationFeeRate: 20000, // 2%
|
|
rewardAddress: 'P-lux1c7wevm4667l4umtzh93r25wpxlpsadkhka6gv6',
|
|
changeAddress: 'P-lux1c7wevm4667l4umtzh93r25wpxlpsadkhka6gv6',
|
|
})
|
|
console.log('AddPermissionlessValidator result:', JSON.stringify(result, null, 2))
|
|
return
|
|
} catch (e) {
|
|
console.log(`addPermissionlessValidator not available: ${e.message}`)
|
|
}
|
|
|
|
// If all else fails, print manual instructions
|
|
console.log('\n=== Manual Validator Addition Required ===')
|
|
console.log('The production node does not expose keystore/wallet APIs.')
|
|
console.log('To add the 8th validator, use one of:')
|
|
console.log('')
|
|
console.log('Option 1: Lux Wallet (wallet.lux.network)')
|
|
console.log(' 1. Import mnemonic')
|
|
console.log(' 2. Export 3 LUX from C-Chain to P-Chain')
|
|
console.log(' 3. Add Validator with NodeID:', V8.nodeID)
|
|
console.log('')
|
|
console.log('Option 2: Enable keystore API on node temporarily')
|
|
console.log(' Add --api-keystore-enabled=true to node config')
|
|
console.log(' Then re-run this script')
|
|
console.log('')
|
|
console.log('Option 3: Local node with admin API')
|
|
console.log(' lux network start --mainnet')
|
|
console.log(' Then use keystore API on localhost:9630')
|
|
}
|
|
|
|
async function buildAddValidatorTx(networkID) {
|
|
// This would require the luxfi JS SDK for proper transaction building
|
|
// For now, return null to fall through to other methods
|
|
return null
|
|
}
|
|
|
|
main().catch(e => {
|
|
console.error('ERROR:', e.message)
|
|
process.exit(1)
|
|
})
|