github.com/linapex/ethereum-dpos-chinese@v0.0.0-20190316121959-b78b3a4a1ece/consensus/dpos/epoch_context_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 12:09:33</date>
    10  //</624342611448041472>
    11  
    12  package dpos
    13  
    14  import (
    15  	"math/big"
    16  	"strconv"
    17  	"strings"
    18  	"testing"
    19  
    20  	"github.com/ethereum/go-ethereum/common"
    21  	"github.com/ethereum/go-ethereum/core/state"
    22  	"github.com/ethereum/go-ethereum/core/types"
    23  	"github.com/ethereum/go-ethereum/ethdb"
    24  	"github.com/ethereum/go-ethereum/trie"
    25  
    26  	"github.com/stretchr/testify/assert"
    27  )
    28  
    29  func TestEpochContextCountVotes(t *testing.T) {
    30  	voteMap := map[common.Address][]common.Address{
    31  		common.HexToAddress("0x44d1ce0b7cb3588bca96151fe1bc05af38f91b6e"): {
    32  			common.HexToAddress("0xb040353ec0f2c113d5639444f7253681aecda1f8"),
    33  		},
    34  		common.HexToAddress("0xa60a3886b552ff9992cfcd208ec1152079e046c2"): {
    35  			common.HexToAddress("0x14432e15f21237013017fa6ee90fc99433dec82c"),
    36  			common.HexToAddress("0x9f30d0e5c9c88cade54cd1adecf6bc2c7e0e5af6"),
    37  		},
    38  		common.HexToAddress("0x4e080e49f62694554871e669aeb4ebe17c4a9670"): {
    39  			common.HexToAddress("0xd83b44a3719720ec54cdb9f54c0202de68f1ebcb"),
    40  			common.HexToAddress("0x56cc452e450551b7b9cffe25084a069e8c1e9441"),
    41  			common.HexToAddress("0xbcfcb3fa8250be4f2bf2b1e70e1da500c668377b"),
    42  		},
    43  		common.HexToAddress("0x9d9667c71bb09d6ca7c3ed12bfe5e7be24e2ffe1"): {},
    44  	}
    45  	balance := int64(5)
    46  	db := ethdb.NewMemDatabase()
    47  	stateDB, _ := state.New(common.Hash{}, state.NewDatabase(db))
    48  	trieDB := trie.NewDatabase(db)
    49  	dposContext, err := types.NewDposContext(trieDB)
    50  	assert.Nil(t, err)
    51  
    52  	epochContext := &EpochContext{
    53  		DposContext: dposContext,
    54  		statedb:     stateDB,
    55  	}
    56  	_, err = epochContext.countVotes()
    57  	assert.NotNil(t, err)
    58  
    59  	for candidate, electors := range voteMap {
    60  		assert.Nil(t, dposContext.BecomeCandidate(candidate))
    61  		for _, elector := range electors {
    62  			stateDB.SetBalance(elector, big.NewInt(balance))
    63  			assert.Nil(t, dposContext.Delegate(elector, candidate))
    64  		}
    65  	}
    66  	result, err := epochContext.countVotes()
    67  	assert.Nil(t, err)
    68  	assert.Equal(t, len(voteMap), len(result))
    69  	for candidate, electors := range voteMap {
    70  		voteCount, ok := result[candidate]
    71  		assert.True(t, ok)
    72  		assert.Equal(t, balance*int64(len(electors)), voteCount.Int64())
    73  	}
    74  }
    75  
    76  func TestLookupValidator(t *testing.T) {
    77  	db := ethdb.NewMemDatabase()
    78  	trieDB := trie.NewDatabase(db)
    79  	dposCtx, _ := types.NewDposContext(trieDB)
    80  	mockEpochContext := &EpochContext{
    81  		DposContext: dposCtx,
    82  	}
    83  	validators := []common.Address{
    84  		common.StringToAddress("addr1"),
    85  		common.StringToAddress("addr2"),
    86  		common.StringToAddress("addr3"),
    87  	}
    88  	mockEpochContext.DposContext.SetValidators(validators)
    89  	for i, expected := range validators {
    90  		got, _ := mockEpochContext.lookupValidator(int64(i) * blockInterval)
    91  		if got != expected {
    92  			t.Errorf("Failed to test lookup validator, %s was expected but got %s", expected.Str(), got.Str())
    93  		}
    94  	}
    95  	blockInterval := 10
    96  	_, err := mockEpochContext.lookupValidator(blockInterval - 1)
    97  	if err != ErrInvalidMintBlockTime {
    98  		t.Errorf("Failed to test lookup validator. err '%v' was expected but got '%v'", ErrInvalidMintBlockTime, err)
    99  	}
   100  }
   101  
   102  func TestEpochContextKickoutValidator(t *testing.T) {
   103  	db := ethdb.NewMemDatabase()
   104  	stateDB, _ := state.New(common.Hash{}, state.NewDatabase(db))
   105  	trieDB := trie.NewDatabase(db)
   106  	dposContext, err := types.NewDposContext(trieDB)
   107  	assert.Nil(t, err)
   108  	epochContext := &EpochContext{
   109  		TimeStamp:   epochInterval,
   110  		DposContext: dposContext,
   111  		statedb:     stateDB,
   112  	}
   113  	atLeastMintCnt := epochInterval / blockInterval / maxValidatorSize / 2
   114  	testEpoch := int64(1)
   115  
   116  //没有验证器可以被踢出,因为所有验证器至少能制造足够的块。
   117  	validators := []common.Address{}
   118  	for i := 0; i < maxValidatorSize; i++ {
   119  		validator := common.StringToAddress("addr" + strconv.Itoa(i))
   120  		validators = append(validators, validator)
   121  		assert.Nil(t, dposContext.BecomeCandidate(validator))
   122  		setTestMintCnt(dposContext, testEpoch, validator, atLeastMintCnt)
   123  	}
   124  	assert.Nil(t, dposContext.SetValidators(validators))
   125  	assert.Nil(t, dposContext.BecomeCandidate(common.StringToAddress("addr")))
   126  	assert.Nil(t, epochContext.kickoutValidator(testEpoch))
   127  	candidateMap := getCandidates(dposContext.CandidateTrie())
   128  	assert.Equal(t, maxValidatorSize +1, len(candidateMap))
   129  
   130  //至少有一个最安全的候选人将保留
   131  	trieDB = trie.NewDatabase(db)
   132  	dposContext, err = types.NewDposContext(trieDB)
   133  	assert.Nil(t, err)
   134  	epochContext = &EpochContext{
   135  		TimeStamp:   epochInterval,
   136  		DposContext: dposContext,
   137  		statedb:     stateDB,
   138  	}
   139  	validators = []common.Address{}
   140  	for i := 0; i < maxValidatorSize; i++ {
   141  		validator := common.StringToAddress("addr" + strconv.Itoa(i))
   142  		validators = append(validators, validator)
   143  		assert.Nil(t, dposContext.BecomeCandidate(validator))
   144  		setTestMintCnt(dposContext, testEpoch, validator, atLeastMintCnt-int64(i)-1)
   145  	}
   146  	assert.Nil(t, dposContext.SetValidators(validators))
   147  	assert.Nil(t, epochContext.kickoutValidator(testEpoch))
   148  	candidateMap = getCandidates(dposContext.CandidateTrie())
   149  	assert.Equal(t, safeSize, len(candidateMap))
   150  	for i := maxValidatorSize - 1; i >= safeSize; i-- {
   151  		assert.False(t, candidateMap[common.StringToAddress("addr"+strconv.Itoa(i))])
   152  	}
   153  
   154  //所有验证器都将被取消,因为所有验证器至少没有创建足够的块。
   155  	trieDB = trie.NewDatabase(db)
   156  	dposContext, err = types.NewDposContext(trieDB)
   157  	assert.Nil(t, err)
   158  	epochContext = &EpochContext{
   159  		TimeStamp:   epochInterval,
   160  		DposContext: dposContext,
   161  		statedb:     stateDB,
   162  	}
   163  	validators = []common.Address{}
   164  	for i := 0; i < maxValidatorSize; i++ {
   165  		validator := common.StringToAddress("addr" + strconv.Itoa(i))
   166  		validators = append(validators, validator)
   167  		assert.Nil(t, dposContext.BecomeCandidate(validator))
   168  		setTestMintCnt(dposContext, testEpoch, validator, atLeastMintCnt-1)
   169  	}
   170  	for i := maxValidatorSize; i < maxValidatorSize *2; i++ {
   171  		candidate := common.StringToAddress("addr" + strconv.Itoa(i))
   172  		assert.Nil(t, dposContext.BecomeCandidate(candidate))
   173  	}
   174  	assert.Nil(t, dposContext.SetValidators(validators))
   175  	assert.Nil(t, epochContext.kickoutValidator(testEpoch))
   176  	candidateMap = getCandidates(dposContext.CandidateTrie())
   177  	assert.Equal(t, maxValidatorSize, len(candidateMap))
   178  
   179  //只有一个验证器铸币计数不够
   180  	trieDB = trie.NewDatabase(db)
   181  	dposContext, err = types.NewDposContext(trieDB)
   182  	assert.Nil(t, err)
   183  	epochContext = &EpochContext{
   184  		TimeStamp:   epochInterval,
   185  		DposContext: dposContext,
   186  		statedb:     stateDB,
   187  	}
   188  	validators = []common.Address{}
   189  	for i := 0; i < maxValidatorSize; i++ {
   190  		validator := common.StringToAddress("addr" + strconv.Itoa(i))
   191  		validators = append(validators, validator)
   192  		assert.Nil(t, dposContext.BecomeCandidate(validator))
   193  		if i == 0 {
   194  			setTestMintCnt(dposContext, testEpoch, validator, atLeastMintCnt-1)
   195  		} else {
   196  			setTestMintCnt(dposContext, testEpoch, validator, atLeastMintCnt)
   197  		}
   198  	}
   199  	assert.Nil(t, dposContext.BecomeCandidate(common.StringToAddress("addr")))
   200  	assert.Nil(t, dposContext.SetValidators(validators))
   201  	assert.Nil(t, epochContext.kickoutValidator(testEpoch))
   202  	candidateMap = getCandidates(dposContext.CandidateTrie())
   203  	assert.Equal(t, maxValidatorSize, len(candidateMap))
   204  	assert.False(t, candidateMap[common.StringToAddress("addr"+strconv.Itoa(0))])
   205  
   206  //epochtime不完整,所有验证器都至少创建足够的块
   207  	trieDB = trie.NewDatabase(db)
   208  	dposContext, err = types.NewDposContext(trieDB)
   209  	assert.Nil(t, err)
   210  	epochContext = &EpochContext{
   211  		TimeStamp:   epochInterval / 2,
   212  		DposContext: dposContext,
   213  		statedb:     stateDB,
   214  	}
   215  	validators = []common.Address{}
   216  	for i := 0; i < maxValidatorSize; i++ {
   217  		validator := common.StringToAddress("addr" + strconv.Itoa(i))
   218  		validators = append(validators, validator)
   219  		assert.Nil(t, dposContext.BecomeCandidate(validator))
   220  		setTestMintCnt(dposContext, testEpoch, validator, atLeastMintCnt/2)
   221  	}
   222  	for i := maxValidatorSize; i < maxValidatorSize *2; i++ {
   223  		candidate := common.StringToAddress("addr" + strconv.Itoa(i))
   224  		assert.Nil(t, dposContext.BecomeCandidate(candidate))
   225  	}
   226  	assert.Nil(t, dposContext.SetValidators(validators))
   227  	assert.Nil(t, epochContext.kickoutValidator(testEpoch))
   228  	candidateMap = getCandidates(dposContext.CandidateTrie())
   229  	assert.Equal(t, maxValidatorSize *2, len(candidateMap))
   230  
   231  //epochtime不完整,所有验证器至少没有创建足够的块
   232  	trieDB = trie.NewDatabase(db)
   233  	dposContext, err = types.NewDposContext(trieDB)
   234  	assert.Nil(t, err)
   235  	epochContext = &EpochContext{
   236  		TimeStamp:   epochInterval / 2,
   237  		DposContext: dposContext,
   238  		statedb:     stateDB,
   239  	}
   240  	validators = []common.Address{}
   241  	for i := 0; i < maxValidatorSize; i++ {
   242  		validator := common.StringToAddress("addr" + strconv.Itoa(i))
   243  		validators = append(validators, validator)
   244  		assert.Nil(t, dposContext.BecomeCandidate(validator))
   245  		setTestMintCnt(dposContext, testEpoch, validator, atLeastMintCnt/2-1)
   246  	}
   247  	for i := maxValidatorSize; i < maxValidatorSize *2; i++ {
   248  		candidate := common.StringToAddress("addr" + strconv.Itoa(i))
   249  		assert.Nil(t, dposContext.BecomeCandidate(candidate))
   250  	}
   251  	assert.Nil(t, dposContext.SetValidators(validators))
   252  	assert.Nil(t, epochContext.kickoutValidator(testEpoch))
   253  	candidateMap = getCandidates(dposContext.CandidateTrie())
   254  	assert.Equal(t, maxValidatorSize, len(candidateMap))
   255  
   256  	trieDB = trie.NewDatabase(db)
   257  	dposContext, err = types.NewDposContext(trieDB)
   258  	assert.Nil(t, err)
   259  	epochContext = &EpochContext{
   260  		TimeStamp:   epochInterval / 2,
   261  		DposContext: dposContext,
   262  		statedb:     stateDB,
   263  	}
   264  	assert.NotNil(t, epochContext.kickoutValidator(testEpoch))
   265  	dposContext.SetValidators([]common.Address{})
   266  	assert.NotNil(t, epochContext.kickoutValidator(testEpoch))
   267  }
   268  
   269  func setTestMintCnt(dposContext *types.DposContext, epoch int64, validator common.Address, count int64) {
   270  	for i := int64(0); i < count; i++ {
   271  		updateMintCnt(epoch*epochInterval, epoch*epochInterval+blockInterval, validator, dposContext)
   272  	}
   273  }
   274  
   275  func getCandidates(candidateTrie *trie.Trie) map[common.Address]bool {
   276  	candidateMap := map[common.Address]bool{}
   277  	iter := trie.NewIterator(candidateTrie.NodeIterator(nil))
   278  	for iter.Next() {
   279  		candidateMap[common.BytesToAddress(iter.Value)] = true
   280  	}
   281  	return candidateMap
   282  }
   283  
   284  func TestEpochContextTryElect(t *testing.T) {
   285  	db := ethdb.NewMemDatabase()
   286  	stateDB, _ := state.New(common.Hash{}, state.NewDatabase(db))
   287  	trieDB := trie.NewDatabase(db)
   288  	dposContext, err := types.NewDposContext(trieDB)
   289  	assert.Nil(t, err)
   290  	epochContext := &EpochContext{
   291  		TimeStamp:   epochInterval,
   292  		DposContext: dposContext,
   293  		statedb:     stateDB,
   294  	}
   295  	atLeastMintCnt := epochInterval / blockInterval / maxValidatorSize / 2
   296  	testEpoch := int64(1)
   297  	validators := []common.Address{}
   298  	for i := 0; i < maxValidatorSize; i++ {
   299  		validator := common.StringToAddress("addr" + strconv.Itoa(i))
   300  		validators = append(validators, validator)
   301  		assert.Nil(t, dposContext.BecomeCandidate(validator))
   302  		assert.Nil(t, dposContext.Delegate(validator, validator))
   303  		stateDB.SetBalance(validator, big.NewInt(1))
   304  		setTestMintCnt(dposContext, testEpoch, validator, atLeastMintCnt-1)
   305  	}
   306  	dposContext.BecomeCandidate(common.StringToAddress("more"))
   307  	assert.Nil(t, dposContext.SetValidators(validators))
   308  
   309  //genesisepoch==parentepoch不开始
   310  	genesis := &types.Header{
   311  		Time: big.NewInt(0),
   312  	}
   313  	parent := &types.Header{
   314  		Time: big.NewInt(epochInterval - blockInterval),
   315  	}
   316  	oldHash := dposContext.EpochTrie().Hash()
   317  	assert.Nil(t, epochContext.tryElect(genesis, parent))
   318  	result, err := dposContext.GetValidators()
   319  	assert.Nil(t, err)
   320  	assert.Equal(t, maxValidatorSize, len(result))
   321  	for _, validator := range result {
   322  		assert.True(t, strings.Contains(validator.Str(), "addr"))
   323  	}
   324  	assert.NotEqual(t, oldHash, dposContext.EpochTrie().Hash())
   325  
   326  //基因时代!=parentepoch和have none mintcnt不退出
   327  	genesis = &types.Header{
   328  		Time: big.NewInt(-epochInterval),
   329  	}
   330  	parent = &types.Header{
   331  		Difficulty: big.NewInt(1),
   332  		Time:       big.NewInt(epochInterval - blockInterval),
   333  	}
   334  	epochContext.TimeStamp = epochInterval
   335  	oldHash = dposContext.EpochTrie().Hash()
   336  	assert.Nil(t, epochContext.tryElect(genesis, parent))
   337  	result, err = dposContext.GetValidators()
   338  	assert.Nil(t, err)
   339  	assert.Equal(t, maxValidatorSize, len(result))
   340  	for _, validator := range result {
   341  		assert.True(t, strings.Contains(validator.Str(), "addr"))
   342  	}
   343  	assert.NotEqual(t, oldHash, dposContext.EpochTrie().Hash())
   344  
   345  //基因时代!=ParentEpoch启动
   346  	genesis = &types.Header{
   347  		Time: big.NewInt(0),
   348  	}
   349  	parent = &types.Header{
   350  		Time: big.NewInt(epochInterval*2 - blockInterval),
   351  	}
   352  	epochContext.TimeStamp = epochInterval * 2
   353  	oldHash = dposContext.EpochTrie().Hash()
   354  	assert.Nil(t, epochContext.tryElect(genesis, parent))
   355  	result, err = dposContext.GetValidators()
   356  	assert.Nil(t, err)
   357  	assert.Equal(t, safeSize, len(result))
   358  	moreCnt := 0
   359  	for _, validator := range result {
   360  		if strings.Contains(validator.Str(), "more") {
   361  			moreCnt++
   362  		}
   363  	}
   364  	assert.Equal(t, 1, moreCnt)
   365  	assert.NotEqual(t, oldHash, dposContext.EpochTrie().Hash())
   366  
   367  //parentepoch==currentpoch不选择
   368  	genesis = &types.Header{
   369  		Time: big.NewInt(0),
   370  	}
   371  	parent = &types.Header{
   372  		Time: big.NewInt(epochInterval),
   373  	}
   374  	epochContext.TimeStamp = epochInterval + blockInterval
   375  	oldHash = dposContext.EpochTrie().Hash()
   376  	assert.Nil(t, epochContext.tryElect(genesis, parent))
   377  	result, err = dposContext.GetValidators()
   378  	assert.Nil(t, err)
   379  	assert.Equal(t, safeSize, len(result))
   380  	assert.Equal(t, oldHash, dposContext.EpochTrie().Hash())
   381  }
   382