go-libp2p-core icon indicating copy to clipboard operation
go-libp2p-core copied to clipboard

Unable to marshal / unmarshal AddrInfo within my struct

Open pocockn opened this issue 4 years ago • 4 comments

I need to unicast messages to other peers on my network. I want to store the AddrInfo struct on my message so I have the nodes addr who unicast me so I can send a response back.

func init() {
    gob.Register(&Message{})
}

// Message stores a type of message and a body.
// The message is the data that gets passed between nodes when they communicate.
type Message struct {
    ID         string           `json:"id"`
    Body       []byte           `json:"body"`        // the actual payload.
    OriginNode peer.AddrInfo    `json:"origin_node"` // the node that sent the message
}

// NewMessage builds up a new message.
func NewMessage(b []byte, o peer.AddrInfo) Message {
    return Message{
        ID:        uuid.New().String(),
        Body:      b,
        OriginNode: o,
    }
}

// Marshal takes a node message and marshals it into an array of bytes.
func (m Message) Marshal() []byte {
    var buf bytes.Buffer

    enc := gob.NewEncoder(&buf)

    if err := enc.Encode(m); err != nil {
        zap.S().Fatal(err)
    }

    return buf.Bytes()
}

// UnmarshalMessage takes a slice of bytes and a returns a message struct.
func UnmarshalMessage(input []byte) (Message, error) {
    msg := Message{}
    buf := bytes.NewBuffer(input)
    dec := gob.NewDecoder(buf)

    if err := dec.Decode(&msg); err != nil {
        return Message{}, err
    }

    return msg, nil
}

When I marshal my message struct and then attempt to unmarshal I receive the following error:

panic: interface conversion: interface is nil, not encoding.BinaryUnmarshaler [recovered] panic: interface conversion: interface is nil, not encoding.BinaryUnmarshaler UnmarshalMessage(0xc0001c7200, 0x454, 0x480, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, .

Is there something I need to do to be able to marshal and unmarshal my struct that contains the peer.AddrInfo ?

pocockn avatar Feb 10 '21 15:02 pocockn

cc @Stebalien @marten-seemann

aarshkshah1992 avatar Feb 12 '21 06:02 aarshkshah1992

This seems to be a problem with encoding/gob, not in our stack. If I just use encoding/json everything works fine:

// Message stores a type of message and a body.
// The message is the data that gets passed between nodes when they communicate.
type Message struct {
	ID         string        `json:"id"`
	Body       []byte        `json:"body"`        // the actual payload.
	OriginNode peer.AddrInfo `json:"origin_node"` // the node that sent the message
}

// NewMessage builds up a new message.
func NewMessage(b []byte, o peer.AddrInfo) Message {
	return Message{
		ID:         "identifier",
		Body:       b,
		OriginNode: o,
	}
}

// Marshal takes a node message and marshals it into an array of bytes.
func (m Message) Marshal() ([]byte, error) {
	return json.Marshal(m)
}

// UnmarshalMessage takes a slice of bytes and a returns a message struct.
func UnmarshalMessage(input []byte) (Message, error) {
	msg := Message{}
	return msg, json.Unmarshal(input, &msg)
}

I'm not really familiar with encoding/gob to be honest, but here's the relevant part of the stack trace:

panic(0x131e400, 0xc00009de60)
	/usr/local/Cellar/go/1.15.7_1/libexec/src/runtime/panic.go:969 +0x1b9
encoding/gob.catchError(0xc00012c0f0)
	/usr/local/Cellar/go/1.15.7_1/libexec/src/encoding/gob/error.go:38 +0x85
panic(0x131e400, 0xc00009de60)
	/usr/local/Cellar/go/1.15.7_1/libexec/src/runtime/panic.go:969 +0x1b9
encoding/gob.(*Decoder).decodeGobDecoder(0xc00012c080, 0xc0000cdcc0, 0xc0000b1760, 0x1342be0, 0xc000099fb0, 0x194)
	/usr/local/Cellar/go/1.15.7_1/libexec/src/encoding/gob/decode.go:736 +0x1fd
encoding/gob.(*Decoder).gobDecodeOpFor.func1(0xc0001481c0, 0xc0000b1760, 0x1342be0, 0xc000099fb0, 0x194)
	/usr/local/Cellar/go/1.15.7_1/libexec/src/encoding/gob/decode.go:960 +0x7a
encoding/gob.(*Decoder).decodeArrayHelper(0xc00012c080, 0xc0000b1760, 0x1303b60, 0xc0000e4268, 0x197, 0xc0000b1960, 0x1, 0x14df3e0, 0xc000099fa0, 0x0)
	/usr/local/Cellar/go/1.15.7_1/libexec/src/encoding/gob/decode.go:530 +0x179
encoding/gob.(*Decoder).decodeSlice(0xc00012c080, 0xc0000b1760, 0x1303b60, 0xc0000e4268, 0x197, 0xc0000b1960, 0x14df3e0, 0xc000099fa0, 0x0)
	/usr/local/Cellar/go/1.15.7_1/libexec/src/encoding/gob/decode.go:630 +0x23c
encoding/gob.(*Decoder).decOpFor.func3(0xc0000ce8f8, 0xc0000b1760, 0x1303b60, 0xc0000e4268, 0x197)
	/usr/local/Cellar/go/1.15.7_1/libexec/src/encoding/gob/decode.go:848 +0x78
encoding/gob.(*Decoder).decodeStruct(0xc00012c080, 0xc0000b1920, 0x133a2e0, 0xc0000e4258, 0x199)
	/usr/local/Cellar/go/1.15.7_1/libexec/src/encoding/gob/decode.go:471 +0xe6
encoding/gob.(*Decoder).decOpFor.func4(0xc0000bc800, 0xc0000b1740, 0x133a2e0, 0xc0000e4258, 0x199)
	/usr/local/Cellar/go/1.15.7_1/libexec/src/encoding/gob/decode.go:860 +0x54
encoding/gob.(*Decoder).decodeStruct(0xc00012c080, 0xc0000b1900, 0x133fda0, 0xc0000e4230, 0x199)
	/usr/local/Cellar/go/1.15.7_1/libexec/src/encoding/gob/decode.go:471 +0xe6
encoding/gob.(*Decoder).decodeValue(0xc00012c080, 0x41, 0x1316d40, 0xc0000e4230, 0x16)
	/usr/local/Cellar/go/1.15.7_1/libexec/src/encoding/gob/decode.go:1205 +0x2c5
encoding/gob.(*Decoder).DecodeValue(0xc00012c080, 0x1316d40, 0xc0000e4230, 0x16, 0x0, 0x0)
	/usr/local/Cellar/go/1.15.7_1/libexec/src/encoding/gob/decoder.go:213 +0x146
encoding/gob.(*Decoder).Decode(0xc00012c080, 0x1316d40, 0xc0000e4230, 0xc000099c40, 0x1)
	/usr/local/Cellar/go/1.15.7_1/libexec/src/encoding/gob/decoder.go:188 +0x173
github.com/libp2p/go-libp2p-core/cmd.UnmarshalMessage(0xc00013e000, 0xd2, 0x17c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, ...)

marten-seemann avatar Feb 12 '21 06:02 marten-seemann

It looks like we need to implement BinaryUnmarshal.

Stebalien avatar Feb 12 '21 06:02 Stebalien

But I'm not sure how to actually do this unless we want to define a custom "binary" format for address info objects.

Stebalien avatar Feb 12 '21 06:02 Stebalien