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

Commit

Permalink
feat(proposer): add more tests for propsoer (#686)
Browse files Browse the repository at this point in the history
  • Loading branch information
mask-pp authored Apr 5, 2024
1 parent 8c85703 commit cd26204
Show file tree
Hide file tree
Showing 4 changed files with 207 additions and 27 deletions.
2 changes: 1 addition & 1 deletion driver/chain_syncer/calldata/syncer.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ func (s *Syncer) onBlockProposed(
// Decode transactions list.
var txListDecoder txlistfetcher.TxListFetcher
if event.Meta.BlobUsed {
txListDecoder = txlistfetcher.NewBlobTxListFetcher(s.rpc)
txListDecoder = txlistfetcher.NewBlobTxListFetcher(s.rpc.L1Beacon)
} else {
txListDecoder = new(txlistfetcher.CalldataFetcher)
}
Expand Down
8 changes: 4 additions & 4 deletions driver/txlist_fetcher/blob.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ import (

// BlobFetcher is responsible for fetching the txList blob from the L1 block sidecar.
type BlobFetcher struct {
rpc *rpc.Client
l1Beacon *rpc.BeaconClient
}

// NewBlobTxListFetcher creates a new BlobFetcher instance based on the given rpc client.
func NewBlobTxListFetcher(rpc *rpc.Client) *BlobFetcher {
return &BlobFetcher{rpc}
func NewBlobTxListFetcher(l1Beacon *rpc.BeaconClient) *BlobFetcher {
return &BlobFetcher{l1Beacon}
}

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

// Fetch the L1 block sidecars.
sidecars, err := d.rpc.L1Beacon.GetBlobs(ctx, meta.Timestamp)
sidecars, err := d.l1Beacon.GetBlobs(ctx, meta.Timestamp)
if err != nil {
return nil, err
}
Expand Down
52 changes: 52 additions & 0 deletions internal/testutils/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ import (
"crypto/rand"
"errors"
"fmt"
"math/big"
"net/http"
"net/url"
"os"
"time"

"github.com/cenkalti/backoff/v4"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
Expand All @@ -21,6 +23,7 @@ import (
"github.com/phayes/freeport"

"github.com/taikoxyz/taiko-client/bindings"
"github.com/taikoxyz/taiko-client/pkg/rpc"
"github.com/taikoxyz/taiko-client/prover/server"
)

Expand Down Expand Up @@ -305,3 +308,52 @@ func LocalRandomProverEndpoint() *url.URL {
func SignatureFromRSV(r, s string, v byte) []byte {
return append(append(hexutil.MustDecode(r), hexutil.MustDecode(s)...), v)
}

// SendDynamicFeeTx sends a dynamic transaction, used for tests.
func SendDynamicFeeTx(
client *rpc.EthClient,
priv *ecdsa.PrivateKey,
to *common.Address,
value *big.Int,
data []byte,
) (*types.Transaction, error) {
head, err := client.HeaderByNumber(context.Background(), nil)
if err != nil {
return nil, err
}

auth, err := bind.NewKeyedTransactorWithChainID(priv, client.ChainID)
if err != nil {
return nil, err
}

nonce, err := client.PendingNonceAt(context.Background(), auth.From)
if err != nil {
return nil, err
}

gasTipCap, err := client.SuggestGasTipCap(context.Background())
if err != nil {
return nil, err
}

tx, err := auth.Signer(auth.From, types.NewTx(&types.DynamicFeeTx{
To: to,
Nonce: nonce,
Value: value,
GasTipCap: gasTipCap,
GasFeeCap: new(big.Int).Add(
gasTipCap,
new(big.Int).Mul(head.BaseFee, big.NewInt(2)),
),
Gas: 2100_000,
Data: data,
}))
if err != nil {
return nil, err
}
if err = client.SendTransaction(context.Background(), tx); err != nil {
return nil, err
}
return tx, nil
}
172 changes: 150 additions & 22 deletions proposer/proposer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,54 @@ package proposer

import (
"context"
"math/big"
"os"
"testing"
"time"

"github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/miner"
"github.com/ethereum/go-ethereum/rlp"
"github.com/stretchr/testify/suite"

"github.com/taikoxyz/taiko-client/bindings"
"github.com/taikoxyz/taiko-client/driver/chain_syncer/beaconsync"
"github.com/taikoxyz/taiko-client/driver/chain_syncer/calldata"
"github.com/taikoxyz/taiko-client/driver/state"
txlistfetcher "github.com/taikoxyz/taiko-client/driver/txlist_fetcher"
"github.com/taikoxyz/taiko-client/internal/testutils"
"github.com/taikoxyz/taiko-client/internal/utils"
"github.com/taikoxyz/taiko-client/pkg/jwt"
"github.com/taikoxyz/taiko-client/pkg/rpc"
)

type ProposerTestSuite struct {
testutils.ClientTestSuite
s *calldata.Syncer
p *Proposer
cancel context.CancelFunc
}

func (s *ProposerTestSuite) SetupTest() {
s.ClientTestSuite.SetupTest()

state2, err := state.New(context.Background(), s.RPCClient)
s.Nil(err)

syncer, err := calldata.NewSyncer(
context.Background(),
s.RPCClient,
state2,
beaconsync.NewSyncProgressTracker(s.RPCClient.L2, 1*time.Hour),
0,
)
s.Nil(err)
s.s = syncer

l1ProposerPrivKey, err := crypto.ToECDSA(common.FromHex(os.Getenv("L1_PROPOSER_PRIVATE_KEY")))
s.Nil(err)

Expand Down Expand Up @@ -82,6 +104,132 @@ func (s *ProposerTestSuite) SetupTest() {
s.cancel = cancel
}

func parseTxs(client *rpc.Client, event *bindings.TaikoL1ClientBlockProposed) (types.Transactions, error) {
tx, err := client.L1.TransactionInBlock(context.Background(), event.Raw.BlockHash, event.Raw.TxIndex)
if err != nil {
return nil, err
}

// Decode transactions list.
var txListDecoder txlistfetcher.TxListFetcher
if event.Meta.BlobUsed {
txListDecoder = txlistfetcher.NewBlobTxListFetcher(client.L1Beacon)
} else {
txListDecoder = new(txlistfetcher.CalldataFetcher)
}
txListBytes, err := txListDecoder.Fetch(context.Background(), tx, &event.Meta)
if err != nil {
return nil, err
}

txListBytes, err = utils.Decompress(txListBytes)
if err != nil {
return nil, err
}

var txs types.Transactions
return txs, rlp.DecodeBytes(txListBytes, &txs)
}

func (s *ProposerTestSuite) getLatestProposedTxs(
n int,
timeout time.Duration,
) (<-chan []types.Transactions, error) {
sink := make(chan *bindings.TaikoL1ClientBlockProposed)
sub, err := s.p.rpc.TaikoL1.WatchBlockProposed(nil, sink, nil, nil)
if err != nil {
return nil, err
}

var resCh = make(chan []types.Transactions, 1)
go func() {
defer sub.Unsubscribe()

txLst := make([]types.Transactions, 0, n)
tick := time.After(timeout)
for len(txLst) < cap(txLst) {
select {
case event := <-sink:
txs, err := parseTxs(s.RPCClient, event)
if err != nil {
log.Error("failed to parse txs", "err", err)
}
txLst = append(txLst, txs)
case <-tick:
return
}
}
resCh <- txLst
}()

return resCh, nil
}

func (s *ProposerTestSuite) TestProposeOpNoEmptyBlock() {
defer s.Nil(s.s.ProcessL1Blocks(context.Background()))

p := s.p

batchSize := 100

var err error
for i := 0; i < batchSize; i++ {
to := common.BytesToAddress(testutils.RandomBytes(32))
_, err = testutils.SendDynamicFeeTx(s.RPCClient.L2, s.TestAddrPrivKey, &to, nil, nil)
s.Nil(err)
}

var preBuiltTxList []*miner.PreBuiltTxList
for i := 0; i < 3 && len(preBuiltTxList) == 0; i++ {
preBuiltTxList, err = s.RPCClient.GetPoolContent(
context.Background(),
p.proposerAddress,
p.protocolConfigs.BlockMaxGasLimit,
rpc.BlockMaxTxListBytes,
p.LocalAddresses,
p.MaxProposedTxListsPerEpoch,
)
time.Sleep(time.Second)
}
s.Nil(err)
s.Equal(true, len(preBuiltTxList) > 0)

txsCh, err := s.getLatestProposedTxs(len(preBuiltTxList), time.Minute)
s.Nil(err)

var (
blockMinGasLimit uint64 = math.MaxUint64
blockMinTxListBytes uint64 = math.MaxUint64
txLists = make([]types.Transactions, 0, len(preBuiltTxList))
)
for _, txs := range preBuiltTxList {
if txs.EstimatedGasUsed <= blockMinGasLimit {
blockMinGasLimit = txs.EstimatedGasUsed
} else {
break
}
if txs.BytesLength <= blockMinTxListBytes {
blockMinTxListBytes = txs.BytesLength
} else {
break
}
txLists = append(txLists, txs.TxList)
}

// Start proposer
p.LocalAddressesOnly = false
p.MinGasUsed = blockMinGasLimit
p.MinTxListBytes = blockMinTxListBytes
p.ProposeInterval = time.Second
p.MinProposingInternal = time.Minute
s.Nil(p.ProposeOp(context.Background()))

txs := <-txsCh
for i := 0; i < len(txLists); i++ {
s.Equal(txLists[i].Len(), txs[i].Len())
}
}

func (s *ProposerTestSuite) TestName() {
s.Equal("proposer", s.p.Name())
}
Expand All @@ -97,29 +245,9 @@ func (s *ProposerTestSuite) TestProposeOp() {
close(sink)
}()

nonce, err := s.p.rpc.L2.PendingNonceAt(context.Background(), s.TestAddr)
s.Nil(err)

parent, err := s.p.rpc.L2.BlockByNumber(context.Background(), nil)
s.Nil(err)

baseFeeInfo, err := s.p.rpc.TaikoL2.GetBasefee(nil, 1, uint32(parent.GasUsed()))
s.Nil(err)

to := common.BytesToAddress(testutils.RandomBytes(32))
tx := types.NewTx(&types.DynamicFeeTx{
ChainID: s.RPCClient.L2.ChainID,
Nonce: nonce,
GasTipCap: common.Big0,
GasFeeCap: new(big.Int).SetUint64(baseFeeInfo.Basefee.Uint64() * 2),
Gas: 21000,
To: &to,
Value: common.Big1,
})

signedTx, err := types.SignTx(tx, types.LatestSignerForChainID(s.p.rpc.L2.ChainID), s.TestAddrPrivKey)
_, err = testutils.SendDynamicFeeTx(s.p.rpc.L2, s.TestAddrPrivKey, &to, common.Big1, nil)
s.Nil(err)
s.Nil(s.p.rpc.L2.SendTransaction(context.Background(), signedTx))

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

Expand Down

0 comments on commit cd26204

Please sign in to comment.