github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/core/chain_indexer_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:35</date>
    10  //</624450078433611776>
    11  
    12  
    13  package core
    14  
    15  import (
    16  	"context"
    17  	"fmt"
    18  	"math/big"
    19  	"math/rand"
    20  	"testing"
    21  	"time"
    22  
    23  	"github.com/ethereum/go-ethereum/common"
    24  	"github.com/ethereum/go-ethereum/core/rawdb"
    25  	"github.com/ethereum/go-ethereum/core/types"
    26  	"github.com/ethereum/go-ethereum/ethdb"
    27  )
    28  
    29  //使用随机参数运行多个测试。
    30  func TestChainIndexerSingle(t *testing.T) {
    31  	for i := 0; i < 10; i++ {
    32  		testChainIndexer(t, 1)
    33  	}
    34  }
    35  
    36  //使用随机参数和不同数量的
    37  //链后端。
    38  func TestChainIndexerWithChildren(t *testing.T) {
    39  	for i := 2; i < 8; i++ {
    40  		testChainIndexer(t, i)
    41  	}
    42  }
    43  
    44  //TestChainIndexer使用单个链索引器或
    45  //多个后端。节大小和所需的确认计数参数
    46  //是随机的。
    47  func testChainIndexer(t *testing.T, count int) {
    48  	db := ethdb.NewMemDatabase()
    49  	defer db.Close()
    50  
    51  //创建索引器链并确保它们都报告为空
    52  	backends := make([]*testChainIndexBackend, count)
    53  	for i := 0; i < count; i++ {
    54  		var (
    55  			sectionSize = uint64(rand.Intn(100) + 1)
    56  			confirmsReq = uint64(rand.Intn(10))
    57  		)
    58  		backends[i] = &testChainIndexBackend{t: t, processCh: make(chan uint64)}
    59  		backends[i].indexer = NewChainIndexer(db, ethdb.NewTable(db, string([]byte{byte(i)})), backends[i], sectionSize, confirmsReq, 0, fmt.Sprintf("indexer-%d", i))
    60  
    61  		if sections, _, _ := backends[i].indexer.Sections(); sections != 0 {
    62  			t.Fatalf("Canonical section count mismatch: have %v, want %v", sections, 0)
    63  		}
    64  		if i > 0 {
    65  			backends[i-1].indexer.AddChildIndexer(backends[i].indexer)
    66  		}
    67  	}
    68  defer backends[0].indexer.Close() //父索引器关闭子级
    69  //通知ping根索引器有关新头或REORG的信息,然后
    70  //如果节可处理,则处理块
    71  	notify := func(headNum, failNum uint64, reorg bool) {
    72  		backends[0].indexer.newHead(headNum, reorg)
    73  		if reorg {
    74  			for _, backend := range backends {
    75  				headNum = backend.reorg(headNum)
    76  				backend.assertSections()
    77  			}
    78  			return
    79  		}
    80  		var cascade bool
    81  		for _, backend := range backends {
    82  			headNum, cascade = backend.assertBlocks(headNum, failNum)
    83  			if !cascade {
    84  				break
    85  			}
    86  			backend.assertSections()
    87  		}
    88  	}
    89  //inject将新的随机规范头直接插入数据库
    90  	inject := func(number uint64) {
    91  		header := &types.Header{Number: big.NewInt(int64(number)), Extra: big.NewInt(rand.Int63()).Bytes()}
    92  		if number > 0 {
    93  			header.ParentHash = rawdb.ReadCanonicalHash(db, number-1)
    94  		}
    95  		rawdb.WriteHeader(db, header)
    96  		rawdb.WriteCanonicalHash(db, header.Hash(), number)
    97  	}
    98  //使用已存在的链启动索引器
    99  	for i := uint64(0); i <= 100; i++ {
   100  		inject(i)
   101  	}
   102  	notify(100, 100, false)
   103  
   104  //逐个添加新块
   105  	for i := uint64(101); i <= 1000; i++ {
   106  		inject(i)
   107  		notify(i, i, false)
   108  	}
   109  //做一次练习
   110  	notify(500, 500, true)
   111  
   112  //创建新的叉子
   113  	for i := uint64(501); i <= 1000; i++ {
   114  		inject(i)
   115  		notify(i, i, false)
   116  	}
   117  	for i := uint64(1001); i <= 1500; i++ {
   118  		inject(i)
   119  	}
   120  //处理可用块少于通知块的方案失败
   121  	notify(2000, 1500, false)
   122  
   123  //通知有关REORG的信息(如果在处理过程中发生,可能会导致丢失的块)
   124  	notify(1500, 1500, true)
   125  
   126  //创建新的叉子
   127  	for i := uint64(1501); i <= 2000; i++ {
   128  		inject(i)
   129  		notify(i, i, false)
   130  	}
   131  }
   132  
   133  //testchainindexbackend实现chainindexerbackend
   134  type testChainIndexBackend struct {
   135  	t                          *testing.T
   136  	indexer                    *ChainIndexer
   137  	section, headerCnt, stored uint64
   138  	processCh                  chan uint64
   139  }
   140  
   141  //断言节验证链索引器的节数是否正确。
   142  func (b *testChainIndexBackend) assertSections() {
   143  //如果不匹配,继续尝试3秒钟
   144  	var sections uint64
   145  	for i := 0; i < 300; i++ {
   146  		sections, _, _ = b.indexer.Sections()
   147  		if sections == b.stored {
   148  			return
   149  		}
   150  		time.Sleep(10 * time.Millisecond)
   151  	}
   152  	b.t.Fatalf("Canonical section count mismatch: have %v, want %v", sections, b.stored)
   153  }
   154  
   155  //断言块需要在新块到达后进行处理调用。如果
   156  //failnum<headnum,然后我们将模拟发生REORG的场景
   157  //在处理开始并且一个部分的处理失败之后。
   158  func (b *testChainIndexBackend) assertBlocks(headNum, failNum uint64) (uint64, bool) {
   159  	var sections uint64
   160  	if headNum >= b.indexer.confirmsReq {
   161  		sections = (headNum + 1 - b.indexer.confirmsReq) / b.indexer.sectionSize
   162  		if sections > b.stored {
   163  //预期已处理的块
   164  			for expectd := b.stored * b.indexer.sectionSize; expectd < sections*b.indexer.sectionSize; expectd++ {
   165  				if expectd > failNum {
   166  //在处理开始后回滚,不需要更多的处理调用
   167  //等待更新完成,以确保处理实际失败
   168  					var updating bool
   169  					for i := 0; i < 300; i++ {
   170  						b.indexer.lock.Lock()
   171  						updating = b.indexer.knownSections > b.indexer.storedSections
   172  						b.indexer.lock.Unlock()
   173  						if !updating {
   174  							break
   175  						}
   176  						time.Sleep(10 * time.Millisecond)
   177  					}
   178  					if updating {
   179  						b.t.Fatalf("update did not finish")
   180  					}
   181  					sections = expectd / b.indexer.sectionSize
   182  					break
   183  				}
   184  				select {
   185  				case <-time.After(10 * time.Second):
   186  					b.t.Fatalf("Expected processed block #%d, got nothing", expectd)
   187  				case processed := <-b.processCh:
   188  					if processed != expectd {
   189  						b.t.Errorf("Expected processed block #%d, got #%d", expectd, processed)
   190  					}
   191  				}
   192  			}
   193  			b.stored = sections
   194  		}
   195  	}
   196  	if b.stored == 0 {
   197  		return 0, false
   198  	}
   199  	return b.stored*b.indexer.sectionSize - 1, true
   200  }
   201  
   202  func (b *testChainIndexBackend) reorg(headNum uint64) uint64 {
   203  	firstChanged := headNum / b.indexer.sectionSize
   204  	if firstChanged < b.stored {
   205  		b.stored = firstChanged
   206  	}
   207  	return b.stored * b.indexer.sectionSize
   208  }
   209  
   210  func (b *testChainIndexBackend) Reset(ctx context.Context, section uint64, prevHead common.Hash) error {
   211  	b.section = section
   212  	b.headerCnt = 0
   213  	return nil
   214  }
   215  
   216  func (b *testChainIndexBackend) Process(ctx context.Context, header *types.Header) error {
   217  	b.headerCnt++
   218  	if b.headerCnt > b.indexer.sectionSize {
   219  		b.t.Error("Processing too many headers")
   220  	}
   221  //t.processch<-header.number.uint64()。
   222  	select {
   223  	case <-time.After(10 * time.Second):
   224  		b.t.Fatal("Unexpected call to Process")
   225  	case b.processCh <- header.Number.Uint64():
   226  	}
   227  	return nil
   228  }
   229  
   230  func (b *testChainIndexBackend) Commit() error {
   231  	if b.headerCnt != b.indexer.sectionSize {
   232  		b.t.Error("Not enough headers processed")
   233  	}
   234  	return nil
   235  }
   236