-
Notifications
You must be signed in to change notification settings - Fork 214
/
funder.go
125 lines (113 loc) · 4.36 KB
/
funder.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
// Copyright (C) 2023, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package load
import (
"context"
"crypto/ecdsa"
"fmt"
"math/big"
"github.com/ava-labs/subnet-evm/cmd/simulator/key"
"github.com/ava-labs/subnet-evm/cmd/simulator/metrics"
"github.com/ava-labs/subnet-evm/cmd/simulator/txs"
"github.com/ava-labs/subnet-evm/core/types"
"github.com/ava-labs/subnet-evm/ethclient"
"github.com/ava-labs/subnet-evm/params"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/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: params.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
}