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 }