Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Return PrunedError when trying to read unavailable historical data #13014

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
23 changes: 23 additions & 0 deletions core/state/history_reader_v3.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package state

import (
"errors"
"fmt"

"github.com/erigontech/erigon-lib/common"
Expand All @@ -25,6 +26,8 @@ import (
"github.com/erigontech/erigon-lib/types/accounts"
)

var PrunedError = errors.New("old data not available due to pruning")

// HistoryReaderV3 Implements StateReader and StateWriter
type HistoryReaderV3 struct {
txNum uint64
Expand All @@ -50,6 +53,26 @@ func (hr *HistoryReaderV3) SetTxNum(txNum uint64) { hr.txNum = txNum }
func (hr *HistoryReaderV3) GetTxNum() uint64 { return hr.txNum }
func (hr *HistoryReaderV3) SetTrace(trace bool) { hr.trace = trace }

// Gets the txNum where Account, Storage and Code history begins.
// If the node is an archive node all history will be available therefore
// the result will be 0.
//
// For non-archive node old history files get deleted, so this number will vary
// but the goal is to know where the historical data begins.
func (hr *HistoryReaderV3) StateHistoryStartFrom() uint64 {
var earliestTxNum uint64 = 0
// get the first txnum where accounts, storage , and code are all available in history files
// This is max(HistoryStart(Accounts), HistoryStart(Storage), HistoryStart(Code))
stateDomainNames := []kv.Domain{kv.AccountsDomain, kv.StorageDomain, kv.CodeDomain}
for _, domainName := range stateDomainNames {
domainStartingTxNum := hr.ttx.HistoryStartFrom(domainName)
if domainStartingTxNum > earliestTxNum {
earliestTxNum = domainStartingTxNum
}
}
return earliestTxNum
}

func (hr *HistoryReaderV3) ReadSet() map[string]*state.KvList { return nil }
func (hr *HistoryReaderV3) ResetReadSet() {}
func (hr *HistoryReaderV3) DiscardReadList() {}
Expand Down
3 changes: 3 additions & 0 deletions erigon-lib/kv/kv_interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,9 @@ type TemporalTx interface {
Tx
TemporalGetter

// return the earliest known txnum in history of a given domain
HistoryStartFrom(domainName Domain) uint64

// DomainGetAsOf - state as of given `ts`
// Example: GetAsOf(Account, key, txNum) - retuns account's value before `txNum` transaction changed it
// Means if you want re-execute `txNum` on historical state - do `DomainGetAsOf(key, txNum)` to read state
Expand Down
4 changes: 4 additions & 0 deletions erigon-lib/kv/membatchwithdb/memory_mutation.go
Original file line number Diff line number Diff line change
Expand Up @@ -741,3 +741,7 @@ func (m *MemoryMutation) HistoryRange(name kv.Domain, fromTs, toTs int, asc orde
panic("not supported")
// return m.db.(kv.TemporalTx).HistoryRange(name, fromTs, toTs, asc, limit)
}

func (m *MemoryMutation) HistoryStartFrom(name kv.Domain) uint64 {
return m.db.(kv.TemporalTx).HistoryStartFrom(name)
}
6 changes: 6 additions & 0 deletions erigon-lib/kv/remotedb/kv_remote.go
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,12 @@ func (c *remoteCursorDupSort) PrevNoDup() ([]byte, []byte, error) { return c.pre
func (c *remoteCursorDupSort) LastDup() ([]byte, error) { return c.lastDup() }

// Temporal Methods

func (tx *tx) HistoryStartFrom(name kv.Domain) uint64 {
// TODO: not yet implemented, return 0 for now
return 0
}

func (tx *tx) GetAsOf(name kv.Domain, k []byte, ts uint64) (v []byte, ok bool, err error) {
reply, err := tx.db.remoteKV.GetLatest(tx.ctx, &remote.GetLatestReq{TxId: tx.id, Table: name.String(), K: k, Ts: ts})
if err != nil {
Expand Down
4 changes: 4 additions & 0 deletions erigon-lib/kv/temporal/kv_temporal.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,10 @@ func (tx *Tx) Commit() error {
return mdbxTx.Commit()
}

func (tx *Tx) HistoryStartFrom(name kv.Domain) uint64 {
return tx.filesTx.HistoryStartFrom(name)
}

func (tx *Tx) RangeAsOf(name kv.Domain, fromKey, toKey []byte, asOfTs uint64, asc order.By, limit int) (stream.KV, error) {
it, err := tx.filesTx.RangeAsOf(tx.ctx, tx.MdbxTx, name, fromKey, toKey, asOfTs, asc, limit)
if err != nil {
Expand Down
5 changes: 5 additions & 0 deletions erigon-lib/state/aggregator.go
Original file line number Diff line number Diff line change
Expand Up @@ -1729,6 +1729,11 @@ func (a *Aggregator) BuildFilesInBackground(txNum uint64) chan struct{} {
return fin
}

// Returns the first known txNum found in history files of a given domain
func (ac *AggregatorRoTx) HistoryStartFrom(domainName kv.Domain) uint64 {
return ac.d[domainName].HistoryStartFrom()
}

func (ac *AggregatorRoTx) IndexRange(name kv.InvertedIdx, k []byte, fromTs, toTs int, asc order.By, limit int, tx kv.Tx) (timestamps stream.U64, err error) {
switch name {
case kv.AccountsHistoryIdx:
Expand Down
8 changes: 8 additions & 0 deletions erigon-lib/state/domain.go
Original file line number Diff line number Diff line change
Expand Up @@ -1584,6 +1584,14 @@ func (dt *DomainRoTx) getFromFiles(filekey []byte, maxTxNum uint64) (v []byte, f
return nil, false, 0, 0, nil
}

// Returns the first txNum from available history
func (dt *DomainRoTx) HistoryStartFrom() uint64 {
if len(dt.ht.files) == 0 {
return 0
}
return dt.ht.files[0].startTxNum
}

func (dt *DomainRoTx) GetAsOfFile(key []byte, txNum uint64) ([]byte, bool, error) {
var v []byte
var foundStep uint64
Expand Down
9 changes: 8 additions & 1 deletion turbo/rpchelper/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,14 @@ func CreateHistoryStateReader(tx kv.Tx, txNumsReader rawdbv3.TxNumsReader, block
if err != nil {
return nil, err
}
r.SetTxNum(uint64(int(minTxNum) + txnIndex + /* 1 system txNum in beginning of block */ 1))
txNum := uint64(int(minTxNum) + txnIndex + /* 1 system txNum in beginning of block */ 1)
earliestTxNum := r.StateHistoryStartFrom()
if txNum < earliestTxNum {
// data available only starting from earliestTxNum, throw error to avoid unintended
// consequences of using this StateReader
return r, state.PrunedError
}
r.SetTxNum(txNum)
return r, nil
}

Expand Down
Loading