From 6383b8ea2afb14e8878631d5fc949dadeb115ff7 Mon Sep 17 00:00:00 2001 From: dmarz Date: Fri, 22 Dec 2023 12:12:40 -0500 Subject: [PATCH] add signMessage precompile (#127) * add signMessage precompile * precompile implementation * chore: remove http.port flag in dev mode (#125) * Fix console cmd for suave devnet (#122) * err on suave precompile is type ErrExecutionReverted (#129) * err on suave precompile is type ErrExecutionReverted * restrict go version to 1.21.1 * Add PeekerReverted error to Suave abi (#130) * Add PeekerReverted error to Suave abi * Commit real one * Add precompile to make remote http calls (#110) * Add precompile to make remote http calls * Fix error * Handle error and integrate other external calls * Last fixes * Fix * Show more diffs * Fix test * Validate for duplicated names/addrs in precompile spec (#132) * bid -> dataRecord (#128) * bid -> dataRecord * working renames * change FetchBid to FetchRecord * update vm contracts * update devenv mevshare script * the last references of bid * the LAST improper bid reference * Add format to Solidity --------- Co-authored-by: Ferran Borreguero * Add console2 log support (#126) * Add console log support * Fix lint * [fix] fix bootnode's address (#134) * add signMessage precompile * precompile implementation * update artifacts * fix bad merge * cherrypick and update libs * regen --------- Co-authored-by: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Co-authored-by: Ferran Borreguero Co-authored-by: halo3mic <46010359+halo3mic@users.noreply.github.com> Co-authored-by: Ferran Borreguero Co-authored-by: Anton Co-authored-by: Andy Tudhope <13001517+andytudhope@users.noreply.github.com> --- core/types/suave_structs.go | 2 +- core/vm/contracts_suave.go | 15 ++++ core/vm/contracts_suave_runtime_adapter.go | 81 ++++++++++++++----- .../contracts_suave_runtime_adapter_test.go | 4 + suave/artifacts/SuaveLib.json | 2 +- suave/artifacts/addresses.go | 62 +++++++------- suave/e2e/workflow_test.go | 40 +++++++++ suave/gen/suave_spec.yaml | 12 +++ suave/sol/libraries/Suave.sol | 17 +++- suave/sol/libraries/SuaveForge.sol | 11 ++- 10 files changed, 191 insertions(+), 55 deletions(-) diff --git a/core/types/suave_structs.go b/core/types/suave_structs.go index 718de0317..132f5ef81 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: 80e00d3e46ad61ee6925143b317f91c0ad551d66908d0ab20e244db63b40ff40 +// Hash: 1e4312debc072d2aa68e9aa2ddf7ef264f705d23d86a1aea0966c3751b36d318 package types import "github.com/ethereum/go-ethereum/common" diff --git a/core/vm/contracts_suave.go b/core/vm/contracts_suave.go index 33350109c..d8a9a956e 100644 --- a/core/vm/contracts_suave.go +++ b/core/vm/contracts_suave.go @@ -12,6 +12,7 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/suave/consolelog" @@ -115,6 +116,20 @@ func (b *suaveRuntime) fetchDataRecords(targetBlock uint64, namespace string) ([ return records, nil } +func (s *suaveRuntime) signMessage(digest []byte, signingKey string) ([]byte, error) { + key, err := crypto.HexToECDSA(signingKey) + if err != nil { + return nil, fmt.Errorf("key not formatted properly: %w", err) + } + + signature, err := crypto.Sign(digest, key) + if err != nil { + return nil, fmt.Errorf("Failed to sign message: %v", err) + } + + return signature, nil +} + func mustParseAbi(data string) abi.ABI { inoutAbi, err := abi.JSON(strings.NewReader(data)) if err != nil { diff --git a/core/vm/contracts_suave_runtime_adapter.go b/core/vm/contracts_suave_runtime_adapter.go index 658b43441..5410a7f7a 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: 80e00d3e46ad61ee6925143b317f91c0ad551d66908d0ab20e244db63b40ff40 +// Hash: 1e4312debc072d2aa68e9aa2ddf7ef264f705d23d86a1aea0966c3751b36d318 package vm import ( @@ -28,30 +28,32 @@ type SuaveRuntime interface { fillMevShareBundle(dataId types.DataId) ([]byte, error) newDataRecord(decryptionCondition uint64, allowedPeekers []common.Address, allowedStores []common.Address, dataType string) (types.DataRecord, error) signEthTransaction(txn []byte, chainId string, signingKey string) ([]byte, error) + signMessage(digest []byte, signingKey string) ([]byte, error) simulateBundle(bundleData []byte) (uint64, error) submitBundleJsonRPC(url string, method string, params []byte) ([]byte, error) submitEthBlockToRelay(relayUrl string, builderBid []byte) ([]byte, error) } var ( - buildEthBlockAddr = common.HexToAddress("0x0000000000000000000000000000000042100001") - confidentialInputsAddr = common.HexToAddress("0x0000000000000000000000000000000042010001") - confidentialRetrieveAddr = common.HexToAddress("0x0000000000000000000000000000000042020001") - confidentialStoreAddr = common.HexToAddress("0x0000000000000000000000000000000042020000") - doHTTPRequestAddr = common.HexToAddress("0x0000000000000000000000000000000043200002") - ethcallAddr = common.HexToAddress("0x0000000000000000000000000000000042100003") - extractHintAddr = common.HexToAddress("0x0000000000000000000000000000000042100037") - fetchDataRecordsAddr = common.HexToAddress("0x0000000000000000000000000000000042030001") - fillMevShareBundleAddr = common.HexToAddress("0x0000000000000000000000000000000043200001") - newDataRecordAddr = common.HexToAddress("0x0000000000000000000000000000000042030000") - signEthTransactionAddr = common.HexToAddress("0x0000000000000000000000000000000040100001") - simulateBundleAddr = common.HexToAddress("0x0000000000000000000000000000000042100000") - submitBundleJsonRPCAddr = common.HexToAddress("0x0000000000000000000000000000000043000001") + buildEthBlockAddr = common.HexToAddress("0x0000000000000000000000000000000042100001") + confidentialInputsAddr = common.HexToAddress("0x0000000000000000000000000000000042010001") + confidentialRetrieveAddr = common.HexToAddress("0x0000000000000000000000000000000042020001") + confidentialStoreAddr = common.HexToAddress("0x0000000000000000000000000000000042020000") + doHTTPRequestAddr = common.HexToAddress("0x0000000000000000000000000000000043200002") + ethcallAddr = common.HexToAddress("0x0000000000000000000000000000000042100003") + extractHintAddr = common.HexToAddress("0x0000000000000000000000000000000042100037") + fetchDataRecordsAddr = common.HexToAddress("0x0000000000000000000000000000000042030001") + fillMevShareBundleAddr = common.HexToAddress("0x0000000000000000000000000000000043200001") + newDataRecordAddr = common.HexToAddress("0x0000000000000000000000000000000042030000") + signEthTransactionAddr = common.HexToAddress("0x0000000000000000000000000000000040100001") + signMessageAddr = common.HexToAddress("0x0000000000000000000000000000000040100003") + simulateBundleAddr = common.HexToAddress("0x0000000000000000000000000000000042100000") + submitBundleJsonRPCAddr = common.HexToAddress("0x0000000000000000000000000000000043000001") submitEthBlockToRelayAddr = common.HexToAddress("0x0000000000000000000000000000000042100002") ) var addrList = []common.Address{ - buildEthBlockAddr, confidentialInputsAddr, confidentialRetrieveAddr, confidentialStoreAddr, doHTTPRequestAddr, ethcallAddr, extractHintAddr, fetchDataRecordsAddr, fillMevShareBundleAddr, newDataRecordAddr, signEthTransactionAddr, simulateBundleAddr, submitBundleJsonRPCAddr, submitEthBlockToRelayAddr, + buildEthBlockAddr, confidentialInputsAddr, confidentialRetrieveAddr, confidentialStoreAddr, doHTTPRequestAddr, ethcallAddr, extractHintAddr, fetchDataRecordsAddr, fillMevShareBundleAddr, newDataRecordAddr, signEthTransactionAddr, signMessageAddr, simulateBundleAddr, submitBundleJsonRPCAddr, submitEthBlockToRelayAddr, } type SuaveRuntimeAdapter struct { @@ -93,6 +95,9 @@ func (b *SuaveRuntimeAdapter) run(addr common.Address, input []byte) ([]byte, er case signEthTransactionAddr: return b.signEthTransaction(input) + case signMessageAddr: + return b.signMessage(input) + case simulateBundleAddr: return b.simulateBundle(input) @@ -545,6 +550,46 @@ func (b *SuaveRuntimeAdapter) signEthTransaction(input []byte) (res []byte, err } +func (b *SuaveRuntimeAdapter) signMessage(input []byte) (res []byte, err error) { + var ( + unpacked []interface{} + result []byte + ) + + _ = unpacked + _ = result + + unpacked, err = artifacts.SuaveAbi.Methods["signMessage"].Inputs.Unpack(input) + if err != nil { + err = errFailedToUnpackInput + return + } + + var ( + digest []byte + signingKey string + ) + + digest = unpacked[0].([]byte) + signingKey = unpacked[1].(string) + + var ( + output1 []byte + ) + + if output1, err = b.impl.signMessage(digest, signingKey); err != nil { + return + } + + result, err = artifacts.SuaveAbi.Methods["signMessage"].Outputs.Pack(output1) + if err != nil { + err = errFailedToPackOutput + return + } + return result, nil + +} + func (b *SuaveRuntimeAdapter) simulateBundle(input []byte) (res []byte, err error) { var ( unpacked []interface{} @@ -638,17 +683,17 @@ func (b *SuaveRuntimeAdapter) submitEthBlockToRelay(input []byte) (res []byte, e var ( relayUrl string - builderDataRecord []byte + builderBid []byte ) relayUrl = unpacked[0].(string) - builderDataRecord = unpacked[1].([]byte) + builderBid = unpacked[1].([]byte) var ( output1 []byte ) - if output1, err = b.impl.submitEthBlockToRelay(relayUrl, builderDataRecord); err != nil { + if output1, err = b.impl.submitEthBlockToRelay(relayUrl, builderBid); err != nil { return } diff --git a/core/vm/contracts_suave_runtime_adapter_test.go b/core/vm/contracts_suave_runtime_adapter_test.go index 0f0a8b26c..a166e176c 100644 --- a/core/vm/contracts_suave_runtime_adapter_test.go +++ b/core/vm/contracts_suave_runtime_adapter_test.go @@ -55,6 +55,10 @@ func (m *mockRuntime) signEthTransaction(txn []byte, chainId string, signingKey return []byte{0x1}, nil } +func (m *mockRuntime) signMessage(digest []byte, signingKey string) ([]byte, error) { + return []byte{0x1}, nil +} + func (m *mockRuntime) simulateBundle(bundleData []byte) (uint64, error) { return 1, nil } diff --git a/suave/artifacts/SuaveLib.json b/suave/artifacts/SuaveLib.json index 8c2e3b579..853935c32 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":"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":"dataId","type":"bytes16","internalType":"struct Suave.DataId"},{"name":"namespace","type":"string","internalType":"string"}],"outputs":[{"name":"output1","type":"bytes","internalType":"bytes"},{"name":"output2","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"confidentialInputs","outputs":[{"name":"output1","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":"output1","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"confidentialStore","inputs":[{"name":"dataId","type":"bytes16","internalType":"struct Suave.DataId"},{"name":"key","type":"string","internalType":"string"},{"name":"data1","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"}]}],"outputs":[{"name":"response","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"ethcall","inputs":[{"name":"contractAddr","type":"address","internalType":"address"},{"name":"input1","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"output1","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"extractHint","inputs":[{"name":"bundleData","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"output1","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":"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":"signEthTransaction","inputs":[{"name":"txn","type":"bytes","internalType":"bytes"},{"name":"chainId","type":"string","internalType":"string"},{"name":"signingKey","type":"string","internalType":"string"}],"outputs":[{"name":"output1","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"simulateBundle","inputs":[{"name":"bundleData","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"output1","type":"uint64","internalType":"uint64"}]},{"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":"output1","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"submitEthBlockToRelay","inputs":[{"name":"relayUrl","type":"string","internalType":"string"},{"name":"builderBid","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"output1","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":"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":"dataId","type":"bytes16","internalType":"struct Suave.DataId"},{"name":"namespace","type":"string","internalType":"string"}],"outputs":[{"name":"output1","type":"bytes","internalType":"bytes"},{"name":"output2","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"confidentialInputs","outputs":[{"name":"output1","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":"output1","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"confidentialStore","inputs":[{"name":"dataId","type":"bytes16","internalType":"struct Suave.DataId"},{"name":"key","type":"string","internalType":"string"},{"name":"data1","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"}]}],"outputs":[{"name":"response","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"ethcall","inputs":[{"name":"contractAddr","type":"address","internalType":"address"},{"name":"input1","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"output1","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"extractHint","inputs":[{"name":"bundleData","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"output1","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":"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":"signEthTransaction","inputs":[{"name":"txn","type":"bytes","internalType":"bytes"},{"name":"chainId","type":"string","internalType":"string"},{"name":"signingKey","type":"string","internalType":"string"}],"outputs":[{"name":"output1","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"signMessage","inputs":[{"name":"digest","type":"bytes","internalType":"bytes"},{"name":"signingKey","type":"string","internalType":"string"}],"outputs":[{"name":"output1","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"simulateBundle","inputs":[{"name":"bundleData","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"output1","type":"uint64","internalType":"uint64"}]},{"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":"output1","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"submitEthBlockToRelay","inputs":[{"name":"relayUrl","type":"string","internalType":"string"},{"name":"builderBid","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"output1","type":"bytes","internalType":"bytes"}]}] \ No newline at end of file diff --git a/suave/artifacts/addresses.go b/suave/artifacts/addresses.go index 28d9038e5..409170744 100644 --- a/suave/artifacts/addresses.go +++ b/suave/artifacts/addresses.go @@ -1,5 +1,5 @@ // Code generated by suave/gen. DO NOT EDIT. -// Hash: 80e00d3e46ad61ee6925143b317f91c0ad551d66908d0ab20e244db63b40ff40 +// Hash: 1e4312debc072d2aa68e9aa2ddf7ef264f705d23d86a1aea0966c3751b36d318 package artifacts import ( @@ -8,37 +8,39 @@ import ( // List of suave precompile addresses var ( - buildEthBlockAddr = common.HexToAddress("0x0000000000000000000000000000000042100001") - confidentialInputsAddr = common.HexToAddress("0x0000000000000000000000000000000042010001") - confidentialRetrieveAddr = common.HexToAddress("0x0000000000000000000000000000000042020001") - confidentialStoreAddr = common.HexToAddress("0x0000000000000000000000000000000042020000") - doHTTPRequestAddr = common.HexToAddress("0x0000000000000000000000000000000043200002") - ethcallAddr = common.HexToAddress("0x0000000000000000000000000000000042100003") - extractHintAddr = common.HexToAddress("0x0000000000000000000000000000000042100037") - fetchDataRecordsAddr = common.HexToAddress("0x0000000000000000000000000000000042030001") - fillMevShareBundleAddr = common.HexToAddress("0x0000000000000000000000000000000043200001") - newDataRecordAddr = common.HexToAddress("0x0000000000000000000000000000000042030000") - signEthTransactionAddr = common.HexToAddress("0x0000000000000000000000000000000040100001") - simulateBundleAddr = common.HexToAddress("0x0000000000000000000000000000000042100000") - submitBundleJsonRPCAddr = common.HexToAddress("0x0000000000000000000000000000000043000001") - submitEthBlockToRelayAddr = common.HexToAddress("0x0000000000000000000000000000000042100002") + buildEthBlockAddr = common.HexToAddress("0x0000000000000000000000000000000042100001") + confidentialInputsAddr = common.HexToAddress("0x0000000000000000000000000000000042010001") + confidentialRetrieveAddr = common.HexToAddress("0x0000000000000000000000000000000042020001") + confidentialStoreAddr = common.HexToAddress("0x0000000000000000000000000000000042020000") + doHTTPRequestAddr = common.HexToAddress("0x0000000000000000000000000000000043200002") + ethcallAddr = common.HexToAddress("0x0000000000000000000000000000000042100003") + extractHintAddr = common.HexToAddress("0x0000000000000000000000000000000042100037") + fetchDataRecordsAddr = common.HexToAddress("0x0000000000000000000000000000000042030001") + fillMevShareBundleAddr = common.HexToAddress("0x0000000000000000000000000000000043200001") + newDataRecordAddr = common.HexToAddress("0x0000000000000000000000000000000042030000") + signEthTransactionAddr = common.HexToAddress("0x0000000000000000000000000000000040100001") + signMessageAddr = common.HexToAddress("0x0000000000000000000000000000000040100003") + simulateBundleAddr = common.HexToAddress("0x0000000000000000000000000000000042100000") + submitBundleJsonRPCAddr = common.HexToAddress("0x0000000000000000000000000000000043000001") + submitEthBlockToRelayAddr = common.HexToAddress("0x0000000000000000000000000000000042100002") ) var SuaveMethods = map[string]common.Address{ - "buildEthBlock": buildEthBlockAddr, - "confidentialInputs": confidentialInputsAddr, - "confidentialRetrieve": confidentialRetrieveAddr, - "confidentialStore": confidentialStoreAddr, - "doHTTPRequest": doHTTPRequestAddr, - "ethcall": ethcallAddr, - "extractHint": extractHintAddr, - "fetchDataRecords": fetchDataRecordsAddr, - "fillMevShareBundle": fillMevShareBundleAddr, - "newDataRecord": newDataRecordAddr, - "signEthTransaction": signEthTransactionAddr, - "simulateBundle": simulateBundleAddr, - "submitBundleJsonRPC": submitBundleJsonRPCAddr, - "submitEthBlockToRelay": submitEthBlockToRelayAddr, + "buildEthBlock": buildEthBlockAddr, + "confidentialInputs": confidentialInputsAddr, + "confidentialRetrieve": confidentialRetrieveAddr, + "confidentialStore": confidentialStoreAddr, + "doHTTPRequest": doHTTPRequestAddr, + "ethcall": ethcallAddr, + "extractHint": extractHintAddr, + "fetchDataRecords": fetchDataRecordsAddr, + "fillMevShareBundle": fillMevShareBundleAddr, + "newDataRecord": newDataRecordAddr, + "signEthTransaction": signEthTransactionAddr, + "signMessage": signMessageAddr, + "simulateBundle": simulateBundleAddr, + "submitBundleJsonRPC": submitBundleJsonRPCAddr, + "submitEthBlockToRelay": submitEthBlockToRelayAddr, } func PrecompileAddressToName(addr common.Address) string { @@ -65,6 +67,8 @@ func PrecompileAddressToName(addr common.Address) string { return "newDataRecord" case signEthTransactionAddr: return "signEthTransaction" + case signMessageAddr: + return "signMessage" case simulateBundleAddr: return "simulateBundle" case submitBundleJsonRPCAddr: diff --git a/suave/e2e/workflow_test.go b/suave/e2e/workflow_test.go index fc87f0ae8..1548b4830 100644 --- a/suave/e2e/workflow_test.go +++ b/suave/e2e/workflow_test.go @@ -276,6 +276,45 @@ func TestTxSigningPrecompile(t *testing.T) { require.Equal(t, crypto.PubkeyToAddress(sk.PublicKey), sender) } +func TestSignMessagePrecompile(t *testing.T) { + fr := newFramework(t) + defer fr.Close() + + // Prepare the message digest and generate a signing key + message := "Hello, world!" + digest := crypto.Keccak256([]byte(message)) + + sk, err := crypto.GenerateKey() + require.NoError(t, err) + skHex := hex.EncodeToString(crypto.FromECDSA(sk)) + + // function signMessage(bytes memory digest, string memory signingKey) + args, err := artifacts.SuaveAbi.Methods["signMessage"].Inputs.Pack(digest, skHex) + require.NoError(t, err) + + gas := hexutil.Uint64(1000000) + + var callResult hexutil.Bytes + err = fr.suethSrv.RPCNode().Call(&callResult, "eth_call", setTxArgsDefaults(ethapi.TransactionArgs{ + To: &signMessage, + Gas: &gas, + IsConfidential: true, + Data: (*hexutil.Bytes)(&args), + }), "latest") + requireNoRpcError(t, err) + + // Unpack the call result to get the signed message + unpackedCallResult, err := artifacts.SuaveAbi.Methods["signMessage"].Outputs.Unpack(callResult) + require.NoError(t, err) + + // Assert that recovered key is correct + signature := unpackedCallResult[0].([]byte) + pubKeyRecovered, err := crypto.SigToPub(digest, signature) + require.NoError(t, err) + + require.Equal(t, crypto.PubkeyToAddress(sk.PublicKey), crypto.PubkeyToAddress(*pubKeyRecovered)) +} + type HintEvent struct { BidId [16]uint8 `abi:"dataId"` Hint []byte `abi:"hint"` @@ -1410,6 +1449,7 @@ var ( fillMevShareBundleAddress = common.HexToAddress("0x43200001") signEthTransaction = common.HexToAddress("0x40100001") + signMessage = common.HexToAddress("0x40100003") simulateBundleAddress = common.HexToAddress("0x42100000") buildEthBlockAddress = common.HexToAddress("0x42100001") diff --git a/suave/gen/suave_spec.yaml b/suave/gen/suave_spec.yaml index 809308a56..623abdd85 100644 --- a/suave/gen/suave_spec.yaml +++ b/suave/gen/suave_spec.yaml @@ -209,6 +209,18 @@ functions: fields: - name: encodedBundle type: bytes + - name: signMessage + address: "0x0000000000000000000000000000000040100003" + isConfidential: true + input: + - name: digest + type: bytes + - name: signingKey + type: string + output: + fields: + - name: output1 + type: bytes - name: doHTTPRequest address: "0x0000000000000000000000000000000043200002" input: diff --git a/suave/sol/libraries/Suave.sol b/suave/sol/libraries/Suave.sol index e77a50cc3..f155bbc88 100644 --- a/suave/sol/libraries/Suave.sol +++ b/suave/sol/libraries/Suave.sol @@ -67,6 +67,8 @@ library Suave { address public constant SIGN_ETH_TRANSACTION = 0x0000000000000000000000000000000040100001; + address public constant SIGN_MESSAGE = 0x0000000000000000000000000000000040100003; + address public constant SIMULATE_BUNDLE = 0x0000000000000000000000000000000042100000; address public constant SUBMIT_BUNDLE_JSON_RPC = 0x0000000000000000000000000000000043000001; @@ -200,6 +202,16 @@ library Suave { return abi.decode(data, (bytes)); } + function signMessage(bytes memory digest, string memory signingKey) internal view returns (bytes memory) { + require(isConfidential()); + (bool success, bytes memory data) = SIGN_MESSAGE.staticcall(abi.encode(digest, signingKey)); + if (!success) { + revert PeekerReverted(SIGN_MESSAGE, data); + } + + return abi.decode(data, (bytes)); + } + function simulateBundle(bytes memory bundleData) internal view returns (uint64) { (bool success, bytes memory data) = SIMULATE_BUNDLE.staticcall(abi.encode(bundleData)); if (!success) { @@ -223,14 +235,13 @@ library Suave { return data; } - function submitEthBlockToRelay(string memory relayUrl, bytes memory builderDataRecord) + function submitEthBlockToRelay(string memory relayUrl, bytes memory builderBid) internal view returns (bytes memory) { require(isConfidential()); - (bool success, bytes memory data) = - SUBMIT_ETH_BLOCK_TO_RELAY.staticcall(abi.encode(relayUrl, builderDataRecord)); + (bool success, bytes memory data) = SUBMIT_ETH_BLOCK_TO_RELAY.staticcall(abi.encode(relayUrl, builderBid)); if (!success) { revert PeekerReverted(SUBMIT_ETH_BLOCK_TO_RELAY, data); } diff --git a/suave/sol/libraries/SuaveForge.sol b/suave/sol/libraries/SuaveForge.sol index f0e45e9df..4f8602649 100644 --- a/suave/sol/libraries/SuaveForge.sol +++ b/suave/sol/libraries/SuaveForge.sol @@ -117,6 +117,12 @@ library SuaveForge { 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)); @@ -133,13 +139,12 @@ library SuaveForge { return data; } - function submitEthBlockToRelay(string memory relayUrl, bytes memory builderDataRecord) + function submitEthBlockToRelay(string memory relayUrl, bytes memory builderBid) internal view returns (bytes memory) { - bytes memory data = - forgeIt("0x0000000000000000000000000000000042100002", abi.encode(relayUrl, builderDataRecord)); + bytes memory data = forgeIt("0x0000000000000000000000000000000042100002", abi.encode(relayUrl, builderBid)); return data; }