Skip to content

Commit

Permalink
feat(manager): state update mishbehavior detection (#1130)
Browse files Browse the repository at this point in the history
  • Loading branch information
srene authored Oct 28, 2024
1 parent 3a38fb3 commit 314003c
Show file tree
Hide file tree
Showing 36 changed files with 1,153 additions and 639 deletions.
5 changes: 5 additions & 0 deletions block/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ func (m *Manager) applyBlock(block *types.Block, commit *types.Commit, blockMeta
return fmt.Errorf("save block source: %w", err)
}

_, err = m.Store.SaveDRSVersion(block.Header.Height, responses.EndBlock.RollappParamUpdates.Version, nil)
if err != nil {
return fmt.Errorf("add drs version: %w", err)
}

// Commit block to app
appHash, retainHeight, err = m.Executor.Commit(m.State, block, responses)
if err != nil {
Expand Down
47 changes: 22 additions & 25 deletions block/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,12 @@ func (m *Manager) Start(ctx context.Context) error {
/* ----------------------------- full node mode ----------------------------- */
if !isProposer {

// update latest finalized height
err = m.updateLastFinalizedHeightFromSettlement()
if err != nil {
return fmt.Errorf("sync block manager from settlement: %w", err)
}

// Start the settlement validation loop in the background
uerrors.ErrGroupGoLog(eg, m.logger, func() error {
return m.SettlementValidateLoop(ctx)
Expand Down Expand Up @@ -321,7 +327,8 @@ func (m *Manager) updateFromLastSettlementState() error {
m.logger.Error("update bonded sequencer set", "error", err)
}

res, err := m.SLClient.GetLatestBatch()
// update latest height from SL
latestHeight, err := m.SLClient.GetLatestHeight()
if errors.Is(err, gerrc.ErrNotFound) {
// The SL hasn't got any batches for this chain yet.
m.logger.Info("No batches for chain found in SL.")
Expand All @@ -334,17 +341,24 @@ func (m *Manager) updateFromLastSettlementState() error {
return err
}

m.LastSettlementHeight.Store(res.EndHeight)
m.LastSettlementHeight.Store(latestHeight)

if res.EndHeight >= m.State.NextHeight() {
m.UpdateTargetHeight(res.EndHeight)
if latestHeight >= m.State.NextHeight() {
m.UpdateTargetHeight(latestHeight)
}

// get the latest finalized height to know from where to start validating
err = m.UpdateFinalizedHeight()
if err != nil {
return err
return nil
}

func (m *Manager) updateLastFinalizedHeightFromSettlement() error {
// update latest finalized height from SL
height, err := m.SLClient.GetLatestFinalizedHeight()
if errors.Is(err, gerrc.ErrNotFound) {
m.logger.Info("No finalized batches for chain found in SL.")
} else if err != nil {
return fmt.Errorf("getting finalized height. err: %w", err)
}
m.SettlementValidator.UpdateLastValidatedHeight(height)

return nil
}
Expand All @@ -362,23 +376,6 @@ func (m *Manager) UpdateTargetHeight(h uint64) {
}
}

// UpdateFinalizedHeight retrieves the latest finalized batch and updates validation height with it
func (m *Manager) UpdateFinalizedHeight() error {
res, err := m.SLClient.GetLatestFinalizedBatch()
if err != nil && !errors.Is(err, gerrc.ErrNotFound) {
// The SL hasn't got any batches for this chain yet.
return fmt.Errorf("getting finalized height. err: %w", err)
}
if errors.Is(err, gerrc.ErrNotFound) {
// The SL hasn't got any batches for this chain yet.
m.logger.Info("No finalized batches for chain found in SL.")
} else {
// update validation height with latest finalized height (it will be updated only of finalized height is higher)
m.SettlementValidator.UpdateLastValidatedHeight(res.EndHeight)
}
return nil
}

// ValidateConfigWithRollappParams checks the configuration params are consistent with the params in the dymint state (e.g. DA and version)
func (m *Manager) ValidateConfigWithRollappParams() error {
if version.Commit != m.State.RollappParams.Version {
Expand Down
2 changes: 1 addition & 1 deletion block/pruning_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (

func TestPruningRetainHeight(t *testing.T) {
require := require.New(t)
app := testutil.GetAppMock()
app := testutil.GetAppMock(testutil.EndBlock)
app.On("EndBlock", mock.Anything).Return(abci.ResponseEndBlock{
RollappParamUpdates: &abci.RollappParams{
Da: "mock",
Expand Down
40 changes: 36 additions & 4 deletions block/stateupdate_validator.go → block/slvalidator.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package block
import (
"bytes"
"crypto/sha256"
"encoding/hex"
"errors"
"fmt"
"sync/atomic"

Expand Down Expand Up @@ -71,7 +73,19 @@ func (v *SettlementValidator) ValidateStateUpdate(batch *settlement.ResultRetrie
if daBatch.Code == da.StatusSuccess {
break
}

// fraud detected in case blob is retrieved but unable to get blocks from it.
if errors.Is(daBatch.BaseResult.Error, da.ErrBlobNotParsed) {
return types.NewErrStateUpdateBlobCorruptedFraud(batch.StateIndex, string(batch.MetaData.DA.Client), batch.MetaData.DA.Height, hex.EncodeToString(batch.MetaData.DA.Commitment))
}

// fraud detected in case availability checks fail and therefore there certainty the blob, according to the state update DA path, is not available.
checkBatchResult := v.blockManager.Retriever.CheckBatchAvailability(batch.MetaData.DA)
if errors.Is(checkBatchResult.Error, da.ErrBlobNotIncluded) {
return types.NewErrStateUpdateBlobNotAvailableFraud(batch.StateIndex, string(batch.MetaData.DA.Client), batch.MetaData.DA.Height, hex.EncodeToString(batch.MetaData.DA.Commitment))
}
}

for _, batch := range daBatch.Batches {
daBlocks = append(daBlocks, batch.Blocks...)
}
Expand Down Expand Up @@ -127,10 +141,9 @@ func (v *SettlementValidator) ValidateP2PBlocks(daBlocks []*types.Block, p2pBloc
func (v *SettlementValidator) ValidateDaBlocks(slBatch *settlement.ResultRetrieveBatch, daBlocks []*types.Block) error {
// we first verify the numblocks included in the state info match the block descriptors and the blocks obtained from DA
numSlBDs := uint64(len(slBatch.BlockDescriptors))
numDABlocks := uint64(len(daBlocks))
numSLBlocks := slBatch.NumBlocks
if numSLBlocks != numDABlocks || numSLBlocks != numSlBDs {
return types.NewErrStateUpdateNumBlocksNotMatchingFraud(slBatch.EndHeight, numSLBlocks, numDABlocks, numSLBlocks)
if numSLBlocks != numSlBDs {
return types.NewErrStateUpdateNumBlocksNotMatchingFraud(slBatch.EndHeight, numSLBlocks, numSLBlocks)
}

// we compare all DA blocks against the information included in the state info block descriptors
Expand All @@ -148,6 +161,12 @@ func (v *SettlementValidator) ValidateDaBlocks(slBatch *settlement.ResultRetriev
if !bd.Timestamp.Equal(daBlocks[i].Header.GetTimestamp()) {
return types.NewErrStateUpdateTimestampNotMatchingFraud(slBatch.StateIndex, bd.Height, bd.Timestamp, daBlocks[i].Header.GetTimestamp())
}

// we validate block descriptor drs version per height
err := v.validateDRS(slBatch.StateIndex, bd.Height, bd.DrsVersion)
if err != nil {
return err
}
}
v.logger.Debug("DA blocks validated successfully", "start height", daBlocks[0].Header.Height, "end height", daBlocks[len(daBlocks)-1].Header.Height)
return nil
Expand All @@ -173,11 +192,24 @@ func (v *SettlementValidator) GetLastValidatedHeight() uint64 {
return v.lastValidatedHeight.Load()
}

// GetLastValidatedHeight returns the next height that needs to be validated with settlement state updates.
// NextValidationHeight returns the next height that needs to be validated with settlement state updates.
func (v *SettlementValidator) NextValidationHeight() uint64 {
return v.lastValidatedHeight.Load() + 1
}

// validateDRS compares the DRS version stored for the specific height, obtained from rollapp params.
func (v *SettlementValidator) validateDRS(stateIndex uint64, height uint64, version string) error {
drs, err := v.blockManager.Store.LoadDRSVersion(height)
if err != nil {
return err
}
if drs != version {
return types.NewErrStateUpdateDRSVersionFraud(stateIndex, height, drs, version)
}

return nil
}

// blockHash generates a hash from the block bytes to compare them
func blockHash(block *types.Block) ([]byte, error) {
blockBytes, err := block.MarshalBinary()
Expand Down
Loading

0 comments on commit 314003c

Please sign in to comment.