mirror of
https://github.com/luxfi/netrunner.git
synced 2026-07-27 00:04:23 +00:00
fix: correct network ID usage and remove HID conflicts
- Remove ledger-lux-go transitive dependency - Fix Network ID vs Chain ID confusion in genesis_config.go - Use constants.MainnetID/TestnetID instead of configs.ChainID - Fix port base calculation for multi-network mode - Add snapshot server functionality - Update protobuf definitions with new RPC methods - Add mnemonic test coverage
This commit is contained in:
@@ -6,6 +6,9 @@ breaking:
|
||||
- FILE
|
||||
lint:
|
||||
use:
|
||||
- DEFAULT
|
||||
- STANDARD
|
||||
except:
|
||||
- PACKAGE_VERSION_SUFFIX
|
||||
- RPC_REQUEST_RESPONSE_UNIQUE
|
||||
- RPC_REQUEST_STANDARD_NAME
|
||||
- RPC_RESPONSE_STANDARD_NAME
|
||||
|
||||
@@ -52,6 +52,7 @@ type Client interface {
|
||||
SendOutboundMessage(ctx context.Context, nodeName string, peerID string, op uint32, msgBody []byte) (*rpcpb.SendOutboundMessageResponse, error)
|
||||
Close() error
|
||||
SaveSnapshot(ctx context.Context, snapshotName string) (*rpcpb.SaveSnapshotResponse, error)
|
||||
SaveHotSnapshot(ctx context.Context, snapshotName string) (*rpcpb.SaveSnapshotResponse, error)
|
||||
LoadSnapshot(ctx context.Context, snapshotName string, opts ...OpOption) (*rpcpb.LoadSnapshotResponse, error)
|
||||
RemoveSnapshot(ctx context.Context, snapshotName string) (*rpcpb.RemoveSnapshotResponse, error)
|
||||
GetSnapshotNames(ctx context.Context) ([]string, error)
|
||||
@@ -343,6 +344,11 @@ func (c *client) SaveSnapshot(ctx context.Context, snapshotName string) (*rpcpb.
|
||||
return c.controlc.SaveSnapshot(ctx, &rpcpb.SaveSnapshotRequest{SnapshotName: snapshotName})
|
||||
}
|
||||
|
||||
func (c *client) SaveHotSnapshot(ctx context.Context, snapshotName string) (*rpcpb.SaveSnapshotResponse, error) {
|
||||
c.logger.Info("save hot snapshot", log.String("snapshot-name", snapshotName))
|
||||
return c.controlc.SaveHotSnapshot(ctx, &rpcpb.SaveSnapshotRequest{SnapshotName: snapshotName})
|
||||
}
|
||||
|
||||
func (c *client) LoadSnapshot(ctx context.Context, snapshotName string, opts ...OpOption) (*rpcpb.LoadSnapshotResponse, error) {
|
||||
c.logger.Info("load snapshot", log.String("snapshot-name", snapshotName))
|
||||
ret := &Op{}
|
||||
|
||||
+61
-27
@@ -54,19 +54,7 @@ var startCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
func runMultiNetwork(cmd *cobra.Command, args []string) error {
|
||||
fmt.Println("🚀 Starting networks in parallel mode...")
|
||||
|
||||
dbPath := "/tmp/netrunner-shared-db"
|
||||
if sharedDB {
|
||||
fmt.Println("📦 Using shared BadgerDB for cross-chain transactions")
|
||||
}
|
||||
|
||||
// Create multi-network manager
|
||||
manager, err := multinet.NewMultiNetworkManager(startLogger, dbPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create multi-network manager: %w", err)
|
||||
}
|
||||
defer manager.Shutdown()
|
||||
fmt.Println("🚀 Starting multiple networks...")
|
||||
|
||||
// Determine which networks to start
|
||||
networksToStart := networks
|
||||
@@ -74,25 +62,71 @@ func runMultiNetwork(cmd *cobra.Command, args []string) error {
|
||||
networksToStart = []string{"mainnet", "testnet"}
|
||||
}
|
||||
|
||||
// Configure networks
|
||||
for _, network := range networksToStart {
|
||||
config := getNetworkConfig(network)
|
||||
if err := manager.AddNetwork(config); err != nil {
|
||||
return fmt.Errorf("failed to add network %s: %w", network, err)
|
||||
// Load validator keys once
|
||||
ks := keys.NewKeyStore(keysDir)
|
||||
validatorKeys, err := ks.LoadAll()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load validator keys: %w", err)
|
||||
}
|
||||
if len(validatorKeys) == 0 {
|
||||
return fmt.Errorf("no validator keys found in %s", keysDir)
|
||||
}
|
||||
|
||||
fmt.Printf("📋 Loaded %d validator keys\n", len(validatorKeys))
|
||||
|
||||
// Track running networks for cleanup
|
||||
var runningNetworks []network.Network
|
||||
defer func() {
|
||||
for _, ln := range runningNetworks {
|
||||
_ = ln.Stop(cmd.Context())
|
||||
}
|
||||
}()
|
||||
|
||||
// Start each network
|
||||
for _, networkName := range networksToStart {
|
||||
fmt.Printf("\n🔧 Starting %s...\n", networkName)
|
||||
|
||||
var netConfig network.Config
|
||||
switch networkName {
|
||||
case "mainnet":
|
||||
netConfig, err = local.NewMainnetConfigWithKeys(binaryPath, keysDir)
|
||||
case "testnet":
|
||||
netConfig, err = local.NewTestnetConfigWithKeys(binaryPath, keysDir)
|
||||
default:
|
||||
return fmt.Errorf("unknown network: %s", networkName)
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create %s config: %w", networkName, err)
|
||||
}
|
||||
|
||||
ln, err := local.NewNetwork(
|
||||
startLogger,
|
||||
netConfig,
|
||||
"/tmp/netrunner/"+networkName,
|
||||
"", // snapshot dir
|
||||
true, // reassign ports if busy
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start %s: %w", networkName, err)
|
||||
}
|
||||
runningNetworks = append(runningNetworks, ln)
|
||||
|
||||
nodes, _ := ln.GetAllNodes()
|
||||
for name, n := range nodes {
|
||||
fmt.Printf(" %s: http://localhost:%d\n", name, n.GetAPIPort())
|
||||
}
|
||||
}
|
||||
|
||||
// Start all networks
|
||||
if err := manager.StartAll(); err != nil {
|
||||
return fmt.Errorf("failed to start networks: %w", err)
|
||||
}
|
||||
|
||||
fmt.Println("\n✅ Networks started successfully!")
|
||||
fmt.Println("\n✅ All networks started!")
|
||||
printNetworkEndpoints(networksToStart)
|
||||
|
||||
fmt.Println("\nPress Ctrl+C to stop...")
|
||||
select {}
|
||||
// Wait for interrupt
|
||||
fmt.Println("\n⏳ Press Ctrl+C to stop...")
|
||||
sigCh := make(chan os.Signal, 1)
|
||||
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
|
||||
<-sigCh
|
||||
|
||||
fmt.Println("\n🛑 Shutting down all networks...")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -214,7 +248,7 @@ func printNetworkEndpoints(networks []string) {
|
||||
case "mainnet":
|
||||
fmt.Println(" Lux Mainnet: http://localhost:9630")
|
||||
case "testnet":
|
||||
fmt.Println(" Lux Testnet: http://localhost:9620")
|
||||
fmt.Println(" Lux Testnet: http://localhost:9640")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,19 +6,28 @@ exclude google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1
|
||||
|
||||
exclude github.com/luxfi/geth v1.16.1
|
||||
|
||||
replace github.com/luxfi/genesis => /Users/z/work/lux/genesis
|
||||
|
||||
replace github.com/luxfi/sdk => /Users/z/work/lux/sdk
|
||||
|
||||
replace github.com/luxfi/node => /Users/z/work/lux/node
|
||||
|
||||
require (
|
||||
github.com/btcsuite/btcd v0.24.2
|
||||
github.com/btcsuite/btcd/btcutil v1.1.6
|
||||
github.com/dgraph-io/badger/v4 v4.8.0
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1
|
||||
github.com/luxfi/config v1.0.0
|
||||
github.com/luxfi/consensus v1.22.41
|
||||
github.com/luxfi/constants v1.2.8
|
||||
github.com/luxfi/consensus v1.22.46
|
||||
github.com/luxfi/constants v1.3.0
|
||||
github.com/luxfi/crypto v1.17.26
|
||||
github.com/luxfi/genesis v1.5.11
|
||||
github.com/luxfi/geth v1.16.60
|
||||
github.com/luxfi/genesis v1.5.16
|
||||
github.com/luxfi/geth v1.16.64
|
||||
github.com/luxfi/go-bip39 v1.1.2
|
||||
github.com/luxfi/ids v1.2.5
|
||||
github.com/luxfi/keys v1.0.3
|
||||
github.com/luxfi/log v1.1.26
|
||||
github.com/luxfi/math v1.1.1
|
||||
github.com/luxfi/math v1.2.0
|
||||
github.com/luxfi/metric v1.4.8
|
||||
github.com/luxfi/node v1.22.61
|
||||
github.com/luxfi/units v1.0.0
|
||||
@@ -29,11 +38,10 @@ require (
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible
|
||||
github.com/spf13/cobra v1.9.1
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/tyler-smith/go-bip32 v1.0.0
|
||||
github.com/tyler-smith/go-bip39 v1.1.0
|
||||
go.uber.org/multierr v1.11.0
|
||||
golang.org/x/crypto v0.46.0
|
||||
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b
|
||||
golang.org/x/mod v0.30.0
|
||||
golang.org/x/mod v0.31.0
|
||||
golang.org/x/sync v0.19.0
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250811230008-5f3141c8851a
|
||||
google.golang.org/grpc v1.75.1
|
||||
@@ -43,14 +51,13 @@ require (
|
||||
|
||||
require (
|
||||
github.com/DataDog/zstd v1.5.7 // indirect
|
||||
github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e // indirect
|
||||
github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec // indirect
|
||||
github.com/Masterminds/semver/v3 v3.4.0 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251211224604-2e727cd2e6fe // indirect
|
||||
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251221085550-b8e13ca38217 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.24.4 // indirect
|
||||
github.com/btcsuite/btcd/btcutil v1.1.6 // indirect
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.3.5 // indirect
|
||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 // indirect
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cloudflare/circl v1.6.2-0.20251204010831-23491bd573cf // indirect
|
||||
@@ -95,17 +102,19 @@ require (
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/luxfi/address v1.0.0 // indirect
|
||||
github.com/luxfi/cache v1.1.0 // indirect
|
||||
github.com/luxfi/database v1.2.14 // indirect
|
||||
github.com/luxfi/database v1.2.17 // indirect
|
||||
github.com/luxfi/go-bip32 v1.0.2 // indirect
|
||||
github.com/luxfi/go-bip39 v1.1.2 // indirect
|
||||
github.com/luxfi/ledger-lux-go v1.0.2 // indirect
|
||||
github.com/luxfi/keychain v1.0.0 // indirect
|
||||
github.com/luxfi/mock v0.1.0 // indirect
|
||||
github.com/luxfi/p2p v1.5.0 // indirect
|
||||
github.com/luxfi/p2p v1.18.2 // indirect
|
||||
github.com/luxfi/sampler v1.0.0 // indirect
|
||||
github.com/luxfi/staking v1.0.0 // indirect
|
||||
github.com/luxfi/trace v0.1.4 // indirect
|
||||
github.com/luxfi/utils v1.1.0 // indirect
|
||||
github.com/luxfi/vm v1.0.1 // indirect
|
||||
github.com/luxfi/warp v1.18.0 // indirect
|
||||
github.com/luxfi/warp v1.18.2 // indirect
|
||||
github.com/mr-tron/base58 v1.2.0 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/otiai10/mint v1.6.3 // indirect
|
||||
@@ -118,13 +127,13 @@ require (
|
||||
github.com/prometheus/procfs v0.19.2 // indirect
|
||||
github.com/rogpeppe/go-internal v1.14.1 // indirect
|
||||
github.com/rs/cors v1.11.1 // indirect
|
||||
github.com/sagikazarmark/locafero v0.10.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.11.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
|
||||
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||
github.com/spf13/afero v1.14.0 // indirect
|
||||
github.com/spf13/afero v1.15.0 // indirect
|
||||
github.com/spf13/cast v1.10.0 // indirect
|
||||
github.com/spf13/pflag v1.0.7 // indirect
|
||||
github.com/spf13/viper v1.20.1 // indirect
|
||||
github.com/spf13/pflag v1.0.10 // indirect
|
||||
github.com/spf13/viper v1.21.0 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/supranational/blst v0.3.16 // indirect
|
||||
@@ -143,14 +152,14 @@ require (
|
||||
go.opentelemetry.io/proto/otlp v1.7.1 // indirect
|
||||
go.uber.org/automaxprocs v1.6.0 // indirect
|
||||
go.uber.org/mock v0.6.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
go.uber.org/zap v1.27.1 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
||||
golang.org/x/crypto v0.46.0 // indirect
|
||||
golang.org/x/net v0.47.0 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/net v0.48.0 // indirect
|
||||
golang.org/x/sys v0.39.0 // indirect
|
||||
golang.org/x/text v0.32.0 // indirect
|
||||
golang.org/x/time v0.12.0 // indirect
|
||||
golang.org/x/tools v0.39.0 // indirect
|
||||
golang.org/x/tools v0.40.0 // indirect
|
||||
gonum.org/v1/gonum v0.16.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8 // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
github.com/DataDog/zstd v1.5.7 h1:ybO8RBeh29qrxIhCA9E8gKY6xfONU9T6G6aP9DTKfLE=
|
||||
github.com/DataDog/zstd v1.5.7/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
|
||||
github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e h1:ahyvB3q25YnZWly5Gq1ekg6jcmWaGj/vG/MhF4aisoc=
|
||||
github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e/go.mod h1:kGUqhHd//musdITWjFvNTHn90WG9bMLBEPQZ17Cmlpw=
|
||||
github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec h1:1Qb69mGp/UtRPn422BH4/Y4Q3SLUrD9KHuDkm8iodFc=
|
||||
github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec/go.mod h1:CD8UlnlLDiqb36L110uqiP2iSflVjx9g/3U9hCI4q2U=
|
||||
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
|
||||
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||
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-20251211224604-2e727cd2e6fe h1:Z93WiwkZABbBBb0hGVFSF9nofjiYRvdF7PUxB75oeyE=
|
||||
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251211224604-2e727cd2e6fe/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI=
|
||||
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251221085550-b8e13ca38217 h1:RNTau6sw5B3ltE+2GlzEom63eBMzoLUc+dhjmdIwNpI=
|
||||
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251221085550-b8e13ca38217/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI=
|
||||
github.com/VictoriaMetrics/fastcache v1.13.0 h1:AW4mheMR5Vd9FkAPUv+NH6Nhw+fmbTMGMsNAoA/+4G0=
|
||||
github.com/VictoriaMetrics/fastcache v1.13.0/go.mod h1:hHXhl4DA2fTL2HTZDJFXWgW0LNjo6B+4aj2Wmng3TjU=
|
||||
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
|
||||
@@ -20,9 +16,12 @@ github.com/bits-and-blooms/bitset v1.24.4/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6
|
||||
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 h1:aLmxPguqxza+4ag8R1I2nnJjSu2iFn/kqtHTIImswcY=
|
||||
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/btcec/v2 v2.3.5 h1:dpAlnAwmT1yIBm3exhT1/8iUSD98RDJM5vqJVQDQLiU=
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.3.5/go.mod h1:m22FrOAiuxl/tht9wIqAoGHcbnCCaPWyauO8y2LGGtQ=
|
||||
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=
|
||||
@@ -30,6 +29,7 @@ github.com/btcsuite/btcd/btcutil v1.1.6 h1:zFL2+c3Lb9gEgqKNzowKUPQNb8jV7v5Oaodi/
|
||||
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 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ=
|
||||
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=
|
||||
@@ -51,8 +51,6 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/cloudflare/circl v1.6.2-0.20251204010831-23491bd573cf h1:+0huF756d72ZdHxachRP5HYmvRkkOk8CLyKUmJM138c=
|
||||
github.com/cloudflare/circl v1.6.2-0.20251204010831-23491bd573cf/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
||||
github.com/cmars/basen v0.0.0-20150613233007-fe3947df716e h1:0XBUw73chJ1VYSsfvcPvVT7auykAJce9FpRr10L6Qhw=
|
||||
github.com/cmars/basen v0.0.0-20150613233007-fe3947df716e/go.mod h1:P13beTBKr5Q18lJe1rIoLUqjM+CB1zYrRg44ZqGuQSA=
|
||||
github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4=
|
||||
github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU=
|
||||
github.com/cockroachdb/errors v1.12.0 h1:d7oCs6vuIMUQRVbi6jWWWEJZahLCfJpnJSVobd1/sUo=
|
||||
@@ -233,44 +231,48 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzWu4=
|
||||
github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c=
|
||||
github.com/luxfi/address v1.0.0 h1:9BPx5VyUfzIET3xqrSnvhmi3jY9ZIjcThO1dDdHnqlU=
|
||||
github.com/luxfi/address v1.0.0/go.mod h1:K6EISvpZ4Ovgna4fsqVRv2xCHw7L1J12LxzqD2jAvv0=
|
||||
github.com/luxfi/cache v1.1.0 h1:6LUyGGZ+rrMAJBbAU6+UwkcamXj3zsboRUodIof2Ong=
|
||||
github.com/luxfi/cache v1.1.0/go.mod h1:9GvlEEE9rFPaaWxvVpSPwW8ZMo2+8VMNNcuPa4AwzPg=
|
||||
github.com/luxfi/config v1.0.0 h1:EFIFkdus3YdDklhlwHKJNYLpSVuvWqHm+fs/TGckv5U=
|
||||
github.com/luxfi/config v1.0.0/go.mod h1:71q4A4m7vj+o1qnL1dNk/tVII+Ugiwvi9+jVMN2xxTU=
|
||||
github.com/luxfi/consensus v1.22.41 h1:YU5/QI3TADwVpWwPpsxWII8T7Ioohp84nCqadUdHR7w=
|
||||
github.com/luxfi/consensus v1.22.41/go.mod h1:M7+JXcLOeyCMeNZzH2+UEKwEcfnooON385zTu14decM=
|
||||
github.com/luxfi/constants v1.2.8 h1:VmtbkwiCz5IEIfjiuLb54L4C7ayn7MqshcWzd+JpIag=
|
||||
github.com/luxfi/constants v1.2.8/go.mod h1:YyHBi205pBpzgYCmD44QPb6dCOzKTK0jwpGrfB4kcRA=
|
||||
github.com/luxfi/consensus v1.22.46 h1:w97OZjtW/anWvgM0N/q8Y70LEJlqlFG6yeS10rKcnx4=
|
||||
github.com/luxfi/consensus v1.22.46/go.mod h1:cbvtxHH5pab/XJIlHekg66tYaIChTwTt9SNMMgz8+as=
|
||||
github.com/luxfi/constants v1.3.0 h1:nPWEhGyhYt+fJS00TUgV1FsyDlSVvsXf0fXq7UBI1RY=
|
||||
github.com/luxfi/constants v1.3.0/go.mod h1:xHHeq8ALnhuDrc63B5dIhaOtSetBnA/LO17JbZFd0SU=
|
||||
github.com/luxfi/crypto v1.17.26 h1:B4hebyl2whu9Og+mb7QoQavD4LM1cUpwWQEnwi3H6fU=
|
||||
github.com/luxfi/crypto v1.17.26/go.mod h1:v6eaW5ejuzW2gecChWKQqx4CtVhoM1b+e40ro/6WVPg=
|
||||
github.com/luxfi/database v1.2.14 h1:0lxsLiCUOb/42tffrRFgIQN/imD0N9rZdAb9r97pbJE=
|
||||
github.com/luxfi/database v1.2.14/go.mod h1:5xrFyVRGw8z50vkwt9fF1GkvkFvvvEqxrdeSZqGar0U=
|
||||
github.com/luxfi/genesis v1.5.11 h1:Lv8VGecA1fsF0pEH/SF5u4SSK9RXUH3c7XIAQuEOQMw=
|
||||
github.com/luxfi/genesis v1.5.11/go.mod h1:a8NdOhQxUrl88cvI6dQBIC8PiTbKBe4xH5vbzu4NXRo=
|
||||
github.com/luxfi/geth v1.16.60 h1:0fm8U815I4U6JhjCDWaVO0cF9jjksggcxSc/TuK3T9M=
|
||||
github.com/luxfi/geth v1.16.60/go.mod h1:vRIzu5NRkevNIx50kksP+M+xBeLj0jTWbfn7KvMBqSo=
|
||||
github.com/luxfi/database v1.2.17 h1:1PE0FlAarorfAMl+vAvO6EDbkS+a4ktL7kvtRk58DWY=
|
||||
github.com/luxfi/database v1.2.17/go.mod h1:IzvNHGFZWdRs5CL5vswqz8/uu8t796cMGlbqPWs032s=
|
||||
github.com/luxfi/geth v1.16.64 h1:kfuVA6FkgHOsOKiz75e+h6UG4eHLOInqXmJEhx9NKTs=
|
||||
github.com/luxfi/geth v1.16.64/go.mod h1:7lQ25do4pQizMtrVKdepji2Pl/pL+8DUo5Q0SbSuWpE=
|
||||
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.5 h1:w4YSj3S6LQiUXhiChUmcbrMCRMfkCaBFIGn0Eo4ZSxk=
|
||||
github.com/luxfi/ids v1.2.5/go.mod h1:vZBjJWSmqMIZuodgnoszvTDvXIwsbnhM2pkktg6FGrg=
|
||||
github.com/luxfi/keychain v1.0.0 h1:FzRW9ZlfsFQZ8RsMGL5X9Jhcg8G1DEmK+WAeGTA2tfM=
|
||||
github.com/luxfi/keychain v1.0.0/go.mod h1:X7HLk5QRZ1VHJxovAyBL6z7f/nlosoXlveEtvUGhoaw=
|
||||
github.com/luxfi/keys v1.0.3 h1:O1f2h42G1hI6EIoRPl6zSYwrRF7gtUlskHMHbguRHlE=
|
||||
github.com/luxfi/keys v1.0.3/go.mod h1:YrmQQEJWaSdDJym1Wzs0ngNzvaBR5oJNRat0sHj6R1o=
|
||||
github.com/luxfi/ledger-lux-go v1.0.2 h1:+B8wcF+bcmJ2Kr/dszMmbj20q8wUV5jLY60fkDkWqWo=
|
||||
github.com/luxfi/ledger-lux-go v1.0.2/go.mod h1:BM5rJ3UPxhmElPRqjP+AHbB+JzwylUGmiGA/Gh4SKUU=
|
||||
github.com/luxfi/log v1.1.26 h1:ECnJ4wV7TF6WZ5gC6CD6ddZ4OhF+zhQij4bgVUhumDg=
|
||||
github.com/luxfi/log v1.1.26/go.mod h1:ACN1FtMlJ/NRuWROYH2TtiFmJuUbSG+OxINlRzJM/38=
|
||||
github.com/luxfi/math v1.1.1 h1:lR8iS/WQpbNxZsLFVagVee/t90xs0qohL5eC3kK0JR0=
|
||||
github.com/luxfi/math v1.1.1/go.mod h1:02NrpmajRYR8V1e6o669DWF12gztdtaF6RpiweVuA/k=
|
||||
github.com/luxfi/math v1.2.0 h1:cZVQQj1PrIWh3Oy6xfbBL44xay4QFuAXZBg9+pWPBmE=
|
||||
github.com/luxfi/math v1.2.0/go.mod h1:gcmMy6raVf6xrQW/4UmFXEEXC/UC2tZeGkxdiDzeBSg=
|
||||
github.com/luxfi/metric v1.4.8 h1:mMbcJW+E1z6qQxn34+BE109HlvT6ciiDXuc10qEBJRs=
|
||||
github.com/luxfi/metric v1.4.8/go.mod h1:AUQ7NSNz9WndAcr/SKnOkP7XSFFnBXOa+ihtJYfDaQY=
|
||||
github.com/luxfi/mock v0.1.0 h1:IwElfNu+T9sXvzFX6tudPDx1vqPuACRSRdxpD5lxW+o=
|
||||
github.com/luxfi/mock v0.1.0/go.mod h1:izF+9K0gGzFC9zERn6Po37v46eLdPB+EIsDjL3GLk+U=
|
||||
github.com/luxfi/node v1.22.61 h1:BU+hJeyPqxr2nH8hSLzbCJr8tVW1DwroktufzK90XFY=
|
||||
github.com/luxfi/node v1.22.61/go.mod h1:zTMpqUkfVeer0D6s0ZZShI1W8u+1ZIGotGlkC0h8NsU=
|
||||
github.com/luxfi/p2p v1.5.0 h1:g03aCreOBRwMXuxFAT2ca0jDbW3RF32vBaM2/rfDmF8=
|
||||
github.com/luxfi/p2p v1.5.0/go.mod h1:yVPJQbCm2uxnFfDwOiBWh0gfG0GschIroLUsLn5Fhm8=
|
||||
github.com/luxfi/p2p v1.18.2 h1:elpyXr58wht1KP64D4BcKXft1AEjJ3IRACj/W8vdLI8=
|
||||
github.com/luxfi/p2p v1.18.2/go.mod h1:eM6XpmBFbDjaI7fFuGYBPab77TS1esxHxAOluBLAC0Y=
|
||||
github.com/luxfi/precompiles v0.1.8 h1:GWigA4sE+jKbO43Oum4apDcLHbhATv6rRivPsnUc5nY=
|
||||
github.com/luxfi/precompiles v0.1.8/go.mod h1:bzX6yzT1FNhNwCSBu1XB7X/mQ8WO/vKORtJ4ejUbEHM=
|
||||
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/staking v1.0.0 h1:pYI7XMGGb/zK2ATfTIkush/7HPWE7IS2yfmJqj4nzes=
|
||||
github.com/luxfi/staking v1.0.0/go.mod h1:E+fm3upfQTOu0fLCr3Uo4xYgAm7r/vEzgcUpNAhJuSE=
|
||||
github.com/luxfi/trace v0.1.4 h1:ttCRyXGwWuz232se+lIUqhWHBoTuvPLhHH/hLWyqtaM=
|
||||
github.com/luxfi/trace v0.1.4/go.mod h1:Az7HWh+PCuPftXjQu+ssjv51nKauaFu+q2un7bmZYBA=
|
||||
github.com/luxfi/units v1.0.0 h1:2aNVB+WsP1XeDob71IsO0w3jJqP3FtZdYnFsmORkJZg=
|
||||
@@ -279,8 +281,8 @@ github.com/luxfi/utils v1.1.0 h1:ti7HvjNwJd4ILDMERJtOAWE9mF8l+zqDVkgWnF7Agic=
|
||||
github.com/luxfi/utils v1.1.0/go.mod h1:ABqhBdGNig0CaDcnNYldv1byS0BTNi5IKPbJSPF1p98=
|
||||
github.com/luxfi/vm v1.0.1 h1:eZqrp6mEFPkhiv5kf5ZXmYAbXXjpS9axpUQG3cd3yts=
|
||||
github.com/luxfi/vm v1.0.1/go.mod h1:PJinuF4McLaHN57iKf8p7h89wIQhQT//LD/fac+/Dk8=
|
||||
github.com/luxfi/warp v1.18.0 h1:4FRpB+mnrQeOhPvcKUWOdQbNODV1bY67ll4FKi1mYKc=
|
||||
github.com/luxfi/warp v1.18.0/go.mod h1:8kjDhuE5UKdd0IUGvoD2rnNphLuATCBOddyriz91pTM=
|
||||
github.com/luxfi/warp v1.18.2 h1:TWmXjKXI7nxaDOnXIrw8BQTI/nCEniA17bs06bwNB4Q=
|
||||
github.com/luxfi/warp v1.18.2/go.mod h1:qu3TZv98repNhIDf04I6pzXePSgHjzP+JoSfWmF4PBA=
|
||||
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=
|
||||
@@ -328,18 +330,18 @@ github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0
|
||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 h1:oYW+YCJ1pachXTQmzR3rNLYGGz4g/UgFcjb28p/viDM=
|
||||
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0=
|
||||
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
|
||||
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
|
||||
github.com/pion/dtls/v2 v2.2.7 h1:cSUBsETxepsCSFSxC3mc/aDo14qQLMSL+O6IjG28yV8=
|
||||
github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s=
|
||||
github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
|
||||
github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
|
||||
github.com/pingcap/errors v0.11.5-0.20210425183316-da1aaba5fb63 h1:+FZIDR/D97YOPik4N4lPDaUcLDF/EQPogxtlHB2ZZRM=
|
||||
github.com/pingcap/errors v0.11.5-0.20210425183316-da1aaba5fb63/go.mod h1:X2r9ueLEUZgtx2cIogM0v4Zj5uvvzhuuiu7Pn8HzMPg=
|
||||
github.com/pion/dtls/v2 v2.2.12 h1:KP7H5/c1EiVAAKUmXyCzPiQe5+bCJrpOeKg/L05dunk=
|
||||
github.com/pion/dtls/v2 v2.2.12/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE=
|
||||
github.com/pion/logging v0.2.3 h1:gHuf0zpoh1GW67Nr6Gj4cv5Z9ZscU7g/EaoC/Ke/igI=
|
||||
github.com/pion/logging v0.2.3/go.mod h1:z8YfknkquMe1csOrxK5kc+5/ZPAzMxbKLX5aXpbpC90=
|
||||
github.com/pion/stun/v2 v2.0.0 h1:A5+wXKLAypxQri59+tmQKVs7+l6mMM+3d+eER9ifRU0=
|
||||
github.com/pion/stun/v2 v2.0.0/go.mod h1:22qRSh08fSEttYUmJZGlriq9+03jtVmXNODgLccj8GQ=
|
||||
github.com/pion/transport/v2 v2.2.1 h1:7qYnCBlpgSJNYMbLCKuSY9KbQdBFoETvPNETv0y4N7c=
|
||||
github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g=
|
||||
github.com/pion/transport/v3 v3.0.1 h1:gDTlPJwROfSfz6QfSi0ZmeCSkFcnWWiiR9ES0ouANiM=
|
||||
github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0=
|
||||
github.com/pion/transport/v2 v2.2.10 h1:ucLBLE8nuxiHfvkFKnkDQRYWYfp8ejf4YBOPfaQpw6Q=
|
||||
github.com/pion/transport/v2 v2.2.10/go.mod h1:sq1kSLWs+cHW9E+2fJP95QudkzbK7wscs8yYgQToO5E=
|
||||
github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1o0=
|
||||
github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo=
|
||||
github.com/pires/go-proxyproto v0.8.1 h1:9KEixbdJfhrbtjpz/ZwCdWDD2Xem0NZ38qMYaASJgp0=
|
||||
github.com/pires/go-proxyproto v0.8.1/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
@@ -367,8 +369,8 @@ github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA=
|
||||
github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sagikazarmark/locafero v0.10.0 h1:FM8Cv6j2KqIhM2ZK7HZjm4mpj9NBktLgowT1aN9q5Cc=
|
||||
github.com/sagikazarmark/locafero v0.10.0/go.mod h1:Ieo3EUsjifvQu4NZwV5sPd4dwvu0OCgEQV7vjc9yDjw=
|
||||
github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
|
||||
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
|
||||
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/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
|
||||
@@ -377,23 +379,22 @@ github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrel
|
||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=
|
||||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA=
|
||||
github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo=
|
||||
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
||||
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
|
||||
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
|
||||
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
|
||||
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
||||
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M=
|
||||
github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4=
|
||||
github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4=
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
|
||||
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
|
||||
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/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.1.5-0.20170601210322-f6abca593680/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
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=
|
||||
@@ -415,12 +416,10 @@ github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8O
|
||||
github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4=
|
||||
github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso=
|
||||
github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
|
||||
github.com/tyler-smith/go-bip32 v1.0.0 h1:sDR9juArbUgX+bO/iblgZnMPeWY1KZMUC2AFUJdv5KE=
|
||||
github.com/tyler-smith/go-bip32 v1.0.0/go.mod h1:onot+eHknzV4BVPwrzqY5OoVpyCvnwD7lMawL5aQupE=
|
||||
github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8=
|
||||
github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U=
|
||||
github.com/urfave/cli/v2 v2.27.7 h1:bH59vdhbjLv3LAvIu6gd0usJHgoTTPhCFib8qqOwXYU=
|
||||
github.com/urfave/cli/v2 v2.27.7/go.mod h1:CyNAG/xg+iAOg0N4MPGZqVmv2rCoP267496AOXUZjA4=
|
||||
github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU=
|
||||
github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA=
|
||||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
|
||||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
@@ -448,6 +447,8 @@ go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mx
|
||||
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
|
||||
go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4=
|
||||
go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE=
|
||||
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
|
||||
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
|
||||
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
@@ -456,11 +457,12 @@ go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
|
||||
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
|
||||
go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
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-20170613210332-850760c427c5/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
@@ -472,8 +474,8 @@ golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b/go.mod h1:4QTo5u+SEIbbKW1Rac
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
|
||||
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
|
||||
golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=
|
||||
golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=
|
||||
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=
|
||||
@@ -486,8 +488,8 @@ golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
|
||||
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -535,8 +537,8 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||
golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
|
||||
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
|
||||
golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA=
|
||||
golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -577,5 +579,3 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
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=
|
||||
launchpad.net/gocheck v0.0.0-20140225173054-000000000087 h1:Izowp2XBH6Ya6rv+hqbceQyw/gSGoXfH/UPoTGduL54=
|
||||
launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80Vse0e+BUHsHMTEhd0O4cpUHr/e/BUM=
|
||||
|
||||
+35
-8
@@ -17,8 +17,9 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/luxfi/genesis/pkg/genesis"
|
||||
"github.com/luxfi/keys"
|
||||
|
||||
"github.com/luxfi/genesis/pkg/genesis"
|
||||
"github.com/luxfi/node/vms/platformvm/reward"
|
||||
|
||||
"github.com/luxfi/node/vms/components/lux"
|
||||
@@ -1277,24 +1278,27 @@ type wallet struct {
|
||||
// getDefaultKey loads the first key from ~/.lux/keys for wallet operations.
|
||||
// Priority: LUX_MNEMONIC > LUX_PRIVATE_KEY > disk keys
|
||||
func getDefaultKey() (*secp256k1.PrivateKey, error) {
|
||||
// If LUX_MNEMONIC is set, derive key from mnemonic (index 0)
|
||||
// If LUX_MNEMONIC is set, derive key using BIP44 path m/44'/9000'/0'/0/0 (LUX native)
|
||||
// CRITICAL: This MUST match the derivation path used in genesis allocations
|
||||
// (keys.DeriveValidatorFromMnemonic uses m/44'/9000'/0'/0/{index})
|
||||
if mnemonic := os.Getenv("LUX_MNEMONIC"); mnemonic != "" {
|
||||
fmt.Printf("🔑 getDefaultKey: Using LUX_MNEMONIC (len=%d)\n", len(mnemonic))
|
||||
|
||||
// Use the SAME derivation function as genesis allocations
|
||||
// This ensures wallet operations use keys that have funds allocated in genesis
|
||||
vk, err := keys.DeriveValidatorFromMnemonic(mnemonic, 0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to derive key from mnemonic: %w", err)
|
||||
}
|
||||
fmt.Printf("🔑 VK.PChainAddr (from mnemonic derivation): %s\n", vk.PChainAddr.String())
|
||||
|
||||
privKey, err := secp256k1.ToPrivateKey(vk.ECPrivateKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("failed to convert key: %w", err)
|
||||
}
|
||||
|
||||
pubKey := privKey.PublicKey()
|
||||
walletAddr := ids.ShortID(pubKey.Address())
|
||||
fmt.Printf("🔑 Wallet address (from secp256k1 key): %s\n", walletAddr.String())
|
||||
fmt.Printf("🔑 Addresses match: %v\n", vk.PChainAddr == walletAddr)
|
||||
|
||||
fmt.Printf("🔑 Wallet address (from mnemonic m/44'/9000'/0'/0/0): %s\n", walletAddr.String())
|
||||
|
||||
return privKey, nil
|
||||
}
|
||||
|
||||
@@ -2440,6 +2444,29 @@ func (ln *localNetwork) setBlockchainConfigFiles(
|
||||
participants = nodeNames
|
||||
}
|
||||
chainAlias := blockchainTxs[i].ID().String()
|
||||
|
||||
// For EVM chains, write genesis.json to chainConfigs directory
|
||||
// This is required for the EVM to load the chain configuration
|
||||
if len(chainSpec.Genesis) > 0 {
|
||||
for _, nodeName := range participants {
|
||||
_, b := ln.nodes[nodeName]
|
||||
if !b {
|
||||
return nil, fmt.Errorf("participant node %s is not in network nodes", nodeName)
|
||||
}
|
||||
// Initialize GenesisConfigFiles map if nil
|
||||
if ln.nodes[nodeName].config.GenesisConfigFiles == nil {
|
||||
ln.nodes[nodeName].config.GenesisConfigFiles = make(map[string]string)
|
||||
}
|
||||
ln.nodes[nodeName].config.GenesisConfigFiles[chainAlias] = string(chainSpec.Genesis)
|
||||
nodesToRestart.Add(nodeName)
|
||||
}
|
||||
log.Info("set genesis config for chain",
|
||||
"chainAlias", chainAlias,
|
||||
"participants", len(participants),
|
||||
"genesisLength", len(chainSpec.Genesis),
|
||||
)
|
||||
}
|
||||
|
||||
// update config info. set defaults and node specifics
|
||||
if chainSpec.ChainConfig != nil || len(chainSpec.PerNodeChainConfig) != 0 {
|
||||
for _, nodeName := range participants {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,14 +2,16 @@
|
||||
"consensus-api-enabled": false,
|
||||
"geth-admin-api-enabled": false,
|
||||
"geth-admin-api-dir": "",
|
||||
"eth-apis": ["eth", "eth-filter", "net", "web3", "internal-eth", "internal-blockchain", "internal-transaction", "internal-account"],
|
||||
"eth-apis": ["eth", "eth-filter", "net", "web3", "internal-eth", "internal-blockchain", "internal-transaction", "internal-account", "admin"],
|
||||
"admin-api-enabled": true,
|
||||
"continuous-profiler-dir": "",
|
||||
"continuous-profiler-frequency": 900000000000,
|
||||
"continuous-profiler-max-files": 5,
|
||||
"rpc-gas-cap": 50000000,
|
||||
"rpc-tx-fee-cap": 100,
|
||||
"preimages-enabled": false,
|
||||
"pruning-enabled": true,
|
||||
"pruning-enabled": false,
|
||||
"allow-missing-tries": true,
|
||||
"snapshot-async": true,
|
||||
"snapshot-verification-enabled": false,
|
||||
"metrics-enabled": false,
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
"index-enabled":true,
|
||||
"log-display-level":"ERROR",
|
||||
"log-level": "INFO",
|
||||
"sybil-protection-enabled": false,
|
||||
"sybil-protection-disabled-weight": 100,
|
||||
"sybil-protection-enabled": true,
|
||||
"network-health-min-conn-peers": 0,
|
||||
"track-all-chains": true
|
||||
}
|
||||
|
||||
+119
-3
@@ -10,6 +10,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/luxfi/constants"
|
||||
@@ -335,6 +336,85 @@ func NewLocalConfig(binaryPath string, numNodes uint32) (network.Config, error)
|
||||
return NewConfigForNetwork(binaryPath, numNodes, configs.CustomID)
|
||||
}
|
||||
|
||||
// NewCanonicalMainnetConfig creates a mainnet network config using the CANONICAL genesis bytes.
|
||||
// This function loads the pre-serialized genesis.json file directly, ensuring byte-for-byte
|
||||
// deterministic output. Use this to avoid "db contains invalid genesis hash" errors on restart.
|
||||
//
|
||||
// CRITICAL: For mainnet/testnet, always use this function or NewCanonicalTestnetConfig
|
||||
// rather than functions that regenerate genesis (which causes non-deterministic bytes).
|
||||
func NewCanonicalMainnetConfig(binaryPath string, numNodes uint32) (network.Config, error) {
|
||||
return newCanonicalConfig(binaryPath, numNodes, configs.MainnetID, 9630)
|
||||
}
|
||||
|
||||
// NewCanonicalTestnetConfig creates a testnet network config using the CANONICAL genesis bytes.
|
||||
// See NewCanonicalMainnetConfig for details on why this is critical.
|
||||
func NewCanonicalTestnetConfig(binaryPath string, numNodes uint32) (network.Config, error) {
|
||||
return newCanonicalConfig(binaryPath, numNodes, configs.TestnetID, 9640)
|
||||
}
|
||||
|
||||
// NewCanonicalDevnetConfig creates a devnet network config using the CANONICAL genesis bytes.
|
||||
// See NewCanonicalMainnetConfig for details on why this is critical.
|
||||
func NewCanonicalDevnetConfig(binaryPath string, numNodes uint32) (network.Config, error) {
|
||||
return newCanonicalConfig(binaryPath, numNodes, configs.DevnetID, 9650)
|
||||
}
|
||||
|
||||
// NewCanonicalCustomConfig creates a custom (local) network config using the CANONICAL genesis bytes.
|
||||
// See NewCanonicalMainnetConfig for details on why this is critical.
|
||||
func NewCanonicalCustomConfig(binaryPath string, numNodes uint32) (network.Config, error) {
|
||||
return newCanonicalConfig(binaryPath, numNodes, configs.CustomID, 9660)
|
||||
}
|
||||
|
||||
// newCanonicalConfig creates a network config with canonical (pre-serialized) genesis bytes
|
||||
// and loads validator keys from ~/.lux/keys/node{0..n-1}/
|
||||
func newCanonicalConfig(binaryPath string, numNodes uint32, networkID uint32, portBase int) (network.Config, error) {
|
||||
// Load CANONICAL genesis bytes - no parsing/re-serialization
|
||||
genesisBytes, err := configs.GetCanonicalGenesisBytes(networkID)
|
||||
if err != nil {
|
||||
return network.Config{}, fmt.Errorf("failed to load canonical genesis: %w", err)
|
||||
}
|
||||
|
||||
// Start with default config
|
||||
netConfig := NewDefaultConfig(binaryPath)
|
||||
netConfig.Genesis = string(genesisBytes)
|
||||
|
||||
// Load validator keys from ~/.lux/keys/
|
||||
keysDir := os.Getenv("LUX_KEYS_DIR")
|
||||
if keysDir == "" {
|
||||
keysDir = validatorKeysDir()
|
||||
}
|
||||
ks := keys.NewKeyStore(keysDir)
|
||||
|
||||
// Load keys for each node
|
||||
nodeConfigs := make([]node.Config, numNodes)
|
||||
for i := uint32(0); i < numNodes; i++ {
|
||||
name := fmt.Sprintf("node%d", i)
|
||||
vk, err := ks.Load(name)
|
||||
if err != nil {
|
||||
return network.Config{}, fmt.Errorf("failed to load validator key %s from %s: %w (run 'lux key generate' first)", name, keysDir, err)
|
||||
}
|
||||
|
||||
port := portBase + int(i)*2
|
||||
nodeConfigs[i] = node.Config{
|
||||
Flags: map[string]interface{}{
|
||||
config.HTTPPortKey: port,
|
||||
config.StakingPortKey: port + 1,
|
||||
},
|
||||
StakingKey: string(vk.StakerKey),
|
||||
StakingCert: string(vk.StakerCert),
|
||||
StakingSigningKey: base64.StdEncoding.EncodeToString(vk.BLSSecretKey),
|
||||
IsBeacon: true,
|
||||
ChainConfigFiles: map[string]string{},
|
||||
UpgradeConfigFiles: map[string]string{},
|
||||
PChainConfigFiles: map[string]string{},
|
||||
}
|
||||
fmt.Printf(" Loaded validator %d: %s\n", i, vk.NodeID.String())
|
||||
}
|
||||
netConfig.NodeConfigs = nodeConfigs
|
||||
|
||||
fmt.Printf("✅ Loaded canonical genesis for network %d with %d validators\n", networkID, numNodes)
|
||||
return netConfig, nil
|
||||
}
|
||||
|
||||
// NewConfigForNetworkWithCustomGenesis creates a network config with a custom genesis string.
|
||||
// Use this for networks not defined in the configs package or for testing.
|
||||
func NewConfigForNetworkWithCustomGenesis(binaryPath string, numNodes uint32, genesisJSON string) (network.Config, error) {
|
||||
@@ -394,11 +474,15 @@ func NewConfigForNetworkWithCustomGenesis(binaryPath string, numNodes uint32, ge
|
||||
// - ec/private.key for P-Chain addresses (optional)
|
||||
func NewConfigWithPreExistingKeys(binaryPath string, networkID uint32, keysDir string) (network.Config, error) {
|
||||
// Determine port base based on network ID
|
||||
// Mainnet (96369): 9630 base
|
||||
// Testnet (96368): 9640 base
|
||||
// Mainnet (1): 9630 base
|
||||
// Testnet (2): 9640 base
|
||||
// Devnet (3): 9650 base
|
||||
portBase := 9630
|
||||
if networkID == configs.TestnetChainID {
|
||||
switch networkID {
|
||||
case constants.TestnetID: // 2
|
||||
portBase = 9640
|
||||
case constants.DevnetID: // 3
|
||||
portBase = 9650
|
||||
}
|
||||
|
||||
// Get genesis for the specified network
|
||||
@@ -920,6 +1004,38 @@ func NewConfigFromMnemonic(binaryPath string, networkID uint32, numNodes uint32)
|
||||
genesis["initialStakers"] = initialStakers
|
||||
genesis["allocations"] = allocations
|
||||
|
||||
// CRITICAL: Also update xChainGenesis.allocations!
|
||||
// The X-chain reads allocations from the embedded xChainGenesis JSON string,
|
||||
// NOT from the top-level allocations array. We must update both.
|
||||
if xChainGenesisStr, ok := genesis["xChainGenesis"].(string); ok {
|
||||
var xChainGenesis map[string]interface{}
|
||||
if err := json.Unmarshal([]byte(xChainGenesisStr), &xChainGenesis); err == nil {
|
||||
// Build X-chain specific allocations (only X-chain addresses, use avaxAddr format)
|
||||
xAllocations := make([]map[string]interface{}, 0)
|
||||
for _, alloc := range allocations {
|
||||
allocMap := alloc.(map[string]interface{})
|
||||
luxAddr, _ := allocMap["luxAddr"].(string)
|
||||
// Only include X-chain allocations (start with "X-")
|
||||
if strings.HasPrefix(luxAddr, "X-") {
|
||||
initialAmount, _ := allocMap["initialAmount"].(uint64)
|
||||
if initialAmount > 0 {
|
||||
xAllocations = append(xAllocations, map[string]interface{}{
|
||||
"avaxAddr": luxAddr, // X-chain uses avaxAddr field name
|
||||
"ethAddr": allocMap["ethAddr"],
|
||||
"initialAmount": initialAmount,
|
||||
"unlockSchedule": []interface{}{},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
xChainGenesis["allocations"] = xAllocations
|
||||
if updatedXChain, err := json.Marshal(xChainGenesis); err == nil {
|
||||
genesis["xChainGenesis"] = string(updatedXChain)
|
||||
fmt.Printf("✅ Updated xChainGenesis with %d allocations\n", len(xAllocations))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IMPORTANT: Set initialStakedFunds to EMPTY so that P-chain allocations
|
||||
// are NOT filtered by the builder. The builder filters allocations where
|
||||
// the underlying ShortID matches initialStakedFunds, which would incorrectly
|
||||
|
||||
+58
-7
@@ -11,9 +11,11 @@ import (
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/luxfi/netrunner/network/node"
|
||||
"github.com/luxfi/node/config"
|
||||
"github.com/luxfi/constants"
|
||||
"github.com/luxfi/log"
|
||||
"github.com/luxfi/netrunner/network/node"
|
||||
"github.com/luxfi/netrunner/utils"
|
||||
"github.com/luxfi/node/config"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -95,12 +97,50 @@ func writeFiles(networkID uint32, genesis []byte, nodeRootDir string, nodeConfig
|
||||
contents: decodedStakingSigningKey,
|
||||
},
|
||||
}
|
||||
// Always write genesis file if provided, even for LocalID
|
||||
// This allows custom genesis (e.g., with mnemonic-derived validators) to override defaults
|
||||
if len(genesis) > 0 {
|
||||
// Check if this is a resume from existing state:
|
||||
// If genesis.json already exists AND db directory has chain data, preserve existing genesis
|
||||
// This prevents the "db contains invalid genesis hash" error when resuming from snapshots
|
||||
// EXCEPTION: For canonical networks (mainnet/testnet), if the existing genesis has a different
|
||||
// networkID, we must clean the db and write the correct canonical genesis.
|
||||
existingGenesisPath := filepath.Join(nodeRootDir, genesisFileName)
|
||||
dbDir := filepath.Join(nodeRootDir, "db")
|
||||
shouldPreserveGenesis := false
|
||||
|
||||
if _, err := os.Stat(existingGenesisPath); err == nil {
|
||||
// Genesis exists, check if db has data (indicating we're resuming)
|
||||
if entries, err := os.ReadDir(dbDir); err == nil && len(entries) > 0 {
|
||||
shouldPreserveGenesis = true
|
||||
|
||||
// For canonical networks, verify the existing genesis matches
|
||||
if networkID == constants.MainnetID || networkID == constants.TestnetID {
|
||||
existingGenesisBytes, readErr := os.ReadFile(existingGenesisPath)
|
||||
if readErr == nil {
|
||||
existingNetworkID, parseErr := utils.NetworkIDFromGenesis(existingGenesisBytes)
|
||||
if parseErr == nil && existingNetworkID != networkID {
|
||||
// NetworkID mismatch! Clean db and write correct canonical genesis
|
||||
log.Info("Genesis networkID mismatch for canonical network, cleaning db",
|
||||
log.Uint32("existing", existingNetworkID),
|
||||
log.Uint32("target", networkID),
|
||||
log.String("dbDir", dbDir))
|
||||
if err := os.RemoveAll(dbDir); err != nil {
|
||||
return nil, fmt.Errorf("failed to clean db directory for canonical network: %w", err)
|
||||
}
|
||||
// Recreate empty db directory
|
||||
if err := os.MkdirAll(dbDir, 0o750); err != nil {
|
||||
return nil, fmt.Errorf("failed to recreate db directory: %w", err)
|
||||
}
|
||||
shouldPreserveGenesis = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(genesis) > 0 && !shouldPreserveGenesis {
|
||||
// Write new genesis (fresh start or custom genesis with mnemonic-derived validators)
|
||||
files = append(files, file{
|
||||
flagValue: filepath.Join(nodeRootDir, genesisFileName),
|
||||
path: filepath.Join(nodeRootDir, genesisFileName),
|
||||
flagValue: existingGenesisPath,
|
||||
path: existingGenesisPath,
|
||||
pathKey: config.GenesisFileKey,
|
||||
contents: genesis,
|
||||
})
|
||||
@@ -120,6 +160,10 @@ func writeFiles(networkID uint32, genesis []byte, nodeRootDir string, nodeConfig
|
||||
return nil, fmt.Errorf("couldn't write file at %q: %w", f.path, err)
|
||||
}
|
||||
}
|
||||
// If preserving existing genesis, add the flag without writing the file
|
||||
if shouldPreserveGenesis {
|
||||
flags[config.GenesisFileKey] = existingGenesisPath
|
||||
}
|
||||
// chain configs dir
|
||||
chainConfigDir := filepath.Join(nodeRootDir, chainConfigSubDir)
|
||||
if err := os.MkdirAll(chainConfigDir, 0o750); err != nil {
|
||||
@@ -139,6 +183,13 @@ func writeFiles(networkID uint32, genesis []byte, nodeRootDir string, nodeConfig
|
||||
return nil, fmt.Errorf("couldn't write file at %q: %w", chainConfigPath, err)
|
||||
}
|
||||
}
|
||||
// genesis files for EVM chains
|
||||
for chainAlias, genesisFile := range nodeConfig.GenesisConfigFiles {
|
||||
genesisPath := filepath.Join(chainConfigDir, chainAlias, genesisFileName)
|
||||
if err := createFileAndWrite(genesisPath, []byte(genesisFile)); err != nil {
|
||||
return nil, fmt.Errorf("couldn't write genesis file at %q: %w", genesisPath, err)
|
||||
}
|
||||
}
|
||||
// network upgrades
|
||||
for chainAlias, chainUpgradeFile := range nodeConfig.UpgradeConfigFiles {
|
||||
chainUpgradePath := filepath.Join(chainConfigDir, chainAlias, upgradeConfigFileName)
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
package local
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/btcsuite/btcd/btcutil/hdkeychain"
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
"github.com/luxfi/go-bip39"
|
||||
luxcrypto "github.com/luxfi/crypto/secp256k1"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
func TestMnemonicDerivation(t *testing.T) {
|
||||
mnemonic := os.Getenv("LUX_MNEMONIC")
|
||||
if mnemonic == "" {
|
||||
t.Skip("LUX_MNEMONIC not set")
|
||||
}
|
||||
|
||||
fmt.Printf("Mnemonic: %s...\n", mnemonic[:20])
|
||||
|
||||
if !bip39.IsMnemonicValid(mnemonic) {
|
||||
t.Fatal("Invalid mnemonic")
|
||||
}
|
||||
|
||||
seed := bip39.NewSeed(mnemonic, "")
|
||||
|
||||
// Derive using BIP44 m/44'/60'/0'/0/0
|
||||
masterKey, _ := hdkeychain.NewMaster(seed, &chaincfg.MainNetParams)
|
||||
purpose, _ := masterKey.Derive(hdkeychain.HardenedKeyStart + 44)
|
||||
coinType, _ := purpose.Derive(hdkeychain.HardenedKeyStart + 60)
|
||||
account, _ := coinType.Derive(hdkeychain.HardenedKeyStart + 0)
|
||||
change, _ := account.Derive(0)
|
||||
addressKey, _ := change.Derive(0)
|
||||
|
||||
ecPrivKey, _ := addressKey.ECPrivKey()
|
||||
privKeyBytes := ecPrivKey.Serialize()
|
||||
|
||||
privKey, err := luxcrypto.ToPrivateKey(privKeyBytes)
|
||||
if err != nil {
|
||||
t.Fatalf("Error: %v", err)
|
||||
}
|
||||
pubKey := privKey.PublicKey()
|
||||
shortID := pubKey.Address()
|
||||
|
||||
// Get Ethereum address
|
||||
pubKeyBytes := pubKey.Bytes()
|
||||
hash := sha3.NewLegacyKeccak256()
|
||||
hash.Write(pubKeyBytes[1:]) // skip the 0x04 prefix
|
||||
ethAddr := hash.Sum(nil)[12:]
|
||||
|
||||
fmt.Printf("\n=== Key Derivation Results (m/44'/60'/0'/0/0) ===\n")
|
||||
fmt.Printf("Lux Short ID: %s\n", shortID.String())
|
||||
fmt.Printf("Ethereum Address: 0x%x\n", ethAddr)
|
||||
fmt.Printf("Expected Treasury: 0x9011E888251AB053B7bD1cdB598Db4f9DEd94714\n")
|
||||
|
||||
// Also derive using coin type 9000 for comparison
|
||||
coinType9000, _ := purpose.Derive(hdkeychain.HardenedKeyStart + 9000)
|
||||
account9000, _ := coinType9000.Derive(hdkeychain.HardenedKeyStart + 0)
|
||||
change9000, _ := account9000.Derive(0)
|
||||
addressKey9000, _ := change9000.Derive(0)
|
||||
ecPrivKey9000, _ := addressKey9000.ECPrivKey()
|
||||
privKey9000, _ := luxcrypto.ToPrivateKey(ecPrivKey9000.Serialize())
|
||||
shortID9000 := privKey9000.PublicKey().Address()
|
||||
|
||||
fmt.Printf("\n=== Comparison: Coin Type 9000 (m/44'/9000'/0'/0/0) ===\n")
|
||||
fmt.Printf("Lux Short ID (9000): %s\n", shortID9000.String())
|
||||
|
||||
fmt.Printf("\n=== Genesis Treasury P-chain Address ===\n")
|
||||
fmt.Printf("Expected: P-lux1jqg73zp9r2c98daarnd4nrd5l80dj3c5eha5fl\n")
|
||||
}
|
||||
+7
-5
@@ -55,7 +55,7 @@ const (
|
||||
// healthCheckFreq reduced from 3s to 1s for faster health polling
|
||||
healthCheckFreq = 1 * time.Second
|
||||
DefaultNumNodes = 5
|
||||
snapshotPrefix = "anr-snapshot-"
|
||||
snapshotPrefix = "lux-snapshot-"
|
||||
networkRootDirPrefix = "network"
|
||||
defaultDBSubdir = "db"
|
||||
defaultLogsSubdir = "logs"
|
||||
@@ -114,6 +114,8 @@ type localNetwork struct {
|
||||
binaryPath string
|
||||
// chain config files to use per default
|
||||
chainConfigFiles map[string]string
|
||||
// genesis config files for EVM chains (per blockchain ID)
|
||||
genesisConfigFiles map[string]string
|
||||
// upgrade config files to use per default
|
||||
upgradeConfigFiles map[string]string
|
||||
// P-chain config files to use per default
|
||||
@@ -546,14 +548,14 @@ func (ln *localNetwork) loadConfig(ctx context.Context, networkConfig network.Co
|
||||
if ln.chainConfigFiles == nil {
|
||||
ln.chainConfigFiles = map[string]string{}
|
||||
}
|
||||
ln.genesisConfigFiles = networkConfig.GenesisConfigFiles
|
||||
if ln.genesisConfigFiles == nil {
|
||||
ln.genesisConfigFiles = map[string]string{}
|
||||
}
|
||||
ln.upgradeConfigFiles = networkConfig.UpgradeConfigFiles
|
||||
if ln.upgradeConfigFiles == nil {
|
||||
ln.upgradeConfigFiles = map[string]string{}
|
||||
}
|
||||
ln.chainConfigFiles = networkConfig.ChainConfigFiles
|
||||
if ln.chainConfigFiles == nil {
|
||||
ln.chainConfigFiles = map[string]string{}
|
||||
}
|
||||
|
||||
// Sort node configs so beacons start first
|
||||
var nodeConfigs []node.Config
|
||||
|
||||
+3
-2
@@ -12,15 +12,16 @@ import (
|
||||
"github.com/luxfi/netrunner/api"
|
||||
"github.com/luxfi/netrunner/network/node"
|
||||
"github.com/luxfi/netrunner/network/node/status"
|
||||
"github.com/luxfi/constants"
|
||||
"github.com/luxfi/ids"
|
||||
"github.com/luxfi/node/message"
|
||||
"github.com/luxfi/node/network/peer"
|
||||
"github.com/luxfi/node/network/throttling"
|
||||
"github.com/luxfi/node/network/tracker"
|
||||
"github.com/luxfi/node/utils/compression"
|
||||
"github.com/luxfi/consensus/validator" // package name is validators
|
||||
"github.com/luxfi/node/staking"
|
||||
"github.com/luxfi/node/utils"
|
||||
"github.com/luxfi/constants"
|
||||
"github.com/luxfi/crypto/bls"
|
||||
"github.com/luxfi/log"
|
||||
"github.com/luxfi/math/set"
|
||||
@@ -99,7 +100,7 @@ func (node *localNode) AttachPeer(ctx context.Context, router peer.InboundHandle
|
||||
}
|
||||
mc, err := message.NewCreator(
|
||||
prometheus.NewRegistry(),
|
||||
constants.DefaultNetworkCompressionType,
|
||||
compression.TypeZstd,
|
||||
10*time.Second,
|
||||
)
|
||||
if err != nil {
|
||||
|
||||
@@ -151,6 +151,7 @@ func (ln *localNetwork) SaveSnapshot(ctx context.Context, snapshotName string) (
|
||||
NodeConfigs: []node.Config{},
|
||||
BinaryPath: ln.binaryPath,
|
||||
ChainConfigFiles: ln.chainConfigFiles,
|
||||
GenesisConfigFiles: ln.genesisConfigFiles,
|
||||
UpgradeConfigFiles: ln.upgradeConfigFiles,
|
||||
PChainConfigFiles: ln.pChainConfigFiles,
|
||||
}
|
||||
@@ -293,6 +294,122 @@ func (ln *localNetwork) loadSnapshot(
|
||||
return ln.loadConfig(ctx, networkConfig)
|
||||
}
|
||||
|
||||
// SaveHotSnapshot saves a snapshot without stopping the network.
|
||||
// Uses copy-on-write on filesystems that support it (APFS on macOS).
|
||||
// WARNING: Hot snapshots may have minor inconsistencies if writes occur during copy.
|
||||
// For guaranteed consistency, use SaveSnapshot which stops the network.
|
||||
func (ln *localNetwork) SaveHotSnapshot(ctx context.Context, snapshotName string) (string, error) {
|
||||
ln.lock.RLock() // Read lock - doesn't block network operations
|
||||
defer ln.lock.RUnlock()
|
||||
|
||||
if ln.stopCalled() {
|
||||
return "", network.ErrStopped
|
||||
}
|
||||
if len(snapshotName) == 0 {
|
||||
return "", fmt.Errorf("invalid snapshotName %q", snapshotName)
|
||||
}
|
||||
// check if snapshot already exists
|
||||
snapshotDir := filepath.Join(ln.snapshotsDir, snapshotPrefix+snapshotName)
|
||||
if _, err := os.Stat(snapshotDir); err == nil {
|
||||
return "", fmt.Errorf("snapshot %q already exists", snapshotName)
|
||||
}
|
||||
|
||||
// Collect node info (read-only, safe during operation)
|
||||
nodesConfig := map[string]node.Config{}
|
||||
nodesDBDir := map[string]string{}
|
||||
for nodeName, node := range ln.nodes {
|
||||
nodeConfig := node.config
|
||||
nodeConfig.Flags = maps.Clone(nodeConfig.Flags)
|
||||
nodesConfig[nodeName] = nodeConfig
|
||||
nodesDBDir[nodeName] = node.GetDbDir()
|
||||
}
|
||||
|
||||
// Preserve current node ports
|
||||
for nodeName, nodeConfig := range nodesConfig {
|
||||
nodeConfig.Flags[config.HTTPPortKey] = ln.nodes[nodeName].GetAPIPort()
|
||||
nodeConfig.Flags[config.StakingPortKey] = ln.nodes[nodeName].GetP2PPort()
|
||||
}
|
||||
|
||||
// Make copy of network flags
|
||||
networkConfigFlags := maps.Clone(ln.flags)
|
||||
delete(networkConfigFlags, config.DataDirKey)
|
||||
delete(networkConfigFlags, config.LogsDirKey)
|
||||
for nodeName, nodeConfig := range nodesConfig {
|
||||
if nodeConfig.ConfigFile != "" {
|
||||
var err error
|
||||
nodeConfig.ConfigFile, err = utils.SetJSONKey(nodeConfig.ConfigFile, config.LogsDirKey, "")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
delete(nodeConfig.Flags, config.DataDirKey)
|
||||
delete(nodeConfig.Flags, config.LogsDirKey)
|
||||
nodesConfig[nodeName] = nodeConfig
|
||||
}
|
||||
|
||||
// Sync filesystem to flush pending writes
|
||||
syncFilesystem()
|
||||
|
||||
// Create main snapshot dirs
|
||||
snapshotDBDir := filepath.Join(snapshotDir, defaultDBSubdir)
|
||||
if err := os.MkdirAll(snapshotDBDir, os.ModePerm); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Copy db directories (hot - network still running)
|
||||
// On APFS (macOS), dircopy uses clonefile which is CoW and instant
|
||||
for _, nodeConfig := range nodesConfig {
|
||||
sourceDBDir, ok := nodesDBDir[nodeConfig.Name]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("failure obtaining db path for node %q", nodeConfig.Name)
|
||||
}
|
||||
sourceDBDir = filepath.Join(sourceDBDir, constants.NetworkName(ln.networkID))
|
||||
targetDBDir := filepath.Join(filepath.Join(snapshotDBDir, nodeConfig.Name), constants.NetworkName(ln.networkID))
|
||||
if err := dircopy.Copy(sourceDBDir, targetDBDir); err != nil {
|
||||
return "", fmt.Errorf("failure saving node %q db dir: %w", nodeConfig.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Save network config
|
||||
networkConfig := network.Config{
|
||||
Genesis: string(ln.genesis),
|
||||
Flags: networkConfigFlags,
|
||||
NodeConfigs: []node.Config{},
|
||||
BinaryPath: ln.binaryPath,
|
||||
ChainConfigFiles: ln.chainConfigFiles,
|
||||
GenesisConfigFiles: ln.genesisConfigFiles,
|
||||
UpgradeConfigFiles: ln.upgradeConfigFiles,
|
||||
PChainConfigFiles: ln.pChainConfigFiles,
|
||||
}
|
||||
networkConfig.NodeConfigs = append(networkConfig.NodeConfigs, maps.Values(nodesConfig)...)
|
||||
networkConfigJSON, err := json.MarshalIndent(networkConfig, "", " ")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := createFileAndWrite(filepath.Join(snapshotDir, "network.json"), networkConfigJSON); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Save dynamic network state
|
||||
chainID2ElasticChainID := map[string]string{}
|
||||
for chainID, elasticChainID := range ln.chainID2ElasticChainID {
|
||||
chainID2ElasticChainID[chainID.String()] = elasticChainID.String()
|
||||
}
|
||||
networkState := NetworkState{
|
||||
ChainID2ElasticChainID: chainID2ElasticChainID,
|
||||
}
|
||||
networkStateJSON, err := json.MarshalIndent(networkState, "", " ")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := createFileAndWrite(filepath.Join(snapshotDir, "state.json"), networkStateJSON); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
ln.logger.Info("Hot snapshot saved", log.String("snapshot", snapshotName), log.String("path", snapshotDir))
|
||||
return snapshotDir, nil
|
||||
}
|
||||
|
||||
// Remove network snapshot
|
||||
func (ln *localNetwork) RemoveSnapshot(snapshotName string) error {
|
||||
snapshotDir := filepath.Join(ln.snapshotsDir, snapshotPrefix+snapshotName)
|
||||
|
||||
@@ -71,6 +71,8 @@ type Config struct {
|
||||
BinaryPath string `json:"binaryPath"`
|
||||
// Chain config files to use per default, if not specified in node config
|
||||
ChainConfigFiles map[string]string `json:"chainConfigFiles"`
|
||||
// Genesis config files for EVM chains (per blockchain ID)
|
||||
GenesisConfigFiles map[string]string `json:"genesisConfigFiles"`
|
||||
// Upgrade config files to use per default, if not specified in node config
|
||||
UpgradeConfigFiles map[string]string `json:"upgradeConfigFiles"`
|
||||
// P-Chain config files to use per default, if not specified in node config
|
||||
|
||||
@@ -99,6 +99,10 @@ type Network interface {
|
||||
// Network is stopped in order to do a safe preservation
|
||||
// Returns the full local path to the snapshot dir
|
||||
SaveSnapshot(context.Context, string) (string, error)
|
||||
// SaveHotSnapshot saves a snapshot without stopping the network
|
||||
// Uses CoW on APFS (macOS) for instant snapshots
|
||||
// WARNING: May have minor inconsistencies if writes occur during copy
|
||||
SaveHotSnapshot(context.Context, string) (string, error)
|
||||
// Remove network snapshot
|
||||
RemoveSnapshot(string) error
|
||||
// Get name of available snapshots
|
||||
|
||||
@@ -79,6 +79,8 @@ type Config struct {
|
||||
ConfigFile string `json:"configFile"`
|
||||
// May be nil.
|
||||
ChainConfigFiles map[string]string `json:"chainConfigFiles"`
|
||||
// May be nil. Genesis files for EVM chains (genesis.json)
|
||||
GenesisConfigFiles map[string]string `json:"genesisConfigFiles"`
|
||||
// May be nil.
|
||||
UpgradeConfigFiles map[string]string `json:"upgradeConfigFiles"`
|
||||
// May be nil. P-Chain chain (formerly subnet) config files.
|
||||
|
||||
+35
-32
@@ -3796,7 +3796,7 @@ const file_rpcpb_rpc_proto_rawDesc = "" +
|
||||
"\x18GetSnapshotNamesResponse\x12%\n" +
|
||||
"\x0esnapshot_names\x18\x01 \x03(\tR\rsnapshotNames2S\n" +
|
||||
"\vPingService\x12D\n" +
|
||||
"\x04Ping\x12\x12.rpcpb.PingRequest\x1a\x13.rpcpb.PingResponse\"\x13\x82\xd3\xe4\x93\x02\r:\x01*\"\b/v1/ping2\xe7\x14\n" +
|
||||
"\x04Ping\x12\x12.rpcpb.PingRequest\x1a\x13.rpcpb.PingResponse\"\x13\x82\xd3\xe4\x93\x02\r:\x01*\"\b/v1/ping2\xdb\x15\n" +
|
||||
"\x0eControlService\x12d\n" +
|
||||
"\n" +
|
||||
"RPCVersion\x12\x18.rpcpb.RPCVersionRequest\x1a\x19.rpcpb.RPCVersionResponse\"!\x82\xd3\xe4\x93\x02\x1b:\x01*\"\x16/v1/control/rpcversion\x12P\n" +
|
||||
@@ -3822,7 +3822,8 @@ const file_rpcpb_rpc_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"AttachPeer\x12\x18.rpcpb.AttachPeerRequest\x1a\x19.rpcpb.AttachPeerResponse\"!\x82\xd3\xe4\x93\x02\x1b:\x01*\"\x16/v1/control/attachpeer\x12\x88\x01\n" +
|
||||
"\x13SendOutboundMessage\x12!.rpcpb.SendOutboundMessageRequest\x1a\".rpcpb.SendOutboundMessageResponse\"*\x82\xd3\xe4\x93\x02$:\x01*\"\x1f/v1/control/sendoutboundmessage\x12l\n" +
|
||||
"\fSaveSnapshot\x12\x1a.rpcpb.SaveSnapshotRequest\x1a\x1b.rpcpb.SaveSnapshotResponse\"#\x82\xd3\xe4\x93\x02\x1d:\x01*\"\x18/v1/control/savesnapshot\x12l\n" +
|
||||
"\fSaveSnapshot\x12\x1a.rpcpb.SaveSnapshotRequest\x1a\x1b.rpcpb.SaveSnapshotResponse\"#\x82\xd3\xe4\x93\x02\x1d:\x01*\"\x18/v1/control/savesnapshot\x12r\n" +
|
||||
"\x0fSaveHotSnapshot\x12\x1a.rpcpb.SaveSnapshotRequest\x1a\x1b.rpcpb.SaveSnapshotResponse\"&\x82\xd3\xe4\x93\x02 :\x01*\"\x1b/v1/control/savehotsnapshot\x12l\n" +
|
||||
"\fLoadSnapshot\x12\x1a.rpcpb.LoadSnapshotRequest\x1a\x1b.rpcpb.LoadSnapshotResponse\"#\x82\xd3\xe4\x93\x02\x1d:\x01*\"\x18/v1/control/loadsnapshot\x12t\n" +
|
||||
"\x0eRemoveSnapshot\x12\x1c.rpcpb.RemoveSnapshotRequest\x1a\x1d.rpcpb.RemoveSnapshotResponse\"%\x82\xd3\xe4\x93\x02\x1f:\x01*\"\x1a/v1/control/removesnapshot\x12|\n" +
|
||||
"\x10GetSnapshotNames\x12\x1e.rpcpb.GetSnapshotNamesRequest\x1a\x1f.rpcpb.GetSnapshotNamesResponse\"'\x82\xd3\xe4\x93\x02!:\x01*\"\x1c/v1/control/getsnapshotnamesB\"Z github.com/luxfi/netrunner;rpcpbb\x06proto3"
|
||||
@@ -3993,36 +3994,38 @@ var file_rpcpb_rpc_proto_depIdxs = []int32{
|
||||
50, // 68: rpcpb.ControlService.AttachPeer:input_type -> rpcpb.AttachPeerRequest
|
||||
52, // 69: rpcpb.ControlService.SendOutboundMessage:input_type -> rpcpb.SendOutboundMessageRequest
|
||||
54, // 70: rpcpb.ControlService.SaveSnapshot:input_type -> rpcpb.SaveSnapshotRequest
|
||||
56, // 71: rpcpb.ControlService.LoadSnapshot:input_type -> rpcpb.LoadSnapshotRequest
|
||||
58, // 72: rpcpb.ControlService.RemoveSnapshot:input_type -> rpcpb.RemoveSnapshotRequest
|
||||
60, // 73: rpcpb.ControlService.GetSnapshotNames:input_type -> rpcpb.GetSnapshotNamesRequest
|
||||
1, // 74: rpcpb.PingService.Ping:output_type -> rpcpb.PingResponse
|
||||
11, // 75: rpcpb.ControlService.RPCVersion:output_type -> rpcpb.RPCVersionResponse
|
||||
12, // 76: rpcpb.ControlService.Start:output_type -> rpcpb.StartResponse
|
||||
25, // 77: rpcpb.ControlService.CreateBlockchains:output_type -> rpcpb.CreateBlockchainsResponse
|
||||
16, // 78: rpcpb.ControlService.TransformElasticChains:output_type -> rpcpb.TransformElasticChainsResponse
|
||||
19, // 79: rpcpb.ControlService.AddPermissionlessValidator:output_type -> rpcpb.AddPermissionlessValidatorResponse
|
||||
22, // 80: rpcpb.ControlService.RemoveChainValidator:output_type -> rpcpb.RemoveChainValidatorResponse
|
||||
27, // 81: rpcpb.ControlService.CreateChains:output_type -> rpcpb.CreateChainsResponse
|
||||
29, // 82: rpcpb.ControlService.Health:output_type -> rpcpb.HealthResponse
|
||||
31, // 83: rpcpb.ControlService.URIs:output_type -> rpcpb.URIsResponse
|
||||
33, // 84: rpcpb.ControlService.WaitForHealthy:output_type -> rpcpb.WaitForHealthyResponse
|
||||
35, // 85: rpcpb.ControlService.Status:output_type -> rpcpb.StatusResponse
|
||||
37, // 86: rpcpb.ControlService.StreamStatus:output_type -> rpcpb.StreamStatusResponse
|
||||
41, // 87: rpcpb.ControlService.RemoveNode:output_type -> rpcpb.RemoveNodeResponse
|
||||
47, // 88: rpcpb.ControlService.AddNode:output_type -> rpcpb.AddNodeResponse
|
||||
39, // 89: rpcpb.ControlService.RestartNode:output_type -> rpcpb.RestartNodeResponse
|
||||
43, // 90: rpcpb.ControlService.PauseNode:output_type -> rpcpb.PauseNodeResponse
|
||||
45, // 91: rpcpb.ControlService.ResumeNode:output_type -> rpcpb.ResumeNodeResponse
|
||||
49, // 92: rpcpb.ControlService.Stop:output_type -> rpcpb.StopResponse
|
||||
51, // 93: rpcpb.ControlService.AttachPeer:output_type -> rpcpb.AttachPeerResponse
|
||||
53, // 94: rpcpb.ControlService.SendOutboundMessage:output_type -> rpcpb.SendOutboundMessageResponse
|
||||
55, // 95: rpcpb.ControlService.SaveSnapshot:output_type -> rpcpb.SaveSnapshotResponse
|
||||
57, // 96: rpcpb.ControlService.LoadSnapshot:output_type -> rpcpb.LoadSnapshotResponse
|
||||
59, // 97: rpcpb.ControlService.RemoveSnapshot:output_type -> rpcpb.RemoveSnapshotResponse
|
||||
61, // 98: rpcpb.ControlService.GetSnapshotNames:output_type -> rpcpb.GetSnapshotNamesResponse
|
||||
74, // [74:99] is the sub-list for method output_type
|
||||
49, // [49:74] is the sub-list for method input_type
|
||||
54, // 71: rpcpb.ControlService.SaveHotSnapshot:input_type -> rpcpb.SaveSnapshotRequest
|
||||
56, // 72: rpcpb.ControlService.LoadSnapshot:input_type -> rpcpb.LoadSnapshotRequest
|
||||
58, // 73: rpcpb.ControlService.RemoveSnapshot:input_type -> rpcpb.RemoveSnapshotRequest
|
||||
60, // 74: rpcpb.ControlService.GetSnapshotNames:input_type -> rpcpb.GetSnapshotNamesRequest
|
||||
1, // 75: rpcpb.PingService.Ping:output_type -> rpcpb.PingResponse
|
||||
11, // 76: rpcpb.ControlService.RPCVersion:output_type -> rpcpb.RPCVersionResponse
|
||||
12, // 77: rpcpb.ControlService.Start:output_type -> rpcpb.StartResponse
|
||||
25, // 78: rpcpb.ControlService.CreateBlockchains:output_type -> rpcpb.CreateBlockchainsResponse
|
||||
16, // 79: rpcpb.ControlService.TransformElasticChains:output_type -> rpcpb.TransformElasticChainsResponse
|
||||
19, // 80: rpcpb.ControlService.AddPermissionlessValidator:output_type -> rpcpb.AddPermissionlessValidatorResponse
|
||||
22, // 81: rpcpb.ControlService.RemoveChainValidator:output_type -> rpcpb.RemoveChainValidatorResponse
|
||||
27, // 82: rpcpb.ControlService.CreateChains:output_type -> rpcpb.CreateChainsResponse
|
||||
29, // 83: rpcpb.ControlService.Health:output_type -> rpcpb.HealthResponse
|
||||
31, // 84: rpcpb.ControlService.URIs:output_type -> rpcpb.URIsResponse
|
||||
33, // 85: rpcpb.ControlService.WaitForHealthy:output_type -> rpcpb.WaitForHealthyResponse
|
||||
35, // 86: rpcpb.ControlService.Status:output_type -> rpcpb.StatusResponse
|
||||
37, // 87: rpcpb.ControlService.StreamStatus:output_type -> rpcpb.StreamStatusResponse
|
||||
41, // 88: rpcpb.ControlService.RemoveNode:output_type -> rpcpb.RemoveNodeResponse
|
||||
47, // 89: rpcpb.ControlService.AddNode:output_type -> rpcpb.AddNodeResponse
|
||||
39, // 90: rpcpb.ControlService.RestartNode:output_type -> rpcpb.RestartNodeResponse
|
||||
43, // 91: rpcpb.ControlService.PauseNode:output_type -> rpcpb.PauseNodeResponse
|
||||
45, // 92: rpcpb.ControlService.ResumeNode:output_type -> rpcpb.ResumeNodeResponse
|
||||
49, // 93: rpcpb.ControlService.Stop:output_type -> rpcpb.StopResponse
|
||||
51, // 94: rpcpb.ControlService.AttachPeer:output_type -> rpcpb.AttachPeerResponse
|
||||
53, // 95: rpcpb.ControlService.SendOutboundMessage:output_type -> rpcpb.SendOutboundMessageResponse
|
||||
55, // 96: rpcpb.ControlService.SaveSnapshot:output_type -> rpcpb.SaveSnapshotResponse
|
||||
55, // 97: rpcpb.ControlService.SaveHotSnapshot:output_type -> rpcpb.SaveSnapshotResponse
|
||||
57, // 98: rpcpb.ControlService.LoadSnapshot:output_type -> rpcpb.LoadSnapshotResponse
|
||||
59, // 99: rpcpb.ControlService.RemoveSnapshot:output_type -> rpcpb.RemoveSnapshotResponse
|
||||
61, // 100: rpcpb.ControlService.GetSnapshotNames:output_type -> rpcpb.GetSnapshotNamesResponse
|
||||
75, // [75:101] is the sub-list for method output_type
|
||||
49, // [49:75] is the sub-list for method input_type
|
||||
49, // [49:49] is the sub-list for extension type_name
|
||||
49, // [49:49] is the sub-list for extension extendee
|
||||
0, // [0:49] is the sub-list for field type_name
|
||||
|
||||
@@ -625,6 +625,33 @@ func local_request_ControlService_SaveSnapshot_0(ctx context.Context, marshaler
|
||||
return msg, metadata, err
|
||||
}
|
||||
|
||||
func request_ControlService_SaveHotSnapshot_0(ctx context.Context, marshaler runtime.Marshaler, client ControlServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var (
|
||||
protoReq SaveSnapshotRequest
|
||||
metadata runtime.ServerMetadata
|
||||
)
|
||||
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
if req.Body != nil {
|
||||
_, _ = io.Copy(io.Discard, req.Body)
|
||||
}
|
||||
msg, err := client.SaveHotSnapshot(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
}
|
||||
|
||||
func local_request_ControlService_SaveHotSnapshot_0(ctx context.Context, marshaler runtime.Marshaler, server ControlServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var (
|
||||
protoReq SaveSnapshotRequest
|
||||
metadata runtime.ServerMetadata
|
||||
)
|
||||
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
msg, err := server.SaveHotSnapshot(ctx, &protoReq)
|
||||
return msg, metadata, err
|
||||
}
|
||||
|
||||
func request_ControlService_LoadSnapshot_0(ctx context.Context, marshaler runtime.Marshaler, client ControlServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var (
|
||||
protoReq LoadSnapshotRequest
|
||||
@@ -1149,6 +1176,26 @@ func RegisterControlServiceHandlerServer(ctx context.Context, mux *runtime.Serve
|
||||
}
|
||||
forward_ControlService_SaveSnapshot_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
})
|
||||
mux.Handle(http.MethodPost, pattern_ControlService_SaveHotSnapshot_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
var stream runtime.ServerTransportStream
|
||||
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/rpcpb.ControlService/SaveHotSnapshot", runtime.WithHTTPPathPattern("/v1/control/savehotsnapshot"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := local_request_ControlService_SaveHotSnapshot_0(annotatedContext, inboundMarshaler, server, req, pathParams)
|
||||
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
forward_ControlService_SaveHotSnapshot_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
})
|
||||
mux.Handle(http.MethodPost, pattern_ControlService_LoadSnapshot_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
@@ -1670,6 +1717,23 @@ func RegisterControlServiceHandlerClient(ctx context.Context, mux *runtime.Serve
|
||||
}
|
||||
forward_ControlService_SaveSnapshot_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
})
|
||||
mux.Handle(http.MethodPost, pattern_ControlService_SaveHotSnapshot_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/rpcpb.ControlService/SaveHotSnapshot", runtime.WithHTTPPathPattern("/v1/control/savehotsnapshot"))
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_ControlService_SaveHotSnapshot_0(annotatedContext, inboundMarshaler, client, req, pathParams)
|
||||
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
forward_ControlService_SaveHotSnapshot_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
})
|
||||
mux.Handle(http.MethodPost, pattern_ControlService_LoadSnapshot_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
@@ -1746,6 +1810,7 @@ var (
|
||||
pattern_ControlService_AttachPeer_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "control", "attachpeer"}, ""))
|
||||
pattern_ControlService_SendOutboundMessage_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "control", "sendoutboundmessage"}, ""))
|
||||
pattern_ControlService_SaveSnapshot_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "control", "savesnapshot"}, ""))
|
||||
pattern_ControlService_SaveHotSnapshot_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "control", "savehotsnapshot"}, ""))
|
||||
pattern_ControlService_LoadSnapshot_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "control", "loadsnapshot"}, ""))
|
||||
pattern_ControlService_RemoveSnapshot_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "control", "removesnapshot"}, ""))
|
||||
pattern_ControlService_GetSnapshotNames_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "control", "getsnapshotnames"}, ""))
|
||||
@@ -1773,6 +1838,7 @@ var (
|
||||
forward_ControlService_AttachPeer_0 = runtime.ForwardResponseMessage
|
||||
forward_ControlService_SendOutboundMessage_0 = runtime.ForwardResponseMessage
|
||||
forward_ControlService_SaveSnapshot_0 = runtime.ForwardResponseMessage
|
||||
forward_ControlService_SaveHotSnapshot_0 = runtime.ForwardResponseMessage
|
||||
forward_ControlService_LoadSnapshot_0 = runtime.ForwardResponseMessage
|
||||
forward_ControlService_RemoveSnapshot_0 = runtime.ForwardResponseMessage
|
||||
forward_ControlService_GetSnapshotNames_0 = runtime.ForwardResponseMessage
|
||||
|
||||
@@ -169,6 +169,15 @@ service ControlService {
|
||||
};
|
||||
}
|
||||
|
||||
// SaveHotSnapshot saves a snapshot without stopping the network
|
||||
// Uses Copy-on-Write on APFS (macOS) for instant snapshots
|
||||
rpc SaveHotSnapshot(SaveSnapshotRequest) returns (SaveSnapshotResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v1/control/savehotsnapshot"
|
||||
body: "*"
|
||||
};
|
||||
}
|
||||
|
||||
rpc LoadSnapshot(LoadSnapshotRequest) returns (LoadSnapshotResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v1/control/loadsnapshot"
|
||||
|
||||
@@ -142,6 +142,7 @@ const (
|
||||
ControlService_AttachPeer_FullMethodName = "/rpcpb.ControlService/AttachPeer"
|
||||
ControlService_SendOutboundMessage_FullMethodName = "/rpcpb.ControlService/SendOutboundMessage"
|
||||
ControlService_SaveSnapshot_FullMethodName = "/rpcpb.ControlService/SaveSnapshot"
|
||||
ControlService_SaveHotSnapshot_FullMethodName = "/rpcpb.ControlService/SaveHotSnapshot"
|
||||
ControlService_LoadSnapshot_FullMethodName = "/rpcpb.ControlService/LoadSnapshot"
|
||||
ControlService_RemoveSnapshot_FullMethodName = "/rpcpb.ControlService/RemoveSnapshot"
|
||||
ControlService_GetSnapshotNames_FullMethodName = "/rpcpb.ControlService/GetSnapshotNames"
|
||||
@@ -172,6 +173,9 @@ type ControlServiceClient interface {
|
||||
AttachPeer(ctx context.Context, in *AttachPeerRequest, opts ...grpc.CallOption) (*AttachPeerResponse, error)
|
||||
SendOutboundMessage(ctx context.Context, in *SendOutboundMessageRequest, opts ...grpc.CallOption) (*SendOutboundMessageResponse, error)
|
||||
SaveSnapshot(ctx context.Context, in *SaveSnapshotRequest, opts ...grpc.CallOption) (*SaveSnapshotResponse, error)
|
||||
// SaveHotSnapshot saves a snapshot without stopping the network
|
||||
// Uses Copy-on-Write on APFS (macOS) for instant snapshots
|
||||
SaveHotSnapshot(ctx context.Context, in *SaveSnapshotRequest, opts ...grpc.CallOption) (*SaveSnapshotResponse, error)
|
||||
LoadSnapshot(ctx context.Context, in *LoadSnapshotRequest, opts ...grpc.CallOption) (*LoadSnapshotResponse, error)
|
||||
RemoveSnapshot(ctx context.Context, in *RemoveSnapshotRequest, opts ...grpc.CallOption) (*RemoveSnapshotResponse, error)
|
||||
GetSnapshotNames(ctx context.Context, in *GetSnapshotNamesRequest, opts ...grpc.CallOption) (*GetSnapshotNamesResponse, error)
|
||||
@@ -404,6 +408,16 @@ func (c *controlServiceClient) SaveSnapshot(ctx context.Context, in *SaveSnapsho
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *controlServiceClient) SaveHotSnapshot(ctx context.Context, in *SaveSnapshotRequest, opts ...grpc.CallOption) (*SaveSnapshotResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(SaveSnapshotResponse)
|
||||
err := c.cc.Invoke(ctx, ControlService_SaveHotSnapshot_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *controlServiceClient) LoadSnapshot(ctx context.Context, in *LoadSnapshotRequest, opts ...grpc.CallOption) (*LoadSnapshotResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(LoadSnapshotResponse)
|
||||
@@ -459,6 +473,9 @@ type ControlServiceServer interface {
|
||||
AttachPeer(context.Context, *AttachPeerRequest) (*AttachPeerResponse, error)
|
||||
SendOutboundMessage(context.Context, *SendOutboundMessageRequest) (*SendOutboundMessageResponse, error)
|
||||
SaveSnapshot(context.Context, *SaveSnapshotRequest) (*SaveSnapshotResponse, error)
|
||||
// SaveHotSnapshot saves a snapshot without stopping the network
|
||||
// Uses Copy-on-Write on APFS (macOS) for instant snapshots
|
||||
SaveHotSnapshot(context.Context, *SaveSnapshotRequest) (*SaveSnapshotResponse, error)
|
||||
LoadSnapshot(context.Context, *LoadSnapshotRequest) (*LoadSnapshotResponse, error)
|
||||
RemoveSnapshot(context.Context, *RemoveSnapshotRequest) (*RemoveSnapshotResponse, error)
|
||||
GetSnapshotNames(context.Context, *GetSnapshotNamesRequest) (*GetSnapshotNamesResponse, error)
|
||||
@@ -535,6 +552,9 @@ func (UnimplementedControlServiceServer) SendOutboundMessage(context.Context, *S
|
||||
func (UnimplementedControlServiceServer) SaveSnapshot(context.Context, *SaveSnapshotRequest) (*SaveSnapshotResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method SaveSnapshot not implemented")
|
||||
}
|
||||
func (UnimplementedControlServiceServer) SaveHotSnapshot(context.Context, *SaveSnapshotRequest) (*SaveSnapshotResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method SaveHotSnapshot not implemented")
|
||||
}
|
||||
func (UnimplementedControlServiceServer) LoadSnapshot(context.Context, *LoadSnapshotRequest) (*LoadSnapshotResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method LoadSnapshot not implemented")
|
||||
}
|
||||
@@ -936,6 +956,24 @@ func _ControlService_SaveSnapshot_Handler(srv interface{}, ctx context.Context,
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _ControlService_SaveHotSnapshot_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(SaveSnapshotRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ControlServiceServer).SaveHotSnapshot(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: ControlService_SaveHotSnapshot_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ControlServiceServer).SaveHotSnapshot(ctx, req.(*SaveSnapshotRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _ControlService_LoadSnapshot_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(LoadSnapshotRequest)
|
||||
if err := dec(in); err != nil {
|
||||
@@ -1077,6 +1115,10 @@ var ControlService_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "SaveSnapshot",
|
||||
Handler: _ControlService_SaveSnapshot_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "SaveHotSnapshot",
|
||||
Handler: _ControlService_SaveHotSnapshot_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "LoadSnapshot",
|
||||
Handler: _ControlService_LoadSnapshot_Handler,
|
||||
|
||||
+93
-8
@@ -137,6 +137,7 @@ func newLocalNetwork(opts localNetworkOptions) (*localNetwork, error) {
|
||||
// TODO document.
|
||||
// Assumes [lc.lock] is held.
|
||||
func (lc *localNetwork) createConfig() error {
|
||||
lc.log.Info("createConfig() ENTERED", "globalNodeConfig", lc.options.globalNodeConfig)
|
||||
var globalConfig map[string]interface{}
|
||||
|
||||
if lc.options.globalNodeConfig != "" {
|
||||
@@ -164,36 +165,82 @@ func (lc *localNetwork) createConfig() error {
|
||||
}
|
||||
}
|
||||
|
||||
lc.log.Info("createConfig networkID parsed", "networkID", networkID, "MainnetID", luxd_constants.MainnetID, "TestnetID", luxd_constants.TestnetID)
|
||||
|
||||
// CRITICAL: Check if we're resuming from existing state.
|
||||
// If node1/genesis.json exists AND node1/db has data, we MUST use the existing genesis
|
||||
// to avoid "db contains invalid genesis hash" errors. JSON serialization is non-deterministic
|
||||
// due to Go map iteration order, so regenerating genesis produces different bytes.
|
||||
existingGenesis, isResume := lc.checkForExistingGenesis()
|
||||
if isResume {
|
||||
ux.Print(lc.log, log.Green.Wrap("📂 RESUME DETECTED: Using existing genesis from %s"), lc.options.rootDataDir)
|
||||
fmt.Fprintf(os.Stderr, "📂 RESUME DETECTED: Using existing genesis from %s\n", lc.options.rootDataDir)
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "DEBUG: Not resuming - checkForExistingGenesis returned false (rootDataDir=%s)\n", lc.options.rootDataDir)
|
||||
}
|
||||
|
||||
// Use the appropriate genesis configuration based on network ID
|
||||
// If LUX_MNEMONIC is set, derive validators from it (preferred for mainnet/testnet)
|
||||
// CRITICAL: For mainnet/testnet, ALWAYS use canonical genesis to ensure byte-for-byte
|
||||
// deterministic output. This prevents "db contains invalid genesis hash" errors on restart.
|
||||
// The canonical genesis files are pre-serialized and never re-generated.
|
||||
|
||||
// Check if LUX_MNEMONIC is set - if so, use mnemonic-based config to generate
|
||||
// genesis with funds allocated to the mnemonic-derived address
|
||||
mnemonic := os.Getenv("LUX_MNEMONIC")
|
||||
useMnemonic := mnemonic != ""
|
||||
fmt.Printf("🔍 DEBUG: LUX_MNEMONIC set: %v (len=%d)\n", useMnemonic, len(mnemonic))
|
||||
|
||||
switch networkID {
|
||||
case luxd_constants.MainnetID: // LUX Mainnet (1)
|
||||
if useMnemonic {
|
||||
fmt.Printf("🔑 Using NewMainnetConfigFromMnemonic (mnemonic-derived keys)\n")
|
||||
ux.Print(lc.log, "%s", log.Green.Wrap("Loading mainnet genesis FROM MNEMONIC (funds allocated to derived address)"))
|
||||
cfg, err = local.NewMainnetConfigFromMnemonic(lc.options.execPath, lc.options.numNodes)
|
||||
} else {
|
||||
fmt.Printf("📦 Using NewMainnetConfig (embedded keys)\n")
|
||||
cfg, err = local.NewMainnetConfig(lc.options.execPath, lc.options.numNodes)
|
||||
// ALWAYS use canonical genesis for mainnet - never regenerate
|
||||
ux.Print(lc.log, "%s", log.Green.Wrap("Loading CANONICAL mainnet genesis (deterministic bytes)"))
|
||||
cfg, err = local.NewCanonicalMainnetConfig(lc.options.execPath, lc.options.numNodes)
|
||||
}
|
||||
case luxd_constants.TestnetID: // LUX Testnet (2)
|
||||
if useMnemonic {
|
||||
ux.Print(lc.log, "%s", log.Green.Wrap("Loading testnet genesis FROM MNEMONIC (funds allocated to derived address)"))
|
||||
cfg, err = local.NewTestnetConfigFromMnemonic(lc.options.execPath, lc.options.numNodes)
|
||||
} else {
|
||||
// Use pre-existing keys from ~/.lux/keys if available
|
||||
fmt.Printf("📦 Using NewTestnetConfigWithKeys (pre-existing keys from ~/.lux/keys)\n")
|
||||
cfg, err = local.NewTestnetConfigWithKeys(lc.options.execPath, "")
|
||||
// ALWAYS use canonical genesis for testnet - never regenerate
|
||||
ux.Print(lc.log, "%s", log.Green.Wrap("Loading CANONICAL testnet genesis (deterministic bytes)"))
|
||||
cfg, err = local.NewCanonicalTestnetConfig(lc.options.execPath, lc.options.numNodes)
|
||||
}
|
||||
case luxd_constants.DevnetID: // LUX Devnet (3)
|
||||
// ALWAYS use canonical genesis for devnet - never regenerate
|
||||
ux.Print(lc.log, "%s", log.Green.Wrap("Loading CANONICAL devnet genesis (deterministic bytes)"))
|
||||
cfg, err = local.NewCanonicalDevnetConfig(lc.options.execPath, lc.options.numNodes)
|
||||
case luxd_constants.CustomID: // Custom/Local (1337)
|
||||
// ALWAYS use canonical genesis for custom network - never regenerate
|
||||
ux.Print(lc.log, "%s", log.Green.Wrap("Loading CANONICAL custom genesis (deterministic bytes)"))
|
||||
cfg, err = local.NewCanonicalCustomConfig(lc.options.execPath, lc.options.numNodes)
|
||||
default:
|
||||
// Fallback for unknown network IDs
|
||||
ux.Print(lc.log, "%s", log.Orange.Wrap(fmt.Sprintf("Unknown network ID %d, using default config", networkID)))
|
||||
cfg, err = local.NewDefaultConfigNNodes(lc.options.execPath, lc.options.numNodes)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// For resume scenarios, verify genesis matches (but don't override for mainnet/testnet
|
||||
// since canonical genesis is already deterministic)
|
||||
if isResume && existingGenesis != "" {
|
||||
if cfg.Genesis != existingGenesis {
|
||||
if networkID == luxd_constants.MainnetID || networkID == luxd_constants.TestnetID {
|
||||
// Log warning but use canonical - this shouldn't happen with proper setup
|
||||
ux.Print(lc.log, "%s", log.Orange.Wrap("WARNING: Existing genesis differs from canonical. Using canonical."))
|
||||
fmt.Fprintf(os.Stderr, "WARNING: Genesis mismatch detected. Using canonical genesis.\n")
|
||||
} else {
|
||||
// For custom networks, use existing genesis to preserve state
|
||||
ux.Print(lc.log, "%s", log.Green.Wrap("Using existing genesis for custom network"))
|
||||
cfg.Genesis = existingGenesis
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure required staking flags are set for non-local networks
|
||||
// stake-minting-period must be >= max-stake-duration (1 year = 8760h)
|
||||
if _, ok := cfg.Flags["stake-minting-period"]; !ok {
|
||||
@@ -314,6 +361,44 @@ func (lc *localNetwork) Start(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkForExistingGenesis checks if we're resuming from existing state.
|
||||
// Returns (genesisJSON, isResume) where isResume is true if:
|
||||
// 1. node1/genesis.json exists, AND
|
||||
// 2. node1/db directory has data (meaning blocks have been committed)
|
||||
//
|
||||
// This is critical because JSON serialization is non-deterministic in Go
|
||||
// (map iteration order varies), so regenerating genesis from the same source
|
||||
// data produces different bytes, causing "db contains invalid genesis hash" errors.
|
||||
func (lc *localNetwork) checkForExistingGenesis() (string, bool) {
|
||||
if lc.options.rootDataDir == "" {
|
||||
return "", false
|
||||
}
|
||||
|
||||
// Check node1 for existing genesis and db data
|
||||
node1Dir := filepath.Join(lc.options.rootDataDir, "node1")
|
||||
genesisPath := filepath.Join(node1Dir, "genesis.json")
|
||||
dbDir := filepath.Join(node1Dir, "db")
|
||||
|
||||
// Check if genesis.json exists
|
||||
genesisBytes, err := os.ReadFile(genesisPath)
|
||||
if err != nil {
|
||||
// No existing genesis file - this is a fresh start
|
||||
return "", false
|
||||
}
|
||||
|
||||
// Check if db directory has data (indicating we're resuming)
|
||||
entries, err := os.ReadDir(dbDir)
|
||||
if err != nil || len(entries) == 0 {
|
||||
// No db data - this is a fresh start even though genesis file exists
|
||||
// (perhaps from a failed previous start)
|
||||
return "", false
|
||||
}
|
||||
|
||||
// Both genesis.json exists AND db has data - we're resuming
|
||||
// Note: We can't log here since lc.log may not be set yet during config creation
|
||||
return string(genesisBytes), true
|
||||
}
|
||||
|
||||
// Creates the blockchains specified in [chainSpecs].
|
||||
// Assumes [lc.lock] isn't held.
|
||||
func (lc *localNetwork) CreateChains(
|
||||
|
||||
@@ -1493,6 +1493,32 @@ func (s *server) SaveSnapshot(ctx context.Context, req *rpcpb.SaveSnapshotReques
|
||||
return &rpcpb.SaveSnapshotResponse{SnapshotPath: snapshotPath}, nil
|
||||
}
|
||||
|
||||
// SaveHotSnapshot saves a snapshot without stopping the network
|
||||
// Uses Copy-on-Write on APFS (macOS) for instant snapshots
|
||||
func (s *server) SaveHotSnapshot(ctx context.Context, req *rpcpb.SaveSnapshotRequest) (*rpcpb.SaveSnapshotResponse, error) {
|
||||
s.mu.RLock() // Read lock - doesn't block network operations
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
s.logger.Info("SaveHotSnapshot", log.String("snapshot-name", req.SnapshotName))
|
||||
|
||||
if s.network == nil {
|
||||
return nil, ErrNotBootstrapped
|
||||
}
|
||||
|
||||
snapshotPath, err := s.network.nw.SaveHotSnapshot(ctx, req.SnapshotName)
|
||||
if err != nil {
|
||||
s.logger.Warn("hot snapshot save failed to complete", log.Err(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Note: We do NOT stop the network for hot snapshots
|
||||
s.logger.Info("Hot snapshot saved successfully",
|
||||
log.String("snapshot-name", req.SnapshotName),
|
||||
log.String("path", snapshotPath))
|
||||
|
||||
return &rpcpb.SaveSnapshotResponse{SnapshotPath: snapshotPath}, nil
|
||||
}
|
||||
|
||||
func (s *server) RemoveSnapshot(_ context.Context, req *rpcpb.RemoveSnapshotRequest) (*rpcpb.RemoveSnapshotResponse, error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
Reference in New Issue
Block a user