github.com/koko1123/flow-go-1@v0.29.6/consensus/hotstuff/committees/cluster_committee_test.go (about)

     1  package committees
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/stretchr/testify/suite"
     7  
     8  	"github.com/koko1123/flow-go-1/consensus/hotstuff/model"
     9  	"github.com/koko1123/flow-go-1/model/cluster"
    10  	"github.com/koko1123/flow-go-1/model/flow"
    11  	clusterstate "github.com/koko1123/flow-go-1/state/cluster"
    12  	"github.com/koko1123/flow-go-1/state/protocol"
    13  	protocolmock "github.com/koko1123/flow-go-1/state/protocol/mock"
    14  	"github.com/koko1123/flow-go-1/state/protocol/seed"
    15  	storagemock "github.com/koko1123/flow-go-1/storage/mock"
    16  	"github.com/koko1123/flow-go-1/utils/unittest"
    17  )
    18  
    19  // ClusterSuite defines a test suite for the cluster committee.
    20  type ClusterSuite struct {
    21  	suite.Suite
    22  
    23  	state    *protocolmock.State
    24  	snap     *protocolmock.Snapshot
    25  	cluster  *protocolmock.Cluster
    26  	epoch    *protocolmock.Epoch
    27  	payloads *storagemock.ClusterPayloads
    28  
    29  	members flow.IdentityList
    30  	root    *cluster.Block
    31  	me      *flow.Identity
    32  
    33  	com *Cluster
    34  }
    35  
    36  func TestClusterCommittee(t *testing.T) {
    37  	suite.Run(t, new(ClusterSuite))
    38  }
    39  
    40  func (suite *ClusterSuite) SetupTest() {
    41  
    42  	suite.state = new(protocolmock.State)
    43  	suite.snap = new(protocolmock.Snapshot)
    44  	suite.cluster = new(protocolmock.Cluster)
    45  	suite.epoch = new(protocolmock.Epoch)
    46  	suite.payloads = new(storagemock.ClusterPayloads)
    47  
    48  	suite.members = unittest.IdentityListFixture(5, unittest.WithRole(flow.RoleCollection))
    49  	suite.me = suite.members[0]
    50  	counter := uint64(1)
    51  	suite.root = clusterstate.CanonicalRootBlock(counter, suite.members)
    52  
    53  	suite.cluster.On("EpochCounter").Return(counter)
    54  	suite.cluster.On("Index").Return(uint(1))
    55  	suite.cluster.On("Members").Return(suite.members)
    56  	suite.cluster.On("RootBlock").Return(suite.root)
    57  	suite.epoch.On("Counter").Return(counter, nil)
    58  	suite.epoch.On("RandomSource").Return(unittest.SeedFixture(seed.RandomSourceLength), nil)
    59  
    60  	var err error
    61  	suite.com, err = NewClusterCommittee(
    62  		suite.state,
    63  		suite.payloads,
    64  		suite.cluster,
    65  		suite.epoch,
    66  		suite.me.NodeID,
    67  	)
    68  	suite.Require().NoError(err)
    69  }
    70  
    71  // TestInvalidSigner tests that the InvalidSignerError sentinel is
    72  // returned under the appropriate conditions.
    73  func (suite *ClusterSuite) TestInvalidSigner() {
    74  
    75  	// hook up cluster->main chain connection for root and non-root cluster block
    76  	nonRootBlockID := unittest.IdentifierFixture()
    77  	rootBlockID := suite.root.ID()
    78  
    79  	refID := unittest.IdentifierFixture()            // reference block on main chain
    80  	payload := cluster.EmptyPayload(refID)           // payload referencing main chain
    81  	rootPayload := cluster.EmptyPayload(flow.ZeroID) // root cluster block payload
    82  
    83  	suite.payloads.On("ByBlockID", nonRootBlockID).Return(&payload, nil)
    84  	suite.payloads.On("ByBlockID", rootBlockID).Return(&rootPayload, nil)
    85  
    86  	// a real cluster member which continues to be a valid member
    87  	realClusterMember := suite.members[1]
    88  	// a real cluster member which loses all its weight between cluster initialization
    89  	// and the test's reference block
    90  	realNoWeightClusterMember := suite.members[2]
    91  	realNoWeightClusterMember.Weight = 0
    92  	// a real cluster member which is ejected between cluster initialization and
    93  	// the test's reference block
    94  	realEjectedClusterMember := suite.members[3]
    95  	realEjectedClusterMember.Ejected = true
    96  	realNonClusterMember := unittest.IdentityFixture(unittest.WithRole(flow.RoleCollection))
    97  	fakeID := unittest.IdentifierFixture()
    98  
    99  	suite.state.On("AtBlockID", refID).Return(suite.snap)
   100  	suite.snap.On("Identity", realClusterMember.NodeID).Return(realClusterMember, nil)
   101  	suite.snap.On("Identity", realNoWeightClusterMember.NodeID).Return(realNoWeightClusterMember, nil)
   102  	suite.snap.On("Identity", realEjectedClusterMember.NodeID).Return(realEjectedClusterMember, nil)
   103  	suite.snap.On("Identity", realNonClusterMember.NodeID).Return(realNonClusterMember, nil)
   104  	suite.snap.On("Identity", fakeID).Return(nil, protocol.IdentityNotFoundError{})
   105  
   106  	suite.Run("should return InvalidSignerError for non-existent signer", func() {
   107  		suite.Run("root block", func() {
   108  			_, err := suite.com.Identity(rootBlockID, fakeID)
   109  			suite.Assert().True(model.IsInvalidSignerError(err))
   110  		})
   111  		suite.Run("non-root block", func() {
   112  			_, err := suite.com.Identity(nonRootBlockID, fakeID)
   113  			suite.Assert().True(model.IsInvalidSignerError(err))
   114  		})
   115  	})
   116  
   117  	suite.Run("should return InvalidSignerError for existent non-cluster-member", func() {
   118  		suite.Run("root block", func() {
   119  			_, err := suite.com.Identity(rootBlockID, realNonClusterMember.NodeID)
   120  			suite.Assert().True(model.IsInvalidSignerError(err))
   121  		})
   122  		suite.Run("non-root block", func() {
   123  			_, err := suite.com.Identity(nonRootBlockID, realNonClusterMember.NodeID)
   124  			suite.Assert().True(model.IsInvalidSignerError(err))
   125  		})
   126  	})
   127  
   128  	suite.Run("should return ErrInvalidSigner for existent but ejected cluster member", func() {
   129  		// at the root block, the cluster member is not ejected yet
   130  		suite.Run("root block", func() {
   131  			actual, err := suite.com.Identity(rootBlockID, realEjectedClusterMember.NodeID)
   132  			suite.Require().NoError(err)
   133  			suite.Assert().Equal(realEjectedClusterMember, actual)
   134  		})
   135  		suite.Run("non-root block", func() {
   136  			_, err := suite.com.Identity(nonRootBlockID, realEjectedClusterMember.NodeID)
   137  			suite.Assert().True(model.IsInvalidSignerError(err))
   138  		})
   139  	})
   140  
   141  	suite.Run("should return ErrInvalidSigner for existent but zero-weight cluster member", func() {
   142  		// at the root block, the cluster member has its initial weight
   143  		suite.Run("root block", func() {
   144  			actual, err := suite.com.Identity(rootBlockID, realNoWeightClusterMember.NodeID)
   145  			suite.Require().NoError(err)
   146  			suite.Assert().Equal(realNoWeightClusterMember, actual)
   147  		})
   148  		suite.Run("non-root block", func() {
   149  			_, err := suite.com.Identity(nonRootBlockID, realNoWeightClusterMember.NodeID)
   150  			suite.Assert().True(model.IsInvalidSignerError(err))
   151  		})
   152  	})
   153  
   154  	suite.Run("should return identity for existent cluster member", func() {
   155  		suite.Run("root block", func() {
   156  			actual, err := suite.com.Identity(rootBlockID, realClusterMember.NodeID)
   157  			suite.Require().NoError(err)
   158  			suite.Assert().Equal(realClusterMember, actual)
   159  		})
   160  		suite.Run("non-root block", func() {
   161  			actual, err := suite.com.Identity(nonRootBlockID, realClusterMember.NodeID)
   162  			suite.Require().NoError(err)
   163  			suite.Assert().Equal(realClusterMember, actual)
   164  		})
   165  	})
   166  }