Skip to content

Commit

Permalink
Return PrunedError when trying to read unavailable historical data (#…
Browse files Browse the repository at this point in the history
…13014)

Certain RPC queries require access to historical data, however this data
may have been deleted in a full node due to pruning. To avoid unexpected
errors down the line it would be better to throw a more indicative error
earlier in the request handling flows when the `HistoryReaderV3` is
created.

---------

Co-authored-by: antonis19 <[email protected]>
  • Loading branch information
antonis19 and antonis19 authored Dec 16, 2024
1 parent 38482df commit 9841062
Show file tree
Hide file tree
Showing 8 changed files with 61 additions and 1 deletion.
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 @@ -44,6 +47,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 @@ -1730,6 +1730,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 @@ -1580,6 +1580,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.TemporalTx, txNumsReader rawdbv3.TxNumsReade
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

0 comments on commit 9841062

Please sign in to comment.