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  }