mirror of
https://gitlab.com/pulsechaincom/erigon-pulse.git
synced 2024-12-22 19:50:36 +00:00
f9ab76f9ef
From what I understand, this should be sufficient to add `--prune=r` support for Gnosis and Chiado.
440 lines
10 KiB
Go
440 lines
10 KiB
Go
package prune
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"errors"
|
|
"fmt"
|
|
"math"
|
|
"reflect"
|
|
"strings"
|
|
|
|
"github.com/ledgerwatch/erigon-lib/kv"
|
|
"github.com/ledgerwatch/erigon/params"
|
|
"github.com/ledgerwatch/log/v3"
|
|
)
|
|
|
|
var DefaultMode = Mode{
|
|
Initialised: true,
|
|
History: Distance(math.MaxUint64), // all off
|
|
Receipts: Distance(math.MaxUint64),
|
|
TxIndex: Distance(math.MaxUint64),
|
|
CallTraces: Distance(math.MaxUint64),
|
|
Experiments: Experiments{}, // all off
|
|
}
|
|
|
|
var (
|
|
mainnetDepositContractBlock uint64 = 11052984
|
|
sepoliaDepositContractBlock uint64 = 1273020
|
|
goerliDepositContractBlock uint64 = 4367322
|
|
gnosisDepositContractBlock uint64 = 19475089
|
|
chiadoDepositContractBlock uint64 = 155530
|
|
)
|
|
|
|
type Experiments struct {
|
|
}
|
|
|
|
func FromCli(chainId uint64, flags string, exactHistory, exactReceipts, exactTxIndex, exactCallTraces,
|
|
beforeH, beforeR, beforeT, beforeC uint64, experiments []string) (Mode, error) {
|
|
mode := DefaultMode
|
|
|
|
if flags != "default" && flags != "disabled" {
|
|
for _, flag := range flags {
|
|
switch flag {
|
|
case 'h':
|
|
mode.History = Distance(params.FullImmutabilityThreshold)
|
|
case 'r':
|
|
mode.Receipts = Distance(params.FullImmutabilityThreshold)
|
|
case 't':
|
|
mode.TxIndex = Distance(params.FullImmutabilityThreshold)
|
|
case 'c':
|
|
mode.CallTraces = Distance(params.FullImmutabilityThreshold)
|
|
default:
|
|
return DefaultMode, fmt.Errorf("unexpected flag found: %c", flag)
|
|
}
|
|
}
|
|
}
|
|
|
|
pruneBlockBefore := pruneBlockDefault(chainId)
|
|
|
|
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)
|
|
}
|
|
|
|
if beforeH > 0 {
|
|
mode.History = Before(beforeH)
|
|
}
|
|
if beforeR > 0 {
|
|
if pruneBlockBefore != 0 {
|
|
log.Warn("specifying prune.before.r might break CL compatibility")
|
|
if beforeR > pruneBlockBefore {
|
|
log.Warn("the specified prune.before.r block number is higher than the deposit contract contract block number", "highest block number", pruneBlockBefore)
|
|
}
|
|
}
|
|
mode.Receipts = Before(beforeR)
|
|
} else if exactReceipts == 0 && mode.Receipts.Enabled() && pruneBlockBefore != 0 {
|
|
// Default --prune=r to pruning receipts before the Beacon Chain genesis
|
|
mode.Receipts = Before(pruneBlockBefore)
|
|
}
|
|
if beforeT > 0 {
|
|
mode.TxIndex = Before(beforeT)
|
|
}
|
|
if beforeC > 0 {
|
|
mode.CallTraces = Before(beforeC)
|
|
}
|
|
|
|
for _, ex := range experiments {
|
|
switch ex {
|
|
case "":
|
|
// skip
|
|
default:
|
|
return DefaultMode, fmt.Errorf("unexpected experiment found: %s", ex)
|
|
}
|
|
}
|
|
return mode, nil
|
|
}
|
|
|
|
func pruneBlockDefault(chainId uint64) uint64 {
|
|
switch chainId {
|
|
case 1 /* mainnet */ :
|
|
return mainnetDepositContractBlock
|
|
case 11155111 /* sepolia */ :
|
|
return sepoliaDepositContractBlock
|
|
case 5 /* goerli */ :
|
|
return goerliDepositContractBlock
|
|
case 10200 /* chiado */ :
|
|
return chiadoDepositContractBlock
|
|
case 100 /* gnosis */ :
|
|
return gnosisDepositContractBlock
|
|
}
|
|
|
|
return 0
|
|
}
|
|
|
|
func Get(db kv.Getter) (Mode, error) {
|
|
prune := DefaultMode
|
|
prune.Initialised = true
|
|
|
|
blockAmount, err := get(db, kv.PruneHistory)
|
|
if err != nil {
|
|
return prune, err
|
|
}
|
|
if blockAmount != nil {
|
|
prune.History = blockAmount
|
|
}
|
|
|
|
blockAmount, err = get(db, kv.PruneReceipts)
|
|
if err != nil {
|
|
return prune, err
|
|
}
|
|
if blockAmount != nil {
|
|
prune.Receipts = blockAmount
|
|
}
|
|
|
|
blockAmount, err = get(db, kv.PruneTxIndex)
|
|
if err != nil {
|
|
return prune, err
|
|
}
|
|
if blockAmount != nil {
|
|
prune.TxIndex = blockAmount
|
|
}
|
|
|
|
blockAmount, err = get(db, kv.PruneCallTraces)
|
|
if err != nil {
|
|
return prune, err
|
|
}
|
|
if blockAmount != nil {
|
|
prune.CallTraces = blockAmount
|
|
}
|
|
|
|
return prune, nil
|
|
}
|
|
|
|
type Mode struct {
|
|
Initialised bool // Set when the values are initialised (not default)
|
|
History BlockAmount
|
|
Receipts BlockAmount
|
|
TxIndex BlockAmount
|
|
CallTraces BlockAmount
|
|
Experiments Experiments
|
|
}
|
|
|
|
type BlockAmount interface {
|
|
PruneTo(stageHead uint64) uint64
|
|
Enabled() bool
|
|
toValue() uint64
|
|
dbType() []byte
|
|
useDefaultValue() bool
|
|
}
|
|
|
|
// 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) toValue() uint64 { return uint64(p) }
|
|
func (p Distance) useDefaultValue() bool { return uint64(p) == params.FullImmutabilityThreshold }
|
|
func (p Distance) dbType() []byte { return kv.PruneTypeOlder }
|
|
|
|
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)
|
|
}
|
|
|
|
// Before number after which keep in DB
|
|
type Before uint64
|
|
|
|
func (b Before) Enabled() bool { return b > 0 }
|
|
func (b Before) toValue() uint64 { return uint64(b) }
|
|
func (b Before) useDefaultValue() bool { return uint64(b) == 0 }
|
|
func (b Before) dbType() []byte { return kv.PruneTypeBefore }
|
|
|
|
func (b Before) PruneTo(uint64) uint64 {
|
|
if b == 0 {
|
|
return uint64(b)
|
|
}
|
|
|
|
return uint64(b) - 1
|
|
}
|
|
|
|
func (m Mode) String() string {
|
|
if !m.Initialised {
|
|
return "default"
|
|
}
|
|
const defaultVal uint64 = params.FullImmutabilityThreshold
|
|
long := ""
|
|
short := ""
|
|
if m.History.Enabled() {
|
|
if m.History.useDefaultValue() {
|
|
short += fmt.Sprintf(" --prune.h.older=%d", defaultVal)
|
|
} else {
|
|
long += fmt.Sprintf(" --prune.h.%s=%d", m.History.dbType(), m.History.toValue())
|
|
}
|
|
}
|
|
if m.Receipts.Enabled() {
|
|
if m.Receipts.useDefaultValue() {
|
|
short += fmt.Sprintf(" --prune.r.older=%d", defaultVal)
|
|
} else {
|
|
long += fmt.Sprintf(" --prune.r.%s=%d", m.Receipts.dbType(), m.Receipts.toValue())
|
|
}
|
|
}
|
|
if m.TxIndex.Enabled() {
|
|
if m.TxIndex.useDefaultValue() {
|
|
short += fmt.Sprintf(" --prune.t.older=%d", defaultVal)
|
|
} else {
|
|
long += fmt.Sprintf(" --prune.t.%s=%d", m.TxIndex.dbType(), m.TxIndex.toValue())
|
|
}
|
|
}
|
|
if m.CallTraces.Enabled() {
|
|
if m.CallTraces.useDefaultValue() {
|
|
short += fmt.Sprintf(" --prune.c.older=%d", defaultVal)
|
|
} else {
|
|
long += fmt.Sprintf(" --prune.c.%s=%d", m.CallTraces.dbType(), m.CallTraces.toValue())
|
|
}
|
|
}
|
|
|
|
return strings.TrimLeft(short+long, " ")
|
|
}
|
|
|
|
func Override(db kv.RwTx, sm Mode) error {
|
|
var (
|
|
err error
|
|
)
|
|
|
|
err = set(db, kv.PruneHistory, sm.History)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = set(db, kv.PruneReceipts, sm.Receipts)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = set(db, kv.PruneTxIndex, sm.TxIndex)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = set(db, kv.PruneCallTraces, sm.CallTraces)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// EnsureNotChanged - prohibit change some configs after node creation. prohibit from human mistakes
|
|
func EnsureNotChanged(tx kv.GetPut, pruneMode Mode) (Mode, error) {
|
|
err := setIfNotExist(tx, pruneMode)
|
|
if err != nil {
|
|
return pruneMode, err
|
|
}
|
|
|
|
pm, err := Get(tx)
|
|
if err != nil {
|
|
return pruneMode, err
|
|
}
|
|
|
|
if pruneMode.Initialised {
|
|
// If storage mode is not explicitly specified, we take whatever is in the database
|
|
if !reflect.DeepEqual(pm, pruneMode) {
|
|
if bytes.Equal(pm.Receipts.dbType(), kv.PruneTypeOlder) && bytes.Equal(pruneMode.Receipts.dbType(), kv.PruneTypeBefore) {
|
|
log.Error("--prune=r flag has been changed to mean pruning of receipts before the Beacon Chain genesis. Please re-sync Erigon from scratch. " +
|
|
"Alternatively, enforce the old behaviour explicitly by --prune.r.older=90000 flag at the risk of breaking the Consensus Layer.")
|
|
}
|
|
return pm, errors.New("not allowed change of --prune flag, last time you used: " + pm.String())
|
|
}
|
|
}
|
|
return pm, nil
|
|
}
|
|
|
|
func setIfNotExist(db kv.GetPut, pm Mode) error {
|
|
var (
|
|
err error
|
|
)
|
|
if !pm.Initialised {
|
|
pm = DefaultMode
|
|
}
|
|
|
|
pruneDBData := map[string]BlockAmount{
|
|
string(kv.PruneHistory): pm.History,
|
|
string(kv.PruneReceipts): pm.Receipts,
|
|
string(kv.PruneTxIndex): pm.TxIndex,
|
|
string(kv.PruneCallTraces): pm.CallTraces,
|
|
}
|
|
|
|
for key, value := range pruneDBData {
|
|
err = setOnEmpty(db, []byte(key), value)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func createBlockAmount(pruneType []byte, v []byte) (BlockAmount, error) {
|
|
var blockAmount BlockAmount
|
|
|
|
switch string(pruneType) {
|
|
case string(kv.PruneTypeOlder):
|
|
blockAmount = Distance(binary.BigEndian.Uint64(v))
|
|
case string(kv.PruneTypeBefore):
|
|
blockAmount = Before(binary.BigEndian.Uint64(v))
|
|
default:
|
|
return nil, fmt.Errorf("unexpected block amount type: %s", string(pruneType))
|
|
}
|
|
|
|
return blockAmount, nil
|
|
}
|
|
|
|
func get(db kv.Getter, key []byte) (BlockAmount, error) {
|
|
v, err := db.GetOne(kv.DatabaseInfo, key)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
vType, err := db.GetOne(kv.DatabaseInfo, keyType(key))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if v != nil {
|
|
blockAmount, err := createBlockAmount(vType, v)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return blockAmount, nil
|
|
}
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
func set(db kv.Putter, key []byte, blockAmount BlockAmount) error {
|
|
v := make([]byte, 8)
|
|
binary.BigEndian.PutUint64(v, blockAmount.toValue())
|
|
if err := db.Put(kv.DatabaseInfo, key, v); err != nil {
|
|
return err
|
|
}
|
|
|
|
keyType := keyType(key)
|
|
|
|
if err := db.Put(kv.DatabaseInfo, keyType, blockAmount.dbType()); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func keyType(name []byte) []byte {
|
|
return append(name, []byte("Type")...)
|
|
}
|
|
|
|
func setOnEmpty(db kv.GetPut, key []byte, blockAmount BlockAmount) error {
|
|
mode, err := db.GetOne(kv.DatabaseInfo, key)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(mode) == 0 {
|
|
v := make([]byte, 8)
|
|
binary.BigEndian.PutUint64(v, blockAmount.toValue())
|
|
if err = db.Put(kv.DatabaseInfo, key, v); err != nil {
|
|
return err
|
|
}
|
|
if err = db.Put(kv.DatabaseInfo, keyType(key), blockAmount.dbType()); 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
|
|
}
|