offchain - resurrect multi usdc transfers per msg#633
Conversation
…ge (#632) ## Motivation ## Solution
|
I see you updated files related to core. Please run |
RensR
left a comment
There was a problem hiding this comment.
I feel like the test coverage around actually using multiple usdc tokens in a single msg is missing
Just updated the test to include this scenario. one msg 2 usdc tokens, one non-usdc token |
| usdcTokenIndex := (len(allUsdcTokensData) - 1) - usdcTokenIndexOffset | ||
|
|
||
| if usdcTokenIndex < 0 || usdcTokenIndex >= len(allUsdcTokensData) { | ||
| return nil, errors.Errorf("no USDC message found prior to log index %d, usdc token:%d in tx %s, usdcTokenIndex=%d", |
There was a problem hiding this comment.
IMO it's better to log errors with so many details because it's easier to search in Loki then. The returned error could be simplified then
| current := logs[len(logs)-i-1] | ||
| // collect the logs with log index less than the provided log index | ||
| allUsdcTokensData := make([][]byte, 0) | ||
| for _, current := range logs { |
There was a problem hiding this comment.
I think we miss tests covering this logic, no?
| } | ||
|
|
||
| func (u *USDCReaderImpl) GetLastUSDCMessagePriorToLogIndexInTx(ctx context.Context, logIndex int64, txHash string) ([]byte, error) { | ||
| func (u *USDCReaderImpl) GetUSDCMessagePriorToLogIndexInTx(ctx context.Context, logIndex int64, usdcTokenIndexOffset int, txHash string) ([]byte, error) { |
There was a problem hiding this comment.
Correct me if I'm wrong, but the only difference is that we fetch all the USDC logs from a single TX and the we pick the right one based on the usdcTokenIndexOffset, no?
IIUC, having a CCIP message with multiple USDC transfers will always fetch all the logs from a single TX (u.lp.IndexedLogsByTxHash), pick the correct one, and then discard the rest of them. Also, it would be done multiple times, right?
There was a problem hiding this comment.
Very nice observation, we can improve it as a follow up, by passing a list of tokens probably.
I would prefer not to include it here as it requires changes in several places not related to this change.
There was a problem hiding this comment.
Sounds good, please make sure that ticket for that followup is created ;)
| // if usdcTokenIndexOffset is 0 we select usdc2 | ||
| // if usdcTokenIndexOffset is 1 we select usdc1 | ||
| // The message logs are found using the provided transaction hash. | ||
| GetUSDCMessagePriorToLogIndexInTx(ctx context.Context, logIndex int64, usdcTokenIndexOffset int, txHash string) ([]byte, error) |
There was a problem hiding this comment.
PriorToLogIndex? IMO it's more like GetUSDCMessageBasedOnTheLogIndex
There was a problem hiding this comment.
I don't like both names :D Let's rename it in the follow-up with the performance improvement.
There was a problem hiding this comment.
GetUSDCMessageByLogIndex? This is the convention we have in LogPoller (but not in all places :P). PriorToIndex is very misleading to me, because it sounds like "give me all logs from [0, tokenIndex]"
There was a problem hiding this comment.
Message singular and PriorToIndex implies the last singular log before the given index 🤷🏼
| return nil, fmt.Errorf("invalid token index %d for msg with %d tokens", tokenIndex, len(msg.TokenAmounts)) | ||
| } | ||
|
|
||
| offsetFromLastUsdcToken := 0 |
There was a problem hiding this comment.
I would extract that to a separate method that just returns a proper index of usdc token
| // GetLastUSDCMessagePriorToLogIndexInTx returns the last USDC message that was sent | ||
| // before the provided log index in the given transaction. | ||
| GetLastUSDCMessagePriorToLogIndexInTx(ctx context.Context, logIndex int64, txHash string) ([]byte, error) | ||
| // GetUSDCMessagePriorToLogIndexInTx returns the specified USDC message data. |
There was a problem hiding this comment.
Correct me if I'm wrong, but it works as follows
for i := range msg.tokens {
if usdc {
log := fetchLogFromDatabase(i)
attestation := fetchAttestation(log, i)
}
}This means that 100 usdc messages will equal 100 database calls, right?
Whereas we could be more efficient here probably by doing sth like this. This assumes that all logs are in the same TX, I'm not sure if this is a valid assumption
// grouping by type first
usdcMsgs := []msgs
for i := range msg.tokens {
if usdc {
usdcMsgs = append(usdcMsgs, msg.tokens[i])
}
}
logs := fetchLogFromDatabase()
for i:= range usdcMessages {
fetchLogFromDatabase(logs[i]. i)
}There was a problem hiding this comment.
sounds like a nice improvement, we can do as a follow up since it requires more interface changes that I don't want to include in this pr
There was a problem hiding this comment.
While nice in theory, the odds of multiple usdc txs in 1 tx is so low it's totally fine to punt imo
| func (s *TokenDataReader) getUSDCMessageBody( | ||
| ctx context.Context, | ||
| msg cciptypes.EVM2EVMOnRampCCIPSendRequestedWithMeta, | ||
| tokenIndex int, |
There was a problem hiding this comment.
Why isnt this a uint, it should never be <0 correct?
There was a problem hiding this comment.
A bit less casting for comparisons e.g. with len. Imo not really important
## Knowledge-sharing for reviewers
1. For each token of a msg we find the token pool and call lock_or_burn.
2. For USDC pool, the usdc contract we call emits a `UsdcMessageSent`
event.
3. This events are picked up by the log poller.
## The problem
For a given ccip msg (tx hash, log index) we called:
```
usdcreader.GetLastUSDCMessagePriorToLogIndexInTx(logIndex, txHash)
```
But this only gives us the `Last` usdc message log.
A ccip message can contain multiple usdc token transfers with one log
emitted for each.
## The fix
I am updating the method above to also accept `usdcTokenIndexOffset`
```
usdcreader.GetUSDCMessagePriorToLogIndexInTx(logIndex, usdcTokenIndexOffset, txHash) ([]byte, error)
```
Here is an example:
```
msg.tokenAmounts = [
{token: "0xusdc", amount: 12} // offset=2
{token: "0xSomehingElse", amount: 3123}
{token: "0xusdc", amount: 14} // offset=1
{token: "0xusdc", amount: 114} // offset=0 (that's what we would return without this fix)
]
```
The reason that we essentially ignore non-usdc tokens is that for a
message containing multiple usdc transfers the logs returned by the
logPoller do not contain non-usdc logs, so we need to have that offset
to pick the correct log.
---------
Co-authored-by: AnieeG <anindita.ghosh@smartcontract.com>
Co-authored-by: Anindita Ghosh <88458927+AnieeG@users.noreply.github.com>
Knowledge-sharing for reviewers
UsdcMessageSentevent.The problem
For a given ccip msg (tx hash, log index) we called:
But this only gives us the
Lastusdc message log.A ccip message can contain multiple usdc token transfers with one log emitted for each.
The fix
I am updating the method above to also accept
usdcTokenIndexOffsetHere is an example:
The reason that we essentially ignore non-usdc tokens is that for a message containing multiple usdc transfers the logs returned by the logPoller do not contain non-usdc logs, so we need to have that offset to pick the correct log.