diff --git a/core/validator.go b/core/validator.go index 5f2457eecd..0497b45c88 100644 --- a/core/validator.go +++ b/core/validator.go @@ -7,6 +7,7 @@ import ( var ( ErrChunkLengthMismatch = errors.New("chunk length mismatch") ErrInvalidHeader = errors.New("invalid header") + ErrBlobQuorumSkip = errors.New("blob skipped for a quorum before verification") ) type ChunkValidator interface { @@ -32,81 +33,91 @@ func NewChunkValidator(enc Encoder, asgn AssignmentCoordinator, cst ChainState, } } -func (v *chunkValidator) ValidateBlob(blob *BlobMessage, operatorState *OperatorState) error { - if len(blob.Bundles) != len(blob.BlobHeader.QuorumInfos) { - return errors.New("number of bundles does not match number of quorums") +func (v *chunkValidator) preprocessBlob(quorumHeader *BlobQuorumInfo, blob *BlobMessage, operatorState *OperatorState) ([]*Chunk, *Assignment, *EncodingParams, error) { + if quorumHeader.AdversaryThreshold >= quorumHeader.QuorumThreshold { + return nil, nil, nil, errors.New("invalid header: quorum threshold does not exceed adversary threshold") } - // Validate the blob length - err := v.encoder.VerifyBlobLength(blob.BlobHeader.BlobCommitments) - if err != nil { - return err + // Check if the operator is a member of the quorum + if _, ok := operatorState.Operators[quorumHeader.QuorumID]; !ok { + return nil, nil, nil, ErrBlobQuorumSkip } - for _, quorumHeader := range blob.BlobHeader.QuorumInfos { + // Get the assignments for the quorum + assignment, info, err := v.assignment.GetOperatorAssignment(operatorState, quorumHeader.QuorumID, quorumHeader.QuantizationFactor, v.operatorID) + if err != nil { + return nil, nil, nil, err + } - if quorumHeader.AdversaryThreshold >= quorumHeader.QuorumThreshold { - return errors.New("invalid header: quorum threshold does not exceed adversary threshold") - } + // Validate the number of chunks + if assignment.NumChunks == 0 { + return nil, nil, nil, ErrBlobQuorumSkip + } + if assignment.NumChunks != uint(len(blob.Bundles[quorumHeader.QuorumID])) { + return nil, nil, nil, errors.New("number of chunks does not match assignment") + } - // Check if the operator is a member of the quorum - if _, ok := operatorState.Operators[quorumHeader.QuorumID]; !ok { - continue - } + chunkLength, err := v.assignment.GetChunkLengthFromHeader(operatorState, quorumHeader) + if err != nil { + return nil, nil, nil, err + } - // Get the assignments for the quorum - assignment, info, err := v.assignment.GetOperatorAssignment(operatorState, quorumHeader.QuorumID, quorumHeader.QuantizationFactor, v.operatorID) - if err != nil { - return err - } + // Validate the chunkLength against the quorum and adversary threshold parameters + numOperators := uint(len(operatorState.Operators[quorumHeader.QuorumID])) + minChunkLength, err := v.assignment.GetMinimumChunkLength(numOperators, blob.BlobHeader.BlobCommitments.Length, quorumHeader.QuantizationFactor, quorumHeader.QuorumThreshold, quorumHeader.AdversaryThreshold) + if err != nil { + return nil, nil, nil, err + } + params, err := GetEncodingParams(minChunkLength, info.TotalChunks) + if err != nil { + return nil, nil, nil, err + } - // Validate the number of chunks - if assignment.NumChunks == 0 { - continue - } - if assignment.NumChunks != uint(len(blob.Bundles[quorumHeader.QuorumID])) { - return errors.New("number of chunks does not match assignment") - } + if params.ChunkLength != chunkLength { + return nil, nil, nil, errors.New("number of chunks does not match assignment") + } - chunkLength, err := v.assignment.GetChunkLengthFromHeader(operatorState, quorumHeader) - if err != nil { - return err + // Get the chunk length + chunks := blob.Bundles[quorumHeader.QuorumID] + for _, chunk := range chunks { + if uint(chunk.Length()) != chunkLength { + return nil, nil, nil, ErrChunkLengthMismatch } + } - // Validate the chunkLength against the quorum and adversary threshold parameters - numOperators := uint(len(operatorState.Operators[quorumHeader.QuorumID])) - minChunkLength, err := v.assignment.GetMinimumChunkLength(numOperators, blob.BlobHeader.BlobCommitments.Length, quorumHeader.QuantizationFactor, quorumHeader.QuorumThreshold, quorumHeader.AdversaryThreshold) - if err != nil { - return err - } - params, err := GetEncodingParams(minChunkLength, info.TotalChunks) - if err != nil { - return err - } + // Validate the chunk length + if chunkLength*quorumHeader.QuantizationFactor*numOperators != quorumHeader.EncodedBlobLength { + return nil, nil, nil, ErrInvalidHeader + } - if params.ChunkLength != chunkLength { - return errors.New("number of chunks does not match assignment") - } + return chunks, &assignment, ¶ms, nil +} - // Get the chunk length - chunks := blob.Bundles[quorumHeader.QuorumID] - for _, chunk := range chunks { - if uint(chunk.Length()) != chunkLength { - return ErrChunkLengthMismatch - } - } +func (v *chunkValidator) ValidateBlob(blob *BlobMessage, operatorState *OperatorState) error { + if len(blob.Bundles) != len(blob.BlobHeader.QuorumInfos) { + return errors.New("number of bundles does not match number of quorums") + } - // Validate the chunk length - if chunkLength*quorumHeader.QuantizationFactor*numOperators != quorumHeader.EncodedBlobLength { - return ErrInvalidHeader - } + // Validate the blob length + err := v.encoder.VerifyBlobLength(blob.BlobHeader.BlobCommitments) + if err != nil { + return err + } - // Check the received chunks against the commitment - err = v.encoder.VerifyChunks(chunks, assignment.GetIndices(), blob.BlobHeader.BlobCommitments, params) - if err != nil { + for _, quorumHeader := range blob.BlobHeader.QuorumInfos { + // preprocess validation info + chunks, assignment, params, err := v.preprocessBlob(quorumHeader, blob, operatorState) + if err == ErrBlobQuorumSkip { + continue + } else if err != nil { return err + } else { + // Check the received chunks against the commitment + err = v.encoder.VerifyChunks(chunks, assignment.GetIndices(), blob.BlobHeader.BlobCommitments, *params) + if err != nil { + return err + } } - } return nil @@ -133,72 +144,33 @@ func (v *chunkValidator) ValidateBatch(blobs []*BlobMessage, operatorState *Oper // for each quorum for _, quorumHeader := range blob.BlobHeader.QuorumInfos { // Check if the operator is a member of the quorum - if _, ok := operatorState.Operators[quorumHeader.QuorumID]; !ok { + chunks, assignment, params, err := v.preprocessBlob(quorumHeader, blob, operatorState) + if err == ErrBlobQuorumSkip { continue - } - - // Get the assignments for the quorum - assignment, info, err := v.assignment.GetOperatorAssignment( - operatorState, - quorumHeader.QuorumID, - quorumHeader.QuantizationFactor, - v.operatorID, - ) - if err != nil { - return err - } - - // Validate the number of chunks - if assignment.NumChunks == 0 { - continue - } - if assignment.NumChunks != uint(len(blob.Bundles[quorumHeader.QuorumID])) { - return errors.New("number of chunks does not match assignment") - } - - chunkLength, err := v.assignment.GetChunkLengthFromHeader(operatorState, quorumHeader) - if err != nil { + } else if err != nil { return err - } - - // Get the chunk length - chunks := blob.Bundles[quorumHeader.QuorumID] - for _, chunk := range chunks { - if uint(chunk.Length()) != chunkLength { - return ErrChunkLengthMismatch + } else { + // Check the received chunks against the commitment + indices := assignment.GetIndices() + samples := make([]Sample, len(chunks)) + for ind := range chunks { + samples[ind] = Sample{ + Commitment: blob.BlobHeader.BlobCommitments.Commitment, + Chunk: chunks[ind], + EvalIndex: uint(indices[ind]), + BlobIndex: i, + } } - } - - // Validate the chunk length - numOperators := uint(len(operatorState.Operators[quorumHeader.QuorumID])) - if chunkLength*quorumHeader.QuantizationFactor*numOperators != quorumHeader.EncodedBlobLength { - return ErrInvalidHeader - } - // Get Encoding Params - params := EncodingParams{ChunkLength: chunkLength, NumChunks: info.TotalChunks} - - // Check the received chunks against the commitment - indices := assignment.GetIndices() - samples := make([]Sample, 0) - for ind := range chunks { - sample := Sample{ - Commitment: blob.BlobHeader.BlobCommitments.Commitment, - Chunk: chunks[ind], - EvalIndex: uint(indices[ind]), - BlobIndex: i, + // Add into subBatch + subBatch, ok := subBatchMap[*params] + if !ok { + subBatch.Samples = samples + subBatch.NumBlobs = 1 + } else { + subBatch.Samples = append(subBatch.Samples, samples...) + subBatch.NumBlobs += 1 } - samples = append(samples, sample) - } - - // Sort into subBatch - subBatch, ok := subBatchMap[params] - if !ok { - subBatch.Samples = samples - subBatch.NumBlobs = 1 - } else { - subBatch.Samples = append(subBatch.Samples, samples...) - subBatch.NumBlobs += 1 } } }