Skip to content

Conversation

mfw78
Copy link
Collaborator

@mfw78 mfw78 commented May 27, 2025

Checklist

  • I have read the coding guide.
  • My change requires a documentation update, and I have done it.
  • I have added tests to cover my changes.
  • I have filled out the description and linked the related issues.

Description

This PR revises the handshake protocol such that:

  1. There is the inclusive of a Capabilities message within the Ack that determines if the node is a full node and/or supports tracing headers.
  2. The Handshake protobuf now is a oneof message which enforces strict typing of the messages on the wire.
  3. Unless specifically enabled by the configuration flag, no Headers are passed in the opening of new streams within libp2p, essentially reducing RTTs / network traffic that is needlessly sending empty headers on every single stream creation except Handshake. The functionality is preserved for developers / those wanting to debug the protocol.

Open API Spec Version Changes (if applicable)

N/A

Motivation and Context (Optional)

Strict typing and reduction in needless network chatter.

Related Issue (Optional)

Screenshots (if appropriate):

N/A

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Migrates the golang CI yaml to version 2, given that the project now uses go 1.24.0

if: github.ref != 'refs/heads/master'
uses: wagoid/commitlint-github-action@v5
- name: GolangCI-Lint
uses: golangci/golangci-lint-action@v7
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bump required here as v2.1.6 of GolangCI-lint is only supported in @7

Comment on lines +74 to +88
- linters:
- staticcheck
text: "QF1008:"
- linters:
- staticcheck
text: "QF1001:"
- linters:
- staticcheck
text: "QF1002:"
- linters:
- staticcheck
text: "QF1003:"
- linters:
- staticcheck
text: "QF1006:"
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Disregarded as these are general in nature

@mfw78 mfw78 marked this pull request as ready for review May 27, 2025 21:50
@gacevicljubisa gacevicljubisa requested a review from janos May 28, 2025 08:36
@zelig zelig added ready for review The PR is ready to be reviewed in progress ongoing development , hold out with review go Pull requests that update go code labels May 29, 2025
@zelig
Copy link
Member

zelig commented May 30, 2025

@mfw78 please fix linting errors for a green CI, ta

@mfw78
Copy link
Collaborator Author

mfw78 commented May 30, 2025

@mfw78 please fix linting errors for a green CI, ta

I've bumped to the latest golang linters for CI/CD within this PR, the failing CI/CD currently seems limited to beekeeper settlement tests.

@gacevicljubisa gacevicljubisa self-requested a review June 2, 2025 07:51
@gacevicljubisa gacevicljubisa requested a review from nugaon July 3, 2025 15:07
ProtocolName = "handshake"
// ProtocolVersion is the current handshake protocol version.
ProtocolVersion = "13.0.0"
ProtocolVersion = "14.0.0"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have to increase this once more, ProtocolVersion was already increased in the meanwhile

Copy link
Member

@nugaon nugaon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice initiative!

- packaging/**
branches:
- '**'
push:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unnecessary indentation doubling on each line.

// F F F
//
// F F F F S
// # F F F F S
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unnecessary change as well as below

- goheader
text: go-ethereum Authors
- linters:
- goconst
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

originally, next to the rules there we comments like "temporally disable...", but this is how temporal solutions work usually : D

return &stream{Stream: s, metrics: metrics}
}

func newStreamWithHeaders(s network.Stream, metrics metrics, ctx context.Context, headler p2p.HeadlerFunc, peerAddress swarm.Address, headers p2p.Headers) (*stream, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

headers parameter is unused and ctx is usually the first parameter


// Write data to trigger the protocol handler
if stream != nil {
_, _ = stream.Write([]byte("test"))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would do error check here because it may lead to silent failure.

return
// Check if BOTH local and remote peer support trace headers
// At this point, handshake has completed and capabilities are known
peerCaps := s.peers.capabilities(overlay)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it could be optimized by mapping to the capabilities only once and get traceability flag along with fullnode flag (above this change)

}

func TestInfo_CapabilitiesReuse(t *testing.T) {
// Test that Info struct reuses the protobuf Capabilities object
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does it test the pointer functionality in Go? :D

}

func (m *Syn) Marshal() (dAtA []byte, err error) {
// 438 bytes of a gzipped FileDescriptorProto
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get different output by running
protoc --gogofaster_out=. --gogofaster_opt=paths=source_relative,Mgzip.proto ./pkg/p2p/libp2p/internal/handshake/pb/handshake.proto


if s.picker != nil {
if !s.picker.Pick(p2p.Peer{Address: overlay, FullNode: ack.FullNode}) {
if !s.picker.Pick(p2p.Peer{Address: overlay, FullNode: ack.Capabilities.FullNode}) {
Copy link
Member

@nugaon nugaon Jul 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Protocol Buffers does not automatically create empty objects for missing fields and it can cause nil pointer dereference here since there is no nil check on Capabilities.

BzzAddress: remoteBzzAddress,
FullNode: ack.FullNode,
BzzAddress: remoteBzzAddress,
Capabilities: ack.Capabilities,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above

Copy link
Member

@janos janos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Capabilities

Grouping Capabilities into a dedicated protobuf message is nice and adds clarity to the specific features that a node can provide.

Stream Headers

The Headers feature that is added to the Streams in bee are supposed to be a general purpose headers as string key/value pairs similar to the HTTP headers, as libp2p did not have any way to share metadata upon establishing Streams. Tracing is using Stream Headers to avoid adding tracing field to most of bee protobuf messages. So every mention of the tracing alongside headers in names should be removed.

At the time it was measured that headers did not add any significant overhead to streams. I hope that making Headers optional is influenced by measurements and metrics that support this change.

Unrelated changes

This PR holds some changes that are unrelated to the handshake protocol, and I think that they should be part of other PRs.

GOBIN ?= $$($(GO) env GOPATH)/bin
GOLANGCI_LINT ?= $(GOBIN)/golangci-lint
GOLANGCI_LINT_VERSION ?= v1.64.5
GOLANGCI_LINT_VERSION ?= v2.1.6
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Raising golangci-lint version to 2 involves a larger set of changes in this PR which as a different purpose. I would recommend to create a separate PR for improvimg golangci-lint usage.


logger.Debug("write root", "hash", rootRef)
_, err = writer.WriteString(fmt.Sprintf("%s\n", rootRef))
_, err = fmt.Fprintf(writer, "%s\n", rootRef)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While this change can be considered as an improvement, as well as a change a few lines below, it does not relate to the handshake protocol and are not crucial.

hostFactory func(...libp2p.Option) (host.Host, error)
HeadersRWTimeout time.Duration
Registry *prometheus.Registry
EnableTraceHeaders bool
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EnableTraceHeaders should be named only EnableHeaders as the whole headers functionality is now optional, not only tracing. Tracing is just one of the possible headers. Headers in bee streams have general purpose just as HTTP protocol headers are, just keys and values. It is ok to make them optional, like here, but tracing is just one of the header key/value pair. I do not think that there are any other usages of headers currently.


// PendingNonceAt returns the nonce we should use, but we will
// compare this to our pending tx list, therefore the -1.
var maxNonce uint64 = onchainNonce - 1
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file has an unrelated changes to the handshake protocol.

peerCaps := s.peers.capabilities(overlay)
localSupportsTrace := s.handshakeService.SupportsTraceHeaders()
remoteSupportsTrace := peerCaps != nil && peerCaps.TraceHeaders
bothSupportTrace := localSupportsTrace && remoteSupportsTrace
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thise value should be called localSupportsHeaders, remoteSupportsTrace, bothSupportHeaders...

Ack Ack = 2;
message Capabilities {
bool full_node = 1;
bool trace_headers = 2;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just as on other places, where I provided some context on the naming for similar flags, this filed should be called supports_headers or something similar, just without tracing, as tracing is just one feature that uses headers feature.

option go_package = "pb";

message Syn {
message Handshake {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure why Handshake message is needed as it only wraps existing messages. Maybe this indirection could be avoided and only to use explicit messages as before as their order is sequential and deterministic in the protocol?

return found, full, peerID
}

func (r *peerRegistry) capabilities(overlay swarm.Address) *pb.Capabilities {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this would be a cleaner solution:

func (r *peerRegistry) capabilities(overlay swarm.Address) *pb.Capabilities {
	r.mu.RLock()
	defer r.mu.RUnlock()

	peerID, exists := r.underlays[overlay.ByteString()]
	if !exists {
		return nil
	}

	return r.peerCapabilities[peerID]
}

@nugaon
Copy link
Member

nugaon commented Jul 14, 2025

investigated the beekeeper test error and it turned out swap protocol also uses libp2p header exchange.
I uploaded the fix here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

go Pull requests that update go code in progress ongoing development , hold out with review ready for review The PR is ready to be reviewed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants