Skip to content

Commit

Permalink
Build ExecutionPayload from block data (#24)
Browse files Browse the repository at this point in the history
Previously, only deposit transactions were included.
  • Loading branch information
NiloCK authored May 9, 2024
1 parent fcf9c54 commit d5881eb
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 52 deletions.
28 changes: 16 additions & 12 deletions builder/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,16 +85,17 @@ func (b *Builder) Rollback(head, safe, finalized common.Hash) error {
}

type Payload struct {
// Transactions functions as an inclusion list.
Transactions bfttypes.Txs
// InjectedTransactions functions as an inclusion list. It contains transactions
// from the consensus layer that must be included in the block.
InjectedTransactions bfttypes.Txs
// TODO: make the gas limit actually be enforced. Need to translate between cosmos and op gas limit.
GasLimit uint64
Timestamp uint64
NoTxPool bool
}

func (b *Builder) Build(payload *Payload) error {
txs := slices.Clone(payload.Transactions) // Shallow clone is ok, we just don't want to modify the slice itself.
func (b *Builder) Build(payload *Payload) (*monomer.Block, error) {
txs := slices.Clone(payload.InjectedTransactions) // Shallow clone is ok, we just don't want to modify the slice itself.
if !payload.NoTxPool {
for {
// TODO there is risk of losing txs if mempool db fails.
Expand All @@ -120,7 +121,7 @@ func (b *Builder) Build(payload *Payload) error {
currentHeight := info.GetLastBlockHeight()
currentHead := b.blockStore.BlockByNumber(currentHeight)
if currentHead == nil {
return fmt.Errorf("block not found at height: %d", currentHeight)
return nil, fmt.Errorf("block not found at height: %d", currentHeight)
}
header := &monomer.Header{
ChainID: b.chainID,
Expand All @@ -141,7 +142,7 @@ func (b *Builder) Build(payload *Payload) error {
Tx: tx,
})
if resp.IsErr() {
return fmt.Errorf("deliver tx: %v", resp.GetLog())
return nil, fmt.Errorf("deliver tx: %v", resp.GetLog())
}
txResults = append(txResults, &abcitypes.TxResult{
Height: currentHeight + 1,
Expand All @@ -155,23 +156,26 @@ func (b *Builder) Build(payload *Payload) error {
})
b.app.Commit()

// Append block.
b.blockStore.AddBlock(&monomer.Block{
block := &monomer.Block{
Header: header,
Txs: txs,
})
}

// Append block.
b.blockStore.AddBlock(block)
// Index txs.
if err := b.txStore.Add(txResults); err != nil {
return fmt.Errorf("add tx results: %v", err)
return nil, fmt.Errorf("add tx results: %v", err)
}
// Publish events.
for _, txResult := range txResults {
if err := b.eventBus.PublishEventTx(bfttypes.EventDataTx{
TxResult: *txResult,
}); err != nil {
return fmt.Errorf("publish event tx: %v", err)
return nil, fmt.Errorf("publish event tx: %v", err)
}
}

// TODO publish other things like new blocks.
return nil
return block, nil
}
22 changes: 12 additions & 10 deletions builder/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,14 @@ func TestBuild(t *testing.T) {
)

payload := &builder.Payload{
Transactions: bfttypes.ToTxs(inclusionListTxs),
GasLimit: 0,
Timestamp: g.Time + 1,
NoTxPool: test.noTxPool,
InjectedTransactions: bfttypes.ToTxs(inclusionListTxs),
GasLimit: 0,
Timestamp: g.Time + 1,
NoTxPool: test.noTxPool,
}
preBuildInfo := app.Info(abcitypes.RequestInfo{})
require.NoError(t, b.Build(payload))
builtBlock, err := b.Build(payload)
require.NoError(t, err)
postBuildInfo := app.Info(abcitypes.RequestInfo{})

// Application.
Expand Down Expand Up @@ -154,6 +155,7 @@ func TestBuild(t *testing.T) {
Txs: bfttypes.ToTxs(allTxs),
}
wantBlock.Hash()
require.Equal(t, wantBlock, builtBlock)
require.Equal(t, wantBlock, gotBlock)

// Tx store and event bus.
Expand Down Expand Up @@ -216,11 +218,11 @@ func TestRollback(t *testing.T) {
kvs := map[string]string{
"test": "test",
}
require.NoError(t, b.Build(&builder.Payload{
Timestamp: g.Time + 1,
Transactions: bfttypes.ToTxs(testapp.ToTxs(t, kvs)),
}))
block := blockStore.HeadBlock()
block, err := b.Build(&builder.Payload{
Timestamp: g.Time + 1,
InjectedTransactions: bfttypes.ToTxs(testapp.ToTxs(t, kvs)),
})
require.NoError(t, err)
require.NotNil(t, block)
require.NoError(t, blockStore.UpdateLabel(eth.Unsafe, block.Hash()))
require.NoError(t, blockStore.UpdateLabel(eth.Safe, block.Hash()))
Expand Down
63 changes: 49 additions & 14 deletions engine/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum/go-ethereum/beacon/engine"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/polymerdao/monomer"
"github.com/polymerdao/monomer/app/peptide/store"
"github.com/polymerdao/monomer/builder"
Expand All @@ -26,20 +27,28 @@ type EngineAPI struct {
txValidator TxValidator
blockStore BlockStore
currentPayloadAttributes *monomer.PayloadAttributes
adapter monomer.PayloadTxAdapter
ethCosmosAdapter monomer.PayloadTxAdapter
cosmosEthAdapter monomer.CosmosTxAdapter
lock sync.RWMutex
}

type TxValidator interface {
CheckTx(abci.RequestCheckTx) abci.ResponseCheckTx
}

func NewEngineAPI(b *builder.Builder, txValidator TxValidator, adapter monomer.PayloadTxAdapter, blockStore BlockStore) *EngineAPI {
func NewEngineAPI(
b *builder.Builder,
txValidator TxValidator,
ethCosmosAdapter monomer.PayloadTxAdapter,
cosmosEthAdapter monomer.CosmosTxAdapter,
blockStore BlockStore,
) *EngineAPI {
return &EngineAPI{
txValidator: txValidator,
blockStore: blockStore,
builder: b,
adapter: adapter,
txValidator: txValidator,
blockStore: blockStore,
builder: b,
ethCosmosAdapter: ethCosmosAdapter,
cosmosEthAdapter: cosmosEthAdapter,
}
}

Expand Down Expand Up @@ -159,7 +168,7 @@ func (e *EngineAPI) ForkchoiceUpdatedV3(
return nil, engine.InvalidPayloadAttributes.With(errors.New("gas limit not provided"))
}

cosmosTxs, err := e.adapter(pa.Transactions)
cosmosTxs, err := e.ethCosmosAdapter(pa.Transactions)
if err != nil {
return nil, engine.InvalidPayloadAttributes.With(fmt.Errorf("convert payload attributes txs to cosmos txs: %v", err))
}
Expand Down Expand Up @@ -237,16 +246,42 @@ func (e *EngineAPI) GetPayloadV3(payloadID engine.PayloadID) (*eth.ExecutionPayl

// TODO: handle time slot based block production
// for now assume block is sealed by this call
if err := e.builder.Build(&builder.Payload{
Transactions: e.currentPayloadAttributes.CosmosTxs,
GasLimit: e.currentPayloadAttributes.GasLimit,
Timestamp: e.currentPayloadAttributes.Timestamp,
NoTxPool: e.currentPayloadAttributes.NoTxPool,
}); err != nil {
block, err := e.builder.Build(&builder.Payload{
InjectedTransactions: e.currentPayloadAttributes.CosmosTxs,
GasLimit: e.currentPayloadAttributes.GasLimit,
Timestamp: e.currentPayloadAttributes.Timestamp,
NoTxPool: e.currentPayloadAttributes.NoTxPool,
})
if err != nil {
log.Panicf("failed to commit block: %v", err) // TODO error handling. An error here is potentially a big problem.
}
payloadEnvelope := e.currentPayloadAttributes.ToExecutionPayloadEnvelope(e.blockStore.HeadBlock().Hash())

txs, err := e.cosmosEthAdapter(block.Txs)
if err != nil {
return nil, engine.GenericServerError.With(fmt.Errorf("convert cosmos txs to eth txs: %v", err))
}

txBytes := make([]hexutil.Bytes, len(txs))
for i, tx := range txs {
txBytes[i], err = tx.MarshalBinary()
if err != nil {
return nil, engine.GenericServerError.With(fmt.Errorf("marshal tx binary: %v", err))
}
}

payloadEnvelope := &eth.ExecutionPayloadEnvelope{
ExecutionPayload: &eth.ExecutionPayload{
ParentHash: e.currentPayloadAttributes.ParentHash,
BlockNumber: hexutil.Uint64(e.currentPayloadAttributes.Height),
BlockHash: block.Hash(),
FeeRecipient: e.currentPayloadAttributes.SuggestedFeeRecipient,
Timestamp: hexutil.Uint64(e.currentPayloadAttributes.Timestamp),
PrevRandao: e.currentPayloadAttributes.PrevRandao,
Withdrawals: e.currentPayloadAttributes.Withdrawals,
Transactions: txBytes,
GasLimit: hexutil.Uint64(e.currentPayloadAttributes.GasLimit),
},
}
// remove payload
e.currentPayloadAttributes = nil

Expand Down
16 changes: 0 additions & 16 deletions monomer.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,22 +212,6 @@ func hashDataAsBinary(h hash.Hash, data any) {
}
}

func (p *PayloadAttributes) ToExecutionPayloadEnvelope(blockHash common.Hash) *opeth.ExecutionPayloadEnvelope {
return &opeth.ExecutionPayloadEnvelope{
ExecutionPayload: &opeth.ExecutionPayload{
ParentHash: p.ParentHash,
BlockNumber: hexutil.Uint64(p.Height),
BlockHash: blockHash,
FeeRecipient: p.SuggestedFeeRecipient,
Timestamp: hexutil.Uint64(p.Timestamp),
PrevRandao: p.PrevRandao,
Withdrawals: p.Withdrawals,
Transactions: p.Transactions,
GasLimit: hexutil.Uint64(p.GasLimit),
},
}
}

// ValidForkchoiceUpdateResult returns a valid ForkchoiceUpdateResult with given head block hash.
func ValidForkchoiceUpdateResult(headBlockHash *common.Hash, id *engine.PayloadID) *opeth.ForkchoiceUpdatedResult {
return &opeth.ForkchoiceUpdatedResult{
Expand Down
1 change: 1 addition & 0 deletions node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ func (n *Node) Run(ctx context.Context, env *environment.Env) error {
builder.New(mpool, n.app, blockStore, txStore, eventBus, n.genesis.ChainID),
n.app,
n.adaptPayloadTxsToCosmosTxs,
n.adaptCosmosTxsToEthTxs,
blockStore,
),
},
Expand Down

0 comments on commit d5881eb

Please sign in to comment.