123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179 |
- package core
- import (
- "fmt"
- "math/big"
- "math/rand"
- "testing"
- "time"
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/consensus/istanbul"
- qbfttypes "github.com/ethereum/go-ethereum/consensus/istanbul/qbft/types"
- "github.com/ethereum/go-ethereum/consensus/istanbul/validator"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/crypto"
- )
- // Tests combinations of justifications that evaluate to true.
- func TestJustifyTrue(t *testing.T) {
- for quorumSize := 3; quorumSize <= 10; quorumSize++ {
- // All ROUND-CHANGE messages have pr/pb nil
- testParameterizedCase(t, quorumSize, quorumSize, 0, 0, 0, 0, 0, true)
- // Some ROUND-CHANGE message has pr/pb not nil
- for equal := 1; equal <= quorumSize; equal++ {
- for less := 0; less <= quorumSize-equal; less++ {
- nil := quorumSize - equal - less
- testParameterizedCase(t, quorumSize, nil, equal, less, 0, quorumSize, 0, true)
- }
- }
- }
- }
- // Tests combinations of justifications that evaluate to false.
- func TestJustifyFalse(t *testing.T) {
- for quorumSize := 3; quorumSize <= 10; quorumSize++ {
- // Total ROUND-CHANGE messages less than quorumSize
- // all have pr/pb nil
- for totalRoundChange := 0; totalRoundChange < quorumSize; totalRoundChange++ {
- testParameterizedCase(t, quorumSize, totalRoundChange, 0, 0, 0, 0, 0, false)
- }
- // some has pr/pb not nil
- for totalRoundChange := 0; totalRoundChange < quorumSize; totalRoundChange++ {
- for equal := 1; equal <= totalRoundChange; equal++ {
- for less := 0; less <= totalRoundChange-equal; less++ {
- nil := totalRoundChange - equal - less
- testParameterizedCase(t, quorumSize, nil, equal, less, 0, quorumSize, 0, false)
- }
- }
- }
- // Total ROUND-CHANGE messages equal to quorumSize
- for equal := 1; equal <= quorumSize; equal++ {
- for less := 0; less <= quorumSize-equal; less++ {
- nil := quorumSize - equal - less
- // Total PREPARE messages less than quorumSize
- for total := 0; total < quorumSize; total++ {
- testParameterizedCase(t, quorumSize, nil, equal, less, 0, total, quorumSize-total, false)
- }
- // Total PREPARE messages equal to quorumSize and some PREPARE message has round different than others
- for different := 1; different <= quorumSize; different++ {
- testParameterizedCase(t, quorumSize, nil, equal, less, 0, quorumSize-different, different, false)
- }
- }
- }
- }
- }
- func testParameterizedCase(
- t *testing.T,
- quorumSize int,
- rcForNil int,
- rcEqualToTargetRound int,
- rcLowerThanTargetRound int,
- rcHigherThanTargetRound int,
- preparesForTargetRound int,
- preparesNotForTargetRound int,
- messageJustified bool) {
- pp := istanbul.NewRoundRobinProposerPolicy()
- pp.Use(istanbul.ValidatorSortByByte())
- validatorSet := validator.NewSet(generateValidators(quorumSize), pp)
- block := makeBlock(1)
- var round int64 = 10
- var targetPreparedRound int64 = 5
- rng := rand.New(rand.NewSource(time.Now().UTC().UnixNano()))
- if rcForNil+rcEqualToTargetRound+rcLowerThanTargetRound+rcHigherThanTargetRound > quorumSize {
- t.Errorf("rcForNil (%v) + rcEqualToTargetRound (%v) + rcLowerThanTargetRound (%v) + rcHigherThanTargetRound (%v) > quorumSize (%v)",
- rcForNil, rcEqualToTargetRound, rcLowerThanTargetRound, rcHigherThanTargetRound, quorumSize)
- }
- if preparesForTargetRound+preparesNotForTargetRound > quorumSize {
- t.Errorf("preparesForTargetRound (%v) + preparesNotForTargetRound (%v) > quorumSize (%v)", preparesForTargetRound, preparesNotForTargetRound, quorumSize)
- }
- // ROUND-CHANGE messages
- roundChangeMessages := make([]*qbfttypes.SignedRoundChangePayload, 0)
- for index, validator := range validatorSet.List() {
- var m *qbfttypes.SignedRoundChangePayload
- if index < rcForNil {
- m = createRoundChangeMessage(validator.Address(), round, 0, nil)
- } else if index >= rcForNil && index < rcForNil+rcEqualToTargetRound {
- m = createRoundChangeMessage(validator.Address(), round, targetPreparedRound, block)
- } else if index >= rcForNil+rcEqualToTargetRound && index < rcForNil+rcEqualToTargetRound+rcLowerThanTargetRound {
- m = createRoundChangeMessage(validator.Address(), round, int64(rng.Intn(int(targetPreparedRound)-1)+1), block)
- } else if index >= rcForNil+rcEqualToTargetRound+rcLowerThanTargetRound && index < rcForNil+rcEqualToTargetRound+rcLowerThanTargetRound+rcHigherThanTargetRound {
- m = createRoundChangeMessage(validator.Address(), round, int64(rng.Intn(int(targetPreparedRound))+int(targetPreparedRound)+1), block)
- } else {
- break
- }
- roundChangeMessages = append(roundChangeMessages, m)
- }
- // PREPARE messages
- prepareMessages := make([]*qbfttypes.Prepare, 0)
- for index, validator := range validatorSet.List() {
- var m *qbfttypes.Prepare
- if index < preparesForTargetRound {
- m = createPrepareMessage(validator.Address(), targetPreparedRound, block)
- } else if index >= preparesForTargetRound && index < preparesForTargetRound+preparesNotForTargetRound {
- notTargetPreparedRound := targetPreparedRound
- for notTargetPreparedRound == targetPreparedRound {
- notTargetPreparedRound = rng.Int63()
- }
- m = createPrepareMessage(validator.Address(), notTargetPreparedRound, block)
- } else {
- break
- }
- prepareMessages = append(prepareMessages, m)
- }
- for _, m := range roundChangeMessages {
- fmt.Printf("RC %v\n", m)
- }
- for _, m := range prepareMessages {
- fmt.Printf("PR %v\n", m)
- }
- fmt.Println("roundChangeMessages", roundChangeMessages, len(roundChangeMessages))
- if err := isJustified(block, roundChangeMessages, prepareMessages, quorumSize); err == nil && !messageJustified {
- t.Errorf("quorumSize = %v, rcForNil = %v, rcEqualToTargetRound = %v, rcLowerThanTargetRound = %v, rcHigherThanTargetRound = %v, preparesForTargetRound = %v, preparesNotForTargetRound = %v (Expected: %v, Actual: %v)",
- quorumSize, rcForNil, rcEqualToTargetRound, rcLowerThanTargetRound, rcHigherThanTargetRound, preparesForTargetRound, preparesNotForTargetRound, err == nil, !messageJustified)
- }
- }
- func createRoundChangeMessage(from common.Address, round int64, preparedRound int64, preparedBlock istanbul.Proposal) *qbfttypes.SignedRoundChangePayload {
- m := qbfttypes.NewRoundChange(big.NewInt(1), big.NewInt(1), big.NewInt(preparedRound), preparedBlock)
- m.SetSource(from)
- return &m.SignedRoundChangePayload
- }
- func createPrepareMessage(from common.Address, round int64, preparedBlock istanbul.Proposal) *qbfttypes.Prepare {
- return qbfttypes.NewPrepareWithSigAndSource(big.NewInt(1), big.NewInt(round), preparedBlock.Hash(), nil, from)
- }
- func generateValidators(n int) []common.Address {
- vals := make([]common.Address, 0)
- for i := 0; i < n; i++ {
- privateKey, _ := crypto.GenerateKey()
- vals = append(vals, crypto.PubkeyToAddress(privateKey.PublicKey))
- }
- return vals
- }
- func makeBlock(number int64) *types.Block {
- header := &types.Header{
- Difficulty: big.NewInt(0),
- Number: big.NewInt(number),
- GasLimit: 0,
- GasUsed: 0,
- Time: 0,
- }
- block := &types.Block{}
- return block.WithSeal(header)
- }
|