Skip to content

Commit

Permalink
test(taiko-client): introduce MemoryBlobServer for tests (#18880)
Browse files Browse the repository at this point in the history
  • Loading branch information
davidtaikocha authored Feb 6, 2025
1 parent 760fb56 commit 7943979
Show file tree
Hide file tree
Showing 18 changed files with 230 additions and 22 deletions.
2 changes: 1 addition & 1 deletion packages/taiko-client/bindings/pacaya/.githead
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1713be26d6226695f393da883a8cf22d4152dcc9
4a9202fffac67f14678a34daac6b0e6050fe7f64
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func (s *BlobSyncerTestSuite) SetupTest() {
state2,
beaconsync.NewSyncProgressTracker(s.RPCClient.L2, 1*time.Hour),
0,
nil,
s.BlobServer.URL(),
nil,
)
s.Nil(err)
Expand Down Expand Up @@ -314,6 +314,7 @@ func (s *BlobSyncerTestSuite) initProposer() {
}, nil, nil))

s.p = prop
s.p.RegisterTxMgrSelctorToBlobServer(s.BlobServer)
}

func TestBlobSyncerTestSuite(t *testing.T) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func (s *ChainSyncerTestSuite) SetupTest() {
false,
1*time.Hour,
0,
nil,
s.BlobServer.URL(),
nil,
)
s.Nil(err)
Expand Down Expand Up @@ -98,6 +98,7 @@ func (s *ChainSyncerTestSuite) SetupTest() {
}, nil, nil))

s.p = prop
s.p.RegisterTxMgrSelctorToBlobServer(s.BlobServer)
}

func (s *ChainSyncerTestSuite) TestGetInnerSyncers() {
Expand Down
3 changes: 3 additions & 0 deletions packages/taiko-client/driver/driver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ func (s *DriverTestSuite) SetupTest() {
TaikoL2Address: common.HexToAddress(os.Getenv("TAIKO_ANCHOR")),
JwtSecret: string(jwtSecret),
},
BlobServerEndpoint: s.BlobServer.URL(),
}))
s.d = d
s.cancel = cancel
Expand Down Expand Up @@ -617,6 +618,7 @@ func (s *DriverTestSuite) InitProposer() {
L2SuggestedFeeRecipient: common.HexToAddress(os.Getenv("L2_SUGGESTED_FEE_RECIPIENT")),
ProposeInterval: 1024 * time.Hour,
MaxProposedTxListsPerEpoch: 1,
BlobAllowed: true,
TxmgrConfigs: &txmgr.CLIConfig{
L1RPCURL: os.Getenv("L1_WS"),
NumConfirmations: 0,
Expand Down Expand Up @@ -649,6 +651,7 @@ func (s *DriverTestSuite) InitProposer() {
},
}, nil, nil))
s.p = p
s.p.RegisterTxMgrSelctorToBlobServer(s.BlobServer)
}

func TestDriverTestSuite(t *testing.T) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ services:
container_name: l1_node
image: ghcr.io/foundry-rs/foundry:nightly
restart: unless-stopped
platform: linux/amd64
pull_policy: always
ports:
- "8545"
Expand All @@ -13,6 +12,8 @@ services:
- "32301"
- --host
- "0.0.0.0"
- --hardfork
- cancun

l2_geth:
container_name: l2_geth
Expand Down
1 change: 1 addition & 0 deletions packages/taiko-client/internal/testutils/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ type Proposer interface {
utils.SubcommandApplication
ProposeOp(ctx context.Context) error
ProposeTxLists(ctx context.Context, txLists []types.Transactions) error
RegisterTxMgrSelctorToBlobServer(blobServer *MemoryBlobServer)
}
172 changes: 172 additions & 0 deletions packages/taiko-client/internal/testutils/memory_blob_server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
package testutils

import (
"context"
"encoding/json"
"math/big"
"net/http"
"net/http/httptest"
"net/url"
"path"

"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
gethRPC "github.com/ethereum/go-ethereum/rpc"

"github.com/taikoxyz/taiko-mono/packages/taiko-client/pkg/rpc"
)

// MemoryBlobTxMgr is a mock tx manager that stores blobs in memory.
type MemoryBlobTxMgr struct {
rpc *rpc.Client
mgr txmgr.TxManager
server *MemoryBlobServer
}

// NewMemoryBlobTxMgr creates a new MemoryBlobTxMgr.
func NewMemoryBlobTxMgr(rpc *rpc.Client, mgr txmgr.TxManager, server *MemoryBlobServer) *MemoryBlobTxMgr {
return &MemoryBlobTxMgr{
rpc: rpc,
mgr: mgr,
server: server,
}
}

// Send sends a transaction to the tx manager.
func (m *MemoryBlobTxMgr) Send(ctx context.Context, candidate txmgr.TxCandidate) (*types.Receipt, error) {
receipt, err := m.mgr.Send(ctx, candidate)
if err != nil {
return nil, err
}

tx, _, err := m.rpc.L1.TransactionByHash(ctx, receipt.TxHash)
if err != nil {
return nil, err
}

if tx.Type() != types.BlobTxType {
return receipt, nil
}

return receipt, m.server.AddBlob(tx.BlobHashes(), candidate.Blobs)
}

// SendAsync implements TxManager interface.
func (m *MemoryBlobTxMgr) SendAsync(ctx context.Context, candidate txmgr.TxCandidate, ch chan txmgr.SendResponse) {
m.mgr.SendAsync(ctx, candidate, ch)
}

// From implements TxManager interface.
func (m *MemoryBlobTxMgr) From() common.Address {
return m.mgr.From()
}

// BlockNumber implements TxManager interface.
func (m *MemoryBlobTxMgr) BlockNumber(ctx context.Context) (uint64, error) {
return m.mgr.BlockNumber(ctx)
}

// API implements TxManager interface.
func (m *MemoryBlobTxMgr) API() gethRPC.API {
return m.mgr.API()
}

// Close implements TxManager interface.
func (m *MemoryBlobTxMgr) Close() {
m.mgr.Close()
}

// IsClosed implements TxManager interface.
func (m *MemoryBlobTxMgr) IsClosed() bool {
return m.mgr.IsClosed()
}

// SuggestGasPriceCaps implements TxManager interface.
func (m *MemoryBlobTxMgr) SuggestGasPriceCaps(
ctx context.Context,
) (tipCap *big.Int, baseFee *big.Int, blobBaseFee *big.Int, err error) {
return m.mgr.SuggestGasPriceCaps(ctx)
}

// BlobInfo contains the data and commitment of a blob.
type BlobInfo struct {
Data string
Commitment string
}

// MemoryBlobServer is a mock blob server that stores blobs in memory.
type MemoryBlobServer struct {
blobs map[common.Hash]*BlobInfo
server *httptest.Server
}

// NewMemoryBlobServer creates a new MemoryBlobServer.
func NewMemoryBlobServer() *MemoryBlobServer {
blobsMap := make(map[common.Hash]*BlobInfo)
return &MemoryBlobServer{
blobs: blobsMap,
server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
blobHash := path.Base(r.URL.Path)

blobInfo, ok := blobsMap[common.HexToHash(blobHash)]
if !ok {
log.Error("Blob not found", "hash", blobHash)
w.WriteHeader(http.StatusNotFound)
return
}

w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(&rpc.BlobServerResponse{
Commitment: blobInfo.Commitment,
Data: blobInfo.Data,
VersionedHash: blobHash,
}); err != nil {
log.Error("Failed to encode blob server response", "err", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
})),
}
}

// Close closes the server.
func (s *MemoryBlobServer) Close() {
s.server.Close()
}

// URL returns the URL of the server.
func (s *MemoryBlobServer) URL() *url.URL {
url, err := url.Parse(s.server.URL)
if err != nil {
log.Crit("Failed to parse server URL", "err", err)
}
return url
}

// AddBlob adds a blob to the server.
func (s *MemoryBlobServer) AddBlob(blobHashes []common.Hash, blobs []*eth.Blob) error {
for i, hash := range blobHashes {
commitment, err := blobs[i].ComputeKZGCommitment()
if err != nil {
return err
}

s.blobs[hash] = &BlobInfo{
Data: blobs[i].String(),
Commitment: common.Bytes2Hex(commitment[:]),
}

log.Info(
"New blob added to memory blob server",
"hash", hash,
"commitment", common.Bytes2Hex(commitment[:]),
"url", s.server.URL,
)
}

return nil
}
4 changes: 3 additions & 1 deletion packages/taiko-client/internal/testutils/suite.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ type ClientTestSuite struct {
TestAddrPrivKey *ecdsa.PrivateKey
TestAddr common.Address
AddressManager *ontakeBindings.AddressManager
BlobServer *MemoryBlobServer
}

func (s *ClientTestSuite) SetupTest() {
Expand Down Expand Up @@ -102,6 +103,7 @@ func (s *ClientTestSuite) SetupTest() {
}

s.testnetL1SnapshotID = s.SetL1Snapshot()
s.BlobServer = NewMemoryBlobServer()
}

func (s *ClientTestSuite) setAllowance(key *ecdsa.PrivateKey) {
Expand Down Expand Up @@ -150,8 +152,8 @@ func (s *ClientTestSuite) setAllowance(key *ecdsa.PrivateKey) {

func (s *ClientTestSuite) TearDownTest() {
s.RevertL1Snapshot(s.testnetL1SnapshotID)

s.Nil(rpc.SetHead(context.Background(), s.RPCClient.L2, common.Big0))
s.BlobServer.Close()
}

func (s *ClientTestSuite) SetL1Automine(automine bool) {
Expand Down
20 changes: 15 additions & 5 deletions packages/taiko-client/pkg/utils/txmgr_selector.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,16 @@ var (
// it will choose the transaction manager for a private mempool if it is available and works well,
// otherwise it will choose the normal transaction manager.
type TxMgrSelector struct {
txMgr *txmgr.SimpleTxManager
privateTxMgr *txmgr.SimpleTxManager
txMgr txmgr.TxManager
privateTxMgr txmgr.TxManager
privateTxMgrFailedAt *time.Time
privateTxMgrRetryInterval time.Duration
}

// NewTxMgrSelector creates a new TxMgrSelector instance.
func NewTxMgrSelector(
txMgr *txmgr.SimpleTxManager,
privateTxMgr *txmgr.SimpleTxManager,
txMgr txmgr.TxManager,
privateTxMgr txmgr.TxManager,
privateTxMgrRetryInterval *time.Duration,
) *TxMgrSelector {
retryInterval := defaultPrivateTxMgrRetryInterval
Expand All @@ -40,7 +40,7 @@ func NewTxMgrSelector(
}

// Select selects a transaction manager based on the current state.
func (s *TxMgrSelector) Select() (*txmgr.SimpleTxManager, bool) {
func (s *TxMgrSelector) Select() (txmgr.TxManager, bool) {
// If there is no private transaction manager, return the normal transaction manager.
if s.privateTxMgr == nil {
return s.txMgr, false
Expand All @@ -60,3 +60,13 @@ func (s *TxMgrSelector) RecordPrivateTxMgrFailed() {
now := time.Now()
s.privateTxMgrFailedAt = &now
}

// TxMgrmethod returns the inner transaction manager.
func (s *TxMgrSelector) TxMgr() txmgr.TxManager {
return s.txMgr
}

// PrivateTxMgr returns the private transaction manager.
func (s *TxMgrSelector) PrivateTxMgr() txmgr.TxManager {
return s.privateTxMgr
}
11 changes: 11 additions & 0 deletions packages/taiko-client/proposer/proposer.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (

"github.com/taikoxyz/taiko-mono/packages/taiko-client/bindings/encoding"
"github.com/taikoxyz/taiko-mono/packages/taiko-client/internal/metrics"
"github.com/taikoxyz/taiko-mono/packages/taiko-client/internal/testutils"
"github.com/taikoxyz/taiko-mono/packages/taiko-client/pkg/config"
"github.com/taikoxyz/taiko-mono/packages/taiko-client/pkg/rpc"
"github.com/taikoxyz/taiko-mono/packages/taiko-client/pkg/utils"
Expand Down Expand Up @@ -506,3 +507,13 @@ func (p *Proposer) SendTx(ctx context.Context, txCandidate *txmgr.TxCandidate) e
func (p *Proposer) Name() string {
return "proposer"
}

// RegisterTxMgrSelctorToBlobServer registers the tx manager selector to the given blob server,
// should only be used for testing.
func (p *Proposer) RegisterTxMgrSelctorToBlobServer(blobServer *testutils.MemoryBlobServer) {
p.txmgrSelector = utils.NewTxMgrSelector(
testutils.NewMemoryBlobTxMgr(p.rpc, p.txmgrSelector.TxMgr(), blobServer),
testutils.NewMemoryBlobTxMgr(p.rpc, p.txmgrSelector.PrivateTxMgr(), blobServer),
nil,
)
}
5 changes: 4 additions & 1 deletion packages/taiko-client/proposer/proposer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func (s *ProposerTestSuite) SetupTest() {
state2,
beaconsync.NewSyncProgressTracker(s.RPCClient.L2, 1*time.Hour),
0,
nil,
s.BlobServer.URL(),
nil,
)
s.Nil(err)
Expand Down Expand Up @@ -80,6 +80,7 @@ func (s *ProposerTestSuite) SetupTest() {
ProposeInterval: 1024 * time.Hour,
MaxProposedTxListsPerEpoch: 1,
ProposeBlockTxGasLimit: 10_000_000,
BlobAllowed: true,
FallbackToCalldata: true,
TxmgrConfigs: &txmgr.CLIConfig{
L1RPCURL: os.Getenv("L1_WS"),
Expand Down Expand Up @@ -114,6 +115,7 @@ func (s *ProposerTestSuite) SetupTest() {
}, nil, nil))

s.p = p
s.p.RegisterTxMgrSelctorToBlobServer(s.BlobServer)
s.cancel = cancel
}

Expand Down Expand Up @@ -347,6 +349,7 @@ func (s *ProposerTestSuite) TestProposeEmptyBlockOp() {
s.p.lastProposedAt = time.Now().Add(-10 * time.Second)
s.Nil(s.p.ProposeOp(context.Background()))
}

func (s *ProposerTestSuite) TestUpdateProposingTicker() {
s.p.ProposeInterval = 1 * time.Hour
s.NotPanics(s.p.updateProposingTicker)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ type ProofContesterOntake struct {
func NewProofContester(
rpcClient *rpc.Client,
gasLimit uint64,
txmgr *txmgr.SimpleTxManager,
privateTxmgr *txmgr.SimpleTxManager,
txmgr txmgr.TxManager,
privateTxmgr txmgr.TxManager,
proverSetAddress common.Address,
graffiti string,
builder *transaction.ProveBlockTxBuilder,
Expand Down
Loading

0 comments on commit 7943979

Please sign in to comment.