| 
									
										
										
										
											2018-08-23 14:03:13 +03:00
										 |  |  | // 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 <http://www.gnu.org/licenses/>. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // +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" | 
					
						
							| 
									
										
										
										
											2018-11-07 09:55:56 +01:00
										 |  |  | 	"github.com/ethereum/go-ethereum/p2p/enode" | 
					
						
							| 
									
										
										
										
											2018-08-23 14:03:13 +03:00
										 |  |  | 	"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 | 
					
						
							| 
									
										
										
										
											2018-11-07 09:55:56 +01:00
										 |  |  | 		enodes []*enode.Node | 
					
						
							| 
									
										
										
										
											2018-08-23 14:03:13 +03:00
										 |  |  | 	) | 
					
						
							|  |  |  | 	for i := 0; i < 4; i++ { | 
					
						
							|  |  |  | 		// Start the node and wait until it's up | 
					
						
							| 
									
										
										
										
											2018-11-07 09:55:56 +01:00
										 |  |  | 		node, err := makeMiner(genesis) | 
					
						
							| 
									
										
										
										
											2018-08-23 14:03:13 +03:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			panic(err) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-02-07 11:40:36 +01:00
										 |  |  | 		defer node.Close() | 
					
						
							| 
									
										
										
										
											2018-08-23 14:03:13 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		for node.Server().NodeInfo().Ports.Listener == 0 { | 
					
						
							|  |  |  | 			time.Sleep(250 * time.Millisecond) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Connect the node to al the previous ones | 
					
						
							| 
									
										
										
										
											2018-11-07 09:55:56 +01:00
										 |  |  | 		for _, n := range enodes { | 
					
						
							|  |  |  | 			node.Server().AddPeer(n) | 
					
						
							| 
									
										
										
										
											2018-08-23 14:03:13 +03:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-11-07 09:55:56 +01:00
										 |  |  | 		// Start tracking the node and it's enode | 
					
						
							| 
									
										
										
										
											2018-08-23 14:03:13 +03:00
										 |  |  | 		nodes = append(nodes, node) | 
					
						
							| 
									
										
										
										
											2018-11-07 09:55:56 +01:00
										 |  |  | 		enodes = append(enodes, node.Server().Self()) | 
					
						
							| 
									
										
										
										
											2018-08-23 14:03:13 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// 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 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-07 09:55:56 +01:00
										 |  |  | func makeMiner(genesis *core.Genesis) (*node.Node, error) { | 
					
						
							| 
									
										
										
										
											2018-08-23 14:03:13 +03:00
										 |  |  | 	// 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, | 
					
						
							| 
									
										
										
										
											2019-04-23 15:08:51 +08:00
										 |  |  | 			Miner: Config{ | 
					
						
							|  |  |  | 				GasFloor: genesis.GasLimit * 9 / 10, | 
					
						
							|  |  |  | 				GasCeil:  genesis.GasLimit * 11 / 10, | 
					
						
							|  |  |  | 				GasPrice: big.NewInt(1), | 
					
						
							|  |  |  | 				Recommit: time.Second, | 
					
						
							|  |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2018-08-23 14:03:13 +03:00
										 |  |  | 		}) | 
					
						
							|  |  |  | 	}); err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Start the node and return if successful | 
					
						
							|  |  |  | 	return stack, stack.Start() | 
					
						
							|  |  |  | } |