p2p accounting (#17951)

* p2p/protocols: introduced protocol accounting

* p2p/protocols: added TestExchange simulation

* p2p/protocols: add accounting simulation

* p2p/protocols: remove unnecessary tests

* p2p/protocols: comments for accounting simulation

* p2p/protocols: addressed PR comments

* p2p/protocols: finalized accounting implementation

* p2p/protocols: removed unused code

* p2p/protocols: addressed @nonsense PR comments
This commit is contained in:
holisticode
2018-10-25 17:26:31 -05:00
committed by Viktor Trón
parent 80d3907767
commit 8ed4739176
5 changed files with 937 additions and 0 deletions

View File

@ -17,12 +17,15 @@
package protocols
import (
"bytes"
"context"
"errors"
"fmt"
"testing"
"time"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
@ -185,6 +188,169 @@ func runProtoHandshake(t *testing.T, proto *protoHandshake, errs ...error) {
}
}
type dummyHook struct {
peer *Peer
size uint32
msg interface{}
send bool
err error
waitC chan struct{}
}
type dummyMsg struct {
Content string
}
func (d *dummyHook) Send(peer *Peer, size uint32, msg interface{}) error {
d.peer = peer
d.size = size
d.msg = msg
d.send = true
return d.err
}
func (d *dummyHook) Receive(peer *Peer, size uint32, msg interface{}) error {
d.peer = peer
d.size = size
d.msg = msg
d.send = false
d.waitC <- struct{}{}
return d.err
}
func TestProtocolHook(t *testing.T) {
testHook := &dummyHook{
waitC: make(chan struct{}, 1),
}
spec := &Spec{
Name: "test",
Version: 42,
MaxMsgSize: 10 * 1024,
Messages: []interface{}{
dummyMsg{},
},
Hook: testHook,
}
runFunc := func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
peer := NewPeer(p, rw, spec)
ctx := context.TODO()
err := peer.Send(ctx, &dummyMsg{
Content: "handshake"})
if err != nil {
t.Fatal(err)
}
handle := func(ctx context.Context, msg interface{}) error {
return nil
}
return peer.Run(handle)
}
conf := adapters.RandomNodeConfig()
tester := p2ptest.NewProtocolTester(t, conf.ID, 2, runFunc)
err := tester.TestExchanges(p2ptest.Exchange{
Expects: []p2ptest.Expect{
{
Code: 0,
Msg: &dummyMsg{Content: "handshake"},
Peer: tester.Nodes[0].ID(),
},
},
})
if err != nil {
t.Fatal(err)
}
if testHook.msg == nil || testHook.msg.(*dummyMsg).Content != "handshake" {
t.Fatal("Expected msg to be set, but it is not")
}
if !testHook.send {
t.Fatal("Expected a send message, but it is not")
}
if testHook.peer == nil || testHook.peer.ID() != tester.Nodes[0].ID() {
t.Fatal("Expected peer ID to be set correctly, but it is not")
}
if testHook.size != 11 { //11 is the length of the encoded message
t.Fatalf("Expected size to be %d, but it is %d ", 1, testHook.size)
}
err = tester.TestExchanges(p2ptest.Exchange{
Triggers: []p2ptest.Trigger{
{
Code: 0,
Msg: &dummyMsg{Content: "response"},
Peer: tester.Nodes[1].ID(),
},
},
})
<-testHook.waitC
if err != nil {
t.Fatal(err)
}
if testHook.msg == nil || testHook.msg.(*dummyMsg).Content != "response" {
t.Fatal("Expected msg to be set, but it is not")
}
if testHook.send {
t.Fatal("Expected a send message, but it is not")
}
if testHook.peer == nil || testHook.peer.ID() != tester.Nodes[1].ID() {
t.Fatal("Expected peer ID to be set correctly, but it is not")
}
if testHook.size != 10 { //11 is the length of the encoded message
t.Fatalf("Expected size to be %d, but it is %d ", 1, testHook.size)
}
testHook.err = fmt.Errorf("dummy error")
err = tester.TestExchanges(p2ptest.Exchange{
Triggers: []p2ptest.Trigger{
{
Code: 0,
Msg: &dummyMsg{Content: "response"},
Peer: tester.Nodes[1].ID(),
},
},
})
<-testHook.waitC
time.Sleep(100 * time.Millisecond)
err = tester.TestDisconnected(&p2ptest.Disconnect{tester.Nodes[1].ID(), testHook.err})
if err != nil {
t.Fatalf("Expected a specific disconnect error, but got different one: %v", err)
}
}
//We need to test that if the hook is not defined, then message infrastructure
//(send,receive) still works
func TestNoHook(t *testing.T) {
//create a test spec
spec := createTestSpec()
//a random node
id := adapters.RandomNodeConfig().ID
//a peer
p := p2p.NewPeer(id, "testPeer", nil)
rw := &dummyRW{}
peer := NewPeer(p, rw, spec)
ctx := context.TODO()
msg := &perBytesMsgSenderPays{Content: "testBalance"}
//send a message
err := peer.Send(ctx, msg)
if err != nil {
t.Fatal(err)
}
//simulate receiving a message
rw.msg = msg
peer.handleIncoming(func(ctx context.Context, msg interface{}) error {
return nil
})
//all should just work and not result in any error
}
func TestProtoHandshakeVersionMismatch(t *testing.T) {
runProtoHandshake(t, &protoHandshake{41, "420"}, errorf(ErrHandshake, errorf(ErrHandler, "(msg code 0): 41 (!= 42)").Error()))
}
@ -386,3 +552,39 @@ func XTestMultiplePeersDropOther(t *testing.T) {
fmt.Errorf("subprotocol error"),
)
}
//dummy implementation of a MsgReadWriter
//this allows for quick and easy unit tests without
//having to build up the complete protocol
type dummyRW struct {
msg interface{}
size uint32
code uint64
}
func (d *dummyRW) WriteMsg(msg p2p.Msg) error {
return nil
}
func (d *dummyRW) ReadMsg() (p2p.Msg, error) {
enc := bytes.NewReader(d.getDummyMsg())
return p2p.Msg{
Code: d.code,
Size: d.size,
Payload: enc,
ReceivedAt: time.Now(),
}, nil
}
func (d *dummyRW) getDummyMsg() []byte {
r, _ := rlp.EncodeToBytes(d.msg)
var b bytes.Buffer
wmsg := WrappedMsg{
Context: b.Bytes(),
Size: uint32(len(r)),
Payload: r,
}
rr, _ := rlp.EncodeToBytes(wmsg)
d.size = uint32(len(rr))
return rr
}