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 issues relating to race conditions where draftTx does not exist #15420

Merged
merged 2 commits into from Aug 2, 2022

Conversation

brad-decker
Copy link
Member

@brad-decker brad-decker commented Aug 2, 2022

Explanation

This PR adds additional safety to the send duck when draftTransaction is not present. It does this in a few ways:

  1. Only attempts to update draftTransaction.status if a draftTransaction exists. To make sure we reflect the appropriate status we also return 'true' for isSendFormInvalid when draftTransaction does not exist.
  2. In the case for 'initializeSendState.fulfilled' we only update draftTransaction values if it exists, but the very possibility that we hit this fulfilled condition without a valid draftTransaction implies that we have a race condition, so...
  3. An additional failsafe is added to the end of the initialize thunk that re-checks state to see if the draftTransaction has changed. This check happens after the async methods that are awaited in this initialize method.

More Information

Fixes #15403
Fixes #15401

Screenshots/Screencaps

Before

After

Manual Testing Steps

Pre-Merge Checklist

  • PR template is filled out
  • IF this PR fixes a bug, a test that would have caught the bug has been added
  • PR is linked to the appropriate GitHub issue
  • PR has been added to the appropriate release Milestone

+ If there are functional changes:

  • Manual testing complete & passed
  • "Extension QA Board" label has been applied

Comment on lines +606 to 608
return thunkApi.rejectWithValue(
'draftTransaction not found, possibly not on send flow',
);
Copy link
Member Author

Choose a reason for hiding this comment

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

without returning this the rest of the code in this slice would run, this is unwanted. Returning here prevents attempts to access draftTransaction erroring out further in this code. Although in this case the error would be handled by redux and would not bubble up to sentry.

Comment on lines +1399 to +1422
if (draftTransaction) {
switch (true) {
case Boolean(draftTransaction.amount.error):
case Boolean(draftTransaction.gas.error):
case Boolean(draftTransaction.asset.error):
case draftTransaction.asset.type === ASSET_TYPES.TOKEN &&
draftTransaction.asset.details === null:
case state.stage === SEND_STAGES.ADD_RECIPIENT:
case state.stage === SEND_STAGES.INACTIVE:
case state.gasEstimateIsLoading:
case new BigNumber(draftTransaction.gas.gasLimit, 16).lessThan(
new BigNumber(state.gasLimitMinimum),
):
draftTransaction.status = SEND_STATUSES.INVALID;
break;
case draftTransaction.recipient.warning === 'loading':
case draftTransaction.recipient.warning ===
KNOWN_RECIPIENT_ADDRESS_WARNING &&
draftTransaction.recipient.recipientWarningAcknowledged === false:
draftTransaction.status = SEND_STATUSES.INVALID;
break;
default:
draftTransaction.status = SEND_STATUSES.VALID;
}
Copy link
Member Author

Choose a reason for hiding this comment

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

Only change the validity if the draftTransaction exists.

Comment on lines +2648 to +2652
const draftTransaction = getCurrentDraftTransaction(state);
if (!draftTransaction) {
return true;
}
return draftTransaction.status === SEND_STATUSES.INVALID;
Copy link
Member Author

Choose a reason for hiding this comment

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

If the draftTransaction does not exist, its invalid.

Comment on lines +1517 to +1531
if (draftTransaction) {
draftTransaction.gas.gasLimit = action.payload.gasLimit;
draftTransaction.gas.gasTotal = action.payload.gasTotal;
if (action.payload.chainHasChanged) {
// If the state was reinitialized as a result of the user changing
// the network from the network dropdown, then the selected asset is
// no longer valid and should be set to the native asset for the
// network.
draftTransaction.asset.type = ASSET_TYPES.NATIVE;
draftTransaction.asset.balance =
draftTransaction.fromAccount?.balance ??
state.selectedAccount.balance;
draftTransaction.asset.details = null;
}
}
Copy link
Member Author

Choose a reason for hiding this comment

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

So the only way this code should be hit is if there was an inflight attempt to initialize the send state, which at the time had a valid draftTransaction, and then the send flow is reset between when the initialize is fulfilled and when redux runs this case the only way this could be done (to my knowledge) is by changing networks in the send flow and canceling and hope you get lucky to time it right?

@PeterYinusa PeterYinusa added this to the v10.18.2 milestone Aug 2, 2022
Comment on lines +681 to +693

// There may be a case where the send has been canceled by the user while
// the gas estimate is being computed. So we check again to make sure that
// a currentTransactionUUID exists and matches the previous tx.
const newState = thunkApi.getState();
if (
newState.send.currentTransactionUUID !== sendState.currentTransactionUUID
) {
return thunkApi.rejectWithValue(
`draftTransaction changed during initialization.
A new initializeSendState action must be dispatched.`,
);
}
Copy link
Member Author

Choose a reason for hiding this comment

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

This should hopefully eliminate the race condition, or make it very difficult to hit.

@brad-decker brad-decker marked this pull request as ready for review August 2, 2022 17:40
@brad-decker brad-decker requested a review from a team as a code owner August 2, 2022 17:40
@brad-decker brad-decker requested a review from danjm August 2, 2022 17:40
Copy link
Contributor

@segun segun left a comment

Choose a reason for hiding this comment

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

LGTM

@metamaskbot
Copy link
Collaborator

Builds ready [2e5d11d]
Page Load Metrics (1721 ± 43 ms)
PlatformPageMetricMin (ms)Max (ms)Average (ms)StandardDeviation (ms)MarginOfError (ms)
ChromeHomefirstPaint881626182332159
domContentLoaded1592183416997435
load1592195617218943
domInteractive1592183416997435

highlights:

storybook

@brad-decker brad-decker merged commit f425e48 into develop Aug 2, 2022
@brad-decker brad-decker deleted the fix-send-flow-issues branch August 2, 2022 18:52
@github-actions github-actions bot locked and limited conversation to collaborators Aug 2, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
5 participants