github.com/celestiaorg/celestia-node@v0.15.0-beta.1/share/eds/ods.go (about)

     1  package eds
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"encoding/binary"
     7  	"errors"
     8  	"fmt"
     9  	"io"
    10  
    11  	cbor "github.com/ipfs/go-ipld-cbor"
    12  	"github.com/ipld/go-car"
    13  	"github.com/ipld/go-car/util"
    14  )
    15  
    16  // bufferedODSReader will read odsSquareSize amount of leaves from reader into the buffer.
    17  // It exposes the buffer to be read by io.Reader interface implementation
    18  type bufferedODSReader struct {
    19  	carReader *bufio.Reader
    20  	// current is the amount of CARv1 encoded leaves that have been read from reader. When current
    21  	// reaches odsSquareSize, bufferedODSReader will prevent further reads by returning io.EOF
    22  	current, odsSquareSize int
    23  	buf                    *bytes.Buffer
    24  }
    25  
    26  // ODSReader reads CARv1 encoded data from io.ReadCloser and limits the reader to the CAR header
    27  // and first quadrant (ODS)
    28  func ODSReader(carReader io.Reader) (io.Reader, error) {
    29  	if carReader == nil {
    30  		return nil, errors.New("eds: can't create ODSReader over nil reader")
    31  	}
    32  
    33  	odsR := &bufferedODSReader{
    34  		carReader: bufio.NewReader(carReader),
    35  		buf:       new(bytes.Buffer),
    36  	}
    37  
    38  	// first LdRead reads the full CAR header to determine amount of shares in the ODS
    39  	data, err := util.LdRead(odsR.carReader)
    40  	if err != nil {
    41  		return nil, fmt.Errorf("reading header: %v", err)
    42  	}
    43  
    44  	var header car.CarHeader
    45  	err = cbor.DecodeInto(data, &header)
    46  	if err != nil {
    47  		return nil, fmt.Errorf("invalid header: %w", err)
    48  	}
    49  
    50  	// car header contains both row roots and col roots which is why
    51  	// we divide by 4 to get the ODSWidth
    52  	odsWidth := len(header.Roots) / 4
    53  	odsR.odsSquareSize = odsWidth * odsWidth
    54  
    55  	// NewCarReader will expect to read the header first, so write it first
    56  	return odsR, util.LdWrite(odsR.buf, data)
    57  }
    58  
    59  func (r *bufferedODSReader) Read(p []byte) (n int, err error) {
    60  	// read leafs to the buffer until it has sufficient data to fill provided container or full ods is
    61  	// read
    62  	for r.current < r.odsSquareSize && r.buf.Len() < len(p) {
    63  		if err := r.readLeaf(); err != nil {
    64  			return 0, err
    65  		}
    66  
    67  		r.current++
    68  	}
    69  
    70  	// read buffer to slice
    71  	return r.buf.Read(p)
    72  }
    73  
    74  // readLeaf reads one leaf from reader into bufferedODSReader buffer
    75  func (r *bufferedODSReader) readLeaf() error {
    76  	if _, err := r.carReader.Peek(1); err != nil { // no more blocks, likely clean io.EOF
    77  		return err
    78  	}
    79  
    80  	l, err := binary.ReadUvarint(r.carReader)
    81  	if err != nil {
    82  		if err == io.EOF {
    83  			return io.ErrUnexpectedEOF // don't silently pretend this is a clean EOF
    84  		}
    85  		return err
    86  	}
    87  
    88  	if l > uint64(util.MaxAllowedSectionSize) { // Don't OOM
    89  		return fmt.Errorf("malformed car; header `length`: %v is bigger than %v", l, util.MaxAllowedSectionSize)
    90  	}
    91  
    92  	buf := make([]byte, 8)
    93  	n := binary.PutUvarint(buf, l)
    94  	r.buf.Write(buf[:n])
    95  
    96  	_, err = r.buf.ReadFrom(io.LimitReader(r.carReader, int64(l)))
    97  	return err
    98  }