diff --git a/ethstorage/miner/l1_mining_api.go b/ethstorage/miner/l1_mining_api.go index 0e5cb625..292b28ba 100644 --- a/ethstorage/miner/l1_mining_api.go +++ b/ethstorage/miner/l1_mining_api.go @@ -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 { @@ -112,56 +115,38 @@ func (m *l1MiningAPI) SubmitMinedResult(ctx context.Context, contract common.Add } m.lg.Info("Estimated gas done", "gas", estimatedGas) - 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("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)) - m.lg.Warn("Mining tx dropped: the profit will not meet expectation", "estimatedProfit", fmtEth(profit), "minimumProfit", fmtEth(cfg.MinimumProfit)) - return common.Hash{}, errDropped - } - if !useConfig { - gasFeeCap = profitableGasFeeCap - m.lg.Info("Using profitable gas fee cap", "gasFeeCap", gasFeeCap) - } - } else { - if !useConfig { - // (tip + 2*baseFee) to ensure the tx to be marketable for six consecutive 100% full blocks. - gasFeeCap = new(big.Int).Add(new(big.Int).Mul(new(big.Int).Sub(gasFeeCap, tip), big.NewInt(2)), tip) - m.lg.Info("Using marketable gas fee cap", "gasFeeCap", gasFeeCap) - } - } - - 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{ + safeGas := uint64(float64(estimatedGas) * gasBufferRatio) + + unsignedTx := types.NewTx(&types.DynamicFeeTx{ ChainID: m.NetworkID, Nonce: nonce, GasTipCap: tip, GasFeeCap: gasFeeCap, - Gas: gas, + Gas: safeGas, To: &contract, Value: common.Big0, Data: calldata, + }) + gasFeeCapChecked, err := checkGasPrice(ctx, m, unsignedTx, rst, cfg.MinimumProfit, gasFeeCap, tip, estimatedGas, safeGas, m.rc != nil, useConfig, m.lg) + if err != nil { + return common.Hash{}, err } - signedTx, err := sign(ctx, cfg.SignerAddr, types.NewTx(rawTx)) + + sign := cfg.SignerFnFactory(m.NetworkID) + signedTx, err := sign(ctx, cfg.SignerAddr, unsignedTx) if err != nil { m.lg.Error("Sign tx error", "error", err) return common.Hash{}, err } err = m.SendTransaction(ctx, signedTx) if err != nil { - m.lg.Error("Send tx failed", "txNonce", nonce, "gasFeeCap", gasFeeCap, "error", err) + m.lg.Error("Send tx failed", "txNonce", nonce, "gasFeeCap", gasFeeCapChecked, "error", err) return common.Hash{}, err } m.lg.Info("Submit mined result done", "shard", rst.startShardId, "block", rst.blockNumber, @@ -169,6 +154,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) @@ -256,3 +264,74 @@ func (m *l1MiningAPI) suggestGasPrices(ctx context.Context, cfg Config) (*big.In } return tip, gasFeeCap, useConfig, nil } + +type GasPriceChecker interface { + GetL1Fee(ctx context.Context, tx *types.Transaction) (*big.Int, error) + GetMiningReward(shardID uint64, blockNumber int64) (*big.Int, error) +} + +// Adjust the gas price based on the estimated rewards, costs, and default tx fee cap. +func checkGasPrice( + ctx context.Context, + checker GasPriceChecker, + unsignedTx *types.Transaction, + rst result, + minProfit, gasFeeCap, tip *big.Int, + estimatedGas, safeGas uint64, + useL2, useConfig bool, + lg log.Logger, +) (*big.Int, error) { + extraCost := new(big.Int) + // Add L1 data fee as tx cost when es-node is deployed as an L3 + if useL2 { + l1fee, err := checker.GetL1Fee(ctx, unsignedTx) + if err != nil { + lg.Warn("Failed to get L1 fee", "error", err) + } else { + lg.Info("Get L1 fee done", "l1Fee", l1fee) + extraCost = l1fee + } + } + reward, err := checker.GetMiningReward(rst.startShardId, rst.blockNumber.Int64()) + if err != nil { + lg.Warn("Query mining reward failed", "error", err.Error()) + } + gasFeeCapChecked := gasFeeCap + if reward != nil { + lg.Info("Query mining reward done", "reward", reward) + costCap := new(big.Int).Sub(reward, minProfit) + txCostCap := new(big.Int).Sub(costCap, extraCost) + lg.Debug("Tx cost cap", "txCostCap", txCostCap) + profitableGasFeeCap := new(big.Int).Div(txCostCap, new(big.Int).SetUint64(estimatedGas)) + 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)) + profit = new(big.Int).Sub(profit, extraCost) + lg.Warn("Mining tx dropped: the profit will not meet expectation", "estimatedProfit", fmtEth(profit), "minimumProfit", fmtEth(minProfit)) + return nil, errDropped + } + if !useConfig { + gasFeeCapChecked = profitableGasFeeCap + lg.Info("Using profitable gas fee cap", "gasFeeCap", gasFeeCapChecked) + } + } else { + if !useConfig { + // (tip + 2*baseFee) to ensure the tx to be marketable for six consecutive 100% full blocks. + gasFeeCapChecked = new(big.Int).Add(new(big.Int).Mul(new(big.Int).Sub(gasFeeCap, tip), big.NewInt(2)), tip) + lg.Info("Using marketable gas fee cap", "gasFeeCap", gasFeeCapChecked) + } + } + + // Check gas fee against tx fee cap (e.g., --rpc.txfeecap as in geth) early to avoid tx fail + txFeeCapDefault := ether + if useL2 { + txFeeCapDefault = txFeeCapDefaultL2 + } + txFee := new(big.Int).Mul(new(big.Int).SetUint64(safeGas), gasFeeCapChecked) + lg.Debug("Estimated tx fee on the safe side", "safeGas", safeGas, "txFee", txFee) + if txFee.Cmp(txFeeCapDefault) == 1 { + gasFeeCapChecked = new(big.Int).Div(txFeeCapDefault, new(big.Int).SetUint64(safeGas)) + lg.Warn("Tx fee exceeds the configured cap, lower the gasFeeCap", "txFee", fmtEth(txFee), "gasFeeCapUpdated", gasFeeCapChecked) + } + return gasFeeCapChecked, nil +} diff --git a/ethstorage/miner/l1_mining_api_test.go b/ethstorage/miner/l1_mining_api_test.go new file mode 100644 index 00000000..112fe13e --- /dev/null +++ b/ethstorage/miner/l1_mining_api_test.go @@ -0,0 +1,271 @@ +// Copyright 2022-2023, EthStorage. +// For license information, see https://github.com/ethstorage/es-node/blob/main/LICENSE +package miner + +import ( + "context" + "fmt" + "math/big" + "os" + "testing" + + "github.com/ethereum/go-ethereum/core/types" + esLog "github.com/ethstorage/go-ethstorage/ethstorage/log" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "golang.org/x/term" +) + +var gwei = new(big.Int).Exp(big.NewInt(10), big.NewInt(9), nil) + +func Test_l1MiningAPI_checkGasPrice(t *testing.T) { + unsignedTx := &types.Transaction{} + mockResult := result{ + startShardId: 0, + blockNumber: big.NewInt(100), + } + lgr := esLog.NewLogger(esLog.CLIConfig{ + Level: "debug", + Format: "text", + Color: term.IsTerminal(int(os.Stdout.Fd())), + }) + + estimatedGas := uint64(500000) + safeGas := uint64(600000) + + testCases := []struct { + name string + + // params + minProfit *big.Int + gasFeeCap *big.Int + tip *big.Int + useL2 bool + useConfig bool + + // mocked results + l1Fee *big.Int + l1FeeErr error + reward *big.Int + rewardErr error + + // test results + wantDropped bool + wantGasFeeCap *big.Int + }{ + { + name: "SWC Beta: relax the gas price until minimal profit remains", + minProfit: big.NewInt(0), + gasFeeCap: big.NewInt(1000251), + tip: big.NewInt(1000000), + useL2: true, + useConfig: false, + l1Fee: new(big.Int).Mul(big.NewInt(25), gwei), + l1FeeErr: nil, + reward: new(big.Int).Mul(big.NewInt(25), ether), + rewardErr: nil, + wantDropped: false, + wantGasFeeCap: new(big.Int).SetInt64(49999999950000), + }, + { + name: "SWC Beta: relax the gas price until minimal profit remains, but cut by tx fee cap", + minProfit: big.NewInt(0), + gasFeeCap: big.NewInt(1000251), + tip: big.NewInt(1000000), + useL2: true, + useConfig: false, + l1Fee: new(big.Int).Mul(big.NewInt(25), gwei), + l1FeeErr: nil, + reward: new(big.Int).Mul(big.NewInt(1000), ether), + rewardErr: nil, + wantDropped: false, + wantGasFeeCap: new(big.Int).SetInt64(1666666666666666), + }, + { + name: "SWC Beta: tx dropped due to low reward", + minProfit: big.NewInt(0), + gasFeeCap: big.NewInt(1000251), + tip: big.NewInt(1000000), + useL2: true, + useConfig: false, + l1Fee: new(big.Int).Mul(big.NewInt(25), gwei), + l1FeeErr: nil, + reward: new(big.Int).Mul(big.NewInt(250), gwei), + rewardErr: nil, + wantDropped: true, + wantGasFeeCap: nil, + }, + { + name: "SWC Beta: tx dropped due to high minimum profit expected", + minProfit: new(big.Int).Mul(big.NewInt(25), ether), + gasFeeCap: big.NewInt(1000251), + tip: big.NewInt(1000000), + useL2: true, + useConfig: false, + l1Fee: new(big.Int).Mul(big.NewInt(25), gwei), + l1FeeErr: nil, + reward: new(big.Int).Mul(big.NewInt(25), ether), + rewardErr: nil, + wantDropped: true, + wantGasFeeCap: nil, + }, + { + name: "SWC Beta: tx dropped due to high gas price", + minProfit: big.NewInt(0), + gasFeeCap: new(big.Int).Mul(big.NewInt(250000), gwei), + tip: big.NewInt(1000000), + useL2: true, + useConfig: false, + l1Fee: new(big.Int).Mul(big.NewInt(25), gwei), + l1FeeErr: nil, + reward: new(big.Int).Mul(big.NewInt(25), ether), + rewardErr: nil, + wantDropped: true, + wantGasFeeCap: nil, + }, + { + name: "SWC Beta: tx dropped due to high l1 data fee", + minProfit: big.NewInt(0), + gasFeeCap: big.NewInt(1000251), + tip: big.NewInt(1000000), + useL2: true, + useConfig: false, + l1Fee: new(big.Int).Mul(big.NewInt(25), ether), + l1FeeErr: nil, + reward: new(big.Int).Mul(big.NewInt(25), ether), + rewardErr: nil, + wantDropped: true, + wantGasFeeCap: nil, + }, + { + name: "SWC Beta: failed to get l1 data fee", + minProfit: big.NewInt(0), + gasFeeCap: big.NewInt(1000251), + tip: big.NewInt(1000000), + useL2: true, + useConfig: false, + l1Fee: nil, + l1FeeErr: fmt.Errorf("l1fee error"), + reward: new(big.Int).Mul(big.NewInt(25), ether), + rewardErr: nil, + wantDropped: false, + wantGasFeeCap: big.NewInt(50000000000000), + }, + + { + name: "SWC Beta: unable to get reward; use marketable gas price", + minProfit: big.NewInt(0), + gasFeeCap: big.NewInt(1000251), + tip: big.NewInt(1000000), + useL2: true, + useConfig: false, + l1Fee: new(big.Int).Mul(big.NewInt(25), gwei), + l1FeeErr: nil, + reward: nil, + rewardErr: fmt.Errorf("reward error"), + wantDropped: false, + wantGasFeeCap: big.NewInt(1000502), + }, + { + name: "SWC Beta: use configured gas price", + minProfit: big.NewInt(0), + gasFeeCap: big.NewInt(10002510), + tip: big.NewInt(1000000), + useL2: true, + useConfig: true, + l1Fee: new(big.Int).Mul(big.NewInt(25), gwei), + l1FeeErr: nil, + reward: new(big.Int).Mul(big.NewInt(25), ether), + rewardErr: nil, + wantDropped: false, + wantGasFeeCap: new(big.Int).SetInt64(10002510), + }, + { + name: "SWC Beta: use configured gas price, but dropped", + minProfit: big.NewInt(0), + gasFeeCap: new(big.Int).Mul(big.NewInt(1), ether), + tip: big.NewInt(1000000), + useL2: true, + useConfig: true, + l1Fee: new(big.Int).Mul(big.NewInt(25), gwei), + l1FeeErr: nil, + reward: new(big.Int).Mul(big.NewInt(25), ether), + rewardErr: nil, + wantDropped: true, + wantGasFeeCap: nil, + }, + { + name: "Sepolia: relax the gas price until minimal profit remains", + minProfit: big.NewInt(0), + gasFeeCap: new(big.Int).Mul(big.NewInt(5), gwei), + tip: new(big.Int).Mul(big.NewInt(1), gwei), + useL2: false, + useConfig: false, + l1Fee: nil, + l1FeeErr: nil, + reward: new(big.Int).Exp(big.NewInt(10), big.NewInt(17), nil), // 0.1 eth + rewardErr: nil, + wantDropped: false, + wantGasFeeCap: new(big.Int).Mul(big.NewInt(200), gwei), + }, + { + name: "Sepolia: relax the gas price until minimal profit remains, but cut by tx fee cap", + minProfit: big.NewInt(0), + gasFeeCap: new(big.Int).Mul(big.NewInt(5), gwei), + tip: new(big.Int).Mul(big.NewInt(1), gwei), + useL2: false, + useConfig: false, + l1Fee: nil, + l1FeeErr: nil, + reward: new(big.Int).Mul(big.NewInt(1), ether), + rewardErr: nil, + wantDropped: false, + wantGasFeeCap: new(big.Int).SetInt64(1666666666666), + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + ctx := context.Background() + mockAPI := &MockMiningAPI{l1MiningAPI: l1MiningAPI{lg: lgr}} + mockAPI.On("GetL1Fee", ctx, unsignedTx).Return(tc.l1Fee, tc.l1FeeErr) + mockAPI.On("GetMiningReward", mockResult.startShardId, mockResult.blockNumber.Int64()).Return(tc.reward, tc.rewardErr) + + gotGasFeeCap, gotErr := checkGasPrice( + ctx, + mockAPI, + unsignedTx, + mockResult, + tc.minProfit, + tc.gasFeeCap, + tc.tip, + estimatedGas, + safeGas, + tc.useL2, + tc.useConfig, + lgr, + ) + if tc.wantDropped { + assert.ErrorIs(t, gotErr, errDropped, "expected an dropped error, but got none") + } else { + assert.NoError(t, gotErr, "unexpected error") + } + assert.Equal(t, tc.wantGasFeeCap, gotGasFeeCap, "unexpected final gasFeeCap") + }) + } +} + +type MockMiningAPI struct { + l1MiningAPI + mock.Mock +} + +func (m *MockMiningAPI) GetL1Fee(ctx context.Context, tx *types.Transaction) (*big.Int, error) { + args := m.Called(ctx, tx) + return args.Get(0).(*big.Int), args.Error(1) +} + +func (m *MockMiningAPI) GetMiningReward(shardId uint64, blockNumber int64) (*big.Int, error) { + args := m.Called(shardId, blockNumber) + return args.Get(0).(*big.Int), args.Error(1) +} diff --git a/ethstorage/miner/worker.go b/ethstorage/miner/worker.go index 4d880a41..f15e1b6a 100644 --- a/ethstorage/miner/worker.go +++ b/ethstorage/miner/worker.go @@ -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), diff --git a/go.mod b/go.mod index ef219c52..5e38a3ec 100644 --- a/go.mod +++ b/go.mod @@ -27,6 +27,7 @@ require ( github.com/protolambda/go-kzg v0.0.0-20221224134646-c91cee5e954e github.com/spf13/cobra v1.5.0 github.com/status-im/keycard-go v0.2.0 + github.com/stretchr/testify v1.8.4 github.com/urfave/cli v1.22.9 golang.org/x/crypto v0.14.0 golang.org/x/sys v0.13.0 @@ -66,7 +67,7 @@ require ( github.com/rivo/uniseg v0.4.3 // indirect github.com/rs/cors v1.9.0 // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect - github.com/stretchr/testify v1.8.4 // indirect + github.com/stretchr/objx v0.5.0 // indirect github.com/supranational/blst v0.3.11 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a // indirect github.com/tetratelabs/wazero v1.8.0 // indirect @@ -154,7 +155,7 @@ require ( github.com/onsi/ginkgo/v2 v2.13.0 // indirect github.com/opencontainers/runtime-spec v1.1.0 // indirect github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect - github.com/pkg/errors v0.9.1 // indirect + github.com/pkg/errors v0.9.1 github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.17.0 github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect diff --git a/go.sum b/go.sum index 25616595..78c6910b 100644 --- a/go.sum +++ b/go.sum @@ -634,13 +634,18 @@ github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DM github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= diff --git a/integration_tests/run_tests.sh b/integration_tests/run_tests.sh index 0b5950fa..fef71557 100755 --- a/integration_tests/run_tests.sh +++ b/integration_tests/run_tests.sh @@ -26,7 +26,7 @@ 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 fi # A contract with zkp verifier of mode 1 (one proof per sample) if [ -z "$ES_NODE_STORAGE_L1CONTRACT_ZKP1" ]; then @@ -34,7 +34,7 @@ if [ -z "$ES_NODE_STORAGE_L1CONTRACT_ZKP1" ]; then 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