github.com/aigarnetwork/aigar@v0.0.0-20191115204914-d59a6eb70f8e/p2p/testing/protocoltester.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // Copyright 2019 The go-aigar Authors 3 // This file is part of the go-aigar library. 4 // 5 // The go-aigar library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The go-aigar library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the go-aigar library. If not, see <http://www.gnu.org/licenses/>. 17 18 /* 19 the p2p/testing package provides a unit test scheme to check simple 20 protocol message exchanges with one pivot node and a number of dummy peers 21 The pivot test node runs a node.Service, the dummy peers run a mock node 22 that can be used to send and receive messages 23 */ 24 25 package testing 26 27 import ( 28 "bytes" 29 "crypto/ecdsa" 30 "fmt" 31 "io" 32 "io/ioutil" 33 "strings" 34 "sync" 35 36 "github.com/AigarNetwork/aigar/log" 37 "github.com/AigarNetwork/aigar/node" 38 "github.com/AigarNetwork/aigar/p2p" 39 "github.com/AigarNetwork/aigar/p2p/enode" 40 "github.com/AigarNetwork/aigar/p2p/simulations" 41 "github.com/AigarNetwork/aigar/p2p/simulations/adapters" 42 "github.com/AigarNetwork/aigar/rlp" 43 "github.com/AigarNetwork/aigar/rpc" 44 ) 45 46 // ProtocolTester is the tester environment used for unit testing protocol 47 // message exchanges. It uses p2p/simulations framework 48 type ProtocolTester struct { 49 *ProtocolSession 50 network *simulations.Network 51 } 52 53 // NewProtocolTester constructs a new ProtocolTester 54 // it takes as argument the pivot node id, the number of dummy peers and the 55 // protocol run function called on a peer connection by the p2p server 56 func NewProtocolTester(prvkey *ecdsa.PrivateKey, nodeCount int, run func(*p2p.Peer, p2p.MsgReadWriter) error) *ProtocolTester { 57 services := adapters.Services{ 58 "test": func(ctx *adapters.ServiceContext) (node.Service, error) { 59 return &testNode{run}, nil 60 }, 61 "mock": func(ctx *adapters.ServiceContext) (node.Service, error) { 62 return newMockNode(), nil 63 }, 64 } 65 adapter := adapters.NewSimAdapter(services) 66 net := simulations.NewNetwork(adapter, &simulations.NetworkConfig{}) 67 nodeConfig := &adapters.NodeConfig{ 68 PrivateKey: prvkey, 69 EnableMsgEvents: true, 70 Services: []string{"test"}, 71 } 72 if _, err := net.NewNodeWithConfig(nodeConfig); err != nil { 73 panic(err.Error()) 74 } 75 if err := net.Start(nodeConfig.ID); err != nil { 76 panic(err.Error()) 77 } 78 79 node := net.GetNode(nodeConfig.ID).Node.(*adapters.SimNode) 80 peers := make([]*adapters.NodeConfig, nodeCount) 81 nodes := make([]*enode.Node, nodeCount) 82 for i := 0; i < nodeCount; i++ { 83 peers[i] = adapters.RandomNodeConfig() 84 peers[i].Services = []string{"mock"} 85 if _, err := net.NewNodeWithConfig(peers[i]); err != nil { 86 panic(fmt.Sprintf("error initializing peer %v: %v", peers[i].ID, err)) 87 } 88 if err := net.Start(peers[i].ID); err != nil { 89 panic(fmt.Sprintf("error starting peer %v: %v", peers[i].ID, err)) 90 } 91 nodes[i] = peers[i].Node() 92 } 93 events := make(chan *p2p.PeerEvent, 1000) 94 node.SubscribeEvents(events) 95 ps := &ProtocolSession{ 96 Server: node.Server(), 97 Nodes: nodes, 98 adapter: adapter, 99 events: events, 100 } 101 self := &ProtocolTester{ 102 ProtocolSession: ps, 103 network: net, 104 } 105 106 self.Connect(nodeConfig.ID, peers...) 107 108 return self 109 } 110 111 // Stop stops the p2p server 112 func (t *ProtocolTester) Stop() { 113 t.Server.Stop() 114 t.network.Shutdown() 115 } 116 117 // Connect brings up the remote peer node and connects it using the 118 // p2p/simulations network connection with the in memory network adapter 119 func (t *ProtocolTester) Connect(selfID enode.ID, peers ...*adapters.NodeConfig) { 120 for _, peer := range peers { 121 log.Trace(fmt.Sprintf("connect to %v", peer.ID)) 122 if err := t.network.Connect(selfID, peer.ID); err != nil { 123 panic(fmt.Sprintf("error connecting to peer %v: %v", peer.ID, err)) 124 } 125 } 126 127 } 128 129 // testNode wraps a protocol run function and implements the node.Service 130 // interface 131 type testNode struct { 132 run func(*p2p.Peer, p2p.MsgReadWriter) error 133 } 134 135 func (t *testNode) Protocols() []p2p.Protocol { 136 return []p2p.Protocol{{ 137 Length: 100, 138 Run: t.run, 139 }} 140 } 141 142 func (t *testNode) APIs() []rpc.API { 143 return nil 144 } 145 146 func (t *testNode) Start(server *p2p.Server) error { 147 return nil 148 } 149 150 func (t *testNode) Stop() error { 151 return nil 152 } 153 154 // mockNode is a testNode which doesn't actually run a protocol, instead 155 // exposing channels so that tests can manually trigger and expect certain 156 // messages 157 type mockNode struct { 158 testNode 159 160 trigger chan *Trigger 161 expect chan []Expect 162 err chan error 163 stop chan struct{} 164 stopOnce sync.Once 165 } 166 167 func newMockNode() *mockNode { 168 mock := &mockNode{ 169 trigger: make(chan *Trigger), 170 expect: make(chan []Expect), 171 err: make(chan error), 172 stop: make(chan struct{}), 173 } 174 mock.testNode.run = mock.Run 175 return mock 176 } 177 178 // Run is a protocol run function which just loops waiting for tests to 179 // instruct it to either trigger or expect a message from the peer 180 func (m *mockNode) Run(peer *p2p.Peer, rw p2p.MsgReadWriter) error { 181 for { 182 select { 183 case trig := <-m.trigger: 184 wmsg := Wrap(trig.Msg) 185 m.err <- p2p.Send(rw, trig.Code, wmsg) 186 case exps := <-m.expect: 187 m.err <- expectMsgs(rw, exps) 188 case <-m.stop: 189 return nil 190 } 191 } 192 } 193 194 func (m *mockNode) Trigger(trig *Trigger) error { 195 m.trigger <- trig 196 return <-m.err 197 } 198 199 func (m *mockNode) Expect(exp ...Expect) error { 200 m.expect <- exp 201 return <-m.err 202 } 203 204 func (m *mockNode) Stop() error { 205 m.stopOnce.Do(func() { close(m.stop) }) 206 return nil 207 } 208 209 func expectMsgs(rw p2p.MsgReadWriter, exps []Expect) error { 210 matched := make([]bool, len(exps)) 211 for { 212 msg, err := rw.ReadMsg() 213 if err != nil { 214 if err == io.EOF { 215 break 216 } 217 return err 218 } 219 actualContent, err := ioutil.ReadAll(msg.Payload) 220 if err != nil { 221 return err 222 } 223 var found bool 224 for i, exp := range exps { 225 if exp.Code == msg.Code && bytes.Equal(actualContent, mustEncodeMsg(Wrap(exp.Msg))) { 226 if matched[i] { 227 return fmt.Errorf("message #%d received two times", i) 228 } 229 matched[i] = true 230 found = true 231 break 232 } 233 } 234 if !found { 235 expected := make([]string, 0) 236 for i, exp := range exps { 237 if matched[i] { 238 continue 239 } 240 expected = append(expected, fmt.Sprintf("code %d payload %x", exp.Code, mustEncodeMsg(Wrap(exp.Msg)))) 241 } 242 return fmt.Errorf("unexpected message code %d payload %x, expected %s", msg.Code, actualContent, strings.Join(expected, " or ")) 243 } 244 done := true 245 for _, m := range matched { 246 if !m { 247 done = false 248 break 249 } 250 } 251 if done { 252 return nil 253 } 254 } 255 for i, m := range matched { 256 if !m { 257 return fmt.Errorf("expected message #%d not received", i) 258 } 259 } 260 return nil 261 } 262 263 // mustEncodeMsg uses rlp to encode a message. 264 // In case of error it panics. 265 func mustEncodeMsg(msg interface{}) []byte { 266 contentEnc, err := rlp.EncodeToBytes(msg) 267 if err != nil { 268 panic("content encode error: " + err.Error()) 269 } 270 return contentEnc 271 } 272 273 type WrappedMsg struct { 274 Context []byte 275 Size uint32 276 Payload []byte 277 } 278 279 func Wrap(msg interface{}) interface{} { 280 data, _ := rlp.EncodeToBytes(msg) 281 return &WrappedMsg{ 282 Size: uint32(len(data)), 283 Payload: data, 284 } 285 }