// 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 . // +build none // 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" "path/filepath" "time" "github.com/ethereum/go-ethereum/accounts/keystore" "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/log" "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, filepath.Join(os.Getenv("HOME"), ".ethash")) // Create an Ethash network based off of the Ropsten config genesis := makeGenesis(faucets) var ( nodes []*node.Node enodes []*enode.Node ) for i := 0; i < 4; i++ { // Start the node and wait until it's up node, err := makeMiner(genesis) if err != nil { panic(err) } defer node.Close() for node.Server().NodeInfo().Ports.Listener == 0 { time.Sleep(250 * time.Millisecond) } // Connect the node to al the previous ones for _, n := range enodes { node.Server().AddPeer(n) } // Start tracking the node and it's enode nodes = append(nodes, node) enodes = append(enodes, node.Server().Self()) // Inject the signer key and start sealing with it store := node.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) if _, err := store.NewAccount(""); err != nil { panic(err) } } // Iterate over all the nodes and start signing with them time.Sleep(3 * time.Second) for _, node := range nodes { var ethereum *eth.Ethereum if err := node.Service(ðereum); err != nil { panic(err) } if err := ethereum.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 { index := rand.Intn(len(faucets)) // Fetch the accessor for the relevant signer var ethereum *eth.Ethereum if err := nodes[index%len(nodes)].Service(ðereum); err != nil { panic(err) } // 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 := ethereum.TxPool().AddLocal(tx); err != nil { panic(err) } nonces[index]++ // Wait if we're too saturated if pend, _ := ethereum.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.DefaultTestnetGenesisBlock() 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, 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, }, NoUSB: true, UseLightweightKDF: true, } // Start the node and configure a full Ethereum node on it stack, err := node.New(config) if err != nil { return nil, err } if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { return eth.New(ctx, ð.Config{ Genesis: genesis, NetworkId: genesis.Config.ChainID.Uint64(), SyncMode: downloader.FullSync, DatabaseCache: 256, DatabaseHandles: 256, TxPool: core.DefaultTxPoolConfig, GPO: eth.DefaultConfig.GPO, Ethash: eth.DefaultConfig.Ethash, Miner: Config{ GasFloor: genesis.GasLimit * 9 / 10, GasCeil: genesis.GasLimit * 11 / 10, GasPrice: big.NewInt(1), Recommit: time.Second, }, }) }); err != nil { return nil, err } // Start the node and return if successful return stack, stack.Start() }