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