diff --git a/core/rawdb/accessors_skipped_txs.go b/core/rawdb/accessors_skipped_txs.go deleted file mode 100644 index fe38f4a7e..000000000 --- a/core/rawdb/accessors_skipped_txs.go +++ /dev/null @@ -1,249 +0,0 @@ -package rawdb - -import ( - "bytes" - "encoding/binary" - "math/big" - "sync" - - "github.com/morph-l2/go-ethereum/common" - "github.com/morph-l2/go-ethereum/core/types" - "github.com/morph-l2/go-ethereum/ethdb" - "github.com/morph-l2/go-ethereum/log" - "github.com/morph-l2/go-ethereum/rlp" -) - -// mutex used to avoid concurrent updates of NumSkippedTransactions -var mu sync.Mutex - -// writeNumSkippedTransactions writes the number of skipped transactions to the database. -func writeNumSkippedTransactions(db ethdb.KeyValueWriter, numSkipped uint64) { - value := big.NewInt(0).SetUint64(numSkipped).Bytes() - - if err := db.Put(numSkippedTransactionsKey, value); err != nil { - log.Crit("Failed to update the number of skipped transactions", "err", err) - } -} - -// ReadNumSkippedTransactions retrieves the number of skipped transactions. -func ReadNumSkippedTransactions(db ethdb.Reader) uint64 { - data, err := db.Get(numSkippedTransactionsKey) - if err != nil && isNotFoundErr(err) { - return 0 - } - if err != nil { - log.Crit("Failed to read number of skipped transactions from database", "err", err) - } - if len(data) == 0 { - return 0 - } - - number := new(big.Int).SetBytes(data) - if !number.IsUint64() { - log.Crit("Unexpected number of skipped transactions in database", "number", number) - } - return number.Uint64() -} - -// SkippedTransaction stores the transaction object, along with the skip reason and block context. -type SkippedTransaction struct { - // Tx is the skipped transaction. - // We store the tx itself because otherwise geth will discard it after skipping. - Tx *types.Transaction - - // Reason is the skip reason. - Reason string - - // BlockNumber is the number of the block in which this transaction was skipped. - BlockNumber uint64 - - // BlockHash is the hash of the block in which this transaction was skipped or nil. - BlockHash *common.Hash -} - -// SkippedTransactionV2 stores the SkippedTransaction object along with serialized traces. -type SkippedTransactionV2 struct { - // Tx is the skipped transaction. - // We store the tx itself otherwise geth will discard it after skipping. - Tx *types.Transaction - - // Traces is the serialized wrapped traces of the skipped transaction. - // We only store it when `MinerStoreSkippedTxTracesFlag` is enabled, so it might be empty. - // Note that we do not directly utilize `*types.BlockTrace` due to the fact that - // types.BlockTrace.StorageTrace.Proofs is of type `map[string][]hexutil.Bytes`, which is not RLP-serializable. - TracesBytes []byte - - // Reason is the skip reason. - Reason string - - // BlockNumber is the number of the block in which this transaction was skipped. - BlockNumber uint64 - - // BlockHash is the hash of the block in which this transaction was skipped or nil. - BlockHash *common.Hash -} - -// writeSkippedTransaction writes a skipped transaction to the database. -func writeSkippedTransaction(db ethdb.KeyValueWriter, tx *types.Transaction, reason string, blockNumber uint64, blockHash *common.Hash) { - var err error - // workaround: RLP decoding fails if this is nil - if blockHash == nil { - blockHash = &common.Hash{} - } - stx := SkippedTransactionV2{Tx: tx, Reason: reason, BlockNumber: blockNumber, BlockHash: blockHash} - bytes, err := rlp.EncodeToBytes(stx) - if err != nil { - log.Crit("Failed to RLP encode skipped transaction", "hash", tx.Hash().String(), "err", err) - } - if err := db.Put(SkippedTransactionKey(tx.Hash()), bytes); err != nil { - log.Crit("Failed to store skipped transaction", "hash", tx.Hash().String(), "err", err) - } -} - -// writeSkippedTransactionV1 is the old version of writeSkippedTransaction, we keep it for testing compatibility purpose. -func writeSkippedTransactionV1(db ethdb.KeyValueWriter, tx *types.Transaction, reason string, blockNumber uint64, blockHash *common.Hash) { - // workaround: RLP decoding fails if this is nil - if blockHash == nil { - blockHash = &common.Hash{} - } - stx := SkippedTransaction{Tx: tx, Reason: reason, BlockNumber: blockNumber, BlockHash: blockHash} - bytes, err := rlp.EncodeToBytes(stx) - if err != nil { - log.Crit("Failed to RLP encode skipped transaction", "hash", tx.Hash().String(), "err", err) - } - if err := db.Put(SkippedTransactionKey(tx.Hash()), bytes); err != nil { - log.Crit("Failed to store skipped transaction", "hash", tx.Hash().String(), "err", err) - } -} - -// readSkippedTransactionRLP retrieves a skipped transaction in its raw RLP database encoding. -func readSkippedTransactionRLP(db ethdb.Reader, txHash common.Hash) rlp.RawValue { - data, err := db.Get(SkippedTransactionKey(txHash)) - if err != nil && isNotFoundErr(err) { - return nil - } - if err != nil { - log.Crit("Failed to load skipped transaction", "hash", txHash.String(), "err", err) - } - return data -} - -// ReadSkippedTransaction retrieves a skipped transaction by its hash, along with its skipped reason. -func ReadSkippedTransaction(db ethdb.Reader, txHash common.Hash) *SkippedTransactionV2 { - data := readSkippedTransactionRLP(db, txHash) - if len(data) == 0 { - return nil - } - var stxV2 SkippedTransactionV2 - var stx SkippedTransaction - if err := rlp.Decode(bytes.NewReader(data), &stxV2); err != nil { - if err := rlp.Decode(bytes.NewReader(data), &stx); err != nil { - log.Crit("Invalid skipped transaction RLP", "hash", txHash.String(), "data", data, "err", err) - } - stxV2.Tx = stx.Tx - stxV2.Reason = stx.Reason - stxV2.BlockNumber = stx.BlockNumber - stxV2.BlockHash = stx.BlockHash - } - - if stxV2.BlockHash != nil && *stxV2.BlockHash == (common.Hash{}) { - stxV2.BlockHash = nil - } - return &stxV2 -} - -// writeSkippedTransactionHash writes the hash of a skipped transaction to the database. -func writeSkippedTransactionHash(db ethdb.KeyValueWriter, index uint64, txHash common.Hash) { - if err := db.Put(SkippedTransactionHashKey(index), txHash[:]); err != nil { - log.Crit("Failed to store skipped transaction hash", "index", index, "hash", txHash.String(), "err", err) - } -} - -// ReadSkippedTransactionHash retrieves the hash of a skipped transaction by its index. -func ReadSkippedTransactionHash(db ethdb.Reader, index uint64) *common.Hash { - data, err := db.Get(SkippedTransactionHashKey(index)) - if err != nil && isNotFoundErr(err) { - return nil - } - if err != nil { - log.Crit("Failed to load skipped transaction hash", "index", index, "err", err) - } - hash := common.BytesToHash(data) - return &hash -} - -// WriteSkippedTransaction writes a skipped transaction to the database and also updates the count and lookup index. -// Note: The lookup index and count will include duplicates if there are chain reorgs. -func WriteSkippedTransaction(db ethdb.Database, tx *types.Transaction, reason string, blockNumber uint64, blockHash *common.Hash) { - // this method is not accessed concurrently, but just to be sure... - mu.Lock() - defer mu.Unlock() - - index := ReadNumSkippedTransactions(db) - - // update in a batch - batch := db.NewBatch() - writeSkippedTransaction(batch, tx, reason, blockNumber, blockHash) - writeSkippedTransactionHash(batch, index, tx.Hash()) - writeNumSkippedTransactions(batch, index+1) - - // write to DB - if err := batch.Write(); err != nil { - log.Crit("Failed to store skipped transaction", "hash", tx.Hash().String(), "err", err) - } -} - -// SkippedTransactionIterator is a wrapper around ethdb.Iterator that -// allows us to iterate over skipped transaction hashes in the database. -// It implements an interface similar to ethdb.Iterator. -type SkippedTransactionIterator struct { - inner ethdb.Iterator - db ethdb.Reader - keyLength int -} - -// IterateSkippedTransactionsFrom creates a SkippedTransactionIterator that iterates -// over all skipped transaction hashes in the database starting at the provided index. -func IterateSkippedTransactionsFrom(db ethdb.Database, index uint64) SkippedTransactionIterator { - start := encodeBigEndian(index) - it := db.NewIterator(skippedTransactionHashPrefix, start) - keyLength := len(skippedTransactionHashPrefix) + 8 - - return SkippedTransactionIterator{ - inner: it, - db: db, - keyLength: keyLength, - } -} - -// Next moves the iterator to the next key/value pair. -// It returns false when the iterator is exhausted. -// TODO: Consider reading items in batches. -func (it *SkippedTransactionIterator) Next() bool { - for it.inner.Next() { - key := it.inner.Key() - if len(key) == it.keyLength { - return true - } - } - return false -} - -// Index returns the index of the current skipped transaction hash. -func (it *SkippedTransactionIterator) Index() uint64 { - key := it.inner.Key() - raw := key[len(skippedTransactionHashPrefix) : len(skippedTransactionHashPrefix)+8] - index := binary.BigEndian.Uint64(raw) - return index -} - -// TransactionHash returns the current skipped transaction hash. -func (it *SkippedTransactionIterator) TransactionHash() common.Hash { - data := it.inner.Value() - return common.BytesToHash(data) -} - -// Release releases the associated resources. -func (it *SkippedTransactionIterator) Release() { - it.inner.Release() -} diff --git a/core/rawdb/accessors_skipped_txs_test.go b/core/rawdb/accessors_skipped_txs_test.go deleted file mode 100644 index 7f612aab5..000000000 --- a/core/rawdb/accessors_skipped_txs_test.go +++ /dev/null @@ -1,144 +0,0 @@ -package rawdb - -import ( - "math/big" - "sync" - "testing" - - "github.com/morph-l2/go-ethereum/common" - "github.com/morph-l2/go-ethereum/core/types" -) - -func TestReadWriteNumSkippedTransactions(t *testing.T) { - blockNumbers := []uint64{ - 1, - 1 << 2, - 1 << 8, - 1 << 16, - 1 << 32, - } - - db := NewMemoryDatabase() - for _, num := range blockNumbers { - writeNumSkippedTransactions(db, num) - got := ReadNumSkippedTransactions(db) - - if got != num { - t.Fatal("Num skipped transactions mismatch", "expected", num, "got", got) - } - } -} - -func newTestTransaction(queueIndex uint64) *types.Transaction { - l1msg := types.L1MessageTx{ - QueueIndex: queueIndex, - Gas: 0, - To: &common.Address{}, - Value: big.NewInt(0), - Data: nil, - Sender: common.Address{}, - } - return types.NewTx(&l1msg) -} - -func TestReadWriteSkippedTransactionNoIndex(t *testing.T) { - tx := newTestTransaction(123) - db := NewMemoryDatabase() - writeSkippedTransaction(db, tx, "random reason", 1, &common.Hash{1}) - got := ReadSkippedTransaction(db, tx.Hash()) - if got == nil || got.Tx.Hash() != tx.Hash() || got.Reason != "random reason" || got.BlockNumber != 1 || got.BlockHash == nil || *got.BlockHash != (common.Hash{1}) { - t.Fatal("Skipped transaction mismatch", "got", got) - } -} - -func TestReadSkippedTransactionV1AsV2(t *testing.T) { - tx := newTestTransaction(123) - db := NewMemoryDatabase() - writeSkippedTransactionV1(db, tx, "random reason", 1, &common.Hash{1}) - got := ReadSkippedTransaction(db, tx.Hash()) - if got == nil || got.Tx.Hash() != tx.Hash() || got.Reason != "random reason" || got.BlockNumber != 1 || got.BlockHash == nil || *got.BlockHash != (common.Hash{1}) { - t.Fatal("Skipped transaction mismatch", "got", got) - } -} - -func TestReadWriteSkippedTransaction(t *testing.T) { - tx := newTestTransaction(123) - db := NewMemoryDatabase() - WriteSkippedTransaction(db, tx, "random reason", 1, &common.Hash{1}) - got := ReadSkippedTransaction(db, tx.Hash()) - if got == nil || got.Tx.Hash() != tx.Hash() || got.Reason != "random reason" || got.BlockNumber != 1 || got.BlockHash == nil || *got.BlockHash != (common.Hash{1}) { - t.Fatal("Skipped transaction mismatch", "got", got) - } - count := ReadNumSkippedTransactions(db) - if count != 1 { - t.Fatal("Skipped transaction count mismatch", "expected", 1, "got", count) - } - hash := ReadSkippedTransactionHash(db, 0) - if hash == nil || *hash != tx.Hash() { - t.Fatal("Skipped L1 message hash mismatch", "expected", tx.Hash(), "got", hash) - } -} - -func TestSkippedTransactionConcurrentUpdate(t *testing.T) { - count := 20 - tx := newTestTransaction(123) - db := NewMemoryDatabase() - var wg sync.WaitGroup - for ii := 0; ii < count; ii++ { - wg.Add(1) - go func() { - defer wg.Done() - WriteSkippedTransaction(db, tx, "random reason", 1, &common.Hash{1}) - }() - } - wg.Wait() - got := ReadNumSkippedTransactions(db) - if got != uint64(count) { - t.Fatal("Skipped transaction count mismatch", "expected", count, "got", got) - } -} - -func TestIterateSkippedTransactions(t *testing.T) { - db := NewMemoryDatabase() - - txs := []*types.Transaction{ - newTestTransaction(1), - newTestTransaction(2), - newTestTransaction(3), - newTestTransaction(4), - newTestTransaction(5), - } - - for _, tx := range txs { - WriteSkippedTransaction(db, tx, "random reason", 1, &common.Hash{1}) - } - - // simulate skipped L2 tx that's not included in the index - l2tx := newTestTransaction(6) - writeSkippedTransaction(db, l2tx, "random reason", 1, &common.Hash{1}) - - it := IterateSkippedTransactionsFrom(db, 2) - defer it.Release() - - for ii := 2; ii < len(txs); ii++ { - finished := !it.Next() - if finished { - t.Fatal("Iterator terminated early", "ii", ii) - } - - index := it.Index() - if index != uint64(ii) { - t.Fatal("Invalid skipped transaction index", "expected", ii, "got", index) - } - - hash := it.TransactionHash() - if hash != txs[ii].Hash() { - t.Fatal("Invalid skipped transaction hash", "expected", txs[ii].Hash(), "got", hash) - } - } - - finished := !it.Next() - if !finished { - t.Fatal("Iterator did not terminate") - } -} diff --git a/eth/api.go b/eth/api.go index 6d5184525..30db55276 100644 --- a/eth/api.go +++ b/eth/api.go @@ -19,11 +19,9 @@ package eth import ( "compress/gzip" "context" - "encoding/json" "errors" "fmt" "io" - "math/big" "os" "strings" "time" @@ -552,11 +550,6 @@ func (api *MorphAPI) GetBlockByNumber(ctx context.Context, number rpc.BlockNumbe return nil, err } -// GetNumSkippedTransactions returns the number of skipped transactions. -func (api *MorphAPI) GetNumSkippedTransactions(ctx context.Context) (uint64, error) { - return rawdb.ReadNumSkippedTransactions(api.eth.ChainDb()), nil -} - // EstimateL1DataFee returns an estimate of the L1 data fee required to // process the given transaction against the current pending block. func (api *MorphAPI) EstimateL1DataFee(ctx context.Context, args ethapi.TransactionArgs, blockNrOrHash *rpc.BlockNumberOrHash) (*hexutil.Uint64, error) { @@ -585,44 +578,6 @@ type RPCTransaction struct { Traces *types.BlockTrace `json:"traces,omitempty"` } -// GetSkippedTransaction returns a skipped transaction by its hash. -func (api *MorphAPI) GetSkippedTransaction(ctx context.Context, hash common.Hash) (*RPCTransaction, error) { - stx := rawdb.ReadSkippedTransaction(api.eth.ChainDb(), hash) - if stx == nil { - return nil, nil - } - var rpcTx RPCTransaction - rpcTx.RPCTransaction = *ethapi.NewRPCTransaction(stx.Tx, common.Hash{}, 0, 0, nil, api.eth.blockchain.Config()) - rpcTx.SkipReason = stx.Reason - rpcTx.SkipBlockNumber = (*hexutil.Big)(new(big.Int).SetUint64(stx.BlockNumber)) - rpcTx.SkipBlockHash = stx.BlockHash - if len(stx.TracesBytes) != 0 { - traces := &types.BlockTrace{} - if err := json.Unmarshal(stx.TracesBytes, traces); err != nil { - return nil, fmt.Errorf("fail to Unmarshal traces for skipped tx, hash: %s, err: %w", hash.String(), err) - } - rpcTx.Traces = traces - } - return &rpcTx, nil -} - -// GetSkippedTransactionHashes returns a list of skipped transaction hashes between the two indices provided (inclusive). -func (api *MorphAPI) GetSkippedTransactionHashes(ctx context.Context, from uint64, to uint64) ([]common.Hash, error) { - it := rawdb.IterateSkippedTransactionsFrom(api.eth.ChainDb(), from) - defer it.Release() - - var hashes []common.Hash - - for it.Next() { - if it.Index() > to { - break - } - hashes = append(hashes, it.TransactionHash()) - } - - return hashes, nil -} - type RPCRollupBatch struct { Version uint `json:"version"` Hash common.Hash `json:"hash"` diff --git a/eth/catalyst/api_types.go b/eth/catalyst/api_types.go index 77594f7c4..a7d20b241 100644 --- a/eth/catalyst/api_types.go +++ b/eth/catalyst/api_types.go @@ -19,8 +19,6 @@ package catalyst import ( "math/big" - "github.com/morph-l2/go-ethereum/core/types" - "github.com/morph-l2/go-ethereum/common" "github.com/morph-l2/go-ethereum/common/hexutil" ) @@ -101,12 +99,11 @@ type ExecutableL2Data struct { Transactions [][]byte `json:"transactions" gencodec:"required"` // execution result - StateRoot common.Hash `json:"stateRoot"` - GasUsed uint64 `json:"gasUsed"` - ReceiptRoot common.Hash `json:"receiptsRoot"` - LogsBloom []byte `json:"logsBloom"` - WithdrawTrieRoot common.Hash `json:"withdrawTrieRoot"` - SkippedTxs []*types.SkippedTransaction `json:"skippedTxs"` + StateRoot common.Hash `json:"stateRoot"` + GasUsed uint64 `json:"gasUsed"` + ReceiptRoot common.Hash `json:"receiptsRoot"` + LogsBloom []byte `json:"logsBloom"` + WithdrawTrieRoot common.Hash `json:"withdrawTrieRoot"` NextL1MessageIndex uint64 `json:"nextL1MessageIndex"` diff --git a/eth/catalyst/gen_l2_ed.go b/eth/catalyst/gen_l2_ed.go index 18bb6a39e..6c6d4d713 100644 --- a/eth/catalyst/gen_l2_ed.go +++ b/eth/catalyst/gen_l2_ed.go @@ -9,7 +9,6 @@ import ( "github.com/morph-l2/go-ethereum/common" "github.com/morph-l2/go-ethereum/common/hexutil" - "github.com/morph-l2/go-ethereum/core/types" ) var _ = (*executableL2DataMarshaling)(nil) @@ -17,21 +16,20 @@ var _ = (*executableL2DataMarshaling)(nil) // MarshalJSON marshals as JSON. func (e ExecutableL2Data) MarshalJSON() ([]byte, error) { type ExecutableL2Data struct { - ParentHash common.Hash `json:"parentHash" gencodec:"required"` - Miner common.Address `json:"miner" gencodec:"required"` - Number hexutil.Uint64 `json:"number" gencodec:"required"` - GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"` - BaseFee *hexutil.Big `json:"baseFeePerGas"` - Timestamp hexutil.Uint64 `json:"timestamp" gencodec:"required"` - Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"` - StateRoot common.Hash `json:"stateRoot"` - GasUsed hexutil.Uint64 `json:"gasUsed"` - ReceiptRoot common.Hash `json:"receiptsRoot"` - LogsBloom hexutil.Bytes `json:"logsBloom"` - WithdrawTrieRoot common.Hash `json:"withdrawTrieRoot"` - SkippedTxs []*types.SkippedTransaction `json:"skippedTxs"` - NextL1MessageIndex uint64 `json:"nextL1MessageIndex"` - Hash common.Hash `json:"hash"` + ParentHash common.Hash `json:"parentHash" gencodec:"required"` + Miner common.Address `json:"miner" gencodec:"required"` + Number hexutil.Uint64 `json:"number" gencodec:"required"` + GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"` + BaseFee *hexutil.Big `json:"baseFeePerGas"` + Timestamp hexutil.Uint64 `json:"timestamp" gencodec:"required"` + Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"` + StateRoot common.Hash `json:"stateRoot"` + GasUsed hexutil.Uint64 `json:"gasUsed"` + ReceiptRoot common.Hash `json:"receiptsRoot"` + LogsBloom hexutil.Bytes `json:"logsBloom"` + WithdrawTrieRoot common.Hash `json:"withdrawTrieRoot"` + NextL1MessageIndex uint64 `json:"nextL1MessageIndex"` + Hash common.Hash `json:"hash"` } var enc ExecutableL2Data enc.ParentHash = e.ParentHash @@ -51,7 +49,6 @@ func (e ExecutableL2Data) MarshalJSON() ([]byte, error) { enc.ReceiptRoot = e.ReceiptRoot enc.LogsBloom = e.LogsBloom enc.WithdrawTrieRoot = e.WithdrawTrieRoot - enc.SkippedTxs = e.SkippedTxs enc.NextL1MessageIndex = e.NextL1MessageIndex enc.Hash = e.Hash return json.Marshal(&enc) @@ -60,21 +57,20 @@ func (e ExecutableL2Data) MarshalJSON() ([]byte, error) { // UnmarshalJSON unmarshals from JSON. func (e *ExecutableL2Data) UnmarshalJSON(input []byte) error { type ExecutableL2Data struct { - ParentHash *common.Hash `json:"parentHash" gencodec:"required"` - Miner *common.Address `json:"miner" gencodec:"required"` - Number *hexutil.Uint64 `json:"number" gencodec:"required"` - GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"` - BaseFee *hexutil.Big `json:"baseFeePerGas"` - Timestamp *hexutil.Uint64 `json:"timestamp" gencodec:"required"` - Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"` - StateRoot *common.Hash `json:"stateRoot"` - GasUsed *hexutil.Uint64 `json:"gasUsed"` - ReceiptRoot *common.Hash `json:"receiptsRoot"` - LogsBloom *hexutil.Bytes `json:"logsBloom"` - WithdrawTrieRoot *common.Hash `json:"withdrawTrieRoot"` - SkippedTxs []*types.SkippedTransaction `json:"skippedTxs"` - NextL1MessageIndex *uint64 `json:"nextL1MessageIndex"` - Hash *common.Hash `json:"hash"` + ParentHash *common.Hash `json:"parentHash" gencodec:"required"` + Miner *common.Address `json:"miner" gencodec:"required"` + Number *hexutil.Uint64 `json:"number" gencodec:"required"` + GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"` + BaseFee *hexutil.Big `json:"baseFeePerGas"` + Timestamp *hexutil.Uint64 `json:"timestamp" gencodec:"required"` + Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"` + StateRoot *common.Hash `json:"stateRoot"` + GasUsed *hexutil.Uint64 `json:"gasUsed"` + ReceiptRoot *common.Hash `json:"receiptsRoot"` + LogsBloom *hexutil.Bytes `json:"logsBloom"` + WithdrawTrieRoot *common.Hash `json:"withdrawTrieRoot"` + NextL1MessageIndex *uint64 `json:"nextL1MessageIndex"` + Hash *common.Hash `json:"hash"` } var dec ExecutableL2Data if err := json.Unmarshal(input, &dec); err != nil { @@ -125,9 +121,6 @@ func (e *ExecutableL2Data) UnmarshalJSON(input []byte) error { if dec.WithdrawTrieRoot != nil { e.WithdrawTrieRoot = *dec.WithdrawTrieRoot } - if dec.SkippedTxs != nil { - e.SkippedTxs = dec.SkippedTxs - } if dec.NextL1MessageIndex != nil { e.NextL1MessageIndex = *dec.NextL1MessageIndex } diff --git a/eth/catalyst/l2_api.go b/eth/catalyst/l2_api.go index 1fc053e20..45b7a293f 100644 --- a/eth/catalyst/l2_api.go +++ b/eth/catalyst/l2_api.go @@ -8,7 +8,6 @@ import ( "time" "github.com/morph-l2/go-ethereum/common" - "github.com/morph-l2/go-ethereum/core/rawdb" "github.com/morph-l2/go-ethereum/core/state" "github.com/morph-l2/go-ethereum/core/types" "github.com/morph-l2/go-ethereum/eth" @@ -60,7 +59,6 @@ type executionResult struct { block *types.Block state *state.StateDB receipts types.Receipts - skippedTxs []*types.SkippedTransaction procTime time.Duration withdrawTrieRoot common.Hash } @@ -83,7 +81,6 @@ func (api *l2ConsensusAPI) AssembleL2Block(params AssembleL2BlockParams) (*Execu } start := time.Now() - //block, stateDB, receipts, rc, skippedTxs, err := api.eth.Miner().BuildBlock(parent.Hash(), time.Now(), transactions) newBlockResult, err := api.eth.Miner().BuildBlock(parent.Hash(), time.Now(), transactions) if err != nil { return nil, err @@ -94,7 +91,7 @@ func (api *l2ConsensusAPI) AssembleL2Block(params AssembleL2BlockParams) (*Execu // return nil, nil // } procTime := time.Since(start) - withdrawTrieRoot := api.writeVerified(newBlockResult.State, newBlockResult.Block, newBlockResult.Receipts, newBlockResult.SkippedTxs, procTime) + withdrawTrieRoot := api.writeVerified(newBlockResult.State, newBlockResult.Block, newBlockResult.Receipts, procTime) return &ExecutableL2Data{ ParentHash: newBlockResult.Block.ParentHash(), Number: newBlockResult.Block.NumberU64(), @@ -110,7 +107,6 @@ func (api *l2ConsensusAPI) AssembleL2Block(params AssembleL2BlockParams) (*Execu LogsBloom: newBlockResult.Block.Bloom().Bytes(), NextL1MessageIndex: newBlockResult.Block.Header().NextL1MsgIndex, WithdrawTrieRoot: withdrawTrieRoot, - SkippedTxs: newBlockResult.SkippedTxs, Hash: newBlockResult.Block.Hash(), }, nil @@ -155,29 +151,6 @@ func (api *l2ConsensusAPI) ValidateL2Block(params ExecutableL2Data) (*GenericRes }, nil } - // check whether the skipped transactions should be skipped. - var skippedTransactions []*types.SkippedTransaction - if len(params.SkippedTxs) > 0 { - l1Transactions := make(types.Transactions, len(params.SkippedTxs)) - for i, st := range params.SkippedTxs { - l1Transactions[i] = st.Tx - } - involvedTxs, realSkipped, err := api.eth.Miner().SimulateL1Messages(params.ParentHash, l1Transactions) - if err != nil { - log.Error("failed to simulate L1Messages", "error", err) - return &GenericResponse{ - false, - }, nil - } - if len(involvedTxs) > 0 { - log.Error("found the skipped transactions that should not be skipped", "involved tx count", len(involvedTxs)) - return &GenericResponse{ - false, - }, nil - } - skippedTransactions = realSkipped - } - stateDB, receipts, _, procTime, err := api.eth.BlockChain().ProcessBlock(block, parent.Header(), false) if err != nil { log.Error("error processing block", "error", err) @@ -192,7 +165,7 @@ func (api *l2ConsensusAPI) ValidateL2Block(params ExecutableL2Data) (*GenericRes false, }, nil } - api.writeVerified(stateDB, block, receipts, skippedTransactions, procTime) + api.writeVerified(stateDB, block, receipts, procTime) return &GenericResponse{ true, }, nil @@ -231,10 +204,6 @@ func (api *l2ConsensusAPI) NewL2Block(params ExecutableL2Data, batchHash *common bas, verified := api.isVerified(block.Hash()) if verified { api.eth.BlockChain().UpdateBlockProcessMetrics(bas.state, bas.procTime) - for _, skipped := range bas.skippedTxs { - bh := block.Hash() - rawdb.WriteSkippedTransaction(api.eth.ChainDb(), skipped.Tx, skipped.Reason, block.NumberU64(), &bh) - } return api.eth.BlockChain().WriteStateAndSetHead(block, bas.receipts, bas.state, bas.procTime) } @@ -248,11 +217,6 @@ func (api *l2ConsensusAPI) NewL2Block(params ExecutableL2Data, batchHash *common return err } - bh := block.Hash() - for _, skippedL1Tx := range params.SkippedTxs { - rawdb.WriteSkippedTransaction(api.eth.ChainDb(), skippedL1Tx.Tx, skippedL1Tx.Reason, block.NumberU64(), &bh) - } - return api.eth.BlockChain().WriteStateAndSetHead(block, receipts, stateDB, procTime) } @@ -363,7 +327,7 @@ func (api *l2ConsensusAPI) verifyBlock(block *types.Block) error { return nil } -func (api *l2ConsensusAPI) writeVerified(state *state.StateDB, block *types.Block, receipts types.Receipts, skipped []*types.SkippedTransaction, procTime time.Duration) common.Hash { +func (api *l2ConsensusAPI) writeVerified(state *state.StateDB, block *types.Block, receipts types.Receipts, procTime time.Duration) common.Hash { withdrawTrieRoot := withdrawtrie.ReadWTRSlot(rcfg.L2MessageQueueAddress, state) api.verifiedMapLock.Lock() defer api.verifiedMapLock.Unlock() @@ -371,7 +335,6 @@ func (api *l2ConsensusAPI) writeVerified(state *state.StateDB, block *types.Bloc block: block, state: state, receipts: receipts, - skippedTxs: skipped, procTime: procTime, withdrawTrieRoot: withdrawTrieRoot, } diff --git a/miner/miner.go b/miner/miner.go index 81373069b..8328fb991 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -176,26 +176,6 @@ func (miner *Miner) BuildBlock(parentHash common.Hash, timestamp time.Time, tran }) } -func (miner *Miner) SimulateL1Messages(parentHash common.Hash, transactions types.Transactions) ([]*types.Transaction, []*types.SkippedTransaction, error) { - if transactions.Len() == 0 { - return nil, nil, nil - } - - ret, err := miner.getSealingBlockAndState(&generateParams{ - timestamp: uint64(time.Now().Unix()), - parentHash: parentHash, - coinbase: miner.config.PendingFeeRecipient, - transactions: transactions, - simulate: true, - timeout: miner.newBlockTimeout * 2, // double the timeout, in case it is blocked due to the previous work - }) - if err != nil { - return nil, nil, err - } - - return ret.Block.Transactions(), ret.SkippedTxs, nil -} - // getPending retrieves the pending block based on the current head block. // The result might be nil if pending generation is failed. func (miner *Miner) getPending() *NewBlockResult { diff --git a/miner/worker.go b/miner/worker.go index c7ce88fff..41722acbb 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -71,10 +71,9 @@ type getWorkReq struct { } type NewBlockResult struct { - Block *types.Block - State *state.StateDB - Receipts types.Receipts - SkippedTxs []*types.SkippedTransaction + Block *types.Block + State *state.StateDB + Receipts types.Receipts } // generateParams wraps various of settings for generating sealing task. @@ -123,10 +122,9 @@ func (miner *Miner) generateWork(genParams *generateParams, interrupt *int32) (* return nil, finalizeErr } return &NewBlockResult{ - Block: block, - State: work.state, - Receipts: work.receipts, - SkippedTxs: nil, + Block: block, + State: work.state, + Receipts: work.receipts, }, nil } @@ -134,7 +132,7 @@ func (miner *Miner) generateWork(genParams *generateParams, interrupt *int32) (* work.gasPool = new(core.GasPool).AddGas(work.header.GasLimit) } - fillTxErr, skippedTxs := miner.fillTransactions(work, genParams.transactions, interrupt) + fillTxErr := miner.fillTransactions(work, genParams.transactions, interrupt) if fillTxErr != nil && errors.Is(fillTxErr, errBlockInterruptedByTimeout) { log.Warn("Block building is interrupted", "allowance", common.PrettyDuration(miner.newBlockTimeout)) } @@ -144,10 +142,9 @@ func (miner *Miner) generateWork(genParams *generateParams, interrupt *int32) (* return nil, finalizeErr } return &NewBlockResult{ - Block: block, - State: work.state, - Receipts: work.receipts, - SkippedTxs: skippedTxs, + Block: block, + State: work.state, + Receipts: work.receipts, }, nil } @@ -261,14 +258,14 @@ func (miner *Miner) commitTransaction(env *environment, tx *types.Transaction, c return nil } -func (miner *Miner) commitTransactions(env *environment, txs *types.TransactionsByPriceAndNonce, coinbase common.Address, interrupt *int32) (error, []*types.SkippedTransaction) { +func (miner *Miner) commitTransactions(env *environment, txs *types.TransactionsByPriceAndNonce, coinbase common.Address, interrupt *int32) error { defer func(t0 time.Time) { l2CommitTxsTimer.Update(time.Since(t0)) }(time.Now()) // Short circuit if current is nil if env == nil { - return errors.New("no env found"), nil + return errors.New("no env found") } gasLimit := env.header.GasLimit @@ -276,10 +273,7 @@ func (miner *Miner) commitTransactions(env *environment, txs *types.Transactions env.gasPool = new(core.GasPool).AddGas(gasLimit) } - var ( - loops int64 - skippedL1Txs = make([]*types.SkippedTransaction, 0) - ) + var loops int64 loop: for { @@ -287,7 +281,7 @@ loop: loops++ if interrupt != nil { if signal := atomic.LoadInt32(interrupt); signal != commitInterruptNone { - return signalToErr(signal), skippedL1Txs + return signalToErr(signal) } } if env.gasPool.Gas() < params.TxGas { @@ -340,20 +334,11 @@ loop: case errors.Is(err, core.ErrGasLimitReached) && tx.IsL1MessageTx(): // If this block already contains some L1 messages, // terminate here and try again in the next block. - if env.l1TxCount > 0 { - break loop + if env.l1TxCount == 0 { + l1TxGasLimitExceededCounter.Inc(1) + log.Warn("Single L1 message gas limit exceeded for current block", "L1 message gas limit", tx.Gas(), "block gas limit", env.header.GasLimit, "tx hash", tx.Hash()) } - // A single L1 message leads to out-of-gas. Skip it. - queueIndex := tx.AsL1MessageTx().QueueIndex - log.Info("Skipping L1 message", "queueIndex", queueIndex, "tx", tx.Hash().String(), "block", env.header.Number, "reason", "gas limit exceeded") - env.nextL1MsgIndex = queueIndex + 1 - txs.Shift() - - skippedL1Txs = append(skippedL1Txs, &types.SkippedTransaction{ - Tx: tx, - Reason: "gas limit exceeded", - }) - l1TxGasLimitExceededCounter.Inc(1) + break loop case errors.Is(err, core.ErrGasLimitReached): // Pop the current out-of-gas transaction without shifting in the next from the account @@ -400,30 +385,23 @@ loop: // nonce-too-high clause will prevent us from executing in vain). log.Debug("Transaction failed, account skipped", "hash", tx.Hash().String(), "err", err) if tx.IsL1MessageTx() { - queueIndex := tx.AsL1MessageTx().QueueIndex - log.Info("Skipping L1 message", "queueIndex", queueIndex, "tx", tx.Hash().String(), "block", env.header.Number, "reason", "strange error", "err", err) - env.nextL1MsgIndex = queueIndex + 1 - - skippedL1Txs = append(skippedL1Txs, &types.SkippedTransaction{ - Tx: tx, - Reason: fmt.Sprintf("strange error: %v", err), - }) + log.Warn("L1 messages encounter strange error, stops filling following L1 messages") l1TxStrangeErrCounter.Inc(1) + break loop } txs.Shift() } } - return nil, skippedL1Txs + return nil } // fillTransactions retrieves the pending transactions from the txpool and fills them // into the given sealing block. The transaction selection and ordering strategy can // be customized with the plugin in the future. -func (miner *Miner) fillTransactions(env *environment, l1Transactions types.Transactions, interrupt *int32) (error, []*types.SkippedTransaction) { +func (miner *Miner) fillTransactions(env *environment, l1Transactions types.Transactions, interrupt *int32) error { var ( - err error - skippedL1Txs []*types.SkippedTransaction + err error ) defer func(env *environment) { @@ -445,15 +423,15 @@ func (miner *Miner) fillTransactions(env *environment, l1Transactions types.Tran } } txs := types.NewTransactionsByPriceAndNonce(env.signer, l1Txs, env.header.BaseFee) - err, skippedL1Txs = miner.commitTransactions(env, txs, env.header.Coinbase, interrupt) + err = miner.commitTransactions(env, txs, env.header.Coinbase, interrupt) if err != nil { - return err, skippedL1Txs + return err } } // Giving up involving L2 transactions if it is simulation for L1Messages if env.isSimulate { - return err, skippedL1Txs + return err } // Split the pending transactions into locals and remotes @@ -469,17 +447,17 @@ func (miner *Miner) fillTransactions(env *environment, l1Transactions types.Tran if len(localTxs) > 0 { txs := types.NewTransactionsByPriceAndNonce(env.signer, localTxs, env.header.BaseFee) - err, _ = miner.commitTransactions(env, txs, env.header.Coinbase, interrupt) + err = miner.commitTransactions(env, txs, env.header.Coinbase, interrupt) if err != nil { - return err, skippedL1Txs + return err } } if len(remoteTxs) > 0 { txs := types.NewTransactionsByPriceAndNonce(env.signer, remoteTxs, env.header.BaseFee) - err, _ = miner.commitTransactions(env, txs, env.header.Coinbase, nil) // always return false + err = miner.commitTransactions(env, txs, env.header.Coinbase, nil) // always return false } - return err, skippedL1Txs + return err } func withTimer(timer metrics.Timer, f func()) {