main.go 6.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
// 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/>.

// This file contains a miner stress test based on the Clique consensus engine.
package main

import (
	"bytes"
	"crypto/ecdsa"
	"io/ioutil"
	"math/big"
	"math/rand"
	"os"
	"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/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"
37
	"github.com/ethereum/go-ethereum/eth/ethconfig"
38
	"github.com/ethereum/go-ethereum/log"
39
	"github.com/ethereum/go-ethereum/miner"
40 41
	"github.com/ethereum/go-ethereum/node"
	"github.com/ethereum/go-ethereum/p2p"
G
gary rong 已提交
42
	"github.com/ethereum/go-ethereum/p2p/enode"
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
	"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()
	}
	sealers := make([]*ecdsa.PrivateKey, 4)
	for i := 0; i < len(sealers); i++ {
		sealers[i], _ = crypto.GenerateKey()
	}
	// Create a Clique network based off of the Rinkeby config
	genesis := makeGenesis(faucets, sealers)

	var (
R
rene 已提交
63
		nodes  []*eth.Ethereum
G
gary rong 已提交
64
		enodes []*enode.Node
65
	)
R
rene 已提交
66

67 68
	for _, sealer := range sealers {
		// Start the node and wait until it's up
R
rene 已提交
69
		stack, ethBackend, err := makeSealer(genesis)
70 71 72
		if err != nil {
			panic(err)
		}
R
rene 已提交
73
		defer stack.Close()
74

R
rene 已提交
75
		for stack.Server().NodeInfo().Ports.Listener == 0 {
76 77
			time.Sleep(250 * time.Millisecond)
		}
R
rene 已提交
78
		// Connect the node to all the previous ones
G
gary rong 已提交
79
		for _, n := range enodes {
R
rene 已提交
80
			stack.Server().AddPeer(n)
81
		}
R
rene 已提交
82 83 84
		// Start tracking the node and its enode
		nodes = append(nodes, ethBackend)
		enodes = append(enodes, stack.Server().Self())
85 86

		// Inject the signer key and start sealing with it
R
rene 已提交
87
		store := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
88 89 90 91 92 93 94 95 96
		signer, err := store.ImportECDSA(sealer, "")
		if err != nil {
			panic(err)
		}
		if err := store.Unlock(signer, ""); err != nil {
			panic(err)
		}
	}

R
rene 已提交
97 98
	// Iterate over all the nodes and start signing on them
	time.Sleep(3 * time.Second)
99
	for _, node := range nodes {
R
rene 已提交
100
		if err := node.StartMining(1); err != nil {
101 102 103 104 105 106 107 108
			panic(err)
		}
	}
	time.Sleep(3 * time.Second)

	// Start injecting transactions from the faucet like crazy
	nonces := make([]uint64, len(faucets))
	for {
R
rene 已提交
109
		// Pick a random signer node
110
		index := rand.Intn(len(faucets))
R
rene 已提交
111
		backend := nodes[index%len(nodes)]
112 113 114 115 116 117

		// 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), nil), types.HomesteadSigner{}, faucets[index])
		if err != nil {
			panic(err)
		}
R
rene 已提交
118
		if err := backend.TxPool().AddLocal(tx); err != nil {
119 120 121 122 123
			panic(err)
		}
		nonces[index]++

		// Wait if we're too saturated
R
rene 已提交
124
		if pend, _ := backend.TxPool().Stats(); pend > 2048 {
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
			time.Sleep(100 * time.Millisecond)
		}
	}
}

// makeGenesis creates a custom Clique genesis block based on some pre-defined
// signer and faucet accounts.
func makeGenesis(faucets []*ecdsa.PrivateKey, sealers []*ecdsa.PrivateKey) *core.Genesis {
	// Create a Clique network based off of the Rinkeby config
	genesis := core.DefaultRinkebyGenesisBlock()
	genesis.GasLimit = 25000000

	genesis.Config.ChainID = big.NewInt(18)
	genesis.Config.Clique.Period = 1
	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),
		}
	}
	// Sort the signers and embed into the extra-data section
	signers := make([]common.Address, len(sealers))
	for i, sealer := range sealers {
		signers[i] = crypto.PubkeyToAddress(sealer.PublicKey)
	}
	for i := 0; i < len(signers); i++ {
		for j := i + 1; j < len(signers); j++ {
			if bytes.Compare(signers[i][:], signers[j][:]) > 0 {
				signers[i], signers[j] = signers[j], signers[i]
			}
		}
	}
	genesis.ExtraData = make([]byte, 32+len(signers)*common.AddressLength+65)
	for i, signer := range signers {
		copy(genesis.ExtraData[32+i*common.AddressLength:], signer[:])
	}
	// Return the genesis block for initialization
	return genesis
}

R
rene 已提交
167
func makeSealer(genesis *core.Genesis) (*node.Node, *eth.Ethereum, error) {
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
	// 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,
		},
	}
	// Start the node and configure a full Ethereum node on it
	stack, err := node.New(config)
	if err != nil {
R
rene 已提交
184
		return nil, nil, err
185
	}
R
rene 已提交
186
	// Create and register the backend
187
	ethBackend, err := eth.New(stack, &ethconfig.Config{
R
rene 已提交
188 189 190 191 192 193
		Genesis:         genesis,
		NetworkId:       genesis.Config.ChainID.Uint64(),
		SyncMode:        downloader.FullSync,
		DatabaseCache:   256,
		DatabaseHandles: 256,
		TxPool:          core.DefaultTxPoolConfig,
194
		GPO:             ethconfig.Defaults.GPO,
R
rene 已提交
195 196 197 198 199 200 201 202
		Miner: miner.Config{
			GasCeil:  genesis.GasLimit * 11 / 10,
			GasPrice: big.NewInt(1),
			Recommit: time.Second,
		},
	})
	if err != nil {
		return nil, nil, err
203
	}
R
rene 已提交
204 205 206

	err = stack.Start()
	return stack, ethBackend, err
207
}