github.com/yinchengtsinghua/golang-Eos-dpos-Ethereum@v0.0.0-20190121132951-92cc4225ed8e/core/chain_indexer_test.go (about)

     1  
     2  //此源码被清华学神尹成大魔王专业翻译分析并修改
     3  //尹成QQ77025077
     4  //尹成微信18510341407
     5  //尹成所在QQ群721929980
     6  //尹成邮箱 yinc13@mails.tsinghua.edu.cn
     7  //尹成毕业于清华大学,微软区块链领域全球最有价值专家
     8  //https://mvp.microsoft.com/zh-cn/PublicProfile/4033620
     9  //版权所有2017 Go Ethereum作者
    10  //此文件是Go以太坊库的一部分。
    11  //
    12  //Go-Ethereum库是免费软件:您可以重新分发它和/或修改
    13  //根据GNU发布的较低通用公共许可证的条款
    14  //自由软件基金会,或者许可证的第3版,或者
    15  //(由您选择)任何更高版本。
    16  //
    17  //Go以太坊图书馆的发行目的是希望它会有用,
    18  //但没有任何保证;甚至没有
    19  //适销性或特定用途的适用性。见
    20  //GNU较低的通用公共许可证,了解更多详细信息。
    21  //
    22  //你应该收到一份GNU较低级别的公共许可证副本
    23  //以及Go以太坊图书馆。如果没有,请参见<http://www.gnu.org/licenses/>。
    24  
    25  package core
    26  
    27  import (
    28  	"context"
    29  	"fmt"
    30  	"math/big"
    31  	"math/rand"
    32  	"testing"
    33  	"time"
    34  
    35  	"github.com/ethereum/go-ethereum/common"
    36  	"github.com/ethereum/go-ethereum/core/rawdb"
    37  	"github.com/ethereum/go-ethereum/core/types"
    38  	"github.com/ethereum/go-ethereum/ethdb"
    39  )
    40  
    41  //使用随机参数运行多个测试。
    42  func TestChainIndexerSingle(t *testing.T) {
    43  	for i := 0; i < 10; i++ {
    44  		testChainIndexer(t, 1)
    45  	}
    46  }
    47  
    48  //使用随机参数和不同数量的
    49  //链后端。
    50  func TestChainIndexerWithChildren(t *testing.T) {
    51  	for i := 2; i < 8; i++ {
    52  		testChainIndexer(t, i)
    53  	}
    54  }
    55  
    56  //TestChainIndexer使用单个链索引器或
    57  //多个后端。节大小和所需的确认计数参数
    58  //是随机的。
    59  func testChainIndexer(t *testing.T, count int) {
    60  	db := ethdb.NewMemDatabase()
    61  	defer db.Close()
    62  
    63  //创建索引器链并确保它们都报告为空
    64  	backends := make([]*testChainIndexBackend, count)
    65  	for i := 0; i < count; i++ {
    66  		var (
    67  			sectionSize = uint64(rand.Intn(100) + 1)
    68  			confirmsReq = uint64(rand.Intn(10))
    69  		)
    70  		backends[i] = &testChainIndexBackend{t: t, processCh: make(chan uint64)}
    71  		backends[i].indexer = NewChainIndexer(db, ethdb.NewTable(db, string([]byte{byte(i)})), backends[i], sectionSize, confirmsReq, 0, fmt.Sprintf("indexer-%d", i))
    72  
    73  		if sections, _, _ := backends[i].indexer.Sections(); sections != 0 {
    74  			t.Fatalf("Canonical section count mismatch: have %v, want %v", sections, 0)
    75  		}
    76  		if i > 0 {
    77  			backends[i-1].indexer.AddChildIndexer(backends[i].indexer)
    78  		}
    79  	}
    80  defer backends[0].indexer.Close() //父索引器关闭子级
    81  //通知ping根索引器有关新头或REORG的信息,然后
    82  //如果节可处理,则处理块
    83  	notify := func(headNum, failNum uint64, reorg bool) {
    84  		backends[0].indexer.newHead(headNum, reorg)
    85  		if reorg {
    86  			for _, backend := range backends {
    87  				headNum = backend.reorg(headNum)
    88  				backend.assertSections()
    89  			}
    90  			return
    91  		}
    92  		var cascade bool
    93  		for _, backend := range backends {
    94  			headNum, cascade = backend.assertBlocks(headNum, failNum)
    95  			if !cascade {
    96  				break
    97  			}
    98  			backend.assertSections()
    99  		}
   100  	}
   101  //inject将新的随机规范头直接插入数据库
   102  	inject := func(number uint64) {
   103  		header := &types.Header{Number: big.NewInt(int64(number)), Extra: big.NewInt(rand.Int63()).Bytes()}
   104  		if number > 0 {
   105  			header.ParentHash = rawdb.ReadCanonicalHash(db, number-1)
   106  		}
   107  		rawdb.WriteHeader(db, header)
   108  		rawdb.WriteCanonicalHash(db, header.Hash(), number)
   109  	}
   110  //使用已存在的链启动索引器
   111  	for i := uint64(0); i <= 100; i++ {
   112  		inject(i)
   113  	}
   114  	notify(100, 100, false)
   115  
   116  //逐个添加新块
   117  	for i := uint64(101); i <= 1000; i++ {
   118  		inject(i)
   119  		notify(i, i, false)
   120  	}
   121  //做一次练习
   122  	notify(500, 500, true)
   123  
   124  //创建新的叉子
   125  	for i := uint64(501); i <= 1000; i++ {
   126  		inject(i)
   127  		notify(i, i, false)
   128  	}
   129  	for i := uint64(1001); i <= 1500; i++ {
   130  		inject(i)
   131  	}
   132  //处理可用块少于通知块的方案失败
   133  	notify(2000, 1500, false)
   134  
   135  //通知有关REORG的信息(如果在处理过程中发生,可能会导致丢失的块)
   136  	notify(1500, 1500, true)
   137  
   138  //创建新的叉子
   139  	for i := uint64(1501); i <= 2000; i++ {
   140  		inject(i)
   141  		notify(i, i, false)
   142  	}
   143  }
   144  
   145  //testchainindexbackend实现chainindexerbackend
   146  type testChainIndexBackend struct {
   147  	t                          *testing.T
   148  	indexer                    *ChainIndexer
   149  	section, headerCnt, stored uint64
   150  	processCh                  chan uint64
   151  }
   152  
   153  //断言节验证链索引器的节数是否正确。
   154  func (b *testChainIndexBackend) assertSections() {
   155  //如果不匹配,继续尝试3秒钟
   156  	var sections uint64
   157  	for i := 0; i < 300; i++ {
   158  		sections, _, _ = b.indexer.Sections()
   159  		if sections == b.stored {
   160  			return
   161  		}
   162  		time.Sleep(10 * time.Millisecond)
   163  	}
   164  	b.t.Fatalf("Canonical section count mismatch: have %v, want %v", sections, b.stored)
   165  }
   166  
   167  //断言块需要在新块到达后进行处理调用。如果
   168  //failnum<headnum,然后我们将模拟发生REORG的场景
   169  //在处理开始并且一个部分的处理失败之后。
   170  func (b *testChainIndexBackend) assertBlocks(headNum, failNum uint64) (uint64, bool) {
   171  	var sections uint64
   172  	if headNum >= b.indexer.confirmsReq {
   173  		sections = (headNum + 1 - b.indexer.confirmsReq) / b.indexer.sectionSize
   174  		if sections > b.stored {
   175  //预期已处理的块
   176  			for expectd := b.stored * b.indexer.sectionSize; expectd < sections*b.indexer.sectionSize; expectd++ {
   177  				if expectd > failNum {
   178  //在处理开始后回滚,不需要更多的处理调用
   179  //等待更新完成,以确保处理实际失败
   180  					var updating bool
   181  					for i := 0; i < 300; i++ {
   182  						b.indexer.lock.Lock()
   183  						updating = b.indexer.knownSections > b.indexer.storedSections
   184  						b.indexer.lock.Unlock()
   185  						if !updating {
   186  							break
   187  						}
   188  						time.Sleep(10 * time.Millisecond)
   189  					}
   190  					if updating {
   191  						b.t.Fatalf("update did not finish")
   192  					}
   193  					sections = expectd / b.indexer.sectionSize
   194  					break
   195  				}
   196  				select {
   197  				case <-time.After(10 * time.Second):
   198  					b.t.Fatalf("Expected processed block #%d, got nothing", expectd)
   199  				case processed := <-b.processCh:
   200  					if processed != expectd {
   201  						b.t.Errorf("Expected processed block #%d, got #%d", expectd, processed)
   202  					}
   203  				}
   204  			}
   205  			b.stored = sections
   206  		}
   207  	}
   208  	if b.stored == 0 {
   209  		return 0, false
   210  	}
   211  	return b.stored*b.indexer.sectionSize - 1, true
   212  }
   213  
   214  func (b *testChainIndexBackend) reorg(headNum uint64) uint64 {
   215  	firstChanged := headNum / b.indexer.sectionSize
   216  	if firstChanged < b.stored {
   217  		b.stored = firstChanged
   218  	}
   219  	return b.stored * b.indexer.sectionSize
   220  }
   221  
   222  func (b *testChainIndexBackend) Reset(ctx context.Context, section uint64, prevHead common.Hash) error {
   223  	b.section = section
   224  	b.headerCnt = 0
   225  	return nil
   226  }
   227  
   228  func (b *testChainIndexBackend) Process(ctx context.Context, header *types.Header) error {
   229  	b.headerCnt++
   230  	if b.headerCnt > b.indexer.sectionSize {
   231  		b.t.Error("Processing too many headers")
   232  	}
   233  //t.processch<-header.number.uint64()。
   234  	select {
   235  	case <-time.After(10 * time.Second):
   236  		b.t.Fatal("Unexpected call to Process")
   237  	case b.processCh <- header.Number.Uint64():
   238  	}
   239  	return nil
   240  }
   241  
   242  func (b *testChainIndexBackend) Commit() error {
   243  	if b.headerCnt != b.indexer.sectionSize {
   244  		b.t.Error("Not enough headers processed")
   245  	}
   246  	return nil
   247  }