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 }