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  }