Skip to content
This repository was archived by the owner on May 11, 2024. It is now read-only.

Commit 1bd56c0

Browse files
feat(all): use an unified transaction sender implementation (#560)
Co-authored-by: David <david@taiko.xyz>
1 parent e70b7a0 commit 1bd56c0

File tree

13 files changed

+859
-194
lines changed

13 files changed

+859
-194
lines changed

driver/driver_test.go

+16-6
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,10 @@ func (s *DriverTestSuite) TestProcessL1Blocks() {
129129
}
130130

131131
func (s *DriverTestSuite) TestCheckL1ReorgToHigherFork() {
132-
var testnetL1SnapshotID = s.SetL1Snapshot()
133-
132+
var (
133+
testnetL1SnapshotID = s.SetL1Snapshot()
134+
sender = s.p.GetSender()
135+
)
134136
l1Head1, err := s.d.rpc.L1.HeaderByNumber(context.Background(), nil)
135137
s.Nil(err)
136138
l2Head1, err := s.d.rpc.L2.HeaderByNumber(context.Background(), nil)
@@ -164,6 +166,8 @@ func (s *DriverTestSuite) TestCheckL1ReorgToHigherFork() {
164166
s.Equal(l1Head3.Number.Uint64(), l1Head1.Number.Uint64())
165167
s.Equal(l1Head3.Hash(), l1Head1.Hash())
166168

169+
// Because of evm_revert operation, the nonce of the proposer need to be adjusted.
170+
sender.AdjustNonce(nil)
167171
// Propose ten blocks on another fork
168172
for i := 0; i < 10; i++ {
169173
s.ProposeInvalidTxListBytes(s.p)
@@ -188,8 +192,10 @@ func (s *DriverTestSuite) TestCheckL1ReorgToHigherFork() {
188192
}
189193

190194
func (s *DriverTestSuite) TestCheckL1ReorgToLowerFork() {
191-
var testnetL1SnapshotID = s.SetL1Snapshot()
192-
195+
var (
196+
testnetL1SnapshotID = s.SetL1Snapshot()
197+
sender = s.p.GetSender()
198+
)
193199
l1Head1, err := s.d.rpc.L1.HeaderByNumber(context.Background(), nil)
194200
s.Nil(err)
195201
l2Head1, err := s.d.rpc.L2.HeaderByNumber(context.Background(), nil)
@@ -223,6 +229,7 @@ func (s *DriverTestSuite) TestCheckL1ReorgToLowerFork() {
223229
s.Equal(l1Head3.Number.Uint64(), l1Head1.Number.Uint64())
224230
s.Equal(l1Head3.Hash(), l1Head1.Hash())
225231

232+
sender.AdjustNonce(nil)
226233
// Propose one blocks on another fork
227234
s.ProposeInvalidTxListBytes(s.p)
228235

@@ -244,8 +251,10 @@ func (s *DriverTestSuite) TestCheckL1ReorgToLowerFork() {
244251
}
245252

246253
func (s *DriverTestSuite) TestCheckL1ReorgToSameHeightFork() {
247-
var testnetL1SnapshotID = s.SetL1Snapshot()
248-
254+
var (
255+
testnetL1SnapshotID = s.SetL1Snapshot()
256+
sender = s.p.GetSender()
257+
)
249258
l1Head1, err := s.d.rpc.L1.HeaderByNumber(context.Background(), nil)
250259
s.Nil(err)
251260
l2Head1, err := s.d.rpc.L2.HeaderByNumber(context.Background(), nil)
@@ -279,6 +288,7 @@ func (s *DriverTestSuite) TestCheckL1ReorgToSameHeightFork() {
279288
s.Equal(l1Head3.Number.Uint64(), l1Head1.Number.Uint64())
280289
s.Equal(l1Head3.Hash(), l1Head1.Hash())
281290

291+
sender.AdjustNonce(nil)
282292
// Propose two blocks on another fork
283293
s.ProposeInvalidTxListBytes(s.p)
284294
time.Sleep(3 * time.Second)

go.mod

+5-1
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,15 @@ require (
1212
github.com/joho/godotenv v1.5.1
1313
github.com/labstack/echo/v4 v4.11.1
1414
github.com/modern-go/reflect2 v1.0.2
15+
github.com/orcaman/concurrent-map/v2 v2.0.1
16+
github.com/pborman/uuid v1.2.1
1517
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5
1618
github.com/prysmaticlabs/prysm/v4 v4.2.0
1719
github.com/stretchr/testify v1.8.4
1820
github.com/swaggo/swag v1.16.2
1921
github.com/urfave/cli/v2 v2.25.7
2022
golang.org/x/sync v0.5.0
23+
modernc.org/mathutil v1.6.0
2124
)
2225

2326
require (
@@ -79,7 +82,7 @@ require (
7982
github.com/google/gofuzz v1.2.0 // indirect
8083
github.com/google/gopacket v1.1.19 // indirect
8184
github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b // indirect
82-
github.com/google/uuid v1.3.0 // indirect
85+
github.com/google/uuid v1.6.0 // indirect
8386
github.com/gorilla/mux v1.8.0 // indirect
8487
github.com/gorilla/websocket v1.5.0 // indirect
8588
github.com/grpc-ecosystem/grpc-gateway/v2 v2.0.1 // indirect
@@ -164,6 +167,7 @@ require (
164167
github.com/quic-go/quic-go v0.39.3 // indirect
165168
github.com/quic-go/webtransport-go v0.6.0 // indirect
166169
github.com/raulk/go-watchdog v1.3.0 // indirect
170+
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
167171
github.com/rivo/uniseg v0.4.4 // indirect
168172
github.com/rogpeppe/go-internal v1.9.0 // indirect
169173
github.com/rs/cors v1.7.0 // indirect

go.sum

+10-2
Original file line numberDiff line numberDiff line change
@@ -442,8 +442,8 @@ github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3
442442
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
443443
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
444444
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
445-
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
446-
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
445+
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
446+
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
447447
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
448448
github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
449449
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
@@ -797,13 +797,17 @@ github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTm
797797
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
798798
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
799799
github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
800+
github.com/orcaman/concurrent-map/v2 v2.0.1 h1:jOJ5Pg2w1oeB6PeDurIYf6k9PQ+aTITr/6lP/L/zp6c=
801+
github.com/orcaman/concurrent-map/v2 v2.0.1/go.mod h1:9Eq3TG2oBe5FirmYWQfYO5iH1q0Jv47PLaNK++uCdOM=
800802
github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
801803
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
802804
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
803805
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
804806
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0=
805807
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y=
806808
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
809+
github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw=
810+
github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
807811
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
808812
github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
809813
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
@@ -887,6 +891,8 @@ github.com/raulk/go-watchdog v1.3.0 h1:oUmdlHxdkXRJlwfG0O9omj8ukerm8MEQavSiDTEtB
887891
github.com/raulk/go-watchdog v1.3.0/go.mod h1:fIvOnLbF0b0ZwkB9YU4mOW9Did//4vPZtDqv66NfsMU=
888892
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
889893
github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
894+
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
895+
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
890896
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
891897
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
892898
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
@@ -1648,6 +1654,8 @@ k8s.io/utils v0.0.0-20201110183641-67b214c5f920 h1:CbnUZsM497iRC5QMVkHwyl8s2tB3g
16481654
k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
16491655
lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI=
16501656
lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
1657+
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
1658+
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
16511659
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
16521660
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
16531661
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=

internal/sender/common.go

+202
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
package sender
2+
3+
import (
4+
"fmt"
5+
"math/big"
6+
"time"
7+
8+
"github.com/ethereum/go-ethereum/common"
9+
"github.com/ethereum/go-ethereum/core/types"
10+
"github.com/ethereum/go-ethereum/log"
11+
"github.com/holiman/uint256"
12+
"github.com/pborman/uuid"
13+
"modernc.org/mathutil"
14+
15+
"github.com/taikoxyz/taiko-client/pkg/rpc"
16+
)
17+
18+
// adjustGas adjusts the gas fee cap and gas tip cap of the given transaction with the configured
19+
// growth rate.
20+
func (s *Sender) adjustGas(txData types.TxData) {
21+
rate := s.GasGrowthRate + 100
22+
switch baseTx := txData.(type) {
23+
case *types.DynamicFeeTx:
24+
gasFeeCap := baseTx.GasFeeCap.Int64()
25+
gasFeeCap = gasFeeCap / 100 * int64(rate)
26+
gasFeeCap = mathutil.MinInt64(gasFeeCap, int64(s.MaxGasFee))
27+
baseTx.GasFeeCap = big.NewInt(gasFeeCap)
28+
29+
gasTipCap := baseTx.GasTipCap.Int64()
30+
gasTipCap = gasTipCap / 100 * int64(rate)
31+
gasTipCap = mathutil.MinInt64(gasFeeCap, mathutil.MinInt64(gasTipCap, int64(s.MaxGasFee)))
32+
baseTx.GasTipCap = big.NewInt(gasTipCap)
33+
case *types.BlobTx:
34+
gasFeeCap := baseTx.GasFeeCap.Uint64()
35+
gasFeeCap = gasFeeCap / 100 * rate
36+
gasFeeCap = mathutil.MinUint64(gasFeeCap, s.MaxGasFee)
37+
baseTx.GasFeeCap = uint256.NewInt(gasFeeCap)
38+
39+
gasTipCap := baseTx.GasTipCap.Uint64()
40+
gasTipCap = gasTipCap / 100 * rate
41+
gasTipCap = mathutil.MinUint64(gasFeeCap, mathutil.MinUint64(gasTipCap, s.MaxGasFee))
42+
baseTx.GasTipCap = uint256.NewInt(gasTipCap)
43+
44+
blobFeeCap := baseTx.BlobFeeCap.Uint64()
45+
blobFeeCap = blobFeeCap / 100 * rate
46+
blobFeeCap = mathutil.MinUint64(blobFeeCap, s.MaxBlobFee)
47+
baseTx.BlobFeeCap = uint256.NewInt(blobFeeCap)
48+
}
49+
}
50+
51+
// AdjustNonce adjusts the nonce of the given transaction with the current nonce of the sender.
52+
func (s *Sender) AdjustNonce(txData types.TxData) {
53+
nonce, err := s.client.NonceAt(s.ctx, s.Opts.From, nil)
54+
if err != nil {
55+
log.Warn("Failed to get the nonce", "from", s.Opts.From, "err", err)
56+
return
57+
}
58+
s.Opts.Nonce = new(big.Int).SetUint64(nonce)
59+
60+
switch tx := txData.(type) {
61+
case *types.DynamicFeeTx:
62+
tx.Nonce = nonce
63+
case *types.BlobTx:
64+
tx.Nonce = nonce
65+
default:
66+
log.Warn("Unsupported transaction type", "from", s.Opts.From)
67+
}
68+
}
69+
70+
// updateGasTipGasFee updates the gas tip cap and gas fee cap of the sender with the given chain head info.
71+
func (s *Sender) updateGasTipGasFee(head *types.Header) error {
72+
// Get the gas tip cap
73+
gasTipCap, err := s.client.SuggestGasTipCap(s.ctx)
74+
if err != nil {
75+
return err
76+
}
77+
78+
// Get the gas fee cap
79+
gasFeeCap := new(big.Int).Add(gasTipCap, new(big.Int).Mul(head.BaseFee, big.NewInt(2)))
80+
// Check if the gas fee cap is less than the gas tip cap
81+
if gasFeeCap.Cmp(gasTipCap) < 0 {
82+
return fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", gasFeeCap, gasTipCap)
83+
}
84+
maxGasFee := new(big.Int).SetUint64(s.MaxGasFee)
85+
if gasFeeCap.Cmp(maxGasFee) > 0 {
86+
gasFeeCap = new(big.Int).Set(maxGasFee)
87+
gasTipCap = new(big.Int).Set(maxGasFee)
88+
}
89+
90+
s.Opts.GasTipCap = gasTipCap
91+
s.Opts.GasFeeCap = gasFeeCap
92+
93+
return nil
94+
}
95+
96+
// buildTxData assembles the transaction data from the given transaction.
97+
func (s *Sender) buildTxData(tx *types.Transaction) (types.TxData, error) {
98+
switch tx.Type() {
99+
case types.DynamicFeeTxType:
100+
return &types.DynamicFeeTx{
101+
ChainID: s.client.ChainID,
102+
To: tx.To(),
103+
Nonce: tx.Nonce(),
104+
GasFeeCap: s.Opts.GasFeeCap,
105+
GasTipCap: s.Opts.GasTipCap,
106+
Gas: tx.Gas(),
107+
Value: tx.Value(),
108+
Data: tx.Data(),
109+
AccessList: tx.AccessList(),
110+
}, nil
111+
case types.BlobTxType:
112+
var to common.Address
113+
if tx.To() != nil {
114+
to = *tx.To()
115+
}
116+
return &types.BlobTx{
117+
ChainID: uint256.MustFromBig(s.client.ChainID),
118+
To: to,
119+
Nonce: tx.Nonce(),
120+
GasFeeCap: uint256.MustFromBig(s.Opts.GasFeeCap),
121+
GasTipCap: uint256.MustFromBig(s.Opts.GasTipCap),
122+
Gas: tx.Gas(),
123+
Value: uint256.MustFromBig(tx.Value()),
124+
Data: tx.Data(),
125+
AccessList: tx.AccessList(),
126+
BlobFeeCap: uint256.MustFromBig(tx.BlobGasFeeCap()),
127+
BlobHashes: tx.BlobHashes(),
128+
Sidecar: tx.BlobTxSidecar(),
129+
}, nil
130+
default:
131+
return nil, fmt.Errorf("unsupported transaction type: %v", tx.Type())
132+
}
133+
}
134+
135+
// handleReorgTransactions handles the transactions which are backed to the mempool due to reorg.
136+
func (s *Sender) handleReorgTransactions() { // nolint: unused
137+
content, err := rpc.Content(s.ctx, s.client)
138+
if err != nil {
139+
log.Warn("failed to get the unconfirmed transactions", "address", s.Opts.From.String(), "err", err)
140+
return
141+
}
142+
if len(content) == 0 {
143+
return
144+
}
145+
146+
txs := map[common.Hash]*types.Transaction{}
147+
for _, txMapStatus := range content {
148+
for key, txMapNonce := range txMapStatus {
149+
addr := common.HexToAddress(key)
150+
if addr != s.Opts.From {
151+
continue
152+
}
153+
for _, tx := range txMapNonce {
154+
txs[tx.Hash()] = tx
155+
}
156+
}
157+
}
158+
// Remove the already handled transactions.
159+
for _, confirm := range s.unconfirmedTxs.Items() {
160+
delete(txs, confirm.CurrentTx.Hash())
161+
}
162+
for _, tx := range txs {
163+
baseTx, err := s.buildTxData(tx)
164+
if err != nil {
165+
log.Warn("failed to make the transaction data when handle reorg txs", "tx_hash", tx.Hash().String(), "err", err)
166+
return
167+
}
168+
txID := uuid.New()
169+
confirm := &TxToConfirm{
170+
ID: txID,
171+
CurrentTx: tx,
172+
originalTx: baseTx,
173+
}
174+
s.unconfirmedTxs.Set(txID, confirm)
175+
s.txToConfirmCh.Set(txID, make(chan *TxToConfirm, 1))
176+
log.Info("handle reorg tx", "tx_hash", tx.Hash().String(), "tx_id", txID)
177+
}
178+
}
179+
180+
// setDefault sets the default value if the given value is 0.
181+
func setDefault[T uint64 | time.Duration](src, dest T) T {
182+
if src == 0 {
183+
return dest
184+
}
185+
return src
186+
}
187+
188+
// setConfigWithDefaultValues sets the config with default values if the given config is nil.
189+
func setConfigWithDefaultValues(config *Config) *Config {
190+
if config == nil {
191+
return DefaultConfig
192+
}
193+
return &Config{
194+
ConfirmationDepth: setDefault(config.ConfirmationDepth, DefaultConfig.ConfirmationDepth),
195+
MaxRetrys: setDefault(config.MaxRetrys, DefaultConfig.MaxRetrys),
196+
MaxWaitingTime: setDefault(config.MaxWaitingTime, DefaultConfig.MaxWaitingTime),
197+
GasLimit: setDefault(config.GasLimit, DefaultConfig.GasLimit),
198+
GasGrowthRate: setDefault(config.GasGrowthRate, DefaultConfig.GasGrowthRate),
199+
MaxGasFee: setDefault(config.MaxGasFee, DefaultConfig.MaxGasFee),
200+
MaxBlobFee: setDefault(config.MaxBlobFee, DefaultConfig.MaxBlobFee),
201+
}
202+
}

0 commit comments

Comments
 (0)