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:
Hanzo AI
2026-04-04 11:22:32 -07:00
commit 2b46a8bc3c
80 changed files with 262902 additions and 0 deletions
+189
View File
@@ -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
+29
View File
@@ -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.
+461
View File
@@ -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*
+65
View File
@@ -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/
+135
View File
@@ -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
+191
View File
@@ -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.
+958
View File
@@ -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)
}
+193
View File
@@ -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)
})
}
}
+51
View File
@@ -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)
}
}
+167
View File
@@ -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", &timestamp)
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)
}
+86
View File
@@ -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
}
+215
View File
@@ -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)
}
+244
View File
@@ -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
}
+8
View File
@@ -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
}
+8
View File
@@ -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
}
+8
View File
@@ -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
}
+8
View File
@@ -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
}
+398
View File
@@ -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)
}
+116
View File
@@ -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"
}
+161
View File
@@ -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\"}"
}
+5
View File
@@ -0,0 +1,5 @@
{
"networkID": 1337,
"message": "Omni Lucem Petunt",
"startTime": 1766708400
}
+92
View File
@@ -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"
}
}
]
}
+85
View File
@@ -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"
}
+92
View File
@@ -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
}
+14
View File
@@ -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"
}
]
+368
View File
@@ -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
+5
View File
@@ -0,0 +1,5 @@
{
"networkID": 3,
"message": "Lux et Libertas",
"startTime": 1770739200
}
File diff suppressed because it is too large Load Diff
+17
View File
@@ -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())
}
}
+64
View File
@@ -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)
}
}
}
+71
View File
@@ -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"
}
+71
View File
@@ -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"
}
+71
View File
@@ -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"
}
+86
View File
@@ -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
+5
View File
@@ -0,0 +1,5 @@
{
"networkID": 1337,
"message": "Local Dev Network",
"startTime": 0
}
+22
View File
@@ -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"
}
]
+75
View File
@@ -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
+5
View File
@@ -0,0 +1,5 @@
{
"networkID": 1,
"message": "Lux et Libertas",
"startTime": 1765573611
}
File diff suppressed because it is too large Load Diff
+70
View File
@@ -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"
}
+70
View File
@@ -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"
}
+70
View File
@@ -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"
}
+70
View File
@@ -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"
}
+70
View File
@@ -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"
}
+70
View File
@@ -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"
}
+10
View File
@@ -0,0 +1,10 @@
[
{
"id": "NodeID-Mf3JfSY91oDwfBqf7rCLmhg4NDtDghw1f",
"ip": "134.199.187.16:9641"
},
{
"id": "NodeID-2TwSZ2oyeBK2mv7JiseEQ8m74rotDj4QR",
"ip": "209.38.174.84:9641"
}
]
+65
View File
@@ -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
+5
View File
@@ -0,0 +1,5 @@
{
"networkID": 2,
"message": "Lux ex Tenebris",
"startTime": 1770739200
}
+148
View File
@@ -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
+368
View File
@@ -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"
}
+71
View File
@@ -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"
}
+368
View File
@@ -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"
}
+34
View File
@@ -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
+190
View File
@@ -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
+4130
View File
File diff suppressed because it is too large Load Diff
+73
View File
@@ -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
+255
View File
@@ -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=
+8
View File
@@ -0,0 +1,8 @@
-----BEGIN CERTIFICATE-----
MIIBETCBt6ADAgECAgEAMAoGCCqGSM49BAMCMAAwIBcNOTkxMjMxMDAwMDAwWhgP
MjA5OTEyMzEwMDAwMDBaMAAwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAR44B3Q
XD5lXOJudiUTZiu/g5l6Uxy9JcxTtJfIpDtkXpeSoLXjDB5/i+mGsKQsLhCFWI8S
Fgm/WR4pfEzFa30uoyAwHjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAK
BggqhkjOPQQDAgNJADBGAiEAq5oc+KsKa4V3uH/+SD0JX8Pr7xiC1dgRpJ+k8iu0
oKQCIQCdbywYTWMs45Ku+PGvnmVGYYR5YBSyD92O0596930gcg==
-----END CERTIFICATE-----
+5
View File
@@ -0,0 +1,5 @@
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgByb+f3Bl1lXKUVKR
q/QVTbe2QdjsmPnNiQtnEQYep6ehRANCAAR44B3QXD5lXOJudiUTZiu/g5l6Uxy9
JcxTtJfIpDtkXpeSoLXjDB5/i+mGsKQsLhCFWI8SFgm/WR4pfEzFa30u
-----END PRIVATE KEY-----
+1
View File
@@ -0,0 +1 @@
ZßÛk†M2³ùIšO$%Pdzö§H×!—4À‰@
+222
View File
@@ -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()
}
+285
View File
@@ -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")
}
+285
View File
@@ -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
}
+263
View File
@@ -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
}
+113
View File
@@ -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
}
+734
View File
@@ -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
}
+28
View File
@@ -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
}
+366
View File
@@ -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
}
+308
View File
@@ -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)
}
+167
View File
@@ -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
}
+265
View File
@@ -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
}
+28
View File
@@ -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")
}
+67
View File
@@ -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()