git.frostfs.info/TrueCloudLab/frostfs-sdk-go@v0.0.0-20241022124111-5361f0ecebd3/object/erasurecode/verify.go (about)

     1  package erasurecode
     2  
     3  import (
     4  	"fmt"
     5  
     6  	objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
     7  )
     8  
     9  // Verify verifies that parts are well formed.
    10  // All parts are expected to be non-nil.
    11  // The number of parts must be equal to `total` field of the EC header
    12  // and parts must be sorted by index.
    13  func (c *Constructor) Verify(parts []*objectSDK.Object) error {
    14  	c.clear()
    15  
    16  	var headerLength int
    17  	for i := range parts {
    18  		if parts[i] == nil {
    19  			return ErrMalformedSlice
    20  		}
    21  
    22  		var err error
    23  		headerLength, err = validatePart(parts, i, headerLength)
    24  		if err != nil {
    25  			return err
    26  		}
    27  	}
    28  
    29  	p0 := parts[0]
    30  	for i := 1; i < len(parts); i++ {
    31  		// This part must be kept in sync with copyRequiredFields().
    32  		pi := parts[i]
    33  		if p0.OwnerID().Equals(pi.OwnerID()) {
    34  			return fmt.Errorf("%w: owner id mismatch: %s != %s", ErrMalformedSlice, p0.OwnerID(), pi.OwnerID())
    35  		}
    36  		if p0.Version() == nil && pi.Version() != nil || !p0.Version().Equal(*pi.Version()) {
    37  			return fmt.Errorf("%w: version mismatch: %s != %s", ErrMalformedSlice, p0.Version(), pi.Version())
    38  		}
    39  
    40  		cnr0, _ := p0.ContainerID()
    41  		cnri, _ := pi.ContainerID()
    42  		if !cnr0.Equals(cnri) {
    43  			return fmt.Errorf("%w: container id mismatch: %s != %s", ErrMalformedSlice, cnr0, cnri)
    44  		}
    45  	}
    46  
    47  	if err := c.fillHeader(parts); err != nil {
    48  		return err
    49  	}
    50  	c.fillPayload(parts)
    51  
    52  	ok, err := c.enc.Verify(c.headerShards)
    53  	if err != nil {
    54  		return err
    55  	}
    56  	if !ok {
    57  		return ErrMalformedSlice
    58  	}
    59  
    60  	ok, err = c.enc.Verify(c.payloadShards)
    61  	if err != nil {
    62  		return err
    63  	}
    64  	if !ok {
    65  		return ErrMalformedSlice
    66  	}
    67  	return nil
    68  }
    69  
    70  // copyRequiredFields sets all fields in dst which are copied from src and shared among all chunks.
    71  // src can be either another chunk of full object.
    72  // dst must be a chunk.
    73  func copyRequiredFields(dst *objectSDK.Object, src *objectSDK.Object) {
    74  	dst.SetVersion(src.Version())
    75  	dst.SetOwnerID(src.OwnerID())
    76  	dst.SetCreationEpoch(src.CreationEpoch())
    77  	dst.SetSessionToken(src.SessionToken())
    78  
    79  	cnr, _ := src.ContainerID()
    80  	dst.SetContainerID(cnr)
    81  }
    82  
    83  // validatePart makes i-th part is consistent with the rest.
    84  // If headerLength is not zero it is asserted to be equal in the ec header.
    85  // Otherwise, new headerLength is returned.
    86  func validatePart(parts []*objectSDK.Object, i int, headerLength int) (int, error) {
    87  	ec := parts[i].GetECHeader()
    88  	if ec == nil {
    89  		return headerLength, fmt.Errorf("%w: missing EC header", ErrMalformedSlice)
    90  	}
    91  	if ec.Index() != uint32(i) {
    92  		return headerLength, fmt.Errorf("%w: index=%d, ec.index=%d", ErrMalformedSlice, i, ec.Index())
    93  	}
    94  	if ec.Total() != uint32(len(parts)) {
    95  		return headerLength, fmt.Errorf("%w: len(parts)=%d, total=%d", ErrMalformedSlice, len(parts), ec.Total())
    96  	}
    97  	if headerLength == 0 {
    98  		return int(ec.HeaderLength()), nil
    99  	}
   100  	if ec.HeaderLength() != uint32(headerLength) {
   101  		return headerLength, fmt.Errorf("%w: header length mismatch %d != %d", ErrMalformedSlice, headerLength, ec.HeaderLength())
   102  	}
   103  	return headerLength, nil
   104  }