gitlab.com/aquachain/aquachain@v1.17.16-rc3.0.20221018032414-e3ddf1e1c055/cmd/utils/cmd.go (about)

     1  // Copyright 2018 The aquachain Authors
     2  // This file is part of aquachain.
     3  //
     4  // aquachain is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU 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  // aquachain 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 General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU General Public License
    15  // along with aquachain. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  // Package utils contains internal helper functions for aquachain commands.
    18  package utils
    19  
    20  import (
    21  	"compress/gzip"
    22  	"fmt"
    23  	"io"
    24  	"os"
    25  	"os/signal"
    26  	"runtime"
    27  	"strings"
    28  	"syscall"
    29  
    30  	"gitlab.com/aquachain/aquachain/common"
    31  	"gitlab.com/aquachain/aquachain/common/log"
    32  	"gitlab.com/aquachain/aquachain/core"
    33  	"gitlab.com/aquachain/aquachain/core/types"
    34  	"gitlab.com/aquachain/aquachain/internal/debug"
    35  	"gitlab.com/aquachain/aquachain/node"
    36  	"gitlab.com/aquachain/aquachain/rlp"
    37  )
    38  
    39  const (
    40  	importBatchSize = 2500
    41  )
    42  
    43  // Fatalf formats a message to standard error and exits the program.
    44  // The message is also printed to standard output if standard error
    45  // is redirected to a different file.
    46  func Fatalf(format string, args ...interface{}) {
    47  	w := io.MultiWriter(os.Stdout, os.Stderr)
    48  	if runtime.GOOS == "windows" {
    49  		// The SameFile check below doesn't work on Windows.
    50  		// stdout is unlikely to get redirected though, so just print there.
    51  		w = os.Stdout
    52  	} else {
    53  		outf, _ := os.Stdout.Stat()
    54  		errf, _ := os.Stderr.Stat()
    55  		if outf != nil && errf != nil && os.SameFile(outf, errf) {
    56  			w = os.Stderr
    57  		}
    58  	}
    59  	fmt.Fprintf(w, "Fatal: "+format+"\n", args...)
    60  
    61  	// small traceback
    62  	if debug := os.Getenv("DEBUG"); debug != "" && debug != "0" {
    63  		pc := make([]uintptr, 5)
    64  		n := runtime.Callers(1, pc)
    65  		if n != 0 {
    66  			pc = pc[:n]
    67  			frames := runtime.CallersFrames(pc)
    68  			for {
    69  				frame, more := frames.Next()
    70  				fmt.Fprintf(w, "\t >%s:%d %s\n", frame.File, frame.Line,
    71  					frame.Function)
    72  				if !more {
    73  					break
    74  				}
    75  			}
    76  		}
    77  
    78  	}
    79  	os.Exit(111)
    80  }
    81  
    82  func StartNode(stack *node.Node) {
    83  	if err := stack.Start(); err != nil {
    84  		Fatalf("Error starting protocol stack: %v", err)
    85  	}
    86  	go func() {
    87  		sigc := make(chan os.Signal, 1)
    88  		signal.Notify(sigc, syscall.SIGINT, syscall.SIGTERM)
    89  		defer signal.Stop(sigc)
    90  		<-sigc
    91  		log.Info("Got interrupt, shutting down...")
    92  		go stack.Stop()
    93  		for i := 10; i > 0; i-- {
    94  			<-sigc
    95  			if i > 1 {
    96  				log.Warn("Already shutting down, interrupt more to panic.", "times", i-1)
    97  			}
    98  		}
    99  		debug.Exit() // ensure trace and CPU profile data is flushed.
   100  		debug.LoudPanic("boom")
   101  	}()
   102  }
   103  
   104  func ImportChain(chain *core.BlockChain, fn string) error {
   105  	// Watch for Ctrl-C while the import is running.
   106  	// If a signal is received, the import will stop at the next batch.
   107  	interrupt := make(chan os.Signal, 1)
   108  	stop := make(chan struct{})
   109  	signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM)
   110  	defer signal.Stop(interrupt)
   111  	defer close(interrupt)
   112  	go func() {
   113  		if _, ok := <-interrupt; ok {
   114  			log.Info("Interrupted during import, stopping at next batch")
   115  		}
   116  		close(stop)
   117  	}()
   118  	checkInterrupt := func() bool {
   119  		select {
   120  		case <-stop:
   121  			return true
   122  		default:
   123  			return false
   124  		}
   125  	}
   126  
   127  	log.Info("Importing blockchain", "file", fn)
   128  	fh, err := os.Open(fn)
   129  	if err != nil {
   130  		return err
   131  	}
   132  	defer fh.Close()
   133  
   134  	var reader io.Reader = fh
   135  	if strings.HasSuffix(fn, ".gz") {
   136  		if reader, err = gzip.NewReader(reader); err != nil {
   137  			return err
   138  		}
   139  	}
   140  	stream := rlp.NewStream(reader, 0)
   141  
   142  	// Run actual the import.
   143  	blocks := make(types.Blocks, importBatchSize)
   144  	n := 0
   145  	for batch := 0; ; batch++ {
   146  		// Load a batch of RLP blocks.
   147  		if checkInterrupt() {
   148  			return fmt.Errorf("interrupted")
   149  		}
   150  		i := 0
   151  		for ; i < importBatchSize; i++ {
   152  			var b types.Block
   153  			if err := stream.Decode(&b); err == io.EOF {
   154  				break
   155  			} else if err != nil {
   156  				return fmt.Errorf("at block %d: %v", n, err)
   157  			}
   158  			// don't import first block
   159  			if b.NumberU64() == 0 {
   160  				i--
   161  				continue
   162  			}
   163  			blocks[i] = &b
   164  			n++
   165  		}
   166  		if i == 0 {
   167  			break
   168  		}
   169  		// Import the batch.
   170  		if checkInterrupt() {
   171  			return fmt.Errorf("interrupted")
   172  		}
   173  		missing := missingBlocks(chain, blocks[:i])
   174  		if len(missing) == 0 {
   175  			log.Info("Skipping batch as all blocks present", "batch", batch, "first", blocks[0].Hash(), "last", blocks[i-1].Hash(), "blocknumber", blocks[i-1].Number(), "date", common.FormatTimestamp(blocks[i-1].Time().Uint64()))
   176  			continue
   177  		}
   178  		if _, err := chain.InsertChain(missing); err != nil {
   179  			return fmt.Errorf("%v", err)
   180  		}
   181  	}
   182  	return nil
   183  }
   184  
   185  func missingBlocks(chain *core.BlockChain, blocks []*types.Block) []*types.Block {
   186  	head := chain.CurrentBlock()
   187  	for i, block := range blocks {
   188  		// If we're behind the chain head, only check block, state is available at head
   189  		if head.NumberU64() > block.NumberU64() {
   190  			if !chain.HasBlock(block.SetVersion(chain.Config().GetBlockVersion(block.Number())), block.NumberU64()) {
   191  				return blocks[i:]
   192  			}
   193  			continue
   194  		}
   195  
   196  		// If we're above the chain head, state availability is a must
   197  		if !chain.HasBlockAndState(block.SetVersion(chain.Config().GetBlockVersion(block.Number())), block.NumberU64()) {
   198  			return blocks[i:]
   199  		}
   200  	}
   201  	return nil
   202  }
   203  
   204  func ExportChain(blockchain *core.BlockChain, fn string) error {
   205  	log.Info("Exporting blockchain", "file", fn)
   206  	fh, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm)
   207  	if err != nil {
   208  		return err
   209  	}
   210  	defer fh.Close()
   211  
   212  	var writer io.Writer = fh
   213  	if strings.HasSuffix(fn, ".gz") {
   214  		writer = gzip.NewWriter(writer)
   215  		defer writer.(*gzip.Writer).Close()
   216  	}
   217  
   218  	if err := blockchain.Export(writer); err != nil {
   219  		return err
   220  	}
   221  	log.Info("Exported blockchain", "file", fn)
   222  
   223  	return nil
   224  }
   225  
   226  func ExportAppendChain(blockchain *core.BlockChain, fn string, first uint64, last uint64) error {
   227  	log.Info("Exporting blockchain", "file", fn)
   228  	// TODO verify mode perms
   229  	fh, err := os.OpenFile(fn, os.O_CREATE|os.O_APPEND|os.O_WRONLY, os.ModePerm)
   230  	if err != nil {
   231  		return err
   232  	}
   233  	defer fh.Close()
   234  
   235  	var writer io.Writer = fh
   236  	if strings.HasSuffix(fn, ".gz") {
   237  		writer = gzip.NewWriter(writer)
   238  		defer writer.(*gzip.Writer).Close()
   239  	}
   240  
   241  	if err := blockchain.ExportN(writer, first, last); err != nil {
   242  		return err
   243  	}
   244  	log.Info("Exported blockchain to", "file", fn)
   245  	return nil
   246  }