This commit is contained in:
Patrick O'Grady
2022-03-13 13:15:54 -07:00
parent dd9b639588
commit 4185e04994
2 changed files with 123 additions and 110 deletions
+12 -6
View File
@@ -12,6 +12,7 @@ import (
const (
endpointsKey = "endpoints"
chainIdKey = "chain-id"
baseFeeKey = "base-fee"
priorityFeeKey = "priority-fee"
concurrencyKey = "concurrency"
@@ -37,23 +38,28 @@ func main() {
if len(endpoints) == 0 {
log.Fatal("no available endpoints")
}
chainId := v.GetUint64(chainIdKey)
if chainId == 0 {
log.Fatal("chainID is 0")
}
concurrency := v.GetInt(concurrencyKey)
if concurrency == 0 {
log.Fatal("concurrency is 0")
}
baseFee := v.GetUint64(baseFeeKey)
if baseFee == 0 {
log.Fatal("base fee is 0")
}
// We allow a priority fee of 0, so we don't check this
priorityFee := v.GetUint64(priorityFeeKey)
concurrency := v.GetInt(concurrencyKey)
if concurrency == 0 {
log.Fatal("concurrency is 0")
}
log.Printf(
"starting simulator (endpoints=%v base fee=%d priority fee=%d concurrency=%d)\n",
"starting simulator (endpoints=%v chainID=%d concurrency=%d base fee=%d priority fee=%d)\n",
endpoints,
chainId,
baseFee,
priorityFee,
concurrency,
)
ctx := context.Background()
log.Fatal(worker.Run(ctx, endpoints, concurrency, baseFee, priorityFee))
log.Fatal(worker.Run(ctx, endpoints, chainId, concurrency, baseFee, priorityFee))
}
+111 -104
View File
@@ -17,20 +17,33 @@ import (
var (
transferGasLimit = uint64(21000)
transferGasPrice = big.NewInt(225 * params.GWei)
transferGas = new(big.Int).Mul(big.NewInt(int64(transferGasLimit)), transferGasPrice)
transferAmount = big.NewInt(1)
workDelay = time.Duration(100 * time.Millisecond)
retryDelay = time.Duration(500 * time.Millisecond)
transferBalance = new(big.Int).Add(transferGas, transferAmount)
requestBalance = new(big.Int).Mul(transferBalance, big.NewInt(50))
minFunderBalance = new(big.Int).Add(transferBalance, requestBalance)
chainID *big.Int
signer types.Signer
feeCap *big.Int
priorityFee *big.Int
chainID = big.NewInt(43112)
signer = types.LatestSignerForChainID(chainID)
workDelay = time.Duration(100 * time.Millisecond)
maxTransferCost *big.Int
requestAmount *big.Int
minFunderBalance *big.Int
)
func SetupVars(cID uint64, bFee uint64, pFee uint64) {
chainID = new(big.Int).SetUint64(cID)
signer = types.LatestSignerForChainID(chainID)
priorityFee = new(big.Int).SetUint64(pFee * params.GWei)
feeCap = new(big.Int).Add(new(big.Int).SetUint64(bFee*params.GWei), priorityFee)
maxTransferCost = new(big.Int).Mul(new(big.Int).SetUint64(transferGasLimit), feeCap)
maxTransferCost = new(big.Int).Add(maxTransferCost, transferAmount)
requestAmount = new(big.Int).Mul(maxTransferCost, big.NewInt(100))
minFunderBalance = new(big.Int).Add(maxTransferCost, requestAmount)
}
func CreateWorkers(ctx context.Context, keys []*Key, endpoints []string, desiredWorkers int) (*Worker, []*Worker, error) {
var master *Worker
var workers []*Worker
@@ -39,12 +52,14 @@ func CreateWorkers(ctx context.Context, keys []*Key, endpoints []string, desired
if err != nil {
return nil, nil, err
}
if err := worker.FetchBalance(ctx); err != nil {
return nil, nil, err
}
if err := worker.FetchNonce(ctx); err != nil {
return nil, nil, fmt.Errorf("could not get nonce: %w", err)
}
log.Printf("loaded worker %s (balance=%s nonce=%d)\n", worker.k.addr.Hex(), worker.balance.String(), worker.nonce)
log.Printf("loaded worker %s with balance %s\n", worker.k.addr.Hex(), worker.balance.String())
switch {
case master == nil:
master = worker
@@ -81,6 +96,7 @@ type Worker struct {
k *Key
balance *big.Int
nonce uint64
}
func New(k *Key, endpoint string) (*Worker, error) {
@@ -104,21 +120,40 @@ func New(k *Key, endpoint string) (*Worker, error) {
c: client,
k: k,
balance: big.NewInt(0),
nonce: 0,
}, nil
}
func (w *Worker) FetchBalance(ctx context.Context) error {
balance, err := w.c.BalanceAt(ctx, w.k.addr, nil)
if err != nil {
return fmt.Errorf("could not get balance: %w", err)
for ctx.Err() == nil {
balance, err := w.c.BalanceAt(ctx, w.k.addr, nil)
if err != nil {
log.Printf("could not get balance: %s\n", err.Error())
time.Sleep(retryDelay)
continue
}
w.balance = balance
return nil
}
return ctx.Err()
}
w.balance = balance
return nil
func (w *Worker) FetchNonce(ctx context.Context) error {
for ctx.Err() == nil {
nonce, err := w.c.PendingNonceAt(ctx, w.k.addr)
if err != nil {
log.Printf("could not get nonce: %s\n", err.Error())
time.Sleep(retryDelay)
continue
}
w.nonce = nonce
return nil
}
return ctx.Err()
}
func (w *Worker) waitForBalance(ctx context.Context, stdout bool, minBalance *big.Int) error {
for {
for ctx.Err() == nil {
if err := w.FetchBalance(ctx); err != nil {
return fmt.Errorf("could not get balance: %w", err)
}
@@ -134,71 +169,70 @@ func (w *Worker) waitForBalance(ctx context.Context, stdout bool, minBalance *bi
}
time.Sleep(5 * time.Second)
}
return ctx.Err()
}
func (w *Worker) Work(ctx context.Context, availableWorkers []*Worker, fundRequest chan common.Address) error {
nonce, err := w.c.PendingNonceAt(ctx, w.k.addr)
if err != nil {
return fmt.Errorf("could not get nonce: %w", err)
}
func (w *Worker) sendTx(ctx context.Context, recipient common.Address, value *big.Int) error {
for ctx.Err() == nil {
if new(big.Int).Sub(w.balance, transferBalance).Sign() < 0 {
log.Printf("%s requesting funds from master\n", w.k.addr.Hex())
fundRequest <- w.k.addr
if err := w.waitForBalance(ctx, false, transferBalance); err != nil {
return fmt.Errorf("could not get minimum balance: %w", err)
}
}
recipient := availableWorkers[rand.Intn(len(availableWorkers))]
if recipient.k.addr == w.k.addr {
continue
}
tx := types.NewTx(&types.DynamicFeeTx{
ChainID: chainID,
Nonce: nonce,
To: &recipient.k.addr,
Gas: transferGasLimit,
// TODO: replace
GasFeeCap: transferGasPrice,
GasTipCap: common.Big0,
ChainID: chainID,
Nonce: w.nonce,
To: &recipient,
Gas: transferGasLimit,
GasFeeCap: feeCap,
GasTipCap: priorityFee,
Value: value,
Data: []byte{},
})
signedTx, err := types.SignTx(tx, signer, w.k.pk)
if err != nil {
return fmt.Errorf("failed to sign transaction: %w", err)
}
if err := w.c.SendTransaction(ctx, signedTx); err != nil {
// return fmt.Errorf("unable to send tx: %w", err)
time.Sleep(workDelay)
log.Printf("failed to sign transaction: %s", err.Error())
time.Sleep(retryDelay)
continue
}
log.Printf("%s broadcasted transaction: %s\n", w.k.addr.Hex(), signedTx.Hash().Hex())
if err := w.confirmTransaction(ctx, nonce); err != nil {
return fmt.Errorf("unable to confirm %s: %w", signedTx.Hash().Hex(), err)
if err := w.c.SendTransaction(ctx, signedTx); err != nil {
log.Printf("failed to send transaction: %s", err.Error())
time.Sleep(retryDelay)
continue
}
txHash := signedTx.Hash()
log.Printf("%s broadcasted transaction: %s\n", w.k.addr.Hex(), txHash.Hex())
cost, err := w.confirmTransaction(ctx, txHash)
if err != nil {
log.Printf("failed to confirm %s: %s", txHash.Hex(), err.Error())
time.Sleep(retryDelay)
continue
}
w.nonce++
w.balance = new(big.Int).Sub(w.balance, cost)
w.balance = new(big.Int).Sub(w.balance, transferAmount)
return nil
}
return ctx.Err()
}
nonce++
// TODO: need to pull receipt to see how much went
w.balance = new(big.Int).Sub(w.balance, transferBalance)
func (w *Worker) Work(ctx context.Context, availableWorkers []*Worker, fundRequest chan common.Address) error {
for ctx.Err() == nil {
if new(big.Int).Sub(w.balance, maxTransferCost).Sign() < 0 {
log.Printf("%s requesting funds from master\n", w.k.addr.Hex())
fundRequest <- w.k.addr
if err := w.waitForBalance(ctx, false, maxTransferCost); err != nil {
return fmt.Errorf("could not get balance: %w", err)
}
}
recipient := availableWorkers[rand.Intn(len(availableWorkers))]
if recipient.k.addr == w.k.addr {
continue
}
if err := w.sendTx(ctx, recipient.k.addr, transferAmount); err != nil {
return err
}
time.Sleep(workDelay)
}
return ctx.Err()
}
func (w *Worker) Fund(ctx context.Context, fundRequest chan common.Address) error {
nonce, err := w.c.PendingNonceAt(ctx, w.k.addr)
if err != nil {
return fmt.Errorf("could not get nonce: %w", err)
}
for {
select {
case recipient := <-fundRequest:
@@ -207,62 +241,35 @@ func (w *Worker) Fund(ctx context.Context, fundRequest chan common.Address) erro
return fmt.Errorf("could not get minimum balance: %w", err)
}
}
// TODO: unify to single tx func
tx := types.NewTransaction(
nonce,
recipient,
requestBalance,
transferGasLimit,
transferGasPrice,
nil,
)
signedTx, err := types.SignTx(tx, signer, w.k.pk)
if err != nil {
return fmt.Errorf("failed to sign transaction: %w", err)
}
if err := w.c.SendTransaction(ctx, signedTx); err != nil {
if err := w.sendTx(ctx, recipient, requestAmount); err != nil {
return fmt.Errorf("unable to send tx: %w", err)
}
log.Printf("broadcasted funding transaction: %s\n", signedTx.Hash().Hex())
if err := w.confirmTransaction(ctx, nonce); err != nil {
return fmt.Errorf("unable to confirm %s: %w", signedTx.Hash().Hex(), err)
}
nonce++
w.balance = new(big.Int).Sub(w.balance, requestBalance)
w.balance = new(big.Int).Sub(w.balance, transferGas)
case <-ctx.Done():
return ctx.Err()
}
}
}
func (w *Worker) confirmTransaction(ctx context.Context, broadcastNonce uint64) error {
func (w *Worker) confirmTransaction(ctx context.Context, tx common.Hash) (*big.Int, error) {
for ctx.Err() == nil {
// TODO: get receipt
nonce, err := w.c.NonceAt(ctx, w.k.addr, nil)
if err == nil && nonce >= broadcastNonce {
return nil
result, pending, _ := w.c.TransactionByHash(ctx, tx)
if result == nil || pending {
time.Sleep(retryDelay)
continue
}
time.Sleep(2 * time.Second)
continue
return result.Cost(), nil
}
return ctx.Err()
return nil, ctx.Err()
}
func Run(ctx context.Context, endpoints []string, cWorkers int, maxBaseFee uint64, maxPriorityFee uint64) error {
rpks, err := LoadAvailableKeys(ctx)
func Run(ctx context.Context, endpoints []string, chainId uint64, concurrency int, baseFee uint64, priorityFee uint64) error {
SetupVars(chainId, baseFee, priorityFee)
ks, err := LoadAvailableKeys(ctx)
if err != nil {
return fmt.Errorf("unable to load keys: %w", err)
}
master, workers, err := CreateWorkers(ctx, rpks, endpoints, cWorkers)
master, workers, err := CreateWorkers(ctx, ks, endpoints, concurrency)
if err != nil {
return fmt.Errorf("unable to load available workers: %w", err)
}