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

Commit cd26204

Browse files
authored
feat(proposer): add more tests for propsoer (#686)
1 parent 8c85703 commit cd26204

File tree

4 files changed

+207
-27
lines changed

4 files changed

+207
-27
lines changed

driver/chain_syncer/calldata/syncer.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ func (s *Syncer) onBlockProposed(
239239
// Decode transactions list.
240240
var txListDecoder txlistfetcher.TxListFetcher
241241
if event.Meta.BlobUsed {
242-
txListDecoder = txlistfetcher.NewBlobTxListFetcher(s.rpc)
242+
txListDecoder = txlistfetcher.NewBlobTxListFetcher(s.rpc.L1Beacon)
243243
} else {
244244
txListDecoder = new(txlistfetcher.CalldataFetcher)
245245
}

driver/txlist_fetcher/blob.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@ import (
1616

1717
// BlobFetcher is responsible for fetching the txList blob from the L1 block sidecar.
1818
type BlobFetcher struct {
19-
rpc *rpc.Client
19+
l1Beacon *rpc.BeaconClient
2020
}
2121

2222
// NewBlobTxListFetcher creates a new BlobFetcher instance based on the given rpc client.
23-
func NewBlobTxListFetcher(rpc *rpc.Client) *BlobFetcher {
24-
return &BlobFetcher{rpc}
23+
func NewBlobTxListFetcher(l1Beacon *rpc.BeaconClient) *BlobFetcher {
24+
return &BlobFetcher{l1Beacon}
2525
}
2626

2727
// Fetch implements the TxListFetcher interface.
@@ -35,7 +35,7 @@ func (d *BlobFetcher) Fetch(
3535
}
3636

3737
// Fetch the L1 block sidecars.
38-
sidecars, err := d.rpc.L1Beacon.GetBlobs(ctx, meta.Timestamp)
38+
sidecars, err := d.l1Beacon.GetBlobs(ctx, meta.Timestamp)
3939
if err != nil {
4040
return nil, err
4141
}

internal/testutils/helper.go

+52
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@ import (
66
"crypto/rand"
77
"errors"
88
"fmt"
9+
"math/big"
910
"net/http"
1011
"net/url"
1112
"os"
1213
"time"
1314

1415
"github.com/cenkalti/backoff/v4"
16+
"github.com/ethereum/go-ethereum/accounts/abi/bind"
1517
"github.com/ethereum/go-ethereum/common"
1618
"github.com/ethereum/go-ethereum/common/hexutil"
1719
"github.com/ethereum/go-ethereum/core/types"
@@ -21,6 +23,7 @@ import (
2123
"github.com/phayes/freeport"
2224

2325
"github.com/taikoxyz/taiko-client/bindings"
26+
"github.com/taikoxyz/taiko-client/pkg/rpc"
2427
"github.com/taikoxyz/taiko-client/prover/server"
2528
)
2629

@@ -305,3 +308,52 @@ func LocalRandomProverEndpoint() *url.URL {
305308
func SignatureFromRSV(r, s string, v byte) []byte {
306309
return append(append(hexutil.MustDecode(r), hexutil.MustDecode(s)...), v)
307310
}
311+
312+
// SendDynamicFeeTx sends a dynamic transaction, used for tests.
313+
func SendDynamicFeeTx(
314+
client *rpc.EthClient,
315+
priv *ecdsa.PrivateKey,
316+
to *common.Address,
317+
value *big.Int,
318+
data []byte,
319+
) (*types.Transaction, error) {
320+
head, err := client.HeaderByNumber(context.Background(), nil)
321+
if err != nil {
322+
return nil, err
323+
}
324+
325+
auth, err := bind.NewKeyedTransactorWithChainID(priv, client.ChainID)
326+
if err != nil {
327+
return nil, err
328+
}
329+
330+
nonce, err := client.PendingNonceAt(context.Background(), auth.From)
331+
if err != nil {
332+
return nil, err
333+
}
334+
335+
gasTipCap, err := client.SuggestGasTipCap(context.Background())
336+
if err != nil {
337+
return nil, err
338+
}
339+
340+
tx, err := auth.Signer(auth.From, types.NewTx(&types.DynamicFeeTx{
341+
To: to,
342+
Nonce: nonce,
343+
Value: value,
344+
GasTipCap: gasTipCap,
345+
GasFeeCap: new(big.Int).Add(
346+
gasTipCap,
347+
new(big.Int).Mul(head.BaseFee, big.NewInt(2)),
348+
),
349+
Gas: 2100_000,
350+
Data: data,
351+
}))
352+
if err != nil {
353+
return nil, err
354+
}
355+
if err = client.SendTransaction(context.Background(), tx); err != nil {
356+
return nil, err
357+
}
358+
return tx, nil
359+
}

proposer/proposer_test.go

+150-22
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,54 @@ package proposer
22

33
import (
44
"context"
5-
"math/big"
65
"os"
76
"testing"
87
"time"
98

109
"github.com/ethereum-optimism/optimism/op-service/txmgr"
1110
"github.com/ethereum/go-ethereum/common"
11+
"github.com/ethereum/go-ethereum/common/math"
1212
"github.com/ethereum/go-ethereum/core/types"
1313
"github.com/ethereum/go-ethereum/crypto"
14+
"github.com/ethereum/go-ethereum/log"
15+
"github.com/ethereum/go-ethereum/miner"
16+
"github.com/ethereum/go-ethereum/rlp"
1417
"github.com/stretchr/testify/suite"
1518

1619
"github.com/taikoxyz/taiko-client/bindings"
20+
"github.com/taikoxyz/taiko-client/driver/chain_syncer/beaconsync"
21+
"github.com/taikoxyz/taiko-client/driver/chain_syncer/calldata"
22+
"github.com/taikoxyz/taiko-client/driver/state"
23+
txlistfetcher "github.com/taikoxyz/taiko-client/driver/txlist_fetcher"
1724
"github.com/taikoxyz/taiko-client/internal/testutils"
25+
"github.com/taikoxyz/taiko-client/internal/utils"
1826
"github.com/taikoxyz/taiko-client/pkg/jwt"
1927
"github.com/taikoxyz/taiko-client/pkg/rpc"
2028
)
2129

2230
type ProposerTestSuite struct {
2331
testutils.ClientTestSuite
32+
s *calldata.Syncer
2433
p *Proposer
2534
cancel context.CancelFunc
2635
}
2736

2837
func (s *ProposerTestSuite) SetupTest() {
2938
s.ClientTestSuite.SetupTest()
3039

40+
state2, err := state.New(context.Background(), s.RPCClient)
41+
s.Nil(err)
42+
43+
syncer, err := calldata.NewSyncer(
44+
context.Background(),
45+
s.RPCClient,
46+
state2,
47+
beaconsync.NewSyncProgressTracker(s.RPCClient.L2, 1*time.Hour),
48+
0,
49+
)
50+
s.Nil(err)
51+
s.s = syncer
52+
3153
l1ProposerPrivKey, err := crypto.ToECDSA(common.FromHex(os.Getenv("L1_PROPOSER_PRIVATE_KEY")))
3254
s.Nil(err)
3355

@@ -82,6 +104,132 @@ func (s *ProposerTestSuite) SetupTest() {
82104
s.cancel = cancel
83105
}
84106

107+
func parseTxs(client *rpc.Client, event *bindings.TaikoL1ClientBlockProposed) (types.Transactions, error) {
108+
tx, err := client.L1.TransactionInBlock(context.Background(), event.Raw.BlockHash, event.Raw.TxIndex)
109+
if err != nil {
110+
return nil, err
111+
}
112+
113+
// Decode transactions list.
114+
var txListDecoder txlistfetcher.TxListFetcher
115+
if event.Meta.BlobUsed {
116+
txListDecoder = txlistfetcher.NewBlobTxListFetcher(client.L1Beacon)
117+
} else {
118+
txListDecoder = new(txlistfetcher.CalldataFetcher)
119+
}
120+
txListBytes, err := txListDecoder.Fetch(context.Background(), tx, &event.Meta)
121+
if err != nil {
122+
return nil, err
123+
}
124+
125+
txListBytes, err = utils.Decompress(txListBytes)
126+
if err != nil {
127+
return nil, err
128+
}
129+
130+
var txs types.Transactions
131+
return txs, rlp.DecodeBytes(txListBytes, &txs)
132+
}
133+
134+
func (s *ProposerTestSuite) getLatestProposedTxs(
135+
n int,
136+
timeout time.Duration,
137+
) (<-chan []types.Transactions, error) {
138+
sink := make(chan *bindings.TaikoL1ClientBlockProposed)
139+
sub, err := s.p.rpc.TaikoL1.WatchBlockProposed(nil, sink, nil, nil)
140+
if err != nil {
141+
return nil, err
142+
}
143+
144+
var resCh = make(chan []types.Transactions, 1)
145+
go func() {
146+
defer sub.Unsubscribe()
147+
148+
txLst := make([]types.Transactions, 0, n)
149+
tick := time.After(timeout)
150+
for len(txLst) < cap(txLst) {
151+
select {
152+
case event := <-sink:
153+
txs, err := parseTxs(s.RPCClient, event)
154+
if err != nil {
155+
log.Error("failed to parse txs", "err", err)
156+
}
157+
txLst = append(txLst, txs)
158+
case <-tick:
159+
return
160+
}
161+
}
162+
resCh <- txLst
163+
}()
164+
165+
return resCh, nil
166+
}
167+
168+
func (s *ProposerTestSuite) TestProposeOpNoEmptyBlock() {
169+
defer s.Nil(s.s.ProcessL1Blocks(context.Background()))
170+
171+
p := s.p
172+
173+
batchSize := 100
174+
175+
var err error
176+
for i := 0; i < batchSize; i++ {
177+
to := common.BytesToAddress(testutils.RandomBytes(32))
178+
_, err = testutils.SendDynamicFeeTx(s.RPCClient.L2, s.TestAddrPrivKey, &to, nil, nil)
179+
s.Nil(err)
180+
}
181+
182+
var preBuiltTxList []*miner.PreBuiltTxList
183+
for i := 0; i < 3 && len(preBuiltTxList) == 0; i++ {
184+
preBuiltTxList, err = s.RPCClient.GetPoolContent(
185+
context.Background(),
186+
p.proposerAddress,
187+
p.protocolConfigs.BlockMaxGasLimit,
188+
rpc.BlockMaxTxListBytes,
189+
p.LocalAddresses,
190+
p.MaxProposedTxListsPerEpoch,
191+
)
192+
time.Sleep(time.Second)
193+
}
194+
s.Nil(err)
195+
s.Equal(true, len(preBuiltTxList) > 0)
196+
197+
txsCh, err := s.getLatestProposedTxs(len(preBuiltTxList), time.Minute)
198+
s.Nil(err)
199+
200+
var (
201+
blockMinGasLimit uint64 = math.MaxUint64
202+
blockMinTxListBytes uint64 = math.MaxUint64
203+
txLists = make([]types.Transactions, 0, len(preBuiltTxList))
204+
)
205+
for _, txs := range preBuiltTxList {
206+
if txs.EstimatedGasUsed <= blockMinGasLimit {
207+
blockMinGasLimit = txs.EstimatedGasUsed
208+
} else {
209+
break
210+
}
211+
if txs.BytesLength <= blockMinTxListBytes {
212+
blockMinTxListBytes = txs.BytesLength
213+
} else {
214+
break
215+
}
216+
txLists = append(txLists, txs.TxList)
217+
}
218+
219+
// Start proposer
220+
p.LocalAddressesOnly = false
221+
p.MinGasUsed = blockMinGasLimit
222+
p.MinTxListBytes = blockMinTxListBytes
223+
p.ProposeInterval = time.Second
224+
p.MinProposingInternal = time.Minute
225+
s.Nil(p.ProposeOp(context.Background()))
226+
227+
txs := <-txsCh
228+
for i := 0; i < len(txLists); i++ {
229+
s.Equal(txLists[i].Len(), txs[i].Len())
230+
}
231+
}
232+
85233
func (s *ProposerTestSuite) TestName() {
86234
s.Equal("proposer", s.p.Name())
87235
}
@@ -97,29 +245,9 @@ func (s *ProposerTestSuite) TestProposeOp() {
97245
close(sink)
98246
}()
99247

100-
nonce, err := s.p.rpc.L2.PendingNonceAt(context.Background(), s.TestAddr)
101-
s.Nil(err)
102-
103-
parent, err := s.p.rpc.L2.BlockByNumber(context.Background(), nil)
104-
s.Nil(err)
105-
106-
baseFeeInfo, err := s.p.rpc.TaikoL2.GetBasefee(nil, 1, uint32(parent.GasUsed()))
107-
s.Nil(err)
108-
109248
to := common.BytesToAddress(testutils.RandomBytes(32))
110-
tx := types.NewTx(&types.DynamicFeeTx{
111-
ChainID: s.RPCClient.L2.ChainID,
112-
Nonce: nonce,
113-
GasTipCap: common.Big0,
114-
GasFeeCap: new(big.Int).SetUint64(baseFeeInfo.Basefee.Uint64() * 2),
115-
Gas: 21000,
116-
To: &to,
117-
Value: common.Big1,
118-
})
119-
120-
signedTx, err := types.SignTx(tx, types.LatestSignerForChainID(s.p.rpc.L2.ChainID), s.TestAddrPrivKey)
249+
_, err = testutils.SendDynamicFeeTx(s.p.rpc.L2, s.TestAddrPrivKey, &to, common.Big1, nil)
121250
s.Nil(err)
122-
s.Nil(s.p.rpc.L2.SendTransaction(context.Background(), signedTx))
123251

124252
s.Nil(s.p.ProposeOp(context.Background()))
125253

0 commit comments

Comments
 (0)