Files
evm/LLM.md
zeekayandHanzo Dev c67047cc8d docs: LLM.md documents the software, not our operation of it
This is a tracked file in a public repository. It carried a deployment analysis
naming a cluster, namespace and statefulset, a recovery narrative, transaction
hashes and balances from a running network, an address described as one the
author controls, and a run of dated Problem/Status-FIXED postmortems. That is
operational record, and it belongs to the private one.

The RewardManager section keeps the mechanism, the config that activates it, the
in-process proof, and the per-chain DAO addresses a reader needs to configure it;
what it exercised on a running network is now stated as what was verified rather
than as where and on which pods. The durable facts inside the postmortems are
kept as properties: no beacon chain, so the Cancun blob fields are not part of a
Lux EVM block; a genesis file has to reproduce the chain's block 0; importing
history moves the database under anything reading it.

Also drops "subnet" from two lines describing our own chains.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-26 13:32:49 -07:00

944 lines
39 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Lux EVM Module
## Project Overview
Lux EVM (formerly EVM) is the Ethereum Virtual Machine implementation for Lux L2 chains. This module provides EVM compatibility for the Lux network.
## CRITICAL VERSION REQUIREMENTS
**ALWAYS use these Lux-specific versions:**
- `github.com/luxfi/node v1.22.64` - Latest Lux node version
- `github.com/luxfi/geth v1.16.64` - Our fork of go-ethereum (with PQ crypto precompiles)
- `github.com/luxfi/crypto v1.17.27` - Cryptographic primitives
- `github.com/luxfi/precompiles v0.1.2` - Standalone precompile contracts
- `github.com/luxfi/p2p` - P2P networking package
- `github.com/luxfi/warp` - Warp messaging package
- `github.com/luxfi/consensus` - Consensus package
### IMPORTANT: Package Usage
- Use `lp118` package for p2p handlers
- Import: `github.com/luxfi/p2p/lp118`
- All handler IDs: `lp118.HandlerID`
- All functions: `lp118.NewCachedHandler`, `lp118.NewSignatureAggregator`
- NEVER import from ava-labs packages
- NEVER use go-ethereum directly, always use luxfi/geth
### p2p.Handler Interface
The `p2p.Handler` interface uses these methods:
- `Gossip(ctx, nodeID, gossipBytes)` - NOT AppGossip
- `Request(ctx, nodeID, deadline, requestBytes) ([]byte, *p2p.Error)` - NOT AppRequest
### p2p.Sender Interface
The `p2p.Sender` interface requires:
- `SendRequest(ctx, nodeIDs, requestID, request) error`
- `SendResponse(ctx, nodeID, requestID, response) error`
- `SendError(ctx, nodeID, requestID, errorCode, errorMessage) error`
- `SendGossip(ctx, config p2p.SendConfig, msg) error`
### p2p.Network Methods
- `Request(ctx, nodeID, requestID, deadline, request)` - NOT AppRequest
- `Response(ctx, nodeID, requestID, response)` - NOT AppResponse
- `Gossip(ctx, nodeID, gossipBytes)` - NOT AppGossip
### warp Package Types
- `NewUnsignedMessage(networkID uint32, sourceChainID ids.ID, payload []byte)` - takes ids.ID directly
- `Validator.NodeID` is `ids.NodeID` - NOT []byte
## Module Structure
```
/home/z/work/lux/evm/
├── plugin/evm/ # Main VM implementation
├── core/ # Core blockchain logic
├── consensus/ # Consensus engine (dummy for Lux)
├── eth/ # Ethereum protocol implementation
├── miner/ # Block building and mining
├── precompile/ # Precompiled contracts and warp
├── network/ # P2P networking
├── params/ # Chain configuration
├── scripts/ # Build and test scripts
└── warp/ # Cross-chain messaging
```
## Current Build Status
**✅ ALL TESTS PASSING - v0.8.18**
All 62 test packages pass. Key fixes:
1. **PQ Crypto Precompiles** - ML-DSA, SLH-DSA, ML-KEM integrated via geth v1.16.64
2. **Import Cycle Fixed** - precompiles v0.1.2 has no geth/core/vm dependency
3. **Bind Tests Fixed** - All ABI binding tests now pass
### Package Versions (2025-12-25)
| Package | Version | Status |
|---------|---------|--------|
| evm | v0.8.18 | ✅ All tests pass |
| geth | v1.16.64 | ✅ With PQ precompiles |
| precompiles | v0.1.2 | ✅ Standalone |
| crypto | v1.17.27 | ✅ All tests pass |
| node | v1.22.64 | ✅ Builds clean |
### Post-Quantum Crypto Precompiles
LP-aligned addresses (P=2 for PQ/Identity family):
- **ML-DSA** (FIPS 204) - Lattice-based signatures - `0x12202`
- **SLH-DSA** (FIPS 205) - Hash-based signatures - `0x12203`
- **ML-KEM** (FIPS 203) - Key encapsulation - `0x12201`
## EVM Upgrade Defaults (2026-03-29)
All EVM network upgrades are enabled at genesis (timestamp 0) by default:
- **EVMTimestamp** = 0 (always)
- **DurangoTimestamp** = 0 (Shanghai: PUSH0, warm coinbase)
- **EtnaTimestamp** = 0 (Cancun: MCOPY, TSTORE, TLOAD, BLOBHASH, BLOBBASEFEE)
- **FortunaTimestamp** = 0
- **GraniteTimestamp** = 0
`EVMDefaultChainConfig` also sets `ShanghaiTime` and `CancunTime` at 0 with
`BlobScheduleConfig` so geth's jump table selects the full Cancun instruction set.
The mapping: Durango -> Shanghai, Etna -> Cancun. These are set in `SetEthUpgrades()`.
**Stateful precompiles** (nativeminter, feemanager, etc.) are NOT enabled in the
default config because they require explicit permission configuration (admin
addresses). Call `SetAllGenesisPrecompiles()` on the extras config to enable them.
**Genesis precompile activation**: `ApplyPrecompileActivations` now runs at genesis
(parentTimestamp=nil). Previously it returned early, preventing genesis precompile
state from being written. The fix ensures deterministic genesis because precompile
configs are part of the chain config.
## C-Chain Tx-Fee Routing — RewardManager → DAO Gov Safe (LIVE PATH)
**Owner decision (supersedes the 50/50 below): 100% of every C-Chain tx fee accrues
to the chain's DAO Gov Safe, DAO-governed and redirectable on-chain.** This decouples
*collection* from *policy* — the DAO later decides burn / validators / treasury via
governance, on-chain, without another node upgrade. Real money on a 2T supply — staged
devnet→testnet→mainnet, never touch mainnet fee config without staged proof + owner go.
### Mechanism — existing RewardManager precompile, NO new fee-path code
The routing is stock C-Chain machinery, activated by config:
- `rewardmanager` precompile at **0x10205** (`precompile/contracts/rewardmanager/`).
- Activated via **precompileUpgrades** (`upgrade.json`) at a dated timestamp (coordinated
fork, NOT a genesis edit), gated so mainnet is inert until set.
- `initialRewardConfig.rewardAddress = <DAO Gov Safe>` → `StoreRewardAddress`.
`adminAddresses = [<DAO Gov Safe>]` → the DAO can call `setRewardAddress` on-chain
anytime (the "continually governed / redirectable" requirement; role-gated at
`contract.go:195` — non-admin callers revert).
- Routing path (all existing production code): `GetCoinbaseAt`
(`core/blockchain_reader.go:411`) reads `GetStoredRewardAddress` → sets
`header.Coinbase = reward address` → `core/state_transition.go:546` `creditTxFee`
credits the **full** fee to the coinbase. So 100% of the fee lands at the DAO Safe.
- The 50/50 `creditTxFee`/`FeeSplitTimestamp` code (below) stays committed but **DORMANT**
(FeeSplitTimestamp nil ⇒ legacy full-fee-to-coinbase branch), so it is transparent to
RewardManager and available if the DAO ever wants an in-protocol split.
### Proven (in-process, production code paths)
`core/reward_manager_routing_test.go` — `TestRewardManagerRoutesFeesToDAOSafe`: with
RewardManager active + reward = Lux DAO Gov Safe `0x8E29b816…`, the production
`GetCoinbaseAt` returns the DAO Safe (NOT blackhole); a **real `setRewardAddress` tx
from the admin** redirects routing to a new address live (observed via `GetCoinbaseAt`).
Combined with `TestCreditTxFeeLegacy` (full fee → coinbase, FeeSplit off) this proves
100% → reward address + on-chain DAO redirect. RewardManager's own suite
(`precompile/contracts/rewardmanager/`) covers role-gating + storage.
### Verified end to end on a multi-node network
Activation, routing and redirect were exercised on a running 5-node network with
an unmodified node and evm build — RewardManager is already present, so this is a
config change and not a rebuild.
- **Activation**: append `rewardManagerConfig` to `precompileUpgrades` with
`initialRewardConfig.rewardAddress` set to the chain's DAO Safe and
`adminAddresses` to the address that will govern it, then restart the fleet to
load it.
- **100% routing**: every tx fee accrues to the reward address; the blackhole
address stays flat.
- **On-chain redirect**: `setRewardAddress` from an admin moves accrual to the new
address from the next block, and back again — the DAO keeps control without a
node upgrade.
- **Determinism**: the reward balance and the state root agree across every node
at the same height, which is what makes this a consensus change rather than a
local accounting one.
### Per-chain DAO reward addresses (read canonical from `~/work/lux/standard/deployments/org-safes/`)
- **Lux C-Chain 96369** → DAO Gov Safe `0x8E29b816c6C35b13cE1ff68D33E245C2bda8ac3D`
- **Zoo 200200** → DAO Safe `0x229599f227231d8C90fcF1a78589F5DC4b7A6962`
- **Hanzo / Pars** → DAO Safes not yet in org-safes/ — STAGE before activation.
Deployable `upgrade.json` templates: `~/work/lux/standard/deployments/rewardmanager/`
(`lux-96369.json`, `zoo-200200.json`; `blockTimestamp` sentinel 9999999999 = inert until
the operator sets the coordinated fork time).
### How the upgrade schedule reaches the node
The C-Chain upgrade schedule is a file at `<chain-config-dir>/C/upgrade.json`. An
operator delivering it by ConfigMap copies the key into that path at boot; the
node reads it on start, so a schedule change is a restart and not a rebuild.
## Tx-Fee 50/50 Split — burn + staking-reward fold (DORMANT option, NOT the live path)
> Superseded as the live behavior by the RewardManager DAO routing above. This code is
> committed but gated OFF (`FeeSplitTimestamp` nil everywhere) and kept as a ready option
> if the DAO ever chooses an in-protocol burn+stake split. The P-Chain fold-in was NOT
> built (design only). Original design follows.
Owner decision (superseded): every C-Chain transaction fee is divided **50% true burn**
(real supply reduction) + **50% folded into the P-Chain staking reward** (validators'
returns grow from fee flow, unified into the native reward payout — NOT a separate
C-Chain coinbase stream).
### Current reality this replaces
The C-Chain credits the **entire** fee (base fee + tip, EVM-mode accounting) to
`block.Coinbase`, which defaults to the blackhole `0x0100…0000`
(`core/state_transition.go` fee-credit site; coinbase forced to blackhole by
`GetCoinbaseAt`). Nothing is burned; the fee just piles up in an uncontrolled EOA
(~3,778 LUX stuck there). The intended split was never built. Note: geth's own
`core/state_transition.go:569` is a *different* path used only by geth-direct
chains — the C-Chain runs **evm's vendored** `core/state_transition.go`, so the
split lives there.
### Mechanism (the ONE seam, deterministic, config-gated)
Single fee-disbursement site: `evm/core/state_transition.go` `execute()`. The
per-tx credit is delegated to `creditTxFee` (`evm/core/fee_split.go`):
- **Gate** — `extras.ChainConfig.FeeSplitTimestamp` (a *standalone* fee-routing
fork, sibling to `AllowFeeRecipients`; intentionally NOT in the `NetworkUpgrades`
opcode-fork chain — it is orthogonal to EVM/Durango/Quasar/… and has no ordering
relationship to them). `IsFeeSplit(time)` gates activation. A compatibility check
(`checkConfigCompatible`) freezes the activation time once effective, so a node
cannot silently reschedule it and fork.
- **Split** — `fee` = `gasUsed × effectiveGasPrice` (a consensus value):
- `rewardShare = fee >> 1` (floor(fee/2)) → credited to `extras.FeeRewardVault`
(`0x0100…0002`), a protocol-owned account that holds real, tracked balance.
- `burnShare = fee − rewardShare` (ceil(fee/2)) → **credited to no account**.
Never re-crediting it removes it from the account trie: the sum of all balances
(the supply) drops by exactly `burnShare`. This is a **true burn**, the same
mechanism as the EIP-1559 base-fee burn — NOT a transfer to a dead EOA (which
leaves supply unchanged). The odd wei on an odd fee goes to the burn.
- **Legacy path unchanged** — when `FeeSplit` is inactive, the full fee goes to the
coinbase exactly as before.
**Determinism**: `fee` is a consensus value; the split is pure uint256 integer math
(one right shift) to a fixed address — every validator computes an identical
post-state, so the handler cannot fork the chain. A running multi-node net that
keeps finalizing *is* the determinism proof.
### Conservation invariant
Per tx (fee F): `Δ(Σ balances) = −burnShare`, `Δ(vault) = +rewardShare`,
`rewardShare + burnShare = F`. Whole-system, after the vault is atomically exported
to P-Chain and paid to validators (a *move*, not a mint):
`total_supply_after = total_supply_before − Σ burnShare`. The reward half is
supply-neutral (redistributed); the curve-based staking mint is orthogonal and
unchanged. **No mint for the fee half ⇒ structurally impossible to double-mint.**
### Implemented + PROVEN on real block execution (C-Chain half)
Files: `evm/core/fee_split.go` (helper), `evm/core/state_transition.go` (seam),
`evm/params/extras/config.go` (`FeeRewardVault`, `FeeSplitTimestamp`, `IsFeeSplit`,
compat, description). Tests (all pass, `CGO_ENABLED=0 go test ./core/ ./params/extras/`):
- `core/fee_split_test.go` — split math, conservation, odd-wei rule, determinism
(identical post-state root), legacy-path preserved.
- `params/extras/fee_split_test.go` — activation-gate boundary + compat freeze.
- `core/fee_split_block_test.go` — **end-to-end on produced+accepted blocks**:
10 LUX genesis, 5 transfers @ 21000 gas × 225 gwei ⇒
- blackhole `0x0100…0000` balance = **0** (stuck behavior replaced)
- vault `0x0100…0002` = **11,812,500,000,000,000 wei** (exactly 50%)
- **BURNED = 11,812,500,000,000,000 wei** — supply 10e18 → 9.9881875e18 (real decrease)
- burn == vault (exact 50/50 on even fees); sender debit == value + fee. Conservation exact.
### P-Chain fold-in (designed; scoped — the larger, consensus-critical lift)
The reward half must reach P-Chain validators as staking reward. Chosen model **R1
(move, not mint)** — simplest correct, conservation by construction:
1. **Periodic atomic export** of the `FeeRewardVault` balance C→P (Avalanche/Lux
shared-memory export/import — the native conservation-exact value-move; today
exports are user-initiated, so a *system-triggered* epoch export is new machinery:
a hook in the C-Chain `FinalizeAndAssemble`/atomic path emitting an export UTXO to
a canonical P-Chain fee-reward-pool address, draining the vault).
2. **P-Chain fee-reward pool** — new persisted state (`vms/platformvm/state`:
`Get/SetFeeRewardPool`) funded by the import. (New persisted field ⇒ state
serialization/migration ⇒ consensus-critical; must be gated + migration-tested.)
3. **Distribution** — at `RewardValidatorTx` commit
(`vms/platformvm/txs/executor/proposal_tx_executor.go` `rewardValidatorTx`, ~line
285 where `reward := validator.PotentialReward`), add the validator's pro-rata
share of the pool (deterministic: by stake-weight over the accrual epoch) to the
payout UTXO — unifying it into the native staking-reward path with no second mint.
4. **Supply sync** — decrement P-Chain `currentSupply` by the epoch's burn `B` so the
reward curve (`vms/platformvm/reward/calculator.go`, driven by
`remainingSupply = SupplyCap − currentSupply`) sees true post-burn supply.
Rejected alternative **R2 (burn-on-C + re-mint-on-P)**: same net supply, but needs a
trustless cross-chain counter read and an exact burn==mint match (a double
supply-touch = double-mint/loss risk). Violates "no double-mint, simplest correct".
> Interpretation flag for owner: "unifies into the staking-reward **mint**" is
> realized as unifying into the staking-reward **payout**, funded by moved fees, not
> a second mint — the conservation-safe reading. Confirm before P-Chain build.
### Rollout (gated — do NOT skip a stage)
1. **Devnet 96367** — build the C-Chain plugin via **ARC CI** (never local docker),
deploy to the 5-validator devnet, set `feeSplitTimestamp` in the C-Chain upgrade
config to a near-future time. Send txs; verify via RPC on **all 5** nodes:
`eth_getBalance(0x0100…0002)` grows by Σfee/2; `eth_getBalance(0x0100…0000)` stays
flat; per-tx `gasUsed`+effective price ⇒ expected burn; identical state root /
block hash at each height across all 5 (determinism); capture before/after + tx
hashes. (Unit + block-level tests above already prove the invariants deterministically.)
2. **Testnet** — same config, longer soak; confirm no fork, supply-decrease trend.
3. **Mainnet 96369** — ONLY after owner go: add `feeSplitTimestamp` (future) to the
C-Chain upgrade config via a dated fork tx (not a genesis edit). Separate owner
decision: sweep vs. burn the ~3,778 LUX already stuck at `0x0100…0000`.
Guardrails honored: luxfi packages only; patch-semver; ARC CI builds (local builds
here were unit-tests only); deterministic; supply conserved exactly.
## Key Implementation Details
### Context Management
- VM uses `context.Context` instead of consensus.Context struct
- Access consensus data via helper functions:
- `consensus.GetChainID(ctx)`
- `consensus.GetNetworkID(ctx)`
- `consensus.GetNodeID(ctx)`
- `consensus.GetLogger(ctx)`
- `consensus.GetWarpSigner(ctx)`
### Interface Compatibility
- Block implements both `chain.Block` and `consensuschain.Block`
- BuildBlock returns `consensuschain.Block` for compatibility
- AppSender uses `set.Set[ids.NodeID]` for node sets
- Version uses `consensus/version.Application` not node's version
### Package Dependencies
**NEVER use these packages:**
- ❌ `github.com/ethereum/go-ethereum` - Use `github.com/luxfi/geth`
- ❌ `github.com/ava-labs/*` - Use `github.com/luxfi/*`
- ❌ `github.com/luxfi/node v1.16.x` - Use v1.13.4 for compatibility
**Always use:**
- ✅ `github.com/luxfi/consensus` - Local consensus package
- ✅ `github.com/luxfi/crypto` - Local crypto package
- ✅ `github.com/luxfi/warp` - Local warp package
- ✅ `github.com/luxfi/geth` - Our Ethereum fork
### Build Commands
```bash
cd /home/z/work/lux/evm
go build ./... # Currently fails due to interface issues
go test ./... # Will work after build issues are resolved
```
### Common Issues and Fixes
1. **Version Requirements**
- Always use node v1.16.15 or later Lux versions
- Never use ava-labs packages
- Check go.mod replace directives
2. **Interface Compatibility**
- Block needs SetStatus method (even if no-op)
- BuildBlock must return consensuschain.Block
- Context is context.Context, not a struct
3. **Missing Metrics**
- Metrics registration is currently disabled (TODO)
- Will be re-enabled when consensus context supports it
4. **ID Type Conversions**
```go
// Convert between node's IDs and consensus IDs
func nodeIDToConsensus(id nodeids.NodeID) ids.NodeID {
var consensusID ids.NodeID
copy(consensusID[:], id[:])
return consensusID
}
```
## Testing
- Run with `-short` flag for quick tests
- 28 packages with tests, 14 without (expected)
- Tests will pass after build issues are resolved
## Important Notes
- This module is actively being migrated from evm
- Maintains backwards compatibility with existing Lux L2 chains
- Uses single validator POA for development (k=1 consensus)
- Major refactoring needed to reconcile ID type differences between packages
## Documentation Status (2025-11-12)
### ✅ Documentation Enhanced
Successfully created comprehensive documentation for the Lux EVM implementation.
#### Documentation Created
1. **Enhanced index.mdx** (`/Users/z/work/lux/evm/docs/content/docs/index.mdx`)
- Complete EVM overview and architecture
- Key differences from standard EVM
- Smart contract deployment guide
- Gas optimization strategies
- Comprehensive API reference (eth, web3, net, admin, debug, validators, warp)
- Integration with Lux blockchain
- Performance tuning configuration
- Security best practices
- Troubleshooting guide
- Migration guides from Ethereum and C-Chain
#### Documentation Features Added
- **Architecture Section**: VM, Core, Precompiles detailed
- **API Reference**: 40+ JSON-RPC endpoints documented
- **Code Examples**: JavaScript, Solidity, configuration files
- **Performance Guide**: State management, transaction pool, benchmarking
- **Security Guide**: Access control, gas limits, cross-chain security
- **Troubleshooting**: Common issues and debug commands
- **Migration Guides**: From Ethereum and C-Chain
#### Build Status
- ✅ Documentation site builds successfully
- ✅ Next.js 16.0.1 with Turbopack
- ✅ Static site generation working
- ✅ All pages render correctly
### Completeness Score: 95/100
#### What's Complete
- ✅ Overview and introduction (100%)
- ✅ Architecture documentation (100%)
- ✅ API reference (100%)
- ✅ Smart contract deployment (100%)
- ✅ Gas optimization (100%)
- ✅ Integration guide (100%)
- ✅ Performance tuning (100%)
- ✅ Security considerations (100%)
- ✅ Troubleshooting (100%)
- ✅ Migration guides (100%)
#### What Could Be Added (5%)
- Additional code examples for each precompile
- Detailed tutorials for specific use cases
- Video documentation links
- Interactive API explorer
- Benchmark results and graphs
### Precompiled Contracts Available
1. **DeployerAllowList** - Contract deployment permissions
2. **FeeManager** - Dynamic fee configuration
3. **NativeMinter** - Native token minting
4. **RewardManager** - Validator rewards
5. **TxAllowList** - Transaction permissions
6. **Warp** - Cross-chain messaging
7. **PQCrypto** - Post-quantum cryptography
8. **Quasar** - Advanced consensus features
## Readonly Database Support (2025-11-22)
### Status: ✅ VERIFIED WORKING
Successfully implemented and verified readonly database access for legacy PebbleDB databases.
### Key Changes
1. **Database Factory Fix** (`~/work/lux/database/factory/pebbledb.go`)
- Added `readOnly bool` parameter to `newPebbleDB()` function
- Passes readonly flag to `pebbledb.New()` instead of hardcoded `false`
- Committed to database repo (commit aaee95a)
2. **EVM Integration** (`~/work/lux/evm/go.mod`)
- Added replace directive: `replace github.com/luxfi/database => ../database`
- Enables EVM to use local database with readonly fix
3. **Test Verification** (`test-readonly-db.go`)
- Successfully opens 7.1GB legacy PebbleDB in readonly mode
- Can read all keys without write access
- No corruption or modification risk
### Legacy Database Details
- **Location**: `/Users/z/work/lux/state/chaindata/lux-mainnet-96369/db/pebbledb`
- **Size**: 7.1GB (751 files)
- **Blockchain ID**: `dnmzhuf6poM6PUNQCe7MWWfBdTJEnddhHRNXz2x7H6qSmyBEJ`
- **Chain ID**: 96369
- **Purpose**: Legacy evm data for regenesis export
### Correct Migration Approach
**IMPORTANT**: Do NOT manually migrate database files between formats.
The proper workflow using lux-cli and VM interfaces:
1. **Deploy L2**: Use `lux l2 create` and `lux l2 deploy` to create a Net
2. **Export Data**: Use VM's exporter interface via lux-cli export commands
3. **Import to C-Chain**: Use VM's importer interface via `lux migrate import`
### VM Importer/Exporter Interface
Each VM must implement:
- **Exporter Interface**: Serialize blockchain state to portable format
- **Importer Interface**: Deserialize and load blockchain state
- **Format**: VM-agnostic, standardized data structure
### Current lux-cli Status
The `lux migrate` command exists but is incomplete:
- `lux migrate prepare` - Placeholder, needs migration-tools implementation
- `lux migrate import` - Not yet implemented
- `lux migrate bootstrap` - Partial implementation
- `lux migrate validate` - Not implemented
### Implementation Needed
To complete the migration workflow:
1. **VM Exporter** (`plugin/evm/export.go`):
```go
func (vm *VM) Export(ctx context.Context) ([]byte, error) {
// Export blockchain state to standardized format
// Include: genesis, blocks, state trie, metadata
}
```
2. **VM Importer** (`plugin/evm/import.go`):
```go
func (vm *VM) Import(ctx context.Context, data []byte) error {
// Import blockchain state from standardized format
// Validate and load into C-Chain database
}
```
3. **lux-cli Migration Tools**:
- Implement `migration-tools/migrate.go` that calls VM exporter/importer
- Complete `lux migrate prepare` to use VM interfaces
- Implement `lux migrate import` for C-Chain import
### Architecture Principles
- ✅ Use VM's native export/import interfaces
- ✅ Let each VM handle its own data format
- ✅ Generic migration via standardized interfaces
- ❌ NO manual database file copying
- ❌ NO format-specific conversion scripts
- ❌ NO direct database manipulation outside VM
### Current Implementation Status (2025-11-23)
**✅ Fixed:**
1. Import paths updated to use `luxfi/geth` instead of `go-ethereum`
2. Import paths updated to use `luxfi/ids` instead of `luxfi/node/ids`
3. Chainmigrate interfaces.go fixed with correct imports
4. Duplicate ChainMigrator definition resolved (renamed struct to Migrator)
5. Broken implementation files disabled (.go.broken extension)
6. Package consistency verified - all luxfi packages used correctly
**📦 Required Package Imports:**
- Ethereum types: `github.com/luxfi/geth` (NOT go-ethereum)
- IDs: `github.com/luxfi/ids` (NOT luxfi/node/ids)
- Logging: `github.com/luxfi/log` (ALWAYS use luxfi/log for consistency)
- Chainmigrate: `github.com/luxfi/node/chainmigrate`
**🔄 In Progress:**
- Fixing exporter.go to match actual VM structure
- Need to access NetworkID from chainCtx, not config
- Need to find correct method for GetTd (total difficulty)
- Need to create proper error types (ErrMissing, ErrNotImplemented)
- Need to convert uint256.Int to *big.Int for balance
**⏳ Next Steps:**
1. Complete exporter.go fixes to compile successfully
2. Create importer.go implementation
3. Test export functionality with readonly database
4. Create migration-tools in lux-cli that use these interfaces
5. Complete `lux migrate` command implementation
6. Test full export → import workflow
7. Verify C-Chain can serve exported data via RPC
## lux-cli Integration (2025-11-23)
### ✅ COMPLETE: ChainExporter integrated with lux-cli
**Integration Architecture:**
```
lux-cli migrate
↓
migration-tools/migrate (symlink)
↓
node/cmd/chainmigrate/chainmigrate (binary)
↓
node/chainmigrate/interfaces.go (ChainExporter interface)
↓
evm/plugin/evm/exporter.go (implementation)
```
**CLI Tool Location:**
- Binary: `/Users/z/work/lux/node/cmd/chainmigrate/chainmigrate`
- Symlink: `/Users/z/work/lux/cli/migration-tools/migrate`
**Usage via lux-cli:**
```bash
lux migrate prepare \
--source-db ~/.node/chaindata/subnet-96369/db/pebbledb \
--output ./mainnet-migration \
--network-id 96369 \
--validators 5
```
**Direct Binary Usage:**
```bash
node/cmd/chainmigrate/chainmigrate \
--src-pebble /path/to/source/db \
--dst-leveldb /path/to/dest/db \
--chain-id 96369 \
--start-block 0 \
--end-block 1000 \
--batch-size 100
```
**Features:**
- ✅ Uses luxfi/log for logging
- ✅ Uses luxfi/geth for Ethereum types
- ✅ Uses ChainExporter interface
- ✅ Configurable batch sizes
- ✅ Block range selection
- ✅ Export-only and import-only modes
**Integration Tests:** All passing ✅
**Next Steps:**
1. Complete full EVM integration (initialize VM with readonly DB)
2. Implement importer.go for destination chain
3. Test end-to-end export → import workflow
## Integration Approach - RPC-BASED (2025-11-23)
### ✅ RPC Control: lux-cli uses netrunner + RPC only
**Previous Approaches (WRONG):** ❌
1. Created ad-hoc cmd/chainmigrate binary in node repo
2. Used symlinks to bridge binaries
3. Used ChainExporter interface as Go import in lux-cli
4. Direct Go package dependencies
**Current Approach (CORRECT):** ✅
- **lux-cli**: RPC client for fleet control
- **netrunner**: Deploys and manages node fleet
- **EVM MigrateAPI**: RPC endpoints for export/import
- NO Go package imports between cli and evm
- Pure RPC communication only
**Architecture:**
```
lux-cli (RPC client)
↓ HTTP JSON-RPC calls
netrunner (fleet manager)
↓ deploys nodes
EVM node (with MigrateAPI)
↓ migrate_getBlocks
↓ migrate_importBlocks
Database (PebbleDB/LevelDB)
```
**Implementation:**
```go
// lux-cli/cmd/migratecmd/utils.go
func runMigration(sourceRPC, destRPC string, chainID int64) error {
// Get current block via RPC
blockNum, err := getCurrentBlock(ctx, sourceRPC)
// Call migrate_getBlocks via RPC (no Go imports!)
req := &RPCRequest{
Method: "migrate_getBlocks",
Params: []interface{}{0, blockNum, 100},
}
callRPC(sourceRPC, req, &blocks)
// Import via RPC to destination
req = &RPCRequest{
Method: "migrate_importBlocks",
Params: []interface{}{blocks},
}
callRPC(destRPC, req, &result)
}
```
**EVM RPC Endpoints:**
```go
// plugin/evm/api_migrate.go
type MigrateAPI struct {
vm *VM
}
// RPC: migrate_getChainInfo
func (api *MigrateAPI) GetChainInfo() (*ChainInfo, error)
// RPC: migrate_getBlocks (batch, max 100 blocks)
func (api *MigrateAPI) GetBlocks(start, end, limit uint64) ([]*BlockData, error)
// RPC: migrate_streamBlocks (streaming via channels)
func (api *MigrateAPI) StreamBlocks(start, end uint64) (chan *BlockData, chan error)
// RPC: migrate_importBlocks
func (api *MigrateAPI) ImportBlocks(blocks []*BlockData) (int, error)
```
**Benefits:**
- True fleet control via RPC (lux-cli controls remote nodes)
- No Go package coupling between repos
- Can control nodes anywhere (local, remote, cloud)
- Netrunner handles deployment, lux-cli handles orchestration
- Works with any number of nodes
- Clean separation: deploy vs control vs execution
**Workflow:**
1. Deploy source EVM with netrunner (readonly DB):
```bash
netrunner engine start evm-source --data-dir=/readonly/db
```
2. Deploy destination C-Chain with netrunner:
```bash
netrunner engine start c-chain
```
3. Run migration via lux-cli (auto-discovers RPC endpoints):
```bash
lux migrate prepare
# RPC endpoints discovered from netrunner at runtime
# Source: ext/bc/<blockchain-id>/rpc (old 96369 net)
# Dest: ext/bc/C/rpc (C-Chain)
# Internal RPC uses port 9630 (not 9650)
# Hosts/ports known at runtime, not hardcoded
```
**RPC Path Format:**
- **C-Chain**: `ext/bc/C/rpc` (uses C alias)
- **Old 96369 Net**: `ext/bc/dnmzhuf6poM6PUNQCe7MWWfBdTJEnddhHRNXz2x7H6qSmyBEJ/rpc` (uses blockchain ID)
## MigrateAPI Registration Status (2025-11-23)
### ✅ COMPLETE: MigrateAPI Registered with EVM Node
The MigrateAPI has been successfully registered with the EVM node and is now available via RPC.
**Changes Made:**
1. Added `MigrateAPIEnabled` config flag to `plugin/evm/config/config.go`
2. Set default to `true` in `plugin/evm/config/default_config.go`
3. Registered MigrateAPI in `plugin/evm/vm.go` (similar to WarpAPI)
4. Fixed type errors in `plugin/evm/api_migrate.go`:
- Changed `Transactions` field from `[]types.Transaction` to `[]*types.Transaction`
- Fixed `WithBody` call to use `types.Body` directly
**Available RPC Methods:**
- `migrate_getChainInfo` - Returns blockchain metadata (chain ID, network ID, current height, etc.)
- `migrate_getBlocks` - Exports blocks in batches (max 100 blocks per call)
- `migrate_streamBlocks` - Streams blocks via channels (not yet exposed via JSON-RPC)
- `migrate_importBlocks` - Imports blocks to the blockchain
**Configuration:**
```json
{
"migrate-api-enabled": true // Default: true
}
```
**Testing Status:**
- ✅ EVM plugin builds successfully
- ✅ MigrateAPI properly registered in RPC handler
- ✅ CLI commands (export-data, import-data) implemented
- ⏳ End-to-end RPC testing pending
**Next Steps:**
1. Deploy EVM node with readonly database
2. Test `migrate_getChainInfo` RPC call
3. Test `migrate_getBlocks` with various block ranges
4. Test full export → import workflow via lux-cli
5. Verify imported data on destination chain
---
## Properties this EVM holds
**No beacon chain.** Lux networks run their own consensus, so the Cancun-era
EIP-4844 beacon fields are not part of a Lux EVM block and the blob-fee path is
not reachable on a fork that does not enable it.
**A genesis file has to reproduce the chain's block 0.** For a chain being
brought up against existing history, the genesis hash computed from genesis.json
must equal the original block 0 hash exactly; import checks it rather than
assuming it, because a genesis that differs describes a different chain.
**Importing history is not a read-only operation.** `admin_importChain` moves the
chain database underneath everything reading it, so it runs against a node that
is not concurrently serving those reads.
## Post-Quantum Cryptography Precompiles (2025-12-24)
### Status: ✅ COMPLETE - All PQ Crypto Precompiles Implemented and Tested
Lux EVM includes native precompiled contracts for NIST FIPS 203-205 post-quantum cryptography algorithms.
### Precompile Addresses (LP-aligned)
| Precompile | Address | Description |
|------------|---------|-------------|
| **PQCrypto Unified** | `0x0000000000000000000000000000000000012201` | All PQ crypto operations (P=2 PQ/Identity) |
| **ML-DSA Verify** | `0x0000000000000000000000000000000000012202` | Dedicated ML-DSA verification |
| **SLH-DSA Verify** | `0x0000000000000000000000000000000000012203` | Dedicated SLH-DSA verification |
### Gas Costs (Per Mode)
#### ML-DSA Signature Verification (FIPS 204)
| Mode | Security | Mode Byte | Gas Cost |
|------|----------|-----------|----------|
| ML-DSA-44 | Level 2 | `0x44` | **75,000** |
| ML-DSA-65 | Level 3 | `0x65` | **100,000** |
| ML-DSA-87 | Level 5 | `0x87` | **150,000** |
#### ML-KEM Key Encapsulation (FIPS 203)
| Mode | Security | Mode Byte | Encap Gas | Decap Gas |
|------|----------|-----------|-----------|-----------|
| ML-KEM-512 | Level 1 | `0x00` | **6,000** | **6,000** |
| ML-KEM-768 | Level 3 | `0x01` | **8,000** | **8,000** |
| ML-KEM-1024 | Level 5 | `0x02` | **10,000** | **10,000** |
#### SLH-DSA Signature Verification (FIPS 205)
| Mode | Hash | Security | Mode Byte | Gas Cost |
|------|------|----------|-----------|----------|
| 128s | SHA-256 | Level 1 | `0x00` | **50,000** |
| 128f | SHA-256 | Level 1 | `0x01` | **75,000** |
| 192s | SHA-256 | Level 3 | `0x02` | **100,000** |
| 192f | SHA-256 | Level 3 | `0x03` | **150,000** |
| 256s | SHA-256 | Level 5 | `0x04` | **175,000** |
| 256f | SHA-256 | Level 5 | `0x05` | **250,000** |
| 128s | SHAKE | Level 1 | `0x10` | **50,000** |
| 128f | SHAKE | Level 1 | `0x11` | **75,000** |
| 192s | SHAKE | Level 3 | `0x12` | **100,000** |
| 192f | SHAKE | Level 3 | `0x13` | **150,000** |
| 256s | SHAKE | Level 5 | `0x14` | **175,000** |
| 256f | SHAKE | Level 5 | `0x15` | **250,000** |
### Implementation Files
```
precompile/contracts/
├── mldsa/
│ ├── contract.go # ML-DSA precompile (182 lines)
│ ├── contract_test.go # 334 lines, 10 test cases
│ └── module.go # Registration
└── pqcrypto/
├── contract.go # Unified PQ precompile (382 lines)
├── contract_test.go # 234 lines, 20 test cases
├── module.go # Registration
└── config.go # Configuration
```
### Mode Byte Encoding
**Critical**: Precompile mode bytes differ from library internal values:
```go
// Precompile mode bytes (used in input)
ModeMLDSA44 uint8 = 0x44 // Library: mldsa.MLDSA44 = 0
ModeMLDSA65 uint8 = 0x65 // Library: mldsa.MLDSA65 = 1
ModeMLDSA87 uint8 = 0x87 // Library: mldsa.MLDSA87 = 2
```
The precompile implementation converts between these formats in the `Run()` method.
### Function Selectors (PQCrypto Unified)
| Selector | Bytes | Operation |
|----------|-------|-----------|
| `"mlds"` | `0x6d6c6473` | ML-DSA Verify |
| `"encp"` | `0x656e6370` | ML-KEM Encapsulate |
| `"decp"` | `0x64656370` | ML-KEM Decapsulate |
| `"slhs"` | `0x736c6873` | SLH-DSA Verify |
### Test Status
```
=== ML-DSA Tests ===
TestMLDSAVerify_ValidSignature PASS
TestMLDSAVerify_InvalidSignature PASS
TestMLDSAVerify_WrongMessage PASS
TestMLDSAVerify_InputTooShort PASS
TestMLDSAVerify_EmptyMessage PASS
TestMLDSAVerify_LargeMessage PASS
TestMLDSAVerify_GasCost PASS
TestMLDSAPrecompile_Address PASS
=== PQCrypto Tests ===
TestPQCryptoPrecompile PASS
TestMLDSAVerify PASS
TestMLKEMEncapsulateDecapsulate PASS
TestSLHDSAVerify PASS
TestGasCalculation (15 subtests) PASS
Total: 20 tests, 0 failures
```
### Documentation
Full specification documented in:
- **LP-3520**: Post-Quantum Cryptography Precompile Implementation Guide
- **LP-4200**: Post-Quantum Cryptography Suite for Lux Network
- **LP-3502**: ML-DSA Post-Quantum Signature Precompile
### Dependencies
- `github.com/luxfi/crypto/mldsa` - ML-DSA implementation (FIPS 204)
- `github.com/luxfi/crypto/mlkem` - ML-KEM implementation (FIPS 203)
- `github.com/luxfi/crypto/slhdsa` - SLH-DSA implementation (FIPS 205)
- Backend: Cloudflare CIRCL (audited, FIPS compliant)
---
## Pluggable Backends + Auto-Import
### Backend selection (build tags)
| Build | Backend | Notes |
|-------|---------|-------|
| `go build ./plugin` | Go EVM (Block-STM) | Default. Pure Go. |
| `-tags gpu` | Go EVM + Metal GPU bridge | `core/parallel/gpu_bridge.go`. Requires working `luxfi/gpu` module. Currently broken upstream — MLX bindings (`mlx_zeros`, `mlx_from_slice_int64`, `mlx_floor`, `mlx_full`, etc.) are stubs in `~/work/luxcpp/mlx-c-api/mlx_c_api.c` returning NULL with mismatched signatures. Don't use until upstream lands real MLX impls. |
| `-tags cevm` | C++ EVM via cgo | `core/parallel/backend_cevm.go`. Real GPU path — links `~/work/luxcpp/cevm/build-phase5b/lib/{libevm,libevm-gpu-state,libevm-metal-hosts,libevm-kernel-metal}`. Requires `bash chains/evm/cevm/fetch-luxcpp.sh` to populate libs. |
| `-tags revm` | Rust EVM via FFI | `core/parallel/backend_revm.go`. Requires `librustc_revm.a` from `luxfi/revm`. |
`gpu_bridge.go:4` build tag is `cgo && darwin && gpu` (opt-in).
### `--import-chain-data` plumbing
luxd's `--import-chain-data=<path.rlp>` flag now flows through to the EVM plugin:
1. `node/config/config.go:2012-2028` config bridge injects the path into
`nodeConfig.ChainConfigs["C"].Config` JSON map.
2. EVM plugin reads `vm.config.ImportChainData` (declared in
`plugin/evm/config/config.go`, JSON key `import-chain-data`).
3. After `initializeChain()` in `plugin/evm/vm.go::Initialize()`, if
`ImportChainData != ""`, calls `importBlocksFromFile(chain, path,
persistAccepted)` from `plugin/evm/admin_api.go:220`. Same code path as
`admin_importChain` RPC.
4. Batches of 2,500 blocks. State trie committed every `CommitInterval`
(default 4,096) blocks; `acceptedBlockDB` updated atomically with each commit.
Canonical mainnet hashes/state root and required precompiles live in
`~/work/lux/genesis/LLM.md` "Canonical Requirements" — single source of truth.
### Build commands
```bash
cd ~/work/lux/evm
# Default: builds and installs the plugin at the canonical path
# (~/.lux/plugins/<EVMID> where EVMID = CB58(ids.ID{'e','v','m'}) per luxfi/constants.EVMID).
# Handles codesign on darwin in one shot. No hardcoded plugin paths, no aliases.
./scripts/build.sh
# Variants (build, then have luxd point its --plugin-dir at the chosen build):
./scripts/build.sh /tmp/evm-default # plain Go EVM (default)
GOFLAGS='-tags cevm' ./scripts/build.sh /tmp/evm-cevm # C++ backend (real GPU)
GOFLAGS='-tags revm' ./scripts/build.sh /tmp/evm-revm # Rust REVM backend
```
The plugin filename is the binary's location: luxd's chain manager looks up
`<plugin-dir>/<VMID>` and `constants.EVMID = ids.ID{'e','v','m'}` (CB58 =
`mgj786NP…`) is the canonical primary-network C-Chain ID. Same ID is used
for every EVM-based L2 chain in Lux. No second filename, no aliases.
### Known upstream gaps
- `luxfi/gpu@v0.30.0` MLX C bindings unimplemented — stubs in
`~/work/luxcpp/mlx-c-api/mlx_c_api.c`. Don't use `-tags gpu` until landed.
- `luxfi/accel@v1.0.7` API drift — `accel.VMSession`, `NewVMSession`,
`WithPriority`, `PriorityHigh` undefined. Blocks `chains/keyvm` and likely
other chains/*vm. Independent of EVM stack.
- `luxfi/coreth` is DEPRECATED per `coreth/DEPRECATED.md` — use this repo
(`luxfi/evm`) for all new C-Chain and L1 EVM work.