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