github.com/n1ghtfa1l/go-vnt@v0.6.4-alpha.6/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 "strings" 27 "time" 28 "unicode" 29 30 "github.com/vntchain/go-vnt/accounts/abi/bind" 31 "github.com/vntchain/go-vnt/common" 32 "github.com/vntchain/go-vnt/contracts/chequebook" 33 "github.com/vntchain/go-vnt/contracts/vns" 34 "github.com/vntchain/go-vnt/crypto" 35 "github.com/vntchain/go-vnt/log" 36 "github.com/vntchain/go-vnt/metrics" 37 "github.com/vntchain/go-vnt/node" 38 "github.com/vntchain/go-vnt/params" 39 "github.com/vntchain/go-vnt/rpc" 40 "github.com/vntchain/go-vnt/swarm/api" 41 httpapi "github.com/vntchain/go-vnt/swarm/api/http" 42 "github.com/vntchain/go-vnt/swarm/fuse" 43 "github.com/vntchain/go-vnt/swarm/network" 44 "github.com/vntchain/go-vnt/swarm/storage" 45 "github.com/vntchain/go-vnt/vntclient" 46 "github.com/vntchain/go-vnt/vntp2p" 47 ) 48 49 var ( 50 startTime time.Time 51 updateGaugesPeriod = 5 * time.Second 52 startCounter = metrics.NewRegisteredCounter("stack,start", nil) 53 stopCounter = metrics.NewRegisteredCounter("stack,stop", nil) 54 uptimeGauge = metrics.NewRegisteredGauge("stack.uptime", nil) 55 dbSizeGauge = metrics.NewRegisteredGauge("storage.db.chunks.size", nil) 56 cacheSizeGauge = metrics.NewRegisteredGauge("storage.db.cache.size", nil) 57 ) 58 59 // the swarm stack 60 type Swarm struct { 61 config *api.Config // swarm configuration 62 api *api.Api // high level api layer (fs/manifest) 63 dns api.Resolver // DNS registrar 64 dbAccess *network.DbAccess // access to local chunk db iterator and storage counter 65 storage storage.ChunkStore // internal access to storage, common interface to cloud storage backends 66 dpa *storage.DPA // distributed preimage archive, the local API to the storage with document level storage/retrieval support 67 depo network.StorageHandler // remote request handler, interface between bzz protocol and the storage 68 cloud storage.CloudStore // procurement, cloud storage backend (can multi-cloud) 69 hive *network.Hive // the logistic manager 70 backend chequebook.Backend // simple blockchain Backend 71 privateKey *ecdsa.PrivateKey 72 corsString string 73 swapEnabled bool 74 lstore *storage.LocalStore // local store, needs to store for releasing resources after node stopped 75 sfs *fuse.SwarmFS // need this to cleanup all the active mounts on node exit 76 } 77 78 type SwarmAPI struct { 79 Api *api.Api 80 Backend chequebook.Backend 81 PrvKey *ecdsa.PrivateKey 82 } 83 84 func (self *Swarm) API() *SwarmAPI { 85 return &SwarmAPI{ 86 Api: self.api, 87 Backend: self.backend, 88 PrvKey: self.privateKey, 89 } 90 } 91 92 // creates a new swarm service instance 93 // implements node.Service 94 func NewSwarm(ctx *node.ServiceContext, backend chequebook.Backend, config *api.Config) (self *Swarm, err error) { 95 if bytes.Equal(common.FromHex(config.PublicKey), storage.ZeroKey) { 96 return nil, fmt.Errorf("empty public key") 97 } 98 if bytes.Equal(common.FromHex(config.BzzKey), storage.ZeroKey) { 99 return nil, fmt.Errorf("empty bzz key") 100 } 101 102 self = &Swarm{ 103 config: config, 104 swapEnabled: config.SwapEnabled, 105 backend: backend, 106 privateKey: config.Swap.PrivateKey(), 107 corsString: config.Cors, 108 } 109 log.Debug(fmt.Sprintf("Setting up Swarm service components")) 110 111 hash := storage.MakeHashFunc(config.ChunkerParams.Hash) 112 self.lstore, err = storage.NewLocalStore(hash, config.StoreParams) 113 if err != nil { 114 return 115 } 116 117 // setup local store 118 log.Debug(fmt.Sprintf("Set up local storage")) 119 120 self.dbAccess = network.NewDbAccess(self.lstore) 121 log.Debug(fmt.Sprintf("Set up local db access (iterator/counter)")) 122 123 // set up the kademlia hive 124 self.hive = network.NewHive( 125 common.HexToHash(self.config.BzzKey), // key to hive (kademlia base address) 126 config.HiveParams, // configuration parameters 127 config.SwapEnabled, // SWAP enabled 128 config.SyncEnabled, // syncronisation enabled 129 ) 130 log.Debug(fmt.Sprintf("Set up swarm network with Kademlia hive")) 131 132 // setup cloud storage backend 133 self.cloud = network.NewForwarder(self.hive) 134 log.Debug(fmt.Sprintf("-> set swarm forwarder as cloud storage backend")) 135 136 // setup cloud storage internal access layer 137 self.storage = storage.NewNetStore(hash, self.lstore, self.cloud, config.StoreParams) 138 log.Debug(fmt.Sprintf("-> swarm net store shared access layer to Swarm Chunk Store")) 139 140 // set up Depo (storage handler = cloud storage access layer for incoming remote requests) 141 self.depo = network.NewDepo(hash, self.lstore, self.storage) 142 log.Debug(fmt.Sprintf("-> REmote Access to CHunks")) 143 144 // set up DPA, the cloud storage local access layer 145 dpaChunkStore := storage.NewDpaChunkStore(self.lstore, self.storage) 146 log.Debug(fmt.Sprintf("-> Local Access to Swarm")) 147 // Swarm Hash Merklised Chunking for Arbitrary-length Document/File storage 148 self.dpa = storage.NewDPA(dpaChunkStore, self.config.ChunkerParams) 149 log.Debug(fmt.Sprintf("-> Content Store API")) 150 151 if len(config.VnsAPIs) > 0 { 152 opts := []api.MultiResolverOption{} 153 for _, c := range config.VnsAPIs { 154 tld, endpoint, addr := parseVnsAPIAddress(c) 155 r, err := newVnsClient(endpoint, addr, config) 156 if err != nil { 157 return nil, err 158 } 159 opts = append(opts, api.MultiResolverOptionWithResolver(r, tld)) 160 } 161 self.dns = api.NewMultiResolver(opts...) 162 } 163 164 self.api = api.NewApi(self.dpa, self.dns) 165 // Manifests for Smart Hosting 166 log.Debug(fmt.Sprintf("-> Web3 virtual server API")) 167 168 self.sfs = fuse.NewSwarmFS(self.api) 169 log.Debug("-> Initializing Fuse file system") 170 171 return self, nil 172 } 173 174 // parseVnsAPIAddress parses string according to format 175 // [tld:][contract-addr@]url and returns VNSClientConfig structure 176 // with endpoint, contract address and TLD. 177 func parseVnsAPIAddress(s string) (tld, endpoint string, addr common.Address) { 178 isAllLetterString := func(s string) bool { 179 for _, r := range s { 180 if !unicode.IsLetter(r) { 181 return false 182 } 183 } 184 return true 185 } 186 endpoint = s 187 if i := strings.Index(endpoint, ":"); i > 0 { 188 if isAllLetterString(endpoint[:i]) && len(endpoint) > i+2 && endpoint[i+1:i+3] != "//" { 189 tld = endpoint[:i] 190 endpoint = endpoint[i+1:] 191 } 192 } 193 if i := strings.Index(endpoint, "@"); i > 0 { 194 addr = common.HexToAddress(endpoint[:i]) 195 endpoint = endpoint[i+1:] 196 } 197 return 198 } 199 200 // newVnsClient creates a new VNS client for that is a consumer of 201 // a VNS API on a specific endpoint. It is used as a helper function 202 // for creating multiple resolvers in NewSwarm function. 203 func newVnsClient(endpoint string, addr common.Address, config *api.Config) (*vns.VNS, error) { 204 log.Info("connecting to VNS API", "url", endpoint) 205 client, err := rpc.Dial(endpoint) 206 if err != nil { 207 return nil, fmt.Errorf("error connecting to VNS API %s: %s", endpoint, err) 208 } 209 vnsClient := vntclient.NewClient(client) 210 211 vnsRoot := config.VnsRoot 212 if addr != (common.Address{}) { 213 vnsRoot = addr 214 } else { 215 a, err := detectVnsAddr(client) 216 if err == nil { 217 vnsRoot = a 218 } else { 219 log.Warn(fmt.Sprintf("could not determine VNS contract address, using default %s", vnsRoot), "err", err) 220 } 221 } 222 transactOpts := bind.NewKeyedTransactor(config.Swap.PrivateKey(), big.NewInt(int64(config.NetworkId))) 223 dns, err := vns.NewVNS(transactOpts, vnsRoot, vnsClient) 224 if err != nil { 225 return nil, err 226 } 227 log.Debug(fmt.Sprintf("-> Swarm Domain Name Registrar %v @ address %v", endpoint, vnsRoot.Hex())) 228 return dns, err 229 } 230 231 // detectVnsAddr determines the VNS contract address by getting both the 232 // version and genesis hash using the client and matching them to either 233 // mainnet or testnet addresses 234 func detectVnsAddr(client *rpc.Client) (common.Address, error) { 235 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 236 defer cancel() 237 238 var version string 239 if err := client.CallContext(ctx, &version, "net_version"); err != nil { 240 return common.Address{}, err 241 } 242 243 block, err := vntclient.NewClient(client).BlockByNumber(ctx, big.NewInt(0)) 244 if err != nil { 245 return common.Address{}, err 246 } 247 248 switch { 249 250 case version == "1" && block.Hash() == params.MainnetGenesisHash: 251 log.Info("using Mainnet VNS contract address", "addr", vns.MainNetAddress) 252 return vns.MainNetAddress, nil 253 254 //case version == "3" && block.Hash() == params.TestnetGenesisHash: 255 // log.Info("using Testnet VNS contract address", "addr", vns.TestNetAddress) 256 // return vns.TestNetAddress, nil 257 258 default: 259 return common.Address{}, fmt.Errorf("unknown version and genesis hash: %s %s", version, block.Hash()) 260 } 261 } 262 263 /* 264 Start is called when the stack is started 265 * starts the network kademlia hive peer management 266 * (starts netStore level 0 api) 267 * starts DPA level 1 api (chunking -> store/retrieve requests) 268 * (starts level 2 api) 269 * starts http proxy server 270 * registers url scheme handlers for bzz, etc 271 * TODO: start subservices like sword, swear, swarmdns 272 */ 273 // implements the node.Service interface 274 func (self *Swarm) Start(srv *vntp2p.Server) error { 275 startTime = time.Now() 276 ctx := context.Background() 277 278 connectPeer := func(url string) error { 279 node, err := vntp2p.ParseNode(url) 280 if err != nil { 281 return fmt.Errorf("invalid node URL: %v", err) 282 } 283 srv.AddPeer(ctx, node) 284 return nil 285 } 286 // set chequebook 287 if self.swapEnabled { 288 ctx := context.Background() // The initial setup has no deadline. 289 err := self.SetChequebook(ctx) 290 if err != nil { 291 return fmt.Errorf("Unable to set chequebook for SWAP: %v", err) 292 } 293 log.Debug(fmt.Sprintf("-> cheque book for SWAP: %v", self.config.Swap.Chequebook())) 294 } else { 295 log.Debug(fmt.Sprintf("SWAP disabled: no cheque book set")) 296 } 297 298 log.Warn(fmt.Sprintf("Starting Swarm service")) 299 self.hive.Start( 300 vntp2p.PubkeyID(&srv.PrivateKey.PublicKey), 301 func() string { return srv.ListenAddr }, 302 connectPeer, 303 ) 304 log.Info(fmt.Sprintf("Swarm network started on bzz address: %v", self.hive.Addr())) 305 306 self.dpa.Start() 307 log.Debug(fmt.Sprintf("Swarm DPA started")) 308 309 // start swarm http proxy server 310 if self.config.Port != "" { 311 addr := net.JoinHostPort(self.config.ListenAddr, self.config.Port) 312 go httpapi.StartHttpServer(self.api, &httpapi.ServerConfig{ 313 Addr: addr, 314 CorsString: self.corsString, 315 }) 316 log.Info(fmt.Sprintf("Swarm http proxy started on %v", addr)) 317 318 if self.corsString != "" { 319 log.Debug(fmt.Sprintf("Swarm http proxy started with corsdomain: %v", self.corsString)) 320 } 321 } 322 323 self.periodicallyUpdateGauges() 324 325 startCounter.Inc(1) 326 return nil 327 } 328 329 func (self *Swarm) periodicallyUpdateGauges() { 330 ticker := time.NewTicker(updateGaugesPeriod) 331 332 go func() { 333 for range ticker.C { 334 self.updateGauges() 335 } 336 }() 337 } 338 339 func (self *Swarm) updateGauges() { 340 dbSizeGauge.Update(int64(self.lstore.DbCounter())) 341 cacheSizeGauge.Update(int64(self.lstore.CacheCounter())) 342 uptimeGauge.Update(time.Since(startTime).Nanoseconds()) 343 } 344 345 // implements the node.Service interface 346 // stops all component services. 347 func (self *Swarm) Stop() error { 348 self.dpa.Stop() 349 err := self.hive.Stop() 350 if ch := self.config.Swap.Chequebook(); ch != nil { 351 ch.Stop() 352 ch.Save() 353 } 354 355 if self.lstore != nil { 356 self.lstore.DbStore.Close() 357 } 358 self.sfs.Stop() 359 stopCounter.Inc(1) 360 return err 361 } 362 363 // implements the node.Service interface 364 func (self *Swarm) Protocols() []vntp2p.Protocol { 365 proto, err := network.Bzz(self.depo, self.backend, self.hive, self.dbAccess, self.config.Swap, self.config.SyncParams, self.config.NetworkId) 366 if err != nil { 367 return nil 368 } 369 return []vntp2p.Protocol{proto} 370 } 371 372 // implements node.Service 373 // Apis returns the RPC Api descriptors the Swarm implementation offers 374 func (self *Swarm) APIs() []rpc.API { 375 return []rpc.API{ 376 // public APIs 377 { 378 Namespace: "bzz", 379 Version: "0.1", 380 Service: &Info{self.config, chequebook.ContractParams}, 381 Public: true, 382 }, 383 // admin APIs 384 { 385 Namespace: "bzz", 386 Version: "0.1", 387 Service: api.NewControl(self.api, self.hive), 388 Public: false, 389 }, 390 { 391 Namespace: "chequebook", 392 Version: chequebook.Version, 393 Service: chequebook.NewApi(self.config.Swap.Chequebook), 394 Public: false, 395 }, 396 { 397 Namespace: "swarmfs", 398 Version: fuse.Swarmfs_Version, 399 Service: self.sfs, 400 Public: false, 401 }, 402 // storage APIs 403 // DEPRECATED: Use the HTTP API instead 404 { 405 Namespace: "bzz", 406 Version: "0.1", 407 Service: api.NewStorage(self.api), 408 Public: true, 409 }, 410 { 411 Namespace: "bzz", 412 Version: "0.1", 413 Service: api.NewFileSystem(self.api), 414 Public: false, 415 }, 416 // {Namespace, Version, api.NewAdmin(self), false}, 417 } 418 } 419 420 func (self *Swarm) Api() *api.Api { 421 return self.api 422 } 423 424 // SetChequebook ensures that the local checquebook is set up on chain. 425 func (self *Swarm) SetChequebook(ctx context.Context) error { 426 err := self.config.Swap.SetChequebook(ctx, big.NewInt(int64(self.config.NetworkId)), self.backend, self.config.Path) 427 if err != nil { 428 return err 429 } 430 log.Info(fmt.Sprintf("new chequebook set (%v): saving config file, resetting all connections in the hive", self.config.Swap.Contract.Hex())) 431 self.hive.DropAll() 432 return nil 433 } 434 435 // Local swarm without netStore 436 func NewLocalSwarm(datadir, port string) (self *Swarm, err error) { 437 438 prvKey, err := crypto.GenerateKey() 439 if err != nil { 440 return 441 } 442 443 config := api.NewDefaultConfig() 444 config.Path = datadir 445 config.Init(prvKey) 446 config.Port = port 447 448 dpa, err := storage.NewLocalDPA(datadir) 449 if err != nil { 450 return 451 } 452 453 self = &Swarm{ 454 api: api.NewApi(dpa, nil), 455 config: config, 456 } 457 458 return 459 } 460 461 // serialisable info about swarm 462 type Info struct { 463 *api.Config 464 *chequebook.Params 465 } 466 467 func (self *Info) Info() *Info { 468 return self 469 }