Skip to content

Commit

Permalink
fix(validation): added da batch validation against state BD desc (dym…
Browse files Browse the repository at this point in the history
  • Loading branch information
mtsitrin authored Nov 12, 2024
1 parent 30a84df commit 3c33a48
Show file tree
Hide file tree
Showing 9 changed files with 50 additions and 44 deletions.
1 change: 1 addition & 0 deletions block/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -760,6 +760,7 @@ func TestDAFetch(t *testing.T) {
DA: c.daMetaData,
},
BlockDescriptors: bds,
EndHeight: batch.EndHeight(),
}
err := manager.ApplyBatchFromSL(slBatch)
require.Equal(c.err, err)
Expand Down
6 changes: 6 additions & 0 deletions block/retriever.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ func (m *Manager) ApplyBatchFromSL(slBatch *settlement.Batch) error {
m.blockCache.Delete(block.Header.Height)
}
}

// validate the batch applied successfully and we are at the end height
if m.State.Height() != slBatch.EndHeight {
return fmt.Errorf("state height mismatch: state height: %d: batch end height: %d", m.State.Height(), slBatch.EndHeight)
}

types.LastReceivedDAHeightGauge.Set(lastAppliedHeight)

return nil
Expand Down
63 changes: 27 additions & 36 deletions block/slvalidator.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,9 @@ func (v *SettlementValidator) ValidateStateUpdate(batch *settlement.ResultRetrie
continue
}
p2pBlocks[block.Header.Height] = block

}

// load all DA blocks from the batch to be validated
daBlocks := []*types.Block{}
var daBatch da.ResultRetrieveBatch
for {
daBatch = v.blockManager.Retriever.RetrieveBatches(batch.MetaData.DA)
Expand All @@ -84,8 +82,12 @@ func (v *SettlementValidator) ValidateStateUpdate(batch *settlement.ResultRetrie
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))
}

// FIXME: how to handle non-happy case? not returning error?
continue
}

daBlocks := []*types.Block{}
for _, batch := range daBatch.Batches {
daBlocks = append(daBlocks, batch.Blocks...)
}
Expand Down Expand Up @@ -142,13 +144,9 @@ func (v *SettlementValidator) ValidateDaBlocks(slBatch *settlement.ResultRetriev
// 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))
numSLBlocks := slBatch.NumBlocks
if numSLBlocks != numSlBDs {
return types.NewErrStateUpdateNumBlocksNotMatchingFraud(slBatch.EndHeight, numSLBlocks, numSLBlocks)
}

currentProposer := v.blockManager.State.GetProposer()
if currentProposer == nil {
return fmt.Errorf("proposer is not set")
numDABlocks := uint64(len(daBlocks))
if numSLBlocks != numSlBDs || numDABlocks < numSlBDs {
return types.NewErrStateUpdateNumBlocksNotMatchingFraud(slBatch.EndHeight, numSLBlocks, numSLBlocks, numDABlocks)
}

// we compare all DA blocks against the information included in the state info block descriptors
Expand All @@ -172,36 +170,29 @@ func (v *SettlementValidator) ValidateDaBlocks(slBatch *settlement.ResultRetriev
if err != nil {
return err
}
}

// we compare the sequencer address between SL state info and DA block
// if next sequencer is not set, we check if the sequencer hash is equal to the next sequencer hash
// because it did not change. If the next sequencer is set, we check if the next sequencer hash is equal on the
// last block of the batch
isLastBlock := i == len(slBatch.BlockDescriptors)-1
if slBatch.NextSequencer != currentProposer.SettlementAddress && isLastBlock {
err := v.blockManager.UpdateSequencerSetFromSL()
if err != nil {
return fmt.Errorf("update sequencer set from SL: %w", err)
}
nextSequencer, found := v.blockManager.Sequencers.GetByAddress(slBatch.NextSequencer)
if !found {
return fmt.Errorf("next sequencer not found")
}
if !bytes.Equal(nextSequencer.MustHash(), daBlocks[i].Header.NextSequencersHash[:]) {
return types.NewErrInvalidNextSequencersHashFraud(
[32]byte(nextSequencer.MustHash()),
daBlocks[i].Header.NextSequencersHash,
)
}
} else {
if !bytes.Equal(daBlocks[i].Header.SequencerHash[:], daBlocks[i].Header.NextSequencersHash[:]) {
return types.NewErrInvalidNextSequencersHashFraud(
daBlocks[i].Header.SequencerHash,
daBlocks[i].Header.NextSequencersHash,
)
}
// we compare the sequencer address between SL state info and DA block
// if next sequencer is not set, we check if the sequencer hash is equal to the next sequencer hash
// because it did not change. If the next sequencer is set, we check if the next sequencer hash is equal on the
// last block of the batch
lastDABlock := daBlocks[numSlBDs-1]
expectedNextSeqHash := lastDABlock.Header.SequencerHash
if slBatch.NextSequencer != slBatch.Sequencer {
nextSequencer, found := v.blockManager.Sequencers.GetByAddress(slBatch.NextSequencer)
if !found {
return fmt.Errorf("next sequencer not found")
}
copy(expectedNextSeqHash[:], nextSequencer.MustHash())
}
// FIXME: support hard fork
if !bytes.Equal(expectedNextSeqHash[:], lastDABlock.Header.NextSequencersHash[:]) {
return types.NewErrInvalidNextSequencersHashFraud(
expectedNextSeqHash,
lastDABlock.Header.NextSequencersHash,
)
}

v.logger.Debug("DA blocks validated successfully", "start height", daBlocks[0].Header.Height, "end height", daBlocks[len(daBlocks)-1].Header.Height)
return nil
}
Expand Down
5 changes: 3 additions & 2 deletions block/slvalidator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ package block_test
import (
"crypto/rand"
"encoding/hex"
"github.com/tendermint/tendermint/crypto/ed25519"
tmtypes "github.com/tendermint/tendermint/types"
"reflect"
"testing"
"time"

"github.com/tendermint/tendermint/crypto/ed25519"
tmtypes "github.com/tendermint/tendermint/types"

"github.com/celestiaorg/celestia-openrpc/types/blob"
"github.com/dymensionxyz/dymint/block"
"github.com/dymensionxyz/dymint/da"
Expand Down
6 changes: 3 additions & 3 deletions block/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ func (m *Manager) SettlementValidateLoop(ctx context.Context) error {
return ctx.Err()
case <-m.settlementValidationC:

m.logger.Info("validating state updates to target height", "targetHeight", min(m.LastSettlementHeight.Load(), m.State.Height()))

for currH := m.SettlementValidator.NextValidationHeight(); currH <= min(m.LastSettlementHeight.Load(), m.State.Height()); currH = m.SettlementValidator.NextValidationHeight() {
targetValidationHeight := min(m.LastSettlementHeight.Load(), m.State.Height())
m.logger.Info("validating state updates to target height", "targetHeight", targetValidationHeight)

for currH := m.SettlementValidator.NextValidationHeight(); currH <= targetValidationHeight; currH = m.SettlementValidator.NextValidationHeight() {
// get next batch that needs to be validated from SL
batch, err := m.SLClient.GetBatchAtHeight(currH)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion settlement/settlement.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ type Batch struct {

// MetaData about the batch in the DA layer
MetaData *BatchMetaData
NumBlocks uint64
NumBlocks uint64 // FIXME: can be removed. not used and will be deprecated
}

type ResultRetrieveBatch struct {
Expand Down
4 changes: 4 additions & 0 deletions testutil/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/dymensionxyz/dymint/indexers/txindex/kv"
"github.com/dymensionxyz/dymint/p2p"
"github.com/dymensionxyz/dymint/settlement"
"github.com/dymensionxyz/dymint/types"

"github.com/tendermint/tendermint/crypto/ed25519"
"github.com/tendermint/tendermint/libs/log"
Expand Down Expand Up @@ -132,6 +133,9 @@ func GetManagerWithProposerKey(conf config.BlockManagerConfig, proposerKey crypt
if err = p2pClient.Start(context.Background()); err != nil {
return nil, err
}

manager.Sequencers.Set([]types.Sequencer{*state.Proposer.Load()})

return manager, nil
}

Expand Down
1 change: 1 addition & 0 deletions testutil/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,7 @@ func GenerateStateWithSequencer(initialHeight int64, lastBlockHeight int64, pubk
GenerateSettlementAddress(),
[]string{GenerateSettlementAddress()},
))

s.SetHeight(uint64(lastBlockHeight))
return s
}
Expand Down
6 changes: 4 additions & 2 deletions types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -325,18 +325,20 @@ type ErrStateUpdateNumBlocksNotMatchingFraud struct {
StateIndex uint64
SLNumBlocks uint64
NumBds uint64
NumDABlocks uint64
}

func NewErrStateUpdateNumBlocksNotMatchingFraud(stateIndex uint64, slNumBlocks uint64, numbds uint64) error {
func NewErrStateUpdateNumBlocksNotMatchingFraud(stateIndex, slNumBlocks, numbds, numDABlocks uint64) error {
return &ErrStateUpdateNumBlocksNotMatchingFraud{
StateIndex: stateIndex,
SLNumBlocks: slNumBlocks,
NumBds: numbds,
NumDABlocks: numDABlocks,
}
}

func (e ErrStateUpdateNumBlocksNotMatchingFraud) Error() string {
return fmt.Sprintf("numblocks not matching. StateIndex: %d Batch numblocks: %d Num of block descriptors: %d", e.StateIndex, e.SLNumBlocks, e.NumBds)
return fmt.Sprintf("blocks not matching. StateIndex: %d Batch numblocks: %d Num of block descriptors: %d Num of DA blocks: %d", e.StateIndex, e.SLNumBlocks, e.NumBds, e.NumDABlocks)
}

func (e ErrStateUpdateNumBlocksNotMatchingFraud) Unwrap() error {
Expand Down

0 comments on commit 3c33a48

Please sign in to comment.