github.com/Tri-stone/burrow@v0.25.0/core/kernel.go (about) 1 // Copyright 2017 Monax Industries Limited 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package core 16 17 import ( 18 "bytes" 19 "context" 20 "fmt" 21 "net" 22 _ "net/http/pprof" 23 "os" 24 "os/signal" 25 "sync" 26 "syscall" 27 "time" 28 29 "github.com/go-kit/kit/log" 30 "github.com/hyperledger/burrow/bcm" 31 "github.com/hyperledger/burrow/consensus/tendermint" 32 "github.com/hyperledger/burrow/crypto" 33 "github.com/hyperledger/burrow/event" 34 "github.com/hyperledger/burrow/execution" 35 "github.com/hyperledger/burrow/execution/state" 36 "github.com/hyperledger/burrow/genesis" 37 "github.com/hyperledger/burrow/keys" 38 "github.com/hyperledger/burrow/logging" 39 "github.com/hyperledger/burrow/logging/structure" 40 "github.com/hyperledger/burrow/process" 41 "github.com/hyperledger/burrow/rpc" 42 "github.com/hyperledger/burrow/txs" 43 "github.com/streadway/simpleuuid" 44 "github.com/tendermint/tendermint/blockchain" 45 dbm "github.com/tendermint/tendermint/libs/db" 46 tmTypes "github.com/tendermint/tendermint/types" 47 ) 48 49 const ( 50 CooldownTime = 1000 * time.Millisecond 51 ServerShutdownTimeout = 5000 * time.Millisecond 52 LoggingCallerDepth = 5 53 AccountsRingMutexCount = 100 54 BurrowDBName = "burrow_state" 55 ) 56 57 // Kernel is the root structure of Burrow 58 type Kernel struct { 59 // Expose these public-facing interfaces to allow programmatic extension of the Kernel by other projects 60 Emitter *event.Emitter 61 Service *rpc.Service 62 Launchers []process.Launcher 63 State *state.State 64 Blockchain *bcm.Blockchain 65 Node *tendermint.Node 66 Transactor *execution.Transactor 67 RunID simpleuuid.UUID // Time-based UUID randomly generated each time Burrow is started 68 Logger *logging.Logger 69 database dbm.DB 70 txCodec txs.Codec 71 exeOptions []execution.ExecutionOption 72 checker execution.BatchExecutor 73 committer execution.BatchCommitter 74 keyClient keys.KeyClient 75 keyStore *keys.KeyStore 76 info string 77 processes map[string]process.Process 78 listeners map[string]net.Listener 79 timeoutFactor float64 80 shutdownNotify chan struct{} 81 shutdownOnce sync.Once 82 } 83 84 // NewKernel initializes an empty kernel 85 func NewKernel(dbDir string) (*Kernel, error) { 86 if dbDir == "" { 87 return nil, fmt.Errorf("Burrow requires a database directory") 88 } 89 runID, err := simpleuuid.NewTime(time.Now()) // Create a random ID based on start time 90 return &Kernel{ 91 Logger: logging.NewNoopLogger(), 92 RunID: runID, 93 Emitter: event.NewEmitter(), 94 processes: make(map[string]process.Process), 95 listeners: make(map[string]net.Listener), 96 shutdownNotify: make(chan struct{}), 97 txCodec: txs.NewAminoCodec(), 98 database: dbm.NewDB(BurrowDBName, dbm.GoLevelDBBackend, dbDir), 99 }, err 100 } 101 102 // SetLogger initializes the kernel with the provided logger 103 func (kern *Kernel) SetLogger(logger *logging.Logger) { 104 logger = logger.WithScope("NewKernel()").With(structure.TimeKey, 105 log.DefaultTimestampUTC, structure.RunId, kern.RunID.String()) 106 heightValuer := log.Valuer(func() interface{} { return kern.Blockchain.LastBlockHeight() }) 107 kern.Logger = logger.WithInfo(structure.CallerKey, log.Caller(LoggingCallerDepth)).With("height", heightValuer) 108 kern.Emitter.SetLogger(logger) 109 } 110 111 // LoadState starts from scratch or previous chain 112 func (kern *Kernel) LoadState(genesisDoc *genesis.GenesisDoc) (err error) { 113 var existing bool 114 existing, kern.Blockchain, err = bcm.LoadOrNewBlockchain(kern.database, genesisDoc, kern.Logger) 115 if err != nil { 116 return fmt.Errorf("error creating or loading blockchain state: %v", err) 117 } 118 119 if existing { 120 kern.Logger.InfoMsg("Loading application state", "height", kern.Blockchain.LastBlockHeight()) 121 kern.State, err = state.LoadState(kern.database, execution.VersionAtHeight(kern.Blockchain.LastBlockHeight())) 122 if err != nil { 123 return fmt.Errorf("could not load persisted execution state at hash 0x%X: %v", 124 kern.Blockchain.AppHashAfterLastBlock(), err) 125 } 126 127 if !bytes.Equal(kern.State.Hash(), kern.Blockchain.AppHashAfterLastBlock()) { 128 return fmt.Errorf("state and blockchain disagree on app hash at height %d: "+ 129 "state gives %X, blockchain gives %X", kern.Blockchain.LastBlockHeight(), 130 kern.State.Hash(), kern.Blockchain.AppHashAfterLastBlock()) 131 } 132 133 } else { 134 kern.Logger.InfoMsg("Creating new application state from genesis") 135 kern.State, err = state.MakeGenesisState(kern.database, genesisDoc) 136 if err != nil { 137 return fmt.Errorf("could not build genesis state: %v", err) 138 } 139 140 if err = kern.State.InitialCommit(); err != nil { 141 return err 142 } 143 } 144 145 kern.Logger.InfoMsg("State loading successful") 146 147 params := execution.ParamsFromGenesis(genesisDoc) 148 kern.checker = execution.NewBatchChecker(kern.State, params, kern.Blockchain, kern.Logger) 149 kern.committer = execution.NewBatchCommitter(kern.State, params, kern.Blockchain, kern.Emitter, kern.Logger, kern.exeOptions...) 150 return nil 151 } 152 153 // LoadDump restores chain state from the given dump file 154 func (kern *Kernel) LoadDump(genesisDoc *genesis.GenesisDoc, restoreFile string) (err error) { 155 if _, kern.Blockchain, err = bcm.LoadOrNewBlockchain(kern.database, genesisDoc, kern.Logger); err != nil { 156 return fmt.Errorf("error creating or loading blockchain state: %v", err) 157 } 158 kern.Blockchain.SetBlockStore(bcm.NewBlockStore(blockchain.NewBlockStore(kern.database))) 159 160 if kern.State, err = state.MakeGenesisState(kern.database, genesisDoc); err != nil { 161 return fmt.Errorf("could not build genesis state: %v", err) 162 } 163 164 if len(genesisDoc.AppHash) == 0 { 165 return fmt.Errorf("AppHash is required when restoring chain") 166 } 167 168 reader, err := state.NewFileDumpReader(restoreFile) 169 if err != nil { 170 return err 171 } 172 173 if err = kern.State.LoadDump(reader); err != nil { 174 return err 175 } 176 177 if err = kern.State.InitialCommit(); err != nil { 178 return err 179 } 180 181 if !bytes.Equal(kern.State.Hash(), kern.Blockchain.GenesisDoc().AppHash) { 182 return fmt.Errorf("Restore produced a different apphash expect 0x%x got 0x%x", 183 kern.Blockchain.GenesisDoc().AppHash, kern.State.Hash()) 184 } 185 err = kern.Blockchain.CommitWithAppHash(kern.State.Hash()) 186 if err != nil { 187 return fmt.Errorf("Unable to commit %v", err) 188 } 189 190 kern.Logger.InfoMsg("State restore successful: %d", kern.Blockchain.LastBlockHeight()) 191 return nil 192 } 193 194 // GetNodeView builds and returns a wrapper of our tendermint node 195 func (kern *Kernel) GetNodeView() (*tendermint.NodeView, error) { 196 if kern.Node == nil { 197 return nil, nil 198 } 199 return tendermint.NewNodeView(kern.Node, kern.txCodec, kern.RunID) 200 } 201 202 // AddExecutionOptions extends our execution options 203 func (kern *Kernel) AddExecutionOptions(opts ...execution.ExecutionOption) { 204 kern.exeOptions = append(kern.exeOptions, opts...) 205 } 206 207 // AddProcesses extends the services that we launch at boot 208 func (kern *Kernel) AddProcesses(pl ...process.Launcher) { 209 kern.Launchers = append(kern.Launchers, pl...) 210 } 211 212 // SetKeyClient explicitly sets the key client 213 func (kern *Kernel) SetKeyClient(client keys.KeyClient) { 214 kern.keyClient = client 215 } 216 217 // SetKeyStore explicitly sets the key store 218 func (kern *Kernel) SetKeyStore(store *keys.KeyStore) { 219 kern.keyStore = store 220 } 221 222 // Generates an in-memory Tendermint PrivValidator (suitable for passing to LoadTendermintFromConfig) 223 func (kern *Kernel) PrivValidator(validator crypto.Address) (tmTypes.PrivValidator, error) { 224 val, err := keys.AddressableSigner(kern.keyClient, validator) 225 if err != nil { 226 return nil, fmt.Errorf("could not get validator addressable from keys client: %v", err) 227 } 228 signer, err := keys.AddressableSigner(kern.keyClient, val.GetAddress()) 229 if err != nil { 230 return nil, err 231 } 232 return tendermint.NewPrivValidatorMemory(val, signer), nil 233 } 234 235 // Boot the kernel starting Tendermint and RPC layers 236 func (kern *Kernel) Boot() (err error) { 237 for _, launcher := range kern.Launchers { 238 if launcher.Enabled { 239 srvr, err := launcher.Launch() 240 if err != nil { 241 return fmt.Errorf("error launching %s server: %v", launcher.Name, err) 242 } 243 244 kern.processes[launcher.Name] = srvr 245 } 246 } 247 go kern.supervise() 248 return nil 249 } 250 251 func (kern *Kernel) Panic(err error) { 252 fmt.Fprintf(os.Stderr, "%v: shutting down due to panic: %v", kern, err) 253 kern.ShutdownAndExit() 254 } 255 256 // Wait for a graceful shutdown 257 func (kern *Kernel) WaitForShutdown() { 258 // Supports multiple goroutines waiting for shutdown since channel is closed 259 <-kern.shutdownNotify 260 } 261 262 func (kern *Kernel) registerListener(name string, listener net.Listener) error { 263 _, ok := kern.listeners[name] 264 if ok { 265 return fmt.Errorf("registerListener(): listener '%s' already registered", name) 266 } 267 kern.listeners[name] = listener 268 return nil 269 } 270 271 func (kern *Kernel) GRPCListenAddress() net.Addr { 272 l, ok := kern.listeners[GRPCProcessName] 273 if !ok { 274 return nil 275 } 276 return l.Addr() 277 } 278 279 func (kern *Kernel) InfoListenAddress() net.Addr { 280 l, ok := kern.listeners[InfoProcessName] 281 if !ok { 282 return nil 283 } 284 return l.Addr() 285 } 286 287 func (kern *Kernel) MetricsListenAddress() net.Addr { 288 l, ok := kern.listeners[MetricsProcessName] 289 if !ok { 290 return nil 291 } 292 return l.Addr() 293 } 294 295 func (kern *Kernel) String() string { 296 return fmt.Sprintf("Kernel[%s]", kern.info) 297 } 298 299 // Supervise kernel once booted 300 func (kern *Kernel) supervise() { 301 // perform disaster restarts of the kernel; rejoining the network as if we were a new node. 302 shutdownCh := make(chan os.Signal, 1) 303 reloadCh := make(chan os.Signal, 1) 304 syncCh := make(chan os.Signal, 1) 305 signal.Notify(shutdownCh, syscall.SIGINT, syscall.SIGTERM, syscall.SIGKILL) 306 signal.Notify(reloadCh, syscall.SIGHUP) 307 signal.Notify(syncCh, syscall.SIGUSR1) 308 for { 309 select { 310 case <-reloadCh: 311 err := kern.Logger.Reload() 312 if err != nil { 313 fmt.Fprintf(os.Stderr, "%v: could not reload logger: %v", kern, err) 314 } 315 case <-syncCh: 316 err := kern.Logger.Sync() 317 if err != nil { 318 fmt.Fprintf(os.Stderr, "%v: could not sync logger: %v", kern, err) 319 } 320 case sig := <-shutdownCh: 321 kern.Logger.InfoMsg(fmt.Sprintf("Caught %v signal so shutting down", sig), 322 "signal", sig.String()) 323 kern.ShutdownAndExit() 324 return 325 } 326 } 327 } 328 329 func (kern *Kernel) ShutdownAndExit() { 330 ctx, cancel := context.WithTimeout(context.Background(), ServerShutdownTimeout) 331 defer cancel() 332 err := kern.Shutdown(ctx) 333 if err != nil { 334 fmt.Fprintf(os.Stderr, "%v: error shutting down: %v", kern, err) 335 os.Exit(1) 336 } 337 os.Exit(0) 338 } 339 340 // Shutdown stops the kernel allowing for a graceful shutdown of components in order 341 func (kern *Kernel) Shutdown(ctx context.Context) (err error) { 342 kern.shutdownOnce.Do(func() { 343 logger := kern.Logger.WithScope("Shutdown") 344 logger.InfoMsg("Attempting graceful shutdown...") 345 logger.InfoMsg("Shutting down servers") 346 // Shutdown servers in reverse order to boot 347 for i := len(kern.Launchers) - 1; i >= 0; i-- { 348 name := kern.Launchers[i].Name 349 proc, ok := kern.processes[name] 350 if ok { 351 logger.InfoMsg("Shutting down server", "server_name", name) 352 sErr := proc.Shutdown(ctx) 353 if sErr != nil { 354 logger.InfoMsg("Failed to shutdown server", 355 "server_name", name, 356 structure.ErrorKey, sErr) 357 if err == nil { 358 err = sErr 359 } 360 } 361 } 362 } 363 logger.InfoMsg("Shutdown complete") 364 // Best effort 365 structure.Sync(kern.Logger.Info) 366 structure.Sync(kern.Logger.Trace) 367 // We don't want to wait for them, but yielding for a cooldown Let other goroutines flush 368 // potentially interesting final output (e.g. log messages) 369 time.Sleep(CooldownTime) 370 close(kern.shutdownNotify) 371 }) 372 return 373 }