2022-06-10 15:18:43 +00:00
|
|
|
package commands
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2022-06-20 08:00:45 +00:00
|
|
|
"errors"
|
2022-06-10 15:18:43 +00:00
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"os/signal"
|
|
|
|
"path"
|
|
|
|
"path/filepath"
|
|
|
|
"syscall"
|
|
|
|
"time"
|
|
|
|
|
2022-06-20 08:00:45 +00:00
|
|
|
libcommon "github.com/ledgerwatch/erigon-lib/common"
|
2022-11-20 03:41:30 +00:00
|
|
|
"github.com/ledgerwatch/erigon-lib/common/datadir"
|
2022-06-20 08:00:45 +00:00
|
|
|
"github.com/ledgerwatch/erigon-lib/kv"
|
|
|
|
"github.com/ledgerwatch/erigon-lib/kv/mdbx"
|
2022-06-10 15:18:43 +00:00
|
|
|
kv2 "github.com/ledgerwatch/erigon-lib/kv/mdbx"
|
|
|
|
libstate "github.com/ledgerwatch/erigon-lib/state"
|
2022-12-07 17:45:44 +00:00
|
|
|
"github.com/ledgerwatch/log/v3"
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
|
2022-06-10 15:18:43 +00:00
|
|
|
"github.com/ledgerwatch/erigon/common"
|
|
|
|
"github.com/ledgerwatch/erigon/consensus/ethash"
|
|
|
|
"github.com/ledgerwatch/erigon/consensus/misc"
|
|
|
|
"github.com/ledgerwatch/erigon/core"
|
|
|
|
"github.com/ledgerwatch/erigon/core/rawdb"
|
|
|
|
"github.com/ledgerwatch/erigon/core/state"
|
|
|
|
"github.com/ledgerwatch/erigon/core/types"
|
|
|
|
"github.com/ledgerwatch/erigon/core/vm"
|
|
|
|
"github.com/ledgerwatch/erigon/eth/ethconfig"
|
|
|
|
"github.com/ledgerwatch/erigon/params"
|
|
|
|
"github.com/ledgerwatch/erigon/turbo/services"
|
|
|
|
"github.com/ledgerwatch/erigon/turbo/snapshotsync"
|
|
|
|
)
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
withBlock(history22Cmd)
|
|
|
|
withDataDir(history22Cmd)
|
|
|
|
history22Cmd.Flags().IntVar(&traceBlock, "traceblock", 0, "block number at which to turn on tracing")
|
|
|
|
history22Cmd.Flags().IntVar(&blockTo, "blockto", 0, "block number to stop replay of history at")
|
|
|
|
rootCmd.AddCommand(history22Cmd)
|
|
|
|
}
|
|
|
|
|
|
|
|
var history22Cmd = &cobra.Command{
|
|
|
|
Use: "history22",
|
|
|
|
Short: "Exerimental command to re-execute historical transactions in erigon2 format",
|
|
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
|
|
logger := log.New()
|
|
|
|
return History22(genesis, logger)
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
func History22(genesis *core.Genesis, logger log.Logger) error {
|
|
|
|
sigs := make(chan os.Signal, 1)
|
|
|
|
interruptCh := make(chan bool, 1)
|
|
|
|
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
<-sigs
|
|
|
|
interruptCh <- true
|
|
|
|
}()
|
2022-11-20 03:41:30 +00:00
|
|
|
dirs := datadir.New(datadirCli)
|
2022-08-19 02:35:43 +00:00
|
|
|
historyDb, err := kv2.NewMDBX(logger).Path(dirs.Chaindata).Open()
|
2022-06-10 15:18:43 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("opening chaindata as read only: %v", err)
|
|
|
|
}
|
|
|
|
defer historyDb.Close()
|
|
|
|
ctx := context.Background()
|
|
|
|
historyTx, err1 := historyDb.BeginRo(ctx)
|
|
|
|
if err1 != nil {
|
|
|
|
return err1
|
|
|
|
}
|
|
|
|
defer historyTx.Rollback()
|
2022-10-21 06:45:20 +00:00
|
|
|
aggPath := filepath.Join(datadirCli, "erigon23")
|
|
|
|
h, err := libstate.NewAggregator(aggPath, dirs.Tmp, ethconfig.HistoryV3AggregationStep)
|
2022-06-20 08:00:45 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("create history: %w", err)
|
2022-06-10 15:18:43 +00:00
|
|
|
}
|
|
|
|
defer h.Close()
|
2022-10-21 06:45:20 +00:00
|
|
|
readDbPath := path.Join(datadirCli, "readdb")
|
2022-06-20 08:00:45 +00:00
|
|
|
if block == 0 {
|
|
|
|
if _, err = os.Stat(readDbPath); err != nil {
|
|
|
|
if !errors.Is(err, os.ErrNotExist) {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else if err = os.RemoveAll(readDbPath); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
db, err := kv2.NewMDBX(logger).Path(readDbPath).WriteMap().Open()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer db.Close()
|
2022-10-21 06:45:20 +00:00
|
|
|
readPath := filepath.Join(datadirCli, "reads")
|
2022-06-20 08:00:45 +00:00
|
|
|
if block == 0 {
|
|
|
|
if _, err = os.Stat(readPath); err != nil {
|
|
|
|
if !errors.Is(err, os.ErrNotExist) {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else if err = os.RemoveAll(readPath); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = os.Mkdir(readPath, os.ModePerm); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2022-10-21 06:45:20 +00:00
|
|
|
ri, err := libstate.NewReadIndices(readPath, dirs.Tmp, ethconfig.HistoryV3AggregationStep)
|
2022-06-20 08:00:45 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("create read indices: %w", err)
|
|
|
|
}
|
|
|
|
var rwTx kv.RwTx
|
|
|
|
defer func() {
|
|
|
|
if rwTx != nil {
|
|
|
|
rwTx.Rollback()
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
if rwTx, err = db.BeginRw(ctx); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
ri.SetTx(rwTx)
|
2022-06-10 15:18:43 +00:00
|
|
|
chainConfig := genesis.Config
|
|
|
|
vmConfig := vm.Config{}
|
|
|
|
|
|
|
|
interrupt := false
|
|
|
|
blockNum := uint64(0)
|
|
|
|
var txNum uint64 = 2
|
|
|
|
trace := false
|
|
|
|
logEvery := time.NewTicker(logInterval)
|
|
|
|
defer logEvery.Stop()
|
|
|
|
prevBlock := blockNum
|
|
|
|
prevTime := time.Now()
|
|
|
|
|
|
|
|
var blockReader services.FullBlockReader
|
2022-10-21 06:45:20 +00:00
|
|
|
allSnapshots := snapshotsync.NewRoSnapshots(ethconfig.NewSnapCfg(true, false, true), path.Join(datadirCli, "snapshots"))
|
2022-06-10 15:18:43 +00:00
|
|
|
defer allSnapshots.Close()
|
2022-07-28 09:57:38 +00:00
|
|
|
if err := allSnapshots.ReopenWithDB(db); err != nil {
|
2022-06-10 15:18:43 +00:00
|
|
|
return fmt.Errorf("reopen snapshot segments: %w", err)
|
|
|
|
}
|
|
|
|
blockReader = snapshotsync.NewBlockReaderWithSnapshots(allSnapshots)
|
2022-08-27 08:21:02 +00:00
|
|
|
readWrapper := state.NewHistoryReader23(h.MakeContext(), ri)
|
2022-06-10 15:18:43 +00:00
|
|
|
|
|
|
|
for !interrupt {
|
|
|
|
select {
|
|
|
|
default:
|
|
|
|
case <-logEvery.C:
|
|
|
|
currentTime := time.Now()
|
|
|
|
interval := currentTime.Sub(prevTime)
|
|
|
|
speed := float64(blockNum-prevBlock) / (float64(interval) / float64(time.Second))
|
|
|
|
prevBlock = blockNum
|
|
|
|
prevTime = currentTime
|
|
|
|
log.Info("Progress", "block", blockNum, "blk/s", speed)
|
|
|
|
}
|
|
|
|
blockNum++
|
|
|
|
if blockNum > uint64(blockTo) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
blockHash, err := rawdb.ReadCanonicalHash(historyTx, blockNum)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
b, _, err := blockReader.BlockWithSenders(ctx, historyTx, blockHash, blockNum)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if b == nil {
|
|
|
|
log.Info("history: block is nil", "block", blockNum)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if blockNum <= block {
|
|
|
|
// Skip that block, but increase txNum
|
|
|
|
txNum += uint64(len(b.Transactions())) + 2 // Pre and Post block transaction
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if traceBlock != 0 {
|
2022-06-12 11:44:01 +00:00
|
|
|
readWrapper.SetTrace(blockNum == uint64(traceBlock))
|
2022-06-10 15:18:43 +00:00
|
|
|
}
|
|
|
|
writeWrapper := state.NewNoopWriter()
|
|
|
|
getHeader := func(hash common.Hash, number uint64) *types.Header {
|
|
|
|
h, err := blockReader.Header(ctx, historyTx, hash, number)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
return h
|
|
|
|
}
|
|
|
|
if txNum, _, err = runHistory22(trace, blockNum, txNum, readWrapper, writeWrapper, chainConfig, getHeader, b, vmConfig); err != nil {
|
|
|
|
return fmt.Errorf("block %d: %w", blockNum, err)
|
|
|
|
}
|
|
|
|
txNum++ // Post block transaction
|
|
|
|
// Check for interrupts
|
|
|
|
select {
|
|
|
|
case interrupt = <-interruptCh:
|
|
|
|
log.Info(fmt.Sprintf("interrupted, please wait for cleanup, next time start with --block %d", blockNum))
|
|
|
|
default:
|
|
|
|
}
|
2022-06-20 08:00:45 +00:00
|
|
|
// Commit transaction only when interrupted or just before computing commitment (so it can be re-done)
|
|
|
|
commit := interrupt
|
|
|
|
if !commit && (blockNum+1)%uint64(commitmentFrequency) == 0 {
|
|
|
|
var spaceDirty uint64
|
|
|
|
if spaceDirty, _, err = rwTx.(*mdbx.MdbxTx).SpaceDirty(); err != nil {
|
|
|
|
return fmt.Errorf("retrieving spaceDirty: %w", err)
|
|
|
|
}
|
|
|
|
if spaceDirty >= dirtySpaceThreshold {
|
|
|
|
log.Info("Initiated tx commit", "block", blockNum, "space dirty", libcommon.ByteCount(spaceDirty))
|
|
|
|
commit = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if commit {
|
|
|
|
if err = rwTx.Commit(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if !interrupt {
|
|
|
|
if rwTx, err = db.BeginRw(ctx); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ri.SetTx(rwTx)
|
|
|
|
}
|
2022-06-10 15:18:43 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-12-22 02:37:32 +00:00
|
|
|
func runHistory22(trace bool, blockNum, txNumStart uint64, hw *state.HistoryReaderV4, ww state.StateWriter, chainConfig *params.ChainConfig, getHeader func(hash common.Hash, number uint64) *types.Header, block *types.Block, vmConfig vm.Config) (uint64, types.Receipts, error) {
|
2022-06-10 15:18:43 +00:00
|
|
|
header := block.Header()
|
|
|
|
vmConfig.TraceJumpDest = true
|
|
|
|
engine := ethash.NewFullFaker()
|
|
|
|
gp := new(core.GasPool).AddGas(block.GasLimit())
|
|
|
|
usedGas := new(uint64)
|
|
|
|
var receipts types.Receipts
|
2022-12-07 17:45:44 +00:00
|
|
|
rules := chainConfig.Rules(block.NumberU64(), block.Time())
|
2022-06-10 15:18:43 +00:00
|
|
|
txNum := txNumStart
|
2022-07-02 19:48:42 +00:00
|
|
|
hw.SetTxNum(txNum)
|
|
|
|
daoFork := chainConfig.DAOForkSupport && chainConfig.DAOForkBlock != nil && chainConfig.DAOForkBlock.Cmp(block.Number()) == 0
|
|
|
|
if daoFork {
|
|
|
|
ibs := state.New(hw)
|
|
|
|
misc.ApplyDAOHardFork(ibs)
|
|
|
|
if err := ibs.FinalizeTx(rules, ww); err != nil {
|
|
|
|
return 0, nil, err
|
|
|
|
}
|
|
|
|
if err := hw.FinishTx(); err != nil {
|
|
|
|
return 0, nil, fmt.Errorf("finish dao fork failed: %w", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
txNum++ // Pre block transaction
|
2022-06-10 15:18:43 +00:00
|
|
|
for i, tx := range block.Transactions() {
|
2022-06-12 11:44:01 +00:00
|
|
|
hw.SetTxNum(txNum)
|
2022-06-10 15:18:43 +00:00
|
|
|
ibs := state.New(hw)
|
|
|
|
ibs.Prepare(tx.Hash(), block.Hash(), i)
|
2022-09-01 18:49:29 +00:00
|
|
|
receipt, _, err := core.ApplyTransaction(chainConfig, core.GetHashFn(header, getHeader), engine, nil, gp, ibs, ww, header, tx, usedGas, vmConfig)
|
2022-06-10 15:18:43 +00:00
|
|
|
if err != nil {
|
|
|
|
return 0, nil, fmt.Errorf("could not apply tx %d [%x] failed: %w", i, tx.Hash(), err)
|
|
|
|
}
|
|
|
|
if traceBlock != 0 && blockNum == uint64(traceBlock) {
|
|
|
|
fmt.Printf("tx idx %d, num %d, gas used %d\n", i, txNum, receipt.GasUsed)
|
|
|
|
}
|
|
|
|
receipts = append(receipts, receipt)
|
2022-06-20 08:00:45 +00:00
|
|
|
if err = hw.FinishTx(); err != nil {
|
|
|
|
return 0, nil, fmt.Errorf("finish tx %d [%x] failed: %w", i, tx.Hash(), err)
|
|
|
|
}
|
2022-06-10 15:18:43 +00:00
|
|
|
txNum++
|
2022-06-12 11:44:01 +00:00
|
|
|
hw.SetTxNum(txNum)
|
2022-06-10 15:18:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return txNum, receipts, nil
|
|
|
|
}
|