github.com/ledgerwatch/erigon-lib@v1.0.0/state/state_recon.go (about) 1 /* 2 Copyright 2022 Erigon contributors 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package state 18 19 import ( 20 "bytes" 21 "encoding/binary" 22 23 "github.com/ledgerwatch/erigon-lib/compress" 24 "github.com/ledgerwatch/erigon-lib/recsplit" 25 "github.com/ledgerwatch/erigon-lib/recsplit/eliasfano32" 26 ) 27 28 // Algorithms for reconstituting the state from state history 29 30 type ReconItem struct { 31 g *compress.Getter 32 key []byte 33 txNum uint64 34 startTxNum uint64 35 endTxNum uint64 36 startOffset uint64 37 lastOffset uint64 38 } 39 40 type ReconHeap []*ReconItem 41 42 func (rh ReconHeap) Len() int { 43 return len(rh) 44 } 45 46 // Less (part of heap.Interface) compares two links. For persisted links, those with the lower block heights get evicted first. This means that more recently persisted links are preferred. 47 // For non-persisted links, those with the highest block heights get evicted first. This is to prevent "holes" in the block heights that may cause inability to 48 // insert headers in the ascending order of their block heights. 49 func (rh ReconHeap) Less(i, j int) bool { 50 c := bytes.Compare(rh[i].key, rh[j].key) 51 if c == 0 { 52 return rh[i].txNum < rh[j].txNum 53 } 54 return c < 0 55 } 56 57 // Swap (part of heap.Interface) moves two links in the queue into each other's places. Note that each link has idx attribute that is getting adjusted during 58 // the swap. The idx attribute allows the removal of links from the middle of the queue (in case if links are getting invalidated due to 59 // failed verification of unavailability of parent headers) 60 func (rh ReconHeap) Swap(i, j int) { 61 rh[i], rh[j] = rh[j], rh[i] 62 } 63 64 // Push (part of heap.Interface) places a new link onto the end of queue. Note that idx attribute is set to the correct position of the new link 65 func (rh *ReconHeap) Push(x interface{}) { 66 // Push and Pop use pointer receivers because they modify the slice's length, 67 // not just its contents. 68 l := x.(*ReconItem) 69 *rh = append(*rh, l) 70 } 71 72 // Pop (part of heap.Interface) removes the first link from the queue 73 func (rh *ReconHeap) Pop() interface{} { 74 old := *rh 75 n := len(old) 76 x := old[n-1] 77 old[n-1] = nil 78 *rh = old[0 : n-1] 79 return x 80 } 81 82 type ReconHeapOlderFirst struct { 83 ReconHeap 84 } 85 86 func (rh ReconHeapOlderFirst) Less(i, j int) bool { 87 c := bytes.Compare(rh.ReconHeap[i].key, rh.ReconHeap[j].key) 88 if c == 0 { 89 return rh.ReconHeap[i].txNum >= rh.ReconHeap[j].txNum 90 } 91 return c < 0 92 } 93 94 type ScanIteratorInc struct { 95 g *compress.Getter 96 key []byte 97 nextTxNum uint64 98 hasNext bool 99 } 100 101 func (sii *ScanIteratorInc) advance() { 102 if !sii.hasNext { 103 return 104 } 105 if sii.key == nil { 106 sii.hasNext = false 107 return 108 } 109 val, _ := sii.g.NextUncompressed() 110 max := eliasfano32.Max(val) 111 sii.nextTxNum = max 112 if sii.g.HasNext() { 113 sii.key, _ = sii.g.NextUncompressed() 114 } else { 115 sii.key = nil 116 } 117 } 118 119 func (sii *ScanIteratorInc) HasNext() bool { 120 return sii.hasNext 121 } 122 123 func (sii *ScanIteratorInc) Next() (uint64, error) { 124 n := sii.nextTxNum 125 sii.advance() 126 return n, nil 127 } 128 129 func (hs *HistoryStep) iterateTxs() *ScanIteratorInc { 130 var sii ScanIteratorInc 131 sii.g = hs.indexFile.getter 132 sii.g.Reset(0) 133 if sii.g.HasNext() { 134 sii.key, _ = sii.g.NextUncompressed() 135 sii.hasNext = true 136 } else { 137 sii.hasNext = false 138 } 139 sii.advance() 140 return &sii 141 } 142 143 type HistoryIteratorInc struct { 144 uptoTxNum uint64 145 indexG *compress.Getter 146 historyG *compress.Getter 147 r *recsplit.IndexReader 148 key []byte 149 nextKey []byte 150 nextVal []byte 151 hasNext bool 152 compressVals bool 153 } 154 155 func (hs *HistoryStep) interateHistoryBeforeTxNum(txNum uint64) *HistoryIteratorInc { 156 var hii HistoryIteratorInc 157 hii.indexG = hs.indexFile.getter 158 hii.historyG = hs.historyFile.getter 159 hii.r = hs.historyFile.reader 160 hii.compressVals = hs.compressVals 161 hii.indexG.Reset(0) 162 if hii.indexG.HasNext() { 163 hii.key, _ = hii.indexG.NextUncompressed() 164 hii.uptoTxNum = txNum 165 hii.hasNext = true 166 } else { 167 hii.hasNext = false 168 } 169 hii.advance() 170 return &hii 171 } 172 173 func (hii *HistoryIteratorInc) advance() { 174 if !hii.hasNext { 175 return 176 } 177 if hii.key == nil { 178 hii.hasNext = false 179 return 180 } 181 hii.nextKey = nil 182 for hii.nextKey == nil && hii.key != nil { 183 val, _ := hii.indexG.NextUncompressed() 184 ef, _ := eliasfano32.ReadEliasFano(val) 185 if n, ok := ef.Search(hii.uptoTxNum); ok { 186 var txKey [8]byte 187 binary.BigEndian.PutUint64(txKey[:], n) 188 offset := hii.r.Lookup2(txKey[:], hii.key) 189 hii.historyG.Reset(offset) 190 hii.nextKey = hii.key 191 if hii.compressVals { 192 hii.nextVal, _ = hii.historyG.Next(nil) 193 } else { 194 hii.nextVal, _ = hii.historyG.NextUncompressed() 195 } 196 } 197 if hii.indexG.HasNext() { 198 hii.key, _ = hii.indexG.NextUncompressed() 199 } else { 200 hii.key = nil 201 } 202 } 203 if hii.nextKey == nil { 204 hii.hasNext = false 205 } 206 } 207 208 func (hii *HistoryIteratorInc) HasNext() bool { 209 return hii.hasNext 210 } 211 212 func (hii *HistoryIteratorInc) Next() ([]byte, []byte, error) { 213 k, v := hii.nextKey, hii.nextVal 214 hii.advance() 215 return k, v, nil 216 }