package btcwire import ( "bytes" "errors" "io" "reflect" "testing" "git.smesh.lol/nostrino/pkg/crypto/chainhash" ) func TestInvVectTypes(t *testing.T) { if InvVectTypeError != 0 || InvVectTypeTx != 1 || InvVectTypeBlock != 2 || InvVectTypeFilteredBlock != 3 || InvVectTypeWitnessTx != 0x40000001 { t.Fatalf("inventory type constants changed: %d %d %d %d %#x", InvVectTypeError, InvVectTypeTx, InvVectTypeBlock, InvVectTypeFilteredBlock, InvVectTypeWitnessTx) } } func TestInvListRoundTrip(t *testing.T) { types := []uint32{ InvVectTypeError, InvVectTypeTx, InvVectTypeBlock, InvVectTypeFilteredBlock, InvVectTypeWitnessTx, } var list []InvVect for i, typ := range types { list = append(list, InvVect{Type: typ, Hash: testHash(byte(0x60 + i))}) } tests := []struct { name string value Payload zero func() Payload }{ {"inv", &Inv{InvList: list}, func() Payload { return &Inv{} }}, {"getdata", &GetData{InvList: list}, func() Payload { return &GetData{} }}, {"notfound", &NotFound{InvList: list}, func() Payload { return &NotFound{} }}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { payload, err := tc.value.EncodePayload() if err != nil { t.Fatalf("EncodePayload: %v", err) } if got, wantLen := len(payload), 1+len(list)*invVectSize; got != wantLen { t.Fatalf("payload length = %d, want %d", got, wantLen) } // First vector: type little endian, then the hash in wire order. if got, want := payload[1:5], []byte{0x00, 0x00, 0x00, 0x00}; !bytes.Equal(got, want) { t.Fatalf("first vector type bytes = %x, want %x", got, want) } if got := payload[5 : 5+chainhash.HashSize]; !bytes.Equal(got, list[0].Hash[:]) { t.Fatalf("first vector hash = %x, want %x", got, list[0].Hash) } decoded := tc.zero() if err := decoded.DecodePayload(payload); err != nil { t.Fatalf("DecodePayload: %v", err) } if !reflect.DeepEqual(decoded, tc.value) { t.Fatalf("decoded = %+v, want %+v", decoded, tc.value) } }) } } func TestInvListEmpty(t *testing.T) { payload, err := (&Inv{InvList: []InvVect{}}).EncodePayload() if err != nil { t.Fatalf("EncodePayload: %v", err) } if !bytes.Equal(payload, []byte{0x00}) { t.Fatalf("payload = %x, want 00", payload) } var got Inv if err := got.DecodePayload(payload); err != nil { t.Fatalf("DecodePayload: %v", err) } if len(got.InvList) != 0 { t.Fatalf("decoded %d vectors, want 0", len(got.InvList)) } } func TestInvListLimits(t *testing.T) { tooMany := make([]InvVect, MaxInvPerMsg+1) if _, err := (&GetData{InvList: tooMany}).EncodePayload(); !errors.Is(err, ErrTooManyItems) { t.Fatalf("EncodePayload error = %v, want ErrTooManyItems", err) } // 50001 encoded as 0xfd 0x51 0xc3. payload := []byte{0xfd, byte((MaxInvPerMsg + 1) & 0xff), byte((MaxInvPerMsg + 1) >> 8)} var got NotFound if err := got.DecodePayload(payload); !errors.Is(err, ErrTooManyItems) { t.Fatalf("DecodePayload error = %v, want ErrTooManyItems", err) } } func TestInvListTruncated(t *testing.T) { want := &Inv{InvList: []InvVect{ {Type: InvVectTypeTx, Hash: testHash(0x70)}, {Type: InvVectTypeBlock, Hash: testHash(0x71)}, }} payload, err := want.EncodePayload() if err != nil { t.Fatalf("EncodePayload: %v", err) } for n := 0; n < len(payload); n++ { var got Inv if err := got.DecodePayload(payload[:n]); err == nil { t.Fatalf("decoding %d of %d bytes succeeded", n, len(payload)) } } // A count that claims more data than the payload holds is truncated // before any allocation is attempted. var got Inv if err := got.DecodePayload([]byte{0x02, 0x01}); !errors.Is(err, io.ErrUnexpectedEOF) { t.Fatalf("error = %v, want io.ErrUnexpectedEOF", err) } }