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

Support Quark v2 in QuarkBuilder #83

Merged
merged 4 commits into from
Sep 24, 2024
Merged

Conversation

kevincheng96
Copy link
Contributor

@kevincheng96 kevincheng96 commented Sep 23, 2024

Adds nonceSecret and replayCount to QuarkBuilder. For replayable operations, the QuarkBuilder will also generate the nonce using the nonceSecret and replayCount (see generateNonceFromSecret function).

We also uncover and fix a bug where 0 is being used as the nonce for an operation when a nonce is not provided for an account by the client. Now, the code reverts if a nonceSecret is not given for an account. We buff up the unit tests to assert the right values are being used for the nonces, since nonces were being checked previously.

@kevincheng96 kevincheng96 changed the title Support new Quark v2 interfaces in QuarkBuilder Support Quark v2 in QuarkBuilder Sep 23, 2024
Comment on lines +300 to +307
// We need to set Bob as the withdrawer because only he has an account on chain 8453
cometWithdraw_({
chainId: 8453,
comet: cometUsdc_(8453),
assetSymbol: "LINK",
amount: 5e18,
withdrawer: address(0xb0b)
}),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Example of the nonce bug in action. Previously, we were withdrawing using 0xa11ce as the withdrawer on chain 8453, even though a nonce for 0xa11ce wasn't provided for chain 8453. Now, we have to set the withdrawer as 0xb0b since only he has a nonce on chain 8453.

@@ -182,16 +181,17 @@ library Accounts {
return findAssetPositions(assetAddress, chainAccounts.assetPositionsList);
}

function findQuarkState(address account, Accounts.QuarkState[] memory quarkStates)
function findQuarkSecret(address account, Accounts.QuarkSecret[] memory quarkSecrets)
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 need to do it this way? Wouldn't just "next random" work?

Copy link
Contributor Author

@kevincheng96 kevincheng96 Sep 24, 2024

Choose a reason for hiding this comment

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

Synced offline on this. I don't think we resolved this discussion yet, but here's my response:

I considered specifying nonceSecrets on a non-account basis, but I decided to keep it account-based for a few reasons.

  1. It conforms to the current interface that the client is passing in.
  2. It reduces the complexity of the QuarkBuilder having to pick out nonceSecrets and keep track of which ones are already assigned to which operations.
  3. It's kind of nice that this account-based approach guarantees there will always be enough nonceSecrets to construct the quark operations. It'll be one less thing that could go wrong to worry about in the QuarkBuilder

Copy link
Contributor

Choose a reason for hiding this comment

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

TBF, if we needed to do two operations on the same chain, this strategy wouldn't work? E.g. let's say we wanted to make two delayed operations on the same chain.

Copy link
Contributor Author

@kevincheng96 kevincheng96 Sep 24, 2024

Choose a reason for hiding this comment

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

If we have two operations on the same chain for the same account, we wrap those in a multicall operation. So having a single nonceSecret for an account on each chain is guaranteed to be sufficient.

EDIT: I see your point. If there are two operations that have to be separate, then this wouldn't work. Good point. I can work on the "next random" approach in a follow-up PR to see how complicated it is

@@ -58,6 +58,9 @@ library Actions {
uint256 constant SWAP_EXPIRY_BUFFER = 3 days;
uint256 constant TRANSFER_EXPIRY_BUFFER = 7 days;

/* replay counts */
uint256 constant RECURRING_SWAP_REPLAY_COUNT = 500;
Copy link
Contributor

Choose a reason for hiding this comment

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

I tend to think 500 is pretty high. We should just discuss as a product question generally

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should be easy to change this when we want to.

// operations, the `nonce` will be the `nonceSecret` (the hash chain has a length of 1)
bytes32 nonceSecret;
// The number of times an operation can be replayed. For non-replayable operations, this will be 0
uint256 replayCount;
Copy link
Contributor

Choose a reason for hiding this comment

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

On the backend, we've been more keen to use "totalPlays" which tends to map a little better since it doesn't have a discontinuity at 0.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Adjusted to totalPlays to be consistent. Both are fine imo.

@@ -245,6 +256,11 @@ library Actions {
address paymentToken;
string paymentTokenSymbol;
uint256 paymentMaxCost;
// The secret used to generate the hash chain for a replayable operation. For non-replayable
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we return nonce and nonceSecret? Generally we've been on "verify" mode on the client (as opposed to "generate"). That is, we run a check that hash_n(input.nonceSecret, input.totalPlays) == input.nonce

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The actual nonce is on the QuarkOperation, so I don't think we need to put it here.

@@ -1487,6 +1544,18 @@ library Actions {
return result;
}

function generateNonceFromSecret(bytes32 secret, uint256 replayCount) internal pure returns (bytes32) {
assembly ("memory-safe") {
Copy link
Contributor

Choose a reason for hiding this comment

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

cool-zone

pragma solidity ^0.8.27;

library ReplayableHelper {
function generateNonceFromSecret(bytes32 secret, uint256 replayCount) internal pure returns (bytes32) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Any reason not to use the optimized version above?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Mainly just because we don't need the optimized version here to save us from stack too deep. Also I thought it's kinda nice to have a more readable version so we can sanity check the optimized version in our tests with a different implementation.

@@ -182,16 +181,17 @@ library Accounts {
return findAssetPositions(assetAddress, chainAccounts.assetPositionsList);
}

function findQuarkState(address account, Accounts.QuarkState[] memory quarkStates)
function findQuarkSecret(address account, Accounts.QuarkSecret[] memory quarkSecrets)
Copy link
Contributor

Choose a reason for hiding this comment

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

TBF, if we needed to do two operations on the same chain, this strategy wouldn't work? E.g. let's say we wanted to make two delayed operations on the same chain.

@kevincheng96 kevincheng96 merged commit bf5b1aa into kevin/quark-v2 Sep 24, 2024
4 checks passed
@kevincheng96 kevincheng96 deleted the kevin/builder-v2 branch September 24, 2024 23:17
kevincheng96 added a commit that referenced this pull request Sep 25, 2024
Adds `nonceSecret` and `replayCount` to QuarkBuilder. For replayable
operations, the QuarkBuilder will also generate the nonce using the
`nonceSecret` and `replayCount` (see `generateNonceFromSecret`
function).

We also uncover and fix a bug where `0` is being used as the `nonce` for
an operation when a nonce is not provided for an account by the client.
Now, the code reverts if a `nonceSecret` is not given for an account. We
buff up the unit tests to assert the right values are being used for the
nonces, since nonces were being checked previously.
kevincheng96 added a commit that referenced this pull request Sep 25, 2024
Adds `nonceSecret` and `replayCount` to QuarkBuilder. For replayable
operations, the QuarkBuilder will also generate the nonce using the
`nonceSecret` and `replayCount` (see `generateNonceFromSecret`
function).

We also uncover and fix a bug where `0` is being used as the `nonce` for
an operation when a nonce is not provided for an account by the client.
Now, the code reverts if a `nonceSecret` is not given for an account. We
buff up the unit tests to assert the right values are being used for the
nonces, since nonces were being checked previously.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants