Skip to content

Commit

Permalink
p2p: move rlpx into separate package (ethereum#21464)
Browse files Browse the repository at this point in the history
This change moves the RLPx protocol implementation into a separate package,
p2p/rlpx. The new package can be used to establish RLPx connections for
protocol testing purposes.

Co-authored-by: Felix Lange <[email protected]>
  • Loading branch information
2 people authored and enriquefynn committed Feb 15, 2021
1 parent 483e6a3 commit 20f39de
Show file tree
Hide file tree
Showing 10 changed files with 901 additions and 742 deletions.
1 change: 1 addition & 0 deletions cmd/devp2p/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ func init() {
discv5Command,
dnsCommand,
nodesetCommand,
rlpxCommand,
}
}

Expand Down
94 changes: 94 additions & 0 deletions cmd/devp2p/rlpxcmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Copyright 2020 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.

package main

import (
"fmt"
"net"

"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/rlpx"
"github.com/ethereum/go-ethereum/rlp"
"gopkg.in/urfave/cli.v1"
)

var (
rlpxCommand = cli.Command{
Name: "rlpx",
Usage: "RLPx Commands",
Subcommands: []cli.Command{
rlpxPingCommand,
},
}
rlpxPingCommand = cli.Command{
Name: "ping",
Usage: "Perform a RLPx handshake",
ArgsUsage: "<node>",
Action: rlpxPing,
}
)

func rlpxPing(ctx *cli.Context) error {
n := getNodeArg(ctx)

fd, err := net.Dial("tcp", fmt.Sprintf("%v:%d", n.IP(), n.TCP()))
if err != nil {
return err
}
conn := rlpx.NewConn(fd, n.Pubkey())

ourKey, _ := crypto.GenerateKey()
_, err = conn.Handshake(ourKey)
if err != nil {
return err
}

code, data, _, err := conn.Read()
if err != nil {
return err
}
switch code {
case 0:
var h devp2pHandshake
if err := rlp.DecodeBytes(data, &h); err != nil {
return fmt.Errorf("invalid handshake: %v", err)
}
fmt.Printf("%+v\n", h)
case 1:
var msg []p2p.DiscReason
if rlp.DecodeBytes(data, &msg); len(msg) == 0 {
return fmt.Errorf("invalid disconnect message")
}
return fmt.Errorf("received disconnect message: %v", msg[0])
default:
return fmt.Errorf("invalid message code %d, expected handshake (code zero)", code)
}
return nil
}

// devp2pHandshake is the RLP structure of the devp2p protocol handshake.
type devp2pHandshake struct {
Version uint64
Name string
Caps []p2p.Cap
ListenPort uint64
ID hexutil.Bytes // secp256k1 public key
// Ignore additional fields (for forward compatibility).
Rest []rlp.RawValue `rlp:"tail"`
}
11 changes: 0 additions & 11 deletions p2p/message_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,9 @@ package p2p

import (
"bytes"
"encoding/hex"
"fmt"
"io"
"runtime"
"strings"
"testing"
"time"
)
Expand Down Expand Up @@ -141,12 +139,3 @@ func TestEOFSignal(t *testing.T) {
default:
}
}

func unhex(str string) []byte {
r := strings.NewReplacer("\t", "", " ", "", "\n", "")
b, err := hex.DecodeString(r.Replace(str))
if err != nil {
panic(fmt.Sprintf("invalid hex string: %q", str))
}
return b
}
15 changes: 12 additions & 3 deletions p2p/peer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,15 @@ func newNode(id enode.ID, addr string) *enode.Node {
}

func testPeer(protos []Protocol) (func(), *conn, *Peer, <-chan error) {
fd1, fd2 := net.Pipe()
c1 := &conn{fd: fd1, node: newNode(randomID(), ""), transport: newTestTransport(&newkey().PublicKey, fd1)}
c2 := &conn{fd: fd2, node: newNode(randomID(), ""), transport: newTestTransport(&newkey().PublicKey, fd2)}
var (
fd1, fd2 = net.Pipe()
key1, key2 = newkey(), newkey()
t1 = newTestTransport(&key2.PublicKey, fd1, nil)
t2 = newTestTransport(&key1.PublicKey, fd2, &key1.PublicKey)
)

c1 := &conn{fd: fd1, node: newNode(uintID(1), ""), transport: t1}
c2 := &conn{fd: fd2, node: newNode(uintID(2), ""), transport: t2}
for _, p := range protos {
c1.caps = append(c1.caps, p.cap())
c2.caps = append(c2.caps, p.cap())
Expand Down Expand Up @@ -173,9 +179,12 @@ func TestPeerPing(t *testing.T) {
}
}

// This test checks that a disconnect message sent by a peer is returned
// as the error from Peer.run.
func TestPeerDisconnect(t *testing.T) {
closer, rw, _, disc := testPeer(nil)
defer closer()

if err := SendItems(rw, discMsg, DiscQuitting); err != nil {
t.Fatal(err)
}
Expand Down
Loading

0 comments on commit 20f39de

Please sign in to comment.