github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/network/codec/cbor/codec.go (about) 1 package cbor 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 8 "github.com/fxamacker/cbor/v2" 9 10 cborcodec "github.com/onflow/flow-go/model/encoding/cbor" 11 "github.com/onflow/flow-go/network" 12 "github.com/onflow/flow-go/network/codec" 13 _ "github.com/onflow/flow-go/utils/binstat" 14 ) 15 16 var defaultDecMode, _ = cbor.DecOptions{ExtraReturnErrors: cbor.ExtraDecErrorUnknownField}.DecMode() 17 18 // Codec represents a CBOR codec for our network. 19 type Codec struct { 20 } 21 22 // NewCodec creates a new CBOR codec. 23 func NewCodec() *Codec { 24 c := &Codec{} 25 return c 26 } 27 28 // NewEncoder creates a new CBOR encoder with the given underlying writer. 29 func (c *Codec) NewEncoder(w io.Writer) network.Encoder { 30 enc := cborcodec.EncMode.NewEncoder(w) 31 return &Encoder{enc: enc} 32 } 33 34 // NewDecoder creates a new CBOR decoder with the given underlying reader. 35 func (c *Codec) NewDecoder(r io.Reader) network.Decoder { 36 dec := defaultDecMode.NewDecoder(r) 37 return &Decoder{dec: dec} 38 } 39 40 // Encode will, given a Golang interface 'v', return a []byte 'envelope'. 41 // Return an error if packing the envelope fails. 42 // NOTE: 'v' is the network message payload in unserialized form. 43 // NOTE: 'code' is the message type. 44 // NOTE: 'what' is the 'code' name for debugging / instrumentation. 45 // NOTE: 'envelope' contains 'code' & serialized / encoded 'v'. 46 // i.e. 1st byte is 'code' and remaining bytes are CBOR encoded 'v'. 47 func (c *Codec) Encode(v interface{}) ([]byte, error) { 48 49 // encode the value 50 code, what, err := codec.MessageCodeFromInterface(v) 51 if err != nil { 52 return nil, fmt.Errorf("could not determine envelope code: %w", err) 53 } 54 55 // NOTE: benchmarking shows that prepending the code and then using 56 // .NewEncoder() to .Encode() is the fastest. 57 58 // encode / append the envelope code 59 //bs1 := binstat.EnterTime(binstat.BinNet + ":wire<1(cbor)envelope2payload") 60 var data bytes.Buffer 61 data.WriteByte(code.Uint8()) 62 //binstat.LeaveVal(bs1, int64(data.Len())) 63 64 // encode the payload 65 //bs2 := binstat.EnterTime(fmt.Sprintf("%s%s%s:%d", binstat.BinNet, ":wire<2(cbor)", what, code)) // e.g. ~3net::wire<1(cbor)CodeEntityRequest:23 66 encoder := cborcodec.EncMode.NewEncoder(&data) 67 err = encoder.Encode(v) 68 //binstat.LeaveVal(bs2, int64(data.Len())) 69 if err != nil { 70 return nil, fmt.Errorf("could not encode CBOR payload with envelope code %d AKA %s: %w", code, what, err) // e.g. 2, "CodeBlockProposal", <CBOR error> 71 } 72 73 dataBytes := data.Bytes() 74 75 return dataBytes, nil 76 } 77 78 // Decode will, given a []byte 'envelope', return a Golang interface 'v'. 79 // Return an error if unpacking the envelope fails. 80 // NOTE: 'v' is the network message payload in un-serialized form. 81 // NOTE: 'code' is the message type. 82 // NOTE: 'what' is the 'code' name for debugging / instrumentation. 83 // NOTE: 'envelope' contains 'code' & serialized / encoded 'v'. 84 // i.e. 1st byte is 'code' and remaining bytes are CBOR encoded 'v'. 85 // Expected error returns during normal operations: 86 // - codec.ErrInvalidEncoding if message encoding is invalid. 87 // - codec.ErrUnknownMsgCode if message code byte does not match any of the configured message codes. 88 // - codec.ErrMsgUnmarshal if the codec fails to unmarshal the data to the message type denoted by the message code. 89 func (c *Codec) Decode(data []byte) (interface{}, error) { 90 91 msgCode, err := codec.MessageCodeFromPayload(data) 92 if err != nil { 93 return nil, err 94 } 95 // decode the envelope 96 //bs1 := binstat.EnterTime(binstat.BinNet + ":wire>3(cbor)payload2envelope") 97 98 //binstat.LeaveVal(bs1, int64(len(data))) 99 100 msgInterface, what, err := codec.InterfaceFromMessageCode(msgCode) 101 if err != nil { 102 return nil, err 103 } 104 105 // unmarshal the payload 106 //bs2 := binstat.EnterTimeVal(fmt.Sprintf("%s%s%s:%d", binstat.BinNet, ":wire>4(cbor)", what, code), int64(len(data))) // e.g. ~3net:wire>4(cbor)CodeEntityRequest:23 107 err = defaultDecMode.Unmarshal(data[1:], msgInterface) // all but first byte 108 //binstat.Leave(bs2) 109 if err != nil { 110 return nil, codec.NewMsgUnmarshalErr(data[0], what, err) 111 } 112 113 return msgInterface, nil 114 }