github.com/Blockdaemon/celo-blockchain@v0.0.0-20200129231733-e667f6b08419/p2p/protocols/accounting_simulation_test.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum 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 go-ethereum 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 go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package protocols 18 19 import ( 20 "context" 21 "flag" 22 "fmt" 23 "io/ioutil" 24 "math/rand" 25 "os" 26 "path/filepath" 27 "reflect" 28 "sync" 29 "testing" 30 "time" 31 32 "github.com/ethereum/go-ethereum/log" 33 "github.com/ethereum/go-ethereum/node" 34 "github.com/ethereum/go-ethereum/p2p" 35 "github.com/ethereum/go-ethereum/p2p/enode" 36 "github.com/ethereum/go-ethereum/p2p/simulations" 37 "github.com/ethereum/go-ethereum/p2p/simulations/adapters" 38 "github.com/ethereum/go-ethereum/rpc" 39 "github.com/mattn/go-colorable" 40 ) 41 42 const ( 43 content = "123456789" 44 ) 45 46 var ( 47 nodes = flag.Int("nodes", 30, "number of nodes to create (default 30)") 48 msgs = flag.Int("msgs", 100, "number of messages sent by node (default 100)") 49 loglevel = flag.Int("loglevel", 0, "verbosity of logs") 50 rawlog = flag.Bool("rawlog", false, "remove terminal formatting from logs") 51 ) 52 53 func init() { 54 flag.Parse() 55 log.PrintOrigins(true) 56 log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*loglevel), log.StreamHandler(colorable.NewColorableStderr(), log.TerminalFormat(!*rawlog)))) 57 } 58 59 //TestAccountingSimulation runs a p2p/simulations simulation 60 //It creates a *nodes number of nodes, connects each one with each other, 61 //then sends out a random selection of messages up to *msgs amount of messages 62 //from the test protocol spec. 63 //The spec has some accounted messages defined through the Prices interface. 64 //The test does accounting for all the message exchanged, and then checks 65 //that every node has the same balance with a peer, but with opposite signs. 66 //Balance(AwithB) = 0 - Balance(BwithA) or Abs|Balance(AwithB)| == Abs|Balance(BwithA)| 67 func TestAccountingSimulation(t *testing.T) { 68 //setup the balances objects for every node 69 bal := newBalances(*nodes) 70 //setup the metrics system or tests will fail trying to write metrics 71 dir, err := ioutil.TempDir("", "account-sim") 72 if err != nil { 73 t.Fatal(err) 74 } 75 defer os.RemoveAll(dir) 76 SetupAccountingMetrics(1*time.Second, filepath.Join(dir, "metrics.db")) 77 //define the node.Service for this test 78 services := adapters.Services{ 79 "accounting": func(ctx *adapters.ServiceContext) (node.Service, error) { 80 return bal.newNode(), nil 81 }, 82 } 83 //setup the simulation 84 adapter := adapters.NewSimAdapter(services) 85 net := simulations.NewNetwork(adapter, &simulations.NetworkConfig{DefaultService: "accounting"}) 86 defer net.Shutdown() 87 88 // we send msgs messages per node, wait for all messages to arrive 89 bal.wg.Add(*nodes * *msgs) 90 trigger := make(chan enode.ID) 91 go func() { 92 // wait for all of them to arrive 93 bal.wg.Wait() 94 // then trigger a check 95 // the selected node for the trigger is irrelevant, 96 // we just want to trigger the end of the simulation 97 trigger <- net.Nodes[0].ID() 98 }() 99 100 // create nodes and start them 101 for i := 0; i < *nodes; i++ { 102 conf := adapters.RandomNodeConfig() 103 bal.id2n[conf.ID] = i 104 if _, err := net.NewNodeWithConfig(conf); err != nil { 105 t.Fatal(err) 106 } 107 if err := net.Start(conf.ID); err != nil { 108 t.Fatal(err) 109 } 110 } 111 // fully connect nodes 112 for i, n := range net.Nodes { 113 for _, m := range net.Nodes[i+1:] { 114 if err := net.Connect(n.ID(), m.ID()); err != nil { 115 t.Fatal(err) 116 } 117 } 118 } 119 120 // empty action 121 action := func(ctx context.Context) error { 122 return nil 123 } 124 // check always checks out 125 check := func(ctx context.Context, id enode.ID) (bool, error) { 126 return true, nil 127 } 128 129 // run simulation 130 timeout := 30 * time.Second 131 ctx, cancel := context.WithTimeout(context.Background(), timeout) 132 defer cancel() 133 result := simulations.NewSimulation(net).Run(ctx, &simulations.Step{ 134 Action: action, 135 Trigger: trigger, 136 Expect: &simulations.Expectation{ 137 Nodes: []enode.ID{net.Nodes[0].ID()}, 138 Check: check, 139 }, 140 }) 141 142 if result.Error != nil { 143 t.Fatal(result.Error) 144 } 145 146 // check if balance matrix is symmetric 147 if err := bal.symmetric(); err != nil { 148 t.Fatal(err) 149 } 150 } 151 152 // matrix is a matrix of nodes and its balances 153 // matrix is in fact a linear array of size n*n, 154 // so the balance for any node A with B is at index 155 // A*n + B, while the balance of node B with A is at 156 // B*n + A 157 // (n entries in the array will not be filled - 158 // the balance of a node with itself) 159 type matrix struct { 160 n int //number of nodes 161 m []int64 //array of balances 162 } 163 164 // create a new matrix 165 func newMatrix(n int) *matrix { 166 return &matrix{ 167 n: n, 168 m: make([]int64, n*n), 169 } 170 } 171 172 // called from the testBalance's Add accounting function: register balance change 173 func (m *matrix) add(i, j int, v int64) error { 174 // index for the balance of local node i with remote nodde j is 175 // i * number of nodes + remote node 176 mi := i*m.n + j 177 // register that balance 178 m.m[mi] += v 179 return nil 180 } 181 182 // check that the balances are symmetric: 183 // balance of node i with node j is the same as j with i but with inverted signs 184 func (m *matrix) symmetric() error { 185 //iterate all nodes 186 for i := 0; i < m.n; i++ { 187 //iterate starting +1 188 for j := i + 1; j < m.n; j++ { 189 log.Debug("bal", "1", i, "2", j, "i,j", m.m[i*m.n+j], "j,i", m.m[j*m.n+i]) 190 if m.m[i*m.n+j] != -m.m[j*m.n+i] { 191 return fmt.Errorf("value mismatch. m[%v, %v] = %v; m[%v, %v] = %v", i, j, m.m[i*m.n+j], j, i, m.m[j*m.n+i]) 192 } 193 } 194 } 195 return nil 196 } 197 198 // all the balances 199 type balances struct { 200 i int 201 *matrix 202 id2n map[enode.ID]int 203 wg *sync.WaitGroup 204 } 205 206 func newBalances(n int) *balances { 207 return &balances{ 208 matrix: newMatrix(n), 209 id2n: make(map[enode.ID]int), 210 wg: &sync.WaitGroup{}, 211 } 212 } 213 214 // create a new testNode for every node created as part of the service 215 func (b *balances) newNode() *testNode { 216 defer func() { b.i++ }() 217 return &testNode{ 218 bal: b, 219 i: b.i, 220 peers: make([]*testPeer, b.n), //a node will be connected to n-1 peers 221 } 222 } 223 224 type testNode struct { 225 bal *balances 226 i int 227 lock sync.Mutex 228 peers []*testPeer 229 peerCount int 230 } 231 232 // do the accounting for the peer's test protocol 233 // testNode implements protocols.Balance 234 func (t *testNode) Add(a int64, p *Peer) error { 235 //get the index for the remote peer 236 remote := t.bal.id2n[p.ID()] 237 log.Debug("add", "local", t.i, "remote", remote, "amount", a) 238 return t.bal.add(t.i, remote, a) 239 } 240 241 //run the p2p protocol 242 //for every node, represented by testNode, create a remote testPeer 243 func (t *testNode) run(p *p2p.Peer, rw p2p.MsgReadWriter) error { 244 spec := createTestSpec() 245 //create accounting hook 246 spec.Hook = NewAccounting(t, &dummyPrices{}) 247 248 //create a peer for this node 249 tp := &testPeer{NewPeer(p, rw, spec), t.i, t.bal.id2n[p.ID()], t.bal.wg} 250 t.lock.Lock() 251 t.peers[t.bal.id2n[p.ID()]] = tp 252 t.peerCount++ 253 if t.peerCount == t.bal.n-1 { 254 //when all peer connections are established, start sending messages from this peer 255 go t.send() 256 } 257 t.lock.Unlock() 258 return tp.Run(tp.handle) 259 } 260 261 // p2p message receive handler function 262 func (tp *testPeer) handle(ctx context.Context, msg interface{}) error { 263 tp.wg.Done() 264 log.Debug("receive", "from", tp.remote, "to", tp.local, "type", reflect.TypeOf(msg), "msg", msg) 265 return nil 266 } 267 268 type testPeer struct { 269 *Peer 270 local, remote int 271 wg *sync.WaitGroup 272 } 273 274 func (t *testNode) send() { 275 log.Debug("start sending") 276 for i := 0; i < *msgs; i++ { 277 //determine randomly to which peer to send 278 whom := rand.Intn(t.bal.n - 1) 279 if whom >= t.i { 280 whom++ 281 } 282 t.lock.Lock() 283 p := t.peers[whom] 284 t.lock.Unlock() 285 286 //determine a random message from the spec's messages to be sent 287 which := rand.Intn(len(p.spec.Messages)) 288 msg := p.spec.Messages[which] 289 switch msg.(type) { 290 case *perBytesMsgReceiverPays: 291 msg = &perBytesMsgReceiverPays{Content: content[:rand.Intn(len(content))]} 292 case *perBytesMsgSenderPays: 293 msg = &perBytesMsgSenderPays{Content: content[:rand.Intn(len(content))]} 294 } 295 log.Debug("send", "from", t.i, "to", whom, "type", reflect.TypeOf(msg), "msg", msg) 296 p.Send(context.TODO(), msg) 297 } 298 } 299 300 // define the protocol 301 func (t *testNode) Protocols() []p2p.Protocol { 302 return []p2p.Protocol{{ 303 Length: 100, 304 Run: t.run, 305 }} 306 } 307 308 func (t *testNode) APIs() []rpc.API { 309 return nil 310 } 311 312 func (t *testNode) Start(server *p2p.Server) error { 313 return nil 314 } 315 316 func (t *testNode) Stop() error { 317 return nil 318 }