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