From b713278a13f7bb991acd8ba4b28e40dcf2d1f071 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 24 Apr 2025 18:57:09 +0200 Subject: [PATCH 001/197] initial commit --- tests/load/agent/agent.go | 25 +++++ tests/load/agent/dependencies.go | 48 ++++++++++ tests/load/dependencies.go | 49 ++++++++++ tests/load/execute.go | 127 +++++++++++++++++++++++++ tests/load/funder.go | 123 ++++++++++++++++++++++++ tests/load/generator/distribute.go | 107 +++++++++++++++++++++ tests/load/generator/self.go | 80 ++++++++++++++++ tests/load/issuer/issuer.go | 118 +++++++++++++++++++++++ tests/load/issuer/newhead.go | 113 ++++++++++++++++++++++ tests/load/key.go | 49 ++++++++++ tests/load/orchestrate/burst.go | 85 +++++++++++++++++ tests/load/orchestrate/dependencies.go | 49 ++++++++++ tests/load/tracker/counter.go | 26 +++++ tests/load/tracker/metrics.go | 95 ++++++++++++++++++ tests/load/tracker/metrics_server.go | 78 +++++++++++++++ tests/load/tracker/tracker.go | 95 ++++++++++++++++++ 16 files changed, 1267 insertions(+) create mode 100644 tests/load/agent/agent.go create mode 100644 tests/load/agent/dependencies.go create mode 100644 tests/load/dependencies.go create mode 100644 tests/load/execute.go create mode 100644 tests/load/funder.go create mode 100644 tests/load/generator/distribute.go create mode 100644 tests/load/generator/self.go create mode 100644 tests/load/issuer/issuer.go create mode 100644 tests/load/issuer/newhead.go create mode 100644 tests/load/key.go create mode 100644 tests/load/orchestrate/burst.go create mode 100644 tests/load/orchestrate/dependencies.go create mode 100644 tests/load/tracker/counter.go create mode 100644 tests/load/tracker/metrics.go create mode 100644 tests/load/tracker/metrics_server.go create mode 100644 tests/load/tracker/tracker.go diff --git a/tests/load/agent/agent.go b/tests/load/agent/agent.go new file mode 100644 index 000000000000..7be4cbcfa09d --- /dev/null +++ b/tests/load/agent/agent.go @@ -0,0 +1,25 @@ +// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package agent + +type Agent[T, U comparable] struct { + TxTarget uint64 + Generator TxGenerator[T] + Issuer Issuer[T] + Tracker Tracker[U] +} + +func New[T, U comparable]( + txTarget uint64, + generator TxGenerator[T], + issuer Issuer[T], + tracker Tracker[U], +) *Agent[T, U] { + return &Agent[T, U]{ + TxTarget: txTarget, + Generator: generator, + Issuer: issuer, + Tracker: tracker, + } +} diff --git a/tests/load/agent/dependencies.go b/tests/load/agent/dependencies.go new file mode 100644 index 000000000000..63a0e1d8190e --- /dev/null +++ b/tests/load/agent/dependencies.go @@ -0,0 +1,48 @@ +// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package agent + +import "context" + +type TxGenerator[T comparable] interface { + // GenerateTx returns a valid transaction. + GenerateTx() (T, error) +} + +type Issuer[T comparable] interface { + // Listen for the final status of transactions and notify the tracker + // Listen stops if the context is done, an error occurs, or if the issuer + // has sent all their transactions. + // Listen MUST return a nil error if the context is canceled. + Listen(ctx context.Context) error + + // Stop notifies the issuer that no further transactions will be issued. + // If a transaction is issued after Stop has been called, the issuer should error. + Stop() + + // Issue sends a tx to the network, and informs the tracker that its sent + // said transaction. + IssueTx(ctx context.Context, tx T) error +} + +// Tracker keeps track of the status of transactions. +// This must be thread-safe, so it can be called in parallel by the issuer(s) or orchestrator. +type Tracker[T comparable] interface { + // IssueStart records a transaction that is being issued. + IssueStart(T) + // IssueEnd records a transaction that was issued, but whose final status is + // not yet known. + IssueEnd(T) + // ObserveConfirmed records a transaction that was confirmed. + ObserveConfirmed(T) + // ObserveFailed records a transaction that failed (e.g. expired) + ObserveFailed(T) + + // GetObservedConfirmed returns the number of transactions that the tracker has + // confirmed were accepted. + GetObservedConfirmed() uint64 + // GetObservedFailed returns the number of transactions that the tracker has + // confirmed failed. + GetObservedFailed() uint64 +} diff --git a/tests/load/dependencies.go b/tests/load/dependencies.go new file mode 100644 index 000000000000..ce5f2bd01abc --- /dev/null +++ b/tests/load/dependencies.go @@ -0,0 +1,49 @@ +// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package load + +import "context" + +type TxGenerator[T comparable] interface { + // GenerateTx returns a valid transaction. + GenerateTx() (T, error) +} + +type Issuer[T comparable] interface { + // Listen for the final status of transactions and notify the tracker + // Listen stops if the context is done, an error occurs, or if the issuer + // has sent all their transactions. + // Listen MUST return a nil error if the context is canceled. + Listen(ctx context.Context) error + + // Stop notifies the issuer that no further transactions will be issued. + // If a transaction is issued after Stop has been called, the issuer should error. + Stop() + + // Issue sends a tx to the network, and informs the tracker that its sent + // said transaction. + IssueTx(ctx context.Context, tx T) error +} + +// Tracker keeps track of the status of transactions. +// This must be thread-safe, so it can be called in parallel by the issuer(s) or orchestrator. +type Tracker[T comparable] interface { + // Issue records a transaction that was submitted, but whose final status is + // not yet known. + Issue(T) + // ObserveConfirmed records a transaction that was confirmed. + ObserveConfirmed(T) + // ObserveFailed records a transaction that failed (e.g. expired) + ObserveFailed(T) + + // GetObservedIssued returns the number of transactions that the tracker has + // confirmed were issued. + GetObservedIssued() uint64 + // GetObservedConfirmed returns the number of transactions that the tracker has + // confirmed were accepted. + GetObservedConfirmed() uint64 + // GetObservedFailed returns the number of transactions that the tracker has + // confirmed failed. + GetObservedFailed() uint64 +} diff --git a/tests/load/execute.go b/tests/load/execute.go new file mode 100644 index 000000000000..fe024ea6248d --- /dev/null +++ b/tests/load/execute.go @@ -0,0 +1,127 @@ +// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package load + +import ( + "context" + "crypto/ecdsa" + "fmt" + "math/big" + "path/filepath" + "time" + + "github.com/ava-labs/coreth/params" + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/types" + "github.com/ava-labs/libevm/ethclient" + "github.com/prometheus/client_golang/prometheus" + + "github.com/ava-labs/avalanchego/tests/load/agent" + "github.com/ava-labs/avalanchego/tests/load/generator" + "github.com/ava-labs/avalanchego/tests/load/issuer" + "github.com/ava-labs/avalanchego/tests/load/orchestrate" + "github.com/ava-labs/avalanchego/tests/load/tracker" + + ethcrypto "github.com/ava-labs/libevm/crypto" +) + +const ( + keyDir = "./keys" +) + +type Config struct { + Endpoints []string `json:"endpoints"` + MaxFeeCap int64 `json:"max-fee-cap"` + MaxTipCap int64 `json:"max-tip-cap"` + Agents uint `json:"agents"` + TxsPerAgent uint64 `json:"txs-per-agent"` +} + +func Execute(ctx context.Context, config Config) error { + keys, err := loadKeysFromDir(keyDir) + if err != nil { + return fmt.Errorf("loading keys: %w", err) + } + + keys, err = ensureKeysNumber(keys, config.Agents) + if err != nil { + return fmt.Errorf("ensuring keys number: %w", err) + } + + // Minimum to fund gas for all of the transactions for an address: + minFundsPerAddr := new(big.Int).SetUint64(params.GWei * uint64(config.MaxFeeCap) * params.TxGas * config.TxsPerAgent) + err = ensureMinimumFunds(ctx, config.Endpoints[0], keys, minFundsPerAddr) + if err != nil { + return fmt.Errorf("ensuring minimum funds: %w", err) + } + + registry := prometheus.NewRegistry() + metricsServer := tracker.NewMetricsServer("127.0.0.1:8082", registry) + tracker := tracker.New(registry) + + agents := make([]*agent.Agent[*types.Transaction, common.Hash], config.Agents) + for i := range agents { + endpoint := config.Endpoints[i%len(config.Endpoints)] + client, err := ethclient.DialContext(ctx, endpoint) + if err != nil { + return fmt.Errorf("dialing %s: %w", endpoint, err) + } + generator, err := generator.NewSelf(ctx, client, + big.NewInt(config.MaxTipCap), big.NewInt(config.MaxFeeCap), keys[i]) + if err != nil { + return fmt.Errorf("creating generator: %w", err) + } + address := ethcrypto.PubkeyToAddress(keys[i].PublicKey) + issuer := issuer.New(client, tracker, address) + agents[i] = agent.New[*types.Transaction, common.Hash](config.TxsPerAgent, generator, issuer, tracker) + } + + metricsErrCh, err := metricsServer.Start() + if err != nil { + return fmt.Errorf("starting metrics server: %w", err) + } + + orchestratorCtx, orchestratorCancel := context.WithCancel(ctx) + defer orchestratorCancel() + orchestrator := orchestrate.NewBurstOrchestrator(agents, time.Second) + orchestratorErrCh := make(chan error) + go func() { + orchestratorErrCh <- orchestrator.Execute(orchestratorCtx) + }() + + select { + case err := <-orchestratorErrCh: + if err != nil { + _ = metricsServer.Stop() + return fmt.Errorf("orchestrator error: %w", err) + } + err = metricsServer.Stop() + if err != nil { + return fmt.Errorf("stopping metrics server: %w", err) + } + return nil + case err := <-metricsErrCh: + orchestratorCancel() + <-orchestratorErrCh + return fmt.Errorf("metrics server error: %w", err) + } +} + +func ensureKeysNumber(keys []*ecdsa.PrivateKey, target uint) ([]*ecdsa.PrivateKey, error) { + for len(keys) < int(target) { + newKey, err := ethcrypto.GenerateKey() + if err != nil { + return nil, fmt.Errorf("generating key: %w", err) + } + + address := ethcrypto.PubkeyToAddress(newKey.PublicKey).Hex() + filePath := filepath.Join(keyDir, address) + err = ethcrypto.SaveECDSA(filePath, newKey) + if err != nil { + return nil, fmt.Errorf("saving key at index %d: %w", len(keys)-1, err) + } + keys = append(keys, newKey) + } + return keys, nil +} diff --git a/tests/load/funder.go b/tests/load/funder.go new file mode 100644 index 000000000000..b8ec3332b976 --- /dev/null +++ b/tests/load/funder.go @@ -0,0 +1,123 @@ +// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package load + +import ( + "context" + "crypto/ecdsa" + "fmt" + "maps" + "math/big" + "slices" + "sort" + "time" + + "github.com/ava-labs/coreth/ethclient" + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/types" + + "github.com/ava-labs/avalanchego/tests/load/agent" + "github.com/ava-labs/avalanchego/tests/load/generator" + "github.com/ava-labs/avalanchego/tests/load/issuer" + "github.com/ava-labs/avalanchego/tests/load/orchestrate" + "github.com/ava-labs/avalanchego/tests/load/tracker" + + ethcrypto "github.com/ava-labs/libevm/crypto" +) + +// ensureMinimumFunds ensures that each key has at least `minFundsPerAddr` by sending funds +// from the key with the highest starting balance to keys with balances below the minimum. +func ensureMinimumFunds(ctx context.Context, endpoint string, keys []*ecdsa.PrivateKey, minFundsPerAddr *big.Int) error { + client, err := ethclient.DialContext(ctx, endpoint) + if err != nil { + return fmt.Errorf("dialing %s: %w", endpoint, err) + } + + keyToBalance, err := getKeyToBalance(ctx, client, keys) + if err != nil { + return fmt.Errorf("getting key to balance: %w", err) + } + + sort.Slice(keys, func(i, j int) bool { + return keyToBalance[keys[i]].Cmp(keyToBalance[keys[j]]) < 0 + }) + + needFundsKeys := make(map[*ecdsa.PrivateKey]*big.Int, len(keys)) + totalFundsRequired := new(big.Int) + for _, key := range keys { + balance := keyToBalance[key] + diff := new(big.Int).Sub(minFundsPerAddr, balance) + if diff.Cmp(common.Big0) <= 0 { + // Found the key with funds equal or above the minimum funds, + // so all the next keys have enough funds. + break + } + totalFundsRequired.Add(totalFundsRequired, diff) + needFundsKeys[key] = diff + } + + maxFundsKey := keys[len(keys)-1] + maxFundsBalance := keyToBalance[maxFundsKey] + if maxFundsBalance.Cmp(totalFundsRequired) < 0 { + return fmt.Errorf("insufficient funds %d (require %d) to distribute to %d keys", + maxFundsBalance, totalFundsRequired, len(needFundsKeys)) + } + + maxFundsAddress := ethcrypto.PubkeyToAddress(maxFundsKey.PublicKey) + txTarget := uint64(len(needFundsKeys)) + generator, err := generator.NewDistributor(ctx, client, maxFundsKey, needFundsKeys) + if err != nil { + return fmt.Errorf("creating distribution generator: %w", err) + } + tracker := tracker.NewCounter() + issuer := issuer.New(client, tracker, maxFundsAddress) + agents := []*agent.Agent[*types.Transaction, common.Hash]{ + agent.New(txTarget, generator, issuer, tracker), + } + orchestrator := orchestrate.NewBurstOrchestrator(agents, time.Second) + + err = orchestrator.Execute(ctx) + if err != nil { + return fmt.Errorf("executing fund distribution transactions: %w", err) + } + + err = checkBalancesHaveMin(ctx, client, slices.Collect(maps.Keys(needFundsKeys)), minFundsPerAddr) + if err != nil { + return fmt.Errorf("checking balances after funding: %w", err) + } + + return nil +} + +type ethClientBalancer interface { + BalanceAt(ctx context.Context, address common.Address, blockNumber *big.Int) (*big.Int, error) +} + +func getKeyToBalance(ctx context.Context, client ethClientBalancer, keys []*ecdsa.PrivateKey) (map[*ecdsa.PrivateKey]*big.Int, error) { + keyToBalance := make(map[*ecdsa.PrivateKey]*big.Int, len(keys)) + var err error + for _, key := range keys { + address := ethcrypto.PubkeyToAddress(key.PublicKey) + blockNumber := (*big.Int)(nil) + keyToBalance[key], err = client.BalanceAt(ctx, address, blockNumber) + if err != nil { + return nil, fmt.Errorf("fetching balance for address %s: %w", address, err) + } + } + return keyToBalance, nil +} + +func checkBalancesHaveMin(ctx context.Context, client ethClientBalancer, keys []*ecdsa.PrivateKey, minFundsPerAddr *big.Int) error { + keyToBalance, err := getKeyToBalance(ctx, client, keys) + if err != nil { + return fmt.Errorf("getting key to balance for newly funded keys: %w", err) + } + for key, balance := range keyToBalance { + if balance.Cmp(minFundsPerAddr) < 0 { + address := ethcrypto.PubkeyToAddress(key.PublicKey) + return fmt.Errorf("address %s has insufficient funds %d < %d", address, balance, minFundsPerAddr) + } + } + return nil +} diff --git a/tests/load/generator/distribute.go b/tests/load/generator/distribute.go new file mode 100644 index 000000000000..f3b45103a080 --- /dev/null +++ b/tests/load/generator/distribute.go @@ -0,0 +1,107 @@ +// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package generator + +import ( + "context" + "crypto/ecdsa" + "errors" + "fmt" + "math/big" + + "github.com/ava-labs/coreth/params" + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/types" + + ethcrypto "github.com/ava-labs/libevm/crypto" +) + +type DistributorClient interface { + SelfClient + EstimateBaseFee(ctx context.Context) (*big.Int, error) + SuggestGasTipCap(ctx context.Context) (*big.Int, error) +} + +// Distributor +type Distributor struct { + from *ecdsa.PrivateKey + address common.Address // corresponding to `from` + nonce uint64 + to map[*ecdsa.PrivateKey]*big.Int + toIndex int + signer types.Signer + chainID *big.Int + gasTipCap *big.Int + gasFeeCap *big.Int +} + +func NewDistributor(ctx context.Context, client DistributorClient, from *ecdsa.PrivateKey, + to map[*ecdsa.PrivateKey]*big.Int, +) (*Distributor, error) { + address := ethcrypto.PubkeyToAddress(from.PublicKey) + blockNumber := (*big.Int)(nil) + nonce, err := client.NonceAt(ctx, address, blockNumber) + if err != nil { + return nil, fmt.Errorf("getting nonce for address %s: %w", address, err) + } + + chainID, err := client.ChainID(ctx) + if err != nil { + return nil, fmt.Errorf("getting chain ID: %w", err) + } + + gasTipCap, err := client.SuggestGasTipCap(ctx) + if err != nil { + return nil, fmt.Errorf("getting suggested gas tip cap: %w", err) + } + + gasFeeCap, err := client.EstimateBaseFee(ctx) + if err != nil { + return nil, fmt.Errorf("getting estimated base fee: %w", err) + } + + return &Distributor{ + from: from, + address: address, + nonce: nonce + 1, + to: to, + signer: types.LatestSignerForChainID(chainID), + chainID: chainID, + gasTipCap: gasTipCap, + gasFeeCap: gasFeeCap, + }, nil +} + +func (d *Distributor) GenerateTx() (*types.Transaction, error) { + if d.toIndex == len(d.to) { + // The caller of this function should prevent this error from being + // returned by bounding the number of transactions generated. + return nil, errors.New("no more keys to distribute funds to") + } + + var toKey *ecdsa.PrivateKey + var funds *big.Int + for toKey, funds = range d.to { + break + } + delete(d.to, toKey) + + to := ethcrypto.PubkeyToAddress(toKey.PublicKey) + tx, err := types.SignNewTx(d.from, d.signer, &types.DynamicFeeTx{ + ChainID: d.chainID, + Nonce: d.nonce, + GasTipCap: d.gasTipCap, + GasFeeCap: d.gasFeeCap, + Gas: params.TxGas, + To: &to, + Data: nil, + Value: funds, + }) + if err != nil { + return nil, err + } + d.nonce++ + d.toIndex++ + return tx, nil +} diff --git a/tests/load/generator/self.go b/tests/load/generator/self.go new file mode 100644 index 000000000000..ba80260b5e7e --- /dev/null +++ b/tests/load/generator/self.go @@ -0,0 +1,80 @@ +// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package generator + +import ( + "context" + "crypto/ecdsa" + "fmt" + "math/big" + + "github.com/ava-labs/coreth/params" + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/types" + + ethcrypto "github.com/ava-labs/libevm/crypto" +) + +type SelfClient interface { + ChainID(ctx context.Context) (*big.Int, error) + NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) +} + +// Self generates transactions sending 0 fund to the sender key. +type Self struct { + key *ecdsa.PrivateKey + address common.Address // corresponding to key + nonce uint64 + signer types.Signer + chainID *big.Int + gasTipCap *big.Int + gasFeeCap *big.Int +} + +func NewSelf(ctx context.Context, client SelfClient, + maxTipCap, maxFeeCap *big.Int, key *ecdsa.PrivateKey, +) (*Self, error) { + address := ethcrypto.PubkeyToAddress(key.PublicKey) + blockNumber := (*big.Int)(nil) + nonce, err := client.NonceAt(ctx, address, blockNumber) + if err != nil { + return nil, fmt.Errorf("getting nonce for address %s: %w", address, err) + } + + bigGwei := big.NewInt(params.GWei) + gasTipCap := new(big.Int).Mul(bigGwei, maxTipCap) + gasFeeCap := new(big.Int).Mul(bigGwei, maxFeeCap) + chainID, err := client.ChainID(ctx) + if err != nil { + return nil, fmt.Errorf("getting chain id: %w", err) + } + + return &Self{ + key: key, + address: address, + nonce: nonce + 1, + signer: types.LatestSignerForChainID(chainID), + chainID: chainID, + gasTipCap: gasTipCap, + gasFeeCap: gasFeeCap, + }, nil +} + +func (s *Self) GenerateTx() (*types.Transaction, error) { + tx, err := types.SignNewTx(s.key, s.signer, &types.DynamicFeeTx{ + ChainID: s.chainID, + Nonce: s.nonce, + GasTipCap: s.gasTipCap, + GasFeeCap: s.gasFeeCap, + Gas: params.TxGas, + To: &s.address, // self + Data: nil, + Value: common.Big0, + }) + if err != nil { + return nil, err + } + s.nonce++ + return tx, nil +} diff --git a/tests/load/issuer/issuer.go b/tests/load/issuer/issuer.go new file mode 100644 index 000000000000..9575a50a6975 --- /dev/null +++ b/tests/load/issuer/issuer.go @@ -0,0 +1,118 @@ +// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package issuer + +import ( + "context" + "fmt" + "math/big" + "sync" + + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/types" +) + +type EthClient interface { + NewHeadSubscriber + SendTransaction(ctx context.Context, tx *types.Transaction) error + NonceAt(ctx context.Context, addr common.Address, blockNumber *big.Int) (uint64, error) +} + +type Tracker interface { + IssueStart(txHash common.Hash) + IssueEnd(txHash common.Hash) + ObserveConfirmed(txHash common.Hash) +} + +// Issuer issues transactions to a node and waits for them to be accepted (or failed). +// It +type Issuer struct { + // Injected parameters + client EthClient + tracker Tracker + address common.Address + + // Internal state + mutex sync.Mutex + issuedTxs uint64 + lastIssuedNonce uint64 + inFlightTxHashes []common.Hash + allIssued bool +} + +func New(client EthClient, tracker Tracker, address common.Address) *Issuer { + return &Issuer{ + client: client, + tracker: tracker, + address: address, + } +} + +func (i *Issuer) IssueTx(ctx context.Context, tx *types.Transaction) error { + i.mutex.Lock() + defer i.mutex.Unlock() + + txHash, txNonce := tx.Hash(), tx.Nonce() + if txNonce > 0 && txNonce != i.lastIssuedNonce+1 { + return fmt.Errorf("transaction nonce %d is not equal to the last issued nonce %d plus one", txNonce, i.lastIssuedNonce) + } + i.tracker.IssueStart(txHash) + if err := i.client.SendTransaction(ctx, tx); err != nil { + return err + } + i.tracker.IssueEnd(txHash) + i.inFlightTxHashes = append(i.inFlightTxHashes, txHash) + i.issuedTxs++ + i.lastIssuedNonce = txNonce + return nil +} + +func (i *Issuer) Listen(ctx context.Context) error { + headNotifier := newHeadNotifier(i.client) + newHeadSignal, notifierErrCh := headNotifier.start(ctx) + defer headNotifier.stop() + + for { + blockNumber := (*big.Int)(nil) + nonce, err := i.client.NonceAt(ctx, i.address, blockNumber) + if err != nil { + if ctx.Err() != nil { + return nil + } + return fmt.Errorf("checking last block account nonce for address %s: %w", i.address, err) + } + + i.mutex.Lock() + confirmed := uint64(len(i.inFlightTxHashes)) + if nonce < i.lastIssuedNonce { // lagging behind last issued nonce + lag := i.lastIssuedNonce - nonce + confirmed -= lag + } + for index := range confirmed { + txHash := i.inFlightTxHashes[index] + i.tracker.ObserveConfirmed(txHash) + } + i.inFlightTxHashes = i.inFlightTxHashes[confirmed:] + finished := i.allIssued && len(i.inFlightTxHashes) == 0 + if finished { + i.mutex.Unlock() + return nil + } + i.mutex.Unlock() + + select { + case <-ctx.Done(): + return nil + case err := <-notifierErrCh: + return fmt.Errorf("new head notifier failed: %w", err) + case <-newHeadSignal: + } + } +} + +func (i *Issuer) Stop() { + i.mutex.Lock() + defer i.mutex.Unlock() + i.allIssued = true +} diff --git a/tests/load/issuer/newhead.go b/tests/load/issuer/newhead.go new file mode 100644 index 000000000000..806dac604cd5 --- /dev/null +++ b/tests/load/issuer/newhead.go @@ -0,0 +1,113 @@ +// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package issuer + +import ( + "context" + "fmt" + "time" + + "github.com/ava-labs/libevm/core/types" + "github.com/ava-labs/libevm/log" + + ethereum "github.com/ava-labs/libevm" +) + +type NewHeadSubscriber interface { + SubscribeNewHead(ctx context.Context, ch chan<- *types.Header) (ethereum.Subscription, error) +} + +type headNotifier struct { + client NewHeadSubscriber + listenStop chan<- struct{} + listenDone <-chan struct{} + subscription ethereum.Subscription +} + +func newHeadNotifier(client NewHeadSubscriber) *headNotifier { + return &headNotifier{ + client: client, + } +} + +func (n *headNotifier) start(ctx context.Context) (newHead <-chan struct{}, runError <-chan error) { + newHeadSignal := make(chan struct{}) + + listenStop := make(chan struct{}) + n.listenStop = listenStop + listenDone := make(chan struct{}) + n.listenDone = listenDone + ready := make(chan struct{}) + + subscriptionCh := make(chan *types.Header) + subscription, err := n.client.SubscribeNewHead(ctx, subscriptionCh) + if err != nil { + log.Debug("failed to subscribe new heads, falling back to polling", "err", err) + go periodicNotify(listenStop, listenDone, ready, newHeadSignal) + <-ready + return newHeadSignal, nil // no possible error at runtime + } + go subscriptionChToSignal(listenStop, listenDone, ready, subscriptionCh, newHeadSignal) + <-ready + n.subscription = subscription + return newHeadSignal, n.makeRunErrCh() +} + +func (n *headNotifier) stop() { + if n.subscription != nil { + n.subscription.Unsubscribe() + } + close(n.listenStop) + <-n.listenDone +} + +// periodicNotify is the fallback if the connection is not a websocket. +func periodicNotify(listenStop <-chan struct{}, listenDone, ready chan<- struct{}, + newHeadSignal chan<- struct{}, +) { + defer close(listenDone) + ticker := time.NewTicker(time.Second) + close(ready) + for { + select { + case <-listenStop: + ticker.Stop() + return + case <-ticker.C: + newHeadSignal <- struct{}{} + } + } +} + +func subscriptionChToSignal(listenStop <-chan struct{}, listenDone, ready chan<- struct{}, + subCh <-chan *types.Header, newHeadSignal chan<- struct{}, +) { + defer close(listenDone) + close(ready) + for { + select { + case <-listenStop: + return + case <-subCh: + newHeadSignal <- struct{}{} + } + } +} + +// makeRunErrCh makes sure the [newHeadNotifyer] fully stops when +// a subscription error is encountered. +func (n *headNotifier) makeRunErrCh() <-chan error { + errCh := make(chan error) + go func() { + err, ok := <-n.subscription.Err() + if !ok { + // channel is closed when [ethereum.Subscription] `Unsubscribe` + // is called within [Issuer.stopForwarding]. + return + } + n.stop() + errCh <- fmt.Errorf("subscription error: %w", err) + }() + return errCh +} diff --git a/tests/load/key.go b/tests/load/key.go new file mode 100644 index 000000000000..04352ff83e1d --- /dev/null +++ b/tests/load/key.go @@ -0,0 +1,49 @@ +// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package load + +import ( + "crypto/ecdsa" + "fmt" + "os" + "path/filepath" + + ethcrypto "github.com/ava-labs/libevm/crypto" +) + +func loadKeysFromDir(dir string) ([]*ecdsa.PrivateKey, error) { + _, err := os.Stat(dir) + if err != nil { + if !os.IsNotExist(err) { + return nil, err + } + if err := os.MkdirAll(dir, 0o755); err != nil { + return nil, err + } + return nil, nil + } + + var paths []string + err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } else if path == dir || info.IsDir() { + return nil + } + paths = append(paths, path) + return nil + }) + if err != nil { + return nil, fmt.Errorf("walking %s: %w", dir, err) + } + + keys := make([]*ecdsa.PrivateKey, len(paths)) + for i, path := range paths { + keys[i], err = ethcrypto.LoadECDSA(path) + if err != nil { + return nil, fmt.Errorf("loading private key from %s: %w", path, err) + } + } + return keys, nil +} diff --git a/tests/load/orchestrate/burst.go b/tests/load/orchestrate/burst.go new file mode 100644 index 000000000000..29f5f707e47e --- /dev/null +++ b/tests/load/orchestrate/burst.go @@ -0,0 +1,85 @@ +// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package orchestrate + +import ( + "context" + "fmt" + "time" + + "golang.org/x/sync/errgroup" + + "github.com/ava-labs/avalanchego/tests/load/agent" +) + +// BurstOrchestrator executes a series of worker/tx sequence pairs. +// Each worker/txSequence pair issues [batchSize] transactions, confirms all +// of them as accepted, and then moves to the next batch until the txSequence +// is exhausted. +type BurstOrchestrator[T, U comparable] struct { + agents []*agent.Agent[T, U] + timeout time.Duration +} + +func NewBurstOrchestrator[T, U comparable]( + agents []*agent.Agent[T, U], + timeout time.Duration, +) *BurstOrchestrator[T, U] { + return &BurstOrchestrator[T, U]{ + agents: agents, + timeout: timeout, + } +} + +func (o *BurstOrchestrator[T, U]) Execute(ctx context.Context) error { + observerCtx, observerCancel := context.WithCancel(ctx) + defer observerCancel() + + // start a goroutine to confirm each issuer's transactions + observerGroup := errgroup.Group{} + for _, loader := range o.agents { + observerGroup.Go(func() error { return loader.Issuer.Listen(observerCtx) }) + } + + // start issuing transactions sequentially from each issuer + issuerGroup := errgroup.Group{} + for _, agent := range o.agents { + issuerGroup.Go(func() error { + defer agent.Issuer.Stop() + + for range agent.TxTarget { + tx, err := agent.Generator.GenerateTx() + if err != nil { + return fmt.Errorf("generating transaction: %w", err) + } + if err := agent.Issuer.IssueTx(ctx, tx); err != nil { + return fmt.Errorf("issuing transaction: %w", err) + } + } + return nil + }) + } + + // wait for all issuers to finish sending their transactions + if err := issuerGroup.Wait(); err != nil { + return fmt.Errorf("issuers: %w", err) + } + + ctx, cancel := context.WithTimeout(ctx, o.timeout) + defer cancel() + + // start a goroutine that will cancel the observer group's context + // if either the parent context is cancelled or our timeout elapses + go func() { + <-ctx.Done() + observerCancel() + }() + + // blocks until either all of the issuers have finished or our context + // is cancelled signalling for early termination (with an error) + if err := observerGroup.Wait(); err != nil { + return fmt.Errorf("observers: %w", err) + } + return nil +} diff --git a/tests/load/orchestrate/dependencies.go b/tests/load/orchestrate/dependencies.go new file mode 100644 index 000000000000..7fb985201e5d --- /dev/null +++ b/tests/load/orchestrate/dependencies.go @@ -0,0 +1,49 @@ +// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package orchestrate + +import "context" + +type TxGenerator[T comparable] interface { + // GenerateTx returns a valid transaction. + GenerateTx() (T, error) +} + +type Issuer[T comparable] interface { + // Listen for the final status of transactions and notify the tracker + // Listen stops if the context is done, an error occurs, or if the issuer + // has sent all their transactions. + // Listen MUST return a nil error if the context is canceled. + Listen(ctx context.Context) error + + // Stop notifies the issuer that no further transactions will be issued. + // If a transaction is issued after Stop has been called, the issuer should error. + Stop() + + // Issue sends a tx to the network, and informs the tracker that its sent + // said transaction. + IssueTx(ctx context.Context, tx T) error +} + +// Tracker keeps track of the status of transactions. +// This must be thread-safe, so it can be called in parallel by the issuer(s) or orchestrator. +type Tracker[T comparable] interface { + // Issue records a transaction that was submitted, but whose final status is + // not yet known. + Issue(T) + // ObserveConfirmed records a transaction that was confirmed. + ObserveConfirmed(T) + // ObserveFailed records a transaction that failed (e.g. expired) + ObserveFailed(T) + + // GetObservedIssued returns the number of transactions that the tracker has + // confirmed were issued. + GetObservedIssued() uint64 + // GetObservedConfirmed returns the number of transactions that the tracker has + // confirmed were accepted. + GetObservedConfirmed() uint64 + // GetObservedFailed returns the number of transactions that the tracker has + // confirmed failed. + GetObservedFailed() uint64 +} diff --git a/tests/load/tracker/counter.go b/tests/load/tracker/counter.go new file mode 100644 index 000000000000..8bc572d240ce --- /dev/null +++ b/tests/load/tracker/counter.go @@ -0,0 +1,26 @@ +// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package tracker + +import ( + "github.com/ava-labs/libevm/common" +) + +// Counter only counts the number of transactions confirmed or failed, +// so that it is accessible through Go code. +type Counter struct { + confirmed uint64 + failed uint64 +} + +func NewCounter() *Counter { + return &Counter{} +} + +func (*Counter) IssueStart(_ common.Hash) {} +func (*Counter) IssueEnd(_ common.Hash) {} +func (c *Counter) ObserveConfirmed(_ common.Hash) { c.confirmed++ } +func (c *Counter) ObserveFailed(_ common.Hash) { c.failed++ } +func (c *Counter) GetObservedConfirmed() uint64 { return c.confirmed } +func (c *Counter) GetObservedFailed() uint64 { return c.failed } diff --git a/tests/load/tracker/metrics.go b/tests/load/tracker/metrics.go new file mode 100644 index 000000000000..e6d09e77d87c --- /dev/null +++ b/tests/load/tracker/metrics.go @@ -0,0 +1,95 @@ +// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package tracker + +import ( + "fmt" + + "github.com/prometheus/client_golang/prometheus" +) + +type metrics struct { + // Confirmed is the number of confirmed transactions. + Confirmed prometheus.Counter + // Failed is the number of failed transactions. + Failed prometheus.Counter + // InFlightIssuances is the number of transactions that are being issued, from issuance start + // to issuance end. + InFlightIssuances prometheus.Gauge + // InFlightTxs is the number of transactions that are in-flight, from issuance start + // to confirmation end or failure. + InFlightTxs prometheus.Gauge + // IssuanceTxTimes is the summary of the quantiles of individual issuance tx times. + IssuanceTxTimes prometheus.Summary + // ConfirmationTxTimes is the summary of the quantiles of individual confirmation tx times. + // Failed transactions do not show in this metric. + ConfirmationTxTimes prometheus.Summary + + registry PrometheusRegistry +} + +type PrometheusRegistry interface { + prometheus.Registerer + prometheus.Gatherer +} + +func newMetrics(registry PrometheusRegistry) *metrics { + confirmed := prometheus.NewCounter(prometheus.CounterOpts{ + Name: "tx_confirmed", + Help: "Number of confirmed transactions", + }) + failed := prometheus.NewCounter(prometheus.CounterOpts{ + Name: "tx_failed", + Help: "Number of failed transactions", + }) + inFlightIssuances := prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "tx_in_flight_issuances", + Help: "Number of transactions in flight issuances", + }) + inFlightTxs := prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "tx_in_flight_txs", + Help: "Number of transactions in flight", + }) + issuanceTxTimes := prometheus.NewSummary(prometheus.SummaryOpts{ + Name: "tx_issuance_time", + Help: "Individual Tx Issuance Times for a Load Test", + Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, + }) + confirmationTxTimes := prometheus.NewSummary(prometheus.SummaryOpts{ + Name: "tx_confirmation_time", + Help: "Individual Tx Confirmation Times for a Load Test", + Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, + }) + + registry.MustRegister(confirmed, failed, inFlightIssuances, inFlightTxs, + issuanceTxTimes, confirmationTxTimes) + + return &metrics{ + registry: registry, + Confirmed: confirmed, + InFlightIssuances: inFlightIssuances, + InFlightTxs: inFlightTxs, + IssuanceTxTimes: issuanceTxTimes, + ConfirmationTxTimes: confirmationTxTimes, + } +} + +func (m *metrics) String() string { + s := "Metrics:\n" + + metricFamilies, err := m.registry.Gather() + if err != nil { + return s + "failed to gather metrics: " + err.Error() + "\n" + } + + for _, mf := range metricFamilies { + metrics := mf.GetMetric() + for _, metric := range metrics { + s += fmt.Sprintf("Name: %s, Type: %s, Description: %s, Values: %s\n", + mf.GetName(), mf.GetType().String(), mf.GetHelp(), metric.String()) + } + } + + return s +} diff --git a/tests/load/tracker/metrics_server.go b/tests/load/tracker/metrics_server.go new file mode 100644 index 000000000000..11af47e603f5 --- /dev/null +++ b/tests/load/tracker/metrics_server.go @@ -0,0 +1,78 @@ +// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package tracker + +import ( + "context" + "errors" + "fmt" + "net" + "net/http" + "time" + + "github.com/ava-labs/libevm/log" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" +) + +type MetricsServer struct { + addr string + registry *prometheus.Registry + server http.Server +} + +func NewMetricsServer(addr string, registry *prometheus.Registry) *MetricsServer { + return &MetricsServer{ + addr: addr, + registry: registry, + } +} + +func (*MetricsServer) String() string { + return "metrics server" +} + +func (s *MetricsServer) Start() (runError <-chan error, err error) { + const metricsPattern = "/metrics" + + mux := http.NewServeMux() + handlerOpts := promhttp.HandlerOpts{Registry: s.registry} + mux.Handle(metricsPattern, promhttp.HandlerFor(s.registry, handlerOpts)) + + listener, err := net.Listen("tcp", s.addr) + if err != nil { + return nil, err + } + + s.server = http.Server{ + Addr: s.addr, + Handler: mux, + ReadHeaderTimeout: time.Second, + ReadTimeout: time.Second, + } + + runErrorBiDirectional := make(chan error) + runError = runErrorBiDirectional + ready := make(chan struct{}) + go func() { + close(ready) + err = s.server.Serve(listener) + if errors.Is(err, http.ErrServerClosed) { + return + } + runErrorBiDirectional <- err + }() + <-ready + + log.Info(fmt.Sprintf("Metrics server available at http://%s%s", listener.Addr(), metricsPattern)) + + return runError, nil +} + +func (s *MetricsServer) Stop() (err error) { + const shutdownTimeout = time.Second + shutdownCtx, cancel := context.WithTimeout(context.Background(), shutdownTimeout) + defer cancel() + return s.server.Shutdown(shutdownCtx) +} diff --git a/tests/load/tracker/tracker.go b/tests/load/tracker/tracker.go new file mode 100644 index 000000000000..1ecfbf68689f --- /dev/null +++ b/tests/load/tracker/tracker.go @@ -0,0 +1,95 @@ +// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package tracker + +import ( + "sync" + "time" + + "github.com/ava-labs/libevm/common" +) + +type Tracker struct { + timeNow func() time.Time + txHashToLastTime map[common.Hash]time.Time + metrics *metrics + + stats struct { + confirmed uint64 + failed uint64 + } + statsMutex sync.RWMutex +} + +// New creates a new Tracker instance. +// The tracker should then be started usign [Tracker.Start] and stopped +// using [Tracker.Stop]. +func New(registry PrometheusRegistry) *Tracker { + return &Tracker{ + timeNow: time.Now, + txHashToLastTime: make(map[common.Hash]time.Time), + metrics: newMetrics(registry), + } +} + +// IssueStart records a transaction that is being issued. +func (t *Tracker) IssueStart(txHash common.Hash) { + t.metrics.InFlightIssuances.Inc() + t.metrics.InFlightTxs.Inc() + t.txHashToLastTime[txHash] = t.timeNow() +} + +// IssueEnd records a transaction that was issued, but whose final status is +// not yet known. +func (t *Tracker) IssueEnd(txHash common.Hash) { + t.metrics.InFlightIssuances.Dec() + + start := t.txHashToLastTime[txHash] + now := t.timeNow() + diff := now.Sub(start) + t.metrics.IssuanceTxTimes.Observe(diff.Seconds()) + t.txHashToLastTime[txHash] = t.timeNow() +} + +// ObserveConfirmed records a transaction that was confirmed. +func (t *Tracker) ObserveConfirmed(txHash common.Hash) { + t.metrics.InFlightTxs.Dec() + t.metrics.Confirmed.Inc() + issuedTime := t.txHashToLastTime[txHash] + now := t.timeNow() + diff := now.Sub(issuedTime) + t.metrics.ConfirmationTxTimes.Observe(diff.Seconds()) + delete(t.txHashToLastTime, txHash) + + t.statsMutex.Lock() + t.stats.confirmed++ + t.statsMutex.Unlock() +} + +// ObserveFailed records a transaction that failed (e.g. expired) +func (t *Tracker) ObserveFailed(txHash common.Hash) { + t.metrics.InFlightTxs.Dec() + t.metrics.Failed.Inc() + delete(t.txHashToLastTime, txHash) + + t.statsMutex.Lock() + t.stats.failed++ + t.statsMutex.Unlock() +} + +// GetObservedConfirmed returns the number of transactions that the tracker has +// confirmed were accepted. +func (t *Tracker) GetObservedConfirmed() uint64 { + t.statsMutex.RLock() + defer t.statsMutex.RUnlock() + return t.stats.confirmed +} + +// GetObservedFailed returns the number of transactions that the tracker has +// confirmed failed. +func (t *Tracker) GetObservedFailed() uint64 { + t.statsMutex.RLock() + defer t.statsMutex.RUnlock() + return t.stats.failed +} From 863adccf233da10d34c4f4cfb616178eea854284 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 24 Apr 2025 20:45:11 +0200 Subject: [PATCH 002/197] Split listeners in poll and subscription depending if connection is websocket --- tests/load/execute.go | 4 ++- tests/load/funder.go | 4 ++- tests/load/issuer/issuer.go | 58 +++++++-------------------------- tests/load/issuer/listenpoll.go | 51 +++++++++++++++++++++++++++++ tests/load/issuer/listensub.go | 56 +++++++++++++++++++++++++++++++ tests/load/issuer/newhead.go | 33 +++---------------- 6 files changed, 129 insertions(+), 77 deletions(-) create mode 100644 tests/load/issuer/listenpoll.go create mode 100644 tests/load/issuer/listensub.go diff --git a/tests/load/execute.go b/tests/load/execute.go index fe024ea6248d..2f7181a9c1db 100644 --- a/tests/load/execute.go +++ b/tests/load/execute.go @@ -9,6 +9,7 @@ import ( "fmt" "math/big" "path/filepath" + "strings" "time" "github.com/ava-labs/coreth/params" @@ -63,6 +64,7 @@ func Execute(ctx context.Context, config Config) error { agents := make([]*agent.Agent[*types.Transaction, common.Hash], config.Agents) for i := range agents { endpoint := config.Endpoints[i%len(config.Endpoints)] + websocket := strings.HasPrefix(endpoint, "ws://") || strings.HasPrefix(endpoint, "wss://") client, err := ethclient.DialContext(ctx, endpoint) if err != nil { return fmt.Errorf("dialing %s: %w", endpoint, err) @@ -73,7 +75,7 @@ func Execute(ctx context.Context, config Config) error { return fmt.Errorf("creating generator: %w", err) } address := ethcrypto.PubkeyToAddress(keys[i].PublicKey) - issuer := issuer.New(client, tracker, address) + issuer := issuer.New(client, websocket, tracker, address) agents[i] = agent.New[*types.Transaction, common.Hash](config.TxsPerAgent, generator, issuer, tracker) } diff --git a/tests/load/funder.go b/tests/load/funder.go index b8ec3332b976..96b9386d0fb4 100644 --- a/tests/load/funder.go +++ b/tests/load/funder.go @@ -11,6 +11,7 @@ import ( "math/big" "slices" "sort" + "strings" "time" "github.com/ava-labs/coreth/ethclient" @@ -29,6 +30,7 @@ import ( // ensureMinimumFunds ensures that each key has at least `minFundsPerAddr` by sending funds // from the key with the highest starting balance to keys with balances below the minimum. func ensureMinimumFunds(ctx context.Context, endpoint string, keys []*ecdsa.PrivateKey, minFundsPerAddr *big.Int) error { + websocket := strings.HasPrefix(endpoint, "ws") || strings.HasPrefix(endpoint, "wss") client, err := ethclient.DialContext(ctx, endpoint) if err != nil { return fmt.Errorf("dialing %s: %w", endpoint, err) @@ -71,7 +73,7 @@ func ensureMinimumFunds(ctx context.Context, endpoint string, keys []*ecdsa.Priv return fmt.Errorf("creating distribution generator: %w", err) } tracker := tracker.NewCounter() - issuer := issuer.New(client, tracker, maxFundsAddress) + issuer := issuer.New(client, websocket, tracker, maxFundsAddress) agents := []*agent.Agent[*types.Transaction, common.Hash]{ agent.New(txTarget, generator, issuer, tracker), } diff --git a/tests/load/issuer/issuer.go b/tests/load/issuer/issuer.go index 9575a50a6975..892a806831fa 100644 --- a/tests/load/issuer/issuer.go +++ b/tests/load/issuer/issuer.go @@ -29,9 +29,10 @@ type Tracker interface { // It type Issuer struct { // Injected parameters - client EthClient - tracker Tracker - address common.Address + client EthClient + websocket bool + tracker Tracker + address common.Address // Internal state mutex sync.Mutex @@ -41,11 +42,12 @@ type Issuer struct { allIssued bool } -func New(client EthClient, tracker Tracker, address common.Address) *Issuer { +func New(client EthClient, websocket bool, tracker Tracker, address common.Address) *Issuer { return &Issuer{ - client: client, - tracker: tracker, - address: address, + client: client, + websocket: websocket, + tracker: tracker, + address: address, } } @@ -69,46 +71,10 @@ func (i *Issuer) IssueTx(ctx context.Context, tx *types.Transaction) error { } func (i *Issuer) Listen(ctx context.Context) error { - headNotifier := newHeadNotifier(i.client) - newHeadSignal, notifierErrCh := headNotifier.start(ctx) - defer headNotifier.stop() - - for { - blockNumber := (*big.Int)(nil) - nonce, err := i.client.NonceAt(ctx, i.address, blockNumber) - if err != nil { - if ctx.Err() != nil { - return nil - } - return fmt.Errorf("checking last block account nonce for address %s: %w", i.address, err) - } - - i.mutex.Lock() - confirmed := uint64(len(i.inFlightTxHashes)) - if nonce < i.lastIssuedNonce { // lagging behind last issued nonce - lag := i.lastIssuedNonce - nonce - confirmed -= lag - } - for index := range confirmed { - txHash := i.inFlightTxHashes[index] - i.tracker.ObserveConfirmed(txHash) - } - i.inFlightTxHashes = i.inFlightTxHashes[confirmed:] - finished := i.allIssued && len(i.inFlightTxHashes) == 0 - if finished { - i.mutex.Unlock() - return nil - } - i.mutex.Unlock() - - select { - case <-ctx.Done(): - return nil - case err := <-notifierErrCh: - return fmt.Errorf("new head notifier failed: %w", err) - case <-newHeadSignal: - } + if i.websocket { + return i.listenSub(ctx) } + return i.listenPoll(ctx) } func (i *Issuer) Stop() { diff --git a/tests/load/issuer/listenpoll.go b/tests/load/issuer/listenpoll.go new file mode 100644 index 000000000000..058c6ffa2717 --- /dev/null +++ b/tests/load/issuer/listenpoll.go @@ -0,0 +1,51 @@ +// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package issuer + +import ( + "context" + "fmt" + "math/big" + "time" +) + +func (i *Issuer) listenPoll(ctx context.Context) error { + ticker := time.NewTicker(time.Second) + defer ticker.Stop() + + for { + blockNumber := (*big.Int)(nil) + nonce, err := i.client.NonceAt(ctx, i.address, blockNumber) + if err != nil { + if ctx.Err() != nil { + return nil + } + return fmt.Errorf("checking last block account nonce for address %s: %w", i.address, err) + } + + i.mutex.Lock() + confirmed := uint64(len(i.inFlightTxHashes)) + if nonce < i.lastIssuedNonce { // lagging behind last issued nonce + lag := i.lastIssuedNonce - nonce + confirmed -= lag + } + for index := range confirmed { + txHash := i.inFlightTxHashes[index] + i.tracker.ObserveConfirmed(txHash) + } + i.inFlightTxHashes = i.inFlightTxHashes[confirmed:] + finished := i.allIssued && len(i.inFlightTxHashes) == 0 + if finished { + i.mutex.Unlock() + return nil + } + i.mutex.Unlock() + + select { + case <-ctx.Done(): + return nil + case <-ticker.C: + } + } +} diff --git a/tests/load/issuer/listensub.go b/tests/load/issuer/listensub.go new file mode 100644 index 000000000000..1ecf35135d1c --- /dev/null +++ b/tests/load/issuer/listensub.go @@ -0,0 +1,56 @@ +// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package issuer + +import ( + "context" + "fmt" + "math/big" +) + +func (i *Issuer) listenSub(ctx context.Context) error { + headNotifier := newHeadNotifier(i.client) + newHeadSignal, notifierErrCh, err := headNotifier.start(ctx) + if err != nil { + return fmt.Errorf("starting new head notifier: %w", err) + } + defer headNotifier.stop() + + for { + blockNumber := (*big.Int)(nil) + nonce, err := i.client.NonceAt(ctx, i.address, blockNumber) + if err != nil { + if ctx.Err() != nil { + return nil + } + return fmt.Errorf("checking last block account nonce for address %s: %w", i.address, err) + } + + i.mutex.Lock() + confirmed := uint64(len(i.inFlightTxHashes)) + if nonce < i.lastIssuedNonce { // lagging behind last issued nonce + lag := i.lastIssuedNonce - nonce + confirmed -= lag + } + for index := range confirmed { + txHash := i.inFlightTxHashes[index] + i.tracker.ObserveConfirmed(txHash) + } + i.inFlightTxHashes = i.inFlightTxHashes[confirmed:] + finished := i.allIssued && len(i.inFlightTxHashes) == 0 + if finished { + i.mutex.Unlock() + return nil + } + i.mutex.Unlock() + + select { + case <-ctx.Done(): + return nil + case err := <-notifierErrCh: + return fmt.Errorf("new head notifier failed: %w", err) + case <-newHeadSignal: + } + } +} diff --git a/tests/load/issuer/newhead.go b/tests/load/issuer/newhead.go index 806dac604cd5..e5fe7d38d1a0 100644 --- a/tests/load/issuer/newhead.go +++ b/tests/load/issuer/newhead.go @@ -6,10 +6,8 @@ package issuer import ( "context" "fmt" - "time" "github.com/ava-labs/libevm/core/types" - "github.com/ava-labs/libevm/log" ethereum "github.com/ava-labs/libevm" ) @@ -31,7 +29,7 @@ func newHeadNotifier(client NewHeadSubscriber) *headNotifier { } } -func (n *headNotifier) start(ctx context.Context) (newHead <-chan struct{}, runError <-chan error) { +func (n *headNotifier) start(ctx context.Context) (newHead <-chan struct{}, runError <-chan error, err error) { newHeadSignal := make(chan struct{}) listenStop := make(chan struct{}) @@ -43,43 +41,20 @@ func (n *headNotifier) start(ctx context.Context) (newHead <-chan struct{}, runE subscriptionCh := make(chan *types.Header) subscription, err := n.client.SubscribeNewHead(ctx, subscriptionCh) if err != nil { - log.Debug("failed to subscribe new heads, falling back to polling", "err", err) - go periodicNotify(listenStop, listenDone, ready, newHeadSignal) - <-ready - return newHeadSignal, nil // no possible error at runtime + return nil, nil, fmt.Errorf("subscribing to new head: %w", err) } go subscriptionChToSignal(listenStop, listenDone, ready, subscriptionCh, newHeadSignal) <-ready n.subscription = subscription - return newHeadSignal, n.makeRunErrCh() + return newHeadSignal, n.makeRunErrCh(), nil } func (n *headNotifier) stop() { - if n.subscription != nil { - n.subscription.Unsubscribe() - } + n.subscription.Unsubscribe() close(n.listenStop) <-n.listenDone } -// periodicNotify is the fallback if the connection is not a websocket. -func periodicNotify(listenStop <-chan struct{}, listenDone, ready chan<- struct{}, - newHeadSignal chan<- struct{}, -) { - defer close(listenDone) - ticker := time.NewTicker(time.Second) - close(ready) - for { - select { - case <-listenStop: - ticker.Stop() - return - case <-ticker.C: - newHeadSignal <- struct{}{} - } - } -} - func subscriptionChToSignal(listenStop <-chan struct{}, listenDone, ready chan<- struct{}, subCh <-chan *types.Header, newHeadSignal chan<- struct{}, ) { From c6030dba7aab227952a6a13e098d5437ecbc9210 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Mon, 28 Apr 2025 09:39:53 +0200 Subject: [PATCH 003/197] Always generate keys except for the prefunded key --- tests/load/execute.go | 35 +++++++++---------------------- tests/load/key.go | 49 ------------------------------------------- 2 files changed, 10 insertions(+), 74 deletions(-) delete mode 100644 tests/load/key.go diff --git a/tests/load/execute.go b/tests/load/execute.go index 2f7181a9c1db..6e49d6fe9fda 100644 --- a/tests/load/execute.go +++ b/tests/load/execute.go @@ -8,7 +8,6 @@ import ( "crypto/ecdsa" "fmt" "math/big" - "path/filepath" "strings" "time" @@ -27,10 +26,6 @@ import ( ethcrypto "github.com/ava-labs/libevm/crypto" ) -const ( - keyDir = "./keys" -) - type Config struct { Endpoints []string `json:"endpoints"` MaxFeeCap int64 `json:"max-fee-cap"` @@ -39,16 +34,12 @@ type Config struct { TxsPerAgent uint64 `json:"txs-per-agent"` } -func Execute(ctx context.Context, config Config) error { - keys, err := loadKeysFromDir(keyDir) - if err != nil { - return fmt.Errorf("loading keys: %w", err) - } - - keys, err = ensureKeysNumber(keys, config.Agents) +func Execute(ctx context.Context, preFundedKey *ecdsa.PrivateKey, config Config) error { + keys, err := generateKeys(config.Agents - 1) if err != nil { - return fmt.Errorf("ensuring keys number: %w", err) + return fmt.Errorf("generating keys: %w", err) } + keys = append(keys, preFundedKey) // Minimum to fund gas for all of the transactions for an address: minFundsPerAddr := new(big.Int).SetUint64(params.GWei * uint64(config.MaxFeeCap) * params.TxGas * config.TxsPerAgent) @@ -110,20 +101,14 @@ func Execute(ctx context.Context, config Config) error { } } -func ensureKeysNumber(keys []*ecdsa.PrivateKey, target uint) ([]*ecdsa.PrivateKey, error) { - for len(keys) < int(target) { - newKey, err := ethcrypto.GenerateKey() - if err != nil { - return nil, fmt.Errorf("generating key: %w", err) - } - - address := ethcrypto.PubkeyToAddress(newKey.PublicKey).Hex() - filePath := filepath.Join(keyDir, address) - err = ethcrypto.SaveECDSA(filePath, newKey) +func generateKeys(target uint) ([]*ecdsa.PrivateKey, error) { + keys := make([]*ecdsa.PrivateKey, target, target+1) + for i := range target { + key, err := ethcrypto.GenerateKey() if err != nil { - return nil, fmt.Errorf("saving key at index %d: %w", len(keys)-1, err) + return nil, fmt.Errorf("generating key at index %d: %w", i, err) } - keys = append(keys, newKey) + keys[i] = key } return keys, nil } diff --git a/tests/load/key.go b/tests/load/key.go deleted file mode 100644 index 04352ff83e1d..000000000000 --- a/tests/load/key.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package load - -import ( - "crypto/ecdsa" - "fmt" - "os" - "path/filepath" - - ethcrypto "github.com/ava-labs/libevm/crypto" -) - -func loadKeysFromDir(dir string) ([]*ecdsa.PrivateKey, error) { - _, err := os.Stat(dir) - if err != nil { - if !os.IsNotExist(err) { - return nil, err - } - if err := os.MkdirAll(dir, 0o755); err != nil { - return nil, err - } - return nil, nil - } - - var paths []string - err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { - if err != nil { - return err - } else if path == dir || info.IsDir() { - return nil - } - paths = append(paths, path) - return nil - }) - if err != nil { - return nil, fmt.Errorf("walking %s: %w", dir, err) - } - - keys := make([]*ecdsa.PrivateKey, len(paths)) - for i, path := range paths { - keys[i], err = ethcrypto.LoadECDSA(path) - if err != nil { - return nil, fmt.Errorf("loading private key from %s: %w", path, err) - } - } - return keys, nil -} From a560a9479a30541d2da8b35c8655401c29b69977 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Mon, 28 Apr 2025 09:44:21 +0200 Subject: [PATCH 004/197] Lower poll period from 1s to 50ms --- tests/load/issuer/listenpoll.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/load/issuer/listenpoll.go b/tests/load/issuer/listenpoll.go index 058c6ffa2717..164a39d37efa 100644 --- a/tests/load/issuer/listenpoll.go +++ b/tests/load/issuer/listenpoll.go @@ -11,7 +11,8 @@ import ( ) func (i *Issuer) listenPoll(ctx context.Context) error { - ticker := time.NewTicker(time.Second) + const period = 50 * time.Millisecond + ticker := time.NewTicker(period) defer ticker.Stop() for { From 7942c76d3805be84c27365910af56101e78074f8 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Mon, 28 Apr 2025 12:19:13 +0200 Subject: [PATCH 005/197] Abort fund distribution if no key needs it --- tests/load/funder.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/load/funder.go b/tests/load/funder.go index 96b9386d0fb4..3849ad404e68 100644 --- a/tests/load/funder.go +++ b/tests/load/funder.go @@ -59,6 +59,10 @@ func ensureMinimumFunds(ctx context.Context, endpoint string, keys []*ecdsa.Priv needFundsKeys[key] = diff } + if len(needFundsKeys) == 0 { + return nil + } + maxFundsKey := keys[len(keys)-1] maxFundsBalance := keyToBalance[maxFundsKey] if maxFundsBalance.Cmp(totalFundsRequired) < 0 { From f6e300c61f4c941a1667fdb617f6d6bcb7c43dab Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Mon, 28 Apr 2025 12:19:24 +0200 Subject: [PATCH 006/197] Ginkgo setup --- tests/fixture/tmpnet/utils.go | 18 +++++ tests/load/ginkgo_test.go | 126 ++++++++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+) create mode 100644 tests/load/ginkgo_test.go diff --git a/tests/fixture/tmpnet/utils.go b/tests/fixture/tmpnet/utils.go index e02fa9f618c3..becf8af9f9fa 100644 --- a/tests/fixture/tmpnet/utils.go +++ b/tests/fixture/tmpnet/utils.go @@ -9,6 +9,7 @@ import ( "errors" "fmt" "net" + "net/url" "os" "syscall" "time" @@ -72,6 +73,23 @@ func GetNodeURIs(nodes []*Node) []NodeURI { return uris } +// GetNodeWebsocketURIs returns a list of websocket URIs for the given nodes and +// blockchain ID, in the form "ws:///ext/bc//ws". +// Ephemeral and stopped nodes are ignored. +func GetNodeWebsocketURIs(nodes []*Node, blockchainID string) ([]string, error) { + nodeURIs := GetNodeURIs(nodes) + wsURIs := make([]string, len(nodeURIs)) + for i := range nodeURIs { + uri, err := url.Parse(nodeURIs[i].URI) + if err != nil { + return nil, fmt.Errorf("parsing uri: %w", err) + } + uri.Scheme = "ws" // use websocket to be able to stream events + wsURIs[i] = fmt.Sprintf("%s/ext/bc/%s/ws", uri, blockchainID) + } + return wsURIs, nil +} + // Marshal to json with default prefix and indent. func DefaultJSONMarshal(v interface{}) ([]byte, error) { return json.MarshalIndent(v, "", " ") diff --git a/tests/load/ginkgo_test.go b/tests/load/ginkgo_test.go new file mode 100644 index 000000000000..8dc6c7a6b05b --- /dev/null +++ b/tests/load/ginkgo_test.go @@ -0,0 +1,126 @@ +// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package load + +import ( + "context" + "crypto/ecdsa" + "fmt" + "os" + "os/signal" + "syscall" + "testing" + + "github.com/ava-labs/libevm/log" + "github.com/onsi/ginkgo/v2" + "github.com/stretchr/testify/require" + + "github.com/ava-labs/avalanchego/tests/fixture/e2e" + "github.com/ava-labs/avalanchego/tests/fixture/tmpnet" +) + +// Run this using the command: +// ./bin/ginkgo -v ./tests/load -- --avalanchego-path=$PWD/build/avalanchego +func TestLoad(t *testing.T) { + ginkgo.RunSpecs(t, "load tests") +} + +var flagVars *e2e.FlagVars + +func init() { + flagVars = e2e.RegisterFlagsWithDefaultOwner("avalanchego-load") +} + +var _ = ginkgo.SynchronizedBeforeSuite(func() []byte { + // Run only once in the first ginkgo process + + tc := e2e.NewTestContext() + nodes := tmpnet.NewNodesOrPanic(tmpnet.DefaultNodeCount) + network := &tmpnet.Network{ + Owner: "avalanchego-load-test", + Nodes: nodes, + } + + env := e2e.NewTestEnvironment( + tc, + flagVars, + network, + ) + + return env.Marshal() +}, func(envBytes []byte) { + // Run in every ginkgo process + + // Initialize the local test environment from the global state + e2e.InitSharedTestEnvironment(e2e.NewTestContext(), envBytes) +}) + +var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { + var privateNetwork *tmpnet.Network + + ginkgo.BeforeAll(func() { + tc := e2e.NewTestContext() + env := e2e.GetEnv(tc) + privateNetwork = tmpnet.NewDefaultNetwork("avalanchego-load-test") + privateNetwork.DefaultFlags = tmpnet.FlagsMap{} + publicNetwork := env.GetNetwork() + privateNetwork.DefaultFlags.SetDefaults(publicNetwork.DefaultFlags) + privateNetwork.Nodes = make([]*tmpnet.Node, tmpnet.DefaultNodeCount) + for i := range privateNetwork.Nodes { + node := tmpnet.NewNode() + err := node.EnsureKeys() + require.NoError(tc, err, "ensuring keys for node %s", node.NodeID) + privateNetwork.Nodes[i] = node + } + env.StartPrivateNetwork(privateNetwork) + }) + + ginkgo.It("C-Chain", func() { + nodes := privateNetwork.Nodes + preFundedKey := privateNetwork.PreFundedKeys[0].ToECDSA() + const blockchainID = "C" + endpoints, err := tmpnet.GetNodeWebsocketURIs(nodes, blockchainID) + require.NoError(ginkgo.GinkgoT(), err, "getting node websocket URIs") + config := Config{ + Endpoints: endpoints, + MaxFeeCap: 50, + MaxTipCap: 1, + Agents: 1, + TxsPerAgent: 100, + } + err = run(preFundedKey, config) + if err != nil { + log.Error(err.Error()) + os.Exit(1) + } + }) +}) + +func run(preFundedKey *ecdsa.PrivateKey, config Config) error { + signalCh := make(chan os.Signal, 1) + signal.Notify(signalCh, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT) + + logger := log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)) + log.SetDefault(logger) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + runError := make(chan error) + go func() { + runError <- Execute(ctx, preFundedKey, config) + }() + + select { + case signal := <-signalCh: + cancel() + <-runError + return fmt.Errorf("received OS signal %s", signal) + case err := <-runError: + if err != nil { + return fmt.Errorf("execution failed: %w", err) + } + return nil + } +} From a9348181da6ca10c1bb74a498a8189597f14ebd3 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Mon, 28 Apr 2025 13:01:36 +0200 Subject: [PATCH 007/197] Define client interfaces for poll and subscriber eth clients --- tests/load/issuer/issuer.go | 5 ++--- tests/load/issuer/listenpoll.go | 6 ++++++ tests/load/issuer/listensub.go | 7 +++++++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/tests/load/issuer/issuer.go b/tests/load/issuer/issuer.go index 892a806831fa..4fa577991767 100644 --- a/tests/load/issuer/issuer.go +++ b/tests/load/issuer/issuer.go @@ -6,7 +6,6 @@ package issuer import ( "context" "fmt" - "math/big" "sync" "github.com/ava-labs/libevm/common" @@ -14,9 +13,9 @@ import ( ) type EthClient interface { - NewHeadSubscriber SendTransaction(ctx context.Context, tx *types.Transaction) error - NonceAt(ctx context.Context, addr common.Address, blockNumber *big.Int) (uint64, error) + EthClientPoll + EthClientSubscriber } type Tracker interface { diff --git a/tests/load/issuer/listenpoll.go b/tests/load/issuer/listenpoll.go index 164a39d37efa..0f0e2d9e6cb4 100644 --- a/tests/load/issuer/listenpoll.go +++ b/tests/load/issuer/listenpoll.go @@ -8,8 +8,14 @@ import ( "fmt" "math/big" "time" + + "github.com/ava-labs/libevm/common" ) +type EthClientPoll interface { + NonceAt(ctx context.Context, addr common.Address, blockNumber *big.Int) (uint64, error) +} + func (i *Issuer) listenPoll(ctx context.Context) error { const period = 50 * time.Millisecond ticker := time.NewTicker(period) diff --git a/tests/load/issuer/listensub.go b/tests/load/issuer/listensub.go index 1ecf35135d1c..4aa2c7981ef1 100644 --- a/tests/load/issuer/listensub.go +++ b/tests/load/issuer/listensub.go @@ -7,8 +7,15 @@ import ( "context" "fmt" "math/big" + + "github.com/ava-labs/libevm/common" ) +type EthClientSubscriber interface { + NonceAt(ctx context.Context, addr common.Address, blockNumber *big.Int) (uint64, error) + NewHeadSubscriber +} + func (i *Issuer) listenSub(ctx context.Context) error { headNotifier := newHeadNotifier(i.client) newHeadSignal, notifierErrCh, err := headNotifier.start(ctx) From 8224d4eed42313bfa13ccb0aad85c70c81def52d Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Mon, 28 Apr 2025 13:30:11 +0200 Subject: [PATCH 008/197] Observe block times working for both polling and websocket --- tests/load/issuer/issuer.go | 6 +++ tests/load/issuer/listenpoll.go | 68 +++++++++++++++++++++++++++++++-- tests/load/issuer/listensub.go | 5 ++- tests/load/issuer/newhead.go | 14 +++---- tests/load/tracker/counter.go | 1 + tests/load/tracker/metrics.go | 10 ++++- tests/load/tracker/tracker.go | 28 ++++++++++++++ 7 files changed, 119 insertions(+), 13 deletions(-) diff --git a/tests/load/issuer/issuer.go b/tests/load/issuer/issuer.go index 4fa577991767..c202aa96d6d6 100644 --- a/tests/load/issuer/issuer.go +++ b/tests/load/issuer/issuer.go @@ -22,6 +22,12 @@ type Tracker interface { IssueStart(txHash common.Hash) IssueEnd(txHash common.Hash) ObserveConfirmed(txHash common.Hash) + // ObserveBlock observes a new block with the given number. + // It should be called when a new block is received. + // It should ignore the block if the number has already been observed. + // It may also ignore the first block observed when it comes to time, + // given the missing information on the time start for the first block. + ObserveBlock(number uint64) } // Issuer issues transactions to a node and waits for them to be accepted (or failed). diff --git a/tests/load/issuer/listenpoll.go b/tests/load/issuer/listenpoll.go index 0f0e2d9e6cb4..c7c9d167d194 100644 --- a/tests/load/issuer/listenpoll.go +++ b/tests/load/issuer/listenpoll.go @@ -13,6 +13,7 @@ import ( ) type EthClientPoll interface { + BlockNumber(ctx context.Context) (uint64, error) NonceAt(ctx context.Context, addr common.Address, blockNumber *big.Int) (uint64, error) } @@ -22,15 +23,16 @@ func (i *Issuer) listenPoll(ctx context.Context) error { defer ticker.Stop() for { - blockNumber := (*big.Int)(nil) - nonce, err := i.client.NonceAt(ctx, i.address, blockNumber) + blockNumber, nonce, err := pollParallel(ctx, i.client, i.address) if err != nil { if ctx.Err() != nil { return nil } - return fmt.Errorf("checking last block account nonce for address %s: %w", i.address, err) + return fmt.Errorf("polling node: %w", err) } + i.tracker.ObserveBlock(blockNumber) + i.mutex.Lock() confirmed := uint64(len(i.inFlightTxHashes)) if nonce < i.lastIssuedNonce { // lagging behind last issued nonce @@ -56,3 +58,63 @@ func (i *Issuer) listenPoll(ctx context.Context) error { } } } + +type blockNumResult struct { + blockNumber uint64 + err error +} + +type nonceResult struct { + nonce uint64 + err error +} + +func pollParallel(ctx context.Context, client EthClientPoll, address common.Address) ( + blockNumber, nonce uint64, err error, +) { + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + blockNumberCh := make(chan blockNumResult) + go func() { + number, err := client.BlockNumber(ctx) + if err != nil { + err = fmt.Errorf("getting block number: %w", err) + } + blockNumberCh <- blockNumResult{blockNumber: number, err: err} + }() + + nonceCh := make(chan nonceResult) + go func() { + nonceQueryBlock := (*big.Int)(nil) + nonce, err = client.NonceAt(ctx, address, nonceQueryBlock) + if err != nil { + err = fmt.Errorf("checking last block account nonce for address %s: %w", address, err) + } + nonceCh <- nonceResult{nonce: nonce, err: err} + }() + + const numGoroutines = 2 + for range numGoroutines { + select { + case <-blockNumberCh: + result := <-blockNumberCh + if err == nil && result.err != nil { + err = result.err + cancel() + continue + } + blockNumber = result.blockNumber + case <-nonceCh: + result := <-nonceCh + if err == nil && result.err != nil { + err = result.err + cancel() + continue + } + nonce = result.nonce + } + } + + return blockNumber, nonce, err +} diff --git a/tests/load/issuer/listensub.go b/tests/load/issuer/listensub.go index 4aa2c7981ef1..711f7561ff0b 100644 --- a/tests/load/issuer/listensub.go +++ b/tests/load/issuer/listensub.go @@ -18,7 +18,7 @@ type EthClientSubscriber interface { func (i *Issuer) listenSub(ctx context.Context) error { headNotifier := newHeadNotifier(i.client) - newHeadSignal, notifierErrCh, err := headNotifier.start(ctx) + newHeadCh, notifierErrCh, err := headNotifier.start(ctx) if err != nil { return fmt.Errorf("starting new head notifier: %w", err) } @@ -57,7 +57,8 @@ func (i *Issuer) listenSub(ctx context.Context) error { return nil case err := <-notifierErrCh: return fmt.Errorf("new head notifier failed: %w", err) - case <-newHeadSignal: + case blockNumber := <-newHeadCh: + i.tracker.ObserveBlock(blockNumber) } } } diff --git a/tests/load/issuer/newhead.go b/tests/load/issuer/newhead.go index e5fe7d38d1a0..6b4ce0f9e162 100644 --- a/tests/load/issuer/newhead.go +++ b/tests/load/issuer/newhead.go @@ -29,8 +29,8 @@ func newHeadNotifier(client NewHeadSubscriber) *headNotifier { } } -func (n *headNotifier) start(ctx context.Context) (newHead <-chan struct{}, runError <-chan error, err error) { - newHeadSignal := make(chan struct{}) +func (n *headNotifier) start(ctx context.Context) (newHead <-chan uint64, runError <-chan error, err error) { + newHeadCh := make(chan uint64) listenStop := make(chan struct{}) n.listenStop = listenStop @@ -43,10 +43,10 @@ func (n *headNotifier) start(ctx context.Context) (newHead <-chan struct{}, runE if err != nil { return nil, nil, fmt.Errorf("subscribing to new head: %w", err) } - go subscriptionChToSignal(listenStop, listenDone, ready, subscriptionCh, newHeadSignal) + go subscriptionChToSignal(listenStop, listenDone, ready, subscriptionCh, newHeadCh) <-ready n.subscription = subscription - return newHeadSignal, n.makeRunErrCh(), nil + return newHeadCh, n.makeRunErrCh(), nil } func (n *headNotifier) stop() { @@ -56,7 +56,7 @@ func (n *headNotifier) stop() { } func subscriptionChToSignal(listenStop <-chan struct{}, listenDone, ready chan<- struct{}, - subCh <-chan *types.Header, newHeadSignal chan<- struct{}, + subCh <-chan *types.Header, newHeadCh chan<- uint64, ) { defer close(listenDone) close(ready) @@ -64,8 +64,8 @@ func subscriptionChToSignal(listenStop <-chan struct{}, listenDone, ready chan<- select { case <-listenStop: return - case <-subCh: - newHeadSignal <- struct{}{} + case header := <-subCh: + newHeadCh <- header.Number.Uint64() } } } diff --git a/tests/load/tracker/counter.go b/tests/load/tracker/counter.go index 8bc572d240ce..864c4c3f77c8 100644 --- a/tests/load/tracker/counter.go +++ b/tests/load/tracker/counter.go @@ -22,5 +22,6 @@ func (*Counter) IssueStart(_ common.Hash) {} func (*Counter) IssueEnd(_ common.Hash) {} func (c *Counter) ObserveConfirmed(_ common.Hash) { c.confirmed++ } func (c *Counter) ObserveFailed(_ common.Hash) { c.failed++ } +func (*Counter) ObserveBlock(_ uint64) {} func (c *Counter) GetObservedConfirmed() uint64 { return c.confirmed } func (c *Counter) GetObservedFailed() uint64 { return c.failed } diff --git a/tests/load/tracker/metrics.go b/tests/load/tracker/metrics.go index e6d09e77d87c..81c680d0fc36 100644 --- a/tests/load/tracker/metrics.go +++ b/tests/load/tracker/metrics.go @@ -25,6 +25,8 @@ type metrics struct { // ConfirmationTxTimes is the summary of the quantiles of individual confirmation tx times. // Failed transactions do not show in this metric. ConfirmationTxTimes prometheus.Summary + // BlockTimes is the summary of the quantiles of individual block times. + BlockTimes prometheus.Summary registry PrometheusRegistry } @@ -61,9 +63,14 @@ func newMetrics(registry PrometheusRegistry) *metrics { Help: "Individual Tx Confirmation Times for a Load Test", Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, }) + blockTimes := prometheus.NewSummary(prometheus.SummaryOpts{ + Name: "block_time", + Help: "Individual block times for a load test", + Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, + }) registry.MustRegister(confirmed, failed, inFlightIssuances, inFlightTxs, - issuanceTxTimes, confirmationTxTimes) + issuanceTxTimes, confirmationTxTimes, blockTimes) return &metrics{ registry: registry, @@ -72,6 +79,7 @@ func newMetrics(registry PrometheusRegistry) *metrics { InFlightTxs: inFlightTxs, IssuanceTxTimes: issuanceTxTimes, ConfirmationTxTimes: confirmationTxTimes, + BlockTimes: blockTimes, } } diff --git a/tests/load/tracker/tracker.go b/tests/load/tracker/tracker.go index 1ecfbf68689f..afc37800d85a 100644 --- a/tests/load/tracker/tracker.go +++ b/tests/load/tracker/tracker.go @@ -13,6 +13,8 @@ import ( type Tracker struct { timeNow func() time.Time txHashToLastTime map[common.Hash]time.Time + lastBlockNumber uint64 + lastBlockTime time.Time metrics *metrics stats struct { @@ -78,6 +80,32 @@ func (t *Tracker) ObserveFailed(txHash common.Hash) { t.statsMutex.Unlock() } +// ObserveBlock records a new block with the given number. +// Note it ignores the first block, to avoid misleading metrics due to the +// absence of information on when the previous block was created. +func (t *Tracker) ObserveBlock(number uint64) { + now := t.timeNow() + if t.lastBlockNumber == 0 { + t.lastBlockTime = now + t.lastBlockNumber = number + return + } + + if number == t.lastBlockNumber { + // No new block. This can happen when polling the node periodically. + return + } + + // Usually numberDiff should be 1, but it may happen it is bigger, especially + // when polling the node periodically instead of using a subscription. + numberDiff := number - t.lastBlockNumber + timeDiff := now.Sub(t.lastBlockTime) + secondsPerBlock := timeDiff.Seconds() / float64(numberDiff) + t.metrics.BlockTimes.Observe(secondsPerBlock) + t.lastBlockTime = now + t.lastBlockNumber = number +} + // GetObservedConfirmed returns the number of transactions that the tracker has // confirmed were accepted. func (t *Tracker) GetObservedConfirmed() uint64 { From 57ad33bb79bc785298edc2b9ce95eaaec6a8a1c4 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Mon, 28 Apr 2025 13:33:21 +0200 Subject: [PATCH 009/197] Use ginkgo context --- tests/load/ginkgo_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/load/ginkgo_test.go b/tests/load/ginkgo_test.go index 8dc6c7a6b05b..74557e80b210 100644 --- a/tests/load/ginkgo_test.go +++ b/tests/load/ginkgo_test.go @@ -76,7 +76,7 @@ var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { env.StartPrivateNetwork(privateNetwork) }) - ginkgo.It("C-Chain", func() { + ginkgo.It("C-Chain", func(ctx context.Context) { nodes := privateNetwork.Nodes preFundedKey := privateNetwork.PreFundedKeys[0].ToECDSA() const blockchainID = "C" @@ -89,7 +89,7 @@ var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { Agents: 1, TxsPerAgent: 100, } - err = run(preFundedKey, config) + err = run(ctx, preFundedKey, config) if err != nil { log.Error(err.Error()) os.Exit(1) @@ -97,14 +97,14 @@ var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { }) }) -func run(preFundedKey *ecdsa.PrivateKey, config Config) error { +func run(ctx context.Context, preFundedKey *ecdsa.PrivateKey, config Config) error { signalCh := make(chan os.Signal, 1) signal.Notify(signalCh, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT) logger := log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)) log.SetDefault(logger) - ctx, cancel := context.WithCancel(context.Background()) + ctx, cancel := context.WithCancel(ctx) defer cancel() runError := make(chan error) From 29502bdef9963b97a8de71ec259dace4cd248447 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Mon, 28 Apr 2025 13:40:04 +0200 Subject: [PATCH 010/197] Fail ginkgo properly using `ginkgo.GinkgoT().Error` --- tests/load/ginkgo_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/load/ginkgo_test.go b/tests/load/ginkgo_test.go index 74557e80b210..e4a841405653 100644 --- a/tests/load/ginkgo_test.go +++ b/tests/load/ginkgo_test.go @@ -91,8 +91,7 @@ var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { } err = run(ctx, preFundedKey, config) if err != nil { - log.Error(err.Error()) - os.Exit(1) + ginkgo.GinkgoT().Error(err) } }) }) From a213610df4f269de1b2da00c2a05b35785661a09 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Mon, 28 Apr 2025 13:45:26 +0200 Subject: [PATCH 011/197] Rely on ginkgo to handle OS signals --- tests/load/ginkgo_test.go | 26 +------------------------- 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/tests/load/ginkgo_test.go b/tests/load/ginkgo_test.go index e4a841405653..b0d820015340 100644 --- a/tests/load/ginkgo_test.go +++ b/tests/load/ginkgo_test.go @@ -6,10 +6,7 @@ package load import ( "context" "crypto/ecdsa" - "fmt" "os" - "os/signal" - "syscall" "testing" "github.com/ava-labs/libevm/log" @@ -97,29 +94,8 @@ var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { }) func run(ctx context.Context, preFundedKey *ecdsa.PrivateKey, config Config) error { - signalCh := make(chan os.Signal, 1) - signal.Notify(signalCh, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT) - logger := log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)) log.SetDefault(logger) - ctx, cancel := context.WithCancel(ctx) - defer cancel() - - runError := make(chan error) - go func() { - runError <- Execute(ctx, preFundedKey, config) - }() - - select { - case signal := <-signalCh: - cancel() - <-runError - return fmt.Errorf("received OS signal %s", signal) - case err := <-runError: - if err != nil { - return fmt.Errorf("execution failed: %w", err) - } - return nil - } + return Execute(ctx, preFundedKey, config) } From 2a859a22d642a3b969cd4d950626ab03076ab368 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Mon, 28 Apr 2025 13:46:31 +0200 Subject: [PATCH 012/197] Simplify unneeded run function --- tests/load/execute.go | 5 +++++ tests/load/ginkgo_test.go | 12 +----------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/tests/load/execute.go b/tests/load/execute.go index 6e49d6fe9fda..a401d24956e8 100644 --- a/tests/load/execute.go +++ b/tests/load/execute.go @@ -8,6 +8,7 @@ import ( "crypto/ecdsa" "fmt" "math/big" + "os" "strings" "time" @@ -15,6 +16,7 @@ import ( "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/ethclient" + "github.com/ava-labs/libevm/log" "github.com/prometheus/client_golang/prometheus" "github.com/ava-labs/avalanchego/tests/load/agent" @@ -35,6 +37,9 @@ type Config struct { } func Execute(ctx context.Context, preFundedKey *ecdsa.PrivateKey, config Config) error { + logger := log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)) + log.SetDefault(logger) + keys, err := generateKeys(config.Agents - 1) if err != nil { return fmt.Errorf("generating keys: %w", err) diff --git a/tests/load/ginkgo_test.go b/tests/load/ginkgo_test.go index b0d820015340..a2db82f86388 100644 --- a/tests/load/ginkgo_test.go +++ b/tests/load/ginkgo_test.go @@ -5,11 +5,8 @@ package load import ( "context" - "crypto/ecdsa" - "os" "testing" - "github.com/ava-labs/libevm/log" "github.com/onsi/ginkgo/v2" "github.com/stretchr/testify/require" @@ -86,16 +83,9 @@ var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { Agents: 1, TxsPerAgent: 100, } - err = run(ctx, preFundedKey, config) + err = Execute(ctx, preFundedKey, config) if err != nil { ginkgo.GinkgoT().Error(err) } }) }) - -func run(ctx context.Context, preFundedKey *ecdsa.PrivateKey, config Config) error { - logger := log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)) - log.SetDefault(logger) - - return Execute(ctx, preFundedKey, config) -} From 2be38c1efd79fa93f77bf9d41853c48fa8816c37 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Mon, 28 Apr 2025 13:47:10 +0200 Subject: [PATCH 013/197] Remove unneeded dependencies.go file --- tests/load/dependencies.go | 49 -------------------------------------- 1 file changed, 49 deletions(-) delete mode 100644 tests/load/dependencies.go diff --git a/tests/load/dependencies.go b/tests/load/dependencies.go deleted file mode 100644 index ce5f2bd01abc..000000000000 --- a/tests/load/dependencies.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package load - -import "context" - -type TxGenerator[T comparable] interface { - // GenerateTx returns a valid transaction. - GenerateTx() (T, error) -} - -type Issuer[T comparable] interface { - // Listen for the final status of transactions and notify the tracker - // Listen stops if the context is done, an error occurs, or if the issuer - // has sent all their transactions. - // Listen MUST return a nil error if the context is canceled. - Listen(ctx context.Context) error - - // Stop notifies the issuer that no further transactions will be issued. - // If a transaction is issued after Stop has been called, the issuer should error. - Stop() - - // Issue sends a tx to the network, and informs the tracker that its sent - // said transaction. - IssueTx(ctx context.Context, tx T) error -} - -// Tracker keeps track of the status of transactions. -// This must be thread-safe, so it can be called in parallel by the issuer(s) or orchestrator. -type Tracker[T comparable] interface { - // Issue records a transaction that was submitted, but whose final status is - // not yet known. - Issue(T) - // ObserveConfirmed records a transaction that was confirmed. - ObserveConfirmed(T) - // ObserveFailed records a transaction that failed (e.g. expired) - ObserveFailed(T) - - // GetObservedIssued returns the number of transactions that the tracker has - // confirmed were issued. - GetObservedIssued() uint64 - // GetObservedConfirmed returns the number of transactions that the tracker has - // confirmed were accepted. - GetObservedConfirmed() uint64 - // GetObservedFailed returns the number of transactions that the tracker has - // confirmed failed. - GetObservedFailed() uint64 -} From 8701f175e6d38ced052c4dc32cd5fe40bdb99bc9 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Mon, 28 Apr 2025 13:50:06 +0200 Subject: [PATCH 014/197] Unexport symbols --- tests/load/execute.go | 28 ++++++++++++++-------------- tests/load/ginkgo_test.go | 14 +++++++------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/tests/load/execute.go b/tests/load/execute.go index a401d24956e8..7d3e06cad0f0 100644 --- a/tests/load/execute.go +++ b/tests/load/execute.go @@ -28,27 +28,27 @@ import ( ethcrypto "github.com/ava-labs/libevm/crypto" ) -type Config struct { - Endpoints []string `json:"endpoints"` - MaxFeeCap int64 `json:"max-fee-cap"` - MaxTipCap int64 `json:"max-tip-cap"` - Agents uint `json:"agents"` - TxsPerAgent uint64 `json:"txs-per-agent"` +type config struct { + endpoints []string + maxFeeCap int64 + maxTipCap int64 + agents uint + txsPerAgent uint64 } -func Execute(ctx context.Context, preFundedKey *ecdsa.PrivateKey, config Config) error { +func execute(ctx context.Context, preFundedKey *ecdsa.PrivateKey, config config) error { logger := log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)) log.SetDefault(logger) - keys, err := generateKeys(config.Agents - 1) + keys, err := generateKeys(config.agents - 1) if err != nil { return fmt.Errorf("generating keys: %w", err) } keys = append(keys, preFundedKey) // Minimum to fund gas for all of the transactions for an address: - minFundsPerAddr := new(big.Int).SetUint64(params.GWei * uint64(config.MaxFeeCap) * params.TxGas * config.TxsPerAgent) - err = ensureMinimumFunds(ctx, config.Endpoints[0], keys, minFundsPerAddr) + minFundsPerAddr := new(big.Int).SetUint64(params.GWei * uint64(config.maxFeeCap) * params.TxGas * config.txsPerAgent) + err = ensureMinimumFunds(ctx, config.endpoints[0], keys, minFundsPerAddr) if err != nil { return fmt.Errorf("ensuring minimum funds: %w", err) } @@ -57,22 +57,22 @@ func Execute(ctx context.Context, preFundedKey *ecdsa.PrivateKey, config Config) metricsServer := tracker.NewMetricsServer("127.0.0.1:8082", registry) tracker := tracker.New(registry) - agents := make([]*agent.Agent[*types.Transaction, common.Hash], config.Agents) + agents := make([]*agent.Agent[*types.Transaction, common.Hash], config.agents) for i := range agents { - endpoint := config.Endpoints[i%len(config.Endpoints)] + endpoint := config.endpoints[i%len(config.endpoints)] websocket := strings.HasPrefix(endpoint, "ws://") || strings.HasPrefix(endpoint, "wss://") client, err := ethclient.DialContext(ctx, endpoint) if err != nil { return fmt.Errorf("dialing %s: %w", endpoint, err) } generator, err := generator.NewSelf(ctx, client, - big.NewInt(config.MaxTipCap), big.NewInt(config.MaxFeeCap), keys[i]) + big.NewInt(config.maxTipCap), big.NewInt(config.maxFeeCap), keys[i]) if err != nil { return fmt.Errorf("creating generator: %w", err) } address := ethcrypto.PubkeyToAddress(keys[i].PublicKey) issuer := issuer.New(client, websocket, tracker, address) - agents[i] = agent.New[*types.Transaction, common.Hash](config.TxsPerAgent, generator, issuer, tracker) + agents[i] = agent.New[*types.Transaction, common.Hash](config.txsPerAgent, generator, issuer, tracker) } metricsErrCh, err := metricsServer.Start() diff --git a/tests/load/ginkgo_test.go b/tests/load/ginkgo_test.go index a2db82f86388..1a16ed0260d2 100644 --- a/tests/load/ginkgo_test.go +++ b/tests/load/ginkgo_test.go @@ -76,14 +76,14 @@ var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { const blockchainID = "C" endpoints, err := tmpnet.GetNodeWebsocketURIs(nodes, blockchainID) require.NoError(ginkgo.GinkgoT(), err, "getting node websocket URIs") - config := Config{ - Endpoints: endpoints, - MaxFeeCap: 50, - MaxTipCap: 1, - Agents: 1, - TxsPerAgent: 100, + config := config{ + endpoints: endpoints, + maxFeeCap: 50, + maxTipCap: 1, + agents: 1, + txsPerAgent: 100, } - err = Execute(ctx, preFundedKey, config) + err = execute(ctx, preFundedKey, config) if err != nil { ginkgo.GinkgoT().Error(err) } From e5f2f79a6b567a06671dd047abe3978919f1e3f0 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Mon, 28 Apr 2025 14:38:14 +0200 Subject: [PATCH 015/197] Use all prefunded keys instead of only one --- tests/load/execute.go | 22 ++++++++++++++-------- tests/load/ginkgo_test.go | 3 +-- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/tests/load/execute.go b/tests/load/execute.go index 7d3e06cad0f0..bc9a95e98a4c 100644 --- a/tests/load/execute.go +++ b/tests/load/execute.go @@ -24,6 +24,7 @@ import ( "github.com/ava-labs/avalanchego/tests/load/issuer" "github.com/ava-labs/avalanchego/tests/load/orchestrate" "github.com/ava-labs/avalanchego/tests/load/tracker" + "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" ethcrypto "github.com/ava-labs/libevm/crypto" ) @@ -36,15 +37,14 @@ type config struct { txsPerAgent uint64 } -func execute(ctx context.Context, preFundedKey *ecdsa.PrivateKey, config config) error { +func execute(ctx context.Context, preFundedKeys []*secp256k1.PrivateKey, config config) error { logger := log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)) log.SetDefault(logger) - keys, err := generateKeys(config.agents - 1) + keys, err := fixKeysCount(preFundedKeys, int(config.agents)) if err != nil { - return fmt.Errorf("generating keys: %w", err) + return fmt.Errorf("fixing keys count: %w", err) } - keys = append(keys, preFundedKey) // Minimum to fund gas for all of the transactions for an address: minFundsPerAddr := new(big.Int).SetUint64(params.GWei * uint64(config.maxFeeCap) * params.TxGas * config.txsPerAgent) @@ -106,14 +106,20 @@ func execute(ctx context.Context, preFundedKey *ecdsa.PrivateKey, config config) } } -func generateKeys(target uint) ([]*ecdsa.PrivateKey, error) { - keys := make([]*ecdsa.PrivateKey, target, target+1) - for i := range target { +func fixKeysCount(preFundedKeys []*secp256k1.PrivateKey, target int) ([]*ecdsa.PrivateKey, error) { + keys := make([]*ecdsa.PrivateKey, 0, target) + for i := 0; i < min(target, len(preFundedKeys)); i++ { + keys = append(keys, preFundedKeys[i].ToECDSA()) + } + if len(keys) == target { + return keys, nil + } + for i := len(keys); i < target; i++ { key, err := ethcrypto.GenerateKey() if err != nil { return nil, fmt.Errorf("generating key at index %d: %w", i, err) } - keys[i] = key + keys = append(keys, key) } return keys, nil } diff --git a/tests/load/ginkgo_test.go b/tests/load/ginkgo_test.go index 1a16ed0260d2..6716aaba9719 100644 --- a/tests/load/ginkgo_test.go +++ b/tests/load/ginkgo_test.go @@ -72,7 +72,6 @@ var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { ginkgo.It("C-Chain", func(ctx context.Context) { nodes := privateNetwork.Nodes - preFundedKey := privateNetwork.PreFundedKeys[0].ToECDSA() const blockchainID = "C" endpoints, err := tmpnet.GetNodeWebsocketURIs(nodes, blockchainID) require.NoError(ginkgo.GinkgoT(), err, "getting node websocket URIs") @@ -83,7 +82,7 @@ var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { agents: 1, txsPerAgent: 100, } - err = execute(ctx, preFundedKey, config) + err = execute(ctx, privateNetwork.PreFundedKeys, config) if err != nil { ginkgo.GinkgoT().Error(err) } From ee6132efba47c617b2ef49b79e9373a870df62ca Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Mon, 28 Apr 2025 15:06:57 +0200 Subject: [PATCH 016/197] Thread safe tracker --- tests/load/tracker/tracker.go | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/tests/load/tracker/tracker.go b/tests/load/tracker/tracker.go index afc37800d85a..c484b08468d3 100644 --- a/tests/load/tracker/tracker.go +++ b/tests/load/tracker/tracker.go @@ -21,7 +21,7 @@ type Tracker struct { confirmed uint64 failed uint64 } - statsMutex sync.RWMutex + mutex sync.Mutex } // New creates a new Tracker instance. @@ -37,6 +37,9 @@ func New(registry PrometheusRegistry) *Tracker { // IssueStart records a transaction that is being issued. func (t *Tracker) IssueStart(txHash common.Hash) { + t.mutex.Lock() + defer t.mutex.Unlock() + t.metrics.InFlightIssuances.Inc() t.metrics.InFlightTxs.Inc() t.txHashToLastTime[txHash] = t.timeNow() @@ -45,8 +48,10 @@ func (t *Tracker) IssueStart(txHash common.Hash) { // IssueEnd records a transaction that was issued, but whose final status is // not yet known. func (t *Tracker) IssueEnd(txHash common.Hash) { - t.metrics.InFlightIssuances.Dec() + t.mutex.Lock() + defer t.mutex.Unlock() + t.metrics.InFlightIssuances.Dec() start := t.txHashToLastTime[txHash] now := t.timeNow() diff := now.Sub(start) @@ -56,6 +61,9 @@ func (t *Tracker) IssueEnd(txHash common.Hash) { // ObserveConfirmed records a transaction that was confirmed. func (t *Tracker) ObserveConfirmed(txHash common.Hash) { + t.mutex.Lock() + defer t.mutex.Unlock() + t.metrics.InFlightTxs.Dec() t.metrics.Confirmed.Inc() issuedTime := t.txHashToLastTime[txHash] @@ -63,27 +71,27 @@ func (t *Tracker) ObserveConfirmed(txHash common.Hash) { diff := now.Sub(issuedTime) t.metrics.ConfirmationTxTimes.Observe(diff.Seconds()) delete(t.txHashToLastTime, txHash) - - t.statsMutex.Lock() t.stats.confirmed++ - t.statsMutex.Unlock() } // ObserveFailed records a transaction that failed (e.g. expired) func (t *Tracker) ObserveFailed(txHash common.Hash) { + t.mutex.Lock() + defer t.mutex.Unlock() + t.metrics.InFlightTxs.Dec() t.metrics.Failed.Inc() delete(t.txHashToLastTime, txHash) - - t.statsMutex.Lock() t.stats.failed++ - t.statsMutex.Unlock() } // ObserveBlock records a new block with the given number. // Note it ignores the first block, to avoid misleading metrics due to the // absence of information on when the previous block was created. func (t *Tracker) ObserveBlock(number uint64) { + t.mutex.Lock() + defer t.mutex.Unlock() + now := t.timeNow() if t.lastBlockNumber == 0 { t.lastBlockTime = now @@ -109,15 +117,15 @@ func (t *Tracker) ObserveBlock(number uint64) { // GetObservedConfirmed returns the number of transactions that the tracker has // confirmed were accepted. func (t *Tracker) GetObservedConfirmed() uint64 { - t.statsMutex.RLock() - defer t.statsMutex.RUnlock() + t.mutex.Lock() + defer t.mutex.Unlock() return t.stats.confirmed } // GetObservedFailed returns the number of transactions that the tracker has // confirmed failed. func (t *Tracker) GetObservedFailed() uint64 { - t.statsMutex.RLock() - defer t.statsMutex.RUnlock() + t.mutex.Lock() + defer t.mutex.Unlock() return t.stats.failed } From 35450dc4dd2e567f5933155d064ec147c44b124a Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Mon, 28 Apr 2025 16:49:18 +0200 Subject: [PATCH 017/197] CI setup --- .github/workflows/ci.yml | 18 ++++++++++++++++++ Taskfile.yml | 6 ++++++ 2 files changed, 24 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index edb582bddc49..106bd5829f44 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -205,3 +205,21 @@ jobs: - name: Run e2e tests shell: bash run: nix develop --command ./scripts/run_task.sh test-bootstrap-monitor-e2e + e2e_load: + name: e2e load tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: ./.github/actions/setup-go-for-project + - uses: ./.github/actions/install-nix + - uses: ./.github/actions/run-monitored-tmpnet-cmd + with: + run: ./scripts/run_task.sh test-e2e-ci + artifact_prefix: e2e-load + filter_by_owner: avalanchego-e2e + prometheus_username: ${{ secrets.PROMETHEUS_ID || '' }} + prometheus_password: ${{ secrets.PROMETHEUS_PASSWORD || '' }} + loki_username: ${{ secrets.LOKI_ID || '' }} + loki_password: ${{ secrets.LOKI_PASSWORD || '' }} diff --git a/Taskfile.yml b/Taskfile.yml index fa6d64c18064..d294cf75268c 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -173,6 +173,12 @@ tasks: - task: build-xsvm - cmd: bash -x ./scripts/tests.e2e.existing.sh {{.CLI_ARGS}} + test-e2e-load: + desc: Runs e2e load tests + cmds: + - task: build + - cmd: ./bin/ginkgo -v ./tests/load -- --avalanchego-path=$PWD/build/avalanchego + # To use a different fuzz time, run `task test-fuzz FUZZTIME=[value in seconds]`. # A value of `-1` will run until it encounters a failing output. From 5ea102a1cd6f1a5e43e212161a2c2537dbc3de52 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Tue, 29 Apr 2025 10:37:23 +0200 Subject: [PATCH 018/197] Split issuer and listener --- tests/load/agent/agent.go | 3 + tests/load/agent/dependencies.go | 19 +++--- tests/load/execute.go | 6 +- tests/load/funder.go | 6 +- tests/load/issuer/issuer.go | 56 ++++------------- tests/load/listen/listener.go | 68 +++++++++++++++++++++ tests/load/{issuer => listen}/listenpoll.go | 28 ++++----- tests/load/{issuer => listen}/listensub.go | 32 +++++----- tests/load/{issuer => listen}/newhead.go | 2 +- tests/load/orchestrate/burst.go | 8 +-- 10 files changed, 134 insertions(+), 94 deletions(-) create mode 100644 tests/load/listen/listener.go rename tests/load/{issuer => listen}/listenpoll.go (78%) rename tests/load/{issuer => listen}/listensub.go (59%) rename tests/load/{issuer => listen}/newhead.go (99%) diff --git a/tests/load/agent/agent.go b/tests/load/agent/agent.go index 7be4cbcfa09d..490cb4b1b13d 100644 --- a/tests/load/agent/agent.go +++ b/tests/load/agent/agent.go @@ -7,6 +7,7 @@ type Agent[T, U comparable] struct { TxTarget uint64 Generator TxGenerator[T] Issuer Issuer[T] + Listener Listener[T] Tracker Tracker[U] } @@ -14,12 +15,14 @@ func New[T, U comparable]( txTarget uint64, generator TxGenerator[T], issuer Issuer[T], + listener Listener[T], tracker Tracker[U], ) *Agent[T, U] { return &Agent[T, U]{ TxTarget: txTarget, Generator: generator, Issuer: issuer, + Listener: listener, Tracker: tracker, } } diff --git a/tests/load/agent/dependencies.go b/tests/load/agent/dependencies.go index 63a0e1d8190e..e58723172f74 100644 --- a/tests/load/agent/dependencies.go +++ b/tests/load/agent/dependencies.go @@ -11,21 +11,20 @@ type TxGenerator[T comparable] interface { } type Issuer[T comparable] interface { - // Listen for the final status of transactions and notify the tracker - // Listen stops if the context is done, an error occurs, or if the issuer - // has sent all their transactions. - // Listen MUST return a nil error if the context is canceled. - Listen(ctx context.Context) error - - // Stop notifies the issuer that no further transactions will be issued. - // If a transaction is issued after Stop has been called, the issuer should error. - Stop() - // Issue sends a tx to the network, and informs the tracker that its sent // said transaction. IssueTx(ctx context.Context, tx T) error } +type Listener[T comparable] interface { + // Listen listens for transaction confirmations from a node, as well as + // new blocks. + Listen(ctx context.Context) error + // RegisterIssued registers a transaction that was issued, so the listener + // is aware it should track it. + RegisterIssued(T) +} + // Tracker keeps track of the status of transactions. // This must be thread-safe, so it can be called in parallel by the issuer(s) or orchestrator. type Tracker[T comparable] interface { diff --git a/tests/load/execute.go b/tests/load/execute.go index bc9a95e98a4c..bbefc4b2b80f 100644 --- a/tests/load/execute.go +++ b/tests/load/execute.go @@ -22,6 +22,7 @@ import ( "github.com/ava-labs/avalanchego/tests/load/agent" "github.com/ava-labs/avalanchego/tests/load/generator" "github.com/ava-labs/avalanchego/tests/load/issuer" + "github.com/ava-labs/avalanchego/tests/load/listen" "github.com/ava-labs/avalanchego/tests/load/orchestrate" "github.com/ava-labs/avalanchego/tests/load/tracker" "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" @@ -71,8 +72,9 @@ func execute(ctx context.Context, preFundedKeys []*secp256k1.PrivateKey, config return fmt.Errorf("creating generator: %w", err) } address := ethcrypto.PubkeyToAddress(keys[i].PublicKey) - issuer := issuer.New(client, websocket, tracker, address) - agents[i] = agent.New[*types.Transaction, common.Hash](config.txsPerAgent, generator, issuer, tracker) + issuer := issuer.New(client, tracker, address) + listener := listen.New(client, websocket, tracker, config.txsPerAgent, address) + agents[i] = agent.New[*types.Transaction, common.Hash](config.txsPerAgent, generator, issuer, listener, tracker) } metricsErrCh, err := metricsServer.Start() diff --git a/tests/load/funder.go b/tests/load/funder.go index 3849ad404e68..2c392f5688a5 100644 --- a/tests/load/funder.go +++ b/tests/load/funder.go @@ -21,6 +21,7 @@ import ( "github.com/ava-labs/avalanchego/tests/load/agent" "github.com/ava-labs/avalanchego/tests/load/generator" "github.com/ava-labs/avalanchego/tests/load/issuer" + "github.com/ava-labs/avalanchego/tests/load/listen" "github.com/ava-labs/avalanchego/tests/load/orchestrate" "github.com/ava-labs/avalanchego/tests/load/tracker" @@ -77,9 +78,10 @@ func ensureMinimumFunds(ctx context.Context, endpoint string, keys []*ecdsa.Priv return fmt.Errorf("creating distribution generator: %w", err) } tracker := tracker.NewCounter() - issuer := issuer.New(client, websocket, tracker, maxFundsAddress) + issuer := issuer.New(client, tracker, maxFundsAddress) + listener := listen.New(client, websocket, tracker, txTarget, maxFundsAddress) agents := []*agent.Agent[*types.Transaction, common.Hash]{ - agent.New(txTarget, generator, issuer, tracker), + agent.New(txTarget, generator, issuer, listener, tracker), } orchestrator := orchestrate.NewBurstOrchestrator(agents, time.Second) diff --git a/tests/load/issuer/issuer.go b/tests/load/issuer/issuer.go index c202aa96d6d6..ad76cf99bde8 100644 --- a/tests/load/issuer/issuer.go +++ b/tests/load/issuer/issuer.go @@ -6,7 +6,6 @@ package issuer import ( "context" "fmt" - "sync" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/types" @@ -14,54 +13,36 @@ import ( type EthClient interface { SendTransaction(ctx context.Context, tx *types.Transaction) error - EthClientPoll - EthClientSubscriber } type Tracker interface { IssueStart(txHash common.Hash) IssueEnd(txHash common.Hash) - ObserveConfirmed(txHash common.Hash) - // ObserveBlock observes a new block with the given number. - // It should be called when a new block is received. - // It should ignore the block if the number has already been observed. - // It may also ignore the first block observed when it comes to time, - // given the missing information on the time start for the first block. - ObserveBlock(number uint64) } -// Issuer issues transactions to a node and waits for them to be accepted (or failed). -// It +// Issuer issues transactions to a node. type Issuer struct { // Injected parameters - client EthClient - websocket bool - tracker Tracker - address common.Address + client EthClient + tracker Tracker + address common.Address - // Internal state - mutex sync.Mutex - issuedTxs uint64 - lastIssuedNonce uint64 - inFlightTxHashes []common.Hash - allIssued bool + // State + lastIssuedNonce uint64 // for programming assumptions checks only } -func New(client EthClient, websocket bool, tracker Tracker, address common.Address) *Issuer { +func New(client EthClient, tracker Tracker, address common.Address) *Issuer { return &Issuer{ - client: client, - websocket: websocket, - tracker: tracker, - address: address, + client: client, + tracker: tracker, + address: address, } } func (i *Issuer) IssueTx(ctx context.Context, tx *types.Transaction) error { - i.mutex.Lock() - defer i.mutex.Unlock() - txHash, txNonce := tx.Hash(), tx.Nonce() if txNonce > 0 && txNonce != i.lastIssuedNonce+1 { + // the listener relies on this being true return fmt.Errorf("transaction nonce %d is not equal to the last issued nonce %d plus one", txNonce, i.lastIssuedNonce) } i.tracker.IssueStart(txHash) @@ -69,21 +50,6 @@ func (i *Issuer) IssueTx(ctx context.Context, tx *types.Transaction) error { return err } i.tracker.IssueEnd(txHash) - i.inFlightTxHashes = append(i.inFlightTxHashes, txHash) - i.issuedTxs++ i.lastIssuedNonce = txNonce return nil } - -func (i *Issuer) Listen(ctx context.Context) error { - if i.websocket { - return i.listenSub(ctx) - } - return i.listenPoll(ctx) -} - -func (i *Issuer) Stop() { - i.mutex.Lock() - defer i.mutex.Unlock() - i.allIssued = true -} diff --git a/tests/load/listen/listener.go b/tests/load/listen/listener.go new file mode 100644 index 000000000000..54d1e544c8a8 --- /dev/null +++ b/tests/load/listen/listener.go @@ -0,0 +1,68 @@ +// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package listen + +import ( + "context" + "sync" + + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/types" +) + +type EthClient interface { + EthClientPoll + EthClientSubscriber +} + +type Tracker interface { + ObserveConfirmed(txHash common.Hash) + // ObserveBlock observes a new block with the given number. + // It should be called when a new block is received. + // It should ignore the block if the number has already been observed. + // It may also ignore the first block observed when it comes to time, + // given the missing information on the time start for the first block. + ObserveBlock(number uint64) +} + +// Listener listens for transaction confirmations from a node. +type Listener struct { + // Injected parameters + client EthClient + websocket bool + tracker Tracker + txTarget uint64 + address common.Address + + // Internal state + mutex sync.Mutex + issued uint64 + lastIssuedNonce uint64 + inFlightTxHashes []common.Hash +} + +func New(client EthClient, websocket bool, tracker Tracker, txTarget uint64, address common.Address) *Listener { + return &Listener{ + client: client, + websocket: websocket, + tracker: tracker, + txTarget: txTarget, + address: address, + } +} + +func (l *Listener) Listen(ctx context.Context) error { + if l.websocket { + return l.listenSub(ctx) + } + return l.listenPoll(ctx) +} + +func (l *Listener) RegisterIssued(tx *types.Transaction) { + l.mutex.Lock() + defer l.mutex.Unlock() + l.issued++ + l.lastIssuedNonce = tx.Nonce() + l.inFlightTxHashes = append(l.inFlightTxHashes, tx.Hash()) +} diff --git a/tests/load/issuer/listenpoll.go b/tests/load/listen/listenpoll.go similarity index 78% rename from tests/load/issuer/listenpoll.go rename to tests/load/listen/listenpoll.go index c7c9d167d194..a4abad5a8ce6 100644 --- a/tests/load/issuer/listenpoll.go +++ b/tests/load/listen/listenpoll.go @@ -1,7 +1,7 @@ // Copyright (C) 2025, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package issuer +package listen import ( "context" @@ -17,13 +17,13 @@ type EthClientPoll interface { NonceAt(ctx context.Context, addr common.Address, blockNumber *big.Int) (uint64, error) } -func (i *Issuer) listenPoll(ctx context.Context) error { +func (l *Listener) listenPoll(ctx context.Context) error { const period = 50 * time.Millisecond ticker := time.NewTicker(period) defer ticker.Stop() for { - blockNumber, nonce, err := pollParallel(ctx, i.client, i.address) + blockNumber, nonce, err := pollParallel(ctx, l.client, l.address) if err != nil { if ctx.Err() != nil { return nil @@ -31,25 +31,25 @@ func (i *Issuer) listenPoll(ctx context.Context) error { return fmt.Errorf("polling node: %w", err) } - i.tracker.ObserveBlock(blockNumber) + l.tracker.ObserveBlock(blockNumber) - i.mutex.Lock() - confirmed := uint64(len(i.inFlightTxHashes)) - if nonce < i.lastIssuedNonce { // lagging behind last issued nonce - lag := i.lastIssuedNonce - nonce + l.mutex.Lock() + confirmed := uint64(len(l.inFlightTxHashes)) + if nonce < l.lastIssuedNonce { // lagging behind last issued nonce + lag := l.lastIssuedNonce - nonce confirmed -= lag } for index := range confirmed { - txHash := i.inFlightTxHashes[index] - i.tracker.ObserveConfirmed(txHash) + txHash := l.inFlightTxHashes[index] + l.tracker.ObserveConfirmed(txHash) } - i.inFlightTxHashes = i.inFlightTxHashes[confirmed:] - finished := i.allIssued && len(i.inFlightTxHashes) == 0 + l.inFlightTxHashes = l.inFlightTxHashes[confirmed:] + finished := l.issued == l.txTarget && len(l.inFlightTxHashes) == 0 if finished { - i.mutex.Unlock() + l.mutex.Unlock() return nil } - i.mutex.Unlock() + l.mutex.Unlock() select { case <-ctx.Done(): diff --git a/tests/load/issuer/listensub.go b/tests/load/listen/listensub.go similarity index 59% rename from tests/load/issuer/listensub.go rename to tests/load/listen/listensub.go index 711f7561ff0b..eef4a903f0d8 100644 --- a/tests/load/issuer/listensub.go +++ b/tests/load/listen/listensub.go @@ -1,7 +1,7 @@ // Copyright (C) 2025, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package issuer +package listen import ( "context" @@ -16,8 +16,8 @@ type EthClientSubscriber interface { NewHeadSubscriber } -func (i *Issuer) listenSub(ctx context.Context) error { - headNotifier := newHeadNotifier(i.client) +func (l *Listener) listenSub(ctx context.Context) error { + headNotifier := newHeadNotifier(l.client) newHeadCh, notifierErrCh, err := headNotifier.start(ctx) if err != nil { return fmt.Errorf("starting new head notifier: %w", err) @@ -26,31 +26,31 @@ func (i *Issuer) listenSub(ctx context.Context) error { for { blockNumber := (*big.Int)(nil) - nonce, err := i.client.NonceAt(ctx, i.address, blockNumber) + nonce, err := l.client.NonceAt(ctx, l.address, blockNumber) if err != nil { if ctx.Err() != nil { return nil } - return fmt.Errorf("checking last block account nonce for address %s: %w", i.address, err) + return fmt.Errorf("checking last block account nonce for address %s: %w", l.address, err) } - i.mutex.Lock() - confirmed := uint64(len(i.inFlightTxHashes)) - if nonce < i.lastIssuedNonce { // lagging behind last issued nonce - lag := i.lastIssuedNonce - nonce + l.mutex.Lock() + confirmed := uint64(len(l.inFlightTxHashes)) + if nonce < l.lastIssuedNonce { // lagging behind last issued nonce + lag := l.lastIssuedNonce - nonce confirmed -= lag } for index := range confirmed { - txHash := i.inFlightTxHashes[index] - i.tracker.ObserveConfirmed(txHash) + txHash := l.inFlightTxHashes[index] + l.tracker.ObserveConfirmed(txHash) } - i.inFlightTxHashes = i.inFlightTxHashes[confirmed:] - finished := i.allIssued && len(i.inFlightTxHashes) == 0 + l.inFlightTxHashes = l.inFlightTxHashes[confirmed:] + finished := l.issued == l.txTarget && len(l.inFlightTxHashes) == 0 if finished { - i.mutex.Unlock() + l.mutex.Unlock() return nil } - i.mutex.Unlock() + l.mutex.Unlock() select { case <-ctx.Done(): @@ -58,7 +58,7 @@ func (i *Issuer) listenSub(ctx context.Context) error { case err := <-notifierErrCh: return fmt.Errorf("new head notifier failed: %w", err) case blockNumber := <-newHeadCh: - i.tracker.ObserveBlock(blockNumber) + l.tracker.ObserveBlock(blockNumber) } } } diff --git a/tests/load/issuer/newhead.go b/tests/load/listen/newhead.go similarity index 99% rename from tests/load/issuer/newhead.go rename to tests/load/listen/newhead.go index 6b4ce0f9e162..577f863f5b87 100644 --- a/tests/load/issuer/newhead.go +++ b/tests/load/listen/newhead.go @@ -1,7 +1,7 @@ // Copyright (C) 2025, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package issuer +package listen import ( "context" diff --git a/tests/load/orchestrate/burst.go b/tests/load/orchestrate/burst.go index 29f5f707e47e..df081aa5ca2d 100644 --- a/tests/load/orchestrate/burst.go +++ b/tests/load/orchestrate/burst.go @@ -38,16 +38,14 @@ func (o *BurstOrchestrator[T, U]) Execute(ctx context.Context) error { // start a goroutine to confirm each issuer's transactions observerGroup := errgroup.Group{} - for _, loader := range o.agents { - observerGroup.Go(func() error { return loader.Issuer.Listen(observerCtx) }) + for _, agent := range o.agents { + observerGroup.Go(func() error { return agent.Listener.Listen(observerCtx) }) } // start issuing transactions sequentially from each issuer issuerGroup := errgroup.Group{} for _, agent := range o.agents { issuerGroup.Go(func() error { - defer agent.Issuer.Stop() - for range agent.TxTarget { tx, err := agent.Generator.GenerateTx() if err != nil { @@ -56,6 +54,8 @@ func (o *BurstOrchestrator[T, U]) Execute(ctx context.Context) error { if err := agent.Issuer.IssueTx(ctx, tx); err != nil { return fmt.Errorf("issuing transaction: %w", err) } + + agent.Listener.RegisterIssued(tx) } return nil }) From 3be8f0390b6b18cbc4cd32483bae170925947197 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Tue, 29 Apr 2025 10:44:28 +0200 Subject: [PATCH 019/197] Rename package names issuer and generator to issue and generate --- tests/load/execute.go | 8 ++++---- tests/load/funder.go | 8 ++++---- tests/load/{generator => generate}/distribute.go | 2 +- tests/load/{generator => generate}/self.go | 2 +- tests/load/{issuer => issue}/issuer.go | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) rename tests/load/{generator => generate}/distribute.go (99%) rename tests/load/{generator => generate}/self.go (99%) rename tests/load/{issuer => issue}/issuer.go (98%) diff --git a/tests/load/execute.go b/tests/load/execute.go index bbefc4b2b80f..ef295aaddb65 100644 --- a/tests/load/execute.go +++ b/tests/load/execute.go @@ -20,8 +20,8 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/ava-labs/avalanchego/tests/load/agent" - "github.com/ava-labs/avalanchego/tests/load/generator" - "github.com/ava-labs/avalanchego/tests/load/issuer" + "github.com/ava-labs/avalanchego/tests/load/generate" + "github.com/ava-labs/avalanchego/tests/load/issue" "github.com/ava-labs/avalanchego/tests/load/listen" "github.com/ava-labs/avalanchego/tests/load/orchestrate" "github.com/ava-labs/avalanchego/tests/load/tracker" @@ -66,13 +66,13 @@ func execute(ctx context.Context, preFundedKeys []*secp256k1.PrivateKey, config if err != nil { return fmt.Errorf("dialing %s: %w", endpoint, err) } - generator, err := generator.NewSelf(ctx, client, + generator, err := generate.NewSelf(ctx, client, big.NewInt(config.maxTipCap), big.NewInt(config.maxFeeCap), keys[i]) if err != nil { return fmt.Errorf("creating generator: %w", err) } address := ethcrypto.PubkeyToAddress(keys[i].PublicKey) - issuer := issuer.New(client, tracker, address) + issuer := issue.New(client, tracker, address) listener := listen.New(client, websocket, tracker, config.txsPerAgent, address) agents[i] = agent.New[*types.Transaction, common.Hash](config.txsPerAgent, generator, issuer, listener, tracker) } diff --git a/tests/load/funder.go b/tests/load/funder.go index 2c392f5688a5..f7c814961fd2 100644 --- a/tests/load/funder.go +++ b/tests/load/funder.go @@ -19,8 +19,8 @@ import ( "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/avalanchego/tests/load/agent" - "github.com/ava-labs/avalanchego/tests/load/generator" - "github.com/ava-labs/avalanchego/tests/load/issuer" + "github.com/ava-labs/avalanchego/tests/load/generate" + "github.com/ava-labs/avalanchego/tests/load/issue" "github.com/ava-labs/avalanchego/tests/load/listen" "github.com/ava-labs/avalanchego/tests/load/orchestrate" "github.com/ava-labs/avalanchego/tests/load/tracker" @@ -73,12 +73,12 @@ func ensureMinimumFunds(ctx context.Context, endpoint string, keys []*ecdsa.Priv maxFundsAddress := ethcrypto.PubkeyToAddress(maxFundsKey.PublicKey) txTarget := uint64(len(needFundsKeys)) - generator, err := generator.NewDistributor(ctx, client, maxFundsKey, needFundsKeys) + generator, err := generate.NewDistributor(ctx, client, maxFundsKey, needFundsKeys) if err != nil { return fmt.Errorf("creating distribution generator: %w", err) } tracker := tracker.NewCounter() - issuer := issuer.New(client, tracker, maxFundsAddress) + issuer := issue.New(client, tracker, maxFundsAddress) listener := listen.New(client, websocket, tracker, txTarget, maxFundsAddress) agents := []*agent.Agent[*types.Transaction, common.Hash]{ agent.New(txTarget, generator, issuer, listener, tracker), diff --git a/tests/load/generator/distribute.go b/tests/load/generate/distribute.go similarity index 99% rename from tests/load/generator/distribute.go rename to tests/load/generate/distribute.go index f3b45103a080..e4bd0f00b66c 100644 --- a/tests/load/generator/distribute.go +++ b/tests/load/generate/distribute.go @@ -1,7 +1,7 @@ // Copyright (C) 2025, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package generator +package generate import ( "context" diff --git a/tests/load/generator/self.go b/tests/load/generate/self.go similarity index 99% rename from tests/load/generator/self.go rename to tests/load/generate/self.go index ba80260b5e7e..05bd6d9c8deb 100644 --- a/tests/load/generator/self.go +++ b/tests/load/generate/self.go @@ -1,7 +1,7 @@ // Copyright (C) 2025, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package generator +package generate import ( "context" diff --git a/tests/load/issuer/issuer.go b/tests/load/issue/issuer.go similarity index 98% rename from tests/load/issuer/issuer.go rename to tests/load/issue/issuer.go index ad76cf99bde8..44f667132d9d 100644 --- a/tests/load/issuer/issuer.go +++ b/tests/load/issue/issuer.go @@ -1,7 +1,7 @@ // Copyright (C) 2025, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package issuer +package issue import ( "context" From 0251e6f7f9ed0fd23552f11c4e7765fa71bc9e47 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Tue, 29 Apr 2025 11:38:21 +0200 Subject: [PATCH 020/197] Mark remaining in flight transactions as failed when exiting listener --- tests/load/listen/listener.go | 10 ++++++++++ tests/load/listen/listenpoll.go | 1 + tests/load/listen/listensub.go | 1 + tests/load/tracker/metrics.go | 1 + 4 files changed, 13 insertions(+) diff --git a/tests/load/listen/listener.go b/tests/load/listen/listener.go index 54d1e544c8a8..09eb7aff8104 100644 --- a/tests/load/listen/listener.go +++ b/tests/load/listen/listener.go @@ -18,6 +18,7 @@ type EthClient interface { type Tracker interface { ObserveConfirmed(txHash common.Hash) + ObserveFailed(txHash common.Hash) // ObserveBlock observes a new block with the given number. // It should be called when a new block is received. // It should ignore the block if the number has already been observed. @@ -66,3 +67,12 @@ func (l *Listener) RegisterIssued(tx *types.Transaction) { l.lastIssuedNonce = tx.Nonce() l.inFlightTxHashes = append(l.inFlightTxHashes, tx.Hash()) } + +func (l *Listener) markRemainingAsFailed() { + l.mutex.Lock() + defer l.mutex.Unlock() + for _, txHash := range l.inFlightTxHashes { + l.tracker.ObserveFailed(txHash) + } + l.inFlightTxHashes = nil +} diff --git a/tests/load/listen/listenpoll.go b/tests/load/listen/listenpoll.go index a4abad5a8ce6..b9620ff02cf7 100644 --- a/tests/load/listen/listenpoll.go +++ b/tests/load/listen/listenpoll.go @@ -21,6 +21,7 @@ func (l *Listener) listenPoll(ctx context.Context) error { const period = 50 * time.Millisecond ticker := time.NewTicker(period) defer ticker.Stop() + defer l.markRemainingAsFailed() for { blockNumber, nonce, err := pollParallel(ctx, l.client, l.address) diff --git a/tests/load/listen/listensub.go b/tests/load/listen/listensub.go index eef4a903f0d8..7f72740114f6 100644 --- a/tests/load/listen/listensub.go +++ b/tests/load/listen/listensub.go @@ -23,6 +23,7 @@ func (l *Listener) listenSub(ctx context.Context) error { return fmt.Errorf("starting new head notifier: %w", err) } defer headNotifier.stop() + defer l.markRemainingAsFailed() for { blockNumber := (*big.Int)(nil) diff --git a/tests/load/tracker/metrics.go b/tests/load/tracker/metrics.go index 81c680d0fc36..352efec4033f 100644 --- a/tests/load/tracker/metrics.go +++ b/tests/load/tracker/metrics.go @@ -75,6 +75,7 @@ func newMetrics(registry PrometheusRegistry) *metrics { return &metrics{ registry: registry, Confirmed: confirmed, + Failed: failed, InFlightIssuances: inFlightIssuances, InFlightTxs: inFlightTxs, IssuanceTxTimes: issuanceTxTimes, From 8658ca04870510c5750da3602e97581b98ece766 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Tue, 29 Apr 2025 15:08:54 +0200 Subject: [PATCH 021/197] Fix listener poll implementation --- tests/load/listen/listenpoll.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/load/listen/listenpoll.go b/tests/load/listen/listenpoll.go index b9620ff02cf7..e6f3bf75be67 100644 --- a/tests/load/listen/listenpoll.go +++ b/tests/load/listen/listenpoll.go @@ -98,16 +98,14 @@ func pollParallel(ctx context.Context, client EthClientPoll, address common.Addr const numGoroutines = 2 for range numGoroutines { select { - case <-blockNumberCh: - result := <-blockNumberCh + case result := <-blockNumberCh: if err == nil && result.err != nil { err = result.err cancel() continue } blockNumber = result.blockNumber - case <-nonceCh: - result := <-nonceCh + case result := <-nonceCh: if err == nil && result.err != nil { err = result.err cancel() From 66eb14758e77d85981698632cbafd94f3025607a Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Tue, 29 Apr 2025 17:31:49 +0200 Subject: [PATCH 022/197] Use env network default nodes directly --- tests/load/ginkgo_test.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/load/ginkgo_test.go b/tests/load/ginkgo_test.go index 6716aaba9719..e17064d1d29a 100644 --- a/tests/load/ginkgo_test.go +++ b/tests/load/ginkgo_test.go @@ -60,12 +60,10 @@ var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { privateNetwork.DefaultFlags = tmpnet.FlagsMap{} publicNetwork := env.GetNetwork() privateNetwork.DefaultFlags.SetDefaults(publicNetwork.DefaultFlags) - privateNetwork.Nodes = make([]*tmpnet.Node, tmpnet.DefaultNodeCount) - for i := range privateNetwork.Nodes { - node := tmpnet.NewNode() + privateNetwork.Nodes = privateNetwork.Nodes[:tmpnet.DefaultNodeCount] + for _, node := range privateNetwork.Nodes { err := node.EnsureKeys() require.NoError(tc, err, "ensuring keys for node %s", node.NodeID) - privateNetwork.Nodes[i] = node } env.StartPrivateNetwork(privateNetwork) }) From 50095f69056962d441f7a9df6c1752cfb2044cad Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Tue, 29 Apr 2025 17:32:07 +0200 Subject: [PATCH 023/197] Limit number of nodes to 1 --- tests/load/ginkgo_test.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/load/ginkgo_test.go b/tests/load/ginkgo_test.go index e17064d1d29a..e6a1d51bf4ee 100644 --- a/tests/load/ginkgo_test.go +++ b/tests/load/ginkgo_test.go @@ -26,11 +26,13 @@ func init() { flagVars = e2e.RegisterFlagsWithDefaultOwner("avalanchego-load") } +const nodesCount = 1 + var _ = ginkgo.SynchronizedBeforeSuite(func() []byte { // Run only once in the first ginkgo process tc := e2e.NewTestContext() - nodes := tmpnet.NewNodesOrPanic(tmpnet.DefaultNodeCount) + nodes := tmpnet.NewNodesOrPanic(nodesCount) network := &tmpnet.Network{ Owner: "avalanchego-load-test", Nodes: nodes, @@ -60,7 +62,7 @@ var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { privateNetwork.DefaultFlags = tmpnet.FlagsMap{} publicNetwork := env.GetNetwork() privateNetwork.DefaultFlags.SetDefaults(publicNetwork.DefaultFlags) - privateNetwork.Nodes = privateNetwork.Nodes[:tmpnet.DefaultNodeCount] + privateNetwork.Nodes = privateNetwork.Nodes[:nodesCount] for _, node := range privateNetwork.Nodes { err := node.EnsureKeys() require.NoError(tc, err, "ensuring keys for node %s", node.NodeID) From 92616fc9dbb767f65e855e5c449a0b809e0d4874 Mon Sep 17 00:00:00 2001 From: Michael Kaplan Date: Wed, 30 Apr 2025 10:10:44 +0200 Subject: [PATCH 024/197] Fix block production --- tests/load/generate/distribute.go | 2 +- tests/load/generate/self.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/load/generate/distribute.go b/tests/load/generate/distribute.go index e4bd0f00b66c..5da296fabf81 100644 --- a/tests/load/generate/distribute.go +++ b/tests/load/generate/distribute.go @@ -64,7 +64,7 @@ func NewDistributor(ctx context.Context, client DistributorClient, from *ecdsa.P return &Distributor{ from: from, address: address, - nonce: nonce + 1, + nonce: nonce, to: to, signer: types.LatestSignerForChainID(chainID), chainID: chainID, diff --git a/tests/load/generate/self.go b/tests/load/generate/self.go index 05bd6d9c8deb..8804dbc974ce 100644 --- a/tests/load/generate/self.go +++ b/tests/load/generate/self.go @@ -53,7 +53,7 @@ func NewSelf(ctx context.Context, client SelfClient, return &Self{ key: key, address: address, - nonce: nonce + 1, + nonce: nonce, signer: types.LatestSignerForChainID(chainID), chainID: chainID, gasTipCap: gasTipCap, From c2ebb6f864436a09446a992c796ba36f0f855179 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 30 Apr 2025 10:13:29 +0200 Subject: [PATCH 025/197] Fix listen subscription exit --- tests/load/listen/newhead.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/load/listen/newhead.go b/tests/load/listen/newhead.go index 577f863f5b87..77eb43d7e17a 100644 --- a/tests/load/listen/newhead.go +++ b/tests/load/listen/newhead.go @@ -65,7 +65,11 @@ func subscriptionChToSignal(listenStop <-chan struct{}, listenDone, ready chan<- case <-listenStop: return case header := <-subCh: - newHeadCh <- header.Number.Uint64() + select { + case newHeadCh <- header.Number.Uint64(): + case <-listenStop: + return + } } } } From e32336812105a98abc0548c9fefe79a22d57d760 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 30 Apr 2025 10:13:42 +0200 Subject: [PATCH 026/197] Minor comment fixes --- tests/load/listen/newhead.go | 2 +- tests/load/orchestrate/burst.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/load/listen/newhead.go b/tests/load/listen/newhead.go index 77eb43d7e17a..5f8e05c8dbfa 100644 --- a/tests/load/listen/newhead.go +++ b/tests/load/listen/newhead.go @@ -74,7 +74,7 @@ func subscriptionChToSignal(listenStop <-chan struct{}, listenDone, ready chan<- } } -// makeRunErrCh makes sure the [newHeadNotifyer] fully stops when +// makeRunErrCh makes sure the [headNotifier] fully stops when // a subscription error is encountered. func (n *headNotifier) makeRunErrCh() <-chan error { errCh := make(chan error) diff --git a/tests/load/orchestrate/burst.go b/tests/load/orchestrate/burst.go index df081aa5ca2d..08f53ac94033 100644 --- a/tests/load/orchestrate/burst.go +++ b/tests/load/orchestrate/burst.go @@ -76,7 +76,7 @@ func (o *BurstOrchestrator[T, U]) Execute(ctx context.Context) error { observerCancel() }() - // blocks until either all of the issuers have finished or our context + // blocks until either all of the observers have finished or our context // is cancelled signalling for early termination (with an error) if err := observerGroup.Wait(); err != nil { return fmt.Errorf("observers: %w", err) From e13e3ae836a34468b98ec5c3fc1e6cd86628cb53 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 30 Apr 2025 10:21:19 +0200 Subject: [PATCH 027/197] Log out average duration per block at the end --- tests/load/execute.go | 3 +++ tests/load/tracker/tracker.go | 21 +++++++++++++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/tests/load/execute.go b/tests/load/execute.go index ef295aaddb65..b42dad4697e4 100644 --- a/tests/load/execute.go +++ b/tests/load/execute.go @@ -57,6 +57,9 @@ func execute(ctx context.Context, preFundedKeys []*secp256k1.PrivateKey, config registry := prometheus.NewRegistry() metricsServer := tracker.NewMetricsServer("127.0.0.1:8082", registry) tracker := tracker.New(registry) + defer func() { + log.Info("average duration per block", "value", tracker.GetAverageDurationPerBlock().String()) + }() agents := make([]*agent.Agent[*types.Transaction, common.Hash], config.agents) for i := range agents { diff --git a/tests/load/tracker/tracker.go b/tests/load/tracker/tracker.go index c484b08468d3..49ee1b18176a 100644 --- a/tests/load/tracker/tracker.go +++ b/tests/load/tracker/tracker.go @@ -18,8 +18,9 @@ type Tracker struct { metrics *metrics stats struct { - confirmed uint64 - failed uint64 + confirmed uint64 + failed uint64 + durationPerBlock []time.Duration } mutex sync.Mutex } @@ -108,8 +109,9 @@ func (t *Tracker) ObserveBlock(number uint64) { // when polling the node periodically instead of using a subscription. numberDiff := number - t.lastBlockNumber timeDiff := now.Sub(t.lastBlockTime) - secondsPerBlock := timeDiff.Seconds() / float64(numberDiff) - t.metrics.BlockTimes.Observe(secondsPerBlock) + durationPerBlock := timeDiff / time.Duration(numberDiff) + t.stats.durationPerBlock = append(t.stats.durationPerBlock, durationPerBlock) + t.metrics.BlockTimes.Observe(durationPerBlock.Seconds()) t.lastBlockTime = now t.lastBlockNumber = number } @@ -129,3 +131,14 @@ func (t *Tracker) GetObservedFailed() uint64 { defer t.mutex.Unlock() return t.stats.failed } + +// GetAverageDurationPerBlock returns the average duration per block. +func (t *Tracker) GetAverageDurationPerBlock() time.Duration { + t.mutex.Lock() + defer t.mutex.Unlock() + var average time.Duration + for _, durationPerBlock := range t.stats.durationPerBlock { + average += durationPerBlock + } + return average / time.Duration(len(t.stats.durationPerBlock)) +} From 46b4e0d2f677af21d298725a209a87f6269263b1 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 30 Apr 2025 11:36:58 +0200 Subject: [PATCH 028/197] Ignore load tests in unit tests --- scripts/build_test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/build_test.sh b/scripts/build_test.sh index 1511c351782a..b87b6e5e50bf 100755 --- a/scripts/build_test.sh +++ b/scripts/build_test.sh @@ -7,7 +7,7 @@ AVALANCHE_PATH=$( cd "$( dirname "${BASH_SOURCE[0]}" )"; cd .. && pwd ) # Load the constants source "$AVALANCHE_PATH"/scripts/constants.sh -EXCLUDED_TARGETS="| grep -v /mocks | grep -v proto | grep -v tests/e2e | grep -v tests/upgrade | grep -v tests/fixture/bootstrapmonitor/e2e" +EXCLUDED_TARGETS="| grep -v /mocks | grep -v proto | grep -v tests/e2e | grep -v tests/load | grep -v tests/upgrade | grep -v tests/fixture/bootstrapmonitor/e2e" if [[ "$(go env GOOS)" == "windows" ]]; then # Test discovery for the antithesis test setups is broken due to From b758aa5a57e2e76d7d759a44358043e753055650 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 30 Apr 2025 11:37:15 +0200 Subject: [PATCH 029/197] Add load testing to readme --- tests/e2e/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/e2e/README.md b/tests/e2e/README.md index 1a4b212d0282..eebf05ba99bb 100644 --- a/tests/e2e/README.md +++ b/tests/e2e/README.md @@ -9,6 +9,7 @@ ./scripts/build.sh # Builds avalanchego for use in deploying a test network ./scripts/build_xsvm.sh # Builds xsvm for use in deploying a test network with a subnet ./bin/ginkgo -v ./tests/e2e -- --avalanchego-path=$PWD/build/avalanchego # Note that the path given for --avalanchego-path must be an absolute and not a relative path. +./bin/ginkgo -v ./tests/load -- --avalanchego-path=$PWD/build/avalanchego ``` See [`tests.e2e.sh`](../../scripts/tests.e2e.sh) for an example. From f595e150c08feb8bd973b3d25579dda95a834454 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 30 Apr 2025 15:16:37 +0200 Subject: [PATCH 030/197] Fix double cancelation of listener subscription --- tests/load/listen/listensub.go | 2 +- tests/load/listen/newhead.go | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/load/listen/listensub.go b/tests/load/listen/listensub.go index 7f72740114f6..c7beec2c6eb9 100644 --- a/tests/load/listen/listensub.go +++ b/tests/load/listen/listensub.go @@ -18,7 +18,7 @@ type EthClientSubscriber interface { func (l *Listener) listenSub(ctx context.Context) error { headNotifier := newHeadNotifier(l.client) - newHeadCh, notifierErrCh, err := headNotifier.start(ctx) + newHeadCh, notifierErrCh, err := headNotifier.start() if err != nil { return fmt.Errorf("starting new head notifier: %w", err) } diff --git a/tests/load/listen/newhead.go b/tests/load/listen/newhead.go index 5f8e05c8dbfa..4fcb170e8851 100644 --- a/tests/load/listen/newhead.go +++ b/tests/load/listen/newhead.go @@ -29,7 +29,7 @@ func newHeadNotifier(client NewHeadSubscriber) *headNotifier { } } -func (n *headNotifier) start(ctx context.Context) (newHead <-chan uint64, runError <-chan error, err error) { +func (n *headNotifier) start() (newHead <-chan uint64, runError <-chan error, err error) { newHeadCh := make(chan uint64) listenStop := make(chan struct{}) @@ -39,7 +39,9 @@ func (n *headNotifier) start(ctx context.Context) (newHead <-chan uint64, runErr ready := make(chan struct{}) subscriptionCh := make(chan *types.Header) - subscription, err := n.client.SubscribeNewHead(ctx, subscriptionCh) + // Note the subscription gets stopped with subscription.Unsubscribe() and does + // not rely on its subscribe context cancelation. + subscription, err := n.client.SubscribeNewHead(context.Background(), subscriptionCh) if err != nil { return nil, nil, fmt.Errorf("subscribing to new head: %w", err) } From aed6ec45e3db6a55c70a80f487a421c2baba8a5c Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 30 Apr 2025 15:17:03 +0200 Subject: [PATCH 031/197] Change metrics route to /ext/metrics --- tests/load/tracker/metrics_server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/load/tracker/metrics_server.go b/tests/load/tracker/metrics_server.go index 11af47e603f5..fef77cc46649 100644 --- a/tests/load/tracker/metrics_server.go +++ b/tests/load/tracker/metrics_server.go @@ -34,7 +34,7 @@ func (*MetricsServer) String() string { } func (s *MetricsServer) Start() (runError <-chan error, err error) { - const metricsPattern = "/metrics" + const metricsPattern = "/ext/metrics" mux := http.NewServeMux() handlerOpts := promhttp.HandlerOpts{Registry: s.registry} From 1ce8f95383e5e0b6d800da080b7518d13c370d6f Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 30 Apr 2025 15:46:38 +0200 Subject: [PATCH 032/197] Fix distribution of funds --- tests/load/funder.go | 3 ++- tests/load/generate/distribute.go | 7 +++---- tests/load/issue/issuer.go | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/load/funder.go b/tests/load/funder.go index f7c814961fd2..34e07963bee3 100644 --- a/tests/load/funder.go +++ b/tests/load/funder.go @@ -83,7 +83,8 @@ func ensureMinimumFunds(ctx context.Context, endpoint string, keys []*ecdsa.Priv agents := []*agent.Agent[*types.Transaction, common.Hash]{ agent.New(txTarget, generator, issuer, listener, tracker), } - orchestrator := orchestrate.NewBurstOrchestrator(agents, time.Second) + const timeout = 3 * time.Second + orchestrator := orchestrate.NewBurstOrchestrator(agents, timeout) err = orchestrator.Execute(ctx) if err != nil { diff --git a/tests/load/generate/distribute.go b/tests/load/generate/distribute.go index 5da296fabf81..9f7e7e73acdc 100644 --- a/tests/load/generate/distribute.go +++ b/tests/load/generate/distribute.go @@ -8,6 +8,7 @@ import ( "crypto/ecdsa" "errors" "fmt" + "maps" "math/big" "github.com/ava-labs/coreth/params" @@ -29,7 +30,6 @@ type Distributor struct { address common.Address // corresponding to `from` nonce uint64 to map[*ecdsa.PrivateKey]*big.Int - toIndex int signer types.Signer chainID *big.Int gasTipCap *big.Int @@ -65,7 +65,7 @@ func NewDistributor(ctx context.Context, client DistributorClient, from *ecdsa.P from: from, address: address, nonce: nonce, - to: to, + to: maps.Clone(to), // need to clone the map to avoid modifying the original signer: types.LatestSignerForChainID(chainID), chainID: chainID, gasTipCap: gasTipCap, @@ -74,7 +74,7 @@ func NewDistributor(ctx context.Context, client DistributorClient, from *ecdsa.P } func (d *Distributor) GenerateTx() (*types.Transaction, error) { - if d.toIndex == len(d.to) { + if len(d.to) == 0 { // The caller of this function should prevent this error from being // returned by bounding the number of transactions generated. return nil, errors.New("no more keys to distribute funds to") @@ -102,6 +102,5 @@ func (d *Distributor) GenerateTx() (*types.Transaction, error) { return nil, err } d.nonce++ - d.toIndex++ return tx, nil } diff --git a/tests/load/issue/issuer.go b/tests/load/issue/issuer.go index 44f667132d9d..ffd3598d58f6 100644 --- a/tests/load/issue/issuer.go +++ b/tests/load/issue/issuer.go @@ -41,7 +41,7 @@ func New(client EthClient, tracker Tracker, address common.Address) *Issuer { func (i *Issuer) IssueTx(ctx context.Context, tx *types.Transaction) error { txHash, txNonce := tx.Hash(), tx.Nonce() - if txNonce > 0 && txNonce != i.lastIssuedNonce+1 { + if i.lastIssuedNonce > 0 && txNonce != i.lastIssuedNonce+1 { // the listener relies on this being true return fmt.Errorf("transaction nonce %d is not equal to the last issued nonce %d plus one", txNonce, i.lastIssuedNonce) } From b711dd08138cb09d2588d6eb7a7d63b9f056738c Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 30 Apr 2025 15:54:31 +0200 Subject: [PATCH 033/197] Remove listener polling --- tests/load/execute.go | 4 +- tests/load/funder.go | 4 +- tests/load/listen/listener.go | 73 +++++++++++++++----- tests/load/listen/listenpoll.go | 119 -------------------------------- tests/load/listen/listensub.go | 65 ----------------- 5 files changed, 59 insertions(+), 206 deletions(-) delete mode 100644 tests/load/listen/listenpoll.go delete mode 100644 tests/load/listen/listensub.go diff --git a/tests/load/execute.go b/tests/load/execute.go index b42dad4697e4..24457753d226 100644 --- a/tests/load/execute.go +++ b/tests/load/execute.go @@ -9,7 +9,6 @@ import ( "fmt" "math/big" "os" - "strings" "time" "github.com/ava-labs/coreth/params" @@ -64,7 +63,6 @@ func execute(ctx context.Context, preFundedKeys []*secp256k1.PrivateKey, config agents := make([]*agent.Agent[*types.Transaction, common.Hash], config.agents) for i := range agents { endpoint := config.endpoints[i%len(config.endpoints)] - websocket := strings.HasPrefix(endpoint, "ws://") || strings.HasPrefix(endpoint, "wss://") client, err := ethclient.DialContext(ctx, endpoint) if err != nil { return fmt.Errorf("dialing %s: %w", endpoint, err) @@ -76,7 +74,7 @@ func execute(ctx context.Context, preFundedKeys []*secp256k1.PrivateKey, config } address := ethcrypto.PubkeyToAddress(keys[i].PublicKey) issuer := issue.New(client, tracker, address) - listener := listen.New(client, websocket, tracker, config.txsPerAgent, address) + listener := listen.New(client, tracker, config.txsPerAgent, address) agents[i] = agent.New[*types.Transaction, common.Hash](config.txsPerAgent, generator, issuer, listener, tracker) } diff --git a/tests/load/funder.go b/tests/load/funder.go index 34e07963bee3..4d162645d13b 100644 --- a/tests/load/funder.go +++ b/tests/load/funder.go @@ -11,7 +11,6 @@ import ( "math/big" "slices" "sort" - "strings" "time" "github.com/ava-labs/coreth/ethclient" @@ -31,7 +30,6 @@ import ( // ensureMinimumFunds ensures that each key has at least `minFundsPerAddr` by sending funds // from the key with the highest starting balance to keys with balances below the minimum. func ensureMinimumFunds(ctx context.Context, endpoint string, keys []*ecdsa.PrivateKey, minFundsPerAddr *big.Int) error { - websocket := strings.HasPrefix(endpoint, "ws") || strings.HasPrefix(endpoint, "wss") client, err := ethclient.DialContext(ctx, endpoint) if err != nil { return fmt.Errorf("dialing %s: %w", endpoint, err) @@ -79,7 +77,7 @@ func ensureMinimumFunds(ctx context.Context, endpoint string, keys []*ecdsa.Priv } tracker := tracker.NewCounter() issuer := issue.New(client, tracker, maxFundsAddress) - listener := listen.New(client, websocket, tracker, txTarget, maxFundsAddress) + listener := listen.New(client, tracker, txTarget, maxFundsAddress) agents := []*agent.Agent[*types.Transaction, common.Hash]{ agent.New(txTarget, generator, issuer, listener, tracker), } diff --git a/tests/load/listen/listener.go b/tests/load/listen/listener.go index 09eb7aff8104..1308c5de11b6 100644 --- a/tests/load/listen/listener.go +++ b/tests/load/listen/listener.go @@ -5,6 +5,8 @@ package listen import ( "context" + "fmt" + "math/big" "sync" "github.com/ava-labs/libevm/common" @@ -12,8 +14,8 @@ import ( ) type EthClient interface { - EthClientPoll - EthClientSubscriber + NonceAt(ctx context.Context, addr common.Address, blockNumber *big.Int) (uint64, error) + NewHeadSubscriber } type Tracker interface { @@ -30,11 +32,10 @@ type Tracker interface { // Listener listens for transaction confirmations from a node. type Listener struct { // Injected parameters - client EthClient - websocket bool - tracker Tracker - txTarget uint64 - address common.Address + client EthClient + tracker Tracker + txTarget uint64 + address common.Address // Internal state mutex sync.Mutex @@ -43,21 +44,61 @@ type Listener struct { inFlightTxHashes []common.Hash } -func New(client EthClient, websocket bool, tracker Tracker, txTarget uint64, address common.Address) *Listener { +func New(client EthClient, tracker Tracker, txTarget uint64, address common.Address) *Listener { return &Listener{ - client: client, - websocket: websocket, - tracker: tracker, - txTarget: txTarget, - address: address, + client: client, + tracker: tracker, + txTarget: txTarget, + address: address, } } func (l *Listener) Listen(ctx context.Context) error { - if l.websocket { - return l.listenSub(ctx) + headNotifier := newHeadNotifier(l.client) + newHeadCh, notifierErrCh, err := headNotifier.start() + if err != nil { + return fmt.Errorf("starting new head notifier: %w", err) + } + defer headNotifier.stop() + defer l.markRemainingAsFailed() + + for { + blockNumber := (*big.Int)(nil) + nonce, err := l.client.NonceAt(ctx, l.address, blockNumber) + if err != nil { + if ctx.Err() != nil { + return nil + } + return fmt.Errorf("checking last block account nonce for address %s: %w", l.address, err) + } + + l.mutex.Lock() + confirmed := uint64(len(l.inFlightTxHashes)) + if nonce < l.lastIssuedNonce { // lagging behind last issued nonce + lag := l.lastIssuedNonce - nonce + confirmed -= lag + } + for index := range confirmed { + txHash := l.inFlightTxHashes[index] + l.tracker.ObserveConfirmed(txHash) + } + l.inFlightTxHashes = l.inFlightTxHashes[confirmed:] + finished := l.issued == l.txTarget && len(l.inFlightTxHashes) == 0 + if finished { + l.mutex.Unlock() + return nil + } + l.mutex.Unlock() + + select { + case <-ctx.Done(): + return nil + case err := <-notifierErrCh: + return fmt.Errorf("new head notifier failed: %w", err) + case blockNumber := <-newHeadCh: + l.tracker.ObserveBlock(blockNumber) + } } - return l.listenPoll(ctx) } func (l *Listener) RegisterIssued(tx *types.Transaction) { diff --git a/tests/load/listen/listenpoll.go b/tests/load/listen/listenpoll.go deleted file mode 100644 index e6f3bf75be67..000000000000 --- a/tests/load/listen/listenpoll.go +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package listen - -import ( - "context" - "fmt" - "math/big" - "time" - - "github.com/ava-labs/libevm/common" -) - -type EthClientPoll interface { - BlockNumber(ctx context.Context) (uint64, error) - NonceAt(ctx context.Context, addr common.Address, blockNumber *big.Int) (uint64, error) -} - -func (l *Listener) listenPoll(ctx context.Context) error { - const period = 50 * time.Millisecond - ticker := time.NewTicker(period) - defer ticker.Stop() - defer l.markRemainingAsFailed() - - for { - blockNumber, nonce, err := pollParallel(ctx, l.client, l.address) - if err != nil { - if ctx.Err() != nil { - return nil - } - return fmt.Errorf("polling node: %w", err) - } - - l.tracker.ObserveBlock(blockNumber) - - l.mutex.Lock() - confirmed := uint64(len(l.inFlightTxHashes)) - if nonce < l.lastIssuedNonce { // lagging behind last issued nonce - lag := l.lastIssuedNonce - nonce - confirmed -= lag - } - for index := range confirmed { - txHash := l.inFlightTxHashes[index] - l.tracker.ObserveConfirmed(txHash) - } - l.inFlightTxHashes = l.inFlightTxHashes[confirmed:] - finished := l.issued == l.txTarget && len(l.inFlightTxHashes) == 0 - if finished { - l.mutex.Unlock() - return nil - } - l.mutex.Unlock() - - select { - case <-ctx.Done(): - return nil - case <-ticker.C: - } - } -} - -type blockNumResult struct { - blockNumber uint64 - err error -} - -type nonceResult struct { - nonce uint64 - err error -} - -func pollParallel(ctx context.Context, client EthClientPoll, address common.Address) ( - blockNumber, nonce uint64, err error, -) { - ctx, cancel := context.WithCancel(ctx) - defer cancel() - - blockNumberCh := make(chan blockNumResult) - go func() { - number, err := client.BlockNumber(ctx) - if err != nil { - err = fmt.Errorf("getting block number: %w", err) - } - blockNumberCh <- blockNumResult{blockNumber: number, err: err} - }() - - nonceCh := make(chan nonceResult) - go func() { - nonceQueryBlock := (*big.Int)(nil) - nonce, err = client.NonceAt(ctx, address, nonceQueryBlock) - if err != nil { - err = fmt.Errorf("checking last block account nonce for address %s: %w", address, err) - } - nonceCh <- nonceResult{nonce: nonce, err: err} - }() - - const numGoroutines = 2 - for range numGoroutines { - select { - case result := <-blockNumberCh: - if err == nil && result.err != nil { - err = result.err - cancel() - continue - } - blockNumber = result.blockNumber - case result := <-nonceCh: - if err == nil && result.err != nil { - err = result.err - cancel() - continue - } - nonce = result.nonce - } - } - - return blockNumber, nonce, err -} diff --git a/tests/load/listen/listensub.go b/tests/load/listen/listensub.go deleted file mode 100644 index c7beec2c6eb9..000000000000 --- a/tests/load/listen/listensub.go +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package listen - -import ( - "context" - "fmt" - "math/big" - - "github.com/ava-labs/libevm/common" -) - -type EthClientSubscriber interface { - NonceAt(ctx context.Context, addr common.Address, blockNumber *big.Int) (uint64, error) - NewHeadSubscriber -} - -func (l *Listener) listenSub(ctx context.Context) error { - headNotifier := newHeadNotifier(l.client) - newHeadCh, notifierErrCh, err := headNotifier.start() - if err != nil { - return fmt.Errorf("starting new head notifier: %w", err) - } - defer headNotifier.stop() - defer l.markRemainingAsFailed() - - for { - blockNumber := (*big.Int)(nil) - nonce, err := l.client.NonceAt(ctx, l.address, blockNumber) - if err != nil { - if ctx.Err() != nil { - return nil - } - return fmt.Errorf("checking last block account nonce for address %s: %w", l.address, err) - } - - l.mutex.Lock() - confirmed := uint64(len(l.inFlightTxHashes)) - if nonce < l.lastIssuedNonce { // lagging behind last issued nonce - lag := l.lastIssuedNonce - nonce - confirmed -= lag - } - for index := range confirmed { - txHash := l.inFlightTxHashes[index] - l.tracker.ObserveConfirmed(txHash) - } - l.inFlightTxHashes = l.inFlightTxHashes[confirmed:] - finished := l.issued == l.txTarget && len(l.inFlightTxHashes) == 0 - if finished { - l.mutex.Unlock() - return nil - } - l.mutex.Unlock() - - select { - case <-ctx.Done(): - return nil - case err := <-notifierErrCh: - return fmt.Errorf("new head notifier failed: %w", err) - case blockNumber := <-newHeadCh: - l.tracker.ObserveBlock(blockNumber) - } - } -} From a7d46f7baf52d9af285aad25218ebd79ea8301c8 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 30 Apr 2025 16:40:50 +0200 Subject: [PATCH 034/197] Wait 3s after fund distribution to check for balances --- tests/load/funder.go | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/tests/load/funder.go b/tests/load/funder.go index 4d162645d13b..28a9dc64b661 100644 --- a/tests/load/funder.go +++ b/tests/load/funder.go @@ -81,14 +81,25 @@ func ensureMinimumFunds(ctx context.Context, endpoint string, keys []*ecdsa.Priv agents := []*agent.Agent[*types.Transaction, common.Hash]{ agent.New(txTarget, generator, issuer, listener, tracker), } - const timeout = 3 * time.Second - orchestrator := orchestrate.NewBurstOrchestrator(agents, timeout) + orchestrator := orchestrate.NewBurstOrchestrator(agents, time.Second) err = orchestrator.Execute(ctx) if err != nil { return fmt.Errorf("executing fund distribution transactions: %w", err) } + // Wait for transactions to be taken into account, especially because + // the orchestrator Execute method does not wait for all transactions + // to be confirmed. + const timeout = 3 * time.Second + timer := time.NewTimer(timeout) + select { + case <-ctx.Done(): + timer.Stop() + return ctx.Err() + case <-timer.C: + } + err = checkBalancesHaveMin(ctx, client, slices.Collect(maps.Keys(needFundsKeys)), minFundsPerAddr) if err != nil { return fmt.Errorf("checking balances after funding: %w", err) From 2514b3c5693a3bbd258431c94f13c8c9f908973a Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 30 Apr 2025 16:55:46 +0200 Subject: [PATCH 035/197] Do not track block durations --- tests/load/execute.go | 3 --- tests/load/listen/listener.go | 9 +------ tests/load/listen/newhead.go | 10 ++++---- tests/load/tracker/metrics.go | 10 +------- tests/load/tracker/tracker.go | 48 ++--------------------------------- 5 files changed, 9 insertions(+), 71 deletions(-) diff --git a/tests/load/execute.go b/tests/load/execute.go index 24457753d226..4b8e52ea3eb5 100644 --- a/tests/load/execute.go +++ b/tests/load/execute.go @@ -56,9 +56,6 @@ func execute(ctx context.Context, preFundedKeys []*secp256k1.PrivateKey, config registry := prometheus.NewRegistry() metricsServer := tracker.NewMetricsServer("127.0.0.1:8082", registry) tracker := tracker.New(registry) - defer func() { - log.Info("average duration per block", "value", tracker.GetAverageDurationPerBlock().String()) - }() agents := make([]*agent.Agent[*types.Transaction, common.Hash], config.agents) for i := range agents { diff --git a/tests/load/listen/listener.go b/tests/load/listen/listener.go index 1308c5de11b6..f1ae666cefe3 100644 --- a/tests/load/listen/listener.go +++ b/tests/load/listen/listener.go @@ -21,12 +21,6 @@ type EthClient interface { type Tracker interface { ObserveConfirmed(txHash common.Hash) ObserveFailed(txHash common.Hash) - // ObserveBlock observes a new block with the given number. - // It should be called when a new block is received. - // It should ignore the block if the number has already been observed. - // It may also ignore the first block observed when it comes to time, - // given the missing information on the time start for the first block. - ObserveBlock(number uint64) } // Listener listens for transaction confirmations from a node. @@ -95,8 +89,7 @@ func (l *Listener) Listen(ctx context.Context) error { return nil case err := <-notifierErrCh: return fmt.Errorf("new head notifier failed: %w", err) - case blockNumber := <-newHeadCh: - l.tracker.ObserveBlock(blockNumber) + case <-newHeadCh: } } } diff --git a/tests/load/listen/newhead.go b/tests/load/listen/newhead.go index 4fcb170e8851..ff4d93345d19 100644 --- a/tests/load/listen/newhead.go +++ b/tests/load/listen/newhead.go @@ -29,8 +29,8 @@ func newHeadNotifier(client NewHeadSubscriber) *headNotifier { } } -func (n *headNotifier) start() (newHead <-chan uint64, runError <-chan error, err error) { - newHeadCh := make(chan uint64) +func (n *headNotifier) start() (newHead <-chan struct{}, runError <-chan error, err error) { + newHeadCh := make(chan struct{}) listenStop := make(chan struct{}) n.listenStop = listenStop @@ -58,7 +58,7 @@ func (n *headNotifier) stop() { } func subscriptionChToSignal(listenStop <-chan struct{}, listenDone, ready chan<- struct{}, - subCh <-chan *types.Header, newHeadCh chan<- uint64, + subCh <-chan *types.Header, newHeadCh chan<- struct{}, ) { defer close(listenDone) close(ready) @@ -66,9 +66,9 @@ func subscriptionChToSignal(listenStop <-chan struct{}, listenDone, ready chan<- select { case <-listenStop: return - case header := <-subCh: + case <-subCh: select { - case newHeadCh <- header.Number.Uint64(): + case newHeadCh <- struct{}{}: case <-listenStop: return } diff --git a/tests/load/tracker/metrics.go b/tests/load/tracker/metrics.go index 352efec4033f..d1a5182e2abc 100644 --- a/tests/load/tracker/metrics.go +++ b/tests/load/tracker/metrics.go @@ -25,8 +25,6 @@ type metrics struct { // ConfirmationTxTimes is the summary of the quantiles of individual confirmation tx times. // Failed transactions do not show in this metric. ConfirmationTxTimes prometheus.Summary - // BlockTimes is the summary of the quantiles of individual block times. - BlockTimes prometheus.Summary registry PrometheusRegistry } @@ -63,14 +61,9 @@ func newMetrics(registry PrometheusRegistry) *metrics { Help: "Individual Tx Confirmation Times for a Load Test", Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, }) - blockTimes := prometheus.NewSummary(prometheus.SummaryOpts{ - Name: "block_time", - Help: "Individual block times for a load test", - Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, - }) registry.MustRegister(confirmed, failed, inFlightIssuances, inFlightTxs, - issuanceTxTimes, confirmationTxTimes, blockTimes) + issuanceTxTimes, confirmationTxTimes) return &metrics{ registry: registry, @@ -80,7 +73,6 @@ func newMetrics(registry PrometheusRegistry) *metrics { InFlightTxs: inFlightTxs, IssuanceTxTimes: issuanceTxTimes, ConfirmationTxTimes: confirmationTxTimes, - BlockTimes: blockTimes, } } diff --git a/tests/load/tracker/tracker.go b/tests/load/tracker/tracker.go index 49ee1b18176a..508b44b251e1 100644 --- a/tests/load/tracker/tracker.go +++ b/tests/load/tracker/tracker.go @@ -13,14 +13,11 @@ import ( type Tracker struct { timeNow func() time.Time txHashToLastTime map[common.Hash]time.Time - lastBlockNumber uint64 - lastBlockTime time.Time metrics *metrics stats struct { - confirmed uint64 - failed uint64 - durationPerBlock []time.Duration + confirmed uint64 + failed uint64 } mutex sync.Mutex } @@ -86,36 +83,6 @@ func (t *Tracker) ObserveFailed(txHash common.Hash) { t.stats.failed++ } -// ObserveBlock records a new block with the given number. -// Note it ignores the first block, to avoid misleading metrics due to the -// absence of information on when the previous block was created. -func (t *Tracker) ObserveBlock(number uint64) { - t.mutex.Lock() - defer t.mutex.Unlock() - - now := t.timeNow() - if t.lastBlockNumber == 0 { - t.lastBlockTime = now - t.lastBlockNumber = number - return - } - - if number == t.lastBlockNumber { - // No new block. This can happen when polling the node periodically. - return - } - - // Usually numberDiff should be 1, but it may happen it is bigger, especially - // when polling the node periodically instead of using a subscription. - numberDiff := number - t.lastBlockNumber - timeDiff := now.Sub(t.lastBlockTime) - durationPerBlock := timeDiff / time.Duration(numberDiff) - t.stats.durationPerBlock = append(t.stats.durationPerBlock, durationPerBlock) - t.metrics.BlockTimes.Observe(durationPerBlock.Seconds()) - t.lastBlockTime = now - t.lastBlockNumber = number -} - // GetObservedConfirmed returns the number of transactions that the tracker has // confirmed were accepted. func (t *Tracker) GetObservedConfirmed() uint64 { @@ -131,14 +98,3 @@ func (t *Tracker) GetObservedFailed() uint64 { defer t.mutex.Unlock() return t.stats.failed } - -// GetAverageDurationPerBlock returns the average duration per block. -func (t *Tracker) GetAverageDurationPerBlock() time.Duration { - t.mutex.Lock() - defer t.mutex.Unlock() - var average time.Duration - for _, durationPerBlock := range t.stats.durationPerBlock { - average += durationPerBlock - } - return average / time.Duration(len(t.stats.durationPerBlock)) -} From 3172d41f7840735cdc2430aa57f348ef8896194b Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 30 Apr 2025 17:03:45 +0200 Subject: [PATCH 036/197] =?UTF-8?q?Tidy=20dependencies=20=F0=9F=A4=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.sum | 3 +++ 1 file changed, 3 insertions(+) diff --git a/go.sum b/go.sum index 374dc3ded212..a628f3370d91 100644 --- a/go.sum +++ b/go.sum @@ -258,7 +258,10 @@ github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM= +github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= +github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4= github.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= From aa4978712e0b3f88e700dbc8cc134f89e9718750 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 30 Apr 2025 17:17:50 +0200 Subject: [PATCH 037/197] Use initially created network --- tests/load/ginkgo_test.go | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/tests/load/ginkgo_test.go b/tests/load/ginkgo_test.go index e6a1d51bf4ee..926888593782 100644 --- a/tests/load/ginkgo_test.go +++ b/tests/load/ginkgo_test.go @@ -53,36 +53,31 @@ var _ = ginkgo.SynchronizedBeforeSuite(func() []byte { }) var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { - var privateNetwork *tmpnet.Network + var network *tmpnet.Network ginkgo.BeforeAll(func() { tc := e2e.NewTestContext() env := e2e.GetEnv(tc) - privateNetwork = tmpnet.NewDefaultNetwork("avalanchego-load-test") - privateNetwork.DefaultFlags = tmpnet.FlagsMap{} - publicNetwork := env.GetNetwork() - privateNetwork.DefaultFlags.SetDefaults(publicNetwork.DefaultFlags) - privateNetwork.Nodes = privateNetwork.Nodes[:nodesCount] - for _, node := range privateNetwork.Nodes { + network = env.GetNetwork() + network.Nodes = network.Nodes[:nodesCount] + for _, node := range network.Nodes { err := node.EnsureKeys() require.NoError(tc, err, "ensuring keys for node %s", node.NodeID) } - env.StartPrivateNetwork(privateNetwork) }) ginkgo.It("C-Chain", func(ctx context.Context) { - nodes := privateNetwork.Nodes const blockchainID = "C" - endpoints, err := tmpnet.GetNodeWebsocketURIs(nodes, blockchainID) + endpoints, err := tmpnet.GetNodeWebsocketURIs(network.Nodes, blockchainID) require.NoError(ginkgo.GinkgoT(), err, "getting node websocket URIs") config := config{ endpoints: endpoints, maxFeeCap: 50, maxTipCap: 1, - agents: 1, - txsPerAgent: 100, + agents: 10, + txsPerAgent: 1000, } - err = execute(ctx, privateNetwork.PreFundedKeys, config) + err = execute(ctx, network.PreFundedKeys, config) if err != nil { ginkgo.GinkgoT().Error(err) } From 15065c42488e688bf93462dffe62f06a7b37d5d2 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Fri, 2 May 2025 12:43:36 +0200 Subject: [PATCH 038/197] Replace in flight txs gauge with issued counter --- tests/load/tracker/metrics.go | 17 ++++++++--------- tests/load/tracker/tracker.go | 4 +--- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/tests/load/tracker/metrics.go b/tests/load/tracker/metrics.go index d1a5182e2abc..03269f6a02d0 100644 --- a/tests/load/tracker/metrics.go +++ b/tests/load/tracker/metrics.go @@ -10,6 +10,8 @@ import ( ) type metrics struct { + // Issued is the number of issued transactions. + Issued prometheus.Counter // Confirmed is the number of confirmed transactions. Confirmed prometheus.Counter // Failed is the number of failed transactions. @@ -17,9 +19,6 @@ type metrics struct { // InFlightIssuances is the number of transactions that are being issued, from issuance start // to issuance end. InFlightIssuances prometheus.Gauge - // InFlightTxs is the number of transactions that are in-flight, from issuance start - // to confirmation end or failure. - InFlightTxs prometheus.Gauge // IssuanceTxTimes is the summary of the quantiles of individual issuance tx times. IssuanceTxTimes prometheus.Summary // ConfirmationTxTimes is the summary of the quantiles of individual confirmation tx times. @@ -35,6 +34,10 @@ type PrometheusRegistry interface { } func newMetrics(registry PrometheusRegistry) *metrics { + issued := prometheus.NewCounter(prometheus.CounterOpts{ + Name: "tx_issued", + Help: "Number of issued transactions", + }) confirmed := prometheus.NewCounter(prometheus.CounterOpts{ Name: "tx_confirmed", Help: "Number of confirmed transactions", @@ -47,10 +50,6 @@ func newMetrics(registry PrometheusRegistry) *metrics { Name: "tx_in_flight_issuances", Help: "Number of transactions in flight issuances", }) - inFlightTxs := prometheus.NewGauge(prometheus.GaugeOpts{ - Name: "tx_in_flight_txs", - Help: "Number of transactions in flight", - }) issuanceTxTimes := prometheus.NewSummary(prometheus.SummaryOpts{ Name: "tx_issuance_time", Help: "Individual Tx Issuance Times for a Load Test", @@ -62,15 +61,15 @@ func newMetrics(registry PrometheusRegistry) *metrics { Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, }) - registry.MustRegister(confirmed, failed, inFlightIssuances, inFlightTxs, + registry.MustRegister(issued, confirmed, failed, inFlightIssuances, issuanceTxTimes, confirmationTxTimes) return &metrics{ registry: registry, + Issued: issued, Confirmed: confirmed, Failed: failed, InFlightIssuances: inFlightIssuances, - InFlightTxs: inFlightTxs, IssuanceTxTimes: issuanceTxTimes, ConfirmationTxTimes: confirmationTxTimes, } diff --git a/tests/load/tracker/tracker.go b/tests/load/tracker/tracker.go index 508b44b251e1..fa2e31e02583 100644 --- a/tests/load/tracker/tracker.go +++ b/tests/load/tracker/tracker.go @@ -39,7 +39,6 @@ func (t *Tracker) IssueStart(txHash common.Hash) { defer t.mutex.Unlock() t.metrics.InFlightIssuances.Inc() - t.metrics.InFlightTxs.Inc() t.txHashToLastTime[txHash] = t.timeNow() } @@ -49,6 +48,7 @@ func (t *Tracker) IssueEnd(txHash common.Hash) { t.mutex.Lock() defer t.mutex.Unlock() + t.metrics.Issued.Inc() t.metrics.InFlightIssuances.Dec() start := t.txHashToLastTime[txHash] now := t.timeNow() @@ -62,7 +62,6 @@ func (t *Tracker) ObserveConfirmed(txHash common.Hash) { t.mutex.Lock() defer t.mutex.Unlock() - t.metrics.InFlightTxs.Dec() t.metrics.Confirmed.Inc() issuedTime := t.txHashToLastTime[txHash] now := t.timeNow() @@ -77,7 +76,6 @@ func (t *Tracker) ObserveFailed(txHash common.Hash) { t.mutex.Lock() defer t.mutex.Unlock() - t.metrics.InFlightTxs.Dec() t.metrics.Failed.Inc() delete(t.txHashToLastTime, txHash) t.stats.failed++ From 3cf8af896c0c941c5c308edfaa6907d49a3a8e0d Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Fri, 2 May 2025 12:50:36 +0200 Subject: [PATCH 039/197] Remove issuing detail metrics as likely unneeded --- tests/load/agent/dependencies.go | 6 ++---- tests/load/issue/issuer.go | 6 ++---- tests/load/tracker/counter.go | 3 +-- tests/load/tracker/metrics.go | 20 ++------------------ tests/load/tracker/tracker.go | 18 ++---------------- 5 files changed, 9 insertions(+), 44 deletions(-) diff --git a/tests/load/agent/dependencies.go b/tests/load/agent/dependencies.go index e58723172f74..4968620ba4b2 100644 --- a/tests/load/agent/dependencies.go +++ b/tests/load/agent/dependencies.go @@ -28,11 +28,9 @@ type Listener[T comparable] interface { // Tracker keeps track of the status of transactions. // This must be thread-safe, so it can be called in parallel by the issuer(s) or orchestrator. type Tracker[T comparable] interface { - // IssueStart records a transaction that is being issued. - IssueStart(T) - // IssueEnd records a transaction that was issued, but whose final status is + // Issue records a transaction that was issued, but whose final status is // not yet known. - IssueEnd(T) + Issue(T) // ObserveConfirmed records a transaction that was confirmed. ObserveConfirmed(T) // ObserveFailed records a transaction that failed (e.g. expired) diff --git a/tests/load/issue/issuer.go b/tests/load/issue/issuer.go index ffd3598d58f6..14f383847b08 100644 --- a/tests/load/issue/issuer.go +++ b/tests/load/issue/issuer.go @@ -16,8 +16,7 @@ type EthClient interface { } type Tracker interface { - IssueStart(txHash common.Hash) - IssueEnd(txHash common.Hash) + Issue(txHash common.Hash) } // Issuer issues transactions to a node. @@ -45,11 +44,10 @@ func (i *Issuer) IssueTx(ctx context.Context, tx *types.Transaction) error { // the listener relies on this being true return fmt.Errorf("transaction nonce %d is not equal to the last issued nonce %d plus one", txNonce, i.lastIssuedNonce) } - i.tracker.IssueStart(txHash) if err := i.client.SendTransaction(ctx, tx); err != nil { return err } - i.tracker.IssueEnd(txHash) + i.tracker.Issue(txHash) i.lastIssuedNonce = txNonce return nil } diff --git a/tests/load/tracker/counter.go b/tests/load/tracker/counter.go index 864c4c3f77c8..5d9b8fef97bb 100644 --- a/tests/load/tracker/counter.go +++ b/tests/load/tracker/counter.go @@ -18,8 +18,7 @@ func NewCounter() *Counter { return &Counter{} } -func (*Counter) IssueStart(_ common.Hash) {} -func (*Counter) IssueEnd(_ common.Hash) {} +func (*Counter) Issue(_ common.Hash) {} func (c *Counter) ObserveConfirmed(_ common.Hash) { c.confirmed++ } func (c *Counter) ObserveFailed(_ common.Hash) { c.failed++ } func (*Counter) ObserveBlock(_ uint64) {} diff --git a/tests/load/tracker/metrics.go b/tests/load/tracker/metrics.go index 03269f6a02d0..a08e3fc7824a 100644 --- a/tests/load/tracker/metrics.go +++ b/tests/load/tracker/metrics.go @@ -16,11 +16,6 @@ type metrics struct { Confirmed prometheus.Counter // Failed is the number of failed transactions. Failed prometheus.Counter - // InFlightIssuances is the number of transactions that are being issued, from issuance start - // to issuance end. - InFlightIssuances prometheus.Gauge - // IssuanceTxTimes is the summary of the quantiles of individual issuance tx times. - IssuanceTxTimes prometheus.Summary // ConfirmationTxTimes is the summary of the quantiles of individual confirmation tx times. // Failed transactions do not show in this metric. ConfirmationTxTimes prometheus.Summary @@ -46,31 +41,20 @@ func newMetrics(registry PrometheusRegistry) *metrics { Name: "tx_failed", Help: "Number of failed transactions", }) - inFlightIssuances := prometheus.NewGauge(prometheus.GaugeOpts{ - Name: "tx_in_flight_issuances", - Help: "Number of transactions in flight issuances", - }) - issuanceTxTimes := prometheus.NewSummary(prometheus.SummaryOpts{ - Name: "tx_issuance_time", - Help: "Individual Tx Issuance Times for a Load Test", - Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, - }) confirmationTxTimes := prometheus.NewSummary(prometheus.SummaryOpts{ Name: "tx_confirmation_time", Help: "Individual Tx Confirmation Times for a Load Test", Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, }) - registry.MustRegister(issued, confirmed, failed, inFlightIssuances, - issuanceTxTimes, confirmationTxTimes) + registry.MustRegister(issued, confirmed, failed, + confirmationTxTimes) return &metrics{ registry: registry, Issued: issued, Confirmed: confirmed, Failed: failed, - InFlightIssuances: inFlightIssuances, - IssuanceTxTimes: issuanceTxTimes, ConfirmationTxTimes: confirmationTxTimes, } } diff --git a/tests/load/tracker/tracker.go b/tests/load/tracker/tracker.go index fa2e31e02583..2f70ea643b91 100644 --- a/tests/load/tracker/tracker.go +++ b/tests/load/tracker/tracker.go @@ -33,27 +33,13 @@ func New(registry PrometheusRegistry) *Tracker { } } -// IssueStart records a transaction that is being issued. -func (t *Tracker) IssueStart(txHash common.Hash) { - t.mutex.Lock() - defer t.mutex.Unlock() - - t.metrics.InFlightIssuances.Inc() - t.txHashToLastTime[txHash] = t.timeNow() -} - -// IssueEnd records a transaction that was issued, but whose final status is +// Issue records a transaction that was issued, but whose final status is // not yet known. -func (t *Tracker) IssueEnd(txHash common.Hash) { +func (t *Tracker) Issue(txHash common.Hash) { t.mutex.Lock() defer t.mutex.Unlock() t.metrics.Issued.Inc() - t.metrics.InFlightIssuances.Dec() - start := t.txHashToLastTime[txHash] - now := t.timeNow() - diff := now.Sub(start) - t.metrics.IssuanceTxTimes.Observe(diff.Seconds()) t.txHashToLastTime[txHash] = t.timeNow() } From 8270efbf44e55ff6499e585236f93b313640685c Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Fri, 2 May 2025 12:51:30 +0200 Subject: [PATCH 040/197] Unexport metrics fields --- tests/load/tracker/metrics.go | 24 ++++++++++++------------ tests/load/tracker/tracker.go | 8 ++++---- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/tests/load/tracker/metrics.go b/tests/load/tracker/metrics.go index a08e3fc7824a..c5a55709241b 100644 --- a/tests/load/tracker/metrics.go +++ b/tests/load/tracker/metrics.go @@ -10,15 +10,15 @@ import ( ) type metrics struct { - // Issued is the number of issued transactions. - Issued prometheus.Counter - // Confirmed is the number of confirmed transactions. - Confirmed prometheus.Counter - // Failed is the number of failed transactions. - Failed prometheus.Counter - // ConfirmationTxTimes is the summary of the quantiles of individual confirmation tx times. + // issued is the number of issued transactions. + issued prometheus.Counter + // confirmed is the number of confirmed transactions. + confirmed prometheus.Counter + // failed is the number of failed transactions. + failed prometheus.Counter + // confirmationTxTimes is the summary of the quantiles of individual confirmation tx times. // Failed transactions do not show in this metric. - ConfirmationTxTimes prometheus.Summary + confirmationTxTimes prometheus.Summary registry PrometheusRegistry } @@ -52,10 +52,10 @@ func newMetrics(registry PrometheusRegistry) *metrics { return &metrics{ registry: registry, - Issued: issued, - Confirmed: confirmed, - Failed: failed, - ConfirmationTxTimes: confirmationTxTimes, + issued: issued, + confirmed: confirmed, + failed: failed, + confirmationTxTimes: confirmationTxTimes, } } diff --git a/tests/load/tracker/tracker.go b/tests/load/tracker/tracker.go index 2f70ea643b91..3483703028ce 100644 --- a/tests/load/tracker/tracker.go +++ b/tests/load/tracker/tracker.go @@ -39,7 +39,7 @@ func (t *Tracker) Issue(txHash common.Hash) { t.mutex.Lock() defer t.mutex.Unlock() - t.metrics.Issued.Inc() + t.metrics.issued.Inc() t.txHashToLastTime[txHash] = t.timeNow() } @@ -48,11 +48,11 @@ func (t *Tracker) ObserveConfirmed(txHash common.Hash) { t.mutex.Lock() defer t.mutex.Unlock() - t.metrics.Confirmed.Inc() + t.metrics.confirmed.Inc() issuedTime := t.txHashToLastTime[txHash] now := t.timeNow() diff := now.Sub(issuedTime) - t.metrics.ConfirmationTxTimes.Observe(diff.Seconds()) + t.metrics.confirmationTxTimes.Observe(diff.Seconds()) delete(t.txHashToLastTime, txHash) t.stats.confirmed++ } @@ -62,7 +62,7 @@ func (t *Tracker) ObserveFailed(txHash common.Hash) { t.mutex.Lock() defer t.mutex.Unlock() - t.metrics.Failed.Inc() + t.metrics.failed.Inc() delete(t.txHashToLastTime, txHash) t.stats.failed++ } From 55fdfdf79af6a104a5e50e339bd3e85f84026902 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Fri, 2 May 2025 12:54:32 +0200 Subject: [PATCH 041/197] Change metrics to match hypersdk ones --- tests/load/tracker/metrics.go | 29 ++++++++++++++--------------- tests/load/tracker/tracker.go | 5 ++--- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/tests/load/tracker/metrics.go b/tests/load/tracker/metrics.go index c5a55709241b..3af443ee824d 100644 --- a/tests/load/tracker/metrics.go +++ b/tests/load/tracker/metrics.go @@ -16,9 +16,9 @@ type metrics struct { confirmed prometheus.Counter // failed is the number of failed transactions. failed prometheus.Counter - // confirmationTxTimes is the summary of the quantiles of individual confirmation tx times. + // txLatency is a histogram of individual tx times. // Failed transactions do not show in this metric. - confirmationTxTimes prometheus.Summary + txLatency prometheus.Histogram registry PrometheusRegistry } @@ -30,32 +30,31 @@ type PrometheusRegistry interface { func newMetrics(registry PrometheusRegistry) *metrics { issued := prometheus.NewCounter(prometheus.CounterOpts{ - Name: "tx_issued", + Name: "txs_issued", Help: "Number of issued transactions", }) confirmed := prometheus.NewCounter(prometheus.CounterOpts{ - Name: "tx_confirmed", + Name: "txs_confirmed", Help: "Number of confirmed transactions", }) failed := prometheus.NewCounter(prometheus.CounterOpts{ - Name: "tx_failed", + Name: "txs_failed", Help: "Number of failed transactions", }) - confirmationTxTimes := prometheus.NewSummary(prometheus.SummaryOpts{ - Name: "tx_confirmation_time", - Help: "Individual Tx Confirmation Times for a Load Test", - Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, + txLatency := prometheus.NewHistogram(prometheus.HistogramOpts{ + Name: "tx_latency", + Help: "Latency of transactions", }) registry.MustRegister(issued, confirmed, failed, - confirmationTxTimes) + txLatency) return &metrics{ - registry: registry, - issued: issued, - confirmed: confirmed, - failed: failed, - confirmationTxTimes: confirmationTxTimes, + registry: registry, + issued: issued, + confirmed: confirmed, + failed: failed, + txLatency: txLatency, } } diff --git a/tests/load/tracker/tracker.go b/tests/load/tracker/tracker.go index 3483703028ce..7b73a2b6941b 100644 --- a/tests/load/tracker/tracker.go +++ b/tests/load/tracker/tracker.go @@ -50,9 +50,8 @@ func (t *Tracker) ObserveConfirmed(txHash common.Hash) { t.metrics.confirmed.Inc() issuedTime := t.txHashToLastTime[txHash] - now := t.timeNow() - diff := now.Sub(issuedTime) - t.metrics.confirmationTxTimes.Observe(diff.Seconds()) + diff := t.timeNow().Sub(issuedTime) + t.metrics.txLatency.Observe(float64(diff.Milliseconds())) delete(t.txHashToLastTime, txHash) t.stats.confirmed++ } From 238dcff1cd0103bf131c9f05d83475e996cf90bd Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Fri, 2 May 2025 14:28:14 +0200 Subject: [PATCH 042/197] Remove unneeded address field for issuer --- tests/load/execute.go | 2 +- tests/load/funder.go | 2 +- tests/load/ginkgo_test.go | 6 +++--- tests/load/issue/issuer.go | 4 +--- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/tests/load/execute.go b/tests/load/execute.go index 4b8e52ea3eb5..3083777dc1dd 100644 --- a/tests/load/execute.go +++ b/tests/load/execute.go @@ -69,8 +69,8 @@ func execute(ctx context.Context, preFundedKeys []*secp256k1.PrivateKey, config if err != nil { return fmt.Errorf("creating generator: %w", err) } + issuer := issue.New(client, tracker) address := ethcrypto.PubkeyToAddress(keys[i].PublicKey) - issuer := issue.New(client, tracker, address) listener := listen.New(client, tracker, config.txsPerAgent, address) agents[i] = agent.New[*types.Transaction, common.Hash](config.txsPerAgent, generator, issuer, listener, tracker) } diff --git a/tests/load/funder.go b/tests/load/funder.go index 28a9dc64b661..d8cdf323f075 100644 --- a/tests/load/funder.go +++ b/tests/load/funder.go @@ -76,7 +76,7 @@ func ensureMinimumFunds(ctx context.Context, endpoint string, keys []*ecdsa.Priv return fmt.Errorf("creating distribution generator: %w", err) } tracker := tracker.NewCounter() - issuer := issue.New(client, tracker, maxFundsAddress) + issuer := issue.New(client, tracker) listener := listen.New(client, tracker, txTarget, maxFundsAddress) agents := []*agent.Agent[*types.Transaction, common.Hash]{ agent.New(txTarget, generator, issuer, listener, tracker), diff --git a/tests/load/ginkgo_test.go b/tests/load/ginkgo_test.go index 926888593782..545036bc15f7 100644 --- a/tests/load/ginkgo_test.go +++ b/tests/load/ginkgo_test.go @@ -72,10 +72,10 @@ var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { require.NoError(ginkgo.GinkgoT(), err, "getting node websocket URIs") config := config{ endpoints: endpoints, - maxFeeCap: 50, + maxFeeCap: 5000, maxTipCap: 1, - agents: 10, - txsPerAgent: 1000, + agents: 60, + txsPerAgent: 100000, } err = execute(ctx, network.PreFundedKeys, config) if err != nil { diff --git a/tests/load/issue/issuer.go b/tests/load/issue/issuer.go index 14f383847b08..e9b1b45233e7 100644 --- a/tests/load/issue/issuer.go +++ b/tests/load/issue/issuer.go @@ -24,17 +24,15 @@ type Issuer struct { // Injected parameters client EthClient tracker Tracker - address common.Address // State lastIssuedNonce uint64 // for programming assumptions checks only } -func New(client EthClient, tracker Tracker, address common.Address) *Issuer { +func New(client EthClient, tracker Tracker) *Issuer { return &Issuer{ client: client, tracker: tracker, - address: address, } } From baabe223f66e05a34e99e6574e41f3b35b51335a Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Fri, 2 May 2025 15:16:07 +0200 Subject: [PATCH 043/197] Always set max tip cap to 1 --- tests/load/execute.go | 3 +-- tests/load/generate/self.go | 4 ++-- tests/load/ginkgo_test.go | 1 - 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/tests/load/execute.go b/tests/load/execute.go index 3083777dc1dd..462ff3895e26 100644 --- a/tests/load/execute.go +++ b/tests/load/execute.go @@ -32,7 +32,6 @@ import ( type config struct { endpoints []string maxFeeCap int64 - maxTipCap int64 agents uint txsPerAgent uint64 } @@ -65,7 +64,7 @@ func execute(ctx context.Context, preFundedKeys []*secp256k1.PrivateKey, config return fmt.Errorf("dialing %s: %w", endpoint, err) } generator, err := generate.NewSelf(ctx, client, - big.NewInt(config.maxTipCap), big.NewInt(config.maxFeeCap), keys[i]) + big.NewInt(config.maxFeeCap), keys[i]) if err != nil { return fmt.Errorf("creating generator: %w", err) } diff --git a/tests/load/generate/self.go b/tests/load/generate/self.go index 8804dbc974ce..05d77b27eefd 100644 --- a/tests/load/generate/self.go +++ b/tests/load/generate/self.go @@ -33,7 +33,7 @@ type Self struct { } func NewSelf(ctx context.Context, client SelfClient, - maxTipCap, maxFeeCap *big.Int, key *ecdsa.PrivateKey, + maxFeeCap *big.Int, key *ecdsa.PrivateKey, ) (*Self, error) { address := ethcrypto.PubkeyToAddress(key.PublicKey) blockNumber := (*big.Int)(nil) @@ -43,7 +43,7 @@ func NewSelf(ctx context.Context, client SelfClient, } bigGwei := big.NewInt(params.GWei) - gasTipCap := new(big.Int).Mul(bigGwei, maxTipCap) + gasTipCap := new(big.Int).Mul(bigGwei, big.NewInt(1)) gasFeeCap := new(big.Int).Mul(bigGwei, maxFeeCap) chainID, err := client.ChainID(ctx) if err != nil { diff --git a/tests/load/ginkgo_test.go b/tests/load/ginkgo_test.go index 545036bc15f7..259cbd76b386 100644 --- a/tests/load/ginkgo_test.go +++ b/tests/load/ginkgo_test.go @@ -73,7 +73,6 @@ var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { config := config{ endpoints: endpoints, maxFeeCap: 5000, - maxTipCap: 1, agents: 60, txsPerAgent: 100000, } From ea16ed3500e08071dddd8f29be3e73e0fcf057fe Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Fri, 2 May 2025 15:17:06 +0200 Subject: [PATCH 044/197] Require largest balance address to remain with min funds --- tests/load/funder.go | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/load/funder.go b/tests/load/funder.go index d8cdf323f075..39ded4099ad5 100644 --- a/tests/load/funder.go +++ b/tests/load/funder.go @@ -57,6 +57,7 @@ func ensureMinimumFunds(ctx context.Context, endpoint string, keys []*ecdsa.Priv totalFundsRequired.Add(totalFundsRequired, diff) needFundsKeys[key] = diff } + totalFundsRequired.Add(totalFundsRequired, minFundsPerAddr) // for largest balance key if len(needFundsKeys) == 0 { return nil From ba4e3774354ed3c88655c507b3c69a0adae416e2 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Fri, 2 May 2025 15:24:57 +0200 Subject: [PATCH 045/197] Prevent new head subscriber race condition on exit --- tests/load/listen/newhead.go | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/tests/load/listen/newhead.go b/tests/load/listen/newhead.go index ff4d93345d19..986b826656fa 100644 --- a/tests/load/listen/newhead.go +++ b/tests/load/listen/newhead.go @@ -6,6 +6,7 @@ package listen import ( "context" "fmt" + "sync" "github.com/ava-labs/libevm/core/types" @@ -21,6 +22,9 @@ type headNotifier struct { listenStop chan<- struct{} listenDone <-chan struct{} subscription ethereum.Subscription + + stopMutex sync.Mutex + stopped bool } func newHeadNotifier(client NewHeadSubscriber) *headNotifier { @@ -52,8 +56,13 @@ func (n *headNotifier) start() (newHead <-chan struct{}, runError <-chan error, } func (n *headNotifier) stop() { - n.subscription.Unsubscribe() - close(n.listenStop) + n.stopMutex.Lock() + defer n.stopMutex.Unlock() + if !n.stopped { + n.subscription.Unsubscribe() + close(n.listenStop) + n.stopped = true + } <-n.listenDone } From db3010a76f78c4c7c85fca5deecd12a9e3682b7a Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Fri, 2 May 2025 16:29:20 +0200 Subject: [PATCH 046/197] Fix minimum funds calculation --- tests/load/execute.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/load/execute.go b/tests/load/execute.go index 462ff3895e26..b1f6d62ac62b 100644 --- a/tests/load/execute.go +++ b/tests/load/execute.go @@ -11,11 +11,11 @@ import ( "os" "time" - "github.com/ava-labs/coreth/params" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/ethclient" "github.com/ava-labs/libevm/log" + "github.com/ava-labs/libevm/params" "github.com/prometheus/client_golang/prometheus" "github.com/ava-labs/avalanchego/tests/load/agent" @@ -46,7 +46,10 @@ func execute(ctx context.Context, preFundedKeys []*secp256k1.PrivateKey, config } // Minimum to fund gas for all of the transactions for an address: - minFundsPerAddr := new(big.Int).SetUint64(params.GWei * uint64(config.maxFeeCap) * params.TxGas * config.txsPerAgent) + minFundsPerAddr := big.NewInt(params.GWei) + minFundsPerAddr = minFundsPerAddr.Mul(minFundsPerAddr, big.NewInt(config.maxFeeCap)) + minFundsPerAddr = minFundsPerAddr.Mul(minFundsPerAddr, new(big.Int).SetUint64(params.TxGas)) + minFundsPerAddr = minFundsPerAddr.Mul(minFundsPerAddr, new(big.Int).SetUint64(config.txsPerAgent)) err = ensureMinimumFunds(ctx, config.endpoints[0], keys, minFundsPerAddr) if err != nil { return fmt.Errorf("ensuring minimum funds: %w", err) From f22abadedfdecc1948a8d7043ccf41844a74b9ec Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Mon, 5 May 2025 10:36:04 +0200 Subject: [PATCH 047/197] issue period configuration option --- tests/load/execute.go | 3 ++- tests/load/funder.go | 8 +++++++- tests/load/ginkgo_test.go | 2 ++ tests/load/issue/issuer.go | 28 ++++++++++++++++++++++------ 4 files changed, 33 insertions(+), 8 deletions(-) diff --git a/tests/load/execute.go b/tests/load/execute.go index b1f6d62ac62b..8866f14d89fc 100644 --- a/tests/load/execute.go +++ b/tests/load/execute.go @@ -34,6 +34,7 @@ type config struct { maxFeeCap int64 agents uint txsPerAgent uint64 + issuePeriod time.Duration } func execute(ctx context.Context, preFundedKeys []*secp256k1.PrivateKey, config config) error { @@ -71,7 +72,7 @@ func execute(ctx context.Context, preFundedKeys []*secp256k1.PrivateKey, config if err != nil { return fmt.Errorf("creating generator: %w", err) } - issuer := issue.New(client, tracker) + issuer := issue.New(client, tracker, config.issuePeriod) address := ethcrypto.PubkeyToAddress(keys[i].PublicKey) listener := listen.New(client, tracker, config.txsPerAgent, address) agents[i] = agent.New[*types.Transaction, common.Hash](config.txsPerAgent, generator, issuer, listener, tracker) diff --git a/tests/load/funder.go b/tests/load/funder.go index 39ded4099ad5..6d3fcdb92565 100644 --- a/tests/load/funder.go +++ b/tests/load/funder.go @@ -77,7 +77,8 @@ func ensureMinimumFunds(ctx context.Context, endpoint string, keys []*ecdsa.Priv return fmt.Errorf("creating distribution generator: %w", err) } tracker := tracker.NewCounter() - issuer := issue.New(client, tracker) + const issuePeriod = 0 // no delay between transaction issuance + issuer := issue.New(client, tracker, issuePeriod) listener := listen.New(client, tracker, txTarget, maxFundsAddress) agents := []*agent.Agent[*types.Transaction, common.Hash]{ agent.New(txTarget, generator, issuer, listener, tracker), @@ -140,3 +141,8 @@ func checkBalancesHaveMin(ctx context.Context, client ethClientBalancer, keys [] } return nil } + +// end goal for time per block + +// Opcode distribution - debug trace block endpoint +// Solidity contract with opcode input read/write to different addresses (new) in storage, or application level contracts like uniswap, erc20 diff --git a/tests/load/ginkgo_test.go b/tests/load/ginkgo_test.go index 259cbd76b386..f1ac392d1ecd 100644 --- a/tests/load/ginkgo_test.go +++ b/tests/load/ginkgo_test.go @@ -6,6 +6,7 @@ package load import ( "context" "testing" + "time" "github.com/onsi/ginkgo/v2" "github.com/stretchr/testify/require" @@ -75,6 +76,7 @@ var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { maxFeeCap: 5000, agents: 60, txsPerAgent: 100000, + issuePeriod: 20 * time.Millisecond, } err = execute(ctx, network.PreFundedKeys, config) if err != nil { diff --git a/tests/load/issue/issuer.go b/tests/load/issue/issuer.go index e9b1b45233e7..e74f65d1ff20 100644 --- a/tests/load/issue/issuer.go +++ b/tests/load/issue/issuer.go @@ -6,6 +6,7 @@ package issue import ( "context" "fmt" + "time" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/types" @@ -22,30 +23,45 @@ type Tracker interface { // Issuer issues transactions to a node. type Issuer struct { // Injected parameters - client EthClient - tracker Tracker + client EthClient + tracker Tracker + issuePeriod time.Duration // State + lastIssue time.Time lastIssuedNonce uint64 // for programming assumptions checks only } -func New(client EthClient, tracker Tracker) *Issuer { +func New(client EthClient, tracker Tracker, issuePeriod time.Duration) *Issuer { return &Issuer{ - client: client, - tracker: tracker, + client: client, + tracker: tracker, + issuePeriod: issuePeriod, } } func (i *Issuer) IssueTx(ctx context.Context, tx *types.Transaction) error { + if i.issuePeriod > 0 && !i.lastIssue.IsZero() && + time.Since(i.lastIssue) < i.issuePeriod { + timer := time.NewTimer(i.issuePeriod - time.Since(i.lastIssue)) + select { + case <-timer.C: + case <-ctx.Done(): + timer.Stop() + return ctx.Err() + } + } + txHash, txNonce := tx.Hash(), tx.Nonce() if i.lastIssuedNonce > 0 && txNonce != i.lastIssuedNonce+1 { // the listener relies on this being true return fmt.Errorf("transaction nonce %d is not equal to the last issued nonce %d plus one", txNonce, i.lastIssuedNonce) } if err := i.client.SendTransaction(ctx, tx); err != nil { - return err + return fmt.Errorf("issuing transaction with nonce %d: %w", txNonce, err) } i.tracker.Issue(txHash) i.lastIssuedNonce = txNonce + i.lastIssue = time.Now() return nil } From 77086f48806ce87e21a52de48b2cec6be41be107 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Mon, 5 May 2025 10:42:09 +0200 Subject: [PATCH 048/197] Final timeout config option --- tests/load/execute.go | 13 +++++++------ tests/load/ginkgo_test.go | 11 ++++++----- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/tests/load/execute.go b/tests/load/execute.go index 8866f14d89fc..969f63f702df 100644 --- a/tests/load/execute.go +++ b/tests/load/execute.go @@ -30,11 +30,12 @@ import ( ) type config struct { - endpoints []string - maxFeeCap int64 - agents uint - txsPerAgent uint64 - issuePeriod time.Duration + endpoints []string + maxFeeCap int64 + agents uint + txsPerAgent uint64 + issuePeriod time.Duration + finalTimeout time.Duration } func execute(ctx context.Context, preFundedKeys []*secp256k1.PrivateKey, config config) error { @@ -85,7 +86,7 @@ func execute(ctx context.Context, preFundedKeys []*secp256k1.PrivateKey, config orchestratorCtx, orchestratorCancel := context.WithCancel(ctx) defer orchestratorCancel() - orchestrator := orchestrate.NewBurstOrchestrator(agents, time.Second) + orchestrator := orchestrate.NewBurstOrchestrator(agents, config.finalTimeout) orchestratorErrCh := make(chan error) go func() { orchestratorErrCh <- orchestrator.Execute(orchestratorCtx) diff --git a/tests/load/ginkgo_test.go b/tests/load/ginkgo_test.go index f1ac392d1ecd..40e13cf93c07 100644 --- a/tests/load/ginkgo_test.go +++ b/tests/load/ginkgo_test.go @@ -72,11 +72,12 @@ var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { endpoints, err := tmpnet.GetNodeWebsocketURIs(network.Nodes, blockchainID) require.NoError(ginkgo.GinkgoT(), err, "getting node websocket URIs") config := config{ - endpoints: endpoints, - maxFeeCap: 5000, - agents: 60, - txsPerAgent: 100000, - issuePeriod: 20 * time.Millisecond, + endpoints: endpoints, + maxFeeCap: 5000, + agents: 60, + txsPerAgent: 100000, + issuePeriod: 20 * time.Millisecond, + finalTimeout: time.Minute, } err = execute(ctx, network.PreFundedKeys, config) if err != nil { From c4fa76c0c88431b95be160e5742fc4bf78fc7102 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Mon, 5 May 2025 12:13:19 +0200 Subject: [PATCH 049/197] Add Prometheus and grafana instructions and configs --- tests/load/.gitignore | 2 + tests/load/dashboard.json | 763 +++++++++++++++++++++++++++++ tests/load/prometheus.template.yml | 17 + tests/load/readme.md | 43 ++ 4 files changed, 825 insertions(+) create mode 100644 tests/load/.gitignore create mode 100644 tests/load/dashboard.json create mode 100644 tests/load/prometheus.template.yml create mode 100644 tests/load/readme.md diff --git a/tests/load/.gitignore b/tests/load/.gitignore new file mode 100644 index 000000000000..e5993540470e --- /dev/null +++ b/tests/load/.gitignore @@ -0,0 +1,2 @@ +prometheus.yml +/data diff --git a/tests/load/dashboard.json b/tests/load/dashboard.json new file mode 100644 index 000000000000..a574f3e7f114 --- /dev/null +++ b/tests/load/dashboard.json @@ -0,0 +1,763 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 1, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "dekh3fflmqzggf" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "purple", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "txs" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 0, + "y": 0 + }, + "id": 9, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.6.1", + "targets": [ + { + "editorMode": "code", + "expr": "sum(txs_issued)", + "legendFormat": "Confirmed", + "range": true, + "refId": "A" + } + ], + "title": "Issued txs", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "dekh3fflmqzggf" + }, + "description": "Issued transactions which are confirmed and accepted in a block", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "super-light-green", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "txs" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 4, + "y": 0 + }, + "id": 7, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.6.1", + "targets": [ + { + "editorMode": "code", + "expr": "sum(txs_confirmed)", + "legendFormat": "Confirmed", + "range": true, + "refId": "A" + } + ], + "title": "Confirmed txs", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "dekh3fflmqzggf" + }, + "description": "Issued transactions which are confirmed and accepted in a block", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "semi-dark-red", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "txs" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 8, + "y": 0 + }, + "id": 8, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.6.1", + "targets": [ + { + "editorMode": "code", + "expr": "sum(txs_failed)", + "legendFormat": "Confirmed", + "range": true, + "refId": "A" + } + ], + "title": "Failed txs", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "dekh3fflmqzggf" + }, + "description": "Transactions issued but not yet confirmed or failed", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "super-light-orange", + "mode": "shades" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "txs" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 12, + "y": 0 + }, + "id": 10, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.6.1", + "targets": [ + { + "editorMode": "code", + "expr": "sum(txs_issued)-sum(txs_confirmed)-sum(txs_failed)", + "legendFormat": "Confirmed", + "range": true, + "refId": "A" + } + ], + "title": "In flight txs", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "dekh3fflmqzggf" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "super-light-purple", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 5, + "x": 0, + "y": 4 + }, + "id": 5, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.6.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "dekh3fflmqzggf" + }, + "editorMode": "code", + "expr": "sum(rate(avalanche_evm_eth_chain_block_inserts[1m]))/sum(rate(avalanche_evm_eth_chain_block_inserts_count[1m]))", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Insert time per block", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "dekh3fflmqzggf" + }, + "description": "Gas used in processed block", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "super-light-purple", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 5, + "y": 4 + }, + "id": 11, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.6.1", + "targets": [ + { + "editorMode": "code", + "expr": "sum(avalanche_evm_eth_chain_block_gas_used_processed) / sum(avalanche_evm_eth_chain_block_inserts_count)", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Gas per block", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "dekh3fflmqzggf" + }, + "description": "Number of transactions processed per block", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "super-light-purple", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 9, + "y": 4 + }, + "id": 12, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.6.1", + "targets": [ + { + "editorMode": "code", + "expr": "sum(avalanche_evm_eth_chain_txs_processed) / sum(avalanche_evm_eth_chain_block_inserts_count)", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Txs per block", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "dekh3fflmqzggf" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "super-light-yellow", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + }, + "unit": "wei" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 3, + "x": 13, + "y": 4 + }, + "id": 13, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.6.1", + "targets": [ + { + "editorMode": "code", + "expr": "sum(avalanche_evm_eth_chain_latest_basefee)", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Base fee", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "dekh3fflmqzggf" + }, + "description": "Number of blocks processed per second", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "light-blue", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 5, + "x": 0, + "y": 8 + }, + "id": 15, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.6.1", + "targets": [ + { + "editorMode": "code", + "expr": "sum(rate(avalanche_evm_eth_chain_block_inserts[1m]))", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Blocks per second", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "dekh3fflmqzggf" + }, + "description": "Number of gas processed per second", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "light-blue", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 5, + "y": 8 + }, + "id": 16, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.6.1", + "targets": [ + { + "editorMode": "code", + "expr": "sum(rate(avalanche_evm_eth_chain_block_gas_used_processed[1m]))", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Gas per second", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "dekh3fflmqzggf" + }, + "description": "Number of transactions processed per second", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "light-blue", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 9, + "y": 8 + }, + "id": 14, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.6.1", + "targets": [ + { + "editorMode": "code", + "expr": "sum(rate(avalanche_evm_eth_chain_txs_processed[60s]))", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Txs per second", + "type": "stat" + } + ], + "preload": true, + "schemaVersion": 41, + "tags": [], + "templating": { + "list": [ + { + "baseFilters": [], + "datasource": { + "type": "prometheus", + "uid": "eegbq9z6fow74e" + }, + "filters": [ + { + "condition": "", + "key": "chain", + "keyLabel": "chain", + "operator": "=", + "value": "C", + "valueLabels": [ + "C" + ] + } + ], + "name": "filter", + "type": "adhoc" + } + ] + }, + "time": { + "from": "now-5m", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Load testing", + "uid": "aejze3k4d0mpsb", + "version": 6 +} \ No newline at end of file diff --git a/tests/load/prometheus.template.yml b/tests/load/prometheus.template.yml new file mode 100644 index 000000000000..d30b080c9870 --- /dev/null +++ b/tests/load/prometheus.template.yml @@ -0,0 +1,17 @@ +global: + scrape_interval: 10s + evaluation_interval: 5s + scrape_timeout: 5s + +scrape_configs: + - job_name: "load" + metrics_path: "/ext/metrics" + static_configs: + - targets: ["localhost:8082"] + + - job_name: "tmpnet" + metrics_path: "/ext/metrics" + file_sd_configs: + - files: + - "${HOME}/.tmpnet/prometheus/file_sd_configs/*.json" + refresh_interval: 1m diff --git a/tests/load/readme.md b/tests/load/readme.md new file mode 100644 index 000000000000..57d617277610 --- /dev/null +++ b/tests/load/readme.md @@ -0,0 +1,43 @@ +# Load testing + +## Prometheus + +1. Navigate to this directory with `cd tests/load`. +1. Setup the Prometheus configuration file: `envsubst < prometheus.template.yml > prometheus.yml` +1. Launch Prometheus using the dev shell: + + ```bash + nix develop + ``` + + ```nix + prometheus --config.file prometheus.yml + ``` + + This starts Prometheus listening on port `9090`. + +## Grafana + +1. In a separate terminal, install and launch the Grafana service: + + ```bash + brew install grafana + brew services start grafana + ``` + +1. Open Grafana in your browser at [localhost:3000](http://localhost:3000) and log in with the default credentials `admin` and `admin`. +1. Add a new Prometheus data source starting at [localhost:3000/connections/datasources/prometheus](http://localhost:3000/connections/datasources/prometheus) + 1. Click on "Add new data source" + 1. Name it `prometheus` + 1. In the Connection section, set the URL to `http://localhost:9090` + 1. Click the "Save & Test" button at the bottom to verify the connection. +1. Create a dashboard at [localhost:3000/dashboard/new?editview=json-model](http://localhost:3000/dashboard/new?editview=json-model) and paste the JSON content of [`dashboard.json`](dashboard.json) into the text area, and click "Save changes". +1. Open the Load testing dashboard at [localhost:3000/d/aejze3k4d0mpsb/load-testing](http://localhost:3000/d/aejze3k4d0mpsb/load-testing) + +## Run the load test + +From the root of the repository: + +```bash +./bin/ginkgo -v tests/load -- --avalanchego-path=$PWD/build/avalanchego +``` From 435a0f1a97cbaa6539db315f2868d7be2cf42b48 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Mon, 5 May 2025 12:44:14 +0200 Subject: [PATCH 050/197] Log tracker stats every 10s --- tests/load/agent/dependencies.go | 3 +++ tests/load/orchestrate/burst.go | 9 +++++++++ tests/load/tracker/counter.go | 1 + tests/load/tracker/tracker.go | 15 +++++++++++++++ 4 files changed, 28 insertions(+) diff --git a/tests/load/agent/dependencies.go b/tests/load/agent/dependencies.go index 4968620ba4b2..f61923ea34d3 100644 --- a/tests/load/agent/dependencies.go +++ b/tests/load/agent/dependencies.go @@ -42,4 +42,7 @@ type Tracker[T comparable] interface { // GetObservedFailed returns the number of transactions that the tracker has // confirmed failed. GetObservedFailed() uint64 + + // Log logs the current state of the tracker. + Log() } diff --git a/tests/load/orchestrate/burst.go b/tests/load/orchestrate/burst.go index 08f53ac94033..97f78279850b 100644 --- a/tests/load/orchestrate/burst.go +++ b/tests/load/orchestrate/burst.go @@ -42,6 +42,9 @@ func (o *BurstOrchestrator[T, U]) Execute(ctx context.Context) error { observerGroup.Go(func() error { return agent.Listener.Listen(observerCtx) }) } + const logInterval = 10 * time.Second + logTicker := time.NewTicker(logInterval) + // start issuing transactions sequentially from each issuer issuerGroup := errgroup.Group{} for _, agent := range o.agents { @@ -56,6 +59,12 @@ func (o *BurstOrchestrator[T, U]) Execute(ctx context.Context) error { } agent.Listener.RegisterIssued(tx) + + select { + case <-logTicker.C: + agent.Tracker.Log() + default: + } } return nil }) diff --git a/tests/load/tracker/counter.go b/tests/load/tracker/counter.go index 5d9b8fef97bb..7e3b9fe3d50b 100644 --- a/tests/load/tracker/counter.go +++ b/tests/load/tracker/counter.go @@ -24,3 +24,4 @@ func (c *Counter) ObserveFailed(_ common.Hash) { c.failed++ } func (*Counter) ObserveBlock(_ uint64) {} func (c *Counter) GetObservedConfirmed() uint64 { return c.confirmed } func (c *Counter) GetObservedFailed() uint64 { return c.failed } +func (*Counter) Log() {} diff --git a/tests/load/tracker/tracker.go b/tests/load/tracker/tracker.go index 7b73a2b6941b..e08ffd1f3dc7 100644 --- a/tests/load/tracker/tracker.go +++ b/tests/load/tracker/tracker.go @@ -8,6 +8,7 @@ import ( "time" "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/log" ) type Tracker struct { @@ -16,6 +17,7 @@ type Tracker struct { metrics *metrics stats struct { + issued uint64 confirmed uint64 failed uint64 } @@ -40,6 +42,7 @@ func (t *Tracker) Issue(txHash common.Hash) { defer t.mutex.Unlock() t.metrics.issued.Inc() + t.stats.issued++ t.txHashToLastTime[txHash] = t.timeNow() } @@ -81,3 +84,15 @@ func (t *Tracker) GetObservedFailed() uint64 { defer t.mutex.Unlock() return t.stats.failed } + +func (t *Tracker) Log() { + t.mutex.Lock() + defer t.mutex.Unlock() + + log.Info("Tracker stats", + "issued", t.stats.issued, + "confirmed", t.stats.confirmed, + "failed", t.stats.failed, + "inflight", len(t.txHashToLastTime), + ) +} From e2dd77fbbcc05b951ecc3299d8edd6762e3a9e1f Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Mon, 5 May 2025 12:46:57 +0200 Subject: [PATCH 051/197] Change counter tracker to noop tracker --- tests/load/funder.go | 2 +- tests/load/tracker/counter.go | 27 --------------------------- tests/load/tracker/noop.go | 23 +++++++++++++++++++++++ 3 files changed, 24 insertions(+), 28 deletions(-) delete mode 100644 tests/load/tracker/counter.go create mode 100644 tests/load/tracker/noop.go diff --git a/tests/load/funder.go b/tests/load/funder.go index 6d3fcdb92565..8858f67e3f46 100644 --- a/tests/load/funder.go +++ b/tests/load/funder.go @@ -76,7 +76,7 @@ func ensureMinimumFunds(ctx context.Context, endpoint string, keys []*ecdsa.Priv if err != nil { return fmt.Errorf("creating distribution generator: %w", err) } - tracker := tracker.NewCounter() + tracker := tracker.NewNoop() const issuePeriod = 0 // no delay between transaction issuance issuer := issue.New(client, tracker, issuePeriod) listener := listen.New(client, tracker, txTarget, maxFundsAddress) diff --git a/tests/load/tracker/counter.go b/tests/load/tracker/counter.go deleted file mode 100644 index 7e3b9fe3d50b..000000000000 --- a/tests/load/tracker/counter.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package tracker - -import ( - "github.com/ava-labs/libevm/common" -) - -// Counter only counts the number of transactions confirmed or failed, -// so that it is accessible through Go code. -type Counter struct { - confirmed uint64 - failed uint64 -} - -func NewCounter() *Counter { - return &Counter{} -} - -func (*Counter) Issue(_ common.Hash) {} -func (c *Counter) ObserveConfirmed(_ common.Hash) { c.confirmed++ } -func (c *Counter) ObserveFailed(_ common.Hash) { c.failed++ } -func (*Counter) ObserveBlock(_ uint64) {} -func (c *Counter) GetObservedConfirmed() uint64 { return c.confirmed } -func (c *Counter) GetObservedFailed() uint64 { return c.failed } -func (*Counter) Log() {} diff --git a/tests/load/tracker/noop.go b/tests/load/tracker/noop.go new file mode 100644 index 000000000000..7e6a8d1b89a7 --- /dev/null +++ b/tests/load/tracker/noop.go @@ -0,0 +1,23 @@ +// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package tracker + +import ( + "github.com/ava-labs/libevm/common" +) + +// Noop is a no-op implementation of the tracker. +type Noop struct{} + +func NewNoop() *Noop { + return &Noop{} +} + +func (Noop) Issue(common.Hash) {} +func (Noop) ObserveConfirmed(common.Hash) {} +func (Noop) ObserveFailed(common.Hash) {} +func (Noop) ObserveBlock(uint64) {} +func (Noop) GetObservedConfirmed() uint64 { return 0 } +func (Noop) GetObservedFailed() uint64 { return 0 } +func (Noop) Log() {} From 19d3b3a1a59ce4974b0e931de644729594b108ac Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Tue, 6 May 2025 14:52:49 +0200 Subject: [PATCH 052/197] Merge generator and issuer together --- tests/load/agent/agent.go | 19 ++- tests/load/agent/dependencies.go | 11 +- tests/load/execute.go | 10 +- tests/load/funder.go | 10 +- tests/load/generate/self.go | 80 ------------ tests/load/ginkgo_test.go | 2 +- .../distribute.go => issue/distributor.go} | 52 ++++---- tests/load/issue/issuer.go | 67 ---------- tests/load/issue/self.go | 116 ++++++++++++++++++ tests/load/orchestrate/burst.go | 7 +- 10 files changed, 166 insertions(+), 208 deletions(-) delete mode 100644 tests/load/generate/self.go rename tests/load/{generate/distribute.go => issue/distributor.go} (75%) delete mode 100644 tests/load/issue/issuer.go create mode 100644 tests/load/issue/self.go diff --git a/tests/load/agent/agent.go b/tests/load/agent/agent.go index 490cb4b1b13d..ffa58774c00c 100644 --- a/tests/load/agent/agent.go +++ b/tests/load/agent/agent.go @@ -4,25 +4,22 @@ package agent type Agent[T, U comparable] struct { - TxTarget uint64 - Generator TxGenerator[T] - Issuer Issuer[T] - Listener Listener[T] - Tracker Tracker[U] + TxTarget uint64 + Issuer Issuer[T] + Listener Listener[T] + Tracker Tracker[U] } func New[T, U comparable]( txTarget uint64, - generator TxGenerator[T], issuer Issuer[T], listener Listener[T], tracker Tracker[U], ) *Agent[T, U] { return &Agent[T, U]{ - TxTarget: txTarget, - Generator: generator, - Issuer: issuer, - Listener: listener, - Tracker: tracker, + TxTarget: txTarget, + Issuer: issuer, + Listener: listener, + Tracker: tracker, } } diff --git a/tests/load/agent/dependencies.go b/tests/load/agent/dependencies.go index f61923ea34d3..9b0aaa28d044 100644 --- a/tests/load/agent/dependencies.go +++ b/tests/load/agent/dependencies.go @@ -5,15 +5,10 @@ package agent import "context" -type TxGenerator[T comparable] interface { - // GenerateTx returns a valid transaction. - GenerateTx() (T, error) -} - type Issuer[T comparable] interface { - // Issue sends a tx to the network, and informs the tracker that its sent - // said transaction. - IssueTx(ctx context.Context, tx T) error + // GenerateAndIssueTx generates and sends a tx to the network, and informs the + // tracker that its sent said transaction. It returns the send transaction. + GenerateAndIssueTx(ctx context.Context) (tx T, err error) } type Listener[T comparable] interface { diff --git a/tests/load/execute.go b/tests/load/execute.go index 969f63f702df..62a9ed9f709c 100644 --- a/tests/load/execute.go +++ b/tests/load/execute.go @@ -19,7 +19,6 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/ava-labs/avalanchego/tests/load/agent" - "github.com/ava-labs/avalanchego/tests/load/generate" "github.com/ava-labs/avalanchego/tests/load/issue" "github.com/ava-labs/avalanchego/tests/load/listen" "github.com/ava-labs/avalanchego/tests/load/orchestrate" @@ -68,15 +67,14 @@ func execute(ctx context.Context, preFundedKeys []*secp256k1.PrivateKey, config if err != nil { return fmt.Errorf("dialing %s: %w", endpoint, err) } - generator, err := generate.NewSelf(ctx, client, - big.NewInt(config.maxFeeCap), keys[i]) + issuer, err := issue.NewSelf(ctx, client, tracker, + big.NewInt(config.maxFeeCap), keys[i], config.issuePeriod) if err != nil { - return fmt.Errorf("creating generator: %w", err) + return fmt.Errorf("creating issuer: %w", err) } - issuer := issue.New(client, tracker, config.issuePeriod) address := ethcrypto.PubkeyToAddress(keys[i].PublicKey) listener := listen.New(client, tracker, config.txsPerAgent, address) - agents[i] = agent.New[*types.Transaction, common.Hash](config.txsPerAgent, generator, issuer, listener, tracker) + agents[i] = agent.New[*types.Transaction, common.Hash](config.txsPerAgent, issuer, listener, tracker) } metricsErrCh, err := metricsServer.Start() diff --git a/tests/load/funder.go b/tests/load/funder.go index 8858f67e3f46..ae8931f0cd29 100644 --- a/tests/load/funder.go +++ b/tests/load/funder.go @@ -18,7 +18,6 @@ import ( "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/avalanchego/tests/load/agent" - "github.com/ava-labs/avalanchego/tests/load/generate" "github.com/ava-labs/avalanchego/tests/load/issue" "github.com/ava-labs/avalanchego/tests/load/listen" "github.com/ava-labs/avalanchego/tests/load/orchestrate" @@ -72,16 +71,15 @@ func ensureMinimumFunds(ctx context.Context, endpoint string, keys []*ecdsa.Priv maxFundsAddress := ethcrypto.PubkeyToAddress(maxFundsKey.PublicKey) txTarget := uint64(len(needFundsKeys)) - generator, err := generate.NewDistributor(ctx, client, maxFundsKey, needFundsKeys) + const issuePeriod = 0 // no delay between transaction issuance + issuer, err := issue.NewDistributor(ctx, client, maxFundsKey, needFundsKeys) if err != nil { - return fmt.Errorf("creating distribution generator: %w", err) + return fmt.Errorf("creating issuer: %w", err) } tracker := tracker.NewNoop() - const issuePeriod = 0 // no delay between transaction issuance - issuer := issue.New(client, tracker, issuePeriod) listener := listen.New(client, tracker, txTarget, maxFundsAddress) agents := []*agent.Agent[*types.Transaction, common.Hash]{ - agent.New(txTarget, generator, issuer, listener, tracker), + agent.New(txTarget, issuer, listener, tracker), } orchestrator := orchestrate.NewBurstOrchestrator(agents, time.Second) diff --git a/tests/load/generate/self.go b/tests/load/generate/self.go deleted file mode 100644 index 05d77b27eefd..000000000000 --- a/tests/load/generate/self.go +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package generate - -import ( - "context" - "crypto/ecdsa" - "fmt" - "math/big" - - "github.com/ava-labs/coreth/params" - "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/core/types" - - ethcrypto "github.com/ava-labs/libevm/crypto" -) - -type SelfClient interface { - ChainID(ctx context.Context) (*big.Int, error) - NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) -} - -// Self generates transactions sending 0 fund to the sender key. -type Self struct { - key *ecdsa.PrivateKey - address common.Address // corresponding to key - nonce uint64 - signer types.Signer - chainID *big.Int - gasTipCap *big.Int - gasFeeCap *big.Int -} - -func NewSelf(ctx context.Context, client SelfClient, - maxFeeCap *big.Int, key *ecdsa.PrivateKey, -) (*Self, error) { - address := ethcrypto.PubkeyToAddress(key.PublicKey) - blockNumber := (*big.Int)(nil) - nonce, err := client.NonceAt(ctx, address, blockNumber) - if err != nil { - return nil, fmt.Errorf("getting nonce for address %s: %w", address, err) - } - - bigGwei := big.NewInt(params.GWei) - gasTipCap := new(big.Int).Mul(bigGwei, big.NewInt(1)) - gasFeeCap := new(big.Int).Mul(bigGwei, maxFeeCap) - chainID, err := client.ChainID(ctx) - if err != nil { - return nil, fmt.Errorf("getting chain id: %w", err) - } - - return &Self{ - key: key, - address: address, - nonce: nonce, - signer: types.LatestSignerForChainID(chainID), - chainID: chainID, - gasTipCap: gasTipCap, - gasFeeCap: gasFeeCap, - }, nil -} - -func (s *Self) GenerateTx() (*types.Transaction, error) { - tx, err := types.SignNewTx(s.key, s.signer, &types.DynamicFeeTx{ - ChainID: s.chainID, - Nonce: s.nonce, - GasTipCap: s.gasTipCap, - GasFeeCap: s.gasFeeCap, - Gas: params.TxGas, - To: &s.address, // self - Data: nil, - Value: common.Big0, - }) - if err != nil { - return nil, err - } - s.nonce++ - return tx, nil -} diff --git a/tests/load/ginkgo_test.go b/tests/load/ginkgo_test.go index 40e13cf93c07..ad0470d86a97 100644 --- a/tests/load/ginkgo_test.go +++ b/tests/load/ginkgo_test.go @@ -74,7 +74,7 @@ var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { config := config{ endpoints: endpoints, maxFeeCap: 5000, - agents: 60, + agents: 5, txsPerAgent: 100000, issuePeriod: 20 * time.Millisecond, finalTimeout: time.Minute, diff --git a/tests/load/generate/distribute.go b/tests/load/issue/distributor.go similarity index 75% rename from tests/load/generate/distribute.go rename to tests/load/issue/distributor.go index 9f7e7e73acdc..10b3eb0aa03b 100644 --- a/tests/load/generate/distribute.go +++ b/tests/load/issue/distributor.go @@ -1,12 +1,11 @@ // Copyright (C) 2025, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package generate +package issue import ( "context" "crypto/ecdsa" - "errors" "fmt" "maps" "math/big" @@ -19,25 +18,31 @@ import ( ) type DistributorClient interface { - SelfClient + EthClient EstimateBaseFee(ctx context.Context) (*big.Int, error) SuggestGasTipCap(ctx context.Context) (*big.Int, error) } -// Distributor +// Issuer issues transactions to a node. type Distributor struct { - from *ecdsa.PrivateKey - address common.Address // corresponding to `from` - nonce uint64 - to map[*ecdsa.PrivateKey]*big.Int + // Injected parameters + client EthClient + from *ecdsa.PrivateKey + to map[*ecdsa.PrivateKey]*big.Int + + // Determined by constructor + address common.Address // corresponding to key signer types.Signer chainID *big.Int - gasTipCap *big.Int gasFeeCap *big.Int + gasTipCap *big.Int + + // State + nonce uint64 } -func NewDistributor(ctx context.Context, client DistributorClient, from *ecdsa.PrivateKey, - to map[*ecdsa.PrivateKey]*big.Int, +func NewDistributor(ctx context.Context, client DistributorClient, + from *ecdsa.PrivateKey, to map[*ecdsa.PrivateKey]*big.Int, ) (*Distributor, error) { address := ethcrypto.PubkeyToAddress(from.PublicKey) blockNumber := (*big.Int)(nil) @@ -46,11 +51,6 @@ func NewDistributor(ctx context.Context, client DistributorClient, from *ecdsa.P return nil, fmt.Errorf("getting nonce for address %s: %w", address, err) } - chainID, err := client.ChainID(ctx) - if err != nil { - return nil, fmt.Errorf("getting chain ID: %w", err) - } - gasTipCap, err := client.SuggestGasTipCap(ctx) if err != nil { return nil, fmt.Errorf("getting suggested gas tip cap: %w", err) @@ -61,25 +61,25 @@ func NewDistributor(ctx context.Context, client DistributorClient, from *ecdsa.P return nil, fmt.Errorf("getting estimated base fee: %w", err) } + chainID, err := client.ChainID(ctx) + if err != nil { + return nil, fmt.Errorf("getting chain id: %w", err) + } + return &Distributor{ + client: client, from: from, address: address, nonce: nonce, to: maps.Clone(to), // need to clone the map to avoid modifying the original signer: types.LatestSignerForChainID(chainID), chainID: chainID, - gasTipCap: gasTipCap, gasFeeCap: gasFeeCap, + gasTipCap: gasTipCap, }, nil } -func (d *Distributor) GenerateTx() (*types.Transaction, error) { - if len(d.to) == 0 { - // The caller of this function should prevent this error from being - // returned by bounding the number of transactions generated. - return nil, errors.New("no more keys to distribute funds to") - } - +func (d *Distributor) GenerateAndIssueTx(ctx context.Context) (*types.Transaction, error) { var toKey *ecdsa.PrivateKey var funds *big.Int for toKey, funds = range d.to { @@ -101,6 +101,10 @@ func (d *Distributor) GenerateTx() (*types.Transaction, error) { if err != nil { return nil, err } + + if err := d.client.SendTransaction(ctx, tx); err != nil { + return nil, fmt.Errorf("issuing transaction with nonce %d: %w", d.nonce, err) + } d.nonce++ return tx, nil } diff --git a/tests/load/issue/issuer.go b/tests/load/issue/issuer.go deleted file mode 100644 index e74f65d1ff20..000000000000 --- a/tests/load/issue/issuer.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package issue - -import ( - "context" - "fmt" - "time" - - "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/core/types" -) - -type EthClient interface { - SendTransaction(ctx context.Context, tx *types.Transaction) error -} - -type Tracker interface { - Issue(txHash common.Hash) -} - -// Issuer issues transactions to a node. -type Issuer struct { - // Injected parameters - client EthClient - tracker Tracker - issuePeriod time.Duration - - // State - lastIssue time.Time - lastIssuedNonce uint64 // for programming assumptions checks only -} - -func New(client EthClient, tracker Tracker, issuePeriod time.Duration) *Issuer { - return &Issuer{ - client: client, - tracker: tracker, - issuePeriod: issuePeriod, - } -} - -func (i *Issuer) IssueTx(ctx context.Context, tx *types.Transaction) error { - if i.issuePeriod > 0 && !i.lastIssue.IsZero() && - time.Since(i.lastIssue) < i.issuePeriod { - timer := time.NewTimer(i.issuePeriod - time.Since(i.lastIssue)) - select { - case <-timer.C: - case <-ctx.Done(): - timer.Stop() - return ctx.Err() - } - } - - txHash, txNonce := tx.Hash(), tx.Nonce() - if i.lastIssuedNonce > 0 && txNonce != i.lastIssuedNonce+1 { - // the listener relies on this being true - return fmt.Errorf("transaction nonce %d is not equal to the last issued nonce %d plus one", txNonce, i.lastIssuedNonce) - } - if err := i.client.SendTransaction(ctx, tx); err != nil { - return fmt.Errorf("issuing transaction with nonce %d: %w", txNonce, err) - } - i.tracker.Issue(txHash) - i.lastIssuedNonce = txNonce - i.lastIssue = time.Now() - return nil -} diff --git a/tests/load/issue/self.go b/tests/load/issue/self.go new file mode 100644 index 000000000000..d9420f4cd647 --- /dev/null +++ b/tests/load/issue/self.go @@ -0,0 +1,116 @@ +// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package issue + +import ( + "context" + "crypto/ecdsa" + "fmt" + "math/big" + "time" + + "github.com/ava-labs/coreth/params" + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/types" + + ethcrypto "github.com/ava-labs/libevm/crypto" +) + +type EthClient interface { + ChainID(ctx context.Context) (*big.Int, error) + NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) + SendTransaction(ctx context.Context, tx *types.Transaction) error +} + +type Tracker interface { + Issue(txHash common.Hash) +} + +// Self generates and issues transactions sending 0 fund to the sender. +type Self struct { + // Injected parameters + client EthClient + tracker Tracker + key *ecdsa.PrivateKey + gasFeeCap *big.Int + issuePeriod time.Duration + + // Determined by constructor + address common.Address // corresponding to key + signer types.Signer + chainID *big.Int + gasTipCap *big.Int + + // State + nonce uint64 + lastIssue time.Time +} + +func NewSelf(ctx context.Context, client EthClient, tracker Tracker, + maxFeeCap *big.Int, key *ecdsa.PrivateKey, issuePeriod time.Duration, +) (*Self, error) { + address := ethcrypto.PubkeyToAddress(key.PublicKey) + blockNumber := (*big.Int)(nil) + nonce, err := client.NonceAt(ctx, address, blockNumber) + if err != nil { + return nil, fmt.Errorf("getting nonce for address %s: %w", address, err) + } + + bigGwei := big.NewInt(params.GWei) + gasTipCap := new(big.Int).Mul(bigGwei, big.NewInt(1)) + gasFeeCap := new(big.Int).Mul(bigGwei, maxFeeCap) + + chainID, err := client.ChainID(ctx) + if err != nil { + return nil, fmt.Errorf("getting chain id: %w", err) + } + + return &Self{ + client: client, + tracker: tracker, + key: key, + address: address, + nonce: nonce, + signer: types.LatestSignerForChainID(chainID), + chainID: chainID, + gasTipCap: gasTipCap, + gasFeeCap: gasFeeCap, + issuePeriod: issuePeriod, + }, nil +} + +func (i *Self) GenerateAndIssueTx(ctx context.Context) (*types.Transaction, error) { + tx, err := types.SignNewTx(i.key, i.signer, &types.DynamicFeeTx{ + ChainID: i.chainID, + Nonce: i.nonce, + GasTipCap: i.gasTipCap, + GasFeeCap: i.gasFeeCap, + Gas: params.TxGas, + To: &i.address, // self + Data: nil, + Value: common.Big0, + }) + if err != nil { + return nil, err + } + + if i.issuePeriod > 0 && !i.lastIssue.IsZero() && + time.Since(i.lastIssue) < i.issuePeriod { + timer := time.NewTimer(i.issuePeriod - time.Since(i.lastIssue)) + select { + case <-timer.C: + case <-ctx.Done(): + timer.Stop() + return nil, ctx.Err() + } + } + + if err := i.client.SendTransaction(ctx, tx); err != nil { + return nil, fmt.Errorf("issuing transaction with nonce %d: %w", i.nonce, err) + } + i.nonce++ + i.tracker.Issue(tx.Hash()) + i.lastIssue = time.Now() + return tx, nil +} diff --git a/tests/load/orchestrate/burst.go b/tests/load/orchestrate/burst.go index 97f78279850b..82a454e888a4 100644 --- a/tests/load/orchestrate/burst.go +++ b/tests/load/orchestrate/burst.go @@ -50,12 +50,9 @@ func (o *BurstOrchestrator[T, U]) Execute(ctx context.Context) error { for _, agent := range o.agents { issuerGroup.Go(func() error { for range agent.TxTarget { - tx, err := agent.Generator.GenerateTx() + tx, err := agent.Issuer.GenerateAndIssueTx(ctx) if err != nil { - return fmt.Errorf("generating transaction: %w", err) - } - if err := agent.Issuer.IssueTx(ctx, tx); err != nil { - return fmt.Errorf("issuing transaction: %w", err) + return fmt.Errorf("generating and issuing transaction: %w", err) } agent.Listener.RegisterIssued(tx) From b8124198a47953961bd45d35eeed3023a77f8372 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Tue, 6 May 2025 15:45:51 +0200 Subject: [PATCH 053/197] Remove unneeded constant issuePeriod --- tests/load/funder.go | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/load/funder.go b/tests/load/funder.go index ae8931f0cd29..c701d2c4941f 100644 --- a/tests/load/funder.go +++ b/tests/load/funder.go @@ -71,7 +71,6 @@ func ensureMinimumFunds(ctx context.Context, endpoint string, keys []*ecdsa.Priv maxFundsAddress := ethcrypto.PubkeyToAddress(maxFundsKey.PublicKey) txTarget := uint64(len(needFundsKeys)) - const issuePeriod = 0 // no delay between transaction issuance issuer, err := issue.NewDistributor(ctx, client, maxFundsKey, needFundsKeys) if err != nil { return fmt.Errorf("creating issuer: %w", err) From 8aa1b53560559fe7629e4e59c1209b1871c36381 Mon Sep 17 00:00:00 2001 From: Rodrigo Villar Date: Tue, 6 May 2025 15:40:55 -0400 Subject: [PATCH 054/197] init --- tests/load/agent.go | 30 +++ tests/load/dependencies.go | 57 +++++ tests/load/gradual_orchestrator.go | 268 ++++++++++++++++++++++++ tests/load/gradual_orchestrator_test.go | 202 ++++++++++++++++++ tests/load/prometheus_tracker.go | 121 +++++++++++ 5 files changed, 678 insertions(+) create mode 100644 tests/load/agent.go create mode 100644 tests/load/dependencies.go create mode 100644 tests/load/gradual_orchestrator.go create mode 100644 tests/load/gradual_orchestrator_test.go create mode 100644 tests/load/prometheus_tracker.go diff --git a/tests/load/agent.go b/tests/load/agent.go new file mode 100644 index 000000000000..16390ac518ab --- /dev/null +++ b/tests/load/agent.go @@ -0,0 +1,30 @@ +// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package load + +type Agent[T, U comparable] struct { + Issuer Issuer[T] + Listener Listener[T] + Tracker Tracker[U] +} + +func NewAgent[T, U comparable]( + issuer Issuer[T], + listener Listener[T], + tracker Tracker[U], +) Agent[T, U] { + return Agent[T, U]{ + Issuer: issuer, + Listener: listener, + Tracker: tracker, + } +} + +func GetTotalObservedConfirmed[T, U comparable](agents []Agent[T, U]) uint64 { + total := uint64(0) + for _, agent := range agents { + total += agent.Tracker.GetObservedConfirmed() + } + return total +} diff --git a/tests/load/dependencies.go b/tests/load/dependencies.go new file mode 100644 index 000000000000..ecbb386fd077 --- /dev/null +++ b/tests/load/dependencies.go @@ -0,0 +1,57 @@ +// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package load + +import ( + "context" +) + +type Issuer[T comparable] interface { + // GenerateAndIssueTx generates and sends a tx to the network, and informs the + // tracker that it sent said transaction. It returns the sent transaction. + GenerateAndIssueTx(ctx context.Context) (tx T, err error) +} + +type Listener[T comparable] interface { + // Listen for the final status of transactions and notify the tracker + // Listen stops if the context is done, an error occurs, or if it received + // all the transactions issued and the issuer no longer issues any. + // Listen MUST return a nil error if the context is canceled. + Listen(ctx context.Context) error + + // RegisterIssued informs the listener that a transaction was issued. + RegisterIssued(tx T) + + // IssuingDone informs the listener that no more transactions will be issued. + IssuingDone() +} + +// Tracker keeps track of the status of transactions. +// This must be thread-safe, so it can be called in parallel by the issuer(s) or orchestrator. +type Tracker[T comparable] interface { + // Issue records a transaction that was submitted, but whose final status is + // not yet known. + Issue(T) + // ObserveConfirmed records a transaction that was confirmed. + ObserveConfirmed(T) + // ObserveFailed records a transaction that failed (e.g. expired) + ObserveFailed(T) + + // GetObservedIssued returns the number of transactions that the tracker has + // confirmed were issued. + GetObservedIssued() uint64 + // GetObservedConfirmed returns the number of transactions that the tracker has + // confirmed were accepted. + GetObservedConfirmed() uint64 + // GetObservedFailed returns the number of transactions that the tracker has + // confirmed failed. + GetObservedFailed() uint64 +} + +// orchestrator executes the load test by coordinating the issuers to send +// transactions, in a manner depending on the implementation. +type orchestrator interface { + // Execute the load test + Execute(ctx context.Context) error +} diff --git a/tests/load/gradual_orchestrator.go b/tests/load/gradual_orchestrator.go new file mode 100644 index 000000000000..ced89e803c99 --- /dev/null +++ b/tests/load/gradual_orchestrator.go @@ -0,0 +1,268 @@ +// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package load + +import ( + "context" + "errors" + "math" + "sync/atomic" + "time" + + "github.com/ava-labs/avalanchego/utils/logging" + "go.uber.org/zap" + "golang.org/x/sync/errgroup" +) + +var ( + _ orchestrator = (*GradualOrchestrator[any, any])(nil) + + ErrFailedToReachTargetTPS = errors.New("failed to reach target TPS") +) + +type GradualOrchestratorConfig struct { + // The maximum TPS the orchestrator should aim for. + MaxTPS uint64 + // The minimum TPS the orchestrator should start with. + MinTPS uint64 + // The step size to increase the TPS by. + Step uint64 + + // The factor by which to pad the number of txs an issuer sends per second + // for example, if targetTPS = 1000 and numIssuers = 10, then each issuer + // will send (1000/10)*TxRateMultiplier transactions per second. + // + // Maintaining a multiplier above target provides a toggle to keep load + // persistently above instead of below target. This ensures load generation + // does not pause issuance at the target and persistently under-issue and + // fail to account for the time it takes to add more load. + TxRateMultiplier float64 + + // The time period which TPS is averaged over + // Similarly, the time period which the orchestrator will wait before + // computing the average TPS. + SustainedTime time.Duration + // The number of attempts to try achieving a given target TPS before giving up. + MaxAttempts uint64 + + // Whether the orchestrator should return if the maxTPS has been reached + Terminate bool +} + +func DefaultGradualOrchestratorConfig() GradualOrchestratorConfig { + return GradualOrchestratorConfig{ + MaxTPS: 5_000, + MinTPS: 1_000, + Step: 1_000, + TxRateMultiplier: 1.3, + SustainedTime: 20 * time.Second, + MaxAttempts: 3, + Terminate: true, + } +} + +// GradualOrchestrator tests the network by continuously sending +// transactions at a given rate (currTargetTPS) and increasing that rate until it detects that +// the network can no longer make progress (i.e. the rate at the network accepts +// transactions is less than currTargetTPS). +type GradualOrchestrator[T, U comparable] struct { + agents []Agent[T, U] + + log logging.Logger + + maxObservedTPS atomic.Uint64 + + observerGroup errgroup.Group + issuerGroup *errgroup.Group + + config GradualOrchestratorConfig +} + +func NewGradualOrchestrator[T, U comparable]( + agents []Agent[T, U], + log logging.Logger, + config GradualOrchestratorConfig, +) (*GradualOrchestrator[T, U], error) { + return &GradualOrchestrator[T, U]{ + agents: agents, + log: log, + config: config, + }, nil +} + +func (o *GradualOrchestrator[T, U]) Execute(ctx context.Context) error { + ctx, cancel := context.WithCancel(ctx) + + // start a goroutine to confirm each issuer's transactions + for _, agent := range o.agents { + o.observerGroup.Go(func() error { return agent.Listener.Listen(ctx) }) + } + + // start the test and block until it's done + success := o.run(ctx) + + var err error + if !success { + err = ErrFailedToReachTargetTPS + } + + // stop the observers and issuers + cancel() + + // block until both the observers and issuers have stopped + return errors.Join(o.issuerGroup.Wait(), o.observerGroup.Wait(), err) +} + +// run the gradual load test by continuously increasing the rate at which +// transactions are sent +// +// run blocks until one of the following conditions is met: +// +// 1. an issuer has errored +// 2. the max TPS target has been reached and we can terminate +// 3. the maximum number of attempts to reach a target TPS has been reached +func (o *GradualOrchestrator[T, U]) run(ctx context.Context) bool { + var ( + prevConfirmed = GetTotalObservedConfirmed(o.agents) + prevTime = time.Now() + currTargetTPS = new(atomic.Uint64) + attempts uint64 = 1 + // true if the orchestrator has reached the max TPS target + achievedTargetTPS bool + ) + + currTargetTPS.Store(o.config.MinTPS) + + issuerGroup, issuerCtx := errgroup.WithContext(ctx) + o.issuerGroup = issuerGroup + + o.issueTxs(issuerCtx, currTargetTPS) + + for { + // wait for the sustained time to pass or for the context to be cancelled + select { + case <-time.After(o.config.SustainedTime): + case <-issuerCtx.Done(): // the parent context was cancelled or an issuer errored + } + + if issuerCtx.Err() != nil { + break // Case 1 + } + + currConfirmed := GetTotalObservedConfirmed(o.agents) + currTime := time.Now() + + tps := computeTPS(prevConfirmed, currConfirmed, currTime.Sub(prevTime)) + o.setMaxObservedTPS(tps) + + // if max TPS target has been reached and we don't terminate, then continue here + // so we do not keep increasing the target TPS + if achievedTargetTPS && !o.config.Terminate { + o.log.Info( + "current network state", + zap.Uint64("current TPS", tps), + zap.Uint64("max observed TPS", o.maxObservedTPS.Load()), + ) + continue + } + + if tps >= currTargetTPS.Load() { + if currTargetTPS.Load() >= o.config.MaxTPS { + achievedTargetTPS = true + o.log.Info( + "max TPS target reached", + zap.Uint64("max TPS target", currTargetTPS.Load()), + zap.Uint64("average TPS", tps), + ) + if o.config.Terminate { + o.log.Info("terminating orchestrator") + break // Case 2 + } else { + o.log.Info("orchestrator will now continue running at max TPS") + continue + } + } + o.log.Info( + "increasing TPS", + zap.Uint64("previous target TPS", currTargetTPS.Load()), + zap.Uint64("average TPS", tps), + zap.Uint64("new target TPS", currTargetTPS.Load()+o.config.Step), + ) + currTargetTPS.Add(o.config.Step) + attempts = 1 + } else { + if attempts >= o.config.MaxAttempts { + o.log.Info( + "max attempts reached", + zap.Uint64("attempted target TPS", currTargetTPS.Load()), + zap.Uint64("number of attempts", attempts), + ) + break // Case 3 + } + o.log.Info( + "failed to reach target TPS, retrying", + zap.Uint64("current target TPS", currTargetTPS.Load()), + zap.Uint64("average TPS", tps), + zap.Uint64("attempt number", attempts), + ) + attempts++ + } + + prevConfirmed = currConfirmed + prevTime = currTime + } + + return achievedTargetTPS +} + +// GetObservedIssued returns the max TPS the orchestrator observed +func (o *GradualOrchestrator[T, U]) GetMaxObservedTPS() uint64 { + return o.maxObservedTPS.Load() +} + +// start a goroutine to each issuer to continuously send transactions +// if an issuer errors, all other issuers will stop as well. +func (o *GradualOrchestrator[T, U]) issueTxs(ctx context.Context, currTargetTPS *atomic.Uint64) { + for _, agent := range o.agents { + o.issuerGroup.Go(func() error { + for { + if ctx.Err() != nil { + return nil //nolint:nilerr + } + currTime := time.Now() + txsPerIssuer := uint64(math.Ceil(float64(currTargetTPS.Load())/float64(len(o.agents))) * o.config.TxRateMultiplier) + // always listen until listener context is cancelled, do not call agent.Listener.IssuingDone(). + for range txsPerIssuer { + tx, err := agent.Issuer.GenerateAndIssueTx(ctx) + if err != nil { + return err + } + agent.Listener.RegisterIssued(tx) + } + diff := time.Second - time.Since(currTime) + if diff > 0 { + time.Sleep(diff) + } + } + }) + } +} + +// setMaxObservedTPS only if tps > the current max observed TPS. +func (o *GradualOrchestrator[T, U]) setMaxObservedTPS(tps uint64) { + if tps > o.maxObservedTPS.Load() { + o.maxObservedTPS.Store(tps) + } +} + +func computeTPS(initial uint64, final uint64, duration time.Duration) uint64 { + if duration <= 0 { + return 0 + } + + numTxs := final - initial + tps := float64(numTxs) / duration.Seconds() + + return uint64(tps) +} diff --git a/tests/load/gradual_orchestrator_test.go b/tests/load/gradual_orchestrator_test.go new file mode 100644 index 000000000000..0d0f11b6d4bd --- /dev/null +++ b/tests/load/gradual_orchestrator_test.go @@ -0,0 +1,202 @@ +// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package load + +import ( + "context" + "errors" + "math" + "testing" + "time" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/logging" + "github.com/prometheus/client_golang/prometheus" + "github.com/stretchr/testify/require" +) + +var _ Issuer[ids.ID] = (*mockIssuer)(nil) + +func TestGradualOrchestratorTPS(t *testing.T) { + tests := []struct { + name string + serverTPS uint64 + config GradualOrchestratorConfig + expectedErr error + }{ + { + name: "orchestrator achieves max TPS", + serverTPS: math.MaxUint64, + config: GradualOrchestratorConfig{ + MaxTPS: 2_000, + MinTPS: 1_000, + Step: 1_000, + TxRateMultiplier: 1.5, + SustainedTime: time.Second, + MaxAttempts: 2, + Terminate: true, + }, + }, + { + name: "orchestrator TPS limited by network", + serverTPS: 1_000, + config: GradualOrchestratorConfig{ + MaxTPS: 2_000, + MinTPS: 1_000, + Step: 1_000, + TxRateMultiplier: 1.3, + SustainedTime: time.Second, + MaxAttempts: 2, + Terminate: true, + }, + expectedErr: ErrFailedToReachTargetTPS, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := require.New(t) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + tracker, err := NewPrometheusTracker[ids.ID](prometheus.NewRegistry()) + r.NoError(err) + + agents := []Agent[ids.ID, ids.ID]{ + NewAgent( + &mockIssuer{ + generateTxF: func() (ids.ID, error) { + return ids.GenerateTestID(), nil + }, + tracker: tracker, + maxTxs: tt.serverTPS, + }, + &mockListener{}, + tracker, + ), + } + + orchestrator, err := NewGradualOrchestrator( + agents, + logging.NoLog{}, + tt.config, + ) + r.NoError(err) + + r.ErrorIs(orchestrator.Execute(ctx), tt.expectedErr) + + if tt.expectedErr == nil { + r.GreaterOrEqual(orchestrator.GetMaxObservedTPS(), tt.config.MaxTPS) + } else { + r.Less(orchestrator.GetMaxObservedTPS(), tt.config.MaxTPS) + } + }) + } +} + +// test that the orchestrator returns early if the txGenerators or the issuers error +func TestGradualOrchestratorExecution(t *testing.T) { + var ( + errMockTxGenerator = errors.New("mock tx generator error") + errMockIssuer = errors.New("mock issuer error") + ) + + tracker, err := NewPrometheusTracker[ids.ID](prometheus.NewRegistry()) + require.NoError(t, err, "creating tracker") + + tests := []struct { + name string + agents []Agent[ids.ID, ids.ID] + expectedErr error + }{ + { + name: "generator error", + agents: []Agent[ids.ID, ids.ID]{ + NewAgent[ids.ID, ids.ID]( + &mockIssuer{ + generateTxF: func() (ids.ID, error) { + return ids.Empty, errMockTxGenerator + }, + }, + &mockListener{}, + tracker, + ), + }, + expectedErr: errMockTxGenerator, + }, + { + name: "issuer error", + agents: []Agent[ids.ID, ids.ID]{ + NewAgent[ids.ID, ids.ID]( + &mockIssuer{ + generateTxF: func() (ids.ID, error) { + return ids.GenerateTestID(), nil + }, + issueTxErr: errMockIssuer, + }, + &mockListener{}, + tracker, + ), + }, + expectedErr: errMockIssuer, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := require.New(t) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + orchestrator, err := NewGradualOrchestrator( + tt.agents, + logging.NoLog{}, + DefaultGradualOrchestratorConfig(), + ) + r.NoError(err) + + r.ErrorIs(orchestrator.Execute(ctx), tt.expectedErr) + }) + } +} + +type mockIssuer struct { + generateTxF func() (ids.ID, error) + currTxsIssued uint64 + maxTxs uint64 + tracker Tracker[ids.ID] + issueTxErr error +} + +// GenerateAndIssueTx immediately generates, issues and confirms a tx. +// To simulate TPS, the number of txs IssueTx can issue/confirm is capped by maxTxs +func (m *mockIssuer) GenerateAndIssueTx(_ context.Context) (ids.ID, error) { + id, err := m.generateTxF() + if err != nil { + return id, err + } + + if m.issueTxErr != nil { + return ids.ID{}, m.issueTxErr + } + + if m.currTxsIssued >= m.maxTxs { + return ids.ID{}, nil + } + + m.tracker.Issue(id) + m.tracker.ObserveConfirmed(id) + m.currTxsIssued++ + return id, nil +} + +type mockListener struct{} + +func (*mockListener) Listen(context.Context) error { + return nil +} + +func (*mockListener) RegisterIssued(ids.ID) {} + +func (*mockListener) IssuingDone() {} diff --git a/tests/load/prometheus_tracker.go b/tests/load/prometheus_tracker.go new file mode 100644 index 000000000000..ffd53d70d043 --- /dev/null +++ b/tests/load/prometheus_tracker.go @@ -0,0 +1,121 @@ +// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package load + +import ( + "sync" + "time" + + "github.com/ava-labs/avalanchego/utils/wrappers" + "github.com/prometheus/client_golang/prometheus" +) + +const namespace = "load" + +var _ Tracker[any] = (*PrometheusTracker[any])(nil) + +type PrometheusTracker[T comparable] struct { + lock sync.RWMutex + + outstandingTxs map[T]time.Time + + txsIssued uint64 + txsConfirmed uint64 + txsFailed uint64 + + // metrics + txsIssuedCounter prometheus.Counter + txsConfirmedCounter prometheus.Counter + txsFailedCounter prometheus.Counter + txLatency prometheus.Histogram +} + +func NewPrometheusTracker[T comparable](reg *prometheus.Registry) (*PrometheusTracker[T], error) { + prometheusTracker := &PrometheusTracker[T]{ + outstandingTxs: make(map[T]time.Time), + txsIssuedCounter: prometheus.NewCounter(prometheus.CounterOpts{ + Namespace: namespace, + Name: "txs_issued", + Help: "Number of transactions issued", + }), + txsConfirmedCounter: prometheus.NewCounter(prometheus.CounterOpts{ + Namespace: namespace, + Name: "txs_confirmed", + Help: "Number of transactions confirmed", + }), + txsFailedCounter: prometheus.NewCounter(prometheus.CounterOpts{ + Namespace: namespace, + Name: "txs_failed", + Help: "Number of transactions failed", + }), + txLatency: prometheus.NewHistogram(prometheus.HistogramOpts{ + Namespace: namespace, + Name: "tx_latency", + Help: "Latency of transactions", + }), + } + + errs := wrappers.Errs{} + errs.Add( + reg.Register(prometheusTracker.txsIssuedCounter), + reg.Register(prometheusTracker.txsConfirmedCounter), + reg.Register(prometheusTracker.txsFailedCounter), + reg.Register(prometheusTracker.txLatency), + ) + return prometheusTracker, errs.Err +} + +func (p *PrometheusTracker[T]) GetObservedConfirmed() uint64 { + p.lock.RLock() + defer p.lock.RUnlock() + + return p.txsConfirmed +} + +func (p *PrometheusTracker[T]) GetObservedFailed() uint64 { + p.lock.RLock() + defer p.lock.RUnlock() + + return p.txsFailed +} + +func (p *PrometheusTracker[T]) GetObservedIssued() uint64 { + p.lock.RLock() + defer p.lock.RUnlock() + + return p.txsIssued +} + +func (p *PrometheusTracker[T]) Issue(tx T) { + p.lock.Lock() + defer p.lock.Unlock() + + p.outstandingTxs[tx] = time.Now() + p.txsIssued++ + p.txsIssuedCounter.Inc() +} + +func (p *PrometheusTracker[T]) ObserveConfirmed(tx T) { + p.lock.Lock() + defer p.lock.Unlock() + + startTime := p.outstandingTxs[tx] + delete(p.outstandingTxs, tx) + + p.txsConfirmed++ + p.txsConfirmedCounter.Inc() + p.txLatency.Observe(float64(time.Since(startTime).Milliseconds())) +} + +func (p *PrometheusTracker[T]) ObserveFailed(tx T) { + p.lock.Lock() + defer p.lock.Unlock() + + startTime := p.outstandingTxs[tx] + delete(p.outstandingTxs, tx) + + p.txsFailed++ + p.txsFailedCounter.Inc() + p.txLatency.Observe(float64(time.Since(startTime).Milliseconds())) +} From f34d312a6867ca000aa63cd37fee389974aa68c5 Mon Sep 17 00:00:00 2001 From: Rodrigo Villar Date: Tue, 6 May 2025 16:03:48 -0400 Subject: [PATCH 055/197] lint --- tests/load/dependencies.go | 4 +--- tests/load/gradual_orchestrator.go | 3 ++- tests/load/gradual_orchestrator_test.go | 5 +++-- tests/load/prometheus_tracker.go | 3 ++- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/tests/load/dependencies.go b/tests/load/dependencies.go index ecbb386fd077..613b2bc0f8d1 100644 --- a/tests/load/dependencies.go +++ b/tests/load/dependencies.go @@ -3,9 +3,7 @@ package load -import ( - "context" -) +import "context" type Issuer[T comparable] interface { // GenerateAndIssueTx generates and sends a tx to the network, and informs the diff --git a/tests/load/gradual_orchestrator.go b/tests/load/gradual_orchestrator.go index ced89e803c99..4b12aa9d2ee6 100644 --- a/tests/load/gradual_orchestrator.go +++ b/tests/load/gradual_orchestrator.go @@ -10,9 +10,10 @@ import ( "sync/atomic" "time" - "github.com/ava-labs/avalanchego/utils/logging" "go.uber.org/zap" "golang.org/x/sync/errgroup" + + "github.com/ava-labs/avalanchego/utils/logging" ) var ( diff --git a/tests/load/gradual_orchestrator_test.go b/tests/load/gradual_orchestrator_test.go index 0d0f11b6d4bd..b4a097895537 100644 --- a/tests/load/gradual_orchestrator_test.go +++ b/tests/load/gradual_orchestrator_test.go @@ -10,10 +10,11 @@ import ( "testing" "time" - "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/utils/logging" "github.com/prometheus/client_golang/prometheus" "github.com/stretchr/testify/require" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/logging" ) var _ Issuer[ids.ID] = (*mockIssuer)(nil) diff --git a/tests/load/prometheus_tracker.go b/tests/load/prometheus_tracker.go index ffd53d70d043..d63e05744ac7 100644 --- a/tests/load/prometheus_tracker.go +++ b/tests/load/prometheus_tracker.go @@ -7,8 +7,9 @@ import ( "sync" "time" - "github.com/ava-labs/avalanchego/utils/wrappers" "github.com/prometheus/client_golang/prometheus" + + "github.com/ava-labs/avalanchego/utils/wrappers" ) const namespace = "load" From 1155cd07b4decf86e9c670e77c948a8916b4ce17 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 7 May 2025 10:00:02 +0200 Subject: [PATCH 056/197] issue: add C chain opcode simulator issuer --- tests/load/issue/c_opcode.go | 194 ++++ .../load/issue/contracts/EVMLoadSimulator.go | 907 ++++++++++++++++++ .../load/issue/contracts/EVMLoadSimulator.sol | 72 ++ .../issue/contracts/generate_abi_bindings.sh | 30 + tests/load/issue/contracts/generate_test.go | 6 + 5 files changed, 1209 insertions(+) create mode 100644 tests/load/issue/c_opcode.go create mode 100644 tests/load/issue/contracts/EVMLoadSimulator.go create mode 100644 tests/load/issue/contracts/EVMLoadSimulator.sol create mode 100755 tests/load/issue/contracts/generate_abi_bindings.sh create mode 100644 tests/load/issue/contracts/generate_test.go diff --git a/tests/load/issue/c_opcode.go b/tests/load/issue/c_opcode.go new file mode 100644 index 000000000000..4d20422765c1 --- /dev/null +++ b/tests/load/issue/c_opcode.go @@ -0,0 +1,194 @@ +// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package issue + +import ( + "context" + "crypto/ecdsa" + "fmt" + "math/big" + "math/rand/v2" + "time" + + "github.com/ava-labs/coreth/accounts/abi/bind" + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/types" + "github.com/ava-labs/libevm/rpc" + + evmloadsimulator "github.com/ava-labs/avalanchego/tests/load/issue/contracts" + ethcrypto "github.com/ava-labs/libevm/crypto" +) + +type BindEthClient interface { + EthClient + bind.DeployBackend + bind.ContractBackend +} + +// OpCodeSimulator generates and issues transactions that randomly call the +// external functions of the [evmloadsimulator.EVMLoadSimulator] contract +// instance that it deploys. +type OpCodeSimulator struct { + // Injected parameters + client BindEthClient + tracker Tracker + senderKey *ecdsa.PrivateKey + maxFeeCap *big.Int + issuePeriod time.Duration + + // Determined by constructor + chainID *big.Int + maxTipCap *big.Int + contractAddress common.Address + contractInstance *evmloadsimulator.EVMLoadSimulator + + // State + nonce uint64 + lastIssue time.Time +} + +func NewOpCodeSimulator( + ctx context.Context, + client BindEthClient, + tracker Tracker, + maxFeeCap *big.Int, + key *ecdsa.PrivateKey, + issuePeriod time.Duration, +) (*OpCodeSimulator, error) { + chainID, err := client.ChainID(ctx) + if err != nil { + return nil, fmt.Errorf("getting chain id: %w", err) + } + + nonce, err := client.NonceAt(ctx, ethcrypto.PubkeyToAddress(key.PublicKey), big.NewInt(int64(rpc.PendingBlockNumber))) + if err != nil { + return nil, fmt.Errorf("getting nonce: %w", err) + } + + maxTipCap := big.NewInt(1) + txOpts, err := newTxOpts(ctx, key, chainID, maxFeeCap, maxTipCap, nonce) + if err != nil { + return nil, fmt.Errorf("creating transaction opts: %w", err) + } + _, simulatorDeploymentTx, simulatorInstance, err := evmloadsimulator.DeployEVMLoadSimulator(txOpts, client) + if err != nil { + return nil, fmt.Errorf("deploying simulator contract: %w", err) + } + nonce++ // deploying contract consumes one nonce + + simulatorAddress, err := bind.WaitDeployed(ctx, client, simulatorDeploymentTx) + if err != nil { + return nil, fmt.Errorf("waiting for simulator contract to be mined: %w", err) + } + + return &OpCodeSimulator{ + client: client, + tracker: tracker, + senderKey: key, + maxFeeCap: maxFeeCap, + issuePeriod: issuePeriod, + chainID: chainID, + maxTipCap: maxTipCap, + contractAddress: simulatorAddress, + contractInstance: simulatorInstance, + nonce: nonce, + }, nil +} + +func (o *OpCodeSimulator) GenerateAndIssueTx(ctx context.Context) (tx *types.Transaction, err error) { + if o.issuePeriod > 0 && !o.lastIssue.IsZero() && + time.Since(o.lastIssue) < o.issuePeriod { + timer := time.NewTimer(o.issuePeriod - time.Since(o.lastIssue)) + select { + case <-timer.C: + case <-ctx.Done(): + timer.Stop() + return nil, ctx.Err() + } + } + + txOpts, err := o.newTxOpts(ctx) + if err != nil { + return nil, fmt.Errorf("creating transaction opts: %w", err) + } + + loadTypes := allLoadTypes() + loadType := loadTypes[rand.IntN(len(loadTypes))] //nolint:gosec + + switch loadType { + case randomWrite: + const maxWriteSizeBytes = 5 + writeSize := big.NewInt(rand.Int64N(maxWriteSizeBytes)) //nolint:gosec + tx, err = o.contractInstance.SimulateRandomWrite(txOpts, writeSize) + case stateModification: + const maxStateSizeBytes = 5 + stateSize := big.NewInt(rand.Int64N(maxStateSizeBytes)) //nolint:gosec + tx, err = o.contractInstance.SimulateModification(txOpts, stateSize) + case randomReads: + const maxReadSizeBytes = 5 + numReads := big.NewInt(rand.Int64N(maxReadSizeBytes)) //nolint:gosec + tx, err = o.contractInstance.SimulateReads(txOpts, numReads) + case hashing: + const maxRounds = 3 + rounds := big.NewInt(rand.Int64N(maxRounds)) //nolint:gosec + tx, err = o.contractInstance.SimulateHashing(txOpts, rounds) + case memory: + const maxArraySize = 4 + arraySize := big.NewInt(rand.Int64N(maxArraySize)) //nolint:gosec + tx, err = o.contractInstance.SimulateMemory(txOpts, arraySize) + case callDepth: + const maxDepth = 5 + depth := big.NewInt(rand.Int64N(maxDepth)) //nolint:gosec + tx, err = o.contractInstance.SimulateCallDepth(txOpts, depth) + default: + return nil, fmt.Errorf("invalid load type: %s", loadType) + } + + if err != nil { + return nil, fmt.Errorf("calling simulator contract with load type %s: %w", loadType, err) + } + + o.nonce++ + o.tracker.Issue(tx.Hash()) + o.lastIssue = time.Now() + return tx, err +} + +const ( + randomWrite = "random write" + stateModification = "state modification" + randomReads = "random reads" + hashing = "hashing" + memory = "memory" + callDepth = "call depth" +) + +func allLoadTypes() []string { + return []string{ + randomWrite, + stateModification, + randomReads, + hashing, + memory, + callDepth, + } +} + +func (o *OpCodeSimulator) newTxOpts(ctx context.Context) (*bind.TransactOpts, error) { + return newTxOpts(ctx, o.senderKey, o.chainID, o.maxFeeCap, o.maxTipCap, o.nonce) +} + +func newTxOpts(ctx context.Context, key *ecdsa.PrivateKey, + chainID, maxFeeCap, maxTipCap *big.Int, nonce uint64, +) (*bind.TransactOpts, error) { + txOpts, err := bind.NewKeyedTransactorWithChainID(key, chainID) + if err != nil { + return nil, fmt.Errorf("creating transaction opts: %w", err) + } + txOpts.Nonce = new(big.Int).SetUint64(nonce) + txOpts.GasFeeCap = maxFeeCap + txOpts.GasTipCap = maxTipCap + txOpts.Context = ctx + return txOpts, nil +} diff --git a/tests/load/issue/contracts/EVMLoadSimulator.go b/tests/load/issue/contracts/EVMLoadSimulator.go new file mode 100644 index 000000000000..fc9e6b6f755d --- /dev/null +++ b/tests/load/issue/contracts/EVMLoadSimulator.go @@ -0,0 +1,907 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package contracts + +import ( + "errors" + "math/big" + "strings" + + "github.com/ava-labs/coreth/accounts/abi" + "github.com/ava-labs/coreth/accounts/abi/bind" + ethereum "github.com/ava-labs/libevm" + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/types" + "github.com/ava-labs/libevm/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +// EVMLoadSimulatorMetaData contains all meta data concerning the EVMLoadSimulator contract. +var EVMLoadSimulatorMetaData = &bind.MetaData{ + ABI: "[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"hash\",\"type\":\"bytes32\"}],\"name\":\"HashCalculates\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"arr\",\"type\":\"uint256[]\"}],\"name\":\"MemoryWritten\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"accountId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"StorageUpdate\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"sum\",\"type\":\"uint256\"}],\"name\":\"SumCalculated\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"balancesCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"depth\",\"type\":\"uint256\"}],\"name\":\"simulateCallDepth\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"rounds\",\"type\":\"uint256\"}],\"name\":\"simulateHashing\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"hash\",\"type\":\"bytes32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"sizeInWords\",\"type\":\"uint256\"}],\"name\":\"simulateMemory\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"sum\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"count\",\"type\":\"uint256\"}],\"name\":\"simulateModification\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"count\",\"type\":\"uint256\"}],\"name\":\"simulateRandomWrite\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"count\",\"type\":\"uint256\"}],\"name\":\"simulateReads\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"sum\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x6080604052348015600e575f5ffd5b5061097b8061001c5f395ff3fe608060405234801561000f575f5ffd5b506004361061007b575f3560e01c8063aae05a6511610059578063aae05a65146100e9578063b77513d114610105578063f05ed79e14610121578063fb0c0012146101515761007b565b80633851d6e71461007f578063542eedd91461009d5780637db6ecb1146100b9575b5f5ffd5b610087610181565b60405161009491906105c4565b60405180910390f35b6100b760048036038101906100b2919061060b565b610187565b005b6100d360048036038101906100ce919061060b565b610205565b6040516100e0919061064e565b60405180910390f35b61010360048036038101906100fe919061060b565b6102af565b005b61011f600480360381019061011a919061060b565b6103b8565b005b61013b6004803603810190610136919061060b565b610443565b60405161014891906105c4565b60405180910390f35b61016b6004803603810190610166919061060b565b610530565b60405161017891906105c4565b60405180910390f35b60015481565b5f811115610202573073ffffffffffffffffffffffffffffffffffffffff1663542eedd96001836101b89190610694565b6040518263ffffffff1660e01b81526004016101d491906105c4565b5f604051808303815f87803b1580156101eb575f5ffd5b505af11580156101fd573d5f5f3e3d5ffd5b505050505b50565b5f6040516020016102159061071b565b6040516020818303038152906040528051906020012090505f5f90505b8281101561027257818160405160200161024d92919061076f565b6040516020818303038152906040528051906020012091508080600101915050610232565b507f30ca2ef0880ae63712fdaf11aefb67752968cff6f845956fcbdfcf421f4647cb816040516102a2919061064e565b60405180910390a1919050565b5f600190505b8181116103b457600154811015610339575f60015f5f8481526020019081526020015f20546102e4919061079a565b9050805f5f8481526020019081526020015f2081905550817fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc88260405161032b91906105c4565b60405180910390a2506103a1565b5f60015f81548092919061034c906107cd565b919050559050815f5f8381526020019081526020015f2081905550807fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc88360405161039791906105c4565b60405180910390a2505b80806103ac906107cd565b9150506102b5565b5050565b5f600190505b81811161043f575f60015f8154809291906103d8906107cd565b919050559050815f5f8381526020019081526020015f2081905550807fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc88360405161042391906105c4565b60405180910390a2508080610437906107cd565b9150506103be565b5050565b5f5f8267ffffffffffffffff81111561045f5761045e610814565b5b60405190808252806020026020018201604052801561048d5781602001602082028036833780820191505090505b5090505f5f90505b838110156104f257808282815181106104b1576104b0610841565b5b6020026020010181815250508181815181106104d0576104cf610841565b5b6020026020010151836104e3919061079a565b92508080600101915050610495565b507f542a9e74627abe4fb012aa9be028f3234ff2b2253530c6fa2220e29f03e4215d816040516105229190610925565b60405180910390a150919050565b5f5f600190505b82811161056f575f5f8281526020019081526020015f20548261055a919061079a565b91508080610567906107cd565b915050610537565b507fe32d91cad5061d7491327c51e7b799c677b41d033204a5c5022b120f5da4becb8160405161059f91906105c4565b60405180910390a1919050565b5f819050919050565b6105be816105ac565b82525050565b5f6020820190506105d75f8301846105b5565b92915050565b5f5ffd5b6105ea816105ac565b81146105f4575f5ffd5b50565b5f81359050610605816105e1565b92915050565b5f602082840312156106205761061f6105dd565b5b5f61062d848285016105f7565b91505092915050565b5f819050919050565b61064881610636565b82525050565b5f6020820190506106615f83018461063f565b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f61069e826105ac565b91506106a9836105ac565b92508282039050818111156106c1576106c0610667565b5b92915050565b5f81905092915050565b7f696e697469616c000000000000000000000000000000000000000000000000005f82015250565b5f6107056007836106c7565b9150610710826106d1565b600782019050919050565b5f610725826106f9565b9150819050919050565b5f819050919050565b61074961074482610636565b61072f565b82525050565b5f819050919050565b610769610764826105ac565b61074f565b82525050565b5f61077a8285610738565b60208201915061078a8284610758565b6020820191508190509392505050565b5f6107a4826105ac565b91506107af836105ac565b92508282019050808211156107c7576107c6610667565b5b92915050565b5f6107d7826105ac565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361080957610808610667565b5b600182019050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b6108a0816105ac565b82525050565b5f6108b18383610897565b60208301905092915050565b5f602082019050919050565b5f6108d38261086e565b6108dd8185610878565b93506108e883610888565b805f5b838110156109185781516108ff88826108a6565b975061090a836108bd565b9250506001810190506108eb565b5085935050505092915050565b5f6020820190508181035f83015261093d81846108c9565b90509291505056fea2646970667358221220473465ddecf81541dfcac22f9cccc88acda0bc45532126ee227eb5dba6fd6a0a64736f6c634300081d0033", +} + +// EVMLoadSimulatorABI is the input ABI used to generate the binding from. +// Deprecated: Use EVMLoadSimulatorMetaData.ABI instead. +var EVMLoadSimulatorABI = EVMLoadSimulatorMetaData.ABI + +// EVMLoadSimulatorBin is the compiled bytecode used for deploying new contracts. +// Deprecated: Use EVMLoadSimulatorMetaData.Bin instead. +var EVMLoadSimulatorBin = EVMLoadSimulatorMetaData.Bin + +// DeployEVMLoadSimulator deploys a new Ethereum contract, binding an instance of EVMLoadSimulator to it. +func DeployEVMLoadSimulator(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *EVMLoadSimulator, error) { + parsed, err := EVMLoadSimulatorMetaData.GetAbi() + if err != nil { + return common.Address{}, nil, nil, err + } + if parsed == nil { + return common.Address{}, nil, nil, errors.New("GetABI returned nil") + } + + address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(EVMLoadSimulatorBin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &EVMLoadSimulator{EVMLoadSimulatorCaller: EVMLoadSimulatorCaller{contract: contract}, EVMLoadSimulatorTransactor: EVMLoadSimulatorTransactor{contract: contract}, EVMLoadSimulatorFilterer: EVMLoadSimulatorFilterer{contract: contract}}, nil +} + +// EVMLoadSimulator is an auto generated Go binding around an Ethereum contract. +type EVMLoadSimulator struct { + EVMLoadSimulatorCaller // Read-only binding to the contract + EVMLoadSimulatorTransactor // Write-only binding to the contract + EVMLoadSimulatorFilterer // Log filterer for contract events +} + +// EVMLoadSimulatorCaller is an auto generated read-only Go binding around an Ethereum contract. +type EVMLoadSimulatorCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// EVMLoadSimulatorTransactor is an auto generated write-only Go binding around an Ethereum contract. +type EVMLoadSimulatorTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// EVMLoadSimulatorFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type EVMLoadSimulatorFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// EVMLoadSimulatorSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type EVMLoadSimulatorSession struct { + Contract *EVMLoadSimulator // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// EVMLoadSimulatorCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type EVMLoadSimulatorCallerSession struct { + Contract *EVMLoadSimulatorCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// EVMLoadSimulatorTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type EVMLoadSimulatorTransactorSession struct { + Contract *EVMLoadSimulatorTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// EVMLoadSimulatorRaw is an auto generated low-level Go binding around an Ethereum contract. +type EVMLoadSimulatorRaw struct { + Contract *EVMLoadSimulator // Generic contract binding to access the raw methods on +} + +// EVMLoadSimulatorCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type EVMLoadSimulatorCallerRaw struct { + Contract *EVMLoadSimulatorCaller // Generic read-only contract binding to access the raw methods on +} + +// EVMLoadSimulatorTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type EVMLoadSimulatorTransactorRaw struct { + Contract *EVMLoadSimulatorTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewEVMLoadSimulator creates a new instance of EVMLoadSimulator, bound to a specific deployed contract. +func NewEVMLoadSimulator(address common.Address, backend bind.ContractBackend) (*EVMLoadSimulator, error) { + contract, err := bindEVMLoadSimulator(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &EVMLoadSimulator{EVMLoadSimulatorCaller: EVMLoadSimulatorCaller{contract: contract}, EVMLoadSimulatorTransactor: EVMLoadSimulatorTransactor{contract: contract}, EVMLoadSimulatorFilterer: EVMLoadSimulatorFilterer{contract: contract}}, nil +} + +// NewEVMLoadSimulatorCaller creates a new read-only instance of EVMLoadSimulator, bound to a specific deployed contract. +func NewEVMLoadSimulatorCaller(address common.Address, caller bind.ContractCaller) (*EVMLoadSimulatorCaller, error) { + contract, err := bindEVMLoadSimulator(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &EVMLoadSimulatorCaller{contract: contract}, nil +} + +// NewEVMLoadSimulatorTransactor creates a new write-only instance of EVMLoadSimulator, bound to a specific deployed contract. +func NewEVMLoadSimulatorTransactor(address common.Address, transactor bind.ContractTransactor) (*EVMLoadSimulatorTransactor, error) { + contract, err := bindEVMLoadSimulator(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &EVMLoadSimulatorTransactor{contract: contract}, nil +} + +// NewEVMLoadSimulatorFilterer creates a new log filterer instance of EVMLoadSimulator, bound to a specific deployed contract. +func NewEVMLoadSimulatorFilterer(address common.Address, filterer bind.ContractFilterer) (*EVMLoadSimulatorFilterer, error) { + contract, err := bindEVMLoadSimulator(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &EVMLoadSimulatorFilterer{contract: contract}, nil +} + +// bindEVMLoadSimulator binds a generic wrapper to an already deployed contract. +func bindEVMLoadSimulator(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := EVMLoadSimulatorMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_EVMLoadSimulator *EVMLoadSimulatorRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _EVMLoadSimulator.Contract.EVMLoadSimulatorCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_EVMLoadSimulator *EVMLoadSimulatorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _EVMLoadSimulator.Contract.EVMLoadSimulatorTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_EVMLoadSimulator *EVMLoadSimulatorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _EVMLoadSimulator.Contract.EVMLoadSimulatorTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_EVMLoadSimulator *EVMLoadSimulatorCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _EVMLoadSimulator.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_EVMLoadSimulator *EVMLoadSimulatorTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _EVMLoadSimulator.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_EVMLoadSimulator *EVMLoadSimulatorTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _EVMLoadSimulator.Contract.contract.Transact(opts, method, params...) +} + +// BalancesCount is a free data retrieval call binding the contract method 0x3851d6e7. +// +// Solidity: function balancesCount() view returns(uint256) +func (_EVMLoadSimulator *EVMLoadSimulatorCaller) BalancesCount(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _EVMLoadSimulator.contract.Call(opts, &out, "balancesCount") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// BalancesCount is a free data retrieval call binding the contract method 0x3851d6e7. +// +// Solidity: function balancesCount() view returns(uint256) +func (_EVMLoadSimulator *EVMLoadSimulatorSession) BalancesCount() (*big.Int, error) { + return _EVMLoadSimulator.Contract.BalancesCount(&_EVMLoadSimulator.CallOpts) +} + +// BalancesCount is a free data retrieval call binding the contract method 0x3851d6e7. +// +// Solidity: function balancesCount() view returns(uint256) +func (_EVMLoadSimulator *EVMLoadSimulatorCallerSession) BalancesCount() (*big.Int, error) { + return _EVMLoadSimulator.Contract.BalancesCount(&_EVMLoadSimulator.CallOpts) +} + +// SimulateCallDepth is a paid mutator transaction binding the contract method 0x542eedd9. +// +// Solidity: function simulateCallDepth(uint256 depth) returns() +func (_EVMLoadSimulator *EVMLoadSimulatorTransactor) SimulateCallDepth(opts *bind.TransactOpts, depth *big.Int) (*types.Transaction, error) { + return _EVMLoadSimulator.contract.Transact(opts, "simulateCallDepth", depth) +} + +// SimulateCallDepth is a paid mutator transaction binding the contract method 0x542eedd9. +// +// Solidity: function simulateCallDepth(uint256 depth) returns() +func (_EVMLoadSimulator *EVMLoadSimulatorSession) SimulateCallDepth(depth *big.Int) (*types.Transaction, error) { + return _EVMLoadSimulator.Contract.SimulateCallDepth(&_EVMLoadSimulator.TransactOpts, depth) +} + +// SimulateCallDepth is a paid mutator transaction binding the contract method 0x542eedd9. +// +// Solidity: function simulateCallDepth(uint256 depth) returns() +func (_EVMLoadSimulator *EVMLoadSimulatorTransactorSession) SimulateCallDepth(depth *big.Int) (*types.Transaction, error) { + return _EVMLoadSimulator.Contract.SimulateCallDepth(&_EVMLoadSimulator.TransactOpts, depth) +} + +// SimulateHashing is a paid mutator transaction binding the contract method 0x7db6ecb1. +// +// Solidity: function simulateHashing(uint256 rounds) returns(bytes32 hash) +func (_EVMLoadSimulator *EVMLoadSimulatorTransactor) SimulateHashing(opts *bind.TransactOpts, rounds *big.Int) (*types.Transaction, error) { + return _EVMLoadSimulator.contract.Transact(opts, "simulateHashing", rounds) +} + +// SimulateHashing is a paid mutator transaction binding the contract method 0x7db6ecb1. +// +// Solidity: function simulateHashing(uint256 rounds) returns(bytes32 hash) +func (_EVMLoadSimulator *EVMLoadSimulatorSession) SimulateHashing(rounds *big.Int) (*types.Transaction, error) { + return _EVMLoadSimulator.Contract.SimulateHashing(&_EVMLoadSimulator.TransactOpts, rounds) +} + +// SimulateHashing is a paid mutator transaction binding the contract method 0x7db6ecb1. +// +// Solidity: function simulateHashing(uint256 rounds) returns(bytes32 hash) +func (_EVMLoadSimulator *EVMLoadSimulatorTransactorSession) SimulateHashing(rounds *big.Int) (*types.Transaction, error) { + return _EVMLoadSimulator.Contract.SimulateHashing(&_EVMLoadSimulator.TransactOpts, rounds) +} + +// SimulateMemory is a paid mutator transaction binding the contract method 0xf05ed79e. +// +// Solidity: function simulateMemory(uint256 sizeInWords) returns(uint256 sum) +func (_EVMLoadSimulator *EVMLoadSimulatorTransactor) SimulateMemory(opts *bind.TransactOpts, sizeInWords *big.Int) (*types.Transaction, error) { + return _EVMLoadSimulator.contract.Transact(opts, "simulateMemory", sizeInWords) +} + +// SimulateMemory is a paid mutator transaction binding the contract method 0xf05ed79e. +// +// Solidity: function simulateMemory(uint256 sizeInWords) returns(uint256 sum) +func (_EVMLoadSimulator *EVMLoadSimulatorSession) SimulateMemory(sizeInWords *big.Int) (*types.Transaction, error) { + return _EVMLoadSimulator.Contract.SimulateMemory(&_EVMLoadSimulator.TransactOpts, sizeInWords) +} + +// SimulateMemory is a paid mutator transaction binding the contract method 0xf05ed79e. +// +// Solidity: function simulateMemory(uint256 sizeInWords) returns(uint256 sum) +func (_EVMLoadSimulator *EVMLoadSimulatorTransactorSession) SimulateMemory(sizeInWords *big.Int) (*types.Transaction, error) { + return _EVMLoadSimulator.Contract.SimulateMemory(&_EVMLoadSimulator.TransactOpts, sizeInWords) +} + +// SimulateModification is a paid mutator transaction binding the contract method 0xaae05a65. +// +// Solidity: function simulateModification(uint256 count) returns() +func (_EVMLoadSimulator *EVMLoadSimulatorTransactor) SimulateModification(opts *bind.TransactOpts, count *big.Int) (*types.Transaction, error) { + return _EVMLoadSimulator.contract.Transact(opts, "simulateModification", count) +} + +// SimulateModification is a paid mutator transaction binding the contract method 0xaae05a65. +// +// Solidity: function simulateModification(uint256 count) returns() +func (_EVMLoadSimulator *EVMLoadSimulatorSession) SimulateModification(count *big.Int) (*types.Transaction, error) { + return _EVMLoadSimulator.Contract.SimulateModification(&_EVMLoadSimulator.TransactOpts, count) +} + +// SimulateModification is a paid mutator transaction binding the contract method 0xaae05a65. +// +// Solidity: function simulateModification(uint256 count) returns() +func (_EVMLoadSimulator *EVMLoadSimulatorTransactorSession) SimulateModification(count *big.Int) (*types.Transaction, error) { + return _EVMLoadSimulator.Contract.SimulateModification(&_EVMLoadSimulator.TransactOpts, count) +} + +// SimulateRandomWrite is a paid mutator transaction binding the contract method 0xb77513d1. +// +// Solidity: function simulateRandomWrite(uint256 count) returns() +func (_EVMLoadSimulator *EVMLoadSimulatorTransactor) SimulateRandomWrite(opts *bind.TransactOpts, count *big.Int) (*types.Transaction, error) { + return _EVMLoadSimulator.contract.Transact(opts, "simulateRandomWrite", count) +} + +// SimulateRandomWrite is a paid mutator transaction binding the contract method 0xb77513d1. +// +// Solidity: function simulateRandomWrite(uint256 count) returns() +func (_EVMLoadSimulator *EVMLoadSimulatorSession) SimulateRandomWrite(count *big.Int) (*types.Transaction, error) { + return _EVMLoadSimulator.Contract.SimulateRandomWrite(&_EVMLoadSimulator.TransactOpts, count) +} + +// SimulateRandomWrite is a paid mutator transaction binding the contract method 0xb77513d1. +// +// Solidity: function simulateRandomWrite(uint256 count) returns() +func (_EVMLoadSimulator *EVMLoadSimulatorTransactorSession) SimulateRandomWrite(count *big.Int) (*types.Transaction, error) { + return _EVMLoadSimulator.Contract.SimulateRandomWrite(&_EVMLoadSimulator.TransactOpts, count) +} + +// SimulateReads is a paid mutator transaction binding the contract method 0xfb0c0012. +// +// Solidity: function simulateReads(uint256 count) returns(uint256 sum) +func (_EVMLoadSimulator *EVMLoadSimulatorTransactor) SimulateReads(opts *bind.TransactOpts, count *big.Int) (*types.Transaction, error) { + return _EVMLoadSimulator.contract.Transact(opts, "simulateReads", count) +} + +// SimulateReads is a paid mutator transaction binding the contract method 0xfb0c0012. +// +// Solidity: function simulateReads(uint256 count) returns(uint256 sum) +func (_EVMLoadSimulator *EVMLoadSimulatorSession) SimulateReads(count *big.Int) (*types.Transaction, error) { + return _EVMLoadSimulator.Contract.SimulateReads(&_EVMLoadSimulator.TransactOpts, count) +} + +// SimulateReads is a paid mutator transaction binding the contract method 0xfb0c0012. +// +// Solidity: function simulateReads(uint256 count) returns(uint256 sum) +func (_EVMLoadSimulator *EVMLoadSimulatorTransactorSession) SimulateReads(count *big.Int) (*types.Transaction, error) { + return _EVMLoadSimulator.Contract.SimulateReads(&_EVMLoadSimulator.TransactOpts, count) +} + +// EVMLoadSimulatorHashCalculatesIterator is returned from FilterHashCalculates and is used to iterate over the raw logs and unpacked data for HashCalculates events raised by the EVMLoadSimulator contract. +type EVMLoadSimulatorHashCalculatesIterator struct { + Event *EVMLoadSimulatorHashCalculates // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *EVMLoadSimulatorHashCalculatesIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(EVMLoadSimulatorHashCalculates) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(EVMLoadSimulatorHashCalculates) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *EVMLoadSimulatorHashCalculatesIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *EVMLoadSimulatorHashCalculatesIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// EVMLoadSimulatorHashCalculates represents a HashCalculates event raised by the EVMLoadSimulator contract. +type EVMLoadSimulatorHashCalculates struct { + Hash [32]byte + Raw types.Log // Blockchain specific contextual infos +} + +// FilterHashCalculates is a free log retrieval operation binding the contract event 0x30ca2ef0880ae63712fdaf11aefb67752968cff6f845956fcbdfcf421f4647cb. +// +// Solidity: event HashCalculates(bytes32 hash) +func (_EVMLoadSimulator *EVMLoadSimulatorFilterer) FilterHashCalculates(opts *bind.FilterOpts) (*EVMLoadSimulatorHashCalculatesIterator, error) { + + logs, sub, err := _EVMLoadSimulator.contract.FilterLogs(opts, "HashCalculates") + if err != nil { + return nil, err + } + return &EVMLoadSimulatorHashCalculatesIterator{contract: _EVMLoadSimulator.contract, event: "HashCalculates", logs: logs, sub: sub}, nil +} + +// WatchHashCalculates is a free log subscription operation binding the contract event 0x30ca2ef0880ae63712fdaf11aefb67752968cff6f845956fcbdfcf421f4647cb. +// +// Solidity: event HashCalculates(bytes32 hash) +func (_EVMLoadSimulator *EVMLoadSimulatorFilterer) WatchHashCalculates(opts *bind.WatchOpts, sink chan<- *EVMLoadSimulatorHashCalculates) (event.Subscription, error) { + + logs, sub, err := _EVMLoadSimulator.contract.WatchLogs(opts, "HashCalculates") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(EVMLoadSimulatorHashCalculates) + if err := _EVMLoadSimulator.contract.UnpackLog(event, "HashCalculates", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseHashCalculates is a log parse operation binding the contract event 0x30ca2ef0880ae63712fdaf11aefb67752968cff6f845956fcbdfcf421f4647cb. +// +// Solidity: event HashCalculates(bytes32 hash) +func (_EVMLoadSimulator *EVMLoadSimulatorFilterer) ParseHashCalculates(log types.Log) (*EVMLoadSimulatorHashCalculates, error) { + event := new(EVMLoadSimulatorHashCalculates) + if err := _EVMLoadSimulator.contract.UnpackLog(event, "HashCalculates", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// EVMLoadSimulatorMemoryWrittenIterator is returned from FilterMemoryWritten and is used to iterate over the raw logs and unpacked data for MemoryWritten events raised by the EVMLoadSimulator contract. +type EVMLoadSimulatorMemoryWrittenIterator struct { + Event *EVMLoadSimulatorMemoryWritten // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *EVMLoadSimulatorMemoryWrittenIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(EVMLoadSimulatorMemoryWritten) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(EVMLoadSimulatorMemoryWritten) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *EVMLoadSimulatorMemoryWrittenIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *EVMLoadSimulatorMemoryWrittenIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// EVMLoadSimulatorMemoryWritten represents a MemoryWritten event raised by the EVMLoadSimulator contract. +type EVMLoadSimulatorMemoryWritten struct { + Arr []*big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterMemoryWritten is a free log retrieval operation binding the contract event 0x542a9e74627abe4fb012aa9be028f3234ff2b2253530c6fa2220e29f03e4215d. +// +// Solidity: event MemoryWritten(uint256[] arr) +func (_EVMLoadSimulator *EVMLoadSimulatorFilterer) FilterMemoryWritten(opts *bind.FilterOpts) (*EVMLoadSimulatorMemoryWrittenIterator, error) { + + logs, sub, err := _EVMLoadSimulator.contract.FilterLogs(opts, "MemoryWritten") + if err != nil { + return nil, err + } + return &EVMLoadSimulatorMemoryWrittenIterator{contract: _EVMLoadSimulator.contract, event: "MemoryWritten", logs: logs, sub: sub}, nil +} + +// WatchMemoryWritten is a free log subscription operation binding the contract event 0x542a9e74627abe4fb012aa9be028f3234ff2b2253530c6fa2220e29f03e4215d. +// +// Solidity: event MemoryWritten(uint256[] arr) +func (_EVMLoadSimulator *EVMLoadSimulatorFilterer) WatchMemoryWritten(opts *bind.WatchOpts, sink chan<- *EVMLoadSimulatorMemoryWritten) (event.Subscription, error) { + + logs, sub, err := _EVMLoadSimulator.contract.WatchLogs(opts, "MemoryWritten") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(EVMLoadSimulatorMemoryWritten) + if err := _EVMLoadSimulator.contract.UnpackLog(event, "MemoryWritten", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseMemoryWritten is a log parse operation binding the contract event 0x542a9e74627abe4fb012aa9be028f3234ff2b2253530c6fa2220e29f03e4215d. +// +// Solidity: event MemoryWritten(uint256[] arr) +func (_EVMLoadSimulator *EVMLoadSimulatorFilterer) ParseMemoryWritten(log types.Log) (*EVMLoadSimulatorMemoryWritten, error) { + event := new(EVMLoadSimulatorMemoryWritten) + if err := _EVMLoadSimulator.contract.UnpackLog(event, "MemoryWritten", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// EVMLoadSimulatorStorageUpdateIterator is returned from FilterStorageUpdate and is used to iterate over the raw logs and unpacked data for StorageUpdate events raised by the EVMLoadSimulator contract. +type EVMLoadSimulatorStorageUpdateIterator struct { + Event *EVMLoadSimulatorStorageUpdate // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *EVMLoadSimulatorStorageUpdateIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(EVMLoadSimulatorStorageUpdate) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(EVMLoadSimulatorStorageUpdate) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *EVMLoadSimulatorStorageUpdateIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *EVMLoadSimulatorStorageUpdateIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// EVMLoadSimulatorStorageUpdate represents a StorageUpdate event raised by the EVMLoadSimulator contract. +type EVMLoadSimulatorStorageUpdate struct { + AccountId *big.Int + Value *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterStorageUpdate is a free log retrieval operation binding the contract event 0xbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc8. +// +// Solidity: event StorageUpdate(uint256 indexed accountId, uint256 value) +func (_EVMLoadSimulator *EVMLoadSimulatorFilterer) FilterStorageUpdate(opts *bind.FilterOpts, accountId []*big.Int) (*EVMLoadSimulatorStorageUpdateIterator, error) { + + var accountIdRule []interface{} + for _, accountIdItem := range accountId { + accountIdRule = append(accountIdRule, accountIdItem) + } + + logs, sub, err := _EVMLoadSimulator.contract.FilterLogs(opts, "StorageUpdate", accountIdRule) + if err != nil { + return nil, err + } + return &EVMLoadSimulatorStorageUpdateIterator{contract: _EVMLoadSimulator.contract, event: "StorageUpdate", logs: logs, sub: sub}, nil +} + +// WatchStorageUpdate is a free log subscription operation binding the contract event 0xbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc8. +// +// Solidity: event StorageUpdate(uint256 indexed accountId, uint256 value) +func (_EVMLoadSimulator *EVMLoadSimulatorFilterer) WatchStorageUpdate(opts *bind.WatchOpts, sink chan<- *EVMLoadSimulatorStorageUpdate, accountId []*big.Int) (event.Subscription, error) { + + var accountIdRule []interface{} + for _, accountIdItem := range accountId { + accountIdRule = append(accountIdRule, accountIdItem) + } + + logs, sub, err := _EVMLoadSimulator.contract.WatchLogs(opts, "StorageUpdate", accountIdRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(EVMLoadSimulatorStorageUpdate) + if err := _EVMLoadSimulator.contract.UnpackLog(event, "StorageUpdate", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseStorageUpdate is a log parse operation binding the contract event 0xbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc8. +// +// Solidity: event StorageUpdate(uint256 indexed accountId, uint256 value) +func (_EVMLoadSimulator *EVMLoadSimulatorFilterer) ParseStorageUpdate(log types.Log) (*EVMLoadSimulatorStorageUpdate, error) { + event := new(EVMLoadSimulatorStorageUpdate) + if err := _EVMLoadSimulator.contract.UnpackLog(event, "StorageUpdate", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// EVMLoadSimulatorSumCalculatedIterator is returned from FilterSumCalculated and is used to iterate over the raw logs and unpacked data for SumCalculated events raised by the EVMLoadSimulator contract. +type EVMLoadSimulatorSumCalculatedIterator struct { + Event *EVMLoadSimulatorSumCalculated // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *EVMLoadSimulatorSumCalculatedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(EVMLoadSimulatorSumCalculated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(EVMLoadSimulatorSumCalculated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *EVMLoadSimulatorSumCalculatedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *EVMLoadSimulatorSumCalculatedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// EVMLoadSimulatorSumCalculated represents a SumCalculated event raised by the EVMLoadSimulator contract. +type EVMLoadSimulatorSumCalculated struct { + Sum *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterSumCalculated is a free log retrieval operation binding the contract event 0xe32d91cad5061d7491327c51e7b799c677b41d033204a5c5022b120f5da4becb. +// +// Solidity: event SumCalculated(uint256 sum) +func (_EVMLoadSimulator *EVMLoadSimulatorFilterer) FilterSumCalculated(opts *bind.FilterOpts) (*EVMLoadSimulatorSumCalculatedIterator, error) { + + logs, sub, err := _EVMLoadSimulator.contract.FilterLogs(opts, "SumCalculated") + if err != nil { + return nil, err + } + return &EVMLoadSimulatorSumCalculatedIterator{contract: _EVMLoadSimulator.contract, event: "SumCalculated", logs: logs, sub: sub}, nil +} + +// WatchSumCalculated is a free log subscription operation binding the contract event 0xe32d91cad5061d7491327c51e7b799c677b41d033204a5c5022b120f5da4becb. +// +// Solidity: event SumCalculated(uint256 sum) +func (_EVMLoadSimulator *EVMLoadSimulatorFilterer) WatchSumCalculated(opts *bind.WatchOpts, sink chan<- *EVMLoadSimulatorSumCalculated) (event.Subscription, error) { + + logs, sub, err := _EVMLoadSimulator.contract.WatchLogs(opts, "SumCalculated") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(EVMLoadSimulatorSumCalculated) + if err := _EVMLoadSimulator.contract.UnpackLog(event, "SumCalculated", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseSumCalculated is a log parse operation binding the contract event 0xe32d91cad5061d7491327c51e7b799c677b41d033204a5c5022b120f5da4becb. +// +// Solidity: event SumCalculated(uint256 sum) +func (_EVMLoadSimulator *EVMLoadSimulatorFilterer) ParseSumCalculated(log types.Log) (*EVMLoadSimulatorSumCalculated, error) { + event := new(EVMLoadSimulatorSumCalculated) + if err := _EVMLoadSimulator.contract.UnpackLog(event, "SumCalculated", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} diff --git a/tests/load/issue/contracts/EVMLoadSimulator.sol b/tests/load/issue/contracts/EVMLoadSimulator.sol new file mode 100644 index 000000000000..0a55385cf402 --- /dev/null +++ b/tests/load/issue/contracts/EVMLoadSimulator.sol @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +contract EVMLoadSimulator { + // Storage mappings for read/write simulations + mapping(uint256 => uint256) private balances; + uint256 public balancesCount; + + // Events for simulating logging overhead + event StorageUpdate(uint256 indexed accountId, uint256 value); + event SumCalculated(uint256 sum); + event HashCalculates(bytes32 hash); + event MemoryWritten(uint256[] arr); + + // Simulate random storage writes + function simulateRandomWrite(uint256 count) external { + for (uint256 i = 1; i <= count; i++) { + uint256 id = balancesCount++; + balances[id] = i; + emit StorageUpdate(id, i); + } + } + + // Simulate overwriting existing values or adding new ones + function simulateModification(uint256 count) external { + for (uint256 i = 1; i <= count; i++) { + if (i < balancesCount) { + uint256 newVal = balances[i] + 1; + balances[i] = newVal; + emit StorageUpdate(i, newVal); + } else { + uint256 id = balancesCount++; + balances[id] = i; + emit StorageUpdate(id, i); + } + } + } + + // Simulate repeated storage reads + function simulateReads(uint256 count) external returns (uint256 sum) { + for (uint256 i = 1; i <= count; i++) { + sum += balances[i]; + } + emit SumCalculated(sum); + } + + // Simulate hashing computation (e.g. keccak256) + function simulateHashing(uint256 rounds) external returns (bytes32 hash) { + hash = keccak256(abi.encodePacked("initial")); + for (uint256 i = 0; i < rounds; i++) { + hash = keccak256(abi.encodePacked(hash, i)); + } + emit HashCalculates(hash); + } + + // Simulate dynamic memory allocation and usage + function simulateMemory(uint256 sizeInWords) external returns (uint256 sum) { + uint256[] memory arr = new uint256[](sizeInWords); + for (uint256 i = 0; i < sizeInWords; i++) { + arr[i] = i; + sum += arr[i]; + } + emit MemoryWritten(arr); + } + + // Simulate deep call stack + function simulateCallDepth(uint256 depth) external { + if (depth > 0) { + this.simulateCallDepth(depth - 1); + } + } +} diff --git a/tests/load/issue/contracts/generate_abi_bindings.sh b/tests/load/issue/contracts/generate_abi_bindings.sh new file mode 100755 index 000000000000..40ba4ac461b3 --- /dev/null +++ b/tests/load/issue/contracts/generate_abi_bindings.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash + +set -euo pipefail + + +# Ensure required tools are installed +if ! command -v solc &> /dev/null; then + echo "Error: solc (Solidity compiler) is not installed, trying to install with brew." + brew install solidity + if ! command -v solc &> /dev/null; then + echo "Error: solc installation failed. Please install it manually." + exit 1 + fi +fi + +CONTRACTS_DIR="$(dirname "$0")" + +for FILE in "${CONTRACTS_DIR}"/*.sol; do + echo "Generating Go bindings from Solidity contract $FILE..." + CONTRACT_NAME=$(basename "$FILE" .sol) + solc --abi --bin --overwrite -o "$CONTRACTS_DIR" "${CONTRACTS_DIR}/${CONTRACT_NAME}.sol" + go run github.com/ava-labs/coreth/cmd/abigen \ + --bin="${CONTRACTS_DIR}/${CONTRACT_NAME}.bin" \ + --abi="${CONTRACTS_DIR}/${CONTRACT_NAME}.abi" \ + --type $CONTRACT_NAME \ + --pkg=contracts \ + --out="${CONTRACTS_DIR}/${CONTRACT_NAME}.go" + rm "${CONTRACTS_DIR}/${CONTRACT_NAME}.bin" "${CONTRACTS_DIR}/${CONTRACT_NAME}.abi" + echo "Generated ${CONTRACT_NAME}.go" +done diff --git a/tests/load/issue/contracts/generate_test.go b/tests/load/issue/contracts/generate_test.go new file mode 100644 index 000000000000..398f23002e7f --- /dev/null +++ b/tests/load/issue/contracts/generate_test.go @@ -0,0 +1,6 @@ +// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package contracts + +//go:generate ./generate_abi_bindings.sh From 1f9b865dcc31829fcff3df00832987c9621e0b2a Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 7 May 2025 10:09:33 +0200 Subject: [PATCH 057/197] Generate contract using libevm abigen --- tests/load/issue/c_opcode.go | 2 +- tests/load/issue/contracts/EVMLoadSimulator.go | 6 +++--- tests/load/issue/contracts/generate_abi_bindings.sh | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/load/issue/c_opcode.go b/tests/load/issue/c_opcode.go index 4d20422765c1..1c9739d656b5 100644 --- a/tests/load/issue/c_opcode.go +++ b/tests/load/issue/c_opcode.go @@ -11,7 +11,7 @@ import ( "math/rand/v2" "time" - "github.com/ava-labs/coreth/accounts/abi/bind" + "github.com/ava-labs/libevm/accounts/abi/bind" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/rpc" diff --git a/tests/load/issue/contracts/EVMLoadSimulator.go b/tests/load/issue/contracts/EVMLoadSimulator.go index fc9e6b6f755d..3e90bf9184ea 100644 --- a/tests/load/issue/contracts/EVMLoadSimulator.go +++ b/tests/load/issue/contracts/EVMLoadSimulator.go @@ -8,9 +8,9 @@ import ( "math/big" "strings" - "github.com/ava-labs/coreth/accounts/abi" - "github.com/ava-labs/coreth/accounts/abi/bind" ethereum "github.com/ava-labs/libevm" + "github.com/ava-labs/libevm/accounts/abi" + "github.com/ava-labs/libevm/accounts/abi/bind" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/event" @@ -32,7 +32,7 @@ var ( // EVMLoadSimulatorMetaData contains all meta data concerning the EVMLoadSimulator contract. var EVMLoadSimulatorMetaData = &bind.MetaData{ ABI: "[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"hash\",\"type\":\"bytes32\"}],\"name\":\"HashCalculates\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"arr\",\"type\":\"uint256[]\"}],\"name\":\"MemoryWritten\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"accountId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"StorageUpdate\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"sum\",\"type\":\"uint256\"}],\"name\":\"SumCalculated\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"balancesCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"depth\",\"type\":\"uint256\"}],\"name\":\"simulateCallDepth\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"rounds\",\"type\":\"uint256\"}],\"name\":\"simulateHashing\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"hash\",\"type\":\"bytes32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"sizeInWords\",\"type\":\"uint256\"}],\"name\":\"simulateMemory\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"sum\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"count\",\"type\":\"uint256\"}],\"name\":\"simulateModification\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"count\",\"type\":\"uint256\"}],\"name\":\"simulateRandomWrite\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"count\",\"type\":\"uint256\"}],\"name\":\"simulateReads\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"sum\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x6080604052348015600e575f5ffd5b5061097b8061001c5f395ff3fe608060405234801561000f575f5ffd5b506004361061007b575f3560e01c8063aae05a6511610059578063aae05a65146100e9578063b77513d114610105578063f05ed79e14610121578063fb0c0012146101515761007b565b80633851d6e71461007f578063542eedd91461009d5780637db6ecb1146100b9575b5f5ffd5b610087610181565b60405161009491906105c4565b60405180910390f35b6100b760048036038101906100b2919061060b565b610187565b005b6100d360048036038101906100ce919061060b565b610205565b6040516100e0919061064e565b60405180910390f35b61010360048036038101906100fe919061060b565b6102af565b005b61011f600480360381019061011a919061060b565b6103b8565b005b61013b6004803603810190610136919061060b565b610443565b60405161014891906105c4565b60405180910390f35b61016b6004803603810190610166919061060b565b610530565b60405161017891906105c4565b60405180910390f35b60015481565b5f811115610202573073ffffffffffffffffffffffffffffffffffffffff1663542eedd96001836101b89190610694565b6040518263ffffffff1660e01b81526004016101d491906105c4565b5f604051808303815f87803b1580156101eb575f5ffd5b505af11580156101fd573d5f5f3e3d5ffd5b505050505b50565b5f6040516020016102159061071b565b6040516020818303038152906040528051906020012090505f5f90505b8281101561027257818160405160200161024d92919061076f565b6040516020818303038152906040528051906020012091508080600101915050610232565b507f30ca2ef0880ae63712fdaf11aefb67752968cff6f845956fcbdfcf421f4647cb816040516102a2919061064e565b60405180910390a1919050565b5f600190505b8181116103b457600154811015610339575f60015f5f8481526020019081526020015f20546102e4919061079a565b9050805f5f8481526020019081526020015f2081905550817fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc88260405161032b91906105c4565b60405180910390a2506103a1565b5f60015f81548092919061034c906107cd565b919050559050815f5f8381526020019081526020015f2081905550807fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc88360405161039791906105c4565b60405180910390a2505b80806103ac906107cd565b9150506102b5565b5050565b5f600190505b81811161043f575f60015f8154809291906103d8906107cd565b919050559050815f5f8381526020019081526020015f2081905550807fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc88360405161042391906105c4565b60405180910390a2508080610437906107cd565b9150506103be565b5050565b5f5f8267ffffffffffffffff81111561045f5761045e610814565b5b60405190808252806020026020018201604052801561048d5781602001602082028036833780820191505090505b5090505f5f90505b838110156104f257808282815181106104b1576104b0610841565b5b6020026020010181815250508181815181106104d0576104cf610841565b5b6020026020010151836104e3919061079a565b92508080600101915050610495565b507f542a9e74627abe4fb012aa9be028f3234ff2b2253530c6fa2220e29f03e4215d816040516105229190610925565b60405180910390a150919050565b5f5f600190505b82811161056f575f5f8281526020019081526020015f20548261055a919061079a565b91508080610567906107cd565b915050610537565b507fe32d91cad5061d7491327c51e7b799c677b41d033204a5c5022b120f5da4becb8160405161059f91906105c4565b60405180910390a1919050565b5f819050919050565b6105be816105ac565b82525050565b5f6020820190506105d75f8301846105b5565b92915050565b5f5ffd5b6105ea816105ac565b81146105f4575f5ffd5b50565b5f81359050610605816105e1565b92915050565b5f602082840312156106205761061f6105dd565b5b5f61062d848285016105f7565b91505092915050565b5f819050919050565b61064881610636565b82525050565b5f6020820190506106615f83018461063f565b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f61069e826105ac565b91506106a9836105ac565b92508282039050818111156106c1576106c0610667565b5b92915050565b5f81905092915050565b7f696e697469616c000000000000000000000000000000000000000000000000005f82015250565b5f6107056007836106c7565b9150610710826106d1565b600782019050919050565b5f610725826106f9565b9150819050919050565b5f819050919050565b61074961074482610636565b61072f565b82525050565b5f819050919050565b610769610764826105ac565b61074f565b82525050565b5f61077a8285610738565b60208201915061078a8284610758565b6020820191508190509392505050565b5f6107a4826105ac565b91506107af836105ac565b92508282019050808211156107c7576107c6610667565b5b92915050565b5f6107d7826105ac565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361080957610808610667565b5b600182019050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b6108a0816105ac565b82525050565b5f6108b18383610897565b60208301905092915050565b5f602082019050919050565b5f6108d38261086e565b6108dd8185610878565b93506108e883610888565b805f5b838110156109185781516108ff88826108a6565b975061090a836108bd565b9250506001810190506108eb565b5085935050505092915050565b5f6020820190508181035f83015261093d81846108c9565b90509291505056fea2646970667358221220473465ddecf81541dfcac22f9cccc88acda0bc45532126ee227eb5dba6fd6a0a64736f6c634300081d0033", + Bin: "0x6080604052348015600e575f5ffd5b5061097b8061001c5f395ff3fe608060405234801561000f575f5ffd5b506004361061007b575f3560e01c8063aae05a6511610059578063aae05a65146100e9578063b77513d114610105578063f05ed79e14610121578063fb0c0012146101515761007b565b80633851d6e71461007f578063542eedd91461009d5780637db6ecb1146100b9575b5f5ffd5b610087610181565b60405161009491906105c4565b60405180910390f35b6100b760048036038101906100b2919061060b565b610187565b005b6100d360048036038101906100ce919061060b565b610205565b6040516100e0919061064e565b60405180910390f35b61010360048036038101906100fe919061060b565b6102af565b005b61011f600480360381019061011a919061060b565b6103b8565b005b61013b6004803603810190610136919061060b565b610443565b60405161014891906105c4565b60405180910390f35b61016b6004803603810190610166919061060b565b610530565b60405161017891906105c4565b60405180910390f35b60015481565b5f811115610202573073ffffffffffffffffffffffffffffffffffffffff1663542eedd96001836101b89190610694565b6040518263ffffffff1660e01b81526004016101d491906105c4565b5f604051808303815f87803b1580156101eb575f5ffd5b505af11580156101fd573d5f5f3e3d5ffd5b505050505b50565b5f6040516020016102159061071b565b6040516020818303038152906040528051906020012090505f5f90505b8281101561027257818160405160200161024d92919061076f565b6040516020818303038152906040528051906020012091508080600101915050610232565b507f30ca2ef0880ae63712fdaf11aefb67752968cff6f845956fcbdfcf421f4647cb816040516102a2919061064e565b60405180910390a1919050565b5f600190505b8181116103b457600154811015610339575f60015f5f8481526020019081526020015f20546102e4919061079a565b9050805f5f8481526020019081526020015f2081905550817fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc88260405161032b91906105c4565b60405180910390a2506103a1565b5f60015f81548092919061034c906107cd565b919050559050815f5f8381526020019081526020015f2081905550807fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc88360405161039791906105c4565b60405180910390a2505b80806103ac906107cd565b9150506102b5565b5050565b5f600190505b81811161043f575f60015f8154809291906103d8906107cd565b919050559050815f5f8381526020019081526020015f2081905550807fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc88360405161042391906105c4565b60405180910390a2508080610437906107cd565b9150506103be565b5050565b5f5f8267ffffffffffffffff81111561045f5761045e610814565b5b60405190808252806020026020018201604052801561048d5781602001602082028036833780820191505090505b5090505f5f90505b838110156104f257808282815181106104b1576104b0610841565b5b6020026020010181815250508181815181106104d0576104cf610841565b5b6020026020010151836104e3919061079a565b92508080600101915050610495565b507f542a9e74627abe4fb012aa9be028f3234ff2b2253530c6fa2220e29f03e4215d816040516105229190610925565b60405180910390a150919050565b5f5f600190505b82811161056f575f5f8281526020019081526020015f20548261055a919061079a565b91508080610567906107cd565b915050610537565b507fe32d91cad5061d7491327c51e7b799c677b41d033204a5c5022b120f5da4becb8160405161059f91906105c4565b60405180910390a1919050565b5f819050919050565b6105be816105ac565b82525050565b5f6020820190506105d75f8301846105b5565b92915050565b5f5ffd5b6105ea816105ac565b81146105f4575f5ffd5b50565b5f81359050610605816105e1565b92915050565b5f602082840312156106205761061f6105dd565b5b5f61062d848285016105f7565b91505092915050565b5f819050919050565b61064881610636565b82525050565b5f6020820190506106615f83018461063f565b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f61069e826105ac565b91506106a9836105ac565b92508282039050818111156106c1576106c0610667565b5b92915050565b5f81905092915050565b7f696e697469616c000000000000000000000000000000000000000000000000005f82015250565b5f6107056007836106c7565b9150610710826106d1565b600782019050919050565b5f610725826106f9565b9150819050919050565b5f819050919050565b61074961074482610636565b61072f565b82525050565b5f819050919050565b610769610764826105ac565b61074f565b82525050565b5f61077a8285610738565b60208201915061078a8284610758565b6020820191508190509392505050565b5f6107a4826105ac565b91506107af836105ac565b92508282019050808211156107c7576107c6610667565b5b92915050565b5f6107d7826105ac565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361080957610808610667565b5b600182019050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b6108a0816105ac565b82525050565b5f6108b18383610897565b60208301905092915050565b5f602082019050919050565b5f6108d38261086e565b6108dd8185610878565b93506108e883610888565b805f5b838110156109185781516108ff88826108a6565b975061090a836108bd565b9250506001810190506108eb565b5085935050505092915050565b5f6020820190508181035f83015261093d81846108c9565b90509291505056fea2646970667358221220bfdf2ebb58e30b7078b5899d8b6202d94fdc2296ad4fba0105d08e5004339dff64736f6c634300081d0033", } // EVMLoadSimulatorABI is the input ABI used to generate the binding from. diff --git a/tests/load/issue/contracts/generate_abi_bindings.sh b/tests/load/issue/contracts/generate_abi_bindings.sh index 40ba4ac461b3..8ab28cbf7047 100755 --- a/tests/load/issue/contracts/generate_abi_bindings.sh +++ b/tests/load/issue/contracts/generate_abi_bindings.sh @@ -19,7 +19,7 @@ for FILE in "${CONTRACTS_DIR}"/*.sol; do echo "Generating Go bindings from Solidity contract $FILE..." CONTRACT_NAME=$(basename "$FILE" .sol) solc --abi --bin --overwrite -o "$CONTRACTS_DIR" "${CONTRACTS_DIR}/${CONTRACT_NAME}.sol" - go run github.com/ava-labs/coreth/cmd/abigen \ + go run github.com/ava-labs/libevm/cmd/abigen@latest \ --bin="${CONTRACTS_DIR}/${CONTRACT_NAME}.bin" \ --abi="${CONTRACTS_DIR}/${CONTRACT_NAME}.abi" \ --type $CONTRACT_NAME \ From 4a4b7d76fdcdbd39a00a642fc0577d023b74de63 Mon Sep 17 00:00:00 2001 From: Rodrigo Villar Date: Wed, 7 May 2025 09:44:58 -0400 Subject: [PATCH 058/197] migrate --- tests/load/README.MD | 129 ++++++++++++++++++++++++ tests/load/agent.go | 19 +--- tests/load/burst_orchestrator.go | 89 ++++++++++++++++ tests/load/gradual_orchestrator.go | 17 ++-- tests/load/gradual_orchestrator_test.go | 17 ++-- 5 files changed, 240 insertions(+), 31 deletions(-) create mode 100644 tests/load/README.MD create mode 100644 tests/load/burst_orchestrator.go diff --git a/tests/load/README.MD b/tests/load/README.MD new file mode 100644 index 000000000000..13b052f21f5c --- /dev/null +++ b/tests/load/README.MD @@ -0,0 +1,129 @@ +# Load + +This package provides generic utilities for blockchain load testing. We break load generation down into the following components: + +- tx issuer(s) +- tx listener(s) +- tracker +- orchestrator + +The transaction issuer(s) and listener(s) may be VM specific and provide the +necessary injected dependencies for the orchestrator. This enables us to +construct different load testing strategies on top of the same re-usable code. +For example, we can re-use these components for a short burst of transactions or +to perform gradual load testing. + +## Architecture + +```mermaid +graph + O["Orchestrator"] + subgraph "Agent 3" + A3_I["Issuer 3"] + A3_L["Listener 3"] + end + subgraph "Agent 2" + A2_I["Issuer 2"] + A2_L["Listener 2"] + end + subgraph "Agent 1" + A1_I["Issuer 1"] + A1_L["Listener 1"] + end + T["Tracker"] + + T --> O + + O --> A1_I + O --> A2_I + O --> A3_I + + A1_I --> A1_L + A2_I --> A2_L + A3_I --> A3_L + + A1_I --> T + A2_I --> T + A3_I --> T + A1_L --> T + A2_L --> T + A3_L --> T +``` + +### Orchestrator + +The orchestrator is responsible for directing issuers and listeners +to send transactions to the network and detect when a transaction is confirmed. +The strategy for how the orchestrator directs issuers varies +between implementations (e.g. short burst vs gradual load). + +### Issuer + +The issuer is responsible for generating and issuing transactions. +It notifies the tracker of all issued transactions. + +### Listener + +The listener is responsible for listening to the network and confirming +transactions or marking them as failed. As it receives transactions, it +notifies the tracker of the transaction status. + +### Tracker + +The tracker is responsible for maintaining metrics for all sent txs. Since the +tracker is used by both the issuers, listeners and the orchestrator, all methods of the +tracker must be thread safe. + +## Default Orchestrators + +This package comes with the following orchestrators: + +### Short Burst + +The short burst orchestator is used to send a fixed number of transactions to the network at +once. This orchestrator is parameterizable via the following: + +- `N`: the number of transactions an issuer will send to the network. +- `timeout`: the maximum amount of time which, after all transactions have been sent, + the orchestrator will wait to hear the confirmation of all outstanding + transactions. + +### Gradual Load + +The gradual load orchestrator sends transactions at an initial rate (TPS) and +increases that rate until hitting the maxiumum desired rate or until the +orchestrator determines that it can no longer make progress. + +The current TPS in the gradual load orchestrator is determined by taking the +number of transactions confirmed in a given time window (`SustainedTime`) and +diving it by `SustainedTime` (in terms of seconds) Furthermore, the orchestator +has `maxAttempt` tries to try and achieve a given TPS before determining that +the given TPS is not achievable. + +Below is the pseudocode for how the gradual load orchestrator determines TPS and +for how it increases TPS: + +``` +currTargetTPS := current TPS we want to achieve +maxAttempts := maximum number of attempts we have to achieve currTargetTPS + +txsPerIssuer := currTargetTPS / numOfIssuers +attempts := 0 + +for each issuer { // Async + send txsPerIssuer txs per second +} + +for { + wait for SustainedTime + + tps := number of accepted txs divided by the SustainedTime + if tps >= currTargetTPS: + increase currTargerTPS by step + iters = 0 + else: + if attempts >= maxAttempts: + fail + iters += 1 +} +``` diff --git a/tests/load/agent.go b/tests/load/agent.go index 16390ac518ab..b0362b5219aa 100644 --- a/tests/load/agent.go +++ b/tests/load/agent.go @@ -3,28 +3,17 @@ package load -type Agent[T, U comparable] struct { +type Agent[T comparable] struct { Issuer Issuer[T] Listener Listener[T] - Tracker Tracker[U] } -func NewAgent[T, U comparable]( +func NewAgent[T comparable]( issuer Issuer[T], listener Listener[T], - tracker Tracker[U], -) Agent[T, U] { - return Agent[T, U]{ +) Agent[T] { + return Agent[T]{ Issuer: issuer, Listener: listener, - Tracker: tracker, } } - -func GetTotalObservedConfirmed[T, U comparable](agents []Agent[T, U]) uint64 { - total := uint64(0) - for _, agent := range agents { - total += agent.Tracker.GetObservedConfirmed() - } - return total -} diff --git a/tests/load/burst_orchestrator.go b/tests/load/burst_orchestrator.go new file mode 100644 index 000000000000..9ff157b55706 --- /dev/null +++ b/tests/load/burst_orchestrator.go @@ -0,0 +1,89 @@ +// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package load + +import ( + "context" + "time" + + "golang.org/x/sync/errgroup" + + "github.com/ava-labs/avalanchego/utils/logging" +) + +var _ orchestrator = (*BurstOrchestrator[any])(nil) + +type BurstOrchestratorConfig struct { + TxsPerIssuer uint64 + Timeout time.Duration +} + +// BurstOrchestrator tests the network by sending a fixed number of +// transactions en masse in a short timeframe. +type BurstOrchestrator[T comparable] struct { + agents []Agent[T] + log logging.Logger + + config BurstOrchestratorConfig +} + +func NewBurstOrchestrator[T comparable]( + agents []Agent[T], + log logging.Logger, + config BurstOrchestratorConfig, +) (*BurstOrchestrator[T], error) { + return &BurstOrchestrator[T]{ + agents: agents, + log: log, + config: config, + }, nil +} + +// Execute orders issuers to send a fixed number of transactions and then waits +// for all of their statuses to be confirmed or for a timeout to occur. +func (o *BurstOrchestrator[T]) Execute(ctx context.Context) error { + observerCtx, observerCancel := context.WithCancel(ctx) + defer observerCancel() + + // start a goroutine to confirm each issuer's transactions + observerGroup := errgroup.Group{} + for _, agent := range o.agents { + observerGroup.Go(func() error { return agent.Listener.Listen(observerCtx) }) + } + + // start issuing transactions sequentially from each issuer + issuerGroup := errgroup.Group{} + for _, agent := range o.agents { + issuerGroup.Go(func() error { + defer agent.Listener.IssuingDone() + for range o.config.TxsPerIssuer { + tx, err := agent.Issuer.GenerateAndIssueTx(ctx) + if err != nil { + return err + } + agent.Listener.RegisterIssued(tx) + } + return nil + }) + } + + // wait for all issuers to finish sending their transactions + if err := issuerGroup.Wait(); err != nil { + return err + } + + ctx, cancel := context.WithTimeout(ctx, o.config.Timeout) + defer cancel() + + // start a goroutine that will cancel the observer group's context + // if either the parent context is cancelled or our timeout elapses + go func() { + <-ctx.Done() + observerCancel() + }() + + // blocks until either all of the issuers have finished or our context + // is cancelled signalling for early termination (with an error) + return observerGroup.Wait() +} diff --git a/tests/load/gradual_orchestrator.go b/tests/load/gradual_orchestrator.go index 4b12aa9d2ee6..5e80f2993548 100644 --- a/tests/load/gradual_orchestrator.go +++ b/tests/load/gradual_orchestrator.go @@ -68,7 +68,8 @@ func DefaultGradualOrchestratorConfig() GradualOrchestratorConfig { // the network can no longer make progress (i.e. the rate at the network accepts // transactions is less than currTargetTPS). type GradualOrchestrator[T, U comparable] struct { - agents []Agent[T, U] + agents []Agent[T] + tracker Tracker[U] log logging.Logger @@ -81,14 +82,16 @@ type GradualOrchestrator[T, U comparable] struct { } func NewGradualOrchestrator[T, U comparable]( - agents []Agent[T, U], + agents []Agent[T], + tracker Tracker[U], log logging.Logger, config GradualOrchestratorConfig, ) (*GradualOrchestrator[T, U], error) { return &GradualOrchestrator[T, U]{ - agents: agents, - log: log, - config: config, + agents: agents, + tracker: tracker, + log: log, + config: config, }, nil } @@ -125,7 +128,7 @@ func (o *GradualOrchestrator[T, U]) Execute(ctx context.Context) error { // 3. the maximum number of attempts to reach a target TPS has been reached func (o *GradualOrchestrator[T, U]) run(ctx context.Context) bool { var ( - prevConfirmed = GetTotalObservedConfirmed(o.agents) + prevConfirmed = o.tracker.GetObservedConfirmed() prevTime = time.Now() currTargetTPS = new(atomic.Uint64) attempts uint64 = 1 @@ -151,7 +154,7 @@ func (o *GradualOrchestrator[T, U]) run(ctx context.Context) bool { break // Case 1 } - currConfirmed := GetTotalObservedConfirmed(o.agents) + currConfirmed := o.tracker.GetObservedConfirmed() currTime := time.Now() tps := computeTPS(prevConfirmed, currConfirmed, currTime.Sub(prevTime)) diff --git a/tests/load/gradual_orchestrator_test.go b/tests/load/gradual_orchestrator_test.go index b4a097895537..9781e10cf73e 100644 --- a/tests/load/gradual_orchestrator_test.go +++ b/tests/load/gradual_orchestrator_test.go @@ -64,7 +64,7 @@ func TestGradualOrchestratorTPS(t *testing.T) { tracker, err := NewPrometheusTracker[ids.ID](prometheus.NewRegistry()) r.NoError(err) - agents := []Agent[ids.ID, ids.ID]{ + agents := []Agent[ids.ID]{ NewAgent( &mockIssuer{ generateTxF: func() (ids.ID, error) { @@ -74,12 +74,12 @@ func TestGradualOrchestratorTPS(t *testing.T) { maxTxs: tt.serverTPS, }, &mockListener{}, - tracker, ), } orchestrator, err := NewGradualOrchestrator( agents, + tracker, logging.NoLog{}, tt.config, ) @@ -108,28 +108,27 @@ func TestGradualOrchestratorExecution(t *testing.T) { tests := []struct { name string - agents []Agent[ids.ID, ids.ID] + agents []Agent[ids.ID] expectedErr error }{ { name: "generator error", - agents: []Agent[ids.ID, ids.ID]{ - NewAgent[ids.ID, ids.ID]( + agents: []Agent[ids.ID]{ + NewAgent[ids.ID]( &mockIssuer{ generateTxF: func() (ids.ID, error) { return ids.Empty, errMockTxGenerator }, }, &mockListener{}, - tracker, ), }, expectedErr: errMockTxGenerator, }, { name: "issuer error", - agents: []Agent[ids.ID, ids.ID]{ - NewAgent[ids.ID, ids.ID]( + agents: []Agent[ids.ID]{ + NewAgent[ids.ID]( &mockIssuer{ generateTxF: func() (ids.ID, error) { return ids.GenerateTestID(), nil @@ -137,7 +136,6 @@ func TestGradualOrchestratorExecution(t *testing.T) { issueTxErr: errMockIssuer, }, &mockListener{}, - tracker, ), }, expectedErr: errMockIssuer, @@ -152,6 +150,7 @@ func TestGradualOrchestratorExecution(t *testing.T) { orchestrator, err := NewGradualOrchestrator( tt.agents, + tracker, logging.NoLog{}, DefaultGradualOrchestratorConfig(), ) From 98957e62407ca357dc4da80e79f96a7ee9df76fc Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 7 May 2025 17:19:27 +0200 Subject: [PATCH 059/197] Pre-adapt to add-load-framework branch --- go.sum | 3 - tests/load/agent.go | 19 ++ tests/load/agent/agent.go | 25 -- tests/load/agent/dependencies.go | 43 --- tests/load/burst_orchestrator.go | 89 ++++++ tests/load/{ => c}/.gitignore | 0 .../contracts/EVMLoadSimulator.go | 0 .../contracts/EVMLoadSimulator.sol | 0 .../contracts/generate_abi_bindings.sh | 0 .../{issue => c}/contracts/generate_test.go | 0 tests/load/{ => c}/dashboard.json | 0 tests/load/{ => c}/execute.go | 41 +-- tests/load/{ => c}/funder.go | 29 +- tests/load/{ => c}/ginkgo_test.go | 2 +- .../distributor.go => c/issuer_distribute.go} | 16 +- .../{issue/c_opcode.go => c/issuer_opcode.go} | 30 +- .../{issue/self.go => c/issuer_simple.go} | 26 +- tests/load/{listen => c}/listener.go | 58 ++-- tests/load/{listen => c}/newhead.go | 2 +- tests/load/{ => c}/prometheus.template.yml | 0 tests/load/{ => c}/readme.md | 4 +- tests/load/{orchestrate => }/dependencies.go | 34 ++- tests/load/gradual_orchestrator.go | 272 ++++++++++++++++++ tests/load/gradual_orchestrator_test.go | 202 +++++++++++++ tests/load/noop_tracker.go | 19 ++ tests/load/orchestrate/burst.go | 91 ------ ...metrics_server.go => prometheus_server.go} | 4 +- tests/load/prometheus_tracker.go | 151 ++++++++++ tests/load/tracker/metrics.go | 78 ----- tests/load/tracker/noop.go | 23 -- tests/load/tracker/tracker.go | 98 ------- 31 files changed, 883 insertions(+), 476 deletions(-) create mode 100644 tests/load/agent.go delete mode 100644 tests/load/agent/agent.go delete mode 100644 tests/load/agent/dependencies.go create mode 100644 tests/load/burst_orchestrator.go rename tests/load/{ => c}/.gitignore (100%) rename tests/load/{issue => c}/contracts/EVMLoadSimulator.go (100%) rename tests/load/{issue => c}/contracts/EVMLoadSimulator.sol (100%) rename tests/load/{issue => c}/contracts/generate_abi_bindings.sh (100%) rename tests/load/{issue => c}/contracts/generate_test.go (100%) rename tests/load/{ => c}/dashboard.json (100%) rename tests/load/{ => c}/execute.go (76%) rename tests/load/{ => c}/funder.go (85%) rename tests/load/{ => c}/ginkgo_test.go (99%) rename tests/load/{issue/distributor.go => c/issuer_distribute.go} (88%) rename tests/load/{issue/c_opcode.go => c/issuer_opcode.go} (88%) rename tests/load/{issue/self.go => c/issuer_simple.go} (82%) rename tests/load/{listen => c}/listener.go (66%) rename tests/load/{listen => c}/newhead.go (99%) rename tests/load/{ => c}/prometheus.template.yml (100%) rename tests/load/{ => c}/readme.md (91%) rename tests/load/{orchestrate => }/dependencies.go (59%) create mode 100644 tests/load/gradual_orchestrator.go create mode 100644 tests/load/gradual_orchestrator_test.go create mode 100644 tests/load/noop_tracker.go delete mode 100644 tests/load/orchestrate/burst.go rename tests/load/{tracker/metrics_server.go => prometheus_server.go} (94%) create mode 100644 tests/load/prometheus_tracker.go delete mode 100644 tests/load/tracker/metrics.go delete mode 100644 tests/load/tracker/noop.go delete mode 100644 tests/load/tracker/tracker.go diff --git a/go.sum b/go.sum index 976e8ac09d9b..6881d4188a19 100644 --- a/go.sum +++ b/go.sum @@ -258,10 +258,7 @@ github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM= -github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= -github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= -github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4= github.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= diff --git a/tests/load/agent.go b/tests/load/agent.go new file mode 100644 index 000000000000..b0362b5219aa --- /dev/null +++ b/tests/load/agent.go @@ -0,0 +1,19 @@ +// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package load + +type Agent[T comparable] struct { + Issuer Issuer[T] + Listener Listener[T] +} + +func NewAgent[T comparable]( + issuer Issuer[T], + listener Listener[T], +) Agent[T] { + return Agent[T]{ + Issuer: issuer, + Listener: listener, + } +} diff --git a/tests/load/agent/agent.go b/tests/load/agent/agent.go deleted file mode 100644 index ffa58774c00c..000000000000 --- a/tests/load/agent/agent.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package agent - -type Agent[T, U comparable] struct { - TxTarget uint64 - Issuer Issuer[T] - Listener Listener[T] - Tracker Tracker[U] -} - -func New[T, U comparable]( - txTarget uint64, - issuer Issuer[T], - listener Listener[T], - tracker Tracker[U], -) *Agent[T, U] { - return &Agent[T, U]{ - TxTarget: txTarget, - Issuer: issuer, - Listener: listener, - Tracker: tracker, - } -} diff --git a/tests/load/agent/dependencies.go b/tests/load/agent/dependencies.go deleted file mode 100644 index 9b0aaa28d044..000000000000 --- a/tests/load/agent/dependencies.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package agent - -import "context" - -type Issuer[T comparable] interface { - // GenerateAndIssueTx generates and sends a tx to the network, and informs the - // tracker that its sent said transaction. It returns the send transaction. - GenerateAndIssueTx(ctx context.Context) (tx T, err error) -} - -type Listener[T comparable] interface { - // Listen listens for transaction confirmations from a node, as well as - // new blocks. - Listen(ctx context.Context) error - // RegisterIssued registers a transaction that was issued, so the listener - // is aware it should track it. - RegisterIssued(T) -} - -// Tracker keeps track of the status of transactions. -// This must be thread-safe, so it can be called in parallel by the issuer(s) or orchestrator. -type Tracker[T comparable] interface { - // Issue records a transaction that was issued, but whose final status is - // not yet known. - Issue(T) - // ObserveConfirmed records a transaction that was confirmed. - ObserveConfirmed(T) - // ObserveFailed records a transaction that failed (e.g. expired) - ObserveFailed(T) - - // GetObservedConfirmed returns the number of transactions that the tracker has - // confirmed were accepted. - GetObservedConfirmed() uint64 - // GetObservedFailed returns the number of transactions that the tracker has - // confirmed failed. - GetObservedFailed() uint64 - - // Log logs the current state of the tracker. - Log() -} diff --git a/tests/load/burst_orchestrator.go b/tests/load/burst_orchestrator.go new file mode 100644 index 000000000000..9ff157b55706 --- /dev/null +++ b/tests/load/burst_orchestrator.go @@ -0,0 +1,89 @@ +// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package load + +import ( + "context" + "time" + + "golang.org/x/sync/errgroup" + + "github.com/ava-labs/avalanchego/utils/logging" +) + +var _ orchestrator = (*BurstOrchestrator[any])(nil) + +type BurstOrchestratorConfig struct { + TxsPerIssuer uint64 + Timeout time.Duration +} + +// BurstOrchestrator tests the network by sending a fixed number of +// transactions en masse in a short timeframe. +type BurstOrchestrator[T comparable] struct { + agents []Agent[T] + log logging.Logger + + config BurstOrchestratorConfig +} + +func NewBurstOrchestrator[T comparable]( + agents []Agent[T], + log logging.Logger, + config BurstOrchestratorConfig, +) (*BurstOrchestrator[T], error) { + return &BurstOrchestrator[T]{ + agents: agents, + log: log, + config: config, + }, nil +} + +// Execute orders issuers to send a fixed number of transactions and then waits +// for all of their statuses to be confirmed or for a timeout to occur. +func (o *BurstOrchestrator[T]) Execute(ctx context.Context) error { + observerCtx, observerCancel := context.WithCancel(ctx) + defer observerCancel() + + // start a goroutine to confirm each issuer's transactions + observerGroup := errgroup.Group{} + for _, agent := range o.agents { + observerGroup.Go(func() error { return agent.Listener.Listen(observerCtx) }) + } + + // start issuing transactions sequentially from each issuer + issuerGroup := errgroup.Group{} + for _, agent := range o.agents { + issuerGroup.Go(func() error { + defer agent.Listener.IssuingDone() + for range o.config.TxsPerIssuer { + tx, err := agent.Issuer.GenerateAndIssueTx(ctx) + if err != nil { + return err + } + agent.Listener.RegisterIssued(tx) + } + return nil + }) + } + + // wait for all issuers to finish sending their transactions + if err := issuerGroup.Wait(); err != nil { + return err + } + + ctx, cancel := context.WithTimeout(ctx, o.config.Timeout) + defer cancel() + + // start a goroutine that will cancel the observer group's context + // if either the parent context is cancelled or our timeout elapses + go func() { + <-ctx.Done() + observerCancel() + }() + + // blocks until either all of the issuers have finished or our context + // is cancelled signalling for early termination (with an error) + return observerGroup.Wait() +} diff --git a/tests/load/.gitignore b/tests/load/c/.gitignore similarity index 100% rename from tests/load/.gitignore rename to tests/load/c/.gitignore diff --git a/tests/load/issue/contracts/EVMLoadSimulator.go b/tests/load/c/contracts/EVMLoadSimulator.go similarity index 100% rename from tests/load/issue/contracts/EVMLoadSimulator.go rename to tests/load/c/contracts/EVMLoadSimulator.go diff --git a/tests/load/issue/contracts/EVMLoadSimulator.sol b/tests/load/c/contracts/EVMLoadSimulator.sol similarity index 100% rename from tests/load/issue/contracts/EVMLoadSimulator.sol rename to tests/load/c/contracts/EVMLoadSimulator.sol diff --git a/tests/load/issue/contracts/generate_abi_bindings.sh b/tests/load/c/contracts/generate_abi_bindings.sh similarity index 100% rename from tests/load/issue/contracts/generate_abi_bindings.sh rename to tests/load/c/contracts/generate_abi_bindings.sh diff --git a/tests/load/issue/contracts/generate_test.go b/tests/load/c/contracts/generate_test.go similarity index 100% rename from tests/load/issue/contracts/generate_test.go rename to tests/load/c/contracts/generate_test.go diff --git a/tests/load/dashboard.json b/tests/load/c/dashboard.json similarity index 100% rename from tests/load/dashboard.json rename to tests/load/c/dashboard.json diff --git a/tests/load/execute.go b/tests/load/c/execute.go similarity index 76% rename from tests/load/execute.go rename to tests/load/c/execute.go index 62a9ed9f709c..8079ed9e1bda 100644 --- a/tests/load/execute.go +++ b/tests/load/c/execute.go @@ -1,29 +1,23 @@ // Copyright (C) 2025, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package load +package c import ( "context" "crypto/ecdsa" "fmt" "math/big" - "os" "time" - "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/ethclient" - "github.com/ava-labs/libevm/log" "github.com/ava-labs/libevm/params" "github.com/prometheus/client_golang/prometheus" - "github.com/ava-labs/avalanchego/tests/load/agent" - "github.com/ava-labs/avalanchego/tests/load/issue" - "github.com/ava-labs/avalanchego/tests/load/listen" - "github.com/ava-labs/avalanchego/tests/load/orchestrate" - "github.com/ava-labs/avalanchego/tests/load/tracker" + "github.com/ava-labs/avalanchego/tests/load" "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" + "github.com/ava-labs/avalanchego/utils/logging" ethcrypto "github.com/ava-labs/libevm/crypto" ) @@ -38,8 +32,7 @@ type config struct { } func execute(ctx context.Context, preFundedKeys []*secp256k1.PrivateKey, config config) error { - logger := log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)) - log.SetDefault(logger) + logger := logging.NewLogger("") keys, err := fixKeysCount(preFundedKeys, int(config.agents)) if err != nil { @@ -57,24 +50,27 @@ func execute(ctx context.Context, preFundedKeys []*secp256k1.PrivateKey, config } registry := prometheus.NewRegistry() - metricsServer := tracker.NewMetricsServer("127.0.0.1:8082", registry) - tracker := tracker.New(registry) + metricsServer := load.NewPrometheusServer("127.0.0.1:8082", registry) + tracker, err := load.NewPrometheusTracker[*types.Transaction](registry, logger) + if err != nil { + return fmt.Errorf("creating tracker: %w", err) + } - agents := make([]*agent.Agent[*types.Transaction, common.Hash], config.agents) + agents := make([]load.Agent[*types.Transaction], config.agents) for i := range agents { endpoint := config.endpoints[i%len(config.endpoints)] client, err := ethclient.DialContext(ctx, endpoint) if err != nil { return fmt.Errorf("dialing %s: %w", endpoint, err) } - issuer, err := issue.NewSelf(ctx, client, tracker, + issuer, err := NewSimpleIssuer(ctx, client, tracker, big.NewInt(config.maxFeeCap), keys[i], config.issuePeriod) if err != nil { return fmt.Errorf("creating issuer: %w", err) } address := ethcrypto.PubkeyToAddress(keys[i].PublicKey) - listener := listen.New(client, tracker, config.txsPerAgent, address) - agents[i] = agent.New[*types.Transaction, common.Hash](config.txsPerAgent, issuer, listener, tracker) + listener := NewListener(client, tracker, address) + agents[i] = load.NewAgent(issuer, listener) } metricsErrCh, err := metricsServer.Start() @@ -84,7 +80,16 @@ func execute(ctx context.Context, preFundedKeys []*secp256k1.PrivateKey, config orchestratorCtx, orchestratorCancel := context.WithCancel(ctx) defer orchestratorCancel() - orchestrator := orchestrate.NewBurstOrchestrator(agents, config.finalTimeout) + orchestratorConfig := load.BurstOrchestratorConfig{ + TxsPerIssuer: config.txsPerAgent, + Timeout: config.finalTimeout, + } + orchestrator, err := load.NewBurstOrchestrator(agents, logger, orchestratorConfig) + if err != nil { + _ = metricsServer.Stop() + return fmt.Errorf("creating orchestrator: %w", err) + } + orchestratorErrCh := make(chan error) go func() { orchestratorErrCh <- orchestrator.Execute(orchestratorCtx) diff --git a/tests/load/funder.go b/tests/load/c/funder.go similarity index 85% rename from tests/load/funder.go rename to tests/load/c/funder.go index c701d2c4941f..462cb278cd84 100644 --- a/tests/load/funder.go +++ b/tests/load/c/funder.go @@ -1,7 +1,7 @@ // Copyright (C) 2025, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package load +package c import ( "context" @@ -17,11 +17,8 @@ import ( "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/types" - "github.com/ava-labs/avalanchego/tests/load/agent" - "github.com/ava-labs/avalanchego/tests/load/issue" - "github.com/ava-labs/avalanchego/tests/load/listen" - "github.com/ava-labs/avalanchego/tests/load/orchestrate" - "github.com/ava-labs/avalanchego/tests/load/tracker" + "github.com/ava-labs/avalanchego/tests/load" + "github.com/ava-labs/avalanchego/utils/logging" ethcrypto "github.com/ava-labs/libevm/crypto" ) @@ -70,17 +67,23 @@ func ensureMinimumFunds(ctx context.Context, endpoint string, keys []*ecdsa.Priv } maxFundsAddress := ethcrypto.PubkeyToAddress(maxFundsKey.PublicKey) - txTarget := uint64(len(needFundsKeys)) - issuer, err := issue.NewDistributor(ctx, client, maxFundsKey, needFundsKeys) + issuer, err := NewDistributingIssuer(ctx, client, maxFundsKey, needFundsKeys) if err != nil { return fmt.Errorf("creating issuer: %w", err) } - tracker := tracker.NewNoop() - listener := listen.New(client, tracker, txTarget, maxFundsAddress) - agents := []*agent.Agent[*types.Transaction, common.Hash]{ - agent.New(txTarget, issuer, listener, tracker), + tracker := load.NewNoopTracker[*types.Transaction]() + listener := NewListener(client, tracker, maxFundsAddress) + agents := []load.Agent[*types.Transaction]{ + load.NewAgent(issuer, listener), + } + orchestratorConfig := load.BurstOrchestratorConfig{ + TxsPerIssuer: uint64(len(needFundsKeys)), + Timeout: time.Second, + } + orchestrator, err := load.NewBurstOrchestrator(agents, logging.NoLog{}, orchestratorConfig) + if err != nil { + return fmt.Errorf("creating orchestrator: %w", err) } - orchestrator := orchestrate.NewBurstOrchestrator(agents, time.Second) err = orchestrator.Execute(ctx) if err != nil { diff --git a/tests/load/ginkgo_test.go b/tests/load/c/ginkgo_test.go similarity index 99% rename from tests/load/ginkgo_test.go rename to tests/load/c/ginkgo_test.go index ad0470d86a97..75c4dd082785 100644 --- a/tests/load/ginkgo_test.go +++ b/tests/load/c/ginkgo_test.go @@ -1,7 +1,7 @@ // Copyright (C) 2025, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package load +package c import ( "context" diff --git a/tests/load/issue/distributor.go b/tests/load/c/issuer_distribute.go similarity index 88% rename from tests/load/issue/distributor.go rename to tests/load/c/issuer_distribute.go index 10b3eb0aa03b..afc201bddde4 100644 --- a/tests/load/issue/distributor.go +++ b/tests/load/c/issuer_distribute.go @@ -1,7 +1,7 @@ // Copyright (C) 2025, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package issue +package c import ( "context" @@ -18,15 +18,15 @@ import ( ) type DistributorClient interface { - EthClient + EthClientSimpleIssuer EstimateBaseFee(ctx context.Context) (*big.Int, error) SuggestGasTipCap(ctx context.Context) (*big.Int, error) } // Issuer issues transactions to a node. -type Distributor struct { +type DistributingIssuer struct { // Injected parameters - client EthClient + client DistributorClient from *ecdsa.PrivateKey to map[*ecdsa.PrivateKey]*big.Int @@ -41,9 +41,9 @@ type Distributor struct { nonce uint64 } -func NewDistributor(ctx context.Context, client DistributorClient, +func NewDistributingIssuer(ctx context.Context, client DistributorClient, from *ecdsa.PrivateKey, to map[*ecdsa.PrivateKey]*big.Int, -) (*Distributor, error) { +) (*DistributingIssuer, error) { address := ethcrypto.PubkeyToAddress(from.PublicKey) blockNumber := (*big.Int)(nil) nonce, err := client.NonceAt(ctx, address, blockNumber) @@ -66,7 +66,7 @@ func NewDistributor(ctx context.Context, client DistributorClient, return nil, fmt.Errorf("getting chain id: %w", err) } - return &Distributor{ + return &DistributingIssuer{ client: client, from: from, address: address, @@ -79,7 +79,7 @@ func NewDistributor(ctx context.Context, client DistributorClient, }, nil } -func (d *Distributor) GenerateAndIssueTx(ctx context.Context) (*types.Transaction, error) { +func (d *DistributingIssuer) GenerateAndIssueTx(ctx context.Context) (*types.Transaction, error) { var toKey *ecdsa.PrivateKey var funds *big.Int for toKey, funds = range d.to { diff --git a/tests/load/issue/c_opcode.go b/tests/load/c/issuer_opcode.go similarity index 88% rename from tests/load/issue/c_opcode.go rename to tests/load/c/issuer_opcode.go index 1c9739d656b5..12141e815616 100644 --- a/tests/load/issue/c_opcode.go +++ b/tests/load/c/issuer_opcode.go @@ -1,7 +1,7 @@ // Copyright (C) 2025, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package issue +package c import ( "context" @@ -16,23 +16,23 @@ import ( "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/rpc" - evmloadsimulator "github.com/ava-labs/avalanchego/tests/load/issue/contracts" + evmloadsimulator "github.com/ava-labs/avalanchego/tests/load/c/contracts" ethcrypto "github.com/ava-labs/libevm/crypto" ) -type BindEthClient interface { - EthClient +type EthClientOpcodeIssuer interface { + EthClientSimpleIssuer bind.DeployBackend bind.ContractBackend } -// OpCodeSimulator generates and issues transactions that randomly call the +// OpcodeIssuer generates and issues transactions that randomly call the // external functions of the [evmloadsimulator.EVMLoadSimulator] contract // instance that it deploys. -type OpCodeSimulator struct { +type OpcodeIssuer struct { // Injected parameters - client BindEthClient - tracker Tracker + client EthClientOpcodeIssuer + tracker IssueTracker senderKey *ecdsa.PrivateKey maxFeeCap *big.Int issuePeriod time.Duration @@ -50,12 +50,12 @@ type OpCodeSimulator struct { func NewOpCodeSimulator( ctx context.Context, - client BindEthClient, - tracker Tracker, + client EthClientOpcodeIssuer, + tracker IssueTracker, maxFeeCap *big.Int, key *ecdsa.PrivateKey, issuePeriod time.Duration, -) (*OpCodeSimulator, error) { +) (*OpcodeIssuer, error) { chainID, err := client.ChainID(ctx) if err != nil { return nil, fmt.Errorf("getting chain id: %w", err) @@ -82,7 +82,7 @@ func NewOpCodeSimulator( return nil, fmt.Errorf("waiting for simulator contract to be mined: %w", err) } - return &OpCodeSimulator{ + return &OpcodeIssuer{ client: client, tracker: tracker, senderKey: key, @@ -96,7 +96,7 @@ func NewOpCodeSimulator( }, nil } -func (o *OpCodeSimulator) GenerateAndIssueTx(ctx context.Context) (tx *types.Transaction, err error) { +func (o *OpcodeIssuer) GenerateAndIssueTx(ctx context.Context) (tx *types.Transaction, err error) { if o.issuePeriod > 0 && !o.lastIssue.IsZero() && time.Since(o.lastIssue) < o.issuePeriod { timer := time.NewTimer(o.issuePeriod - time.Since(o.lastIssue)) @@ -150,7 +150,7 @@ func (o *OpCodeSimulator) GenerateAndIssueTx(ctx context.Context) (tx *types.Tra } o.nonce++ - o.tracker.Issue(tx.Hash()) + o.tracker.Issue(tx) o.lastIssue = time.Now() return tx, err } @@ -175,7 +175,7 @@ func allLoadTypes() []string { } } -func (o *OpCodeSimulator) newTxOpts(ctx context.Context) (*bind.TransactOpts, error) { +func (o *OpcodeIssuer) newTxOpts(ctx context.Context) (*bind.TransactOpts, error) { return newTxOpts(ctx, o.senderKey, o.chainID, o.maxFeeCap, o.maxTipCap, o.nonce) } diff --git a/tests/load/issue/self.go b/tests/load/c/issuer_simple.go similarity index 82% rename from tests/load/issue/self.go rename to tests/load/c/issuer_simple.go index d9420f4cd647..611a2a92210b 100644 --- a/tests/load/issue/self.go +++ b/tests/load/c/issuer_simple.go @@ -1,7 +1,7 @@ // Copyright (C) 2025, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package issue +package c import ( "context" @@ -17,21 +17,21 @@ import ( ethcrypto "github.com/ava-labs/libevm/crypto" ) -type EthClient interface { +type EthClientSimpleIssuer interface { ChainID(ctx context.Context) (*big.Int, error) NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) SendTransaction(ctx context.Context, tx *types.Transaction) error } -type Tracker interface { - Issue(txHash common.Hash) +type IssueTracker interface { + Issue(tx *types.Transaction) } -// Self generates and issues transactions sending 0 fund to the sender. -type Self struct { +// SimplerIssuer generates and issues transactions sending 0 fund to the sender. +type SimplerIssuer struct { // Injected parameters - client EthClient - tracker Tracker + client EthClientSimpleIssuer + tracker IssueTracker key *ecdsa.PrivateKey gasFeeCap *big.Int issuePeriod time.Duration @@ -47,9 +47,9 @@ type Self struct { lastIssue time.Time } -func NewSelf(ctx context.Context, client EthClient, tracker Tracker, +func NewSimpleIssuer(ctx context.Context, client EthClientSimpleIssuer, tracker IssueTracker, maxFeeCap *big.Int, key *ecdsa.PrivateKey, issuePeriod time.Duration, -) (*Self, error) { +) (*SimplerIssuer, error) { address := ethcrypto.PubkeyToAddress(key.PublicKey) blockNumber := (*big.Int)(nil) nonce, err := client.NonceAt(ctx, address, blockNumber) @@ -66,7 +66,7 @@ func NewSelf(ctx context.Context, client EthClient, tracker Tracker, return nil, fmt.Errorf("getting chain id: %w", err) } - return &Self{ + return &SimplerIssuer{ client: client, tracker: tracker, key: key, @@ -80,7 +80,7 @@ func NewSelf(ctx context.Context, client EthClient, tracker Tracker, }, nil } -func (i *Self) GenerateAndIssueTx(ctx context.Context) (*types.Transaction, error) { +func (i *SimplerIssuer) GenerateAndIssueTx(ctx context.Context) (*types.Transaction, error) { tx, err := types.SignNewTx(i.key, i.signer, &types.DynamicFeeTx{ ChainID: i.chainID, Nonce: i.nonce, @@ -110,7 +110,7 @@ func (i *Self) GenerateAndIssueTx(ctx context.Context) (*types.Transaction, erro return nil, fmt.Errorf("issuing transaction with nonce %d: %w", i.nonce, err) } i.nonce++ - i.tracker.Issue(tx.Hash()) + i.tracker.Issue(tx) i.lastIssue = time.Now() return tx, nil } diff --git a/tests/load/listen/listener.go b/tests/load/c/listener.go similarity index 66% rename from tests/load/listen/listener.go rename to tests/load/c/listener.go index f1ae666cefe3..00eb0e4cf3f2 100644 --- a/tests/load/listen/listener.go +++ b/tests/load/c/listener.go @@ -1,7 +1,7 @@ // Copyright (C) 2025, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package listen +package c import ( "context" @@ -13,37 +13,35 @@ import ( "github.com/ava-labs/libevm/core/types" ) -type EthClient interface { +type EthClientListener interface { NonceAt(ctx context.Context, addr common.Address, blockNumber *big.Int) (uint64, error) NewHeadSubscriber } -type Tracker interface { - ObserveConfirmed(txHash common.Hash) - ObserveFailed(txHash common.Hash) +type Observer interface { + ObserveConfirmed(tx *types.Transaction) + ObserveFailed(tx *types.Transaction) } // Listener listens for transaction confirmations from a node. type Listener struct { // Injected parameters - client EthClient - tracker Tracker - txTarget uint64 - address common.Address + client EthClientListener + tracker Observer + address common.Address // Internal state - mutex sync.Mutex - issued uint64 - lastIssuedNonce uint64 - inFlightTxHashes []common.Hash + mutex sync.Mutex + allIssued bool + lastIssuedNonce uint64 + inFlightTxs []*types.Transaction } -func New(client EthClient, tracker Tracker, txTarget uint64, address common.Address) *Listener { +func NewListener(client EthClientListener, tracker Observer, address common.Address) *Listener { return &Listener{ - client: client, - tracker: tracker, - txTarget: txTarget, - address: address, + client: client, + tracker: tracker, + address: address, } } @@ -67,18 +65,17 @@ func (l *Listener) Listen(ctx context.Context) error { } l.mutex.Lock() - confirmed := uint64(len(l.inFlightTxHashes)) + confirmed := uint64(len(l.inFlightTxs)) if nonce < l.lastIssuedNonce { // lagging behind last issued nonce lag := l.lastIssuedNonce - nonce confirmed -= lag } for index := range confirmed { - txHash := l.inFlightTxHashes[index] - l.tracker.ObserveConfirmed(txHash) + tx := l.inFlightTxs[index] + l.tracker.ObserveConfirmed(tx) } - l.inFlightTxHashes = l.inFlightTxHashes[confirmed:] - finished := l.issued == l.txTarget && len(l.inFlightTxHashes) == 0 - if finished { + l.inFlightTxs = l.inFlightTxs[confirmed:] + if l.allIssued && len(l.inFlightTxs) == 0 { l.mutex.Unlock() return nil } @@ -97,16 +94,21 @@ func (l *Listener) Listen(ctx context.Context) error { func (l *Listener) RegisterIssued(tx *types.Transaction) { l.mutex.Lock() defer l.mutex.Unlock() - l.issued++ l.lastIssuedNonce = tx.Nonce() - l.inFlightTxHashes = append(l.inFlightTxHashes, tx.Hash()) + l.inFlightTxs = append(l.inFlightTxs, tx) +} + +func (l *Listener) IssuingDone() { + l.mutex.Lock() + defer l.mutex.Unlock() + l.allIssued = true } func (l *Listener) markRemainingAsFailed() { l.mutex.Lock() defer l.mutex.Unlock() - for _, txHash := range l.inFlightTxHashes { + for _, txHash := range l.inFlightTxs { l.tracker.ObserveFailed(txHash) } - l.inFlightTxHashes = nil + l.inFlightTxs = nil } diff --git a/tests/load/listen/newhead.go b/tests/load/c/newhead.go similarity index 99% rename from tests/load/listen/newhead.go rename to tests/load/c/newhead.go index 986b826656fa..2ebc0e8f386c 100644 --- a/tests/load/listen/newhead.go +++ b/tests/load/c/newhead.go @@ -1,7 +1,7 @@ // Copyright (C) 2025, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package listen +package c import ( "context" diff --git a/tests/load/prometheus.template.yml b/tests/load/c/prometheus.template.yml similarity index 100% rename from tests/load/prometheus.template.yml rename to tests/load/c/prometheus.template.yml diff --git a/tests/load/readme.md b/tests/load/c/readme.md similarity index 91% rename from tests/load/readme.md rename to tests/load/c/readme.md index 57d617277610..c04381f5bc76 100644 --- a/tests/load/readme.md +++ b/tests/load/c/readme.md @@ -2,7 +2,7 @@ ## Prometheus -1. Navigate to this directory with `cd tests/load`. +1. Navigate to this directory with `cd tests/load/c`. 1. Setup the Prometheus configuration file: `envsubst < prometheus.template.yml > prometheus.yml` 1. Launch Prometheus using the dev shell: @@ -39,5 +39,5 @@ From the root of the repository: ```bash -./bin/ginkgo -v tests/load -- --avalanchego-path=$PWD/build/avalanchego +./bin/ginkgo -v tests/load/c -- --avalanchego-path=$PWD/build/avalanchego ``` diff --git a/tests/load/orchestrate/dependencies.go b/tests/load/dependencies.go similarity index 59% rename from tests/load/orchestrate/dependencies.go rename to tests/load/dependencies.go index 7fb985201e5d..613b2bc0f8d1 100644 --- a/tests/load/orchestrate/dependencies.go +++ b/tests/load/dependencies.go @@ -1,29 +1,28 @@ -// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package orchestrate +package load import "context" -type TxGenerator[T comparable] interface { - // GenerateTx returns a valid transaction. - GenerateTx() (T, error) +type Issuer[T comparable] interface { + // GenerateAndIssueTx generates and sends a tx to the network, and informs the + // tracker that it sent said transaction. It returns the sent transaction. + GenerateAndIssueTx(ctx context.Context) (tx T, err error) } -type Issuer[T comparable] interface { +type Listener[T comparable] interface { // Listen for the final status of transactions and notify the tracker - // Listen stops if the context is done, an error occurs, or if the issuer - // has sent all their transactions. + // Listen stops if the context is done, an error occurs, or if it received + // all the transactions issued and the issuer no longer issues any. // Listen MUST return a nil error if the context is canceled. Listen(ctx context.Context) error - // Stop notifies the issuer that no further transactions will be issued. - // If a transaction is issued after Stop has been called, the issuer should error. - Stop() + // RegisterIssued informs the listener that a transaction was issued. + RegisterIssued(tx T) - // Issue sends a tx to the network, and informs the tracker that its sent - // said transaction. - IssueTx(ctx context.Context, tx T) error + // IssuingDone informs the listener that no more transactions will be issued. + IssuingDone() } // Tracker keeps track of the status of transactions. @@ -47,3 +46,10 @@ type Tracker[T comparable] interface { // confirmed failed. GetObservedFailed() uint64 } + +// orchestrator executes the load test by coordinating the issuers to send +// transactions, in a manner depending on the implementation. +type orchestrator interface { + // Execute the load test + Execute(ctx context.Context) error +} diff --git a/tests/load/gradual_orchestrator.go b/tests/load/gradual_orchestrator.go new file mode 100644 index 000000000000..5e80f2993548 --- /dev/null +++ b/tests/load/gradual_orchestrator.go @@ -0,0 +1,272 @@ +// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package load + +import ( + "context" + "errors" + "math" + "sync/atomic" + "time" + + "go.uber.org/zap" + "golang.org/x/sync/errgroup" + + "github.com/ava-labs/avalanchego/utils/logging" +) + +var ( + _ orchestrator = (*GradualOrchestrator[any, any])(nil) + + ErrFailedToReachTargetTPS = errors.New("failed to reach target TPS") +) + +type GradualOrchestratorConfig struct { + // The maximum TPS the orchestrator should aim for. + MaxTPS uint64 + // The minimum TPS the orchestrator should start with. + MinTPS uint64 + // The step size to increase the TPS by. + Step uint64 + + // The factor by which to pad the number of txs an issuer sends per second + // for example, if targetTPS = 1000 and numIssuers = 10, then each issuer + // will send (1000/10)*TxRateMultiplier transactions per second. + // + // Maintaining a multiplier above target provides a toggle to keep load + // persistently above instead of below target. This ensures load generation + // does not pause issuance at the target and persistently under-issue and + // fail to account for the time it takes to add more load. + TxRateMultiplier float64 + + // The time period which TPS is averaged over + // Similarly, the time period which the orchestrator will wait before + // computing the average TPS. + SustainedTime time.Duration + // The number of attempts to try achieving a given target TPS before giving up. + MaxAttempts uint64 + + // Whether the orchestrator should return if the maxTPS has been reached + Terminate bool +} + +func DefaultGradualOrchestratorConfig() GradualOrchestratorConfig { + return GradualOrchestratorConfig{ + MaxTPS: 5_000, + MinTPS: 1_000, + Step: 1_000, + TxRateMultiplier: 1.3, + SustainedTime: 20 * time.Second, + MaxAttempts: 3, + Terminate: true, + } +} + +// GradualOrchestrator tests the network by continuously sending +// transactions at a given rate (currTargetTPS) and increasing that rate until it detects that +// the network can no longer make progress (i.e. the rate at the network accepts +// transactions is less than currTargetTPS). +type GradualOrchestrator[T, U comparable] struct { + agents []Agent[T] + tracker Tracker[U] + + log logging.Logger + + maxObservedTPS atomic.Uint64 + + observerGroup errgroup.Group + issuerGroup *errgroup.Group + + config GradualOrchestratorConfig +} + +func NewGradualOrchestrator[T, U comparable]( + agents []Agent[T], + tracker Tracker[U], + log logging.Logger, + config GradualOrchestratorConfig, +) (*GradualOrchestrator[T, U], error) { + return &GradualOrchestrator[T, U]{ + agents: agents, + tracker: tracker, + log: log, + config: config, + }, nil +} + +func (o *GradualOrchestrator[T, U]) Execute(ctx context.Context) error { + ctx, cancel := context.WithCancel(ctx) + + // start a goroutine to confirm each issuer's transactions + for _, agent := range o.agents { + o.observerGroup.Go(func() error { return agent.Listener.Listen(ctx) }) + } + + // start the test and block until it's done + success := o.run(ctx) + + var err error + if !success { + err = ErrFailedToReachTargetTPS + } + + // stop the observers and issuers + cancel() + + // block until both the observers and issuers have stopped + return errors.Join(o.issuerGroup.Wait(), o.observerGroup.Wait(), err) +} + +// run the gradual load test by continuously increasing the rate at which +// transactions are sent +// +// run blocks until one of the following conditions is met: +// +// 1. an issuer has errored +// 2. the max TPS target has been reached and we can terminate +// 3. the maximum number of attempts to reach a target TPS has been reached +func (o *GradualOrchestrator[T, U]) run(ctx context.Context) bool { + var ( + prevConfirmed = o.tracker.GetObservedConfirmed() + prevTime = time.Now() + currTargetTPS = new(atomic.Uint64) + attempts uint64 = 1 + // true if the orchestrator has reached the max TPS target + achievedTargetTPS bool + ) + + currTargetTPS.Store(o.config.MinTPS) + + issuerGroup, issuerCtx := errgroup.WithContext(ctx) + o.issuerGroup = issuerGroup + + o.issueTxs(issuerCtx, currTargetTPS) + + for { + // wait for the sustained time to pass or for the context to be cancelled + select { + case <-time.After(o.config.SustainedTime): + case <-issuerCtx.Done(): // the parent context was cancelled or an issuer errored + } + + if issuerCtx.Err() != nil { + break // Case 1 + } + + currConfirmed := o.tracker.GetObservedConfirmed() + currTime := time.Now() + + tps := computeTPS(prevConfirmed, currConfirmed, currTime.Sub(prevTime)) + o.setMaxObservedTPS(tps) + + // if max TPS target has been reached and we don't terminate, then continue here + // so we do not keep increasing the target TPS + if achievedTargetTPS && !o.config.Terminate { + o.log.Info( + "current network state", + zap.Uint64("current TPS", tps), + zap.Uint64("max observed TPS", o.maxObservedTPS.Load()), + ) + continue + } + + if tps >= currTargetTPS.Load() { + if currTargetTPS.Load() >= o.config.MaxTPS { + achievedTargetTPS = true + o.log.Info( + "max TPS target reached", + zap.Uint64("max TPS target", currTargetTPS.Load()), + zap.Uint64("average TPS", tps), + ) + if o.config.Terminate { + o.log.Info("terminating orchestrator") + break // Case 2 + } else { + o.log.Info("orchestrator will now continue running at max TPS") + continue + } + } + o.log.Info( + "increasing TPS", + zap.Uint64("previous target TPS", currTargetTPS.Load()), + zap.Uint64("average TPS", tps), + zap.Uint64("new target TPS", currTargetTPS.Load()+o.config.Step), + ) + currTargetTPS.Add(o.config.Step) + attempts = 1 + } else { + if attempts >= o.config.MaxAttempts { + o.log.Info( + "max attempts reached", + zap.Uint64("attempted target TPS", currTargetTPS.Load()), + zap.Uint64("number of attempts", attempts), + ) + break // Case 3 + } + o.log.Info( + "failed to reach target TPS, retrying", + zap.Uint64("current target TPS", currTargetTPS.Load()), + zap.Uint64("average TPS", tps), + zap.Uint64("attempt number", attempts), + ) + attempts++ + } + + prevConfirmed = currConfirmed + prevTime = currTime + } + + return achievedTargetTPS +} + +// GetObservedIssued returns the max TPS the orchestrator observed +func (o *GradualOrchestrator[T, U]) GetMaxObservedTPS() uint64 { + return o.maxObservedTPS.Load() +} + +// start a goroutine to each issuer to continuously send transactions +// if an issuer errors, all other issuers will stop as well. +func (o *GradualOrchestrator[T, U]) issueTxs(ctx context.Context, currTargetTPS *atomic.Uint64) { + for _, agent := range o.agents { + o.issuerGroup.Go(func() error { + for { + if ctx.Err() != nil { + return nil //nolint:nilerr + } + currTime := time.Now() + txsPerIssuer := uint64(math.Ceil(float64(currTargetTPS.Load())/float64(len(o.agents))) * o.config.TxRateMultiplier) + // always listen until listener context is cancelled, do not call agent.Listener.IssuingDone(). + for range txsPerIssuer { + tx, err := agent.Issuer.GenerateAndIssueTx(ctx) + if err != nil { + return err + } + agent.Listener.RegisterIssued(tx) + } + diff := time.Second - time.Since(currTime) + if diff > 0 { + time.Sleep(diff) + } + } + }) + } +} + +// setMaxObservedTPS only if tps > the current max observed TPS. +func (o *GradualOrchestrator[T, U]) setMaxObservedTPS(tps uint64) { + if tps > o.maxObservedTPS.Load() { + o.maxObservedTPS.Store(tps) + } +} + +func computeTPS(initial uint64, final uint64, duration time.Duration) uint64 { + if duration <= 0 { + return 0 + } + + numTxs := final - initial + tps := float64(numTxs) / duration.Seconds() + + return uint64(tps) +} diff --git a/tests/load/gradual_orchestrator_test.go b/tests/load/gradual_orchestrator_test.go new file mode 100644 index 000000000000..66ad2b35e911 --- /dev/null +++ b/tests/load/gradual_orchestrator_test.go @@ -0,0 +1,202 @@ +// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package load + +import ( + "context" + "errors" + "math" + "testing" + "time" + + "github.com/prometheus/client_golang/prometheus" + "github.com/stretchr/testify/require" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/logging" +) + +var _ Issuer[ids.ID] = (*mockIssuer)(nil) + +func TestGradualOrchestratorTPS(t *testing.T) { + tests := []struct { + name string + serverTPS uint64 + config GradualOrchestratorConfig + expectedErr error + }{ + { + name: "orchestrator achieves max TPS", + serverTPS: math.MaxUint64, + config: GradualOrchestratorConfig{ + MaxTPS: 2_000, + MinTPS: 1_000, + Step: 1_000, + TxRateMultiplier: 1.5, + SustainedTime: time.Second, + MaxAttempts: 2, + Terminate: true, + }, + }, + { + name: "orchestrator TPS limited by network", + serverTPS: 1_000, + config: GradualOrchestratorConfig{ + MaxTPS: 2_000, + MinTPS: 1_000, + Step: 1_000, + TxRateMultiplier: 1.3, + SustainedTime: time.Second, + MaxAttempts: 2, + Terminate: true, + }, + expectedErr: ErrFailedToReachTargetTPS, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := require.New(t) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + tracker, err := NewPrometheusTracker[ids.ID](prometheus.NewRegistry(), logging.NoLog{}) + r.NoError(err) + + agents := []Agent[ids.ID]{ + NewAgent( + &mockIssuer{ + generateTxF: func() (ids.ID, error) { + return ids.GenerateTestID(), nil + }, + tracker: tracker, + maxTxs: tt.serverTPS, + }, + &mockListener{}, + ), + } + + orchestrator, err := NewGradualOrchestrator( + agents, + tracker, + logging.NoLog{}, + tt.config, + ) + r.NoError(err) + + r.ErrorIs(orchestrator.Execute(ctx), tt.expectedErr) + + if tt.expectedErr == nil { + r.GreaterOrEqual(orchestrator.GetMaxObservedTPS(), tt.config.MaxTPS) + } else { + r.Less(orchestrator.GetMaxObservedTPS(), tt.config.MaxTPS) + } + }) + } +} + +// test that the orchestrator returns early if the txGenerators or the issuers error +func TestGradualOrchestratorExecution(t *testing.T) { + var ( + errMockTxGenerator = errors.New("mock tx generator error") + errMockIssuer = errors.New("mock issuer error") + ) + + tracker, err := NewPrometheusTracker[ids.ID](prometheus.NewRegistry(), logging.NoLog{}) + require.NoError(t, err, "creating tracker") + + tests := []struct { + name string + agents []Agent[ids.ID] + expectedErr error + }{ + { + name: "generator error", + agents: []Agent[ids.ID]{ + NewAgent[ids.ID]( + &mockIssuer{ + generateTxF: func() (ids.ID, error) { + return ids.Empty, errMockTxGenerator + }, + }, + &mockListener{}, + ), + }, + expectedErr: errMockTxGenerator, + }, + { + name: "issuer error", + agents: []Agent[ids.ID]{ + NewAgent[ids.ID]( + &mockIssuer{ + generateTxF: func() (ids.ID, error) { + return ids.GenerateTestID(), nil + }, + issueTxErr: errMockIssuer, + }, + &mockListener{}, + ), + }, + expectedErr: errMockIssuer, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := require.New(t) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + orchestrator, err := NewGradualOrchestrator( + tt.agents, + tracker, + logging.NoLog{}, + DefaultGradualOrchestratorConfig(), + ) + r.NoError(err) + + r.ErrorIs(orchestrator.Execute(ctx), tt.expectedErr) + }) + } +} + +type mockIssuer struct { + generateTxF func() (ids.ID, error) + currTxsIssued uint64 + maxTxs uint64 + tracker Tracker[ids.ID] + issueTxErr error +} + +// GenerateAndIssueTx immediately generates, issues and confirms a tx. +// To simulate TPS, the number of txs IssueTx can issue/confirm is capped by maxTxs +func (m *mockIssuer) GenerateAndIssueTx(_ context.Context) (ids.ID, error) { + id, err := m.generateTxF() + if err != nil { + return id, err + } + + if m.issueTxErr != nil { + return ids.ID{}, m.issueTxErr + } + + if m.currTxsIssued >= m.maxTxs { + return ids.ID{}, nil + } + + m.tracker.Issue(id) + m.tracker.ObserveConfirmed(id) + m.currTxsIssued++ + return id, nil +} + +type mockListener struct{} + +func (*mockListener) Listen(context.Context) error { + return nil +} + +func (*mockListener) RegisterIssued(ids.ID) {} + +func (*mockListener) IssuingDone() {} diff --git a/tests/load/noop_tracker.go b/tests/load/noop_tracker.go new file mode 100644 index 000000000000..4baaaedd3ee9 --- /dev/null +++ b/tests/load/noop_tracker.go @@ -0,0 +1,19 @@ +// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package load + +// NoopTracker is a no-op implementation of the tracker. +type NoopTracker[T comparable] struct{} + +func NewNoopTracker[T comparable]() *NoopTracker[T] { + return &NoopTracker[T]{} +} + +func (NoopTracker[T]) Issue(T) {} +func (NoopTracker[T]) ObserveConfirmed(T) {} +func (NoopTracker[T]) ObserveFailed(T) {} +func (NoopTracker[T]) ObserveBlock(uint64) {} +func (NoopTracker[T]) GetObservedConfirmed() uint64 { return 0 } +func (NoopTracker[T]) GetObservedFailed() uint64 { return 0 } +func (NoopTracker[T]) Log() {} diff --git a/tests/load/orchestrate/burst.go b/tests/load/orchestrate/burst.go deleted file mode 100644 index 82a454e888a4..000000000000 --- a/tests/load/orchestrate/burst.go +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package orchestrate - -import ( - "context" - "fmt" - "time" - - "golang.org/x/sync/errgroup" - - "github.com/ava-labs/avalanchego/tests/load/agent" -) - -// BurstOrchestrator executes a series of worker/tx sequence pairs. -// Each worker/txSequence pair issues [batchSize] transactions, confirms all -// of them as accepted, and then moves to the next batch until the txSequence -// is exhausted. -type BurstOrchestrator[T, U comparable] struct { - agents []*agent.Agent[T, U] - timeout time.Duration -} - -func NewBurstOrchestrator[T, U comparable]( - agents []*agent.Agent[T, U], - timeout time.Duration, -) *BurstOrchestrator[T, U] { - return &BurstOrchestrator[T, U]{ - agents: agents, - timeout: timeout, - } -} - -func (o *BurstOrchestrator[T, U]) Execute(ctx context.Context) error { - observerCtx, observerCancel := context.WithCancel(ctx) - defer observerCancel() - - // start a goroutine to confirm each issuer's transactions - observerGroup := errgroup.Group{} - for _, agent := range o.agents { - observerGroup.Go(func() error { return agent.Listener.Listen(observerCtx) }) - } - - const logInterval = 10 * time.Second - logTicker := time.NewTicker(logInterval) - - // start issuing transactions sequentially from each issuer - issuerGroup := errgroup.Group{} - for _, agent := range o.agents { - issuerGroup.Go(func() error { - for range agent.TxTarget { - tx, err := agent.Issuer.GenerateAndIssueTx(ctx) - if err != nil { - return fmt.Errorf("generating and issuing transaction: %w", err) - } - - agent.Listener.RegisterIssued(tx) - - select { - case <-logTicker.C: - agent.Tracker.Log() - default: - } - } - return nil - }) - } - - // wait for all issuers to finish sending their transactions - if err := issuerGroup.Wait(); err != nil { - return fmt.Errorf("issuers: %w", err) - } - - ctx, cancel := context.WithTimeout(ctx, o.timeout) - defer cancel() - - // start a goroutine that will cancel the observer group's context - // if either the parent context is cancelled or our timeout elapses - go func() { - <-ctx.Done() - observerCancel() - }() - - // blocks until either all of the observers have finished or our context - // is cancelled signalling for early termination (with an error) - if err := observerGroup.Wait(); err != nil { - return fmt.Errorf("observers: %w", err) - } - return nil -} diff --git a/tests/load/tracker/metrics_server.go b/tests/load/prometheus_server.go similarity index 94% rename from tests/load/tracker/metrics_server.go rename to tests/load/prometheus_server.go index fef77cc46649..8c9aea04dd86 100644 --- a/tests/load/tracker/metrics_server.go +++ b/tests/load/prometheus_server.go @@ -1,7 +1,7 @@ // Copyright (C) 2025, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package tracker +package load import ( "context" @@ -22,7 +22,7 @@ type MetricsServer struct { server http.Server } -func NewMetricsServer(addr string, registry *prometheus.Registry) *MetricsServer { +func NewPrometheusServer(addr string, registry *prometheus.Registry) *MetricsServer { return &MetricsServer{ addr: addr, registry: registry, diff --git a/tests/load/prometheus_tracker.go b/tests/load/prometheus_tracker.go new file mode 100644 index 000000000000..53651601a2dc --- /dev/null +++ b/tests/load/prometheus_tracker.go @@ -0,0 +1,151 @@ +// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package load + +import ( + "context" + "sync" + "time" + + "github.com/prometheus/client_golang/prometheus" + "go.uber.org/zap" + + "github.com/ava-labs/avalanchego/utils/logging" + "github.com/ava-labs/avalanchego/utils/wrappers" +) + +const namespace = "load" + +var _ Tracker[any] = (*PrometheusTracker[any])(nil) + +type PrometheusTracker[T comparable] struct { + lock sync.RWMutex + + outstandingTxs map[T]time.Time + + txsIssued uint64 + txsConfirmed uint64 + txsFailed uint64 + + // metrics + txsIssuedCounter prometheus.Counter + txsConfirmedCounter prometheus.Counter + txsFailedCounter prometheus.Counter + txLatency prometheus.Histogram + + logger logging.Logger +} + +func NewPrometheusTracker[T comparable](reg *prometheus.Registry, logger logging.Logger) (*PrometheusTracker[T], error) { + prometheusTracker := &PrometheusTracker[T]{ + outstandingTxs: make(map[T]time.Time), + txsIssuedCounter: prometheus.NewCounter(prometheus.CounterOpts{ + Namespace: namespace, + Name: "txs_issued", + Help: "Number of transactions issued", + }), + txsConfirmedCounter: prometheus.NewCounter(prometheus.CounterOpts{ + Namespace: namespace, + Name: "txs_confirmed", + Help: "Number of transactions confirmed", + }), + txsFailedCounter: prometheus.NewCounter(prometheus.CounterOpts{ + Namespace: namespace, + Name: "txs_failed", + Help: "Number of transactions failed", + }), + txLatency: prometheus.NewHistogram(prometheus.HistogramOpts{ + Namespace: namespace, + Name: "tx_latency", + Help: "Latency of transactions", + }), + logger: logger, + } + + errs := wrappers.Errs{} + errs.Add( + reg.Register(prometheusTracker.txsIssuedCounter), + reg.Register(prometheusTracker.txsConfirmedCounter), + reg.Register(prometheusTracker.txsFailedCounter), + reg.Register(prometheusTracker.txLatency), + ) + return prometheusTracker, errs.Err +} + +func (p *PrometheusTracker[T]) GetObservedConfirmed() uint64 { + p.lock.RLock() + defer p.lock.RUnlock() + + return p.txsConfirmed +} + +func (p *PrometheusTracker[T]) GetObservedFailed() uint64 { + p.lock.RLock() + defer p.lock.RUnlock() + + return p.txsFailed +} + +func (p *PrometheusTracker[T]) GetObservedIssued() uint64 { + p.lock.RLock() + defer p.lock.RUnlock() + + return p.txsIssued +} + +func (p *PrometheusTracker[T]) Issue(tx T) { + p.lock.Lock() + defer p.lock.Unlock() + + p.outstandingTxs[tx] = time.Now() + p.txsIssued++ + p.txsIssuedCounter.Inc() +} + +func (p *PrometheusTracker[T]) ObserveConfirmed(tx T) { + p.lock.Lock() + defer p.lock.Unlock() + + startTime := p.outstandingTxs[tx] + delete(p.outstandingTxs, tx) + + p.txsConfirmed++ + p.txsConfirmedCounter.Inc() + p.txLatency.Observe(float64(time.Since(startTime).Milliseconds())) +} + +func (p *PrometheusTracker[T]) ObserveFailed(tx T) { + p.lock.Lock() + defer p.lock.Unlock() + + startTime := p.outstandingTxs[tx] + delete(p.outstandingTxs, tx) + + p.txsFailed++ + p.txsFailedCounter.Inc() + p.txLatency.Observe(float64(time.Since(startTime).Milliseconds())) +} + +func (t *PrometheusTracker[T]) LogPeriodically(ctx context.Context) { + const period = 10 * time.Second + + ticker := time.NewTicker(period) + defer ticker.Stop() + + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + } + t.lock.RLock() + t.logger.Info("Tracker stats", + zap.Uint64("issued", t.txsIssued), + zap.Uint64("confirmed", t.txsConfirmed), + zap.Uint64("failed", t.txsFailed), + zap.Int("inflight", len(t.outstandingTxs)), + ) + t.lock.RUnlock() + } +} diff --git a/tests/load/tracker/metrics.go b/tests/load/tracker/metrics.go deleted file mode 100644 index 3af443ee824d..000000000000 --- a/tests/load/tracker/metrics.go +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package tracker - -import ( - "fmt" - - "github.com/prometheus/client_golang/prometheus" -) - -type metrics struct { - // issued is the number of issued transactions. - issued prometheus.Counter - // confirmed is the number of confirmed transactions. - confirmed prometheus.Counter - // failed is the number of failed transactions. - failed prometheus.Counter - // txLatency is a histogram of individual tx times. - // Failed transactions do not show in this metric. - txLatency prometheus.Histogram - - registry PrometheusRegistry -} - -type PrometheusRegistry interface { - prometheus.Registerer - prometheus.Gatherer -} - -func newMetrics(registry PrometheusRegistry) *metrics { - issued := prometheus.NewCounter(prometheus.CounterOpts{ - Name: "txs_issued", - Help: "Number of issued transactions", - }) - confirmed := prometheus.NewCounter(prometheus.CounterOpts{ - Name: "txs_confirmed", - Help: "Number of confirmed transactions", - }) - failed := prometheus.NewCounter(prometheus.CounterOpts{ - Name: "txs_failed", - Help: "Number of failed transactions", - }) - txLatency := prometheus.NewHistogram(prometheus.HistogramOpts{ - Name: "tx_latency", - Help: "Latency of transactions", - }) - - registry.MustRegister(issued, confirmed, failed, - txLatency) - - return &metrics{ - registry: registry, - issued: issued, - confirmed: confirmed, - failed: failed, - txLatency: txLatency, - } -} - -func (m *metrics) String() string { - s := "Metrics:\n" - - metricFamilies, err := m.registry.Gather() - if err != nil { - return s + "failed to gather metrics: " + err.Error() + "\n" - } - - for _, mf := range metricFamilies { - metrics := mf.GetMetric() - for _, metric := range metrics { - s += fmt.Sprintf("Name: %s, Type: %s, Description: %s, Values: %s\n", - mf.GetName(), mf.GetType().String(), mf.GetHelp(), metric.String()) - } - } - - return s -} diff --git a/tests/load/tracker/noop.go b/tests/load/tracker/noop.go deleted file mode 100644 index 7e6a8d1b89a7..000000000000 --- a/tests/load/tracker/noop.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package tracker - -import ( - "github.com/ava-labs/libevm/common" -) - -// Noop is a no-op implementation of the tracker. -type Noop struct{} - -func NewNoop() *Noop { - return &Noop{} -} - -func (Noop) Issue(common.Hash) {} -func (Noop) ObserveConfirmed(common.Hash) {} -func (Noop) ObserveFailed(common.Hash) {} -func (Noop) ObserveBlock(uint64) {} -func (Noop) GetObservedConfirmed() uint64 { return 0 } -func (Noop) GetObservedFailed() uint64 { return 0 } -func (Noop) Log() {} diff --git a/tests/load/tracker/tracker.go b/tests/load/tracker/tracker.go deleted file mode 100644 index e08ffd1f3dc7..000000000000 --- a/tests/load/tracker/tracker.go +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package tracker - -import ( - "sync" - "time" - - "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/log" -) - -type Tracker struct { - timeNow func() time.Time - txHashToLastTime map[common.Hash]time.Time - metrics *metrics - - stats struct { - issued uint64 - confirmed uint64 - failed uint64 - } - mutex sync.Mutex -} - -// New creates a new Tracker instance. -// The tracker should then be started usign [Tracker.Start] and stopped -// using [Tracker.Stop]. -func New(registry PrometheusRegistry) *Tracker { - return &Tracker{ - timeNow: time.Now, - txHashToLastTime: make(map[common.Hash]time.Time), - metrics: newMetrics(registry), - } -} - -// Issue records a transaction that was issued, but whose final status is -// not yet known. -func (t *Tracker) Issue(txHash common.Hash) { - t.mutex.Lock() - defer t.mutex.Unlock() - - t.metrics.issued.Inc() - t.stats.issued++ - t.txHashToLastTime[txHash] = t.timeNow() -} - -// ObserveConfirmed records a transaction that was confirmed. -func (t *Tracker) ObserveConfirmed(txHash common.Hash) { - t.mutex.Lock() - defer t.mutex.Unlock() - - t.metrics.confirmed.Inc() - issuedTime := t.txHashToLastTime[txHash] - diff := t.timeNow().Sub(issuedTime) - t.metrics.txLatency.Observe(float64(diff.Milliseconds())) - delete(t.txHashToLastTime, txHash) - t.stats.confirmed++ -} - -// ObserveFailed records a transaction that failed (e.g. expired) -func (t *Tracker) ObserveFailed(txHash common.Hash) { - t.mutex.Lock() - defer t.mutex.Unlock() - - t.metrics.failed.Inc() - delete(t.txHashToLastTime, txHash) - t.stats.failed++ -} - -// GetObservedConfirmed returns the number of transactions that the tracker has -// confirmed were accepted. -func (t *Tracker) GetObservedConfirmed() uint64 { - t.mutex.Lock() - defer t.mutex.Unlock() - return t.stats.confirmed -} - -// GetObservedFailed returns the number of transactions that the tracker has -// confirmed failed. -func (t *Tracker) GetObservedFailed() uint64 { - t.mutex.Lock() - defer t.mutex.Unlock() - return t.stats.failed -} - -func (t *Tracker) Log() { - t.mutex.Lock() - defer t.mutex.Unlock() - - log.Info("Tracker stats", - "issued", t.stats.issued, - "confirmed", t.stats.confirmed, - "failed", t.stats.failed, - "inflight", len(t.txHashToLastTime), - ) -} From 0faf824553caa4ea7cadf7c82c3c35bad4280e9c Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 7 May 2025 17:32:58 +0200 Subject: [PATCH 060/197] Move issuers to c/issuers --- tests/load/c/execute.go | 3 +- tests/load/c/funder.go | 3 +- .../distributor.go} | 21 +++++---- .../c/{issuer_opcode.go => issuers/opcode.go} | 33 ++++++------- .../c/{issuer_simple.go => issuers/simple.go} | 46 +++++++++---------- tests/load/c/listener.go | 6 +-- 6 files changed, 58 insertions(+), 54 deletions(-) rename tests/load/c/{issuer_distribute.go => issuers/distributor.go} (83%) rename tests/load/c/{issuer_opcode.go => issuers/opcode.go} (85%) rename tests/load/c/{issuer_simple.go => issuers/simple.go} (69%) diff --git a/tests/load/c/execute.go b/tests/load/c/execute.go index 8079ed9e1bda..abbea56180a8 100644 --- a/tests/load/c/execute.go +++ b/tests/load/c/execute.go @@ -16,6 +16,7 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/ava-labs/avalanchego/tests/load" + "github.com/ava-labs/avalanchego/tests/load/c/issuers" "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" "github.com/ava-labs/avalanchego/utils/logging" @@ -63,7 +64,7 @@ func execute(ctx context.Context, preFundedKeys []*secp256k1.PrivateKey, config if err != nil { return fmt.Errorf("dialing %s: %w", endpoint, err) } - issuer, err := NewSimpleIssuer(ctx, client, tracker, + issuer, err := issuers.NewSimple(ctx, client, tracker, big.NewInt(config.maxFeeCap), keys[i], config.issuePeriod) if err != nil { return fmt.Errorf("creating issuer: %w", err) diff --git a/tests/load/c/funder.go b/tests/load/c/funder.go index 462cb278cd84..098bcb4d4933 100644 --- a/tests/load/c/funder.go +++ b/tests/load/c/funder.go @@ -18,6 +18,7 @@ import ( "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/avalanchego/tests/load" + "github.com/ava-labs/avalanchego/tests/load/c/issuers" "github.com/ava-labs/avalanchego/utils/logging" ethcrypto "github.com/ava-labs/libevm/crypto" @@ -67,7 +68,7 @@ func ensureMinimumFunds(ctx context.Context, endpoint string, keys []*ecdsa.Priv } maxFundsAddress := ethcrypto.PubkeyToAddress(maxFundsKey.PublicKey) - issuer, err := NewDistributingIssuer(ctx, client, maxFundsKey, needFundsKeys) + issuer, err := issuers.NewDistributor(ctx, client, maxFundsKey, needFundsKeys) if err != nil { return fmt.Errorf("creating issuer: %w", err) } diff --git a/tests/load/c/issuer_distribute.go b/tests/load/c/issuers/distributor.go similarity index 83% rename from tests/load/c/issuer_distribute.go rename to tests/load/c/issuers/distributor.go index afc201bddde4..a63ffd8670ab 100644 --- a/tests/load/c/issuer_distribute.go +++ b/tests/load/c/issuers/distributor.go @@ -1,7 +1,7 @@ // Copyright (C) 2025, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package c +package issuers import ( "context" @@ -17,16 +17,17 @@ import ( ethcrypto "github.com/ava-labs/libevm/crypto" ) -type DistributorClient interface { - EthClientSimpleIssuer +type EthClientDistributor interface { + EthClientSimple EstimateBaseFee(ctx context.Context) (*big.Int, error) SuggestGasTipCap(ctx context.Context) (*big.Int, error) } -// Issuer issues transactions to a node. -type DistributingIssuer struct { +// Distributor issues transactions to a node to distribute funds from a single address +// to multiple addresses. +type Distributor struct { // Injected parameters - client DistributorClient + client EthClientDistributor from *ecdsa.PrivateKey to map[*ecdsa.PrivateKey]*big.Int @@ -41,9 +42,9 @@ type DistributingIssuer struct { nonce uint64 } -func NewDistributingIssuer(ctx context.Context, client DistributorClient, +func NewDistributor(ctx context.Context, client EthClientDistributor, from *ecdsa.PrivateKey, to map[*ecdsa.PrivateKey]*big.Int, -) (*DistributingIssuer, error) { +) (*Distributor, error) { address := ethcrypto.PubkeyToAddress(from.PublicKey) blockNumber := (*big.Int)(nil) nonce, err := client.NonceAt(ctx, address, blockNumber) @@ -66,7 +67,7 @@ func NewDistributingIssuer(ctx context.Context, client DistributorClient, return nil, fmt.Errorf("getting chain id: %w", err) } - return &DistributingIssuer{ + return &Distributor{ client: client, from: from, address: address, @@ -79,7 +80,7 @@ func NewDistributingIssuer(ctx context.Context, client DistributorClient, }, nil } -func (d *DistributingIssuer) GenerateAndIssueTx(ctx context.Context) (*types.Transaction, error) { +func (d *Distributor) GenerateAndIssueTx(ctx context.Context) (*types.Transaction, error) { var toKey *ecdsa.PrivateKey var funds *big.Int for toKey, funds = range d.to { diff --git a/tests/load/c/issuer_opcode.go b/tests/load/c/issuers/opcode.go similarity index 85% rename from tests/load/c/issuer_opcode.go rename to tests/load/c/issuers/opcode.go index 12141e815616..8eed34277ffd 100644 --- a/tests/load/c/issuer_opcode.go +++ b/tests/load/c/issuers/opcode.go @@ -1,7 +1,7 @@ // Copyright (C) 2025, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package c +package issuers import ( "context" @@ -16,22 +16,23 @@ import ( "github.com/ava-labs/libevm/core/types" "github.com/ava-labs/libevm/rpc" - evmloadsimulator "github.com/ava-labs/avalanchego/tests/load/c/contracts" + "github.com/ava-labs/avalanchego/tests/load/c/contracts" + ethcrypto "github.com/ava-labs/libevm/crypto" ) -type EthClientOpcodeIssuer interface { - EthClientSimpleIssuer +type EthClientOpcoder interface { + EthClientSimple bind.DeployBackend bind.ContractBackend } -// OpcodeIssuer generates and issues transactions that randomly call the -// external functions of the [evmloadsimulator.EVMLoadSimulator] contract +// Opcoder generates and issues transactions that randomly call the +// external functions of the [contracts.EVMLoadSimulator] contract // instance that it deploys. -type OpcodeIssuer struct { +type Opcoder struct { // Injected parameters - client EthClientOpcodeIssuer + client EthClientOpcoder tracker IssueTracker senderKey *ecdsa.PrivateKey maxFeeCap *big.Int @@ -41,21 +42,21 @@ type OpcodeIssuer struct { chainID *big.Int maxTipCap *big.Int contractAddress common.Address - contractInstance *evmloadsimulator.EVMLoadSimulator + contractInstance *contracts.EVMLoadSimulator // State nonce uint64 lastIssue time.Time } -func NewOpCodeSimulator( +func NewOpcoder( ctx context.Context, - client EthClientOpcodeIssuer, + client EthClientOpcoder, tracker IssueTracker, maxFeeCap *big.Int, key *ecdsa.PrivateKey, issuePeriod time.Duration, -) (*OpcodeIssuer, error) { +) (*Opcoder, error) { chainID, err := client.ChainID(ctx) if err != nil { return nil, fmt.Errorf("getting chain id: %w", err) @@ -71,7 +72,7 @@ func NewOpCodeSimulator( if err != nil { return nil, fmt.Errorf("creating transaction opts: %w", err) } - _, simulatorDeploymentTx, simulatorInstance, err := evmloadsimulator.DeployEVMLoadSimulator(txOpts, client) + _, simulatorDeploymentTx, simulatorInstance, err := contracts.DeployEVMLoadSimulator(txOpts, client) if err != nil { return nil, fmt.Errorf("deploying simulator contract: %w", err) } @@ -82,7 +83,7 @@ func NewOpCodeSimulator( return nil, fmt.Errorf("waiting for simulator contract to be mined: %w", err) } - return &OpcodeIssuer{ + return &Opcoder{ client: client, tracker: tracker, senderKey: key, @@ -96,7 +97,7 @@ func NewOpCodeSimulator( }, nil } -func (o *OpcodeIssuer) GenerateAndIssueTx(ctx context.Context) (tx *types.Transaction, err error) { +func (o *Opcoder) GenerateAndIssueTx(ctx context.Context) (tx *types.Transaction, err error) { if o.issuePeriod > 0 && !o.lastIssue.IsZero() && time.Since(o.lastIssue) < o.issuePeriod { timer := time.NewTimer(o.issuePeriod - time.Since(o.lastIssue)) @@ -175,7 +176,7 @@ func allLoadTypes() []string { } } -func (o *OpcodeIssuer) newTxOpts(ctx context.Context) (*bind.TransactOpts, error) { +func (o *Opcoder) newTxOpts(ctx context.Context) (*bind.TransactOpts, error) { return newTxOpts(ctx, o.senderKey, o.chainID, o.maxFeeCap, o.maxTipCap, o.nonce) } diff --git a/tests/load/c/issuer_simple.go b/tests/load/c/issuers/simple.go similarity index 69% rename from tests/load/c/issuer_simple.go rename to tests/load/c/issuers/simple.go index 611a2a92210b..c62dea0db6cd 100644 --- a/tests/load/c/issuer_simple.go +++ b/tests/load/c/issuers/simple.go @@ -1,7 +1,7 @@ // Copyright (C) 2025, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package c +package issuers import ( "context" @@ -17,7 +17,7 @@ import ( ethcrypto "github.com/ava-labs/libevm/crypto" ) -type EthClientSimpleIssuer interface { +type EthClientSimple interface { ChainID(ctx context.Context) (*big.Int, error) NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) SendTransaction(ctx context.Context, tx *types.Transaction) error @@ -27,10 +27,10 @@ type IssueTracker interface { Issue(tx *types.Transaction) } -// SimplerIssuer generates and issues transactions sending 0 fund to the sender. -type SimplerIssuer struct { +// Simple generates and issues transactions sending 0 fund to the sender. +type Simple struct { // Injected parameters - client EthClientSimpleIssuer + client EthClientSimple tracker IssueTracker key *ecdsa.PrivateKey gasFeeCap *big.Int @@ -47,9 +47,9 @@ type SimplerIssuer struct { lastIssue time.Time } -func NewSimpleIssuer(ctx context.Context, client EthClientSimpleIssuer, tracker IssueTracker, +func NewSimple(ctx context.Context, client EthClientSimple, tracker IssueTracker, maxFeeCap *big.Int, key *ecdsa.PrivateKey, issuePeriod time.Duration, -) (*SimplerIssuer, error) { +) (*Simple, error) { address := ethcrypto.PubkeyToAddress(key.PublicKey) blockNumber := (*big.Int)(nil) nonce, err := client.NonceAt(ctx, address, blockNumber) @@ -66,7 +66,7 @@ func NewSimpleIssuer(ctx context.Context, client EthClientSimpleIssuer, tracker return nil, fmt.Errorf("getting chain id: %w", err) } - return &SimplerIssuer{ + return &Simple{ client: client, tracker: tracker, key: key, @@ -80,14 +80,14 @@ func NewSimpleIssuer(ctx context.Context, client EthClientSimpleIssuer, tracker }, nil } -func (i *SimplerIssuer) GenerateAndIssueTx(ctx context.Context) (*types.Transaction, error) { - tx, err := types.SignNewTx(i.key, i.signer, &types.DynamicFeeTx{ - ChainID: i.chainID, - Nonce: i.nonce, - GasTipCap: i.gasTipCap, - GasFeeCap: i.gasFeeCap, +func (s *Simple) GenerateAndIssueTx(ctx context.Context) (*types.Transaction, error) { + tx, err := types.SignNewTx(s.key, s.signer, &types.DynamicFeeTx{ + ChainID: s.chainID, + Nonce: s.nonce, + GasTipCap: s.gasTipCap, + GasFeeCap: s.gasFeeCap, Gas: params.TxGas, - To: &i.address, // self + To: &s.address, // self Data: nil, Value: common.Big0, }) @@ -95,9 +95,9 @@ func (i *SimplerIssuer) GenerateAndIssueTx(ctx context.Context) (*types.Transact return nil, err } - if i.issuePeriod > 0 && !i.lastIssue.IsZero() && - time.Since(i.lastIssue) < i.issuePeriod { - timer := time.NewTimer(i.issuePeriod - time.Since(i.lastIssue)) + if s.issuePeriod > 0 && !s.lastIssue.IsZero() && + time.Since(s.lastIssue) < s.issuePeriod { + timer := time.NewTimer(s.issuePeriod - time.Since(s.lastIssue)) select { case <-timer.C: case <-ctx.Done(): @@ -106,11 +106,11 @@ func (i *SimplerIssuer) GenerateAndIssueTx(ctx context.Context) (*types.Transact } } - if err := i.client.SendTransaction(ctx, tx); err != nil { - return nil, fmt.Errorf("issuing transaction with nonce %d: %w", i.nonce, err) + if err := s.client.SendTransaction(ctx, tx); err != nil { + return nil, fmt.Errorf("issuing transaction with nonce %d: %w", s.nonce, err) } - i.nonce++ - i.tracker.Issue(tx) - i.lastIssue = time.Now() + s.nonce++ + s.tracker.Issue(tx) + s.lastIssue = time.Now() return tx, nil } diff --git a/tests/load/c/listener.go b/tests/load/c/listener.go index 00eb0e4cf3f2..18ef4a6edde1 100644 --- a/tests/load/c/listener.go +++ b/tests/load/c/listener.go @@ -13,7 +13,7 @@ import ( "github.com/ava-labs/libevm/core/types" ) -type EthClientListener interface { +type EthClient interface { NonceAt(ctx context.Context, addr common.Address, blockNumber *big.Int) (uint64, error) NewHeadSubscriber } @@ -26,7 +26,7 @@ type Observer interface { // Listener listens for transaction confirmations from a node. type Listener struct { // Injected parameters - client EthClientListener + client EthClient tracker Observer address common.Address @@ -37,7 +37,7 @@ type Listener struct { inFlightTxs []*types.Transaction } -func NewListener(client EthClientListener, tracker Observer, address common.Address) *Listener { +func NewListener(client EthClient, tracker Observer, address common.Address) *Listener { return &Listener{ client: client, tracker: tracker, From 1f366f3b79812d61eb89f3bfa52b0060ceb501f2 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 7 May 2025 17:35:54 +0200 Subject: [PATCH 061/197] Move listener code to c/listener --- tests/load/c/execute.go | 3 ++- tests/load/c/funder.go | 3 ++- tests/load/c/{ => listener}/listener.go | 4 ++-- tests/load/c/{ => listener}/newhead.go | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) rename tests/load/c/{ => listener}/listener.go (96%) rename tests/load/c/{ => listener}/newhead.go (99%) diff --git a/tests/load/c/execute.go b/tests/load/c/execute.go index abbea56180a8..d887af4e2065 100644 --- a/tests/load/c/execute.go +++ b/tests/load/c/execute.go @@ -17,6 +17,7 @@ import ( "github.com/ava-labs/avalanchego/tests/load" "github.com/ava-labs/avalanchego/tests/load/c/issuers" + "github.com/ava-labs/avalanchego/tests/load/c/listener" "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" "github.com/ava-labs/avalanchego/utils/logging" @@ -70,7 +71,7 @@ func execute(ctx context.Context, preFundedKeys []*secp256k1.PrivateKey, config return fmt.Errorf("creating issuer: %w", err) } address := ethcrypto.PubkeyToAddress(keys[i].PublicKey) - listener := NewListener(client, tracker, address) + listener := listener.New(client, tracker, address) agents[i] = load.NewAgent(issuer, listener) } diff --git a/tests/load/c/funder.go b/tests/load/c/funder.go index 098bcb4d4933..34375c1117ef 100644 --- a/tests/load/c/funder.go +++ b/tests/load/c/funder.go @@ -19,6 +19,7 @@ import ( "github.com/ava-labs/avalanchego/tests/load" "github.com/ava-labs/avalanchego/tests/load/c/issuers" + "github.com/ava-labs/avalanchego/tests/load/c/listener" "github.com/ava-labs/avalanchego/utils/logging" ethcrypto "github.com/ava-labs/libevm/crypto" @@ -73,7 +74,7 @@ func ensureMinimumFunds(ctx context.Context, endpoint string, keys []*ecdsa.Priv return fmt.Errorf("creating issuer: %w", err) } tracker := load.NewNoopTracker[*types.Transaction]() - listener := NewListener(client, tracker, maxFundsAddress) + listener := listener.New(client, tracker, maxFundsAddress) agents := []load.Agent[*types.Transaction]{ load.NewAgent(issuer, listener), } diff --git a/tests/load/c/listener.go b/tests/load/c/listener/listener.go similarity index 96% rename from tests/load/c/listener.go rename to tests/load/c/listener/listener.go index 18ef4a6edde1..bb10cfb901c1 100644 --- a/tests/load/c/listener.go +++ b/tests/load/c/listener/listener.go @@ -1,7 +1,7 @@ // Copyright (C) 2025, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package c +package listener import ( "context" @@ -37,7 +37,7 @@ type Listener struct { inFlightTxs []*types.Transaction } -func NewListener(client EthClient, tracker Observer, address common.Address) *Listener { +func New(client EthClient, tracker Observer, address common.Address) *Listener { return &Listener{ client: client, tracker: tracker, diff --git a/tests/load/c/newhead.go b/tests/load/c/listener/newhead.go similarity index 99% rename from tests/load/c/newhead.go rename to tests/load/c/listener/newhead.go index 2ebc0e8f386c..532c66b49809 100644 --- a/tests/load/c/newhead.go +++ b/tests/load/c/listener/newhead.go @@ -1,7 +1,7 @@ // Copyright (C) 2025, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package c +package listener import ( "context" From 8ccb80061f4aa04be916e3b146d15672734aa00f Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 7 May 2025 17:40:07 +0200 Subject: [PATCH 062/197] Update grafana dashboard --- tests/load/c/dashboard.json | 358 ++++++++++++++++++++++++++++++------ 1 file changed, 297 insertions(+), 61 deletions(-) diff --git a/tests/load/c/dashboard.json b/tests/load/c/dashboard.json index a574f3e7f114..7e04e981219f 100644 --- a/tests/load/c/dashboard.json +++ b/tests/load/c/dashboard.json @@ -21,6 +21,153 @@ "id": 1, "links": [], "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 19, + "panels": [], + "title": "General information", + "type": "row" + }, + { + "datasource": { + "uid": "dekh3fflmqzggf" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "super-light-blue", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 0, + "y": 1 + }, + "id": 18, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.6.1", + "targets": [ + { + "editorMode": "code", + "expr": "count(\n count by (node_id) (\n rate(avalanche_evm_eth_chain_block_inserts_count[30s]) > 0\n )\n)\n", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Number of nodes", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "dekh3fflmqzggf" + }, + "description": "Base fee averaged across all nodes", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "super-light-yellow", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + }, + "unit": "wei" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 3, + "x": 4, + "y": 1 + }, + "id": 13, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.6.1", + "targets": [ + { + "editorMode": "code", + "expr": "avg(avalanche_evm_eth_chain_latest_basefee)", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Average base fee ", + "type": "stat" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 5 + }, + "id": 20, + "panels": [], + "title": "Load test metrics", + "type": "row" + }, { "datasource": { "type": "prometheus", @@ -54,7 +201,7 @@ "h": 4, "w": 4, "x": 0, - "y": 0 + "y": 6 }, "id": 9, "options": { @@ -120,7 +267,7 @@ "h": 4, "w": 4, "x": 4, - "y": 0 + "y": 6 }, "id": 7, "options": { @@ -186,7 +333,7 @@ "h": 4, "w": 4, "x": 8, - "y": 0 + "y": 6 }, "id": 8, "options": { @@ -252,7 +399,7 @@ "h": 4, "w": 4, "x": 12, - "y": 0 + "y": 6 }, "id": 10, "options": { @@ -285,15 +432,29 @@ "title": "In flight txs", "type": "stat" }, + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 10 + }, + "id": 22, + "panels": [], + "title": "Per second", + "type": "row" + }, { "datasource": { "type": "prometheus", "uid": "dekh3fflmqzggf" }, + "description": "Number of transactions processed per second averaged across all nodes", "fieldConfig": { "defaults": { "color": { - "fixedColor": "super-light-purple", + "fixedColor": "light-blue", "mode": "fixed" }, "mappings": [], @@ -305,17 +466,17 @@ } ] }, - "unit": "ms" + "unit": "none" }, "overrides": [] }, "gridPos": { "h": 4, - "w": 5, + "w": 4, "x": 0, - "y": 4 + "y": 11 }, - "id": 5, + "id": 14, "options": { "colorMode": "value", "graphMode": "area", @@ -336,18 +497,14 @@ "pluginVersion": "11.6.1", "targets": [ { - "datasource": { - "type": "prometheus", - "uid": "dekh3fflmqzggf" - }, "editorMode": "code", - "expr": "sum(rate(avalanche_evm_eth_chain_block_inserts[1m]))/sum(rate(avalanche_evm_eth_chain_block_inserts_count[1m]))", + "expr": "avg(rate(avalanche_evm_eth_chain_txs_processed[1m]))", "legendFormat": "__auto", "range": true, "refId": "A" } ], - "title": "Insert time per block", + "title": "Average txs per second", "type": "stat" }, { @@ -355,11 +512,11 @@ "type": "prometheus", "uid": "dekh3fflmqzggf" }, - "description": "Gas used in processed block", + "description": "Number of gas processed per second averaged across all nodes", "fieldConfig": { "defaults": { "color": { - "fixedColor": "super-light-purple", + "fixedColor": "light-blue", "mode": "fixed" }, "mappings": [], @@ -378,10 +535,10 @@ "gridPos": { "h": 4, "w": 4, - "x": 5, - "y": 4 + "x": 4, + "y": 11 }, - "id": 11, + "id": 16, "options": { "colorMode": "value", "graphMode": "area", @@ -403,13 +560,13 @@ "targets": [ { "editorMode": "code", - "expr": "sum(avalanche_evm_eth_chain_block_gas_used_processed) / sum(avalanche_evm_eth_chain_block_inserts_count)", + "expr": "avg(rate(avalanche_evm_eth_chain_block_gas_used_processed[1m]))", "legendFormat": "__auto", "range": true, "refId": "A" } ], - "title": "Gas per block", + "title": "Average gas per second", "type": "stat" }, { @@ -417,11 +574,11 @@ "type": "prometheus", "uid": "dekh3fflmqzggf" }, - "description": "Number of transactions processed per block", + "description": "Number of blocks produced per son, averaged across all nodes", "fieldConfig": { "defaults": { "color": { - "fixedColor": "super-light-purple", + "fixedColor": "light-blue", "mode": "fixed" }, "mappings": [], @@ -440,10 +597,10 @@ "gridPos": { "h": 4, "w": 4, - "x": 9, - "y": 4 + "x": 8, + "y": 11 }, - "id": 12, + "id": 23, "options": { "colorMode": "value", "graphMode": "area", @@ -465,13 +622,13 @@ "targets": [ { "editorMode": "code", - "expr": "sum(avalanche_evm_eth_chain_txs_processed) / sum(avalanche_evm_eth_chain_block_inserts_count)", + "expr": "avg(rate(avalanche_evm_eth_chain_block_inserts[30s]))", "legendFormat": "__auto", "range": true, "refId": "A" } ], - "title": "Txs per block", + "title": "Blocks per second", "type": "stat" }, { @@ -479,11 +636,11 @@ "type": "prometheus", "uid": "dekh3fflmqzggf" }, - "description": "", + "description": "Max current number of transactions processed per second across all nodes", "fieldConfig": { "defaults": { "color": { - "fixedColor": "super-light-yellow", + "fixedColor": "light-blue", "mode": "fixed" }, "mappings": [], @@ -495,17 +652,17 @@ } ] }, - "unit": "wei" + "unit": "none" }, "overrides": [] }, "gridPos": { "h": 4, - "w": 3, - "x": 13, - "y": 4 + "w": 4, + "x": 0, + "y": 15 }, - "id": 13, + "id": 17, "options": { "colorMode": "value", "graphMode": "area", @@ -527,13 +684,13 @@ "targets": [ { "editorMode": "code", - "expr": "sum(avalanche_evm_eth_chain_latest_basefee)", + "expr": "max(rate(avalanche_evm_eth_chain_txs_processed[1m]))", "legendFormat": "__auto", "range": true, "refId": "A" } ], - "title": "Base fee", + "title": "Max txs per second", "type": "stat" }, { @@ -541,7 +698,7 @@ "type": "prometheus", "uid": "dekh3fflmqzggf" }, - "description": "Number of blocks processed per second", + "description": "Max number of gas processed per second across all nodes", "fieldConfig": { "defaults": { "color": { @@ -563,11 +720,11 @@ }, "gridPos": { "h": 4, - "w": 5, - "x": 0, - "y": 8 + "w": 4, + "x": 4, + "y": 15 }, - "id": 15, + "id": 24, "options": { "colorMode": "value", "graphMode": "area", @@ -589,25 +746,38 @@ "targets": [ { "editorMode": "code", - "expr": "sum(rate(avalanche_evm_eth_chain_block_inserts[1m]))", + "expr": "max(rate(avalanche_evm_eth_chain_block_gas_used_processed[1m]))", "legendFormat": "__auto", "range": true, "refId": "A" } ], - "title": "Blocks per second", + "title": "Max gas per second", "type": "stat" }, + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 19 + }, + "id": 21, + "panels": [], + "title": "Per block", + "type": "row" + }, { "datasource": { "type": "prometheus", "uid": "dekh3fflmqzggf" }, - "description": "Number of gas processed per second", + "description": "Number of transactions processed per block averaged across all nodes", "fieldConfig": { "defaults": { "color": { - "fixedColor": "light-blue", + "fixedColor": "super-light-purple", "mode": "fixed" }, "mappings": [], @@ -626,10 +796,10 @@ "gridPos": { "h": 4, "w": 4, - "x": 5, - "y": 8 + "x": 0, + "y": 20 }, - "id": 16, + "id": 12, "options": { "colorMode": "value", "graphMode": "area", @@ -651,13 +821,13 @@ "targets": [ { "editorMode": "code", - "expr": "sum(rate(avalanche_evm_eth_chain_block_gas_used_processed[1m]))", + "expr": "avg(rate(avalanche_evm_eth_chain_txs_processed[1m])) / avg(rate(avalanche_evm_eth_chain_block_inserts_count[1m]))", "legendFormat": "__auto", "range": true, "refId": "A" } ], - "title": "Gas per second", + "title": "Average txs per block", "type": "stat" }, { @@ -665,11 +835,11 @@ "type": "prometheus", "uid": "dekh3fflmqzggf" }, - "description": "Number of transactions processed per second", + "description": "Gas used in processed block averaged across all nodes", "fieldConfig": { "defaults": { "color": { - "fixedColor": "light-blue", + "fixedColor": "super-light-purple", "mode": "fixed" }, "mappings": [], @@ -688,10 +858,10 @@ "gridPos": { "h": 4, "w": 4, - "x": 9, - "y": 8 + "x": 4, + "y": 20 }, - "id": 14, + "id": 11, "options": { "colorMode": "value", "graphMode": "area", @@ -713,13 +883,79 @@ "targets": [ { "editorMode": "code", - "expr": "sum(rate(avalanche_evm_eth_chain_txs_processed[60s]))", + "expr": "avg(rate(avalanche_evm_eth_chain_block_gas_used_processed[1m])) / avg(rate(avalanche_evm_eth_chain_block_inserts_count[1m]))", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Average gas per block", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "dekh3fflmqzggf" + }, + "description": "Insert time per block averaged across all nodes", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "super-light-purple", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 8, + "y": 20 + }, + "id": 5, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.6.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "dekh3fflmqzggf" + }, + "editorMode": "code", + "expr": "avg(rate(avalanche_evm_eth_chain_block_inserts[1m]))/avg(rate(avalanche_evm_eth_chain_block_inserts_count[1m]))", "legendFormat": "__auto", "range": true, "refId": "A" } ], - "title": "Txs per second", + "title": "Average insert time per block", "type": "stat" } ], @@ -752,12 +988,12 @@ ] }, "time": { - "from": "now-5m", + "from": "now-30m", "to": "now" }, "timepicker": {}, "timezone": "browser", "title": "Load testing", "uid": "aejze3k4d0mpsb", - "version": 6 + "version": 15 } \ No newline at end of file From a9e7e0f8e73bdabd6849914f600d6a5088d4d1f4 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 7 May 2025 18:25:02 +0200 Subject: [PATCH 063/197] Fix logging using avalanchego's logger --- tests/load/c/execute.go | 5 +++-- tests/load/prometheus_server.go | 8 +++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/tests/load/c/execute.go b/tests/load/c/execute.go index d887af4e2065..65d00d17c10e 100644 --- a/tests/load/c/execute.go +++ b/tests/load/c/execute.go @@ -8,6 +8,7 @@ import ( "crypto/ecdsa" "fmt" "math/big" + "os" "time" "github.com/ava-labs/libevm/core/types" @@ -34,7 +35,7 @@ type config struct { } func execute(ctx context.Context, preFundedKeys []*secp256k1.PrivateKey, config config) error { - logger := logging.NewLogger("") + logger := logging.NewLogger("", logging.NewWrappedCore(logging.Info, os.Stdout, logging.Auto.ConsoleEncoder())) keys, err := fixKeysCount(preFundedKeys, int(config.agents)) if err != nil { @@ -52,7 +53,7 @@ func execute(ctx context.Context, preFundedKeys []*secp256k1.PrivateKey, config } registry := prometheus.NewRegistry() - metricsServer := load.NewPrometheusServer("127.0.0.1:8082", registry) + metricsServer := load.NewPrometheusServer("127.0.0.1:8082", registry, logger) tracker, err := load.NewPrometheusTracker[*types.Transaction](registry, logger) if err != nil { return fmt.Errorf("creating tracker: %w", err) diff --git a/tests/load/prometheus_server.go b/tests/load/prometheus_server.go index 8c9aea04dd86..fa4919ee4539 100644 --- a/tests/load/prometheus_server.go +++ b/tests/load/prometheus_server.go @@ -11,7 +11,7 @@ import ( "net/http" "time" - "github.com/ava-labs/libevm/log" + "github.com/ava-labs/avalanchego/utils/logging" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" ) @@ -20,12 +20,14 @@ type MetricsServer struct { addr string registry *prometheus.Registry server http.Server + logger logging.Logger } -func NewPrometheusServer(addr string, registry *prometheus.Registry) *MetricsServer { +func NewPrometheusServer(addr string, registry *prometheus.Registry, logger logging.Logger) *MetricsServer { return &MetricsServer{ addr: addr, registry: registry, + logger: logger, } } @@ -65,7 +67,7 @@ func (s *MetricsServer) Start() (runError <-chan error, err error) { }() <-ready - log.Info(fmt.Sprintf("Metrics server available at http://%s%s", listener.Addr(), metricsPattern)) + s.logger.Info(fmt.Sprintf("Metrics server available at http://%s%s", listener.Addr(), metricsPattern)) return runError, nil } From 97a154d147c3da2a217af74c79f554ce1408f4c3 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 8 May 2025 12:06:11 +0200 Subject: [PATCH 064/197] Fix gci linting --- tests/load/prometheus_server.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/load/prometheus_server.go b/tests/load/prometheus_server.go index fa4919ee4539..e1d7a5490a4c 100644 --- a/tests/load/prometheus_server.go +++ b/tests/load/prometheus_server.go @@ -11,9 +11,10 @@ import ( "net/http" "time" - "github.com/ava-labs/avalanchego/utils/logging" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" + + "github.com/ava-labs/avalanchego/utils/logging" ) type MetricsServer struct { From ccaff101c46b0bd5543308ff09f8801c3d3e2057 Mon Sep 17 00:00:00 2001 From: Rodrigo Villar Date: Thu, 8 May 2025 09:15:53 -0400 Subject: [PATCH 065/197] relax generic type constraint --- tests/load/agent.go | 4 ++-- tests/load/burst_orchestrator.go | 4 ++-- tests/load/dependencies.go | 6 +++--- tests/load/gradual_orchestrator.go | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/load/agent.go b/tests/load/agent.go index b0362b5219aa..1af369cae9c7 100644 --- a/tests/load/agent.go +++ b/tests/load/agent.go @@ -3,12 +3,12 @@ package load -type Agent[T comparable] struct { +type Agent[T any] struct { Issuer Issuer[T] Listener Listener[T] } -func NewAgent[T comparable]( +func NewAgent[T any]( issuer Issuer[T], listener Listener[T], ) Agent[T] { diff --git a/tests/load/burst_orchestrator.go b/tests/load/burst_orchestrator.go index 9ff157b55706..529b278c98cf 100644 --- a/tests/load/burst_orchestrator.go +++ b/tests/load/burst_orchestrator.go @@ -21,14 +21,14 @@ type BurstOrchestratorConfig struct { // BurstOrchestrator tests the network by sending a fixed number of // transactions en masse in a short timeframe. -type BurstOrchestrator[T comparable] struct { +type BurstOrchestrator[T any] struct { agents []Agent[T] log logging.Logger config BurstOrchestratorConfig } -func NewBurstOrchestrator[T comparable]( +func NewBurstOrchestrator[T any]( agents []Agent[T], log logging.Logger, config BurstOrchestratorConfig, diff --git a/tests/load/dependencies.go b/tests/load/dependencies.go index 613b2bc0f8d1..1d7bc2b6fcb2 100644 --- a/tests/load/dependencies.go +++ b/tests/load/dependencies.go @@ -5,13 +5,13 @@ package load import "context" -type Issuer[T comparable] interface { +type Issuer[T any] interface { // GenerateAndIssueTx generates and sends a tx to the network, and informs the // tracker that it sent said transaction. It returns the sent transaction. GenerateAndIssueTx(ctx context.Context) (tx T, err error) } -type Listener[T comparable] interface { +type Listener[T any] interface { // Listen for the final status of transactions and notify the tracker // Listen stops if the context is done, an error occurs, or if it received // all the transactions issued and the issuer no longer issues any. @@ -27,7 +27,7 @@ type Listener[T comparable] interface { // Tracker keeps track of the status of transactions. // This must be thread-safe, so it can be called in parallel by the issuer(s) or orchestrator. -type Tracker[T comparable] interface { +type Tracker[T any] interface { // Issue records a transaction that was submitted, but whose final status is // not yet known. Issue(T) diff --git a/tests/load/gradual_orchestrator.go b/tests/load/gradual_orchestrator.go index 5e80f2993548..14b639860ec9 100644 --- a/tests/load/gradual_orchestrator.go +++ b/tests/load/gradual_orchestrator.go @@ -67,7 +67,7 @@ func DefaultGradualOrchestratorConfig() GradualOrchestratorConfig { // transactions at a given rate (currTargetTPS) and increasing that rate until it detects that // the network can no longer make progress (i.e. the rate at the network accepts // transactions is less than currTargetTPS). -type GradualOrchestrator[T, U comparable] struct { +type GradualOrchestrator[T, U any] struct { agents []Agent[T] tracker Tracker[U] @@ -81,7 +81,7 @@ type GradualOrchestrator[T, U comparable] struct { config GradualOrchestratorConfig } -func NewGradualOrchestrator[T, U comparable]( +func NewGradualOrchestrator[T, U any]( agents []Agent[T], tracker Tracker[U], log logging.Logger, From 855bfb92014a9ecd17e3ecc5c323daafd6a1b25f Mon Sep 17 00:00:00 2001 From: Rodrigo Villar Date: Thu, 8 May 2025 09:26:32 -0400 Subject: [PATCH 066/197] remove unused logger from burst orchestrator --- tests/load/burst_orchestrator.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/load/burst_orchestrator.go b/tests/load/burst_orchestrator.go index 529b278c98cf..01e937c2b70c 100644 --- a/tests/load/burst_orchestrator.go +++ b/tests/load/burst_orchestrator.go @@ -23,7 +23,6 @@ type BurstOrchestratorConfig struct { // transactions en masse in a short timeframe. type BurstOrchestrator[T any] struct { agents []Agent[T] - log logging.Logger config BurstOrchestratorConfig } @@ -35,7 +34,6 @@ func NewBurstOrchestrator[T any]( ) (*BurstOrchestrator[T], error) { return &BurstOrchestrator[T]{ agents: agents, - log: log, config: config, }, nil } From eed3292f88758472d1f050445db46e5e1b153e05 Mon Sep 17 00:00:00 2001 From: Rodrigo Villar Date: Thu, 8 May 2025 09:36:24 -0400 Subject: [PATCH 067/197] MD => md --- tests/load/README.md | 129 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 tests/load/README.md diff --git a/tests/load/README.md b/tests/load/README.md new file mode 100644 index 000000000000..13b052f21f5c --- /dev/null +++ b/tests/load/README.md @@ -0,0 +1,129 @@ +# Load + +This package provides generic utilities for blockchain load testing. We break load generation down into the following components: + +- tx issuer(s) +- tx listener(s) +- tracker +- orchestrator + +The transaction issuer(s) and listener(s) may be VM specific and provide the +necessary injected dependencies for the orchestrator. This enables us to +construct different load testing strategies on top of the same re-usable code. +For example, we can re-use these components for a short burst of transactions or +to perform gradual load testing. + +## Architecture + +```mermaid +graph + O["Orchestrator"] + subgraph "Agent 3" + A3_I["Issuer 3"] + A3_L["Listener 3"] + end + subgraph "Agent 2" + A2_I["Issuer 2"] + A2_L["Listener 2"] + end + subgraph "Agent 1" + A1_I["Issuer 1"] + A1_L["Listener 1"] + end + T["Tracker"] + + T --> O + + O --> A1_I + O --> A2_I + O --> A3_I + + A1_I --> A1_L + A2_I --> A2_L + A3_I --> A3_L + + A1_I --> T + A2_I --> T + A3_I --> T + A1_L --> T + A2_L --> T + A3_L --> T +``` + +### Orchestrator + +The orchestrator is responsible for directing issuers and listeners +to send transactions to the network and detect when a transaction is confirmed. +The strategy for how the orchestrator directs issuers varies +between implementations (e.g. short burst vs gradual load). + +### Issuer + +The issuer is responsible for generating and issuing transactions. +It notifies the tracker of all issued transactions. + +### Listener + +The listener is responsible for listening to the network and confirming +transactions or marking them as failed. As it receives transactions, it +notifies the tracker of the transaction status. + +### Tracker + +The tracker is responsible for maintaining metrics for all sent txs. Since the +tracker is used by both the issuers, listeners and the orchestrator, all methods of the +tracker must be thread safe. + +## Default Orchestrators + +This package comes with the following orchestrators: + +### Short Burst + +The short burst orchestator is used to send a fixed number of transactions to the network at +once. This orchestrator is parameterizable via the following: + +- `N`: the number of transactions an issuer will send to the network. +- `timeout`: the maximum amount of time which, after all transactions have been sent, + the orchestrator will wait to hear the confirmation of all outstanding + transactions. + +### Gradual Load + +The gradual load orchestrator sends transactions at an initial rate (TPS) and +increases that rate until hitting the maxiumum desired rate or until the +orchestrator determines that it can no longer make progress. + +The current TPS in the gradual load orchestrator is determined by taking the +number of transactions confirmed in a given time window (`SustainedTime`) and +diving it by `SustainedTime` (in terms of seconds) Furthermore, the orchestator +has `maxAttempt` tries to try and achieve a given TPS before determining that +the given TPS is not achievable. + +Below is the pseudocode for how the gradual load orchestrator determines TPS and +for how it increases TPS: + +``` +currTargetTPS := current TPS we want to achieve +maxAttempts := maximum number of attempts we have to achieve currTargetTPS + +txsPerIssuer := currTargetTPS / numOfIssuers +attempts := 0 + +for each issuer { // Async + send txsPerIssuer txs per second +} + +for { + wait for SustainedTime + + tps := number of accepted txs divided by the SustainedTime + if tps >= currTargetTPS: + increase currTargerTPS by step + iters = 0 + else: + if attempts >= maxAttempts: + fail + iters += 1 +} +``` From c2e2b01211517bbda1f29df6270c75c114c47c00 Mon Sep 17 00:00:00 2001 From: rodrigo <77309055+RodrigoVillar@users.noreply.github.com> Date: Thu, 8 May 2025 09:38:36 -0400 Subject: [PATCH 068/197] Update tests/load/gradual_orchestrator.go Co-authored-by: Quentin McGaw Signed-off-by: rodrigo <77309055+RodrigoVillar@users.noreply.github.com> --- tests/load/gradual_orchestrator.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/load/gradual_orchestrator.go b/tests/load/gradual_orchestrator.go index 14b639860ec9..135de5a75051 100644 --- a/tests/load/gradual_orchestrator.go +++ b/tests/load/gradual_orchestrator.go @@ -225,7 +225,7 @@ func (o *GradualOrchestrator[T, U]) GetMaxObservedTPS() uint64 { return o.maxObservedTPS.Load() } -// start a goroutine to each issuer to continuously send transactions +// start a goroutine to each issuer to continuously send transactions. // if an issuer errors, all other issuers will stop as well. func (o *GradualOrchestrator[T, U]) issueTxs(ctx context.Context, currTargetTPS *atomic.Uint64) { for _, agent := range o.agents { From b5faf4f3da3bf14532fcb96a40e27cba83e57a66 Mon Sep 17 00:00:00 2001 From: Rodrigo Villar Date: Thu, 8 May 2025 09:50:46 -0400 Subject: [PATCH 069/197] nits --- tests/load/gradual_orchestrator.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/load/gradual_orchestrator.go b/tests/load/gradual_orchestrator.go index 135de5a75051..c1a836c3f03d 100644 --- a/tests/load/gradual_orchestrator.go +++ b/tests/load/gradual_orchestrator.go @@ -182,10 +182,9 @@ func (o *GradualOrchestrator[T, U]) run(ctx context.Context) bool { if o.config.Terminate { o.log.Info("terminating orchestrator") break // Case 2 - } else { - o.log.Info("orchestrator will now continue running at max TPS") - continue } + o.log.Info("orchestrator will now continue running at max TPS") + continue } o.log.Info( "increasing TPS", From c33e89416bdbd7452e843f4acf28ac67b93c9f0b Mon Sep 17 00:00:00 2001 From: rodrigo <77309055+RodrigoVillar@users.noreply.github.com> Date: Thu, 8 May 2025 10:08:04 -0400 Subject: [PATCH 070/197] Update tests/load/gradual_orchestrator.go Co-authored-by: Quentin McGaw Signed-off-by: rodrigo <77309055+RodrigoVillar@users.noreply.github.com> --- tests/load/gradual_orchestrator.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/load/gradual_orchestrator.go b/tests/load/gradual_orchestrator.go index c1a836c3f03d..d50664045a97 100644 --- a/tests/load/gradual_orchestrator.go +++ b/tests/load/gradual_orchestrator.go @@ -245,7 +245,13 @@ func (o *GradualOrchestrator[T, U]) issueTxs(ctx context.Context, currTargetTPS } diff := time.Second - time.Since(currTime) if diff > 0 { - time.Sleep(diff) + timer := time.NewTimer(diff) + select { + case <-ctx.Done(): + timer.Stop() + return nil + case <-timer.C: + } } } }) From 480e4bca15ea57e505b0cc4eb21297e73acbf960 Mon Sep 17 00:00:00 2001 From: Rodrigo Villar Date: Thu, 8 May 2025 10:48:44 -0400 Subject: [PATCH 071/197] remove named return parameters --- tests/load/dependencies.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/load/dependencies.go b/tests/load/dependencies.go index 1d7bc2b6fcb2..c0247a0430e7 100644 --- a/tests/load/dependencies.go +++ b/tests/load/dependencies.go @@ -8,7 +8,7 @@ import "context" type Issuer[T any] interface { // GenerateAndIssueTx generates and sends a tx to the network, and informs the // tracker that it sent said transaction. It returns the sent transaction. - GenerateAndIssueTx(ctx context.Context) (tx T, err error) + GenerateAndIssueTx(ctx context.Context) (T, error) } type Listener[T any] interface { From 8e19faa5b935f3bc3181c6a321e3579b81c89759 Mon Sep 17 00:00:00 2001 From: Rodrigo Villar Date: Thu, 8 May 2025 11:04:03 -0400 Subject: [PATCH 072/197] lint --- tests/load/burst_orchestrator.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/load/burst_orchestrator.go b/tests/load/burst_orchestrator.go index 01e937c2b70c..1fb560e94c1b 100644 --- a/tests/load/burst_orchestrator.go +++ b/tests/load/burst_orchestrator.go @@ -8,8 +8,6 @@ import ( "time" "golang.org/x/sync/errgroup" - - "github.com/ava-labs/avalanchego/utils/logging" ) var _ orchestrator = (*BurstOrchestrator[any])(nil) @@ -29,7 +27,6 @@ type BurstOrchestrator[T any] struct { func NewBurstOrchestrator[T any]( agents []Agent[T], - log logging.Logger, config BurstOrchestratorConfig, ) (*BurstOrchestrator[T], error) { return &BurstOrchestrator[T]{ From 9385b6e25fd077db0f8beaeaca14dd7d41df8d9e Mon Sep 17 00:00:00 2001 From: Rodrigo Villar Date: Thu, 8 May 2025 11:21:29 -0400 Subject: [PATCH 073/197] remove duplicate MD --- tests/load/README.MD | 129 ------------------------------------------- 1 file changed, 129 deletions(-) delete mode 100644 tests/load/README.MD diff --git a/tests/load/README.MD b/tests/load/README.MD deleted file mode 100644 index 13b052f21f5c..000000000000 --- a/tests/load/README.MD +++ /dev/null @@ -1,129 +0,0 @@ -# Load - -This package provides generic utilities for blockchain load testing. We break load generation down into the following components: - -- tx issuer(s) -- tx listener(s) -- tracker -- orchestrator - -The transaction issuer(s) and listener(s) may be VM specific and provide the -necessary injected dependencies for the orchestrator. This enables us to -construct different load testing strategies on top of the same re-usable code. -For example, we can re-use these components for a short burst of transactions or -to perform gradual load testing. - -## Architecture - -```mermaid -graph - O["Orchestrator"] - subgraph "Agent 3" - A3_I["Issuer 3"] - A3_L["Listener 3"] - end - subgraph "Agent 2" - A2_I["Issuer 2"] - A2_L["Listener 2"] - end - subgraph "Agent 1" - A1_I["Issuer 1"] - A1_L["Listener 1"] - end - T["Tracker"] - - T --> O - - O --> A1_I - O --> A2_I - O --> A3_I - - A1_I --> A1_L - A2_I --> A2_L - A3_I --> A3_L - - A1_I --> T - A2_I --> T - A3_I --> T - A1_L --> T - A2_L --> T - A3_L --> T -``` - -### Orchestrator - -The orchestrator is responsible for directing issuers and listeners -to send transactions to the network and detect when a transaction is confirmed. -The strategy for how the orchestrator directs issuers varies -between implementations (e.g. short burst vs gradual load). - -### Issuer - -The issuer is responsible for generating and issuing transactions. -It notifies the tracker of all issued transactions. - -### Listener - -The listener is responsible for listening to the network and confirming -transactions or marking them as failed. As it receives transactions, it -notifies the tracker of the transaction status. - -### Tracker - -The tracker is responsible for maintaining metrics for all sent txs. Since the -tracker is used by both the issuers, listeners and the orchestrator, all methods of the -tracker must be thread safe. - -## Default Orchestrators - -This package comes with the following orchestrators: - -### Short Burst - -The short burst orchestator is used to send a fixed number of transactions to the network at -once. This orchestrator is parameterizable via the following: - -- `N`: the number of transactions an issuer will send to the network. -- `timeout`: the maximum amount of time which, after all transactions have been sent, - the orchestrator will wait to hear the confirmation of all outstanding - transactions. - -### Gradual Load - -The gradual load orchestrator sends transactions at an initial rate (TPS) and -increases that rate until hitting the maxiumum desired rate or until the -orchestrator determines that it can no longer make progress. - -The current TPS in the gradual load orchestrator is determined by taking the -number of transactions confirmed in a given time window (`SustainedTime`) and -diving it by `SustainedTime` (in terms of seconds) Furthermore, the orchestator -has `maxAttempt` tries to try and achieve a given TPS before determining that -the given TPS is not achievable. - -Below is the pseudocode for how the gradual load orchestrator determines TPS and -for how it increases TPS: - -``` -currTargetTPS := current TPS we want to achieve -maxAttempts := maximum number of attempts we have to achieve currTargetTPS - -txsPerIssuer := currTargetTPS / numOfIssuers -attempts := 0 - -for each issuer { // Async - send txsPerIssuer txs per second -} - -for { - wait for SustainedTime - - tps := number of accepted txs divided by the SustainedTime - if tps >= currTargetTPS: - increase currTargerTPS by step - iters = 0 - else: - if attempts >= maxAttempts: - fail - iters += 1 -} -``` From 672f377cf5812097d51ac83a71a16c0a97c418ff Mon Sep 17 00:00:00 2001 From: Rodrigo Villar Date: Thu, 8 May 2025 15:57:26 -0400 Subject: [PATCH 074/197] remove unnecessary errors from orchestrator constructors --- tests/load/burst_orchestrator.go | 4 ++-- tests/load/gradual_orchestrator.go | 4 ++-- tests/load/gradual_orchestrator_test.go | 5 ++--- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/tests/load/burst_orchestrator.go b/tests/load/burst_orchestrator.go index 1fb560e94c1b..2c2fb98db099 100644 --- a/tests/load/burst_orchestrator.go +++ b/tests/load/burst_orchestrator.go @@ -28,11 +28,11 @@ type BurstOrchestrator[T any] struct { func NewBurstOrchestrator[T any]( agents []Agent[T], config BurstOrchestratorConfig, -) (*BurstOrchestrator[T], error) { +) *BurstOrchestrator[T] { return &BurstOrchestrator[T]{ agents: agents, config: config, - }, nil + } } // Execute orders issuers to send a fixed number of transactions and then waits diff --git a/tests/load/gradual_orchestrator.go b/tests/load/gradual_orchestrator.go index d50664045a97..1072e2ed7da4 100644 --- a/tests/load/gradual_orchestrator.go +++ b/tests/load/gradual_orchestrator.go @@ -86,13 +86,13 @@ func NewGradualOrchestrator[T, U any]( tracker Tracker[U], log logging.Logger, config GradualOrchestratorConfig, -) (*GradualOrchestrator[T, U], error) { +) *GradualOrchestrator[T, U] { return &GradualOrchestrator[T, U]{ agents: agents, tracker: tracker, log: log, config: config, - }, nil + } } func (o *GradualOrchestrator[T, U]) Execute(ctx context.Context) error { diff --git a/tests/load/gradual_orchestrator_test.go b/tests/load/gradual_orchestrator_test.go index 9781e10cf73e..75959ee37173 100644 --- a/tests/load/gradual_orchestrator_test.go +++ b/tests/load/gradual_orchestrator_test.go @@ -77,13 +77,12 @@ func TestGradualOrchestratorTPS(t *testing.T) { ), } - orchestrator, err := NewGradualOrchestrator( + orchestrator := NewGradualOrchestrator( agents, tracker, logging.NoLog{}, tt.config, ) - r.NoError(err) r.ErrorIs(orchestrator.Execute(ctx), tt.expectedErr) @@ -148,7 +147,7 @@ func TestGradualOrchestratorExecution(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - orchestrator, err := NewGradualOrchestrator( + orchestrator := NewGradualOrchestrator( tt.agents, tracker, logging.NoLog{}, From 5ef56602ba492aa926bd3d62daa2776576b03daf Mon Sep 17 00:00:00 2001 From: rodrigo <77309055+RodrigoVillar@users.noreply.github.com> Date: Thu, 8 May 2025 15:58:09 -0400 Subject: [PATCH 075/197] Update tests/load/gradual_orchestrator.go Co-authored-by: Quentin McGaw Signed-off-by: rodrigo <77309055+RodrigoVillar@users.noreply.github.com> --- tests/load/gradual_orchestrator.go | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/tests/load/gradual_orchestrator.go b/tests/load/gradual_orchestrator.go index 1072e2ed7da4..09017743fdfb 100644 --- a/tests/load/gradual_orchestrator.go +++ b/tests/load/gradual_orchestrator.go @@ -244,14 +244,15 @@ func (o *GradualOrchestrator[T, U]) issueTxs(ctx context.Context, currTargetTPS agent.Listener.RegisterIssued(tx) } diff := time.Second - time.Since(currTime) - if diff > 0 { - timer := time.NewTimer(diff) - select { - case <-ctx.Done(): - timer.Stop() - return nil - case <-timer.C: - } + if diff <= 0 { + continue + } + timer := time.NewTimer(diff) + select { + case <-ctx.Done(): + timer.Stop() + return nil + case <-timer.C: } } }) From 0595e310e678125943f76eb3224bff85a2730f6e Mon Sep 17 00:00:00 2001 From: Rodrigo Villar Date: Thu, 8 May 2025 16:08:17 -0400 Subject: [PATCH 076/197] make observerGroup a pointer --- tests/load/gradual_orchestrator.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/load/gradual_orchestrator.go b/tests/load/gradual_orchestrator.go index 09017743fdfb..e8ac61b6b9a3 100644 --- a/tests/load/gradual_orchestrator.go +++ b/tests/load/gradual_orchestrator.go @@ -75,7 +75,7 @@ type GradualOrchestrator[T, U any] struct { maxObservedTPS atomic.Uint64 - observerGroup errgroup.Group + observerGroup *errgroup.Group issuerGroup *errgroup.Group config GradualOrchestratorConfig @@ -99,6 +99,7 @@ func (o *GradualOrchestrator[T, U]) Execute(ctx context.Context) error { ctx, cancel := context.WithCancel(ctx) // start a goroutine to confirm each issuer's transactions + o.observerGroup = &errgroup.Group{} for _, agent := range o.agents { o.observerGroup.Go(func() error { return agent.Listener.Listen(ctx) }) } From 3e2294f7bd83e1c7558324ab358c472874a709ac Mon Sep 17 00:00:00 2001 From: Rodrigo Villar Date: Thu, 8 May 2025 16:14:47 -0400 Subject: [PATCH 077/197] default => new --- tests/load/gradual_orchestrator.go | 2 +- tests/load/gradual_orchestrator_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/load/gradual_orchestrator.go b/tests/load/gradual_orchestrator.go index e8ac61b6b9a3..b7929a93542e 100644 --- a/tests/load/gradual_orchestrator.go +++ b/tests/load/gradual_orchestrator.go @@ -51,7 +51,7 @@ type GradualOrchestratorConfig struct { Terminate bool } -func DefaultGradualOrchestratorConfig() GradualOrchestratorConfig { +func NewGradualOrchestratorConfig() GradualOrchestratorConfig { return GradualOrchestratorConfig{ MaxTPS: 5_000, MinTPS: 1_000, diff --git a/tests/load/gradual_orchestrator_test.go b/tests/load/gradual_orchestrator_test.go index 75959ee37173..e75e7869da8f 100644 --- a/tests/load/gradual_orchestrator_test.go +++ b/tests/load/gradual_orchestrator_test.go @@ -151,7 +151,7 @@ func TestGradualOrchestratorExecution(t *testing.T) { tt.agents, tracker, logging.NoLog{}, - DefaultGradualOrchestratorConfig(), + NewGradualOrchestratorConfig(), ) r.NoError(err) From 781bf6082dc13aec971719cb97df5b3456097373 Mon Sep 17 00:00:00 2001 From: Rodrigo Villar Date: Fri, 9 May 2025 09:33:12 -0400 Subject: [PATCH 078/197] convert tracker from interface to impl --- tests/load/dependencies.go | 22 --------- tests/load/gradual_orchestrator.go | 8 ++-- tests/load/gradual_orchestrator_test.go | 6 +-- .../{prometheus_tracker.go => tracker.go} | 45 ++++++++++++------- 4 files changed, 36 insertions(+), 45 deletions(-) rename tests/load/{prometheus_tracker.go => tracker.go} (59%) diff --git a/tests/load/dependencies.go b/tests/load/dependencies.go index c0247a0430e7..ae2625a735dc 100644 --- a/tests/load/dependencies.go +++ b/tests/load/dependencies.go @@ -25,28 +25,6 @@ type Listener[T any] interface { IssuingDone() } -// Tracker keeps track of the status of transactions. -// This must be thread-safe, so it can be called in parallel by the issuer(s) or orchestrator. -type Tracker[T any] interface { - // Issue records a transaction that was submitted, but whose final status is - // not yet known. - Issue(T) - // ObserveConfirmed records a transaction that was confirmed. - ObserveConfirmed(T) - // ObserveFailed records a transaction that failed (e.g. expired) - ObserveFailed(T) - - // GetObservedIssued returns the number of transactions that the tracker has - // confirmed were issued. - GetObservedIssued() uint64 - // GetObservedConfirmed returns the number of transactions that the tracker has - // confirmed were accepted. - GetObservedConfirmed() uint64 - // GetObservedFailed returns the number of transactions that the tracker has - // confirmed failed. - GetObservedFailed() uint64 -} - // orchestrator executes the load test by coordinating the issuers to send // transactions, in a manner depending on the implementation. type orchestrator interface { diff --git a/tests/load/gradual_orchestrator.go b/tests/load/gradual_orchestrator.go index b7929a93542e..97979e93019a 100644 --- a/tests/load/gradual_orchestrator.go +++ b/tests/load/gradual_orchestrator.go @@ -67,9 +67,9 @@ func NewGradualOrchestratorConfig() GradualOrchestratorConfig { // transactions at a given rate (currTargetTPS) and increasing that rate until it detects that // the network can no longer make progress (i.e. the rate at the network accepts // transactions is less than currTargetTPS). -type GradualOrchestrator[T, U any] struct { +type GradualOrchestrator[T any, U comparable] struct { agents []Agent[T] - tracker Tracker[U] + tracker *Tracker[U] log logging.Logger @@ -81,9 +81,9 @@ type GradualOrchestrator[T, U any] struct { config GradualOrchestratorConfig } -func NewGradualOrchestrator[T, U any]( +func NewGradualOrchestrator[T any, U comparable]( agents []Agent[T], - tracker Tracker[U], + tracker *Tracker[U], log logging.Logger, config GradualOrchestratorConfig, ) *GradualOrchestrator[T, U] { diff --git a/tests/load/gradual_orchestrator_test.go b/tests/load/gradual_orchestrator_test.go index e75e7869da8f..071f31a88df3 100644 --- a/tests/load/gradual_orchestrator_test.go +++ b/tests/load/gradual_orchestrator_test.go @@ -61,7 +61,7 @@ func TestGradualOrchestratorTPS(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - tracker, err := NewPrometheusTracker[ids.ID](prometheus.NewRegistry()) + tracker, err := NewTracker[ids.ID](prometheus.NewRegistry()) r.NoError(err) agents := []Agent[ids.ID]{ @@ -102,7 +102,7 @@ func TestGradualOrchestratorExecution(t *testing.T) { errMockIssuer = errors.New("mock issuer error") ) - tracker, err := NewPrometheusTracker[ids.ID](prometheus.NewRegistry()) + tracker, err := NewTracker[ids.ID](prometheus.NewRegistry()) require.NoError(t, err, "creating tracker") tests := []struct { @@ -164,7 +164,7 @@ type mockIssuer struct { generateTxF func() (ids.ID, error) currTxsIssued uint64 maxTxs uint64 - tracker Tracker[ids.ID] + tracker *Tracker[ids.ID] issueTxErr error } diff --git a/tests/load/prometheus_tracker.go b/tests/load/tracker.go similarity index 59% rename from tests/load/prometheus_tracker.go rename to tests/load/tracker.go index d63e05744ac7..b05bb16055d2 100644 --- a/tests/load/prometheus_tracker.go +++ b/tests/load/tracker.go @@ -14,9 +14,9 @@ import ( const namespace = "load" -var _ Tracker[any] = (*PrometheusTracker[any])(nil) - -type PrometheusTracker[T comparable] struct { +// Tracker keeps track of the status of transactions. +// This is thread-safe and can be called in parallel by the issuer(s) or orchestrator. +type Tracker[T comparable] struct { lock sync.RWMutex outstandingTxs map[T]time.Time @@ -32,8 +32,11 @@ type PrometheusTracker[T comparable] struct { txLatency prometheus.Histogram } -func NewPrometheusTracker[T comparable](reg *prometheus.Registry) (*PrometheusTracker[T], error) { - prometheusTracker := &PrometheusTracker[T]{ +// NewTracker returns a new Tracker instance which records metrics for the number +// of transactions issued, confirmed, and failed. It also tracks the latency of +// transactions. +func NewTracker[T comparable](reg *prometheus.Registry) (*Tracker[T], error) { + tracker := &Tracker[T]{ outstandingTxs: make(map[T]time.Time), txsIssuedCounter: prometheus.NewCounter(prometheus.CounterOpts{ Namespace: namespace, @@ -59,36 +62,44 @@ func NewPrometheusTracker[T comparable](reg *prometheus.Registry) (*PrometheusTr errs := wrappers.Errs{} errs.Add( - reg.Register(prometheusTracker.txsIssuedCounter), - reg.Register(prometheusTracker.txsConfirmedCounter), - reg.Register(prometheusTracker.txsFailedCounter), - reg.Register(prometheusTracker.txLatency), + reg.Register(tracker.txsIssuedCounter), + reg.Register(tracker.txsConfirmedCounter), + reg.Register(tracker.txsFailedCounter), + reg.Register(tracker.txLatency), ) - return prometheusTracker, errs.Err + return tracker, errs.Err } -func (p *PrometheusTracker[T]) GetObservedConfirmed() uint64 { +// GetObservedConfirmed returns the number of transactions that the tracker has +// confirmed were accepted. +func (p *Tracker[T]) GetObservedConfirmed() uint64 { p.lock.RLock() defer p.lock.RUnlock() return p.txsConfirmed } -func (p *PrometheusTracker[T]) GetObservedFailed() uint64 { +// GetObservedFailed returns the number of transactions that the tracker has +// confirmed failed. +func (p *Tracker[T]) GetObservedFailed() uint64 { p.lock.RLock() defer p.lock.RUnlock() return p.txsFailed } -func (p *PrometheusTracker[T]) GetObservedIssued() uint64 { +// GetObservedIssued returns the number of transactions that the tracker has +// confirmed were issued. +func (p *Tracker[T]) GetObservedIssued() uint64 { p.lock.RLock() defer p.lock.RUnlock() return p.txsIssued } -func (p *PrometheusTracker[T]) Issue(tx T) { +// Issue records a transaction that was submitted, but whose final status is +// not yet known. +func (p *Tracker[T]) Issue(tx T) { p.lock.Lock() defer p.lock.Unlock() @@ -97,7 +108,8 @@ func (p *PrometheusTracker[T]) Issue(tx T) { p.txsIssuedCounter.Inc() } -func (p *PrometheusTracker[T]) ObserveConfirmed(tx T) { +// ObserveConfirmed records a transaction that was confirmed. +func (p *Tracker[T]) ObserveConfirmed(tx T) { p.lock.Lock() defer p.lock.Unlock() @@ -109,7 +121,8 @@ func (p *PrometheusTracker[T]) ObserveConfirmed(tx T) { p.txLatency.Observe(float64(time.Since(startTime).Milliseconds())) } -func (p *PrometheusTracker[T]) ObserveFailed(tx T) { +// ObserveFailed records a transaction that failed (e.g. expired) +func (p *Tracker[T]) ObserveFailed(tx T) { p.lock.Lock() defer p.lock.Unlock() From b0ae321dc3d4868a65e2f832761933ebee062e3d Mon Sep 17 00:00:00 2001 From: Rodrigo Villar Date: Fri, 9 May 2025 09:45:36 -0400 Subject: [PATCH 079/197] single generic on gradual orchestrator --- tests/load/gradual_orchestrator.go | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/load/gradual_orchestrator.go b/tests/load/gradual_orchestrator.go index 97979e93019a..d4a317d507d8 100644 --- a/tests/load/gradual_orchestrator.go +++ b/tests/load/gradual_orchestrator.go @@ -17,7 +17,7 @@ import ( ) var ( - _ orchestrator = (*GradualOrchestrator[any, any])(nil) + _ orchestrator = (*GradualOrchestrator[any])(nil) ErrFailedToReachTargetTPS = errors.New("failed to reach target TPS") ) @@ -67,9 +67,9 @@ func NewGradualOrchestratorConfig() GradualOrchestratorConfig { // transactions at a given rate (currTargetTPS) and increasing that rate until it detects that // the network can no longer make progress (i.e. the rate at the network accepts // transactions is less than currTargetTPS). -type GradualOrchestrator[T any, U comparable] struct { +type GradualOrchestrator[T comparable] struct { agents []Agent[T] - tracker *Tracker[U] + tracker *Tracker[T] log logging.Logger @@ -81,13 +81,13 @@ type GradualOrchestrator[T any, U comparable] struct { config GradualOrchestratorConfig } -func NewGradualOrchestrator[T any, U comparable]( +func NewGradualOrchestrator[T comparable]( agents []Agent[T], - tracker *Tracker[U], + tracker *Tracker[T], log logging.Logger, config GradualOrchestratorConfig, -) *GradualOrchestrator[T, U] { - return &GradualOrchestrator[T, U]{ +) *GradualOrchestrator[T] { + return &GradualOrchestrator[T]{ agents: agents, tracker: tracker, log: log, @@ -95,7 +95,7 @@ func NewGradualOrchestrator[T any, U comparable]( } } -func (o *GradualOrchestrator[T, U]) Execute(ctx context.Context) error { +func (o *GradualOrchestrator[T]) Execute(ctx context.Context) error { ctx, cancel := context.WithCancel(ctx) // start a goroutine to confirm each issuer's transactions @@ -127,7 +127,7 @@ func (o *GradualOrchestrator[T, U]) Execute(ctx context.Context) error { // 1. an issuer has errored // 2. the max TPS target has been reached and we can terminate // 3. the maximum number of attempts to reach a target TPS has been reached -func (o *GradualOrchestrator[T, U]) run(ctx context.Context) bool { +func (o *GradualOrchestrator[T]) run(ctx context.Context) bool { var ( prevConfirmed = o.tracker.GetObservedConfirmed() prevTime = time.Now() @@ -221,13 +221,13 @@ func (o *GradualOrchestrator[T, U]) run(ctx context.Context) bool { } // GetObservedIssued returns the max TPS the orchestrator observed -func (o *GradualOrchestrator[T, U]) GetMaxObservedTPS() uint64 { +func (o *GradualOrchestrator[T]) GetMaxObservedTPS() uint64 { return o.maxObservedTPS.Load() } // start a goroutine to each issuer to continuously send transactions. // if an issuer errors, all other issuers will stop as well. -func (o *GradualOrchestrator[T, U]) issueTxs(ctx context.Context, currTargetTPS *atomic.Uint64) { +func (o *GradualOrchestrator[T]) issueTxs(ctx context.Context, currTargetTPS *atomic.Uint64) { for _, agent := range o.agents { o.issuerGroup.Go(func() error { for { @@ -261,7 +261,7 @@ func (o *GradualOrchestrator[T, U]) issueTxs(ctx context.Context, currTargetTPS } // setMaxObservedTPS only if tps > the current max observed TPS. -func (o *GradualOrchestrator[T, U]) setMaxObservedTPS(tps uint64) { +func (o *GradualOrchestrator[T]) setMaxObservedTPS(tps uint64) { if tps > o.maxObservedTPS.Load() { o.maxObservedTPS.Store(tps) } From 4daa6b0834d17a32982b0a70e1f3d07880599455 Mon Sep 17 00:00:00 2001 From: Rodrigo Villar Date: Fri, 9 May 2025 12:29:04 -0400 Subject: [PATCH 080/197] merge burst into gradual --- tests/load/burst_orchestrator.go | 84 ------------------------------ tests/load/gradual_orchestrator.go | 36 +++++++++---- 2 files changed, 25 insertions(+), 95 deletions(-) delete mode 100644 tests/load/burst_orchestrator.go diff --git a/tests/load/burst_orchestrator.go b/tests/load/burst_orchestrator.go deleted file mode 100644 index 2c2fb98db099..000000000000 --- a/tests/load/burst_orchestrator.go +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package load - -import ( - "context" - "time" - - "golang.org/x/sync/errgroup" -) - -var _ orchestrator = (*BurstOrchestrator[any])(nil) - -type BurstOrchestratorConfig struct { - TxsPerIssuer uint64 - Timeout time.Duration -} - -// BurstOrchestrator tests the network by sending a fixed number of -// transactions en masse in a short timeframe. -type BurstOrchestrator[T any] struct { - agents []Agent[T] - - config BurstOrchestratorConfig -} - -func NewBurstOrchestrator[T any]( - agents []Agent[T], - config BurstOrchestratorConfig, -) *BurstOrchestrator[T] { - return &BurstOrchestrator[T]{ - agents: agents, - config: config, - } -} - -// Execute orders issuers to send a fixed number of transactions and then waits -// for all of their statuses to be confirmed or for a timeout to occur. -func (o *BurstOrchestrator[T]) Execute(ctx context.Context) error { - observerCtx, observerCancel := context.WithCancel(ctx) - defer observerCancel() - - // start a goroutine to confirm each issuer's transactions - observerGroup := errgroup.Group{} - for _, agent := range o.agents { - observerGroup.Go(func() error { return agent.Listener.Listen(observerCtx) }) - } - - // start issuing transactions sequentially from each issuer - issuerGroup := errgroup.Group{} - for _, agent := range o.agents { - issuerGroup.Go(func() error { - defer agent.Listener.IssuingDone() - for range o.config.TxsPerIssuer { - tx, err := agent.Issuer.GenerateAndIssueTx(ctx) - if err != nil { - return err - } - agent.Listener.RegisterIssued(tx) - } - return nil - }) - } - - // wait for all issuers to finish sending their transactions - if err := issuerGroup.Wait(); err != nil { - return err - } - - ctx, cancel := context.WithTimeout(ctx, o.config.Timeout) - defer cancel() - - // start a goroutine that will cancel the observer group's context - // if either the parent context is cancelled or our timeout elapses - go func() { - <-ctx.Done() - observerCancel() - }() - - // blocks until either all of the issuers have finished or our context - // is cancelled signalling for early termination (with an error) - return observerGroup.Wait() -} diff --git a/tests/load/gradual_orchestrator.go b/tests/load/gradual_orchestrator.go index d4a317d507d8..b0a0fa6cb9a6 100644 --- a/tests/load/gradual_orchestrator.go +++ b/tests/load/gradual_orchestrator.go @@ -24,11 +24,15 @@ var ( type GradualOrchestratorConfig struct { // The maximum TPS the orchestrator should aim for. - MaxTPS uint64 + // + // If set to -1, the orchestrator will behave in a burst fashion, instead + // sending MinTPS transactions at once and waiting sustainedTime seconds + // before checking if MinTPS transactions were confirmed as accepted. + MaxTPS int64 // The minimum TPS the orchestrator should start with. - MinTPS uint64 + MinTPS int64 // The step size to increase the TPS by. - Step uint64 + Step int64 // The factor by which to pad the number of txs an issuer sends per second // for example, if targetTPS = 1000 and numIssuers = 10, then each issuer @@ -131,7 +135,7 @@ func (o *GradualOrchestrator[T]) run(ctx context.Context) bool { var ( prevConfirmed = o.tracker.GetObservedConfirmed() prevTime = time.Now() - currTargetTPS = new(atomic.Uint64) + currTargetTPS = new(atomic.Int64) attempts uint64 = 1 // true if the orchestrator has reached the max TPS target achievedTargetTPS bool @@ -158,6 +162,10 @@ func (o *GradualOrchestrator[T]) run(ctx context.Context) bool { currConfirmed := o.tracker.GetObservedConfirmed() currTime := time.Now() + if o.config.MaxTPS == -1 { + return currTargetTPS.Load() == int64(currConfirmed) + } + tps := computeTPS(prevConfirmed, currConfirmed, currTime.Sub(prevTime)) o.setMaxObservedTPS(tps) @@ -172,12 +180,12 @@ func (o *GradualOrchestrator[T]) run(ctx context.Context) bool { continue } - if tps >= currTargetTPS.Load() { + if int64(tps) >= currTargetTPS.Load() { if currTargetTPS.Load() >= o.config.MaxTPS { achievedTargetTPS = true o.log.Info( "max TPS target reached", - zap.Uint64("max TPS target", currTargetTPS.Load()), + zap.Int64("max TPS target", currTargetTPS.Load()), zap.Uint64("average TPS", tps), ) if o.config.Terminate { @@ -189,9 +197,9 @@ func (o *GradualOrchestrator[T]) run(ctx context.Context) bool { } o.log.Info( "increasing TPS", - zap.Uint64("previous target TPS", currTargetTPS.Load()), + zap.Int64("previous target TPS", currTargetTPS.Load()), zap.Uint64("average TPS", tps), - zap.Uint64("new target TPS", currTargetTPS.Load()+o.config.Step), + zap.Int64("new target TPS", currTargetTPS.Load()+o.config.Step), ) currTargetTPS.Add(o.config.Step) attempts = 1 @@ -199,14 +207,14 @@ func (o *GradualOrchestrator[T]) run(ctx context.Context) bool { if attempts >= o.config.MaxAttempts { o.log.Info( "max attempts reached", - zap.Uint64("attempted target TPS", currTargetTPS.Load()), + zap.Int64("attempted target TPS", currTargetTPS.Load()), zap.Uint64("number of attempts", attempts), ) break // Case 3 } o.log.Info( "failed to reach target TPS, retrying", - zap.Uint64("current target TPS", currTargetTPS.Load()), + zap.Int64("current target TPS", currTargetTPS.Load()), zap.Uint64("average TPS", tps), zap.Uint64("attempt number", attempts), ) @@ -227,7 +235,7 @@ func (o *GradualOrchestrator[T]) GetMaxObservedTPS() uint64 { // start a goroutine to each issuer to continuously send transactions. // if an issuer errors, all other issuers will stop as well. -func (o *GradualOrchestrator[T]) issueTxs(ctx context.Context, currTargetTPS *atomic.Uint64) { +func (o *GradualOrchestrator[T]) issueTxs(ctx context.Context, currTargetTPS *atomic.Int64) { for _, agent := range o.agents { o.issuerGroup.Go(func() error { for { @@ -244,6 +252,12 @@ func (o *GradualOrchestrator[T]) issueTxs(ctx context.Context, currTargetTPS *at } agent.Listener.RegisterIssued(tx) } + + if o.config.MaxTPS == -1 { + agent.Listener.IssuingDone() + return nil + } + diff := time.Second - time.Since(currTime) if diff <= 0 { continue From 4232002f802499c6bf4c723d4acaab32c3ccdbaf Mon Sep 17 00:00:00 2001 From: Rodrigo Villar Date: Fri, 9 May 2025 12:30:27 -0400 Subject: [PATCH 081/197] remove unnecessary orchestrator interface --- tests/load/dependencies.go | 7 ------- tests/load/gradual_orchestrator.go | 6 +----- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/tests/load/dependencies.go b/tests/load/dependencies.go index ae2625a735dc..c0b3843911b4 100644 --- a/tests/load/dependencies.go +++ b/tests/load/dependencies.go @@ -24,10 +24,3 @@ type Listener[T any] interface { // IssuingDone informs the listener that no more transactions will be issued. IssuingDone() } - -// orchestrator executes the load test by coordinating the issuers to send -// transactions, in a manner depending on the implementation. -type orchestrator interface { - // Execute the load test - Execute(ctx context.Context) error -} diff --git a/tests/load/gradual_orchestrator.go b/tests/load/gradual_orchestrator.go index b0a0fa6cb9a6..f65d589d9d93 100644 --- a/tests/load/gradual_orchestrator.go +++ b/tests/load/gradual_orchestrator.go @@ -16,11 +16,7 @@ import ( "github.com/ava-labs/avalanchego/utils/logging" ) -var ( - _ orchestrator = (*GradualOrchestrator[any])(nil) - - ErrFailedToReachTargetTPS = errors.New("failed to reach target TPS") -) +var ErrFailedToReachTargetTPS = errors.New("failed to reach target TPS") type GradualOrchestratorConfig struct { // The maximum TPS the orchestrator should aim for. From 3ba6372708c10627b3b6c5803c6d3d447d71dfa7 Mon Sep 17 00:00:00 2001 From: Rodrigo Villar Date: Fri, 9 May 2025 15:25:37 -0400 Subject: [PATCH 082/197] rm dependencies.go --- tests/load/dependencies.go | 26 -------------------------- tests/load/gradual_orchestrator.go | 20 ++++++++++++++++++++ 2 files changed, 20 insertions(+), 26 deletions(-) delete mode 100644 tests/load/dependencies.go diff --git a/tests/load/dependencies.go b/tests/load/dependencies.go deleted file mode 100644 index c0b3843911b4..000000000000 --- a/tests/load/dependencies.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package load - -import "context" - -type Issuer[T any] interface { - // GenerateAndIssueTx generates and sends a tx to the network, and informs the - // tracker that it sent said transaction. It returns the sent transaction. - GenerateAndIssueTx(ctx context.Context) (T, error) -} - -type Listener[T any] interface { - // Listen for the final status of transactions and notify the tracker - // Listen stops if the context is done, an error occurs, or if it received - // all the transactions issued and the issuer no longer issues any. - // Listen MUST return a nil error if the context is canceled. - Listen(ctx context.Context) error - - // RegisterIssued informs the listener that a transaction was issued. - RegisterIssued(tx T) - - // IssuingDone informs the listener that no more transactions will be issued. - IssuingDone() -} diff --git a/tests/load/gradual_orchestrator.go b/tests/load/gradual_orchestrator.go index f65d589d9d93..eb2c68225162 100644 --- a/tests/load/gradual_orchestrator.go +++ b/tests/load/gradual_orchestrator.go @@ -18,6 +18,26 @@ import ( var ErrFailedToReachTargetTPS = errors.New("failed to reach target TPS") +type Issuer[T any] interface { + // GenerateAndIssueTx generates and sends a tx to the network, and informs the + // tracker that it sent said transaction. It returns the sent transaction. + GenerateAndIssueTx(ctx context.Context) (T, error) +} + +type Listener[T any] interface { + // Listen for the final status of transactions and notify the tracker + // Listen stops if the context is done, an error occurs, or if it received + // all the transactions issued and the issuer no longer issues any. + // Listen MUST return a nil error if the context is canceled. + Listen(ctx context.Context) error + + // RegisterIssued informs the listener that a transaction was issued. + RegisterIssued(tx T) + + // IssuingDone informs the listener that no more transactions will be issued. + IssuingDone() +} + type GradualOrchestratorConfig struct { // The maximum TPS the orchestrator should aim for. // From a35ceef16d9fe2c734f756a3dd43da9efb17df32 Mon Sep 17 00:00:00 2001 From: Rodrigo Villar Date: Fri, 9 May 2025 15:28:02 -0400 Subject: [PATCH 083/197] rename gradual orchestrator to just orchestrator --- ...radual_orchestrator.go => orchestrator.go} | 32 +++++++++---------- ...hestrator_test.go => orchestrator_test.go} | 16 +++++----- 2 files changed, 24 insertions(+), 24 deletions(-) rename tests/load/{gradual_orchestrator.go => orchestrator.go} (90%) rename tests/load/{gradual_orchestrator_test.go => orchestrator_test.go} (92%) diff --git a/tests/load/gradual_orchestrator.go b/tests/load/orchestrator.go similarity index 90% rename from tests/load/gradual_orchestrator.go rename to tests/load/orchestrator.go index eb2c68225162..506e7d431a1f 100644 --- a/tests/load/gradual_orchestrator.go +++ b/tests/load/orchestrator.go @@ -38,7 +38,7 @@ type Listener[T any] interface { IssuingDone() } -type GradualOrchestratorConfig struct { +type OrchestratorConfig struct { // The maximum TPS the orchestrator should aim for. // // If set to -1, the orchestrator will behave in a burst fashion, instead @@ -71,8 +71,8 @@ type GradualOrchestratorConfig struct { Terminate bool } -func NewGradualOrchestratorConfig() GradualOrchestratorConfig { - return GradualOrchestratorConfig{ +func NewOrchestratorConfig() OrchestratorConfig { + return OrchestratorConfig{ MaxTPS: 5_000, MinTPS: 1_000, Step: 1_000, @@ -83,11 +83,11 @@ func NewGradualOrchestratorConfig() GradualOrchestratorConfig { } } -// GradualOrchestrator tests the network by continuously sending +// Orchestrator tests the network by continuously sending // transactions at a given rate (currTargetTPS) and increasing that rate until it detects that // the network can no longer make progress (i.e. the rate at the network accepts // transactions is less than currTargetTPS). -type GradualOrchestrator[T comparable] struct { +type Orchestrator[T comparable] struct { agents []Agent[T] tracker *Tracker[T] @@ -98,16 +98,16 @@ type GradualOrchestrator[T comparable] struct { observerGroup *errgroup.Group issuerGroup *errgroup.Group - config GradualOrchestratorConfig + config OrchestratorConfig } -func NewGradualOrchestrator[T comparable]( +func NewOrchestrator[T comparable]( agents []Agent[T], tracker *Tracker[T], log logging.Logger, - config GradualOrchestratorConfig, -) *GradualOrchestrator[T] { - return &GradualOrchestrator[T]{ + config OrchestratorConfig, +) *Orchestrator[T] { + return &Orchestrator[T]{ agents: agents, tracker: tracker, log: log, @@ -115,7 +115,7 @@ func NewGradualOrchestrator[T comparable]( } } -func (o *GradualOrchestrator[T]) Execute(ctx context.Context) error { +func (o *Orchestrator[T]) Execute(ctx context.Context) error { ctx, cancel := context.WithCancel(ctx) // start a goroutine to confirm each issuer's transactions @@ -139,7 +139,7 @@ func (o *GradualOrchestrator[T]) Execute(ctx context.Context) error { return errors.Join(o.issuerGroup.Wait(), o.observerGroup.Wait(), err) } -// run the gradual load test by continuously increasing the rate at which +// run the load test by continuously increasing the rate at which // transactions are sent // // run blocks until one of the following conditions is met: @@ -147,7 +147,7 @@ func (o *GradualOrchestrator[T]) Execute(ctx context.Context) error { // 1. an issuer has errored // 2. the max TPS target has been reached and we can terminate // 3. the maximum number of attempts to reach a target TPS has been reached -func (o *GradualOrchestrator[T]) run(ctx context.Context) bool { +func (o *Orchestrator[T]) run(ctx context.Context) bool { var ( prevConfirmed = o.tracker.GetObservedConfirmed() prevTime = time.Now() @@ -245,13 +245,13 @@ func (o *GradualOrchestrator[T]) run(ctx context.Context) bool { } // GetObservedIssued returns the max TPS the orchestrator observed -func (o *GradualOrchestrator[T]) GetMaxObservedTPS() uint64 { +func (o *Orchestrator[T]) GetMaxObservedTPS() uint64 { return o.maxObservedTPS.Load() } // start a goroutine to each issuer to continuously send transactions. // if an issuer errors, all other issuers will stop as well. -func (o *GradualOrchestrator[T]) issueTxs(ctx context.Context, currTargetTPS *atomic.Int64) { +func (o *Orchestrator[T]) issueTxs(ctx context.Context, currTargetTPS *atomic.Int64) { for _, agent := range o.agents { o.issuerGroup.Go(func() error { for { @@ -291,7 +291,7 @@ func (o *GradualOrchestrator[T]) issueTxs(ctx context.Context, currTargetTPS *at } // setMaxObservedTPS only if tps > the current max observed TPS. -func (o *GradualOrchestrator[T]) setMaxObservedTPS(tps uint64) { +func (o *Orchestrator[T]) setMaxObservedTPS(tps uint64) { if tps > o.maxObservedTPS.Load() { o.maxObservedTPS.Store(tps) } diff --git a/tests/load/gradual_orchestrator_test.go b/tests/load/orchestrator_test.go similarity index 92% rename from tests/load/gradual_orchestrator_test.go rename to tests/load/orchestrator_test.go index 071f31a88df3..fa66aa1cf8df 100644 --- a/tests/load/gradual_orchestrator_test.go +++ b/tests/load/orchestrator_test.go @@ -19,17 +19,17 @@ import ( var _ Issuer[ids.ID] = (*mockIssuer)(nil) -func TestGradualOrchestratorTPS(t *testing.T) { +func TestOrchestratorTPS(t *testing.T) { tests := []struct { name string serverTPS uint64 - config GradualOrchestratorConfig + config OrchestratorConfig expectedErr error }{ { name: "orchestrator achieves max TPS", serverTPS: math.MaxUint64, - config: GradualOrchestratorConfig{ + config: OrchestratorConfig{ MaxTPS: 2_000, MinTPS: 1_000, Step: 1_000, @@ -42,7 +42,7 @@ func TestGradualOrchestratorTPS(t *testing.T) { { name: "orchestrator TPS limited by network", serverTPS: 1_000, - config: GradualOrchestratorConfig{ + config: OrchestratorConfig{ MaxTPS: 2_000, MinTPS: 1_000, Step: 1_000, @@ -77,7 +77,7 @@ func TestGradualOrchestratorTPS(t *testing.T) { ), } - orchestrator := NewGradualOrchestrator( + orchestrator := NewOrchestrator( agents, tracker, logging.NoLog{}, @@ -96,7 +96,7 @@ func TestGradualOrchestratorTPS(t *testing.T) { } // test that the orchestrator returns early if the txGenerators or the issuers error -func TestGradualOrchestratorExecution(t *testing.T) { +func TestOrchestratorExecution(t *testing.T) { var ( errMockTxGenerator = errors.New("mock tx generator error") errMockIssuer = errors.New("mock issuer error") @@ -147,11 +147,11 @@ func TestGradualOrchestratorExecution(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - orchestrator := NewGradualOrchestrator( + orchestrator := NewOrchestrator( tt.agents, tracker, logging.NoLog{}, - NewGradualOrchestratorConfig(), + NewOrchestratorConfig(), ) r.NoError(err) From 9f9eebd66a8e12a1fee03f2a2beae526b358e181 Mon Sep 17 00:00:00 2001 From: Rodrigo Villar Date: Fri, 9 May 2025 15:33:15 -0400 Subject: [PATCH 084/197] add doc for NewOrchestratorConfig() --- tests/load/orchestrator.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/load/orchestrator.go b/tests/load/orchestrator.go index 506e7d431a1f..4812b278cd78 100644 --- a/tests/load/orchestrator.go +++ b/tests/load/orchestrator.go @@ -71,6 +71,8 @@ type OrchestratorConfig struct { Terminate bool } +// NewOrchestratorConfig returns a default OrchestratorConfig with pre-set parameters +// for gradual load testing. func NewOrchestratorConfig() OrchestratorConfig { return OrchestratorConfig{ MaxTPS: 5_000, From c1e5aced83ebb66cae48815eb60d3cd72d82ae63 Mon Sep 17 00:00:00 2001 From: Rodrigo Villar Date: Fri, 9 May 2025 15:59:17 -0400 Subject: [PATCH 085/197] use int64 throughout orchestrator --- tests/load/agent.go | 16 ++++++------- tests/load/orchestrator.go | 40 ++++++++++++++++----------------- tests/load/orchestrator_test.go | 24 ++++++++++---------- tests/load/tracker.go | 32 +++++++++++++------------- 4 files changed, 56 insertions(+), 56 deletions(-) diff --git a/tests/load/agent.go b/tests/load/agent.go index 1af369cae9c7..d170e0a596d1 100644 --- a/tests/load/agent.go +++ b/tests/load/agent.go @@ -3,16 +3,16 @@ package load -type Agent[T any] struct { - Issuer Issuer[T] - Listener Listener[T] +type Agent struct { + Issuer Issuer + Listener Listener } -func NewAgent[T any]( - issuer Issuer[T], - listener Listener[T], -) Agent[T] { - return Agent[T]{ +func NewAgent( + issuer Issuer, + listener Listener, +) Agent { + return Agent{ Issuer: issuer, Listener: listener, } diff --git a/tests/load/orchestrator.go b/tests/load/orchestrator.go index 4812b278cd78..62c6c0d0bbab 100644 --- a/tests/load/orchestrator.go +++ b/tests/load/orchestrator.go @@ -18,13 +18,13 @@ import ( var ErrFailedToReachTargetTPS = errors.New("failed to reach target TPS") -type Issuer[T any] interface { +type Issuer interface { // GenerateAndIssueTx generates and sends a tx to the network, and informs the // tracker that it sent said transaction. It returns the sent transaction. - GenerateAndIssueTx(ctx context.Context) (T, error) + GenerateAndIssueTx(ctx context.Context) ([32]byte, error) } -type Listener[T any] interface { +type Listener interface { // Listen for the final status of transactions and notify the tracker // Listen stops if the context is done, an error occurs, or if it received // all the transactions issued and the issuer no longer issues any. @@ -32,7 +32,7 @@ type Listener[T any] interface { Listen(ctx context.Context) error // RegisterIssued informs the listener that a transaction was issued. - RegisterIssued(tx T) + RegisterIssued(txID [32]byte) // IssuingDone informs the listener that no more transactions will be issued. IssuingDone() @@ -89,13 +89,13 @@ func NewOrchestratorConfig() OrchestratorConfig { // transactions at a given rate (currTargetTPS) and increasing that rate until it detects that // the network can no longer make progress (i.e. the rate at the network accepts // transactions is less than currTargetTPS). -type Orchestrator[T comparable] struct { - agents []Agent[T] - tracker *Tracker[T] +type Orchestrator struct { + agents []Agent + tracker *Tracker log logging.Logger - maxObservedTPS atomic.Uint64 + maxObservedTPS atomic.Int64 observerGroup *errgroup.Group issuerGroup *errgroup.Group @@ -103,13 +103,13 @@ type Orchestrator[T comparable] struct { config OrchestratorConfig } -func NewOrchestrator[T comparable]( - agents []Agent[T], - tracker *Tracker[T], +func NewOrchestrator( + agents []Agent, + tracker *Tracker, log logging.Logger, config OrchestratorConfig, -) *Orchestrator[T] { - return &Orchestrator[T]{ +) *Orchestrator { + return &Orchestrator{ agents: agents, tracker: tracker, log: log, @@ -117,7 +117,7 @@ func NewOrchestrator[T comparable]( } } -func (o *Orchestrator[T]) Execute(ctx context.Context) error { +func (o *Orchestrator) Execute(ctx context.Context) error { ctx, cancel := context.WithCancel(ctx) // start a goroutine to confirm each issuer's transactions @@ -149,7 +149,7 @@ func (o *Orchestrator[T]) Execute(ctx context.Context) error { // 1. an issuer has errored // 2. the max TPS target has been reached and we can terminate // 3. the maximum number of attempts to reach a target TPS has been reached -func (o *Orchestrator[T]) run(ctx context.Context) bool { +func (o *Orchestrator) run(ctx context.Context) bool { var ( prevConfirmed = o.tracker.GetObservedConfirmed() prevTime = time.Now() @@ -185,7 +185,7 @@ func (o *Orchestrator[T]) run(ctx context.Context) bool { } tps := computeTPS(prevConfirmed, currConfirmed, currTime.Sub(prevTime)) - o.setMaxObservedTPS(tps) + o.setMaxObservedTPS(int64(tps)) // if max TPS target has been reached and we don't terminate, then continue here // so we do not keep increasing the target TPS @@ -193,7 +193,7 @@ func (o *Orchestrator[T]) run(ctx context.Context) bool { o.log.Info( "current network state", zap.Uint64("current TPS", tps), - zap.Uint64("max observed TPS", o.maxObservedTPS.Load()), + zap.Int64("max observed TPS", o.maxObservedTPS.Load()), ) continue } @@ -247,13 +247,13 @@ func (o *Orchestrator[T]) run(ctx context.Context) bool { } // GetObservedIssued returns the max TPS the orchestrator observed -func (o *Orchestrator[T]) GetMaxObservedTPS() uint64 { +func (o *Orchestrator) GetMaxObservedTPS() int64 { return o.maxObservedTPS.Load() } // start a goroutine to each issuer to continuously send transactions. // if an issuer errors, all other issuers will stop as well. -func (o *Orchestrator[T]) issueTxs(ctx context.Context, currTargetTPS *atomic.Int64) { +func (o *Orchestrator) issueTxs(ctx context.Context, currTargetTPS *atomic.Int64) { for _, agent := range o.agents { o.issuerGroup.Go(func() error { for { @@ -293,7 +293,7 @@ func (o *Orchestrator[T]) issueTxs(ctx context.Context, currTargetTPS *atomic.In } // setMaxObservedTPS only if tps > the current max observed TPS. -func (o *Orchestrator[T]) setMaxObservedTPS(tps uint64) { +func (o *Orchestrator) setMaxObservedTPS(tps int64) { if tps > o.maxObservedTPS.Load() { o.maxObservedTPS.Store(tps) } diff --git a/tests/load/orchestrator_test.go b/tests/load/orchestrator_test.go index fa66aa1cf8df..674a1e674156 100644 --- a/tests/load/orchestrator_test.go +++ b/tests/load/orchestrator_test.go @@ -17,7 +17,7 @@ import ( "github.com/ava-labs/avalanchego/utils/logging" ) -var _ Issuer[ids.ID] = (*mockIssuer)(nil) +var _ Issuer = (*mockIssuer)(nil) func TestOrchestratorTPS(t *testing.T) { tests := []struct { @@ -61,10 +61,10 @@ func TestOrchestratorTPS(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - tracker, err := NewTracker[ids.ID](prometheus.NewRegistry()) + tracker, err := NewTracker(prometheus.NewRegistry()) r.NoError(err) - agents := []Agent[ids.ID]{ + agents := []Agent{ NewAgent( &mockIssuer{ generateTxF: func() (ids.ID, error) { @@ -102,18 +102,18 @@ func TestOrchestratorExecution(t *testing.T) { errMockIssuer = errors.New("mock issuer error") ) - tracker, err := NewTracker[ids.ID](prometheus.NewRegistry()) + tracker, err := NewTracker(prometheus.NewRegistry()) require.NoError(t, err, "creating tracker") tests := []struct { name string - agents []Agent[ids.ID] + agents []Agent expectedErr error }{ { name: "generator error", - agents: []Agent[ids.ID]{ - NewAgent[ids.ID]( + agents: []Agent{ + NewAgent( &mockIssuer{ generateTxF: func() (ids.ID, error) { return ids.Empty, errMockTxGenerator @@ -126,8 +126,8 @@ func TestOrchestratorExecution(t *testing.T) { }, { name: "issuer error", - agents: []Agent[ids.ID]{ - NewAgent[ids.ID]( + agents: []Agent{ + NewAgent( &mockIssuer{ generateTxF: func() (ids.ID, error) { return ids.GenerateTestID(), nil @@ -164,13 +164,13 @@ type mockIssuer struct { generateTxF func() (ids.ID, error) currTxsIssued uint64 maxTxs uint64 - tracker *Tracker[ids.ID] + tracker *Tracker issueTxErr error } // GenerateAndIssueTx immediately generates, issues and confirms a tx. // To simulate TPS, the number of txs IssueTx can issue/confirm is capped by maxTxs -func (m *mockIssuer) GenerateAndIssueTx(_ context.Context) (ids.ID, error) { +func (m *mockIssuer) GenerateAndIssueTx(_ context.Context) ([32]byte, error) { id, err := m.generateTxF() if err != nil { return id, err @@ -196,6 +196,6 @@ func (*mockListener) Listen(context.Context) error { return nil } -func (*mockListener) RegisterIssued(ids.ID) {} +func (*mockListener) RegisterIssued([32]byte) {} func (*mockListener) IssuingDone() {} diff --git a/tests/load/tracker.go b/tests/load/tracker.go index b05bb16055d2..a0c640d190bc 100644 --- a/tests/load/tracker.go +++ b/tests/load/tracker.go @@ -16,10 +16,10 @@ const namespace = "load" // Tracker keeps track of the status of transactions. // This is thread-safe and can be called in parallel by the issuer(s) or orchestrator. -type Tracker[T comparable] struct { +type Tracker struct { lock sync.RWMutex - outstandingTxs map[T]time.Time + outstandingTxs map[[32]byte]time.Time txsIssued uint64 txsConfirmed uint64 @@ -35,9 +35,9 @@ type Tracker[T comparable] struct { // NewTracker returns a new Tracker instance which records metrics for the number // of transactions issued, confirmed, and failed. It also tracks the latency of // transactions. -func NewTracker[T comparable](reg *prometheus.Registry) (*Tracker[T], error) { - tracker := &Tracker[T]{ - outstandingTxs: make(map[T]time.Time), +func NewTracker(reg *prometheus.Registry) (*Tracker, error) { + tracker := &Tracker{ + outstandingTxs: make(map[[32]byte]time.Time), txsIssuedCounter: prometheus.NewCounter(prometheus.CounterOpts{ Namespace: namespace, Name: "txs_issued", @@ -72,7 +72,7 @@ func NewTracker[T comparable](reg *prometheus.Registry) (*Tracker[T], error) { // GetObservedConfirmed returns the number of transactions that the tracker has // confirmed were accepted. -func (p *Tracker[T]) GetObservedConfirmed() uint64 { +func (p *Tracker) GetObservedConfirmed() uint64 { p.lock.RLock() defer p.lock.RUnlock() @@ -81,7 +81,7 @@ func (p *Tracker[T]) GetObservedConfirmed() uint64 { // GetObservedFailed returns the number of transactions that the tracker has // confirmed failed. -func (p *Tracker[T]) GetObservedFailed() uint64 { +func (p *Tracker) GetObservedFailed() uint64 { p.lock.RLock() defer p.lock.RUnlock() @@ -90,7 +90,7 @@ func (p *Tracker[T]) GetObservedFailed() uint64 { // GetObservedIssued returns the number of transactions that the tracker has // confirmed were issued. -func (p *Tracker[T]) GetObservedIssued() uint64 { +func (p *Tracker) GetObservedIssued() uint64 { p.lock.RLock() defer p.lock.RUnlock() @@ -99,22 +99,22 @@ func (p *Tracker[T]) GetObservedIssued() uint64 { // Issue records a transaction that was submitted, but whose final status is // not yet known. -func (p *Tracker[T]) Issue(tx T) { +func (p *Tracker) Issue(txID [32]byte) { p.lock.Lock() defer p.lock.Unlock() - p.outstandingTxs[tx] = time.Now() + p.outstandingTxs[txID] = time.Now() p.txsIssued++ p.txsIssuedCounter.Inc() } // ObserveConfirmed records a transaction that was confirmed. -func (p *Tracker[T]) ObserveConfirmed(tx T) { +func (p *Tracker) ObserveConfirmed(txID [32]byte) { p.lock.Lock() defer p.lock.Unlock() - startTime := p.outstandingTxs[tx] - delete(p.outstandingTxs, tx) + startTime := p.outstandingTxs[txID] + delete(p.outstandingTxs, txID) p.txsConfirmed++ p.txsConfirmedCounter.Inc() @@ -122,12 +122,12 @@ func (p *Tracker[T]) ObserveConfirmed(tx T) { } // ObserveFailed records a transaction that failed (e.g. expired) -func (p *Tracker[T]) ObserveFailed(tx T) { +func (p *Tracker) ObserveFailed(txID [32]byte) { p.lock.Lock() defer p.lock.Unlock() - startTime := p.outstandingTxs[tx] - delete(p.outstandingTxs, tx) + startTime := p.outstandingTxs[txID] + delete(p.outstandingTxs, txID) p.txsFailed++ p.txsFailedCounter.Inc() From 8549ec25d255b70a4815585e5c58b585c5c77522 Mon Sep 17 00:00:00 2001 From: Rodrigo Villar Date: Fri, 9 May 2025 16:22:01 -0400 Subject: [PATCH 086/197] use type constraint for txIDs --- tests/load/agent.go | 16 +++++++------- tests/load/orchestrator.go | 38 ++++++++++++++++++--------------- tests/load/orchestrator_test.go | 20 ++++++++--------- tests/load/tracker.go | 22 +++++++++---------- 4 files changed, 50 insertions(+), 46 deletions(-) diff --git a/tests/load/agent.go b/tests/load/agent.go index d170e0a596d1..586008e57fc8 100644 --- a/tests/load/agent.go +++ b/tests/load/agent.go @@ -3,16 +3,16 @@ package load -type Agent struct { - Issuer Issuer - Listener Listener +type Agent[T TxID] struct { + Issuer Issuer[T] + Listener Listener[T] } -func NewAgent( - issuer Issuer, - listener Listener, -) Agent { - return Agent{ +func NewAgent[T TxID]( + issuer Issuer[T], + listener Listener[T], +) Agent[T] { + return Agent[T]{ Issuer: issuer, Listener: listener, } diff --git a/tests/load/orchestrator.go b/tests/load/orchestrator.go index 62c6c0d0bbab..c3309fdf365d 100644 --- a/tests/load/orchestrator.go +++ b/tests/load/orchestrator.go @@ -18,13 +18,17 @@ import ( var ErrFailedToReachTargetTPS = errors.New("failed to reach target TPS") -type Issuer interface { +type TxID interface { + ~[32]byte +} + +type Issuer[T TxID] interface { // GenerateAndIssueTx generates and sends a tx to the network, and informs the // tracker that it sent said transaction. It returns the sent transaction. - GenerateAndIssueTx(ctx context.Context) ([32]byte, error) + GenerateAndIssueTx(ctx context.Context) (T, error) } -type Listener interface { +type Listener[T TxID] interface { // Listen for the final status of transactions and notify the tracker // Listen stops if the context is done, an error occurs, or if it received // all the transactions issued and the issuer no longer issues any. @@ -32,7 +36,7 @@ type Listener interface { Listen(ctx context.Context) error // RegisterIssued informs the listener that a transaction was issued. - RegisterIssued(txID [32]byte) + RegisterIssued(txID T) // IssuingDone informs the listener that no more transactions will be issued. IssuingDone() @@ -89,9 +93,9 @@ func NewOrchestratorConfig() OrchestratorConfig { // transactions at a given rate (currTargetTPS) and increasing that rate until it detects that // the network can no longer make progress (i.e. the rate at the network accepts // transactions is less than currTargetTPS). -type Orchestrator struct { - agents []Agent - tracker *Tracker +type Orchestrator[T TxID] struct { + agents []Agent[T] + tracker *Tracker[T] log logging.Logger @@ -103,13 +107,13 @@ type Orchestrator struct { config OrchestratorConfig } -func NewOrchestrator( - agents []Agent, - tracker *Tracker, +func NewOrchestrator[T TxID]( + agents []Agent[T], + tracker *Tracker[T], log logging.Logger, config OrchestratorConfig, -) *Orchestrator { - return &Orchestrator{ +) *Orchestrator[T] { + return &Orchestrator[T]{ agents: agents, tracker: tracker, log: log, @@ -117,7 +121,7 @@ func NewOrchestrator( } } -func (o *Orchestrator) Execute(ctx context.Context) error { +func (o *Orchestrator[_]) Execute(ctx context.Context) error { ctx, cancel := context.WithCancel(ctx) // start a goroutine to confirm each issuer's transactions @@ -149,7 +153,7 @@ func (o *Orchestrator) Execute(ctx context.Context) error { // 1. an issuer has errored // 2. the max TPS target has been reached and we can terminate // 3. the maximum number of attempts to reach a target TPS has been reached -func (o *Orchestrator) run(ctx context.Context) bool { +func (o *Orchestrator[_]) run(ctx context.Context) bool { var ( prevConfirmed = o.tracker.GetObservedConfirmed() prevTime = time.Now() @@ -247,13 +251,13 @@ func (o *Orchestrator) run(ctx context.Context) bool { } // GetObservedIssued returns the max TPS the orchestrator observed -func (o *Orchestrator) GetMaxObservedTPS() int64 { +func (o *Orchestrator[_]) GetMaxObservedTPS() int64 { return o.maxObservedTPS.Load() } // start a goroutine to each issuer to continuously send transactions. // if an issuer errors, all other issuers will stop as well. -func (o *Orchestrator) issueTxs(ctx context.Context, currTargetTPS *atomic.Int64) { +func (o *Orchestrator[_]) issueTxs(ctx context.Context, currTargetTPS *atomic.Int64) { for _, agent := range o.agents { o.issuerGroup.Go(func() error { for { @@ -293,7 +297,7 @@ func (o *Orchestrator) issueTxs(ctx context.Context, currTargetTPS *atomic.Int64 } // setMaxObservedTPS only if tps > the current max observed TPS. -func (o *Orchestrator) setMaxObservedTPS(tps int64) { +func (o *Orchestrator[_]) setMaxObservedTPS(tps int64) { if tps > o.maxObservedTPS.Load() { o.maxObservedTPS.Store(tps) } diff --git a/tests/load/orchestrator_test.go b/tests/load/orchestrator_test.go index 674a1e674156..1db7374a9983 100644 --- a/tests/load/orchestrator_test.go +++ b/tests/load/orchestrator_test.go @@ -17,7 +17,7 @@ import ( "github.com/ava-labs/avalanchego/utils/logging" ) -var _ Issuer = (*mockIssuer)(nil) +var _ Issuer[ids.ID] = (*mockIssuer)(nil) func TestOrchestratorTPS(t *testing.T) { tests := []struct { @@ -61,10 +61,10 @@ func TestOrchestratorTPS(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - tracker, err := NewTracker(prometheus.NewRegistry()) + tracker, err := NewTracker[ids.ID](prometheus.NewRegistry()) r.NoError(err) - agents := []Agent{ + agents := []Agent[ids.ID]{ NewAgent( &mockIssuer{ generateTxF: func() (ids.ID, error) { @@ -102,17 +102,17 @@ func TestOrchestratorExecution(t *testing.T) { errMockIssuer = errors.New("mock issuer error") ) - tracker, err := NewTracker(prometheus.NewRegistry()) + tracker, err := NewTracker[ids.ID](prometheus.NewRegistry()) require.NoError(t, err, "creating tracker") tests := []struct { name string - agents []Agent + agents []Agent[ids.ID] expectedErr error }{ { name: "generator error", - agents: []Agent{ + agents: []Agent[ids.ID]{ NewAgent( &mockIssuer{ generateTxF: func() (ids.ID, error) { @@ -126,7 +126,7 @@ func TestOrchestratorExecution(t *testing.T) { }, { name: "issuer error", - agents: []Agent{ + agents: []Agent[ids.ID]{ NewAgent( &mockIssuer{ generateTxF: func() (ids.ID, error) { @@ -164,13 +164,13 @@ type mockIssuer struct { generateTxF func() (ids.ID, error) currTxsIssued uint64 maxTxs uint64 - tracker *Tracker + tracker *Tracker[ids.ID] issueTxErr error } // GenerateAndIssueTx immediately generates, issues and confirms a tx. // To simulate TPS, the number of txs IssueTx can issue/confirm is capped by maxTxs -func (m *mockIssuer) GenerateAndIssueTx(_ context.Context) ([32]byte, error) { +func (m *mockIssuer) GenerateAndIssueTx(_ context.Context) (ids.ID, error) { id, err := m.generateTxF() if err != nil { return id, err @@ -196,6 +196,6 @@ func (*mockListener) Listen(context.Context) error { return nil } -func (*mockListener) RegisterIssued([32]byte) {} +func (*mockListener) RegisterIssued(ids.ID) {} func (*mockListener) IssuingDone() {} diff --git a/tests/load/tracker.go b/tests/load/tracker.go index a0c640d190bc..d64713db74e0 100644 --- a/tests/load/tracker.go +++ b/tests/load/tracker.go @@ -16,10 +16,10 @@ const namespace = "load" // Tracker keeps track of the status of transactions. // This is thread-safe and can be called in parallel by the issuer(s) or orchestrator. -type Tracker struct { +type Tracker[T TxID] struct { lock sync.RWMutex - outstandingTxs map[[32]byte]time.Time + outstandingTxs map[T]time.Time txsIssued uint64 txsConfirmed uint64 @@ -35,9 +35,9 @@ type Tracker struct { // NewTracker returns a new Tracker instance which records metrics for the number // of transactions issued, confirmed, and failed. It also tracks the latency of // transactions. -func NewTracker(reg *prometheus.Registry) (*Tracker, error) { - tracker := &Tracker{ - outstandingTxs: make(map[[32]byte]time.Time), +func NewTracker[T TxID](reg *prometheus.Registry) (*Tracker[T], error) { + tracker := &Tracker[T]{ + outstandingTxs: make(map[T]time.Time), txsIssuedCounter: prometheus.NewCounter(prometheus.CounterOpts{ Namespace: namespace, Name: "txs_issued", @@ -72,7 +72,7 @@ func NewTracker(reg *prometheus.Registry) (*Tracker, error) { // GetObservedConfirmed returns the number of transactions that the tracker has // confirmed were accepted. -func (p *Tracker) GetObservedConfirmed() uint64 { +func (p *Tracker[_]) GetObservedConfirmed() uint64 { p.lock.RLock() defer p.lock.RUnlock() @@ -81,7 +81,7 @@ func (p *Tracker) GetObservedConfirmed() uint64 { // GetObservedFailed returns the number of transactions that the tracker has // confirmed failed. -func (p *Tracker) GetObservedFailed() uint64 { +func (p *Tracker[_]) GetObservedFailed() uint64 { p.lock.RLock() defer p.lock.RUnlock() @@ -90,7 +90,7 @@ func (p *Tracker) GetObservedFailed() uint64 { // GetObservedIssued returns the number of transactions that the tracker has // confirmed were issued. -func (p *Tracker) GetObservedIssued() uint64 { +func (p *Tracker[_]) GetObservedIssued() uint64 { p.lock.RLock() defer p.lock.RUnlock() @@ -99,7 +99,7 @@ func (p *Tracker) GetObservedIssued() uint64 { // Issue records a transaction that was submitted, but whose final status is // not yet known. -func (p *Tracker) Issue(txID [32]byte) { +func (p *Tracker[T]) Issue(txID T) { p.lock.Lock() defer p.lock.Unlock() @@ -109,7 +109,7 @@ func (p *Tracker) Issue(txID [32]byte) { } // ObserveConfirmed records a transaction that was confirmed. -func (p *Tracker) ObserveConfirmed(txID [32]byte) { +func (p *Tracker[T]) ObserveConfirmed(txID T) { p.lock.Lock() defer p.lock.Unlock() @@ -122,7 +122,7 @@ func (p *Tracker) ObserveConfirmed(txID [32]byte) { } // ObserveFailed records a transaction that failed (e.g. expired) -func (p *Tracker) ObserveFailed(txID [32]byte) { +func (p *Tracker[T]) ObserveFailed(txID T) { p.lock.Lock() defer p.lock.Unlock() From d521e30703abdcadad8524bbf00a7a7163d59021 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Mon, 12 May 2025 17:27:09 +0200 Subject: [PATCH 087/197] Unexport Distribute --- tests/load/c/distribute.go | 4 ++-- tests/load/c/execute.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/load/c/distribute.go b/tests/load/c/distribute.go index c9dd2b1d8333..43928348baf6 100644 --- a/tests/load/c/distribute.go +++ b/tests/load/c/distribute.go @@ -18,11 +18,11 @@ import ( ethcrypto "github.com/ava-labs/libevm/crypto" ) -// Distribute distributes as close to equally the funds for each given key. +// distribute distributes as close to equally the funds for each given key. // It is not exact because of the gas fees for each transaction, so a minimum // balance required for each key is calculated as follows: // minBalance = averageBalance - (numberOfKeys * txCost) -func Distribute(ctx context.Context, endpoint string, keys []*ecdsa.PrivateKey) error { +func distribute(ctx context.Context, endpoint string, keys []*ecdsa.PrivateKey) error { client, err := ethclient.DialContext(ctx, endpoint) if err != nil { return fmt.Errorf("dialing %s: %w", endpoint, err) diff --git a/tests/load/c/execute.go b/tests/load/c/execute.go index aa7622add092..5c9a3ce2cb98 100644 --- a/tests/load/c/execute.go +++ b/tests/load/c/execute.go @@ -41,7 +41,7 @@ func execute(ctx context.Context, preFundedKeys []*secp256k1.PrivateKey, config return fmt.Errorf("fixing keys count: %w", err) } - err = Distribute(ctx, config.endpoints[0], keys) + err = distribute(ctx, config.endpoints[0], keys) if err != nil { return fmt.Errorf("ensuring minimum funds: %w", err) } @@ -68,7 +68,7 @@ func execute(ctx context.Context, preFundedKeys []*secp256k1.PrivateKey, config return fmt.Errorf("getting nonce for address %s: %w", address, err) } - issuer, err := issuers.NewSimple(ctx, client, tracker, + issuer, err := issuers.NewOpcoder(ctx, client, tracker, nonce, big.NewInt(config.maxFeeCap), keys[i]) if err != nil { return fmt.Errorf("creating issuer: %w", err) From 8113fd24b9ba1993d4d41ec554db999a3735f76d Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Mon, 12 May 2025 17:27:28 +0200 Subject: [PATCH 088/197] Update dashboard --- tests/load/c/dashboard.json | 134 ++++++++++++++++++++++++++++++------ 1 file changed, 113 insertions(+), 21 deletions(-) diff --git a/tests/load/c/dashboard.json b/tests/load/c/dashboard.json index 2a30ce69a7d4..7c6ad12fa1d8 100644 --- a/tests/load/c/dashboard.json +++ b/tests/load/c/dashboard.json @@ -21,13 +21,105 @@ "id": 1, "links": [], "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "dekh3fflmqzggf" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 25, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.6.1", + "targets": [ + { + "editorMode": "code", + "expr": "rate(avalanche_evm_eth_chain_txs_processed[30s])", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Panel Title", + "type": "timeseries" + }, { "collapsed": false, "gridPos": { "h": 1, "w": 24, "x": 0, - "y": 0 + "y": 8 }, "id": 19, "panels": [], @@ -60,7 +152,7 @@ "h": 4, "w": 4, "x": 0, - "y": 1 + "y": 9 }, "id": 18, "options": { @@ -122,7 +214,7 @@ "h": 4, "w": 3, "x": 4, - "y": 1 + "y": 9 }, "id": 13, "options": { @@ -161,7 +253,7 @@ "h": 1, "w": 24, "x": 0, - "y": 5 + "y": 13 }, "id": 20, "panels": [], @@ -201,7 +293,7 @@ "h": 4, "w": 4, "x": 0, - "y": 6 + "y": 14 }, "id": 9, "options": { @@ -267,7 +359,7 @@ "h": 4, "w": 4, "x": 4, - "y": 6 + "y": 14 }, "id": 7, "options": { @@ -333,7 +425,7 @@ "h": 4, "w": 4, "x": 8, - "y": 6 + "y": 14 }, "id": 8, "options": { @@ -399,7 +491,7 @@ "h": 4, "w": 4, "x": 12, - "y": 6 + "y": 14 }, "id": 10, "options": { @@ -438,7 +530,7 @@ "h": 1, "w": 24, "x": 0, - "y": 10 + "y": 18 }, "id": 22, "panels": [], @@ -474,7 +566,7 @@ "h": 4, "w": 4, "x": 0, - "y": 11 + "y": 19 }, "id": 14, "options": { @@ -536,7 +628,7 @@ "h": 4, "w": 4, "x": 4, - "y": 11 + "y": 19 }, "id": 16, "options": { @@ -598,7 +690,7 @@ "h": 4, "w": 4, "x": 8, - "y": 11 + "y": 19 }, "id": 23, "options": { @@ -660,7 +752,7 @@ "h": 4, "w": 4, "x": 0, - "y": 15 + "y": 23 }, "id": 17, "options": { @@ -722,7 +814,7 @@ "h": 4, "w": 4, "x": 4, - "y": 15 + "y": 23 }, "id": 24, "options": { @@ -761,7 +853,7 @@ "h": 1, "w": 24, "x": 0, - "y": 19 + "y": 27 }, "id": 21, "panels": [], @@ -797,7 +889,7 @@ "h": 4, "w": 4, "x": 0, - "y": 20 + "y": 28 }, "id": 12, "options": { @@ -859,7 +951,7 @@ "h": 4, "w": 4, "x": 4, - "y": 20 + "y": 28 }, "id": 11, "options": { @@ -921,7 +1013,7 @@ "h": 4, "w": 4, "x": 8, - "y": 20 + "y": 28 }, "id": 5, "options": { @@ -988,12 +1080,12 @@ ] }, "time": { - "from": "now-15m", - "to": "now" + "from": "2025-05-12T15:16:01.588Z", + "to": "2025-05-12T15:18:19.665Z" }, "timepicker": {}, "timezone": "browser", "title": "Load testing", "uid": "aejze3k4d0mpsb", - "version": 16 + "version": 17 } \ No newline at end of file From 2963c898a2afc960eb3e748478842a9e0e143798 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Mon, 12 May 2025 17:40:28 +0200 Subject: [PATCH 089/197] Update dashboard --- tests/load/c/dashboard.json | 134 ++++++------------------------------ 1 file changed, 21 insertions(+), 113 deletions(-) diff --git a/tests/load/c/dashboard.json b/tests/load/c/dashboard.json index 7c6ad12fa1d8..502addbcd985 100644 --- a/tests/load/c/dashboard.json +++ b/tests/load/c/dashboard.json @@ -21,105 +21,13 @@ "id": 1, "links": [], "panels": [ - { - "datasource": { - "type": "prometheus", - "uid": "dekh3fflmqzggf" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "barWidthFactor": 0.6, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 0 - }, - "id": 25, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "hideZeros": false, - "mode": "single", - "sort": "none" - } - }, - "pluginVersion": "11.6.1", - "targets": [ - { - "editorMode": "code", - "expr": "rate(avalanche_evm_eth_chain_txs_processed[30s])", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Panel Title", - "type": "timeseries" - }, { "collapsed": false, "gridPos": { "h": 1, "w": 24, "x": 0, - "y": 8 + "y": 0 }, "id": 19, "panels": [], @@ -152,7 +60,7 @@ "h": 4, "w": 4, "x": 0, - "y": 9 + "y": 1 }, "id": 18, "options": { @@ -214,7 +122,7 @@ "h": 4, "w": 3, "x": 4, - "y": 9 + "y": 1 }, "id": 13, "options": { @@ -253,7 +161,7 @@ "h": 1, "w": 24, "x": 0, - "y": 13 + "y": 5 }, "id": 20, "panels": [], @@ -293,7 +201,7 @@ "h": 4, "w": 4, "x": 0, - "y": 14 + "y": 6 }, "id": 9, "options": { @@ -359,7 +267,7 @@ "h": 4, "w": 4, "x": 4, - "y": 14 + "y": 6 }, "id": 7, "options": { @@ -425,7 +333,7 @@ "h": 4, "w": 4, "x": 8, - "y": 14 + "y": 6 }, "id": 8, "options": { @@ -491,7 +399,7 @@ "h": 4, "w": 4, "x": 12, - "y": 14 + "y": 6 }, "id": 10, "options": { @@ -530,7 +438,7 @@ "h": 1, "w": 24, "x": 0, - "y": 18 + "y": 10 }, "id": 22, "panels": [], @@ -566,7 +474,7 @@ "h": 4, "w": 4, "x": 0, - "y": 19 + "y": 11 }, "id": 14, "options": { @@ -628,7 +536,7 @@ "h": 4, "w": 4, "x": 4, - "y": 19 + "y": 11 }, "id": 16, "options": { @@ -690,7 +598,7 @@ "h": 4, "w": 4, "x": 8, - "y": 19 + "y": 11 }, "id": 23, "options": { @@ -752,7 +660,7 @@ "h": 4, "w": 4, "x": 0, - "y": 23 + "y": 15 }, "id": 17, "options": { @@ -814,7 +722,7 @@ "h": 4, "w": 4, "x": 4, - "y": 23 + "y": 15 }, "id": 24, "options": { @@ -853,7 +761,7 @@ "h": 1, "w": 24, "x": 0, - "y": 27 + "y": 19 }, "id": 21, "panels": [], @@ -889,7 +797,7 @@ "h": 4, "w": 4, "x": 0, - "y": 28 + "y": 20 }, "id": 12, "options": { @@ -951,7 +859,7 @@ "h": 4, "w": 4, "x": 4, - "y": 28 + "y": 20 }, "id": 11, "options": { @@ -1013,7 +921,7 @@ "h": 4, "w": 4, "x": 8, - "y": 28 + "y": 20 }, "id": 5, "options": { @@ -1080,12 +988,12 @@ ] }, "time": { - "from": "2025-05-12T15:16:01.588Z", - "to": "2025-05-12T15:18:19.665Z" + "from": "2025-05-12T15:20:41.799Z", + "to": "2025-05-12T15:22:31.414Z" }, "timepicker": {}, "timezone": "browser", "title": "Load testing", "uid": "aejze3k4d0mpsb", - "version": 17 + "version": 18 } \ No newline at end of file From ad060bbb996217039f025e9ada8203aaccab388e Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Mon, 12 May 2025 17:57:59 +0200 Subject: [PATCH 090/197] Issuer implementation as config option --- tests/load/c/execute.go | 28 ++++++++++++++++++++++++++-- tests/load/c/ginkgo_test.go | 28 ++++++++++++++++++++++++---- 2 files changed, 50 insertions(+), 6 deletions(-) diff --git a/tests/load/c/execute.go b/tests/load/c/execute.go index 5c9a3ce2cb98..72a7b69f9921 100644 --- a/tests/load/c/execute.go +++ b/tests/load/c/execute.go @@ -31,8 +31,16 @@ type config struct { minTPS int64 maxTPS int64 step int64 + issuer issuerType } +type issuerType string + +const ( + issuerSimple issuerType = "simple" + issuerOpcoder issuerType = "opcoder" +) + func execute(ctx context.Context, preFundedKeys []*secp256k1.PrivateKey, config config) error { logger := logging.NewLogger("", logging.NewWrappedCore(logging.Info, os.Stdout, logging.Auto.ConsoleEncoder())) @@ -68,8 +76,8 @@ func execute(ctx context.Context, preFundedKeys []*secp256k1.PrivateKey, config return fmt.Errorf("getting nonce for address %s: %w", address, err) } - issuer, err := issuers.NewOpcoder(ctx, client, tracker, - nonce, big.NewInt(config.maxFeeCap), keys[i]) + issuer, err := createIssuer(ctx, config.issuer, + client, tracker, config.maxFeeCap, nonce, keys[i]) if err != nil { return fmt.Errorf("creating issuer: %w", err) } @@ -132,3 +140,19 @@ func fixKeysCount(preFundedKeys []*secp256k1.PrivateKey, target int) ([]*ecdsa.P } return keys, nil } + +func createIssuer(ctx context.Context, typ issuerType, + client *ethclient.Client, tracker *load.Tracker[common.Hash], + maxFeeCap int64, nonce uint64, key *ecdsa.PrivateKey, +) (load.Issuer[common.Hash], error) { + switch typ { + case issuerSimple: + return issuers.NewSimple(ctx, client, tracker, + nonce, big.NewInt(maxFeeCap), key) + case issuerOpcoder: + return issuers.NewOpcoder(ctx, client, tracker, + nonce, big.NewInt(maxFeeCap), key) + default: + return nil, fmt.Errorf("unknown issuer type %s", typ) + } +} diff --git a/tests/load/c/ginkgo_test.go b/tests/load/c/ginkgo_test.go index 45d1ab4f59e6..b2178adf5552 100644 --- a/tests/load/c/ginkgo_test.go +++ b/tests/load/c/ginkgo_test.go @@ -66,16 +66,36 @@ var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { } }) - ginkgo.It("C-Chain", func(ctx context.Context) { + ginkgo.It("C-Chain simple", func(ctx context.Context) { const blockchainID = "C" endpoints, err := tmpnet.GetNodeWebsocketURIs(network.Nodes, blockchainID) require.NoError(ginkgo.GinkgoT(), err, "getting node websocket URIs") config := config{ endpoints: endpoints, - maxFeeCap: 5000, + issuer: issuerSimple, + maxFeeCap: 3000, agents: 1, - minTPS: 10, - maxTPS: 100, + minTPS: 50, + maxTPS: 90, + step: 10, + } + err = execute(ctx, network.PreFundedKeys, config) + if err != nil { + ginkgo.GinkgoT().Error(err) + } + }) + + ginkgo.It("C-Chain opcoder", func(ctx context.Context) { + const blockchainID = "C" + endpoints, err := tmpnet.GetNodeWebsocketURIs(network.Nodes, blockchainID) + require.NoError(ginkgo.GinkgoT(), err, "getting node websocket URIs") + config := config{ + endpoints: endpoints, + issuer: issuerOpcoder, + maxFeeCap: 300000000000, + agents: 1, + minTPS: 30, + maxTPS: 60, step: 5, } err = execute(ctx, network.PreFundedKeys, config) From ae437f39d5c3fc68563fd36c88484b5528a1e038 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Tue, 13 May 2025 08:41:45 +0200 Subject: [PATCH 091/197] Remove periodic log of tracker --- tests/load/c/execute.go | 2 -- tests/load/tracker.go | 25 ------------------------- 2 files changed, 27 deletions(-) diff --git a/tests/load/c/execute.go b/tests/load/c/execute.go index 72a7b69f9921..ad73f2827dac 100644 --- a/tests/load/c/execute.go +++ b/tests/load/c/execute.go @@ -103,8 +103,6 @@ func execute(ctx context.Context, preFundedKeys []*secp256k1.PrivateKey, config orchestratorErrCh <- orchestrator.Execute(orchestratorCtx) }() - go tracker.LogPeriodically(orchestratorCtx) - select { case err := <-orchestratorErrCh: if err != nil { diff --git a/tests/load/tracker.go b/tests/load/tracker.go index e04b17963049..65a88c6c8502 100644 --- a/tests/load/tracker.go +++ b/tests/load/tracker.go @@ -4,12 +4,10 @@ package load import ( - "context" "sync" "time" "github.com/prometheus/client_golang/prometheus" - "go.uber.org/zap" "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/utils/wrappers" @@ -140,26 +138,3 @@ func (p *Tracker[T]) ObserveFailed(txID T) { p.txsFailedCounter.Inc() p.txLatency.Observe(float64(time.Since(startTime).Milliseconds())) } - -func (t *Tracker[T]) LogPeriodically(ctx context.Context) { - const period = 10 * time.Second - - ticker := time.NewTicker(period) - defer ticker.Stop() - - for { - select { - case <-ctx.Done(): - return - case <-ticker.C: - } - t.lock.RLock() - t.logger.Info("Tracker stats", - zap.Uint64("issued", t.txsIssued), - zap.Uint64("confirmed", t.txsConfirmed), - zap.Uint64("failed", t.txsFailed), - zap.Int("inflight", len(t.outstandingTxs)), - ) - t.lock.RUnlock() - } -} From 7064e1d2e2115618023b2e42023a0258d142209c Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Tue, 13 May 2025 08:42:03 +0200 Subject: [PATCH 092/197] Lower tx rate multiplier to 1.05 --- tests/load/c/execute.go | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/load/c/execute.go b/tests/load/c/execute.go index ad73f2827dac..e44a6dfce7a7 100644 --- a/tests/load/c/execute.go +++ b/tests/load/c/execute.go @@ -97,6 +97,7 @@ func execute(ctx context.Context, preFundedKeys []*secp256k1.PrivateKey, config orchestratorConfig.MaxTPS = config.maxTPS orchestratorConfig.Step = config.step orchestratorConfig.SustainedTime = 10 * time.Second + orchestratorConfig.TxRateMultiplier = 1.05 orchestrator := load.NewOrchestrator(agents, tracker, logger, orchestratorConfig) orchestratorErrCh := make(chan error) go func() { From 84c6ac146f7418de2873ee88a372a02f2cafdcec Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Tue, 13 May 2025 08:42:15 +0200 Subject: [PATCH 093/197] Increase nodes count from 1 to 3 --- tests/load/c/ginkgo_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/load/c/ginkgo_test.go b/tests/load/c/ginkgo_test.go index b2178adf5552..859004e025f3 100644 --- a/tests/load/c/ginkgo_test.go +++ b/tests/load/c/ginkgo_test.go @@ -26,7 +26,7 @@ func init() { flagVars = e2e.RegisterFlagsWithDefaultOwner("avalanchego-load") } -const nodesCount = 1 +const nodesCount = 3 var _ = ginkgo.SynchronizedBeforeSuite(func() []byte { // Run only once in the first ginkgo process From f199d8490b95061931ab6ce38cc9c992f28ee84a Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Tue, 13 May 2025 08:44:37 +0200 Subject: [PATCH 094/197] Reset sustained time to default 20s --- tests/load/c/execute.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/load/c/execute.go b/tests/load/c/execute.go index e44a6dfce7a7..980f9ec01090 100644 --- a/tests/load/c/execute.go +++ b/tests/load/c/execute.go @@ -9,7 +9,6 @@ import ( "fmt" "math/big" "os" - "time" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/ethclient" @@ -96,7 +95,6 @@ func execute(ctx context.Context, preFundedKeys []*secp256k1.PrivateKey, config orchestratorConfig.MinTPS = config.minTPS orchestratorConfig.MaxTPS = config.maxTPS orchestratorConfig.Step = config.step - orchestratorConfig.SustainedTime = 10 * time.Second orchestratorConfig.TxRateMultiplier = 1.05 orchestrator := load.NewOrchestrator(agents, tracker, logger, orchestratorConfig) orchestratorErrCh := make(chan error) From 14c44f93a6485e992c4a262810189118f11ddabf Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Tue, 13 May 2025 10:24:49 +0200 Subject: [PATCH 095/197] Remove unneeded logger from tracker --- tests/load/c/execute.go | 2 +- tests/load/orchestrator_test.go | 4 ++-- tests/load/tracker.go | 7 +------ 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/tests/load/c/execute.go b/tests/load/c/execute.go index 980f9ec01090..ebab30776621 100644 --- a/tests/load/c/execute.go +++ b/tests/load/c/execute.go @@ -55,7 +55,7 @@ func execute(ctx context.Context, preFundedKeys []*secp256k1.PrivateKey, config registry := prometheus.NewRegistry() metricsServer := load.NewPrometheusServer("127.0.0.1:8082", registry, logger) - tracker, err := load.NewTracker[common.Hash](registry, logger) + tracker, err := load.NewTracker[common.Hash](registry) if err != nil { return fmt.Errorf("creating tracker: %w", err) } diff --git a/tests/load/orchestrator_test.go b/tests/load/orchestrator_test.go index b480dd4ba6e6..1db7374a9983 100644 --- a/tests/load/orchestrator_test.go +++ b/tests/load/orchestrator_test.go @@ -61,7 +61,7 @@ func TestOrchestratorTPS(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - tracker, err := NewTracker[ids.ID](prometheus.NewRegistry(), logging.NoLog{}) + tracker, err := NewTracker[ids.ID](prometheus.NewRegistry()) r.NoError(err) agents := []Agent[ids.ID]{ @@ -102,7 +102,7 @@ func TestOrchestratorExecution(t *testing.T) { errMockIssuer = errors.New("mock issuer error") ) - tracker, err := NewTracker[ids.ID](prometheus.NewRegistry(), logging.NoLog{}) + tracker, err := NewTracker[ids.ID](prometheus.NewRegistry()) require.NoError(t, err, "creating tracker") tests := []struct { diff --git a/tests/load/tracker.go b/tests/load/tracker.go index 65a88c6c8502..2ca07a6444c4 100644 --- a/tests/load/tracker.go +++ b/tests/load/tracker.go @@ -9,7 +9,6 @@ import ( "github.com/prometheus/client_golang/prometheus" - "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/utils/wrappers" ) @@ -31,15 +30,12 @@ type Tracker[T TxID] struct { txsConfirmedCounter prometheus.Counter txsFailedCounter prometheus.Counter txLatency prometheus.Histogram - - logger logging.Logger } // NewTracker returns a new Tracker instance which records metrics for the number // of transactions issued, confirmed, and failed. It also tracks the latency of // transactions. -func NewTracker[T TxID](reg prometheus.Registerer, logger logging.Logger, -) (*Tracker[T], error) { +func NewTracker[T TxID](reg prometheus.Registerer) (*Tracker[T], error) { tracker := &Tracker[T]{ outstandingTxs: make(map[T]time.Time), txsIssuedCounter: prometheus.NewCounter(prometheus.CounterOpts{ @@ -62,7 +58,6 @@ func NewTracker[T TxID](reg prometheus.Registerer, logger logging.Logger, Name: "tx_latency", Help: "Latency of transactions", }), - logger: logger, } errs := wrappers.Errs{} From a72032bc6c20632f01e96086bdc8828461043f7a Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Tue, 13 May 2025 11:10:07 +0200 Subject: [PATCH 096/197] Fix copyright notices --- .../{EVMLoadSimulator.go => EVMLoadSimulator.bindings.go} | 0 tests/load/c/contracts/generate_abi_bindings.sh | 4 ++-- tests/load/c/contracts/generate_test.go | 2 +- tests/load/c/distribute.go | 2 +- tests/load/c/execute.go | 2 +- tests/load/c/ginkgo_test.go | 2 +- tests/load/c/issuers/opcode.go | 2 +- tests/load/c/issuers/simple.go | 2 +- tests/load/c/listener/listener.go | 2 +- tests/load/c/listener/newhead.go | 2 +- tests/load/prometheus.go | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) rename tests/load/c/contracts/{EVMLoadSimulator.go => EVMLoadSimulator.bindings.go} (100%) diff --git a/tests/load/c/contracts/EVMLoadSimulator.go b/tests/load/c/contracts/EVMLoadSimulator.bindings.go similarity index 100% rename from tests/load/c/contracts/EVMLoadSimulator.go rename to tests/load/c/contracts/EVMLoadSimulator.bindings.go diff --git a/tests/load/c/contracts/generate_abi_bindings.sh b/tests/load/c/contracts/generate_abi_bindings.sh index 8ab28cbf7047..e0ec5b15246e 100755 --- a/tests/load/c/contracts/generate_abi_bindings.sh +++ b/tests/load/c/contracts/generate_abi_bindings.sh @@ -24,7 +24,7 @@ for FILE in "${CONTRACTS_DIR}"/*.sol; do --abi="${CONTRACTS_DIR}/${CONTRACT_NAME}.abi" \ --type $CONTRACT_NAME \ --pkg=contracts \ - --out="${CONTRACTS_DIR}/${CONTRACT_NAME}.go" + --out="${CONTRACTS_DIR}/${CONTRACT_NAME}.bindings.go" rm "${CONTRACTS_DIR}/${CONTRACT_NAME}.bin" "${CONTRACTS_DIR}/${CONTRACT_NAME}.abi" - echo "Generated ${CONTRACT_NAME}.go" + echo "Generated ${CONTRACT_NAME}.bindings.go" done diff --git a/tests/load/c/contracts/generate_test.go b/tests/load/c/contracts/generate_test.go index 398f23002e7f..a58dd42aeec2 100644 --- a/tests/load/c/contracts/generate_test.go +++ b/tests/load/c/contracts/generate_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. +// Co// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package contracts diff --git a/tests/load/c/distribute.go b/tests/load/c/distribute.go index 43928348baf6..befacbaa3ae4 100644 --- a/tests/load/c/distribute.go +++ b/tests/load/c/distribute.go @@ -1,4 +1,4 @@ -// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. +// Co// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package c diff --git a/tests/load/c/execute.go b/tests/load/c/execute.go index ebab30776621..b671666b7873 100644 --- a/tests/load/c/execute.go +++ b/tests/load/c/execute.go @@ -1,4 +1,4 @@ -// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. +// Co// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package c diff --git a/tests/load/c/ginkgo_test.go b/tests/load/c/ginkgo_test.go index 859004e025f3..b200cfb16caf 100644 --- a/tests/load/c/ginkgo_test.go +++ b/tests/load/c/ginkgo_test.go @@ -1,4 +1,4 @@ -// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. +// Co// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package c diff --git a/tests/load/c/issuers/opcode.go b/tests/load/c/issuers/opcode.go index 26768f15de67..c2b11a25cf1b 100644 --- a/tests/load/c/issuers/opcode.go +++ b/tests/load/c/issuers/opcode.go @@ -1,4 +1,4 @@ -// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. +// Co// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package issuers diff --git a/tests/load/c/issuers/simple.go b/tests/load/c/issuers/simple.go index 6a712cc64bd1..c7a77cd74149 100644 --- a/tests/load/c/issuers/simple.go +++ b/tests/load/c/issuers/simple.go @@ -1,4 +1,4 @@ -// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. +// Co// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package issuers diff --git a/tests/load/c/listener/listener.go b/tests/load/c/listener/listener.go index 3b3e47881c0c..0770e7653e40 100644 --- a/tests/load/c/listener/listener.go +++ b/tests/load/c/listener/listener.go @@ -1,4 +1,4 @@ -// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. +// Co// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package listener diff --git a/tests/load/c/listener/newhead.go b/tests/load/c/listener/newhead.go index 532c66b49809..1562618ce658 100644 --- a/tests/load/c/listener/newhead.go +++ b/tests/load/c/listener/newhead.go @@ -1,4 +1,4 @@ -// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. +// Co// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package listener diff --git a/tests/load/prometheus.go b/tests/load/prometheus.go index e1d7a5490a4c..bdf0dcb42eb8 100644 --- a/tests/load/prometheus.go +++ b/tests/load/prometheus.go @@ -1,4 +1,4 @@ -// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. +// Co// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package load From f2ddaf545c4e8724d5a7dee0401e7971aafa7cec Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Tue, 13 May 2025 11:10:54 +0200 Subject: [PATCH 097/197] Fix ginkgo command for C chain tests --- Taskfile.yml | 4 ++-- tests/e2e/README.md | 23 +++++++++++------------ tests/load/c/ginkgo_test.go | 2 +- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/Taskfile.yml b/Taskfile.yml index d294cf75268c..642722f3115e 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -2,7 +2,7 @@ # To run on a system without task installed, `./scripts/run_task.sh` will execute it with `go run`. # If in the nix dev shell, `task` is available. -version: '3' +version: "3" tasks: default: ./scripts/run_task.sh --list @@ -177,7 +177,7 @@ tasks: desc: Runs e2e load tests cmds: - task: build - - cmd: ./bin/ginkgo -v ./tests/load -- --avalanchego-path=$PWD/build/avalanchego + - cmd: ./bin/ginkgo -v ./tests/load/c -- --avalanchego-path=$PWD/build/avalanchego # To use a different fuzz time, run `task test-fuzz FUZZTIME=[value in seconds]`. # A value of `-1` will run until it encounters a failing output. diff --git a/tests/e2e/README.md b/tests/e2e/README.md index eb2b4b020853..65f5cfb228a2 100644 --- a/tests/e2e/README.md +++ b/tests/e2e/README.md @@ -9,7 +9,7 @@ ./scripts/build.sh # Builds avalanchego for use in deploying a test network ./scripts/build_xsvm.sh # Builds xsvm for use in deploying a test network with a subnet ./bin/ginkgo -v ./tests/e2e -- --avalanchego-path=$PWD/build/avalanchego # Note that the path given for --avalanchego-path must be an absolute and not a relative path. -./bin/ginkgo -v ./tests/load -- --avalanchego-path=$PWD/build/avalanchego +./bin/ginkgo -v ./tests/load/c -- --avalanchego-path=$PWD/build/avalanchego ``` See [`tests.e2e.sh`](../../scripts/tests.e2e.sh) for an example. @@ -31,7 +31,6 @@ are defined as constants in [`describe.go`](../fixture/e2e/describe.go) with nam of the form `*Label`. The following example runs only those tests that primarily target the X-Chain: - ```bash ./bin/ginkgo -v --label-filter=x ./tests/e2e -- --avalanchego-path=$PWD/build/avalanchego ``` @@ -114,19 +113,19 @@ E2E_SKIP_BOOTSTRAP_CHECKS=1 ./bin/ginkgo -v ./tests/e2e ... It is possible to enable collection of logs and metrics from the temporary networks used for e2e testing by: - - Supplying `--start-collectors` as an argument to the test suite - - Starting collectors in advance of a test run with `tmpnetctl +- Supplying `--start-collectors` as an argument to the test suite +- Starting collectors in advance of a test run with `tmpnetctl start-collectors` Both methods require: - - Auth credentials to be supplied as env vars: - - `PROMETHEUS_USERNAME` - - `PROMETHEUS_PASSWORD` - - `LOKI_USERNAME` - - `LOKI_PASSWORD` - - The availability in the path of binaries for promtail and prometheus - - Starting a development shell with `nix develop` is one way to +- Auth credentials to be supplied as env vars: + - `PROMETHEUS_USERNAME` + - `PROMETHEUS_PASSWORD` + - `LOKI_USERNAME` + - `LOKI_PASSWORD` +- The availability in the path of binaries for promtail and prometheus + - Starting a development shell with `nix develop` is one way to ensure this and requires the [installation of nix](https://github.com/DeterminateSystems/nix-installer?tab=readme-ov-file#install-nix). @@ -134,6 +133,6 @@ Once started, the collectors will continue to run in the background until stopped by `tmpnetctl stop-collectors`. The results of collection will be viewable at -https://grafana-poc.avax-dev.network. +. For more detail, see the [tmpnet docs](../fixture/tmpnet/README.md##monitoring). diff --git a/tests/load/c/ginkgo_test.go b/tests/load/c/ginkgo_test.go index b200cfb16caf..643df1a92c6f 100644 --- a/tests/load/c/ginkgo_test.go +++ b/tests/load/c/ginkgo_test.go @@ -15,7 +15,7 @@ import ( ) // Run this using the command: -// ./bin/ginkgo -v ./tests/load -- --avalanchego-path=$PWD/build/avalanchego +// ./bin/ginkgo -v ./tests/load/c -- --avalanchego-path=$PWD/build/avalanchego func TestLoad(t *testing.T) { ginkgo.RunSpecs(t, "load tests") } From be69a42f07900eb98a025ecd35b279b5da784c26 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Tue, 13 May 2025 11:15:52 +0200 Subject: [PATCH 098/197] Add CI step to check contract bindings are up to date --- .github/workflows/ci.yml | 3 +++ Taskfile.yml | 12 ++++++++++++ 2 files changed, 15 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a5ff3967ce02..bb9020361ddb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -221,6 +221,9 @@ jobs: fetch-depth: 0 - uses: ./.github/actions/setup-go-for-project - uses: ./.github/actions/install-nix + - name: Contract bindings are up to date + shell: bash + run: ./scripts/run_task.sh check-generate-load-contract-bindings - uses: ./.github/actions/run-monitored-tmpnet-cmd with: run: ./scripts/run_task.sh test-e2e-ci diff --git a/Taskfile.yml b/Taskfile.yml index 642722f3115e..ba353688cdb1 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -75,6 +75,12 @@ tasks: - cmd: go mod tidy - task: check-clean-branch + check-generate-load-contract-bindings: + desc: Checks that generated load contract bindings are up-to-date (requires a clean git working tree) + cmds: + - task: generate-load-contract-bindings + - task: check-clean-branch + generate-mocks: desc: Generates testing mocks cmds: @@ -89,6 +95,12 @@ tasks: desc: Generates protobuf cmd: ./scripts/protobuf_codegen.sh + generate-load-contract-bindings: + desc: Generates load contract bindings + cmds: + - cmd: grep -lr -E '^// Code generated - DO NOT EDIT\.$' tests/load | xargs -r rm + - cmd: go generate ./tests/load/... + install-nix: desc: Installs nix with the determinate systems installer cmd: curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install From 8217ce489ab7a4d3659bbef4d2723c93b6cb9445 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Tue, 13 May 2025 11:43:15 +0200 Subject: [PATCH 099/197] Ignore .bindings.go for copyright check --- scripts/lint.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/lint.sh b/scripts/lint.sh index fd1201b5d18e..9db7769409e4 100755 --- a/scripts/lint.sh +++ b/scripts/lint.sh @@ -41,7 +41,7 @@ function test_golangci_lint { _addlicense_flags=${ADDLICENSE_FLAGS:-"--verify --debug"} function test_license_header { local files=() - while IFS= read -r line; do files+=("$line"); done < <(find . -type f -name '*.go' ! -name '*.pb.go' ! -name 'mock_*.go' ! -name 'mocks_*.go' ! -path './**/*mock/*.go' ! -name '*.canoto.go') + while IFS= read -r line; do files+=("$line"); done < <(find . -type f -name '*.go' ! -name '*.pb.go' ! -name 'mock_*.go' ! -name 'mocks_*.go' ! -path './**/*mock/*.go' ! -name '*.canoto.go' ! -name '*.bindings.go') # shellcheck disable=SC2086 go run github.com/palantir/go-license@v1.25.0 \ From 2f05785b88424adb0a0ae9419142b0ae358f6420 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 14 May 2025 12:51:55 +0200 Subject: [PATCH 100/197] Run with 5 nodes at least --- tests/load/c/ginkgo_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/load/c/ginkgo_test.go b/tests/load/c/ginkgo_test.go index 643df1a92c6f..8fc240f1c805 100644 --- a/tests/load/c/ginkgo_test.go +++ b/tests/load/c/ginkgo_test.go @@ -26,11 +26,12 @@ func init() { flagVars = e2e.RegisterFlagsWithDefaultOwner("avalanchego-load") } -const nodesCount = 3 +const nodesCount = 5 var _ = ginkgo.SynchronizedBeforeSuite(func() []byte { // Run only once in the first ginkgo process + require.GreaterOrEqual(ginkgo.GinkgoT(), nodesCount, 5, "number of nodes must be at least 5") tc := e2e.NewTestContext() nodes := tmpnet.NewNodesOrPanic(nodesCount) network := &tmpnet.Network{ From e0570266be4d0c07996d777cbeb2ff1c416fd82d Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 15 May 2025 16:59:07 +0200 Subject: [PATCH 101/197] Format solidity contract --- tests/load/c/contracts/EVMLoadSimulator.sol | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/load/c/contracts/EVMLoadSimulator.sol b/tests/load/c/contracts/EVMLoadSimulator.sol index 0a55385cf402..999465b74fc7 100644 --- a/tests/load/c/contracts/EVMLoadSimulator.sol +++ b/tests/load/c/contracts/EVMLoadSimulator.sol @@ -54,7 +54,9 @@ contract EVMLoadSimulator { } // Simulate dynamic memory allocation and usage - function simulateMemory(uint256 sizeInWords) external returns (uint256 sum) { + function simulateMemory( + uint256 sizeInWords + ) external returns (uint256 sum) { uint256[] memory arr = new uint256[](sizeInWords); for (uint256 i = 0; i < sizeInWords; i++) { arr[i] = i; From 4f6ea609d03eebdc0658da3c5324442c8eda0275 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 15 May 2025 16:59:30 +0200 Subject: [PATCH 102/197] Emit storage update for simulate call depth function --- tests/load/c/contracts/EVMLoadSimulator.sol | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/load/c/contracts/EVMLoadSimulator.sol b/tests/load/c/contracts/EVMLoadSimulator.sol index 999465b74fc7..b00a6bc58ec5 100644 --- a/tests/load/c/contracts/EVMLoadSimulator.sol +++ b/tests/load/c/contracts/EVMLoadSimulator.sol @@ -67,8 +67,10 @@ contract EVMLoadSimulator { // Simulate deep call stack function simulateCallDepth(uint256 depth) external { - if (depth > 0) { - this.simulateCallDepth(depth - 1); + if (depth == 0) { + emit StorageUpdate(0, 0); // Emit an event to indicate completion + return; } + this.simulateCallDepth(depth - 1); } } From 7c32918ce23c2f2dd3784810bd1cbd7db7b89eee Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 15 May 2025 17:38:19 +0200 Subject: [PATCH 103/197] createAgent helper function --- tests/load/c/execute.go | 43 +++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/tests/load/c/execute.go b/tests/load/c/execute.go index b671666b7873..bb94a52f83b4 100644 --- a/tests/load/c/execute.go +++ b/tests/load/c/execute.go @@ -63,25 +63,10 @@ func execute(ctx context.Context, preFundedKeys []*secp256k1.PrivateKey, config agents := make([]load.Agent[common.Hash], config.agents) for i := range agents { endpoint := config.endpoints[i%len(config.endpoints)] - client, err := ethclient.DialContext(ctx, endpoint) + agents[i], err = createAgent(ctx, endpoint, keys[i], config.issuer, tracker, config.maxFeeCap) if err != nil { - return fmt.Errorf("dialing %s: %w", endpoint, err) + return fmt.Errorf("creating agent: %w", err) } - - address := ethcrypto.PubkeyToAddress(keys[i].PublicKey) - blockNumber := (*big.Int)(nil) - nonce, err := client.NonceAt(ctx, address, blockNumber) - if err != nil { - return fmt.Errorf("getting nonce for address %s: %w", address, err) - } - - issuer, err := createIssuer(ctx, config.issuer, - client, tracker, config.maxFeeCap, nonce, keys[i]) - if err != nil { - return fmt.Errorf("creating issuer: %w", err) - } - listener := listener.New(client, tracker, address, nonce) - agents[i] = load.NewAgent(issuer, listener) } metricsErrCh, err := metricsServer.Start() @@ -138,6 +123,30 @@ func fixKeysCount(preFundedKeys []*secp256k1.PrivateKey, target int) ([]*ecdsa.P return keys, nil } +func createAgent(ctx context.Context, endpoint string, key *ecdsa.PrivateKey, + issuerType issuerType, tracker *load.Tracker[common.Hash], maxFeeCap int64, +) (load.Agent[common.Hash], error) { + client, err := ethclient.DialContext(ctx, endpoint) + if err != nil { + return load.Agent[common.Hash]{}, fmt.Errorf("dialing %s: %w", endpoint, err) + } + + address := ethcrypto.PubkeyToAddress(key.PublicKey) + blockNumber := (*big.Int)(nil) + nonce, err := client.NonceAt(ctx, address, blockNumber) + if err != nil { + return load.Agent[common.Hash]{}, fmt.Errorf("getting nonce for address %s: %w", address, err) + } + + issuer, err := createIssuer(ctx, issuerType, + client, tracker, maxFeeCap, nonce, key) + if err != nil { + return load.Agent[common.Hash]{}, fmt.Errorf("creating issuer: %w", err) + } + listener := listener.New(client, tracker, address, nonce) + return load.NewAgent(issuer, listener), nil +} + func createIssuer(ctx context.Context, typ issuerType, client *ethclient.Client, tracker *load.Tracker[common.Hash], maxFeeCap int64, nonce uint64, key *ecdsa.PrivateKey, From a5942341a643a59eb0f9b7b6795c899a17688f4a Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 15 May 2025 17:47:11 +0200 Subject: [PATCH 104/197] createAgents helping function --- tests/load/c/execute.go | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/tests/load/c/execute.go b/tests/load/c/execute.go index bb94a52f83b4..d02b641b2f13 100644 --- a/tests/load/c/execute.go +++ b/tests/load/c/execute.go @@ -60,13 +60,9 @@ func execute(ctx context.Context, preFundedKeys []*secp256k1.PrivateKey, config return fmt.Errorf("creating tracker: %w", err) } - agents := make([]load.Agent[common.Hash], config.agents) - for i := range agents { - endpoint := config.endpoints[i%len(config.endpoints)] - agents[i], err = createAgent(ctx, endpoint, keys[i], config.issuer, tracker, config.maxFeeCap) - if err != nil { - return fmt.Errorf("creating agent: %w", err) - } + agents, err := createAgents(ctx, config, keys, tracker) + if err != nil { + return fmt.Errorf("creating agents: %w", err) } metricsErrCh, err := metricsServer.Start() @@ -123,6 +119,21 @@ func fixKeysCount(preFundedKeys []*secp256k1.PrivateKey, target int) ([]*ecdsa.P return keys, nil } +func createAgents(ctx context.Context, config config, keys []*ecdsa.PrivateKey, + tracker *load.Tracker[common.Hash], +) ([]load.Agent[common.Hash], error) { + agents := make([]load.Agent[common.Hash], config.agents) + var err error + for i := range agents { + endpoint := config.endpoints[i%len(config.endpoints)] + agents[i], err = createAgent(ctx, endpoint, keys[i], config.issuer, tracker, config.maxFeeCap) + if err != nil { + return nil, err + } + } + return agents, nil +} + func createAgent(ctx context.Context, endpoint string, key *ecdsa.PrivateKey, issuerType issuerType, tracker *load.Tracker[common.Hash], maxFeeCap int64, ) (load.Agent[common.Hash], error) { From 4cb0924700db8cd26eaf2028abe05ec2b1e60b3c Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 15 May 2025 19:16:04 +0200 Subject: [PATCH 105/197] parallel agents creation --- tests/load/c/execute.go | 43 +++++++++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/tests/load/c/execute.go b/tests/load/c/execute.go index d02b641b2f13..06d206cb448f 100644 --- a/tests/load/c/execute.go +++ b/tests/load/c/execute.go @@ -119,18 +119,49 @@ func fixKeysCount(preFundedKeys []*secp256k1.PrivateKey, target int) ([]*ecdsa.P return keys, nil } +// createAgents creates agents for the given configuration and keys. +// It creates them in parallel because creating issuers can sometimes take a while, +// and this adds up for many agents. For example, deploying the Opcoder contract +// takes a few seconds. Running the creation in parallel can reduce the time significantly. func createAgents(ctx context.Context, config config, keys []*ecdsa.PrivateKey, tracker *load.Tracker[common.Hash], ) ([]load.Agent[common.Hash], error) { - agents := make([]load.Agent[common.Hash], config.agents) - var err error - for i := range agents { + ctx, cancel := context.WithCancel(ctx) + defer cancel() + type result struct { + agent load.Agent[common.Hash] + err error + } + ch := make(chan result) + for i := range int(config.agents) { + key := keys[i] endpoint := config.endpoints[i%len(config.endpoints)] - agents[i], err = createAgent(ctx, endpoint, keys[i], config.issuer, tracker, config.maxFeeCap) - if err != nil { - return nil, err + go func(key *ecdsa.PrivateKey, endpoint string) { + agent, err := createAgent(ctx, endpoint, key, config.issuer, tracker, config.maxFeeCap) + ch <- result{agent: agent, err: err} + }(key, endpoint) + } + + var err error + agents := make([]load.Agent[common.Hash], 0, int(config.agents)) + for range int(config.agents) { + result := <-ch + switch { + case result.err == nil && err == nil: // no previous error or new error + agents = append(agents, result.agent) + case err != nil: // error already occurred + continue + case result.err != nil: // first error + err = result.err + cancel() + default: + panic("unreachable") } } + + if err != nil { + return nil, err + } return agents, nil } From 8bfb8005257e7291ad9904981a4d0ce8fbfeca2e Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 15 May 2025 19:52:05 +0200 Subject: [PATCH 106/197] Add intro to readme --- tests/load/c/readme.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/load/c/readme.md b/tests/load/c/readme.md index c04381f5bc76..7090eb0f7071 100644 --- a/tests/load/c/readme.md +++ b/tests/load/c/readme.md @@ -1,5 +1,26 @@ # Load testing +The C-chain load test entrypoint is in [ginkgo_test.go](ginkgo_test.go). + +It runs with 5 nodes and 5 "agents". + +Each "agent" runs a transaction issuer and a transaction listener asynchronously, +and is assigned uniformly to the nodes available, via websocket connections. + +There are two load tests: + +1. "Simple" load test, where transactions issued are zero-fund transfers to the sender address. +2. "Complex" load test, where [this contract](contracts/EVMLoadSimulator.sol) is deployed and transactions call functions of this contract at random with random parameters. This contract has different functions, each testing a particular performance aspect of the EVM, for example memory writes. + +From the load test perspective, only the TPS (transactions per second) is logged out. Metrics available are: + +- total transactions issued `txs_issued` +- total transactions confirmed `txs_confirmed` +- total transactions failed `txs_failed` +- transaction latency histogram `tx_latency` + +There are more interesting metrics available from the tmpnet nodes being load tested. The following sub-sections explain how to set up the monitoring stack to visualize the metrics during the load tests. + ## Prometheus 1. Navigate to this directory with `cd tests/load/c`. From 064c91e8781c4c0c44648a6c7862fcd46a38bc00 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Fri, 16 May 2025 13:12:21 +0200 Subject: [PATCH 107/197] Add contract creation testing --- tests/load/c/contracts/Dummy.bindings.go | 638 ++++++++++++++++++ tests/load/c/contracts/Dummy.sol | 33 + .../c/contracts/EVMLoadSimulator.bindings.go | 25 +- tests/load/c/contracts/EVMLoadSimulator.sol | 6 + .../load/c/contracts/generate_abi_bindings.sh | 10 +- tests/load/c/issuers/opcode.go | 4 + 6 files changed, 709 insertions(+), 7 deletions(-) create mode 100644 tests/load/c/contracts/Dummy.bindings.go create mode 100644 tests/load/c/contracts/Dummy.sol diff --git a/tests/load/c/contracts/Dummy.bindings.go b/tests/load/c/contracts/Dummy.bindings.go new file mode 100644 index 000000000000..b35dba2e8cc3 --- /dev/null +++ b/tests/load/c/contracts/Dummy.bindings.go @@ -0,0 +1,638 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package contracts + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/ava-labs/libevm" + "github.com/ava-labs/libevm/accounts/abi" + "github.com/ava-labs/libevm/accounts/abi/bind" + "github.com/ava-labs/libevm/common" + "github.com/ava-labs/libevm/core/types" + "github.com/ava-labs/libevm/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +// DummyMetaData contains all meta data concerning the Dummy contract. +var DummyMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"key\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"DataWritten\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newValue\",\"type\":\"uint256\"}],\"name\":\"ValueUpdated\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"data\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"key\",\"type\":\"uint256\"}],\"name\":\"readData\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newValue\",\"type\":\"uint256\"}],\"name\":\"updateValue\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"value\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"key\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"val\",\"type\":\"uint256\"}],\"name\":\"writeData\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x6080604052348015600e575f5ffd5b50602a5f819055503360015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506103a1806100635f395ff3fe608060405234801561000f575f5ffd5b5060043610610060575f3560e01c806337ebbc03146100645780633fa4f24514610094578063573c0bd3146100b25780638da5cb5b146100ce578063c71ba63b146100ec578063f0ba844014610108575b5f5ffd5b61007e6004803603810190610079919061025b565b610138565b60405161008b9190610295565b60405180910390f35b61009c610152565b6040516100a99190610295565b60405180910390f35b6100cc60048036038101906100c7919061025b565b610157565b005b6100d6610197565b6040516100e391906102ed565b60405180910390f35b61010660048036038101906101019190610306565b6101bc565b005b610122600480360381019061011d919061025b565b61020f565b60405161012f9190610295565b60405180910390f35b5f60025f8381526020019081526020015f20549050919050565b5f5481565b805f819055507f4273d0736f60e0dedfe745e86718093d8ec8646ebd2a60cd60643eeced5658118160405161018c9190610295565b60405180910390a150565b60015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b8060025f8481526020019081526020015f20819055507f36c0e38a11934bb6e80e00c4ae42212be021022fdb5aff12c53720f1d951c2818282604051610203929190610344565b60405180910390a15050565b6002602052805f5260405f205f915090505481565b5f5ffd5b5f819050919050565b61023a81610228565b8114610244575f5ffd5b50565b5f8135905061025581610231565b92915050565b5f602082840312156102705761026f610224565b5b5f61027d84828501610247565b91505092915050565b61028f81610228565b82525050565b5f6020820190506102a85f830184610286565b92915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6102d7826102ae565b9050919050565b6102e7816102cd565b82525050565b5f6020820190506103005f8301846102de565b92915050565b5f5f6040838503121561031c5761031b610224565b5b5f61032985828601610247565b925050602061033a85828601610247565b9150509250929050565b5f6040820190506103575f830185610286565b6103646020830184610286565b939250505056fea2646970667358221220903ff8ac6aec2e45b8ca86cbb59be29b3dcfa00f77033329976bbcf33ba63e7064736f6c634300081d0033", +} + +// DummyABI is the input ABI used to generate the binding from. +// Deprecated: Use DummyMetaData.ABI instead. +var DummyABI = DummyMetaData.ABI + +// DummyBin is the compiled bytecode used for deploying new contracts. +// Deprecated: Use DummyMetaData.Bin instead. +var DummyBin = DummyMetaData.Bin + +// DeployDummy deploys a new Ethereum contract, binding an instance of Dummy to it. +func DeployDummy(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *Dummy, error) { + parsed, err := DummyMetaData.GetAbi() + if err != nil { + return common.Address{}, nil, nil, err + } + if parsed == nil { + return common.Address{}, nil, nil, errors.New("GetABI returned nil") + } + + address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(DummyBin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &Dummy{DummyCaller: DummyCaller{contract: contract}, DummyTransactor: DummyTransactor{contract: contract}, DummyFilterer: DummyFilterer{contract: contract}}, nil +} + +// Dummy is an auto generated Go binding around an Ethereum contract. +type Dummy struct { + DummyCaller // Read-only binding to the contract + DummyTransactor // Write-only binding to the contract + DummyFilterer // Log filterer for contract events +} + +// DummyCaller is an auto generated read-only Go binding around an Ethereum contract. +type DummyCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// DummyTransactor is an auto generated write-only Go binding around an Ethereum contract. +type DummyTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// DummyFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type DummyFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// DummySession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type DummySession struct { + Contract *Dummy // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// DummyCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type DummyCallerSession struct { + Contract *DummyCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// DummyTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type DummyTransactorSession struct { + Contract *DummyTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// DummyRaw is an auto generated low-level Go binding around an Ethereum contract. +type DummyRaw struct { + Contract *Dummy // Generic contract binding to access the raw methods on +} + +// DummyCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type DummyCallerRaw struct { + Contract *DummyCaller // Generic read-only contract binding to access the raw methods on +} + +// DummyTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type DummyTransactorRaw struct { + Contract *DummyTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewDummy creates a new instance of Dummy, bound to a specific deployed contract. +func NewDummy(address common.Address, backend bind.ContractBackend) (*Dummy, error) { + contract, err := bindDummy(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &Dummy{DummyCaller: DummyCaller{contract: contract}, DummyTransactor: DummyTransactor{contract: contract}, DummyFilterer: DummyFilterer{contract: contract}}, nil +} + +// NewDummyCaller creates a new read-only instance of Dummy, bound to a specific deployed contract. +func NewDummyCaller(address common.Address, caller bind.ContractCaller) (*DummyCaller, error) { + contract, err := bindDummy(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &DummyCaller{contract: contract}, nil +} + +// NewDummyTransactor creates a new write-only instance of Dummy, bound to a specific deployed contract. +func NewDummyTransactor(address common.Address, transactor bind.ContractTransactor) (*DummyTransactor, error) { + contract, err := bindDummy(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &DummyTransactor{contract: contract}, nil +} + +// NewDummyFilterer creates a new log filterer instance of Dummy, bound to a specific deployed contract. +func NewDummyFilterer(address common.Address, filterer bind.ContractFilterer) (*DummyFilterer, error) { + contract, err := bindDummy(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &DummyFilterer{contract: contract}, nil +} + +// bindDummy binds a generic wrapper to an already deployed contract. +func bindDummy(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := DummyMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Dummy *DummyRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _Dummy.Contract.DummyCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Dummy *DummyRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Dummy.Contract.DummyTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Dummy *DummyRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Dummy.Contract.DummyTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Dummy *DummyCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _Dummy.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Dummy *DummyTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Dummy.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Dummy *DummyTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Dummy.Contract.contract.Transact(opts, method, params...) +} + +// Data is a free data retrieval call binding the contract method 0xf0ba8440. +// +// Solidity: function data(uint256 ) view returns(uint256) +func (_Dummy *DummyCaller) Data(opts *bind.CallOpts, arg0 *big.Int) (*big.Int, error) { + var out []interface{} + err := _Dummy.contract.Call(opts, &out, "data", arg0) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Data is a free data retrieval call binding the contract method 0xf0ba8440. +// +// Solidity: function data(uint256 ) view returns(uint256) +func (_Dummy *DummySession) Data(arg0 *big.Int) (*big.Int, error) { + return _Dummy.Contract.Data(&_Dummy.CallOpts, arg0) +} + +// Data is a free data retrieval call binding the contract method 0xf0ba8440. +// +// Solidity: function data(uint256 ) view returns(uint256) +func (_Dummy *DummyCallerSession) Data(arg0 *big.Int) (*big.Int, error) { + return _Dummy.Contract.Data(&_Dummy.CallOpts, arg0) +} + +// Owner is a free data retrieval call binding the contract method 0x8da5cb5b. +// +// Solidity: function owner() view returns(address) +func (_Dummy *DummyCaller) Owner(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _Dummy.contract.Call(opts, &out, "owner") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// Owner is a free data retrieval call binding the contract method 0x8da5cb5b. +// +// Solidity: function owner() view returns(address) +func (_Dummy *DummySession) Owner() (common.Address, error) { + return _Dummy.Contract.Owner(&_Dummy.CallOpts) +} + +// Owner is a free data retrieval call binding the contract method 0x8da5cb5b. +// +// Solidity: function owner() view returns(address) +func (_Dummy *DummyCallerSession) Owner() (common.Address, error) { + return _Dummy.Contract.Owner(&_Dummy.CallOpts) +} + +// ReadData is a free data retrieval call binding the contract method 0x37ebbc03. +// +// Solidity: function readData(uint256 key) view returns(uint256) +func (_Dummy *DummyCaller) ReadData(opts *bind.CallOpts, key *big.Int) (*big.Int, error) { + var out []interface{} + err := _Dummy.contract.Call(opts, &out, "readData", key) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// ReadData is a free data retrieval call binding the contract method 0x37ebbc03. +// +// Solidity: function readData(uint256 key) view returns(uint256) +func (_Dummy *DummySession) ReadData(key *big.Int) (*big.Int, error) { + return _Dummy.Contract.ReadData(&_Dummy.CallOpts, key) +} + +// ReadData is a free data retrieval call binding the contract method 0x37ebbc03. +// +// Solidity: function readData(uint256 key) view returns(uint256) +func (_Dummy *DummyCallerSession) ReadData(key *big.Int) (*big.Int, error) { + return _Dummy.Contract.ReadData(&_Dummy.CallOpts, key) +} + +// Value is a free data retrieval call binding the contract method 0x3fa4f245. +// +// Solidity: function value() view returns(uint256) +func (_Dummy *DummyCaller) Value(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _Dummy.contract.Call(opts, &out, "value") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Value is a free data retrieval call binding the contract method 0x3fa4f245. +// +// Solidity: function value() view returns(uint256) +func (_Dummy *DummySession) Value() (*big.Int, error) { + return _Dummy.Contract.Value(&_Dummy.CallOpts) +} + +// Value is a free data retrieval call binding the contract method 0x3fa4f245. +// +// Solidity: function value() view returns(uint256) +func (_Dummy *DummyCallerSession) Value() (*big.Int, error) { + return _Dummy.Contract.Value(&_Dummy.CallOpts) +} + +// UpdateValue is a paid mutator transaction binding the contract method 0x573c0bd3. +// +// Solidity: function updateValue(uint256 newValue) returns() +func (_Dummy *DummyTransactor) UpdateValue(opts *bind.TransactOpts, newValue *big.Int) (*types.Transaction, error) { + return _Dummy.contract.Transact(opts, "updateValue", newValue) +} + +// UpdateValue is a paid mutator transaction binding the contract method 0x573c0bd3. +// +// Solidity: function updateValue(uint256 newValue) returns() +func (_Dummy *DummySession) UpdateValue(newValue *big.Int) (*types.Transaction, error) { + return _Dummy.Contract.UpdateValue(&_Dummy.TransactOpts, newValue) +} + +// UpdateValue is a paid mutator transaction binding the contract method 0x573c0bd3. +// +// Solidity: function updateValue(uint256 newValue) returns() +func (_Dummy *DummyTransactorSession) UpdateValue(newValue *big.Int) (*types.Transaction, error) { + return _Dummy.Contract.UpdateValue(&_Dummy.TransactOpts, newValue) +} + +// WriteData is a paid mutator transaction binding the contract method 0xc71ba63b. +// +// Solidity: function writeData(uint256 key, uint256 val) returns() +func (_Dummy *DummyTransactor) WriteData(opts *bind.TransactOpts, key *big.Int, val *big.Int) (*types.Transaction, error) { + return _Dummy.contract.Transact(opts, "writeData", key, val) +} + +// WriteData is a paid mutator transaction binding the contract method 0xc71ba63b. +// +// Solidity: function writeData(uint256 key, uint256 val) returns() +func (_Dummy *DummySession) WriteData(key *big.Int, val *big.Int) (*types.Transaction, error) { + return _Dummy.Contract.WriteData(&_Dummy.TransactOpts, key, val) +} + +// WriteData is a paid mutator transaction binding the contract method 0xc71ba63b. +// +// Solidity: function writeData(uint256 key, uint256 val) returns() +func (_Dummy *DummyTransactorSession) WriteData(key *big.Int, val *big.Int) (*types.Transaction, error) { + return _Dummy.Contract.WriteData(&_Dummy.TransactOpts, key, val) +} + +// DummyDataWrittenIterator is returned from FilterDataWritten and is used to iterate over the raw logs and unpacked data for DataWritten events raised by the Dummy contract. +type DummyDataWrittenIterator struct { + Event *DummyDataWritten // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *DummyDataWrittenIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(DummyDataWritten) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(DummyDataWritten) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *DummyDataWrittenIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *DummyDataWrittenIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// DummyDataWritten represents a DataWritten event raised by the Dummy contract. +type DummyDataWritten struct { + Key *big.Int + Value *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterDataWritten is a free log retrieval operation binding the contract event 0x36c0e38a11934bb6e80e00c4ae42212be021022fdb5aff12c53720f1d951c281. +// +// Solidity: event DataWritten(uint256 key, uint256 value) +func (_Dummy *DummyFilterer) FilterDataWritten(opts *bind.FilterOpts) (*DummyDataWrittenIterator, error) { + + logs, sub, err := _Dummy.contract.FilterLogs(opts, "DataWritten") + if err != nil { + return nil, err + } + return &DummyDataWrittenIterator{contract: _Dummy.contract, event: "DataWritten", logs: logs, sub: sub}, nil +} + +// WatchDataWritten is a free log subscription operation binding the contract event 0x36c0e38a11934bb6e80e00c4ae42212be021022fdb5aff12c53720f1d951c281. +// +// Solidity: event DataWritten(uint256 key, uint256 value) +func (_Dummy *DummyFilterer) WatchDataWritten(opts *bind.WatchOpts, sink chan<- *DummyDataWritten) (event.Subscription, error) { + + logs, sub, err := _Dummy.contract.WatchLogs(opts, "DataWritten") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(DummyDataWritten) + if err := _Dummy.contract.UnpackLog(event, "DataWritten", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseDataWritten is a log parse operation binding the contract event 0x36c0e38a11934bb6e80e00c4ae42212be021022fdb5aff12c53720f1d951c281. +// +// Solidity: event DataWritten(uint256 key, uint256 value) +func (_Dummy *DummyFilterer) ParseDataWritten(log types.Log) (*DummyDataWritten, error) { + event := new(DummyDataWritten) + if err := _Dummy.contract.UnpackLog(event, "DataWritten", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// DummyValueUpdatedIterator is returned from FilterValueUpdated and is used to iterate over the raw logs and unpacked data for ValueUpdated events raised by the Dummy contract. +type DummyValueUpdatedIterator struct { + Event *DummyValueUpdated // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *DummyValueUpdatedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(DummyValueUpdated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(DummyValueUpdated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *DummyValueUpdatedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *DummyValueUpdatedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// DummyValueUpdated represents a ValueUpdated event raised by the Dummy contract. +type DummyValueUpdated struct { + NewValue *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterValueUpdated is a free log retrieval operation binding the contract event 0x4273d0736f60e0dedfe745e86718093d8ec8646ebd2a60cd60643eeced565811. +// +// Solidity: event ValueUpdated(uint256 newValue) +func (_Dummy *DummyFilterer) FilterValueUpdated(opts *bind.FilterOpts) (*DummyValueUpdatedIterator, error) { + + logs, sub, err := _Dummy.contract.FilterLogs(opts, "ValueUpdated") + if err != nil { + return nil, err + } + return &DummyValueUpdatedIterator{contract: _Dummy.contract, event: "ValueUpdated", logs: logs, sub: sub}, nil +} + +// WatchValueUpdated is a free log subscription operation binding the contract event 0x4273d0736f60e0dedfe745e86718093d8ec8646ebd2a60cd60643eeced565811. +// +// Solidity: event ValueUpdated(uint256 newValue) +func (_Dummy *DummyFilterer) WatchValueUpdated(opts *bind.WatchOpts, sink chan<- *DummyValueUpdated) (event.Subscription, error) { + + logs, sub, err := _Dummy.contract.WatchLogs(opts, "ValueUpdated") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(DummyValueUpdated) + if err := _Dummy.contract.UnpackLog(event, "ValueUpdated", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseValueUpdated is a log parse operation binding the contract event 0x4273d0736f60e0dedfe745e86718093d8ec8646ebd2a60cd60643eeced565811. +// +// Solidity: event ValueUpdated(uint256 newValue) +func (_Dummy *DummyFilterer) ParseValueUpdated(log types.Log) (*DummyValueUpdated, error) { + event := new(DummyValueUpdated) + if err := _Dummy.contract.UnpackLog(event, "ValueUpdated", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} diff --git a/tests/load/c/contracts/Dummy.sol b/tests/load/c/contracts/Dummy.sol new file mode 100644 index 000000000000..ad55ca23bfef --- /dev/null +++ b/tests/load/c/contracts/Dummy.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +/// @notice Dummy is a contract used only for simulating the load +/// of contract creation operations in the EVM. It has state variables, +/// a constructor and simple functions to have a real-like deployed size. +contract Dummy { + uint256 public value; + address public owner; + mapping(uint256 => uint256) public data; + + event ValueUpdated(uint256 newValue); + event DataWritten(uint256 key, uint256 value); + + constructor() { + value = 42; + owner = msg.sender; + } + + function updateValue(uint256 newValue) external { + value = newValue; + emit ValueUpdated(newValue); + } + + function writeData(uint256 key, uint256 val) external { + data[key] = val; + emit DataWritten(key, val); + } + + function readData(uint256 key) external view returns (uint256) { + return data[key]; + } +} diff --git a/tests/load/c/contracts/EVMLoadSimulator.bindings.go b/tests/load/c/contracts/EVMLoadSimulator.bindings.go index 3e90bf9184ea..02679ea82937 100644 --- a/tests/load/c/contracts/EVMLoadSimulator.bindings.go +++ b/tests/load/c/contracts/EVMLoadSimulator.bindings.go @@ -31,8 +31,8 @@ var ( // EVMLoadSimulatorMetaData contains all meta data concerning the EVMLoadSimulator contract. var EVMLoadSimulatorMetaData = &bind.MetaData{ - ABI: "[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"hash\",\"type\":\"bytes32\"}],\"name\":\"HashCalculates\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"arr\",\"type\":\"uint256[]\"}],\"name\":\"MemoryWritten\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"accountId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"StorageUpdate\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"sum\",\"type\":\"uint256\"}],\"name\":\"SumCalculated\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"balancesCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"depth\",\"type\":\"uint256\"}],\"name\":\"simulateCallDepth\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"rounds\",\"type\":\"uint256\"}],\"name\":\"simulateHashing\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"hash\",\"type\":\"bytes32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"sizeInWords\",\"type\":\"uint256\"}],\"name\":\"simulateMemory\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"sum\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"count\",\"type\":\"uint256\"}],\"name\":\"simulateModification\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"count\",\"type\":\"uint256\"}],\"name\":\"simulateRandomWrite\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"count\",\"type\":\"uint256\"}],\"name\":\"simulateReads\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"sum\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x6080604052348015600e575f5ffd5b5061097b8061001c5f395ff3fe608060405234801561000f575f5ffd5b506004361061007b575f3560e01c8063aae05a6511610059578063aae05a65146100e9578063b77513d114610105578063f05ed79e14610121578063fb0c0012146101515761007b565b80633851d6e71461007f578063542eedd91461009d5780637db6ecb1146100b9575b5f5ffd5b610087610181565b60405161009491906105c4565b60405180910390f35b6100b760048036038101906100b2919061060b565b610187565b005b6100d360048036038101906100ce919061060b565b610205565b6040516100e0919061064e565b60405180910390f35b61010360048036038101906100fe919061060b565b6102af565b005b61011f600480360381019061011a919061060b565b6103b8565b005b61013b6004803603810190610136919061060b565b610443565b60405161014891906105c4565b60405180910390f35b61016b6004803603810190610166919061060b565b610530565b60405161017891906105c4565b60405180910390f35b60015481565b5f811115610202573073ffffffffffffffffffffffffffffffffffffffff1663542eedd96001836101b89190610694565b6040518263ffffffff1660e01b81526004016101d491906105c4565b5f604051808303815f87803b1580156101eb575f5ffd5b505af11580156101fd573d5f5f3e3d5ffd5b505050505b50565b5f6040516020016102159061071b565b6040516020818303038152906040528051906020012090505f5f90505b8281101561027257818160405160200161024d92919061076f565b6040516020818303038152906040528051906020012091508080600101915050610232565b507f30ca2ef0880ae63712fdaf11aefb67752968cff6f845956fcbdfcf421f4647cb816040516102a2919061064e565b60405180910390a1919050565b5f600190505b8181116103b457600154811015610339575f60015f5f8481526020019081526020015f20546102e4919061079a565b9050805f5f8481526020019081526020015f2081905550817fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc88260405161032b91906105c4565b60405180910390a2506103a1565b5f60015f81548092919061034c906107cd565b919050559050815f5f8381526020019081526020015f2081905550807fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc88360405161039791906105c4565b60405180910390a2505b80806103ac906107cd565b9150506102b5565b5050565b5f600190505b81811161043f575f60015f8154809291906103d8906107cd565b919050559050815f5f8381526020019081526020015f2081905550807fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc88360405161042391906105c4565b60405180910390a2508080610437906107cd565b9150506103be565b5050565b5f5f8267ffffffffffffffff81111561045f5761045e610814565b5b60405190808252806020026020018201604052801561048d5781602001602082028036833780820191505090505b5090505f5f90505b838110156104f257808282815181106104b1576104b0610841565b5b6020026020010181815250508181815181106104d0576104cf610841565b5b6020026020010151836104e3919061079a565b92508080600101915050610495565b507f542a9e74627abe4fb012aa9be028f3234ff2b2253530c6fa2220e29f03e4215d816040516105229190610925565b60405180910390a150919050565b5f5f600190505b82811161056f575f5f8281526020019081526020015f20548261055a919061079a565b91508080610567906107cd565b915050610537565b507fe32d91cad5061d7491327c51e7b799c677b41d033204a5c5022b120f5da4becb8160405161059f91906105c4565b60405180910390a1919050565b5f819050919050565b6105be816105ac565b82525050565b5f6020820190506105d75f8301846105b5565b92915050565b5f5ffd5b6105ea816105ac565b81146105f4575f5ffd5b50565b5f81359050610605816105e1565b92915050565b5f602082840312156106205761061f6105dd565b5b5f61062d848285016105f7565b91505092915050565b5f819050919050565b61064881610636565b82525050565b5f6020820190506106615f83018461063f565b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f61069e826105ac565b91506106a9836105ac565b92508282039050818111156106c1576106c0610667565b5b92915050565b5f81905092915050565b7f696e697469616c000000000000000000000000000000000000000000000000005f82015250565b5f6107056007836106c7565b9150610710826106d1565b600782019050919050565b5f610725826106f9565b9150819050919050565b5f819050919050565b61074961074482610636565b61072f565b82525050565b5f819050919050565b610769610764826105ac565b61074f565b82525050565b5f61077a8285610738565b60208201915061078a8284610758565b6020820191508190509392505050565b5f6107a4826105ac565b91506107af836105ac565b92508282019050808211156107c7576107c6610667565b5b92915050565b5f6107d7826105ac565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361080957610808610667565b5b600182019050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b6108a0816105ac565b82525050565b5f6108b18383610897565b60208301905092915050565b5f602082019050919050565b5f6108d38261086e565b6108dd8185610878565b93506108e883610888565b805f5b838110156109185781516108ff88826108a6565b975061090a836108bd565b9250506001810190506108eb565b5085935050505092915050565b5f6020820190508181035f83015261093d81846108c9565b90509291505056fea2646970667358221220bfdf2ebb58e30b7078b5899d8b6202d94fdc2296ad4fba0105d08e5004339dff64736f6c634300081d0033", + ABI: "[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"hash\",\"type\":\"bytes32\"}],\"name\":\"HashCalculates\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"arr\",\"type\":\"uint256[]\"}],\"name\":\"MemoryWritten\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"accountId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"StorageUpdate\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"sum\",\"type\":\"uint256\"}],\"name\":\"SumCalculated\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"balancesCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"depth\",\"type\":\"uint256\"}],\"name\":\"simulateCallDepth\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"simulateContractCreation\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"rounds\",\"type\":\"uint256\"}],\"name\":\"simulateHashing\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"hash\",\"type\":\"bytes32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"sizeInWords\",\"type\":\"uint256\"}],\"name\":\"simulateMemory\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"sum\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"count\",\"type\":\"uint256\"}],\"name\":\"simulateModification\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"count\",\"type\":\"uint256\"}],\"name\":\"simulateRandomWrite\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"count\",\"type\":\"uint256\"}],\"name\":\"simulateReads\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"sum\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x6080604052348015600e575f5ffd5b50610e618061001c5f395ff3fe608060405234801561000f575f5ffd5b5060043610610086575f3560e01c8063aae05a6511610059578063aae05a65146100fe578063b77513d11461011a578063f05ed79e14610136578063fb0c00121461016657610086565b80633851d6e71461008a578063542eedd9146100a85780635de583ef146100c45780637db6ecb1146100ce575b5f5ffd5b610092610196565b60405161009f919061064b565b60405180910390f35b6100c260048036038101906100bd9190610692565b61019c565b005b6100cc610256565b005b6100e860048036038101906100e39190610692565b61027f565b6040516100f591906106d5565b60405180910390f35b61011860048036038101906101139190610692565b610329565b005b610134600480360381019061012f9190610692565b610432565b005b610150600480360381019061014b9190610692565b6104bd565b60405161015d919061064b565b60405180910390f35b610180600480360381019061017b9190610692565b6105aa565b60405161018d919061064b565b60405180910390f35b60015481565b5f81036101e0575f7fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc85f6040516101d39190610730565b60405180910390a2610253565b3073ffffffffffffffffffffffffffffffffffffffff1663542eedd96001836102099190610776565b6040518263ffffffff1660e01b8152600401610225919061064b565b5f604051808303815f87803b15801561023c575f5ffd5b505af115801561024e573d5f5f3e3d5ffd5b505050505b50565b60405161026290610626565b604051809103905ff08015801561027b573d5f5f3e3d5ffd5b5050565b5f60405160200161028f906107fd565b6040516020818303038152906040528051906020012090505f5f90505b828110156102ec5781816040516020016102c7929190610851565b60405160208183030381529060405280519060200120915080806001019150506102ac565b507f30ca2ef0880ae63712fdaf11aefb67752968cff6f845956fcbdfcf421f4647cb8160405161031c91906106d5565b60405180910390a1919050565b5f600190505b81811161042e576001548110156103b3575f60015f5f8481526020019081526020015f205461035e919061087c565b9050805f5f8481526020019081526020015f2081905550817fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc8826040516103a5919061064b565b60405180910390a25061041b565b5f60015f8154809291906103c6906108af565b919050559050815f5f8381526020019081526020015f2081905550807fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc883604051610411919061064b565b60405180910390a2505b8080610426906108af565b91505061032f565b5050565b5f600190505b8181116104b9575f60015f815480929190610452906108af565b919050559050815f5f8381526020019081526020015f2081905550807fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc88360405161049d919061064b565b60405180910390a25080806104b1906108af565b915050610438565b5050565b5f5f8267ffffffffffffffff8111156104d9576104d86108f6565b5b6040519080825280602002602001820160405280156105075781602001602082028036833780820191505090505b5090505f5f90505b8381101561056c578082828151811061052b5761052a610923565b5b60200260200101818152505081818151811061054a57610549610923565b5b60200260200101518361055d919061087c565b9250808060010191505061050f565b507f542a9e74627abe4fb012aa9be028f3234ff2b2253530c6fa2220e29f03e4215d8160405161059c9190610a07565b60405180910390a150919050565b5f5f600190505b8281116105e9575f5f8281526020019081526020015f2054826105d4919061087c565b915080806105e1906108af565b9150506105b1565b507fe32d91cad5061d7491327c51e7b799c677b41d033204a5c5022b120f5da4becb81604051610619919061064b565b60405180910390a1919050565b61040480610a2883390190565b5f819050919050565b61064581610633565b82525050565b5f60208201905061065e5f83018461063c565b92915050565b5f5ffd5b61067181610633565b811461067b575f5ffd5b50565b5f8135905061068c81610668565b92915050565b5f602082840312156106a7576106a6610664565b5b5f6106b48482850161067e565b91505092915050565b5f819050919050565b6106cf816106bd565b82525050565b5f6020820190506106e85f8301846106c6565b92915050565b5f819050919050565b5f819050919050565b5f61071a610715610710846106ee565b6106f7565b610633565b9050919050565b61072a81610700565b82525050565b5f6020820190506107435f830184610721565b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f61078082610633565b915061078b83610633565b92508282039050818111156107a3576107a2610749565b5b92915050565b5f81905092915050565b7f696e697469616c000000000000000000000000000000000000000000000000005f82015250565b5f6107e76007836107a9565b91506107f2826107b3565b600782019050919050565b5f610807826107db565b9150819050919050565b5f819050919050565b61082b610826826106bd565b610811565b82525050565b5f819050919050565b61084b61084682610633565b610831565b82525050565b5f61085c828561081a565b60208201915061086c828461083a565b6020820191508190509392505050565b5f61088682610633565b915061089183610633565b92508282019050808211156108a9576108a8610749565b5b92915050565b5f6108b982610633565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036108eb576108ea610749565b5b600182019050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b61098281610633565b82525050565b5f6109938383610979565b60208301905092915050565b5f602082019050919050565b5f6109b582610950565b6109bf818561095a565b93506109ca8361096a565b805f5b838110156109fa5781516109e18882610988565b97506109ec8361099f565b9250506001810190506109cd565b5085935050505092915050565b5f6020820190508181035f830152610a1f81846109ab565b90509291505056fe6080604052348015600e575f5ffd5b50602a5f819055503360015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506103a1806100635f395ff3fe608060405234801561000f575f5ffd5b5060043610610060575f3560e01c806337ebbc03146100645780633fa4f24514610094578063573c0bd3146100b25780638da5cb5b146100ce578063c71ba63b146100ec578063f0ba844014610108575b5f5ffd5b61007e6004803603810190610079919061025b565b610138565b60405161008b9190610295565b60405180910390f35b61009c610152565b6040516100a99190610295565b60405180910390f35b6100cc60048036038101906100c7919061025b565b610157565b005b6100d6610197565b6040516100e391906102ed565b60405180910390f35b61010660048036038101906101019190610306565b6101bc565b005b610122600480360381019061011d919061025b565b61020f565b60405161012f9190610295565b60405180910390f35b5f60025f8381526020019081526020015f20549050919050565b5f5481565b805f819055507f4273d0736f60e0dedfe745e86718093d8ec8646ebd2a60cd60643eeced5658118160405161018c9190610295565b60405180910390a150565b60015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b8060025f8481526020019081526020015f20819055507f36c0e38a11934bb6e80e00c4ae42212be021022fdb5aff12c53720f1d951c2818282604051610203929190610344565b60405180910390a15050565b6002602052805f5260405f205f915090505481565b5f5ffd5b5f819050919050565b61023a81610228565b8114610244575f5ffd5b50565b5f8135905061025581610231565b92915050565b5f602082840312156102705761026f610224565b5b5f61027d84828501610247565b91505092915050565b61028f81610228565b82525050565b5f6020820190506102a85f830184610286565b92915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6102d7826102ae565b9050919050565b6102e7816102cd565b82525050565b5f6020820190506103005f8301846102de565b92915050565b5f5f6040838503121561031c5761031b610224565b5b5f61032985828601610247565b925050602061033a85828601610247565b9150509250929050565b5f6040820190506103575f830185610286565b6103646020830184610286565b939250505056fea2646970667358221220903ff8ac6aec2e45b8ca86cbb59be29b3dcfa00f77033329976bbcf33ba63e7064736f6c634300081d0033a2646970667358221220ba27fff9501a2d06900cf30dbc92efde59d9d050f4b8c4f9c08aa87c5ad5fbde64736f6c634300081d0033", } // EVMLoadSimulatorABI is the input ABI used to generate the binding from. @@ -254,6 +254,27 @@ func (_EVMLoadSimulator *EVMLoadSimulatorTransactorSession) SimulateCallDepth(de return _EVMLoadSimulator.Contract.SimulateCallDepth(&_EVMLoadSimulator.TransactOpts, depth) } +// SimulateContractCreation is a paid mutator transaction binding the contract method 0x5de583ef. +// +// Solidity: function simulateContractCreation() returns() +func (_EVMLoadSimulator *EVMLoadSimulatorTransactor) SimulateContractCreation(opts *bind.TransactOpts) (*types.Transaction, error) { + return _EVMLoadSimulator.contract.Transact(opts, "simulateContractCreation") +} + +// SimulateContractCreation is a paid mutator transaction binding the contract method 0x5de583ef. +// +// Solidity: function simulateContractCreation() returns() +func (_EVMLoadSimulator *EVMLoadSimulatorSession) SimulateContractCreation() (*types.Transaction, error) { + return _EVMLoadSimulator.Contract.SimulateContractCreation(&_EVMLoadSimulator.TransactOpts) +} + +// SimulateContractCreation is a paid mutator transaction binding the contract method 0x5de583ef. +// +// Solidity: function simulateContractCreation() returns() +func (_EVMLoadSimulator *EVMLoadSimulatorTransactorSession) SimulateContractCreation() (*types.Transaction, error) { + return _EVMLoadSimulator.Contract.SimulateContractCreation(&_EVMLoadSimulator.TransactOpts) +} + // SimulateHashing is a paid mutator transaction binding the contract method 0x7db6ecb1. // // Solidity: function simulateHashing(uint256 rounds) returns(bytes32 hash) diff --git a/tests/load/c/contracts/EVMLoadSimulator.sol b/tests/load/c/contracts/EVMLoadSimulator.sol index b00a6bc58ec5..2c76566b7075 100644 --- a/tests/load/c/contracts/EVMLoadSimulator.sol +++ b/tests/load/c/contracts/EVMLoadSimulator.sol @@ -1,6 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.24; +import {Dummy} from "./Dummy.sol"; + contract EVMLoadSimulator { // Storage mappings for read/write simulations mapping(uint256 => uint256) private balances; @@ -73,4 +75,8 @@ contract EVMLoadSimulator { } this.simulateCallDepth(depth - 1); } + + function simulateContractCreation() external { + new Dummy(); + } } diff --git a/tests/load/c/contracts/generate_abi_bindings.sh b/tests/load/c/contracts/generate_abi_bindings.sh index e0ec5b15246e..d3e7ecb5581c 100755 --- a/tests/load/c/contracts/generate_abi_bindings.sh +++ b/tests/load/c/contracts/generate_abi_bindings.sh @@ -14,17 +14,17 @@ if ! command -v solc &> /dev/null; then fi CONTRACTS_DIR="$(dirname "$0")" - +TEMPDIR=$(mktemp -d) for FILE in "${CONTRACTS_DIR}"/*.sol; do echo "Generating Go bindings from Solidity contract $FILE..." CONTRACT_NAME=$(basename "$FILE" .sol) - solc --abi --bin --overwrite -o "$CONTRACTS_DIR" "${CONTRACTS_DIR}/${CONTRACT_NAME}.sol" + solc --abi --bin --overwrite -o "$TEMPDIR" "${CONTRACTS_DIR}/${CONTRACT_NAME}.sol" go run github.com/ava-labs/libevm/cmd/abigen@latest \ - --bin="${CONTRACTS_DIR}/${CONTRACT_NAME}.bin" \ - --abi="${CONTRACTS_DIR}/${CONTRACT_NAME}.abi" \ + --bin="${TEMPDIR}/${CONTRACT_NAME}.bin" \ + --abi="${TEMPDIR}/${CONTRACT_NAME}.abi" \ --type $CONTRACT_NAME \ --pkg=contracts \ --out="${CONTRACTS_DIR}/${CONTRACT_NAME}.bindings.go" - rm "${CONTRACTS_DIR}/${CONTRACT_NAME}.bin" "${CONTRACTS_DIR}/${CONTRACT_NAME}.abi" echo "Generated ${CONTRACT_NAME}.bindings.go" done +rm -r "${TEMPDIR}" diff --git a/tests/load/c/issuers/opcode.go b/tests/load/c/issuers/opcode.go index c2b11a25cf1b..138ef49dfceb 100644 --- a/tests/load/c/issuers/opcode.go +++ b/tests/load/c/issuers/opcode.go @@ -122,6 +122,8 @@ func (o *Opcoder) GenerateAndIssueTx(ctx context.Context) (common.Hash, error) { const maxDepth = 5 depth := big.NewInt(rand.Int64N(maxDepth)) //nolint:gosec tx, err = o.contractInstance.SimulateCallDepth(txOpts, depth) + case contractCreation: + tx, err = o.contractInstance.SimulateContractCreation(txOpts) default: return common.Hash{}, fmt.Errorf("invalid load type: %s", loadType) } @@ -144,6 +146,7 @@ const ( hashing = "hashing" memory = "memory" callDepth = "call depth" + contractCreation = "contract creation" ) func allLoadTypes() []string { @@ -154,6 +157,7 @@ func allLoadTypes() []string { hashing, memory, callDepth, + contractCreation, } } From 4990e2446041aea255afd6ea87e80191f0215310 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Fri, 16 May 2025 13:24:46 +0200 Subject: [PATCH 108/197] Add solhint to contract bindings generation script --- flake.nix | 3 +++ tests/load/c/contracts/.solhint.json | 11 +++++++++++ tests/load/c/contracts/generate_abi_bindings.sh | 6 ++++++ 3 files changed, 20 insertions(+) create mode 100644 tests/load/c/contracts/.solhint.json diff --git a/flake.nix b/flake.nix index a3d1e647db3e..28172cddd55d 100644 --- a/flake.nix +++ b/flake.nix @@ -56,6 +56,9 @@ buf protoc-gen-go protoc-gen-go-grpc + + # Solidity + solhint ] ++ lib.optionals stdenv.isDarwin [ # macOS-specific frameworks darwin.apple_sdk.frameworks.Security diff --git a/tests/load/c/contracts/.solhint.json b/tests/load/c/contracts/.solhint.json new file mode 100644 index 000000000000..5ee631cab545 --- /dev/null +++ b/tests/load/c/contracts/.solhint.json @@ -0,0 +1,11 @@ +{ + "extends": "solhint:recommended", + "rules": { + "func-visibility": [ + "warn", + { + "ignoreConstructors": true + } + ] + } +} \ No newline at end of file diff --git a/tests/load/c/contracts/generate_abi_bindings.sh b/tests/load/c/contracts/generate_abi_bindings.sh index d3e7ecb5581c..d6d4b386058b 100755 --- a/tests/load/c/contracts/generate_abi_bindings.sh +++ b/tests/load/c/contracts/generate_abi_bindings.sh @@ -13,9 +13,15 @@ if ! command -v solc &> /dev/null; then fi fi +if ! command -v solhint &> /dev/null; then + echo "Error: solhint not found. Run this command within Nix shell." + exit 1 +fi + CONTRACTS_DIR="$(dirname "$0")" TEMPDIR=$(mktemp -d) for FILE in "${CONTRACTS_DIR}"/*.sol; do + solhint --config ${CONTRACTS_DIR}/.solhint.json "${FILE}" echo "Generating Go bindings from Solidity contract $FILE..." CONTRACT_NAME=$(basename "$FILE" .sol) solc --abi --bin --overwrite -o "$TEMPDIR" "${CONTRACTS_DIR}/${CONTRACT_NAME}.sol" From b86e9040954495503bce44a21e981d8b09fc3517 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Fri, 16 May 2025 13:24:57 +0200 Subject: [PATCH 109/197] Fix orchestrator bug --- tests/load/orchestrator.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/load/orchestrator.go b/tests/load/orchestrator.go index c3309fdf365d..56c071b3dcc9 100644 --- a/tests/load/orchestrator.go +++ b/tests/load/orchestrator.go @@ -265,7 +265,14 @@ func (o *Orchestrator[_]) issueTxs(ctx context.Context, currTargetTPS *atomic.In return nil //nolint:nilerr } currTime := time.Now() - txsPerIssuer := uint64(math.Ceil(float64(currTargetTPS.Load())/float64(len(o.agents))) * o.config.TxRateMultiplier) + targetTPSPerAgent := math.Ceil(float64(currTargetTPS.Load()) / float64(len(o.agents))) + txsPerIssuer := uint64(targetTPSPerAgent * o.config.TxRateMultiplier) + if txsPerIssuer == uint64(targetTPSPerAgent) { + // For low target TPS and rate multiplier, the rate multiplier can have no effect. + // For example, for 50 TPS target, 5 agents, and rate multiplier of 1.05, we have + // uint64(10*1.05) = 10 therefore we add 1 to force an increase in txs issued. + txsPerIssuer++ + } // always listen until listener context is cancelled, do not call agent.Listener.IssuingDone(). for range txsPerIssuer { tx, err := agent.Issuer.GenerateAndIssueTx(ctx) From 0521a48807af620cabe7a961407994dc8ee2abd4 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Fri, 16 May 2025 13:45:10 +0200 Subject: [PATCH 110/197] Add pure compute contract function --- tests/load/c/contracts/Dummy.bindings.go | 2 +- .../c/contracts/EVMLoadSimulator.bindings.go | 25 +++++++++++++++++-- tests/load/c/contracts/EVMLoadSimulator.sol | 12 +++++++++ tests/load/c/issuers/opcode.go | 5 ++++ 4 files changed, 41 insertions(+), 3 deletions(-) diff --git a/tests/load/c/contracts/Dummy.bindings.go b/tests/load/c/contracts/Dummy.bindings.go index b35dba2e8cc3..143beece614e 100644 --- a/tests/load/c/contracts/Dummy.bindings.go +++ b/tests/load/c/contracts/Dummy.bindings.go @@ -32,7 +32,7 @@ var ( // DummyMetaData contains all meta data concerning the Dummy contract. var DummyMetaData = &bind.MetaData{ ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"key\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"DataWritten\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newValue\",\"type\":\"uint256\"}],\"name\":\"ValueUpdated\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"data\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"key\",\"type\":\"uint256\"}],\"name\":\"readData\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newValue\",\"type\":\"uint256\"}],\"name\":\"updateValue\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"value\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"key\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"val\",\"type\":\"uint256\"}],\"name\":\"writeData\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x6080604052348015600e575f5ffd5b50602a5f819055503360015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506103a1806100635f395ff3fe608060405234801561000f575f5ffd5b5060043610610060575f3560e01c806337ebbc03146100645780633fa4f24514610094578063573c0bd3146100b25780638da5cb5b146100ce578063c71ba63b146100ec578063f0ba844014610108575b5f5ffd5b61007e6004803603810190610079919061025b565b610138565b60405161008b9190610295565b60405180910390f35b61009c610152565b6040516100a99190610295565b60405180910390f35b6100cc60048036038101906100c7919061025b565b610157565b005b6100d6610197565b6040516100e391906102ed565b60405180910390f35b61010660048036038101906101019190610306565b6101bc565b005b610122600480360381019061011d919061025b565b61020f565b60405161012f9190610295565b60405180910390f35b5f60025f8381526020019081526020015f20549050919050565b5f5481565b805f819055507f4273d0736f60e0dedfe745e86718093d8ec8646ebd2a60cd60643eeced5658118160405161018c9190610295565b60405180910390a150565b60015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b8060025f8481526020019081526020015f20819055507f36c0e38a11934bb6e80e00c4ae42212be021022fdb5aff12c53720f1d951c2818282604051610203929190610344565b60405180910390a15050565b6002602052805f5260405f205f915090505481565b5f5ffd5b5f819050919050565b61023a81610228565b8114610244575f5ffd5b50565b5f8135905061025581610231565b92915050565b5f602082840312156102705761026f610224565b5b5f61027d84828501610247565b91505092915050565b61028f81610228565b82525050565b5f6020820190506102a85f830184610286565b92915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6102d7826102ae565b9050919050565b6102e7816102cd565b82525050565b5f6020820190506103005f8301846102de565b92915050565b5f5f6040838503121561031c5761031b610224565b5b5f61032985828601610247565b925050602061033a85828601610247565b9150509250929050565b5f6040820190506103575f830185610286565b6103646020830184610286565b939250505056fea2646970667358221220903ff8ac6aec2e45b8ca86cbb59be29b3dcfa00f77033329976bbcf33ba63e7064736f6c634300081d0033", + Bin: "0x6080604052348015600e575f5ffd5b50602a5f819055503360015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506103a1806100635f395ff3fe608060405234801561000f575f5ffd5b5060043610610060575f3560e01c806337ebbc03146100645780633fa4f24514610094578063573c0bd3146100b25780638da5cb5b146100ce578063c71ba63b146100ec578063f0ba844014610108575b5f5ffd5b61007e6004803603810190610079919061025b565b610138565b60405161008b9190610295565b60405180910390f35b61009c610152565b6040516100a99190610295565b60405180910390f35b6100cc60048036038101906100c7919061025b565b610157565b005b6100d6610197565b6040516100e391906102ed565b60405180910390f35b61010660048036038101906101019190610306565b6101bc565b005b610122600480360381019061011d919061025b565b61020f565b60405161012f9190610295565b60405180910390f35b5f60025f8381526020019081526020015f20549050919050565b5f5481565b805f819055507f4273d0736f60e0dedfe745e86718093d8ec8646ebd2a60cd60643eeced5658118160405161018c9190610295565b60405180910390a150565b60015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b8060025f8481526020019081526020015f20819055507f36c0e38a11934bb6e80e00c4ae42212be021022fdb5aff12c53720f1d951c2818282604051610203929190610344565b60405180910390a15050565b6002602052805f5260405f205f915090505481565b5f5ffd5b5f819050919050565b61023a81610228565b8114610244575f5ffd5b50565b5f8135905061025581610231565b92915050565b5f602082840312156102705761026f610224565b5b5f61027d84828501610247565b91505092915050565b61028f81610228565b82525050565b5f6020820190506102a85f830184610286565b92915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6102d7826102ae565b9050919050565b6102e7816102cd565b82525050565b5f6020820190506103005f8301846102de565b92915050565b5f5f6040838503121561031c5761031b610224565b5b5f61032985828601610247565b925050602061033a85828601610247565b9150509250929050565b5f6040820190506103575f830185610286565b6103646020830184610286565b939250505056fea26469706673582212209dac5c694a03c04f90df0fab6ca0b8f6cc26a78f00312f518d7d99cd098e5ab764736f6c634300081d0033", } // DummyABI is the input ABI used to generate the binding from. diff --git a/tests/load/c/contracts/EVMLoadSimulator.bindings.go b/tests/load/c/contracts/EVMLoadSimulator.bindings.go index 02679ea82937..44c719b7b76c 100644 --- a/tests/load/c/contracts/EVMLoadSimulator.bindings.go +++ b/tests/load/c/contracts/EVMLoadSimulator.bindings.go @@ -31,8 +31,8 @@ var ( // EVMLoadSimulatorMetaData contains all meta data concerning the EVMLoadSimulator contract. var EVMLoadSimulatorMetaData = &bind.MetaData{ - ABI: "[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"hash\",\"type\":\"bytes32\"}],\"name\":\"HashCalculates\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"arr\",\"type\":\"uint256[]\"}],\"name\":\"MemoryWritten\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"accountId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"StorageUpdate\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"sum\",\"type\":\"uint256\"}],\"name\":\"SumCalculated\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"balancesCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"depth\",\"type\":\"uint256\"}],\"name\":\"simulateCallDepth\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"simulateContractCreation\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"rounds\",\"type\":\"uint256\"}],\"name\":\"simulateHashing\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"hash\",\"type\":\"bytes32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"sizeInWords\",\"type\":\"uint256\"}],\"name\":\"simulateMemory\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"sum\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"count\",\"type\":\"uint256\"}],\"name\":\"simulateModification\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"count\",\"type\":\"uint256\"}],\"name\":\"simulateRandomWrite\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"count\",\"type\":\"uint256\"}],\"name\":\"simulateReads\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"sum\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x6080604052348015600e575f5ffd5b50610e618061001c5f395ff3fe608060405234801561000f575f5ffd5b5060043610610086575f3560e01c8063aae05a6511610059578063aae05a65146100fe578063b77513d11461011a578063f05ed79e14610136578063fb0c00121461016657610086565b80633851d6e71461008a578063542eedd9146100a85780635de583ef146100c45780637db6ecb1146100ce575b5f5ffd5b610092610196565b60405161009f919061064b565b60405180910390f35b6100c260048036038101906100bd9190610692565b61019c565b005b6100cc610256565b005b6100e860048036038101906100e39190610692565b61027f565b6040516100f591906106d5565b60405180910390f35b61011860048036038101906101139190610692565b610329565b005b610134600480360381019061012f9190610692565b610432565b005b610150600480360381019061014b9190610692565b6104bd565b60405161015d919061064b565b60405180910390f35b610180600480360381019061017b9190610692565b6105aa565b60405161018d919061064b565b60405180910390f35b60015481565b5f81036101e0575f7fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc85f6040516101d39190610730565b60405180910390a2610253565b3073ffffffffffffffffffffffffffffffffffffffff1663542eedd96001836102099190610776565b6040518263ffffffff1660e01b8152600401610225919061064b565b5f604051808303815f87803b15801561023c575f5ffd5b505af115801561024e573d5f5f3e3d5ffd5b505050505b50565b60405161026290610626565b604051809103905ff08015801561027b573d5f5f3e3d5ffd5b5050565b5f60405160200161028f906107fd565b6040516020818303038152906040528051906020012090505f5f90505b828110156102ec5781816040516020016102c7929190610851565b60405160208183030381529060405280519060200120915080806001019150506102ac565b507f30ca2ef0880ae63712fdaf11aefb67752968cff6f845956fcbdfcf421f4647cb8160405161031c91906106d5565b60405180910390a1919050565b5f600190505b81811161042e576001548110156103b3575f60015f5f8481526020019081526020015f205461035e919061087c565b9050805f5f8481526020019081526020015f2081905550817fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc8826040516103a5919061064b565b60405180910390a25061041b565b5f60015f8154809291906103c6906108af565b919050559050815f5f8381526020019081526020015f2081905550807fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc883604051610411919061064b565b60405180910390a2505b8080610426906108af565b91505061032f565b5050565b5f600190505b8181116104b9575f60015f815480929190610452906108af565b919050559050815f5f8381526020019081526020015f2081905550807fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc88360405161049d919061064b565b60405180910390a25080806104b1906108af565b915050610438565b5050565b5f5f8267ffffffffffffffff8111156104d9576104d86108f6565b5b6040519080825280602002602001820160405280156105075781602001602082028036833780820191505090505b5090505f5f90505b8381101561056c578082828151811061052b5761052a610923565b5b60200260200101818152505081818151811061054a57610549610923565b5b60200260200101518361055d919061087c565b9250808060010191505061050f565b507f542a9e74627abe4fb012aa9be028f3234ff2b2253530c6fa2220e29f03e4215d8160405161059c9190610a07565b60405180910390a150919050565b5f5f600190505b8281116105e9575f5f8281526020019081526020015f2054826105d4919061087c565b915080806105e1906108af565b9150506105b1565b507fe32d91cad5061d7491327c51e7b799c677b41d033204a5c5022b120f5da4becb81604051610619919061064b565b60405180910390a1919050565b61040480610a2883390190565b5f819050919050565b61064581610633565b82525050565b5f60208201905061065e5f83018461063c565b92915050565b5f5ffd5b61067181610633565b811461067b575f5ffd5b50565b5f8135905061068c81610668565b92915050565b5f602082840312156106a7576106a6610664565b5b5f6106b48482850161067e565b91505092915050565b5f819050919050565b6106cf816106bd565b82525050565b5f6020820190506106e85f8301846106c6565b92915050565b5f819050919050565b5f819050919050565b5f61071a610715610710846106ee565b6106f7565b610633565b9050919050565b61072a81610700565b82525050565b5f6020820190506107435f830184610721565b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f61078082610633565b915061078b83610633565b92508282039050818111156107a3576107a2610749565b5b92915050565b5f81905092915050565b7f696e697469616c000000000000000000000000000000000000000000000000005f82015250565b5f6107e76007836107a9565b91506107f2826107b3565b600782019050919050565b5f610807826107db565b9150819050919050565b5f819050919050565b61082b610826826106bd565b610811565b82525050565b5f819050919050565b61084b61084682610633565b610831565b82525050565b5f61085c828561081a565b60208201915061086c828461083a565b6020820191508190509392505050565b5f61088682610633565b915061089183610633565b92508282019050808211156108a9576108a8610749565b5b92915050565b5f6108b982610633565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036108eb576108ea610749565b5b600182019050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b61098281610633565b82525050565b5f6109938383610979565b60208301905092915050565b5f602082019050919050565b5f6109b582610950565b6109bf818561095a565b93506109ca8361096a565b805f5b838110156109fa5781516109e18882610988565b97506109ec8361099f565b9250506001810190506109cd565b5085935050505092915050565b5f6020820190508181035f830152610a1f81846109ab565b90509291505056fe6080604052348015600e575f5ffd5b50602a5f819055503360015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506103a1806100635f395ff3fe608060405234801561000f575f5ffd5b5060043610610060575f3560e01c806337ebbc03146100645780633fa4f24514610094578063573c0bd3146100b25780638da5cb5b146100ce578063c71ba63b146100ec578063f0ba844014610108575b5f5ffd5b61007e6004803603810190610079919061025b565b610138565b60405161008b9190610295565b60405180910390f35b61009c610152565b6040516100a99190610295565b60405180910390f35b6100cc60048036038101906100c7919061025b565b610157565b005b6100d6610197565b6040516100e391906102ed565b60405180910390f35b61010660048036038101906101019190610306565b6101bc565b005b610122600480360381019061011d919061025b565b61020f565b60405161012f9190610295565b60405180910390f35b5f60025f8381526020019081526020015f20549050919050565b5f5481565b805f819055507f4273d0736f60e0dedfe745e86718093d8ec8646ebd2a60cd60643eeced5658118160405161018c9190610295565b60405180910390a150565b60015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b8060025f8481526020019081526020015f20819055507f36c0e38a11934bb6e80e00c4ae42212be021022fdb5aff12c53720f1d951c2818282604051610203929190610344565b60405180910390a15050565b6002602052805f5260405f205f915090505481565b5f5ffd5b5f819050919050565b61023a81610228565b8114610244575f5ffd5b50565b5f8135905061025581610231565b92915050565b5f602082840312156102705761026f610224565b5b5f61027d84828501610247565b91505092915050565b61028f81610228565b82525050565b5f6020820190506102a85f830184610286565b92915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6102d7826102ae565b9050919050565b6102e7816102cd565b82525050565b5f6020820190506103005f8301846102de565b92915050565b5f5f6040838503121561031c5761031b610224565b5b5f61032985828601610247565b925050602061033a85828601610247565b9150509250929050565b5f6040820190506103575f830185610286565b6103646020830184610286565b939250505056fea2646970667358221220903ff8ac6aec2e45b8ca86cbb59be29b3dcfa00f77033329976bbcf33ba63e7064736f6c634300081d0033a2646970667358221220ba27fff9501a2d06900cf30dbc92efde59d9d050f4b8c4f9c08aa87c5ad5fbde64736f6c634300081d0033", + ABI: "[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"hash\",\"type\":\"bytes32\"}],\"name\":\"HashCalculates\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"arr\",\"type\":\"uint256[]\"}],\"name\":\"MemoryWritten\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"accountId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"StorageUpdate\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"sum\",\"type\":\"uint256\"}],\"name\":\"SumCalculated\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"balancesCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"depth\",\"type\":\"uint256\"}],\"name\":\"simulateCallDepth\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"simulateContractCreation\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"rounds\",\"type\":\"uint256\"}],\"name\":\"simulateHashing\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"hash\",\"type\":\"bytes32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"sizeInWords\",\"type\":\"uint256\"}],\"name\":\"simulateMemory\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"sum\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"count\",\"type\":\"uint256\"}],\"name\":\"simulateModification\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"iterations\",\"type\":\"uint256\"}],\"name\":\"simulatePureCompute\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"result\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"count\",\"type\":\"uint256\"}],\"name\":\"simulateRandomWrite\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"count\",\"type\":\"uint256\"}],\"name\":\"simulateReads\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"sum\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x6080604052348015600e575f5ffd5b5061100a8061001c5f395ff3fe608060405234801561000f575f5ffd5b5060043610610091575f3560e01c80637db6ecb1116100645780637db6ecb114610109578063aae05a6514610139578063b77513d114610155578063f05ed79e14610171578063fb0c0012146101a157610091565b8063130fcab6146100955780633851d6e7146100c5578063542eedd9146100e35780635de583ef146100ff575b5f5ffd5b6100af60048036038101906100aa9190610745565b6101d1565b6040516100bc919061077f565b60405180910390f35b6100cd610271565b6040516100da919061077f565b60405180910390f35b6100fd60048036038101906100f89190610745565b610277565b005b610107610331565b005b610123600480360381019061011e9190610745565b61035a565b60405161013091906107b0565b60405180910390f35b610153600480360381019061014e9190610745565b610404565b005b61016f600480360381019061016a9190610745565b61050d565b005b61018b60048036038101906101869190610745565b610598565b604051610198919061077f565b60405180910390f35b6101bb60048036038101906101b69190610745565b610685565b6040516101c8919061077f565b60405180910390f35b5f5f5f90505b82811015610233576001816101ec91906107f6565b81600283846101fb9190610829565b6102059190610897565b61020f91906107f6565b61021991906108c7565b8261022491906107f6565b915080806001019150506101d7565b505f7fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc85f6040516102649190610939565b60405180910390a2919050565b60015481565b5f81036102bb575f7fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc85f6040516102ae9190610939565b60405180910390a261032e565b3073ffffffffffffffffffffffffffffffffffffffff1663542eedd96001836102e49190610952565b6040518263ffffffff1660e01b8152600401610300919061077f565b5f604051808303815f87803b158015610317575f5ffd5b505af1158015610329573d5f5f3e3d5ffd5b505050505b50565b60405161033d90610701565b604051809103905ff080158015610356573d5f5f3e3d5ffd5b5050565b5f60405160200161036a906109d9565b6040516020818303038152906040528051906020012090505f5f90505b828110156103c75781816040516020016103a2929190610a2d565b6040516020818303038152906040528051906020012091508080600101915050610387565b507f30ca2ef0880ae63712fdaf11aefb67752968cff6f845956fcbdfcf421f4647cb816040516103f791906107b0565b60405180910390a1919050565b5f600190505b8181116105095760015481101561048e575f60015f5f8481526020019081526020015f205461043991906107f6565b9050805f5f8481526020019081526020015f2081905550817fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc882604051610480919061077f565b60405180910390a2506104f6565b5f60015f8154809291906104a190610a58565b919050559050815f5f8381526020019081526020015f2081905550807fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc8836040516104ec919061077f565b60405180910390a2505b808061050190610a58565b91505061040a565b5050565b5f600190505b818111610594575f60015f81548092919061052d90610a58565b919050559050815f5f8381526020019081526020015f2081905550807fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc883604051610578919061077f565b60405180910390a250808061058c90610a58565b915050610513565b5050565b5f5f8267ffffffffffffffff8111156105b4576105b3610a9f565b5b6040519080825280602002602001820160405280156105e25781602001602082028036833780820191505090505b5090505f5f90505b83811015610647578082828151811061060657610605610acc565b5b60200260200101818152505081818151811061062557610624610acc565b5b60200260200101518361063891906107f6565b925080806001019150506105ea565b507f542a9e74627abe4fb012aa9be028f3234ff2b2253530c6fa2220e29f03e4215d816040516106779190610bb0565b60405180910390a150919050565b5f5f600190505b8281116106c4575f5f8281526020019081526020015f2054826106af91906107f6565b915080806106bc90610a58565b91505061068c565b507fe32d91cad5061d7491327c51e7b799c677b41d033204a5c5022b120f5da4becb816040516106f4919061077f565b60405180910390a1919050565b61040480610bd183390190565b5f5ffd5b5f819050919050565b61072481610712565b811461072e575f5ffd5b50565b5f8135905061073f8161071b565b92915050565b5f6020828403121561075a5761075961070e565b5b5f61076784828501610731565b91505092915050565b61077981610712565b82525050565b5f6020820190506107925f830184610770565b92915050565b5f819050919050565b6107aa81610798565b82525050565b5f6020820190506107c35f8301846107a1565b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f61080082610712565b915061080b83610712565b9250828201905080821115610823576108226107c9565b5b92915050565b5f61083382610712565b915061083e83610712565b925082820261084c81610712565b91508282048414831517610863576108626107c9565b5b5092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f6108a182610712565b91506108ac83610712565b9250826108bc576108bb61086a565b5b828204905092915050565b5f6108d182610712565b91506108dc83610712565b9250826108ec576108eb61086a565b5b828206905092915050565b5f819050919050565b5f819050919050565b5f61092361091e610919846108f7565b610900565b610712565b9050919050565b61093381610909565b82525050565b5f60208201905061094c5f83018461092a565b92915050565b5f61095c82610712565b915061096783610712565b925082820390508181111561097f5761097e6107c9565b5b92915050565b5f81905092915050565b7f696e697469616c000000000000000000000000000000000000000000000000005f82015250565b5f6109c3600783610985565b91506109ce8261098f565b600782019050919050565b5f6109e3826109b7565b9150819050919050565b5f819050919050565b610a07610a0282610798565b6109ed565b82525050565b5f819050919050565b610a27610a2282610712565b610a0d565b82525050565b5f610a3882856109f6565b602082019150610a488284610a16565b6020820191508190509392505050565b5f610a6282610712565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203610a9457610a936107c9565b5b600182019050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b610b2b81610712565b82525050565b5f610b3c8383610b22565b60208301905092915050565b5f602082019050919050565b5f610b5e82610af9565b610b688185610b03565b9350610b7383610b13565b805f5b83811015610ba3578151610b8a8882610b31565b9750610b9583610b48565b925050600181019050610b76565b5085935050505092915050565b5f6020820190508181035f830152610bc88184610b54565b90509291505056fe6080604052348015600e575f5ffd5b50602a5f819055503360015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506103a1806100635f395ff3fe608060405234801561000f575f5ffd5b5060043610610060575f3560e01c806337ebbc03146100645780633fa4f24514610094578063573c0bd3146100b25780638da5cb5b146100ce578063c71ba63b146100ec578063f0ba844014610108575b5f5ffd5b61007e6004803603810190610079919061025b565b610138565b60405161008b9190610295565b60405180910390f35b61009c610152565b6040516100a99190610295565b60405180910390f35b6100cc60048036038101906100c7919061025b565b610157565b005b6100d6610197565b6040516100e391906102ed565b60405180910390f35b61010660048036038101906101019190610306565b6101bc565b005b610122600480360381019061011d919061025b565b61020f565b60405161012f9190610295565b60405180910390f35b5f60025f8381526020019081526020015f20549050919050565b5f5481565b805f819055507f4273d0736f60e0dedfe745e86718093d8ec8646ebd2a60cd60643eeced5658118160405161018c9190610295565b60405180910390a150565b60015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b8060025f8481526020019081526020015f20819055507f36c0e38a11934bb6e80e00c4ae42212be021022fdb5aff12c53720f1d951c2818282604051610203929190610344565b60405180910390a15050565b6002602052805f5260405f205f915090505481565b5f5ffd5b5f819050919050565b61023a81610228565b8114610244575f5ffd5b50565b5f8135905061025581610231565b92915050565b5f602082840312156102705761026f610224565b5b5f61027d84828501610247565b91505092915050565b61028f81610228565b82525050565b5f6020820190506102a85f830184610286565b92915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6102d7826102ae565b9050919050565b6102e7816102cd565b82525050565b5f6020820190506103005f8301846102de565b92915050565b5f5f6040838503121561031c5761031b610224565b5b5f61032985828601610247565b925050602061033a85828601610247565b9150509250929050565b5f6040820190506103575f830185610286565b6103646020830184610286565b939250505056fea26469706673582212209dac5c694a03c04f90df0fab6ca0b8f6cc26a78f00312f518d7d99cd098e5ab764736f6c634300081d0033a264697066735822122095c9398bddcddd15c7685777ca61dffd7ebbcf4b89432aa410e425b643d6f6f764736f6c634300081d0033", } // EVMLoadSimulatorABI is the input ABI used to generate the binding from. @@ -338,6 +338,27 @@ func (_EVMLoadSimulator *EVMLoadSimulatorTransactorSession) SimulateModification return _EVMLoadSimulator.Contract.SimulateModification(&_EVMLoadSimulator.TransactOpts, count) } +// SimulatePureCompute is a paid mutator transaction binding the contract method 0x130fcab6. +// +// Solidity: function simulatePureCompute(uint256 iterations) returns(uint256 result) +func (_EVMLoadSimulator *EVMLoadSimulatorTransactor) SimulatePureCompute(opts *bind.TransactOpts, iterations *big.Int) (*types.Transaction, error) { + return _EVMLoadSimulator.contract.Transact(opts, "simulatePureCompute", iterations) +} + +// SimulatePureCompute is a paid mutator transaction binding the contract method 0x130fcab6. +// +// Solidity: function simulatePureCompute(uint256 iterations) returns(uint256 result) +func (_EVMLoadSimulator *EVMLoadSimulatorSession) SimulatePureCompute(iterations *big.Int) (*types.Transaction, error) { + return _EVMLoadSimulator.Contract.SimulatePureCompute(&_EVMLoadSimulator.TransactOpts, iterations) +} + +// SimulatePureCompute is a paid mutator transaction binding the contract method 0x130fcab6. +// +// Solidity: function simulatePureCompute(uint256 iterations) returns(uint256 result) +func (_EVMLoadSimulator *EVMLoadSimulatorTransactorSession) SimulatePureCompute(iterations *big.Int) (*types.Transaction, error) { + return _EVMLoadSimulator.Contract.SimulatePureCompute(&_EVMLoadSimulator.TransactOpts, iterations) +} + // SimulateRandomWrite is a paid mutator transaction binding the contract method 0xb77513d1. // // Solidity: function simulateRandomWrite(uint256 count) returns() diff --git a/tests/load/c/contracts/EVMLoadSimulator.sol b/tests/load/c/contracts/EVMLoadSimulator.sol index 2c76566b7075..5402910a50bb 100644 --- a/tests/load/c/contracts/EVMLoadSimulator.sol +++ b/tests/load/c/contracts/EVMLoadSimulator.sol @@ -79,4 +79,16 @@ contract EVMLoadSimulator { function simulateContractCreation() external { new Dummy(); } + + // Measure pure computation cost without memory/storage overhead. + // Don't mark this function as pure, in order to generate a transaction + // for the load test orchestrator. + function simulatePureCompute( + uint256 iterations + ) external returns (uint256 result) { + for (uint256 i = 0; i < iterations; i++) { + result += (((i * i) / 2) + i) % (i + 1); + } + emit StorageUpdate(0, 0); // Emit an event to indicate completion + } } diff --git a/tests/load/c/issuers/opcode.go b/tests/load/c/issuers/opcode.go index 138ef49dfceb..8f537166a2dc 100644 --- a/tests/load/c/issuers/opcode.go +++ b/tests/load/c/issuers/opcode.go @@ -124,6 +124,9 @@ func (o *Opcoder) GenerateAndIssueTx(ctx context.Context) (common.Hash, error) { tx, err = o.contractInstance.SimulateCallDepth(txOpts, depth) case contractCreation: tx, err = o.contractInstance.SimulateContractCreation(txOpts) + case pureCompute: + const iterations = 100 + tx, err = o.contractInstance.SimulatePureCompute(txOpts, big.NewInt(iterations)) default: return common.Hash{}, fmt.Errorf("invalid load type: %s", loadType) } @@ -147,6 +150,7 @@ const ( memory = "memory" callDepth = "call depth" contractCreation = "contract creation" + pureCompute = "pure compute" ) func allLoadTypes() []string { @@ -158,6 +162,7 @@ func allLoadTypes() []string { memory, callDepth, contractCreation, + pureCompute, } } From 1a6fab1d85a849cb88d7a6bf1727e555608ac3a1 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Fri, 16 May 2025 15:17:47 +0200 Subject: [PATCH 111/197] load/c/issuers/opcode: remove unused field lastIssue --- tests/load/c/issuers/opcode.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/load/c/issuers/opcode.go b/tests/load/c/issuers/opcode.go index 8f537166a2dc..f3dddcfcb46b 100644 --- a/tests/load/c/issuers/opcode.go +++ b/tests/load/c/issuers/opcode.go @@ -9,7 +9,6 @@ import ( "fmt" "math/big" "math/rand/v2" - "time" "github.com/ava-labs/libevm/accounts/abi/bind" "github.com/ava-labs/libevm/common" @@ -41,8 +40,7 @@ type Opcoder struct { contractInstance *contracts.EVMLoadSimulator // State - nonce uint64 - lastIssue time.Time + nonce uint64 } func NewOpcoder( @@ -138,7 +136,6 @@ func (o *Opcoder) GenerateAndIssueTx(ctx context.Context) (common.Hash, error) { o.nonce++ txHash := tx.Hash() o.tracker.Issue(txHash) - o.lastIssue = time.Now() return txHash, err } From 395676ed125829d4b989188df3d4ac0b2117b5a7 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Fri, 16 May 2025 15:20:24 +0200 Subject: [PATCH 112/197] Large event emission simulation function --- .../c/contracts/EVMLoadSimulator.bindings.go | 159 +++++++++++++++++- tests/load/c/contracts/EVMLoadSimulator.sol | 9 + tests/load/c/issuers/opcode.go | 5 + 3 files changed, 171 insertions(+), 2 deletions(-) diff --git a/tests/load/c/contracts/EVMLoadSimulator.bindings.go b/tests/load/c/contracts/EVMLoadSimulator.bindings.go index 44c719b7b76c..5c767a68d80a 100644 --- a/tests/load/c/contracts/EVMLoadSimulator.bindings.go +++ b/tests/load/c/contracts/EVMLoadSimulator.bindings.go @@ -31,8 +31,8 @@ var ( // EVMLoadSimulatorMetaData contains all meta data concerning the EVMLoadSimulator contract. var EVMLoadSimulatorMetaData = &bind.MetaData{ - ABI: "[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"hash\",\"type\":\"bytes32\"}],\"name\":\"HashCalculates\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"arr\",\"type\":\"uint256[]\"}],\"name\":\"MemoryWritten\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"accountId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"StorageUpdate\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"sum\",\"type\":\"uint256\"}],\"name\":\"SumCalculated\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"balancesCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"depth\",\"type\":\"uint256\"}],\"name\":\"simulateCallDepth\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"simulateContractCreation\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"rounds\",\"type\":\"uint256\"}],\"name\":\"simulateHashing\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"hash\",\"type\":\"bytes32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"sizeInWords\",\"type\":\"uint256\"}],\"name\":\"simulateMemory\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"sum\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"count\",\"type\":\"uint256\"}],\"name\":\"simulateModification\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"iterations\",\"type\":\"uint256\"}],\"name\":\"simulatePureCompute\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"result\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"count\",\"type\":\"uint256\"}],\"name\":\"simulateRandomWrite\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"count\",\"type\":\"uint256\"}],\"name\":\"simulateReads\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"sum\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x6080604052348015600e575f5ffd5b5061100a8061001c5f395ff3fe608060405234801561000f575f5ffd5b5060043610610091575f3560e01c80637db6ecb1116100645780637db6ecb114610109578063aae05a6514610139578063b77513d114610155578063f05ed79e14610171578063fb0c0012146101a157610091565b8063130fcab6146100955780633851d6e7146100c5578063542eedd9146100e35780635de583ef146100ff575b5f5ffd5b6100af60048036038101906100aa9190610745565b6101d1565b6040516100bc919061077f565b60405180910390f35b6100cd610271565b6040516100da919061077f565b60405180910390f35b6100fd60048036038101906100f89190610745565b610277565b005b610107610331565b005b610123600480360381019061011e9190610745565b61035a565b60405161013091906107b0565b60405180910390f35b610153600480360381019061014e9190610745565b610404565b005b61016f600480360381019061016a9190610745565b61050d565b005b61018b60048036038101906101869190610745565b610598565b604051610198919061077f565b60405180910390f35b6101bb60048036038101906101b69190610745565b610685565b6040516101c8919061077f565b60405180910390f35b5f5f5f90505b82811015610233576001816101ec91906107f6565b81600283846101fb9190610829565b6102059190610897565b61020f91906107f6565b61021991906108c7565b8261022491906107f6565b915080806001019150506101d7565b505f7fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc85f6040516102649190610939565b60405180910390a2919050565b60015481565b5f81036102bb575f7fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc85f6040516102ae9190610939565b60405180910390a261032e565b3073ffffffffffffffffffffffffffffffffffffffff1663542eedd96001836102e49190610952565b6040518263ffffffff1660e01b8152600401610300919061077f565b5f604051808303815f87803b158015610317575f5ffd5b505af1158015610329573d5f5f3e3d5ffd5b505050505b50565b60405161033d90610701565b604051809103905ff080158015610356573d5f5f3e3d5ffd5b5050565b5f60405160200161036a906109d9565b6040516020818303038152906040528051906020012090505f5f90505b828110156103c75781816040516020016103a2929190610a2d565b6040516020818303038152906040528051906020012091508080600101915050610387565b507f30ca2ef0880ae63712fdaf11aefb67752968cff6f845956fcbdfcf421f4647cb816040516103f791906107b0565b60405180910390a1919050565b5f600190505b8181116105095760015481101561048e575f60015f5f8481526020019081526020015f205461043991906107f6565b9050805f5f8481526020019081526020015f2081905550817fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc882604051610480919061077f565b60405180910390a2506104f6565b5f60015f8154809291906104a190610a58565b919050559050815f5f8381526020019081526020015f2081905550807fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc8836040516104ec919061077f565b60405180910390a2505b808061050190610a58565b91505061040a565b5050565b5f600190505b818111610594575f60015f81548092919061052d90610a58565b919050559050815f5f8381526020019081526020015f2081905550807fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc883604051610578919061077f565b60405180910390a250808061058c90610a58565b915050610513565b5050565b5f5f8267ffffffffffffffff8111156105b4576105b3610a9f565b5b6040519080825280602002602001820160405280156105e25781602001602082028036833780820191505090505b5090505f5f90505b83811015610647578082828151811061060657610605610acc565b5b60200260200101818152505081818151811061062557610624610acc565b5b60200260200101518361063891906107f6565b925080806001019150506105ea565b507f542a9e74627abe4fb012aa9be028f3234ff2b2253530c6fa2220e29f03e4215d816040516106779190610bb0565b60405180910390a150919050565b5f5f600190505b8281116106c4575f5f8281526020019081526020015f2054826106af91906107f6565b915080806106bc90610a58565b91505061068c565b507fe32d91cad5061d7491327c51e7b799c677b41d033204a5c5022b120f5da4becb816040516106f4919061077f565b60405180910390a1919050565b61040480610bd183390190565b5f5ffd5b5f819050919050565b61072481610712565b811461072e575f5ffd5b50565b5f8135905061073f8161071b565b92915050565b5f6020828403121561075a5761075961070e565b5b5f61076784828501610731565b91505092915050565b61077981610712565b82525050565b5f6020820190506107925f830184610770565b92915050565b5f819050919050565b6107aa81610798565b82525050565b5f6020820190506107c35f8301846107a1565b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f61080082610712565b915061080b83610712565b9250828201905080821115610823576108226107c9565b5b92915050565b5f61083382610712565b915061083e83610712565b925082820261084c81610712565b91508282048414831517610863576108626107c9565b5b5092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f6108a182610712565b91506108ac83610712565b9250826108bc576108bb61086a565b5b828204905092915050565b5f6108d182610712565b91506108dc83610712565b9250826108ec576108eb61086a565b5b828206905092915050565b5f819050919050565b5f819050919050565b5f61092361091e610919846108f7565b610900565b610712565b9050919050565b61093381610909565b82525050565b5f60208201905061094c5f83018461092a565b92915050565b5f61095c82610712565b915061096783610712565b925082820390508181111561097f5761097e6107c9565b5b92915050565b5f81905092915050565b7f696e697469616c000000000000000000000000000000000000000000000000005f82015250565b5f6109c3600783610985565b91506109ce8261098f565b600782019050919050565b5f6109e3826109b7565b9150819050919050565b5f819050919050565b610a07610a0282610798565b6109ed565b82525050565b5f819050919050565b610a27610a2282610712565b610a0d565b82525050565b5f610a3882856109f6565b602082019150610a488284610a16565b6020820191508190509392505050565b5f610a6282610712565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203610a9457610a936107c9565b5b600182019050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b610b2b81610712565b82525050565b5f610b3c8383610b22565b60208301905092915050565b5f602082019050919050565b5f610b5e82610af9565b610b688185610b03565b9350610b7383610b13565b805f5b83811015610ba3578151610b8a8882610b31565b9750610b9583610b48565b925050600181019050610b76565b5085935050505092915050565b5f6020820190508181035f830152610bc88184610b54565b90509291505056fe6080604052348015600e575f5ffd5b50602a5f819055503360015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506103a1806100635f395ff3fe608060405234801561000f575f5ffd5b5060043610610060575f3560e01c806337ebbc03146100645780633fa4f24514610094578063573c0bd3146100b25780638da5cb5b146100ce578063c71ba63b146100ec578063f0ba844014610108575b5f5ffd5b61007e6004803603810190610079919061025b565b610138565b60405161008b9190610295565b60405180910390f35b61009c610152565b6040516100a99190610295565b60405180910390f35b6100cc60048036038101906100c7919061025b565b610157565b005b6100d6610197565b6040516100e391906102ed565b60405180910390f35b61010660048036038101906101019190610306565b6101bc565b005b610122600480360381019061011d919061025b565b61020f565b60405161012f9190610295565b60405180910390f35b5f60025f8381526020019081526020015f20549050919050565b5f5481565b805f819055507f4273d0736f60e0dedfe745e86718093d8ec8646ebd2a60cd60643eeced5658118160405161018c9190610295565b60405180910390a150565b60015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b8060025f8481526020019081526020015f20819055507f36c0e38a11934bb6e80e00c4ae42212be021022fdb5aff12c53720f1d951c2818282604051610203929190610344565b60405180910390a15050565b6002602052805f5260405f205f915090505481565b5f5ffd5b5f819050919050565b61023a81610228565b8114610244575f5ffd5b50565b5f8135905061025581610231565b92915050565b5f602082840312156102705761026f610224565b5b5f61027d84828501610247565b91505092915050565b61028f81610228565b82525050565b5f6020820190506102a85f830184610286565b92915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6102d7826102ae565b9050919050565b6102e7816102cd565b82525050565b5f6020820190506103005f8301846102de565b92915050565b5f5f6040838503121561031c5761031b610224565b5b5f61032985828601610247565b925050602061033a85828601610247565b9150509250929050565b5f6040820190506103575f830185610286565b6103646020830184610286565b939250505056fea26469706673582212209dac5c694a03c04f90df0fab6ca0b8f6cc26a78f00312f518d7d99cd098e5ab764736f6c634300081d0033a264697066735822122095c9398bddcddd15c7685777ca61dffd7ebbcf4b89432aa410e425b643d6f6f764736f6c634300081d0033", + ABI: "[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"hash\",\"type\":\"bytes32\"}],\"name\":\"HashCalculates\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"largeData\",\"type\":\"bytes\"}],\"name\":\"LargeLog\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"arr\",\"type\":\"uint256[]\"}],\"name\":\"MemoryWritten\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"accountId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"StorageUpdate\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"sum\",\"type\":\"uint256\"}],\"name\":\"SumCalculated\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"balancesCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"depth\",\"type\":\"uint256\"}],\"name\":\"simulateCallDepth\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"simulateContractCreation\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"rounds\",\"type\":\"uint256\"}],\"name\":\"simulateHashing\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"hash\",\"type\":\"bytes32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"size\",\"type\":\"uint256\"}],\"name\":\"simulateLargeEvent\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"sizeInWords\",\"type\":\"uint256\"}],\"name\":\"simulateMemory\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"sum\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"count\",\"type\":\"uint256\"}],\"name\":\"simulateModification\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"iterations\",\"type\":\"uint256\"}],\"name\":\"simulatePureCompute\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"result\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"count\",\"type\":\"uint256\"}],\"name\":\"simulateRandomWrite\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"count\",\"type\":\"uint256\"}],\"name\":\"simulateReads\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"sum\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x6080604052348015600e575f5ffd5b506111ac8061001c5f395ff3fe608060405234801561000f575f5ffd5b506004361061009c575f3560e01c8063aae05a6511610064578063aae05a6514610144578063ab7611d114610160578063b77513d11461017c578063f05ed79e14610198578063fb0c0012146101c85761009c565b8063130fcab6146100a05780633851d6e7146100d0578063542eedd9146100ee5780635de583ef1461010a5780637db6ecb114610114575b5f5ffd5b6100ba60048036038101906100b59190610857565b6101f8565b6040516100c79190610891565b60405180910390f35b6100d8610298565b6040516100e59190610891565b60405180910390f35b61010860048036038101906101039190610857565b61029e565b005b610112610358565b005b61012e60048036038101906101299190610857565b610381565b60405161013b91906108c2565b60405180910390f35b61015e60048036038101906101599190610857565b61042b565b005b61017a60048036038101906101759190610857565b610534565b005b61019660048036038101906101919190610857565b61061f565b005b6101b260048036038101906101ad9190610857565b6106aa565b6040516101bf9190610891565b60405180910390f35b6101e260048036038101906101dd9190610857565b610797565b6040516101ef9190610891565b60405180910390f35b5f5f5f90505b8281101561025a576001816102139190610908565b8160028384610222919061093b565b61022c91906109a9565b6102369190610908565b61024091906109d9565b8261024b9190610908565b915080806001019150506101fe565b505f7fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc85f60405161028b9190610a4b565b60405180910390a2919050565b60015481565b5f81036102e2575f7fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc85f6040516102d59190610a4b565b60405180910390a2610355565b3073ffffffffffffffffffffffffffffffffffffffff1663542eedd960018361030b9190610a64565b6040518263ffffffff1660e01b81526004016103279190610891565b5f604051808303815f87803b15801561033e575f5ffd5b505af1158015610350573d5f5f3e3d5ffd5b505050505b50565b60405161036490610813565b604051809103905ff08015801561037d573d5f5f3e3d5ffd5b5050565b5f60405160200161039190610aeb565b6040516020818303038152906040528051906020012090505f5f90505b828110156103ee5781816040516020016103c9929190610b3f565b60405160208183030381529060405280519060200120915080806001019150506103ae565b507f30ca2ef0880ae63712fdaf11aefb67752968cff6f845956fcbdfcf421f4647cb8160405161041e91906108c2565b60405180910390a1919050565b5f600190505b818111610530576001548110156104b5575f60015f5f8481526020019081526020015f20546104609190610908565b9050805f5f8481526020019081526020015f2081905550817fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc8826040516104a79190610891565b60405180910390a25061051d565b5f60015f8154809291906104c890610b6a565b919050559050815f5f8381526020019081526020015f2081905550807fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc8836040516105139190610891565b60405180910390a2505b808061052890610b6a565b915050610431565b5050565b5f8167ffffffffffffffff81111561054f5761054e610bb1565b5b6040519080825280601f01601f1916602001820160405280156105815781602001600182028036833780820191505090505b5090505f5f90505b828110156105e3578060f81b8282815181106105a8576105a7610bde565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053508080600101915050610589565b507f5e53254f5b56e942cb89e1beff9257b039a5593ffe94274d0640a636b57fd0ac816040516106139190610c7b565b60405180910390a15050565b5f600190505b8181116106a6575f60015f81548092919061063f90610b6a565b919050559050815f5f8381526020019081526020015f2081905550807fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc88360405161068a9190610891565b60405180910390a250808061069e90610b6a565b915050610625565b5050565b5f5f8267ffffffffffffffff8111156106c6576106c5610bb1565b5b6040519080825280602002602001820160405280156106f45781602001602082028036833780820191505090505b5090505f5f90505b83811015610759578082828151811061071857610717610bde565b5b60200260200101818152505081818151811061073757610736610bde565b5b60200260200101518361074a9190610908565b925080806001019150506106fc565b507f542a9e74627abe4fb012aa9be028f3234ff2b2253530c6fa2220e29f03e4215d816040516107899190610d52565b60405180910390a150919050565b5f5f600190505b8281116107d6575f5f8281526020019081526020015f2054826107c19190610908565b915080806107ce90610b6a565b91505061079e565b507fe32d91cad5061d7491327c51e7b799c677b41d033204a5c5022b120f5da4becb816040516108069190610891565b60405180910390a1919050565b61040480610d7383390190565b5f5ffd5b5f819050919050565b61083681610824565b8114610840575f5ffd5b50565b5f813590506108518161082d565b92915050565b5f6020828403121561086c5761086b610820565b5b5f61087984828501610843565b91505092915050565b61088b81610824565b82525050565b5f6020820190506108a45f830184610882565b92915050565b5f819050919050565b6108bc816108aa565b82525050565b5f6020820190506108d55f8301846108b3565b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f61091282610824565b915061091d83610824565b9250828201905080821115610935576109346108db565b5b92915050565b5f61094582610824565b915061095083610824565b925082820261095e81610824565b91508282048414831517610975576109746108db565b5b5092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f6109b382610824565b91506109be83610824565b9250826109ce576109cd61097c565b5b828204905092915050565b5f6109e382610824565b91506109ee83610824565b9250826109fe576109fd61097c565b5b828206905092915050565b5f819050919050565b5f819050919050565b5f610a35610a30610a2b84610a09565b610a12565b610824565b9050919050565b610a4581610a1b565b82525050565b5f602082019050610a5e5f830184610a3c565b92915050565b5f610a6e82610824565b9150610a7983610824565b9250828203905081811115610a9157610a906108db565b5b92915050565b5f81905092915050565b7f696e697469616c000000000000000000000000000000000000000000000000005f82015250565b5f610ad5600783610a97565b9150610ae082610aa1565b600782019050919050565b5f610af582610ac9565b9150819050919050565b5f819050919050565b610b19610b14826108aa565b610aff565b82525050565b5f819050919050565b610b39610b3482610824565b610b1f565b82525050565b5f610b4a8285610b08565b602082019150610b5a8284610b28565b6020820191508190509392505050565b5f610b7482610824565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203610ba657610ba56108db565b5b600182019050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f81519050919050565b5f82825260208201905092915050565b8281835e5f83830152505050565b5f601f19601f8301169050919050565b5f610c4d82610c0b565b610c578185610c15565b9350610c67818560208601610c25565b610c7081610c33565b840191505092915050565b5f6020820190508181035f830152610c938184610c43565b905092915050565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b610ccd81610824565b82525050565b5f610cde8383610cc4565b60208301905092915050565b5f602082019050919050565b5f610d0082610c9b565b610d0a8185610ca5565b9350610d1583610cb5565b805f5b83811015610d45578151610d2c8882610cd3565b9750610d3783610cea565b925050600181019050610d18565b5085935050505092915050565b5f6020820190508181035f830152610d6a8184610cf6565b90509291505056fe6080604052348015600e575f5ffd5b50602a5f819055503360015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506103a1806100635f395ff3fe608060405234801561000f575f5ffd5b5060043610610060575f3560e01c806337ebbc03146100645780633fa4f24514610094578063573c0bd3146100b25780638da5cb5b146100ce578063c71ba63b146100ec578063f0ba844014610108575b5f5ffd5b61007e6004803603810190610079919061025b565b610138565b60405161008b9190610295565b60405180910390f35b61009c610152565b6040516100a99190610295565b60405180910390f35b6100cc60048036038101906100c7919061025b565b610157565b005b6100d6610197565b6040516100e391906102ed565b60405180910390f35b61010660048036038101906101019190610306565b6101bc565b005b610122600480360381019061011d919061025b565b61020f565b60405161012f9190610295565b60405180910390f35b5f60025f8381526020019081526020015f20549050919050565b5f5481565b805f819055507f4273d0736f60e0dedfe745e86718093d8ec8646ebd2a60cd60643eeced5658118160405161018c9190610295565b60405180910390a150565b60015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b8060025f8481526020019081526020015f20819055507f36c0e38a11934bb6e80e00c4ae42212be021022fdb5aff12c53720f1d951c2818282604051610203929190610344565b60405180910390a15050565b6002602052805f5260405f205f915090505481565b5f5ffd5b5f819050919050565b61023a81610228565b8114610244575f5ffd5b50565b5f8135905061025581610231565b92915050565b5f602082840312156102705761026f610224565b5b5f61027d84828501610247565b91505092915050565b61028f81610228565b82525050565b5f6020820190506102a85f830184610286565b92915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6102d7826102ae565b9050919050565b6102e7816102cd565b82525050565b5f6020820190506103005f8301846102de565b92915050565b5f5f6040838503121561031c5761031b610224565b5b5f61032985828601610247565b925050602061033a85828601610247565b9150509250929050565b5f6040820190506103575f830185610286565b6103646020830184610286565b939250505056fea26469706673582212209dac5c694a03c04f90df0fab6ca0b8f6cc26a78f00312f518d7d99cd098e5ab764736f6c634300081d0033a2646970667358221220c8e8483555bc94d2e804745fafd41aea77e2dee1aa2733654dad769fd6b5f2d764736f6c634300081d0033", } // EVMLoadSimulatorABI is the input ABI used to generate the binding from. @@ -296,6 +296,27 @@ func (_EVMLoadSimulator *EVMLoadSimulatorTransactorSession) SimulateHashing(roun return _EVMLoadSimulator.Contract.SimulateHashing(&_EVMLoadSimulator.TransactOpts, rounds) } +// SimulateLargeEvent is a paid mutator transaction binding the contract method 0xab7611d1. +// +// Solidity: function simulateLargeEvent(uint256 size) returns() +func (_EVMLoadSimulator *EVMLoadSimulatorTransactor) SimulateLargeEvent(opts *bind.TransactOpts, size *big.Int) (*types.Transaction, error) { + return _EVMLoadSimulator.contract.Transact(opts, "simulateLargeEvent", size) +} + +// SimulateLargeEvent is a paid mutator transaction binding the contract method 0xab7611d1. +// +// Solidity: function simulateLargeEvent(uint256 size) returns() +func (_EVMLoadSimulator *EVMLoadSimulatorSession) SimulateLargeEvent(size *big.Int) (*types.Transaction, error) { + return _EVMLoadSimulator.Contract.SimulateLargeEvent(&_EVMLoadSimulator.TransactOpts, size) +} + +// SimulateLargeEvent is a paid mutator transaction binding the contract method 0xab7611d1. +// +// Solidity: function simulateLargeEvent(uint256 size) returns() +func (_EVMLoadSimulator *EVMLoadSimulatorTransactorSession) SimulateLargeEvent(size *big.Int) (*types.Transaction, error) { + return _EVMLoadSimulator.Contract.SimulateLargeEvent(&_EVMLoadSimulator.TransactOpts, size) +} + // SimulateMemory is a paid mutator transaction binding the contract method 0xf05ed79e. // // Solidity: function simulateMemory(uint256 sizeInWords) returns(uint256 sum) @@ -535,6 +556,140 @@ func (_EVMLoadSimulator *EVMLoadSimulatorFilterer) ParseHashCalculates(log types return event, nil } +// EVMLoadSimulatorLargeLogIterator is returned from FilterLargeLog and is used to iterate over the raw logs and unpacked data for LargeLog events raised by the EVMLoadSimulator contract. +type EVMLoadSimulatorLargeLogIterator struct { + Event *EVMLoadSimulatorLargeLog // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *EVMLoadSimulatorLargeLogIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(EVMLoadSimulatorLargeLog) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(EVMLoadSimulatorLargeLog) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *EVMLoadSimulatorLargeLogIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *EVMLoadSimulatorLargeLogIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// EVMLoadSimulatorLargeLog represents a LargeLog event raised by the EVMLoadSimulator contract. +type EVMLoadSimulatorLargeLog struct { + LargeData []byte + Raw types.Log // Blockchain specific contextual infos +} + +// FilterLargeLog is a free log retrieval operation binding the contract event 0x5e53254f5b56e942cb89e1beff9257b039a5593ffe94274d0640a636b57fd0ac. +// +// Solidity: event LargeLog(bytes largeData) +func (_EVMLoadSimulator *EVMLoadSimulatorFilterer) FilterLargeLog(opts *bind.FilterOpts) (*EVMLoadSimulatorLargeLogIterator, error) { + + logs, sub, err := _EVMLoadSimulator.contract.FilterLogs(opts, "LargeLog") + if err != nil { + return nil, err + } + return &EVMLoadSimulatorLargeLogIterator{contract: _EVMLoadSimulator.contract, event: "LargeLog", logs: logs, sub: sub}, nil +} + +// WatchLargeLog is a free log subscription operation binding the contract event 0x5e53254f5b56e942cb89e1beff9257b039a5593ffe94274d0640a636b57fd0ac. +// +// Solidity: event LargeLog(bytes largeData) +func (_EVMLoadSimulator *EVMLoadSimulatorFilterer) WatchLargeLog(opts *bind.WatchOpts, sink chan<- *EVMLoadSimulatorLargeLog) (event.Subscription, error) { + + logs, sub, err := _EVMLoadSimulator.contract.WatchLogs(opts, "LargeLog") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(EVMLoadSimulatorLargeLog) + if err := _EVMLoadSimulator.contract.UnpackLog(event, "LargeLog", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseLargeLog is a log parse operation binding the contract event 0x5e53254f5b56e942cb89e1beff9257b039a5593ffe94274d0640a636b57fd0ac. +// +// Solidity: event LargeLog(bytes largeData) +func (_EVMLoadSimulator *EVMLoadSimulatorFilterer) ParseLargeLog(log types.Log) (*EVMLoadSimulatorLargeLog, error) { + event := new(EVMLoadSimulatorLargeLog) + if err := _EVMLoadSimulator.contract.UnpackLog(event, "LargeLog", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + // EVMLoadSimulatorMemoryWrittenIterator is returned from FilterMemoryWritten and is used to iterate over the raw logs and unpacked data for MemoryWritten events raised by the EVMLoadSimulator contract. type EVMLoadSimulatorMemoryWrittenIterator struct { Event *EVMLoadSimulatorMemoryWritten // Event containing the contract specifics and raw log diff --git a/tests/load/c/contracts/EVMLoadSimulator.sol b/tests/load/c/contracts/EVMLoadSimulator.sol index 5402910a50bb..9069200ad9e2 100644 --- a/tests/load/c/contracts/EVMLoadSimulator.sol +++ b/tests/load/c/contracts/EVMLoadSimulator.sol @@ -13,6 +13,7 @@ contract EVMLoadSimulator { event SumCalculated(uint256 sum); event HashCalculates(bytes32 hash); event MemoryWritten(uint256[] arr); + event LargeLog(bytes largeData); // Simulate random storage writes function simulateRandomWrite(uint256 count) external { @@ -91,4 +92,12 @@ contract EVMLoadSimulator { } emit StorageUpdate(0, 0); // Emit an event to indicate completion } + + function simulateLargeEvent(uint256 size) external { + bytes memory data = new bytes(size); + for (uint256 i = 0; i < size; i++) { + data[i] = bytes1(uint8(i)); + } + emit LargeLog(data); + } } diff --git a/tests/load/c/issuers/opcode.go b/tests/load/c/issuers/opcode.go index f3dddcfcb46b..b29dfd688d91 100644 --- a/tests/load/c/issuers/opcode.go +++ b/tests/load/c/issuers/opcode.go @@ -125,6 +125,9 @@ func (o *Opcoder) GenerateAndIssueTx(ctx context.Context) (common.Hash, error) { case pureCompute: const iterations = 100 tx, err = o.contractInstance.SimulatePureCompute(txOpts, big.NewInt(iterations)) + case largeEvent: + const maxEventSize = 100 + tx, err = o.contractInstance.SimulateLargeEvent(txOpts, big.NewInt(maxEventSize)) default: return common.Hash{}, fmt.Errorf("invalid load type: %s", loadType) } @@ -148,6 +151,7 @@ const ( callDepth = "call depth" contractCreation = "contract creation" pureCompute = "pure compute" + largeEvent = "large event" ) func allLoadTypes() []string { @@ -160,6 +164,7 @@ func allLoadTypes() []string { callDepth, contractCreation, pureCompute, + largeEvent, } } From 7ba62b9b0ef0ae74d3d70e40e321fe46d8c15406 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Fri, 16 May 2025 15:29:02 +0200 Subject: [PATCH 113/197] External contract call simulation function --- tests/load/c/contracts/Dummy.bindings.go | 2 +- tests/load/c/contracts/Dummy.sol | 1 - .../c/contracts/EVMLoadSimulator.bindings.go | 56 ++++++++++++++++++- tests/load/c/contracts/EVMLoadSimulator.sol | 12 +++- tests/load/c/issuers/opcode.go | 4 ++ 5 files changed, 70 insertions(+), 5 deletions(-) diff --git a/tests/load/c/contracts/Dummy.bindings.go b/tests/load/c/contracts/Dummy.bindings.go index 143beece614e..fcfda4e36b32 100644 --- a/tests/load/c/contracts/Dummy.bindings.go +++ b/tests/load/c/contracts/Dummy.bindings.go @@ -32,7 +32,7 @@ var ( // DummyMetaData contains all meta data concerning the Dummy contract. var DummyMetaData = &bind.MetaData{ ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"key\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"DataWritten\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newValue\",\"type\":\"uint256\"}],\"name\":\"ValueUpdated\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"data\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"key\",\"type\":\"uint256\"}],\"name\":\"readData\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newValue\",\"type\":\"uint256\"}],\"name\":\"updateValue\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"value\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"key\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"val\",\"type\":\"uint256\"}],\"name\":\"writeData\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x6080604052348015600e575f5ffd5b50602a5f819055503360015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506103a1806100635f395ff3fe608060405234801561000f575f5ffd5b5060043610610060575f3560e01c806337ebbc03146100645780633fa4f24514610094578063573c0bd3146100b25780638da5cb5b146100ce578063c71ba63b146100ec578063f0ba844014610108575b5f5ffd5b61007e6004803603810190610079919061025b565b610138565b60405161008b9190610295565b60405180910390f35b61009c610152565b6040516100a99190610295565b60405180910390f35b6100cc60048036038101906100c7919061025b565b610157565b005b6100d6610197565b6040516100e391906102ed565b60405180910390f35b61010660048036038101906101019190610306565b6101bc565b005b610122600480360381019061011d919061025b565b61020f565b60405161012f9190610295565b60405180910390f35b5f60025f8381526020019081526020015f20549050919050565b5f5481565b805f819055507f4273d0736f60e0dedfe745e86718093d8ec8646ebd2a60cd60643eeced5658118160405161018c9190610295565b60405180910390a150565b60015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b8060025f8481526020019081526020015f20819055507f36c0e38a11934bb6e80e00c4ae42212be021022fdb5aff12c53720f1d951c2818282604051610203929190610344565b60405180910390a15050565b6002602052805f5260405f205f915090505481565b5f5ffd5b5f819050919050565b61023a81610228565b8114610244575f5ffd5b50565b5f8135905061025581610231565b92915050565b5f602082840312156102705761026f610224565b5b5f61027d84828501610247565b91505092915050565b61028f81610228565b82525050565b5f6020820190506102a85f830184610286565b92915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6102d7826102ae565b9050919050565b6102e7816102cd565b82525050565b5f6020820190506103005f8301846102de565b92915050565b5f5f6040838503121561031c5761031b610224565b5b5f61032985828601610247565b925050602061033a85828601610247565b9150509250929050565b5f6040820190506103575f830185610286565b6103646020830184610286565b939250505056fea26469706673582212209dac5c694a03c04f90df0fab6ca0b8f6cc26a78f00312f518d7d99cd098e5ab764736f6c634300081d0033", + Bin: "0x6080604052348015600e575f5ffd5b50602a5f819055503360015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555061036a806100635f395ff3fe608060405234801561000f575f5ffd5b5060043610610060575f3560e01c806337ebbc03146100645780633fa4f24514610094578063573c0bd3146100b25780638da5cb5b146100ce578063c71ba63b146100ec578063f0ba844014610108575b5f5ffd5b61007e60048036038101906100799190610224565b610138565b60405161008b919061025e565b60405180910390f35b61009c610152565b6040516100a9919061025e565b60405180910390f35b6100cc60048036038101906100c79190610224565b610157565b005b6100d6610160565b6040516100e391906102b6565b60405180910390f35b610106600480360381019061010191906102cf565b610185565b005b610122600480360381019061011d9190610224565b6101d8565b60405161012f919061025e565b60405180910390f35b5f60025f8381526020019081526020015f20549050919050565b5f5481565b805f8190555050565b60015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b8060025f8481526020019081526020015f20819055507f36c0e38a11934bb6e80e00c4ae42212be021022fdb5aff12c53720f1d951c28182826040516101cc92919061030d565b60405180910390a15050565b6002602052805f5260405f205f915090505481565b5f5ffd5b5f819050919050565b610203816101f1565b811461020d575f5ffd5b50565b5f8135905061021e816101fa565b92915050565b5f60208284031215610239576102386101ed565b5b5f61024684828501610210565b91505092915050565b610258816101f1565b82525050565b5f6020820190506102715f83018461024f565b92915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6102a082610277565b9050919050565b6102b081610296565b82525050565b5f6020820190506102c95f8301846102a7565b92915050565b5f5f604083850312156102e5576102e46101ed565b5b5f6102f285828601610210565b925050602061030385828601610210565b9150509250929050565b5f6040820190506103205f83018561024f565b61032d602083018461024f565b939250505056fea26469706673582212201893623673c7d322a3501952be3510a3b2582a332e23a9ee7765420a7e2c8f7464736f6c634300081d0033", } // DummyABI is the input ABI used to generate the binding from. diff --git a/tests/load/c/contracts/Dummy.sol b/tests/load/c/contracts/Dummy.sol index ad55ca23bfef..90c621bf3b4a 100644 --- a/tests/load/c/contracts/Dummy.sol +++ b/tests/load/c/contracts/Dummy.sol @@ -19,7 +19,6 @@ contract Dummy { function updateValue(uint256 newValue) external { value = newValue; - emit ValueUpdated(newValue); } function writeData(uint256 key, uint256 val) external { diff --git a/tests/load/c/contracts/EVMLoadSimulator.bindings.go b/tests/load/c/contracts/EVMLoadSimulator.bindings.go index 5c767a68d80a..dd792e6006e8 100644 --- a/tests/load/c/contracts/EVMLoadSimulator.bindings.go +++ b/tests/load/c/contracts/EVMLoadSimulator.bindings.go @@ -31,8 +31,8 @@ var ( // EVMLoadSimulatorMetaData contains all meta data concerning the EVMLoadSimulator contract. var EVMLoadSimulatorMetaData = &bind.MetaData{ - ABI: "[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"hash\",\"type\":\"bytes32\"}],\"name\":\"HashCalculates\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"largeData\",\"type\":\"bytes\"}],\"name\":\"LargeLog\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"arr\",\"type\":\"uint256[]\"}],\"name\":\"MemoryWritten\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"accountId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"StorageUpdate\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"sum\",\"type\":\"uint256\"}],\"name\":\"SumCalculated\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"balancesCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"depth\",\"type\":\"uint256\"}],\"name\":\"simulateCallDepth\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"simulateContractCreation\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"rounds\",\"type\":\"uint256\"}],\"name\":\"simulateHashing\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"hash\",\"type\":\"bytes32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"size\",\"type\":\"uint256\"}],\"name\":\"simulateLargeEvent\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"sizeInWords\",\"type\":\"uint256\"}],\"name\":\"simulateMemory\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"sum\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"count\",\"type\":\"uint256\"}],\"name\":\"simulateModification\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"iterations\",\"type\":\"uint256\"}],\"name\":\"simulatePureCompute\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"result\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"count\",\"type\":\"uint256\"}],\"name\":\"simulateRandomWrite\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"count\",\"type\":\"uint256\"}],\"name\":\"simulateReads\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"sum\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x6080604052348015600e575f5ffd5b506111ac8061001c5f395ff3fe608060405234801561000f575f5ffd5b506004361061009c575f3560e01c8063aae05a6511610064578063aae05a6514610144578063ab7611d114610160578063b77513d11461017c578063f05ed79e14610198578063fb0c0012146101c85761009c565b8063130fcab6146100a05780633851d6e7146100d0578063542eedd9146100ee5780635de583ef1461010a5780637db6ecb114610114575b5f5ffd5b6100ba60048036038101906100b59190610857565b6101f8565b6040516100c79190610891565b60405180910390f35b6100d8610298565b6040516100e59190610891565b60405180910390f35b61010860048036038101906101039190610857565b61029e565b005b610112610358565b005b61012e60048036038101906101299190610857565b610381565b60405161013b91906108c2565b60405180910390f35b61015e60048036038101906101599190610857565b61042b565b005b61017a60048036038101906101759190610857565b610534565b005b61019660048036038101906101919190610857565b61061f565b005b6101b260048036038101906101ad9190610857565b6106aa565b6040516101bf9190610891565b60405180910390f35b6101e260048036038101906101dd9190610857565b610797565b6040516101ef9190610891565b60405180910390f35b5f5f5f90505b8281101561025a576001816102139190610908565b8160028384610222919061093b565b61022c91906109a9565b6102369190610908565b61024091906109d9565b8261024b9190610908565b915080806001019150506101fe565b505f7fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc85f60405161028b9190610a4b565b60405180910390a2919050565b60015481565b5f81036102e2575f7fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc85f6040516102d59190610a4b565b60405180910390a2610355565b3073ffffffffffffffffffffffffffffffffffffffff1663542eedd960018361030b9190610a64565b6040518263ffffffff1660e01b81526004016103279190610891565b5f604051808303815f87803b15801561033e575f5ffd5b505af1158015610350573d5f5f3e3d5ffd5b505050505b50565b60405161036490610813565b604051809103905ff08015801561037d573d5f5f3e3d5ffd5b5050565b5f60405160200161039190610aeb565b6040516020818303038152906040528051906020012090505f5f90505b828110156103ee5781816040516020016103c9929190610b3f565b60405160208183030381529060405280519060200120915080806001019150506103ae565b507f30ca2ef0880ae63712fdaf11aefb67752968cff6f845956fcbdfcf421f4647cb8160405161041e91906108c2565b60405180910390a1919050565b5f600190505b818111610530576001548110156104b5575f60015f5f8481526020019081526020015f20546104609190610908565b9050805f5f8481526020019081526020015f2081905550817fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc8826040516104a79190610891565b60405180910390a25061051d565b5f60015f8154809291906104c890610b6a565b919050559050815f5f8381526020019081526020015f2081905550807fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc8836040516105139190610891565b60405180910390a2505b808061052890610b6a565b915050610431565b5050565b5f8167ffffffffffffffff81111561054f5761054e610bb1565b5b6040519080825280601f01601f1916602001820160405280156105815781602001600182028036833780820191505090505b5090505f5f90505b828110156105e3578060f81b8282815181106105a8576105a7610bde565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053508080600101915050610589565b507f5e53254f5b56e942cb89e1beff9257b039a5593ffe94274d0640a636b57fd0ac816040516106139190610c7b565b60405180910390a15050565b5f600190505b8181116106a6575f60015f81548092919061063f90610b6a565b919050559050815f5f8381526020019081526020015f2081905550807fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc88360405161068a9190610891565b60405180910390a250808061069e90610b6a565b915050610625565b5050565b5f5f8267ffffffffffffffff8111156106c6576106c5610bb1565b5b6040519080825280602002602001820160405280156106f45781602001602082028036833780820191505090505b5090505f5f90505b83811015610759578082828151811061071857610717610bde565b5b60200260200101818152505081818151811061073757610736610bde565b5b60200260200101518361074a9190610908565b925080806001019150506106fc565b507f542a9e74627abe4fb012aa9be028f3234ff2b2253530c6fa2220e29f03e4215d816040516107899190610d52565b60405180910390a150919050565b5f5f600190505b8281116107d6575f5f8281526020019081526020015f2054826107c19190610908565b915080806107ce90610b6a565b91505061079e565b507fe32d91cad5061d7491327c51e7b799c677b41d033204a5c5022b120f5da4becb816040516108069190610891565b60405180910390a1919050565b61040480610d7383390190565b5f5ffd5b5f819050919050565b61083681610824565b8114610840575f5ffd5b50565b5f813590506108518161082d565b92915050565b5f6020828403121561086c5761086b610820565b5b5f61087984828501610843565b91505092915050565b61088b81610824565b82525050565b5f6020820190506108a45f830184610882565b92915050565b5f819050919050565b6108bc816108aa565b82525050565b5f6020820190506108d55f8301846108b3565b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f61091282610824565b915061091d83610824565b9250828201905080821115610935576109346108db565b5b92915050565b5f61094582610824565b915061095083610824565b925082820261095e81610824565b91508282048414831517610975576109746108db565b5b5092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f6109b382610824565b91506109be83610824565b9250826109ce576109cd61097c565b5b828204905092915050565b5f6109e382610824565b91506109ee83610824565b9250826109fe576109fd61097c565b5b828206905092915050565b5f819050919050565b5f819050919050565b5f610a35610a30610a2b84610a09565b610a12565b610824565b9050919050565b610a4581610a1b565b82525050565b5f602082019050610a5e5f830184610a3c565b92915050565b5f610a6e82610824565b9150610a7983610824565b9250828203905081811115610a9157610a906108db565b5b92915050565b5f81905092915050565b7f696e697469616c000000000000000000000000000000000000000000000000005f82015250565b5f610ad5600783610a97565b9150610ae082610aa1565b600782019050919050565b5f610af582610ac9565b9150819050919050565b5f819050919050565b610b19610b14826108aa565b610aff565b82525050565b5f819050919050565b610b39610b3482610824565b610b1f565b82525050565b5f610b4a8285610b08565b602082019150610b5a8284610b28565b6020820191508190509392505050565b5f610b7482610824565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203610ba657610ba56108db565b5b600182019050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f81519050919050565b5f82825260208201905092915050565b8281835e5f83830152505050565b5f601f19601f8301169050919050565b5f610c4d82610c0b565b610c578185610c15565b9350610c67818560208601610c25565b610c7081610c33565b840191505092915050565b5f6020820190508181035f830152610c938184610c43565b905092915050565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b610ccd81610824565b82525050565b5f610cde8383610cc4565b60208301905092915050565b5f602082019050919050565b5f610d0082610c9b565b610d0a8185610ca5565b9350610d1583610cb5565b805f5b83811015610d45578151610d2c8882610cd3565b9750610d3783610cea565b925050600181019050610d18565b5085935050505092915050565b5f6020820190508181035f830152610d6a8184610cf6565b90509291505056fe6080604052348015600e575f5ffd5b50602a5f819055503360015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506103a1806100635f395ff3fe608060405234801561000f575f5ffd5b5060043610610060575f3560e01c806337ebbc03146100645780633fa4f24514610094578063573c0bd3146100b25780638da5cb5b146100ce578063c71ba63b146100ec578063f0ba844014610108575b5f5ffd5b61007e6004803603810190610079919061025b565b610138565b60405161008b9190610295565b60405180910390f35b61009c610152565b6040516100a99190610295565b60405180910390f35b6100cc60048036038101906100c7919061025b565b610157565b005b6100d6610197565b6040516100e391906102ed565b60405180910390f35b61010660048036038101906101019190610306565b6101bc565b005b610122600480360381019061011d919061025b565b61020f565b60405161012f9190610295565b60405180910390f35b5f60025f8381526020019081526020015f20549050919050565b5f5481565b805f819055507f4273d0736f60e0dedfe745e86718093d8ec8646ebd2a60cd60643eeced5658118160405161018c9190610295565b60405180910390a150565b60015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b8060025f8481526020019081526020015f20819055507f36c0e38a11934bb6e80e00c4ae42212be021022fdb5aff12c53720f1d951c2818282604051610203929190610344565b60405180910390a15050565b6002602052805f5260405f205f915090505481565b5f5ffd5b5f819050919050565b61023a81610228565b8114610244575f5ffd5b50565b5f8135905061025581610231565b92915050565b5f602082840312156102705761026f610224565b5b5f61027d84828501610247565b91505092915050565b61028f81610228565b82525050565b5f6020820190506102a85f830184610286565b92915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6102d7826102ae565b9050919050565b6102e7816102cd565b82525050565b5f6020820190506103005f8301846102de565b92915050565b5f5f6040838503121561031c5761031b610224565b5b5f61032985828601610247565b925050602061033a85828601610247565b9150509250929050565b5f6040820190506103575f830185610286565b6103646020830184610286565b939250505056fea26469706673582212209dac5c694a03c04f90df0fab6ca0b8f6cc26a78f00312f518d7d99cd098e5ab764736f6c634300081d0033a2646970667358221220c8e8483555bc94d2e804745fafd41aea77e2dee1aa2733654dad769fd6b5f2d764736f6c634300081d0033", + ABI: "[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"hash\",\"type\":\"bytes32\"}],\"name\":\"HashCalculates\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"largeData\",\"type\":\"bytes\"}],\"name\":\"LargeLog\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"arr\",\"type\":\"uint256[]\"}],\"name\":\"MemoryWritten\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"accountId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"StorageUpdate\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"sum\",\"type\":\"uint256\"}],\"name\":\"SumCalculated\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"balancesCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy\",\"outputs\":[{\"internalType\":\"contractDummy\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"depth\",\"type\":\"uint256\"}],\"name\":\"simulateCallDepth\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"simulateContractCreation\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"simulateExternalCall\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"rounds\",\"type\":\"uint256\"}],\"name\":\"simulateHashing\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"hash\",\"type\":\"bytes32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"size\",\"type\":\"uint256\"}],\"name\":\"simulateLargeEvent\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"sizeInWords\",\"type\":\"uint256\"}],\"name\":\"simulateMemory\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"sum\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"count\",\"type\":\"uint256\"}],\"name\":\"simulateModification\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"iterations\",\"type\":\"uint256\"}],\"name\":\"simulatePureCompute\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"result\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"count\",\"type\":\"uint256\"}],\"name\":\"simulateRandomWrite\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"count\",\"type\":\"uint256\"}],\"name\":\"simulateReads\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"sum\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x6080604052348015600e575f5ffd5b506114368061001c5f395ff3fe608060405234801561000f575f5ffd5b50600436106100b2575f3560e01c8063aae05a651161006f578063aae05a6514610178578063ab7611d114610194578063b77513d1146101b0578063e730b4bd146101cc578063f05ed79e146101d6578063fb0c001214610206576100b2565b8063130fcab6146100b657806332e43a11146100e65780633851d6e714610104578063542eedd9146101225780635de583ef1461013e5780637db6ecb114610148575b5f5ffd5b6100d060048036038101906100cb9190610a3c565b610236565b6040516100dd9190610a76565b60405180910390f35b6100ee6102d6565b6040516100fb9190610b09565b60405180910390f35b61010c6102fb565b6040516101199190610a76565b60405180910390f35b61013c60048036038101906101379190610a3c565b610301565b005b6101466103bb565b005b610162600480360381019061015d9190610a3c565b610422565b60405161016f9190610b3a565b60405180910390f35b610192600480360381019061018d9190610a3c565b6104cc565b005b6101ae60048036038101906101a99190610a3c565b6105d5565b005b6101ca60048036038101906101c59190610a3c565b6106c0565b005b6101d461074b565b005b6101f060048036038101906101eb9190610a3c565b61088f565b6040516101fd9190610a76565b60405180910390f35b610220600480360381019061021b9190610a3c565b61097c565b60405161022d9190610a76565b60405180910390f35b5f5f5f90505b82811015610298576001816102519190610b80565b81600283846102609190610bb3565b61026a9190610c21565b6102749190610b80565b61027e9190610c51565b826102899190610b80565b9150808060010191505061023c565b505f7fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc85f6040516102c99190610cba565b60405180910390a2919050565b60025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60015481565b5f8103610345575f7fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc85f6040516103389190610cba565b60405180910390a26103b8565b3073ffffffffffffffffffffffffffffffffffffffff1663542eedd960018361036e9190610cd3565b6040518263ffffffff1660e01b815260040161038a9190610a76565b5f604051808303815f87803b1580156103a1575f5ffd5b505af11580156103b3573d5f5f3e3d5ffd5b505050505b50565b6040516103c7906109f8565b604051809103905ff0801580156103e0573d5f5f3e3d5ffd5b5060025f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b5f60405160200161043290610d5a565b6040516020818303038152906040528051906020012090505f5f90505b8281101561048f57818160405160200161046a929190610dae565b604051602081830303815290604052805190602001209150808060010191505061044f565b507f30ca2ef0880ae63712fdaf11aefb67752968cff6f845956fcbdfcf421f4647cb816040516104bf9190610b3a565b60405180910390a1919050565b5f600190505b8181116105d157600154811015610556575f60015f5f8481526020019081526020015f20546105019190610b80565b9050805f5f8481526020019081526020015f2081905550817fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc8826040516105489190610a76565b60405180910390a2506105be565b5f60015f81548092919061056990610dd9565b919050559050815f5f8381526020019081526020015f2081905550807fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc8836040516105b49190610a76565b60405180910390a2505b80806105c990610dd9565b9150506104d2565b5050565b5f8167ffffffffffffffff8111156105f0576105ef610e20565b5b6040519080825280601f01601f1916602001820160405280156106225781602001600182028036833780820191505090505b5090505f5f90505b82811015610684578060f81b82828151811061064957610648610e4d565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a905350808060010191505061062a565b507f5e53254f5b56e942cb89e1beff9257b039a5593ffe94274d0640a636b57fd0ac816040516106b49190610eea565b60405180910390a15050565b5f600190505b818111610747575f60015f8154809291906106e090610dd9565b919050559050815f5f8381526020019081526020015f2081905550807fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc88360405161072b9190610a76565b60405180910390a250808061073f90610dd9565b9150506106c6565b5050565b5f73ffffffffffffffffffffffffffffffffffffffff1660025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1603610805576040516107ab906109f8565b604051809103905ff0801580156107c4573d5f5f3e3d5ffd5b5060025f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b60025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663573c0bd3602a6040518263ffffffff1660e01b81526004016108609190610f43565b5f604051808303815f87803b158015610877575f5ffd5b505af1158015610889573d5f5f3e3d5ffd5b50505050565b5f5f8267ffffffffffffffff8111156108ab576108aa610e20565b5b6040519080825280602002602001820160405280156108d95781602001602082028036833780820191505090505b5090505f5f90505b8381101561093e57808282815181106108fd576108fc610e4d565b5b60200260200101818152505081818151811061091c5761091b610e4d565b5b60200260200101518361092f9190610b80565b925080806001019150506108e1565b507f542a9e74627abe4fb012aa9be028f3234ff2b2253530c6fa2220e29f03e4215d8160405161096e9190611013565b60405180910390a150919050565b5f5f600190505b8281116109bb575f5f8281526020019081526020015f2054826109a69190610b80565b915080806109b390610dd9565b915050610983565b507fe32d91cad5061d7491327c51e7b799c677b41d033204a5c5022b120f5da4becb816040516109eb9190610a76565b60405180910390a1919050565b6103cd8061103483390190565b5f5ffd5b5f819050919050565b610a1b81610a09565b8114610a25575f5ffd5b50565b5f81359050610a3681610a12565b92915050565b5f60208284031215610a5157610a50610a05565b5b5f610a5e84828501610a28565b91505092915050565b610a7081610a09565b82525050565b5f602082019050610a895f830184610a67565b92915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f819050919050565b5f610ad1610acc610ac784610a8f565b610aae565b610a8f565b9050919050565b5f610ae282610ab7565b9050919050565b5f610af382610ad8565b9050919050565b610b0381610ae9565b82525050565b5f602082019050610b1c5f830184610afa565b92915050565b5f819050919050565b610b3481610b22565b82525050565b5f602082019050610b4d5f830184610b2b565b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f610b8a82610a09565b9150610b9583610a09565b9250828201905080821115610bad57610bac610b53565b5b92915050565b5f610bbd82610a09565b9150610bc883610a09565b9250828202610bd681610a09565b91508282048414831517610bed57610bec610b53565b5b5092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f610c2b82610a09565b9150610c3683610a09565b925082610c4657610c45610bf4565b5b828204905092915050565b5f610c5b82610a09565b9150610c6683610a09565b925082610c7657610c75610bf4565b5b828206905092915050565b5f819050919050565b5f610ca4610c9f610c9a84610c81565b610aae565b610a09565b9050919050565b610cb481610c8a565b82525050565b5f602082019050610ccd5f830184610cab565b92915050565b5f610cdd82610a09565b9150610ce883610a09565b9250828203905081811115610d0057610cff610b53565b5b92915050565b5f81905092915050565b7f696e697469616c000000000000000000000000000000000000000000000000005f82015250565b5f610d44600783610d06565b9150610d4f82610d10565b600782019050919050565b5f610d6482610d38565b9150819050919050565b5f819050919050565b610d88610d8382610b22565b610d6e565b82525050565b5f819050919050565b610da8610da382610a09565b610d8e565b82525050565b5f610db98285610d77565b602082019150610dc98284610d97565b6020820191508190509392505050565b5f610de382610a09565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203610e1557610e14610b53565b5b600182019050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f81519050919050565b5f82825260208201905092915050565b8281835e5f83830152505050565b5f601f19601f8301169050919050565b5f610ebc82610e7a565b610ec68185610e84565b9350610ed6818560208601610e94565b610edf81610ea2565b840191505092915050565b5f6020820190508181035f830152610f028184610eb2565b905092915050565b5f819050919050565b5f610f2d610f28610f2384610f0a565b610aae565b610a09565b9050919050565b610f3d81610f13565b82525050565b5f602082019050610f565f830184610f34565b92915050565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b610f8e81610a09565b82525050565b5f610f9f8383610f85565b60208301905092915050565b5f602082019050919050565b5f610fc182610f5c565b610fcb8185610f66565b9350610fd683610f76565b805f5b83811015611006578151610fed8882610f94565b9750610ff883610fab565b925050600181019050610fd9565b5085935050505092915050565b5f6020820190508181035f83015261102b8184610fb7565b90509291505056fe6080604052348015600e575f5ffd5b50602a5f819055503360015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555061036a806100635f395ff3fe608060405234801561000f575f5ffd5b5060043610610060575f3560e01c806337ebbc03146100645780633fa4f24514610094578063573c0bd3146100b25780638da5cb5b146100ce578063c71ba63b146100ec578063f0ba844014610108575b5f5ffd5b61007e60048036038101906100799190610224565b610138565b60405161008b919061025e565b60405180910390f35b61009c610152565b6040516100a9919061025e565b60405180910390f35b6100cc60048036038101906100c79190610224565b610157565b005b6100d6610160565b6040516100e391906102b6565b60405180910390f35b610106600480360381019061010191906102cf565b610185565b005b610122600480360381019061011d9190610224565b6101d8565b60405161012f919061025e565b60405180910390f35b5f60025f8381526020019081526020015f20549050919050565b5f5481565b805f8190555050565b60015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b8060025f8481526020019081526020015f20819055507f36c0e38a11934bb6e80e00c4ae42212be021022fdb5aff12c53720f1d951c28182826040516101cc92919061030d565b60405180910390a15050565b6002602052805f5260405f205f915090505481565b5f5ffd5b5f819050919050565b610203816101f1565b811461020d575f5ffd5b50565b5f8135905061021e816101fa565b92915050565b5f60208284031215610239576102386101ed565b5b5f61024684828501610210565b91505092915050565b610258816101f1565b82525050565b5f6020820190506102715f83018461024f565b92915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6102a082610277565b9050919050565b6102b081610296565b82525050565b5f6020820190506102c95f8301846102a7565b92915050565b5f5f604083850312156102e5576102e46101ed565b5b5f6102f285828601610210565b925050602061030385828601610210565b9150509250929050565b5f6040820190506103205f83018561024f565b61032d602083018461024f565b939250505056fea26469706673582212201893623673c7d322a3501952be3510a3b2582a332e23a9ee7765420a7e2c8f7464736f6c634300081d0033a264697066735822122090228c8b6f95fa9d531e807cb5b42e7e95e0169608120a681aef450c806270de64736f6c634300081d0033", } // EVMLoadSimulatorABI is the input ABI used to generate the binding from. @@ -233,6 +233,37 @@ func (_EVMLoadSimulator *EVMLoadSimulatorCallerSession) BalancesCount() (*big.In return _EVMLoadSimulator.Contract.BalancesCount(&_EVMLoadSimulator.CallOpts) } +// Dummy is a free data retrieval call binding the contract method 0x32e43a11. +// +// Solidity: function dummy() view returns(address) +func (_EVMLoadSimulator *EVMLoadSimulatorCaller) Dummy(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _EVMLoadSimulator.contract.Call(opts, &out, "dummy") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// Dummy is a free data retrieval call binding the contract method 0x32e43a11. +// +// Solidity: function dummy() view returns(address) +func (_EVMLoadSimulator *EVMLoadSimulatorSession) Dummy() (common.Address, error) { + return _EVMLoadSimulator.Contract.Dummy(&_EVMLoadSimulator.CallOpts) +} + +// Dummy is a free data retrieval call binding the contract method 0x32e43a11. +// +// Solidity: function dummy() view returns(address) +func (_EVMLoadSimulator *EVMLoadSimulatorCallerSession) Dummy() (common.Address, error) { + return _EVMLoadSimulator.Contract.Dummy(&_EVMLoadSimulator.CallOpts) +} + // SimulateCallDepth is a paid mutator transaction binding the contract method 0x542eedd9. // // Solidity: function simulateCallDepth(uint256 depth) returns() @@ -275,6 +306,27 @@ func (_EVMLoadSimulator *EVMLoadSimulatorTransactorSession) SimulateContractCrea return _EVMLoadSimulator.Contract.SimulateContractCreation(&_EVMLoadSimulator.TransactOpts) } +// SimulateExternalCall is a paid mutator transaction binding the contract method 0xe730b4bd. +// +// Solidity: function simulateExternalCall() returns() +func (_EVMLoadSimulator *EVMLoadSimulatorTransactor) SimulateExternalCall(opts *bind.TransactOpts) (*types.Transaction, error) { + return _EVMLoadSimulator.contract.Transact(opts, "simulateExternalCall") +} + +// SimulateExternalCall is a paid mutator transaction binding the contract method 0xe730b4bd. +// +// Solidity: function simulateExternalCall() returns() +func (_EVMLoadSimulator *EVMLoadSimulatorSession) SimulateExternalCall() (*types.Transaction, error) { + return _EVMLoadSimulator.Contract.SimulateExternalCall(&_EVMLoadSimulator.TransactOpts) +} + +// SimulateExternalCall is a paid mutator transaction binding the contract method 0xe730b4bd. +// +// Solidity: function simulateExternalCall() returns() +func (_EVMLoadSimulator *EVMLoadSimulatorTransactorSession) SimulateExternalCall() (*types.Transaction, error) { + return _EVMLoadSimulator.Contract.SimulateExternalCall(&_EVMLoadSimulator.TransactOpts) +} + // SimulateHashing is a paid mutator transaction binding the contract method 0x7db6ecb1. // // Solidity: function simulateHashing(uint256 rounds) returns(bytes32 hash) diff --git a/tests/load/c/contracts/EVMLoadSimulator.sol b/tests/load/c/contracts/EVMLoadSimulator.sol index 9069200ad9e2..0768e634f81f 100644 --- a/tests/load/c/contracts/EVMLoadSimulator.sol +++ b/tests/load/c/contracts/EVMLoadSimulator.sol @@ -15,6 +15,9 @@ contract EVMLoadSimulator { event MemoryWritten(uint256[] arr); event LargeLog(bytes largeData); + // Dummy contract + Dummy public dummy; + // Simulate random storage writes function simulateRandomWrite(uint256 count) external { for (uint256 i = 1; i <= count; i++) { @@ -78,7 +81,7 @@ contract EVMLoadSimulator { } function simulateContractCreation() external { - new Dummy(); + dummy = new Dummy(); } // Measure pure computation cost without memory/storage overhead. @@ -100,4 +103,11 @@ contract EVMLoadSimulator { } emit LargeLog(data); } + + function simulateExternalCall() external { + if (dummy == Dummy(address(0))) { + dummy = new Dummy(); + } + dummy.updateValue(42); + } } diff --git a/tests/load/c/issuers/opcode.go b/tests/load/c/issuers/opcode.go index b29dfd688d91..604e0f044eb1 100644 --- a/tests/load/c/issuers/opcode.go +++ b/tests/load/c/issuers/opcode.go @@ -128,6 +128,8 @@ func (o *Opcoder) GenerateAndIssueTx(ctx context.Context) (common.Hash, error) { case largeEvent: const maxEventSize = 100 tx, err = o.contractInstance.SimulateLargeEvent(txOpts, big.NewInt(maxEventSize)) + case externalCall: + tx, err = o.contractInstance.SimulateExternalCall(txOpts) default: return common.Hash{}, fmt.Errorf("invalid load type: %s", loadType) } @@ -152,6 +154,7 @@ const ( contractCreation = "contract creation" pureCompute = "pure compute" largeEvent = "large event" + externalCall = "external call" ) func allLoadTypes() []string { @@ -165,6 +168,7 @@ func allLoadTypes() []string { contractCreation, pureCompute, largeEvent, + externalCall, } } From a100eb113532395971b11f09b24894826de67089 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Fri, 16 May 2025 17:26:26 +0200 Subject: [PATCH 114/197] Remove unneeded ensureKey calls --- tests/load/c/ginkgo_test.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/load/c/ginkgo_test.go b/tests/load/c/ginkgo_test.go index 8fc240f1c805..0c594322d55e 100644 --- a/tests/load/c/ginkgo_test.go +++ b/tests/load/c/ginkgo_test.go @@ -61,10 +61,6 @@ var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { env := e2e.GetEnv(tc) network = env.GetNetwork() network.Nodes = network.Nodes[:nodesCount] - for _, node := range network.Nodes { - err := node.EnsureKeys() - require.NoError(tc, err, "ensuring keys for node %s", node.NodeID) - } }) ginkgo.It("C-Chain simple", func(ctx context.Context) { From 53e717a5145858186ab6345bc0ce02bced909e20 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Mon, 19 May 2025 17:51:22 +0200 Subject: [PATCH 115/197] Rename `config` to `loadConfig` --- tests/load/c/execute.go | 6 +++--- tests/load/c/ginkgo_test.go | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/load/c/execute.go b/tests/load/c/execute.go index 06d206cb448f..b47dd6edfdef 100644 --- a/tests/load/c/execute.go +++ b/tests/load/c/execute.go @@ -23,7 +23,7 @@ import ( ethcrypto "github.com/ava-labs/libevm/crypto" ) -type config struct { +type loadConfig struct { endpoints []string maxFeeCap int64 agents uint @@ -40,7 +40,7 @@ const ( issuerOpcoder issuerType = "opcoder" ) -func execute(ctx context.Context, preFundedKeys []*secp256k1.PrivateKey, config config) error { +func execute(ctx context.Context, preFundedKeys []*secp256k1.PrivateKey, config loadConfig) error { logger := logging.NewLogger("", logging.NewWrappedCore(logging.Info, os.Stdout, logging.Auto.ConsoleEncoder())) keys, err := fixKeysCount(preFundedKeys, int(config.agents)) @@ -123,7 +123,7 @@ func fixKeysCount(preFundedKeys []*secp256k1.PrivateKey, target int) ([]*ecdsa.P // It creates them in parallel because creating issuers can sometimes take a while, // and this adds up for many agents. For example, deploying the Opcoder contract // takes a few seconds. Running the creation in parallel can reduce the time significantly. -func createAgents(ctx context.Context, config config, keys []*ecdsa.PrivateKey, +func createAgents(ctx context.Context, config loadConfig, keys []*ecdsa.PrivateKey, tracker *load.Tracker[common.Hash], ) ([]load.Agent[common.Hash], error) { ctx, cancel := context.WithCancel(ctx) diff --git a/tests/load/c/ginkgo_test.go b/tests/load/c/ginkgo_test.go index 0c594322d55e..9a9cd3122d1e 100644 --- a/tests/load/c/ginkgo_test.go +++ b/tests/load/c/ginkgo_test.go @@ -67,7 +67,7 @@ var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { const blockchainID = "C" endpoints, err := tmpnet.GetNodeWebsocketURIs(network.Nodes, blockchainID) require.NoError(ginkgo.GinkgoT(), err, "getting node websocket URIs") - config := config{ + config := loadConfig{ endpoints: endpoints, issuer: issuerSimple, maxFeeCap: 3000, @@ -86,7 +86,7 @@ var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { const blockchainID = "C" endpoints, err := tmpnet.GetNodeWebsocketURIs(network.Nodes, blockchainID) require.NoError(ginkgo.GinkgoT(), err, "getting node websocket URIs") - config := config{ + config := loadConfig{ endpoints: endpoints, issuer: issuerOpcoder, maxFeeCap: 300000000000, From f9e8ec0ebebbc3fd9b2de0053ad082ae4879ef2c Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Mon, 19 May 2025 17:57:13 +0200 Subject: [PATCH 116/197] wip bump gas limit per second --- tests/load/c/ginkgo_test.go | 78 +++++++++++++++++++++++++------------ 1 file changed, 54 insertions(+), 24 deletions(-) diff --git a/tests/load/c/ginkgo_test.go b/tests/load/c/ginkgo_test.go index 9a9cd3122d1e..3517a626f7b9 100644 --- a/tests/load/c/ginkgo_test.go +++ b/tests/load/c/ginkgo_test.go @@ -5,11 +5,14 @@ package c import ( "context" + "encoding/json" "testing" + "github.com/ava-labs/coreth/core" "github.com/onsi/ginkgo/v2" "github.com/stretchr/testify/require" + "github.com/ava-labs/avalanchego/config" "github.com/ava-labs/avalanchego/tests/fixture/e2e" "github.com/ava-labs/avalanchego/tests/fixture/tmpnet" ) @@ -26,18 +29,20 @@ func init() { flagVars = e2e.RegisterFlagsWithDefaultOwner("avalanchego-load") } -const nodesCount = 5 +const nodesCount = 1 var _ = ginkgo.SynchronizedBeforeSuite(func() []byte { // Run only once in the first ginkgo process - require.GreaterOrEqual(ginkgo.GinkgoT(), nodesCount, 5, "number of nodes must be at least 5") + // require.GreaterOrEqual(ginkgo.GinkgoT(), nodesCount, 5, "number of nodes must be at least 5") tc := e2e.NewTestContext() nodes := tmpnet.NewNodesOrPanic(nodesCount) network := &tmpnet.Network{ Owner: "avalanchego-load-test", Nodes: nodes, } + const preFundedKeysCount = 50 + setNetworkGas(tc, network, preFundedKeysCount) env := e2e.NewTestEnvironment( tc, @@ -70,10 +75,10 @@ var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { config := loadConfig{ endpoints: endpoints, issuer: issuerSimple, - maxFeeCap: 3000, - agents: 1, - minTPS: 50, - maxTPS: 90, + maxFeeCap: 4761904, // max fee cap equivalent to 100 ether + agents: 30, + minTPS: 95, + maxTPS: 150, step: 10, } err = execute(ctx, network.PreFundedKeys, config) @@ -82,22 +87,47 @@ var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { } }) - ginkgo.It("C-Chain opcoder", func(ctx context.Context) { - const blockchainID = "C" - endpoints, err := tmpnet.GetNodeWebsocketURIs(network.Nodes, blockchainID) - require.NoError(ginkgo.GinkgoT(), err, "getting node websocket URIs") - config := loadConfig{ - endpoints: endpoints, - issuer: issuerOpcoder, - maxFeeCap: 300000000000, - agents: 1, - minTPS: 30, - maxTPS: 60, - step: 5, - } - err = execute(ctx, network.PreFundedKeys, config) - if err != nil { - ginkgo.GinkgoT().Error(err) - } - }) + // ginkgo.It("C-Chain opcoder", func(ctx context.Context) { + // const blockchainID = "C" + // endpoints, err := tmpnet.GetNodeWebsocketURIs(network.Nodes, blockchainID) + // require.NoError(ginkgo.GinkgoT(), err, "getting node websocket URIs") + // config := loadConfig{ + // endpoints: endpoints, + // issuer: issuerOpcoder, + // maxFeeCap: 300000000000, + // agents: 10, + // minTPS: 30, + // maxTPS: 100, + // step: 10, + // } + // err = execute(ctx, network.PreFundedKeys, config) + // if err != nil { + // ginkgo.GinkgoT().Error(err) + // } + // }) }) + +func setNetworkGas(t require.TestingT, network *tmpnet.Network, preFundedKeysCount int) { + if network.DefaultFlags == nil { + network.DefaultFlags = make(tmpnet.FlagsMap) + } + network.DefaultFlags[config.DynamicFeesMaxGasCapacityKey] = "1000000000000000000" + network.DefaultFlags[config.DynamicFeesMaxGasPerSecondKey] = "10000000000000000000" + network.DefaultFlags[config.DynamicFeesTargetGasPerSecondKey] = "10000000000000000000" + + // We must set the pre-funded keys to generate a default genesis + // with those keys. + preFundedKeys, err := tmpnet.NewPrivateKeys(preFundedKeysCount) + require.NoError(t, err, "creating pre-funded keys") + network.PreFundedKeys = preFundedKeys + + network.Genesis, err = network.DefaultGenesis() + require.NoError(t, err, "creating genesis") + var cChainGenesis core.Genesis + err = json.Unmarshal([]byte(network.Genesis.CChainGenesis), &cChainGenesis) + require.NoError(t, err, "unmarshalling genesis") + cChainGenesis.GasLimit = 10000000000000000000 + encodedChainGenesis, err := json.Marshal(cChainGenesis) + require.NoError(t, err, "marshalling C chain genesis") + network.Genesis.CChainGenesis = string(encodedChainGenesis) +} From 411468843a9e93027bcb5ac70119632c003f8238 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Tue, 20 May 2025 12:58:38 +0200 Subject: [PATCH 117/197] Use max possible fee cap for simple txs --- tests/load/c/ginkgo_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/load/c/ginkgo_test.go b/tests/load/c/ginkgo_test.go index 9a9cd3122d1e..08b5583bec85 100644 --- a/tests/load/c/ginkgo_test.go +++ b/tests/load/c/ginkgo_test.go @@ -70,7 +70,7 @@ var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { config := loadConfig{ endpoints: endpoints, issuer: issuerSimple, - maxFeeCap: 3000, + maxFeeCap: 4761904, // max fee cap equivalent to 100 ether agents: 1, minTPS: 50, maxTPS: 90, From 201be41ddd6a53fa3348963679fac6f5c231ced2 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Tue, 20 May 2025 13:10:35 +0200 Subject: [PATCH 118/197] Use coreth with high gas limit --- go.mod | 2 +- go.sum | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 31f6058ca2aa..477af90157c8 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/NYTimes/gziphandler v1.1.1 github.com/StephenButtolph/canoto v0.15.0 github.com/antithesishq/antithesis-sdk-go v0.3.8 - github.com/ava-labs/coreth v0.15.1-rc.1 + github.com/ava-labs/coreth v0.15.1-rc.0.0.20250520105702-ca776d7463bf github.com/ava-labs/ledger-avalanche/go v0.0.0-20241009183145-e6f90a8a1a60 github.com/ava-labs/libevm v1.13.14-0.2.0.release github.com/btcsuite/btcd/btcutil v1.1.3 diff --git a/go.sum b/go.sum index 6881d4188a19..3d03dd8c9bcc 100644 --- a/go.sum +++ b/go.sum @@ -68,8 +68,8 @@ github.com/antithesishq/antithesis-sdk-go v0.3.8/go.mod h1:IUpT2DPAKh6i/YhSbt6Gl github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/ava-labs/coreth v0.15.1-rc.1 h1:TsewI2vMa4XV5HGp3spFjEOtqaW48R10K6KOd8aM5as= -github.com/ava-labs/coreth v0.15.1-rc.1/go.mod h1:fWfu0nOh9GLmRRMh1GJH14p1ycPQob9q7GlELNpIPBY= +github.com/ava-labs/coreth v0.15.1-rc.0.0.20250520105702-ca776d7463bf h1:ZxPyP3vbjkumzYBlDEW8EXV3Z5JUpChEufJBPoNtRRs= +github.com/ava-labs/coreth v0.15.1-rc.0.0.20250520105702-ca776d7463bf/go.mod h1:6HipGuNCN6IIe30AXwvG11Ja0AudQMtrMdTypjsju5U= github.com/ava-labs/ledger-avalanche/go v0.0.0-20241009183145-e6f90a8a1a60 h1:EL66gtXOAwR/4KYBjOV03LTWgkEXvLePribLlJNu4g0= github.com/ava-labs/ledger-avalanche/go v0.0.0-20241009183145-e6f90a8a1a60/go.mod h1:/7qKobTfbzBu7eSTVaXMTr56yTYk4j2Px6/8G+idxHo= github.com/ava-labs/libevm v1.13.14-0.2.0.release h1:uKGCc5/ceeBbfAPRVtBUxbQt50WzB2pEDb8Uy93ePgQ= @@ -258,7 +258,10 @@ github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM= +github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= +github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4= github.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= From 2a684557642dc69c3d71f840771709c724b60364 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Tue, 20 May 2025 17:01:08 +0200 Subject: [PATCH 119/197] Distribute uses the max fee cap (100 eth) to send its transactions --- tests/load/c/distribute.go | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/tests/load/c/distribute.go b/tests/load/c/distribute.go index befacbaa3ae4..247e93fe3c5c 100644 --- a/tests/load/c/distribute.go +++ b/tests/load/c/distribute.go @@ -28,12 +28,9 @@ func distribute(ctx context.Context, endpoint string, keys []*ecdsa.PrivateKey) return fmt.Errorf("dialing %s: %w", endpoint, err) } - gasFeeCap, err := client.EstimateBaseFee(ctx) - if err != nil { - return fmt.Errorf("getting estimated base fee: %w", err) - } + maxGasFeeCap := new(big.Int).SetInt64(4761904) // equivalent to 100 ether txCost := big.NewInt(params.GWei) - txCost.Mul(txCost, gasFeeCap) + txCost.Mul(txCost, maxGasFeeCap) txCost.Mul(txCost, new(big.Int).SetUint64(params.TxGas)) keyToBalance, err := getKeyToBalance(ctx, client, keys) @@ -43,7 +40,7 @@ func distribute(ctx context.Context, endpoint string, keys []*ecdsa.PrivateKey) minBalance := determineMinBalance(keyToBalance, txCost) - txs, err := createTxs(ctx, client, keyToBalance, minBalance, txCost, gasFeeCap) + txs, err := createTxs(ctx, client, keyToBalance, minBalance, txCost, maxGasFeeCap) if err != nil { return fmt.Errorf("creating transactions: %w", err) } else if len(txs) == 0 { From 41125d7a40d621bafd3461c2c87dc7d1cb24ab3b Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Tue, 20 May 2025 17:06:06 +0200 Subject: [PATCH 120/197] Set pre-funded keys to the number of agents --- tests/load/c/ginkgo_test.go | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/tests/load/c/ginkgo_test.go b/tests/load/c/ginkgo_test.go index 08b5583bec85..39682eab1fb3 100644 --- a/tests/load/c/ginkgo_test.go +++ b/tests/load/c/ginkgo_test.go @@ -26,7 +26,11 @@ func init() { flagVars = e2e.RegisterFlagsWithDefaultOwner("avalanchego-load") } -const nodesCount = 5 +const ( + nodesCount = 5 + agentsPerNode = 50 + agentsCount = nodesCount * agentsPerNode +) var _ = ginkgo.SynchronizedBeforeSuite(func() []byte { // Run only once in the first ginkgo process @@ -38,6 +42,7 @@ var _ = ginkgo.SynchronizedBeforeSuite(func() []byte { Owner: "avalanchego-load-test", Nodes: nodes, } + setPrefundedKeys(tc, network, agentsCount) env := e2e.NewTestEnvironment( tc, @@ -71,7 +76,7 @@ var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { endpoints: endpoints, issuer: issuerSimple, maxFeeCap: 4761904, // max fee cap equivalent to 100 ether - agents: 1, + agents: agentsPerNode, minTPS: 50, maxTPS: 90, step: 10, @@ -90,7 +95,7 @@ var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { endpoints: endpoints, issuer: issuerOpcoder, maxFeeCap: 300000000000, - agents: 1, + agents: agentsCount, minTPS: 30, maxTPS: 60, step: 5, @@ -101,3 +106,15 @@ var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { } }) }) + +// setPrefundedKeys sets the pre-funded keys for the network, and keeps +// keys already set if any. If there are more keys than required, it +// keeps the already set keys as they are. +func setPrefundedKeys(t require.TestingT, network *tmpnet.Network, minKeys int) { + if len(network.PreFundedKeys) >= minKeys { + return + } + missingPreFundedKeys, err := tmpnet.NewPrivateKeys(minKeys - len(network.PreFundedKeys)) + require.NoError(t, err, "creating pre-funded keys") + network.PreFundedKeys = append(network.PreFundedKeys, missingPreFundedKeys...) +} From 564610a6415ae064ccc06d965d83b368db3b1256 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Tue, 20 May 2025 17:10:48 +0200 Subject: [PATCH 121/197] Remove now unneeded distribution --- tests/load/c/distribute.go | 256 ------------------------------------- tests/load/c/execute.go | 29 +---- 2 files changed, 3 insertions(+), 282 deletions(-) delete mode 100644 tests/load/c/distribute.go diff --git a/tests/load/c/distribute.go b/tests/load/c/distribute.go deleted file mode 100644 index 247e93fe3c5c..000000000000 --- a/tests/load/c/distribute.go +++ /dev/null @@ -1,256 +0,0 @@ -// Co// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package c - -import ( - "context" - "crypto/ecdsa" - "fmt" - "math/big" - "sync" - - "github.com/ava-labs/coreth/ethclient" - "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/core/types" - "github.com/ava-labs/libevm/params" - - ethcrypto "github.com/ava-labs/libevm/crypto" -) - -// distribute distributes as close to equally the funds for each given key. -// It is not exact because of the gas fees for each transaction, so a minimum -// balance required for each key is calculated as follows: -// minBalance = averageBalance - (numberOfKeys * txCost) -func distribute(ctx context.Context, endpoint string, keys []*ecdsa.PrivateKey) error { - client, err := ethclient.DialContext(ctx, endpoint) - if err != nil { - return fmt.Errorf("dialing %s: %w", endpoint, err) - } - - maxGasFeeCap := new(big.Int).SetInt64(4761904) // equivalent to 100 ether - txCost := big.NewInt(params.GWei) - txCost.Mul(txCost, maxGasFeeCap) - txCost.Mul(txCost, new(big.Int).SetUint64(params.TxGas)) - - keyToBalance, err := getKeyToBalance(ctx, client, keys) - if err != nil { - return fmt.Errorf("getting key-balance pairs: %w", err) - } - - minBalance := determineMinBalance(keyToBalance, txCost) - - txs, err := createTxs(ctx, client, keyToBalance, minBalance, txCost, maxGasFeeCap) - if err != nil { - return fmt.Errorf("creating transactions: %w", err) - } else if len(txs) == 0 { - return nil - } - - collector := newAcceptedTxsCollector(client) - err = collector.start(ctx) - if err != nil { - return fmt.Errorf("subscribing to accepted transactions: %w", err) - } - - for _, tx := range txs { - if err := client.SendTransaction(ctx, tx); err != nil { - return fmt.Errorf("sending transaction: %w", err) - } - collector.issued(tx.Hash()) - } - - if err := collector.wait(ctx); err != nil { - return fmt.Errorf("waiting for accepted transactions: %w", err) - } - - err = checkBalances(ctx, client, keys, minBalance) - if err != nil { - return fmt.Errorf("checking balances after funding: %w", err) - } - - return nil -} - -func getKeyToBalance(ctx context.Context, client ethclient.Client, keys []*ecdsa.PrivateKey) (map[*ecdsa.PrivateKey]*big.Int, error) { - keyToBalance := make(map[*ecdsa.PrivateKey]*big.Int, len(keys)) - for _, key := range keys { - address := ethcrypto.PubkeyToAddress(key.PublicKey) - blockNumber := (*big.Int)(nil) - balance, err := client.BalanceAt(ctx, address, blockNumber) - if err != nil { - return nil, fmt.Errorf("fetching balance for address %s: %w", address, err) - } - keyToBalance[key] = balance - } - - return keyToBalance, nil -} - -func determineMinBalance(keyToBalance map[*ecdsa.PrivateKey]*big.Int, txCost *big.Int) *big.Int { - totalBalance := new(big.Int) - for _, balance := range keyToBalance { - totalBalance.Add(totalBalance, balance) - } - averageBalance := new(big.Int).Div(totalBalance, big.NewInt(int64(len(keyToBalance)))) - maxTxCosts := new(big.Int).Mul(txCost, big.NewInt(int64(len(keyToBalance)))) - return averageBalance.Sub(averageBalance, maxTxCosts) -} - -func createTxs(ctx context.Context, client ethclient.Client, keyToBalance map[*ecdsa.PrivateKey]*big.Int, minBalance, txCost, gasFeeCap *big.Int) ([]*types.Transaction, error) { - chainID, err := client.ChainID(ctx) - if err != nil { - return nil, fmt.Errorf("getting chain id: %w", err) - } - - var txs []*types.Transaction - for toKey, balance := range keyToBalance { - diff := new(big.Int).Sub(balance, minBalance) - if diff.Cmp(common.Big0) >= 0 { - continue // this key has enough funds - } - diff = new(big.Int).Neg(diff) - to := ethcrypto.PubkeyToAddress(toKey.PublicKey) - remainingToMove := new(big.Int).Set(diff) - for fromKey, fromBalance := range keyToBalance { - if fromKey == toKey { - continue - } - available := new(big.Int).Sub(fromBalance, minBalance) - available.Sub(available, txCost) - if available.Cmp(common.Big0) <= 0 { - continue // this key has not enough funds - } - toMove := diff - if available.Cmp(diff) < 0 { - // not enough funds to send diff entirely - toMove = available - } - remainingToMove.Sub(remainingToMove, toMove) - fromBalance.Sub(fromBalance, toMove) - - fromAddr := ethcrypto.PubkeyToAddress(fromKey.PublicKey) - blockNumber := (*big.Int)(nil) - nonce, err := client.NonceAt(ctx, fromAddr, blockNumber) - if err != nil { - return nil, fmt.Errorf("getting nonce for address %s: %w", fromAddr, err) - } - - signer := types.LatestSignerForChainID(chainID) - tx, err := types.SignNewTx(fromKey, signer, &types.DynamicFeeTx{ - ChainID: chainID, - Nonce: nonce, - GasTipCap: big.NewInt(0), - GasFeeCap: gasFeeCap, - Gas: params.TxGas, - To: &to, - Data: nil, - Value: toMove, - }) - if err != nil { - return nil, fmt.Errorf("signing transaction: %w", err) - } - txs = append(txs, tx) - - if remainingToMove.Cmp(common.Big0) == 0 { - break - } - } - } - return txs, nil -} - -func checkBalances(ctx context.Context, client ethclient.Client, keys []*ecdsa.PrivateKey, minBalance *big.Int) error { - keyToBalance, err := getKeyToBalance(ctx, client, keys) - if err != nil { - return fmt.Errorf("getting key to balance for newly funded keys: %w", err) - } - for key, balance := range keyToBalance { - if balance.Cmp(minBalance) < 0 { - address := ethcrypto.PubkeyToAddress(key.PublicKey) - return fmt.Errorf("address %s has insufficient funds %d < %d", address, balance, minBalance) - } - } - return nil -} - -type acceptedTxsCollector struct { - // Injected parameters - client ethclient.Client - - // State - lock sync.RWMutex - inFlight map[common.Hash]struct{} - allIssued bool - waitErr <-chan error -} - -func newAcceptedTxsCollector(client ethclient.Client) *acceptedTxsCollector { - return &acceptedTxsCollector{ - client: client, - inFlight: make(map[common.Hash]struct{}), - } -} - -// start subscribes to the new accepted transactions and -// collects until either the context is done, an error occurs, or all -// transactions are issued and accepted. -func (c *acceptedTxsCollector) start(ctx context.Context) error { - newAcceptedTxsCh := make(chan *common.Hash) - subscription, err := c.client.SubscribeNewAcceptedTransactions(ctx, newAcceptedTxsCh) - if err != nil { - return err - } - - ready := make(chan struct{}) - waitErr := make(chan error) - c.waitErr = waitErr - go func() { - defer subscription.Unsubscribe() - close(ready) - for { - select { - case hash := <-newAcceptedTxsCh: - if hash == nil { - continue - } - c.lock.Lock() - delete(c.inFlight, *hash) - if c.allIssued && len(c.inFlight) == 0 { - waitErr <- nil - c.lock.Unlock() - return - } - c.lock.Unlock() - case <-ctx.Done(): - waitErr <- ctx.Err() - return - case err := <-subscription.Err(): - waitErr <- err - return - } - } - }() - <-ready - - return nil -} - -func (c *acceptedTxsCollector) issued(hash common.Hash) { - c.lock.Lock() - defer c.lock.Unlock() - c.inFlight[hash] = struct{}{} -} - -// wait must only be called after all transactions are issued. -func (c *acceptedTxsCollector) wait(ctx context.Context) error { - c.lock.Lock() - c.allIssued = true - c.lock.Unlock() - select { - case <-ctx.Done(): - return ctx.Err() - case err := <-c.waitErr: - return err - } -} diff --git a/tests/load/c/execute.go b/tests/load/c/execute.go index b47dd6edfdef..3c1b296d89e2 100644 --- a/tests/load/c/execute.go +++ b/tests/load/c/execute.go @@ -43,14 +43,9 @@ const ( func execute(ctx context.Context, preFundedKeys []*secp256k1.PrivateKey, config loadConfig) error { logger := logging.NewLogger("", logging.NewWrappedCore(logging.Info, os.Stdout, logging.Auto.ConsoleEncoder())) - keys, err := fixKeysCount(preFundedKeys, int(config.agents)) - if err != nil { - return fmt.Errorf("fixing keys count: %w", err) - } - - err = distribute(ctx, config.endpoints[0], keys) - if err != nil { - return fmt.Errorf("ensuring minimum funds: %w", err) + keys := make([]*ecdsa.PrivateKey, len(preFundedKeys)) + for i, key := range preFundedKeys { + keys[i] = key.ToECDSA() } registry := prometheus.NewRegistry() @@ -101,24 +96,6 @@ func execute(ctx context.Context, preFundedKeys []*secp256k1.PrivateKey, config } } -func fixKeysCount(preFundedKeys []*secp256k1.PrivateKey, target int) ([]*ecdsa.PrivateKey, error) { - keys := make([]*ecdsa.PrivateKey, 0, target) - for i := 0; i < min(target, len(preFundedKeys)); i++ { - keys = append(keys, preFundedKeys[i].ToECDSA()) - } - if len(keys) == target { - return keys, nil - } - for i := len(keys); i < target; i++ { - key, err := ethcrypto.GenerateKey() - if err != nil { - return nil, fmt.Errorf("generating key at index %d: %w", i, err) - } - keys = append(keys, key) - } - return keys, nil -} - // createAgents creates agents for the given configuration and keys. // It creates them in parallel because creating issuers can sometimes take a while, // and this adds up for many agents. For example, deploying the Opcoder contract From 33b81469e28fcbfc4610070eb2e97fabb3fd4a40 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Tue, 20 May 2025 17:15:12 +0200 Subject: [PATCH 122/197] Use secp256k1.PrivateKey to simplify code --- tests/load/c/execute.go | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/tests/load/c/execute.go b/tests/load/c/execute.go index 3c1b296d89e2..87bf5543277b 100644 --- a/tests/load/c/execute.go +++ b/tests/load/c/execute.go @@ -5,7 +5,6 @@ package c import ( "context" - "crypto/ecdsa" "fmt" "math/big" "os" @@ -19,8 +18,6 @@ import ( "github.com/ava-labs/avalanchego/tests/load/c/listener" "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" "github.com/ava-labs/avalanchego/utils/logging" - - ethcrypto "github.com/ava-labs/libevm/crypto" ) type loadConfig struct { @@ -40,14 +37,9 @@ const ( issuerOpcoder issuerType = "opcoder" ) -func execute(ctx context.Context, preFundedKeys []*secp256k1.PrivateKey, config loadConfig) error { +func execute(ctx context.Context, keys []*secp256k1.PrivateKey, config loadConfig) error { logger := logging.NewLogger("", logging.NewWrappedCore(logging.Info, os.Stdout, logging.Auto.ConsoleEncoder())) - keys := make([]*ecdsa.PrivateKey, len(preFundedKeys)) - for i, key := range preFundedKeys { - keys[i] = key.ToECDSA() - } - registry := prometheus.NewRegistry() metricsServer := load.NewPrometheusServer("127.0.0.1:8082", registry, logger) tracker, err := load.NewTracker[common.Hash](registry) @@ -100,7 +92,7 @@ func execute(ctx context.Context, preFundedKeys []*secp256k1.PrivateKey, config // It creates them in parallel because creating issuers can sometimes take a while, // and this adds up for many agents. For example, deploying the Opcoder contract // takes a few seconds. Running the creation in parallel can reduce the time significantly. -func createAgents(ctx context.Context, config loadConfig, keys []*ecdsa.PrivateKey, +func createAgents(ctx context.Context, config loadConfig, keys []*secp256k1.PrivateKey, tracker *load.Tracker[common.Hash], ) ([]load.Agent[common.Hash], error) { ctx, cancel := context.WithCancel(ctx) @@ -113,7 +105,7 @@ func createAgents(ctx context.Context, config loadConfig, keys []*ecdsa.PrivateK for i := range int(config.agents) { key := keys[i] endpoint := config.endpoints[i%len(config.endpoints)] - go func(key *ecdsa.PrivateKey, endpoint string) { + go func(key *secp256k1.PrivateKey, endpoint string) { agent, err := createAgent(ctx, endpoint, key, config.issuer, tracker, config.maxFeeCap) ch <- result{agent: agent, err: err} }(key, endpoint) @@ -142,7 +134,7 @@ func createAgents(ctx context.Context, config loadConfig, keys []*ecdsa.PrivateK return agents, nil } -func createAgent(ctx context.Context, endpoint string, key *ecdsa.PrivateKey, +func createAgent(ctx context.Context, endpoint string, key *secp256k1.PrivateKey, issuerType issuerType, tracker *load.Tracker[common.Hash], maxFeeCap int64, ) (load.Agent[common.Hash], error) { client, err := ethclient.DialContext(ctx, endpoint) @@ -150,7 +142,7 @@ func createAgent(ctx context.Context, endpoint string, key *ecdsa.PrivateKey, return load.Agent[common.Hash]{}, fmt.Errorf("dialing %s: %w", endpoint, err) } - address := ethcrypto.PubkeyToAddress(key.PublicKey) + address := key.EthAddress() blockNumber := (*big.Int)(nil) nonce, err := client.NonceAt(ctx, address, blockNumber) if err != nil { @@ -168,15 +160,15 @@ func createAgent(ctx context.Context, endpoint string, key *ecdsa.PrivateKey, func createIssuer(ctx context.Context, typ issuerType, client *ethclient.Client, tracker *load.Tracker[common.Hash], - maxFeeCap int64, nonce uint64, key *ecdsa.PrivateKey, + maxFeeCap int64, nonce uint64, key *secp256k1.PrivateKey, ) (load.Issuer[common.Hash], error) { switch typ { case issuerSimple: return issuers.NewSimple(ctx, client, tracker, - nonce, big.NewInt(maxFeeCap), key) + nonce, big.NewInt(maxFeeCap), key.ToECDSA()) case issuerOpcoder: return issuers.NewOpcoder(ctx, client, tracker, - nonce, big.NewInt(maxFeeCap), key) + nonce, big.NewInt(maxFeeCap), key.ToECDSA()) default: return nil, fmt.Errorf("unknown issuer type %s", typ) } From b778684e1ff3fec865a69028cbaecb6d0cfcbacf Mon Sep 17 00:00:00 2001 From: Elvis <43846394+Elvis339@users.noreply.github.com> Date: Tue, 20 May 2025 19:07:41 +0400 Subject: [PATCH 123/197] ci: load test monitoring (#3959) Signed-off-by: Elvis <43846394+Elvis339@users.noreply.github.com> Co-authored-by: Quentin McGaw --- .github/workflows/ci.yml | 4 +- flake.nix | 4 +- tests/fixture/tmpnet/network.go | 57 +++++++++++++++- tests/load/c/contracts/.solhint.json | 11 --- tests/load/c/contracts/Dummy.bindings.go | 2 +- tests/load/c/contracts/Dummy.sol | 2 +- .../c/contracts/EVMLoadSimulator.bindings.go | 2 +- tests/load/c/contracts/EVMLoadSimulator.sol | 2 +- .../load/c/contracts/generate_abi_bindings.sh | 14 +--- tests/load/c/contracts/generate_test.go | 2 +- tests/load/c/execute.go | 44 ++---------- tests/load/c/ginkgo_test.go | 47 +++++++++++-- tests/load/c/issuers/opcode.go | 2 +- tests/load/c/issuers/simple.go | 2 +- tests/load/c/listener/listener.go | 2 +- tests/load/c/listener/newhead.go | 2 +- tests/load/metrics.go | 67 +++++++++++++++++++ tests/load/metrics_link.go | 49 ++++++++++++++ tests/load/orchestrator_test.go | 8 ++- tests/load/prometheus.go | 34 +++++++++- tests/load/tracker.go | 52 ++------------ 21 files changed, 278 insertions(+), 131 deletions(-) delete mode 100644 tests/load/c/contracts/.solhint.json create mode 100644 tests/load/metrics.go create mode 100644 tests/load/metrics_link.go diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bb9020361ddb..c2c0e8240c73 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -223,10 +223,10 @@ jobs: - uses: ./.github/actions/install-nix - name: Contract bindings are up to date shell: bash - run: ./scripts/run_task.sh check-generate-load-contract-bindings + run: nix develop --command ./scripts/run_task.sh check-generate-load-contract-bindings - uses: ./.github/actions/run-monitored-tmpnet-cmd with: - run: ./scripts/run_task.sh test-e2e-ci + run: ./scripts/run_task.sh test-e2e-load artifact_prefix: e2e-load filter_by_owner: avalanchego-e2e prometheus_username: ${{ secrets.PROMETHEUS_ID || '' }} diff --git a/flake.nix b/flake.nix index 28172cddd55d..b95c5ebf0284 100644 --- a/flake.nix +++ b/flake.nix @@ -57,8 +57,8 @@ protoc-gen-go protoc-gen-go-grpc - # Solidity - solhint + # Solidity compiler from nixpkgs 24.11 + solc ] ++ lib.optionals stdenv.isDarwin [ # macOS-specific frameworks darwin.apple_sdk.frameworks.Security diff --git a/tests/fixture/tmpnet/network.go b/tests/fixture/tmpnet/network.go index 2cfaad9eafde..095fe8ea4341 100644 --- a/tests/fixture/tmpnet/network.go +++ b/tests/fixture/tmpnet/network.go @@ -11,7 +11,9 @@ import ( "encoding/json" "errors" "fmt" + "maps" "net/netip" + "net/url" "os" "os/exec" "path/filepath" @@ -57,6 +59,9 @@ const ( // eth address: 0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC HardHatKeyStr = "56289e99c94b6912bfc12adc093c9b51124f0dc54ac7a766b2bc5ccf558d8027" + + // grafanaURI is remote Grafana URI + grafanaURI = "grafana-poc.avax-dev.network" ) var ( @@ -1052,9 +1057,59 @@ func MetricsLinkForNetwork(networkUUID string, startTime string, endTime string) endTime = "now" } return fmt.Sprintf( - "https://grafana-poc.avax-dev.network/d/kBQpRdWnk/avalanche-main-dashboard?&var-filter=network_uuid%%7C%%3D%%7C%s&var-filter=is_ephemeral_node%%7C%%3D%%7Cfalse&from=%s&to=%s", + "https://%s/d/kBQpRdWnk/avalanche-main-dashboard?&var-filter=network_uuid%%7C%%3D%%7C%s&var-filter=is_ephemeral_node%%7C%%3D%%7Cfalse&from=%s&to=%s", + grafanaURI, networkUUID, startTime, endTime, ) } + +// GrafanaFilterOptions contains filters to apply to Grafana link in form of a query +// Example: https://grafana-poc.avax-dev.network/?start_time=now-1h&endTime=now +type GrafanaFilterOptions struct { + StartTime string + EndTime string + Filters map[string]string +} + +func BuildMetricsURLForNetwork(dashboardID, dashboardName, networkUUID string, options GrafanaFilterOptions) string { + // Set defaults for options if not provided + startTime := "now-1h" + if options.StartTime != "" { + startTime = options.StartTime + } + + endTime := "now" + if options.EndTime != "" { + endTime = options.EndTime + } + + baseURL := url.URL{ + Scheme: "https", + Host: grafanaURI, + Path: fmt.Sprintf("/d/%s/%s", dashboardID, dashboardName), + } + + query := baseURL.Query() + + query.Add("from", startTime) + query.Add("to", endTime) + + filters := make(map[string]string) + if options.Filters != nil { + filters = maps.Clone(options.Filters) + } + + // Ensure network_uuid is set + if _, exists := filters["network_uuid"]; !exists { + filters["network_uuid"] = networkUUID + } + + for key, value := range filters { + query.Add("var-filter", fmt.Sprintf("%s|=|%s", key, value)) + } + + baseURL.RawQuery = query.Encode() + return baseURL.String() +} diff --git a/tests/load/c/contracts/.solhint.json b/tests/load/c/contracts/.solhint.json deleted file mode 100644 index 5ee631cab545..000000000000 --- a/tests/load/c/contracts/.solhint.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "solhint:recommended", - "rules": { - "func-visibility": [ - "warn", - { - "ignoreConstructors": true - } - ] - } -} \ No newline at end of file diff --git a/tests/load/c/contracts/Dummy.bindings.go b/tests/load/c/contracts/Dummy.bindings.go index fcfda4e36b32..32c48cb9f6ba 100644 --- a/tests/load/c/contracts/Dummy.bindings.go +++ b/tests/load/c/contracts/Dummy.bindings.go @@ -32,7 +32,7 @@ var ( // DummyMetaData contains all meta data concerning the Dummy contract. var DummyMetaData = &bind.MetaData{ ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"key\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"DataWritten\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newValue\",\"type\":\"uint256\"}],\"name\":\"ValueUpdated\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"data\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"key\",\"type\":\"uint256\"}],\"name\":\"readData\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newValue\",\"type\":\"uint256\"}],\"name\":\"updateValue\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"value\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"key\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"val\",\"type\":\"uint256\"}],\"name\":\"writeData\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x6080604052348015600e575f5ffd5b50602a5f819055503360015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555061036a806100635f395ff3fe608060405234801561000f575f5ffd5b5060043610610060575f3560e01c806337ebbc03146100645780633fa4f24514610094578063573c0bd3146100b25780638da5cb5b146100ce578063c71ba63b146100ec578063f0ba844014610108575b5f5ffd5b61007e60048036038101906100799190610224565b610138565b60405161008b919061025e565b60405180910390f35b61009c610152565b6040516100a9919061025e565b60405180910390f35b6100cc60048036038101906100c79190610224565b610157565b005b6100d6610160565b6040516100e391906102b6565b60405180910390f35b610106600480360381019061010191906102cf565b610185565b005b610122600480360381019061011d9190610224565b6101d8565b60405161012f919061025e565b60405180910390f35b5f60025f8381526020019081526020015f20549050919050565b5f5481565b805f8190555050565b60015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b8060025f8481526020019081526020015f20819055507f36c0e38a11934bb6e80e00c4ae42212be021022fdb5aff12c53720f1d951c28182826040516101cc92919061030d565b60405180910390a15050565b6002602052805f5260405f205f915090505481565b5f5ffd5b5f819050919050565b610203816101f1565b811461020d575f5ffd5b50565b5f8135905061021e816101fa565b92915050565b5f60208284031215610239576102386101ed565b5b5f61024684828501610210565b91505092915050565b610258816101f1565b82525050565b5f6020820190506102715f83018461024f565b92915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6102a082610277565b9050919050565b6102b081610296565b82525050565b5f6020820190506102c95f8301846102a7565b92915050565b5f5f604083850312156102e5576102e46101ed565b5b5f6102f285828601610210565b925050602061030385828601610210565b9150509250929050565b5f6040820190506103205f83018561024f565b61032d602083018461024f565b939250505056fea26469706673582212201893623673c7d322a3501952be3510a3b2582a332e23a9ee7765420a7e2c8f7464736f6c634300081d0033", + Bin: "0x608060405234801561000f575f80fd5b50602a5f819055503360015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555061036a806100645f395ff3fe608060405234801561000f575f80fd5b5060043610610060575f3560e01c806337ebbc03146100645780633fa4f24514610094578063573c0bd3146100b25780638da5cb5b146100ce578063c71ba63b146100ec578063f0ba844014610108575b5f80fd5b61007e60048036038101906100799190610224565b610138565b60405161008b919061025e565b60405180910390f35b61009c610152565b6040516100a9919061025e565b60405180910390f35b6100cc60048036038101906100c79190610224565b610157565b005b6100d6610160565b6040516100e391906102b6565b60405180910390f35b610106600480360381019061010191906102cf565b610185565b005b610122600480360381019061011d9190610224565b6101d8565b60405161012f919061025e565b60405180910390f35b5f60025f8381526020019081526020015f20549050919050565b5f5481565b805f8190555050565b60015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b8060025f8481526020019081526020015f20819055507f36c0e38a11934bb6e80e00c4ae42212be021022fdb5aff12c53720f1d951c28182826040516101cc92919061030d565b60405180910390a15050565b6002602052805f5260405f205f915090505481565b5f80fd5b5f819050919050565b610203816101f1565b811461020d575f80fd5b50565b5f8135905061021e816101fa565b92915050565b5f60208284031215610239576102386101ed565b5b5f61024684828501610210565b91505092915050565b610258816101f1565b82525050565b5f6020820190506102715f83018461024f565b92915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6102a082610277565b9050919050565b6102b081610296565b82525050565b5f6020820190506102c95f8301846102a7565b92915050565b5f80604083850312156102e5576102e46101ed565b5b5f6102f285828601610210565b925050602061030385828601610210565b9150509250929050565b5f6040820190506103205f83018561024f565b61032d602083018461024f565b939250505056fea2646970667358221220a44af404da38b49bae0be72ddce95db226c9a908c51f433a00208892fba4801964736f6c63430008150033", } // DummyABI is the input ABI used to generate the binding from. diff --git a/tests/load/c/contracts/Dummy.sol b/tests/load/c/contracts/Dummy.sol index 90c621bf3b4a..bbd4d641cb83 100644 --- a/tests/load/c/contracts/Dummy.sol +++ b/tests/load/c/contracts/Dummy.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.24; +pragma solidity ^0.8.21; /// @notice Dummy is a contract used only for simulating the load /// of contract creation operations in the EVM. It has state variables, diff --git a/tests/load/c/contracts/EVMLoadSimulator.bindings.go b/tests/load/c/contracts/EVMLoadSimulator.bindings.go index dd792e6006e8..1b8023702820 100644 --- a/tests/load/c/contracts/EVMLoadSimulator.bindings.go +++ b/tests/load/c/contracts/EVMLoadSimulator.bindings.go @@ -32,7 +32,7 @@ var ( // EVMLoadSimulatorMetaData contains all meta data concerning the EVMLoadSimulator contract. var EVMLoadSimulatorMetaData = &bind.MetaData{ ABI: "[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"hash\",\"type\":\"bytes32\"}],\"name\":\"HashCalculates\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"largeData\",\"type\":\"bytes\"}],\"name\":\"LargeLog\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256[]\",\"name\":\"arr\",\"type\":\"uint256[]\"}],\"name\":\"MemoryWritten\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"accountId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"StorageUpdate\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"sum\",\"type\":\"uint256\"}],\"name\":\"SumCalculated\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"balancesCount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"dummy\",\"outputs\":[{\"internalType\":\"contractDummy\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"depth\",\"type\":\"uint256\"}],\"name\":\"simulateCallDepth\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"simulateContractCreation\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"simulateExternalCall\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"rounds\",\"type\":\"uint256\"}],\"name\":\"simulateHashing\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"hash\",\"type\":\"bytes32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"size\",\"type\":\"uint256\"}],\"name\":\"simulateLargeEvent\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"sizeInWords\",\"type\":\"uint256\"}],\"name\":\"simulateMemory\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"sum\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"count\",\"type\":\"uint256\"}],\"name\":\"simulateModification\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"iterations\",\"type\":\"uint256\"}],\"name\":\"simulatePureCompute\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"result\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"count\",\"type\":\"uint256\"}],\"name\":\"simulateRandomWrite\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"count\",\"type\":\"uint256\"}],\"name\":\"simulateReads\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"sum\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x6080604052348015600e575f5ffd5b506114368061001c5f395ff3fe608060405234801561000f575f5ffd5b50600436106100b2575f3560e01c8063aae05a651161006f578063aae05a6514610178578063ab7611d114610194578063b77513d1146101b0578063e730b4bd146101cc578063f05ed79e146101d6578063fb0c001214610206576100b2565b8063130fcab6146100b657806332e43a11146100e65780633851d6e714610104578063542eedd9146101225780635de583ef1461013e5780637db6ecb114610148575b5f5ffd5b6100d060048036038101906100cb9190610a3c565b610236565b6040516100dd9190610a76565b60405180910390f35b6100ee6102d6565b6040516100fb9190610b09565b60405180910390f35b61010c6102fb565b6040516101199190610a76565b60405180910390f35b61013c60048036038101906101379190610a3c565b610301565b005b6101466103bb565b005b610162600480360381019061015d9190610a3c565b610422565b60405161016f9190610b3a565b60405180910390f35b610192600480360381019061018d9190610a3c565b6104cc565b005b6101ae60048036038101906101a99190610a3c565b6105d5565b005b6101ca60048036038101906101c59190610a3c565b6106c0565b005b6101d461074b565b005b6101f060048036038101906101eb9190610a3c565b61088f565b6040516101fd9190610a76565b60405180910390f35b610220600480360381019061021b9190610a3c565b61097c565b60405161022d9190610a76565b60405180910390f35b5f5f5f90505b82811015610298576001816102519190610b80565b81600283846102609190610bb3565b61026a9190610c21565b6102749190610b80565b61027e9190610c51565b826102899190610b80565b9150808060010191505061023c565b505f7fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc85f6040516102c99190610cba565b60405180910390a2919050565b60025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60015481565b5f8103610345575f7fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc85f6040516103389190610cba565b60405180910390a26103b8565b3073ffffffffffffffffffffffffffffffffffffffff1663542eedd960018361036e9190610cd3565b6040518263ffffffff1660e01b815260040161038a9190610a76565b5f604051808303815f87803b1580156103a1575f5ffd5b505af11580156103b3573d5f5f3e3d5ffd5b505050505b50565b6040516103c7906109f8565b604051809103905ff0801580156103e0573d5f5f3e3d5ffd5b5060025f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b5f60405160200161043290610d5a565b6040516020818303038152906040528051906020012090505f5f90505b8281101561048f57818160405160200161046a929190610dae565b604051602081830303815290604052805190602001209150808060010191505061044f565b507f30ca2ef0880ae63712fdaf11aefb67752968cff6f845956fcbdfcf421f4647cb816040516104bf9190610b3a565b60405180910390a1919050565b5f600190505b8181116105d157600154811015610556575f60015f5f8481526020019081526020015f20546105019190610b80565b9050805f5f8481526020019081526020015f2081905550817fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc8826040516105489190610a76565b60405180910390a2506105be565b5f60015f81548092919061056990610dd9565b919050559050815f5f8381526020019081526020015f2081905550807fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc8836040516105b49190610a76565b60405180910390a2505b80806105c990610dd9565b9150506104d2565b5050565b5f8167ffffffffffffffff8111156105f0576105ef610e20565b5b6040519080825280601f01601f1916602001820160405280156106225781602001600182028036833780820191505090505b5090505f5f90505b82811015610684578060f81b82828151811061064957610648610e4d565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a905350808060010191505061062a565b507f5e53254f5b56e942cb89e1beff9257b039a5593ffe94274d0640a636b57fd0ac816040516106b49190610eea565b60405180910390a15050565b5f600190505b818111610747575f60015f8154809291906106e090610dd9565b919050559050815f5f8381526020019081526020015f2081905550807fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc88360405161072b9190610a76565b60405180910390a250808061073f90610dd9565b9150506106c6565b5050565b5f73ffffffffffffffffffffffffffffffffffffffff1660025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1603610805576040516107ab906109f8565b604051809103905ff0801580156107c4573d5f5f3e3d5ffd5b5060025f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b60025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663573c0bd3602a6040518263ffffffff1660e01b81526004016108609190610f43565b5f604051808303815f87803b158015610877575f5ffd5b505af1158015610889573d5f5f3e3d5ffd5b50505050565b5f5f8267ffffffffffffffff8111156108ab576108aa610e20565b5b6040519080825280602002602001820160405280156108d95781602001602082028036833780820191505090505b5090505f5f90505b8381101561093e57808282815181106108fd576108fc610e4d565b5b60200260200101818152505081818151811061091c5761091b610e4d565b5b60200260200101518361092f9190610b80565b925080806001019150506108e1565b507f542a9e74627abe4fb012aa9be028f3234ff2b2253530c6fa2220e29f03e4215d8160405161096e9190611013565b60405180910390a150919050565b5f5f600190505b8281116109bb575f5f8281526020019081526020015f2054826109a69190610b80565b915080806109b390610dd9565b915050610983565b507fe32d91cad5061d7491327c51e7b799c677b41d033204a5c5022b120f5da4becb816040516109eb9190610a76565b60405180910390a1919050565b6103cd8061103483390190565b5f5ffd5b5f819050919050565b610a1b81610a09565b8114610a25575f5ffd5b50565b5f81359050610a3681610a12565b92915050565b5f60208284031215610a5157610a50610a05565b5b5f610a5e84828501610a28565b91505092915050565b610a7081610a09565b82525050565b5f602082019050610a895f830184610a67565b92915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f819050919050565b5f610ad1610acc610ac784610a8f565b610aae565b610a8f565b9050919050565b5f610ae282610ab7565b9050919050565b5f610af382610ad8565b9050919050565b610b0381610ae9565b82525050565b5f602082019050610b1c5f830184610afa565b92915050565b5f819050919050565b610b3481610b22565b82525050565b5f602082019050610b4d5f830184610b2b565b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f610b8a82610a09565b9150610b9583610a09565b9250828201905080821115610bad57610bac610b53565b5b92915050565b5f610bbd82610a09565b9150610bc883610a09565b9250828202610bd681610a09565b91508282048414831517610bed57610bec610b53565b5b5092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f610c2b82610a09565b9150610c3683610a09565b925082610c4657610c45610bf4565b5b828204905092915050565b5f610c5b82610a09565b9150610c6683610a09565b925082610c7657610c75610bf4565b5b828206905092915050565b5f819050919050565b5f610ca4610c9f610c9a84610c81565b610aae565b610a09565b9050919050565b610cb481610c8a565b82525050565b5f602082019050610ccd5f830184610cab565b92915050565b5f610cdd82610a09565b9150610ce883610a09565b9250828203905081811115610d0057610cff610b53565b5b92915050565b5f81905092915050565b7f696e697469616c000000000000000000000000000000000000000000000000005f82015250565b5f610d44600783610d06565b9150610d4f82610d10565b600782019050919050565b5f610d6482610d38565b9150819050919050565b5f819050919050565b610d88610d8382610b22565b610d6e565b82525050565b5f819050919050565b610da8610da382610a09565b610d8e565b82525050565b5f610db98285610d77565b602082019150610dc98284610d97565b6020820191508190509392505050565b5f610de382610a09565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203610e1557610e14610b53565b5b600182019050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f81519050919050565b5f82825260208201905092915050565b8281835e5f83830152505050565b5f601f19601f8301169050919050565b5f610ebc82610e7a565b610ec68185610e84565b9350610ed6818560208601610e94565b610edf81610ea2565b840191505092915050565b5f6020820190508181035f830152610f028184610eb2565b905092915050565b5f819050919050565b5f610f2d610f28610f2384610f0a565b610aae565b610a09565b9050919050565b610f3d81610f13565b82525050565b5f602082019050610f565f830184610f34565b92915050565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b610f8e81610a09565b82525050565b5f610f9f8383610f85565b60208301905092915050565b5f602082019050919050565b5f610fc182610f5c565b610fcb8185610f66565b9350610fd683610f76565b805f5b83811015611006578151610fed8882610f94565b9750610ff883610fab565b925050600181019050610fd9565b5085935050505092915050565b5f6020820190508181035f83015261102b8184610fb7565b90509291505056fe6080604052348015600e575f5ffd5b50602a5f819055503360015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555061036a806100635f395ff3fe608060405234801561000f575f5ffd5b5060043610610060575f3560e01c806337ebbc03146100645780633fa4f24514610094578063573c0bd3146100b25780638da5cb5b146100ce578063c71ba63b146100ec578063f0ba844014610108575b5f5ffd5b61007e60048036038101906100799190610224565b610138565b60405161008b919061025e565b60405180910390f35b61009c610152565b6040516100a9919061025e565b60405180910390f35b6100cc60048036038101906100c79190610224565b610157565b005b6100d6610160565b6040516100e391906102b6565b60405180910390f35b610106600480360381019061010191906102cf565b610185565b005b610122600480360381019061011d9190610224565b6101d8565b60405161012f919061025e565b60405180910390f35b5f60025f8381526020019081526020015f20549050919050565b5f5481565b805f8190555050565b60015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b8060025f8481526020019081526020015f20819055507f36c0e38a11934bb6e80e00c4ae42212be021022fdb5aff12c53720f1d951c28182826040516101cc92919061030d565b60405180910390a15050565b6002602052805f5260405f205f915090505481565b5f5ffd5b5f819050919050565b610203816101f1565b811461020d575f5ffd5b50565b5f8135905061021e816101fa565b92915050565b5f60208284031215610239576102386101ed565b5b5f61024684828501610210565b91505092915050565b610258816101f1565b82525050565b5f6020820190506102715f83018461024f565b92915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6102a082610277565b9050919050565b6102b081610296565b82525050565b5f6020820190506102c95f8301846102a7565b92915050565b5f5f604083850312156102e5576102e46101ed565b5b5f6102f285828601610210565b925050602061030385828601610210565b9150509250929050565b5f6040820190506103205f83018561024f565b61032d602083018461024f565b939250505056fea26469706673582212201893623673c7d322a3501952be3510a3b2582a332e23a9ee7765420a7e2c8f7464736f6c634300081d0033a264697066735822122090228c8b6f95fa9d531e807cb5b42e7e95e0169608120a681aef450c806270de64736f6c634300081d0033", + Bin: "0x608060405234801561000f575f80fd5b506114608061001d5f395ff3fe608060405234801561000f575f80fd5b50600436106100b2575f3560e01c8063aae05a651161006f578063aae05a6514610178578063ab7611d114610194578063b77513d1146101b0578063e730b4bd146101cc578063f05ed79e146101d6578063fb0c001214610206576100b2565b8063130fcab6146100b657806332e43a11146100e65780633851d6e714610104578063542eedd9146101225780635de583ef1461013e5780637db6ecb114610148575b5f80fd5b6100d060048036038101906100cb9190610a4b565b610236565b6040516100dd9190610a85565b60405180910390f35b6100ee6102dc565b6040516100fb9190610b18565b60405180910390f35b61010c610301565b6040516101199190610a85565b60405180910390f35b61013c60048036038101906101379190610a4b565b610307565b005b6101466103c1565b005b610162600480360381019061015d9190610a4b565b610428565b60405161016f9190610b49565b60405180910390f35b610192600480360381019061018d9190610a4b565b6104d5565b005b6101ae60048036038101906101a99190610a4b565b6105de565b005b6101ca60048036038101906101c59190610a4b565b6106cc565b005b6101d4610757565b005b6101f060048036038101906101eb9190610a4b565b61089b565b6040516101fd9190610a85565b60405180910390f35b610220600480360381019061021b9190610a4b565b61098b565b60405161022d9190610a85565b60405180910390f35b5f805f90505b8281101561029e576001816102519190610b8f565b81600283846102609190610bc2565b61026a9190610c30565b6102749190610b8f565b61027e9190610c60565b826102899190610b8f565b9150808061029690610c90565b91505061023c565b505f7fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc85f6040516102cf9190610d10565b60405180910390a2919050565b60025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60015481565b5f810361034b575f7fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc85f60405161033e9190610d10565b60405180910390a26103be565b3073ffffffffffffffffffffffffffffffffffffffff1663542eedd96001836103749190610d29565b6040518263ffffffff1660e01b81526004016103909190610a85565b5f604051808303815f87803b1580156103a7575f80fd5b505af11580156103b9573d5f803e3d5ffd5b505050505b50565b6040516103cd90610a07565b604051809103905ff0801580156103e6573d5f803e3d5ffd5b5060025f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b5f60405160200161043890610db0565b6040516020818303038152906040528051906020012090505f5b8281101561049857818160405160200161046d929190610e04565b604051602081830303815290604052805190602001209150808061049090610c90565b915050610452565b507f30ca2ef0880ae63712fdaf11aefb67752968cff6f845956fcbdfcf421f4647cb816040516104c89190610b49565b60405180910390a1919050565b5f600190505b8181116105da5760015481101561055f575f60015f808481526020019081526020015f205461050a9190610b8f565b9050805f808481526020019081526020015f2081905550817fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc8826040516105519190610a85565b60405180910390a2506105c7565b5f60015f81548092919061057290610c90565b919050559050815f808381526020019081526020015f2081905550807fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc8836040516105bd9190610a85565b60405180910390a2505b80806105d290610c90565b9150506104db565b5050565b5f8167ffffffffffffffff8111156105f9576105f8610e2f565b5b6040519080825280601f01601f19166020018201604052801561062b5781602001600182028036833780820191505090505b5090505f5b82811015610690578060f81b82828151811061064f5761064e610e5c565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a905350808061068890610c90565b915050610630565b507f5e53254f5b56e942cb89e1beff9257b039a5593ffe94274d0640a636b57fd0ac816040516106c09190610f13565b60405180910390a15050565b5f600190505b818111610753575f60015f8154809291906106ec90610c90565b919050559050815f808381526020019081526020015f2081905550807fbed7bf46680bfe44399acf02887c2443b1894b86596db85714936273e7db7cc8836040516107379190610a85565b60405180910390a250808061074b90610c90565b9150506106d2565b5050565b5f73ffffffffffffffffffffffffffffffffffffffff1660025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1603610811576040516107b790610a07565b604051809103905ff0801580156107d0573d5f803e3d5ffd5b5060025f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b60025f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663573c0bd3602a6040518263ffffffff1660e01b815260040161086c9190610f6c565b5f604051808303815f87803b158015610883575f80fd5b505af1158015610895573d5f803e3d5ffd5b50505050565b5f808267ffffffffffffffff8111156108b7576108b6610e2f565b5b6040519080825280602002602001820160405280156108e55781602001602082028036833780820191505090505b5090505f5b8381101561094d578082828151811061090657610905610e5c565b5b60200260200101818152505081818151811061092557610924610e5c565b5b6020026020010151836109389190610b8f565b9250808061094590610c90565b9150506108ea565b507f542a9e74627abe4fb012aa9be028f3234ff2b2253530c6fa2220e29f03e4215d8160405161097d919061103c565b60405180910390a150919050565b5f80600190505b8281116109ca575f808281526020019081526020015f2054826109b59190610b8f565b915080806109c290610c90565b915050610992565b507fe32d91cad5061d7491327c51e7b799c677b41d033204a5c5022b120f5da4becb816040516109fa9190610a85565b60405180910390a1919050565b6103ce8061105d83390190565b5f80fd5b5f819050919050565b610a2a81610a18565b8114610a34575f80fd5b50565b5f81359050610a4581610a21565b92915050565b5f60208284031215610a6057610a5f610a14565b5b5f610a6d84828501610a37565b91505092915050565b610a7f81610a18565b82525050565b5f602082019050610a985f830184610a76565b92915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f819050919050565b5f610ae0610adb610ad684610a9e565b610abd565b610a9e565b9050919050565b5f610af182610ac6565b9050919050565b5f610b0282610ae7565b9050919050565b610b1281610af8565b82525050565b5f602082019050610b2b5f830184610b09565b92915050565b5f819050919050565b610b4381610b31565b82525050565b5f602082019050610b5c5f830184610b3a565b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f610b9982610a18565b9150610ba483610a18565b9250828201905080821115610bbc57610bbb610b62565b5b92915050565b5f610bcc82610a18565b9150610bd783610a18565b9250828202610be581610a18565b91508282048414831517610bfc57610bfb610b62565b5b5092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f610c3a82610a18565b9150610c4583610a18565b925082610c5557610c54610c03565b5b828204905092915050565b5f610c6a82610a18565b9150610c7583610a18565b925082610c8557610c84610c03565b5b828206905092915050565b5f610c9a82610a18565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203610ccc57610ccb610b62565b5b600182019050919050565b5f819050919050565b5f610cfa610cf5610cf084610cd7565b610abd565b610a18565b9050919050565b610d0a81610ce0565b82525050565b5f602082019050610d235f830184610d01565b92915050565b5f610d3382610a18565b9150610d3e83610a18565b9250828203905081811115610d5657610d55610b62565b5b92915050565b5f81905092915050565b7f696e697469616c000000000000000000000000000000000000000000000000005f82015250565b5f610d9a600783610d5c565b9150610da582610d66565b600782019050919050565b5f610dba82610d8e565b9150819050919050565b5f819050919050565b610dde610dd982610b31565b610dc4565b82525050565b5f819050919050565b610dfe610df982610a18565b610de4565b82525050565b5f610e0f8285610dcd565b602082019150610e1f8284610ded565b6020820191508190509392505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f81519050919050565b5f82825260208201905092915050565b5f5b83811015610ec0578082015181840152602081019050610ea5565b5f8484015250505050565b5f601f19601f8301169050919050565b5f610ee582610e89565b610eef8185610e93565b9350610eff818560208601610ea3565b610f0881610ecb565b840191505092915050565b5f6020820190508181035f830152610f2b8184610edb565b905092915050565b5f819050919050565b5f610f56610f51610f4c84610f33565b610abd565b610a18565b9050919050565b610f6681610f3c565b82525050565b5f602082019050610f7f5f830184610f5d565b92915050565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b610fb781610a18565b82525050565b5f610fc88383610fae565b60208301905092915050565b5f602082019050919050565b5f610fea82610f85565b610ff48185610f8f565b9350610fff83610f9f565b805f5b8381101561102f5781516110168882610fbd565b975061102183610fd4565b925050600181019050611002565b5085935050505092915050565b5f6020820190508181035f8301526110548184610fe0565b90509291505056fe608060405234801561000f575f80fd5b50602a5f819055503360015f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555061036a806100645f395ff3fe608060405234801561000f575f80fd5b5060043610610060575f3560e01c806337ebbc03146100645780633fa4f24514610094578063573c0bd3146100b25780638da5cb5b146100ce578063c71ba63b146100ec578063f0ba844014610108575b5f80fd5b61007e60048036038101906100799190610224565b610138565b60405161008b919061025e565b60405180910390f35b61009c610152565b6040516100a9919061025e565b60405180910390f35b6100cc60048036038101906100c79190610224565b610157565b005b6100d6610160565b6040516100e391906102b6565b60405180910390f35b610106600480360381019061010191906102cf565b610185565b005b610122600480360381019061011d9190610224565b6101d8565b60405161012f919061025e565b60405180910390f35b5f60025f8381526020019081526020015f20549050919050565b5f5481565b805f8190555050565b60015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b8060025f8481526020019081526020015f20819055507f36c0e38a11934bb6e80e00c4ae42212be021022fdb5aff12c53720f1d951c28182826040516101cc92919061030d565b60405180910390a15050565b6002602052805f5260405f205f915090505481565b5f80fd5b5f819050919050565b610203816101f1565b811461020d575f80fd5b50565b5f8135905061021e816101fa565b92915050565b5f60208284031215610239576102386101ed565b5b5f61024684828501610210565b91505092915050565b610258816101f1565b82525050565b5f6020820190506102715f83018461024f565b92915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6102a082610277565b9050919050565b6102b081610296565b82525050565b5f6020820190506102c95f8301846102a7565b92915050565b5f80604083850312156102e5576102e46101ed565b5b5f6102f285828601610210565b925050602061030385828601610210565b9150509250929050565b5f6040820190506103205f83018561024f565b61032d602083018461024f565b939250505056fea2646970667358221220a44af404da38b49bae0be72ddce95db226c9a908c51f433a00208892fba4801964736f6c63430008150033a2646970667358221220dd764531ab8daa512ea36f609f93d58d0b9ba8711ea011fadd4aa298e0f9bcec64736f6c63430008150033", } // EVMLoadSimulatorABI is the input ABI used to generate the binding from. diff --git a/tests/load/c/contracts/EVMLoadSimulator.sol b/tests/load/c/contracts/EVMLoadSimulator.sol index 0768e634f81f..cb0eeda8b5c3 100644 --- a/tests/load/c/contracts/EVMLoadSimulator.sol +++ b/tests/load/c/contracts/EVMLoadSimulator.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.24; +pragma solidity ^0.8.21; import {Dummy} from "./Dummy.sol"; diff --git a/tests/load/c/contracts/generate_abi_bindings.sh b/tests/load/c/contracts/generate_abi_bindings.sh index d6d4b386058b..1c219fbd9980 100755 --- a/tests/load/c/contracts/generate_abi_bindings.sh +++ b/tests/load/c/contracts/generate_abi_bindings.sh @@ -5,30 +5,20 @@ set -euo pipefail # Ensure required tools are installed if ! command -v solc &> /dev/null; then - echo "Error: solc (Solidity compiler) is not installed, trying to install with brew." - brew install solidity - if ! command -v solc &> /dev/null; then - echo "Error: solc installation failed. Please install it manually." - exit 1 - fi -fi - -if ! command -v solhint &> /dev/null; then - echo "Error: solhint not found. Run this command within Nix shell." + echo "Error: solc not found. Run this command from Nix shell." exit 1 fi CONTRACTS_DIR="$(dirname "$0")" TEMPDIR=$(mktemp -d) for FILE in "${CONTRACTS_DIR}"/*.sol; do - solhint --config ${CONTRACTS_DIR}/.solhint.json "${FILE}" echo "Generating Go bindings from Solidity contract $FILE..." CONTRACT_NAME=$(basename "$FILE" .sol) solc --abi --bin --overwrite -o "$TEMPDIR" "${CONTRACTS_DIR}/${CONTRACT_NAME}.sol" go run github.com/ava-labs/libevm/cmd/abigen@latest \ --bin="${TEMPDIR}/${CONTRACT_NAME}.bin" \ --abi="${TEMPDIR}/${CONTRACT_NAME}.abi" \ - --type $CONTRACT_NAME \ + --type "$CONTRACT_NAME" \ --pkg=contracts \ --out="${CONTRACTS_DIR}/${CONTRACT_NAME}.bindings.go" echo "Generated ${CONTRACT_NAME}.bindings.go" diff --git a/tests/load/c/contracts/generate_test.go b/tests/load/c/contracts/generate_test.go index a58dd42aeec2..70e44ab96d71 100644 --- a/tests/load/c/contracts/generate_test.go +++ b/tests/load/c/contracts/generate_test.go @@ -1,4 +1,4 @@ -// Co// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package contracts diff --git a/tests/load/c/execute.go b/tests/load/c/execute.go index 87bf5543277b..384fa0d3fb3f 100644 --- a/tests/load/c/execute.go +++ b/tests/load/c/execute.go @@ -1,4 +1,4 @@ -// Co// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package c @@ -7,11 +7,9 @@ import ( "context" "fmt" "math/big" - "os" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/ethclient" - "github.com/prometheus/client_golang/prometheus" "github.com/ava-labs/avalanchego/tests/load" "github.com/ava-labs/avalanchego/tests/load/c/issuers" @@ -37,26 +35,13 @@ const ( issuerOpcoder issuerType = "opcoder" ) -func execute(ctx context.Context, keys []*secp256k1.PrivateKey, config loadConfig) error { - logger := logging.NewLogger("", logging.NewWrappedCore(logging.Info, os.Stdout, logging.Auto.ConsoleEncoder())) - - registry := prometheus.NewRegistry() - metricsServer := load.NewPrometheusServer("127.0.0.1:8082", registry, logger) - tracker, err := load.NewTracker[common.Hash](registry) - if err != nil { - return fmt.Errorf("creating tracker: %w", err) - } - +func execute(ctx context.Context, keys []*secp256k1.PrivateKey, config loadConfig, metrics *load.Metrics, logger logging.Logger) error { + tracker := load.NewTracker[common.Hash](metrics) agents, err := createAgents(ctx, config, keys, tracker) if err != nil { return fmt.Errorf("creating agents: %w", err) } - metricsErrCh, err := metricsServer.Start() - if err != nil { - return fmt.Errorf("starting metrics server: %w", err) - } - orchestratorCtx, orchestratorCancel := context.WithCancel(ctx) defer orchestratorCancel() orchestratorConfig := load.NewOrchestratorConfig() @@ -65,27 +50,8 @@ func execute(ctx context.Context, keys []*secp256k1.PrivateKey, config loadConfi orchestratorConfig.Step = config.step orchestratorConfig.TxRateMultiplier = 1.05 orchestrator := load.NewOrchestrator(agents, tracker, logger, orchestratorConfig) - orchestratorErrCh := make(chan error) - go func() { - orchestratorErrCh <- orchestrator.Execute(orchestratorCtx) - }() - - select { - case err := <-orchestratorErrCh: - if err != nil { - _ = metricsServer.Stop() - return fmt.Errorf("orchestrator error: %w", err) - } - err = metricsServer.Stop() - if err != nil { - return fmt.Errorf("stopping metrics server: %w", err) - } - return nil - case err := <-metricsErrCh: - orchestratorCancel() - <-orchestratorErrCh - return fmt.Errorf("metrics server error: %w", err) - } + + return orchestrator.Execute(orchestratorCtx) } // createAgents creates agents for the given configuration and keys. diff --git a/tests/load/c/ginkgo_test.go b/tests/load/c/ginkgo_test.go index 39682eab1fb3..42026f0f92d2 100644 --- a/tests/load/c/ginkgo_test.go +++ b/tests/load/c/ginkgo_test.go @@ -1,17 +1,21 @@ -// Co// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package c import ( "context" + "os" "testing" "github.com/onsi/ginkgo/v2" + "github.com/prometheus/client_golang/prometheus" "github.com/stretchr/testify/require" "github.com/ava-labs/avalanchego/tests/fixture/e2e" "github.com/ava-labs/avalanchego/tests/fixture/tmpnet" + "github.com/ava-labs/avalanchego/tests/load" + "github.com/ava-labs/avalanchego/utils/logging" ) // Run this using the command: @@ -27,6 +31,7 @@ func init() { } const ( + blockchainID = "C" nodesCount = 5 agentsPerNode = 50 agentsCount = nodesCount * agentsPerNode @@ -59,17 +64,48 @@ var _ = ginkgo.SynchronizedBeforeSuite(func() []byte { }) var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { - var network *tmpnet.Network + var ( + network *tmpnet.Network + metrics *load.Metrics + + logger = logging.NewLogger("c-chain-load-testing", logging.NewWrappedCore(logging.Info, os.Stdout, logging.Auto.ConsoleEncoder())) + ) ginkgo.BeforeAll(func() { tc := e2e.NewTestContext() env := e2e.GetEnv(tc) + registry := prometheus.NewRegistry() + network = env.GetNetwork() network.Nodes = network.Nodes[:nodesCount] + for _, node := range network.Nodes { + err := node.EnsureKeys() + require.NoError(tc, err, "ensuring keys for node %s", node.NodeID) + } + + loadMetrics, err := load.NewMetrics(registry) + require.NoError(tc, err, "failed to register load metrics") + metrics = loadMetrics + + metricsServer := load.NewPrometheusServer("127.0.0.1:0", registry, logger) + metricsErrCh, err := metricsServer.Start() + require.NoError(tc, err, "failed to start load metrics server") + + monitoringConfigFilePath, err := metricsServer.GenerateMonitoringConfig(network.UUID, network.Owner) + require.NoError(tc, err, "failed to generate monitoring config file") + + ginkgo.DeferCleanup(func() { + select { + case err := <-metricsErrCh: + require.NoError(tc, err, "metrics server exited with error") + default: + require.NoError(tc, metricsServer.Stop(), "failed to stop metrics server") + } + require.NoError(tc, os.Remove(monitoringConfigFilePath), "failed to remove monitoring config file") + }) }) ginkgo.It("C-Chain simple", func(ctx context.Context) { - const blockchainID = "C" endpoints, err := tmpnet.GetNodeWebsocketURIs(network.Nodes, blockchainID) require.NoError(ginkgo.GinkgoT(), err, "getting node websocket URIs") config := loadConfig{ @@ -81,14 +117,13 @@ var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { maxTPS: 90, step: 10, } - err = execute(ctx, network.PreFundedKeys, config) + err = execute(ctx, network.PreFundedKeys, config, metrics, logger) if err != nil { ginkgo.GinkgoT().Error(err) } }) ginkgo.It("C-Chain opcoder", func(ctx context.Context) { - const blockchainID = "C" endpoints, err := tmpnet.GetNodeWebsocketURIs(network.Nodes, blockchainID) require.NoError(ginkgo.GinkgoT(), err, "getting node websocket URIs") config := loadConfig{ @@ -100,7 +135,7 @@ var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { maxTPS: 60, step: 5, } - err = execute(ctx, network.PreFundedKeys, config) + err = execute(ctx, network.PreFundedKeys, config, metrics, logger) if err != nil { ginkgo.GinkgoT().Error(err) } diff --git a/tests/load/c/issuers/opcode.go b/tests/load/c/issuers/opcode.go index 604e0f044eb1..a3b5c71d8183 100644 --- a/tests/load/c/issuers/opcode.go +++ b/tests/load/c/issuers/opcode.go @@ -1,4 +1,4 @@ -// Co// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package issuers diff --git a/tests/load/c/issuers/simple.go b/tests/load/c/issuers/simple.go index c7a77cd74149..95a5832ca4aa 100644 --- a/tests/load/c/issuers/simple.go +++ b/tests/load/c/issuers/simple.go @@ -1,4 +1,4 @@ -// Co// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package issuers diff --git a/tests/load/c/listener/listener.go b/tests/load/c/listener/listener.go index 0770e7653e40..0478b2e29bbb 100644 --- a/tests/load/c/listener/listener.go +++ b/tests/load/c/listener/listener.go @@ -1,4 +1,4 @@ -// Co// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package listener diff --git a/tests/load/c/listener/newhead.go b/tests/load/c/listener/newhead.go index 1562618ce658..17b21011bc77 100644 --- a/tests/load/c/listener/newhead.go +++ b/tests/load/c/listener/newhead.go @@ -1,4 +1,4 @@ -// Co// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package listener diff --git a/tests/load/metrics.go b/tests/load/metrics.go new file mode 100644 index 000000000000..4111faa0f618 --- /dev/null +++ b/tests/load/metrics.go @@ -0,0 +1,67 @@ +// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package load + +import ( + "errors" + + "github.com/prometheus/client_golang/prometheus" +) + +type Metrics struct { + txsIssuedCounter prometheus.Counter + txsConfirmedCounter prometheus.Counter + txsFailedCounter prometheus.Counter + txLatency prometheus.Histogram +} + +func NewMetrics(registry *prometheus.Registry) (*Metrics, error) { + m := &Metrics{ + txsIssuedCounter: prometheus.NewCounter(prometheus.CounterOpts{ + Namespace: namespace, + Name: "txs_issued", + Help: "Number of transactions issued", + }), + txsConfirmedCounter: prometheus.NewCounter(prometheus.CounterOpts{ + Namespace: namespace, + Name: "txs_confirmed", + Help: "Number of transactions confirmed", + }), + txsFailedCounter: prometheus.NewCounter(prometheus.CounterOpts{ + Namespace: namespace, + Name: "txs_failed", + Help: "Number of transactions failed", + }), + txLatency: prometheus.NewHistogram(prometheus.HistogramOpts{ + Namespace: namespace, + Name: "tx_latency", + Help: "Latency of transactions", + }), + } + + if err := errors.Join( + registry.Register(m.txsIssuedCounter), + registry.Register(m.txsConfirmedCounter), + registry.Register(m.txsFailedCounter), + registry.Register(m.txLatency), + ); err != nil { + return nil, err + } + + return m, nil +} + +func (m *Metrics) IncIssuedTx() { + m.txsIssuedCounter.Inc() +} + +func (m *Metrics) RecordConfirmedTx(latencyMS float64) { + m.txsConfirmedCounter.Inc() + m.txLatency.Observe(latencyMS) +} + +func (m *Metrics) RecordFailedTx(latencyMS float64) { + m.txsFailedCounter.Inc() + m.txLatency.Observe(latencyMS) +} diff --git a/tests/load/metrics_link.go b/tests/load/metrics_link.go new file mode 100644 index 000000000000..ff2b54f737c0 --- /dev/null +++ b/tests/load/metrics_link.go @@ -0,0 +1,49 @@ +// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package load + +import ( + "strconv" + "time" + + "github.com/onsi/ginkgo/v2" + "go.uber.org/zap" + + "github.com/ava-labs/avalanchego/tests/fixture/e2e" + "github.com/ava-labs/avalanchego/tests/fixture/tmpnet" +) + +const ( + dashboardID = "eabddd1d-0a06-4ba1-8e68-a44504e37535" + dashboardName = "C-Chain Load" +) + +var ( + // Disable default metrics link generation to prevent duplicate links. + // We generate load specific links. + _ = ginkgo.JustBeforeEach(func() { + e2e.EmitMetricsLink = false + }) + + _ = ginkgo.AfterEach(func() { + tc := e2e.NewTestContext() + env := e2e.GetEnv(tc) + + if env == nil { + return + } + + specReport := ginkgo.CurrentSpecReport() + startTimeMs := specReport.StartTime.UnixMilli() + + grafanaLink := tmpnet.BuildMetricsURLForNetwork(dashboardID, dashboardName, env.GetNetwork().UUID, tmpnet.GrafanaFilterOptions{ + StartTime: strconv.FormatInt(startTimeMs, 10), + EndTime: strconv.FormatInt(time.Now().Add(tmpnet.NetworkShutdownDelay).UnixMilli(), 10), + }) + + tc.Log().Info(tmpnet.MetricsAvailableMessage, + zap.String("uri", grafanaLink), + ) + }) +) diff --git a/tests/load/orchestrator_test.go b/tests/load/orchestrator_test.go index 1db7374a9983..aa551d1a1ee7 100644 --- a/tests/load/orchestrator_test.go +++ b/tests/load/orchestrator_test.go @@ -61,9 +61,11 @@ func TestOrchestratorTPS(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - tracker, err := NewTracker[ids.ID](prometheus.NewRegistry()) + m, err := NewMetrics(prometheus.NewRegistry()) r.NoError(err) + tracker := NewTracker[ids.ID](m) + agents := []Agent[ids.ID]{ NewAgent( &mockIssuer{ @@ -102,8 +104,8 @@ func TestOrchestratorExecution(t *testing.T) { errMockIssuer = errors.New("mock issuer error") ) - tracker, err := NewTracker[ids.ID](prometheus.NewRegistry()) - require.NoError(t, err, "creating tracker") + m, err := NewMetrics(prometheus.NewRegistry()) + tracker := NewTracker[ids.ID](m) tests := []struct { name string diff --git a/tests/load/prometheus.go b/tests/load/prometheus.go index bdf0dcb42eb8..ddabeaf593c2 100644 --- a/tests/load/prometheus.go +++ b/tests/load/prometheus.go @@ -1,19 +1,23 @@ -// Co// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. +// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package load import ( "context" + "encoding/json" "errors" "fmt" "net" "net/http" + "os" + "path/filepath" "time" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" + "github.com/ava-labs/avalanchego/tests/fixture/tmpnet" "github.com/ava-labs/avalanchego/utils/logging" ) @@ -79,3 +83,31 @@ func (s *MetricsServer) Stop() (err error) { defer cancel() return s.server.Shutdown(shutdownCtx) } + +// GenerateMonitoringConfig generates and writes the Prometheus collector configuration +// so tmpnet can dynamically discover new scrape target via file-based service discovery +// It returns the collector file path and an eventual error +func (s *MetricsServer) GenerateMonitoringConfig(networkUUID, networkOwner string) (string, error) { + const metricsFilePath = ".tmpnet/prometheus/file_sd_configs/load-test.json" + + homedir, err := os.UserHomeDir() + if err != nil { + return "", err + } + + collectorFilePath := filepath.Join(homedir, metricsFilePath) + config, err := json.MarshalIndent([]tmpnet.ConfigMap{ + { + "targets": []string{s.addr}, + "labels": map[string]string{ + "network_uuid": networkUUID, + "network_owner": networkOwner, + }, + }, + }, "", " ") + if err != nil { + return "", err + } + + return collectorFilePath, os.WriteFile(collectorFilePath, config, 0o600) +} diff --git a/tests/load/tracker.go b/tests/load/tracker.go index 2ca07a6444c4..358674181e12 100644 --- a/tests/load/tracker.go +++ b/tests/load/tracker.go @@ -6,10 +6,6 @@ package load import ( "sync" "time" - - "github.com/prometheus/client_golang/prometheus" - - "github.com/ava-labs/avalanchego/utils/wrappers" ) const namespace = "load" @@ -25,49 +21,17 @@ type Tracker[T TxID] struct { txsConfirmed uint64 txsFailed uint64 - // metrics - txsIssuedCounter prometheus.Counter - txsConfirmedCounter prometheus.Counter - txsFailedCounter prometheus.Counter - txLatency prometheus.Histogram + metrics *Metrics } // NewTracker returns a new Tracker instance which records metrics for the number // of transactions issued, confirmed, and failed. It also tracks the latency of // transactions. -func NewTracker[T TxID](reg prometheus.Registerer) (*Tracker[T], error) { - tracker := &Tracker[T]{ +func NewTracker[T TxID](metrics *Metrics) *Tracker[T] { + return &Tracker[T]{ outstandingTxs: make(map[T]time.Time), - txsIssuedCounter: prometheus.NewCounter(prometheus.CounterOpts{ - Namespace: namespace, - Name: "txs_issued", - Help: "Number of transactions issued", - }), - txsConfirmedCounter: prometheus.NewCounter(prometheus.CounterOpts{ - Namespace: namespace, - Name: "txs_confirmed", - Help: "Number of transactions confirmed", - }), - txsFailedCounter: prometheus.NewCounter(prometheus.CounterOpts{ - Namespace: namespace, - Name: "txs_failed", - Help: "Number of transactions failed", - }), - txLatency: prometheus.NewHistogram(prometheus.HistogramOpts{ - Namespace: namespace, - Name: "tx_latency", - Help: "Latency of transactions", - }), + metrics: metrics, } - - errs := wrappers.Errs{} - errs.Add( - reg.Register(tracker.txsIssuedCounter), - reg.Register(tracker.txsConfirmedCounter), - reg.Register(tracker.txsFailedCounter), - reg.Register(tracker.txLatency), - ) - return tracker, errs.Err } // GetObservedConfirmed returns the number of transactions that the tracker has @@ -105,7 +69,7 @@ func (p *Tracker[T]) Issue(txID T) { p.outstandingTxs[txID] = time.Now() p.txsIssued++ - p.txsIssuedCounter.Inc() + p.metrics.IncIssuedTx() } // ObserveConfirmed records a transaction that was confirmed. @@ -117,8 +81,7 @@ func (p *Tracker[T]) ObserveConfirmed(txID T) { delete(p.outstandingTxs, txID) p.txsConfirmed++ - p.txsConfirmedCounter.Inc() - p.txLatency.Observe(float64(time.Since(startTime).Milliseconds())) + p.metrics.RecordConfirmedTx(float64(time.Since(startTime).Milliseconds())) } // ObserveFailed records a transaction that failed (e.g. expired) @@ -130,6 +93,5 @@ func (p *Tracker[T]) ObserveFailed(txID T) { delete(p.outstandingTxs, txID) p.txsFailed++ - p.txsFailedCounter.Inc() - p.txLatency.Observe(float64(time.Since(startTime).Milliseconds())) + p.metrics.RecordFailedTx(float64(time.Since(startTime).Milliseconds())) } From 559c39a2ad39030b420c51d9e349a40a1e3c926e Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Tue, 20 May 2025 17:45:31 +0200 Subject: [PATCH 124/197] Bump rate multiplier to 1.1 --- tests/load/c/execute.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/load/c/execute.go b/tests/load/c/execute.go index 384fa0d3fb3f..ce17a95f3c10 100644 --- a/tests/load/c/execute.go +++ b/tests/load/c/execute.go @@ -48,7 +48,7 @@ func execute(ctx context.Context, keys []*secp256k1.PrivateKey, config loadConfi orchestratorConfig.MinTPS = config.minTPS orchestratorConfig.MaxTPS = config.maxTPS orchestratorConfig.Step = config.step - orchestratorConfig.TxRateMultiplier = 1.05 + orchestratorConfig.TxRateMultiplier = 1.1 orchestrator := load.NewOrchestrator(agents, tracker, logger, orchestratorConfig) return orchestrator.Execute(orchestratorCtx) From 3aa0e99a23ac51d51f20313a1998f883c0123dec Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Tue, 20 May 2025 17:47:02 +0200 Subject: [PATCH 125/197] Set config values that pass --- tests/load/c/ginkgo_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/load/c/ginkgo_test.go b/tests/load/c/ginkgo_test.go index 42026f0f92d2..c41692bbd172 100644 --- a/tests/load/c/ginkgo_test.go +++ b/tests/load/c/ginkgo_test.go @@ -113,9 +113,9 @@ var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { issuer: issuerSimple, maxFeeCap: 4761904, // max fee cap equivalent to 100 ether agents: agentsPerNode, - minTPS: 50, - maxTPS: 90, - step: 10, + minTPS: 2400, + maxTPS: 3000, + step: 100, } err = execute(ctx, network.PreFundedKeys, config, metrics, logger) if err != nil { @@ -131,9 +131,9 @@ var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { issuer: issuerOpcoder, maxFeeCap: 300000000000, agents: agentsCount, - minTPS: 30, - maxTPS: 60, - step: 5, + minTPS: 1000, + maxTPS: 1600, + step: 50, } err = execute(ctx, network.PreFundedKeys, config, metrics, logger) if err != nil { From b24bf8008784942378c88cd70eff2819e04f0f33 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Tue, 20 May 2025 17:57:52 +0200 Subject: [PATCH 126/197] Try (fail) setting the gas limit without touching coreth --- go.mod | 2 +- go.sum | 4 ++-- tests/load/c/ginkgo_test.go | 38 +++++++++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 477af90157c8..c85fe7d1eb32 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/NYTimes/gziphandler v1.1.1 github.com/StephenButtolph/canoto v0.15.0 github.com/antithesishq/antithesis-sdk-go v0.3.8 - github.com/ava-labs/coreth v0.15.1-rc.0.0.20250520105702-ca776d7463bf + github.com/ava-labs/coreth v0.15.1-rc.0.0.20250509150914-391115af7620 github.com/ava-labs/ledger-avalanche/go v0.0.0-20241009183145-e6f90a8a1a60 github.com/ava-labs/libevm v1.13.14-0.2.0.release github.com/btcsuite/btcd/btcutil v1.1.3 diff --git a/go.sum b/go.sum index 3d03dd8c9bcc..0e1110fe76d5 100644 --- a/go.sum +++ b/go.sum @@ -68,8 +68,8 @@ github.com/antithesishq/antithesis-sdk-go v0.3.8/go.mod h1:IUpT2DPAKh6i/YhSbt6Gl github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/ava-labs/coreth v0.15.1-rc.0.0.20250520105702-ca776d7463bf h1:ZxPyP3vbjkumzYBlDEW8EXV3Z5JUpChEufJBPoNtRRs= -github.com/ava-labs/coreth v0.15.1-rc.0.0.20250520105702-ca776d7463bf/go.mod h1:6HipGuNCN6IIe30AXwvG11Ja0AudQMtrMdTypjsju5U= +github.com/ava-labs/coreth v0.15.1-rc.0.0.20250509150914-391115af7620 h1:Vm790Gn1PqUcUnOXSIbZw7BSnQCpsPAN7/IBgbqujNo= +github.com/ava-labs/coreth v0.15.1-rc.0.0.20250509150914-391115af7620/go.mod h1:mfvr1iQAADUfqkKkmQXJPwm3q9LSpcWF+fzIGk4HHuw= github.com/ava-labs/ledger-avalanche/go v0.0.0-20241009183145-e6f90a8a1a60 h1:EL66gtXOAwR/4KYBjOV03LTWgkEXvLePribLlJNu4g0= github.com/ava-labs/ledger-avalanche/go v0.0.0-20241009183145-e6f90a8a1a60/go.mod h1:/7qKobTfbzBu7eSTVaXMTr56yTYk4j2Px6/8G+idxHo= github.com/ava-labs/libevm v1.13.14-0.2.0.release h1:uKGCc5/ceeBbfAPRVtBUxbQt50WzB2pEDb8Uy93ePgQ= diff --git a/tests/load/c/ginkgo_test.go b/tests/load/c/ginkgo_test.go index c41692bbd172..1b663c83e9cb 100644 --- a/tests/load/c/ginkgo_test.go +++ b/tests/load/c/ginkgo_test.go @@ -5,13 +5,16 @@ package c import ( "context" + "encoding/json" "os" "testing" + "github.com/ava-labs/coreth/core" "github.com/onsi/ginkgo/v2" "github.com/prometheus/client_golang/prometheus" "github.com/stretchr/testify/require" + "github.com/ava-labs/avalanchego/config" "github.com/ava-labs/avalanchego/tests/fixture/e2e" "github.com/ava-labs/avalanchego/tests/fixture/tmpnet" "github.com/ava-labs/avalanchego/tests/load" @@ -48,6 +51,7 @@ var _ = ginkgo.SynchronizedBeforeSuite(func() []byte { Nodes: nodes, } setPrefundedKeys(tc, network, agentsCount) + setNetworkGas(tc, network) env := e2e.NewTestEnvironment( tc, @@ -153,3 +157,37 @@ func setPrefundedKeys(t require.TestingT, network *tmpnet.Network, minKeys int) require.NoError(t, err, "creating pre-funded keys") network.PreFundedKeys = append(network.PreFundedKeys, missingPreFundedKeys...) } + +func setNetworkGas(t require.TestingT, network *tmpnet.Network) { + if network.PrimaryChainConfigs == nil { + network.PrimaryChainConfigs = make(map[string]tmpnet.ConfigMap, 1) + } + primaryConfig, ok := network.PrimaryChainConfigs["C"] + if !ok { + primaryConfig = make(tmpnet.ConfigMap, 1) + network.PrimaryChainConfigs["C"] = primaryConfig + } + primaryConfig["gas-target"] = uint64(1000000000000000000) + + if network.DefaultFlags == nil { + network.DefaultFlags = make(tmpnet.FlagsMap) + } + network.DefaultFlags[config.DynamicFeesMaxGasCapacityKey] = "1000000000000000000" + network.DefaultFlags[config.DynamicFeesMaxGasPerSecondKey] = "10000000000000000000" + network.DefaultFlags[config.DynamicFeesTargetGasPerSecondKey] = "10000000000000000000" + + // We must set the pre-funded keys to generate a default genesis + // with those keys, so ensure at least one key is set. + require.NotEmpty(t, network.PreFundedKeys, "no pre-funded keys set") + + var err error + network.Genesis, err = network.DefaultGenesis() + require.NoError(t, err, "creating genesis") + var cChainGenesis core.Genesis + err = json.Unmarshal([]byte(network.Genesis.CChainGenesis), &cChainGenesis) + require.NoError(t, err, "unmarshalling genesis") + cChainGenesis.GasLimit = 10000000000000000000 + encodedChainGenesis, err := json.Marshal(cChainGenesis) + require.NoError(t, err, "marshalling C chain genesis") + network.Genesis.CChainGenesis = string(encodedChainGenesis) +} From 26c8f3f1bbe39104392ac008d58b3fc12738ef78 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Tue, 20 May 2025 18:09:33 +0200 Subject: [PATCH 127/197] Remove unneeded EnsureKeys --- tests/load/c/ginkgo_test.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/load/c/ginkgo_test.go b/tests/load/c/ginkgo_test.go index 1b663c83e9cb..1d732fff4bf6 100644 --- a/tests/load/c/ginkgo_test.go +++ b/tests/load/c/ginkgo_test.go @@ -82,10 +82,6 @@ var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { network = env.GetNetwork() network.Nodes = network.Nodes[:nodesCount] - for _, node := range network.Nodes { - err := node.EnsureKeys() - require.NoError(tc, err, "ensuring keys for node %s", node.NodeID) - } loadMetrics, err := load.NewMetrics(registry) require.NoError(tc, err, "failed to register load metrics") From 49ee4fee2c05bd3b5a40186a9856cbf8083a3641 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Tue, 20 May 2025 18:10:06 +0200 Subject: [PATCH 128/197] ci: rename e2e_load to load_tests --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c2c0e8240c73..980b8f8da489 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -212,8 +212,8 @@ jobs: - name: Run e2e tests shell: bash run: nix develop --command ./scripts/run_task.sh test-bootstrap-monitor-e2e - e2e_load: - name: e2e load tests + load_tests: + name: load tests runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 From 87faf426755327a4f2b4af02fcf3b81c2e9e02d2 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Tue, 20 May 2025 18:10:26 +0200 Subject: [PATCH 129/197] taskfile: rename test-e2e-load to test-load --- Taskfile.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Taskfile.yml b/Taskfile.yml index ba353688cdb1..30833efbf87b 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -185,8 +185,8 @@ tasks: - task: build-xsvm - cmd: bash -x ./scripts/tests.e2e.existing.sh {{.CLI_ARGS}} - test-e2e-load: - desc: Runs e2e load tests + test-load: + desc: Runs load tests cmds: - task: build - cmd: ./bin/ginkgo -v ./tests/load/c -- --avalanchego-path=$PWD/build/avalanchego From 8cc8864aaaab42162933c052db0bfeef0d313638 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Tue, 20 May 2025 18:11:31 +0200 Subject: [PATCH 130/197] Rename ginkgo_test.go to load_test.go --- tests/load/c/{ginkgo_test.go => load_test.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/load/c/{ginkgo_test.go => load_test.go} (100%) diff --git a/tests/load/c/ginkgo_test.go b/tests/load/c/load_test.go similarity index 100% rename from tests/load/c/ginkgo_test.go rename to tests/load/c/load_test.go From a9b13cc625cfa2d596fe7107c80b5cd278d7eff1 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 21 May 2025 10:41:37 +0200 Subject: [PATCH 131/197] Use avalanche-monitoring grafana dashboard json --- tests/load/c/dashboard.json | 999 ------------------------------------ tests/load/c/readme.md | 2 +- 2 files changed, 1 insertion(+), 1000 deletions(-) delete mode 100644 tests/load/c/dashboard.json diff --git a/tests/load/c/dashboard.json b/tests/load/c/dashboard.json deleted file mode 100644 index 502addbcd985..000000000000 --- a/tests/load/c/dashboard.json +++ /dev/null @@ -1,999 +0,0 @@ -{ - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": { - "type": "grafana", - "uid": "-- Grafana --" - }, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "type": "dashboard" - } - ] - }, - "editable": true, - "fiscalYearStartMonth": 0, - "graphTooltip": 0, - "id": 1, - "links": [], - "panels": [ - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 0 - }, - "id": 19, - "panels": [], - "title": "General information", - "type": "row" - }, - { - "datasource": { - "uid": "dekh3fflmqzggf" - }, - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "super-light-blue", - "mode": "fixed" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 4, - "x": 0, - "y": 1 - }, - "id": 18, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "11.6.1", - "targets": [ - { - "editorMode": "code", - "expr": "count(\n count by (node_id) (\n rate(avalanche_evm_eth_chain_block_inserts_count[30s]) > 0\n )\n)\n", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Number of nodes", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "dekh3fflmqzggf" - }, - "description": "Base fee averaged across all nodes", - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "super-light-yellow", - "mode": "fixed" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - }, - "unit": "wei" - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 3, - "x": 4, - "y": 1 - }, - "id": 13, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "11.6.1", - "targets": [ - { - "editorMode": "code", - "expr": "avg(avalanche_evm_eth_chain_latest_basefee)", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Average base fee ", - "type": "stat" - }, - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 5 - }, - "id": 20, - "panels": [], - "title": "Load test metrics", - "type": "row" - }, - { - "datasource": { - "type": "prometheus", - "uid": "dekh3fflmqzggf" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "purple", - "mode": "fixed" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "txs" - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 4, - "x": 0, - "y": 6 - }, - "id": 9, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "11.6.1", - "targets": [ - { - "editorMode": "code", - "expr": "sum(load_txs_issued)", - "legendFormat": "Confirmed", - "range": true, - "refId": "A" - } - ], - "title": "Issued txs", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "dekh3fflmqzggf" - }, - "description": "Issued transactions which are confirmed and accepted in a block", - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "super-light-green", - "mode": "fixed" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "txs" - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 4, - "x": 4, - "y": 6 - }, - "id": 7, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "11.6.1", - "targets": [ - { - "editorMode": "code", - "expr": "sum(load_txs_confirmed)", - "legendFormat": "Confirmed", - "range": true, - "refId": "A" - } - ], - "title": "Confirmed txs", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "dekh3fflmqzggf" - }, - "description": "Issued transactions which are confirmed and accepted in a block", - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "semi-dark-red", - "mode": "fixed" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "txs" - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 4, - "x": 8, - "y": 6 - }, - "id": 8, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "11.6.1", - "targets": [ - { - "editorMode": "code", - "expr": "sum(load_txs_failed)", - "legendFormat": "Confirmed", - "range": true, - "refId": "A" - } - ], - "title": "Failed txs", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "dekh3fflmqzggf" - }, - "description": "Transactions issued but not yet confirmed or failed", - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "super-light-orange", - "mode": "shades" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "txs" - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 4, - "x": 12, - "y": 6 - }, - "id": 10, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "11.6.1", - "targets": [ - { - "editorMode": "code", - "expr": "sum(load_txs_issued)-sum(load_txs_confirmed)-sum(load_txs_failed)", - "legendFormat": "Confirmed", - "range": true, - "refId": "A" - } - ], - "title": "In flight txs", - "type": "stat" - }, - { - "collapsed": true, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 10 - }, - "id": 22, - "panels": [], - "title": "Per second", - "type": "row" - }, - { - "datasource": { - "type": "prometheus", - "uid": "dekh3fflmqzggf" - }, - "description": "Number of transactions processed per second averaged across all nodes", - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "light-blue", - "mode": "fixed" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 4, - "x": 0, - "y": 11 - }, - "id": 14, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "11.6.1", - "targets": [ - { - "editorMode": "code", - "expr": "avg(rate(avalanche_evm_eth_chain_txs_processed[1m]))", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Average txs per second", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "dekh3fflmqzggf" - }, - "description": "Number of gas processed per second averaged across all nodes", - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "light-blue", - "mode": "fixed" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 4, - "x": 4, - "y": 11 - }, - "id": 16, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "11.6.1", - "targets": [ - { - "editorMode": "code", - "expr": "avg(rate(avalanche_evm_eth_chain_block_gas_used_processed[1m]))", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Average gas per second", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "dekh3fflmqzggf" - }, - "description": "Number of blocks produced per son, averaged across all nodes", - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "light-blue", - "mode": "fixed" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 4, - "x": 8, - "y": 11 - }, - "id": 23, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "11.6.1", - "targets": [ - { - "editorMode": "code", - "expr": "avg(rate(avalanche_evm_eth_chain_block_inserts[30s]))", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Blocks per second", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "dekh3fflmqzggf" - }, - "description": "Max current number of transactions processed per second across all nodes", - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "light-blue", - "mode": "fixed" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 4, - "x": 0, - "y": 15 - }, - "id": 17, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "11.6.1", - "targets": [ - { - "editorMode": "code", - "expr": "max(rate(avalanche_evm_eth_chain_txs_processed[1m]))", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Max txs per second", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "dekh3fflmqzggf" - }, - "description": "Max number of gas processed per second across all nodes", - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "light-blue", - "mode": "fixed" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 4, - "x": 4, - "y": 15 - }, - "id": 24, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "11.6.1", - "targets": [ - { - "editorMode": "code", - "expr": "max(rate(avalanche_evm_eth_chain_block_gas_used_processed[1m]))", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Max gas per second", - "type": "stat" - }, - { - "collapsed": true, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 19 - }, - "id": 21, - "panels": [], - "title": "Per block", - "type": "row" - }, - { - "datasource": { - "type": "prometheus", - "uid": "dekh3fflmqzggf" - }, - "description": "Number of transactions processed per block averaged across all nodes", - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "super-light-purple", - "mode": "fixed" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 4, - "x": 0, - "y": 20 - }, - "id": 12, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "11.6.1", - "targets": [ - { - "editorMode": "code", - "expr": "avg(rate(avalanche_evm_eth_chain_txs_processed[1m])) / avg(rate(avalanche_evm_eth_chain_block_inserts_count[1m]))", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Average txs per block", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "dekh3fflmqzggf" - }, - "description": "Gas used in processed block averaged across all nodes", - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "super-light-purple", - "mode": "fixed" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 4, - "x": 4, - "y": 20 - }, - "id": 11, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "11.6.1", - "targets": [ - { - "editorMode": "code", - "expr": "avg(rate(avalanche_evm_eth_chain_block_gas_used_processed[1m])) / avg(rate(avalanche_evm_eth_chain_block_inserts_count[1m]))", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Average gas per block", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "dekh3fflmqzggf" - }, - "description": "Insert time per block averaged across all nodes", - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "super-light-purple", - "mode": "fixed" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green" - } - ] - }, - "unit": "ms" - }, - "overrides": [] - }, - "gridPos": { - "h": 4, - "w": 4, - "x": 8, - "y": 20 - }, - "id": 5, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "11.6.1", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "dekh3fflmqzggf" - }, - "editorMode": "code", - "expr": "avg(rate(avalanche_evm_eth_chain_block_inserts[1m]))/avg(rate(avalanche_evm_eth_chain_block_inserts_count[1m]))", - "legendFormat": "__auto", - "range": true, - "refId": "A" - } - ], - "title": "Average insert time per block", - "type": "stat" - } - ], - "preload": true, - "schemaVersion": 41, - "tags": [], - "templating": { - "list": [ - { - "baseFilters": [], - "datasource": { - "type": "prometheus", - "uid": "eegbq9z6fow74e" - }, - "filters": [ - { - "condition": "", - "key": "chain", - "keyLabel": "chain", - "operator": "=", - "value": "C", - "valueLabels": [ - "C" - ] - } - ], - "name": "filter", - "type": "adhoc" - } - ] - }, - "time": { - "from": "2025-05-12T15:20:41.799Z", - "to": "2025-05-12T15:22:31.414Z" - }, - "timepicker": {}, - "timezone": "browser", - "title": "Load testing", - "uid": "aejze3k4d0mpsb", - "version": 18 -} \ No newline at end of file diff --git a/tests/load/c/readme.md b/tests/load/c/readme.md index 7090eb0f7071..e629a6fd72a2 100644 --- a/tests/load/c/readme.md +++ b/tests/load/c/readme.md @@ -52,7 +52,7 @@ There are more interesting metrics available from the tmpnet nodes being load te 1. Name it `prometheus` 1. In the Connection section, set the URL to `http://localhost:9090` 1. Click the "Save & Test" button at the bottom to verify the connection. -1. Create a dashboard at [localhost:3000/dashboard/new?editview=json-model](http://localhost:3000/dashboard/new?editview=json-model) and paste the JSON content of [`dashboard.json`](dashboard.json) into the text area, and click "Save changes". +1. Create a dashboard at [localhost:3000/dashboard/new?editview=json-model](http://localhost:3000/dashboard/new?editview=json-model) and paste the JSON content of [`dashboard.json`](https://github.com/ava-labs/avalanche-monitoring/blob/main/grafana/dashboards/c_chain_load.json) into the text area, and click "Save changes". 1. Open the Load testing dashboard at [localhost:3000/d/aejze3k4d0mpsb/load-testing](http://localhost:3000/d/aejze3k4d0mpsb/load-testing) ## Run the load test From 9380693d1dc93a01497ed4a1d35313699f15151c Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 21 May 2025 10:42:47 +0200 Subject: [PATCH 132/197] Revert "Try (fail) setting the gas limit without touching coreth" This reverts commit b24bf8008784942378c88cd70eff2819e04f0f33. --- go.mod | 2 +- go.sum | 4 ++-- tests/load/c/load_test.go | 38 -------------------------------------- 3 files changed, 3 insertions(+), 41 deletions(-) diff --git a/go.mod b/go.mod index c85fe7d1eb32..477af90157c8 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/NYTimes/gziphandler v1.1.1 github.com/StephenButtolph/canoto v0.15.0 github.com/antithesishq/antithesis-sdk-go v0.3.8 - github.com/ava-labs/coreth v0.15.1-rc.0.0.20250509150914-391115af7620 + github.com/ava-labs/coreth v0.15.1-rc.0.0.20250520105702-ca776d7463bf github.com/ava-labs/ledger-avalanche/go v0.0.0-20241009183145-e6f90a8a1a60 github.com/ava-labs/libevm v1.13.14-0.2.0.release github.com/btcsuite/btcd/btcutil v1.1.3 diff --git a/go.sum b/go.sum index 0e1110fe76d5..3d03dd8c9bcc 100644 --- a/go.sum +++ b/go.sum @@ -68,8 +68,8 @@ github.com/antithesishq/antithesis-sdk-go v0.3.8/go.mod h1:IUpT2DPAKh6i/YhSbt6Gl github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/ava-labs/coreth v0.15.1-rc.0.0.20250509150914-391115af7620 h1:Vm790Gn1PqUcUnOXSIbZw7BSnQCpsPAN7/IBgbqujNo= -github.com/ava-labs/coreth v0.15.1-rc.0.0.20250509150914-391115af7620/go.mod h1:mfvr1iQAADUfqkKkmQXJPwm3q9LSpcWF+fzIGk4HHuw= +github.com/ava-labs/coreth v0.15.1-rc.0.0.20250520105702-ca776d7463bf h1:ZxPyP3vbjkumzYBlDEW8EXV3Z5JUpChEufJBPoNtRRs= +github.com/ava-labs/coreth v0.15.1-rc.0.0.20250520105702-ca776d7463bf/go.mod h1:6HipGuNCN6IIe30AXwvG11Ja0AudQMtrMdTypjsju5U= github.com/ava-labs/ledger-avalanche/go v0.0.0-20241009183145-e6f90a8a1a60 h1:EL66gtXOAwR/4KYBjOV03LTWgkEXvLePribLlJNu4g0= github.com/ava-labs/ledger-avalanche/go v0.0.0-20241009183145-e6f90a8a1a60/go.mod h1:/7qKobTfbzBu7eSTVaXMTr56yTYk4j2Px6/8G+idxHo= github.com/ava-labs/libevm v1.13.14-0.2.0.release h1:uKGCc5/ceeBbfAPRVtBUxbQt50WzB2pEDb8Uy93ePgQ= diff --git a/tests/load/c/load_test.go b/tests/load/c/load_test.go index 1d732fff4bf6..436c3bdf72e8 100644 --- a/tests/load/c/load_test.go +++ b/tests/load/c/load_test.go @@ -5,16 +5,13 @@ package c import ( "context" - "encoding/json" "os" "testing" - "github.com/ava-labs/coreth/core" "github.com/onsi/ginkgo/v2" "github.com/prometheus/client_golang/prometheus" "github.com/stretchr/testify/require" - "github.com/ava-labs/avalanchego/config" "github.com/ava-labs/avalanchego/tests/fixture/e2e" "github.com/ava-labs/avalanchego/tests/fixture/tmpnet" "github.com/ava-labs/avalanchego/tests/load" @@ -51,7 +48,6 @@ var _ = ginkgo.SynchronizedBeforeSuite(func() []byte { Nodes: nodes, } setPrefundedKeys(tc, network, agentsCount) - setNetworkGas(tc, network) env := e2e.NewTestEnvironment( tc, @@ -153,37 +149,3 @@ func setPrefundedKeys(t require.TestingT, network *tmpnet.Network, minKeys int) require.NoError(t, err, "creating pre-funded keys") network.PreFundedKeys = append(network.PreFundedKeys, missingPreFundedKeys...) } - -func setNetworkGas(t require.TestingT, network *tmpnet.Network) { - if network.PrimaryChainConfigs == nil { - network.PrimaryChainConfigs = make(map[string]tmpnet.ConfigMap, 1) - } - primaryConfig, ok := network.PrimaryChainConfigs["C"] - if !ok { - primaryConfig = make(tmpnet.ConfigMap, 1) - network.PrimaryChainConfigs["C"] = primaryConfig - } - primaryConfig["gas-target"] = uint64(1000000000000000000) - - if network.DefaultFlags == nil { - network.DefaultFlags = make(tmpnet.FlagsMap) - } - network.DefaultFlags[config.DynamicFeesMaxGasCapacityKey] = "1000000000000000000" - network.DefaultFlags[config.DynamicFeesMaxGasPerSecondKey] = "10000000000000000000" - network.DefaultFlags[config.DynamicFeesTargetGasPerSecondKey] = "10000000000000000000" - - // We must set the pre-funded keys to generate a default genesis - // with those keys, so ensure at least one key is set. - require.NotEmpty(t, network.PreFundedKeys, "no pre-funded keys set") - - var err error - network.Genesis, err = network.DefaultGenesis() - require.NoError(t, err, "creating genesis") - var cChainGenesis core.Genesis - err = json.Unmarshal([]byte(network.Genesis.CChainGenesis), &cChainGenesis) - require.NoError(t, err, "unmarshalling genesis") - cChainGenesis.GasLimit = 10000000000000000000 - encodedChainGenesis, err := json.Marshal(cChainGenesis) - require.NoError(t, err, "marshalling C chain genesis") - network.Genesis.CChainGenesis = string(encodedChainGenesis) -} From 3a6809d94d50833918f883255ed50fa4e931c166 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 21 May 2025 10:46:35 +0200 Subject: [PATCH 133/197] Update readme for both remote and local visualization --- tests/load/c/readme.md | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/tests/load/c/readme.md b/tests/load/c/readme.md index e629a6fd72a2..a2faaf23bae3 100644 --- a/tests/load/c/readme.md +++ b/tests/load/c/readme.md @@ -19,9 +19,23 @@ From the load test perspective, only the TPS (transactions per second) is logged - total transactions failed `txs_failed` - transaction latency histogram `tx_latency` -There are more interesting metrics available from the tmpnet nodes being load tested. The following sub-sections explain how to set up the monitoring stack to visualize the metrics during the load tests. +There are more interesting metrics available from the tmpnet nodes being load tested. -## Prometheus +If you have the Grafana credentials, the easiest way to visualize the metrics is to click the Grafana URL logged by the load test, in the form + +```log +INFO metrics and logs available via grafana (collectors must be running) {"url": "https://grafana-poc.avax-dev.network/d/kBQpRdWnk/avalanche-main-dashboard?&var-filter=network_uuid%7C%3D%7Cdb0cb247-e0a6-46e1-b265-4ac6b3c8f6c4&var-filter=is_ephemeral_node%7C%3D%7Cfalse&from=1747816981769&to=now"} +``` + +If you don't, follow the local steps in the [visualize metrics locally](#visualize-metrics-locally) section. + +Finally, to run the load test, from the root of the repository: + +```bash +./bin/ginkgo -v tests/load/c -- --avalanchego-path=$PWD/build/avalanchego +``` + +## Visualize metrics locally 1. Navigate to this directory with `cd tests/load/c`. 1. Setup the Prometheus configuration file: `envsubst < prometheus.template.yml > prometheus.yml` @@ -36,10 +50,7 @@ There are more interesting metrics available from the tmpnet nodes being load te ``` This starts Prometheus listening on port `9090`. - -## Grafana - -1. In a separate terminal, install and launch the Grafana service: +1. In a separate terminal, install and launch the Grafana service. For example on MacOS: ```bash brew install grafana @@ -54,11 +65,3 @@ There are more interesting metrics available from the tmpnet nodes being load te 1. Click the "Save & Test" button at the bottom to verify the connection. 1. Create a dashboard at [localhost:3000/dashboard/new?editview=json-model](http://localhost:3000/dashboard/new?editview=json-model) and paste the JSON content of [`dashboard.json`](https://github.com/ava-labs/avalanche-monitoring/blob/main/grafana/dashboards/c_chain_load.json) into the text area, and click "Save changes". 1. Open the Load testing dashboard at [localhost:3000/d/aejze3k4d0mpsb/load-testing](http://localhost:3000/d/aejze3k4d0mpsb/load-testing) - -## Run the load test - -From the root of the repository: - -```bash -./bin/ginkgo -v tests/load/c -- --avalanchego-path=$PWD/build/avalanchego -``` From a657afce9b0de78959eb20cd955c813f85d17718 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 21 May 2025 13:58:17 +0200 Subject: [PATCH 134/197] Update readme for remote grafana --- tests/load/c/readme.md | 47 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/tests/load/c/readme.md b/tests/load/c/readme.md index a2faaf23bae3..ee850d91fbce 100644 --- a/tests/load/c/readme.md +++ b/tests/load/c/readme.md @@ -21,21 +21,50 @@ From the load test perspective, only the TPS (transactions per second) is logged There are more interesting metrics available from the tmpnet nodes being load tested. -If you have the Grafana credentials, the easiest way to visualize the metrics is to click the Grafana URL logged by the load test, in the form +Finally, to run the load test, run: -```log -INFO metrics and logs available via grafana (collectors must be running) {"url": "https://grafana-poc.avax-dev.network/d/kBQpRdWnk/avalanche-main-dashboard?&var-filter=network_uuid%7C%3D%7Cdb0cb247-e0a6-46e1-b265-4ac6b3c8f6c4&var-filter=is_ephemeral_node%7C%3D%7Cfalse&from=1747816981769&to=now"} +```bash +./bin/ginkgo -v tests/load/c -- --avalanchego-path=$PWD/build/avalanchego ``` -If you don't, follow the local steps in the [visualize metrics locally](#visualize-metrics-locally) section. +## Visualize metrics in Grafana -Finally, to run the load test, from the root of the repository: +### Private remote instances -```bash -./bin/ginkgo -v tests/load/c -- --avalanchego-path=$PWD/build/avalanchego -``` +If you have the credentials (internal to Ava Labs) for the remote Prometheus and Grafana PoC, you can visualize the metrics following these steps: + +1. Start the dev shell to have Prometheus setup to scrape the load test metrics and send it to the remote Prometheus instance: + + ```bash + nix develop + ``` + +1. Set your Prometheus credentials using the credentials you can find in your password manager + + ```bash + export PROMETHEUS_USERNAME= + export PROMETHEUS_PASSWORD= + export LOKI_USERNAME= + export LOKI_PASSWORD= + ``` + +1. Run the load test: + + ```bash + ./bin/ginkgo -v tests/load/c -- --avalanchego-path=$PWD/build/avalanchego --start-collectors + ``` + +1. Wait for the load test to finish, this will log out a URL at the end of the test, in the form + + ```log + INFO metrics and logs available via grafana (collectors must be running) {"uri": "https://grafana-poc.avax-dev.network/d/eabddd1d-0a06-4ba1-8e68-a44504e37535/C-Chain%20Load?from=1747817500582&to=1747817952631&var-filter=network_uuid%7C%3D%7C4f419e3a-dba5-4ccd-b2fd-bda15f9826ff"} + ``` + +1. Open the URL in your browser, and log in with the Grafana credentials which you can find in your password manager. + +For reference, see [the tmpnet monitoring section](../../fixture/tmpnet/README.md#monitoring) -## Visualize metrics locally +### Locally 1. Navigate to this directory with `cd tests/load/c`. 1. Setup the Prometheus configuration file: `envsubst < prometheus.template.yml > prometheus.yml` From c9cb78730ee9b8a17c89df6112511ef48548f2c9 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 21 May 2025 14:21:28 +0200 Subject: [PATCH 135/197] fix(metrics): set server address for Prometheus scrape config --- tests/load/prometheus.go | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/load/prometheus.go b/tests/load/prometheus.go index ddabeaf593c2..61fb7dae6677 100644 --- a/tests/load/prometheus.go +++ b/tests/load/prometheus.go @@ -51,6 +51,7 @@ func (s *MetricsServer) Start() (runError <-chan error, err error) { if err != nil { return nil, err } + s.addr = listener.Addr().String() s.server = http.Server{ Addr: s.addr, From 5aeb7763ca63fe4254fbdf1423b675ae9ba8823c Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 21 May 2025 14:57:50 +0200 Subject: [PATCH 136/197] Import libevm instead of coreth for params package --- tests/load/c/issuers/simple.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/load/c/issuers/simple.go b/tests/load/c/issuers/simple.go index 95a5832ca4aa..4169cdc46254 100644 --- a/tests/load/c/issuers/simple.go +++ b/tests/load/c/issuers/simple.go @@ -10,9 +10,9 @@ import ( "math/big" "time" - "github.com/ava-labs/coreth/params" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/types" + "github.com/ava-labs/libevm/params" ethcrypto "github.com/ava-labs/libevm/crypto" ) From 3de4dda836004289b44cf5f4e564d96446302687 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 21 May 2025 14:58:17 +0200 Subject: [PATCH 137/197] c/issuers/simple: remove unused lastIssue field --- tests/load/c/issuers/simple.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/load/c/issuers/simple.go b/tests/load/c/issuers/simple.go index 4169cdc46254..345de4eeb7f1 100644 --- a/tests/load/c/issuers/simple.go +++ b/tests/load/c/issuers/simple.go @@ -8,7 +8,6 @@ import ( "crypto/ecdsa" "fmt" "math/big" - "time" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/types" @@ -42,8 +41,7 @@ type Simple struct { gasTipCap *big.Int // State - nonce uint64 - lastIssue time.Time + nonce uint64 } func NewSimple(ctx context.Context, client EthClientSimple, tracker IssueTracker, @@ -94,6 +92,5 @@ func (s *Simple) GenerateAndIssueTx(ctx context.Context) (common.Hash, error) { s.nonce++ txHash := tx.Hash() s.tracker.Issue(txHash) - s.lastIssue = time.Now() return txHash, nil } From 7c0b301b9a715fe2d0931cc0befc227d26b2575f Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 21 May 2025 14:59:03 +0200 Subject: [PATCH 138/197] c/readme: fix path to entrypoint as load_test.go --- tests/load/c/readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/load/c/readme.md b/tests/load/c/readme.md index ee850d91fbce..7172d0c1b6c2 100644 --- a/tests/load/c/readme.md +++ b/tests/load/c/readme.md @@ -1,6 +1,6 @@ # Load testing -The C-chain load test entrypoint is in [ginkgo_test.go](ginkgo_test.go). +The C-chain load test entrypoint is in [load_test.go](load_test.go). It runs with 5 nodes and 5 "agents". From d067e02bbf9b02d429ca86b24561916c91751343 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 21 May 2025 17:38:40 +0200 Subject: [PATCH 139/197] Use `task test-load` --- tests/load/c/load_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/load/c/load_test.go b/tests/load/c/load_test.go index 436c3bdf72e8..c30375170421 100644 --- a/tests/load/c/load_test.go +++ b/tests/load/c/load_test.go @@ -18,8 +18,8 @@ import ( "github.com/ava-labs/avalanchego/utils/logging" ) -// Run this using the command: -// ./bin/ginkgo -v ./tests/load/c -- --avalanchego-path=$PWD/build/avalanchego +// Run this using the command from the root of the repository in a Nix develop shell: +// task test-load func TestLoad(t *testing.T) { ginkgo.RunSpecs(t, "load tests") } From e2ecc1c322a93696fa3eaaa43dc5c6ce32ee28bb Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 21 May 2025 17:40:54 +0200 Subject: [PATCH 140/197] Fix CI workflow to run test-load task --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 323c3c57003f..cc4b7b5c86df 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -226,7 +226,7 @@ jobs: run: nix develop --command ./scripts/run_task.sh check-generate-load-contract-bindings - uses: ./.github/actions/run-monitored-tmpnet-cmd with: - run: ./scripts/run_task.sh test-e2e-load + run: ./scripts/run_task.sh test-load artifact_prefix: e2e-load filter_by_owner: avalanchego-e2e prometheus_username: ${{ secrets.PROMETHEUS_ID || '' }} From a60a5e8ad19ff83f37a87cdf9d7de23dcbf7374e Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 21 May 2025 17:41:57 +0200 Subject: [PATCH 141/197] ci: remove `filter_by_owner` field --- .github/workflows/ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cc4b7b5c86df..2e29fbf2a825 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -228,7 +228,6 @@ jobs: with: run: ./scripts/run_task.sh test-load artifact_prefix: e2e-load - filter_by_owner: avalanchego-e2e prometheus_username: ${{ secrets.PROMETHEUS_ID || '' }} prometheus_password: ${{ secrets.PROMETHEUS_PASSWORD || '' }} loki_username: ${{ secrets.LOKI_ID || '' }} From 0a58664b713071ea302390751338b2232307a196 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 21 May 2025 17:55:02 +0200 Subject: [PATCH 142/197] Run generate-load-contract-bindings as part of load test task --- Taskfile.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/Taskfile.yml b/Taskfile.yml index f6ab0990f2bf..5b8da4bf6d5d 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -213,6 +213,7 @@ tasks: test-load: desc: Runs load tests cmds: + - task: generate-load-contract-bindings - task: build - cmd: ./bin/ginkgo -v ./tests/load/c -- --avalanchego-path=$PWD/build/avalanchego From aa6e24197590cdcbb6bcc511dd7ad51660b100df Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 21 May 2025 17:56:14 +0200 Subject: [PATCH 143/197] Remove load test command from e2e readme --- tests/e2e/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/e2e/README.md b/tests/e2e/README.md index a266a7dc767f..65bc1ddf4677 100644 --- a/tests/e2e/README.md +++ b/tests/e2e/README.md @@ -9,7 +9,6 @@ ./scripts/build.sh # Builds avalanchego for use in deploying a test network ./scripts/build_xsvm.sh # Builds xsvm for use in deploying a test network with a subnet ./bin/ginkgo -v ./tests/e2e -- --avalanchego-path=$PWD/build/avalanchego # Note that the path given for --avalanchego-path must be an absolute and not a relative path. -./bin/ginkgo -v ./tests/load/c -- --avalanchego-path=$PWD/build/avalanchego ``` See [`tests.e2e.sh`](../../scripts/tests.e2e.sh) for an example. From bb97a1e41a4d3382b83e73f525a99d412778ef96 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Wed, 21 May 2025 17:57:18 +0200 Subject: [PATCH 144/197] ci: remove Contract bindings are up to date since it's already part of the task --- .github/workflows/ci.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2e29fbf2a825..b3c9b31c1a7c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -221,9 +221,6 @@ jobs: fetch-depth: 0 - uses: ./.github/actions/setup-go-for-project - uses: ./.github/actions/install-nix - - name: Contract bindings are up to date - shell: bash - run: nix develop --command ./scripts/run_task.sh check-generate-load-contract-bindings - uses: ./.github/actions/run-monitored-tmpnet-cmd with: run: ./scripts/run_task.sh test-load From 9c79e066df9f86b05c1f5511b7f24865afa65ee3 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 22 May 2025 10:57:51 +0200 Subject: [PATCH 145/197] Use abigen from go.mod libevm version --- tests/load/c/contracts/generate_abi_bindings.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/load/c/contracts/generate_abi_bindings.sh b/tests/load/c/contracts/generate_abi_bindings.sh index 1c219fbd9980..de6d4e032289 100755 --- a/tests/load/c/contracts/generate_abi_bindings.sh +++ b/tests/load/c/contracts/generate_abi_bindings.sh @@ -15,7 +15,7 @@ for FILE in "${CONTRACTS_DIR}"/*.sol; do echo "Generating Go bindings from Solidity contract $FILE..." CONTRACT_NAME=$(basename "$FILE" .sol) solc --abi --bin --overwrite -o "$TEMPDIR" "${CONTRACTS_DIR}/${CONTRACT_NAME}.sol" - go run github.com/ava-labs/libevm/cmd/abigen@latest \ + go run github.com/ava-labs/libevm/cmd/abigen \ --bin="${TEMPDIR}/${CONTRACT_NAME}.bin" \ --abi="${TEMPDIR}/${CONTRACT_NAME}.abi" \ --type "$CONTRACT_NAME" \ From de7dd40dbc308f73649fab17707ab2139131e15b Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 22 May 2025 10:58:52 +0200 Subject: [PATCH 146/197] Change ci artificat prefix to load --- .github/workflows/ci.yml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b3c9b31c1a7c..9ea87c524f7f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,15 @@ jobs: strategy: fail-fast: false matrix: - os: [macos-14, ubuntu-22.04, ubuntu-24.04, windows-2022, custom-arm64-jammy, custom-arm64-noble] + os: + [ + macos-14, + ubuntu-22.04, + ubuntu-24.04, + windows-2022, + custom-arm64-jammy, + custom-arm64-noble, + ] steps: - uses: actions/checkout@v4 - uses: ./.github/actions/setup-go-for-project @@ -224,7 +232,7 @@ jobs: - uses: ./.github/actions/run-monitored-tmpnet-cmd with: run: ./scripts/run_task.sh test-load - artifact_prefix: e2e-load + artifact_prefix: load prometheus_username: ${{ secrets.PROMETHEUS_ID || '' }} prometheus_password: ${{ secrets.PROMETHEUS_PASSWORD || '' }} loki_username: ${{ secrets.LOKI_ID || '' }} From 93b5eeff895984a54b4dd47467e192240d5fd528 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 22 May 2025 11:00:41 +0200 Subject: [PATCH 147/197] Reduce flake.nix solc comment --- flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index a91f82508b4b..e4a2580520dd 100644 --- a/flake.nix +++ b/flake.nix @@ -58,7 +58,7 @@ protoc-gen-go protoc-gen-go-grpc - # Solidity compiler from nixpkgs 24.11 + # Solidity compiler solc ] ++ lib.optionals stdenv.isDarwin [ # macOS-specific frameworks From 8d55364011407b46e5f418d87c5b961766bbea6b Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 22 May 2025 11:01:41 +0200 Subject: [PATCH 148/197] Restrict contract bindings generation to c directory --- Taskfile.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Taskfile.yml b/Taskfile.yml index 5b8da4bf6d5d..86a6dc58caf9 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -2,7 +2,7 @@ # To run on a system without task installed, `./scripts/run_task.sh` will execute it with `go run`. # If in the nix dev shell, `task` is available. -version: '3' +version: "3" tasks: default: ./scripts/run_task.sh --list @@ -94,8 +94,8 @@ tasks: generate-load-contract-bindings: desc: Generates load contract bindings cmds: - - cmd: grep -lr -E '^// Code generated - DO NOT EDIT\.$' tests/load | xargs -r rm - - cmd: go generate ./tests/load/... + - cmd: grep -lr -E '^// Code generated - DO NOT EDIT\.$' tests/load/c | xargs -r rm + - cmd: go generate ./tests/load/c/... generate-protobuf: desc: Generates protobuf From 6f138caf01cb79bd42983d2775df7b5ef6d3bf32 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 22 May 2025 11:02:06 +0200 Subject: [PATCH 149/197] Restrict unit test exclusion from tests/load to tests/load/c --- scripts/build_test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/build_test.sh b/scripts/build_test.sh index b87b6e5e50bf..2f4c534c76cf 100755 --- a/scripts/build_test.sh +++ b/scripts/build_test.sh @@ -7,7 +7,7 @@ AVALANCHE_PATH=$( cd "$( dirname "${BASH_SOURCE[0]}" )"; cd .. && pwd ) # Load the constants source "$AVALANCHE_PATH"/scripts/constants.sh -EXCLUDED_TARGETS="| grep -v /mocks | grep -v proto | grep -v tests/e2e | grep -v tests/load | grep -v tests/upgrade | grep -v tests/fixture/bootstrapmonitor/e2e" +EXCLUDED_TARGETS="| grep -v /mocks | grep -v proto | grep -v tests/e2e | grep -v tests/load/c | grep -v tests/upgrade | grep -v tests/fixture/bootstrapmonitor/e2e" if [[ "$(go env GOOS)" == "windows" ]]; then # Test discovery for the antithesis test setups is broken due to From b079371d2e2e32aca30a83dfa044f2935efdeeed Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 22 May 2025 11:03:20 +0200 Subject: [PATCH 150/197] `BuildMetricsURLForNetwork` -> `BuildMonitoringURLForNetwork` --- tests/fixture/tmpnet/network.go | 2 +- tests/load/metrics_link.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/fixture/tmpnet/network.go b/tests/fixture/tmpnet/network.go index e262c063927f..7dcc850353d4 100644 --- a/tests/fixture/tmpnet/network.go +++ b/tests/fixture/tmpnet/network.go @@ -1081,7 +1081,7 @@ type GrafanaFilterOptions struct { Filters map[string]string } -func BuildMetricsURLForNetwork(dashboardID, dashboardName, networkUUID string, options GrafanaFilterOptions) string { +func BuildMonitoringURLForNetwork(dashboardID, dashboardName, networkUUID string, options GrafanaFilterOptions) string { // Set defaults for options if not provided startTime := "now-1h" if options.StartTime != "" { diff --git a/tests/load/metrics_link.go b/tests/load/metrics_link.go index ff2b54f737c0..db1088a474cf 100644 --- a/tests/load/metrics_link.go +++ b/tests/load/metrics_link.go @@ -37,7 +37,7 @@ var ( specReport := ginkgo.CurrentSpecReport() startTimeMs := specReport.StartTime.UnixMilli() - grafanaLink := tmpnet.BuildMetricsURLForNetwork(dashboardID, dashboardName, env.GetNetwork().UUID, tmpnet.GrafanaFilterOptions{ + grafanaLink := tmpnet.BuildMonitoringURLForNetwork(dashboardID, dashboardName, env.GetNetwork().UUID, tmpnet.GrafanaFilterOptions{ StartTime: strconv.FormatInt(startTimeMs, 10), EndTime: strconv.FormatInt(time.Now().Add(tmpnet.NetworkShutdownDelay).UnixMilli(), 10), }) From 615eab7353dec741e9d29f00d002f294d963854a Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 22 May 2025 11:03:52 +0200 Subject: [PATCH 151/197] Remove redundant comment on network_uuid presence --- tests/fixture/tmpnet/network.go | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/fixture/tmpnet/network.go b/tests/fixture/tmpnet/network.go index 7dcc850353d4..f6826ce418f3 100644 --- a/tests/fixture/tmpnet/network.go +++ b/tests/fixture/tmpnet/network.go @@ -1109,7 +1109,6 @@ func BuildMonitoringURLForNetwork(dashboardID, dashboardName, networkUUID string filters = maps.Clone(options.Filters) } - // Ensure network_uuid is set if _, exists := filters["network_uuid"]; !exists { filters["network_uuid"] = networkUUID } From daebfdce91c4116884cb94fd86c0f6a5b405d71d Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 22 May 2025 11:08:23 +0200 Subject: [PATCH 152/197] Remove _test.go suffix from contract bindings generate file --- tests/load/c/contracts/{generate_test.go => generate.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/load/c/contracts/{generate_test.go => generate.go} (100%) diff --git a/tests/load/c/contracts/generate_test.go b/tests/load/c/contracts/generate.go similarity index 100% rename from tests/load/c/contracts/generate_test.go rename to tests/load/c/contracts/generate.go From abd5354c4995625285e146baea663d394898d234 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 22 May 2025 11:13:29 +0200 Subject: [PATCH 153/197] Add --start-collectors flag to test-load task --- Taskfile.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Taskfile.yml b/Taskfile.yml index 86a6dc58caf9..98f440ab0510 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -215,7 +215,7 @@ tasks: cmds: - task: generate-load-contract-bindings - task: build - - cmd: ./bin/ginkgo -v ./tests/load/c -- --avalanchego-path=$PWD/build/avalanchego + - cmd: ./bin/ginkgo -v ./tests/load/c -- --avalanchego-path=$PWD/build/avalanchego --start-collectors test-unit: desc: Runs unit tests From 3a47e2d765e7fd28116777bfc7f9f3e234becee3 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 22 May 2025 11:14:09 +0200 Subject: [PATCH 154/197] Update load/c/readme to use nix and the test-load task --- tests/load/c/readme.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/load/c/readme.md b/tests/load/c/readme.md index 7172d0c1b6c2..da87c8995425 100644 --- a/tests/load/c/readme.md +++ b/tests/load/c/readme.md @@ -24,7 +24,10 @@ There are more interesting metrics available from the tmpnet nodes being load te Finally, to run the load test, run: ```bash -./bin/ginkgo -v tests/load/c -- --avalanchego-path=$PWD/build/avalanchego +# Start the dev shell +nix develop +# Start the load test +task test-load ``` ## Visualize metrics in Grafana @@ -51,7 +54,7 @@ If you have the credentials (internal to Ava Labs) for the remote Prometheus and 1. Run the load test: ```bash - ./bin/ginkgo -v tests/load/c -- --avalanchego-path=$PWD/build/avalanchego --start-collectors + task test-load ``` 1. Wait for the load test to finish, this will log out a URL at the end of the test, in the form From 26dc86aa948e8fb4c71086e0d71a2fb0e890e93d Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 22 May 2025 11:15:03 +0200 Subject: [PATCH 155/197] Change wording to "CI monitoring stack" --- tests/load/c/readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/load/c/readme.md b/tests/load/c/readme.md index da87c8995425..da667ee12a4e 100644 --- a/tests/load/c/readme.md +++ b/tests/load/c/readme.md @@ -34,7 +34,7 @@ task test-load ### Private remote instances -If you have the credentials (internal to Ava Labs) for the remote Prometheus and Grafana PoC, you can visualize the metrics following these steps: +If you have the credentials (internal to Ava Labs) for the CI monitoring stack, you can visualize the metrics following these steps: 1. Start the dev shell to have Prometheus setup to scrape the load test metrics and send it to the remote Prometheus instance: From 66fa3a4d16687d63146e04b3dc2e1e3d6189141c Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 22 May 2025 11:16:30 +0200 Subject: [PATCH 156/197] Improve wording for step 1 of private remote instance --- tests/load/c/readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/load/c/readme.md b/tests/load/c/readme.md index da667ee12a4e..bc7688d629b4 100644 --- a/tests/load/c/readme.md +++ b/tests/load/c/readme.md @@ -36,7 +36,7 @@ task test-load If you have the credentials (internal to Ava Labs) for the CI monitoring stack, you can visualize the metrics following these steps: -1. Start the dev shell to have Prometheus setup to scrape the load test metrics and send it to the remote Prometheus instance: +1. Start a dev shell to ensure `prometheus` and `promtail` binaries are available to the test runner so it can use them to collect metrics and logs: ```bash nix develop From fc1251b546fba764e52220bc28500babfef37dad Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 22 May 2025 11:17:42 +0200 Subject: [PATCH 157/197] readme: prometheus -> monitoring (prometheus+loki) --- tests/load/c/readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/load/c/readme.md b/tests/load/c/readme.md index bc7688d629b4..16072d01c456 100644 --- a/tests/load/c/readme.md +++ b/tests/load/c/readme.md @@ -42,7 +42,7 @@ If you have the credentials (internal to Ava Labs) for the CI monitoring stack, nix develop ``` -1. Set your Prometheus credentials using the credentials you can find in your password manager +1. Set your monitoring credentials using the credentials you can find in your password manager ```bash export PROMETHEUS_USERNAME= From 69bb3053e9e5392f05d6646c4bf16b83d820e7a9 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 22 May 2025 11:18:26 +0200 Subject: [PATCH 158/197] Replace --start-collectors with `--start-metrics-collector --start-logs-collector` --- Taskfile.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Taskfile.yml b/Taskfile.yml index 98f440ab0510..914b134eb1f6 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -215,7 +215,7 @@ tasks: cmds: - task: generate-load-contract-bindings - task: build - - cmd: ./bin/ginkgo -v ./tests/load/c -- --avalanchego-path=$PWD/build/avalanchego --start-collectors + - cmd: ./bin/ginkgo -v ./tests/load/c -- --avalanchego-path=$PWD/build/avalanchego --start-metrics-collector --start-logs-collector test-unit: desc: Runs unit tests From d8896ccc6cc4588bd79bb3ed8d5da74807a293a7 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 22 May 2025 11:25:15 +0200 Subject: [PATCH 159/197] Export and use `tmpnet.GetServiceDiscoveryDir` --- tests/fixture/tmpnet/monitor_processes.go | 7 ++++--- tests/fixture/tmpnet/process_runtime.go | 2 +- tests/load/prometheus.go | 8 +++----- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/tests/fixture/tmpnet/monitor_processes.go b/tests/fixture/tmpnet/monitor_processes.go index b7dcda663d56..9bd3e560ce99 100644 --- a/tests/fixture/tmpnet/monitor_processes.go +++ b/tests/fixture/tmpnet/monitor_processes.go @@ -172,7 +172,7 @@ func startPrometheus(ctx context.Context, log logging.Logger) error { return err } - serviceDiscoveryDir, err := getServiceDiscoveryDir(cmdName) + serviceDiscoveryDir, err := GetServiceDiscoveryDir(cmdName) if err != nil { return err } @@ -216,7 +216,7 @@ func startPromtail(ctx context.Context, log logging.Logger) error { return err } - serviceDiscoveryDir, err := getServiceDiscoveryDir(cmdName) + serviceDiscoveryDir, err := GetServiceDiscoveryDir(cmdName) if err != nil { return err } @@ -253,7 +253,8 @@ func getWorkingDir(cmdName string) (string, error) { return filepath.Join(tmpnetDir, cmdName), nil } -func getServiceDiscoveryDir(cmdName string) (string, error) { +// GetServiceDiscoveryDir returns the service discovery directory path. +func GetServiceDiscoveryDir(cmdName string) (string, error) { tmpnetDir, err := getTmpnetPath() if err != nil { return "", err diff --git a/tests/fixture/tmpnet/process_runtime.go b/tests/fixture/tmpnet/process_runtime.go index 9088f0928210..d1729c5f5618 100644 --- a/tests/fixture/tmpnet/process_runtime.go +++ b/tests/fixture/tmpnet/process_runtime.go @@ -372,7 +372,7 @@ func (p *ProcessRuntime) writeMonitoringConfig() error { func (p *ProcessRuntime) getMonitoringConfigPath(name string) (string, error) { // Ensure a unique filename to allow config files to be added and removed // by multiple nodes without conflict. - serviceDiscoveryDir, err := getServiceDiscoveryDir(name) + serviceDiscoveryDir, err := GetServiceDiscoveryDir(name) if err != nil { return "", err } diff --git a/tests/load/prometheus.go b/tests/load/prometheus.go index 61fb7dae6677..0ec59263bf99 100644 --- a/tests/load/prometheus.go +++ b/tests/load/prometheus.go @@ -89,14 +89,12 @@ func (s *MetricsServer) Stop() (err error) { // so tmpnet can dynamically discover new scrape target via file-based service discovery // It returns the collector file path and an eventual error func (s *MetricsServer) GenerateMonitoringConfig(networkUUID, networkOwner string) (string, error) { - const metricsFilePath = ".tmpnet/prometheus/file_sd_configs/load-test.json" - - homedir, err := os.UserHomeDir() + discoveryDir, err := tmpnet.GetServiceDiscoveryDir("prometheus") if err != nil { - return "", err + return "", fmt.Errorf("getting tmpnet service discovery directory: %w", err) } - collectorFilePath := filepath.Join(homedir, metricsFilePath) + collectorFilePath := filepath.Join(discoveryDir, "load-test.json") config, err := json.MarshalIndent([]tmpnet.ConfigMap{ { "targets": []string{s.addr}, From da8b9041ef0ac76b0c923af3651afd35c7c79697 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 22 May 2025 11:25:55 +0200 Subject: [PATCH 160/197] Shorten GenerateMonitoringConfig comment --- tests/load/prometheus.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/load/prometheus.go b/tests/load/prometheus.go index 0ec59263bf99..49ecc63c2104 100644 --- a/tests/load/prometheus.go +++ b/tests/load/prometheus.go @@ -87,7 +87,7 @@ func (s *MetricsServer) Stop() (err error) { // GenerateMonitoringConfig generates and writes the Prometheus collector configuration // so tmpnet can dynamically discover new scrape target via file-based service discovery -// It returns the collector file path and an eventual error +// It returns the collector file path. func (s *MetricsServer) GenerateMonitoringConfig(networkUUID, networkOwner string) (string, error) { discoveryDir, err := tmpnet.GetServiceDiscoveryDir("prometheus") if err != nil { From c7d2bebadb1460bf141576ee967af6fd0df81792 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 22 May 2025 11:27:20 +0200 Subject: [PATCH 161/197] Use github.com/ava-labs/avalanchego/utils/perms --- tests/load/prometheus.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/load/prometheus.go b/tests/load/prometheus.go index 49ecc63c2104..53859bb78200 100644 --- a/tests/load/prometheus.go +++ b/tests/load/prometheus.go @@ -19,6 +19,7 @@ import ( "github.com/ava-labs/avalanchego/tests/fixture/tmpnet" "github.com/ava-labs/avalanchego/utils/logging" + "github.com/ava-labs/avalanchego/utils/perms" ) type MetricsServer struct { @@ -108,5 +109,5 @@ func (s *MetricsServer) GenerateMonitoringConfig(networkUUID, networkOwner strin return "", err } - return collectorFilePath, os.WriteFile(collectorFilePath, config, 0o600) + return collectorFilePath, os.WriteFile(collectorFilePath, config, perms.ReadWrite) } From 45d4f869c9c93b4ed2ddf4472ece53740cb6d5ae Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 22 May 2025 11:31:16 +0200 Subject: [PATCH 162/197] Use require.NoError in load_test.go --- tests/load/c/load_test.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/load/c/load_test.go b/tests/load/c/load_test.go index c30375170421..029bfb576d4a 100644 --- a/tests/load/c/load_test.go +++ b/tests/load/c/load_test.go @@ -114,9 +114,7 @@ var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { step: 100, } err = execute(ctx, network.PreFundedKeys, config, metrics, logger) - if err != nil { - ginkgo.GinkgoT().Error(err) - } + require.NoError(ginkgo.GinkgoT(), err, "executing load test") }) ginkgo.It("C-Chain opcoder", func(ctx context.Context) { @@ -132,9 +130,7 @@ var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { step: 50, } err = execute(ctx, network.PreFundedKeys, config, metrics, logger) - if err != nil { - ginkgo.GinkgoT().Error(err) - } + require.NoError(ginkgo.GinkgoT(), err, "executing load test") }) }) From 2aba1cd261cca783b47dd6bb19a75d5eacfa1ec3 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 22 May 2025 11:32:00 +0200 Subject: [PATCH 163/197] Remove unneeded network.Nodes[:nodesCount] --- tests/load/c/load_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/load/c/load_test.go b/tests/load/c/load_test.go index 029bfb576d4a..e3b2ee23573d 100644 --- a/tests/load/c/load_test.go +++ b/tests/load/c/load_test.go @@ -77,7 +77,6 @@ var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { registry := prometheus.NewRegistry() network = env.GetNetwork() - network.Nodes = network.Nodes[:nodesCount] loadMetrics, err := load.NewMetrics(registry) require.NoError(tc, err, "failed to register load metrics") From d3d866105954b98e25332b0e01948fbd14fd58f1 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 22 May 2025 11:35:22 +0200 Subject: [PATCH 164/197] Remove unneeded logger from metrics server --- tests/load/c/load_test.go | 2 +- tests/load/prometheus.go | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/tests/load/c/load_test.go b/tests/load/c/load_test.go index e3b2ee23573d..2488a43bfaf3 100644 --- a/tests/load/c/load_test.go +++ b/tests/load/c/load_test.go @@ -82,7 +82,7 @@ var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { require.NoError(tc, err, "failed to register load metrics") metrics = loadMetrics - metricsServer := load.NewPrometheusServer("127.0.0.1:0", registry, logger) + metricsServer := load.NewPrometheusServer("127.0.0.1:0", registry) metricsErrCh, err := metricsServer.Start() require.NoError(tc, err, "failed to start load metrics server") diff --git a/tests/load/prometheus.go b/tests/load/prometheus.go index 53859bb78200..79f03babffb6 100644 --- a/tests/load/prometheus.go +++ b/tests/load/prometheus.go @@ -18,7 +18,6 @@ import ( "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/ava-labs/avalanchego/tests/fixture/tmpnet" - "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/utils/perms" ) @@ -26,14 +25,12 @@ type MetricsServer struct { addr string registry *prometheus.Registry server http.Server - logger logging.Logger } -func NewPrometheusServer(addr string, registry *prometheus.Registry, logger logging.Logger) *MetricsServer { +func NewPrometheusServer(addr string, registry *prometheus.Registry) *MetricsServer { return &MetricsServer{ addr: addr, registry: registry, - logger: logger, } } @@ -74,8 +71,6 @@ func (s *MetricsServer) Start() (runError <-chan error, err error) { }() <-ready - s.logger.Info(fmt.Sprintf("Metrics server available at http://%s%s", listener.Addr(), metricsPattern)) - return runError, nil } From d45e40b817babaa5477ce9c18d772dfbbf665248 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 22 May 2025 11:36:27 +0200 Subject: [PATCH 165/197] Use test context logger --- tests/load/c/load_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/load/c/load_test.go b/tests/load/c/load_test.go index 2488a43bfaf3..9f4c7fc6248f 100644 --- a/tests/load/c/load_test.go +++ b/tests/load/c/load_test.go @@ -68,11 +68,12 @@ var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { network *tmpnet.Network metrics *load.Metrics - logger = logging.NewLogger("c-chain-load-testing", logging.NewWrappedCore(logging.Info, os.Stdout, logging.Auto.ConsoleEncoder())) + logger logging.Logger ) ginkgo.BeforeAll(func() { tc := e2e.NewTestContext() + logger = tc.Log() env := e2e.GetEnv(tc) registry := prometheus.NewRegistry() From e21dfbd349342477ac490886ecdfde1a20c15dc9 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 22 May 2025 11:41:48 +0200 Subject: [PATCH 166/197] Use two DeferCleanup --- tests/load/c/load_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/load/c/load_test.go b/tests/load/c/load_test.go index 9f4c7fc6248f..5b13581df6ca 100644 --- a/tests/load/c/load_test.go +++ b/tests/load/c/load_test.go @@ -97,6 +97,8 @@ var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { default: require.NoError(tc, metricsServer.Stop(), "failed to stop metrics server") } + }) + ginkgo.DeferCleanup(func() { require.NoError(tc, os.Remove(monitoringConfigFilePath), "failed to remove monitoring config file") }) }) From eeb7581eb7b335ccb0e87fb8d4096c3f36aa87c1 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 22 May 2025 11:44:00 +0200 Subject: [PATCH 167/197] Remove unused fields in c/issuers/opcode --- tests/load/c/issuers/opcode.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tests/load/c/issuers/opcode.go b/tests/load/c/issuers/opcode.go index a3b5c71d8183..0a794450c0f8 100644 --- a/tests/load/c/issuers/opcode.go +++ b/tests/load/c/issuers/opcode.go @@ -28,7 +28,6 @@ type EthClientOpcoder interface { // instance that it deploys. type Opcoder struct { // Injected parameters - client EthClientOpcoder tracker IssueTracker senderKey *ecdsa.PrivateKey maxFeeCap *big.Int @@ -36,7 +35,6 @@ type Opcoder struct { // Determined by constructor chainID *big.Int maxTipCap *big.Int - contractAddress common.Address contractInstance *contracts.EVMLoadSimulator // State @@ -67,19 +65,17 @@ func NewOpcoder( } nonce++ // deploying contract consumes one nonce - simulatorAddress, err := bind.WaitDeployed(ctx, client, simulatorDeploymentTx) + _, err = bind.WaitDeployed(ctx, client, simulatorDeploymentTx) if err != nil { return nil, fmt.Errorf("waiting for simulator contract to be mined: %w", err) } return &Opcoder{ - client: client, tracker: tracker, senderKey: key, maxFeeCap: maxFeeCap, chainID: chainID, maxTipCap: maxTipCap, - contractAddress: simulatorAddress, contractInstance: simulatorInstance, nonce: nonce, }, nil From 4c00861c2fbf1094a18b4ed909c6240df3ee0dfd Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 22 May 2025 11:48:09 +0200 Subject: [PATCH 168/197] Remove unneeded maxTipCap --- tests/load/c/issuers/opcode.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/load/c/issuers/opcode.go b/tests/load/c/issuers/opcode.go index 0a794450c0f8..3d77e47875da 100644 --- a/tests/load/c/issuers/opcode.go +++ b/tests/load/c/issuers/opcode.go @@ -34,7 +34,6 @@ type Opcoder struct { // Determined by constructor chainID *big.Int - maxTipCap *big.Int contractInstance *contracts.EVMLoadSimulator // State @@ -75,7 +74,6 @@ func NewOpcoder( senderKey: key, maxFeeCap: maxFeeCap, chainID: chainID, - maxTipCap: maxTipCap, contractInstance: simulatorInstance, nonce: nonce, }, nil @@ -169,7 +167,8 @@ func allLoadTypes() []string { } func (o *Opcoder) newTxOpts(ctx context.Context) (*bind.TransactOpts, error) { - return newTxOpts(ctx, o.senderKey, o.chainID, o.maxFeeCap, o.maxTipCap, o.nonce) + maxTipCap := big.NewInt(1) + return newTxOpts(ctx, o.senderKey, o.chainID, o.maxFeeCap, maxTipCap, o.nonce) } func newTxOpts(ctx context.Context, key *ecdsa.PrivateKey, From fd97d80b69ce92816c6c289ac2d5a6a4774e69c6 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 22 May 2025 11:50:08 +0200 Subject: [PATCH 169/197] Remove unneeded NonceAt method in client interface --- tests/load/c/issuers/simple.go | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/load/c/issuers/simple.go b/tests/load/c/issuers/simple.go index 345de4eeb7f1..e12bde9aa8fd 100644 --- a/tests/load/c/issuers/simple.go +++ b/tests/load/c/issuers/simple.go @@ -18,7 +18,6 @@ import ( type EthClientSimple interface { ChainID(ctx context.Context) (*big.Int, error) - NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) SendTransaction(ctx context.Context, tx *types.Transaction) error } From 4f229907be7e559dade2fed4e72ad947e81771bf Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 22 May 2025 12:20:06 +0200 Subject: [PATCH 170/197] Issuer picks a tx type according to a weight --- tests/load/c/issuers/opcode.go | 242 +++++++++++++++++++++------------ 1 file changed, 158 insertions(+), 84 deletions(-) diff --git a/tests/load/c/issuers/opcode.go b/tests/load/c/issuers/opcode.go index 3d77e47875da..f25954c95366 100644 --- a/tests/load/c/issuers/opcode.go +++ b/tests/load/c/issuers/opcode.go @@ -29,12 +29,10 @@ type EthClientOpcoder interface { type Opcoder struct { // Injected parameters tracker IssueTracker - senderKey *ecdsa.PrivateKey maxFeeCap *big.Int // Determined by constructor - chainID *big.Int - contractInstance *contracts.EVMLoadSimulator + txTypes []txType // State nonce uint64 @@ -70,66 +68,18 @@ func NewOpcoder( } return &Opcoder{ - tracker: tracker, - senderKey: key, - maxFeeCap: maxFeeCap, - chainID: chainID, - contractInstance: simulatorInstance, - nonce: nonce, + tracker: tracker, + txTypes: makeTxTypes(simulatorInstance, key, chainID), + nonce: nonce, }, nil } func (o *Opcoder) GenerateAndIssueTx(ctx context.Context) (common.Hash, error) { - txOpts, err := o.newTxOpts(ctx) - if err != nil { - return common.Hash{}, fmt.Errorf("creating transaction opts: %w", err) - } - - loadTypes := allLoadTypes() - loadType := loadTypes[rand.IntN(len(loadTypes))] //nolint:gosec - - var tx *types.Transaction - switch loadType { - case randomWrite: - const maxWriteSizeBytes = 5 - writeSize := big.NewInt(rand.Int64N(maxWriteSizeBytes)) //nolint:gosec - tx, err = o.contractInstance.SimulateRandomWrite(txOpts, writeSize) - case stateModification: - const maxStateSizeBytes = 5 - stateSize := big.NewInt(rand.Int64N(maxStateSizeBytes)) //nolint:gosec - tx, err = o.contractInstance.SimulateModification(txOpts, stateSize) - case randomReads: - const maxReadSizeBytes = 5 - numReads := big.NewInt(rand.Int64N(maxReadSizeBytes)) //nolint:gosec - tx, err = o.contractInstance.SimulateReads(txOpts, numReads) - case hashing: - const maxRounds = 3 - rounds := big.NewInt(rand.Int64N(maxRounds)) //nolint:gosec - tx, err = o.contractInstance.SimulateHashing(txOpts, rounds) - case memory: - const maxArraySize = 4 - arraySize := big.NewInt(rand.Int64N(maxArraySize)) //nolint:gosec - tx, err = o.contractInstance.SimulateMemory(txOpts, arraySize) - case callDepth: - const maxDepth = 5 - depth := big.NewInt(rand.Int64N(maxDepth)) //nolint:gosec - tx, err = o.contractInstance.SimulateCallDepth(txOpts, depth) - case contractCreation: - tx, err = o.contractInstance.SimulateContractCreation(txOpts) - case pureCompute: - const iterations = 100 - tx, err = o.contractInstance.SimulatePureCompute(txOpts, big.NewInt(iterations)) - case largeEvent: - const maxEventSize = 100 - tx, err = o.contractInstance.SimulateLargeEvent(txOpts, big.NewInt(maxEventSize)) - case externalCall: - tx, err = o.contractInstance.SimulateExternalCall(txOpts) - default: - return common.Hash{}, fmt.Errorf("invalid load type: %s", loadType) - } + txType := pickWeightedRandom(o.txTypes) + tx, err := txType.generateAndIssueTx(ctx, o.maxFeeCap, o.nonce) if err != nil { - return common.Hash{}, fmt.Errorf("calling simulator contract with load type %s: %w", loadType, err) + return common.Hash{}, fmt.Errorf("generating and issuing transaction of type %s: %w", txType.name, err) } o.nonce++ @@ -138,37 +88,161 @@ func (o *Opcoder) GenerateAndIssueTx(ctx context.Context) (common.Hash, error) { return txHash, err } -const ( - randomWrite = "random write" - stateModification = "state modification" - randomReads = "random reads" - hashing = "hashing" - memory = "memory" - callDepth = "call depth" - contractCreation = "contract creation" - pureCompute = "pure compute" - largeEvent = "large event" - externalCall = "external call" -) +func makeTxTypes(contractInstance *contracts.EVMLoadSimulator, senderKey *ecdsa.PrivateKey, chainID *big.Int) []txType { + return []txType{ + { + name: "random write", + weight: 1, + generateAndIssueTx: func(txCtx context.Context, maxFeeCap *big.Int, nonce uint64) (*types.Transaction, error) { + txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, big.NewInt(1), nonce) + if err != nil { + return nil, fmt.Errorf("creating transaction opts: %w", err) + } + const maxWriteSizeBytes = 5 + return contractInstance.SimulateRandomWrite(txOpts, intNBigInt(maxWriteSizeBytes)) + }, + }, + { + name: "state modification", + weight: 1, + generateAndIssueTx: func(txCtx context.Context, maxFeeCap *big.Int, nonce uint64) (*types.Transaction, error) { + txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, big.NewInt(1), nonce) + if err != nil { + return nil, fmt.Errorf("creating transaction opts: %w", err) + } + const maxStateSizeBytes = 5 + return contractInstance.SimulateModification(txOpts, intNBigInt(maxStateSizeBytes)) + }, + }, + { + name: "random read", + weight: 1, + generateAndIssueTx: func(txCtx context.Context, maxFeeCap *big.Int, nonce uint64) (*types.Transaction, error) { + txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, big.NewInt(1), nonce) + if err != nil { + return nil, fmt.Errorf("creating transaction opts: %w", err) + } + const maxReadSizeBytes = 5 + return contractInstance.SimulateReads(txOpts, intNBigInt(maxReadSizeBytes)) + }, + }, + { + name: "hashing", + weight: 1, + generateAndIssueTx: func(txCtx context.Context, maxFeeCap *big.Int, nonce uint64) (*types.Transaction, error) { + txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, big.NewInt(1), nonce) + if err != nil { + return nil, fmt.Errorf("creating transaction opts: %w", err) + } + const maxRounds = 3 + return contractInstance.SimulateHashing(txOpts, intNBigInt(maxRounds)) + }, + }, + { + name: "memory", + weight: 1, + generateAndIssueTx: func(txCtx context.Context, maxFeeCap *big.Int, nonce uint64) (*types.Transaction, error) { + txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, big.NewInt(1), nonce) + if err != nil { + return nil, fmt.Errorf("creating transaction opts: %w", err) + } + const maxArraySize = 4 + return contractInstance.SimulateMemory(txOpts, intNBigInt(maxArraySize)) + }, + }, + { + name: "call depth", + weight: 1, + generateAndIssueTx: func(txCtx context.Context, maxFeeCap *big.Int, nonce uint64) (*types.Transaction, error) { + txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, big.NewInt(1), nonce) + if err != nil { + return nil, fmt.Errorf("creating transaction opts: %w", err) + } + const maxDepth = 5 + return contractInstance.SimulateCallDepth(txOpts, intNBigInt(maxDepth)) + }, + }, + { + name: "contract creation", + weight: 1, + generateAndIssueTx: func(txCtx context.Context, maxFeeCap *big.Int, nonce uint64) (*types.Transaction, error) { + txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, big.NewInt(1), nonce) + if err != nil { + return nil, fmt.Errorf("creating transaction opts: %w", err) + } + return contractInstance.SimulateContractCreation(txOpts) + }, + }, + { + name: "pure compute", + weight: 1, + generateAndIssueTx: func(txCtx context.Context, maxFeeCap *big.Int, nonce uint64) (*types.Transaction, error) { + txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, big.NewInt(1), nonce) + if err != nil { + return nil, fmt.Errorf("creating transaction opts: %w", err) + } + const iterations = 100 + return contractInstance.SimulatePureCompute(txOpts, big.NewInt(iterations)) + }, + }, + { + name: "large event", + weight: 1, + generateAndIssueTx: func(txCtx context.Context, maxFeeCap *big.Int, nonce uint64) (*types.Transaction, error) { + txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, big.NewInt(1), nonce) + if err != nil { + return nil, fmt.Errorf("creating transaction opts: %w", err) + } + const maxEventSize = 100 + return contractInstance.SimulateLargeEvent(txOpts, big.NewInt(maxEventSize)) + }, + }, + { + name: "external call", + weight: 1, + generateAndIssueTx: func(txCtx context.Context, maxFeeCap *big.Int, nonce uint64) (*types.Transaction, error) { + txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, big.NewInt(1), nonce) + if err != nil { + return nil, fmt.Errorf("creating transaction opts: %w", err) + } + return contractInstance.SimulateExternalCall(txOpts) + }, + }, + } +} + +type txType struct { + name string // for error wrapping only + weight uint + generateAndIssueTx func(txCtx context.Context, gasFeeCap *big.Int, nonce uint64) (*types.Transaction, error) +} + +func pickWeightedRandom(txTypes []txType) txType { + var totalWeight uint + for _, txType := range txTypes { + totalWeight += txType.weight + } + + if totalWeight == 0 { + panic("Total weight cannot be zero") + } + + r := rand.UintN(totalWeight) //nolint:gosec -func allLoadTypes() []string { - return []string{ - randomWrite, - stateModification, - randomReads, - hashing, - memory, - callDepth, - contractCreation, - pureCompute, - largeEvent, - externalCall, + for _, txType := range txTypes { + if r < txType.weight { + return txType + } + r -= txType.weight } + panic("failed to pick a tx type") } -func (o *Opcoder) newTxOpts(ctx context.Context) (*bind.TransactOpts, error) { - maxTipCap := big.NewInt(1) - return newTxOpts(ctx, o.senderKey, o.chainID, o.maxFeeCap, maxTipCap, o.nonce) +func intNBigInt(n int64) *big.Int { + if n <= 0 { + panic("n must be greater than 0") + } + return big.NewInt(rand.Int64N(n)) //nolint:gosec } func newTxOpts(ctx context.Context, key *ecdsa.PrivateKey, From e7427886381ae8a921bc9c5d2ce0af65bb8abc6f Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 22 May 2025 12:35:35 +0200 Subject: [PATCH 171/197] Merge both load types together --- tests/load/c/execute.go | 32 +------ tests/load/c/{issuers/opcode.go => issuer.go} | 66 ++++++++++--- tests/load/c/issuers/simple.go | 95 ------------------- tests/load/c/load_test.go | 21 +--- tests/load/c/readme.md | 5 +- 5 files changed, 61 insertions(+), 158 deletions(-) rename tests/load/c/{issuers/opcode.go => issuer.go} (81%) delete mode 100644 tests/load/c/issuers/simple.go diff --git a/tests/load/c/execute.go b/tests/load/c/execute.go index ce17a95f3c10..d2667103c592 100644 --- a/tests/load/c/execute.go +++ b/tests/load/c/execute.go @@ -12,7 +12,6 @@ import ( "github.com/ava-labs/libevm/ethclient" "github.com/ava-labs/avalanchego/tests/load" - "github.com/ava-labs/avalanchego/tests/load/c/issuers" "github.com/ava-labs/avalanchego/tests/load/c/listener" "github.com/ava-labs/avalanchego/utils/crypto/secp256k1" "github.com/ava-labs/avalanchego/utils/logging" @@ -25,16 +24,8 @@ type loadConfig struct { minTPS int64 maxTPS int64 step int64 - issuer issuerType } -type issuerType string - -const ( - issuerSimple issuerType = "simple" - issuerOpcoder issuerType = "opcoder" -) - func execute(ctx context.Context, keys []*secp256k1.PrivateKey, config loadConfig, metrics *load.Metrics, logger logging.Logger) error { tracker := load.NewTracker[common.Hash](metrics) agents, err := createAgents(ctx, config, keys, tracker) @@ -72,7 +63,7 @@ func createAgents(ctx context.Context, config loadConfig, keys []*secp256k1.Priv key := keys[i] endpoint := config.endpoints[i%len(config.endpoints)] go func(key *secp256k1.PrivateKey, endpoint string) { - agent, err := createAgent(ctx, endpoint, key, config.issuer, tracker, config.maxFeeCap) + agent, err := createAgent(ctx, endpoint, key, tracker, config.maxFeeCap) ch <- result{agent: agent, err: err} }(key, endpoint) } @@ -101,7 +92,7 @@ func createAgents(ctx context.Context, config loadConfig, keys []*secp256k1.Priv } func createAgent(ctx context.Context, endpoint string, key *secp256k1.PrivateKey, - issuerType issuerType, tracker *load.Tracker[common.Hash], maxFeeCap int64, + tracker *load.Tracker[common.Hash], maxFeeCap int64, ) (load.Agent[common.Hash], error) { client, err := ethclient.DialContext(ctx, endpoint) if err != nil { @@ -115,27 +106,10 @@ func createAgent(ctx context.Context, endpoint string, key *secp256k1.PrivateKey return load.Agent[common.Hash]{}, fmt.Errorf("getting nonce for address %s: %w", address, err) } - issuer, err := createIssuer(ctx, issuerType, - client, tracker, maxFeeCap, nonce, key) + issuer, err := createIssuer(ctx, client, tracker, nonce, new(big.Int).SetInt64(maxFeeCap), key.ToECDSA()) if err != nil { return load.Agent[common.Hash]{}, fmt.Errorf("creating issuer: %w", err) } listener := listener.New(client, tracker, address, nonce) return load.NewAgent(issuer, listener), nil } - -func createIssuer(ctx context.Context, typ issuerType, - client *ethclient.Client, tracker *load.Tracker[common.Hash], - maxFeeCap int64, nonce uint64, key *secp256k1.PrivateKey, -) (load.Issuer[common.Hash], error) { - switch typ { - case issuerSimple: - return issuers.NewSimple(ctx, client, tracker, - nonce, big.NewInt(maxFeeCap), key.ToECDSA()) - case issuerOpcoder: - return issuers.NewOpcoder(ctx, client, tracker, - nonce, big.NewInt(maxFeeCap), key.ToECDSA()) - default: - return nil, fmt.Errorf("unknown issuer type %s", typ) - } -} diff --git a/tests/load/c/issuers/opcode.go b/tests/load/c/issuer.go similarity index 81% rename from tests/load/c/issuers/opcode.go rename to tests/load/c/issuer.go index f25954c95366..43235348cb83 100644 --- a/tests/load/c/issuers/opcode.go +++ b/tests/load/c/issuer.go @@ -1,7 +1,7 @@ // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package issuers +package c import ( "context" @@ -13,20 +13,32 @@ import ( "github.com/ava-labs/libevm/accounts/abi/bind" "github.com/ava-labs/libevm/common" "github.com/ava-labs/libevm/core/types" + "github.com/ava-labs/libevm/params" "github.com/ava-labs/avalanchego/tests/load/c/contracts" + + ethcrypto "github.com/ava-labs/libevm/crypto" ) -type EthClientOpcoder interface { - EthClientSimple +type EthClient interface { + ChainID(ctx context.Context) (*big.Int, error) + EthClientSender bind.DeployBackend bind.ContractBackend } -// Opcoder generates and issues transactions that randomly call the +type EthClientSender interface { + SendTransaction(ctx context.Context, tx *types.Transaction) error +} + +type IssueTracker interface { + Issue(tx common.Hash) +} + +// issuer generates and issues transactions that randomly call the // external functions of the [contracts.EVMLoadSimulator] contract // instance that it deploys. -type Opcoder struct { +type issuer struct { // Injected parameters tracker IssueTracker maxFeeCap *big.Int @@ -38,14 +50,14 @@ type Opcoder struct { nonce uint64 } -func NewOpcoder( +func createIssuer( ctx context.Context, - client EthClientOpcoder, + client EthClient, tracker IssueTracker, nonce uint64, maxFeeCap *big.Int, key *ecdsa.PrivateKey, -) (*Opcoder, error) { +) (*issuer, error) { chainID, err := client.ChainID(ctx) if err != nil { return nil, fmt.Errorf("getting chain id: %w", err) @@ -67,14 +79,14 @@ func NewOpcoder( return nil, fmt.Errorf("waiting for simulator contract to be mined: %w", err) } - return &Opcoder{ + return &issuer{ tracker: tracker, - txTypes: makeTxTypes(simulatorInstance, key, chainID), + txTypes: makeTxTypes(simulatorInstance, key, chainID, client), nonce: nonce, }, nil } -func (o *Opcoder) GenerateAndIssueTx(ctx context.Context) (common.Hash, error) { +func (o *issuer) GenerateAndIssueTx(ctx context.Context) (common.Hash, error) { txType := pickWeightedRandom(o.txTypes) tx, err := txType.generateAndIssueTx(ctx, o.maxFeeCap, o.nonce) @@ -88,8 +100,38 @@ func (o *Opcoder) GenerateAndIssueTx(ctx context.Context) (common.Hash, error) { return txHash, err } -func makeTxTypes(contractInstance *contracts.EVMLoadSimulator, senderKey *ecdsa.PrivateKey, chainID *big.Int) []txType { +func makeTxTypes(contractInstance *contracts.EVMLoadSimulator, senderKey *ecdsa.PrivateKey, + chainID *big.Int, client EthClientSender, +) []txType { + senderAddress := ethcrypto.PubkeyToAddress(senderKey.PublicKey) + signer := types.LatestSignerForChainID(chainID) return []txType{ + { + name: "zero self transfer", + weight: 1, + generateAndIssueTx: func(txCtx context.Context, maxFeeCap *big.Int, nonce uint64) (*types.Transaction, error) { + bigGwei := big.NewInt(params.GWei) + gasTipCap := new(big.Int).Mul(bigGwei, big.NewInt(1)) + gasFeeCap := new(big.Int).Mul(bigGwei, maxFeeCap) + tx, err := types.SignNewTx(senderKey, signer, &types.DynamicFeeTx{ + ChainID: chainID, + Nonce: nonce, + GasTipCap: gasTipCap, + GasFeeCap: gasFeeCap, + Gas: params.TxGas, + To: &senderAddress, + Data: nil, + Value: common.Big0, + }) + if err != nil { + return nil, fmt.Errorf("signing transaction: %w", err) + } + if err := client.SendTransaction(txCtx, tx); err != nil { + return nil, fmt.Errorf("issuing transaction with nonce %d: %w", nonce, err) + } + return tx, nil + }, + }, { name: "random write", weight: 1, diff --git a/tests/load/c/issuers/simple.go b/tests/load/c/issuers/simple.go deleted file mode 100644 index e12bde9aa8fd..000000000000 --- a/tests/load/c/issuers/simple.go +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package issuers - -import ( - "context" - "crypto/ecdsa" - "fmt" - "math/big" - - "github.com/ava-labs/libevm/common" - "github.com/ava-labs/libevm/core/types" - "github.com/ava-labs/libevm/params" - - ethcrypto "github.com/ava-labs/libevm/crypto" -) - -type EthClientSimple interface { - ChainID(ctx context.Context) (*big.Int, error) - SendTransaction(ctx context.Context, tx *types.Transaction) error -} - -type IssueTracker interface { - Issue(tx common.Hash) -} - -// Simple generates and issues transactions sending 0 fund to the sender. -type Simple struct { - // Injected parameters - client EthClientSimple - tracker IssueTracker - key *ecdsa.PrivateKey - gasFeeCap *big.Int - - // Determined by constructor - address common.Address // corresponding to key - signer types.Signer - chainID *big.Int - gasTipCap *big.Int - - // State - nonce uint64 -} - -func NewSimple(ctx context.Context, client EthClientSimple, tracker IssueTracker, - nonce uint64, maxFeeCap *big.Int, key *ecdsa.PrivateKey, -) (*Simple, error) { - address := ethcrypto.PubkeyToAddress(key.PublicKey) - - bigGwei := big.NewInt(params.GWei) - gasTipCap := new(big.Int).Mul(bigGwei, big.NewInt(1)) - gasFeeCap := new(big.Int).Mul(bigGwei, maxFeeCap) - - chainID, err := client.ChainID(ctx) - if err != nil { - return nil, fmt.Errorf("getting chain id: %w", err) - } - - return &Simple{ - client: client, - tracker: tracker, - key: key, - address: address, - nonce: nonce, - signer: types.LatestSignerForChainID(chainID), - chainID: chainID, - gasTipCap: gasTipCap, - gasFeeCap: gasFeeCap, - }, nil -} - -func (s *Simple) GenerateAndIssueTx(ctx context.Context) (common.Hash, error) { - tx, err := types.SignNewTx(s.key, s.signer, &types.DynamicFeeTx{ - ChainID: s.chainID, - Nonce: s.nonce, - GasTipCap: s.gasTipCap, - GasFeeCap: s.gasFeeCap, - Gas: params.TxGas, - To: &s.address, // self - Data: nil, - Value: common.Big0, - }) - if err != nil { - return common.Hash{}, err - } - - if err := s.client.SendTransaction(ctx, tx); err != nil { - return common.Hash{}, fmt.Errorf("issuing transaction with nonce %d: %w", s.nonce, err) - } - s.nonce++ - txHash := tx.Hash() - s.tracker.Issue(txHash) - return txHash, nil -} diff --git a/tests/load/c/load_test.go b/tests/load/c/load_test.go index 5b13581df6ca..ba61fea1ca9d 100644 --- a/tests/load/c/load_test.go +++ b/tests/load/c/load_test.go @@ -103,13 +103,12 @@ var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { }) }) - ginkgo.It("C-Chain simple", func(ctx context.Context) { + ginkgo.It("C-Chain", func(ctx context.Context) { endpoints, err := tmpnet.GetNodeWebsocketURIs(network.Nodes, blockchainID) require.NoError(ginkgo.GinkgoT(), err, "getting node websocket URIs") config := loadConfig{ endpoints: endpoints, - issuer: issuerSimple, - maxFeeCap: 4761904, // max fee cap equivalent to 100 ether + maxFeeCap: 300000000000, agents: agentsPerNode, minTPS: 2400, maxTPS: 3000, @@ -118,22 +117,6 @@ var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { err = execute(ctx, network.PreFundedKeys, config, metrics, logger) require.NoError(ginkgo.GinkgoT(), err, "executing load test") }) - - ginkgo.It("C-Chain opcoder", func(ctx context.Context) { - endpoints, err := tmpnet.GetNodeWebsocketURIs(network.Nodes, blockchainID) - require.NoError(ginkgo.GinkgoT(), err, "getting node websocket URIs") - config := loadConfig{ - endpoints: endpoints, - issuer: issuerOpcoder, - maxFeeCap: 300000000000, - agents: agentsCount, - minTPS: 1000, - maxTPS: 1600, - step: 50, - } - err = execute(ctx, network.PreFundedKeys, config, metrics, logger) - require.NoError(ginkgo.GinkgoT(), err, "executing load test") - }) }) // setPrefundedKeys sets the pre-funded keys for the network, and keeps diff --git a/tests/load/c/readme.md b/tests/load/c/readme.md index 16072d01c456..c6b2d18751e3 100644 --- a/tests/load/c/readme.md +++ b/tests/load/c/readme.md @@ -7,10 +7,9 @@ It runs with 5 nodes and 5 "agents". Each "agent" runs a transaction issuer and a transaction listener asynchronously, and is assigned uniformly to the nodes available, via websocket connections. -There are two load tests: +The load test picks at weighted random a transaction type to generate and issue, defined in [issuer.go](issuer.go). -1. "Simple" load test, where transactions issued are zero-fund transfers to the sender address. -2. "Complex" load test, where [this contract](contracts/EVMLoadSimulator.sol) is deployed and transactions call functions of this contract at random with random parameters. This contract has different functions, each testing a particular performance aspect of the EVM, for example memory writes. +For some transaction types, [this contract](contracts/EVMLoadSimulator.sol) is deployed and transactions call functions of this contract. This contract has different functions, each testing a particular performance aspect of the EVM, for example memory writes. From the load test perspective, only the TPS (transactions per second) is logged out. Metrics available are: From 4ce935916c226527bce936bcc4f2e1133f444dc7 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 22 May 2025 12:38:17 +0200 Subject: [PATCH 172/197] chore: newTxOpts always set max tip cap to 1 --- tests/load/c/issuer.go | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/tests/load/c/issuer.go b/tests/load/c/issuer.go index 43235348cb83..a22bbb55748b 100644 --- a/tests/load/c/issuer.go +++ b/tests/load/c/issuer.go @@ -63,8 +63,7 @@ func createIssuer( return nil, fmt.Errorf("getting chain id: %w", err) } - maxTipCap := big.NewInt(1) - txOpts, err := newTxOpts(ctx, key, chainID, maxFeeCap, maxTipCap, nonce) + txOpts, err := newTxOpts(ctx, key, chainID, maxFeeCap, nonce) if err != nil { return nil, fmt.Errorf("creating transaction opts: %w", err) } @@ -136,7 +135,7 @@ func makeTxTypes(contractInstance *contracts.EVMLoadSimulator, senderKey *ecdsa. name: "random write", weight: 1, generateAndIssueTx: func(txCtx context.Context, maxFeeCap *big.Int, nonce uint64) (*types.Transaction, error) { - txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, big.NewInt(1), nonce) + txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, nonce) if err != nil { return nil, fmt.Errorf("creating transaction opts: %w", err) } @@ -148,7 +147,7 @@ func makeTxTypes(contractInstance *contracts.EVMLoadSimulator, senderKey *ecdsa. name: "state modification", weight: 1, generateAndIssueTx: func(txCtx context.Context, maxFeeCap *big.Int, nonce uint64) (*types.Transaction, error) { - txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, big.NewInt(1), nonce) + txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, nonce) if err != nil { return nil, fmt.Errorf("creating transaction opts: %w", err) } @@ -160,7 +159,7 @@ func makeTxTypes(contractInstance *contracts.EVMLoadSimulator, senderKey *ecdsa. name: "random read", weight: 1, generateAndIssueTx: func(txCtx context.Context, maxFeeCap *big.Int, nonce uint64) (*types.Transaction, error) { - txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, big.NewInt(1), nonce) + txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, nonce) if err != nil { return nil, fmt.Errorf("creating transaction opts: %w", err) } @@ -172,7 +171,7 @@ func makeTxTypes(contractInstance *contracts.EVMLoadSimulator, senderKey *ecdsa. name: "hashing", weight: 1, generateAndIssueTx: func(txCtx context.Context, maxFeeCap *big.Int, nonce uint64) (*types.Transaction, error) { - txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, big.NewInt(1), nonce) + txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, nonce) if err != nil { return nil, fmt.Errorf("creating transaction opts: %w", err) } @@ -184,7 +183,7 @@ func makeTxTypes(contractInstance *contracts.EVMLoadSimulator, senderKey *ecdsa. name: "memory", weight: 1, generateAndIssueTx: func(txCtx context.Context, maxFeeCap *big.Int, nonce uint64) (*types.Transaction, error) { - txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, big.NewInt(1), nonce) + txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, nonce) if err != nil { return nil, fmt.Errorf("creating transaction opts: %w", err) } @@ -196,7 +195,7 @@ func makeTxTypes(contractInstance *contracts.EVMLoadSimulator, senderKey *ecdsa. name: "call depth", weight: 1, generateAndIssueTx: func(txCtx context.Context, maxFeeCap *big.Int, nonce uint64) (*types.Transaction, error) { - txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, big.NewInt(1), nonce) + txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, nonce) if err != nil { return nil, fmt.Errorf("creating transaction opts: %w", err) } @@ -208,7 +207,7 @@ func makeTxTypes(contractInstance *contracts.EVMLoadSimulator, senderKey *ecdsa. name: "contract creation", weight: 1, generateAndIssueTx: func(txCtx context.Context, maxFeeCap *big.Int, nonce uint64) (*types.Transaction, error) { - txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, big.NewInt(1), nonce) + txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, nonce) if err != nil { return nil, fmt.Errorf("creating transaction opts: %w", err) } @@ -219,7 +218,7 @@ func makeTxTypes(contractInstance *contracts.EVMLoadSimulator, senderKey *ecdsa. name: "pure compute", weight: 1, generateAndIssueTx: func(txCtx context.Context, maxFeeCap *big.Int, nonce uint64) (*types.Transaction, error) { - txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, big.NewInt(1), nonce) + txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, nonce) if err != nil { return nil, fmt.Errorf("creating transaction opts: %w", err) } @@ -231,7 +230,7 @@ func makeTxTypes(contractInstance *contracts.EVMLoadSimulator, senderKey *ecdsa. name: "large event", weight: 1, generateAndIssueTx: func(txCtx context.Context, maxFeeCap *big.Int, nonce uint64) (*types.Transaction, error) { - txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, big.NewInt(1), nonce) + txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, nonce) if err != nil { return nil, fmt.Errorf("creating transaction opts: %w", err) } @@ -243,7 +242,7 @@ func makeTxTypes(contractInstance *contracts.EVMLoadSimulator, senderKey *ecdsa. name: "external call", weight: 1, generateAndIssueTx: func(txCtx context.Context, maxFeeCap *big.Int, nonce uint64) (*types.Transaction, error) { - txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, big.NewInt(1), nonce) + txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, nonce) if err != nil { return nil, fmt.Errorf("creating transaction opts: %w", err) } @@ -288,7 +287,7 @@ func intNBigInt(n int64) *big.Int { } func newTxOpts(ctx context.Context, key *ecdsa.PrivateKey, - chainID, maxFeeCap, maxTipCap *big.Int, nonce uint64, + chainID, maxFeeCap *big.Int, nonce uint64, ) (*bind.TransactOpts, error) { txOpts, err := bind.NewKeyedTransactorWithChainID(key, chainID) if err != nil { @@ -296,7 +295,7 @@ func newTxOpts(ctx context.Context, key *ecdsa.PrivateKey, } txOpts.Nonce = new(big.Int).SetUint64(nonce) txOpts.GasFeeCap = maxFeeCap - txOpts.GasTipCap = maxTipCap + txOpts.GasTipCap = common.Big1 txOpts.Context = ctx return txOpts, nil } From 57360258648a2f9574d45bf41799b1fb6a1891fe Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 22 May 2025 12:40:58 +0200 Subject: [PATCH 173/197] Max fee cap set per transaction type in issuer --- tests/load/c/execute.go | 7 ++--- tests/load/c/issuer.go | 63 +++++++++++++++++++++++---------------- tests/load/c/load_test.go | 1 - 3 files changed, 40 insertions(+), 31 deletions(-) diff --git a/tests/load/c/execute.go b/tests/load/c/execute.go index d2667103c592..56ee87778240 100644 --- a/tests/load/c/execute.go +++ b/tests/load/c/execute.go @@ -19,7 +19,6 @@ import ( type loadConfig struct { endpoints []string - maxFeeCap int64 agents uint minTPS int64 maxTPS int64 @@ -63,7 +62,7 @@ func createAgents(ctx context.Context, config loadConfig, keys []*secp256k1.Priv key := keys[i] endpoint := config.endpoints[i%len(config.endpoints)] go func(key *secp256k1.PrivateKey, endpoint string) { - agent, err := createAgent(ctx, endpoint, key, tracker, config.maxFeeCap) + agent, err := createAgent(ctx, endpoint, key, tracker) ch <- result{agent: agent, err: err} }(key, endpoint) } @@ -92,7 +91,7 @@ func createAgents(ctx context.Context, config loadConfig, keys []*secp256k1.Priv } func createAgent(ctx context.Context, endpoint string, key *secp256k1.PrivateKey, - tracker *load.Tracker[common.Hash], maxFeeCap int64, + tracker *load.Tracker[common.Hash], ) (load.Agent[common.Hash], error) { client, err := ethclient.DialContext(ctx, endpoint) if err != nil { @@ -106,7 +105,7 @@ func createAgent(ctx context.Context, endpoint string, key *secp256k1.PrivateKey return load.Agent[common.Hash]{}, fmt.Errorf("getting nonce for address %s: %w", address, err) } - issuer, err := createIssuer(ctx, client, tracker, nonce, new(big.Int).SetInt64(maxFeeCap), key.ToECDSA()) + issuer, err := createIssuer(ctx, client, tracker, nonce, key.ToECDSA()) if err != nil { return load.Agent[common.Hash]{}, fmt.Errorf("creating issuer: %w", err) } diff --git a/tests/load/c/issuer.go b/tests/load/c/issuer.go index a22bbb55748b..d8a87d4ba1d4 100644 --- a/tests/load/c/issuer.go +++ b/tests/load/c/issuer.go @@ -40,8 +40,7 @@ type IssueTracker interface { // instance that it deploys. type issuer struct { // Injected parameters - tracker IssueTracker - maxFeeCap *big.Int + tracker IssueTracker // Determined by constructor txTypes []txType @@ -55,7 +54,6 @@ func createIssuer( client EthClient, tracker IssueTracker, nonce uint64, - maxFeeCap *big.Int, key *ecdsa.PrivateKey, ) (*issuer, error) { chainID, err := client.ChainID(ctx) @@ -63,6 +61,7 @@ func createIssuer( return nil, fmt.Errorf("getting chain id: %w", err) } + maxFeeCap := big.NewInt(300000000000) // enough for contract deployment in parallel txOpts, err := newTxOpts(ctx, key, chainID, maxFeeCap, nonce) if err != nil { return nil, fmt.Errorf("creating transaction opts: %w", err) @@ -88,7 +87,7 @@ func createIssuer( func (o *issuer) GenerateAndIssueTx(ctx context.Context) (common.Hash, error) { txType := pickWeightedRandom(o.txTypes) - tx, err := txType.generateAndIssueTx(ctx, o.maxFeeCap, o.nonce) + tx, err := txType.generateAndIssueTx(ctx, txType.maxFeeCap, o.nonce) if err != nil { return common.Hash{}, fmt.Errorf("generating and issuing transaction of type %s: %w", txType.name, err) } @@ -106,8 +105,9 @@ func makeTxTypes(contractInstance *contracts.EVMLoadSimulator, senderKey *ecdsa. signer := types.LatestSignerForChainID(chainID) return []txType{ { - name: "zero self transfer", - weight: 1, + name: "zero self transfer", + weight: 1, + maxFeeCap: big.NewInt(300000000000), generateAndIssueTx: func(txCtx context.Context, maxFeeCap *big.Int, nonce uint64) (*types.Transaction, error) { bigGwei := big.NewInt(params.GWei) gasTipCap := new(big.Int).Mul(bigGwei, big.NewInt(1)) @@ -132,8 +132,9 @@ func makeTxTypes(contractInstance *contracts.EVMLoadSimulator, senderKey *ecdsa. }, }, { - name: "random write", - weight: 1, + name: "random write", + weight: 1, + maxFeeCap: big.NewInt(300000000000), generateAndIssueTx: func(txCtx context.Context, maxFeeCap *big.Int, nonce uint64) (*types.Transaction, error) { txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, nonce) if err != nil { @@ -144,8 +145,9 @@ func makeTxTypes(contractInstance *contracts.EVMLoadSimulator, senderKey *ecdsa. }, }, { - name: "state modification", - weight: 1, + name: "state modification", + weight: 1, + maxFeeCap: big.NewInt(300000000000), generateAndIssueTx: func(txCtx context.Context, maxFeeCap *big.Int, nonce uint64) (*types.Transaction, error) { txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, nonce) if err != nil { @@ -156,8 +158,9 @@ func makeTxTypes(contractInstance *contracts.EVMLoadSimulator, senderKey *ecdsa. }, }, { - name: "random read", - weight: 1, + name: "random read", + weight: 1, + maxFeeCap: big.NewInt(300000000000), generateAndIssueTx: func(txCtx context.Context, maxFeeCap *big.Int, nonce uint64) (*types.Transaction, error) { txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, nonce) if err != nil { @@ -168,8 +171,9 @@ func makeTxTypes(contractInstance *contracts.EVMLoadSimulator, senderKey *ecdsa. }, }, { - name: "hashing", - weight: 1, + name: "hashing", + weight: 1, + maxFeeCap: big.NewInt(300000000000), generateAndIssueTx: func(txCtx context.Context, maxFeeCap *big.Int, nonce uint64) (*types.Transaction, error) { txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, nonce) if err != nil { @@ -180,8 +184,9 @@ func makeTxTypes(contractInstance *contracts.EVMLoadSimulator, senderKey *ecdsa. }, }, { - name: "memory", - weight: 1, + name: "memory", + weight: 1, + maxFeeCap: big.NewInt(300000000000), generateAndIssueTx: func(txCtx context.Context, maxFeeCap *big.Int, nonce uint64) (*types.Transaction, error) { txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, nonce) if err != nil { @@ -192,8 +197,9 @@ func makeTxTypes(contractInstance *contracts.EVMLoadSimulator, senderKey *ecdsa. }, }, { - name: "call depth", - weight: 1, + name: "call depth", + weight: 1, + maxFeeCap: big.NewInt(300000000000), generateAndIssueTx: func(txCtx context.Context, maxFeeCap *big.Int, nonce uint64) (*types.Transaction, error) { txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, nonce) if err != nil { @@ -204,8 +210,9 @@ func makeTxTypes(contractInstance *contracts.EVMLoadSimulator, senderKey *ecdsa. }, }, { - name: "contract creation", - weight: 1, + name: "contract creation", + weight: 1, + maxFeeCap: big.NewInt(300000000000), generateAndIssueTx: func(txCtx context.Context, maxFeeCap *big.Int, nonce uint64) (*types.Transaction, error) { txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, nonce) if err != nil { @@ -215,8 +222,9 @@ func makeTxTypes(contractInstance *contracts.EVMLoadSimulator, senderKey *ecdsa. }, }, { - name: "pure compute", - weight: 1, + name: "pure compute", + weight: 1, + maxFeeCap: big.NewInt(300000000000), generateAndIssueTx: func(txCtx context.Context, maxFeeCap *big.Int, nonce uint64) (*types.Transaction, error) { txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, nonce) if err != nil { @@ -227,8 +235,9 @@ func makeTxTypes(contractInstance *contracts.EVMLoadSimulator, senderKey *ecdsa. }, }, { - name: "large event", - weight: 1, + name: "large event", + weight: 1, + maxFeeCap: big.NewInt(300000000000), generateAndIssueTx: func(txCtx context.Context, maxFeeCap *big.Int, nonce uint64) (*types.Transaction, error) { txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, nonce) if err != nil { @@ -239,8 +248,9 @@ func makeTxTypes(contractInstance *contracts.EVMLoadSimulator, senderKey *ecdsa. }, }, { - name: "external call", - weight: 1, + name: "external call", + weight: 1, + maxFeeCap: big.NewInt(300000000000), generateAndIssueTx: func(txCtx context.Context, maxFeeCap *big.Int, nonce uint64) (*types.Transaction, error) { txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, nonce) if err != nil { @@ -255,6 +265,7 @@ func makeTxTypes(contractInstance *contracts.EVMLoadSimulator, senderKey *ecdsa. type txType struct { name string // for error wrapping only weight uint + maxFeeCap *big.Int generateAndIssueTx func(txCtx context.Context, gasFeeCap *big.Int, nonce uint64) (*types.Transaction, error) } diff --git a/tests/load/c/load_test.go b/tests/load/c/load_test.go index ba61fea1ca9d..5a1598b04731 100644 --- a/tests/load/c/load_test.go +++ b/tests/load/c/load_test.go @@ -108,7 +108,6 @@ var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { require.NoError(ginkgo.GinkgoT(), err, "getting node websocket URIs") config := loadConfig{ endpoints: endpoints, - maxFeeCap: 300000000000, agents: agentsPerNode, minTPS: 2400, maxTPS: 3000, From 7e6af872529f0beb48ca66b4395d439f5ee60f96 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 22 May 2025 12:42:33 +0200 Subject: [PATCH 174/197] Adjust max fee cap for zero self transfers --- tests/load/c/issuer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/load/c/issuer.go b/tests/load/c/issuer.go index d8a87d4ba1d4..c968f56ed070 100644 --- a/tests/load/c/issuer.go +++ b/tests/load/c/issuer.go @@ -107,7 +107,7 @@ func makeTxTypes(contractInstance *contracts.EVMLoadSimulator, senderKey *ecdsa. { name: "zero self transfer", weight: 1, - maxFeeCap: big.NewInt(300000000000), + maxFeeCap: big.NewInt(4761904), // equiavelent to 100 ETH which is the maximum value generateAndIssueTx: func(txCtx context.Context, maxFeeCap *big.Int, nonce uint64) (*types.Transaction, error) { bigGwei := big.NewInt(params.GWei) gasTipCap := new(big.Int).Mul(bigGwei, big.NewInt(1)) From 177cbbeea666bae4c38d76f870a1b431a1d25f34 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 22 May 2025 12:44:00 +0200 Subject: [PATCH 175/197] Adjust weights to lower contract deployment --- tests/load/c/issuer.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/load/c/issuer.go b/tests/load/c/issuer.go index c968f56ed070..2a7247d0b637 100644 --- a/tests/load/c/issuer.go +++ b/tests/load/c/issuer.go @@ -106,7 +106,7 @@ func makeTxTypes(contractInstance *contracts.EVMLoadSimulator, senderKey *ecdsa. return []txType{ { name: "zero self transfer", - weight: 1, + weight: 1000, maxFeeCap: big.NewInt(4761904), // equiavelent to 100 ETH which is the maximum value generateAndIssueTx: func(txCtx context.Context, maxFeeCap *big.Int, nonce uint64) (*types.Transaction, error) { bigGwei := big.NewInt(params.GWei) @@ -133,7 +133,7 @@ func makeTxTypes(contractInstance *contracts.EVMLoadSimulator, senderKey *ecdsa. }, { name: "random write", - weight: 1, + weight: 100, maxFeeCap: big.NewInt(300000000000), generateAndIssueTx: func(txCtx context.Context, maxFeeCap *big.Int, nonce uint64) (*types.Transaction, error) { txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, nonce) @@ -146,7 +146,7 @@ func makeTxTypes(contractInstance *contracts.EVMLoadSimulator, senderKey *ecdsa. }, { name: "state modification", - weight: 1, + weight: 100, maxFeeCap: big.NewInt(300000000000), generateAndIssueTx: func(txCtx context.Context, maxFeeCap *big.Int, nonce uint64) (*types.Transaction, error) { txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, nonce) @@ -159,7 +159,7 @@ func makeTxTypes(contractInstance *contracts.EVMLoadSimulator, senderKey *ecdsa. }, { name: "random read", - weight: 1, + weight: 200, maxFeeCap: big.NewInt(300000000000), generateAndIssueTx: func(txCtx context.Context, maxFeeCap *big.Int, nonce uint64) (*types.Transaction, error) { txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, nonce) @@ -172,7 +172,7 @@ func makeTxTypes(contractInstance *contracts.EVMLoadSimulator, senderKey *ecdsa. }, { name: "hashing", - weight: 1, + weight: 50, maxFeeCap: big.NewInt(300000000000), generateAndIssueTx: func(txCtx context.Context, maxFeeCap *big.Int, nonce uint64) (*types.Transaction, error) { txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, nonce) @@ -185,7 +185,7 @@ func makeTxTypes(contractInstance *contracts.EVMLoadSimulator, senderKey *ecdsa. }, { name: "memory", - weight: 1, + weight: 100, maxFeeCap: big.NewInt(300000000000), generateAndIssueTx: func(txCtx context.Context, maxFeeCap *big.Int, nonce uint64) (*types.Transaction, error) { txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, nonce) @@ -198,7 +198,7 @@ func makeTxTypes(contractInstance *contracts.EVMLoadSimulator, senderKey *ecdsa. }, { name: "call depth", - weight: 1, + weight: 50, maxFeeCap: big.NewInt(300000000000), generateAndIssueTx: func(txCtx context.Context, maxFeeCap *big.Int, nonce uint64) (*types.Transaction, error) { txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, nonce) @@ -223,7 +223,7 @@ func makeTxTypes(contractInstance *contracts.EVMLoadSimulator, senderKey *ecdsa. }, { name: "pure compute", - weight: 1, + weight: 100, maxFeeCap: big.NewInt(300000000000), generateAndIssueTx: func(txCtx context.Context, maxFeeCap *big.Int, nonce uint64) (*types.Transaction, error) { txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, nonce) @@ -236,7 +236,7 @@ func makeTxTypes(contractInstance *contracts.EVMLoadSimulator, senderKey *ecdsa. }, { name: "large event", - weight: 1, + weight: 100, maxFeeCap: big.NewInt(300000000000), generateAndIssueTx: func(txCtx context.Context, maxFeeCap *big.Int, nonce uint64) (*types.Transaction, error) { txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, nonce) @@ -249,7 +249,7 @@ func makeTxTypes(contractInstance *contracts.EVMLoadSimulator, senderKey *ecdsa. }, { name: "external call", - weight: 1, + weight: 50, maxFeeCap: big.NewInt(300000000000), generateAndIssueTx: func(txCtx context.Context, maxFeeCap *big.Int, nonce uint64) (*types.Transaction, error) { txOpts, err := newTxOpts(txCtx, senderKey, chainID, maxFeeCap, nonce) From 4d61ab1f8691eb21432d21b0237de2ad3bdb2791 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 22 May 2025 12:47:34 +0200 Subject: [PATCH 176/197] Fix pinning of libevm/cmd/abigen --- go.mod | 11 +++++++++++ go.sum | 46 +++++++++++++++++++++++++++++++++++++++++++++- tools.go | 1 + 3 files changed, 57 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 991651d2f5a2..eef511488ddd 100644 --- a/go.mod +++ b/go.mod @@ -101,6 +101,7 @@ require ( github.com/crate-crypto/go-kzg-4844 v0.7.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/deckarep/golang-set/v2 v2.1.0 // indirect + github.com/deepmap/oapi-codegen v1.6.0 // indirect github.com/distribution/reference v0.5.0 // indirect github.com/dlclark/regexp2 v1.7.0 // indirect github.com/docker/go-connections v0.4.0 // indirect @@ -109,6 +110,7 @@ require ( github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/ethereum/c-kzg-4844 v0.4.0 // indirect github.com/fatih/structtag v1.2.0 // indirect + github.com/ferranbt/fastssz v0.1.2 // indirect github.com/frankban/quicktest v1.14.4 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect @@ -124,12 +126,14 @@ require ( github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/gofrs/flock v0.8.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect github.com/gorilla/websocket v1.5.0 // indirect + github.com/graph-gophers/graphql-go v1.3.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect github.com/hashicorp/go-bexpr v0.1.10 // indirect github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect @@ -138,9 +142,13 @@ require ( github.com/holiman/bloomfilter/v2 v2.0.3 // indirect github.com/imdario/mergo v0.3.16 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/influxdata/influxdb-client-go/v2 v2.4.0 // indirect + github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c // indirect + github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.15.15 // indirect + github.com/klauspost/cpuid/v2 v2.0.9 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/magiconair/properties v1.8.6 // indirect @@ -150,6 +158,7 @@ require ( github.com/mattn/go-runewidth v0.0.13 // indirect github.com/mattn/go-shellwords v1.0.12 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/minio/sha256-simd v1.0.0 // indirect github.com/mitchellh/pointerstructure v1.2.0 // indirect github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/moby/spdystream v0.2.0 // indirect @@ -159,8 +168,10 @@ require ( github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opentracing/opentracing-go v1.1.0 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.0.5 // indirect + github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/procfs v0.10.1 // indirect diff --git a/go.sum b/go.sum index d75c18515daf..be832ebb6ada 100644 --- a/go.sum +++ b/go.sum @@ -155,6 +155,7 @@ github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233/go.mod h1:geZJ github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA= github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -168,7 +169,10 @@ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeC github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc= github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= +github.com/deepmap/oapi-codegen v1.6.0 h1:w/d1ntwh91XI0b/8ja7+u5SvA4IFfM0UNNLmiDR1gg0= +github.com/deepmap/oapi-codegen v1.6.0/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M= github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= @@ -202,6 +206,8 @@ github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4= github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= +github.com/ferranbt/fastssz v0.1.2 h1:Dky6dXlngF6Qjc+EfDipAkE83N5I5DE68bY6O0VLNPk= +github.com/ferranbt/fastssz v0.1.2/go.mod h1:X5UPrE2u1UJjxHA8X54u04SBwdAQjG2sFtWs39YxyWs= github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -214,12 +220,15 @@ github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 h1:f6D9Hr8x github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 h1:BAIP2GihuqhwdILrV+7GJel5lyPV3u1+PgzrWLc0TkE= github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46/go.mod h1:QNpY22eby74jVhqH4WhDLDwxc/vqsern6pW+u2kbkpc= +github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= github.com/getsentry/sentry-go v0.12.0/go.mod h1:NSap0JBYWzHND8oMbyi0+XZhUalc1TBdRL1M71JZW2c= github.com/getsentry/sentry-go v0.18.0 h1:MtBW5H9QgdcJabtZcuJG80BMOwaBpkRDZkxRkNC1sN0= github.com/getsentry/sentry-go v0.18.0/go.mod h1:Kgon4Mby+FJ7ZWHFUAZgVaIa8sxHtnRJRLTXZr51aKQ= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= +github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= @@ -236,10 +245,12 @@ github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AE github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU= @@ -258,7 +269,6 @@ github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM= -github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= @@ -296,6 +306,7 @@ github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6 github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y= github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -355,6 +366,8 @@ github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/graph-gophers/graphql-go v1.3.0 h1:Eb9x/q6MFpCLz7jBCiP/WTxjSDrYLR1QY41SORZyNJ0= +github.com/graph-gophers/graphql-go v1.3.0/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= @@ -387,6 +400,12 @@ github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/C github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/influxdata/influxdb-client-go/v2 v2.4.0 h1:HGBfZYStlx3Kqvsv1h2pJixbCl/jhnFtxpKFAv9Tu5k= +github.com/influxdata/influxdb-client-go/v2 v2.4.0/go.mod h1:vLNHdxTJkIf2mSLvGrpj8TCcISApPoXkaxP8g9uRlW8= +github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c h1:qSHzRbhzK8RdXOsAdfDgO49TtqC1oZ+acxPrkfTxcCs= +github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 h1:W9WBk7wlPfJLvMCdtV4zPulc4uCPrlywQOmbFOhgQNU= +github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI= github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0= github.com/iris-contrib/jade v1.1.3/go.mod h1:H/geBymxJhShH5kecoiOCSssPX7QWYH7UaeZTSWddIk= @@ -424,6 +443,9 @@ github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0 github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw= github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -436,6 +458,7 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg= github.com/labstack/echo/v4 v4.5.0/go.mod h1:czIriw4a0C1dFun+ObrXp7ok03xON0N1awStJ6ArI7Y= github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= @@ -443,9 +466,13 @@ github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2 github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= @@ -458,6 +485,7 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= @@ -468,6 +496,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zk github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8= github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= +github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= +github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= @@ -528,11 +558,15 @@ github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg= github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg= github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= +github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 h1:oYW+YCJ1pachXTQmzR3rNLYGGz4g/UgFcjb28p/viDM= +github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pires/go-proxyproto v0.6.2 h1:KAZ7UteSOt6urjme6ZldyFm4wDe/z0ZUP0Yv0Dos0d8= @@ -554,6 +588,8 @@ github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/prysmaticlabs/gohashtree v0.0.1-alpha.0.20220714111606-acbb2962fb48 h1:cSo6/vk8YpvkLbk9v3FO97cakNmUoxwi2KMP8hd5WIw= +github.com/prysmaticlabs/gohashtree v0.0.1-alpha.0.20220714111606-acbb2962fb48/go.mod h1:4pWaT30XoEx1j8KNJf3TV+E3mQkaufn7mf+jRNb/Fuk= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -707,6 +743,8 @@ golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= @@ -790,6 +828,7 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= @@ -842,6 +881,7 @@ golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -861,6 +901,7 @@ golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -868,6 +909,7 @@ golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -893,6 +935,7 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= @@ -913,6 +956,7 @@ golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/tools.go b/tools.go index f6b9754a6fd1..bff79d71c340 100644 --- a/tools.go +++ b/tools.go @@ -5,5 +5,6 @@ package avalanchego import ( _ "github.com/StephenButtolph/canoto/generate" + _ "github.com/ava-labs/libevm/cmd/abigen" _ "golang.org/x/mod/semver" // golang.org/x/mod to satisfy requirement for go.uber.org/mock/mockgen@v0.5 ) From 75a659abab7415c90e04d90b4bf6c3f2ac59476e Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 22 May 2025 13:06:04 +0200 Subject: [PATCH 177/197] More conservative min and max TPS --- tests/load/c/load_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/load/c/load_test.go b/tests/load/c/load_test.go index 5a1598b04731..a3d9a93c84d0 100644 --- a/tests/load/c/load_test.go +++ b/tests/load/c/load_test.go @@ -109,8 +109,8 @@ var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { config := loadConfig{ endpoints: endpoints, agents: agentsPerNode, - minTPS: 2400, - maxTPS: 3000, + minTPS: 1000, + maxTPS: 1700, step: 100, } err = execute(ctx, network.PreFundedKeys, config, metrics, logger) From 72c67d158895cf7ce25c7f50965b2da39032003e Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 22 May 2025 13:07:22 +0200 Subject: [PATCH 178/197] Add `test-load-local` task for local monitoring --- Taskfile.yml | 9 ++++++++- tests/load/c/readme.md | 2 ++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Taskfile.yml b/Taskfile.yml index 914b134eb1f6..98e69f05c7d4 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -211,12 +211,19 @@ tasks: cmd: ./scripts/build_fuzz.sh {{.FUZZTIME}} ./x/merkledb test-load: - desc: Runs load tests + desc: Runs load tests with remote monitoring cmds: - task: generate-load-contract-bindings - task: build - cmd: ./bin/ginkgo -v ./tests/load/c -- --avalanchego-path=$PWD/build/avalanchego --start-metrics-collector --start-logs-collector + test-load-local: + desc: Runs load tests with local monitoring + cmds: + - task: generate-load-contract-bindings + - task: build + - cmd: ./bin/ginkgo -v ./tests/load/c -- --avalanchego-path=$PWD/build/avalanchego + test-unit: desc: Runs unit tests # Invoking with bash ensures compatibility with CI execution on Windows diff --git a/tests/load/c/readme.md b/tests/load/c/readme.md index c6b2d18751e3..9b2871f407e7 100644 --- a/tests/load/c/readme.md +++ b/tests/load/c/readme.md @@ -27,6 +27,8 @@ Finally, to run the load test, run: nix develop # Start the load test task test-load +# If you don't have access to the Ava Labs CI monitoring stack, use: +task test-load-local ``` ## Visualize metrics in Grafana From d9ea754482d1d2e1d788060a9c0857c7db0efcf9 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 22 May 2025 13:12:31 +0200 Subject: [PATCH 179/197] Move body of SynchronizedBeforeSuite to BeforeAll --- tests/load/c/load_test.go | 41 +++++++++++++-------------------------- 1 file changed, 14 insertions(+), 27 deletions(-) diff --git a/tests/load/c/load_test.go b/tests/load/c/load_test.go index a3d9a93c84d0..f38b92acdc9d 100644 --- a/tests/load/c/load_test.go +++ b/tests/load/c/load_test.go @@ -37,32 +37,6 @@ const ( agentsCount = nodesCount * agentsPerNode ) -var _ = ginkgo.SynchronizedBeforeSuite(func() []byte { - // Run only once in the first ginkgo process - - require.GreaterOrEqual(ginkgo.GinkgoT(), nodesCount, 5, "number of nodes must be at least 5") - tc := e2e.NewTestContext() - nodes := tmpnet.NewNodesOrPanic(nodesCount) - network := &tmpnet.Network{ - Owner: "avalanchego-load-test", - Nodes: nodes, - } - setPrefundedKeys(tc, network, agentsCount) - - env := e2e.NewTestEnvironment( - tc, - flagVars, - network, - ) - - return env.Marshal() -}, func(envBytes []byte) { - // Run in every ginkgo process - - // Initialize the local test environment from the global state - e2e.InitSharedTestEnvironment(e2e.NewTestContext(), envBytes) -}) - var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { var ( network *tmpnet.Network @@ -72,9 +46,22 @@ var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { ) ginkgo.BeforeAll(func() { + require.GreaterOrEqual(ginkgo.GinkgoT(), nodesCount, 5, "number of nodes must be at least 5") tc := e2e.NewTestContext() + nodes := tmpnet.NewNodesOrPanic(nodesCount) + network = &tmpnet.Network{ + Owner: "avalanchego-load-test", + Nodes: nodes, + } + setPrefundedKeys(tc, network, agentsCount) + + env := e2e.NewTestEnvironment( + tc, + flagVars, + network, + ) + logger = tc.Log() - env := e2e.GetEnv(tc) registry := prometheus.NewRegistry() network = env.GetNetwork() From 3952c3aa75396773fa140af186f76f658f3465df Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 22 May 2025 13:15:48 +0200 Subject: [PATCH 180/197] Revert unrelated auto-formatting diffs --- .github/workflows/ci.yml | 10 +--------- Taskfile.yml | 2 +- tests/load/README.md | 1 + 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9ea87c524f7f..7c73db498a31 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,15 +25,7 @@ jobs: strategy: fail-fast: false matrix: - os: - [ - macos-14, - ubuntu-22.04, - ubuntu-24.04, - windows-2022, - custom-arm64-jammy, - custom-arm64-noble, - ] + os: [macos-14, ubuntu-22.04, ubuntu-24.04, windows-2022, custom-arm64-jammy, custom-arm64-noble] steps: - uses: actions/checkout@v4 - uses: ./.github/actions/setup-go-for-project diff --git a/Taskfile.yml b/Taskfile.yml index 98e69f05c7d4..84fc99bff57b 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -2,7 +2,7 @@ # To run on a system without task installed, `./scripts/run_task.sh` will execute it with `go run`. # If in the nix dev shell, `task` is available. -version: "3" +version: '3' tasks: default: ./scripts/run_task.sh --list diff --git a/tests/load/README.md b/tests/load/README.md index 83e3a20f0c34..431df20ec342 100644 --- a/tests/load/README.md +++ b/tests/load/README.md @@ -113,3 +113,4 @@ for { iters += 1 } ``` + From 5c6221bb014d0697d9f8949cb04503fd9f693333 Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 22 May 2025 13:19:43 +0200 Subject: [PATCH 181/197] Update local monitoring instructions to not pollute the source tree --- tests/load/c/.gitignore | 2 -- tests/load/c/readme.md | 10 ++++------ 2 files changed, 4 insertions(+), 8 deletions(-) delete mode 100644 tests/load/c/.gitignore diff --git a/tests/load/c/.gitignore b/tests/load/c/.gitignore deleted file mode 100644 index e5993540470e..000000000000 --- a/tests/load/c/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -prometheus.yml -/data diff --git a/tests/load/c/readme.md b/tests/load/c/readme.md index 9b2871f407e7..785d9aa61566 100644 --- a/tests/load/c/readme.md +++ b/tests/load/c/readme.md @@ -70,16 +70,14 @@ For reference, see [the tmpnet monitoring section](../../fixture/tmpnet/README.m ### Locally -1. Navigate to this directory with `cd tests/load/c`. -1. Setup the Prometheus configuration file: `envsubst < prometheus.template.yml > prometheus.yml` +1. Create a directory `/yourpath` to store the Prometheus configuration file and its data. +1. Setup the Prometheus configuration file: `envsubst < tests/load/c/prometheus.template.yml > /yourpath/prometheus.yml` 1. Launch Prometheus using the dev shell: ```bash nix develop - ``` - - ```nix - prometheus --config.file prometheus.yml + cd /yourpath + prometheus --config.file /yourpath/prometheus.yml ``` This starts Prometheus listening on port `9090`. From 308e0964957eb0155b2f63019675daa4a214099c Mon Sep 17 00:00:00 2001 From: Quentin Mc Gaw Date: Thu, 22 May 2025 16:16:36 +0200 Subject: [PATCH 182/197] Fix pinning of abigen (again) --- go.mod | 11 ----- go.sum | 46 +------------------ .../load/c/contracts/generate_abi_bindings.sh | 2 +- tools.go | 1 - 4 files changed, 2 insertions(+), 58 deletions(-) diff --git a/go.mod b/go.mod index eef511488ddd..991651d2f5a2 100644 --- a/go.mod +++ b/go.mod @@ -101,7 +101,6 @@ require ( github.com/crate-crypto/go-kzg-4844 v0.7.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/deckarep/golang-set/v2 v2.1.0 // indirect - github.com/deepmap/oapi-codegen v1.6.0 // indirect github.com/distribution/reference v0.5.0 // indirect github.com/dlclark/regexp2 v1.7.0 // indirect github.com/docker/go-connections v0.4.0 // indirect @@ -110,7 +109,6 @@ require ( github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/ethereum/c-kzg-4844 v0.4.0 // indirect github.com/fatih/structtag v1.2.0 // indirect - github.com/ferranbt/fastssz v0.1.2 // indirect github.com/frankban/quicktest v1.14.4 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect @@ -126,14 +124,12 @@ require ( github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/gofrs/flock v0.8.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect github.com/gorilla/websocket v1.5.0 // indirect - github.com/graph-gophers/graphql-go v1.3.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect github.com/hashicorp/go-bexpr v0.1.10 // indirect github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect @@ -142,13 +138,9 @@ require ( github.com/holiman/bloomfilter/v2 v2.0.3 // indirect github.com/imdario/mergo v0.3.16 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/influxdata/influxdb-client-go/v2 v2.4.0 // indirect - github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c // indirect - github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.15.15 // indirect - github.com/klauspost/cpuid/v2 v2.0.9 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/magiconair/properties v1.8.6 // indirect @@ -158,7 +150,6 @@ require ( github.com/mattn/go-runewidth v0.0.13 // indirect github.com/mattn/go-shellwords v1.0.12 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect - github.com/minio/sha256-simd v1.0.0 // indirect github.com/mitchellh/pointerstructure v1.2.0 // indirect github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/moby/spdystream v0.2.0 // indirect @@ -168,10 +159,8 @@ require ( github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opentracing/opentracing-go v1.1.0 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.0.5 // indirect - github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/procfs v0.10.1 // indirect diff --git a/go.sum b/go.sum index be832ebb6ada..d75c18515daf 100644 --- a/go.sum +++ b/go.sum @@ -155,7 +155,6 @@ github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233/go.mod h1:geZJ github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA= github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -169,10 +168,7 @@ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeC github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc= github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= -github.com/deepmap/oapi-codegen v1.6.0 h1:w/d1ntwh91XI0b/8ja7+u5SvA4IFfM0UNNLmiDR1gg0= -github.com/deepmap/oapi-codegen v1.6.0/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M= github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= @@ -206,8 +202,6 @@ github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4= github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= -github.com/ferranbt/fastssz v0.1.2 h1:Dky6dXlngF6Qjc+EfDipAkE83N5I5DE68bY6O0VLNPk= -github.com/ferranbt/fastssz v0.1.2/go.mod h1:X5UPrE2u1UJjxHA8X54u04SBwdAQjG2sFtWs39YxyWs= github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -220,15 +214,12 @@ github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 h1:f6D9Hr8x github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 h1:BAIP2GihuqhwdILrV+7GJel5lyPV3u1+PgzrWLc0TkE= github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46/go.mod h1:QNpY22eby74jVhqH4WhDLDwxc/vqsern6pW+u2kbkpc= -github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= github.com/getsentry/sentry-go v0.12.0/go.mod h1:NSap0JBYWzHND8oMbyi0+XZhUalc1TBdRL1M71JZW2c= github.com/getsentry/sentry-go v0.18.0 h1:MtBW5H9QgdcJabtZcuJG80BMOwaBpkRDZkxRkNC1sN0= github.com/getsentry/sentry-go v0.18.0/go.mod h1:Kgon4Mby+FJ7ZWHFUAZgVaIa8sxHtnRJRLTXZr51aKQ= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= -github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= @@ -245,12 +236,10 @@ github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AE github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU= @@ -269,6 +258,7 @@ github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM= +github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= @@ -306,7 +296,6 @@ github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6 github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y= github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -366,8 +355,6 @@ github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/graph-gophers/graphql-go v1.3.0 h1:Eb9x/q6MFpCLz7jBCiP/WTxjSDrYLR1QY41SORZyNJ0= -github.com/graph-gophers/graphql-go v1.3.0/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= @@ -400,12 +387,6 @@ github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/C github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/influxdata/influxdb-client-go/v2 v2.4.0 h1:HGBfZYStlx3Kqvsv1h2pJixbCl/jhnFtxpKFAv9Tu5k= -github.com/influxdata/influxdb-client-go/v2 v2.4.0/go.mod h1:vLNHdxTJkIf2mSLvGrpj8TCcISApPoXkaxP8g9uRlW8= -github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c h1:qSHzRbhzK8RdXOsAdfDgO49TtqC1oZ+acxPrkfTxcCs= -github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= -github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 h1:W9WBk7wlPfJLvMCdtV4zPulc4uCPrlywQOmbFOhgQNU= -github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI= github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0= github.com/iris-contrib/jade v1.1.3/go.mod h1:H/geBymxJhShH5kecoiOCSssPX7QWYH7UaeZTSWddIk= @@ -443,9 +424,6 @@ github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0 github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw= github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= -github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -458,7 +436,6 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg= github.com/labstack/echo/v4 v4.5.0/go.mod h1:czIriw4a0C1dFun+ObrXp7ok03xON0N1awStJ6ArI7Y= github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= @@ -466,13 +443,9 @@ github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2 github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= @@ -485,7 +458,6 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= @@ -496,8 +468,6 @@ github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zk github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8= github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= -github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= -github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= @@ -558,15 +528,11 @@ github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg= github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg= github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= -github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 h1:oYW+YCJ1pachXTQmzR3rNLYGGz4g/UgFcjb28p/viDM= -github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pires/go-proxyproto v0.6.2 h1:KAZ7UteSOt6urjme6ZldyFm4wDe/z0ZUP0Yv0Dos0d8= @@ -588,8 +554,6 @@ github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= -github.com/prysmaticlabs/gohashtree v0.0.1-alpha.0.20220714111606-acbb2962fb48 h1:cSo6/vk8YpvkLbk9v3FO97cakNmUoxwi2KMP8hd5WIw= -github.com/prysmaticlabs/gohashtree v0.0.1-alpha.0.20220714111606-acbb2962fb48/go.mod h1:4pWaT30XoEx1j8KNJf3TV+E3mQkaufn7mf+jRNb/Fuk= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -743,8 +707,6 @@ golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= @@ -828,7 +790,6 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= @@ -881,7 +842,6 @@ golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -901,7 +861,6 @@ golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -909,7 +868,6 @@ golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -935,7 +893,6 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= @@ -956,7 +913,6 @@ golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/tests/load/c/contracts/generate_abi_bindings.sh b/tests/load/c/contracts/generate_abi_bindings.sh index de6d4e032289..d7861d8a774d 100755 --- a/tests/load/c/contracts/generate_abi_bindings.sh +++ b/tests/load/c/contracts/generate_abi_bindings.sh @@ -15,7 +15,7 @@ for FILE in "${CONTRACTS_DIR}"/*.sol; do echo "Generating Go bindings from Solidity contract $FILE..." CONTRACT_NAME=$(basename "$FILE" .sol) solc --abi --bin --overwrite -o "$TEMPDIR" "${CONTRACTS_DIR}/${CONTRACT_NAME}.sol" - go run github.com/ava-labs/libevm/cmd/abigen \ + go run github.com/ava-labs/libevm/cmd/abigen@v1.13.14-0.2.0.release \ --bin="${TEMPDIR}/${CONTRACT_NAME}.bin" \ --abi="${TEMPDIR}/${CONTRACT_NAME}.abi" \ --type "$CONTRACT_NAME" \ diff --git a/tools.go b/tools.go index bff79d71c340..f6b9754a6fd1 100644 --- a/tools.go +++ b/tools.go @@ -5,6 +5,5 @@ package avalanchego import ( _ "github.com/StephenButtolph/canoto/generate" - _ "github.com/ava-labs/libevm/cmd/abigen" _ "golang.org/x/mod/semver" // golang.org/x/mod to satisfy requirement for go.uber.org/mock/mockgen@v0.5 ) From 3b5e69a44af8c829c2610ee596239393a202a7c8 Mon Sep 17 00:00:00 2001 From: Rodrigo Villar Date: Fri, 23 May 2025 13:50:10 -0400 Subject: [PATCH 183/197] remove ginkgo from load tests --- Taskfile.yml | 4 +- tests/load/c/load_test.go | 147 ++++++++++++++++++------------------- tests/load/metrics_link.go | 36 +++------ 3 files changed, 83 insertions(+), 104 deletions(-) diff --git a/Taskfile.yml b/Taskfile.yml index 84fc99bff57b..917fa5008217 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -215,14 +215,14 @@ tasks: cmds: - task: generate-load-contract-bindings - task: build - - cmd: ./bin/ginkgo -v ./tests/load/c -- --avalanchego-path=$PWD/build/avalanchego --start-metrics-collector --start-logs-collector + - cmd: go test ./tests/load/c -test.v -- --avalanchego-path=$PWD/build/avalanchego --start-metrics-collector --start-logs-collector test-load-local: desc: Runs load tests with local monitoring cmds: - task: generate-load-contract-bindings - task: build - - cmd: ./bin/ginkgo -v ./tests/load/c -- --avalanchego-path=$PWD/build/avalanchego + - cmd: go test ./tests/load/c -test.v -- --avalanchego-path=$PWD/build/avalanchego test-unit: desc: Runs unit tests diff --git a/tests/load/c/load_test.go b/tests/load/c/load_test.go index 45839ce5bcdb..e80a4e66bfab 100644 --- a/tests/load/c/load_test.go +++ b/tests/load/c/load_test.go @@ -7,103 +7,98 @@ import ( "context" "os" "testing" + "time" - "github.com/onsi/ginkgo/v2" "github.com/prometheus/client_golang/prometheus" "github.com/stretchr/testify/require" + "github.com/ava-labs/avalanchego/tests" "github.com/ava-labs/avalanchego/tests/fixture/e2e" "github.com/ava-labs/avalanchego/tests/fixture/tmpnet" "github.com/ava-labs/avalanchego/tests/load" - "github.com/ava-labs/avalanchego/utils/logging" ) -// Run this using the command from the root of the repository in a Nix develop shell: -// task test-load -func TestLoad(t *testing.T) { - ginkgo.RunSpecs(t, "load tests") -} +const ( + blockchainID = "C" + // invariant: nodesCount >= 5 + nodesCount = 5 + agentsPerNode = 50 + agentsCount = nodesCount * agentsPerNode + logPrefix = "avalanchego-load-test" +) var flagVars *e2e.FlagVars func init() { flagVars = e2e.RegisterFlagsWithDefaultOwner("avalanchego-load") + + // Disable default metrics link generation to prevent duplicate links. + // We generate load specific links. + e2e.EmitMetricsLink = false } -const ( - blockchainID = "C" - nodesCount = 5 - agentsPerNode = 50 - agentsCount = nodesCount * agentsPerNode -) +func TestLoad(t *testing.T) { + require := require.New(t) + ctx := context.Background() + log := tests.NewDefaultLogger(logPrefix) + tc := tests.NewTestContext(log) + + startTime := time.Now() + nodes := tmpnet.NewNodesOrPanic(nodesCount) + network := &tmpnet.Network{ + Owner: "avalanchego-load-test", + Nodes: nodes, + } -var _ = ginkgo.Describe("[Load Simulator]", ginkgo.Ordered, func() { - var ( - network *tmpnet.Network - metrics *load.Metrics + setPrefundedKeys(t, network, agentsCount) - logger logging.Logger - ) + testEnv := e2e.NewTestEnvironment(tc, flagVars, network) + defer network.Stop(ctx) + + registry := prometheus.NewRegistry() + metrics, err := load.NewMetrics(registry) + require.NoError(err, "failed to register load metrics") + + metricsServer := load.NewPrometheusServer("127.0.0.1:0", registry) + merticsErrCh, err := metricsServer.Start() + require.NoError(err, "failed to start load metrics server") + + monitoringConfigFilePath, err := metricsServer.GenerateMonitoringConfig(network.UUID, network.Owner) + require.NoError(err, "failed to generate monitoring config file") - ginkgo.BeforeAll(func() { - require.GreaterOrEqual(ginkgo.GinkgoT(), nodesCount, 5, "number of nodes must be at least 5") - tc := e2e.NewTestContext() - nodes := tmpnet.NewNodesOrPanic(nodesCount) - network = &tmpnet.Network{ - Owner: "avalanchego-load-test", - Nodes: nodes, + defer func() { + select { + case err := <-merticsErrCh: + require.NoError(err, "metrics server exited with error") + default: + require.NoError(metricsServer.Stop(), "failed to stop metrics server") } - setPrefundedKeys(tc, network, agentsCount) + }() - env := e2e.NewTestEnvironment( - tc, - flagVars, - network, + defer func() { + require.NoError( + os.Remove(monitoringConfigFilePath), + "failed †o remove monitoring config file", ) + }() + + endpoints, err := tmpnet.GetNodeWebsocketURIs(ctx, network.Nodes, blockchainID) + require.NoError(err, "failed †o get node websocket URIs") + config := loadConfig{ + endpoints: endpoints, + agents: agentsPerNode, + minTPS: 100, + maxTPS: 500, + step: 100, + } - logger = tc.Log() - registry := prometheus.NewRegistry() - - network = env.GetNetwork() - - loadMetrics, err := load.NewMetrics(registry) - require.NoError(tc, err, "failed to register load metrics") - metrics = loadMetrics - - metricsServer := load.NewPrometheusServer("127.0.0.1:0", registry) - metricsErrCh, err := metricsServer.Start() - require.NoError(tc, err, "failed to start load metrics server") - - monitoringConfigFilePath, err := metricsServer.GenerateMonitoringConfig(network.UUID, network.Owner) - require.NoError(tc, err, "failed to generate monitoring config file") - - ginkgo.DeferCleanup(func() { - select { - case err := <-metricsErrCh: - require.NoError(tc, err, "metrics server exited with error") - default: - require.NoError(tc, metricsServer.Stop(), "failed to stop metrics server") - } - }) - ginkgo.DeferCleanup(func() { - require.NoError(tc, os.Remove(monitoringConfigFilePath), "failed to remove monitoring config file") - }) - }) - - ginkgo.It("C-Chain", func(ctx context.Context) { - endpoints, err := tmpnet.GetNodeWebsocketURIs(ctx, network.Nodes, blockchainID) - require.NoError(ginkgo.GinkgoT(), err, "getting node websocket URIs") - config := loadConfig{ - endpoints: endpoints, - agents: agentsPerNode, - minTPS: 1000, - maxTPS: 1700, - step: 100, - } - err = execute(ctx, network.PreFundedKeys, config, metrics, logger) - require.NoError(ginkgo.GinkgoT(), err, "executing load test") - }) -}) + require.NoError( + execute(ctx, network.PreFundedKeys, config, metrics, log), + "failed to execute load test", + ) + + load.GenerateMetricsLink(testEnv, log, startTime) +} // setPrefundedKeys sets the pre-funded keys for the network, and keeps // keys already set if any. If there are more keys than required, it @@ -112,7 +107,9 @@ func setPrefundedKeys(t require.TestingT, network *tmpnet.Network, minKeys int) if len(network.PreFundedKeys) >= minKeys { return } + + require := require.New(t) missingPreFundedKeys, err := tmpnet.NewPrivateKeys(minKeys - len(network.PreFundedKeys)) - require.NoError(t, err, "creating pre-funded keys") + require.NoError(err, "creating pre-funded keys") network.PreFundedKeys = append(network.PreFundedKeys, missingPreFundedKeys...) } diff --git a/tests/load/metrics_link.go b/tests/load/metrics_link.go index db1088a474cf..43e347da9419 100644 --- a/tests/load/metrics_link.go +++ b/tests/load/metrics_link.go @@ -7,11 +7,11 @@ import ( "strconv" "time" - "github.com/onsi/ginkgo/v2" "go.uber.org/zap" "github.com/ava-labs/avalanchego/tests/fixture/e2e" "github.com/ava-labs/avalanchego/tests/fixture/tmpnet" + "github.com/ava-labs/avalanchego/utils/logging" ) const ( @@ -19,31 +19,13 @@ const ( dashboardName = "C-Chain Load" ) -var ( - // Disable default metrics link generation to prevent duplicate links. - // We generate load specific links. - _ = ginkgo.JustBeforeEach(func() { - e2e.EmitMetricsLink = false +func GenerateMetricsLink(env *e2e.TestEnvironment, log logging.Logger, startTime time.Time) { + grafanaLink := tmpnet.BuildMonitoringURLForNetwork(dashboardID, dashboardName, env.GetNetwork().UUID, tmpnet.GrafanaFilterOptions{ + StartTime: strconv.FormatInt(startTime.UnixMilli(), 10), + EndTime: strconv.FormatInt(time.Now().Add(tmpnet.NetworkShutdownDelay).UnixMilli(), 10), }) - _ = ginkgo.AfterEach(func() { - tc := e2e.NewTestContext() - env := e2e.GetEnv(tc) - - if env == nil { - return - } - - specReport := ginkgo.CurrentSpecReport() - startTimeMs := specReport.StartTime.UnixMilli() - - grafanaLink := tmpnet.BuildMonitoringURLForNetwork(dashboardID, dashboardName, env.GetNetwork().UUID, tmpnet.GrafanaFilterOptions{ - StartTime: strconv.FormatInt(startTimeMs, 10), - EndTime: strconv.FormatInt(time.Now().Add(tmpnet.NetworkShutdownDelay).UnixMilli(), 10), - }) - - tc.Log().Info(tmpnet.MetricsAvailableMessage, - zap.String("uri", grafanaLink), - ) - }) -) + log.Info(tmpnet.MetricsAvailableMessage, + zap.String("uri", grafanaLink), + ) +} From 3d211a7d3315a8f743eeea309da04520bbbf6810 Mon Sep 17 00:00:00 2001 From: Rodrigo Villar Date: Fri, 23 May 2025 14:04:11 -0400 Subject: [PATCH 184/197] single test-load task --- Taskfile.yml | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/Taskfile.yml b/Taskfile.yml index 917fa5008217..9cd1262d0c98 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -211,18 +211,11 @@ tasks: cmd: ./scripts/build_fuzz.sh {{.FUZZTIME}} ./x/merkledb test-load: - desc: Runs load tests with remote monitoring + desc: Runs load tests cmds: - task: generate-load-contract-bindings - task: build - - cmd: go test ./tests/load/c -test.v -- --avalanchego-path=$PWD/build/avalanchego --start-metrics-collector --start-logs-collector - - test-load-local: - desc: Runs load tests with local monitoring - cmds: - - task: generate-load-contract-bindings - - task: build - - cmd: go test ./tests/load/c -test.v -- --avalanchego-path=$PWD/build/avalanchego + - cmd: go test ./tests/load/c -test.v -- --avalanchego-path=$PWD/build/avalanchego test-unit: desc: Runs unit tests From ffad23fee846c7efc9ab52e0cfb8c3cc85297e70 Mon Sep 17 00:00:00 2001 From: Rodrigo Villar Date: Fri, 23 May 2025 14:05:46 -0400 Subject: [PATCH 185/197] remove network owner label --- tests/load/prometheus.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/load/prometheus.go b/tests/load/prometheus.go index 79f03babffb6..7f5f358462e7 100644 --- a/tests/load/prometheus.go +++ b/tests/load/prometheus.go @@ -94,10 +94,7 @@ func (s *MetricsServer) GenerateMonitoringConfig(networkUUID, networkOwner strin config, err := json.MarshalIndent([]tmpnet.ConfigMap{ { "targets": []string{s.addr}, - "labels": map[string]string{ - "network_uuid": networkUUID, - "network_owner": networkOwner, - }, + "labels": map[string]string{"network_uuid": networkUUID}, }, }, "", " ") if err != nil { From 0e1e3882c756f096f7a3cf985285bca9287c8f72 Mon Sep 17 00:00:00 2001 From: Rodrigo Villar Date: Fri, 23 May 2025 14:17:17 -0400 Subject: [PATCH 186/197] lint --- tests/fixture/tmpnet/utils.go | 2 +- tests/load/c/load_test.go | 6 ++++-- tests/load/prometheus.go | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/fixture/tmpnet/utils.go b/tests/fixture/tmpnet/utils.go index 1a6d536cc3e8..c9084c75f051 100644 --- a/tests/fixture/tmpnet/utils.go +++ b/tests/fixture/tmpnet/utils.go @@ -96,7 +96,7 @@ func FilterAvailableNodes(nodes []*Node) []*Node { // blockchain ID, in the form "ws:///ext/bc//ws". // Ephemeral and stopped nodes are ignored. func GetNodeWebsocketURIs(ctx context.Context, nodes []*Node, blockchainID string) ([]string, error) { - deferCleanup := func(f func()) {} + deferCleanup := func(func()) {} nodeURIs, err := GetNodeURIs(ctx, nodes, deferCleanup) if err != nil { return nil, fmt.Errorf("getting node URIs: %w", err) diff --git a/tests/load/c/load_test.go b/tests/load/c/load_test.go index e80a4e66bfab..7a281e6fd17b 100644 --- a/tests/load/c/load_test.go +++ b/tests/load/c/load_test.go @@ -53,7 +53,9 @@ func TestLoad(t *testing.T) { setPrefundedKeys(t, network, agentsCount) testEnv := e2e.NewTestEnvironment(tc, flagVars, network) - defer network.Stop(ctx) + defer func() { + require.NoError(network.Stop(ctx), "failed to stop network") + }() registry := prometheus.NewRegistry() metrics, err := load.NewMetrics(registry) @@ -63,7 +65,7 @@ func TestLoad(t *testing.T) { merticsErrCh, err := metricsServer.Start() require.NoError(err, "failed to start load metrics server") - monitoringConfigFilePath, err := metricsServer.GenerateMonitoringConfig(network.UUID, network.Owner) + monitoringConfigFilePath, err := metricsServer.GenerateMonitoringConfig(network.UUID) require.NoError(err, "failed to generate monitoring config file") defer func() { diff --git a/tests/load/prometheus.go b/tests/load/prometheus.go index 7f5f358462e7..0527c39953ea 100644 --- a/tests/load/prometheus.go +++ b/tests/load/prometheus.go @@ -84,7 +84,7 @@ func (s *MetricsServer) Stop() (err error) { // GenerateMonitoringConfig generates and writes the Prometheus collector configuration // so tmpnet can dynamically discover new scrape target via file-based service discovery // It returns the collector file path. -func (s *MetricsServer) GenerateMonitoringConfig(networkUUID, networkOwner string) (string, error) { +func (s *MetricsServer) GenerateMonitoringConfig(networkUUID string) (string, error) { discoveryDir, err := tmpnet.GetServiceDiscoveryDir("prometheus") if err != nil { return "", fmt.Errorf("getting tmpnet service discovery directory: %w", err) From 9a9ef0fe3e4093f16d2613cba49f9ad2f5d540cd Mon Sep 17 00:00:00 2001 From: Rodrigo Villar Date: Tue, 27 May 2025 09:34:38 -0400 Subject: [PATCH 187/197] clean func signatures --- tests/load/c/execute.go | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/tests/load/c/execute.go b/tests/load/c/execute.go index 56ee87778240..6222f97d530a 100644 --- a/tests/load/c/execute.go +++ b/tests/load/c/execute.go @@ -25,7 +25,13 @@ type loadConfig struct { step int64 } -func execute(ctx context.Context, keys []*secp256k1.PrivateKey, config loadConfig, metrics *load.Metrics, logger logging.Logger) error { +func execute( + ctx context.Context, + keys []*secp256k1.PrivateKey, + config loadConfig, + metrics *load.Metrics, + logger logging.Logger, +) error { tracker := load.NewTracker[common.Hash](metrics) agents, err := createAgents(ctx, config, keys, tracker) if err != nil { @@ -48,7 +54,10 @@ func execute(ctx context.Context, keys []*secp256k1.PrivateKey, config loadConfi // It creates them in parallel because creating issuers can sometimes take a while, // and this adds up for many agents. For example, deploying the Opcoder contract // takes a few seconds. Running the creation in parallel can reduce the time significantly. -func createAgents(ctx context.Context, config loadConfig, keys []*secp256k1.PrivateKey, +func createAgents( + ctx context.Context, + config loadConfig, + keys []*secp256k1.PrivateKey, tracker *load.Tracker[common.Hash], ) ([]load.Agent[common.Hash], error) { ctx, cancel := context.WithCancel(ctx) @@ -90,7 +99,10 @@ func createAgents(ctx context.Context, config loadConfig, keys []*secp256k1.Priv return agents, nil } -func createAgent(ctx context.Context, endpoint string, key *secp256k1.PrivateKey, +func createAgent( + ctx context.Context, + endpoint string, + key *secp256k1.PrivateKey, tracker *load.Tracker[common.Hash], ) (load.Agent[common.Hash], error) { client, err := ethclient.DialContext(ctx, endpoint) From d5c7fdff1d92d7005f4fc0c992089272f174bd47 Mon Sep 17 00:00:00 2001 From: Elvis S Date: Wed, 28 May 2025 14:44:26 +0200 Subject: [PATCH 188/197] ci: k8s load test --- .github/workflows/k8s-load-tests.yaml | 40 +++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 .github/workflows/k8s-load-tests.yaml diff --git a/.github/workflows/k8s-load-tests.yaml b/.github/workflows/k8s-load-tests.yaml new file mode 100644 index 000000000000..5b28ad0fbfd5 --- /dev/null +++ b/.github/workflows/k8s-load-tests.yaml @@ -0,0 +1,40 @@ +name: K8s Load Tests + +on: + workflow_dispatch: + inputs: + branch: + description: 'Branch to test' + required: false + type: string + runner_label: + description: 'Runner' + required: false + default: 'self-hosted' + type: choice + options: + - 'self-hosted' + - 'ubuntu-latest' + +permissions: + contents: read + +jobs: + load: + name: "K8S Load Test" + runs-on: "self-hosted" + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.event.inputs.branch || github.ref }} + fetch-depth: 0 + - uses: ./.github/actions/setup-go-for-project + - uses: ./.github/actions/install-nix + - uses: ./.github/actions/run-monitored-tmpnet-cmd + with: + run: ./scripts/run_task.sh test-load + artifact_prefix: load-manual-${{ github.run_number }} + prometheus_username: ${{ secrets.PROMETHEUS_ID || '' }} + prometheus_password: ${{ secrets.PROMETHEUS_PASSWORD || '' }} + loki_username: ${{ secrets.LOKI_ID || '' }} + loki_password: ${{ secrets.LOKI_PASSWORD || '' }} From b73f38777fe67039052c4fd57c34c2ec35c6b055 Mon Sep 17 00:00:00 2001 From: Rodrigo Villar Date: Wed, 28 May 2025 08:51:51 -0400 Subject: [PATCH 189/197] clean up issuer --- tests/load/c/execute.go | 2 +- tests/load/c/issuer.go | 27 ++++++++++++--------------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/tests/load/c/execute.go b/tests/load/c/execute.go index 6222f97d530a..e2572a15eba3 100644 --- a/tests/load/c/execute.go +++ b/tests/load/c/execute.go @@ -117,7 +117,7 @@ func createAgent( return load.Agent[common.Hash]{}, fmt.Errorf("getting nonce for address %s: %w", address, err) } - issuer, err := createIssuer(ctx, client, tracker, nonce, key.ToECDSA()) + issuer, err := createIssuer(ctx, client, nonce, key.ToECDSA()) if err != nil { return load.Agent[common.Hash]{}, fmt.Errorf("creating issuer: %w", err) } diff --git a/tests/load/c/issuer.go b/tests/load/c/issuer.go index 2a7247d0b637..ba47b3bd7c1e 100644 --- a/tests/load/c/issuer.go +++ b/tests/load/c/issuer.go @@ -31,17 +31,10 @@ type EthClientSender interface { SendTransaction(ctx context.Context, tx *types.Transaction) error } -type IssueTracker interface { - Issue(tx common.Hash) -} - // issuer generates and issues transactions that randomly call the // external functions of the [contracts.EVMLoadSimulator] contract // instance that it deploys. type issuer struct { - // Injected parameters - tracker IssueTracker - // Determined by constructor txTypes []txType @@ -52,7 +45,6 @@ type issuer struct { func createIssuer( ctx context.Context, client EthClient, - tracker IssueTracker, nonce uint64, key *ecdsa.PrivateKey, ) (*issuer, error) { @@ -78,7 +70,6 @@ func createIssuer( } return &issuer{ - tracker: tracker, txTypes: makeTxTypes(simulatorInstance, key, chainID, client), nonce: nonce, }, nil @@ -94,12 +85,14 @@ func (o *issuer) GenerateAndIssueTx(ctx context.Context) (common.Hash, error) { o.nonce++ txHash := tx.Hash() - o.tracker.Issue(txHash) - return txHash, err + return txHash, nil } -func makeTxTypes(contractInstance *contracts.EVMLoadSimulator, senderKey *ecdsa.PrivateKey, - chainID *big.Int, client EthClientSender, +func makeTxTypes( + contractInstance *contracts.EVMLoadSimulator, + senderKey *ecdsa.PrivateKey, + chainID *big.Int, + client EthClientSender, ) []txType { senderAddress := ethcrypto.PubkeyToAddress(senderKey.PublicKey) signer := types.LatestSignerForChainID(chainID) @@ -297,8 +290,12 @@ func intNBigInt(n int64) *big.Int { return big.NewInt(rand.Int64N(n)) //nolint:gosec } -func newTxOpts(ctx context.Context, key *ecdsa.PrivateKey, - chainID, maxFeeCap *big.Int, nonce uint64, +func newTxOpts( + ctx context.Context, + key *ecdsa.PrivateKey, + chainID *big.Int, + maxFeeCap *big.Int, + nonce uint64, ) (*bind.TransactOpts, error) { txOpts, err := bind.NewKeyedTransactorWithChainID(key, chainID) if err != nil { From e1a42f2f13444e0786bc4ab41dd334af372a080c Mon Sep 17 00:00:00 2001 From: Elvis S Date: Wed, 28 May 2025 14:55:57 +0200 Subject: [PATCH 190/197] ci: rename k8s-load-tests extension from yaml to yml --- .../workflows/{k8s-load-tests.yaml => k8s-load-tests.yml} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename .github/workflows/{k8s-load-tests.yaml => k8s-load-tests.yml} (83%) diff --git a/.github/workflows/k8s-load-tests.yaml b/.github/workflows/k8s-load-tests.yml similarity index 83% rename from .github/workflows/k8s-load-tests.yaml rename to .github/workflows/k8s-load-tests.yml index 5b28ad0fbfd5..68770e1858e8 100644 --- a/.github/workflows/k8s-load-tests.yaml +++ b/.github/workflows/k8s-load-tests.yml @@ -4,11 +4,11 @@ on: workflow_dispatch: inputs: branch: - description: 'Branch to test' + description: 'Branch to test (leave empty for current branch)' required: false type: string runner_label: - description: 'Runner' + description: 'Runner label (self-hosted for K8s)' required: false default: 'self-hosted' type: choice @@ -22,7 +22,7 @@ permissions: jobs: load: name: "K8S Load Test" - runs-on: "self-hosted" + runs-on: ${{ github.event.inputs.runner_label || 'self-hosted' }} steps: - uses: actions/checkout@v4 with: From 3a1360ec577e2e7ea03d70022a198d1714e8a6f2 Mon Sep 17 00:00:00 2001 From: Elvis S Date: Wed, 28 May 2025 15:01:26 +0200 Subject: [PATCH 191/197] ci: disable CI --- .github/workflows/ci.yml | 462 +++++++++++++++++++-------------------- 1 file changed, 231 insertions(+), 231 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7c73db498a31..87d033731882 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,231 +1,231 @@ -name: Tests - -on: - push: - tags: - - "*" - branches: - - master - - dev - pull_request: - merge_group: - types: [checks_requested] - -permissions: - contents: read - -# Cancel ongoing workflow runs if a new one is started -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - Unit: - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - os: [macos-14, ubuntu-22.04, ubuntu-24.04, windows-2022, custom-arm64-jammy, custom-arm64-noble] - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-go-for-project - - name: Set timeout on Windows # Windows UT run slower and need a longer timeout - shell: bash - if: matrix.os == 'windows-2022' - run: echo "TIMEOUT=240s" >> "$GITHUB_ENV" - - name: test-unit - shell: bash - run: ./scripts/run_task.sh test-unit - env: - TIMEOUT: ${{ env.TIMEOUT }} - Fuzz: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-go-for-project - - name: test-fuzz - shell: bash - run: ./scripts/run_task.sh test-fuzz - e2e: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-go-for-project - - name: Run e2e tests - uses: ./.github/actions/run-monitored-tmpnet-cmd - with: - run: ./scripts/run_task.sh test-e2e-ci - artifact_prefix: e2e - filter_by_owner: avalanchego-e2e - prometheus_username: ${{ secrets.PROMETHEUS_ID || '' }} - prometheus_password: ${{ secrets.PROMETHEUS_PASSWORD || '' }} - loki_username: ${{ secrets.LOKI_ID || '' }} - loki_password: ${{ secrets.LOKI_PASSWORD || '' }} - e2e_post_granite: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-go-for-project - - name: Run e2e tests - uses: ./.github/actions/run-monitored-tmpnet-cmd - with: - run: ./scripts/run_task.sh test-e2e-ci -- --activate-granite - artifact_prefix: e2e-post-granite - filter_by_owner: avalanchego-e2e - prometheus_username: ${{ secrets.PROMETHEUS_ID || '' }} - prometheus_password: ${{ secrets.PROMETHEUS_PASSWORD || '' }} - loki_username: ${{ secrets.LOKI_ID || '' }} - loki_password: ${{ secrets.LOKI_PASSWORD || '' }} - e2e_existing_network: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-go-for-project - - name: Run e2e tests with existing network - uses: ./.github/actions/run-monitored-tmpnet-cmd - with: - run: ./scripts/run_task.sh test-e2e-existing-ci - artifact_prefix: e2e-existing-network - prometheus_username: ${{ secrets.PROMETHEUS_ID || '' }} - prometheus_password: ${{ secrets.PROMETHEUS_PASSWORD || '' }} - loki_username: ${{ secrets.LOKI_ID || '' }} - loki_password: ${{ secrets.LOKI_PASSWORD || '' }} - Upgrade: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-go-for-project - - name: Run e2e tests - uses: ./.github/actions/run-monitored-tmpnet-cmd - with: - run: ./scripts/run_task.sh test-upgrade - artifact_prefix: upgrade - prometheus_username: ${{ secrets.PROMETHEUS_ID || '' }} - prometheus_password: ${{ secrets.PROMETHEUS_PASSWORD || '' }} - loki_username: ${{ secrets.LOKI_ID || '' }} - loki_password: ${{ secrets.LOKI_PASSWORD || '' }} - Lint: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-go-for-project - - uses: ./.github/actions/install-nix - - name: Runs all lint checks - shell: nix develop --command bash -x {0} - run: ./scripts/run_task.sh lint-all-ci - buf-lint: - name: Protobuf Lint - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: bufbuild/buf-action@dfda68eacb65895184c76b9ae522b977636a2c47 #v1.1.4 - with: - input: "proto" - pr_comment: false - # Breaking changes are managed by the rpcchainvm protocol version. - breaking: false - # buf-action defaults to pushing on non-fork branch pushes - # which is never desirable for this job. The buf-push job is - # responsible for pushes. - push: false - # This version should match the version installed in the nix dev shell - version: 1.47.2 - links-lint: - name: Markdown Links Lint - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: umbrelladocs/action-linkspector@a0567ce1c7c13de4a2358587492ed43cab5d0102 #v1.3.4 - with: - fail_level: any - check_generated_protobuf: - name: Up-to-date protobuf - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-go-for-project - # Use the dev shell instead of bufbuild/buf-action to ensure the dev shell provides the expected versions - - uses: ./.github/actions/install-nix - - shell: nix develop --command bash -x {0} - run: ./scripts/run_task.sh check-generate-protobuf - check_mockgen: - name: Up-to-date mocks - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-go-for-project - - shell: bash - run: ./scripts/run_task.sh check-generate-mocks - check_canotogen: - name: Up-to-date canoto - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-go-for-project - - shell: bash - run: ./scripts/run_task.sh check-generate-canoto - go_mod_tidy: - name: Up-to-date go.mod and go.sum - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-go-for-project - - shell: bash - run: ./scripts/run_task.sh check-go-mod-tidy - test_build_image: - name: Image build - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Install qemu (required for cross-platform builds) - run: | - sudo apt update - sudo apt -y install qemu-system qemu-user-static - - name: Check image build - shell: bash - run: ./scripts/run_task.sh test-build-image - test_build_antithesis_avalanchego_images: - name: Build Antithesis avalanchego images - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-go-for-project - - name: Check image build for avalanchego test setup - shell: bash - run: ./scripts/run_task.sh test-build-antithesis-images-avalanchego - test_build_antithesis_xsvm_images: - name: Build Antithesis xsvm images - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-go-for-project - - name: Check image build for xsvm test setup - shell: bash - run: ./scripts/run_task.sh test-build-antithesis-images-xsvm - e2e_bootstrap_monitor: - name: Run bootstrap monitor e2e tests - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-go-for-project - - uses: ./.github/actions/install-nix - - name: Run e2e tests - shell: bash - run: nix develop --command ./scripts/run_task.sh test-bootstrap-monitor-e2e - load_tests: - name: load tests - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - uses: ./.github/actions/setup-go-for-project - - uses: ./.github/actions/install-nix - - uses: ./.github/actions/run-monitored-tmpnet-cmd - with: - run: ./scripts/run_task.sh test-load - artifact_prefix: load - prometheus_username: ${{ secrets.PROMETHEUS_ID || '' }} - prometheus_password: ${{ secrets.PROMETHEUS_PASSWORD || '' }} - loki_username: ${{ secrets.LOKI_ID || '' }} - loki_password: ${{ secrets.LOKI_PASSWORD || '' }} +#name: Tests +# +#on: +# push: +# tags: +# - "*" +# branches: +# - master +# - dev +# pull_request: +# merge_group: +# types: [checks_requested] +# +#permissions: +# contents: read +# +## Cancel ongoing workflow runs if a new one is started +#concurrency: +# group: ${{ github.workflow }}-${{ github.ref }} +# cancel-in-progress: true +# +#jobs: +# Unit: +# runs-on: ${{ matrix.os }} +# strategy: +# fail-fast: false +# matrix: +# os: [macos-14, ubuntu-22.04, ubuntu-24.04, windows-2022, custom-arm64-jammy, custom-arm64-noble] +# steps: +# - uses: actions/checkout@v4 +# - uses: ./.github/actions/setup-go-for-project +# - name: Set timeout on Windows # Windows UT run slower and need a longer timeout +# shell: bash +# if: matrix.os == 'windows-2022' +# run: echo "TIMEOUT=240s" >> "$GITHUB_ENV" +# - name: test-unit +# shell: bash +# run: ./scripts/run_task.sh test-unit +# env: +# TIMEOUT: ${{ env.TIMEOUT }} +# Fuzz: +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v4 +# - uses: ./.github/actions/setup-go-for-project +# - name: test-fuzz +# shell: bash +# run: ./scripts/run_task.sh test-fuzz +# e2e: +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v4 +# - uses: ./.github/actions/setup-go-for-project +# - name: Run e2e tests +# uses: ./.github/actions/run-monitored-tmpnet-cmd +# with: +# run: ./scripts/run_task.sh test-e2e-ci +# artifact_prefix: e2e +# filter_by_owner: avalanchego-e2e +# prometheus_username: ${{ secrets.PROMETHEUS_ID || '' }} +# prometheus_password: ${{ secrets.PROMETHEUS_PASSWORD || '' }} +# loki_username: ${{ secrets.LOKI_ID || '' }} +# loki_password: ${{ secrets.LOKI_PASSWORD || '' }} +# e2e_post_granite: +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v4 +# - uses: ./.github/actions/setup-go-for-project +# - name: Run e2e tests +# uses: ./.github/actions/run-monitored-tmpnet-cmd +# with: +# run: ./scripts/run_task.sh test-e2e-ci -- --activate-granite +# artifact_prefix: e2e-post-granite +# filter_by_owner: avalanchego-e2e +# prometheus_username: ${{ secrets.PROMETHEUS_ID || '' }} +# prometheus_password: ${{ secrets.PROMETHEUS_PASSWORD || '' }} +# loki_username: ${{ secrets.LOKI_ID || '' }} +# loki_password: ${{ secrets.LOKI_PASSWORD || '' }} +# e2e_existing_network: +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v4 +# - uses: ./.github/actions/setup-go-for-project +# - name: Run e2e tests with existing network +# uses: ./.github/actions/run-monitored-tmpnet-cmd +# with: +# run: ./scripts/run_task.sh test-e2e-existing-ci +# artifact_prefix: e2e-existing-network +# prometheus_username: ${{ secrets.PROMETHEUS_ID || '' }} +# prometheus_password: ${{ secrets.PROMETHEUS_PASSWORD || '' }} +# loki_username: ${{ secrets.LOKI_ID || '' }} +# loki_password: ${{ secrets.LOKI_PASSWORD || '' }} +# Upgrade: +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v4 +# - uses: ./.github/actions/setup-go-for-project +# - name: Run e2e tests +# uses: ./.github/actions/run-monitored-tmpnet-cmd +# with: +# run: ./scripts/run_task.sh test-upgrade +# artifact_prefix: upgrade +# prometheus_username: ${{ secrets.PROMETHEUS_ID || '' }} +# prometheus_password: ${{ secrets.PROMETHEUS_PASSWORD || '' }} +# loki_username: ${{ secrets.LOKI_ID || '' }} +# loki_password: ${{ secrets.LOKI_PASSWORD || '' }} +# Lint: +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v4 +# - uses: ./.github/actions/setup-go-for-project +# - uses: ./.github/actions/install-nix +# - name: Runs all lint checks +# shell: nix develop --command bash -x {0} +# run: ./scripts/run_task.sh lint-all-ci +# buf-lint: +# name: Protobuf Lint +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v4 +# - uses: bufbuild/buf-action@dfda68eacb65895184c76b9ae522b977636a2c47 #v1.1.4 +# with: +# input: "proto" +# pr_comment: false +# # Breaking changes are managed by the rpcchainvm protocol version. +# breaking: false +# # buf-action defaults to pushing on non-fork branch pushes +# # which is never desirable for this job. The buf-push job is +# # responsible for pushes. +# push: false +# # This version should match the version installed in the nix dev shell +# version: 1.47.2 +# links-lint: +# name: Markdown Links Lint +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v4 +# - uses: umbrelladocs/action-linkspector@a0567ce1c7c13de4a2358587492ed43cab5d0102 #v1.3.4 +# with: +# fail_level: any +# check_generated_protobuf: +# name: Up-to-date protobuf +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v4 +# - uses: ./.github/actions/setup-go-for-project +# # Use the dev shell instead of bufbuild/buf-action to ensure the dev shell provides the expected versions +# - uses: ./.github/actions/install-nix +# - shell: nix develop --command bash -x {0} +# run: ./scripts/run_task.sh check-generate-protobuf +# check_mockgen: +# name: Up-to-date mocks +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v4 +# - uses: ./.github/actions/setup-go-for-project +# - shell: bash +# run: ./scripts/run_task.sh check-generate-mocks +# check_canotogen: +# name: Up-to-date canoto +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v4 +# - uses: ./.github/actions/setup-go-for-project +# - shell: bash +# run: ./scripts/run_task.sh check-generate-canoto +# go_mod_tidy: +# name: Up-to-date go.mod and go.sum +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v4 +# - uses: ./.github/actions/setup-go-for-project +# - shell: bash +# run: ./scripts/run_task.sh check-go-mod-tidy +# test_build_image: +# name: Image build +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v4 +# - name: Install qemu (required for cross-platform builds) +# run: | +# sudo apt update +# sudo apt -y install qemu-system qemu-user-static +# - name: Check image build +# shell: bash +# run: ./scripts/run_task.sh test-build-image +# test_build_antithesis_avalanchego_images: +# name: Build Antithesis avalanchego images +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v4 +# - uses: ./.github/actions/setup-go-for-project +# - name: Check image build for avalanchego test setup +# shell: bash +# run: ./scripts/run_task.sh test-build-antithesis-images-avalanchego +# test_build_antithesis_xsvm_images: +# name: Build Antithesis xsvm images +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v4 +# - uses: ./.github/actions/setup-go-for-project +# - name: Check image build for xsvm test setup +# shell: bash +# run: ./scripts/run_task.sh test-build-antithesis-images-xsvm +# e2e_bootstrap_monitor: +# name: Run bootstrap monitor e2e tests +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v4 +# - uses: ./.github/actions/setup-go-for-project +# - uses: ./.github/actions/install-nix +# - name: Run e2e tests +# shell: bash +# run: nix develop --command ./scripts/run_task.sh test-bootstrap-monitor-e2e +# load_tests: +# name: load tests +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v4 +# with: +# fetch-depth: 0 +# - uses: ./.github/actions/setup-go-for-project +# - uses: ./.github/actions/install-nix +# - uses: ./.github/actions/run-monitored-tmpnet-cmd +# with: +# run: ./scripts/run_task.sh test-load +# artifact_prefix: load +# prometheus_username: ${{ secrets.PROMETHEUS_ID || '' }} +# prometheus_password: ${{ secrets.PROMETHEUS_PASSWORD || '' }} +# loki_username: ${{ secrets.LOKI_ID || '' }} +# loki_password: ${{ secrets.LOKI_PASSWORD || '' }} From 39b9aac056aa8d32fa1f10e0597b1b67aec866be Mon Sep 17 00:00:00 2001 From: Elvis S Date: Wed, 28 May 2025 15:01:34 +0200 Subject: [PATCH 192/197] ci: simplify k8s load tests --- .github/workflows/k8s-load-tests.yml | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/.github/workflows/k8s-load-tests.yml b/.github/workflows/k8s-load-tests.yml index 68770e1858e8..0a57ee9ef63b 100644 --- a/.github/workflows/k8s-load-tests.yml +++ b/.github/workflows/k8s-load-tests.yml @@ -3,18 +3,10 @@ name: K8s Load Tests on: workflow_dispatch: inputs: - branch: - description: 'Branch to test (leave empty for current branch)' - required: false - type: string - runner_label: - description: 'Runner label (self-hosted for K8s)' - required: false - default: 'self-hosted' - type: choice - options: - - 'self-hosted' - - 'ubuntu-latest' + scenario: + description: 'Load test scenario to run' + required: true + default: 'baseline' permissions: contents: read @@ -22,12 +14,9 @@ permissions: jobs: load: name: "K8S Load Test" - runs-on: ${{ github.event.inputs.runner_label || 'self-hosted' }} + runs-on: "self-hosted" steps: - uses: actions/checkout@v4 - with: - ref: ${{ github.event.inputs.branch || github.ref }} - fetch-depth: 0 - uses: ./.github/actions/setup-go-for-project - uses: ./.github/actions/install-nix - uses: ./.github/actions/run-monitored-tmpnet-cmd From 292668375d5522a5cde00406e7aa3855e1945ef8 Mon Sep 17 00:00:00 2001 From: Elvis S Date: Wed, 28 May 2025 15:11:43 +0200 Subject: [PATCH 193/197] ci: test --- .github/workflows/ci.yml | 77 ++++++++++++++++++++-------- .github/workflows/k8s-load-tests.yml | 29 ----------- 2 files changed, 55 insertions(+), 51 deletions(-) delete mode 100644 .github/workflows/k8s-load-tests.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 87d033731882..3c315aeb95bc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,25 +1,58 @@ -#name: Tests -# -#on: -# push: -# tags: -# - "*" -# branches: -# - master -# - dev -# pull_request: -# merge_group: -# types: [checks_requested] -# -#permissions: -# contents: read -# -## Cancel ongoing workflow runs if a new one is started -#concurrency: -# group: ${{ github.workflow }}-${{ github.ref }} -# cancel-in-progress: true -# -#jobs: +name: Tests + +on: + push: + tags: + - "*" + branches: + - master + - dev + - arc-load-test + pull_request: + merge_group: + types: [checks_requested] + workflow_dispatch: + inputs: + branch: + description: 'Branch to test' + required: false + type: string + runner_label: + description: 'Runner' + required: false + default: 'self-hosted' + type: choice + options: + - 'self-hosted' + - 'ubuntu-latest' + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + k8s_load_tests: + name: "K8s Load Tests" + runs-on: ${{ github.event.inputs.runner_label || 'self-hosted' }} + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.event.inputs.branch || github.ref }} + fetch-depth: 0 + - uses: ./.github/actions/setup-go-for-project + - uses: ./.github/actions/install-nix + - uses: ./.github/actions/run-monitored-tmpnet-cmd + with: + run: ./scripts/run_task.sh test-load + artifact_prefix: k8s-load-${{ github.run_number }} + prometheus_username: ${{ secrets.PROMETHEUS_ID || '' }} + prometheus_password: ${{ secrets.PROMETHEUS_PASSWORD || '' }} + loki_username: ${{ secrets.LOKI_ID || '' }} + loki_password: ${{ secrets.LOKI_PASSWORD || '' }} + # Unit: # runs-on: ${{ matrix.os }} # strategy: diff --git a/.github/workflows/k8s-load-tests.yml b/.github/workflows/k8s-load-tests.yml deleted file mode 100644 index 0a57ee9ef63b..000000000000 --- a/.github/workflows/k8s-load-tests.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: K8s Load Tests - -on: - workflow_dispatch: - inputs: - scenario: - description: 'Load test scenario to run' - required: true - default: 'baseline' - -permissions: - contents: read - -jobs: - load: - name: "K8S Load Test" - runs-on: "self-hosted" - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-go-for-project - - uses: ./.github/actions/install-nix - - uses: ./.github/actions/run-monitored-tmpnet-cmd - with: - run: ./scripts/run_task.sh test-load - artifact_prefix: load-manual-${{ github.run_number }} - prometheus_username: ${{ secrets.PROMETHEUS_ID || '' }} - prometheus_password: ${{ secrets.PROMETHEUS_PASSWORD || '' }} - loki_username: ${{ secrets.LOKI_ID || '' }} - loki_password: ${{ secrets.LOKI_PASSWORD || '' }} From 694cacf303f786d5ee051cf98b8fa5efb3e6c4c4 Mon Sep 17 00:00:00 2001 From: Elvis S Date: Wed, 28 May 2025 15:15:31 +0200 Subject: [PATCH 194/197] ci: update label --- .github/workflows/ci.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3c315aeb95bc..5133dc0db945 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,10 +20,11 @@ on: runner_label: description: 'Runner' required: false - default: 'self-hosted' + default: 'k8s-runner' type: choice options: - - 'self-hosted' + - 'k8s-runner' + - 'avalanche-avalanchego' - 'ubuntu-latest' permissions: @@ -36,7 +37,7 @@ concurrency: jobs: k8s_load_tests: name: "K8s Load Tests" - runs-on: ${{ github.event.inputs.runner_label || 'self-hosted' }} + runs-on: ${{ github.event.inputs.runner_label || 'k8s-runner' }} steps: - uses: actions/checkout@v4 with: From 3ab9c6a4b0282de1975aab4f5b597b107a31d2d8 Mon Sep 17 00:00:00 2001 From: Elvis S Date: Mon, 2 Jun 2025 20:04:40 +0200 Subject: [PATCH 195/197] ci: update label to avalanche-avalanchego-runner --- .github/workflows/ci.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5133dc0db945..09d6bf37f313 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,11 +20,10 @@ on: runner_label: description: 'Runner' required: false - default: 'k8s-runner' + default: 'avalanche-avalanchego-runner' type: choice options: - - 'k8s-runner' - - 'avalanche-avalanchego' + - 'avalanche-avalanchego-runner' - 'ubuntu-latest' permissions: @@ -37,7 +36,7 @@ concurrency: jobs: k8s_load_tests: name: "K8s Load Tests" - runs-on: ${{ github.event.inputs.runner_label || 'k8s-runner' }} + runs-on: ${{ github.event.inputs.runner_label || 'avalanche-avalanchego-runner' }} steps: - uses: actions/checkout@v4 with: From 18ff7c3b9a17addd9e73e71de870cbea3f48dc41 Mon Sep 17 00:00:00 2001 From: Elvis S Date: Fri, 13 Jun 2025 18:05:54 +0400 Subject: [PATCH 196/197] ci --- .github/workflows/ci.yml | 451 ++++++++++++++++++++------------------- tests/load/c/readme.md | 43 +--- 2 files changed, 237 insertions(+), 257 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0db104d2ee74..09d6bf37f313 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,249 +7,258 @@ on: branches: - master - dev + - arc-load-test pull_request: merge_group: types: [checks_requested] + workflow_dispatch: + inputs: + branch: + description: 'Branch to test' + required: false + type: string + runner_label: + description: 'Runner' + required: false + default: 'avalanche-avalanchego-runner' + type: choice + options: + - 'avalanche-avalanchego-runner' + - 'ubuntu-latest' permissions: contents: read -# Cancel ongoing workflow runs if a new one is started concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: - Unit: - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - os: [macos-14, ubuntu-22.04, ubuntu-24.04, windows-2022, custom-arm64-jammy, custom-arm64-noble] - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-go-for-project - - name: Set timeout on Windows # Windows UT run slower and need a longer timeout - shell: bash - if: matrix.os == 'windows-2022' - run: echo "TIMEOUT=240s" >> "$GITHUB_ENV" - - name: test-unit - shell: bash - run: ./scripts/run_task.sh test-unit - env: - TIMEOUT: ${{ env.TIMEOUT }} - Fuzz: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-go-for-project - - name: test-fuzz - shell: bash - run: ./scripts/run_task.sh test-fuzz - e2e: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-go-for-project - - name: Run e2e tests - uses: ./.github/actions/run-monitored-tmpnet-cmd - with: - run: ./scripts/run_task.sh test-e2e-ci - artifact_prefix: e2e - filter_by_owner: avalanchego-e2e - prometheus_username: ${{ secrets.PROMETHEUS_ID || '' }} - prometheus_password: ${{ secrets.PROMETHEUS_PASSWORD || '' }} - loki_username: ${{ secrets.LOKI_ID || '' }} - loki_password: ${{ secrets.LOKI_PASSWORD || '' }} - e2e_post_granite: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-go-for-project - - name: Run e2e tests - uses: ./.github/actions/run-monitored-tmpnet-cmd - with: - run: ./scripts/run_task.sh test-e2e-ci -- --activate-granite - artifact_prefix: e2e-post-granite - filter_by_owner: avalanchego-e2e - prometheus_username: ${{ secrets.PROMETHEUS_ID || '' }} - prometheus_password: ${{ secrets.PROMETHEUS_PASSWORD || '' }} - loki_username: ${{ secrets.LOKI_ID || '' }} - loki_password: ${{ secrets.LOKI_PASSWORD || '' }} - e2e_kube: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-go-for-project - - uses: ./.github/actions/run-monitored-tmpnet-cmd - with: - run: ./scripts/run_task.sh test-e2e-kube-ci - runtime: kube - artifact_prefix: e2e-kube - filter_by_owner: avalanchego-e2e - prometheus_username: ${{ secrets.PROMETHEUS_ID || '' }} - prometheus_password: ${{ secrets.PROMETHEUS_PASSWORD || '' }} - loki_username: ${{ secrets.LOKI_ID || '' }} - loki_password: ${{ secrets.LOKI_PASSWORD || '' }} - e2e_existing_network: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-go-for-project - - name: Run e2e tests with existing network - uses: ./.github/actions/run-monitored-tmpnet-cmd - with: - run: ./scripts/run_task.sh test-e2e-existing-ci - artifact_prefix: e2e-existing-network - prometheus_username: ${{ secrets.PROMETHEUS_ID || '' }} - prometheus_password: ${{ secrets.PROMETHEUS_PASSWORD || '' }} - loki_username: ${{ secrets.LOKI_ID || '' }} - loki_password: ${{ secrets.LOKI_PASSWORD || '' }} - Upgrade: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-go-for-project - - name: Run e2e tests - uses: ./.github/actions/run-monitored-tmpnet-cmd - with: - run: ./scripts/run_task.sh test-upgrade - artifact_prefix: upgrade - prometheus_username: ${{ secrets.PROMETHEUS_ID || '' }} - prometheus_password: ${{ secrets.PROMETHEUS_PASSWORD || '' }} - loki_username: ${{ secrets.LOKI_ID || '' }} - loki_password: ${{ secrets.LOKI_PASSWORD || '' }} - Lint: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-go-for-project - - uses: ./.github/actions/install-nix - - name: Runs all lint checks - shell: nix develop --command bash -x {0} - run: ./scripts/run_task.sh lint-all-ci - buf-lint: - name: Protobuf Lint - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: bufbuild/buf-action@dfda68eacb65895184c76b9ae522b977636a2c47 #v1.1.4 - with: - input: "proto" - pr_comment: false - # Breaking changes are managed by the rpcchainvm protocol version. - breaking: false - # buf-action defaults to pushing on non-fork branch pushes - # which is never desirable for this job. The buf-push job is - # responsible for pushes. - push: false - # This version should match the version installed in the nix dev shell - version: 1.47.2 - links-lint: - name: Markdown Links Lint - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: umbrelladocs/action-linkspector@a0567ce1c7c13de4a2358587492ed43cab5d0102 #v1.3.4 - with: - fail_level: any - check_generated_protobuf: - name: Up-to-date protobuf - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-go-for-project - # Use the dev shell instead of bufbuild/buf-action to ensure the dev shell provides the expected versions - - uses: ./.github/actions/install-nix - - shell: nix develop --command bash -x {0} - run: ./scripts/run_task.sh check-generate-protobuf - check_mockgen: - name: Up-to-date mocks - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-go-for-project - - shell: bash - run: ./scripts/run_task.sh check-generate-mocks - check_canotogen: - name: Up-to-date canoto - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-go-for-project - - shell: bash - run: ./scripts/run_task.sh check-generate-canoto - check_contract_bindings: - name: Up-to-date contract bindings - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-go-for-project - - uses: ./.github/actions/install-nix - - shell: nix develop --command bash -x {0} - run: task check-generate-load-contract-bindings - go_mod_tidy: - name: Up-to-date go.mod and go.sum - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-go-for-project - - shell: bash - run: ./scripts/run_task.sh check-go-mod-tidy - test_build_image: - name: Image build - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Install qemu (required for cross-platform builds) - run: | - sudo apt update - sudo apt -y install qemu-system qemu-user-static - - name: Check image build - shell: bash - run: ./scripts/run_task.sh test-build-image - test_build_antithesis_avalanchego_images: - name: Build Antithesis avalanchego images - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-go-for-project - - name: Check image build for avalanchego test setup - shell: bash - run: ./scripts/run_task.sh test-build-antithesis-images-avalanchego - test_build_antithesis_xsvm_images: - name: Build Antithesis xsvm images - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-go-for-project - - name: Check image build for xsvm test setup - shell: bash - run: ./scripts/run_task.sh test-build-antithesis-images-xsvm - e2e_bootstrap_monitor: - name: Run bootstrap monitor e2e tests - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-go-for-project - - uses: ./.github/actions/install-nix - - name: Run e2e tests - shell: bash - run: nix develop --command ./scripts/run_task.sh test-bootstrap-monitor-e2e - load_tests: - name: load tests - runs-on: ubuntu-latest + k8s_load_tests: + name: "K8s Load Tests" + runs-on: ${{ github.event.inputs.runner_label || 'avalanche-avalanchego-runner' }} steps: - uses: actions/checkout@v4 with: + ref: ${{ github.event.inputs.branch || github.ref }} fetch-depth: 0 - uses: ./.github/actions/setup-go-for-project - uses: ./.github/actions/install-nix - uses: ./.github/actions/run-monitored-tmpnet-cmd with: run: ./scripts/run_task.sh test-load - artifact_prefix: load + artifact_prefix: k8s-load-${{ github.run_number }} prometheus_username: ${{ secrets.PROMETHEUS_ID || '' }} prometheus_password: ${{ secrets.PROMETHEUS_PASSWORD || '' }} loki_username: ${{ secrets.LOKI_ID || '' }} loki_password: ${{ secrets.LOKI_PASSWORD || '' }} + +# Unit: +# runs-on: ${{ matrix.os }} +# strategy: +# fail-fast: false +# matrix: +# os: [macos-14, ubuntu-22.04, ubuntu-24.04, windows-2022, custom-arm64-jammy, custom-arm64-noble] +# steps: +# - uses: actions/checkout@v4 +# - uses: ./.github/actions/setup-go-for-project +# - name: Set timeout on Windows # Windows UT run slower and need a longer timeout +# shell: bash +# if: matrix.os == 'windows-2022' +# run: echo "TIMEOUT=240s" >> "$GITHUB_ENV" +# - name: test-unit +# shell: bash +# run: ./scripts/run_task.sh test-unit +# env: +# TIMEOUT: ${{ env.TIMEOUT }} +# Fuzz: +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v4 +# - uses: ./.github/actions/setup-go-for-project +# - name: test-fuzz +# shell: bash +# run: ./scripts/run_task.sh test-fuzz +# e2e: +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v4 +# - uses: ./.github/actions/setup-go-for-project +# - name: Run e2e tests +# uses: ./.github/actions/run-monitored-tmpnet-cmd +# with: +# run: ./scripts/run_task.sh test-e2e-ci +# artifact_prefix: e2e +# filter_by_owner: avalanchego-e2e +# prometheus_username: ${{ secrets.PROMETHEUS_ID || '' }} +# prometheus_password: ${{ secrets.PROMETHEUS_PASSWORD || '' }} +# loki_username: ${{ secrets.LOKI_ID || '' }} +# loki_password: ${{ secrets.LOKI_PASSWORD || '' }} +# e2e_post_granite: +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v4 +# - uses: ./.github/actions/setup-go-for-project +# - name: Run e2e tests +# uses: ./.github/actions/run-monitored-tmpnet-cmd +# with: +# run: ./scripts/run_task.sh test-e2e-ci -- --activate-granite +# artifact_prefix: e2e-post-granite +# filter_by_owner: avalanchego-e2e +# prometheus_username: ${{ secrets.PROMETHEUS_ID || '' }} +# prometheus_password: ${{ secrets.PROMETHEUS_PASSWORD || '' }} +# loki_username: ${{ secrets.LOKI_ID || '' }} +# loki_password: ${{ secrets.LOKI_PASSWORD || '' }} +# e2e_existing_network: +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v4 +# - uses: ./.github/actions/setup-go-for-project +# - name: Run e2e tests with existing network +# uses: ./.github/actions/run-monitored-tmpnet-cmd +# with: +# run: ./scripts/run_task.sh test-e2e-existing-ci +# artifact_prefix: e2e-existing-network +# prometheus_username: ${{ secrets.PROMETHEUS_ID || '' }} +# prometheus_password: ${{ secrets.PROMETHEUS_PASSWORD || '' }} +# loki_username: ${{ secrets.LOKI_ID || '' }} +# loki_password: ${{ secrets.LOKI_PASSWORD || '' }} +# Upgrade: +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v4 +# - uses: ./.github/actions/setup-go-for-project +# - name: Run e2e tests +# uses: ./.github/actions/run-monitored-tmpnet-cmd +# with: +# run: ./scripts/run_task.sh test-upgrade +# artifact_prefix: upgrade +# prometheus_username: ${{ secrets.PROMETHEUS_ID || '' }} +# prometheus_password: ${{ secrets.PROMETHEUS_PASSWORD || '' }} +# loki_username: ${{ secrets.LOKI_ID || '' }} +# loki_password: ${{ secrets.LOKI_PASSWORD || '' }} +# Lint: +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v4 +# - uses: ./.github/actions/setup-go-for-project +# - uses: ./.github/actions/install-nix +# - name: Runs all lint checks +# shell: nix develop --command bash -x {0} +# run: ./scripts/run_task.sh lint-all-ci +# buf-lint: +# name: Protobuf Lint +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v4 +# - uses: bufbuild/buf-action@dfda68eacb65895184c76b9ae522b977636a2c47 #v1.1.4 +# with: +# input: "proto" +# pr_comment: false +# # Breaking changes are managed by the rpcchainvm protocol version. +# breaking: false +# # buf-action defaults to pushing on non-fork branch pushes +# # which is never desirable for this job. The buf-push job is +# # responsible for pushes. +# push: false +# # This version should match the version installed in the nix dev shell +# version: 1.47.2 +# links-lint: +# name: Markdown Links Lint +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v4 +# - uses: umbrelladocs/action-linkspector@a0567ce1c7c13de4a2358587492ed43cab5d0102 #v1.3.4 +# with: +# fail_level: any +# check_generated_protobuf: +# name: Up-to-date protobuf +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v4 +# - uses: ./.github/actions/setup-go-for-project +# # Use the dev shell instead of bufbuild/buf-action to ensure the dev shell provides the expected versions +# - uses: ./.github/actions/install-nix +# - shell: nix develop --command bash -x {0} +# run: ./scripts/run_task.sh check-generate-protobuf +# check_mockgen: +# name: Up-to-date mocks +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v4 +# - uses: ./.github/actions/setup-go-for-project +# - shell: bash +# run: ./scripts/run_task.sh check-generate-mocks +# check_canotogen: +# name: Up-to-date canoto +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v4 +# - uses: ./.github/actions/setup-go-for-project +# - shell: bash +# run: ./scripts/run_task.sh check-generate-canoto +# go_mod_tidy: +# name: Up-to-date go.mod and go.sum +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v4 +# - uses: ./.github/actions/setup-go-for-project +# - shell: bash +# run: ./scripts/run_task.sh check-go-mod-tidy +# test_build_image: +# name: Image build +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v4 +# - name: Install qemu (required for cross-platform builds) +# run: | +# sudo apt update +# sudo apt -y install qemu-system qemu-user-static +# - name: Check image build +# shell: bash +# run: ./scripts/run_task.sh test-build-image +# test_build_antithesis_avalanchego_images: +# name: Build Antithesis avalanchego images +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v4 +# - uses: ./.github/actions/setup-go-for-project +# - name: Check image build for avalanchego test setup +# shell: bash +# run: ./scripts/run_task.sh test-build-antithesis-images-avalanchego +# test_build_antithesis_xsvm_images: +# name: Build Antithesis xsvm images +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v4 +# - uses: ./.github/actions/setup-go-for-project +# - name: Check image build for xsvm test setup +# shell: bash +# run: ./scripts/run_task.sh test-build-antithesis-images-xsvm +# e2e_bootstrap_monitor: +# name: Run bootstrap monitor e2e tests +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v4 +# - uses: ./.github/actions/setup-go-for-project +# - uses: ./.github/actions/install-nix +# - name: Run e2e tests +# shell: bash +# run: nix develop --command ./scripts/run_task.sh test-bootstrap-monitor-e2e +# load_tests: +# name: load tests +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v4 +# with: +# fetch-depth: 0 +# - uses: ./.github/actions/setup-go-for-project +# - uses: ./.github/actions/install-nix +# - uses: ./.github/actions/run-monitored-tmpnet-cmd +# with: +# run: ./scripts/run_task.sh test-load +# artifact_prefix: load +# prometheus_username: ${{ secrets.PROMETHEUS_ID || '' }} +# prometheus_password: ${{ secrets.PROMETHEUS_PASSWORD || '' }} +# loki_username: ${{ secrets.LOKI_ID || '' }} +# loki_password: ${{ secrets.LOKI_PASSWORD || '' }} diff --git a/tests/load/c/readme.md b/tests/load/c/readme.md index 785d9aa61566..e6103c590f0f 100644 --- a/tests/load/c/readme.md +++ b/tests/load/c/readme.md @@ -1,6 +1,6 @@ # Load testing -The C-chain load test entrypoint is in [load_test.go](load_test.go). +The C-chain load test entrypoint is in [main.go](main/main.go). It runs with 5 nodes and 5 "agents". @@ -23,12 +23,12 @@ There are more interesting metrics available from the tmpnet nodes being load te Finally, to run the load test, run: ```bash +# Install nix (skip this step if nix is already installed) +./scripts/run_task.sh install-nix # Start the dev shell nix develop # Start the load test task test-load -# If you don't have access to the Ava Labs CI monitoring stack, use: -task test-load-local ``` ## Visualize metrics in Grafana @@ -43,7 +43,7 @@ If you have the credentials (internal to Ava Labs) for the CI monitoring stack, nix develop ``` -1. Set your monitoring credentials using the credentials you can find in your password manager +2. Set your monitoring credentials using the credentials you can find in your password manager ```bash export PROMETHEUS_USERNAME= @@ -52,47 +52,18 @@ If you have the credentials (internal to Ava Labs) for the CI monitoring stack, export LOKI_PASSWORD= ``` -1. Run the load test: +3. Run the load test: ```bash task test-load ``` -1. Wait for the load test to finish, this will log out a URL at the end of the test, in the form +4. Prior to the test beginning, you will see the following log: ```log INFO metrics and logs available via grafana (collectors must be running) {"uri": "https://grafana-poc.avax-dev.network/d/eabddd1d-0a06-4ba1-8e68-a44504e37535/C-Chain%20Load?from=1747817500582&to=1747817952631&var-filter=network_uuid%7C%3D%7C4f419e3a-dba5-4ccd-b2fd-bda15f9826ff"} ``` -1. Open the URL in your browser, and log in with the Grafana credentials which you can find in your password manager. +5. Open the URL in your browser, and log in with the Grafana credentials which you can find in your password manager. For reference, see [the tmpnet monitoring section](../../fixture/tmpnet/README.md#monitoring) - -### Locally - -1. Create a directory `/yourpath` to store the Prometheus configuration file and its data. -1. Setup the Prometheus configuration file: `envsubst < tests/load/c/prometheus.template.yml > /yourpath/prometheus.yml` -1. Launch Prometheus using the dev shell: - - ```bash - nix develop - cd /yourpath - prometheus --config.file /yourpath/prometheus.yml - ``` - - This starts Prometheus listening on port `9090`. -1. In a separate terminal, install and launch the Grafana service. For example on MacOS: - - ```bash - brew install grafana - brew services start grafana - ``` - -1. Open Grafana in your browser at [localhost:3000](http://localhost:3000) and log in with the default credentials `admin` and `admin`. -1. Add a new Prometheus data source starting at [localhost:3000/connections/datasources/prometheus](http://localhost:3000/connections/datasources/prometheus) - 1. Click on "Add new data source" - 1. Name it `prometheus` - 1. In the Connection section, set the URL to `http://localhost:9090` - 1. Click the "Save & Test" button at the bottom to verify the connection. -1. Create a dashboard at [localhost:3000/dashboard/new?editview=json-model](http://localhost:3000/dashboard/new?editview=json-model) and paste the JSON content of [`dashboard.json`](https://github.com/ava-labs/avalanche-monitoring/blob/main/grafana/dashboards/c_chain_load.json) into the text area, and click "Save changes". -1. Open the Load testing dashboard at [localhost:3000/d/aejze3k4d0mpsb/load-testing](http://localhost:3000/d/aejze3k4d0mpsb/load-testing) From 42da393954bb22140762a7a0307130513adf1b51 Mon Sep 17 00:00:00 2001 From: Elvis S Date: Fri, 13 Jun 2025 18:24:50 +0400 Subject: [PATCH 197/197] test ARC runner