Skip to content

Commit

Permalink
tmpnet: Add support for subnets
Browse files Browse the repository at this point in the history
  • Loading branch information
marun committed Dec 18, 2023
1 parent b86451d commit 6b65ce9
Show file tree
Hide file tree
Showing 13 changed files with 529 additions and 34 deletions.
11 changes: 8 additions & 3 deletions tests/fixture/e2e/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (

"github.com/stretchr/testify/require"

"github.com/ava-labs/avalanchego/config"
"github.com/ava-labs/avalanchego/tests"
"github.com/ava-labs/avalanchego/tests/fixture"
"github.com/ava-labs/avalanchego/tests/fixture/tmpnet"
Expand Down Expand Up @@ -53,7 +54,7 @@ func (te *TestEnvironment) Marshal() []byte {
}

// Initialize a new test environment with a shared network (either pre-existing or newly created).
func NewTestEnvironment(flagVars *FlagVars) *TestEnvironment {
func NewTestEnvironment(flagVars *FlagVars, subnets ...*tmpnet.Subnet) *TestEnvironment {
require := require.New(ginkgo.GinkgoT())

networkDir := flagVars.NetworkDir()
Expand All @@ -66,9 +67,11 @@ func NewTestEnvironment(flagVars *FlagVars) *TestEnvironment {
require.NoError(err)
tests.Outf("{{yellow}}Using an existing network configured at %s{{/}}\n", network.Dir)
} else {
network = StartNetwork(flagVars.AvalancheGoExecPath(), DefaultNetworkDir)
network = StartNetwork(flagVars.AvalancheGoExecPath(), flagVars.pluginDir, DefaultNetworkDir)
}

require.NoError(network.CreateSubnets(DefaultContext(), ginkgo.GinkgoWriter, subnets))

uris := tmpnet.GetNodeURIs(network.Nodes)
require.NotEmpty(uris, "network contains no nodes")
tests.Outf("{{green}}network URIs: {{/}} %+v\n", uris)
Expand Down Expand Up @@ -132,5 +135,7 @@ func (te *TestEnvironment) NewPrivateNetwork() *tmpnet.Network {
privateNetworksDir := filepath.Join(sharedNetwork.Dir, PrivateNetworksDirName)
te.require.NoError(os.MkdirAll(privateNetworksDir, perms.ReadWriteExecute))

return StartNetwork(sharedNetwork.DefaultRuntimeConfig.AvalancheGoPath, privateNetworksDir)
pluginDir, err := sharedNetwork.DefaultFlags.GetStringVal(config.PluginDirKey)
te.require.NoError(err)
return StartNetwork(sharedNetwork.DefaultRuntimeConfig.AvalancheGoPath, pluginDir, privateNetworksDir)
}
19 changes: 15 additions & 4 deletions tests/fixture/e2e/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,19 @@ import (

type FlagVars struct {
avalancheGoExecPath string
pluginDir string
networkDir string
useExistingNetwork bool
}

func (v *FlagVars) AvalancheGoExecPath() string {
return v.avalancheGoExecPath
}

func (v *FlagVars) PluginDir() string {
return v.pluginDir
}

func (v *FlagVars) NetworkDir() string {
if !v.useExistingNetwork {
return ""
Expand All @@ -27,10 +36,6 @@ func (v *FlagVars) NetworkDir() string {
return os.Getenv(tmpnet.NetworkDirEnvName)
}

func (v *FlagVars) AvalancheGoExecPath() string {
return v.avalancheGoExecPath
}

func (v *FlagVars) UseExistingNetwork() bool {
return v.useExistingNetwork
}
Expand All @@ -43,6 +48,12 @@ func RegisterFlags() *FlagVars {
os.Getenv(tmpnet.AvalancheGoPathEnvName),
fmt.Sprintf("avalanchego executable path (required if not using an existing network). Also possible to configure via the %s env variable.", tmpnet.AvalancheGoPathEnvName),
)
flag.StringVar(
&vars.pluginDir,
"plugin-dir",
os.ExpandEnv("$HOME/.avalanchego/plugins"),
"[optional] the dir containing VM plugins.",
)
flag.StringVar(
&vars.networkDir,
"network-dir",
Expand Down
14 changes: 4 additions & 10 deletions tests/fixture/e2e/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import (
"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/tests"
"github.com/ava-labs/avalanchego/tests/fixture/tmpnet"
"github.com/ava-labs/avalanchego/vms/platformvm/txs/executor"
"github.com/ava-labs/avalanchego/vms/secp256k1fx"
"github.com/ava-labs/avalanchego/wallet/subnet/primary"
"github.com/ava-labs/avalanchego/wallet/subnet/primary/common"
Expand All @@ -35,19 +34,14 @@ const (
// contention.
DefaultTimeout = 2 * time.Minute

// Interval appropriate for network operations that should be
// retried periodically but not too often.
DefaultPollingInterval = 500 * time.Millisecond
DefaultPollingInterval = tmpnet.DefaultPollingInterval

// Setting this env will disable post-test bootstrap
// checks. Useful for speeding up iteration during test
// development.
SkipBootstrapChecksEnvName = "E2E_SKIP_BOOTSTRAP_CHECKS"

// Validator start time must be a minimum of SyncBound from the
// current time for validator addition to succeed, and adding 20
// seconds provides a buffer in case of any delay in processing.
DefaultValidatorStartTimeDiff = executor.SyncBound + 20*time.Second
DefaultValidatorStartTimeDiff = tmpnet.DefaultValidatorStartTimeDiff

DefaultGasLimit = uint64(21000) // Standard gas limit

Expand Down Expand Up @@ -207,10 +201,10 @@ func CheckBootstrapIsPossible(network *tmpnet.Network) {
}

// Start a temporary network with the provided avalanchego binary.
func StartNetwork(avalancheGoExecPath string, rootNetworkDir string) *tmpnet.Network {
func StartNetwork(avalancheGoExecPath string, pluginDir string, rootNetworkDir string) *tmpnet.Network {
require := require.New(ginkgo.GinkgoT())

network, err := tmpnet.NewDefaultNetwork(ginkgo.GinkgoWriter, avalancheGoExecPath, tmpnet.DefaultNodeCount)
network, err := tmpnet.NewDefaultNetwork(ginkgo.GinkgoWriter, avalancheGoExecPath, pluginDir, tmpnet.DefaultNodeCount)
require.NoError(err)
require.NoError(network.Create(rootNetworkDir))
require.NoError(network.Start(DefaultContext(), ginkgo.GinkgoWriter))
Expand Down
16 changes: 13 additions & 3 deletions tests/fixture/tmpnet/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ the following non-test files:
| node.go | Node | Orchestrates and configures nodes |
| node_config.go | Node | Reads and writes node configuration |
| node_process.go | NodeProcess | Orchestrates node processes |
| subnet.go | Subnet | Orchestrates subnets |
| utils.go | | Defines shared utility functions |

## Usage
Expand Down Expand Up @@ -108,6 +109,10 @@ avalanchego on node start. The use of dynamic ports supports testing
with many temporary networks without having to manually select compatible
port ranges.

## Subnet configuration

TODO(marun)

## Configuration on disk

A temporary network relies on configuration written to disk in the following structure:
Expand All @@ -130,11 +135,16 @@ HOME
│ │ └── ...
│ └── process.json // Node process details (PID, API URI, staking address)
├── chains
│ └── C
│ └── config.json // C-Chain config for all nodes
│ ├── C
│ │ └── config.json // C-Chain config for all nodes
│ └── raZ51bwfepaSaZ1MNSRNYNs3ZPfj...U7pa3
│ └── config.json // Custom chain configuration for all nodes
├── config.json // Common configuration (including defaults and pre-funded keys)
├── genesis.json // Genesis for all nodes
└── network.env // Sets network dir env var to simplify network usage
├── network.env // Sets network dir env var to simplify network usage
└── subnets // Parent directory for subnet definitions
├─ subnet-a.json // Configuration for subnet-a and its chain(s)
└─ subnet-b.json // Configuration for subnet-b and its chain(s)
```

### Common networking configuration
Expand Down
32 changes: 24 additions & 8 deletions tests/fixture/tmpnet/cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,12 @@ var (
)

func main() {
var networkDir string
rootCmd := &cobra.Command{
Use: "tmpnetctl",
Short: "tmpnetctl commands",
}
rootCmd.PersistentFlags().StringVar(&networkDir, "network-dir", os.Getenv(tmpnet.NetworkDirEnvName), "The path to the configuration directory of a temporary network")

versionCmd := &cobra.Command{
Use: "version",
Expand All @@ -46,21 +48,22 @@ func main() {
rootCmd.AddCommand(versionCmd)

var (
rootDir string
execPath string
nodeCount uint8
rootDir string
avalancheGoPath string
pluginDir string
nodeCount uint8
)
startNetworkCmd := &cobra.Command{
Use: "start-network",
Short: "Start a new temporary network",
RunE: func(*cobra.Command, []string) error {
if len(execPath) == 0 {
if len(avalancheGoPath) == 0 {
return errAvalancheGoRequired
}

// Root dir will be defaulted on start if not provided

network, err := tmpnet.NewDefaultNetwork(os.Stdout, execPath, int(nodeCount))
network, err := tmpnet.NewDefaultNetwork(os.Stdout, avalancheGoPath, pluginDir, int(nodeCount))
if err != nil {
return err
}
Expand Down Expand Up @@ -98,11 +101,11 @@ func main() {
},
}
startNetworkCmd.PersistentFlags().StringVar(&rootDir, "root-dir", os.Getenv(tmpnet.RootDirEnvName), "The path to the root directory for temporary networks")
startNetworkCmd.PersistentFlags().StringVar(&execPath, "avalanchego-path", os.Getenv(tmpnet.AvalancheGoPathEnvName), "The path to an avalanchego binary")
startNetworkCmd.PersistentFlags().StringVar(&avalancheGoPath, "avalanchego-path", os.Getenv(tmpnet.AvalancheGoPathEnvName), "The path to an avalanchego binary")
startNetworkCmd.PersistentFlags().StringVar(&pluginDir, "plugin-dir", os.ExpandEnv("$HOME/.avalanchego/plugins"), "[optional] the dir containing VM plugins")
startNetworkCmd.PersistentFlags().Uint8Var(&nodeCount, "node-count", tmpnet.DefaultNodeCount, "Number of nodes the network should initially consist of")
rootCmd.AddCommand(startNetworkCmd)

var networkDir string
stopNetworkCmd := &cobra.Command{
Use: "stop-network",
Short: "Stop a temporary network",
Expand All @@ -119,9 +122,22 @@ func main() {
return nil
},
}
stopNetworkCmd.PersistentFlags().StringVar(&networkDir, "network-dir", os.Getenv(tmpnet.NetworkDirEnvName), "The path to the configuration directory of a temporary network")
rootCmd.AddCommand(stopNetworkCmd)

restartNetworkCmd := &cobra.Command{
Use: "restart-network",
Short: "Restart a temporary network",
RunE: func(*cobra.Command, []string) error {
if len(networkDir) == 0 {
return errNetworkDirRequired
}
ctx, cancel := context.WithTimeout(context.Background(), tmpnet.DefaultNetworkTimeout)
defer cancel()
return tmpnet.RestartNetwork(ctx, os.Stdout, networkDir)
},
}
rootCmd.AddCommand(restartNetworkCmd)

if err := rootCmd.Execute(); err != nil {
fmt.Fprintf(os.Stderr, "tmpnetctl failed: %v\n", err)
os.Exit(1)
Expand Down
10 changes: 10 additions & 0 deletions tests/fixture/tmpnet/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,19 @@ import (
"time"

"github.com/ava-labs/avalanchego/config"
"github.com/ava-labs/avalanchego/vms/platformvm/txs/executor"
)

const (
// Interval appropriate for network operations that should be
// retried periodically but not too often.
DefaultPollingInterval = 500 * time.Millisecond

// Validator start time must be a minimum of SyncBound from the
// current time for validator addition to succeed, and adding 20
// seconds provides a buffer in case of any delay in processing.
DefaultValidatorStartTimeDiff = executor.SyncBound + 20*time.Second

DefaultNetworkTimeout = 2 * time.Minute

// Minimum required to ensure connectivity-based health checks will pass
Expand Down
4 changes: 2 additions & 2 deletions tests/fixture/tmpnet/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,8 @@ func NewTestGenesis(
},
StartTime: uint64(now.Unix()),
InitialStakedFunds: []string{stakeAddress},
InitialStakeDuration: 365 * 24 * 60 * 60, // 1 year
InitialStakeDurationOffset: 90 * 60, // 90 minutes
InitialStakeDuration: uint64(genesis.LocalParams.MaxStakeDuration.Seconds()),
InitialStakeDurationOffset: 90 * 60, // 90 minutes
Message: "hello avalanche!",
InitialStakers: initialStakers,
}
Expand Down
Loading

0 comments on commit 6b65ce9

Please sign in to comment.