Build block?

This commit is contained in:
Ferran Borreguero 2024-02-07 09:32:46 +00:00
parent 7d72612dbb
commit 733df027e9
6 changed files with 144 additions and 16 deletions

View file

@ -33,4 +33,5 @@ type API interface {
NewSession(ctx context.Context, args *BuildBlockArgs) (string, error) NewSession(ctx context.Context, args *BuildBlockArgs) (string, error)
AddTransaction(ctx context.Context, sessionId string, tx *types.Transaction) (*types.SimulateTransactionResult, error) AddTransaction(ctx context.Context, sessionId string, tx *types.Transaction) (*types.SimulateTransactionResult, error)
AddBundle(ctx context.Context, sessionId string, bundle Bundle) error AddBundle(ctx context.Context, sessionId string, bundle Bundle) error
BuildBlock(ctx context.Context, sessionId string) error
} }

View file

@ -44,3 +44,7 @@ func (a *APIClient) AddTransaction(ctx context.Context, sessionId string, tx *ty
func (a *APIClient) AddBundle(ctx context.Context, sessionId string, bundle Bundle) error { func (a *APIClient) AddBundle(ctx context.Context, sessionId string, bundle Bundle) error {
return a.rpc.CallContext(ctx, nil, "suavex_addBundle", sessionId, bundle) return a.rpc.CallContext(ctx, nil, "suavex_addBundle", sessionId, bundle)
} }
func (a *APIClient) BuildBlock(ctx context.Context, sessionId string) error {
return a.rpc.CallContext(ctx, nil, "suavex_buildBlock", sessionId)
}

View file

@ -11,6 +11,7 @@ type SessionManager interface {
NewSession(context.Context, *BuildBlockArgs) (string, error) NewSession(context.Context, *BuildBlockArgs) (string, error)
AddTransaction(sessionId string, tx *types.Transaction) (*types.SimulateTransactionResult, error) AddTransaction(sessionId string, tx *types.Transaction) (*types.SimulateTransactionResult, error)
AddBundle(sessionId string, bundle Bundle) error AddBundle(sessionId string, bundle Bundle) error
BuildBlock(sessionId string) error
} }
func NewServer(s SessionManager) *Server { func NewServer(s SessionManager) *Server {
@ -36,6 +37,10 @@ func (s *Server) AddBundle(ctx context.Context, sessionId string, bundle Bundle)
return s.sessionMngr.AddBundle(sessionId, bundle) return s.sessionMngr.AddBundle(sessionId, bundle)
} }
func (s *Server) BuildBlock(ctx context.Context, sessionId string) error {
return s.sessionMngr.BuildBlock(sessionId)
}
type MockServer struct { type MockServer struct {
} }
@ -50,3 +55,7 @@ func (s *MockServer) AddTransaction(ctx context.Context, sessionId string, tx *t
func (s *MockServer) AddBundle(ctx context.Context, sessionId string, bundle Bundle) error { func (s *MockServer) AddBundle(ctx context.Context, sessionId string, bundle Bundle) error {
return nil return nil
} }
func (s *MockServer) BuildBlock(ctx context.Context) error {
return nil
}

View file

@ -41,3 +41,7 @@ func (nullSessionManager) AddTransaction(sessionId string, tx *types.Transaction
func (nullSessionManager) AddBundle(sessionId string, bundle Bundle) error { func (nullSessionManager) AddBundle(sessionId string, bundle Bundle) error {
return nil return nil
} }
func (nullSessionManager) BuildBlock(sessionId string) error {
return nil
}

View file

@ -1,9 +1,13 @@
package builder package builder
import ( import (
"fmt"
"math/big" "math/big"
"sync/atomic"
"time"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
@ -21,6 +25,9 @@ type builder struct {
gasPool *core.GasPool gasPool *core.GasPool
gasUsed *uint64 gasUsed *uint64
signer types.Signer signer types.Signer
args api.BuildBlockArgs
coinbasePreBalance *big.Int
engine consensus.Engine
} }
type builderConfig struct { type builderConfig struct {
@ -28,6 +35,14 @@ type builderConfig struct {
header *types.Header header *types.Header
config *params.ChainConfig config *params.ChainConfig
context core.ChainContext context core.ChainContext
chainReader consensus.ChainHeaderReader
// newpayloadTimeout is the maximum timeout allowance for creating payload.
// The default value is 2 seconds but node operator can set it to arbitrary
// large value. A large timeout allowance may cause Geth to fail creating
// a non-empty payload within the specified time and eventually miss the slot
// in case there are some computation expensive transactions in txpool.
newpayloadTimeout time.Duration
} }
func newBuilder(config *builderConfig) *builder { func newBuilder(config *builderConfig) *builder {
@ -40,6 +55,7 @@ func newBuilder(config *builderConfig) *builder {
gasPool: &gp, gasPool: &gp,
gasUsed: &gasUsed, gasUsed: &gasUsed,
signer: types.MakeSigner(config.config, config.header.Number, config.header.Time), signer: types.MakeSigner(config.config, config.header.Number, config.header.Time),
coinbasePreBalance: config.preState.GetBalance(config.header.Coinbase),
} }
} }
@ -171,3 +187,89 @@ func (b *builder) AddTransaction(txn *types.Transaction) (*types.SimulateTransac
return result, nil return result, nil
} }
func (b *builder) commitPendingTxs() error {
interrupt := new(atomic.Int32)
timer := time.AfterFunc(b.config.newpayloadTimeout, func() {
interrupt.Store(commitInterruptTimeout)
})
defer timer.Stop()
if err := b.fillTransactions(); err != nil {
return err
}
return nil
}
func (b *builder) fillTransactions() error {
// Split the pending transactions into locals and remotes
// Fill the block with all available pending transactions.
pending := w.eth.TxPool().Pending(true)
localTxs, remoteTxs := make(map[common.Address]types.Transactions), pending
for _, account := range w.eth.TxPool().Locals() {
if txs := remoteTxs[account]; len(txs) > 0 {
delete(remoteTxs, account)
localTxs[account] = txs
}
}
if len(localTxs) > 0 {
txs := types.NewTransactionsByPriceAndNonce(env.signer, localTxs, env.header.BaseFee)
if err := b.commitTransactions(env, txs, interrupt); err != nil {
return err
}
}
if len(remoteTxs) > 0 {
txs := types.NewTransactionsByPriceAndNonce(env.signer, remoteTxs, env.header.BaseFee)
if err := w.commitTransactions(env, txs, interrupt); err != nil {
return err
}
}
return nil
}
func (b *builder) BuildBlock() error {
if b.args.FillPending {
if err := b.commitPendingTxs(); err != nil {
return err
}
}
// create ephemeral addr and private key for payment txn
ephemeralPrivKey, err := crypto.GenerateKey()
if err != nil {
return err
}
ephemeralAddr := crypto.PubkeyToAddress(ephemeralPrivKey.PublicKey)
// Assume static 28000 gas transfers for both mev-share and proposer payments
refundTransferCost := new(big.Int).Mul(big.NewInt(28000), b.config.header.BaseFee)
profitPost := b.state.GetBalance(b.config.header.Coinbase)
proposerProfit := new(big.Int).Set(profitPost) // = post-pre-transfer_cost
proposerProfit = proposerProfit.Sub(profitPost, b.coinbasePreBalance)
proposerProfit = proposerProfit.Sub(proposerProfit, refundTransferCost)
currNonce := b.state.GetNonce(ephemeralAddr)
paymentTx, err := types.SignTx(types.NewTx(&types.LegacyTx{
Nonce: currNonce,
To: &b.args.FeeRecipient,
Value: proposerProfit,
Gas: 28000,
GasPrice: b.config.header.BaseFee,
}), b.signer, ephemeralPrivKey)
if err != nil {
return fmt.Errorf("could not sign proposer payment: %w", err)
}
// commit payment txn
if _, err := b.AddTransaction(paymentTx); err != nil {
return err
}
block, err := b.engine.FinalizeAndAssemble(b.config.chainReader, b.config.header, b.state, b.txns, []*types.Header{}, b.receipts, b.args.Withdrawals)
if err != nil {
return err
}
fmt.Println("-- block --", block)
return nil
}

View file

@ -171,6 +171,14 @@ func (s *SessionManager) AddBundle(sessionId string, bundle api.Bundle) error {
return builder.AddBundle(bundle) return builder.AddBundle(bundle)
} }
func (s *SessionManager) BuildBlock(sessionId string) error {
builder, err := s.getSession(sessionId)
if err != nil {
return err
}
return builder.BuildBlock()
}
// CalcBaseFee calculates the basefee of the header. // CalcBaseFee calculates the basefee of the header.
func CalcBaseFee(config *params.ChainConfig, parent *types.Header) *big.Int { func CalcBaseFee(config *params.ChainConfig, parent *types.Header) *big.Int {
// If the current block is the first EIP-1559 block, return the InitialBaseFee. // If the current block is the first EIP-1559 block, return the InitialBaseFee.