-
Notifications
You must be signed in to change notification settings - Fork 69
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(validation): added da batch validation against state BD desc #1218
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -47,7 +47,7 @@ func (v *SettlementValidator) ValidateStateUpdate(batch *settlement.ResultRetrie | |||||
for height := batch.StartHeight; height <= batch.EndHeight; height++ { | ||||||
source, err := v.blockManager.Store.LoadBlockSource(height) | ||||||
if err != nil { | ||||||
v.logger.Error("load block source", "error", err) | ||||||
// v.logger.Error("load block source", "error", err) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i dont think you need to remove this log. this will just happen in case no block is stored for the height, which should not happen, i think, because there will be always a block applied for the validated height. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll add the log back. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. im not sure this can happen. if you are at height h, and there is a new state update for h+100, then the node will sync first to height h+100, and then validate for the whole state info. upon reception of a new state update where endheight is higher than node height, it first calls the sync loop and from the sync loop it triggers validation once blocks are applied. so when a state info is validated the block should exist locally always, unless there is a bug. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes u right
mtsitrin marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
continue | ||||||
} | ||||||
|
||||||
|
@@ -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) | ||||||
|
@@ -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 | ||||||
srene marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
} | ||||||
|
||||||
daBlocks := []*types.Block{} | ||||||
for _, batch := range daBatch.Batches { | ||||||
daBlocks = append(daBlocks, batch.Blocks...) | ||||||
} | ||||||
|
@@ -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) | ||||||
Comment on lines
+148
to
+149
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why do we check numSLBlocks != numSlBDs . Isn't it guaranteed to be true by the hub? |
||||||
} | ||||||
|
||||||
// we compare all DA blocks against the information included in the state info block descriptors | ||||||
|
@@ -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[len(daBlocks)-1] | ||||||
mtsitrin marked this conversation as resolved.
Show resolved
Hide resolved
mtsitrin marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
expectedNextSeqHash := lastDABlock.Header.SequencerHash | ||||||
if slBatch.NextSequencer != slBatch.Sequencer { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||
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 | ||||||
} | ||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if the batch isn't applied succesfully wouldn't we get an err from the
applyBlockWithFraudHandling
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we're looping over
for i, block := range batch.Blocks
, which data on the DAit can be empty/missing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure I got this @mtsitrin . Is it related to rollback on L1 truncating state info?