Skip to content

Commit

Permalink
create save outbound function and unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
kingpinXD committed Mar 15, 2024
1 parent 29bd7c5 commit 8c7758a
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 44 deletions.
30 changes: 10 additions & 20 deletions x/crosschain/keeper/cctx_utils_outbound.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ func SetRevertOutboundValues(cctx *types.CrossChainTx, gasLimit uint64) {
OutboundTxGasLimit: gasLimit,
TssPubkey: cctx.GetCurrentOutTxParam().TssPubkey,
}
// The original outbound has been finalized, the new outbound is pending
cctx.GetCurrentOutTxParam().TxFinalizationStatus = types.TxFinalizationStatus_Executed
cctx.OutboundTxParams = append(cctx.OutboundTxParams, revertTxParams)
}
func SetOutboundValues(ctx sdk.Context, cctx *types.CrossChainTx, msg types.MsgVoteOnObservedOutboundTx, ballotStatus observertypes.BallotStatus) error {
Expand Down Expand Up @@ -118,16 +120,7 @@ func (k Keeper) ProcessFailedOutbound(ctx sdk.Context, cctx *types.CrossChainTx,
}

// create new OutboundTxParams for the revert
revertTxParams := &types.OutboundTxParams{
Receiver: cctx.InboundTxParams.Sender,
ReceiverChainId: cctx.InboundTxParams.SenderChainId,
Amount: cctx.InboundTxParams.Amount,
OutboundTxGasLimit: gasLimit,
TssPubkey: cctx.GetCurrentOutTxParam().TssPubkey,
}
// TODO : consider setting the first outbount to executed
// Move this creation to a separate function
cctx.OutboundTxParams = append(cctx.OutboundTxParams, revertTxParams)
SetRevertOutboundValues(cctx, gasLimit)

err = k.PayGasAndUpdateCctx(
ctx,
Expand Down Expand Up @@ -173,21 +166,18 @@ func (k Keeper) ProcessOutbound(ctx sdk.Context, cctx *types.CrossChainTx, ballo
return nil
}

func (k Keeper) SaveFailedOutBound(ctx sdk.Context, cctx *types.CrossChainTx, errMessage string) {
receiverChain := cctx.GetCurrentOutTxParam().ReceiverChainId
tssPubkey := cctx.GetCurrentOutTxParam().TssPubkey
outTxTssNonce := cctx.GetCurrentOutTxParam().OutboundTxTssNonce

func (k Keeper) SaveFailedOutBound(ctx sdk.Context, cctx *types.CrossChainTx, errMessage string, ballotIndex string) {
cctx.CctxStatus.ChangeStatus(types.CctxStatus_Aborted, errMessage)
cctx.GetCurrentOutTxParam().TxFinalizationStatus = types.TxFinalizationStatus_Executed
ctx.Logger().Error(errMessage)
// #nosec G701 always in range
k.GetObserverKeeper().RemoveFromPendingNonces(ctx, tssPubkey, receiverChain, int64(outTxTssNonce))
k.RemoveOutTxTracker(ctx, receiverChain, outTxTssNonce)
k.SetCctxAndNonceToCctxAndInTxHashToCctx(ctx, *cctx)

k.SaveOutbound(ctx, cctx, ballotIndex)
}

func (k Keeper) SaveSuccessfulOutBound(ctx sdk.Context, cctx *types.CrossChainTx, ballotIndex string) {
k.SaveOutbound(ctx, cctx, ballotIndex)
}

func (k Keeper) SaveOutbound(ctx sdk.Context, cctx *types.CrossChainTx, ballotIndex string) {
receiverChain := cctx.GetCurrentOutTxParam().ReceiverChainId
tssPubkey := cctx.GetCurrentOutTxParam().TssPubkey
outTxTssNonce := cctx.GetCurrentOutTxParam().OutboundTxTssNonce
Expand Down
57 changes: 56 additions & 1 deletion x/crosschain/keeper/cctx_utils_outbound_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ func TestKeeper_SaveFailedOutBound(t *testing.T) {
HashList: nil,
})
cctx.CctxStatus.Status = types.CctxStatus_PendingOutbound
k.SaveFailedOutBound(ctx, cctx, sample.String())
k.SaveFailedOutBound(ctx, cctx, sample.String(), sample.ZetaIndex(t))
require.Equal(t, cctx.CctxStatus.Status, types.CctxStatus_Aborted)
require.Equal(t, cctx.GetCurrentOutTxParam().TxFinalizationStatus, types.TxFinalizationStatus_Executed)
_, found := k.GetOutTxTracker(ctx, cctx.GetCurrentOutTxParam().ReceiverChainId, cctx.GetCurrentOutTxParam().OutboundTxTssNonce)
Expand All @@ -311,3 +311,58 @@ func TestKeeper_SaveSuccessfulOutBound(t *testing.T) {
require.False(t, found)
})
}

func TestKeeper_SaveOutbound(t *testing.T) {
t.Run("successfully save outbound", func(t *testing.T) {
k, ctx, _, zk := keepertest.CrosschainKeeper(t)

// setup state for crosschain and observer modules
cctx := sample.CrossChainTx(t, "test")
cctx.CctxStatus.Status = types.CctxStatus_PendingOutbound
ballotIndex := sample.String()
k.SetOutTxTracker(ctx, types.OutTxTracker{
Index: "",
ChainId: cctx.GetCurrentOutTxParam().ReceiverChainId,
Nonce: cctx.GetCurrentOutTxParam().OutboundTxTssNonce,
HashList: nil,
})

zk.ObserverKeeper.SetPendingNonces(ctx, observertypes.PendingNonces{
NonceLow: int64(cctx.GetCurrentOutTxParam().OutboundTxTssNonce) - 1,
NonceHigh: int64(cctx.GetCurrentOutTxParam().OutboundTxTssNonce) + 1,
ChainId: cctx.GetCurrentOutTxParam().ReceiverChainId,
Tss: cctx.GetCurrentOutTxParam().TssPubkey,
})
zk.ObserverKeeper.SetTSS(ctx, observertypes.TSS{
TssPubkey: cctx.GetCurrentOutTxParam().TssPubkey,
})

// Save outbound and assert all values are successfully saved
k.SaveOutbound(ctx, cctx, ballotIndex)
require.Equal(t, cctx.GetCurrentOutTxParam().OutboundTxBallotIndex, ballotIndex)
require.Equal(t, cctx.GetCurrentOutTxParam().TxFinalizationStatus, types.TxFinalizationStatus_Executed)
_, found := k.GetOutTxTracker(ctx, cctx.GetCurrentOutTxParam().ReceiverChainId, cctx.GetCurrentOutTxParam().OutboundTxTssNonce)
require.False(t, found)
pn, found := zk.ObserverKeeper.GetPendingNonces(ctx, cctx.GetCurrentOutTxParam().TssPubkey, cctx.GetCurrentOutTxParam().ReceiverChainId)
require.True(t, found)
require.Equal(t, pn.NonceLow, int64(cctx.GetCurrentOutTxParam().OutboundTxTssNonce)+1)
require.Equal(t, pn.NonceHigh, int64(cctx.GetCurrentOutTxParam().OutboundTxTssNonce)+1)
_, found = k.GetInTxHashToCctx(ctx, cctx.InboundTxParams.InboundTxObservedHash)
require.True(t, found)
_, found = zk.ObserverKeeper.GetNonceToCctx(ctx, cctx.GetCurrentOutTxParam().TssPubkey, cctx.GetCurrentOutTxParam().ReceiverChainId, int64(cctx.GetCurrentOutTxParam().OutboundTxTssNonce))
require.True(t, found)
})
}

func Test_SetRevertOutboundValues(t *testing.T) {
cctx := sample.CrossChainTx(t, "test")
cctx.OutboundTxParams = cctx.OutboundTxParams[:1]
keeper.SetRevertOutboundValues(cctx, 100)
require.Len(t, cctx.OutboundTxParams, 2)
require.Equal(t, cctx.GetCurrentOutTxParam().Receiver, cctx.InboundTxParams.Sender)
require.Equal(t, cctx.GetCurrentOutTxParam().ReceiverChainId, cctx.InboundTxParams.SenderChainId)
require.Equal(t, cctx.GetCurrentOutTxParam().Amount, cctx.InboundTxParams.Amount)
require.Equal(t, cctx.GetCurrentOutTxParam().OutboundTxGasLimit, uint64(100))
require.Equal(t, cctx.GetCurrentOutTxParam().TssPubkey, cctx.OutboundTxParams[0].TssPubkey)
require.Equal(t, types.TxFinalizationStatus_Executed, cctx.OutboundTxParams[0].TxFinalizationStatus)
}
18 changes: 7 additions & 11 deletions x/crosschain/keeper/msg_server_vote_outbound_tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,14 @@ func (k msgServer) VoteOnObservedOutboundTx(goCtx context.Context, msg *types.Ms
if cctx.GetCurrentOutTxParam().OutboundTxTssNonce != msg.OutTxTssNonce {
return nil, cosmoserrors.Wrap(sdkerrors.ErrInvalidRequest, fmt.Sprintf("OutTxTssNonce %d does not match CCTX OutTxTssNonce %d", msg.OutTxTssNonce, cctx.GetCurrentOutTxParam().OutboundTxTssNonce))
}
// do not process outbound vote if TSS is not found
_, found = k.zetaObserverKeeper.GetTSS(ctx)
if !found {
return nil, types.ErrCannotFindTSSKeys
}

// get ballot index
ballotIndex := msg.Digest()

// vote on outbound ballot
isFinalizingVote, isNew, ballot, observationChain, err := k.zetaObserverKeeper.VoteOnOutboundBallot(
ctx,
Expand All @@ -80,20 +84,12 @@ func (k msgServer) VoteOnObservedOutboundTx(goCtx context.Context, msg *types.Ms
// if the ballot is new, set the index to the CCTX
if isNew {
observerkeeper.EmitEventBallotCreated(ctx, ballot, msg.ObservedOutTxHash, observationChain)
// Set this the first time when the ballot is created
// The ballot might change if there are more votes in a different outbound ballot for this cctx hash
cctx.GetCurrentOutTxParam().OutboundTxBallotIndex = ballotIndex
}

// if not finalized commit state here
if !isFinalizingVote {
return &types.MsgVoteOnObservedOutboundTxResponse{}, nil
}

_, found = k.zetaObserverKeeper.GetTSS(ctx)
if !found {
return nil, types.ErrCannotFindTSSKeys
}
// if ballot successful, the value received should be the out tx amount
err = SetOutboundValues(ctx, &cctx, *msg, ballot.BallotStatus)
if err != nil {
Expand All @@ -104,9 +100,9 @@ func (k msgServer) VoteOnObservedOutboundTx(goCtx context.Context, msg *types.Ms

err = k.ProcessOutbound(ctx, &cctx, ballot.BallotStatus, msg.ValueReceived.String())
if err != nil {
k.SaveFailedOutBound(ctx, &cctx, msg.ValueReceived.String())
k.SaveFailedOutBound(ctx, &cctx, err.Error(), ballotIndex)
return &types.MsgVoteOnObservedOutboundTxResponse{}, nil
}
k.SaveSuccessfulOutBound(ctx, &cctx, msg.ValueReceived.String())
k.SaveSuccessfulOutBound(ctx, &cctx, ballotIndex)
return &types.MsgVoteOnObservedOutboundTxResponse{}, nil
}
73 changes: 61 additions & 12 deletions x/crosschain/keeper/msg_server_vote_outbound_tx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,46 +223,95 @@ func TestKeeper_VoteOnObservedOutboundTx(t *testing.T) {
})

t.Run("fail to finalize outbound if not a finalizing vote", func(t *testing.T) {
k, ctx, _, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{
UseObserverMock: true,
})
k, ctx, sk, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{})

// Setup mock data
observerMock := keepertest.GetCrosschainObserverMock(t, k)
receiver := sample.EthAddress()
amount := big.NewInt(42)
senderChain := getValidEthChain(t)
asset := ""
observer := sample.AccAddress()
r := rand.New(rand.NewSource(42))
validator := sample.Validator(t, r)
tss := sample.Tss()
zk.ObserverKeeper.SetObserverSet(ctx, observertypes.ObserverSet{ObserverList: []string{observer}})

// set state to successfully vote on outbound tx
accAddress, err := observertypes.GetAccAddressFromOperatorAddress(validator.OperatorAddress)
require.NoError(t, err)
zk.ObserverKeeper.SetObserverSet(ctx, observertypes.ObserverSet{ObserverList: []string{accAddress.String(), sample.AccAddress(), sample.AccAddress()}})
sk.StakingKeeper.SetValidator(ctx, validator)
cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount)
cctx.GetCurrentOutTxParam().TssPubkey = tss.TssPubkey
cctx.CctxStatus.Status = types.CctxStatus_PendingOutbound
k.SetCrossChainTx(ctx, *cctx)

observerMock.On("VoteOnOutboundBallot", ctx, mock.Anything, cctx.GetCurrentOutTxParam().ReceiverChainId, common.ReceiveStatus_Success, observer).
Return(false, true, observertypes.Ballot{BallotStatus: observertypes.BallotStatus_BallotFinalized_SuccessObservation}, senderChain.ChainName.String(), nil).Once()
zk.ObserverKeeper.SetTSS(ctx, tss)

msgServer := keeper.NewMsgServerImpl(*k)
msg := &types.MsgVoteOnObservedOutboundTx{
CctxHash: cctx.Index,
OutTxTssNonce: cctx.GetCurrentOutTxParam().OutboundTxTssNonce,
OutTxChain: cctx.GetCurrentOutTxParam().ReceiverChainId,
Status: common.ReceiveStatus_Success,
Creator: observer,
Creator: accAddress.String(),
ObservedOutTxHash: sample.Hash().String(),
ValueReceived: cctx.GetCurrentOutTxParam().Amount,
ObservedOutTxBlockHeight: 10,
ObservedOutTxEffectiveGasPrice: math.NewInt(21),
ObservedOutTxGasUsed: 21,
CoinType: cctx.CoinType,
}
_, err := msgServer.VoteOnObservedOutboundTx(ctx, msg)
_, err = msgServer.VoteOnObservedOutboundTx(ctx, msg)
require.NoError(t, err)
c, found := k.GetCrossChainTx(ctx, cctx.Index)
require.True(t, found)
require.Equal(t, types.CctxStatus_PendingOutbound, c.CctxStatus.Status)
ballot, found := zk.ObserverKeeper.GetBallot(ctx, msg.Digest())
require.True(t, found)
require.True(t, ballot.HasVoted(accAddress.String()))
})

t.Run("unable to add vote if tss is not present", func(t *testing.T) {
k, ctx, sk, zk := keepertest.CrosschainKeeperWithMocks(t, keepertest.CrosschainMockOptions{})

// Setup mock data
receiver := sample.EthAddress()
amount := big.NewInt(42)
senderChain := getValidEthChain(t)
asset := ""
r := rand.New(rand.NewSource(42))
validator := sample.Validator(t, r)
tss := sample.Tss()

// set state to successfully vote on outbound tx
accAddress, err := observertypes.GetAccAddressFromOperatorAddress(validator.OperatorAddress)
require.NoError(t, err)
zk.ObserverKeeper.SetObserverSet(ctx, observertypes.ObserverSet{ObserverList: []string{accAddress.String()}})
sk.StakingKeeper.SetValidator(ctx, validator)
cctx := GetERC20Cctx(t, receiver, *senderChain, asset, amount)
cctx.GetCurrentOutTxParam().TssPubkey = tss.TssPubkey
cctx.CctxStatus.Status = types.CctxStatus_PendingOutbound
k.SetCrossChainTx(ctx, *cctx)

msgServer := keeper.NewMsgServerImpl(*k)
msg := &types.MsgVoteOnObservedOutboundTx{
CctxHash: cctx.Index,
OutTxTssNonce: cctx.GetCurrentOutTxParam().OutboundTxTssNonce,
OutTxChain: cctx.GetCurrentOutTxParam().ReceiverChainId,
Status: common.ReceiveStatus_Success,
Creator: accAddress.String(),
ObservedOutTxHash: sample.Hash().String(),
ValueReceived: cctx.GetCurrentOutTxParam().Amount,
ObservedOutTxBlockHeight: 10,
ObservedOutTxEffectiveGasPrice: math.NewInt(21),
ObservedOutTxGasUsed: 21,
CoinType: cctx.CoinType,
}
_, err = msgServer.VoteOnObservedOutboundTx(ctx, msg)
require.ErrorIs(t, err, types.ErrCannotFindTSSKeys)
c, found := k.GetCrossChainTx(ctx, cctx.Index)
require.True(t, found)
// Status not changed if this is not the finalizing vote
require.Equal(t, types.CctxStatus_PendingOutbound, c.CctxStatus.Status)
_, found = zk.ObserverKeeper.GetBallot(ctx, msg.Digest())
require.False(t, found)

})
}

0 comments on commit 8c7758a

Please sign in to comment.