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

feat(proposer): add more tests for propsoer #686

Merged
merged 1 commit into from
Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading