github.com/daragao/go-ethereum@v1.8.14-0.20180809141559-45eaef243198/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/discover" 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/mock" 53 "github.com/ethereum/go-ethereum/swarm/storage/mru" 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 lstore *storage.LocalStore // local store, needs to store for releasing resources after node stopped 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 PrvKey *ecdsa.PrivateKey 89 } 90 91 func (self *Swarm) API() *SwarmAPI { 92 return &SwarmAPI{ 93 Api: self.api, 94 Backend: self.backend, 95 PrvKey: self.privateKey, 96 } 97 } 98 99 // creates a new swarm service instance 100 // implements node.Service 101 // If mockStore is not nil, it will be used as the storage for chunk data. 102 // MockStore should be used only for testing. 103 func NewSwarm(config *api.Config, mockStore *mock.NodeStore) (self *Swarm, err error) { 104 105 if bytes.Equal(common.FromHex(config.PublicKey), storage.ZeroAddr) { 106 return nil, fmt.Errorf("empty public key") 107 } 108 if bytes.Equal(common.FromHex(config.BzzKey), storage.ZeroAddr) { 109 return nil, fmt.Errorf("empty bzz key") 110 } 111 112 var backend chequebook.Backend 113 if config.SwapAPI != "" && config.SwapEnabled { 114 log.Info("connecting to SWAP API", "url", config.SwapAPI) 115 backend, err = ethclient.Dial(config.SwapAPI) 116 if err != nil { 117 return nil, fmt.Errorf("error connecting to SWAP API %s: %s", config.SwapAPI, err) 118 } 119 } 120 121 self = &Swarm{ 122 config: config, 123 backend: backend, 124 privateKey: config.ShiftPrivateKey(), 125 } 126 log.Debug(fmt.Sprintf("Setting up Swarm service components")) 127 128 config.HiveParams.Discovery = true 129 130 log.Debug(fmt.Sprintf("-> swarm net store shared access layer to Swarm Chunk Store")) 131 132 nodeID, err := discover.HexID(config.NodeID) 133 if err != nil { 134 return nil, err 135 } 136 addr := &network.BzzAddr{ 137 OAddr: common.FromHex(config.BzzKey), 138 UAddr: []byte(discover.NewNode(nodeID, net.IP{127, 0, 0, 1}, 30303, 30303).String()), 139 } 140 141 bzzconfig := &network.BzzConfig{ 142 NetworkID: config.NetworkID, 143 OverlayAddr: addr.OAddr, 144 UnderlayAddr: addr.UAddr, 145 HiveParams: config.HiveParams, 146 LightNode: config.LightNodeEnabled, 147 } 148 149 stateStore, err := state.NewDBStore(filepath.Join(config.Path, "state-store.db")) 150 if err != nil { 151 return 152 } 153 154 // set up high level api 155 var resolver *api.MultiResolver 156 if len(config.EnsAPIs) > 0 { 157 opts := []api.MultiResolverOption{} 158 for _, c := range config.EnsAPIs { 159 tld, endpoint, addr := parseEnsAPIAddress(c) 160 r, err := newEnsClient(endpoint, addr, config, self.privateKey) 161 if err != nil { 162 return nil, err 163 } 164 opts = append(opts, api.MultiResolverOptionWithResolver(r, tld)) 165 166 } 167 resolver = api.NewMultiResolver(opts...) 168 self.dns = resolver 169 } 170 171 self.lstore, err = storage.NewLocalStore(config.LocalStoreParams, mockStore) 172 if err != nil { 173 return 174 } 175 176 db := storage.NewDBAPI(self.lstore) 177 to := network.NewKademlia( 178 common.FromHex(config.BzzKey), 179 network.NewKadParams(), 180 ) 181 delivery := stream.NewDelivery(to, db) 182 183 self.streamer = stream.NewRegistry(addr, delivery, db, stateStore, &stream.RegistryOptions{ 184 SkipCheck: config.DeliverySkipCheck, 185 DoSync: config.SyncEnabled, 186 DoRetrieve: true, 187 SyncUpdateDelay: config.SyncUpdateDelay, 188 }) 189 190 // set up NetStore, the cloud storage local access layer 191 netStore := storage.NewNetStore(self.lstore, self.streamer.Retrieve) 192 // Swarm Hash Merklised Chunking for Arbitrary-length Document/File storage 193 self.fileStore = storage.NewFileStore(netStore, self.config.FileStoreParams) 194 195 var resourceHandler *mru.Handler 196 rhparams := &mru.HandlerParams{} 197 198 resourceHandler, err = mru.NewHandler(rhparams) 199 if err != nil { 200 return nil, err 201 } 202 resourceHandler.SetStore(netStore) 203 204 var validators []storage.ChunkValidator 205 validators = append(validators, storage.NewContentAddressValidator(storage.MakeHashFunc(storage.DefaultHash))) 206 if resourceHandler != nil { 207 validators = append(validators, resourceHandler) 208 } 209 self.lstore.Validators = validators 210 211 // setup local store 212 log.Debug(fmt.Sprintf("Set up local storage")) 213 214 self.bzz = network.NewBzz(bzzconfig, to, stateStore, stream.Spec, self.streamer.Run) 215 216 // Pss = postal service over swarm (devp2p over bzz) 217 self.ps, err = pss.NewPss(to, config.Pss) 218 if err != nil { 219 return nil, err 220 } 221 if pss.IsActiveHandshake { 222 pss.SetHandshakeController(self.ps, pss.NewHandshakeParams()) 223 } 224 225 self.api = api.NewAPI(self.fileStore, self.dns, resourceHandler) 226 // Manifests for Smart Hosting 227 log.Debug(fmt.Sprintf("-> Web3 virtual server API")) 228 229 self.sfs = fuse.NewSwarmFS(self.api) 230 log.Debug("-> Initializing Fuse file system") 231 232 return self, nil 233 } 234 235 // parseEnsAPIAddress parses string according to format 236 // [tld:][contract-addr@]url and returns ENSClientConfig structure 237 // with endpoint, contract address and TLD. 238 func parseEnsAPIAddress(s string) (tld, endpoint string, addr common.Address) { 239 isAllLetterString := func(s string) bool { 240 for _, r := range s { 241 if !unicode.IsLetter(r) { 242 return false 243 } 244 } 245 return true 246 } 247 endpoint = s 248 if i := strings.Index(endpoint, ":"); i > 0 { 249 if isAllLetterString(endpoint[:i]) && len(endpoint) > i+2 && endpoint[i+1:i+3] != "//" { 250 tld = endpoint[:i] 251 endpoint = endpoint[i+1:] 252 } 253 } 254 if i := strings.Index(endpoint, "@"); i > 0 { 255 addr = common.HexToAddress(endpoint[:i]) 256 endpoint = endpoint[i+1:] 257 } 258 return 259 } 260 261 // ensClient provides functionality for api.ResolveValidator 262 type ensClient struct { 263 *ens.ENS 264 *ethclient.Client 265 } 266 267 // newEnsClient creates a new ENS client for that is a consumer of 268 // a ENS API on a specific endpoint. It is used as a helper function 269 // for creating multiple resolvers in NewSwarm function. 270 func newEnsClient(endpoint string, addr common.Address, config *api.Config, privkey *ecdsa.PrivateKey) (*ensClient, error) { 271 log.Info("connecting to ENS API", "url", endpoint) 272 client, err := rpc.Dial(endpoint) 273 if err != nil { 274 return nil, fmt.Errorf("error connecting to ENS API %s: %s", endpoint, err) 275 } 276 ethClient := ethclient.NewClient(client) 277 278 ensRoot := config.EnsRoot 279 if addr != (common.Address{}) { 280 ensRoot = addr 281 } else { 282 a, err := detectEnsAddr(client) 283 if err == nil { 284 ensRoot = a 285 } else { 286 log.Warn(fmt.Sprintf("could not determine ENS contract address, using default %s", ensRoot), "err", err) 287 } 288 } 289 transactOpts := bind.NewKeyedTransactor(privkey) 290 dns, err := ens.NewENS(transactOpts, ensRoot, ethClient) 291 if err != nil { 292 return nil, err 293 } 294 log.Debug(fmt.Sprintf("-> Swarm Domain Name Registrar %v @ address %v", endpoint, ensRoot.Hex())) 295 return &ensClient{ 296 ENS: dns, 297 Client: ethClient, 298 }, err 299 } 300 301 // detectEnsAddr determines the ENS contract address by getting both the 302 // version and genesis hash using the client and matching them to either 303 // mainnet or testnet addresses 304 func detectEnsAddr(client *rpc.Client) (common.Address, error) { 305 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 306 defer cancel() 307 308 var version string 309 if err := client.CallContext(ctx, &version, "net_version"); err != nil { 310 return common.Address{}, err 311 } 312 313 block, err := ethclient.NewClient(client).BlockByNumber(ctx, big.NewInt(0)) 314 if err != nil { 315 return common.Address{}, err 316 } 317 318 switch { 319 320 case version == "1" && block.Hash() == params.MainnetGenesisHash: 321 log.Info("using Mainnet ENS contract address", "addr", ens.MainNetAddress) 322 return ens.MainNetAddress, nil 323 324 case version == "3" && block.Hash() == params.TestnetGenesisHash: 325 log.Info("using Testnet ENS contract address", "addr", ens.TestNetAddress) 326 return ens.TestNetAddress, nil 327 328 default: 329 return common.Address{}, fmt.Errorf("unknown version and genesis hash: %s %s", version, block.Hash()) 330 } 331 } 332 333 /* 334 Start is called when the stack is started 335 * starts the network kademlia hive peer management 336 * (starts netStore level 0 api) 337 * starts DPA level 1 api (chunking -> store/retrieve requests) 338 * (starts level 2 api) 339 * starts http proxy server 340 * registers url scheme handlers for bzz, etc 341 * TODO: start subservices like sword, swear, swarmdns 342 */ 343 // implements the node.Service interface 344 func (self *Swarm) Start(srv *p2p.Server) error { 345 startTime = time.Now() 346 347 self.tracerClose = tracing.Closer 348 349 // update uaddr to correct enode 350 newaddr := self.bzz.UpdateLocalAddr([]byte(srv.Self().String())) 351 log.Warn("Updated bzz local addr", "oaddr", fmt.Sprintf("%x", newaddr.OAddr), "uaddr", fmt.Sprintf("%s", newaddr.UAddr)) 352 // set chequebook 353 if self.config.SwapEnabled { 354 ctx := context.Background() // The initial setup has no deadline. 355 err := self.SetChequebook(ctx) 356 if err != nil { 357 return fmt.Errorf("Unable to set chequebook for SWAP: %v", err) 358 } 359 log.Debug(fmt.Sprintf("-> cheque book for SWAP: %v", self.config.Swap.Chequebook())) 360 } else { 361 log.Debug(fmt.Sprintf("SWAP disabled: no cheque book set")) 362 } 363 364 log.Warn(fmt.Sprintf("Starting Swarm service")) 365 366 err := self.bzz.Start(srv) 367 if err != nil { 368 log.Error("bzz failed", "err", err) 369 return err 370 } 371 log.Info(fmt.Sprintf("Swarm network started on bzz address: %x", self.bzz.Hive.Overlay.BaseAddr())) 372 373 if self.ps != nil { 374 self.ps.Start(srv) 375 log.Info("Pss started") 376 } 377 378 // start swarm http proxy server 379 if self.config.Port != "" { 380 addr := net.JoinHostPort(self.config.ListenAddr, self.config.Port) 381 server := httpapi.NewServer(self.api, self.config.Cors) 382 383 go server.ListenAndServe(addr) 384 } 385 386 log.Debug(fmt.Sprintf("Swarm http proxy started on port: %v", self.config.Port)) 387 388 if self.config.Cors != "" { 389 log.Debug(fmt.Sprintf("Swarm http proxy started with corsdomain: %v", self.config.Cors)) 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.lstore.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.lstore != nil { 433 self.lstore.DbStore.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 // storage APIs 491 // DEPRECATED: Use the HTTP API instead 492 { 493 Namespace: "bzz", 494 Version: "0.1", 495 Service: api.NewStorage(self.api), 496 Public: true, 497 }, 498 { 499 Namespace: "bzz", 500 Version: "0.1", 501 Service: api.NewFileSystem(self.api), 502 Public: false, 503 }, 504 // {Namespace, Version, api.NewAdmin(self), false}, 505 } 506 507 apis = append(apis, self.bzz.APIs()...) 508 509 if self.ps != nil { 510 apis = append(apis, self.ps.APIs()...) 511 } 512 513 return apis 514 } 515 516 func (self *Swarm) Api() *api.API { 517 return self.api 518 } 519 520 // SetChequebook ensures that the local checquebook is set up on chain. 521 func (self *Swarm) SetChequebook(ctx context.Context) error { 522 err := self.config.Swap.SetChequebook(ctx, self.backend, self.config.Path) 523 if err != nil { 524 return err 525 } 526 log.Info(fmt.Sprintf("new chequebook set (%v): saving config file, resetting all connections in the hive", self.config.Swap.Contract.Hex())) 527 return nil 528 } 529 530 // serialisable info about swarm 531 type Info struct { 532 *api.Config 533 *chequebook.Params 534 } 535 536 func (self *Info) Info() *Info { 537 return self 538 }