github.com/m3shine/gochain@v2.2.26+incompatible/swarm/swarm.go (about)

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