Co-authored-by: Emmanuel T Odeke <emmanuel@orijtech.com> Closes #5907 - add init-corpus to blockchain reactor - remove validator-set FromBytes test now that we have proto, we don't need to test it! bye amino - simplify mempool test do we want to test remote ABCI app? - do not recreate mux on every crash in jsonrpc test - update p2p pex reactor test - remove p2p/listener test the API has changed + I did not understand what it's tested anyway - update secretconnection test - add readme and makefile - list inputs in readme - add nightly workflow - remove blockchain fuzz test EncodeMsg / DecodeMsg no longer existpull/5961/head
@ -0,0 +1,63 @@ | |||||
# Runs fuzzing nightly. | |||||
name: fuzz-nightly | |||||
on: | |||||
workflow_dispatch: # allow running workflow manually | |||||
schedule: | |||||
- cron: '0 3 * * *' | |||||
jobs: | |||||
fuzz-nightly-test: | |||||
runs-on: ubuntu-latest | |||||
steps: | |||||
- uses: actions/setup-go@v2 | |||||
with: | |||||
go-version: '1.15' | |||||
- uses: actions/checkout@v2 | |||||
- name: Install go-fuzz | |||||
working-directory: test/fuzz | |||||
run: go get -u github.com/dvyukov/go-fuzz/go-fuzz github.com/dvyukov/go-fuzz/go-fuzz-build | |||||
- name: Fuzz mempool | |||||
working-directory: test/fuzz | |||||
run: timeout 10m make fuzz-mempool | |||||
- name: Fuzz p2p-addrbook | |||||
working-directory: test/fuzz | |||||
run: timeout 10m make fuzz-p2p-addrbook | |||||
- name: Fuzz p2p-pex | |||||
working-directory: test/fuzz | |||||
run: timeout 10m make fuzz-p2p-pex | |||||
- name: Fuzz p2p-sc | |||||
working-directory: test/fuzz | |||||
run: timeout 10m make fuzz-p2p-sc | |||||
- name: Fuzz p2p-rpc-server | |||||
working-directory: test/fuzz | |||||
run: timeout 10m make fuzz-rpc-server | |||||
- name: Set crashers count | |||||
working-directory: test/fuzz | |||||
run: echo "::set-output name=crashers-count::$(find . -type d -name "crashers" | xargs -I % sh -c 'ls % | wc -l' | awk '{total += $1} END {print total}')" | |||||
id: set-crashers-count | |||||
outputs: | |||||
crashers_count: ${{ steps.set-crashers-count.outputs.crashers-count }} | |||||
fuzz-nightly-fail: | |||||
needs: fuzz-nightly-test | |||||
if: ${{ needs.set-crashers-count.outputs.crashers-count != 0 }} | |||||
runs-on: ubuntu-latest | |||||
steps: | |||||
- name: Notify Slack if any crashers | |||||
uses: rtCamp/action-slack-notify@ae4223259071871559b6e9d08b24a63d71b3f0c0 | |||||
env: | |||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} | |||||
SLACK_CHANNEL: tendermint-internal | |||||
SLACK_USERNAME: Nightly Fuzz Tests | |||||
SLACK_ICON_EMOJI: ':skull:' | |||||
SLACK_COLOR: danger | |||||
SLACK_MESSAGE: Crashers found in Nightly Fuzz tests | |||||
SLACK_FOOTER: '' |
@ -0,0 +1,39 @@ | |||||
#!/usr/bin/make -f | |||||
.PHONY: fuzz-mempool | |||||
fuzz-mempool: | |||||
cd mempool && \ | |||||
rm -f *-fuzz.zip && \ | |||||
go-fuzz-build && \ | |||||
go-fuzz | |||||
.PHONY: fuzz-p2p-addrbook | |||||
fuzz-p2p-addrbook: | |||||
cd p2p/addrbook && \ | |||||
rm -f *-fuzz.zip && \ | |||||
go run ./init-corpus/main.go && \ | |||||
go-fuzz-build && \ | |||||
go-fuzz | |||||
.PHONY: fuzz-p2p-pex | |||||
fuzz-p2p-pex: | |||||
cd p2p/pex && \ | |||||
rm -f *-fuzz.zip && \ | |||||
go run ./init-corpus/main.go && \ | |||||
go-fuzz-build && \ | |||||
go-fuzz | |||||
.PHONY: fuzz-p2p-sc | |||||
fuzz-p2p-sc: | |||||
cd p2p/secret_connection && \ | |||||
rm -f *-fuzz.zip && \ | |||||
go run ./init-corpus/main.go && \ | |||||
go-fuzz-build && \ | |||||
go-fuzz | |||||
.PHONY: fuzz-rpc-server | |||||
fuzz-rpc-server: | |||||
cd rpc/jsonrpc/server && \ | |||||
rm -f *-fuzz.zip && \ | |||||
go-fuzz-build && \ | |||||
go-fuzz |
@ -0,0 +1,72 @@ | |||||
# fuzz | |||||
Fuzzing for various packages in Tendermint using [go-fuzz](https://github.com/dvyukov/go-fuzz) library. | |||||
Inputs: | |||||
- mempool `CheckTx` (using kvstore in-process ABCI app) | |||||
- p2p `Addrbook#AddAddress` | |||||
- p2p `pex.Reactor#Receive` | |||||
- p2p `SecretConnection#Read` and `SecretConnection#Write` | |||||
- rpc jsonrpc server | |||||
## Directory structure | |||||
``` | |||||
| test | |||||
| |- corpus/ | |||||
| |- crashers/ | |||||
| |- init-corpus/ | |||||
| |- suppressions/ | |||||
| |- testdata/ | |||||
| |- <testname>.go | |||||
``` | |||||
`/corpus` directory contains corpus data. The idea is to help the fuzzier to | |||||
understand what bytes sequences are semantically valid (e.g. if we're testing | |||||
PNG decoder, then we would put black-white PNG into corpus directory; with | |||||
blockchain reactor - we would put blockchain messages into corpus). | |||||
`/init-corpus` (if present) contains a script for generating corpus data. | |||||
`/testdata` directory may contain an additional data (like `addrbook.json`). | |||||
Upon running the fuzzier, `/crashers` and `/suppressions` dirs will be created, | |||||
along with <testname>.zip archive. `/crashers` will show any inputs, which have | |||||
lead to panics (plus a trace). `/suppressions` will show any suppressed inputs. | |||||
## Running | |||||
```sh | |||||
make fuzz-mempool | |||||
make fuzz-p2p-addrbook | |||||
make fuzz-p2p-pex | |||||
make fuzz-p2p-sc | |||||
make fuzz-rpc-server | |||||
``` | |||||
Each command will create corpus data (if needed), generate a fuzz archive and | |||||
call `go-fuzz` executable. | |||||
Then watch out for the respective outputs in the fuzzer output to announce new | |||||
crashers which can be found in the directory `crashers`. | |||||
For example if we find | |||||
```sh | |||||
ls crashers/ | |||||
61bde465f47c93254d64d643c3b2480e0a54666e | |||||
61bde465f47c93254d64d643c3b2480e0a54666e.output | |||||
61bde465f47c93254d64d643c3b2480e0a54666e.quoted | |||||
da39a3ee5e6b4b0d3255bfef95601890afd80709 | |||||
da39a3ee5e6b4b0d3255bfef95601890afd80709.output | |||||
da39a3ee5e6b4b0d3255bfef95601890afd80709.quoted | |||||
``` | |||||
the crashing bytes generated by the fuzzer will be in | |||||
`61bde465f47c93254d64d643c3b2480e0a54666e` the respective crash report in | |||||
`61bde465f47c93254d64d643c3b2480e0a54666e.output` | |||||
and the bug report can be created by retrieving the bytes in | |||||
`61bde465f47c93254d64d643c3b2480e0a54666e` and feeding those back into the | |||||
`Fuzz` function. |
@ -0,0 +1,34 @@ | |||||
package checktx | |||||
import ( | |||||
"github.com/tendermint/tendermint/abci/example/kvstore" | |||||
"github.com/tendermint/tendermint/config" | |||||
mempl "github.com/tendermint/tendermint/mempool" | |||||
"github.com/tendermint/tendermint/proxy" | |||||
) | |||||
var mempool mempl.Mempool | |||||
func init() { | |||||
app := kvstore.NewApplication() | |||||
cc := proxy.NewLocalClientCreator(app) | |||||
appConnMem, _ := cc.NewABCIClient() | |||||
err := appConnMem.Start() | |||||
if err != nil { | |||||
panic(err) | |||||
} | |||||
cfg := config.DefaultMempoolConfig() | |||||
cfg.Broadcast = false | |||||
mempool = mempl.NewCListMempool(cfg, appConnMem, 0) | |||||
} | |||||
func Fuzz(data []byte) int { | |||||
err := mempool.CheckTx(data, nil, mempl.TxInfo{}) | |||||
if err != nil { | |||||
return 0 | |||||
} | |||||
return 1 | |||||
} |
@ -0,0 +1,35 @@ | |||||
// nolint: gosec | |||||
package addr | |||||
import ( | |||||
"encoding/json" | |||||
"fmt" | |||||
"math/rand" | |||||
"github.com/tendermint/tendermint/p2p" | |||||
"github.com/tendermint/tendermint/p2p/pex" | |||||
) | |||||
var addrBook = pex.NewAddrBook("./testdata/addrbook.json", true) | |||||
func Fuzz(data []byte) int { | |||||
addr := new(p2p.NetAddress) | |||||
if err := json.Unmarshal(data, addr); err != nil { | |||||
return -1 | |||||
} | |||||
// Fuzz AddAddress. | |||||
err := addrBook.AddAddress(addr, addr) | |||||
if err != nil { | |||||
return 0 | |||||
} | |||||
// Also, make sure PickAddress always returns a non-nil address. | |||||
bias := rand.Intn(100) | |||||
if p := addrBook.PickAddress(bias); p == nil { | |||||
panic(fmt.Sprintf("picked a nil address (bias: %d, addrBook size: %v)", | |||||
bias, addrBook.Size())) | |||||
} | |||||
return 1 | |||||
} |
@ -0,0 +1,58 @@ | |||||
// nolint: gosec | |||||
package main | |||||
import ( | |||||
"encoding/json" | |||||
"flag" | |||||
"fmt" | |||||
"io/ioutil" | |||||
"log" | |||||
"net" | |||||
"os" | |||||
"path/filepath" | |||||
"github.com/tendermint/tendermint/crypto/ed25519" | |||||
"github.com/tendermint/tendermint/p2p" | |||||
) | |||||
func main() { | |||||
baseDir := flag.String("base", ".", `where the "corpus" directory will live`) | |||||
flag.Parse() | |||||
initCorpus(*baseDir) | |||||
} | |||||
func initCorpus(baseDir string) { | |||||
log.SetFlags(0) | |||||
// create "corpus" directory | |||||
corpusDir := filepath.Join(baseDir, "corpus") | |||||
if err := os.MkdirAll(corpusDir, 0755); err != nil { | |||||
log.Fatalf("Creating %q err: %v", corpusDir, err) | |||||
} | |||||
// create corpus | |||||
privKey := ed25519.GenPrivKey() | |||||
addrs := []*p2p.NetAddress{ | |||||
{ID: p2p.NodeIDFromPubKey(privKey.PubKey()), IP: net.IPv4(0, 0, 0, 0), Port: 0}, | |||||
{ID: p2p.NodeIDFromPubKey(privKey.PubKey()), IP: net.IPv4(127, 0, 0, 0), Port: 80}, | |||||
{ID: p2p.NodeIDFromPubKey(privKey.PubKey()), IP: net.IPv4(213, 87, 10, 200), Port: 8808}, | |||||
{ID: p2p.NodeIDFromPubKey(privKey.PubKey()), IP: net.IPv4(111, 111, 111, 111), Port: 26656}, | |||||
{ID: p2p.NodeIDFromPubKey(privKey.PubKey()), IP: net.ParseIP("2001:db8::68"), Port: 26656}, | |||||
} | |||||
for i, addr := range addrs { | |||||
filename := filepath.Join(corpusDir, fmt.Sprintf("%d.json", i)) | |||||
bz, err := json.Marshal(addr) | |||||
if err != nil { | |||||
log.Fatalf("can't marshal %v: %v", addr, err) | |||||
} | |||||
if err := ioutil.WriteFile(filename, bz, 0644); err != nil { | |||||
log.Fatalf("can't write %v to %q: %v", addr, filename, err) | |||||
} | |||||
log.Printf("wrote %q", filename) | |||||
} | |||||
} |
@ -0,0 +1,82 @@ | |||||
// nolint: gosec | |||||
package main | |||||
import ( | |||||
"flag" | |||||
"fmt" | |||||
"io/ioutil" | |||||
"log" | |||||
"math/rand" | |||||
"os" | |||||
"path/filepath" | |||||
"github.com/tendermint/tendermint/crypto/ed25519" | |||||
"github.com/tendermint/tendermint/p2p" | |||||
tmp2p "github.com/tendermint/tendermint/proto/tendermint/p2p" | |||||
) | |||||
func main() { | |||||
baseDir := flag.String("base", ".", `where the "corpus" directory will live`) | |||||
flag.Parse() | |||||
initCorpus(*baseDir) | |||||
} | |||||
func initCorpus(rootDir string) { | |||||
log.SetFlags(0) | |||||
corpusDir := filepath.Join(rootDir, "corpus") | |||||
if err := os.MkdirAll(corpusDir, 0755); err != nil { | |||||
log.Fatalf("Creating %q err: %v", corpusDir, err) | |||||
} | |||||
sizes := []int{0, 1, 2, 17, 5, 31} | |||||
// Make the PRNG predictable | |||||
rand.Seed(10) | |||||
for _, n := range sizes { | |||||
var addrs []*p2p.NetAddress | |||||
// IPv4 addresses | |||||
for i := 0; i < n; i++ { | |||||
privKey := ed25519.GenPrivKey() | |||||
addr := fmt.Sprintf( | |||||
"%s@%v.%v.%v.%v:26656", | |||||
p2p.NodeIDFromPubKey(privKey.PubKey()), | |||||
rand.Int()%256, | |||||
rand.Int()%256, | |||||
rand.Int()%256, | |||||
rand.Int()%256, | |||||
) | |||||
netAddr, _ := p2p.NewNetAddressString(addr) | |||||
addrs = append(addrs, netAddr) | |||||
} | |||||
// IPv6 addresses | |||||
privKey := ed25519.GenPrivKey() | |||||
ipv6a, err := p2p.NewNetAddressString( | |||||
fmt.Sprintf("%s@[ff02::1:114]:26656", p2p.NodeIDFromPubKey(privKey.PubKey()))) | |||||
if err != nil { | |||||
log.Fatalf("can't create a new netaddress: %v", err) | |||||
} | |||||
addrs = append(addrs, ipv6a) | |||||
msg := tmp2p.Message{ | |||||
Sum: &tmp2p.Message_PexAddrs{ | |||||
PexAddrs: &tmp2p.PexAddrs{Addrs: p2p.NetAddressesToProto(addrs)}, | |||||
}, | |||||
} | |||||
bz, err := msg.Marshal() | |||||
if err != nil { | |||||
log.Fatalf("unable to marshal: %v", err) | |||||
} | |||||
filename := filepath.Join(rootDir, "corpus", fmt.Sprintf("%d", n)) | |||||
if err := ioutil.WriteFile(filename, bz, 0644); err != nil { | |||||
log.Fatalf("can't write %X to %q: %v", bz, filename, err) | |||||
} | |||||
log.Printf("wrote %q", filename) | |||||
} | |||||
} |
@ -0,0 +1,86 @@ | |||||
package pex | |||||
import ( | |||||
"net" | |||||
"github.com/tendermint/tendermint/config" | |||||
"github.com/tendermint/tendermint/crypto/ed25519" | |||||
"github.com/tendermint/tendermint/libs/log" | |||||
"github.com/tendermint/tendermint/libs/service" | |||||
"github.com/tendermint/tendermint/p2p" | |||||
"github.com/tendermint/tendermint/p2p/pex" | |||||
"github.com/tendermint/tendermint/version" | |||||
) | |||||
var ( | |||||
pexR *pex.Reactor | |||||
peer p2p.Peer | |||||
) | |||||
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.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 | |||||
}) | |||||
pexR.SetSwitch(sw) | |||||
pexR.Receive(pex.PexChannel, peer, data) | |||||
return 1 | |||||
} | |||||
type fuzzPeer struct { | |||||
*service.BaseService | |||||
m map[string]interface{} | |||||
} | |||||
var _ p2p.Peer = (*fuzzPeer)(nil) | |||||
func newFuzzPeer() *fuzzPeer { | |||||
fp := &fuzzPeer{m: make(map[string]interface{})} | |||||
fp.BaseService = service.NewBaseService(nil, "fuzzPeer", fp) | |||||
return fp | |||||
} | |||||
var privKey = ed25519.GenPrivKey() | |||||
var nodeID = p2p.NodeIDFromPubKey(privKey.PubKey()) | |||||
var defaultNodeInfo = p2p.NodeInfo{ | |||||
ProtocolVersion: p2p.NewProtocolVersion( | |||||
version.P2PProtocol, | |||||
version.BlockProtocol, | |||||
0, | |||||
), | |||||
NodeID: nodeID, | |||||
ListenAddr: "0.0.0.0:98992", | |||||
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) RemoteAddr() net.Addr { | |||||
return &net.TCPAddr{IP: fp.RemoteIP(), Port: 98991, Zone: ""} | |||||
} | |||||
func (fp *fuzzPeer) IsOutbound() bool { return false } | |||||
func (fp *fuzzPeer) IsPersistent() bool { return false } | |||||
func (fp *fuzzPeer) CloseConn() error { return nil } | |||||
func (fp *fuzzPeer) NodeInfo() p2p.NodeInfo { return defaultNodeInfo } | |||||
func (fp *fuzzPeer) Status() p2p.ConnectionStatus { var cs p2p.ConnectionStatus; return cs } | |||||
func (fp *fuzzPeer) SocketAddr() *p2p.NetAddress { return p2p.NewNetAddress(fp.ID(), fp.RemoteAddr()) } | |||||
func (fp *fuzzPeer) Send(byte, []byte) bool { return true } | |||||
func (fp *fuzzPeer) TrySend(byte, []byte) bool { return true } | |||||
func (fp *fuzzPeer) Set(key string, value interface{}) { fp.m[key] = value } | |||||
func (fp *fuzzPeer) Get(key string) interface{} { return fp.m[key] } |
@ -0,0 +1,48 @@ | |||||
// nolint: gosec | |||||
package main | |||||
import ( | |||||
"flag" | |||||
"fmt" | |||||
"io/ioutil" | |||||
"log" | |||||
"os" | |||||
"path/filepath" | |||||
) | |||||
func main() { | |||||
baseDir := flag.String("base", ".", `where the "corpus" directory will live`) | |||||
flag.Parse() | |||||
initCorpus(*baseDir) | |||||
} | |||||
func initCorpus(baseDir string) { | |||||
log.SetFlags(0) | |||||
corpusDir := filepath.Join(baseDir, "corpus") | |||||
if err := os.MkdirAll(corpusDir, 0755); err != nil { | |||||
log.Fatal(err) | |||||
} | |||||
data := []string{ | |||||
"dadc04c2-cfb1-4aa9-a92a-c0bf780ec8b6", | |||||
"", | |||||
" ", | |||||
" a ", | |||||
`{"a": 12, "tsp": 999, k: "blue"}`, | |||||
`9999.999`, | |||||
`""`, | |||||
`Tendermint fuzzing`, | |||||
} | |||||
for i, datum := range data { | |||||
filename := filepath.Join(corpusDir, fmt.Sprintf("%d", i)) | |||||
if err := ioutil.WriteFile(filename, []byte(datum), 0644); err != nil { | |||||
log.Fatalf("can't write %v to %q: %v", datum, filename, err) | |||||
} | |||||
log.Printf("wrote %q", filename) | |||||
} | |||||
} |
@ -0,0 +1,107 @@ | |||||
package secretconnection | |||||
import ( | |||||
"bytes" | |||||
"fmt" | |||||
"io" | |||||
"log" | |||||
"github.com/tendermint/tendermint/crypto/ed25519" | |||||
"github.com/tendermint/tendermint/libs/async" | |||||
sc "github.com/tendermint/tendermint/p2p/conn" | |||||
) | |||||
func Fuzz(data []byte) int { | |||||
if len(data) == 0 { | |||||
return -1 | |||||
} | |||||
fooConn, barConn := makeSecretConnPair() | |||||
n, err := fooConn.Write(data) | |||||
if err != nil { | |||||
panic(err) | |||||
} | |||||
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])) | |||||
} | |||||
return 1 | |||||
} | |||||
type kvstoreConn struct { | |||||
*io.PipeReader | |||||
*io.PipeWriter | |||||
} | |||||
func (drw kvstoreConn) Close() (err error) { | |||||
err2 := drw.PipeWriter.CloseWithError(io.EOF) | |||||
err1 := drw.PipeReader.Close() | |||||
if err2 != nil { | |||||
return err | |||||
} | |||||
return err1 | |||||
} | |||||
// Each returned ReadWriteCloser is akin to a net.Connection | |||||
func makeKVStoreConnPair() (fooConn, barConn kvstoreConn) { | |||||
barReader, fooWriter := io.Pipe() | |||||
fooReader, barWriter := io.Pipe() | |||||
return kvstoreConn{fooReader, fooWriter}, kvstoreConn{barReader, barWriter} | |||||
} | |||||
func makeSecretConnPair() (fooSecConn, barSecConn *sc.SecretConnection) { | |||||
var ( | |||||
fooConn, barConn = makeKVStoreConnPair() | |||||
fooPrvKey = ed25519.GenPrivKey() | |||||
fooPubKey = fooPrvKey.PubKey() | |||||
barPrvKey = ed25519.GenPrivKey() | |||||
barPubKey = barPrvKey.PubKey() | |||||
) | |||||
// Make connections from both sides in parallel. | |||||
var trs, ok = async.Parallel( | |||||
func(_ int) (val interface{}, abort bool, err error) { | |||||
fooSecConn, err = sc.MakeSecretConnection(fooConn, fooPrvKey) | |||||
if err != nil { | |||||
log.Printf("failed to establish SecretConnection for foo: %v", err) | |||||
return nil, true, err | |||||
} | |||||
remotePubBytes := fooSecConn.RemotePubKey() | |||||
if !remotePubBytes.Equals(barPubKey) { | |||||
err = fmt.Errorf("unexpected fooSecConn.RemotePubKey. Expected %v, got %v", | |||||
barPubKey, fooSecConn.RemotePubKey()) | |||||
log.Print(err) | |||||
return nil, true, err | |||||
} | |||||
return nil, false, nil | |||||
}, | |||||
func(_ int) (val interface{}, abort bool, err error) { | |||||
barSecConn, err = sc.MakeSecretConnection(barConn, barPrvKey) | |||||
if barSecConn == nil { | |||||
log.Printf("failed to establish SecretConnection for bar: %v", err) | |||||
return nil, true, err | |||||
} | |||||
remotePubBytes := barSecConn.RemotePubKey() | |||||
if !remotePubBytes.Equals(fooPubKey) { | |||||
err = fmt.Errorf("unexpected barSecConn.RemotePubKey. Expected %v, got %v", | |||||
fooPubKey, barSecConn.RemotePubKey()) | |||||
log.Print(err) | |||||
return nil, true, err | |||||
} | |||||
return nil, false, nil | |||||
}, | |||||
) | |||||
if trs.FirstError() != nil { | |||||
log.Fatalf("unexpected error: %v", trs.FirstError()) | |||||
} | |||||
if !ok { | |||||
log.Fatal("Unexpected task abortion") | |||||
} | |||||
return fooSecConn, barSecConn | |||||
} |
@ -0,0 +1,44 @@ | |||||
package handler | |||||
import ( | |||||
"bytes" | |||||
"encoding/json" | |||||
"io/ioutil" | |||||
"net/http" | |||||
"net/http/httptest" | |||||
"github.com/tendermint/tendermint/libs/log" | |||||
rs "github.com/tendermint/tendermint/rpc/jsonrpc/server" | |||||
types "github.com/tendermint/tendermint/rpc/jsonrpc/types" | |||||
) | |||||
var rpcFuncMap = map[string]*rs.RPCFunc{ | |||||
"c": rs.NewRPCFunc(func(s string, i int) (string, int) { return "foo", 200 }, "s,i"), | |||||
} | |||||
var mux *http.ServeMux | |||||
func init() { | |||||
mux := http.NewServeMux() | |||||
buf := new(bytes.Buffer) | |||||
lgr := log.NewTMLogger(buf) | |||||
rs.RegisterRPCFuncs(mux, rpcFuncMap, lgr) | |||||
} | |||||
func Fuzz(data []byte) int { | |||||
req, _ := http.NewRequest("POST", "http://localhost/", bytes.NewReader(data)) | |||||
rec := httptest.NewRecorder() | |||||
mux.ServeHTTP(rec, req) | |||||
res := rec.Result() | |||||
blob, err := ioutil.ReadAll(res.Body) | |||||
if err != nil { | |||||
panic(err) | |||||
} | |||||
if err := res.Body.Close(); err != nil { | |||||
panic(err) | |||||
} | |||||
recv := new(types.RPCResponse) | |||||
if err := json.Unmarshal(blob, recv); err != nil { | |||||
panic(err) | |||||
} | |||||
return 1 | |||||
} |