Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[tmpnet] Enable single node networks #3003

Merged
merged 5 commits into from
May 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions tests/e2e/c/dynamic_fees.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,7 @@ var _ = e2e.DescribeCChain("[Dynamic Fees]", func() {

ginkgo.It("should ensure that the gas price is affected by load", func() {
ginkgo.By("creating a new private network to ensure isolation from other tests")
privateNetwork := &tmpnet.Network{
Owner: "avalanchego-e2e-dynamic-fees",
}
privateNetwork := tmpnet.NewDefaultNetwork("avalanchego-e2e-dynamic-fees")
e2e.Env.StartPrivateNetwork(privateNetwork)

ginkgo.By("allocating a pre-funded key")
Expand Down
6 changes: 1 addition & 5 deletions tests/e2e/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"testing"

"github.com/onsi/gomega"
"github.com/stretchr/testify/require"

// ensure test packages are scanned by ginkgo
_ "github.com/ava-labs/avalanchego/tests/e2e/banff"
Expand Down Expand Up @@ -38,11 +37,8 @@ func init() {
var _ = ginkgo.SynchronizedBeforeSuite(func() []byte {
// Run only once in the first ginkgo process

nodes, err := tmpnet.NewNodes(tmpnet.DefaultNodeCount)
require.NoError(ginkgo.GinkgoT(), err)
Comment on lines -41 to -42
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

q: why panicking instead of using the require infra? No action required

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this were only for e2e it makes sense, but I was looking to support antithesis testing as well as e2e and we're not using assertions because failing fast isn't the pattern. The process running the tests needs to run continuously for an arbitrary length of time and error logging is the signal that something is wrong.

Copy link
Contributor Author

@marun marun May 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I think I'm confusing things with that mention of 'failing fast'. That's the reason to not use require, but it doesn't explain why I'm using panic. I wanted to enable a simpler declarative form for network definition e.g.

network := tmpnet.Network{
    ...
    Nodes: tmpnet.NewNodesOrPanic(tmpnet.DefaultNodeCount),
} 

The counterpoint is that any network with subnets requires associating nodes to subnets so this simple declarative form isn't sufficient.

So maybe this approach is best described as an experiment to see if simpler panic-based test setup helpers are a good idea or not. My thinking is that this approach is compatible so long as we're either going to use these functions at the start of an antithesis or load test, where failure to perform test setup should exit the process, or with a test runner like ginkgo where a panic at any point during test execution will fail the test but not halt execution of subsequent tests.


nodes := tmpnet.NewNodesOrPanic(flagVars.NodeCount())
subnets := vms.XSVMSubnets(nodes...)

return e2e.NewTestEnvironment(
flagVars,
&tmpnet.Network{
Expand Down
11 changes: 11 additions & 0 deletions tests/fixture/e2e/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type FlagVars struct {
reuseNetwork bool
networkShutdownDelay time.Duration
stopNetwork bool
nodeCount int
}

func (v *FlagVars) AvalancheGoExecPath() string {
Expand Down Expand Up @@ -51,6 +52,10 @@ func (v *FlagVars) StopNetwork() bool {
return v.stopNetwork
}

func (v *FlagVars) NodeCount() int {
return v.nodeCount
}

func RegisterFlags() *FlagVars {
vars := FlagVars{}
flag.StringVar(
Expand Down Expand Up @@ -89,6 +94,12 @@ func RegisterFlags() *FlagVars {
false,
"[optional] stop an existing network and exit without executing any tests.",
)
flag.IntVar(
&vars.nodeCount,
"node-count",
tmpnet.DefaultNodeCount,
"number of nodes the network should initially consist of",
)

return &vars
}
1 change: 0 additions & 1 deletion tests/fixture/e2e/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,6 @@ func StartNetwork(
DefaultNetworkDir,
avalancheGoExecPath,
pluginDir,
tmpnet.DefaultNodeCount,
),
)

Expand Down
5 changes: 3 additions & 2 deletions tests/fixture/tmpnet/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ A temporary network can be managed by the `tmpnetctl` cli tool:
# Build the tmpnetctl binary
$ ./scripts/build_tmpnetctl.sh

# Start a new network
# Start a new network. Possible to specify the number of nodes (> 1) with --node-count.
$ ./build/tmpnetctl start-network --avalanchego-path=/path/to/avalanchego
...
Started network /home/me/.tmpnet/networks/20240306-152305.924531 (UUID: abaab590-b375-44f6-9ca5-f8a6dc061725)
Expand Down Expand Up @@ -87,6 +87,7 @@ network := &tmpnet.Network{ // Configure non-default values fo
DefaultFlags: tmpnet.FlagsMap{
config.LogLevelKey: "INFO", // Change one of the network's defaults
},
Nodes: tmpnet.NewNodesOrPanic(5), // Number of initial validating nodes
Subnets: []*tmpnet.Subnet{ // Subnets to create on the new network once it is running
{
Name: "xsvm-a", // User-defined name used to reference subnet in code and on disk
Expand All @@ -97,6 +98,7 @@ network := &tmpnet.Network{ // Configure non-default values fo
PreFundedKey: <key>, // (Optional) A private key that is funded in the genesis bytes
},
},
ValidatorIDs: <node ids>, // The IDs of nodes that validate the subnet
},
},
}
Expand All @@ -108,7 +110,6 @@ _ := tmpnet.StartNewNetwork( // Start the network
"", // Empty string uses the default network path (~/tmpnet/networks)
"/path/to/avalanchego", // The path to the binary that nodes will execute
"/path/to/plugins", // The path nodes will use for plugin binaries (suggested value ~/.avalanchego/plugins)
5, // Number of initial validating nodes
)

uris := network.GetNodeURIs()
Expand Down
2 changes: 1 addition & 1 deletion tests/fixture/tmpnet/cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ func main() {

network := &tmpnet.Network{
Owner: networkOwner,
Nodes: tmpnet.NewNodesOrPanic(int(nodeCount)),
}

// Extreme upper bound, should never take this long
Expand All @@ -80,7 +81,6 @@ func main() {
rootDir,
avalancheGoPath,
pluginDir,
int(nodeCount),
)
if err != nil {
return err
Expand Down
39 changes: 24 additions & 15 deletions tests/fixture/tmpnet/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,13 @@ const (
HardHatKeyStr = "56289e99c94b6912bfc12adc093c9b51124f0dc54ac7a766b2bc5ccf558d8027"
)

// HardhatKey is a legacy used for hardhat testing in subnet-evm
// TODO(marun) Remove when no longer needed.
var HardhatKey *secp256k1.PrivateKey
var (
// Key expected to be funded for subnet-evm hardhat testing
// TODO(marun) Remove when subnet-evm configures the genesis with this key.
HardhatKey *secp256k1.PrivateKey

errInsufficientNodes = errors.New("network needs at least one node to start")
)

func init() {
hardhatKeyBytes, err := hex.DecodeString(HardHatKeyStr)
Expand Down Expand Up @@ -105,6 +109,13 @@ type Network struct {
Subnets []*Subnet
}

func NewDefaultNetwork(owner string) *Network {
return &Network{
Owner: owner,
Nodes: NewNodesOrPanic(DefaultNodeCount),
}
}

// Ensure a real and absolute network dir so that node
// configuration that embeds the network path will continue to
// work regardless of symlink and working directory changes.
Expand All @@ -123,9 +134,11 @@ func StartNewNetwork(
rootNetworkDir string,
avalancheGoExecPath string,
pluginDir string,
nodeCount int,
) error {
if err := network.EnsureDefaultConfig(w, avalancheGoExecPath, pluginDir, nodeCount); err != nil {
if len(network.Nodes) == 0 {
return errInsufficientNodes
}
if err := network.EnsureDefaultConfig(w, avalancheGoExecPath, pluginDir); err != nil {
return err
}
if err := network.Create(rootNetworkDir); err != nil {
Expand Down Expand Up @@ -171,7 +184,7 @@ func ReadNetwork(dir string) (*Network, error) {
}

// Initializes a new network with default configuration.
func (n *Network) EnsureDefaultConfig(w io.Writer, avalancheGoPath string, pluginDir string, nodeCount int) error {
func (n *Network) EnsureDefaultConfig(w io.Writer, avalancheGoPath string, pluginDir string) error {
if _, err := fmt.Fprintf(w, "Preparing configuration for new network with %s\n", avalancheGoPath); err != nil {
return err
}
Expand All @@ -187,6 +200,11 @@ func (n *Network) EnsureDefaultConfig(w io.Writer, avalancheGoPath string, plugi
}
n.DefaultFlags.SetDefaults(DefaultFlags())

if len(n.Nodes) == 1 {
// Sybil protection needs to be disabled for a single node network to start
n.DefaultFlags[config.SybilProtectionEnabledKey] = false
}

// Only configure the plugin dir with a non-empty value to ensure
// the use of the default value (`[datadir]/plugins`) when
// no plugin dir is configured.
Expand Down Expand Up @@ -222,15 +240,6 @@ func (n *Network) EnsureDefaultConfig(w io.Writer, avalancheGoPath string, plugi
n.DefaultRuntimeConfig.AvalancheGoPath = avalancheGoPath
}

// Ensure nodes are created
if len(n.Nodes) == 0 {
nodes, err := NewNodes(nodeCount)
if err != nil {
return err
}
n.Nodes = nodes
}

// Ensure nodes are configured
for i := range n.Nodes {
if err := n.EnsureNodeConfig(n.Nodes[i]); err != nil {
Expand Down
4 changes: 2 additions & 2 deletions tests/fixture/tmpnet/network_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ func TestNetworkSerialization(t *testing.T) {

tmpDir := t.TempDir()

network := &Network{}
require.NoError(network.EnsureDefaultConfig(&bytes.Buffer{}, "/path/to/avalanche/go", "", 1))
network := NewDefaultNetwork("testnet")
require.NoError(network.EnsureDefaultConfig(&bytes.Buffer{}, "/path/to/avalanche/go", ""))
require.NoError(network.Create(tmpDir))
// Ensure node runtime is initialized
require.NoError(network.readNodes())
Expand Down
6 changes: 3 additions & 3 deletions tests/fixture/tmpnet/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,16 +104,16 @@ func NewEphemeralNode(flags FlagsMap) *Node {
}

// Initializes the specified number of nodes.
func NewNodes(count int) ([]*Node, error) {
joshua-kim marked this conversation as resolved.
Show resolved Hide resolved
func NewNodesOrPanic(count int) []*Node {
nodes := make([]*Node, count)
for i := range nodes {
node := NewNode("")
if err := node.EnsureKeys(); err != nil {
return nil, err
panic(err)
}
nodes[i] = node
}
return nodes, nil
return nodes
}

// Reads a node's configuration from the specified directory.
Expand Down
4 changes: 1 addition & 3 deletions tests/upgrade/upgrade_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,7 @@ var _ = ginkgo.Describe("[Upgrade]", func() {
require := require.New(ginkgo.GinkgoT())

ginkgo.It("can upgrade versions", func() {
network := &tmpnet.Network{
Owner: "avalanchego-upgrade",
}
network := tmpnet.NewDefaultNetwork("avalanchego-upgrade")
e2e.StartNetwork(network, avalancheGoExecPath, "" /* pluginDir */, 0 /* shutdownDelay */, false /* reuseNetwork */)

ginkgo.By(fmt.Sprintf("restarting all nodes with %q binary", avalancheGoExecPathToUpgradeTo))
Expand Down
Loading