From 1c1848d6fd3c5d466540e5c2fb1b5f0fd06ec500 Mon Sep 17 00:00:00 2001 From: Henrique Marlon Date: Fri, 20 Dec 2024 09:37:22 -0300 Subject: [PATCH 1/6] feat: workflow to deploy autogenerated docs --- .github/workflows/docs.yml | 50 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 .github/workflows/docs.yml diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..426c529 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,50 @@ +name: Deploy documentation to GitHub Pages + +on: + push: + branches: ["main"] + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +jobs: + build: + name: Build and upload artifact + runs-on: ubuntu-latest + defaults: + run: + shell: bash + working-directory: . + steps: + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: generate documentation artifacts + run: forge doc -b + + - name: Setup Pages + uses: actions/configure-pages@v4 + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: docs/book + + deploy: + name: Deploy to GitHub Pages + needs: build + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 From b5f6c4834a15ffe7a6fa900a5ae0315d786e9c23 Mon Sep 17 00:00:00 2001 From: Henrique Marlon Date: Fri, 20 Dec 2024 09:38:06 -0300 Subject: [PATCH 2/6] feat: :bookmark: abstract contract with voucher suport as output and some improvements --- src/BaseContract.sol | 90 --------------------------- src/CoprocessorAdapter.sol | 116 +++++++++++++++++++++++++++++++++++ src/ICoprocessor.sol | 15 ++++- src/ICoprocessorCallback.sol | 18 ++++-- src/ICoprocessorOutputs.sol | 21 +++++++ src/library/LibAddress.sol | 59 ++++++++++++++++++ src/library/LibError.sol | 18 ++++++ 7 files changed, 239 insertions(+), 98 deletions(-) delete mode 100644 src/BaseContract.sol create mode 100644 src/CoprocessorAdapter.sol create mode 100644 src/ICoprocessorOutputs.sol create mode 100644 src/library/LibAddress.sol create mode 100644 src/library/LibError.sol diff --git a/src/BaseContract.sol b/src/BaseContract.sol deleted file mode 100644 index a494ab6..0000000 --- a/src/BaseContract.sol +++ /dev/null @@ -1,90 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.28; - -import "./ICoprocessor.sol"; -import "./ICoprocessorCallback.sol"; - -/// @title BaseContract -/// @notice A base contract, which should be inherited for interacting with the Coprocessor -abstract contract BaseContract is ICoprocessorCallback { - ICoprocessor public coprocessor; - bytes32 public machineHash; - - error UnauthorizedCaller(address caller); - error InvalidOutputLength(uint256 length); - error ComputationNotFound(bytes32 payloadHash); - error MachineHashMismatch(bytes32 current, bytes32 expected); - error InvalidOutputSelector(bytes4 selector, bytes4 expected); - - /// @notice Tracks whether a computation has been sent for a specific input hash - mapping(bytes32 => bool) public computationSent; - - /// @notice Emitted when a result is received - event ResultReceived(bytes output); - - /// @param _coprocessorAddress The address of the ICoprocessor contract - /// @param _machineHash The machine hash associated with dapp whose logic the coProcessor would run - constructor(address _coprocessorAddress, bytes32 _machineHash) { - require(_coprocessorAddress != address(0), "Invalid coprocessor address"); - coprocessor = ICoprocessor(_coprocessorAddress); - machineHash = _machineHash; - } - - /// @notice Issues a task to the coprocessor - /// @param input The input data to process - function callCoprocessor(bytes calldata input) internal { - bytes32 inputHash = keccak256(input); - - require(!computationSent[inputHash], "Computation already sent"); - - computationSent[inputHash] = true; - - coprocessor.issueTask(machineHash, input, address(this)); - } - - /// @notice Callback function called by the coprocessor with outputs - /// @param _machineHash The machine hash associated with dapp whose logic the coProcessor would run - /// @param _payloadHash The hash of the input payload - /// @param outputs The outputs returned by the coprocessor - function coprocessorCallbackOutputsOnly(bytes32 _machineHash, bytes32 _payloadHash, bytes[] calldata outputs) - external - override - { - if (msg.sender != address(coprocessor)) { - revert UnauthorizedCaller(msg.sender); - } - - if (_machineHash != machineHash) { - revert MachineHashMismatch(_machineHash, machineHash); - } - - if (!computationSent[_payloadHash]) { - revert ComputationNotFound(_payloadHash); - } - - for (uint256 i = 0; i < outputs.length; i++) { - bytes calldata output = outputs[i]; - - if (output.length <= 3) { - revert InvalidOutputLength(output.length); - } - - bytes4 selector = bytes4(output[:4]); - bytes calldata arguments = output[4:]; - - if (selector != ICoprocessorOutputs.Notice.selector) { - revert InvalidOutputSelector(selector, ICoprocessorOutputs.Notice.selector); - } - - handleNotice(arguments); - } - - delete computationSent[_payloadHash]; - } - - /// @notice Handles a notice from the coprocessor - /// @param notice The notice data - function handleNotice(bytes calldata notice) internal virtual { - emit ResultReceived(notice); - } -} diff --git a/src/CoprocessorAdapter.sol b/src/CoprocessorAdapter.sol new file mode 100644 index 0000000..1ba8b80 --- /dev/null +++ b/src/CoprocessorAdapter.sol @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.28; + +import {LibError} from "./library/LibError.sol"; +import {ICoprocessor} from "./ICoprocessor.sol"; +import {LibAddress} from "./library/LibAddress.sol"; +import {ICoprocessorOutputs} from "./ICoprocessorOutputs.sol"; +import {ICoprocessorCallback} from "./ICoprocessorCallback.sol"; + +/// @title BaseContract +/// @notice A base contract, which should be inherited for interacting with the Coprocessor +abstract contract CoprocessorAdapter is ICoprocessorCallback { + using LibError for bytes; + using LibAddress for address; + + bytes32 public machineHash; + ICoprocessor public coprocessor; + + error UnauthorizedCaller(address caller); + error InvalidOutputLength(uint256 length); + error ComputationNotFound(bytes32 payloadHash); + error InsufficientFunds(uint256 value, uint256 balance); + error MachineHashMismatch(bytes32 current, bytes32 expected); + error InvalidOutputSelector(bytes4 selector, bytes4 expected); + + mapping(bytes32 => bool) public computationSent; + + /// @notice Initializes the contract with the coprocessor address and machine hash + /// @param _coprocessorAddress Address of the coprocessor + /// @param _machineHash Initial machine hash + constructor(address _coprocessorAddress, bytes32 _machineHash) { + coprocessor = ICoprocessor(_coprocessorAddress); + machineHash = _machineHash; + } + + /// @notice Issues a task to the coprocessor + /// @param input ABI-encoded input data for the coprocessor + function callCoprocessor(bytes calldata input) external { + bytes32 inputHash = keccak256(input); + computationSent[inputHash] = true; + coprocessor.issueTask(machineHash, input, address(this)); + } + + /// @notice Handles notices sent back from the coprocessor + /// @dev This function should be overridden by child contracts to define specific behavior + /// @param notice ABI-encoded notice data + function handleNotice(bytes memory notice) internal virtual {} + + /// @notice Callback function invoked by the coprocessor with computation outputs + /// @param _machineHash The hash of the machine that processed the task + /// @param _payloadHash The hash of the input payload + /// @param outputs Array of ABI-encoded outputs from the coprocessor + function coprocessorCallbackOutputsOnly( + bytes32 _machineHash, + bytes32 _payloadHash, + bytes[] calldata outputs + ) external override { + if (msg.sender != address(coprocessor)) { + revert UnauthorizedCaller(msg.sender); + } + + if (_machineHash != machineHash) { + revert MachineHashMismatch(_machineHash, machineHash); + } + + if (!computationSent[_payloadHash]) { + revert ComputationNotFound(_payloadHash); + } + + for (uint256 i = 0; i < outputs.length; i++) { + bytes calldata output = outputs[i]; + + if (output.length <= 3) { + revert InvalidOutputLength(output.length); + } + + bytes4 selector = bytes4(output[:4]); + bytes calldata arguments = output[4:]; + + if (selector == ICoprocessorOutputs.Notice.selector) { + handleNotice(abi.decode(arguments, (bytes))); + } else if (selector == ICoprocessorOutputs.Voucher.selector) { + _executeVoucher(arguments); + } else { + revert InvalidOutputSelector( + selector, + ICoprocessorOutputs.Notice.selector + ); + } + } + delete computationSent[_payloadHash]; + } + + /// @notice Executes a voucher + /// @dev This function decodes and executes a voucher with the specified parameters + /// @param arguments ABI-encoded arguments containing the destination, value, and payload + function _executeVoucher(bytes calldata arguments) internal { + address destination; + uint256 value; + bytes memory payload; + + (destination, value, payload) = abi.decode( + arguments, + (address, uint256, bytes) + ); + + bool enoughFunds; + uint256 balance; + + (enoughFunds, balance) = destination.safeCall(value, payload); + + if (!enoughFunds) { + revert InsufficientFunds(value, balance); + } + } +} diff --git a/src/ICoprocessor.sol b/src/ICoprocessor.sol index 1bc3668..66e8458 100644 --- a/src/ICoprocessor.sol +++ b/src/ICoprocessor.sol @@ -1,6 +1,17 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.28; +pragma solidity 0.8.28; + +/// @title ICoprocessor Interface +/// @notice Defines the interface for interacting with a coprocessor contract interface ICoprocessor { - function issueTask(bytes32 machineHash, bytes calldata input, address callbackAddress) external; + /// @notice Issues a task to the coprocessor + /// @param machineHash The hash of the machine to which the task is assigned + /// @param input The ABI-encoded input data for the task + /// @param callbackAddress The address to which the callback will be sent upon task completion + function issueTask( + bytes32 machineHash, + bytes calldata input, + address callbackAddress + ) external; } diff --git a/src/ICoprocessorCallback.sol b/src/ICoprocessorCallback.sol index 65cdbdc..9bff869 100644 --- a/src/ICoprocessorCallback.sol +++ b/src/ICoprocessorCallback.sol @@ -1,11 +1,17 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.28; -interface ICoprocessorOutputs { - function Notice(bytes calldata payload) external; -} +pragma solidity 0.8.28; +/// @title ICoprocessorCallback Interface +/// @notice Defines the callback mechanism for handling coprocessor outputs interface ICoprocessorCallback { - function coprocessorCallbackOutputsOnly(bytes32 machineHash, bytes32 payloadHash, bytes[] calldata outputs) - external; + /// @notice Handles outputs from the coprocessor + /// @param machineHash The hash of the machine that processed the task + /// @param payloadHash The hash of the input payload that generated these outputs + /// @param outputs Array of ABI-encoded outputs from the coprocessor + function coprocessorCallbackOutputsOnly( + bytes32 machineHash, + bytes32 payloadHash, + bytes[] calldata outputs + ) external; } diff --git a/src/ICoprocessorOutputs.sol b/src/ICoprocessorOutputs.sol new file mode 100644 index 0000000..31d762f --- /dev/null +++ b/src/ICoprocessorOutputs.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.28; + +/// @title ICoprocessorOutputs Interface +/// @notice Defines the outputs that can be generated by a coprocessor +interface ICoprocessorOutputs { + /// @notice Emits a notice event with the given payload + /// @param payload The ABI-encoded payload containing notice data + function Notice(bytes calldata payload) external; + + /// @notice Issues a voucher to execute a specific action + /// @param destination The address to which the voucher is directed + /// @param value The amount of ETH to transfer with the voucher + /// @param payload The ABI-encoded payload containing the action data + function Voucher( + address destination, + uint256 value, + bytes calldata payload + ) external; +} diff --git a/src/library/LibAddress.sol b/src/library/LibAddress.sol new file mode 100644 index 0000000..5831aff --- /dev/null +++ b/src/library/LibAddress.sol @@ -0,0 +1,59 @@ +// (c) Cartesi and individual authors (see AUTHORS) +// SPDX-License-Identifier: Apache-2.0 (see LICENSE) + +pragma solidity ^0.8.28; + +import {LibError} from "../library/LibError.sol"; + +library LibAddress { + using LibError for bytes; + + /// @notice Perform a low level call and raise error if failed + /// @param destination The address that will be called + /// @param value The amount of Wei to be transferred through the call + /// @param payload The payload, which—in the case of Solidity + /// contracts—encodes a function call + /// @return Whether the caller had enough Ether to make the call, + /// and the balance before the call + function safeCall( + address destination, + uint256 value, + bytes memory payload + ) internal returns (bool, uint256) { + address caller = address(this); + uint256 balance = caller.balance; + + if (value > balance) { + return (false, balance); + } + + bool success; + bytes memory returndata; + + (success, returndata) = destination.call{value: value}(payload); + + if (!success) { + returndata.raise(); + } + + return (true, balance); + } + + /// @notice Perform a delegate call and raise error if failed + /// @param destination The address that will be called + /// @param payload The payload, which—in the case of Solidity + /// libraries—encodes a function call + function safeDelegateCall( + address destination, + bytes memory payload + ) internal { + bool success; + bytes memory returndata; + + (success, returndata) = destination.delegatecall(payload); + + if (!success) { + returndata.raise(); + } + } +} \ No newline at end of file diff --git a/src/library/LibError.sol b/src/library/LibError.sol new file mode 100644 index 0000000..740ad16 --- /dev/null +++ b/src/library/LibError.sol @@ -0,0 +1,18 @@ +// (c) Cartesi and individual authors (see AUTHORS) +// SPDX-License-Identifier: Apache-2.0 (see LICENSE) + +pragma solidity ^0.8.28; + +library LibError { + /// @notice Raise error data + /// @param errordata Data returned by failed low-level call + function raise(bytes memory errordata) internal pure { + if (errordata.length == 0) { + revert(); + } else { + assembly { + revert(add(32, errordata), mload(errordata)) + } + } + } +} \ No newline at end of file From d8d691d176318efa685ee1cb9b59e42e93b00b1d Mon Sep 17 00:00:00 2001 From: Henrique Marlon Date: Fri, 20 Dec 2024 09:38:47 -0300 Subject: [PATCH 3/6] test: more tests --- test/BaseContract.t.sol | 57 ------- test/CoprocessorCallerTest.t.sol | 203 ++++++++++++++++++++++++ test/mock/CoprocessorMock.sol | 16 ++ test/utils/CoprocessorAdapterSample.sol | 26 +++ test/utils/Counter.sol | 18 +++ 5 files changed, 263 insertions(+), 57 deletions(-) delete mode 100644 test/BaseContract.t.sol create mode 100644 test/CoprocessorCallerTest.t.sol create mode 100644 test/mock/CoprocessorMock.sol create mode 100644 test/utils/CoprocessorAdapterSample.sol create mode 100644 test/utils/Counter.sol diff --git a/test/BaseContract.t.sol b/test/BaseContract.t.sol deleted file mode 100644 index ee934a5..0000000 --- a/test/BaseContract.t.sol +++ /dev/null @@ -1,57 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.28; - -import "forge-std/Test.sol"; -import "../src/BaseContract.sol"; -import "../src/ICoprocessor.sol"; -import "../src/ICoprocessorCallback.sol"; - -contract MockCoprocessor is ICoprocessor { - event TaskIssued(bytes32 machineHash, bytes input, address callback); - - function issueTask(bytes32 machineHash, bytes calldata input, address callback) external override { - emit TaskIssued(machineHash, input, callback); - } -} - -contract TestBaseContract is BaseContract { - constructor(address _coprocessorAddress, bytes32 _machineHash) BaseContract(_coprocessorAddress, _machineHash) {} - - function handleCallCoProcessor(bytes calldata input) external { - callCoprocessor(input); - } -} - -contract BaseContractTest is Test { - MockCoprocessor public mockCoprocessor; - TestBaseContract public testBaseContract; - - bytes32 constant MACHINE_HASH = keccak256("machine_hash"); - - function setUp() public { - // Deploy the mock coprocessor - mockCoprocessor = new MockCoprocessor(); - - // Deploy the test contract - testBaseContract = new TestBaseContract(address(mockCoprocessor), MACHINE_HASH); - } - - function testInitialization() public view { - assertEq(address(testBaseContract.coprocessor()), address(mockCoprocessor), "Coprocessor address mismatch"); - assertEq(testBaseContract.machineHash(), MACHINE_HASH, "Machine hash mismatch"); - } - - function testCallCoprocessor() public { - bytes memory input = "test input"; - bytes32 inputHash = keccak256(input); - - // Expect the issueTask event - vm.expectEmit(true, true, true, true); - emit MockCoprocessor.TaskIssued(MACHINE_HASH, input, address(testBaseContract)); - - testBaseContract.handleCallCoProcessor(input); - - // Ensure computationSent is updated - assertTrue(testBaseContract.computationSent(inputHash), "ComputationSent should be true"); - } -} diff --git a/test/CoprocessorCallerTest.t.sol b/test/CoprocessorCallerTest.t.sol new file mode 100644 index 0000000..4067d03 --- /dev/null +++ b/test/CoprocessorCallerTest.t.sol @@ -0,0 +1,203 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {Test} from "forge-std/Test.sol"; +import {Counter} from "./utils/Counter.sol"; +import {console} from "forge-std/console.sol"; +import {CoprocessorMock} from "./mock/CoprocessorMock.sol"; +import {CoprocessorAdapterSample} from "./utils/CoprocessorAdapterSample.sol"; + +contract TestCoprocessorAdapterSampl is Test { + address caller = vm.addr(4); + + bytes32 machineHash = bytes32(0); + + Counter counter; + CoprocessorMock mock; + CoprocessorAdapterSample sample; + + function setUp() public { + counter = new Counter(); + mock = new CoprocessorMock(); + sample = new CoprocessorAdapterSample(address(mock), machineHash); + } + + function testCallCoprocessorAdapterSampleWithValilNoticeInput() public { + bytes memory encoded_tx = abi.encodeWithSignature( + "setNumber(uint256)", + 1596 + ); + + bytes memory payload = abi.encode(address(counter), encoded_tx); + + sample.callCoprocessor(payload); + + bytes memory notice = abi.encodeWithSignature("Notice(bytes)", payload); + + bytes[] memory outputs = new bytes[](1); + outputs[0] = notice; + + vm.expectEmit(); + + emit CoprocessorMock.TaskIssued(machineHash, payload, address(sample)); + + sample.callCoprocessor(payload); + + vm.prank(address(mock)); + + sample.coprocessorCallbackOutputsOnly( + machineHash, + keccak256(payload), + outputs + ); + + uint256 number = counter.number(); + assertEq(number, 1596); + } + + function testCallCoprocessorAdapterSampleWithValidVoucherInput() public { + bytes memory encoded_tx = abi.encodeWithSignature( + "setNumber(uint256)", + 1596 + ); + + bytes memory payload = abi.encode(address(counter), encoded_tx); + + sample.callCoprocessor(payload); + + bytes memory voucher = abi.encodeWithSignature( + "Voucher(address,uint256,bytes)", + address(counter), + 0, + encoded_tx + ); + + bytes[] memory outputs = new bytes[](1); + outputs[0] = voucher; + + vm.expectEmit(); + + emit CoprocessorMock.TaskIssued(machineHash, payload, address(sample)); + + sample.callCoprocessor(payload); + + vm.prank(address(mock)); + + sample.coprocessorCallbackOutputsOnly( + machineHash, + keccak256(payload), + outputs + ); + + uint256 number = counter.number(); + assertEq(number, 1596); + } + + function testCallCoprocessorAdapterSampleWithValidVoucherInputAndValue() public { + bytes memory encoded_tx = abi.encodeWithSignature( + "setNumberPaid(uint256)", + 1596 + ); + + bytes memory payload = abi.encode(address(counter), encoded_tx); + + sample.callCoprocessor(payload); + + bytes memory voucher = abi.encodeWithSignature( + "Voucher(address,uint256,bytes)", + address(counter), + 1596, + encoded_tx + ); + + bytes[] memory outputs = new bytes[](1); + outputs[0] = voucher; + + vm.expectEmit(); + + emit CoprocessorMock.TaskIssued(machineHash, payload, address(sample)); + + vm.deal(address(sample), 2024); + + sample.callCoprocessor(payload); + + vm.prank(address(mock)); + + sample.coprocessorCallbackOutputsOnly( + machineHash, + keccak256(payload), + outputs + ); + + uint256 number = counter.number(); + assertEq(number, 1596); + } + + function testCallCoprocessorAdapterSampleWithInvalidMachineHash() public { + bytes memory encoded_tx = abi.encodeWithSignature( + "setNumber(uint256)", + 1596 + ); + + bytes memory payload = abi.encode(address(counter), encoded_tx); + + sample.callCoprocessor(payload); + + bytes memory notice = abi.encodeWithSignature("Notice(bytes)", payload); + + bytes[] memory outputs = new bytes[](1); + outputs[0] = notice; + + vm.expectEmit(); + + emit CoprocessorMock.TaskIssued(machineHash, payload, address(sample)); + + sample.callCoprocessor(payload); + + vm.prank(address(mock)); + + bytes32 invalidMachineHash = keccak256("1596"); + + vm.expectRevert(); + + sample.coprocessorCallbackOutputsOnly( + invalidMachineHash, + keccak256(payload), + outputs + ); + } + + function testCallCoprocessorAdapterSampleWithInvalidPayloadHash() public { + bytes memory encoded_tx = abi.encodeWithSignature( + "setNumber(uint256)", + 1596 + ); + + bytes memory payload = abi.encode(address(counter), encoded_tx); + + sample.callCoprocessor(payload); + + bytes memory notice = abi.encodeWithSignature("Notice(bytes)", payload); + + bytes[] memory outputs = new bytes[](1); + outputs[0] = notice; + + vm.expectEmit(); + + emit CoprocessorMock.TaskIssued(machineHash, payload, address(sample)); + + sample.callCoprocessor(payload); + + vm.prank(address(mock)); + + bytes32 invalidPayloadHash = keccak256("1596"); + + vm.expectRevert(); + + sample.coprocessorCallbackOutputsOnly( + machineHash, + invalidPayloadHash, + outputs + ); + } +} diff --git a/test/mock/CoprocessorMock.sol b/test/mock/CoprocessorMock.sol new file mode 100644 index 0000000..41898d8 --- /dev/null +++ b/test/mock/CoprocessorMock.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.9; + +import {ICoprocessor} from "../../src/ICoprocessor.sol"; + +contract CoprocessorMock is ICoprocessor { + event TaskIssued(bytes32 machineHash, bytes input, address callback); + + function issueTask( + bytes32 machineHash, + bytes calldata input, + address callback + ) public { + emit TaskIssued(machineHash, input, callback); + } +} diff --git a/test/utils/CoprocessorAdapterSample.sol b/test/utils/CoprocessorAdapterSample.sol new file mode 100644 index 0000000..2029acc --- /dev/null +++ b/test/utils/CoprocessorAdapterSample.sol @@ -0,0 +1,26 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.8.28; + +import "../../src/CoprocessorAdapter.sol"; + +contract CoprocessorAdapterSample is CoprocessorAdapter { + constructor( + address _coprocessorAddress, + bytes32 _machineHash + ) CoprocessorAdapter(_coprocessorAddress, _machineHash) {} + + function handleNotice(bytes memory notice) internal override { + address destination; + bytes memory decodedPayload; + + (destination, decodedPayload) = abi.decode( + notice, + (address, bytes) + ); + + bool success; + bytes memory returndata; + + (success, returndata) = destination.call(decodedPayload); + } +} diff --git a/test/utils/Counter.sol b/test/utils/Counter.sol new file mode 100644 index 0000000..ab24cbb --- /dev/null +++ b/test/utils/Counter.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +contract Counter { + uint256 public number; + + function setNumber(uint256 newNumber) public { + number = newNumber; + } + + function setNumberPaid(uint256 newNumber) public payable { + number = newNumber; + } + + function increment() public { + number++; + } +} From d92b2f4881e85d29fbc025aa0fedf79f83379868 Mon Sep 17 00:00:00 2001 From: Henrique Marlon Date: Fri, 20 Dec 2024 09:40:10 -0300 Subject: [PATCH 4/6] chore: remove script folder --- script/Counter.s.sol | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 script/Counter.s.sol diff --git a/script/Counter.s.sol b/script/Counter.s.sol deleted file mode 100644 index 0669bf7..0000000 --- a/script/Counter.s.sol +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import {Script, console} from "forge-std/Script.sol"; -import {BaseContract} from "../src/BaseContract.sol"; - -contract CounterScript is Script { - BaseContract public baseContract; - - function setUp() public {} - - function run() public { - vm.startBroadcast(); - - // baseContract = new BaseContract(); - - vm.stopBroadcast(); - } -} From db2faf69ac86212eb57ab20ad86f347e01579457 Mon Sep 17 00:00:00 2001 From: Henrique Marlon Date: Fri, 20 Dec 2024 09:41:52 -0300 Subject: [PATCH 5/6] chore: dealing with the formatter --- src/CoprocessorAdapter.sol | 19 ++----- src/ICoprocessor.sol | 6 +- src/ICoprocessorCallback.sol | 7 +-- src/ICoprocessorOutputs.sol | 6 +- src/library/LibAddress.sol | 13 +---- src/library/LibError.sol | 2 +- test/CoprocessorCallerTest.t.sol | 73 +++++-------------------- test/mock/CoprocessorMock.sol | 6 +- test/utils/CoprocessorAdapterSample.sol | 12 ++-- 9 files changed, 34 insertions(+), 110 deletions(-) diff --git a/src/CoprocessorAdapter.sol b/src/CoprocessorAdapter.sol index 1ba8b80..4e5bc5b 100644 --- a/src/CoprocessorAdapter.sol +++ b/src/CoprocessorAdapter.sol @@ -50,11 +50,10 @@ abstract contract CoprocessorAdapter is ICoprocessorCallback { /// @param _machineHash The hash of the machine that processed the task /// @param _payloadHash The hash of the input payload /// @param outputs Array of ABI-encoded outputs from the coprocessor - function coprocessorCallbackOutputsOnly( - bytes32 _machineHash, - bytes32 _payloadHash, - bytes[] calldata outputs - ) external override { + function coprocessorCallbackOutputsOnly(bytes32 _machineHash, bytes32 _payloadHash, bytes[] calldata outputs) + external + override + { if (msg.sender != address(coprocessor)) { revert UnauthorizedCaller(msg.sender); } @@ -82,10 +81,7 @@ abstract contract CoprocessorAdapter is ICoprocessorCallback { } else if (selector == ICoprocessorOutputs.Voucher.selector) { _executeVoucher(arguments); } else { - revert InvalidOutputSelector( - selector, - ICoprocessorOutputs.Notice.selector - ); + revert InvalidOutputSelector(selector, ICoprocessorOutputs.Notice.selector); } } delete computationSent[_payloadHash]; @@ -99,10 +95,7 @@ abstract contract CoprocessorAdapter is ICoprocessorCallback { uint256 value; bytes memory payload; - (destination, value, payload) = abi.decode( - arguments, - (address, uint256, bytes) - ); + (destination, value, payload) = abi.decode(arguments, (address, uint256, bytes)); bool enoughFunds; uint256 balance; diff --git a/src/ICoprocessor.sol b/src/ICoprocessor.sol index 66e8458..c95b6ce 100644 --- a/src/ICoprocessor.sol +++ b/src/ICoprocessor.sol @@ -9,9 +9,5 @@ interface ICoprocessor { /// @param machineHash The hash of the machine to which the task is assigned /// @param input The ABI-encoded input data for the task /// @param callbackAddress The address to which the callback will be sent upon task completion - function issueTask( - bytes32 machineHash, - bytes calldata input, - address callbackAddress - ) external; + function issueTask(bytes32 machineHash, bytes calldata input, address callbackAddress) external; } diff --git a/src/ICoprocessorCallback.sol b/src/ICoprocessorCallback.sol index 9bff869..02fc05f 100644 --- a/src/ICoprocessorCallback.sol +++ b/src/ICoprocessorCallback.sol @@ -9,9 +9,6 @@ interface ICoprocessorCallback { /// @param machineHash The hash of the machine that processed the task /// @param payloadHash The hash of the input payload that generated these outputs /// @param outputs Array of ABI-encoded outputs from the coprocessor - function coprocessorCallbackOutputsOnly( - bytes32 machineHash, - bytes32 payloadHash, - bytes[] calldata outputs - ) external; + function coprocessorCallbackOutputsOnly(bytes32 machineHash, bytes32 payloadHash, bytes[] calldata outputs) + external; } diff --git a/src/ICoprocessorOutputs.sol b/src/ICoprocessorOutputs.sol index 31d762f..c5b14ae 100644 --- a/src/ICoprocessorOutputs.sol +++ b/src/ICoprocessorOutputs.sol @@ -13,9 +13,5 @@ interface ICoprocessorOutputs { /// @param destination The address to which the voucher is directed /// @param value The amount of ETH to transfer with the voucher /// @param payload The ABI-encoded payload containing the action data - function Voucher( - address destination, - uint256 value, - bytes calldata payload - ) external; + function Voucher(address destination, uint256 value, bytes calldata payload) external; } diff --git a/src/library/LibAddress.sol b/src/library/LibAddress.sol index 5831aff..af92529 100644 --- a/src/library/LibAddress.sol +++ b/src/library/LibAddress.sol @@ -15,11 +15,7 @@ library LibAddress { /// contracts—encodes a function call /// @return Whether the caller had enough Ether to make the call, /// and the balance before the call - function safeCall( - address destination, - uint256 value, - bytes memory payload - ) internal returns (bool, uint256) { + function safeCall(address destination, uint256 value, bytes memory payload) internal returns (bool, uint256) { address caller = address(this); uint256 balance = caller.balance; @@ -43,10 +39,7 @@ library LibAddress { /// @param destination The address that will be called /// @param payload The payload, which—in the case of Solidity /// libraries—encodes a function call - function safeDelegateCall( - address destination, - bytes memory payload - ) internal { + function safeDelegateCall(address destination, bytes memory payload) internal { bool success; bytes memory returndata; @@ -56,4 +49,4 @@ library LibAddress { returndata.raise(); } } -} \ No newline at end of file +} diff --git a/src/library/LibError.sol b/src/library/LibError.sol index 740ad16..e4c6980 100644 --- a/src/library/LibError.sol +++ b/src/library/LibError.sol @@ -15,4 +15,4 @@ library LibError { } } } -} \ No newline at end of file +} diff --git a/test/CoprocessorCallerTest.t.sol b/test/CoprocessorCallerTest.t.sol index 4067d03..da38e59 100644 --- a/test/CoprocessorCallerTest.t.sol +++ b/test/CoprocessorCallerTest.t.sol @@ -23,10 +23,7 @@ contract TestCoprocessorAdapterSampl is Test { } function testCallCoprocessorAdapterSampleWithValilNoticeInput() public { - bytes memory encoded_tx = abi.encodeWithSignature( - "setNumber(uint256)", - 1596 - ); + bytes memory encoded_tx = abi.encodeWithSignature("setNumber(uint256)", 1596); bytes memory payload = abi.encode(address(counter), encoded_tx); @@ -45,32 +42,21 @@ contract TestCoprocessorAdapterSampl is Test { vm.prank(address(mock)); - sample.coprocessorCallbackOutputsOnly( - machineHash, - keccak256(payload), - outputs - ); + sample.coprocessorCallbackOutputsOnly(machineHash, keccak256(payload), outputs); uint256 number = counter.number(); assertEq(number, 1596); } function testCallCoprocessorAdapterSampleWithValidVoucherInput() public { - bytes memory encoded_tx = abi.encodeWithSignature( - "setNumber(uint256)", - 1596 - ); + bytes memory encoded_tx = abi.encodeWithSignature("setNumber(uint256)", 1596); bytes memory payload = abi.encode(address(counter), encoded_tx); sample.callCoprocessor(payload); - bytes memory voucher = abi.encodeWithSignature( - "Voucher(address,uint256,bytes)", - address(counter), - 0, - encoded_tx - ); + bytes memory voucher = + abi.encodeWithSignature("Voucher(address,uint256,bytes)", address(counter), 0, encoded_tx); bytes[] memory outputs = new bytes[](1); outputs[0] = voucher; @@ -83,32 +69,21 @@ contract TestCoprocessorAdapterSampl is Test { vm.prank(address(mock)); - sample.coprocessorCallbackOutputsOnly( - machineHash, - keccak256(payload), - outputs - ); + sample.coprocessorCallbackOutputsOnly(machineHash, keccak256(payload), outputs); uint256 number = counter.number(); assertEq(number, 1596); } function testCallCoprocessorAdapterSampleWithValidVoucherInputAndValue() public { - bytes memory encoded_tx = abi.encodeWithSignature( - "setNumberPaid(uint256)", - 1596 - ); + bytes memory encoded_tx = abi.encodeWithSignature("setNumberPaid(uint256)", 1596); bytes memory payload = abi.encode(address(counter), encoded_tx); sample.callCoprocessor(payload); - bytes memory voucher = abi.encodeWithSignature( - "Voucher(address,uint256,bytes)", - address(counter), - 1596, - encoded_tx - ); + bytes memory voucher = + abi.encodeWithSignature("Voucher(address,uint256,bytes)", address(counter), 1596, encoded_tx); bytes[] memory outputs = new bytes[](1); outputs[0] = voucher; @@ -118,26 +93,19 @@ contract TestCoprocessorAdapterSampl is Test { emit CoprocessorMock.TaskIssued(machineHash, payload, address(sample)); vm.deal(address(sample), 2024); - + sample.callCoprocessor(payload); vm.prank(address(mock)); - sample.coprocessorCallbackOutputsOnly( - machineHash, - keccak256(payload), - outputs - ); + sample.coprocessorCallbackOutputsOnly(machineHash, keccak256(payload), outputs); uint256 number = counter.number(); assertEq(number, 1596); } function testCallCoprocessorAdapterSampleWithInvalidMachineHash() public { - bytes memory encoded_tx = abi.encodeWithSignature( - "setNumber(uint256)", - 1596 - ); + bytes memory encoded_tx = abi.encodeWithSignature("setNumber(uint256)", 1596); bytes memory payload = abi.encode(address(counter), encoded_tx); @@ -160,18 +128,11 @@ contract TestCoprocessorAdapterSampl is Test { vm.expectRevert(); - sample.coprocessorCallbackOutputsOnly( - invalidMachineHash, - keccak256(payload), - outputs - ); + sample.coprocessorCallbackOutputsOnly(invalidMachineHash, keccak256(payload), outputs); } function testCallCoprocessorAdapterSampleWithInvalidPayloadHash() public { - bytes memory encoded_tx = abi.encodeWithSignature( - "setNumber(uint256)", - 1596 - ); + bytes memory encoded_tx = abi.encodeWithSignature("setNumber(uint256)", 1596); bytes memory payload = abi.encode(address(counter), encoded_tx); @@ -194,10 +155,6 @@ contract TestCoprocessorAdapterSampl is Test { vm.expectRevert(); - sample.coprocessorCallbackOutputsOnly( - machineHash, - invalidPayloadHash, - outputs - ); + sample.coprocessorCallbackOutputsOnly(machineHash, invalidPayloadHash, outputs); } } diff --git a/test/mock/CoprocessorMock.sol b/test/mock/CoprocessorMock.sol index 41898d8..204de93 100644 --- a/test/mock/CoprocessorMock.sol +++ b/test/mock/CoprocessorMock.sol @@ -6,11 +6,7 @@ import {ICoprocessor} from "../../src/ICoprocessor.sol"; contract CoprocessorMock is ICoprocessor { event TaskIssued(bytes32 machineHash, bytes input, address callback); - function issueTask( - bytes32 machineHash, - bytes calldata input, - address callback - ) public { + function issueTask(bytes32 machineHash, bytes calldata input, address callback) public { emit TaskIssued(machineHash, input, callback); } } diff --git a/test/utils/CoprocessorAdapterSample.sol b/test/utils/CoprocessorAdapterSample.sol index 2029acc..a1536dc 100644 --- a/test/utils/CoprocessorAdapterSample.sol +++ b/test/utils/CoprocessorAdapterSample.sol @@ -4,19 +4,15 @@ pragma solidity ^0.8.28; import "../../src/CoprocessorAdapter.sol"; contract CoprocessorAdapterSample is CoprocessorAdapter { - constructor( - address _coprocessorAddress, - bytes32 _machineHash - ) CoprocessorAdapter(_coprocessorAddress, _machineHash) {} + constructor(address _coprocessorAddress, bytes32 _machineHash) + CoprocessorAdapter(_coprocessorAddress, _machineHash) + {} function handleNotice(bytes memory notice) internal override { address destination; bytes memory decodedPayload; - (destination, decodedPayload) = abi.decode( - notice, - (address, bytes) - ); + (destination, decodedPayload) = abi.decode(notice, (address, bytes)); bool success; bytes memory returndata; From ea97b0dc019e01e7af3023821de83f83b7d9255c Mon Sep 17 00:00:00 2001 From: Idogwu Emmanuel Chinonso <104998136+Nonnyjoe@users.noreply.github.com> Date: Sun, 22 Dec 2024 09:32:44 +0000 Subject: [PATCH 6/6] mod: corrected contract title --- src/CoprocessorAdapter.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CoprocessorAdapter.sol b/src/CoprocessorAdapter.sol index 4e5bc5b..c309ed9 100644 --- a/src/CoprocessorAdapter.sol +++ b/src/CoprocessorAdapter.sol @@ -7,7 +7,7 @@ import {LibAddress} from "./library/LibAddress.sol"; import {ICoprocessorOutputs} from "./ICoprocessorOutputs.sol"; import {ICoprocessorCallback} from "./ICoprocessorCallback.sol"; -/// @title BaseContract +/// @title CoprocessorAdapter /// @notice A base contract, which should be inherited for interacting with the Coprocessor abstract contract CoprocessorAdapter is ICoprocessorCallback { using LibError for bytes;