Skip to content

Commit

Permalink
Add stateless builder
Browse files Browse the repository at this point in the history
  • Loading branch information
ferranbt committed Dec 4, 2023
1 parent 3429ee5 commit 89003d3
Show file tree
Hide file tree
Showing 7 changed files with 548 additions and 0 deletions.
14 changes: 14 additions & 0 deletions suave/builder/api/api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package api

import (
"context"

"github.com/ethereum/go-ethereum/beacon/engine"
"github.com/ethereum/go-ethereum/core/types"
)

type API interface {
NewSession(ctx context.Context) (string, error)
AddTransaction(ctx context.Context, sessionId string, tx *types.Transaction) error
Finalize(ctx context.Context, sessionId string) (*engine.ExecutionPayloadEnvelope, error)
}
44 changes: 44 additions & 0 deletions suave/builder/api/api_client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package api

import (
"context"

"github.com/ethereum/go-ethereum/beacon/engine"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rpc"
)

var _ API = (*APIClient)(nil)

type APIClient struct {
rpc *rpc.Client
}

func NewClient(endpoint string) (*APIClient, error) {
clt, err := rpc.Dial(endpoint)
if err != nil {
return nil, err
}
return NewClientFromRPC(clt), nil
}

func NewClientFromRPC(rpc *rpc.Client) *APIClient {
return &APIClient{rpc: rpc}
}

func (a *APIClient) NewSession(ctx context.Context) (string, error) {
var id string
err := a.rpc.CallContext(ctx, &id, "builder_newSession")
return id, err
}

func (a *APIClient) AddTransaction(ctx context.Context, sessionId string, tx *types.Transaction) error {
err := a.rpc.CallContext(ctx, nil, "builder_addTransaction", sessionId, tx)
return err
}

func (a *APIClient) Finalize(ctx context.Context, sessionId string) (*engine.ExecutionPayloadEnvelope, error) {
var res *engine.ExecutionPayloadEnvelope
err := a.rpc.CallContext(ctx, &res, "builder_finalize", sessionId)
return res, err
}
38 changes: 38 additions & 0 deletions suave/builder/api/api_server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package api

import (
"context"

"github.com/ethereum/go-ethereum/beacon/engine"
"github.com/ethereum/go-ethereum/core/types"
)

// sessionManager is the backend that manages the session state of the builder API.
type sessionManager interface {
NewSession() (string, error)
AddTransaction(sessionId string, tx *types.Transaction) error
Finalize(sessionId string) (*engine.ExecutionPayloadEnvelope, error)
}

func NewServer(s sessionManager) *Server {
api := &Server{
sessionMngr: s,
}
return api
}

type Server struct {
sessionMngr sessionManager
}

func (s *Server) NewSession(ctx context.Context) (string, error) {
return s.sessionMngr.NewSession()
}

func (s *Server) AddTransaction(ctx context.Context, sessionId string, tx *types.Transaction) error {
return s.sessionMngr.AddTransaction(sessionId, tx)
}

func (s *Server) Finalize(ctx context.Context, sessionId string) (*engine.ExecutionPayloadEnvelope, error) {
return s.sessionMngr.Finalize(sessionId)
}
55 changes: 55 additions & 0 deletions suave/builder/api/api_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package api

import (
"context"
"math/big"
"testing"

"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"
"github.com/stretchr/testify/require"
)

func TestAPI(t *testing.T) {
srv := rpc.NewServer()

builderAPI := NewServer(&nullSessionManager{})
srv.RegisterName("builder", builderAPI)

c := NewClientFromRPC(rpc.DialInProc(srv))

res0, err := c.NewSession(context.Background())
require.NoError(t, err)
require.Equal(t, res0, "1")

txn := types.NewTransaction(0, common.Address{}, big.NewInt(1), 1, big.NewInt(1), []byte{})
err = c.AddTransaction(context.Background(), "1", txn)
require.NoError(t, err)

res1, err := c.Finalize(context.Background(), "1")
require.NoError(t, err)
require.Equal(t, res1.BlockValue, big.NewInt(1))
}

type nullSessionManager struct{}

func (n *nullSessionManager) NewSession() (string, error) {
return "1", nil
}

func (n *nullSessionManager) AddTransaction(sessionId string, tx *types.Transaction) error {
return nil
}

func (n *nullSessionManager) Finalize(sessionId string) (*engine.ExecutionPayloadEnvelope, error) {
return &engine.ExecutionPayloadEnvelope{
BlockValue: big.NewInt(1),
ExecutionPayload: &engine.ExecutableData{
Number: 1,
BaseFeePerGas: big.NewInt(1),
Transactions: [][]byte{},
},
}, nil
}
68 changes: 68 additions & 0 deletions suave/builder/builder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package builder

import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie"
)

type builder struct {
config *builderConfig
txns []*types.Transaction
receipts []*types.Receipt
state *state.StateDB
gasPool *core.GasPool
gasUsed *uint64
}

type builderConfig struct {
preState *state.StateDB
header *types.Header
config *params.ChainConfig
context core.ChainContext
vmConfig vm.Config
}

func newBuilder(config *builderConfig) *builder {
gp := core.GasPool(config.header.GasLimit)
var gasUsed uint64

return &builder{
config: config,
state: config.preState.Copy(),
gasPool: &gp,
gasUsed: &gasUsed,
}
}

func (b *builder) AddTransaction(txn *types.Transaction) error {
dummyAuthor := common.Address{}

receipt, err := core.ApplyTransaction(b.config.config, b.config.context, &dummyAuthor, b.gasPool, b.state, b.config.header, txn, b.gasUsed, b.config.vmConfig)
if err != nil {
return err
}

b.txns = append(b.txns, txn)
b.receipts = append(b.receipts, receipt)

return nil
}

func (b *builder) Finalize() (*types.Block, error) {
root, err := b.state.Commit(true)
if err != nil {
return nil, err
}

header := b.config.header
header.Root = root
header.GasUsed = *b.gasUsed

block := types.NewBlock(header, b.txns, nil, b.receipts, trie.NewStackTrie(nil))
return block, nil
}
Loading

0 comments on commit 89003d3

Please sign in to comment.