Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix StopConsumerChain not running in cachedContext #802

Merged
merged 3 commits into from
Mar 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion tests/integration/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -597,7 +597,7 @@ func (tr TestRun) addChainToRelayer(
keyName := "query"
rpcAddr := "http://" + queryNodeIP + ":26658"
grpcAddr := "tcp://" + queryNodeIP + ":9091"
wsAddr := "ws://" + queryNodeIP + ":26657/websocket"
wsAddr := "ws://" + queryNodeIP + ":26658/websocket"

chainConfig := fmt.Sprintf(hermesChainConfigTemplate,
grpcAddr,
Expand Down Expand Up @@ -693,6 +693,28 @@ type addIbcChannelAction struct {
order string
}

type startHermesAction struct {
}

func (tr TestRun) startHermes(
action startHermesAction,
verbose bool,
) {
// hermes start is running in detached mode
//#nosec G204 -- Bypass linter warning for spawning subprocess with cmd arguments.
cmd := exec.Command("docker", "exec", "-d", tr.containerConfig.instanceName, "hermes",
"start",
)

if err := cmd.Start(); err != nil {
log.Fatal(err)
}

if verbose {
fmt.Println("started Hermes")
}
}

func (tr TestRun) addIbcChannel(
action addIbcChannelAction,
verbose bool,
Expand Down
2 changes: 2 additions & 0 deletions tests/integration/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ func (tr *TestRun) runStep(step Step, verbose bool) {
tr.assignConsumerPubKey(action, verbose)
case slashThrottleDequeue:
tr.waitForSlashThrottleDequeue(action, verbose)
case startHermesAction:
tr.startHermes(action, verbose)
default:
log.Fatalf("unknown action in testRun %s: %#v", tr.name, action)
}
Expand Down
4 changes: 3 additions & 1 deletion tests/integration/steps.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ var happyPathSteps = concatSteps(
stepsRejectEquivocationProposal("consu", 2), // prop to tombstone bob is rejected
stepsDoubleSignOnProviderAndConsumer("consu"), // carol double signs on provider, bob double signs on consumer
stepsSubmitEquivocationProposal("consu", 2), // now prop to tombstone bob is submitted and accepted
stepsStopChain("consu", 3),
stepsStartHermes(),
stepsConsumerRemovalPropNotPassing("consu", 3), // submit removal prop but vote no on it - chain should stay
MSalopek marked this conversation as resolved.
Show resolved Hide resolved
stepsStopChain("consu", 4), // stop chain
)

var slashThrottleSteps = concatSteps(
Expand Down
69 changes: 69 additions & 0 deletions tests/integration/steps_stop_chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@ package main

import "time"

// start hermes so that all messages are relayed
func stepsStartHermes() []Step {
return []Step{
{
action: startHermesAction{},
state: State{},
},
}
}

// submits a consumer-removal proposal and removes the chain
func stepsStopChain(consumerName string, propNumber uint) []Step {
s := []Step{
Expand Down Expand Up @@ -58,3 +68,62 @@ func stepsStopChain(consumerName string, propNumber uint) []Step {

return s
}

// submits a consumer-removal proposal and votes no on it
// the chain should not be removed
func stepsConsumerRemovalPropNotPassing(consumerName string, propNumber uint) []Step {
s := []Step{

{
action: submitConsumerRemovalProposalAction{
chain: chainID("provi"),
from: validatorID("bob"),
deposit: 10000001,
consumerChain: chainID(consumerName),
stopTimeOffset: 0 * time.Millisecond,
},
state: State{
chainID("provi"): ChainState{
ValBalances: &map[validatorID]uint{
validatorID("bob"): 9489999999,
},
Proposals: &map[uint]Proposal{
propNumber: ConsumerRemovalProposal{
Deposit: 10000001,
Chain: chainID(consumerName),
StopTime: 0,
Status: "PROPOSAL_STATUS_VOTING_PERIOD",
},
},
ConsumerChains: &map[chainID]bool{"consu": true}, // consumer chain not removed
},
},
},
{
action: voteGovProposalAction{
chain: chainID("provi"),
from: []validatorID{validatorID("alice"), validatorID("bob"), validatorID("carol")},
vote: []string{"no", "no", "no"},
propNumber: propNumber,
},
state: State{
chainID("provi"): ChainState{
Proposals: &map[uint]Proposal{
propNumber: ConsumerRemovalProposal{
Deposit: 10000001,
Chain: chainID(consumerName),
StopTime: 0,
Status: "PROPOSAL_STATUS_REJECTED",
},
},
ValBalances: &map[validatorID]uint{
validatorID("bob"): 9500000000,
},
ConsumerChains: &map[chainID]bool{"consu": true}, // consumer chain not removed
},
},
},
}

return s
}
2 changes: 1 addition & 1 deletion x/ccv/provider/keeper/proposal.go
Original file line number Diff line number Diff line change
Expand Up @@ -598,7 +598,7 @@ func (k Keeper) CreateConsumerClientInCachedCtx(ctx sdk.Context, p types.Consume
// from a given consumer removal proposal in a cached context
func (k Keeper) StopConsumerChainInCachedCtx(ctx sdk.Context, p types.ConsumerRemovalProposal) (cc sdk.Context, writeCache func(), err error) {
cc, writeCache = ctx.CacheContext()
err = k.StopConsumerChain(ctx, p.ChainId, true)
err = k.StopConsumerChain(cc, p.ChainId, true)
return
}

Expand Down
62 changes: 51 additions & 11 deletions x/ccv/provider/keeper/proposal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ func TestHandleConsumerRemovalProposal(t *testing.T) {

type testCase struct {
description string
malleate func(ctx sdk.Context, k providerkeeper.Keeper, chainID string)
setupMocks func(ctx sdk.Context, k providerkeeper.Keeper, chainID string)

// Consumer removal proposal to handle
prop *providertypes.ConsumerRemovalProposal
Expand All @@ -392,16 +392,21 @@ func TestHandleConsumerRemovalProposal(t *testing.T) {
// Whether it's expected that the proposal is successfully verified
// and appended to the pending proposals
expAppendProp bool

// chainID of the consumer chain
// tests need to check that the CCV channel is not closed prematurely
chainId string
}

// Snapshot times asserted in tests
now := time.Now().UTC()
hourFromNow := now.Add(time.Hour).UTC()
hourAfterNow := now.Add(time.Hour).UTC()
hourBeforeNow := now.Add(-time.Hour).UTC()
MSalopek marked this conversation as resolved.
Show resolved Hide resolved

tests := []testCase{
{
description: "valid proposal",
malleate: func(ctx sdk.Context, k providerkeeper.Keeper, chainID string) {
setupMocks: func(ctx sdk.Context, k providerkeeper.Keeper, chainID string) {
k.SetConsumerClientId(ctx, chainID, "ClientID")
},
prop: providertypes.NewConsumerRemovalProposal(
Expand All @@ -410,20 +415,52 @@ func TestHandleConsumerRemovalProposal(t *testing.T) {
"chainID",
now,
).(*providertypes.ConsumerRemovalProposal),
blockTime: hourFromNow, // After stop time.
blockTime: hourAfterNow, // After stop time.
expAppendProp: true,
chainId: "chainID",
},
{
description: "invalid valid proposal: consumer chain does not exist",
malleate: func(ctx sdk.Context, k providerkeeper.Keeper, chainID string) {},
description: "valid proposal - stop_time in the past",
setupMocks: func(ctx sdk.Context, k providerkeeper.Keeper, chainID string) {
k.SetConsumerClientId(ctx, chainID, "ClientID")
},
prop: providertypes.NewConsumerRemovalProposal(
"title",
"description",
"chainID",
hourBeforeNow,
).(*providertypes.ConsumerRemovalProposal),
blockTime: hourAfterNow, // After stop time.
expAppendProp: true,
chainId: "chainID",
},
{
description: "valid proposal - before stop_time in the future",
setupMocks: func(ctx sdk.Context, k providerkeeper.Keeper, chainID string) {
k.SetConsumerClientId(ctx, chainID, "ClientID")
},
prop: providertypes.NewConsumerRemovalProposal(
"title",
"description",
"chainID",
hourAfterNow,
).(*providertypes.ConsumerRemovalProposal),
blockTime: now,
expAppendProp: true,
chainId: "chainID",
},
{
description: "rejected valid proposal - consumer chain does not exist",
setupMocks: func(ctx sdk.Context, k providerkeeper.Keeper, chainID string) {},
prop: providertypes.NewConsumerRemovalProposal(
"title",
"description",
"chainID-2",
hourFromNow,
hourAfterNow,
).(*providertypes.ConsumerRemovalProposal),
blockTime: hourFromNow, // After stop time.
blockTime: hourAfterNow, // After stop time.
expAppendProp: false,
chainId: "chainID-2",
},
}

Expand All @@ -436,13 +473,13 @@ func TestHandleConsumerRemovalProposal(t *testing.T) {
ctx = ctx.WithBlockTime(tc.blockTime)

// Mock expectations and setup for stopping the consumer chain, if applicable
// Note: when expAppendProp is false, no mocks are setup,
// meaning no external keeper methods are allowed to be called.
if tc.expAppendProp {
testkeeper.SetupForStoppingConsumerChain(t, ctx, &providerKeeper, mocks)
}
// Note: when expAppendProp is false, no mocks are setup,
// meaning no external keeper methods are allowed to be called.

tc.malleate(ctx, providerKeeper, tc.prop.ChainId)
tc.setupMocks(ctx, providerKeeper, tc.prop.ChainId)

err := providerKeeper.HandleConsumerRemovalProposal(ctx, tc.prop)

Expand All @@ -453,6 +490,9 @@ func TestHandleConsumerRemovalProposal(t *testing.T) {
found := providerKeeper.PendingConsumerRemovalPropExists(ctx, tc.prop.ChainId, tc.prop.StopTime)
require.True(t, found)

// confirm that the channel was not closed
_, found = providerKeeper.GetChainToChannel(ctx, tc.chainId)
require.True(t, found)
} else {
require.Error(t, err)

Expand Down