robustness_test.go raw
1 package btcwire
2
3 import (
4 "bytes"
5 "encoding/binary"
6 "testing"
7 )
8
9 // TestDecodersNeverPanic feeds deterministic pseudo-random bytes to every
10 // decoder. Errors are expected; panics and hangs are not.
11 func TestDecodersNeverPanic(t *testing.T) {
12 decoders := []struct {
13 name string
14 decode func([]byte) error
15 }{
16 {"version", func(b []byte) error { var v Version; return v.DecodePayload(b) }},
17 {"verack", func(b []byte) error { var v VerAck; return v.DecodePayload(b) }},
18 {"sendheaders", func(b []byte) error { var v SendHeaders; return v.DecodePayload(b) }},
19 {"wtxidrelay", func(b []byte) error { var v WtxidRelay; return v.DecodePayload(b) }},
20 {"sendaddrv2", func(b []byte) error { var v SendAddrV2; return v.DecodePayload(b) }},
21 {"ping", func(b []byte) error { var v Ping; return v.DecodePayload(b) }},
22 {"pong", func(b []byte) error { var v Pong; return v.DecodePayload(b) }},
23 {"feefilter", func(b []byte) error { var v FeeFilter; return v.DecodePayload(b) }},
24 {"getheaders", func(b []byte) error { var v GetHeaders; return v.DecodePayload(b) }},
25 {"headers", func(b []byte) error { var v Headers; return v.DecodePayload(b) }},
26 {"getcfheaders", func(b []byte) error { var v GetCFHeaders; return v.DecodePayload(b) }},
27 {"cfheaders", func(b []byte) error { var v CFHeaders; return v.DecodePayload(b) }},
28 {"getcfilters", func(b []byte) error { var v GetCFilters; return v.DecodePayload(b) }},
29 {"cfilter", func(b []byte) error { var v CFilter; return v.DecodePayload(b) }},
30 {"getcfcheckpt", func(b []byte) error { var v GetCFCheckpt; return v.DecodePayload(b) }},
31 {"cfcheckpt", func(b []byte) error { var v CFCheckpt; return v.DecodePayload(b) }},
32 {"inv", func(b []byte) error { var v Inv; return v.DecodePayload(b) }},
33 {"getdata", func(b []byte) error { var v GetData; return v.DecodePayload(b) }},
34 {"notfound", func(b []byte) error { var v NotFound; return v.DecodePayload(b) }},
35 {"tx", func(b []byte) error { var v Tx; return v.DecodePayload(b) }},
36 }
37
38 // xorshift64 keeps the test deterministic and dependency free.
39 var state uint64 = 0x9e3779b97f4a7c15
40 next := func() byte {
41 state ^= state << 13
42 state ^= state >> 7
43 state ^= state << 17
44 return byte(state)
45 }
46 for length := 0; length < 160; length++ {
47 buf := make([]byte, length)
48 for i := range buf {
49 buf[i] = next()
50 }
51 for _, decoder := range decoders {
52 // The only contract is that decoding random bytes does not
53 // panic.
54 _ = decoder.decode(buf)
55 }
56
57 // A frame whose magic is correct but whose command, length, and
58 // checksum are random exercises the framing validation paths.
59 frame := make([]byte, MessageHeaderSize+length)
60 for i := range frame {
61 frame[i] = next()
62 }
63 binary.BigEndian.PutUint32(frame[0:4], MainNet)
64 // Use the actual payload length so the decoder only allocates the
65 // bytes the frame really carries.
66 binary.LittleEndian.PutUint32(frame[16:20], uint32(length))
67 _, _ = DecodeMessage(MainNet, bytes.NewReader(frame))
68
69 // The same frame under the wrong network must fail on the magic.
70 if _, err := DecodeMessage(TestNet3, bytes.NewReader(frame)); err == nil {
71 t.Fatalf("frame with mainnet magic decoded as testnet (length %d)", length)
72 }
73 }
74 }
75
76 // TestBlockHeaderDeserializeNeverPanics checks every short and random input
77 // length for the fixed-size header decoder.
78 func TestBlockHeaderDeserializeNeverPanics(t *testing.T) {
79 var state uint64 = 0x123456789abcdef
80 next := func() byte {
81 state ^= state << 13
82 state ^= state >> 7
83 state ^= state << 17
84 return byte(state)
85 }
86 for length := 0; length < 200; length++ {
87 buf := make([]byte, length)
88 for i := range buf {
89 buf[i] = next()
90 }
91 var h BlockHeader
92 err := h.Deserialize(buf)
93 if length < BlockHeaderSize && err == nil {
94 t.Fatalf("Deserialize(%d bytes) succeeded", length)
95 }
96 if length >= BlockHeaderSize && err != nil {
97 t.Fatalf("Deserialize(%d bytes): %v", length, err)
98 }
99 // Target must reject rather than panic on random bits.
100 _ = h.CheckProofOfWork()
101 _, _ = h.Target()
102 }
103 }
104