package btcwire import ( "bytes" "encoding/binary" "testing" ) // TestDecodersNeverPanic feeds deterministic pseudo-random bytes to every // decoder. Errors are expected; panics and hangs are not. func TestDecodersNeverPanic(t *testing.T) { decoders := []struct { name string decode func([]byte) error }{ {"version", func(b []byte) error { var v Version; return v.DecodePayload(b) }}, {"verack", func(b []byte) error { var v VerAck; return v.DecodePayload(b) }}, {"sendheaders", func(b []byte) error { var v SendHeaders; return v.DecodePayload(b) }}, {"wtxidrelay", func(b []byte) error { var v WtxidRelay; return v.DecodePayload(b) }}, {"sendaddrv2", func(b []byte) error { var v SendAddrV2; return v.DecodePayload(b) }}, {"ping", func(b []byte) error { var v Ping; return v.DecodePayload(b) }}, {"pong", func(b []byte) error { var v Pong; return v.DecodePayload(b) }}, {"feefilter", func(b []byte) error { var v FeeFilter; return v.DecodePayload(b) }}, {"getheaders", func(b []byte) error { var v GetHeaders; return v.DecodePayload(b) }}, {"headers", func(b []byte) error { var v Headers; return v.DecodePayload(b) }}, {"getcfheaders", func(b []byte) error { var v GetCFHeaders; return v.DecodePayload(b) }}, {"cfheaders", func(b []byte) error { var v CFHeaders; return v.DecodePayload(b) }}, {"getcfilters", func(b []byte) error { var v GetCFilters; return v.DecodePayload(b) }}, {"cfilter", func(b []byte) error { var v CFilter; return v.DecodePayload(b) }}, {"getcfcheckpt", func(b []byte) error { var v GetCFCheckpt; return v.DecodePayload(b) }}, {"cfcheckpt", func(b []byte) error { var v CFCheckpt; return v.DecodePayload(b) }}, {"inv", func(b []byte) error { var v Inv; return v.DecodePayload(b) }}, {"getdata", func(b []byte) error { var v GetData; return v.DecodePayload(b) }}, {"notfound", func(b []byte) error { var v NotFound; return v.DecodePayload(b) }}, {"tx", func(b []byte) error { var v Tx; return v.DecodePayload(b) }}, } // xorshift64 keeps the test deterministic and dependency free. var state uint64 = 0x9e3779b97f4a7c15 next := func() byte { state ^= state << 13 state ^= state >> 7 state ^= state << 17 return byte(state) } for length := 0; length < 160; length++ { buf := make([]byte, length) for i := range buf { buf[i] = next() } for _, decoder := range decoders { // The only contract is that decoding random bytes does not // panic. _ = decoder.decode(buf) } // A frame whose magic is correct but whose command, length, and // checksum are random exercises the framing validation paths. frame := make([]byte, MessageHeaderSize+length) for i := range frame { frame[i] = next() } binary.BigEndian.PutUint32(frame[0:4], MainNet) // Use the actual payload length so the decoder only allocates the // bytes the frame really carries. binary.LittleEndian.PutUint32(frame[16:20], uint32(length)) _, _ = DecodeMessage(MainNet, bytes.NewReader(frame)) // The same frame under the wrong network must fail on the magic. if _, err := DecodeMessage(TestNet3, bytes.NewReader(frame)); err == nil { t.Fatalf("frame with mainnet magic decoded as testnet (length %d)", length) } } } // TestBlockHeaderDeserializeNeverPanics checks every short and random input // length for the fixed-size header decoder. func TestBlockHeaderDeserializeNeverPanics(t *testing.T) { var state uint64 = 0x123456789abcdef next := func() byte { state ^= state << 13 state ^= state >> 7 state ^= state << 17 return byte(state) } for length := 0; length < 200; length++ { buf := make([]byte, length) for i := range buf { buf[i] = next() } var h BlockHeader err := h.Deserialize(buf) if length < BlockHeaderSize && err == nil { t.Fatalf("Deserialize(%d bytes) succeeded", length) } if length >= BlockHeaderSize && err != nil { t.Fatalf("Deserialize(%d bytes): %v", length, err) } // Target must reject rather than panic on random bits. _ = h.CheckProofOfWork() _, _ = h.Target() } }