From 10bbf568b560f9697066382bd2f4730ba8a3f458 Mon Sep 17 00:00:00 2001 From: Philip Offtermatt Date: Thu, 8 Feb 2024 11:23:57 +0100 Subject: [PATCH 1/2] Port key assignment to MBT driver --- tests/mbt/driver/core.go | 15 ++- tests/mbt/driver/generate_more_traces.sh | 3 +- tests/mbt/driver/generate_traces.sh | 3 +- tests/mbt/driver/mbt_test.go | 125 ++++++++++++++++++----- tests/mbt/driver/stats.go | 2 + 5 files changed, 118 insertions(+), 30 deletions(-) diff --git a/tests/mbt/driver/core.go b/tests/mbt/driver/core.go index f9f1e12e05..b9a4293df1 100644 --- a/tests/mbt/driver/core.go +++ b/tests/mbt/driver/core.go @@ -20,6 +20,7 @@ import ( abcitypes "github.com/cometbft/cometbft/abci/types" cmttypes "github.com/cometbft/cometbft/types" + "github.com/cometbft/cometbft/proto/tendermint/crypto" appConsumer "github.com/cosmos/interchain-security/v4/app/consumer" appProvider "github.com/cosmos/interchain-security/v4/app/provider" simibc "github.com/cosmos/interchain-security/v4/testutil/simibc" @@ -123,9 +124,13 @@ func (s *Driver) consumerPower(i int64, chain ChainId) (int64, error) { return v.Power, nil } +func (s *Driver) stakingValidator(i int64) (stakingtypes.Validator, bool) { + return s.providerStakingKeeper().GetValidator(s.ctx(PROVIDER), s.validator(i)) +} + // providerPower returns the power(=number of bonded tokens) of the i-th validator on the provider. func (s *Driver) providerPower(i int64) (int64, error) { - v, found := s.providerStakingKeeper().GetValidator(s.ctx(PROVIDER), s.validator(i)) + v, found := s.stakingValidator(i) if !found { return 0, fmt.Errorf("validator with id %v not found on provider", i) } else { @@ -370,6 +375,14 @@ func (s *Driver) setTime(chain ChainId, newTime time.Time) { testChain.App.BeginBlock(abcitypes.RequestBeginBlock{Header: testChain.CurrentHeader}) } +func (s *Driver) AssignKey(chain ChainId, valIndex int64, value crypto.PublicKey) error { + stakingVal, found := s.stakingValidator(valIndex) + if !found { + return fmt.Errorf("validator with id %v not found on provider", valIndex) + } + return s.providerKeeper().AssignConsumerKey(s.providerCtx(), string(chain), stakingVal, value) +} + // DeliverPacketToConsumer delivers a packet from the provider to the given consumer recipient. // It updates the client before delivering the packet. // Since the channel is ordered, the packet that is delivered is the first packet in the outbox. diff --git a/tests/mbt/driver/generate_more_traces.sh b/tests/mbt/driver/generate_more_traces.sh index 9af4da82e9..40589bb83b 100755 --- a/tests/mbt/driver/generate_more_traces.sh +++ b/tests/mbt/driver/generate_more_traces.sh @@ -9,4 +9,5 @@ go run ./... -modelPath=../model/ccv_boundeddrift.qnt -step stepBoundedDrift -in echo "Generating synced traces with maturations" go run ./... -modelPath=../model/ccv_sync.qnt -init initSync -step stepSync -invariant CanReceiveMaturations -traceFolder traces/sync_mat -numTraces 20 -numSteps 300 -numSamples 20 echo "Generating long synced traces without invariants" -go run ./... -modelPath=../model/ccv_sync.qnt -init initSync -step stepSync -traceFolder traces/sync_noinv -numTraces 20 -numSteps 500 -numSamples 1 \ No newline at end of file +go run ./... -modelPath=../model/ccv_sync.qnt -init initSync -step stepSync -traceFolder traces/sync_noinv -numTraces 20 -numSteps 500 -numSamples 1 +go run ./... -modelPath=../model/ccv_boundeddrift.qnt --step stepBoundedDriftKeyAssignment --traceFolder traces/bound_key -numTraces 20 -numSteps 100 -numSamples 20 \ No newline at end of file diff --git a/tests/mbt/driver/generate_traces.sh b/tests/mbt/driver/generate_traces.sh index ca0a6ba973..9f1134fb26 100755 --- a/tests/mbt/driver/generate_traces.sh +++ b/tests/mbt/driver/generate_traces.sh @@ -9,4 +9,5 @@ go run ./... -modelPath=../model/ccv_boundeddrift.qnt -step stepBoundedDrift -in echo "Generating synced traces with maturations" go run ./... -modelPath=../model/ccv_sync.qnt -init initSync -step stepSync -invariant CanReceiveMaturations -traceFolder traces/sync_mat -numTraces 1 -numSteps 300 -numSamples 20 echo "Generating long synced traces without invariants" -go run ./... -modelPath=../model/ccv_sync.qnt -init initSync -step stepSync -traceFolder traces/sync_noinv -numTraces 1 -numSteps 500 -numSamples 1 \ No newline at end of file +go run ./... -modelPath=../model/ccv_sync.qnt -init initSync -step stepSync -traceFolder traces/sync_noinv -numTraces 1 -numSteps 500 -numSamples 1 +go run ./... -modelPath=../model/ccv_boundeddrift.qnt --step stepBoundedDriftKeyAssignment --traceFolder traces/bound_key -numTraces 1 -numSteps 100 -numSamples 20 \ No newline at end of file diff --git a/tests/mbt/driver/mbt_test.go b/tests/mbt/driver/mbt_test.go index 78f9e7910f..a55d870dda 100644 --- a/tests/mbt/driver/mbt_test.go +++ b/tests/mbt/driver/mbt_test.go @@ -15,10 +15,13 @@ import ( "github.com/kylelemons/godebug/pretty" "github.com/stretchr/testify/require" - sdktypes "github.com/cosmos/cosmos-sdk/types" - cmttypes "github.com/cometbft/cometbft/types" + tmencoding "github.com/cometbft/cometbft/crypto/encoding" + "github.com/cosmos/interchain-security/v4/testutil/integration" + + sdktypes "github.com/cosmos/cosmos-sdk/types" + providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" ) @@ -69,6 +72,7 @@ func TestMBT(t *testing.T) { t.Logf("Number of sent packets: %v", stats.numSentPackets) t.Logf("Number of blocks: %v", stats.numBlocks) t.Logf("Number of transactions: %v", stats.numTxs) + t.Logf("Number of key assignments: %v", stats.numKeyAssignments) t.Logf("Average summed block time delta passed per trace: %v", stats.totalBlockTimePassedPerTrace/time.Duration(numTraces)) } @@ -117,6 +121,21 @@ func RunItfTrace(t *testing.T, path string) { t.Log("Chains are: ", chains) + // generate keys that can be assigned on consumers, according to the ConsumerAddresses in the trace + consumerAddressesExpr := params["ConsumerAddresses"].Value.(itf.ListExprType) + + _, _, consumerPrivVals, err := integration.CreateValidators(len(consumerAddressesExpr)) + require.NoError(t, err, "Error creating consumer signers") + + consumerAddrNamesToPrivVals := make(map[string]cmttypes.PrivValidator, len(consumerAddressesExpr)) + realAddrsToModelConsAddrs := make(map[string]string, len(consumerAddressesExpr)) + i := 0 + for address, privVal := range consumerPrivVals { + consumerAddrNamesToPrivVals[consumerAddressesExpr[i].Value.(string)] = privVal + realAddrsToModelConsAddrs[address] = consumerAddressesExpr[i].Value.(string) + i++ + } + // create params struct vscTimeout := time.Duration(params["VscTimeout"].Value.(int64)) * time.Second @@ -145,6 +164,15 @@ func RunItfTrace(t *testing.T, path string) { valSet, addressMap, signers, err := CreateValSet(initialValSet) require.NoError(t, err, "Error creating validator set") + // get the set of signers for consumers: the validator signers, plus signers for the assignable addresses + consumerSigners := make(map[string]cmttypes.PrivValidator, 0) + for consAddr, consPrivVal := range consumerPrivVals { + consumerSigners[consAddr] = consPrivVal + } + for consAddr, signer := range signers { + consumerSigners[consAddr] = signer + } + // get a slice of validators in the right order nodes := make([]*cmttypes.Validator, len(valNames)) for i, valName := range valNames { @@ -211,6 +239,10 @@ func RunItfTrace(t *testing.T, path string) { // and then increment the rest of the time runningConsumersBefore := driver.runningConsumers() driver.endAndBeginBlock("provider", 1*time.Nanosecond) + for _, consumer := range driver.runningConsumers() { + UpdateProviderClientOnConsumer(t, driver, consumer.ChainId) + } + driver.endAndBeginBlock("provider", time.Duration(timeAdvancement)*time.Second-1*time.Nanosecond) runningConsumersAfter := driver.runningConsumers() @@ -243,7 +275,7 @@ func RunItfTrace(t *testing.T, path string) { consumer.Value.(string), modelParams, driver.providerChain().Vals, - signers, + consumerSigners, nodes, valNames, driver.providerChain(), @@ -268,11 +300,8 @@ func RunItfTrace(t *testing.T, path string) { if len(consumersToStart) > 0 && consumer.ChainId == consumersToStart[len(consumersToStart)-1].Value.(string) { continue } - consumerChainId := consumer.ChainId - driver.path(ChainId(consumerChainId)).AddClientHeader(PROVIDER, driver.providerHeader()) - err := driver.path(ChainId(consumerChainId)).UpdateClient(consumerChainId, false) - require.True(t, err == nil, "Error updating client from %v on provider: %v", consumerChainId, err) + UpdateProviderClientOnConsumer(t, driver, consumer.ChainId) } case "EndAndBeginBlockForConsumer": @@ -286,13 +315,12 @@ func RunItfTrace(t *testing.T, path string) { _ = headerBefore driver.endAndBeginBlock(ChainId(consumerChain), 1*time.Nanosecond) + UpdateConsumerClientOnProvider(t, driver, consumerChain) + driver.endAndBeginBlock(ChainId(consumerChain), time.Duration(timeAdvancement)*time.Second-1*time.Nanosecond) // update the client on the provider - consumerHeader := driver.chain(ChainId(consumerChain)).LastHeader - driver.path(ChainId(consumerChain)).AddClientHeader(consumerChain, consumerHeader) - err := driver.path(ChainId(consumerChain)).UpdateClient(PROVIDER, false) - require.True(t, err == nil, "Error updating client from %v on provider: %v", consumerChain, err) + UpdateConsumerClientOnProvider(t, driver, consumerChain) case "DeliverVscPacket": consumerChain := lastAction["consumerChain"].Value.(string) @@ -328,8 +356,26 @@ func RunItfTrace(t *testing.T, path string) { expectError = false driver.DeliverPacketFromConsumer(ChainId(consumerChain), expectError) } - default: + case "KeyAssignment": + consumerChain := lastAction["consumerChain"].Value.(string) + node := lastAction["validator"].Value.(string) + consumerAddr := lastAction["consumerAddr"].Value.(string) + + t.Log("KeyAssignment", consumerChain, node, consumerAddr) + stats.numKeyAssignments++ + valIndex := getIndexOfString(node, valNames) + assignedPrivVal := consumerAddrNamesToPrivVals[consumerAddr] + assignedKey, err := assignedPrivVal.GetPubKey() + require.NoError(t, err, "Error getting pubkey") + + protoPubKey, err := tmencoding.PubKeyToProto(assignedKey) + require.NoError(t, err, "Error converting pubkey to proto") + + error := driver.AssignKey(ChainId(consumerChain), int64(valIndex), protoPubKey) + require.NoError(t, error, "Error assigning key") + + default: log.Fatalf("Error loading trace file %s, step %v: do not know action type %s", path, index, actionKind) } @@ -364,7 +410,7 @@ func RunItfTrace(t *testing.T, path string) { require.Equal(t, modelRunningConsumers, actualRunningConsumers, "Running consumers do not match") // check validator sets - provider current validator set should be the one from the staking keeper - CompareValidatorSets(t, driver, currentModelState, actualRunningConsumers) + CompareValidatorSets(t, driver, currentModelState, actualRunningConsumers, realAddrsToModelConsAddrs) // check times - sanity check that the block times match the ones from the model CompareTimes(driver, actualRunningConsumers, currentModelState, timeOffset) @@ -383,7 +429,27 @@ func RunItfTrace(t *testing.T, path string) { t.Log("🟢 Trace is ok!") } -func CompareValidatorSets(t *testing.T, driver *Driver, currentModelState map[string]itf.Expr, consumers []string) { +func UpdateProviderClientOnConsumer(t *testing.T, driver *Driver, consumerChainId string) { + driver.path(ChainId(consumerChainId)).AddClientHeader(PROVIDER, driver.providerHeader()) + err := driver.path(ChainId(consumerChainId)).UpdateClient(consumerChainId, false) + require.True(t, err == nil, "Error updating client from %v on provider: %v", consumerChainId, err) +} + +func UpdateConsumerClientOnProvider(t *testing.T, driver *Driver, consumerChain string) { + consumerHeader := driver.chain(ChainId(consumerChain)).LastHeader + driver.path(ChainId(consumerChain)).AddClientHeader(consumerChain, consumerHeader) + err := driver.path(ChainId(consumerChain)).UpdateClient(PROVIDER, false) + require.True(t, err == nil, "Error updating client from %v on provider: %v", consumerChain, err) +} + +func CompareValidatorSets( + t *testing.T, + driver *Driver, + currentModelState map[string]itf.Expr, + consumers []string, + // a map from real addresses to the names of those consumer addresses in the model + keyAddrsToModelConsAddrName map[string]string, +) { t.Helper() modelValSet := ValidatorSet(currentModelState, "provider") @@ -407,23 +473,28 @@ func CompareValidatorSets(t *testing.T, driver *Driver, currentModelState map[st pubkey, err := val.ConsPubKey() require.NoError(t, err, "Error getting pubkey") - consAddr := providertypes.NewConsumerConsAddress(sdktypes.ConsAddress(pubkey.Address().Bytes())) + consAddrModelName, ok := keyAddrsToModelConsAddrName[pubkey.Address().String()] + if ok { // the node has a key assigned, use the name of the consumer address in the model + consumerCurValSet[consAddrModelName] = val.Power + } else { // the node doesn't have a key assigned yet, get the validator moniker + consAddr := providertypes.NewConsumerConsAddress(sdktypes.ConsAddress(pubkey.Address().Bytes())) - // the consumer vals right now are CrossChainValidators, for which we don't know their mnemonic - // so we need to find the mnemonic of the consumer val now to enter it by name in the map + // the consumer vals right now are CrossChainValidators, for which we don't know their mnemonic + // so we need to find the mnemonic of the consumer val now to enter it by name in the map - // get the address on the provider that corresponds to the consumer address - providerConsAddr, found := driver.providerKeeper().GetValidatorByConsumerAddr(driver.providerCtx(), consumer, consAddr) - if !found { - providerConsAddr = providertypes.NewProviderConsAddress(consAddr.Address) - } + // get the address on the provider that corresponds to the consumer address + providerConsAddr, found := driver.providerKeeper().GetValidatorByConsumerAddr(driver.providerCtx(), consumer, consAddr) + if !found { + providerConsAddr = providertypes.NewProviderConsAddress(consAddr.Address) + } - // get the validator for that address on the provider - providerVal, found := driver.providerStakingKeeper().GetValidatorByConsAddr(driver.providerCtx(), providerConsAddr.Address) - require.True(t, found, "Error getting provider validator") + // get the validator for that address on the provider + providerVal, found := driver.providerStakingKeeper().GetValidatorByConsAddr(driver.providerCtx(), providerConsAddr.Address) + require.True(t, found, "Error getting provider validator") - // use the moniker of that validator - consumerCurValSet[providerVal.GetMoniker()] = val.Power + // use the moniker of that validator + consumerCurValSet[providerVal.GetMoniker()] = val.Power + } } require.NoError(t, CompareValSet(modelValSet, consumerCurValSet), "Validator sets do not match for consumer %v", consumer) } diff --git a/tests/mbt/driver/stats.go b/tests/mbt/driver/stats.go index 0d397571be..8b4c95a3dd 100644 --- a/tests/mbt/driver/stats.go +++ b/tests/mbt/driver/stats.go @@ -16,4 +16,6 @@ type Stats struct { numTxs int totalBlockTimePassedPerTrace time.Duration + + numKeyAssignments int } From f73f69725ba60c2fffec572f94ebf7b5c1d2c650 Mon Sep 17 00:00:00 2001 From: Philip Offtermatt Date: Thu, 8 Feb 2024 14:20:46 +0100 Subject: [PATCH 2/2] Add comment and make var names clearer --- tests/mbt/driver/core.go | 4 ++-- tests/mbt/driver/mbt_test.go | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/mbt/driver/core.go b/tests/mbt/driver/core.go index b9a4293df1..66413bafe9 100644 --- a/tests/mbt/driver/core.go +++ b/tests/mbt/driver/core.go @@ -375,12 +375,12 @@ func (s *Driver) setTime(chain ChainId, newTime time.Time) { testChain.App.BeginBlock(abcitypes.RequestBeginBlock{Header: testChain.CurrentHeader}) } -func (s *Driver) AssignKey(chain ChainId, valIndex int64, value crypto.PublicKey) error { +func (s *Driver) AssignKey(chain ChainId, valIndex int64, key crypto.PublicKey) error { stakingVal, found := s.stakingValidator(valIndex) if !found { return fmt.Errorf("validator with id %v not found on provider", valIndex) } - return s.providerKeeper().AssignConsumerKey(s.providerCtx(), string(chain), stakingVal, value) + return s.providerKeeper().AssignConsumerKey(s.providerCtx(), string(chain), stakingVal, key) } // DeliverPacketToConsumer delivers a packet from the provider to the given consumer recipient. diff --git a/tests/mbt/driver/mbt_test.go b/tests/mbt/driver/mbt_test.go index a55d870dda..e57aa00f10 100644 --- a/tests/mbt/driver/mbt_test.go +++ b/tests/mbt/driver/mbt_test.go @@ -127,6 +127,10 @@ func RunItfTrace(t *testing.T, path string) { _, _, consumerPrivVals, err := integration.CreateValidators(len(consumerAddressesExpr)) require.NoError(t, err, "Error creating consumer signers") + // consumerAddrNames are the human readable names of consumer addresses in the model + // "realAddrs" are the addresses of the consumer keys on chain + // these maps relate the consumerAddrNames to the priv validators (from which one can get the real address) + // and from the real ddresses to the consumerAddrNames to allow converting between the two easily consumerAddrNamesToPrivVals := make(map[string]cmttypes.PrivValidator, len(consumerAddressesExpr)) realAddrsToModelConsAddrs := make(map[string]string, len(consumerAddressesExpr)) i := 0