diff --git a/cmd/bee/cmd/cmd.go b/cmd/bee/cmd/cmd.go index 72c8872bc72..c14364fa139 100644 --- a/cmd/bee/cmd/cmd.go +++ b/cmd/bee/cmd/cmd.go @@ -352,7 +352,8 @@ func (c *command) setAllFlags(cmd *cobra.Command) { cmd.Flags().Int64(optionNamePaymentTolerance, 25, "excess debt above payment threshold in percentages where you disconnect from your peer") cmd.Flags().Int64(optionNamePaymentEarly, 50, "percentage below the peers payment threshold when we initiate settlement") cmd.Flags().StringSlice(optionNameResolverEndpoints, []string{}, "ENS compatible API endpoint for a TLD and with Universal Resolver address, can be repeated, format [tld:][contract-addr@]url. Default contract is the ENS Universal Resolver, not the registry") - cmd.Flags().Bool(optionNameBootnodeMode, false, "cause the node to always accept incoming connections") + cmd.Flags().Bool(optionNameBootnodeMode, false, "cause the node to always accept incoming connections (deprecated: use --node-mode=bootnode)") + _ = cmd.Flags().MarkDeprecated(optionNameBootnodeMode, "use --node-mode=bootnode instead") cmd.Flags().String(optionNameBlockchainRpcEndpoint, "", "rpc blockchain endpoint") cmd.Flags().Duration(optionNameBlockchainRpcDialTimeout, 30*time.Second, "blockchain rpc TCP dial timeout") cmd.Flags().Duration(optionNameBlockchainRpcTLSTimeout, 10*time.Second, "blockchain rpc TLS handshake timeout") @@ -361,7 +362,7 @@ func (c *command) setAllFlags(cmd *cobra.Command) { cmd.Flags().String(optionNameSwapFactoryAddress, "", "swap factory addresses") cmd.Flags().String(optionNameBzzTokenAddress, "", "bzz token contract address") cmd.Flags().String(optionNameSwapInitialDeposit, "0", "initial deposit if deploying a new chequebook") - cmd.Flags().String(optionNameNodeMode, "", "node operational mode: full, light, or ultra-light (unset: inferred from deprecated full-node and blockchain-rpc-endpoint)") + cmd.Flags().String(optionNameNodeMode, "", "node operational mode: full, light, ultra-light, or bootnode (unset: inferred from deprecated full-node, bootnode-mode and blockchain-rpc-endpoint)") cmd.Flags().Bool(optionNameSwapEnable, false, "enable swap") cmd.Flags().Bool(optionNameChequebookEnable, true, "enable chequebook") cmd.Flags().Bool(optionNameChequebookVerification, false, "reject full-node hive/handshake records that carry no chequebook address") diff --git a/cmd/bee/cmd/resolve_node_mode_test.go b/cmd/bee/cmd/resolve_node_mode_test.go index 6e22c572cbc..bb06d39af11 100644 --- a/cmd/bee/cmd/resolve_node_mode_test.go +++ b/cmd/bee/cmd/resolve_node_mode_test.go @@ -6,6 +6,7 @@ package cmd_test import ( "bytes" + "fmt" "strings" "testing" @@ -369,6 +370,89 @@ func TestResolveNodeMode(t *testing.T) { }, wantMode: node.FullMode, }, + { + name: "bootnode implies no swap, chequebook or incentives", + config: map[string]any{ + cmd.OptionNameNodeMode: "bootnode", + cmd.ConfigKeyBlockchainRpcEndpoint: testRPCEndpoint, + }, + wantMode: node.FullMode, + wantOptions: map[string]bool{ + cmd.OptionNameBootnodeMode: true, + cmd.OptionNameSwapEnable: false, + cmd.OptionNameChequebookEnable: false, + cmd.OptionNameStorageIncentivesEnable: false, + }, + }, + { + name: "uppercase bootnode is accepted", + config: map[string]any{ + cmd.OptionNameNodeMode: " BOOTNODE ", + cmd.ConfigKeyBlockchainRpcEndpoint: testRPCEndpoint, + }, + wantMode: node.FullMode, + wantOptions: map[string]bool{cmd.OptionNameBootnodeMode: true}, + }, + { + name: "bootnode without rpc is rejected", + config: map[string]any{ + cmd.OptionNameNodeMode: "bootnode", + }, + wantErr: "bootnode requires blockchain-rpc-endpoint", + }, + { + name: "bootnode with swap enables chequebook", + config: map[string]any{ + cmd.OptionNameNodeMode: "bootnode", + cmd.ConfigKeyBlockchainRpcEndpoint: testRPCEndpoint, + cmd.OptionNameSwapEnable: true, + }, + wantMode: node.FullMode, + wantOptions: map[string]bool{ + cmd.OptionNameBootnodeMode: true, + cmd.OptionNameSwapEnable: true, + cmd.OptionNameChequebookEnable: true, + }, + }, + { + name: "bootnode accepts storage incentives it cannot use and reserve doubling", + config: map[string]any{ + cmd.OptionNameNodeMode: "bootnode", + cmd.ConfigKeyBlockchainRpcEndpoint: testRPCEndpoint, + cmd.OptionNameStorageIncentivesEnable: true, + cmd.OptionNameReserveCapacityDoubling: 1, + }, + wantMode: node.FullMode, + wantOptions: map[string]bool{cmd.OptionNameBootnodeMode: true}, + }, + { + name: "bootnode rejects chequebook without swap", + config: map[string]any{ + cmd.OptionNameNodeMode: "bootnode", + cmd.ConfigKeyBlockchainRpcEndpoint: testRPCEndpoint, + cmd.OptionNameChequebookEnable: true, + }, + wantErr: "chequebook-enable requires swap-enable", + }, + { + name: "bootnode overrides an explicit bootnode-mode false", + config: map[string]any{ + cmd.OptionNameNodeMode: "bootnode", + cmd.ConfigKeyBlockchainRpcEndpoint: testRPCEndpoint, + cmd.OptionNameBootnodeMode: false, + }, + wantMode: node.FullMode, + wantOptions: map[string]bool{cmd.OptionNameBootnodeMode: true}, + }, + { + name: "bootnode accepts chequebook-verification without swap", + config: map[string]any{ + cmd.OptionNameNodeMode: "bootnode", + cmd.ConfigKeyBlockchainRpcEndpoint: testRPCEndpoint, + cmd.OptionNameChequebookVerification: true, + }, + wantMode: node.FullMode, + }, { name: "light bootnode is rejected", config: map[string]any{ @@ -634,6 +718,17 @@ func TestResolveNodeModeWithBoundFlags(t *testing.T) { cmd.OptionNameStorageIncentivesEnable: false, }, }, + { + name: "node-mode bootnode does not inherit chequebook or incentives defaults", + args: []string{"--node-mode=bootnode", "--blockchain-rpc-endpoint=" + testRPCEndpoint}, + wantMode: node.FullMode, + wantOptions: map[string]bool{ + cmd.OptionNameBootnodeMode: true, + cmd.OptionNameSwapEnable: false, + cmd.OptionNameChequebookEnable: false, + cmd.OptionNameStorageIncentivesEnable: false, + }, + }, { name: "node-mode full bootnode with swap enables chequebook", args: []string{"--node-mode=full", "--bootnode-mode", "--swap-enable", "--blockchain-rpc-endpoint=" + testRPCEndpoint}, @@ -788,6 +883,38 @@ func TestResolveNodeModeWarnings(t *testing.T) { args: []string{"--blockchain-rpc-endpoint= "}, want: []string{`"set"=["node-mode=light"]`, "blockchain-rpc-endpoint contains only whitespace"}, }, + { + name: "full with bootnode-mode is deprecated in favor of node-mode bootnode", + args: []string{"--node-mode=full", "--bootnode-mode", "--blockchain-rpc-endpoint=" + testRPCEndpoint}, + want: []string{"bootnode-mode is deprecated; use node-mode: bootnode"}, + }, + { + name: "node-mode bootnode is not deprecated", + args: []string{"--node-mode=bootnode", "--blockchain-rpc-endpoint=" + testRPCEndpoint}, + absent: []string{"is deprecated", "has no effect", "is ignored"}, + }, + { + name: "bootnode warns about storage incentives it cannot use", + args: []string{"--node-mode=bootnode", "--storage-incentives-enable", "--reserve-capacity-doubling=1", "--blockchain-rpc-endpoint=" + testRPCEndpoint}, + want: []string{"storage-incentives-enable has no effect on a bootnode"}, + absent: []string{"reserve-capacity-doubling"}, + }, + { + name: "bootnode reports an ignored bootnode-mode false", + args: []string{"--node-mode=bootnode", "--bootnode-mode=false", "--blockchain-rpc-endpoint=" + testRPCEndpoint}, + want: []string{"bootnode-mode=false is ignored with node-mode: bootnode"}, + }, + { + name: "legacy bootnode lists the bootnode migration", + args: []string{"--full-node", "--bootnode-mode", "--blockchain-rpc-endpoint=" + testRPCEndpoint}, + want: []string{`"node_mode"="bootnode"`, `"set"=["node-mode=bootnode"]`, `"remove"=["full-node","bootnode-mode"]`}, + }, + { + name: "legacy bootnode keeps reserve doubling", + args: []string{"--full-node", "--bootnode-mode", "--reserve-capacity-doubling=1", "--blockchain-rpc-endpoint=" + testRPCEndpoint}, + want: []string{`"remove"=["full-node","bootnode-mode"]`}, + absent: []string{`"reserve-capacity-doubling"`}, + }, { name: "legacy bootnode error is not preceded by the migration warning", args: []string{"--bootnode-mode", "--blockchain-rpc-endpoint=" + testRPCEndpoint}, @@ -838,97 +965,105 @@ func TestLegacyMigrationPreservesBehavior(t *testing.T) { tri := []string{"", "true", "false"} type effect struct { - mode node.NodeMode - swap, chequebook, incentives, verification bool + mode node.NodeMode + bootnode, swap, chequebook, incentives, verification bool + doubling int } - effectOf := func(cfg *viper.Viper, mode node.NodeMode, bootnode bool) effect { + // The bootnode flag is read from the resolved config, so a migration to + // node-mode: bootnode must set it for the node to behave the same. + effectOf := func(cfg *viper.Viper, mode node.NodeMode) effect { + bootnode := cfg.GetBool(cmd.OptionNameBootnodeMode) swap := cfg.GetBool(cmd.OptionNameSwapEnable) return effect{ mode: mode, + bootnode: bootnode, swap: swap, chequebook: swap && cfg.GetBool(cmd.OptionNameChequebookEnable), incentives: mode == node.FullMode && !bootnode && cfg.GetBool(cmd.OptionNameStorageIncentivesEnable), verification: cfg.GetBool(cmd.OptionNameChequebookVerification), + doubling: cfg.GetInt(cmd.OptionNameReserveCapacityDoubling), } } var checked, rejected, changed int - for _, bootnode := range []bool{false, true} { + for _, bootnode := range tri { for _, rpc := range []bool{false, true} { for _, fullNode := range tri { for _, swap := range tri { for _, chequebook := range tri { for _, incentives := range tri { for _, verification := range []string{"", "true"} { - var args []string - add := func(name, value string) { - if value != "" { - args = append(args, "--"+name+"="+value) + for _, doubling := range []string{"", "1"} { + var args []string + add := func(name, value string) { + if value != "" { + args = append(args, "--"+name+"="+value) + } } - } - if bootnode { - add(cmd.OptionNameBootnodeMode, "true") - } - if rpc { - add("blockchain-rpc-endpoint", testRPCEndpoint) - } - add(cmd.OptionNameFullNode, fullNode) - add(cmd.OptionNameSwapEnable, swap) - add(cmd.OptionNameChequebookEnable, chequebook) - add(cmd.OptionNameStorageIncentivesEnable, incentives) - add(cmd.OptionNameChequebookVerification, verification) - - // Rules of releases before node-mode: a bootnode must be - // full, a full node needs a chain, swap needs a chain, - // and verification needs a full node with a chain. - full := fullNode == "true" - chain := full || rpc - wantRejected := (bootnode && !full) || (full && !rpc) || (swap == "true" && !chain) || - (verification == "true" && (!full || !chain)) - - legacyCfg := boundStartConfig(t, args) - mode, set, remove, notes, err := cmd.LegacyMigrationForTest(legacyCfg) - if (err != nil) != wantRejected { - t.Errorf("%v: rejected=%t, want %t (err: %v)", args, err != nil, wantRejected, err) - continue - } - if err != nil { - rejected++ - continue - } - checked++ - before := effectOf(legacyCfg, mode, bootnode) - - var migrated []string - next: - for _, a := range args { - for _, r := range remove { - if strings.HasPrefix(a, "--"+r+"=") { - continue next + add(cmd.OptionNameBootnodeMode, bootnode) + if rpc { + add("blockchain-rpc-endpoint", testRPCEndpoint) + } + add(cmd.OptionNameFullNode, fullNode) + add(cmd.OptionNameSwapEnable, swap) + add(cmd.OptionNameChequebookEnable, chequebook) + add(cmd.OptionNameStorageIncentivesEnable, incentives) + add(cmd.OptionNameChequebookVerification, verification) + add(cmd.OptionNameReserveCapacityDoubling, doubling) + + // Rules of releases before node-mode: a bootnode must be + // full, a full node needs a chain, swap needs a chain, + // verification needs a full node with a chain, and + // reserve capacity doubling needs a full node. + full := fullNode == "true" + chain := full || rpc + wantRejected := (bootnode == "true" && !full) || (full && !rpc) || (swap == "true" && !chain) || + (verification == "true" && (!full || !chain)) || (doubling != "" && !full) + + legacyCfg := boundStartConfig(t, args) + mode, set, remove, notes, err := cmd.LegacyMigrationForTest(legacyCfg) + if (err != nil) != wantRejected { + t.Errorf("%v: rejected=%t, want %t (err: %v)", args, err != nil, wantRejected, err) + continue + } + if err != nil { + rejected++ + continue + } + checked++ + before := effectOf(legacyCfg, mode) + + var migrated []string + next: + for _, a := range args { + for _, r := range remove { + if strings.HasPrefix(a, "--"+r+"=") { + continue next + } } + migrated = append(migrated, a) + } + for _, kv := range set { + k, v, _ := strings.Cut(kv, "=") + migrated = append(migrated, "--"+k+"="+v) } - migrated = append(migrated, a) - } - for _, kv := range set { - k, v, _ := strings.Cut(kv, "=") - migrated = append(migrated, "--"+k+"="+v) - } - migratedCfg := boundStartConfig(t, migrated) - gotMode, err := cmd.ResolveNodeModeForTest(migratedCfg) - if len(notes) > 0 { - changed++ - if err == nil && effectOf(migratedCfg, gotMode, bootnode) == before { - t.Errorf("%v: warning notes a behavior change %v, but the migrated config %v behaves the same", args, notes, migrated) + migratedCfg := boundStartConfig(t, migrated) + gotMode, err := cmd.ResolveNodeModeForTest(migratedCfg) + if len(notes) > 0 { + changed++ + if err == nil && effectOf(migratedCfg, gotMode) == before { + t.Errorf("%v: warning notes a behavior change %v, but the migrated config %v behaves the same", args, notes, migrated) + } + continue + } + if err != nil { + t.Errorf("%v: migrated config %v fails: %v", args, migrated, err) + continue + } + if after := effectOf(migratedCfg, gotMode); after != before { + t.Errorf("%v: migrated config %v changes behavior: before %+v, after %+v", args, migrated, before, after) } - continue - } - if err != nil { - t.Errorf("%v: migrated config %v fails: %v", args, migrated, err) - continue - } - if after := effectOf(migratedCfg, gotMode, bootnode); after != before { - t.Errorf("%v: migrated config %v changes behavior: before %+v, after %+v", args, migrated, before, after) } } } @@ -968,6 +1103,71 @@ func TestResolveNodeModeTrimsRPCEndpoint(t *testing.T) { } } +// TestNodeModeBootnodeMatchesFullBootnode checks that node-mode: bootnode +// resolves exactly like node-mode: full with the deprecated bootnode-mode for +// every combination of the options a bootnode config may carry: the same mode +// and option values, or the same error. +func TestNodeModeBootnodeMatchesFullBootnode(t *testing.T) { + t.Parallel() + + tri := []string{"", "true", "false"} + options := []string{ + cmd.OptionNameBootnodeMode, + cmd.OptionNameSwapEnable, + cmd.OptionNameChequebookEnable, + cmd.OptionNameStorageIncentivesEnable, + cmd.OptionNameChequebookVerification, + } + resolve := func(args []string) string { + cfg := boundStartConfig(t, args) + mode, err := cmd.ResolveNodeModeForTest(cfg) + if err != nil { + return "error: " + err.Error() + } + got := fmt.Sprintf("mode=%s reserve-capacity-doubling=%d", mode, cfg.GetInt(cmd.OptionNameReserveCapacityDoubling)) + for _, o := range options { + got += fmt.Sprintf(" %s=%t", o, cfg.GetBool(o)) + } + return got + } + + var compared int + for _, rpc := range []bool{false, true} { + for _, swap := range tri { + for _, chequebook := range tri { + for _, incentives := range tri { + for _, verification := range []string{"", "true"} { + for _, doubling := range []string{"", "1"} { + var rest []string + add := func(name, value string) { + if value != "" { + rest = append(rest, "--"+name+"="+value) + } + } + if rpc { + add("blockchain-rpc-endpoint", testRPCEndpoint) + } + add(cmd.OptionNameSwapEnable, swap) + add(cmd.OptionNameChequebookEnable, chequebook) + add(cmd.OptionNameStorageIncentivesEnable, incentives) + add(cmd.OptionNameChequebookVerification, verification) + add(cmd.OptionNameReserveCapacityDoubling, doubling) + + bootnode := resolve(append([]string{"--node-mode=bootnode"}, rest...)) + alias := resolve(append([]string{"--node-mode=full", "--bootnode-mode"}, rest...)) + if bootnode != alias { + t.Errorf("%v:\n node-mode=bootnode: %s\n node-mode=full, bootnode-mode: %s", rest, bootnode, alias) + } + compared++ + } + } + } + } + } + } + t.Logf("compared %d option combinations", compared) +} + // boundStartConfig returns a viper bound to the real start command flags // parsed from args, as production does. func boundStartConfig(t *testing.T, args []string) *viper.Viper { diff --git a/cmd/bee/cmd/start.go b/cmd/bee/cmd/start.go index 213b9fa193b..3c208758719 100644 --- a/cmd/bee/cmd/start.go +++ b/cmd/bee/cmd/start.go @@ -374,6 +374,11 @@ func buildBeeNode(ctx context.Context, c *command, cmd *cobra.Command, logger lo return b, err } +// nodeModeBootnode is the node-mode value for bootnodes. It is a config-level +// mode: the node runs as a full node with bootnode-mode set, exactly as a full +// node configured with the deprecated bootnode-mode option. +const nodeModeBootnode = "bootnode" + // legacyNodeModeRemovalVersion is the release in which node-mode inference from // the deprecated full-node option and blockchain-rpc-endpoint presence is // removed. From that release on, node-mode is required. @@ -390,12 +395,13 @@ const legacyNodeModeRemovalVersion = "v2.11.0" // node-mode is set, and the release in which the inference is removed. // // node-mode set: the mode owns the config. Options the mode implies are -// enabled unless the operator explicitly disabled them (full bootnodes are -// exempt), options the mode cannot support are rejected when explicitly enabled +// enabled unless the operator explicitly disabled them (bootnodes are exempt), +// options the mode cannot support are rejected when explicitly enabled // (overriding the pflag defaults of true when unset), and -// blockchain-rpc-endpoint is required for light and full nodes. An unset -// chequebook-enable follows swap-enable. Implied values are written back to -// config so the rest of node startup picks them up. +// blockchain-rpc-endpoint is required for light, full and bootnodes. An unset +// chequebook-enable follows swap-enable. node-mode: bootnode runs as a full +// node with bootnode-mode set. Implied values are written back to config so the +// rest of node startup picks them up. // // In both regimes, options the resolved mode cannot run with are rejected // before the node is built. @@ -427,13 +433,25 @@ func (c *command) resolveNodeMode(logger log.Logger) (node.NodeMode, error) { // resolveExplicitNodeMode applies the regime in which node-mode is set. The // value is matched case-insensitively, ignoring surrounding whitespace. func (c *command) resolveExplicitNodeMode(logger log.Logger, raw string) (node.NodeMode, error) { - mode := node.NodeMode(strings.ToLower(strings.TrimSpace(raw))) + value := strings.ToLower(strings.TrimSpace(raw)) + bootnodeValue := value == nodeModeBootnode + if bootnodeValue { + if c.config.IsSet(optionNameBootnodeMode) && !c.config.GetBool(optionNameBootnodeMode) { + logger.Warning("bootnode-mode=false is ignored with node-mode: bootnode") + } + c.config.Set(optionNameBootnodeMode, true) + value = string(node.FullMode) + } + mode := node.NodeMode(value) if !mode.IsValid() { - return "", fmt.Errorf("invalid node-mode %q: must be one of full, light, ultra-light", raw) + return "", fmt.Errorf("invalid node-mode %q: must be one of full, light, ultra-light, bootnode", raw) } if c.config.GetBool(optionNameFullNode) { logger.Warning("--full-node is set alongside --node-mode; --full-node is ignored") } + if !bootnodeValue && mode == node.FullMode && c.config.GetBool(optionNameBootnodeMode) { + logger.Warning("bootnode-mode is deprecated; use node-mode: bootnode instead of node-mode: full with bootnode-mode") + } // Surrounding whitespace is not significant: a whitespace-only endpoint // counts as unset, and the trimmed value is what the node dials. @@ -444,23 +462,30 @@ func (c *command) resolveExplicitNodeMode(logger log.Logger, raw string) (node.N switch mode { case node.FullMode: + bootnode := c.config.GetBool(optionNameBootnodeMode) if rpcEndpoint == "" { - return "", errors.New("full node requires blockchain-rpc-endpoint to be set") + kind := "full node" + if bootnode { + kind = "bootnode" + } + return "", fmt.Errorf("%s requires blockchain-rpc-endpoint to be set", kind) } // A full node implies swap, chequebook and storage incentives. Bootnodes // are exempt: NewBee never runs the reserve or the incentives agent for // them, so they have no use for the implied options. Swap still starts on // a bootnode when explicitly enabled, and chequebook follows it. - if !c.config.GetBool(optionNameBootnodeMode) { + if !bootnode { c.enableImpliedOption(logger, mode, optionNameSwapEnable) if c.config.GetBool(optionNameSwapEnable) { c.enableImpliedOption(logger, mode, optionNameChequebookEnable) } c.enableImpliedOption(logger, mode, optionNameStorageIncentivesEnable) - } else if !c.config.IsSet(optionNameStorageIncentivesEnable) { + break + } + if !c.config.IsSet(optionNameStorageIncentivesEnable) { c.config.Set(optionNameStorageIncentivesEnable, false) } else if c.config.GetBool(optionNameStorageIncentivesEnable) { - logger.Warning("storage-incentives-enable has no effect on a bootnode", "node_mode", mode) + logger.Warning("storage-incentives-enable has no effect on a bootnode", "node_mode", nodeModeBootnode) } case node.LightMode: if rpcEndpoint == "" { @@ -558,7 +583,7 @@ func (c *command) validateNodeModeOptions(mode node.NodeMode) error { chainEnabled := mode == node.FullMode || mode == node.LightMode switch { case c.config.GetBool(optionNameBootnodeMode) && mode != node.FullMode: - return errors.New("boot node must be started as a full node") + return errors.New("boot node must be started as a full node; use node-mode: bootnode") case mode != node.FullMode && c.config.GetInt(optionReserveCapacityDoubling) != 0: return errors.New("reserve capacity doubling is only allowed for full nodes") case c.config.GetBool(optionNameSwapEnable) && !chainEnabled: @@ -585,10 +610,17 @@ func (c *command) legacyMigration(mode node.NodeMode) legacyNodeModeMigration { bootnode := c.config.GetBool(optionNameBootnodeMode) swap := c.config.GetBool(optionNameSwapEnable) - m.set = append(m.set, optionNameNodeMode+"="+string(mode)) + if bootnode { + m.set = append(m.set, optionNameNodeMode+"="+nodeModeBootnode) + } else { + m.set = append(m.set, optionNameNodeMode+"="+string(mode)) + } if c.config.IsSet(optionNameFullNode) { m.remove = append(m.remove, optionNameFullNode) } + if c.config.IsSet(optionNameBootnodeMode) { + m.remove = append(m.remove, optionNameBootnodeMode) + } // node-mode: full implies swap, which legacy full nodes did not. if mode == node.FullMode && !bootnode && !c.config.IsSet(optionNameSwapEnable) { m.set = append(m.set, optionNameSwapEnable+"=false") @@ -615,8 +647,12 @@ func (c *command) legacyMigration(mode node.NodeMode) legacyNodeModeMigration { // config changes that keep the node's behavior once node-mode is set. func (c *command) logLegacyNodeModeMigration(logger log.Logger, mode node.NodeMode) { m := c.legacyMigration(mode) + inferred := string(mode) + if c.config.GetBool(optionNameBootnodeMode) { + inferred = nodeModeBootnode + } kv := []any{ - "node_mode", mode, + "node_mode", inferred, "removed_in", legacyNodeModeRemovalVersion, "set", m.set, } diff --git a/packaging/bee.yaml b/packaging/bee.yaml index 19e474dd2f6..86f9efb839a 100644 --- a/packaging/bee.yaml +++ b/packaging/bee.yaml @@ -4,15 +4,18 @@ ## Selects the operational mode of this node. ## full - participates in storage and incentives; requires blockchain-rpc; ## implies swap-enable, chequebook-enable and storage-incentives-enable -## (bootnodes are exempt: swap only when set, chequebook follows swap) ## light - uploads and downloads only; requires blockchain-rpc; beyond the ## free allowance also needs swap-enable and a funded chequebook ## ultra-light - free-tier downloads only; no blockchain connection needed +## bootnode - always accepts incoming connections to help peers join; +## requires blockchain-rpc; implies no swap, chequebook or +## storage incentives (chequebook follows swap when it is set) ## When unset (the default), the mode is inferred from the deprecated full-node -## option and blockchain-rpc presence (deprecated, removed in v2.11.0). +## and bootnode-mode options and blockchain-rpc presence (deprecated, removed in +## v2.11.0). # node-mode: "" -## ── Blockchain / RPC (required for full and light nodes) ───────────────────── +## ── Blockchain / RPC (required for full, light and bootnodes) ──────────────── ## blockchain rpc configuration # blockchain-rpc: # endpoint: "" @@ -60,7 +63,7 @@ # network-id: "1" ## initial nodes to connect to # bootnode: ["/dnsaddr/mainnet.ethswarm.org"] -## cause the node to always accept incoming connections +## cause the node to always accept incoming connections (deprecated: use node-mode: bootnode) # bootnode-mode: false ## protect nodes from getting kicked out on bootnode # static-nodes: [] diff --git a/packaging/docker/README.md b/packaging/docker/README.md index ee73934432c..8a1201c5245 100644 --- a/packaging/docker/README.md +++ b/packaging/docker/README.md @@ -11,7 +11,7 @@ wget -q https://raw.githubusercontent.com/ethersphere/bee/master/packaging/docke Set all configuration variables inside `.env` -Select the node mode with `BEE_NODE_MODE` (`full`, `light`, or `ultra-light`). A full node needs `BEE_BLOCKCHAIN_RPC_ENDPOINT` and implies swap, chequebook and storage incentives. +Select the node mode with `BEE_NODE_MODE` (`full`, `light`, `ultra-light`, or `bootnode`). A full node needs `BEE_BLOCKCHAIN_RPC_ENDPOINT` and implies swap, chequebook and storage incentives; a bootnode also needs it but implies none of them. Bee requires an Ethereum endpoint to function. Obtain a free Infura account and set: diff --git a/packaging/docker/env b/packaging/docker/env index e65c13ee23a..d71e770d870 100644 --- a/packaging/docker/env +++ b/packaging/docker/env @@ -29,7 +29,7 @@ # BEE_BLOCKCHAIN_RPC_TLS_TIMEOUT=10s ## initial nodes to connect to (default [/dnsaddr/mainnet.ethswarm.org]) # BEE_BOOTNODE=[/dnsaddr/mainnet.ethswarm.org] -## cause the node to always accept incoming connections (default false) +## cause the node to always accept incoming connections (deprecated: use BEE_NODE_MODE=bootnode) (default false) # BEE_BOOTNODE_MODE=false ## bzz token contract address # BEE_BZZ_TOKEN_ADDRESS= @@ -71,7 +71,7 @@ # BEE_NEIGHBORHOOD_SUGGESTER=https://api.swarmscan.io/v1/network/neighborhoods/suggestion ## ID of the Swarm network (default mainnet id from bee) # BEE_NETWORK_ID=1 -## node operational mode: full, light, or ultra-light; unset infers the mode from BEE_FULL_NODE and BEE_BLOCKCHAIN_RPC_ENDPOINT (deprecated) (default unset) +## node operational mode: full, light, ultra-light, or bootnode; unset infers the mode from BEE_FULL_NODE, BEE_BOOTNODE_MODE and BEE_BLOCKCHAIN_RPC_ENDPOINT (deprecated) (default unset) # BEE_NODE_MODE= ## P2P listen address (default :1634) # BEE_P2P_ADDR=:1634 diff --git a/packaging/homebrew-amd64/bee.yaml b/packaging/homebrew-amd64/bee.yaml index 2e9e2c4aebd..4707eb56926 100644 --- a/packaging/homebrew-amd64/bee.yaml +++ b/packaging/homebrew-amd64/bee.yaml @@ -4,15 +4,18 @@ ## Selects the operational mode of this node. ## full - participates in storage and incentives; requires blockchain-rpc; ## implies swap-enable, chequebook-enable and storage-incentives-enable -## (bootnodes are exempt: swap only when set, chequebook follows swap) ## light - uploads and downloads only; requires blockchain-rpc; beyond the ## free allowance also needs swap-enable and a funded chequebook ## ultra-light - free-tier downloads only; no blockchain connection needed +## bootnode - always accepts incoming connections to help peers join; +## requires blockchain-rpc; implies no swap, chequebook or +## storage incentives (chequebook follows swap when it is set) ## When unset (the default), the mode is inferred from the deprecated full-node -## option and blockchain-rpc presence (deprecated, removed in v2.11.0). +## and bootnode-mode options and blockchain-rpc presence (deprecated, removed in +## v2.11.0). # node-mode: "" -## ── Blockchain / RPC (required for full and light nodes) ───────────────────── +## ── Blockchain / RPC (required for full, light and bootnodes) ──────────────── ## blockchain rpc configuration # blockchain-rpc: # endpoint: "" @@ -60,7 +63,7 @@ # network-id: "1" ## initial nodes to connect to # bootnode: ["/dnsaddr/mainnet.ethswarm.org"] -## cause the node to always accept incoming connections +## cause the node to always accept incoming connections (deprecated: use node-mode: bootnode) # bootnode-mode: false ## protect nodes from getting kicked out on bootnode # static-nodes: [] diff --git a/packaging/homebrew-arm64/bee.yaml b/packaging/homebrew-arm64/bee.yaml index 03ad30d6db4..ad85340c24a 100644 --- a/packaging/homebrew-arm64/bee.yaml +++ b/packaging/homebrew-arm64/bee.yaml @@ -4,15 +4,18 @@ ## Selects the operational mode of this node. ## full - participates in storage and incentives; requires blockchain-rpc; ## implies swap-enable, chequebook-enable and storage-incentives-enable -## (bootnodes are exempt: swap only when set, chequebook follows swap) ## light - uploads and downloads only; requires blockchain-rpc; beyond the ## free allowance also needs swap-enable and a funded chequebook ## ultra-light - free-tier downloads only; no blockchain connection needed +## bootnode - always accepts incoming connections to help peers join; +## requires blockchain-rpc; implies no swap, chequebook or +## storage incentives (chequebook follows swap when it is set) ## When unset (the default), the mode is inferred from the deprecated full-node -## option and blockchain-rpc presence (deprecated, removed in v2.11.0). +## and bootnode-mode options and blockchain-rpc presence (deprecated, removed in +## v2.11.0). # node-mode: "" -## ── Blockchain / RPC (required for full and light nodes) ───────────────────── +## ── Blockchain / RPC (required for full, light and bootnodes) ──────────────── ## blockchain rpc configuration # blockchain-rpc: # endpoint: "" @@ -60,7 +63,7 @@ # network-id: "1" ## initial nodes to connect to # bootnode: ["/dnsaddr/mainnet.ethswarm.org"] -## cause the node to always accept incoming connections +## cause the node to always accept incoming connections (deprecated: use node-mode: bootnode) # bootnode-mode: false ## protect nodes from getting kicked out on bootnode # static-nodes: [] diff --git a/packaging/scoop/bee.yaml b/packaging/scoop/bee.yaml index 713b175eb39..0257376ac98 100644 --- a/packaging/scoop/bee.yaml +++ b/packaging/scoop/bee.yaml @@ -4,15 +4,18 @@ ## Selects the operational mode of this node. ## full - participates in storage and incentives; requires blockchain-rpc; ## implies swap-enable, chequebook-enable and storage-incentives-enable -## (bootnodes are exempt: swap only when set, chequebook follows swap) ## light - uploads and downloads only; requires blockchain-rpc; beyond the ## free allowance also needs swap-enable and a funded chequebook ## ultra-light - free-tier downloads only; no blockchain connection needed +## bootnode - always accepts incoming connections to help peers join; +## requires blockchain-rpc; implies no swap, chequebook or +## storage incentives (chequebook follows swap when it is set) ## When unset (the default), the mode is inferred from the deprecated full-node -## option and blockchain-rpc presence (deprecated, removed in v2.11.0). +## and bootnode-mode options and blockchain-rpc presence (deprecated, removed in +## v2.11.0). # node-mode: "" -## ── Blockchain / RPC (required for full and light nodes) ───────────────────── +## ── Blockchain / RPC (required for full, light and bootnodes) ──────────────── ## blockchain rpc configuration # blockchain-rpc: # endpoint: "" @@ -60,7 +63,7 @@ # network-id: "1" ## initial nodes to connect to # bootnode: ["/dnsaddr/mainnet.ethswarm.org"] -## cause the node to always accept incoming connections +## cause the node to always accept incoming connections (deprecated: use node-mode: bootnode) # bootnode-mode: false ## protect nodes from getting kicked out on bootnode # static-nodes: []