github.com/SmartMeshFoundation/Spectrum@v0.0.0-20220621030607-452a266fee1e/swarm/swarm.go (about)

     1  // Copyright 2016 The Spectrum Authors
     2  // This file is part of the Spectrum library.
     3  //
     4  // The Spectrum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The Spectrum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the Spectrum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package swarm
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	"crypto/ecdsa"
    23  	"fmt"
    24  	"net"
    25  
    26  	"github.com/SmartMeshFoundation/Spectrum/accounts/abi/bind"
    27  	"github.com/SmartMeshFoundation/Spectrum/common"
    28  	"github.com/SmartMeshFoundation/Spectrum/contracts/chequebook"
    29  	"github.com/SmartMeshFoundation/Spectrum/contracts/ens"
    30  	"github.com/SmartMeshFoundation/Spectrum/crypto"
    31  	"github.com/SmartMeshFoundation/Spectrum/ethclient"
    32  	"github.com/SmartMeshFoundation/Spectrum/log"
    33  	"github.com/SmartMeshFoundation/Spectrum/node"
    34  	"github.com/SmartMeshFoundation/Spectrum/p2p"
    35  	"github.com/SmartMeshFoundation/Spectrum/p2p/discover"
    36  	"github.com/SmartMeshFoundation/Spectrum/rpc"
    37  	"github.com/SmartMeshFoundation/Spectrum/swarm/api"
    38  	httpapi "github.com/SmartMeshFoundation/Spectrum/swarm/api/http"
    39  	"github.com/SmartMeshFoundation/Spectrum/swarm/fuse"
    40  	"github.com/SmartMeshFoundation/Spectrum/swarm/network"
    41  	"github.com/SmartMeshFoundation/Spectrum/swarm/storage"
    42  )
    43  
    44  // the swarm stack
    45  type Swarm struct {
    46  	config      *api.Config            // swarm configuration
    47  	api         *api.Api               // high level api layer (fs/manifest)
    48  	dns         api.Resolver           // DNS registrar
    49  	dbAccess    *network.DbAccess      // access to local chunk db iterator and storage counter
    50  	storage     storage.ChunkStore     // internal access to storage, common interface to cloud storage backends
    51  	dpa         *storage.DPA           // distributed preimage archive, the local API to the storage with document level storage/retrieval support
    52  	depo        network.StorageHandler // remote request handler, interface between bzz protocol and the storage
    53  	cloud       storage.CloudStore     // procurement, cloud storage backend (can multi-cloud)
    54  	hive        *network.Hive          // the logistic manager
    55  	backend     chequebook.Backend     // simple blockchain Backend
    56  	privateKey  *ecdsa.PrivateKey
    57  	corsString  string
    58  	swapEnabled bool
    59  	lstore      *storage.LocalStore // local store, needs to store for releasing resources after node stopped
    60  	sfs         *fuse.SwarmFS       // need this to cleanup all the active mounts on node exit
    61  }
    62  
    63  type SwarmAPI struct {
    64  	Api     *api.Api
    65  	Backend chequebook.Backend
    66  	PrvKey  *ecdsa.PrivateKey
    67  }
    68  
    69  func (self *Swarm) API() *SwarmAPI {
    70  	return &SwarmAPI{
    71  		Api:     self.api,
    72  		Backend: self.backend,
    73  		PrvKey:  self.privateKey,
    74  	}
    75  }
    76  
    77  // creates a new swarm service instance
    78  // implements node.Service
    79  func NewSwarm(ctx *node.ServiceContext, backend chequebook.Backend, ensClient *ethclient.Client, config *api.Config, swapEnabled, syncEnabled bool, cors string) (self *Swarm, err error) {
    80  	if bytes.Equal(common.FromHex(config.PublicKey), storage.ZeroKey) {
    81  		return nil, fmt.Errorf("empty public key")
    82  	}
    83  	if bytes.Equal(common.FromHex(config.BzzKey), storage.ZeroKey) {
    84  		return nil, fmt.Errorf("empty bzz key")
    85  	}
    86  
    87  	self = &Swarm{
    88  		config:      config,
    89  		swapEnabled: swapEnabled,
    90  		backend:     backend,
    91  		privateKey:  config.Swap.PrivateKey(),
    92  		corsString:  cors,
    93  	}
    94  	log.Debug(fmt.Sprintf("Setting up Swarm service components"))
    95  
    96  	hash := storage.MakeHashFunc(config.ChunkerParams.Hash)
    97  	self.lstore, err = storage.NewLocalStore(hash, config.StoreParams)
    98  	if err != nil {
    99  		return
   100  	}
   101  
   102  	// setup local store
   103  	log.Debug(fmt.Sprintf("Set up local storage"))
   104  
   105  	self.dbAccess = network.NewDbAccess(self.lstore)
   106  	log.Debug(fmt.Sprintf("Set up local db access (iterator/counter)"))
   107  
   108  	// set up the kademlia hive
   109  	self.hive = network.NewHive(
   110  		common.HexToHash(self.config.BzzKey), // key to hive (kademlia base address)
   111  		config.HiveParams,                    // configuration parameters
   112  		swapEnabled,                          // SWAP enabled
   113  		syncEnabled,                          // syncronisation enabled
   114  	)
   115  	log.Debug(fmt.Sprintf("Set up swarm network with Kademlia hive"))
   116  
   117  	// setup cloud storage backend
   118  	self.cloud = network.NewForwarder(self.hive)
   119  	log.Debug(fmt.Sprintf("-> set swarm forwarder as cloud storage backend"))
   120  
   121  	// setup cloud storage internal access layer
   122  	self.storage = storage.NewNetStore(hash, self.lstore, self.cloud, config.StoreParams)
   123  	log.Debug(fmt.Sprintf("-> swarm net store shared access layer to Swarm Chunk Store"))
   124  
   125  	// set up Depo (storage handler = cloud storage access layer for incoming remote requests)
   126  	self.depo = network.NewDepo(hash, self.lstore, self.storage)
   127  	log.Debug(fmt.Sprintf("-> REmote Access to CHunks"))
   128  
   129  	// set up DPA, the cloud storage local access layer
   130  	dpaChunkStore := storage.NewDpaChunkStore(self.lstore, self.storage)
   131  	log.Debug(fmt.Sprintf("-> Local Access to Swarm"))
   132  	// Swarm Hash Merklised Chunking for Arbitrary-length Document/File storage
   133  	self.dpa = storage.NewDPA(dpaChunkStore, self.config.ChunkerParams)
   134  	log.Debug(fmt.Sprintf("-> Content Store API"))
   135  
   136  	// set up high level api
   137  	transactOpts := bind.NewKeyedTransactor(self.privateKey)
   138  
   139  	if ensClient == nil {
   140  		log.Warn("No ENS, please specify non-empty --ens-api to use domain name resolution")
   141  	} else {
   142  		self.dns, err = ens.NewENS(transactOpts, config.EnsRoot, ensClient)
   143  		if err != nil {
   144  			return nil, err
   145  		}
   146  	}
   147  	log.Debug(fmt.Sprintf("-> Swarm Domain Name Registrar @ address %v", config.EnsRoot.Hex()))
   148  
   149  	self.api = api.NewApi(self.dpa, self.dns)
   150  	// Manifests for Smart Hosting
   151  	log.Debug(fmt.Sprintf("-> Web3 virtual server API"))
   152  
   153  	self.sfs = fuse.NewSwarmFS(self.api)
   154  	log.Debug("-> Initializing Fuse file system")
   155  
   156  	return self, nil
   157  }
   158  
   159  /*
   160  Start is called when the stack is started
   161  * starts the network kademlia hive peer management
   162  * (starts netStore level 0 api)
   163  * starts DPA level 1 api (chunking -> store/retrieve requests)
   164  * (starts level 2 api)
   165  * starts http proxy server
   166  * registers url scheme handlers for bzz, etc
   167  * TODO: start subservices like sword, swear, swarmdns
   168  */
   169  // implements the node.Service interface
   170  func (self *Swarm) Start(srv *p2p.Server) error {
   171  	connectPeer := func(url string) error {
   172  		node, err := discover.ParseNode(url)
   173  		if err != nil {
   174  			return fmt.Errorf("invalid node URL: %v", err)
   175  		}
   176  		srv.AddPeer(node)
   177  		return nil
   178  	}
   179  	// set chequebook
   180  	if self.swapEnabled {
   181  		ctx := context.Background() // The initial setup has no deadline.
   182  		err := self.SetChequebook(ctx)
   183  		if err != nil {
   184  			return fmt.Errorf("Unable to set chequebook for SWAP: %v", err)
   185  		}
   186  		log.Debug(fmt.Sprintf("-> cheque book for SWAP: %v", self.config.Swap.Chequebook()))
   187  	} else {
   188  		log.Debug(fmt.Sprintf("SWAP disabled: no cheque book set"))
   189  	}
   190  
   191  	log.Warn(fmt.Sprintf("Starting Swarm service"))
   192  	self.hive.Start(
   193  		discover.PubkeyID(&srv.PrivateKey.PublicKey),
   194  		func() string { return srv.ListenAddr },
   195  		connectPeer,
   196  	)
   197  	log.Info(fmt.Sprintf("Swarm network started on bzz address: %v", self.hive.Addr()))
   198  
   199  	self.dpa.Start()
   200  	log.Debug(fmt.Sprintf("Swarm DPA started"))
   201  
   202  	// start swarm http proxy server
   203  	if self.config.Port != "" {
   204  		addr := net.JoinHostPort(self.config.ListenAddr, self.config.Port)
   205  		go httpapi.StartHttpServer(self.api, &httpapi.ServerConfig{
   206  			Addr:       addr,
   207  			CorsString: self.corsString,
   208  		})
   209  		log.Info(fmt.Sprintf("Swarm http proxy started on %v", addr))
   210  
   211  		if self.corsString != "" {
   212  			log.Debug(fmt.Sprintf("Swarm http proxy started with corsdomain: %v", self.corsString))
   213  		}
   214  	}
   215  
   216  	return nil
   217  }
   218  
   219  // implements the node.Service interface
   220  // stops all component services.
   221  func (self *Swarm) Stop() error {
   222  	self.dpa.Stop()
   223  	err := self.hive.Stop()
   224  	if ch := self.config.Swap.Chequebook(); ch != nil {
   225  		ch.Stop()
   226  		ch.Save()
   227  	}
   228  
   229  	if self.lstore != nil {
   230  		self.lstore.DbStore.Close()
   231  	}
   232  	self.sfs.Stop()
   233  	return err
   234  }
   235  
   236  // implements the node.Service interface
   237  func (self *Swarm) Protocols() []p2p.Protocol {
   238  	proto, err := network.Bzz(self.depo, self.backend, self.hive, self.dbAccess, self.config.Swap, self.config.SyncParams, self.config.NetworkId)
   239  	if err != nil {
   240  		return nil
   241  	}
   242  	return []p2p.Protocol{proto}
   243  }
   244  
   245  // implements node.Service
   246  // Apis returns the RPC Api descriptors the Swarm implementation offers
   247  func (self *Swarm) APIs() []rpc.API {
   248  	return []rpc.API{
   249  		// public APIs
   250  		{
   251  			Namespace: "bzz",
   252  			Version:   "0.1",
   253  			Service:   &Info{self.config, chequebook.ContractParams},
   254  			Public:    true,
   255  		},
   256  		// admin APIs
   257  		{
   258  			Namespace: "bzz",
   259  			Version:   "0.1",
   260  			Service:   api.NewControl(self.api, self.hive),
   261  			Public:    false,
   262  		},
   263  		{
   264  			Namespace: "chequebook",
   265  			Version:   chequebook.Version,
   266  			Service:   chequebook.NewApi(self.config.Swap.Chequebook),
   267  			Public:    false,
   268  		},
   269  		{
   270  			Namespace: "swarmfs",
   271  			Version:   fuse.Swarmfs_Version,
   272  			Service:   self.sfs,
   273  			Public:    false,
   274  		},
   275  		// storage APIs
   276  		// DEPRECATED: Use the HTTP API instead
   277  		{
   278  			Namespace: "bzz",
   279  			Version:   "0.1",
   280  			Service:   api.NewStorage(self.api),
   281  			Public:    true,
   282  		},
   283  		{
   284  			Namespace: "bzz",
   285  			Version:   "0.1",
   286  			Service:   api.NewFileSystem(self.api),
   287  			Public:    false,
   288  		},
   289  		// {Namespace, Version, api.NewAdmin(self), false},
   290  	}
   291  }
   292  
   293  func (self *Swarm) Api() *api.Api {
   294  	return self.api
   295  }
   296  
   297  // SetChequebook ensures that the local checquebook is set up on chain.
   298  func (self *Swarm) SetChequebook(ctx context.Context) error {
   299  	err := self.config.Swap.SetChequebook(ctx, self.backend, self.config.Path)
   300  	if err != nil {
   301  		return err
   302  	}
   303  	log.Info(fmt.Sprintf("new chequebook set (%v): saving config file, resetting all connections in the hive", self.config.Swap.Contract.Hex()))
   304  	self.hive.DropAll()
   305  	return nil
   306  }
   307  
   308  // Local swarm without netStore
   309  func NewLocalSwarm(datadir, port string) (self *Swarm, err error) {
   310  
   311  	prvKey, err := crypto.GenerateKey()
   312  	if err != nil {
   313  		return
   314  	}
   315  
   316  	config := api.NewDefaultConfig()
   317  	config.Path = datadir
   318  	config.Init(prvKey)
   319  	config.Port = port
   320  
   321  	dpa, err := storage.NewLocalDPA(datadir)
   322  	if err != nil {
   323  		return
   324  	}
   325  
   326  	self = &Swarm{
   327  		api:    api.NewApi(dpa, nil),
   328  		config: config,
   329  	}
   330  
   331  	return
   332  }
   333  
   334  // serialisable info about swarm
   335  type Info struct {
   336  	*api.Config
   337  	*chequebook.Params
   338  }
   339  
   340  func (self *Info) Info() *Info {
   341  	return self
   342  }