github.com/ethereum-optimism/optimism@v1.7.2/op-node/rollup/derive/frame.go (about)

     1  package derive
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  )
    10  
    11  // Frames cannot be larger than 1 MB.
    12  // Data transactions that carry frames are generally not larger than 128 KB due to L1 network conditions,
    13  // but we leave space to grow larger anyway (gas limit allows for more data).
    14  const MaxFrameLen = 1_000_000
    15  
    16  // Data Format
    17  //
    18  // frame = channel_id ++ frame_number ++ frame_data_length ++ frame_data ++ is_last
    19  //
    20  // channel_id        = bytes16
    21  // frame_number      = uint16
    22  // frame_data_length = uint32
    23  // frame_data        = bytes
    24  // is_last           = bool
    25  
    26  type Frame struct {
    27  	ID          ChannelID `json:"id"`
    28  	FrameNumber uint16    `json:"frame_number"`
    29  	Data        []byte    `json:"data"`
    30  	IsLast      bool      `json:"is_last"`
    31  }
    32  
    33  // MarshalBinary writes the frame to `w`.
    34  // It returns any errors encountered while writing, but
    35  // generally expects the writer very rarely fail.
    36  func (f *Frame) MarshalBinary(w io.Writer) error {
    37  	_, err := w.Write(f.ID[:])
    38  	if err != nil {
    39  		return err
    40  	}
    41  	if err := binary.Write(w, binary.BigEndian, f.FrameNumber); err != nil {
    42  		return err
    43  	}
    44  	if err := binary.Write(w, binary.BigEndian, uint32(len(f.Data))); err != nil {
    45  		return err
    46  	}
    47  	_, err = w.Write(f.Data)
    48  	if err != nil {
    49  		return err
    50  	}
    51  	if f.IsLast {
    52  		if _, err = w.Write([]byte{1}); err != nil {
    53  			return err
    54  		}
    55  	} else {
    56  		if _, err = w.Write([]byte{0}); err != nil {
    57  			return err
    58  		}
    59  	}
    60  	return nil
    61  }
    62  
    63  type ByteReader interface {
    64  	io.Reader
    65  	io.ByteReader
    66  }
    67  
    68  // UnmarshalBinary consumes a full frame from the reader.
    69  // If `r` fails a read, it returns the error from the reader
    70  // The reader will be left in a partially read state.
    71  //
    72  // If r doesn't return any bytes, returns io.EOF.
    73  // If r unexpectedly stops returning data half-way, returns io.ErrUnexpectedEOF.
    74  func (f *Frame) UnmarshalBinary(r ByteReader) error {
    75  	if _, err := io.ReadFull(r, f.ID[:]); err != nil {
    76  		// Forward io.EOF here ok, would mean not a single byte from r.
    77  		return fmt.Errorf("reading channel_id: %w", err)
    78  	}
    79  	if err := binary.Read(r, binary.BigEndian, &f.FrameNumber); err != nil {
    80  		return fmt.Errorf("reading frame_number: %w", eofAsUnexpectedMissing(err))
    81  	}
    82  
    83  	var frameLength uint32
    84  	if err := binary.Read(r, binary.BigEndian, &frameLength); err != nil {
    85  		return fmt.Errorf("reading frame_data_length: %w", eofAsUnexpectedMissing(err))
    86  	}
    87  
    88  	// Cap frame length to MaxFrameLen (currently 1MB)
    89  	if frameLength > MaxFrameLen {
    90  		return fmt.Errorf("frame_data_length is too large: %d", frameLength)
    91  	}
    92  	f.Data = make([]byte, int(frameLength))
    93  	if _, err := io.ReadFull(r, f.Data); err != nil {
    94  		return fmt.Errorf("reading frame_data: %w", eofAsUnexpectedMissing(err))
    95  	}
    96  
    97  	if isLastByte, err := r.ReadByte(); err != nil {
    98  		return fmt.Errorf("reading final byte (is_last): %w", eofAsUnexpectedMissing(err))
    99  	} else if isLastByte == 0 {
   100  		f.IsLast = false
   101  	} else if isLastByte == 1 {
   102  		f.IsLast = true
   103  	} else {
   104  		return errors.New("invalid byte as is_last")
   105  	}
   106  	return nil
   107  }
   108  
   109  // eofAsUnexpectedMissing converts an io.EOF in the error chain of err into an
   110  // io.ErrUnexpectedEOF. It should be used to convert intermediate io.EOF errors
   111  // in unmarshalling code to achieve idiomatic error behavior.
   112  // Other errors are passed through unchanged.
   113  func eofAsUnexpectedMissing(err error) error {
   114  	if errors.Is(err, io.EOF) {
   115  		return fmt.Errorf("fully missing: %w", io.ErrUnexpectedEOF)
   116  	}
   117  	return err
   118  }
   119  
   120  // Frames are stored in L1 transactions with the following format:
   121  // data = DerivationVersion0 ++ Frame(s)
   122  // Where there is one or more frames concatenated together.
   123  
   124  // ParseFrames parse the on chain serialization of frame(s) in
   125  // an L1 transaction. Currently only version 0 of the serialization
   126  // format is supported.
   127  // All frames must be parsed without error and there must not be
   128  // any left over data and there must be at least one frame.
   129  func ParseFrames(data []byte) ([]Frame, error) {
   130  	if len(data) == 0 {
   131  		return nil, errors.New("data array must not be empty")
   132  	}
   133  	if data[0] != DerivationVersion0 {
   134  		return nil, fmt.Errorf("invalid derivation format byte: got %d", data[0])
   135  	}
   136  	buf := bytes.NewBuffer(data[1:])
   137  	var frames []Frame
   138  	for buf.Len() > 0 {
   139  		var f Frame
   140  		if err := f.UnmarshalBinary(buf); err != nil {
   141  			return nil, fmt.Errorf("parsing frame %d: %w", len(frames), err)
   142  		}
   143  		frames = append(frames, f)
   144  	}
   145  	if buf.Len() != 0 {
   146  		return nil, fmt.Errorf("did not fully consume data: have %d frames and %d bytes left", len(frames), buf.Len())
   147  	}
   148  	if len(frames) == 0 {
   149  		return nil, errors.New("was not able to find any frames")
   150  	}
   151  	return frames, nil
   152  }