From 94500ae52b1663458b640b0a01e98a7d9f8a187f Mon Sep 17 00:00:00 2001 From: guillaumemichel Date: Fri, 9 Aug 2024 13:14:27 +0200 Subject: [PATCH 1/4] adding quic field to enr --- p2p/enode/localnode.go | 2 +- p2p/enode/node.go | 24 +++++++++++++++++++++--- p2p/enode/node_test.go | 22 +++++++++++++++++----- p2p/enr/entries.go | 10 ++++++++++ 4 files changed, 49 insertions(+), 9 deletions(-) diff --git a/p2p/enode/localnode.go b/p2p/enode/localnode.go index 6e79c9cbdc74..255b673594a5 100644 --- a/p2p/enode/localnode.go +++ b/p2p/enode/localnode.go @@ -298,7 +298,7 @@ func (ln *LocalNode) sign() { panic(fmt.Errorf("enode: can't verify local record: %v", err)) } ln.cur.Store(n) - log.Info("New local node record", "seq", ln.seq, "id", n.ID(), "ip", n.IPAddr(), "udp", n.UDP(), "tcp", n.TCP()) + log.Info("New local node record", "seq", ln.seq, "id", n.ID(), "ip", n.IPAddr(), "udp", n.UDP(), "tcp", n.TCP(), "quic", n.QUIC()) } func (ln *LocalNode) bumpSeq() { diff --git a/p2p/enode/node.go b/p2p/enode/node.go index cb4ac8d1726c..c4dcfb8e20e2 100644 --- a/p2p/enode/node.go +++ b/p2p/enode/node.go @@ -38,9 +38,10 @@ type Node struct { r enr.Record id ID // endpoint information - ip netip.Addr - udp uint16 - tcp uint16 + ip netip.Addr + udp uint16 + tcp uint16 + quic uint16 } // New wraps a node record. The record must be valid according to the given @@ -105,6 +106,7 @@ func (n *Node) setIP4(ip netip.Addr) { n.ip = ip n.Load((*enr.UDP)(&n.udp)) n.Load((*enr.TCP)(&n.tcp)) + n.Load((*enr.QUIC)(&n.quic)) } func (n *Node) setIP6(ip netip.Addr) { @@ -119,6 +121,9 @@ func (n *Node) setIP6(ip netip.Addr) { if err := n.Load((*enr.TCP6)(&n.tcp)); err != nil { n.Load((*enr.TCP)(&n.tcp)) } + if err := n.Load((*enr.QUIC6)(&n.quic)); err != nil { + n.Load((*enr.QUIC)(&n.quic)) + } } // MustParse parses a node record or enode:// URL. It panics if the input is invalid. @@ -184,6 +189,11 @@ func (n *Node) TCP() int { return int(n.tcp) } +// QUIC returns the QUIC port of the node. +func (n *Node) QUIC() int { + return int(n.quic) +} + // UDPEndpoint returns the announced UDP endpoint. func (n *Node) UDPEndpoint() (netip.AddrPort, bool) { if !n.ip.IsValid() || n.ip.IsUnspecified() || n.udp == 0 { @@ -200,6 +210,14 @@ func (n *Node) TCPEndpoint() (netip.AddrPort, bool) { return netip.AddrPortFrom(n.ip, n.tcp), true } +// QUICEndpoint returns the announced QUIC endpoint. +func (n *Node) QUICEndpoint() (netip.AddrPort, bool) { + if !n.ip.IsValid() || n.ip.IsUnspecified() || n.quic == 0 { + return netip.AddrPort{}, false + } + return netip.AddrPortFrom(n.ip, n.quic), true +} + // Pubkey returns the secp256k1 public key of the node, if present. func (n *Node) Pubkey() *ecdsa.PublicKey { var key ecdsa.PublicKey diff --git a/p2p/enode/node_test.go b/p2p/enode/node_test.go index 56e196e82e2d..05946b50b605 100644 --- a/p2p/enode/node_test.go +++ b/p2p/enode/node_test.go @@ -68,11 +68,12 @@ func TestPythonInterop(t *testing.T) { func TestNodeEndpoints(t *testing.T) { id := HexID("00000000000000806ad9b61fa5ae014307ebdc964253adcd9f2c0a392aa11abc") type endpointTest struct { - name string - node *Node - wantIP netip.Addr - wantUDP int - wantTCP int + name string + node *Node + wantIP netip.Addr + wantUDP int + wantTCP int + wantQUIC int } tests := []endpointTest{ { @@ -98,6 +99,14 @@ func TestNodeEndpoints(t *testing.T) { return SignNull(&r, id) }(), }, + { + name: "quic-only", + node: func() *Node { + var r enr.Record + r.Set(enr.QUIC(9000)) + return SignNull(&r, id) + }(), + }, { name: "ipv4-only-loopback", node: func() *Node { @@ -222,6 +231,9 @@ func TestNodeEndpoints(t *testing.T) { if test.wantTCP != test.node.TCP() { t.Errorf("node has wrong TCP port %d, want %d", test.node.TCP(), test.wantTCP) } + if test.wantQUIC != test.node.QUIC() { + t.Errorf("node has wrong QUIC port %d, want %d", test.node.QUIC(), test.wantQUIC) + } }) } } diff --git a/p2p/enr/entries.go b/p2p/enr/entries.go index 155ec4c02320..58e660c15471 100644 --- a/p2p/enr/entries.go +++ b/p2p/enr/entries.go @@ -77,6 +77,16 @@ type UDP6 uint16 func (v UDP6) ENRKey() string { return "udp6" } +// QUIC is the "quic" key, which holds the QUIC port of the node. +type QUIC uint16 + +func (v QUIC) ENRKey() string { return "quic" } + +// QUIC6 is the "quic6" key, which holds the IPv6-specific quic6 port of the node. +type QUIC6 uint16 + +func (v QUIC6) ENRKey() string { return "quic6" } + // ID is the "id" key, which holds the name of the identity scheme. type ID string From f3ab1b3df139b9172c5ba1c4d506964fbd185156 Mon Sep 17 00:00:00 2001 From: guillaumemichel Date: Fri, 9 Aug 2024 14:21:44 +0200 Subject: [PATCH 2/4] p2p: removed quic accessor --- p2p/enode/localnode.go | 2 +- p2p/enode/node.go | 22 +++++++--------------- p2p/enode/node_test.go | 14 +++++--------- 3 files changed, 13 insertions(+), 25 deletions(-) diff --git a/p2p/enode/localnode.go b/p2p/enode/localnode.go index 255b673594a5..6e79c9cbdc74 100644 --- a/p2p/enode/localnode.go +++ b/p2p/enode/localnode.go @@ -298,7 +298,7 @@ func (ln *LocalNode) sign() { panic(fmt.Errorf("enode: can't verify local record: %v", err)) } ln.cur.Store(n) - log.Info("New local node record", "seq", ln.seq, "id", n.ID(), "ip", n.IPAddr(), "udp", n.UDP(), "tcp", n.TCP(), "quic", n.QUIC()) + log.Info("New local node record", "seq", ln.seq, "id", n.ID(), "ip", n.IPAddr(), "udp", n.UDP(), "tcp", n.TCP()) } func (ln *LocalNode) bumpSeq() { diff --git a/p2p/enode/node.go b/p2p/enode/node.go index c4dcfb8e20e2..c8d521529f67 100644 --- a/p2p/enode/node.go +++ b/p2p/enode/node.go @@ -38,10 +38,9 @@ type Node struct { r enr.Record id ID // endpoint information - ip netip.Addr - udp uint16 - tcp uint16 - quic uint16 + ip netip.Addr + udp uint16 + tcp uint16 } // New wraps a node record. The record must be valid according to the given @@ -106,7 +105,6 @@ func (n *Node) setIP4(ip netip.Addr) { n.ip = ip n.Load((*enr.UDP)(&n.udp)) n.Load((*enr.TCP)(&n.tcp)) - n.Load((*enr.QUIC)(&n.quic)) } func (n *Node) setIP6(ip netip.Addr) { @@ -121,9 +119,6 @@ func (n *Node) setIP6(ip netip.Addr) { if err := n.Load((*enr.TCP6)(&n.tcp)); err != nil { n.Load((*enr.TCP)(&n.tcp)) } - if err := n.Load((*enr.QUIC6)(&n.quic)); err != nil { - n.Load((*enr.QUIC)(&n.quic)) - } } // MustParse parses a node record or enode:// URL. It panics if the input is invalid. @@ -189,11 +184,6 @@ func (n *Node) TCP() int { return int(n.tcp) } -// QUIC returns the QUIC port of the node. -func (n *Node) QUIC() int { - return int(n.quic) -} - // UDPEndpoint returns the announced UDP endpoint. func (n *Node) UDPEndpoint() (netip.AddrPort, bool) { if !n.ip.IsValid() || n.ip.IsUnspecified() || n.udp == 0 { @@ -212,10 +202,12 @@ func (n *Node) TCPEndpoint() (netip.AddrPort, bool) { // QUICEndpoint returns the announced QUIC endpoint. func (n *Node) QUICEndpoint() (netip.AddrPort, bool) { - if !n.ip.IsValid() || n.ip.IsUnspecified() || n.quic == 0 { + var quic enr.QUIC + n.Load(&quic) + if !n.ip.IsValid() || n.ip.IsUnspecified() || quic == 0 { return netip.AddrPort{}, false } - return netip.AddrPortFrom(n.ip, n.quic), true + return netip.AddrPortFrom(n.ip, uint16(quic)), true } // Pubkey returns the secp256k1 public key of the node, if present. diff --git a/p2p/enode/node_test.go b/p2p/enode/node_test.go index 05946b50b605..457d27192782 100644 --- a/p2p/enode/node_test.go +++ b/p2p/enode/node_test.go @@ -68,12 +68,11 @@ func TestPythonInterop(t *testing.T) { func TestNodeEndpoints(t *testing.T) { id := HexID("00000000000000806ad9b61fa5ae014307ebdc964253adcd9f2c0a392aa11abc") type endpointTest struct { - name string - node *Node - wantIP netip.Addr - wantUDP int - wantTCP int - wantQUIC int + name string + node *Node + wantIP netip.Addr + wantUDP int + wantTCP int } tests := []endpointTest{ { @@ -231,9 +230,6 @@ func TestNodeEndpoints(t *testing.T) { if test.wantTCP != test.node.TCP() { t.Errorf("node has wrong TCP port %d, want %d", test.node.TCP(), test.wantTCP) } - if test.wantQUIC != test.node.QUIC() { - t.Errorf("node has wrong QUIC port %d, want %d", test.node.QUIC(), test.wantQUIC) - } }) } } From 299eff3eb994cb8b2f7413b5424fb13a91cfb243 Mon Sep 17 00:00:00 2001 From: guillaumemichel Date: Tue, 13 Aug 2024 13:27:46 +0200 Subject: [PATCH 3/4] p2p: handle ip4/6 quic addresses --- p2p/enode/node.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/p2p/enode/node.go b/p2p/enode/node.go index c8d521529f67..4d93d3f6be8b 100644 --- a/p2p/enode/node.go +++ b/p2p/enode/node.go @@ -202,12 +202,16 @@ func (n *Node) TCPEndpoint() (netip.AddrPort, bool) { // QUICEndpoint returns the announced QUIC endpoint. func (n *Node) QUICEndpoint() (netip.AddrPort, bool) { - var quic enr.QUIC - n.Load(&quic) + var quic uint16 + if n.ip.Is4() || n.ip.Is4In6() { + n.Load((*enr.QUIC)(&quic)) + } else if n.ip.Is6() { + n.Load((*enr.QUIC6)(&quic)) + } if !n.ip.IsValid() || n.ip.IsUnspecified() || quic == 0 { return netip.AddrPort{}, false } - return netip.AddrPortFrom(n.ip, uint16(quic)), true + return netip.AddrPortFrom(n.ip, quic), true } // Pubkey returns the secp256k1 public key of the node, if present. From 3b4a63934c57e4e8a7c769384fe3fe34562ff186 Mon Sep 17 00:00:00 2001 From: lightclient Date: Sun, 18 Aug 2024 16:44:37 -0600 Subject: [PATCH 4/4] p2p/enode: more quic tests --- p2p/enode/node_test.go | 64 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 59 insertions(+), 5 deletions(-) diff --git a/p2p/enode/node_test.go b/p2p/enode/node_test.go index 457d27192782..f38c77415e07 100644 --- a/p2p/enode/node_test.go +++ b/p2p/enode/node_test.go @@ -68,11 +68,12 @@ func TestPythonInterop(t *testing.T) { func TestNodeEndpoints(t *testing.T) { id := HexID("00000000000000806ad9b61fa5ae014307ebdc964253adcd9f2c0a392aa11abc") type endpointTest struct { - name string - node *Node - wantIP netip.Addr - wantUDP int - wantTCP int + name string + node *Node + wantIP netip.Addr + wantUDP int + wantTCP int + wantQUIC int } tests := []endpointTest{ { @@ -106,6 +107,14 @@ func TestNodeEndpoints(t *testing.T) { return SignNull(&r, id) }(), }, + { + name: "quic6-only", + node: func() *Node { + var r enr.Record + r.Set(enr.QUIC6(9000)) + return SignNull(&r, id) + }(), + }, { name: "ipv4-only-loopback", node: func() *Node { @@ -217,6 +226,48 @@ func TestNodeEndpoints(t *testing.T) { wantIP: netip.MustParseAddr("192.168.2.2"), wantUDP: 30304, }, + { + name: "ipv4-quic", + node: func() *Node { + var r enr.Record + r.Set(enr.IPv4Addr(netip.MustParseAddr("99.22.33.1"))) + r.Set(enr.QUIC(9001)) + return SignNull(&r, id) + }(), + wantIP: netip.MustParseAddr("99.22.33.1"), + wantQUIC: 9001, + }, + { // Because the node is IPv4, the quic6 entry won't be loaded. + name: "ipv4-quic6", + node: func() *Node { + var r enr.Record + r.Set(enr.IPv4Addr(netip.MustParseAddr("99.22.33.1"))) + r.Set(enr.QUIC6(9001)) + return SignNull(&r, id) + }(), + wantIP: netip.MustParseAddr("99.22.33.1"), + }, + { + name: "ipv6-quic", + node: func() *Node { + var r enr.Record + r.Set(enr.IPv6Addr(netip.MustParseAddr("2001::ff00:0042:8329"))) + r.Set(enr.QUIC(9001)) + return SignNull(&r, id) + }(), + wantIP: netip.MustParseAddr("2001::ff00:0042:8329"), + }, + { + name: "ipv6-quic6", + node: func() *Node { + var r enr.Record + r.Set(enr.IPv6Addr(netip.MustParseAddr("2001::ff00:0042:8329"))) + r.Set(enr.QUIC6(9001)) + return SignNull(&r, id) + }(), + wantIP: netip.MustParseAddr("2001::ff00:0042:8329"), + wantQUIC: 9001, + }, } for _, test := range tests { @@ -230,6 +281,9 @@ func TestNodeEndpoints(t *testing.T) { if test.wantTCP != test.node.TCP() { t.Errorf("node has wrong TCP port %d, want %d", test.node.TCP(), test.wantTCP) } + if quic, _ := test.node.QUICEndpoint(); test.wantQUIC != int(quic.Port()) { + t.Errorf("node has wrong QUIC port %d, want %d", quic.Port(), test.wantQUIC) + } }) } }