package certifiers_test import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/certifiers" "github.com/tendermint/tendermint/certifiers/errors" ) // TestDynamicCert just makes sure it still works like StaticCert func TestDynamicCert(t *testing.T) { // assert, require := assert.New(t), require.New(t) assert := assert.New(t) // require := require.New(t) keys := certifiers.GenValKeys(4) // 20, 30, 40, 50 - the first 3 don't have 2/3, the last 3 do! vals := keys.ToValidators(20, 10) // and a certifier based on our known set chainID := "test-dyno" cert := certifiers.NewDynamic(chainID, vals, 0) cases := []struct { keys certifiers.ValKeys vals *types.ValidatorSet height int first, last int // who actually signs proper bool // true -> expect no error changed bool // true -> expect validator change error }{ // perfect, signed by everyone {keys, vals, 1, 0, len(keys), true, false}, // skip little guy is okay {keys, vals, 2, 1, len(keys), true, false}, // but not the big guy {keys, vals, 3, 0, len(keys) - 1, false, false}, // even changing the power a little bit breaks the static validator // the sigs are enough, but the validator hash is unknown {keys, keys.ToValidators(20, 11), 4, 0, len(keys), false, true}, } for _, tc := range cases { check := tc.keys.GenCommit(chainID, tc.height, nil, tc.vals, []byte("bar"), tc.first, tc.last) err := cert.Certify(check) if tc.proper { assert.Nil(err, "%+v", err) assert.Equal(cert.LastHeight(), tc.height) } else { assert.NotNil(err) if tc.changed { assert.True(errors.IsValidatorsChangedErr(err), "%+v", err) } } } } // TestDynamicUpdate makes sure we update safely and sanely func TestDynamicUpdate(t *testing.T) { assert, require := assert.New(t), require.New(t) chainID := "test-dyno-up" keys := certifiers.GenValKeys(5) vals := keys.ToValidators(20, 0) cert := certifiers.NewDynamic(chainID, vals, 40) // one valid block to give us a sense of time h := 100 good := keys.GenCommit(chainID, h, nil, vals, []byte("foo"), 0, len(keys)) err := cert.Certify(good) require.Nil(err, "%+v", err) // some new sets to try later keys2 := keys.Extend(2) keys3 := keys2.Extend(4) // we try to update with some blocks cases := []struct { keys certifiers.ValKeys vals *types.ValidatorSet height int first, last int // who actually signs proper bool // true -> expect no error changed bool // true -> expect too much change error }{ // same validator set, well signed, of course it is okay {keys, vals, h + 10, 0, len(keys), true, false}, // same validator set, poorly signed, fails {keys, vals, h + 20, 2, len(keys), false, false}, // shift the power a little, works if properly signed {keys, keys.ToValidators(10, 0), h + 30, 1, len(keys), true, false}, // but not on a poor signature {keys, keys.ToValidators(10, 0), h + 40, 2, len(keys), false, false}, // and not if it was in the past {keys, keys.ToValidators(10, 0), h + 25, 0, len(keys), false, false}, // let's try to adjust to a whole new validator set (we have 5/7 of the votes) {keys2, keys2.ToValidators(10, 0), h + 33, 0, len(keys2), true, false}, // properly signed but too much change, not allowed (only 7/11 validators known) {keys3, keys3.ToValidators(10, 0), h + 50, 0, len(keys3), false, true}, } for _, tc := range cases { fc := tc.keys.GenFullCommit(chainID, tc.height, nil, tc.vals, []byte("bar"), tc.first, tc.last) err := cert.Update(fc) if tc.proper { assert.Nil(err, "%d: %+v", tc.height, err) // we update last seen height assert.Equal(cert.LastHeight(), tc.height) // and we update the proper validators assert.EqualValues(fc.Header.ValidatorsHash, cert.Hash()) } else { assert.NotNil(err, "%d", tc.height) // we don't update the height assert.NotEqual(cert.LastHeight(), tc.height) if tc.changed { assert.True(errors.IsTooMuchChangeErr(err), "%d: %+v", tc.height, err) } } } }