Skip to content

Commit

Permalink
Cancel Core Script
Browse files Browse the repository at this point in the history
This patch simply adds a cancel core script that can be used (a) as a nop to cancel a chain, or (b) to directly call `nonceManager.cancel(nonce)` with one or more nonces. These are merely given as helpers for the end-users.
  • Loading branch information
hayesgm committed Sep 12, 2024
1 parent c2aaa37 commit 070df39
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 29 deletions.
42 changes: 42 additions & 0 deletions src/quark-core-scripts/src/Cancel.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// SPDX-License-Identifier: BSD-3-Clause
pragma solidity 0.8.27;

import {IQuarkWallet} from "quark-core/src/QuarkWallet.sol";
import {QuarkNonceManager} from "quark-core/src/QuarkNonceManager.sol";

/**
* @title Cancel Core Script
* @notice Core transaction script that can be used to cancel quark operations.
* @author Compound Labs, Inc.
*/
contract Cancel {

/**
* @notice May cancel a script by being run as a no-op (no operation).
*/
function nop() external pure {}

/**
* @notice Cancels a script by calling into nonce manager to cancel the script's nonce.
* @param nonce The nonce of the quark operation to cancel (exhaust)
*/
function cancel(bytes32 nonce) external {
nonceManager().cancel(nonce);
}

/**
* @notice Cancels many scripts by calling into nonce manager to cancel each script's nonce.
* @param nonces A list of nonces of the quark operations to cancel (exhaust)
*/
function cancelMany(bytes32[] calldata nonces) external {
QuarkNonceManager manager = nonceManager();
for (uint256 i = 0; i < nonces.length; i++) {
bytes32 nonce = nonces[i];
manager.cancel(nonce);
}
}

function nonceManager() internal view returns (QuarkNonceManager) {
return QuarkNonceManager(IQuarkWallet(address(this)).nonceManager());
}
}
14 changes: 1 addition & 13 deletions test/lib/CancelOtherScript.sol → test/lib/CheckNonceScript.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,7 @@ pragma solidity 0.8.27;
import "quark-core/src/QuarkWallet.sol";
import "quark-core/src/QuarkScript.sol";

contract CancelOtherScript is QuarkScript {
event Nop();
event CancelNonce(bytes32 nonce);

function nop() public {
emit Nop();
}

function run(bytes32 nonce) public {
nonceManager().cancel(nonce);
emit CancelNonce(nonce);
}

contract CheckNonceScript is QuarkScript {
function checkNonce() public view returns (bytes32) {
return getActiveNonce();
}
Expand Down
20 changes: 16 additions & 4 deletions test/lib/QuarkOperationHelper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,18 @@ contract QuarkOperationHelper is Test {
);
}

function newBasicOpWithCalldata(
QuarkWallet wallet,
bytes memory scriptSource,
bytes memory scriptCalldata,
ScriptType scriptType,
bytes32 nonce
) public returns (QuarkWallet.QuarkOperation memory) {
return newBasicOpWithCalldata(
wallet, scriptSource, scriptCalldata, new bytes[](0), scriptType, nonce
);
}

function newBasicOpWithCalldata(
QuarkWallet wallet,
bytes memory scriptSource,
Expand Down Expand Up @@ -144,18 +156,18 @@ contract QuarkOperationHelper is Test {
returns (QuarkWallet.QuarkOperation memory)
{
return getCancelOperation(
wallet, semiRandomNonce(wallet), abi.encodeWithSignature("run(bytes32)", quarkOperation.nonce)
wallet, semiRandomNonce(wallet), abi.encodeWithSignature("cancel(bytes32)", quarkOperation.nonce)
);
}

function getCancelOperation(QuarkWallet wallet, bytes32 selfNonce, bytes memory callData)
public
returns (QuarkWallet.QuarkOperation memory)
{
bytes memory cancelOtherScript = new YulHelper().getCode("CancelOtherScript.sol/CancelOtherScript.json");
address scriptAddress = wallet.codeJar().saveCode(cancelOtherScript);
bytes memory cancelScript = new YulHelper().getCode("Cancel.sol/Cancel.json");
address scriptAddress = wallet.codeJar().saveCode(cancelScript);
bytes[] memory scriptSources = new bytes[](1);
scriptSources[0] = cancelOtherScript;
scriptSources[0] = cancelScript;
return QuarkWallet.QuarkOperation({
scriptAddress: scriptAddress,
scriptSources: scriptSources,
Expand Down
15 changes: 10 additions & 5 deletions test/quark-core/Noncer.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -357,21 +357,26 @@ contract NoncerTest is Test {
assertEq(replayCount, 2);
}

function testGetActiveReplayCountWithCancel() public {
function testGetActiveReplayCountWithNonReplayCancel() public {
// gas: do not meter set-up
vm.pauseGasMetering();
bytes memory noncerScript = new YulHelper().getCode("Noncer.sol/Noncer.json");
bytes memory checkNonceScript = new YulHelper().getCode("CheckNonceScript.sol/CheckNonceScript.json");
(QuarkWallet.QuarkOperation memory op, bytes32[] memory submissionTokens) = new QuarkOperationHelper()
.newReplayableOpWithCalldata(
aliceWallet, noncerScript, abi.encodeWithSignature("checkReplayCount()"), ScriptType.ScriptSource, 2
);
(uint8 v, bytes32 r, bytes32 s) = new SignatureHelper().signOp(alicePrivateKey, aliceWallet, op);

QuarkWallet.QuarkOperation memory cancelOp = new QuarkOperationHelper().getCancelOperation(
aliceWallet, op.nonce, abi.encodeWithSignature("checkReplayCount()")
QuarkWallet.QuarkOperation memory checkReplayCountOp = new QuarkOperationHelper().newBasicOpWithCalldata(
aliceWallet,
checkNonceScript,
abi.encodeWithSignature("checkReplayCount()"),
ScriptType.ScriptSource,
op.nonce
);
(uint8 cancelV, bytes32 cancelR, bytes32 cancelS) =
new SignatureHelper().signOp(alicePrivateKey, aliceWallet, cancelOp);
new SignatureHelper().signOp(alicePrivateKey, aliceWallet, checkReplayCountOp);

// gas: meter execute
vm.resumeGasMetering();
Expand All @@ -381,7 +386,7 @@ contract NoncerTest is Test {
assertEq(replayCount, 0);

result = aliceWallet.executeQuarkOperationWithSubmissionToken(
cancelOp, submissionTokens[1], cancelV, cancelR, cancelS
checkReplayCountOp, submissionTokens[1], cancelV, cancelR, cancelS
);

(replayCount) = abi.decode(result, (uint256));
Expand Down
12 changes: 5 additions & 7 deletions test/quark-core/QuarkWallet.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import {Incrementer} from "test/lib/Incrementer.sol";
import {PrecompileCaller} from "test/lib/PrecompileCaller.sol";
import {MaxCounterScript} from "test/lib/MaxCounterScript.sol";
import {GetMessageDetails} from "test/lib/GetMessageDetails.sol";
import {CancelOtherScript} from "test/lib/CancelOtherScript.sol";
import {CheckNonceScript} from "test/lib/CheckNonceScript.sol";

contract QuarkWalletTest is Test {
enum ExecutionType {
Expand Down Expand Up @@ -592,8 +592,6 @@ contract QuarkWalletTest is Test {
(uint8 cancelV, bytes32 cancelR, bytes32 cancelS) =
new SignatureHelper().signOp(alicePrivateKey, aliceWallet, cancelOtherOp);
vm.resumeGasMetering();
vm.expectEmit(true, true, true, true);
emit CancelOtherScript.Nop();
aliceWallet.executeQuarkOperationWithSubmissionToken(
cancelOtherOp, submissionTokens[1], cancelV, cancelR, cancelS
);
Expand Down Expand Up @@ -638,15 +636,15 @@ contract QuarkWalletTest is Test {

// can cancel the replayable nonce...
vm.pauseGasMetering();
QuarkWallet.QuarkOperation memory cancelOtherOp =
QuarkWallet.QuarkOperation memory cancelOp =
new QuarkOperationHelper().cancelReplayableByNewOp(aliceWallet, op);
(uint8 cancelV, bytes32 cancelR, bytes32 cancelS) =
new SignatureHelper().signOp(alicePrivateKey, aliceWallet, cancelOtherOp);
new SignatureHelper().signOp(alicePrivateKey, aliceWallet, cancelOp);
vm.resumeGasMetering();
vm.expectEmit(true, true, true, true);
emit CancelOtherScript.CancelNonce(op.nonce);
emit QuarkNonceManager.NonceCanceled(address(aliceWallet), op.nonce);
aliceWallet.executeQuarkOperationWithSubmissionToken(
cancelOtherOp, submissionTokens[1], cancelV, cancelR, cancelS
cancelOp, submissionTokens[1], cancelV, cancelR, cancelS
);

// and now you can no longer replay
Expand Down

0 comments on commit 070df39

Please sign in to comment.