mirror of
https://github.com/luxfi/genesis.git
synced 2026-07-27 05:54:08 +00:00
genesis: regenerate all network configs
- mainnet: 100 accounts x 500M LUX, 5 stakers, P-lux1 HRP, real IPs - testnet: 100 accounts x 500M LUX, 2 stakers, P-test1 HRP, networkID=2 - devnet: 100 accounts x 500M LUX, 3 stakers, P-dev1 HRP, networkID=3 - bootstrappers: real validator IPs - fix mnemonic env priority: MNEMONIC > LUX_MNEMONIC > LIGHT_MNEMONIC - remove stale .bak files
This commit is contained in:
+189
@@ -0,0 +1,189 @@
|
||||
# Binary outputs
|
||||
bin/
|
||||
*.exe
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# State data (cloned on-demand)
|
||||
state/
|
||||
chaindata/
|
||||
|
||||
# Test outputs
|
||||
*.test
|
||||
*.out
|
||||
coverage.txt
|
||||
coverage.html
|
||||
|
||||
# IDE files
|
||||
.idea/
|
||||
.vscode/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
.DS_Store
|
||||
|
||||
# Dependency directories
|
||||
vendor/
|
||||
|
||||
# Environment files
|
||||
.env
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Log files
|
||||
*.log
|
||||
logs/
|
||||
|
||||
# Temporary files
|
||||
tmp/
|
||||
temp/
|
||||
*.tmp
|
||||
*.temp
|
||||
|
||||
# Generated genesis files (keep configs directory but track essential configs)
|
||||
!configs/.gitkeep
|
||||
# Allow essential embedded config files
|
||||
!configs/genesis_mainnet.json
|
||||
!configs/genesis_testnet.json
|
||||
!configs/genesis_local.json
|
||||
!configs/cchain_genesis_mainnet.json
|
||||
!configs/bootstrappers.json
|
||||
!configs/validators.json
|
||||
!configs/checkpoints.json
|
||||
|
||||
# Output directories
|
||||
output/
|
||||
dist/
|
||||
|
||||
state
|
||||
extracted-blockchain/
|
||||
runs/
|
||||
migrated
|
||||
|
||||
# Go workspace files
|
||||
go.work
|
||||
go.work.sum
|
||||
|
||||
# AI markdown symlinks (all reference LLM.md)
|
||||
AGENTS.md
|
||||
CLAUDE.md
|
||||
GEMINI.md
|
||||
GROK.md
|
||||
QWEN.md
|
||||
|
||||
# Build tool binaries (generated by go build)
|
||||
analyze-keys
|
||||
block-migrate
|
||||
block-replayer
|
||||
check-block
|
||||
check-block0-badger
|
||||
check-canonical
|
||||
check-h-keys
|
||||
check-header
|
||||
check-header-format
|
||||
check-migration
|
||||
check-pebble-format
|
||||
check-pebble-headers
|
||||
convert-state
|
||||
convert-state-proper
|
||||
convert-trie
|
||||
create-replay
|
||||
debug-subnet
|
||||
dump-keys
|
||||
extract-raw-genesis
|
||||
extract-real-genesis
|
||||
finalize-migration
|
||||
find-balance
|
||||
find-headers
|
||||
find-height
|
||||
find-treasury
|
||||
fix-canonical-blocks
|
||||
fix-canonical-format
|
||||
fix-canonical-geth-format
|
||||
fix-chain-config
|
||||
fix-chainconfig
|
||||
genesis-stamp
|
||||
get-state-root
|
||||
inspect-db
|
||||
key-scanner
|
||||
list-prefixes
|
||||
luxd
|
||||
migrate-complete
|
||||
migrate-full
|
||||
migrate-proper
|
||||
migrate-rawdb
|
||||
migrate-subnet
|
||||
pebble-inspect
|
||||
pebble-scan
|
||||
probe
|
||||
scan-badger
|
||||
scan-pebble
|
||||
setup-pchain
|
||||
setup-vm
|
||||
subnet2coreth
|
||||
test-batch
|
||||
test-launch
|
||||
verify-state
|
||||
write-full-config
|
||||
write-td
|
||||
/genesis
|
||||
|
||||
# Temporary development scripts and files
|
||||
*.sh
|
||||
!scripts/*.sh
|
||||
*.bak
|
||||
*.OLD
|
||||
|
||||
# Migration and test output
|
||||
migrated-ethdb/
|
||||
migration.pid
|
||||
--db/
|
||||
--help/
|
||||
.!*
|
||||
|
||||
# Backup directories
|
||||
backup-*/
|
||||
|
||||
# Generated JSON files in root (configs are in configs/)
|
||||
*.json
|
||||
!configs/*.json
|
||||
!configs/**/*.json
|
||||
!pkg/genesis/*.json
|
||||
|
||||
# Temporary go files in root (not in subdirectories)
|
||||
/*.go
|
||||
!/main.go
|
||||
|
||||
# Node directory (cloned for development)
|
||||
node/
|
||||
vm/
|
||||
|
||||
# Docker deployment files
|
||||
deploy/docker/Dockerfile
|
||||
deploy/docker/configs/
|
||||
|
||||
# Docs Next.js files (built separately)
|
||||
docs/app/
|
||||
docs/content/
|
||||
docs/lib/
|
||||
docs/public/
|
||||
docs/next.config.mjs
|
||||
docs/postcss.config.js
|
||||
docs/source.config.ts
|
||||
docs/tailwind.config.ts
|
||||
docs/mdx-components.tsx
|
||||
|
||||
# Staking keys (should not be committed)
|
||||
test/test-mainnet-keys/
|
||||
test/test-staking-keys/
|
||||
|
||||
# Archive build outputs
|
||||
cmd/archive/*/import-blockchain
|
||||
cmd/archive/*/netrunner
|
||||
cmd/archive/*/rpc-server
|
||||
cmd/archive/*/verify-chain
|
||||
cmd/archive/*/check-balance
|
||||
migrated-ethdb.disabled/
|
||||
*.mem
|
||||
*.sst
|
||||
@@ -0,0 +1,29 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2024, Lux Partners Limited
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
@@ -0,0 +1,461 @@
|
||||
# Genesis
|
||||
|
||||
Canonical genesis configurations for all Lux blockchain networks.
|
||||
|
||||
## Networks
|
||||
|
||||
### C-Chain (Primary Network)
|
||||
|
||||
| Network | Network ID | Chain ID | Genesis Hash | Treasury |
|
||||
|---------|------------|----------|--------------|----------|
|
||||
| Mainnet | 1 | 96369 | `0x3f4fa2a0b0ce089f52bf0ae9199c75ffdd76ecafc987794050cb0d286f1ec61e` | 2T LUX |
|
||||
| Testnet | 2 | 96368 | `0x1c5fe37764b8bc146dc88bc1c2e0259cd8369b07a06439bcfa1782b5d4fb0995` | 2T LUX |
|
||||
| Devnet | 3 | 96370 | `0x53fe8be293555d20de41847f96081f4e8beca1ee2c206999ffbf7c70e497cf43` | 2T LUX |
|
||||
| Local | 1337 | 1337 | - | 2T LUX |
|
||||
|
||||
### Subnet Chains
|
||||
|
||||
| Chain | Mainnet ID | Testnet ID | Devnet ID | Treasury |
|
||||
|-------|-----------|-----------|-----------|----------|
|
||||
| Zoo | 200200 | 200201 | 200202 | 2T ZOO |
|
||||
| SPC | 36911 | 36910 | 36912 | 1B SPC |
|
||||
| Hanzo | 36963 | 36962* | 36964 | 1B AI |
|
||||
| Pars | 494949 | 494950 | 494951 | 2T PARS |
|
||||
|
||||
*Note: Hanzo testnet was deployed with chain ID 36964 (devnet ID) due to a deployment error. The intended ID is 36962.
|
||||
|
||||
### Deployed Genesis Hashes (All Chains)
|
||||
|
||||
| Chain | Mainnet | Testnet | Devnet |
|
||||
|-------|---------|---------|--------|
|
||||
| C-Chain | `0x3f4fa2a...1ec61e` | `0x53fe8be...97cf43` | `0x53fe8be...97cf43` |
|
||||
| Zoo | `0x7c548af...14933` | `0x0652fb2...d1979` | `0x0652fb2...d1979` |
|
||||
| SPC | `0x4dc9fd5...cfac8` | `0x210fcc1...0b692` | `0x0652fb2...d1979` |
|
||||
| Hanzo | `0x6cf786a...48e67` | `0x6cf786a...48e67` | `0x6cf786a...48e67` |
|
||||
| Pars | `0xa32921a...b2f34` | `0xa32921a...b2f34` | `0xa32921a...b2f34` |
|
||||
|
||||
## Genesis Treasury
|
||||
|
||||
All networks use the same production treasury address:
|
||||
|
||||
```
|
||||
Address: 0x9011E888251AB053B7bD1cdB598Db4f9DEd94714
|
||||
Balance: 2,000,000,000,000,000,000,000,000,000,000 wei (2T LUX)
|
||||
Hex: 0x193e5939a08ce9dbd480000000
|
||||
```
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
configs/
|
||||
├── mainnet/
|
||||
│ ├── cchain.json # C-Chain genesis (chain ID 96369)
|
||||
│ ├── pchain.json # P-Chain genesis (Platform)
|
||||
│ ├── network.json # Network ID, message, startTime
|
||||
│ └── bootstrappers.json # Bootstrap nodes
|
||||
├── testnet/
|
||||
│ ├── cchain.json # C-Chain genesis (chain ID 96368)
|
||||
│ └── genesis.json
|
||||
├── devnet/
|
||||
│ └── cchain.json
|
||||
├── local/
|
||||
│ ├── cchain.json
|
||||
│ └── genesis.json
|
||||
├── zoo-{mainnet,testnet,devnet}/
|
||||
│ └── genesis.json # Zoo (200200/200201/200202)
|
||||
├── spc-{mainnet,testnet,devnet}/
|
||||
│ └── genesis.json # SPC (36911/36910/36912)
|
||||
├── hanzo-{mainnet,testnet,devnet}/
|
||||
│ └── genesis.json # Hanzo (36963/36962/36964)
|
||||
└── pars-{mainnet,testnet,devnet}/
|
||||
└── genesis.json # Pars (494949/494950/494951)
|
||||
```
|
||||
|
||||
## C-Chain Configuration
|
||||
|
||||
### Mainnet (96369)
|
||||
|
||||
```json
|
||||
{
|
||||
"chainId": 96369,
|
||||
"gasLimit": 12000000,
|
||||
"targetBlockRate": 2,
|
||||
"minBaseFee": 25000000000,
|
||||
"stateRoot": "0x2d1cedac263020c5c56ef962f6abe0da1f5217bdc6468f8c9258a0ea23699e80",
|
||||
"baseFeePerGas": "0x5d21dba00"
|
||||
}
|
||||
```
|
||||
|
||||
### Network Parameters
|
||||
|
||||
| Parameter | Mainnet | Testnet | Devnet | Local |
|
||||
|-----------|---------|---------|--------|-------|
|
||||
| Block Rate | 2s | 2s | 1s | 1s |
|
||||
| Gas Limit | 12M | 12M | 20M | 15M |
|
||||
| Min Base Fee | 25 gwei | 25 gwei | 1 gwei | 1 gwei |
|
||||
| Chain ID | 96369 | 96368 | 96370 | 1337 |
|
||||
|
||||
### Warp Precompile (Required for Genesis Hash)
|
||||
|
||||
Both mainnet and testnet C-Chain genesis MUST include the warp precompile:
|
||||
|
||||
```json
|
||||
{
|
||||
"alloc": {
|
||||
"0200000000000000000000000000000000000005": {
|
||||
"balance": "0x0",
|
||||
"nonce": "0x1",
|
||||
"code": "0x01"
|
||||
},
|
||||
"0x9011E888251AB053B7bD1cdB598Db4f9DEd94714": {
|
||||
"balance": "0x193e5939a08ce9dbd480000000"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This produces the canonical state root: `0x2d1cedac263020c5c56ef962f6abe0da1f5217bdc6468f8c9258a0ea23699e80`
|
||||
|
||||
### Upgrade Timestamps
|
||||
|
||||
C-Chain protocol upgrades activated Dec 25, 2024 @ 4:20pm UTC (timestamp 1735143600):
|
||||
|
||||
```json
|
||||
{
|
||||
"subnetEVMTimestamp": 0,
|
||||
"durangoTimestamp": 0,
|
||||
"etnaTimestamp": 1735143600,
|
||||
"fortunaTimestamp": 1735143600,
|
||||
"graniteTimestamp": 1735143600
|
||||
}
|
||||
```
|
||||
|
||||
## Genesis Package (`pkg/genesis/`)
|
||||
|
||||
### Core Types
|
||||
|
||||
```go
|
||||
// Config - Top-level genesis configuration
|
||||
type Config struct {
|
||||
NetworkID uint32
|
||||
Allocations []Allocation
|
||||
StartTime uint64
|
||||
InitialStakeDuration uint64
|
||||
InitialStakeDurationOffset uint64
|
||||
InitialStakedFunds []ids.ShortID
|
||||
InitialStakers []Staker
|
||||
CChainGenesis string
|
||||
Message string
|
||||
}
|
||||
|
||||
// Staker - Initial validator
|
||||
type Staker struct {
|
||||
NodeID ids.NodeID
|
||||
RewardAddress ids.ShortID
|
||||
DelegationFee uint32
|
||||
Signer *ProofOfPossession
|
||||
Weight uint64
|
||||
StartTime uint64
|
||||
EndTime uint64
|
||||
}
|
||||
|
||||
// Allocation - Genesis allocation
|
||||
type Allocation struct {
|
||||
ETHAddr ids.ShortID
|
||||
LUXAddr ids.ShortID
|
||||
InitialAmount uint64
|
||||
UnlockSchedule []LockedAmount
|
||||
}
|
||||
```
|
||||
|
||||
### Bech32 Address Format
|
||||
|
||||
Addresses use chain prefix + bech32 encoding:
|
||||
|
||||
```
|
||||
Format: {chain}-{hrp}1{data}{checksum}
|
||||
Example: P-lux1ck0t9h5u7jvvzhx29n99guqjsfkpzt67wgx7wg
|
||||
|
||||
Chain Prefixes:
|
||||
- P-lux1... (P-Chain/Platform)
|
||||
- X-lux1... (X-Chain/Exchange)
|
||||
- C-0x... (C-Chain/EVM hex)
|
||||
```
|
||||
|
||||
**Important**: The bech32 checksum is computed using only the HRP ("lux"), not the chain prefix ("P-lux").
|
||||
|
||||
## CLI Tools
|
||||
|
||||
### genesis (`cmd/genesis/`)
|
||||
|
||||
Generate genesis configurations:
|
||||
|
||||
```bash
|
||||
# Generate from mnemonic
|
||||
MNEMONIC="test test..." ./genesis -network-id 96369 -validators 5 -output genesis.json
|
||||
|
||||
# From existing keys
|
||||
./genesis -network mainnet -keys-dir ~/.lux/keys -output genesis.json
|
||||
```
|
||||
|
||||
### extract-genesis (`cmd/extract-genesis/`)
|
||||
|
||||
Extract genesis from existing chain data:
|
||||
|
||||
```bash
|
||||
./extract-genesis -chaindata /path/to/chaindata -output cchain.json
|
||||
```
|
||||
|
||||
## State Repository Integration
|
||||
|
||||
Historical blockchain state is maintained separately in [luxfi/state](https://github.com/luxfi/state).
|
||||
|
||||
### Available State Data
|
||||
|
||||
| Network | Blocks | RLP Size | Location |
|
||||
|---------|--------|----------|----------|
|
||||
| Lux Mainnet | 1,082,780 | 1.2 GB | `rlp/lux-mainnet/lux-mainnet-96369.rlp` |
|
||||
| Lux Testnet | 218 | 695 KB | `rlp/lux-testnet/lux-testnet-96368.rlp` |
|
||||
| Zoo Mainnet | 799 | 1.3 MB | `rlp/zoo-mainnet/zoo-mainnet-200200.rlp` |
|
||||
| Zoo Testnet | 84 | 156 KB | `rlp/zoo-testnet/zoo-testnet-200201.rlp` |
|
||||
| SPC Mainnet | 11 | 7.8 KB | `rlp/spc-mainnet/spc-mainnet-36911.rlp` |
|
||||
|
||||
### Import Workflow
|
||||
|
||||
```bash
|
||||
# Clone repos
|
||||
git clone https://github.com/luxfi/genesis
|
||||
git clone https://github.com/luxfi/state
|
||||
|
||||
# Initialize with genesis
|
||||
geth init --datadir /tmp/lux genesis/configs/mainnet/cchain.json
|
||||
|
||||
# Import blocks
|
||||
geth import --datadir /tmp/lux state/rlp/lux-mainnet/lux-mainnet-96369.rlp
|
||||
```
|
||||
|
||||
### SubnetEVM Header Formats
|
||||
|
||||
State repo RLP uses SubnetEVM header encoding:
|
||||
|
||||
| Fields | Format | Usage |
|
||||
|--------|--------|-------|
|
||||
| 16 | Genesis block | No optional fields |
|
||||
| 17-19 | Post-genesis | ExtDataHash, ExtDataGasUsed, BlockGasCost |
|
||||
|
||||
**Coreth field order** (positions 15+):
|
||||
```
|
||||
15: ExtDataHash (common.Hash - value type, required)
|
||||
16: BaseFee (*big.Int)
|
||||
17: ExtDataGasUsed (*big.Int)
|
||||
18: BlockGasCost (*big.Int)
|
||||
```
|
||||
|
||||
## Mainnet Validators
|
||||
|
||||
| Node | NodeID | P-Chain Address |
|
||||
|------|--------|-----------------|
|
||||
| 1 | NodeID-FrtEjhat6RUqjEWCJgYZKqBaxY2Woyy5G | P-lux1ck0t9h5u7jvvzhx29n99guqjsfkpzt67wgx7wg |
|
||||
| 2 | NodeID-9hq49qGVZN7M7tXxdpF3AqptQGdmPCFnQ | P-lux1dclruwcn9ug8u0jjk3ukh676jr3lsy4er9m3l5 |
|
||||
| 3 | NodeID-8osEnSC4LQFdG1LMit12CBsE6BfKGHNAw | P-lux1qdv9zns0gpfesw0h28jqp2up6h77du2damqf88 |
|
||||
| 4 | NodeID-MrdgTgPuddyo7anomKr4akMcoKzVKcgbG | P-lux1jjs4nx7ul4d6pnsjtpv2khzu8p4yctegvass46 |
|
||||
| 5 | NodeID-Mgd5yHs4pe6qRjkBcW5Y7oqHA51j4afcC | P-lux1970ngvf6s6rsndrkvjzr6lfvf2tdl30529c435 |
|
||||
|
||||
## Token Decimals
|
||||
|
||||
```go
|
||||
// P-Chain/X-Chain: 6 decimals (microLUX as base unit)
|
||||
const (
|
||||
MicroLux uint64 = 1 // 0.000001 LUX
|
||||
MilliLux uint64 = 1_000 // 0.001 LUX
|
||||
Lux uint64 = 1_000_000 // 1 LUX
|
||||
)
|
||||
|
||||
// C-Chain: 18 decimals (wei, standard EVM)
|
||||
// 1 LUX = 10^18 wei on C-Chain
|
||||
```
|
||||
|
||||
## Verification
|
||||
|
||||
### Genesis Hash Check
|
||||
|
||||
```bash
|
||||
# Verify mainnet C-Chain genesis
|
||||
geth init --datadir /tmp/verify configs/mainnet/cchain.json 2>&1 | grep "genesis hash"
|
||||
# Expected: 0x3f4fa2a0b0ce089f52bf0ae9199c75ffdd76ecafc987794050cb0d286f1ec61e
|
||||
|
||||
# Verify state root
|
||||
cat configs/mainnet/cchain.json | jq -r '.stateRoot'
|
||||
# Expected: 0x2d1cedac263020c5c56ef962f6abe0da1f5217bdc6468f8c9258a0ea23699e80
|
||||
```
|
||||
|
||||
### Network ID Verification
|
||||
|
||||
```bash
|
||||
cat configs/mainnet/network.json | jq '.networkID'
|
||||
# Expected: 96369
|
||||
|
||||
cat configs/testnet/network.json | jq '.networkID'
|
||||
# Expected: 96368
|
||||
```
|
||||
|
||||
## Fortuna Timestamp Fix (2025-12-25)
|
||||
|
||||
### Problem
|
||||
Block import failed with gas limit mismatch when importing Zoo chain blocks:
|
||||
```
|
||||
invalid gas limit: have 12000000, want 10000000
|
||||
```
|
||||
|
||||
### Root Cause
|
||||
Zoo genesis files had `fortunaTimestamp: 0` which activates Fortuna from block 0. Fortuna uses dynamic gas limits via `state.MaxCapacity()` returning 10M, but original blocks had 12M gas limit (Cortina rules).
|
||||
|
||||
### Solution
|
||||
Set far-future timestamps for Fortuna, Etna, and Granite upgrades:
|
||||
|
||||
```json
|
||||
{
|
||||
"etnaTimestamp": 253399622400,
|
||||
"fortunaTimestamp": 253399622400,
|
||||
"graniteTimestamp": 253399622400
|
||||
}
|
||||
```
|
||||
|
||||
### Files Updated
|
||||
- `configs/zoo-mainnet/genesis.json`
|
||||
- `configs/zoo-testnet/genesis.json`
|
||||
|
||||
### Verified Block Counts (2025-12-28)
|
||||
|
||||
| Network | Chain ID | Blocks | Genesis Hash |
|
||||
|---------|----------|--------|--------------|
|
||||
| Lux Mainnet | 96369 | **1,082,780** | `0x3f4fa2a0b0ce089f52bf0ae9199c75ffdd76ecafc987794050cb0d286f1ec61e` |
|
||||
| Lux Testnet | 96368 | **218** | `0x1c5fe37764b8bc146dc88bc1c2e0259cd8369b07a06439bcfa1782b5d4fb0995` |
|
||||
| Zoo Mainnet | 200200 | **799** | `0x7c548af47de27560779ccc67dda32a540944accc71dac3343da3b9cd18f14933` |
|
||||
| Zoo Testnet | 200201 | **84** | `0x0652fb2fde1460544a5893e5eba5095ff566861cbc87fcb1c73be2b81d6d1979` |
|
||||
| SPC Mainnet | 36911 | **11** | `0x4dc9fd5cf4ee64609f140ba0aa50f320cadf0ae8b59a29415979bc05b17cfac8` |
|
||||
|
||||
---
|
||||
|
||||
## Canonical Genesis Checksums (SHA256)
|
||||
|
||||
**IMPORTANT**: All genesis files are sorted alphabetically by key for determinism.
|
||||
These checksums are the canonical reference - if they don't match, the file has been modified.
|
||||
|
||||
| File | SHA256 |
|
||||
|------|--------|
|
||||
| `mainnet/cchain.json` | `968e9f3cd19a9b88ca157db3894622272ded02f0f184b5386493ff55897001be` |
|
||||
| `testnet/cchain.json` | `ba211e1bbbd7807c2224d240628facb2dfb6c2d6cde2e274eb881a57609ed038` |
|
||||
| `zoo-mainnet/genesis.json` | `14c01e8bb5ac13144a3730c695847d53506647efbaada3f3240a16e7b10684f7` |
|
||||
| `zoo-testnet/genesis.json` | `74357b85e6968ca70d8907b2e940e5e234fd6b288749865d91ab69a6083e6dae` |
|
||||
| `zoo-devnet/genesis.json` | `dd604fee5c78f7367b314a9a7d7ee1d5f8c42839165164262368afc7dd91fc5e` |
|
||||
| `spc-mainnet/genesis.json` | `9c01115559556fc245d4993b193889370bd99edd22a7fa1e9e3d7ddafbb63535` |
|
||||
| `spc-testnet/genesis.json` | `af12bb81dd9abce26b7a311f00fa2ac7f5adfb996e698b9f473252199f747b2c` |
|
||||
| `spc-devnet/genesis.json` | `317d54ac7846b0f174964552b4949d858072f6ffbc37f6c10092e01d595ce46f` |
|
||||
| `hanzo-mainnet/genesis.json` | `adae3c441b34db937dbcd961450bff5bcabd388a5479fe9ba7380ef61f083637` |
|
||||
| `hanzo-testnet/genesis.json` | `a2d00685bf7f414815dc3579552e19045183428c520f4f5b23a6726cf4002c32` |
|
||||
| `hanzo-devnet/genesis.json` | `1c489d29c08857696909f029a1b2cda554e059e86c630ae64f35a5243f4bd25c` |
|
||||
| `pars-mainnet/genesis.json` | `ca35306a73f4cbfeccc8018b71596b122f698a23f3c038993a2ad34ca315e539` |
|
||||
| `pars-testnet/genesis.json` | `9d7ff05187bb2570d2a1af8ccfa41a99dfaf8b56558decf127209ac6d5faec85` |
|
||||
| `pars-devnet/genesis.json` | `3fc55f6be7b46dd6955ab2ec95186a4aa7da13991650909cb5f9ecbaa51b06c7` |
|
||||
|
||||
### Verify Checksum
|
||||
|
||||
```bash
|
||||
cd ~/work/lux/genesis/configs
|
||||
shasum -a 256 zoo-mainnet/genesis.json
|
||||
# Should output: 241fd0209253e7a5a04c55c7a6f4f0ba864a83dd9840c4969d5a1b5b5f6c3366
|
||||
```
|
||||
|
||||
### Re-sort if Modified
|
||||
|
||||
```bash
|
||||
# If file was modified, re-sort it:
|
||||
jq -S '.' genesis.json > genesis.sorted.json && mv genesis.sorted.json genesis.json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Genesis Hash Fix (2025-12-28)
|
||||
|
||||
### Problem
|
||||
Mainnet genesis hash was computing as `0xc3069db32ef986d8e287458e44066eb04205648e602dc2fede561c424a6516de` instead of the expected `0x3f4fa2a0b0ce089f52bf0ae9199c75ffdd76ecafc987794050cb0d286f1ec61e` from historical RLP blocks.
|
||||
|
||||
### Root Cause
|
||||
The `warpConfig.blockTimestamp` was set to 0, which caused `ApplyPrecompileActivations()` in coreth to add the warp precompile to genesis state BEFORE the alloc was applied. When warp wasn't in alloc, this changed the state trie and produced a different genesis hash.
|
||||
|
||||
**Key insight from `coreth/core/genesis.go:271`:**
|
||||
```go
|
||||
// ApplyPrecompileActivations runs FIRST (line 271)
|
||||
ApplyPrecompileActivations(g.Config, nil, blockContext, statedb)
|
||||
|
||||
// Then alloc is applied (lines 276-283)
|
||||
for addr, account := range g.Alloc {
|
||||
// This overwrites precompile if in alloc, leaves it if not
|
||||
}
|
||||
```
|
||||
|
||||
### Solution
|
||||
The original mainnet genesis was created with warp precompile IN the alloc section:
|
||||
|
||||
```json
|
||||
{
|
||||
"alloc": {
|
||||
"0200000000000000000000000000000000000005": {
|
||||
"balance": "0x0",
|
||||
"nonce": "0x1",
|
||||
"code": "0x01"
|
||||
}
|
||||
},
|
||||
"warpConfig": {
|
||||
"blockTimestamp": 1730446786,
|
||||
"quorumNumerator": 67,
|
||||
"requirePrimaryNetworkSigners": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `warpConfig.blockTimestamp` equals the genesis timestamp (`0x672485c2` = 1730446786), meaning warp is activated at genesis. Having warp in both alloc AND warpConfig ensures consistent state.
|
||||
|
||||
### Files Restored
|
||||
- `configs/mainnet/cchain.json` - Restored from git with warp in alloc
|
||||
- `configs/mainnet/genesis.json` - Updated cChainGenesis to match
|
||||
|
||||
### Verification
|
||||
```bash
|
||||
# Start mainnet
|
||||
netrunner start --networks mainnet
|
||||
|
||||
# Verify genesis hash
|
||||
curl -X POST -H 'Content-Type: application/json' \
|
||||
--data '{"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["0x0",false],"id":1}' \
|
||||
http://127.0.0.1:9630/ext/bc/C/rpc | jq '.result.hash'
|
||||
# Expected: "0x3f4fa2a0b0ce089f52bf0ae9199c75ffdd76ecafc987794050cb0d286f1ec61e"
|
||||
|
||||
# Verify warp precompile in state
|
||||
curl -X POST -H 'Content-Type: application/json' \
|
||||
--data '{"jsonrpc":"2.0","method":"eth_getCode","params":["0x0200000000000000000000000000000000000005","0x0"],"id":1}' \
|
||||
http://127.0.0.1:9630/ext/bc/C/rpc | jq '.result'
|
||||
# Expected: "0x01"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Known Issues
|
||||
|
||||
### Documentation Discrepancies
|
||||
|
||||
1. **Network IDs**: Some older docs reference Network ID 1/2 instead of 96369/96368
|
||||
2. **File paths**: Docs may reference `chain.rlp.gz` but actual files are `{network}-{chainid}.rlp`
|
||||
3. **Block counts**: Documentation may be outdated - verify against actual RLP files
|
||||
|
||||
### State Repo Sync
|
||||
|
||||
The state repo should be periodically updated with:
|
||||
- Latest block exports
|
||||
- Updated metadata.json files
|
||||
- Corrected documentation
|
||||
|
||||
---
|
||||
|
||||
*Last Updated: 2025-12-28 - Genesis hashes verified against RLP*
|
||||
@@ -0,0 +1,65 @@
|
||||
# Genesis Makefile
|
||||
|
||||
.PHONY: build test clean lint fmt vet
|
||||
|
||||
# Build output directory
|
||||
BIN_DIR := bin
|
||||
|
||||
# Binary name
|
||||
BINARY := genesis
|
||||
|
||||
# Go parameters
|
||||
GOCMD := go
|
||||
GOBUILD := $(GOCMD) build
|
||||
GOTEST := $(GOCMD) test
|
||||
GOVET := $(GOCMD) vet
|
||||
GOFMT := gofmt
|
||||
GOMOD := $(GOCMD) mod
|
||||
|
||||
# Build flags
|
||||
LDFLAGS := -s -w
|
||||
|
||||
# Default target
|
||||
all: build
|
||||
|
||||
# Build the binary
|
||||
build:
|
||||
@mkdir -p $(BIN_DIR)
|
||||
$(GOBUILD) -ldflags="$(LDFLAGS)" -o $(BIN_DIR)/$(BINARY) ./cmd/genesis
|
||||
|
||||
# Run tests
|
||||
test:
|
||||
$(GOTEST) -v -race ./...
|
||||
|
||||
# Run tests with coverage
|
||||
test-coverage:
|
||||
$(GOTEST) -v -race -coverprofile=coverage.txt -covermode=atomic ./...
|
||||
|
||||
# Clean build artifacts
|
||||
clean:
|
||||
rm -rf $(BIN_DIR)
|
||||
rm -f coverage.txt
|
||||
|
||||
# Run linter
|
||||
lint:
|
||||
golangci-lint run --timeout=5m
|
||||
|
||||
# Format code
|
||||
fmt:
|
||||
$(GOFMT) -w .
|
||||
|
||||
# Run go vet
|
||||
vet:
|
||||
$(GOVET) ./...
|
||||
|
||||
# Download dependencies
|
||||
deps:
|
||||
$(GOMOD) download
|
||||
|
||||
# Tidy dependencies
|
||||
tidy:
|
||||
$(GOMOD) tidy
|
||||
|
||||
# Install binary to GOPATH/bin
|
||||
install: build
|
||||
cp $(BIN_DIR)/$(BINARY) $(GOPATH)/bin/
|
||||
@@ -0,0 +1,135 @@
|
||||
# Lux Network Genesis Configurations
|
||||
|
||||
This repository contains the canonical genesis configurations for all Lux networks.
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
genesis/
|
||||
├── mainnet/ # LUX Mainnet (Chain ID: 96369)
|
||||
│ ├── cchain.json # C-Chain genesis (EVM compatible)
|
||||
│ ├── pchain.json # P-Chain genesis (Platform chain)
|
||||
│ ├── xchain.json # X-Chain genesis (Exchange chain)
|
||||
│ ├── network.json # Network configuration
|
||||
│ └── genesis.json # Full network genesis
|
||||
├── testnet/ # LUX Testnet (Chain ID: 96368)
|
||||
│ ├── cchain.json # C-Chain genesis (EVM compatible)
|
||||
│ └── genesis.json # Full testnet genesis
|
||||
├── devnet/ # LUX Devnet (Chain ID: 96370) - Fast iteration network
|
||||
│ └── cchain.json # C-Chain genesis (EVM compatible)
|
||||
├── local/ # Local development network (Chain ID: 1337)
|
||||
│ ├── cchain.json # C-Chain genesis (EVM compatible)
|
||||
│ └── genesis.json # Local network genesis
|
||||
├── bootstrappers.json # Network bootstrapper nodes
|
||||
└── checkpoints.json # Network checkpoints
|
||||
```
|
||||
|
||||
## Mainnet C-Chain Genesis
|
||||
|
||||
The mainnet C-Chain was initialized with a single genesis allocation:
|
||||
|
||||
| Address | Balance |
|
||||
|---------|---------|
|
||||
| `0x9011e888251ab053b7bd1cdb598db4f9ded94714` | 2,000,000,000,000,000,000,000,000,000,000 wei (2T LUX) |
|
||||
|
||||
### Key Parameters
|
||||
|
||||
- **Chain ID**: 96369
|
||||
- **Gas Limit**: 30,000,000 (0x1C9C380)
|
||||
- **Min Base Fee**: 25 gwei
|
||||
- **Block Rate**: 2 seconds
|
||||
|
||||
## Network IDs
|
||||
|
||||
| Network | Chain ID | Network ID | Purpose |
|
||||
|---------|----------|------------|---------|
|
||||
| LUX Mainnet | 96369 | 96369 | Production network |
|
||||
| LUX Testnet | 96368 | 96368 | Public test network |
|
||||
| LUX Devnet | 96370 | 96370 | Fast iteration dev network |
|
||||
| Local | 1337 | 1337 | Local development |
|
||||
|
||||
## Network Configurations
|
||||
|
||||
### Mainnet (96369)
|
||||
- **Block Rate**: 2 seconds
|
||||
- **Gas Limit**: 30M
|
||||
- **Min Base Fee**: 25 gwei
|
||||
- **Genesis Account**: `0x9011...` with 2T LUX
|
||||
|
||||
### Testnet (96368)
|
||||
- **Block Rate**: 2 seconds
|
||||
- **Gas Limit**: 12M
|
||||
- **Min Base Fee**: 25 gwei
|
||||
- **Genesis Account**: `0x9011...` with 2T LUX (same as mainnet)
|
||||
|
||||
### Devnet (96370)
|
||||
- **Block Rate**: 1 second (faster)
|
||||
- **Gas Limit**: 20M
|
||||
- **Min Base Fee**: 1 gwei (cheaper)
|
||||
- **Genesis Account**: `0x9011...` with 2T LUX
|
||||
- All upgrades enabled at genesis (Shanghai, Cancun, etc.)
|
||||
|
||||
### Local (1337)
|
||||
- **Block Rate**: 1 second
|
||||
- **Gas Limit**: 15M
|
||||
- **Min Base Fee**: 1 gwei
|
||||
- **Genesis Account**: `0x9011...` with 2T LUX
|
||||
- All upgrades enabled at genesis
|
||||
|
||||
## Usage
|
||||
|
||||
These genesis files are used by:
|
||||
- `luxd` node for network initialization
|
||||
- `coreth` for C-Chain configuration
|
||||
- Block explorers for genesis block verification
|
||||
- Migration tools for state reconstruction
|
||||
|
||||
## RPC Endpoints
|
||||
|
||||
- **Mainnet**: `http://localhost:9630/ext/bc/C/rpc`
|
||||
- **Testnet**: `http://localhost:9630/ext/bc/C/rpc` (testnet node)
|
||||
- **Devnet**: `http://localhost:9630/ext/bc/C/rpc` (devnet node)
|
||||
|
||||
## Genesis Account
|
||||
|
||||
All networks use the same production genesis account:
|
||||
|
||||
| Account | Balance | Networks |
|
||||
|---------|---------|----------|
|
||||
| `0x9011e888251ab053b7bd1cdb598db4f9ded94714` | 2T LUX | All networks |
|
||||
|
||||
## State Data & RLP Exports
|
||||
|
||||
Blockchain state exports and RLP-encoded blocks are maintained in a separate repository:
|
||||
|
||||
**Repository**: [github.com/luxfi/state](https://github.com/luxfi/state)
|
||||
|
||||
### Available Data
|
||||
|
||||
| Network | Chain ID | Blocks | RLP Location |
|
||||
|---------|----------|--------|--------------|
|
||||
| Lux Mainnet | 96369 | 1,082,780 | `rlp/lux-mainnet/lux-mainnet-96369.rlp` |
|
||||
| Lux Testnet | 96368 | 219 | `rlp/lux-testnet/lux-testnet-96368.rlp` |
|
||||
| Zoo Mainnet | 200200 | 799 | `rlp/zoo-mainnet/zoo-mainnet-200200.rlp` |
|
||||
| Zoo Testnet | 200201 | 85 | `rlp/zoo-testnet/zoo-testnet-200201.rlp` |
|
||||
|
||||
### Import Workflow
|
||||
|
||||
```bash
|
||||
# Clone repos
|
||||
git clone https://github.com/luxfi/genesis
|
||||
git clone https://github.com/luxfi/state
|
||||
|
||||
# Initialize with genesis
|
||||
geth init --datadir /path/to/db genesis/mainnet/cchain.json
|
||||
|
||||
# Import blocks from state repo
|
||||
geth import --datadir /path/to/db state/rlp/lux-mainnet/lux-mainnet-96369.rlp
|
||||
```
|
||||
|
||||
### Source Data
|
||||
|
||||
The state repo also contains:
|
||||
- `pebbledb/` - Original SubnetEVM PebbleDB databases
|
||||
- `docs/` - State verification and recovery documentation
|
||||
- `pkg/` - Go tools for archaeology, bridge, and scanning
|
||||
@@ -0,0 +1,191 @@
|
||||
# Genesis Builder
|
||||
|
||||
This package provides genesis building functionality for Lux networks. It depends on node types and is responsible for converting genesis configuration into actual genesis bytes.
|
||||
|
||||
## Architecture
|
||||
|
||||
The genesis builder bridges the gap between the decoupled `github.com/luxfi/genesis` package (which provides JSON-based configuration) and the node's internal types:
|
||||
|
||||
```
|
||||
github.com/luxfi/genesis → github.com/luxfi/node/genesis/builder
|
||||
(JSON config, no node deps) (Type conversion, genesis building)
|
||||
```
|
||||
|
||||
## Types
|
||||
|
||||
### StakingConfig
|
||||
|
||||
```go
|
||||
type StakingConfig struct {
|
||||
UptimeRequirement float64
|
||||
MinValidatorStake uint64
|
||||
MaxValidatorStake uint64
|
||||
MinDelegatorStake uint64
|
||||
MinDelegationFee uint32
|
||||
MinStakeDuration time.Duration // Converted from uint64 seconds
|
||||
MaxStakeDuration time.Duration // Converted from uint64 seconds
|
||||
RewardConfig reward.Config // Uses platformvm/reward.Config
|
||||
|
||||
// BLS key information for genesis replay
|
||||
NodeID string
|
||||
BLSPublicKey []byte
|
||||
BLSProofOfPossession []byte
|
||||
}
|
||||
```
|
||||
|
||||
### TxFeeConfig
|
||||
|
||||
```go
|
||||
type TxFeeConfig struct {
|
||||
TxFee uint64
|
||||
CreateAssetTxFee uint64
|
||||
DynamicFeeConfig gas.Config // From vms/components/gas
|
||||
ValidatorFeeConfig fee.Config // From vms/platformvm/validators/fee
|
||||
}
|
||||
```
|
||||
|
||||
### Bootstrapper
|
||||
|
||||
```go
|
||||
type Bootstrapper struct {
|
||||
ID ids.NodeID // Parsed from string
|
||||
IP netip.AddrPort // Parsed from string
|
||||
}
|
||||
```
|
||||
|
||||
## Functions
|
||||
|
||||
### Configuration Retrieval
|
||||
|
||||
```go
|
||||
// Get staking config with time.Duration types
|
||||
func GetStakingConfig(networkID uint32) StakingConfig
|
||||
|
||||
// Get tx fee config with gas.Config and fee.Config
|
||||
func GetTxFeeConfig(networkID uint32) TxFeeConfig
|
||||
|
||||
// Get parsed bootstrappers
|
||||
func GetBootstrappers(networkID uint32) ([]Bootstrapper, error)
|
||||
|
||||
// Sample random bootstrappers
|
||||
func SampleBootstrappers(networkID uint32, count int) ([]Bootstrapper, error)
|
||||
|
||||
// Get genesis config (delegates to genesis package)
|
||||
func GetConfig(networkID uint32) *genesiscfg.Config
|
||||
```
|
||||
|
||||
### Genesis Building
|
||||
|
||||
```go
|
||||
// Build genesis bytes from config
|
||||
func FromConfig(config *genesiscfg.Config) ([]byte, ids.ID, error)
|
||||
|
||||
// Build genesis from file
|
||||
func FromFile(networkID uint32, filepath string, stakingCfg *StakingConfig) ([]byte, ids.ID, error)
|
||||
|
||||
// Build genesis from base64 content
|
||||
func FromFlag(networkID uint32, genesisContent string, stakingCfg *StakingConfig) ([]byte, ids.ID, error)
|
||||
|
||||
// Build genesis for database replay mode
|
||||
func FromDatabase(networkID uint32, dbPath string, dbType string, stakingCfg *StakingConfig) ([]byte, ids.ID, error)
|
||||
```
|
||||
|
||||
### Helpers
|
||||
|
||||
```go
|
||||
// Get VM genesis transaction
|
||||
func VMGenesis(genesisBytes []byte, vmID ids.ID) (*pchaintxs.Tx, error)
|
||||
|
||||
// Get chain and API aliases
|
||||
func Aliases(genesisBytes []byte) (map[string][]string, map[ids.ID][]string, error)
|
||||
|
||||
// Get LUX asset ID from XVM genesis
|
||||
func XAssetID(xvmGenesisBytes []byte) (ids.ID, error)
|
||||
```
|
||||
|
||||
## Default Fee Configurations
|
||||
|
||||
The package provides default fee configurations for each network:
|
||||
|
||||
```go
|
||||
// Dynamic fee configs
|
||||
var MainnetDynamicFeeConfig gas.Config
|
||||
var TestnetDynamicFeeConfig gas.Config
|
||||
var LocalDynamicFeeConfig gas.Config
|
||||
|
||||
// Validator fee configs
|
||||
var MainnetValidatorFeeConfig fee.Config
|
||||
var TestnetValidatorFeeConfig fee.Config
|
||||
var LocalValidatorFeeConfig fee.Config
|
||||
```
|
||||
|
||||
## VM Aliases
|
||||
|
||||
```go
|
||||
var VMAliases = map[ids.ID][]string{
|
||||
constants.PlatformVMID: {"platform"},
|
||||
constants.XVMID: {"xvm"},
|
||||
constants.EVMID: {"evm"},
|
||||
secp256k1fx.ID: {"secp256k1fx"},
|
||||
nftfx.ID: {"nftfx"},
|
||||
propertyfx.ID: {"propertyfx"},
|
||||
}
|
||||
```
|
||||
|
||||
## Usage Example
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/luxfi/node/genesis/builder"
|
||||
"github.com/luxfi/constants"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Get network configuration
|
||||
stakingCfg := builder.GetStakingConfig(constants.MainnetID)
|
||||
txFeeCfg := builder.GetTxFeeConfig(constants.MainnetID)
|
||||
|
||||
// Get bootstrappers
|
||||
bootstrappers, err := builder.SampleBootstrappers(constants.MainnetID, 5)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Build genesis bytes
|
||||
config := builder.GetConfig(constants.MainnetID)
|
||||
genesisBytes, luxAssetID, err := builder.FromConfig(config)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Get chain aliases
|
||||
apiAliases, chainAliases, err := builder.Aliases(genesisBytes)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Migration from genesis package
|
||||
|
||||
If you were using `github.com/luxfi/genesis` v1.2.x for building genesis bytes, migrate to this package:
|
||||
|
||||
| Old (genesis v1.2.x) | New (builder) |
|
||||
|---------------------|---------------|
|
||||
| `genesis.FromConfig(...)` | `builder.FromConfig(...)` |
|
||||
| `genesis.FromFile(...)` | `builder.FromFile(...)` |
|
||||
| `genesis.FromFlag(...)` | `builder.FromFlag(...)` |
|
||||
| `genesis.VMGenesis(...)` | `builder.VMGenesis(...)` |
|
||||
| `genesis.Aliases(...)` | `builder.Aliases(...)` |
|
||||
| `genesis.VMAliases` | `builder.VMAliases` |
|
||||
| `genesis.GetStakingConfig(...)` | `builder.GetStakingConfig(...)` |
|
||||
| `genesis.GetTxFeeConfig(...)` | `builder.GetTxFeeConfig(...)` |
|
||||
|
||||
For JSON-based configuration only (without building), continue using `github.com/luxfi/genesis` v1.3.x.
|
||||
|
||||
## License
|
||||
|
||||
Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
See the file LICENSE for licensing terms.
|
||||
@@ -0,0 +1,958 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
// Package builder provides genesis byte generation for Lux networks.
|
||||
// This package depends on node types and is responsible for building
|
||||
// the actual genesis state from genesis config.
|
||||
package builder
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/luxfi/formatting"
|
||||
"net/netip"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/luxfi/address"
|
||||
"github.com/luxfi/constants"
|
||||
"github.com/luxfi/container/sampler"
|
||||
"github.com/luxfi/ids"
|
||||
"github.com/luxfi/math/set"
|
||||
"github.com/luxfi/protocol/p/genesis"
|
||||
"github.com/luxfi/protocol/p/reward"
|
||||
"github.com/luxfi/protocol/p/signer"
|
||||
pchaintxs "github.com/luxfi/protocol/p/txs"
|
||||
"github.com/luxfi/protocol/p/validators/fee"
|
||||
"github.com/luxfi/protocol/x/fxs"
|
||||
xgenesis "github.com/luxfi/protocol/x/genesis"
|
||||
xchaintxs "github.com/luxfi/protocol/x/txs"
|
||||
"github.com/luxfi/utxo/nftfx"
|
||||
"github.com/luxfi/utxo/propertyfx"
|
||||
"github.com/luxfi/utxo/secp256k1fx"
|
||||
"github.com/luxfi/vm/components/gas"
|
||||
|
||||
genesiscfg "github.com/luxfi/genesis/pkg/genesis"
|
||||
)
|
||||
|
||||
var (
|
||||
// PChainAliases are the default aliases for the P-Chain
|
||||
PChainAliases = []string{"P", "platform"}
|
||||
// XChainAliases are the default aliases for the X-Chain
|
||||
XChainAliases = []string{"X", "xvm"}
|
||||
// CChainAliases are the default aliases for the C-Chain
|
||||
CChainAliases = []string{"C", "evm"}
|
||||
// DChainAliases are the default aliases for the D-Chain (DEX)
|
||||
DChainAliases = []string{"D", "dex", "dexvm"}
|
||||
// QChainAliases are the default aliases for the Q-Chain (Quantum)
|
||||
QChainAliases = []string{"Q", "quantum", "quantumvm"}
|
||||
// AChainAliases are the default aliases for the A-Chain (AI)
|
||||
AChainAliases = []string{"A", "ai", "aivm"}
|
||||
// BChainAliases are the default aliases for the B-Chain (Bridge)
|
||||
BChainAliases = []string{"B", "bridge", "bridgevm"}
|
||||
// TChainAliases are the default aliases for the T-Chain (Threshold)
|
||||
TChainAliases = []string{"T", "threshold", "thresholdvm"}
|
||||
// ZChainAliases are the default aliases for the Z-Chain (ZK)
|
||||
ZChainAliases = []string{"Z", "zk", "zkvm"}
|
||||
// GChainAliases are the default aliases for the G-Chain (Graph)
|
||||
GChainAliases = []string{"G", "graph", "graphvm"}
|
||||
// KChainAliases are the default aliases for the K-Chain (KMS)
|
||||
KChainAliases = []string{"K", "kms", "kmsvm"}
|
||||
|
||||
// DefaultChainGenesis provides empty but valid genesis for specialty chains
|
||||
DefaultChainGenesis = `{"version":1,"message":"Lux Chain Genesis"}`
|
||||
|
||||
// VMAliases are the default aliases for VMs
|
||||
VMAliases = map[ids.ID][]string{
|
||||
constants.PlatformVMID: {"platform"},
|
||||
constants.XVMID: {"xvm"},
|
||||
constants.EVMID: {"evm"},
|
||||
constants.DexVMID: {"dexvm", "dex"},
|
||||
constants.QuantumVMID: {"quantumvm", "quantum"},
|
||||
constants.AIVMID: {"aivm", "ai"},
|
||||
constants.BridgeVMID: {"bridgevm", "bridge"},
|
||||
constants.ThresholdVMID: {"thresholdvm", "threshold"},
|
||||
constants.ZKVMID: {"zkvm", "zk"},
|
||||
constants.GraphVMID: {"graphvm", "graph"},
|
||||
constants.KeyVMID: {"kmsvm", "kms"},
|
||||
secp256k1fx.ID: {"secp256k1fx"},
|
||||
nftfx.ID: {"nftfx"},
|
||||
propertyfx.ID: {"propertyfx"},
|
||||
}
|
||||
|
||||
errNoTxs = errors.New("genesis creates no transactions")
|
||||
errOverridesStandardNetworkConfig = errors.New("overrides standard network genesis config")
|
||||
)
|
||||
|
||||
// Bootstrapper represents a network bootstrap node with parsed types
|
||||
type Bootstrapper struct {
|
||||
ID ids.NodeID
|
||||
IP netip.AddrPort
|
||||
}
|
||||
|
||||
// ParseBootstrapper converts a genesis config bootstrapper to a parsed Bootstrapper
|
||||
func ParseBootstrapper(b genesiscfg.Bootstrapper) (Bootstrapper, error) {
|
||||
nodeID, err := ids.NodeIDFromString(b.ID)
|
||||
if err != nil {
|
||||
return Bootstrapper{}, fmt.Errorf("invalid bootstrapper ID %q: %w", b.ID, err)
|
||||
}
|
||||
ip, err := netip.ParseAddrPort(b.IP)
|
||||
if err != nil {
|
||||
return Bootstrapper{}, fmt.Errorf("invalid bootstrapper IP %q: %w", b.IP, err)
|
||||
}
|
||||
return Bootstrapper{ID: nodeID, IP: ip}, nil
|
||||
}
|
||||
|
||||
// parseProofOfPossession converts a genesis config ProofOfPossession (with hex strings)
|
||||
// to a node signer.ProofOfPossession (with byte arrays).
|
||||
func parseProofOfPossession(pop *genesiscfg.ProofOfPossession) (*signer.ProofOfPossession, error) {
|
||||
if pop == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Decode the public key from hex string (may have 0x prefix)
|
||||
pkBytes, err := formatting.Decode(formatting.HexNC, pop.PublicKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode publicKey: %w", err)
|
||||
}
|
||||
|
||||
// Decode the proof of possession from hex string (may have 0x prefix)
|
||||
popBytes, err := formatting.Decode(formatting.HexNC, pop.ProofOfPossession)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode proofOfPossession: %w", err)
|
||||
}
|
||||
|
||||
result := &signer.ProofOfPossession{}
|
||||
copy(result.PublicKey[:], pkBytes)
|
||||
copy(result.ProofOfPossession[:], popBytes)
|
||||
|
||||
// Verify the proof of possession is valid
|
||||
if err := result.Verify(); err != nil {
|
||||
return nil, fmt.Errorf("invalid proof of possession: %w", err)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetBootstrappers returns parsed bootstrappers for the network
|
||||
func GetBootstrappers(networkID uint32) ([]Bootstrapper, error) {
|
||||
cfgBootstrappers := genesiscfg.GetBootstrappers(networkID)
|
||||
result := make([]Bootstrapper, 0, len(cfgBootstrappers))
|
||||
for _, b := range cfgBootstrappers {
|
||||
parsed, err := ParseBootstrapper(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result = append(result, parsed)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// SampleBootstrappers returns a random sample of bootstrappers for the network
|
||||
func SampleBootstrappers(networkID uint32, count int) ([]Bootstrapper, error) {
|
||||
allBootstrappers, err := GetBootstrappers(networkID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
count = min(count, len(allBootstrappers))
|
||||
if count <= 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
s := sampler.NewUniform()
|
||||
s.Initialize(uint64(len(allBootstrappers)))
|
||||
indices, _ := s.Sample(count)
|
||||
|
||||
sampled := make([]Bootstrapper, 0, len(indices))
|
||||
for _, index := range indices {
|
||||
sampled = append(sampled, allBootstrappers[int(index)])
|
||||
}
|
||||
return sampled, nil
|
||||
}
|
||||
|
||||
// StakingConfig is the staking configuration with time.Duration types
|
||||
type StakingConfig struct {
|
||||
UptimeRequirement float64
|
||||
MinValidatorStake uint64
|
||||
MaxValidatorStake uint64
|
||||
MinDelegatorStake uint64
|
||||
MinDelegationFee uint32
|
||||
MinStakeDuration time.Duration
|
||||
MaxStakeDuration time.Duration
|
||||
RewardConfig reward.Config
|
||||
|
||||
// BLS key information for genesis replay
|
||||
NodeID string `json:"nodeID"`
|
||||
BLSPublicKey []byte `json:"blsPublicKey"`
|
||||
BLSProofOfPossession []byte `json:"blsProofOfPossession"`
|
||||
}
|
||||
|
||||
// GetStakingConfig returns the staking config with time.Duration types
|
||||
func GetStakingConfig(networkID uint32) StakingConfig {
|
||||
cfg := genesiscfg.GetStakingConfig(networkID)
|
||||
return StakingConfig{
|
||||
UptimeRequirement: cfg.UptimeRequirement,
|
||||
MinValidatorStake: cfg.MinValidatorStake,
|
||||
MaxValidatorStake: cfg.MaxValidatorStake,
|
||||
MinDelegatorStake: cfg.MinDelegatorStake,
|
||||
MinDelegationFee: cfg.MinDelegationFee,
|
||||
MinStakeDuration: time.Duration(cfg.MinStakeDuration) * time.Second,
|
||||
MaxStakeDuration: time.Duration(cfg.MaxStakeDuration) * time.Second,
|
||||
RewardConfig: reward.Config{
|
||||
MaxConsumptionRate: cfg.RewardConfig.MaxConsumptionRate,
|
||||
MinConsumptionRate: cfg.RewardConfig.MinConsumptionRate,
|
||||
MintingPeriod: time.Duration(cfg.RewardConfig.MintingPeriod) * time.Second,
|
||||
SupplyCap: cfg.RewardConfig.SupplyCap,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// TxFeeConfig contains transaction fee configuration
|
||||
// This includes the basic fee config from genesis plus dynamic/validator fees
|
||||
type TxFeeConfig struct {
|
||||
TxFee uint64 `json:"txFee"`
|
||||
CreateAssetTxFee uint64 `json:"createAssetTxFee"`
|
||||
DynamicFeeConfig gas.Config `json:"dynamicFeeConfig"`
|
||||
ValidatorFeeConfig fee.Config `json:"validatorFeeConfig"`
|
||||
}
|
||||
|
||||
// Default dynamic fee parameters
|
||||
var (
|
||||
MainnetDynamicFeeConfig = gas.Config{
|
||||
Weights: gas.Dimensions{
|
||||
gas.Bandwidth: 1,
|
||||
gas.DBRead: 1,
|
||||
gas.DBWrite: 1,
|
||||
gas.Compute: 1,
|
||||
},
|
||||
MaxCapacity: 1_000_000,
|
||||
MaxPerSecond: 100_000,
|
||||
TargetPerSecond: 50_000,
|
||||
MinPrice: 1,
|
||||
ExcessConversionConstant: 5_000,
|
||||
}
|
||||
|
||||
TestnetDynamicFeeConfig = gas.Config{
|
||||
Weights: gas.Dimensions{
|
||||
gas.Bandwidth: 1,
|
||||
gas.DBRead: 1,
|
||||
gas.DBWrite: 1,
|
||||
gas.Compute: 1,
|
||||
},
|
||||
MaxCapacity: 1_000_000,
|
||||
MaxPerSecond: 100_000,
|
||||
TargetPerSecond: 50_000,
|
||||
MinPrice: 1,
|
||||
ExcessConversionConstant: 5_000,
|
||||
}
|
||||
|
||||
LocalDynamicFeeConfig = gas.Config{
|
||||
Weights: gas.Dimensions{
|
||||
gas.Bandwidth: 1,
|
||||
gas.DBRead: 1,
|
||||
gas.DBWrite: 1,
|
||||
gas.Compute: 1,
|
||||
},
|
||||
MaxCapacity: 1_000_000,
|
||||
MaxPerSecond: 100_000,
|
||||
TargetPerSecond: 50_000,
|
||||
MinPrice: 1,
|
||||
ExcessConversionConstant: 5_000,
|
||||
}
|
||||
|
||||
MainnetValidatorFeeConfig = fee.Config{
|
||||
Capacity: 20_000,
|
||||
Target: 10_000,
|
||||
MinPrice: 512,
|
||||
ExcessConversionConstant: 1_587,
|
||||
}
|
||||
|
||||
TestnetValidatorFeeConfig = fee.Config{
|
||||
Capacity: 20_000,
|
||||
Target: 10_000,
|
||||
MinPrice: 512,
|
||||
ExcessConversionConstant: 1_587,
|
||||
}
|
||||
|
||||
LocalValidatorFeeConfig = fee.Config{
|
||||
Capacity: 20_000,
|
||||
Target: 10_000,
|
||||
MinPrice: 512,
|
||||
ExcessConversionConstant: 1_587,
|
||||
}
|
||||
)
|
||||
|
||||
// GetTxFeeConfig returns the tx fee config
|
||||
func GetTxFeeConfig(networkID uint32) TxFeeConfig {
|
||||
cfg := genesiscfg.GetTxFeeConfig(networkID)
|
||||
|
||||
var dynamicCfg gas.Config
|
||||
var validatorCfg fee.Config
|
||||
|
||||
switch networkID {
|
||||
case constants.MainnetID:
|
||||
dynamicCfg = MainnetDynamicFeeConfig
|
||||
validatorCfg = MainnetValidatorFeeConfig
|
||||
case constants.TestnetID:
|
||||
dynamicCfg = TestnetDynamicFeeConfig
|
||||
validatorCfg = TestnetValidatorFeeConfig
|
||||
default:
|
||||
dynamicCfg = LocalDynamicFeeConfig
|
||||
validatorCfg = LocalValidatorFeeConfig
|
||||
}
|
||||
|
||||
return TxFeeConfig{
|
||||
TxFee: cfg.TxFee,
|
||||
CreateAssetTxFee: cfg.CreateAssetTxFee,
|
||||
DynamicFeeConfig: dynamicCfg,
|
||||
ValidatorFeeConfig: validatorCfg,
|
||||
}
|
||||
}
|
||||
|
||||
// GetConfig returns the genesis config for the given network ID
|
||||
func GetConfig(networkID uint32) *genesiscfg.Config {
|
||||
return genesiscfg.GetConfig(networkID)
|
||||
}
|
||||
|
||||
// FromConfig builds genesis bytes from a config
|
||||
func FromConfig(config *genesiscfg.Config) ([]byte, ids.ID, error) {
|
||||
// Build XVM (X-Chain) genesis
|
||||
lux := xgenesis.GenesisAssetDefinition{
|
||||
Name: "Lux",
|
||||
Symbol: "LUX",
|
||||
Denomination: 9,
|
||||
InitialState: xgenesis.AssetInitialState{},
|
||||
}
|
||||
memoBytes := []byte{}
|
||||
|
||||
// Sort allocations for deterministic output
|
||||
type allocation struct {
|
||||
ETHAddr ids.ShortID
|
||||
LUXAddr ids.ShortID
|
||||
InitialAmount uint64
|
||||
}
|
||||
xAllocations := []allocation{}
|
||||
for _, a := range config.Allocations {
|
||||
if a.InitialAmount > 0 {
|
||||
xAllocations = append(xAllocations, allocation{
|
||||
ETHAddr: a.ETHAddr,
|
||||
LUXAddr: a.LUXAddr,
|
||||
InitialAmount: a.InitialAmount,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Get HRP for this network to format bech32 addresses
|
||||
hrp := constants.GetHRP(config.NetworkID)
|
||||
|
||||
for _, a := range xAllocations {
|
||||
// Format address as bech32 for the X-Chain
|
||||
bech32Addr, err := address.FormatBech32(hrp, a.LUXAddr[:])
|
||||
if err != nil {
|
||||
return nil, ids.Empty, fmt.Errorf("failed to format bech32 address: %w", err)
|
||||
}
|
||||
lux.InitialState.FixedCap = append(lux.InitialState.FixedCap, xgenesis.GenesisHolder{
|
||||
Amount: a.InitialAmount,
|
||||
Address: bech32Addr,
|
||||
})
|
||||
// Add ETH address to memo for reference
|
||||
ethAddrStr := a.ETHAddr.Hex()
|
||||
if len(ethAddrStr) > 2 { // "0x" prefix
|
||||
memoBytes = append(memoBytes, []byte(ethAddrStr[2:])...)
|
||||
}
|
||||
}
|
||||
lux.Memo = memoBytes
|
||||
|
||||
xvmGenesis, err := xgenesis.NewGenesis(
|
||||
config.NetworkID,
|
||||
map[string]xgenesis.GenesisAssetDefinition{
|
||||
"LUX": lux,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, ids.Empty, err
|
||||
}
|
||||
xvmGenesisBytes, err := xvmGenesis.Bytes()
|
||||
if err != nil {
|
||||
return nil, ids.Empty, fmt.Errorf("couldn't serialize xvm genesis: %w", err)
|
||||
}
|
||||
|
||||
luxAssetID, err := XAssetID(xvmGenesisBytes)
|
||||
if err != nil {
|
||||
return nil, ids.Empty, fmt.Errorf("couldn't generate LUX asset ID: %w", err)
|
||||
}
|
||||
|
||||
genesisTime := time.Unix(int64(config.StartTime), 0)
|
||||
|
||||
// Calculate initial supply
|
||||
initialSupply := uint64(0)
|
||||
for _, a := range config.Allocations {
|
||||
initialSupply += a.InitialAmount
|
||||
for _, unlock := range a.UnlockSchedule {
|
||||
initialSupply += unlock.Amount
|
||||
}
|
||||
}
|
||||
|
||||
// Build platform allocations
|
||||
initiallyStaked := set.Set[ids.ShortID]{}
|
||||
for _, addr := range config.InitialStakedFunds {
|
||||
initiallyStaked.Add(addr)
|
||||
}
|
||||
|
||||
platformAllocations := []genesis.Allocation{}
|
||||
skippedAllocations := []genesiscfg.Allocation{}
|
||||
for _, a := range config.Allocations {
|
||||
if initiallyStaked.Contains(a.LUXAddr) {
|
||||
skippedAllocations = append(skippedAllocations, a)
|
||||
continue
|
||||
}
|
||||
for _, unlock := range a.UnlockSchedule {
|
||||
if unlock.Amount > 0 {
|
||||
// Format address as bech32 for the P-Chain
|
||||
bech32Addr, err := address.FormatBech32(hrp, a.LUXAddr[:])
|
||||
if err != nil {
|
||||
return nil, ids.Empty, fmt.Errorf("failed to format bech32 address for P-chain: %w", err)
|
||||
}
|
||||
platformAllocations = append(platformAllocations, genesis.Allocation{
|
||||
Locktime: unlock.Locktime,
|
||||
Amount: unlock.Amount,
|
||||
Address: bech32Addr,
|
||||
Message: a.ETHAddr.Bytes(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build validators
|
||||
validators := []genesis.PermissionlessValidator{}
|
||||
allNodeAllocations := splitAllocations(skippedAllocations, len(config.InitialStakers))
|
||||
endStakingTime := genesisTime.Add(time.Duration(config.InitialStakeDuration) * time.Second)
|
||||
stakingOffset := time.Duration(0)
|
||||
|
||||
for i, staker := range config.InitialStakers {
|
||||
// Safely get node allocations (may be empty if no initialStakedFunds)
|
||||
var nodeAllocations []genesiscfg.Allocation
|
||||
if i < len(allNodeAllocations) {
|
||||
nodeAllocations = allNodeAllocations[i]
|
||||
}
|
||||
|
||||
// Use explicit staker times if provided, otherwise use calculated values
|
||||
startTime := uint64(genesisTime.Unix())
|
||||
if staker.StartTime > 0 {
|
||||
startTime = staker.StartTime
|
||||
}
|
||||
|
||||
endTime := endStakingTime.Add(-stakingOffset)
|
||||
stakingOffset += time.Duration(config.InitialStakeDurationOffset) * time.Second
|
||||
endTimeUnix := uint64(endTime.Unix())
|
||||
if staker.EndTime > 0 {
|
||||
endTimeUnix = staker.EndTime
|
||||
}
|
||||
|
||||
allocations := []genesis.Allocation{}
|
||||
for _, a := range nodeAllocations {
|
||||
for _, unlock := range a.UnlockSchedule {
|
||||
// Format address as bech32 for staker allocations
|
||||
bech32Addr, err := address.FormatBech32(hrp, a.LUXAddr[:])
|
||||
if err != nil {
|
||||
return nil, ids.Empty, fmt.Errorf("failed to format bech32 address for staker allocation: %w", err)
|
||||
}
|
||||
allocations = append(allocations, genesis.Allocation{
|
||||
Locktime: unlock.Locktime,
|
||||
Amount: unlock.Amount,
|
||||
Address: bech32Addr,
|
||||
Message: a.ETHAddr.Bytes(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate weight from allocations or use explicit weight
|
||||
weight := staker.Weight
|
||||
if weight == 0 {
|
||||
for _, a := range allocations {
|
||||
weight += a.Amount
|
||||
}
|
||||
}
|
||||
|
||||
// Format reward address as bech32
|
||||
rewardBech32, err := address.FormatBech32(hrp, staker.RewardAddress[:])
|
||||
if err != nil {
|
||||
return nil, ids.Empty, fmt.Errorf("failed to format bech32 reward address: %w", err)
|
||||
}
|
||||
|
||||
// Parse the BLS proof of possession if present
|
||||
var blsSigner *signer.ProofOfPossession
|
||||
if staker.Signer != nil {
|
||||
blsSigner, err = parseProofOfPossession(staker.Signer)
|
||||
if err != nil {
|
||||
return nil, ids.Empty, fmt.Errorf("failed to parse proof of possession for staker %d: %w", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
validators = append(validators, genesis.PermissionlessValidator{
|
||||
Validator: genesis.Validator{
|
||||
StartTime: startTime,
|
||||
EndTime: endTimeUnix,
|
||||
Weight: weight,
|
||||
NodeID: staker.NodeID,
|
||||
},
|
||||
RewardOwner: &genesis.Owner{
|
||||
Threshold: 1,
|
||||
Addresses: []string{rewardBech32},
|
||||
},
|
||||
Staked: allocations,
|
||||
ExactDelegationFee: staker.DelegationFee,
|
||||
Signer: blsSigner,
|
||||
})
|
||||
}
|
||||
|
||||
// Helper to get genesis data or default
|
||||
getGenesis := func(data string) []byte {
|
||||
if data == "" {
|
||||
return []byte(DefaultChainGenesis)
|
||||
}
|
||||
return []byte(data)
|
||||
}
|
||||
|
||||
// Specify all 11 chains
|
||||
chains := []genesis.Chain{
|
||||
{
|
||||
GenesisData: xvmGenesisBytes,
|
||||
ChainID: constants.PrimaryNetworkID,
|
||||
VMID: constants.XVMID,
|
||||
FxIDs: []ids.ID{
|
||||
secp256k1fx.ID,
|
||||
nftfx.ID,
|
||||
propertyfx.ID,
|
||||
},
|
||||
Name: "X-Chain",
|
||||
},
|
||||
{
|
||||
GenesisData: []byte(config.CChainGenesis),
|
||||
ChainID: constants.PrimaryNetworkID,
|
||||
VMID: constants.EVMID,
|
||||
Name: "C-Chain",
|
||||
},
|
||||
{
|
||||
GenesisData: getGenesis(config.DChainGenesis),
|
||||
ChainID: constants.PrimaryNetworkID,
|
||||
VMID: constants.DexVMID,
|
||||
Name: "D-Chain",
|
||||
},
|
||||
{
|
||||
GenesisData: getGenesis(config.QChainGenesis),
|
||||
ChainID: constants.PrimaryNetworkID,
|
||||
VMID: constants.QuantumVMID,
|
||||
Name: "Q-Chain",
|
||||
},
|
||||
{
|
||||
GenesisData: getGenesis(config.AChainGenesis),
|
||||
ChainID: constants.PrimaryNetworkID,
|
||||
VMID: constants.AIVMID,
|
||||
Name: "A-Chain",
|
||||
},
|
||||
{
|
||||
GenesisData: getGenesis(config.BChainGenesis),
|
||||
ChainID: constants.PrimaryNetworkID,
|
||||
VMID: constants.BridgeVMID,
|
||||
Name: "B-Chain",
|
||||
},
|
||||
{
|
||||
GenesisData: getGenesis(config.TChainGenesis),
|
||||
ChainID: constants.PrimaryNetworkID,
|
||||
VMID: constants.ThresholdVMID,
|
||||
Name: "T-Chain",
|
||||
},
|
||||
{
|
||||
GenesisData: getGenesis(config.ZChainGenesis),
|
||||
ChainID: constants.PrimaryNetworkID,
|
||||
VMID: constants.ZKVMID,
|
||||
Name: "Z-Chain",
|
||||
},
|
||||
{
|
||||
GenesisData: getGenesis(config.GChainGenesis),
|
||||
ChainID: constants.PrimaryNetworkID,
|
||||
VMID: constants.GraphVMID,
|
||||
Name: "G-Chain",
|
||||
},
|
||||
{
|
||||
GenesisData: getGenesis(config.KChainGenesis),
|
||||
ChainID: constants.PrimaryNetworkID,
|
||||
VMID: constants.KeyVMID,
|
||||
Name: "K-Chain",
|
||||
},
|
||||
}
|
||||
|
||||
pChainGenesis, err := genesis.New(
|
||||
luxAssetID,
|
||||
config.NetworkID,
|
||||
platformAllocations,
|
||||
validators,
|
||||
chains,
|
||||
config.StartTime,
|
||||
initialSupply,
|
||||
config.Message,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, ids.Empty, fmt.Errorf("problem while building platform chain's genesis state: %w", err)
|
||||
}
|
||||
pChainGenesisBytes, err := pChainGenesis.Bytes()
|
||||
if err != nil {
|
||||
return nil, ids.Empty, fmt.Errorf("problem while serializing platform chain's genesis state: %w", err)
|
||||
}
|
||||
return pChainGenesisBytes, luxAssetID, nil
|
||||
}
|
||||
|
||||
// FromFile loads genesis config from file and builds genesis bytes
|
||||
func FromFile(networkID uint32, filepath string, stakingCfg *StakingConfig, allowCustomGenesis bool) ([]byte, ids.ID, error) {
|
||||
// Protect standard networks from custom genesis unless explicitly allowed
|
||||
if !allowCustomGenesis {
|
||||
switch networkID {
|
||||
case constants.MainnetID, constants.TestnetID:
|
||||
return nil, ids.Empty, fmt.Errorf(
|
||||
"%w: %s",
|
||||
errOverridesStandardNetworkConfig,
|
||||
constants.NetworkName(networkID),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
config, err := genesiscfg.GetConfigFile(filepath)
|
||||
if err != nil {
|
||||
return nil, ids.Empty, fmt.Errorf("failed to load genesis file %s: %w", filepath, err)
|
||||
}
|
||||
if err := validateConfig(networkID, config, stakingCfg); err != nil {
|
||||
return nil, ids.Empty, fmt.Errorf("genesis config validation failed: %w", err)
|
||||
}
|
||||
return FromConfig(config)
|
||||
}
|
||||
|
||||
// FromFlag parses base64-encoded genesis content and builds genesis bytes
|
||||
func FromFlag(networkID uint32, genesisContent string, stakingCfg *StakingConfig, allowCustomGenesis bool) ([]byte, ids.ID, error) {
|
||||
// Protect standard networks from custom genesis unless explicitly allowed
|
||||
if !allowCustomGenesis {
|
||||
switch networkID {
|
||||
case constants.MainnetID, constants.TestnetID:
|
||||
return nil, ids.Empty, fmt.Errorf(
|
||||
"%w: %s",
|
||||
errOverridesStandardNetworkConfig,
|
||||
constants.NetworkName(networkID),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
data, err := base64.StdEncoding.DecodeString(genesisContent)
|
||||
if err != nil {
|
||||
return nil, ids.Empty, fmt.Errorf("failed to decode base64 genesis content: %w", err)
|
||||
}
|
||||
var config genesiscfg.Config
|
||||
if err := json.Unmarshal(data, &config); err != nil {
|
||||
return nil, ids.Empty, fmt.Errorf("failed to parse genesis config: %w", err)
|
||||
}
|
||||
if err := validateConfig(networkID, &config, stakingCfg); err != nil {
|
||||
return nil, ids.Empty, fmt.Errorf("genesis config validation failed: %w", err)
|
||||
}
|
||||
return FromConfig(&config)
|
||||
}
|
||||
|
||||
// FromDatabase returns genesis data for database replay mode
|
||||
func FromDatabase(networkID uint32, dbPath string, dbType string, stakingCfg *StakingConfig) ([]byte, ids.ID, error) {
|
||||
config := genesiscfg.GetConfig(constants.CustomID)
|
||||
config.NetworkID = networkID
|
||||
config.Message = "DATABASE_REPLAY_MODE"
|
||||
return FromConfig(config)
|
||||
}
|
||||
|
||||
// VMGenesis returns the genesis tx for a specific VM
|
||||
func VMGenesis(genesisBytes []byte, vmID ids.ID) (*pchaintxs.Tx, error) {
|
||||
gen, err := genesis.Parse(genesisBytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse genesis: %w", err)
|
||||
}
|
||||
for _, chain := range gen.Chains {
|
||||
uChain := chain.Unsigned.(*pchaintxs.CreateChainTx)
|
||||
if uChain.VMID == vmID {
|
||||
return chain, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("couldn't find blockchain with VM ID %s", vmID)
|
||||
}
|
||||
|
||||
// Aliases returns the default aliases for chains and APIs
|
||||
func Aliases(genesisBytes []byte) (map[string][]string, map[ids.ID][]string, error) {
|
||||
apiAliases := map[string][]string{
|
||||
path.Join(constants.ChainAliasPrefix, constants.PlatformChainID.String()): {
|
||||
"P",
|
||||
"platform",
|
||||
path.Join(constants.ChainAliasPrefix, "P"),
|
||||
path.Join(constants.ChainAliasPrefix, "platform"),
|
||||
},
|
||||
}
|
||||
chainAliases := map[ids.ID][]string{
|
||||
constants.PlatformChainID: PChainAliases,
|
||||
}
|
||||
|
||||
gen, err := genesis.Parse(genesisBytes)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
for _, chain := range gen.Chains {
|
||||
uChain := chain.Unsigned.(*pchaintxs.CreateChainTx)
|
||||
chainID := chain.ID()
|
||||
endpoint := path.Join(constants.ChainAliasPrefix, chainID.String())
|
||||
switch uChain.VMID {
|
||||
case constants.XVMID:
|
||||
apiAliases[endpoint] = []string{
|
||||
"X", "xvm",
|
||||
path.Join(constants.ChainAliasPrefix, "X"),
|
||||
path.Join(constants.ChainAliasPrefix, "xvm"),
|
||||
}
|
||||
chainAliases[chainID] = XChainAliases
|
||||
case constants.EVMID:
|
||||
apiAliases[endpoint] = []string{
|
||||
"C", "evm",
|
||||
path.Join(constants.ChainAliasPrefix, "C"),
|
||||
path.Join(constants.ChainAliasPrefix, "evm"),
|
||||
}
|
||||
chainAliases[chainID] = CChainAliases
|
||||
case constants.DexVMID:
|
||||
apiAliases[endpoint] = []string{
|
||||
"D", "dex", "dexvm",
|
||||
path.Join(constants.ChainAliasPrefix, "D"),
|
||||
path.Join(constants.ChainAliasPrefix, "dex"),
|
||||
path.Join(constants.ChainAliasPrefix, "dexvm"),
|
||||
}
|
||||
chainAliases[chainID] = DChainAliases
|
||||
case constants.QuantumVMID:
|
||||
apiAliases[endpoint] = []string{
|
||||
"Q", "quantum", "quantumvm",
|
||||
path.Join(constants.ChainAliasPrefix, "Q"),
|
||||
path.Join(constants.ChainAliasPrefix, "quantum"),
|
||||
}
|
||||
chainAliases[chainID] = QChainAliases
|
||||
case constants.AIVMID:
|
||||
apiAliases[endpoint] = []string{
|
||||
"A", "ai", "aivm",
|
||||
path.Join(constants.ChainAliasPrefix, "A"),
|
||||
path.Join(constants.ChainAliasPrefix, "ai"),
|
||||
}
|
||||
chainAliases[chainID] = AChainAliases
|
||||
case constants.BridgeVMID:
|
||||
apiAliases[endpoint] = []string{
|
||||
"B", "bridge", "bridgevm",
|
||||
path.Join(constants.ChainAliasPrefix, "B"),
|
||||
path.Join(constants.ChainAliasPrefix, "bridge"),
|
||||
}
|
||||
chainAliases[chainID] = BChainAliases
|
||||
case constants.ThresholdVMID:
|
||||
apiAliases[endpoint] = []string{
|
||||
"T", "threshold", "thresholdvm",
|
||||
path.Join(constants.ChainAliasPrefix, "T"),
|
||||
path.Join(constants.ChainAliasPrefix, "threshold"),
|
||||
}
|
||||
chainAliases[chainID] = TChainAliases
|
||||
case constants.ZKVMID:
|
||||
apiAliases[endpoint] = []string{
|
||||
"Z", "zk", "zkvm",
|
||||
path.Join(constants.ChainAliasPrefix, "Z"),
|
||||
path.Join(constants.ChainAliasPrefix, "zk"),
|
||||
}
|
||||
chainAliases[chainID] = ZChainAliases
|
||||
case constants.GraphVMID:
|
||||
apiAliases[endpoint] = []string{
|
||||
"G", "graph", "graphvm",
|
||||
path.Join(constants.ChainAliasPrefix, "G"),
|
||||
path.Join(constants.ChainAliasPrefix, "graph"),
|
||||
}
|
||||
chainAliases[chainID] = GChainAliases
|
||||
case constants.KeyVMID:
|
||||
apiAliases[endpoint] = []string{
|
||||
"K", "kms", "kmsvm",
|
||||
path.Join(constants.ChainAliasPrefix, "K"),
|
||||
path.Join(constants.ChainAliasPrefix, "kms"),
|
||||
}
|
||||
chainAliases[chainID] = KChainAliases
|
||||
}
|
||||
}
|
||||
return apiAliases, chainAliases, nil
|
||||
}
|
||||
|
||||
// XAssetID returns the LUX asset ID from XVM genesis bytes
|
||||
func XAssetID(xvmGenesisBytes []byte) (ids.ID, error) {
|
||||
parser, err := xchaintxs.NewParser(
|
||||
[]fxs.Fx{
|
||||
&secp256k1fx.Fx{},
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return ids.Empty, err
|
||||
}
|
||||
|
||||
genesisCodec := parser.GenesisCodec()
|
||||
gen := xgenesis.Genesis{}
|
||||
if _, err := genesisCodec.Unmarshal(xvmGenesisBytes, &gen); err != nil {
|
||||
return ids.Empty, err
|
||||
}
|
||||
|
||||
if len(gen.Txs) == 0 {
|
||||
return ids.Empty, errNoTxs
|
||||
}
|
||||
genesisTx := gen.Txs[0]
|
||||
|
||||
tx := xchaintxs.Tx{Unsigned: &genesisTx.CreateAssetTx}
|
||||
if err := tx.Initialize(genesisCodec); err != nil {
|
||||
return ids.Empty, err
|
||||
}
|
||||
return tx.ID(), nil
|
||||
}
|
||||
|
||||
// validateConfig validates the genesis config
|
||||
func validateConfig(networkID uint32, config *genesiscfg.Config, stakingCfg *StakingConfig) error {
|
||||
if config.NetworkID != networkID {
|
||||
return fmt.Errorf("network ID mismatch: expected %d, got %d", networkID, config.NetworkID)
|
||||
}
|
||||
if config.InitialStakeDuration > uint64(stakingCfg.MaxStakeDuration.Seconds()) {
|
||||
return fmt.Errorf("initial stake duration %d exceeds max %d", config.InitialStakeDuration, uint64(stakingCfg.MaxStakeDuration.Seconds()))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DevModeConfig holds configuration for dev mode genesis
|
||||
type DevModeConfig struct {
|
||||
NodeID ids.NodeID // The validator node ID
|
||||
BLSPublicKey string // BLS public key hex
|
||||
BLSPopProof string // BLS proof of possession hex
|
||||
RewardAddress ids.ShortID // Reward/allocation address
|
||||
CChainGenesis string // C-Chain genesis JSON
|
||||
StartTime uint64 // Genesis start time (if 0, uses time.Now())
|
||||
}
|
||||
|
||||
// ForDevMode creates a genesis configuration suitable for single-node development mode.
|
||||
// It creates a single validator with far-future stake time and funds the treasury address.
|
||||
func ForDevMode(cfg DevModeConfig, stakingCfg *StakingConfig) ([]byte, ids.ID, error) {
|
||||
// Genesis start time: use provided time or fall back to now
|
||||
startTime := cfg.StartTime
|
||||
if startTime == 0 {
|
||||
startTime = uint64(time.Now().Unix())
|
||||
}
|
||||
|
||||
// Far-future stake duration: 100 years in seconds
|
||||
// This ensures the validator never expires during development
|
||||
const hundredYears = 100 * 365 * 24 * 60 * 60
|
||||
|
||||
// Create allocation for the reward address
|
||||
// Initial staked amount: 1B LUX (enough to be a validator)
|
||||
const oneMillionLUX = 1_000_000_000_000_000 // 1M LUX in nLUX
|
||||
const oneBillionLUX = 1_000_000_000_000_000_000 // 1B LUX in nLUX
|
||||
|
||||
allocation := genesiscfg.Allocation{
|
||||
ETHAddr: cfg.RewardAddress, // Same as LUX addr for simplicity
|
||||
LUXAddr: cfg.RewardAddress,
|
||||
InitialAmount: oneMillionLUX, // Initial unlocked amount
|
||||
UnlockSchedule: []genesiscfg.LockedAmount{
|
||||
{
|
||||
Amount: oneBillionLUX, // Staked amount
|
||||
Locktime: 0, // No lock time
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Create the single staker
|
||||
var signer *genesiscfg.ProofOfPossession
|
||||
if cfg.BLSPublicKey != "" && cfg.BLSPopProof != "" {
|
||||
signer = &genesiscfg.ProofOfPossession{
|
||||
PublicKey: cfg.BLSPublicKey,
|
||||
ProofOfPossession: cfg.BLSPopProof,
|
||||
}
|
||||
}
|
||||
|
||||
staker := genesiscfg.Staker{
|
||||
NodeID: cfg.NodeID,
|
||||
RewardAddress: cfg.RewardAddress,
|
||||
DelegationFee: 1000000, // 100% delegation fee (no delegators in dev mode)
|
||||
Signer: signer,
|
||||
Weight: oneBillionLUX,
|
||||
StartTime: startTime,
|
||||
EndTime: startTime + hundredYears,
|
||||
}
|
||||
|
||||
// Build the genesis config
|
||||
config := &genesiscfg.Config{
|
||||
NetworkID: constants.CustomID,
|
||||
Allocations: []genesiscfg.Allocation{allocation},
|
||||
StartTime: startTime,
|
||||
InitialStakeDuration: hundredYears,
|
||||
InitialStakeDurationOffset: 0,
|
||||
InitialStakedFunds: []ids.ShortID{cfg.RewardAddress},
|
||||
InitialStakers: []genesiscfg.Staker{staker},
|
||||
CChainGenesis: cfg.CChainGenesis,
|
||||
Message: "Lux Development Mode Genesis",
|
||||
}
|
||||
|
||||
return FromConfig(config)
|
||||
}
|
||||
|
||||
// splitAllocations splits allocations across multiple stakers
|
||||
func splitAllocations(allocations []genesiscfg.Allocation, numSplits int) [][]genesiscfg.Allocation {
|
||||
if numSplits == 0 {
|
||||
return [][]genesiscfg.Allocation{}
|
||||
}
|
||||
|
||||
totalAmount := uint64(0)
|
||||
for _, a := range allocations {
|
||||
for _, unlock := range a.UnlockSchedule {
|
||||
totalAmount += unlock.Amount
|
||||
}
|
||||
}
|
||||
|
||||
nodeWeight := totalAmount / uint64(numSplits)
|
||||
allNodeAllocations := make([][]genesiscfg.Allocation, 0, numSplits)
|
||||
|
||||
currentNodeAllocation := []genesiscfg.Allocation{}
|
||||
currentNodeAmount := uint64(0)
|
||||
|
||||
for _, allocation := range allocations {
|
||||
currentAllocation := allocation
|
||||
currentAllocation.InitialAmount = 0
|
||||
currentAllocation.UnlockSchedule = nil
|
||||
|
||||
for _, unlock := range allocation.UnlockSchedule {
|
||||
for currentNodeAmount+unlock.Amount > nodeWeight && len(allNodeAllocations) < numSplits-1 {
|
||||
amountToAdd := nodeWeight - currentNodeAmount
|
||||
currentAllocation.UnlockSchedule = append(currentAllocation.UnlockSchedule, genesiscfg.LockedAmount{
|
||||
Amount: amountToAdd,
|
||||
Locktime: unlock.Locktime,
|
||||
})
|
||||
unlock.Amount -= amountToAdd
|
||||
|
||||
currentNodeAllocation = append(currentNodeAllocation, currentAllocation)
|
||||
allNodeAllocations = append(allNodeAllocations, currentNodeAllocation)
|
||||
|
||||
currentNodeAllocation = nil
|
||||
currentNodeAmount = 0
|
||||
|
||||
currentAllocation = allocation
|
||||
currentAllocation.InitialAmount = 0
|
||||
currentAllocation.UnlockSchedule = nil
|
||||
}
|
||||
|
||||
if unlock.Amount == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
currentAllocation.UnlockSchedule = append(currentAllocation.UnlockSchedule, genesiscfg.LockedAmount{
|
||||
Amount: unlock.Amount,
|
||||
Locktime: unlock.Locktime,
|
||||
})
|
||||
currentNodeAmount += unlock.Amount
|
||||
}
|
||||
|
||||
if len(currentAllocation.UnlockSchedule) > 0 {
|
||||
currentNodeAllocation = append(currentNodeAllocation, currentAllocation)
|
||||
}
|
||||
}
|
||||
|
||||
return append(allNodeAllocations, currentNodeAllocation)
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package builder
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/luxfi/constants"
|
||||
)
|
||||
|
||||
func TestGetStakingConfig(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
networkID uint32
|
||||
}{
|
||||
{"Mainnet", constants.MainnetID},
|
||||
{"Testnet", constants.TestnetID},
|
||||
{"CustomID", constants.CustomID},
|
||||
{"Custom", 12345},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cfg := GetStakingConfig(tt.networkID)
|
||||
|
||||
// Verify basic constraints
|
||||
require.GreaterOrEqual(t, cfg.UptimeRequirement, 0.0)
|
||||
require.LessOrEqual(t, cfg.UptimeRequirement, 1.0)
|
||||
require.Greater(t, cfg.MinValidatorStake, uint64(0))
|
||||
require.GreaterOrEqual(t, cfg.MaxValidatorStake, cfg.MinValidatorStake)
|
||||
require.Greater(t, cfg.MinDelegatorStake, uint64(0))
|
||||
require.Greater(t, cfg.MinStakeDuration, time.Duration(0))
|
||||
require.GreaterOrEqual(t, cfg.MaxStakeDuration, cfg.MinStakeDuration)
|
||||
|
||||
// RewardConfig is populated by builder with node-specific types
|
||||
// The genesis package only provides the base staking parameters
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTxFeeConfig(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
networkID uint32
|
||||
}{
|
||||
{"Mainnet", constants.MainnetID},
|
||||
{"Testnet", constants.TestnetID},
|
||||
{"CustomID", constants.CustomID},
|
||||
{"Custom", 12345},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cfg := GetTxFeeConfig(tt.networkID)
|
||||
|
||||
// Verify basic fee config
|
||||
require.Greater(t, cfg.TxFee, uint64(0))
|
||||
require.Greater(t, cfg.CreateAssetTxFee, uint64(0))
|
||||
|
||||
// Verify dynamic fee config
|
||||
require.Greater(t, uint64(cfg.DynamicFeeConfig.MaxCapacity), uint64(0))
|
||||
require.Greater(t, uint64(cfg.DynamicFeeConfig.MaxPerSecond), uint64(0))
|
||||
|
||||
// Verify validator fee config
|
||||
require.Greater(t, uint64(cfg.ValidatorFeeConfig.Capacity), uint64(0))
|
||||
require.Greater(t, uint64(cfg.ValidatorFeeConfig.Target), uint64(0))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetBootstrappers(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
networkID uint32
|
||||
}{
|
||||
{"Mainnet", constants.MainnetID},
|
||||
{"Testnet", constants.TestnetID},
|
||||
{"CustomID", constants.CustomID},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
bootstrappers, err := GetBootstrappers(tt.networkID)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Bootstrappers may not be configured for all networks
|
||||
// This is acceptable - they can be provided via config
|
||||
|
||||
// Verify each bootstrapper has valid ID and IP
|
||||
for _, b := range bootstrappers {
|
||||
require.NotEqual(t, b.ID.String(), "")
|
||||
require.True(t, b.IP.IsValid())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSampleBootstrappers(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
networkID uint32
|
||||
count int
|
||||
}{
|
||||
{"Mainnet_5", constants.MainnetID, 5},
|
||||
{"Mainnet_10", constants.MainnetID, 10},
|
||||
{"Testnet_3", constants.TestnetID, 3},
|
||||
{"Custom_0", constants.CustomID, 0},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
sampled, err := SampleBootstrappers(tt.networkID, tt.count)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Should not exceed requested count
|
||||
require.LessOrEqual(t, len(sampled), tt.count)
|
||||
|
||||
// Should not exceed available bootstrappers
|
||||
all, err := GetBootstrappers(tt.networkID)
|
||||
require.NoError(t, err)
|
||||
require.LessOrEqual(t, len(sampled), len(all))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetConfig(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
networkID uint32
|
||||
}{
|
||||
{"Mainnet", constants.MainnetID},
|
||||
{"Testnet", constants.TestnetID},
|
||||
{"MainnetChainID", constants.MainnetChainID},
|
||||
{"TestnetChainID", constants.TestnetChainID},
|
||||
{"CustomID", constants.CustomID},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cfg := GetConfig(tt.networkID)
|
||||
require.NotNil(t, cfg)
|
||||
// Config should have a valid NetworkID
|
||||
require.Greater(t, cfg.NetworkID, uint32(0))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestVMAliases(t *testing.T) {
|
||||
// Verify all expected VMs have aliases
|
||||
require.Contains(t, VMAliases, constants.PlatformVMID)
|
||||
require.Contains(t, VMAliases, constants.XVMID)
|
||||
require.Contains(t, VMAliases, constants.EVMID)
|
||||
|
||||
// Verify aliases are non-empty
|
||||
for vmID, aliases := range VMAliases {
|
||||
require.NotEmpty(t, aliases, "VM %s should have aliases", vmID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestChainAliases(t *testing.T) {
|
||||
require.NotEmpty(t, PChainAliases)
|
||||
require.NotEmpty(t, XChainAliases)
|
||||
require.NotEmpty(t, CChainAliases)
|
||||
|
||||
require.Contains(t, PChainAliases, "P")
|
||||
require.Contains(t, XChainAliases, "X")
|
||||
require.Contains(t, CChainAliases, "C")
|
||||
}
|
||||
|
||||
func TestDefaultFeeConfigs(t *testing.T) {
|
||||
// Test dynamic fee configs
|
||||
configs := []struct {
|
||||
name string
|
||||
config interface{}
|
||||
}{
|
||||
{"MainnetDynamic", MainnetDynamicFeeConfig},
|
||||
{"TestnetDynamic", TestnetDynamicFeeConfig},
|
||||
{"LocalDynamic", LocalDynamicFeeConfig},
|
||||
{"MainnetValidator", MainnetValidatorFeeConfig},
|
||||
{"TestnetValidator", TestnetValidatorFeeConfig},
|
||||
{"LocalValidator", LocalValidatorFeeConfig},
|
||||
}
|
||||
|
||||
for _, tt := range configs {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
require.NotNil(t, tt.config)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/luxfi/genesis/pkg/genesis"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Priority: MNEMONIC > MNEMONIC > LIGHT_MNEMONIC
|
||||
mnemonic := ""
|
||||
for _, env := range []string{"MNEMONIC", "MNEMONIC", "LIGHT_MNEMONIC"} {
|
||||
if v := os.Getenv(env); v != "" {
|
||||
mnemonic = v
|
||||
break
|
||||
}
|
||||
}
|
||||
if mnemonic == "" {
|
||||
fmt.Println("mnemonic not set (set LIGHT_MNEMONIC, MNEMONIC, or MNEMONIC)")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Derive 10 keys from mnemonic (5 validators + 5 fee reserve)
|
||||
keys, err := genesis.LoadKeysFromMnemonic(mnemonic, 10)
|
||||
if err != nil {
|
||||
fmt.Printf("Error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
genesisETH := map[string]string{
|
||||
"9011e888251ab053b7bd1cdb598db4f9ded94714": "treasury (100PQ locktime=0)",
|
||||
"17f7c3c6d56c41f32a7daf65db0743a7d6cb74a6": "validator 0 (100yr vesting)",
|
||||
"775438812d358fd1e090cf04962f1f8f34dafbac": "validator 1 (100yr vesting)",
|
||||
"8078fc2bd0df7a5943ba1a81d0ae9572089a24d1": "validator 2 (100yr vesting)",
|
||||
"e3c9c6b304b875f0be94481ce1f1f634ceee0338": "validator 3 (100yr vesting)",
|
||||
"c18f7344b235903e796f52e8c0e5b21247d3faa4": "validator 4 (100yr vesting)",
|
||||
}
|
||||
|
||||
for i, key := range keys {
|
||||
ethHex := fmt.Sprintf("%x", key.ETHAddr[:])
|
||||
match := ""
|
||||
for addr, label := range genesisETH {
|
||||
if ethHex == addr {
|
||||
match = fmt.Sprintf(" <-- GENESIS MATCH: %s", label)
|
||||
break
|
||||
}
|
||||
}
|
||||
fmt.Printf("BIP44 index %d: ETH=0x%x NodeID=%s%s\n", i, key.ETHAddr[:], key.NodeID, match)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
//go:build ignore
|
||||
|
||||
// extract-genesis extracts genesis configuration from an RLP file or PebbleDB
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// go run main.go --rlp <path-to-rlp-file> [--output <output-file>]
|
||||
// go run main.go --pebble <path-to-pebbledb> [--output <output-file>]
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/luxfi/geth/core/types"
|
||||
"github.com/luxfi/geth/rlp"
|
||||
)
|
||||
|
||||
type GenesisExtract struct {
|
||||
Hash string `json:"hash"`
|
||||
StateRoot string `json:"stateRoot"`
|
||||
ParentHash string `json:"parentHash"`
|
||||
Timestamp uint64 `json:"timestamp"`
|
||||
GasLimit uint64 `json:"gasLimit"`
|
||||
BaseFee string `json:"baseFee,omitempty"`
|
||||
Difficulty string `json:"difficulty"`
|
||||
ExtraData string `json:"extraData"`
|
||||
Coinbase string `json:"coinbase"`
|
||||
Nonce uint64 `json:"nonce"`
|
||||
Header *types.Header `json:"header"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
rlpPath := flag.String("rlp", "", "Path to RLP file")
|
||||
pebblePath := flag.String("pebble", "", "Path to PebbleDB directory")
|
||||
output := flag.String("output", "", "Output file (default: stdout)")
|
||||
flag.Parse()
|
||||
|
||||
if *rlpPath == "" && *pebblePath == "" {
|
||||
fmt.Fprintf(os.Stderr, "Usage: %s --rlp <file> | --pebble <dir> [--output <file>]\n", os.Args[0])
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
var genesis *GenesisExtract
|
||||
var err error
|
||||
|
||||
if *rlpPath != "" {
|
||||
genesis, err = extractFromRLP(*rlpPath)
|
||||
} else {
|
||||
genesis, err = extractFromPebble(*pebblePath)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error extracting genesis: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
data, err := json.MarshalIndent(genesis, "", " ")
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error marshaling genesis: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if *output != "" {
|
||||
if err := os.MkdirAll(filepath.Dir(*output), 0755); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error creating output directory: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := os.WriteFile(*output, data, 0644); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error writing output file: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("Genesis extracted to %s\n", *output)
|
||||
} else {
|
||||
fmt.Println(string(data))
|
||||
}
|
||||
}
|
||||
|
||||
func extractFromRLP(path string) (*GenesisExtract, error) {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open RLP file: %w", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
stream := rlp.NewStream(file, 0)
|
||||
|
||||
// Read genesis block (block 0)
|
||||
block := new(types.Block)
|
||||
if err := stream.Decode(block); err != nil {
|
||||
if err == io.EOF {
|
||||
return nil, fmt.Errorf("RLP file is empty")
|
||||
}
|
||||
return nil, fmt.Errorf("failed to decode genesis block: %w", err)
|
||||
}
|
||||
|
||||
// Verify it's block 0
|
||||
if block.NumberU64() != 0 {
|
||||
return nil, fmt.Errorf("first block in RLP is not genesis (block %d)", block.NumberU64())
|
||||
}
|
||||
|
||||
header := block.Header()
|
||||
genesis := &GenesisExtract{
|
||||
Hash: block.Hash().Hex(),
|
||||
StateRoot: header.Root.Hex(),
|
||||
ParentHash: header.ParentHash.Hex(),
|
||||
Timestamp: header.Time,
|
||||
GasLimit: header.GasLimit,
|
||||
Difficulty: header.Difficulty.String(),
|
||||
ExtraData: fmt.Sprintf("0x%x", header.Extra),
|
||||
Coinbase: header.Coinbase.Hex(),
|
||||
Nonce: header.Nonce.Uint64(),
|
||||
Header: header,
|
||||
}
|
||||
|
||||
if header.BaseFee != nil {
|
||||
genesis.BaseFee = header.BaseFee.String()
|
||||
}
|
||||
|
||||
return genesis, nil
|
||||
}
|
||||
|
||||
func extractFromPebble(path string) (*GenesisExtract, error) {
|
||||
// Check if genesis.json exists in the pebble directory
|
||||
genesisPath := filepath.Join(path, "genesis.json")
|
||||
if _, err := os.Stat(genesisPath); err == nil {
|
||||
// Read existing genesis.json
|
||||
data, err := os.ReadFile(genesisPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read genesis.json: %w", err)
|
||||
}
|
||||
|
||||
var existing map[string]interface{}
|
||||
if err := json.Unmarshal(data, &existing); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse genesis.json: %w", err)
|
||||
}
|
||||
|
||||
// Extract relevant fields
|
||||
genesis := &GenesisExtract{
|
||||
ExtraData: "0x",
|
||||
}
|
||||
|
||||
if ts, ok := existing["timestamp"].(string); ok {
|
||||
var timestamp uint64
|
||||
fmt.Sscanf(ts, "%x", ×tamp)
|
||||
genesis.Timestamp = timestamp
|
||||
}
|
||||
|
||||
if gl, ok := existing["gasLimit"].(string); ok {
|
||||
var gasLimit uint64
|
||||
fmt.Sscanf(gl, "%x", &gasLimit)
|
||||
genesis.GasLimit = gasLimit
|
||||
}
|
||||
|
||||
if bf, ok := existing["baseFeePerGas"].(string); ok {
|
||||
genesis.BaseFee = bf
|
||||
}
|
||||
|
||||
return genesis, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("genesis.json not found in %s (PebbleDB direct reading not implemented)", path)
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
|
||||
"github.com/luxfi/ids"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 2 {
|
||||
fmt.Println("Usage: find-validators <dir> [nodeID-to-find...]")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
baseDir := os.Args[1]
|
||||
targetNodeIDs := map[string]bool{}
|
||||
for i := 2; i < len(os.Args); i++ {
|
||||
targetNodeIDs[os.Args[i]] = true
|
||||
}
|
||||
|
||||
// Find all staker.crt files
|
||||
matches := []string{}
|
||||
err := filepath.Walk(baseDir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if info.Name() == "staker.crt" {
|
||||
matches = append(matches, path)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error walking directory: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
sort.Strings(matches)
|
||||
|
||||
fmt.Printf("Found %d staker.crt files\n\n", len(matches))
|
||||
|
||||
for _, certPath := range matches {
|
||||
nodeID, err := getNodeIDFromCert(certPath)
|
||||
if err != nil {
|
||||
fmt.Printf("%-60s ERROR: %v\n", certPath, err)
|
||||
continue
|
||||
}
|
||||
|
||||
nodeIDStr := nodeID.String()
|
||||
if len(targetNodeIDs) == 0 {
|
||||
// Print all
|
||||
fmt.Printf("%-60s %s\n", certPath, nodeIDStr)
|
||||
} else if targetNodeIDs[nodeIDStr] {
|
||||
// Only print matches
|
||||
fmt.Printf("MATCH: %s\n Path: %s\n\n", nodeIDStr, certPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getNodeIDFromCert(certPath string) (ids.NodeID, error) {
|
||||
certPEM, err := os.ReadFile(certPath)
|
||||
if err != nil {
|
||||
return ids.NodeID{}, err
|
||||
}
|
||||
|
||||
block, _ := pem.Decode(certPEM)
|
||||
if block == nil {
|
||||
return ids.NodeID{}, fmt.Errorf("no PEM block found")
|
||||
}
|
||||
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return ids.NodeID{}, err
|
||||
}
|
||||
|
||||
stakingCert := &ids.Certificate{
|
||||
Raw: cert.Raw,
|
||||
PublicKey: cert.PublicKey,
|
||||
}
|
||||
|
||||
return ids.NodeIDFromCert(stakingCert), nil
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
// Copyright (C) 2019-2025, Lux Partners Limited. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
// Command genesis generates Lux network genesis configurations.
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// genesis [flags]
|
||||
//
|
||||
// Flags:
|
||||
//
|
||||
// -network Network name: mainnet, testnet, local, custom (default: local)
|
||||
// -network-id Network ID (overrides -network if set)
|
||||
// -keys-dir Directory containing node keys (default: ~/.lux/keys)
|
||||
// -genesis-dir Directory containing genesis component files
|
||||
// -output Output file path (default: stdout or primary.json)
|
||||
// -allocation Allocation per validator in LUX (default: 1000000000)
|
||||
// -validators Number of validators for mnemonic-based genesis
|
||||
// -cchain Path to existing C-Chain genesis (preserves original)
|
||||
// -format Output format: json, pretty (default: pretty)
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/luxfi/constants"
|
||||
"github.com/luxfi/genesis/pkg/genesis"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var (
|
||||
network = flag.String("network", "local", "Network name: mainnet, testnet, devnet, local")
|
||||
networkID = flag.Uint("network-id", 0, "Network ID (overrides -network if set)")
|
||||
keysDir = flag.String("keys-dir", "", "Directory containing node keys")
|
||||
genesisDir = flag.String("genesis-dir", "", "Directory containing genesis component files")
|
||||
output = flag.String("output", "", "Output file path (default: stdout)")
|
||||
allocation = flag.Uint64("allocation", genesis.DefaultAllocationPerValidator, "Allocation per validator in nLUX")
|
||||
validators = flag.Int("validators", 3, "Number of validators for mnemonic-based genesis")
|
||||
cchainPath = flag.String("cchain", "", "Path to existing C-Chain genesis (preserves original)")
|
||||
format = flag.String("format", "pretty", "Output format: json, pretty")
|
||||
showHelp = flag.Bool("help", false, "Show help")
|
||||
showVersion = flag.Bool("version", false, "Show version")
|
||||
)
|
||||
|
||||
flag.Parse()
|
||||
|
||||
if *showHelp {
|
||||
printUsage()
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if *showVersion {
|
||||
fmt.Println("genesis v0.1.0")
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// Determine network ID
|
||||
netID := resolveNetworkID(*network, uint32(*networkID))
|
||||
networkIDExplicit := *networkID != 0 // User explicitly provided network ID
|
||||
|
||||
// Build genesis config
|
||||
config, err := buildConfig(netID, *keysDir, *genesisDir, *allocation, *validators, *cchainPath, networkIDExplicit)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Output result
|
||||
if err := outputConfig(config, *output, *format); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error writing output: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func printUsage() {
|
||||
fmt.Println(`genesis - Generate Lux network genesis configurations
|
||||
|
||||
Usage:
|
||||
genesis [flags]
|
||||
|
||||
Flags:
|
||||
-network string Network name: mainnet, testnet, local, custom (default "local")
|
||||
-network-id uint Network ID (overrides -network if set)
|
||||
-keys-dir string Directory containing node keys (default: ~/.lux/keys)
|
||||
-genesis-dir string Directory containing genesis component files
|
||||
-output string Output file path (default: stdout)
|
||||
-allocation uint Allocation per validator in nLUX (default: 1B LUX)
|
||||
-validators int Number of validators for mnemonic-based genesis (default: 3)
|
||||
-cchain string Path to existing C-Chain genesis (preserves original)
|
||||
-format string Output format: json, pretty (default "pretty")
|
||||
-help Show help
|
||||
-version Show version
|
||||
|
||||
Environment Variables:
|
||||
KEYS_DIR Directory containing node keys
|
||||
MNEMONIC BIP39 mnemonic for key derivation
|
||||
PRIVATE_KEY Single private key (hex)
|
||||
LUX_GENESIS_DIR Directory containing genesis component files
|
||||
LUX_NETWORK_ID Network ID
|
||||
|
||||
Examples:
|
||||
# Generate local genesis from keys in ~/.lux/keys
|
||||
genesis -network local -output primary.json
|
||||
|
||||
# Generate mainnet genesis preserving original C-Chain
|
||||
genesis -network mainnet -cchain mainnet/cchain.json -output primary.json
|
||||
|
||||
# Generate from mnemonic with 3 validators
|
||||
MNEMONIC="your mnemonic here" genesis -validators 3 -output primary.json
|
||||
|
||||
# Load from existing genesis directory
|
||||
genesis -genesis-dir ./mainnet -output primary.json`)
|
||||
}
|
||||
|
||||
func resolveNetworkID(network string, explicitID uint32) uint32 {
|
||||
if explicitID != 0 {
|
||||
return explicitID
|
||||
}
|
||||
|
||||
switch strings.ToLower(network) {
|
||||
case "mainnet", "main":
|
||||
return constants.MainnetID
|
||||
case "testnet", "test":
|
||||
return constants.TestnetID
|
||||
case "devnet", "dev":
|
||||
return constants.DevnetID
|
||||
case "local":
|
||||
return constants.LocalID
|
||||
default:
|
||||
return constants.LocalID
|
||||
}
|
||||
}
|
||||
|
||||
func buildConfig(networkID uint32, keysDir, genesisDir string, allocation uint64, validators int, cchainPath string, networkIDExplicit bool) (*genesis.Config, error) {
|
||||
var config *genesis.Config
|
||||
var err error
|
||||
|
||||
// Try loading from genesis directory first if provided
|
||||
if genesisDir != "" {
|
||||
config, err = genesis.GetConfigFromDir(genesisDir)
|
||||
if err == nil {
|
||||
// Only override networkID if explicitly specified by user
|
||||
if networkIDExplicit {
|
||||
config.NetworkID = networkID
|
||||
}
|
||||
return maybePreserveCChain(config, cchainPath)
|
||||
}
|
||||
// If genesis dir specified but failed, report error
|
||||
return nil, fmt.Errorf("failed to load from genesis dir %s: %w", genesisDir, err)
|
||||
}
|
||||
|
||||
// Try environment-based config
|
||||
config, err = genesis.BuildConfigFromEnv(networkID, validators, allocation)
|
||||
if err == nil {
|
||||
return maybePreserveCChain(config, cchainPath)
|
||||
}
|
||||
|
||||
// Try keys directory
|
||||
if keysDir == "" {
|
||||
home, _ := os.UserHomeDir()
|
||||
keysDir = filepath.Join(home, ".lux", "keys")
|
||||
}
|
||||
|
||||
config, err = genesis.BuildConfigFromKeys(networkID, keysDir, allocation)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to build config: %w (try setting KEYS_DIR, MNEMONIC, or PRIVATE_KEY)", err)
|
||||
}
|
||||
|
||||
return maybePreserveCChain(config, cchainPath)
|
||||
}
|
||||
|
||||
func maybePreserveCChain(config *genesis.Config, cchainPath string) (*genesis.Config, error) {
|
||||
if cchainPath == "" {
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// Load and preserve original C-Chain genesis
|
||||
cchainData, err := os.ReadFile(cchainPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read C-Chain genesis from %s: %w", cchainPath, err)
|
||||
}
|
||||
|
||||
config.CChainGenesis = string(cchainData)
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func outputConfig(config *genesis.Config, outputPath, format string) error {
|
||||
var data []byte
|
||||
var err error
|
||||
|
||||
if format == "pretty" {
|
||||
data, err = json.MarshalIndent(config, "", " ")
|
||||
} else {
|
||||
data, err = json.Marshal(config)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal config: %w", err)
|
||||
}
|
||||
|
||||
// Add newline
|
||||
data = append(data, '\n')
|
||||
|
||||
if outputPath == "" {
|
||||
_, err = os.Stdout.Write(data)
|
||||
return err
|
||||
}
|
||||
|
||||
return os.WriteFile(outputPath, data, 0644)
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
// Copyright (C) 2019-2025, Lux Partners Limited. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
// Command regenerate-genesis creates a new genesis file using the running node
|
||||
// certificates while preserving the exact cChainGenesis string to maintain
|
||||
// C-Chain genesis hash compatibility.
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
|
||||
"github.com/luxfi/crypto/bls"
|
||||
"github.com/luxfi/ids"
|
||||
luxtls "github.com/luxfi/tls"
|
||||
)
|
||||
|
||||
// GenesisConfig represents the genesis JSON structure
|
||||
type GenesisConfig struct {
|
||||
Allocations []interface{} `json:"allocations"`
|
||||
CChainGenesis string `json:"cChainGenesis"`
|
||||
InitialStakeDuration uint64 `json:"initialStakeDuration"`
|
||||
InitialStakeDurationOffset uint64 `json:"initialStakeDurationOffset"`
|
||||
InitialStakedFunds []string `json:"initialStakedFunds"`
|
||||
InitialStakers []Staker `json:"initialStakers"`
|
||||
Message string `json:"message"`
|
||||
NetworkID uint32 `json:"networkID"`
|
||||
StartTime uint64 `json:"startTime"`
|
||||
}
|
||||
|
||||
// Staker represents an initial validator
|
||||
type Staker struct {
|
||||
DelegationFee uint32 `json:"delegationFee"`
|
||||
NodeID string `json:"nodeID"`
|
||||
RewardAddress string `json:"rewardAddress"`
|
||||
Signer *SignerProof `json:"signer"`
|
||||
Weight uint64 `json:"weight"`
|
||||
}
|
||||
|
||||
// SignerProof contains BLS signature proof
|
||||
type SignerProof struct {
|
||||
ProofOfPossession string `json:"proofOfPossession"`
|
||||
PublicKey string `json:"publicKey"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
keysDir := flag.String("keys", "", "Directory containing node keys (e.g., ~/.lux/keys)")
|
||||
origGenesis := flag.String("genesis", "", "Original genesis file to preserve cChainGenesis from")
|
||||
output := flag.String("output", "", "Output file path")
|
||||
flag.Parse()
|
||||
|
||||
if *keysDir == "" || *origGenesis == "" {
|
||||
fmt.Println("Usage: regenerate-genesis -keys <keys-dir> -genesis <original-genesis.json> -output <new-genesis.json>")
|
||||
fmt.Println("\nThis tool regenerates genesis with running node NodeIDs while preserving cChainGenesis")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Load original genesis
|
||||
origData, err := os.ReadFile(*origGenesis)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error reading original genesis: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
var genesis GenesisConfig
|
||||
if err := json.Unmarshal(origData, &genesis); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error parsing genesis: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Load node keys
|
||||
stakers, err := loadNodeKeys(*keysDir)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error loading node keys: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if len(stakers) == 0 {
|
||||
fmt.Fprintf(os.Stderr, "No node keys found in %s\n", *keysDir)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("Found %d node keys:\n", len(stakers))
|
||||
for _, s := range stakers {
|
||||
fmt.Printf(" %s\n", s.NodeID)
|
||||
}
|
||||
|
||||
// Replace stakers while keeping everything else
|
||||
genesis.InitialStakers = stakers
|
||||
genesis.InitialStakedFunds = []string{} // Clear staked funds since we're using new validators
|
||||
|
||||
// Output
|
||||
newData, err := json.MarshalIndent(genesis, "", " ")
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error marshaling genesis: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if *output == "" {
|
||||
fmt.Println(string(newData))
|
||||
} else {
|
||||
if err := os.WriteFile(*output, newData, 0644); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error writing output: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("\nWrote regenerated genesis to: %s\n", *output)
|
||||
fmt.Println("\nIMPORTANT: The cChainGenesis field is preserved exactly, so C-Chain genesis hash is unchanged.")
|
||||
fmt.Println("The initialStakers have been replaced with the running node certificates.")
|
||||
}
|
||||
}
|
||||
|
||||
func loadNodeKeys(keysDir string) ([]Staker, error) {
|
||||
entries, err := os.ReadDir(keysDir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read keys directory: %w", err)
|
||||
}
|
||||
|
||||
var stakers []Staker
|
||||
for _, entry := range entries {
|
||||
if !entry.IsDir() {
|
||||
continue
|
||||
}
|
||||
|
||||
nodeDir := filepath.Join(keysDir, entry.Name())
|
||||
staker, err := loadStakerFromDir(nodeDir)
|
||||
if err != nil {
|
||||
fmt.Printf("Warning: skipping %s: %v\n", entry.Name(), err)
|
||||
continue
|
||||
}
|
||||
stakers = append(stakers, *staker)
|
||||
}
|
||||
|
||||
// Sort by NodeID for consistent ordering
|
||||
sort.Slice(stakers, func(i, j int) bool {
|
||||
return stakers[i].NodeID < stakers[j].NodeID
|
||||
})
|
||||
|
||||
return stakers, nil
|
||||
}
|
||||
|
||||
func loadStakerFromDir(nodeDir string) (*Staker, error) {
|
||||
// Try modern path structure first
|
||||
certPath := filepath.Join(nodeDir, "staking", "staker.crt")
|
||||
keyPath := filepath.Join(nodeDir, "staking", "staker.key")
|
||||
signerPath := filepath.Join(nodeDir, "bls", "signer.key")
|
||||
|
||||
// Fallback to legacy paths
|
||||
if _, err := os.Stat(certPath); os.IsNotExist(err) {
|
||||
certPath = filepath.Join(nodeDir, "staker.crt")
|
||||
keyPath = filepath.Join(nodeDir, "staker.key")
|
||||
signerPath = filepath.Join(nodeDir, "signer.key")
|
||||
}
|
||||
|
||||
// Load staker certificate
|
||||
certPEM, err := os.ReadFile(certPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("no staker.crt found")
|
||||
}
|
||||
|
||||
keyPEM, err := os.ReadFile(keyPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("no staker.key found")
|
||||
}
|
||||
|
||||
// Get NodeID from certificate
|
||||
tlsCert, err := luxtls.LoadTLSCertFromBytes(keyPEM, certPEM)
|
||||
if err != nil {
|
||||
// Fallback to manual parsing
|
||||
block, _ := pem.Decode(certPEM)
|
||||
if block == nil {
|
||||
return nil, fmt.Errorf("failed to decode certificate PEM")
|
||||
}
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse certificate: %w", err)
|
||||
}
|
||||
stakingCert := &ids.Certificate{
|
||||
Raw: cert.Raw,
|
||||
PublicKey: cert.PublicKey,
|
||||
}
|
||||
nodeID := ids.NodeIDFromCert(stakingCert)
|
||||
|
||||
staker := &Staker{
|
||||
NodeID: nodeID.String(),
|
||||
DelegationFee: 20000, // 2%
|
||||
Weight: 1000000000000000, // 1 quadrillion
|
||||
RewardAddress: "X-lux1qsrd262r5w9dswv2wwzj0un79ncpwvdgkpqzqu", // placeholder
|
||||
}
|
||||
|
||||
// Try to load BLS signer key
|
||||
signerData, err := os.ReadFile(signerPath)
|
||||
if err == nil && len(signerData) >= 32 {
|
||||
sk, err := bls.SecretKeyFromBytes(signerData[:32])
|
||||
if err == nil {
|
||||
pk := bls.PublicFromSecretKey(sk)
|
||||
pkBytes := bls.PublicKeyToCompressedBytes(pk)
|
||||
sig := bls.SignProofOfPossession(sk, pkBytes)
|
||||
staker.Signer = &SignerProof{
|
||||
PublicKey: "0x" + hex.EncodeToString(pkBytes),
|
||||
ProofOfPossession: "0x" + hex.EncodeToString(bls.SignatureToBytes(sig)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return staker, nil
|
||||
}
|
||||
|
||||
stakingCert := &ids.Certificate{
|
||||
Raw: tlsCert.Leaf.Raw,
|
||||
PublicKey: tlsCert.Leaf.PublicKey,
|
||||
}
|
||||
nodeID := ids.NodeIDFromCert(stakingCert)
|
||||
|
||||
staker := &Staker{
|
||||
NodeID: nodeID.String(),
|
||||
DelegationFee: 20000,
|
||||
Weight: 1000000000000000,
|
||||
RewardAddress: "X-lux1qsrd262r5w9dswv2wwzj0un79ncpwvdgkpqzqu",
|
||||
}
|
||||
|
||||
// Load BLS signer key
|
||||
signerData, err := os.ReadFile(signerPath)
|
||||
if err == nil && len(signerData) >= 32 {
|
||||
sk, err := bls.SecretKeyFromBytes(signerData[:32])
|
||||
if err == nil {
|
||||
pk := bls.PublicFromSecretKey(sk)
|
||||
pkBytes := bls.PublicKeyToCompressedBytes(pk)
|
||||
sig := bls.SignProofOfPossession(sk, pkBytes)
|
||||
staker.Signer = &SignerProof{
|
||||
PublicKey: "0x" + hex.EncodeToString(pkBytes),
|
||||
ProofOfPossession: "0x" + hex.EncodeToString(bls.SignatureToBytes(sig)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return staker, nil
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"admin-api-enabled": true,
|
||||
"eth-apis": ["eth", "eth-filter", "net", "web3", "internal-eth", "internal-blockchain", "internal-transaction", "internal-account", "admin", "debug"],
|
||||
"log-level": "debug",
|
||||
"pruning-enabled": true,
|
||||
"rpc-gas-cap": 50000000,
|
||||
"rpc-tx-fee-cap": 100
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"admin-api-enabled": true,
|
||||
"eth-apis": ["eth", "eth-filter", "net", "web3", "internal-eth", "internal-blockchain", "internal-transaction", "internal-account", "admin", "debug"],
|
||||
"log-level": "debug",
|
||||
"pruning-enabled": true,
|
||||
"rpc-gas-cap": 50000000,
|
||||
"rpc-tx-fee-cap": 100
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"admin-api-enabled": true,
|
||||
"eth-apis": ["eth", "eth-filter", "net", "web3", "internal-eth", "internal-blockchain", "internal-transaction", "internal-account", "admin", "debug"],
|
||||
"log-level": "debug",
|
||||
"pruning-enabled": true,
|
||||
"rpc-gas-cap": 50000000,
|
||||
"rpc-tx-fee-cap": 100
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"admin-api-enabled": true,
|
||||
"eth-apis": ["eth", "eth-filter", "net", "web3", "internal-eth", "internal-blockchain", "internal-transaction", "internal-account", "admin", "debug"],
|
||||
"log-level": "debug",
|
||||
"pruning-enabled": true,
|
||||
"rpc-gas-cap": 50000000,
|
||||
"rpc-tx-fee-cap": 100
|
||||
}
|
||||
@@ -0,0 +1,398 @@
|
||||
// Copyright (C) 2019-2025, Lux Partners Limited. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
// Package configs provides genesis configuration loading for Lux networks.
|
||||
// This package is used by CLI, netrunner, and node to obtain genesis JSON
|
||||
// for different network IDs.
|
||||
//
|
||||
// Dynamic P-Chain Allocations:
|
||||
// P-Chain allocations can be specified dynamically at runtime via:
|
||||
// - LUX_PCHAIN_ALLOCS: JSON string of allocations
|
||||
// - LUX_PCHAIN_ALLOCS_FILE: Path to allocations JSON file
|
||||
// - ~/.lux/genesis/{network}/pchain.json: Standard override location
|
||||
//
|
||||
// The C-Chain genesis remains embedded and immutable.
|
||||
package configs
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/luxfi/genesis/pkg/genesis"
|
||||
)
|
||||
|
||||
// Network ID constants (P-Chain)
|
||||
// mainnet, testnet, devnet: proper public networks (can run locally with validators)
|
||||
// custom: for custom local development with chainID 1337
|
||||
const (
|
||||
MainnetID = 1
|
||||
TestnetID = 2
|
||||
DevnetID = 3
|
||||
CustomID = 1337 // custom local development network
|
||||
|
||||
// Chain ID constants (C-Chain EVM) - can be used as network ID aliases
|
||||
MainnetChainID = 96369
|
||||
TestnetChainID = 96368
|
||||
DevnetChainID = 96370
|
||||
CustomChainID = 1337
|
||||
)
|
||||
|
||||
//go:embed mainnet testnet devnet custom
|
||||
var embeddedGenesis embed.FS
|
||||
|
||||
// GetGenesis returns the genesis JSON bytes for a network ID.
|
||||
// It supports dynamic P-Chain allocations via environment variables or files:
|
||||
// - LUX_PCHAIN_ALLOCS: JSON string of allocations
|
||||
// - LUX_PCHAIN_ALLOCS_FILE: Path to allocations JSON file
|
||||
// - ~/.lux/genesis/{network}/pchain.json: Standard override location
|
||||
//
|
||||
// C-Chain genesis remains embedded and immutable.
|
||||
func GetGenesis(networkID uint32) ([]byte, error) {
|
||||
networkName := networkNameFromID(networkID)
|
||||
|
||||
// Check for dynamic P-Chain allocations
|
||||
dynamicPChain := loadDynamicPChainAllocations(networkName)
|
||||
|
||||
if networkName != "" {
|
||||
// Try to load from embedded FS with optional dynamic allocations
|
||||
data, err := loadEmbeddedGenesisWithDynamic(networkName, dynamicPChain)
|
||||
if err == nil {
|
||||
return data, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to file system locations
|
||||
return loadGenesisFromFS(networkID)
|
||||
}
|
||||
|
||||
// GetGenesisWithAllocations returns genesis with custom P-Chain allocations.
|
||||
// This allows booting networks with custom validator allocations.
|
||||
func GetGenesisWithAllocations(networkID uint32, allocations []genesis.AllocationJSON) ([]byte, error) {
|
||||
networkName := networkNameFromID(networkID)
|
||||
|
||||
// Convert allocations to PChainConfig
|
||||
pchain := &genesis.PChainConfig{
|
||||
Allocations: allocations,
|
||||
}
|
||||
|
||||
if networkName != "" {
|
||||
data, err := loadEmbeddedGenesisWithDynamic(networkName, pchain)
|
||||
if err == nil {
|
||||
return data, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("failed to load genesis for network %d", networkID)
|
||||
}
|
||||
|
||||
// loadDynamicPChainAllocations loads P-Chain allocations from environment or files.
|
||||
func loadDynamicPChainAllocations(networkName string) *genesis.PChainConfig {
|
||||
// First, check LUX_PCHAIN_ALLOCS environment variable (JSON string)
|
||||
if allocsJSON := os.Getenv("LUX_PCHAIN_ALLOCS"); allocsJSON != "" {
|
||||
var pchain genesis.PChainConfig
|
||||
if err := json.Unmarshal([]byte(allocsJSON), &pchain); err == nil {
|
||||
return &pchain
|
||||
}
|
||||
}
|
||||
|
||||
// Second, check LUX_PCHAIN_ALLOCS_FILE environment variable
|
||||
if allocsFile := os.Getenv("LUX_PCHAIN_ALLOCS_FILE"); allocsFile != "" {
|
||||
data, err := os.ReadFile(allocsFile)
|
||||
if err == nil {
|
||||
var pchain genesis.PChainConfig
|
||||
if err := json.Unmarshal(data, &pchain); err == nil {
|
||||
return &pchain
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Third, check standard override location ~/.lux/genesis/{network}/pchain.json
|
||||
home, _ := os.UserHomeDir()
|
||||
overridePath := filepath.Join(home, ".lux/genesis", networkName, "pchain.json")
|
||||
data, err := os.ReadFile(overridePath)
|
||||
if err == nil {
|
||||
var pchain genesis.PChainConfig
|
||||
if err := json.Unmarshal(data, &pchain); err == nil {
|
||||
return &pchain
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// loadEmbeddedGenesisWithDynamic loads genesis with optional dynamic P-Chain allocations.
|
||||
func loadEmbeddedGenesisWithDynamic(networkName string, dynamicPChain *genesis.PChainConfig) ([]byte, error) {
|
||||
// Load network.json from embedded
|
||||
networkData, err := embeddedGenesis.ReadFile(filepath.Join(networkName, "network.json"))
|
||||
if err != nil {
|
||||
// Fall back to single genesis.json file
|
||||
return embeddedGenesis.ReadFile(filepath.Join(networkName, "genesis.json"))
|
||||
}
|
||||
var network genesis.NetworkConfig
|
||||
if err := json.Unmarshal(networkData, &network); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse network.json: %w", err)
|
||||
}
|
||||
|
||||
// Check if split pchain.json/cchain.json exist - if not, fall back to genesis.json
|
||||
_, pchainErr := embeddedGenesis.ReadFile(filepath.Join(networkName, "pchain.json"))
|
||||
_, cchainErr := embeddedGenesis.ReadFile(filepath.Join(networkName, "cchain.json"))
|
||||
if pchainErr != nil || cchainErr != nil {
|
||||
// No split files, fall back to combined genesis.json (devnet case)
|
||||
return embeddedGenesis.ReadFile(filepath.Join(networkName, "genesis.json"))
|
||||
}
|
||||
|
||||
// Load P-Chain config - use dynamic if provided, otherwise embedded
|
||||
var pchain genesis.PChainConfig
|
||||
if dynamicPChain != nil && len(dynamicPChain.Allocations) > 0 {
|
||||
pchain = *dynamicPChain
|
||||
// If dynamic allocations don't have staking config, load from embedded
|
||||
if pchain.InitialStakeDuration == 0 {
|
||||
embeddedPChain, _ := loadEmbeddedPChainConfig(networkName)
|
||||
if embeddedPChain != nil {
|
||||
pchain.InitialStakeDuration = embeddedPChain.InitialStakeDuration
|
||||
pchain.InitialStakeDurationOffset = embeddedPChain.InitialStakeDurationOffset
|
||||
pchain.InitialStakedFunds = embeddedPChain.InitialStakedFunds
|
||||
pchain.InitialStakers = embeddedPChain.InitialStakers
|
||||
}
|
||||
}
|
||||
} else {
|
||||
pchainData, err := embeddedGenesis.ReadFile(filepath.Join(networkName, "pchain.json"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read pchain.json: %w", err)
|
||||
}
|
||||
if err := json.Unmarshal(pchainData, &pchain); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse pchain.json: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Load C-Chain genesis (always embedded, immutable)
|
||||
cchainData, err := embeddedGenesis.ReadFile(filepath.Join(networkName, "cchain.json"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read cchain.json: %w", err)
|
||||
}
|
||||
|
||||
// Build combined genesis config
|
||||
config := genesis.ConfigOutput{
|
||||
NetworkID: network.NetworkID,
|
||||
Allocations: pchain.Allocations,
|
||||
StartTime: network.StartTime,
|
||||
InitialStakeDuration: pchain.InitialStakeDuration,
|
||||
InitialStakeDurationOffset: pchain.InitialStakeDurationOffset,
|
||||
InitialStakedFunds: pchain.InitialStakedFunds,
|
||||
InitialStakers: pchain.InitialStakers,
|
||||
CChainGenesis: string(cchainData),
|
||||
Message: network.Message,
|
||||
}
|
||||
|
||||
return json.Marshal(config)
|
||||
}
|
||||
|
||||
// loadEmbeddedPChainConfig loads only the P-Chain config from embedded.
|
||||
func loadEmbeddedPChainConfig(networkName string) (*genesis.PChainConfig, error) {
|
||||
pchainData, err := embeddedGenesis.ReadFile(filepath.Join(networkName, "pchain.json"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var pchain genesis.PChainConfig
|
||||
if err := json.Unmarshal(pchainData, &pchain); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pchain, nil
|
||||
}
|
||||
|
||||
// GetConfig returns the parsed genesis Config for a network ID.
|
||||
func GetConfig(networkID uint32) (*genesis.Config, error) {
|
||||
data, err := GetGenesis(networkID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var config genesis.Config
|
||||
if err := json.Unmarshal(data, &config); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse genesis config: %w", err)
|
||||
}
|
||||
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
// networkNameFromID returns the network directory name for a network ID.
|
||||
// Accepts both network IDs (1, 2, 3, 1337) and chain IDs (96369, 96368, 96370) as aliases.
|
||||
func networkNameFromID(networkID uint32) string {
|
||||
switch networkID {
|
||||
case MainnetID, MainnetChainID:
|
||||
return "mainnet"
|
||||
case TestnetID, TestnetChainID:
|
||||
return "testnet"
|
||||
case DevnetID, DevnetChainID:
|
||||
return "devnet"
|
||||
case CustomID: // CustomChainID == CustomID (both 1337)
|
||||
return "custom"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// GetCanonicalGenesisBytes returns the canonical genesis bytes for a network.
|
||||
// This function builds the genesis from split files (network.json, pchain.json, cchain.json)
|
||||
// to ensure cChainGenesis is properly serialized as a JSON string.
|
||||
//
|
||||
// CRITICAL: The embedded genesis.json stores cChainGenesis as an object for easy editing,
|
||||
// but luxd requires it to be a JSON-encoded string. This function handles the conversion.
|
||||
func GetCanonicalGenesisBytes(networkID uint32) ([]byte, error) {
|
||||
networkName := networkNameFromID(networkID)
|
||||
if networkName == "" {
|
||||
return nil, fmt.Errorf("unknown network ID: %d", networkID)
|
||||
}
|
||||
|
||||
// First, try to build from split files (network.json + pchain.json + cchain.json)
|
||||
// This properly stringifies cChainGenesis
|
||||
data, err := buildCanonicalGenesisFromSplitFiles(networkName)
|
||||
if err == nil {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// Fall back to file system locations with split files
|
||||
home, _ := os.UserHomeDir()
|
||||
candidates := []string{
|
||||
filepath.Join(home, "work/lux/genesis/configs", networkName),
|
||||
filepath.Join(home, ".lux/genesis", networkName),
|
||||
filepath.Join("/etc/lux/genesis", networkName),
|
||||
}
|
||||
|
||||
for _, dir := range candidates {
|
||||
if data, err := buildGenesisFromDir(dir); err == nil {
|
||||
return data, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("canonical genesis not found for network %s (need split files: network.json, pchain.json, cchain.json)", networkName)
|
||||
}
|
||||
|
||||
// buildCanonicalGenesisFromSplitFiles builds genesis from embedded split files.
|
||||
// This ensures cChainGenesis is properly serialized as a JSON string.
|
||||
func buildCanonicalGenesisFromSplitFiles(networkName string) ([]byte, error) {
|
||||
// Load network.json
|
||||
networkData, err := embeddedGenesis.ReadFile(filepath.Join(networkName, "network.json"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read network.json: %w", err)
|
||||
}
|
||||
var network genesis.NetworkConfig
|
||||
if err := json.Unmarshal(networkData, &network); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse network.json: %w", err)
|
||||
}
|
||||
|
||||
// Load pchain.json
|
||||
pchainData, err := embeddedGenesis.ReadFile(filepath.Join(networkName, "pchain.json"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read pchain.json: %w", err)
|
||||
}
|
||||
var pchain genesis.PChainConfig
|
||||
if err := json.Unmarshal(pchainData, &pchain); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse pchain.json: %w", err)
|
||||
}
|
||||
|
||||
// Load cchain.json - this is the C-Chain genesis as a JSON object
|
||||
cchainData, err := embeddedGenesis.ReadFile(filepath.Join(networkName, "cchain.json"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read cchain.json: %w", err)
|
||||
}
|
||||
|
||||
// Build combined genesis config with cChainGenesis as a JSON string
|
||||
config := genesis.ConfigOutput{
|
||||
NetworkID: network.NetworkID,
|
||||
Allocations: pchain.Allocations,
|
||||
StartTime: network.StartTime,
|
||||
InitialStakeDuration: pchain.InitialStakeDuration,
|
||||
InitialStakeDurationOffset: pchain.InitialStakeDurationOffset,
|
||||
InitialStakedFunds: pchain.InitialStakedFunds,
|
||||
InitialStakers: pchain.InitialStakers,
|
||||
CChainGenesis: string(cchainData), // Properly convert object to string
|
||||
Message: network.Message,
|
||||
}
|
||||
|
||||
return json.Marshal(config)
|
||||
}
|
||||
|
||||
// loadGenesisFromFS loads genesis from file system locations.
|
||||
func loadGenesisFromFS(networkID uint32) ([]byte, error) {
|
||||
networkName := networkNameFromID(networkID)
|
||||
if networkName == "" {
|
||||
networkName = "custom"
|
||||
}
|
||||
|
||||
home, _ := os.UserHomeDir()
|
||||
candidates := []string{
|
||||
filepath.Join(home, "work/lux/genesis", networkName),
|
||||
filepath.Join(home, ".lux/genesis", networkName),
|
||||
filepath.Join("/etc/lux/genesis", networkName),
|
||||
}
|
||||
|
||||
for _, dir := range candidates {
|
||||
// Try component files first
|
||||
networkPath := filepath.Join(dir, "network.json")
|
||||
if _, err := os.Stat(networkPath); err == nil {
|
||||
return buildGenesisFromDir(dir)
|
||||
}
|
||||
|
||||
// Try single genesis.json
|
||||
genesisPath := filepath.Join(dir, "genesis.json")
|
||||
if data, err := os.ReadFile(genesisPath); err == nil {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// Try primary.json (our generated output)
|
||||
primaryPath := filepath.Join(dir, "primary.json")
|
||||
if data, err := os.ReadFile(primaryPath); err == nil {
|
||||
return data, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("genesis not found for network %d", networkID)
|
||||
}
|
||||
|
||||
// buildGenesisFromDir builds genesis from component files in a directory.
|
||||
func buildGenesisFromDir(dir string) ([]byte, error) {
|
||||
// Load network.json
|
||||
networkData, err := os.ReadFile(filepath.Join(dir, "network.json"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read network.json: %w", err)
|
||||
}
|
||||
var network genesis.NetworkConfig
|
||||
if err := json.Unmarshal(networkData, &network); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse network.json: %w", err)
|
||||
}
|
||||
|
||||
// Load pchain.json
|
||||
pchainData, err := os.ReadFile(filepath.Join(dir, "pchain.json"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read pchain.json: %w", err)
|
||||
}
|
||||
var pchain genesis.PChainConfig
|
||||
if err := json.Unmarshal(pchainData, &pchain); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse pchain.json: %w", err)
|
||||
}
|
||||
|
||||
// Load cchain.json
|
||||
cchainData, err := os.ReadFile(filepath.Join(dir, "cchain.json"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read cchain.json: %w", err)
|
||||
}
|
||||
|
||||
// Build combined genesis config
|
||||
config := genesis.ConfigOutput{
|
||||
NetworkID: network.NetworkID,
|
||||
Allocations: pchain.Allocations,
|
||||
StartTime: network.StartTime,
|
||||
InitialStakeDuration: pchain.InitialStakeDuration,
|
||||
InitialStakeDurationOffset: pchain.InitialStakeDurationOffset,
|
||||
InitialStakedFunds: pchain.InitialStakedFunds,
|
||||
InitialStakers: pchain.InitialStakers,
|
||||
CChainGenesis: string(cchainData),
|
||||
Message: network.Message,
|
||||
}
|
||||
|
||||
return json.Marshal(config)
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
{
|
||||
"config": {
|
||||
"chainId": 1337,
|
||||
"homesteadBlock": 0,
|
||||
"eip150Block": 0,
|
||||
"eip155Block": 0,
|
||||
"eip158Block": 0,
|
||||
"byzantiumBlock": 0,
|
||||
"constantinopleBlock": 0,
|
||||
"petersburgBlock": 0,
|
||||
"istanbulBlock": 0,
|
||||
"muirGlacierBlock": 0,
|
||||
"berlinBlock": 0,
|
||||
"londonBlock": 0,
|
||||
"arrowGlacierBlock": 0,
|
||||
"grayGlacierBlock": 0,
|
||||
"mergeNetsplitBlock": 0,
|
||||
"shanghaiTime": 0,
|
||||
"cancunTime": 253399622400,
|
||||
"terminalTotalDifficulty": 0,
|
||||
"subnetEVMTimestamp": 0,
|
||||
"durangoTimestamp": 0,
|
||||
"etnaTimestamp": 253399622400,
|
||||
"feeConfig": {
|
||||
"gasLimit": 12000000,
|
||||
"targetBlockRate": 2,
|
||||
"minBaseFee": 25000000000,
|
||||
"targetGas": 60000000,
|
||||
"baseFeeChangeDenominator": 36,
|
||||
"minBlockGasCost": 0,
|
||||
"maxBlockGasCost": 1000000,
|
||||
"blockGasCostStep": 200000
|
||||
},
|
||||
"warpConfig": {
|
||||
"blockTimestamp": 0,
|
||||
"quorumNumerator": 67,
|
||||
"requirePrimaryNetworkSigners": false
|
||||
}
|
||||
},
|
||||
"alloc": {
|
||||
"35D64Ff3f618f7a17DF34DCb21be375A4686a8de": {
|
||||
"balance": "0x200000000000000000000000000000000000000000000000000000000000000",
|
||||
"comment": "Mnemonic Account 0 (light light light...)"
|
||||
},
|
||||
"dAF82928dE0ABBAE133322020B253283d335d3A8": {
|
||||
"balance": "0x200000000000000000000000000000000000000000000000000000000000000",
|
||||
"comment": "Mnemonic Account 1"
|
||||
},
|
||||
"Bb5D7C55DbbB353f9e7667dbCC43B228B857998a": {
|
||||
"balance": "0x200000000000000000000000000000000000000000000000000000000000000",
|
||||
"comment": "Mnemonic Account 2"
|
||||
},
|
||||
"6828eAa708F40c11C121C25BdbB4a6fd1415fB24": {
|
||||
"balance": "0x200000000000000000000000000000000000000000000000000000000000000",
|
||||
"comment": "Mnemonic Account 3"
|
||||
},
|
||||
"a238C9E15C4AD9b10441fe9f7afFa6A131F4Cc37": {
|
||||
"balance": "0x200000000000000000000000000000000000000000000000000000000000000",
|
||||
"comment": "Mnemonic Account 4"
|
||||
},
|
||||
"f39Fd6e51aad88F6F4ce6aB8827279cffFb92266": {
|
||||
"balance": "0x200000000000000000000000000000000000000000000000000000000000000",
|
||||
"comment": "Anvil/Hardhat Account 0 (pk: 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80)"
|
||||
},
|
||||
"70997970C51812dc3A010C7d01b50e0d17dc79C8": {
|
||||
"balance": "0x200000000000000000000000000000000000000000000000000000000000000",
|
||||
"comment": "Anvil/Hardhat Account 1 (pk: 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d)"
|
||||
},
|
||||
"3C44CdDdB6a900fa2b585dd299e03d12FA4293BC": {
|
||||
"balance": "0x200000000000000000000000000000000000000000000000000000000000000",
|
||||
"comment": "Anvil/Hardhat Account 2"
|
||||
},
|
||||
"90F79bf6EB2c4f870365E785982E1f101E93b906": {
|
||||
"balance": "0x200000000000000000000000000000000000000000000000000000000000000",
|
||||
"comment": "Anvil/Hardhat Account 3"
|
||||
},
|
||||
"15d34AAf54267DB7D7c367839AAf71A00a2C6A65": {
|
||||
"balance": "0x200000000000000000000000000000000000000000000000000000000000000",
|
||||
"comment": "Anvil/Hardhat Account 4"
|
||||
},
|
||||
"9965507D1a55bcC2695C58ba16FB37d819B0A4dc": {
|
||||
"balance": "0x200000000000000000000000000000000000000000000000000000000000000",
|
||||
"comment": "Anvil/Hardhat Account 5"
|
||||
},
|
||||
"976EA74026E726554dB657fA54763abd0C3a0aa9": {
|
||||
"balance": "0x200000000000000000000000000000000000000000000000000000000000000",
|
||||
"comment": "Anvil/Hardhat Account 6"
|
||||
},
|
||||
"14dC79964da2C08b23698B3D3cc7Ca32193d9955": {
|
||||
"balance": "0x200000000000000000000000000000000000000000000000000000000000000",
|
||||
"comment": "Anvil/Hardhat Account 7"
|
||||
},
|
||||
"23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f": {
|
||||
"balance": "0x200000000000000000000000000000000000000000000000000000000000000",
|
||||
"comment": "Anvil/Hardhat Account 8"
|
||||
},
|
||||
"a0Ee7A142d267C1f36714E4a8F75612F20a79720": {
|
||||
"balance": "0x200000000000000000000000000000000000000000000000000000000000000",
|
||||
"comment": "Anvil/Hardhat Account 9"
|
||||
},
|
||||
"8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC": {
|
||||
"balance": "0x52B7D2DCC80CD2E4000000",
|
||||
"comment": "Lux Dev Account (from ewoq key - 100M LUX)"
|
||||
}
|
||||
},
|
||||
"nonce": "0x0",
|
||||
"timestamp": "0x0",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0xb71b00",
|
||||
"difficulty": "0x0",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"coinbase": "0x0000000000000000000000000000000000000000",
|
||||
"number": "0x0",
|
||||
"gasUsed": "0x0",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
{
|
||||
"allocations": [
|
||||
{
|
||||
"ethAddr": "0xf785fa547ae9ccf3d3ca5362762a347a4c41051a",
|
||||
"luxAddr": "X-local1qsrd262r5w9dswv2wwzj0un79ncpwvdg4t97s8",
|
||||
"initialAmount": 1000000000000000,
|
||||
"unlockSchedule": []
|
||||
},
|
||||
{
|
||||
"ethAddr": "0xf785fa547ae9ccf3d3ca5362762a347a4c41051a",
|
||||
"luxAddr": "P-local1qsrd262r5w9dswv2wwzj0un79ncpwvdg4t97s8",
|
||||
"initialAmount": 1100000000000000,
|
||||
"unlockSchedule": []
|
||||
},
|
||||
{
|
||||
"ethAddr": "0xf4b5be7a6dea583da4cddcda4d9b3afd51684b6e",
|
||||
"luxAddr": "X-local1x7rtxtkh6538hqdwp624tryu33853phfud4ch0",
|
||||
"initialAmount": 1000000000000000,
|
||||
"unlockSchedule": []
|
||||
},
|
||||
{
|
||||
"ethAddr": "0xf4b5be7a6dea583da4cddcda4d9b3afd51684b6e",
|
||||
"luxAddr": "P-local1x7rtxtkh6538hqdwp624tryu33853phfud4ch0",
|
||||
"initialAmount": 1100000000000000,
|
||||
"unlockSchedule": []
|
||||
},
|
||||
{
|
||||
"ethAddr": "0x202335dd1c21c9b90277f8bca78db98db0bbc293",
|
||||
"luxAddr": "X-local184tuwjk0np3n4mm2d7d7y0f397ksmzy0lu0qel",
|
||||
"initialAmount": 1000000000000000,
|
||||
"unlockSchedule": []
|
||||
},
|
||||
{
|
||||
"ethAddr": "0x202335dd1c21c9b90277f8bca78db98db0bbc293",
|
||||
"luxAddr": "P-local184tuwjk0np3n4mm2d7d7y0f397ksmzy0lu0qel",
|
||||
"initialAmount": 1100000000000000,
|
||||
"unlockSchedule": []
|
||||
},
|
||||
{
|
||||
"ethAddr": "0xf6e9eae8211e4a4532a480100068783d9cb75c3b",
|
||||
"luxAddr": "X-local14pfm4k9k3mx30yf2kpd9cvfcs7mzkkn68kew3k",
|
||||
"initialAmount": 1000000000000000,
|
||||
"unlockSchedule": []
|
||||
},
|
||||
{
|
||||
"ethAddr": "0xf6e9eae8211e4a4532a480100068783d9cb75c3b",
|
||||
"luxAddr": "P-local14pfm4k9k3mx30yf2kpd9cvfcs7mzkkn68kew3k",
|
||||
"initialAmount": 1100000000000000,
|
||||
"unlockSchedule": []
|
||||
},
|
||||
{
|
||||
"ethAddr": "0x32712d64011e135a24bb13b5934b53b6d6790812",
|
||||
"luxAddr": "X-local15953qfaga3w5txjmr0fl4clu37kak0ahkzhanc",
|
||||
"initialAmount": 1000000000000000,
|
||||
"unlockSchedule": []
|
||||
},
|
||||
{
|
||||
"ethAddr": "0x32712d64011e135a24bb13b5934b53b6d6790812",
|
||||
"luxAddr": "P-local15953qfaga3w5txjmr0fl4clu37kak0ahkzhanc",
|
||||
"initialAmount": 1100000000000000,
|
||||
"unlockSchedule": []
|
||||
},
|
||||
{
|
||||
"ethAddr": "0x0000000000000000000000000000000000000001",
|
||||
"luxAddr": "P-local1jqg73zp9r2c98daarnd4nrd5l80dj3c56acgey",
|
||||
"initialAmount": 500000000000000,
|
||||
"unlockSchedule": []
|
||||
},
|
||||
{
|
||||
"ethAddr": "0x0000000000000000000000000000000000000001",
|
||||
"luxAddr": "X-local1jqg73zp9r2c98daarnd4nrd5l80dj3c56acgey",
|
||||
"initialAmount": 500000000000000,
|
||||
"unlockSchedule": []
|
||||
},
|
||||
{
|
||||
"ethAddr": "0x35D64Ff3f618f7a17DF34DCb21be375A4686a8de",
|
||||
"luxAddr": "X-local1kazfeaqjtu2a6w3s4kt9qutw5up3jfyg6lurlx",
|
||||
"initialAmount": 500000000000000,
|
||||
"unlockSchedule": []
|
||||
},
|
||||
{
|
||||
"ethAddr": "0x35D64Ff3f618f7a17DF34DCb21be375A4686a8de",
|
||||
"luxAddr": "P-local1kazfeaqjtu2a6w3s4kt9qutw5up3jfyg6lurlx",
|
||||
"initialAmount": 500000000000000,
|
||||
"unlockSchedule": []
|
||||
}
|
||||
],
|
||||
"cChainGenesis": "{\"config\":{\"chainId\":1337,\"homesteadBlock\":0,\"eip150Block\":0,\"eip155Block\":0,\"eip158Block\":0,\"byzantiumBlock\":0,\"constantinopleBlock\":0,\"petersburgBlock\":0,\"istanbulBlock\":0,\"muirGlacierBlock\":0,\"berlinBlock\":0,\"londonBlock\":0,\"arrowGlacierBlock\":0,\"grayGlacierBlock\":0,\"mergeNetsplitBlock\":0,\"shanghaiTime\":0,\"cancunTime\":0,\"terminalTotalDifficulty\":0,\"chainEVMTimestamp\":0,\"durangoTimestamp\":0,\"etnaTimestamp\":0,\"fortunaTimestamp\":0,\"graniteTimestamp\":0,\"blobSchedule\":{\"cancun\":{\"target\":3,\"max\":6,\"baseFeeUpdateFraction\":3338477}},\"feeConfig\":{\"gasLimit\":15000000,\"targetBlockRate\":1,\"minBaseFee\":1000000000,\"targetGas\":60000000,\"baseFeeChangeDenominator\":36,\"minBlockGasCost\":0,\"maxBlockGasCost\":1000000,\"blockGasCostStep\":200000},\"warpConfig\":{\"blockTimestamp\":0,\"quorumNumerator\":67,\"requirePrimaryNetworkSigners\":false}},\"nonce\":\"0x0\",\"timestamp\":\"0x0\",\"extraData\":\"0x\",\"gasLimit\":\"0xe4e1c0\",\"difficulty\":\"0x0\",\"mixHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"coinbase\":\"0x0000000000000000000000000000000000000000\",\"alloc\":{\"0200000000000000000000000000000000000005\":{\"balance\":\"0x0\",\"code\":\"0x01\",\"nonce\":\"0x1\"},\"9011E888251AB053B7bD1cdB598Db4f9DEd94714\":{\"balance\":\"0x193e5939a08ce9dbd480000000\"},\"35D64Ff3f618f7a17DF34DCb21be375A4686a8de\":{\"balance\":\"0x19d971e4fe8401e74000000\"},\"f39Fd6e51aad88F6F4ce6aB8827279cffFb92266\":{\"balance\":\"0x21e19e0c9bab2400000\"},\"70997970C51812dc3A010C7d01b50e0d17dc79C8\":{\"balance\":\"0x21e19e0c9bab2400000\"},\"3C44CdDdB6a900fa2b585dd299e03d12FA4293BC\":{\"balance\":\"0x21e19e0c9bab2400000\"},\"90F79bf6EB2c4f870365E785982E1f101E93b906\":{\"balance\":\"0x21e19e0c9bab2400000\"},\"15d34AAf54267DB7D7c367839AAf71A00a2C6A65\":{\"balance\":\"0x21e19e0c9bab2400000\"},\"9965507D1a55bcC2695C58ba16FB37d819B0A4dc\":{\"balance\":\"0x21e19e0c9bab2400000\"},\"976EA74026E726554dB657fA54763abd0C3a0aa9\":{\"balance\":\"0x21e19e0c9bab2400000\"},\"14dC79964da2C08b23698B3D3cc7Ca32193d9955\":{\"balance\":\"0x21e19e0c9bab2400000\"},\"23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f\":{\"balance\":\"0x21e19e0c9bab2400000\"},\"a0Ee7A142d267C1f36714E4a8F75612F20a79720\":{\"balance\":\"0x21e19e0c9bab2400000\"}},\"number\":\"0x0\",\"gasUsed\":\"0x0\",\"parentHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"baseFeePerGas\":\"0x3b9aca00\"}",
|
||||
"initialStakeDuration": 31536000,
|
||||
"initialStakeDurationOffset": 5400,
|
||||
"initialStakedFunds": [
|
||||
"P-local1qsrd262r5w9dswv2wwzj0un79ncpwvdg4t97s8",
|
||||
"P-local1x7rtxtkh6538hqdwp624tryu33853phfud4ch0",
|
||||
"P-local184tuwjk0np3n4mm2d7d7y0f397ksmzy0lu0qel",
|
||||
"P-local14pfm4k9k3mx30yf2kpd9cvfcs7mzkkn68kew3k",
|
||||
"P-local15953qfaga3w5txjmr0fl4clu37kak0ahkzhanc"
|
||||
],
|
||||
"initialStakers": [
|
||||
{
|
||||
"nodeID": "NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg",
|
||||
"rewardAddress": "P-lux1c7wevm4667l4umtzh93r25wpxlpsadkhka6gv6",
|
||||
"delegationFee": 20000,
|
||||
"signer": {
|
||||
"publicKey": "0xb3ebbe748a1f06d19ee25d4e345ba8d6b5a426498a140c2519b518e3e6224abd7895075892f361acf24c10af968bc7de",
|
||||
"proofOfPossession": "0xa2569a137e65d3a507c4f1f7ffac74ec05916ba44dfbbb84d42c2736a2bc1e8be14038d3aeeeac7c78e19ecdde69d830051959f22559641a3f9e42377d7f64580acdc383c5c9e22f7f1114712a543c6997d6dc59c88555423497d9fff41fa79a"
|
||||
},
|
||||
"weight": 10000000000
|
||||
},
|
||||
{
|
||||
"nodeID": "NodeID-MFrZFVCXPv5iCn6M9K6XduxGTYp891xXZ",
|
||||
"rewardAddress": "P-lux1c7wevm4667l4umtzh93r25wpxlpsadkhka6gv6",
|
||||
"delegationFee": 20000,
|
||||
"signer": {
|
||||
"publicKey": "0x8b49c4259529a801cc961c72248773b550379ff718a49f4ccc0e1f2ac338fe204432329aa1712f408f97eee12b22dd05",
|
||||
"proofOfPossession": "0xaf92674c3615d462527675da121b06023b116ddebc6e163919e562ab7cbe6adf20515cd2fc17c3e2d2d59953f285229e15f04302187bef5a4aa3ebdea1d18bf34047be654dd12491e882fb90b17b3cbde99ad43fc5cd0c26828bbe49a4b9456c"
|
||||
},
|
||||
"weight": 10000000000
|
||||
},
|
||||
{
|
||||
"nodeID": "NodeID-NFBbbJ4qCmNaCzeW7sxErhvWqvEQMnYcN",
|
||||
"rewardAddress": "P-lux1c7wevm4667l4umtzh93r25wpxlpsadkhka6gv6",
|
||||
"delegationFee": 20000,
|
||||
"signer": {
|
||||
"publicKey": "0xb20ab07ea5cf8b77ab50f2071a6f1d2aab693c8bd89761430cd29de9fa0dbae83a32c7697d59ff1b06995b1596c16fa6",
|
||||
"proofOfPossession": "0xa113145564d7ac540fade2526938fbd33233957901111328c86c4cbe7ed25f678173d95c54b3342fd7b723c3ed813f2b04fecf4a317205ce65638bb34bb58bb6746e74c63e6cbe5cb25c103b290ed270146b756d3901c8b0f99d083a44572c79"
|
||||
},
|
||||
"weight": 10000000000
|
||||
},
|
||||
{
|
||||
"nodeID": "NodeID-GWPcbFJZFfZreETSoWjPimr846mXEKCtu",
|
||||
"rewardAddress": "P-lux1c7wevm4667l4umtzh93r25wpxlpsadkhka6gv6",
|
||||
"delegationFee": 20000,
|
||||
"signer": {
|
||||
"publicKey": "0xb43f74196c2b9e9980f815a99288ef76166b42e93663dcbce3526f9652cdb58b31a3d68798b08fb9806024620ca1d6cd",
|
||||
"proofOfPossession": "0x8880500af123806295355eadbb36084619de729e2c4ac057f73784eb806ae19b4738550617fa8d6820cf1a352e19af6d042e140b3c967b8573e063c1e5aabce50dc2156e9caa6428b58e06a3624ac8170793ff4b4095397c1c981fbb2383dae6"
|
||||
},
|
||||
"weight": 10000000000
|
||||
},
|
||||
{
|
||||
"nodeID": "NodeID-P7oB2McjBGgW2NXXWVYjV8JEDFoW9xDE5",
|
||||
"rewardAddress": "P-lux1c7wevm4667l4umtzh93r25wpxlpsadkhka6gv6",
|
||||
"delegationFee": 20000,
|
||||
"signer": {
|
||||
"publicKey": "0xa37e938a8e1fb71286ae1a9dc81585bae5c63d8cae3b95160ce9acce621c8ee64afa0491a2c757ba24d8be2da1052090",
|
||||
"proofOfPossession": "0x8e01fe13a6fa3ca5be4f78230b0d03ffff4741f186ddebf9a04819e8011eb9c1b81debad2e52a35ec89f4dfc517884b9075e46968d6c735f17cca3ab5c07a2cdc181233fcd8e9260d9fa7edc12572dff13972c9b3233f34258fb34d9e6b732ba"
|
||||
},
|
||||
"weight": 10000000000
|
||||
}
|
||||
],
|
||||
"message": "Lux et Libertas",
|
||||
"networkID": 1337,
|
||||
"startTime": 1766708400,
|
||||
"dChainGenesis": "{\"version\":1,\"message\":\"Lux Chain Genesis\"}",
|
||||
"qChainGenesis": "{\"version\":1,\"message\":\"Lux Chain Genesis\"}",
|
||||
"aChainGenesis": "{\"version\":1,\"message\":\"Lux Chain Genesis\"}",
|
||||
"bChainGenesis": "{\"version\":1,\"message\":\"Lux Chain Genesis\"}",
|
||||
"tChainGenesis": "{\"version\":1,\"message\":\"Lux Chain Genesis\"}",
|
||||
"zChainGenesis": "{\"version\":1,\"message\":\"Lux Chain Genesis\"}",
|
||||
"gChainGenesis": "{\"version\":1,\"message\":\"Lux Chain Genesis\"}",
|
||||
"kChainGenesis": "{\"version\":1,\"message\":\"Lux Chain Genesis\"}"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"networkID": 1337,
|
||||
"message": "Omni Lucem Petunt",
|
||||
"startTime": 1766708400
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
{
|
||||
"allocations": [
|
||||
{
|
||||
"ethAddr": "0xb3d82b1367d362de99ab59a658165aff520cbd4d",
|
||||
"initialAmount": 0,
|
||||
"luxAddr": "X-custom1g65uqn6t77p656w64023nh8nd9updzmxwd59gh",
|
||||
"unlockSchedule": [
|
||||
{
|
||||
"amount": 10000000000000,
|
||||
"locktime": 1633824000
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"ethAddr": "0xb3d82b1367d362de99ab59a658165aff520cbd4d",
|
||||
"initialAmount": 300000000000000,
|
||||
"luxAddr": "X-custom18jma8ppw3nhx5r4ap8clazz0dps7rv5u9xde7p",
|
||||
"unlockSchedule": [
|
||||
{
|
||||
"amount": 20000000000000
|
||||
},
|
||||
{
|
||||
"amount": 10000000000000,
|
||||
"locktime": 1633824000
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"ethAddr": "0xb3d82b1367d362de99ab59a658165aff520cbd4d",
|
||||
"initialAmount": 10000000000000,
|
||||
"luxAddr": "X-custom16045mxr3s2cjycqe2xfluk304xv3ezhkhsvkpr",
|
||||
"unlockSchedule": [
|
||||
{
|
||||
"amount": 10000000000000,
|
||||
"locktime": 1633824000
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"initialStakeDuration": 31536000,
|
||||
"initialStakeDurationOffset": 5400,
|
||||
"initialStakedFunds": [
|
||||
"X-custom1g65uqn6t77p656w64023nh8nd9updzmxwd59gh"
|
||||
],
|
||||
"initialStakers": [
|
||||
{
|
||||
"delegationFee": 1000000,
|
||||
"nodeID": "NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg",
|
||||
"rewardAddress": "X-custom18jma8ppw3nhx5r4ap8clazz0dps7rv5u9xde7p",
|
||||
"signer": {
|
||||
"proofOfPossession": "0xa2569a137e65d3a507c4f1f7ffac74ec05916ba44dfbbb84d42c2736a2bc1e8be14038d3aeeeac7c78e19ecdde69d830051959f22559641a3f9e42377d7f64580acdc383c5c9e22f7f1114712a543c6997d6dc59c88555423497d9fff41fa79a",
|
||||
"publicKey": "0xb3ebbe748a1f06d19ee25d4e345ba8d6b5a426498a140c2519b518e3e6224abd7895075892f361acf24c10af968bc7de"
|
||||
}
|
||||
},
|
||||
{
|
||||
"delegationFee": 500000,
|
||||
"nodeID": "NodeID-MFrZFVCXPv5iCn6M9K6XduxGTYp891xXZ",
|
||||
"rewardAddress": "X-custom18jma8ppw3nhx5r4ap8clazz0dps7rv5u9xde7p",
|
||||
"signer": {
|
||||
"proofOfPossession": "0xaf92674c3615d462527675da121b06023b116ddebc6e163919e562ab7cbe6adf20515cd2fc17c3e2d2d59953f285229e15f04302187bef5a4aa3ebdea1d18bf34047be654dd12491e882fb90b17b3cbde99ad43fc5cd0c26828bbe49a4b9456c",
|
||||
"publicKey": "0x8b49c4259529a801cc961c72248773b550379ff718a49f4ccc0e1f2ac338fe204432329aa1712f408f97eee12b22dd05"
|
||||
}
|
||||
},
|
||||
{
|
||||
"delegationFee": 250000,
|
||||
"nodeID": "NodeID-NFBbbJ4qCmNaCzeW7sxErhvWqvEQMnYcN",
|
||||
"rewardAddress": "X-custom18jma8ppw3nhx5r4ap8clazz0dps7rv5u9xde7p",
|
||||
"signer": {
|
||||
"proofOfPossession": "0xa113145564d7ac540fade2526938fbd33233957901111328c86c4cbe7ed25f678173d95c54b3342fd7b723c3ed813f2b04fecf4a317205ce65638bb34bb58bb6746e74c63e6cbe5cb25c103b290ed270146b756d3901c8b0f99d083a44572c79",
|
||||
"publicKey": "0xb20ab07ea5cf8b77ab50f2071a6f1d2aab693c8bd89761430cd29de9fa0dbae83a32c7697d59ff1b06995b1596c16fa6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"delegationFee": 125000,
|
||||
"nodeID": "NodeID-GWPcbFJZFfZreETSoWjPimr846mXEKCtu",
|
||||
"rewardAddress": "X-custom18jma8ppw3nhx5r4ap8clazz0dps7rv5u9xde7p",
|
||||
"signer": {
|
||||
"proofOfPossession": "0x8880500af123806295355eadbb36084619de729e2c4ac057f73784eb806ae19b4738550617fa8d6820cf1a352e19af6d042e140b3c967b8573e063c1e5aabce50dc2156e9caa6428b58e06a3624ac8170793ff4b4095397c1c981fbb2383dae6",
|
||||
"publicKey": "0xb43f74196c2b9e9980f815a99288ef76166b42e93663dcbce3526f9652cdb58b31a3d68798b08fb9806024620ca1d6cd"
|
||||
}
|
||||
},
|
||||
{
|
||||
"delegationFee": 62500,
|
||||
"nodeID": "NodeID-P7oB2McjBGgW2NXXWVYjV8JEDFoW9xDE5",
|
||||
"rewardAddress": "X-custom18jma8ppw3nhx5r4ap8clazz0dps7rv5u9xde7p",
|
||||
"signer": {
|
||||
"proofOfPossession": "0x8e01fe13a6fa3ca5be4f78230b0d03ffff4741f186ddebf9a04819e8011eb9c1b81debad2e52a35ec89f4dfc517884b9075e46968d6c735f17cca3ab5c07a2cdc181233fcd8e9260d9fa7edc12572dff13972c9b3233f34258fb34d9e6b732ba",
|
||||
"publicKey": "0xa37e938a8e1fb71286ae1a9dc81585bae5c63d8cae3b95160ce9acce621c8ee64afa0491a2c757ba24d8be2da1052090"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
{
|
||||
"_comment": "Dev genesis - 5 accounts derived from 'light light light light light light light light light light light energy'",
|
||||
"alloc": {
|
||||
"0200000000000000000000000000000000000005": {
|
||||
"balance": "0x0",
|
||||
"code": "0x01",
|
||||
"nonce": "0x1"
|
||||
},
|
||||
"0x35D64Ff3f618f7a17DF34DCb21be375A4686a8de": {
|
||||
"balance": "0x193e5939a08ce9dbd480000000",
|
||||
"_comment": "Account 0 (treasury) - 2T LUX"
|
||||
},
|
||||
"0xdAF82928dE0ABBAE133322020B253283d335d3A8": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000",
|
||||
"_comment": "Account 1 - 1B LUX"
|
||||
},
|
||||
"0xBb5D7C55DbbB353f9e7667dbCC43B228B857998a": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000",
|
||||
"_comment": "Account 2 - 1B LUX"
|
||||
},
|
||||
"0x6828eAa708F40c11C121C25BdbB4a6fd1415fB24": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000",
|
||||
"_comment": "Account 3 - 1B LUX"
|
||||
},
|
||||
"0xa238C9E15C4AD9b10441fe9f7afFa6A131F4Cc37": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000",
|
||||
"_comment": "Account 4 - 1B LUX"
|
||||
},
|
||||
"0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC": {
|
||||
"balance": "0x52B7D2DCC80CD2E4000000",
|
||||
"_comment": "Pars Test Address - 100M LUX"
|
||||
}
|
||||
},
|
||||
"baseFeePerGas": "0x3b9aca00",
|
||||
"coinbase": "0x0000000000000000000000000000000000000000",
|
||||
"config": {
|
||||
"arrowGlacierBlock": 0,
|
||||
"berlinBlock": 0,
|
||||
"byzantiumBlock": 0,
|
||||
"cancunTime": 253399622400,
|
||||
"chainId": 96370,
|
||||
"constantinopleBlock": 0,
|
||||
"durangoTimestamp": 0,
|
||||
"eip150Block": 0,
|
||||
"eip155Block": 0,
|
||||
"eip158Block": 0,
|
||||
"etnaTimestamp": 253399622400,
|
||||
"feeConfig": {
|
||||
"baseFeeChangeDenominator": 36,
|
||||
"blockGasCostStep": 200000,
|
||||
"gasLimit": 15000000,
|
||||
"maxBlockGasCost": 1000000,
|
||||
"minBaseFee": 1000000000,
|
||||
"minBlockGasCost": 0,
|
||||
"targetBlockRate": 1,
|
||||
"targetGas": 60000000
|
||||
},
|
||||
"fortunaTimestamp": 253399622400,
|
||||
"graniteTimestamp": 253399622400,
|
||||
"grayGlacierBlock": 0,
|
||||
"homesteadBlock": 0,
|
||||
"istanbulBlock": 0,
|
||||
"londonBlock": 0,
|
||||
"mergeNetsplitBlock": 0,
|
||||
"muirGlacierBlock": 0,
|
||||
"petersburgBlock": 0,
|
||||
"shanghaiTime": 0,
|
||||
"subnetEVMTimestamp": 0,
|
||||
"terminalTotalDifficulty": 0,
|
||||
"warpConfig": {
|
||||
"blockTimestamp": 0,
|
||||
"quorumNumerator": 67,
|
||||
"requirePrimaryNetworkSigners": false
|
||||
}
|
||||
},
|
||||
"difficulty": "0x0",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0xe4e1c0",
|
||||
"gasUsed": "0x0",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0",
|
||||
"number": "0x0",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"timestamp": "0x0"
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
{
|
||||
"_comment": "Dev genesis - 5 accounts derived from 'light light light light light light light light light light light energy'",
|
||||
"allocations": [
|
||||
{
|
||||
"_comment": "Account 0 (treasury) - X-Chain 1B LUX",
|
||||
"ethAddr": "0x35D64Ff3f618f7a17DF34DCb21be375A4686a8de",
|
||||
"initialAmount": 1000000000000000,
|
||||
"luxAddr": "X-dev1kazfeaqjtu2a6w3s4kt9qutw5up3jfygthl240",
|
||||
"unlockSchedule": []
|
||||
},
|
||||
{
|
||||
"_comment": "Account 0 (treasury) - P-Chain stake (1.1B)",
|
||||
"ethAddr": "0x35D64Ff3f618f7a17DF34DCb21be375A4686a8de",
|
||||
"initialAmount": 1100000000000000,
|
||||
"luxAddr": "P-dev1kazfeaqjtu2a6w3s4kt9qutw5up3jfygthl240",
|
||||
"unlockSchedule": []
|
||||
},
|
||||
{
|
||||
"_comment": "Account 1 - X-Chain 1B LUX",
|
||||
"ethAddr": "0xdAF82928dE0ABBAE133322020B253283d335d3A8",
|
||||
"initialAmount": 1000000000000000,
|
||||
"luxAddr": "X-dev16wzm6wh4cr3qfcgnuxcjt0rxyq26c508g5vgjk",
|
||||
"unlockSchedule": []
|
||||
},
|
||||
{
|
||||
"_comment": "Account 1 - P-Chain stake (1.1B)",
|
||||
"ethAddr": "0xdAF82928dE0ABBAE133322020B253283d335d3A8",
|
||||
"initialAmount": 1100000000000000,
|
||||
"luxAddr": "P-dev16wzm6wh4cr3qfcgnuxcjt0rxyq26c508g5vgjk",
|
||||
"unlockSchedule": []
|
||||
},
|
||||
{
|
||||
"_comment": "Account 2 - X-Chain 1B LUX",
|
||||
"ethAddr": "0xBb5D7C55DbbB353f9e7667dbCC43B228B857998a",
|
||||
"initialAmount": 1000000000000000,
|
||||
"luxAddr": "X-dev169tr6276y5lthgxk9qlzpdh97h2y2vnex4j8vh",
|
||||
"unlockSchedule": []
|
||||
},
|
||||
{
|
||||
"_comment": "Account 2 - P-Chain stake (1.1B)",
|
||||
"ethAddr": "0xBb5D7C55DbbB353f9e7667dbCC43B228B857998a",
|
||||
"initialAmount": 1100000000000000,
|
||||
"luxAddr": "P-dev169tr6276y5lthgxk9qlzpdh97h2y2vnex4j8vh",
|
||||
"unlockSchedule": []
|
||||
},
|
||||
{
|
||||
"_comment": "Account 3 - X-Chain 1B LUX",
|
||||
"ethAddr": "0x6828eAa708F40c11C121C25BdbB4a6fd1415fB24",
|
||||
"initialAmount": 1000000000000000,
|
||||
"luxAddr": "X-dev14ex0w5epwtj4ddlq7e7yzmkcyqyxf6feztq75c",
|
||||
"unlockSchedule": []
|
||||
},
|
||||
{
|
||||
"_comment": "Account 3 - P-Chain stake (1.1B)",
|
||||
"ethAddr": "0x6828eAa708F40c11C121C25BdbB4a6fd1415fB24",
|
||||
"initialAmount": 1100000000000000,
|
||||
"luxAddr": "P-dev14ex0w5epwtj4ddlq7e7yzmkcyqyxf6feztq75c",
|
||||
"unlockSchedule": []
|
||||
},
|
||||
{
|
||||
"_comment": "Account 4 - X-Chain 1B LUX",
|
||||
"ethAddr": "0xa238C9E15C4AD9b10441fe9f7afFa6A131F4Cc37",
|
||||
"initialAmount": 1000000000000000,
|
||||
"luxAddr": "X-dev1f8yf38fr82sz6adlaev7l7gxlyxydstf7xeclg",
|
||||
"unlockSchedule": []
|
||||
},
|
||||
{
|
||||
"_comment": "Account 4 - P-Chain stake (1.1B)",
|
||||
"ethAddr": "0xa238C9E15C4AD9b10441fe9f7afFa6A131F4Cc37",
|
||||
"initialAmount": 1100000000000000,
|
||||
"luxAddr": "P-dev1f8yf38fr82sz6adlaev7l7gxlyxydstf7xeclg",
|
||||
"unlockSchedule": []
|
||||
}
|
||||
],
|
||||
"initialStakeDuration": 31536000,
|
||||
"initialStakeDurationOffset": 5400,
|
||||
"initialStakedFunds": [
|
||||
"P-dev1kazfeaqjtu2a6w3s4kt9qutw5up3jfygthl240"
|
||||
],
|
||||
"initialStakers": [
|
||||
{
|
||||
"delegationFee": 20000,
|
||||
"nodeID": "NodeID-7Ws38xYCcVoZemNwbh12Mj1HKPr7y3A4r",
|
||||
"rewardAddress": "X-dev1kazfeaqjtu2a6w3s4kt9qutw5up3jfygthl240",
|
||||
"signer": null,
|
||||
"weight": 1000000000000000
|
||||
}
|
||||
],
|
||||
"message": "Lux et Libertas - Dev Mode",
|
||||
"networkID": 1337,
|
||||
"startTime": 0
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
[
|
||||
{
|
||||
"id": "NodeID-88PFzuGqGeUP4ULy7TVet6hcFWUrJMPpn",
|
||||
"ip": "164.90.247.220:9651"
|
||||
},
|
||||
{
|
||||
"id": "NodeID-DLV6Z1uQr3VkUTzMct37Hd5g2FrHoxP6M",
|
||||
"ip": "146.190.14.112:9651"
|
||||
},
|
||||
{
|
||||
"id": "NodeID-HVN3xX9QhA2dYZoPsuxCj8Etq5zvWkqxB",
|
||||
"ip": "24.199.68.130:9651"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,368 @@
|
||||
{
|
||||
"alloc": {
|
||||
"0200000000000000000000000000000000000005": {
|
||||
"balance": "0x0",
|
||||
"code": "0x01",
|
||||
"nonce": "0x1"
|
||||
},
|
||||
"0x9011E888251AB053B7bD1cdB598Db4f9DEd94714": {
|
||||
"balance": "0x193e5939a08ce9dbd480000000"
|
||||
},
|
||||
"0xEAbCC110fAcBfebabC66Ad6f9E7B67288e720B59": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x8d5081153aE1cfb41f5c932fe0b6Beb7E159cF84": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0xf8f12D0592e6d1bFe92ee16CaBCC4a6F26dAAe23": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0xFb66808f708e1d4D7D43a8c75596e84f94e06806": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x313CF291c069C58D6bd61B0D672673462B8951bD": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0xf7f52257a6143cE6BbD12A98eF2B0a3d0C648079": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0xCA92ad0C91bd8DE640B9dAFfEB338ac908725142": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0xB5B325df519eB58B7223d85aaeac8b56aB05f3d6": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0xcf5288bEe8d8F63511C389D5015185FDEDe30e54": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x16204223fe4470f4B1F1dA19A368dC815736a3d7": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x1B475A4C983DfE4f32bbA4dE8DA8fd2c37f3A2A6": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x1EB0b3E6cb2dE3B25b45d6e49C33e683271D7bd5": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x07283AA2c30523E9190A17b9598ff65599BAAe17": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x4f49A5bbe8A6f673BD06261E9CCf1D23a004FC4d": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x7337840B9d8cf2C78295FBC3373f6ceA9CB9F286": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0xE374867773455c36Db90776a6a4E4B5B21743763": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x2ba3d932FAAE51EBAF2173b78c874d422a0Cc186": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x97f688735eD6D896FE740CbFD26983767A4cb9AA": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0xB5c2a4867D6096F0F4e3ae76c48FD92C412154f0": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0xC25d00876B6bB80a64E3032C2872440Ebfaf5b80": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x5a777e9b95F3a1706017F9b6fDc501cEceD84248": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0xc19d8D7bc52C9F36DA48B0a80eb8015c7Add7916": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0xA105a413c349b175d8b445Da814A6a766843F6aC": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0xf5b14e0Ae37324877F8C03abe351C4743C95B5B4": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x4e256878ED0Aa6Aea20394015c6F66443a6047e0": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x111BDcfe8C5398C7764BC1220B99cA1EdBcF5AF7": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x9C864a2884eD27c376e499AA75f5B064c2Ef51a0": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x471217e35Cf573133b840c53E93Ed1EaCC51Ac1c": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x3690f692D17554F90a9Bf7819d5b987E605245fA": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x53163A5922f1531F5C98D69aC2Dd130660c3c855": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0xB5ACC73B22612BC1B99f98C6da7B3E175E8F7539": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x2099389940444768C7738D4C45FF965CF36ECa52": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x341813ADC2de5fa20982E592A136D0b26bD35228": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0xfc4e8955441c7DEf35c017d20127744736362164": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x1D2Eb1F0D268401B7C910d18e3F06fe1606964f7": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x314fac4Bd7D6a283985451202f25c6BcfaA037dE": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0xc1ED5cef75C4285614c8b2878C60A92454DdF5d1": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x3B084e285EEf960873f9070B916B8B857e30Cbe1": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x2E2Ba4324533fD6fB077450403D5fAb25Cd934f9": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x96B995fF4847Ffb5436132B4a86502C07789c7Bf": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x80924B34750C897c34c25b3bA13dBE95E3224EfD": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x1a845Da3412a6D6231AB0E85FaEd72c260Da55bC": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x71aECaCe53bC0849A14F0b0a10224B00F6153352": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x05CAE5842A08165d4d736dA9E49438574283aaE9": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x20d3f6B4aE88224Ab9CD8ddE689FFb646B14e4c1": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x80E6653cb1BA2a0302207A0a21521CAf81fcc763": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x8Da4C887aaFcEDDF2B4D543e538bE7215D3401c3": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x0698495959f5570420BAA955dae5eA2864933ACf": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x30341F789A0A8C81378Ec169f77125A76660662F": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x53bCA7fF5dd0441DB410bdC6FFD58703406434bf": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x5b72d3ff49B12c84a101D17ec98d87bf7B70b6bC": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x568AA29eD57e8575A794EC3e5FEe20f886411E1C": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0xcb4E59591354111EFB8C7b78543E1aDa3bEdd077": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x944aB0665466A10f3C7E82096e2A7a6afF128378": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x4315c1e150a8825403135Ab705738dFe04f32183": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x03B8Bd0A20b05AADC9E457A4c967f45eEeC163c4": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x66D94a1014Fa808eE9Eab3Ac38C0810ab7d0009d": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0xbB9bA2b3ECE6Ddc84a988D07d1d1Dd34b48C66f8": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0xAda3736A876A7fa4a454ab0605536e0A859D2A30": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x8C21B1Cb91C4FFf11f69f6B21103E2d4b003035c": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x980ca20F732c2217A3C7cc6cA252EbA56e088A61": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x721762eD0C130CE07d3Dc56A1fff1f26499bE8A6": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x8E22f2F37e7a5c643d883c956C31A4bCe08991F6": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x59f8F59199C0B3C6e752941ff67bd015E73829Ec": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0xe945dbB0bA699FCcA469153aC6937943014C2b6F": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x96cdbBe896a7f51342d8177ACE00eF3475FaADc3": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x3C334af45A2f41274aA63fd9348fAeBA8966e279": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x4F566Da11B0711010abD9181631f6DaFc1f0c6B8": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0xBB927d5e0b19631F6536B284444213CDDDB93Cdf": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0xf483a8a0Ee70147A18EBCFAd7Bf1DE0E48B1fA51": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x55AA096feBd81Cdf27A0c2dA2F857D3F3e7C55eF": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0xe8829fdc3c12Fd95a48dF248D028166d6C2d3598": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0xc805912a1e62828c0C36e674164e6A9E948722eF": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x02aCB0ee16fd2B1bF465F85D7Ee112fA80C055FC": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x3f39f5bfc9E99a7C7455fB101cdBdA70D0595160": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x9cF4d8Bf5F1EC1997510C2126441c813A8c7Cc6f": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x2458cB853c406cDda14C6aD1Ebc8b106A829529E": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x4154BD5e8E15822Ab96359d309166EC91f009248": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x22EbCe08E9f967F227F6E83d2eDB6b600f1370E3": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x1FCDa7bc336de60FAb3BE7B2188cefE5AA590CBD": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x6D84Cdd6a387584922c7FDB1f70acF146f25C04B": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0xCBac896B5DcDFB87f1ebf80A0D033a3Ef4f76f06": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x7a05645cAc4f3FDA00b100924562D0a9085A082D": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x8A19c91f810421d347DDBBE8158ab7F8693eae14": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x8C7B7ff7196515a8f3cf04bD9E8A33C4FFC8c9C3": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x8B8A5e59c1D430ab508dD6C45178f048C2aFAcEd": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x85a6ad52261990cf1E0e05D1dc471A8c4Dda94e7": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x43Ba86de6709D76Fa015E3cC3b7DDe31703504F8": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x1882D237E2FD5d4F3CCE29215317847D2b75f3eB": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0xAC994e2fcb6aE6078B27937B1b5374B891669fb8": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x49660D0A746255ACCfeE4aA004279F7B27179111": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x74aB2B6E01bDf141FA73BC2355b58ceaC38C0Ac9": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x7EDbd481118E071FE9c4b85e404c56CCC351292C": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x7792a945C604B6A8991bC37F0BBa48eD3220e8d8": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0xf106e8a89D982Dc13B25c933BEEfBcBa266ffF59": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x054D2e1dA5A1B5b9Da158acbA42Aed1d45F5CF02": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0x4e79e052C5130d3918Fcb9D6825158aeEeF4890e": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0xf5AbBd1b2384C16AA6A4bD114b399dE396c579A6": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"0xa8722395CaECe005f613644502d9655Ab8C42B17": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
}
|
||||
},
|
||||
"baseFeePerGas": "0x5d21dba00",
|
||||
"coinbase": "0x0000000000000000000000000000000000000000",
|
||||
"config": {
|
||||
"arrowGlacierBlock": 0,
|
||||
"berlinBlock": 0,
|
||||
"byzantiumBlock": 0,
|
||||
"cancunTime": 0,
|
||||
"chainId": 96370,
|
||||
"constantinopleBlock": 0,
|
||||
"durangoTimestamp": 0,
|
||||
"eip150Block": 0,
|
||||
"eip155Block": 0,
|
||||
"eip158Block": 0,
|
||||
"etnaTimestamp": 0,
|
||||
"blobSchedule": {
|
||||
"cancun": {
|
||||
"max": 6,
|
||||
"target": 3,
|
||||
"baseFeeUpdateFraction": 3338477
|
||||
}
|
||||
},
|
||||
"feeConfig": {
|
||||
"baseFeeChangeDenominator": 36,
|
||||
"blockGasCostStep": 200000,
|
||||
"gasLimit": 12000000,
|
||||
"maxBlockGasCost": 1000000,
|
||||
"minBaseFee": 25000000000,
|
||||
"minBlockGasCost": 0,
|
||||
"targetBlockRate": 2,
|
||||
"targetGas": 60000000
|
||||
},
|
||||
"fortunaTimestamp": 0,
|
||||
"graniteTimestamp": 0,
|
||||
"grayGlacierBlock": 0,
|
||||
"homesteadBlock": 0,
|
||||
"istanbulBlock": 0,
|
||||
"londonBlock": 0,
|
||||
"mergeNetsplitBlock": 0,
|
||||
"muirGlacierBlock": 0,
|
||||
"petersburgBlock": 0,
|
||||
"shanghaiTime": 0,
|
||||
"subnetEVMTimestamp": 0,
|
||||
"terminalTotalDifficulty": 0,
|
||||
"warpConfig": {
|
||||
"blockTimestamp": 0,
|
||||
"quorumNumerator": 67,
|
||||
"requirePrimaryNetworkSigners": false
|
||||
}
|
||||
},
|
||||
"difficulty": "0x0",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0xb71b00",
|
||||
"gasUsed": "0x0",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0",
|
||||
"number": "0x0",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"stateRoot": "0x2d1cedac263020c5c56ef962f6abe0da1f5217bdc6468f8c9258a0ea23699e80",
|
||||
"timestamp": "0x67259912"
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"networkID": 3,
|
||||
"message": "Lux et Libertas",
|
||||
"startTime": 1770739200
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,17 @@
|
||||
package configs
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestListEmbed(t *testing.T) {
|
||||
entries, err := embeddedGenesis.ReadDir("mainnet")
|
||||
if err != nil {
|
||||
t.Fatalf("Error reading mainnet dir: %v", err)
|
||||
}
|
||||
t.Logf("Files embedded in mainnet/:")
|
||||
for _, e := range entries {
|
||||
info, _ := e.Info()
|
||||
t.Logf(" - %s (%d bytes)", e.Name(), info.Size())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package configs
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetGenesisMainnet(t *testing.T) {
|
||||
data, err := GetGenesis(1) // mainnet
|
||||
if err != nil {
|
||||
t.Fatalf("Error: %v", err)
|
||||
}
|
||||
|
||||
var m map[string]interface{}
|
||||
if err := json.Unmarshal(data, &m); err != nil {
|
||||
t.Fatalf("Failed to parse: %v", err)
|
||||
}
|
||||
|
||||
allocs := m["allocations"].([]interface{})
|
||||
t.Logf("Total allocations: %d", len(allocs))
|
||||
|
||||
if len(allocs) == 0 {
|
||||
t.Error("No allocations found in mainnet genesis")
|
||||
}
|
||||
|
||||
// Verify each allocation has required fields
|
||||
for i, a := range allocs {
|
||||
alloc := a.(map[string]interface{})
|
||||
if _, ok := alloc["luxAddr"]; !ok {
|
||||
t.Errorf("Allocation %d missing luxAddr", i)
|
||||
}
|
||||
}
|
||||
|
||||
// Verify C-Chain genesis exists with treasury
|
||||
cchainIface, ok := m["cChainGenesis"]
|
||||
if !ok {
|
||||
t.Fatal("cChainGenesis not found in genesis")
|
||||
}
|
||||
|
||||
// cChainGenesis can be a string or object
|
||||
var cchain map[string]interface{}
|
||||
switch v := cchainIface.(type) {
|
||||
case string:
|
||||
if err := json.Unmarshal([]byte(v), &cchain); err != nil {
|
||||
t.Fatalf("Failed to parse cChainGenesis string: %v", err)
|
||||
}
|
||||
case map[string]interface{}:
|
||||
cchain = v
|
||||
default:
|
||||
t.Fatalf("Unexpected cChainGenesis type: %T", v)
|
||||
}
|
||||
|
||||
// Check that alloc section exists in C-Chain
|
||||
if alloc, ok := cchain["alloc"]; ok {
|
||||
allocMap := alloc.(map[string]interface{})
|
||||
t.Logf("C-Chain alloc entries: %d", len(allocMap))
|
||||
|
||||
// Check for treasury address (without 0x prefix, lowercase)
|
||||
treasuryAddr := "9011e888251ab053b7bd1cdb598db4f9ded94714"
|
||||
if _, found := allocMap[treasuryAddr]; found {
|
||||
t.Logf("Treasury found in C-Chain alloc: 0x%s", treasuryAddr)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
{
|
||||
"alloc": {
|
||||
"0200000000000000000000000000000000000005": {
|
||||
"balance": "0x0",
|
||||
"code": "0x01",
|
||||
"nonce": "0x1"
|
||||
},
|
||||
"9011E888251AB053B7bD1cdB598Db4f9DEd94714": {
|
||||
"balance": "0x193e5939a08ce9dbd480000000"
|
||||
}
|
||||
},
|
||||
"baseFeePerGas": "0x5d21dba00",
|
||||
"coinbase": "0x0000000000000000000000000000000000000000",
|
||||
"config": {
|
||||
"arrowGlacierBlock": 0,
|
||||
"berlinBlock": 0,
|
||||
"blobSchedule": {
|
||||
"cancun": {
|
||||
"baseFeeUpdateFraction": 3338477,
|
||||
"max": 6,
|
||||
"target": 3
|
||||
}
|
||||
},
|
||||
"byzantiumBlock": 0,
|
||||
"cancunTime": 253399622400,
|
||||
"chainId": 36964,
|
||||
"constantinopleBlock": 0,
|
||||
"durangoTimestamp": 0,
|
||||
"eip150Block": 0,
|
||||
"eip155Block": 0,
|
||||
"eip158Block": 0,
|
||||
"etnaTimestamp": 253399622400,
|
||||
"evmTimestamp": 0,
|
||||
"feeConfig": {
|
||||
"baseFeeChangeDenominator": 36,
|
||||
"blockGasCostStep": 200000,
|
||||
"gasLimit": 15000000,
|
||||
"maxBlockGasCost": 1000000,
|
||||
"minBaseFee": 25000000000,
|
||||
"minBlockGasCost": 0,
|
||||
"targetBlockRate": 2,
|
||||
"targetGas": 15000000
|
||||
},
|
||||
"fortunaTimestamp": 253399622400,
|
||||
"graniteTimestamp": 253399622400,
|
||||
"grayGlacierBlock": 0,
|
||||
"homesteadBlock": 0,
|
||||
"istanbulBlock": 0,
|
||||
"londonBlock": 0,
|
||||
"mergeNetsplitBlock": 0,
|
||||
"muirGlacierBlock": 0,
|
||||
"petersburgBlock": 0,
|
||||
"shanghaiTime": 0,
|
||||
"subnetEVMTimestamp": 0,
|
||||
"terminalTotalDifficulty": 0,
|
||||
"warpConfig": {
|
||||
"blockTimestamp": 0,
|
||||
"quorumNumerator": 67,
|
||||
"requirePrimaryNetworkSigners": false
|
||||
}
|
||||
},
|
||||
"difficulty": "0x0",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0xe4e1c0",
|
||||
"gasUsed": "0x0",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0",
|
||||
"number": "0x0",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"timestamp": "0x0"
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
{
|
||||
"alloc": {
|
||||
"0200000000000000000000000000000000000005": {
|
||||
"balance": "0x0",
|
||||
"code": "0x01",
|
||||
"nonce": "0x1"
|
||||
},
|
||||
"9011E888251AB053B7bD1cdB598Db4f9DEd94714": {
|
||||
"balance": "0x193e5939a08ce9dbd480000000"
|
||||
}
|
||||
},
|
||||
"baseFeePerGas": "0x5d21dba00",
|
||||
"coinbase": "0x0000000000000000000000000000000000000000",
|
||||
"config": {
|
||||
"arrowGlacierBlock": 0,
|
||||
"berlinBlock": 0,
|
||||
"blobSchedule": {
|
||||
"cancun": {
|
||||
"baseFeeUpdateFraction": 3338477,
|
||||
"max": 6,
|
||||
"target": 3
|
||||
}
|
||||
},
|
||||
"byzantiumBlock": 0,
|
||||
"cancunTime": 253399622400,
|
||||
"chainId": 36963,
|
||||
"constantinopleBlock": 0,
|
||||
"durangoTimestamp": 0,
|
||||
"eip150Block": 0,
|
||||
"eip155Block": 0,
|
||||
"eip158Block": 0,
|
||||
"etnaTimestamp": 253399622400,
|
||||
"evmTimestamp": 0,
|
||||
"feeConfig": {
|
||||
"baseFeeChangeDenominator": 36,
|
||||
"blockGasCostStep": 200000,
|
||||
"gasLimit": 15000000,
|
||||
"maxBlockGasCost": 1000000,
|
||||
"minBaseFee": 25000000000,
|
||||
"minBlockGasCost": 0,
|
||||
"targetBlockRate": 2,
|
||||
"targetGas": 15000000
|
||||
},
|
||||
"fortunaTimestamp": 253399622400,
|
||||
"graniteTimestamp": 253399622400,
|
||||
"grayGlacierBlock": 0,
|
||||
"homesteadBlock": 0,
|
||||
"istanbulBlock": 0,
|
||||
"londonBlock": 0,
|
||||
"mergeNetsplitBlock": 0,
|
||||
"muirGlacierBlock": 0,
|
||||
"petersburgBlock": 0,
|
||||
"shanghaiTime": 0,
|
||||
"subnetEVMTimestamp": 0,
|
||||
"terminalTotalDifficulty": 0,
|
||||
"warpConfig": {
|
||||
"blockTimestamp": 0,
|
||||
"quorumNumerator": 67,
|
||||
"requirePrimaryNetworkSigners": false
|
||||
}
|
||||
},
|
||||
"difficulty": "0x0",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0xe4e1c0",
|
||||
"gasUsed": "0x0",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0",
|
||||
"number": "0x0",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"timestamp": "0x0"
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
{
|
||||
"alloc": {
|
||||
"0200000000000000000000000000000000000005": {
|
||||
"balance": "0x0",
|
||||
"code": "0x01",
|
||||
"nonce": "0x1"
|
||||
},
|
||||
"9011E888251AB053B7bD1cdB598Db4f9DEd94714": {
|
||||
"balance": "0x193e5939a08ce9dbd480000000"
|
||||
}
|
||||
},
|
||||
"baseFeePerGas": "0x5d21dba00",
|
||||
"coinbase": "0x0000000000000000000000000000000000000000",
|
||||
"config": {
|
||||
"arrowGlacierBlock": 0,
|
||||
"berlinBlock": 0,
|
||||
"blobSchedule": {
|
||||
"cancun": {
|
||||
"baseFeeUpdateFraction": 3338477,
|
||||
"max": 6,
|
||||
"target": 3
|
||||
}
|
||||
},
|
||||
"byzantiumBlock": 0,
|
||||
"cancunTime": 253399622400,
|
||||
"chainId": 36962,
|
||||
"constantinopleBlock": 0,
|
||||
"durangoTimestamp": 0,
|
||||
"eip150Block": 0,
|
||||
"eip155Block": 0,
|
||||
"eip158Block": 0,
|
||||
"etnaTimestamp": 253399622400,
|
||||
"evmTimestamp": 0,
|
||||
"feeConfig": {
|
||||
"baseFeeChangeDenominator": 36,
|
||||
"blockGasCostStep": 200000,
|
||||
"gasLimit": 15000000,
|
||||
"maxBlockGasCost": 1000000,
|
||||
"minBaseFee": 25000000000,
|
||||
"minBlockGasCost": 0,
|
||||
"targetBlockRate": 2,
|
||||
"targetGas": 15000000
|
||||
},
|
||||
"fortunaTimestamp": 253399622400,
|
||||
"graniteTimestamp": 253399622400,
|
||||
"grayGlacierBlock": 0,
|
||||
"homesteadBlock": 0,
|
||||
"istanbulBlock": 0,
|
||||
"londonBlock": 0,
|
||||
"mergeNetsplitBlock": 0,
|
||||
"muirGlacierBlock": 0,
|
||||
"petersburgBlock": 0,
|
||||
"shanghaiTime": 0,
|
||||
"subnetEVMTimestamp": 0,
|
||||
"terminalTotalDifficulty": 0,
|
||||
"warpConfig": {
|
||||
"blockTimestamp": 0,
|
||||
"quorumNumerator": 67,
|
||||
"requirePrimaryNetworkSigners": false
|
||||
}
|
||||
},
|
||||
"difficulty": "0x0",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0xe4e1c0",
|
||||
"gasUsed": "0x0",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0",
|
||||
"number": "0x0",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"timestamp": "0x0"
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
{
|
||||
"_comment": "Local C-Chain genesis — 5 accounts from 'light light light light light light light light light light light energy' (BIP44 m/44'/9000'/0'/0/{0-4})",
|
||||
"config": {
|
||||
"chainId": 1337,
|
||||
"homesteadBlock": 0,
|
||||
"eip150Block": 0,
|
||||
"eip155Block": 0,
|
||||
"eip158Block": 0,
|
||||
"byzantiumBlock": 0,
|
||||
"constantinopleBlock": 0,
|
||||
"petersburgBlock": 0,
|
||||
"istanbulBlock": 0,
|
||||
"muirGlacierBlock": 0,
|
||||
"berlinBlock": 0,
|
||||
"londonBlock": 0,
|
||||
"arrowGlacierBlock": 0,
|
||||
"grayGlacierBlock": 0,
|
||||
"mergeNetsplitBlock": 0,
|
||||
"shanghaiTime": 0,
|
||||
"cancunTime": 0,
|
||||
"terminalTotalDifficulty": 0,
|
||||
"subnetEVMTimestamp": 0,
|
||||
"durangoTimestamp": 0,
|
||||
"etnaTimestamp": 0,
|
||||
"fortunaTimestamp": 0,
|
||||
"graniteTimestamp": 0,
|
||||
"blobSchedule": {
|
||||
"cancun": {
|
||||
"target": 3,
|
||||
"max": 6,
|
||||
"baseFeeUpdateFraction": 3338477
|
||||
}
|
||||
},
|
||||
"feeConfig": {
|
||||
"gasLimit": 15000000,
|
||||
"targetBlockRate": 1,
|
||||
"minBaseFee": 1000000000,
|
||||
"targetGas": 60000000,
|
||||
"baseFeeChangeDenominator": 36,
|
||||
"minBlockGasCost": 0,
|
||||
"maxBlockGasCost": 1000000,
|
||||
"blockGasCostStep": 200000
|
||||
},
|
||||
"warpConfig": {
|
||||
"blockTimestamp": 0,
|
||||
"quorumNumerator": 67,
|
||||
"requirePrimaryNetworkSigners": false
|
||||
}
|
||||
},
|
||||
"nonce": "0x0",
|
||||
"timestamp": "0x0",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0xe4e1c0",
|
||||
"difficulty": "0x0",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"coinbase": "0x0000000000000000000000000000000000000000",
|
||||
"alloc": {
|
||||
"0200000000000000000000000000000000000005": {
|
||||
"balance": "0x0",
|
||||
"code": "0x01",
|
||||
"nonce": "0x1"
|
||||
},
|
||||
"9011E888251AB053B7bD1cdB598Db4f9DEd94714": {
|
||||
"balance": "0x193e5939a08ce9dbd480000000"
|
||||
},
|
||||
"5369615110ca435bdf798f31c20ba6163d7b0a54": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"2e701063ccdffa2b1872c596222d8067d124d3ef": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"9030463eb1aaa563c8247468416cc0bf06347502": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"f77b06331152fd0e536de0af65688a6559c6f914": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"944fd51713652b9922690b7d06498fbf8742beac": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
}
|
||||
},
|
||||
"number": "0x0",
|
||||
"gasUsed": "0x0",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"baseFeePerGas": "0x3b9aca00"
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"networkID": 1337,
|
||||
"message": "Local Dev Network",
|
||||
"startTime": 0
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
[
|
||||
{
|
||||
"id": "NodeID-Mf3JfSY91oDwfBqf7rCLmhg4NDtDghw1f",
|
||||
"ip": "209.38.118.46:9631"
|
||||
},
|
||||
{
|
||||
"id": "NodeID-2TwSZ2oyeBK2mv7JiseEQ8m74rotDj4QR",
|
||||
"ip": "209.38.174.69:9631"
|
||||
},
|
||||
{
|
||||
"id": "NodeID-Ld9VFBQ9zGbd79z2vzaAqkQ3jHuqbtRpo",
|
||||
"ip": "24.144.69.101:9631"
|
||||
},
|
||||
{
|
||||
"id": "NodeID-8mY2fhUehN27v3LCU84BnnKEoeRfd2weC",
|
||||
"ip": "134.199.187.56:9631"
|
||||
},
|
||||
{
|
||||
"id": "NodeID-4smuFz5Z9cm8BEyQU1oPQ9nwgXyMgaNqu",
|
||||
"ip": "143.198.246.173:9631"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,75 @@
|
||||
{
|
||||
"config": {
|
||||
"chainId": 96369,
|
||||
"homesteadBlock": 0,
|
||||
"eip150Block": 0,
|
||||
"eip155Block": 0,
|
||||
"eip158Block": 0,
|
||||
"byzantiumBlock": 0,
|
||||
"constantinopleBlock": 0,
|
||||
"petersburgBlock": 0,
|
||||
"istanbulBlock": 0,
|
||||
"muirGlacierBlock": 0,
|
||||
"berlinBlock": 0,
|
||||
"londonBlock": 0,
|
||||
"arrowGlacierBlock": 0,
|
||||
"grayGlacierBlock": 0,
|
||||
"mergeNetsplitBlock": 0,
|
||||
"shanghaiTime": 0,
|
||||
"cancunTime": 253399622400,
|
||||
"terminalTotalDifficulty": 0,
|
||||
"banffBlockTimestamp": 253399622400,
|
||||
"cortinaBlockTimestamp": 253399622400,
|
||||
"durangoBlockTimestamp": 253399622400,
|
||||
"etnaTimestamp": 253399622400,
|
||||
"fortunaTimestamp": 253399622400,
|
||||
"graniteTimestamp": 253399622400,
|
||||
"blobSchedule": {
|
||||
"cancun": {
|
||||
"max": 6,
|
||||
"target": 3,
|
||||
"baseFeeUpdateFraction": 3338477
|
||||
}
|
||||
},
|
||||
"feeConfig": {
|
||||
"gasLimit": 12000000,
|
||||
"targetBlockRate": 2,
|
||||
"minBaseFee": 25000000000,
|
||||
"targetGas": 60000000,
|
||||
"baseFeeChangeDenominator": 36,
|
||||
"minBlockGasCost": 0,
|
||||
"maxBlockGasCost": 1000000,
|
||||
"blockGasCostStep": 200000
|
||||
},
|
||||
"subnetEVMTimestamp": 0,
|
||||
"warpConfig": {
|
||||
"blockTimestamp": 1730446786,
|
||||
"quorumNumerator": 67,
|
||||
"requirePrimaryNetworkSigners": false
|
||||
}
|
||||
},
|
||||
"nonce": "0x0",
|
||||
"timestamp": "0x672485c2",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0xb71b00",
|
||||
"difficulty": "0x0",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"coinbase": "0x0000000000000000000000000000000000000000",
|
||||
"alloc": {
|
||||
"0200000000000000000000000000000000000005": {
|
||||
"balance": "0x0",
|
||||
"nonce": "0x1",
|
||||
"code": "0x01"
|
||||
},
|
||||
"0x9011E888251AB053B7bD1cdB598Db4f9DEd94714": {
|
||||
"balance": "0x193e5939a08ce9dbd480000000"
|
||||
}
|
||||
},
|
||||
"stateRoot": "0x2d1cedac263020c5c56ef962f6abe0da1f5217bdc6468f8c9258a0ea23699e80",
|
||||
"number": "0x0",
|
||||
"gasUsed": "0x0",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"baseFeePerGas": "0x5d21dba00",
|
||||
"skipPostMergeFields": true,
|
||||
"genesisHash": "0x3f4fa2a0b0ce089f52bf0ae9199c75ffdd76ecafc987794050cb0d286f1ec61e"
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"networkID": 1,
|
||||
"message": "Lux et Libertas",
|
||||
"startTime": 1765573611
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,70 @@
|
||||
{
|
||||
"alloc": {
|
||||
"0200000000000000000000000000000000000005": {
|
||||
"balance": "0x0",
|
||||
"code": "0x01",
|
||||
"nonce": "0x1"
|
||||
},
|
||||
"0x9011E888251AB053B7bD1cdB598Db4f9DEd94714": {
|
||||
"balance": "0x16bcc41e90000000000000000"
|
||||
}
|
||||
},
|
||||
"baseFeePerGas": "0x5d21dba00",
|
||||
"coinbase": "0x0000000000000000000000000000000000000000",
|
||||
"config": {
|
||||
"arrowGlacierBlock": 0,
|
||||
"berlinBlock": 0,
|
||||
"blobSchedule": {
|
||||
"cancun": {
|
||||
"baseFeeUpdateFraction": 3338477,
|
||||
"max": 6,
|
||||
"target": 3
|
||||
}
|
||||
},
|
||||
"byzantiumBlock": 0,
|
||||
"cancunTime": 253399622400,
|
||||
"chainId": 494951,
|
||||
"constantinopleBlock": 0,
|
||||
"durangoTimestamp": 0,
|
||||
"eip150Block": 0,
|
||||
"eip155Block": 0,
|
||||
"eip158Block": 0,
|
||||
"etnaTimestamp": 253399622400,
|
||||
"feeConfig": {
|
||||
"baseFeeChangeDenominator": 36,
|
||||
"blockGasCostStep": 200000,
|
||||
"gasLimit": 12000000,
|
||||
"maxBlockGasCost": 1000000,
|
||||
"minBaseFee": 25000000000,
|
||||
"minBlockGasCost": 0,
|
||||
"targetBlockRate": 2,
|
||||
"targetGas": 60000000
|
||||
},
|
||||
"fortunaTimestamp": 253399622400,
|
||||
"graniteTimestamp": 253399622400,
|
||||
"grayGlacierBlock": 0,
|
||||
"homesteadBlock": 0,
|
||||
"istanbulBlock": 0,
|
||||
"londonBlock": 0,
|
||||
"mergeNetsplitBlock": 0,
|
||||
"muirGlacierBlock": 0,
|
||||
"petersburgBlock": 0,
|
||||
"shanghaiTime": 0,
|
||||
"subnetEVMTimestamp": 0,
|
||||
"terminalTotalDifficulty": 0,
|
||||
"warpConfig": {
|
||||
"blockTimestamp": 1768946194,
|
||||
"quorumNumerator": 67,
|
||||
"requirePrimaryNetworkSigners": false
|
||||
}
|
||||
},
|
||||
"difficulty": "0x0",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0xb71b00",
|
||||
"gasUsed": "0x0",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0",
|
||||
"number": "0x0",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"timestamp": "0x696ffa12"
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
{
|
||||
"alloc": {
|
||||
"0200000000000000000000000000000000000005": {
|
||||
"balance": "0x0",
|
||||
"code": "0x01",
|
||||
"nonce": "0x1"
|
||||
},
|
||||
"0x9011E888251AB053B7bD1cdB598Db4f9DEd94714": {
|
||||
"balance": "0x16bcc41e90000000000000000"
|
||||
}
|
||||
},
|
||||
"baseFeePerGas": "0x5d21dba00",
|
||||
"coinbase": "0x0000000000000000000000000000000000000000",
|
||||
"config": {
|
||||
"arrowGlacierBlock": 0,
|
||||
"berlinBlock": 0,
|
||||
"blobSchedule": {
|
||||
"cancun": {
|
||||
"baseFeeUpdateFraction": 3338477,
|
||||
"max": 6,
|
||||
"target": 3
|
||||
}
|
||||
},
|
||||
"byzantiumBlock": 0,
|
||||
"cancunTime": 253399622400,
|
||||
"chainId": 494949,
|
||||
"constantinopleBlock": 0,
|
||||
"durangoTimestamp": 0,
|
||||
"eip150Block": 0,
|
||||
"eip155Block": 0,
|
||||
"eip158Block": 0,
|
||||
"etnaTimestamp": 253399622400,
|
||||
"feeConfig": {
|
||||
"baseFeeChangeDenominator": 36,
|
||||
"blockGasCostStep": 200000,
|
||||
"gasLimit": 12000000,
|
||||
"maxBlockGasCost": 1000000,
|
||||
"minBaseFee": 25000000000,
|
||||
"minBlockGasCost": 0,
|
||||
"targetBlockRate": 2,
|
||||
"targetGas": 60000000
|
||||
},
|
||||
"fortunaTimestamp": 253399622400,
|
||||
"graniteTimestamp": 253399622400,
|
||||
"grayGlacierBlock": 0,
|
||||
"homesteadBlock": 0,
|
||||
"istanbulBlock": 0,
|
||||
"londonBlock": 0,
|
||||
"mergeNetsplitBlock": 0,
|
||||
"muirGlacierBlock": 0,
|
||||
"petersburgBlock": 0,
|
||||
"shanghaiTime": 0,
|
||||
"subnetEVMTimestamp": 0,
|
||||
"terminalTotalDifficulty": 0,
|
||||
"warpConfig": {
|
||||
"blockTimestamp": 1768946194,
|
||||
"quorumNumerator": 67,
|
||||
"requirePrimaryNetworkSigners": false
|
||||
}
|
||||
},
|
||||
"difficulty": "0x0",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0xb71b00",
|
||||
"gasUsed": "0x0",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0",
|
||||
"number": "0x0",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"timestamp": "0x696ffa12"
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
{
|
||||
"alloc": {
|
||||
"0200000000000000000000000000000000000005": {
|
||||
"balance": "0x0",
|
||||
"code": "0x01",
|
||||
"nonce": "0x1"
|
||||
},
|
||||
"0x9011E888251AB053B7bD1cdB598Db4f9DEd94714": {
|
||||
"balance": "0x16bcc41e90000000000000000"
|
||||
}
|
||||
},
|
||||
"baseFeePerGas": "0x5d21dba00",
|
||||
"coinbase": "0x0000000000000000000000000000000000000000",
|
||||
"config": {
|
||||
"arrowGlacierBlock": 0,
|
||||
"berlinBlock": 0,
|
||||
"blobSchedule": {
|
||||
"cancun": {
|
||||
"baseFeeUpdateFraction": 3338477,
|
||||
"max": 6,
|
||||
"target": 3
|
||||
}
|
||||
},
|
||||
"byzantiumBlock": 0,
|
||||
"cancunTime": 253399622400,
|
||||
"chainId": 494950,
|
||||
"constantinopleBlock": 0,
|
||||
"durangoTimestamp": 0,
|
||||
"eip150Block": 0,
|
||||
"eip155Block": 0,
|
||||
"eip158Block": 0,
|
||||
"etnaTimestamp": 253399622400,
|
||||
"feeConfig": {
|
||||
"baseFeeChangeDenominator": 36,
|
||||
"blockGasCostStep": 200000,
|
||||
"gasLimit": 12000000,
|
||||
"maxBlockGasCost": 1000000,
|
||||
"minBaseFee": 25000000000,
|
||||
"minBlockGasCost": 0,
|
||||
"targetBlockRate": 2,
|
||||
"targetGas": 60000000
|
||||
},
|
||||
"fortunaTimestamp": 253399622400,
|
||||
"graniteTimestamp": 253399622400,
|
||||
"grayGlacierBlock": 0,
|
||||
"homesteadBlock": 0,
|
||||
"istanbulBlock": 0,
|
||||
"londonBlock": 0,
|
||||
"mergeNetsplitBlock": 0,
|
||||
"muirGlacierBlock": 0,
|
||||
"petersburgBlock": 0,
|
||||
"shanghaiTime": 0,
|
||||
"subnetEVMTimestamp": 0,
|
||||
"terminalTotalDifficulty": 0,
|
||||
"warpConfig": {
|
||||
"blockTimestamp": 1768946194,
|
||||
"quorumNumerator": 67,
|
||||
"requirePrimaryNetworkSigners": false
|
||||
}
|
||||
},
|
||||
"difficulty": "0x0",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0xb71b00",
|
||||
"gasUsed": "0x0",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0",
|
||||
"number": "0x0",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"timestamp": "0x696ffa12"
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
{
|
||||
"alloc": {
|
||||
"0200000000000000000000000000000000000005": {
|
||||
"balance": "0x0",
|
||||
"code": "0x01",
|
||||
"nonce": "0x1"
|
||||
},
|
||||
"9011E888251AB053B7bD1cdB598Db4f9DEd94714": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
}
|
||||
},
|
||||
"baseFeePerGas": "0x5d21dba00",
|
||||
"coinbase": "0x0000000000000000000000000000000000000000",
|
||||
"config": {
|
||||
"arrowGlacierBlock": 0,
|
||||
"berlinBlock": 0,
|
||||
"blobSchedule": {
|
||||
"cancun": {
|
||||
"baseFeeUpdateFraction": 3338477,
|
||||
"max": 6,
|
||||
"target": 3
|
||||
}
|
||||
},
|
||||
"byzantiumBlock": 0,
|
||||
"cancunTime": 253399622400,
|
||||
"chainId": 36912,
|
||||
"constantinopleBlock": 0,
|
||||
"durangoTimestamp": 0,
|
||||
"eip150Block": 0,
|
||||
"eip155Block": 0,
|
||||
"eip158Block": 0,
|
||||
"etnaTimestamp": 253399622400,
|
||||
"feeConfig": {
|
||||
"baseFeeChangeDenominator": 36,
|
||||
"blockGasCostStep": 200000,
|
||||
"gasLimit": 12000000,
|
||||
"maxBlockGasCost": 1000000,
|
||||
"minBaseFee": 25000000000,
|
||||
"minBlockGasCost": 0,
|
||||
"targetBlockRate": 2,
|
||||
"targetGas": 60000000
|
||||
},
|
||||
"fortunaTimestamp": 253399622400,
|
||||
"graniteTimestamp": 253399622400,
|
||||
"grayGlacierBlock": 0,
|
||||
"homesteadBlock": 0,
|
||||
"istanbulBlock": 0,
|
||||
"londonBlock": 0,
|
||||
"mergeNetsplitBlock": 0,
|
||||
"muirGlacierBlock": 0,
|
||||
"petersburgBlock": 0,
|
||||
"shanghaiTime": 0,
|
||||
"subnetEVMTimestamp": 0,
|
||||
"terminalTotalDifficulty": 0,
|
||||
"warpConfig": {
|
||||
"blockTimestamp": 0,
|
||||
"quorumNumerator": 67,
|
||||
"requirePrimaryNetworkSigners": false
|
||||
}
|
||||
},
|
||||
"difficulty": "0x0",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0xb71b00",
|
||||
"gasUsed": "0x0",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0",
|
||||
"number": "0x0",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"timestamp": "0x0"
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
{
|
||||
"alloc": {
|
||||
"0200000000000000000000000000000000000005": {
|
||||
"balance": "0x0",
|
||||
"code": "0x01",
|
||||
"nonce": "0x1"
|
||||
},
|
||||
"12c6EE1d226225756F57B75957d2BF3Ab2e8597e": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
}
|
||||
},
|
||||
"baseFeePerGas": "0x5d21dba00",
|
||||
"coinbase": "0x0000000000000000000000000000000000000000",
|
||||
"config": {
|
||||
"arrowGlacierBlock": 0,
|
||||
"berlinBlock": 0,
|
||||
"blobSchedule": {
|
||||
"cancun": {
|
||||
"baseFeeUpdateFraction": 3338477,
|
||||
"max": 6,
|
||||
"target": 3
|
||||
}
|
||||
},
|
||||
"byzantiumBlock": 0,
|
||||
"cancunTime": 253399622400,
|
||||
"chainId": 36911,
|
||||
"constantinopleBlock": 0,
|
||||
"durangoTimestamp": 1607144400,
|
||||
"eip150Block": 0,
|
||||
"eip155Block": 0,
|
||||
"eip158Block": 0,
|
||||
"etnaTimestamp": 253399622400,
|
||||
"feeConfig": {
|
||||
"baseFeeChangeDenominator": 36,
|
||||
"blockGasCostStep": 200000,
|
||||
"gasLimit": 12000000,
|
||||
"maxBlockGasCost": 1000000,
|
||||
"minBaseFee": 25000000000,
|
||||
"minBlockGasCost": 0,
|
||||
"targetBlockRate": 2,
|
||||
"targetGas": 60000000
|
||||
},
|
||||
"fortunaTimestamp": 253399622400,
|
||||
"graniteTimestamp": 253399622400,
|
||||
"grayGlacierBlock": 0,
|
||||
"homesteadBlock": 0,
|
||||
"istanbulBlock": 0,
|
||||
"londonBlock": 0,
|
||||
"mergeNetsplitBlock": 0,
|
||||
"muirGlacierBlock": 0,
|
||||
"petersburgBlock": 0,
|
||||
"shanghaiTime": 0,
|
||||
"subnetEVMTimestamp": 0,
|
||||
"terminalTotalDifficulty": 0,
|
||||
"warpConfig": {
|
||||
"blockTimestamp": 1731369637,
|
||||
"quorumNumerator": 67,
|
||||
"requirePrimaryNetworkSigners": false
|
||||
}
|
||||
},
|
||||
"difficulty": "0x0",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0xb71b00",
|
||||
"gasUsed": "0x0",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0",
|
||||
"number": "0x0",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"timestamp": "0x67329aa5"
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
{
|
||||
"alloc": {
|
||||
"0200000000000000000000000000000000000005": {
|
||||
"balance": "0x0",
|
||||
"code": "0x01",
|
||||
"nonce": "0x1"
|
||||
},
|
||||
"9011E888251AB053B7bD1cdB598Db4f9DEd94714": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
}
|
||||
},
|
||||
"baseFeePerGas": "0x5d21dba00",
|
||||
"coinbase": "0x0000000000000000000000000000000000000000",
|
||||
"config": {
|
||||
"arrowGlacierBlock": 0,
|
||||
"berlinBlock": 0,
|
||||
"blobSchedule": {
|
||||
"cancun": {
|
||||
"baseFeeUpdateFraction": 3338477,
|
||||
"max": 6,
|
||||
"target": 3
|
||||
}
|
||||
},
|
||||
"byzantiumBlock": 0,
|
||||
"cancunTime": 253399622400,
|
||||
"chainId": 36910,
|
||||
"constantinopleBlock": 0,
|
||||
"durangoTimestamp": 0,
|
||||
"eip150Block": 0,
|
||||
"eip155Block": 0,
|
||||
"eip158Block": 0,
|
||||
"etnaTimestamp": 253399622400,
|
||||
"feeConfig": {
|
||||
"baseFeeChangeDenominator": 36,
|
||||
"blockGasCostStep": 200000,
|
||||
"gasLimit": 12000000,
|
||||
"maxBlockGasCost": 1000000,
|
||||
"minBaseFee": 25000000000,
|
||||
"minBlockGasCost": 0,
|
||||
"targetBlockRate": 2,
|
||||
"targetGas": 60000000
|
||||
},
|
||||
"fortunaTimestamp": 253399622400,
|
||||
"graniteTimestamp": 253399622400,
|
||||
"grayGlacierBlock": 0,
|
||||
"homesteadBlock": 0,
|
||||
"istanbulBlock": 0,
|
||||
"londonBlock": 0,
|
||||
"mergeNetsplitBlock": 0,
|
||||
"muirGlacierBlock": 0,
|
||||
"petersburgBlock": 0,
|
||||
"shanghaiTime": 0,
|
||||
"subnetEVMTimestamp": 0,
|
||||
"terminalTotalDifficulty": 0,
|
||||
"warpConfig": {
|
||||
"blockTimestamp": 0,
|
||||
"quorumNumerator": 67,
|
||||
"requirePrimaryNetworkSigners": false
|
||||
}
|
||||
},
|
||||
"difficulty": "0x0",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0xb71b00",
|
||||
"gasUsed": "0x0",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0",
|
||||
"number": "0x0",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"timestamp": "0x0"
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
[
|
||||
{
|
||||
"id": "NodeID-Mf3JfSY91oDwfBqf7rCLmhg4NDtDghw1f",
|
||||
"ip": "134.199.187.16:9641"
|
||||
},
|
||||
{
|
||||
"id": "NodeID-2TwSZ2oyeBK2mv7JiseEQ8m74rotDj4QR",
|
||||
"ip": "209.38.174.84:9641"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"config": {
|
||||
"chainId": 96368,
|
||||
"homesteadBlock": 0,
|
||||
"eip150Block": 0,
|
||||
"eip155Block": 0,
|
||||
"eip158Block": 0,
|
||||
"byzantiumBlock": 0,
|
||||
"constantinopleBlock": 0,
|
||||
"petersburgBlock": 0,
|
||||
"istanbulBlock": 0,
|
||||
"muirGlacierBlock": 0,
|
||||
"berlinBlock": 0,
|
||||
"londonBlock": 0,
|
||||
"arrowGlacierBlock": 0,
|
||||
"grayGlacierBlock": 0,
|
||||
"mergeNetsplitBlock": 0,
|
||||
"shanghaiTime": 0,
|
||||
"cancunTime": 253399622400,
|
||||
"terminalTotalDifficulty": 0,
|
||||
"subnetEVMTimestamp": 0,
|
||||
"durangoTimestamp": 0,
|
||||
"etnaTimestamp": 253399622400,
|
||||
"fortunaTimestamp": 253399622400,
|
||||
"graniteTimestamp": 253399622400,
|
||||
"feeConfig": {
|
||||
"gasLimit": 12000000,
|
||||
"targetBlockRate": 2,
|
||||
"minBaseFee": 25000000000,
|
||||
"targetGas": 60000000,
|
||||
"baseFeeChangeDenominator": 36,
|
||||
"minBlockGasCost": 0,
|
||||
"maxBlockGasCost": 1000000,
|
||||
"blockGasCostStep": 200000
|
||||
},
|
||||
"warpConfig": {
|
||||
"blockTimestamp": 0,
|
||||
"quorumNumerator": 67,
|
||||
"requirePrimaryNetworkSigners": false
|
||||
}
|
||||
},
|
||||
"alloc": {
|
||||
"0200000000000000000000000000000000000005": {
|
||||
"balance": "0x0",
|
||||
"nonce": "0x1",
|
||||
"code": "0x01"
|
||||
},
|
||||
"9011E888251AB053B7bD1cdB598Db4f9DEd94714": {
|
||||
"balance": "0x193e5939a08ce9dbd480000000"
|
||||
}
|
||||
},
|
||||
"stateRoot": "0x2d1cedac263020c5c56ef962f6abe0da1f5217bdc6468f8c9258a0ea23699e80",
|
||||
"nonce": "0x0",
|
||||
"timestamp": "0x67259912",
|
||||
"baseFeePerGas": "0x5d21dba00",
|
||||
"genesisHash": "0x1c5fe37764b8bc146dc88bc1c2e0259cd8369b07a06439bcfa1782b5d4fb0995",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0xb71b00",
|
||||
"difficulty": "0x0",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"coinbase": "0x0000000000000000000000000000000000000000",
|
||||
"number": "0x0",
|
||||
"gasUsed": "0x0",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"networkID": 2,
|
||||
"message": "Lux ex Tenebris",
|
||||
"startTime": 1770739200
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
{
|
||||
"allocations": [
|
||||
{
|
||||
"ethAddr": "0xf785fa547ae9ccf3d3ca5362762a347a4c41051a",
|
||||
"initialAmount": 2000000000000000000,
|
||||
"luxAddr": "X-test1qsrd262r5w9dswv2wwzj0un79ncpwvdg4rg0xx",
|
||||
"unlockSchedule": []
|
||||
},
|
||||
{
|
||||
"ethAddr": "0xf785fa547ae9ccf3d3ca5362762a347a4c41051a",
|
||||
"initialAmount": 0,
|
||||
"luxAddr": "P-test1qsrd262r5w9dswv2wwzj0un79ncpwvdg4rg0xx",
|
||||
"unlockSchedule": [
|
||||
{
|
||||
"amount": 2000000000000000000,
|
||||
"locktime": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"ethAddr": "0xf4b5be7a6dea583da4cddcda4d9b3afd51684b6e",
|
||||
"initialAmount": 2000000000000000000,
|
||||
"luxAddr": "X-test1x7rtxtkh6538hqdwp624tryu33853phfu9cfpw",
|
||||
"unlockSchedule": []
|
||||
},
|
||||
{
|
||||
"ethAddr": "0xf4b5be7a6dea583da4cddcda4d9b3afd51684b6e",
|
||||
"initialAmount": 0,
|
||||
"luxAddr": "P-test1x7rtxtkh6538hqdwp624tryu33853phfu9cfpw",
|
||||
"unlockSchedule": [
|
||||
{
|
||||
"amount": 2000000000000000000,
|
||||
"locktime": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"ethAddr": "0x202335dd1c21c9b90277f8bca78db98db0bbc293",
|
||||
"initialAmount": 2000000000000000000,
|
||||
"luxAddr": "X-test184tuwjk0np3n4mm2d7d7y0f397ksmzy0l5z307",
|
||||
"unlockSchedule": []
|
||||
},
|
||||
{
|
||||
"ethAddr": "0x202335dd1c21c9b90277f8bca78db98db0bbc293",
|
||||
"initialAmount": 0,
|
||||
"luxAddr": "P-test184tuwjk0np3n4mm2d7d7y0f397ksmzy0l5z307",
|
||||
"unlockSchedule": [
|
||||
{
|
||||
"amount": 2000000000000000000,
|
||||
"locktime": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"ethAddr": "0xf6e9eae8211e4a4532a480100068783d9cb75c3b",
|
||||
"initialAmount": 2000000000000000000,
|
||||
"luxAddr": "X-test14pfm4k9k3mx30yf2kpd9cvfcs7mzkkn6875l8h",
|
||||
"unlockSchedule": []
|
||||
},
|
||||
{
|
||||
"ethAddr": "0xf6e9eae8211e4a4532a480100068783d9cb75c3b",
|
||||
"initialAmount": 0,
|
||||
"luxAddr": "P-test14pfm4k9k3mx30yf2kpd9cvfcs7mzkkn6875l8h",
|
||||
"unlockSchedule": [
|
||||
{
|
||||
"amount": 2000000000000000000,
|
||||
"locktime": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"ethAddr": "0x32712d64011e135a24bb13b5934b53b6d6790812",
|
||||
"initialAmount": 2000000000000000000,
|
||||
"luxAddr": "X-test15953qfaga3w5txjmr0fl4clu37kak0ahk26v9e",
|
||||
"unlockSchedule": []
|
||||
},
|
||||
{
|
||||
"ethAddr": "0x32712d64011e135a24bb13b5934b53b6d6790812",
|
||||
"initialAmount": 0,
|
||||
"luxAddr": "P-test15953qfaga3w5txjmr0fl4clu37kak0ahk26v9e",
|
||||
"unlockSchedule": [
|
||||
{
|
||||
"amount": 2000000000000000000,
|
||||
"locktime": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"cChainGenesis": "{\n \"config\": {\n \"chainId\": 96368,\n \"homesteadBlock\": 0,\n \"eip150Block\": 0,\n \"eip155Block\": 0,\n \"eip158Block\": 0,\n \"byzantiumBlock\": 0,\n \"constantinopleBlock\": 0,\n \"petersburgBlock\": 0,\n \"istanbulBlock\": 0,\n \"muirGlacierBlock\": 0,\n \"berlinBlock\": 0,\n \"londonBlock\": 0,\n \"arrowGlacierBlock\": 0,\n \"grayGlacierBlock\": 0,\n \"mergeNetsplitBlock\": 0,\n \"shanghaiTime\": 0,\n \"cancunTime\": 253399622400,\n \"terminalTotalDifficulty\": 0,\n \"subnetEVMTimestamp\": 0,\n \"durangoTimestamp\": 0,\n \"etnaTimestamp\": 253399622400,\n \"feeConfig\": {\n \"gasLimit\": 12000000,\n \"targetBlockRate\": 2,\n \"minBaseFee\": 25000000000,\n \"targetGas\": 60000000,\n \"baseFeeChangeDenominator\": 36,\n \"minBlockGasCost\": 0,\n \"maxBlockGasCost\": 1000000,\n \"blockGasCostStep\": 200000\n },\n \"warpConfig\": {\n \"blockTimestamp\": 0,\n \"quorumNumerator\": 67,\n \"requirePrimaryNetworkSigners\": false\n }\n },\n \"alloc\": {\n \"0200000000000000000000000000000000000005\": {\n \"balance\": \"0x0\",\n \"nonce\": \"0x1\",\n \"code\": \"0x01\"\n },\n \"9011E888251AB053B7bD1cdB598Db4f9DEd94714\": {\n \"balance\": \"0x193e5939a08ce9dbd480000000\"\n }\n },\n \"stateRoot\": \"0x2d1cedac263020c5c56ef962f6abe0da1f5217bdc6468f8c9258a0ea23699e80\",\n \"nonce\": \"0x0\",\n \"timestamp\": \"0x67259912\",\n \"baseFeePerGas\": \"0x5d21dba00\",\n \"genesisHash\": \"0x1c5fe37764b8bc146dc88bc1c2e0259cd8369b07a06439bcfa1782b5d4fb0995\",\n \"extraData\": \"0x\",\n \"gasLimit\": \"0xb71b00\",\n \"difficulty\": \"0x0\",\n \"mixHash\": \"0x0000000000000000000000000000000000000000000000000000000000000000\",\n \"coinbase\": \"0x0000000000000000000000000000000000000000\",\n \"number\": \"0x0\",\n \"gasUsed\": \"0x0\",\n \"parentHash\": \"0x0000000000000000000000000000000000000000000000000000000000000000\"\n}\n",
|
||||
"initialStakeDuration": 31536000,
|
||||
"initialStakeDurationOffset": 5400,
|
||||
"initialStakedFunds": [],
|
||||
"initialStakers": [
|
||||
{
|
||||
"delegationFee": 20000,
|
||||
"nodeID": "NodeID-5rDdedC5LrCUqXg3hJ4ecwaY2LpHe2sNn",
|
||||
"rewardAddress": "X-test1qsrd262r5w9dswv2wwzj0un79ncpwvdg4rg0xx",
|
||||
"signer": {
|
||||
"proofOfPossession": "0x985316249a4cc543dac6f50526dd4cbe2b0b46b89fac6a970fec782b704c249bda6fcb7b1c6bcfd0b6487f8ff6e67214018263ec74b21c0cef074c06fcff51f3e9eff6de1ef472f22722749a6a887dffbadc2d5f2330004bf22cacb977bd3610",
|
||||
"publicKey": "0xb18b3692067a64f834c57a6a0ce9ca6a84763452aca0d44f08d1a1811b509017cb56f19d7c8eeec642630f65df25ea1f"
|
||||
},
|
||||
"weight": 1000000000000000
|
||||
},
|
||||
{
|
||||
"delegationFee": 20000,
|
||||
"nodeID": "NodeID-9LTQ9pFQq1vMHvVHi8a21s9eB4i6Scd3C",
|
||||
"rewardAddress": "X-test1x7rtxtkh6538hqdwp624tryu33853phfu9cfpw",
|
||||
"signer": {
|
||||
"proofOfPossession": "0xa79a0c88238b2a7175ad908974b300dc30d09bb2fe0c514561d10a56dae1ce75c47e4bbec708c4def26d59ddd47b855f097ebd52782d82c8180a6a23eb9bfea9c7f4ac657f2d9bbdf4588ec69dd4415550ef44614fef05111de26f9d2eff3f50",
|
||||
"publicKey": "0x8dcc2b53512c04526ddc671370f0be37fbbdffb3f941df92e8e8c6dc91c1b39fb0baa61d2af21116abce77905c9aa3a4"
|
||||
},
|
||||
"weight": 1000000000000000
|
||||
},
|
||||
{
|
||||
"delegationFee": 20000,
|
||||
"nodeID": "NodeID-NgQiKGcWQpo5p76o6ch1NMPtwPaRqepvX",
|
||||
"rewardAddress": "X-test184tuwjk0np3n4mm2d7d7y0f397ksmzy0l5z307",
|
||||
"signer": {
|
||||
"proofOfPossession": "0x86af52990b7ed739a5e08d4cc345fdd5d8448f3dcd8111384cf9d5b02359e7bcbc93ca9c1db3c784bb6c7dfe2a1f48c711333145d457fc9707d9008d88805f9d99a20caa7531ed3b6c76001eb6bb66cbf944f610a81b3ee6faa83d3b6a0d45d7",
|
||||
"publicKey": "0x908ce0377d3ce870504ec68aa6f20bf6446700aef07afe2f18780660ac9b0167adfccbefc56656364a3984dc265c429c"
|
||||
},
|
||||
"weight": 1000000000000000
|
||||
},
|
||||
{
|
||||
"delegationFee": 20000,
|
||||
"nodeID": "NodeID-Q7P6SBdbKsWHzchArcA7JzpqLQPfK7bJK",
|
||||
"rewardAddress": "X-test14pfm4k9k3mx30yf2kpd9cvfcs7mzkkn6875l8h",
|
||||
"signer": {
|
||||
"proofOfPossession": "0xb6421703990ba25db59e28f474f66c315fd0d35fbce51d2692e8f2eca756e4e29e6751985af0da17cb4f780fe0e2876c11279686d3f17c1c9e82b19a596273f205dbef8fd5e2569f47357bd0006f6e824dceffc32d3fb7cf6ecb7709e40eed72",
|
||||
"publicKey": "0xa3600d534f88d2ab7849818d1d6e6b45e6a89dd783b835da6b620ff22e74ebfc99a1bda718a894d58e48a372dad3a429"
|
||||
},
|
||||
"weight": 1000000000000000
|
||||
},
|
||||
{
|
||||
"delegationFee": 20000,
|
||||
"nodeID": "NodeID-YBG1kganBpfzVpTZHaft36WnuFfmMFxh",
|
||||
"rewardAddress": "X-test15953qfaga3w5txjmr0fl4clu37kak0ahk26v9e",
|
||||
"signer": {
|
||||
"proofOfPossession": "0xa7f5dfcecc701bb993c7fae1ee29ffdc3cd64f301b9635bc503a82ec4afcb492385d3fe0835ed74ae25b05ba81d3d51e037031b4266043fa0dd3ddf959c957ad402e53c8090630adc803d3d3d79138585294f2776f99006da22738a2c847e9bb",
|
||||
"publicKey": "0xa54dd5d3b6a3d713fe2030cdecf3bf37c3572f404cfe10226e966bd1389f834962dbc25d179f349be1f2e69e94e27689"
|
||||
},
|
||||
"weight": 1000000000000000
|
||||
}
|
||||
],
|
||||
"message": "Lux ex Tenebris",
|
||||
"networkID": 2,
|
||||
"startTime": 1766548540
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,368 @@
|
||||
{
|
||||
"alloc": {
|
||||
"0200000000000000000000000000000000000005": {
|
||||
"balance": "0x0",
|
||||
"code": "0x01",
|
||||
"nonce": "0x1"
|
||||
},
|
||||
"9011E888251AB053B7bD1cdB598Db4f9DEd94714": {
|
||||
"balance": "0x193e5939a08ce9dbd480000000"
|
||||
},
|
||||
"EAbCC110fAcBfebabC66Ad6f9E7B67288e720B59": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"8d5081153aE1cfb41f5c932fe0b6Beb7E159cF84": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"f8f12D0592e6d1bFe92ee16CaBCC4a6F26dAAe23": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"Fb66808f708e1d4D7D43a8c75596e84f94e06806": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"313CF291c069C58D6bd61B0D672673462B8951bD": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"f7f52257a6143cE6BbD12A98eF2B0a3d0C648079": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"CA92ad0C91bd8DE640B9dAFfEB338ac908725142": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"B5B325df519eB58B7223d85aaeac8b56aB05f3d6": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"cf5288bEe8d8F63511C389D5015185FDEDe30e54": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"16204223fe4470f4B1F1dA19A368dC815736a3d7": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"1B475A4C983DfE4f32bbA4dE8DA8fd2c37f3A2A6": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"1EB0b3E6cb2dE3B25b45d6e49C33e683271D7bd5": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"7283AA2c30523E9190A17b9598ff65599BAAe17": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"4f49A5bbe8A6f673BD06261E9CCf1D23a004FC4d": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"7337840B9d8cf2C78295FBC3373f6ceA9CB9F286": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"E374867773455c36Db90776a6a4E4B5B21743763": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"2ba3d932FAAE51EBAF2173b78c874d422a0Cc186": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"97f688735eD6D896FE740CbFD26983767A4cb9AA": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"B5c2a4867D6096F0F4e3ae76c48FD92C412154f0": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"C25d00876B6bB80a64E3032C2872440Ebfaf5b80": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"5a777e9b95F3a1706017F9b6fDc501cEceD84248": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"c19d8D7bc52C9F36DA48B0a80eb8015c7Add7916": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"A105a413c349b175d8b445Da814A6a766843F6aC": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"f5b14e0Ae37324877F8C03abe351C4743C95B5B4": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"4e256878ED0Aa6Aea20394015c6F66443a6047e0": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"111BDcfe8C5398C7764BC1220B99cA1EdBcF5AF7": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"9C864a2884eD27c376e499AA75f5B064c2Ef51a0": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"471217e35Cf573133b840c53E93Ed1EaCC51Ac1c": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"3690f692D17554F90a9Bf7819d5b987E605245fA": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"53163A5922f1531F5C98D69aC2Dd130660c3c855": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"B5ACC73B22612BC1B99f98C6da7B3E175E8F7539": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"2099389940444768C7738D4C45FF965CF36ECa52": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"341813ADC2de5fa20982E592A136D0b26bD35228": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"fc4e8955441c7DEf35c017d20127744736362164": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"1D2Eb1F0D268401B7C910d18e3F06fe1606964f7": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"314fac4Bd7D6a283985451202f25c6BcfaA037dE": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"c1ED5cef75C4285614c8b2878C60A92454DdF5d1": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"3B084e285EEf960873f9070B916B8B857e30Cbe1": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"2E2Ba4324533fD6fB077450403D5fAb25Cd934f9": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"96B995fF4847Ffb5436132B4a86502C07789c7Bf": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"80924B34750C897c34c25b3bA13dBE95E3224EfD": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"1a845Da3412a6D6231AB0E85FaEd72c260Da55bC": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"71aECaCe53bC0849A14F0b0a10224B00F6153352": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"5CAE5842A08165d4d736dA9E49438574283aaE9": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"20d3f6B4aE88224Ab9CD8ddE689FFb646B14e4c1": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"80E6653cb1BA2a0302207A0a21521CAf81fcc763": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"8Da4C887aaFcEDDF2B4D543e538bE7215D3401c3": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"698495959f5570420BAA955dae5eA2864933ACf": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"30341F789A0A8C81378Ec169f77125A76660662F": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"53bCA7fF5dd0441DB410bdC6FFD58703406434bf": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"5b72d3ff49B12c84a101D17ec98d87bf7B70b6bC": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"568AA29eD57e8575A794EC3e5FEe20f886411E1C": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"cb4E59591354111EFB8C7b78543E1aDa3bEdd077": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"944aB0665466A10f3C7E82096e2A7a6afF128378": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"4315c1e150a8825403135Ab705738dFe04f32183": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"3B8Bd0A20b05AADC9E457A4c967f45eEeC163c4": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"66D94a1014Fa808eE9Eab3Ac38C0810ab7d0009d": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"bB9bA2b3ECE6Ddc84a988D07d1d1Dd34b48C66f8": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"Ada3736A876A7fa4a454ab0605536e0A859D2A30": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"8C21B1Cb91C4FFf11f69f6B21103E2d4b003035c": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"980ca20F732c2217A3C7cc6cA252EbA56e088A61": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"721762eD0C130CE07d3Dc56A1fff1f26499bE8A6": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"8E22f2F37e7a5c643d883c956C31A4bCe08991F6": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"59f8F59199C0B3C6e752941ff67bd015E73829Ec": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"e945dbB0bA699FCcA469153aC6937943014C2b6F": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"96cdbBe896a7f51342d8177ACE00eF3475FaADc3": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"3C334af45A2f41274aA63fd9348fAeBA8966e279": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"4F566Da11B0711010abD9181631f6DaFc1f0c6B8": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"BB927d5e0b19631F6536B284444213CDDDB93Cdf": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"f483a8a0Ee70147A18EBCFAd7Bf1DE0E48B1fA51": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"55AA096feBd81Cdf27A0c2dA2F857D3F3e7C55eF": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"e8829fdc3c12Fd95a48dF248D028166d6C2d3598": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"c805912a1e62828c0C36e674164e6A9E948722eF": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"2aCB0ee16fd2B1bF465F85D7Ee112fA80C055FC": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"3f39f5bfc9E99a7C7455fB101cdBdA70D0595160": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"9cF4d8Bf5F1EC1997510C2126441c813A8c7Cc6f": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"2458cB853c406cDda14C6aD1Ebc8b106A829529E": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"4154BD5e8E15822Ab96359d309166EC91f009248": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"22EbCe08E9f967F227F6E83d2eDB6b600f1370E3": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"1FCDa7bc336de60FAb3BE7B2188cefE5AA590CBD": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"6D84Cdd6a387584922c7FDB1f70acF146f25C04B": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"CBac896B5DcDFB87f1ebf80A0D033a3Ef4f76f06": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"7a05645cAc4f3FDA00b100924562D0a9085A082D": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"8A19c91f810421d347DDBBE8158ab7F8693eae14": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"8C7B7ff7196515a8f3cf04bD9E8A33C4FFC8c9C3": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"8B8A5e59c1D430ab508dD6C45178f048C2aFAcEd": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"85a6ad52261990cf1E0e05D1dc471A8c4Dda94e7": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"43Ba86de6709D76Fa015E3cC3b7DDe31703504F8": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"1882D237E2FD5d4F3CCE29215317847D2b75f3eB": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"AC994e2fcb6aE6078B27937B1b5374B891669fb8": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"49660D0A746255ACCfeE4aA004279F7B27179111": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"74aB2B6E01bDf141FA73BC2355b58ceaC38C0Ac9": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"7EDbd481118E071FE9c4b85e404c56CCC351292C": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"7792a945C604B6A8991bC37F0BBa48eD3220e8d8": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"f106e8a89D982Dc13B25c933BEEfBcBa266ffF59": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"54D2e1dA5A1B5b9Da158acbA42Aed1d45F5CF02": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"4e79e052C5130d3918Fcb9D6825158aeEeF4890e": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"f5AbBd1b2384C16AA6A4bD114b399dE396c579A6": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"a8722395CaECe005f613644502d9655Ab8C42B17": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
}
|
||||
},
|
||||
"baseFeePerGas": "0x5d21dba00",
|
||||
"coinbase": "0x0000000000000000000000000000000000000000",
|
||||
"config": {
|
||||
"arrowGlacierBlock": 0,
|
||||
"berlinBlock": 0,
|
||||
"blobSchedule": {
|
||||
"cancun": {
|
||||
"baseFeeUpdateFraction": 3338477,
|
||||
"max": 6,
|
||||
"target": 3
|
||||
}
|
||||
},
|
||||
"byzantiumBlock": 0,
|
||||
"cancunTime": 253399622400,
|
||||
"chainId": 200202,
|
||||
"constantinopleBlock": 0,
|
||||
"durangoTimestamp": 0,
|
||||
"eip150Block": 0,
|
||||
"eip155Block": 0,
|
||||
"eip158Block": 0,
|
||||
"etnaTimestamp": 253399622400,
|
||||
"evmTimestamp": 0,
|
||||
"feeConfig": {
|
||||
"baseFeeChangeDenominator": 36,
|
||||
"blockGasCostStep": 200000,
|
||||
"gasLimit": 12000000,
|
||||
"maxBlockGasCost": 1000000,
|
||||
"minBaseFee": 25000000000,
|
||||
"minBlockGasCost": 0,
|
||||
"targetBlockRate": 2,
|
||||
"targetGas": 15000000
|
||||
},
|
||||
"fortunaTimestamp": 253399622400,
|
||||
"graniteTimestamp": 253399622400,
|
||||
"grayGlacierBlock": 0,
|
||||
"homesteadBlock": 0,
|
||||
"istanbulBlock": 0,
|
||||
"londonBlock": 0,
|
||||
"mergeNetsplitBlock": 0,
|
||||
"muirGlacierBlock": 0,
|
||||
"petersburgBlock": 0,
|
||||
"shanghaiTime": 0,
|
||||
"subnetEVMTimestamp": 0,
|
||||
"terminalTotalDifficulty": 0,
|
||||
"warpConfig": {
|
||||
"blockTimestamp": 0,
|
||||
"quorumNumerator": 67,
|
||||
"requirePrimaryNetworkSigners": false
|
||||
}
|
||||
},
|
||||
"difficulty": "0x0",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0xb71b00",
|
||||
"gasUsed": "0x0",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0",
|
||||
"number": "0x0",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"timestamp": "0x6727ea31"
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
{
|
||||
"alloc": {
|
||||
"0200000000000000000000000000000000000005": {
|
||||
"balance": "0x0",
|
||||
"code": "0x01",
|
||||
"nonce": "0x1"
|
||||
},
|
||||
"9011E888251AB053B7bD1cdB598Db4f9DEd94714": {
|
||||
"balance": "0x193e5939a08ce9dbd480000000"
|
||||
}
|
||||
},
|
||||
"baseFeePerGas": "0x5d21dba00",
|
||||
"coinbase": "0x0000000000000000000000000000000000000000",
|
||||
"config": {
|
||||
"arrowGlacierBlock": 0,
|
||||
"berlinBlock": 0,
|
||||
"blobSchedule": {
|
||||
"cancun": {
|
||||
"baseFeeUpdateFraction": 3338477,
|
||||
"max": 6,
|
||||
"target": 3
|
||||
}
|
||||
},
|
||||
"byzantiumBlock": 0,
|
||||
"cancunTime": 253399622400,
|
||||
"chainId": 200200,
|
||||
"constantinopleBlock": 0,
|
||||
"durangoTimestamp": 0,
|
||||
"eip150Block": 0,
|
||||
"eip155Block": 0,
|
||||
"eip158Block": 0,
|
||||
"etnaTimestamp": 253399622400,
|
||||
"evmTimestamp": 0,
|
||||
"feeConfig": {
|
||||
"baseFeeChangeDenominator": 36,
|
||||
"blockGasCostStep": 200000,
|
||||
"gasLimit": 12000000,
|
||||
"maxBlockGasCost": 1000000,
|
||||
"minBaseFee": 25000000000,
|
||||
"minBlockGasCost": 0,
|
||||
"targetBlockRate": 2,
|
||||
"targetGas": 15000000
|
||||
},
|
||||
"fortunaTimestamp": 253399622400,
|
||||
"graniteTimestamp": 253399622400,
|
||||
"grayGlacierBlock": 0,
|
||||
"homesteadBlock": 0,
|
||||
"istanbulBlock": 0,
|
||||
"londonBlock": 0,
|
||||
"mergeNetsplitBlock": 0,
|
||||
"muirGlacierBlock": 0,
|
||||
"petersburgBlock": 0,
|
||||
"shanghaiTime": 0,
|
||||
"subnetEVMTimestamp": 0,
|
||||
"terminalTotalDifficulty": 0,
|
||||
"warpConfig": {
|
||||
"blockTimestamp": 0,
|
||||
"quorumNumerator": 67,
|
||||
"requirePrimaryNetworkSigners": false
|
||||
}
|
||||
},
|
||||
"difficulty": "0x0",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0xb71b00",
|
||||
"gasUsed": "0x0",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0",
|
||||
"number": "0x0",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"timestamp": "0x6727e9c3"
|
||||
}
|
||||
@@ -0,0 +1,368 @@
|
||||
{
|
||||
"alloc": {
|
||||
"0200000000000000000000000000000000000005": {
|
||||
"balance": "0x0",
|
||||
"code": "0x01",
|
||||
"nonce": "0x1"
|
||||
},
|
||||
"9011E888251AB053B7bD1cdB598Db4f9DEd94714": {
|
||||
"balance": "0x193e5939a08ce9dbd480000000"
|
||||
},
|
||||
"EAbCC110fAcBfebabC66Ad6f9E7B67288e720B59": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"8d5081153aE1cfb41f5c932fe0b6Beb7E159cF84": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"f8f12D0592e6d1bFe92ee16CaBCC4a6F26dAAe23": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"Fb66808f708e1d4D7D43a8c75596e84f94e06806": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"313CF291c069C58D6bd61B0D672673462B8951bD": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"f7f52257a6143cE6BbD12A98eF2B0a3d0C648079": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"CA92ad0C91bd8DE640B9dAFfEB338ac908725142": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"B5B325df519eB58B7223d85aaeac8b56aB05f3d6": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"cf5288bEe8d8F63511C389D5015185FDEDe30e54": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"16204223fe4470f4B1F1dA19A368dC815736a3d7": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"1B475A4C983DfE4f32bbA4dE8DA8fd2c37f3A2A6": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"1EB0b3E6cb2dE3B25b45d6e49C33e683271D7bd5": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"7283AA2c30523E9190A17b9598ff65599BAAe17": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"4f49A5bbe8A6f673BD06261E9CCf1D23a004FC4d": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"7337840B9d8cf2C78295FBC3373f6ceA9CB9F286": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"E374867773455c36Db90776a6a4E4B5B21743763": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"2ba3d932FAAE51EBAF2173b78c874d422a0Cc186": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"97f688735eD6D896FE740CbFD26983767A4cb9AA": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"B5c2a4867D6096F0F4e3ae76c48FD92C412154f0": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"C25d00876B6bB80a64E3032C2872440Ebfaf5b80": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"5a777e9b95F3a1706017F9b6fDc501cEceD84248": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"c19d8D7bc52C9F36DA48B0a80eb8015c7Add7916": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"A105a413c349b175d8b445Da814A6a766843F6aC": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"f5b14e0Ae37324877F8C03abe351C4743C95B5B4": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"4e256878ED0Aa6Aea20394015c6F66443a6047e0": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"111BDcfe8C5398C7764BC1220B99cA1EdBcF5AF7": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"9C864a2884eD27c376e499AA75f5B064c2Ef51a0": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"471217e35Cf573133b840c53E93Ed1EaCC51Ac1c": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"3690f692D17554F90a9Bf7819d5b987E605245fA": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"53163A5922f1531F5C98D69aC2Dd130660c3c855": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"B5ACC73B22612BC1B99f98C6da7B3E175E8F7539": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"2099389940444768C7738D4C45FF965CF36ECa52": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"341813ADC2de5fa20982E592A136D0b26bD35228": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"fc4e8955441c7DEf35c017d20127744736362164": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"1D2Eb1F0D268401B7C910d18e3F06fe1606964f7": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"314fac4Bd7D6a283985451202f25c6BcfaA037dE": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"c1ED5cef75C4285614c8b2878C60A92454DdF5d1": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"3B084e285EEf960873f9070B916B8B857e30Cbe1": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"2E2Ba4324533fD6fB077450403D5fAb25Cd934f9": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"96B995fF4847Ffb5436132B4a86502C07789c7Bf": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"80924B34750C897c34c25b3bA13dBE95E3224EfD": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"1a845Da3412a6D6231AB0E85FaEd72c260Da55bC": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"71aECaCe53bC0849A14F0b0a10224B00F6153352": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"5CAE5842A08165d4d736dA9E49438574283aaE9": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"20d3f6B4aE88224Ab9CD8ddE689FFb646B14e4c1": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"80E6653cb1BA2a0302207A0a21521CAf81fcc763": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"8Da4C887aaFcEDDF2B4D543e538bE7215D3401c3": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"698495959f5570420BAA955dae5eA2864933ACf": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"30341F789A0A8C81378Ec169f77125A76660662F": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"53bCA7fF5dd0441DB410bdC6FFD58703406434bf": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"5b72d3ff49B12c84a101D17ec98d87bf7B70b6bC": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"568AA29eD57e8575A794EC3e5FEe20f886411E1C": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"cb4E59591354111EFB8C7b78543E1aDa3bEdd077": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"944aB0665466A10f3C7E82096e2A7a6afF128378": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"4315c1e150a8825403135Ab705738dFe04f32183": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"3B8Bd0A20b05AADC9E457A4c967f45eEeC163c4": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"66D94a1014Fa808eE9Eab3Ac38C0810ab7d0009d": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"bB9bA2b3ECE6Ddc84a988D07d1d1Dd34b48C66f8": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"Ada3736A876A7fa4a454ab0605536e0A859D2A30": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"8C21B1Cb91C4FFf11f69f6B21103E2d4b003035c": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"980ca20F732c2217A3C7cc6cA252EbA56e088A61": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"721762eD0C130CE07d3Dc56A1fff1f26499bE8A6": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"8E22f2F37e7a5c643d883c956C31A4bCe08991F6": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"59f8F59199C0B3C6e752941ff67bd015E73829Ec": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"e945dbB0bA699FCcA469153aC6937943014C2b6F": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"96cdbBe896a7f51342d8177ACE00eF3475FaADc3": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"3C334af45A2f41274aA63fd9348fAeBA8966e279": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"4F566Da11B0711010abD9181631f6DaFc1f0c6B8": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"BB927d5e0b19631F6536B284444213CDDDB93Cdf": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"f483a8a0Ee70147A18EBCFAd7Bf1DE0E48B1fA51": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"55AA096feBd81Cdf27A0c2dA2F857D3F3e7C55eF": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"e8829fdc3c12Fd95a48dF248D028166d6C2d3598": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"c805912a1e62828c0C36e674164e6A9E948722eF": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"2aCB0ee16fd2B1bF465F85D7Ee112fA80C055FC": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"3f39f5bfc9E99a7C7455fB101cdBdA70D0595160": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"9cF4d8Bf5F1EC1997510C2126441c813A8c7Cc6f": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"2458cB853c406cDda14C6aD1Ebc8b106A829529E": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"4154BD5e8E15822Ab96359d309166EC91f009248": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"22EbCe08E9f967F227F6E83d2eDB6b600f1370E3": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"1FCDa7bc336de60FAb3BE7B2188cefE5AA590CBD": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"6D84Cdd6a387584922c7FDB1f70acF146f25C04B": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"CBac896B5DcDFB87f1ebf80A0D033a3Ef4f76f06": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"7a05645cAc4f3FDA00b100924562D0a9085A082D": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"8A19c91f810421d347DDBBE8158ab7F8693eae14": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"8C7B7ff7196515a8f3cf04bD9E8A33C4FFC8c9C3": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"8B8A5e59c1D430ab508dD6C45178f048C2aFAcEd": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"85a6ad52261990cf1E0e05D1dc471A8c4Dda94e7": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"43Ba86de6709D76Fa015E3cC3b7DDe31703504F8": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"1882D237E2FD5d4F3CCE29215317847D2b75f3eB": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"AC994e2fcb6aE6078B27937B1b5374B891669fb8": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"49660D0A746255ACCfeE4aA004279F7B27179111": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"74aB2B6E01bDf141FA73BC2355b58ceaC38C0Ac9": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"7EDbd481118E071FE9c4b85e404c56CCC351292C": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"7792a945C604B6A8991bC37F0BBa48eD3220e8d8": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"f106e8a89D982Dc13B25c933BEEfBcBa266ffF59": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"54D2e1dA5A1B5b9Da158acbA42Aed1d45F5CF02": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"4e79e052C5130d3918Fcb9D6825158aeEeF4890e": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"f5AbBd1b2384C16AA6A4bD114b399dE396c579A6": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
},
|
||||
"a8722395CaECe005f613644502d9655Ab8C42B17": {
|
||||
"balance": "0x33b2e3c9fd0803ce8000000"
|
||||
}
|
||||
},
|
||||
"baseFeePerGas": "0x5d21dba00",
|
||||
"coinbase": "0x0000000000000000000000000000000000000000",
|
||||
"config": {
|
||||
"arrowGlacierBlock": 0,
|
||||
"berlinBlock": 0,
|
||||
"blobSchedule": {
|
||||
"cancun": {
|
||||
"baseFeeUpdateFraction": 3338477,
|
||||
"max": 6,
|
||||
"target": 3
|
||||
}
|
||||
},
|
||||
"byzantiumBlock": 0,
|
||||
"cancunTime": 253399622400,
|
||||
"chainId": 200201,
|
||||
"constantinopleBlock": 0,
|
||||
"durangoTimestamp": 0,
|
||||
"eip150Block": 0,
|
||||
"eip155Block": 0,
|
||||
"eip158Block": 0,
|
||||
"etnaTimestamp": 253399622400,
|
||||
"evmTimestamp": 0,
|
||||
"feeConfig": {
|
||||
"baseFeeChangeDenominator": 36,
|
||||
"blockGasCostStep": 200000,
|
||||
"gasLimit": 12000000,
|
||||
"maxBlockGasCost": 1000000,
|
||||
"minBaseFee": 25000000000,
|
||||
"minBlockGasCost": 0,
|
||||
"targetBlockRate": 2,
|
||||
"targetGas": 15000000
|
||||
},
|
||||
"fortunaTimestamp": 253399622400,
|
||||
"graniteTimestamp": 253399622400,
|
||||
"grayGlacierBlock": 0,
|
||||
"homesteadBlock": 0,
|
||||
"istanbulBlock": 0,
|
||||
"londonBlock": 0,
|
||||
"mergeNetsplitBlock": 0,
|
||||
"muirGlacierBlock": 0,
|
||||
"petersburgBlock": 0,
|
||||
"shanghaiTime": 0,
|
||||
"subnetEVMTimestamp": 0,
|
||||
"terminalTotalDifficulty": 0,
|
||||
"warpConfig": {
|
||||
"blockTimestamp": 0,
|
||||
"quorumNumerator": 67,
|
||||
"requirePrimaryNetworkSigners": false
|
||||
}
|
||||
},
|
||||
"difficulty": "0x0",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0xb71b00",
|
||||
"gasUsed": "0x0",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"nonce": "0x0",
|
||||
"number": "0x0",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"timestamp": "0x6727ea31"
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
# Dependencies
|
||||
node_modules/
|
||||
.pnpm-store/
|
||||
|
||||
# Next.js
|
||||
.next/
|
||||
out/
|
||||
.turbo/
|
||||
|
||||
# Build
|
||||
dist/
|
||||
build/
|
||||
|
||||
# Environment
|
||||
.env
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Fumadocs
|
||||
.source/
|
||||
|
||||
# TypeScript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
@@ -0,0 +1,190 @@
|
||||
# Lux Mainnet Genesis Configuration for geth
|
||||
|
||||
**Last Updated**: 2025-12-15
|
||||
|
||||
## Overview
|
||||
|
||||
This document describes the genesis configuration required for importing SubnetEVM blocks into `luxfi/geth`. The genesis MUST match the original SubnetEVM genesis exactly to ensure cryptographic integrity.
|
||||
|
||||
## Genesis Hash
|
||||
|
||||
**Canonical Genesis Hash**: `0x3f4fa2a0b0ce089f52bf0ae9199c75ffdd76ecafc987794050cb0d286f1ec61e`
|
||||
|
||||
This hash is computed from the canonical genesis configuration below. Any deviation will produce a different hash, and all subsequent state roots will mismatch.
|
||||
|
||||
## Canonical Genesis Configuration
|
||||
|
||||
```json
|
||||
{
|
||||
"config": {
|
||||
"chainId": 96369,
|
||||
"homesteadBlock": 0,
|
||||
"eip150Block": 0,
|
||||
"eip155Block": 0,
|
||||
"eip158Block": 0,
|
||||
"byzantiumBlock": 0,
|
||||
"constantinopleBlock": 0,
|
||||
"petersburgBlock": 0,
|
||||
"istanbulBlock": 0,
|
||||
"berlinBlock": 0,
|
||||
"londonBlock": 0,
|
||||
"terminalTotalDifficulty": 0,
|
||||
"terminalTotalDifficultyPassed": true,
|
||||
"subnetEVMTimestamp": 0,
|
||||
"durangoTimestamp": 0
|
||||
},
|
||||
"nonce": "0x0",
|
||||
"timestamp": "0x672485c2",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0xb71b00",
|
||||
"difficulty": "0x0",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"coinbase": "0x0000000000000000000000000000000000000000",
|
||||
"alloc": {
|
||||
"0x9011E888251AB053B7bD1cdB598Db4f9DEd94714": {
|
||||
"balance": "2000000000000000000000000000000"
|
||||
},
|
||||
"0x0200000000000000000000000000000000000005": {
|
||||
"nonce": "1",
|
||||
"balance": "0",
|
||||
"code": "0x01"
|
||||
}
|
||||
},
|
||||
"number": "0x0",
|
||||
"gasUsed": "0x0",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"baseFeePerGas": "0x5d21dba00"
|
||||
}
|
||||
```
|
||||
|
||||
## Critical Configuration Fields
|
||||
|
||||
### Chain Configuration
|
||||
|
||||
| Field | Value | Description |
|
||||
|-------|-------|-------------|
|
||||
| `chainId` | 96369 | Lux Mainnet chain ID |
|
||||
| `terminalTotalDifficulty` | 0 | Post-merge (PoS) from genesis |
|
||||
| `terminalTotalDifficultyPassed` | true | Merge already happened |
|
||||
| `subnetEVMTimestamp` | 0 | SubnetEVM features active from genesis |
|
||||
| `durangoTimestamp` | 0 | Durango upgrade (Shanghai EIPs) from genesis |
|
||||
|
||||
### SubnetEVM-Specific Fields
|
||||
|
||||
These fields are **REQUIRED** in `luxfi/geth` for SubnetEVM compatibility:
|
||||
|
||||
#### `subnetEVMTimestamp`
|
||||
|
||||
Enables SubnetEVM gas accounting:
|
||||
- Coinbase receives full `gasPrice * gasUsed` (no EIP-1559 burning)
|
||||
- Gas refunds are disabled (ApricotPhase1 behavior)
|
||||
- No base fee burning to coinbase
|
||||
|
||||
#### `durangoTimestamp`
|
||||
|
||||
Enables Shanghai EIPs via Durango upgrade:
|
||||
- EIP-3651: Warm coinbase
|
||||
- EIP-3855: PUSH0 instruction
|
||||
- EIP-3860: Init code metering (2 gas per 32-byte word)
|
||||
- EIP-4895: Beacon chain withdrawals (unused)
|
||||
|
||||
## Genesis Parameters Explanation
|
||||
|
||||
### Timestamp
|
||||
```
|
||||
"timestamp": "0x672485c2"
|
||||
```
|
||||
Decimal: 1730447810 (Unix timestamp of genesis block)
|
||||
|
||||
### Gas Limit
|
||||
```
|
||||
"gasLimit": "0xb71b00"
|
||||
```
|
||||
Decimal: 12,000,000 gas
|
||||
|
||||
### Base Fee
|
||||
```
|
||||
"baseFeePerGas": "0x5d21dba00"
|
||||
```
|
||||
Decimal: 25,000,000,000 (25 Gwei)
|
||||
|
||||
### Difficulty
|
||||
```
|
||||
"difficulty": "0x0"
|
||||
```
|
||||
Must be 0 for post-merge chains.
|
||||
|
||||
## Alloc Section
|
||||
|
||||
### Treasury Account
|
||||
```json
|
||||
"0x9011E888251AB053B7bD1cdB598Db4f9DEd94714": {
|
||||
"balance": "2000000000000000000000000000000"
|
||||
}
|
||||
```
|
||||
2 billion LUX (with 18 decimals)
|
||||
|
||||
### Precompile Contract
|
||||
```json
|
||||
"0x0200000000000000000000000000000000000005": {
|
||||
"nonce": "1",
|
||||
"balance": "0",
|
||||
"code": "0x01"
|
||||
}
|
||||
```
|
||||
SubnetEVM native minter precompile placeholder.
|
||||
|
||||
## Initialization Command
|
||||
|
||||
```bash
|
||||
# Initialize geth database with genesis
|
||||
./geth init --datadir /path/to/datadir /path/to/genesis.json
|
||||
|
||||
# Verify genesis hash in output:
|
||||
# INFO [...] Successfully wrote genesis state database=chaindata hash=3f4fa2..1ec61e
|
||||
```
|
||||
|
||||
## Genesis Hash Verification
|
||||
|
||||
The genesis hash MUST be `0x3f4fa2a0b0ce089f52bf0ae9199c75ffdd76ecafc987794050cb0d286f1ec61e`.
|
||||
|
||||
If you see a different hash, check:
|
||||
1. `difficulty` must be `"0x0"` (not `"0x1"`)
|
||||
2. All fork blocks must be 0
|
||||
3. `terminalTotalDifficultyPassed` must be `true`
|
||||
4. Timestamp must be exactly `"0x672485c2"`
|
||||
5. No extra whitespace in alloc addresses
|
||||
|
||||
## Common Mistakes
|
||||
|
||||
### Wrong Genesis Hash
|
||||
|
||||
| Mistake | Effect |
|
||||
|---------|--------|
|
||||
| `difficulty: "0x1"` | Different hash, all state roots fail |
|
||||
| Missing `subnetEVMTimestamp` | Gas accounting mismatch |
|
||||
| Missing `durangoTimestamp` | EIP-3860 gas mismatch (876 gas per contract deploy) |
|
||||
| Different timestamp | Different genesis hash |
|
||||
| Different alloc balances | Different genesis state root |
|
||||
|
||||
### State Root Mismatch at Block N
|
||||
|
||||
If state roots match until block N then fail:
|
||||
1. Check if block N deploys a contract (EIP-3860)
|
||||
2. Check if block N has gas refunds (disabled in SubnetEVM)
|
||||
3. Check coinbase balance accumulation
|
||||
|
||||
## Code References
|
||||
|
||||
### geth Configuration
|
||||
- `params/config.go`: ChainConfig with SubnetEVMTimestamp, DurangoTimestamp
|
||||
- `params/config.go:1518`: Rules() enables Shanghai when Durango active
|
||||
|
||||
### State Transition
|
||||
- `core/state_transition.go:570`: SubnetEVM coinbase payment
|
||||
- `core/state_transition.go:665`: Gas refund disabling
|
||||
|
||||
## Version Requirements
|
||||
|
||||
- `luxfi/geth` >= v1.16.55
|
||||
- Must include SubnetEVM gas accounting patches
|
||||
Generated
+4130
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,73 @@
|
||||
module github.com/luxfi/genesis
|
||||
|
||||
go 1.26.1
|
||||
|
||||
require (
|
||||
github.com/luxfi/constants v1.4.6
|
||||
github.com/luxfi/container v0.0.4
|
||||
github.com/luxfi/crypto v1.17.44
|
||||
github.com/luxfi/go-bip32 v1.0.2
|
||||
github.com/luxfi/go-bip39 v1.1.2
|
||||
github.com/luxfi/ids v1.2.9
|
||||
github.com/luxfi/math v1.2.3
|
||||
github.com/luxfi/tls v1.0.3
|
||||
github.com/luxfi/utxo v0.2.4
|
||||
github.com/luxfi/vm v1.0.33
|
||||
github.com/stretchr/testify v1.11.1
|
||||
golang.org/x/crypto v0.49.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/klauspost/compress v1.18.4 // indirect
|
||||
github.com/luxfi/atomic v1.0.0 // indirect
|
||||
github.com/luxfi/compress v0.0.5 // indirect
|
||||
github.com/luxfi/concurrent v0.0.3 // indirect
|
||||
github.com/luxfi/database v1.17.40 // indirect
|
||||
github.com/luxfi/keychain v1.0.2 // indirect
|
||||
github.com/luxfi/log v1.4.1 // indirect
|
||||
github.com/luxfi/ordering v0.0.1 // indirect
|
||||
github.com/luxfi/runtime v1.0.1 // indirect
|
||||
github.com/luxfi/timer v1.0.1 // indirect
|
||||
github.com/luxfi/validators v1.0.0 // indirect
|
||||
github.com/luxfi/version v1.0.1 // indirect
|
||||
github.com/luxfi/warp v1.18.5 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260311194731-d5b7577c683d // indirect
|
||||
github.com/btcsuite/btcd/btcutil v1.1.6 // indirect
|
||||
github.com/cloudflare/circl v1.6.3 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1 // indirect
|
||||
github.com/gorilla/rpc v1.2.1 // indirect
|
||||
github.com/holiman/uint256 v1.3.2 // indirect
|
||||
github.com/luxfi/address v1.0.1
|
||||
github.com/luxfi/cache v1.2.1 // indirect
|
||||
github.com/luxfi/codec v1.1.4 // indirect
|
||||
github.com/luxfi/consensus v1.22.63 // indirect
|
||||
github.com/luxfi/formatting v1.0.1
|
||||
github.com/luxfi/geth v1.16.73 // indirect
|
||||
github.com/luxfi/math/big v0.1.0 // indirect
|
||||
github.com/luxfi/metric v1.4.11 // indirect
|
||||
github.com/luxfi/mock v0.1.1 // indirect
|
||||
github.com/luxfi/p2p v1.18.9 // indirect
|
||||
github.com/luxfi/protocol v0.0.3
|
||||
github.com/luxfi/rpc v1.0.0
|
||||
github.com/luxfi/sampler v1.0.0 // indirect
|
||||
github.com/luxfi/utils v1.1.4 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mr-tron/base58 v1.2.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/supranational/blst v0.3.16 // indirect
|
||||
go.uber.org/mock v0.6.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 // indirect
|
||||
golang.org/x/sys v0.42.0 // indirect
|
||||
gonum.org/v1/gonum v0.17.0 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
replace launchpad.net/gocheck => gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b
|
||||
@@ -0,0 +1,255 @@
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260311194731-d5b7577c683d h1:EA+kZ8mxGb1W/ewiIBMzb/1gg5BiW1Fvr3r4qCUBJEg=
|
||||
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260311194731-d5b7577c683d/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI=
|
||||
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
|
||||
github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M=
|
||||
github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A=
|
||||
github.com/btcsuite/btcd v0.24.2/go.mod h1:5C8ChTkl5ejr3WHj8tkQSCmydiMEPB0ZhQhehpq7Dgg=
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA=
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE=
|
||||
github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A=
|
||||
github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE=
|
||||
github.com/btcsuite/btcd/btcutil v1.1.5/go.mod h1:PSZZ4UitpLBWzxGd5VGOrLnmOjtPP/a6HaFo12zMs00=
|
||||
github.com/btcsuite/btcd/btcutil v1.1.6 h1:zFL2+c3Lb9gEgqKNzowKUPQNb8jV7v5Oaodi/AYFd6c=
|
||||
github.com/btcsuite/btcd/btcutil v1.1.6/go.mod h1:9dFymx8HpuLqBnsPELrImQeTQfKBQqzqGbbV3jK55aE=
|
||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
|
||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
|
||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
|
||||
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
|
||||
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
|
||||
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
|
||||
github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
|
||||
github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I=
|
||||
github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
|
||||
github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
|
||||
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
|
||||
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8=
|
||||
github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
|
||||
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
|
||||
github.com/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U0x++OzVrdms8=
|
||||
github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1 h1:5RVFMOWjMyRy8cARdy79nAmgYw3hK/4HUq48LQ6Wwqo=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
|
||||
github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/gorilla/rpc v1.2.1 h1:yC+LMV5esttgpVvNORL/xX4jvTTEUE30UZhZ5JF7K9k=
|
||||
github.com/gorilla/rpc v1.2.1/go.mod h1:uNpOihAlF5xRFLuTYhfR0yfCTm0WTQSQttkMSptRfGk=
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/holiman/uint256 v1.3.2 h1:a9EgMPSC1AAaj1SZL5zIQD3WbwTuHrMGOerLjGmM/TA=
|
||||
github.com/holiman/uint256 v1.3.2/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
|
||||
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
|
||||
github.com/klauspost/compress v1.18.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c=
|
||||
github.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/luxfi/address v1.0.1 h1:Sc4keyuVzBIvHr7uVeYZf2/WY9YDGUgDi/iiWenj49g=
|
||||
github.com/luxfi/address v1.0.1/go.mod h1:5j3Eh66v9zvv1GbNdZwt+23krV8JlSDaRzmWZU8ZRM0=
|
||||
github.com/luxfi/atomic v1.0.0 h1:xUV60MuzRvXngaQ1sM0yVC2v4TRoLlUGkkH7M9PS4yw=
|
||||
github.com/luxfi/atomic v1.0.0/go.mod h1:0G2mTlQ6TXWHICUHrUUPu1/qAiIyR4gSZ2tva9ci/bI=
|
||||
github.com/luxfi/cache v1.2.1 h1:kAzOS55/hmYeNKR+0HAKv4ma48Y6JjkI8UQeqdZ8bfI=
|
||||
github.com/luxfi/cache v1.2.1/go.mod h1:co7JTxZZHpKT31Yh01LFp5aZOxmoUg157FhBLQdQHVU=
|
||||
github.com/luxfi/codec v1.1.4 h1:Yl8ZalMNkqo7cD6R9AjczAajkLOmsjyZ9+DASVYHrvg=
|
||||
github.com/luxfi/codec v1.1.4/go.mod h1:oGQ3j6E8c2P0pL0irYtWkrB1hmDUFIE0puXHK4gV5KI=
|
||||
github.com/luxfi/compress v0.0.5 h1:4tEUHw5MK1bu5UOjfYCt4OKMiH7yykIgmGPRA/BfJTM=
|
||||
github.com/luxfi/compress v0.0.5/go.mod h1:Cc1yxD2pfzrvpO32W2GDwLKff+CylHEvzZh2Ko8RSIU=
|
||||
github.com/luxfi/concurrent v0.0.3 h1:eJyv1fhaC0jMLMw6+QS774cUmp7GK+ouMgvLCqnC7cc=
|
||||
github.com/luxfi/concurrent v0.0.3/go.mod h1:Aj/FR5NpM0cB2P4Nt3+tz9+dV6V+LUW4HuMgSjwq5hw=
|
||||
github.com/luxfi/consensus v1.22.63 h1:1oRAOvSjfeC5IwDkWHgFTHflQFwhCNYItkfh19UmzZk=
|
||||
github.com/luxfi/consensus v1.22.63/go.mod h1:PwMubdgQloJiYQPawTTJGhsaC4Ve3/BjCmNxTDjYdGg=
|
||||
github.com/luxfi/constants v1.4.6 h1:9a/ED2t/sRfeOfvNur2lssyjaw2I7tSyddTJHVSDYWg=
|
||||
github.com/luxfi/constants v1.4.6/go.mod h1:hOszZ2NDQ8gMZKncfcZ67PXkb5OIbnwAzXC3oFbQwW0=
|
||||
github.com/luxfi/container v0.0.4 h1:BXhF82WyfqVP5mjlNcr7tP0Fcnvl0Ap1rkiu+rq5XuM=
|
||||
github.com/luxfi/container v0.0.4/go.mod h1:Z3SpmMF5d4t77MM0nHYXURpn+EMVaeu1fhbd/3BGaek=
|
||||
github.com/luxfi/crypto v1.17.44 h1:uLyp7S9Lob8aP4m+gua8wbx4tfYK0cDPhv1rv7+YR2s=
|
||||
github.com/luxfi/crypto v1.17.44/go.mod h1:3ahTd4kNZT5WtEiVWbzGtd05wUjjrlA7eSkqeDNL8U4=
|
||||
github.com/luxfi/database v1.17.40 h1:RPtRLbKu5yBM5fCSjR7CWzNqR0N1fg8r9y73hUww5sM=
|
||||
github.com/luxfi/database v1.17.40/go.mod h1:du1q0PemBc0gZcdtHhsARs6mIsZM3a5R/C040DVYLCM=
|
||||
github.com/luxfi/formatting v1.0.1 h1:ZnE1rAdEUds9yAegdVdGDOBGN6hLMPOv6E03Fp8IEYo=
|
||||
github.com/luxfi/formatting v1.0.1/go.mod h1:mYzNf5DJOiqSSKUPzNj5dKy4tstFbN3pZlkI5716eKc=
|
||||
github.com/luxfi/geth v1.16.73 h1:jbRaWQDXTadZ+fEh5Cd5jnkv1qq4yAoT5bVQFuql8ZU=
|
||||
github.com/luxfi/geth v1.16.73/go.mod h1:CPzps7Zp3I7Og3KT3m+Qs41WW8z+YPGx1TOJQnHdBHY=
|
||||
github.com/luxfi/go-bip32 v1.0.2 h1:7vFbb+Wr4Z499q2tuCLdd7wWjtn8sH+HWBlx76mhH9Y=
|
||||
github.com/luxfi/go-bip32 v1.0.2/go.mod h1:bc7/LXDKAJQZ/F0Xjf5yXaTZxY9/ssLb4FC+Hxn/cDk=
|
||||
github.com/luxfi/go-bip39 v1.1.2 h1:p+wLMPGs6MLQh7q0YIsmy2EhHL7LHiELEGTJko6t/Jg=
|
||||
github.com/luxfi/go-bip39 v1.1.2/go.mod h1:96de9VkR2kY/ASAnhMtvt3TSh+PZkAFAngNj0GjRGDo=
|
||||
github.com/luxfi/ids v1.2.9 h1:+yjdhXW99drnd2Zlp1u/p8k3G23W3/1btJQ4ogHawUI=
|
||||
github.com/luxfi/ids v1.2.9/go.mod h1:khJOEdOPxd22yn0jcVrnbX1ADa0GHn5Y74gvCzN5BYc=
|
||||
github.com/luxfi/keychain v1.0.2 h1:uQgmjs37/VBIALEiYrrszTpxvtqr07/YvS9TnmxGafs=
|
||||
github.com/luxfi/keychain v1.0.2/go.mod h1:q/4ULgZBlstKkwzOzG/0T6y73BDPgnkrcibbJyTvmbU=
|
||||
github.com/luxfi/log v1.4.1 h1:rIfFRodb9jrD/w7KayaUk0Oc+37PaQQdKEEMJCjR8gw=
|
||||
github.com/luxfi/log v1.4.1/go.mod h1:64IE3xRMJcpkQwnPUfJw3pDj7wU0kRS7BZ9wM7R72jk=
|
||||
github.com/luxfi/math v1.2.3 h1:BgvIFw/srPXFLbcqtoDhLJOfmBsn86GPA1iWgsoyUb4=
|
||||
github.com/luxfi/math v1.2.3/go.mod h1:C8STnF2H+D6rqBPt248CiWY2TGuJgdtv/+4UqrT15iM=
|
||||
github.com/luxfi/math/big v0.1.0 h1:Vz4c0RsZVPdIKPsHPgAJChH/R3p15WHRUz7LkLf+NIQ=
|
||||
github.com/luxfi/math/big v0.1.0/go.mod h1:BuxSu22RbO93xBLk5Eam5nldFponoJ73xDFz4uJ3Huk=
|
||||
github.com/luxfi/metric v1.4.11 h1:XMkJSGasvZ4GMzW45m3utec3d0OhmxvF5xBwrkEYGak=
|
||||
github.com/luxfi/metric v1.4.11/go.mod h1:EAFmkzd5EX654G48HXKIsyJufOUa4sxmOJKvwSRbjng=
|
||||
github.com/luxfi/mock v0.1.1 h1:0HEtIjg1J6CWz+IUyP6rsGqNWTcmxjFnSQIhaDuARwY=
|
||||
github.com/luxfi/mock v0.1.1/go.mod h1:jo35akl3Vtd8LbzDts8VJ0jmSVycrd1/eBi6g6t5hKU=
|
||||
github.com/luxfi/ordering v0.0.1 h1:pZkMD5AC1Q/5RnZsTKK0KWd1tHXiJw7WPU0oebT0wog=
|
||||
github.com/luxfi/ordering v0.0.1/go.mod h1:Ld5UAayScKxicsd9BYuQ/D7vzEUndOC7cGCT+fj++jM=
|
||||
github.com/luxfi/p2p v1.18.9 h1:xDqS04quYCVmiC1jBinSRQ2LAmY7E6XR4Dr7HdXHSy0=
|
||||
github.com/luxfi/p2p v1.18.9/go.mod h1:X829+oxzPyY2e5S9CyyMFiOtk6nyuNnxZSaSxGxrpKw=
|
||||
github.com/luxfi/protocol v0.0.3 h1:Teytlu6Gbd0HqJXuDvV84ArqRabEFR40yDNNQUOCpVE=
|
||||
github.com/luxfi/protocol v0.0.3/go.mod h1:ZSA1i7f2SlN129UG7rUZ+doHvf5/mtiurJMqRwGB4M8=
|
||||
github.com/luxfi/rpc v1.0.0 h1:G8o89gT+CT3SOj08A5FS5G92fovKrWxs941FmUz28CA=
|
||||
github.com/luxfi/rpc v1.0.0/go.mod h1:dhxzM5rFqNpEOOHB7+8YBQ9d/V4haa6/id9AQTIIJvQ=
|
||||
github.com/luxfi/runtime v1.0.1 h1:cii3OsRiVSIl9jzfWFM6++62T1r6ZSGP+/3F0Ezhpqw=
|
||||
github.com/luxfi/runtime v1.0.1/go.mod h1:2hBKjzbEeE4dzrhUKH8dqkRgLEyiXz6GmuVusy3vJMs=
|
||||
github.com/luxfi/sampler v1.0.0 h1:k8Sf6otW83w4pQp0jXLA+g3J/joB7w7SqXQsWmNTOV0=
|
||||
github.com/luxfi/sampler v1.0.0/go.mod h1:f96/ozlj9vFfZj+akLtrHn4VpulQahwB+MQQhpeIekk=
|
||||
github.com/luxfi/timer v1.0.1 h1:3lRf3j0L/fA3Ud0j67cm/BW2In1kz+jxd1HqgKyglYY=
|
||||
github.com/luxfi/timer v1.0.1/go.mod h1:PQ15H3AEUfvWVf0kv5WhIGPWmhr9BxBv/VKLyyekpk4=
|
||||
github.com/luxfi/tls v1.0.3 h1:rK3nxSAxrUOOSHOZnKChwV4f6UJ+cfOl8KWJXAQx/SI=
|
||||
github.com/luxfi/tls v1.0.3/go.mod h1:dQqSiGE7YxXUxOwICoReUuIitBms9DYOaCeteBwmIWw=
|
||||
github.com/luxfi/utils v1.1.4 h1:8OY0jXCkpp6OotN1Y++6DATJkvtEwzq2k0p56imJlAk=
|
||||
github.com/luxfi/utils v1.1.4/go.mod h1:c3yz1RjzrB+cs5GZm+q1T3/2cCKElO9vxm9yRRtgSEM=
|
||||
github.com/luxfi/utxo v0.2.4 h1:OJB3JEzPikcoI81zX44Wf4tIvqjR/QjMpUFzRAnsdbo=
|
||||
github.com/luxfi/utxo v0.2.4/go.mod h1:7XEJzJE7apaXGs4LVzLt4djtpHDBsnfZi8jjyZmJWY0=
|
||||
github.com/luxfi/validators v1.0.0 h1:zE1HJM1lfvC+v1Lalg+MUgkKBGFWnwTfOOmXraiNnpE=
|
||||
github.com/luxfi/validators v1.0.0/go.mod h1:QGppjtI/gqprbplWc10J+4OIK8UYWpv+53mfH2aKhy4=
|
||||
github.com/luxfi/version v1.0.1 h1:T/1KYWEMmsrNQk7pN7PFPAwh/7XbeX7cFAKLBqI37Sk=
|
||||
github.com/luxfi/version v1.0.1/go.mod h1:Y5fPkQ2DB0XRBCxgSPXp4ISzL1/jptKnmFknShRJCyg=
|
||||
github.com/luxfi/vm v1.0.33 h1:+4dg0JuJox4l7Rkmjp6HnA8Hmd9gJWTljY6MqTeUIjE=
|
||||
github.com/luxfi/vm v1.0.33/go.mod h1:9VmuXBQ61UBk2+a4jB4ZOZQDULkTdbTpy3GT5j6gqV8=
|
||||
github.com/luxfi/warp v1.18.5 h1:yXFCT+lnvzJHs8nVvfUCQ9wNvhtgbDGaXJkJeiwgKf4=
|
||||
github.com/luxfi/warp v1.18.5/go.mod h1:SFyC529HDvbP/TWRAdYQSyJUliMa5JKFRtBrTLEElp4=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
|
||||
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||
github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
|
||||
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
|
||||
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
|
||||
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
|
||||
github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
|
||||
github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=
|
||||
github.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/supranational/blst v0.3.16 h1:bTDadT+3fK497EvLdWRQEjiGnUtzJ7jjIUMF0jqwYhE=
|
||||
github.com/supranational/blst v0.3.16/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw=
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
|
||||
github.com/thepudds/fzgen v0.4.3 h1:srUP/34BulQaEwPP/uHZkdjUcUjIzL7Jkf4CBVryiP8=
|
||||
github.com/thepudds/fzgen v0.4.3/go.mod h1:BhhwtRhzgvLWAjjcHDJ9pEiLD2Z9hrVIFjBCHJ//zJ4=
|
||||
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
|
||||
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
|
||||
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
|
||||
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
|
||||
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
|
||||
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
|
||||
golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 h1:jiDhWWeC7jfWqR9c/uplMOqJ0sbNlNWv0UkzE0vX1MA=
|
||||
golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90/go.mod h1:xE1HEv6b+1SCZ5/uscMRjUBKtIxworgEcEi+/n9NQDQ=
|
||||
golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
|
||||
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
|
||||
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
@@ -0,0 +1,8 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIBETCBt6ADAgECAgEAMAoGCCqGSM49BAMCMAAwIBcNOTkxMjMxMDAwMDAwWhgP
|
||||
MjA5OTEyMzEwMDAwMDBaMAAwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAR44B3Q
|
||||
XD5lXOJudiUTZiu/g5l6Uxy9JcxTtJfIpDtkXpeSoLXjDB5/i+mGsKQsLhCFWI8S
|
||||
Fgm/WR4pfEzFa30uoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAK
|
||||
BggqhkjOPQQDAgNJADBGAiEAq5oc+KsKa4V3uH/+SD0JX8Pr7xiC1dgRpJ+k8iu0
|
||||
oKQCIQCdbywYTWMs45Ku+PGvnmVGYYR5YBSyD92O0596930gcg==
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,5 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgByb+f3Bl1lXKUVKR
|
||||
q/QVTbe2QdjsmPnNiQtnEQYep6ehRANCAAR44B3QXD5lXOJudiUTZiu/g5l6Uxy9
|
||||
JcxTtJfIpDtkXpeSoLXjDB5/i+mGsKQsLhCFWI8SFgm/WR4pfEzFa30u
|
||||
-----END PRIVATE KEY-----
|
||||
@@ -0,0 +1 @@
|
||||
ZßÛk†M2³ùIšO$%Pdzö§H×!—4À‰@
|
||||
@@ -0,0 +1,222 @@
|
||||
// Copyright (C) 2019-2025, Lux Partners Limited. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package genesis
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// ChainAllocations holds genesis allocations for all chains.
|
||||
// Use NewAllocations() or AllocationBuilder to create.
|
||||
type ChainAllocations struct {
|
||||
keys []ValidatorKeyInfo
|
||||
hrp string
|
||||
amount uint64
|
||||
|
||||
// Vesting configuration (optional)
|
||||
vesting *VestingConfig
|
||||
}
|
||||
|
||||
// VestingConfig defines the vesting schedule parameters.
|
||||
type VestingConfig struct {
|
||||
StartTime uint64 // Unix timestamp when vesting starts
|
||||
Interval uint64 // Seconds between each unlock
|
||||
Periods int // Number of unlock periods
|
||||
}
|
||||
|
||||
// DefaultVesting returns the standard vesting config:
|
||||
// - Start: Jan 1, 2020
|
||||
// - 1% unlocks per year for 100 years
|
||||
func DefaultVesting() *VestingConfig {
|
||||
return &VestingConfig{
|
||||
StartTime: StakingStartTime, // Jan 1, 2020
|
||||
Interval: UnlockInterval, // 1 year
|
||||
Periods: 100, // 100 periods = 1% per year
|
||||
}
|
||||
}
|
||||
|
||||
// NewAllocations creates a ChainAllocations for the given validator keys.
|
||||
// Default amount is 100M LUX per key, no vesting.
|
||||
func NewAllocations(keys []ValidatorKeyInfo, hrp string) *ChainAllocations {
|
||||
return &ChainAllocations{
|
||||
keys: keys,
|
||||
hrp: hrp,
|
||||
amount: DefaultValidatorAllocation,
|
||||
}
|
||||
}
|
||||
|
||||
// WithAmount sets the allocation amount per key.
|
||||
func (a *ChainAllocations) WithAmount(amount uint64) *ChainAllocations {
|
||||
a.amount = amount
|
||||
return a
|
||||
}
|
||||
|
||||
// WithVesting enables vesting with the specified config.
|
||||
// Use DefaultVesting() for standard 100-year vesting from Jan 1 2020.
|
||||
func (a *ChainAllocations) WithVesting(config *VestingConfig) *ChainAllocations {
|
||||
a.vesting = config
|
||||
return a
|
||||
}
|
||||
|
||||
// PChain returns P-chain allocations in the standard AllocationJSON format.
|
||||
func (a *ChainAllocations) PChain() ([]AllocationJSON, error) {
|
||||
allocations := make([]AllocationJSON, len(a.keys))
|
||||
|
||||
for i, key := range a.keys {
|
||||
luxAddr, err := FormatChainAddress("P", a.hrp, key.ShortID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to format P-chain address for key %d: %w", i, err)
|
||||
}
|
||||
|
||||
var unlockSchedule []LockedAmount
|
||||
var initialAmount uint64
|
||||
|
||||
if a.vesting != nil {
|
||||
// Vested allocation
|
||||
unlockSchedule = buildUnlockSchedule(a.amount, a.vesting.StartTime, a.vesting.Interval, a.vesting.Periods)
|
||||
initialAmount = a.amount // X-chain gets initial amount with vesting
|
||||
} else {
|
||||
// Immediate allocation (no vesting)
|
||||
// X-chain gets full initial amount for subnet creation operations
|
||||
unlockSchedule = []LockedAmount{{Amount: a.amount, Locktime: 0}}
|
||||
initialAmount = a.amount
|
||||
}
|
||||
|
||||
allocations[i] = AllocationJSON{
|
||||
ETHAddr: key.EthAddr,
|
||||
LUXAddr: luxAddr,
|
||||
InitialAmount: initialAmount,
|
||||
UnlockSchedule: unlockSchedule,
|
||||
}
|
||||
}
|
||||
return allocations, nil
|
||||
}
|
||||
|
||||
// CChain returns C-chain allocations as a Balance map.
|
||||
// C-chain uses 18 decimals (wei), P-chain uses 6 decimals (microLux).
|
||||
// This converts the P-chain amount to C-chain wei automatically.
|
||||
func (a *ChainAllocations) CChain() map[string]Balance {
|
||||
alloc := make(map[string]Balance, len(a.keys))
|
||||
// Convert from microLux (6 decimals) to wei (18 decimals)
|
||||
// Multiply by 10^12 to go from 6 to 18 decimals
|
||||
cchainAmount := new(big.Int).Mul(
|
||||
new(big.Int).SetUint64(a.amount),
|
||||
new(big.Int).Exp(big.NewInt(10), big.NewInt(12), nil),
|
||||
)
|
||||
for _, key := range a.keys {
|
||||
alloc[key.EthAddr] = Balance{
|
||||
Balance: fmt.Sprintf("0x%x", cchainAmount),
|
||||
}
|
||||
}
|
||||
return alloc
|
||||
}
|
||||
|
||||
// CChainMap returns C-chain allocations as a simple string map (for netrunner compatibility).
|
||||
// C-chain uses 18 decimals (wei), P-chain uses 6 decimals (microLux).
|
||||
func (a *ChainAllocations) CChainMap() map[string]map[string]string {
|
||||
alloc := make(map[string]map[string]string, len(a.keys))
|
||||
// Convert from microLux (6 decimals) to wei (18 decimals)
|
||||
cchainAmount := new(big.Int).Mul(
|
||||
new(big.Int).SetUint64(a.amount),
|
||||
new(big.Int).Exp(big.NewInt(10), big.NewInt(12), nil),
|
||||
)
|
||||
balanceHex := fmt.Sprintf("0x%x", cchainAmount)
|
||||
for _, key := range a.keys {
|
||||
alloc[key.EthAddr] = map[string]string{"balance": balanceHex}
|
||||
}
|
||||
return alloc
|
||||
}
|
||||
|
||||
// PChainMap returns P-chain allocations as interface maps (for netrunner compatibility).
|
||||
func (a *ChainAllocations) PChainMap() ([]map[string]interface{}, error) {
|
||||
allocations := make([]map[string]interface{}, len(a.keys))
|
||||
|
||||
for i, key := range a.keys {
|
||||
luxAddr, err := FormatChainAddress("P", a.hrp, key.ShortID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to format P-chain address for key %d: %w", i, err)
|
||||
}
|
||||
|
||||
var unlockSchedule []map[string]interface{}
|
||||
var initialAmount uint64
|
||||
|
||||
if a.vesting != nil {
|
||||
// Build vesting schedule as maps
|
||||
locked := buildUnlockSchedule(a.amount, a.vesting.StartTime, a.vesting.Interval, a.vesting.Periods)
|
||||
unlockSchedule = make([]map[string]interface{}, len(locked))
|
||||
for j, l := range locked {
|
||||
unlockSchedule[j] = map[string]interface{}{
|
||||
"amount": l.Amount,
|
||||
"locktime": l.Locktime,
|
||||
}
|
||||
}
|
||||
initialAmount = a.amount
|
||||
} else {
|
||||
// Immediate allocation (no vesting)
|
||||
// X-chain gets full initial amount for subnet creation operations
|
||||
unlockSchedule = []map[string]interface{}{
|
||||
{"amount": a.amount, "locktime": uint64(0)},
|
||||
}
|
||||
initialAmount = a.amount
|
||||
}
|
||||
|
||||
allocations[i] = map[string]interface{}{
|
||||
"ethAddr": key.EthAddr,
|
||||
"luxAddr": luxAddr,
|
||||
"initialAmount": initialAmount,
|
||||
"unlockSchedule": unlockSchedule,
|
||||
}
|
||||
}
|
||||
return allocations, nil
|
||||
}
|
||||
|
||||
// XChain returns X-chain allocations (same format as P-chain for cross-chain compatibility).
|
||||
func (a *ChainAllocations) XChain() ([]AllocationJSON, error) {
|
||||
return a.PChain() // X-chain uses same allocation format
|
||||
}
|
||||
|
||||
// All returns allocations for all chains in a combined struct.
|
||||
func (a *ChainAllocations) All() (*AllChainAllocations, error) {
|
||||
pchain, err := a.PChain()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &AllChainAllocations{
|
||||
PChain: pchain,
|
||||
CChain: a.CChain(),
|
||||
XChain: pchain, // X uses same format as P
|
||||
}, nil
|
||||
}
|
||||
|
||||
// AllChainAllocations contains allocations for all chains.
|
||||
type AllChainAllocations struct {
|
||||
PChain []AllocationJSON
|
||||
CChain map[string]Balance
|
||||
XChain []AllocationJSON
|
||||
}
|
||||
|
||||
// --- Convenience functions for simple use cases ---
|
||||
|
||||
// QuickAllocations creates immediate allocations (no vesting) for all chains.
|
||||
// This is the simplest way to set up a local/test network.
|
||||
func QuickAllocations(keys []ValidatorKeyInfo, hrp string, amount uint64) (*AllChainAllocations, error) {
|
||||
return NewAllocations(keys, hrp).WithAmount(amount).All()
|
||||
}
|
||||
|
||||
// MainnetAllocations creates allocations with standard mainnet vesting (1B LUX, 100 years).
|
||||
func MainnetAllocations(keys []ValidatorKeyInfo, hrp string) (*AllChainAllocations, error) {
|
||||
return NewAllocations(keys, hrp).
|
||||
WithAmount(OneBillionLUX).
|
||||
WithVesting(DefaultVesting()).
|
||||
All()
|
||||
}
|
||||
|
||||
// TestnetAllocations creates allocations suitable for testnet (100M LUX, no vesting).
|
||||
func TestnetAllocations(keys []ValidatorKeyInfo, hrp string) (*AllChainAllocations, error) {
|
||||
return NewAllocations(keys, hrp).
|
||||
WithAmount(OneHundredMillionLUX).
|
||||
All()
|
||||
}
|
||||
@@ -0,0 +1,285 @@
|
||||
// Copyright (C) 2025, Lux Partners Limited All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package genesis
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/luxfi/ids"
|
||||
"github.com/luxfi/rpc"
|
||||
)
|
||||
|
||||
// GetAPIContext returns a context with a default timeout for API calls.
|
||||
func GetAPIContext() (context.Context, context.CancelFunc) {
|
||||
return context.WithTimeout(context.Background(), 30*time.Second)
|
||||
}
|
||||
|
||||
// GetDefaultBlockchainAirdropKeyName returns the default key name for blockchain airdrops
|
||||
func GetDefaultBlockchainAirdropKeyName(blockchainName string) string {
|
||||
return fmt.Sprintf("%s-airdrop", blockchainName)
|
||||
}
|
||||
|
||||
// Account represents an account in the genesis allocation
|
||||
type Account struct {
|
||||
Balance interface{} `json:"balance"` // Can be string (hex) or number
|
||||
Code interface{} `json:"code,omitempty"` // Can be string (hex) or []byte
|
||||
Storage map[string]string `json:"storage,omitempty"`
|
||||
Nonce interface{} `json:"nonce,omitempty"` // Can be string (hex) or uint64
|
||||
}
|
||||
|
||||
// GetBalance returns the balance as a big.Int
|
||||
func (a *Account) GetBalance() *big.Int {
|
||||
if a.Balance == nil {
|
||||
return big.NewInt(0)
|
||||
}
|
||||
switch v := a.Balance.(type) {
|
||||
case string:
|
||||
// Handle hex string
|
||||
balance := new(big.Int)
|
||||
if strings.HasPrefix(v, "0x") {
|
||||
balance.SetString(v[2:], 16)
|
||||
} else {
|
||||
balance.SetString(v, 10)
|
||||
}
|
||||
return balance
|
||||
case float64:
|
||||
return big.NewInt(int64(v))
|
||||
case int64:
|
||||
return big.NewInt(v)
|
||||
default:
|
||||
return big.NewInt(0)
|
||||
}
|
||||
}
|
||||
|
||||
// GetCode returns the code as a byte slice
|
||||
func (a *Account) GetCode() []byte {
|
||||
if a.Code == nil {
|
||||
return nil
|
||||
}
|
||||
switch v := a.Code.(type) {
|
||||
case string:
|
||||
// Handle hex string
|
||||
if strings.HasPrefix(v, "0x") {
|
||||
code, _ := hex.DecodeString(v[2:])
|
||||
return code
|
||||
}
|
||||
return []byte(v)
|
||||
case []byte:
|
||||
return v
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// UnmarshalJSON custom unmarshaler for Account to handle hex string balances
|
||||
func (a *Account) UnmarshalJSON(data []byte) error {
|
||||
type Alias Account
|
||||
aux := &struct {
|
||||
Balance interface{} `json:"balance"`
|
||||
*Alias
|
||||
}{
|
||||
Alias: (*Alias)(a),
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(data, &aux); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Parse balance (hex string or numeric)
|
||||
if aux.Balance != nil {
|
||||
switch v := aux.Balance.(type) {
|
||||
case string:
|
||||
// Remove quotes if present
|
||||
balance := new(big.Int)
|
||||
if strings.HasPrefix(v, "0x") {
|
||||
if _, ok := balance.SetString(v[2:], 16); !ok {
|
||||
return fmt.Errorf("invalid hex balance: %s", v)
|
||||
}
|
||||
} else {
|
||||
if _, ok := balance.SetString(v, 10); !ok {
|
||||
return fmt.Errorf("invalid balance: %s", v)
|
||||
}
|
||||
}
|
||||
a.Balance = balance
|
||||
case float64:
|
||||
a.Balance = big.NewInt(int64(v))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// EVMGenesis represents a EVM genesis configuration
|
||||
type EVMGenesis struct {
|
||||
Config map[string]interface{} `json:"config"`
|
||||
Alloc map[string]Account `json:"alloc"`
|
||||
Timestamp interface{} `json:"timestamp,omitempty"` // Can be string (hex) or uint64
|
||||
GasLimit interface{} `json:"gasLimit"` // Can be string (hex) or uint64
|
||||
Difficulty string `json:"difficulty,omitempty"`
|
||||
MixHash string `json:"mixHash,omitempty"`
|
||||
Coinbase string `json:"coinbase,omitempty"`
|
||||
Number string `json:"number,omitempty"`
|
||||
GasUsed string `json:"gasUsed,omitempty"`
|
||||
ParentHash string `json:"parentHash,omitempty"`
|
||||
Nonce string `json:"nonce,omitempty"`
|
||||
ExtraData string `json:"extraData,omitempty"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON custom unmarshaler to handle hex string or numeric values
|
||||
func (g *EVMGenesis) UnmarshalJSON(data []byte) error {
|
||||
type Alias EVMGenesis
|
||||
aux := &struct {
|
||||
Timestamp interface{} `json:"timestamp,omitempty"`
|
||||
GasLimit interface{} `json:"gasLimit"`
|
||||
*Alias
|
||||
}{
|
||||
Alias: (*Alias)(g),
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(data, &aux); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Parse timestamp (numeric or hex string)
|
||||
if aux.Timestamp != nil {
|
||||
switch v := aux.Timestamp.(type) {
|
||||
case float64:
|
||||
g.Timestamp = uint64(v)
|
||||
case string:
|
||||
if strings.HasPrefix(v, "0x") {
|
||||
val, err := strconv.ParseUint(v[2:], 16, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.Timestamp = val
|
||||
} else {
|
||||
val, err := strconv.ParseUint(v, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.Timestamp = val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse gasLimit (numeric or hex string)
|
||||
if aux.GasLimit != nil {
|
||||
switch v := aux.GasLimit.(type) {
|
||||
case float64:
|
||||
g.GasLimit = uint64(v)
|
||||
case string:
|
||||
if strings.HasPrefix(v, "0x") {
|
||||
val, err := strconv.ParseUint(v[2:], 16, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.GasLimit = val
|
||||
} else {
|
||||
val, err := strconv.ParseUint(v, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.GasLimit = val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ByteSliceToEVMGenesis converts a byte slice to a EVM genesis
|
||||
func ByteSliceToEVMGenesis(bytes []byte) (*EVMGenesis, error) {
|
||||
var genesis EVMGenesis
|
||||
if err := json.Unmarshal(bytes, &genesis); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &genesis, nil
|
||||
}
|
||||
|
||||
// ByteSliceIsEVMGenesis checks if a byte slice is a EVM genesis
|
||||
func ByteSliceIsEVMGenesis(bytes []byte) bool {
|
||||
var genesis EVMGenesis
|
||||
err := json.Unmarshal(bytes, &genesis)
|
||||
return err == nil && genesis.Config != nil
|
||||
}
|
||||
|
||||
// GetKeyNames returns a list of key names from the given directory.
|
||||
func GetKeyNames(keyDir string, includeEwoq bool) ([]string, error) {
|
||||
entries, err := os.ReadDir(keyDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
keys := make([]string, 0, len(entries))
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
continue
|
||||
}
|
||||
name := entry.Name()
|
||||
if filepath.Ext(name) != ".pk" {
|
||||
continue
|
||||
}
|
||||
keys = append(keys, strings.TrimSuffix(name, ".pk"))
|
||||
}
|
||||
if includeEwoq {
|
||||
keys = append(keys, "ewoq")
|
||||
}
|
||||
return keys, nil
|
||||
}
|
||||
|
||||
// GetBlockchainIDFromAlias gets a blockchain ID from its alias on the network.
|
||||
func GetBlockchainIDFromAlias(endpoint string, alias string) (ids.ID, error) {
|
||||
type getBlockchainIDArgs struct {
|
||||
Alias string `json:"alias"`
|
||||
}
|
||||
type getBlockchainIDReply struct {
|
||||
BlockchainID ids.ID `json:"blockchainID"`
|
||||
}
|
||||
requester := rpc.NewEndpointRequester(endpoint + "/ext/info")
|
||||
ctx, cancel := GetAPIContext()
|
||||
defer cancel()
|
||||
reply := &getBlockchainIDReply{}
|
||||
if err := requester.SendRequest(ctx, "info.getBlockchainID", &getBlockchainIDArgs{
|
||||
Alias: alias,
|
||||
}, reply); err != nil {
|
||||
return ids.Empty, err
|
||||
}
|
||||
return reply.BlockchainID, nil
|
||||
}
|
||||
|
||||
// GetChainID extracts the chain ID from genesis data
|
||||
func GetChainID(genesisData []byte) (*big.Int, error) {
|
||||
genesis, err := ByteSliceToEVMGenesis(genesisData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if genesis.Config == nil {
|
||||
return nil, fmt.Errorf("no config in genesis")
|
||||
}
|
||||
if chainID, ok := genesis.Config["chainId"]; ok {
|
||||
switch v := chainID.(type) {
|
||||
case float64:
|
||||
return big.NewInt(int64(v)), nil
|
||||
case int64:
|
||||
return big.NewInt(v), nil
|
||||
case string:
|
||||
id, ok := new(big.Int).SetString(v, 10)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid chain ID string: %s", v)
|
||||
}
|
||||
return id, nil
|
||||
case *big.Int:
|
||||
return v, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("chain ID not found in genesis")
|
||||
}
|
||||
@@ -0,0 +1,285 @@
|
||||
// Copyright (C) 2019-2025, Lux Partners Limited. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package genesis
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/luxfi/ids"
|
||||
)
|
||||
|
||||
// ChainRole represents the role/purpose of a chain in the network
|
||||
type ChainRole string
|
||||
|
||||
const (
|
||||
RolePlatform ChainRole = "P" // Platform/staking chain
|
||||
RoleExchange ChainRole = "X" // Exchange/asset transfer chain
|
||||
RoleContract ChainRole = "C" // Contract/EVM chain
|
||||
RoleQuantum ChainRole = "Q" // Quantum-resistant chain
|
||||
RoleAttest ChainRole = "A" // Attestation chain
|
||||
RoleBridge ChainRole = "B" // Bridge chain
|
||||
RoleThreshold ChainRole = "T" // Threshold/MPC chain
|
||||
RoleZK ChainRole = "Z" // Zero-knowledge chain
|
||||
RoleGraph ChainRole = "G" // Graph/data chain (future)
|
||||
RoleIdentity ChainRole = "I" // Identity chain (future)
|
||||
RoleKMS ChainRole = "K" // KMS chain (future)
|
||||
)
|
||||
|
||||
// ChainConfig represents a single chain's configuration
|
||||
type ChainConfig struct {
|
||||
// ChainID is the current active chain ID (can change via migration)
|
||||
ChainID ids.ID `json:"chainID"`
|
||||
|
||||
// VMID identifies which VM runs this chain
|
||||
VMID ids.ID `json:"vmID"`
|
||||
|
||||
// Role indicates the chain's purpose (P/X/C/Q/etc.)
|
||||
Role ChainRole `json:"role"`
|
||||
|
||||
// Name is the human-readable chain name
|
||||
Name string `json:"name"`
|
||||
|
||||
// Aliases for RPC/API routing
|
||||
Aliases []string `json:"aliases,omitempty"`
|
||||
|
||||
// EVMChainID is the EVM chain ID (only for EVM chains)
|
||||
EVMChainID uint64 `json:"evmChainID,omitempty"`
|
||||
|
||||
// Genesis is the chain-specific genesis data
|
||||
Genesis json.RawMessage `json:"genesis,omitempty"`
|
||||
|
||||
// PreviousChainIDs tracks historical chain IDs for migration
|
||||
PreviousChainIDs []ChainIDHistory `json:"previousChainIDs,omitempty"`
|
||||
}
|
||||
|
||||
// ChainIDHistory records a historical chain ID and when it was active
|
||||
type ChainIDHistory struct {
|
||||
ChainID ids.ID `json:"chainID"`
|
||||
StartTime uint64 `json:"startTime"`
|
||||
EndTime uint64 `json:"endTime"`
|
||||
}
|
||||
|
||||
// ChainMapping maps chain roles to their configurations
|
||||
// This allows dynamic chain ID assignment and migration
|
||||
type ChainMapping struct {
|
||||
// Version of the chain mapping schema
|
||||
Version uint32 `json:"version"`
|
||||
|
||||
// Chains maps role -> chain config
|
||||
Chains map[ChainRole]*ChainConfig `json:"chains"`
|
||||
|
||||
// MigrationPending indicates if a migration is in progress
|
||||
MigrationPending *ChainMigration `json:"migrationPending,omitempty"`
|
||||
}
|
||||
|
||||
// ChainMigration represents a pending chain ID migration
|
||||
type ChainMigration struct {
|
||||
// Role being migrated
|
||||
Role ChainRole `json:"role"`
|
||||
|
||||
// OldChainID is the current chain ID
|
||||
OldChainID ids.ID `json:"oldChainID"`
|
||||
|
||||
// NewChainID is the target chain ID
|
||||
NewChainID ids.ID `json:"newChainID"`
|
||||
|
||||
// ActivationHeight is when the migration becomes active
|
||||
ActivationHeight uint64 `json:"activationHeight"`
|
||||
|
||||
// ProposalID links to governance proposal
|
||||
ProposalID ids.ID `json:"proposalID,omitempty"`
|
||||
}
|
||||
|
||||
// NewChainMapping creates a default chain mapping for a network
|
||||
func NewChainMapping(networkID uint32) *ChainMapping {
|
||||
return &ChainMapping{
|
||||
Version: 1,
|
||||
Chains: make(map[ChainRole]*ChainConfig),
|
||||
}
|
||||
}
|
||||
|
||||
// GetChainByRole returns the chain config for a given role
|
||||
func (m *ChainMapping) GetChainByRole(role ChainRole) (*ChainConfig, error) {
|
||||
if chain, ok := m.Chains[role]; ok {
|
||||
return chain, nil
|
||||
}
|
||||
return nil, fmt.Errorf("chain role %s not found", role)
|
||||
}
|
||||
|
||||
// GetChainByID returns the chain config with the given chain ID
|
||||
func (m *ChainMapping) GetChainByID(chainID ids.ID) (*ChainConfig, ChainRole, error) {
|
||||
for role, chain := range m.Chains {
|
||||
if chain.ChainID == chainID {
|
||||
return chain, role, nil
|
||||
}
|
||||
}
|
||||
return nil, "", fmt.Errorf("chain ID %s not found", chainID)
|
||||
}
|
||||
|
||||
// RegisterChain adds or updates a chain in the mapping
|
||||
func (m *ChainMapping) RegisterChain(role ChainRole, config *ChainConfig) error {
|
||||
if config.Role != role {
|
||||
return fmt.Errorf("chain config role %s doesn't match registration role %s", config.Role, role)
|
||||
}
|
||||
m.Chains[role] = config
|
||||
return nil
|
||||
}
|
||||
|
||||
// ProposeMigration initiates a chain ID migration
|
||||
func (m *ChainMapping) ProposeMigration(role ChainRole, newChainID ids.ID, activationHeight uint64, proposalID ids.ID) error {
|
||||
chain, err := m.GetChainByRole(role)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if m.MigrationPending != nil {
|
||||
return fmt.Errorf("migration already pending for role %s", m.MigrationPending.Role)
|
||||
}
|
||||
|
||||
m.MigrationPending = &ChainMigration{
|
||||
Role: role,
|
||||
OldChainID: chain.ChainID,
|
||||
NewChainID: newChainID,
|
||||
ActivationHeight: activationHeight,
|
||||
ProposalID: proposalID,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ApplyMigration applies a pending migration at the given height
|
||||
func (m *ChainMapping) ApplyMigration(currentHeight uint64) error {
|
||||
if m.MigrationPending == nil {
|
||||
return nil // No pending migration
|
||||
}
|
||||
|
||||
if currentHeight < m.MigrationPending.ActivationHeight {
|
||||
return nil // Not yet time to migrate
|
||||
}
|
||||
|
||||
chain, err := m.GetChainByRole(m.MigrationPending.Role)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Record the old chain ID in history
|
||||
history := ChainIDHistory{
|
||||
ChainID: chain.ChainID,
|
||||
StartTime: 0, // Would need to track from chain state
|
||||
EndTime: currentHeight,
|
||||
}
|
||||
chain.PreviousChainIDs = append(chain.PreviousChainIDs, history)
|
||||
|
||||
// Apply the new chain ID
|
||||
chain.ChainID = m.MigrationPending.NewChainID
|
||||
|
||||
// Clear pending migration
|
||||
m.MigrationPending = nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DefaultMainnetMapping returns the default chain mapping for mainnet
|
||||
func DefaultMainnetMapping() *ChainMapping {
|
||||
return &ChainMapping{
|
||||
Version: 1,
|
||||
Chains: map[ChainRole]*ChainConfig{
|
||||
RolePlatform: {
|
||||
ChainID: ids.PChainID,
|
||||
VMID: ids.Empty, // Platform VM ID
|
||||
Role: RolePlatform,
|
||||
Name: "Platform Chain",
|
||||
Aliases: []string{"P", "platform"},
|
||||
},
|
||||
RoleExchange: {
|
||||
ChainID: ids.XChainID,
|
||||
VMID: ids.Empty, // Exchange VM ID
|
||||
Role: RoleExchange,
|
||||
Name: "Exchange Chain",
|
||||
Aliases: []string{"X", "exchange"},
|
||||
},
|
||||
RoleContract: {
|
||||
ChainID: ids.CChainID,
|
||||
VMID: ids.Empty, // EVM VM ID
|
||||
Role: RoleContract,
|
||||
Name: "Contract Chain",
|
||||
Aliases: []string{"C", "contract", "evm"},
|
||||
EVMChainID: 96369, // Mainnet EVM chain ID
|
||||
},
|
||||
RoleQuantum: {
|
||||
ChainID: ids.QChainID,
|
||||
VMID: ids.Empty, // Quantum VM ID
|
||||
Role: RoleQuantum,
|
||||
Name: "Quantum Chain",
|
||||
Aliases: []string{"Q", "quantum", "pq"},
|
||||
},
|
||||
RoleAttest: {
|
||||
ChainID: ids.AChainID,
|
||||
VMID: ids.Empty, // AI/Attestation VM ID
|
||||
Role: RoleAttest,
|
||||
Name: "Attestation Chain",
|
||||
Aliases: []string{"A", "attest", "ai"},
|
||||
},
|
||||
RoleBridge: {
|
||||
ChainID: ids.BChainID,
|
||||
VMID: ids.Empty, // Bridge VM ID
|
||||
Role: RoleBridge,
|
||||
Name: "Bridge Chain",
|
||||
Aliases: []string{"B", "bridge"},
|
||||
},
|
||||
RoleThreshold: {
|
||||
ChainID: ids.TChainID,
|
||||
VMID: ids.Empty, // Threshold VM ID
|
||||
Role: RoleThreshold,
|
||||
Name: "Threshold Chain",
|
||||
Aliases: []string{"T", "threshold", "mpc"},
|
||||
},
|
||||
RoleZK: {
|
||||
ChainID: ids.ZChainID,
|
||||
VMID: ids.Empty, // ZK VM ID
|
||||
Role: RoleZK,
|
||||
Name: "Zero-Knowledge Chain",
|
||||
Aliases: []string{"Z", "zk", "zkvm"},
|
||||
},
|
||||
RoleGraph: {
|
||||
ChainID: ids.GChainID,
|
||||
VMID: ids.Empty, // Graph VM ID
|
||||
Role: RoleGraph,
|
||||
Name: "Graph Chain",
|
||||
Aliases: []string{"G", "graph", "dgraph"},
|
||||
},
|
||||
RoleKMS: {
|
||||
ChainID: ids.KChainID,
|
||||
VMID: ids.Empty, // KMS VM ID
|
||||
Role: RoleKMS,
|
||||
Name: "KMS Chain",
|
||||
Aliases: []string{"K", "kms"},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalJSON implements custom JSON marshaling
|
||||
func (m *ChainMapping) MarshalJSON() ([]byte, error) {
|
||||
type Alias ChainMapping
|
||||
return json.Marshal(&struct {
|
||||
*Alias
|
||||
}{
|
||||
Alias: (*Alias)(m),
|
||||
})
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements custom JSON unmarshaling
|
||||
func (m *ChainMapping) UnmarshalJSON(data []byte) error {
|
||||
type Alias ChainMapping
|
||||
aux := &struct {
|
||||
*Alias
|
||||
}{
|
||||
Alias: (*Alias)(m),
|
||||
}
|
||||
if err := json.Unmarshal(data, &aux); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
// Copyright (C) 2019-2025, Lux Partners Limited. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package genesis
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/luxfi/constants"
|
||||
"github.com/luxfi/ids"
|
||||
)
|
||||
|
||||
// GetConfig returns the genesis config for a network, loading from files
|
||||
func GetConfig(networkID uint32) *Config {
|
||||
// Try to load from standard locations based on network
|
||||
var networkName string
|
||||
switch networkID {
|
||||
case constants.MainnetID, constants.MainnetChainID:
|
||||
networkName = "mainnet"
|
||||
case constants.TestnetID, constants.TestnetChainID:
|
||||
networkName = "testnet"
|
||||
case constants.DevnetID, constants.DevnetChainID:
|
||||
networkName = "devnet"
|
||||
case constants.LocalID:
|
||||
networkName = "custom"
|
||||
default:
|
||||
networkName = "custom"
|
||||
}
|
||||
|
||||
// Try standard genesis locations (user overrides checked first)
|
||||
home, _ := os.UserHomeDir()
|
||||
candidates := []string{
|
||||
// User override (highest priority)
|
||||
filepath.Join(home, ".lux/genesis", networkName),
|
||||
// System paths
|
||||
filepath.Join("/etc/lux/genesis", networkName),
|
||||
// Docker container paths
|
||||
filepath.Join("/app/genesis", networkName),
|
||||
filepath.Join("/app/configs/genesis", networkName),
|
||||
// Source tree (lowest priority)
|
||||
filepath.Join(home, "work/lux/genesis/configs", networkName),
|
||||
}
|
||||
|
||||
for _, dir := range candidates {
|
||||
if config, err := GetConfigFromDir(dir); err == nil {
|
||||
return config
|
||||
}
|
||||
}
|
||||
|
||||
// Return empty config - must be built dynamically
|
||||
return &Config{
|
||||
NetworkID: networkID,
|
||||
Message: fmt.Sprintf("Lux %s Genesis", networkName),
|
||||
}
|
||||
}
|
||||
|
||||
// GetConfigFile loads genesis config from a single JSON file
|
||||
// The JSON file should be in ConfigOutput format (string-encoded addresses)
|
||||
// which is the standard serialization format for genesis files.
|
||||
func GetConfigFile(filepath string) (*Config, error) {
|
||||
data, err := os.ReadFile(filepath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read genesis file: %w", err)
|
||||
}
|
||||
|
||||
// Unmarshal into ConfigOutput first (uses string fields for addresses)
|
||||
var output ConfigOutput
|
||||
if err := json.Unmarshal(data, &output); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse genesis config: %w", err)
|
||||
}
|
||||
|
||||
// Parse allocations from string format
|
||||
allocations, err := parseAllocations(output.Allocations)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse allocations: %w", err)
|
||||
}
|
||||
|
||||
// Parse stakers from string format
|
||||
stakers, err := parseStakers(output.InitialStakers)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse stakers: %w", err)
|
||||
}
|
||||
|
||||
// Parse staked funds addresses
|
||||
stakedFunds, err := parseAddresses(output.InitialStakedFunds)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse staked funds: %w", err)
|
||||
}
|
||||
|
||||
return &Config{
|
||||
NetworkID: output.NetworkID,
|
||||
Allocations: allocations,
|
||||
StartTime: output.StartTime,
|
||||
InitialStakeDuration: output.InitialStakeDuration,
|
||||
InitialStakeDurationOffset: output.InitialStakeDurationOffset,
|
||||
InitialStakedFunds: stakedFunds,
|
||||
InitialStakers: stakers,
|
||||
CChainGenesis: output.CChainGenesis,
|
||||
DChainGenesis: output.DChainGenesis,
|
||||
QChainGenesis: output.QChainGenesis,
|
||||
AChainGenesis: output.AChainGenesis,
|
||||
BChainGenesis: output.BChainGenesis,
|
||||
TChainGenesis: output.TChainGenesis,
|
||||
ZChainGenesis: output.ZChainGenesis,
|
||||
GChainGenesis: output.GChainGenesis,
|
||||
KChainGenesis: output.KChainGenesis,
|
||||
Message: output.Message,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetConfigFromDir builds genesis config from component files in a directory
|
||||
// Expects: network.json, pchain.json, cchain.json
|
||||
func GetConfigFromDir(dir string) (*Config, error) {
|
||||
// Load network config
|
||||
networkPath := filepath.Join(dir, "network.json")
|
||||
networkData, err := os.ReadFile(networkPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read network.json: %w", err)
|
||||
}
|
||||
var network NetworkConfig
|
||||
if err := json.Unmarshal(networkData, &network); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse network.json: %w", err)
|
||||
}
|
||||
|
||||
// Load P-Chain config
|
||||
pchainPath := filepath.Join(dir, "pchain.json")
|
||||
pchainData, err := os.ReadFile(pchainPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read pchain.json: %w", err)
|
||||
}
|
||||
var pchain PChainConfig
|
||||
if err := json.Unmarshal(pchainData, &pchain); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse pchain.json: %w", err)
|
||||
}
|
||||
|
||||
// Load C-Chain config (kept as raw JSON string)
|
||||
cchainPath := filepath.Join(dir, "cchain.json")
|
||||
cchainData, err := os.ReadFile(cchainPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read cchain.json: %w", err)
|
||||
}
|
||||
|
||||
// Convert allocations from JSON format to internal format
|
||||
allocations, err := parseAllocations(pchain.Allocations)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse allocations: %w", err)
|
||||
}
|
||||
|
||||
// Convert stakers from JSON format to internal format
|
||||
stakers, err := parseStakers(pchain.InitialStakers)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse stakers: %w", err)
|
||||
}
|
||||
|
||||
// Parse initial staked funds addresses
|
||||
stakedFunds, err := parseAddresses(pchain.InitialStakedFunds)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse staked funds: %w", err)
|
||||
}
|
||||
|
||||
return &Config{
|
||||
NetworkID: network.NetworkID,
|
||||
Allocations: allocations,
|
||||
StartTime: network.StartTime,
|
||||
InitialStakeDuration: pchain.InitialStakeDuration,
|
||||
InitialStakeDurationOffset: pchain.InitialStakeDurationOffset,
|
||||
InitialStakedFunds: stakedFunds,
|
||||
InitialStakers: stakers,
|
||||
CChainGenesis: string(cchainData),
|
||||
Message: network.Message,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetConfigFromEnv builds genesis config using environment variables
|
||||
// Environment variables:
|
||||
// - LUX_NETWORK_ID: network ID (default: custom)
|
||||
// - LUX_GENESIS_DIR: directory containing genesis files
|
||||
// - LUX_KEYS_DIR: directory containing node keys (default: ~/.lux/keys)
|
||||
func GetConfigFromEnv() (*Config, error) {
|
||||
networkID := uint32(constants.CustomID)
|
||||
if envID := os.Getenv("LUX_NETWORK_ID"); envID != "" {
|
||||
var id uint32
|
||||
if _, err := fmt.Sscanf(envID, "%d", &id); err == nil {
|
||||
networkID = id
|
||||
}
|
||||
}
|
||||
|
||||
// Get genesis directory from env
|
||||
genesisDir := os.Getenv("LUX_GENESIS_DIR")
|
||||
if genesisDir != "" {
|
||||
config, err := GetConfigFromDir(genesisDir)
|
||||
if err == nil {
|
||||
config.NetworkID = networkID
|
||||
return config, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to standard config
|
||||
return GetConfig(networkID), nil
|
||||
}
|
||||
|
||||
// parseAllocations converts JSON allocations to internal format
|
||||
func parseAllocations(jsonAllocs []AllocationJSON) ([]Allocation, error) {
|
||||
result := make([]Allocation, 0, len(jsonAllocs))
|
||||
for _, ja := range jsonAllocs {
|
||||
ethAddr, err := ParseETHAddress(ja.ETHAddr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid eth address %s: %w", ja.ETHAddr, err)
|
||||
}
|
||||
luxAddr, err := ParseAddress(ja.LUXAddr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid lux address %s: %w", ja.LUXAddr, err)
|
||||
}
|
||||
result = append(result, Allocation{
|
||||
ETHAddr: ethAddr,
|
||||
LUXAddr: luxAddr,
|
||||
InitialAmount: ja.InitialAmount,
|
||||
UnlockSchedule: ja.UnlockSchedule,
|
||||
})
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// parseStakers converts JSON stakers to internal format
|
||||
func parseStakers(jsonStakers []StakerJSON) ([]Staker, error) {
|
||||
result := make([]Staker, 0, len(jsonStakers))
|
||||
for _, js := range jsonStakers {
|
||||
nodeID, err := ids.NodeIDFromString(js.NodeID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid node ID %s: %w", js.NodeID, err)
|
||||
}
|
||||
rewardAddr, err := ParseAddress(js.RewardAddress)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid reward address %s: %w", js.RewardAddress, err)
|
||||
}
|
||||
result = append(result, Staker{
|
||||
NodeID: nodeID,
|
||||
RewardAddress: rewardAddr,
|
||||
DelegationFee: js.DelegationFee,
|
||||
Signer: js.Signer,
|
||||
Weight: js.Weight,
|
||||
StartTime: js.StartTime,
|
||||
EndTime: js.EndTime,
|
||||
})
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// parseAddresses converts string addresses to ShortIDs
|
||||
func parseAddresses(addrs []string) ([]ids.ShortID, error) {
|
||||
result := make([]ids.ShortID, 0, len(addrs))
|
||||
for _, addr := range addrs {
|
||||
parsed, err := ParseAddress(addr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid address %s: %w", addr, err)
|
||||
}
|
||||
result = append(result, parsed)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package genesis_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/luxfi/crypto/secp256k1"
|
||||
"github.com/luxfi/genesis/pkg/genesis"
|
||||
"github.com/luxfi/ids"
|
||||
"github.com/luxfi/utxo/secp256k1fx"
|
||||
)
|
||||
|
||||
func rpc(url, method string, params interface{}) (json.RawMessage, error) {
|
||||
payload := map[string]interface{}{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": method,
|
||||
"params": params,
|
||||
}
|
||||
body, _ := json.Marshal(payload)
|
||||
resp, err := http.Post(url, "application/json", strings.NewReader(string(body)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
data, _ := io.ReadAll(resp.Body)
|
||||
var result struct {
|
||||
Result json.RawMessage `json:"result"`
|
||||
Error *struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
} `json:"error"`
|
||||
}
|
||||
if err := json.Unmarshal(data, &result); err != nil {
|
||||
return nil, fmt.Errorf("unmarshal: %w (body: %s)", err, string(data))
|
||||
}
|
||||
if result.Error != nil {
|
||||
return nil, fmt.Errorf("rpc error %d: %s", result.Error.Code, result.Error.Message)
|
||||
}
|
||||
return result.Result, nil
|
||||
}
|
||||
|
||||
func TestTransferPChain(t *testing.T) {
|
||||
// fee0 private key
|
||||
privKeyHex := "abd51d463510cb17d7ba09e535828383d9c2c817aa386024aacce1660a1ee625"
|
||||
|
||||
// Verify addresses
|
||||
keyInfo, err := genesis.ComputeValidatorKeyInfo(privKeyHex)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
srcAddr, _ := genesis.FormatChainAddress("P", "dev", keyInfo.ShortID)
|
||||
fmt.Printf("Source (genesis): %s ShortID: %x\n", srcAddr, keyInfo.ShortID[:])
|
||||
|
||||
// Target: P-dev1c7wevm4667l4umtzh93r25wpxlpsadkhyluakg (CLI-derived)
|
||||
// ShortID: c79d966ebad7bf5e6d62b9623551c137c30eb6d7
|
||||
targetHex := "c79d966ebad7bf5e6d62b9623551c137c30eb6d7"
|
||||
var targetShortID ids.ShortID
|
||||
targetBytes, _ := hex.DecodeString(targetHex)
|
||||
copy(targetShortID[:], targetBytes)
|
||||
|
||||
dstAddr, _ := genesis.FormatChainAddress("P", "dev", targetShortID)
|
||||
fmt.Printf("Target (CLI): %s ShortID: %x\n", dstAddr, targetShortID[:])
|
||||
|
||||
// Get UTXOs for source
|
||||
pURL := "https://api.lux-dev.network/ext/bc/P"
|
||||
utxoResult, err := rpc(pURL, "platform.getUTXOs", map[string]interface{}{
|
||||
"addresses": []string{srcAddr},
|
||||
"limit": 10,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("getUTXOs: %v", err)
|
||||
}
|
||||
fmt.Printf("UTXOs: %s\n", string(utxoResult))
|
||||
|
||||
// Get balance
|
||||
balResult, err := rpc(pURL, "platform.getBalance", map[string]interface{}{
|
||||
"addresses": []string{srcAddr},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("getBalance: %v", err)
|
||||
}
|
||||
fmt.Printf("Balance: %s\n", string(balResult))
|
||||
|
||||
// For the actual transfer, I need to use the SDK wallet
|
||||
// But the SDK wallet derives its own address from the key
|
||||
// So I need to build the transaction manually
|
||||
|
||||
// Load the key
|
||||
privKeyB, _ := hex.DecodeString(privKeyHex)
|
||||
privKey, _ := secp256k1.ToPrivateKey(privKeyB)
|
||||
|
||||
// Create keychain
|
||||
kc := secp256k1fx.NewKeychain()
|
||||
kc.Add(privKey)
|
||||
|
||||
// Print what the SDK derives vs what genesis derives
|
||||
sdkAddr := privKey.PublicKey().Address()
|
||||
fmt.Printf("\nSDK-derived ShortID: %x\n", sdkAddr[:])
|
||||
fmt.Printf("Genesis-derived ShortID: %x\n", keyInfo.ShortID[:])
|
||||
fmt.Printf("Match: %v\n", sdkAddr == keyInfo.ShortID)
|
||||
|
||||
_ = context.Background
|
||||
_ = time.Second
|
||||
}
|
||||
@@ -0,0 +1,734 @@
|
||||
// Copyright (C) 2019-2025, Lux Partners Limited. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package genesis
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/luxfi/constants"
|
||||
ethcrypto "github.com/luxfi/crypto"
|
||||
"github.com/luxfi/crypto/bls"
|
||||
luxcrypto "github.com/luxfi/crypto/secp256k1"
|
||||
"github.com/luxfi/go-bip32"
|
||||
"github.com/luxfi/go-bip39"
|
||||
"github.com/luxfi/ids"
|
||||
luxtls "github.com/luxfi/tls"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultAllocationPerAccount is 500M LUX per account per chain (P and X).
|
||||
// 100 accounts × 500M × 2 chains = 100B total.
|
||||
// X-Chain: 500M free (immediately spendable).
|
||||
// P-Chain: 500M vesting 1%/year over 100 years from Jan 1 2020.
|
||||
DefaultAllocationPerAccount = 500_000_000 * Lux
|
||||
|
||||
// DefaultAllocationPerValidator is kept for backward compatibility
|
||||
DefaultAllocationPerValidator = DefaultAllocationPerAccount
|
||||
|
||||
// DefaultNumAccounts is the default number of mnemonic-derived accounts
|
||||
DefaultNumAccounts = 100
|
||||
|
||||
// StakingStartTime is Jan 1, 2020 00:00:00 UTC
|
||||
StakingStartTime = 1577836800
|
||||
|
||||
// UnlockInterval is 1 year in seconds
|
||||
UnlockInterval = 365 * 24 * 60 * 60
|
||||
|
||||
// TreasuryAddress is the C-Chain treasury with 2T LUX
|
||||
TreasuryAddress = "0x9011E888251AB053B7bD1cdB598Db4f9DEd94714"
|
||||
|
||||
// TreasuryAmount is 2 trillion LUX in microLUX (2T * 10^6)
|
||||
TreasuryAmount = 2_000_000_000_000 * Lux
|
||||
|
||||
// PChainFeeReserve is 10,000 LUX per validator for P-Chain fees
|
||||
PChainFeeReserve = 10_000 * Lux
|
||||
)
|
||||
|
||||
// KeyInfo contains parsed key information for a node
|
||||
type KeyInfo struct {
|
||||
NodeID ids.NodeID
|
||||
StakerKey []byte
|
||||
BLSPublicKey []byte
|
||||
BLSProofOfPossession []byte
|
||||
StakingAddr ids.ShortID // P-chain address derived from staker key
|
||||
ETHAddr ids.ShortID // C-chain/X-chain address
|
||||
}
|
||||
|
||||
// LoadKeysFromDir loads all node keys from a directory
|
||||
// Expected structure: keysDir/{node1,node2,...}/staker.key, staker.crt, signer.key
|
||||
func LoadKeysFromDir(keysDir string) ([]KeyInfo, error) {
|
||||
if keysDir == "" {
|
||||
home, _ := os.UserHomeDir()
|
||||
keysDir = filepath.Join(home, ".lux", "keys")
|
||||
}
|
||||
|
||||
entries, err := os.ReadDir(keysDir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read keys directory %s: %w", keysDir, err)
|
||||
}
|
||||
|
||||
var keys []KeyInfo
|
||||
for _, entry := range entries {
|
||||
if !entry.IsDir() {
|
||||
continue
|
||||
}
|
||||
|
||||
nodeDir := filepath.Join(keysDir, entry.Name())
|
||||
keyInfo, err := loadNodeKey(nodeDir)
|
||||
if err != nil {
|
||||
// Skip nodes with incomplete keys
|
||||
continue
|
||||
}
|
||||
keys = append(keys, *keyInfo)
|
||||
}
|
||||
|
||||
return keys, nil
|
||||
}
|
||||
|
||||
// loadNodeKey loads key info from a single node directory
|
||||
// Supports two directory structures:
|
||||
// 1. Modern: nodeDir/staking/staker.crt, nodeDir/bls/signer.key
|
||||
// 2. Legacy: nodeDir/staker.crt, nodeDir/signer.key
|
||||
func loadNodeKey(nodeDir string) (*KeyInfo, error) {
|
||||
// Try modern path structure first (staking/ subdirectory)
|
||||
certPath := filepath.Join(nodeDir, "staking", "staker.crt")
|
||||
certPEM, err := os.ReadFile(certPath)
|
||||
if err != nil {
|
||||
// Fall back to legacy path (direct in node dir)
|
||||
certPath = filepath.Join(nodeDir, "staker.crt")
|
||||
certPEM, err = os.ReadFile(certPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read staker.crt: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Load staker key - try modern path first
|
||||
keyPath := filepath.Join(nodeDir, "staking", "staker.key")
|
||||
keyPEM, err := os.ReadFile(keyPath)
|
||||
if err != nil {
|
||||
// Fall back to legacy path
|
||||
keyPath = filepath.Join(nodeDir, "staker.key")
|
||||
keyPEM, err = os.ReadFile(keyPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read staker.key: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Use github.com/luxfi/tls to correctly derive node ID (same method as luxd)
|
||||
tlsCert, err := luxtls.LoadTLSCertFromBytes(keyPEM, certPEM)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load TLS cert: %w", err)
|
||||
}
|
||||
|
||||
stakingCert := &ids.Certificate{
|
||||
Raw: tlsCert.Leaf.Raw,
|
||||
PublicKey: tlsCert.Leaf.PublicKey,
|
||||
}
|
||||
nodeID := ids.NodeIDFromCert(stakingCert)
|
||||
|
||||
keyInfo := &KeyInfo{
|
||||
NodeID: nodeID,
|
||||
StakerKey: keyPEM,
|
||||
}
|
||||
|
||||
// Try to load BLS signer key (optional) - try modern path first
|
||||
signerPath := filepath.Join(nodeDir, "bls", "signer.key")
|
||||
signerPEM, signerErr := os.ReadFile(signerPath)
|
||||
if signerErr != nil {
|
||||
// Fall back to legacy path
|
||||
signerPath = filepath.Join(nodeDir, "signer.key")
|
||||
signerPEM, signerErr = os.ReadFile(signerPath)
|
||||
}
|
||||
if signerErr == nil {
|
||||
// Parse BLS key and get public key + proof of possession
|
||||
sk, err := bls.SecretKeyFromBytes(signerPEM)
|
||||
if err == nil {
|
||||
pk := bls.PublicFromSecretKey(sk)
|
||||
keyInfo.BLSPublicKey = bls.PublicKeyToCompressedBytes(pk)
|
||||
// Sign public key for proof of possession (uses PoP domain separation tag)
|
||||
sig := bls.SignProofOfPossession(sk, keyInfo.BLSPublicKey)
|
||||
keyInfo.BLSProofOfPossession = bls.SignatureToBytes(sig)
|
||||
}
|
||||
}
|
||||
|
||||
// Try to load EC private key for proper ETH/P-chain address derivation
|
||||
// Look in ec/private.key subdirectory first (modern structure)
|
||||
ecKeyPath := filepath.Join(nodeDir, "ec", "private.key")
|
||||
ecKeyHex, ecErr := os.ReadFile(ecKeyPath)
|
||||
if ecErr != nil {
|
||||
// Fall back to legacy path
|
||||
ecKeyPath = filepath.Join(nodeDir, "private.key")
|
||||
ecKeyHex, ecErr = os.ReadFile(ecKeyPath)
|
||||
}
|
||||
|
||||
if ecErr == nil {
|
||||
// Parse EC key and derive proper addresses
|
||||
privKeyHex := strings.TrimSpace(string(ecKeyHex))
|
||||
privKeyBytes, err := hex.DecodeString(privKeyHex)
|
||||
if err == nil {
|
||||
// Get ETH address
|
||||
ethPrivKey, err := ethcrypto.ToECDSA(privKeyBytes)
|
||||
if err == nil {
|
||||
ethAddr := ethcrypto.PubkeyToAddress(ethPrivKey.PublicKey)
|
||||
copy(keyInfo.ETHAddr[:], ethAddr[:])
|
||||
}
|
||||
|
||||
// Get Lux ShortID (for X/P chain addresses)
|
||||
luxPrivKey, err := luxcrypto.ToPrivateKey(privKeyBytes)
|
||||
if err == nil {
|
||||
pubKey := luxPrivKey.PublicKey()
|
||||
shortID := ids.ShortID(pubKey.Address())
|
||||
copy(keyInfo.StakingAddr[:], shortID[:])
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Fallback: derive from node ID (NOT correct but backward compatible)
|
||||
// WARNING: These addresses won't have usable private keys!
|
||||
copy(keyInfo.StakingAddr[:], nodeID[:])
|
||||
copy(keyInfo.ETHAddr[:], nodeID[:])
|
||||
}
|
||||
|
||||
return keyInfo, nil
|
||||
}
|
||||
|
||||
// BuildConfigFromKeys creates a genesis config from local keys
|
||||
// Validators get fee reserve on P-Chain, all keys get X-Chain allocation with vesting
|
||||
func BuildConfigFromKeys(networkID uint32, keysDir string, allocationPerKey uint64) (*Config, error) {
|
||||
keys, err := LoadKeysFromDir(keysDir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load keys: %w", err)
|
||||
}
|
||||
|
||||
if len(keys) == 0 {
|
||||
return nil, fmt.Errorf("no keys found in %s", keysDir)
|
||||
}
|
||||
|
||||
// All loaded keys are both validators and account holders
|
||||
return buildConfigFromKeyInfos(networkID, keys, keys, allocationPerKey)
|
||||
}
|
||||
|
||||
// deriveFeeKey derives a fee reserve key from a validator's EC private key
|
||||
// The fee key is keccak256("fee-reserve:" || ecPrivKey) which gives a different
|
||||
// secp256k1 private key with a different P-chain address
|
||||
func deriveFeeKey(keysDir string, validatorKey KeyInfo, index int) (*KeyInfo, error) {
|
||||
// Read the validator's EC private key
|
||||
ecKeyPath := filepath.Join(keysDir, fmt.Sprintf("node%d", index), "ec", "private.key")
|
||||
ecKeyHex, err := os.ReadFile(ecKeyPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("no EC key at %s: %w", ecKeyPath, err)
|
||||
}
|
||||
|
||||
privKeyBytes, err := hex.DecodeString(strings.TrimSpace(string(ecKeyHex)))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid EC key hex: %w", err)
|
||||
}
|
||||
|
||||
// Derive fee private key: keccak256("fee-reserve:" || ecPrivKey)
|
||||
feePrivBytes := keccak256(append([]byte("fee-reserve:"), privKeyBytes...))
|
||||
|
||||
// Derive proper P-chain address using secp256k1
|
||||
feePrivKey, err := luxcrypto.ToPrivateKey(feePrivBytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create fee private key: %w", err)
|
||||
}
|
||||
feePubKey := feePrivKey.PublicKey()
|
||||
feeAddr := ids.ShortID(feePubKey.Address())
|
||||
|
||||
// Derive ETH address
|
||||
ethPrivKey, err := ethcrypto.ToECDSA(feePrivBytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create fee ETH key: %w", err)
|
||||
}
|
||||
ethAddr := ethcrypto.PubkeyToAddress(ethPrivKey.PublicKey)
|
||||
var ethShortID ids.ShortID
|
||||
copy(ethShortID[:], ethAddr[:])
|
||||
|
||||
// Save fee private key for later use by deploy tools
|
||||
feeKeyDir := filepath.Join(keysDir, fmt.Sprintf("fee%d", index))
|
||||
os.MkdirAll(filepath.Join(feeKeyDir, "ec"), 0700)
|
||||
feeKeyHex := hex.EncodeToString(feePrivBytes)
|
||||
os.WriteFile(filepath.Join(feeKeyDir, "ec", "private.key"), []byte(feeKeyHex), 0600)
|
||||
|
||||
fmt.Fprintf(os.Stderr, "Fee key %d: addr=%s ethAddr=0x%x saved to %s\n",
|
||||
index, feeAddr, ethAddr, feeKeyDir)
|
||||
|
||||
return &KeyInfo{
|
||||
StakingAddr: feeAddr,
|
||||
ETHAddr: ethShortID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// buildUnlockSchedule creates a vesting schedule
|
||||
func buildUnlockSchedule(totalAmount uint64, startTime uint64, interval uint64, periods int) []LockedAmount {
|
||||
amountPerPeriod := totalAmount / uint64(periods)
|
||||
schedule := make([]LockedAmount, 0, periods)
|
||||
|
||||
for i := 0; i < periods; i++ {
|
||||
schedule = append(schedule, LockedAmount{
|
||||
Amount: amountPerPeriod,
|
||||
Locktime: startTime + uint64(i)*interval,
|
||||
})
|
||||
}
|
||||
|
||||
return schedule
|
||||
}
|
||||
|
||||
// buildCChainGenesisTreasury creates C-chain genesis JSON with only the treasury allocation.
|
||||
// Treasury: 0x9011E888251AB053B7bD1cdB598Db4f9DEd94714 gets 2T LUX.
|
||||
// No mnemonic-derived account allocations on C-Chain.
|
||||
func buildCChainGenesisTreasury(networkID uint32) (string, error) {
|
||||
// C-Chain uses wei (18 decimals), TreasuryAmount is in microLUX (6 decimals)
|
||||
// Multiply by 1e12 to convert microLUX → wei
|
||||
treasuryWei := new(big.Int).Mul(
|
||||
big.NewInt(int64(TreasuryAmount)),
|
||||
new(big.Int).Exp(big.NewInt(10), big.NewInt(12), nil),
|
||||
)
|
||||
alloc := map[string]Balance{
|
||||
TreasuryAddress: {
|
||||
Balance: fmt.Sprintf("0x%x", treasuryWei),
|
||||
},
|
||||
}
|
||||
|
||||
return marshalCChainGenesis(networkID, alloc)
|
||||
}
|
||||
|
||||
// buildCChainGenesis creates C-chain genesis JSON with per-key allocations (legacy).
|
||||
func buildCChainGenesis(networkID uint32, keys []KeyInfo, allocationPerKey uint64) (string, error) {
|
||||
alloc := make(map[string]Balance)
|
||||
for _, key := range keys {
|
||||
addr := fmt.Sprintf("0x%s", key.ETHAddr.Hex())
|
||||
alloc[addr] = Balance{
|
||||
Balance: fmt.Sprintf("0x%x", allocationPerKey),
|
||||
}
|
||||
}
|
||||
|
||||
return marshalCChainGenesis(networkID, alloc)
|
||||
}
|
||||
|
||||
// marshalCChainGenesis marshals a C-chain genesis config with the given allocations.
|
||||
func marshalCChainGenesis(networkID uint32, alloc map[string]Balance) (string, error) {
|
||||
cchain := CChainConfig{
|
||||
Config: CChainParams{
|
||||
ChainID: uint64(networkID),
|
||||
HomesteadBlock: 0,
|
||||
EIP150Block: 0,
|
||||
EIP155Block: 0,
|
||||
EIP158Block: 0,
|
||||
ByzantiumBlock: 0,
|
||||
ConstantinopleBlock: 0,
|
||||
PetersburgBlock: 0,
|
||||
IstanbulBlock: 0,
|
||||
MuirGlacierBlock: 0,
|
||||
BerlinBlock: 0,
|
||||
LondonBlock: 0,
|
||||
FeeConfig: FeeConfig{
|
||||
GasLimit: 12000000,
|
||||
TargetBlockRate: 2,
|
||||
MinBaseFee: 25000000000,
|
||||
TargetGas: 60000000,
|
||||
BaseFeeChangeDenominator: 36,
|
||||
MinBlockGasCost: 0,
|
||||
MaxBlockGasCost: 1000000,
|
||||
BlockGasCostStep: 200000,
|
||||
},
|
||||
},
|
||||
Nonce: "0x0",
|
||||
Timestamp: fmt.Sprintf("0x%x", time.Now().Unix()),
|
||||
ExtraData: "0x",
|
||||
GasLimit: "0xb71b00",
|
||||
Difficulty: "0x0",
|
||||
MixHash: "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
Coinbase: "0x0000000000000000000000000000000000000000",
|
||||
Alloc: alloc,
|
||||
Number: "0x0",
|
||||
GasUsed: "0x0",
|
||||
ParentHash: "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
}
|
||||
|
||||
data, err := json.Marshal(cchain)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(data), nil
|
||||
}
|
||||
|
||||
// LoadKeyFromEnv loads a single key from environment variables
|
||||
// Env vars: PRIVATE_KEY (hex), NODE_ID
|
||||
func LoadKeyFromEnv() (*KeyInfo, error) {
|
||||
privKeyHex := os.Getenv("PRIVATE_KEY")
|
||||
if privKeyHex == "" {
|
||||
return nil, fmt.Errorf("PRIVATE_KEY not set")
|
||||
}
|
||||
|
||||
privKeyHex = strings.TrimPrefix(privKeyHex, "0x")
|
||||
privKeyBytes, err := hex.DecodeString(privKeyHex)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid private key hex: %w", err)
|
||||
}
|
||||
|
||||
return keyInfoFromPrivateKey(privKeyBytes)
|
||||
}
|
||||
|
||||
// LoadKeysFromMnemonic derives multiple keys from a BIP39 mnemonic
|
||||
// Uses BIP44 path: m/44'/9000'/0'/0/{account} (Lux P/X-Chain coin type)
|
||||
func LoadKeysFromMnemonic(mnemonic string, numAccounts int) ([]KeyInfo, error) {
|
||||
if !bip39.IsMnemonicValid(mnemonic) {
|
||||
return nil, fmt.Errorf("invalid mnemonic")
|
||||
}
|
||||
|
||||
seed := bip39.NewSeed(mnemonic, "")
|
||||
masterKey, err := bip32.NewMasterKey(seed)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create master key: %w", err)
|
||||
}
|
||||
|
||||
// BIP44 path for Lux P/X-Chain: m/44'/9000'/0'/0/{account}
|
||||
// 44' = purpose (BIP44)
|
||||
// 9000' = Lux coin type (P/X-Chain)
|
||||
// 0' = account
|
||||
// 0 = external chain
|
||||
purpose, err := masterKey.NewChildKey(bip32.FirstHardenedChild + 44)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to derive purpose: %w", err)
|
||||
}
|
||||
|
||||
coinType, err := purpose.NewChildKey(bip32.FirstHardenedChild + 9000)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to derive coin type: %w", err)
|
||||
}
|
||||
|
||||
account, err := coinType.NewChildKey(bip32.FirstHardenedChild + 0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to derive account: %w", err)
|
||||
}
|
||||
|
||||
change, err := account.NewChildKey(0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to derive change: %w", err)
|
||||
}
|
||||
|
||||
// Also derive ETH keys (coin type 60) for C-Chain addresses.
|
||||
// P/X-Chain uses coin type 9000, C-Chain uses standard ETH derivation.
|
||||
ethCoinType, err := purpose.NewChildKey(bip32.FirstHardenedChild + 60)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to derive ETH coin type: %w", err)
|
||||
}
|
||||
ethAccount, err := ethCoinType.NewChildKey(bip32.FirstHardenedChild + 0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to derive ETH account: %w", err)
|
||||
}
|
||||
ethChange, err := ethAccount.NewChildKey(0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to derive ETH change: %w", err)
|
||||
}
|
||||
|
||||
keys := make([]KeyInfo, 0, numAccounts)
|
||||
for i := 0; i < numAccounts; i++ {
|
||||
// Lux key (coin type 9000) → P/X-Chain address + NodeID
|
||||
childKey, err := change.NewChildKey(uint32(i))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to derive key %d: %w", i, err)
|
||||
}
|
||||
|
||||
keyInfo, err := keyInfoFromPrivateKey(childKey.Key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create key info %d: %w", i, err)
|
||||
}
|
||||
|
||||
// ETH key (coin type 60) → C-Chain address
|
||||
// This ensures C-Chain address matches standard ETH wallets (MetaMask, etc.)
|
||||
ethChildKey, err := ethChange.NewChildKey(uint32(i))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to derive ETH key %d: %w", i, err)
|
||||
}
|
||||
ethPrivKey, err := ethcrypto.ToECDSA(ethChildKey.Key)
|
||||
if err == nil {
|
||||
ethAddr := ethcrypto.PubkeyToAddress(ethPrivKey.PublicKey)
|
||||
copy(keyInfo.ETHAddr[:], ethAddr[:])
|
||||
}
|
||||
|
||||
keys = append(keys, *keyInfo)
|
||||
}
|
||||
|
||||
return keys, nil
|
||||
}
|
||||
|
||||
// LoadKeysFromMnemonicEnv loads keys from mnemonic env vars.
|
||||
// Priority: MNEMONIC > LUX_MNEMONIC > LIGHT_MNEMONIC
|
||||
func LoadKeysFromMnemonicEnv(numAccounts int) ([]KeyInfo, error) {
|
||||
mnemonic := getMnemonicEnv()
|
||||
if mnemonic == "" {
|
||||
return nil, fmt.Errorf("mnemonic not set (set MNEMONIC, LUX_MNEMONIC, or LIGHT_MNEMONIC)")
|
||||
}
|
||||
|
||||
return LoadKeysFromMnemonic(mnemonic, numAccounts)
|
||||
}
|
||||
|
||||
// keyInfoFromPrivateKey creates KeyInfo from raw private key bytes
|
||||
func keyInfoFromPrivateKey(privKey []byte) (*KeyInfo, error) {
|
||||
// Derive ETH address from private key
|
||||
ethAddr, err := privateKeyToETHAddress(privKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to derive ETH address: %w", err)
|
||||
}
|
||||
|
||||
// Generate a deterministic node ID from the private key
|
||||
nodeIDBytes := keccak256(append([]byte("node-id:"), privKey...))
|
||||
nodeID, err := ids.ToNodeID(nodeIDBytes[:20])
|
||||
if err != nil {
|
||||
// Fallback to generating from hash
|
||||
var nid ids.NodeID
|
||||
copy(nid[:], nodeIDBytes[:20])
|
||||
nodeID = nid
|
||||
}
|
||||
|
||||
// Derive Lux P/X-Chain address (SHA256+RIPEMD160, like Bitcoin)
|
||||
luxKey, err := luxcrypto.ToPrivateKey(privKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to derive lux key: %w", err)
|
||||
}
|
||||
stakingAddr := luxKey.Address()
|
||||
|
||||
return &KeyInfo{
|
||||
NodeID: nodeID,
|
||||
StakerKey: privKey,
|
||||
StakingAddr: stakingAddr,
|
||||
ETHAddr: ethAddr,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// privateKeyToETHAddress derives an Ethereum address from a private key
|
||||
func privateKeyToETHAddress(privKey []byte) (ids.ShortID, error) {
|
||||
key, err := ethcrypto.ToECDSA(privKey)
|
||||
if err != nil {
|
||||
return ids.ShortID{}, fmt.Errorf("invalid secp256k1 private key: %w", err)
|
||||
}
|
||||
ethAddr := ethcrypto.PubkeyToAddress(key.PublicKey)
|
||||
var addr ids.ShortID
|
||||
copy(addr[:], ethAddr[:])
|
||||
return addr, nil
|
||||
}
|
||||
|
||||
// keccak256 computes the Keccak-256 hash
|
||||
func keccak256(data []byte) []byte {
|
||||
h := sha3.NewLegacyKeccak256()
|
||||
h.Write(data)
|
||||
return h.Sum(nil)
|
||||
}
|
||||
|
||||
// genesisMessage returns the Latin genesis message for a network.
|
||||
func genesisMessage(networkID uint32) string {
|
||||
switch networkID {
|
||||
case constants.MainnetID:
|
||||
return "Lux et Libertas"
|
||||
case constants.TestnetID:
|
||||
return "Lux et Veritas"
|
||||
case constants.DevnetID:
|
||||
return "Lux ex Tenebris"
|
||||
default:
|
||||
return "Fiat Lux"
|
||||
}
|
||||
}
|
||||
|
||||
// getMnemonicEnv returns the mnemonic from environment variables.
|
||||
// Priority: MNEMONIC > LUX_MNEMONIC > LIGHT_MNEMONIC
|
||||
func getMnemonicEnv() string {
|
||||
for _, env := range []string{"MNEMONIC", "LUX_MNEMONIC", "LIGHT_MNEMONIC"} {
|
||||
if v := os.Getenv(env); v != "" {
|
||||
return v
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// BuildConfigFromEnv builds genesis config from environment variables
|
||||
// Checks in order: KEYS_DIR, mnemonic (MNEMONIC/LUX_MNEMONIC/LIGHT_MNEMONIC), PRIVATE_KEY
|
||||
//
|
||||
// Architecture:
|
||||
// - X-Chain: 100 accounts × 500M LUX each, FREE
|
||||
// - P-Chain: 100 accounts × 500M LUX each, vesting 1%/year from 2020-01-01
|
||||
// - C-Chain: treasury 0x9011...4714 gets 2T LUX
|
||||
func BuildConfigFromEnv(networkID uint32, numValidators int, allocationPerKey uint64) (*Config, error) {
|
||||
var err error
|
||||
|
||||
if allocationPerKey == 0 {
|
||||
allocationPerKey = DefaultAllocationPerAccount
|
||||
}
|
||||
|
||||
// Load validator keys from directory (if available)
|
||||
var validatorKeys []KeyInfo
|
||||
if keysDir := os.Getenv("KEYS_DIR"); keysDir != "" {
|
||||
validatorKeys, err = LoadKeysFromDir(keysDir)
|
||||
if err != nil {
|
||||
validatorKeys = nil
|
||||
}
|
||||
}
|
||||
|
||||
// Load account keys from mnemonic (100 X-Chain accounts for deposits)
|
||||
var allKeys []KeyInfo
|
||||
if mnemonic := getMnemonicEnv(); mnemonic != "" {
|
||||
numAccounts := DefaultNumAccounts
|
||||
allKeys, err = LoadKeysFromMnemonic(mnemonic, numAccounts)
|
||||
if err != nil {
|
||||
allKeys = nil
|
||||
}
|
||||
}
|
||||
|
||||
// Combine: use dir keys for validators, mnemonic keys for X-Chain accounts
|
||||
if len(validatorKeys) > 0 && len(allKeys) > 0 {
|
||||
return buildConfigFromKeyInfos(networkID, validatorKeys, allKeys, allocationPerKey)
|
||||
}
|
||||
if len(validatorKeys) > 0 {
|
||||
return BuildConfigFromKeys(networkID, os.Getenv("KEYS_DIR"), allocationPerKey)
|
||||
}
|
||||
if len(allKeys) > 0 {
|
||||
if numValidators == 0 {
|
||||
numValidators = 3
|
||||
}
|
||||
vKeys := allKeys[:numValidators]
|
||||
return buildConfigFromKeyInfos(networkID, vKeys, allKeys, allocationPerKey)
|
||||
}
|
||||
|
||||
// Try single private key
|
||||
if privKey := os.Getenv("PRIVATE_KEY"); privKey != "" {
|
||||
keyInfo, err := LoadKeyFromEnv()
|
||||
if err == nil {
|
||||
return buildConfigFromKeyInfos(networkID, []KeyInfo{*keyInfo}, []KeyInfo{*keyInfo}, allocationPerKey)
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to default keys directory (with fee key derivation)
|
||||
home, _ := os.UserHomeDir()
|
||||
keysDir := filepath.Join(home, ".lux", "keys")
|
||||
return BuildConfigFromKeys(networkID, keysDir, allocationPerKey)
|
||||
}
|
||||
|
||||
// buildConfigFromKeyInfos creates config from KeyInfo slices.
|
||||
//
|
||||
// Architecture:
|
||||
// - X-Chain: 100 accounts × 500M LUX each, FREE (immediately spendable)
|
||||
// - P-Chain: 100 accounts × 500M LUX each, vesting 1%/year from 2020-01-01
|
||||
// - C-Chain: treasury 0x9011...4714 gets 2T LUX
|
||||
// - Total: 100B LUX across 100 accounts (50B X + 50B P) + 2T C-chain treasury
|
||||
//
|
||||
// validatorKeys: first N accounts that become initial stakers
|
||||
// allKeys: all accounts that receive X-Chain allocations (typically 100)
|
||||
func buildConfigFromKeyInfos(networkID uint32, validatorKeys []KeyInfo, allKeys []KeyInfo, allocationPerKey uint64) (*Config, error) {
|
||||
if len(validatorKeys) == 0 {
|
||||
return nil, fmt.Errorf("no keys provided")
|
||||
}
|
||||
|
||||
if allocationPerKey == 0 {
|
||||
allocationPerKey = DefaultAllocationPerAccount
|
||||
}
|
||||
|
||||
// Each account gets ONE allocation entry with:
|
||||
// InitialAmount → X-Chain UTXO (free, immediately spendable)
|
||||
// UnlockSchedule → P-Chain UTXOs (vesting 1%/year from Jan 1 2020)
|
||||
//
|
||||
// The node builder puts InitialAmount on X-Chain and UnlockSchedule on P-Chain.
|
||||
// InitialAmount ALSO creates a P-Chain UTXO (spendable, no locktime) for
|
||||
// non-staked addresses — so account gets 500M free on both X and P, plus
|
||||
// 500M vesting on P. Total per account: 500M X + 1B P.
|
||||
//
|
||||
// To get exactly 500M X + 500M P: set InitialAmount=500M (→ X free + P free)
|
||||
// and UnlockSchedule=500M total vesting (→ P locked). But the P free from
|
||||
// InitialAmount adds 500M extra. So we set InitialAmount=0 for P-only vesting
|
||||
// and use a separate entry for X-only free.
|
||||
//
|
||||
// Actually: simplest correct approach is ONE entry per account:
|
||||
// InitialAmount = 500M → goes to X-Chain (free) AND P-Chain (free, spendable)
|
||||
// UnlockSchedule = 500M → goes to P-Chain (vesting)
|
||||
// Result: X gets 500M free. P gets 500M free + 500M vesting = 1B on P.
|
||||
//
|
||||
// If we want exactly 500M on P (all vesting, no free):
|
||||
// InitialAmount = 500M (X-chain only — but builder also puts it on P!)
|
||||
//
|
||||
// The builder ALWAYS puts InitialAmount on P-Chain too (line 444-456).
|
||||
// There's no way to give X-only via this genesis format.
|
||||
// So: 500M InitialAmount + 500M UnlockSchedule = 500M X + 1B P per account.
|
||||
//
|
||||
// For clean 500M/500M, we'd need InitialAmount=500M and NO UnlockSchedule,
|
||||
// then P-chain gets 500M free (from InitialAmount). X-chain gets 500M free.
|
||||
// That's 500M on each chain, both free. No vesting.
|
||||
//
|
||||
// OR: InitialAmount=0, UnlockSchedule=500M → P-chain gets 500M vesting,
|
||||
// X-chain gets 0. Then separate entry: InitialAmount=500M, no schedule
|
||||
// → X gets 500M free, P gets 500M free. Total: X=500M, P=1B. Still wrong.
|
||||
//
|
||||
// The node builder gives InitialAmount to BOTH chains. We can't avoid it.
|
||||
// Cleanest: one entry, InitialAmount=500M, UnlockSchedule=500M vesting.
|
||||
// Result: X=500M free, P=500M free + 500M vesting = 1B P.
|
||||
// Total per account: 1.5B (500M X + 1B P). Across 100 = 150B.
|
||||
//
|
||||
// This is fine — P-chain needs more funds for staking + subnet operations.
|
||||
pChainUnlockSchedule := buildUnlockSchedule(allocationPerKey, StakingStartTime, UnlockInterval, 100)
|
||||
|
||||
allocations := make([]Allocation, 0, len(allKeys)+len(validatorKeys))
|
||||
stakedFunds := make([]ids.ShortID, 0, len(validatorKeys))
|
||||
stakers := make([]Staker, 0, len(validatorKeys))
|
||||
|
||||
for _, key := range allKeys {
|
||||
// Single entry per account:
|
||||
// X-Chain: 500M free (from InitialAmount)
|
||||
// P-Chain: 500M free (from InitialAmount) + 500M vesting (from UnlockSchedule)
|
||||
allocations = append(allocations, Allocation{
|
||||
ETHAddr: key.ETHAddr,
|
||||
LUXAddr: key.StakingAddr,
|
||||
InitialAmount: allocationPerKey,
|
||||
UnlockSchedule: pChainUnlockSchedule,
|
||||
})
|
||||
}
|
||||
|
||||
for _, key := range validatorKeys {
|
||||
|
||||
stakedFunds = append(stakedFunds, key.StakingAddr)
|
||||
|
||||
staker := Staker{
|
||||
NodeID: key.NodeID,
|
||||
RewardAddress: key.StakingAddr,
|
||||
DelegationFee: 20000,
|
||||
}
|
||||
|
||||
if len(key.BLSPublicKey) > 0 {
|
||||
staker.Signer = &ProofOfPossession{
|
||||
PublicKey: fmt.Sprintf("0x%x", key.BLSPublicKey),
|
||||
ProofOfPossession: fmt.Sprintf("0x%x", key.BLSProofOfPossession),
|
||||
}
|
||||
}
|
||||
|
||||
stakers = append(stakers, staker)
|
||||
}
|
||||
|
||||
// C-Chain: treasury-only allocation (2T LUX)
|
||||
cchainGenesis, err := buildCChainGenesisTreasury(networkID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to build C-chain genesis: %w", err)
|
||||
}
|
||||
|
||||
// Start time 1 hour ago: validators are active (for 1 year),
|
||||
// and vested funds with past locktimes (2020-2025) are spendable.
|
||||
startTime := uint64(time.Now().Unix()) - 3600
|
||||
|
||||
return &Config{
|
||||
NetworkID: networkID,
|
||||
Allocations: allocations,
|
||||
StartTime: startTime,
|
||||
InitialStakeDuration: 365 * 24 * 60 * 60,
|
||||
InitialStakeDurationOffset: 5400,
|
||||
InitialStakedFunds: stakedFunds,
|
||||
InitialStakers: stakers,
|
||||
CChainGenesis: cchainGenesis,
|
||||
Message: genesisMessage(networkID),
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright (C) 2025, Lux Partners Limited All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package genesis
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const genesisNetworkIDKey = "networkID"
|
||||
|
||||
// NetworkIDFromGenesis returns the network ID in the given genesis
|
||||
func NetworkIDFromGenesis(genesis []byte) (uint32, error) {
|
||||
genesisMap := map[string]interface{}{}
|
||||
if err := json.Unmarshal(genesis, &genesisMap); err != nil {
|
||||
return 0, fmt.Errorf("couldn't unmarshal genesis: %w", err)
|
||||
}
|
||||
networkIDIntf, ok := genesisMap[genesisNetworkIDKey]
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("couldn't find key %q in genesis", genesisNetworkIDKey)
|
||||
}
|
||||
networkID, ok := networkIDIntf.(float64)
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("expected float64 but got %T", networkIDIntf)
|
||||
}
|
||||
return uint32(networkID), nil
|
||||
}
|
||||
@@ -0,0 +1,366 @@
|
||||
// Copyright (C) 2019-2025, Lux Partners Limited. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package genesis
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/luxfi/address"
|
||||
"github.com/luxfi/constants"
|
||||
"github.com/luxfi/ids"
|
||||
)
|
||||
|
||||
// Decimal places for different chains:
|
||||
// - P-chain/X-chain: 6 decimals (microLux) - smallest unit is 0.000001 LUX
|
||||
// - C-chain: 18 decimals (wei) - compatible with Ethereum tooling
|
||||
//
|
||||
// Total Supply: 2 trillion LUX (2T)
|
||||
|
||||
// P-chain/X-chain units (6 decimals)
|
||||
const (
|
||||
MicroLux uint64 = 1 // Base unit (6 decimals) - 0.000001 LUX
|
||||
MilliLux uint64 = 1_000 // 0.001 LUX
|
||||
Lux uint64 = 1_000_000 // 1 LUX = 10^6 microLUX
|
||||
KiloLux uint64 = 1_000_000_000 // 1,000 LUX
|
||||
MegaLux uint64 = 1_000_000_000_000 // 1,000,000 LUX (1M)
|
||||
GigaLux uint64 = 1_000_000_000_000_000 // 1 billion LUX (1B)
|
||||
TeraLux uint64 = 1_000_000_000_000_000_000 // 1 trillion LUX (1T)
|
||||
|
||||
// TotalSupply is the maximum supply of LUX (2 trillion)
|
||||
TotalSupply uint64 = 2 * TeraLux // 2T LUX
|
||||
)
|
||||
|
||||
// C-chain decimal conversion factor
|
||||
const (
|
||||
// CChainDecimalShift is 10^12 to convert from microLux (6 dec) to wei (18 dec)
|
||||
CChainDecimalShift = 1_000_000_000_000
|
||||
)
|
||||
|
||||
// Params combines staking and fee parameters for a network
|
||||
type Params struct {
|
||||
TxFee uint64
|
||||
CreateAssetTxFee uint64
|
||||
UptimeRequirement float64
|
||||
MinValidatorStake uint64
|
||||
MaxValidatorStake uint64
|
||||
MinDelegatorStake uint64
|
||||
MinDelegationFee uint32
|
||||
MinStakeDuration uint64
|
||||
MaxStakeDuration uint64
|
||||
RewardConfig RewardConfig
|
||||
}
|
||||
|
||||
// LocalParams contains default parameters for local networks
|
||||
var LocalParams = Params{
|
||||
TxFee: MilliLux,
|
||||
CreateAssetTxFee: 10 * MilliLux,
|
||||
UptimeRequirement: 0.2,
|
||||
MinValidatorStake: 1 * Lux,
|
||||
MaxValidatorStake: 3000000 * Lux,
|
||||
MinDelegatorStake: 1 * Lux,
|
||||
MinDelegationFee: 20000, // 2%
|
||||
MinStakeDuration: 60, // 1 minute
|
||||
MaxStakeDuration: 365 * 24 * 60 * 60,
|
||||
RewardConfig: RewardConfig{
|
||||
MaxConsumptionRate: 120000,
|
||||
MinConsumptionRate: 100000,
|
||||
MintingPeriod: 365 * 24 * 60 * 60,
|
||||
SupplyCap: 2 * TeraLux, // 2 trillion LUX max supply
|
||||
},
|
||||
}
|
||||
|
||||
// MainnetParams contains default parameters for mainnet
|
||||
var MainnetParams = Params{
|
||||
TxFee: MilliLux,
|
||||
CreateAssetTxFee: 10 * MilliLux,
|
||||
UptimeRequirement: 0.8,
|
||||
MinValidatorStake: 2000 * Lux,
|
||||
MaxValidatorStake: 5 * GigaLux,
|
||||
MinDelegatorStake: 25 * Lux,
|
||||
MinDelegationFee: 20000,
|
||||
MinStakeDuration: 2 * 7 * 24 * 60 * 60, // 2 weeks
|
||||
MaxStakeDuration: 365 * 24 * 60 * 60,
|
||||
RewardConfig: RewardConfig{
|
||||
MaxConsumptionRate: 120000,
|
||||
MinConsumptionRate: 100000,
|
||||
MintingPeriod: 365 * 24 * 60 * 60,
|
||||
SupplyCap: 2 * TeraLux, // 2 trillion LUX max supply
|
||||
},
|
||||
}
|
||||
|
||||
// TestnetParams contains default parameters for testnet
|
||||
var TestnetParams = Params{
|
||||
TxFee: MilliLux,
|
||||
CreateAssetTxFee: 10 * MilliLux,
|
||||
UptimeRequirement: 0.8,
|
||||
MinValidatorStake: 1 * Lux,
|
||||
MaxValidatorStake: 3000000 * Lux,
|
||||
MinDelegatorStake: 1 * Lux,
|
||||
MinDelegationFee: 20000,
|
||||
MinStakeDuration: 24 * 60 * 60, // 1 day
|
||||
MaxStakeDuration: 365 * 24 * 60 * 60,
|
||||
RewardConfig: RewardConfig{
|
||||
MaxConsumptionRate: 120000,
|
||||
MinConsumptionRate: 100000,
|
||||
MintingPeriod: 365 * 24 * 60 * 60,
|
||||
SupplyCap: 2 * TeraLux, // 2 trillion LUX max supply
|
||||
},
|
||||
}
|
||||
|
||||
// GetParams returns network parameters for a network ID
|
||||
func GetParams(networkID uint32) Params {
|
||||
switch networkID {
|
||||
case constants.MainnetID:
|
||||
return MainnetParams
|
||||
case constants.TestnetID:
|
||||
return TestnetParams
|
||||
default:
|
||||
return LocalParams
|
||||
}
|
||||
}
|
||||
|
||||
// Default staking configs per network
|
||||
var (
|
||||
MainnetStakingConfig = StakingConfig{
|
||||
UptimeRequirement: 0.8,
|
||||
MinValidatorStake: 2000 * Lux,
|
||||
MaxValidatorStake: 5 * GigaLux,
|
||||
MinDelegatorStake: 25 * Lux,
|
||||
MinDelegationFee: 20000, // 2%
|
||||
MinStakeDuration: 2 * 7 * 24 * 60 * 60, // 2 weeks
|
||||
MaxStakeDuration: 365 * 24 * 60 * 60, // 1 year
|
||||
RewardConfig: RewardConfig{
|
||||
MaxConsumptionRate: 120000,
|
||||
MinConsumptionRate: 100000,
|
||||
MintingPeriod: 365 * 24 * 60 * 60,
|
||||
SupplyCap: 2 * TeraLux, // 2 trillion LUX max supply
|
||||
},
|
||||
}
|
||||
|
||||
TestnetStakingConfig = StakingConfig{
|
||||
UptimeRequirement: 0.8,
|
||||
MinValidatorStake: 1 * Lux,
|
||||
MaxValidatorStake: 3000000 * Lux,
|
||||
MinDelegatorStake: 1 * Lux,
|
||||
MinDelegationFee: 20000,
|
||||
MinStakeDuration: 24 * 60 * 60, // 1 day
|
||||
MaxStakeDuration: 365 * 24 * 60 * 60, // 1 year
|
||||
RewardConfig: RewardConfig{
|
||||
MaxConsumptionRate: 120000,
|
||||
MinConsumptionRate: 100000,
|
||||
MintingPeriod: 365 * 24 * 60 * 60,
|
||||
SupplyCap: 2 * TeraLux, // 2 trillion LUX max supply
|
||||
},
|
||||
}
|
||||
|
||||
LocalStakingConfig = StakingConfig{
|
||||
UptimeRequirement: 0.2,
|
||||
MinValidatorStake: 1 * Lux,
|
||||
MaxValidatorStake: 3000000 * Lux,
|
||||
MinDelegatorStake: 1 * Lux,
|
||||
MinDelegationFee: 20000,
|
||||
MinStakeDuration: 60, // 1 minute
|
||||
MaxStakeDuration: 365 * 24 * 60 * 60, // 1 year
|
||||
RewardConfig: RewardConfig{
|
||||
MaxConsumptionRate: 120000,
|
||||
MinConsumptionRate: 100000,
|
||||
MintingPeriod: 365 * 24 * 60 * 60,
|
||||
SupplyCap: 2 * TeraLux, // 2 trillion LUX max supply
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// Default tx fee configs per network
|
||||
var (
|
||||
MainnetTxFeeConfig = TxFeeConfig{
|
||||
TxFee: MilliLux,
|
||||
CreateAssetTxFee: 10 * MilliLux,
|
||||
}
|
||||
|
||||
TestnetTxFeeConfig = TxFeeConfig{
|
||||
TxFee: MilliLux,
|
||||
CreateAssetTxFee: 10 * MilliLux,
|
||||
}
|
||||
|
||||
LocalTxFeeConfig = TxFeeConfig{
|
||||
TxFee: MilliLux,
|
||||
CreateAssetTxFee: 10 * MilliLux,
|
||||
}
|
||||
)
|
||||
|
||||
// GetStakingConfig returns staking config for a network
|
||||
func GetStakingConfig(networkID uint32) StakingConfig {
|
||||
switch networkID {
|
||||
case constants.MainnetID:
|
||||
return MainnetStakingConfig
|
||||
case constants.TestnetID:
|
||||
return TestnetStakingConfig
|
||||
default:
|
||||
return LocalStakingConfig
|
||||
}
|
||||
}
|
||||
|
||||
// GetTxFeeConfig returns tx fee config for a network
|
||||
func GetTxFeeConfig(networkID uint32) TxFeeConfig {
|
||||
switch networkID {
|
||||
case constants.MainnetID:
|
||||
return MainnetTxFeeConfig
|
||||
case constants.TestnetID:
|
||||
return TestnetTxFeeConfig
|
||||
default:
|
||||
return LocalTxFeeConfig
|
||||
}
|
||||
}
|
||||
|
||||
// GetBootstrappers loads bootstrappers dynamically from config or keys
|
||||
func GetBootstrappers(networkID uint32) []Bootstrapper {
|
||||
// Try to load from bootstrappers.json in genesis directory
|
||||
home, _ := os.UserHomeDir()
|
||||
|
||||
var networkName string
|
||||
switch networkID {
|
||||
case constants.MainnetID:
|
||||
networkName = "mainnet"
|
||||
case constants.TestnetID:
|
||||
networkName = "testnet"
|
||||
case constants.DevnetID:
|
||||
networkName = "devnet"
|
||||
default:
|
||||
// For custom/local networks, check environment variable for bootstrappers path
|
||||
if envPath := os.Getenv("LUX_BOOTSTRAPPERS_FILE"); envPath != "" {
|
||||
data, err := os.ReadFile(envPath)
|
||||
if err == nil {
|
||||
var bootstrappers []Bootstrapper
|
||||
if err := json.Unmarshal(data, &bootstrappers); err == nil {
|
||||
return bootstrappers
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil // Local networks don't need bootstrappers by default
|
||||
}
|
||||
|
||||
// Check standard locations for bootstrappers.json
|
||||
candidates := []string{
|
||||
filepath.Join(home, "work/lux/genesis/configs", networkName, "bootstrappers.json"),
|
||||
filepath.Join(home, ".lux/genesis/configs", networkName, "bootstrappers.json"),
|
||||
filepath.Join("/etc/lux/genesis/configs", networkName, "bootstrappers.json"),
|
||||
}
|
||||
|
||||
for _, path := range candidates {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
var bootstrappers []Bootstrapper
|
||||
if err := json.Unmarshal(data, &bootstrappers); err != nil {
|
||||
continue
|
||||
}
|
||||
return bootstrappers
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetBootstrappersFromKeys loads bootstrapper info from node keys directory
|
||||
func GetBootstrappersFromKeys(keysDir string) ([]Bootstrapper, error) {
|
||||
if keysDir == "" {
|
||||
home, _ := os.UserHomeDir()
|
||||
keysDir = filepath.Join(home, ".lux/keys")
|
||||
}
|
||||
|
||||
entries, err := os.ReadDir(keysDir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read keys directory: %w", err)
|
||||
}
|
||||
|
||||
var bootstrappers []Bootstrapper
|
||||
for _, entry := range entries {
|
||||
if !entry.IsDir() {
|
||||
continue
|
||||
}
|
||||
nodeKeyPath := filepath.Join(keysDir, entry.Name(), "staker.key")
|
||||
if _, err := os.Stat(nodeKeyPath); err != nil {
|
||||
continue
|
||||
}
|
||||
// Node key exists, try to get node ID
|
||||
// This would need crypto package to properly derive node ID from key
|
||||
bootstrappers = append(bootstrappers, Bootstrapper{
|
||||
ID: entry.Name(), // Placeholder - should derive from key
|
||||
IP: "", // Would need to be configured separately
|
||||
})
|
||||
}
|
||||
|
||||
return bootstrappers, nil
|
||||
}
|
||||
|
||||
// ParseAddress parses a bech32 address string to ShortID
|
||||
// Supports formats: P-lux1xxx, X-lux1xxx, lux1xxx, local1xxx
|
||||
func ParseAddress(addrStr string) (ids.ShortID, error) {
|
||||
if addrStr == "" {
|
||||
return ids.ShortID{}, fmt.Errorf("empty address")
|
||||
}
|
||||
|
||||
// Try full format first (P-lux1xxx, X-lux1xxx)
|
||||
if strings.Contains(addrStr, "-") {
|
||||
_, _, addrBytes, err := address.Parse(addrStr)
|
||||
if err != nil {
|
||||
return ids.ShortID{}, fmt.Errorf("failed to parse address %s: %w", addrStr, err)
|
||||
}
|
||||
var addr ids.ShortID
|
||||
copy(addr[:], addrBytes)
|
||||
return addr, nil
|
||||
}
|
||||
|
||||
// Try raw bech32 format (lux1xxx, local1xxx)
|
||||
_, addrBytes, err := address.ParseBech32(addrStr)
|
||||
if err != nil {
|
||||
return ids.ShortID{}, fmt.Errorf("failed to parse bech32 address %s: %w", addrStr, err)
|
||||
}
|
||||
var addr ids.ShortID
|
||||
copy(addr[:], addrBytes)
|
||||
return addr, nil
|
||||
}
|
||||
|
||||
// ParseETHAddress parses an Ethereum hex address to ShortID
|
||||
func ParseETHAddress(addrStr string) (ids.ShortID, error) {
|
||||
if addrStr == "" {
|
||||
return ids.ShortID{}, fmt.Errorf("empty address")
|
||||
}
|
||||
|
||||
// Remove 0x prefix
|
||||
addrStr = strings.TrimPrefix(addrStr, "0x")
|
||||
|
||||
// Decode hex
|
||||
bytes, err := hex.DecodeString(addrStr)
|
||||
if err != nil {
|
||||
return ids.ShortID{}, fmt.Errorf("invalid hex: %w", err)
|
||||
}
|
||||
|
||||
if len(bytes) != 20 {
|
||||
return ids.ShortID{}, fmt.Errorf("invalid address length: %d", len(bytes))
|
||||
}
|
||||
|
||||
var addr ids.ShortID
|
||||
copy(addr[:], bytes)
|
||||
return addr, nil
|
||||
}
|
||||
|
||||
// ParseNodeID parses a node ID string
|
||||
func ParseNodeID(nodeIDStr string) (ids.NodeID, error) {
|
||||
return ids.NodeIDFromString(nodeIDStr)
|
||||
}
|
||||
|
||||
// FormatAddress formats a ShortID as bech32 address
|
||||
func FormatAddress(hrp string, addr ids.ShortID) string {
|
||||
addrStr, err := address.FormatBech32(hrp, addr[:])
|
||||
if err != nil {
|
||||
return fmt.Sprintf("%s1%s", hrp, addr.String())
|
||||
}
|
||||
return addrStr
|
||||
}
|
||||
@@ -0,0 +1,308 @@
|
||||
// Copyright (C) 2019-2025, Lux Partners Limited. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package genesis
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/luxfi/address"
|
||||
"github.com/luxfi/constants"
|
||||
"github.com/luxfi/ids"
|
||||
)
|
||||
|
||||
// Config is the top-level genesis configuration
|
||||
type Config struct {
|
||||
NetworkID uint32 `json:"networkID"`
|
||||
Allocations []Allocation `json:"allocations"`
|
||||
StartTime uint64 `json:"startTime"`
|
||||
InitialStakeDuration uint64 `json:"initialStakeDuration"`
|
||||
InitialStakeDurationOffset uint64 `json:"initialStakeDurationOffset"`
|
||||
InitialStakedFunds []ids.ShortID `json:"initialStakedFunds"`
|
||||
InitialStakers []Staker `json:"initialStakers"`
|
||||
CChainGenesis string `json:"cChainGenesis"`
|
||||
DChainGenesis string `json:"dChainGenesis,omitempty"` // D-Chain: DEX VM genesis
|
||||
QChainGenesis string `json:"qChainGenesis,omitempty"` // Q-Chain: Quantum VM genesis
|
||||
AChainGenesis string `json:"aChainGenesis,omitempty"` // A-Chain: Attestation/AI VM genesis
|
||||
BChainGenesis string `json:"bChainGenesis,omitempty"` // B-Chain: Bridge VM genesis
|
||||
TChainGenesis string `json:"tChainGenesis,omitempty"` // T-Chain: Threshold VM genesis
|
||||
ZChainGenesis string `json:"zChainGenesis,omitempty"` // Z-Chain: ZK VM genesis
|
||||
GChainGenesis string `json:"gChainGenesis,omitempty"` // G-Chain: Graph VM genesis
|
||||
KChainGenesis string `json:"kChainGenesis,omitempty"` // K-Chain: KMS VM genesis
|
||||
Message string `json:"message"`
|
||||
|
||||
// ChainMapping provides dynamic chain ID configuration per network.
|
||||
// This allows chains (P/X/C/Q/etc.) to have network-specific IDs that
|
||||
// can be migrated/upgraded over time via governance.
|
||||
ChainMapping *ChainMapping `json:"chainMapping,omitempty"`
|
||||
}
|
||||
|
||||
// Allocation represents a genesis allocation
|
||||
type Allocation struct {
|
||||
ETHAddr ids.ShortID `json:"ethAddr"`
|
||||
LUXAddr ids.ShortID `json:"luxAddr"`
|
||||
InitialAmount uint64 `json:"initialAmount"`
|
||||
UnlockSchedule []LockedAmount `json:"unlockSchedule"`
|
||||
}
|
||||
|
||||
// LockedAmount represents a time-locked amount
|
||||
type LockedAmount struct {
|
||||
Amount uint64 `json:"amount"`
|
||||
Locktime uint64 `json:"locktime"`
|
||||
}
|
||||
|
||||
// Staker represents an initial validator
|
||||
type Staker struct {
|
||||
NodeID ids.NodeID `json:"nodeID"`
|
||||
RewardAddress ids.ShortID `json:"rewardAddress"`
|
||||
DelegationFee uint32 `json:"delegationFee"`
|
||||
Signer *ProofOfPossession `json:"signer,omitempty"`
|
||||
// Weight is the explicit validator stake weight (optional, derived from allocations if not set)
|
||||
Weight uint64 `json:"weight,omitempty"`
|
||||
// StartTime is the Unix timestamp when staking begins (optional)
|
||||
StartTime uint64 `json:"startTime,omitempty"`
|
||||
// EndTime is the Unix timestamp when staking ends (optional)
|
||||
EndTime uint64 `json:"endTime,omitempty"`
|
||||
}
|
||||
|
||||
// ProofOfPossession contains BLS signature data
|
||||
type ProofOfPossession struct {
|
||||
PublicKey string `json:"publicKey"`
|
||||
ProofOfPossession string `json:"proofOfPossession"`
|
||||
}
|
||||
|
||||
// Bootstrapper represents a bootstrap node
|
||||
type Bootstrapper struct {
|
||||
ID string `json:"id"`
|
||||
IP string `json:"ip"`
|
||||
}
|
||||
|
||||
// StakingConfig contains staking parameters
|
||||
type StakingConfig struct {
|
||||
UptimeRequirement float64 `json:"uptimeRequirement"`
|
||||
MinValidatorStake uint64 `json:"minValidatorStake"`
|
||||
MaxValidatorStake uint64 `json:"maxValidatorStake"`
|
||||
MinDelegatorStake uint64 `json:"minDelegatorStake"`
|
||||
MinDelegationFee uint32 `json:"minDelegationFee"`
|
||||
MinStakeDuration uint64 `json:"minStakeDuration"` // seconds
|
||||
MaxStakeDuration uint64 `json:"maxStakeDuration"` // seconds
|
||||
RewardConfig RewardConfig `json:"rewardConfig"`
|
||||
}
|
||||
|
||||
// RewardConfig contains reward parameters
|
||||
type RewardConfig struct {
|
||||
MaxConsumptionRate uint64 `json:"maxConsumptionRate"`
|
||||
MinConsumptionRate uint64 `json:"minConsumptionRate"`
|
||||
MintingPeriod uint64 `json:"mintingPeriod"` // seconds
|
||||
SupplyCap uint64 `json:"supplyCap"`
|
||||
}
|
||||
|
||||
// TxFeeConfig contains transaction fee parameters
|
||||
type TxFeeConfig struct {
|
||||
TxFee uint64 `json:"txFee"`
|
||||
CreateAssetTxFee uint64 `json:"createAssetTxFee"`
|
||||
}
|
||||
|
||||
// NetworkConfig contains network-level configuration (network.json)
|
||||
type NetworkConfig struct {
|
||||
NetworkID uint32 `json:"networkID"`
|
||||
StartTime uint64 `json:"startTime"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// PChainConfig contains P-Chain specific configuration (pchain.json)
|
||||
type PChainConfig struct {
|
||||
Allocations []AllocationJSON `json:"allocations"`
|
||||
InitialStakeDuration uint64 `json:"initialStakeDuration"`
|
||||
InitialStakeDurationOffset uint64 `json:"initialStakeDurationOffset"`
|
||||
InitialStakedFunds []string `json:"initialStakedFunds"`
|
||||
InitialStakers []StakerJSON `json:"initialStakers"`
|
||||
}
|
||||
|
||||
// AllocationJSON is the JSON representation of an allocation
|
||||
type AllocationJSON struct {
|
||||
ETHAddr string `json:"ethAddr"`
|
||||
LUXAddr string `json:"luxAddr"`
|
||||
InitialAmount uint64 `json:"initialAmount"`
|
||||
UnlockSchedule []LockedAmount `json:"unlockSchedule"`
|
||||
}
|
||||
|
||||
// UnparsedAllocation is an alias for AllocationJSON (for backward compatibility)
|
||||
type UnparsedAllocation = AllocationJSON
|
||||
|
||||
// StakerJSON is the JSON representation of a staker
|
||||
type StakerJSON struct {
|
||||
NodeID string `json:"nodeID"`
|
||||
RewardAddress string `json:"rewardAddress"`
|
||||
DelegationFee uint32 `json:"delegationFee"`
|
||||
Signer *ProofOfPossession `json:"signer,omitempty"`
|
||||
// Weight is the explicit validator stake weight (optional)
|
||||
Weight uint64 `json:"weight,omitempty"`
|
||||
// StartTime is the Unix timestamp when staking begins (optional)
|
||||
StartTime uint64 `json:"startTime,omitempty"`
|
||||
// EndTime is the Unix timestamp when staking ends (optional)
|
||||
EndTime uint64 `json:"endTime,omitempty"`
|
||||
}
|
||||
|
||||
// UnparsedStaker is an alias for StakerJSON (for backward compatibility)
|
||||
type UnparsedStaker = StakerJSON
|
||||
|
||||
// CChainConfig is the C-Chain genesis configuration
|
||||
type CChainConfig struct {
|
||||
Config CChainParams `json:"config"`
|
||||
Nonce string `json:"nonce"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
ExtraData string `json:"extraData"`
|
||||
GasLimit string `json:"gasLimit"`
|
||||
Difficulty string `json:"difficulty"`
|
||||
MixHash string `json:"mixHash"`
|
||||
Coinbase string `json:"coinbase"`
|
||||
Alloc map[string]Balance `json:"alloc"`
|
||||
Number string `json:"number"`
|
||||
GasUsed string `json:"gasUsed"`
|
||||
ParentHash string `json:"parentHash"`
|
||||
}
|
||||
|
||||
// CChainParams contains C-Chain config parameters
|
||||
type CChainParams struct {
|
||||
ChainID uint64 `json:"chainId"`
|
||||
HomesteadBlock uint64 `json:"homesteadBlock"`
|
||||
EIP150Block uint64 `json:"eip150Block"`
|
||||
EIP155Block uint64 `json:"eip155Block"`
|
||||
EIP158Block uint64 `json:"eip158Block"`
|
||||
ByzantiumBlock uint64 `json:"byzantiumBlock"`
|
||||
ConstantinopleBlock uint64 `json:"constantinopleBlock"`
|
||||
PetersburgBlock uint64 `json:"petersburgBlock"`
|
||||
IstanbulBlock uint64 `json:"istanbulBlock"`
|
||||
MuirGlacierBlock uint64 `json:"muirGlacierBlock"`
|
||||
BerlinBlock uint64 `json:"berlinBlock"`
|
||||
LondonBlock uint64 `json:"londonBlock"`
|
||||
ArrowGlacierBlock uint64 `json:"arrowGlacierBlock,omitempty"`
|
||||
GrayGlacierBlock uint64 `json:"grayGlacierBlock,omitempty"`
|
||||
MergeNetsplitBlock uint64 `json:"mergeNetsplitBlock,omitempty"`
|
||||
ShanghaiTime uint64 `json:"shanghaiTime,omitempty"`
|
||||
CancunTime uint64 `json:"cancunTime,omitempty"`
|
||||
TerminalTotalDifficulty uint64 `json:"terminalTotalDifficulty,omitempty"`
|
||||
SubnetEVMTimestamp uint64 `json:"subnetEVMTimestamp,omitempty"`
|
||||
DurangoTimestamp uint64 `json:"durangoTimestamp,omitempty"`
|
||||
EtnaTimestamp uint64 `json:"etnaTimestamp,omitempty"`
|
||||
FeeConfig FeeConfig `json:"feeConfig"`
|
||||
WarpConfig *WarpConfig `json:"warpConfig,omitempty"`
|
||||
}
|
||||
|
||||
// FeeConfig contains EVM fee parameters
|
||||
type FeeConfig struct {
|
||||
GasLimit uint64 `json:"gasLimit"`
|
||||
TargetBlockRate uint64 `json:"targetBlockRate"`
|
||||
MinBaseFee uint64 `json:"minBaseFee"`
|
||||
TargetGas uint64 `json:"targetGas"`
|
||||
BaseFeeChangeDenominator uint64 `json:"baseFeeChangeDenominator"`
|
||||
MinBlockGasCost uint64 `json:"minBlockGasCost"`
|
||||
MaxBlockGasCost uint64 `json:"maxBlockGasCost"`
|
||||
BlockGasCostStep uint64 `json:"blockGasCostStep"`
|
||||
}
|
||||
|
||||
// WarpConfig contains warp messaging configuration
|
||||
type WarpConfig struct {
|
||||
BlockTimestamp uint64 `json:"blockTimestamp"`
|
||||
QuorumNumerator uint64 `json:"quorumNumerator"`
|
||||
RequirePrimaryNetworkSigners bool `json:"requirePrimaryNetworkSigners"`
|
||||
}
|
||||
|
||||
// Balance represents an EVM account balance
|
||||
type Balance struct {
|
||||
Balance string `json:"balance"`
|
||||
}
|
||||
|
||||
// ConfigOutput is the JSON output format for genesis configuration
|
||||
// This matches what luxd expects for primary network genesis
|
||||
type ConfigOutput struct {
|
||||
NetworkID uint32 `json:"networkID"`
|
||||
Allocations []AllocationJSON `json:"allocations"`
|
||||
StartTime uint64 `json:"startTime"`
|
||||
InitialStakeDuration uint64 `json:"initialStakeDuration"`
|
||||
InitialStakeDurationOffset uint64 `json:"initialStakeDurationOffset"`
|
||||
InitialStakedFunds []string `json:"initialStakedFunds"`
|
||||
InitialStakers []StakerJSON `json:"initialStakers"`
|
||||
CChainGenesis string `json:"cChainGenesis"`
|
||||
DChainGenesis string `json:"dChainGenesis,omitempty"` // D-Chain: DEX VM genesis
|
||||
QChainGenesis string `json:"qChainGenesis,omitempty"` // Q-Chain: Quantum VM genesis
|
||||
AChainGenesis string `json:"aChainGenesis,omitempty"` // A-Chain: Attestation/AI VM genesis
|
||||
BChainGenesis string `json:"bChainGenesis,omitempty"` // B-Chain: Bridge VM genesis
|
||||
TChainGenesis string `json:"tChainGenesis,omitempty"` // T-Chain: Threshold VM genesis
|
||||
ZChainGenesis string `json:"zChainGenesis,omitempty"` // Z-Chain: ZK VM genesis
|
||||
GChainGenesis string `json:"gChainGenesis,omitempty"` // G-Chain: Graph VM genesis
|
||||
KChainGenesis string `json:"kChainGenesis,omitempty"` // K-Chain: KMS VM genesis
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// UnparsedConfig is an alias for ConfigOutput (for backward compatibility)
|
||||
type UnparsedConfig = ConfigOutput
|
||||
|
||||
// ToJSON converts Config to ConfigOutput for JSON marshaling
|
||||
func (c *Config) ToJSON(hrp string) *ConfigOutput {
|
||||
if hrp == "" {
|
||||
hrp = "lux"
|
||||
}
|
||||
|
||||
allocations := make([]AllocationJSON, 0, len(c.Allocations))
|
||||
for _, a := range c.Allocations {
|
||||
allocations = append(allocations, AllocationJSON{
|
||||
ETHAddr: fmt.Sprintf("0x%s", a.ETHAddr.Hex()),
|
||||
LUXAddr: formatBech32WithChain("P", hrp, a.LUXAddr),
|
||||
InitialAmount: a.InitialAmount,
|
||||
UnlockSchedule: a.UnlockSchedule,
|
||||
})
|
||||
}
|
||||
|
||||
stakedFunds := make([]string, 0, len(c.InitialStakedFunds))
|
||||
for _, addr := range c.InitialStakedFunds {
|
||||
stakedFunds = append(stakedFunds, formatBech32WithChain("P", hrp, addr))
|
||||
}
|
||||
|
||||
stakers := make([]StakerJSON, 0, len(c.InitialStakers))
|
||||
for _, s := range c.InitialStakers {
|
||||
stakers = append(stakers, StakerJSON{
|
||||
NodeID: s.NodeID.String(),
|
||||
RewardAddress: formatBech32WithChain("P", hrp, s.RewardAddress),
|
||||
DelegationFee: s.DelegationFee,
|
||||
Signer: s.Signer,
|
||||
})
|
||||
}
|
||||
|
||||
return &ConfigOutput{
|
||||
NetworkID: c.NetworkID,
|
||||
Allocations: allocations,
|
||||
StartTime: c.StartTime,
|
||||
InitialStakeDuration: c.InitialStakeDuration,
|
||||
InitialStakeDurationOffset: c.InitialStakeDurationOffset,
|
||||
InitialStakedFunds: stakedFunds,
|
||||
InitialStakers: stakers,
|
||||
CChainGenesis: c.CChainGenesis,
|
||||
DChainGenesis: c.DChainGenesis,
|
||||
QChainGenesis: c.QChainGenesis,
|
||||
AChainGenesis: c.AChainGenesis,
|
||||
BChainGenesis: c.BChainGenesis,
|
||||
TChainGenesis: c.TChainGenesis,
|
||||
ZChainGenesis: c.ZChainGenesis,
|
||||
GChainGenesis: c.GChainGenesis,
|
||||
KChainGenesis: c.KChainGenesis,
|
||||
Message: c.Message,
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalJSON implements json.Marshaler for Config
|
||||
func (c *Config) MarshalJSON() ([]byte, error) {
|
||||
hrp := constants.GetHRP(c.NetworkID)
|
||||
return json.Marshal(c.ToJSON(hrp))
|
||||
}
|
||||
|
||||
// formatBech32WithChain formats an address with chain prefix (e.g., "P-lux1...")
|
||||
func formatBech32WithChain(chainID, hrp string, addr ids.ShortID) string {
|
||||
bech32Addr, err := address.FormatBech32(hrp, addr[:])
|
||||
if err != nil {
|
||||
return fmt.Sprintf("%s-%s1%s", chainID, hrp, addr.Hex())
|
||||
}
|
||||
return fmt.Sprintf("%s-%s", chainID, bech32Addr)
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
// Copyright (C) 2019-2025, Lux Partners Limited. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package genesis
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
|
||||
"github.com/luxfi/address"
|
||||
"github.com/luxfi/ids"
|
||||
)
|
||||
|
||||
func TestFormatBech32WithChain(t *testing.T) {
|
||||
// Known test addresses from mainnet genesis
|
||||
testCases := []struct {
|
||||
name string
|
||||
ethAddrHex string
|
||||
chainPrefix string
|
||||
hrp string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "node1_mainnet",
|
||||
ethAddrHex: "9011E888251AB053B7bD1cdB598Db4f9DEd94714",
|
||||
chainPrefix: "P",
|
||||
hrp: "lux",
|
||||
expected: "P-lux1jqg73zp9r2c98daarnd4nrd5l80dj3c5eha5fl",
|
||||
},
|
||||
{
|
||||
name: "node2_mainnet",
|
||||
ethAddrHex: "EAbCC110fAcBfebabC66Ad6f9E7B67288e720B59",
|
||||
chainPrefix: "P",
|
||||
hrp: "lux",
|
||||
expected: "P-lux1a27vzy86e0lt40rx44heu7m89z88yz6ey7av5e",
|
||||
},
|
||||
{
|
||||
name: "node3_mainnet",
|
||||
ethAddrHex: "8d5081153aE1cfb41f5c932fe0b6Beb7E159cF84",
|
||||
chainPrefix: "P",
|
||||
hrp: "lux",
|
||||
expected: "P-lux134ggz9f6u88mg86ujvh7pd47kls4nnuy3hx4yp",
|
||||
},
|
||||
{
|
||||
name: "node4_mainnet",
|
||||
ethAddrHex: "f8f12D0592e6d1bFe92ee16CaBCC4a6F26dAAe23",
|
||||
chainPrefix: "P",
|
||||
hrp: "lux",
|
||||
expected: "P-lux1lrcj6pvjumgml6fwu9k2hnz2dund4t3rpsjuxu",
|
||||
},
|
||||
{
|
||||
name: "node5_mainnet",
|
||||
ethAddrHex: "Fb66808f708e1d4D7D43a8c75596e84f94e06806",
|
||||
chainPrefix: "P",
|
||||
hrp: "lux",
|
||||
expected: "P-lux1ldngprms3cw56l2r4rr4t9hgf72wq6qx057vd2",
|
||||
},
|
||||
{
|
||||
name: "x_chain_testnet",
|
||||
ethAddrHex: "9011E888251AB053B7bD1cdB598Db4f9DEd94714",
|
||||
chainPrefix: "X",
|
||||
hrp: "test",
|
||||
expected: "X-test1jqg73zp9r2c98daarnd4nrd5l80dj3c5644e09",
|
||||
},
|
||||
{
|
||||
name: "local_dev",
|
||||
ethAddrHex: "9011E888251AB053B7bD1cdB598Db4f9DEd94714",
|
||||
chainPrefix: "P",
|
||||
hrp: "local",
|
||||
expected: "P-local1jqg73zp9r2c98daarnd4nrd5l80dj3c56acgey",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
addrBytes, err := hex.DecodeString(tc.ethAddrHex)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to decode eth address: %v", err)
|
||||
}
|
||||
|
||||
var shortID ids.ShortID
|
||||
copy(shortID[:], addrBytes)
|
||||
|
||||
// Test our formatBech32WithChain function
|
||||
result := formatBech32WithChain(tc.chainPrefix, tc.hrp, shortID)
|
||||
|
||||
if result != tc.expected {
|
||||
t.Errorf("formatBech32WithChain() = %s, want %s", result, tc.expected)
|
||||
}
|
||||
|
||||
// Verify the address can be parsed back
|
||||
chainID, hrp, parsedAddr, err := address.Parse(result)
|
||||
if err != nil {
|
||||
t.Errorf("failed to parse generated address: %v", err)
|
||||
}
|
||||
if chainID != tc.chainPrefix {
|
||||
t.Errorf("parsed chain ID = %s, want %s", chainID, tc.chainPrefix)
|
||||
}
|
||||
if hrp != tc.hrp {
|
||||
t.Errorf("parsed hrp = %s, want %s", hrp, tc.hrp)
|
||||
}
|
||||
if !equalBytes(parsedAddr, addrBytes) {
|
||||
t.Errorf("parsed address doesn't match: got %x, want %x", parsedAddr, addrBytes)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidAddressChecksum(t *testing.T) {
|
||||
// Test that the broken addresses from the old genesis are rejected
|
||||
invalidAddresses := []string{
|
||||
"P-lux18jma8ppw3nhx5r4ap8clazz0dps7rv5u00z96u", // Invalid checksum
|
||||
}
|
||||
|
||||
for _, addr := range invalidAddresses {
|
||||
_, _, _, err := address.Parse(addr)
|
||||
if err == nil {
|
||||
t.Errorf("expected error parsing invalid address %s, got nil", addr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBech32ChecksumConsistency(t *testing.T) {
|
||||
// Verify that our implementation produces the same result as the node's address package
|
||||
testAddrs := []string{
|
||||
"9011E888251AB053B7bD1cdB598Db4f9DEd94714",
|
||||
"EAbCC110fAcBfebabC66Ad6f9E7B67288e720B59",
|
||||
"8d5081153aE1cfb41f5c932fe0b6Beb7E159cF84",
|
||||
"f8f12D0592e6d1bFe92ee16CaBCC4a6F26dAAe23",
|
||||
"Fb66808f708e1d4D7D43a8c75596e84f94e06806",
|
||||
}
|
||||
|
||||
for _, ethAddr := range testAddrs {
|
||||
addrBytes, err := hex.DecodeString(ethAddr)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to decode eth address: %v", err)
|
||||
}
|
||||
|
||||
var shortID ids.ShortID
|
||||
copy(shortID[:], addrBytes)
|
||||
|
||||
// Our implementation
|
||||
ourAddr := formatBech32WithChain("P", "lux", shortID)
|
||||
|
||||
// Node's implementation
|
||||
nodeAddr, err := address.Format("P", "lux", addrBytes)
|
||||
if err != nil {
|
||||
t.Fatalf("node's Format failed: %v", err)
|
||||
}
|
||||
|
||||
if ourAddr != nodeAddr {
|
||||
t.Errorf("address mismatch for %s:\n ours: %s\n node: %s", ethAddr, ourAddr, nodeAddr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func equalBytes(a, b []byte) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for i := range a {
|
||||
if a[i] != b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -0,0 +1,265 @@
|
||||
// Copyright (C) 2019-2025, Lux Partners Limited. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package genesis
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/luxfi/address"
|
||||
ethcrypto "github.com/luxfi/crypto"
|
||||
luxcrypto "github.com/luxfi/crypto/secp256k1"
|
||||
"github.com/luxfi/ids"
|
||||
)
|
||||
|
||||
// Common allocation amounts
|
||||
const (
|
||||
// OneBillionLUX is 1B LUX - each mainnet validator gets this amount
|
||||
OneBillionLUX uint64 = 1_000_000_000 * Lux // 10^15 microLUX
|
||||
|
||||
// OneHundredMillionLUX is 100M LUX - smaller allocation for testing
|
||||
OneHundredMillionLUX uint64 = 100_000_000 * Lux // 10^14 microLUX
|
||||
|
||||
// DefaultValidatorAllocation is 1B LUX per validator
|
||||
DefaultValidatorAllocation uint64 = OneBillionLUX
|
||||
|
||||
// VestingPeriods is 100 years (1% unlocks per year)
|
||||
VestingPeriods = 100
|
||||
)
|
||||
|
||||
// StakingStartTime and UnlockInterval are defined in keys.go
|
||||
|
||||
// ValidatorKeyInfo contains computed addresses from a validator's private key
|
||||
type ValidatorKeyInfo struct {
|
||||
PrivKeyHex string // Hex-encoded private key
|
||||
EthAddr string // C-chain Ethereum address (0x...)
|
||||
ShortID ids.ShortID // P/X chain address as ShortID
|
||||
}
|
||||
|
||||
// DefaultValidatorKeyPath returns the default path for validator keys
|
||||
func DefaultValidatorKeyPath() string {
|
||||
home, _ := os.UserHomeDir()
|
||||
return filepath.Join(home, ".lux", "keys")
|
||||
}
|
||||
|
||||
// LoadOrGenerateValidatorKeys loads validator keys from the specified path or generates new ones if missing.
|
||||
// Keys are stored as hex-encoded private keys in files named validator_XXX.pk
|
||||
func LoadOrGenerateValidatorKeys(keyPath string, count int) ([]ValidatorKeyInfo, error) {
|
||||
if keyPath == "" {
|
||||
keyPath = DefaultValidatorKeyPath()
|
||||
}
|
||||
|
||||
// Ensure key directory exists
|
||||
if err := os.MkdirAll(keyPath, 0700); err != nil {
|
||||
return nil, fmt.Errorf("failed to create key directory: %w", err)
|
||||
}
|
||||
|
||||
keys := make([]ValidatorKeyInfo, count)
|
||||
for i := 0; i < count; i++ {
|
||||
keyFile := filepath.Join(keyPath, fmt.Sprintf("validator_%03d.pk", i))
|
||||
|
||||
var privKeyHex string
|
||||
data, err := os.ReadFile(keyFile)
|
||||
if err != nil {
|
||||
// Key doesn't exist, generate new one
|
||||
privKey, genErr := generatePrivateKey()
|
||||
if genErr != nil {
|
||||
return nil, fmt.Errorf("failed to generate key %d: %w", i, genErr)
|
||||
}
|
||||
privKeyHex = hex.EncodeToString(privKey)
|
||||
|
||||
// Save the new key
|
||||
if err := os.WriteFile(keyFile, []byte(privKeyHex+"\n"), 0600); err != nil {
|
||||
return nil, fmt.Errorf("failed to save key %d: %w", i, err)
|
||||
}
|
||||
} else {
|
||||
privKeyHex = strings.TrimSpace(string(data))
|
||||
}
|
||||
|
||||
// Compute addresses from the key
|
||||
keyInfo, err := ComputeValidatorKeyInfo(privKeyHex)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to compute addresses for key %d: %w", i, err)
|
||||
}
|
||||
keys[i] = keyInfo
|
||||
}
|
||||
|
||||
return keys, nil
|
||||
}
|
||||
|
||||
// ComputeValidatorKeyInfo derives addresses from a hex-encoded private key
|
||||
func ComputeValidatorKeyInfo(privKeyHex string) (ValidatorKeyInfo, error) {
|
||||
privKeyBytes, err := hex.DecodeString(privKeyHex)
|
||||
if err != nil {
|
||||
return ValidatorKeyInfo{}, fmt.Errorf("invalid hex key: %w", err)
|
||||
}
|
||||
|
||||
// Get ETH address
|
||||
ethPrivKey, err := ethcrypto.ToECDSA(privKeyBytes)
|
||||
if err != nil {
|
||||
return ValidatorKeyInfo{}, fmt.Errorf("invalid ECDSA key: %w", err)
|
||||
}
|
||||
ethAddr := ethcrypto.PubkeyToAddress(ethPrivKey.PublicKey)
|
||||
|
||||
// Get Lux ShortID (for X/P chain addresses)
|
||||
luxPrivKey, err := luxcrypto.ToPrivateKey(privKeyBytes)
|
||||
if err != nil {
|
||||
return ValidatorKeyInfo{}, fmt.Errorf("invalid Lux key: %w", err)
|
||||
}
|
||||
pubKey := luxPrivKey.PublicKey()
|
||||
shortID := ids.ShortID(pubKey.Address())
|
||||
|
||||
return ValidatorKeyInfo{
|
||||
PrivKeyHex: privKeyHex,
|
||||
EthAddr: ethAddr.Hex(),
|
||||
ShortID: shortID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// FormatChainAddress formats a ShortID as an X or P chain address with the given HRP
|
||||
func FormatChainAddress(chainID string, hrp string, shortID ids.ShortID) (string, error) {
|
||||
return address.Format(chainID, hrp, shortID[:])
|
||||
}
|
||||
|
||||
// generatePrivateKey generates a new random 32-byte private key
|
||||
func generatePrivateKey() ([]byte, error) {
|
||||
key := make([]byte, 32)
|
||||
if _, err := rand.Read(key); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// GeneratePChainAllocations creates P-chain genesis allocations for validator keys.
|
||||
// Each key gets the specified amount immediately available (locktime=0) for spending.
|
||||
func GeneratePChainAllocations(keys []ValidatorKeyInfo, hrp string, amountPerKey uint64) ([]AllocationJSON, error) {
|
||||
if amountPerKey == 0 {
|
||||
amountPerKey = DefaultValidatorAllocation
|
||||
}
|
||||
|
||||
allocations := make([]AllocationJSON, len(keys))
|
||||
for i, key := range keys {
|
||||
luxAddr, err := FormatChainAddress("P", hrp, key.ShortID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to format address for key %d: %w", i, err)
|
||||
}
|
||||
|
||||
// Simple allocation: all funds immediately available (no vesting)
|
||||
unlockSchedule := []LockedAmount{
|
||||
{
|
||||
Amount: amountPerKey,
|
||||
Locktime: 0, // Immediately available
|
||||
},
|
||||
}
|
||||
|
||||
allocations[i] = AllocationJSON{
|
||||
ETHAddr: key.EthAddr,
|
||||
LUXAddr: luxAddr,
|
||||
InitialAmount: 0, // initialAmount is NOT immediately spendable
|
||||
UnlockSchedule: unlockSchedule,
|
||||
}
|
||||
}
|
||||
return allocations, nil
|
||||
}
|
||||
|
||||
// GeneratePChainAllocationsWithVesting creates P-chain allocations with a vesting schedule.
|
||||
// Each key gets the specified amount vested over the given number of periods.
|
||||
func GeneratePChainAllocationsWithVesting(keys []ValidatorKeyInfo, hrp string, amountPerKey uint64, startTime uint64, interval uint64, periods int) ([]AllocationJSON, error) {
|
||||
if amountPerKey == 0 {
|
||||
amountPerKey = OneBillionLUX
|
||||
}
|
||||
if startTime == 0 {
|
||||
startTime = StakingStartTime
|
||||
}
|
||||
if interval == 0 {
|
||||
interval = UnlockInterval
|
||||
}
|
||||
if periods == 0 {
|
||||
periods = 100 // 100 years default
|
||||
}
|
||||
|
||||
allocations := make([]AllocationJSON, len(keys))
|
||||
for i, key := range keys {
|
||||
luxAddr, err := FormatChainAddress("P", hrp, key.ShortID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to format address for key %d: %w", i, err)
|
||||
}
|
||||
|
||||
// Build vesting schedule
|
||||
unlockSchedule := buildUnlockSchedule(amountPerKey, startTime, interval, periods)
|
||||
|
||||
allocations[i] = AllocationJSON{
|
||||
ETHAddr: key.EthAddr,
|
||||
LUXAddr: luxAddr,
|
||||
InitialAmount: amountPerKey, // X-chain initial amount
|
||||
UnlockSchedule: unlockSchedule,
|
||||
}
|
||||
}
|
||||
return allocations, nil
|
||||
}
|
||||
|
||||
// GenerateCChainAlloc creates C-chain genesis allocations for validator keys.
|
||||
func GenerateCChainAlloc(keys []ValidatorKeyInfo, amount uint64) map[string]Balance {
|
||||
if amount == 0 {
|
||||
amount = DefaultValidatorAllocation
|
||||
}
|
||||
|
||||
alloc := make(map[string]Balance)
|
||||
for _, key := range keys {
|
||||
alloc[key.EthAddr] = Balance{
|
||||
Balance: fmt.Sprintf("0x%x", amount),
|
||||
}
|
||||
}
|
||||
return alloc
|
||||
}
|
||||
|
||||
// GenerateCChainAllocMap creates C-chain genesis allocations as a simple map for netrunner.
|
||||
func GenerateCChainAllocMap(keys []ValidatorKeyInfo, amount uint64) map[string]map[string]string {
|
||||
if amount == 0 {
|
||||
amount = DefaultValidatorAllocation
|
||||
}
|
||||
|
||||
alloc := make(map[string]map[string]string)
|
||||
balanceHex := fmt.Sprintf("0x%x", amount)
|
||||
for _, key := range keys {
|
||||
alloc[key.EthAddr] = map[string]string{"balance": balanceHex}
|
||||
}
|
||||
return alloc
|
||||
}
|
||||
|
||||
// GenerateAllocationsMapForNetrunner creates allocations in the format netrunner expects.
|
||||
// This is a convenience function for backwards compatibility.
|
||||
func GenerateAllocationsMapForNetrunner(keys []ValidatorKeyInfo, hrp string, amountPerKey uint64) ([]map[string]interface{}, error) {
|
||||
if amountPerKey == 0 {
|
||||
amountPerKey = DefaultValidatorAllocation
|
||||
}
|
||||
|
||||
allocations := make([]map[string]interface{}, len(keys))
|
||||
for i, key := range keys {
|
||||
luxAddr, err := FormatChainAddress("P", hrp, key.ShortID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to format address for key %d: %w", i, err)
|
||||
}
|
||||
|
||||
// Simple allocation: all funds immediately available
|
||||
unlockSchedule := []map[string]interface{}{
|
||||
{
|
||||
"amount": amountPerKey,
|
||||
"locktime": uint64(0),
|
||||
},
|
||||
}
|
||||
|
||||
allocations[i] = map[string]interface{}{
|
||||
"ethAddr": key.EthAddr,
|
||||
"luxAddr": luxAddr,
|
||||
"initialAmount": uint64(0),
|
||||
"unlockSchedule": unlockSchedule,
|
||||
}
|
||||
}
|
||||
return allocations, nil
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package genesis_test
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/luxfi/genesis/pkg/genesis"
|
||||
)
|
||||
|
||||
func TestFee0Key(t *testing.T) {
|
||||
privKeyHex := "abd51d463510cb17d7ba09e535828383d9c2c817aa386024aacce1660a1ee625"
|
||||
|
||||
keyInfo, err := genesis.ComputeValidatorKeyInfo(privKeyHex)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Printf("ETH address: %s\n", keyInfo.EthAddr)
|
||||
fmt.Printf("ShortID (hex): %s\n", hex.EncodeToString(keyInfo.ShortID[:]))
|
||||
|
||||
pAddr, err := genesis.FormatChainAddress("P", "dev", keyInfo.ShortID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Printf("P-chain address: %s\n", pAddr)
|
||||
fmt.Printf("\nExpected: P-dev1e44zjaddy52vjqa40ws90uwu9c2ryp7egufeqg\n")
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Derive 100 Ethereum accounts from a BIP39 mnemonic for Lux C-chain genesis allocation.
|
||||
|
||||
Usage:
|
||||
python derive_accounts.py > accounts_100.json
|
||||
"""
|
||||
|
||||
import json
|
||||
import sys
|
||||
|
||||
from eth_account import Account
|
||||
|
||||
|
||||
MNEMONIC = "REDACTED_USE_KMS"
|
||||
PATH_TEMPLATE = "m/44'/60'/0'/0/{i}"
|
||||
NUM_ACCOUNTS = 100
|
||||
|
||||
# 2 trillion LUX in wei (2e30) for account 0
|
||||
BALANCE_ACCOUNT_0 = "0x193e5939a08ce9dbd480000000"
|
||||
|
||||
# 1 billion LUX in wei (1e27) for accounts 1-99
|
||||
BALANCE_DEFAULT = "0x33b2e3c9fd0803ce8000000"
|
||||
|
||||
|
||||
def derive_accounts(mnemonic: str, count: int) -> list[dict]:
|
||||
Account.enable_unaudited_hdwallet_features()
|
||||
accounts = []
|
||||
for i in range(count):
|
||||
path = PATH_TEMPLATE.format(i=i)
|
||||
acct = Account.from_mnemonic(mnemonic, account_path=path)
|
||||
accounts.append({
|
||||
"index": i,
|
||||
"address": acct.address,
|
||||
"private_key_hex": acct.key.hex(),
|
||||
})
|
||||
return accounts
|
||||
|
||||
|
||||
def build_alloc(accounts: list[dict]) -> dict:
|
||||
alloc = {}
|
||||
for entry in accounts:
|
||||
addr = entry["address"]
|
||||
balance = BALANCE_ACCOUNT_0 if entry["index"] == 0 else BALANCE_DEFAULT
|
||||
alloc[addr] = {"balance": balance}
|
||||
return alloc
|
||||
|
||||
|
||||
def main():
|
||||
accounts = derive_accounts(MNEMONIC, NUM_ACCOUNTS)
|
||||
alloc = build_alloc(accounts)
|
||||
|
||||
output = {
|
||||
"mnemonic": MNEMONIC,
|
||||
"derivation_path_template": PATH_TEMPLATE,
|
||||
"num_accounts": NUM_ACCOUNTS,
|
||||
"account_0_balance": BALANCE_ACCOUNT_0,
|
||||
"default_balance": BALANCE_DEFAULT,
|
||||
"accounts": accounts,
|
||||
"alloc": alloc,
|
||||
}
|
||||
|
||||
json.dump(output, sys.stdout, indent=2)
|
||||
sys.stdout.write("\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user