From 88aa3621dec566a2b5fe87e2ecb2e2fe3bb71128 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Thu, 22 Dec 2022 14:23:26 +0100 Subject: [PATCH] livefuzzer: improve cli (#14) * livefuzzer: improve cli * happy linter, happy life * README: updated instructions --- README.md | 6 +- cmd/livefuzzer/main.go | 209 +++++++++++++++++++++++++++++++---------- go.mod | 1 + go.sum | 2 + 4 files changed, 166 insertions(+), 52 deletions(-) diff --git a/README.md b/README.md index d496867..634fc99 100644 --- a/README.md +++ b/README.md @@ -17,5 +17,9 @@ Tx-fuzz allows for an optional seed parameter to get reproducible fuzz transacti You can optionally specify a seed parameter or a secret key to use as a faucet ``` -./livefuzzer spam [no-al] +./livefuzzer spam --seed --sk ``` + +You can set the RPC to use with `--rpc `. + +Some nodes (besu) don't have the `eth_createAccessList` RPC call, in this case it makes sense to disable accesslist creation with `--no-al`. diff --git a/cmd/livefuzzer/main.go b/cmd/livefuzzer/main.go index 04cba94..07c68ec 100644 --- a/cmd/livefuzzer/main.go +++ b/cmd/livefuzzer/main.go @@ -22,6 +22,7 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" + "github.com/urfave/cli/v2" ) var ( @@ -29,81 +30,134 @@ var ( txPerAccount = 1000 airdropValue = new(big.Int).Mul(big.NewInt(int64(txPerAccount*100000)), big.NewInt(params.GWei)) corpus [][]byte -) -func main() { - // eth.sendTransaction({from:personal.listAccounts[0], to:"0xb02A2EdA1b317FBd16760128836B0Ac59B560e9D", value: "100000000000000"}) - if len(os.Args) < 2 { - panic("invalid amount of args, need 2") + seedFlag = &cli.Int64Flag{ + Name: "seed", + Usage: "Seed for the RNG, (Default = RandomSeed)", + Value: 0, } - var ( - accesslist = true - seed *int64 - ) + skFlag = &cli.StringFlag{ + Name: "sk", + Usage: "Secret key", + Value: "0xcdfbe6f7602f67a97602e3e9fc24cde1cdffa88acd47745c0b84c5ff55891e1b", + } - var offset int - if len(os.Args) >= 3 { - a := common.LeftPadBytes(common.FromHex(os.Args[2]), 8) - s := int64(binary.BigEndian.Uint64(a)) - seed = &s - offset += 1 + corpusFlag = &cli.StringFlag{ + Name: "corpus", + Usage: "Use additional Corpus", } - if len(os.Args) > 3+offset && os.Args[3+offset] == "no-al" { - accesslist = false - offset += 1 + noALFlag = &cli.BoolFlag{ + Name: "no-al", + Usage: "Disable accesslist creation", + Value: false, } - if len(os.Args) > 3+offset { - txfuzz.SK = os.Args[3+offset] - sk := crypto.ToECDSAUnsafe(common.FromHex(txfuzz.SK)) - txfuzz.ADDR = crypto.PubkeyToAddress(sk.PublicKey).Hex() + countFlag = &cli.IntFlag{ + Name: "count", + Usage: "Count of addresses to create", + Value: 100, } - switch os.Args[1] { - case "airdrop": - airdrop(airdropValue) - case "spam": - for { - airdrop(airdropValue) - SpamTransactions(uint64(txPerAccount), false, accesslist, seed) - time.Sleep(10 * time.Second) - } - case "corpus": - cp, err := readCorpusElements(os.Args[2]) - if err != nil { - panic(err) - } - corpus = cp - SpamTransactions(uint64(txPerAccount), true, accesslist, seed) - case "unstuck": - unstuckTransactions() - case "send": - send() - case "create": - createAddresses(100) - default: - fmt.Println("unrecognized option") + rpcFlag = &cli.StringFlag{ + Name: "rpc", + Usage: "RPC provider", + Value: "http://127.0.0.1:8545", + } +) + +var airdropCommand = &cli.Command{ + Name: "airdrop", + Usage: "Airdrops to a list of accounts", + Action: runAirdrop, + Flags: []cli.Flag{ + skFlag, + rpcFlag, + }, +} + +var spamCommand = &cli.Command{ + Name: "spam", + Usage: "Send spam transactions", + Action: runSpam, + Flags: []cli.Flag{ + skFlag, + seedFlag, + noALFlag, + corpusFlag, + rpcFlag, + }, +} + +var unstuckCommand = &cli.Command{ + Name: "unstuck", + Usage: "Tries to unstuck an account", + Action: runUnstuck, + Flags: []cli.Flag{ + skFlag, + rpcFlag, + }, +} +var sendCommand = &cli.Command{ + Name: "send", + Usage: "Sends a single transaction", + Action: runSend, + Flags: []cli.Flag{ + skFlag, + rpcFlag, + }, +} +var createCommand = &cli.Command{ + Name: "create", + Usage: "Create ephemeral accounts", + Action: runCreate, + Flags: []cli.Flag{ + countFlag, + rpcFlag, + }, +} + +func initApp() *cli.App { + app := cli.NewApp() + app.Name = "tx-fuzz" + app.Usage = "Fuzzer for sending spam transactions" + app.Commands = []*cli.Command{ + airdropCommand, + spamCommand, + unstuckCommand, + sendCommand, + createCommand, + } + return app +} + +var app = initApp() + +func main() { + // eth.sendTransaction({from:personal.listAccounts[0], to:"0xb02A2EdA1b317FBd16760128836B0Ac59B560e9D", value: "100000000000000"}) + if err := app.Run(os.Args); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) } } -func SpamTransactions(N uint64, fromCorpus bool, accessList bool, seed *int64) { +func SpamTransactions(N uint64, fromCorpus bool, accessList bool, seed int64) { backend, _, err := getRealBackend() if err != nil { log.Warn("Could not get backend", "error", err) return } var src rand.Rand - if seed == nil { + if seed == 0 { fmt.Println("No seed provided, creating one") rnd := make([]byte, 8) crand.Read(rnd) s := int64(binary.BigEndian.Uint64(rnd)) - seed = &s + seed = s } - src = *rand.New(rand.NewSource(*seed)) - fmt.Printf("Spamming transactions with seed: 0x%x\n", *seed) + src = *rand.New(rand.NewSource(seed)) + fmt.Printf("Spamming transactions with seed: 0x%x\n", seed) // Now let everyone spam baikal transactions var wg sync.WaitGroup wg.Add(len(keys)) @@ -206,3 +260,56 @@ func send() { value := new(big.Int).Mul(big.NewInt(100000), big.NewInt(params.Ether)) sendTx(sk, client, to, value) } + +func runAirdrop(c *cli.Context) error { + setupDefaults(c) + airdrop(airdropValue) + return nil +} + +func runSpam(c *cli.Context) error { + setupDefaults(c) + noAL := c.Bool(noALFlag.Name) + seed := c.Int64(seedFlag.Name) + // Setup corpus if needed + if corpusFile := c.String(corpusFlag.Name); corpusFile != "" { + cp, err := readCorpusElements(corpusFile) + if err != nil { + panic(err) + } + corpus = cp + } + + for { + airdrop(airdropValue) + SpamTransactions(uint64(txPerAccount), false, !noAL, seed) + time.Sleep(10 * time.Second) + } +} + +func runUnstuck(c *cli.Context) error { + setupDefaults(c) + unstuckTransactions() + return nil +} + +func runSend(c *cli.Context) error { + setupDefaults(c) + send() + return nil +} + +func runCreate(c *cli.Context) error { + setupDefaults(c) + createAddresses(100) + return nil +} + +func setupDefaults(c *cli.Context) { + if sk := c.String(skFlag.Name); sk != "" { + txfuzz.SK = c.String(sk) + sk := crypto.ToECDSAUnsafe(common.FromHex(txfuzz.SK)) + txfuzz.ADDR = crypto.PubkeyToAddress(sk.PublicKey).Hex() + } + address = c.String(rpcFlag.Name) +} diff --git a/go.mod b/go.mod index c27945d..4aff82c 100644 --- a/go.mod +++ b/go.mod @@ -6,4 +6,5 @@ require ( github.com/MariusVanDerWijden/FuzzyVM v0.0.0-20221202121132-bd37e8fb1d0d github.com/ethereum/go-ethereum v1.10.26 github.com/holiman/goevmlab v0.0.0-20221207202144-89074274e1b7 + github.com/urfave/cli/v2 v2.23.7 // indirect ) diff --git a/go.sum b/go.sum index 7cd65ea..a83c98c 100644 --- a/go.sum +++ b/go.sum @@ -423,6 +423,8 @@ github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/X github.com/urfave/cli/v2 v2.10.2/go.mod h1:f8iq5LtQ/bLxafbdBSLPPNsgaW0l/2fYYEHhAyPlwvo= github.com/urfave/cli/v2 v2.23.5 h1:xbrU7tAYviSpqeR3X4nEFWUdB/uDZ6DE+HxmRU7Xtyw= github.com/urfave/cli/v2 v2.23.5/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= +github.com/urfave/cli/v2 v2.23.7 h1:YHDQ46s3VghFHFf1DdF+Sh7H4RqhcM+t0TmZRJx4oJY= +github.com/urfave/cli/v2 v2.23.7/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=