Skip to content
This repository was archived by the owner on May 12, 2025. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion core/types/suave_structs.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

46 changes: 46 additions & 0 deletions core/vm/contracts_suave.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"net/http"
"net/url"
"strings"
"sync"
"time"

"github.com/ethereum/go-ethereum/accounts"
Expand Down Expand Up @@ -296,6 +297,51 @@ func (s *suaveRuntime) doHTTPRequest(request types.HttpRequest) ([]byte, error)
return data, nil
}

// TODO: Non-aggregated error handling
func (s *suaveRuntime) doHTTPRequests(requests []types.HttpRequest) ([][]byte, error) {
var wg sync.WaitGroup
responses := make([][]byte, len(requests))
errCh := make(chan error, len(requests))
resCh := make(chan struct {
index int
resp []byte
}, len(requests))

for i, request := range requests {
wg.Add(1)
go func(i int, request types.HttpRequest) {
defer wg.Done()
resp, err := s.doHTTPRequest(request)
if err != nil {
errCh <- err
return
}
resCh <- struct {
index int
resp []byte
}{index: i, resp: resp}
}(i, request)
}

go func() {
wg.Wait()
close(errCh)
close(resCh)
}()

for err := range errCh {
if err != nil {
return nil, err
}
}

for res := range resCh {
responses[res.index] = res.resp
}

return responses, nil
}

func (s *suaveRuntime) resolveURL(urlOrServiceName string) (string, error) {
var allowed bool
// resolve dns if possible
Expand Down
50 changes: 48 additions & 2 deletions core/vm/contracts_suave_runtime_adapter.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions core/vm/contracts_suave_runtime_adapter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@ func (m *mockRuntime) doHTTPRequest(request types.HttpRequest) ([]byte, error) {
return []byte{0x1}, nil
}

func (m *mockRuntime) doHTTPRequests(request []types.HttpRequest) ([][]byte, error) {
var byteSlices [][]byte
byteSlices = append(byteSlices, []byte{0x1})
return byteSlices, nil
}

func (m *mockRuntime) newBuilder() (string, error) {
return "", nil
}
Expand Down
30 changes: 30 additions & 0 deletions core/vm/contracts_suave_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package vm
import (
"context"
"math/big"
"fmt"
"net/http"
"net/http/httptest"
"strings"
Expand Down Expand Up @@ -293,6 +294,35 @@ func TestSuave_HttpRequest_Basic(t *testing.T) {
}
}

func TestSuave_HttpRequests_Basic(t *testing.T) {
srv := httptest.NewServer(&httpTestHandler{
fn: basicHandler,
})

s := &suaveRuntime{
suaveContext: &SuaveContext{
Backend: &SuaveExecutionBackend{
ExternalWhitelist: []string{"httpbin.org"},
},
},
}

defer srv.Close()

var requests []types.HttpRequest
for i := 0; i < 3; i++ {
requests = append(requests, types.HttpRequest{Url: "https://httpbin.org/delay/1", Method: "GET"})
}

start := time.Now()
resp, err := s.doHTTPRequests(requests)
elapsed := time.Since(start)
require.NoError(t, err)
fmt.Println(resp)

require.True(t, elapsed < 2*time.Second)
}

func TestSuave_HttpRequest_FlashbotsSignatue(t *testing.T) {
signingKey, _ := crypto.GenerateKey()
signingKeyAddr := crypto.PubkeyToAddress(signingKey.PublicKey).Hex()
Expand Down
2 changes: 1 addition & 1 deletion suave/artifacts/SuaveLib.json

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion suave/artifacts/addresses.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions suave/gen/suave_spec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,18 @@ functions:
- name: httpResponse
type: bytes
description: "Body of the response"
- name: doHTTPRequests
address: "0x0000000000000000000000000000000043200003"
description: "Executes multiple `doHTTPRequest`s async"
input:
- name: requests
type: HttpRequest[]
description: "Requests to perform"
output:
fields:
- name: httpResponses
type: bytes[]
description: "Body of the responses"
- name: newBuilder
address: "0x0000000000000000000000000000000053200001"
description: "Initializes a new remote builder session"
Expand Down
14 changes: 14 additions & 0 deletions suave/sol/libraries/Suave.sol
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ library Suave {

address public constant DO_HTTPREQUEST = 0x0000000000000000000000000000000043200002;

address public constant DO_HTTPREQUESTS = 0x0000000000000000000000000000000043200003;

address public constant ETHCALL = 0x0000000000000000000000000000000042100003;

address public constant EXTRACT_HINT = 0x0000000000000000000000000000000042100037;
Expand Down Expand Up @@ -264,6 +266,18 @@ library Suave {
return abi.decode(data, (bytes));
}

/// @notice Executes multiple `doHTTPRequest`s async
/// @param requests Requests to perform
/// @return httpResponses Body of the responses
function doHTTPRequests(HttpRequest[] memory requests) internal returns (bytes[] memory) {
(bool success, bytes memory data) = DO_HTTPREQUESTS.call(abi.encode(requests));
if (!success) {
revert PeekerReverted(DO_HTTPREQUESTS, data);
}

return abi.decode(data, (bytes[]));
}

/// @notice Uses the `eth_call` JSON RPC method to let you simulate a function call and return the response.
/// @param contractAddr Address of the contract to call
/// @param input1 Data to send to the contract
Expand Down