github.com/arieschain/arieschain@v0.0.0-20191023063405-37c074544356/consensus/dpos/epoch_context_test.go (about)

     1  package dpos
     2  
     3  import (
     4  	"math/big"
     5  	"strconv"
     6  	"strings"
     7  	"testing"
     8  
     9  	"github.com/quickchainproject/quickchain/common"
    10  	"github.com/quickchainproject/quickchain/core/state"
    11  	"github.com/quickchainproject/quickchain/core/types"
    12  	"github.com/quickchainproject/quickchain/trie"
    13  	"github.com/quickchainproject/quickchain/qctdb"
    14  
    15  	"github.com/stretchr/testify/assert"
    16  )
    17  
    18  func TestEpochContextCountVotes(t *testing.T) {
    19  	voteMap := map[common.Address][]common.Address{
    20  		common.HexToAddress("0x44d1ce0b7cb3588bca96151fe1bc05af38f91b6e"): {
    21  			common.HexToAddress("0xb040353ec0f2c113d5639444f7253681aecda1f8"),
    22  		},
    23  		common.HexToAddress("0xa60a3886b552ff9992cfcd208ec1152079e046c2"): {
    24  			common.HexToAddress("0x14432e15f21237013017fa6ee90fc99433dec82c"),
    25  			common.HexToAddress("0x9f30d0e5c9c88cade54cd1adecf6bc2c7e0e5af6"),
    26  		},
    27  		common.HexToAddress("0x4e080e49f62694554871e669aeb4ebe17c4a9670"): {
    28  			common.HexToAddress("0xd83b44a3719720ec54cdb9f54c0202de68f1ebcb"),
    29  			common.HexToAddress("0x56cc452e450551b7b9cffe25084a069e8c1e9441"),
    30  			common.HexToAddress("0xbcfcb3fa8250be4f2bf2b1e70e1da500c668377b"),
    31  		},
    32  		common.HexToAddress("0x9d9667c71bb09d6ca7c3ed12bfe5e7be24e2ffe1"): {},
    33  	}
    34  	balance := int64(5)
    35  	db, _ := qctdb.NewMemDatabase()
    36  	stateDB, _ := state.New(common.Hash{}, state.NewDatabase(db))
    37  	dposContext, err := types.NewDposContext(db)
    38  	assert.Nil(t, err)
    39  
    40  	epochContext := &EpochContext{
    41  		DposContext: dposContext,
    42  		statedb:     stateDB,
    43  	}
    44  	_, err = epochContext.countVotes()
    45  	assert.NotNil(t, err)
    46  
    47  	for candidate, electors := range voteMap {
    48  		assert.Nil(t, dposContext.BecomeCandidate(candidate))
    49  		for _, elector := range electors {
    50  			stateDB.SetBalance(elector, big.NewInt(balance))
    51  			assert.Nil(t, dposContext.Delegate(elector, candidate, big.NewInt(1), big.NewInt(1)))
    52  		}
    53  	}
    54  	result, err := epochContext.countVotes()
    55  	assert.Nil(t, err)
    56  	assert.Equal(t, len(voteMap), len(result))
    57  	for candidate, electors := range voteMap {
    58  		voteCount, ok := result[candidate]
    59  		assert.True(t, ok)
    60  		assert.Equal(t, balance*int64(len(electors)), voteCount.Int64())
    61  	}
    62  }
    63  
    64  /*
    65  func TestLookupValidator(t *testing.T) {
    66  	db, _ := qctdb.NewMemDatabase()
    67  	dposCtx, _ := types.NewDposContext(db)
    68  	mockEpochContext := &EpochContext{
    69  		DposContext: dposCtx,
    70  	}
    71  	validators := []common.Address{
    72  		common.StringToAddress("addr1"),
    73  		common.StringToAddress("addr2"),
    74  		common.StringToAddress("addr3"),
    75  	}
    76  	mockEpochContext.DposContext.SetValidators(validators)
    77  	for i, expected := range validators {
    78  		got, _ := mockEpochContext.lookupValidator(int64(i) * blockInterval)
    79  		if got != expected {
    80  			t.Errorf("Failed to test lookup validator, %s was expected but got %s", expected.Str(), got.Str())
    81  		}
    82  	}
    83  	_, err := mockEpochContext.lookupValidator(blockInterval - 1)
    84  	if err != ErrInvalidMintBlockTime {
    85  		t.Errorf("Failed to test lookup validator. err '%v' was expected but got '%v'", ErrInvalidMintBlockTime, err)
    86  	}
    87  }
    88  
    89  func TestEpochContextKickoutValidator(t *testing.T) {
    90  	db, _ := qctdb.NewMemDatabase()
    91  	stateDB, _ := state.New(common.Hash{}, state.NewDatabase(db))
    92  	dposContext, err := types.NewDposContext(db)
    93  	assert.Nil(t, err)
    94  	epochContext := &EpochContext{
    95  		TimeStamp:   epochInterval,
    96  		DposContext: dposContext,
    97  		statedb:     stateDB,
    98  	}
    99  	atLeastMintCnt := epochInterval / blockInterval / maxValidatorSize / 2
   100  	testEpoch := int64(1)
   101  
   102  	// no validator can be kickout, because all validators mint enough block at least
   103  	validators := []common.Address{}
   104  	for i := 0; i < maxValidatorSize; i++ {
   105  		validator := common.StringToAddress("addr" + strconv.Itoa(i))
   106  		validators = append(validators, validator)
   107  		assert.Nil(t, dposContext.BecomeCandidate(validator))
   108  		setTestMintCnt(dposContext, testEpoch, validator, atLeastMintCnt)
   109  	}
   110  	assert.Nil(t, dposContext.SetValidators(validators))
   111  	assert.Nil(t, dposContext.BecomeCandidate(common.StringToAddress("addr")))
   112  	assert.Nil(t, epochContext.kickoutValidator(testEpoch))
   113  	candidateMap := getCandidates(dposContext.CandidateTrie())
   114  	assert.Equal(t, maxValidatorSize+1, len(candidateMap))
   115  
   116  	// atLeast a safeSize count candidate will reserve
   117  	dposContext, err = types.NewDposContext(db)
   118  	assert.Nil(t, err)
   119  	epochContext = &EpochContext{
   120  		TimeStamp:   epochInterval,
   121  		DposContext: dposContext,
   122  		statedb:     stateDB,
   123  	}
   124  	validators = []common.Address{}
   125  	for i := 0; i < maxValidatorSize; i++ {
   126  		validator := common.StringToAddress("addr" + strconv.Itoa(i))
   127  		validators = append(validators, validator)
   128  		assert.Nil(t, dposContext.BecomeCandidate(validator))
   129  		setTestMintCnt(dposContext, testEpoch, validator, atLeastMintCnt-int64(i)-1)
   130  	}
   131  	assert.Nil(t, dposContext.SetValidators(validators))
   132  	assert.Nil(t, epochContext.kickoutValidator(testEpoch))
   133  	candidateMap = getCandidates(dposContext.CandidateTrie())
   134  	assert.Equal(t, safeSize, len(candidateMap))
   135  	for i := maxValidatorSize - 1; i >= safeSize; i-- {
   136  		assert.False(t, candidateMap[common.StringToAddress("addr"+strconv.Itoa(i))])
   137  	}
   138  
   139  	// all validator will be kickout, because all validators didn't mint enough block at least
   140  	dposContext, err = types.NewDposContext(db)
   141  	assert.Nil(t, err)
   142  	epochContext = &EpochContext{
   143  		TimeStamp:   epochInterval,
   144  		DposContext: dposContext,
   145  		statedb:     stateDB,
   146  	}
   147  	validators = []common.Address{}
   148  	for i := 0; i < maxValidatorSize; i++ {
   149  		validator := common.StringToAddress("addr" + strconv.Itoa(i))
   150  		validators = append(validators, validator)
   151  		assert.Nil(t, dposContext.BecomeCandidate(validator))
   152  		setTestMintCnt(dposContext, testEpoch, validator, atLeastMintCnt-1)
   153  	}
   154  	for i := maxValidatorSize; i < maxValidatorSize*2; i++ {
   155  		candidate := common.StringToAddress("addr" + strconv.Itoa(i))
   156  		assert.Nil(t, dposContext.BecomeCandidate(candidate))
   157  	}
   158  	assert.Nil(t, dposContext.SetValidators(validators))
   159  	assert.Nil(t, epochContext.kickoutValidator(testEpoch))
   160  	candidateMap = getCandidates(dposContext.CandidateTrie())
   161  	assert.Equal(t, maxValidatorSize, len(candidateMap))
   162  
   163  	// only one validator mint count is not enough
   164  	dposContext, err = types.NewDposContext(db)
   165  	assert.Nil(t, err)
   166  	epochContext = &EpochContext{
   167  		TimeStamp:   epochInterval,
   168  		DposContext: dposContext,
   169  		statedb:     stateDB,
   170  	}
   171  	validators = []common.Address{}
   172  	for i := 0; i < maxValidatorSize; i++ {
   173  		validator := common.StringToAddress("addr" + strconv.Itoa(i))
   174  		validators = append(validators, validator)
   175  		assert.Nil(t, dposContext.BecomeCandidate(validator))
   176  		if i == 0 {
   177  			setTestMintCnt(dposContext, testEpoch, validator, atLeastMintCnt-1)
   178  		} else {
   179  			setTestMintCnt(dposContext, testEpoch, validator, atLeastMintCnt)
   180  		}
   181  	}
   182  	assert.Nil(t, dposContext.BecomeCandidate(common.StringToAddress("addr")))
   183  	assert.Nil(t, dposContext.SetValidators(validators))
   184  	assert.Nil(t, epochContext.kickoutValidator(testEpoch))
   185  	candidateMap = getCandidates(dposContext.CandidateTrie())
   186  	assert.Equal(t, maxValidatorSize, len(candidateMap))
   187  	assert.False(t, candidateMap[common.StringToAddress("addr"+strconv.Itoa(0))])
   188  
   189  	// epochTime is not complete, all validators mint enough block at least
   190  	dposContext, err = types.NewDposContext(db)
   191  	assert.Nil(t, err)
   192  	epochContext = &EpochContext{
   193  		TimeStamp:   epochInterval / 2,
   194  		DposContext: dposContext,
   195  		statedb:     stateDB,
   196  	}
   197  	validators = []common.Address{}
   198  	for i := 0; i < maxValidatorSize; i++ {
   199  		validator := common.StringToAddress("addr" + strconv.Itoa(i))
   200  		validators = append(validators, validator)
   201  		assert.Nil(t, dposContext.BecomeCandidate(validator))
   202  		setTestMintCnt(dposContext, testEpoch, validator, atLeastMintCnt/2)
   203  	}
   204  	for i := maxValidatorSize; i < maxValidatorSize*2; i++ {
   205  		candidate := common.StringToAddress("addr" + strconv.Itoa(i))
   206  		assert.Nil(t, dposContext.BecomeCandidate(candidate))
   207  	}
   208  	assert.Nil(t, dposContext.SetValidators(validators))
   209  	assert.Nil(t, epochContext.kickoutValidator(testEpoch))
   210  	candidateMap = getCandidates(dposContext.CandidateTrie())
   211  	assert.Equal(t, maxValidatorSize*2, len(candidateMap))
   212  
   213  	// epochTime is not complete, all validators didn't mint enough block at least
   214  	dposContext, err = types.NewDposContext(db)
   215  	assert.Nil(t, err)
   216  	epochContext = &EpochContext{
   217  		TimeStamp:   epochInterval / 2,
   218  		DposContext: dposContext,
   219  		statedb:     stateDB,
   220  	}
   221  	validators = []common.Address{}
   222  	for i := 0; i < maxValidatorSize; i++ {
   223  		validator := common.StringToAddress("addr" + strconv.Itoa(i))
   224  		validators = append(validators, validator)
   225  		assert.Nil(t, dposContext.BecomeCandidate(validator))
   226  		setTestMintCnt(dposContext, testEpoch, validator, atLeastMintCnt/2-1)
   227  	}
   228  	for i := maxValidatorSize; i < maxValidatorSize*2; i++ {
   229  		candidate := common.StringToAddress("addr" + strconv.Itoa(i))
   230  		assert.Nil(t, dposContext.BecomeCandidate(candidate))
   231  	}
   232  	assert.Nil(t, dposContext.SetValidators(validators))
   233  	assert.Nil(t, epochContext.kickoutValidator(testEpoch))
   234  	candidateMap = getCandidates(dposContext.CandidateTrie())
   235  	assert.Equal(t, maxValidatorSize, len(candidateMap))
   236  
   237  	dposContext, err = types.NewDposContext(db)
   238  	assert.Nil(t, err)
   239  	epochContext = &EpochContext{
   240  		TimeStamp:   epochInterval / 2,
   241  		DposContext: dposContext,
   242  		statedb:     stateDB,
   243  	}
   244  	assert.NotNil(t, epochContext.kickoutValidator(testEpoch))
   245  	dposContext.SetValidators([]common.Address{})
   246  	assert.NotNil(t, epochContext.kickoutValidator(testEpoch))
   247  }
   248  
   249  func setTestMintCnt(dposContext *types.DposContext, epoch int64, validator common.Address, count int64) {
   250  	for i := int64(0); i < count; i++ {
   251  		updateMintCnt(epoch*epochInterval, epoch*epochInterval+blockInterval, validator, dposContext)
   252  	}
   253  }
   254  
   255  func getCandidates(candidateTrie *trie.Trie) map[common.Address]bool {
   256  	candidateMap := map[common.Address]bool{}
   257  	iter := trie.NewIterator(candidateTrie.NodeIterator(nil))
   258  	for iter.Next() {
   259  		candidateMap[common.BytesToAddress(iter.Value)] = true
   260  	}
   261  	return candidateMap
   262  }
   263  
   264  func TestEpochContextTryElect(t *testing.T) {
   265  	db, _ := qctdb.NewMemDatabase()
   266  	stateDB, _ := state.New(common.Hash{}, state.NewDatabase(db))
   267  	dposContext, err := types.NewDposContext(db)
   268  	assert.Nil(t, err)
   269  	epochContext := &EpochContext{
   270  		TimeStamp:   epochInterval,
   271  		DposContext: dposContext,
   272  		statedb:     stateDB,
   273  	}
   274  	atLeastMintCnt := epochInterval / blockInterval / maxValidatorSize / 2
   275  	testEpoch := int64(1)
   276  	validators := []common.Address{}
   277  	for i := 0; i < maxValidatorSize; i++ {
   278  		validator := common.StringToAddress("addr" + strconv.Itoa(i))
   279  		validators = append(validators, validator)
   280  		assert.Nil(t, dposContext.BecomeCandidate(validator))
   281  		assert.Nil(t, dposContext.Delegate(validator, validator, big.NewInt(1), big.NewInt(1)))
   282  		stateDB.SetBalance(validator, big.NewInt(1))
   283  		setTestMintCnt(dposContext, testEpoch, validator, atLeastMintCnt-1)
   284  	}
   285  	dposContext.BecomeCandidate(common.StringToAddress("more"))
   286  	assert.Nil(t, dposContext.SetValidators(validators))
   287  
   288  	// genesisEpoch == parentEpoch do not kickout
   289  	genesis := &types.Header{
   290  		Time: big.NewInt(0),
   291  	}
   292  	parent := &types.Header{
   293  		Time: big.NewInt(epochInterval - blockInterval),
   294  	}
   295  	oldHash := dposContext.EpochTrie().Hash()
   296  	assert.Nil(t, epochContext.tryElect(genesis, parent))
   297  	result, err := dposContext.GetValidators()
   298  	assert.Nil(t, err)
   299  	assert.Equal(t, maxValidatorSize, len(result))
   300  	for _, validator := range result {
   301  		assert.True(t, strings.Contains(validator.Str(), "addr"))
   302  	}
   303  	assert.NotEqual(t, oldHash, dposContext.EpochTrie().Hash())
   304  
   305  	// genesisEpoch != parentEpoch and have none mintCnt do not kickout
   306  	genesis = &types.Header{
   307  		Time: big.NewInt(-epochInterval),
   308  	}
   309  	parent = &types.Header{
   310  		Difficulty: big.NewInt(1),
   311  		Time:       big.NewInt(epochInterval - blockInterval),
   312  	}
   313  	epochContext.TimeStamp = epochInterval
   314  	oldHash = dposContext.EpochTrie().Hash()
   315  	assert.Nil(t, epochContext.tryElect(genesis, parent))
   316  	result, err = dposContext.GetValidators()
   317  	assert.Nil(t, err)
   318  	assert.Equal(t, maxValidatorSize, len(result))
   319  	for _, validator := range result {
   320  		assert.True(t, strings.Contains(validator.Str(), "addr"))
   321  	}
   322  	assert.NotEqual(t, oldHash, dposContext.EpochTrie().Hash())
   323  
   324  	// genesisEpoch != parentEpoch kickout
   325  	genesis = &types.Header{
   326  		Time: big.NewInt(0),
   327  	}
   328  	parent = &types.Header{
   329  		Time: big.NewInt(epochInterval*2 - blockInterval),
   330  	}
   331  	epochContext.TimeStamp = epochInterval * 2
   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, safeSize, len(result))
   337  	moreCnt := 0
   338  	for _, validator := range result {
   339  		if strings.Contains(validator.Str(), "more") {
   340  			moreCnt++
   341  		}
   342  	}
   343  	assert.Equal(t, 1, moreCnt)
   344  	assert.NotEqual(t, oldHash, dposContext.EpochTrie().Hash())
   345  
   346  	// parentEpoch == currentEpoch do not elect
   347  	genesis = &types.Header{
   348  		Time: big.NewInt(0),
   349  	}
   350  	parent = &types.Header{
   351  		Time: big.NewInt(epochInterval),
   352  	}
   353  	epochContext.TimeStamp = epochInterval + blockInterval
   354  	oldHash = dposContext.EpochTrie().Hash()
   355  	assert.Nil(t, epochContext.tryElect(genesis, parent))
   356  	result, err = dposContext.GetValidators()
   357  	assert.Nil(t, err)
   358  	assert.Equal(t, safeSize, len(result))
   359  	assert.Equal(t, oldHash, dposContext.EpochTrie().Hash())
   360  }
   361  */