Skip to content

Conversation

bragaigor
Copy link

@bragaigor bragaigor commented Oct 17, 2025

part of NIT-4027
pulled-by OffchainLabs/nitro#3876

Adds map to store transaction hash along with its gas used to bypass transaction execution for problematic txs execution on ARM architecture.

add map to store transaction hash along with its gas
used to bypass transaction execution for problematic
txs execution on ARM architecture

Signed-off-by: Igor Braga <[email protected]>
@cla-bot cla-bot bot added the s CLA signed label Oct 17, 2025
@bragaigor bragaigor changed the title add hardcoded transaction hash to gas used map core: add hardcoded transaction hash to gas used map Oct 17, 2025
@bragaigor bragaigor requested a review from tsahee October 17, 2025 20:41

// HardcodedTxGasUsed maps specific transaction hashes to the amount of GAS used by that
// specific transaction alone.
var HardcodedTxGasUsed = map[common.Hash]uint64{
Copy link
Contributor

Choose a reason for hiding this comment

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

let's have something in the name about it being a reverted transaction.

Copy link
Author

Choose a reason for hiding this comment

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

done in ae8da64

// Check against hardcoded transaction hashes that have previously reverted, so instead
// of executing the transaction we just update state nonce and remaining gas to avoid
// state divergence.
usedMultiGas, vmerr = st.handleRevertedTx(msg, usedMultiGas)
Copy link
Author

Choose a reason for hiding this comment

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

Is this extra level of indirection (function call) okay in this case? Or should we just inline it all? I'm worried about performance, given for every tx execution we introduced 3 checks:

// 1. check if mg.Tx is not nil
if msg.Tx == nil {
// 2. check if tx is inside RevertedTxGasUsed map
if l2GasUsed, ok := RevertedTxGasUsed[txHash]; ok {
// 3. make sure we continue on critical path if vmerr still nil
if vmerr == nil {

Copy link
Author

@bragaigor bragaigor Oct 18, 2025

Choose a reason for hiding this comment

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

I think I found a way to reduce those checks to just one for the critical path. Instead of doing the above, we make 2 changes:

  • Instead of having a map of TxHash -> L2GasUsed we would have a map BlockNumber -> (TxHash, L2GasUsed)
  • Instead of doing the check before if contractCreation { we guard st.evm.Call only

That's only possible because we can get the block number from st.evm.Context.BlockNumber without a nil check. The problem with this approach is that not every transaction inside the target block number are considered reverted transactions, so we need to call st.evm.Call for those other transactions; and I think that's okay since that would just happen for the entries found inside RevertedTxGasUsed. On the other hand, we introduce just one map lookup to all other transactions from the other blocks instead of adding a function call and a couple if checks.

The resulting code would look something like the following:

  • map gets updated to:
var RevertedTxGasUsed = map[uint64]RevertedTxEntry{
	204060366: {
		TxHash:    common.HexToHash("0x58df300a7f04fe31d41d24672786cbe1c58b4f3d8329d0d74392d814dd9f7e40"),
		L2GasUsed: 45606,
	},
}
  • and the only logic in state_transition that gets updated would be around st.evm.Call:
if revTxEntry, ok := RevertedTxGasUsed[st.evm.Context.BlockNumber.Uint64()]; ok {
    ret, st.gasRemaining, multiGas, vmerr = st.handleRevertedTx(revTxEntry, msg, usedMultiGas, value)
} else {
    ret, st.gasRemaining, multiGas, vmerr = st.evm.Call(msg.From, st.to(), msg.Data, st.gasRemaining, value)
}

where handleRevertedTx would look like:

func (st *stateTransition) handleRevertedTx(revTxEntry RevertedTxEntry, msg *Message, multiGas multigas.MultiGas, value *uint256.Int) (ret []byte, leftOverGas uint64, retUsedMultiGas multigas.MultiGas, err error) {
	if msg.Tx == nil {
		return []byte{}, st.gasRemaining, multiGas, nil
	}

	txHash := msg.Tx.Hash()
	if revTxEntry.TxHash == txHash {
		adjustedGas := revTxEntry.L2GasUsed - params.TxGas
		gasRemaining := st.gasRemaining - adjustedGas

		return []byte{}, gasRemaining, multiGas, vm.ErrExecutionReverted
	} else {
		// A block might contain more than one transaction, therefore we need to make sure we execute such
		// transactions in case they're not considered reverted.
		return st.evm.Call(msg.From, st.to(), msg.Data, st.gasRemaining, value)
	}
}

let me know if you prefer this new approach or the original one @tsahee

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

s CLA signed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants