github.1485827954.workers.dev/ethereum/go-ethereum@v1.14.3/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/ethdb"
    25  	"github.com/ethereum/go-ethereum/log"
    26  )
    27  
    28  // HistoryStats wraps the history inspection statistics.
    29  type HistoryStats struct {
    30  	Start   uint64   // Block number of the first queried history
    31  	End     uint64   // Block number of the last queried history
    32  	Blocks  []uint64 // Blocks refers to the list of block numbers in which the state is mutated
    33  	Origins [][]byte // Origins refers to the original value of the state before its mutation
    34  }
    35  
    36  // sanitizeRange limits the given range to fit within the local history store.
    37  func sanitizeRange(start, end uint64, freezer ethdb.AncientReader) (uint64, uint64, error) {
    38  	// Load the id of the first history object in local store.
    39  	tail, err := freezer.Tail()
    40  	if err != nil {
    41  		return 0, 0, err
    42  	}
    43  	first := tail + 1
    44  	if start != 0 && start > first {
    45  		first = start
    46  	}
    47  	// Load the id of the last history object in local store.
    48  	head, err := freezer.Ancients()
    49  	if err != nil {
    50  		return 0, 0, err
    51  	}
    52  	last := head - 1
    53  	if end != 0 && end < last {
    54  		last = end
    55  	}
    56  	// Make sure the range is valid
    57  	if first >= last {
    58  		return 0, 0, fmt.Errorf("range is invalid, first: %d, last: %d", first, last)
    59  	}
    60  	return first, last, nil
    61  }
    62  
    63  func inspectHistory(freezer ethdb.AncientReader, start, end uint64, onHistory func(*history, *HistoryStats)) (*HistoryStats, error) {
    64  	var (
    65  		stats  = &HistoryStats{}
    66  		init   = time.Now()
    67  		logged = time.Now()
    68  	)
    69  	start, end, err := sanitizeRange(start, end, freezer)
    70  	if err != nil {
    71  		return nil, err
    72  	}
    73  	for id := start; id <= end; id += 1 {
    74  		// The entire history object is decoded, although it's unnecessary for
    75  		// account inspection. TODO(rjl493456442) optimization is worthwhile.
    76  		h, err := readHistory(freezer, id)
    77  		if err != nil {
    78  			return nil, err
    79  		}
    80  		if id == start {
    81  			stats.Start = h.meta.block
    82  		}
    83  		if id == end {
    84  			stats.End = h.meta.block
    85  		}
    86  		onHistory(h, stats)
    87  
    88  		if time.Since(logged) > time.Second*8 {
    89  			logged = time.Now()
    90  			eta := float64(time.Since(init)) / float64(id-start+1) * float64(end-id)
    91  			log.Info("Inspecting state history", "checked", id-start+1, "left", end-id, "elapsed", common.PrettyDuration(time.Since(init)), "eta", common.PrettyDuration(eta))
    92  		}
    93  	}
    94  	log.Info("Inspected state history", "total", end-start+1, "elapsed", common.PrettyDuration(time.Since(init)))
    95  	return stats, nil
    96  }
    97  
    98  // accountHistory inspects the account history within the range.
    99  func accountHistory(freezer ethdb.AncientReader, address common.Address, start, end uint64) (*HistoryStats, error) {
   100  	return inspectHistory(freezer, start, end, func(h *history, stats *HistoryStats) {
   101  		blob, exists := h.accounts[address]
   102  		if !exists {
   103  			return
   104  		}
   105  		stats.Blocks = append(stats.Blocks, h.meta.block)
   106  		stats.Origins = append(stats.Origins, blob)
   107  	})
   108  }
   109  
   110  // storageHistory inspects the storage history within the range.
   111  func storageHistory(freezer ethdb.AncientReader, address common.Address, slot common.Hash, start uint64, end uint64) (*HistoryStats, error) {
   112  	return inspectHistory(freezer, start, end, func(h *history, stats *HistoryStats) {
   113  		slots, exists := h.storages[address]
   114  		if !exists {
   115  			return
   116  		}
   117  		blob, exists := slots[slot]
   118  		if !exists {
   119  			return
   120  		}
   121  		stats.Blocks = append(stats.Blocks, h.meta.block)
   122  		stats.Origins = append(stats.Origins, blob)
   123  	})
   124  }
   125  
   126  // historyRange returns the block number range of local state histories.
   127  func historyRange(freezer ethdb.AncientReader) (uint64, uint64, error) {
   128  	// Load the id of the first history object in local store.
   129  	tail, err := freezer.Tail()
   130  	if err != nil {
   131  		return 0, 0, err
   132  	}
   133  	first := tail + 1
   134  
   135  	// Load the id of the last history object in local store.
   136  	head, err := freezer.Ancients()
   137  	if err != nil {
   138  		return 0, 0, err
   139  	}
   140  	last := head - 1
   141  
   142  	fh, err := readHistory(freezer, first)
   143  	if err != nil {
   144  		return 0, 0, err
   145  	}
   146  	lh, err := readHistory(freezer, last)
   147  	if err != nil {
   148  		return 0, 0, err
   149  	}
   150  	return fh.meta.block, lh.meta.block, nil
   151  }