Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add validation for --ffi enabled + suave binary in forge #18

Merged
merged 12 commits into from
Jan 9, 2024
Merged
4 changes: 4 additions & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
@@ -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
34 changes: 34 additions & 0 deletions .github/workflows/check-gen-code.yml
Original file line number Diff line number Diff line change
@@ -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
22 changes: 4 additions & 18 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: CI
on:
push:
branches:
- master
- main
pull_request:

env:
Expand All @@ -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/[email protected]

- name: Run suave
run: |
./suave-geth/build/bin/suave --suave.dev &
suave-geth --suave.dev &

- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
Expand Down
29 changes: 28 additions & 1 deletion .github/workflows/suave-lib-sync.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
name: SuaveLib sync

on: [repository_dispatch, workflow_dispatch]
on:
workflow_dispatch:
repository_dispatch:
types: [suavelib-sync]

permissions:
pull-requests: write
Expand All @@ -12,12 +15,25 @@ 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:
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:
Expand All @@ -39,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:
Expand Down
64 changes: 58 additions & 6 deletions src/Test.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
2 changes: 1 addition & 1 deletion src/forge/Connector.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
3 changes: 2 additions & 1 deletion src/forge/Registry.sol
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -13,7 +14,7 @@ library Registry {
function enableLib(address addr) public {
// code for Wrapper
bytes memory code =
hex"608060405234801561001057600080fd5b506004361061002b5760003560e01c8063671ff786146100a1575b6040516bffffffffffffffffffffffff193060601b1660208201526000906100959060340160408051808303601f19018152602036601f8101829004820285018201909352828452909291600091819084018382808284376000920191909152506100ca92505050565b90508081518060208301f35b6100b46100af36600461048c565b610259565b6040516100c1919061055c565b60405180910390f35b606060006100d784610259565b905060006100e484610259565b60408051600480825260a0820190925291925060009190816020015b606081526020019060019003908161010057905050905060405180604001604052806005815260200164737561766560d81b8152508160008151811061014857610148610576565b602002602001018190525060405180604001604052806005815260200164666f72676560d81b8152508160018151811061018457610184610576565b602002602001018190525082816002815181106101a3576101a3610576565b602002602001018190525081816003815181106101c2576101c2610576565b6020908102919091010152604051638916046760e01b8152600090737109709ecfa91a80626ff3989d68f67f5b1dd12d9063891604679061020790859060040161058c565b600060405180830381865afa158015610224573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261024c91908101906105ee565b9450505050505b92915050565b606060008251600261026b919061067b565b67ffffffffffffffff8111156102835761028361041d565b6040519080825280601f01601f1916602001820160405280156102ad576020820181803683370190505b5060408051808201909152601081526f181899199a1a9b1b9c1cb0b131b232b360811b602082015290915060005b84518110156103f3578182518683815181106102f9576102f9610576565b016020015161030b919060f81c6106a8565b8151811061031b5761031b610576565b01602001516001600160f81b0319168361033683600261067b565b8151811061034657610346610576565b60200101906001600160f81b031916908160001a90535081825186838151811061037257610372610576565b0160200151610384919060f81c6106bc565b8151811061039457610394610576565b01602001516001600160f81b031916836103af83600261067b565b6103ba9060016106d0565b815181106103ca576103ca610576565b60200101906001600160f81b031916908160001a905350806103eb816106e3565b9150506102db565b508160405160200161040591906106fc565b60405160208183030381529060405292505050919050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff8111828210171561045c5761045c61041d565b604052919050565b600067ffffffffffffffff82111561047e5761047e61041d565b50601f01601f191660200190565b60006020828403121561049e57600080fd5b813567ffffffffffffffff8111156104b557600080fd5b8201601f810184136104c657600080fd5b80356104d96104d482610464565b610433565b8181528560208385010111156104ee57600080fd5b81602084016020830137600091810160200191909152949350505050565b60005b8381101561052757818101518382015260200161050f565b50506000910152565b6000815180845261054881602086016020860161050c565b601f01601f19169290920160200192915050565b60208152600061056f6020830184610530565b9392505050565b634e487b7160e01b600052603260045260246000fd5b6000602080830181845280855180835260408601915060408160051b870101925083870160005b828110156105e157603f198886030184526105cf858351610530565b945092850192908501906001016105b3565b5092979650505050505050565b60006020828403121561060057600080fd5b815167ffffffffffffffff81111561061757600080fd5b8201601f8101841361062857600080fd5b80516106366104d482610464565b81815285602083850101111561064b57600080fd5b61065c82602083016020860161050c565b95945050505050565b634e487b7160e01b600052601160045260246000fd5b808202811582820484141761025357610253610665565b634e487b7160e01b600052601260045260246000fd5b6000826106b7576106b7610692565b500490565b6000826106cb576106cb610692565b500690565b8082018082111561025357610253610665565b6000600182016106f5576106f5610665565b5060010190565b61060f60f31b81526000825161071981600285016020870161050c565b919091016002019291505056fea2646970667358221220e66d500bc9a9ca9c0748086adfc51de57c85b7c9f66cc760e823099f0439820b64736f6c63430008130033";
hex"608060405234801561001057600080fd5b506004361061002b5760003560e01c8063671ff786146100a1575b6040516bffffffffffffffffffffffff193060601b1660208201526000906100959060340160408051808303601f19018152602036601f8101829004820285018201909352828452909291600091819084018382808284376000920191909152506100ca92505050565b90508081518060208301f35b6100b46100af366004610487565b61025e565b6040516100c19190610557565b60405180910390f35b606060006100d78461025e565b905060006100e48461025e565b60408051600480825260a0820190925291925060009190816020015b60608152602001906001900390816101005790505090506040518060400160405280600a8152602001690e6eac2ecca5acecae8d60b31b8152508160008151811061014d5761014d610571565b602002602001018190525060405180604001604052806005815260200164666f72676560d81b8152508160018151811061018957610189610571565b602002602001018190525082816002815181106101a8576101a8610571565b602002602001018190525081816003815181106101c7576101c7610571565b6020908102919091010152604051638916046760e01b8152600090737109709ecfa91a80626ff3989d68f67f5b1dd12d9063891604679061020c908590600401610587565b600060405180830381865afa158015610229573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261025191908101906105eb565b9450505050505b92915050565b60606000825160026102709190610678565b67ffffffffffffffff81111561028857610288610418565b6040519080825280601f01601f1916602001820160405280156102b2576020820181803683370190505b5060408051808201909152601081526f181899199a1a9b1b9c1cb0b131b232b360811b602082015290915060005b84518110156103ee578182518683815181106102fe576102fe610571565b0160200151610310919060f81c6106a5565b8151811061032057610320610571565b01602001516001600160f81b0319168361033b836002610678565b8151811061034b5761034b610571565b60200101906001600160f81b031916908160001a90535081825186838151811061037757610377610571565b0160200151610389919060f81c6106b9565b8151811061039957610399610571565b01602001516001600160f81b031916836103b4836002610678565b6103bf9060016106cd565b815181106103cf576103cf610571565b60200101906001600160f81b031916908160001a9053506001016102e0565b508160405160200161040091906106e0565b60405160208183030381529060405292505050919050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff8111828210171561045757610457610418565b604052919050565b600067ffffffffffffffff82111561047957610479610418565b50601f01601f191660200190565b60006020828403121561049957600080fd5b813567ffffffffffffffff8111156104b057600080fd5b8201601f810184136104c157600080fd5b80356104d46104cf8261045f565b61042e565b8181528560208385010111156104e957600080fd5b81602084016020830137600091810160200191909152949350505050565b60005b8381101561052257818101518382015260200161050a565b50506000910152565b60008151808452610543816020860160208601610507565b601f01601f19169290920160200192915050565b60208152600061056a602083018461052b565b9392505050565b634e487b7160e01b600052603260045260246000fd5b600060208083016020845280855180835260408601915060408160051b87010192506020870160005b828110156105de57603f198886030184526105cc85835161052b565b945092850192908501906001016105b0565b5092979650505050505050565b6000602082840312156105fd57600080fd5b815167ffffffffffffffff81111561061457600080fd5b8201601f8101841361062557600080fd5b80516106336104cf8261045f565b81815285602083850101111561064857600080fd5b610659826020830160208601610507565b95945050505050565b634e487b7160e01b600052601160045260246000fd5b808202811582820484141761025857610258610662565b634e487b7160e01b600052601260045260246000fd5b6000826106b4576106b461068f565b500490565b6000826106c8576106c861068f565b500690565b8082018082111561025857610258610662565b61060f60f31b8152600082516106fd816002850160208701610507565b919091016002019291505056fea164736f6c6343000817000a";
vm.etch(addr, code);
}

Expand Down
9 changes: 5 additions & 4 deletions src/suavelib/Suave.sol
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ library Suave {
string method;
string[] headers;
bytes body;
bool withFlashbotsSignature;
}

struct SimulateTransactionResult {
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand Down
6 changes: 6 additions & 0 deletions tools/forge-gen/foundry.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[profile.default]
solc_version = "0.8.23"
src = "../../src"
out = "./out"
libs = ["../../lib"]
bytecode_hash = "none"
41 changes: 30 additions & 11 deletions tools/forge-gen/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import (
"html/template"
"os"
"os/exec"
"path/filepath"
"regexp"
"runtime"
"strings"
)

Expand Down Expand Up @@ -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 {
Expand All @@ -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
}
Expand Down Expand Up @@ -138,31 +145,43 @@ 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
cmd.Stdout = &outBuf
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)
}
Loading