From 3e7f7d2b8ce6c4e6fc3bf740bbab5b5cfbd15977 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 2 Dec 2022 12:30:36 +0000 Subject: [PATCH 1/5] cp --- x/ccv/provider/keeper/key_assignment_test.go | 21 +++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/x/ccv/provider/keeper/key_assignment_test.go b/x/ccv/provider/keeper/key_assignment_test.go index 89308c13e8..16def996af 100644 --- a/x/ccv/provider/keeper/key_assignment_test.go +++ b/x/ccv/provider/keeper/key_assignment_test.go @@ -766,6 +766,10 @@ func TestSimulatedAssignmentsAndUpdateApplication(t *testing.T) { providerValset := CreateValSet(providerIdentities) // NOTE: consumer must have space for provider identities because default key assignments are to provider keys consumerValset := CreateValSet(append(providerIdentities, consumerIdentities...)) + // VSCID -> Set(string(sdk.ConsAddress)) - Used to check that slash lookups are correct + // (history is needed for consumer initiated double sign slashes) + historicConsumerValidators := map[int]map[string]struct{}{} + _ = historicConsumerValidators // Mock calls to GetLastValidatorPower to return directly from the providerValset mocks.MockStakingKeeper.EXPECT().GetLastValidatorPower( @@ -790,6 +794,7 @@ func TestSimulatedAssignmentsAndUpdateApplication(t *testing.T) { updates, err := k.ApplyKeyAssignmentToValUpdates(ctx, CHAINID, updates) require.NoError(t, err) consumerValset.apply(updates) + } // Helper: apply some key assignment transactions to the system @@ -825,13 +830,13 @@ func TestSimulatedAssignmentsAndUpdateApplication(t *testing.T) { // Randomly fast forward the greatest pruned VSCID. This simulates // delivery of maturity packets from the consumer chain. - prunedVscid := greatestPrunedVSCID + rand.Intn(int(k.GetValidatorSetUpdateId(ctx))+1) + prunedVscid := greatestPrunedVSCID + rand.Intn(int(k.GetValidatorSetUpdateId(ctx))+1-greatestPrunedVSCID) k.PruneKeyAssignments(ctx, CHAINID, uint64(prunedVscid)) greatestPrunedVSCID = prunedVscid /* - Properties: Validator Set Replication + Property: Validator Set Replication Each validator set on the provider must be replicated on the consumer. The property in the real system is somewhat weaker, because the consumer chain can forward updates to tendermint in batches. @@ -876,10 +881,20 @@ func TestSimulatedAssignmentsAndUpdateApplication(t *testing.T) { } } - // Check that all keys have been or will eventually be pruned. + /* + Property: Pruning (bounded storage) + Check that all keys have been or will eventually be pruned. + */ require.True(t, checkCorrectPruningProperty(ctx, k, CHAINID)) + /* + Property: Correct Consumer Initiated Slash Lookup + Each consumer validator that is present in a validator set with vscid VSCID + and greatestPrunedVSCID < VSCID maps to a unique provider validator. + (TODO: strengthen) + */ + } ctrl.Finish() } From b92e2dbbbc9f9a34652d48e198352eea1f965fa5 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 2 Dec 2022 13:09:14 +0000 Subject: [PATCH 2/5] wip --- x/ccv/provider/keeper/key_assignment_test.go | 47 ++++++++++++++++++-- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/x/ccv/provider/keeper/key_assignment_test.go b/x/ccv/provider/keeper/key_assignment_test.go index 16def996af..bd0977e1ca 100644 --- a/x/ccv/provider/keeper/key_assignment_test.go +++ b/x/ccv/provider/keeper/key_assignment_test.go @@ -768,8 +768,10 @@ func TestSimulatedAssignmentsAndUpdateApplication(t *testing.T) { consumerValset := CreateValSet(append(providerIdentities, consumerIdentities...)) // VSCID -> Set(string(sdk.ConsAddress)) - Used to check that slash lookups are correct // (history is needed for consumer initiated double sign slashes) - historicConsumerValidators := map[int]map[string]struct{}{} - _ = historicConsumerValidators + historicConsumerValidators := map[uint64]map[string]struct{}{} + + // Sanity check that the validator set update is initialised to 0, for clarity. + require.Equal(t, k.GetValidatorSetUpdateId(ctx), uint64(0)) // Mock calls to GetLastValidatorPower to return directly from the providerValset mocks.MockStakingKeeper.EXPECT().GetLastValidatorPower( @@ -789,12 +791,26 @@ func TestSimulatedAssignmentsAndUpdateApplication(t *testing.T) { }).AnyTimes() // Helper: apply some updates to both the provider and consumer valsets + // and increment the provider vscid. applyUpdates := func(updates []abci.ValidatorUpdate) { providerValset.apply(updates) updates, err := k.ApplyKeyAssignmentToValUpdates(ctx, CHAINID, updates) require.NoError(t, err) consumerValset.apply(updates) + // Record the active validator set + vscid := k.GetValidatorSetUpdateId(ctx) + historicConsumerValidators[vscid] = map[string]struct{}{} + for i, id := range consumerValset.identities { + cons := string(id.SDKConsAddress()) + if 0 < consumerValset.power[i] { + // 0 < power because we want active validators only + historicConsumerValidators[vscid][cons] = struct{}{} + } + } + + // Simulate the VSCID update in EndBlock + k.IncrementValidatorSetUpdateId(ctx) } // Helper: apply some key assignment transactions to the system @@ -830,7 +846,7 @@ func TestSimulatedAssignmentsAndUpdateApplication(t *testing.T) { // Randomly fast forward the greatest pruned VSCID. This simulates // delivery of maturity packets from the consumer chain. - prunedVscid := greatestPrunedVSCID + rand.Intn(int(k.GetValidatorSetUpdateId(ctx))+1-greatestPrunedVSCID) + prunedVscid := greatestPrunedVSCID + rand.Intn(int(k.GetValidatorSetUpdateId(ctx))-greatestPrunedVSCID) k.PruneKeyAssignments(ctx, CHAINID, uint64(prunedVscid)) greatestPrunedVSCID = prunedVscid @@ -890,10 +906,35 @@ func TestSimulatedAssignmentsAndUpdateApplication(t *testing.T) { /* Property: Correct Consumer Initiated Slash Lookup + For all cryptographic identities known to the consumer at a vscid VSCID + with greatestPrunedVSCID < VSCID: the identity Each consumer validator that is present in a validator set with vscid VSCID and greatestPrunedVSCID < VSCID maps to a unique provider validator. (TODO: strengthen) + + */ + for _, id := range consumerIdentities { + consC := string(id.SDKConsAddress()) + first := true + var consP string + + // For each validator set that was not yet matured (pruned) by the consumer + for vscid := uint64(greatestPrunedVSCID + 1); vscid < k.GetValidatorSetUpdateId(ctx); vscid++ { + // Check if the identity was included in the set + if _, ok := historicConsumerValidators[vscid][consC]; ok { + // If it was included in the set + // Check that that the provider consensus address queried is always the same + consPFromSlashQuery := string(k.GetProviderAddrFromConsumerAddr(ctx, CHAINID, id.SDKConsAddress())) + if first { + first = false + consP = consPFromSlashQuery + continue + } + require.Equal(t, consP, consPFromSlashQuery) + } + } + } } ctrl.Finish() From 72932ab89ca750941db39a4599acb3e558d1898d Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 2 Dec 2022 13:29:40 +0000 Subject: [PATCH 3/5] note --- x/ccv/provider/keeper/key_assignment_test.go | 25 +++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/x/ccv/provider/keeper/key_assignment_test.go b/x/ccv/provider/keeper/key_assignment_test.go index bd0977e1ca..6d18002a07 100644 --- a/x/ccv/provider/keeper/key_assignment_test.go +++ b/x/ccv/provider/keeper/key_assignment_test.go @@ -676,6 +676,15 @@ func (vs *ValSet) apply(updates []abci.ValidatorUpdate) { } } +type SlashPropertyHelper struct { + // TODO: + // string(sdk.ConsAddress) -> vscid -> was active consumer validator + active map[string]map[uint64]bool + // TODO: + // string(sdk.ConsAddress) -> vscid -> string(sdk.ConsAddress) + provider map[string]map[uint64]string +} + // A key assignment action to be done type Assignment struct { val stakingtypes.Validator @@ -846,7 +855,9 @@ func TestSimulatedAssignmentsAndUpdateApplication(t *testing.T) { // Randomly fast forward the greatest pruned VSCID. This simulates // delivery of maturity packets from the consumer chain. - prunedVscid := greatestPrunedVSCID + rand.Intn(int(k.GetValidatorSetUpdateId(ctx))-greatestPrunedVSCID) + prunedVscid := greatestPrunedVSCID + + // +1 and -1 because id was incremented (-1), (+1) to make upper bound inclusive + rand.Intn(int(k.GetValidatorSetUpdateId(ctx))+1-1-greatestPrunedVSCID) k.PruneKeyAssignments(ctx, CHAINID, uint64(prunedVscid)) greatestPrunedVSCID = prunedVscid @@ -879,6 +890,10 @@ func TestSimulatedAssignmentsAndUpdateApplication(t *testing.T) { require.Equal(t, providerValset.power[i], consumerValset.power[j]) } } + + // Know that consC -> idP at vscid k.GetValidatorSetUpdate()-1 + // Check that for greatestPrunedVSCID < vscid: + // idP unique and equal to k.GetProviderAddrFromConsumerAddr } } // Check validator set replication backward direction @@ -912,6 +927,14 @@ func TestSimulatedAssignmentsAndUpdateApplication(t *testing.T) { and greatestPrunedVSCID < VSCID maps to a unique provider validator. (TODO: strengthen) + Each block , record the reverse map for each consumer id + consumer id -> vscid -> provider id + + For all VSCID i st greatestPrunedVSCID < i: + + + For all consumers vals active in any VSCID st greatestPrunedVSCID < VSCID + */ for _, id := range consumerIdentities { From 04cd5d99e78de94ae4e3ed777d77121e361861ef Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 2 Dec 2022 13:45:44 +0000 Subject: [PATCH 4/5] cp --- x/ccv/provider/keeper/key_assignment_test.go | 86 +++++--------------- 1 file changed, 21 insertions(+), 65 deletions(-) diff --git a/x/ccv/provider/keeper/key_assignment_test.go b/x/ccv/provider/keeper/key_assignment_test.go index 6d18002a07..2f6b4fb6c8 100644 --- a/x/ccv/provider/keeper/key_assignment_test.go +++ b/x/ccv/provider/keeper/key_assignment_test.go @@ -676,15 +676,6 @@ func (vs *ValSet) apply(updates []abci.ValidatorUpdate) { } } -type SlashPropertyHelper struct { - // TODO: - // string(sdk.ConsAddress) -> vscid -> was active consumer validator - active map[string]map[uint64]bool - // TODO: - // string(sdk.ConsAddress) -> vscid -> string(sdk.ConsAddress) - provider map[string]map[uint64]string -} - // A key assignment action to be done type Assignment struct { val stakingtypes.Validator @@ -775,9 +766,12 @@ func TestSimulatedAssignmentsAndUpdateApplication(t *testing.T) { providerValset := CreateValSet(providerIdentities) // NOTE: consumer must have space for provider identities because default key assignments are to provider keys consumerValset := CreateValSet(append(providerIdentities, consumerIdentities...)) - // VSCID -> Set(string(sdk.ConsAddress)) - Used to check that slash lookups are correct - // (history is needed for consumer initiated double sign slashes) - historicConsumerValidators := map[uint64]map[string]struct{}{} + // TODO: + // maps consumer cons addr -> vscid -> reverse lookup provider addr + historicActiveConsumerValSlashLookups := map[string]map[uint64]string{} + for _, id := range append(providerIdentities, consumerIdentities...) { + historicActiveConsumerValSlashLookups[string(id.SDKConsAddress())] = map[uint64]string{} + } // Sanity check that the validator set update is initialised to 0, for clarity. require.Equal(t, k.GetValidatorSetUpdateId(ctx), uint64(0)) @@ -801,23 +795,11 @@ func TestSimulatedAssignmentsAndUpdateApplication(t *testing.T) { // Helper: apply some updates to both the provider and consumer valsets // and increment the provider vscid. - applyUpdates := func(updates []abci.ValidatorUpdate) { + applyUpdatesAndIncrementVSCID := func(updates []abci.ValidatorUpdate) { providerValset.apply(updates) updates, err := k.ApplyKeyAssignmentToValUpdates(ctx, CHAINID, updates) require.NoError(t, err) consumerValset.apply(updates) - - // Record the active validator set - vscid := k.GetValidatorSetUpdateId(ctx) - historicConsumerValidators[vscid] = map[string]struct{}{} - for i, id := range consumerValset.identities { - cons := string(id.SDKConsAddress()) - if 0 < consumerValset.power[i] { - // 0 < power because we want active validators only - historicConsumerValidators[vscid][cons] = struct{}{} - } - } - // Simulate the VSCID update in EndBlock k.IncrementValidatorSetUpdateId(ctx) } @@ -835,7 +817,7 @@ func TestSimulatedAssignmentsAndUpdateApplication(t *testing.T) { applyAssignments(getAssignments()) // And generate a random provider valset which, in the real system, will // be put into the consumer genesis. - applyUpdates(getStakingUpdates()) + applyUpdatesAndIncrementVSCID(getStakingUpdates()) // Register the consumer chain k.SetConsumerClientId(ctx, CHAINID, "") @@ -851,7 +833,7 @@ func TestSimulatedAssignmentsAndUpdateApplication(t *testing.T) { // Generate and apply assignments and power updates applyAssignments(getAssignments()) - applyUpdates(getStakingUpdates()) + applyUpdatesAndIncrementVSCID(getStakingUpdates()) // Randomly fast forward the greatest pruned VSCID. This simulates // delivery of maturity packets from the consumer chain. @@ -890,10 +872,6 @@ func TestSimulatedAssignmentsAndUpdateApplication(t *testing.T) { require.Equal(t, providerValset.power[i], consumerValset.power[j]) } } - - // Know that consC -> idP at vscid k.GetValidatorSetUpdate()-1 - // Check that for greatestPrunedVSCID < vscid: - // idP unique and equal to k.GetProviderAddrFromConsumerAddr } } // Check validator set replication backward direction @@ -907,8 +885,12 @@ func TestSimulatedAssignmentsAndUpdateApplication(t *testing.T) { for j, idP := range providerValset.identities { if idP.SDKConsAddress().Equals(consP) { require.Equal(t, providerValset.power[j], consumerValset.power[i]) + } } + + vscid := k.GetValidatorSetUpdateId(ctx) - 1 // -1 since it was incremented before + historicActiveConsumerValSlashLookups[string(consC)][vscid] = string(consP) } } @@ -921,44 +903,18 @@ func TestSimulatedAssignmentsAndUpdateApplication(t *testing.T) { /* Property: Correct Consumer Initiated Slash Lookup - For all cryptographic identities known to the consumer at a vscid VSCID - with greatestPrunedVSCID < VSCID: the identity - Each consumer validator that is present in a validator set with vscid VSCID - and greatestPrunedVSCID < VSCID maps to a unique provider validator. - (TODO: strengthen) - - Each block , record the reverse map for each consumer id - consumer id -> vscid -> provider id - - For all VSCID i st greatestPrunedVSCID < i: - - - For all consumers vals active in any VSCID st greatestPrunedVSCID < VSCID - - + Check that the reverse lookup is the same for all consumer ids, + for all vscid : greatestPrunedVSCID < vscid if consumer ever active */ - for _, id := range consumerIdentities { - consC := string(id.SDKConsAddress()) - first := true - var consP string - - // For each validator set that was not yet matured (pruned) by the consumer - for vscid := uint64(greatestPrunedVSCID + 1); vscid < k.GetValidatorSetUpdateId(ctx); vscid++ { - // Check if the identity was included in the set - if _, ok := historicConsumerValidators[vscid][consC]; ok { - // If it was included in the set - // Check that that the provider consensus address queried is always the same - consPFromSlashQuery := string(k.GetProviderAddrFromConsumerAddr(ctx, CHAINID, id.SDKConsAddress())) - if first { - first = false - consP = consPFromSlashQuery - continue - } - require.Equal(t, consP, consPFromSlashQuery) + for _, vscidToConsP := range historicActiveConsumerValSlashLookups { + seen := map[string]bool{} + for vscid, consP := range vscidToConsP { + if uint64(greatestPrunedVSCID) < vscid { + seen[consP] = true } } + require.True(t, len(seen) < 2) } - } ctrl.Finish() } From cb3164690cc17c1a6fa71ddf389460d7e0a2959b Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 2 Dec 2022 14:04:54 +0000 Subject: [PATCH 5/5] Adds slash test --- x/ccv/provider/keeper/key_assignment_test.go | 52 +++++++++++++++----- 1 file changed, 39 insertions(+), 13 deletions(-) diff --git a/x/ccv/provider/keeper/key_assignment_test.go b/x/ccv/provider/keeper/key_assignment_test.go index 2f6b4fb6c8..d427d697b5 100644 --- a/x/ccv/provider/keeper/key_assignment_test.go +++ b/x/ccv/provider/keeper/key_assignment_test.go @@ -766,12 +766,11 @@ func TestSimulatedAssignmentsAndUpdateApplication(t *testing.T) { providerValset := CreateValSet(providerIdentities) // NOTE: consumer must have space for provider identities because default key assignments are to provider keys consumerValset := CreateValSet(append(providerIdentities, consumerIdentities...)) - // TODO: - // maps consumer cons addr -> vscid -> reverse lookup provider addr - historicActiveConsumerValSlashLookups := map[string]map[uint64]string{} - for _, id := range append(providerIdentities, consumerIdentities...) { - historicActiveConsumerValSlashLookups[string(id.SDKConsAddress())] = map[uint64]string{} - } + // For each validator on the consumer, record the corresponding provider + // address as looked up on the provider using GetProviderAddrFromConsumerAddr + // at a given vscid. + // consumer consAddr -> vscid -> provider consAddr + historicSlashQueries := map[string]map[uint64]string{} // Sanity check that the validator set update is initialised to 0, for clarity. require.Equal(t, k.GetValidatorSetUpdateId(ctx), uint64(0)) @@ -869,6 +868,7 @@ func TestSimulatedAssignmentsAndUpdateApplication(t *testing.T) { // Find the corresponding consumer validator (must always be found) for j, idC := range consumerValset.identities { if consC.Equals(idC.SDKConsAddress()) { + // Ensure powers are the same require.Equal(t, providerValset.power[i], consumerValset.power[j]) } } @@ -884,13 +884,10 @@ func TestSimulatedAssignmentsAndUpdateApplication(t *testing.T) { // Find the corresponding provider validator (must always be found) for j, idP := range providerValset.identities { if idP.SDKConsAddress().Equals(consP) { + // Ensure powers are the same require.Equal(t, providerValset.power[j], consumerValset.power[i]) - } } - - vscid := k.GetValidatorSetUpdateId(ctx) - 1 // -1 since it was incremented before - historicActiveConsumerValSlashLookups[string(consC)][vscid] = string(consP) } } @@ -903,18 +900,47 @@ func TestSimulatedAssignmentsAndUpdateApplication(t *testing.T) { /* Property: Correct Consumer Initiated Slash Lookup - Check that the reverse lookup is the same for all consumer ids, - for all vscid : greatestPrunedVSCID < vscid if consumer ever active + + Check that since the last pruning, it has never been possible to query + two different provider addresses from the same consumer address. + We know that the queried provider address was correct at least once, + from checking the validator set replication property. These two facts + together guarantee that the slash lookup is always correct. */ - for _, vscidToConsP := range historicActiveConsumerValSlashLookups { + + // Build up the historicSlashQueries data structure + for i := range consumerValset.identities { + // For each active validator on the consumer chain + consC := consumerValset.identities[i].SDKConsAddress() + if 0 < consumerValset.power[i] { + // Get the provider who assigned the key + consP := k.GetProviderAddrFromConsumerAddr(ctx, CHAINID, consC) + + if _, found := historicSlashQueries[string(consC)]; !found { + historicSlashQueries[string(consC)] = map[uint64]string{} + } + + vscid := k.GetValidatorSetUpdateId(ctx) - 1 // -1 since it was incremented before + // Record the slash query result obtained at this block + historicSlashQueries[string(consC)][vscid] = string(consP) + } + } + + // Check that, for each address known the consumer at some block + // with vscid st. greatestPrunedVSCID < vscid, there were never + // conflicting slash query results. + for _, vscidToConsP := range historicSlashQueries { seen := map[string]bool{} for vscid, consP := range vscidToConsP { if uint64(greatestPrunedVSCID) < vscid { + // The provider would have returned seen[consP] = true } } + // No conflicts. require.True(t, len(seen) < 2) } + } ctrl.Finish() }