Skip to content

Commit fe77fee

Browse files
acudzelig
authored andcommitted
bzzeth: initial support for bzz-eth protocol (ethersphere#1571)
* bzzeth: phase 0: handshake
1 parent e821eb4 commit fe77fee

File tree

5 files changed

+478
-3
lines changed

5 files changed

+478
-3
lines changed

bzzeth/bzzeth.go

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
// Copyright 2019 The Swarm Authors
2+
// This file is part of the Swarm library.
3+
//
4+
// The Swarm library is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Lesser General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// The Swarm library is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Lesser General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Lesser General Public License
15+
// along with the Swarm library. If not, see <http://www.gnu.org/licenses/>.
16+
17+
package bzzeth
18+
19+
import (
20+
"context"
21+
22+
"github.com/ethereum/go-ethereum/node"
23+
"github.com/ethereum/go-ethereum/p2p"
24+
"github.com/ethereum/go-ethereum/rpc"
25+
"github.com/ethersphere/swarm/log"
26+
"github.com/ethersphere/swarm/p2p/protocols"
27+
)
28+
29+
// BzzEth implements node.Service
30+
var _ node.Service = &BzzEth{}
31+
32+
// BzzEth is a global module handling ethereum state on swarm
33+
type BzzEth struct {
34+
peers *peers // bzzeth peer pool
35+
quit chan struct{} // quit channel to close go routines
36+
}
37+
38+
// New constructs the BzzEth node service
39+
func New() *BzzEth {
40+
return &BzzEth{
41+
peers: newPeers(),
42+
quit: make(chan struct{}),
43+
}
44+
}
45+
46+
// Run is the bzzeth protocol run function.
47+
// - creates a peer
48+
// - checks if it is a swarm node, put the protocol in idle mode
49+
// - performs handshake
50+
// - adds peer to the peerpool
51+
// - starts incoming message handler loop
52+
func (b *BzzEth) Run(p *p2p.Peer, rw p2p.MsgReadWriter) error {
53+
peer := protocols.NewPeer(p, rw, Spec)
54+
bp := NewPeer(peer)
55+
56+
// perform handshake and register if peer serves headers
57+
handshake, err := bp.Handshake(context.TODO(), Handshake{ServeHeaders: true}, nil)
58+
if err != nil {
59+
return err
60+
}
61+
bp.serveHeaders = handshake.(*Handshake).ServeHeaders
62+
log.Debug("handshake", "hs", handshake, "peer", bp)
63+
64+
// This protocol is all about interaction between an Eth node and a Swarm Node.
65+
// If another swarm node tries to connect then the protocol goes into idle
66+
if isSwarmNodeFunc(bp) {
67+
<-b.quit
68+
return nil
69+
}
70+
b.peers.add(bp)
71+
defer b.peers.remove(bp)
72+
73+
return peer.Run(b.handleMsg(bp))
74+
}
75+
76+
// handleMsg is the message handler that delegates incoming messages
77+
// handlers are called asynchronously so handler calls do not block incoming msg processing
78+
func (b *BzzEth) handleMsg(p *Peer) func(context.Context, interface{}) error {
79+
return func(ctx context.Context, msg interface{}) error {
80+
p.logger.Debug("bzzeth.handleMsg")
81+
switch msg.(type) {
82+
default:
83+
}
84+
return nil
85+
}
86+
}
87+
88+
// Protocols returns the p2p protocol
89+
func (b *BzzEth) Protocols() []p2p.Protocol {
90+
return []p2p.Protocol{
91+
{
92+
Name: Spec.Name,
93+
Version: Spec.Version,
94+
Length: Spec.Length(),
95+
Run: b.Run,
96+
},
97+
}
98+
}
99+
100+
// APIs return APIs defined on the node service
101+
func (b *BzzEth) APIs() []rpc.API {
102+
return nil
103+
}
104+
105+
// Start starts the BzzEth node service
106+
func (b *BzzEth) Start(server *p2p.Server) error {
107+
log.Info("bzzeth starting...")
108+
return nil
109+
}
110+
111+
// Stop stops the BzzEth node service
112+
func (b *BzzEth) Stop() error {
113+
log.Info("bzzeth shutting down...")
114+
close(b.quit)
115+
return nil
116+
}

bzzeth/bzzeth_test.go

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
// Copyright 2019 The Swarm Authors
2+
// This file is part of the Swarm library.
3+
//
4+
// The Swarm library is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Lesser General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// The Swarm library is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Lesser General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Lesser General Public License
15+
// along with the Swarm library. If not, see <http://www.gnu.org/licenses/>.
16+
17+
package bzzeth
18+
19+
import (
20+
"errors"
21+
"flag"
22+
"os"
23+
"testing"
24+
"time"
25+
26+
"github.com/ethereum/go-ethereum/crypto"
27+
"github.com/ethereum/go-ethereum/log"
28+
"github.com/ethereum/go-ethereum/p2p/enode"
29+
p2ptest "github.com/ethersphere/swarm/p2p/testing"
30+
)
31+
32+
var (
33+
loglevel = flag.Int("loglevel", 0, "verbosity of logs")
34+
)
35+
36+
func init() {
37+
flag.Parse()
38+
39+
log.PrintOrigins(true)
40+
log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*loglevel), log.StreamHandler(os.Stderr, log.TerminalFormat(false))))
41+
}
42+
43+
func newBzzEthTester() (*p2ptest.ProtocolTester, *BzzEth, func(), error) {
44+
b := New()
45+
46+
prvkey, err := crypto.GenerateKey()
47+
if err != nil {
48+
return nil, nil, nil, err
49+
}
50+
51+
protocolTester := p2ptest.NewProtocolTester(prvkey, 1, b.Run)
52+
teardown := func() {
53+
protocolTester.Stop()
54+
}
55+
56+
return protocolTester, b, teardown, nil
57+
}
58+
59+
func handshakeExchange(tester *p2ptest.ProtocolTester, peerID enode.ID, serveHeadersPeer, serveHeadersPivot bool) error {
60+
return tester.TestExchanges(
61+
p2ptest.Exchange{
62+
Label: "Handshake",
63+
Triggers: []p2ptest.Trigger{
64+
{
65+
Code: 0,
66+
Msg: Handshake{
67+
ServeHeaders: serveHeadersPeer,
68+
},
69+
Peer: peerID,
70+
},
71+
},
72+
Expects: []p2ptest.Expect{
73+
{
74+
Code: 0,
75+
Msg: Handshake{
76+
ServeHeaders: serveHeadersPivot,
77+
},
78+
Peer: peerID,
79+
},
80+
},
81+
})
82+
}
83+
84+
// tests handshake between eth node and swarm node
85+
// on successful handshake the protocol does not go idle
86+
// peer added to the pool and serves headers is registered
87+
func TestBzzEthHandshake(t *testing.T) {
88+
tester, b, teardown, err := newBzzEthTester()
89+
if err != nil {
90+
t.Fatal(err)
91+
}
92+
defer teardown()
93+
94+
node := tester.Nodes[0]
95+
err = handshakeExchange(tester, node.ID(), true, true)
96+
if err != nil {
97+
t.Fatalf("expected no error, got %v", err)
98+
}
99+
100+
// after successful handshake, expect peer added to peer pool
101+
var p *Peer
102+
for i := 0; i < 10; i++ {
103+
p = b.peers.get(node.ID())
104+
if p != nil {
105+
break
106+
}
107+
time.Sleep(100 * time.Millisecond)
108+
}
109+
if p == nil {
110+
t.Fatal("bzzeth peer not added")
111+
}
112+
113+
if !p.serveHeaders {
114+
t.Fatal("bzzeth peer serveHeaders not set")
115+
}
116+
117+
close(b.quit)
118+
err = tester.TestDisconnected(&p2ptest.Disconnect{Peer: node.ID(), Error: errors.New("?")})
119+
if err == nil || err.Error() != "timed out waiting for peers to disconnect" {
120+
t.Fatal(err)
121+
}
122+
}
123+
124+
// TestBzzBzzHandshake tests that a handshake between two Swarm nodes
125+
func TestBzzBzzHandshake(t *testing.T) {
126+
tester, b, teardown, err := newBzzEthTester()
127+
if err != nil {
128+
t.Fatal(err)
129+
}
130+
defer teardown()
131+
132+
// redefine isSwarmNodeFunc to force recognise remote peer as swarm node
133+
defer func(f func(*Peer) bool) {
134+
isSwarmNodeFunc = f
135+
}(isSwarmNodeFunc)
136+
isSwarmNodeFunc = func(_ *Peer) bool { return true }
137+
138+
node := tester.Nodes[0]
139+
err = handshakeExchange(tester, node.ID(), false, true)
140+
if err != nil {
141+
t.Fatalf("expected no error, got %v", err)
142+
}
143+
144+
// after handshake expect protocol to hang, peer not added to pool
145+
p := b.peers.get(node.ID())
146+
if p != nil {
147+
t.Fatal("bzzeth swarm peer incorrectly added")
148+
}
149+
150+
// after closing the ptotocall, expect disconnect
151+
close(b.quit)
152+
err = tester.TestDisconnected(&p2ptest.Disconnect{Peer: node.ID(), Error: errors.New("protocol returned")})
153+
if err != nil {
154+
t.Fatal(err)
155+
}
156+
157+
}

0 commit comments

Comments
 (0)