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 }