diff --git a/core/types/suave_structs.go b/core/types/suave_structs.go index 606bb30b76..250af51a2c 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: 5f7dc4aaca73e07546a16000edda8fa3b804777ed38fea2121bc0e09b889103e +// Hash: 23a6dd8b9b224b11b8baea19a80c55c7787c50c0eb2fc69727e66d615c913483 package types import "github.com/ethereum/go-ethereum/common" diff --git a/core/vm/contracts.go b/core/vm/contracts.go index abdf49cd64..7e1de370fd 100644 --- a/core/vm/contracts.go +++ b/core/vm/contracts.go @@ -87,24 +87,26 @@ var PrecompiledContractsIstanbul = map[common.Address]PrecompiledContract{ // contracts used in the suave testnet. It's a superset of Berlin precompiles. // Confidential contracts (implementing SuavePrecompiledContract) // are ran with their respective RunConfidential in confidential setting +// TODO: should be autogenerated var PrecompiledContractsSuave = map[common.Address]SuavePrecompiledContract{ isConfidentialAddress: &isConfidentialPrecompile{}, confidentialInputsAddress: &confidentialInputsPrecompile{}, - confStoreStoreAddress: newConfStoreStore(), - confStoreRetrieveAddress: newConfStoreRetrieve(), + confStoreStoreAddress: &stubPrecompile{staticRequiredGas(1000)}, + confStoreRetrieveAddress: &stubPrecompile{staticRequiredGas(1000)}, - newBidAddress: newNewBid(), - fetchBidsAddress: newFetchBids(), - extractHintAddress: &extractHint{}, + newBidAddress: &stubPrecompile{staticRequiredGas(1000)}, + fetchBidsAddress: &stubPrecompile{staticRequiredGas(1000)}, + extractHintAddress: &stubPrecompile{staticRequiredGas(1000)}, - simulateBundleAddress: &simulateBundle{}, - buildEthBlockAddress: &buildEthBlock{}, - submitEthBlockBidToRelayAddress: &submitEthBlockBidToRelay{}, - submitBundleJsonRPCAddress: &submitBundleJsonRPC{}, - fillMevShareBundleAddress: &fillMevShareBundle{}, + signEthTransactionAddress: &stubPrecompile{staticRequiredGas(1000)}, + simulateBundleAddress: &stubPrecompile{staticRequiredGas(1000)}, + buildEthBlockAddress: &stubPrecompile{staticRequiredGas(1000)}, + submitEthBlockBidToRelayAddress: &stubPrecompile{staticRequiredGas(1000)}, + submitBundleJsonRPCAddress: &stubPrecompile{staticRequiredGas(1000)}, + fillMevShareBundleAddress: &stubPrecompile{staticRequiredGas(1000)}, - ethcallAddr: ðCallPrecompile{}, + ethcallAddr: &stubPrecompile{staticRequiredGas(1000)}, } // PrecompiledContractsBerlin contains the default set of pre-compiled Ethereum diff --git a/core/vm/contracts_suave.go b/core/vm/contracts_suave.go index 2fc89a37a5..29f01a82ee 100644 --- a/core/vm/contracts_suave.go +++ b/core/vm/contracts_suave.go @@ -3,13 +3,10 @@ package vm import ( "errors" "fmt" - "strings" - "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/metrics" - "github.com/ethereum/go-ethereum/suave/artifacts" suave "github.com/ethereum/go-ethereum/suave/core" ) @@ -79,45 +76,7 @@ func (c *confidentialInputsPrecompile) RunConfidential(suaveContext *SuaveContex /* Confidential store precompiles */ -type confStoreStore struct { - inoutAbi abi.Method -} - -func newConfStoreStore() *confStoreStore { - inoutAbi := mustParseMethodAbi(`[{"inputs":[{"type":"bytes16"}, {"type":"bytes16"}, {"type":"string"}, {"type":"bytes"}],"name":"store","outputs":[],"stateMutability":"nonpayable","type":"function"}]`, "store") - - return &confStoreStore{inoutAbi} -} - -func (c *confStoreStore) RequiredGas(input []byte) uint64 { - return uint64(100 * len(input)) -} - -func (c *confStoreStore) Run(input []byte) ([]byte, error) { - return nil, errors.New("not available in this suaveContext") -} - -func (c *confStoreStore) RunConfidential(suaveContext *SuaveContext, input []byte) ([]byte, error) { - if len(suaveContext.CallerStack) == 0 { - return []byte("not allowed"), errors.New("not allowed in this suaveContext") - } - - unpacked, err := c.inoutAbi.Inputs.Unpack(input) - if err != nil { - return []byte(err.Error()), err - } - - bidId := unpacked[0].(types.BidId) - key := unpacked[1].(string) - data := unpacked[2].([]byte) - - if err := c.runImpl(suaveContext, bidId, key, data); err != nil { - return []byte(err.Error()), err - } - return nil, nil -} - -func (c *confStoreStore) runImpl(suaveContext *SuaveContext, bidId suave.BidId, key string, data []byte) error { +func confStoreStoreImpl(suaveContext *SuaveContext, bidId suave.BidId, key string, data []byte) error { bid, err := suaveContext.Backend.ConfidentialStore.FetchBidById(bidId) if err != nil { return suave.ErrBidNotFound @@ -140,37 +99,7 @@ func (c *confStoreStore) runImpl(suaveContext *SuaveContext, bidId suave.BidId, return nil } -type confStoreRetrieve struct{} - -func newConfStoreRetrieve() *confStoreRetrieve { - return &confStoreRetrieve{} -} - -func (c *confStoreRetrieve) RequiredGas(input []byte) uint64 { - return 100 -} - -func (c *confStoreRetrieve) Run(input []byte) ([]byte, error) { - return nil, errors.New("not available in this suaveContext") -} - -func (c *confStoreRetrieve) RunConfidential(suaveContext *SuaveContext, input []byte) ([]byte, error) { - if len(suaveContext.CallerStack) == 0 { - return []byte("not allowed"), errors.New("not allowed in this suaveContext") - } - - unpacked, err := artifacts.SuaveAbi.Methods["retrieve"].Inputs.Unpack(input) - if err != nil { - return []byte(err.Error()), err - } - - bidId := unpacked[0].(suave.BidId) - key := unpacked[1].(string) - - return c.runImpl(suaveContext, bidId, key) -} - -func (c *confStoreRetrieve) runImpl(suaveContext *SuaveContext, bidId suave.BidId, key string) ([]byte, error) { +func confStoreRetrieveImpl(suaveContext *SuaveContext, bidId suave.BidId, key string) ([]byte, error) { bid, err := suaveContext.Backend.ConfidentialStore.FetchBidById(bidId) if err != nil { return nil, suave.ErrBidNotFound @@ -195,43 +124,7 @@ func (c *confStoreRetrieve) runImpl(suaveContext *SuaveContext, bidId suave.BidI /* Bid precompiles */ -type newBid struct { - inoutAbi abi.Method -} - -func newNewBid() *newBid { - inoutAbi := mustParseMethodAbi(`[{ "inputs": [ { "internalType": "uint64", "name": "decryptionCondition", "type": "uint64" }, { "internalType": "address[]", "name": "allowedPeekers", "type": "address[]" }, { "internalType": "string", "name": "BidType", "type": "string" } ], "name": "newBid", "outputs": [ { "components": [ { "internalType": "Suave.BidId", "name": "id", "type": "bytes16" }, { "internalType": "Suave.BidId", "name": "salt", "type": "bytes16" }, { "internalType": "uint64", "name": "decryptionCondition", "type": "uint64" }, { "internalType": "address[]", "name": "allowedPeekers", "type": "address[]" } ], "internalType": "struct Suave.Bid", "name": "", "type": "tuple" } ], "stateMutability": "view", "type": "function" }]`, "newBid") - - return &newBid{inoutAbi} -} - -func (c *newBid) RequiredGas(input []byte) uint64 { - return 1000 -} - -func (c *newBid) Run(input []byte) ([]byte, error) { - return input, nil -} - -func (c *newBid) RunConfidential(suaveContext *SuaveContext, input []byte) ([]byte, error) { - unpacked, err := c.inoutAbi.Inputs.Unpack(input) - if err != nil { - return []byte(err.Error()), err - } - version := unpacked[2].(string) - - decryptionCondition := unpacked[0].(uint64) - allowedPeekers := unpacked[1].([]common.Address) - - bid, err := c.runImpl(suaveContext, version, decryptionCondition, allowedPeekers, []common.Address{}) - if err != nil { - return []byte(err.Error()), err - } - - return c.inoutAbi.Outputs.Pack(bid) -} - -func (c *newBid) runImpl(suaveContext *SuaveContext, version string, decryptionCondition uint64, allowedPeekers []common.Address, allowedStores []common.Address) (*types.Bid, error) { +func newBidImpl(suaveContext *SuaveContext, version string, decryptionCondition uint64, allowedPeekers []common.Address, allowedStores []common.Address) (*types.Bid, error) { if suaveContext.ConfidentialComputeRequestTx == nil { panic("newBid: source transaction not present") } @@ -250,42 +143,7 @@ func (c *newBid) runImpl(suaveContext *SuaveContext, version string, decryptionC return &bid, nil } -type fetchBids struct { - inoutAbi abi.Method -} - -func newFetchBids() *fetchBids { - inoutAbi := mustParseMethodAbi(`[ { "inputs": [ { "internalType": "uint64", "name": "cond", "type": "uint64" }, { "internalType": "string", "name": "namespace", "type": "string" } ], "name": "fetchBids", "outputs": [ { "components": [ { "internalType": "Suave.BidId", "name": "id", "type": "bytes16" }, { "internalType": "Suave.BidId", "name": "salt", "type": "bytes16" }, { "internalType": "uint64", "name": "decryptionCondition", "type": "uint64" }, { "internalType": "address[]", "name": "allowedPeekers", "type": "address[]" }, { "internalType": "address[]", "name": "allowedStores", "type": "address[]" }, { "internalType": "string", "name": "version", "type": "string" } ], "internalType": "struct Suave.Bid[]", "name": "", "type": "tuple[]" } ], "stateMutability": "view", "type": "function" } ]`, "fetchBids") - - return &fetchBids{inoutAbi} -} - -func (c *fetchBids) RequiredGas(input []byte) uint64 { - return 1000 -} - -func (c *fetchBids) Run(input []byte) ([]byte, error) { - return input, nil -} - -func (c *fetchBids) RunConfidential(suaveContext *SuaveContext, input []byte) ([]byte, error) { - unpacked, err := c.inoutAbi.Inputs.Unpack(input) - if err != nil { - return []byte(err.Error()), err - } - - targetBlock := unpacked[0].(uint64) - namespace := unpacked[1].(string) - - bids, err := c.runImpl(suaveContext, targetBlock, namespace) - if err != nil { - return []byte(err.Error()), err - } - - return c.inoutAbi.Outputs.Pack(bids) -} - -func (c *fetchBids) runImpl(suaveContext *SuaveContext, targetBlock uint64, namespace string) ([]types.Bid, error) { +func fetchBidsImpl(suaveContext *SuaveContext, targetBlock uint64, namespace string) ([]types.Bid, error) { bids1 := suaveContext.Backend.ConfidentialStore.FetchBidsByProtocolAndBlock(targetBlock, namespace) bids := make([]types.Bid, 0, len(bids1)) @@ -296,20 +154,6 @@ func (c *fetchBids) runImpl(suaveContext *SuaveContext, targetBlock uint64, name return bids, nil } -func mustParseAbi(data string) abi.ABI { - inoutAbi, err := abi.JSON(strings.NewReader(data)) - if err != nil { - panic(err.Error()) - } - - return inoutAbi -} - -func mustParseMethodAbi(data string, method string) abi.Method { - inoutAbi := mustParseAbi(data) - return inoutAbi.Methods[method] -} - func formatPeekerError(format string, args ...any) ([]byte, error) { err := fmt.Errorf(format, args...) return []byte(err.Error()), err @@ -322,11 +166,11 @@ type suaveRuntime struct { var _ SuaveRuntime = &suaveRuntime{} func (b *suaveRuntime) ethcall(contractAddr common.Address, input []byte) ([]byte, error) { - return (ðCallPrecompile{}).runImpl(b.suaveContext, contractAddr, input) + return ethCallPrecompileImpl(b.suaveContext, contractAddr, input) } func (b *suaveRuntime) buildEthBlock(blockArgs types.BuildBlockArgs, bid types.BidId, namespace string) ([]byte, []byte, error) { - return (&buildEthBlock{}).runImpl(b.suaveContext, blockArgs, bid, namespace) + return buildEthBlockImpl(b.suaveContext, blockArgs, bid, namespace) } func (b *suaveRuntime) confidentialInputs() ([]byte, error) { @@ -334,19 +178,19 @@ func (b *suaveRuntime) confidentialInputs() ([]byte, error) { } func (b *suaveRuntime) confidentialStoreRetrieve(bidId types.BidId, key string) ([]byte, error) { - return (&confStoreRetrieve{}).runImpl(b.suaveContext, bidId, key) + return confStoreRetrieveImpl(b.suaveContext, bidId, key) } func (b *suaveRuntime) confidentialStoreStore(bidId types.BidId, key string, data []byte) error { - return (&confStoreStore{}).runImpl(b.suaveContext, bidId, key, data) + return confStoreStoreImpl(b.suaveContext, bidId, key, data) } func (b *suaveRuntime) extractHint(bundleData []byte) ([]byte, error) { - return (&extractHint{}).runImpl(b.suaveContext, bundleData) + return extractHintImpl(b.suaveContext, bundleData) } func (b *suaveRuntime) fetchBids(cond uint64, namespace string) ([]types.Bid, error) { - bids, err := (&fetchBids{}).runImpl(b.suaveContext, cond, namespace) + bids, err := fetchBidsImpl(b.suaveContext, cond, namespace) if err != nil { return nil, err } @@ -354,15 +198,19 @@ func (b *suaveRuntime) fetchBids(cond uint64, namespace string) ([]types.Bid, er } func (b *suaveRuntime) newBid(decryptionCondition uint64, allowedPeekers []common.Address, allowedStores []common.Address, BidType string) (types.Bid, error) { - bid, err := (&newBid{}).runImpl(b.suaveContext, BidType, decryptionCondition, allowedPeekers, allowedStores) + bid, err := newBidImpl(b.suaveContext, BidType, decryptionCondition, allowedPeekers, allowedStores) if err != nil { return types.Bid{}, err } return *bid, nil } +func (b *suaveRuntime) signEthTransaction(txn []byte, chainId string, signingKey string) ([]byte, error) { + return signEthTransactionImpl(txn, chainId, signingKey) +} + func (b *suaveRuntime) simulateBundle(bundleData []byte) (uint64, error) { - num, err := (&simulateBundle{}).runImpl(b.suaveContext, bundleData) + num, err := simulateBundleImpl(b.suaveContext, bundleData) if err != nil { return 0, err } @@ -370,13 +218,13 @@ func (b *suaveRuntime) simulateBundle(bundleData []byte) (uint64, error) { } func (b *suaveRuntime) submitEthBlockBidToRelay(relayUrl string, builderBid []byte) ([]byte, error) { - return (&submitEthBlockBidToRelay{}).runImpl(b.suaveContext, relayUrl, builderBid) + return submitEthBlockBidToRelayImpl(b.suaveContext, relayUrl, builderBid) } func (b *suaveRuntime) fillMevShareBundle(bidId types.BidId) ([]byte, error) { - return (&fillMevShareBundle{}).runImpl(b.suaveContext, bidId) + return fillMevShareBundleImpl(b.suaveContext, bidId) } func (b *suaveRuntime) submitBundleJsonRPC(url string, method string, params []byte) ([]byte, error) { - return (&submitBundleJsonRPC{}).runImpl(b.suaveContext, url, method, params) + return submitBundleJsonRPCImpl(b.suaveContext, url, method, params) } diff --git a/core/vm/contracts_suave_eth.go b/core/vm/contracts_suave_eth.go index 416bbe264c..c3e58b8e9a 100644 --- a/core/vm/contracts_suave_eth.go +++ b/core/vm/contracts_suave_eth.go @@ -4,7 +4,6 @@ import ( "bytes" "context" "encoding/json" - "errors" "fmt" "io" "math/big" @@ -18,8 +17,6 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/suave/artifacts" - suave "github.com/ethereum/go-ethereum/suave/core" "github.com/flashbots/go-boost-utils/bls" "github.com/flashbots/go-boost-utils/ssz" "github.com/holiman/uint256" @@ -34,6 +31,7 @@ import ( ) var ( + signEthTransactionAddress = common.HexToAddress("0x40100001") simulateBundleAddress = common.HexToAddress("0x42100000") extractHintAddress = common.HexToAddress("0x42100037") buildEthBlockAddress = common.HexToAddress("0x42100001") @@ -43,28 +41,39 @@ var ( fillMevShareBundleAddress = common.HexToAddress("0x43200001") ) -type simulateBundle struct { -} +func signEthTransactionImpl(txn []byte, chainId string, signingKey string) ([]byte, error) { + key, err := crypto.HexToECDSA(signingKey) + if err != nil { + return nil, fmt.Errorf("key not formatted properly: %w", err) + } -func (c *simulateBundle) RequiredGas(input []byte) uint64 { - // Should be proportional to bundle gas limit - return 10000 -} + chainIdInt, err := hexutil.DecodeBig(chainId) + if err != nil { + return nil, fmt.Errorf("chainId not formatted properly: %w", err) + } -func (c *simulateBundle) Run(input []byte) ([]byte, error) { - return input, nil -} + var tx types.Transaction + err = tx.UnmarshalBinary(txn) + if err != nil { + return nil, fmt.Errorf("txn not formatted properly: %w", err) + } + + signer := types.LatestSignerForChainID(chainIdInt) -func (c *simulateBundle) RunConfidential(suaveContext *SuaveContext, input []byte) ([]byte, error) { - egp, err := c.runImpl(suaveContext, input) + signedTx, err := types.SignTx(&tx, signer, key) if err != nil { - return []byte(err.Error()), err + return nil, fmt.Errorf("could not sign: %w", err) } - return artifacts.SuaveAbi.Methods["simulateBundle"].Outputs.Pack(egp.Uint64()) + signedBytes, err := signedTx.MarshalBinary() + if err != nil { + return nil, fmt.Errorf("could not encode signed transaction: %w", err) + } + + return signedBytes, nil } -func (c *simulateBundle) runImpl(suaveContext *SuaveContext, input []byte) (*big.Int, error) { +func simulateBundleImpl(suaveContext *SuaveContext, input []byte) (*big.Int, error) { var bundle types.SBundle err := json.Unmarshal(input, &bundle) if err != nil { @@ -87,28 +96,7 @@ func (c *simulateBundle) runImpl(suaveContext *SuaveContext, input []byte) (*big return egp, nil } -type extractHint struct{} - -func (c *extractHint) RequiredGas(input []byte) uint64 { - return 10000 -} - -func (c *extractHint) Run(input []byte) ([]byte, error) { - return input, nil -} - -func (c *extractHint) RunConfidential(suaveContext *SuaveContext, input []byte) ([]byte, error) { - unpacked, err := artifacts.SuaveAbi.Methods["extractHint"].Inputs.Unpack(input) - if err != nil { - return []byte(err.Error()), err - } - - bundleBytes := unpacked[0].([]byte) - - return c.runImpl(suaveContext, bundleBytes) -} - -func (c *extractHint) runImpl(suaveContext *SuaveContext, bundleBytes []byte) ([]byte, error) { +func extractHintImpl(suaveContext *SuaveContext, bundleBytes []byte) ([]byte, error) { var bundle types.SBundle err := json.Unmarshal(bundleBytes, &bundle) if err != nil { @@ -131,92 +119,11 @@ func (c *extractHint) runImpl(suaveContext *SuaveContext, bundleBytes []byte) ([ return hintBytes, nil } -type ethCallPrecompile struct{} - -func (e *ethCallPrecompile) RequiredGas(input []byte) uint64 { - // Should be proportional to bundle gas limit - return 10000 -} - -func (e *ethCallPrecompile) Run(input []byte) ([]byte, error) { - return input, nil -} - -func (e *ethCallPrecompile) runImpl(suaveContext *SuaveContext, contractAddr common.Address, input []byte) ([]byte, error) { +func ethCallPrecompileImpl(suaveContext *SuaveContext, contractAddr common.Address, input []byte) ([]byte, error) { return suaveContext.Backend.ConfidentialEthBackend.Call(context.Background(), contractAddr, input) } -func (e *ethCallPrecompile) RunConfidential(suaveContext *SuaveContext, input []byte) ([]byte, error) { - return nil, nil -} - -type buildEthBlock struct { -} - -func (c *buildEthBlock) RequiredGas(input []byte) uint64 { - // Should be proportional to bundle gas limit - return 10000 -} - -func (c *buildEthBlock) Run(input []byte) ([]byte, error) { - return input, nil -} - -func (c *buildEthBlock) RunConfidential(suaveContext *SuaveContext, input []byte) ([]byte, error) { - unpacked, err := artifacts.SuaveAbi.Methods["buildEthBlock"].Inputs.Unpack(input) - if err != nil { - return formatPeekerError("could not unpack inputs: %w", err) - } - - // blockArgs := unpacked[0].(types.BuildBlockArgs) - blockArgsRaw := unpacked[0].(struct { - Slot uint64 "json:\"slot\"" - ProposerPubkey []uint8 "json:\"proposerPubkey\"" - Parent common.Hash "json:\"parent\"" - Timestamp uint64 "json:\"timestamp\"" - FeeRecipient common.Address "json:\"feeRecipient\"" - GasLimit uint64 "json:\"gasLimit\"" - Random common.Hash "json:\"random\"" - Withdrawals []struct { - Index uint64 "json:\"index\"" - Validator uint64 "json:\"validator\"" - Address common.Address "json:\"Address\"" - Amount uint64 "json:\"amount\"" - } "json:\"withdrawals\"" - }) - - blockArgs := types.BuildBlockArgs{ - Slot: blockArgsRaw.Slot, - Parent: blockArgsRaw.Parent, - Timestamp: blockArgsRaw.Timestamp, - FeeRecipient: blockArgsRaw.FeeRecipient, - GasLimit: blockArgsRaw.GasLimit, - Random: blockArgsRaw.Random, - ProposerPubkey: blockArgsRaw.ProposerPubkey, - Withdrawals: types.Withdrawals{}, - } - - for _, w := range blockArgsRaw.Withdrawals { - blockArgs.Withdrawals = append(blockArgs.Withdrawals, &types.Withdrawal{ - Index: w.Index, - Validator: w.Validator, - Address: w.Address, - Amount: w.Amount, - }) - } - - bidId := unpacked[1].(suave.BidId) - namespace := unpacked[2].(string) - - bidBytes, envelopeBytes, err := c.runImpl(suaveContext, blockArgs, bidId, namespace) - if err != nil { - return formatPeekerError("could not unpack merged bid ids: %w", err) - } - - return artifacts.SuaveAbi.Methods["buildEthBlock"].Outputs.Pack(bidBytes, envelopeBytes) -} - -func (c *buildEthBlock) runImpl(suaveContext *SuaveContext, blockArgs types.BuildBlockArgs, bidId types.BidId, namespace string) ([]byte, []byte, error) { +func buildEthBlockImpl(suaveContext *SuaveContext, blockArgs types.BuildBlockArgs, bidId types.BidId, namespace string) ([]byte, []byte, error) { bidIds := [][16]byte{} // first check for merged bid, else assume regular bid if mergedBidsBytes, err := suaveContext.Backend.ConfidentialStore.Retrieve(bidId, buildEthBlockAddress, "default:v0:mergedBids"); err == nil { @@ -383,29 +290,7 @@ func (c *buildEthBlock) runImpl(suaveContext *SuaveContext, blockArgs types.Buil return bidBytes, envelopeBytes, nil } -type submitEthBlockBidToRelay struct{} - -func (c *submitEthBlockBidToRelay) RequiredGas(input []byte) uint64 { - return 1000 -} - -func (c *submitEthBlockBidToRelay) Run(input []byte) ([]byte, error) { - return input, nil -} - -func (c *submitEthBlockBidToRelay) RunConfidential(suaveContext *SuaveContext, input []byte) ([]byte, error) { - unpacked, err := artifacts.SuaveAbi.Methods["submitEthBlockBidToRelay"].Inputs.Unpack(input) - if err != nil { - return formatPeekerError("could not unpack inputs: %w", err) - } - - relayUrl := unpacked[0].(string) - builderBidJson := unpacked[1].([]byte) - - return c.runImpl(suaveContext, relayUrl, builderBidJson) -} - -func (c *submitEthBlockBidToRelay) runImpl(suaveContext *SuaveContext, relayUrl string, builderBidJson []byte) ([]byte, error) { +func submitEthBlockBidToRelayImpl(suaveContext *SuaveContext, relayUrl string, builderBidJson []byte) ([]byte, error) { ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(3*time.Second)) defer cancel() @@ -481,22 +366,7 @@ func executableDataToCapellaExecutionPayload(data *engine.ExecutableData) (*spec }, nil } -type submitBundleJsonRPC struct { -} - -func (c *submitBundleJsonRPC) RequiredGas(input []byte) uint64 { - return 1000 -} - -func (c *submitBundleJsonRPC) Run(input []byte) ([]byte, error) { - return nil, errors.New("not available in this context") -} - -func (c *submitBundleJsonRPC) RunConfidential(suaveContext *SuaveContext, input []byte) ([]byte, error) { - return nil, errors.New("not available in this context") -} - -func (c *submitBundleJsonRPC) runImpl(suaveContext *SuaveContext, url string, method string, params []byte) ([]byte, error) { +func submitBundleJsonRPCImpl(suaveContext *SuaveContext, url string, method string, params []byte) ([]byte, error) { ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(3*time.Second)) defer cancel() @@ -548,21 +418,7 @@ func (c *submitBundleJsonRPC) runImpl(suaveContext *SuaveContext, url string, me return nil, nil } -type fillMevShareBundle struct{} - -func (c *fillMevShareBundle) RequiredGas(input []byte) uint64 { - return 1000 -} - -func (c *fillMevShareBundle) Run(input []byte) ([]byte, error) { - return nil, errors.New("not available in this context") -} - -func (c *fillMevShareBundle) RunConfidential(suaveContext *SuaveContext, input []byte) ([]byte, error) { - return nil, errors.New("not available in this context") -} - -func (c *fillMevShareBundle) runImpl(suaveContext *SuaveContext, bidId types.BidId) ([]byte, error) { +func fillMevShareBundleImpl(suaveContext *SuaveContext, bidId types.BidId) ([]byte, error) { bid, err := suaveContext.Backend.ConfidentialStore.FetchBidById(bidId) if err != nil { return nil, err @@ -572,7 +428,7 @@ func (c *fillMevShareBundle) runImpl(suaveContext *SuaveContext, bidId types.Bid return nil, err } - matchedBundleIdsBytes, err := (&confStoreRetrieve{}).runImpl(suaveContext, bidId, "mevshare:v0:mergedBids") + matchedBundleIdsBytes, err := confStoreRetrieveImpl(suaveContext, bidId, "mevshare:v0:mergedBids") if err != nil { return nil, err } @@ -584,7 +440,7 @@ func (c *fillMevShareBundle) runImpl(suaveContext *SuaveContext, bidId types.Bid matchBidIds := unpackedBidIds[0].([][16]byte) - userBundleBytes, err := (&confStoreRetrieve{}).runImpl(suaveContext, matchBidIds[0], "mevshare:v0:ethBundles") + userBundleBytes, err := confStoreRetrieveImpl(suaveContext, matchBidIds[0], "mevshare:v0:ethBundles") if err != nil { return nil, fmt.Errorf("could not retrieve bundle data for bidId %v: %w", matchBidIds[0], err) } @@ -594,7 +450,7 @@ func (c *fillMevShareBundle) runImpl(suaveContext *SuaveContext, bidId types.Bid return nil, fmt.Errorf("could not unmarshal user bundle data for bidId %v: %w", matchBidIds[0], err) } - matchBundleBytes, err := (&confStoreRetrieve{}).runImpl(suaveContext, matchBidIds[1], "mevshare:v0:ethBundles") + matchBundleBytes, err := confStoreRetrieveImpl(suaveContext, matchBidIds[1], "mevshare:v0:ethBundles") if err != nil { return nil, fmt.Errorf("could not retrieve match bundle data for bidId %v: %w", matchBidIds[1], err) } diff --git a/core/vm/contracts_suave_runtime_adapter.go b/core/vm/contracts_suave_runtime_adapter.go index 331fc511e3..54105247f8 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: 5f7dc4aaca73e07546a16000edda8fa3b804777ed38fea2121bc0e09b889103e +// Hash: 23a6dd8b9b224b11b8baea19a80c55c7787c50c0eb2fc69727e66d615c913483 package vm import ( @@ -26,6 +26,7 @@ type SuaveRuntime interface { fetchBids(cond uint64, namespace string) ([]types.Bid, error) fillMevShareBundle(bidId types.BidId) ([]byte, error) newBid(decryptionCondition uint64, allowedPeekers []common.Address, allowedStores []common.Address, bidType string) (types.Bid, error) + signEthTransaction(txn []byte, chainId string, signingKey string) ([]byte, error) simulateBundle(bundleData []byte) (uint64, error) submitBundleJsonRPC(url string, method string, params []byte) ([]byte, error) submitEthBlockBidToRelay(relayUrl string, builderBid []byte) ([]byte, error) @@ -390,6 +391,48 @@ func (b *SuaveRuntimeAdapter) newBid(input []byte) (res []byte, err error) { } +func (b *SuaveRuntimeAdapter) signEthTransaction(input []byte) (res []byte, err error) { + var ( + unpacked []interface{} + result []byte + ) + + _ = unpacked + _ = result + + unpacked, err = artifacts.SuaveAbi.Methods["signEthTransaction"].Inputs.Unpack(input) + if err != nil { + err = errFailedToUnpackInput + return + } + + var ( + txn []byte + chainId string + signingKey string + ) + + txn = unpacked[0].([]byte) + chainId = unpacked[1].(string) + signingKey = unpacked[2].(string) + + var ( + output1 []byte + ) + + if output1, err = b.impl.signEthTransaction(txn, chainId, signingKey); err != nil { + return + } + + result, err = artifacts.SuaveAbi.Methods["signEthTransaction"].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{} diff --git a/core/vm/contracts_suave_test.go b/core/vm/contracts_suave_test.go index 55b6806212..fa817c6ad5 100644 --- a/core/vm/contracts_suave_test.go +++ b/core/vm/contracts_suave_test.go @@ -3,6 +3,7 @@ package vm import ( "context" "math/big" + "regexp" "strings" "testing" @@ -123,6 +124,10 @@ func TestSuavePrecompileStub(t *testing.T) { "precompile buildEthBlock (0000000000000000000000000000000042100001) not allowed on 00000000000000000000000000000000", } + expectedVariableErrors := []*regexp.Regexp{ + regexp.MustCompile("key not formatted properly: invalid hex character.*in private key"), + } + for name, addr := range artifacts.SuaveMethods { abiMethod, ok := artifacts.SuaveAbi.Methods[name] if !ok { @@ -140,6 +145,18 @@ func TestSuavePrecompileStub(t *testing.T) { for _, expectedError := range expectedErrors { if strings.Contains(err.Error(), expectedError) { found = true + break + } + } + + if found { + continue + } + + for _, expectedErrRe := range expectedVariableErrors { + if expectedErrRe.Match([]byte(err.Error())) { + found = true + break } } if !found { diff --git a/core/vm/suave.go b/core/vm/suave.go index 269bcc5e76..0d56dc553d 100644 --- a/core/vm/suave.go +++ b/core/vm/suave.go @@ -2,6 +2,7 @@ package vm import ( "crypto/ecdsa" + "errors" "fmt" "time" @@ -53,6 +54,28 @@ func NewRuntimeSuaveContext(evm *EVM, caller common.Address) *SuaveContext { } } +func staticRequiredGas(amt uint64) func(input []byte) uint64 { + return func(input []byte) uint64 { + return amt + } +} + +type stubPrecompile struct { + requiredGas func(input []byte) uint64 +} + +func (p *stubPrecompile) RequiredGas(input []byte) uint64 { + return p.requiredGas(input) +} + +func (p *stubPrecompile) Run(input []byte) ([]byte, error) { + return nil, errors.New("not available in this context") +} + +func (c *stubPrecompile) RunConfidential(suaveContext *SuaveContext, input []byte) ([]byte, error) { + return nil, errors.New("not available in this context") +} + // Implements PrecompiledContract for confidential smart contracts type SuavePrecompiledContractWrapper struct { addr common.Address @@ -114,6 +137,9 @@ func (p *SuavePrecompiledContractWrapper) Run(input []byte) ([]byte, error) { case extractHintAddress: ret, err = stub.extractHint(input) + case signEthTransactionAddress: + ret, err = stub.signEthTransaction(input) + case simulateBundleAddress: ret, err = stub.simulateBundle(input) diff --git a/core/vm/suave_abis.go b/core/vm/suave_abis.go index b190b48dcb..1b74c4bae5 100644 --- a/core/vm/suave_abis.go +++ b/core/vm/suave_abis.go @@ -1,3 +1,23 @@ package vm +import ( + "strings" + + "github.com/ethereum/go-ethereum/accounts/abi" +) + var bidIdsAbi = mustParseMethodAbi(`[{"inputs": [{ "type": "bytes16[]" }], "name": "bidids", "outputs":[], "type": "function"}]`, "bidids") + +func mustParseAbi(data string) abi.ABI { + inoutAbi, err := abi.JSON(strings.NewReader(data)) + if err != nil { + panic(err.Error()) + } + + return inoutAbi +} + +func mustParseMethodAbi(data string, method string) abi.Method { + inoutAbi := mustParseAbi(data) + return inoutAbi.Methods[method] +} diff --git a/rpc/json.go b/rpc/json.go index 8a3b162cab..06816c52e9 100644 --- a/rpc/json.go +++ b/rpc/json.go @@ -316,7 +316,7 @@ func parsePositionalArguments(rawArgs json.RawMessage, types []reflect.Type) ([] // Set any missing args to nil. for i := len(args); i < len(types); i++ { if types[i].Kind() != reflect.Ptr { - return nil, fmt.Errorf("missing value for required argument %d", i) + return nil, fmt.Errorf("missing value for required argument %d: %s", i, string(rawArgs)) } args = append(args, reflect.Zero(types[i])) } diff --git a/suave/artifacts/Suave.sol/Suave.json b/suave/artifacts/Suave.sol/Suave.json index 8a9436147f..4e2731fae9 100644 --- a/suave/artifacts/Suave.sol/Suave.json +++ b/suave/artifacts/Suave.sol/Suave.json @@ -146,6 +146,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "SIGN_ETH_TRANSACTION", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "SIMULATE_BUNDLE", @@ -186,6 +199,6 @@ "type": "function" } ], - "deployedBytecode": "0x73000000000000000000000000000000000000000030146080604052600436106100d95760003560e01c8063b61b127d11610096578063c91e11df11610070578063c91e11df1461015d578063d91525db14610168578063f0608b1c14610173578063f6ab3de51461017e57600080fd5b8063b61b127d1461013c578063b7817da014610147578063bc50c0051461015257600080fd5b806301c19530146100de578063040e51831461010557806369094cbc146101105780637320cb171461011b578063751afe2c1461012657806394804c6914610131575b600080fd5b6100e9634320000181565b6040516001600160a01b03909116815260200160405180910390f35b6100e9634210000381565b6100e9634201000181565b6100e9634203000081565b6100e9634210003781565b6100e9634210000181565b6100e9634210000081565b6100e9634202000081565b6100e9634210000281565b6100e9634203000181565b6100e9634201000081565b6100e9634300000181565b6100e963420200018156fea164736f6c6343000813000a", - "bytecode": "0x61019661003a600b82828239805160001a60731461002d57634e487b7160e01b600052600060045260246000fd5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600436106100d95760003560e01c8063b61b127d11610096578063c91e11df11610070578063c91e11df1461015d578063d91525db14610168578063f0608b1c14610173578063f6ab3de51461017e57600080fd5b8063b61b127d1461013c578063b7817da014610147578063bc50c0051461015257600080fd5b806301c19530146100de578063040e51831461010557806369094cbc146101105780637320cb171461011b578063751afe2c1461012657806394804c6914610131575b600080fd5b6100e9634320000181565b6040516001600160a01b03909116815260200160405180910390f35b6100e9634210000381565b6100e9634201000181565b6100e9634203000081565b6100e9634210003781565b6100e9634210000181565b6100e9634210000081565b6100e9634202000081565b6100e9634210000281565b6100e9634203000181565b6100e9634201000081565b6100e9634300000181565b6100e963420200018156fea164736f6c6343000813000a" + "deployedBytecode": "0x73000000000000000000000000000000000000000030146080604052600436106100f45760003560e01c8063b61b127d11610096578063c91e11df11610070578063c91e11df14610183578063d91525db1461018e578063f0608b1c14610199578063f6ab3de5146101a457600080fd5b8063b61b127d14610162578063b7817da01461016d578063bc50c0051461017857600080fd5b80637320cb17116100d25780637320cb1714610136578063744795b914610141578063751afe2c1461014c57806394804c691461015757600080fd5b806301c19530146100f9578063040e51831461012057806369094cbc1461012b575b600080fd5b610104634320000181565b6040516001600160a01b03909116815260200160405180910390f35b610104634210000381565b610104634201000181565b610104634203000081565b610104634010000181565b610104634210003781565b610104634210000181565b610104634210000081565b610104634202000081565b610104634210000281565b610104634203000181565b610104634201000081565b610104634300000181565b61010463420200018156fea164736f6c6343000813000a", + "bytecode": "0x6101bc61003a600b82828239805160001a60731461002d57634e487b7160e01b600052600060045260246000fd5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600436106100f45760003560e01c8063b61b127d11610096578063c91e11df11610070578063c91e11df14610183578063d91525db1461018e578063f0608b1c14610199578063f6ab3de5146101a457600080fd5b8063b61b127d14610162578063b7817da01461016d578063bc50c0051461017857600080fd5b80637320cb17116100d25780637320cb1714610136578063744795b914610141578063751afe2c1461014c57806394804c691461015757600080fd5b806301c19530146100f9578063040e51831461012057806369094cbc1461012b575b600080fd5b610104634320000181565b6040516001600160a01b03909116815260200160405180910390f35b610104634210000381565b610104634201000181565b610104634203000081565b610104634010000181565b610104634210003781565b610104634210000181565b610104634210000081565b610104634202000081565b610104634210000281565b610104634203000181565b610104634201000081565b610104634300000181565b61010463420200018156fea164736f6c6343000813000a" } diff --git a/suave/artifacts/SuaveLib.json b/suave/artifacts/SuaveLib.json index 6831312da9..b2dd7839fd 100644 --- a/suave/artifacts/SuaveLib.json +++ b/suave/artifacts/SuaveLib.json @@ -1 +1 @@ -[{"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":"bidId","type":"bytes16","internalType":"struct Suave.BidId"},{"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":"confidentialStoreRetrieve","inputs":[{"name":"bidId","type":"bytes16","internalType":"struct Suave.BidId"},{"name":"key","type":"string","internalType":"string"}],"outputs":[{"name":"output1","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"confidentialStoreStore","inputs":[{"name":"bidId","type":"bytes16","internalType":"struct Suave.BidId"},{"name":"key","type":"string","internalType":"string"},{"name":"data1","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":"fetchBids","inputs":[{"name":"cond","type":"uint64","internalType":"uint64"},{"name":"namespace","type":"string","internalType":"string"}],"outputs":[{"name":"bid","type":"tuple[]","internalType":"struct Suave.Bid[]","components":[{"name":"id","type":"bytes16","internalType":"struct Suave.BidId"},{"name":"salt","type":"bytes16","internalType":"struct Suave.BidId"},{"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":"bidId","type":"bytes16","internalType":"struct Suave.BidId"}],"outputs":[{"name":"encodedBundle","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"newBid","inputs":[{"name":"decryptionCondition","type":"uint64","internalType":"uint64"},{"name":"allowedPeekers","type":"address[]","internalType":"address[]"},{"name":"allowedStores","type":"address[]","internalType":"address[]"},{"name":"bidType","type":"string","internalType":"string"}],"outputs":[{"name":"bid","type":"tuple","internalType":"struct Suave.Bid","components":[{"name":"id","type":"bytes16","internalType":"struct Suave.BidId"},{"name":"salt","type":"bytes16","internalType":"struct Suave.BidId"},{"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":"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":"submitEthBlockBidToRelay","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":"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":"bidId","type":"bytes16","internalType":"struct Suave.BidId"},{"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":"confidentialStoreRetrieve","inputs":[{"name":"bidId","type":"bytes16","internalType":"struct Suave.BidId"},{"name":"key","type":"string","internalType":"string"}],"outputs":[{"name":"output1","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"confidentialStoreStore","inputs":[{"name":"bidId","type":"bytes16","internalType":"struct Suave.BidId"},{"name":"key","type":"string","internalType":"string"},{"name":"data1","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":"fetchBids","inputs":[{"name":"cond","type":"uint64","internalType":"uint64"},{"name":"namespace","type":"string","internalType":"string"}],"outputs":[{"name":"bid","type":"tuple[]","internalType":"struct Suave.Bid[]","components":[{"name":"id","type":"bytes16","internalType":"struct Suave.BidId"},{"name":"salt","type":"bytes16","internalType":"struct Suave.BidId"},{"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":"bidId","type":"bytes16","internalType":"struct Suave.BidId"}],"outputs":[{"name":"encodedBundle","type":"bytes","internalType":"bytes"}]},{"type":"function","name":"newBid","inputs":[{"name":"decryptionCondition","type":"uint64","internalType":"uint64"},{"name":"allowedPeekers","type":"address[]","internalType":"address[]"},{"name":"allowedStores","type":"address[]","internalType":"address[]"},{"name":"bidType","type":"string","internalType":"string"}],"outputs":[{"name":"bid","type":"tuple","internalType":"struct Suave.Bid","components":[{"name":"id","type":"bytes16","internalType":"struct Suave.BidId"},{"name":"salt","type":"bytes16","internalType":"struct Suave.BidId"},{"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":"submitEthBlockBidToRelay","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 f8ec26d5c5..3abe51d486 100644 --- a/suave/artifacts/addresses.go +++ b/suave/artifacts/addresses.go @@ -1,5 +1,5 @@ // Code generated by suave/gen. DO NOT EDIT. -// Hash: 5f7dc4aaca73e07546a16000edda8fa3b804777ed38fea2121bc0e09b889103e +// Hash: 23a6dd8b9b224b11b8baea19a80c55c7787c50c0eb2fc69727e66d615c913483 package artifacts import ( @@ -17,6 +17,7 @@ var ( fetchBidsAddr = common.HexToAddress("0x0000000000000000000000000000000042030001") fillMevShareBundleAddr = common.HexToAddress("0x0000000000000000000000000000000043200001") newBidAddr = common.HexToAddress("0x0000000000000000000000000000000042030000") + signEthTransactionAddr = common.HexToAddress("0x0000000000000000000000000000000040100001") simulateBundleAddr = common.HexToAddress("0x0000000000000000000000000000000042100000") submitBundleJsonRPCAddr = common.HexToAddress("0x0000000000000000000000000000000043000001") submitEthBlockBidToRelayAddr = common.HexToAddress("0x0000000000000000000000000000000042100002") @@ -32,6 +33,7 @@ var SuaveMethods = map[string]common.Address{ "fetchBids": fetchBidsAddr, "fillMevShareBundle": fillMevShareBundleAddr, "newBid": newBidAddr, + "signEthTransaction": signEthTransactionAddr, "simulateBundle": simulateBundleAddr, "submitBundleJsonRPC": submitBundleJsonRPCAddr, "submitEthBlockBidToRelay": submitEthBlockBidToRelayAddr, @@ -57,6 +59,8 @@ func PrecompileAddressToName(addr common.Address) string { return "fillMevShareBundle" case newBidAddr: return "newBid" + case signEthTransactionAddr: + return "signEthTransaction" case simulateBundleAddr: return "simulateBundle" case submitBundleJsonRPCAddr: diff --git a/suave/e2e/workflow_test.go b/suave/e2e/workflow_test.go index b8dbdafb3c..43c7bba94a 100644 --- a/suave/e2e/workflow_test.go +++ b/suave/e2e/workflow_test.go @@ -36,6 +36,7 @@ import ( "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" + "github.com/ethereum/go-ethereum/suave/artifacts" suave "github.com/ethereum/go-ethereum/suave/core" "github.com/ethereum/go-ethereum/suave/cstore" "github.com/ethereum/go-ethereum/suave/sdk" @@ -223,13 +224,64 @@ func TestMempool(t *testing.T) { receipts := block.Receipts require.Equal(t, 1, len(receipts)) require.Equal(t, uint8(types.SuaveTxType), receipts[0].Type) - require.Equal(t, uint64(1), receipts[0].Status) + require.Equal(t, uint64(0), receipts[0].Status) require.Equal(t, 1, len(block.Transactions())) require.Equal(t, []byte(simResult), block.Transactions()[0].Data()) } } +func TestTxSigningPrecompile(t *testing.T) { + fr := newFramework(t) + defer fr.Close() + + tx := types.NewTransaction(15, common.Address{0x14}, big.NewInt(50), 1000000, big.NewInt(42313), []byte{0x42}) + txBytes, err := tx.MarshalBinary() + require.NoError(t, err) + + sk, err := crypto.GenerateKey() + require.NoError(t, err) + skHex := hex.EncodeToString(crypto.FromECDSA(sk)) + + txChainId := big.NewInt(13) + chainIdHex := hexutil.EncodeBig(txChainId) + + // function signEthTransaction(bytes memory txn, string memory chainId, string memory signingKey) + args, err := artifacts.SuaveAbi.Methods["signEthTransaction"].Inputs.Pack(txBytes, chainIdHex, skHex) + require.NoError(t, err) + + gas := hexutil.Uint64(1000000) + chainId := hexutil.Big(*testSuaveGenesis.Config.ChainID) + + var callResult hexutil.Bytes + err = fr.suethSrv.RPCNode().Call(&callResult, "eth_call", setTxArgsDefaults(ethapi.TransactionArgs{ + To: &signEthTransaction, + Gas: &gas, + IsConfidential: true, + ChainID: &chainId, + Data: (*hexutil.Bytes)(&args), + }), "latest") + requireNoRpcError(t, err) + + unpackedCallResult, err := artifacts.SuaveAbi.Methods["signEthTransaction"].Outputs.Unpack(callResult) + require.NoError(t, err) + + var signedTx types.Transaction + require.NoError(t, signedTx.UnmarshalBinary(unpackedCallResult[0].([]byte))) + + require.Equal(t, tx.Nonce(), signedTx.Nonce()) + require.Equal(t, *tx.To(), *signedTx.To()) + require.Equal(t, 0, tx.Value().Cmp(signedTx.Value())) + require.Equal(t, tx.Gas(), signedTx.Gas()) + require.Equal(t, tx.GasPrice(), signedTx.GasPrice()) + require.Equal(t, tx.Data(), signedTx.Data()) + + sender, err := types.Sender(types.LatestSignerForChainID(txChainId), &signedTx) + require.NoError(t, err) + + require.Equal(t, crypto.PubkeyToAddress(sk.PublicKey), sender) +} + func TestBundleBid(t *testing.T) { // t.Fatal("not implemented") @@ -1155,8 +1207,10 @@ var ( isConfidentialAddress = common.HexToAddress("0x42010000") fetchBidsAddress = common.HexToAddress("0x42030001") fillMevShareBundleAddress = common.HexToAddress("0x43200001") - simulateBundleAddress = common.HexToAddress("0x42100000") - buildEthBlockAddress = common.HexToAddress("0x42100001") + + signEthTransaction = common.HexToAddress("0x40100001") + simulateBundleAddress = common.HexToAddress("0x42100000") + buildEthBlockAddress = common.HexToAddress("0x42100001") /* contracts */ newBundleBidAddress = common.HexToAddress("0x642300000") diff --git a/suave/gen/suave_spec.yaml b/suave/gen/suave_spec.yaml index 28c4ee19da..e60fc74434 100644 --- a/suave/gen/suave_spec.yaml +++ b/suave/gen/suave_spec.yaml @@ -99,6 +99,19 @@ functions: fields: - name: output1 type: bytes + - name: signEthTransaction + address: "0x0000000000000000000000000000000040100001" + input: + - name: txn + type: bytes + - name: chainId + type: string + - name: signingKey + type: string + output: + fields: + - name: output1 + type: bytes - name: simulateBundle address: "0x0000000000000000000000000000000042100000" input: diff --git a/suave/sol/libraries/Suave.sol b/suave/sol/libraries/Suave.sol index 2d78be9120..3923371d53 100644 --- a/suave/sol/libraries/Suave.sol +++ b/suave/sol/libraries/Suave.sol @@ -53,6 +53,8 @@ library Suave { address public constant NEW_BID = 0x0000000000000000000000000000000042030000; + address public constant SIGN_ETH_TRANSACTION = 0x0000000000000000000000000000000040100001; + address public constant SIMULATE_BUNDLE = 0x0000000000000000000000000000000042100000; address public constant SUBMIT_BUNDLE_JSON_RPC = 0x0000000000000000000000000000000043000001; @@ -164,6 +166,19 @@ library Suave { return abi.decode(data, (Bid)); } + function signEthTransaction(bytes memory txn, string memory chainId, string memory signingKey) + internal + view + returns (bytes memory) + { + (bool success, bytes memory data) = SIGN_ETH_TRANSACTION.staticcall(abi.encode(txn, chainId, signingKey)); + if (!success) { + revert PeekerReverted(SIGN_ETH_TRANSACTION, 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) { diff --git a/suave/sol/libraries/SuaveAbi.sol b/suave/sol/libraries/SuaveAbi.sol index 2b583884ac..a4da325c50 100644 --- a/suave/sol/libraries/SuaveAbi.sol +++ b/suave/sol/libraries/SuaveAbi.sol @@ -9,6 +9,7 @@ contract SuaveAbi { function fetchBids(uint64 cond, string memory namespace) external view returns (Suave.Bid[] memory) {} function confidentialStoreStore(Suave.BidId bidId, string memory key, bytes memory data) external view {} function confidentialStoreRetrieve(Suave.BidId bidId, string memory key) external view returns (bytes memory) {} + function signEthTransaction(bytes memory txn, string memory chainId, string memory signingKey) external view returns (bytes memory) {} function simulateBundle(bytes memory bundleData) external view returns (uint64) {} function extractHint(bytes memory bundleData) external view returns (bytes memory) {} function buildEthBlock(Suave.BuildBlockArgs memory blockArgs, Suave.BidId bid, string memory namespace) external view returns (bytes memory, bytes memory) {}