Merge branch 'master' into marko/int64-

This commit is contained in:
Marko Baricevic
2021-03-15 12:03:26 +00:00
74 changed files with 1215 additions and 468 deletions

View File

@@ -109,14 +109,7 @@ if [[ $APPEND_TX_CODE != 0 ]]; then
exit 1
fi
echo "... sending tx. expect error"
# second time should get rejected by the mempool (return error and non-zero code)
sendTx $TX true
echo "... sending tx. expect no error"
echo "... sending new tx. expect no error"
# now, TX=01 should pass, with no error
TX=01

View File

@@ -70,6 +70,8 @@ The test runner has the following stages, which can also be executed explicitly
* `cleanup`: removes configuration files and Docker containers/networks.
Auxiliary commands:
* `logs`: outputs all node logs.
* `tail`: tails (follows) node logs until canceled.
@@ -139,3 +141,11 @@ Docker does not enable IPv6 by default. To do so, enter the following in
"fixed-cidr-v6": "2001:db8:1::/64"
}
```
## Benchmarking testnets
It is also possible to run a simple benchmark on a testnet. This is done through the `benchmark` command. This manages the entire process: setting up the environment, starting the test net, waiting for a considerable amount of blocks to be used (currently 100), and then returning the following metrics from the sample of the blockchain:
- Average time to produce a block
- Standard deviation of producing a block
- Minimum and maximum time to produce a block

View File

@@ -14,6 +14,7 @@ type Config struct {
Listen string
Protocol string
Dir string
Mode string `toml:"mode"`
PersistInterval uint64 `toml:"persist_interval"`
SnapshotInterval uint64 `toml:"snapshot_interval"`
RetainBlocks uint64 `toml:"retain_blocks"`

View File

@@ -1,11 +1,14 @@
package main
import (
"context"
"errors"
"fmt"
"net"
"net/http"
"os"
"path/filepath"
"strings"
"time"
"github.com/spf13/viper"
@@ -17,12 +20,18 @@ import (
tmflags "github.com/tendermint/tendermint/libs/cli/flags"
"github.com/tendermint/tendermint/libs/log"
tmnet "github.com/tendermint/tendermint/libs/net"
"github.com/tendermint/tendermint/light"
lproxy "github.com/tendermint/tendermint/light/proxy"
lrpc "github.com/tendermint/tendermint/light/rpc"
dbs "github.com/tendermint/tendermint/light/store/db"
"github.com/tendermint/tendermint/node"
"github.com/tendermint/tendermint/p2p"
"github.com/tendermint/tendermint/privval"
grpcprivval "github.com/tendermint/tendermint/privval/grpc"
privvalproto "github.com/tendermint/tendermint/proto/tendermint/privval"
"github.com/tendermint/tendermint/proxy"
rpcserver "github.com/tendermint/tendermint/rpc/jsonrpc/server"
e2e "github.com/tendermint/tendermint/test/e2e/pkg"
)
var logger = log.NewTMLogger(log.NewSyncWriter(os.Stdout))
@@ -66,10 +75,13 @@ func run(configFile string) error {
case "socket", "grpc":
err = startApp(cfg)
case "builtin":
if cfg.Mode == string(e2e.ModeLight) {
err = startLightClient(cfg)
} else {
err = startNode(cfg)
}
// FIXME: Temporarily remove maverick until it is redesigned
// if len(cfg.Misbehaviors) == 0 {
err = startNode(cfg)
// } else {
// err = startMaverick(cfg)
// }
default:
@@ -137,6 +149,63 @@ func startNode(cfg *Config) error {
return n.Start()
}
func startLightClient(cfg *Config) error {
tmcfg, nodeLogger, _, err := setupNode()
if err != nil {
return err
}
dbContext := &node.DBContext{ID: "light", Config: tmcfg}
lightDB, err := node.DefaultDBProvider(dbContext)
if err != nil {
return err
}
providers := rpcEndpoints(tmcfg.P2P.PersistentPeers)
c, err := light.NewHTTPClient(
context.Background(),
cfg.ChainID,
light.TrustOptions{
Period: tmcfg.StateSync.TrustPeriod,
Height: tmcfg.StateSync.TrustHeight,
Hash: tmcfg.StateSync.TrustHashBytes(),
},
providers[0],
providers[1:],
dbs.New(lightDB),
light.Logger(nodeLogger),
)
if err != nil {
return err
}
rpccfg := rpcserver.DefaultConfig()
rpccfg.MaxBodyBytes = tmcfg.RPC.MaxBodyBytes
rpccfg.MaxHeaderBytes = tmcfg.RPC.MaxHeaderBytes
rpccfg.MaxOpenConnections = tmcfg.RPC.MaxOpenConnections
// If necessary adjust global WriteTimeout to ensure it's greater than
// TimeoutBroadcastTxCommit.
// See https://github.com/tendermint/tendermint/issues/3435
if rpccfg.WriteTimeout <= tmcfg.RPC.TimeoutBroadcastTxCommit {
rpccfg.WriteTimeout = tmcfg.RPC.TimeoutBroadcastTxCommit + 1*time.Second
}
p, err := lproxy.NewProxy(c, tmcfg.RPC.ListenAddress, providers[0], rpccfg, nodeLogger,
lrpc.KeyPathFn(lrpc.DefaultMerkleKeyPathFn()))
if err != nil {
return err
}
logger.Info("Starting proxy...", "laddr", tmcfg.RPC.ListenAddress)
if err := p.ListenAndServe(); err != http.ErrServerClosed {
// Error starting or closing listener:
logger.Error("proxy ListenAndServe", "err", err)
}
return nil
}
// FIXME: Temporarily disconnected maverick until it is redesigned
// startMaverick starts a Maverick node that runs the application directly. It assumes the Tendermint
// configuration is in $TMHOME/config/tendermint.toml.
@@ -176,7 +245,10 @@ func startNode(cfg *Config) error {
// startSigner starts a signer server connecting to the given endpoint.
func startSigner(cfg *Config) error {
filePV := privval.LoadFilePV(cfg.PrivValKey, cfg.PrivValState)
filePV, err := privval.LoadFilePV(cfg.PrivValKey, cfg.PrivValState)
if err != nil {
return err
}
protocol, address := tmnet.ProtocolAndAddress(cfg.PrivValServer)
var dialFn privval.SocketDialer
@@ -210,7 +282,7 @@ func startSigner(cfg *Config) error {
endpoint := privval.NewSignerDialerEndpoint(logger, dialFn,
privval.SignerDialerEndpointRetryWaitInterval(1*time.Second),
privval.SignerDialerEndpointConnRetries(100))
err := privval.NewSignerServer(endpoint, cfg.ChainID, filePV).Start()
err = privval.NewSignerServer(endpoint, cfg.ChainID, filePV).Start()
if err != nil {
return err
}
@@ -264,3 +336,21 @@ func setupNode() (*config.Config, log.Logger, *p2p.NodeKey, error) {
return tmcfg, nodeLogger, &nodeKey, nil
}
// rpcEndpoints takes a list of persistent peers and splits them into a list of rpc endpoints
// using 26657 as the port number
func rpcEndpoints(peers string) []string {
arr := strings.Split(peers, ",")
endpoints := make([]string, len(arr))
for i, v := range arr {
addr, err := p2p.ParseNodeAddress(v)
if err != nil {
panic(err)
}
// use RPC port instead
addr.Port = 26657
rpcEndpoint := "http://" + addr.Hostname + ":" + fmt.Sprint(addr.Port)
endpoints[i] = rpcEndpoint
}
return endpoints
}

View File

@@ -77,7 +77,7 @@ func generateTestnet(r *rand.Rand, opt map[string]interface{}) (e2e.Manifest, er
KeyType: opt["keyType"].(string),
}
var numSeeds, numValidators, numFulls int
var numSeeds, numValidators, numFulls, numLightClients int
switch opt["topology"].(string) {
case "single":
numValidators = 1
@@ -85,7 +85,8 @@ func generateTestnet(r *rand.Rand, opt map[string]interface{}) (e2e.Manifest, er
numValidators = 4
case "large":
// FIXME Networks are kept small since large ones use too much CPU.
numSeeds = r.Intn(4)
numSeeds = r.Intn(3)
numLightClients = r.Intn(3)
numValidators = 4 + r.Intn(7)
numFulls = r.Intn(5)
default:
@@ -143,6 +144,13 @@ func generateTestnet(r *rand.Rand, opt map[string]interface{}) (e2e.Manifest, er
r, e2e.ModeFull, startAt, manifest.InitialHeight, false)
}
for i := 1; i <= numLightClients; i++ {
startAt := manifest.InitialHeight + 5
manifest.Nodes[fmt.Sprintf("light%02d", i)] = generateNode(
r, e2e.ModeLight, startAt+(5*uint64(i)), manifest.InitialHeight, false,
)
}
// We now set up peer discovery for nodes. Seed nodes are fully meshed with
// each other, while non-seed nodes either use a set of random seeds or a
// set of random peers that start before themselves.
@@ -175,6 +183,10 @@ func generateTestnet(r *rand.Rand, opt map[string]interface{}) (e2e.Manifest, er
}
})
for i, name := range peerNames {
// we skip over light clients - they connect to all peers initially
if manifest.Nodes[name].Mode == string(e2e.ModeLight) {
continue
}
if len(seedNames) > 0 && (i == 0 || r.Float64() >= 0.5) {
manifest.Nodes[name].Seeds = uniformSetChoice(seedNames).Choose(r)
} else if i > 0 {
@@ -213,7 +225,7 @@ func generateNode(
node.SnapshotInterval = 3
}
if node.Mode == "validator" {
if node.Mode == string(e2e.ModeValidator) {
misbehaveAt := startAt + 5 + uint64(r.Intn(10))
if startAt == 0 {
misbehaveAt += initialHeight - 1
@@ -224,6 +236,11 @@ func generateNode(
}
}
if node.Mode == string(e2e.ModeLight) {
node.ABCIProtocol = "builtin"
node.StateSync = false
}
// If a node which does not persist state also does not retain blocks, randomly
// choose to either persist state or retain all blocks.
if node.PersistInterval != nil && *node.PersistInterval == 0 && node.RetainBlocks > 0 {

View File

@@ -90,3 +90,13 @@ fast_sync = "v0"
state_sync = true
seeds = ["seed01"]
perturb = ["restart"]
[node.light01]
mode= "light"
start_at= 1005
persistent_peers = ["validator01", "validator02", "validator03"]
[node.light02]
mode= "light"
start_at= 1015
persistent_peers = ["validator04", "full01", "validator05"]

View File

@@ -2,4 +2,4 @@
[node.validator02]
[node.validator03]
[node.validator04]

View File

@@ -58,9 +58,9 @@ type Manifest struct {
// ManifestNode represents a node in a testnet manifest.
type ManifestNode struct {
// Mode specifies the type of node: "validator", "full", or "seed". Defaults to
// "validator". Full nodes do not get a signing key (a dummy key is generated),
// and seed nodes run in seed mode with the PEX reactor enabled.
// Mode specifies the type of node: "validator", "full", "light" or "seed".
// Defaults to "validator". Full nodes do not get a signing key (a dummy key
// is generated), and seed nodes run in seed mode with the PEX reactor enabled.
Mode string `toml:"mode"`
// Seeds is the list of node names to use as P2P seed nodes. Defaults to none.
@@ -68,7 +68,8 @@ type ManifestNode struct {
// PersistentPeers is a list of node names to maintain persistent P2P
// connections to. If neither seeds nor persistent peers are specified,
// this defaults to all other nodes in the network.
// this defaults to all other nodes in the network. For light clients,
// this relates to the providers the light client is connected to.
PersistentPeers []string `toml:"persistent_peers"`
// Database specifies the database backend: "goleveldb", "cleveldb",

View File

@@ -33,6 +33,7 @@ type Perturbation string
const (
ModeValidator Mode = "validator"
ModeFull Mode = "full"
ModeLight Mode = "light"
ModeSeed Mode = "seed"
ProtocolBuiltin Protocol = "builtin"
@@ -151,7 +152,7 @@ func LoadTestnet(file string) (*Testnet, error) {
ProxyPort: proxyPortGen.Next(),
Mode: ModeValidator,
Database: "goleveldb",
ABCIProtocol: ProtocolUNIX,
ABCIProtocol: ProtocolBuiltin,
PrivvalProtocol: ProtocolFile,
StartAt: nodeManifest.StartAt,
FastSync: nodeManifest.FastSync,
@@ -327,6 +328,9 @@ func (n Node) Validate(testnet Testnet) error {
default:
return fmt.Errorf("invalid ABCI protocol setting %q", n.ABCIProtocol)
}
if n.Mode == ModeLight && n.ABCIProtocol != ProtocolBuiltin {
return errors.New("light client must use builtin protocol")
}
switch n.PrivvalProtocol {
case ProtocolFile, ProtocolTCP, ProtocolGRPC, ProtocolUNIX:
default:
@@ -402,7 +406,7 @@ func (t Testnet) LookupNode(name string) *Node {
func (t Testnet) ArchiveNodes() []*Node {
nodes := []*Node{}
for _, node := range t.Nodes {
if node.Mode != ModeSeed && node.StartAt == 0 && node.RetainBlocks == 0 {
if !node.Stateless() && node.StartAt == 0 && node.RetainBlocks == 0 {
nodes = append(nodes, node)
}
}
@@ -476,6 +480,11 @@ func (n Node) Client() (*rpchttp.HTTP, error) {
return rpchttp.New(fmt.Sprintf("http://127.0.0.1:%v", n.ProxyPort))
}
// Stateless returns true if the node is either a seed node or a light node
func (n Node) Stateless() bool {
return n.Mode == ModeLight || n.Mode == ModeSeed
}
// keyGenerator generates pseudorandom Ed25519 keys based on a seed.
type keyGenerator struct {
random *rand.Rand

View File

@@ -0,0 +1,192 @@
package main
import (
"context"
"fmt"
"math"
"time"
e2e "github.com/tendermint/tendermint/test/e2e/pkg"
"github.com/tendermint/tendermint/types"
)
// Benchmark is a simple function for fetching, calculating and printing
// the following metrics:
// 1. Average block production time
// 2. Block interval standard deviation
// 3. Max block interval (slowest block)
// 4. Min block interval (fastest block)
//
// Metrics are based of the `benchmarkLength`, the amount of consecutive blocks
// sampled from in the testnet
func Benchmark(testnet *e2e.Testnet, benchmarkLength uint64) error {
block, _, err := waitForHeight(testnet, 0)
if err != nil {
return err
}
logger.Info("Beginning benchmark period...", "height", block.Height)
// wait for the length of the benchmark period in blocks to pass. We allow 5 seconds for each block
// which should be sufficient.
waitingTime := time.Duration(benchmarkLength*5) * time.Second
endHeight, err := waitForAllNodes(testnet, block.Height+benchmarkLength, waitingTime)
if err != nil {
return err
}
logger.Info("Ending benchmark period", "height", endHeight)
// fetch a sample of blocks
blocks, err := fetchBlockChainSample(testnet, benchmarkLength)
if err != nil {
return err
}
// slice into time intervals and collate data
timeIntervals := splitIntoBlockIntervals(blocks)
testnetStats := extractTestnetStats(timeIntervals)
testnetStats.startHeight = blocks[0].Header.Height
testnetStats.endHeight = blocks[len(blocks)-1].Header.Height
// print and return
logger.Info(testnetStats.String())
return nil
}
type testnetStats struct {
startHeight uint64
endHeight uint64
// average time to produce a block
mean time.Duration
// standard deviation of block production
std float64
// longest time to produce a block
max time.Duration
// shortest time to produce a block
min time.Duration
}
func (t *testnetStats) String() string {
return fmt.Sprintf(`Benchmarked from height %v to %v
Mean Block Interval: %v
Standard Deviation: %f
Max Block Interval: %v
Min Block Interval: %v
`,
t.startHeight,
t.endHeight,
t.mean,
t.std,
t.max,
t.min,
)
}
// fetchBlockChainSample waits for `benchmarkLength` amount of blocks to pass, fetching
// all of the headers for these blocks from an archive node and returning it.
func fetchBlockChainSample(testnet *e2e.Testnet, benchmarkLength uint64) ([]*types.BlockMeta, error) {
var blocks []*types.BlockMeta
// Find the first archive node
archiveNode := testnet.ArchiveNodes()[0]
c, err := archiveNode.Client()
if err != nil {
return nil, err
}
// find the latest height
ctx := context.Background()
s, err := c.Status(ctx)
if err != nil {
return nil, err
}
to := s.SyncInfo.LatestBlockHeight
from := to - benchmarkLength + 1
if from <= testnet.InitialHeight {
return nil, fmt.Errorf("tesnet was unable to reach required height for benchmarking (latest height %d)", to)
}
// Fetch blocks
for from < to {
// fetch the blockchain metas. Currently we can only fetch 20 at a time
resp, err := c.BlockchainInfo(ctx, from, min(from+19, to))
if err != nil {
return nil, err
}
blockMetas := resp.BlockMetas
// we receive blocks in descending order so we have to add them in reverse
for i := len(blockMetas) - 1; i >= 0; i-- {
if blockMetas[i].Header.Height != from {
return nil, fmt.Errorf("node gave us another header. Wanted %d, got %d",
from,
blockMetas[i].Header.Height,
)
}
from++
blocks = append(blocks, blockMetas[i])
}
}
return blocks, nil
}
func splitIntoBlockIntervals(blocks []*types.BlockMeta) []time.Duration {
intervals := make([]time.Duration, len(blocks)-1)
lastTime := blocks[0].Header.Time
for i, block := range blocks {
// skip the first block
if i == 0 {
continue
}
intervals[i-1] = block.Header.Time.Sub(lastTime)
lastTime = block.Header.Time
}
return intervals
}
func extractTestnetStats(intervals []time.Duration) testnetStats {
var (
sum, mean time.Duration
std float64
max = intervals[0]
min = intervals[0]
)
for _, interval := range intervals {
sum += interval
if interval > max {
max = interval
}
if interval < min {
min = interval
}
}
mean = sum / time.Duration(len(intervals))
for _, interval := range intervals {
diff := (interval - mean).Seconds()
std += math.Pow(diff, 2)
}
std = math.Sqrt(std / float64(len(intervals)))
return testnetStats{
mean: mean,
std: std,
max: max,
min: min,
}
}
func min(a, b uint64) uint64 {
if a > b {
return b
}
return a
}

View File

@@ -218,6 +218,63 @@ func NewCLI() *CLI {
},
})
cli.root.AddCommand(&cobra.Command{
Use: "benchmark",
Short: "Benchmarks testnet",
Long: `Benchmarks the following metrics:
Mean Block Interval
Standard Deviation
Min Block Interval
Max Block Interval
over a 100 block sampling period.
Does not run any perbutations.
`,
RunE: func(cmd *cobra.Command, args []string) error {
if err := Cleanup(cli.testnet); err != nil {
return err
}
if err := Setup(cli.testnet); err != nil {
return err
}
chLoadResult := make(chan error)
ctx, loadCancel := context.WithCancel(context.Background())
defer loadCancel()
go func() {
err := Load(ctx, cli.testnet)
if err != nil {
logger.Error(fmt.Sprintf("Transaction load failed: %v", err.Error()))
}
chLoadResult <- err
}()
if err := Start(cli.testnet); err != nil {
return err
}
if err := Wait(cli.testnet, 5); err != nil { // allow some txs to go through
return err
}
// we benchmark performance over the next 100 blocks
if err := Benchmark(cli.testnet, 100); err != nil {
return err
}
loadCancel()
if err := <-chLoadResult; err != nil {
return err
}
if err := Cleanup(cli.testnet); err != nil {
return err
}
return nil
},
})
return cli
}

View File

@@ -86,24 +86,27 @@ func waitForNode(node *e2e.Node, height uint64, timeout time.Duration) (*rpctype
return status, nil
}
time.Sleep(200 * time.Millisecond)
time.Sleep(300 * time.Millisecond)
}
}
// waitForAllNodes waits for all nodes to become available and catch up to the given block height.
func waitForAllNodes(testnet *e2e.Testnet, height uint64, timeout time.Duration) (uint64, error) {
lastHeight := uint64(0)
var lastHeight uint64
for _, node := range testnet.Nodes {
if node.Mode == e2e.ModeSeed {
continue
}
status, err := waitForNode(node, height, 20*time.Second)
status, err := waitForNode(node, height, timeout)
if err != nil {
return 0, err
}
if status.SyncInfo.LatestBlockHeight > lastHeight {
lastHeight = status.SyncInfo.LatestBlockHeight
}
}
return lastHeight, nil
}

View File

@@ -65,23 +65,23 @@ func Setup(testnet *e2e.Testnet) error {
for _, node := range testnet.Nodes {
nodeDir := filepath.Join(testnet.Dir, node.Name)
dirs := []string{
filepath.Join(nodeDir, "config"),
filepath.Join(nodeDir, "data"),
filepath.Join(nodeDir, "data", "app"),
}
for _, dir := range dirs {
// light clients don't need an app directory
if node.Mode == e2e.ModeLight && strings.Contains(dir, "app") {
continue
}
err := os.MkdirAll(dir, 0755)
if err != nil {
return err
}
}
err = genesis.SaveAs(filepath.Join(nodeDir, "config", "genesis.json"))
if err != nil {
return err
}
cfg, err := MakeConfig(node)
if err != nil {
return err
@@ -97,6 +97,16 @@ func Setup(testnet *e2e.Testnet) error {
return err
}
if node.Mode == e2e.ModeLight {
// stop early if a light client
continue
}
err = genesis.SaveAs(filepath.Join(nodeDir, "config", "genesis.json"))
if err != nil {
return err
}
err = (&p2p.NodeKey{PrivKey: node.NodeKey}).SaveAs(filepath.Join(nodeDir, "config", "node_key.json"))
if err != nil {
return err
@@ -168,8 +178,7 @@ services:
image: tendermint/e2e-node
{{- if eq .ABCIProtocol "builtin" }}
entrypoint: /usr/bin/entrypoint-builtin
{{- end }}
{{- if ne .ABCIProtocol "builtin"}}
{{- else }}
command: {{ startCommands .Misbehaviors .LogLevel }}
{{- end }}
init: true
@@ -289,7 +298,7 @@ func MakeConfig(node *e2e.Node) (*config.Config, error) {
case e2e.ModeSeed:
cfg.P2P.SeedMode = true
cfg.P2P.PexReactor = true
case e2e.ModeFull:
case e2e.ModeFull, e2e.ModeLight:
// Don't need to do anything, since we're using a dummy privval key by default.
default:
return nil, fmt.Errorf("unexpected mode %q", node.Mode)
@@ -338,6 +347,8 @@ func MakeAppConfig(node *e2e.Node) ([]byte, error) {
"chain_id": node.Testnet.Name,
"dir": "data/app",
"listen": AppAddressUNIX,
"mode": node.Mode,
"proxy_port": node.ProxyPort,
"protocol": "socket",
"persist_interval": node.PersistInterval,
"snapshot_interval": node.SnapshotInterval,

View File

@@ -58,7 +58,7 @@ func Start(testnet *e2e.Testnet) error {
// Update any state sync nodes with a trusted height and hash
for _, node := range nodeQueue {
if node.StateSync {
if node.StateSync || node.Mode == e2e.ModeLight {
err = UpdateConfigStateSync(node, block.Height, blockID.Hash.Bytes())
if err != nil {
return err

View File

@@ -20,9 +20,15 @@ func Wait(testnet *e2e.Testnet, blocks uint64) error {
// WaitUntil waits until a given height has been reached.
func WaitUntil(testnet *e2e.Testnet, height uint64) error {
logger.Info(fmt.Sprintf("Waiting for all nodes to reach height %v...", height))
_, err := waitForAllNodes(testnet, height, 20*time.Second)
_, err := waitForAllNodes(testnet, height, waitingTime(len(testnet.Nodes)))
if err != nil {
return err
}
return nil
}
// waitingTime estimates how long it should take for a node to reach the height.
// More nodes in a network implies we may expect a slower network and may have to wait longer.
func waitingTime(nodes int) time.Duration {
return time.Duration(20+(nodes*4)) * time.Second
}

View File

@@ -16,7 +16,7 @@ import (
// Tests that any initial state given in genesis has made it into the app.
func TestApp_InitialState(t *testing.T) {
testNode(t, func(t *testing.T, node e2e.Node) {
if node.Mode == e2e.ModeSeed {
if node.Stateless() {
return
}
if len(node.Testnet.InitialState) == 0 {
@@ -81,12 +81,27 @@ func TestApp_Tx(t *testing.T) {
value := fmt.Sprintf("%x", bz)
tx := types.Tx(fmt.Sprintf("%v=%v", key, value))
_, err = client.BroadcastTxCommit(ctx, tx)
resp, err := client.BroadcastTxCommit(ctx, tx)
require.NoError(t, err)
resp, err := client.ABCIQuery(ctx, "", []byte(key))
// wait for the tx to be persisted in the tx indexer
time.Sleep(500 * time.Millisecond)
hash := tx.Hash()
txResp, err := client.Tx(ctx, hash, false)
require.NoError(t, err)
assert.Equal(t, key, string(resp.Response.Key))
assert.Equal(t, value, string(resp.Response.Value))
assert.Equal(t, txResp.Tx, tx)
assert.Equal(t, txResp.Height, resp.Height)
// NOTE: we don't test abci query of the light client
if node.Mode == e2e.ModeLight {
return
}
abciResp, err := client.ABCIQuery(ctx, "", []byte(key))
require.NoError(t, err)
assert.Equal(t, key, string(abciResp.Response.Key))
assert.Equal(t, value, string(abciResp.Response.Value))
})
}

View File

@@ -2,6 +2,7 @@ package pex
import (
"net"
"os"
"github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/crypto/ed25519"
@@ -13,32 +14,38 @@ import (
)
var (
pexR *pex.Reactor
peer p2p.Peer
pexR *pex.Reactor
peer p2p.Peer
logger = log.NewTMLogger(log.NewSyncWriter(os.Stdout))
)
func init() {
addrB := pex.NewAddrBook("./testdata/addrbook1", false)
pexR := pex.NewReactor(addrB, &pex.ReactorConfig{SeedMode: false})
if pexR == nil {
panic("NewReactor returned nil")
}
pexR.SetLogger(log.NewNopLogger())
peer := newFuzzPeer()
pexR = pex.NewReactor(addrB, &pex.ReactorConfig{SeedMode: false})
pexR.SetLogger(logger)
peer = newFuzzPeer()
pexR.AddPeer(peer)
}
func Fuzz(data []byte) int {
// MakeSwitch uses log.TestingLogger which can't be executed in init()
cfg := config.DefaultP2PConfig()
cfg.PexReactor = true
sw := p2p.MakeSwitch(cfg, 0, "127.0.0.1", "123.123.123", func(i int, sw *p2p.Switch) *p2p.Switch {
return sw
})
}, logger)
pexR.SetSwitch(sw)
}
func Fuzz(data []byte) int {
if len(data) == 0 {
return -1
}
pexR.Receive(pex.PexChannel, peer, data)
if !peer.IsRunning() {
// do not increase priority for msgs which lead to peer being stopped
return 0
}
return 1
}
@@ -64,15 +71,15 @@ var defaultNodeInfo = p2p.NodeInfo{
0,
),
NodeID: nodeID,
ListenAddr: "0.0.0.0:98992",
ListenAddr: "127.0.0.1:0",
Moniker: "foo1",
}
func (fp *fuzzPeer) FlushStop() {}
func (fp *fuzzPeer) ID() p2p.NodeID { return nodeID }
func (fp *fuzzPeer) RemoteIP() net.IP { return net.IPv4(0, 0, 0, 0) }
func (fp *fuzzPeer) RemoteIP() net.IP { return net.IPv4(198, 163, 190, 214) }
func (fp *fuzzPeer) RemoteAddr() net.Addr {
return &net.TCPAddr{IP: fp.RemoteIP(), Port: 98991, Zone: ""}
return &net.TCPAddr{IP: fp.RemoteIP(), Port: 26656, Zone: ""}
}
func (fp *fuzzPeer) IsOutbound() bool { return false }
func (fp *fuzzPeer) IsPersistent() bool { return false }

View File

@@ -17,18 +17,40 @@ func Fuzz(data []byte) int {
}
fooConn, barConn := makeSecretConnPair()
n, err := fooConn.Write(data)
if err != nil {
panic(err)
// Run Write in a separate goroutine because if data is greater than 1024
// bytes, each Write must be followed by Read (see io.Pipe documentation).
go func() {
// Copy data because Write modifies the slice.
dataToWrite := make([]byte, len(data))
copy(dataToWrite, data)
n, err := fooConn.Write(dataToWrite)
if err != nil {
panic(err)
}
if n < len(data) {
panic(fmt.Sprintf("wanted to write %d bytes, but %d was written", len(data), n))
}
}()
dataRead := make([]byte, len(data))
totalRead := 0
for totalRead < len(data) {
buf := make([]byte, len(data)-totalRead)
m, err := barConn.Read(buf)
if err != nil {
panic(err)
}
copy(dataRead[totalRead:], buf[:m])
totalRead += m
log.Printf("total read: %d", totalRead)
}
dataRead := make([]byte, n)
m, err := barConn.Read(dataRead)
if err != nil {
panic(err)
}
if !bytes.Equal(data[:n], dataRead[:m]) {
panic(fmt.Sprintf("bytes written %X != read %X", data[:n], dataRead[:m]))
if !bytes.Equal(data, dataRead) {
panic("bytes written != read")
}
return 1
}

View File

@@ -6,6 +6,7 @@ import (
"io/ioutil"
"net/http"
"net/http/httptest"
"os"
"github.com/tendermint/tendermint/libs/log"
rs "github.com/tendermint/tendermint/rpc/jsonrpc/server"
@@ -18,13 +19,16 @@ var rpcFuncMap = map[string]*rs.RPCFunc{
var mux *http.ServeMux
func init() {
mux := http.NewServeMux()
buf := new(bytes.Buffer)
lgr := log.NewTMLogger(buf)
mux = http.NewServeMux()
lgr := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
rs.RegisterRPCFuncs(mux, rpcFuncMap, lgr)
}
func Fuzz(data []byte) int {
if len(data) == 0 {
return -1
}
req, _ := http.NewRequest("POST", "http://localhost/", bytes.NewReader(data))
rec := httptest.NewRecorder()
mux.ServeHTTP(rec, req)
@@ -36,9 +40,11 @@ func Fuzz(data []byte) int {
if err := res.Body.Close(); err != nil {
panic(err)
}
recv := new(types.RPCResponse)
if err := json.Unmarshal(blob, recv); err != nil {
panic(err)
if len(blob) > 0 {
recv := new(types.RPCResponse)
if err := json.Unmarshal(blob, recv); err != nil {
panic(err)
}
}
return 1
}