11 KiB
Lux Network Migration Guide
Last Updated: 2025-12-12
Overview
This guide describes the one and only way to migrate blockchain data between Lux network VMs: using the MigrateAPI (migrate_importBlocks). This API is implemented in both cchainvm (C-Chain) and evm (SubnetEVM).
Architecture: geth vs coreth vs evm
Understanding the relationship between these packages is essential:
Package Hierarchy
┌─────────────────────────────────────────────────────────────────┐
│ luxfi/node │
│ ┌─────────────────┐ ┌─────────────────┐ ┌───────────────┐ │
│ │ cchainvm │ │ evm │ │ other vms │ │
│ │ (C-Chain VM) │ │ (SubnetEVM) │ │ │ │
│ │ + MigrateAPI │ │ + MigrateAPI │ │ │ │
│ └────────┬────────┘ └────────┬────────┘ └───────────────┘ │
└────────────┼────────────────────┼───────────────────────────────┘
│ │
▼ ▼
┌────────────────────┐ ┌────────────────────┐
│ luxfi/coreth │ │ luxfi/evm │
│ (C-Chain impl) │ │ (SubnetEVM impl) │
│ Uses geth │ │ Uses geth │
└─────────┬──────────┘ └─────────┬──────────┘
│ │
└───────────┬───────────┘
▼
┌────────────────────┐
│ luxfi/geth │
│ (go-ethereum fork)│
│ Pure EVM engine │
└────────────────────┘
Package Descriptions
| Package | Repository | Purpose |
|---|---|---|
| geth | github.com/luxfi/geth |
Pure go-ethereum fork. Core EVM execution, RLP encoding, state trie, transaction types. No migration logic here. |
| coreth | github.com/luxfi/coreth |
C-Chain implementation built on geth. Handles C-Chain consensus integration. Used by cchainvm. |
| evm | github.com/luxfi/evm |
SubnetEVM implementation built on geth. For L1/subnet chains. Contains its own MigrateAPI. |
| cchainvm | github.com/luxfi/node/vms/cchainvm |
C-Chain VM wrapper. Contains MigrateAPI for C-Chain. |
| migrate | github.com/luxfi/migrate |
Migration tools: export from PebbleDB, import via RPC. |
Key Points
- geth is ONLY the EVM engine - it has no migration APIs
- MigrateAPI exists in
cchainvm(for C-Chain) andevm(for SubnetEVM) - coreth is used internally by cchainvm, not directly exposed
- All migration goes through
migrate_importBlocksRPC endpoint
Migration Flow
The canonical migration process:
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ Source Chain │ │ JSONL │ │ Target Chain │
│ (PebbleDB) │────▶│ (blocks) │────▶│ (BadgerDB) │
│ │ │ │ │ │
│ Export tool │ │ Standard format │ │ migrate_import │
│ (read-only) │ │ with RLP data │ │ Blocks API │
└──────────────────┘ └──────────────────┘ └──────────────────┘
Step 1: Export Blocks
Export from source database (PebbleDB) to JSONL:
# Build export tool
cd /home/z/work/lux/migrate
go build -o bin/export ./cmd/export/
# Export all blocks
./bin/export \
-db /path/to/pebbledb \
-output blocks.jsonl \
-start 0 \
-end 0 # 0 = head block
Step 2: Import via MigrateAPI
Import to target chain using the migrate_importBlocks RPC:
# Build import tool
go build -o bin/import-rpc ./cmd/import-rpc/
# Import blocks
./bin/import-rpc \
-jsonl blocks.jsonl \
-rpc http://127.0.0.1:9630/ext/bc/C/rpc \
-batch 100 \
-start 1 \
-reload 10000
JSONL Format Specification
The canonical JSONL format uses camelCase field names matching the MigrateAPI:
{
"height": 12345,
"hash": "0x1234...abcd",
"parentHash": "0xabcd...1234",
"timestamp": 1702400000,
"stateRoot": "0x...",
"receiptsRoot": "0x...",
"transactionsRoot": "0x...",
"gasLimit": 8000000,
"gasUsed": 21000,
"difficulty": "1",
"coinbase": "0x0000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"extraData": "0x",
"baseFee": "25000000000",
"header": "0xf90213...",
"body": "0xf90210...",
"receipts": "0xf901ae...",
"stateChanges": {
"0x1234...": {
"balance": "1000000000000000000",
"nonce": 5,
"code": "0x608060...",
"storage": {
"0x0": "0x1"
}
}
}
}
Field Reference
| Field | Type | Description |
|---|---|---|
height |
uint64 | Block number (matches Number in BlockData) |
hash |
hex string | Block hash with 0x prefix |
header |
hex string | RLP-encoded block header |
body |
hex string | RLP-encoded block body (transactions, uncles) |
receipts |
hex string | RLP-encoded transaction receipts |
stateChanges |
object | Optional: account state to import |
Backwards Compatibility
The UnmarshalJSON handler supports multiple formats:
- New canonical (camelCase):
height,hash,header,body,receipts - Legacy export (PascalCase):
Number,Hash,Header,Body,Receipts - RPC format:
number,miner,baseFeePerGas
MigrateAPI Reference
migrate_importBlocks
Import RLP-encoded blocks with optional state changes.
Request:
{
"jsonrpc": "2.0",
"id": 1,
"method": "migrate_importBlocks",
"params": [[
{
"height": 1,
"hash": "0x...",
"header": "0x...",
"body": "0x...",
"receipts": "0x...",
"stateChanges": {}
}
]]
}
Response:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"imported": 100,
"failed": 0,
"errors": []
}
}
migrate_setGenesis
Set genesis block (block 0). Must be called before importing other blocks if genesis needs modification.
Request:
{
"jsonrpc": "2.0",
"id": 1,
"method": "migrate_setGenesis",
"params": [{
"height": 0,
"hash": "0x...",
"header": "0x...",
"body": "0x...",
"receipts": "0x..."
}]
}
lux_reloadBlockchain
Force blockchain to recognize database changes after import.
Request:
{
"jsonrpc": "2.0",
"id": 1,
"method": "lux_reloadBlockchain",
"params": []
}
lux_verifyBlockchain
Verify blockchain integrity after import.
Request:
{
"jsonrpc": "2.0",
"id": 1,
"method": "lux_verifyBlockchain",
"params": []
}
State Migration
With State Changes
To preserve account balances, contract code, and storage:
- Export with state: Include
stateChangesin each block's JSONL - Import: MigrateAPI writes state to trie database
- Verify: Check balances with
eth_getBalance
Without State (Block-only)
For block history without state:
- Export blocks only (no
stateChanges) - Import creates block records
- State must come from genesis or separate migration
Database Considerations
| Database | Use Case | Location |
|---|---|---|
| PebbleDB | Legacy SubnetEVM chains (export source) | /path/to/chain/db/pebbledb |
| BadgerDB | New C-Chain and subnets (import target) | ~/.avalanche-cli/runs/.../db |
| LevelDB | Not recommended | Legacy only |
Important: Always use BadgerDB for target chains. PebbleDB is only for reading legacy data.
Complete Migration Example
Migrate SubnetEVM to C-Chain
# 1. Stop source chain (if running)
# 2. Export blocks from SubnetEVM PebbleDB
cd /home/z/work/lux/migrate
./bin/export \
-db /home/z/work/lux/state/chaindata/lux-mainnet-96369/db/pebbledb \
-output /tmp/blocks.jsonl \
-start 0 \
-end 0
# 3. Start target C-Chain node with migrate API enabled
luxd --network-id=96369 \
--staking-enabled=false \
--api-admin-enabled=true
# 4. Verify migrate API is available
curl -s http://127.0.0.1:9630/ext/bc/C/rpc \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"rpc_modules","params":[]}' \
| grep migrate
# 5. Import blocks (skip genesis if using same genesis config)
./bin/import-rpc \
-jsonl /tmp/blocks.jsonl \
-rpc http://127.0.0.1:9630/ext/bc/C/rpc \
-start 1 \
-batch 100 \
-reload 10000
# 6. Verify import
curl -s http://127.0.0.1:9630/ext/bc/C/rpc \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"eth_blockNumber","params":[]}'
# 7. Verify state (check a known balance)
curl -s http://127.0.0.1:9630/ext/bc/C/rpc \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"eth_getBalance","params":["0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC","latest"]}'
Troubleshooting
Import Fails with "migrate API not available"
Ensure node is started with admin API enabled:
luxd --api-admin-enabled=true
State Root Mismatch
If importing blocks produces state root mismatches:
- Ensure genesis configuration matches source chain
- Include
stateChangesin JSONL export - Verify state trie commits complete
Slow Import Performance
Optimize with:
- Larger batch size:
-batch 500 - Less frequent reloads:
-reload 50000 - SSD storage for database
Memory Issues
If running out of memory:
- Reduce batch size:
-batch 50 - Import in chunks (use
-startand-end)
Files Reference
| Path | Purpose |
|---|---|
/home/z/work/lux/migrate/ |
Migration package |
/home/z/work/lux/migrate/types.go |
BlockData, Account types |
/home/z/work/lux/migrate/cmd/export/ |
Export tool |
/home/z/work/lux/migrate/cmd/import-rpc/ |
Import tool |
/home/z/work/lux/node/vms/cchainvm/api.go |
C-Chain MigrateAPI |
/home/z/work/lux/evm/plugin/evm/api_migrate.go |
SubnetEVM MigrateAPI |
Version History
- 2025-12-12: Unified field naming (camelCase), removed snake_case variants
- 2025-12-04: Added stateChanges support
- 2025-11-30: Initial MigrateAPI implementation