github.com/Elemental-core/elementalcore@v0.0.0-20191206075037-63891242267a/contracts/release/release.go (about) 1 // Copyright 2015 The elementalcore Authors 2 // This file is part of the elementalcore library. 3 // 4 // The elementalcore 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 elementalcore 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 elementalcore library. If not, see <http://www.gnu.org/licenses/>. 16 17 // Package release contains the node service that tracks client releases. 18 package release 19 20 //go:generate abigen --sol ./contract.sol --pkg release --out ./contract.go 21 22 import ( 23 "context" 24 "fmt" 25 "strings" 26 "time" 27 28 "github.com/Elemental-core/elementalcore/accounts/abi/bind" 29 "github.com/Elemental-core/elementalcore/common" 30 "github.com/Elemental-core/elementalcore/eth" 31 "github.com/Elemental-core/elementalcore/internal/ethapi" 32 "github.com/Elemental-core/elementalcore/les" 33 "github.com/Elemental-core/elementalcore/log" 34 "github.com/Elemental-core/elementalcore/node" 35 "github.com/Elemental-core/elementalcore/p2p" 36 "github.com/Elemental-core/elementalcore/rpc" 37 ) 38 39 // Interval to check for new releases 40 const releaseRecheckInterval = time.Hour 41 42 // Config contains the configurations of the release service. 43 type Config struct { 44 Oracle common.Address // Ethereum address of the release oracle 45 Major uint32 // Major version component of the release 46 Minor uint32 // Minor version component of the release 47 Patch uint32 // Patch version component of the release 48 Commit [20]byte // Git SHA1 commit hash of the release 49 } 50 51 // ReleaseService is a node service that periodically checks the blockchain for 52 // newly released versions of the client being run and issues a warning to the 53 // user about it. 54 type ReleaseService struct { 55 config Config // Current version to check releases against 56 oracle *ReleaseOracle // Native binding to the release oracle contract 57 quit chan chan error // Quit channel to terminate the version checker 58 } 59 60 // NewReleaseService creates a new service to periodically check for new client 61 // releases and notify the user of such. 62 func NewReleaseService(ctx *node.ServiceContext, config Config) (node.Service, error) { 63 // Retrieve the Ethereum service dependency to access the blockchain 64 var apiBackend ethapi.Backend 65 var ethereum *eth.Ethereum 66 if err := ctx.Service(ðereum); err == nil { 67 apiBackend = ethereum.ApiBackend 68 } else { 69 var ethereum *les.LightEthereum 70 if err := ctx.Service(ðereum); err == nil { 71 apiBackend = ethereum.ApiBackend 72 } else { 73 return nil, err 74 } 75 } 76 // Construct the release service 77 contract, err := NewReleaseOracle(config.Oracle, eth.NewContractBackend(apiBackend)) 78 if err != nil { 79 return nil, err 80 } 81 return &ReleaseService{ 82 config: config, 83 oracle: contract, 84 quit: make(chan chan error), 85 }, nil 86 } 87 88 // Protocols returns an empty list of P2P protocols as the release service does 89 // not have a networking component. 90 func (r *ReleaseService) Protocols() []p2p.Protocol { return nil } 91 92 // APIs returns an empty list of RPC descriptors as the release service does not 93 // expose any functioanlity to the outside world. 94 func (r *ReleaseService) APIs() []rpc.API { return nil } 95 96 // Start spawns the periodic version checker goroutine 97 func (r *ReleaseService) Start(server *p2p.Server) error { 98 go r.checker() 99 return nil 100 } 101 102 // Stop terminates all goroutines belonging to the service, blocking until they 103 // are all terminated. 104 func (r *ReleaseService) Stop() error { 105 errc := make(chan error) 106 r.quit <- errc 107 return <-errc 108 } 109 110 // checker runs indefinitely in the background, periodically checking for new 111 // client releases. 112 func (r *ReleaseService) checker() { 113 // Set up the timers to periodically check for releases 114 timer := time.NewTimer(0) // Immediately fire a version check 115 defer timer.Stop() 116 117 for { 118 select { 119 case <-timer.C: 120 // Rechedule the timer before continuing 121 timer.Reset(releaseRecheckInterval) 122 r.checkVersion() 123 case errc := <-r.quit: 124 errc <- nil 125 return 126 } 127 } 128 } 129 130 func (r *ReleaseService) checkVersion() { 131 // Retrieve the current version, and handle missing contracts gracefully 132 ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) 133 opts := &bind.CallOpts{Context: ctx} 134 defer cancel() 135 136 version, err := r.oracle.CurrentVersion(opts) 137 if err != nil { 138 if err == bind.ErrNoCode { 139 log.Debug("Release oracle not found", "contract", r.config.Oracle) 140 } else { 141 log.Error("Failed to retrieve current release", "err", err) 142 } 143 return 144 } 145 // Version was successfully retrieved, notify if newer than ours 146 if version.Major > r.config.Major || 147 (version.Major == r.config.Major && version.Minor > r.config.Minor) || 148 (version.Major == r.config.Major && version.Minor == r.config.Minor && version.Patch > r.config.Patch) { 149 150 warning := fmt.Sprintf("Client v%d.%d.%d-%x seems older than the latest upstream release v%d.%d.%d-%x", 151 r.config.Major, r.config.Minor, r.config.Patch, r.config.Commit[:4], version.Major, version.Minor, version.Patch, version.Commit[:4]) 152 howtofix := fmt.Sprintf("Please check https://github.com/Elemental-core/elementalcore/releases for new releases") 153 separator := strings.Repeat("-", len(warning)) 154 155 log.Warn(separator) 156 log.Warn(warning) 157 log.Warn(howtofix) 158 log.Warn(separator) 159 } else { 160 log.Debug("Client seems up to date with upstream", 161 "local", fmt.Sprintf("v%d.%d.%d-%x", r.config.Major, r.config.Minor, r.config.Patch, r.config.Commit[:4]), 162 "upstream", fmt.Sprintf("v%d.%d.%d-%x", version.Major, version.Minor, version.Patch, version.Commit[:4])) 163 } 164 }