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