From f8f464bc9bb106caf31ffe341addc6826b2b926d Mon Sep 17 00:00:00 2001 From: smitrajput Date: Mon, 29 May 2023 22:28:42 +0530 Subject: [PATCH] :art: Explanations, cleanups --- src/CleanPuzzleBoxSolution.sol | 168 ------------------ ...xSolution.sol => PuzzleBoxSolution_V0.sol} | 4 +- src/PuzzleBoxSolution_V1.sol | 22 ++- src/PuzzleBoxSolution_V2.sol | 97 ++++++++++ test/PuzzleBox.t.sol | 8 +- 5 files changed, 118 insertions(+), 181 deletions(-) delete mode 100644 src/CleanPuzzleBoxSolution.sol rename src/{PuzzleBoxSolution.sol => PuzzleBoxSolution_V0.sol} (93%) create mode 100644 src/PuzzleBoxSolution_V2.sol diff --git a/src/CleanPuzzleBoxSolution.sol b/src/CleanPuzzleBoxSolution.sol deleted file mode 100644 index ec61ec6..0000000 --- a/src/CleanPuzzleBoxSolution.sol +++ /dev/null @@ -1,168 +0,0 @@ -pragma solidity ^0.8.19; - -import "./PuzzleBox.sol"; -import "forge-std/Test.sol"; - -contract CleanPuzzleBoxSolution { - - function solve(PuzzleBox _puzzle) external payable { - // How close can you get to opening the box? - // bytes memory selectoors = hex"7159a6188fd66f250091905511551052925facb1000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000092b071e47000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000416e59dacfdb5d457304115bbfb9089531d873b70000000000000000000000000000000000000000000000000000000000000003000000000000000000000000c817dd2a5daa8f790677e399170c92aabd044b570000000000000000000000000000000000000000000000000000000000000096000000000000000000000000000000000000000000000000000000000000004b58657dcfc8f549a7e4cb7e1c60d908cc05ceff53ad731e6ea0736edf7ffeea588dfb42d800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000041c8f549a7e4cb7e1c60d908cc05ceff53ad731e6ea0736edf7ffeea588dfb42d89da3468f3d897010503caed5c52689b959fbac09ff6879275a8279feffcc8a621b00000000000000000000000000000000000000000000000000000000000000"; - // bytes memory helperCode = type(Helper).creationCode; - // bytes memory helperCode = hex"60806040527f7159a618deecedd4925facb10000000000000000000000000000000000000000600052600080600460008073037eda3adb1198021a9b2e88c22b464fd38db3f35af15060008060446004600073037eda3adb1198021a9b2e88c22b464fd38db3f35af15060b3806100776000396000f3fe6080604052610151471460008114601b5760018114606457607b565b7f9f678cca0000000000000000000000000000000000000000000000000000000060005260008060046000606573037eda3adb1198021a9b2e88c22b464fd38db3f35af150607b565b73037eda3adb1198021a9b2e88c22b464fd38db3f5ff5b00fea2646970667358221220cd1ba594295bd87e73888e1b54a24556a64e88bd4f377490115ed520d55d50fc64736f6c63430008130033"; - // console.logBytes(helperCode); - // bytes memory data; - // assembly { - // codecopy(data, 174, 1063) - // } - // console.logBytes(data); - - assembly { - // codecopy(0x20, 469, 1020) - // pop(call(gas(), 0x69209d8a7d258515eC9a4D25F7Be1dB85cB1B826, callvalue(), 0x320, 252, callvalue(), callvalue())) - codecopy(0x20, 155, 1063) - let helperAddr := create(callvalue(), 0x320, 252) - pop(call(gas(), helperAddr, callvalue(), callvalue(), callvalue(), callvalue(), callvalue())) - // puzzle.leak() - pop(call(gas(), 0x69209d8a7d258515eC9a4D25F7Be1dB85cB1B826, callvalue(), 0x24, 4, callvalue(), callvalue())) - // warming up puzzle + 2 // not needed now as it's been done in Helper's fallback - // pop(call(gas(), add(_puzzle, 2), 1, 0, 0, 0, 0)) - // zip - pop(call(gas(), 0x69209d8a7d258515eC9a4D25F7Be1dB85cB1B826, callvalue(), 0x28, 4, callvalue(), callvalue())) - // creep - pop(call(98000, 0x69209d8a7d258515eC9a4D25F7Be1dB85cB1B826, callvalue(), 0x2c, 4, callvalue(), callvalue())) - // torch - pop(call(gas(), 0x69209d8a7d258515eC9a4D25F7Be1dB85cB1B826, callvalue(), 0x30, 293, callvalue(), callvalue())) - // spread - pop(call(gas(), 0x69209d8a7d258515eC9a4D25F7Be1dB85cB1B826, callvalue(), 0x155, 260, callvalue(), callvalue())) - // open - pop(call(gas(), 0x69209d8a7d258515eC9a4D25F7Be1dB85cB1B826, callvalue(), 0x259, 196, callvalue(), callvalue())) - } - bytes memory garage = hex"7159a6188fd66f250091905511551052925facb1000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000092b071e47000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000416e59dacfdb5d457304115bbfb9089531d873b70000000000000000000000000000000000000000000000000000000000000003000000000000000000000000c817dd2a5daa8f790677e399170c92aabd044b570000000000000000000000000000000000000000000000000000000000000096000000000000000000000000000000000000000000000000000000000000004b58657dcfc8f549a7e4cb7e1c60d908cc05ceff53ad731e6ea0736edf7ffeea588dfb42d800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000041c8f549a7e4cb7e1c60d908cc05ceff53ad731e6ea0736edf7ffeea588dfb42d89da3468f3d897010503caed5c52689b959fbac09ff6879275a8279feffcc8a621b00000000000000000000000000000000000000000000000000000000000000000000608060405260006b7159a618deecedd4925facb160a01b81527369209d8a7d258515ec9a4d25f7be1db85cb1b826818260048485855af15081826044600485855af150505060aa806100526000396000f3fe60806040524761015114801560175760018114605d57005b7f9f678cca000000000000000000000000000000000000000000000000000000006000526000806004600060657369209d8a7d258515ec9a4d25f7be1db85cb1b8265af1005b7369209d8a7d258515ec9a4d25f7be1db85cb1b828fffea26469706673582212207d8240af97bb21dbf56dba721b468de6e8a15dc3dc21e7a0175cb7595a37898f64736f6c63430008130033"; - - // bytes memory selectoors = hex"7159a6188fd66f250091905511551052925facb1000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000092b071e47000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000416e59dacfdb5d457304115bbfb9089531d873b70000000000000000000000000000000000000000000000000000000000000003000000000000000000000000c817dd2a5daa8f790677e399170c92aabd044b570000000000000000000000000000000000000000000000000000000000000096000000000000000000000000000000000000000000000000000000000000004b58657dcfc8f549a7e4cb7e1c60d908cc05ceff53ad731e6ea0736edf7ffeea588dfb42d800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000041c8f549a7e4cb7e1c60d908cc05ceff53ad731e6ea0736edf7ffeea588dfb42d89da3468f3d897010503caed5c52689b959fbac09ff6879275a8279feffcc8a621b00000000000000000000000000000000000000000000000000000000000000"; - // // bytes memory helperCode = type(Helper).creationCode; - // bytes memory helperCode = hex"608060405260006b7159a618deecedd4925facb160a01b81527369209d8a7d258515ec9a4d25f7be1db85cb1b826818260048485855af15081826044600485855af150505060aa806100526000396000f3fe60806040524761015114801560175760018114605d57005b7f9f678cca000000000000000000000000000000000000000000000000000000006000526000806004600060657369209d8a7d258515ec9a4d25f7be1db85cb1b8265af1005b7369209d8a7d258515ec9a4d25f7be1db85cb1b828fffea26469706673582212207d8240af97bb21dbf56dba721b468de6e8a15dc3dc21e7a0175cb7595a37898f64736f6c63430008130033"; - // // console.logBytes(helperCode); - // assembly { - // let helperAddr := create(0, add(helperCode, 0x20), mload(helperCode)) - // pop(call(gas(), helperAddr, 0, 0, 0, 0, 0)) - // // puzzle.leak() - // pop(call(gas(), 0x69209d8a7d258515eC9a4D25F7Be1dB85cB1B826, 0, add(selectoors, 0x24), 4, 0, 0)) - // // warming up puzzle + 2 // not needed now as it's been done in Helper's fallback - // // pop(call(gas(), add(_puzzle, 2), 1, 0, 0, 0, 0)) - // // zip - // pop(call(gas(), 0x69209d8a7d258515eC9a4D25F7Be1dB85cB1B826, 0, add(selectoors, 0x28), 4, 0, 0)) - // // creep - // pop(call(98000, 0x69209d8a7d258515eC9a4D25F7Be1dB85cB1B826, 0, add(selectoors, 0x2c), 4, 0, 0)) - // // torch - // pop(call(gas(), 0x69209d8a7d258515eC9a4D25F7Be1dB85cB1B826, 0, add(selectoors, 0x30), 293, 0, 0)) - // // spread - // pop(call(gas(), 0x69209d8a7d258515eC9a4D25F7Be1dB85cB1B826, 0, add(selectoors, 0x155), 260, 0, 0)) - // // open - // pop(call(gas(), 0x69209d8a7d258515eC9a4D25F7Be1dB85cB1B826, 0, add(selectoors, 0x259), 196, 0, 0)) - // } - } -} - - -// contract Helper { -// constructor() payable { -// // bytes memory data = hex"7159a618deecedd4925facb1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; -// // console.logBytes4(bytes4(keccak256("torch(bytes)"))); -// // console.logBytes(abi.encodeWithSignature("lock(bytes4,bool)", bytes4(keccak256("torch(bytes)")), false)); -// assembly { -// mstore(0x00, hex"7159a618deecedd4925facb10000000000000000000000000000000000000000") -// // operate -// pop(call(gas(), 0x037eDa3aDB1198021A9b2e88C22B464fD38db3f3, 0, 0x00, 4, 0, 0)) -// // lock() -// pop(call(gas(), 0x037eDa3aDB1198021A9b2e88C22B464fD38db3f3, 0, 0x04, 68, 0, 0)) -// } -// } - -// fallback() external payable { -// assembly { -// switch eq(selfbalance(), 337) -// case 0 { -// mstore(0, hex"9f678cca") -// pop(call(gas(), 0x037eDa3aDB1198021A9b2e88C22B464fD38db3f3, selfbalance(), 0x00, 4, 0, 0)) -// } -// case 1 { -// // warming puzzle + 2 -// selfdestruct(0x037eDa3aDB1198021A9b2e88C22B464fD38db3f5) -// } -// } -// } -// } - - -// pragma solidity ^0.8.19; - -// import "./PuzzleBox.sol"; -// import "forge-std/Test.sol"; - -// contract CleanPuzzleBoxSolution { - -// function solve() external payable { -// // How close can you get to opening the box? -// bytes memory selectoors = hex"7159a6188fd66f250091905511551052925facb1000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000092b071e47000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000416e59dacfdb5d457304115bbfb9089531d873b70000000000000000000000000000000000000000000000000000000000000003000000000000000000000000c817dd2a5daa8f790677e399170c92aabd044b570000000000000000000000000000000000000000000000000000000000000096000000000000000000000000000000000000000000000000000000000000004b58657dcfc8f549a7e4cb7e1c60d908cc05ceff53ad731e6ea0736edf7ffeea588dfb42d800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000041c8f549a7e4cb7e1c60d908cc05ceff53ad731e6ea0736edf7ffeea588dfb42d89da3468f3d897010503caed5c52689b959fbac09ff6879275a8279feffcc8a621b00000000000000000000000000000000000000000000000000000000000000"; -// // bytes memory helperCode = hex"60c060405273037eda3adb1198021a9b2e88c22b464fd38db3f373ffffffffffffffffffffffffffffffffffffffff1660808173ffffffffffffffffffffffffffffffffffffffff16815250503373ffffffffffffffffffffffffffffffffffffffff1660a08173ffffffffffffffffffffffffffffffffffffffff168152505060805173ffffffffffffffffffffffffffffffffffffffff16637159a6186040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156100ca57600080fd5b505af11580156100de573d6000803e3d6000fd5b5050505073037eda3adb1198021a9b2e88c22b464fd38db3f373ffffffffffffffffffffffffffffffffffffffff1663deecedd463925facb160e01b60006040518363ffffffff1660e01b81526004016101399291906101c6565b600060405180830381600087803b15801561015357600080fd5b505af1158015610167573d6000803e3d6000fd5b505050506101ef565b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b6101a581610170565b82525050565b60008115159050919050565b6101c0816101ab565b82525050565b60006040820190506101db600083018561019c565b6101e860208301846101b7565b9392505050565b60805160a0516101796102126000396000609e01526000600f01526101796000f3fe6080604052610151471461009c577f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1660656040516100529061012e565b60006040518083038185875af1925050503d806000811461008f576040519150601f19603f3d011682016040523d82523d6000602084013e610094565b606091505b5050506100d5565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16ff5b005b600081905092915050565b7f9f678cca00000000000000000000000000000000000000000000000000000000600082015250565b60006101186004836100d7565b9150610123826100e2565b600482019050919050565b60006101398261010b565b915081905091905056fea26469706673582212208f094eb57682c27f678c08cf065a2df5b481a1c1e9514fcefff5890dd9318c1264736f6c63430008130033"; -// bytes memory helperCode = type(Helper).creationCode; -// console.logBytes(helperCode); -// bytes memory helperCode = type(Helper).creationCode; -// // console.logBytes(helperCode); -// // bytes memory helperCalldata = abi.encodePacked(helperCode, abi.encode(_puzzle)); -// // console.logBytes(helperCalldata); - // assembly { - // codecopy(0x20, 155, 1063) - // let helperAddr := create(callvalue(), 0x320, 252) - // pop(call(gas(), helperAddr, callvalue(), callvalue(), callvalue(), callvalue(), callvalue())) - // // puzzle.leak() - // pop(call(gas(), 0x69209d8a7d258515eC9a4D25F7Be1dB85cB1B826, callvalue(), 0x24, 4, callvalue(), callvalue())) - // // warming up puzzle + 2 // not needed now as it's been done in Helper's fallback - // // pop(call(gas(), add(_puzzle, 2), 1, 0, 0, 0, 0)) - // // zip - // pop(call(gas(), 0x69209d8a7d258515eC9a4D25F7Be1dB85cB1B826, callvalue(), 0x28, 4, callvalue(), callvalue())) - // // creep - // pop(call(98000, 0x69209d8a7d258515eC9a4D25F7Be1dB85cB1B826, callvalue(), 0x2c, 4, callvalue(), callvalue())) - // // torch - // pop(call(gas(), 0x69209d8a7d258515eC9a4D25F7Be1dB85cB1B826, callvalue(), 0x30, 293, callvalue(), callvalue())) - // // spread - // pop(call(gas(), 0x69209d8a7d258515eC9a4D25F7Be1dB85cB1B826, callvalue(), 0x155, 260, callvalue(), callvalue())) - // // open - // pop(call(gas(), 0x69209d8a7d258515eC9a4D25F7Be1dB85cB1B826, callvalue(), 0x259, 196, callvalue(), callvalue())) - // } - // bytes memory garage = hex"7159a6188fd66f250091905511551052925facb1000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000092b071e47000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000416e59dacfdb5d457304115bbfb9089531d873b70000000000000000000000000000000000000000000000000000000000000003000000000000000000000000c817dd2a5daa8f790677e399170c92aabd044b570000000000000000000000000000000000000000000000000000000000000096000000000000000000000000000000000000000000000000000000000000004b58657dcfc8f549a7e4cb7e1c60d908cc05ceff53ad731e6ea0736edf7ffeea588dfb42d800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000041c8f549a7e4cb7e1c60d908cc05ceff53ad731e6ea0736edf7ffeea588dfb42d89da3468f3d897010503caed5c52689b959fbac09ff6879275a8279feffcc8a621b00000000000000000000000000000000000000000000000000000000000000000000608060405260006b7159a618deecedd4925facb160a01b81527369209d8a7d258515ec9a4d25f7be1db85cb1b826818260048485855af15081826044600485855af150505060aa806100526000396000f3fe60806040524761015114801560175760018114605d57005b7f9f678cca000000000000000000000000000000000000000000000000000000006000526000806004600060657369209d8a7d258515ec9a4d25f7be1db85cb1b8265af1005b7369209d8a7d258515ec9a4d25f7be1db85cb1b828fffea26469706673582212207d8240af97bb21dbf56dba721b468de6e8a15dc3dc21e7a0175cb7595a37898f64736f6c63430008130033"; - // } -// } - - -// contract Helper { -// constructor() payable { -// // bytes memory data = hex"7159a618deecedd4925facb1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; -// // console.logBytes4(bytes4(keccak256("torch(bytes)"))); -// // console.logBytes(abi.encodeWithSignature("lock(bytes4,bool)", bytes4(keccak256("torch(bytes)")), false)); -// assembly { -// mstore(0x00, hex"7159a618deecedd4925facb10000000000000000000000000000000000000000") -// // operate -// pop(call(gas(), 0x69209d8a7d258515eC9a4D25F7Be1dB85cB1B826, 0, 0x00, 4, 0, 0)) -// // lock() -// pop(call(gas(), 0x69209d8a7d258515eC9a4D25F7Be1dB85cB1B826, 0, 0x04, 68, 0, 0)) -// } -// } - -// fallback() external payable { -// assembly { -// switch eq(selfbalance(), 337) -// case 0 { -// mstore(0, hex"9f678cca") -// pop(call(gas(), 0x69209d8a7d258515eC9a4D25F7Be1dB85cB1B826, 101, 0x00, 4, 0, 0)) -// } -// case 1 { -// // warming puzzle + 2 -// selfdestruct(0x69209d8a7d258515eC9a4D25F7Be1dB85cB1B828) -// } -// } -// } -// } - diff --git a/src/PuzzleBoxSolution.sol b/src/PuzzleBoxSolution_V0.sol similarity index 93% rename from src/PuzzleBoxSolution.sol rename to src/PuzzleBoxSolution_V0.sol index ca49e79..fa7e592 100644 --- a/src/PuzzleBoxSolution.sol +++ b/src/PuzzleBoxSolution_V0.sol @@ -3,7 +3,9 @@ pragma solidity ^0.8.19; import "./PuzzleBox.sol"; import "forge-std/Test.sol"; -contract PuzzleBoxSolution is Test { +/// Basic solution (sans gas-optimizations) +/// Reference: https://gist.github.com/merklejerk/0f97d94a752c32edea0c01f464d0db97 +contract PuzzleBoxSolution_V0 is Test { function solve(PuzzleBox puzzle) external { // How close can you get to opening the box? diff --git a/src/PuzzleBoxSolution_V1.sol b/src/PuzzleBoxSolution_V1.sol index 6905a5f..3d565d4 100644 --- a/src/PuzzleBoxSolution_V1.sol +++ b/src/PuzzleBoxSolution_V1.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.19; import "./PuzzleBox.sol"; /// This is the solution that put me to #6 on the (live) leaderboard: https://ctf.dragonfly.xyz/scores +/// Reference: https://www.kalos.xyz/blog/dragonfly-ctf-puzzlebox-sol contract PuzzleBoxSolution_V1 { fallback() external payable { @@ -24,25 +25,32 @@ contract PuzzleBoxSolution_V1 { assembly { let helperAddr := create(0, add(helperCode, 0x20), mload(helperCode)) pop(call(gas(), helperAddr, 0, 0, 0, 0, 0)) - // puzzle.leak() + + // leak() pop(call(gas(), 0x69209d8a7d258515eC9a4D25F7Be1dB85cB1B826, 0, add(selectoors, 0x24), 4, 0, 0)) + // warming up puzzle + 2 // not needed now as it's been done in Helper's fallback // pop(call(gas(), add(_puzzle, 2), 1, 0, 0, 0, 0)) - // zip + + // zip() pop(call(gas(), 0x69209d8a7d258515eC9a4D25F7Be1dB85cB1B826, 0, add(selectoors, 0x28), 4, 0, 0)) - // creep + + // creep() pop(call(98000, 0x69209d8a7d258515eC9a4D25F7Be1dB85cB1B826, 0, add(selectoors, 0x2c), 4, 0, 0)) - // torch + + // torch() pop(call(gas(), 0x69209d8a7d258515eC9a4D25F7Be1dB85cB1B826, 0, add(selectoors, 0x30), 293, 0, 0)) - // spread + + // spread() pop(call(gas(), 0x69209d8a7d258515eC9a4D25F7Be1dB85cB1B826, 0, add(selectoors, 0x155), 260, 0, 0)) - // open + + // open() pop(call(gas(), 0x69209d8a7d258515eC9a4D25F7Be1dB85cB1B826, 0, add(selectoors, 0x259), 196, 0, 0)) } } } -/* Commeted-out Helper contract, as its deployment bytecode is hardcoded above, to save gas */ +/* Commented-out Helper contract, as its deployment bytecode is hardcoded above, to save gas */ // contract Helper { // constructor() payable { diff --git a/src/PuzzleBoxSolution_V2.sol b/src/PuzzleBoxSolution_V2.sol new file mode 100644 index 0000000..27806cc --- /dev/null +++ b/src/PuzzleBoxSolution_V2.sol @@ -0,0 +1,97 @@ +pragma solidity ^0.8.19; + +import "./PuzzleBox.sol"; +import "forge-std/Test.sol"; + +/// This solution further optimizes PuzzleBoxSolution_V1 using codecopy() to copy the +/// encoded function selectors + Helper contract bytecode from environment code to memory, +/// to avoid 'add(selectoors, specificSelectorOffset)' opcodes during calls to zip, creep, torch, spread, open. +/// Replaing '0' with 'callvalue()' further saves 1 gas as PUSH1 0x00 is 3 gas, while CALLVALUE is 2 gas. +contract PuzzleBoxSolution_V2 { + + function solve(PuzzleBox _puzzle) external payable { + // How close can you get to opening the box? + + // function selectors of leak(), zip(), creep(), torch(), spread(), open() + // bytes memory selectoors = hex"7159a6188fd66f250091905511551052925facb1000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000092b071e47000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000416e59dacfdb5d457304115bbfb9089531d873b70000000000000000000000000000000000000000000000000000000000000003000000000000000000000000c817dd2a5daa8f790677e399170c92aabd044b570000000000000000000000000000000000000000000000000000000000000096000000000000000000000000000000000000000000000000000000000000004b58657dcfc8f549a7e4cb7e1c60d908cc05ceff53ad731e6ea0736edf7ffeea588dfb42d800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000041c8f549a7e4cb7e1c60d908cc05ceff53ad731e6ea0736edf7ffeea588dfb42d89da3468f3d897010503caed5c52689b959fbac09ff6879275a8279feffcc8a621b00000000000000000000000000000000000000000000000000000000000000"; + + // dynamically stored bytecode + // bytes memory helperCode = type(Helper).creationCode; + + // assembly-optimized bytecode with compiler optimizations enabled to 1,000,000 runs + // bytes memory helperCode = hex"60806040527f7159a618deecedd4925facb10000000000000000000000000000000000000000600052600080600460008073037eda3adb1198021a9b2e88c22b464fd38db3f35af15060008060446004600073037eda3adb1198021a9b2e88c22b464fd38db3f35af15060b3806100776000396000f3fe6080604052610151471460008114601b5760018114606457607b565b7f9f678cca0000000000000000000000000000000000000000000000000000000060005260008060046000606573037eda3adb1198021a9b2e88c22b464fd38db3f35af150607b565b73037eda3adb1198021a9b2e88c22b464fd38db3f5ff5b00fea2646970667358221220cd1ba594295bd87e73888e1b54a24556a64e88bd4f377490115ed520d55d50fc64736f6c63430008130033"; + + assembly { + // copy encoded selectors + Helper contract bytecode from environment code (at 'garage' below) to memory, to avoid 'add(selectoors, specificSelectorOffset)' opcodes for + // calls to zip, creep, torch, spread, open + // codecopy(0x20, 469, 1020) + codecopy(0x20, 155, 1063) + + // test call to log calldata at 0x320 + // pop(call(gas(), 0x69209d8a7d258515eC9a4D25F7Be1dB85cB1B826, callvalue(), 0x320, 252, callvalue(), callvalue())) + + // deploy Helper contract and call its fallback function + let helperAddr := create(callvalue(), 0x320, 252) + pop(call(gas(), helperAddr, callvalue(), callvalue(), callvalue(), callvalue(), callvalue())) + + // leak() + pop(call(gas(), 0x69209d8a7d258515eC9a4D25F7Be1dB85cB1B826, callvalue(), 0x24, 4, callvalue(), callvalue())) + + // warming up puzzle + 2 // not needed now as it's been done in Helper's fallback + // pop(call(gas(), add(_puzzle, 2), 1, 0, 0, 0, 0)) + + // zip() + pop(call(gas(), 0x69209d8a7d258515eC9a4D25F7Be1dB85cB1B826, callvalue(), 0x28, 4, callvalue(), callvalue())) + + // creep() + pop(call(98000, 0x69209d8a7d258515eC9a4D25F7Be1dB85cB1B826, callvalue(), 0x2c, 4, callvalue(), callvalue())) + + // torch() + pop(call(gas(), 0x69209d8a7d258515eC9a4D25F7Be1dB85cB1B826, callvalue(), 0x30, 293, callvalue(), callvalue())) + + // spread() + pop(call(gas(), 0x69209d8a7d258515eC9a4D25F7Be1dB85cB1B826, callvalue(), 0x155, 260, callvalue(), callvalue())) + + // open() + pop(call(gas(), 0x69209d8a7d258515eC9a4D25F7Be1dB85cB1B826, callvalue(), 0x259, 196, callvalue(), callvalue())) + } + + // encoded function selectors of leak(), zip(), creep(), torch(), spread(), open() + Helper contract bytecode, so that + // it can be copied from environment code to memory at 0x20 using codecopy() + bytes memory garage = hex"7159a6188fd66f250091905511551052925facb1000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000092b071e47000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000416e59dacfdb5d457304115bbfb9089531d873b70000000000000000000000000000000000000000000000000000000000000003000000000000000000000000c817dd2a5daa8f790677e399170c92aabd044b570000000000000000000000000000000000000000000000000000000000000096000000000000000000000000000000000000000000000000000000000000004b58657dcfc8f549a7e4cb7e1c60d908cc05ceff53ad731e6ea0736edf7ffeea588dfb42d800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000041c8f549a7e4cb7e1c60d908cc05ceff53ad731e6ea0736edf7ffeea588dfb42d89da3468f3d897010503caed5c52689b959fbac09ff6879275a8279feffcc8a621b00000000000000000000000000000000000000000000000000000000000000000000608060405260006b7159a618deecedd4925facb160a01b81527369209d8a7d258515ec9a4d25f7be1db85cb1b826818260048485855af15081826044600485855af150505060aa806100526000396000f3fe60806040524761015114801560175760018114605d57005b7f9f678cca000000000000000000000000000000000000000000000000000000006000526000806004600060657369209d8a7d258515ec9a4d25f7be1db85cb1b8265af1005b7369209d8a7d258515ec9a4d25f7be1db85cb1b828fffea26469706673582212207d8240af97bb21dbf56dba721b468de6e8a15dc3dc21e7a0175cb7595a37898f64736f6c63430008130033"; + } +} + +/* Commented-out Helper contract, as its deployment bytecode is hardcoded above, to save gas */ + +// contract Helper { +// constructor() payable { +// // bytes memory data = hex"7159a618deecedd4925facb1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; +// // console.logBytes4(bytes4(keccak256("torch(bytes)"))); +// // console.logBytes(abi.encodeWithSignature("lock(bytes4,bool)", bytes4(keccak256("torch(bytes)")), false)); +// assembly { +// mstore(0x00, hex"7159a618deecedd4925facb10000000000000000000000000000000000000000") +// // operate +// pop(call(gas(), 0x037eDa3aDB1198021A9b2e88C22B464fD38db3f3, 0, 0x00, 4, 0, 0)) +// // lock() +// pop(call(gas(), 0x037eDa3aDB1198021A9b2e88C22B464fD38db3f3, 0, 0x04, 68, 0, 0)) +// } +// } + +// fallback() external payable { +// assembly { +// switch eq(selfbalance(), 337) +// case 0 { +// mstore(0, hex"9f678cca") +// pop(call(gas(), 0x037eDa3aDB1198021A9b2e88C22B464fD38db3f3, selfbalance(), 0x00, 4, 0, 0)) +// } +// case 1 { +// // warming puzzle + 2 +// selfdestruct(0x037eDa3aDB1198021A9b2e88C22B464fD38db3f5) +// } +// } +// } +// } + + + diff --git a/test/PuzzleBox.t.sol b/test/PuzzleBox.t.sol index be6bbb0..dc7257e 100644 --- a/test/PuzzleBox.t.sol +++ b/test/PuzzleBox.t.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.19; import "forge-std/Test.sol"; import "../src/PuzzleBox.sol"; -import "../src/CleanPuzzleBoxSolution.sol"; +import "../src/PuzzleBoxSolution_V2.sol"; contract PuzzleBoxFixture is Test { event Lock(bytes4 selector, bool isLocked); @@ -18,7 +18,7 @@ contract PuzzleBoxFixture is Test { PuzzleBoxFactory _factory = new PuzzleBoxFactory(); PuzzleBox _puzzle; - CleanPuzzleBoxSolution _solution; + PuzzleBoxSolution_V2 _solution; // address deployer; // Use a modifier instead of setUp() to keep it all in one tx. @@ -27,7 +27,7 @@ contract PuzzleBoxFixture is Test { // vm.deal(deployer, 1 ether); // vm.startPrank(deployer); _puzzle = _factory.createPuzzleBox{value: 1337}(); - _solution = CleanPuzzleBoxSolution(address(new SolutionContainer(type(CleanPuzzleBoxSolution).runtimeCode))); + _solution = PuzzleBoxSolution_V2(address(new SolutionContainer(type(PuzzleBoxSolution_V2).runtimeCode))); _; } @@ -35,12 +35,10 @@ contract PuzzleBoxFixture is Test { // Uncomment to verify a complete solution. // vm.expectEmit(false, false, false, false, address(_puzzle)); // emit Open(address(0)); - // console.log("PUZZZZZLE", address(_puzzle)); // vm.breakpoint("b"); uint256 gas = gasleft(); _solution.solve(_puzzle); console.log("GAS USED", gas - gasleft()); - // address(_solution).call(abi.encodeWithSignature("solve()", _puzzle)); } }