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
c1eba2e6
Unverified
Commit
c1eba2e6
authored
Jan 26, 2022
by
Conner Fromknecht
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: modify txmgr to send EIP-1559 txns
parent
1b2897d3
Changes
10
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
278 additions
and
286 deletions
+278
-286
serious-pets-fly.md
.changeset/serious-pets-fly.md
+5
-0
clear_pending_tx.go
go/batch-submitter/drivers/clear_pending_tx.go
+26
-26
clear_pending_tx_test.go
go/batch-submitter/drivers/clear_pending_tx_test.go
+75
-32
interface.go
go/batch-submitter/drivers/interface.go
+7
-7
driver.go
go/batch-submitter/drivers/proposer/driver.go
+3
-7
driver.go
go/batch-submitter/drivers/sequencer/driver.go
+1
-5
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
+30
-48
txmgr_test.go
go/batch-submitter/txmgr/txmgr_test.go
+90
-134
No files found.
.changeset/serious-pets-fly.md
0 → 100644
View file @
c1eba2e6
---
'
@eth-optimism/batch-submitter-service'
:
patch
---
use EIP-1559 txns for tx/state batches
go/batch-submitter/drivers/clear_pending_tx.go
View file @
c1eba2e6
...
...
@@ -8,7 +8,6 @@ import (
"strings"
"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/core"
"github.com/ethereum/go-ethereum/core/types"
...
...
@@ -50,20 +49,20 @@ func ClearPendingTx(
// price.
sendTx
:=
func
(
ctx
context
.
Context
,
gasPrice
*
big
.
Int
,
)
(
*
types
.
Transaction
,
error
)
{
log
.
Info
(
name
+
" clearing pending tx"
,
"nonce"
,
nonce
,
"gasPrice"
,
gasPrice
)
log
.
Info
(
name
+
" clearing pending tx"
,
"nonce"
,
nonce
)
signedTx
,
err
:=
SignClearingTx
(
ctx
,
walletAddr
,
nonce
,
gasPrice
,
l1Client
,
privKey
,
chainID
,
ctx
,
walletAddr
,
nonce
,
l1Client
,
privKey
,
chainID
,
)
if
err
!=
nil
{
log
.
Error
(
name
+
" unable to sign clearing tx"
,
"nonce"
,
nonce
,
"
gasPrice"
,
gasPrice
,
"
err"
,
err
)
"err"
,
err
)
return
nil
,
err
}
txHash
:=
signedTx
.
Hash
()
gasTipCap
:=
signedTx
.
GasTipCap
()
gasFeeCap
:=
signedTx
.
GasFeeCap
()
err
=
l1Client
.
SendTransaction
(
ctx
,
signedTx
)
switch
{
...
...
@@ -71,7 +70,8 @@ func ClearPendingTx(
// Clearing transaction successfully confirmed.
case
err
==
nil
:
log
.
Info
(
name
+
" submitted clearing tx"
,
"nonce"
,
nonce
,
"gasPrice"
,
gasPrice
,
"txHash"
,
txHash
)
"gasTipCap"
,
gasTipCap
,
"gasFeeCap"
,
gasFeeCap
,
"txHash"
,
txHash
)
return
signedTx
,
nil
...
...
@@ -91,8 +91,8 @@ func ClearPendingTx(
// transaction, or abort if the old one confirms.
default
:
log
.
Error
(
name
+
" unable to submit clearing tx"
,
"nonce"
,
nonce
,
"gas
Price"
,
gasPrice
,
"txHash"
,
txHash
,
"err"
,
err
)
"nonce"
,
nonce
,
"gas
TipCap"
,
gasTipCap
,
"gasFeeCap"
,
gasFeeCap
,
"
txHash"
,
txHash
,
"
err"
,
err
)
return
nil
,
err
}
}
...
...
@@ -130,23 +130,23 @@ func SignClearingTx(
ctx
context
.
Context
,
walletAddr
common
.
Address
,
nonce
uint64
,
gasPrice
*
big
.
Int
,
l1Client
L1Client
,
privKey
*
ecdsa
.
PrivateKey
,
chainID
*
big
.
Int
,
)
(
*
types
.
Transaction
,
error
)
{
gas
Limit
,
err
:=
l1Client
.
EstimateGas
(
ctx
,
ethereum
.
CallMsg
{
To
:
&
walletAddr
,
GasPrice
:
gasPrice
,
Value
:
nil
,
Data
:
nil
,
}
)
gas
TipCap
,
err
:=
l1Client
.
SuggestGasTipCap
(
ctx
)
if
err
!=
nil
{
return
nil
,
err
}
head
,
err
:=
l1Client
.
HeaderByNumber
(
ctx
,
nil
)
if
err
!=
nil
{
return
nil
,
err
}
tx
:=
CraftClearingTx
(
walletAddr
,
nonce
,
gasPrice
,
gasLimit
)
gasFeeCap
:=
txmgr
.
CalcGasFeeCap
(
head
.
BaseFee
,
gasTipCap
)
tx
:=
CraftClearingTx
(
walletAddr
,
nonce
,
gasFeeCap
,
gasTipCap
)
return
types
.
SignTx
(
tx
,
types
.
LatestSignerForChainID
(
chainID
),
privKey
,
...
...
@@ -158,16 +158,16 @@ func SignClearingTx(
func
CraftClearingTx
(
walletAddr
common
.
Address
,
nonce
uint64
,
gas
Price
*
big
.
Int
,
gas
Limit
uint64
,
gas
FeeCap
*
big
.
Int
,
gas
TipCap
*
big
.
Int
,
)
*
types
.
Transaction
{
return
types
.
NewTx
(
&
types
.
Legacy
Tx
{
To
:
&
walletAddr
,
Nonce
:
nonce
,
Gas
Price
:
gasPrice
,
Gas
:
gasLimit
,
Value
:
nil
,
Data
:
nil
,
return
types
.
NewTx
(
&
types
.
DynamicFee
Tx
{
To
:
&
walletAddr
,
Nonce
:
nonce
,
Gas
FeeCap
:
gasFeeCap
,
Gas
TipCap
:
gasTipCap
,
Value
:
nil
,
Data
:
nil
,
})
}
go/batch-submitter/drivers/clear_pending_tx_test.go
View file @
c1eba2e6
...
...
@@ -12,7 +12,6 @@ import (
"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/utils"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
...
...
@@ -27,8 +26,6 @@ func init() {
}
testPrivKey
=
privKey
testWalletAddr
=
crypto
.
PubkeyToAddress
(
privKey
.
PublicKey
)
testChainID
=
new
(
big
.
Int
)
.
SetUint64
(
1
)
testGasPrice
=
new
(
big
.
Int
)
.
SetUint64
(
3
)
}
var
(
...
...
@@ -36,21 +33,22 @@ var (
testWalletAddr
common
.
Address
testChainID
=
big
.
NewInt
(
1
)
testNonce
=
uint64
(
2
)
testGas
Price
=
big
.
NewInt
(
3
)
testGas
Limit
=
uint64
(
4
)
testGas
FeeCap
=
big
.
NewInt
(
3
)
testGas
TipCap
=
big
.
NewInt
(
4
)
testBlockNumber
=
uint64
(
5
)
testBaseFee
=
big
.
NewInt
(
6
)
)
// TestCraftClearingTx asserts that CraftClearingTx produces the expected
// unsigned clearing transaction.
func
TestCraftClearingTx
(
t
*
testing
.
T
)
{
tx
:=
drivers
.
CraftClearingTx
(
testWalletAddr
,
testNonce
,
testGas
Price
,
testGasLimit
,
testWalletAddr
,
testNonce
,
testGas
FeeCap
,
testGasTipCap
,
)
require
.
Equal
(
t
,
&
testWalletAddr
,
tx
.
To
())
require
.
Equal
(
t
,
testNonce
,
tx
.
Nonce
())
require
.
Equal
(
t
,
testGas
Price
,
tx
.
GasPrice
())
require
.
Equal
(
t
,
testGas
Limit
,
tx
.
Gas
())
require
.
Equal
(
t
,
testGas
FeeCap
,
tx
.
GasFeeCap
())
require
.
Equal
(
t
,
testGas
TipCap
,
tx
.
GasTipCap
())
require
.
Equal
(
t
,
new
(
big
.
Int
),
tx
.
Value
())
require
.
Nil
(
t
,
tx
.
Data
())
}
...
...
@@ -59,21 +57,31 @@ func TestCraftClearingTx(t *testing.T) {
// clearing transaction when the call to EstimateGas succeeds.
func
TestSignClearingTxEstimateGasSuccess
(
t
*
testing
.
T
)
{
l1Client
:=
mock
.
NewL1Client
(
mock
.
L1ClientConfig
{
EstimateGas
:
func
(
_
context
.
Context
,
_
ethereum
.
CallMsg
)
(
uint64
,
error
)
{
return
testGasLimit
,
nil
HeaderByNumber
:
func
(
_
context
.
Context
,
_
*
big
.
Int
)
(
*
types
.
Header
,
error
)
{
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
(
context
.
Background
(),
testWalletAddr
,
testNonce
,
testGasPrice
,
l1Client
,
test
PrivKey
,
test
ChainID
,
context
.
Background
(),
testWalletAddr
,
testNonce
,
l1Client
,
testPrivKey
,
testChainID
,
)
require
.
Nil
(
t
,
err
)
require
.
NotNil
(
t
,
tx
)
require
.
Equal
(
t
,
&
testWalletAddr
,
tx
.
To
())
require
.
Equal
(
t
,
testNonce
,
tx
.
Nonce
())
require
.
Equal
(
t
,
testGasPrice
,
tx
.
GasPrice
())
require
.
Equal
(
t
,
testGas
Limit
,
tx
.
Gas
())
require
.
Equal
(
t
,
expGasFeeCap
,
tx
.
GasFeeCap
())
require
.
Equal
(
t
,
testGas
TipCap
,
tx
.
GasTipCap
())
require
.
Equal
(
t
,
new
(
big
.
Int
),
tx
.
Value
())
require
.
Nil
(
t
,
tx
.
Data
())
...
...
@@ -83,22 +91,44 @@ func TestSignClearingTxEstimateGasSuccess(t *testing.T) {
require
.
Equal
(
t
,
testWalletAddr
,
sender
)
}
// TestSignClearingTx
EstimateGasFail asserts that signing a clearing transaction
//
will fail if the underlying call to EstimateGas
fails.
func
TestSignClearingTx
EstimateGas
Fail
(
t
*
testing
.
T
)
{
err
EstimateGas
:=
errors
.
New
(
"estimate gas
"
)
// TestSignClearingTx
SuggestGasTipCapFail asserts that signing a clearing
//
transaction will fail if the underlying call to SuggestGasTipCap
fails.
func
TestSignClearingTx
SuggestGasTipCap
Fail
(
t
*
testing
.
T
)
{
err
SuggestGasTipCap
:=
errors
.
New
(
"suggest gas tip cap
"
)
l1Client
:=
mock
.
NewL1Client
(
mock
.
L1ClientConfig
{
EstimateGas
:
func
(
_
context
.
Context
,
_
ethereum
.
CallMsg
)
(
uint64
,
error
)
{
return
0
,
errEstimateGas
SuggestGasTipCap
:
func
(
_
context
.
Context
)
(
*
big
.
Int
,
error
)
{
return
nil
,
errSuggestGasTipCap
},
})
tx
,
err
:=
drivers
.
SignClearingTx
(
context
.
Background
(),
testWalletAddr
,
testNonce
,
testGasPrice
,
l1Client
,
testPrivKey
,
testChainID
,
context
.
Background
(),
testWalletAddr
,
testNonce
,
l1Client
,
testPrivKey
,
testChainID
,
)
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
(
context
.
Background
(),
testWalletAddr
,
testNonce
,
l1Client
,
testPrivKey
,
testChainID
,
)
require
.
Equal
(
t
,
err
EstimateGas
,
err
)
require
.
Equal
(
t
,
err
HeaderByNumber
,
err
)
require
.
Nil
(
t
,
tx
)
}
...
...
@@ -117,14 +147,21 @@ func newClearPendingTxHarnessWithNumConfs(
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
{
l1ClientConfig
.
NonceAt
=
func
(
_
context
.
Context
,
_
common
.
Address
,
_
*
big
.
Int
)
(
uint64
,
error
)
{
return
testNonce
,
nil
}
}
if
l1ClientConfig
.
EstimateGas
==
nil
{
l1ClientConfig
.
EstimateGas
=
func
(
_
context
.
Context
,
_
ethereum
.
CallMsg
)
(
uint64
,
error
)
{
return
testGas
Limit
,
nil
if
l1ClientConfig
.
SuggestGasTipCap
==
nil
{
l1ClientConfig
.
SuggestGasTipCap
=
func
(
_
context
.
Context
)
(
*
big
.
Int
,
error
)
{
return
testGas
TipCap
,
nil
}
}
...
...
@@ -200,11 +237,14 @@ func TestClearPendingTxTimeout(t *testing.T) {
},
})
ctx
,
cancel
:=
context
.
WithTimeout
(
context
.
Background
(),
5
*
time
.
Second
)
defer
cancel
()
err
:=
drivers
.
ClearPendingTx
(
"test"
,
c
ontext
.
Background
(),
h
.
txMgr
,
h
.
l1Client
,
testWalletAddr
,
test
PrivKey
,
test
ChainID
,
"test"
,
c
tx
,
h
.
txMgr
,
h
.
l1Client
,
testWalletAddr
,
testPrivKey
,
testChainID
,
)
require
.
Equal
(
t
,
txmgr
.
ErrPublishTimeout
,
err
)
require
.
Equal
(
t
,
context
.
DeadlineExceeded
,
err
)
}
// TestClearPendingTxMultipleConfs tests we wait the appropriate number of
...
...
@@ -225,12 +265,15 @@ func TestClearPendingTxMultipleConfs(t *testing.T) {
},
},
numConfs
)
ctx
,
cancel
:=
context
.
WithTimeout
(
context
.
Background
(),
5
*
time
.
Second
)
defer
cancel
()
// The txmgr should timeout waiting for the txn to confirm.
err
:=
drivers
.
ClearPendingTx
(
"test"
,
c
ontext
.
Background
(),
h
.
txMgr
,
h
.
l1Client
,
testWalletAddr
,
test
PrivKey
,
test
ChainID
,
"test"
,
c
tx
,
h
.
txMgr
,
h
.
l1Client
,
testWalletAddr
,
testPrivKey
,
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
// considered sufficiently confirmed.
...
...
go/batch-submitter/drivers/interface.go
View file @
c1eba2e6
...
...
@@ -4,7 +4,6 @@ import (
"context"
"math/big"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
)
...
...
@@ -12,12 +11,9 @@ import (
// L1Client is an abstraction over an L1 Ethereum client functionality required
// by the batch submitter.
type
L1Client
interface
{
// EstimateGas tries to estimate the gas needed to execute a specific
// transaction based on the current pending state of the backend blockchain.
// There is no guarantee that this is the true gas limit requirement as
// 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
)
// HeaderByNumber returns a block header from the current canonical chain.
// If number is nil, the latest known header is returned.
HeaderByNumber
(
context
.
Context
,
*
big
.
Int
)
(
*
types
.
Header
,
error
)
// 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.
...
...
@@ -30,6 +26,10 @@ type L1Client interface {
// method to get the contract address after the transaction has been mined.
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
// hash. Note that the receipt is not available for pending transactions.
TransactionReceipt
(
context
.
Context
,
common
.
Hash
)
(
*
types
.
Receipt
,
error
)
...
...
go/batch-submitter/drivers/proposer/driver.go
View file @
c1eba2e6
...
...
@@ -14,7 +14,6 @@ import (
"github.com/ethereum-optimism/optimism/go/batch-submitter/txmgr"
l2ethclient
"github.com/ethereum-optimism/optimism/l2geth/ethclient"
"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/bind"
"github.com/ethereum/go-ethereum/common"
...
...
@@ -197,7 +196,6 @@ func (d *Driver) CraftBatchTx(
}
opts
.
Context
=
ctx
opts
.
Nonce
=
nonce
opts
.
GasPrice
=
big
.
NewInt
(
params
.
GWei
)
// dummy
opts
.
NoSend
=
true
blockOffset
:=
new
(
big
.
Int
)
.
SetUint64
(
d
.
cfg
.
BlockOffset
)
...
...
@@ -206,13 +204,12 @@ func (d *Driver) CraftBatchTx(
return
d
.
sccContract
.
AppendStateBatch
(
opts
,
stateRoots
,
offsetStartsAtIndex
)
}
// SubmitBatchTx using the passed transaction as a template, signs and
publishes
//
an otherwise identical transaction after setting the provided gas price. The
// final transaction is returned to the caller.
// SubmitBatchTx using the passed transaction as a template, signs and
//
publishes the transaction unmodified apart from sampling the current gas
//
price. The
final transaction is returned to the caller.
func
(
d
*
Driver
)
SubmitBatchTx
(
ctx
context
.
Context
,
tx
*
types
.
Transaction
,
gasPrice
*
big
.
Int
,
)
(
*
types
.
Transaction
,
error
)
{
opts
,
err
:=
bind
.
NewKeyedTransactorWithChainID
(
...
...
@@ -223,7 +220,6 @@ func (d *Driver) SubmitBatchTx(
}
opts
.
Context
=
ctx
opts
.
Nonce
=
new
(
big
.
Int
)
.
SetUint64
(
tx
.
Nonce
())
opts
.
GasPrice
=
gasPrice
return
d
.
rawSccContract
.
RawTransact
(
opts
,
tx
.
Data
())
}
go/batch-submitter/drivers/sequencer/driver.go
View file @
c1eba2e6
...
...
@@ -12,7 +12,6 @@ import (
"github.com/ethereum-optimism/optimism/go/batch-submitter/metrics"
"github.com/ethereum-optimism/optimism/go/batch-submitter/txmgr"
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/bind"
"github.com/ethereum/go-ethereum/common"
...
...
@@ -233,7 +232,6 @@ func (d *Driver) CraftBatchTx(
}
opts
.
Context
=
ctx
opts
.
Nonce
=
nonce
opts
.
GasPrice
=
big
.
NewInt
(
params
.
GWei
)
// dummy
opts
.
NoSend
=
true
return
d
.
rawCtcContract
.
RawTransact
(
opts
,
batchCallData
)
...
...
@@ -241,12 +239,11 @@ func (d *Driver) CraftBatchTx(
}
// 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.
func
(
d
*
Driver
)
SubmitBatchTx
(
ctx
context
.
Context
,
tx
*
types
.
Transaction
,
gasPrice
*
big
.
Int
,
)
(
*
types
.
Transaction
,
error
)
{
opts
,
err
:=
bind
.
NewKeyedTransactorWithChainID
(
...
...
@@ -257,7 +254,6 @@ func (d *Driver) SubmitBatchTx(
}
opts
.
Context
=
ctx
opts
.
Nonce
=
new
(
big
.
Int
)
.
SetUint64
(
tx
.
Nonce
())
opts
.
GasPrice
=
gasPrice
return
d
.
rawCtcContract
.
RawTransact
(
opts
,
tx
.
Data
())
}
go/batch-submitter/mock/l1client.go
View file @
c1eba2e6
...
...
@@ -5,7 +5,6 @@ import (
"math/big"
"sync"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
)
...
...
@@ -16,12 +15,9 @@ type L1ClientConfig struct {
// BlockNumber returns the most recent block number.
BlockNumber
func
(
context
.
Context
)
(
uint64
,
error
)
// EstimateGas tries to estimate the gas needed to execute a specific
// transaction based on the current pending state of the backend blockchain.
// There is no guarantee that this is the true gas limit requirement as
// 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
)
// HeaderByNumber returns a block header from the current canonical chain.
// If number is nil, the latest known header is returned.
HeaderByNumber
func
(
context
.
Context
,
*
big
.
Int
)
(
*
types
.
Header
,
error
)
// 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.
...
...
@@ -34,6 +30,10 @@ type L1ClientConfig struct {
// method to get the contract address after the transaction has been mined.
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
// hash. Note that the receipt is not available for pending transactions.
TransactionReceipt
func
(
context
.
Context
,
common
.
Hash
)
(
*
types
.
Receipt
,
error
)
...
...
@@ -61,12 +61,13 @@ func (c *L1Client) BlockNumber(ctx context.Context) (uint64, error) {
return
c
.
cfg
.
BlockNumber
(
ctx
)
}
// EstimateGas executes the mock EstimateGas method.
func
(
c
*
L1Client
)
EstimateGas
(
ctx
context
.
Context
,
call
ethereum
.
CallMsg
)
(
uint64
,
error
)
{
// HeaderByNumber returns a block header from the current canonical chain. If
// 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
()
defer
c
.
mu
.
RUnlock
()
return
c
.
cfg
.
EstimateGas
(
ctx
,
call
)
return
c
.
cfg
.
HeaderByNumber
(
ctx
,
blockNumber
)
}
// NonceAt executes the mock NonceAt method.
...
...
@@ -85,6 +86,15 @@ func (c *L1Client) SendTransaction(ctx context.Context, tx *types.Transaction) e
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.
func
(
c
*
L1Client
)
TransactionReceipt
(
ctx
context
.
Context
,
txHash
common
.
Hash
)
(
*
types
.
Receipt
,
error
)
{
c
.
mu
.
RLock
()
...
...
@@ -103,17 +113,17 @@ func (c *L1Client) SetBlockNumberFunc(
c
.
cfg
.
BlockNumber
=
f
}
// Set
EstimateGasFunc overrwrites the mock EstimateGas
method.
func
(
c
*
L1Client
)
Set
EstimateGas
Func
(
f
func
(
c
ontext
.
Context
,
ethereum
.
CallMsg
)
(
uint64
,
error
))
{
// Set
HeaderByNumberFunc overwrites the mock HeaderByNumber
method.
func
(
c
*
L1Client
)
Set
HeaderByNumber
Func
(
f
func
(
c
tx
context
.
Context
,
blockNumber
*
big
.
Int
)
(
*
types
.
Header
,
error
))
{
c
.
mu
.
Lock
()
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
(
f
func
(
context
.
Context
,
common
.
Address
,
*
big
.
Int
)
(
uint64
,
error
))
{
...
...
@@ -123,7 +133,7 @@ func (c *L1Client) SetNonceAtFunc(
c
.
cfg
.
NonceAt
=
f
}
// SetSendTransactionFunc over
r
writes the mock SendTransaction method.
// SetSendTransactionFunc overwrites the mock SendTransaction method.
func
(
c
*
L1Client
)
SetSendTransactionFunc
(
f
func
(
context
.
Context
,
*
types
.
Transaction
)
error
)
{
...
...
@@ -133,6 +143,16 @@ func (c *L1Client) SetSendTransactionFunc(
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.
func
(
c
*
L1Client
)
SetTransactionReceiptFunc
(
f
func
(
context
.
Context
,
common
.
Hash
)
(
*
types
.
Receipt
,
error
))
{
...
...
go/batch-submitter/service.go
View file @
c1eba2e6
...
...
@@ -55,12 +55,11 @@ type Driver interface {
)
(
*
types
.
Transaction
,
error
)
// SubmitBatchTx using the passed transaction as a template, signs and
// publishes
an otherwise identical transaction after setting the provided
//
gas
price. The final transaction is returned to the caller.
// publishes
the transaction unmodified apart from sampling the current gas
// price. The final transaction is returned to the caller.
SubmitBatchTx
(
ctx
context
.
Context
,
tx
*
types
.
Transaction
,
gasPrice
*
big
.
Int
,
)
(
*
types
.
Transaction
,
error
)
}
...
...
@@ -194,15 +193,11 @@ func (s *Service) eventLoop() {
// Construct the transaction submission clousure that will attempt
// to send the next transaction at the given nonce and gas price.
sendTx
:=
func
(
ctx
context
.
Context
,
gasPrice
*
big
.
Int
,
)
(
*
types
.
Transaction
,
error
)
{
sendTx
:=
func
(
ctx
context
.
Context
)
(
*
types
.
Transaction
,
error
)
{
log
.
Info
(
name
+
" attempting batch tx"
,
"start"
,
start
,
"end"
,
end
,
"nonce"
,
nonce
,
"gasPrice"
,
gasPrice
)
"end"
,
end
,
"nonce"
,
nonce
)
tx
,
err
:=
s
.
cfg
.
Driver
.
SubmitBatchTx
(
ctx
,
tx
,
gasPrice
)
tx
,
err
:=
s
.
cfg
.
Driver
.
SubmitBatchTx
(
ctx
,
tx
)
if
err
!=
nil
{
return
nil
,
err
}
...
...
@@ -213,7 +208,6 @@ func (s *Service) eventLoop() {
"end"
,
end
,
"nonce"
,
nonce
,
"tx_hash"
,
tx
.
Hash
(),
"gasPrice"
,
gasPrice
,
)
return
tx
,
nil
...
...
go/batch-submitter/txmgr/txmgr.go
View file @
c1eba2e6
...
...
@@ -2,27 +2,21 @@ package txmgr
import
(
"context"
"errors"
"math/big"
"strings"
"sync"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"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
// specific gas price. Implementations of this signature should also return
// promptly when the context is canceled.
type
SendTxFunc
=
func
(
ctx
context
.
Context
,
gasPrice
*
big
.
Int
)
(
*
types
.
Transaction
,
error
)
type
SendTxFunc
=
func
(
ctx
context
.
Context
)
(
*
types
.
Transaction
,
error
)
// Config houses parameters for altering the behavior of a SimpleTxManager.
type
Config
struct
{
...
...
@@ -135,25 +129,29 @@ func (m *SimpleTxManager) Send(
// background, returning the first successfully mined receipt back to
// the main event loop via receiptChan.
receiptChan
:=
make
(
chan
*
types
.
Receipt
,
1
)
sendTxAsync
:=
func
(
gasPrice
*
big
.
Int
)
{
sendTxAsync
:=
func
()
{
defer
wg
.
Done
()
// Sign and publish transaction with current gas price.
tx
,
err
:=
sendTx
(
ctxc
,
gasPrice
)
tx
,
err
:=
sendTx
(
ctxc
)
if
err
!=
nil
{
if
err
==
context
.
Canceled
||
strings
.
Contains
(
err
.
Error
(),
"context canceled"
)
{
return
}
log
.
Error
(
name
+
" unable to publish transaction"
,
"gas_price"
,
gasPrice
,
"err"
,
err
)
log
.
Error
(
name
+
" unable to publish transaction"
,
"err"
,
err
)
if
shouldAbortImmediately
(
err
)
{
cancel
()
}
// TODO(conner): add retry?
return
}
txHash
:=
tx
.
Hash
()
gasTipCap
:=
tx
.
GasTipCap
()
gasFeeCap
:=
tx
.
GasFeeCap
()
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
// back to the main event loop if found.
...
...
@@ -163,7 +161,7 @@ func (m *SimpleTxManager) Send(
)
if
err
!=
nil
{
log
.
Debug
(
name
+
" send tx failed"
,
"hash"
,
txHash
,
"gas
_price"
,
gasPrice
,
"err"
,
err
)
"gas
TipCap"
,
gasTipCap
,
"gasFeeCap"
,
gasFeeCap
,
"err"
,
err
)
}
if
receipt
!=
nil
{
// Use non-blocking select to ensure function can exit
...
...
@@ -171,20 +169,17 @@ func (m *SimpleTxManager) Send(
select
{
case
receiptChan
<-
receipt
:
log
.
Trace
(
name
+
" send tx succeeded"
,
"hash"
,
txHash
,
"gas
_price"
,
gasPrice
)
"gas
TipCap"
,
gasTipCap
,
"gasFeeCap"
,
gasFeeCap
)
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
// background, before entering the event loop and waiting out the
// resubmission timeout.
wg
.
Add
(
1
)
go
sendTxAsync
(
curGasPrice
)
go
sendTxAsync
()
for
{
select
{
...
...
@@ -192,24 +187,9 @@ func (m *SimpleTxManager) Send(
// Whenever a resubmission timeout has elapsed, bump the gas
// price and publish a new transaction.
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.
wg
.
Add
(
1
)
go
sendTxAsync
(
curGasPrice
)
go
sendTxAsync
()
// The passed context has been canceled, i.e. in the event of a
// shutdown.
...
...
@@ -223,6 +203,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
// the tx receipt. Queries are made every queryInterval, regardless of whether
// the backend returns an error. This method can be canceled using the passed
...
...
@@ -289,17 +276,12 @@ func WaitMined(
}
}
// NextGasPrice bumps the current gas price using an additive gasRetryIncrement,
// clamping the resulting value to maxGasPrice.
//
// NOTE: This method does not mutate curGasPrice, but instead returns a copy.
// This removes the possiblity of races occuring from goroutines sharing access
// to the same underlying big.Int.
func
NextGasPrice
(
curGasPrice
,
gasRetryIncrement
,
maxGasPrice
*
big
.
Int
)
*
big
.
Int
{
nextGasPrice
:=
new
(
big
.
Int
)
.
Set
(
curGasPrice
)
nextGasPrice
.
Add
(
nextGasPrice
,
gasRetryIncrement
)
if
nextGasPrice
.
Cmp
(
maxGasPrice
)
==
1
{
nextGasPrice
.
Set
(
maxGasPrice
)
}
return
nextGasPrice
// CalcGasFeeCap deterministically computes the recommended gas fee cap given
// the base fee and gasTipCap. The resulting gasFeeCap is equal to:
// gasTipCap + 2*baseFee.
func
CalcGasFeeCap
(
baseFee
,
gasTipCap
*
big
.
Int
)
*
big
.
Int
{
return
new
(
big
.
Int
)
.
Add
(
gasTipCap
,
new
(
big
.
Int
)
.
Mul
(
baseFee
,
big
.
NewInt
(
2
)),
)
}
go/batch-submitter/txmgr/txmgr_test.go
View file @
c1eba2e6
This diff is collapsed.
Click to expand it.
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