erigon-pulse/cmd/state/commands/check_change_sets.go

243 lines
7.1 KiB
Go
Raw Normal View History

package commands
import (
"bytes"
"context"
"fmt"
"os"
"os/signal"
"sort"
"syscall"
"time"
"github.com/ledgerwatch/turbo-geth/common"
"github.com/ledgerwatch/turbo-geth/common/changeset"
"github.com/ledgerwatch/turbo-geth/common/dbutils"
"github.com/ledgerwatch/turbo-geth/consensus/ethash"
"github.com/ledgerwatch/turbo-geth/core"
"github.com/ledgerwatch/turbo-geth/core/rawdb"
"github.com/ledgerwatch/turbo-geth/core/state"
"github.com/ledgerwatch/turbo-geth/core/types"
"github.com/ledgerwatch/turbo-geth/core/vm"
"github.com/ledgerwatch/turbo-geth/eth/stagedsync/stages"
"github.com/ledgerwatch/turbo-geth/ethdb"
"github.com/ledgerwatch/turbo-geth/log"
"github.com/spf13/cobra"
)
var (
historyfile string
nocheck bool
writeReceipts bool
)
func init() {
withBlock(checkChangeSetsCmd)
withDatadir(checkChangeSetsCmd)
checkChangeSetsCmd.Flags().StringVar(&historyfile, "historyfile", "", "path to the file where the changesets and history are expected to be. If omitted, the same as <datadir>/tg/chaindata")
checkChangeSetsCmd.Flags().BoolVar(&nocheck, "nocheck", false, "set to turn off the changeset checking and only execute transaction (for performance testing)")
checkChangeSetsCmd.Flags().BoolVar(&writeReceipts, "writeReceipts", false, "set to turn on writing receipts as the execution ongoing")
rootCmd.AddCommand(checkChangeSetsCmd)
}
var checkChangeSetsCmd = &cobra.Command{
Use: "checkChangeSets",
Short: "Re-executes historical transactions in read-only mode and checks that their outputs match the database ChangeSets",
RunE: func(cmd *cobra.Command, args []string) error {
return CheckChangeSets(genesis, block, chaindata, historyfile, nocheck, writeReceipts)
},
}
// CheckChangeSets re-executes historical transactions in read-only mode
// and checks that their outputs match the database ChangeSets.
func CheckChangeSets(genesis *core.Genesis, blockNum uint64, chaindata string, historyfile string, nocheck bool, writeReceipts bool) error {
if len(historyfile) == 0 {
historyfile = chaindata
}
startTime := time.Now()
sigs := make(chan os.Signal, 1)
interruptCh := make(chan bool, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
go func() {
<-sigs
interruptCh <- true
}()
chainDb := ethdb.MustOpen(chaindata)
defer chainDb.Close()
historyDb := chainDb
if chaindata != historyfile {
historyDb = ethdb.MustOpen(historyfile)
}
historyTx, err1 := historyDb.Begin(context.Background(), ethdb.RO)
if err1 != nil {
return err1
}
defer historyTx.Rollback()
chainConfig := genesis.Config
engine := ethash.NewFaker()
vmConfig := vm.Config{}
cc := &core.TinyChainContext{}
2021-04-22 17:11:55 +00:00
//cc.SetDB(chainDb)
cc.SetEngine(engine)
noOpWriter := state.NewNoopWriter()
interrupt := false
rwtx, err := chainDb.RwKV().BeginRw(context.Background())
if err != nil {
return err
}
defer rwtx.Rollback()
2021-04-22 17:11:55 +00:00
execAt, err1 := stages.GetStageProgress(rwtx, stages.Execution)
if err1 != nil {
return err1
}
2021-04-22 17:11:55 +00:00
historyAt, err1 := stages.GetStageProgress(rwtx, stages.StorageHistoryIndex)
if err1 != nil {
return err1
}
commitEvery := time.NewTicker(30 * time.Second)
defer commitEvery.Stop()
for !interrupt {
if blockNum > execAt {
log.Warn(fmt.Sprintf("Force stop: because trying to check blockNumber=%d higher than Exec stage=%d", blockNum, execAt))
break
}
if blockNum > historyAt {
log.Warn(fmt.Sprintf("Force stop: because trying to check blockNumber=%d higher than History stage=%d", blockNum, historyAt))
break
}
2021-04-22 17:11:55 +00:00
blockHash, err := rawdb.ReadCanonicalHash(rwtx, blockNum)
if err != nil {
return err
}
2021-04-22 17:11:55 +00:00
block := rawdb.ReadBlock(ethdb.NewRoTxDb(rwtx), blockHash, blockNum)
if block == nil {
break
}
dbstate := state.NewPlainDBState(historyTx, block.NumberU64()-1)
intraBlockState := state.New(dbstate)
csw := state.NewChangeSetWriterPlain(nil /* db */, block.NumberU64()-1)
var blockWriter state.StateWriter
if nocheck {
blockWriter = noOpWriter
} else {
blockWriter = csw
}
2021-04-22 17:11:55 +00:00
getHeader := func(hash common.Hash, number uint64) *types.Header { return rawdb.ReadHeader(rwtx, hash, number) }
2021-04-07 05:38:43 +00:00
receipts, err1 := runBlock(intraBlockState, noOpWriter, blockWriter, chainConfig, getHeader, block, vmConfig)
if err1 != nil {
return err1
}
if writeReceipts {
Aleut support (Eip1559) (#1704) * Where I am at * Refactoring of transaction types * More refactoring * Use Homested signer in rpc daemon * Unified signer * Continue unified signer * A bit more * Fixes and down the rabbit hole... * More tx pool fixes * More refactoring fixes * More fixes' * more fixes * More fixes * More compile fixes * More RLP hand-writing * Finish RLP encoding/decoding of transactions * Fixes to header encoding, start on protocol packets * Transaction decoding * Use DecodeTransaction function * Decoding BlockBodyPacket * Encode and decode for pool txs * Start fixing tests * Introduce SigningHash * Fixes to SignHash * RLP encoding fixes * Fixes for encoding/decoding * More test fixes * Fix more tests * More test fixes * More test fixes * Fix core tests * More fixes for signer * Fix for tx * Fixes to string encoding/size * Fix eip2930 test * Fix rest of ./tests * More fixes * Fix compilation * More test fixes * More test fixes * Test fixes * More fixes * Reuse EncodingSize in EncodeRLP for accessList * Rearrange things in dynamic fee tx * Add MarshalBinary * More fixes * Make V,R,S non-pointers * More NPE fixes * More fixes * Receipt fixes * Fix core/types * Fix ./eth * More compile fixes for tests * More test fixes * More test fixes * Try to see lint errors better * Try to see lint errors better * Fix lint * Debugging eip1559 test * Fix TestEIP1559Transition test * Fix NewBlockPacket encoding/decoding * Fix calculation of TxHash * Fix perf problem with senders * Update aleut config values * Try adding static peers * Add staticpeers to defaul flags * Change aleut networkID * Fix test Co-authored-by: Alex Sharp <alexsharp@Alexs-MacBook-Pro.local> Co-authored-by: Alexey Sharp <alexeysharp@Alexeys-iMac.local>
2021-04-22 17:11:37 +00:00
if chainConfig.IsByzantium(block.Number().Uint64()) {
receiptSha := types.DeriveSha(receipts)
if receiptSha != block.Header().ReceiptHash {
return fmt.Errorf("mismatched receipt headers for block %d", block.NumberU64())
}
}
if err := rawdb.AppendReceipts(rwtx, block.NumberU64(), receipts); err != nil {
return err
}
}
if !nocheck {
accountChanges, err := csw.GetAccountChanges()
if err != nil {
return err
}
sort.Sort(accountChanges)
i := 0
match := true
2021-04-03 01:52:45 +00:00
err = changeset.Walk(historyTx.(ethdb.HasTx).Tx(), dbutils.PlainAccountChangeSetBucket, dbutils.EncodeBlockNumber(blockNum), 8*8, func(blockN uint64, k, v []byte) (bool, error) {
c := accountChanges.Changes[i]
if bytes.Equal(c.Key, k) && bytes.Equal(c.Value, v) {
i++
return true, nil
}
match = false
fmt.Printf("Unexpected account changes in block %d\n", blockNum)
fmt.Printf("In the database: ======================\n")
fmt.Printf("%d: 0x%x: %x\n", i, k, v)
fmt.Printf("Expected: ==========================\n")
fmt.Printf("%d: 0x%x %x\n", i, c.Key, c.Value)
return false, nil
})
if err != nil {
return err
}
if !match {
return fmt.Errorf("check change set failed")
}
i = 0
expectedStorageChanges, err := csw.GetStorageChanges()
if err != nil {
return err
}
if expectedStorageChanges == nil {
expectedStorageChanges = changeset.NewChangeSet()
}
sort.Sort(expectedStorageChanges)
2021-04-03 01:52:45 +00:00
err = changeset.Walk(historyTx.(ethdb.HasTx).Tx(), dbutils.PlainStorageChangeSetBucket, dbutils.EncodeBlockNumber(blockNum), 8*8, func(blockN uint64, k, v []byte) (bool, error) {
c := expectedStorageChanges.Changes[i]
i++
if bytes.Equal(c.Key, k) && bytes.Equal(c.Value, v) {
return false, nil
}
fmt.Printf("Unexpected storage changes in block %d\nIn the database: ======================\n", blockNum)
fmt.Printf("0x%x: %x\n", k, v)
fmt.Printf("Expected: ==========================\n")
fmt.Printf("0x%x %x\n", c.Key, c.Value)
return true, fmt.Errorf("check change set failed")
})
if err != nil {
return err
}
}
blockNum++
if blockNum%1000 == 0 {
log.Info("Checked", "blocks", blockNum)
}
// Check for interrupts
select {
case interrupt = <-interruptCh:
fmt.Println("interrupted, please wait for cleanup...")
case <-commitEvery.C:
if writeReceipts {
log.Info("Committing receipts", "up to block", block.NumberU64())
if err = rwtx.Commit(); err != nil {
return err
}
rwtx, err = chainDb.RwKV().BeginRw(context.Background())
if err != nil {
return err
}
}
default:
}
}
if writeReceipts {
log.Info("Committing final receipts", "batch size")
if err = rwtx.Commit(); err != nil {
return err
}
}
log.Info("Checked", "blocks", blockNum, "next time specify --block", blockNum, "duration", time.Since(startTime))
return nil
}