package commands import ( "context" "encoding/hex" "errors" "fmt" "github.com/ledgerwatch/erigon-lib/metrics" "path/filepath" "runtime" "strings" "time" "github.com/holiman/uint256" "github.com/ledgerwatch/log/v3" "github.com/spf13/cobra" "github.com/ledgerwatch/erigon-lib/commitment" libcommon "github.com/ledgerwatch/erigon-lib/common" "github.com/ledgerwatch/erigon-lib/common/datadir" "github.com/ledgerwatch/erigon-lib/common/dbg" "github.com/ledgerwatch/erigon-lib/common/length" "github.com/ledgerwatch/erigon-lib/kv" kv2 "github.com/ledgerwatch/erigon-lib/kv/mdbx" libstate "github.com/ledgerwatch/erigon-lib/state" "github.com/ledgerwatch/erigon/cmd/utils" "github.com/ledgerwatch/erigon/core" "github.com/ledgerwatch/erigon/core/types/accounts" "github.com/ledgerwatch/erigon/eth/ethconfig" "github.com/ledgerwatch/erigon/node/nodecfg" erigoncli "github.com/ledgerwatch/erigon/turbo/cli" "github.com/ledgerwatch/erigon/turbo/debug" ) func init() { withDataDir(readDomains) withChain(readDomains) withHeimdall(readDomains) withWorkers(readDomains) withStartTx(readDomains) rootCmd.AddCommand(readDomains) } // if trie variant is not hex, we could not have another rootHash with to verify it var ( stepSize uint64 lastStep uint64 dirtySpaceThreshold = uint64(2 * 1024 * 1024 * 1024) /* threshold of dirty space in MDBX transaction that triggers a commit */ blockRootMismatchExpected bool mxBlockExecutionTimer = metrics.GetOrCreateSummary("chain_execution_seconds") mxCommitTook = metrics.GetOrCreateHistogram("domain_commit_took") ) // write command to just seek and query state by addr and domain from state db and files (if any) var readDomains = &cobra.Command{ Use: "read_domains", Short: `Run block execution and commitment with Domains.`, Example: "go run ./cmd/integration read_domains --datadir=... --verbosity=3", ValidArgs: []string{"account", "storage", "code", "commitment"}, Args: cobra.ArbitraryArgs, Run: func(cmd *cobra.Command, args []string) { logger := debug.SetupCobra(cmd, "integration") ctx, _ := libcommon.RootContext() cfg := &nodecfg.DefaultConfig utils.SetNodeConfigCobra(cmd, cfg) ethConfig := ðconfig.Defaults ethConfig.Genesis = core.GenesisBlockByChainName(chain) erigoncli.ApplyFlagsForEthConfigCobra(cmd.Flags(), ethConfig) var readFromDomain string var addrs [][]byte for i := 0; i < len(args); i++ { if i == 0 { switch s := strings.ToLower(args[i]); s { case "account", "storage", "code", "commitment": readFromDomain = s default: logger.Error("invalid domain to read from", "arg", args[i]) return } continue } addr, err := hex.DecodeString(strings.TrimPrefix(args[i], "0x")) if err != nil { logger.Warn("invalid address passed", "str", args[i], "at position", i, "err", err) continue } addrs = append(addrs, addr) } dirs := datadir.New(datadirCli) chainDb, err := openDB(dbCfg(kv.ChainDB, dirs.Chaindata), true, logger) if err != nil { logger.Error("Opening DB", "error", err) return } defer chainDb.Close() stateDb, err := kv2.NewMDBX(log.New()).Path(filepath.Join(dirs.DataDir, "statedb")).WriteMap().Open(ctx) if err != nil { return } defer stateDb.Close() if err := requestDomains(chainDb, stateDb, ctx, readFromDomain, addrs, logger); err != nil { if !errors.Is(err, context.Canceled) { logger.Error(err.Error()) } return } }, } func requestDomains(chainDb, stateDb kv.RwDB, ctx context.Context, readDomain string, addrs [][]byte, logger log.Logger) error { trieVariant := commitment.ParseTrieVariant(commitmentTrie) if trieVariant != commitment.VariantHexPatriciaTrie { blockRootMismatchExpected = true } mode := libstate.ParseCommitmentMode(commitmentMode) libstate.COMPARE_INDEXES = true _, _, _, agg := newDomains(ctx, chainDb, stepSize, mode, trieVariant, logger) defer agg.Close() histTx, err := chainDb.BeginRo(ctx) must(err) defer histTx.Rollback() stateTx, err := stateDb.BeginRw(ctx) must(err) defer stateTx.Rollback() agg.SetTx(stateTx) defer agg.StartWrites().FinishWrites() latestBlock, latestTx, err := agg.SeekCommitment() if err != nil && startTxNum != 0 { return fmt.Errorf("failed to seek commitment to tx %d: %w", startTxNum, err) } if latestTx < startTxNum { return fmt.Errorf("latest available tx to start is %d and its less than start tx %d", latestTx, startTxNum) } if latestTx > 0 { logger.Info("aggregator files opened", "txn", latestTx, "block", latestBlock) } agg.SetTxNum(latestTx) r := ReaderWrapper4{ roTx: histTx, ac: agg.MakeContext(), } switch readDomain { case "account": for _, addr := range addrs { acc, err := r.ReadAccountData(libcommon.BytesToAddress(addr)) if err != nil { logger.Error("failed to read account", "addr", addr, "err", err) continue } fmt.Printf("%x: nonce=%d balance=%d code=%x root=%x\n", addr, acc.Nonce, acc.Balance.Uint64(), acc.CodeHash, acc.Root) } case "storage": for _, addr := range addrs { a, s := libcommon.BytesToAddress(addr[:length.Addr]), libcommon.BytesToHash(addr[length.Addr:]) st, err := r.ReadAccountStorage(a, 0, &s) if err != nil { logger.Error("failed to read storage", "addr", a.String(), "key", s.String(), "err", err) continue } fmt.Printf("%s %s -> %x\n", a.String(), s.String(), st) } case "code": for _, addr := range addrs { code, err := r.ReadAccountCode(libcommon.BytesToAddress(addr), 0, libcommon.Hash{}) if err != nil { logger.Error("failed to read code", "addr", addr, "err", err) continue } fmt.Printf("%s: %x\n", addr, code) } } return nil } // Implements StateReader and StateWriter type ReaderWrapper4 struct { roTx kv.Tx ac *libstate.AggregatorContext } type WriterWrapper4 struct { w *libstate.Aggregator } func (rw *ReaderWrapper4) SetTx(roTx kv.Tx, ctx *libstate.AggregatorContext) { rw.roTx = roTx rw.ac.Close() rw.ac = ctx } func (rw *ReaderWrapper4) ReadAccountData(address libcommon.Address) (*accounts.Account, error) { enc, err := rw.ac.ReadAccountData(address.Bytes(), rw.roTx) if err != nil { return nil, err } if len(enc) == 0 { return nil, nil } var a accounts.Account if err := accounts.DeserialiseV3(&a, enc); err != nil { return nil, err } return &a, nil } func (rw *ReaderWrapper4) ReadAccountStorage(address libcommon.Address, incarnation uint64, key *libcommon.Hash) ([]byte, error) { enc, err := rw.ac.ReadAccountStorage(address.Bytes(), key.Bytes(), rw.roTx) if err != nil { return nil, err } if enc == nil { return nil, nil } if len(enc) == 1 && enc[0] == 0 { return nil, nil } return enc, nil } func (rw *ReaderWrapper4) ReadAccountCode(address libcommon.Address, incarnation uint64, codeHash libcommon.Hash) ([]byte, error) { return rw.ac.ReadAccountCode(address.Bytes(), rw.roTx) } func (rw *ReaderWrapper4) ReadAccountCodeSize(address libcommon.Address, incarnation uint64, codeHash libcommon.Hash) (int, error) { return rw.ac.ReadAccountCodeSize(address.Bytes(), rw.roTx) } func (rw *ReaderWrapper4) ReadAccountIncarnation(address libcommon.Address) (uint64, error) { return 0, nil } func (ww *WriterWrapper4) UpdateAccountData(address libcommon.Address, original, account *accounts.Account) error { value := accounts.SerialiseV3(account) if err := ww.w.UpdateAccountData(address.Bytes(), value); err != nil { return err } return nil } func (ww *WriterWrapper4) UpdateAccountCode(address libcommon.Address, incarnation uint64, codeHash libcommon.Hash, code []byte) error { if err := ww.w.UpdateAccountCode(address.Bytes(), code); err != nil { return err } return nil } func (ww *WriterWrapper4) DeleteAccount(address libcommon.Address, original *accounts.Account) error { if err := ww.w.DeleteAccount(address.Bytes()); err != nil { return err } return nil } func (ww *WriterWrapper4) WriteAccountStorage(address libcommon.Address, incarnation uint64, key *libcommon.Hash, original, value *uint256.Int) error { if err := ww.w.WriteAccountStorage(address.Bytes(), key.Bytes(), value.Bytes()); err != nil { return err } return nil } func (ww *WriterWrapper4) CreateContract(address libcommon.Address) error { return nil } type stat4 struct { prevBlock uint64 blockNum uint64 hits uint64 misses uint64 hitMissRatio float64 blockSpeed float64 txSpeed float64 prevTxNum uint64 txNum uint64 prevTime time.Time mem runtime.MemStats startedAt time.Time } func (s *stat4) print(aStats libstate.FilesStats, logger log.Logger) { totalFiles := aStats.FilesCount totalDatSize := aStats.DataSize totalIdxSize := aStats.IdxSize logger.Info("Progress", "block", s.blockNum, "blk/s", s.blockSpeed, "tx", s.txNum, "txn/s", s.txSpeed, "state files", totalFiles, "total dat", libcommon.ByteCount(totalDatSize), "total idx", libcommon.ByteCount(totalIdxSize), "hit ratio", s.hitMissRatio, "hits+misses", s.hits+s.misses, "alloc", libcommon.ByteCount(s.mem.Alloc), "sys", libcommon.ByteCount(s.mem.Sys), ) } func (s *stat4) delta(blockNum, txNum uint64) *stat4 { currentTime := time.Now() dbg.ReadMemStats(&s.mem) interval := currentTime.Sub(s.prevTime).Seconds() s.blockNum = blockNum s.blockSpeed = float64(s.blockNum-s.prevBlock) / interval s.txNum = txNum s.txSpeed = float64(s.txNum-s.prevTxNum) / interval s.prevBlock = blockNum s.prevTxNum = txNum s.prevTime = currentTime if s.startedAt.IsZero() { s.startedAt = currentTime } total := s.hits + s.misses if total > 0 { s.hitMissRatio = float64(s.hits) / float64(total) } return s }