diff --git a/cmd/geth/main.go b/cmd/geth/main.go index f55bc8ed29..4438cef560 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -196,9 +196,6 @@ var ( utils.MetricsInfluxDBBucketFlag, utils.MetricsInfluxDBOrganizationFlag, } - suaveApiFlags = []cli.Flag{ - utils.SuaveEnabled, - } ) var app = flags.NewApp("the go-ethereum command line interface") @@ -248,7 +245,6 @@ func init() { consoleFlags, debug.Flags, metricsFlags, - suaveApiFlags, ) flags.AutoEnvVars(app.Flags, "GETH") diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 5d9f9fa555..159c47ca01 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -69,7 +69,6 @@ import ( "github.com/ethereum/go-ethereum/p2p/netutil" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" - "github.com/ethereum/go-ethereum/suave" "github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie/triedb/hashdb" "github.com/ethereum/go-ethereum/trie/triedb/pathdb" @@ -909,13 +908,6 @@ Please note that --` + MetricsHTTPFlag.Name + ` must be set to start the server. Value: metrics.DefaultConfig.InfluxDBOrganization, Category: flags.MetricsCategory, } - - // SUAVE namespace rpc settings - SuaveEnabled = &cli.BoolFlag{ - Name: "suave", - Usage: "Enable the suave", - Category: flags.SuaveCategory, - } ) var ( @@ -1352,10 +1344,6 @@ func SetP2PConfig(ctx *cli.Context, cfg *p2p.Config) { } } -func SetSuaveConfig(ctx *cli.Context, cfg *suave.Config) { - cfg.Enabled = ctx.IsSet(SuaveEnabled.Name) -} - // SetNodeConfig applies node-related command line flags to the config. func SetNodeConfig(ctx *cli.Context, cfg *node.Config) { SetP2PConfig(ctx, &cfg.P2P) @@ -1871,18 +1859,11 @@ func SetDNSDiscoveryDefaults(cfg *ethconfig.Config, genesis common.Hash) { // RegisterEthService adds an Ethereum client to the stack. // The second return value is the full node instance. -func RegisterEthService(stack *node.Node, cfg *ethconfig.Config, suaveConfig *suave.Config) (ethapi.Backend, *eth.Ethereum) { +func RegisterEthService(stack *node.Node, cfg *ethconfig.Config) (ethapi.Backend, *eth.Ethereum) { backend, err := eth.New(stack, cfg) if err != nil { Fatalf("Failed to register the Ethereum service: %v", err) } - if suaveConfig.Enabled { - log.Info("Enable suave service") - if err := suave.Register(stack, backend, suaveConfig); err != nil { - Fatalf("Failed to register the suave service: %v", err) - } - } - stack.RegisterAPIs(tracers.APIs(backend.APIBackend)) return backend.APIBackend, backend } diff --git a/core/types/suave_structs.go b/core/types/suave_structs.go index 0d9bab5fec..849acfcf9b 100644 --- a/core/types/suave_structs.go +++ b/core/types/suave_structs.go @@ -30,3 +30,24 @@ type DataRecord struct { AllowedStores []common.Address Version string } + +type HttpRequest struct { + Url string + Method string + Headers []string + Body []byte + WithFlashbotsSignature bool +} + +type SimulateTransactionResult struct { + Egp uint64 + Logs []*SimulatedLog + Success bool + Error string +} + +type SimulatedLog struct { + Data []byte + Addr common.Address + Topics []common.Hash +} \ No newline at end of file diff --git a/eth/api_backend.go b/eth/api_backend.go index d4148ee11a..63469ca766 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -25,6 +25,7 @@ import ( "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/bloombits" @@ -37,6 +38,7 @@ import ( "github.com/ethereum/go-ethereum/eth/tracers" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" @@ -423,3 +425,20 @@ func (b *EthAPIBackend) BuildBlockFromTxs(ctx context.Context, buildArgs *types. func (b *EthAPIBackend) BuildBlockFromBundles(ctx context.Context, buildArgs *types.BuildBlockArgs, bundles []types.SBundle) (*types.Block, *big.Int, error) { return b.eth.Miner().BuildBlockFromBundles(ctx, buildArgs, bundles) } + +func (b *EthAPIBackend) Call(ctx context.Context, contractAddr common.Address, input []byte) ([]byte, error) { + // Note: this is pretty close to be a circle dependency. + data := hexutil.Bytes(input) + txnArgs := ethapi.TransactionArgs{ + To: &contractAddr, + Data: &data, + } + + blockNum := rpc.LatestBlockNumber + res, err := ethapi.DoCall(ctx, b, txnArgs, rpc.BlockNumberOrHash{BlockNumber: &blockNum}, nil, nil, 5*time.Second, 100000) + if err != nil { + return nil, err + } + + return res.ReturnData, nil +} diff --git a/eth/backend.go b/eth/backend.go index cb410d3655..327e0094e7 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -57,6 +57,7 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" + "github.com/ethereum/go-ethereum/suave/backends" suave_builder "github.com/ethereum/go-ethereum/suave/builder" suave_builder_api "github.com/ethereum/go-ethereum/suave/builder/api" ) diff --git a/suave/backends/eth_backend_server.go b/suave/backends/eth_backend_server.go new file mode 100644 index 0000000000..571f4a4edd --- /dev/null +++ b/suave/backends/eth_backend_server.go @@ -0,0 +1,89 @@ +package backends + +import ( + "context" + "math/big" + + "github.com/ethereum/go-ethereum/beacon/engine" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + suave "github.com/ethereum/go-ethereum/suave/core" +) + +// EthBackend is the set of functions exposed from the SUAVE-enabled node +type EthBackend interface { + BuildEthBlock(ctx context.Context, buildArgs *types.BuildBlockArgs, txs types.Transactions) (*engine.ExecutionPayloadEnvelope, error) + BuildEthBlockFromBundles(ctx context.Context, buildArgs *types.BuildBlockArgs, bundles []types.SBundle) (*engine.ExecutionPayloadEnvelope, error) + Call(ctx context.Context, contractAddr common.Address, input []byte) ([]byte, error) +} + +var _ EthBackend = &EthBackendServer{} + +// EthBackendServerBackend is the interface implemented by the SUAVE-enabled node +// to resolve the EthBackend server queries +type EthBackendServerBackend interface { + CurrentHeader() *types.Header + BuildBlockFromTxs(ctx context.Context, buildArgs *suave.BuildBlockArgs, txs types.Transactions) (*types.Block, *big.Int, error) + BuildBlockFromBundles(ctx context.Context, buildArgs *suave.BuildBlockArgs, bundles []types.SBundle) (*types.Block, *big.Int, error) + Call(ctx context.Context, contractAddr common.Address, input []byte) ([]byte, error) +} + +type EthBackendServer struct { + b EthBackendServerBackend +} + +func NewEthBackendServer(b EthBackendServerBackend) *EthBackendServer { + return &EthBackendServer{b} +} + +func (e *EthBackendServer) BuildEthBlock(ctx context.Context, buildArgs *types.BuildBlockArgs, txs types.Transactions) (*engine.ExecutionPayloadEnvelope, error) { + if buildArgs == nil { + head := e.b.CurrentHeader() + buildArgs = &types.BuildBlockArgs{ + Parent: head.Hash(), + Timestamp: head.Time + uint64(12), + FeeRecipient: common.Address{0x42}, + GasLimit: 30000000, + Random: head.Root, + Withdrawals: nil, + Extra: []byte(""), + FillPending: false, + } + } + + block, profit, err := e.b.BuildBlockFromTxs(ctx, buildArgs, txs) + if err != nil { + return nil, err + } + + // TODO: we're not adding blobs, but this is not where you would do it anyways + return engine.BlockToExecutableData(block, profit, nil), nil +} + +func (e *EthBackendServer) BuildEthBlockFromBundles(ctx context.Context, buildArgs *types.BuildBlockArgs, bundles []types.SBundle) (*engine.ExecutionPayloadEnvelope, error) { + if buildArgs == nil { + head := e.b.CurrentHeader() + buildArgs = &types.BuildBlockArgs{ + Parent: head.Hash(), + Timestamp: head.Time + uint64(12), + FeeRecipient: common.Address{0x42}, + GasLimit: 30000000, + Random: head.Root, + Withdrawals: nil, + Extra: []byte(""), + FillPending: false, + } + } + + block, profit, err := e.b.BuildBlockFromBundles(ctx, buildArgs, bundles) + if err != nil { + return nil, err + } + + // TODO: we're not adding blobs, but this is not where you would do it anyways + return engine.BlockToExecutableData(block, profit, nil), nil +} + +func (e *EthBackendServer) Call(ctx context.Context, contractAddr common.Address, input []byte) ([]byte, error) { + return e.b.Call(ctx, contractAddr, input) +} diff --git a/suave/backends/eth_backend_server_test.go b/suave/backends/eth_backend_server_test.go new file mode 100644 index 0000000000..60bc0d70b6 --- /dev/null +++ b/suave/backends/eth_backend_server_test.go @@ -0,0 +1,58 @@ +package backends + +import ( + "context" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/rpc" + "github.com/ethereum/go-ethereum/trie" + "github.com/stretchr/testify/require" + + "github.com/ethereum/go-ethereum/core/types" + suave "github.com/ethereum/go-ethereum/suave/core" +) + +func TestEthBackend_Compatibility(t *testing.T) { + // This test ensures that the client is able to call to the server. + // It does not cover the internal logic implemention of the endpoints. + srv := rpc.NewServer() + require.NoError(t, srv.RegisterName("suavex", NewEthBackendServer(&mockBackend{}))) + + clt := &RemoteEthBackend{client: rpc.DialInProc(srv)} + + _, err := clt.BuildEthBlock(context.Background(), &types.BuildBlockArgs{}, nil) + require.NoError(t, err) + + _, err = clt.BuildEthBlockFromBundles(context.Background(), &types.BuildBlockArgs{}, nil) + require.NoError(t, err) + + _, err = clt.Call(context.Background(), common.Address{}, nil) + require.NoError(t, err) +} + +// mockBackend is a backend for the EthBackendServer that returns mock data +type mockBackend struct{} + +func (n *mockBackend) CurrentHeader() *types.Header { + return &types.Header{} +} + +func (n *mockBackend) BuildBlockFromTxs(ctx context.Context, buildArgs *suave.BuildBlockArgs, txs types.Transactions) (*types.Block, *big.Int, error) { + block := types.NewBlock(&types.Header{GasUsed: 1000, BaseFee: big.NewInt(1)}, txs, nil, nil, trie.NewStackTrie(nil)) + return block, big.NewInt(11000), nil +} + +func (n *mockBackend) BuildBlockFromBundles(ctx context.Context, buildArgs *suave.BuildBlockArgs, bundles []types.SBundle) (*types.Block, *big.Int, error) { + var txs types.Transactions + for _, bundle := range bundles { + txs = append(txs, bundle.Txs...) + } + block := types.NewBlock(&types.Header{GasUsed: 1000, BaseFee: big.NewInt(1)}, txs, nil, nil, trie.NewStackTrie(nil)) + return block, big.NewInt(11000), nil +} + +func (n *mockBackend) Call(ctx context.Context, contractAddr common.Address, input []byte) ([]byte, error) { + return []byte{0x1}, nil +} diff --git a/suave/backends/eth_backends.go b/suave/backends/eth_backends.go new file mode 100644 index 0000000000..6f3d21742a --- /dev/null +++ b/suave/backends/eth_backends.go @@ -0,0 +1,100 @@ +package backends + +import ( + "context" + "math/big" + + "github.com/ethereum/go-ethereum/beacon/engine" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rpc" + builder "github.com/ethereum/go-ethereum/suave/builder/api" + suave "github.com/ethereum/go-ethereum/suave/core" + "github.com/ethereum/go-ethereum/trie" +) + +var ( + _ EthBackend = &EthMock{} + _ EthBackend = &RemoteEthBackend{} +) + +type EthMock struct { + *builder.MockServer +} + +func (e *EthMock) BuildEthBlock(ctx context.Context, args *suave.BuildBlockArgs, txs types.Transactions) (*engine.ExecutionPayloadEnvelope, error) { + block := types.NewBlock(&types.Header{GasUsed: 1000}, txs, nil, nil, trie.NewStackTrie(nil)) + return engine.BlockToExecutableData(block, big.NewInt(11000), nil), nil +} + +func (e *EthMock) BuildEthBlockFromBundles(ctx context.Context, args *suave.BuildBlockArgs, bundles []types.SBundle) (*engine.ExecutionPayloadEnvelope, error) { + var txs types.Transactions + for _, bundle := range bundles { + txs = append(txs, bundle.Txs...) + } + block := types.NewBlock(&types.Header{GasUsed: 1000}, txs, nil, nil, trie.NewStackTrie(nil)) + return engine.BlockToExecutableData(block, big.NewInt(11000), nil), nil +} + +func (e *EthMock) Call(ctx context.Context, contractAddr common.Address, input []byte) ([]byte, error) { + return nil, nil +} + +type RemoteEthBackend struct { + endpoint string + client *rpc.Client + + *builder.APIClient +} + +func NewRemoteEthBackend(endpoint string) *RemoteEthBackend { + r := &RemoteEthBackend{ + endpoint: endpoint, + } + + r.APIClient = builder.NewClientFromRPC(r) + return r +} + +func (e *RemoteEthBackend) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error { + if e.client == nil { + // should lock + var err error + client, err := rpc.DialContext(ctx, e.endpoint) + if err != nil { + return err + } + e.client = client + } + + err := e.client.CallContext(ctx, &result, method, args...) + if err != nil { + client := e.client + e.client = nil + client.Close() + return err + } + + return nil +} + +func (e *RemoteEthBackend) BuildEthBlock(ctx context.Context, args *suave.BuildBlockArgs, txs types.Transactions) (*engine.ExecutionPayloadEnvelope, error) { + var result engine.ExecutionPayloadEnvelope + err := e.CallContext(ctx, &result, "suavex_buildEthBlock", args, txs) + + return &result, err +} + +func (e *RemoteEthBackend) BuildEthBlockFromBundles(ctx context.Context, args *suave.BuildBlockArgs, bundles []types.SBundle) (*engine.ExecutionPayloadEnvelope, error) { + var result engine.ExecutionPayloadEnvelope + err := e.CallContext(ctx, &result, "suavex_buildEthBlockFromBundles", args, bundles) + + return &result, err +} + +func (e *RemoteEthBackend) Call(ctx context.Context, contractAddr common.Address, input []byte) ([]byte, error) { + var result []byte + err := e.CallContext(ctx, &result, "suavex_call", contractAddr, input) + + return result, err +} diff --git a/suave/builder/session_manager.go b/suave/builder/session_manager.go index b3e740cbd4..2ad4d7376b 100644 --- a/suave/builder/session_manager.go +++ b/suave/builder/session_manager.go @@ -7,7 +7,7 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/consensus/misc" + "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" @@ -80,7 +80,7 @@ func (s *SessionManager) NewSession() (string, error) { // Set baseFee and GasLimit if we are on an EIP-1559 chain if chainConfig.IsLondon(header.Number) { - header.BaseFee = misc.CalcBaseFee(chainConfig, parent) + header.BaseFee = CalcBaseFee(chainConfig, parent) if !chainConfig.IsLondon(parent.Number) { parentGasLimit := parent.GasLimit * chainConfig.ElasticityMultiplier() header.GasLimit = core.CalcGasLimit(parentGasLimit, s.config.GasCeil) @@ -136,3 +136,44 @@ func (s *SessionManager) AddTransaction(sessionId string, tx *types.Transaction) } return builder.AddTransaction(tx) } + +// CalcBaseFee calculates the basefee of the header. +func CalcBaseFee(config *params.ChainConfig, parent *types.Header) *big.Int { + // If the current block is the first EIP-1559 block, return the InitialBaseFee. + if !config.IsLondon(parent.Number) { + return new(big.Int).SetUint64(params.InitialBaseFee) + } + + parentGasTarget := parent.GasLimit / config.ElasticityMultiplier() + // If the parent gasUsed is the same as the target, the baseFee remains unchanged. + if parent.GasUsed == parentGasTarget { + return new(big.Int).Set(parent.BaseFee) + } + + var ( + num = new(big.Int) + denom = new(big.Int) + ) + + if parent.GasUsed > parentGasTarget { + // If the parent block used more gas than its target, the baseFee should increase. + // max(1, parentBaseFee * gasUsedDelta / parentGasTarget / baseFeeChangeDenominator) + num.SetUint64(parent.GasUsed - parentGasTarget) + num.Mul(num, parent.BaseFee) + num.Div(num, denom.SetUint64(parentGasTarget)) + num.Div(num, denom.SetUint64(config.BaseFeeChangeDenominator())) + baseFeeDelta := math.BigMax(num, common.Big1) + + return num.Add(parent.BaseFee, baseFeeDelta) + } else { + // Otherwise if the parent block used less gas than its target, the baseFee should decrease. + // max(0, parentBaseFee * gasUsedDelta / parentGasTarget / baseFeeChangeDenominator) + num.SetUint64(parentGasTarget - parent.GasUsed) + num.Mul(num, parent.BaseFee) + num.Div(num, denom.SetUint64(parentGasTarget)) + num.Div(num, denom.SetUint64(config.BaseFeeChangeDenominator())) + baseFee := num.Sub(parent.BaseFee, num) + + return math.BigMax(baseFee, common.Big0) + } +} diff --git a/suave/builder/session_manager_test.go b/suave/builder/session_manager_test.go index 8d9dbaef4f..cdd20a1b43 100644 --- a/suave/builder/session_manager_test.go +++ b/suave/builder/session_manager_test.go @@ -136,11 +136,11 @@ func newMockState(t *testing.T) *mockState { preState.AddBalance(premineKeyAddr, big.NewInt(1000000000000000000)) - root, err := preState.Commit(true) + root, err := preState.Commit(1, true) require.NoError(t, err) // for the sake of this test, we only need all the forks enabled - chainConfig := params.SuaveChainConfig + chainConfig := params.TestChainConfig // Disable london so that we do not check gasFeeCap (TODO: Fix) chainConfig.LondonBlock = big.NewInt(100) diff --git a/suave/config.go b/suave/core/config.go similarity index 100% rename from suave/config.go rename to suave/core/config.go diff --git a/suave/core/types.go b/suave/core/types.go new file mode 100644 index 0000000000..6071d84db5 --- /dev/null +++ b/suave/core/types.go @@ -0,0 +1,50 @@ +package suave + +import ( + "context" + + "github.com/ethereum/go-ethereum/beacon/engine" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" + builder "github.com/ethereum/go-ethereum/suave/builder/api" +) + +var AllowedPeekerAny = common.HexToAddress("0xC8df3686b4Afb2BB53e60EAe97EF043FE03Fb829") // "*" + +type Bytes = hexutil.Bytes +type DataId = types.DataId + +type DataRecord struct { + Id types.DataId + Salt types.DataId + DecryptionCondition uint64 + AllowedPeekers []common.Address + AllowedStores []common.Address + Version string + CreationTx *types.Transaction + Signature []byte +} + +func (b *DataRecord) ToInnerRecord() types.DataRecord { + return types.DataRecord{ + Id: b.Id, + Salt: b.Salt, + DecryptionCondition: b.DecryptionCondition, + AllowedPeekers: b.AllowedPeekers, + AllowedStores: b.AllowedStores, + Version: b.Version, + } +} + +type MEVMBid = types.DataRecord + +type BuildBlockArgs = types.BuildBlockArgs + +type ConfidentialEthBackend interface { + BuildEthBlock(ctx context.Context, args *BuildBlockArgs, txs types.Transactions) (*engine.ExecutionPayloadEnvelope, error) + BuildEthBlockFromBundles(ctx context.Context, args *BuildBlockArgs, bundles []types.SBundle) (*engine.ExecutionPayloadEnvelope, error) + Call(ctx context.Context, contractAddr common.Address, input []byte) ([]byte, error) + + builder.API +}