working builder tests

This commit is contained in:
dmarzzz 2024-01-16 16:49:13 -05:00
parent 28a45e3492
commit 2786f36e04
12 changed files with 384 additions and 28 deletions

View file

@ -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")

View file

@ -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
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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"
)

View file

@ -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)
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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)
}
}

View file

@ -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)

50
suave/core/types.go Normal file
View file

@ -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
}