github.com/lukso-network/go-ethereum@v1.8.22/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 } 165 166 // create a new matrix 167 func newMatrix(n int) *matrix { 168 return &matrix{ 169 n: n, 170 m: make([]int64, n*n), 171 } 172 } 173 174 // called from the testBalance's Add accounting function: register balance change 175 func (m *matrix) add(i, j int, v int64) error { 176 // index for the balance of local node i with remote nodde j is 177 // i * number of nodes + remote node 178 mi := i*m.n + j 179 // register that balance 180 m.m[mi] += v 181 return nil 182 } 183 184 // check that the balances are symmetric: 185 // balance of node i with node j is the same as j with i but with inverted signs 186 func (m *matrix) symmetric() error { 187 //iterate all nodes 188 for i := 0; i < m.n; i++ { 189 //iterate starting +1 190 for j := i + 1; j < m.n; j++ { 191 log.Debug("bal", "1", i, "2", j, "i,j", m.m[i*m.n+j], "j,i", m.m[j*m.n+i]) 192 if m.m[i*m.n+j] != -m.m[j*m.n+i] { 193 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]) 194 } 195 } 196 } 197 return nil 198 } 199 200 // all the balances 201 type balances struct { 202 i int 203 *matrix 204 id2n map[enode.ID]int 205 wg *sync.WaitGroup 206 } 207 208 func newBalances(n int) *balances { 209 return &balances{ 210 matrix: newMatrix(n), 211 id2n: make(map[enode.ID]int), 212 wg: &sync.WaitGroup{}, 213 } 214 } 215 216 // create a new testNode for every node created as part of the service 217 func (b *balances) newNode() *testNode { 218 defer func() { b.i++ }() 219 return &testNode{ 220 bal: b, 221 i: b.i, 222 peers: make([]*testPeer, b.n), //a node will be connected to n-1 peers 223 } 224 } 225 226 type testNode struct { 227 bal *balances 228 i int 229 lock sync.Mutex 230 peers []*testPeer 231 peerCount int 232 } 233 234 // do the accounting for the peer's test protocol 235 // testNode implements protocols.Balance 236 func (t *testNode) Add(a int64, p *Peer) error { 237 //get the index for the remote peer 238 remote := t.bal.id2n[p.ID()] 239 log.Debug("add", "local", t.i, "remote", remote, "amount", a) 240 return t.bal.add(t.i, remote, a) 241 } 242 243 //run the p2p protocol 244 //for every node, represented by testNode, create a remote testPeer 245 func (t *testNode) run(p *p2p.Peer, rw p2p.MsgReadWriter) error { 246 spec := createTestSpec() 247 //create accounting hook 248 spec.Hook = NewAccounting(t, &dummyPrices{}) 249 250 //create a peer for this node 251 tp := &testPeer{NewPeer(p, rw, spec), t.i, t.bal.id2n[p.ID()], t.bal.wg} 252 t.lock.Lock() 253 t.peers[t.bal.id2n[p.ID()]] = tp 254 t.peerCount++ 255 if t.peerCount == t.bal.n-1 { 256 //when all peer connections are established, start sending messages from this peer 257 go t.send() 258 } 259 t.lock.Unlock() 260 return tp.Run(tp.handle) 261 } 262 263 // p2p message receive handler function 264 func (tp *testPeer) handle(ctx context.Context, msg interface{}) error { 265 tp.wg.Done() 266 log.Debug("receive", "from", tp.remote, "to", tp.local, "type", reflect.TypeOf(msg), "msg", msg) 267 return nil 268 } 269 270 type testPeer struct { 271 *Peer 272 local, remote int 273 wg *sync.WaitGroup 274 } 275 276 func (t *testNode) send() { 277 log.Debug("start sending") 278 for i := 0; i < *msgs; i++ { 279 //determine randomly to which peer to send 280 whom := rand.Intn(t.bal.n - 1) 281 if whom >= t.i { 282 whom++ 283 } 284 t.lock.Lock() 285 p := t.peers[whom] 286 t.lock.Unlock() 287 288 //determine a random message from the spec's messages to be sent 289 which := rand.Intn(len(p.spec.Messages)) 290 msg := p.spec.Messages[which] 291 switch msg.(type) { 292 case *perBytesMsgReceiverPays: 293 msg = &perBytesMsgReceiverPays{Content: content[:rand.Intn(len(content))]} 294 case *perBytesMsgSenderPays: 295 msg = &perBytesMsgSenderPays{Content: content[:rand.Intn(len(content))]} 296 } 297 log.Debug("send", "from", t.i, "to", whom, "type", reflect.TypeOf(msg), "msg", msg) 298 p.Send(context.TODO(), msg) 299 } 300 } 301 302 // define the protocol 303 func (t *testNode) Protocols() []p2p.Protocol { 304 return []p2p.Protocol{{ 305 Length: 100, 306 Run: t.run, 307 }} 308 } 309 310 func (t *testNode) APIs() []rpc.API { 311 return nil 312 } 313 314 func (t *testNode) Start(server *p2p.Server) error { 315 return nil 316 } 317 318 func (t *testNode) Stop() error { 319 return nil 320 }