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  }