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 */