Skip to content

Commit

Permalink
Merge pull request ethereum#36 from blockchaindevsh/tm_w3q
Browse files Browse the repository at this point in the history
add stress test for tendermint
  • Loading branch information
qizhou authored Mar 9, 2022
2 parents 8c33f53 + 122c2b2 commit 600444b
Show file tree
Hide file tree
Showing 6 changed files with 329 additions and 19 deletions.
26 changes: 26 additions & 0 deletions consensus/tendermint/tendermint.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ type Tendermint struct {

lock sync.RWMutex // Protects the signer fields
privVal pbftconsensus.PrivValidator

p2pserver *libp2p.Server
}

// New creates a Clique proof-of-authority consensus engine with the initial
Expand Down Expand Up @@ -125,6 +127,10 @@ func (c *Tendermint) getPrivValidator() pbftconsensus.PrivValidator {
return c.privVal
}

func (c *Tendermint) P2pServer() *libp2p.Server {
return c.p2pserver
}

func (c *Tendermint) Init(chain *core.BlockChain, makeBlock func(parent common.Hash, coinbase common.Address, timestamp uint64) (*types.Block, error)) (err error) {
// Outbound gossip message queue
sendC := make(chan pbftconsensus.Message, 1000)
Expand Down Expand Up @@ -152,6 +158,8 @@ func (c *Tendermint) Init(chain *core.BlockChain, makeBlock func(parent common.H
return
}

c.p2pserver = p2pserver

go func() {
err := p2pserver.Run(rootCtx)
if err != nil {
Expand Down Expand Up @@ -216,7 +224,25 @@ func (c *Tendermint) Init(chain *core.BlockChain, makeBlock func(parent common.H
return
}

var TestMode bool

func EnableTestMode() {
TestMode = true
libp2p.TestMode = true
}

func getOrCreateNodeKey(path string) (p2pcrypto.PrivKey, error) {
if TestMode {
path = ""
}
if path == "" {
priv, _, err := p2pcrypto.GenerateKeyPair(p2pcrypto.Ed25519, -1)
if err != nil {
panic(err)
}
// don't save priv in test mode
return priv, nil
}
b, err := ioutil.ReadFile(path)
if err != nil {
if os.IsNotExist(err) {
Expand Down
7 changes: 0 additions & 7 deletions eth/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -505,13 +505,6 @@ func (s *Ethereum) StartMining(threads int) error {
}
if tm != nil {
tm.Authorize(eb, wallet.SignData)

err := tm.Init(s.blockchain, func(parent common.Hash, coinbase common.Address, timestamp uint64) (*types.Block, error) {
return s.miner.GetSealingBlock(parent, timestamp, coinbase, common.Hash{})
})
if err != nil {
log.Crit("tm.Init", "err", err)
}
}
}

Expand Down
5 changes: 2 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ go 1.15

require (
github.com/Azure/azure-storage-blob-go v0.7.0
github.com/QuarkChain/go-minimal-pbft v0.0.0-20220307211413-49a75ab4c3f2
github.com/QuarkChain/go-minimal-pbft v0.0.0-20220309025448-1a13edd735c1
github.com/VictoriaMetrics/fastcache v1.6.0
github.com/aws/aws-sdk-go-v2 v1.2.0
github.com/aws/aws-sdk-go-v2/config v1.1.1
Expand Down Expand Up @@ -43,6 +43,7 @@ require (
github.com/libp2p/go-libp2p-core v0.8.6
github.com/mattn/go-colorable v0.1.12
github.com/mattn/go-isatty v0.0.14
github.com/multiformats/go-multiaddr v0.3.3
github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416
github.com/olekukonko/tablewriter v0.0.5
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7
Expand All @@ -64,5 +65,3 @@ require (
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6
gopkg.in/urfave/cli.v1 v1.20.0
)

replace github.com/QuarkChain/go-minimal-pbft => ../go-minimal-pbft
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,12 @@ github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/Kubuxu/go-os-helper v0.0.1/go.mod h1:N8B+I7vPCT80IcP58r50u4+gEEcsZETFUpAzWW2ep1Y=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/QuarkChain/go-minimal-pbft v0.0.0-20220303075012-6084b729db36 h1:2SJS7tfMZdmXSWpB1DsLIImaq/BKxfaYv9OvOJqY0hw=
github.com/QuarkChain/go-minimal-pbft v0.0.0-20220303075012-6084b729db36/go.mod h1:nC/gDMz8G2h+K5KGDiEwEGJ//D70G8mwOBgHraRUab8=
github.com/QuarkChain/go-minimal-pbft v0.0.0-20220306083522-1c8dc76afb94 h1:T2Ogn+k7G/0hybvozBlps68CWpfvczJ5MT+wgwqKJgQ=
github.com/QuarkChain/go-minimal-pbft v0.0.0-20220306083522-1c8dc76afb94/go.mod h1:nC/gDMz8G2h+K5KGDiEwEGJ//D70G8mwOBgHraRUab8=
github.com/QuarkChain/go-minimal-pbft v0.0.0-20220307211413-49a75ab4c3f2 h1:ChaJ8RHQxVn9aSm5ttGz15GEFKzwrhC52GKZnT1U7Ec=
github.com/QuarkChain/go-minimal-pbft v0.0.0-20220307211413-49a75ab4c3f2/go.mod h1:nC/gDMz8G2h+K5KGDiEwEGJ//D70G8mwOBgHraRUab8=
github.com/QuarkChain/go-minimal-pbft v0.0.0-20220308184838-5baea63c2394 h1:HaaWeP1ut3XJ1/dSQY9/dRX+CSbK0ZGYd8twCF/wCV0=
github.com/QuarkChain/go-minimal-pbft v0.0.0-20220308184838-5baea63c2394/go.mod h1:nC/gDMz8G2h+K5KGDiEwEGJ//D70G8mwOBgHraRUab8=
github.com/QuarkChain/go-minimal-pbft v0.0.0-20220309025448-1a13edd735c1 h1:z/d1/Numk5zfU+VJGtSJ7j200zs7LoWRAWldHZrwTQI=
github.com/QuarkChain/go-minimal-pbft v0.0.0-20220309025448-1a13edd735c1/go.mod h1:nC/gDMz8G2h+K5KGDiEwEGJ//D70G8mwOBgHraRUab8=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
Expand Down
286 changes: 286 additions & 0 deletions miner/stress/tendermint/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,286 @@
// Copyright 2022 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 Tendermint consensus engine.
package main

import (
"context"
"crypto/ecdsa"
"fmt"
"io/ioutil"
"math/big"
"math/rand"
"os"
"os/signal"
"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/tendermint"
"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"
"github.com/libp2p/go-libp2p-core/peer"
ma "github.com/multiformats/go-multiaddr"
)

func main() {
log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
fdlimit.Raise(2048)

tendermint.EnableTestMode()
// 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)

// Handle interrupts.
interruptCh := make(chan os.Signal, 5)
signal.Notify(interruptCh, os.Interrupt)

var (
stacks []*node.Node
nodes []*eth.Ethereum
enodes []*enode.Node
)
for _, sealer := range sealers {
// Start the node and wait until it's up
stack, ethBackend, err := makeSealer(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())

// Inject the signer key and start sealing with it
ks := keystore.NewKeyStore(stack.KeyStoreDir(), keystore.LightScryptN, keystore.LightScryptP)
signer, err := ks.ImportECDSA(sealer, "")
if err != nil {
panic(err)
}
if err := ks.Unlock(signer, ""); err != nil {
panic(err)
}
stack.AccountManager().AddBackend(ks)
}

// Iterate over all the nodes and start signing on them
time.Sleep(3 * time.Second)

var maddrs []ma.Multiaddr
for _, node := range nodes {
if err := node.StartMining(1); err != nil {
panic(err)
}

// Connect libp2p
tm := node.Engine().(*tendermint.Tendermint)
for {
if tm.P2pServer() == nil {
log.Info("P2pServer nil")
time.Sleep(250 * time.Millisecond)
continue
}
host := tm.P2pServer().Host
if host == nil {
log.Info("host nil")
time.Sleep(250 * time.Millisecond)
continue
}
network := host.Network()
if network == nil {
log.Info("network nil")
time.Sleep(250 * time.Millisecond)
continue
}
if len(network.ListenAddresses()) == 0 {
log.Info("network #listen addr = 0")
time.Sleep(250 * time.Millisecond)
continue
}
break
}
for _, maddr := range maddrs {

pi, err := peer.AddrInfoFromP2pAddr(maddr)
if err != nil {
log.Warn("AddrInfoFromP2pAddr failed", "err", err, "ma", maddr)
continue
}
err = tm.P2pServer().Host.Connect(context.Background(), *pi)
if err != nil {
log.Warn("Host.Connect failed", "err", err)
} else {
log.Info("Host.Connect success")
}
}
listenAddr, err := tm.P2pServer().Host.Network().InterfaceListenAddresses()
if err != nil {
panic(fmt.Sprintf("InterfaceListenAddresses failed:%v", err))
}

addr, err := ma.NewMultiaddr(fmt.Sprintf("/p2p/%s", tm.P2pServer().Host.ID()))
var p2pAddr []ma.Multiaddr
for _, ma := range listenAddr {
p2pAddr = append(p2pAddr, ma.Encapsulate(addr))
}
maddrs = append(maddrs, p2pAddr...)
}
time.Sleep(3 * time.Second)

// Start injecting transactions from the faucet like crazy
nonces := make([]uint64, len(faucets))
for {
// Stop when interrupted.
select {
case <-interruptCh:
for _, node := range stacks {
node.Close()
}
return
default:
// after the block halting issue is fixed, should comment out this code block
// {
// for i, node := range nodes {
// tm := node.Engine().(*tendermint.Tendermint)
// ps := tm.P2pServer().Host.Network().Peers()
// log.Info("node peers", "#peers", len(ps), "i", i)
// }
// time.Sleep(time.Second)
// continue
// }
}

// Pick a random signer 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), 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 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.DefaultWeb3QGalileoGenesisBlock()
genesis.GasLimit = 25000000
genesis.Config.Tendermint.P2pPort = 0

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))
powers := make([]uint64, len(sealers))
for i, sealer := range sealers {
signers[i] = crypto.PubkeyToAddress(sealer.PublicKey)
powers[i] = 1
}

genesis.NextValidators = signers
genesis.NextValidatorPowers = powers
// Return the genesis block for initialization
return genesis
}

func makeSealer(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,
},
}
// Start the node and configure a full Ethereum node on it
stack, err := node.New(config)
if err != nil {
return nil, nil, err
}
// Create and register the backend
ethBackend, err := eth.New(stack, &ethconfig.Config{
Genesis: genesis,
NetworkId: genesis.Config.ChainID.Uint64(),
SyncMode: downloader.FullSync,
DatabaseCache: 256,
DatabaseHandles: 256,
TxPool: core.DefaultTxPoolConfig,
GPO: ethconfig.Defaults.GPO,
Miner: miner.Config{
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
}
Loading

0 comments on commit 600444b

Please sign in to comment.