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