From ed5306e53122867d7b989653a06b41343940a4b6 Mon Sep 17 00:00:00 2001 From: Ferran Borreguero Date: Fri, 5 Jan 2024 21:25:11 +0100 Subject: [PATCH 1/9] Suave forge integration without SuaveForge.sol (#11) * Some hacks on forge * return sig-stripped calldata from fallback (#10) * Oh my... this really works * Do not mirror SuaveForge.sol anymore * More updates * Write comment * Fix CI * Enable ffi on ci test * Add REAMDE * Add lang to readme code --------- Co-authored-by: brock smedley <2791467+zeroXbrock@users.noreply.github.com> --- .github/workflows/ci.yml | 23 +++- .github/workflows/suave-lib-sync.yml | 2 - README.md | 35 ++++++ src/Test.sol | 15 +++ src/forge/Connector.sol | 47 ++++++++ src/forge/Registry.sol | 40 +++++++ src/suavelib/SuaveForge.sol | 167 -------------------------- test/Forge.t.sol | 20 ++++ tools/forge-gen/README.md | 13 +++ tools/forge-gen/go.mod | 3 + tools/forge-gen/main.go | 168 +++++++++++++++++++++++++++ 11 files changed, 363 insertions(+), 170 deletions(-) create mode 100644 src/Test.sol create mode 100644 src/forge/Connector.sol create mode 100644 src/forge/Registry.sol delete mode 100644 src/suavelib/SuaveForge.sol create mode 100644 test/Forge.t.sol create mode 100644 tools/forge-gen/README.md create mode 100644 tools/forge-gen/go.mod create mode 100644 tools/forge-gen/main.go diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fb7f9dc..88886fb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,6 +14,27 @@ jobs: steps: - uses: actions/checkout@v2 + - name: Checkout suave-geth repo + uses: actions/checkout@v4 + with: + repository: flashbots/suave-geth + path: suave-geth + persist-credentials: false + fetch-depth: 0 + + - name: Build suave + run: | + cd suave-geth + make suave + + - name: Include the binary on $PATH + run: | + echo "$(pwd)/suave-geth/build/bin" >> $GITHUB_PATH + + - name: Run suave + run: | + ./suave-geth/build/bin/suave --suave.dev & + - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 with: @@ -23,4 +44,4 @@ jobs: run: forge install - name: Run tests - run: forge test + run: forge test --ffi diff --git a/.github/workflows/suave-lib-sync.yml b/.github/workflows/suave-lib-sync.yml index 09552e7..cc581d5 100644 --- a/.github/workflows/suave-lib-sync.yml +++ b/.github/workflows/suave-lib-sync.yml @@ -36,9 +36,7 @@ jobs: - name: Mirror run: | cp suave-geth/suave/sol/libraries/Suave.sol ./src/suavelib/Suave.sol - cp suave-geth/suave/sol/libraries/SuaveForge.sol ./src/suavelib/SuaveForge.sol git add ./src/suavelib/Suave.sol - git add ./src/suavelib/SuaveForge.sol rm -rf suave-geth - name: Create Pull Request diff --git a/README.md b/README.md index 27c171a..144eebd 100644 --- a/README.md +++ b/README.md @@ -39,3 +39,38 @@ contract Example { } } ``` + +## Forge integration + +In order to use `forge`, you need to have a running `Suave` node and the `suave` binary in your path. + +To run `Suave` in development mode, use the following command: + +```bash +$ suave --suave.dev +``` + +Then, your `forge` scripts/test must import the `SuaveEnabled` contract from the `suave-std/Test.sol` file. + +```solidity +// SPDX-License-Identifier: Unlicense +pragma solidity ^0.8.13; + +import "forge-std/Test.sol"; +import "suave-std/Test.sol"; +import "suave-std/Suave.sol"; + +contract TestForge is Test, SuaveEnabled { + address[] public addressList = [0xC8df3686b4Afb2BB53e60EAe97EF043FE03Fb829]; + + function testConfidentialStore() public { + Suave.DataRecord memory record = Suave.newDataRecord(0, addressList, addressList, "namespace"); + + bytes memory value = abi.encode("suave works with forge!"); + Suave.confidentialStore(record.id, "key1", value); + + bytes memory found = Suave.confidentialRetrieve(record.id, "key1"); + assertEq(keccak256(found), keccak256(value)); + } +} +``` diff --git a/src/Test.sol b/src/Test.sol new file mode 100644 index 0000000..9a78e0f --- /dev/null +++ b/src/Test.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.8; + +import "./forge/Registry.sol"; + +contract SuaveEnabled { + function setUp() public { + // TODO: Add checks to validate that: + // - User is running the test with ffi. Since vm.ffi is deployed as a contract, the error if ffi is not active + // is reported as a Suave.PeekerReverted error and it is not clear what the problem is. + // - Suave binary is on $PATH and Suave is running. This could be done with ffi calls to the suave binary. + // Put this logic inside `enable` itself. + Registry.enable(); + } +} diff --git a/src/forge/Connector.sol b/src/forge/Connector.sol new file mode 100644 index 0000000..2e2f4fd --- /dev/null +++ b/src/forge/Connector.sol @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.8; + +interface connectorVM { + function ffi(string[] calldata commandInput) external view returns (bytes memory result); +} + +contract Connector { + connectorVM constant vm = connectorVM(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); + + function forgeIt(bytes memory addr, bytes memory data) internal view returns (bytes memory) { + string memory addrHex = iToHex(addr); + string memory dataHex = iToHex(data); + + string[] memory inputs = new string[](4); + inputs[0] = "suave"; + inputs[1] = "forge"; + inputs[2] = addrHex; + inputs[3] = dataHex; + + bytes memory res = vm.ffi(inputs); + return res; + } + + function iToHex(bytes memory buffer) public pure returns (string memory) { + bytes memory converted = new bytes(buffer.length * 2); + + bytes memory _base = "0123456789abcdef"; + + for (uint256 i = 0; i < buffer.length; i++) { + converted[i * 2] = _base[uint8(buffer[i]) / _base.length]; + converted[i * 2 + 1] = _base[uint8(buffer[i]) % _base.length]; + } + + return string(abi.encodePacked("0x", converted)); + } + + fallback() external { + bytes memory msgdata = forgeIt(abi.encodePacked(address(this)), msg.data); + + assembly { + let location := msgdata + let length := mload(msgdata) + return(add(location, 0x20), length) + } + } +} diff --git a/src/forge/Registry.sol b/src/forge/Registry.sol new file mode 100644 index 0000000..526181d --- /dev/null +++ b/src/forge/Registry.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.8; + +import "../suavelib/Suave.sol"; + +interface registryVM { + function etch(address, bytes calldata) external; +} + +library Registry { + registryVM constant vm = registryVM(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); + + function enableLib(address addr) public { + // code for Wrapper + bytes memory code = + hex"608060405234801561001057600080fd5b506004361061002b5760003560e01c8063671ff786146100a1575b6040516bffffffffffffffffffffffff193060601b1660208201526000906100959060340160408051808303601f19018152602036601f8101829004820285018201909352828452909291600091819084018382808284376000920191909152506100ca92505050565b90508081518060208301f35b6100b46100af36600461048c565b610259565b6040516100c1919061055c565b60405180910390f35b606060006100d784610259565b905060006100e484610259565b60408051600480825260a0820190925291925060009190816020015b606081526020019060019003908161010057905050905060405180604001604052806005815260200164737561766560d81b8152508160008151811061014857610148610576565b602002602001018190525060405180604001604052806005815260200164666f72676560d81b8152508160018151811061018457610184610576565b602002602001018190525082816002815181106101a3576101a3610576565b602002602001018190525081816003815181106101c2576101c2610576565b6020908102919091010152604051638916046760e01b8152600090737109709ecfa91a80626ff3989d68f67f5b1dd12d9063891604679061020790859060040161058c565b600060405180830381865afa158015610224573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261024c91908101906105ee565b9450505050505b92915050565b606060008251600261026b919061067b565b67ffffffffffffffff8111156102835761028361041d565b6040519080825280601f01601f1916602001820160405280156102ad576020820181803683370190505b5060408051808201909152601081526f181899199a1a9b1b9c1cb0b131b232b360811b602082015290915060005b84518110156103f3578182518683815181106102f9576102f9610576565b016020015161030b919060f81c6106a8565b8151811061031b5761031b610576565b01602001516001600160f81b0319168361033683600261067b565b8151811061034657610346610576565b60200101906001600160f81b031916908160001a90535081825186838151811061037257610372610576565b0160200151610384919060f81c6106bc565b8151811061039457610394610576565b01602001516001600160f81b031916836103af83600261067b565b6103ba9060016106d0565b815181106103ca576103ca610576565b60200101906001600160f81b031916908160001a905350806103eb816106e3565b9150506102db565b508160405160200161040591906106fc565b60405160208183030381529060405292505050919050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff8111828210171561045c5761045c61041d565b604052919050565b600067ffffffffffffffff82111561047e5761047e61041d565b50601f01601f191660200190565b60006020828403121561049e57600080fd5b813567ffffffffffffffff8111156104b557600080fd5b8201601f810184136104c657600080fd5b80356104d96104d482610464565b610433565b8181528560208385010111156104ee57600080fd5b81602084016020830137600091810160200191909152949350505050565b60005b8381101561052757818101518382015260200161050f565b50506000910152565b6000815180845261054881602086016020860161050c565b601f01601f19169290920160200192915050565b60208152600061056f6020830184610530565b9392505050565b634e487b7160e01b600052603260045260246000fd5b6000602080830181845280855180835260408601915060408160051b870101925083870160005b828110156105e157603f198886030184526105cf858351610530565b945092850192908501906001016105b3565b5092979650505050505050565b60006020828403121561060057600080fd5b815167ffffffffffffffff81111561061757600080fd5b8201601f8101841361062857600080fd5b80516106366104d482610464565b81815285602083850101111561064b57600080fd5b61065c82602083016020860161050c565b95945050505050565b634e487b7160e01b600052601160045260246000fd5b808202811582820484141761025357610253610665565b634e487b7160e01b600052601260045260246000fd5b6000826106b7576106b7610692565b500490565b6000826106cb576106cb610692565b500690565b8082018082111561025357610253610665565b6000600182016106f5576106f5610665565b5060010190565b61060f60f31b81526000825161071981600285016020870161050c565b919091016002019291505056fea2646970667358221220e66d500bc9a9ca9c0748086adfc51de57c85b7c9f66cc760e823099f0439820b64736f6c63430008130033"; + vm.etch(addr, code); + } + + function enable() public { + enableLib(Suave.IS_CONFIDENTIAL_ADDR); + enableLib(Suave.BUILD_ETH_BLOCK); + enableLib(Suave.CONFIDENTIAL_INPUTS); + enableLib(Suave.CONFIDENTIAL_RETRIEVE); + enableLib(Suave.CONFIDENTIAL_STORE); + enableLib(Suave.DO_HTTPREQUEST); + enableLib(Suave.ETHCALL); + enableLib(Suave.EXTRACT_HINT); + enableLib(Suave.FETCH_DATA_RECORDS); + enableLib(Suave.FILL_MEV_SHARE_BUNDLE); + enableLib(Suave.NEW_BUILDER); + enableLib(Suave.NEW_DATA_RECORD); + enableLib(Suave.SIGN_ETH_TRANSACTION); + enableLib(Suave.SIGN_MESSAGE); + enableLib(Suave.SIMULATE_BUNDLE); + enableLib(Suave.SIMULATE_TRANSACTION); + enableLib(Suave.SUBMIT_BUNDLE_JSON_RPC); + enableLib(Suave.SUBMIT_ETH_BLOCK_TO_RELAY); + } +} diff --git a/src/suavelib/SuaveForge.sol b/src/suavelib/SuaveForge.sol deleted file mode 100644 index e42d6c8..0000000 --- a/src/suavelib/SuaveForge.sol +++ /dev/null @@ -1,167 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.8; - -import "./Suave.sol"; - -interface Vm { - function ffi(string[] calldata commandInput) external view returns (bytes memory result); -} - -library SuaveForge { - Vm constant vm = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); - - function forgeIt(string memory addr, bytes memory data) internal view returns (bytes memory) { - string memory dataHex = iToHex(data); - - string[] memory inputs = new string[](4); - inputs[0] = "suave"; - inputs[1] = "forge"; - inputs[2] = addr; - inputs[3] = dataHex; - - bytes memory res = vm.ffi(inputs); - return res; - } - - function iToHex(bytes memory buffer) public pure returns (string memory) { - bytes memory converted = new bytes(buffer.length * 2); - - bytes memory _base = "0123456789abcdef"; - - for (uint256 i = 0; i < buffer.length; i++) { - converted[i * 2] = _base[uint8(buffer[i]) / _base.length]; - converted[i * 2 + 1] = _base[uint8(buffer[i]) % _base.length]; - } - - return string(abi.encodePacked("0x", converted)); - } - - function buildEthBlock(Suave.BuildBlockArgs memory blockArgs, Suave.DataId dataId, string memory namespace) - internal - view - returns (bytes memory, bytes memory) - { - bytes memory data = - forgeIt("0x0000000000000000000000000000000042100001", abi.encode(blockArgs, dataId, namespace)); - - return abi.decode(data, (bytes, bytes)); - } - - function confidentialInputs() internal view returns (bytes memory) { - bytes memory data = forgeIt("0x0000000000000000000000000000000042010001", abi.encode()); - - return data; - } - - function confidentialRetrieve(Suave.DataId dataId, string memory key) internal view returns (bytes memory) { - bytes memory data = forgeIt("0x0000000000000000000000000000000042020001", abi.encode(dataId, key)); - - return data; - } - - function confidentialStore(Suave.DataId dataId, string memory key, bytes memory data1) internal view { - bytes memory data = forgeIt("0x0000000000000000000000000000000042020000", abi.encode(dataId, key, data1)); - } - - function doHTTPRequest(Suave.HttpRequest memory request) internal view returns (bytes memory) { - bytes memory data = forgeIt("0x0000000000000000000000000000000043200002", abi.encode(request)); - - return abi.decode(data, (bytes)); - } - - function ethcall(address contractAddr, bytes memory input1) internal view returns (bytes memory) { - bytes memory data = forgeIt("0x0000000000000000000000000000000042100003", abi.encode(contractAddr, input1)); - - return abi.decode(data, (bytes)); - } - - function extractHint(bytes memory bundleData) internal view returns (bytes memory) { - bytes memory data = forgeIt("0x0000000000000000000000000000000042100037", abi.encode(bundleData)); - - return data; - } - - function fetchDataRecords(uint64 cond, string memory namespace) internal view returns (Suave.DataRecord[] memory) { - bytes memory data = forgeIt("0x0000000000000000000000000000000042030001", abi.encode(cond, namespace)); - - return abi.decode(data, (Suave.DataRecord[])); - } - - function fillMevShareBundle(Suave.DataId dataId) internal view returns (bytes memory) { - bytes memory data = forgeIt("0x0000000000000000000000000000000043200001", abi.encode(dataId)); - - return data; - } - - function newBuilder() internal view returns (string memory) { - bytes memory data = forgeIt("0x0000000000000000000000000000000053200001", abi.encode()); - - return abi.decode(data, (string)); - } - - function newDataRecord( - uint64 decryptionCondition, - address[] memory allowedPeekers, - address[] memory allowedStores, - string memory dataType - ) internal view returns (Suave.DataRecord memory) { - bytes memory data = forgeIt( - "0x0000000000000000000000000000000042030000", - abi.encode(decryptionCondition, allowedPeekers, allowedStores, dataType) - ); - - return abi.decode(data, (Suave.DataRecord)); - } - - function signEthTransaction(bytes memory txn, string memory chainId, string memory signingKey) - internal - view - returns (bytes memory) - { - bytes memory data = forgeIt("0x0000000000000000000000000000000040100001", abi.encode(txn, chainId, signingKey)); - - return abi.decode(data, (bytes)); - } - - function signMessage(bytes memory digest, string memory signingKey) internal view returns (bytes memory) { - bytes memory data = forgeIt("0x0000000000000000000000000000000040100003", abi.encode(digest, signingKey)); - - return abi.decode(data, (bytes)); - } - - function simulateBundle(bytes memory bundleData) internal view returns (uint64) { - bytes memory data = forgeIt("0x0000000000000000000000000000000042100000", abi.encode(bundleData)); - - return abi.decode(data, (uint64)); - } - - function simulateTransaction(string memory session, bytes memory txn) - internal - view - returns (Suave.SimulateTransactionResult memory) - { - bytes memory data = forgeIt("0x0000000000000000000000000000000053200002", abi.encode(session, txn)); - - return abi.decode(data, (Suave.SimulateTransactionResult)); - } - - function submitBundleJsonRPC(string memory url, string memory method, bytes memory params) - internal - view - returns (bytes memory) - { - bytes memory data = forgeIt("0x0000000000000000000000000000000043000001", abi.encode(url, method, params)); - - return data; - } - - function submitEthBlockToRelay(string memory relayUrl, bytes memory builderBid) - internal - view - returns (bytes memory) - { - bytes memory data = forgeIt("0x0000000000000000000000000000000042100002", abi.encode(relayUrl, builderBid)); - - return data; - } -} diff --git a/test/Forge.t.sol b/test/Forge.t.sol new file mode 100644 index 0000000..b0840db --- /dev/null +++ b/test/Forge.t.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: Unlicense +pragma solidity ^0.8.13; + +import "forge-std/Test.sol"; +import "src/Test.sol"; +import "src/suavelib/Suave.sol"; + +contract TestForge is Test, SuaveEnabled { + address[] public addressList = [0xC8df3686b4Afb2BB53e60EAe97EF043FE03Fb829]; + + function testConfidentialStore() public { + Suave.DataRecord memory record = Suave.newDataRecord(0, addressList, addressList, "namespace"); + + bytes memory value = abi.encode("suave works with forge!"); + Suave.confidentialStore(record.id, "key1", value); + + bytes memory found = Suave.confidentialRetrieve(record.id, "key1"); + assertEq(keccak256(found), keccak256(value)); + } +} diff --git a/tools/forge-gen/README.md b/tools/forge-gen/README.md new file mode 100644 index 0000000..3e8f186 --- /dev/null +++ b/tools/forge-gen/README.md @@ -0,0 +1,13 @@ +# Forge-gen command + +In the `forge` integration, a `forge/Connector.sol` contract is deployed for each of the `Suave` precompiles. The contract uses the fallback function to make an `vm.ffi` call to the `suave forge` command to peform the logic request. + +The `forge-gen` command creates the `forge/Registry.sol` contract which deploys the `Connector.sol` contract in all the precompile addresses using the `vm.etch` function. + +## Usage + +```bash +$ go run tools/forge-gen/main.go --apply +``` + +Use the `apply` flag to write the contract. Otherwise, it prints the contract on the standard output. diff --git a/tools/forge-gen/go.mod b/tools/forge-gen/go.mod new file mode 100644 index 0000000..8250b74 --- /dev/null +++ b/tools/forge-gen/go.mod @@ -0,0 +1,3 @@ +module github.com/flashbots/suave-std/tools/forge-gen + +go 1.21.0 diff --git a/tools/forge-gen/main.go b/tools/forge-gen/main.go new file mode 100644 index 0000000..80fefff --- /dev/null +++ b/tools/forge-gen/main.go @@ -0,0 +1,168 @@ +package main + +import ( + "bytes" + "encoding/json" + "flag" + "fmt" + "html/template" + "os" + "os/exec" + "regexp" + "strings" +) + +var applyFlag bool + +func main() { + flag.BoolVar(&applyFlag, "apply", false, "write to file") + flag.Parse() + + bytecode, err := getForgeConnectorBytecode() + if err != nil { + fmt.Printf("failed to get forge wrapper bytecode: %v\n", err) + os.Exit(1) + } + + precompileNames, err := getPrecompileNames() + if err != nil { + fmt.Printf("failed to get precompile names: %v\n", err) + os.Exit(1) + } + + if err := applyTemplate(bytecode, precompileNames); err != nil { + fmt.Printf("failed to apply template: %v\n", err) + os.Exit(1) + } +} + +var templateFile = `// SPDX-License-Identifier: UNLICENSED +// DO NOT edit this file. Code generated by forge-gen. +pragma solidity ^0.8.8; + +import "../suavelib/Suave.sol"; + +interface registryVM { + function etch(address, bytes calldata) external; +} + +library Registry { + registryVM constant vm = registryVM(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); + + function enableLib(address addr) public { + // code for Wrapper + bytes memory code = + hex"{{.Bytecode}}"; + vm.etch(addr, code); + } + + function enable() public { + {{range .PrecompileNames}} + enableLib(Suave.{{.}}); + {{- end}} + } +}` + +func applyTemplate(bytecode string, precompileNames []string) error { + t, err := template.New("template").Parse(templateFile) + if err != nil { + return err + } + + input := map[string]interface{}{ + "Bytecode": bytecode, + "PrecompileNames": precompileNames, + } + + var outputRaw bytes.Buffer + if err = t.Execute(&outputRaw, input); err != nil { + return err + } + + str := outputRaw.String() + if str, err = formatSolidity(str); err != nil { + return err + } + + if applyFlag { + if err := os.WriteFile("./src/forge/Registry.sol", []byte(str), 0644); err != nil { + return err + } + } else { + fmt.Println(str) + } + return nil +} + +func getForgeConnectorBytecode() (string, error) { + abiContent, err := os.ReadFile("./out/Connector.sol/Connector.json") + if err != nil { + return "", err + } + + var abiArtifact struct { + DeployedBytecode struct { + Object string + } + } + if err := json.Unmarshal(abiContent, &abiArtifact); err != nil { + return "", err + } + + bytecode := abiArtifact.DeployedBytecode.Object[2:] + return bytecode, nil +} + +func getPrecompileNames() ([]string, error) { + content, err := os.ReadFile("./src/suavelib/Suave.sol") + if err != nil { + return nil, err + } + + addrRegexp := regexp.MustCompile(`constant\s+([A-Za-z_]\w*)\s+=`) + + matches := addrRegexp.FindAllStringSubmatch(string(content), -1) + + names := []string{} + for _, match := range matches { + if len(match) > 1 { + name := strings.TrimSpace(match[1]) + if name == "ANYALLOWED" { + continue + } + names = append(names, name) + } + } + + return names, nil +} + +func formatSolidity(code string) (string, error) { + // Check if "forge" command is available in PATH + _, err := exec.LookPath("forge") + if err != nil { + return "", fmt.Errorf("forge command not found in PATH: %v", err) + } + + // Command and arguments for forge fmt + command := "forge" + args := []string{"fmt", "--raw", "-"} + + // Create a command to run the forge fmt command + cmd := exec.Command(command, args...) + + // Set up input from stdin + cmd.Stdin = bytes.NewBufferString(code) + + // Set up output buffer + var outBuf, errBuf bytes.Buffer + cmd.Stdout = &outBuf + cmd.Stderr = &errBuf + + // Run the command + if err = cmd.Run(); err != nil { + return "", fmt.Errorf("error running command: %v", err) + } + + return outBuf.String(), nil +} From 248545734c6204a9368a41e662871cbefa86db2a Mon Sep 17 00:00:00 2001 From: Ferran Borreguero Date: Fri, 5 Jan 2024 21:25:50 +0100 Subject: [PATCH 2/9] RLP encode legacy transactions without to address (#9) --- src/Transactions.sol | 15 +++++++-- test/{Transactions.sol => Transactions.t.sol} | 31 ++++++++++++++++--- 2 files changed, 39 insertions(+), 7 deletions(-) rename test/{Transactions.sol => Transactions.t.sol} (50%) diff --git a/src/Transactions.sol b/src/Transactions.sol index 3b66ffb..0605697 100644 --- a/src/Transactions.sol +++ b/src/Transactions.sol @@ -28,7 +28,12 @@ library Transactions { items[0] = RLPWriter.writeUint(txStruct.nonce); items[1] = RLPWriter.writeUint(txStruct.gasPrice); items[2] = RLPWriter.writeUint(txStruct.gas); - items[3] = RLPWriter.writeAddress(txStruct.to); + + if (txStruct.to == address(0)) { + items[3] = RLPWriter.writeBytes(bytes("")); + } else { + items[3] = RLPWriter.writeAddress(txStruct.to); + } items[4] = RLPWriter.writeUint(txStruct.value); items[5] = RLPWriter.writeBytes(txStruct.data); items[6] = RLPWriter.writeBytes(txStruct.v); @@ -47,7 +52,13 @@ library Transactions { txStruct.nonce = uint64(ls[0].toUint()); txStruct.gasPrice = uint64(ls[1].toUint()); txStruct.gas = uint64(ls[2].toUint()); - txStruct.to = ls[3].toAddress(); + + if (ls[3].toRlpBytes().length == 1) { + txStruct.to = address(0); + } else { + txStruct.to = ls[3].toAddress(); + } + txStruct.value = uint64(ls[4].toUint()); txStruct.data = ls[5].toBytes(); txStruct.v = ls[6].toBytes(); diff --git a/test/Transactions.sol b/test/Transactions.t.sol similarity index 50% rename from test/Transactions.sol rename to test/Transactions.t.sol index 64b8dbd..9746cb7 100644 --- a/test/Transactions.sol +++ b/test/Transactions.t.sol @@ -8,7 +8,7 @@ contract TestTransactions is Test { using Transactions for *; function testLegacyTransactionRLPEncoding() public { - Transactions.Legacy memory legacyTxn0 = Transactions.Legacy({ + Transactions.Legacy memory txnWithToAddress = Transactions.Legacy({ to: address(0x095E7BAea6a6c7c4c2DfeB977eFac326aF552d87), gas: 50000, gasPrice: 10, @@ -21,17 +21,38 @@ contract TestTransactions is Test { s: abi.encodePacked(hex"8a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b1") }); - bytes memory rlp = Transactions.encodeRLP(legacyTxn0); - bytes memory expected = abi.encodePacked( hex"f85f800a82c35094095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba09bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094fa08a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b1" ); - assertEq0(rlp, expected); + _testLegacyTransaction(txnWithToAddress, expected); + + Transactions.Legacy memory txnWithoutToAddress = Transactions.Legacy({ + to: address(0), + gas: 50000, + gasPrice: 10, + value: 10, + nonce: 1, + data: abi.encodePacked(hex"02"), + chainId: 0, + v: abi.encodePacked(hex"1b"), + r: abi.encodePacked(hex"754a33a9c37cfcf61cd61939fd93f5fe194b7d1ee6ef07490e8c880f3bd0d87d"), + s: abi.encodePacked(hex"715bd50fa2c24e2ce0ea595025a44a39ac238558882f9f07dd885ddc51839419") + }); + + expected = abi.encodePacked( + hex"f84b010a82c350800a021ba0754a33a9c37cfcf61cd61939fd93f5fe194b7d1ee6ef07490e8c880f3bd0d87da0715bd50fa2c24e2ce0ea595025a44a39ac238558882f9f07dd885ddc51839419" + ); + _testLegacyTransaction(txnWithoutToAddress, expected); + } + + function _testLegacyTransaction(Transactions.Legacy memory legacyTxn, bytes memory expectedRlp) public { + bytes memory rlp = Transactions.encodeRLP(legacyTxn); + assertEq0(rlp, expectedRlp); Transactions.Legacy memory legacyTxn1 = Transactions.decodeRLP(rlp); // re-encode to validate that the decoding was correct bytes memory rlp1 = Transactions.encodeRLP(legacyTxn1); - assertEq0(rlp1, expected); + assertEq0(rlp1, expectedRlp); } } From 4a04f0aed9f9c178549d9bfb922d8ab08f9667c4 Mon Sep 17 00:00:00 2001 From: sukoneck <19413126+sukoneck@users.noreply.github.com> Date: Sat, 6 Jan 2024 03:39:12 -0700 Subject: [PATCH 3/9] specify dispatch trigger type and add logs to suavelib-sync (#16) --- .github/workflows/suave-lib-sync.yml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/.github/workflows/suave-lib-sync.yml b/.github/workflows/suave-lib-sync.yml index cc581d5..b290a4d 100644 --- a/.github/workflows/suave-lib-sync.yml +++ b/.github/workflows/suave-lib-sync.yml @@ -1,6 +1,9 @@ name: SuaveLib sync -on: [repository_dispatch, workflow_dispatch] +on: + workflow_dispatch: + repository_dispatch: + types: [suavelib-sync] permissions: pull-requests: write @@ -12,6 +15,14 @@ jobs: build: runs-on: ubuntu-latest steps: + - name: Log Dispatch Information + if: ${{ github.event_name == 'repository_dispatch' }} + run: | + echo "this run was triggered by dispatch from repo: flashbots/suave-geth" + echo "ref: ${{ github.event.client_payload.ref }}" + echo "sha: ${{ github.event.client_payload.sha }}" + echo "run: ${{ github.event.client_payload.run }}" + - name: Checkout uses: actions/checkout@v4 with: From 138ee0fe47d759c8262d33d8f0308c1e9c5bd8fd Mon Sep 17 00:00:00 2001 From: Ferran Borreguero Date: Sat, 6 Jan 2024 11:45:03 +0100 Subject: [PATCH 4/9] Regenerate forge registry on suave lib sync (#19) --- .github/workflows/suave-lib-sync.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/.github/workflows/suave-lib-sync.yml b/.github/workflows/suave-lib-sync.yml index b290a4d..9a3d1e6 100644 --- a/.github/workflows/suave-lib-sync.yml +++ b/.github/workflows/suave-lib-sync.yml @@ -29,6 +29,11 @@ jobs: persist-credentials: false fetch-depth: 0 + - name: Set up Go + uses: actions/setup-go@v3 + with: + go-version: ^1.21 + - name: Checkout tools repo uses: actions/checkout@v4 with: @@ -50,6 +55,17 @@ jobs: git add ./src/suavelib/Suave.sol rm -rf suave-geth + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + with: + version: nightly + + - name: Regenerate Forge registry + run: | + forge build + go run ./tools/forge-gen/main.go --apply + git add ./src/forge/Registry.sol + - name: Create Pull Request uses: peter-evans/create-pull-request@v5 with: From 11e5d72238e1daf2b6465c55aa386ee8054180a3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 6 Jan 2024 11:51:15 +0100 Subject: [PATCH 5/9] Update Suave.sol library to https://github.com/flashbots/suave-geth/commit/77f5e8a6cb882ccbe98e9b0287bc45ca54b55c9a (#20) Co-authored-by: sukoneck --- src/forge/Registry.sol | 3 ++- src/suavelib/Suave.sol | 9 +++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/forge/Registry.sol b/src/forge/Registry.sol index 526181d..8d6bad3 100644 --- a/src/forge/Registry.sol +++ b/src/forge/Registry.sol @@ -1,4 +1,5 @@ // SPDX-License-Identifier: UNLICENSED +// DO NOT edit this file. Code generated by forge-gen. pragma solidity ^0.8.8; import "../suavelib/Suave.sol"; @@ -13,7 +14,7 @@ library Registry { function enableLib(address addr) public { // code for Wrapper bytes memory code = - hex"608060405234801561001057600080fd5b506004361061002b5760003560e01c8063671ff786146100a1575b6040516bffffffffffffffffffffffff193060601b1660208201526000906100959060340160408051808303601f19018152602036601f8101829004820285018201909352828452909291600091819084018382808284376000920191909152506100ca92505050565b90508081518060208301f35b6100b46100af36600461048c565b610259565b6040516100c1919061055c565b60405180910390f35b606060006100d784610259565b905060006100e484610259565b60408051600480825260a0820190925291925060009190816020015b606081526020019060019003908161010057905050905060405180604001604052806005815260200164737561766560d81b8152508160008151811061014857610148610576565b602002602001018190525060405180604001604052806005815260200164666f72676560d81b8152508160018151811061018457610184610576565b602002602001018190525082816002815181106101a3576101a3610576565b602002602001018190525081816003815181106101c2576101c2610576565b6020908102919091010152604051638916046760e01b8152600090737109709ecfa91a80626ff3989d68f67f5b1dd12d9063891604679061020790859060040161058c565b600060405180830381865afa158015610224573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261024c91908101906105ee565b9450505050505b92915050565b606060008251600261026b919061067b565b67ffffffffffffffff8111156102835761028361041d565b6040519080825280601f01601f1916602001820160405280156102ad576020820181803683370190505b5060408051808201909152601081526f181899199a1a9b1b9c1cb0b131b232b360811b602082015290915060005b84518110156103f3578182518683815181106102f9576102f9610576565b016020015161030b919060f81c6106a8565b8151811061031b5761031b610576565b01602001516001600160f81b0319168361033683600261067b565b8151811061034657610346610576565b60200101906001600160f81b031916908160001a90535081825186838151811061037257610372610576565b0160200151610384919060f81c6106bc565b8151811061039457610394610576565b01602001516001600160f81b031916836103af83600261067b565b6103ba9060016106d0565b815181106103ca576103ca610576565b60200101906001600160f81b031916908160001a905350806103eb816106e3565b9150506102db565b508160405160200161040591906106fc565b60405160208183030381529060405292505050919050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff8111828210171561045c5761045c61041d565b604052919050565b600067ffffffffffffffff82111561047e5761047e61041d565b50601f01601f191660200190565b60006020828403121561049e57600080fd5b813567ffffffffffffffff8111156104b557600080fd5b8201601f810184136104c657600080fd5b80356104d96104d482610464565b610433565b8181528560208385010111156104ee57600080fd5b81602084016020830137600091810160200191909152949350505050565b60005b8381101561052757818101518382015260200161050f565b50506000910152565b6000815180845261054881602086016020860161050c565b601f01601f19169290920160200192915050565b60208152600061056f6020830184610530565b9392505050565b634e487b7160e01b600052603260045260246000fd5b6000602080830181845280855180835260408601915060408160051b870101925083870160005b828110156105e157603f198886030184526105cf858351610530565b945092850192908501906001016105b3565b5092979650505050505050565b60006020828403121561060057600080fd5b815167ffffffffffffffff81111561061757600080fd5b8201601f8101841361062857600080fd5b80516106366104d482610464565b81815285602083850101111561064b57600080fd5b61065c82602083016020860161050c565b95945050505050565b634e487b7160e01b600052601160045260246000fd5b808202811582820484141761025357610253610665565b634e487b7160e01b600052601260045260246000fd5b6000826106b7576106b7610692565b500490565b6000826106cb576106cb610692565b500690565b8082018082111561025357610253610665565b6000600182016106f5576106f5610665565b5060010190565b61060f60f31b81526000825161071981600285016020870161050c565b919091016002019291505056fea2646970667358221220e66d500bc9a9ca9c0748086adfc51de57c85b7c9f66cc760e823099f0439820b64736f6c63430008130033"; + hex"608060405234801561001057600080fd5b506004361061002b5760003560e01c8063671ff786146100a1575b6040516bffffffffffffffffffffffff193060601b1660208201526000906100959060340160408051808303601f19018152602036601f8101829004820285018201909352828452909291600091819084018382808284376000920191909152506100ca92505050565b90508081518060208301f35b6100b46100af366004610482565b610259565b6040516100c19190610552565b60405180910390f35b606060006100d784610259565b905060006100e484610259565b60408051600480825260a0820190925291925060009190816020015b606081526020019060019003908161010057905050905060405180604001604052806005815260200164737561766560d81b815250816000815181106101485761014861056c565b602002602001018190525060405180604001604052806005815260200164666f72676560d81b815250816001815181106101845761018461056c565b602002602001018190525082816002815181106101a3576101a361056c565b602002602001018190525081816003815181106101c2576101c261056c565b6020908102919091010152604051638916046760e01b8152600090737109709ecfa91a80626ff3989d68f67f5b1dd12d90638916046790610207908590600401610582565b600060405180830381865afa158015610224573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261024c91908101906105e6565b9450505050505b92915050565b606060008251600261026b9190610673565b67ffffffffffffffff81111561028357610283610413565b6040519080825280601f01601f1916602001820160405280156102ad576020820181803683370190505b5060408051808201909152601081526f181899199a1a9b1b9c1cb0b131b232b360811b602082015290915060005b84518110156103e9578182518683815181106102f9576102f961056c565b016020015161030b919060f81c6106a0565b8151811061031b5761031b61056c565b01602001516001600160f81b03191683610336836002610673565b815181106103465761034661056c565b60200101906001600160f81b031916908160001a9053508182518683815181106103725761037261056c565b0160200151610384919060f81c6106b4565b815181106103945761039461056c565b01602001516001600160f81b031916836103af836002610673565b6103ba9060016106c8565b815181106103ca576103ca61056c565b60200101906001600160f81b031916908160001a9053506001016102db565b50816040516020016103fb91906106db565b60405160208183030381529060405292505050919050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff8111828210171561045257610452610413565b604052919050565b600067ffffffffffffffff82111561047457610474610413565b50601f01601f191660200190565b60006020828403121561049457600080fd5b813567ffffffffffffffff8111156104ab57600080fd5b8201601f810184136104bc57600080fd5b80356104cf6104ca8261045a565b610429565b8181528560208385010111156104e457600080fd5b81602084016020830137600091810160200191909152949350505050565b60005b8381101561051d578181015183820152602001610505565b50506000910152565b6000815180845261053e816020860160208601610502565b601f01601f19169290920160200192915050565b6020815260006105656020830184610526565b9392505050565b634e487b7160e01b600052603260045260246000fd5b600060208083016020845280855180835260408601915060408160051b87010192506020870160005b828110156105d957603f198886030184526105c7858351610526565b945092850192908501906001016105ab565b5092979650505050505050565b6000602082840312156105f857600080fd5b815167ffffffffffffffff81111561060f57600080fd5b8201601f8101841361062057600080fd5b805161062e6104ca8261045a565b81815285602083850101111561064357600080fd5b610654826020830160208601610502565b95945050505050565b634e487b7160e01b600052601160045260246000fd5b80820281158282048414176102535761025361065d565b634e487b7160e01b600052601260045260246000fd5b6000826106af576106af61068a565b500490565b6000826106c3576106c361068a565b500690565b808201808211156102535761025361065d565b61060f60f31b8152600082516106f8816002850160208701610502565b919091016002019291505056fea264697066735822122059ae496bcbd2ced9fbec0a08e12c546547a6f5be88964669c6db37d39d43d57e64736f6c63430008170033"; vm.etch(addr, code); } diff --git a/src/suavelib/Suave.sol b/src/suavelib/Suave.sol index 217b0e6..bcaf758 100644 --- a/src/suavelib/Suave.sol +++ b/src/suavelib/Suave.sol @@ -33,6 +33,7 @@ library Suave { string method; string[] headers; bytes body; + bool withFlashbotsSignature; } struct SimulateTransactionResult { @@ -138,8 +139,8 @@ library Suave { return data; } - function confidentialStore(DataId dataId, string memory key, bytes memory data1) internal view { - (bool success, bytes memory data) = CONFIDENTIAL_STORE.staticcall(abi.encode(dataId, key, data1)); + function confidentialStore(DataId dataId, string memory key, bytes memory value) internal view { + (bool success, bytes memory data) = CONFIDENTIAL_STORE.staticcall(abi.encode(dataId, key, value)); if (!success) { revert PeekerReverted(CONFIDENTIAL_STORE, data); } @@ -248,12 +249,12 @@ library Suave { return abi.decode(data, (uint64)); } - function simulateTransaction(string memory session, bytes memory txn) + function simulateTransaction(string memory sessionid, bytes memory txn) internal view returns (SimulateTransactionResult memory) { - (bool success, bytes memory data) = SIMULATE_TRANSACTION.staticcall(abi.encode(session, txn)); + (bool success, bytes memory data) = SIMULATE_TRANSACTION.staticcall(abi.encode(sessionid, txn)); if (!success) { revert PeekerReverted(SIMULATE_TRANSACTION, data); } From 414a6bd48c03cb420d683d13a6bc9e4829c1e17e Mon Sep 17 00:00:00 2001 From: sukoneck <19413126+sukoneck@users.noreply.github.com> Date: Mon, 8 Jan 2024 00:39:36 -0700 Subject: [PATCH 6/9] add codeowners (#17) * add codeowners * fix * fix * Update .github/CODEOWNERS Co-authored-by: Ferran Borreguero --------- Co-authored-by: Ferran Borreguero --- .github/CODEOWNERS | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..3d724cb --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,4 @@ +# These owners will be the default owners for everything in +# the repo. Unless a later match takes precedence, +# they will be requested for review when someone opens a pull request. +* @ferranbt @metachris @zeroXbrock From fd8574d61377c66eef30ae2f754e3beffe4334de Mon Sep 17 00:00:00 2001 From: Ferran Borreguero Date: Tue, 9 Jan 2024 11:33:02 +0100 Subject: [PATCH 7/9] Use suave-toolchain action (#24) * Use suave-toolchain action * Do not checkout suave-geth * Use the PATH binary * Modify binary name * test * Try again * Update registry * Remove ci step --- .github/workflows/ci.yml | 20 +++----------------- src/forge/Connector.sol | 2 +- src/forge/Registry.sol | 2 +- 3 files changed, 5 insertions(+), 19 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 88886fb..c6934b6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,26 +14,12 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Checkout suave-geth repo - uses: actions/checkout@v4 - with: - repository: flashbots/suave-geth - path: suave-geth - persist-credentials: false - fetch-depth: 0 - - - name: Build suave - run: | - cd suave-geth - make suave - - - name: Include the binary on $PATH - run: | - echo "$(pwd)/suave-geth/build/bin" >> $GITHUB_PATH + - name: Install suave-geth + uses: flashbots/suave-toolchain@v0.1 - name: Run suave run: | - ./suave-geth/build/bin/suave --suave.dev & + suave-geth --suave.dev & - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 diff --git a/src/forge/Connector.sol b/src/forge/Connector.sol index 2e2f4fd..b2be8d5 100644 --- a/src/forge/Connector.sol +++ b/src/forge/Connector.sol @@ -13,7 +13,7 @@ contract Connector { string memory dataHex = iToHex(data); string[] memory inputs = new string[](4); - inputs[0] = "suave"; + inputs[0] = "suave-geth"; inputs[1] = "forge"; inputs[2] = addrHex; inputs[3] = dataHex; diff --git a/src/forge/Registry.sol b/src/forge/Registry.sol index 8d6bad3..7758c49 100644 --- a/src/forge/Registry.sol +++ b/src/forge/Registry.sol @@ -14,7 +14,7 @@ library Registry { function enableLib(address addr) public { // code for Wrapper bytes memory code = - hex"608060405234801561001057600080fd5b506004361061002b5760003560e01c8063671ff786146100a1575b6040516bffffffffffffffffffffffff193060601b1660208201526000906100959060340160408051808303601f19018152602036601f8101829004820285018201909352828452909291600091819084018382808284376000920191909152506100ca92505050565b90508081518060208301f35b6100b46100af366004610482565b610259565b6040516100c19190610552565b60405180910390f35b606060006100d784610259565b905060006100e484610259565b60408051600480825260a0820190925291925060009190816020015b606081526020019060019003908161010057905050905060405180604001604052806005815260200164737561766560d81b815250816000815181106101485761014861056c565b602002602001018190525060405180604001604052806005815260200164666f72676560d81b815250816001815181106101845761018461056c565b602002602001018190525082816002815181106101a3576101a361056c565b602002602001018190525081816003815181106101c2576101c261056c565b6020908102919091010152604051638916046760e01b8152600090737109709ecfa91a80626ff3989d68f67f5b1dd12d90638916046790610207908590600401610582565b600060405180830381865afa158015610224573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261024c91908101906105e6565b9450505050505b92915050565b606060008251600261026b9190610673565b67ffffffffffffffff81111561028357610283610413565b6040519080825280601f01601f1916602001820160405280156102ad576020820181803683370190505b5060408051808201909152601081526f181899199a1a9b1b9c1cb0b131b232b360811b602082015290915060005b84518110156103e9578182518683815181106102f9576102f961056c565b016020015161030b919060f81c6106a0565b8151811061031b5761031b61056c565b01602001516001600160f81b03191683610336836002610673565b815181106103465761034661056c565b60200101906001600160f81b031916908160001a9053508182518683815181106103725761037261056c565b0160200151610384919060f81c6106b4565b815181106103945761039461056c565b01602001516001600160f81b031916836103af836002610673565b6103ba9060016106c8565b815181106103ca576103ca61056c565b60200101906001600160f81b031916908160001a9053506001016102db565b50816040516020016103fb91906106db565b60405160208183030381529060405292505050919050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff8111828210171561045257610452610413565b604052919050565b600067ffffffffffffffff82111561047457610474610413565b50601f01601f191660200190565b60006020828403121561049457600080fd5b813567ffffffffffffffff8111156104ab57600080fd5b8201601f810184136104bc57600080fd5b80356104cf6104ca8261045a565b610429565b8181528560208385010111156104e457600080fd5b81602084016020830137600091810160200191909152949350505050565b60005b8381101561051d578181015183820152602001610505565b50506000910152565b6000815180845261053e816020860160208601610502565b601f01601f19169290920160200192915050565b6020815260006105656020830184610526565b9392505050565b634e487b7160e01b600052603260045260246000fd5b600060208083016020845280855180835260408601915060408160051b87010192506020870160005b828110156105d957603f198886030184526105c7858351610526565b945092850192908501906001016105ab565b5092979650505050505050565b6000602082840312156105f857600080fd5b815167ffffffffffffffff81111561060f57600080fd5b8201601f8101841361062057600080fd5b805161062e6104ca8261045a565b81815285602083850101111561064357600080fd5b610654826020830160208601610502565b95945050505050565b634e487b7160e01b600052601160045260246000fd5b80820281158282048414176102535761025361065d565b634e487b7160e01b600052601260045260246000fd5b6000826106af576106af61068a565b500490565b6000826106c3576106c361068a565b500690565b808201808211156102535761025361065d565b61060f60f31b8152600082516106f8816002850160208701610502565b919091016002019291505056fea264697066735822122059ae496bcbd2ced9fbec0a08e12c546547a6f5be88964669c6db37d39d43d57e64736f6c63430008170033"; + hex"608060405234801561001057600080fd5b506004361061002b5760003560e01c8063671ff786146100a1575b6040516bffffffffffffffffffffffff193060601b1660208201526000906100959060340160408051808303601f19018152602036601f8101829004820285018201909352828452909291600091819084018382808284376000920191909152506100ca92505050565b90508081518060208301f35b6100b46100af366004610491565b61025e565b6040516100c19190610561565b60405180910390f35b606060006100d78461025e565b905060006100e48461025e565b60408051600480825260a0820190925291925060009190816020015b60608152602001906001900390816101005790505090506040518060400160405280600a8152602001690e6eac2ecca5acecae8d60b31b8152508160008151811061014d5761014d61057b565b602002602001018190525060405180604001604052806005815260200164666f72676560d81b815250816001815181106101895761018961057b565b602002602001018190525082816002815181106101a8576101a861057b565b602002602001018190525081816003815181106101c7576101c761057b565b6020908102919091010152604051638916046760e01b8152600090737109709ecfa91a80626ff3989d68f67f5b1dd12d9063891604679061020c908590600401610591565b600060405180830381865afa158015610229573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261025191908101906105f3565b9450505050505b92915050565b60606000825160026102709190610680565b67ffffffffffffffff81111561028857610288610422565b6040519080825280601f01601f1916602001820160405280156102b2576020820181803683370190505b5060408051808201909152601081526f181899199a1a9b1b9c1cb0b131b232b360811b602082015290915060005b84518110156103f8578182518683815181106102fe576102fe61057b565b0160200151610310919060f81c6106ad565b815181106103205761032061057b565b01602001516001600160f81b0319168361033b836002610680565b8151811061034b5761034b61057b565b60200101906001600160f81b031916908160001a9053508182518683815181106103775761037761057b565b0160200151610389919060f81c6106c1565b815181106103995761039961057b565b01602001516001600160f81b031916836103b4836002610680565b6103bf9060016106d5565b815181106103cf576103cf61057b565b60200101906001600160f81b031916908160001a905350806103f0816106e8565b9150506102e0565b508160405160200161040a9190610701565b60405160208183030381529060405292505050919050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff8111828210171561046157610461610422565b604052919050565b600067ffffffffffffffff82111561048357610483610422565b50601f01601f191660200190565b6000602082840312156104a357600080fd5b813567ffffffffffffffff8111156104ba57600080fd5b8201601f810184136104cb57600080fd5b80356104de6104d982610469565b610438565b8181528560208385010111156104f357600080fd5b81602084016020830137600091810160200191909152949350505050565b60005b8381101561052c578181015183820152602001610514565b50506000910152565b6000815180845261054d816020860160208601610511565b601f01601f19169290920160200192915050565b6020815260006105746020830184610535565b9392505050565b634e487b7160e01b600052603260045260246000fd5b6000602080830181845280855180835260408601915060408160051b870101925083870160005b828110156105e657603f198886030184526105d4858351610535565b945092850192908501906001016105b8565b5092979650505050505050565b60006020828403121561060557600080fd5b815167ffffffffffffffff81111561061c57600080fd5b8201601f8101841361062d57600080fd5b805161063b6104d982610469565b81815285602083850101111561065057600080fd5b610661826020830160208601610511565b95945050505050565b634e487b7160e01b600052601160045260246000fd5b80820281158282048414176102585761025861066a565b634e487b7160e01b600052601260045260246000fd5b6000826106bc576106bc610697565b500490565b6000826106d0576106d0610697565b500690565b808201808211156102585761025861066a565b6000600182016106fa576106fa61066a565b5060010190565b61060f60f31b81526000825161071e816002850160208701610511565b919091016002019291505056fea2646970667358221220918efca7b62d20c0fa19067dd7a091ef7cf76b94f70985c100a506284cf9d54664736f6c63430008130033"; vm.etch(addr, code); } From d82abfe439b27a9bfcd2366a92b3ce616197a63b Mon Sep 17 00:00:00 2001 From: Ferran Borreguero Date: Tue, 9 Jan 2024 15:44:32 +0100 Subject: [PATCH 8/9] Create deterministic builds with `forge-gen` (#25) --- .github/workflows/check-gen-code.yml | 34 +++++++++++++++++++++++ .github/workflows/ci.yml | 2 +- src/forge/Registry.sol | 2 +- tools/forge-gen/foundry.toml | 6 ++++ tools/forge-gen/main.go | 41 ++++++++++++++++++++-------- 5 files changed, 72 insertions(+), 13 deletions(-) create mode 100644 .github/workflows/check-gen-code.yml create mode 100644 tools/forge-gen/foundry.toml diff --git a/.github/workflows/check-gen-code.yml b/.github/workflows/check-gen-code.yml new file mode 100644 index 0000000..2cf66b9 --- /dev/null +++ b/.github/workflows/check-gen-code.yml @@ -0,0 +1,34 @@ +name: Check forge-gen +on: + push: + branches: + - main + pull_request: + +env: + FOUNDRY_PROFILE: ci + +jobs: + check-forge-gen: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + with: + version: nightly + + - name: Install deps + run: forge install + + - name: Generate forge-gen + run: go run ./tools/forge-gen/main.go --apply + + - name: Compare the expected and actual src/forge/ directories + run: | + if [ "$(git diff --ignore-space-at-eol src/forge/ | wc -l)" -gt "0" ]; then + echo "Detected uncommitted changes after build. See status below:" + git diff + exit 1 + fi diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c6934b6..19efddc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,7 +2,7 @@ name: CI on: push: branches: - - master + - main pull_request: env: diff --git a/src/forge/Registry.sol b/src/forge/Registry.sol index 7758c49..995e42d 100644 --- a/src/forge/Registry.sol +++ b/src/forge/Registry.sol @@ -14,7 +14,7 @@ library Registry { function enableLib(address addr) public { // code for Wrapper bytes memory code = - hex"608060405234801561001057600080fd5b506004361061002b5760003560e01c8063671ff786146100a1575b6040516bffffffffffffffffffffffff193060601b1660208201526000906100959060340160408051808303601f19018152602036601f8101829004820285018201909352828452909291600091819084018382808284376000920191909152506100ca92505050565b90508081518060208301f35b6100b46100af366004610491565b61025e565b6040516100c19190610561565b60405180910390f35b606060006100d78461025e565b905060006100e48461025e565b60408051600480825260a0820190925291925060009190816020015b60608152602001906001900390816101005790505090506040518060400160405280600a8152602001690e6eac2ecca5acecae8d60b31b8152508160008151811061014d5761014d61057b565b602002602001018190525060405180604001604052806005815260200164666f72676560d81b815250816001815181106101895761018961057b565b602002602001018190525082816002815181106101a8576101a861057b565b602002602001018190525081816003815181106101c7576101c761057b565b6020908102919091010152604051638916046760e01b8152600090737109709ecfa91a80626ff3989d68f67f5b1dd12d9063891604679061020c908590600401610591565b600060405180830381865afa158015610229573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261025191908101906105f3565b9450505050505b92915050565b60606000825160026102709190610680565b67ffffffffffffffff81111561028857610288610422565b6040519080825280601f01601f1916602001820160405280156102b2576020820181803683370190505b5060408051808201909152601081526f181899199a1a9b1b9c1cb0b131b232b360811b602082015290915060005b84518110156103f8578182518683815181106102fe576102fe61057b565b0160200151610310919060f81c6106ad565b815181106103205761032061057b565b01602001516001600160f81b0319168361033b836002610680565b8151811061034b5761034b61057b565b60200101906001600160f81b031916908160001a9053508182518683815181106103775761037761057b565b0160200151610389919060f81c6106c1565b815181106103995761039961057b565b01602001516001600160f81b031916836103b4836002610680565b6103bf9060016106d5565b815181106103cf576103cf61057b565b60200101906001600160f81b031916908160001a905350806103f0816106e8565b9150506102e0565b508160405160200161040a9190610701565b60405160208183030381529060405292505050919050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff8111828210171561046157610461610422565b604052919050565b600067ffffffffffffffff82111561048357610483610422565b50601f01601f191660200190565b6000602082840312156104a357600080fd5b813567ffffffffffffffff8111156104ba57600080fd5b8201601f810184136104cb57600080fd5b80356104de6104d982610469565b610438565b8181528560208385010111156104f357600080fd5b81602084016020830137600091810160200191909152949350505050565b60005b8381101561052c578181015183820152602001610514565b50506000910152565b6000815180845261054d816020860160208601610511565b601f01601f19169290920160200192915050565b6020815260006105746020830184610535565b9392505050565b634e487b7160e01b600052603260045260246000fd5b6000602080830181845280855180835260408601915060408160051b870101925083870160005b828110156105e657603f198886030184526105d4858351610535565b945092850192908501906001016105b8565b5092979650505050505050565b60006020828403121561060557600080fd5b815167ffffffffffffffff81111561061c57600080fd5b8201601f8101841361062d57600080fd5b805161063b6104d982610469565b81815285602083850101111561065057600080fd5b610661826020830160208601610511565b95945050505050565b634e487b7160e01b600052601160045260246000fd5b80820281158282048414176102585761025861066a565b634e487b7160e01b600052601260045260246000fd5b6000826106bc576106bc610697565b500490565b6000826106d0576106d0610697565b500690565b808201808211156102585761025861066a565b6000600182016106fa576106fa61066a565b5060010190565b61060f60f31b81526000825161071e816002850160208701610511565b919091016002019291505056fea2646970667358221220918efca7b62d20c0fa19067dd7a091ef7cf76b94f70985c100a506284cf9d54664736f6c63430008130033"; + hex"608060405234801561001057600080fd5b506004361061002b5760003560e01c8063671ff786146100a1575b6040516bffffffffffffffffffffffff193060601b1660208201526000906100959060340160408051808303601f19018152602036601f8101829004820285018201909352828452909291600091819084018382808284376000920191909152506100ca92505050565b90508081518060208301f35b6100b46100af366004610487565b61025e565b6040516100c19190610557565b60405180910390f35b606060006100d78461025e565b905060006100e48461025e565b60408051600480825260a0820190925291925060009190816020015b60608152602001906001900390816101005790505090506040518060400160405280600a8152602001690e6eac2ecca5acecae8d60b31b8152508160008151811061014d5761014d610571565b602002602001018190525060405180604001604052806005815260200164666f72676560d81b8152508160018151811061018957610189610571565b602002602001018190525082816002815181106101a8576101a8610571565b602002602001018190525081816003815181106101c7576101c7610571565b6020908102919091010152604051638916046760e01b8152600090737109709ecfa91a80626ff3989d68f67f5b1dd12d9063891604679061020c908590600401610587565b600060405180830381865afa158015610229573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261025191908101906105eb565b9450505050505b92915050565b60606000825160026102709190610678565b67ffffffffffffffff81111561028857610288610418565b6040519080825280601f01601f1916602001820160405280156102b2576020820181803683370190505b5060408051808201909152601081526f181899199a1a9b1b9c1cb0b131b232b360811b602082015290915060005b84518110156103ee578182518683815181106102fe576102fe610571565b0160200151610310919060f81c6106a5565b8151811061032057610320610571565b01602001516001600160f81b0319168361033b836002610678565b8151811061034b5761034b610571565b60200101906001600160f81b031916908160001a90535081825186838151811061037757610377610571565b0160200151610389919060f81c6106b9565b8151811061039957610399610571565b01602001516001600160f81b031916836103b4836002610678565b6103bf9060016106cd565b815181106103cf576103cf610571565b60200101906001600160f81b031916908160001a9053506001016102e0565b508160405160200161040091906106e0565b60405160208183030381529060405292505050919050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff8111828210171561045757610457610418565b604052919050565b600067ffffffffffffffff82111561047957610479610418565b50601f01601f191660200190565b60006020828403121561049957600080fd5b813567ffffffffffffffff8111156104b057600080fd5b8201601f810184136104c157600080fd5b80356104d46104cf8261045f565b61042e565b8181528560208385010111156104e957600080fd5b81602084016020830137600091810160200191909152949350505050565b60005b8381101561052257818101518382015260200161050a565b50506000910152565b60008151808452610543816020860160208601610507565b601f01601f19169290920160200192915050565b60208152600061056a602083018461052b565b9392505050565b634e487b7160e01b600052603260045260246000fd5b600060208083016020845280855180835260408601915060408160051b87010192506020870160005b828110156105de57603f198886030184526105cc85835161052b565b945092850192908501906001016105b0565b5092979650505050505050565b6000602082840312156105fd57600080fd5b815167ffffffffffffffff81111561061457600080fd5b8201601f8101841361062557600080fd5b80516106336104cf8261045f565b81815285602083850101111561064857600080fd5b610659826020830160208601610507565b95945050505050565b634e487b7160e01b600052601160045260246000fd5b808202811582820484141761025857610258610662565b634e487b7160e01b600052601260045260246000fd5b6000826106b4576106b461068f565b500490565b6000826106c8576106c861068f565b500690565b8082018082111561025857610258610662565b61060f60f31b8152600082516106fd816002850160208701610507565b919091016002019291505056fea164736f6c6343000817000a"; vm.etch(addr, code); } diff --git a/tools/forge-gen/foundry.toml b/tools/forge-gen/foundry.toml new file mode 100644 index 0000000..3967eda --- /dev/null +++ b/tools/forge-gen/foundry.toml @@ -0,0 +1,6 @@ +[profile.default] +solc_version = "0.8.23" +src = "../../src" +out = "./out" +libs = ["../../lib"] +bytecode_hash = "none" \ No newline at end of file diff --git a/tools/forge-gen/main.go b/tools/forge-gen/main.go index 80fefff..92bba9a 100644 --- a/tools/forge-gen/main.go +++ b/tools/forge-gen/main.go @@ -8,7 +8,9 @@ import ( "html/template" "os" "os/exec" + "path/filepath" "regexp" + "runtime" "strings" ) @@ -85,7 +87,7 @@ func applyTemplate(bytecode string, precompileNames []string) error { } if applyFlag { - if err := os.WriteFile("./src/forge/Registry.sol", []byte(str), 0644); err != nil { + if err := os.WriteFile(resolvePath("../../src/forge/Registry.sol"), []byte(str), 0644); err != nil { return err } } else { @@ -95,7 +97,12 @@ func applyTemplate(bytecode string, precompileNames []string) error { } func getForgeConnectorBytecode() (string, error) { - abiContent, err := os.ReadFile("./out/Connector.sol/Connector.json") + // compile the Connector contract with forge and the local configuration + if _, err := execForgeCommand([]string{"build", "--config-path", resolvePath("./foundry.toml")}, ""); err != nil { + return "", err + } + + abiContent, err := os.ReadFile(resolvePath("./out/Connector.sol/Connector.json")) if err != nil { return "", err } @@ -138,21 +145,22 @@ func getPrecompileNames() ([]string, error) { } func formatSolidity(code string) (string, error) { - // Check if "forge" command is available in PATH + return execForgeCommand([]string{"fmt", "--raw", "-"}, code) +} + +func execForgeCommand(args []string, stdin string) (string, error) { _, err := exec.LookPath("forge") if err != nil { return "", fmt.Errorf("forge command not found in PATH: %v", err) } - // Command and arguments for forge fmt - command := "forge" - args := []string{"fmt", "--raw", "-"} - - // Create a command to run the forge fmt command - cmd := exec.Command(command, args...) + // Create a command to run the forge command + cmd := exec.Command("forge", args...) // Set up input from stdin - cmd.Stdin = bytes.NewBufferString(code) + if stdin != "" { + cmd.Stdin = bytes.NewBufferString(stdin) + } // Set up output buffer var outBuf, errBuf bytes.Buffer @@ -160,9 +168,20 @@ func formatSolidity(code string) (string, error) { cmd.Stderr = &errBuf // Run the command - if err = cmd.Run(); err != nil { + if err := cmd.Run(); err != nil { return "", fmt.Errorf("error running command: %v", err) } return outBuf.String(), nil } + +func resolvePath(path string) string { + // Get the caller's file path. + _, filename, _, _ := runtime.Caller(1) + + // Resolve the directory of the caller's file. + callerDir := filepath.Dir(filename) + + // Construct the absolute path to the target file. + return filepath.Join(callerDir, path) +} From 0b375770fc3be287288636fc06334e203995887a Mon Sep 17 00:00:00 2001 From: Ferran Borreguero Date: Tue, 9 Jan 2024 15:57:56 +0100 Subject: [PATCH 9/9] Add validation for --ffi enabled + suave binary in forge (#18) * Add validation for --ffi enabled + suave binary in forge --- src/Test.sol | 64 +++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 58 insertions(+), 6 deletions(-) diff --git a/src/Test.sol b/src/Test.sol index 9a78e0f..a065146 100644 --- a/src/Test.sol +++ b/src/Test.sol @@ -2,14 +2,66 @@ pragma solidity ^0.8.8; import "./forge/Registry.sol"; +import "forge-std/Test.sol"; -contract SuaveEnabled { +contract SuaveEnabled is Test { function setUp() public { - // TODO: Add checks to validate that: - // - User is running the test with ffi. Since vm.ffi is deployed as a contract, the error if ffi is not active - // is reported as a Suave.PeekerReverted error and it is not clear what the problem is. - // - Suave binary is on $PATH and Suave is running. This could be done with ffi calls to the suave binary. - // Put this logic inside `enable` itself. + string[] memory inputs = new string[](3); + inputs[0] = "suave-geth"; + inputs[1] = "forge"; + inputs[2] = "status"; + + try vm.ffi(inputs) returns (bytes memory response) { + // the status call of the `suave-geth forge` command fails with the 'not-ok' prefix + // which is '6e6f742d6f6b' in hex + if (isPrefix(hex"6e6f742d6f6b", response)) { + revert("Local Suave node not detected running"); + } + } catch (bytes memory reason) { + revert(detectErrorMessage(reason)); + } + Registry.enable(); } + + function detectErrorMessage(bytes memory reason) internal pure returns (string memory) { + // Errors from cheatcodes are reported as 'CheatcodeError(string)' events + // 'eeaa9e6f' is the signature of the event + if (!isPrefix(hex"eeaa9e6f", reason)) { + return string(reason); + } + + // retrieve the body of the event by removing the signature + bytes memory eventBody = new bytes(reason.length - 4); + for (uint256 i = 4; i < reason.length; i++) { + eventBody[i - 4] = reason[i]; + } + + // decode event as 'tuple(bytes message)' since it is equivalent to tuple(string) + (bytes memory message) = abi.decode(eventBody, (bytes)); + + // the prefix is 'FFI is disabled' in hex + if (isPrefix(hex"4646492069732064697361626c6564", message)) { + return "Suave <> Forge integration requires the --ffi flag to be enabled"; + } + + // the prefix is 'failed to execute command' in hex + if (isPrefix(hex"6661696c656420746f206578656375746520636f6d6d616e64", message)) { + return "Forge cannot locate the 'suave' binary. Is it installed in $PATH?"; + } + + return string(message); + } + + function isPrefix(bytes memory prefix, bytes memory data) internal pure returns (bool) { + if (prefix.length > data.length) { + return false; + } + for (uint256 i = 0; i < prefix.length; i++) { + if (prefix[i] != data[i]) { + return false; + } + } + return true; + } }