diff --git a/x/crosschain/keeper/msg_server_vote_inbound_tx.go b/x/crosschain/keeper/msg_server_vote_inbound_tx.go index eeaa8d41a3..db287e6b82 100644 --- a/x/crosschain/keeper/msg_server_vote_inbound_tx.go +++ b/x/crosschain/keeper/msg_server_vote_inbound_tx.go @@ -77,7 +77,7 @@ func (k msgServer) VoteOnObservedInboundTx(goCtx context.Context, msg *types.Msg // This may happen if the same inbound is observed twice where msg.Digest gives a different index // This check prevents double spending if isNew { - if k.IsFinalizedInbound(ctx, msg.InTxHash, msg.SenderChainId, msg.EventIndex) { + if k.IsFinalizedInbound(tmpCtx, msg.InTxHash, msg.SenderChainId, msg.EventIndex) { return nil, cosmoserrors.Wrap( types.ErrObservedTxAlreadyFinalized, fmt.Sprintf("InTxHash:%s, SenderChainID:%d, EventIndex:%d", msg.InTxHash, msg.SenderChainId, msg.EventIndex), diff --git a/x/crosschain/keeper/msg_server_vote_inbound_tx_test.go b/x/crosschain/keeper/msg_server_vote_inbound_tx_test.go index c42dddd7a3..803de84576 100644 --- a/x/crosschain/keeper/msg_server_vote_inbound_tx_test.go +++ b/x/crosschain/keeper/msg_server_vote_inbound_tx_test.go @@ -33,6 +33,9 @@ func setObservers(t *testing.T, k *keeper.Keeper, ctx sdk.Context, zk keepertest }) return validatorAddressListFormatted } + +// TODO: Complete the test cases +// https://github.com/zeta-chain/node/issues/1542 func TestKeeper_VoteOnObservedInboundTx(t *testing.T) { t.Run("successfully vote on evm deposit", func(t *testing.T) { k, ctx, _, zk := keepertest.CrosschainKeeper(t) @@ -69,91 +72,88 @@ func TestKeeper_VoteOnObservedInboundTx(t *testing.T) { require.Equal(t, cctx.CctxStatus.Status, types.CctxStatus_OutboundMined) require.Equal(t, cctx.InboundTxParams.TxFinalizationStatus, types.TxFinalizationStatus_Executed) }) - // TODO : https://github.com/zeta-chain/node/issues/1542 -} -/* -Potential Double Event Submission -*/ -func TestNoDoubleEventProtections(t *testing.T) { - k, ctx, _, zk := keepertest.CrosschainKeeper(t) + t.Run("prevent double event submission", func(t *testing.T) { + k, ctx, _, zk := keepertest.CrosschainKeeper(t) - // MsgServer for the crosschain keeper - msgServer := keeper.NewMsgServerImpl(*k) + // MsgServer for the crosschain keeper + msgServer := keeper.NewMsgServerImpl(*k) - // Set the chain ids we want to use to be valid - params := observertypes.DefaultParams() - zk.ObserverKeeper.SetParams( - ctx, params, - ) + // Set the chain ids we want to use to be valid + params := observertypes.DefaultParams() + zk.ObserverKeeper.SetParams( + ctx, params, + ) - // Convert the validator address into a user address. - validators := k.GetStakingKeeper().GetAllValidators(ctx) - validatorAddress := validators[0].OperatorAddress - valAddr, _ := sdk.ValAddressFromBech32(validatorAddress) - addresstmp, _ := sdk.AccAddressFromHexUnsafe(hex.EncodeToString(valAddr.Bytes())) - validatorAddr := addresstmp.String() + // Convert the validator address into a user address. + validators := k.GetStakingKeeper().GetAllValidators(ctx) + validatorAddress := validators[0].OperatorAddress + valAddr, _ := sdk.ValAddressFromBech32(validatorAddress) + addresstmp, _ := sdk.AccAddressFromHexUnsafe(hex.EncodeToString(valAddr.Bytes())) + validatorAddr := addresstmp.String() - // Add validator to the observer list for voting - zk.ObserverKeeper.SetObserverSet(ctx, observertypes.ObserverSet{ - ObserverList: []string{validatorAddr}, - }) + // Add validator to the observer list for voting + zk.ObserverKeeper.SetObserverSet(ctx, observertypes.ObserverSet{ + ObserverList: []string{validatorAddr}, + }) - // Vote on the FIRST message. - msg := &types.MsgVoteOnObservedInboundTx{ - Creator: validatorAddr, - Sender: "0x954598965C2aCdA2885B037561526260764095B8", - SenderChainId: 1337, // ETH - Receiver: "0x954598965C2aCdA2885B037561526260764095B8", - ReceiverChain: 101, // zetachain - Amount: sdkmath.NewUintFromString("10000000"), - Message: "", - InBlockHeight: 1, - GasLimit: 1000000000, - InTxHash: "0x7a900ef978743f91f57ca47c6d1a1add75df4d3531da17671e9cf149e1aefe0b", - CoinType: 0, // zeta - TxOrigin: "0x954598965C2aCdA2885B037561526260764095B8", - Asset: "", - EventIndex: 1, - } - _, err := msgServer.VoteOnObservedInboundTx( - ctx, - msg, - ) - require.NoError(t, err) + // Vote on the FIRST message. + msg := &types.MsgVoteOnObservedInboundTx{ + Creator: validatorAddr, + Sender: "0x954598965C2aCdA2885B037561526260764095B8", + SenderChainId: 1337, // ETH + Receiver: "0x954598965C2aCdA2885B037561526260764095B8", + ReceiverChain: 101, // zetachain + Amount: sdkmath.NewUintFromString("10000000"), + Message: "", + InBlockHeight: 1, + GasLimit: 1000000000, + InTxHash: "0x7a900ef978743f91f57ca47c6d1a1add75df4d3531da17671e9cf149e1aefe0b", + CoinType: 0, // zeta + TxOrigin: "0x954598965C2aCdA2885B037561526260764095B8", + Asset: "", + EventIndex: 1, + } + _, err := msgServer.VoteOnObservedInboundTx( + ctx, + msg, + ) + require.NoError(t, err) - // Check that the vote passed - ballot, found := zk.ObserverKeeper.GetBallot(ctx, msg.Digest()) - require.True(t, found) - require.Equal(t, ballot.BallotStatus, observertypes.BallotStatus_BallotFinalized_SuccessObservation) - //Perform the SAME event. Except, this time, we resubmit the event. - msg2 := &types.MsgVoteOnObservedInboundTx{ - Creator: validatorAddr, - Sender: "0x954598965C2aCdA2885B037561526260764095B8", - SenderChainId: 1337, - Receiver: "0x954598965C2aCdA2885B037561526260764095B8", - ReceiverChain: 101, - Amount: sdkmath.NewUintFromString("10000000"), - Message: "", - InBlockHeight: 1, - GasLimit: 1000000001, // <---- Change here - InTxHash: "0x7a900ef978743f91f57ca47c6d1a1add75df4d3531da17671e9cf149e1aefe0b", - CoinType: 0, - TxOrigin: "0x954598965C2aCdA2885B037561526260764095B8", - Asset: "", - EventIndex: 1, - } + // Check that the vote passed + ballot, found := zk.ObserverKeeper.GetBallot(ctx, msg.Digest()) + require.True(t, found) + require.Equal(t, ballot.BallotStatus, observertypes.BallotStatus_BallotFinalized_SuccessObservation) + //Perform the SAME event. Except, this time, we resubmit the event. + msg2 := &types.MsgVoteOnObservedInboundTx{ + Creator: validatorAddr, + Sender: "0x954598965C2aCdA2885B037561526260764095B8", + SenderChainId: 1337, + Receiver: "0x954598965C2aCdA2885B037561526260764095B8", + ReceiverChain: 101, + Amount: sdkmath.NewUintFromString("10000000"), + Message: "", + InBlockHeight: 1, + GasLimit: 1000000001, // <---- Change here + InTxHash: "0x7a900ef978743f91f57ca47c6d1a1add75df4d3531da17671e9cf149e1aefe0b", + CoinType: 0, + TxOrigin: "0x954598965C2aCdA2885B037561526260764095B8", + Asset: "", + EventIndex: 1, + } - _, err = msgServer.VoteOnObservedInboundTx( - ctx, - msg2, - ) - require.Error(t, err) - require.ErrorIs(t, err, types.ErrObservedTxAlreadyFinalized) - _, found = zk.ObserverKeeper.GetBallot(ctx, msg2.Digest()) - require.False(t, found) + _, err = msgServer.VoteOnObservedInboundTx( + ctx, + msg2, + ) + require.Error(t, err) + require.ErrorIs(t, err, types.ErrObservedTxAlreadyFinalized) + _, found = zk.ObserverKeeper.GetBallot(ctx, msg2.Digest()) + require.False(t, found) + }) } -func TestStatus_StatusTransition(t *testing.T) { + +func TestStatus_ChangeStatus(t *testing.T) { tt := []struct { Name string Status types.Status diff --git a/x/observer/keeper/vote_inbound_test.go b/x/observer/keeper/vote_inbound_test.go index 7f6c9fb81d..aa10c85af1 100644 --- a/x/observer/keeper/vote_inbound_test.go +++ b/x/observer/keeper/vote_inbound_test.go @@ -320,4 +320,66 @@ func TestKeeper_VoteOnInboundBallot(t *testing.T) { require.False(t, isFinalized) require.False(t, isNew) }) + + t.Run("can add vote to an existing ballot and finalize ballot", func(t *testing.T) { + k, ctx, _ := keepertest.ObserverKeeperWithMocks(t, keepertest.ObserverMocksAll) + + observer := sample.AccAddress() + stakingMock := keepertest.GetObserverStakingMock(t, k) + slashingMock := keepertest.GetObserverSlashingMock(t, k) + + k.SetCrosschainFlags(ctx, types.CrosschainFlags{ + IsInboundEnabled: true, + }) + k.SetChainParamsList(ctx, types.ChainParamsList{ + ChainParams: []*types.ChainParams{ + { + ChainId: getValidEthChainIDWithIndex(t, 0), + IsSupported: true, + }, + { + ChainId: getValidEthChainIDWithIndex(t, 1), + IsSupported: true, + }, + }, + }) + k.SetObserverSet(ctx, types.ObserverSet{ + ObserverList: []string{observer}, + }) + stakingMock.MockGetValidator(sample.Validator(t, sample.Rand())) + slashingMock.MockIsTombstoned(false) + + // set a ballot + threshold, err := sdk.NewDecFromStr("0.1") + require.NoError(t, err) + ballot := types.Ballot{ + Index: "index", + BallotIdentifier: "index", + VoterList: []string{ + observer, + sample.AccAddress(), + sample.AccAddress(), + }, + Votes: types.CreateVotes(3), + ObservationType: types.ObservationType_InBoundTx, + BallotThreshold: threshold, + BallotStatus: types.BallotStatus_BallotInProgress, + } + k.SetBallot(ctx, &ballot) + + isFinalized, isNew, err := k.VoteOnInboundBallot( + ctx, + getValidEthChainIDWithIndex(t, 0), + getValidEthChainIDWithIndex(t, 1), + common.CoinType_ERC20, + observer, + "index", + "inTxHash", + ) + require.NoError(t, err) + + // ballot should not be finalized as the threshold is not reached + require.True(t, isFinalized) + require.False(t, isNew) + }) } diff --git a/x/observer/keeper/vote_outbound_test.go b/x/observer/keeper/vote_outbound_test.go index edb7db3fec..a916f2f3e1 100644 --- a/x/observer/keeper/vote_outbound_test.go +++ b/x/observer/keeper/vote_outbound_test.go @@ -190,4 +190,60 @@ func TestKeeper_VoteOnOutboundBallot(t *testing.T) { require.True(t, found) require.Equal(t, expectedBallot, ballot) }) + + t.Run("can add vote to an existing ballot and finalize ballot", func(t *testing.T) { + k, ctx, _ := keepertest.ObserverKeeperWithMocks(t, keepertest.ObserverMocksAll) + + observer := sample.AccAddress() + stakingMock := keepertest.GetObserverStakingMock(t, k) + slashingMock := keepertest.GetObserverSlashingMock(t, k) + + k.SetChainParamsList(ctx, types.ChainParamsList{ + ChainParams: []*types.ChainParams{ + { + ChainId: getValidEthChainIDWithIndex(t, 0), + IsSupported: true, + }, + }, + }) + k.SetObserverSet(ctx, types.ObserverSet{ + ObserverList: []string{observer}, + }) + stakingMock.MockGetValidator(sample.Validator(t, sample.Rand())) + slashingMock.MockIsTombstoned(false) + + // set a ballot + threshold, err := sdk.NewDecFromStr("0.1") + require.NoError(t, err) + ballot := types.Ballot{ + Index: "index", + BallotIdentifier: "index", + VoterList: []string{ + observer, + sample.AccAddress(), + sample.AccAddress(), + }, + Votes: types.CreateVotes(3), + ObservationType: types.ObservationType_OutBoundTx, + BallotThreshold: threshold, + BallotStatus: types.BallotStatus_BallotInProgress, + } + k.SetBallot(ctx, &ballot) + + isFinalized, isNew, ballot, _, err := k.VoteOnOutboundBallot( + ctx, + "index", + getValidEthChainIDWithIndex(t, 0), + common.ReceiveStatus_Success, + observer, + ) + require.NoError(t, err) + + // ballot should be finalized since there is only one observer + require.True(t, isFinalized) + require.False(t, isNew) + expectedBallot, found := k.GetBallot(ctx, "index") + require.True(t, found) + require.Equal(t, expectedBallot, ballot) + }) }