Skip to content

Commit

Permalink
Split MsgApplyL1Txs into two messages
Browse files Browse the repository at this point in the history
MsgSetL1Attributes or MsgApplyUserDeposit
  • Loading branch information
joshklop committed Nov 28, 2024
1 parent 86afcd7 commit b427ec0
Show file tree
Hide file tree
Showing 14 changed files with 733 additions and 462 deletions.
82 changes: 57 additions & 25 deletions adapters.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ package monomer
import (
"errors"
"fmt"
"time"

bfttypes "github.com/cometbft/cometbft/types"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum/go-ethereum/common/hexutil"
ethtypes "github.com/ethereum/go-ethereum/core/types"
rolluptypes "github.com/polymerdao/monomer/x/rollup/types"
Expand Down Expand Up @@ -32,17 +35,12 @@ func AdaptPayloadTxsToCosmosTxs(ethTxs []hexutil.Bytes) (bfttypes.Txs, error) {
return nil, fmt.Errorf("marshal tx: %v", err)
}

cosmosTxs := make(bfttypes.Txs, 0, 1+numDepositTxs)
cosmosTxs = append(cosmosTxs, depositTxBytes)

cosmosNonDepositTxs, err := convertToCosmosNonDepositTxs(ethTxs[numDepositTxs:])
if err != nil {
return nil, fmt.Errorf("convert to cosmos txs: %v", err)
}

cosmosTxs = append(cosmosTxs, cosmosNonDepositTxs...)

return cosmosTxs, nil
return append(bfttypes.Txs{depositTxBytes}, cosmosNonDepositTxs...), nil
}

func countDepositTransactions(ethTxs []hexutil.Bytes) (int, error) {
Expand All @@ -65,17 +63,47 @@ func countDepositTransactions(ethTxs []hexutil.Bytes) (int, error) {
return numDepositTxs, nil
}

func packDepositTxsToCosmosTx(ethDepositTxs []hexutil.Bytes, _ string) (*rolluptypes.DepositsTx, error) { //nolint:unparam
depositTxs := make([]*rolluptypes.EthDepositTx, 0, len(ethDepositTxs))
for _, ethDepositTx := range ethDepositTxs {
depositTxs = append(depositTxs, &rolluptypes.EthDepositTx{
Tx: ethDepositTx,
})
func packDepositTxsToCosmosTx(ethDepositTxs []hexutil.Bytes, _ string) (*rolluptypes.DepositsTx, error) {
var ethL1AttributesTx ethtypes.Transaction
if err := ethL1AttributesTx.UnmarshalBinary(ethDepositTxs[0]); err != nil {
return nil, fmt.Errorf("unmarshal binary: %v", err)
}
return &rolluptypes.DepositsTx{
Deposits: &rolluptypes.MsgApplyL1Txs{
Txs: depositTxs,
l1BlockInfo, err := derive.L1BlockInfoFromBytes(&rollup.Config{}, uint64(time.Now().Unix()), ethL1AttributesTx.Data())
if err != nil {
return nil, fmt.Errorf("l1 block info from bytes: %v", err)
}
l1Attributes := &rolluptypes.MsgSetL1Attributes{
L1BlockInfo: &rolluptypes.L1BlockInfo{
Number: l1BlockInfo.Number,
Time: l1BlockInfo.Time,
BlockHash: l1BlockInfo.BlockHash[:],
SequenceNumber: l1BlockInfo.SequenceNumber,
BatcherAddr: l1BlockInfo.BatcherAddr[:],
L1FeeOverhead: l1BlockInfo.L1FeeOverhead[:],
L1FeeScalar: l1BlockInfo.L1FeeScalar[:],
BaseFeeScalar: l1BlockInfo.BaseFeeScalar,
BlobBaseFeeScalar: l1BlockInfo.BlobBaseFeeScalar,
},
EthTx: ethDepositTxs[0],
}
if l1Attributes.L1BlockInfo.BaseFee != nil {
l1Attributes.L1BlockInfo.BaseFee = l1BlockInfo.BaseFee.Bytes()
}
if l1Attributes.L1BlockInfo.BlobBaseFee != nil {
l1Attributes.L1BlockInfo.BlobBaseFee = l1BlockInfo.BlobBaseFee.Bytes()
}

depositTxs := make([]*rolluptypes.MsgApplyUserDeposit, 0)
if len(ethDepositTxs) > 1 {
for _, ethDepositTx := range ethDepositTxs[1:] {
depositTxs = append(depositTxs, &rolluptypes.MsgApplyUserDeposit{
Tx: ethDepositTx,
})
}
}
return &rolluptypes.DepositsTx{
L1Attributes: l1Attributes,
UserDeposits: depositTxs,
}, nil
}

Expand All @@ -98,31 +126,35 @@ func AdaptCosmosTxsToEthTxs(cosmosTxs bfttypes.Txs) (ethtypes.Transactions, erro
return ethtypes.Transactions{}, nil
}
txsBytes := cosmosTxs.ToSliceOfBytes()
ethTxs, err := GetDepositTxs(txsBytes)
ethTxs, err := GetDepositTxs(txsBytes[0])
if err != nil {
return nil, fmt.Errorf("get deposit txs: %v", err)
}
for _, txBytes := range txsBytes[1:] {
ethTxs = append(ethTxs, AdaptNonDepositCosmosTxToEthTx(txBytes))
if len(txsBytes) > 1 {
for _, txBytes := range txsBytes[1:] {
ethTxs = append(ethTxs, AdaptNonDepositCosmosTxToEthTx(txBytes))
}
}

return ethTxs, nil
}

func GetDepositTxs(txsBytes [][]byte) (ethtypes.Transactions, error) {
func GetDepositTxs(cosmosDepositTx []byte) (ethtypes.Transactions, error) {
msg := new(rolluptypes.DepositsTx)
if err := msg.Unmarshal(txsBytes[0]); err != nil {
if err := msg.Unmarshal(cosmosDepositTx); err != nil {
return nil, fmt.Errorf("unmarshal MsgL1Txs msg: %v", err)
}
ethTxsBytes := msg.GetDeposits().Txs
if len(ethTxsBytes) == 0 {
return nil, errL1AttributesNotFound
var ethL1AttributesTx ethtypes.Transaction
if err := ethL1AttributesTx.UnmarshalBinary(msg.L1Attributes.EthTx); err != nil {
return nil, fmt.Errorf("unmarshal binary l1 attributes tx: %v", err)
}
txs := make(ethtypes.Transactions, 0, len(ethTxsBytes)+len(txsBytes)-1)
txs := ethtypes.Transactions{&ethL1AttributesTx}

ethTxsBytes := msg.GetUserDeposits()
for _, userDepositTx := range ethTxsBytes {
var tx ethtypes.Transaction
if err := tx.UnmarshalBinary(userDepositTx.Tx); err != nil {
return nil, fmt.Errorf("unmarshal binary: %v", err)
return nil, fmt.Errorf("unmarshal binary user deposit tx: %v", err)
}
if !tx.IsDepositTx() {
return nil, errors.New("MsgL1Tx contains non-deposit tx")
Expand Down
2 changes: 1 addition & 1 deletion builder/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ func TestBuildRollupTxs(t *testing.T) {

// Verify deposit and withdrawal msg event
expectedEvents := []string{
"/rollup.v1.MsgApplyL1Txs",
"/rollup.v1.MsgSetL1Attributes",
"/rollup.v1.MsgInitiateWithdrawal",
}

Expand Down
17 changes: 4 additions & 13 deletions docs/docs/learn/deposits.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,22 +42,13 @@ Because the OP stack and the EngineAPI are Ethereum specific, Monomer's chief re

In the `x/rollup` module, Monomer defines a custom Cosmos-SDK message type to carry deposit transaction data.

```go
// MsgApplyL1Txs defines the message for applying all L1 system and user deposit txs.
message MsgApplyL1Txs {
// Array of bytes where each bytes is a eth.Transaction.MarshalBinary tx.
// The first tx must be the L1 system deposit tx, and the rest are user txs if present.
repeated bytes tx_bytes = 1;
}
```
For each rollup block, all deposit transactions sourced from the L1 are batched into a single `DepositTxs` transaction. The engine now awaits the `op-node`'s request to finalize a block. When that request comes:

For each rollup block, all deposit transactions sourced from the L1 are batched into a single `MsgApplyL1Txs` message, which in turn is bundled into a single `cometbft` style transaction and cached in the engine. The engine now awaits the `op-node`'s request to finalize a block. When that request comes:

5. the cached transaction is passed to Monomer's `builder.Build()`, which encapsulates the Cosmos-SDK appchain. In accordance with the OP stack spec, the transaction that packs the `MsgApplyL1Txs` message is the first transaction in each L2 block.
5. the cached transaction is passed to Monomer's `builder.Build()`, which encapsulates the Cosmos-SDK appchain. In accordance with the OP stack spec, the `DepositTxs` transaction is the first transaction in each L2 block.

## The Cosmos-SDK Appchain

The Cosmos Appchain now builds a block as usual. Monomer's contribution here is the `x/rollup` module, and its keeper method `processL1UserDepositTxs`, which:
The Cosmos Appchain now builds a block as usual. Monomer's contribution here is the `x/rollup` module, which:

6. unpacks the `MsgApplyL1Txs` `tx_bytes` field into a slice of `eth.Transaction` objects, minting ETH according to embedded values
6. unpacks the deposit transaction, minting ETH according to embedded values
7. emits events for each deposit
2 changes: 1 addition & 1 deletion mempool/mempool.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func (p *Pool) Enqueue(userTxn comettypes.Tx) (err error) {
// Attempt to adapt the Cosmos transaction to an Ethereum deposit transaction.
// If the adaptation succeeds, it indicates that the
// user transaction is a deposit transaction, which is not allowed in the pool.
if _, err := monomer.GetDepositTxs([][]byte{userTxn}); err == nil {
if _, err := monomer.GetDepositTxs(userTxn); err == nil {
return errors.New("deposit txs are not allowed in the pool")
}

Expand Down
6 changes: 4 additions & 2 deletions mempool/mempool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,13 @@ func TestMempool(t *testing.T) {
})

t.Run("deposit transaction", func(t *testing.T) {
_, depositTx, _ := testutils.GenerateEthTxs(t)
l1AttributesTx, depositTx, _ := testutils.GenerateEthTxs(t)
l1AttributesTxBytes, err := l1AttributesTx.MarshalBinary()
require.NoError(t, err)
depositTxBytes, err := depositTx.MarshalBinary()
require.NoError(t, err)

cosmosTxs, err := monomer.AdaptPayloadTxsToCosmosTxs([]hexutil.Bytes{depositTxBytes})
cosmosTxs, err := monomer.AdaptPayloadTxsToCosmosTxs([]hexutil.Bytes{l1AttributesTxBytes, depositTxBytes})
require.NoError(t, err)
require.ErrorContains(t, pool.Enqueue(cosmosTxs[0]), "deposit txs are not allowed in the pool")
})
Expand Down
34 changes: 21 additions & 13 deletions proto/rollup/v1/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@ option go_package = "github.com/polymerdao/monomer/x/rollup/types";
// Msg defines all tx endpoints for the x/rollup module.
service Msg {
option (cosmos.msg.v1.service) = true;
// ApplyL1Txs defines a method for applying applying all L1 system and user deposit txs.
rpc ApplyL1Txs(MsgApplyL1Txs) returns (MsgApplyL1TxsResponse);

// SetL1Attributes sets the l1 attributes in the L2 state.
rpc SetL1Attributes(MsgSetL1Attributes) returns (MsgSetL1AttributesResponse);

// ApplyUserDeposit defines a method for applying a user deposit tx.
rpc ApplyUserDeposit(MsgApplyUserDeposit) returns (MsgApplyUserDepositResponse);

// InitiateWithdrawal defines a method for initiating a withdrawal from L2 to L1.
rpc InitiateWithdrawal(MsgInitiateWithdrawal) returns (MsgInitiateWithdrawalResponse);
Expand All @@ -28,23 +32,27 @@ service Msg {

// DepositsTx is the Cosmos SDK transaction type that wraps OP Stack deposit transactions.
message DepositsTx {
MsgApplyL1Txs deposits = 1;
MsgSetL1Attributes l1_attributes = 1;
repeated MsgApplyUserDeposit user_deposits = 2;
}

// DepositTx is a eth deposit tx.
message EthDepositTx {
// tx is the marshaled Ethereum Deposit tx.
bytes tx = 1;
// MsgSetL1Attributes is the l1 attributes message.
message MsgSetL1Attributes {
L1BlockInfo l1_block_info = 1;
bytes eth_tx = 2;
}

// MsgApplyL1Txs defines the message for applying all L1 system and user deposit txs.
message MsgApplyL1Txs {
// txs are all of the system and user deposit txs.
repeated EthDepositTx txs = 1;
// MsgSetL1AttributesResponse defines the SetL1Attributes response type.
message MsgSetL1AttributesResponse {}

// MsgApplyUserDeposit is a eth deposit tx.
message MsgApplyUserDeposit {
// tx is the marshaled Ethereum Deposit tx.
bytes tx = 1;
}

// MsgApplyL1TxsResponse defines the Msg/ApplyL1Txs response type.
message MsgApplyL1TxsResponse {}
// MsgApplyUserDepositResponse defines the ApplyUserDeposit response type.
message MsgApplyUserDepositResponse {}

// MsgInitiateWithdrawal defines the message for initiating an L2 withdrawal.
message MsgInitiateWithdrawal {
Expand Down
Loading

0 comments on commit b427ec0

Please sign in to comment.