Skip to content

Commit

Permalink
cmd/livefuzzer: big refactor of the livefuzzer (#37)
Browse files Browse the repository at this point in the history
* cmd/livefuzzer: big refactor of the livefuzzer

* cmd/livefuzzer: cleanup

* cmd/livefuzzer: happy lint, happy life
  • Loading branch information
MariusVanDerWijden committed Oct 8, 2023
1 parent 6dee627 commit 241bebe
Show file tree
Hide file tree
Showing 8 changed files with 254 additions and 270 deletions.
12 changes: 5 additions & 7 deletions cmd/livefuzzer/addresslist.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"fmt"
"math/big"

txfuzz "github.com/MariusVanDerWijden/tx-fuzz"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
Expand All @@ -14,7 +13,7 @@ import (
)

var (
keys = []string{
staticKeys = []string{
"0xaf5ead4413ff4b78bc94191a2926ae9ccbec86ce099d65aaf469e9eb1a0fa87f",
"0xe63135ee5310c0b34c551e4683ad926dce90062b15e43275f9189b0f29bc784c",
"0xc216a7b5048e6ea2437b20bc9c7f9a57cc8aefd5aaf6a991c4db407218ed9e77",
Expand Down Expand Up @@ -244,10 +243,9 @@ func createAddresses(N int) ([]string, []string) {
return keys, addrs
}

func airdrop(value *big.Int) error {
client, sk, _ := getRealBackend()
backend := ethclient.NewClient(client)
sender := common.HexToAddress(txfuzz.ADDR)
func airdrop(config *Config, value *big.Int) error {
backend := ethclient.NewClient(config.backend)
sender := crypto.PubkeyToAddress(config.faucet.PublicKey)
var tx *types.Transaction
chainid, err := backend.ChainID(context.Background())
if err != nil {
Expand All @@ -263,7 +261,7 @@ func airdrop(value *big.Int) error {
to := common.HexToAddress(addr)
gp, _ := backend.SuggestGasPrice(context.Background())
tx2 := types.NewTransaction(nonce, to, value, 21000, gp, nil)
signedTx, _ := types.SignTx(tx2, types.LatestSignerForChainID(chainid), sk)
signedTx, _ := types.SignTx(tx2, types.LatestSignerForChainID(chainid), config.faucet)
if err := backend.SendTransaction(context.Background(), signedTx); err != nil {
fmt.Printf("error sending transaction; could not airdrop: %v\n", err)
return err
Expand Down
84 changes: 5 additions & 79 deletions cmd/livefuzzer/basic.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,110 +3,36 @@ package main
import (
"context"
"crypto/ecdsa"
crand "crypto/rand"
"encoding/binary"
"fmt"
"math/big"
"math/rand"
"sync"
"time"

"github.com/MariusVanDerWijden/FuzzyVM/filler"
txfuzz "github.com/MariusVanDerWijden/tx-fuzz"
"github.com/MariusVanDerWijden/tx-fuzz/mutator"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rpc"
)

func setup(backend *rpc.Client, seed int64, N uint64) (int64, *mutator.Mutator, uint64) {
// Setup seed
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
}

// Setup N
client := ethclient.NewClient(backend)
header, err := client.HeaderByNumber(context.Background(), nil)
if err != nil {
panic(err)
}
if N == 0 {
txPerBlock := header.GasLimit / uint64(defaultGas)
txPerAccount := txPerBlock / uint64(len(keys))
N = txPerAccount
if N == 0 {
N = 1
}
}

mut := mutator.NewMutator(rand.New(rand.NewSource(seed)))
return seed, mut, N
}

func SpamBasicTransactions(N uint64, fromCorpus bool, accessList bool, seed int64) {
backend, _, err := getRealBackend()
if err != nil {
log.Warn("Could not get backend", "error", err)
return
}

// Set up the randomness
seed, mut, N := setup(backend, seed, N)
random := make([]byte, 10000)
mut.FillBytes(&random)

fmt.Printf("Spamming %v transactions per account on %v accounts with seed: 0x%x\n", N, len(keys), seed)
// Now let everyone spam baikal transactions
var wg sync.WaitGroup
wg.Add(len(keys))
for i := range keys {
var f *filler.Filler
if fromCorpus {
elem := corpus[rand.Int31n(int32(len(corpus)))]
mut.MutateBytes(&elem)
f = filler.NewFiller(elem)
} else {
// Use lower entropy randomness for filler
mut.MutateBytes(&random)
f = filler.NewFiller(random)
}
// Start a fuzzing thread
go func(key, addr string, filler *filler.Filler) {
defer wg.Done()
sk := crypto.ToECDSAUnsafe(common.FromHex(key))
SendBasicTransactions(backend, sk, f, addr, N, accessList)
}(keys[i], addrs[i], f)
}
wg.Wait()
}

func SendBasicTransactions(client *rpc.Client, key *ecdsa.PrivateKey, f *filler.Filler, addr string, N uint64, al bool) {
backend := ethclient.NewClient(client)

sender := common.HexToAddress(addr)
func SendBasicTransactions(config *Config, key *ecdsa.PrivateKey, f *filler.Filler) {
backend := ethclient.NewClient(config.backend)
sender := crypto.PubkeyToAddress(key.PublicKey)
chainID, err := backend.ChainID(context.Background())
if err != nil {
log.Warn("Could not get chainID, using default")
chainID = big.NewInt(0x01000666)
}

var lastTx *types.Transaction
for i := uint64(0); i < N; i++ {
for i := uint64(0); i < config.n; i++ {
nonce, err := backend.NonceAt(context.Background(), sender, big.NewInt(-1))
if err != nil {
log.Warn("Could not get nonce: %v", nonce)
continue
}
tx, err := txfuzz.RandomValidTx(client, f, sender, nonce, nil, nil, al)
tx, err := txfuzz.RandomValidTx(config.backend, f, sender, nonce, nil, nil, config.accessList)
if err != nil {
log.Warn("Could not create valid tx: %v", nonce)
continue
Expand Down
52 changes: 6 additions & 46 deletions cmd/livefuzzer/blob.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,76 +5,36 @@ import (
"crypto/ecdsa"
"fmt"
"math/big"
"math/rand"
"strings"
"sync"
"time"

"github.com/MariusVanDerWijden/FuzzyVM/filler"
txfuzz "github.com/MariusVanDerWijden/tx-fuzz"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rpc"
)

func SpamBlobTransactions(N uint64, fromCorpus bool, accessList bool, seed int64) {
backend, _, err := getRealBackend()
if err != nil {
log.Warn("Could not get backend", "error", err)
return
}
// Set up the randomness
random := make([]byte, 10000)
seed, mut, N := setup(backend, seed, N)

fmt.Printf("Spamming %v blob transactions per account on %v accounts with seed: 0x%x\n", N, len(keys), seed)
// Now let everyone spam blob transactions
var wg sync.WaitGroup
wg.Add(len(keys))
for i := range keys {
var f *filler.Filler
if fromCorpus {
elem := corpus[rand.Int31n(int32(len(corpus)))]
mut.MutateBytes(&elem)
f = filler.NewFiller(elem)
} else {
// Use lower entropy randomness for filler
mut.MutateBytes(&random)
f = filler.NewFiller(random)
}
// Start a fuzzing thread
go func(key, addr string, filler *filler.Filler) {
defer wg.Done()
sk := crypto.ToECDSAUnsafe(common.FromHex(key))
SendBlobTransactions(backend, sk, f, addr, N, accessList)
}(keys[i], addrs[i], f)
}
wg.Wait()
}

func SendBlobTransactions(client *rpc.Client, key *ecdsa.PrivateKey, f *filler.Filler, addr string, N uint64, al bool) {
backend := ethclient.NewClient(client)

sender := common.HexToAddress(addr)
func SendBlobTransactions(config *Config, key *ecdsa.PrivateKey, f *filler.Filler) {
backend := ethclient.NewClient(config.backend)
sender := crypto.PubkeyToAddress(key.PublicKey)
chainID, err := backend.ChainID(context.Background())
if err != nil {
log.Warn("Could not get chainID, using default")
chainID = big.NewInt(0x01000666)
}

var lastTx *types.Transaction
for i := uint64(0); i < N; i++ {
for i := uint64(0); i < config.n; i++ {
nonce, err := backend.NonceAt(context.Background(), sender, big.NewInt(-1))
if err != nil {
log.Warn("Could not get nonce: %v", nonce)
continue
}
tx, err := txfuzz.RandomBlobTx(client, f, sender, nonce, nil, nil, al)
tx, err := txfuzz.RandomBlobTx(config.backend, f, sender, nonce, nil, nil, config.accessList)
if err != nil {
log.Warn("Could not create valid tx: %v", nonce)
continue
Expand All @@ -88,7 +48,7 @@ func SendBlobTransactions(client *rpc.Client, key *ecdsa.PrivateKey, f *filler.F
if err != nil {
panic(err)
}
if err := client.CallContext(context.Background(), nil, "eth_sendRawTransaction", hexutil.Encode(rlpData)); err != nil {
if err := config.backend.CallContext(context.Background(), nil, "eth_sendRawTransaction", hexutil.Encode(rlpData)); err != nil {
if strings.Contains(err.Error(), "account limit exceeded") {
// Back off for a bit if we send a lot of transactions at once
time.Sleep(1 * time.Minute)
Expand Down
137 changes: 137 additions & 0 deletions cmd/livefuzzer/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package main

import (
"context"
"crypto/ecdsa"
crand "crypto/rand"
"encoding/binary"
"fmt"
"math/rand"
"os"

txfuzz "github.com/MariusVanDerWijden/tx-fuzz"
"github.com/MariusVanDerWijden/tx-fuzz/mutator"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/rpc"
"github.com/urfave/cli/v2"
)

type Config struct {
backend *rpc.Client // connection to the rpc provider

n uint64 // number of transactions send per account
faucet *ecdsa.PrivateKey // private key of the faucet account
keys []*ecdsa.PrivateKey // private keys of accounts
corpus [][]byte // optional corpus to use elements from
accessList bool // whether to create accesslist transactions
gasLimit uint64 // gas limit per transaction

seed int64 // seed used for generating randomness
mut *mutator.Mutator // Mutator based on the seed
}

func NewConfigFromContext(c *cli.Context) (*Config, error) {
// Setup RPC
rpcAddr := c.String(rpcFlag.Name)
backend, err := rpc.Dial(rpcAddr)
if err != nil {
return nil, err
}

// Setup faucet
faucet := crypto.ToECDSAUnsafe(common.FromHex(txfuzz.SK))
if sk := c.String(skFlag.Name); sk != "" {
faucet, err = crypto.ToECDSA(common.FromHex(sk))
if err != nil {
return nil, err
}
}

// Setup Keys
var keys []*ecdsa.PrivateKey
nKeys := c.Int(countFlag.Name)
if nKeys == 0 || nKeys > len(staticKeys) {
fmt.Printf("Sanitizing count flag from %v to %v\n", nKeys, len(staticKeys))
nKeys = len(staticKeys)
}
for i := 0; i < nKeys; i++ {
keys = append(keys, crypto.ToECDSAUnsafe(common.FromHex(staticKeys[i])))
}

// Setup gasLimit
gasLimit := c.Int(gasLimitFlag.Name)

// Setup N
N := c.Int(txCountFlag.Name)
if N == 0 {
N, err = setupN(backend, len(keys), gasLimit)
if err != nil {
return nil, err
}
}

// Setup seed
seed := c.Int64(seedFlag.Name)
if seed == 0 {
fmt.Println("No seed provided, creating one")
rnd := make([]byte, 8)
crand.Read(rnd)
seed = int64(binary.BigEndian.Uint64(rnd))
}

// Setup Mutator
mut := mutator.NewMutator(rand.New(rand.NewSource(seed)))

// Setup corpus
var corpus [][]byte
if corpusFile := c.String(corpusFlag.Name); corpusFile != "" {
corpus, err = readCorpusElements(corpusFile)
if err != nil {
return nil, err
}
}

return &Config{
backend: backend,
n: uint64(N),
faucet: faucet,
accessList: !c.Bool(noALFlag.Name),
gasLimit: uint64(gasLimit),
seed: seed,
keys: keys,
corpus: corpus,
mut: mut,
}, nil
}

func setupN(backend *rpc.Client, keys int, gasLimit int) (int, error) {
client := ethclient.NewClient(backend)
header, err := client.HeaderByNumber(context.Background(), nil)
if err != nil {
return 0, err
}
txPerBlock := int(header.GasLimit / uint64(gasLimit))
txPerAccount := txPerBlock / keys
if txPerAccount == 0 {
return 1, nil
}
return txPerAccount, nil
}

func readCorpusElements(path string) ([][]byte, error) {
stats, err := os.ReadDir(path)
if err != nil {
return nil, err
}
corpus := make([][]byte, 0, len(stats))
for _, file := range stats {
b, err := os.ReadFile(fmt.Sprintf("%v/%v", path, file.Name()))
if err != nil {
return nil, err
}
corpus = append(corpus, b)
}
return corpus, nil
}
Loading

0 comments on commit 241bebe

Please sign in to comment.