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