Skip to content

Commit

Permalink
Merge pull request #1065 from OffchainLabs/estimate-l1-component
Browse files Browse the repository at this point in the history
Method for estimating just l1 costs
  • Loading branch information
PlasmaPower authored Sep 1, 2022
2 parents dec6c6e + 00d7af2 commit 90edb28
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 20 deletions.
7 changes: 6 additions & 1 deletion arbos/l1pricing/l1pricing.go
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,7 @@ const estimationPaddingBasisPoints = 100
var randomNonce = binary.BigEndian.Uint64(crypto.Keccak256([]byte("Nonce"))[:8])
var randomGasTipCap = new(big.Int).SetBytes(crypto.Keccak256([]byte("GasTipCap"))[:4])
var randomGasFeeCap = new(big.Int).SetBytes(crypto.Keccak256([]byte("GasFeeCap"))[:4])
var RandomGas = uint64(binary.BigEndian.Uint32(crypto.Keccak256([]byte("Gas"))[:4]))
var randV = arbmath.BigMulByUint(params.ArbitrumOneChainConfig().ChainID, 3)
var randR = crypto.Keccak256Hash([]byte("R")).Big()
var randS = crypto.Keccak256Hash([]byte("S")).Big()
Expand All @@ -673,11 +674,15 @@ func makeFakeTxForMessage(message core.Message) *types.Transaction {
if gasFeeCap.Sign() == 0 {
gasFeeCap = randomGasFeeCap
}
gas := message.Gas()
if gas == 0 {
gas = RandomGas
}
return types.NewTx(&types.DynamicFeeTx{
Nonce: nonce,
GasTipCap: gasTipCap,
GasFeeCap: gasFeeCap,
Gas: message.Gas(),
Gas: gas,
To: message.To(),
Value: message.Value(),
Data: message.Data(),
Expand Down
25 changes: 25 additions & 0 deletions contracts/src/node-interface/NodeInterface.sol
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,31 @@ interface NodeInterface {
uint256 l1BaseFeeEstimate
);

/**
* @notice Estimates a transaction's l1 costs.
* @dev Use eth_call to call.
* This method is exactly like gasEstimateComponents, but doesn't include the l2 component
* so that the l1 component can be known even when the tx may fail.
* @param data the tx's calldata. Everything else like "From" and "Gas" are copied over
* @param to the tx's "To" (ignored when contractCreation is true)
* @param contractCreation whether "To" is omitted
* @return gasEstimateForL1 an estimate of the amount of gas needed for the l1 component of this tx
* @return baseFee the l2 base fee
* @return l1BaseFeeEstimate ArbOS's l1 estimate of the l1 base fee
*/
function gasEstimateL1Component(
address to,
bool contractCreation,
bytes calldata data
)
external
payable
returns (
uint64 gasEstimateForL1,
uint256 baseFee,
uint256 l1BaseFeeEstimate
);

/**
* @notice Returns the proof necessary to redeem a message
* @param batchNum index of outbox entry (i.e., outgoing messages Merkle root) in array of outbox entries
Expand Down
76 changes: 57 additions & 19 deletions nodeInterface/NodeInterface.go
Original file line number Diff line number Diff line change
Expand Up @@ -423,30 +423,16 @@ func (n NodeInterface) ConstructOutboxProof(c ctx, evm mech, size, leaf uint64)
return send, root, hashes32, nil
}

func (n NodeInterface) GasEstimateComponents(
c ctx, evm mech, value huge, to addr, contractCreation bool, data []byte,
) (uint64, uint64, huge, huge, error) {
node, err := arbNodeFromNodeInterfaceBackend(n.backend)
if err != nil {
return 0, 0, nil, nil, err
}

if to == types.NodeInterfaceAddress || to == types.NodeInterfaceDebugAddress {
return 0, 0, nil, nil, errors.New("cannot estimate virtual contract")
}

func (n NodeInterface) messageArgs(
evm mech, value huge, to addr, contractCreation bool, data []byte,
) arbitrum.TransactionArgs {
msg := n.sourceMessage
context := n.context
chainid := evm.ChainConfig().ChainID
backend := node.Backend.APIBackend()
gasCap := backend.RPCGasCap()
block := rpc.BlockNumberOrHashWithHash(n.header.Hash(), false)

from := msg.From()
gas := msg.Gas()
nonce := msg.Nonce()
maxFeePerGas := msg.GasFeeCap()
maxPriorityFeePerGas := msg.GasTipCap()
chainid := evm.ChainConfig().ChainID

args := arbitrum.TransactionArgs{
ChainID: (*hexutil.Big)(chainid),
Expand All @@ -461,6 +447,58 @@ func (n NodeInterface) GasEstimateComponents(
if !contractCreation {
args.To = &to
}
return args
}

func (n NodeInterface) GasEstimateL1Component(
c ctx, evm mech, value huge, to addr, contractCreation bool, data []byte,
) (uint64, huge, huge, error) {

// construct a similar message with a random gas limit to avoid underestimating
args := n.messageArgs(evm, value, to, contractCreation, data)
randomGas := l1pricing.RandomGas
args.Gas = (*hexutil.Uint64)(&randomGas)

msg, err := args.ToMessage(randomGas, n.header, evm.StateDB.(*state.StateDB))
if err != nil {
return 0, nil, nil, err
}

pricing := c.State.L1PricingState()
l1BaseFeeEstimate, err := pricing.PricePerUnit()
if err != nil {
return 0, nil, nil, err
}
baseFee, err := c.State.L2PricingState().BaseFeeWei()
if err != nil {
return 0, nil, nil, err
}

// Compute the fee paid for L1 in L2 terms
// See in GasChargingHook that this does not induce truncation error
//
feeForL1, _ := pricing.PosterDataCost(msg, l1pricing.BatchPosterAddress)
feeForL1 = arbmath.BigMulByBips(feeForL1, arbos.GasEstimationL1PricePadding)
gasForL1 := arbmath.BigDiv(feeForL1, baseFee).Uint64()
return gasForL1, baseFee, l1BaseFeeEstimate, nil
}

func (n NodeInterface) GasEstimateComponents(
c ctx, evm mech, value huge, to addr, contractCreation bool, data []byte,
) (uint64, uint64, huge, huge, error) {
node, err := arbNodeFromNodeInterfaceBackend(n.backend)
if err != nil {
return 0, 0, nil, nil, err
}
if to == types.NodeInterfaceAddress || to == types.NodeInterfaceDebugAddress {
return 0, 0, nil, nil, errors.New("cannot estimate virtual contract")
}

context := n.context
backend := node.Backend.APIBackend()
gasCap := backend.RPCGasCap()
block := rpc.BlockNumberOrHashWithHash(n.header.Hash(), false)
args := n.messageArgs(evm, value, to, contractCreation, data)

totalRaw, err := arbitrum.EstimateGas(context, backend, args, block, gasCap)
if err != nil {
Expand All @@ -472,7 +510,7 @@ func (n NodeInterface) GasEstimateComponents(

// Setting the gas will affect the poster data cost
args.Gas = &totalRaw
msg, err = args.ToMessage(gasCap, n.header, evm.StateDB.(*state.StateDB))
msg, err := args.ToMessage(gasCap, n.header, evm.StateDB.(*state.StateDB))
if err != nil {
return 0, 0, nil, nil, err
}
Expand Down

0 comments on commit 90edb28

Please sign in to comment.