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