1// Copyright (C) 2019 Storj Labs, Inc.
2// See LICENSE for copying information.
3
4package audit
5
6import (
7	"context"
8	"math/rand"
9	"testing"
10
11	"github.com/stretchr/testify/assert"
12	"github.com/stretchr/testify/require"
13	"github.com/vivint/infectious"
14
15	"storj.io/common/pkcrypto"
16	"storj.io/common/storj"
17	"storj.io/common/testrand"
18	"storj.io/storj/satellite/metabase"
19)
20
21func TestFailingAudit(t *testing.T) {
22	const (
23		required = 8
24		total    = 14
25	)
26
27	f, err := infectious.NewFEC(required, total)
28	require.NoError(t, err)
29
30	shares := make([]infectious.Share, total)
31	output := func(s infectious.Share) {
32		shares[s.Number] = s.DeepCopy()
33	}
34
35	// the data to encode must be padded to a multiple of required, hence the
36	// underscores.
37	err = f.Encode([]byte("hello, world! __"), output)
38	require.NoError(t, err)
39
40	modifiedShares := make([]infectious.Share, len(shares))
41	for i := range shares {
42		modifiedShares[i] = shares[i].DeepCopy()
43	}
44
45	modifiedShares[0].Data[1] = '!'
46	modifiedShares[2].Data[0] = '#'
47	modifiedShares[3].Data[1] = '!'
48	modifiedShares[4].Data[0] = 'b'
49
50	badPieceNums := []int{0, 2, 3, 4}
51
52	ctx := context.Background()
53	auditPkgShares := make(map[int]Share, len(modifiedShares))
54	for i := range modifiedShares {
55		auditPkgShares[modifiedShares[i].Number] = Share{
56			PieceNum: modifiedShares[i].Number,
57			Data:     append([]byte(nil), modifiedShares[i].Data...),
58		}
59	}
60
61	pieceNums, correctedShares, err := auditShares(ctx, 8, 14, auditPkgShares)
62	if err != nil {
63		panic(err)
64	}
65
66	for i, num := range pieceNums {
67		if num != badPieceNums[i] {
68			t.Fatal("expected nums in pieceNums to be same as in badPieceNums")
69		}
70	}
71
72	require.Equal(t, shares, correctedShares)
73}
74
75func TestNotEnoughShares(t *testing.T) {
76	const (
77		required = 8
78		total    = 14
79	)
80
81	f, err := infectious.NewFEC(required, total)
82	require.NoError(t, err)
83
84	shares := make([]infectious.Share, total)
85	output := func(s infectious.Share) {
86		shares[s.Number] = s.DeepCopy()
87	}
88
89	// the data to encode must be padded to a multiple of required, hence the
90	// underscores.
91	err = f.Encode([]byte("hello, world! __"), output)
92	require.NoError(t, err)
93
94	ctx := context.Background()
95	auditPkgShares := make(map[int]Share, len(shares))
96	for i := range shares {
97		auditPkgShares[shares[i].Number] = Share{
98			PieceNum: shares[i].Number,
99			Data:     append([]byte(nil), shares[i].Data...),
100		}
101	}
102	_, _, err = auditShares(ctx, 20, 40, auditPkgShares)
103	require.NotNil(t, err)
104	require.Contains(t, err.Error(), "must specify at least the number of required shares")
105}
106
107func TestCreatePendingAudits(t *testing.T) {
108	const (
109		required = 8
110		total    = 14
111	)
112
113	f, err := infectious.NewFEC(required, total)
114	require.NoError(t, err)
115
116	shares := make([]infectious.Share, total)
117	output := func(s infectious.Share) {
118		shares[s.Number] = s.DeepCopy()
119	}
120
121	// The data to encode must be padded to a multiple of required, hence the
122	// underscores.
123	err = f.Encode([]byte("hello, world! __"), output)
124	require.NoError(t, err)
125
126	testNodeID := testrand.NodeID()
127
128	ctx := context.Background()
129	contained := make(map[int]storj.NodeID)
130	contained[1] = testNodeID
131
132	segment := testSegment()
133	segmentInfo := metabase.Segment{
134		StreamID:    segment.StreamID,
135		RootPieceID: testrand.PieceID(),
136		Redundancy: storj.RedundancyScheme{
137			Algorithm:      storj.ReedSolomon,
138			RequiredShares: required,
139			TotalShares:    total,
140			ShareSize:      int32(len(shares[0].Data)),
141		},
142	}
143	randomIndex := rand.Int31n(10)
144
145	pending, err := createPendingAudits(ctx, contained, shares, segment, segmentInfo, randomIndex)
146	require.NoError(t, err)
147	require.Equal(t, 1, len(pending))
148	assert.Equal(t, testNodeID, pending[0].NodeID)
149	assert.Equal(t, segmentInfo.RootPieceID, pending[0].PieceID)
150	assert.Equal(t, randomIndex, pending[0].StripeIndex)
151	assert.Equal(t, segmentInfo.Redundancy.ShareSize, pending[0].ShareSize)
152	assert.Equal(t, pkcrypto.SHA256Hash(shares[1].Data), pending[0].ExpectedShareHash)
153	assert.EqualValues(t, 0, pending[0].ReverifyCount)
154}
155