From 48044b87af548e1623a4bfa24cbbc7c5dcbcfa8f Mon Sep 17 00:00:00 2001 From: Dan Moore <100106211+rebelArtists@users.noreply.github.com> Date: Mon, 20 Nov 2023 10:58:07 -0600 Subject: [PATCH] feature: bulk fund crypto wallets (#159) * feature: bulk fund crypto wallets * gofmt * fix: errant print statements * fix: add gen docs * fix: unnecessary pause * fix: pr feedback * fix: lint issues * fix verbosity flag and default gas value * fix docs * fix: auto detect chain id --------- Co-authored-by: dan moore --- README.md | 2 + cmd/fund/fund.go | 317 ++++++++++++++++++++++++++++++++++++++++++++ cmd/fund/usage.md | 10 ++ cmd/root.go | 2 + doc/polycli.md | 2 + doc/polycli_fund.md | 61 +++++++++ go.mod | 4 + go.sum | 34 +++++ 8 files changed, 432 insertions(+) create mode 100644 cmd/fund/fund.go create mode 100644 cmd/fund/usage.md create mode 100644 doc/polycli_fund.md diff --git a/README.md b/README.md index 639f8a65..333e3f24 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,8 @@ Note: Do not modify this section! It is auto-generated by `cobra` using `make ge - [polycli fork](doc/polycli_fork.md) - Take a forked block and walk up the chain to do analysis. +- [polycli fund](doc/polycli_fund.md) - Bulk fund many crypto wallets automatically. + - [polycli hash](doc/polycli_hash.md) - Provide common crypto hashing functions. - [polycli loadtest](doc/polycli_loadtest.md) - Run a generic load test against an Eth/EVM style JSON-RPC endpoint. diff --git a/cmd/fund/fund.go b/cmd/fund/fund.go new file mode 100644 index 00000000..295ebee5 --- /dev/null +++ b/cmd/fund/fund.go @@ -0,0 +1,317 @@ +package fund + +import ( + "crypto/ecdsa" + "encoding/hex" + "encoding/json" + "fmt" + "io/ioutil" + "math/big" + "net/http" + "os" + "strconv" + "strings" + "sync" + "time" + + _ "embed" + + "github.com/chenzhijie/go-web3" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" + "github.com/spf13/cobra" +) + +var ( + //go:embed usage.md + usage string + walletCount int + fundingWalletPK string + fundingWalletPublicKey *ecdsa.PublicKey + chainRPC string + concurrencyLevel int + walletFundingAmt float64 + walletFundingGas uint64 + nonceMutex sync.Mutex + globalNonce uint64 + nonceInitialized bool + outputFileFlag string +) + +// Wallet struct to hold public key, private key, and address +type Wallet struct { + PublicKey *ecdsa.PublicKey + PrivateKey *ecdsa.PrivateKey + Address common.Address +} + +func getChainIDFromNode(chainRPC string) (int64, error) { + // Create an HTTP client + client := &http.Client{} + + // Prepare the JSON-RPC request payload + payload := `{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}` + + // Create the HTTP request + req, err := http.NewRequest("POST", chainRPC, strings.NewReader(payload)) + if err != nil { + return 0, err + } + + // Set the required headers + req.Header.Set("Content-Type", "application/json") + + // Send the request + resp, err := client.Do(req) + if err != nil { + return 0, err + } + defer resp.Body.Close() + + // Read the response body + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return 0, err + } + + // Parse the JSON response + var result map[string]interface{} + if err := json.Unmarshal(body, &result); err != nil { + return 0, err + } + + // Extract the chain ID from the response + chainIDHex, ok := result["result"].(string) + if !ok { + return 0, fmt.Errorf("unable to extract chain ID from response") + } + + // Convert the chain ID from hex to int64 + int64ChainID, err := strconv.ParseInt(chainIDHex, 0, 64) + if err != nil { + return 0, err + } + + return int64ChainID, nil +} + +func generateNonce(web3Client *web3.Web3) (uint64, error) { + nonceMutex.Lock() + defer nonceMutex.Unlock() + + if nonceInitialized { + globalNonce++ + } else { + // Derive the public key from the funding wallet's private key + fundingWalletECDSA, ecdsaErr := crypto.HexToECDSA(fundingWalletPK) + if ecdsaErr != nil { + log.Error().Err(ecdsaErr).Msg("Error getting ECDSA from funding wallet private key") + return 0, ecdsaErr + } + + fundingWalletPublicKey = &fundingWalletECDSA.PublicKey + // Convert ecdsa.PublicKey to common.Address + fundingAddress := crypto.PubkeyToAddress(*fundingWalletPublicKey) + + nonce, err := web3Client.Eth.GetNonce(fundingAddress, nil) + if err != nil { + log.Error().Err(err).Msg("Error getting nonce") + return 0, err + } + globalNonce = nonce + nonceInitialized = true + } + + return globalNonce, nil +} + +func generateWallets(numWallets int) ([]Wallet, error) { + wallets := make([]Wallet, numWallets) + + for i := 0; i < numWallets; i++ { + account, err := crypto.GenerateKey() + if err != nil { + log.Error().Err(err).Msg("Error generating key") + return nil, err + } + + addr := crypto.PubkeyToAddress(account.PublicKey) + wallet := Wallet{ + PublicKey: &account.PublicKey, + PrivateKey: account, + Address: addr, + } + + wallets[i] = wallet + } + return wallets, nil +} + +func fundWallets(web3Client *web3.Web3, wallets []Wallet, amountWei *big.Int, walletFundingGas uint64, concurrency int) error { + // Create a channel to control concurrency + walletChan := make(chan Wallet, len(wallets)) + for _, wallet := range wallets { + walletChan <- wallet + } + close(walletChan) + + // Wait group to ensure all goroutines finish before returning + var wg sync.WaitGroup + wg.Add(concurrency) + + // Function to fund wallets + fundWallet := func() { + defer wg.Done() + for wallet := range walletChan { + nonce, err := generateNonce(web3Client) + if err != nil { + log.Error().Err(err).Msg("Error getting nonce") + return + } + + // Fund the wallet using the obtained nonce + _, err = web3Client.Eth.SyncSendRawTransaction( + wallet.Address, + amountWei, + nonce, + walletFundingGas, + web3Client.Utils.ToGWei(1), + nil, + ) + if err != nil { + log.Error().Err(err).Str("wallet", wallet.Address.Hex()).Msg("Error funding wallet") + return + } + + log.Info().Str("wallet", wallet.Address.Hex()).Msgf("Funded with %s wei", amountWei.String()) + } + } + + // Start funding the wallets concurrently + for i := 0; i < concurrency; i++ { + go fundWallet() + } + + // Wait for all goroutines to finish + wg.Wait() + return nil +} + +// fundCmd represents the fund command +var FundCmd = &cobra.Command{ + Use: "fund", + Short: "Bulk fund many crypto wallets automatically.", + Long: usage, + Run: func(cmd *cobra.Command, args []string) { + if err := runFunding(cmd); err != nil { + log.Error().Err(err).Msg("Error funding wallets") + } + }, +} + +func runFunding(cmd *cobra.Command) error { + // Capture the start time + startTime := time.Now() + + // Remove '0x' prefix from fundingWalletPK if present + fundingWalletPK = strings.TrimPrefix(fundingWalletPK, "0x") + + // setup new web3 session with remote rpc node + web3Client, clientErr := web3.NewWeb3(chainRPC) + if clientErr != nil { + cmd.PrintErrf("There was an error creating web3 client: %s", clientErr.Error()) + return clientErr + } + + // add pk to session for sending signed transactions + if setAcctErr := web3Client.Eth.SetAccount(fundingWalletPK); setAcctErr != nil { + cmd.PrintErrf("There was an error setting account with pk: %s", setAcctErr.Error()) + return setAcctErr + } + + // Query the chain ID from the rpc node + chainID, chainIDErr := getChainIDFromNode(chainRPC) + if chainIDErr != nil { + log.Error().Err(chainIDErr).Msg("Error getting chain ID") + return chainIDErr + } + + // Set proper chainId for corresponding chainRPC + web3Client.Eth.SetChainId(chainID) + + // generate set of new wallet objects + wallets, genWalletErr := generateWallets(walletCount) + if genWalletErr != nil { + cmd.PrintErrf("There was an error generating wallet objects: %s", genWalletErr.Error()) + return genWalletErr + } + + // fund all crypto wallets + log.Info().Msg("Starting to fund loadtest wallets...") + fundWalletErr := fundWallets(web3Client, wallets, big.NewInt(int64(walletFundingAmt*1e18)), uint64(walletFundingGas), concurrencyLevel) + if fundWalletErr != nil { + log.Error().Err(fundWalletErr).Msg("Error funding wallets") + return fundWalletErr + } + + // Save wallet details to a file + outputFile := outputFileFlag // You can modify the file format or name as needed + + type WalletDetails struct { + Address string `json:"Address"` + PrivateKey string `json:"PrivateKey"` + } + + walletDetails := make([]WalletDetails, len(wallets)) + for i, w := range wallets { + privateKey := hex.EncodeToString(w.PrivateKey.D.Bytes()) // Convert private key to hex + walletDetails[i] = WalletDetails{ + Address: w.Address.Hex(), + PrivateKey: privateKey, + } + } + + // Convert walletDetails to JSON + walletsJSON, jsonErr := json.MarshalIndent(walletDetails, "", " ") + if jsonErr != nil { + log.Error().Err(jsonErr).Msg("Error converting wallet details to JSON") + return jsonErr + } + + // Write JSON data to a file + file, createErr := os.Create(outputFile) + if createErr != nil { + log.Error().Err(createErr).Msg("Error creating file") + return createErr + } + defer file.Close() + + _, writeErr := file.Write(walletsJSON) + if writeErr != nil { + log.Error().Err(writeErr).Msg("Error writing wallet details to file") + return writeErr + } + + log.Info().Msgf("Wallet details have been saved to %s", outputFile) + + // Calculate the duration + duration := time.Since(startTime) + log.Info().Msgf("Total execution time: %s", duration) + + return nil +} + +func init() { + // Configure zerolog to output to os.Stdout + log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stdout}) + + FundCmd.Flags().IntVar(&walletCount, "wallet-count", 2, "Number of wallets to fund") + FundCmd.Flags().StringVar(&fundingWalletPK, "funding-wallet-pk", "", "Corresponding private key for funding wallet address, ensure you remove leading 0x") + FundCmd.Flags().StringVar(&chainRPC, "rpc-url", "http://localhost:8545", "The RPC endpoint url") + FundCmd.Flags().IntVar(&concurrencyLevel, "concurrency", 2, "Concurrency level for speeding up funding wallets") + FundCmd.Flags().Float64Var(&walletFundingAmt, "wallet-funding-amt", 0.05, "Amount to fund each wallet with") + FundCmd.Flags().Uint64Var(&walletFundingGas, "wallet-funding-gas", 100000, "Gas for each wallet funding transaction") + FundCmd.Flags().StringVar(&outputFileFlag, "output-file", "funded_wallets.json", "Specify the output JSON file name") +} diff --git a/cmd/fund/usage.md b/cmd/fund/usage.md new file mode 100644 index 00000000..e1b50942 --- /dev/null +++ b/cmd/fund/usage.md @@ -0,0 +1,10 @@ +```bash +$ polycli fund \ + --rpc-url="https://rootchain-devnetsub.zkevmdev.net" \ + --funding-wallet-pk="REPLACE" \ + --wallet-count=5 \ + --wallet-funding-amt=0.00015 \ + --wallet-funding-gas=50000 \ + --concurrency=5 \ + --output-file="/opt/funded_wallets.json" +``` \ No newline at end of file diff --git a/cmd/root.go b/cmd/root.go index c534d962..b2547f59 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -16,6 +16,7 @@ import ( "github.com/maticnetwork/polygon-cli/cmd/abi" "github.com/maticnetwork/polygon-cli/cmd/dumpblocks" "github.com/maticnetwork/polygon-cli/cmd/enr" + "github.com/maticnetwork/polygon-cli/cmd/fund" "github.com/maticnetwork/polygon-cli/cmd/hash" "github.com/maticnetwork/polygon-cli/cmd/loadtest" "github.com/maticnetwork/polygon-cli/cmd/metricsToDash" @@ -106,6 +107,7 @@ func NewPolycliCommand() *cobra.Command { abi.ABICmd, dumpblocks.DumpblocksCmd, fork.ForkCmd, + fund.FundCmd, hash.HashCmd, enr.ENRCmd, dbbench.DBBenchCmd, diff --git a/doc/polycli.md b/doc/polycli.md index c5047876..1ab64525 100644 --- a/doc/polycli.md +++ b/doc/polycli.md @@ -44,6 +44,8 @@ Polycli is a collection of tools that are meant to be useful while building, tes - [polycli fork](polycli_fork.md) - Take a forked block and walk up the chain to do analysis. +- [polycli fund](polycli_fund.md) - Bulk fund many crypto wallets automatically. + - [polycli hash](polycli_hash.md) - Provide common crypto hashing functions. - [polycli loadtest](polycli_loadtest.md) - Run a generic load test against an Eth/EVM style JSON-RPC endpoint. diff --git a/doc/polycli_fund.md b/doc/polycli_fund.md new file mode 100644 index 00000000..dc4848b0 --- /dev/null +++ b/doc/polycli_fund.md @@ -0,0 +1,61 @@ +# `polycli fund` + +> Auto-generated documentation. + +## Table of Contents + +- [Description](#description) +- [Usage](#usage) +- [Flags](#flags) +- [See Also](#see-also) + +## Description + +Bulk fund many crypto wallets automatically. + +```bash +polycli fund [flags] +``` + +## Usage + +```bash +$ polycli fund \ + --rpc-url="https://rootchain-devnetsub.zkevmdev.net" \ + --funding-wallet-pk="REPLACE" \ + --wallet-count=5 \ + --wallet-funding-amt=0.00015 \ + --wallet-funding-gas=50000 \ + --concurrency=5 \ + --output-file="/opt/funded_wallets.json" +``` +## Flags + +```bash + --concurrency int Concurrency level for speeding up funding wallets (default 2) + --funding-wallet-pk string Corresponding private key for funding wallet address, ensure you remove leading 0x + -h, --help help for fund + --output-file string Specify the output JSON file name (default "funded_wallets.json") + --rpc-url string The RPC endpoint url (default "http://localhost:8545") + --wallet-count int Number of wallets to fund (default 2) + --wallet-funding-amt float Amount to fund each wallet with (default 0.05) + --wallet-funding-gas uint Gas for each wallet funding transaction (default 100000) +``` + +The command also inherits flags from parent commands. + +```bash + --config string config file (default is $HOME/.polygon-cli.yaml) + --pretty-logs Should logs be in pretty format or JSON (default true) + -v, --verbosity int 0 - Silent + 100 Fatal + 200 Error + 300 Warning + 400 Info + 500 Debug + 600 Trace (default 400) +``` + +## See also + +- [polycli](polycli.md) - A Swiss Army knife of blockchain tools. diff --git a/go.mod b/go.mod index 753880b3..81518afc 100644 --- a/go.mod +++ b/go.mod @@ -44,11 +44,13 @@ require ( github.com/Microsoft/go-winio v0.6.1 // indirect github.com/StackExchange/wmi v1.2.1 // indirect github.com/VictoriaMetrics/fastcache v1.12.1 // indirect + github.com/andybalholm/brotli v1.0.4 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.7.0 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/chenzhijie/go-web3 v0.0.0-20230921142927-cd8f05f8d203 // indirect github.com/cockroachdb/errors v1.11.1 // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect github.com/cockroachdb/redact v1.1.5 // indirect @@ -124,6 +126,8 @@ require ( github.com/supranational/blst v0.3.11 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasthttp v1.41.0 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect go.opencensus.io v0.24.0 // indirect diff --git a/go.sum b/go.sum index 08bcf33f..a08f60d6 100644 --- a/go.sum +++ b/go.sum @@ -61,6 +61,8 @@ github.com/VictoriaMetrics/fastcache v1.12.1/go.mod h1:tX04vaqcNoQeGLD+ra5pU5sWk github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= +github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= +github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bits-and-blooms/bitset v1.7.0 h1:YjAGVd3XmtK9ktAbX8Zg2g2PwLIMjGREZJHlV4j7NEo= @@ -84,8 +86,11 @@ github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyY github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chenzhijie/go-web3 v0.0.0-20230921142927-cd8f05f8d203 h1:Y0ERn296o8ycmbqmnxONmNq2zvq39/votcf54Ql9o9M= +github.com/chenzhijie/go-web3 v0.0.0-20230921142927-cd8f05f8d203/go.mod h1:Ngj/3SKXcmcLheZuDTudxe/cvnl89m/3Rz39qrzAZiY= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -105,6 +110,8 @@ github.com/cockroachdb/pebble v0.0.0-20230906160148-46873a6a7a06 h1:T+Np/xtzIjYM github.com/cockroachdb/pebble v0.0.0-20230906160148-46873a6a7a06/go.mod h1:bynZ3gvVyhlvjLI7PT6dmZ7g76xzJ7HpxfjgkzCGz6s= github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2/go.mod h1:8BT+cPK6xvFOcRlk0R8eg+OTkcqI6baNH4xAkpiYVvQ= +github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= @@ -126,6 +133,8 @@ github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5il github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -136,6 +145,7 @@ github.com/ethereum/c-kzg-4844 v0.3.1 h1:sR65+68+WdnMKxseNWxSJuAv2tsUrihTpVBTfM/ github.com/ethereum/c-kzg-4844 v0.3.1/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= github.com/ethereum/go-ethereum v1.13.2 h1:g9mCpfPWqCA1OL4e6C98PeVttb0HadfBRuKTGvMnOvw= github.com/ethereum/go-ethereum v1.13.2/go.mod h1:gkQ5Ygi64ZBh9M/4iXY1R8WqoNCx1Ey0CkYn2BD4/fw= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= @@ -155,6 +165,8 @@ github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3Bop github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY= github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= @@ -164,6 +176,7 @@ github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= 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/v4 v4.3.0 h1:kHL1vqdqWNfATmA0FNMdmZNMyZI1U6O31X4rlIPoBog= github.com/golang-jwt/jwt/v4 v4.3.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -179,6 +192,7 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -273,13 +287,17 @@ github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+ github.com/jedib0t/go-pretty/v6 v6.4.8 h1:HiNzyMSEpsBaduKhmK+CwcpulEeBrTmxutz4oX/oWkg= github.com/jedib0t/go-pretty/v6 v6.4.8/go.mod h1:Ndk3ase2CkQbXLLNf5QDHoYb6J9WtVfmHZu9n8rk2xs= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= +github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= @@ -315,10 +333,12 @@ github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWV github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM= github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= @@ -328,6 +348,8 @@ github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8oh github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE= @@ -344,6 +366,8 @@ github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7B github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM= github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8= github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d h1:x3S6kxmy49zXVVyhcnrFqxvNVCBPb2KZ9hV2RBdS840= github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ= github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= @@ -450,6 +474,12 @@ github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2n github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= +github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.41.0 h1:zeR0Z1my1wDHTRiamBCXVglQdbUwgb9uWG3k1HQz6jY= +github.com/valyala/fasthttp v1.41.0/go.mod h1:f6VbjjoI3z1NDOZOv17o6RvtRSWxC77seBFc2uWtgiY= +github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= @@ -481,6 +511,7 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= @@ -557,6 +588,7 @@ golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220906165146-f3363e06e74c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -625,6 +657,7 @@ golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -636,6 +669,7 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=