github.com/koko1123/flow-go-1@v0.29.6/module/executiondatasync/execution_data/serializer.go (about)

     1  package execution_data
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"math"
     7  
     8  	cborlib "github.com/fxamacker/cbor/v2"
     9  	"github.com/ipfs/go-cid"
    10  
    11  	"github.com/koko1123/flow-go-1/model/encoding"
    12  	"github.com/koko1123/flow-go-1/model/encoding/cbor"
    13  	"github.com/koko1123/flow-go-1/network"
    14  	"github.com/koko1123/flow-go-1/network/compressor"
    15  )
    16  
    17  var DefaultSerializer Serializer
    18  
    19  func init() {
    20  	var codec encoding.Codec
    21  
    22  	decMode, err := cborlib.DecOptions{
    23  		MaxArrayElements: math.MaxInt64,
    24  		MaxMapPairs:      math.MaxInt64,
    25  		MaxNestedLevels:  math.MaxInt16,
    26  	}.DecMode()
    27  
    28  	if err != nil {
    29  		panic(err)
    30  	}
    31  
    32  	codec = cbor.NewCodec(cbor.WithDecMode(decMode))
    33  	DefaultSerializer = NewSerializer(codec, compressor.NewLz4Compressor())
    34  }
    35  
    36  // header codes to distinguish between different types of data
    37  // these codes provide simple versioning of execution state data blobs and indicate how the data
    38  // should be deserialized into their original form. Therefore, each input format must have a unique
    39  // code, and the codes must never be reused. This allows for libraries that can accurately decode
    40  // the data without juggling software versions.
    41  const (
    42  	codeRecursiveCIDs = iota + 1
    43  	codeExecutionDataRoot
    44  	codeChunkExecutionData
    45  )
    46  
    47  func getCode(v interface{}) (byte, error) {
    48  	switch v.(type) {
    49  	case *BlockExecutionDataRoot:
    50  		return codeExecutionDataRoot, nil
    51  	case *ChunkExecutionData:
    52  		return codeChunkExecutionData, nil
    53  	case []cid.Cid:
    54  		return codeRecursiveCIDs, nil
    55  	default:
    56  		return 0, fmt.Errorf("invalid type for interface: %T", v)
    57  	}
    58  }
    59  
    60  func getPrototype(code byte) (interface{}, error) {
    61  	switch code {
    62  	case codeExecutionDataRoot:
    63  		return &BlockExecutionDataRoot{}, nil
    64  	case codeChunkExecutionData:
    65  		return &ChunkExecutionData{}, nil
    66  	case codeRecursiveCIDs:
    67  		return &[]cid.Cid{}, nil
    68  	default:
    69  		return nil, fmt.Errorf("invalid code: %v", code)
    70  	}
    71  }
    72  
    73  // Serializer is used to serialize / deserialize Execution Data and CID lists for the
    74  // Execution Data Service.
    75  type Serializer interface {
    76  	Serialize(io.Writer, interface{}) error
    77  	Deserialize(io.Reader) (interface{}, error)
    78  }
    79  
    80  // serializer implements the Serializer interface. Object are serialized by encoding and
    81  // compressing them using the given codec and compressor.
    82  //
    83  // The serialized data is prefixed with a single byte header that identifies the underlying
    84  // data format. This allows adding new data types in a backwards compatible way.
    85  type serializer struct {
    86  	codec      encoding.Codec
    87  	compressor network.Compressor
    88  }
    89  
    90  func NewSerializer(codec encoding.Codec, compressor network.Compressor) *serializer {
    91  	return &serializer{
    92  		codec:      codec,
    93  		compressor: compressor,
    94  	}
    95  }
    96  
    97  // writePrototype writes the header code for the given value to the given writer
    98  func (s *serializer) writePrototype(w io.Writer, v interface{}) error {
    99  	var code byte
   100  	var err error
   101  
   102  	if code, err = getCode(v); err != nil {
   103  		return err
   104  	}
   105  
   106  	if bw, ok := w.(io.ByteWriter); ok {
   107  		err = bw.WriteByte(code)
   108  	} else {
   109  		_, err = w.Write([]byte{code})
   110  	}
   111  
   112  	if err != nil {
   113  		return fmt.Errorf("failed to write code: %w", err)
   114  	}
   115  
   116  	return nil
   117  }
   118  
   119  // Serialize encodes and compresses the given value to the given writer
   120  func (s *serializer) Serialize(w io.Writer, v interface{}) error {
   121  	if err := s.writePrototype(w, v); err != nil {
   122  		return fmt.Errorf("failed to write prototype: %w", err)
   123  	}
   124  
   125  	comp, err := s.compressor.NewWriter(w)
   126  
   127  	if err != nil {
   128  		return fmt.Errorf("failed to create compressor writer: %w", err)
   129  	}
   130  
   131  	enc := s.codec.NewEncoder(comp)
   132  
   133  	if err := enc.Encode(v); err != nil {
   134  		return fmt.Errorf("failed to encode data: %w", err)
   135  	}
   136  
   137  	// flush data out to the underlying writer
   138  	if err := comp.Close(); err != nil {
   139  		return fmt.Errorf("failed to close compressor: %w", err)
   140  	}
   141  
   142  	return nil
   143  }
   144  
   145  // readPrototype reads a header code from the given reader and returns a prototype value
   146  func (s *serializer) readPrototype(r io.Reader) (interface{}, error) {
   147  	var code byte
   148  	var err error
   149  
   150  	if br, ok := r.(io.ByteReader); ok {
   151  		code, err = br.ReadByte()
   152  	} else {
   153  		var buf [1]byte
   154  		_, err = r.Read(buf[:])
   155  		code = buf[0]
   156  	}
   157  
   158  	if err != nil {
   159  		return nil, fmt.Errorf("failed to read code: %w", err)
   160  	}
   161  
   162  	return getPrototype(code)
   163  }
   164  
   165  // Deserialize decompresses and decodes the data from the given reader
   166  func (s *serializer) Deserialize(r io.Reader) (interface{}, error) {
   167  	v, err := s.readPrototype(r)
   168  
   169  	if err != nil {
   170  		return nil, fmt.Errorf("failed to read prototype: %w", err)
   171  	}
   172  
   173  	comp, err := s.compressor.NewReader(r)
   174  
   175  	if err != nil {
   176  		return nil, fmt.Errorf("failed to create compressor reader: %w", err)
   177  	}
   178  
   179  	dec := s.codec.NewDecoder(comp)
   180  
   181  	if err := dec.Decode(v); err != nil {
   182  		return nil, fmt.Errorf("failed to decode data: %w", err)
   183  	}
   184  
   185  	return v, nil
   186  }