Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: drop incompatible orders #94

Merged
merged 7 commits into from
Oct 9, 2023
Merged

fix: drop incompatible orders #94

merged 7 commits into from
Oct 9, 2023

Conversation

mfw78
Copy link
Contributor

@mfw78 mfw78 commented Oct 5, 2023

Description

Currently incompatible orders (where there is a low level reversion that's not expected) are NOT deleted. This corrects that.

Changes

  • Drop orders that return non-compatible interface revert messages.

How to test

  1. Sync goerli from genesis and confirm no unexpected errors.

Related Issues

Fixes #91

@mfw78 mfw78 added the bug Something isn't working label Oct 5, 2023
@mfw78 mfw78 requested review from anxolin, ahhda and a team October 5, 2023 06:39
@mfw78 mfw78 self-assigned this Oct 5, 2023
@mfw78 mfw78 force-pushed the fix-watch-dog-timeout branch from 30c8d59 to f994854 Compare October 6, 2023 01:45
@mfw78 mfw78 force-pushed the fix-drop-incompatible-orders branch from 32b2717 to 4c2a197 Compare October 6, 2023 01:45
src/domain/checkForAndPlaceOrder.ts Show resolved Hide resolved
return {
result: PollResultCode.UNEXPECTED_ERROR,
result: PollResultCode.DONT_TRY_AGAIN,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is a good idea. catching ANY error thrown in the block above and just stop indexing the order might be a source of bugs.

Can't we handle it in a different way?

Maybe could be usefult to extract the error handling to a testable function and create some tests. This way, we can make sure we capture the cases you want to capture and there's no future regressions.

Also, by making the error handling independent, it will make checkForAndPlaceOrders thinner, which would be a good thing

Alternativelly, we could move the whole legacy polling and do the same. This in fact is something that is good is independent, since it should be moved to the SDK. Watch Tower logic should be simpler

Copy link
Contributor Author

@mfw78 mfw78 Oct 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only code path here (yes, it should be tested) that should throw and result in invalidating the order, is customErrorDecode that solely depends on only ABI decoding. All other errors (ethers-js / RPC provider etc), should be routed to the subsequent PollResultCode.TRY_NEXT_BLOCK).

We need to kill these orders from the registry as they will always fail, and they haven't complied with the interface. I'm all for refactoring / moving things after we have observability and stability in prod - including not spamming with error logs in some chains because of incompatible interfaces that are from the initial development versions of ComposableCoW on Goerli.

So, our risks are primarily around killing orders from monitoring that shouldn't be killed.

Therefore I propose we offset this with:

  1. Comprehensive logging. We have ELK in place, and much work has been done with logging to ensure great visibility.
  2. Comprehensive metrics. In chore: metrics todo and naming #95 the order tuple (chainId, handler, owner, id) is labelled against the watch_tower_polling_onchain_invalid_interface_total metric. Therefore we can see when orders are getting dropped due to this, and investigate to ensure compliance.
  3. We do some unit testing - test: unit coverage for poll legacy #97 - and validate while in prod.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only code path here (yes, it should be tested) that should throw and result in invalidating the order, is customErrorDecode that solely depends on only ABI decoding

I would hope we structure it so typescript already can check this assumption

Comprehensive logging. We have ELK in place, and much work has been done with logging to ensure great visibility.
Comprehensive metrics. In #95 the order tuple (chainId, handler, owner, id) is labelled against the watch_tower_polling_onchain_invalid_interface_total metric. Therefore we can see when orders are getting dropped due to this, and investigate to ensure compliance.

I think these don't solve the problem. I think it should be impossible to remove an order if there's some other error that is not the decoding. Typescript can probably help with these at compile time

We do some unit testing - #97 - and validate while in prod.

This will help indeed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've gone through and implemented everything with very strong typing now to enforce the assumptions. Some tests have been added as well.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks better, but you are still not exhaustive. You can't be. Because you use parsedCustomError.selector in the switch, you can't know all selectors. You need to handle an unknown selector, but your code now assumes its impossible that will happen

Copy link
Contributor Author

@mfw78 mfw78 Oct 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The revert data is parsed in parseCustomError to ensure that it complies with the ComposableCoW interface. If it does not, it will throw. Therefore the switch is exhaustive as only valid selectors reach that point in the execution path.

Invalid selectors (ie. that do not comply with the interface that we have prescribed), are caught in handleOnChainCustomError from the parseCustomError throw, dropped, and metrics are in place to monitor this.

@mfw78 mfw78 force-pushed the fix-watch-dog-timeout branch from f994854 to 88b4088 Compare October 6, 2023 11:59
@mfw78 mfw78 force-pushed the fix-drop-incompatible-orders branch from 4c2a197 to 4adace1 Compare October 6, 2023 12:08
@mfw78 mfw78 requested review from anxolin and alfetopito October 6, 2023 14:02
src/domain/checkForAndPlaceOrder.ts Outdated Show resolved Hide resolved
src/domain/checkForAndPlaceOrder.ts Outdated Show resolved Hide resolved
src/domain/checkForAndPlaceOrder.ts Outdated Show resolved Hide resolved
@mfw78 mfw78 force-pushed the fix-watch-dog-timeout branch from cc5126c to ea88ecc Compare October 6, 2023 22:26
Base automatically changed from fix-watch-dog-timeout to main October 6, 2023 22:31
@mfw78 mfw78 force-pushed the fix-drop-incompatible-orders branch from 8cae622 to 7cadfe9 Compare October 6, 2023 22:33
@mfw78 mfw78 force-pushed the fix-drop-incompatible-orders branch 2 times, most recently from 25ce427 to 6c726bc Compare October 7, 2023 03:18
@mfw78 mfw78 force-pushed the fix-drop-incompatible-orders branch from 6c726bc to 034e35b Compare October 7, 2023 03:23
Comment on lines +33 to +52
type ParsedCustomError = {
[K in CustomErrorSelectors]: K extends
| CustomErrorSelectors.PROOF_NOT_AUTHED
| CustomErrorSelectors.SINGLE_ORDER_NOT_AUTHED
| CustomErrorSelectors.INTERFACE_NOT_SUPPORTED
| CustomErrorSelectors.INVALID_FALLBACK_HANDLER
| CustomErrorSelectors.INVALID_HANDLER
| CustomErrorSelectors.SWAP_GUARD_RESTRICTED
? { selector: K }
: K extends
| CustomErrorSelectors.ORDER_NOT_VALID
| CustomErrorSelectors.POLL_TRY_NEXT_BLOCK
| CustomErrorSelectors.POLL_NEVER
? { selector: K; message: string }
: K extends
| CustomErrorSelectors.POLL_TRY_AT_BLOCK
| CustomErrorSelectors.POLL_TRY_AT_EPOCH
? { selector: K; message: string; blockNumberOrEpoch: number }
: never;
}[CustomErrorSelectors];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤯

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree, this feels too complicated. What is this type needed for?

Why not the function just returns a PollResultErrors so you also simplify the othe switch.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This type covers the exact interface for all ComposableCoW-compatible orders (as returned from ABI on-chain). We control this interface, and therefore we can assert that only these revert types are valid. If an order fails to adhere to this interface, it is to be dropped.

Typechain does not do generation of typings for solidity custom error reverts (despite them being in the ABI), and therefore, we've basically had to implement this ourselves, which is essentially what this type is.

src/utils/contracts.ts Outdated Show resolved Hide resolved
return {
selector,
message: msg as string,
blockNumberOrEpoch: (blockNumberOrEpoch as BigNumber).toNumber(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we really need this hard casting of types?

Copy link
Contributor Author

@mfw78 mfw78 Oct 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved the explicit hard casting to be on the same line as the decode. The hard casting is required because when ABI decoding in ethers v5, the return type is Result, whose interface is:

export interface Result extends ReadonlyArray<any> {
    readonly [key: string]: any;
}

IMO we should always hard cast from a decode if the decode is returning Result, as this increases the readability of the code, and enforces that Typescript catches more potential issues.

src/utils/contracts.ts Outdated Show resolved Hide resolved
@mfw78 mfw78 merged commit 5e3c760 into main Oct 9, 2023
3 checks passed
@mfw78 mfw78 deleted the fix-drop-incompatible-orders branch October 9, 2023 23:08
@github-actions github-actions bot locked and limited conversation to collaborators Oct 9, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging this pull request may close these issues.

bug: low level calls cause log spamming if not adhering to the interface
3 participants