diff options
-rw-r--r-- | eth/backend.go | 4 | ||||
-rw-r--r-- | eth/protocol.go | 95 | ||||
-rw-r--r-- | eth/protocol_test.go | 189 | ||||
-rw-r--r-- | p2p/handshake.go | 8 | ||||
-rw-r--r-- | p2p/message.go | 77 | ||||
-rw-r--r-- | p2p/message_test.go | 24 | ||||
-rw-r--r-- | p2p/peer.go | 7 | ||||
-rw-r--r-- | p2p/peer_test.go | 73 | ||||
-rw-r--r-- | p2p/rlpx_test.go | 4 | ||||
-rw-r--r-- | p2p/server.go | 11 | ||||
-rw-r--r-- | p2p/server_test.go | 2 | ||||
-rw-r--r-- | whisper/envelope.go | 2 | ||||
-rw-r--r-- | whisper/peer.go | 9 |
13 files changed, 227 insertions, 278 deletions
diff --git a/eth/backend.go b/eth/backend.go index 9e5324688..afe314d74 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -405,7 +405,7 @@ func (self *Ethereum) txBroadcastLoop() { // automatically stops if unsubscribe for obj := range self.txSub.Chan() { event := obj.(core.TxPreEvent) - self.net.Broadcast("eth", TxMsg, event.Tx.RlpData()) + self.net.Broadcast("eth", TxMsg, []*types.Transaction{event.Tx}) } } @@ -414,7 +414,7 @@ func (self *Ethereum) blockBroadcastLoop() { for obj := range self.blockSub.Chan() { switch ev := obj.(type) { case core.NewMinedBlockEvent: - self.net.Broadcast("eth", NewBlockMsg, ev.Block.RlpData(), ev.Block.Td) + self.net.Broadcast("eth", NewBlockMsg, []interface{}{ev.Block, ev.Block.Td}) } } } diff --git a/eth/protocol.go b/eth/protocol.go index a1c2ae688..6d610a663 100644 --- a/eth/protocol.go +++ b/eth/protocol.go @@ -89,7 +89,7 @@ type blockPool interface { RemovePeer(peerId string) } -// message structs used for rlp decoding +// message structs used for RLP serialization type newBlockMsgData struct { Block *types.Block TD *big.Int @@ -100,6 +100,14 @@ type getBlockHashesMsgData struct { Amount uint64 } +type statusMsgData struct { + ProtocolVersion uint32 + NetworkId uint32 + TD *big.Int + CurrentBlock common.Hash + GenesisBlock common.Hash +} + // main entrypoint, wrappers starting a server running the eth protocol // use this constructor to attach the protocol ("class") to server caps // the Dev p2p layer then runs the protocol instance on each peer @@ -132,18 +140,25 @@ func runEthProtocol(protocolVersion, networkId int, txPool txPool, chainManager }, id: fmt.Sprintf("%x", id[:8]), } - err = self.handleStatus() - if err == nil { - self.propagateTxs() - for { - err = self.handle() - if err != nil { - self.blockPool.RemovePeer(self.id) - break - } + + // handshake. + if err := self.handleStatus(); err != nil { + return err + } + defer self.blockPool.RemovePeer(self.id) + + // propagate existing transactions. new transactions appearing + // after this will be sent via broadcasts. + if err := p2p.Send(rw, TxMsg, txPool.GetTransactions()); err != nil { + return err + } + + // main loop. handle incoming messages. + for { + if err := self.handle(); err != nil { + return err } } - return } func (self *ethProtocol) handle() error { @@ -186,7 +201,7 @@ func (self *ethProtocol) handle() error { request.Amount = maxHashes } hashes := self.chainManager.GetBlockHashesFromHash(request.Hash, request.Amount) - return p2p.EncodeMsg(self.rw, BlockHashesMsg, rlp.Flat(hashes)) + return p2p.Send(self.rw, BlockHashesMsg, hashes) case BlockHashesMsg: msgStream := rlp.NewStream(msg.Payload) @@ -216,7 +231,7 @@ func (self *ethProtocol) handle() error { return err } - var blocks []interface{} + var blocks []*types.Block var i int for { i++ @@ -224,7 +239,7 @@ func (self *ethProtocol) handle() error { err := msgStream.Decode(&hash) if err == rlp.EOL { break - } else { + } else if err != nil { return self.protoError(ErrDecode, "msg %v: %v", msg, err) } @@ -236,7 +251,7 @@ func (self *ethProtocol) handle() error { break } } - return p2p.EncodeMsg(self.rw, BlocksMsg, blocks...) + return p2p.Send(self.rw, BlocksMsg, blocks) case BlocksMsg: msgStream := rlp.NewStream(msg.Payload) @@ -283,29 +298,8 @@ func (self *ethProtocol) handle() error { return nil } -type statusMsgData struct { - ProtocolVersion uint32 - NetworkId uint32 - TD *big.Int - CurrentBlock common.Hash - GenesisBlock common.Hash -} - -func (self *ethProtocol) statusMsg() p2p.Msg { - td, currentBlock, genesisBlock := self.chainManager.Status() - - return p2p.NewMsg(StatusMsg, - uint32(self.protocolVersion), - uint32(self.networkId), - td, - currentBlock, - genesisBlock, - ) -} - func (self *ethProtocol) handleStatus() error { - // send precanned status message - if err := self.rw.WriteMsg(self.statusMsg()); err != nil { + if err := self.sendStatus(); err != nil { return err } @@ -314,11 +308,9 @@ func (self *ethProtocol) handleStatus() error { if err != nil { return err } - if msg.Code != StatusMsg { return self.protoError(ErrNoStatusMsg, "first msg has code %x (!= %x)", msg.Code, StatusMsg) } - if msg.Size > ProtocolMaxMsgSize { return self.protoError(ErrMsgTooLarge, "%v > %v", msg.Size, ProtocolMaxMsgSize) } @@ -351,12 +343,12 @@ func (self *ethProtocol) handleStatus() error { func (self *ethProtocol) requestBlockHashes(from common.Hash) error { self.peer.Debugf("fetching hashes (%d) %x...\n", maxHashes, from[0:4]) - return p2p.EncodeMsg(self.rw, GetBlockHashesMsg, interface{}(from), uint64(maxHashes)) + return p2p.Send(self.rw, GetBlockHashesMsg, getBlockHashesMsgData{from, maxHashes}) } func (self *ethProtocol) requestBlocks(hashes []common.Hash) error { self.peer.Debugf("fetching %v blocks", len(hashes)) - return p2p.EncodeMsg(self.rw, GetBlocksMsg, rlp.Flat(hashes)) + return p2p.Send(self.rw, GetBlocksMsg, hashes) } func (self *ethProtocol) protoError(code int, format string, params ...interface{}) (err *errs.Error) { @@ -365,19 +357,20 @@ func (self *ethProtocol) protoError(code int, format string, params ...interface return } +func (self *ethProtocol) sendStatus() error { + td, currentBlock, genesisBlock := self.chainManager.Status() + return p2p.Send(self.rw, StatusMsg, &statusMsgData{ + ProtocolVersion: uint32(self.protocolVersion), + NetworkId: uint32(self.networkId), + TD: td, + CurrentBlock: currentBlock, + GenesisBlock: genesisBlock, + }) +} + func (self *ethProtocol) protoErrorDisconnect(err *errs.Error) { err.Log(self.peer.Logger) if err.Fatal() { self.peer.Disconnect(p2p.DiscSubprotocolError) } } - -func (self *ethProtocol) propagateTxs() { - transactions := self.txPool.GetTransactions() - iface := make([]interface{}, len(transactions)) - for i, transaction := range transactions { - iface[i] = transaction - } - - self.rw.WriteMsg(p2p.NewMsg(TxMsg, iface...)) -} diff --git a/eth/protocol_test.go b/eth/protocol_test.go index 108fb4475..7620b3854 100644 --- a/eth/protocol_test.go +++ b/eth/protocol_test.go @@ -1,8 +1,6 @@ package eth import ( - "bytes" - "io" "log" "math/big" "os" @@ -29,52 +27,21 @@ func logInit() { } } -type testMsgReadWriter struct { - in chan p2p.Msg - out []p2p.Msg -} - -func (self *testMsgReadWriter) In(msg p2p.Msg) { - self.in <- msg -} - -func (self *testMsgReadWriter) Out() (msg p2p.Msg, ok bool) { - if len(self.out) > 0 { - msg = self.out[0] - self.out = self.out[1:] - ok = true - } - return -} - -func (self *testMsgReadWriter) WriteMsg(msg p2p.Msg) error { - self.out = append(self.out, msg) - return nil -} - -func (self *testMsgReadWriter) ReadMsg() (p2p.Msg, error) { - msg, ok := <-self.in - if !ok { - return msg, io.EOF - } - return msg, nil -} - type testTxPool struct { getTransactions func() []*types.Transaction addTransactions func(txs []*types.Transaction) } type testChainManager struct { - getBlockHashes func(hash []byte, amount uint64) (hashes [][]byte) - getBlock func(hash []byte) *types.Block - status func() (td *big.Int, currentBlock []byte, genesisBlock []byte) + getBlockHashes func(hash common.Hash, amount uint64) (hashes []common.Hash) + getBlock func(hash common.Hash) *types.Block + status func() (td *big.Int, currentBlock common.Hash, genesisBlock common.Hash) } type testBlockPool struct { - addBlockHashes func(next func() ([]byte, bool), peerId string) + addBlockHashes func(next func() (common.Hash, bool), peerId string) addBlock func(block *types.Block, peerId string) (err error) - addPeer func(td *big.Int, currentBlock []byte, peerId string, requestHashes func([]byte) error, requestBlocks func([][]byte) error, peerError func(*errs.Error)) (best bool) + addPeer func(td *big.Int, currentBlock common.Hash, peerId string, requestHashes func(common.Hash) error, requestBlocks func([]common.Hash) error, peerError func(*errs.Error)) (best bool) removePeer func(peerId string) } @@ -93,28 +60,28 @@ func (self *testTxPool) AddTransactions(txs []*types.Transaction) { func (self *testTxPool) GetTransactions() types.Transactions { return nil } -func (self *testChainManager) GetBlockHashesFromHash(hash []byte, amount uint64) (hashes [][]byte) { +func (self *testChainManager) GetBlockHashesFromHash(hash common.Hash, amount uint64) (hashes []common.Hash) { if self.getBlockHashes != nil { hashes = self.getBlockHashes(hash, amount) } return } -func (self *testChainManager) Status() (td *big.Int, currentBlock []byte, genesisBlock []byte) { +func (self *testChainManager) Status() (td *big.Int, currentBlock common.Hash, genesisBlock common.Hash) { if self.status != nil { td, currentBlock, genesisBlock = self.status() } return } -func (self *testChainManager) GetBlock(hash []byte) (block *types.Block) { +func (self *testChainManager) GetBlock(hash common.Hash) (block *types.Block) { if self.getBlock != nil { block = self.getBlock(hash) } return } -func (self *testBlockPool) AddBlockHashes(next func() ([]byte, bool), peerId string) { +func (self *testBlockPool) AddBlockHashes(next func() (common.Hash, bool), peerId string) { if self.addBlockHashes != nil { self.addBlockHashes(next, peerId) } @@ -126,7 +93,7 @@ func (self *testBlockPool) AddBlock(block *types.Block, peerId string) { } } -func (self *testBlockPool) AddPeer(td *big.Int, currentBlock []byte, peerId string, requestBlockHashes func([]byte) error, requestBlocks func([][]byte) error, peerError func(*errs.Error)) (best bool) { +func (self *testBlockPool) AddPeer(td *big.Int, currentBlock common.Hash, peerId string, requestBlockHashes func(common.Hash) error, requestBlocks func([]common.Hash) error, peerError func(*errs.Error)) (best bool) { if self.addPeer != nil { best = self.addPeer(td, currentBlock, peerId, requestBlockHashes, requestBlocks, peerError) } @@ -147,28 +114,36 @@ func testPeer() *p2p.Peer { } type ethProtocolTester struct { + p2p.MsgReadWriter // writing to the tester feeds the protocol + quit chan error - rw *testMsgReadWriter // p2p.MsgReadWriter - txPool *testTxPool // txPool - chainManager *testChainManager // chainManager - blockPool *testBlockPool // blockPool + pipe *p2p.MsgPipeRW // the protocol read/writes on this end + txPool *testTxPool // txPool + chainManager *testChainManager // chainManager + blockPool *testBlockPool // blockPool t *testing.T } func newEth(t *testing.T) *ethProtocolTester { + p1, p2 := p2p.MsgPipe() return ðProtocolTester{ - quit: make(chan error), - rw: &testMsgReadWriter{in: make(chan p2p.Msg, 10)}, - txPool: &testTxPool{}, - chainManager: &testChainManager{}, - blockPool: &testBlockPool{}, - t: t, + MsgReadWriter: p1, + quit: make(chan error, 1), + pipe: p2, + txPool: &testTxPool{}, + chainManager: &testChainManager{}, + blockPool: &testBlockPool{}, + t: t, } } func (self *ethProtocolTester) reset() { - self.rw = &testMsgReadWriter{in: make(chan p2p.Msg, 10)} - self.quit = make(chan error) + self.pipe.Close() + + p1, p2 := p2p.MsgPipe() + self.MsgReadWriter = p1 + self.pipe = p2 + self.quit = make(chan error, 1) } func (self *ethProtocolTester) checkError(expCode int, delay time.Duration) (err error) { @@ -190,33 +165,8 @@ func (self *ethProtocolTester) checkError(expCode int, delay time.Duration) (err return } -func (self *ethProtocolTester) In(msg p2p.Msg) { - self.rw.In(msg) -} - -func (self *ethProtocolTester) Out() (p2p.Msg, bool) { - return self.rw.Out() -} - -func (self *ethProtocolTester) checkMsg(i int, code uint64, val interface{}) (msg p2p.Msg) { - if i >= len(self.rw.out) { - self.t.Errorf("expected at least %v msgs, got %v", i, len(self.rw.out)) - return - } - msg = self.rw.out[i] - if msg.Code != code { - self.t.Errorf("expected msg code %v, got %v", code, msg.Code) - } - if val != nil { - if err := msg.Decode(val); err != nil { - self.t.Errorf("rlp encoding error: %v", err) - } - } - return -} - func (self *ethProtocolTester) run() { - err := runEthProtocol(ProtocolVersion, NetworkId, self.txPool, self.chainManager, self.blockPool, testPeer(), self.rw) + err := runEthProtocol(ProtocolVersion, NetworkId, self.txPool, self.chainManager, self.blockPool, testPeer(), self.pipe) self.quit <- err } @@ -224,41 +174,52 @@ func TestStatusMsgErrors(t *testing.T) { logInit() eth := newEth(t) td := common.Big1 - currentBlock := []byte{1} - genesis := []byte{2} - eth.chainManager.status = func() (*big.Int, []byte, []byte) { return td, currentBlock, genesis } - go eth.run() - statusMsg := p2p.NewMsg(4) - eth.In(statusMsg) - delay := 1 * time.Second - eth.checkError(ErrNoStatusMsg, delay) - var status statusMsgData - eth.checkMsg(0, StatusMsg, &status) // first outgoing msg should be StatusMsg - if status.TD.Cmp(td) != 0 || - status.ProtocolVersion != ProtocolVersion || - status.NetworkId != NetworkId || - status.TD.Cmp(td) != 0 || - bytes.Compare(status.CurrentBlock, currentBlock) != 0 || - bytes.Compare(status.GenesisBlock, genesis) != 0 { - t.Errorf("incorrect outgoing status") - } - - eth.reset() + currentBlock := common.Hash{1} + genesis := common.Hash{2} + eth.chainManager.status = func() (*big.Int, common.Hash, common.Hash) { return td, currentBlock, genesis } go eth.run() - statusMsg = p2p.NewMsg(0, uint32(48), uint32(0), td, currentBlock, genesis) - eth.In(statusMsg) - eth.checkError(ErrProtocolVersionMismatch, delay) - eth.reset() - go eth.run() - statusMsg = p2p.NewMsg(0, uint32(49), uint32(1), td, currentBlock, genesis) - eth.In(statusMsg) - eth.checkError(ErrNetworkIdMismatch, delay) + tests := []struct { + code uint64 + data interface{} + wantErrorCode int + }{ + { + code: TxMsg, data: []interface{}{}, + wantErrorCode: ErrNoStatusMsg, + }, + { + code: StatusMsg, data: statusMsgData{10, NetworkId, td, currentBlock, genesis}, + wantErrorCode: ErrProtocolVersionMismatch, + }, + { + code: StatusMsg, data: statusMsgData{ProtocolVersion, 999, td, currentBlock, genesis}, + wantErrorCode: ErrNetworkIdMismatch, + }, + { + code: StatusMsg, data: statusMsgData{ProtocolVersion, NetworkId, td, currentBlock, common.Hash{3}}, + wantErrorCode: ErrGenesisBlockMismatch, + }, + } + for _, test := range tests { + // first outgoing msg should be StatusMsg. + err := p2p.ExpectMsg(eth, StatusMsg, &statusMsgData{ + ProtocolVersion: ProtocolVersion, + NetworkId: NetworkId, + TD: td, + CurrentBlock: currentBlock, + GenesisBlock: genesis, + }) + if err != nil { + t.Fatalf("incorrect outgoing status: %v", err) + } - eth.reset() - go eth.run() - statusMsg = p2p.NewMsg(0, uint32(49), uint32(0), td, currentBlock, []byte{3}) - eth.In(statusMsg) - eth.checkError(ErrGenesisBlockMismatch, delay) + // the send call might hang until reset because + // the protocol might not read the payload. + go p2p.Send(eth, test.code, test.data) + eth.checkError(test.wantErrorCode, 1*time.Second) + eth.reset() + go eth.run() + } } diff --git a/p2p/handshake.go b/p2p/handshake.go index 7fc497517..031064407 100644 --- a/p2p/handshake.go +++ b/p2p/handshake.go @@ -92,7 +92,7 @@ func setupInboundConn(fd net.Conn, prv *ecdsa.PrivateKey, our *protoHandshake) ( return nil, errors.New("node ID in protocol handshake does not match encryption handshake") } // TODO: validate that handshake node ID matches - if err := writeProtocolHandshake(rw, our); err != nil { + if err := Send(rw, handshakeMsg, our); err != nil { return nil, fmt.Errorf("protocol write error: %v", err) } return &conn{rw, rhs}, nil @@ -106,7 +106,7 @@ func setupOutboundConn(fd net.Conn, prv *ecdsa.PrivateKey, our *protoHandshake, // Run the protocol handshake using authenticated messages. rw := newRlpxFrameRW(fd, secrets) - if err := writeProtocolHandshake(rw, our); err != nil { + if err := Send(rw, handshakeMsg, our); err != nil { return nil, fmt.Errorf("protocol write error: %v", err) } rhs, err := readProtocolHandshake(rw, our) @@ -398,10 +398,6 @@ func xor(one, other []byte) (xor []byte) { return xor } -func writeProtocolHandshake(w MsgWriter, our *protoHandshake) error { - return EncodeMsg(w, handshakeMsg, our.Version, our.Name, our.Caps, our.ListenPort, our.ID[:]) -} - func readProtocolHandshake(r MsgReader, our *protoHandshake) (*protoHandshake, error) { // read and handle remote handshake msg, err := r.ReadMsg() diff --git a/p2p/message.go b/p2p/message.go index 14e4404c9..ef3630a90 100644 --- a/p2p/message.go +++ b/p2p/message.go @@ -11,7 +11,6 @@ import ( "sync/atomic" "time" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/rlp" ) @@ -28,13 +27,7 @@ type Msg struct { Payload io.Reader } -// NewMsg creates an RLP-encoded message with the given code. -func NewMsg(code uint64, params ...interface{}) Msg { - p := bytes.NewReader(common.Encode(params)) - return Msg{Code: code, Size: uint32(p.Len()), Payload: p} -} - -// Decode parse the RLP content of a message into +// Decode parses the RLP content of a message into // the given value, which must be a pointer. // // For the decoding rules, please see package rlp. @@ -76,13 +69,30 @@ type MsgReadWriter interface { MsgWriter } -// EncodeMsg writes an RLP-encoded message with the given code and -// data elements. -func EncodeMsg(w MsgWriter, code uint64, data ...interface{}) error { - return w.WriteMsg(NewMsg(code, data...)) +// Send writes an RLP-encoded message with the given code. +// data should encode as an RLP list. +func Send(w MsgWriter, msgcode uint64, data interface{}) error { + size, r, err := rlp.EncodeToReader(data) + if err != nil { + return err + } + return w.WriteMsg(Msg{Code: msgcode, Size: uint32(size), Payload: r}) +} + +// SendItems writes an RLP with the given code and data elements. +// For a call such as: +// +// SendItems(w, code, e1, e2, e3) +// +// the message payload will be an RLP list containing the items: +// +// [e1, e2, e3] +// +func SendItems(w MsgWriter, msgcode uint64, elems ...interface{}) error { + return Send(w, msgcode, elems) } -// netWrapper wrapsa MsgReadWriter with locks around +// netWrapper wraps a MsgReadWriter with locks around // ReadMsg/WriteMsg and applies read/write deadlines. type netWrapper struct { rmu, wmu sync.Mutex @@ -175,7 +185,10 @@ func (p *MsgPipeRW) WriteMsg(msg Msg) error { case p.w <- msg: if msg.Size > 0 { // wait for payload read or discard - <-consumed + select { + case <-consumed: + case <-p.closing: + } } return nil case <-p.closing: @@ -197,8 +210,8 @@ func (p *MsgPipeRW) ReadMsg() (Msg, error) { } // Close unblocks any pending ReadMsg and WriteMsg calls on both ends -// of the pipe. They will return ErrPipeClosed. Note that Close does -// not interrupt any reads from a message payload. +// of the pipe. They will return ErrPipeClosed. Close also +// interrupts any reads from a message payload. func (p *MsgPipeRW) Close() error { if atomic.AddInt32(p.closed, 1) != 1 { // someone else is already closing @@ -208,3 +221,35 @@ func (p *MsgPipeRW) Close() error { close(p.closing) return nil } + +// ExpectMsg reads a message from r and verifies that its +// code and encoded RLP content match the provided values. +// If content is nil, the payload is discarded and not verified. +func ExpectMsg(r MsgReader, code uint64, content interface{}) error { + msg, err := r.ReadMsg() + if err != nil { + return err + } + if msg.Code != code { + return fmt.Errorf("message code mismatch: got %d, expected %d", msg.Code, code) + } + if content == nil { + return msg.Discard() + } else { + contentEnc, err := rlp.EncodeToBytes(content) + if err != nil { + panic("content encode error: " + err.Error()) + } + if int(msg.Size) != len(contentEnc) { + return fmt.Errorf("message size mismatch: got %d, want %d", msg.Size, len(contentEnc)) + } + actualContent, err := ioutil.ReadAll(msg.Payload) + if err != nil { + return err + } + if !bytes.Equal(actualContent, contentEnc) { + return fmt.Errorf("message payload mismatch:\ngot: %x\nwant: %x", actualContent, contentEnc) + } + } + return nil +} diff --git a/p2p/message_test.go b/p2p/message_test.go index 31ed61d87..9a93dd347 100644 --- a/p2p/message_test.go +++ b/p2p/message_test.go @@ -5,33 +5,17 @@ import ( "encoding/hex" "fmt" "io" - "io/ioutil" "runtime" "strings" "testing" "time" ) -func TestNewMsg(t *testing.T) { - msg := NewMsg(3, 1, "000") - if msg.Code != 3 { - t.Errorf("incorrect code %d, want %d", msg.Code) - } - expect := unhex("c50183303030") - if msg.Size != uint32(len(expect)) { - t.Errorf("incorrect size %d, want %d", msg.Size, len(expect)) - } - pl, _ := ioutil.ReadAll(msg.Payload) - if !bytes.Equal(pl, expect) { - t.Errorf("incorrect payload content, got %x, want %x", pl, expect) - } -} - func ExampleMsgPipe() { rw1, rw2 := MsgPipe() go func() { - EncodeMsg(rw1, 8, []byte{0, 0}) - EncodeMsg(rw1, 5, []byte{1, 1}) + Send(rw1, 8, [][]byte{{0, 0}}) + Send(rw1, 5, [][]byte{{1, 1}}) rw1.Close() }() @@ -40,7 +24,7 @@ func ExampleMsgPipe() { if err != nil { break } - var data [1][]byte + var data [][]byte msg.Decode(&data) fmt.Printf("msg: %d, %x\n", msg.Code, data[0]) } @@ -55,7 +39,7 @@ loop: rw1, rw2 := MsgPipe() done := make(chan struct{}) go func() { - if err := EncodeMsg(rw1, 1); err == nil { + if err := SendItems(rw1, 1); err == nil { t.Error("EncodeMsg returned nil error") } else if err != ErrPipeClosed { t.Error("EncodeMsg returned wrong error: got %v, want %v", err, ErrPipeClosed) diff --git a/p2p/peer.go b/p2p/peer.go index c2c83abfc..6b97ea58d 100644 --- a/p2p/peer.go +++ b/p2p/peer.go @@ -132,7 +132,7 @@ loop: select { case <-ping.C: go func() { - if err := EncodeMsg(p.rw, pingMsg, nil); err != nil { + if err := SendItems(p.rw, pingMsg); err != nil { p.protoErr <- err return } @@ -161,7 +161,7 @@ loop: func (p *Peer) politeDisconnect(reason DiscReason) { done := make(chan struct{}) go func() { - EncodeMsg(p.rw, discMsg, uint(reason)) + SendItems(p.rw, discMsg, uint(reason)) // Wait for the other side to close the connection. // Discard any data that they send until then. io.Copy(ioutil.Discard, p.conn) @@ -192,12 +192,13 @@ func (p *Peer) handle(msg Msg) error { switch { case msg.Code == pingMsg: msg.Discard() - go EncodeMsg(p.rw, pongMsg) + go SendItems(p.rw, pongMsg) case msg.Code == discMsg: var reason [1]DiscReason // no need to discard or for error checking, we'll close the // connection after this. rlp.Decode(msg.Payload, &reason) + p.Debugf("Disconnect requested: %v\n", reason[0]) p.Disconnect(DiscRequested) return discRequestedError(reason[0]) case msg.Code < baseProtocolLength: diff --git a/p2p/peer_test.go b/p2p/peer_test.go index cc9f1f0cd..3c4c71c0c 100644 --- a/p2p/peer_test.go +++ b/p2p/peer_test.go @@ -4,13 +4,10 @@ import ( "bytes" "fmt" "io" - "io/ioutil" "net" "reflect" "testing" "time" - - "github.com/ethereum/go-ethereum/rlp" ) var discard = Protocol{ @@ -55,13 +52,13 @@ func TestPeerProtoReadMsg(t *testing.T) { Name: "a", Length: 5, Run: func(peer *Peer, rw MsgReadWriter) error { - if err := expectMsg(rw, 2, []uint{1}); err != nil { + if err := ExpectMsg(rw, 2, []uint{1}); err != nil { t.Error(err) } - if err := expectMsg(rw, 3, []uint{2}); err != nil { + if err := ExpectMsg(rw, 3, []uint{2}); err != nil { t.Error(err) } - if err := expectMsg(rw, 4, []uint{3}); err != nil { + if err := ExpectMsg(rw, 4, []uint{3}); err != nil { t.Error(err) } close(done) @@ -72,9 +69,9 @@ func TestPeerProtoReadMsg(t *testing.T) { closer, rw, _, errc := testPeer([]Protocol{proto}) defer closer.Close() - EncodeMsg(rw, baseProtocolLength+2, 1) - EncodeMsg(rw, baseProtocolLength+3, 2) - EncodeMsg(rw, baseProtocolLength+4, 3) + Send(rw, baseProtocolLength+2, []uint{1}) + Send(rw, baseProtocolLength+3, []uint{2}) + Send(rw, baseProtocolLength+4, []uint{3}) select { case <-done: @@ -92,10 +89,10 @@ func TestPeerProtoEncodeMsg(t *testing.T) { Name: "a", Length: 2, Run: func(peer *Peer, rw MsgReadWriter) error { - if err := EncodeMsg(rw, 2); err == nil { + if err := SendItems(rw, 2); err == nil { t.Error("expected error for out-of-range msg code, got nil") } - if err := EncodeMsg(rw, 1, "foo", "bar"); err != nil { + if err := SendItems(rw, 1, "foo", "bar"); err != nil { t.Errorf("write error: %v", err) } return nil @@ -104,7 +101,7 @@ func TestPeerProtoEncodeMsg(t *testing.T) { closer, rw, _, _ := testPeer([]Protocol{proto}) defer closer.Close() - if err := expectMsg(rw, 17, []string{"foo", "bar"}); err != nil { + if err := ExpectMsg(rw, 17, []string{"foo", "bar"}); err != nil { t.Error(err) } } @@ -115,11 +112,15 @@ func TestPeerWriteForBroadcast(t *testing.T) { closer, rw, peer, peerErr := testPeer([]Protocol{discard}) defer closer.Close() + emptymsg := func(code uint64) Msg { + return Msg{Code: code, Size: 0, Payload: bytes.NewReader(nil)} + } + // test write errors - if err := peer.writeProtoMsg("b", NewMsg(3)); err == nil { + if err := peer.writeProtoMsg("b", emptymsg(3)); err == nil { t.Errorf("expected error for unknown protocol, got nil") } - if err := peer.writeProtoMsg("discard", NewMsg(8)); err == nil { + if err := peer.writeProtoMsg("discard", emptymsg(8)); err == nil { t.Errorf("expected error for out-of-range msg code, got nil") } else if perr, ok := err.(*peerError); !ok || perr.Code != errInvalidMsgCode { t.Errorf("wrong error for out-of-range msg code, got %#v", err) @@ -128,14 +129,14 @@ func TestPeerWriteForBroadcast(t *testing.T) { // setup for reading the message on the other end read := make(chan struct{}) go func() { - if err := expectMsg(rw, 16, nil); err != nil { + if err := ExpectMsg(rw, 16, nil); err != nil { t.Error(err) } close(read) }() // test successful write - if err := peer.writeProtoMsg("discard", NewMsg(0)); err != nil { + if err := peer.writeProtoMsg("discard", emptymsg(0)); err != nil { t.Errorf("expect no error for known protocol: %v", err) } select { @@ -150,10 +151,10 @@ func TestPeerPing(t *testing.T) { closer, rw, _, _ := testPeer(nil) defer closer.Close() - if err := EncodeMsg(rw, pingMsg); err != nil { + if err := SendItems(rw, pingMsg); err != nil { t.Fatal(err) } - if err := expectMsg(rw, pongMsg, nil); err != nil { + if err := ExpectMsg(rw, pongMsg, nil); err != nil { t.Error(err) } } @@ -163,10 +164,10 @@ func TestPeerDisconnect(t *testing.T) { closer, rw, _, disc := testPeer(nil) defer closer.Close() - if err := EncodeMsg(rw, discMsg, DiscQuitting); err != nil { + if err := SendItems(rw, discMsg, DiscQuitting); err != nil { t.Fatal(err) } - if err := expectMsg(rw, discMsg, []interface{}{DiscRequested}); err != nil { + if err := ExpectMsg(rw, discMsg, []interface{}{DiscRequested}); err != nil { t.Error(err) } closer.Close() // make test end faster @@ -192,35 +193,3 @@ func TestNewPeer(t *testing.T) { p.Disconnect(DiscAlreadyConnected) // Should not hang } - -// expectMsg reads a message from r and verifies that its -// code and encoded RLP content match the provided values. -// If content is nil, the payload is discarded and not verified. -func expectMsg(r MsgReader, code uint64, content interface{}) error { - msg, err := r.ReadMsg() - if err != nil { - return err - } - if msg.Code != code { - return fmt.Errorf("message code mismatch: got %d, expected %d", msg.Code, code) - } - if content == nil { - return msg.Discard() - } else { - contentEnc, err := rlp.EncodeToBytes(content) - if err != nil { - panic("content encode error: " + err.Error()) - } - if int(msg.Size) != len(contentEnc) { - return fmt.Errorf("message size mismatch: got %d, want %d", msg.Size, len(contentEnc)) - } - actualContent, err := ioutil.ReadAll(msg.Payload) - if err != nil { - return err - } - if !bytes.Equal(actualContent, contentEnc) { - return fmt.Errorf("message payload mismatch:\ngot: %x\nwant: %x", actualContent, contentEnc) - } - } - return nil -} diff --git a/p2p/rlpx_test.go b/p2p/rlpx_test.go index 49354c7ed..d98f1c2cd 100644 --- a/p2p/rlpx_test.go +++ b/p2p/rlpx_test.go @@ -30,7 +30,7 @@ ba628a4ba590cb43f7848f41c4382885 `) // Check WriteMsg. This puts a message into the buffer. - if err := EncodeMsg(rw, 8, 1, 2, 3, 4); err != nil { + if err := Send(rw, 8, []uint{1, 2, 3, 4}); err != nil { t.Fatalf("WriteMsg error: %v", err) } written := buf.Bytes() @@ -102,7 +102,7 @@ func TestRlpxFrameRW(t *testing.T) { for i := 0; i < 10; i++ { // write message into conn buffer wmsg := []interface{}{"foo", "bar", strings.Repeat("test", i)} - err := EncodeMsg(rw1, uint64(i), wmsg...) + err := Send(rw1, uint64(i), wmsg) if err != nil { t.Fatalf("WriteMsg error (i=%d): %v", i, err) } diff --git a/p2p/server.go b/p2p/server.go index 9c148ba39..813b0676f 100644 --- a/p2p/server.go +++ b/p2p/server.go @@ -9,10 +9,10 @@ import ( "sync" "time" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/nat" + "github.com/ethereum/go-ethereum/rlp" ) const ( @@ -129,10 +129,14 @@ func (srv *Server) SuggestPeer(n *discover.Node) { // Broadcast sends an RLP-encoded message to all connected peers. // This method is deprecated and will be removed later. -func (srv *Server) Broadcast(protocol string, code uint64, data ...interface{}) { +func (srv *Server) Broadcast(protocol string, code uint64, data interface{}) error { var payload []byte if data != nil { - payload = common.Encode(data) + var err error + payload, err = rlp.EncodeToBytes(data) + if err != nil { + return err + } } srv.lock.RLock() defer srv.lock.RUnlock() @@ -146,6 +150,7 @@ func (srv *Server) Broadcast(protocol string, code uint64, data ...interface{}) peer.writeProtoMsg(protocol, msg) } } + return nil } // Start starts running the server. diff --git a/p2p/server_test.go b/p2p/server_test.go index 30447050c..14e7c7de2 100644 --- a/p2p/server_test.go +++ b/p2p/server_test.go @@ -149,7 +149,7 @@ func TestServerBroadcast(t *testing.T) { connected.Wait() // broadcast one message - srv.Broadcast("discard", 0, "foo") + srv.Broadcast("discard", 0, []string{"foo"}) golden := unhex("66e94d166f0a2c3b884cfa59ca34") // check that the message has been written everywhere diff --git a/whisper/envelope.go b/whisper/envelope.go index 577638046..9ec9fc318 100644 --- a/whisper/envelope.go +++ b/whisper/envelope.go @@ -6,9 +6,9 @@ import ( "fmt" "time" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto/ecies" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/rlp" ) diff --git a/whisper/peer.go b/whisper/peer.go index 4bd9428f5..ee5bffd0b 100644 --- a/whisper/peer.go +++ b/whisper/peer.go @@ -77,12 +77,11 @@ func (self *peer) broadcast(envelopes []*Envelope) error { } if i > 0 { - if err := p2p.EncodeMsg(self.ws, envelopesMsg, envs[:i]...); err != nil { + if err := p2p.Send(self.ws, envelopesMsg, envs[:i]); err != nil { return err } self.peer.DebugDetailln("broadcasted", i, "message(s)") } - return nil } @@ -92,7 +91,7 @@ func (self *peer) addKnown(envelope *Envelope) { func (self *peer) handleStatus() error { ws := self.ws - if err := ws.WriteMsg(self.statusMsg()); err != nil { + if err := p2p.SendItems(ws, statusMsg, protocolVersion); err != nil { return err } msg, err := ws.ReadMsg() @@ -115,7 +114,3 @@ func (self *peer) handleStatus() error { } return msg.Discard() // ignore anything after protocol version } - -func (self *peer) statusMsg() p2p.Msg { - return p2p.NewMsg(statusMsg, protocolVersion) -} |