From 9c4296d991a1a3d9cf45cecab8aa1fdda3666e86 Mon Sep 17 00:00:00 2001 From: Ferran Borreguero Date: Tue, 3 Dec 2024 15:08:09 +0000 Subject: [PATCH 1/2] Add precompile httpRequest2 that returns whole response --- core/types/suave_structs.go | 8 ++- core/vm/contracts_suave.go | 36 ++++++++----- core/vm/contracts_suave_runtime_adapter.go | 52 ++++++++++++++++++- .../contracts_suave_runtime_adapter_test.go | 4 ++ suave/artifacts/SuaveLib.json | 2 +- suave/artifacts/addresses.go | 6 ++- .../a7b739ab8239814d5bc9f53cf4bf4268.json | 1 + suave/e2e/workflow_test.go | 19 ++++++- suave/gen/main.go | 3 ++ suave/gen/suave_spec.yaml | 24 +++++++++ suave/lib/suave-std | 1 + suave/sol/libraries/Suave.sol | 28 +++++++++- suave/sol/standard_peekers/example.sol | 5 ++ 13 files changed, 169 insertions(+), 20 deletions(-) create mode 100644 suave/artifacts/build-info/a7b739ab8239814d5bc9f53cf4bf4268.json create mode 160000 suave/lib/suave-std diff --git a/core/types/suave_structs.go b/core/types/suave_structs.go index 3ff89c3d38..14caa04955 100755 --- a/core/types/suave_structs.go +++ b/core/types/suave_structs.go @@ -1,5 +1,5 @@ // Code generated by suave/gen. DO NOT EDIT. -// Hash: 0c3427c1ad6242fe36606599fbb5d69d17fab52cab79a5350be8055daae46338 +// Hash: 93d0334f831efa37bf623305bc1fadbb86200c71f9e3c4a97337f7879f4216c1 package types import "github.com/ethereum/go-ethereum/common" @@ -40,6 +40,12 @@ type HttpRequest struct { Timeout uint64 } +type HttpResponse struct { + Status uint64 + Body []byte + Error []byte +} + type SimulateTransactionResult struct { Egp uint64 Logs []*SimulatedLog diff --git a/core/vm/contracts_suave.go b/core/vm/contracts_suave.go index 087980e47f..a9fce7aca0 100644 --- a/core/vm/contracts_suave.go +++ b/core/vm/contracts_suave.go @@ -219,12 +219,12 @@ func (c *consoleLogPrecompile) Run(input []byte) ([]byte, error) { var contextCookieKeyPrefix = "__cookie_" -func (s *suaveRuntime) doHTTPRequest(request types.HttpRequest) ([]byte, error) { +func (s *suaveRuntime) doHTTPRequest2(request types.HttpRequest) (types.HttpResponse, error) { if request.Method != "GET" && request.Method != "POST" { - return nil, fmt.Errorf("only GET and POST methods are supported") + return types.HttpResponse{}, fmt.Errorf("only GET and POST methods are supported") } if request.Url == "" { - return nil, fmt.Errorf("url is empty") + return types.HttpResponse{}, fmt.Errorf("url is empty") } var body io.Reader @@ -234,11 +234,11 @@ func (s *suaveRuntime) doHTTPRequest(request types.HttpRequest) ([]byte, error) url, err := s.resolveURL(request.Url) if err != nil { - return nil, err + return types.HttpResponse{}, err } req, err := http.NewRequest(request.Method, url, body) if err != nil { - return nil, err + return types.HttpResponse{}, err } // add any cookies stored in the context @@ -251,7 +251,7 @@ func (s *suaveRuntime) doHTTPRequest(request types.HttpRequest) ([]byte, error) for _, header := range request.Headers { indx := strings.Index(header, ":") if indx == -1 { - return nil, fmt.Errorf("incorrect header format '%s', no ':' present", header) + return types.HttpResponse{}, fmt.Errorf("incorrect header format '%s', no ':' present", header) } req.Header.Add(header[:indx], header[indx+1:]) } @@ -261,7 +261,7 @@ func (s *suaveRuntime) doHTTPRequest(request types.HttpRequest) ([]byte, error) hashedBody := crypto.Keccak256Hash(request.Body).Hex() sig, err := crypto.Sign(accounts.TextHash([]byte(hashedBody)), s.suaveContext.Backend.EthBundleSigningKey) if err != nil { - return nil, err + return types.HttpResponse{}, err } signature := crypto.PubkeyToAddress(s.suaveContext.Backend.EthBundleSigningKey.PublicKey).Hex() + ":" + hexutil.Encode(sig) @@ -280,17 +280,18 @@ func (s *suaveRuntime) doHTTPRequest(request types.HttpRequest) ([]byte, error) } resp, err := client.Do(req) if err != nil { - return nil, err + return types.HttpResponse{}, err } defer resp.Body.Close() data, err := io.ReadAll(resp.Body) if err != nil { - return nil, err + return types.HttpResponse{}, err } - if resp.StatusCode > 299 { - return nil, fmt.Errorf("http error: %s: %v", resp.Status, data) + precResp := types.HttpResponse{ + Status: uint64(resp.StatusCode), + Body: data, } // parse the LB cookies (AWSALB, AWSALBCORS) and set them in the context @@ -300,7 +301,18 @@ func (s *suaveRuntime) doHTTPRequest(request types.HttpRequest) ([]byte, error) } } - return data, nil + return precResp, nil +} + +func (m *suaveRuntime) doHTTPRequest(request types.HttpRequest) ([]byte, error) { + resp, err := m.doHTTPRequest2(request) + if err != nil { + return nil, err + } + if resp.Status > 299 { + return nil, fmt.Errorf("http error: %d: %v", resp.Status, resp.Body) + } + return resp.Body, err } func (s *suaveRuntime) resolveURL(urlOrServiceName string) (string, error) { diff --git a/core/vm/contracts_suave_runtime_adapter.go b/core/vm/contracts_suave_runtime_adapter.go index 87a2b75a8c..f8305a2d33 100644 --- a/core/vm/contracts_suave_runtime_adapter.go +++ b/core/vm/contracts_suave_runtime_adapter.go @@ -1,5 +1,5 @@ // Code generated by suave/gen. DO NOT EDIT. -// Hash: 0c3427c1ad6242fe36606599fbb5d69d17fab52cab79a5350be8055daae46338 +// Hash: 93d0334f831efa37bf623305bc1fadbb86200c71f9e3c4a97337f7879f4216c1 package vm import ( @@ -11,6 +11,8 @@ import ( "math/big" ) +var _ = new(big.Int) + var ( errFailedToUnpackInput = fmt.Errorf("failed to decode input") errFailedToDecodeField = fmt.Errorf("failed to decode field") @@ -27,6 +29,7 @@ type SuaveRuntime interface { confidentialStore(dataId types.DataId, key string, value []byte) error contextGet(key string) ([]byte, error) doHTTPRequest(request types.HttpRequest) ([]byte, error) + doHTTPRequest2(request types.HttpRequest) (types.HttpResponse, error) ethcall(contractAddr common.Address, input1 []byte) ([]byte, error) extractHint(bundleData []byte) ([]byte, error) fetchDataRecords(cond uint64, namespace string) ([]types.DataRecord, error) @@ -54,6 +57,7 @@ var ( confidentialStoreAddr = common.HexToAddress("0x0000000000000000000000000000000042020000") contextGetAddr = common.HexToAddress("0x0000000000000000000000000000000053300003") doHTTPRequestAddr = common.HexToAddress("0x0000000000000000000000000000000043200002") + doHTTPRequest2Addr = common.HexToAddress("0x0000000000000000000000000000000043200003") ethcallAddr = common.HexToAddress("0x0000000000000000000000000000000042100003") extractHintAddr = common.HexToAddress("0x0000000000000000000000000000000042100037") fetchDataRecordsAddr = common.HexToAddress("0x0000000000000000000000000000000042030001") @@ -72,7 +76,7 @@ var ( ) var addrList = []common.Address{ - aesDecryptAddr, aesEncryptAddr, buildEthBlockAddr, buildEthBlockToAddr, confidentialInputsAddr, confidentialRetrieveAddr, confidentialStoreAddr, contextGetAddr, doHTTPRequestAddr, ethcallAddr, extractHintAddr, fetchDataRecordsAddr, fillMevShareBundleAddr, getInsecureTimeAddr, newBuilderAddr, newDataRecordAddr, privateKeyGenAddr, randomBytesAddr, signEthTransactionAddr, signMessageAddr, simulateBundleAddr, simulateTransactionAddr, submitBundleJsonRPCAddr, submitEthBlockToRelayAddr, + aesDecryptAddr, aesEncryptAddr, buildEthBlockAddr, buildEthBlockToAddr, confidentialInputsAddr, confidentialRetrieveAddr, confidentialStoreAddr, contextGetAddr, doHTTPRequestAddr, doHTTPRequest2Addr, ethcallAddr, extractHintAddr, fetchDataRecordsAddr, fillMevShareBundleAddr, getInsecureTimeAddr, newBuilderAddr, newDataRecordAddr, privateKeyGenAddr, randomBytesAddr, signEthTransactionAddr, signMessageAddr, simulateBundleAddr, simulateTransactionAddr, submitBundleJsonRPCAddr, submitEthBlockToRelayAddr, } type SuaveRuntimeAdapter struct { @@ -108,6 +112,9 @@ func (b *SuaveRuntimeAdapter) run(addr common.Address, input []byte) ([]byte, er case doHTTPRequestAddr: return b.doHTTPRequest(input) + case doHTTPRequest2Addr: + return b.doHTTPRequest2(input) + case ethcallAddr: return b.ethcall(input) @@ -531,6 +538,47 @@ func (b *SuaveRuntimeAdapter) doHTTPRequest(input []byte) (res []byte, err error } +func (b *SuaveRuntimeAdapter) doHTTPRequest2(input []byte) (res []byte, err error) { + var ( + unpacked []interface{} + result []byte + ) + + _ = unpacked + _ = result + + unpacked, err = artifacts.SuaveAbi.Methods["doHTTPRequest2"].Inputs.Unpack(input) + if err != nil { + err = errFailedToUnpackInput + return + } + + var ( + request types.HttpRequest + ) + + if err = mapstructure.Decode(unpacked[0], &request); err != nil { + err = errFailedToDecodeField + return + } + + var ( + httpResponse types.HttpResponse + ) + + if httpResponse, err = b.impl.doHTTPRequest2(request); err != nil { + return + } + + result, err = artifacts.SuaveAbi.Methods["doHTTPRequest2"].Outputs.Pack(httpResponse) + if err != nil { + err = errFailedToPackOutput + return + } + return result, nil + +} + func (b *SuaveRuntimeAdapter) ethcall(input []byte) (res []byte, err error) { var ( unpacked []interface{} diff --git a/core/vm/contracts_suave_runtime_adapter_test.go b/core/vm/contracts_suave_runtime_adapter_test.go index c84138915a..44d0a3598e 100644 --- a/core/vm/contracts_suave_runtime_adapter_test.go +++ b/core/vm/contracts_suave_runtime_adapter_test.go @@ -80,6 +80,10 @@ func (m *mockRuntime) doHTTPRequest(request types.HttpRequest) ([]byte, error) { return []byte{0x1}, nil } +func (m *mockRuntime) doHTTPRequest2(request types.HttpRequest) (types.HttpResponse, error) { + return types.HttpResponse{}, nil +} + func (m *mockRuntime) newBuilder() (string, error) { return "", nil } diff --git a/suave/artifacts/SuaveLib.json b/suave/artifacts/SuaveLib.json index 75715d483d..9c610a101e 100644 --- a/suave/artifacts/SuaveLib.json +++ b/suave/artifacts/SuaveLib.json @@ -1 +1 @@ -[{"type":"error","name":"PeekerReverted","inputs":[{"name":"addr","type":"address"},{"name":"err","type":"bytes"}]},{"type":"function","name":"aesDecrypt","inputs":[{"name":"key","type":"bytes","internalType":"bytes"},{"name":"ciphertext","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"message","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"aesEncrypt","inputs":[{"name":"key","type":"bytes","internalType":"bytes"},{"name":"message","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"ciphertext","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"buildEthBlock","inputs":[{"name":"blockArgs","type":"tuple","internalType":"struct Suave.BuildBlockArgs","components":[{"name":"slot","type":"uint64","internalType":"uint64"},{"name":"proposerPubkey","type":"bytes","internalType":"bytes"},{"name":"parent","type":"bytes32","internalType":"bytes32"},{"name":"timestamp","type":"uint64","internalType":"uint64"},{"name":"feeRecipient","type":"address","internalType":"address"},{"name":"gasLimit","type":"uint64","internalType":"uint64"},{"name":"random","type":"bytes32","internalType":"bytes32"},{"name":"withdrawals","type":"tuple[]","internalType":"struct Suave.Withdrawal[]","components":[{"name":"index","type":"uint64","internalType":"uint64"},{"name":"validator","type":"uint64","internalType":"uint64"},{"name":"Address","type":"address","internalType":"address"},{"name":"amount","type":"uint64","internalType":"uint64"}]},{"name":"extra","type":"bytes","internalType":"bytes"},{"name":"beaconRoot","type":"bytes32","internalType":"bytes32"},{"name":"fillPending","type":"bool","internalType":"bool"}]},{"name":"dataId","type":"bytes16","internalType":"struct Suave.DataId"},{"name":"relayUrl","type":"string","internalType":"string"}],"outputs":[{"name":"blockBid","type":"bytes","internalType":"bytes"},{"name":"executionPayload","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"buildEthBlockTo","inputs":[{"name":"executionNodeURL","type":"string","internalType":"string"},{"name":"blockArgs","type":"tuple","internalType":"struct Suave.BuildBlockArgs","components":[{"name":"slot","type":"uint64","internalType":"uint64"},{"name":"proposerPubkey","type":"bytes","internalType":"bytes"},{"name":"parent","type":"bytes32","internalType":"bytes32"},{"name":"timestamp","type":"uint64","internalType":"uint64"},{"name":"feeRecipient","type":"address","internalType":"address"},{"name":"gasLimit","type":"uint64","internalType":"uint64"},{"name":"random","type":"bytes32","internalType":"bytes32"},{"name":"withdrawals","type":"tuple[]","internalType":"struct Suave.Withdrawal[]","components":[{"name":"index","type":"uint64","internalType":"uint64"},{"name":"validator","type":"uint64","internalType":"uint64"},{"name":"Address","type":"address","internalType":"address"},{"name":"amount","type":"uint64","internalType":"uint64"}]},{"name":"extra","type":"bytes","internalType":"bytes"},{"name":"beaconRoot","type":"bytes32","internalType":"bytes32"},{"name":"fillPending","type":"bool","internalType":"bool"}]},{"name":"dataId","type":"bytes16","internalType":"struct Suave.DataId"},{"name":"relayUrl","type":"string","internalType":"string"}],"outputs":[{"name":"blockBid","type":"bytes","internalType":"bytes"},{"name":"executionPayload","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"confidentialInputs","outputs":[{"name":"confindentialData","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"confidentialRetrieve","inputs":[{"name":"dataId","type":"bytes16","internalType":"struct Suave.DataId"},{"name":"key","type":"string","internalType":"string"}],"outputs":[{"name":"value","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"confidentialStore","inputs":[{"name":"dataId","type":"bytes16","internalType":"struct Suave.DataId"},{"name":"key","type":"string","internalType":"string"},{"name":"value","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"contextGet","inputs":[{"name":"key","type":"string","internalType":"string"}],"outputs":[{"name":"value","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"doHTTPRequest","inputs":[{"name":"request","type":"tuple","internalType":"struct Suave.HttpRequest","components":[{"name":"url","type":"string","internalType":"string"},{"name":"method","type":"string","internalType":"string"},{"name":"headers","type":"string[]","internalType":"string[]"},{"name":"body","type":"bytes","internalType":"bytes"},{"name":"withFlashbotsSignature","type":"bool","internalType":"bool"},{"name":"timeout","type":"uint64","internalType":"uint64"}]}],"outputs":[{"name":"httpResponse","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"ethcall","inputs":[{"name":"contractAddr","type":"address","internalType":"address"},{"name":"input1","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"callOutput","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"extractHint","inputs":[{"name":"bundleData","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"hints","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"fetchDataRecords","inputs":[{"name":"cond","type":"uint64","internalType":"uint64"},{"name":"namespace","type":"string","internalType":"string"}],"outputs":[{"name":"dataRecords","type":"tuple[]","internalType":"struct Suave.DataRecord[]","components":[{"name":"id","type":"bytes16","internalType":"struct Suave.DataId"},{"name":"salt","type":"bytes16","internalType":"struct Suave.DataId"},{"name":"decryptionCondition","type":"uint64","internalType":"uint64"},{"name":"allowedPeekers","type":"address[]","internalType":"address[]"},{"name":"allowedStores","type":"address[]","internalType":"address[]"},{"name":"version","type":"string","internalType":"string"}]}]},{"type":"function","name":"fillMevShareBundle","inputs":[{"name":"dataId","type":"bytes16","internalType":"struct Suave.DataId"}],"outputs":[{"name":"encodedBundle","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"getInsecureTime","outputs":[{"name":"time","type":"uint256","internalType":"uint256"}]},{"type":"function","name":"newBuilder","outputs":[{"name":"sessionid","type":"string","internalType":"string"}]},{"type":"function","name":"newDataRecord","inputs":[{"name":"decryptionCondition","type":"uint64","internalType":"uint64"},{"name":"allowedPeekers","type":"address[]","internalType":"address[]"},{"name":"allowedStores","type":"address[]","internalType":"address[]"},{"name":"dataType","type":"string","internalType":"string"}],"outputs":[{"name":"dataRecord","type":"tuple","internalType":"struct Suave.DataRecord","components":[{"name":"id","type":"bytes16","internalType":"struct Suave.DataId"},{"name":"salt","type":"bytes16","internalType":"struct Suave.DataId"},{"name":"decryptionCondition","type":"uint64","internalType":"uint64"},{"name":"allowedPeekers","type":"address[]","internalType":"address[]"},{"name":"allowedStores","type":"address[]","internalType":"address[]"},{"name":"version","type":"string","internalType":"string"}]}]},{"type":"function","name":"privateKeyGen","inputs":[{"name":"crypto","type":"uint8","internalType":"struct Suave.CryptoSignature"}],"outputs":[{"name":"privateKey","type":"string","internalType":"string"}]},{"type":"function","name":"randomBytes","inputs":[{"name":"numBytes","type":"uint8","internalType":"uint8"}],"outputs":[{"name":"value","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"signEthTransaction","inputs":[{"name":"txn","type":"bytes","internalType":"bytes"},{"name":"chainId","type":"string","internalType":"string"},{"name":"signingKey","type":"string","internalType":"string"}],"outputs":[{"name":"signedTxn","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"signMessage","inputs":[{"name":"digest","type":"bytes","internalType":"bytes"},{"name":"crypto","type":"uint8","internalType":"struct Suave.CryptoSignature"},{"name":"signingKey","type":"string","internalType":"string"}],"outputs":[{"name":"signature","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"simulateBundle","inputs":[{"name":"bundleData","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"effectiveGasPrice","type":"uint64","internalType":"uint64"}]},{"type":"function","name":"simulateTransaction","inputs":[{"name":"sessionid","type":"string","internalType":"string"},{"name":"txn","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"simulationResult","type":"tuple","internalType":"struct Suave.SimulateTransactionResult","components":[{"name":"egp","type":"uint64","internalType":"uint64"},{"name":"logs","type":"tuple[]","internalType":"struct Suave.SimulatedLog[]","components":[{"name":"data","type":"bytes","internalType":"bytes"},{"name":"addr","type":"address","internalType":"address"},{"name":"topics","type":"bytes32[]","internalType":"bytes32[]"}]},{"name":"success","type":"bool","internalType":"bool"},{"name":"error","type":"string","internalType":"string"}]}]},{"type":"function","name":"submitBundleJsonRPC","inputs":[{"name":"url","type":"string","internalType":"string"},{"name":"method","type":"string","internalType":"string"},{"name":"params","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"errorMessage","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"submitEthBlockToRelay","inputs":[{"name":"relayUrl","type":"string","internalType":"string"},{"name":"builderBid","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"blockBid","type":"bytes","internalType":"bytes"}]}] \ No newline at end of file +[{"type":"error","name":"PeekerReverted","inputs":[{"name":"addr","type":"address"},{"name":"err","type":"bytes"}]},{"type":"function","name":"aesDecrypt","inputs":[{"name":"key","type":"bytes","internalType":"bytes"},{"name":"ciphertext","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"message","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"aesEncrypt","inputs":[{"name":"key","type":"bytes","internalType":"bytes"},{"name":"message","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"ciphertext","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"buildEthBlock","inputs":[{"name":"blockArgs","type":"tuple","internalType":"struct Suave.BuildBlockArgs","components":[{"name":"slot","type":"uint64","internalType":"uint64"},{"name":"proposerPubkey","type":"bytes","internalType":"bytes"},{"name":"parent","type":"bytes32","internalType":"bytes32"},{"name":"timestamp","type":"uint64","internalType":"uint64"},{"name":"feeRecipient","type":"address","internalType":"address"},{"name":"gasLimit","type":"uint64","internalType":"uint64"},{"name":"random","type":"bytes32","internalType":"bytes32"},{"name":"withdrawals","type":"tuple[]","internalType":"struct Suave.Withdrawal[]","components":[{"name":"index","type":"uint64","internalType":"uint64"},{"name":"validator","type":"uint64","internalType":"uint64"},{"name":"Address","type":"address","internalType":"address"},{"name":"amount","type":"uint64","internalType":"uint64"}]},{"name":"extra","type":"bytes","internalType":"bytes"},{"name":"beaconRoot","type":"bytes32","internalType":"bytes32"},{"name":"fillPending","type":"bool","internalType":"bool"}]},{"name":"dataId","type":"bytes16","internalType":"struct Suave.DataId"},{"name":"relayUrl","type":"string","internalType":"string"}],"outputs":[{"name":"blockBid","type":"bytes","internalType":"bytes"},{"name":"executionPayload","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"buildEthBlockTo","inputs":[{"name":"executionNodeURL","type":"string","internalType":"string"},{"name":"blockArgs","type":"tuple","internalType":"struct Suave.BuildBlockArgs","components":[{"name":"slot","type":"uint64","internalType":"uint64"},{"name":"proposerPubkey","type":"bytes","internalType":"bytes"},{"name":"parent","type":"bytes32","internalType":"bytes32"},{"name":"timestamp","type":"uint64","internalType":"uint64"},{"name":"feeRecipient","type":"address","internalType":"address"},{"name":"gasLimit","type":"uint64","internalType":"uint64"},{"name":"random","type":"bytes32","internalType":"bytes32"},{"name":"withdrawals","type":"tuple[]","internalType":"struct Suave.Withdrawal[]","components":[{"name":"index","type":"uint64","internalType":"uint64"},{"name":"validator","type":"uint64","internalType":"uint64"},{"name":"Address","type":"address","internalType":"address"},{"name":"amount","type":"uint64","internalType":"uint64"}]},{"name":"extra","type":"bytes","internalType":"bytes"},{"name":"beaconRoot","type":"bytes32","internalType":"bytes32"},{"name":"fillPending","type":"bool","internalType":"bool"}]},{"name":"dataId","type":"bytes16","internalType":"struct Suave.DataId"},{"name":"relayUrl","type":"string","internalType":"string"}],"outputs":[{"name":"blockBid","type":"bytes","internalType":"bytes"},{"name":"executionPayload","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"confidentialInputs","outputs":[{"name":"confindentialData","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"confidentialRetrieve","inputs":[{"name":"dataId","type":"bytes16","internalType":"struct Suave.DataId"},{"name":"key","type":"string","internalType":"string"}],"outputs":[{"name":"value","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"confidentialStore","inputs":[{"name":"dataId","type":"bytes16","internalType":"struct Suave.DataId"},{"name":"key","type":"string","internalType":"string"},{"name":"value","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"contextGet","inputs":[{"name":"key","type":"string","internalType":"string"}],"outputs":[{"name":"value","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"doHTTPRequest","inputs":[{"name":"request","type":"tuple","internalType":"struct Suave.HttpRequest","components":[{"name":"url","type":"string","internalType":"string"},{"name":"method","type":"string","internalType":"string"},{"name":"headers","type":"string[]","internalType":"string[]"},{"name":"body","type":"bytes","internalType":"bytes"},{"name":"withFlashbotsSignature","type":"bool","internalType":"bool"},{"name":"timeout","type":"uint64","internalType":"uint64"}]}],"outputs":[{"name":"httpResponse","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"doHTTPRequest2","inputs":[{"name":"request","type":"tuple","internalType":"struct Suave.HttpRequest","components":[{"name":"url","type":"string","internalType":"string"},{"name":"method","type":"string","internalType":"string"},{"name":"headers","type":"string[]","internalType":"string[]"},{"name":"body","type":"bytes","internalType":"bytes"},{"name":"withFlashbotsSignature","type":"bool","internalType":"bool"},{"name":"timeout","type":"uint64","internalType":"uint64"}]}],"outputs":[{"name":"httpResponse","type":"tuple","internalType":"struct Suave.HttpResponse","components":[{"name":"status","type":"uint64","internalType":"uint64"},{"name":"body","type":"bytes","internalType":"bytes"},{"name":"error","type":"bytes","internalType":"bytes"}]}]},{"type":"function","name":"ethcall","inputs":[{"name":"contractAddr","type":"address","internalType":"address"},{"name":"input1","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"callOutput","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"extractHint","inputs":[{"name":"bundleData","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"hints","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"fetchDataRecords","inputs":[{"name":"cond","type":"uint64","internalType":"uint64"},{"name":"namespace","type":"string","internalType":"string"}],"outputs":[{"name":"dataRecords","type":"tuple[]","internalType":"struct Suave.DataRecord[]","components":[{"name":"id","type":"bytes16","internalType":"struct Suave.DataId"},{"name":"salt","type":"bytes16","internalType":"struct Suave.DataId"},{"name":"decryptionCondition","type":"uint64","internalType":"uint64"},{"name":"allowedPeekers","type":"address[]","internalType":"address[]"},{"name":"allowedStores","type":"address[]","internalType":"address[]"},{"name":"version","type":"string","internalType":"string"}]}]},{"type":"function","name":"fillMevShareBundle","inputs":[{"name":"dataId","type":"bytes16","internalType":"struct Suave.DataId"}],"outputs":[{"name":"encodedBundle","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"getInsecureTime","outputs":[{"name":"time","type":"uint256","internalType":"uint256"}]},{"type":"function","name":"newBuilder","outputs":[{"name":"sessionid","type":"string","internalType":"string"}]},{"type":"function","name":"newDataRecord","inputs":[{"name":"decryptionCondition","type":"uint64","internalType":"uint64"},{"name":"allowedPeekers","type":"address[]","internalType":"address[]"},{"name":"allowedStores","type":"address[]","internalType":"address[]"},{"name":"dataType","type":"string","internalType":"string"}],"outputs":[{"name":"dataRecord","type":"tuple","internalType":"struct Suave.DataRecord","components":[{"name":"id","type":"bytes16","internalType":"struct Suave.DataId"},{"name":"salt","type":"bytes16","internalType":"struct Suave.DataId"},{"name":"decryptionCondition","type":"uint64","internalType":"uint64"},{"name":"allowedPeekers","type":"address[]","internalType":"address[]"},{"name":"allowedStores","type":"address[]","internalType":"address[]"},{"name":"version","type":"string","internalType":"string"}]}]},{"type":"function","name":"privateKeyGen","inputs":[{"name":"crypto","type":"uint8","internalType":"struct Suave.CryptoSignature"}],"outputs":[{"name":"privateKey","type":"string","internalType":"string"}]},{"type":"function","name":"randomBytes","inputs":[{"name":"numBytes","type":"uint8","internalType":"uint8"}],"outputs":[{"name":"value","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"signEthTransaction","inputs":[{"name":"txn","type":"bytes","internalType":"bytes"},{"name":"chainId","type":"string","internalType":"string"},{"name":"signingKey","type":"string","internalType":"string"}],"outputs":[{"name":"signedTxn","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"signMessage","inputs":[{"name":"digest","type":"bytes","internalType":"bytes"},{"name":"crypto","type":"uint8","internalType":"struct Suave.CryptoSignature"},{"name":"signingKey","type":"string","internalType":"string"}],"outputs":[{"name":"signature","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"simulateBundle","inputs":[{"name":"bundleData","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"effectiveGasPrice","type":"uint64","internalType":"uint64"}]},{"type":"function","name":"simulateTransaction","inputs":[{"name":"sessionid","type":"string","internalType":"string"},{"name":"txn","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"simulationResult","type":"tuple","internalType":"struct Suave.SimulateTransactionResult","components":[{"name":"egp","type":"uint64","internalType":"uint64"},{"name":"logs","type":"tuple[]","internalType":"struct Suave.SimulatedLog[]","components":[{"name":"data","type":"bytes","internalType":"bytes"},{"name":"addr","type":"address","internalType":"address"},{"name":"topics","type":"bytes32[]","internalType":"bytes32[]"}]},{"name":"success","type":"bool","internalType":"bool"},{"name":"error","type":"string","internalType":"string"}]}]},{"type":"function","name":"submitBundleJsonRPC","inputs":[{"name":"url","type":"string","internalType":"string"},{"name":"method","type":"string","internalType":"string"},{"name":"params","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"errorMessage","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"submitEthBlockToRelay","inputs":[{"name":"relayUrl","type":"string","internalType":"string"},{"name":"builderBid","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"blockBid","type":"bytes","internalType":"bytes"}]}] \ No newline at end of file diff --git a/suave/artifacts/addresses.go b/suave/artifacts/addresses.go index 526f151448..5081eefbb4 100644 --- a/suave/artifacts/addresses.go +++ b/suave/artifacts/addresses.go @@ -1,5 +1,5 @@ // Code generated by suave/gen. DO NOT EDIT. -// Hash: 0c3427c1ad6242fe36606599fbb5d69d17fab52cab79a5350be8055daae46338 +// Hash: 93d0334f831efa37bf623305bc1fadbb86200c71f9e3c4a97337f7879f4216c1 package artifacts import ( @@ -17,6 +17,7 @@ var ( confidentialStoreAddr = common.HexToAddress("0x0000000000000000000000000000000042020000") contextGetAddr = common.HexToAddress("0x0000000000000000000000000000000053300003") doHTTPRequestAddr = common.HexToAddress("0x0000000000000000000000000000000043200002") + doHTTPRequest2Addr = common.HexToAddress("0x0000000000000000000000000000000043200003") ethcallAddr = common.HexToAddress("0x0000000000000000000000000000000042100003") extractHintAddr = common.HexToAddress("0x0000000000000000000000000000000042100037") fetchDataRecordsAddr = common.HexToAddress("0x0000000000000000000000000000000042030001") @@ -44,6 +45,7 @@ var SuaveMethods = map[string]common.Address{ "confidentialStore": confidentialStoreAddr, "contextGet": contextGetAddr, "doHTTPRequest": doHTTPRequestAddr, + "doHTTPRequest2": doHTTPRequest2Addr, "ethcall": ethcallAddr, "extractHint": extractHintAddr, "fetchDataRecords": fetchDataRecordsAddr, @@ -81,6 +83,8 @@ func PrecompileAddressToName(addr common.Address) string { return "contextGet" case doHTTPRequestAddr: return "doHTTPRequest" + case doHTTPRequest2Addr: + return "doHTTPRequest2" case ethcallAddr: return "ethcall" case extractHintAddr: diff --git a/suave/artifacts/build-info/a7b739ab8239814d5bc9f53cf4bf4268.json b/suave/artifacts/build-info/a7b739ab8239814d5bc9f53cf4bf4268.json new file mode 100644 index 0000000000..0da007c9da --- /dev/null +++ b/suave/artifacts/build-info/a7b739ab8239814d5bc9f53cf4bf4268.json @@ -0,0 +1 @@ +{"id":"a7b739ab8239814d5bc9f53cf4bf4268","source_id_to_path":{"0":"lib/forge-std/src/console2.sol","1":"sol/libraries/Suave.sol","2":"sol/standard_peekers/bundles.sol","3":"sol/standard_peekers/example.sol"},"language":"Solidity"} \ No newline at end of file diff --git a/suave/e2e/workflow_test.go b/suave/e2e/workflow_test.go index 6e0f27ffb0..7984c7416c 100644 --- a/suave/e2e/workflow_test.go +++ b/suave/e2e/workflow_test.go @@ -1191,7 +1191,7 @@ func TestE2EConsoleLog(t *testing.T) { require.NoError(t, err) } -func TestE2ERemoteCalls(t *testing.T) { +func TestE2ERemoteCalls_XXX(t *testing.T) { fr := newFramework(t, WithWhitelist([]string{"127.0.0.1"})) defer fr.Close() @@ -1246,6 +1246,23 @@ func TestE2ERemoteCalls(t *testing.T) { _, err := contract.SendTransaction("remoteCall", []interface{}{req}, nil) require.Error(t, err) }) + + t.Run("Status code", func(t *testing.T) { + srvAddr := fr.testHttpRelayer(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(301) + }) + + req := &types.HttpRequest{ + Method: "POST", + Url: srvAddr, + Body: []byte{0x1}, + } + _, err := contract.SendTransaction("remoteCall2", []interface{}{req, uint64(301)}, nil) + require.NoError(t, err) + + _, err = contract.SendTransaction("remoteCall2", []interface{}{req, uint64(302)}, nil) + require.Error(t, err) + }) } func TestE2ERemoteCallsWithDns(t *testing.T) { diff --git a/suave/gen/main.go b/suave/gen/main.go index c9f61684c8..b6856a3414 100644 --- a/suave/gen/main.go +++ b/suave/gen/main.go @@ -269,8 +269,11 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/suave/artifacts" "github.com/mitchellh/mapstructure" + "math/big" ) +var _ = new(big.Int) + var ( errFailedToUnpackInput = fmt.Errorf("failed to decode input") errFailedToDecodeField = fmt.Errorf("failed to decode field") diff --git a/suave/gen/suave_spec.yaml b/suave/gen/suave_spec.yaml index bbb251b419..6f453da77d 100644 --- a/suave/gen/suave_spec.yaml +++ b/suave/gen/suave_spec.yaml @@ -98,6 +98,18 @@ structs: - name: timeout description: "Timeout of the request in milliseconds" type: uint64 + - name: HttpResponse + description: "Description of an HTTP response." + fields: + - name: status + description: "HTTP status code of the response" + type: uint64 + - name: body + description: "Body of the response" + type: bytes + - name: error + description: "Error message if any" + type: bytes - name: SimulateTransactionResult description: "Result of a simulated transaction." fields: @@ -386,6 +398,18 @@ functions: - name: httpResponse type: bytes description: "Body of the response" + - name: doHTTPRequest2 + address: "0x0000000000000000000000000000000043200003" + description: "Performs an HTTP request and returns the response. `request` is the request to perform." + input: + - name: request + type: HttpRequest + description: "Request to perform" + output: + fields: + - name: httpResponse + type: HttpResponse + description: "Response of the request" - name: newBuilder address: "0x0000000000000000000000000000000053200001" description: "Initializes a new remote builder session" diff --git a/suave/lib/suave-std b/suave/lib/suave-std new file mode 160000 index 0000000000..f7c2f02a09 --- /dev/null +++ b/suave/lib/suave-std @@ -0,0 +1 @@ +Subproject commit f7c2f02a0914379c6ea51e2bb24cde26917a150c diff --git a/suave/sol/libraries/Suave.sol b/suave/sol/libraries/Suave.sol index 15fe9a2e85..b6afaf5e42 100644 --- a/suave/sol/libraries/Suave.sol +++ b/suave/sol/libraries/Suave.sol @@ -70,6 +70,16 @@ library Suave { uint64 timeout; } + /// @notice Description of an HTTP response. + /// @param status HTTP status code of the response + /// @param body Body of the response + /// @param error Error message if any + struct HttpResponse { + uint64 status; + bytes body; + bytes error; + } + /// @notice Result of a simulated transaction. /// @param egp Effective Gas Price of the transaction /// @param logs Logs emitted during the simulation @@ -126,6 +136,8 @@ library Suave { address public constant DO_HTTPREQUEST = 0x0000000000000000000000000000000043200002; + address public constant DO_HTTPREQUEST2 = 0x0000000000000000000000000000000043200003; + address public constant ETHCALL = 0x0000000000000000000000000000000042100003; address public constant EXTRACT_HINT = 0x0000000000000000000000000000000042100037; @@ -296,6 +308,18 @@ library Suave { return abi.decode(data, (bytes)); } + /// @notice Performs an HTTP request and returns the response. `request` is the request to perform. + /// @param request Request to perform + /// @return httpResponse Response of the request + function doHTTPRequest2(HttpRequest memory request) internal returns (HttpResponse memory) { + (bool success, bytes memory data) = DO_HTTPREQUEST2.call(abi.encode(request)); + if (!success) { + revert PeekerReverted(DO_HTTPREQUEST2, data); + } + + return abi.decode(data, (HttpResponse)); + } + /// @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 @@ -348,8 +372,8 @@ library Suave { return data; } - /// @notice Returns the current Kettle Unix time in seconds. - /// @return time Current Unix time in seconds + /// @notice Returns the current Kettle Unix time in milliseconds. Insecure because it assumes trust in Kettle's clock. + /// @return time Current Unix time in milliseconds function getInsecureTime() internal returns (uint256) { (bool success, bytes memory data) = GET_INSECURE_TIME.call(abi.encode()); if (!success) { diff --git a/suave/sol/standard_peekers/example.sol b/suave/sol/standard_peekers/example.sol index bbe8cfca6c..6e1ffe3d90 100644 --- a/suave/sol/standard_peekers/example.sol +++ b/suave/sol/standard_peekers/example.sol @@ -24,6 +24,11 @@ contract ExampleEthCallSource { Suave.doHTTPRequest(request); } + function remoteCall2(Suave.HttpRequest memory request, uint64 expectedStatus) public { + Suave.HttpResponse memory response = Suave.doHTTPRequest2(request); + require(response.status == expectedStatus); + } + function emptyCallback() public payable {} function sessionE2ETest(bytes memory subTxn, bytes memory subTxn2) public payable returns (bytes memory) { From 8c7626cc712cacf0fcdefa1b0d519624619976d3 Mon Sep 17 00:00:00 2001 From: Ferran Borreguero Date: Wed, 4 Dec 2024 09:12:23 +0000 Subject: [PATCH 2/2] Update foundry --- .../artifacts/build-info/042632033115408fe18db1103f7f7c21.json | 1 + suave/lib/forge-std | 2 +- suave/lib/suave-std | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 suave/artifacts/build-info/042632033115408fe18db1103f7f7c21.json delete mode 160000 suave/lib/suave-std diff --git a/suave/artifacts/build-info/042632033115408fe18db1103f7f7c21.json b/suave/artifacts/build-info/042632033115408fe18db1103f7f7c21.json new file mode 100644 index 0000000000..1d8a81f371 --- /dev/null +++ b/suave/artifacts/build-info/042632033115408fe18db1103f7f7c21.json @@ -0,0 +1 @@ +{"id":"042632033115408fe18db1103f7f7c21","source_id_to_path":{"0":"lib/forge-std/src/console2.sol","1":"sol/libraries/Suave.sol","2":"sol/standard_peekers/bundles.sol","3":"sol/standard_peekers/example.sol"},"language":"Solidity"} \ No newline at end of file diff --git a/suave/lib/forge-std b/suave/lib/forge-std index dcb0d52bc4..f73c73d201 160000 --- a/suave/lib/forge-std +++ b/suave/lib/forge-std @@ -1 +1 @@ -Subproject commit dcb0d52bc4399d37a6545848e3b8f9d03c77b98d +Subproject commit f73c73d2018eb6a111f35e4dae7b4f27401e9421 diff --git a/suave/lib/suave-std b/suave/lib/suave-std deleted file mode 160000 index f7c2f02a09..0000000000 --- a/suave/lib/suave-std +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f7c2f02a0914379c6ea51e2bb24cde26917a150c