// Copyright 2018 The go-ethereum Authors // This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // The go-ethereum library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . // This file contains a miner stress test based on the Ethash consensus engine. package main import ( "crypto/ecdsa" "io/ioutil" "math/big" "math/rand" "os" "os/signal" "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/fdlimit" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/params" ) func main() { log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stderr, log.TerminalFormat(true)))) fdlimit.Raise(2048) // Generate a batch of accounts to seal and fund with faucets := make([]*ecdsa.PrivateKey, 128) for i := 0; i < len(faucets); i++ { faucets[i], _ = crypto.GenerateKey() } // Pre-generate the ethash mining DAG so we don't race ethash.MakeDataset(1, ethconfig.Defaults.Ethash.DatasetDir) // Create an Ethash network based off of the Ropsten config genesis := makeGenesis(faucets) // Handle interrupts. interruptCh := make(chan os.Signal, 5) signal.Notify(interruptCh, os.Interrupt) var ( stacks []*node.Node nodes []*eth.Ethereum enodes []*enode.Node ) for i := 0; i < 4; i++ { // Start the node and wait until it's up stack, ethBackend, err := makeMiner(genesis) if err != nil { panic(err) } defer stack.Close() for stack.Server().NodeInfo().Ports.Listener == 0 { time.Sleep(250 * time.Millisecond) } // Connect the node to all the previous ones for _, n := range enodes { stack.Server().AddPeer(n) } // Start tracking the node and its enode stacks = append(stacks, stack) nodes = append(nodes, ethBackend) enodes = append(enodes, stack.Server().Self()) } // Iterate over all the nodes and start mining time.Sleep(3 * time.Second) for _, node := range nodes { if err := node.StartMining(1); err != nil { panic(err) } } time.Sleep(3 * time.Second) // Start injecting transactions from the faucets like crazy nonces := make([]uint64, len(faucets)) for { // Stop when interrupted. select { case <-interruptCh: for _, node := range stacks { node.Close() } return default: } // Pick a random mining node index := rand.Intn(len(faucets)) backend := nodes[index%len(nodes)] // Create a self transaction and inject into the pool tx, err := types.SignTx(types.NewTransaction(nonces[index], crypto.PubkeyToAddress(faucets[index].PublicKey), new(big.Int), 21000, big.NewInt(100000000000+rand.Int63n(65536)), nil), types.HomesteadSigner{}, faucets[index]) if err != nil { panic(err) } if err := backend.TxPool().AddLocal(tx); err != nil { panic(err) } nonces[index]++ // Wait if we're too saturated if pend, _ := backend.TxPool().Stats(); pend > 2048 { time.Sleep(100 * time.Millisecond) } } } // makeGenesis creates a custom Ethash genesis block based on some pre-defined // faucet accounts. func makeGenesis(faucets []*ecdsa.PrivateKey) *core.Genesis { genesis := core.DefaultRopstenGenesisBlock() genesis.Difficulty = params.MinimumDifficulty genesis.GasLimit = 25000000 genesis.Config.ChainID = big.NewInt(18) genesis.Config.EIP150Hash = common.Hash{} genesis.Alloc = core.GenesisAlloc{} for _, faucet := range faucets { genesis.Alloc[crypto.PubkeyToAddress(faucet.PublicKey)] = core.GenesisAccount{ Balance: new(big.Int).Exp(big.NewInt(2), big.NewInt(128), nil), } } return genesis } func makeMiner(genesis *core.Genesis) (*node.Node, *eth.Ethereum, error) { // Define the basic configurations for the Ethereum node datadir, _ := ioutil.TempDir("", "") config := &node.Config{ Name: "geth", Version: params.Version, DataDir: datadir, P2P: p2p.Config{ ListenAddr: "0.0.0.0:0", NoDiscovery: true, MaxPeers: 25, }, UseLightweightKDF: true, } // Create the node and configure a full Ethereum node on it stack, err := node.New(config) if err != nil { return nil, nil, err } ethBackend, err := eth.New(stack, ðconfig.Config{ Genesis: genesis, NetworkId: genesis.Config.ChainID.Uint64(), SyncMode: downloader.FullSync, DatabaseCache: 256, DatabaseHandles: 256, TxPool: core.DefaultTxPoolConfig, GPO: ethconfig.Defaults.GPO, Ethash: ethconfig.Defaults.Ethash, Miner: miner.Config{ Etherbase: common.Address{1}, GasCeil: genesis.GasLimit * 11 / 10, GasPrice: big.NewInt(1), Recommit: time.Second, }, }) if err != nil { return nil, nil, err } err = stack.Start() return stack, ethBackend, err }