github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/p2p/protocols/accounting_simulation_test.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 19:16:41</date> 10 //</624450105843388416> 11 12 13 package protocols 14 15 import ( 16 "context" 17 "flag" 18 "fmt" 19 "io/ioutil" 20 "math/rand" 21 "os" 22 "path/filepath" 23 "reflect" 24 "sync" 25 "testing" 26 "time" 27 28 "github.com/mattn/go-colorable" 29 30 "github.com/ethereum/go-ethereum/log" 31 "github.com/ethereum/go-ethereum/rpc" 32 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 ) 39 40 const ( 41 content = "123456789" 42 ) 43 44 var ( 45 nodes = flag.Int("nodes", 30, "number of nodes to create (default 30)") 46 msgs = flag.Int("msgs", 100, "number of messages sent by node (default 100)") 47 loglevel = flag.Int("loglevel", 0, "verbosity of logs") 48 rawlog = flag.Bool("rawlog", false, "remove terminal formatting from logs") 49 ) 50 51 func init() { 52 flag.Parse() 53 log.PrintOrigins(true) 54 log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*loglevel), log.StreamHandler(colorable.NewColorableStderr(), log.TerminalFormat(!*rawlog)))) 55 } 56 57 //测试计算模拟运行p2p/模拟仿真 58 //它创建一个*节点数的节点,彼此连接, 59 //然后发送随机选择的消息,最多可发送*msgs数量的消息 60 //来自测试协议规范。 61 //规范通过价格接口定义了一些记帐消息。 62 //测试会计算所有交换的消息,然后检查 63 //每个节点与对等节点具有相同的平衡,但符号相反。 64 //平衡(awithb)=0-平衡(bwitha)或abs平衡(awithb)==abs平衡(bwitha) 65 func TestAccountingSimulation(t *testing.T) { 66 //为每个节点设置Balances对象 67 bal := newBalances(*nodes) 68 //设置度量系统或测试在尝试写入度量时将失败 69 dir, err := ioutil.TempDir("", "account-sim") 70 if err != nil { 71 t.Fatal(err) 72 } 73 defer os.RemoveAll(dir) 74 SetupAccountingMetrics(1*time.Second, filepath.Join(dir, "metrics.db")) 75 //定义此测试的node.service 76 services := adapters.Services{ 77 "accounting": func(ctx *adapters.ServiceContext) (node.Service, error) { 78 return bal.newNode(), nil 79 }, 80 } 81 //设置模拟 82 adapter := adapters.NewSimAdapter(services) 83 net := simulations.NewNetwork(adapter, &simulations.NetworkConfig{DefaultService: "accounting"}) 84 defer net.Shutdown() 85 86 //我们每个节点发送MSGS消息,等待所有消息到达 87 bal.wg.Add(*nodes * *msgs) 88 trigger := make(chan enode.ID) 89 go func() { 90 //等他们都到了 91 bal.wg.Wait() 92 //然后触发检查 93 //触发器的选定节点不相关, 94 //我们只想触发模拟的结束 95 trigger <- net.Nodes[0].ID() 96 }() 97 98 //创建节点并启动它们 99 for i := 0; i < *nodes; i++ { 100 conf := adapters.RandomNodeConfig() 101 bal.id2n[conf.ID] = i 102 if _, err := net.NewNodeWithConfig(conf); err != nil { 103 t.Fatal(err) 104 } 105 if err := net.Start(conf.ID); err != nil { 106 t.Fatal(err) 107 } 108 } 109 //完全连接节点 110 for i, n := range net.Nodes { 111 for _, m := range net.Nodes[i+1:] { 112 if err := net.Connect(n.ID(), m.ID()); err != nil { 113 t.Fatal(err) 114 } 115 } 116 } 117 118 //空动作 119 action := func(ctx context.Context) error { 120 return nil 121 } 122 //检查始终签出 123 check := func(ctx context.Context, id enode.ID) (bool, error) { 124 return true, nil 125 } 126 127 //运行仿真 128 timeout := 30 * time.Second 129 ctx, cancel := context.WithTimeout(context.Background(), timeout) 130 defer cancel() 131 result := simulations.NewSimulation(net).Run(ctx, &simulations.Step{ 132 Action: action, 133 Trigger: trigger, 134 Expect: &simulations.Expectation{ 135 Nodes: []enode.ID{net.Nodes[0].ID()}, 136 Check: check, 137 }, 138 }) 139 140 if result.Error != nil { 141 t.Fatal(result.Error) 142 } 143 144 //检查平衡矩阵是否对称 145 if err := bal.symmetric(); err != nil { 146 t.Fatal(err) 147 } 148 } 149 150 //矩阵是节点及其平衡的矩阵 151 //矩阵实际上是一个大小为n*n的线性数组, 152 //所以任何节点a和b的余额都在索引处。 153 //a*n+b,b节点与a的平衡为 154 //B*N+A 155 //(数组中的n个条目将不会填充- 156 //节点本身的平衡) 157 type matrix struct { 158 n int //节点数 159 m []int64 //余额数组 160 } 161 162 //创建新矩阵 163 func newMatrix(n int) *matrix { 164 return &matrix{ 165 n: n, 166 m: make([]int64, n*n), 167 } 168 } 169 170 //从testBalance的add accounting函数调用:注册余额更改 171 func (m *matrix) add(i, j int, v int64) error { 172 //本地节点i与远程节点j的平衡索引为 173 //i*节点数+远程节点 174 mi := i*m.n + j 175 //登记余额 176 m.m[mi] += v 177 return nil 178 } 179 180 //检查天平是否对称: 181 //i节点与j节点的平衡与j节点与i节点的平衡相同,但有倒置的符号。 182 func (m *matrix) symmetric() error { 183 //迭代所有节点 184 for i := 0; i < m.n; i++ { 185 //迭代开始+1 186 for j := i + 1; j < m.n; j++ { 187 log.Debug("bal", "1", i, "2", j, "i,j", m.m[i*m.n+j], "j,i", m.m[j*m.n+i]) 188 if m.m[i*m.n+j] != -m.m[j*m.n+i] { 189 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]) 190 } 191 } 192 } 193 return nil 194 } 195 196 //所有余额 197 type balances struct { 198 i int 199 *matrix 200 id2n map[enode.ID]int 201 wg *sync.WaitGroup 202 } 203 204 func newBalances(n int) *balances { 205 return &balances{ 206 matrix: newMatrix(n), 207 id2n: make(map[enode.ID]int), 208 wg: &sync.WaitGroup{}, 209 } 210 } 211 212 //为作为服务一部分创建的每个节点创建一个新的测试节点 213 func (b *balances) newNode() *testNode { 214 defer func() { b.i++ }() 215 return &testNode{ 216 bal: b, 217 i: b.i, 218 peers: make([]*testPeer, b.n), //节点将连接到n-1对等机 219 } 220 } 221 222 type testNode struct { 223 bal *balances 224 i int 225 lock sync.Mutex 226 peers []*testPeer 227 peerCount int 228 } 229 230 //计算对等方的测试协议 231 //testnode实现协议。平衡 232 func (t *testNode) Add(a int64, p *Peer) error { 233 //获取远程对等机的索引 234 remote := t.bal.id2n[p.ID()] 235 log.Debug("add", "local", t.i, "remote", remote, "amount", a) 236 return t.bal.add(t.i, remote, a) 237 } 238 239 //运行p2p协议 240 //对于由testnode表示的每个节点,创建一个远程testpeer 241 func (t *testNode) run(p *p2p.Peer, rw p2p.MsgReadWriter) error { 242 spec := createTestSpec() 243 //创建会计挂钩 244 spec.Hook = NewAccounting(t, &dummyPrices{}) 245 246 //为此节点创建对等点 247 tp := &testPeer{NewPeer(p, rw, spec), t.i, t.bal.id2n[p.ID()], t.bal.wg} 248 t.lock.Lock() 249 t.peers[t.bal.id2n[p.ID()]] = tp 250 t.peerCount++ 251 if t.peerCount == t.bal.n-1 { 252 //建立所有对等连接后,开始从此对等端发送消息 253 go t.send() 254 } 255 t.lock.Unlock() 256 return tp.Run(tp.handle) 257 } 258 259 //P2P消息接收处理函数 260 func (tp *testPeer) handle(ctx context.Context, msg interface{}) error { 261 tp.wg.Done() 262 log.Debug("receive", "from", tp.remote, "to", tp.local, "type", reflect.TypeOf(msg), "msg", msg) 263 return nil 264 } 265 266 type testPeer struct { 267 *Peer 268 local, remote int 269 wg *sync.WaitGroup 270 } 271 272 func (t *testNode) send() { 273 log.Debug("start sending") 274 for i := 0; i < *msgs; i++ { 275 //随机确定要发送到哪个对等机 276 whom := rand.Intn(t.bal.n - 1) 277 if whom >= t.i { 278 whom++ 279 } 280 t.lock.Lock() 281 p := t.peers[whom] 282 t.lock.Unlock() 283 284 //从要发送的规范消息中确定随机消息 285 which := rand.Intn(len(p.spec.Messages)) 286 msg := p.spec.Messages[which] 287 switch msg.(type) { 288 case *perBytesMsgReceiverPays: 289 msg = &perBytesMsgReceiverPays{Content: content[:rand.Intn(len(content))]} 290 case *perBytesMsgSenderPays: 291 msg = &perBytesMsgSenderPays{Content: content[:rand.Intn(len(content))]} 292 } 293 log.Debug("send", "from", t.i, "to", whom, "type", reflect.TypeOf(msg), "msg", msg) 294 p.Send(context.TODO(), msg) 295 } 296 } 297 298 //定义协议 299 func (t *testNode) Protocols() []p2p.Protocol { 300 return []p2p.Protocol{{ 301 Length: 100, 302 Run: t.run, 303 }} 304 } 305 306 func (t *testNode) APIs() []rpc.API { 307 return nil 308 } 309 310 func (t *testNode) Start(server *p2p.Server) error { 311 return nil 312 } 313 314 func (t *testNode) Stop() error { 315 return nil 316 } 317