mirror of
https://github.com/luxfi/evm.git
synced 2026-09-24 00:27:44 +00:00
125 lines
4.3 KiB
Go
125 lines
4.3 KiB
Go
// Copyright (C) 2023, Lux Industries, Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package load
|
|
|
|
import (
|
|
"context"
|
|
"crypto/ecdsa"
|
|
"fmt"
|
|
"math/big"
|
|
"github.com/luxfi/evm/cmd/simulator/key"
|
|
"github.com/luxfi/evm/cmd/simulator/metrics"
|
|
"github.com/luxfi/evm/cmd/simulator/txs"
|
|
"github.com/luxfi/evm/core/types"
|
|
"github.com/luxfi/evm/ethclient"
|
|
ethparams "github.com/luxfi/evm/params"
|
|
"github.com/luxfi/geth/common"
|
|
"github.com/luxfi/log"
|
|
)
|
|
|
|
// DistributeFunds ensures that each address in keys has at least [minFundsPerAddr] by sending funds
|
|
// from the key with the highest starting balance.
|
|
// This function returns a set of at least [numKeys] keys, each having a minimum balance [minFundsPerAddr].
|
|
func DistributeFunds(ctx context.Context, client ethclient.Client, keys []*key.Key, numKeys int, minFundsPerAddr *big.Int, m *metrics.Metrics) ([]*key.Key, error) {
|
|
if len(keys) < numKeys {
|
|
return nil, fmt.Errorf("insufficient number of keys %d < %d", len(keys), numKeys)
|
|
}
|
|
fundedKeys := make([]*key.Key, 0, numKeys)
|
|
// TODO: clean up fund distribution.
|
|
needFundsKeys := make([]*key.Key, 0)
|
|
needFundsAddrs := make([]common.Address, 0)
|
|
|
|
maxFundsKey := keys[0]
|
|
maxFundsBalance := common.Big0
|
|
log.Info("Checking balance of each key to distribute funds")
|
|
for _, key := range keys {
|
|
balance, err := client.BalanceAt(ctx, key.Address, nil)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to fetch balance for addr %s: %w", key.Address, err)
|
|
}
|
|
|
|
if balance.Cmp(minFundsPerAddr) < 0 {
|
|
needFundsKeys = append(needFundsKeys, key)
|
|
needFundsAddrs = append(needFundsAddrs, key.Address)
|
|
} else {
|
|
fundedKeys = append(fundedKeys, key)
|
|
}
|
|
|
|
if balance.Cmp(maxFundsBalance) > 0 {
|
|
maxFundsKey = key
|
|
maxFundsBalance = balance
|
|
}
|
|
}
|
|
requiredFunds := new(big.Int).Mul(minFundsPerAddr, big.NewInt(int64(numKeys)))
|
|
if maxFundsBalance.Cmp(requiredFunds) < 0 {
|
|
return nil, fmt.Errorf("insufficient funds to distribute %d < %d", maxFundsBalance, requiredFunds)
|
|
}
|
|
log.Info("Found max funded key", "address", maxFundsKey.Address, "balance", maxFundsBalance, "numFundAddrs", len(needFundsAddrs))
|
|
if len(fundedKeys) >= numKeys {
|
|
return fundedKeys[:numKeys], nil
|
|
}
|
|
|
|
// If there are not enough funded keys, cut [needFundsAddrs] to the number of keys that
|
|
// must be funded to reach [numKeys] required.
|
|
fundKeysCutLen := numKeys - len(fundedKeys)
|
|
needFundsKeys = needFundsKeys[:fundKeysCutLen]
|
|
needFundsAddrs = needFundsAddrs[:fundKeysCutLen]
|
|
|
|
chainID, err := client.ChainID(ctx)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to fetch chainID: %w", err)
|
|
}
|
|
gasFeeCap, err := client.EstimateBaseFee(ctx)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to fetch estimated base fee: %w", err)
|
|
}
|
|
gasTipCap, err := client.SuggestGasTipCap(ctx)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to fetch suggested gas tip: %w", err)
|
|
}
|
|
signer := types.LatestSignerForChainID(chainID)
|
|
|
|
// Generate a sequence of transactions to distribute the required funds.
|
|
log.Info("Generating distribution transactions...")
|
|
i := 0
|
|
txGenerator := func(key *ecdsa.PrivateKey, nonce uint64) (*types.Transaction, error) {
|
|
tx, err := types.SignNewTx(key, signer, &types.DynamicFeeTx{
|
|
ChainID: chainID,
|
|
Nonce: nonce,
|
|
GasTipCap: gasTipCap,
|
|
GasFeeCap: gasFeeCap,
|
|
Gas: ethparams.TxGas,
|
|
To: &needFundsAddrs[i],
|
|
Data: nil,
|
|
Value: requiredFunds,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
i++
|
|
return tx, nil
|
|
}
|
|
|
|
numTxs := uint64(len(needFundsAddrs))
|
|
txSequence, err := txs.GenerateTxSequence(ctx, txGenerator, client, maxFundsKey.PrivKey, numTxs, false)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to generate fund distribution sequence from %s of length %d", maxFundsKey.Address, len(needFundsAddrs))
|
|
}
|
|
worker := NewSingleAddressTxWorker(ctx, client, maxFundsKey.Address)
|
|
txFunderAgent := txs.NewIssueNAgent[*types.Transaction](txSequence, worker, numTxs, m)
|
|
|
|
if err := txFunderAgent.Execute(ctx); err != nil {
|
|
return nil, err
|
|
}
|
|
for _, addr := range needFundsAddrs {
|
|
balance, err := client.BalanceAt(ctx, addr, nil)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to fetch balance for addr %s: %w", addr, err)
|
|
}
|
|
log.Info("Funded address has balance", "addr", addr, "balance", balance)
|
|
}
|
|
fundedKeys = append(fundedKeys, needFundsKeys...)
|
|
return fundedKeys, nil
|
|
}
|