From d85d4d7707704e9dd4ef55172a8e3e43befe9efe Mon Sep 17 00:00:00 2001 From: Alok Date: Mon, 7 Jul 2025 19:22:17 +0530 Subject: [PATCH 1/7] feat: new bidder emulator --- .../docker/Dockerfile.bidderemulator | 5 +- infrastructure/docker/Dockerfile.builder | 4 +- infrastructure/docker/Dockerfile.l1transactor | 6 - infrastructure/docker/docker-bake.hcl | 11 +- tools/bidder-emulator/bidder.go | 98 +++++++ tools/bidder-emulator/main.go | 263 ++++++++++++++++++ tools/bidder-emulator/transactor_account.go | 88 ++++++ 7 files changed, 453 insertions(+), 22 deletions(-) delete mode 100644 infrastructure/docker/Dockerfile.l1transactor create mode 100644 tools/bidder-emulator/bidder.go create mode 100644 tools/bidder-emulator/main.go create mode 100644 tools/bidder-emulator/transactor_account.go diff --git a/infrastructure/docker/Dockerfile.bidderemulator b/infrastructure/docker/Dockerfile.bidderemulator index 1a8d698f9..f7aca0ef8 100644 --- a/infrastructure/docker/Dockerfile.bidderemulator +++ b/infrastructure/docker/Dockerfile.bidderemulator @@ -1,9 +1,8 @@ # syntax=docker/dockerfile:1.4 FROM alpine:3.10 -COPY --from=builder_ctx /go/bin/real-bidder /usr/local/bin/bidder-emulator -COPY --from=builder_ctx /scripts/bidder-emulator-entrypoint.sh entrypoint.sh +COPY --from=builder_ctx /go/bin/bidder-emulator /usr/local/bin/bidder-emulator EXPOSE 8080 -ENTRYPOINT ["./entrypoint.sh"] +ENTRYPOINT ["bidder-emulator"] diff --git a/infrastructure/docker/Dockerfile.builder b/infrastructure/docker/Dockerfile.builder index d76f6c8a8..59506dd04 100644 --- a/infrastructure/docker/Dockerfile.builder +++ b/infrastructure/docker/Dockerfile.builder @@ -14,7 +14,6 @@ COPY bridge/standard/go.mod bridge/standard/go.sum ./bridge/standard/ COPY cl/go.mod cl/go.sum ./cl/ COPY infrastructure/tools/keystore-generator/go.mod infrastructure/tools/keystore-generator/go.sum ./infrastructure/tools/keystore-generator/ -COPY p2p/integrationtest/real-bidder/entrypoint.sh /scripts/bidder-emulator-entrypoint.sh COPY p2p/integrationtest/provider/entrypoint.sh /scripts/provider-emulator-entrypoint.sh RUN --mount=type=cache,target=/root/.cache/go-build \ @@ -35,11 +34,10 @@ ARG TARGETS="./oracle/cmd \ ./tools/dashboard \ ./tools/bidder-cli \ ./tools/bls-signer \ - ./tools/l1-transaction-emulator \ + ./tools/bidder-emulator \ ./tools/relay-emulator \ ./tools/validators-monitor \ ./tools/points-service \ - ./p2p/integrationtest/real-bidder \ ./p2p/integrationtest/provider" RUN --mount=type=cache,target=/root/.cache/go-build \ diff --git a/infrastructure/docker/Dockerfile.l1transactor b/infrastructure/docker/Dockerfile.l1transactor deleted file mode 100644 index 664e6c930..000000000 --- a/infrastructure/docker/Dockerfile.l1transactor +++ /dev/null @@ -1,6 +0,0 @@ -# syntax=docker/dockerfile:1.4 -FROM alpine:3.10 - -COPY --from=builder_ctx /go/bin/l1-transaction-emulator /usr/local/bin/l1-transactor - -ENTRYPOINT ["l1-transactor"] diff --git a/infrastructure/docker/docker-bake.hcl b/infrastructure/docker/docker-bake.hcl index e8e87d93a..80b06ec8d 100644 --- a/infrastructure/docker/docker-bake.hcl +++ b/infrastructure/docker/docker-bake.hcl @@ -77,16 +77,7 @@ target "relay-emulator" { tags = ["ghcr.io/primev/relay-emulator:${TAG}"] } -target "l1-transactor" { - context = "./" - dockerfile = "Dockerfile.l1transactor" - contexts = { - builder_ctx = "target:mev-commit-builder" - } - tags = ["ghcr.io/primev/l1-transactor:${TAG}"] -} - group "default" { - targets = ["mev-commit-builder", "mev-commit-oracle", "mev-commit", "mev-commit-bridge", "mev-commit-dashboard", "preconf-rpc", "bidder-emulator", "provider-emulator", "relay-emulator", "l1-transactor"] + targets = ["mev-commit-builder", "mev-commit-oracle", "mev-commit", "mev-commit-bridge", "mev-commit-dashboard", "preconf-rpc", "bidder-emulator", "provider-emulator", "relay-emulator"] } diff --git a/tools/bidder-emulator/bidder.go b/tools/bidder-emulator/bidder.go new file mode 100644 index 000000000..12e61f885 --- /dev/null +++ b/tools/bidder-emulator/bidder.go @@ -0,0 +1,98 @@ +package main + +import ( + "context" + "encoding/hex" + "errors" + "fmt" + "io" + "math/big" + + "github.com/ethereum/go-ethereum/core/types" + pb "github.com/primev/mev-commit/p2p/gen/go/bidderapi/v1" + "golang.org/x/exp/rand" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +type bidder struct { + client pb.BidderClient +} + +type result struct { + txn *types.Transaction + bid *pb.Bid + preconfs []*pb.Commitment +} + +func newBidder(rpcURL string, depositAmount string) (*bidder, error) { + creds := insecure.NewCredentials() + conn, err := grpc.NewClient(rpcURL, grpc.WithTransportCredentials(creds)) + if err != nil { + return nil, err + } + + client := pb.NewBidderClient(conn) + b := &bidder{ + client: client, + } + + return b, b.setup(depositAmount) +} + +func (b *bidder) setup(depositAmount string) error { + status, err := b.client.AutoDepositStatus(context.Background(), &pb.EmptyMessage{}) + if err != nil { + return fmt.Errorf("failed to get auto deposit status: %w", err) + } + + if !status.IsAutodepositEnabled { + _, err := b.client.AutoDeposit(context.Background(), &pb.DepositRequest{ + Amount: depositAmount, + }) + if err != nil { + return fmt.Errorf("failed to auto deposit: %w", err) + } + } + return nil +} + +func (b *bidder) SendBid(ctx context.Context, txn *types.Transaction, blockNumber int64) (*result, error) { + txBytes, err := txn.MarshalBinary() + if err != nil { + return nil, fmt.Errorf("failed to marshal transaction: %w", err) + } + + // Choose a random bid amount between 1 and 2 gwei + n := rand.Intn(1_000_000_000) + bidAmount := new(big.Int).Add(big.NewInt(int64(n)), big.NewInt(1_000_000_000)) + + req := &pb.Bid{ + RawTransactions: []string{hex.EncodeToString(txBytes)}, + BlockNumber: blockNumber, + Amount: bidAmount.String(), + } + + resp, err := b.client.SendBid(ctx, req) + if err != nil { + return nil, fmt.Errorf("failed to send bid: %w", err) + } + + res := &result{ + txn: txn, + bid: req, + } + + for { + preconf, err := resp.Recv() + if err != nil { + if errors.Is(err, io.EOF) { + break + } + return nil, fmt.Errorf("failed to receive preconf: %w", err) + } + res.preconfs = append(res.preconfs, preconf) + } + + return res, nil +} diff --git a/tools/bidder-emulator/main.go b/tools/bidder-emulator/main.go new file mode 100644 index 000000000..a85200a5e --- /dev/null +++ b/tools/bidder-emulator/main.go @@ -0,0 +1,263 @@ +package main + +import ( + "context" + "fmt" + "log/slog" + "math/big" + "math/rand/v2" + "os" + "os/signal" + "path" + "slices" + "strings" + "syscall" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/primev/mev-commit/x/util" + "github.com/urfave/cli/v2" + "golang.org/x/sync/errgroup" +) + +var ( + optionKeystorePathPassword = &cli.StringSliceFlag{ + Name: "keystore-path-password", + Usage: "Path to the keystore file and password in the format path:password", + EnvVars: []string{"TRANSACTOR_KEYSTORE_PATH_PASSWORD"}, + Action: func(c *cli.Context, keystores []string) error { + for _, kp := range keystores { + parts := strings.Split(kp, ":") + if len(parts) != 2 { + return fmt.Errorf("invalid keystore-path-password format: %s", kp) + } + } + return nil + }, + } + + optionL1RPCURL = &cli.StringFlag{ + Name: "l1-rpc-url", + Usage: "URL of the L1 RPC server", + EnvVars: []string{"TRANSACTOR_L1_RPC_URL"}, + Required: true, + } + + optionBidderRPCURL = &cli.StringFlag{ + Name: "bidder-rpc-url", + Usage: "URL of the bidder RPC server", + EnvVars: []string{"TRANSACTOR_BIDDER_RPC_URL"}, + Required: true, + } + + optionDepositAmount = &cli.StringFlag{ + Name: "deposit-amount", + Usage: "Amount to deposit in wei", + EnvVars: []string{"TRANSACTOR_DEPOSIT_AMOUNT"}, + Value: "1000000000000000000", // Default to 1 ETH in wei + Action: func(ctx *cli.Context, s string) error { + if _, ok := new(big.Int).SetString(s, 10); !ok { + return fmt.Errorf("invalid deposit amount: %s", s) + } + return nil + }, + } + + optionBidWorkers = &cli.IntFlag{ + Name: "bid-workers", + Usage: "Number of bid workers to run concurrently", + EnvVars: []string{"TRANSACTOR_BID_WORKERS"}, + Value: 1, + Action: func(ctx *cli.Context, i int) error { + if i < 1 { + return fmt.Errorf("invalid bid-workers, must be at least 1") + } + return nil + }, + } + + optionLogFmt = &cli.StringFlag{ + Name: "log-fmt", + Usage: "log format to use, options are 'text' or 'json'", + EnvVars: []string{"TRANSACTOR_LOG_FMT"}, + Value: "text", + Action: func(ctx *cli.Context, s string) error { + if !slices.Contains([]string{"text", "json"}, s) { + return fmt.Errorf("invalid log-fmt, expecting 'text' or 'json'") + } + return nil + }, + } + + optionLogLevel = &cli.StringFlag{ + Name: "log-level", + Usage: "log level to use, options are 'debug', 'info', 'warn', 'error'", + EnvVars: []string{"TRANSACTOR_LOG_LEVEL"}, + Value: "info", + Action: func(ctx *cli.Context, s string) error { + if !slices.Contains([]string{"debug", "info", "warn", "error"}, s) { + return fmt.Errorf("invalid log-level, expecting 'debug', 'info', 'warn', 'error'") + } + return nil + }, + } + + optionLogTags = &cli.StringFlag{ + Name: "log-tags", + Usage: "log tags is a comma-separated list of pairs that will be inserted into each log line", + EnvVars: []string{"TRANSACTOR_LOG_TAGS"}, + Action: func(ctx *cli.Context, s string) error { + for i, p := range strings.Split(s, ",") { + if len(strings.Split(p, ":")) != 2 { + return fmt.Errorf("invalid log-tags at index %d, expecting ", i) + } + } + return nil + }, + } +) + +func main() { + app := &cli.App{ + Name: "bidder-emulator", + Usage: "Issue random transactions for L1 chain from specified accounts and bid on them on mev-commit chain", + Flags: []cli.Flag{ + optionKeystorePathPassword, + optionL1RPCURL, + optionBidderRPCURL, + optionDepositAmount, + optionLogFmt, + optionLogLevel, + optionLogTags, + }, + Action: func(c *cli.Context) error { + l1RPC, err := ethclient.Dial(c.String(optionL1RPCURL.Name)) + if err != nil { + return err + } + + logger, err := util.NewLogger( + c.String(optionLogLevel.Name), + c.String(optionLogFmt.Name), + c.String(optionLogTags.Name), + c.App.Writer, + ) + if err != nil { + return fmt.Errorf("failed to create logger: %w", err) + } + + chainID, err := l1RPC.ChainID(context.Background()) + if err != nil { + return fmt.Errorf("failed getting chain ID: %w", err) + } + + txtors := make([]*transactorAccount, 0) + for _, kp := range c.StringSlice(optionKeystorePathPassword.Name) { + parts := strings.Split(kp, ":") + t, err := newTransactorAccount(chainID, path.Dir(parts[0]), parts[1], l1RPC) + if err != nil { + return err + } + txtors = append(txtors, t) + } + + bidderClient, err := newBidder(c.String(optionBidderRPCURL.Name), c.String(optionDepositAmount.Name)) + if err != nil { + return fmt.Errorf("failed to create bidder client: %w", err) + } + + ctx, cancel := signal.NotifyContext(c.Context, syscall.SIGINT, syscall.SIGTERM) + defer cancel() + + eg, egCtx := errgroup.WithContext(ctx) + for i := 0; i < c.Int(optionBidWorkers.Name); i++ { + eg.Go(func() error { + return bidWorker(egCtx, logger, txtors, bidderClient, l1RPC) + }) + } + + return eg.Wait() + }, + } + + if err := app.Run(os.Args); err != nil { + fmt.Fprintf(os.Stderr, "error: %v\n", err) + os.Exit(1) + } +} + +func bidWorker( + ctx context.Context, + logger *slog.Logger, + txtors []*transactorAccount, + bidderClient *bidder, + l1RPC *ethclient.Client, +) error { + if len(txtors) < 2 { + return fmt.Errorf("at least 2 transactor accounts are required") + } + + for { + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + + start := time.Now() + + rand.Shuffle(len(txtors), func(i, j int) { + txtors[i], txtors[j] = txtors[j], txtors[i] + }) + + from := txtors[0] + to := txtors[1] + + // random amount between 0 and 1_000_000_000_000 + amount := big.NewInt(rand.Int64N(1_000_000_000_000) + 1) + + txn, err := from.SendTransaction(ctx, to.Address(), amount) + if err != nil { + logger.Error("failed to send transaction", "error", err) + continue + } + + signingDuration := time.Since(start) + + logger.Info("transaction sent", "from", from.Address(), "to", to.Address(), "amount", amount) + rcpt, err := bind.WaitMined(ctx, l1RPC, txn) + if err != nil { + logger.Error("failed to wait for transaction receipt", "error", err) + continue + } + + waitDuration := time.Since(start) - signingDuration + + if rcpt.Status != types.ReceiptStatusSuccessful { + logger.Error("transaction failed", "hash", txn.Hash().Hex(), "status", rcpt.Status) + continue + } + logger.Info("transaction mined", "hash", txn.Hash().Hex(), "block", rcpt.BlockNumber) + + res, err := bidderClient.SendBid(ctx, txn, rcpt.BlockNumber.Int64()) + if err != nil { + logger.Error("failed to send bid", "error", err) + continue + } + + preconfDuration := time.Since(start) - signingDuration - waitDuration + + logger.Info( + "bid sent", + "hash", txn.Hash().Hex(), + "bidAmount", res.bid.Amount, + "blockNumber", res.bid.BlockNumber, + "noOfPreconfs", len(res.preconfs), + "signingDuration", signingDuration, + "waitDuration", waitDuration, + "preconfDuration", preconfDuration, + ) + } +} diff --git a/tools/bidder-emulator/transactor_account.go b/tools/bidder-emulator/transactor_account.go new file mode 100644 index 000000000..a562db97d --- /dev/null +++ b/tools/bidder-emulator/transactor_account.go @@ -0,0 +1,88 @@ +package main + +import ( + "context" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/primev/mev-commit/x/keysigner" +) + +const basefeeWiggleMultiplier = 2 + +type transactorAccount struct { + keySigner keysigner.KeySigner + chainID *big.Int + ethClient *ethclient.Client +} + +func newTransactorAccount(chainID *big.Int, keystorePath, password string, l1RPCClient *ethclient.Client) (*transactorAccount, error) { + keySigner, err := keysigner.NewKeystoreSigner(keystorePath, password) + if err != nil { + return nil, fmt.Errorf("failed creating key signer: %w", err) + } + + return &transactorAccount{ + keySigner: keySigner, + chainID: chainID, + ethClient: l1RPCClient, + }, nil +} + +func (t *transactorAccount) Address() common.Address { + return t.keySigner.GetAddress() +} + +func (t *transactorAccount) SendTransaction( + ctx context.Context, + recipient common.Address, + amount *big.Int, +) (*types.Transaction, error) { + nonce, err := t.ethClient.PendingNonceAt(ctx, t.keySigner.GetAddress()) + if err != nil { + return nil, fmt.Errorf("failed getting account nonce: %w", err) + } + + gasLimit := uint64(21000) // basic gas limit for Ether transfer + + head, err := t.ethClient.HeaderByNumber(ctx, nil) + if err != nil { + return nil, fmt.Errorf("failed getting the latest block: %w", err) + } + + tip, err := t.ethClient.SuggestGasTipCap(ctx) + if err != nil { + return nil, fmt.Errorf("failed getting gas tip cap: %w", err) + } + + feeCap := new(big.Int).Add( + tip, + new(big.Int).Mul(head.BaseFee, big.NewInt(basefeeWiggleMultiplier)), + ) + + txData := &types.DynamicFeeTx{ + To: &recipient, + Nonce: nonce, + GasFeeCap: feeCap, + GasTipCap: tip, + Gas: gasLimit, + Value: amount, + } + + tx := types.NewTx(txData) + + signedTx, err := t.keySigner.SignTx(tx, t.chainID) + if err != nil { + return nil, fmt.Errorf("failed to sign transaction payload: %w", err) + } + + err = t.ethClient.SendTransaction(ctx, signedTx) + if err != nil { + return nil, fmt.Errorf("failed to send transaction: %w", err) + } + + return signedTx, nil +} From 6e1558d65fe896aeda2de870766ef19bd1844358 Mon Sep 17 00:00:00 2001 From: Alok Date: Mon, 7 Jul 2025 19:36:49 +0530 Subject: [PATCH 2/7] feat: new bidder emulator --- tools/go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/go.mod b/tools/go.mod index 2b36901fd..28f5a0fe2 100644 --- a/tools/go.mod +++ b/tools/go.mod @@ -25,6 +25,7 @@ require ( github.com/DATA-DOG/go-sqlmock v1.5.2 github.com/google/go-cmp v0.6.0 github.com/testcontainers/testcontainers-go v0.27.0 + golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 ) require ( @@ -66,7 +67,6 @@ require ( go.opentelemetry.io/otel v1.28.0 // indirect go.opentelemetry.io/otel/metric v1.28.0 // indirect go.opentelemetry.io/otel/trace v1.28.0 // indirect - golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect ) replace github.com/primev/mev-commit/p2p => ../p2p From da9953748464c80cc292e567032b1f415c9c09a2 Mon Sep 17 00:00:00 2001 From: Alok Date: Mon, 7 Jul 2025 19:39:46 +0530 Subject: [PATCH 3/7] feat: new bidder emulator --- tools/bidder-emulator/bidder.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tools/bidder-emulator/bidder.go b/tools/bidder-emulator/bidder.go index 12e61f885..6222c075a 100644 --- a/tools/bidder-emulator/bidder.go +++ b/tools/bidder-emulator/bidder.go @@ -7,6 +7,7 @@ import ( "fmt" "io" "math/big" + "time" "github.com/ethereum/go-ethereum/core/types" pb "github.com/primev/mev-commit/p2p/gen/go/bidderapi/v1" @@ -68,9 +69,11 @@ func (b *bidder) SendBid(ctx context.Context, txn *types.Transaction, blockNumbe bidAmount := new(big.Int).Add(big.NewInt(int64(n)), big.NewInt(1_000_000_000)) req := &pb.Bid{ - RawTransactions: []string{hex.EncodeToString(txBytes)}, - BlockNumber: blockNumber, - Amount: bidAmount.String(), + RawTransactions: []string{hex.EncodeToString(txBytes)}, + BlockNumber: blockNumber, + Amount: bidAmount.String(), + DecayStartTimestamp: time.Now().Add(100 * time.Millisecond).UnixMilli(), + DecayEndTimestamp: time.Now().Add(12 * time.Second).UnixMilli(), } resp, err := b.client.SendBid(ctx, req) From 648e77c5de3c5dc39a6433dfa68b3584f1ea3e5d Mon Sep 17 00:00:00 2001 From: Alok Nerurkar Date: Tue, 8 Jul 2025 20:42:56 +0530 Subject: [PATCH 4/7] fix: flag --- tools/bidder-emulator/main.go | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/bidder-emulator/main.go b/tools/bidder-emulator/main.go index a85200a5e..770c81a71 100644 --- a/tools/bidder-emulator/main.go +++ b/tools/bidder-emulator/main.go @@ -128,6 +128,7 @@ func main() { optionL1RPCURL, optionBidderRPCURL, optionDepositAmount, + optionBidWorkers, optionLogFmt, optionLogLevel, optionLogTags, From be44f3997ba20625728d9f9d787ea29380107c05 Mon Sep 17 00:00:00 2001 From: Alok Nerurkar Date: Tue, 8 Jul 2025 21:02:53 +0530 Subject: [PATCH 5/7] fix: grpc dial --- tools/bidder-emulator/bidder.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tools/bidder-emulator/bidder.go b/tools/bidder-emulator/bidder.go index 6222c075a..e3044a924 100644 --- a/tools/bidder-emulator/bidder.go +++ b/tools/bidder-emulator/bidder.go @@ -2,6 +2,7 @@ package main import ( "context" + "crypto/tls" "encoding/hex" "errors" "fmt" @@ -13,7 +14,7 @@ import ( pb "github.com/primev/mev-commit/p2p/gen/go/bidderapi/v1" "golang.org/x/exp/rand" "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" + "google.golang.org/grpc/credentials" ) type bidder struct { @@ -27,8 +28,12 @@ type result struct { } func newBidder(rpcURL string, depositAmount string) (*bidder, error) { - creds := insecure.NewCredentials() - conn, err := grpc.NewClient(rpcURL, grpc.WithTransportCredentials(creds)) + conn, err := grpc.NewClient( + rpcURL, + grpc.WithTransportCredentials(credentials.NewTLS( + &tls.Config{InsecureSkipVerify: true}, + )), + ) if err != nil { return nil, err } From 3ad6df23e1cf05a33784dcb1149e47c2a4bf1edf Mon Sep 17 00:00:00 2001 From: Alok Nerurkar Date: Tue, 8 Jul 2025 21:26:54 +0530 Subject: [PATCH 6/7] fix: sync sending --- tools/bidder-emulator/main.go | 6 ++++++ tools/go.mod | 1 + tools/go.sum | 2 ++ 3 files changed, 9 insertions(+) diff --git a/tools/bidder-emulator/main.go b/tools/bidder-emulator/main.go index 770c81a71..8a2cfba4a 100644 --- a/tools/bidder-emulator/main.go +++ b/tools/bidder-emulator/main.go @@ -20,6 +20,7 @@ import ( "github.com/primev/mev-commit/x/util" "github.com/urfave/cli/v2" "golang.org/x/sync/errgroup" + "resenje.org/multex" ) var ( @@ -200,6 +201,8 @@ func bidWorker( return fmt.Errorf("at least 2 transactor accounts are required") } + mtx := multex.New[string]() + for { select { case <-ctx.Done(): @@ -219,11 +222,14 @@ func bidWorker( // random amount between 0 and 1_000_000_000_000 amount := big.NewInt(rand.Int64N(1_000_000_000_000) + 1) + mtx.Lock(from.Address().Hex()) txn, err := from.SendTransaction(ctx, to.Address(), amount) if err != nil { + mtx.Unlock(from.Address().Hex()) logger.Error("failed to send transaction", "error", err) continue } + mtx.Unlock(from.Address().Hex()) signingDuration := time.Since(start) diff --git a/tools/go.mod b/tools/go.mod index 28f5a0fe2..3105e5f90 100644 --- a/tools/go.mod +++ b/tools/go.mod @@ -26,6 +26,7 @@ require ( github.com/google/go-cmp v0.6.0 github.com/testcontainers/testcontainers-go v0.27.0 golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 + resenje.org/multex v0.2.0 ) require ( diff --git a/tools/go.sum b/tools/go.sum index 4730aca99..e5d5a2775 100644 --- a/tools/go.sum +++ b/tools/go.sum @@ -382,5 +382,7 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY= gotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= +resenje.org/multex v0.2.0 h1:y1S8+bItGZo0lberxtQi9IhbWTpvRezhCWIFvt12VmU= +resenje.org/multex v0.2.0/go.mod h1:z+E+cUHGTgpqYn+P3yFOnC92i3X7rStzSur4rjOZM9s= rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= From d7778b3dbd245e57fb89180c90d2ab6e337448da Mon Sep 17 00:00:00 2001 From: Alok Date: Wed, 9 Jul 2025 12:41:35 +0530 Subject: [PATCH 7/7] fix: common multex --- tools/bidder-emulator/main.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tools/bidder-emulator/main.go b/tools/bidder-emulator/main.go index 8a2cfba4a..b6d53a631 100644 --- a/tools/bidder-emulator/main.go +++ b/tools/bidder-emulator/main.go @@ -173,10 +173,12 @@ func main() { ctx, cancel := signal.NotifyContext(c.Context, syscall.SIGINT, syscall.SIGTERM) defer cancel() + mtx := multex.New[string]() + eg, egCtx := errgroup.WithContext(ctx) for i := 0; i < c.Int(optionBidWorkers.Name); i++ { eg.Go(func() error { - return bidWorker(egCtx, logger, txtors, bidderClient, l1RPC) + return bidWorker(egCtx, logger, txtors, bidderClient, l1RPC, mtx) }) } @@ -196,13 +198,12 @@ func bidWorker( txtors []*transactorAccount, bidderClient *bidder, l1RPC *ethclient.Client, + mtx *multex.Multex[string], ) error { if len(txtors) < 2 { return fmt.Errorf("at least 2 transactor accounts are required") } - mtx := multex.New[string]() - for { select { case <-ctx.Done():