2020-03-11 15:54:09 +00:00
package commands
import (
2021-03-23 16:33:58 +00:00
"bytes"
"context"
"fmt"
"os"
"os/signal"
2022-05-14 19:17:23 +00:00
"path"
2021-03-23 16:33:58 +00:00
"sort"
"syscall"
"time"
2023-09-01 07:31:18 +00:00
"github.com/ledgerwatch/log/v3"
"github.com/spf13/cobra"
chain2 "github.com/ledgerwatch/erigon-lib/chain"
2023-01-13 18:12:18 +00:00
libcommon "github.com/ledgerwatch/erigon-lib/common"
2023-09-01 07:31:18 +00:00
"github.com/ledgerwatch/erigon-lib/common/datadir"
2023-01-13 18:12:18 +00:00
"github.com/ledgerwatch/erigon-lib/common/hexutility"
2021-07-29 11:53:13 +00:00
"github.com/ledgerwatch/erigon-lib/kv"
kv2 "github.com/ledgerwatch/erigon-lib/kv/mdbx"
2022-12-19 08:38:54 +00:00
"github.com/ledgerwatch/erigon-lib/kv/temporal/historyv2"
2023-06-15 06:11:51 +00:00
"github.com/ledgerwatch/erigon/turbo/snapshotsync/freezeblocks"
2023-01-13 18:12:18 +00:00
2023-09-01 07:31:18 +00:00
"github.com/ledgerwatch/erigon/consensus"
2021-05-20 18:25:53 +00:00
"github.com/ledgerwatch/erigon/core/state"
2022-12-10 22:41:04 +00:00
"github.com/ledgerwatch/erigon/core/systemcontracts"
2021-05-20 18:25:53 +00:00
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/core/vm"
2022-05-14 19:17:23 +00:00
"github.com/ledgerwatch/erigon/eth/ethconfig"
2023-09-01 07:31:18 +00:00
"github.com/ledgerwatch/erigon/eth/ethconsensusconfig"
2021-05-20 18:25:53 +00:00
"github.com/ledgerwatch/erigon/eth/stagedsync/stages"
2023-09-01 07:31:18 +00:00
"github.com/ledgerwatch/erigon/node/nodecfg"
"github.com/ledgerwatch/erigon/params"
2023-05-07 06:28:15 +00:00
"github.com/ledgerwatch/erigon/turbo/debug"
2023-09-01 07:31:18 +00:00
"github.com/ledgerwatch/erigon/turbo/services"
2020-03-11 15:54:09 +00:00
)
2020-03-25 20:18:46 +00:00
var (
2023-06-03 08:54:27 +00:00
historyfile string
nocheck bool
2020-03-25 20:18:46 +00:00
)
2020-03-11 15:54:09 +00:00
func init ( ) {
withBlock ( checkChangeSetsCmd )
2022-02-22 17:39:48 +00:00
withDataDir ( checkChangeSetsCmd )
2022-05-14 19:17:23 +00:00
withSnapshotBlocks ( checkChangeSetsCmd )
2021-05-26 10:35:39 +00:00
checkChangeSetsCmd . Flags ( ) . StringVar ( & historyfile , "historyfile" , "" , "path to the file where the changesets and history are expected to be. If omitted, the same as <datadir>/erion/chaindata" )
2020-03-25 20:18:46 +00:00
checkChangeSetsCmd . Flags ( ) . BoolVar ( & nocheck , "nocheck" , false , "set to turn off the changeset checking and only execute transaction (for performance testing)" )
2020-03-11 15:54:09 +00:00
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 {
2023-07-09 07:04:20 +00:00
logger := debug . SetupCobra ( cmd , "check_change_sets" )
2023-10-12 07:11:46 +00:00
return CheckChangeSets ( cmd . Context ( ) , genesis , block , chaindata , historyfile , nocheck , logger )
2020-03-11 15:54:09 +00:00
} ,
}
2021-03-23 16:33:58 +00:00
// CheckChangeSets re-executes historical transactions in read-only mode
// and checks that their outputs match the database ChangeSets.
2023-10-12 07:11:46 +00:00
func CheckChangeSets ( ctx context . Context , genesis * types . Genesis , blockNum uint64 , chaindata string , historyfile string , nocheck bool , logger log . Logger ) error {
2021-03-23 16:33:58 +00:00
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
} ( )
2023-10-12 07:11:46 +00:00
db , err := kv2 . NewMDBX ( logger ) . Path ( chaindata ) . Open ( ctx )
2021-05-27 07:37:23 +00:00
if err != nil {
return err
}
2023-06-15 06:11:51 +00:00
allSnapshots := freezeblocks . NewRoSnapshots ( ethconfig . NewSnapCfg ( true , false , true ) , path . Join ( datadirCli , "snapshots" ) , logger )
2023-02-24 18:49:25 +00:00
defer allSnapshots . Close ( )
if err := allSnapshots . ReopenFolder ( ) ; err != nil {
return fmt . Errorf ( "reopen snapshot segments: %w" , err )
2022-07-31 20:54:23 +00:00
}
2023-08-18 16:10:35 +00:00
blockReader := freezeblocks . NewBlockReader ( allSnapshots , nil /* BorSnapshots */ )
2023-02-24 18:49:25 +00:00
2021-07-28 02:47:38 +00:00
chainDb := db
2021-03-23 16:33:58 +00:00
defer chainDb . Close ( )
historyDb := chainDb
if chaindata != historyfile {
2021-06-19 08:21:53 +00:00
historyDb = kv2 . MustOpen ( historyfile )
2021-03-23 16:33:58 +00:00
}
2022-05-14 19:17:23 +00:00
historyTx , err1 := historyDb . BeginRo ( ctx )
2021-03-23 16:33:58 +00:00
if err1 != nil {
return err1
}
defer historyTx . Rollback ( )
chainConfig := genesis . Config
vmConfig := vm . Config { }
noOpWriter := state . NewNoopWriter ( )
interrupt := false
2022-05-14 19:17:23 +00:00
rwtx , err := chainDb . BeginRw ( ctx )
2021-04-15 10:06:30 +00:00
if err != nil {
return err
}
defer rwtx . Rollback ( )
2021-03-23 16:33:58 +00:00
2021-04-22 17:11:55 +00:00
execAt , err1 := stages . GetStageProgress ( rwtx , stages . Execution )
2021-03-23 16:33:58 +00:00
if err1 != nil {
return err1
}
2021-04-22 17:11:55 +00:00
historyAt , err1 := stages . GetStageProgress ( rwtx , stages . StorageHistoryIndex )
2021-03-23 16:33:58 +00:00
if err1 != nil {
return err1
}
commitEvery := time . NewTicker ( 30 * time . Second )
defer commitEvery . Stop ( )
2022-05-14 19:17:23 +00:00
2023-10-12 07:11:46 +00:00
engine := initConsensusEngine ( ctx , chainConfig , allSnapshots , blockReader , logger )
2022-05-14 19:17:23 +00:00
2021-03-23 16:33:58 +00:00
for ! interrupt {
2021-04-26 05:37:48 +00:00
2021-03-23 16:33:58 +00:00
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
}
2022-05-14 19:17:23 +00:00
blockHash , err := blockReader . CanonicalHash ( ctx , historyTx , blockNum )
2021-03-23 16:33:58 +00:00
if err != nil {
return err
}
2022-02-12 15:48:38 +00:00
var b * types . Block
2022-05-14 19:17:23 +00:00
b , _ , err = blockReader . BlockWithSenders ( ctx , historyTx , blockHash , blockNum )
2021-05-27 07:37:23 +00:00
if err != nil {
return err
}
2022-02-12 15:48:38 +00:00
if b == nil {
2021-03-23 16:33:58 +00:00
break
}
2022-12-10 22:41:04 +00:00
reader := state . NewPlainState ( historyTx , blockNum , systemcontracts . SystemContractCodeLookup [ chainConfig . ChainName ] )
2022-07-31 20:54:23 +00:00
//reader.SetTrace(blockNum == uint64(block))
2022-02-12 15:48:38 +00:00
intraBlockState := state . New ( reader )
2022-02-16 08:38:12 +00:00
csw := state . NewChangeSetWriterPlain ( nil /* db */ , blockNum )
2021-03-23 16:33:58 +00:00
var blockWriter state . StateWriter
if nocheck {
blockWriter = noOpWriter
} else {
blockWriter = csw
}
2023-01-13 18:12:18 +00:00
getHeader := func ( hash libcommon . Hash , number uint64 ) * types . Header {
2022-06-04 22:06:30 +00:00
h , e := blockReader . Header ( ctx , rwtx , hash , number )
if e != nil {
panic ( e )
}
return h
}
2023-06-06 09:55:59 +00:00
receipts , err1 := runBlock ( engine , intraBlockState , noOpWriter , blockWriter , chainConfig , getHeader , b , vmConfig , blockNum == block , logger )
2021-03-23 16:33:58 +00:00
if err1 != nil {
return err1
}
2022-06-04 22:06:30 +00:00
if chainConfig . IsByzantium ( blockNum ) {
receiptSha := types . DeriveSha ( receipts )
if receiptSha != b . ReceiptHash ( ) {
return fmt . Errorf ( "mismatched receipt headers for block %d" , blockNum )
2021-03-23 16:33:58 +00:00
}
}
if ! nocheck {
accountChanges , err := csw . GetAccountChanges ( )
if err != nil {
return err
}
sort . Sort ( accountChanges )
i := 0
match := true
2023-01-13 18:12:18 +00:00
err = historyv2 . ForPrefix ( historyTx , kv . AccountChangeSet , hexutility . EncodeTs ( blockNum ) , func ( blockN uint64 , k , v [ ] byte ) error {
2022-06-04 15:54:22 +00:00
if i >= len ( accountChanges . Changes ) {
if len ( v ) != 0 {
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 )
match = false
}
i ++
return nil
}
2021-03-23 16:33:58 +00:00
c := accountChanges . Changes [ i ]
if bytes . Equal ( c . Key , k ) && bytes . Equal ( c . Value , v ) {
i ++
2021-09-22 00:54:29 +00:00
return nil
2021-03-23 16:33:58 +00:00
}
2022-06-04 15:54:22 +00:00
if len ( v ) == 0 {
return nil
}
2021-03-23 16:33:58 +00:00
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 )
2022-06-04 15:54:22 +00:00
i ++
2021-09-22 00:54:29 +00:00
return nil
2021-03-23 16:33:58 +00:00
} )
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 {
2022-12-19 08:38:54 +00:00
expectedStorageChanges = historyv2 . NewChangeSet ( )
2021-03-23 16:33:58 +00:00
}
sort . Sort ( expectedStorageChanges )
2022-06-04 15:54:22 +00:00
match = true
2023-01-13 18:12:18 +00:00
err = historyv2 . ForPrefix ( historyTx , kv . StorageChangeSet , hexutility . EncodeTs ( blockNum ) , func ( blockN uint64 , k , v [ ] byte ) error {
2022-06-04 15:54:22 +00:00
if i >= len ( expectedStorageChanges . Changes ) {
fmt . Printf ( "Unexpected storage changes in block %d\nIn the database: ======================\n" , blockNum )
fmt . Printf ( "0x%x: %x\n" , k , v )
match = false
i ++
return nil
}
2021-03-23 16:33:58 +00:00
c := expectedStorageChanges . Changes [ i ]
i ++
if bytes . Equal ( c . Key , k ) && bytes . Equal ( c . Value , v ) {
2021-09-22 00:54:29 +00:00
return nil
2021-03-23 16:33:58 +00:00
}
2022-06-04 15:54:22 +00:00
match = false
2021-03-23 16:33:58 +00:00
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 )
2022-06-04 15:54:22 +00:00
i ++
return nil
2021-03-23 16:33:58 +00:00
} )
if err != nil {
return err
}
2022-06-04 15:54:22 +00:00
if ! match {
return fmt . Errorf ( "check change set failed" )
}
2021-03-23 16:33:58 +00:00
}
blockNum ++
if blockNum % 1000 == 0 {
2023-05-07 06:28:15 +00:00
logger . Info ( "Checked" , "blocks" , blockNum )
2021-03-23 16:33:58 +00:00
}
// Check for interrupts
select {
case interrupt = <- interruptCh :
fmt . Println ( "interrupted, please wait for cleanup..." )
default :
}
}
2023-05-07 06:28:15 +00:00
logger . Info ( "Checked" , "blocks" , blockNum , "next time specify --block" , blockNum , "duration" , time . Since ( startTime ) )
2021-03-23 16:33:58 +00:00
return nil
}
2023-09-01 07:31:18 +00:00
2023-10-12 07:11:46 +00:00
func initConsensusEngine ( ctx context . Context , cc * chain2 . Config , snapshots * freezeblocks . RoSnapshots , blockReader services . FullBlockReader , logger log . Logger ) ( engine consensus . Engine ) {
2023-09-01 07:31:18 +00:00
config := ethconfig . Defaults
var consensusConfig interface { }
if cc . Clique != nil {
consensusConfig = params . CliqueSnapshot
} else if cc . Aura != nil {
consensusConfig = & config . Aura
} else if cc . Bor != nil {
consensusConfig = & config . Bor
} else {
consensusConfig = & config . Ethash
}
2023-10-12 07:11:46 +00:00
return ethconsensusconfig . CreateConsensusEngine ( ctx , & nodecfg . Config { Dirs : datadir . New ( datadirCli ) } , cc , consensusConfig , config . Miner . Notify , config . Miner . Noverify , nil /* heimdallClient */ , config . WithoutHeimdall , blockReader , true /* readonly */ , logger )
2023-09-01 07:31:18 +00:00
}