Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add check for L1 attributes transactions #172

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion adapters.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,27 @@ func AdaptPayloadTxsToCosmosTxs(ethTxs []hexutil.Bytes, signTx txSigner, from st

// Count number of deposit txs.
var numDepositTxs int
var l1AttributesTxs int
for _, txBytes := range ethTxs {
var tx ethtypes.Transaction
if err := tx.UnmarshalBinary(txBytes); err != nil {
return nil, fmt.Errorf("unmarshal binary: %v", err)
}
if tx.IsDepositTx() {
numDepositTxs++
if IsL1AttributesTx(&tx) {
l1AttributesTxs++
}
} else {
break // Assume deposit transactions must come first.
}
}
if numDepositTxs == 0 {
if l1AttributesTxs == 0 {
AnkushinDaniil marked this conversation as resolved.
Show resolved Hide resolved
return nil, errL1AttributesNotFound
}
if l1AttributesTxs > 1 {
return nil, errors.New("more than one L1 attributes tx found")
}

// Pack deposit txs into an SDK Msg.
var depositTxsBytes [][]byte
Expand Down
42 changes: 42 additions & 0 deletions adapters_l1_attributes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package monomer

import (
"math/big"

"github.com/ethereum-optimism/optimism/op-node/chaincfg"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
ethtypes "github.com/ethereum/go-ethereum/core/types"
)

// L1 Attributes Deposited Transaction
// https://specs.optimism.io/protocol/deposits.html#l1-attributes-deposited-transaction
// After Ecotone update we no longer need to check calldata
//nolint:lll
// https://github.com/ethereum-optimism/optimism/blob/7e5b9faa14d1f0a8d3cf919654162c4a402f1f38/packages/contracts-bedrock/src/L2/L1Block.sol#L125C44-L125C56

func IsL1AttributesTx(tx *ethtypes.Transaction) bool {
if mint := tx.Mint(); mint != nil && mint.Cmp(big.NewInt(0)) != 0 {
return false
}

rollupCfg := chaincfg.Mainnet // TODO: Can we get this?
timestamp := uint64(tx.Time().Unix())

_, err := derive.L1BlockInfoFromBytes(rollupCfg, timestamp, tx.Data())
if err != nil {
return false
}

isSystemTransaction := true
gas := uint64(150_000_000) //nolint:mnd
if rollupCfg.IsRegolith(timestamp) {
isSystemTransaction = false
gas = derive.RegolithSystemTxGas
}

return tx.To().Cmp(derive.L1BlockAddress) == 0 &&
tx.Value().Cmp(big.NewInt(0)) == 0 &&
tx.Gas() == gas &&
tx.IsSystemTx() == isSystemTransaction
// TODO: Can we check From field?
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think so.

This tx ought to be prepared by the op-node, and populated according to the documented defaults (https://specs.optimism.io/protocol/deposits.html#l1-attributes-deposited-transaction).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but we don't have tx.From() and I can't find another method to extract it.

}
126 changes: 126 additions & 0 deletions adapters_l1_attributes_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package monomer_test

import (
"math/big"
"testing"
"time"

"github.com/ethereum-optimism/optimism/op-node/chaincfg"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/trie"
"github.com/polymerdao/monomer"
"github.com/polymerdao/monomer/testutils"
"github.com/stretchr/testify/require"
)

func TestIsL1AttributesTx(t *testing.T) {
l1InfoTx, depositTx, cosmosEthTx := testutils.GenerateEthTxs(t)
invalidToAddress := common.HexToAddress("0x01")

l1Block := types.NewBlock(&types.Header{
BaseFee: big.NewInt(10),
Difficulty: common.Big0,
Number: big.NewInt(0),
Time: uint64(0),
}, nil, nil, nil, trie.NewStackTrie(nil))
l1InfoRawTx, err := derive.L1InfoDeposit(chaincfg.Mainnet, eth.SystemConfig{}, 0, eth.BlockToInfo(l1Block), l1Block.Time())
require.NoError(t, err)
preEcotoneL1InfoTx := types.NewTx(l1InfoRawTx)
preEcotoneL1InfoTx.SetTime(time.Unix(0, 0))

tests := []struct {
name string
tx *types.Transaction
want bool
}{
{
name: "generated l1InfoTx",
tx: l1InfoTx,
want: true,
},
{
name: "generated depositTx",
tx: depositTx,
want: false,
},
{
name: "generated cosmosEthTx",
tx: cosmosEthTx,
want: false,
},
{
name: "to is invalid",
tx: types.NewTx(&types.DepositTx{
To: &invalidToAddress,
Mint: l1InfoTx.Mint(),
Value: l1InfoTx.Value(),
Gas: l1InfoTx.Gas(),
IsSystemTransaction: l1InfoTx.IsSystemTx(),
Data: l1InfoTx.Data(),
}),
want: false,
},
{
name: "gas is invalid",
tx: types.NewTx(&types.DepositTx{
To: l1InfoTx.To(),
Mint: l1InfoTx.Mint(),
Value: l1InfoTx.Value(),
Gas: 150_000_000,
IsSystemTransaction: l1InfoTx.IsSystemTx(),
Data: l1InfoTx.Data(),
}),
want: false,
},
{
name: "data is short",
tx: types.NewTx(&types.DepositTx{
To: l1InfoTx.To(),
Mint: l1InfoTx.Mint(),
Value: l1InfoTx.Value(),
Gas: l1InfoTx.Gas(),
IsSystemTransaction: l1InfoTx.IsSystemTx(),
Data: l1InfoTx.Data()[:len(derive.L1InfoFuncEcotoneSignature)],
}),
want: false,
},
{
name: "data is invalid",
tx: types.NewTx(&types.DepositTx{
To: l1InfoTx.To(),
Mint: l1InfoTx.Mint(),
Value: l1InfoTx.Value(),
Gas: l1InfoTx.Gas(),
IsSystemTransaction: l1InfoTx.IsSystemTx(),
Data: []byte("invalid"),
}),
want: false,
},
{
name: "IsSystemTransaction is true",
tx: types.NewTx(&types.DepositTx{
To: l1InfoTx.To(),
Mint: l1InfoTx.Mint(),
Value: l1InfoTx.Value(),
Gas: l1InfoTx.Gas(),
IsSystemTransaction: true,
Data: l1InfoTx.Data(),
}),
want: false,
},
{
name: "Before Ecotone update",
tx: preEcotoneL1InfoTx,
want: true,
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
require.Equal(t, test.want, monomer.IsL1AttributesTx(test.tx))
})
}
}
10 changes: 4 additions & 6 deletions testutils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ import (
"math/big"
"math/rand"
"testing"
"time"

"github.com/cockroachdb/pebble"
"github.com/cockroachdb/pebble/vfs"
cometdb "github.com/cometbft/cometbft-db"
bfttypes "github.com/cometbft/cometbft/types"
dbm "github.com/cosmos/cosmos-db"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-node/chaincfg"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-service/testutils"
Expand Down Expand Up @@ -66,10 +67,7 @@ func NewLocalMemDB(t *testing.T) *localdb.DB {
// The transactions are not meant to be executed.
func GenerateEthTxs(t *testing.T) (*gethtypes.Transaction, *gethtypes.Transaction, *gethtypes.Transaction) {
l1Block := GenerateL1Block()
l1InfoRawTx, err := derive.L1InfoDeposit(&rollup.Config{
Genesis: rollup.Genesis{L2: eth.BlockID{Number: 0}},
L2ChainID: big.NewInt(1234),
}, eth.SystemConfig{}, 0, eth.BlockToInfo(l1Block), l1Block.Time())
l1InfoRawTx, err := derive.L1InfoDeposit(chaincfg.Mainnet, eth.SystemConfig{}, 0, eth.BlockToInfo(l1Block), l1Block.Time())
require.NoError(t, err)
l1InfoTx := gethtypes.NewTx(l1InfoRawTx)

Expand Down Expand Up @@ -137,6 +135,6 @@ func GenerateL1Block() *gethtypes.Block {
BaseFee: big.NewInt(10),
Difficulty: common.Big0,
Number: big.NewInt(0),
Time: uint64(0),
Time: uint64(time.Now().Unix()),
}, nil, nil, nil, trie.NewStackTrie(nil))
}
5 changes: 3 additions & 2 deletions x/rollup/keeper/deposits.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/polymerdao/monomer"
"github.com/polymerdao/monomer/x/rollup/types"
"github.com/samber/lo"
)
Expand Down Expand Up @@ -54,7 +55,7 @@ func (k *Keeper) processL1AttributesTx(ctx sdk.Context, txBytes []byte) (*derive
ctx.Logger().Error("First L1 tx must be a L1 attributes tx", "type", tx.Type())
return nil, types.WrapError(types.ErrInvalidL1Txs, "first L1 tx must be a L1 attributes tx, but got type %d", tx.Type())
}
l1blockInfo, err := derive.L1BlockInfoFromBytes(k.rollupCfg, 0, tx.Data())
l1blockInfo, err := derive.L1BlockInfoFromBytes(k.rollupCfg, uint64(tx.Time().Unix()), tx.Data())
if err != nil {
ctx.Logger().Error("Failed to derive L1 block info from L1 Info Deposit tx", "err", err, "txBytes", txBytes)
return nil, types.WrapError(types.ErrInvalidL1Txs, "failed to derive L1 block info from L1 Info Deposit tx: %v", err)
Expand All @@ -75,7 +76,7 @@ func (k *Keeper) processL1UserDepositTxs(ctx sdk.Context, txs [][]byte) error {
ctx.Logger().Error("L1 tx must be a user deposit tx", "index", i, "type", tx.Type())
return types.WrapError(types.ErrInvalidL1Txs, "L1 tx must be a user deposit tx, index:%d, type:%d", i, tx.Type())
}
if tx.IsSystemTx() {
if monomer.IsL1AttributesTx(&tx) {
ctx.Logger().Error("L1 tx must be a user deposit tx", "type", tx.Type())
return types.WrapError(types.ErrInvalidL1Txs, "L1 tx must be a user deposit tx, type %d", tx.Type())
}
Expand Down
3 changes: 2 additions & 1 deletion x/rollup/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package keeper
import (
"cosmossdk.io/core/store"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/ethereum-optimism/optimism/op-node/chaincfg"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/polymerdao/monomer/x/rollup/types"
)
Expand All @@ -24,6 +25,6 @@ func NewKeeper(
cdc: cdc,
storeService: storeService,
bankkeeper: bankKeeper,
rollupCfg: &rollup.Config{},
rollupCfg: chaincfg.Mainnet, // TODO: make this configurable?
}
}
Loading