github.com/koko1123/flow-go-1@v0.29.6/consensus/hotstuff/signature/randombeacon_inspector_test.go (about) 1 package signature 2 3 import ( 4 "errors" 5 mrand "math/rand" 6 "sync" 7 "testing" 8 "time" 9 10 "github.com/stretchr/testify/assert" 11 "github.com/stretchr/testify/require" 12 "github.com/stretchr/testify/suite" 13 14 "github.com/koko1123/flow-go-1/consensus/hotstuff/model" 15 "github.com/onflow/flow-go/crypto" 16 "github.com/onflow/flow-go/crypto/hash" 17 "github.com/koko1123/flow-go-1/module/signature" 18 "github.com/koko1123/flow-go-1/utils/unittest" 19 ) 20 21 func TestRandomBeaconInspector(t *testing.T) { 22 suite.Run(t, new(randomBeaconSuite)) 23 } 24 25 type randomBeaconSuite struct { 26 suite.Suite 27 n int 28 threshold int 29 kmac hash.Hasher 30 signers []int 31 thresholdSignatureMessage []byte 32 skShares []crypto.PrivateKey 33 pkShares []crypto.PublicKey 34 pkGroup crypto.PublicKey 35 } 36 37 func (rs *randomBeaconSuite) SetupTest() { 38 rs.n = 10 39 rs.threshold = signature.RandomBeaconThreshold(rs.n) 40 41 // generate threshold keys 42 mrand.Seed(time.Now().UnixNano()) 43 seed := make([]byte, crypto.SeedMinLenDKG) 44 _, err := mrand.Read(seed) 45 require.NoError(rs.T(), err) 46 rs.skShares, rs.pkShares, rs.pkGroup, err = crypto.BLSThresholdKeyGen(rs.n, rs.threshold, seed) 47 require.NoError(rs.T(), err) 48 49 // generate signature shares 50 rs.signers = make([]int, 0, rs.n) 51 52 // hasher 53 rs.kmac = signature.NewBLSHasher(signature.RandomBeaconTag) 54 rs.thresholdSignatureMessage = []byte("random_message") 55 56 // fill the signers list and shuffle it 57 for i := 0; i < rs.n; i++ { 58 rs.signers = append(rs.signers, i) 59 } 60 mrand.Shuffle(rs.n, func(i, j int) { 61 rs.signers[i], rs.signers[j] = rs.signers[j], rs.signers[i] 62 }) 63 } 64 65 func (rs *randomBeaconSuite) TestHappyPath() { 66 follower, err := NewRandomBeaconInspector(rs.pkGroup, rs.pkShares, rs.threshold, rs.thresholdSignatureMessage) 67 require.NoError(rs.T(), err) 68 69 // check EnoughShares 70 enough := follower.EnoughShares() 71 assert.False(rs.T(), enough) 72 var wg sync.WaitGroup 73 // create (t) signatures of the first randomly chosen signers 74 // (1 signature short of the threshold) 75 for j := 0; j < rs.threshold; j++ { 76 wg.Add(1) 77 // test thread safety 78 go func(j int) { 79 defer wg.Done() 80 i := rs.signers[j] 81 share, err := rs.skShares[i].Sign(rs.thresholdSignatureMessage, rs.kmac) 82 require.NoError(rs.T(), err) 83 // Verify 84 err = follower.Verify(i, share) 85 assert.NoError(rs.T(), err) 86 // TrustedAdd 87 enough, err := follower.TrustedAdd(i, share) 88 assert.NoError(rs.T(), err) 89 assert.False(rs.T(), enough) 90 // check EnoughSignature 91 assert.False(rs.T(), follower.EnoughShares(), "threshold shouldn't be reached") 92 }(j) 93 } 94 wg.Wait() 95 // add the last required signature to get (t+1) shares 96 i := rs.signers[rs.threshold] 97 share, err := rs.skShares[i].Sign(rs.thresholdSignatureMessage, rs.kmac) 98 require.NoError(rs.T(), err) 99 err = follower.Verify(i, share) 100 assert.NoError(rs.T(), err) 101 enough, err = follower.TrustedAdd(i, share) 102 assert.NoError(rs.T(), err) 103 assert.True(rs.T(), enough) 104 // check EnoughSignature 105 assert.True(rs.T(), follower.EnoughShares()) 106 107 // add a share when threshold is reached 108 if rs.threshold+1 < rs.n { 109 i := rs.signers[rs.threshold+1] 110 share, err := rs.skShares[i].Sign(rs.thresholdSignatureMessage, rs.kmac) 111 require.NoError(rs.T(), err) 112 // Trusted Add 113 enough, err := follower.TrustedAdd(i, share) 114 assert.NoError(rs.T(), err) 115 assert.True(rs.T(), enough) 116 } 117 // reconstruct the threshold signature 118 thresholdsignature, err := follower.Reconstruct() 119 require.NoError(rs.T(), err) 120 // VerifyThresholdSignature 121 verif, err := rs.pkGroup.Verify(thresholdsignature, rs.thresholdSignatureMessage, rs.kmac) 122 require.NoError(rs.T(), err) 123 assert.True(rs.T(), verif) 124 } 125 126 func (rs *randomBeaconSuite) TestDuplicateSigner() { 127 follower, err := NewRandomBeaconInspector(rs.pkGroup, rs.pkShares, rs.threshold, rs.thresholdSignatureMessage) 128 require.NoError(rs.T(), err) 129 130 // Create a share and add it 131 i := 0 132 share, err := rs.skShares[i].Sign(rs.thresholdSignatureMessage, rs.kmac) 133 require.NoError(rs.T(), err) 134 enough, err := follower.TrustedAdd(i, share) 135 assert.NoError(rs.T(), err) 136 assert.False(rs.T(), enough) 137 138 // Add an existing share 139 // TrustedAdd 140 enough, err = follower.TrustedAdd(i, share) 141 assert.Error(rs.T(), err) 142 assert.True(rs.T(), model.IsDuplicatedSignerError(err)) 143 assert.False(rs.T(), enough) 144 } 145 146 func (rs *randomBeaconSuite) TestInvalidSignerIndex() { 147 follower, err := NewRandomBeaconInspector(rs.pkGroup, rs.pkShares, rs.threshold, rs.thresholdSignatureMessage) 148 require.NoError(rs.T(), err) 149 150 share, err := rs.skShares[0].Sign(rs.thresholdSignatureMessage, rs.kmac) 151 require.NoError(rs.T(), err) 152 // invalid index 153 for _, invalidIndex := range []int{len(rs.pkShares) + 1, -1} { 154 // Verify 155 err = follower.Verify(invalidIndex, share) 156 assert.Error(rs.T(), err) 157 assert.True(rs.T(), model.IsInvalidSignerError(err)) 158 // TrustedAdd 159 enough, err := follower.TrustedAdd(invalidIndex, share) 160 assert.Error(rs.T(), err) 161 assert.True(rs.T(), model.IsInvalidSignerError(err)) 162 assert.False(rs.T(), enough) 163 } 164 } 165 166 func (rs *randomBeaconSuite) TestInvalidSignature() { 167 follower, err := NewRandomBeaconInspector(rs.pkGroup, rs.pkShares, rs.threshold, rs.thresholdSignatureMessage) 168 require.NoError(rs.T(), err) 169 index := mrand.Intn(rs.n) // random signer 170 share, err := rs.skShares[index].Sign(rs.thresholdSignatureMessage, rs.kmac) 171 require.NoError(rs.T(), err) 172 173 // alter signature - signature is rs.not a valid point 174 share[4] ^= 1 175 // Verify 176 err = follower.Verify(index, share) 177 assert.Error(rs.T(), err) 178 assert.True(rs.T(), errors.Is(err, model.ErrInvalidSignature)) 179 // restore share 180 share[4] ^= 1 181 182 // valid curve point but invalid signature 183 otherIndex := (index + 1) % len(rs.pkShares) // otherIndex is different than index 184 // VerifyShare 185 err = follower.Verify(otherIndex, share) 186 assert.Error(rs.T(), err) 187 assert.True(rs.T(), errors.Is(err, model.ErrInvalidSignature)) 188 } 189 190 func (rs *randomBeaconSuite) TestConstructorErrors() { 191 // too few key shares 192 pkSharesInvalid := make([]crypto.PublicKey, crypto.ThresholdSignMinSize-1) 193 i, err := NewRandomBeaconInspector(rs.pkGroup, pkSharesInvalid, rs.threshold, rs.thresholdSignatureMessage) 194 assert.True(rs.T(), model.IsConfigurationError(err)) 195 assert.Nil(rs.T(), i) 196 197 // too many key shares 198 pkSharesInvalid = make([]crypto.PublicKey, crypto.ThresholdSignMaxSize+1) 199 i, err = NewRandomBeaconInspector(rs.pkGroup, pkSharesInvalid, rs.threshold, rs.thresholdSignatureMessage) 200 assert.True(rs.T(), model.IsConfigurationError(err)) 201 assert.Nil(rs.T(), i) 202 203 // threshold too large 204 i, err = NewRandomBeaconInspector(rs.pkGroup, rs.pkShares, len(rs.pkShares), rs.thresholdSignatureMessage) 205 assert.True(rs.T(), model.IsConfigurationError(err)) 206 assert.Nil(rs.T(), i) 207 208 // threshold negative 209 i, err = NewRandomBeaconInspector(rs.pkGroup, rs.pkShares, 0, rs.thresholdSignatureMessage) 210 assert.True(rs.T(), model.IsConfigurationError(err)) 211 assert.Nil(rs.T(), i) 212 213 // included non-BLS key in public key shares 214 pkSharesInvalid = append(([]crypto.PublicKey)(nil), rs.pkShares...) // copy 215 pkSharesInvalid[len(pkSharesInvalid)-1] = unittest.KeyFixture(crypto.ECDSAP256).PublicKey() 216 i, err = NewRandomBeaconInspector(rs.pkGroup, pkSharesInvalid, rs.threshold, rs.thresholdSignatureMessage) 217 assert.True(rs.T(), model.IsConfigurationError(err)) 218 assert.Nil(rs.T(), i) 219 }