github.com/ethereum/go-ethereum@v1.16.1/triedb/pathdb/history_inspect.go (about)

     1  // Copyright 2024 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package pathdb
    18  
    19  import (
    20  	"fmt"
    21  	"time"
    22  
    23  	"github.com/ethereum/go-ethereum/common"
    24  	"github.com/ethereum/go-ethereum/crypto"
    25  	"github.com/ethereum/go-ethereum/ethdb"
    26  	"github.com/ethereum/go-ethereum/log"
    27  )
    28  
    29  // HistoryStats wraps the history inspection statistics.
    30  type HistoryStats struct {
    31  	Start   uint64   // Block number of the first queried history
    32  	End     uint64   // Block number of the last queried history
    33  	Blocks  []uint64 // Blocks refers to the list of block numbers in which the state is mutated
    34  	Origins [][]byte // Origins refers to the original value of the state before its mutation
    35  }
    36  
    37  // sanitizeRange limits the given range to fit within the local history store.
    38  func sanitizeRange(start, end uint64, freezer ethdb.AncientReader) (uint64, uint64, error) {
    39  	// Load the id of the first history object in local store.
    40  	tail, err := freezer.Tail()
    41  	if err != nil {
    42  		return 0, 0, err
    43  	}
    44  	first := tail + 1
    45  	if start != 0 && start > first {
    46  		first = start
    47  	}
    48  	// Load the id of the last history object in local store.
    49  	head, err := freezer.Ancients()
    50  	if err != nil {
    51  		return 0, 0, err
    52  	}
    53  	last := head - 1
    54  	if end != 0 && end < last {
    55  		last = end
    56  	}
    57  	// Make sure the range is valid
    58  	if first >= last {
    59  		return 0, 0, fmt.Errorf("range is invalid, first: %d, last: %d", first, last)
    60  	}
    61  	return first, last, nil
    62  }
    63  
    64  func inspectHistory(freezer ethdb.AncientReader, start, end uint64, onHistory func(*history, *HistoryStats)) (*HistoryStats, error) {
    65  	var (
    66  		stats  = &HistoryStats{}
    67  		init   = time.Now()
    68  		logged = time.Now()
    69  	)
    70  	start, end, err := sanitizeRange(start, end, freezer)
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  	for id := start; id <= end; id += 1 {
    75  		// The entire history object is decoded, although it's unnecessary for
    76  		// account inspection. TODO(rjl493456442) optimization is worthwhile.
    77  		h, err := readHistory(freezer, id)
    78  		if err != nil {
    79  			return nil, err
    80  		}
    81  		if id == start {
    82  			stats.Start = h.meta.block
    83  		}
    84  		if id == end {
    85  			stats.End = h.meta.block
    86  		}
    87  		onHistory(h, stats)
    88  
    89  		if time.Since(logged) > time.Second*8 {
    90  			logged = time.Now()
    91  			eta := float64(time.Since(init)) / float64(id-start+1) * float64(end-id)
    92  			log.Info("Inspecting state history", "checked", id-start+1, "left", end-id, "elapsed", common.PrettyDuration(time.Since(init)), "eta", common.PrettyDuration(eta))
    93  		}
    94  	}
    95  	log.Info("Inspected state history", "total", end-start+1, "elapsed", common.PrettyDuration(time.Since(init)))
    96  	return stats, nil
    97  }
    98  
    99  // accountHistory inspects the account history within the range.
   100  func accountHistory(freezer ethdb.AncientReader, address common.Address, start, end uint64) (*HistoryStats, error) {
   101  	return inspectHistory(freezer, start, end, func(h *history, stats *HistoryStats) {
   102  		blob, exists := h.accounts[address]
   103  		if !exists {
   104  			return
   105  		}
   106  		stats.Blocks = append(stats.Blocks, h.meta.block)
   107  		stats.Origins = append(stats.Origins, blob)
   108  	})
   109  }
   110  
   111  // storageHistory inspects the storage history within the range.
   112  func storageHistory(freezer ethdb.AncientReader, address common.Address, slot common.Hash, start uint64, end uint64) (*HistoryStats, error) {
   113  	slotHash := crypto.Keccak256Hash(slot.Bytes())
   114  	return inspectHistory(freezer, start, end, func(h *history, stats *HistoryStats) {
   115  		slots, exists := h.storages[address]
   116  		if !exists {
   117  			return
   118  		}
   119  		key := slotHash
   120  		if h.meta.version != stateHistoryV0 {
   121  			key = slot
   122  		}
   123  		blob, exists := slots[key]
   124  		if !exists {
   125  			return
   126  		}
   127  		stats.Blocks = append(stats.Blocks, h.meta.block)
   128  		stats.Origins = append(stats.Origins, blob)
   129  	})
   130  }
   131  
   132  // historyRange returns the block number range of local state histories.
   133  func historyRange(freezer ethdb.AncientReader) (uint64, uint64, error) {
   134  	// Load the id of the first history object in local store.
   135  	tail, err := freezer.Tail()
   136  	if err != nil {
   137  		return 0, 0, err
   138  	}
   139  	first := tail + 1
   140  
   141  	// Load the id of the last history object in local store.
   142  	head, err := freezer.Ancients()
   143  	if err != nil {
   144  		return 0, 0, err
   145  	}
   146  	last := head - 1
   147  
   148  	fh, err := readHistory(freezer, first)
   149  	if err != nil {
   150  		return 0, 0, err
   151  	}
   152  	lh, err := readHistory(freezer, last)
   153  	if err != nil {
   154  		return 0, 0, err
   155  	}
   156  	return fh.meta.block, lh.meta.block, nil
   157  }