Querier: Streaming Swap displayed without source or target asset
TLDR summary: The querier's queryStreamingSwap
and queryStreamingSwaps
aborts (with a break
and error log) the scan for a matching MsgSwap if there is no MsgSwap set for an earlier index
(due to a returned error when no MsgSwap set for that index),
when it should instead assume no MsgSwap set for that index and continue
.
(!3404 (merged) now submitted.)
The streaming swap in question:
https://thornode-v1.ninerealms.com/thorchain/swap/streaming/C79CB01ACB634CAA4A44E20B71C1ECA3F130C638981B60A80C99247DF2698F6C?height=14477924
{
"tx_id": "C79CB01ACB634CAA4A44E20B71C1ECA3F130C638981B60A80C99247DF2698F6C",
"interval": 1,
"quantity": 10,
"count": 1,
"last_height": 14477924,
"trade_target": "0",
"source_asset": ".",
"target_asset": ".",
"deposit": "3177005367",
"in": "317700536",
"out": "317367238"
}
By contrast, an instance with all displayed as expected:
https://thornode-v1.ninerealms.com/thorchain/swap/streaming/BB5F86143218A3D91C707931A9FB59FB5354B15C2412788BCCE53A48AE6856D6?height=14477924
The streaming swap certainly had a corresponding MsgSwap:
https://thornode-v1.ninerealms.com/thorchain/queue/swap?height=14477924
{
"tx": {
"id": "C79CB01ACB634CAA4A44E20B71C1ECA3F130C638981B60A80C99247DF2698F6C",
"chain": "THOR",
"from_address": "0xAd1c7335676f87919a80DeE56Ff852D51FA12242",
"to_address": "noop",
"coins": [
{
"asset": "THOR.ETH",
"amount": "3177005367"
}
],
"gas": [
{
"asset": "THOR.TOR",
"amount": "1"
}
],
"memo": "noop"
},
"target_asset": "ETH.ETH",
"destination": "0xAd1c7335676f87919a80DeE56Ff852D51FA12242",
"trade_target": "0",
"affiliate_basis_points": "0",
"signer": "thor1hpt4l30qgr3pugg2wdp4hmrv2g0qlg2z7z9m7h",
"stream_interval": 1
}
Single Coins, Coins Amount and Deposit identical (3177005367), MsgSwap stream_interval
> 0 so satisfying IsStreaming
.
No broken invariant for streaming swaps indicated in that block.
https://thornode-v1.ninerealms.com/thorchain/invariant/streaming_swaps?height=14477924
queryStreamingSwap
:
https://gitlab.com/thorchain/thornode/-/blob/v1.127.0/x/thorchain/querier.go#L1224-1244
var msgSwap MsgSwap
// Check up to the first two indices (0 through 1) for the MsgSwap; if not found, leave the fields blank.
for i := 0; i <= 1; i++ {
swapQueueItem, err := mgr.Keeper().GetSwapQueueItem(ctx, txid, i)
if err != nil {
ctx.Logger().Error("fail to get swap queue item", "error", err)
// If this errors, leave the MsgSwap-derived fields blank.
break
}
if !swapQueueItem.IsStreaming() {
continue
}
// In case there are multiple streaming swaps with the same TxID, check the input amount.
if len(swapQueueItem.Tx.Coins) == 0 || !swapQueueItem.Tx.Coins[0].Amount.Equal(streamingSwap.Deposit) {
continue
}
msgSwap = swapQueueItem
break
}
result := NewQueryStreamingSwap(streamingSwap, msgSwap)
NewQueryStreamingSwap
:
https://gitlab.com/thorchain/thornode/-/blob/v1.127.0/x/thorchain/types/querier.go#L794-815
var sourceAsset common.Asset
// Leave the source_asset field empty if there is more than a single input Coin.
if len(msgSwap.Tx.Coins) == 1 {
sourceAsset = msgSwap.Tx.Coins[0].Asset
}
return QueryStreamingSwap{
TxID: streamingSwap.TxID,
Interval: streamingSwap.Interval,
Quantity: streamingSwap.Quantity,
Count: streamingSwap.Count,
LastHeight: streamingSwap.LastHeight,
TradeTarget: streamingSwap.TradeTarget,
SourceAsset: sourceAsset,
TargetAsset: msgSwap.TargetAsset,
Destination: msgSwap.Destination,
Deposit: streamingSwap.Deposit,
In: streamingSwap.In,
Out: streamingSwap.Out,
FailedSwaps: streamingSwap.FailedSwaps,
FailedSwapReasons: streamingSwap.FailedSwapReasons,
}
This is the expected adding of the MsgSwap to the swap queue:
https://gitlab.com/thorchain/thornode/-/blob/v1.127.0/x/thorchain/handler_loan_repayment.go#L257
if err := h.mgr.Keeper().SetSwapQueueItem(ctx, *swapMsg, 1); err != nil {
In other words, index 1 expected.
--Ah.
GetSwapQueueItem
:
https://gitlab.com/thorchain/thornode/-/blob/v1.127.0/x/thorchain/keeper/v1/keeper_swap_queue.go#L48-51
ok, err := k.getMsgSwap(ctx, k.GetKey(ctx, prefixSwapQueueItem, fmt.Sprintf("%s-%d", txID.String(), i)), &record)
if !ok {
return record, errors.New("not found")
}
The intention was that the streaming swap would check the indices until it found a MsgSwap which satisfies it,
but since the lack of any MsgSwap returns an error which prompts a break
rather than a continue
, the loop terminates prematurely. \
I currently propose that the error log and break
on GetSwapQueueItem
error here be replaced by a continue
and explanatory comment (in queryStreamingSwaps
as well).
Specifically: !3404 (merged)
'Querier: continue
rather than break
on queryStreamingSwap/s GetSwapQueueItem error (expected when no MsgSwap set for that index)'.