Эх сурвалжийг харах

Node/P2P: Add default bootstrap peers (#3852)

* Node/P2P: Add default bootstrap peers

* Rework

* Update ccqlistener
bruce-riley 1 жил өмнө
parent
commit
d036e70740

+ 2 - 0
devnet/query-server.yaml

@@ -52,6 +52,8 @@ spec:
             - http://eth-devnet:8545
             - --ethContract
             - "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550"
+            - --network
+            - /wormhole/dev
             # Hardcoded devnet bootstrap (generated from deterministic key in guardiand)
             - --bootstrap
             - /dns4/guardian-0.guardian/udp/8996/quic/p2p/12D3KooWL3XJ9EMCyZvmmGXL2LMiVBtrVa2BuESsJiXkSj7333Jw

+ 2 - 0
devnet/spy.yaml

@@ -43,6 +43,8 @@ spec:
             - /tmp/node.key
             - --spyRPC
             - "[::]:7072"
+            - --network
+            - /wormhole/dev
             # Hardcoded devnet bootstrap (generated from deterministic key in guardiand)
             - --bootstrap
             - /dns4/guardian-0.guardian/udp/8999/quic/p2p/12D3KooWL3XJ9EMCyZvmmGXL2LMiVBtrVa2BuESsJiXkSj7333Jw

+ 17 - 22
docs/operations.md

@@ -53,6 +53,7 @@ If you use the same RPC node for Wormhole v1, you also need the following additi
 `getProgramAccount` queries:
 
 <!-- cspell:disable -->
+
 ```
 [... see above for other required parameters ...]
 
@@ -60,6 +61,7 @@ If you use the same RPC node for Wormhole v1, you also need the following additi
 --account-index-include-key WormT3McKhFJ2RkiGpdw9GKvNCrB2aB54gb2uV9MfQC   # for mainnet
 --account-index-include-key 5gQf5AUhAgWYgUCt9ouShm9H7dzzXUsLdssYwe5krKhg  # for testnet
 ```
+
 <!-- cspell:enable -->
 
 Alternatively, if you want to run a general-purpose RPC node with indexes for all programs instead of only Wormhole,
@@ -72,10 +74,12 @@ leave out the filtering:
 On mainnet, we strongly recommend blacklisting KIN and the token program to speed up catchup:
 
 <!-- cspell:disable -->
+
 ```
 --account-index-exclude-key kinXdEcpDQeHPEuQnqmUgtYykqKGVFq6CeVX5iAHJq6  # Mainnet only
 --account-index-exclude-key TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA  # Mainnet only
 ```
+
 <!-- cspell:enable -->
 
 Note that these indexes require extra disk space and may slow down catchup. The first startup after
@@ -261,27 +265,12 @@ may include support for remote signing.
 
 ## Bootstrap Peers
 
-The following bootstrap peers are available in each environment.
-
-### Mainnet
-
-<!-- cspell:disable -->
-```bash
---bootstrap "/dns4/wormhole-v2-mainnet-bootstrap.xlabs.xyz/udp/8999/quic/p2p/12D3KooWNQ9tVrcb64tw6bNs2CaNrUGPM7yRrKvBBheQ5yCyPHKC,/dns4/wormhole.mcf.rocks/udp/8999/quic/p2p/12D3KooWDZVv7BhZ8yFLkarNdaSWaB43D6UbQwExJ8nnGAEmfHcU,/dns4/wormhole-v2-mainnet-bootstrap.staking.fund/udp/8999/quic/p2p/12D3KooWG8obDX9DNi1KUwZNu9xkGwfKqTp2GFwuuHpWZ3nQruS1"
-
---ccqP2pBootstrap "/dns4/wormhole-v2-mainnet-bootstrap.xlabs.xyz/udp/8996/quic/p2p/12D3KooWNQ9tVrcb64tw6bNs2CaNrUGPM7yRrKvBBheQ5yCyPHKC,/dns4/wormhole.mcf.rocks/udp/8996/quic/p2p/12D3KooWDZVv7BhZ8yFLkarNdaSWaB43D6UbQwExJ8nnGAEmfHcU,/dns4/wormhole-v2-mainnet-bootstrap.staking.fund/udp/8996/quic/p2p/12D3KooWG8obDX9DNi1KUwZNu9xkGwfKqTp2GFwuuHpWZ3nQruS1"
-```
-<!-- cspell:enable -->
+The list of supported bootstrap peers is defined in `node/pkg/p2p/network_consts.go`. That file also provides golang functions
+for obtaining the network parameters (network ID and bootstrap peers) based on the environment (mainnet or testnet).
 
-### Testnet
-
-<!-- cspell:disable -->
-```bash
---bootstrap "/dns4/t-guardian-01.nodes.stable.io/udp/8999/quic/p2p/12D3KooWCW3LGUtkCVkHZmVSZHzL3C4WRKWfqAiJPz1NR7dT9Bxh,/dns4/t-guardian-02.nodes.stable.io/udp/8999/quic/p2p/12D3KooWJXA6goBCiWM8ucjzc4jVUBSqL9Rri6UpjHbkMPErz5zK,/dns4/p2p-guardian-testnet-1.solana.p2p.org/udp/8999/quic/p2p/12D3KooWE4dmZwxhfjCKHLUqSaww96Cf7kmq1ZuKmzPz3MrJgZxp"
-
---ccqP2pBootstrap "/dns4/t-guardian-01.nodes.stable.io/udp/8996/quic/p2p/12D3KooWCW3LGUtkCVkHZmVSZHzL3C4WRKWfqAiJPz1NR7dT9Bxh,/dns4/t-guardian-02.nodes.stable.io/udp/8996/quic/p2p/12D3KooWJXA6goBCiWM8ucjzc4jVUBSqL9Rri6UpjHbkMPErz5zK,/dns4/p2p-guardian-testnet-1.solana.p2p.org/udp/8996/quic/p2p/12D3KooWE4dmZwxhfjCKHLUqSaww96Cf7kmq1ZuKmzPz3MrJgZxp"
-```
-<!-- cspell:enable -->
+The common Wormhole applications (guardiand, spy and query proxy server) use those functions, so it is not necessary to specify
+the actual bootstrap parameters in their configs. Developers of any new applications are strongly urged to do the same, and not
+proliferate lists of bootstrap peers which might change over time.
 
 ## Run the Guardian Spy
 
@@ -290,27 +279,31 @@ The spy connects to the wormhole guardian peer to peer network and listens for n
 Start the spy against the testnet wormhole guardian:
 
 <!-- cspell:disable -->
+
 ```bash
 docker run \
     --platform=linux/amd64 \
     -p 7073:7073 \
     --entrypoint /guardiand \
     ghcr.io/wormhole-foundation/guardiand:latest \
-spy --nodeKey /node.key --spyRPC "[::]:7073" --network /wormhole/testnet/2/1 --bootstrap "/dns4/t-guardian-01.nodes.stable.io/udp/8999/quic/p2p/12D3KooWCW3LGUtkCVkHZmVSZHzL3C4WRKWfqAiJPz1NR7dT9Bxh,/dns4/t-guardian-02.nodes.stable.io/udp/8999/quic/p2p/12D3KooWJXA6goBCiWM8ucjzc4jVUBSqL9Rri6UpjHbkMPErz5zK,/dns4/p2p-guardian-testnet-1.solana.p2p.org/udp/8999/quic/p2p/12D3KooWE4dmZwxhfjCKHLUqSaww96Cf7kmq1ZuKmzPz3MrJgZxp"
+    spy --nodeKey /node.key --spyRPC "[::]:7073" --env testnet
 ```
+
 <!-- cspell:enable -->
 
 To run the spy against mainnet:
 
 <!-- cspell:disable -->
+
 ```bash
 docker run \
     --platform=linux/amd64 \
     -p 7073:7073 \
     --entrypoint /guardiand \
     ghcr.io/wormhole-foundation/guardiand:latest \
-spy --nodeKey /node.key --spyRPC "[::]:7073" --network /wormhole/mainnet/2 --bootstrap /dns4/wormhole-v2-mainnet-bootstrap.xlabs.xyz/udp/8999/quic/p2p/12D3KooWNQ9tVrcb64tw6bNs2CaNrUGPM7yRrKvBBheQ5yCyPHKC,/dns4/wormhole.mcf.rocks/udp/8999/quic/p2p/12D3KooWDZVv7BhZ8yFLkarNdaSWaB43D6UbQwExJ8nnGAEmfHcU,/dns4/wormhole-v2-mainnet-bootstrap.staking.fund/udp/8999/quic/p2p/12D3KooWG8obDX9DNi1KUwZNu9xkGwfKqTp2GFwuuHpWZ3nQruS1
+    spy --nodeKey /node.key --spyRPC "[::]:7073" --env mainnet
 ```
+
 <!-- cspell:enable -->
 
 ## Guardian Configurations
@@ -326,12 +319,14 @@ Configuration files, environment variables and flags are all supported.
 **Example**:
 
 <!-- cspell:disable -->
+
 ```yaml
 ethRPC: "ws://eth-devnet:8545"
 ethContract: "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550"
 solanaRPC: "http://solana-devnet:8899"
 solanaContract: "Bridge1p5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o"
 ```
+
 <!-- cspell:enable -->
 
 ### Environment Variables

+ 33 - 16
node/cmd/ccq/query_server.go

@@ -14,6 +14,7 @@ import (
 	"time"
 
 	"github.com/certusone/wormhole/node/pkg/common"
+	"github.com/certusone/wormhole/node/pkg/p2p"
 	"github.com/certusone/wormhole/node/pkg/telemetry"
 	promremotew "github.com/certusone/wormhole/node/pkg/telemetry/prom_remote_write"
 	"github.com/certusone/wormhole/node/pkg/version"
@@ -50,10 +51,10 @@ var (
 const DEV_NETWORK_ID = "/wormhole/dev"
 
 func init() {
-	envStr = QueryServerCmd.Flags().String("env", "", "environment (dev, test, prod)")
-	p2pNetworkID = QueryServerCmd.Flags().String("network", DEV_NETWORK_ID, "P2P network identifier")
+	envStr = QueryServerCmd.Flags().String("env", "", "environment (devnet, testnet, mainnet)")
+	p2pNetworkID = QueryServerCmd.Flags().String("network", "", "P2P network identifier (optional, overrides default for environment)")
 	p2pPort = QueryServerCmd.Flags().Uint("port", 8995, "P2P UDP listener port")
-	p2pBootstrap = QueryServerCmd.Flags().String("bootstrap", "", "P2P bootstrap peers (comma-separated)")
+	p2pBootstrap = QueryServerCmd.Flags().String("bootstrap", "", "P2P bootstrap peers (optional for testnet or mainnet, overrides default, required for devnet)")
 	nodeKeyPath = QueryServerCmd.Flags().String("nodeKey", "", "Path to node key (will be generated if it doesn't exist)")
 	signerKeyPath = QueryServerCmd.Flags().String("signerKey", "", "Path to key used to sign unsigned queries")
 	listenAddr = QueryServerCmd.Flags().String("listenAddr", "[::]:6069", "Listen address for query server (disabled if blank)")
@@ -82,7 +83,6 @@ var QueryServerCmd = &cobra.Command{
 
 func runQueryServer(cmd *cobra.Command, args []string) {
 	common.SetRestrictiveUmask()
-	networkID := *p2pNetworkID + "/ccq"
 
 	// Setup logging
 	lvl, err := ipfslog.LevelFromString(*logLevel)
@@ -94,6 +94,35 @@ func runQueryServer(cmd *cobra.Command, args []string) {
 	logger := ipfslog.Logger("query-server").Desugar()
 	ipfslog.SetAllLoggers(lvl)
 
+	env, err := common.ParseEnvironment(*envStr)
+	if err != nil || (env != common.UnsafeDevNet && env != common.TestNet && env != common.MainNet) {
+		if *envStr == "" {
+			logger.Fatal("Please specify --env")
+		}
+		logger.Fatal("Invalid value for --env, should be devnet, testnet or mainnet", zap.String("val", *envStr))
+	}
+
+	if *p2pNetworkID == "" {
+		*p2pNetworkID = p2p.GetNetworkId(env)
+	} else if env != common.UnsafeDevNet {
+		logger.Warn("overriding default p2p network ID", zap.String("p2pNetworkID", *p2pNetworkID))
+	}
+
+	if *p2pNetworkID == DEV_NETWORK_ID && env != common.UnsafeDevNet {
+		logger.Fatal("May not set --network to dev unless --env is also dev", zap.String("network", *p2pNetworkID), zap.String("env", *envStr))
+	}
+
+	networkID := *p2pNetworkID + "/ccq"
+
+	if *p2pBootstrap == "" {
+		*p2pBootstrap, err = p2p.GetCcqBootstrapPeers(env)
+		if err != nil {
+			logger.Fatal("failed to determine the bootstrap peers from the environment", zap.String("env", string(env)), zap.Error(err))
+		}
+	} else if env != common.UnsafeDevNet {
+		logger.Warn("overriding default p2p bootstrap peers", zap.String("p2pBootstrap", *p2pBootstrap))
+	}
+
 	if *telemetryLokiURL != "" {
 		logger.Info("Using Loki telemetry logger")
 		if *telemetryNodeName == "" {
@@ -114,18 +143,6 @@ func runQueryServer(cmd *cobra.Command, args []string) {
 		logger = tm.WrapLogger(logger) // Wrap logger with telemetry logger
 	}
 
-	env, err := common.ParseEnvironment(*envStr)
-	if err != nil || (env != common.UnsafeDevNet && env != common.TestNet && env != common.MainNet) {
-		if *envStr == "" {
-			logger.Fatal("Please specify --env")
-		}
-		logger.Fatal("Invalid value for --env, must be dev, test or prod", zap.String("val", *envStr))
-	}
-
-	if *p2pNetworkID == DEV_NETWORK_ID && env != common.UnsafeDevNet {
-		logger.Fatal("May not set --network to dev unless --env is also dev", zap.String("network", *p2pNetworkID), zap.String("env", *envStr))
-	}
-
 	// Verify flags
 	if *nodeKeyPath == "" {
 		logger.Fatal("Please specify --nodeKey")

+ 50 - 20
node/cmd/guardiand/node.go

@@ -233,9 +233,9 @@ var (
 )
 
 func init() {
-	p2pNetworkID = NodeCmd.Flags().String("network", "/wormhole/dev", "P2P network identifier")
+	p2pNetworkID = NodeCmd.Flags().String("network", "", "P2P network identifier (optional, overrides default for environment)")
 	p2pPort = NodeCmd.Flags().Uint("port", p2p.DefaultPort, "P2P UDP listener port")
-	p2pBootstrap = NodeCmd.Flags().String("bootstrap", "", "P2P bootstrap peers (comma-separated)")
+	p2pBootstrap = NodeCmd.Flags().String("bootstrap", "", "P2P bootstrap peers (optional for mainnet or testnet, overrides default, required for unsafeDevMode)")
 
 	statusAddr = NodeCmd.Flags().String("statusAddr", "[::]:6060", "Listen address for status server (disabled if blank)")
 
@@ -407,7 +407,7 @@ func init() {
 	ccqEnabled = NodeCmd.Flags().Bool("ccqEnabled", false, "Enable cross chain query support")
 	ccqAllowedRequesters = NodeCmd.Flags().String("ccqAllowedRequesters", "", "Comma separated list of signers allowed to submit cross chain queries")
 	ccqP2pPort = NodeCmd.Flags().Uint("ccqP2pPort", 8996, "CCQ P2P UDP listener port")
-	ccqP2pBootstrap = NodeCmd.Flags().String("ccqP2pBootstrap", "", "CCQ P2P bootstrap peers (comma-separated)")
+	ccqP2pBootstrap = NodeCmd.Flags().String("ccqP2pBootstrap", "", "CCQ P2P bootstrap peers (optional for mainnet or testnet, overrides default, required for unsafeDevMode)")
 	ccqAllowedPeers = NodeCmd.Flags().String("ccqAllowedPeers", "", "CCQ allowed P2P peers (comma-separated)")
 	ccqBackfillCache = NodeCmd.Flags().Bool("ccqBackfillCache", true, "Should EVM chains backfill CCQ timestamp cache on startup")
 
@@ -469,6 +469,16 @@ func runNode(cmd *cobra.Command, args []string) {
 		os.Exit(1)
 	}
 
+	// Determine execution mode
+	var env common.Environment
+	if *unsafeDevMode {
+		env = common.UnsafeDevNet
+	} else if *testnetMode {
+		env = common.TestNet
+	} else {
+		env = common.MainNet
+	}
+
 	if *unsafeDevMode {
 		fmt.Print(devwarning)
 	}
@@ -514,6 +524,10 @@ func runNode(cmd *cobra.Command, args []string) {
 		logger = logger.Named(*nodeName)
 	}
 
+	if *unsafeDevMode && *testnetMode {
+		logger.Fatal("Cannot be in unsafeDevMode and testnetMode at the same time.")
+	}
+
 	// Override the default go-log config, which uses a magic environment variable.
 	ipfslog.SetAllLoggers(lvl)
 
@@ -525,8 +539,15 @@ func runNode(cmd *cobra.Command, args []string) {
 		}
 
 		// Use the first guardian node as bootstrap
-		*p2pBootstrap = fmt.Sprintf("/dns4/guardian-0.guardian/udp/%d/quic/p2p/%s", *p2pPort, g0key.String())
-		*ccqP2pBootstrap = fmt.Sprintf("/dns4/guardian-0.guardian/udp/%d/quic/p2p/%s", *ccqP2pPort, g0key.String())
+		if *p2pBootstrap == "" {
+			*p2pBootstrap = fmt.Sprintf("/dns4/guardian-0.guardian/udp/%d/quic/p2p/%s", *p2pPort, g0key.String())
+		}
+		if *ccqP2pBootstrap == "" {
+			*ccqP2pBootstrap = fmt.Sprintf("/dns4/guardian-0.guardian/udp/%d/quic/p2p/%s", *ccqP2pPort, g0key.String())
+		}
+		if *p2pNetworkID == "" {
+			*p2pNetworkID = p2p.GetNetworkId(env)
+		}
 
 		// Deterministic ganache ETH devnet address.
 		*ethContract = unsafeDevModeEvmContractAddress(*ethContract)
@@ -552,6 +573,30 @@ func runNode(cmd *cobra.Command, args []string) {
 		*baseSepoliaContract = unsafeDevModeEvmContractAddress(*baseSepoliaContract)
 		*optimismSepoliaContract = unsafeDevModeEvmContractAddress(*optimismSepoliaContract)
 		*polygonSepoliaContract = unsafeDevModeEvmContractAddress(*polygonSepoliaContract)
+	} else { // Mainnet or Testnet.
+		// If the network parameters are not specified, use the defaults. Log a warning if they are specified since we want to discourage this.
+		// Note that we don't want to prevent it, to allow for network upgrade testing.
+		if *p2pNetworkID == "" {
+			*p2pNetworkID = p2p.GetNetworkId(env)
+		} else {
+			logger.Warn("overriding default p2p network ID", zap.String("p2pNetworkID", *p2pNetworkID))
+		}
+		if *p2pBootstrap == "" {
+			*p2pBootstrap, err = p2p.GetBootstrapPeers(env)
+			if err != nil {
+				logger.Fatal("failed to determine p2p bootstrap peers", zap.String("env", string(env)), zap.Error(err))
+			}
+		} else {
+			logger.Warn("overriding default p2p bootstrap peers", zap.String("p2pBootstrap", *p2pBootstrap))
+		}
+		if *ccqP2pBootstrap == "" {
+			*ccqP2pBootstrap, err = p2p.GetCcqBootstrapPeers(env)
+			if err != nil {
+				logger.Fatal("failed to determine ccq bootstrap peers", zap.String("env", string(env)), zap.Error(err))
+			}
+		} else {
+			logger.Warn("overriding default ccq bootstrap peers", zap.String("ccqP2pBootstrap", *ccqP2pBootstrap))
+		}
 	}
 
 	// Verify flags
@@ -831,21 +876,6 @@ func runNode(cmd *cobra.Command, args []string) {
 		}
 	}
 
-	// Determine execution mode
-	// TODO: refactor usage of these variables elsewhere. *unsafeDevMode and *testnetMode should not be accessed directly.
-	var env common.Environment
-	if *unsafeDevMode {
-		env = common.UnsafeDevNet
-	} else if *testnetMode {
-		env = common.TestNet
-	} else {
-		env = common.MainNet
-	}
-
-	if *unsafeDevMode && *testnetMode {
-		logger.Fatal("Cannot be in unsafeDevMode and testnetMode at the same time.")
-	}
-
 	// Complain about Infura on mainnet.
 	//
 	// As it turns out, Infura has a bug where it would sometimes incorrectly round

+ 29 - 2
node/cmd/spy/spy.go

@@ -33,6 +33,8 @@ var (
 )
 
 var (
+	envStr *string
+
 	p2pNetworkID *string
 	p2pPort      *uint
 	p2pBootstrap *string
@@ -49,9 +51,10 @@ var (
 )
 
 func init() {
-	p2pNetworkID = SpyCmd.Flags().String("network", "/wormhole/dev", "P2P network identifier")
+	envStr = SpyCmd.Flags().String("env", "", `environment (may be "testnet" or "mainnet", required unless "--bootstrap" is specified)`)
+	p2pNetworkID = SpyCmd.Flags().String("network", "", "P2P network identifier (optional for testnet or mainnet, overrides default, required for devnet)")
 	p2pPort = SpyCmd.Flags().Uint("port", 8999, "P2P UDP listener port")
-	p2pBootstrap = SpyCmd.Flags().String("bootstrap", "", "P2P bootstrap peers (comma-separated)")
+	p2pBootstrap = SpyCmd.Flags().String("bootstrap", "", "P2P bootstrap peers (optional for testnet or mainnet, overrides default, required for devnet)")
 
 	statusAddr = SpyCmd.Flags().String("statusAddr", "[::]:6060", "Listen address for status server (disabled if blank)")
 
@@ -237,6 +240,30 @@ func runSpy(cmd *cobra.Command, args []string) {
 
 	ipfslog.SetAllLoggers(lvl)
 
+	if *envStr != "" {
+		// If they specify --env then use the defaults for the network parameters and don't allow them to override them.
+		if *p2pNetworkID != "" || *p2pBootstrap != "" {
+			logger.Fatal(`If "--env" is specified, "--network" and "--bootstrap" may not be specified`)
+		}
+		env, err := common.ParseEnvironment(*envStr)
+		if err != nil || (env != common.MainNet && env != common.TestNet) {
+			logger.Fatal(`Invalid value for "--env", should be "mainnet" or "testnet"`)
+		}
+		*p2pNetworkID = p2p.GetNetworkId(env)
+		*p2pBootstrap, err = p2p.GetBootstrapPeers(env)
+		if err != nil {
+			logger.Fatal("failed to determine p2p bootstrap peers", zap.String("env", string(env)), zap.Error(err))
+		}
+	} else {
+		// If they don't specify --env, then --network and --bootstrap are required.
+		if *p2pNetworkID == "" {
+			logger.Fatal(`If "--env" is not specified, "--network" must be specified`)
+		}
+		if *p2pBootstrap == "" {
+			logger.Fatal(`If "--env" is not specified, "--bootstrap" must be specified`)
+		}
+	}
+
 	// Status server
 	if *statusAddr != "" {
 		router := mux.NewRouter()

+ 31 - 8
node/hack/query/ccqlistener/ccqlistener.go

@@ -1,10 +1,10 @@
 // This tool can be used to verify that a guardian is properly receiving CCQ queries and publishing responses.
 
 // This tool can be used to passively listen for query responses from any guardian:
-//    go run ccqlistener.go --listenOnly
+//    go run ccqlistener.go --env mainnet --listenOnly
 //
 // Or it can be used to passively listen for query responses from a particular guardian:
-//    go run ccqlistener.go --listenOnly --targetPeerId <yourGuardianP2pPeerId>
+//    go run ccqlistener.go --env mainnet --listenOnly --targetPeerId <yourGuardianP2pPeerId>
 //
 // This should work both in mainnet and testnet because there are routine monitoring queries running every few minutes.
 // Note that you may need to wait 15 minutes or more to see something. Look for message saying "query response received".
@@ -32,7 +32,7 @@
 //
 // To run the tool as a docker image, you can do something like this:
 // - wormhole$ docker build --target build -f node/hack/query/ccqlistener/Dockerfile -t ccqlistener .
-// - wormhole$ docker run -v /ccqlistener/cfg:/app/cfg ccqlistener /ccqlistener --configDir /app/cfg
+// - wormhole$ docker run -v /ccqlistener/cfg:/app/cfg ccqlistener /ccqlistener --env mainnet --configDir /app/cfg
 // Where /ccqlistener is a directory containing these files:
 // - ccqlistener.nodeKey
 // - ccqlistener.signerKey
@@ -68,11 +68,10 @@ import (
 )
 
 var (
-	p2pNetworkID = flag.String("network", "/wormhole/mainnet/2", "P2P network identifier")
-	p2pPort      = flag.Int("port", 8998, "P2P UDP listener port")
-	p2pBootstrap = flag.String("bootstrap",
-		"/dns4/wormhole-mainnet-v2-bootstrap.certus.one/udp/8996/quic/p2p/12D3KooWQp644DK27fd3d4Km3jr7gHiuJJ5ZGmy8hH4py7fP4FP7,/dns4/wormhole-v2-mainnet-bootstrap.xlabs.xyz/udp/8996/quic/p2p/12D3KooWNQ9tVrcb64tw6bNs2CaNrUGPM7yRrKvBBheQ5yCyPHKC,/dns4/wormhole.mcf.rocks/udp/8996/quic/p2p/12D3KooWDZVv7BhZ8yFLkarNdaSWaB43D6UbQwExJ8nnGAEmfHcU,/dns4/wormhole-v2-mainnet-bootstrap.staking.fund/udp/8996/quic/p2p/12D3KooWG8obDX9DNi1KUwZNu9xkGwfKqTp2GFwuuHpWZ3nQruS1",
-		"P2P bootstrap peers (comma-separated)")
+	envStr        = flag.String("env", "", `environment (may be "testnet" or "mainnet", required unless "--bootstrap" is specified)`)
+	p2pNetworkID  = flag.String("network", "", "P2P network identifier (optional, overrides default, required for devnet)")
+	p2pPort       = flag.Int("port", 8998, "P2P UDP listener port")
+	p2pBootstrap  = flag.String("bootstrap", "", "P2P bootstrap peers (optional, overrides default)")
 	nodeKeyPath   = flag.String("nodeKey", "ccqlistener.nodeKey", "Path to node key (will be generated if it doesn't exist)")
 	signerKeyPath = flag.String("signerKey", "ccqlistener.signerKey", "Path to key used to sign unsigned queries")
 	configDir     = flag.String("configDir", ".", "Directory where nodeKey and signerKey are loaded from (default is .)")
@@ -92,6 +91,30 @@ func main() {
 	defer cancel()
 	logger, _ := zap.NewDevelopment()
 
+	if *envStr != "" {
+		// If they specify --env then use the defaults for the network parameters and don't allow them to override them.
+		if *p2pNetworkID != "" || *p2pBootstrap != "" {
+			logger.Fatal(`If "--env" is specified, "--network" and "--bootstrap" may not be specified`)
+		}
+		env, err := common.ParseEnvironment(*envStr)
+		if err != nil || (env != common.MainNet && env != common.TestNet) {
+			logger.Fatal(`Invalid value for "--env", should be "mainnet" or "testnet"`)
+		}
+		*p2pNetworkID = p2p.GetNetworkId(env)
+		*p2pBootstrap, err = p2p.GetCcqBootstrapPeers(env)
+		if err != nil {
+			logger.Fatal("failed to determine p2p bootstrap peers", zap.String("env", string(env)), zap.Error(err))
+		}
+	} else {
+		// If they don't specify --env, then --network and --bootstrap are required.
+		if *p2pNetworkID == "" {
+			logger.Fatal(`If "--env" is not specified, "--network" must be specified`)
+		}
+		if *p2pBootstrap == "" {
+			logger.Fatal(`If "--env" is not specified, "--bootstrap" must be specified`)
+		}
+	}
+
 	nodeKey := *configDir + "/" + *nodeKeyPath
 
 	var err error

+ 52 - 0
node/pkg/p2p/network_consts.go

@@ -0,0 +1,52 @@
+package p2p
+
+import (
+	"fmt"
+
+	"github.com/certusone/wormhole/node/pkg/common"
+)
+
+// This is the definitive source for the default network parameters. Please reference these (or use the methods below), but avoid copying them!
+const MainnetNetworkId = "/wormhole/mainnet/2"
+const MainnetBootstrapPeers = "/dns4/wormhole-v2-mainnet-bootstrap.xlabs.xyz/udp/8999/quic/p2p/12D3KooWNQ9tVrcb64tw6bNs2CaNrUGPM7yRrKvBBheQ5yCyPHKC,/dns4/wormhole.mcf.rocks/udp/8999/quic/p2p/12D3KooWDZVv7BhZ8yFLkarNdaSWaB43D6UbQwExJ8nnGAEmfHcU,/dns4/wormhole-v2-mainnet-bootstrap.staking.fund/udp/8999/quic/p2p/12D3KooWG8obDX9DNi1KUwZNu9xkGwfKqTp2GFwuuHpWZ3nQruS1"
+const MainnetCcqBootstrapPeers = "/dns4/wormhole-v2-mainnet-bootstrap.xlabs.xyz/udp/8996/quic/p2p/12D3KooWNQ9tVrcb64tw6bNs2CaNrUGPM7yRrKvBBheQ5yCyPHKC,/dns4/wormhole.mcf.rocks/udp/8996/quic/p2p/12D3KooWDZVv7BhZ8yFLkarNdaSWaB43D6UbQwExJ8nnGAEmfHcU,/dns4/wormhole-v2-mainnet-bootstrap.staking.fund/udp/8996/quic/p2p/12D3KooWG8obDX9DNi1KUwZNu9xkGwfKqTp2GFwuuHpWZ3nQruS1"
+
+const TestnetNetworkId = "/wormhole/testnet/2/1"
+const TestnetBootstrapPeers = "/dns4/t-guardian-01.nodes.stable.io/udp/8999/quic/p2p/12D3KooWCW3LGUtkCVkHZmVSZHzL3C4WRKWfqAiJPz1NR7dT9Bxh,/dns4/t-guardian-02.nodes.stable.io/udp/8999/quic/p2p/12D3KooWJXA6goBCiWM8ucjzc4jVUBSqL9Rri6UpjHbkMPErz5zK,/dns4/p2p-guardian-testnet-1.solana.p2p.org/udp/8999/quic/p2p/12D3KooWE4dmZwxhfjCKHLUqSaww96Cf7kmq1ZuKmzPz3MrJgZxp"
+const TestnetCcqBootstrapPeers = "/dns4/t-guardian-01.nodes.stable.io/udp/8996/quic/p2p/12D3KooWCW3LGUtkCVkHZmVSZHzL3C4WRKWfqAiJPz1NR7dT9Bxh,/dns4/t-guardian-02.nodes.stable.io/udp/8996/quic/p2p/12D3KooWJXA6goBCiWM8ucjzc4jVUBSqL9Rri6UpjHbkMPErz5zK,/dns4/p2p-guardian-testnet-1.solana.p2p.org/udp/8996/quic/p2p/12D3KooWE4dmZwxhfjCKHLUqSaww96Cf7kmq1ZuKmzPz3MrJgZxp"
+
+// The Devnet bootstrap peers are derived from the guardian key so we can't include them here.
+const DevnetNetworkId = "/wormhole/dev"
+
+// GetNetworkId returns the default network ID.
+func GetNetworkId(env common.Environment) string {
+	if env == common.MainNet {
+		return MainnetNetworkId
+	}
+	if env == common.TestNet {
+		return TestnetNetworkId
+	}
+	return DevnetNetworkId
+}
+
+// GetBootstrapPeers returns the default p2p bootstrap peers for mainnet and testnet. For any other environment, it returns an error.
+func GetBootstrapPeers(env common.Environment) (string, error) {
+	if env == common.MainNet {
+		return MainnetBootstrapPeers, nil
+	}
+	if env == common.TestNet {
+		return TestnetBootstrapPeers, nil
+	}
+	return "", fmt.Errorf("unsupported environment")
+}
+
+// GetCcqBootstrapPeers returns the default ccq bootstrap peers for mainnet and testnet. For any other environment, it returns an error.
+func GetCcqBootstrapPeers(env common.Environment) (string, error) {
+	if env == common.MainNet {
+		return MainnetCcqBootstrapPeers, nil
+	}
+	if env == common.TestNet {
+		return TestnetCcqBootstrapPeers, nil
+	}
+	return "", fmt.Errorf("unsupported environment")
+}

+ 82 - 0
node/pkg/p2p/network_consts_test.go

@@ -0,0 +1,82 @@
+package p2p
+
+import (
+	"testing"
+
+	"github.com/certusone/wormhole/node/pkg/common"
+	"go.uber.org/zap"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+func TestValidateMainnetNetworkId(t *testing.T) {
+	networkId := GetNetworkId(common.MainNet)
+	require.Equal(t, MainnetNetworkId, networkId)
+}
+
+func TestValidateTestnetNetworkId(t *testing.T) {
+	networkId := GetNetworkId(common.TestNet)
+	require.Equal(t, TestnetNetworkId, networkId)
+}
+
+func TestValidateDevnetNetworkId(t *testing.T) {
+	networkId := GetNetworkId(common.UnsafeDevNet)
+	require.Equal(t, DevnetNetworkId, networkId)
+}
+
+func TestValidateMainnetBootstrapPeers(t *testing.T) {
+	bootstrapPeers, err := GetBootstrapPeers(common.MainNet)
+	require.NoError(t, err)
+	require.NotEqual(t, "", bootstrapPeers)
+
+	// Make sure we can parse the result.
+	logger := zap.NewNop()
+	bootStrappers, _ := BootstrapAddrs(logger, bootstrapPeers, "somePeerID")
+	assert.Equal(t, 3, len(bootStrappers))
+}
+
+func TestValidateMainnetCcqBootstrapPeers(t *testing.T) {
+	bootstrapPeers, err := GetCcqBootstrapPeers(common.MainNet)
+	require.NoError(t, err)
+	require.NotEqual(t, "", bootstrapPeers)
+
+	// Make sure we can parse the result.
+	logger := zap.NewNop()
+	bootStrappers, _ := BootstrapAddrs(logger, bootstrapPeers, "somePeerID")
+	assert.Equal(t, 3, len(bootStrappers))
+}
+
+func TestValidateTestnetBootstrapPeers(t *testing.T) {
+	bootstrapPeers, err := GetBootstrapPeers(common.TestNet)
+	require.NoError(t, err)
+	require.NotEqual(t, "", bootstrapPeers)
+
+	// Make sure we can parse the result.
+	logger := zap.NewNop()
+	bootStrappers, _ := BootstrapAddrs(logger, bootstrapPeers, "somePeerID")
+	assert.Equal(t, 3, len(bootStrappers))
+}
+
+func TestValidateTestnetCcqBootstrapPeers(t *testing.T) {
+	bootstrapPeers, err := GetCcqBootstrapPeers(common.TestNet)
+	require.NoError(t, err)
+	require.NotEqual(t, "", bootstrapPeers)
+
+	// Make sure we can parse the result.
+	logger := zap.NewNop()
+	bootStrappers, _ := BootstrapAddrs(logger, bootstrapPeers, "somePeerID")
+	assert.Equal(t, 3, len(bootStrappers))
+}
+
+func TestGetBootstrapPeersFailsForUnsupportedEnvironment(t *testing.T) {
+	bootstrapPeers, err := GetBootstrapPeers(common.UnsafeDevNet)
+	require.ErrorContains(t, err, "unsupported environment")
+	require.Equal(t, "", bootstrapPeers)
+}
+
+func TestGetCcqBootstrapPeersFailsForUnsupportedEnvironment(t *testing.T) {
+	bootstrapPeers, err := GetCcqBootstrapPeers(common.UnsafeDevNet)
+	require.ErrorContains(t, err, "unsupported environment")
+	require.Equal(t, "", bootstrapPeers)
+}