github.com/sbrajchuk/go-ethereum@v1.9.7/cmd/utils/cmd.go (about)

     1  // Copyright 2014 The go-ethereum Authors
     2  // This file is part of go-ethereum.
     3  //
     4  // go-ethereum 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  // go-ethereum 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 go-ethereum. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  // Package utils contains internal helper functions for go-ethereum 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  	"github.com/ethereum/go-ethereum/common"
    31  	"github.com/ethereum/go-ethereum/core"
    32  	"github.com/ethereum/go-ethereum/core/rawdb"
    33  	"github.com/ethereum/go-ethereum/core/types"
    34  	"github.com/ethereum/go-ethereum/crypto"
    35  	"github.com/ethereum/go-ethereum/ethdb"
    36  	"github.com/ethereum/go-ethereum/internal/debug"
    37  	"github.com/ethereum/go-ethereum/log"
    38  	"github.com/ethereum/go-ethereum/node"
    39  	"github.com/ethereum/go-ethereum/rlp"
    40  )
    41  
    42  const (
    43  	importBatchSize = 2500
    44  )
    45  
    46  // Fatalf formats a message to standard error and exits the program.
    47  // The message is also printed to standard output if standard error
    48  // is redirected to a different file.
    49  func Fatalf(format string, args ...interface{}) {
    50  	w := io.MultiWriter(os.Stdout, os.Stderr)
    51  	if runtime.GOOS == "windows" {
    52  		// The SameFile check below doesn't work on Windows.
    53  		// stdout is unlikely to get redirected though, so just print there.
    54  		w = os.Stdout
    55  	} else {
    56  		outf, _ := os.Stdout.Stat()
    57  		errf, _ := os.Stderr.Stat()
    58  		if outf != nil && errf != nil && os.SameFile(outf, errf) {
    59  			w = os.Stderr
    60  		}
    61  	}
    62  	fmt.Fprintf(w, "Fatal: "+format+"\n", args...)
    63  	os.Exit(1)
    64  }
    65  
    66  func StartNode(stack *node.Node) {
    67  	if err := stack.Start(); err != nil {
    68  		Fatalf("Error starting protocol stack: %v", err)
    69  	}
    70  	go func() {
    71  		sigc := make(chan os.Signal, 1)
    72  		signal.Notify(sigc, syscall.SIGINT, syscall.SIGTERM)
    73  		defer signal.Stop(sigc)
    74  		<-sigc
    75  		log.Info("Got interrupt, shutting down...")
    76  		go stack.Stop()
    77  		for i := 10; i > 0; i-- {
    78  			<-sigc
    79  			if i > 1 {
    80  				log.Warn("Already shutting down, interrupt more to panic.", "times", i-1)
    81  			}
    82  		}
    83  		debug.Exit() // ensure trace and CPU profile data is flushed.
    84  		debug.LoudPanic("boom")
    85  	}()
    86  }
    87  
    88  func ImportChain(chain *core.BlockChain, fn string) error {
    89  	// Watch for Ctrl-C while the import is running.
    90  	// If a signal is received, the import will stop at the next batch.
    91  	interrupt := make(chan os.Signal, 1)
    92  	stop := make(chan struct{})
    93  	signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM)
    94  	defer signal.Stop(interrupt)
    95  	defer close(interrupt)
    96  	go func() {
    97  		if _, ok := <-interrupt; ok {
    98  			log.Info("Interrupted during import, stopping at next batch")
    99  		}
   100  		close(stop)
   101  	}()
   102  	checkInterrupt := func() bool {
   103  		select {
   104  		case <-stop:
   105  			return true
   106  		default:
   107  			return false
   108  		}
   109  	}
   110  
   111  	log.Info("Importing blockchain", "file", fn)
   112  
   113  	// Open the file handle and potentially unwrap the gzip stream
   114  	fh, err := os.Open(fn)
   115  	if err != nil {
   116  		return err
   117  	}
   118  	defer fh.Close()
   119  
   120  	var reader io.Reader = fh
   121  	if strings.HasSuffix(fn, ".gz") {
   122  		if reader, err = gzip.NewReader(reader); err != nil {
   123  			return err
   124  		}
   125  	}
   126  	stream := rlp.NewStream(reader, 0)
   127  
   128  	// Run actual the import.
   129  	blocks := make(types.Blocks, importBatchSize)
   130  	n := 0
   131  	for batch := 0; ; batch++ {
   132  		// Load a batch of RLP blocks.
   133  		if checkInterrupt() {
   134  			return fmt.Errorf("interrupted")
   135  		}
   136  		i := 0
   137  		for ; i < importBatchSize; i++ {
   138  			var b types.Block
   139  			if err := stream.Decode(&b); err == io.EOF {
   140  				break
   141  			} else if err != nil {
   142  				return fmt.Errorf("at block %d: %v", n, err)
   143  			}
   144  			// don't import first block
   145  			if b.NumberU64() == 0 {
   146  				i--
   147  				continue
   148  			}
   149  			blocks[i] = &b
   150  			n++
   151  		}
   152  		if i == 0 {
   153  			break
   154  		}
   155  		// Import the batch.
   156  		if checkInterrupt() {
   157  			return fmt.Errorf("interrupted")
   158  		}
   159  		missing := missingBlocks(chain, blocks[:i])
   160  		if len(missing) == 0 {
   161  			log.Info("Skipping batch as all blocks present", "batch", batch, "first", blocks[0].Hash(), "last", blocks[i-1].Hash())
   162  			continue
   163  		}
   164  		if _, err := chain.InsertChain(missing); err != nil {
   165  			return fmt.Errorf("invalid block %d: %v", n, err)
   166  		}
   167  	}
   168  	return nil
   169  }
   170  
   171  func missingBlocks(chain *core.BlockChain, blocks []*types.Block) []*types.Block {
   172  	head := chain.CurrentBlock()
   173  	for i, block := range blocks {
   174  		// If we're behind the chain head, only check block, state is available at head
   175  		if head.NumberU64() > block.NumberU64() {
   176  			if !chain.HasBlock(block.Hash(), block.NumberU64()) {
   177  				return blocks[i:]
   178  			}
   179  			continue
   180  		}
   181  		// If we're above the chain head, state availability is a must
   182  		if !chain.HasBlockAndState(block.Hash(), block.NumberU64()) {
   183  			return blocks[i:]
   184  		}
   185  	}
   186  	return nil
   187  }
   188  
   189  // ExportChain exports a blockchain into the specified file, truncating any data
   190  // already present in the file.
   191  func ExportChain(blockchain *core.BlockChain, fn string) error {
   192  	log.Info("Exporting blockchain", "file", fn)
   193  
   194  	// Open the file handle and potentially wrap with a gzip stream
   195  	fh, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm)
   196  	if err != nil {
   197  		return err
   198  	}
   199  	defer fh.Close()
   200  
   201  	var writer io.Writer = fh
   202  	if strings.HasSuffix(fn, ".gz") {
   203  		writer = gzip.NewWriter(writer)
   204  		defer writer.(*gzip.Writer).Close()
   205  	}
   206  	// Iterate over the blocks and export them
   207  	if err := blockchain.Export(writer); err != nil {
   208  		return err
   209  	}
   210  	log.Info("Exported blockchain", "file", fn)
   211  
   212  	return nil
   213  }
   214  
   215  // ExportAppendChain exports a blockchain into the specified file, appending to
   216  // the file if data already exists in it.
   217  func ExportAppendChain(blockchain *core.BlockChain, fn string, first uint64, last uint64) error {
   218  	log.Info("Exporting blockchain", "file", fn)
   219  
   220  	// Open the file handle and potentially wrap with a gzip stream
   221  	fh, err := os.OpenFile(fn, os.O_CREATE|os.O_APPEND|os.O_WRONLY, os.ModePerm)
   222  	if err != nil {
   223  		return err
   224  	}
   225  	defer fh.Close()
   226  
   227  	var writer io.Writer = fh
   228  	if strings.HasSuffix(fn, ".gz") {
   229  		writer = gzip.NewWriter(writer)
   230  		defer writer.(*gzip.Writer).Close()
   231  	}
   232  	// Iterate over the blocks and export them
   233  	if err := blockchain.ExportN(writer, first, last); err != nil {
   234  		return err
   235  	}
   236  	log.Info("Exported blockchain to", "file", fn)
   237  	return nil
   238  }
   239  
   240  // ImportPreimages imports a batch of exported hash preimages into the database.
   241  func ImportPreimages(db ethdb.Database, fn string) error {
   242  	log.Info("Importing preimages", "file", fn)
   243  
   244  	// Open the file handle and potentially unwrap the gzip stream
   245  	fh, err := os.Open(fn)
   246  	if err != nil {
   247  		return err
   248  	}
   249  	defer fh.Close()
   250  
   251  	var reader io.Reader = fh
   252  	if strings.HasSuffix(fn, ".gz") {
   253  		if reader, err = gzip.NewReader(reader); err != nil {
   254  			return err
   255  		}
   256  	}
   257  	stream := rlp.NewStream(reader, 0)
   258  
   259  	// Import the preimages in batches to prevent disk trashing
   260  	preimages := make(map[common.Hash][]byte)
   261  
   262  	for {
   263  		// Read the next entry and ensure it's not junk
   264  		var blob []byte
   265  
   266  		if err := stream.Decode(&blob); err != nil {
   267  			if err == io.EOF {
   268  				break
   269  			}
   270  			return err
   271  		}
   272  		// Accumulate the preimages and flush when enough ws gathered
   273  		preimages[crypto.Keccak256Hash(blob)] = common.CopyBytes(blob)
   274  		if len(preimages) > 1024 {
   275  			rawdb.WritePreimages(db, preimages)
   276  			preimages = make(map[common.Hash][]byte)
   277  		}
   278  	}
   279  	// Flush the last batch preimage data
   280  	if len(preimages) > 0 {
   281  		rawdb.WritePreimages(db, preimages)
   282  	}
   283  	return nil
   284  }
   285  
   286  // ExportPreimages exports all known hash preimages into the specified file,
   287  // truncating any data already present in the file.
   288  func ExportPreimages(db ethdb.Database, fn string) error {
   289  	log.Info("Exporting preimages", "file", fn)
   290  
   291  	// Open the file handle and potentially wrap with a gzip stream
   292  	fh, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm)
   293  	if err != nil {
   294  		return err
   295  	}
   296  	defer fh.Close()
   297  
   298  	var writer io.Writer = fh
   299  	if strings.HasSuffix(fn, ".gz") {
   300  		writer = gzip.NewWriter(writer)
   301  		defer writer.(*gzip.Writer).Close()
   302  	}
   303  	// Iterate over the preimages and export them
   304  	it := db.NewIteratorWithPrefix([]byte("secure-key-"))
   305  	defer it.Release()
   306  
   307  	for it.Next() {
   308  		if err := rlp.Encode(writer, it.Value()); err != nil {
   309  			return err
   310  		}
   311  	}
   312  	log.Info("Exported preimages", "file", fn)
   313  	return nil
   314  }