Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
N
nebula
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
exchain
nebula
Commits
dc1ed3c9
Unverified
Commit
dc1ed3c9
authored
Jan 30, 2022
by
Matthew Slipper
Committed by
GitHub
Jan 30, 2022
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #2086 from cfromknecht/bss-eip-1559
feat: modify txmgr to send EIP-1559 txns
parents
36151fe8
3a7e7098
Changes
16
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
400 additions
and
367 deletions
+400
-367
serious-pets-fly.md
.changeset/serious-pets-fly.md
+5
-0
batch_submitter.go
go/batch-submitter/batch_submitter.go
+0
-4
config.go
go/batch-submitter/config.go
+0
-10
clear_pending_tx.go
go/batch-submitter/drivers/clear_pending_tx.go
+39
-26
clear_pending_tx_test.go
go/batch-submitter/drivers/clear_pending_tx_test.go
+73
-34
interface.go
go/batch-submitter/drivers/interface.go
+7
-7
max_priority_fee_fallback.go
go/batch-submitter/drivers/max_priority_fee_fallback.go
+26
-0
driver.go
go/batch-submitter/drivers/proposer/driver.go
+47
-9
driver.go
go/batch-submitter/drivers/sequencer/driver.go
+41
-7
flags.go
go/batch-submitter/flags/flags.go
+0
-14
l1client.go
go/batch-submitter/mock/l1client.go
+36
-16
service.go
go/batch-submitter/service.go
+5
-11
txmgr.go
go/batch-submitter/txmgr/txmgr.go
+31
-62
txmgr_test.go
go/batch-submitter/txmgr/txmgr_test.go
+90
-137
gas_price.go
go/batch-submitter/utils/gas_price.go
+0
-12
gas_price_test.go
go/batch-submitter/utils/gas_price_test.go
+0
-18
No files found.
.changeset/serious-pets-fly.md
0 → 100644
View file @
dc1ed3c9
---
'
@eth-optimism/batch-submitter-service'
:
patch
---
use EIP-1559 txns for tx/state batches
go/batch-submitter/batch_submitter.go
View file @
dc1ed3c9
...
@@ -12,7 +12,6 @@ import (
...
@@ -12,7 +12,6 @@ import (
"github.com/ethereum-optimism/optimism/go/batch-submitter/drivers/proposer"
"github.com/ethereum-optimism/optimism/go/batch-submitter/drivers/proposer"
"github.com/ethereum-optimism/optimism/go/batch-submitter/drivers/sequencer"
"github.com/ethereum-optimism/optimism/go/batch-submitter/drivers/sequencer"
"github.com/ethereum-optimism/optimism/go/batch-submitter/txmgr"
"github.com/ethereum-optimism/optimism/go/batch-submitter/txmgr"
"github.com/ethereum-optimism/optimism/go/batch-submitter/utils"
l2ethclient
"github.com/ethereum-optimism/optimism/l2geth/ethclient"
l2ethclient
"github.com/ethereum-optimism/optimism/l2geth/ethclient"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto"
...
@@ -159,9 +158,6 @@ func NewBatchSubmitter(cfg Config, gitVersion string) (*BatchSubmitter, error) {
...
@@ -159,9 +158,6 @@ func NewBatchSubmitter(cfg Config, gitVersion string) (*BatchSubmitter, error) {
}
}
txManagerConfig
:=
txmgr
.
Config
{
txManagerConfig
:=
txmgr
.
Config
{
MinGasPrice
:
utils
.
GasPriceFromGwei
(
1
),
MaxGasPrice
:
utils
.
GasPriceFromGwei
(
cfg
.
MaxGasPriceInGwei
),
GasRetryIncrement
:
utils
.
GasPriceFromGwei
(
cfg
.
GasRetryIncrement
),
ResubmissionTimeout
:
cfg
.
ResubmissionTimeout
,
ResubmissionTimeout
:
cfg
.
ResubmissionTimeout
,
ReceiptQueryInterval
:
time
.
Second
,
ReceiptQueryInterval
:
time
.
Second
,
NumConfirmations
:
cfg
.
NumConfirmations
,
NumConfirmations
:
cfg
.
NumConfirmations
,
...
...
go/batch-submitter/config.go
View file @
dc1ed3c9
...
@@ -133,14 +133,6 @@ type Config struct {
...
@@ -133,14 +133,6 @@ type Config struct {
// blocks.
// blocks.
BlockOffset
uint64
BlockOffset
uint64
// MaxGasPriceInGwei is the maximum gas price in gwei we will allow in order
// to confirm a transaction.
MaxGasPriceInGwei
uint64
// GasRetryIncrement is the step size (in gwei) by which we will ratchet the
// gas price in order to get a transaction confirmed.
GasRetryIncrement
uint64
// SequencerPrivateKey the private key of the wallet used to submit
// SequencerPrivateKey the private key of the wallet used to submit
// transactions to the CTC contract.
// transactions to the CTC contract.
SequencerPrivateKey
string
SequencerPrivateKey
string
...
@@ -199,8 +191,6 @@ func NewConfig(ctx *cli.Context) (Config, error) {
...
@@ -199,8 +191,6 @@ func NewConfig(ctx *cli.Context) (Config, error) {
SentryDsn
:
ctx
.
GlobalString
(
flags
.
SentryDsnFlag
.
Name
),
SentryDsn
:
ctx
.
GlobalString
(
flags
.
SentryDsnFlag
.
Name
),
SentryTraceRate
:
ctx
.
GlobalDuration
(
flags
.
SentryTraceRateFlag
.
Name
),
SentryTraceRate
:
ctx
.
GlobalDuration
(
flags
.
SentryTraceRateFlag
.
Name
),
BlockOffset
:
ctx
.
GlobalUint64
(
flags
.
BlockOffsetFlag
.
Name
),
BlockOffset
:
ctx
.
GlobalUint64
(
flags
.
BlockOffsetFlag
.
Name
),
MaxGasPriceInGwei
:
ctx
.
GlobalUint64
(
flags
.
MaxGasPriceInGweiFlag
.
Name
),
GasRetryIncrement
:
ctx
.
GlobalUint64
(
flags
.
GasRetryIncrementFlag
.
Name
),
SequencerPrivateKey
:
ctx
.
GlobalString
(
flags
.
SequencerPrivateKeyFlag
.
Name
),
SequencerPrivateKey
:
ctx
.
GlobalString
(
flags
.
SequencerPrivateKeyFlag
.
Name
),
ProposerPrivateKey
:
ctx
.
GlobalString
(
flags
.
ProposerPrivateKeyFlag
.
Name
),
ProposerPrivateKey
:
ctx
.
GlobalString
(
flags
.
ProposerPrivateKeyFlag
.
Name
),
Mnemonic
:
ctx
.
GlobalString
(
flags
.
MnemonicFlag
.
Name
),
Mnemonic
:
ctx
.
GlobalString
(
flags
.
MnemonicFlag
.
Name
),
...
...
go/batch-submitter/drivers/clear_pending_tx.go
View file @
dc1ed3c9
...
@@ -8,7 +8,6 @@ import (
...
@@ -8,7 +8,6 @@ import (
"strings"
"strings"
"github.com/ethereum-optimism/optimism/go/batch-submitter/txmgr"
"github.com/ethereum-optimism/optimism/go/batch-submitter/txmgr"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/types"
...
@@ -50,20 +49,20 @@ func ClearPendingTx(
...
@@ -50,20 +49,20 @@ func ClearPendingTx(
// price.
// price.
sendTx
:=
func
(
sendTx
:=
func
(
ctx
context
.
Context
,
ctx
context
.
Context
,
gasPrice
*
big
.
Int
,
)
(
*
types
.
Transaction
,
error
)
{
)
(
*
types
.
Transaction
,
error
)
{
log
.
Info
(
name
+
" clearing pending tx"
,
"nonce"
,
nonce
,
log
.
Info
(
name
+
" clearing pending tx"
,
"nonce"
,
nonce
)
"gasPrice"
,
gasPrice
)
signedTx
,
err
:=
SignClearingTx
(
signedTx
,
err
:=
SignClearingTx
(
ctx
,
walletAddr
,
nonce
,
gasPri
ce
,
l1Client
,
privKey
,
chainID
,
name
,
ctx
,
walletAddr
,
non
ce
,
l1Client
,
privKey
,
chainID
,
)
)
if
err
!=
nil
{
if
err
!=
nil
{
log
.
Error
(
name
+
" unable to sign clearing tx"
,
"nonce"
,
nonce
,
log
.
Error
(
name
+
" unable to sign clearing tx"
,
"nonce"
,
nonce
,
"
gasPrice"
,
gasPrice
,
"
err"
,
err
)
"err"
,
err
)
return
nil
,
err
return
nil
,
err
}
}
txHash
:=
signedTx
.
Hash
()
txHash
:=
signedTx
.
Hash
()
gasTipCap
:=
signedTx
.
GasTipCap
()
gasFeeCap
:=
signedTx
.
GasFeeCap
()
err
=
l1Client
.
SendTransaction
(
ctx
,
signedTx
)
err
=
l1Client
.
SendTransaction
(
ctx
,
signedTx
)
switch
{
switch
{
...
@@ -71,7 +70,8 @@ func ClearPendingTx(
...
@@ -71,7 +70,8 @@ func ClearPendingTx(
// Clearing transaction successfully confirmed.
// Clearing transaction successfully confirmed.
case
err
==
nil
:
case
err
==
nil
:
log
.
Info
(
name
+
" submitted clearing tx"
,
"nonce"
,
nonce
,
log
.
Info
(
name
+
" submitted clearing tx"
,
"nonce"
,
nonce
,
"gasPrice"
,
gasPrice
,
"txHash"
,
txHash
)
"gasTipCap"
,
gasTipCap
,
"gasFeeCap"
,
gasFeeCap
,
"txHash"
,
txHash
)
return
signedTx
,
nil
return
signedTx
,
nil
...
@@ -91,8 +91,8 @@ func ClearPendingTx(
...
@@ -91,8 +91,8 @@ func ClearPendingTx(
// transaction, or abort if the old one confirms.
// transaction, or abort if the old one confirms.
default
:
default
:
log
.
Error
(
name
+
" unable to submit clearing tx"
,
log
.
Error
(
name
+
" unable to submit clearing tx"
,
"nonce"
,
nonce
,
"gas
Price"
,
gasPrice
,
"txHash"
,
txHash
,
"nonce"
,
nonce
,
"gas
TipCap"
,
gasTipCap
,
"gasFeeCap"
,
gasFeeCap
,
"err"
,
err
)
"
txHash"
,
txHash
,
"
err"
,
err
)
return
nil
,
err
return
nil
,
err
}
}
}
}
...
@@ -127,26 +127,39 @@ func ClearPendingTx(
...
@@ -127,26 +127,39 @@ func ClearPendingTx(
// SignClearingTx creates a signed clearing tranaction which sends 0 ETH back to
// SignClearingTx creates a signed clearing tranaction which sends 0 ETH back to
// the sender's address. EstimateGas is used to set an appropriate gas limit.
// the sender's address. EstimateGas is used to set an appropriate gas limit.
func
SignClearingTx
(
func
SignClearingTx
(
name
string
,
ctx
context
.
Context
,
ctx
context
.
Context
,
walletAddr
common
.
Address
,
walletAddr
common
.
Address
,
nonce
uint64
,
nonce
uint64
,
gasPrice
*
big
.
Int
,
l1Client
L1Client
,
l1Client
L1Client
,
privKey
*
ecdsa
.
PrivateKey
,
privKey
*
ecdsa
.
PrivateKey
,
chainID
*
big
.
Int
,
chainID
*
big
.
Int
,
)
(
*
types
.
Transaction
,
error
)
{
)
(
*
types
.
Transaction
,
error
)
{
gasLimit
,
err
:=
l1Client
.
EstimateGas
(
ctx
,
ethereum
.
CallMsg
{
gasTipCap
,
err
:=
l1Client
.
SuggestGasTipCap
(
ctx
)
To
:
&
walletAddr
,
if
err
!=
nil
{
GasPrice
:
gasPrice
,
if
!
IsMaxPriorityFeePerGasNotFoundError
(
err
)
{
Value
:
nil
,
return
nil
,
err
Data
:
nil
,
}
})
// If the transaction failed because the backend does not support
// eth_maxPriorityFeePerGas, fallback to using the default constant.
// Currently Alchemy is the only backend provider that exposes this
// method, so in the event their API is unreachable we can fallback to a
// degraded mode of operation. This also applies to our test
// environments, as hardhat doesn't support the query either.
log
.
Warn
(
name
+
" eth_maxPriorityFeePerGas is unsupported "
+
"by current backend, using fallback gasTipCap"
)
gasTipCap
=
FallbackGasTipCap
}
head
,
err
:=
l1Client
.
HeaderByNumber
(
ctx
,
nil
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
err
return
nil
,
err
}
}
tx
:=
CraftClearingTx
(
walletAddr
,
nonce
,
gasPrice
,
gasLimit
)
gasFeeCap
:=
txmgr
.
CalcGasFeeCap
(
head
.
BaseFee
,
gasTipCap
)
tx
:=
CraftClearingTx
(
walletAddr
,
nonce
,
gasFeeCap
,
gasTipCap
)
return
types
.
SignTx
(
return
types
.
SignTx
(
tx
,
types
.
LatestSignerForChainID
(
chainID
),
privKey
,
tx
,
types
.
LatestSignerForChainID
(
chainID
),
privKey
,
...
@@ -158,16 +171,16 @@ func SignClearingTx(
...
@@ -158,16 +171,16 @@ func SignClearingTx(
func
CraftClearingTx
(
func
CraftClearingTx
(
walletAddr
common
.
Address
,
walletAddr
common
.
Address
,
nonce
uint64
,
nonce
uint64
,
gas
Price
*
big
.
Int
,
gas
FeeCap
*
big
.
Int
,
gas
Limit
uint64
,
gas
TipCap
*
big
.
Int
,
)
*
types
.
Transaction
{
)
*
types
.
Transaction
{
return
types
.
NewTx
(
&
types
.
Legacy
Tx
{
return
types
.
NewTx
(
&
types
.
DynamicFee
Tx
{
To
:
&
walletAddr
,
To
:
&
walletAddr
,
Nonce
:
nonce
,
Nonce
:
nonce
,
Gas
Price
:
gasPrice
,
Gas
FeeCap
:
gasFeeCap
,
Gas
:
gasLimit
,
Gas
TipCap
:
gasTipCap
,
Value
:
nil
,
Value
:
nil
,
Data
:
nil
,
Data
:
nil
,
})
})
}
}
go/batch-submitter/drivers/clear_pending_tx_test.go
View file @
dc1ed3c9
...
@@ -11,8 +11,6 @@ import (
...
@@ -11,8 +11,6 @@ import (
"github.com/ethereum-optimism/optimism/go/batch-submitter/drivers"
"github.com/ethereum-optimism/optimism/go/batch-submitter/drivers"
"github.com/ethereum-optimism/optimism/go/batch-submitter/mock"
"github.com/ethereum-optimism/optimism/go/batch-submitter/mock"
"github.com/ethereum-optimism/optimism/go/batch-submitter/txmgr"
"github.com/ethereum-optimism/optimism/go/batch-submitter/txmgr"
"github.com/ethereum-optimism/optimism/go/batch-submitter/utils"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/types"
...
@@ -27,8 +25,6 @@ func init() {
...
@@ -27,8 +25,6 @@ func init() {
}
}
testPrivKey
=
privKey
testPrivKey
=
privKey
testWalletAddr
=
crypto
.
PubkeyToAddress
(
privKey
.
PublicKey
)
testWalletAddr
=
crypto
.
PubkeyToAddress
(
privKey
.
PublicKey
)
testChainID
=
new
(
big
.
Int
)
.
SetUint64
(
1
)
testGasPrice
=
new
(
big
.
Int
)
.
SetUint64
(
3
)
}
}
var
(
var
(
...
@@ -36,21 +32,22 @@ var (
...
@@ -36,21 +32,22 @@ var (
testWalletAddr
common
.
Address
testWalletAddr
common
.
Address
testChainID
=
big
.
NewInt
(
1
)
testChainID
=
big
.
NewInt
(
1
)
testNonce
=
uint64
(
2
)
testNonce
=
uint64
(
2
)
testGas
Price
=
big
.
NewInt
(
3
)
testGas
FeeCap
=
big
.
NewInt
(
3
)
testGas
Limit
=
uint64
(
4
)
testGas
TipCap
=
big
.
NewInt
(
4
)
testBlockNumber
=
uint64
(
5
)
testBlockNumber
=
uint64
(
5
)
testBaseFee
=
big
.
NewInt
(
6
)
)
)
// TestCraftClearingTx asserts that CraftClearingTx produces the expected
// TestCraftClearingTx asserts that CraftClearingTx produces the expected
// unsigned clearing transaction.
// unsigned clearing transaction.
func
TestCraftClearingTx
(
t
*
testing
.
T
)
{
func
TestCraftClearingTx
(
t
*
testing
.
T
)
{
tx
:=
drivers
.
CraftClearingTx
(
tx
:=
drivers
.
CraftClearingTx
(
testWalletAddr
,
testNonce
,
testGas
Price
,
testGasLimit
,
testWalletAddr
,
testNonce
,
testGas
FeeCap
,
testGasTipCap
,
)
)
require
.
Equal
(
t
,
&
testWalletAddr
,
tx
.
To
())
require
.
Equal
(
t
,
&
testWalletAddr
,
tx
.
To
())
require
.
Equal
(
t
,
testNonce
,
tx
.
Nonce
())
require
.
Equal
(
t
,
testNonce
,
tx
.
Nonce
())
require
.
Equal
(
t
,
testGas
Price
,
tx
.
GasPrice
())
require
.
Equal
(
t
,
testGas
FeeCap
,
tx
.
GasFeeCap
())
require
.
Equal
(
t
,
testGas
Limit
,
tx
.
Gas
())
require
.
Equal
(
t
,
testGas
TipCap
,
tx
.
GasTipCap
())
require
.
Equal
(
t
,
new
(
big
.
Int
),
tx
.
Value
())
require
.
Equal
(
t
,
new
(
big
.
Int
),
tx
.
Value
())
require
.
Nil
(
t
,
tx
.
Data
())
require
.
Nil
(
t
,
tx
.
Data
())
}
}
...
@@ -59,21 +56,31 @@ func TestCraftClearingTx(t *testing.T) {
...
@@ -59,21 +56,31 @@ func TestCraftClearingTx(t *testing.T) {
// clearing transaction when the call to EstimateGas succeeds.
// clearing transaction when the call to EstimateGas succeeds.
func
TestSignClearingTxEstimateGasSuccess
(
t
*
testing
.
T
)
{
func
TestSignClearingTxEstimateGasSuccess
(
t
*
testing
.
T
)
{
l1Client
:=
mock
.
NewL1Client
(
mock
.
L1ClientConfig
{
l1Client
:=
mock
.
NewL1Client
(
mock
.
L1ClientConfig
{
EstimateGas
:
func
(
_
context
.
Context
,
_
ethereum
.
CallMsg
)
(
uint64
,
error
)
{
HeaderByNumber
:
func
(
_
context
.
Context
,
_
*
big
.
Int
)
(
*
types
.
Header
,
error
)
{
return
testGasLimit
,
nil
return
&
types
.
Header
{
BaseFee
:
testBaseFee
,
},
nil
},
SuggestGasTipCap
:
func
(
_
context
.
Context
)
(
*
big
.
Int
,
error
)
{
return
testGasTipCap
,
nil
},
},
})
})
expGasFeeCap
:=
new
(
big
.
Int
)
.
Add
(
testGasTipCap
,
new
(
big
.
Int
)
.
Mul
(
testBaseFee
,
big
.
NewInt
(
2
)),
)
tx
,
err
:=
drivers
.
SignClearingTx
(
tx
,
err
:=
drivers
.
SignClearingTx
(
context
.
Background
(),
testWalletAddr
,
testNonce
,
testGasPri
ce
,
l1Client
,
"TEST"
,
context
.
Background
(),
testWalletAddr
,
testNon
ce
,
l1Client
,
testPrivKey
,
testChainID
,
testPrivKey
,
testChainID
,
)
)
require
.
Nil
(
t
,
err
)
require
.
Nil
(
t
,
err
)
require
.
NotNil
(
t
,
tx
)
require
.
NotNil
(
t
,
tx
)
require
.
Equal
(
t
,
&
testWalletAddr
,
tx
.
To
())
require
.
Equal
(
t
,
&
testWalletAddr
,
tx
.
To
())
require
.
Equal
(
t
,
testNonce
,
tx
.
Nonce
())
require
.
Equal
(
t
,
testNonce
,
tx
.
Nonce
())
require
.
Equal
(
t
,
testGasPrice
,
tx
.
GasPrice
())
require
.
Equal
(
t
,
expGasFeeCap
,
tx
.
GasFeeCap
())
require
.
Equal
(
t
,
testGas
Limit
,
tx
.
Gas
())
require
.
Equal
(
t
,
testGas
TipCap
,
tx
.
GasTipCap
())
require
.
Equal
(
t
,
new
(
big
.
Int
),
tx
.
Value
())
require
.
Equal
(
t
,
new
(
big
.
Int
),
tx
.
Value
())
require
.
Nil
(
t
,
tx
.
Data
())
require
.
Nil
(
t
,
tx
.
Data
())
...
@@ -83,22 +90,44 @@ func TestSignClearingTxEstimateGasSuccess(t *testing.T) {
...
@@ -83,22 +90,44 @@ func TestSignClearingTxEstimateGasSuccess(t *testing.T) {
require
.
Equal
(
t
,
testWalletAddr
,
sender
)
require
.
Equal
(
t
,
testWalletAddr
,
sender
)
}
}
// TestSignClearingTx
EstimateGasFail asserts that signing a clearing transaction
// TestSignClearingTx
SuggestGasTipCapFail asserts that signing a clearing
//
will fail if the underlying call to EstimateGas
fails.
//
transaction will fail if the underlying call to SuggestGasTipCap
fails.
func
TestSignClearingTx
EstimateGas
Fail
(
t
*
testing
.
T
)
{
func
TestSignClearingTx
SuggestGasTipCap
Fail
(
t
*
testing
.
T
)
{
err
EstimateGas
:=
errors
.
New
(
"estimate gas
"
)
err
SuggestGasTipCap
:=
errors
.
New
(
"suggest gas tip cap
"
)
l1Client
:=
mock
.
NewL1Client
(
mock
.
L1ClientConfig
{
l1Client
:=
mock
.
NewL1Client
(
mock
.
L1ClientConfig
{
EstimateGas
:
func
(
_
context
.
Context
,
_
ethereum
.
CallMsg
)
(
uint64
,
error
)
{
SuggestGasTipCap
:
func
(
_
context
.
Context
)
(
*
big
.
Int
,
error
)
{
return
0
,
errEstimateGas
return
nil
,
errSuggestGasTipCap
},
},
})
})
tx
,
err
:=
drivers
.
SignClearingTx
(
tx
,
err
:=
drivers
.
SignClearingTx
(
context
.
Background
(),
testWalletAddr
,
testNonce
,
testGasPri
ce
,
l1Client
,
"TEST"
,
context
.
Background
(),
testWalletAddr
,
testNon
ce
,
l1Client
,
testPrivKey
,
testChainID
,
testPrivKey
,
testChainID
,
)
)
require
.
Equal
(
t
,
errEstimateGas
,
err
)
require
.
Equal
(
t
,
errSuggestGasTipCap
,
err
)
require
.
Nil
(
t
,
tx
)
}
// TestSignClearingTxHeaderByNumberFail asserts that signing a clearing
// transaction will fail if the underlying call to HeaderByNumber fails.
func
TestSignClearingTxHeaderByNumberFail
(
t
*
testing
.
T
)
{
errHeaderByNumber
:=
errors
.
New
(
"header by number"
)
l1Client
:=
mock
.
NewL1Client
(
mock
.
L1ClientConfig
{
HeaderByNumber
:
func
(
_
context
.
Context
,
_
*
big
.
Int
)
(
*
types
.
Header
,
error
)
{
return
nil
,
errHeaderByNumber
},
SuggestGasTipCap
:
func
(
_
context
.
Context
)
(
*
big
.
Int
,
error
)
{
return
testGasTipCap
,
nil
},
})
tx
,
err
:=
drivers
.
SignClearingTx
(
"TEST"
,
context
.
Background
(),
testWalletAddr
,
testNonce
,
l1Client
,
testPrivKey
,
testChainID
,
)
require
.
Equal
(
t
,
errHeaderByNumber
,
err
)
require
.
Nil
(
t
,
tx
)
require
.
Nil
(
t
,
tx
)
}
}
...
@@ -117,22 +146,26 @@ func newClearPendingTxHarnessWithNumConfs(
...
@@ -117,22 +146,26 @@ func newClearPendingTxHarnessWithNumConfs(
return
testBlockNumber
,
nil
return
testBlockNumber
,
nil
}
}
}
}
if
l1ClientConfig
.
HeaderByNumber
==
nil
{
l1ClientConfig
.
HeaderByNumber
=
func
(
_
context
.
Context
,
_
*
big
.
Int
)
(
*
types
.
Header
,
error
)
{
return
&
types
.
Header
{
BaseFee
:
testBaseFee
,
},
nil
}
}
if
l1ClientConfig
.
NonceAt
==
nil
{
if
l1ClientConfig
.
NonceAt
==
nil
{
l1ClientConfig
.
NonceAt
=
func
(
_
context
.
Context
,
_
common
.
Address
,
_
*
big
.
Int
)
(
uint64
,
error
)
{
l1ClientConfig
.
NonceAt
=
func
(
_
context
.
Context
,
_
common
.
Address
,
_
*
big
.
Int
)
(
uint64
,
error
)
{
return
testNonce
,
nil
return
testNonce
,
nil
}
}
}
}
if
l1ClientConfig
.
EstimateGas
==
nil
{
if
l1ClientConfig
.
SuggestGasTipCap
==
nil
{
l1ClientConfig
.
EstimateGas
=
func
(
_
context
.
Context
,
_
ethereum
.
CallMsg
)
(
uint64
,
error
)
{
l1ClientConfig
.
SuggestGasTipCap
=
func
(
_
context
.
Context
)
(
*
big
.
Int
,
error
)
{
return
testGas
Limit
,
nil
return
testGas
TipCap
,
nil
}
}
}
}
l1Client
:=
mock
.
NewL1Client
(
l1ClientConfig
)
l1Client
:=
mock
.
NewL1Client
(
l1ClientConfig
)
txMgr
:=
txmgr
.
NewSimpleTxManager
(
"test"
,
txmgr
.
Config
{
txMgr
:=
txmgr
.
NewSimpleTxManager
(
"test"
,
txmgr
.
Config
{
MinGasPrice
:
utils
.
GasPriceFromGwei
(
1
),
MaxGasPrice
:
utils
.
GasPriceFromGwei
(
100
),
GasRetryIncrement
:
utils
.
GasPriceFromGwei
(
5
),
ResubmissionTimeout
:
time
.
Second
,
ResubmissionTimeout
:
time
.
Second
,
ReceiptQueryInterval
:
50
*
time
.
Millisecond
,
ReceiptQueryInterval
:
50
*
time
.
Millisecond
,
NumConfirmations
:
numConfirmations
,
NumConfirmations
:
numConfirmations
,
...
@@ -200,11 +233,14 @@ func TestClearPendingTxTimeout(t *testing.T) {
...
@@ -200,11 +233,14 @@ func TestClearPendingTxTimeout(t *testing.T) {
},
},
})
})
ctx
,
cancel
:=
context
.
WithTimeout
(
context
.
Background
(),
5
*
time
.
Second
)
defer
cancel
()
err
:=
drivers
.
ClearPendingTx
(
err
:=
drivers
.
ClearPendingTx
(
"test"
,
c
ontext
.
Background
(),
h
.
txMgr
,
h
.
l1Client
,
testWalletAddr
,
"test"
,
c
tx
,
h
.
txMgr
,
h
.
l1Client
,
testWalletAddr
,
testPrivKey
,
test
PrivKey
,
test
ChainID
,
testChainID
,
)
)
require
.
Equal
(
t
,
txmgr
.
ErrPublishTimeout
,
err
)
require
.
Equal
(
t
,
context
.
DeadlineExceeded
,
err
)
}
}
// TestClearPendingTxMultipleConfs tests we wait the appropriate number of
// TestClearPendingTxMultipleConfs tests we wait the appropriate number of
...
@@ -225,12 +261,15 @@ func TestClearPendingTxMultipleConfs(t *testing.T) {
...
@@ -225,12 +261,15 @@ func TestClearPendingTxMultipleConfs(t *testing.T) {
},
},
},
numConfs
)
},
numConfs
)
ctx
,
cancel
:=
context
.
WithTimeout
(
context
.
Background
(),
5
*
time
.
Second
)
defer
cancel
()
// The txmgr should timeout waiting for the txn to confirm.
// The txmgr should timeout waiting for the txn to confirm.
err
:=
drivers
.
ClearPendingTx
(
err
:=
drivers
.
ClearPendingTx
(
"test"
,
c
ontext
.
Background
(),
h
.
txMgr
,
h
.
l1Client
,
testWalletAddr
,
"test"
,
c
tx
,
h
.
txMgr
,
h
.
l1Client
,
testWalletAddr
,
testPrivKey
,
test
PrivKey
,
test
ChainID
,
testChainID
,
)
)
require
.
Equal
(
t
,
txmgr
.
ErrPublishTimeout
,
err
)
require
.
Equal
(
t
,
context
.
DeadlineExceeded
,
err
)
// Now set the chain height to the earliest the transaction will be
// Now set the chain height to the earliest the transaction will be
// considered sufficiently confirmed.
// considered sufficiently confirmed.
...
...
go/batch-submitter/drivers/interface.go
View file @
dc1ed3c9
...
@@ -4,7 +4,6 @@ import (
...
@@ -4,7 +4,6 @@ import (
"context"
"context"
"math/big"
"math/big"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/types"
)
)
...
@@ -12,12 +11,9 @@ import (
...
@@ -12,12 +11,9 @@ import (
// L1Client is an abstraction over an L1 Ethereum client functionality required
// L1Client is an abstraction over an L1 Ethereum client functionality required
// by the batch submitter.
// by the batch submitter.
type
L1Client
interface
{
type
L1Client
interface
{
// EstimateGas tries to estimate the gas needed to execute a specific
// HeaderByNumber returns a block header from the current canonical chain.
// transaction based on the current pending state of the backend blockchain.
// If number is nil, the latest known header is returned.
// There is no guarantee that this is the true gas limit requirement as
HeaderByNumber
(
context
.
Context
,
*
big
.
Int
)
(
*
types
.
Header
,
error
)
// other transactions may be added or removed by miners, but it should
// provide a basis for setting a reasonable default.
EstimateGas
(
context
.
Context
,
ethereum
.
CallMsg
)
(
uint64
,
error
)
// NonceAt returns the account nonce of the given account. The block number
// NonceAt returns the account nonce of the given account. The block number
// can be nil, in which case the nonce is taken from the latest known block.
// can be nil, in which case the nonce is taken from the latest known block.
...
@@ -30,6 +26,10 @@ type L1Client interface {
...
@@ -30,6 +26,10 @@ type L1Client interface {
// method to get the contract address after the transaction has been mined.
// method to get the contract address after the transaction has been mined.
SendTransaction
(
context
.
Context
,
*
types
.
Transaction
)
error
SendTransaction
(
context
.
Context
,
*
types
.
Transaction
)
error
// SuggestGasTipCap retrieves the currently suggested gas tip cap after 1559
// to allow a timely execution of a transaction.
SuggestGasTipCap
(
context
.
Context
)
(
*
big
.
Int
,
error
)
// TransactionReceipt returns the receipt of a transaction by transaction
// TransactionReceipt returns the receipt of a transaction by transaction
// hash. Note that the receipt is not available for pending transactions.
// hash. Note that the receipt is not available for pending transactions.
TransactionReceipt
(
context
.
Context
,
common
.
Hash
)
(
*
types
.
Receipt
,
error
)
TransactionReceipt
(
context
.
Context
,
common
.
Hash
)
(
*
types
.
Receipt
,
error
)
...
...
go/batch-submitter/drivers/max_priority_fee_fallback.go
0 → 100644
View file @
dc1ed3c9
package
drivers
import
(
"errors"
"math/big"
"strings"
)
var
(
errMaxPriorityFeePerGasNotFound
=
errors
.
New
(
"Method eth_maxPriorityFeePerGas not found"
,
)
// FallbackGasTipCap is the default fallback gasTipCap used when we are
// unable to query an L1 backend for a suggested gasTipCap.
FallbackGasTipCap
=
big
.
NewInt
(
1500000000
)
)
// IsMaxPriorityFeePerGasNotFoundError returns true if the provided error
// signals that the backend does not support the eth_maxPrirorityFeePerGas
// method. In this case, the caller should fallback to using the constant above.
func
IsMaxPriorityFeePerGasNotFoundError
(
err
error
)
bool
{
return
strings
.
Contains
(
err
.
Error
(),
errMaxPriorityFeePerGasNotFound
.
Error
(),
)
}
go/batch-submitter/drivers/proposer/driver.go
View file @
dc1ed3c9
...
@@ -14,7 +14,6 @@ import (
...
@@ -14,7 +14,6 @@ import (
"github.com/ethereum-optimism/optimism/go/batch-submitter/txmgr"
"github.com/ethereum-optimism/optimism/go/batch-submitter/txmgr"
l2ethclient
"github.com/ethereum-optimism/optimism/l2geth/ethclient"
l2ethclient
"github.com/ethereum-optimism/optimism/l2geth/ethclient"
"github.com/ethereum-optimism/optimism/l2geth/log"
"github.com/ethereum-optimism/optimism/l2geth/log"
"github.com/ethereum-optimism/optimism/l2geth/params"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common"
...
@@ -197,22 +196,43 @@ func (d *Driver) CraftBatchTx(
...
@@ -197,22 +196,43 @@ func (d *Driver) CraftBatchTx(
}
}
opts
.
Context
=
ctx
opts
.
Context
=
ctx
opts
.
Nonce
=
nonce
opts
.
Nonce
=
nonce
opts
.
GasPrice
=
big
.
NewInt
(
params
.
GWei
)
// dummy
opts
.
NoSend
=
true
opts
.
NoSend
=
true
blockOffset
:=
new
(
big
.
Int
)
.
SetUint64
(
d
.
cfg
.
BlockOffset
)
blockOffset
:=
new
(
big
.
Int
)
.
SetUint64
(
d
.
cfg
.
BlockOffset
)
offsetStartsAtIndex
:=
new
(
big
.
Int
)
.
Sub
(
start
,
blockOffset
)
offsetStartsAtIndex
:=
new
(
big
.
Int
)
.
Sub
(
start
,
blockOffset
)
return
d
.
sccContract
.
AppendStateBatch
(
opts
,
stateRoots
,
offsetStartsAtIndex
)
tx
,
err
:=
d
.
sccContract
.
AppendStateBatch
(
opts
,
stateRoots
,
offsetStartsAtIndex
,
)
switch
{
case
err
==
nil
:
return
tx
,
nil
// If the transaction failed because the backend does not support
// eth_maxPriorityFeePerGas, fallback to using the default constant.
// Currently Alchemy is the only backend provider that exposes this method,
// so in the event their API is unreachable we can fallback to a degraded
// mode of operation. This also applies to our test environments, as hardhat
// doesn't support the query either.
case
drivers
.
IsMaxPriorityFeePerGasNotFoundError
(
err
)
:
log
.
Warn
(
d
.
cfg
.
Name
+
" eth_maxPriorityFeePerGas is unsupported "
+
"by current backend, using fallback gasTipCap"
)
opts
.
GasTipCap
=
drivers
.
FallbackGasTipCap
return
d
.
sccContract
.
AppendStateBatch
(
opts
,
stateRoots
,
offsetStartsAtIndex
,
)
default
:
return
nil
,
err
}
}
}
// SubmitBatchTx using the passed transaction as a template, signs and
publishes
// SubmitBatchTx using the passed transaction as a template, signs and
//
an otherwise identical transaction after setting the provided gas price. The
//
publishes the transaction unmodified apart from sampling the current gas
// final transaction is returned to the caller.
//
price. The
final transaction is returned to the caller.
func
(
d
*
Driver
)
SubmitBatchTx
(
func
(
d
*
Driver
)
SubmitBatchTx
(
ctx
context
.
Context
,
ctx
context
.
Context
,
tx
*
types
.
Transaction
,
tx
*
types
.
Transaction
,
gasPrice
*
big
.
Int
,
)
(
*
types
.
Transaction
,
error
)
{
)
(
*
types
.
Transaction
,
error
)
{
opts
,
err
:=
bind
.
NewKeyedTransactorWithChainID
(
opts
,
err
:=
bind
.
NewKeyedTransactorWithChainID
(
...
@@ -223,7 +243,25 @@ func (d *Driver) SubmitBatchTx(
...
@@ -223,7 +243,25 @@ func (d *Driver) SubmitBatchTx(
}
}
opts
.
Context
=
ctx
opts
.
Context
=
ctx
opts
.
Nonce
=
new
(
big
.
Int
)
.
SetUint64
(
tx
.
Nonce
())
opts
.
Nonce
=
new
(
big
.
Int
)
.
SetUint64
(
tx
.
Nonce
())
opts
.
GasPrice
=
gasPrice
return
d
.
rawSccContract
.
RawTransact
(
opts
,
tx
.
Data
())
finalTx
,
err
:=
d
.
rawSccContract
.
RawTransact
(
opts
,
tx
.
Data
())
switch
{
case
err
==
nil
:
return
finalTx
,
nil
// If the transaction failed because the backend does not support
// eth_maxPriorityFeePerGas, fallback to using the default constant.
// Currently Alchemy is the only backend provider that exposes this method,
// so in the event their API is unreachable we can fallback to a degraded
// mode of operation. This also applies to our test environments, as hardhat
// doesn't support the query either.
case
drivers
.
IsMaxPriorityFeePerGasNotFoundError
(
err
)
:
log
.
Warn
(
d
.
cfg
.
Name
+
" eth_maxPriorityFeePerGas is unsupported "
+
"by current backend, using fallback gasTipCap"
)
opts
.
GasTipCap
=
drivers
.
FallbackGasTipCap
return
d
.
rawSccContract
.
RawTransact
(
opts
,
tx
.
Data
())
default
:
return
nil
,
err
}
}
}
go/batch-submitter/drivers/sequencer/driver.go
View file @
dc1ed3c9
...
@@ -12,7 +12,6 @@ import (
...
@@ -12,7 +12,6 @@ import (
"github.com/ethereum-optimism/optimism/go/batch-submitter/metrics"
"github.com/ethereum-optimism/optimism/go/batch-submitter/metrics"
"github.com/ethereum-optimism/optimism/go/batch-submitter/txmgr"
"github.com/ethereum-optimism/optimism/go/batch-submitter/txmgr"
l2ethclient
"github.com/ethereum-optimism/optimism/l2geth/ethclient"
l2ethclient
"github.com/ethereum-optimism/optimism/l2geth/ethclient"
"github.com/ethereum-optimism/optimism/l2geth/params"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common"
...
@@ -233,20 +232,37 @@ func (d *Driver) CraftBatchTx(
...
@@ -233,20 +232,37 @@ func (d *Driver) CraftBatchTx(
}
}
opts
.
Context
=
ctx
opts
.
Context
=
ctx
opts
.
Nonce
=
nonce
opts
.
Nonce
=
nonce
opts
.
GasPrice
=
big
.
NewInt
(
params
.
GWei
)
// dummy
opts
.
NoSend
=
true
opts
.
NoSend
=
true
return
d
.
rawCtcContract
.
RawTransact
(
opts
,
batchCallData
)
tx
,
err
:=
d
.
rawCtcContract
.
RawTransact
(
opts
,
batchCallData
)
switch
{
case
err
==
nil
:
return
tx
,
nil
// If the transaction failed because the backend does not support
// eth_maxPriorityFeePerGas, fallback to using the default constant.
// Currently Alchemy is the only backend provider that exposes this
// method, so in the event their API is unreachable we can fallback to a
// degraded mode of operation. This also applies to our test
// environments, as hardhat doesn't support the query either.
case
drivers
.
IsMaxPriorityFeePerGasNotFoundError
(
err
)
:
log
.
Warn
(
d
.
cfg
.
Name
+
" eth_maxPriorityFeePerGas is unsupported "
+
"by current backend, using fallback gasTipCap"
)
opts
.
GasTipCap
=
drivers
.
FallbackGasTipCap
return
d
.
rawCtcContract
.
RawTransact
(
opts
,
batchCallData
)
default
:
return
nil
,
err
}
}
}
}
}
// SubmitBatchTx using the passed transaction as a template, signs and publishes
// SubmitBatchTx using the passed transaction as a template, signs and publishes
//
an otherwise identical transaction after setting the provided
gas price. The
//
the transaction unmodified apart from sampling the current
gas price. The
// final transaction is returned to the caller.
// final transaction is returned to the caller.
func
(
d
*
Driver
)
SubmitBatchTx
(
func
(
d
*
Driver
)
SubmitBatchTx
(
ctx
context
.
Context
,
ctx
context
.
Context
,
tx
*
types
.
Transaction
,
tx
*
types
.
Transaction
,
gasPrice
*
big
.
Int
,
)
(
*
types
.
Transaction
,
error
)
{
)
(
*
types
.
Transaction
,
error
)
{
opts
,
err
:=
bind
.
NewKeyedTransactorWithChainID
(
opts
,
err
:=
bind
.
NewKeyedTransactorWithChainID
(
...
@@ -257,7 +273,25 @@ func (d *Driver) SubmitBatchTx(
...
@@ -257,7 +273,25 @@ func (d *Driver) SubmitBatchTx(
}
}
opts
.
Context
=
ctx
opts
.
Context
=
ctx
opts
.
Nonce
=
new
(
big
.
Int
)
.
SetUint64
(
tx
.
Nonce
())
opts
.
Nonce
=
new
(
big
.
Int
)
.
SetUint64
(
tx
.
Nonce
())
opts
.
GasPrice
=
gasPrice
return
d
.
rawCtcContract
.
RawTransact
(
opts
,
tx
.
Data
())
finalTx
,
err
:=
d
.
rawCtcContract
.
RawTransact
(
opts
,
tx
.
Data
())
switch
{
case
err
==
nil
:
return
finalTx
,
nil
// If the transaction failed because the backend does not support
// eth_maxPriorityFeePerGas, fallback to using the default constant.
// Currently Alchemy is the only backend provider that exposes this method,
// so in the event their API is unreachable we can fallback to a degraded
// mode of operation. This also applies to our test environments, as hardhat
// doesn't support the query either.
case
drivers
.
IsMaxPriorityFeePerGasNotFoundError
(
err
)
:
log
.
Warn
(
d
.
cfg
.
Name
+
" eth_maxPriorityFeePerGas is unsupported "
+
"by current backend, using fallback gasTipCap"
)
opts
.
GasTipCap
=
drivers
.
FallbackGasTipCap
return
d
.
rawCtcContract
.
RawTransact
(
opts
,
tx
.
Data
())
default
:
return
nil
,
err
}
}
}
go/batch-submitter/flags/flags.go
View file @
dc1ed3c9
...
@@ -151,18 +151,6 @@ var (
...
@@ -151,18 +151,6 @@ var (
Value
:
1
,
Value
:
1
,
EnvVar
:
prefixEnvVar
(
"BLOCK_OFFSET"
),
EnvVar
:
prefixEnvVar
(
"BLOCK_OFFSET"
),
}
}
MaxGasPriceInGweiFlag
=
cli
.
Uint64Flag
{
Name
:
"max-gas-price-in-gwei"
,
Usage
:
"Maximum gas price the batch submitter can use for transactions"
,
Value
:
100
,
EnvVar
:
prefixEnvVar
(
"MAX_GAS_PRICE_IN_GWEI"
),
}
GasRetryIncrementFlag
=
cli
.
Uint64Flag
{
Name
:
"gas-retry-increment"
,
Usage
:
"Default step by which to increment gas price bumps"
,
Value
:
5
,
EnvVar
:
prefixEnvVar
(
"GAS_RETRY_INCREMENT_FLAG"
),
}
SequencerPrivateKeyFlag
=
cli
.
StringFlag
{
SequencerPrivateKeyFlag
=
cli
.
StringFlag
{
Name
:
"sequencer-private-key"
,
Name
:
"sequencer-private-key"
,
Usage
:
"The private key to use for sending to the sequencer contract"
,
Usage
:
"The private key to use for sending to the sequencer contract"
,
...
@@ -235,8 +223,6 @@ var optionalFlags = []cli.Flag{
...
@@ -235,8 +223,6 @@ var optionalFlags = []cli.Flag{
SentryDsnFlag
,
SentryDsnFlag
,
SentryTraceRateFlag
,
SentryTraceRateFlag
,
BlockOffsetFlag
,
BlockOffsetFlag
,
MaxGasPriceInGweiFlag
,
GasRetryIncrementFlag
,
SequencerPrivateKeyFlag
,
SequencerPrivateKeyFlag
,
ProposerPrivateKeyFlag
,
ProposerPrivateKeyFlag
,
MnemonicFlag
,
MnemonicFlag
,
...
...
go/batch-submitter/mock/l1client.go
View file @
dc1ed3c9
...
@@ -5,7 +5,6 @@ import (
...
@@ -5,7 +5,6 @@ import (
"math/big"
"math/big"
"sync"
"sync"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/types"
)
)
...
@@ -16,12 +15,9 @@ type L1ClientConfig struct {
...
@@ -16,12 +15,9 @@ type L1ClientConfig struct {
// BlockNumber returns the most recent block number.
// BlockNumber returns the most recent block number.
BlockNumber
func
(
context
.
Context
)
(
uint64
,
error
)
BlockNumber
func
(
context
.
Context
)
(
uint64
,
error
)
// EstimateGas tries to estimate the gas needed to execute a specific
// HeaderByNumber returns a block header from the current canonical chain.
// transaction based on the current pending state of the backend blockchain.
// If number is nil, the latest known header is returned.
// There is no guarantee that this is the true gas limit requirement as
HeaderByNumber
func
(
context
.
Context
,
*
big
.
Int
)
(
*
types
.
Header
,
error
)
// other transactions may be added or removed by miners, but it should
// provide a basis for setting a reasonable default.
EstimateGas
func
(
context
.
Context
,
ethereum
.
CallMsg
)
(
uint64
,
error
)
// NonceAt returns the account nonce of the given account. The block number
// NonceAt returns the account nonce of the given account. The block number
// can be nil, in which case the nonce is taken from the latest known block.
// can be nil, in which case the nonce is taken from the latest known block.
...
@@ -34,6 +30,10 @@ type L1ClientConfig struct {
...
@@ -34,6 +30,10 @@ type L1ClientConfig struct {
// method to get the contract address after the transaction has been mined.
// method to get the contract address after the transaction has been mined.
SendTransaction
func
(
context
.
Context
,
*
types
.
Transaction
)
error
SendTransaction
func
(
context
.
Context
,
*
types
.
Transaction
)
error
// SuggestGasTipCap retrieves the currently suggested gas tip cap after 1559
// to allow a timely execution of a transaction.
SuggestGasTipCap
func
(
context
.
Context
)
(
*
big
.
Int
,
error
)
// TransactionReceipt returns the receipt of a transaction by transaction
// TransactionReceipt returns the receipt of a transaction by transaction
// hash. Note that the receipt is not available for pending transactions.
// hash. Note that the receipt is not available for pending transactions.
TransactionReceipt
func
(
context
.
Context
,
common
.
Hash
)
(
*
types
.
Receipt
,
error
)
TransactionReceipt
func
(
context
.
Context
,
common
.
Hash
)
(
*
types
.
Receipt
,
error
)
...
@@ -61,12 +61,13 @@ func (c *L1Client) BlockNumber(ctx context.Context) (uint64, error) {
...
@@ -61,12 +61,13 @@ func (c *L1Client) BlockNumber(ctx context.Context) (uint64, error) {
return
c
.
cfg
.
BlockNumber
(
ctx
)
return
c
.
cfg
.
BlockNumber
(
ctx
)
}
}
// EstimateGas executes the mock EstimateGas method.
// HeaderByNumber returns a block header from the current canonical chain. If
func
(
c
*
L1Client
)
EstimateGas
(
ctx
context
.
Context
,
call
ethereum
.
CallMsg
)
(
uint64
,
error
)
{
// number is nil, the latest known header is returned.
func
(
c
*
L1Client
)
HeaderByNumber
(
ctx
context
.
Context
,
blockNumber
*
big
.
Int
)
(
*
types
.
Header
,
error
)
{
c
.
mu
.
RLock
()
c
.
mu
.
RLock
()
defer
c
.
mu
.
RUnlock
()
defer
c
.
mu
.
RUnlock
()
return
c
.
cfg
.
EstimateGas
(
ctx
,
call
)
return
c
.
cfg
.
HeaderByNumber
(
ctx
,
blockNumber
)
}
}
// NonceAt executes the mock NonceAt method.
// NonceAt executes the mock NonceAt method.
...
@@ -85,6 +86,15 @@ func (c *L1Client) SendTransaction(ctx context.Context, tx *types.Transaction) e
...
@@ -85,6 +86,15 @@ func (c *L1Client) SendTransaction(ctx context.Context, tx *types.Transaction) e
return
c
.
cfg
.
SendTransaction
(
ctx
,
tx
)
return
c
.
cfg
.
SendTransaction
(
ctx
,
tx
)
}
}
// SuggestGasTipCap retrieves the currently suggested gas tip cap after 1559 to
// allow a timely execution of a transaction.
func
(
c
*
L1Client
)
SuggestGasTipCap
(
ctx
context
.
Context
)
(
*
big
.
Int
,
error
)
{
c
.
mu
.
RLock
()
defer
c
.
mu
.
RUnlock
()
return
c
.
cfg
.
SuggestGasTipCap
(
ctx
)
}
// TransactionReceipt executes the mock TransactionReceipt method.
// TransactionReceipt executes the mock TransactionReceipt method.
func
(
c
*
L1Client
)
TransactionReceipt
(
ctx
context
.
Context
,
txHash
common
.
Hash
)
(
*
types
.
Receipt
,
error
)
{
func
(
c
*
L1Client
)
TransactionReceipt
(
ctx
context
.
Context
,
txHash
common
.
Hash
)
(
*
types
.
Receipt
,
error
)
{
c
.
mu
.
RLock
()
c
.
mu
.
RLock
()
...
@@ -103,17 +113,17 @@ func (c *L1Client) SetBlockNumberFunc(
...
@@ -103,17 +113,17 @@ func (c *L1Client) SetBlockNumberFunc(
c
.
cfg
.
BlockNumber
=
f
c
.
cfg
.
BlockNumber
=
f
}
}
// Set
EstimateGasFunc overrwrites the mock EstimateGas
method.
// Set
HeaderByNumberFunc overwrites the mock HeaderByNumber
method.
func
(
c
*
L1Client
)
Set
EstimateGas
Func
(
func
(
c
*
L1Client
)
Set
HeaderByNumber
Func
(
f
func
(
c
ontext
.
Context
,
ethereum
.
CallMsg
)
(
uint64
,
error
))
{
f
func
(
c
tx
context
.
Context
,
blockNumber
*
big
.
Int
)
(
*
types
.
Header
,
error
))
{
c
.
mu
.
Lock
()
c
.
mu
.
Lock
()
defer
c
.
mu
.
Unlock
()
defer
c
.
mu
.
Unlock
()
c
.
cfg
.
EstimateGas
=
f
c
.
cfg
.
HeaderByNumber
=
f
}
}
// SetNonceAtFunc over
r
writes the mock NonceAt method.
// SetNonceAtFunc overwrites the mock NonceAt method.
func
(
c
*
L1Client
)
SetNonceAtFunc
(
func
(
c
*
L1Client
)
SetNonceAtFunc
(
f
func
(
context
.
Context
,
common
.
Address
,
*
big
.
Int
)
(
uint64
,
error
))
{
f
func
(
context
.
Context
,
common
.
Address
,
*
big
.
Int
)
(
uint64
,
error
))
{
...
@@ -123,7 +133,7 @@ func (c *L1Client) SetNonceAtFunc(
...
@@ -123,7 +133,7 @@ func (c *L1Client) SetNonceAtFunc(
c
.
cfg
.
NonceAt
=
f
c
.
cfg
.
NonceAt
=
f
}
}
// SetSendTransactionFunc over
r
writes the mock SendTransaction method.
// SetSendTransactionFunc overwrites the mock SendTransaction method.
func
(
c
*
L1Client
)
SetSendTransactionFunc
(
func
(
c
*
L1Client
)
SetSendTransactionFunc
(
f
func
(
context
.
Context
,
*
types
.
Transaction
)
error
)
{
f
func
(
context
.
Context
,
*
types
.
Transaction
)
error
)
{
...
@@ -133,6 +143,16 @@ func (c *L1Client) SetSendTransactionFunc(
...
@@ -133,6 +143,16 @@ func (c *L1Client) SetSendTransactionFunc(
c
.
cfg
.
SendTransaction
=
f
c
.
cfg
.
SendTransaction
=
f
}
}
// SetSuggestGasTipCapFunc overwrites themock SuggestGasTipCap method.
func
(
c
*
L1Client
)
SetSuggestGasTipCapFunc
(
f
func
(
context
.
Context
)
(
*
big
.
Int
,
error
))
{
c
.
mu
.
Lock
()
defer
c
.
mu
.
Unlock
()
c
.
cfg
.
SuggestGasTipCap
=
f
}
// SetTransactionReceiptFunc overwrites the mock TransactionReceipt method.
// SetTransactionReceiptFunc overwrites the mock TransactionReceipt method.
func
(
c
*
L1Client
)
SetTransactionReceiptFunc
(
func
(
c
*
L1Client
)
SetTransactionReceiptFunc
(
f
func
(
context
.
Context
,
common
.
Hash
)
(
*
types
.
Receipt
,
error
))
{
f
func
(
context
.
Context
,
common
.
Hash
)
(
*
types
.
Receipt
,
error
))
{
...
...
go/batch-submitter/service.go
View file @
dc1ed3c9
...
@@ -55,12 +55,11 @@ type Driver interface {
...
@@ -55,12 +55,11 @@ type Driver interface {
)
(
*
types
.
Transaction
,
error
)
)
(
*
types
.
Transaction
,
error
)
// SubmitBatchTx using the passed transaction as a template, signs and
// SubmitBatchTx using the passed transaction as a template, signs and
// publishes
an otherwise identical transaction after setting the provided
// publishes
the transaction unmodified apart from sampling the current gas
//
gas
price. The final transaction is returned to the caller.
// price. The final transaction is returned to the caller.
SubmitBatchTx
(
SubmitBatchTx
(
ctx
context
.
Context
,
ctx
context
.
Context
,
tx
*
types
.
Transaction
,
tx
*
types
.
Transaction
,
gasPrice
*
big
.
Int
,
)
(
*
types
.
Transaction
,
error
)
)
(
*
types
.
Transaction
,
error
)
}
}
...
@@ -194,15 +193,11 @@ func (s *Service) eventLoop() {
...
@@ -194,15 +193,11 @@ func (s *Service) eventLoop() {
// Construct the transaction submission clousure that will attempt
// Construct the transaction submission clousure that will attempt
// to send the next transaction at the given nonce and gas price.
// to send the next transaction at the given nonce and gas price.
sendTx
:=
func
(
sendTx
:=
func
(
ctx
context
.
Context
)
(
*
types
.
Transaction
,
error
)
{
ctx
context
.
Context
,
gasPrice
*
big
.
Int
,
)
(
*
types
.
Transaction
,
error
)
{
log
.
Info
(
name
+
" attempting batch tx"
,
"start"
,
start
,
log
.
Info
(
name
+
" attempting batch tx"
,
"start"
,
start
,
"end"
,
end
,
"nonce"
,
nonce
,
"end"
,
end
,
"nonce"
,
nonce
)
"gasPrice"
,
gasPrice
)
tx
,
err
:=
s
.
cfg
.
Driver
.
SubmitBatchTx
(
ctx
,
tx
,
gasPrice
)
tx
,
err
:=
s
.
cfg
.
Driver
.
SubmitBatchTx
(
ctx
,
tx
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
err
return
nil
,
err
}
}
...
@@ -213,7 +208,6 @@ func (s *Service) eventLoop() {
...
@@ -213,7 +208,6 @@ func (s *Service) eventLoop() {
"end"
,
end
,
"end"
,
end
,
"nonce"
,
nonce
,
"nonce"
,
nonce
,
"tx_hash"
,
tx
.
Hash
(),
"tx_hash"
,
tx
.
Hash
(),
"gasPrice"
,
gasPrice
,
)
)
return
tx
,
nil
return
tx
,
nil
...
...
go/batch-submitter/txmgr/txmgr.go
View file @
dc1ed3c9
...
@@ -2,46 +2,27 @@ package txmgr
...
@@ -2,46 +2,27 @@ package txmgr
import
(
import
(
"context"
"context"
"errors"
"math/big"
"math/big"
"strings"
"strings"
"sync"
"sync"
"time"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/log"
)
)
// ErrPublishTimeout signals that the tx manager did not receive a confirmation
// for a given tx after publishing with the maximum gas price and waiting out a
// resubmission timeout.
var
ErrPublishTimeout
=
errors
.
New
(
"failed to publish tx with max gas price"
)
// SendTxFunc defines a function signature for publishing a desired tx with a
// SendTxFunc defines a function signature for publishing a desired tx with a
// specific gas price. Implementations of this signature should also return
// specific gas price. Implementations of this signature should also return
// promptly when the context is canceled.
// promptly when the context is canceled.
type
SendTxFunc
=
func
(
type
SendTxFunc
=
func
(
ctx
context
.
Context
)
(
*
types
.
Transaction
,
error
)
ctx
context
.
Context
,
gasPrice
*
big
.
Int
)
(
*
types
.
Transaction
,
error
)
// Config houses parameters for altering the behavior of a SimpleTxManager.
// Config houses parameters for altering the behavior of a SimpleTxManager.
type
Config
struct
{
type
Config
struct
{
// Name the name of the driver to appear in log lines.
Name
string
Name
string
// MinGasPrice is the minimum gas price (in gwei). This is used as the
// initial publication attempt.
MinGasPrice
*
big
.
Int
// MaxGasPrice is the maximum gas price (in gwei). This is used to clamp
// the upper end of the range that the TxManager will ever publish when
// attempting to confirm a transaction.
MaxGasPrice
*
big
.
Int
// GasRetryIncrement is the additive gas price (in gwei) that will be
// used to bump each successive tx after a ResubmissionTimeout has
// elapsed.
GasRetryIncrement
*
big
.
Int
// ResubmissionTimeout is the interval at which, if no previously
// ResubmissionTimeout is the interval at which, if no previously
// published transaction has been mined, the new tx with a bumped gas
// published transaction has been mined, the new tx with a bumped gas
// price will be published. Only one publication at MaxGasPrice will be
// price will be published. Only one publication at MaxGasPrice will be
...
@@ -135,25 +116,29 @@ func (m *SimpleTxManager) Send(
...
@@ -135,25 +116,29 @@ func (m *SimpleTxManager) Send(
// background, returning the first successfully mined receipt back to
// background, returning the first successfully mined receipt back to
// the main event loop via receiptChan.
// the main event loop via receiptChan.
receiptChan
:=
make
(
chan
*
types
.
Receipt
,
1
)
receiptChan
:=
make
(
chan
*
types
.
Receipt
,
1
)
sendTxAsync
:=
func
(
gasPrice
*
big
.
Int
)
{
sendTxAsync
:=
func
()
{
defer
wg
.
Done
()
defer
wg
.
Done
()
// Sign and publish transaction with current gas price.
// Sign and publish transaction with current gas price.
tx
,
err
:=
sendTx
(
ctxc
,
gasPrice
)
tx
,
err
:=
sendTx
(
ctxc
)
if
err
!=
nil
{
if
err
!=
nil
{
if
err
==
context
.
Canceled
||
if
err
==
context
.
Canceled
||
strings
.
Contains
(
err
.
Error
(),
"context canceled"
)
{
strings
.
Contains
(
err
.
Error
(),
"context canceled"
)
{
return
return
}
}
log
.
Error
(
name
+
" unable to publish transaction"
,
log
.
Error
(
name
+
" unable to publish transaction"
,
"err"
,
err
)
"gas_price"
,
gasPrice
,
"err"
,
err
)
if
shouldAbortImmediately
(
err
)
{
cancel
()
}
// TODO(conner): add retry?
// TODO(conner): add retry?
return
return
}
}
txHash
:=
tx
.
Hash
()
txHash
:=
tx
.
Hash
()
gasTipCap
:=
tx
.
GasTipCap
()
gasFeeCap
:=
tx
.
GasFeeCap
()
log
.
Info
(
name
+
" transaction published successfully"
,
"hash"
,
txHash
,
log
.
Info
(
name
+
" transaction published successfully"
,
"hash"
,
txHash
,
"gas
_price"
,
gasPrice
)
"gas
TipCap"
,
gasTipCap
,
"gasFeeCap"
,
gasFeeCap
)
// Wait for the transaction to be mined, reporting the receipt
// Wait for the transaction to be mined, reporting the receipt
// back to the main event loop if found.
// back to the main event loop if found.
...
@@ -163,7 +148,7 @@ func (m *SimpleTxManager) Send(
...
@@ -163,7 +148,7 @@ func (m *SimpleTxManager) Send(
)
)
if
err
!=
nil
{
if
err
!=
nil
{
log
.
Debug
(
name
+
" send tx failed"
,
"hash"
,
txHash
,
log
.
Debug
(
name
+
" send tx failed"
,
"hash"
,
txHash
,
"gas
_price"
,
gasPrice
,
"err"
,
err
)
"gas
TipCap"
,
gasTipCap
,
"gasFeeCap"
,
gasFeeCap
,
"err"
,
err
)
}
}
if
receipt
!=
nil
{
if
receipt
!=
nil
{
// Use non-blocking select to ensure function can exit
// Use non-blocking select to ensure function can exit
...
@@ -171,20 +156,17 @@ func (m *SimpleTxManager) Send(
...
@@ -171,20 +156,17 @@ func (m *SimpleTxManager) Send(
select
{
select
{
case
receiptChan
<-
receipt
:
case
receiptChan
<-
receipt
:
log
.
Trace
(
name
+
" send tx succeeded"
,
"hash"
,
txHash
,
log
.
Trace
(
name
+
" send tx succeeded"
,
"hash"
,
txHash
,
"gas
_price"
,
gasPrice
)
"gas
TipCap"
,
gasTipCap
,
"gasFeeCap"
,
gasFeeCap
)
default
:
default
:
}
}
}
}
}
}
// Initialize our initial gas price to the configured minimum.
curGasPrice
:=
new
(
big
.
Int
)
.
Set
(
m
.
cfg
.
MinGasPrice
)
// Submit and wait for the receipt at our first gas price in the
// Submit and wait for the receipt at our first gas price in the
// background, before entering the event loop and waiting out the
// background, before entering the event loop and waiting out the
// resubmission timeout.
// resubmission timeout.
wg
.
Add
(
1
)
wg
.
Add
(
1
)
go
sendTxAsync
(
curGasPrice
)
go
sendTxAsync
()
for
{
for
{
select
{
select
{
...
@@ -192,24 +174,9 @@ func (m *SimpleTxManager) Send(
...
@@ -192,24 +174,9 @@ func (m *SimpleTxManager) Send(
// Whenever a resubmission timeout has elapsed, bump the gas
// Whenever a resubmission timeout has elapsed, bump the gas
// price and publish a new transaction.
// price and publish a new transaction.
case
<-
time
.
After
(
m
.
cfg
.
ResubmissionTimeout
)
:
case
<-
time
.
After
(
m
.
cfg
.
ResubmissionTimeout
)
:
// If our last attempt published at the max gas price,
// return an error as we are unlikely to succeed in
// publishing. This also indicates that the max gas
// price should likely be adjusted higher for the
// daemon.
if
curGasPrice
.
Cmp
(
m
.
cfg
.
MaxGasPrice
)
>=
0
{
return
nil
,
ErrPublishTimeout
}
// Bump the gas price using linear gas price increments.
curGasPrice
=
NextGasPrice
(
curGasPrice
,
m
.
cfg
.
GasRetryIncrement
,
m
.
cfg
.
MaxGasPrice
,
)
// Submit and wait for the bumped traction to confirm.
// Submit and wait for the bumped traction to confirm.
wg
.
Add
(
1
)
wg
.
Add
(
1
)
go
sendTxAsync
(
curGasPrice
)
go
sendTxAsync
()
// The passed context has been canceled, i.e. in the event of a
// The passed context has been canceled, i.e. in the event of a
// shutdown.
// shutdown.
...
@@ -223,6 +190,13 @@ func (m *SimpleTxManager) Send(
...
@@ -223,6 +190,13 @@ func (m *SimpleTxManager) Send(
}
}
}
}
// shouldAbortImmediately returns true if the txmgr should cancel all
// publication attempts and retry. For now, this only includes nonce errors, as
// that error indicates that none of the transactions will ever confirm.
func
shouldAbortImmediately
(
err
error
)
bool
{
return
strings
.
Contains
(
err
.
Error
(),
core
.
ErrNonceTooLow
.
Error
())
}
// WaitMined blocks until the backend indicates confirmation of tx and returns
// WaitMined blocks until the backend indicates confirmation of tx and returns
// the tx receipt. Queries are made every queryInterval, regardless of whether
// the tx receipt. Queries are made every queryInterval, regardless of whether
// the backend returns an error. This method can be canceled using the passed
// the backend returns an error. This method can be canceled using the passed
...
@@ -289,17 +263,12 @@ func WaitMined(
...
@@ -289,17 +263,12 @@ func WaitMined(
}
}
}
}
// NextGasPrice bumps the current gas price using an additive gasRetryIncrement,
// CalcGasFeeCap deterministically computes the recommended gas fee cap given
// clamping the resulting value to maxGasPrice.
// the base fee and gasTipCap. The resulting gasFeeCap is equal to:
//
// gasTipCap + 2*baseFee.
// NOTE: This method does not mutate curGasPrice, but instead returns a copy.
func
CalcGasFeeCap
(
baseFee
,
gasTipCap
*
big
.
Int
)
*
big
.
Int
{
// This removes the possiblity of races occuring from goroutines sharing access
return
new
(
big
.
Int
)
.
Add
(
// to the same underlying big.Int.
gasTipCap
,
func
NextGasPrice
(
curGasPrice
,
gasRetryIncrement
,
maxGasPrice
*
big
.
Int
)
*
big
.
Int
{
new
(
big
.
Int
)
.
Mul
(
baseFee
,
big
.
NewInt
(
2
)),
nextGasPrice
:=
new
(
big
.
Int
)
.
Set
(
curGasPrice
)
)
nextGasPrice
.
Add
(
nextGasPrice
,
gasRetryIncrement
)
if
nextGasPrice
.
Cmp
(
maxGasPrice
)
==
1
{
nextGasPrice
.
Set
(
maxGasPrice
)
}
return
nextGasPrice
}
}
go/batch-submitter/txmgr/txmgr_test.go
View file @
dc1ed3c9
This diff is collapsed.
Click to expand it.
go/batch-submitter/utils/gas_price.go
deleted
100644 → 0
View file @
36151fe8
package
utils
import
(
"math/big"
"github.com/ethereum/go-ethereum/params"
)
// GasPriceFromGwei converts an uint64 gas price in gwei to a big.Int in wei.
func
GasPriceFromGwei
(
gasPriceInGwei
uint64
)
*
big
.
Int
{
return
new
(
big
.
Int
)
.
SetUint64
(
gasPriceInGwei
*
params
.
GWei
)
}
go/batch-submitter/utils/gas_price_test.go
deleted
100644 → 0
View file @
36151fe8
package
utils_test
import
(
"math/big"
"testing"
"github.com/ethereum-optimism/optimism/go/batch-submitter/utils"
"github.com/ethereum/go-ethereum/params"
"github.com/stretchr/testify/require"
)
// TestGasPriceFromGwei asserts that the integer value is scaled properly by
// 10^9.
func
TestGasPriceFromGwei
(
t
*
testing
.
T
)
{
require
.
Equal
(
t
,
utils
.
GasPriceFromGwei
(
0
),
new
(
big
.
Int
))
require
.
Equal
(
t
,
utils
.
GasPriceFromGwei
(
1
),
big
.
NewInt
(
params
.
GWei
))
require
.
Equal
(
t
,
utils
.
GasPriceFromGwei
(
100
),
big
.
NewInt
(
100
*
params
.
GWei
))
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment