github.com/ethersphere/bee/v2@v2.2.0/pkg/node/node.go (about)

     1  // Copyright 2020 The Swarm Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package node defines the concept of a Bee node
     6  // by bootstrapping and injecting all necessary
     7  // dependencies.
     8  package node
     9  
    10  import (
    11  	"context"
    12  	"crypto/ecdsa"
    13  	"encoding/hex"
    14  	"errors"
    15  	"fmt"
    16  	"io"
    17  	stdlog "log"
    18  	"math/big"
    19  	"net"
    20  	"net/http"
    21  	"path/filepath"
    22  	"runtime"
    23  	"sync"
    24  	"sync/atomic"
    25  	"time"
    26  
    27  	"github.com/ethereum/go-ethereum/common"
    28  	"github.com/ethersphere/bee/v2/pkg/accesscontrol"
    29  	"github.com/ethersphere/bee/v2/pkg/accounting"
    30  	"github.com/ethersphere/bee/v2/pkg/addressbook"
    31  	"github.com/ethersphere/bee/v2/pkg/api"
    32  	"github.com/ethersphere/bee/v2/pkg/config"
    33  	"github.com/ethersphere/bee/v2/pkg/crypto"
    34  	"github.com/ethersphere/bee/v2/pkg/feeds/factory"
    35  	"github.com/ethersphere/bee/v2/pkg/hive"
    36  	"github.com/ethersphere/bee/v2/pkg/log"
    37  	"github.com/ethersphere/bee/v2/pkg/metrics"
    38  	"github.com/ethersphere/bee/v2/pkg/p2p"
    39  	"github.com/ethersphere/bee/v2/pkg/p2p/libp2p"
    40  	"github.com/ethersphere/bee/v2/pkg/pingpong"
    41  	"github.com/ethersphere/bee/v2/pkg/postage"
    42  	"github.com/ethersphere/bee/v2/pkg/postage/batchservice"
    43  	"github.com/ethersphere/bee/v2/pkg/postage/batchstore"
    44  	"github.com/ethersphere/bee/v2/pkg/postage/listener"
    45  	"github.com/ethersphere/bee/v2/pkg/postage/postagecontract"
    46  	"github.com/ethersphere/bee/v2/pkg/pricer"
    47  	"github.com/ethersphere/bee/v2/pkg/pricing"
    48  	"github.com/ethersphere/bee/v2/pkg/pss"
    49  	"github.com/ethersphere/bee/v2/pkg/puller"
    50  	"github.com/ethersphere/bee/v2/pkg/pullsync"
    51  	"github.com/ethersphere/bee/v2/pkg/pusher"
    52  	"github.com/ethersphere/bee/v2/pkg/pushsync"
    53  	"github.com/ethersphere/bee/v2/pkg/resolver/multiresolver"
    54  	"github.com/ethersphere/bee/v2/pkg/retrieval"
    55  	"github.com/ethersphere/bee/v2/pkg/salud"
    56  	"github.com/ethersphere/bee/v2/pkg/settlement/pseudosettle"
    57  	"github.com/ethersphere/bee/v2/pkg/settlement/swap"
    58  	"github.com/ethersphere/bee/v2/pkg/settlement/swap/chequebook"
    59  	"github.com/ethersphere/bee/v2/pkg/settlement/swap/erc20"
    60  	"github.com/ethersphere/bee/v2/pkg/settlement/swap/priceoracle"
    61  	"github.com/ethersphere/bee/v2/pkg/status"
    62  	"github.com/ethersphere/bee/v2/pkg/steward"
    63  	"github.com/ethersphere/bee/v2/pkg/storageincentives"
    64  	"github.com/ethersphere/bee/v2/pkg/storageincentives/redistribution"
    65  	"github.com/ethersphere/bee/v2/pkg/storageincentives/staking"
    66  	"github.com/ethersphere/bee/v2/pkg/storer"
    67  	"github.com/ethersphere/bee/v2/pkg/swarm"
    68  	"github.com/ethersphere/bee/v2/pkg/topology"
    69  	"github.com/ethersphere/bee/v2/pkg/topology/kademlia"
    70  	"github.com/ethersphere/bee/v2/pkg/topology/lightnode"
    71  	"github.com/ethersphere/bee/v2/pkg/tracing"
    72  	"github.com/ethersphere/bee/v2/pkg/transaction"
    73  	"github.com/ethersphere/bee/v2/pkg/util/abiutil"
    74  	"github.com/ethersphere/bee/v2/pkg/util/ioutil"
    75  	"github.com/ethersphere/bee/v2/pkg/util/nbhdutil"
    76  	"github.com/ethersphere/bee/v2/pkg/util/syncutil"
    77  	"github.com/hashicorp/go-multierror"
    78  	ma "github.com/multiformats/go-multiaddr"
    79  	promc "github.com/prometheus/client_golang/prometheus"
    80  	"golang.org/x/crypto/sha3"
    81  	"golang.org/x/sync/errgroup"
    82  )
    83  
    84  // LoggerName is the tree path name of the logger for this package.
    85  const LoggerName = "node"
    86  
    87  type Bee struct {
    88  	p2pService               io.Closer
    89  	p2pHalter                p2p.Halter
    90  	ctxCancel                context.CancelFunc
    91  	apiCloser                io.Closer
    92  	apiServer                *http.Server
    93  	resolverCloser           io.Closer
    94  	errorLogWriter           io.Writer
    95  	tracerCloser             io.Closer
    96  	stateStoreCloser         io.Closer
    97  	stamperStoreCloser       io.Closer
    98  	localstoreCloser         io.Closer
    99  	topologyCloser           io.Closer
   100  	topologyHalter           topology.Halter
   101  	pusherCloser             io.Closer
   102  	pullerCloser             io.Closer
   103  	accountingCloser         io.Closer
   104  	pullSyncCloser           io.Closer
   105  	pssCloser                io.Closer
   106  	ethClientCloser          func()
   107  	transactionMonitorCloser io.Closer
   108  	transactionCloser        io.Closer
   109  	listenerCloser           io.Closer
   110  	postageServiceCloser     io.Closer
   111  	priceOracleCloser        io.Closer
   112  	hiveCloser               io.Closer
   113  	saludCloser              io.Closer
   114  	storageIncetivesCloser   io.Closer
   115  	pushSyncCloser           io.Closer
   116  	retrievalCloser          io.Closer
   117  	shutdownInProgress       bool
   118  	shutdownMutex            sync.Mutex
   119  	syncingStopped           *syncutil.Signaler
   120  	accesscontrolCloser      io.Closer
   121  }
   122  
   123  type Options struct {
   124  	DataDir                       string
   125  	CacheCapacity                 uint64
   126  	DBOpenFilesLimit              uint64
   127  	DBWriteBufferSize             uint64
   128  	DBBlockCacheCapacity          uint64
   129  	DBDisableSeeksCompaction      bool
   130  	APIAddr                       string
   131  	Addr                          string
   132  	NATAddr                       string
   133  	EnableWS                      bool
   134  	WelcomeMessage                string
   135  	Bootnodes                     []string
   136  	CORSAllowedOrigins            []string
   137  	Logger                        log.Logger
   138  	TracingEnabled                bool
   139  	TracingEndpoint               string
   140  	TracingServiceName            string
   141  	PaymentThreshold              string
   142  	PaymentTolerance              int64
   143  	PaymentEarly                  int64
   144  	ResolverConnectionCfgs        []multiresolver.ConnectionConfig
   145  	RetrievalCaching              bool
   146  	BootnodeMode                  bool
   147  	BlockchainRpcEndpoint         string
   148  	SwapFactoryAddress            string
   149  	SwapInitialDeposit            string
   150  	SwapEnable                    bool
   151  	ChequebookEnable              bool
   152  	FullNodeMode                  bool
   153  	PostageContractAddress        string
   154  	PostageContractStartBlock     uint64
   155  	StakingContractAddress        string
   156  	PriceOracleAddress            string
   157  	RedistributionContractAddress string
   158  	BlockTime                     time.Duration
   159  	DeployGasPrice                string
   160  	WarmupTime                    time.Duration
   161  	ChainID                       int64
   162  	Resync                        bool
   163  	BlockProfile                  bool
   164  	MutexProfile                  bool
   165  	StaticNodes                   []swarm.Address
   166  	AllowPrivateCIDRs             bool
   167  	UsePostageSnapshot            bool
   168  	EnableStorageIncentives       bool
   169  	StatestoreCacheCapacity       uint64
   170  	TargetNeighborhood            string
   171  	NeighborhoodSuggester         string
   172  	WhitelistedWithdrawalAddress  []string
   173  	TrxDebugMode                  bool
   174  	MinimumStorageRadius          uint
   175  }
   176  
   177  const (
   178  	refreshRate                   = int64(4_500_000)          // accounting units refreshed per second
   179  	lightFactor                   = 10                        // downscale payment thresholds and their change rate, and refresh rates by this for light nodes
   180  	lightRefreshRate              = refreshRate / lightFactor // refresh rate used by / for light nodes
   181  	basePrice                     = 10_000                    // minimal price for retrieval and pushsync requests of maximum proximity
   182  	postageSyncingStallingTimeout = 10 * time.Minute          //
   183  	postageSyncingBackoffTimeout  = 5 * time.Second           //
   184  	minPaymentThreshold           = 2 * refreshRate           // minimal accepted payment threshold of full nodes
   185  	maxPaymentThreshold           = 24 * refreshRate          // maximal accepted payment threshold of full nodes
   186  	mainnetNetworkID              = uint64(1)                 //
   187  	ReserveCapacity               = 4_194_304                 // 2^22 chunks
   188  	reserveWakeUpDuration         = 15 * time.Minute          // time to wait before waking up reserveWorker
   189  	reserveTreshold               = ReserveCapacity * 5 / 10
   190  	reserveMinEvictCount          = 1_000
   191  	cacheMinEvictCount            = 10_000
   192  )
   193  
   194  func NewBee(
   195  	ctx context.Context,
   196  	addr string,
   197  	publicKey *ecdsa.PublicKey,
   198  	signer crypto.Signer,
   199  	networkID uint64,
   200  	logger log.Logger,
   201  	libp2pPrivateKey,
   202  	pssPrivateKey *ecdsa.PrivateKey,
   203  	session accesscontrol.Session,
   204  	o *Options,
   205  ) (b *Bee, err error) {
   206  	tracer, tracerCloser, err := tracing.NewTracer(&tracing.Options{
   207  		Enabled:     o.TracingEnabled,
   208  		Endpoint:    o.TracingEndpoint,
   209  		ServiceName: o.TracingServiceName,
   210  	})
   211  	if err != nil {
   212  		return nil, fmt.Errorf("tracer: %w", err)
   213  	}
   214  
   215  	ctx, ctxCancel := context.WithCancel(ctx)
   216  	defer func() {
   217  		// if there's been an error on this function
   218  		// we'd like to cancel the p2p context so that
   219  		// incoming connections will not be possible
   220  		if err != nil {
   221  			ctxCancel()
   222  		}
   223  	}()
   224  
   225  	// light nodes have zero warmup time for pull/pushsync protocols
   226  	warmupTime := o.WarmupTime
   227  	if !o.FullNodeMode {
   228  		warmupTime = 0
   229  	}
   230  
   231  	sink := ioutil.WriterFunc(func(p []byte) (int, error) {
   232  		logger.Error(nil, string(p))
   233  		return len(p), nil
   234  	})
   235  
   236  	b = &Bee{
   237  		ctxCancel:      ctxCancel,
   238  		errorLogWriter: sink,
   239  		tracerCloser:   tracerCloser,
   240  		syncingStopped: syncutil.NewSignaler(),
   241  	}
   242  
   243  	defer func(b *Bee) {
   244  		if err != nil {
   245  			logger.Error(err, "got error, shutting down...")
   246  			if err2 := b.Shutdown(); err2 != nil {
   247  				logger.Error(err2, "got error while shutting down")
   248  			}
   249  		}
   250  	}(b)
   251  
   252  	stateStore, stateStoreMetrics, err := InitStateStore(logger, o.DataDir, o.StatestoreCacheCapacity)
   253  	if err != nil {
   254  		return nil, err
   255  	}
   256  
   257  	pubKey, err := signer.PublicKey()
   258  	if err != nil {
   259  		return nil, err
   260  	}
   261  
   262  	nonce, nonceExists, err := overlayNonceExists(stateStore)
   263  	if err != nil {
   264  		return nil, fmt.Errorf("check presence of nonce: %w", err)
   265  	}
   266  
   267  	swarmAddress, err := crypto.NewOverlayAddress(*pubKey, networkID, nonce)
   268  	if err != nil {
   269  		return nil, fmt.Errorf("compute overlay address: %w", err)
   270  	}
   271  
   272  	targetNeighborhood := o.TargetNeighborhood
   273  	if targetNeighborhood == "" && !nonceExists && o.NeighborhoodSuggester != "" {
   274  		logger.Info("fetching target neighborhood from suggester", "url", o.NeighborhoodSuggester)
   275  		targetNeighborhood, err = nbhdutil.FetchNeighborhood(&http.Client{}, o.NeighborhoodSuggester)
   276  		if err != nil {
   277  			return nil, fmt.Errorf("neighborhood suggestion: %w", err)
   278  		}
   279  	}
   280  
   281  	var changedOverlay, resetReserve bool
   282  	if targetNeighborhood != "" {
   283  		neighborhood, err := swarm.ParseBitStrAddress(targetNeighborhood)
   284  		if err != nil {
   285  			return nil, fmt.Errorf("invalid neighborhood. %s", targetNeighborhood)
   286  		}
   287  
   288  		if swarm.Proximity(swarmAddress.Bytes(), neighborhood.Bytes()) < uint8(len(targetNeighborhood)) {
   289  			// mine the overlay
   290  			logger.Info("mining a new overlay address to target the selected neighborhood", "target", targetNeighborhood)
   291  			newSwarmAddress, newNonce, err := nbhdutil.MineOverlay(ctx, *pubKey, networkID, targetNeighborhood)
   292  			if err != nil {
   293  				return nil, fmt.Errorf("mine overlay address: %w", err)
   294  			}
   295  
   296  			if nonceExists {
   297  				logger.Info("Override nonce and clean state for neighborhood", "old_none", hex.EncodeToString(nonce), "new_nonce", hex.EncodeToString(newNonce))
   298  				logger.Warning("you have another 10 seconds to change your mind and kill this process with CTRL-C...")
   299  				time.Sleep(10 * time.Second)
   300  
   301  				err := ioutil.RemoveContent(filepath.Join(o.DataDir, ioutil.DataPathKademlia))
   302  				if err != nil {
   303  					return nil, fmt.Errorf("delete %s: %w", ioutil.DataPathKademlia, err)
   304  				}
   305  
   306  				if err := stateStore.ClearForHopping(); err != nil {
   307  					return nil, fmt.Errorf("clearing stateStore %w", err)
   308  				}
   309  				resetReserve = true
   310  			}
   311  
   312  			swarmAddress = newSwarmAddress
   313  			nonce = newNonce
   314  			err = setOverlay(stateStore, swarmAddress, nonce)
   315  			if err != nil {
   316  				return nil, fmt.Errorf("statestore: save new overlay: %w", err)
   317  			}
   318  			changedOverlay = true
   319  		}
   320  	}
   321  
   322  	b.stateStoreCloser = stateStore
   323  	// Check if the batchstore exists. If not, we can assume it's missing
   324  	// due to a migration or it's a fresh install.
   325  	batchStoreExists, err := batchStoreExists(stateStore)
   326  	if err != nil {
   327  		return nil, fmt.Errorf("batchstore: exists: %w", err)
   328  	}
   329  
   330  	addressbook := addressbook.New(stateStore)
   331  
   332  	logger.Info("using overlay address", "address", swarmAddress)
   333  
   334  	// this will set overlay if it was not set before
   335  	if err = checkOverlay(stateStore, swarmAddress); err != nil {
   336  		return nil, fmt.Errorf("check overlay address: %w", err)
   337  	}
   338  
   339  	var (
   340  		chainBackend       transaction.Backend
   341  		overlayEthAddress  common.Address
   342  		chainID            int64
   343  		transactionService transaction.Service
   344  		transactionMonitor transaction.Monitor
   345  		chequebookFactory  chequebook.Factory
   346  		chequebookService  chequebook.Service = new(noOpChequebookService)
   347  		chequeStore        chequebook.ChequeStore
   348  		cashoutService     chequebook.CashoutService
   349  		erc20Service       erc20.Service
   350  	)
   351  
   352  	chainEnabled := isChainEnabled(o, o.BlockchainRpcEndpoint, logger)
   353  
   354  	var batchStore postage.Storer = new(postage.NoOpBatchStore)
   355  	var evictFn func([]byte) error
   356  
   357  	if chainEnabled {
   358  		batchStore, err = batchstore.New(
   359  			stateStore,
   360  			func(id []byte) error {
   361  				return evictFn(id)
   362  			},
   363  			ReserveCapacity,
   364  			logger,
   365  		)
   366  		if err != nil {
   367  			return nil, fmt.Errorf("batchstore: %w", err)
   368  		}
   369  	}
   370  
   371  	chainBackend, overlayEthAddress, chainID, transactionMonitor, transactionService, err = InitChain(
   372  		ctx,
   373  		logger,
   374  		stateStore,
   375  		o.BlockchainRpcEndpoint,
   376  		o.ChainID,
   377  		signer,
   378  		o.BlockTime,
   379  		chainEnabled)
   380  	if err != nil {
   381  		return nil, fmt.Errorf("init chain: %w", err)
   382  	}
   383  	b.ethClientCloser = chainBackend.Close
   384  
   385  	logger.Info("using chain with network network", "chain_id", chainID, "network_id", networkID)
   386  
   387  	if o.ChainID != -1 && o.ChainID != chainID {
   388  		return nil, fmt.Errorf("connected to wrong blockchain network; network chainID %d; configured chainID %d", chainID, o.ChainID)
   389  	}
   390  
   391  	b.transactionCloser = tracerCloser
   392  	b.transactionMonitorCloser = transactionMonitor
   393  
   394  	beeNodeMode := api.LightMode
   395  	if o.FullNodeMode {
   396  		beeNodeMode = api.FullMode
   397  	} else if !chainEnabled {
   398  		beeNodeMode = api.UltraLightMode
   399  	}
   400  
   401  	// Create api.Probe in healthy state and switch to ready state after all components have been constructed
   402  	probe := api.NewProbe()
   403  	probe.SetHealthy(api.ProbeStatusOK)
   404  	defer func(probe *api.Probe) {
   405  		if err != nil {
   406  			probe.SetHealthy(api.ProbeStatusNOK)
   407  		} else {
   408  			probe.SetReady(api.ProbeStatusOK)
   409  		}
   410  	}(probe)
   411  
   412  	stamperStore, err := InitStamperStore(logger, o.DataDir, stateStore)
   413  	if err != nil {
   414  		return nil, fmt.Errorf("failed to initialize stamper store: %w", err)
   415  	}
   416  	b.stamperStoreCloser = stamperStore
   417  
   418  	var apiService *api.Service
   419  
   420  	if o.APIAddr != "" {
   421  		if o.MutexProfile {
   422  			_ = runtime.SetMutexProfileFraction(1)
   423  		}
   424  		if o.BlockProfile {
   425  			runtime.SetBlockProfileRate(1)
   426  		}
   427  
   428  		apiListener, err := net.Listen("tcp", o.APIAddr)
   429  		if err != nil {
   430  			return nil, fmt.Errorf("api listener: %w", err)
   431  		}
   432  
   433  		apiService = api.New(
   434  			*publicKey,
   435  			pssPrivateKey.PublicKey,
   436  			overlayEthAddress,
   437  			o.WhitelistedWithdrawalAddress,
   438  			logger,
   439  			transactionService,
   440  			batchStore,
   441  			beeNodeMode,
   442  			o.ChequebookEnable,
   443  			o.SwapEnable,
   444  			chainBackend,
   445  			o.CORSAllowedOrigins,
   446  			stamperStore,
   447  		)
   448  		apiService.MountTechnicalDebug()
   449  		apiService.SetProbe(probe)
   450  
   451  		apiServer := &http.Server{
   452  			IdleTimeout:       30 * time.Second,
   453  			ReadHeaderTimeout: 3 * time.Second,
   454  			Handler:           apiService,
   455  			ErrorLog:          stdlog.New(b.errorLogWriter, "", 0),
   456  		}
   457  
   458  		go func() {
   459  			logger.Info("starting debug & api server", "address", apiListener.Addr())
   460  
   461  			if err := apiServer.Serve(apiListener); err != nil && !errors.Is(err, http.ErrServerClosed) {
   462  				logger.Debug("debug & api server failed to start", "error", err)
   463  				logger.Error(nil, "debug & api server failed to start")
   464  			}
   465  		}()
   466  
   467  		b.apiServer = apiServer
   468  		b.apiCloser = apiServer
   469  	}
   470  
   471  	// Sync the with the given Ethereum backend:
   472  	isSynced, _, err := transaction.IsSynced(ctx, chainBackend, maxDelay)
   473  	if err != nil {
   474  		return nil, fmt.Errorf("is synced: %w", err)
   475  	}
   476  	if !isSynced {
   477  		logger.Info("waiting to sync with the blockchain backend")
   478  
   479  		err := transaction.WaitSynced(ctx, logger, chainBackend, maxDelay)
   480  		if err != nil {
   481  			return nil, fmt.Errorf("waiting backend sync: %w", err)
   482  		}
   483  	}
   484  
   485  	if o.SwapEnable {
   486  		chequebookFactory, err = InitChequebookFactory(logger, chainBackend, chainID, transactionService, o.SwapFactoryAddress)
   487  		if err != nil {
   488  			return nil, err
   489  		}
   490  
   491  		erc20Address, err := chequebookFactory.ERC20Address(ctx)
   492  		if err != nil {
   493  			return nil, fmt.Errorf("factory fail: %w", err)
   494  		}
   495  
   496  		erc20Service = erc20.New(transactionService, erc20Address)
   497  
   498  		if o.ChequebookEnable && chainEnabled {
   499  			chequebookService, err = InitChequebookService(
   500  				ctx,
   501  				logger,
   502  				stateStore,
   503  				signer,
   504  				chainID,
   505  				chainBackend,
   506  				overlayEthAddress,
   507  				transactionService,
   508  				chequebookFactory,
   509  				o.SwapInitialDeposit,
   510  				o.DeployGasPrice,
   511  				erc20Service,
   512  			)
   513  			if err != nil {
   514  				return nil, err
   515  			}
   516  		}
   517  
   518  		chequeStore, cashoutService = initChequeStoreCashout(
   519  			stateStore,
   520  			chainBackend,
   521  			chequebookFactory,
   522  			chainID,
   523  			overlayEthAddress,
   524  			transactionService,
   525  		)
   526  	}
   527  
   528  	lightNodes := lightnode.NewContainer(swarmAddress)
   529  
   530  	bootnodes := make([]ma.Multiaddr, 0, len(o.Bootnodes))
   531  
   532  	for _, a := range o.Bootnodes {
   533  		addr, err := ma.NewMultiaddr(a)
   534  		if err != nil {
   535  			logger.Debug("create bootnode multiaddress from string failed", "string", a, "error", err)
   536  			logger.Warning("create bootnode multiaddress from string failed", "string", a)
   537  			continue
   538  		}
   539  
   540  		bootnodes = append(bootnodes, addr)
   541  	}
   542  
   543  	// Perform checks related to payment threshold calculations here to not duplicate
   544  	// the checks in bootstrap process
   545  	paymentThreshold, ok := new(big.Int).SetString(o.PaymentThreshold, 10)
   546  	if !ok {
   547  		return nil, fmt.Errorf("invalid payment threshold: %s", paymentThreshold)
   548  	}
   549  
   550  	if paymentThreshold.Cmp(big.NewInt(minPaymentThreshold)) < 0 {
   551  		return nil, fmt.Errorf("payment threshold below minimum generally accepted value, need at least %d", minPaymentThreshold)
   552  	}
   553  
   554  	if paymentThreshold.Cmp(big.NewInt(maxPaymentThreshold)) > 0 {
   555  		return nil, fmt.Errorf("payment threshold above maximum generally accepted value, needs to be reduced to at most %d", maxPaymentThreshold)
   556  	}
   557  
   558  	if o.PaymentTolerance < 0 {
   559  		return nil, fmt.Errorf("invalid payment tolerance: %d", o.PaymentTolerance)
   560  	}
   561  
   562  	if o.PaymentEarly > 100 || o.PaymentEarly < 0 {
   563  		return nil, fmt.Errorf("invalid payment early: %d", o.PaymentEarly)
   564  	}
   565  
   566  	var initBatchState *postage.ChainSnapshot
   567  	// Bootstrap node with postage snapshot only if it is running on mainnet, is a fresh
   568  	// install or explicitly asked by user to resync
   569  	if networkID == mainnetNetworkID && o.UsePostageSnapshot && (!batchStoreExists || o.Resync) {
   570  		start := time.Now()
   571  		logger.Info("cold postage start detected. fetching postage stamp snapshot from swarm")
   572  		initBatchState, err = bootstrapNode(
   573  			ctx,
   574  			addr,
   575  			swarmAddress,
   576  			nonce,
   577  			addressbook,
   578  			bootnodes,
   579  			lightNodes,
   580  			stateStore,
   581  			signer,
   582  			networkID,
   583  			log.Noop,
   584  			libp2pPrivateKey,
   585  			o,
   586  		)
   587  		logger.Info("bootstrapper created", "elapsed", time.Since(start))
   588  		if err != nil {
   589  			logger.Error(err, "bootstrapper failed to fetch batch state")
   590  		}
   591  	}
   592  
   593  	var registry *promc.Registry
   594  
   595  	if apiService != nil {
   596  		registry = apiService.MetricsRegistry()
   597  	}
   598  
   599  	p2ps, err := libp2p.New(ctx, signer, networkID, swarmAddress, addr, addressbook, stateStore, lightNodes, logger, tracer, libp2p.Options{
   600  		PrivateKey:      libp2pPrivateKey,
   601  		NATAddr:         o.NATAddr,
   602  		EnableWS:        o.EnableWS,
   603  		WelcomeMessage:  o.WelcomeMessage,
   604  		FullNode:        o.FullNodeMode,
   605  		Nonce:           nonce,
   606  		ValidateOverlay: chainEnabled,
   607  		Registry:        registry,
   608  	})
   609  	if err != nil {
   610  		return nil, fmt.Errorf("p2p service: %w", err)
   611  	}
   612  
   613  	apiService.SetP2P(p2ps)
   614  
   615  	b.p2pService = p2ps
   616  	b.p2pHalter = p2ps
   617  
   618  	post, err := postage.NewService(logger, stamperStore, batchStore, chainID)
   619  	if err != nil {
   620  		return nil, fmt.Errorf("postage service: %w", err)
   621  	}
   622  	b.postageServiceCloser = post
   623  	batchStore.SetBatchExpiryHandler(post)
   624  
   625  	var (
   626  		postageStampContractService postagecontract.Interface
   627  		batchSvc                    postage.EventUpdater
   628  		eventListener               postage.Listener
   629  	)
   630  
   631  	chainCfg, found := config.GetByChainID(chainID)
   632  	postageStampContractAddress, postageSyncStart := chainCfg.PostageStampAddress, chainCfg.PostageStampStartBlock
   633  	if o.PostageContractAddress != "" {
   634  		if !common.IsHexAddress(o.PostageContractAddress) {
   635  			return nil, errors.New("malformed postage stamp address")
   636  		}
   637  		postageStampContractAddress = common.HexToAddress(o.PostageContractAddress)
   638  		if o.PostageContractStartBlock == 0 {
   639  			return nil, errors.New("postage contract start block option not provided")
   640  		}
   641  		postageSyncStart = o.PostageContractStartBlock
   642  	} else if !found {
   643  		return nil, errors.New("no known postage stamp addresses for this network")
   644  	}
   645  
   646  	postageStampContractABI := abiutil.MustParseABI(chainCfg.PostageStampABI)
   647  
   648  	bzzTokenAddress, err := postagecontract.LookupERC20Address(ctx, transactionService, postageStampContractAddress, postageStampContractABI, chainEnabled)
   649  	if err != nil {
   650  		return nil, err
   651  	}
   652  
   653  	postageStampContractService = postagecontract.New(
   654  		overlayEthAddress,
   655  		postageStampContractAddress,
   656  		postageStampContractABI,
   657  		bzzTokenAddress,
   658  		transactionService,
   659  		post,
   660  		batchStore,
   661  		chainEnabled,
   662  		o.TrxDebugMode,
   663  	)
   664  
   665  	eventListener = listener.New(b.syncingStopped, logger, chainBackend, postageStampContractAddress, postageStampContractABI, o.BlockTime, postageSyncingStallingTimeout, postageSyncingBackoffTimeout)
   666  	b.listenerCloser = eventListener
   667  
   668  	batchSvc, err = batchservice.New(stateStore, batchStore, logger, eventListener, overlayEthAddress.Bytes(), post, sha3.New256, o.Resync)
   669  	if err != nil {
   670  		return nil, err
   671  	}
   672  
   673  	// Construct protocols.
   674  	pingPong := pingpong.New(p2ps, logger, tracer)
   675  
   676  	if err = p2ps.AddProtocol(pingPong.Protocol()); err != nil {
   677  		return nil, fmt.Errorf("pingpong service: %w", err)
   678  	}
   679  
   680  	hive := hive.New(p2ps, addressbook, networkID, o.BootnodeMode, o.AllowPrivateCIDRs, logger)
   681  
   682  	if err = p2ps.AddProtocol(hive.Protocol()); err != nil {
   683  		return nil, fmt.Errorf("hive service: %w", err)
   684  	}
   685  	b.hiveCloser = hive
   686  
   687  	var swapService *swap.Service
   688  
   689  	kad, err := kademlia.New(swarmAddress, addressbook, hive, p2ps, logger,
   690  		kademlia.Options{Bootnodes: bootnodes, BootnodeMode: o.BootnodeMode, StaticNodes: o.StaticNodes, DataDir: o.DataDir})
   691  	if err != nil {
   692  		return nil, fmt.Errorf("unable to create kademlia: %w", err)
   693  	}
   694  	b.topologyCloser = kad
   695  	b.topologyHalter = kad
   696  	hive.SetAddPeersHandler(kad.AddPeers)
   697  	p2ps.SetPickyNotifier(kad)
   698  
   699  	var path string
   700  
   701  	if o.DataDir != "" {
   702  		logger.Info("using datadir", "path", o.DataDir)
   703  		path = filepath.Join(o.DataDir, ioutil.DataPathLocalstore)
   704  	}
   705  
   706  	lo := &storer.Options{
   707  		Address:                   swarmAddress,
   708  		CacheCapacity:             o.CacheCapacity,
   709  		LdbOpenFilesLimit:         o.DBOpenFilesLimit,
   710  		LdbBlockCacheCapacity:     o.DBBlockCacheCapacity,
   711  		LdbWriteBufferSize:        o.DBWriteBufferSize,
   712  		LdbDisableSeeksCompaction: o.DBDisableSeeksCompaction,
   713  		Batchstore:                batchStore,
   714  		StateStore:                stateStore,
   715  		RadiusSetter:              kad,
   716  		WarmupDuration:            o.WarmupTime,
   717  		Logger:                    logger,
   718  		Tracer:                    tracer,
   719  		CacheMinEvictCount:        cacheMinEvictCount,
   720  		MinimumStorageRadius:      o.MinimumStorageRadius,
   721  	}
   722  
   723  	if o.FullNodeMode && !o.BootnodeMode {
   724  		// configure reserve only for full node
   725  		lo.ReserveCapacity = ReserveCapacity
   726  		lo.ReserveWakeUpDuration = reserveWakeUpDuration
   727  		lo.ReserveMinEvictCount = reserveMinEvictCount
   728  		lo.RadiusSetter = kad
   729  	}
   730  
   731  	localStore, err := storer.New(ctx, path, lo)
   732  	if err != nil {
   733  		return nil, fmt.Errorf("localstore: %w", err)
   734  	}
   735  	b.localstoreCloser = localStore
   736  	evictFn = func(id []byte) error { return localStore.EvictBatch(context.Background(), id) }
   737  
   738  	if resetReserve {
   739  		logger.Warning("resetting the reserve")
   740  		err := localStore.ResetReserve(ctx)
   741  		if err != nil {
   742  			return nil, fmt.Errorf("reset reserve: %w", err)
   743  		}
   744  	}
   745  
   746  	actLogic := accesscontrol.NewLogic(session)
   747  	accesscontrol := accesscontrol.NewController(actLogic)
   748  	b.accesscontrolCloser = accesscontrol
   749  
   750  	var (
   751  		syncErr    atomic.Value
   752  		syncStatus atomic.Value
   753  
   754  		syncStatusFn = func() (isDone bool, err error) {
   755  			iErr := syncErr.Load()
   756  			if iErr != nil {
   757  				err = iErr.(error)
   758  			}
   759  			isDone = syncStatus.Load() != nil
   760  			return isDone, err
   761  		}
   762  	)
   763  
   764  	if batchSvc != nil && chainEnabled {
   765  		logger.Info("waiting to sync postage contract data, this may take a while... more info available in Debug loglevel")
   766  
   767  		paused, err := postageStampContractService.Paused(ctx)
   768  		if err != nil {
   769  			logger.Error(err, "Error checking postage contract is paused")
   770  		}
   771  
   772  		if paused {
   773  			return nil, errors.New("postage contract is paused")
   774  		}
   775  
   776  		if o.FullNodeMode {
   777  			err = batchSvc.Start(ctx, postageSyncStart, initBatchState)
   778  			syncStatus.Store(true)
   779  			if err != nil {
   780  				syncErr.Store(err)
   781  				return nil, fmt.Errorf("unable to start batch service: %w", err)
   782  			}
   783  		} else {
   784  			go func() {
   785  				logger.Info("started postage contract data sync in the background...")
   786  				err := batchSvc.Start(ctx, postageSyncStart, initBatchState)
   787  				syncStatus.Store(true)
   788  				if err != nil {
   789  					syncErr.Store(err)
   790  					logger.Error(err, "unable to sync batches")
   791  					b.syncingStopped.Signal() // trigger shutdown in start.go
   792  				}
   793  			}()
   794  		}
   795  
   796  	}
   797  
   798  	minThreshold := big.NewInt(2 * refreshRate)
   799  	maxThreshold := big.NewInt(24 * refreshRate)
   800  
   801  	if !o.FullNodeMode {
   802  		minThreshold = big.NewInt(2 * lightRefreshRate)
   803  	}
   804  
   805  	lightPaymentThreshold := new(big.Int).Div(paymentThreshold, big.NewInt(lightFactor))
   806  
   807  	pricer := pricer.NewFixedPricer(swarmAddress, basePrice)
   808  
   809  	if paymentThreshold.Cmp(minThreshold) < 0 {
   810  		return nil, fmt.Errorf("payment threshold below minimum generally accepted value, need at least %s", minThreshold)
   811  	}
   812  
   813  	if paymentThreshold.Cmp(maxThreshold) > 0 {
   814  		return nil, fmt.Errorf("payment threshold above maximum generally accepted value, needs to be reduced to at most %s", maxThreshold)
   815  	}
   816  
   817  	pricing := pricing.New(p2ps, logger, paymentThreshold, lightPaymentThreshold, minThreshold)
   818  
   819  	if err = p2ps.AddProtocol(pricing.Protocol()); err != nil {
   820  		return nil, fmt.Errorf("pricing service: %w", err)
   821  	}
   822  
   823  	addrs, err := p2ps.Addresses()
   824  	if err != nil {
   825  		return nil, fmt.Errorf("get server addresses: %w", err)
   826  	}
   827  
   828  	for _, addr := range addrs {
   829  		logger.Debug("p2p address", "address", addr)
   830  	}
   831  
   832  	var enforcedRefreshRate *big.Int
   833  
   834  	if o.FullNodeMode {
   835  		enforcedRefreshRate = big.NewInt(refreshRate)
   836  	} else {
   837  		enforcedRefreshRate = big.NewInt(lightRefreshRate)
   838  	}
   839  
   840  	acc, err := accounting.NewAccounting(
   841  		paymentThreshold,
   842  		o.PaymentTolerance,
   843  		o.PaymentEarly,
   844  		logger,
   845  		stateStore,
   846  		pricing,
   847  		new(big.Int).Set(enforcedRefreshRate),
   848  		lightFactor,
   849  		p2ps,
   850  	)
   851  	if err != nil {
   852  		return nil, fmt.Errorf("accounting: %w", err)
   853  	}
   854  	b.accountingCloser = acc
   855  
   856  	pseudosettleService := pseudosettle.New(p2ps, logger, stateStore, acc, new(big.Int).Set(enforcedRefreshRate), big.NewInt(lightRefreshRate), p2ps)
   857  	if err = p2ps.AddProtocol(pseudosettleService.Protocol()); err != nil {
   858  		return nil, fmt.Errorf("pseudosettle service: %w", err)
   859  	}
   860  
   861  	acc.SetRefreshFunc(pseudosettleService.Pay)
   862  
   863  	if o.SwapEnable && chainEnabled {
   864  		var priceOracle priceoracle.Service
   865  		swapService, priceOracle, err = InitSwap(
   866  			p2ps,
   867  			logger,
   868  			stateStore,
   869  			networkID,
   870  			overlayEthAddress,
   871  			chequebookService,
   872  			chequeStore,
   873  			cashoutService,
   874  			acc,
   875  			o.PriceOracleAddress,
   876  			chainID,
   877  			transactionService,
   878  		)
   879  		if err != nil {
   880  			return nil, err
   881  		}
   882  		b.priceOracleCloser = priceOracle
   883  
   884  		if o.ChequebookEnable {
   885  			acc.SetPayFunc(swapService.Pay)
   886  		}
   887  	}
   888  
   889  	pricing.SetPaymentThresholdObserver(acc)
   890  
   891  	pssService := pss.New(pssPrivateKey, logger)
   892  	b.pssCloser = pssService
   893  
   894  	validStamp := postage.ValidStamp(batchStore)
   895  
   896  	nodeStatus := status.NewService(logger, p2ps, kad, beeNodeMode.String(), batchStore, localStore)
   897  	if err = p2ps.AddProtocol(nodeStatus.Protocol()); err != nil {
   898  		return nil, fmt.Errorf("status service: %w", err)
   899  	}
   900  
   901  	saludService := salud.New(nodeStatus, kad, localStore, logger, warmupTime, api.FullMode.String(), salud.DefaultMinPeersPerBin, salud.DefaultDurPercentile, salud.DefaultConnsPercentile)
   902  	b.saludCloser = saludService
   903  
   904  	rC, unsub := saludService.SubscribeNetworkStorageRadius()
   905  	initialRadiusC := make(chan struct{})
   906  	var networkR atomic.Uint32
   907  	networkR.Store(uint32(swarm.MaxBins))
   908  
   909  	go func() {
   910  		for {
   911  			select {
   912  			case r := <-rC:
   913  				prev := networkR.Load()
   914  				networkR.Store(uint32(r))
   915  				if prev == uint32(swarm.MaxBins) {
   916  					close(initialRadiusC)
   917  				}
   918  				if !o.FullNodeMode { // light and ultra-light nodes do not have a reserve worker to set the radius.
   919  					kad.SetStorageRadius(r)
   920  				}
   921  			case <-ctx.Done():
   922  				unsub()
   923  				return
   924  			}
   925  		}
   926  	}()
   927  
   928  	waitNetworkRFunc := func() (uint8, error) {
   929  		if networkR.Load() == uint32(swarm.MaxBins) {
   930  			select {
   931  			case <-initialRadiusC:
   932  			case <-ctx.Done():
   933  				return 0, ctx.Err()
   934  			}
   935  		}
   936  
   937  		local, network := localStore.StorageRadius(), uint8(networkR.Load())
   938  		if local <= uint8(o.MinimumStorageRadius) {
   939  			return max(network, uint8(o.MinimumStorageRadius)), nil
   940  		} else {
   941  			return local, nil
   942  		}
   943  	}
   944  
   945  	pushSyncProtocol := pushsync.New(swarmAddress, networkID, nonce, p2ps, localStore, waitNetworkRFunc, kad, o.FullNodeMode, pssService.TryUnwrap, validStamp, logger, acc, pricer, signer, tracer, warmupTime)
   946  	b.pushSyncCloser = pushSyncProtocol
   947  
   948  	// set the pushSyncer in the PSS
   949  	pssService.SetPushSyncer(pushSyncProtocol)
   950  
   951  	retrieval := retrieval.New(swarmAddress, waitNetworkRFunc, localStore, p2ps, kad, logger, acc, pricer, tracer, o.RetrievalCaching)
   952  	localStore.SetRetrievalService(retrieval)
   953  
   954  	pusherService := pusher.New(networkID, localStore, pushSyncProtocol, validStamp, logger, warmupTime, pusher.DefaultRetryCount)
   955  	b.pusherCloser = pusherService
   956  
   957  	pusherService.AddFeed(localStore.PusherFeed())
   958  
   959  	pullSyncProtocol := pullsync.New(p2ps, localStore, pssService.TryUnwrap, validStamp, logger, pullsync.DefaultMaxPage)
   960  	b.pullSyncCloser = pullSyncProtocol
   961  
   962  	retrieveProtocolSpec := retrieval.Protocol()
   963  	pushSyncProtocolSpec := pushSyncProtocol.Protocol()
   964  	pullSyncProtocolSpec := pullSyncProtocol.Protocol()
   965  
   966  	if o.FullNodeMode && !o.BootnodeMode {
   967  		logger.Info("starting in full mode")
   968  	} else {
   969  		if chainEnabled {
   970  			logger.Info("starting in light mode")
   971  		} else {
   972  			logger.Info("starting in ultra-light mode")
   973  		}
   974  		p2p.WithBlocklistStreams(p2p.DefaultBlocklistTime, retrieveProtocolSpec)
   975  		p2p.WithBlocklistStreams(p2p.DefaultBlocklistTime, pushSyncProtocolSpec)
   976  		p2p.WithBlocklistStreams(p2p.DefaultBlocklistTime, pullSyncProtocolSpec)
   977  	}
   978  
   979  	if err = p2ps.AddProtocol(retrieveProtocolSpec); err != nil {
   980  		return nil, fmt.Errorf("retrieval service: %w", err)
   981  	}
   982  	if err = p2ps.AddProtocol(pushSyncProtocolSpec); err != nil {
   983  		return nil, fmt.Errorf("pushsync service: %w", err)
   984  	}
   985  	if err = p2ps.AddProtocol(pullSyncProtocolSpec); err != nil {
   986  		return nil, fmt.Errorf("pullsync protocol: %w", err)
   987  	}
   988  
   989  	stakingContractAddress := chainCfg.StakingAddress
   990  	if o.StakingContractAddress != "" {
   991  		if !common.IsHexAddress(o.StakingContractAddress) {
   992  			return nil, errors.New("malformed staking contract address")
   993  		}
   994  		stakingContractAddress = common.HexToAddress(o.StakingContractAddress)
   995  	}
   996  
   997  	stakingContract := staking.New(overlayEthAddress, stakingContractAddress, abiutil.MustParseABI(chainCfg.StakingABI), bzzTokenAddress, transactionService, common.BytesToHash(nonce), o.TrxDebugMode)
   998  
   999  	if chainEnabled && changedOverlay {
  1000  		stake, err := stakingContract.GetPotentialStake(ctx)
  1001  		if err != nil {
  1002  			return nil, errors.New("getting stake balance")
  1003  		}
  1004  		if stake.Cmp(big.NewInt(0)) > 0 {
  1005  			logger.Debug("changing overlay address in staking contract")
  1006  			tx, err := stakingContract.ChangeStakeOverlay(ctx, common.BytesToHash(nonce))
  1007  			if err != nil {
  1008  				return nil, fmt.Errorf("cannot change staking overlay address: %v", err.Error())
  1009  			}
  1010  			logger.Info("overlay address changed in staking contract", "transaction", tx)
  1011  		}
  1012  	}
  1013  
  1014  	var (
  1015  		pullerService *puller.Puller
  1016  		agent         *storageincentives.Agent
  1017  	)
  1018  
  1019  	if o.FullNodeMode && !o.BootnodeMode {
  1020  		pullerService = puller.New(swarmAddress, stateStore, kad, localStore, pullSyncProtocol, p2ps, logger, puller.Options{})
  1021  		b.pullerCloser = pullerService
  1022  
  1023  		localStore.StartReserveWorker(ctx, pullerService, waitNetworkRFunc)
  1024  		nodeStatus.SetSync(pullerService)
  1025  
  1026  		if o.EnableStorageIncentives {
  1027  
  1028  			redistributionContractAddress := chainCfg.RedistributionAddress
  1029  			if o.RedistributionContractAddress != "" {
  1030  				if !common.IsHexAddress(o.RedistributionContractAddress) {
  1031  					return nil, errors.New("malformed redistribution contract address")
  1032  				}
  1033  				redistributionContractAddress = common.HexToAddress(o.RedistributionContractAddress)
  1034  			}
  1035  
  1036  			isFullySynced := func() bool {
  1037  				return localStore.ReserveSize() >= reserveTreshold && pullerService.SyncRate() == 0
  1038  			}
  1039  
  1040  			redistributionContract := redistribution.New(swarmAddress, overlayEthAddress, logger, transactionService, redistributionContractAddress, abiutil.MustParseABI(chainCfg.RedistributionABI), o.TrxDebugMode)
  1041  			agent, err = storageincentives.New(
  1042  				swarmAddress,
  1043  				overlayEthAddress,
  1044  				chainBackend,
  1045  				redistributionContract,
  1046  				postageStampContractService,
  1047  				stakingContract,
  1048  				localStore,
  1049  				isFullySynced,
  1050  				o.BlockTime,
  1051  				storageincentives.DefaultBlocksPerRound,
  1052  				storageincentives.DefaultBlocksPerPhase,
  1053  				stateStore,
  1054  				batchStore,
  1055  				erc20Service,
  1056  				transactionService,
  1057  				saludService,
  1058  				logger,
  1059  			)
  1060  			if err != nil {
  1061  				return nil, fmt.Errorf("storage incentives agent: %w", err)
  1062  			}
  1063  			b.storageIncetivesCloser = agent
  1064  		}
  1065  
  1066  	}
  1067  	multiResolver := multiresolver.NewMultiResolver(
  1068  		multiresolver.WithConnectionConfigs(o.ResolverConnectionCfgs),
  1069  		multiresolver.WithLogger(o.Logger),
  1070  		multiresolver.WithDefaultCIDResolver(),
  1071  	)
  1072  	b.resolverCloser = multiResolver
  1073  
  1074  	feedFactory := factory.New(localStore.Download(true))
  1075  	steward := steward.New(localStore, retrieval, localStore.Cache())
  1076  
  1077  	extraOpts := api.ExtraOptions{
  1078  		Pingpong:        pingPong,
  1079  		TopologyDriver:  kad,
  1080  		LightNodes:      lightNodes,
  1081  		Accounting:      acc,
  1082  		Pseudosettle:    pseudosettleService,
  1083  		Swap:            swapService,
  1084  		Chequebook:      chequebookService,
  1085  		BlockTime:       o.BlockTime,
  1086  		Storer:          localStore,
  1087  		Resolver:        multiResolver,
  1088  		Pss:             pssService,
  1089  		FeedFactory:     feedFactory,
  1090  		Post:            post,
  1091  		AccessControl:   accesscontrol,
  1092  		PostageContract: postageStampContractService,
  1093  		Staking:         stakingContract,
  1094  		Steward:         steward,
  1095  		SyncStatus:      syncStatusFn,
  1096  		NodeStatus:      nodeStatus,
  1097  		PinIntegrity:    localStore.PinIntegrity(),
  1098  	}
  1099  
  1100  	if o.APIAddr != "" {
  1101  		// register metrics from components
  1102  		apiService.MustRegisterMetrics(p2ps.Metrics()...)
  1103  		apiService.MustRegisterMetrics(pingPong.Metrics()...)
  1104  		apiService.MustRegisterMetrics(acc.Metrics()...)
  1105  		apiService.MustRegisterMetrics(localStore.Metrics()...)
  1106  		apiService.MustRegisterMetrics(kad.Metrics()...)
  1107  		apiService.MustRegisterMetrics(saludService.Metrics()...)
  1108  		apiService.MustRegisterMetrics(stateStoreMetrics.Metrics()...)
  1109  
  1110  		if pullerService != nil {
  1111  			apiService.MustRegisterMetrics(pullerService.Metrics()...)
  1112  		}
  1113  
  1114  		if agent != nil {
  1115  			apiService.MustRegisterMetrics(agent.Metrics()...)
  1116  		}
  1117  
  1118  		apiService.MustRegisterMetrics(pushSyncProtocol.Metrics()...)
  1119  		apiService.MustRegisterMetrics(pusherService.Metrics()...)
  1120  		apiService.MustRegisterMetrics(pullSyncProtocol.Metrics()...)
  1121  		apiService.MustRegisterMetrics(retrieval.Metrics()...)
  1122  		apiService.MustRegisterMetrics(lightNodes.Metrics()...)
  1123  		apiService.MustRegisterMetrics(hive.Metrics()...)
  1124  
  1125  		if bs, ok := batchStore.(metrics.Collector); ok {
  1126  			apiService.MustRegisterMetrics(bs.Metrics()...)
  1127  		}
  1128  		if ls, ok := eventListener.(metrics.Collector); ok {
  1129  			apiService.MustRegisterMetrics(ls.Metrics()...)
  1130  		}
  1131  		if pssServiceMetrics, ok := pssService.(metrics.Collector); ok {
  1132  			apiService.MustRegisterMetrics(pssServiceMetrics.Metrics()...)
  1133  		}
  1134  		if swapBackendMetrics, ok := chainBackend.(metrics.Collector); ok {
  1135  			apiService.MustRegisterMetrics(swapBackendMetrics.Metrics()...)
  1136  		}
  1137  
  1138  		if l, ok := logger.(metrics.Collector); ok {
  1139  			apiService.MustRegisterMetrics(l.Metrics()...)
  1140  		}
  1141  		apiService.MustRegisterMetrics(pseudosettleService.Metrics()...)
  1142  		if swapService != nil {
  1143  			apiService.MustRegisterMetrics(swapService.Metrics()...)
  1144  		}
  1145  
  1146  		apiService.Configure(signer, tracer, api.Options{
  1147  			CORSAllowedOrigins: o.CORSAllowedOrigins,
  1148  			WsPingPeriod:       60 * time.Second,
  1149  		}, extraOpts, chainID, erc20Service)
  1150  
  1151  		apiService.MountDebug()
  1152  		apiService.MountAPI()
  1153  
  1154  		apiService.SetSwarmAddress(&swarmAddress)
  1155  		apiService.SetRedistributionAgent(agent)
  1156  	}
  1157  
  1158  	if err := kad.Start(ctx); err != nil {
  1159  		return nil, err
  1160  	}
  1161  
  1162  	if err := p2ps.Ready(); err != nil {
  1163  		return nil, err
  1164  	}
  1165  
  1166  	return b, nil
  1167  }
  1168  
  1169  func (b *Bee) SyncingStopped() chan struct{} {
  1170  	return b.syncingStopped.C
  1171  }
  1172  
  1173  func (b *Bee) Shutdown() error {
  1174  	var mErr error
  1175  
  1176  	// if a shutdown is already in process, return here
  1177  	b.shutdownMutex.Lock()
  1178  	if b.shutdownInProgress {
  1179  		b.shutdownMutex.Unlock()
  1180  		return ErrShutdownInProgress
  1181  	}
  1182  	b.shutdownInProgress = true
  1183  	b.shutdownMutex.Unlock()
  1184  
  1185  	// halt kademlia while shutting down other
  1186  	// components.
  1187  	if b.topologyHalter != nil {
  1188  		b.topologyHalter.Halt()
  1189  	}
  1190  
  1191  	// halt p2p layer from accepting new connections
  1192  	// while shutting down other components
  1193  	if b.p2pHalter != nil {
  1194  		b.p2pHalter.Halt()
  1195  	}
  1196  	// tryClose is a convenient closure which decrease
  1197  	// repetitive io.Closer tryClose procedure.
  1198  	tryClose := func(c io.Closer, errMsg string) {
  1199  		if c == nil {
  1200  			return
  1201  		}
  1202  		if err := c.Close(); err != nil {
  1203  			mErr = multierror.Append(mErr, fmt.Errorf("%s: %w", errMsg, err))
  1204  		}
  1205  	}
  1206  
  1207  	tryClose(b.apiCloser, "api")
  1208  
  1209  	ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
  1210  	defer cancel()
  1211  
  1212  	var eg errgroup.Group
  1213  	if b.apiServer != nil {
  1214  		eg.Go(func() error {
  1215  			if err := b.apiServer.Shutdown(ctx); err != nil {
  1216  				return fmt.Errorf("api server: %w", err)
  1217  			}
  1218  			return nil
  1219  		})
  1220  	}
  1221  	if err := eg.Wait(); err != nil {
  1222  		mErr = multierror.Append(mErr, err)
  1223  	}
  1224  
  1225  	var wg sync.WaitGroup
  1226  	wg.Add(7)
  1227  	go func() {
  1228  		defer wg.Done()
  1229  		tryClose(b.pssCloser, "pss")
  1230  	}()
  1231  	go func() {
  1232  		defer wg.Done()
  1233  		tryClose(b.pusherCloser, "pusher")
  1234  	}()
  1235  	go func() {
  1236  		defer wg.Done()
  1237  		tryClose(b.pullerCloser, "puller")
  1238  	}()
  1239  	go func() {
  1240  		defer wg.Done()
  1241  		tryClose(b.accountingCloser, "accounting")
  1242  	}()
  1243  
  1244  	b.ctxCancel()
  1245  	go func() {
  1246  		defer wg.Done()
  1247  		tryClose(b.pullSyncCloser, "pull sync")
  1248  	}()
  1249  	go func() {
  1250  		defer wg.Done()
  1251  		tryClose(b.hiveCloser, "hive")
  1252  	}()
  1253  	go func() {
  1254  		defer wg.Done()
  1255  		tryClose(b.saludCloser, "salud")
  1256  	}()
  1257  
  1258  	wg.Wait()
  1259  
  1260  	tryClose(b.p2pService, "p2p server")
  1261  	tryClose(b.priceOracleCloser, "price oracle service")
  1262  
  1263  	wg.Add(3)
  1264  	go func() {
  1265  		defer wg.Done()
  1266  		tryClose(b.transactionMonitorCloser, "transaction monitor")
  1267  		tryClose(b.transactionCloser, "transaction")
  1268  	}()
  1269  	go func() {
  1270  		defer wg.Done()
  1271  		tryClose(b.listenerCloser, "listener")
  1272  	}()
  1273  	go func() {
  1274  		defer wg.Done()
  1275  		tryClose(b.postageServiceCloser, "postage service")
  1276  	}()
  1277  
  1278  	wg.Wait()
  1279  
  1280  	if c := b.ethClientCloser; c != nil {
  1281  		c()
  1282  	}
  1283  
  1284  	tryClose(b.accesscontrolCloser, "accesscontrol")
  1285  	tryClose(b.tracerCloser, "tracer")
  1286  	tryClose(b.topologyCloser, "topology driver")
  1287  	tryClose(b.storageIncetivesCloser, "storage incentives agent")
  1288  	tryClose(b.stateStoreCloser, "statestore")
  1289  	tryClose(b.stamperStoreCloser, "stamperstore")
  1290  	tryClose(b.localstoreCloser, "localstore")
  1291  	tryClose(b.resolverCloser, "resolver service")
  1292  
  1293  	return mErr
  1294  }
  1295  
  1296  var ErrShutdownInProgress error = errors.New("shutdown in progress")
  1297  
  1298  func isChainEnabled(o *Options, swapEndpoint string, logger log.Logger) bool {
  1299  	chainDisabled := swapEndpoint == ""
  1300  	lightMode := !o.FullNodeMode
  1301  
  1302  	if lightMode && chainDisabled { // ultra light mode is LightNode mode with chain disabled
  1303  		logger.Info("starting with a disabled chain backend")
  1304  		return false
  1305  	}
  1306  
  1307  	logger.Info("starting with an enabled chain backend")
  1308  	return true // all other modes operate require chain enabled
  1309  }