Add more stuff

This commit is contained in:
Ferran Borreguero 2024-02-12 12:32:33 +00:00
parent 733df027e9
commit dd1a5901d4
5 changed files with 336 additions and 432 deletions

221
miner/builder.go Normal file
View file

@ -0,0 +1,221 @@
package miner
import (
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
)
type BuilderConfig struct {
ChainConfig *params.ChainConfig
Engine consensus.Engine
EthBackend Backend
Chain *core.BlockChain
GasCeil uint64
}
type BuilderArgs struct {
ParentHash common.Hash
FeeRecipient common.Address
Extra []byte
}
type Builder struct {
env *environment
wrk *worker
args *BuilderArgs
profitPre *big.Int
}
func NewBuilder(config *BuilderConfig, args *BuilderArgs) (*Builder, error) {
b := &Builder{
args: args,
}
b.wrk = &worker{
config: &Config{
GasCeil: config.GasCeil,
},
eth: config.EthBackend,
chainConfig: config.ChainConfig,
engine: config.Engine,
chain: config.Chain,
}
workerParams := &generateParams{
parentHash: args.ParentHash,
forceTime: false,
coinbase: args.FeeRecipient,
extra: args.Extra,
}
env, err := b.wrk.prepareWork(workerParams)
if err != nil {
return nil, err
}
env.gasPool = new(core.GasPool).AddGas(env.header.GasLimit)
b.env = env
b.profitPre = env.state.GetBalance(env.coinbase)
return b, nil
}
type SBundle struct {
BlockNumber *big.Int `json:"blockNumber,omitempty"` // if BlockNumber is set it must match DecryptionCondition!
MaxBlock *big.Int `json:"maxBlock,omitempty"`
Txs types.Transactions `json:"txs"`
RevertingHashes []common.Hash `json:"revertingHashes,omitempty"`
RefundPercent *int `json:"percent,omitempty"`
}
func (b *Builder) AddTransaction(txn *types.Transaction) (*types.SimulateTransactionResult, error) {
logs, err := b.wrk.commitTransaction(b.env, txn)
if err != nil {
return &types.SimulateTransactionResult{
Success: false,
}, nil
}
return receiptToSimResult(&types.Receipt{Logs: logs}), nil
}
func (b *Builder) AddBundles(bundles []*SBundle) error {
for _, bundle := range bundles {
if err := b.AddBundle(bundle); err != nil {
return err
}
}
return nil
}
func (b *Builder) AddBundle(bundle *SBundle) error {
work := b.env
// Assume static 28000 gas transfers for both mev-share and proposer payments
refundTransferCost := new(big.Int).Mul(big.NewInt(28000), work.header.BaseFee)
// create ephemeral addr and private key for payment txn
ephemeralPrivKey, err := crypto.GenerateKey()
if err != nil {
return err
}
ephemeralAddr := crypto.PubkeyToAddress(ephemeralPrivKey.PublicKey)
// apply bundle
profitPreBundle := work.state.GetBalance(b.env.coinbase)
if err := b.wrk.rawCommitTransactions(work, bundle.Txs); err != nil {
return err
}
profitPostBundle := work.state.GetBalance(b.env.coinbase)
// calc & refund user if bundle has multiple txns and wants refund
if len(bundle.Txs) > 1 && bundle.RefundPercent != nil {
// Note: PoC logic, this could be gamed by not sending any eth to coinbase
refundPrct := *bundle.RefundPercent
if refundPrct == 0 {
// default refund
refundPrct = 10
}
bundleProfit := new(big.Int).Sub(profitPostBundle, profitPreBundle)
refundAmt := new(big.Int).Div(bundleProfit, big.NewInt(int64(refundPrct)))
// subtract payment txn transfer costs
refundAmt = new(big.Int).Sub(refundAmt, refundTransferCost)
currNonce := work.state.GetNonce(ephemeralAddr)
// HACK to include payment txn
// multi refund block untested
userTx := bundle.Txs[0] // NOTE : assumes first txn is refund recipient
refundAddr, err := types.Sender(types.LatestSignerForChainID(userTx.ChainId()), userTx)
if err != nil {
return err
}
paymentTx, err := types.SignTx(types.NewTx(&types.LegacyTx{
Nonce: currNonce,
To: &refundAddr,
Value: refundAmt,
Gas: 28000,
GasPrice: work.header.BaseFee,
}), work.signer, ephemeralPrivKey)
if err != nil {
return err
}
// commit payment txn
if err := b.wrk.rawCommitTransactions(work, types.Transactions{paymentTx}); err != nil {
return err
}
}
return nil
}
func (b *Builder) FillPending() error {
if err := b.wrk.commitPendingTxs(b.env); err != nil {
return err
}
return nil
}
func (b *Builder) BuildBlock() (*types.Block, error) {
work := b.env
// Assume static 28000 gas transfers for both mev-share and proposer payments
refundTransferCost := new(big.Int).Mul(big.NewInt(28000), work.header.BaseFee)
// create ephemeral addr and private key for payment txn
ephemeralPrivKey, err := crypto.GenerateKey()
if err != nil {
return nil, err
}
ephemeralAddr := crypto.PubkeyToAddress(ephemeralPrivKey.PublicKey)
profitPost := work.state.GetBalance(b.env.coinbase)
proposerProfit := new(big.Int).Set(profitPost) // = post-pre-transfer_cost
proposerProfit = proposerProfit.Sub(profitPost, b.profitPre)
proposerProfit = proposerProfit.Sub(proposerProfit, refundTransferCost)
currNonce := work.state.GetNonce(ephemeralAddr)
paymentTx, err := types.SignTx(types.NewTx(&types.LegacyTx{
Nonce: currNonce,
To: &b.args.FeeRecipient,
Value: proposerProfit,
Gas: 28000,
GasPrice: work.header.BaseFee,
}), work.signer, ephemeralPrivKey)
if err != nil {
return nil, fmt.Errorf("could not sign proposer payment: %w", err)
}
// commit payment txn
if err := b.wrk.rawCommitTransactions(work, types.Transactions{paymentTx}); err != nil {
return nil, fmt.Errorf("could not sign proposer payment: %w", err)
}
block, err := b.wrk.engine.FinalizeAndAssemble(b.wrk.chain, work.header, work.state, work.txs, nil, work.receipts, nil)
if err != nil {
return nil, err
}
return block, nil
}
func receiptToSimResult(receipt *types.Receipt) *types.SimulateTransactionResult {
result := &types.SimulateTransactionResult{
Success: true,
Logs: []*types.SimulatedLog{},
}
for _, log := range receipt.Logs {
result.Logs = append(result.Logs, &types.SimulatedLog{
Addr: log.Address,
Topics: log.Topics,
Data: log.Data,
})
}
return result
}

97
miner/builder_test.go Normal file
View file

@ -0,0 +1,97 @@
package miner
import (
"fmt"
"testing"
"github.com/ethereum/go-ethereum/consensus/clique"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params"
"github.com/stretchr/testify/require"
)
func TestBuilder_AddTxn_Simple(t *testing.T) {
config, backend := newMockBuilderConfig(t)
builder, err := NewBuilder(config, &BuilderArgs{})
require.NoError(t, err)
tx1 := backend.newRandomTx(true)
res, err := builder.AddTransaction(tx1)
require.NoError(t, err)
require.True(t, res.Success)
require.Len(t, builder.env.receipts, 1)
// we cannot add the same transaction again. Note that by design the
// function does not error but returns the SimulateTransactionResult.success = false
res, err = builder.AddTransaction(tx1)
require.NoError(t, err)
require.False(t, res.Success)
require.Len(t, builder.env.receipts, 1)
}
func TestBuilder_FillTransactions(t *testing.T) {
config, backend := newMockBuilderConfig(t)
builder, err := NewBuilder(config, &BuilderArgs{})
require.NoError(t, err)
tx1 := backend.newRandomTx(true)
errArr := backend.TxPool().Add(types.Transactions{tx1}, false, true)
require.NoError(t, errArr[0])
tx2 := backend.newRandomTx(true)
errArr = backend.TxPool().Add(types.Transactions{tx2}, false, true)
require.NoError(t, errArr[0])
require.NoError(t, builder.FillPending())
require.Len(t, builder.env.receipts, 2)
require.Equal(t, tx1.Hash(), builder.env.receipts[0].TxHash)
require.Equal(t, tx2.Hash(), builder.env.receipts[1].TxHash)
}
func TestBuilder_Bundle(t *testing.T) {
t.Skip("TODO")
}
func TestBuilder_BuildBlock(t *testing.T) {
t.Skip("TODO")
config, backend := newMockBuilderConfig(t)
builder, err := NewBuilder(config, &BuilderArgs{})
require.NoError(t, err)
tx1 := backend.newRandomTx(true)
_, err = builder.AddTransaction(tx1)
require.NoError(t, err)
block, err := builder.BuildBlock()
require.NoError(t, err)
fmt.Println(block)
}
func newMockBuilderConfig(t *testing.T) (*BuilderConfig, *testWorkerBackend) {
var (
db = rawdb.NewMemoryDatabase()
config = *params.AllCliqueProtocolChanges
)
config.Clique = &params.CliqueConfig{Period: 1, Epoch: 30000}
engine := clique.New(config.Clique, db)
w, backend := newTestWorker(t, &config, engine, db, 0)
w.close()
bConfig := &BuilderConfig{
ChainConfig: w.chainConfig,
Engine: w.engine,
EthBackend: w.eth,
Chain: w.chain,
GasCeil: 10000000,
}
return bConfig, backend
}

View file

@ -1,275 +0,0 @@
package builder
import (
"fmt"
"math/big"
"sync/atomic"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus"
"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/crypto"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/suave/builder/api"
)
type builder struct {
config *builderConfig
txns []*types.Transaction
receipts []*types.Receipt
state *state.StateDB
gasPool *core.GasPool
gasUsed *uint64
signer types.Signer
args api.BuildBlockArgs
coinbasePreBalance *big.Int
engine consensus.Engine
}
type builderConfig struct {
preState *state.StateDB
header *types.Header
config *params.ChainConfig
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 {
gp := core.GasPool(config.header.GasLimit)
var gasUsed uint64
return &builder{
config: config,
state: config.preState.Copy(),
gasPool: &gp,
gasUsed: &gasUsed,
signer: types.MakeSigner(config.config, config.header.Number, config.header.Time),
coinbasePreBalance: config.preState.GetBalance(config.header.Coinbase),
}
}
func (b *builder) takeSnapshot() func() {
indx := len(b.txns)
snap := b.state.Snapshot()
return func() {
b.txns = b.txns[:indx]
b.receipts = b.receipts[:indx]
b.state.RevertToSnapshot(snap)
}
}
func (b *builder) AddBundle(bundle api.Bundle) error {
revertFn := b.takeSnapshot()
// 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)
// apply bundle
profitPreBundle := b.state.GetBalance(b.config.header.Coinbase)
if err := b.AddTransactions(bundle.Txs); err != nil {
revertFn()
return err
}
profitPostBundle := b.state.GetBalance(b.config.header.Coinbase)
// calc & refund user if bundle has multiple txns and wants refund
if len(bundle.Txs) > 1 && bundle.RefundPercent != nil {
// Note: PoC logic, this could be gamed by not sending any eth to coinbase
refundPrct := *bundle.RefundPercent
if refundPrct == 0 {
// default refund
refundPrct = 10
}
bundleProfit := new(big.Int).Sub(profitPostBundle, profitPreBundle)
refundAmt := new(big.Int).Div(bundleProfit, big.NewInt(int64(refundPrct)))
// subtract payment txn transfer costs
refundAmt = new(big.Int).Sub(refundAmt, refundTransferCost)
currNonce := b.state.GetNonce(ephemeralAddr)
// HACK to include payment txn
// multi refund block untested
userTx := bundle.Txs[0] // NOTE : assumes first txn is refund recipient
refundAddr, err := types.Sender(types.LatestSignerForChainID(userTx.ChainId()), userTx)
if err != nil {
return err
}
paymentTx, err := types.SignTx(types.NewTx(&types.LegacyTx{
Nonce: currNonce,
To: &refundAddr,
Value: refundAmt,
Gas: 28000,
GasPrice: b.config.header.BaseFee,
}), b.signer, ephemeralPrivKey)
if err != nil {
return err
}
// commit payment txn
if _, err := b.AddTransaction(paymentTx); err != nil {
revertFn()
return err
}
}
return nil
}
func (b *builder) AddTransactions(txns types.Transactions) error {
revertFn := b.takeSnapshot()
for _, txn := range txns {
if _, err := b.AddTransaction(txn); err != nil {
revertFn()
return err
}
}
return nil
}
func (b *builder) AddTransaction(txn *types.Transaction) (*types.SimulateTransactionResult, error) {
dummyAuthor := common.Address{}
vmConfig := vm.Config{
NoBaseFee: true,
}
snap := b.state.Snapshot()
b.state.SetTxContext(txn.Hash(), len(b.txns))
receipt, err := core.ApplyTransaction(b.config.config, b.config.context, &dummyAuthor, b.gasPool, b.state, b.config.header, txn, b.gasUsed, vmConfig)
if err != nil {
b.state.RevertToSnapshot(snap)
result := &types.SimulateTransactionResult{
Success: false,
Error: err.Error(),
}
return result, nil
}
b.txns = append(b.txns, txn)
b.receipts = append(b.receipts, receipt)
result := &types.SimulateTransactionResult{
Success: true,
Logs: []*types.SimulatedLog{},
}
for _, log := range receipt.Logs {
result.Logs = append(result.Logs, &types.SimulatedLog{
Addr: log.Address,
Topics: log.Topics,
Data: log.Data,
})
}
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

@ -1,122 +0,0 @@
package builder
import (
"math/big"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/core/types"
"github.com/stretchr/testify/require"
)
func TestBuilder_AddTxn_Simple(t *testing.T) {
to := common.Address{0x01, 0x10, 0xab}
mock := newMockBuilder(t)
txn := mock.state.newTransfer(t, to, big.NewInt(1))
_, err := mock.builder.AddTransaction(txn)
require.NoError(t, err)
mock.expect(t, expectedResult{
txns: []*types.Transaction{
txn,
},
balances: map[common.Address]*big.Int{
to: big.NewInt(1),
},
})
}
func newMockBuilder(t *testing.T) *mockBuilder {
// create a dummy header at 0
header := &types.Header{
Number: big.NewInt(0),
GasLimit: 1000000000000,
Time: 1000,
Difficulty: big.NewInt(1),
}
mState := newMockState(t)
m := &mockBuilder{
state: mState,
}
stateRef, err := mState.stateAt(mState.stateRoot)
require.NoError(t, err)
config := &builderConfig{
header: header,
preState: stateRef,
config: mState.chainConfig,
context: m, // m implements ChainContext with panics
}
m.builder = newBuilder(config)
return m
}
type mockBuilder struct {
builder *builder
state *mockState
}
func (m *mockBuilder) Engine() consensus.Engine {
panic("TODO")
}
func (m *mockBuilder) GetHeader(common.Hash, uint64) *types.Header {
panic("TODO")
}
type expectedResult struct {
txns []*types.Transaction
balances map[common.Address]*big.Int
}
func (m *mockBuilder) expect(t *testing.T, res expectedResult) {
// validate txns
if len(res.txns) != len(m.builder.txns) {
t.Fatalf("expected %d txns, got %d", len(res.txns), len(m.builder.txns))
}
for indx, txn := range res.txns {
if txn.Hash() != m.builder.txns[indx].Hash() {
t.Fatalf("expected txn %d to be %s, got %s", indx, txn.Hash(), m.builder.txns[indx].Hash())
}
}
// The receipts must be the same as the txns
if len(res.txns) != len(m.builder.receipts) {
t.Fatalf("expected %d receipts, got %d", len(res.txns), len(m.builder.receipts))
}
for indx, txn := range res.txns {
if txn.Hash() != m.builder.receipts[indx].TxHash {
t.Fatalf("expected receipt %d to be %s, got %s", indx, txn.Hash(), m.builder.receipts[indx].TxHash)
}
}
// The gas left in the pool must be the header gas limit minus
// the total gas consumed by all the transactions in the block.
totalGasConsumed := uint64(0)
for _, receipt := range m.builder.receipts {
totalGasConsumed += receipt.GasUsed
}
if m.builder.gasPool.Gas() != m.builder.config.header.GasLimit-totalGasConsumed {
t.Fatalf("expected gas pool to be %d, got %d", m.builder.config.header.GasLimit-totalGasConsumed, m.builder.gasPool.Gas())
}
// The 'gasUsed' must match the total gas consumed by all the transactions
if *m.builder.gasUsed != totalGasConsumed {
t.Fatalf("expected gas used to be %d, got %d", totalGasConsumed, m.builder.gasUsed)
}
// The state must match the expected balances
for addr, expectedBalance := range res.balances {
balance := m.builder.state.GetBalance(addr)
if balance.Cmp(expectedBalance) != 0 {
t.Fatalf("expected balance of %s to be %d, got %d", addr, expectedBalance, balance)
}
}
}

View file

@ -12,6 +12,7 @@ import (
"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/miner"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/suave/builder/api"
"github.com/google/uuid"
@ -40,7 +41,7 @@ type Config struct {
type SessionManager struct {
sem chan struct{}
sessions map[string]*builder
sessions map[string]*miner.Builder
sessionTimers map[string]*time.Timer
sessionsLock sync.RWMutex
blockchain blockchain
@ -65,7 +66,7 @@ func NewSessionManager(blockchain blockchain, config *Config) *SessionManager {
s := &SessionManager{
sem: sem,
sessions: make(map[string]*builder),
sessions: make(map[string]*miner.Builder),
sessionTimers: make(map[string]*time.Timer),
blockchain: blockchain,
config: config,
@ -84,41 +85,22 @@ func (s *SessionManager) NewSession(ctx context.Context, args *api.BuildBlockArg
return "", ctx.Err()
}
parent := s.blockchain.CurrentHeader()
chainConfig := s.blockchain.Config()
header := &types.Header{
ParentHash: parent.Hash(),
Number: new(big.Int).Add(parent.Number, common.Big1),
GasLimit: core.CalcGasLimit(parent.GasLimit, s.config.GasCeil),
Time: 1000, // TODO: fix this
Coinbase: common.Address{}, // TODO: fix this
Difficulty: big.NewInt(1),
builderCfg := &miner.BuilderConfig{
ChainConfig: s.blockchain.Config(),
Engine: s.blockchain.Engine(),
// TODO
}
// Set baseFee and GasLimit if we are on an EIP-1559 chain
if chainConfig.IsLondon(header.Number) {
header.BaseFee = CalcBaseFee(chainConfig, parent)
if !chainConfig.IsLondon(parent.Number) {
parentGasLimit := parent.GasLimit * chainConfig.ElasticityMultiplier()
header.GasLimit = core.CalcGasLimit(parentGasLimit, s.config.GasCeil)
}
}
stateRef, err := s.blockchain.StateAt(parent.Root)
if err != nil {
return "", err
}
cfg := &builderConfig{
preState: stateRef,
header: header,
config: s.blockchain.Config(),
context: s.blockchain,
builderArgs := &miner.BuilderArgs{
ParentHash: args.Parent,
}
id := uuid.New().String()[:7]
s.sessions[id] = newBuilder(cfg)
session, err := miner.NewBuilder(builderCfg, builderArgs)
if err != nil {
return "", err
}
s.sessions[id] = session
// start session timer
s.sessionTimers[id] = time.AfterFunc(s.config.SessionIdleTimeout, func() {
@ -140,7 +122,7 @@ func (s *SessionManager) NewSession(ctx context.Context, args *api.BuildBlockArg
return id, nil
}
func (s *SessionManager) getSession(sessionId string) (*builder, error) {
func (s *SessionManager) getSession(sessionId string) (*miner.Builder, error) {
s.sessionsLock.RLock()
defer s.sessionsLock.RUnlock()
@ -168,7 +150,7 @@ func (s *SessionManager) AddBundle(sessionId string, bundle api.Bundle) error {
if err != nil {
return err
}
return builder.AddBundle(bundle)
return builder.AddBundle(nil) // TODO: Use api.Bundle type
}
func (s *SessionManager) BuildBlock(sessionId string) error {
@ -176,7 +158,8 @@ func (s *SessionManager) BuildBlock(sessionId string) error {
if err != nil {
return err
}
return builder.BuildBlock()
_, err = builder.BuildBlock() // TODO: Return more info
return err
}
// CalcBaseFee calculates the basefee of the header.