Skip to content
Open
2 changes: 2 additions & 0 deletions changelog/bragaigor-nit-4266.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
### Changed
- Make `PruneExecutionDB` only depend on `executionDB` by removing `consensusDB` dependency
91 changes: 21 additions & 70 deletions cmd/pruning/pruning.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,12 @@ import (
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/rpc"

"github.com/offchainlabs/nitro/arbnode/dataposter/storage"
"github.com/offchainlabs/nitro/arbnode/db/read"
"github.com/offchainlabs/nitro/arbnode/mel"
"github.com/offchainlabs/nitro/arbutil"
"github.com/offchainlabs/nitro/bold/protocol"
"github.com/offchainlabs/nitro/cmd/chaininfo"
"github.com/offchainlabs/nitro/cmd/conf"
"github.com/offchainlabs/nitro/execution/gethexec"
"github.com/offchainlabs/nitro/solgen/go/bridgegen"
"github.com/offchainlabs/nitro/solgen/go/rollupgen"
"github.com/offchainlabs/nitro/staker"
"github.com/offchainlabs/nitro/staker/bold"
legacystaker "github.com/offchainlabs/nitro/staker/legacy"
multiprotocolstaker "github.com/offchainlabs/nitro/staker/multi_protocol"
Expand Down Expand Up @@ -91,21 +86,11 @@ func (r *importantRoots) addHeader(header *types.Header, overwrite bool) error {
var hashListRegex = regexp.MustCompile("^(0x)?[0-9a-fA-F]{64}(,(0x)?[0-9a-fA-F]{64})*$")

// Finds important roots to retain while proving
func findImportantRoots(ctx context.Context, executionDB ethdb.Database, stack *node.Node, initConfig *conf.InitConfig, cacheConfig *core.BlockChainConfig, persistentConfig *conf.PersistentConfig, l1Client *ethclient.Client, rollupAddrs chaininfo.RollupAddresses, validatorRequired, melEnabled bool) ([]common.Hash, error) {
func findImportantRoots(ctx context.Context, executionDB ethdb.Database, initConfig *conf.InitConfig, l1Client *ethclient.Client, rollupAddrs chaininfo.RollupAddresses, validatorRequired bool) ([]common.Hash, error) {
chainConfig := gethexec.TryReadStoredChainConfig(executionDB)
if chainConfig == nil {
return nil, errors.New("database doesn't have a chain config (was this node initialized?)")
}
consensusDB, err := stack.OpenDatabaseWithOptions("arbitrumdata", node.DatabaseOptions{MetricsNamespace: "arbitrumdata/", ReadOnly: true, PebbleExtraOptions: persistentConfig.Pebble.ExtraOptions("arbitrumdata"), NoFreezer: true})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: stack is an argument of findImportantRoots, and it is not used anymore, so it can be removed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removing that along with cacheConfig and persistentConfig since none are used

if err != nil {
return nil, err
}
defer func() {
err := consensusDB.Close()
if err != nil {
log.Warn("failed to close arbitrum database after finding pruning targets", "err", err)
}
}()
roots := importantRoots{
executionDB: executionDB,
}
Expand All @@ -115,7 +100,7 @@ func findImportantRoots(ctx context.Context, executionDB ethdb.Database, stack *
if genesisHeader == nil {
return nil, errors.New("missing L2 genesis block header")
}
err = roots.addHeader(genesisHeader, false)
err := roots.addHeader(genesisHeader, false)
if err != nil {
return nil, err
}
Expand All @@ -141,24 +126,25 @@ func findImportantRoots(ctx context.Context, executionDB ethdb.Database, stack *
log.Warn("missing latest confirmed block", "hash", confirmedHash)
}

validatorDB := rawdb.NewTable(consensusDB, storage.BlockValidatorPrefix)
lastValidated, err := staker.ReadLastValidatedInfo(validatorDB)
data, err := executionDB.Get(gethexec.ValidatedBlockHashKey)
if err != nil {
return nil, err
}
if lastValidated != nil {
lastValidatedBlockHash := common.BytesToHash(data)

if lastValidatedBlockHash != (common.Hash{}) {
var lastValidatedHeader *types.Header
headerNum, found := rawdb.ReadHeaderNumber(executionDB, lastValidated.GlobalState.BlockHash)
headerNum, found := rawdb.ReadHeaderNumber(executionDB, lastValidatedBlockHash)
if found {
lastValidatedHeader = rawdb.ReadHeader(executionDB, lastValidated.GlobalState.BlockHash, headerNum)
lastValidatedHeader = rawdb.ReadHeader(executionDB, lastValidatedBlockHash, headerNum)
}
if lastValidatedHeader != nil {
err = roots.addHeader(lastValidatedHeader, false)
if err != nil {
return nil, err
}
} else {
log.Warn("missing latest validated block", "hash", lastValidated.GlobalState.BlockHash)
log.Warn("missing latest validated block", "hash", lastValidatedBlockHash)
}
}
} else if initConfig.Prune == "full" || initConfig.Prune == "minimal" {
Expand All @@ -180,57 +166,22 @@ func findImportantRoots(ctx context.Context, executionDB ethdb.Database, stack *
} else {
return nil, fmt.Errorf("unknown pruning mode: \"%v\"", initConfig.Prune)
}
if initConfig.Prune != "minimal" && l1Client != nil {
// in pruning modes other then "minimal", find the latest finalized block and add it as a pruning target
l1Block, err := l1Client.BlockByNumber(ctx, big.NewInt(int64(rpc.FinalizedBlockNumber)))
if err != nil {
return nil, fmt.Errorf("failed to get finalized block: %w", err)
if initConfig.Prune != "minimal" {
// in pruning modes other than "minimal", get the latest finalized block and add it as a pruning target
finalizedBlockHash := rawdb.ReadFinalizedBlockHash(executionDB)
finalizedBlockNumber, ok := rawdb.ReadHeaderNumber(executionDB, finalizedBlockHash)
if !ok {
return nil, errors.New("Number of finalized block is missing")
}
l1BlockNum := l1Block.NumberU64()
var batch uint64
if melEnabled {
batch, err = read.MELSequencerBatchCount(consensusDB)

l2Header := rawdb.ReadHeader(executionDB, finalizedBlockHash, finalizedBlockNumber)
if l2Header == nil {
log.Warn("latest finalized L2 block is unknown", "blockNum", finalizedBlockNumber)
} else {
batch, err = read.SequencerBatchCount(consensusDB)
}
if err != nil {
return nil, err
}
for {
if ctx.Err() != nil {
return nil, ctx.Err()
}
if batch == 0 {
// No batch has been finalized
break
}
batch -= 1
var meta mel.BatchMetadata
if melEnabled {
meta, err = read.MELBatchMetadata(consensusDB, batch)
} else {
meta, err = read.BatchMetadata(consensusDB, batch)
}
err = roots.addHeader(l2Header, false)
if err != nil {
return nil, err
}
if meta.ParentChainBlock <= l1BlockNum {
// #nosec G115
signedBlockNum := int64(arbutil.MessageIndexToBlockNumber(meta.MessageCount, genesisNum)) - 1
// #nosec G115
blockNum := uint64(signedBlockNum)
l2Hash := rawdb.ReadCanonicalHash(executionDB, blockNum)
l2Header := rawdb.ReadHeader(executionDB, l2Hash, blockNum)
if l2Header == nil {
log.Warn("latest finalized L2 block is unknown", "blockNum", signedBlockNum)
break
}
err = roots.addHeader(l2Header, false)
if err != nil {
return nil, err
}
break
}
}
}
roots.roots = append(roots.roots, common.Hash{}) // the latest snapshot
Expand Down Expand Up @@ -296,7 +247,7 @@ func PruneExecutionDB(ctx context.Context, executionDB ethdb.Database, stack *no
if initConfig.Prune == "" {
return pruner.RecoverPruning(stack.InstanceDir(), executionDB, initConfig.PruneThreads)
}
root, err := findImportantRoots(ctx, executionDB, stack, initConfig, cacheConfig, persistentConfig, l1Client, rollupAddrs, validatorRequired, melEnabled)
root, err := findImportantRoots(ctx, executionDB, initConfig, l1Client, rollupAddrs, validatorRequired)
if err != nil {
return fmt.Errorf("failed to find root to retain for pruning: %w", err)
}
Expand Down
2 changes: 1 addition & 1 deletion execution/gethexec/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -627,7 +627,7 @@ func (n *ExecutionNode) SetFinalityData(
finalizedFinalityData *arbutil.FinalityData,
validatedFinalityData *arbutil.FinalityData,
) containers.PromiseInterface[struct{}] {
err := n.SyncMonitor.SetFinalityData(safeFinalityData, finalizedFinalityData, validatedFinalityData)
err := n.SyncMonitor.SetFinalityData(n.ExecutionDB, safeFinalityData, finalizedFinalityData, validatedFinalityData)
if err != nil {
return containers.NewReadyPromise(struct{}{}, err)
}
Expand Down
96 changes: 76 additions & 20 deletions execution/gethexec/sync_monitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,16 @@ import (

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"

"github.com/offchainlabs/nitro/arbutil"
"github.com/offchainlabs/nitro/consensus"
"github.com/offchainlabs/nitro/execution"
)

var ValidatedBlockHashKey = []byte("LastValidatedBlockHashKey")

type syncDataEntry struct {
maxMessageCount arbutil.MessageIndex
timestamp time.Time
Expand Down Expand Up @@ -236,64 +239,117 @@ func (s *SyncMonitor) BlockMetadataByNumber(ctx context.Context, blockNum uint64
return nil, nil
}

func (s *SyncMonitor) getBlockHeaderForFinalityData(
finalityData *arbutil.FinalityData,
finalityDataType string,
modifier func(curr *arbutil.FinalityData) (arbutil.MessageIndex, common.Hash, error),
) (*types.Header, error) {
if finalityData == nil {
return nil, nil
}

msgIdx := finalityData.MsgIdx
blockHash := finalityData.BlockHash

if modifier != nil {
var err error
msgIdx, blockHash, err = modifier(finalityData)
if err != nil {
return nil, err
}
}

blockNumber := s.exec.MessageIndexToBlockNumber(msgIdx)
block := s.exec.bc.GetBlockByNumber(blockNumber)
if block == nil {
log.Debug("block not found", "finalityDataType", finalityDataType, "blockNumber", blockNumber)
return nil, nil
}
if block.Hash() != blockHash {
errorMsg := fmt.Sprintf(
"block hash mismatch,finalityDataType=%s blockNumber=%v, block hash provided by consensus=%v, block hash from execution=%v",
finalityDataType,
blockNumber,
blockHash,
block.Hash(),
)
return nil, errors.New(errorMsg)
}
return block.Header(), nil
}

func (s *SyncMonitor) getFinalityBlockHeader(
waitForBlockValidator bool,
validatedFinalityData *arbutil.FinalityData,
finalityFinalityData *arbutil.FinalityData,
finalityDataType string,
) (*types.Header, error) {
if finalityFinalityData == nil {
return nil, nil
}

finalityMsgIdx := finalityFinalityData.MsgIdx
finalityBlockHash := finalityFinalityData.BlockHash
if waitForBlockValidator {

validatorModifier := func(curr *arbutil.FinalityData) (arbutil.MessageIndex, common.Hash, error) {
if !waitForBlockValidator {
return finalityMsgIdx, finalityBlockHash, nil
}
if validatedFinalityData == nil {
return nil, errors.New("block validator not set")
return 0, common.Hash{}, errors.New("block validator not set")
}
// If the finalized index is ahead of the validated one, cap it at the validated one
if finalityFinalityData.MsgIdx > validatedFinalityData.MsgIdx {
finalityMsgIdx = validatedFinalityData.MsgIdx
finalityBlockHash = validatedFinalityData.BlockHash
return validatedFinalityData.MsgIdx, validatedFinalityData.BlockHash, nil
}
return finalityMsgIdx, finalityBlockHash, nil
}

finalityBlockNumber := s.exec.MessageIndexToBlockNumber(finalityMsgIdx)
finalityBlock := s.exec.bc.GetBlockByNumber(finalityBlockNumber)
if finalityBlock == nil {
log.Debug("Finality block not found", "blockNumber", finalityBlockNumber)
return nil, nil
}
if finalityBlock.Hash() != finalityBlockHash {
errorMsg := fmt.Sprintf(
"finality block hash mismatch, blockNumber=%v, block hash provided by consensus=%v, block hash from execution=%v",
finalityBlockNumber,
finalityBlockHash,
finalityBlock.Hash(),
)
return nil, errors.New(errorMsg)
}
return finalityBlock.Header(), nil
return s.getBlockHeaderForFinalityData(finalityFinalityData, finalityDataType, validatorModifier)
}

func (s *SyncMonitor) SetFinalityData(
executionDB ethdb.Database,
safeFinalityData *arbutil.FinalityData,
finalizedFinalityData *arbutil.FinalityData,
validatedFinalityData *arbutil.FinalityData,
) error {
// Check finalized blocks
finalizedBlockHeader, err := s.getFinalityBlockHeader(
s.config.FinalizedBlockWaitForBlockValidator,
validatedFinalityData,
finalizedFinalityData,
"finalized",
)
if err != nil {
return err
}
s.exec.bc.SetFinalized(finalizedBlockHeader)

// Check validated blocks
validatedBlockHeader, err := s.getBlockHeaderForFinalityData(
validatedFinalityData,
"validated",
nil,
)
if err != nil {
return err
}

if executionDB != nil && validatedBlockHeader != nil {
validatedBlockHash := validatedBlockHeader.Hash()
err := executionDB.Put(ValidatedBlockHashKey, validatedBlockHash.Bytes())
if err != nil {
return err
}
}

// Check safe blocks
safeBlockHeader, err := s.getFinalityBlockHeader(
s.config.SafeBlockWaitForBlockValidator,
validatedFinalityData,
safeFinalityData,
"safe",
)
if err != nil {
return err
Expand Down
Loading
Loading