Skip to content

Commit

Permalink
refactor: reset timeout and confirmation data for crosschain transact…
Browse files Browse the repository at this point in the history
…ion timeout (#769)

Co-authored-by: nulnut <151493716+nulnut@users.noreply.github.com>
  • Loading branch information
zakir-code and nulnut authored Oct 22, 2024
1 parent 9c6341a commit 9ec7942
Show file tree
Hide file tree
Showing 5 changed files with 36 additions and 37 deletions.
19 changes: 5 additions & 14 deletions x/crosschain/keeper/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,23 +148,11 @@ func (k Keeper) bridgeCallSlashing(ctx sdk.Context, oracles types.Oracles, signe
return hasSlash
}

// cleanupTimedOutBatches deletes batches that have passed their expiration on Ethereum
// keep in mind several things when modifying this function
// A) unlike nonces timeouts are not monotonically increasing, meaning batch 5 can have a later timeout than batch 6
//
// this means that we MUST only cleanup a single batch at a time
//
// B) it is possible for ethereumHeight to be zero if no events have ever occurred, make sure your code accounts for this
// C) When we compute the timeout we do our best to estimate the Ethereum block height at that very second. But what we work with
//
// here is the Ethereum block height at the time of the last SendToExternal or SendToFx to be observed. It's very important we do not
// project, if we do a slowdown on ethereum could cause a double spend. Instead timeouts will *only* occur after the timeout period
// AND any deposit or withdraw has occurred to update the Ethereum block height.
func (k Keeper) cleanupTimedOutBatches(ctx sdk.Context) (err error) {
externalBlockHeight := k.GetLastObservedBlockHeight(ctx).ExternalBlockHeight
k.IterateOutgoingTxBatches(ctx, func(batch *types.OutgoingTxBatch) bool {
if batch.BatchTimeout < externalBlockHeight {
if err = k.RefundOutgoingTxBatch(ctx, batch.TokenContract, batch.BatchNonce); err != nil {
if err = k.ResendTimeoutOutgoingTxBatch(ctx, batch); err != nil {
return true
}
}
Expand All @@ -179,13 +167,16 @@ func (k Keeper) cleanupTimeOutBridgeCall(ctx sdk.Context) (err error) {
if data.Timeout > externalBlockHeight {
return true
}

// 1. handler bridge call refund
if err = k.RefundOutgoingBridgeCall(ctx, data); err != nil {
return true
}

// 2. delete bridge call
k.DeleteOutgoingBridgeCallRecord(ctx, data.Nonce)
if err = k.DeleteOutgoingBridgeCallRecord(ctx, data.Nonce); err != nil {
return true
}
return false
})
return err
Expand Down
10 changes: 7 additions & 3 deletions x/crosschain/keeper/bridge_call_out.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,9 @@ func (k Keeper) BridgeCallResultHandler(ctx sdk.Context, claim *types.MsgBridgeC
return err
}
}
k.DeleteOutgoingBridgeCallRecord(ctx, claim.Nonce)

if err := k.DeleteOutgoingBridgeCallRecord(ctx, claim.Nonce); err != nil {
return err
}
ctx.EventManager().EmitEvent(sdk.NewEvent(
types.EventTypeBridgeCallResult,
sdk.NewAttribute(types.AttributeKeyEventNonce, strconv.FormatInt(int64(claim.Nonce), 10)),
Expand Down Expand Up @@ -164,12 +165,15 @@ func (k Keeper) RefundOutgoingBridgeCall(ctx sdk.Context, data *types.OutgoingBr
return nil
}

func (k Keeper) DeleteOutgoingBridgeCallRecord(ctx sdk.Context, bridgeCallNonce uint64) {
func (k Keeper) DeleteOutgoingBridgeCallRecord(ctx sdk.Context, bridgeCallNonce uint64) error {
// 1. delete bridge call
k.DeleteOutgoingBridgeCall(ctx, bridgeCallNonce)

// 2. delete bridge call confirm
k.DeleteBridgeCallConfirm(ctx, bridgeCallNonce)

// 3. delete cache origin amount
return k.erc20Keeper.DeleteCache(ctx, types.NewOriginTokenKey(k.moduleName, bridgeCallNonce))
}

func (k Keeper) SetOutgoingBridgeCall(ctx sdk.Context, outCall *types.OutgoingBridgeCall) {
Expand Down
1 change: 1 addition & 0 deletions x/crosschain/keeper/bridge_call_out_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ func (s *KeeperMockSuite) TestKeeper_BridgeCallResultHandler() {

s.accountKeeper.EXPECT().GetAccount(gomock.Any(), gomock.Any()).Return(nil).Times(1)
s.accountKeeper.EXPECT().NewAccountWithAddress(gomock.Any(), gomock.Any()).Times(1)
s.erc20Keeper.EXPECT().DeleteCache(gomock.Any(), gomock.Any()).Times(1)

s.crosschainKeeper.SetOutgoingBridgeCall(s.ctx, &types.OutgoingBridgeCall{
Sender: helpers.GenExternalAddr(s.moduleName),
Expand Down
38 changes: 23 additions & 15 deletions x/crosschain/keeper/outgoing_tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,9 @@ func (k Keeper) OutgoingTxBatchExecuted(ctx sdk.Context, tokenContract string, b

// Iterate through remaining batches
k.IterateOutgoingTxBatches(ctx, func(iterBatch *types.OutgoingTxBatch) bool {
// If the iterated batches nonce is lower than the one that was just executed, cancel it
// If the iterated batches nonce is lower than the one that was just executed, resend it
if iterBatch.BatchNonce < batch.BatchNonce && iterBatch.TokenContract == tokenContract {
if err = k.RefundOutgoingTxBatch(ctx, tokenContract, iterBatch.BatchNonce); err != nil {
if err = k.ResendTimeoutOutgoingTxBatch(ctx, iterBatch); err != nil {
return true
}
}
Expand Down Expand Up @@ -144,23 +144,31 @@ func (k Keeper) GetOutgoingTxBatch(ctx sdk.Context, tokenContract string, batchN
return batch
}

// RefundOutgoingTxBatch releases all TX in the batch and deletes the batch
func (k Keeper) RefundOutgoingTxBatch(ctx sdk.Context, tokenContract string, batchNonce uint64) error {
batch := k.GetOutgoingTxBatch(ctx, tokenContract, batchNonce)
if batch == nil {
return types.ErrInvalid.Wrapf("batch not found %s %d", tokenContract, batchNonce)
}
// for _, tx := range batch.Transactions {
// todo: need refund
// }

// Delete batch since it is finished
func (k Keeper) ResendTimeoutOutgoingTxBatch(ctx sdk.Context, batch *types.OutgoingTxBatch) error {
k.DeleteBatch(ctx, batch)

k.DeleteBatchConfirm(ctx, batch.BatchNonce, batch.TokenContract)
ctx.EventManager().EmitEvent(sdk.NewEvent(
types.EventTypeOutgoingBatchCanceled,
sdk.NewAttribute(sdk.AttributeKeyModule, k.moduleName),
sdk.NewAttribute(types.AttributeKeyOutgoingBatchNonce, fmt.Sprint(batchNonce)),
sdk.NewAttribute(types.AttributeKeyOutgoingBatchNonce, fmt.Sprint(batch.BatchNonce)),
))

batchTimeout := k.CalExternalTimeoutHeight(ctx, GetExternalBatchTimeout)
if batchTimeout <= 0 {
return types.ErrInvalid.Wrapf("batch timeout height")
}
batch.BatchTimeout = batchTimeout
batch.BatchNonce = k.autoIncrementID(ctx, types.KeyLastOutgoingBatchID)
if err := k.StoreBatch(ctx, batch); err != nil {
return err
}

ctx.EventManager().EmitEvent(sdk.NewEvent(
types.EventTypeOutgoingBatch,
sdk.NewAttribute(sdk.AttributeKeyModule, k.moduleName),
sdk.NewAttribute(types.AttributeKeyOutgoingBatchNonce, fmt.Sprint(batch.BatchNonce)),
sdk.NewAttribute(types.AttributeKeyOutgoingTxIds, fmt.Sprint(batch.Transactions[0].Id)),
sdk.NewAttribute(types.AttributeKeyOutgoingBatchTimeout, fmt.Sprint(batch.BatchTimeout)),
))
return nil
}
Expand Down
5 changes: 0 additions & 5 deletions x/crosschain/types/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,6 @@ const (
AttributeKeyOutgoingBatchNonce = "batch_nonce"
AttributeKeyOutgoingBatchTimeout = "outgoing_batch_timeout"

EventTypeIbcTransfer = "ibc_transfer"
AttributeKeyIbcSendSequence = "ibc_send_sequence"
AttributeKeyIbcSourcePort = "ibc_source_port"
AttributeKeyIbcSourceChannel = "ibc_source_channel"

EventTypeEvmTransfer = "evm_transfer"

EventTypeBridgeCallEvent = "bridge_call_event"
Expand Down

0 comments on commit 9ec7942

Please sign in to comment.