github.com/kisexp/xdchain@v0.0.0-20211206025815-490d6b732aa7/consensus/istanbul/backend/engine_test.go (about) 1 // Copyright 2017 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package backend 18 19 import ( 20 "bytes" 21 "crypto/ecdsa" 22 "math/big" 23 "reflect" 24 "testing" 25 "time" 26 27 "github.com/kisexp/xdchain/common" 28 "github.com/kisexp/xdchain/consensus" 29 "github.com/kisexp/xdchain/consensus/istanbul" 30 istanbulcommon "github.com/kisexp/xdchain/consensus/istanbul/common" 31 "github.com/kisexp/xdchain/consensus/istanbul/testutils" 32 "github.com/kisexp/xdchain/core" 33 "github.com/kisexp/xdchain/core/rawdb" 34 "github.com/kisexp/xdchain/core/types" 35 "github.com/kisexp/xdchain/core/vm" 36 "github.com/kisexp/xdchain/crypto" 37 ) 38 39 func newBlockchainFromConfig(genesis *core.Genesis, nodeKeys []*ecdsa.PrivateKey, cfg *istanbul.Config) (*core.BlockChain, *Backend) { 40 memDB := rawdb.NewMemoryDatabase() 41 42 // Use the first key as private key 43 backend := New(cfg, nodeKeys[0], memDB) 44 45 backend.qbftConsensusEnabled = backend.IsQBFTConsensus() 46 genesis.MustCommit(memDB) 47 48 blockchain, err := core.NewBlockChain(memDB, nil, genesis.Config, backend, vm.Config{}, nil, nil, nil) 49 if err != nil { 50 panic(err) 51 } 52 53 backend.Start(blockchain, blockchain.CurrentBlock, blockchain.HasBadBlock) 54 55 snap, err := backend.snapshot(blockchain, 0, common.Hash{}, nil) 56 if err != nil { 57 panic(err) 58 } 59 if snap == nil { 60 panic("failed to get snapshot") 61 } 62 proposerAddr := snap.ValSet.GetProposer().Address() 63 64 // find proposer key 65 for _, key := range nodeKeys { 66 addr := crypto.PubkeyToAddress(key.PublicKey) 67 if addr.String() == proposerAddr.String() { 68 backend.privateKey = key 69 backend.address = addr 70 } 71 } 72 73 return blockchain, backend 74 } 75 76 // in this test, we can set n to 1, and it means we can process Istanbul and commit a 77 // block by one node. Otherwise, if n is larger than 1, we have to generate 78 // other fake events to process Istanbul. 79 func newBlockChain(n int, qbftBlock *big.Int) (*core.BlockChain, *Backend) { 80 isQBFT := qbftBlock != nil && qbftBlock.Uint64() == 0 81 genesis, nodeKeys := testutils.GenesisAndKeys(n, isQBFT) 82 83 config := copyConfig(istanbul.DefaultConfig) 84 85 config.TestQBFTBlock = qbftBlock 86 87 return newBlockchainFromConfig(genesis, nodeKeys, config) 88 } 89 90 // copyConfig create a copy of istanbul.Config, so that changing it does not update the original 91 func copyConfig(config *istanbul.Config) *istanbul.Config { 92 cpy := *config 93 return &cpy 94 } 95 96 func makeHeader(parent *types.Block, config *istanbul.Config) *types.Header { 97 header := &types.Header{ 98 ParentHash: parent.Hash(), 99 Number: parent.Number().Add(parent.Number(), common.Big1), 100 GasLimit: core.CalcGasLimit(parent, parent.GasLimit(), parent.GasLimit()), 101 GasUsed: 0, 102 Time: parent.Time() + config.BlockPeriod, 103 Difficulty: istanbulcommon.DefaultDifficulty, 104 } 105 return header 106 } 107 108 func makeBlock(chain *core.BlockChain, engine *Backend, parent *types.Block) *types.Block { 109 block := makeBlockWithoutSeal(chain, engine, parent) 110 stopCh := make(chan struct{}) 111 resultCh := make(chan *types.Block, 10) 112 go engine.Seal(chain, block, resultCh, stopCh) 113 blk := <-resultCh 114 return blk 115 } 116 117 func makeBlockWithoutSeal(chain *core.BlockChain, engine *Backend, parent *types.Block) *types.Block { 118 header := makeHeader(parent, engine.config) 119 engine.Prepare(chain, header) 120 state, _, _ := chain.StateAt(parent.Root()) 121 block, _ := engine.FinalizeAndAssemble(chain, header, state, nil, nil, nil) 122 return block 123 } 124 125 func TestIBFTPrepare(t *testing.T) { 126 chain, engine := newBlockChain(1, nil) 127 defer engine.Stop() 128 chain.Config().Istanbul.TestQBFTBlock = nil 129 header := makeHeader(chain.Genesis(), engine.config) 130 err := engine.Prepare(chain, header) 131 if err != nil { 132 t.Errorf("error mismatch: have %v, want nil", err) 133 } 134 header.ParentHash = common.StringToHash("1234567890") 135 err = engine.Prepare(chain, header) 136 if err != consensus.ErrUnknownAncestor { 137 t.Errorf("error mismatch: have %v, want %v", err, consensus.ErrUnknownAncestor) 138 } 139 } 140 141 func TestQBFTPrepare(t *testing.T) { 142 chain, engine := newBlockChain(1, big.NewInt(0)) 143 defer engine.Stop() 144 header := makeHeader(chain.Genesis(), engine.config) 145 err := engine.Prepare(chain, header) 146 if err != nil { 147 t.Errorf("error mismatch: have %v, want nil", err) 148 } 149 150 header.ParentHash = common.StringToHash("1234567890") 151 err = engine.Prepare(chain, header) 152 if err != consensus.ErrUnknownAncestor { 153 t.Errorf("error mismatch: have %v, want %v", err, consensus.ErrUnknownAncestor) 154 } 155 } 156 157 func TestSealStopChannel(t *testing.T) { 158 chain, engine := newBlockChain(1, big.NewInt(0)) 159 defer engine.Stop() 160 block := makeBlockWithoutSeal(chain, engine, chain.Genesis()) 161 stop := make(chan struct{}, 1) 162 eventSub := engine.EventMux().Subscribe(istanbul.RequestEvent{}) 163 eventLoop := func() { 164 ev := <-eventSub.Chan() 165 _, ok := ev.Data.(istanbul.RequestEvent) 166 if !ok { 167 t.Errorf("unexpected event comes: %v", reflect.TypeOf(ev.Data)) 168 } 169 stop <- struct{}{} 170 eventSub.Unsubscribe() 171 } 172 resultCh := make(chan *types.Block, 10) 173 go func() { 174 err := engine.Seal(chain, block, resultCh, stop) 175 if err != nil { 176 t.Errorf("error mismatch: have %v, want nil", err) 177 } 178 }() 179 go eventLoop() 180 181 finalBlock := <-resultCh 182 if finalBlock != nil { 183 t.Errorf("block mismatch: have %v, want nil", finalBlock) 184 } 185 } 186 187 func TestSealCommittedOtherHash(t *testing.T) { 188 chain, engine := newBlockChain(1, big.NewInt(0)) 189 defer engine.Stop() 190 block := makeBlockWithoutSeal(chain, engine, chain.Genesis()) 191 otherBlock := makeBlockWithoutSeal(chain, engine, block) 192 expectedCommittedSeal := append([]byte{1, 2, 3}, bytes.Repeat([]byte{0x00}, types.IstanbulExtraSeal-3)...) 193 194 eventSub := engine.EventMux().Subscribe(istanbul.RequestEvent{}) 195 blockOutputChannel := make(chan *types.Block) 196 stopChannel := make(chan struct{}) 197 198 go func() { 199 ev := <-eventSub.Chan() 200 if _, ok := ev.Data.(istanbul.RequestEvent); !ok { 201 t.Errorf("unexpected event comes: %v", reflect.TypeOf(ev.Data)) 202 } 203 if err := engine.Commit(otherBlock, [][]byte{expectedCommittedSeal}, big.NewInt(0)); err != nil { 204 t.Error(err.Error()) 205 } 206 eventSub.Unsubscribe() 207 }() 208 209 go func() { 210 if err := engine.Seal(chain, block, blockOutputChannel, stopChannel); err != nil { 211 t.Error(err.Error()) 212 } 213 }() 214 215 select { 216 case <-blockOutputChannel: 217 t.Error("Wrong block found!") 218 default: 219 //no block found, stop the sealing 220 close(stopChannel) 221 } 222 223 output := <-blockOutputChannel 224 if output != nil { 225 t.Error("Block not nil!") 226 } 227 } 228 229 func updateQBFTBlock(block *types.Block, addr common.Address) *types.Block { 230 header := block.Header() 231 header.Coinbase = addr 232 return block.WithSeal(header) 233 } 234 235 func TestSealCommitted(t *testing.T) { 236 chain, engine := newBlockChain(1, big.NewInt(0)) 237 defer engine.Stop() 238 block := makeBlockWithoutSeal(chain, engine, chain.Genesis()) 239 expectedBlock := updateQBFTBlock(block, engine.Address()) 240 241 resultCh := make(chan *types.Block, 10) 242 go func() { 243 err := engine.Seal(chain, block, resultCh, make(chan struct{})) 244 245 if err != nil { 246 t.Errorf("error mismatch: have %v, want %v", err, expectedBlock) 247 } 248 }() 249 250 finalBlock := <-resultCh 251 if finalBlock.Hash() != expectedBlock.Hash() { 252 t.Errorf("hash mismatch: have %v, want %v", finalBlock.Hash(), expectedBlock.Hash()) 253 } 254 } 255 256 func TestVerifyHeader(t *testing.T) { 257 chain, engine := newBlockChain(1, big.NewInt(0)) 258 defer engine.Stop() 259 260 // istanbulcommon.ErrEmptyCommittedSeals case 261 block := makeBlockWithoutSeal(chain, engine, chain.Genesis()) 262 header := engine.chain.GetHeader(block.ParentHash(), block.NumberU64()-1) 263 block = updateQBFTBlock(block, engine.Address()) 264 err := engine.VerifyHeader(chain, block.Header(), false) 265 if err != istanbulcommon.ErrEmptyCommittedSeals { 266 t.Errorf("error mismatch: have %v, want %v", err, istanbulcommon.ErrEmptyCommittedSeals) 267 } 268 269 // short extra data 270 header = block.Header() 271 header.Extra = []byte{} 272 err = engine.VerifyHeader(chain, header, false) 273 if err != istanbulcommon.ErrInvalidExtraDataFormat { 274 t.Errorf("error mismatch: have %v, want %v", err, istanbulcommon.ErrInvalidExtraDataFormat) 275 } 276 // incorrect extra format 277 header.Extra = []byte("0000000000000000000000000000000012300000000000000000000000000000000000000000000000000000000000000000") 278 err = engine.VerifyHeader(chain, header, false) 279 if err != istanbulcommon.ErrInvalidExtraDataFormat { 280 t.Errorf("error mismatch: have %v, want %v", err, istanbulcommon.ErrInvalidExtraDataFormat) 281 } 282 283 // non zero MixDigest 284 block = makeBlockWithoutSeal(chain, engine, chain.Genesis()) 285 header = block.Header() 286 header.MixDigest = common.StringToHash("123456789") 287 err = engine.VerifyHeader(chain, header, false) 288 if err != istanbulcommon.ErrInvalidMixDigest { 289 t.Errorf("error mismatch: have %v, want %v", err, istanbulcommon.ErrInvalidMixDigest) 290 } 291 292 // invalid uncles hash 293 block = makeBlockWithoutSeal(chain, engine, chain.Genesis()) 294 header = block.Header() 295 header.UncleHash = common.StringToHash("123456789") 296 err = engine.VerifyHeader(chain, header, false) 297 if err != istanbulcommon.ErrInvalidUncleHash { 298 t.Errorf("error mismatch: have %v, want %v", err, istanbulcommon.ErrInvalidUncleHash) 299 } 300 301 // invalid difficulty 302 block = makeBlockWithoutSeal(chain, engine, chain.Genesis()) 303 header = block.Header() 304 header.Difficulty = big.NewInt(2) 305 err = engine.VerifyHeader(chain, header, false) 306 if err != istanbulcommon.ErrInvalidDifficulty { 307 t.Errorf("error mismatch: have %v, want %v", err, istanbulcommon.ErrInvalidDifficulty) 308 } 309 310 // invalid timestamp 311 block = makeBlockWithoutSeal(chain, engine, chain.Genesis()) 312 header = block.Header() 313 header.Time = chain.Genesis().Time() + (engine.config.BlockPeriod - 1) 314 err = engine.VerifyHeader(chain, header, false) 315 if err != istanbulcommon.ErrInvalidTimestamp { 316 t.Errorf("error mismatch: have %v, want %v", err, istanbulcommon.ErrInvalidTimestamp) 317 } 318 319 // future block 320 block = makeBlockWithoutSeal(chain, engine, chain.Genesis()) 321 header = block.Header() 322 header.Time = uint64(time.Now().Unix() + 10) 323 err = engine.VerifyHeader(chain, header, false) 324 if err != consensus.ErrFutureBlock { 325 t.Errorf("error mismatch: have %v, want %v", err, consensus.ErrFutureBlock) 326 } 327 328 // future block which is within AllowedFutureBlockTime 329 block = makeBlockWithoutSeal(chain, engine, chain.Genesis()) 330 header = block.Header() 331 header.Time = new(big.Int).Add(big.NewInt(time.Now().Unix()), new(big.Int).SetUint64(10)).Uint64() 332 priorValue := engine.config.AllowedFutureBlockTime 333 engine.config.AllowedFutureBlockTime = 10 334 err = engine.VerifyHeader(chain, header, false) 335 engine.config.AllowedFutureBlockTime = priorValue //restore changed value 336 if err == consensus.ErrFutureBlock { 337 t.Errorf("error mismatch: have %v, want nil", err) 338 } 339 340 // TODO This test does not make sense anymore as validate vote type is not stored in nonce 341 // invalid nonce 342 /*block = makeBlockWithoutSeal(chain, engine, chain.Genesis()) 343 header = block.Header() 344 copy(header.Nonce[:], hexutil.MustDecode("0x111111111111")) 345 header.Number = big.NewInt(int64(engine.config.Epoch)) 346 err = engine.VerifyHeader(chain, header, false) 347 if err != errInvalidNonce { 348 t.Errorf("error mismatch: have %v, want %v", err, errInvalidNonce) 349 }*/ 350 } 351 352 func TestVerifyHeaders(t *testing.T) { 353 chain, engine := newBlockChain(1, big.NewInt(0)) 354 defer engine.Stop() 355 genesis := chain.Genesis() 356 357 // success case 358 headers := []*types.Header{} 359 blocks := []*types.Block{} 360 size := 100 361 362 for i := 0; i < size; i++ { 363 var b *types.Block 364 if i == 0 { 365 b = makeBlockWithoutSeal(chain, engine, genesis) 366 b = updateQBFTBlock(b, engine.Address()) 367 } else { 368 b = makeBlockWithoutSeal(chain, engine, blocks[i-1]) 369 b = updateQBFTBlock(b, engine.Address()) 370 } 371 blocks = append(blocks, b) 372 headers = append(headers, blocks[i].Header()) 373 } 374 // now = func() time.Time { 375 // return time.Unix(int64(headers[size-1].Time), 0) 376 // } 377 _, results := engine.VerifyHeaders(chain, headers, nil) 378 const timeoutDura = 2 * time.Second 379 timeout := time.NewTimer(timeoutDura) 380 index := 0 381 OUT1: 382 for { 383 select { 384 case err := <-results: 385 if err != nil { 386 if err != istanbulcommon.ErrEmptyCommittedSeals && err != istanbulcommon.ErrInvalidCommittedSeals && err != consensus.ErrUnknownAncestor { 387 t.Errorf("error mismatch: have %v, want istanbulcommon.ErrEmptyCommittedSeals|istanbulcommon.ErrInvalidCommittedSeals|ErrUnknownAncestor", err) 388 break OUT1 389 } 390 } 391 index++ 392 if index == size { 393 break OUT1 394 } 395 case <-timeout.C: 396 break OUT1 397 } 398 } 399 _, results = engine.VerifyHeaders(chain, headers, nil) 400 timeout = time.NewTimer(timeoutDura) 401 OUT2: 402 for { 403 select { 404 case err := <-results: 405 if err != nil { 406 if err != istanbulcommon.ErrEmptyCommittedSeals && err != istanbulcommon.ErrInvalidCommittedSeals && err != consensus.ErrUnknownAncestor { 407 t.Errorf("error mismatch: have %v, want istanbulcommon.ErrEmptyCommittedSeals|istanbulcommon.ErrInvalidCommittedSeals|ErrUnknownAncestor", err) 408 break OUT2 409 } 410 } 411 case <-timeout.C: 412 break OUT2 413 } 414 } 415 // error header cases 416 headers[2].Number = big.NewInt(100) 417 _, results = engine.VerifyHeaders(chain, headers, nil) 418 timeout = time.NewTimer(timeoutDura) 419 index = 0 420 errors := 0 421 expectedErrors := 0 422 OUT3: 423 for { 424 select { 425 case err := <-results: 426 if err != nil { 427 if err != istanbulcommon.ErrEmptyCommittedSeals && err != istanbulcommon.ErrInvalidCommittedSeals && err != consensus.ErrUnknownAncestor { 428 errors++ 429 } 430 } 431 index++ 432 if index == size { 433 if errors != expectedErrors { 434 t.Errorf("error mismatch: have %v, want %v", errors, expectedErrors) 435 } 436 break OUT3 437 } 438 case <-timeout.C: 439 break OUT3 440 } 441 } 442 }