github.com/unicornultrafoundation/go-u2u@v1.0.0-rc1.0.20240205080301-e74a83d3fadc/cmd/u2u/launcher/import.go (about)

     1  package launcher
     2  
     3  import (
     4  	"bytes"
     5  	"compress/gzip"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  	"math"
    10  	"os"
    11  	"os/signal"
    12  	"strings"
    13  	"syscall"
    14  	"time"
    15  
    16  	"github.com/status-im/keycard-go/hexutils"
    17  	"github.com/unicornultrafoundation/go-helios/hash"
    18  	"github.com/unicornultrafoundation/go-helios/native/idx"
    19  	"github.com/unicornultrafoundation/go-u2u/cmd/utils"
    20  	"github.com/unicornultrafoundation/go-u2u/common"
    21  	"github.com/unicornultrafoundation/go-u2u/log"
    22  	"github.com/unicornultrafoundation/go-u2u/rlp"
    23  	"gopkg.in/urfave/cli.v1"
    24  
    25  	"github.com/unicornultrafoundation/go-u2u/gossip"
    26  	"github.com/unicornultrafoundation/go-u2u/gossip/emitter"
    27  	"github.com/unicornultrafoundation/go-u2u/native"
    28  	"github.com/unicornultrafoundation/go-u2u/u2u/genesisstore"
    29  	"github.com/unicornultrafoundation/go-u2u/utils/ioread"
    30  )
    31  
    32  func importEvm(ctx *cli.Context) error {
    33  	if len(ctx.Args()) < 1 {
    34  		utils.Fatalf("This command requires an argument.")
    35  	}
    36  
    37  	cfg := makeAllConfigs(ctx)
    38  
    39  	rawDbs := makeDirectDBsProducer(cfg)
    40  	gdb := makeGossipStore(rawDbs, cfg)
    41  	defer gdb.Close()
    42  
    43  	for _, fn := range ctx.Args() {
    44  		log.Info("Importing EVM storage from file", "file", fn)
    45  		if err := importEvmFile(fn, gdb); err != nil {
    46  			log.Error("Import error", "file", fn, "err", err)
    47  			return err
    48  		}
    49  		log.Info("Imported EVM storage from file", "file", fn)
    50  	}
    51  
    52  	return nil
    53  }
    54  
    55  func importEvmFile(fn string, gdb *gossip.Store) error {
    56  	// Open the file handle and potentially unwrap the gzip stream
    57  	fh, err := os.Open(fn)
    58  	if err != nil {
    59  		return err
    60  	}
    61  	defer fh.Close()
    62  
    63  	var reader io.Reader = fh
    64  	if strings.HasSuffix(fn, ".gz") {
    65  		if reader, err = gzip.NewReader(reader); err != nil {
    66  			return err
    67  		}
    68  		defer reader.(*gzip.Reader).Close()
    69  	}
    70  
    71  	return gdb.EvmStore().ImportEvm(reader)
    72  }
    73  
    74  func importEvents(ctx *cli.Context) error {
    75  	if len(ctx.Args()) < 1 {
    76  		utils.Fatalf("This command requires an argument.")
    77  	}
    78  
    79  	// avoid P2P interaction, API calls and events emitting
    80  	genesisStore := mayGetGenesisStore(ctx)
    81  	cfg := makeAllConfigs(ctx)
    82  	cfg.U2U.Protocol.EventsSemaphoreLimit.Size = math.MaxUint32
    83  	cfg.U2U.Protocol.EventsSemaphoreLimit.Num = math.MaxUint32
    84  	cfg.Emitter.Validator = emitter.ValidatorConfig{}
    85  	cfg.TxPool.Journal = ""
    86  	cfg.Node.IPCPath = ""
    87  	cfg.Node.HTTPHost = ""
    88  	cfg.Node.WSHost = ""
    89  	cfg.Node.NoUSB = true
    90  	cfg.Node.P2P.ListenAddr = ""
    91  	cfg.Node.P2P.NoDiscovery = true
    92  	cfg.Node.P2P.BootstrapNodes = nil
    93  	cfg.Node.P2P.DiscoveryV5 = false
    94  	cfg.Node.P2P.BootstrapNodesV5 = nil
    95  	cfg.Node.P2P.StaticNodes = nil
    96  	cfg.Node.P2P.TrustedNodes = nil
    97  
    98  	err := importEventsToNode(ctx, cfg, genesisStore, ctx.Args()...)
    99  	if err != nil {
   100  		return err
   101  	}
   102  
   103  	return nil
   104  }
   105  
   106  func importEventsToNode(ctx *cli.Context, cfg *config, genesisStore *genesisstore.Store, args ...string) error {
   107  	node, svc, nodeClose := makeNode(ctx, cfg, genesisStore)
   108  	defer nodeClose()
   109  	startNode(ctx, node)
   110  
   111  	for _, fn := range args {
   112  		log.Info("Importing events from file", "file", fn)
   113  		if err := importEventsFile(svc, fn); err != nil {
   114  			log.Error("Import error", "file", fn, "err", err)
   115  			return err
   116  		}
   117  	}
   118  	return nil
   119  }
   120  
   121  func checkEventsFileHeader(reader io.Reader) error {
   122  	headerAndVersion := make([]byte, len(eventsFileHeader)+len(eventsFileVersion))
   123  	err := ioread.ReadAll(reader, headerAndVersion)
   124  	if err != nil {
   125  		return err
   126  	}
   127  	if bytes.Compare(headerAndVersion[:len(eventsFileHeader)], eventsFileHeader) != 0 {
   128  		return errors.New("expected an events file, mismatched file header")
   129  	}
   130  	if bytes.Compare(headerAndVersion[len(eventsFileHeader):], eventsFileVersion) != 0 {
   131  		got := hexutils.BytesToHex(headerAndVersion[len(eventsFileHeader):])
   132  		expected := hexutils.BytesToHex(eventsFileVersion)
   133  		return errors.New(fmt.Sprintf("wrong version of events file, got=%s, expected=%s", got, expected))
   134  	}
   135  	return nil
   136  }
   137  
   138  func importEventsFile(srv *gossip.Service, fn string) error {
   139  	// Watch for Ctrl-C while the import is running.
   140  	// If a signal is received, the import will stop.
   141  	interrupt := make(chan os.Signal, 1)
   142  	signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM)
   143  	defer signal.Stop(interrupt)
   144  
   145  	// wait until snapshot generation is complete
   146  	for srv.EvmSnapshotGeneration() {
   147  		select {
   148  		case <-interrupt:
   149  			return fmt.Errorf("interrupted")
   150  		case <-time.After(100 * time.Millisecond):
   151  			continue
   152  		}
   153  	}
   154  
   155  	// Open the file handle and potentially unwrap the gzip stream
   156  	fh, err := os.Open(fn)
   157  	if err != nil {
   158  		return err
   159  	}
   160  	defer fh.Close()
   161  
   162  	var reader io.Reader = fh
   163  	if strings.HasSuffix(fn, ".gz") {
   164  		if reader, err = gzip.NewReader(reader); err != nil {
   165  			return err
   166  		}
   167  		defer reader.(*gzip.Reader).Close()
   168  	}
   169  
   170  	// Check file version and header
   171  	if err := checkEventsFileHeader(reader); err != nil {
   172  		return err
   173  	}
   174  
   175  	stream := rlp.NewStream(reader, 0)
   176  
   177  	start := time.Now()
   178  	last := hash.Event{}
   179  
   180  	batch := make(native.EventPayloads, 0, 8*1024)
   181  	batchSize := 0
   182  	maxBatchSize := 8 * 1024 * 1024
   183  	epoch := idx.Epoch(0)
   184  	txs := 0
   185  	events := 0
   186  
   187  	processBatch := func() error {
   188  		if batch.Len() == 0 {
   189  			return nil
   190  		}
   191  		done := make(chan struct{})
   192  		err := srv.DagProcessor().Enqueue("", batch.Bases(), true, nil, func() {
   193  			done <- struct{}{}
   194  		})
   195  		if err != nil {
   196  			return err
   197  		}
   198  		<-done
   199  		last = batch[batch.Len()-1].ID()
   200  		batch = batch[:0]
   201  		batchSize = 0
   202  		return nil
   203  	}
   204  
   205  	for {
   206  		select {
   207  		case <-interrupt:
   208  			return fmt.Errorf("interrupted")
   209  		default:
   210  		}
   211  		e := new(native.EventPayload)
   212  		err = stream.Decode(e)
   213  		if err == io.EOF {
   214  			err = processBatch()
   215  			if err != nil {
   216  				return err
   217  			}
   218  			break
   219  		}
   220  		if err != nil {
   221  			return err
   222  		}
   223  		if e.Epoch() != epoch || batchSize >= maxBatchSize {
   224  			err = processBatch()
   225  			if err != nil {
   226  				return err
   227  			}
   228  		}
   229  		epoch = e.Epoch()
   230  		batch = append(batch, e)
   231  		batchSize += 1024 + e.Size()
   232  		txs += e.Txs().Len()
   233  		events++
   234  	}
   235  	srv.WaitBlockEnd()
   236  	log.Info("Events import is finished", "file", fn, "last", last.String(), "imported", events, "txs", txs, "elapsed", common.PrettyDuration(time.Since(start)))
   237  
   238  	return nil
   239  }