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 L1 data fee when estimating mining transaction profit #360

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
96 changes: 76 additions & 20 deletions ethstorage/miner/l1_mining_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ const (
)

var (
mineSig = crypto.Keccak256Hash([]byte(`mine(uint256,uint256,address,uint256,bytes32[],uint256[],bytes,bytes[],bytes[])`))
mineSig = crypto.Keccak256Hash([]byte(`mine(uint256,uint256,address,uint256,bytes32[],uint256[],bytes,bytes[],bytes[])`))
ether = new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil)
txFeeCapDefaultL2 = new(big.Int).Mul(big.NewInt(1000), ether)
gasPriceOracleL2 = common.HexToAddress("0x420000000000000000000000000000000000000F")
)

func NewL1MiningAPI(l1 *eth.PollingClient, rc *eth.RandaoClient, lg log.Logger) *l1MiningAPI {
Expand Down Expand Up @@ -112,12 +115,59 @@ func (m *l1MiningAPI) SubmitMinedResult(ctx context.Context, contract common.Add
}
m.lg.Info("Estimated gas done", "gas", estimatedGas)

nonce, err := m.NonceAt(ctx, cfg.SignerAddr, big.NewInt(rpc.LatestBlockNumber.Int64()))
if err != nil {
m.lg.Error("Query nonce failed", "error", err.Error())
return common.Hash{}, err
}
m.lg.Debug("Query nonce done", "nonce", nonce)
safeGas := uint64(float64(estimatedGas) * gasBufferRatio)

// Check gas fee against tx fee cap (e.g., --rpc.txfeecap as in geth) early to avoid tx fail)
txFeeCapDefault := ether
if m.rc != nil {
txFeeCapDefault = txFeeCapDefaultL2
}
txFee := new(big.Int).Mul(new(big.Int).SetUint64(safeGas), gasFeeCap)
m.lg.Debug("Estimated tx fee on the safe side", "safeGas", safeGas, "txFee", txFee)
if txFee.Cmp(txFeeCapDefault) == 1 {
m.lg.Error("Mining tx dropped: tx fee exceeds the configured cap", "txFee", fmtEth(txFee), "cap", fmtEth(txFeeCapDefault))
return common.Hash{}, errDropped
}

rawTx := &types.DynamicFeeTx{
ChainID: m.NetworkID,
Nonce: nonce,
GasTipCap: tip,
GasFeeCap: gasFeeCap,
Gas: safeGas,
To: &contract,
Value: common.Big0,
Data: calldata,
}

unsignedTx := types.NewTx(rawTx)
extraCost := new(big.Int)
// Add L1 data fee as tx cost when es-node is deployed as an L3
if m.rc != nil {
l1fee, err := m.getL1Fee(ctx, unsignedTx)
syntrust marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
m.lg.Warn("Failed to get L1 fee", "error", err)
} else {
m.lg.Info("Get L1 fee done", "l1Fee", l1fee)
extraCost = l1fee
}
}

reward, err := m.GetMiningReward(rst.startShardId, rst.blockNumber.Int64())
if err != nil {
m.lg.Warn("Query mining reward failed", "error", err.Error())
}
if reward != nil {
profitableGasFeeCap := new(big.Int).Div(new(big.Int).Sub(reward, cfg.MinimumProfit), new(big.Int).SetUint64(estimatedGas))
m.lg.Info("Query mining reward done", "reward", reward)
costCap := new(big.Int).Sub(reward, cfg.MinimumProfit)
costCap = new(big.Int).Add(costCap, extraCost)
syntrust marked this conversation as resolved.
Show resolved Hide resolved
profitableGasFeeCap := new(big.Int).Div(costCap, new(big.Int).SetUint64(estimatedGas))
m.lg.Info("Minimum profitable gas fee cap", "gasFeeCap", profitableGasFeeCap)
if gasFeeCap.Cmp(profitableGasFeeCap) == 1 {
profit := new(big.Int).Sub(reward, new(big.Int).Mul(new(big.Int).SetUint64(estimatedGas), gasFeeCap))
Expand All @@ -137,24 +187,7 @@ func (m *l1MiningAPI) SubmitMinedResult(ctx context.Context, contract common.Add
}

sign := cfg.SignerFnFactory(m.NetworkID)
nonce, err := m.NonceAt(ctx, cfg.SignerAddr, big.NewInt(rpc.LatestBlockNumber.Int64()))
if err != nil {
m.lg.Error("Query nonce failed", "error", err.Error())
return common.Hash{}, err
}
m.lg.Debug("Query nonce done", "nonce", nonce)
gas := uint64(float64(estimatedGas) * gasBufferRatio)
rawTx := &types.DynamicFeeTx{
ChainID: m.NetworkID,
Nonce: nonce,
GasTipCap: tip,
GasFeeCap: gasFeeCap,
Gas: gas,
To: &contract,
Value: common.Big0,
Data: calldata,
}
signedTx, err := sign(ctx, cfg.SignerAddr, types.NewTx(rawTx))
signedTx, err := sign(ctx, cfg.SignerAddr, unsignedTx)
if err != nil {
m.lg.Error("Sign tx error", "error", err)
return common.Hash{}, err
Expand All @@ -169,6 +202,29 @@ func (m *l1MiningAPI) SubmitMinedResult(ctx context.Context, contract common.Add
return signedTx.Hash(), nil
}

func (m *l1MiningAPI) getL1Fee(ctx context.Context, unsignedTx *types.Transaction) (*big.Int, error) {
unsignedBin, err := unsignedTx.MarshalBinary()
if err != nil {
return nil, err
}
bytesType, _ := abi.NewType("bytes", "", nil)
uint256Type, _ := abi.NewType("uint256", "", nil)
dataField, _ := abi.Arguments{{Type: bytesType}}.Pack(unsignedBin)
h := crypto.Keccak256Hash([]byte(`getL1Fee(bytes)`))
bs, err := m.CallContract(ctx, ethereum.CallMsg{
To: &gasPriceOracleL2,
Data: append(h[0:4], dataField...),
}, nil)
if err != nil {
return nil, err
}
res, err := abi.Arguments{{Type: uint256Type}}.UnpackValues(bs)
if err != nil {
return nil, err
}
return res[0].(*big.Int), nil
}

func (m *l1MiningAPI) getRandaoProof(ctx context.Context, blockNumber *big.Int) ([]byte, error) {
var caller interface {
HeaderByNumber(context.Context, *big.Int) (*types.Header, error)
Expand Down
1 change: 1 addition & 0 deletions ethstorage/miner/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,7 @@ func (w *worker) checkTxStatus(txHash common.Hash, miner common.Address) {
}
}
if reward != nil {
// TODO: the cost should include receipt.L1Fee for op-geth
log.Info("Mining transaction accounting (in ether)",
"reward", fmtEth(reward),
"cost", fmtEth(cost),
Expand Down
4 changes: 2 additions & 2 deletions integration_tests/run_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@ if [ -z "$ES_NODE_STORAGE_L1CONTRACT_CLEF" ]; then
fi
# A newly deployed contract is required for each run for miner test, with zkp verifier of mode 2
if [ -z "$ES_NODE_STORAGE_L1CONTRACT" ]; then
export ES_NODE_STORAGE_L1CONTRACT=0xe8F0898cbA701E677970DB33404A817Ff42D4499
export ES_NODE_STORAGE_L1CONTRACT=0x517ad0ba959f3556930c9Bc483B454584F7e11df
syntrust marked this conversation as resolved.
Show resolved Hide resolved
fi
# A contract with zkp verifier of mode 1 (one proof per sample)
if [ -z "$ES_NODE_STORAGE_L1CONTRACT_ZKP1" ]; then
export ES_NODE_STORAGE_L1CONTRACT_ZKP1=0x90e945b64F5Fe312dDE12F4aaBa8868f2fad2398
fi
# The commonly used l1 eth rpc endpoint
if [ -z "$ES_NODE_L1_ETH_RPC" ]; then
export ES_NODE_L1_ETH_RPC="http://65.109.20.29:8545" # L2
export ES_NODE_L1_ETH_RPC="http://5.9.87.214:8545" # L2
fi
# The clef endpoint that the miner will use to sign the transaction
if [ -z "$ES_NODE_CLEF_RPC" ]; then
Expand Down
Loading