Update to latest warp, fixes for import

This commit is contained in:
2025-12-23 19:03:18 -08:00
parent 1323aa67ee
commit 054dcc96de
11 changed files with 158 additions and 99 deletions
+47
View File
@@ -952,3 +952,50 @@ lux amm swap \
**Location:** `plugin/evm/vm.go:590`
**Impact:** Snapshots not available during import
**Workaround:** Disable state sync for import node
## Cancun/BeaconRoot Disabled (2025-12-22)
### Status: ✅ FIXED - Lux Networks Do Not Use Ethereum Beacon Chain
Lux networks use their own consensus mechanism and do NOT use Ethereum's beacon chain. The following Cancun-era EIP-4844 fields are NOT required for Lux EVM blocks:
- `ExcessBlobGas` - NOT required
- `BlobGasUsed` - NOT required
- `ParentBeaconRoot` - NOT required
### Files Modified
1. **`consensus/dummy/consensus.go`** (lines 231-238)
- Removed mandatory beaconRoot check
- Removed mandatory excessBlobGas/blobGasUsed check
- Only rejects if BlobGasUsed > 0 (which shouldn't happen)
2. **`plugin/evm/block_verification.go`** (lines 136-144)
- Removed mandatory excessBlobGas check
- Removed mandatory blobGasUsed check
- Removed mandatory parentBeaconRoot check
- Only rejects if BlobGasUsed > 0
### Why This Matters
Historic blocks from Lux networks (e.g., Zoo chain 200200) were created before Cancun was even conceived. These blocks don't have:
- `ExcessBlobGas` field
- `BlobGasUsed` field
- `ParentBeaconRoot` field
Setting `cancunTime: null` in genesis doesn't help because the EVM code still checks these fields during block import via `admin.importChain`.
### Key Principle
**Lux networks are NOT Ethereum.** We use our own consensus (Snow/POA) and don't need Ethereum's beacon chain validators or blob transaction support.
### Testing Verification
Successfully imported 799 blocks from Zoo chain (200200) after these fixes:
```bash
curl -X POST http://127.0.0.1:9630/ext/bc/<zoo-id>/admin \
-d '{"jsonrpc":"2.0","id":1,"method":"admin.importChain","params":{"file":"/path/to/zoo-mainnet-200200.rlp"}}'
# Response:
{"jsonrpc":"2.0","result":{"success":true,"blocksImported":799,"heightAfter":799}}
```
+14 -4
View File
@@ -6,7 +6,8 @@ EVM_PATH := $(shell pwd)
# Load constants
GOPATH := $(shell go env GOPATH)
DEFAULT_PLUGIN_DIR := $(HOME)/.lux/plugins
DEFAULT_VM_ID := srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy
# VM ID for "Lux EVM" - computed as base58(sha256(pad32("Lux EVM")))
DEFAULT_VM_ID := ag3GReYPNuSR17rUP8acMdZipQBikdXNRKDyFszAysmy3vDXE
# Git info
EVM_COMMIT := $(shell git rev-parse HEAD 2>/dev/null || echo "unknown")
@@ -28,7 +29,7 @@ CGO_CFLAGS := -O2 -D__BLST_PORTABLE__
export CGO_ENABLED
export CGO_CFLAGS
.PHONY: all build build-node clean test lint install setup generate help
.PHONY: all build build-node clean test lint install link setup generate help
# Default target
all: build
@@ -44,7 +45,8 @@ help:
@echo " all Build EVM plugin (default)"
@echo " build Build EVM plugin to build/evm"
@echo " build-node Build standalone evm-node binary"
@echo " install Install EVM plugin to ~/.luxd/plugins"
@echo " install Install EVM plugin to ~/.lux/plugins (copy)"
@echo " link Link EVM plugin to ~/.lux/plugins/current (symlink)"
@echo " clean Clean build artifacts"
@echo " test Run all tests"
@echo " test-unit Run unit tests"
@@ -76,13 +78,21 @@ fix-deps:
go mod download
@echo "Dependencies fixed."
# Install to default plugin directory (with VM ID)
# Install to default plugin directory (copy binary)
install: build
@echo "Installing EVM plugin to $(DEFAULT_PLUGIN_DIR)"
@mkdir -p $(DEFAULT_PLUGIN_DIR)
@cp build/evm $(DEFAULT_PLUGIN_DIR)/$(DEFAULT_VM_ID)
@echo "Installed: $(DEFAULT_PLUGIN_DIR)/$(DEFAULT_VM_ID)"
# Link to plugins/current directory (symlink for development)
link: build
@echo "Linking EVM plugin to $(DEFAULT_PLUGIN_DIR)/current"
@mkdir -p $(DEFAULT_PLUGIN_DIR)/current
@rm -f $(DEFAULT_PLUGIN_DIR)/current/$(DEFAULT_VM_ID)
@ln -s $(EVM_PATH)/build/evm $(DEFAULT_PLUGIN_DIR)/current/$(DEFAULT_VM_ID)
@echo "Linked: $(DEFAULT_PLUGIN_DIR)/current/$(DEFAULT_VM_ID) -> $(EVM_PATH)/build/evm"
# Clean build artifacts
clean:
@echo "Cleaning build artifacts..."
+6 -11
View File
@@ -17,7 +17,6 @@ import (
"github.com/luxfi/evm/plugin/evm/vmerrors"
"github.com/luxfi/evm/utils"
"github.com/luxfi/geth/common"
"github.com/luxfi/geth/consensus/misc/eip4844"
"github.com/luxfi/geth/core/types"
"github.com/luxfi/geth/trie"
"github.com/luxfi/timer/mockable"
@@ -216,6 +215,8 @@ func (eng *DummyEngine) verifyHeader(chain consensus.ChainHeaderReader, header *
return consensus.ErrInvalidNumber
}
// Verify the existence / non-existence of excessBlobGas
// NOTE: Lux networks do NOT use Ethereum beacon chain.
// ParentBeaconRoot is ALWAYS optional and must be nil or empty if present.
cancun := chain.Config().IsCancun(header.Number, header.Time)
if !cancun {
switch {
@@ -227,16 +228,10 @@ func (eng *DummyEngine) verifyHeader(chain consensus.ChainHeaderReader, header *
return fmt.Errorf("invalid parentBeaconRoot, have %#x, expected nil", *header.ParentBeaconRoot)
}
} else {
if header.ParentBeaconRoot == nil {
return errors.New("header is missing beaconRoot")
}
if *header.ParentBeaconRoot != (common.Hash{}) {
return fmt.Errorf("invalid parentBeaconRoot, have %#x, expected empty", *header.ParentBeaconRoot)
}
if err := eip4844.VerifyEIP4844Header(chain.Config(), parent, header); err != nil {
return err
}
if *header.BlobGasUsed > 0 { // VerifyEIP4844Header ensures BlobGasUsed is non-nil
// Lux does NOT use Ethereum beacon chain or blob transactions
// Skip all Cancun-specific validation for historic block imports
// Only reject if blob gas was actually used (which shouldn't happen on Lux)
if header.BlobGasUsed != nil && *header.BlobGasUsed > 0 {
return fmt.Errorf("blobs not enabled on lux networks: used %d blob gas, expected 0", *header.BlobGasUsed)
}
}
+1 -1
View File
@@ -39,7 +39,7 @@ require (
github.com/luxfi/utils v1.1.0
github.com/luxfi/vm v1.0.1
github.com/luxfi/vms v1.0.0
github.com/luxfi/warp v1.16.39
github.com/luxfi/warp v1.18.0
github.com/mattn/go-colorable v0.1.14
github.com/mattn/go-isatty v0.0.20
github.com/prometheus/client_golang v1.23.2
+2
View File
@@ -272,6 +272,8 @@ github.com/luxfi/vms v1.0.0 h1:b5amEPj1S0k4FDECX3nCS4lFS/VsrCLfyrzacKOqAIs=
github.com/luxfi/vms v1.0.0/go.mod h1:fttFuYQBTLXSsI4oyO2yYAO95py/ejH1Nf/tffHX6X0=
github.com/luxfi/warp v1.16.39 h1:zqBBQZni8b11V/69+AX2eHJxTMREMN9bXl+AKOLF5wg=
github.com/luxfi/warp v1.16.39/go.mod h1:8kjDhuE5UKdd0IUGvoD2rnNphLuATCBOddyriz91pTM=
github.com/luxfi/warp v1.18.0 h1:4FRpB+mnrQeOhPvcKUWOdQbNODV1bY67ll4FKi1mYKc=
github.com/luxfi/warp v1.18.0/go.mod h1:8kjDhuE5UKdd0IUGvoD2rnNphLuATCBOddyriz91pTM=
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=
+5 -16
View File
@@ -4,7 +4,6 @@
package evm
import (
"errors"
"fmt"
"math/big"
@@ -133,24 +132,14 @@ func (v blockValidator) SyntacticVerify(b *Block, rules params.Rules) error {
if !cancun && ethHeader.BlobGasUsed != nil {
return fmt.Errorf("invalid blobGasUsed: have %d, expected nil", *ethHeader.BlobGasUsed)
}
if cancun && ethHeader.ExcessBlobGas == nil {
return errors.New("header is missing excessBlobGas")
}
if cancun && ethHeader.BlobGasUsed == nil {
return errors.New("header is missing blobGasUsed")
// Lux does NOT use Ethereum beacon chain or blob transactions
// Skip Cancun-specific field requirements for historic block imports
// Only reject if blob gas was actually used (which shouldn't happen on Lux)
if ethHeader.BlobGasUsed != nil && *ethHeader.BlobGasUsed > 0 {
return fmt.Errorf("blobs not enabled on lux networks: used %d blob gas", *ethHeader.BlobGasUsed)
}
if !cancun && ethHeader.ParentBeaconRoot != nil {
return fmt.Errorf("invalid parentBeaconRoot: have %x, expected nil", *ethHeader.ParentBeaconRoot)
}
// TODO: decide what to do after Cancun
// currently we are enforcing it to be empty hash
if cancun {
switch {
case ethHeader.ParentBeaconRoot == nil:
return errors.New("header is missing parentBeaconRoot")
case *ethHeader.ParentBeaconRoot != (common.Hash{}):
return fmt.Errorf("invalid parentBeaconRoot: have %x, expected empty hash", ethHeader.ParentBeaconRoot)
}
}
return nil
}
+39 -28
View File
@@ -20,7 +20,7 @@ import (
"github.com/luxfi/cache/metercacher"
"github.com/luxfi/p2p"
"github.com/luxfi/p2p/gossip"
"github.com/luxfi/p2p/lp118"
luxwarp "github.com/luxfi/warp"
// "github.com/luxfi/firewood-go-ethhash/ffi"
"github.com/prometheus/client_golang/prometheus"
@@ -434,20 +434,28 @@ func (vm *VM) initializeInternal(
}
}
fmt.Fprintf(os.Stderr, "[EVM-DEBUG] Calling parseGenesis with genesisAllocFile=%q\n", vm.config.GenesisAllocFile)
// Debug file for capturing initialization errors
debugFile, _ := os.OpenFile("/tmp/evm-debug.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
defer debugFile.Close()
debugLog := func(msg string, args ...interface{}) {
fmt.Fprintf(debugFile, "[EVM-DEBUG] "+msg+"\n", args...)
debugFile.Sync()
}
debugLog("Calling parseGenesis with genesisAllocFile=%q", vm.config.GenesisAllocFile)
g, err := parseGenesis(vm.ctx, genesisBytes, upgradeBytes, vm.config.AirdropFile, vm.config.GenesisAllocFile)
if err != nil {
fmt.Fprintf(os.Stderr, "[EVM-DEBUG] parseGenesis failed: %v\n", err)
debugLog("parseGenesis failed: %v", err)
return err
}
fmt.Fprintf(os.Stderr, "[EVM-DEBUG] parseGenesis succeeded: chainID=%v alloc=%d accounts\n", g.Config.ChainID, len(g.Alloc))
debugLog("parseGenesis succeeded: chainID=%v alloc=%d accounts", g.Config.ChainID, len(g.Alloc))
vm.syntacticBlockValidator = NewBlockValidator()
fmt.Fprintf(os.Stderr, "[EVM-DEBUG] Creating ethConfig\n")
debugLog("Creating ethConfig")
vm.ethConfig = ethconfig.NewDefaultConfig()
vm.ethConfig.Genesis = g
fmt.Fprintf(os.Stderr, "[EVM-DEBUG] ethConfig.Genesis set\n")
debugLog("ethConfig.Genesis set")
// NetworkID here is different than Lux's NetworkID.
// Lux's NetworkID represents the Lux network is running on
// like Testnet, Mainnet, Local, etc.
@@ -557,20 +565,26 @@ func (vm *VM) initializeInternal(
vm.networkCodec = message.Codec
// p2p.Sender is passed directly to network
debugLog("Creating network")
vm.Network, err = network.NewNetwork(context.Background(), sender, vm.networkCodec, vm.config.MaxOutboundActiveRequests, vm.sdkMetrics)
if err != nil {
debugLog("Failed to create network: %v", err)
return fmt.Errorf("failed to create network: %w", err)
}
debugLog("Network created successfully")
// P2PValidators might be nil in test environments
p2pValidatorsInterface := vm.Network.P2PValidators()
if p2pValidatorsInterface != nil {
vm.p2pValidators = p2pValidatorsInterface
}
debugLog("Creating validators manager")
vm.validatorsManager, err = validators.NewManager(vm.ctx, vm.validatorsDB, &vm.clock)
if err != nil {
debugLog("Failed to create validators manager: %v", err)
return fmt.Errorf("failed to initialize validators manager: %w", err)
}
debugLog("Validators manager created successfully")
// Initialize warp backend
offchainWarpMessages := make([][]byte, len(vm.config.WarpOffChainMessages))
@@ -593,17 +607,21 @@ func (vm *VM) initializeInternal(
// VM implements warp.BlockClient directly
// Get warp signer from context - use warptypes.Signer directly, no adapters
debugLog("Getting warp signer from context (WarpSigner=%v)", chainCtx.WarpSigner != nil)
var warpSigner warptypes.Signer
if chainCtx.WarpSigner != nil {
var ok bool
warpSigner, ok = chainCtx.WarpSigner.(warptypes.Signer)
if !ok {
debugLog("Invalid warp signer type: %T", chainCtx.WarpSigner)
return fmt.Errorf("invalid warp signer type: %T", chainCtx.WarpSigner)
}
debugLog("Got warp signer successfully")
}
// Only create warp backend if we have a signer
if warpSigner != nil {
debugLog("Creating warp backend")
vm.warpBackend, err = warp.NewBackend(
chainCtx.NetworkID, // Use NetworkID from consensus Context
chainCtx.ChainID,
@@ -615,13 +633,18 @@ func (vm *VM) initializeInternal(
offchainWarpMessages,
)
if err != nil {
debugLog("Failed to create warp backend: %v", err)
return err
}
debugLog("Warp backend created successfully")
}
debugLog("Initializing chain with lastAcceptedHash=%s", lastAcceptedHash)
if err := vm.initializeChain(lastAcceptedHash, vm.ethConfig); err != nil {
debugLog("Failed to initialize chain: %v", err)
return err
}
debugLog("Chain initialized successfully")
// Start continuous profiler in a goroutine with recovery
go func() {
@@ -633,14 +656,11 @@ func (vm *VM) initializeInternal(
vm.startContinuousProfiler()
}()
// Add p2p warp message warpHandler
// Create adapter to convert our warp backend to lp118.Verifier
// Add p2p warp message handler
warpVerifier := &warpVerifierAdapter{backend: vm.warpBackend}
// Use warp signer directly - warp.Signer is compatible with lp118.Signer
warpHandler := lp118.NewCachedHandler(meteredCache, warpVerifier, warpSigner)
// Use built-in adapter to convert lp118.Handler to p2p.Handler
p2pHandler := lp118.NewHandlerAdapter(warpHandler)
vm.Network.AddHandler(lp118.HandlerID, p2pHandler)
warpHandler := luxwarp.NewCachedSignatureHandler(meteredCache, warpVerifier, warpSigner)
p2pHandler := luxwarp.NewSignatureHandlerAdapter(warpHandler)
vm.Network.AddHandler(luxwarp.SignatureHandlerID, p2pHandler)
vm.setAppRequestHandlers()
@@ -1574,10 +1594,8 @@ func (vm *VM) CreateHandlers(context.Context) (map[string]http.Handler, error) {
}
if vm.config.WarpAPIEnabled {
warpSDKClient := vm.Network.NewClient(lp118.HandlerID)
// lp118.NewSignatureAggregator expects a node/utils/logging.Logger
// For now, pass nil as the logger is optional
signatureAggregator := lp118.NewSignatureAggregator(nil, warpSDKClient)
warpSDKClient := vm.Network.NewClient(luxwarp.SignatureHandlerID)
signatureAggregator := luxwarp.NewSignatureAggregator(nil, warpSDKClient)
if err := handler.RegisterName("warp", warp.NewAPI(vm.ctx, vm.warpBackend, signatureAggregator, vm.requirePrimaryNetworkSigners)); err != nil {
return nil, err
@@ -1893,19 +1911,12 @@ func (vm *VM) GetStateSummary(ctx context.Context, height uint64) (nodeblock.Sta
return nil, fmt.Errorf("summary does not implement message.SyncSummary")
}
// warpVerifierAdapter adapts our warp.Backend to lp118.Verifier
// warpVerifierAdapter adapts our warp.Backend to warp.Verifier
type warpVerifierAdapter struct {
backend warp.Backend
}
// Verify implements lp118.Verifier interface
// Now receives *warptypes.UnsignedMessage directly (no conversion needed)
func (w *warpVerifierAdapter) Verify(ctx context.Context, msg *warptypes.UnsignedMessage, justification []byte) *commonEng.AppError {
if err := w.backend.Verify(ctx, msg, justification); err != nil {
return &commonEng.AppError{
Code: 1,
Message: err.Error(),
}
}
return nil
// Verify implements warp.Verifier interface
func (w *warpVerifierAdapter) Verify(ctx context.Context, msg *warptypes.UnsignedMessage, justification []byte) error {
return w.backend.Verify(ctx, msg, justification)
}
+18 -3
View File
@@ -14,6 +14,7 @@ import (
consensuscontext "github.com/luxfi/consensus/context"
"github.com/luxfi/constants"
luxdatabase "github.com/luxfi/database"
"github.com/luxfi/ids"
"github.com/luxfi/database/factory"
"github.com/luxfi/database/prefixdb"
"github.com/luxfi/database/versiondb"
@@ -69,9 +70,23 @@ func (vm *VM) initializeDBs(avaDB luxdatabase.Database) error {
if useStandAloneDB {
// If we are using a standalone database, we need to create a new database
// for the chain state.
// Use unique path per chain to prevent database conflicts
chainID := vm.chainCtx.ChainID.String()
chainDataDir := filepath.Join("/tmp", "chaindata", chainID)
// Use the per-node ChainDataDir from context to prevent database conflicts
// Each node has its own ChainDataDir, so they won't conflict
var chainDataDir string
if vm.chainCtx != nil && vm.chainCtx.ChainDataDir != "" {
chainDataDir = vm.chainCtx.ChainDataDir
} else {
// Fallback: use a unique temp path with node ID to prevent conflicts
nodeIDStr := "unknown"
if vm.chainCtx != nil && vm.chainCtx.NodeID != ids.EmptyNodeID {
nodeIDStr = vm.chainCtx.NodeID.String()
}
chainID := "unknown"
if vm.chainCtx != nil && vm.chainCtx.ChainID != ids.Empty {
chainID = vm.chainCtx.ChainID.String()
}
chainDataDir = filepath.Join("/tmp", "chaindata", nodeIDStr, chainID)
}
dbConfig, err := getDatabaseConfig(vm.config, chainDataDir)
if err != nil {
return err
-35
View File
@@ -1,35 +0,0 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package warp
import (
"github.com/luxfi/warp"
platformWarp "github.com/luxfi/warp"
)
// LP118SignerAdapter adapts a LocalSigner to the lp118.Signer interface
type LP118SignerAdapter struct {
signer *LocalSigner
}
// NewLP118SignerAdapter creates a new adapter
func NewLP118SignerAdapter(signer *LocalSigner) *LP118SignerAdapter {
return &LP118SignerAdapter{
signer: signer,
}
}
// Sign implements the lp118.Signer interface (using platformvm/warp.UnsignedMessage)
func (a *LP118SignerAdapter) Sign(unsignedMsg *platformWarp.UnsignedMessage) ([]byte, error) {
// Convert platformvm/warp.UnsignedMessage to luxfi/warp.UnsignedMessage
msg, err := warp.NewUnsignedMessage(
unsignedMsg.NetworkID,
unsignedMsg.SourceChainID,
unsignedMsg.Payload,
)
if err != nil {
return nil, err
}
return a.signer.SignUnsignedMessage(msg)
}
+25
View File
@@ -0,0 +1,25 @@
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package warp
import (
"github.com/luxfi/warp"
)
// SignerAdapter adapts a LocalSigner to the warp.Signer interface
type SignerAdapter struct {
signer *LocalSigner
}
// NewSignerAdapter creates a new adapter
func NewSignerAdapter(signer *LocalSigner) *SignerAdapter {
return &SignerAdapter{
signer: signer,
}
}
// Sign implements the warp.Signer interface
func (a *SignerAdapter) Sign(unsignedMsg *warp.UnsignedMessage) ([]byte, error) {
return a.signer.SignUnsignedMessage(unsignedMsg)
}
+1 -1
View File
@@ -23,7 +23,7 @@ const (
)
// Verify verifies the signature of the message
// It also implements the lp118.Verifier interface
// It implements the warp.Verifier interface
func (b *backend) Verify(ctx context.Context, unsignedMessage *warp.UnsignedMessage, _ []byte) error {
messageIDBytes := unsignedMessage.ID()
messageID := ids.ID(crypto.Keccak256Hash(messageIDBytes[:]))