From aeb868101a73d0bf287b3d3f4e573ece161c7682 Mon Sep 17 00:00:00 2001 From: Kevin Cheng Date: Wed, 16 Oct 2024 19:10:29 -0700 Subject: [PATCH] CS5.3 - Prevent nested operations originating from outside contracts (#231) This disallows contracts that are not the wallet itself from executing a nested Quark operation within the context of a parent Quark operation. This offers some protection for scripts that are making external calls. We still allow for nesting of Quark operations, with the caveat that the nested call must come from the Quark wallet itself. --- .gas-snapshot | 292 +++++++++++++++-------------- src/quark-core/src/QuarkWallet.sol | 10 + test/lib/ExecuteOtherOperation.sol | 7 + test/quark-core/QuarkWallet.t.sol | 64 +++++++ 4 files changed, 228 insertions(+), 145 deletions(-) diff --git a/.gas-snapshot b/.gas-snapshot index fc449a7c..41ab1ebc 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -1,20 +1,20 @@ -BatchCallbackTest:testBatchCallWithCallback() (gas: 114394) -BatchExecutorTest:testBatchExecuteWithPartialFailures() (gas: 121190) -BatchExecutorTest:testBatchExecuteWithPartialFailuresDoesNotRevertIfAnyCallsRevert() (gas: 99392) -BatchExecutorTest:testBatchExecuteWithoutPartialFailures() (gas: 121498) -BatchExecutorTest:testBatchExecuteWithoutPartialFailuresRevertsIfAnyCallsRevert() (gas: 96708) -CallbacksTest:testAllowNestedCallbacks() (gas: 116718) -CallbacksTest:testCallbackFromCounter() (gas: 69315) -CallbacksTest:testDelegatecallReentrancyExploitWithUnprotectedScript() (gas: 70828) -CallbacksTest:testDelegatecallReentrancyProtectionWithProtectedScript() (gas: 113962) -CallbacksTest:testNestedCallWithNoCallbackSucceeds() (gas: 109977) -CallbacksTest:testNestedCallbackResetsCallbackSlot() (gas: 87577) -CallbacksTest:testPayableCallback() (gas: 76848) -CallbacksTest:testReentrancyGuardProtectsAgainstDoubleDipping() (gas: 54263) -CallbacksTest:testRevertsOnCallbackWhenNoActiveCallback() (gas: 72521) -CallbacksTest:testSimpleCallback() (gas: 284414) -CallbacksTest:testWithClearedCallback() (gas: 156289) -CallbacksTest:testWithoutAllowCallback() (gas: 156058) +BatchCallbackTest:testBatchCallWithCallback() (gas: 114554) +BatchExecutorTest:testBatchExecuteWithPartialFailures() (gas: 121350) +BatchExecutorTest:testBatchExecuteWithPartialFailuresDoesNotRevertIfAnyCallsRevert() (gas: 99552) +BatchExecutorTest:testBatchExecuteWithoutPartialFailures() (gas: 121658) +BatchExecutorTest:testBatchExecuteWithoutPartialFailuresRevertsIfAnyCallsRevert() (gas: 96868) +CallbacksTest:testAllowNestedCallbacks() (gas: 116999) +CallbacksTest:testCallbackFromCounter() (gas: 69395) +CallbacksTest:testDelegatecallReentrancyExploitWithUnprotectedScript() (gas: 70908) +CallbacksTest:testDelegatecallReentrancyProtectionWithProtectedScript() (gas: 114122) +CallbacksTest:testNestedCallWithNoCallbackSucceeds() (gas: 110258) +CallbacksTest:testNestedCallbackResetsCallbackSlot() (gas: 87858) +CallbacksTest:testPayableCallback() (gas: 76928) +CallbacksTest:testReentrancyGuardProtectsAgainstDoubleDipping() (gas: 54343) +CallbacksTest:testRevertsOnCallbackWhenNoActiveCallback() (gas: 72601) +CallbacksTest:testSimpleCallback() (gas: 284574) +CallbacksTest:testWithClearedCallback() (gas: 156369) +CallbacksTest:testWithoutAllowCallback() (gas: 156138) CodeJarTest:testCodeJarCanBeWacky() (gas: 114646) CodeJarTest:testCodeJarCanDeployCodeThatHadEthSent() (gas: 6153300) CodeJarTest:testCodeJarCounter() (gas: 466199) @@ -31,67 +31,67 @@ CodeJarTest:testCodeJarStoresSelfReference() (gas: 121367) CodeJarTest:testCodeJarTickCounter() (gas: 198471) CodeJarTest:testRevertsOnConstructorRevert() (gas: 85038) CodeJarTest:testRevertsOnSelfDestructingConstructor() (gas: 53921) -ConditionalMulticallTest:testConditionalRunEmptyInputIsValid() (gas: 45865) -ConditionalMulticallTest:testConditionalRunInvalidInput() (gas: 45964) -ConditionalMulticallTest:testConditionalRunMulticallError() (gas: 289737) -ConditionalMulticallTest:testConditionalRunOnPeriodicRepay() (gas: 280663) -ConditionalMulticallTest:testConditionalRunPassed() (gas: 267880) -ConditionalMulticallTest:testConditionalRunUnmet() (gas: 77771) -EIP1271Test:testReturnsMagicValueForValidSignature() (gas: 69418) +ConditionalMulticallTest:testConditionalRunEmptyInputIsValid() (gas: 45945) +ConditionalMulticallTest:testConditionalRunInvalidInput() (gas: 46044) +ConditionalMulticallTest:testConditionalRunMulticallError() (gas: 289817) +ConditionalMulticallTest:testConditionalRunOnPeriodicRepay() (gas: 281063) +ConditionalMulticallTest:testConditionalRunPassed() (gas: 267960) +ConditionalMulticallTest:testConditionalRunUnmet() (gas: 77851) +EIP1271Test:testReturnsMagicValueForValidSignature() (gas: 69498) EIP1271Test:testRevertsIfSignerContractDoesNotReturnMagic() (gas: 16856) -EIP712Test:testExecuteQuarkOperation() (gas: 71078) -EIP712Test:testRequirements() (gas: 165882) +EIP712Test:testExecuteQuarkOperation() (gas: 71158) +EIP712Test:testRequirements() (gas: 166122) EIP712Test:testRevertBadRequirements() (gas: 24437) EIP712Test:testRevertsForBadCalldata() (gas: 19763) EIP712Test:testRevertsForBadCode() (gas: 21547) EIP712Test:testRevertsForBadExpiry() (gas: 20071) EIP712Test:testRevertsForExpiredSignature() (gas: 12635) EIP712Test:testRevertsInvalidS() (gas: 16718) -EIP712Test:testRevertsOnReusedNonce() (gas: 83169) +EIP712Test:testRevertsOnReusedNonce() (gas: 83249) EIP712Test:testStructHash() (gas: 9223372036854754743) -EthcallTest:testEthcallCallReraiseError() (gas: 54244) -EthcallTest:testEthcallCounter() (gas: 66035) -EthcallTest:testEthcallShouldReturnCallResult() (gas: 46851) -EthcallTest:testEthcallSupplyUSDCToComet() (gas: 154684) -EthcallTest:testEthcallWithdrawUSDCFromComet() (gas: 308212) -ExecutorTest:testExecutorCanDirectCall() (gas: 100328) -ExecutorTest:testExecutorCanDirectCallBySig() (gas: 104309) -MulticallTest:testCreateSubWalletAndExecute() (gas: 603114) -MulticallTest:testDelegatecallToMulticallSucceedsWhenInitialized() (gas: 74146) -MulticallTest:testEmptyInputIsValid() (gas: 45510) -MulticallTest:testExecutorCanMulticallAcrossSubwallets() (gas: 252563) -MulticallTest:testInvalidInput() (gas: 45724) -MulticallTest:testInvokeCounterTwice() (gas: 74080) -MulticallTest:testMulticallError() (gas: 282601) -MulticallTest:testMulticallShouldReturnCallResults() (gas: 76569) +EthcallTest:testEthcallCallReraiseError() (gas: 54324) +EthcallTest:testEthcallCounter() (gas: 66115) +EthcallTest:testEthcallShouldReturnCallResult() (gas: 46931) +EthcallTest:testEthcallSupplyUSDCToComet() (gas: 154844) +EthcallTest:testEthcallWithdrawUSDCFromComet() (gas: 308452) +ExecutorTest:testExecutorCanDirectCall() (gas: 100488) +ExecutorTest:testExecutorCanDirectCallBySig() (gas: 104469) +MulticallTest:testCreateSubWalletAndExecute() (gas: 603354) +MulticallTest:testDelegatecallToMulticallSucceedsWhenInitialized() (gas: 74226) +MulticallTest:testEmptyInputIsValid() (gas: 45590) +MulticallTest:testExecutorCanMulticallAcrossSubwallets() (gas: 252883) +MulticallTest:testInvalidInput() (gas: 45804) +MulticallTest:testInvokeCounterTwice() (gas: 74160) +MulticallTest:testMulticallError() (gas: 282681) +MulticallTest:testMulticallShouldReturnCallResults() (gas: 76649) MulticallTest:testRevertsForInvalidCallContext() (gas: 11543) -MulticallTest:testSupplyWETHWithdrawUSDCOnComet() (gas: 247506) -NoncerTest:testGetActiveNonceNested() (gas: 88365) -NoncerTest:testGetActiveNonceReplayable() (gas: 65570) -NoncerTest:testGetActiveNonceSingle() (gas: 44881) -NoncerTest:testGetActiveReplayCount() (gas: 85199) -NoncerTest:testGetActiveReplayCountNested() (gas: 88872) -NoncerTest:testGetActiveReplayCountSingle() (gas: 44744) -NoncerTest:testGetActiveReplayCountWithNonReplaySoftCancel() (gas: 69246) -NoncerTest:testGetActiveSubmissionTokenNested() (gas: 87871) -NoncerTest:testGetActiveSubmissionTokenReplayable() (gas: 65596) -NoncerTest:testGetActiveSubmissionTokenSingle() (gas: 47685) -NoncerTest:testNestedPlayPullingActiveReplayCount() (gas: 608236) -NoncerTest:testPostNestReadsCorrectValue() (gas: 141087) +MulticallTest:testSupplyWETHWithdrawUSDCOnComet() (gas: 247586) +NoncerTest:testGetActiveNonceNested() (gas: 88525) +NoncerTest:testGetActiveNonceReplayable() (gas: 65730) +NoncerTest:testGetActiveNonceSingle() (gas: 44961) +NoncerTest:testGetActiveReplayCount() (gas: 85439) +NoncerTest:testGetActiveReplayCountNested() (gas: 89032) +NoncerTest:testGetActiveReplayCountSingle() (gas: 44824) +NoncerTest:testGetActiveReplayCountWithNonReplaySoftCancel() (gas: 69406) +NoncerTest:testGetActiveSubmissionTokenNested() (gas: 88031) +NoncerTest:testGetActiveSubmissionTokenReplayable() (gas: 65756) +NoncerTest:testGetActiveSubmissionTokenSingle() (gas: 47765) +NoncerTest:testNestedPlayPullingActiveReplayCount() (gas: 608396) +NoncerTest:testPostNestReadsCorrectValue() (gas: 141247) PaycallTest:testInitializeProperlyFromConstructor() (gas: 6368) -PaycallTest:testPaycallForPayWithUSDT() (gas: 116485) -PaycallTest:testPaycallForPayWithWBTC() (gas: 109625) -PaycallTest:testPaycallRevertsWhenCallReverts() (gas: 50463) -PaycallTest:testReturnCallResult() (gas: 86367) +PaycallTest:testPaycallForPayWithUSDT() (gas: 116565) +PaycallTest:testPaycallForPayWithWBTC() (gas: 109705) +PaycallTest:testPaycallRevertsWhenCallReverts() (gas: 50543) +PaycallTest:testReturnCallResult() (gas: 86447) PaycallTest:testRevertsForInvalidCallContext() (gas: 15789) -PaycallTest:testRevertsWhenCostIsMoreThanMaxPaymentCost() (gas: 96275) -PaycallTest:testSimpleCounterAndPayWithUSDC() (gas: 137534) -PaycallTest:testSimpleTransferTokenAndPayWithUSDC() (gas: 138888) -PaycallTest:testSupplyWETHWithdrawUSDCOnCometAndPayWithUSDC() (gas: 290658) -QuarkFactoryTest:testInvariantAddressesBetweenNonces() (gas: 3334036) -QuarkFactoryTest:testQuarkFactoryDeployToDeterministicAddresses() (gas: 3333767) -QuarkFactoryTest:testQuarkFactoryDeployTwice() (gas: 3364699) -QuarkMinimalProxyTest:testSignerExecutor() (gas: 61413) +PaycallTest:testRevertsWhenCostIsMoreThanMaxPaymentCost() (gas: 96355) +PaycallTest:testSimpleCounterAndPayWithUSDC() (gas: 137614) +PaycallTest:testSimpleTransferTokenAndPayWithUSDC() (gas: 138968) +PaycallTest:testSupplyWETHWithdrawUSDCOnCometAndPayWithUSDC() (gas: 290738) +QuarkFactoryTest:testInvariantAddressesBetweenNonces() (gas: 3343501) +QuarkFactoryTest:testQuarkFactoryDeployToDeterministicAddresses() (gas: 3343232) +QuarkFactoryTest:testQuarkFactoryDeployTwice() (gas: 3374202) +QuarkMinimalProxyTest:testSignerExecutor() (gas: 61493) QuarkNonceManagerTest:testCancelChain() (gas: 49553) QuarkNonceManagerTest:testCancelExhaustedIsNoOp() (gas: 40796) QuarkNonceManagerTest:testChangingReplayableness() (gas: 38431) @@ -107,94 +107,96 @@ QuarkNonceManagerTest:testRevertsIfNonceIsAlreadySet() (gas: 36842) QuarkNonceManagerTest:testRevertsIfSubmittingNonMatchingNonceForNonReplayable() (gas: 19507) QuarkNonceManagerTest:testSingleUseRandomValidNonce() (gas: 45067) QuarkWalletProxyFactoryTest:testCreateAdditionalWalletWithSalt() (gas: 221800) -QuarkWalletProxyFactoryTest:testCreateAndExecuteCreatesWallet() (gas: 497400) -QuarkWalletProxyFactoryTest:testCreateAndExecuteMultiCreatesWallet() (gas: 551228) -QuarkWalletProxyFactoryTest:testCreateAndExecuteMultiWithSalt() (gas: 555589) -QuarkWalletProxyFactoryTest:testCreateAndExecuteSetsMsgSender() (gas: 217577) -QuarkWalletProxyFactoryTest:testCreateAndExecuteWithSalt() (gas: 499682) -QuarkWalletProxyFactoryTest:testCreateAndExecuteWithSaltSetsMsgSender() (gas: 370249) +QuarkWalletProxyFactoryTest:testCreateAndExecuteCreatesWallet() (gas: 497480) +QuarkWalletProxyFactoryTest:testCreateAndExecuteMultiCreatesWallet() (gas: 551388) +QuarkWalletProxyFactoryTest:testCreateAndExecuteMultiWithSalt() (gas: 555749) +QuarkWalletProxyFactoryTest:testCreateAndExecuteSetsMsgSender() (gas: 217657) +QuarkWalletProxyFactoryTest:testCreateAndExecuteWithSalt() (gas: 499762) +QuarkWalletProxyFactoryTest:testCreateAndExecuteWithSaltSetsMsgSender() (gas: 370326) QuarkWalletProxyFactoryTest:testCreateRevertsOnRepeat() (gas: 8937393460516733348) QuarkWalletProxyFactoryTest:testCreatesWalletAtDeterministicAddress() (gas: 228550) -QuarkWalletProxyFactoryTest:testExecuteMultiOnExistingWallet() (gas: 549400) -QuarkWalletProxyFactoryTest:testExecuteOnExistingWallet() (gas: 495984) -QuarkWalletProxyFactoryTest:testExecutorIsOtherWallet() (gas: 333440) +QuarkWalletProxyFactoryTest:testExecuteMultiOnExistingWallet() (gas: 549560) +QuarkWalletProxyFactoryTest:testExecuteOnExistingWallet() (gas: 496064) +QuarkWalletProxyFactoryTest:testExecutorIsOtherWallet() (gas: 333600) QuarkWalletProxyFactoryTest:testExecutorSetInCreate() (gas: 109661) QuarkWalletProxyFactoryTest:testVersion() (gas: 5998) -QuarkWalletTest:testAllowsForReusedNonceWithChangedScript() (gas: 99927) -QuarkWalletTest:testAtomicIncrementer() (gas: 68913) -QuarkWalletTest:testAtomicMaxCounterScript() (gas: 161736) -QuarkWalletTest:testAtomicPing() (gas: 47079) -QuarkWalletTest:testAtomicPingWithExternalSignature() (gas: 100173) -QuarkWalletTest:testCanReplaySameScriptWithDifferentCall() (gas: 155624) -QuarkWalletTest:testDirectExecuteFromEOA() (gas: 62117) -QuarkWalletTest:testDirectExecuteFromOtherQuarkWallet() (gas: 100679) -QuarkWalletTest:testDirectExecuteWithScriptSources() (gas: 370398) -QuarkWalletTest:testDisallowAllNullScriptAddress() (gas: 163032) -QuarkWalletTest:testEmitsEventsInDirectExecute() (gas: 40763) -QuarkWalletTest:testEmitsEventsInExecuteQuarkOperation() (gas: 91493) -QuarkWalletTest:testEmitsEventsInReplayableQuarkOperation() (gas: 94229) -QuarkWalletTest:testFailsWithRepeatNonceInDirectExecute() (gas: 90073) -QuarkWalletTest:testGetCodeJar() (gas: 11114) +QuarkWalletTest:testAllowsForReusedNonceWithChangedScript() (gas: 100131) +QuarkWalletTest:testAtomicIncrementer() (gas: 69037) +QuarkWalletTest:testAtomicMaxCounterScript() (gas: 162100) +QuarkWalletTest:testAtomicPing() (gas: 47208) +QuarkWalletTest:testAtomicPingWithExternalSignature() (gas: 100297) +QuarkWalletTest:testCanReplaySameScriptWithDifferentCall() (gas: 155828) +QuarkWalletTest:testDirectExecuteFromEOA() (gas: 62241) +QuarkWalletTest:testDirectExecuteFromOtherQuarkWallet() (gas: 100883) +QuarkWalletTest:testDirectExecuteWithScriptSources() (gas: 370522) +QuarkWalletTest:testDisallowAllNullScriptAddress() (gas: 163064) +QuarkWalletTest:testEmitsEventsInDirectExecute() (gas: 40865) +QuarkWalletTest:testEmitsEventsInExecuteQuarkOperation() (gas: 91653) +QuarkWalletTest:testEmitsEventsInReplayableQuarkOperation() (gas: 94513) +QuarkWalletTest:testFailsWithRepeatNonceInDirectExecute() (gas: 90197) +QuarkWalletTest:testGetCodeJar() (gas: 11158) QuarkWalletTest:testGetExecutor() (gas: 5396) -QuarkWalletTest:testGetNonceManager() (gas: 11048) -QuarkWalletTest:testGetSigner() (gas: 8477) -QuarkWalletTest:testHalfReplayableMultiQuarkOperation() (gas: 280819) -QuarkWalletTest:testMultiQuarkOperationCanCallMultipleOperationsWithOneSignature() (gas: 115244) -QuarkWalletTest:testPrecompileBigModExp() (gas: 45477) -QuarkWalletTest:testPrecompileBlake2F() (gas: 47751) -QuarkWalletTest:testPrecompileBn256Add() (gas: 46953) -QuarkWalletTest:testPrecompileBn256ScalarMul() (gas: 51937) -QuarkWalletTest:testPrecompileDataCopy() (gas: 53512) -QuarkWalletTest:testPrecompileEcRecover() (gas: 51252) -QuarkWalletTest:testPrecompileRipemd160() (gas: 47163) -QuarkWalletTest:testPrecompileSha256() (gas: 46915) -QuarkWalletTest:testQuarkOperationRevertsIfCallReverts() (gas: 45488) -QuarkWalletTest:testReadStorageForWallet() (gas: 161233) -QuarkWalletTest:testReplayableMultiQuarkOperation() (gas: 372305) -QuarkWalletTest:testReplayableMultiQuarkOperationWithSharedNonce() (gas: 252054) -QuarkWalletTest:testRequiresCorrectSubmissionToken() (gas: 123971) -QuarkWalletTest:testRevertOnAllPrecompilesDirectCall() (gas: 415622) -QuarkWalletTest:testRevertsForBadInputsInMultiQuarkOperation() (gas: 11233) -QuarkWalletTest:testRevertsForDirectExecuteByNonExecutorSigner() (gas: 11970) -QuarkWalletTest:testRevertsForNonceReuse() (gas: 83715) -QuarkWalletTest:testRevertsForRandomEmptyScriptAddress() (gas: 93584) +QuarkWalletTest:testGetNonceManager() (gas: 11070) +QuarkWalletTest:testGetSigner() (gas: 8521) +QuarkWalletTest:testHalfReplayableMultiQuarkOperation() (gas: 281139) +QuarkWalletTest:testMultiQuarkOperationCanCallMultipleOperationsWithOneSignature() (gas: 115448) +QuarkWalletTest:testPrecompileBigModExp() (gas: 45579) +QuarkWalletTest:testPrecompileBlake2F() (gas: 47831) +QuarkWalletTest:testPrecompileBn256Add() (gas: 47077) +QuarkWalletTest:testPrecompileBn256ScalarMul() (gas: 52017) +QuarkWalletTest:testPrecompileDataCopy() (gas: 53592) +QuarkWalletTest:testPrecompileEcRecover() (gas: 51354) +QuarkWalletTest:testPrecompileRipemd160() (gas: 47243) +QuarkWalletTest:testPrecompileSha256() (gas: 47039) +QuarkWalletTest:testQuarkOperationDoesNotRevertOnNestedCallFromSelf() (gas: 87690) +QuarkWalletTest:testQuarkOperationRevertsIfCallReverts() (gas: 45568) +QuarkWalletTest:testQuarkOperationRevertsOnNestedCallFromOutsideContract() (gas: 86837) +QuarkWalletTest:testReadStorageForWallet() (gas: 161553) +QuarkWalletTest:testReplayableMultiQuarkOperation() (gas: 372785) +QuarkWalletTest:testReplayableMultiQuarkOperationWithSharedNonce() (gas: 252338) +QuarkWalletTest:testRequiresCorrectSubmissionToken() (gas: 124073) +QuarkWalletTest:testRevertOnAllPrecompilesDirectCall() (gas: 415639) +QuarkWalletTest:testRevertsForBadInputsInMultiQuarkOperation() (gas: 11255) +QuarkWalletTest:testRevertsForDirectExecuteByNonExecutorSigner() (gas: 12014) +QuarkWalletTest:testRevertsForNonceReuse() (gas: 83795) +QuarkWalletTest:testRevertsForRandomEmptyScriptAddress() (gas: 93625) QuarkWalletTest:testRevertsForUnauthorizedDirectExecuteByRandomAddress() (gas: 14184) -QuarkWalletTest:testScriptCanBeCanceledByNewOp() (gas: 153455) -QuarkWalletTest:testScriptCanBeCanceledByNoOp() (gas: 128265) -QuarkWalletTest:testSetsMsgSender() (gas: 46250) -QuarkWalletTest:testSetsMsgSenderDuringDirectExecute() (gas: 38282) +QuarkWalletTest:testScriptCanBeCanceledByNewOp() (gas: 153659) +QuarkWalletTest:testScriptCanBeCanceledByNoOp() (gas: 128469) +QuarkWalletTest:testSetsMsgSender() (gas: 46374) +QuarkWalletTest:testSetsMsgSenderDuringDirectExecute() (gas: 38362) QuotecallTest:testInitializeProperlyFromConstructor() (gas: 6996) -QuotecallTest:testQuotecallForPayWithUSDT() (gas: 116638) -QuotecallTest:testQuotecallForPayWithWBTC() (gas: 109859) -QuotecallTest:testQuotecallRevertsWhenCallReverts() (gas: 86746) -QuotecallTest:testReturnCallResult() (gas: 106459) +QuotecallTest:testQuotecallForPayWithUSDT() (gas: 116718) +QuotecallTest:testQuotecallForPayWithWBTC() (gas: 109939) +QuotecallTest:testQuotecallRevertsWhenCallReverts() (gas: 86826) +QuotecallTest:testReturnCallResult() (gas: 106539) QuotecallTest:testRevertsForInvalidCallContext() (gas: 15764) -QuotecallTest:testRevertsWhenQuoteTooHigh() (gas: 132210) -QuotecallTest:testRevertsWhenQuoteTooLow() (gas: 132052) -QuotecallTest:testSimpleCounterAndPayWithUSDC() (gas: 137675) -QuotecallTest:testSimpleTransferTokenAndPayWithUSDC() (gas: 139041) -ReplayableTransactionsTest:testCancelRecurringPurchase() (gas: 249212) -ReplayableTransactionsTest:testRecurringPurchaseHappyPath() (gas: 198415) -ReplayableTransactionsTest:testRecurringPurchaseMultiplePurchases() (gas: 295717) -ReplayableTransactionsTest:testRecurringPurchaseWithDifferentCalldata() (gas: 484045) +QuotecallTest:testRevertsWhenQuoteTooHigh() (gas: 132290) +QuotecallTest:testRevertsWhenQuoteTooLow() (gas: 132132) +QuotecallTest:testSimpleCounterAndPayWithUSDC() (gas: 137755) +QuotecallTest:testSimpleTransferTokenAndPayWithUSDC() (gas: 139121) +ReplayableTransactionsTest:testCancelRecurringPurchase() (gas: 249372) +ReplayableTransactionsTest:testRecurringPurchaseHappyPath() (gas: 198495) +ReplayableTransactionsTest:testRecurringPurchaseMultiplePurchases() (gas: 295957) +ReplayableTransactionsTest:testRecurringPurchaseWithDifferentCalldata() (gas: 484445) ReplayableTransactionsTest:testRevertsForExpiredQuarkOperation() (gas: 9702) -ReplayableTransactionsTest:testRevertsForExpiredUniswapParams() (gas: 89165) -ReplayableTransactionsTest:testRevertsForPurchaseBeforeNextPurchasePeriod() (gas: 224976) -ReplayableTransactionsTest:testRevertsForPurchasingOverTheLimit() (gas: 225649) -RevertsTest:testRevertsInteger() (gas: 45287) -RevertsTest:testRevertsInvalidOpcode() (gas: 8660281895700907353) -RevertsTest:testRevertsOutOfGas() (gas: 302703) -RevertsTest:testRevertsWhenDividingByZero() (gas: 45128) -UniswapFlashLoanTest:testFlashLoanForCollateralSwapOnCompound() (gas: 369500) -UniswapFlashLoanTest:testRevertsForInsufficientFundsToRepayFlashLoan() (gas: 139793) -UniswapFlashLoanTest:testRevertsForInvalidCaller() (gas: 47313) -UniswapFlashLoanTest:testRevertsForSecondCallback() (gas: 128813) -UniswapFlashLoanTest:testTokensOrderInvariant() (gas: 82780) -UniswapFlashSwapExactOutTest:testInvalidCallerFlashSwap() (gas: 47531) -UniswapFlashSwapExactOutTest:testNotEnoughToPayFlashSwap() (gas: 234970) -UniswapFlashSwapExactOutTest:testRevertsForSecondCallback() (gas: 111799) +ReplayableTransactionsTest:testRevertsForExpiredUniswapParams() (gas: 89245) +ReplayableTransactionsTest:testRevertsForPurchaseBeforeNextPurchasePeriod() (gas: 225136) +ReplayableTransactionsTest:testRevertsForPurchasingOverTheLimit() (gas: 225809) +RevertsTest:testRevertsInteger() (gas: 45367) +RevertsTest:testRevertsInvalidOpcode() (gas: 8660281895700907393) +RevertsTest:testRevertsOutOfGas() (gas: 302743) +RevertsTest:testRevertsWhenDividingByZero() (gas: 45208) +UniswapFlashLoanTest:testFlashLoanForCollateralSwapOnCompound() (gas: 369580) +UniswapFlashLoanTest:testRevertsForInsufficientFundsToRepayFlashLoan() (gas: 139873) +UniswapFlashLoanTest:testRevertsForInvalidCaller() (gas: 47393) +UniswapFlashLoanTest:testRevertsForSecondCallback() (gas: 128893) +UniswapFlashLoanTest:testTokensOrderInvariant() (gas: 82860) +UniswapFlashSwapExactOutTest:testInvalidCallerFlashSwap() (gas: 47611) +UniswapFlashSwapExactOutTest:testNotEnoughToPayFlashSwap() (gas: 235050) +UniswapFlashSwapExactOutTest:testRevertsForSecondCallback() (gas: 111879) UniswapFlashSwapExactOutTest:testRevertsIfCalledDirectly() (gas: 104456) -UniswapFlashSwapExactOutTest:testUniswapFlashSwapExactOutLeverageComet() (gas: 312123) +UniswapFlashSwapExactOutTest:testUniswapFlashSwapExactOutLeverageComet() (gas: 312203) isValidSignatureTest:testIsValidSignatureForEOAOwner() (gas: 13062) isValidSignatureTest:testReturnsMagicValueForValidSignature() (gas: 8324) isValidSignatureTest:testRevertsForEmptyContract() (gas: 10910) diff --git a/src/quark-core/src/QuarkWallet.sol b/src/quark-core/src/QuarkWallet.sol index ff11fbfb..b25b3892 100644 --- a/src/quark-core/src/QuarkWallet.sol +++ b/src/quark-core/src/QuarkWallet.sol @@ -69,6 +69,7 @@ contract QuarkWallet is IERC1271 { error NoActiveCallback(); error SignatureExpired(); error Unauthorized(); + error UnauthorizedNestedOperation(); /// @notice Enum specifying the method of execution for running a Quark script enum ExecutionType { @@ -455,6 +456,15 @@ contract QuarkWallet is IERC1271 { oldActiveSubmissionToken := tload(activeSubmissionTokenSlot) oldCallback := tload(callbackSlot) + // Prevent nested operations coming from an outside caller (i.e. not the Quark wallet itself) + if and(iszero(eq(oldActiveScript, 0)), iszero(eq(caller(), address()))) { + let errorSignature := 0x0c484db9 // Signature for UnauthorizedNestedOperation() + let ptr := mload(0x40) + mstore(ptr, errorSignature) + // Error signature is left-padded with 0s, so we want to fetch the last 4 bytes starting at the 29th byte + revert(add(ptr, 0x1c), 0x04) + } + // Transiently store the active script tstore(activeScriptSlot, scriptAddress) diff --git a/test/lib/ExecuteOtherOperation.sol b/test/lib/ExecuteOtherOperation.sol index 2c510832..5ac6ea0e 100644 --- a/test/lib/ExecuteOtherOperation.sol +++ b/test/lib/ExecuteOtherOperation.sol @@ -10,4 +10,11 @@ contract ExecuteOtherOperation is QuarkScript { allowCallback(); return QuarkWallet(payable(address(this))).executeQuarkOperation(op, signature); } + + function executeFor(address quarkWalletAddress, QuarkWallet.QuarkOperation memory op, bytes memory signature) + external + returns (bytes memory) + { + return QuarkWallet(payable(quarkWalletAddress)).executeQuarkOperation(op, signature); + } } diff --git a/test/quark-core/QuarkWallet.t.sol b/test/quark-core/QuarkWallet.t.sol index ab64179e..3d91c344 100644 --- a/test/quark-core/QuarkWallet.t.sol +++ b/test/quark-core/QuarkWallet.t.sol @@ -29,6 +29,8 @@ import {PrecompileCaller} from "test/lib/PrecompileCaller.sol"; import {MaxCounterScript} from "test/lib/MaxCounterScript.sol"; import {GetMessageDetails} from "test/lib/GetMessageDetails.sol"; import {CheckNonceScript} from "test/lib/CheckNonceScript.sol"; +import {BatchSend} from "test/lib/BatchCallback.sol"; +import {ExecuteOtherOperation} from "test/lib/ExecuteOtherOperation.sol"; contract QuarkWalletTest is Test { enum ExecutionType { @@ -1243,6 +1245,68 @@ contract QuarkWalletTest is Test { aliceWallet.executeMultiQuarkOperationWithSubmissionToken(op1, submissionTokens1[2], opDigests, signature); } + /* ===== nested operation tests ===== */ + + // Note: Nested quark operations called from outside the Quark wallet will revert + function testQuarkOperationRevertsOnNestedCallFromOutsideContract() public { + // gas: do not meter set-up + vm.pauseGasMetering(); + ExecuteOtherOperation executeOtherOperation = new ExecuteOtherOperation(); + bytes memory ethcall = new YulHelper().getCode("Ethcall.sol/Ethcall.json"); + bytes memory ping = new YulHelper().getCode("Logger.sol/Logger.json"); + QuarkWallet.QuarkOperation memory nestedOp = + new QuarkOperationHelper().newBasicOp(aliceWallet, ping, ScriptType.ScriptAddress); + nestedOp.nonce = bytes32(uint256(keccak256(abi.encodePacked(block.timestamp))) - 2); // Don't overlap on nonces + bytes memory nestedOpSignature = new SignatureHelper().signOp(alicePrivateKey, aliceWallet, nestedOp); + QuarkWallet.QuarkOperation memory parentOp = new QuarkOperationHelper().newBasicOpWithCalldata( + aliceWallet, + ethcall, + abi.encodeWithSelector( + Ethcall.run.selector, + address(executeOtherOperation), + abi.encodeCall(ExecuteOtherOperation.executeFor, (address(aliceWallet), nestedOp, nestedOpSignature)), + 0 + ), + ScriptType.ScriptSource + ); + bytes memory parentOpSignature = new SignatureHelper().signOp(alicePrivateKey, aliceWallet, parentOp); + + // gas: meter execute + vm.resumeGasMetering(); + vm.expectRevert(abi.encodeWithSelector(QuarkWallet.UnauthorizedNestedOperation.selector)); + aliceWallet.executeQuarkOperation(parentOp, parentOpSignature); + } + + // Note: Nested quark operations called from the Quark wallet itself will not revert + function testQuarkOperationDoesNotRevertOnNestedCallFromSelf() public { + // gas: do not meter set-up + vm.pauseGasMetering(); + bytes memory ethcall = new YulHelper().getCode("Ethcall.sol/Ethcall.json"); + bytes memory ping = new YulHelper().getCode("Logger.sol/Logger.json"); + QuarkWallet.QuarkOperation memory nestedOp = + new QuarkOperationHelper().newBasicOp(aliceWallet, ping, ScriptType.ScriptAddress); + nestedOp.nonce = bytes32(uint256(keccak256(abi.encodePacked(block.timestamp))) - 2); // Don't overlap on nonces + bytes memory nestedOpSignature = new SignatureHelper().signOp(alicePrivateKey, aliceWallet, nestedOp); + QuarkWallet.QuarkOperation memory parentOp = new QuarkOperationHelper().newBasicOpWithCalldata( + aliceWallet, + ethcall, + abi.encodeWithSelector( + Ethcall.run.selector, + address(aliceWallet), + abi.encodeCall(QuarkWallet.executeQuarkOperation, (nestedOp, nestedOpSignature)), + 0 + ), + ScriptType.ScriptSource + ); + bytes memory parentOpSignature = new SignatureHelper().signOp(alicePrivateKey, aliceWallet, parentOp); + + // gas: meter execute + vm.resumeGasMetering(); + vm.expectEmit(false, false, false, true); + emit Ping(55); + aliceWallet.executeQuarkOperation(parentOp, parentOpSignature); + } + /* ===== basic operation tests ===== */ function testAtomicMaxCounterScript() public {