github.com/muhammedhassanm/blockchain@v0.0.0-20200120143007-697261defd4d/go-ethereum-master/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 "math/big" 25 "net" 26 "path/filepath" 27 "strings" 28 "time" 29 "unicode" 30 31 "github.com/ethereum/go-ethereum/accounts/abi/bind" 32 "github.com/ethereum/go-ethereum/common" 33 "github.com/ethereum/go-ethereum/contracts/chequebook" 34 "github.com/ethereum/go-ethereum/contracts/ens" 35 "github.com/ethereum/go-ethereum/ethclient" 36 "github.com/ethereum/go-ethereum/metrics" 37 "github.com/ethereum/go-ethereum/p2p" 38 "github.com/ethereum/go-ethereum/p2p/discover" 39 "github.com/ethereum/go-ethereum/p2p/protocols" 40 "github.com/ethereum/go-ethereum/params" 41 "github.com/ethereum/go-ethereum/rpc" 42 "github.com/ethereum/go-ethereum/swarm/api" 43 httpapi "github.com/ethereum/go-ethereum/swarm/api/http" 44 "github.com/ethereum/go-ethereum/swarm/fuse" 45 "github.com/ethereum/go-ethereum/swarm/log" 46 "github.com/ethereum/go-ethereum/swarm/network" 47 "github.com/ethereum/go-ethereum/swarm/network/stream" 48 "github.com/ethereum/go-ethereum/swarm/pss" 49 "github.com/ethereum/go-ethereum/swarm/state" 50 "github.com/ethereum/go-ethereum/swarm/storage" 51 "github.com/ethereum/go-ethereum/swarm/storage/mock" 52 "github.com/ethereum/go-ethereum/swarm/storage/mru" 53 ) 54 55 var ( 56 startTime time.Time 57 updateGaugesPeriod = 5 * time.Second 58 startCounter = metrics.NewRegisteredCounter("stack,start", nil) 59 stopCounter = metrics.NewRegisteredCounter("stack,stop", nil) 60 uptimeGauge = metrics.NewRegisteredGauge("stack.uptime", nil) 61 requestsCacheGauge = metrics.NewRegisteredGauge("storage.cache.requests.size", nil) 62 ) 63 64 // the swarm stack 65 type Swarm struct { 66 config *api.Config // swarm configuration 67 api *api.API // high level api layer (fs/manifest) 68 dns api.Resolver // DNS registrar 69 fileStore *storage.FileStore // distributed preimage archive, the local API to the storage with document level storage/retrieval support 70 streamer *stream.Registry 71 bzz *network.Bzz // the logistic manager 72 backend chequebook.Backend // simple blockchain Backend 73 privateKey *ecdsa.PrivateKey 74 corsString string 75 swapEnabled bool 76 lstore *storage.LocalStore // local store, needs to store for releasing resources after node stopped 77 sfs *fuse.SwarmFS // need this to cleanup all the active mounts on node exit 78 ps *pss.Pss 79 } 80 81 type SwarmAPI struct { 82 Api *api.API 83 Backend chequebook.Backend 84 PrvKey *ecdsa.PrivateKey 85 } 86 87 func (self *Swarm) API() *SwarmAPI { 88 return &SwarmAPI{ 89 Api: self.api, 90 Backend: self.backend, 91 PrvKey: self.privateKey, 92 } 93 } 94 95 // creates a new swarm service instance 96 // implements node.Service 97 // If mockStore is not nil, it will be used as the storage for chunk data. 98 // MockStore should be used only for testing. 99 func NewSwarm(config *api.Config, mockStore *mock.NodeStore) (self *Swarm, err error) { 100 101 if bytes.Equal(common.FromHex(config.PublicKey), storage.ZeroAddr) { 102 return nil, fmt.Errorf("empty public key") 103 } 104 if bytes.Equal(common.FromHex(config.BzzKey), storage.ZeroAddr) { 105 return nil, fmt.Errorf("empty bzz key") 106 } 107 108 var backend chequebook.Backend 109 if config.SwapAPI != "" && config.SwapEnabled { 110 log.Info("connecting to SWAP API", "url", config.SwapAPI) 111 backend, err = ethclient.Dial(config.SwapAPI) 112 if err != nil { 113 return nil, fmt.Errorf("error connecting to SWAP API %s: %s", config.SwapAPI, err) 114 } 115 } 116 117 self = &Swarm{ 118 config: config, 119 backend: backend, 120 privateKey: config.ShiftPrivateKey(), 121 } 122 log.Debug(fmt.Sprintf("Setting up Swarm service components")) 123 124 config.HiveParams.Discovery = true 125 126 log.Debug(fmt.Sprintf("-> swarm net store shared access layer to Swarm Chunk Store")) 127 128 nodeID, err := discover.HexID(config.NodeID) 129 if err != nil { 130 return nil, err 131 } 132 addr := &network.BzzAddr{ 133 OAddr: common.FromHex(config.BzzKey), 134 UAddr: []byte(discover.NewNode(nodeID, net.IP{127, 0, 0, 1}, 30303, 30303).String()), 135 } 136 137 bzzconfig := &network.BzzConfig{ 138 NetworkID: config.NetworkID, 139 OverlayAddr: addr.OAddr, 140 UnderlayAddr: addr.UAddr, 141 HiveParams: config.HiveParams, 142 } 143 144 stateStore, err := state.NewDBStore(filepath.Join(config.Path, "state-store.db")) 145 if err != nil { 146 return 147 } 148 149 // set up high level api 150 var resolver *api.MultiResolver 151 if len(config.EnsAPIs) > 0 { 152 opts := []api.MultiResolverOption{} 153 for _, c := range config.EnsAPIs { 154 tld, endpoint, addr := parseEnsAPIAddress(c) 155 r, err := newEnsClient(endpoint, addr, config, self.privateKey) 156 if err != nil { 157 return nil, err 158 } 159 opts = append(opts, api.MultiResolverOptionWithResolver(r, tld)) 160 161 } 162 resolver = api.NewMultiResolver(opts...) 163 self.dns = resolver 164 } 165 166 self.lstore, err = storage.NewLocalStore(config.LocalStoreParams, mockStore) 167 if err != nil { 168 return 169 } 170 171 db := storage.NewDBAPI(self.lstore) 172 to := network.NewKademlia( 173 common.FromHex(config.BzzKey), 174 network.NewKadParams(), 175 ) 176 delivery := stream.NewDelivery(to, db) 177 178 self.streamer = stream.NewRegistry(addr, delivery, db, stateStore, &stream.RegistryOptions{ 179 SkipCheck: config.DeliverySkipCheck, 180 DoSync: config.SyncEnabled, 181 DoRetrieve: true, 182 SyncUpdateDelay: config.SyncUpdateDelay, 183 }) 184 185 // set up NetStore, the cloud storage local access layer 186 netStore := storage.NewNetStore(self.lstore, self.streamer.Retrieve) 187 // Swarm Hash Merklised Chunking for Arbitrary-length Document/File storage 188 self.fileStore = storage.NewFileStore(netStore, self.config.FileStoreParams) 189 190 var resourceHandler *mru.Handler 191 rhparams := &mru.HandlerParams{ 192 // TODO: config parameter to set limits 193 QueryMaxPeriods: &mru.LookupParams{ 194 Limit: false, 195 }, 196 Signer: &mru.GenericSigner{ 197 PrivKey: self.privateKey, 198 }, 199 } 200 if resolver != nil { 201 resolver.SetNameHash(ens.EnsNode) 202 // Set HeaderGetter and OwnerValidator interfaces to resolver only if it is not nil. 203 rhparams.HeaderGetter = resolver 204 rhparams.OwnerValidator = resolver 205 } else { 206 log.Warn("No ETH API specified, resource updates will use block height approximation") 207 // TODO: blockestimator should use saved values derived from last time ethclient was connected 208 rhparams.HeaderGetter = mru.NewBlockEstimator() 209 } 210 resourceHandler, err = mru.NewHandler(rhparams) 211 if err != nil { 212 return nil, err 213 } 214 resourceHandler.SetStore(netStore) 215 216 var validators []storage.ChunkValidator 217 validators = append(validators, storage.NewContentAddressValidator(storage.MakeHashFunc(storage.DefaultHash))) 218 if resourceHandler != nil { 219 validators = append(validators, resourceHandler) 220 } 221 self.lstore.Validators = validators 222 223 // setup local store 224 log.Debug(fmt.Sprintf("Set up local storage")) 225 226 self.bzz = network.NewBzz(bzzconfig, to, stateStore, stream.Spec, self.streamer.Run) 227 228 // Pss = postal service over swarm (devp2p over bzz) 229 self.ps, err = pss.NewPss(to, config.Pss) 230 if err != nil { 231 return nil, err 232 } 233 if pss.IsActiveHandshake { 234 pss.SetHandshakeController(self.ps, pss.NewHandshakeParams()) 235 } 236 237 self.api = api.NewAPI(self.fileStore, self.dns, resourceHandler) 238 // Manifests for Smart Hosting 239 log.Debug(fmt.Sprintf("-> Web3 virtual server API")) 240 241 self.sfs = fuse.NewSwarmFS(self.api) 242 log.Debug("-> Initializing Fuse file system") 243 244 return self, nil 245 } 246 247 // parseEnsAPIAddress parses string according to format 248 // [tld:][contract-addr@]url and returns ENSClientConfig structure 249 // with endpoint, contract address and TLD. 250 func parseEnsAPIAddress(s string) (tld, endpoint string, addr common.Address) { 251 isAllLetterString := func(s string) bool { 252 for _, r := range s { 253 if !unicode.IsLetter(r) { 254 return false 255 } 256 } 257 return true 258 } 259 endpoint = s 260 if i := strings.Index(endpoint, ":"); i > 0 { 261 if isAllLetterString(endpoint[:i]) && len(endpoint) > i+2 && endpoint[i+1:i+3] != "//" { 262 tld = endpoint[:i] 263 endpoint = endpoint[i+1:] 264 } 265 } 266 if i := strings.Index(endpoint, "@"); i > 0 { 267 addr = common.HexToAddress(endpoint[:i]) 268 endpoint = endpoint[i+1:] 269 } 270 return 271 } 272 273 // ensClient provides functionality for api.ResolveValidator 274 type ensClient struct { 275 *ens.ENS 276 *ethclient.Client 277 } 278 279 // newEnsClient creates a new ENS client for that is a consumer of 280 // a ENS API on a specific endpoint. It is used as a helper function 281 // for creating multiple resolvers in NewSwarm function. 282 func newEnsClient(endpoint string, addr common.Address, config *api.Config, privkey *ecdsa.PrivateKey) (*ensClient, error) { 283 log.Info("connecting to ENS API", "url", endpoint) 284 client, err := rpc.Dial(endpoint) 285 if err != nil { 286 return nil, fmt.Errorf("error connecting to ENS API %s: %s", endpoint, err) 287 } 288 ethClient := ethclient.NewClient(client) 289 290 ensRoot := config.EnsRoot 291 if addr != (common.Address{}) { 292 ensRoot = addr 293 } else { 294 a, err := detectEnsAddr(client) 295 if err == nil { 296 ensRoot = a 297 } else { 298 log.Warn(fmt.Sprintf("could not determine ENS contract address, using default %s", ensRoot), "err", err) 299 } 300 } 301 transactOpts := bind.NewKeyedTransactor(privkey) 302 dns, err := ens.NewENS(transactOpts, ensRoot, ethClient) 303 if err != nil { 304 return nil, err 305 } 306 log.Debug(fmt.Sprintf("-> Swarm Domain Name Registrar %v @ address %v", endpoint, ensRoot.Hex())) 307 return &ensClient{ 308 ENS: dns, 309 Client: ethClient, 310 }, err 311 } 312 313 // detectEnsAddr determines the ENS contract address by getting both the 314 // version and genesis hash using the client and matching them to either 315 // mainnet or testnet addresses 316 func detectEnsAddr(client *rpc.Client) (common.Address, error) { 317 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 318 defer cancel() 319 320 var version string 321 if err := client.CallContext(ctx, &version, "net_version"); err != nil { 322 return common.Address{}, err 323 } 324 325 block, err := ethclient.NewClient(client).BlockByNumber(ctx, big.NewInt(0)) 326 if err != nil { 327 return common.Address{}, err 328 } 329 330 switch { 331 332 case version == "1" && block.Hash() == params.MainnetGenesisHash: 333 log.Info("using Mainnet ENS contract address", "addr", ens.MainNetAddress) 334 return ens.MainNetAddress, nil 335 336 case version == "3" && block.Hash() == params.TestnetGenesisHash: 337 log.Info("using Testnet ENS contract address", "addr", ens.TestNetAddress) 338 return ens.TestNetAddress, nil 339 340 default: 341 return common.Address{}, fmt.Errorf("unknown version and genesis hash: %s %s", version, block.Hash()) 342 } 343 } 344 345 /* 346 Start is called when the stack is started 347 * starts the network kademlia hive peer management 348 * (starts netStore level 0 api) 349 * starts DPA level 1 api (chunking -> store/retrieve requests) 350 * (starts level 2 api) 351 * starts http proxy server 352 * registers url scheme handlers for bzz, etc 353 * TODO: start subservices like sword, swear, swarmdns 354 */ 355 // implements the node.Service interface 356 func (self *Swarm) Start(srv *p2p.Server) error { 357 startTime = time.Now() 358 359 // update uaddr to correct enode 360 newaddr := self.bzz.UpdateLocalAddr([]byte(srv.Self().String())) 361 log.Warn("Updated bzz local addr", "oaddr", fmt.Sprintf("%x", newaddr.OAddr), "uaddr", fmt.Sprintf("%s", newaddr.UAddr)) 362 // set chequebook 363 if self.config.SwapEnabled { 364 ctx := context.Background() // The initial setup has no deadline. 365 err := self.SetChequebook(ctx) 366 if err != nil { 367 return fmt.Errorf("Unable to set chequebook for SWAP: %v", err) 368 } 369 log.Debug(fmt.Sprintf("-> cheque book for SWAP: %v", self.config.Swap.Chequebook())) 370 } else { 371 log.Debug(fmt.Sprintf("SWAP disabled: no cheque book set")) 372 } 373 374 log.Warn(fmt.Sprintf("Starting Swarm service")) 375 376 err := self.bzz.Start(srv) 377 if err != nil { 378 log.Error("bzz failed", "err", err) 379 return err 380 } 381 log.Info(fmt.Sprintf("Swarm network started on bzz address: %x", self.bzz.Hive.Overlay.BaseAddr())) 382 383 if self.ps != nil { 384 self.ps.Start(srv) 385 log.Info("Pss started") 386 } 387 388 // start swarm http proxy server 389 if self.config.Port != "" { 390 addr := net.JoinHostPort(self.config.ListenAddr, self.config.Port) 391 go httpapi.StartHTTPServer(self.api, &httpapi.ServerConfig{ 392 Addr: addr, 393 CorsString: self.config.Cors, 394 }) 395 } 396 397 log.Debug(fmt.Sprintf("Swarm http proxy started on port: %v", self.config.Port)) 398 399 if self.config.Cors != "" { 400 log.Debug(fmt.Sprintf("Swarm http proxy started with corsdomain: %v", self.config.Cors)) 401 } 402 403 self.periodicallyUpdateGauges() 404 405 startCounter.Inc(1) 406 self.streamer.Start(srv) 407 return nil 408 } 409 410 func (self *Swarm) periodicallyUpdateGauges() { 411 ticker := time.NewTicker(updateGaugesPeriod) 412 413 go func() { 414 for range ticker.C { 415 self.updateGauges() 416 } 417 }() 418 } 419 420 func (self *Swarm) updateGauges() { 421 uptimeGauge.Update(time.Since(startTime).Nanoseconds()) 422 requestsCacheGauge.Update(int64(self.lstore.RequestsCacheLen())) 423 } 424 425 // implements the node.Service interface 426 // stops all component services. 427 func (self *Swarm) Stop() error { 428 if self.ps != nil { 429 self.ps.Stop() 430 } 431 if ch := self.config.Swap.Chequebook(); ch != nil { 432 ch.Stop() 433 ch.Save() 434 } 435 436 if self.lstore != nil { 437 self.lstore.DbStore.Close() 438 } 439 self.sfs.Stop() 440 stopCounter.Inc(1) 441 self.streamer.Stop() 442 return self.bzz.Stop() 443 } 444 445 // implements the node.Service interface 446 func (self *Swarm) Protocols() (protos []p2p.Protocol) { 447 protos = append(protos, self.bzz.Protocols()...) 448 449 if self.ps != nil { 450 protos = append(protos, self.ps.Protocols()...) 451 } 452 return 453 } 454 455 func (self *Swarm) RegisterPssProtocol(spec *protocols.Spec, targetprotocol *p2p.Protocol, options *pss.ProtocolParams) (*pss.Protocol, error) { 456 if !pss.IsActiveProtocol { 457 return nil, fmt.Errorf("Pss protocols not available (built with !nopssprotocol tag)") 458 } 459 topic := pss.ProtocolTopic(spec) 460 return pss.RegisterProtocol(self.ps, &topic, spec, targetprotocol, options) 461 } 462 463 // implements node.Service 464 // APIs returns the RPC API descriptors the Swarm implementation offers 465 func (self *Swarm) APIs() []rpc.API { 466 467 apis := []rpc.API{ 468 // public APIs 469 { 470 Namespace: "bzz", 471 Version: "3.0", 472 Service: &Info{self.config, chequebook.ContractParams}, 473 Public: true, 474 }, 475 // admin APIs 476 { 477 Namespace: "bzz", 478 Version: "3.0", 479 Service: api.NewControl(self.api, self.bzz.Hive), 480 Public: false, 481 }, 482 { 483 Namespace: "chequebook", 484 Version: chequebook.Version, 485 Service: chequebook.NewApi(self.config.Swap.Chequebook), 486 Public: false, 487 }, 488 { 489 Namespace: "swarmfs", 490 Version: fuse.Swarmfs_Version, 491 Service: self.sfs, 492 Public: false, 493 }, 494 // storage APIs 495 // DEPRECATED: Use the HTTP API instead 496 { 497 Namespace: "bzz", 498 Version: "0.1", 499 Service: api.NewStorage(self.api), 500 Public: true, 501 }, 502 { 503 Namespace: "bzz", 504 Version: "0.1", 505 Service: api.NewFileSystem(self.api), 506 Public: false, 507 }, 508 // {Namespace, Version, api.NewAdmin(self), false}, 509 } 510 511 apis = append(apis, self.bzz.APIs()...) 512 513 if self.ps != nil { 514 apis = append(apis, self.ps.APIs()...) 515 } 516 517 return apis 518 } 519 520 func (self *Swarm) Api() *api.API { 521 return self.api 522 } 523 524 // SetChequebook ensures that the local checquebook is set up on chain. 525 func (self *Swarm) SetChequebook(ctx context.Context) error { 526 err := self.config.Swap.SetChequebook(ctx, self.backend, self.config.Path) 527 if err != nil { 528 return err 529 } 530 log.Info(fmt.Sprintf("new chequebook set (%v): saving config file, resetting all connections in the hive", self.config.Swap.Contract.Hex())) 531 return nil 532 } 533 534 // serialisable info about swarm 535 type Info struct { 536 *api.Config 537 *chequebook.Params 538 } 539 540 func (self *Info) Info() *Info { 541 return self 542 }