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  }