Skip to content

Commit

Permalink
Merge pull request #2202 from OffchainLabs/fix-batchreverts-checking
Browse files Browse the repository at this point in the history
Use raw JSON RPCs to look for batch reverts [NIT-1279]
  • Loading branch information
ganeshvanahalli committed Mar 26, 2024
2 parents adab53d + 2197e44 commit 622abd6
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 13 deletions.
65 changes: 52 additions & 13 deletions arbnode/batch_poster.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/andybalholm/brotli"
"github.com/spf13/pflag"

"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
Expand Down Expand Up @@ -431,6 +432,35 @@ func AccessList(opts *AccessListOpts) types.AccessList {
return l
}

type txInfo struct {
Hash common.Hash `json:"hash"`
Nonce hexutil.Uint64 `json:"nonce"`
From common.Address `json:"from"`
To *common.Address `json:"to"`
Gas hexutil.Uint64 `json:"gas"`
GasPrice *hexutil.Big `json:"gasPrice"`
GasFeeCap *hexutil.Big `json:"maxFeePerGas,omitempty"`
GasTipCap *hexutil.Big `json:"maxPriorityFeePerGas,omitempty"`
Input hexutil.Bytes `json:"input"`
Value *hexutil.Big `json:"value"`
Accesses *types.AccessList `json:"accessList,omitempty"`
}

// getTxsInfoByBlock fetches all the transactions inside block of id 'number' using json rpc
// and returns an array of txInfo which has fields that are necessary in checking for batch reverts
func (b *BatchPoster) getTxsInfoByBlock(ctx context.Context, number int64) ([]txInfo, error) {
blockNrStr := rpc.BlockNumber(number).String()
rawRpcClient := b.l1Reader.Client().Client()
var blk struct {
Transactions []txInfo `json:"transactions"`
}
err := rawRpcClient.CallContext(ctx, &blk, "eth_getBlockByNumber", blockNrStr, true)
if err != nil {
return nil, fmt.Errorf("error fetching block %d : %w", number, err)
}
return blk.Transactions, nil
}

// checkRevert checks blocks with number in range [from, to] whether they
// contain reverted batch_poster transaction.
// It returns true if it finds batch posting needs to halt, which is true if a batch reverts
Expand All @@ -440,29 +470,38 @@ func (b *BatchPoster) checkReverts(ctx context.Context, to int64) (bool, error)
return false, fmt.Errorf("wrong range, from: %d > to: %d", b.nextRevertCheckBlock, to)
}
for ; b.nextRevertCheckBlock <= to; b.nextRevertCheckBlock++ {
number := big.NewInt(b.nextRevertCheckBlock)
block, err := b.l1Reader.Client().BlockByNumber(ctx, number)
txs, err := b.getTxsInfoByBlock(ctx, b.nextRevertCheckBlock)
if err != nil {
return false, fmt.Errorf("getting block: %v by number: %w", number, err)
return false, fmt.Errorf("error getting transactions data of block %d: %w", b.nextRevertCheckBlock, err)
}
for idx, tx := range block.Transactions() {
from, err := b.l1Reader.Client().TransactionSender(ctx, tx, block.Hash(), uint(idx))
if err != nil {
return false, fmt.Errorf("getting sender of transaction tx: %v, %w", tx.Hash(), err)
}
if from == b.dataPoster.Sender() {
r, err := b.l1Reader.Client().TransactionReceipt(ctx, tx.Hash())
for _, tx := range txs {
if tx.From == b.dataPoster.Sender() {
r, err := b.l1Reader.Client().TransactionReceipt(ctx, tx.Hash)
if err != nil {
return false, fmt.Errorf("getting a receipt for transaction: %v, %w", tx.Hash(), err)
return false, fmt.Errorf("getting a receipt for transaction: %v, %w", tx.Hash, err)
}
if r.Status == types.ReceiptStatusFailed {
shouldHalt := !b.config().DataPoster.UseNoOpStorage
logLevel := log.Warn
if shouldHalt {
logLevel = log.Error
}
txErr := arbutil.DetailTxError(ctx, b.l1Reader.Client(), tx, r)
logLevel("Transaction from batch poster reverted", "nonce", tx.Nonce(), "txHash", tx.Hash(), "blockNumber", r.BlockNumber, "blockHash", r.BlockHash, "txErr", txErr)
al := types.AccessList{}
if tx.Accesses != nil {
al = *tx.Accesses
}
txErr := arbutil.DetailTxErrorUsingCallMsg(ctx, b.l1Reader.Client(), tx.Hash, r, ethereum.CallMsg{
From: tx.From,
To: tx.To,
Gas: uint64(tx.Gas),
GasPrice: tx.GasPrice.ToInt(),
GasFeeCap: tx.GasFeeCap.ToInt(),
GasTipCap: tx.GasTipCap.ToInt(),
Value: tx.Value.ToInt(),
Data: tx.Input,
AccessList: al,
})
logLevel("Transaction from batch poster reverted", "nonce", tx.Nonce, "txHash", tx.Hash, "blockNumber", r.BlockNumber, "blockHash", r.BlockHash, "txErr", txErr)
return shouldHalt, nil
}
}
Expand Down
22 changes: 22 additions & 0 deletions arbutil/wait_for_l1.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,25 @@ func DetailTxError(ctx context.Context, client L1Interface, tx *types.Transactio
}
return fmt.Errorf("SendTxAsCall got: %w for tx hash %v", err, tx.Hash())
}

func DetailTxErrorUsingCallMsg(ctx context.Context, client L1Interface, txHash common.Hash, txRes *types.Receipt, callMsg ethereum.CallMsg) error {
// Re-execute the transaction as a call to get a better error
if ctx.Err() != nil {
return ctx.Err()
}
if txRes == nil {
return errors.New("expected receipt")
}
if txRes.Status == types.ReceiptStatusSuccessful {
return nil
}
var err error
if _, err = client.CallContract(ctx, callMsg, txRes.BlockNumber); err == nil {
return fmt.Errorf("tx failed but call succeeded for tx hash %v", txHash)
}
callMsg.Gas = 0
if _, err = client.CallContract(ctx, callMsg, txRes.BlockNumber); err == nil {
return fmt.Errorf("%w for tx hash %v", vm.ErrOutOfGas, txHash)
}
return fmt.Errorf("SendTxAsCall got: %w for tx hash %v", err, txHash)
}

0 comments on commit 622abd6

Please sign in to comment.