erigon-pulse/ethdb/prune/storage_mode.go
2021-07-31 12:24:20 +07:00

313 lines
6.6 KiB
Go

package prune
import (
"encoding/binary"
"fmt"
"math"
"github.com/ledgerwatch/erigon-lib/kv"
"github.com/ledgerwatch/erigon/params"
)
var DefaultMode = Mode{
Initialised: true,
History: math.MaxUint64, // all off
Receipts: math.MaxUint64,
TxIndex: math.MaxUint64,
CallTraces: math.MaxUint64,
Experiments: Experiments{}, // all off
}
type Experiments struct {
TEVM bool
}
func FromCli(flags string, exactHistory, exactReceipts, exactTxIndex, exactCallTraces uint64, experiments []string) (Mode, error) {
mode := DefaultMode
if flags == "default" || flags == "disabled" {
return DefaultMode, nil
}
mode.Initialised = true
for _, flag := range flags {
switch flag {
case 'h':
mode.History = params.FullImmutabilityThreshold
case 'r':
mode.Receipts = params.FullImmutabilityThreshold
case 't':
mode.TxIndex = params.FullImmutabilityThreshold
case 'c':
mode.CallTraces = params.FullImmutabilityThreshold
default:
return DefaultMode, fmt.Errorf("unexpected flag found: %c", flag)
}
}
if exactHistory > 0 {
mode.History = Distance(exactHistory)
}
if exactReceipts > 0 {
mode.Receipts = Distance(exactReceipts)
}
if exactTxIndex > 0 {
mode.TxIndex = Distance(exactTxIndex)
}
if exactCallTraces > 0 {
mode.CallTraces = Distance(exactCallTraces)
}
for _, ex := range experiments {
switch ex {
case "tevm":
mode.Experiments.TEVM = true
case "":
// skip
default:
return DefaultMode, fmt.Errorf("unexpected experiment found: %s", ex)
}
}
return mode, nil
}
func Get(db kv.Getter) (Mode, error) {
prune := DefaultMode
prune.Initialised = true
v, err := db.GetOne(kv.DatabaseInfo, kv.PruneDistanceHistory)
if err != nil {
return prune, err
}
if v != nil {
prune.History = Distance(binary.BigEndian.Uint64(v))
} else {
prune.History = math.MaxUint64
}
v, err = db.GetOne(kv.DatabaseInfo, kv.PruneDistanceReceipts)
if err != nil {
return prune, err
}
if v != nil {
prune.Receipts = Distance(binary.BigEndian.Uint64(v))
} else {
prune.Receipts = math.MaxUint64
}
v, err = db.GetOne(kv.DatabaseInfo, kv.PruneDistanceTxIndex)
if err != nil {
return prune, err
}
if v != nil {
prune.TxIndex = Distance(binary.BigEndian.Uint64(v))
} else {
prune.TxIndex = math.MaxUint64
}
v, err = db.GetOne(kv.DatabaseInfo, kv.PruneDistanceCallTraces)
if err != nil {
return prune, err
}
if v != nil {
prune.CallTraces = Distance(binary.BigEndian.Uint64(v))
} else {
prune.CallTraces = math.MaxUint64
}
v, err = db.GetOne(kv.DatabaseInfo, kv.StorageModeTEVM)
if err != nil {
return prune, err
}
prune.Experiments.TEVM = len(v) == 1 && v[0] == 1
return prune, nil
}
type Mode struct {
Initialised bool // Set when the values are initialised (not default)
History Distance
Receipts Distance
TxIndex Distance
CallTraces Distance
Experiments Experiments
}
// Distance amount of blocks to keep in DB
// but manual manipulation with such distance is very unsafe
// for example:
// deleteUntil := currentStageProgress - pruningDistance
// may delete whole db - because of uint64 underflow when pruningDistance > currentStageProgress
type Distance uint64
func (p Distance) Enabled() bool { return p != math.MaxUint64 }
func (p Distance) PruneTo(stageHead uint64) uint64 {
if p == 0 {
panic("pruning distance were not set")
}
if uint64(p) > stageHead {
return 0
}
return stageHead - uint64(p)
}
func (m Mode) String() string {
if !m.Initialised {
return "default"
}
long := ""
short := "--prune="
if m.History.Enabled() {
if m.History == params.FullImmutabilityThreshold {
short += "h"
} else {
long += fmt.Sprintf(" --prune.h.older=%d", m.History)
}
}
if m.Receipts.Enabled() {
if m.Receipts == params.FullImmutabilityThreshold {
short += "r"
} else {
long += fmt.Sprintf(" --prune.r.older=%d", m.Receipts)
}
}
if m.TxIndex.Enabled() {
if m.TxIndex == params.FullImmutabilityThreshold {
short += "t"
} else {
long += fmt.Sprintf(" --prune.t.older=%d", m.TxIndex)
}
}
if m.CallTraces.Enabled() {
if m.CallTraces == params.FullImmutabilityThreshold {
short += "c"
} else {
long += fmt.Sprintf(" --prune.c.older=%d", m.CallTraces)
}
}
if m.Experiments.TEVM {
long += " --experiments.tevm=enabled"
}
return short + long
}
func Override(db kv.RwTx, sm Mode) error {
var (
err error
)
err = setDistance(db, kv.PruneDistanceHistory, sm.History)
if err != nil {
return err
}
err = setDistance(db, kv.PruneDistanceReceipts, sm.Receipts)
if err != nil {
return err
}
err = setDistance(db, kv.PruneDistanceTxIndex, sm.TxIndex)
if err != nil {
return err
}
err = setDistance(db, kv.PruneDistanceCallTraces, sm.CallTraces)
if err != nil {
return err
}
err = setMode(db, kv.StorageModeTEVM, sm.Experiments.TEVM)
if err != nil {
return err
}
return nil
}
func SetIfNotExist(db kv.GetPut, pm Mode) error {
var (
err error
)
if !pm.Initialised {
pm = DefaultMode
}
err = setDistanceOnEmpty(db, kv.PruneDistanceHistory, pm.History)
if err != nil {
return err
}
err = setDistanceOnEmpty(db, kv.PruneDistanceReceipts, pm.Receipts)
if err != nil {
return err
}
err = setDistanceOnEmpty(db, kv.PruneDistanceTxIndex, pm.TxIndex)
if err != nil {
return err
}
err = setDistanceOnEmpty(db, kv.PruneDistanceCallTraces, pm.CallTraces)
if err != nil {
return err
}
err = setModeOnEmpty(db, kv.StorageModeTEVM, pm.Experiments.TEVM)
if err != nil {
return err
}
return nil
}
func setDistance(db kv.Putter, key []byte, distance Distance) error {
v := make([]byte, 8)
binary.BigEndian.PutUint64(v, uint64(distance))
if err := db.Put(kv.DatabaseInfo, key, v); err != nil {
return err
}
return nil
}
func setDistanceOnEmpty(db kv.GetPut, key []byte, distance Distance) error {
mode, err := db.GetOne(kv.DatabaseInfo, key)
if err != nil {
return err
}
if len(mode) == 0 || binary.BigEndian.Uint64(mode) == math.MaxUint64 {
v := make([]byte, 8)
binary.BigEndian.PutUint64(v, uint64(distance))
if err = db.Put(kv.DatabaseInfo, key, v); err != nil {
return err
}
}
return nil
}
func setMode(db kv.RwTx, key []byte, currentValue bool) error {
val := []byte{2}
if currentValue {
val = []byte{1}
}
if err := db.Put(kv.DatabaseInfo, key, val); err != nil {
return err
}
return nil
}
func setModeOnEmpty(db kv.GetPut, key []byte, currentValue bool) error {
mode, err := db.GetOne(kv.DatabaseInfo, key)
if err != nil {
return err
}
if len(mode) == 0 {
val := []byte{2}
if currentValue {
val = []byte{1}
}
if err = db.Put(kv.DatabaseInfo, key, val); err != nil {
return err
}
}
return nil
}