-
Notifications
You must be signed in to change notification settings - Fork 90
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
p2p: support zero length request protos #1958
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
// Copyright © 2022-2023 Obol Labs Inc. Licensed under the terms of a Business Source License 1.1 | ||
|
||
package peerinfo_test | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
"github.com/libp2p/go-libp2p/core/peer" | ||
"github.com/libp2p/go-libp2p/core/peerstore" | ||
"github.com/stretchr/testify/require" | ||
|
||
"github.com/obolnetwork/charon/app/peerinfo" | ||
"github.com/obolnetwork/charon/p2p" | ||
"github.com/obolnetwork/charon/testutil" | ||
) | ||
|
||
func TestDoOnce(t *testing.T) { | ||
server := testutil.CreateHost(t, testutil.AvailableAddr(t)) | ||
client := testutil.CreateHost(t, testutil.AvailableAddr(t)) | ||
|
||
client.Peerstore().AddAddrs(server.ID(), server.Addrs(), peerstore.PermanentAddrTTL) | ||
|
||
version := "v0" | ||
lockHash := []byte("123") | ||
gitHash := "abc" | ||
// Register the server handler that either | ||
_ = peerinfo.New(server, []peer.ID{server.ID(), client.ID()}, version, lockHash, gitHash, p2p.SendReceive) | ||
|
||
info, _, ok, err := peerinfo.DoOnce(context.Background(), client, server.ID()) | ||
require.NoError(t, err) | ||
require.True(t, ok) | ||
require.Equal(t, version, info.CharonVersion) | ||
require.Equal(t, gitHash, info.GitHash) | ||
require.Equal(t, lockHash, info.LockHash) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -191,6 +191,10 @@ func defaultSendRecvOpts(pID protocol.ID) sendRecvOpts { | |
func SendReceive(ctx context.Context, tcpNode host.Host, peerID peer.ID, | ||
req, resp proto.Message, pID protocol.ID, opts ...SendRecvOption, | ||
) error { | ||
if !isZeroProto(resp) { | ||
return errors.New("bug: response proto must be zero value") | ||
} | ||
|
||
o := defaultSendRecvOpts(pID) | ||
for _, opt := range opts { | ||
opt(&o) | ||
|
@@ -223,10 +227,19 @@ func SendReceive(ctx context.Context, tcpNode host.Host, peerID peer.ID, | |
return errors.Wrap(err, "close write", z.Any("protocol", s.Protocol())) | ||
} | ||
|
||
zeroResp := proto.Clone(resp) | ||
|
||
if err = reader.ReadMsg(resp); err != nil { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this will still return the errors that we are seeing in logs: how is this allowing zero length responses? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See |
||
return errors.Wrap(err, "read response", z.Any("protocol", s.Protocol())) | ||
} | ||
|
||
// TODO(corver): Remove this once we use length-delimited protocols. | ||
// This was added since legacy stream delimited readers couldn't distinguish between | ||
// no response and a zero response. | ||
if proto.Equal(resp, zeroResp) { | ||
return errors.New("no or zero response received", z.Any("protocol", s.Protocol())) | ||
} | ||
|
||
if err = s.Close(); err != nil { | ||
return errors.Wrap(err, "close stream", z.Any("protocol", s.Protocol())) | ||
} | ||
|
@@ -288,13 +301,11 @@ func (w legacyReadWriter) WriteMsg(m proto.Message) error { | |
func (w legacyReadWriter) ReadMsg(m proto.Message) error { | ||
b, err := io.ReadAll(w.stream) | ||
if err != nil { | ||
return errors.Wrap(err, "read response") | ||
} else if len(b) == 0 { | ||
return errors.New("peer errored, no response") | ||
return errors.Wrap(err, "read proto") | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @dB2510 this was the zero length check that was erroring, it has been removed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Got it! |
||
|
||
if err = proto.Unmarshal(b, m); err != nil { | ||
return errors.Wrap(err, "unmarshal response") | ||
return errors.Wrap(err, "unmarshal proto") | ||
} | ||
|
||
return nil | ||
|
@@ -325,3 +336,18 @@ func protocolPrefix(pIDs ...protocol.ID) protocol.ID { | |
|
||
return prefix | ||
} | ||
|
||
// isZeroProto returns true if the provided proto message is zero. | ||
// | ||
// Note this function is inefficient for the negative case (i.e. when the message is not zero) | ||
// as it copies the input argument. | ||
func isZeroProto(m proto.Message) bool { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚀 |
||
if m == nil { | ||
return false | ||
} | ||
|
||
clone := proto.Clone(m) | ||
proto.Reset(clone) | ||
|
||
return proto.Equal(m, clone) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what if
resp
is NOT a zero/empty response?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
true, that is an edge-case not handled by this code
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but that would be a bug in the code that uses this function, so a build-time issue, not a run-time issue. We can look at adding some build-time checks that all passed in responses are zero.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i see, for the time being, we can probably edit the godoc of this function to say that
Callers of this method should ensure that the resp parameter is zero/empty
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a
isZeroProto
check to the SendReceive function.