bindings_test.go 8.43 KB
Newer Older
1
2
3
4
5
6
package go_sectorbuilder_test

import (
	"bytes"
	"crypto/rand"
	"errors"
7
	"fmt"
8
9
	"io"
	"io/ioutil"
10
	"math/big"
11
12
13
14
15
16
	"os"
	"testing"
	"time"
	"unsafe"

	sb "github.com/filecoin-project/go-sectorbuilder"
17
	"github.com/filecoin-project/go-sectorbuilder/sealed_sector_health"
18
	"github.com/filecoin-project/go-sectorbuilder/sealing_state"
19
20
21
22
23

	"github.com/stretchr/testify/require"
)

func TestSectorBuilderLifecycle(t *testing.T) {
24
25
	ticketA := sb.SealTicket{
		BlockHeight: 0,
26
		TicketBytes: [32]byte{5, 4, 2},
27
28
29
30
31
32
33
	}

	ticketB := sb.SealTicket{
		BlockHeight: 10,
		TicketBytes: [32]byte{1, 2, 3},
	}

34
35
36
37
38
39
40
41
42
43
	seedA := sb.SealSeed{
		BlockHeight: 50,
		TicketBytes: [32]byte{7, 4, 2},
	}

	seedB := sb.SealSeed{
		BlockHeight: 60,
		TicketBytes: [32]byte{9, 10, 11},
	}

44
45
	proverID := [32]byte{6, 7, 8}

46
47
48
49
50
51
52
53
54
	metadataDir := requireTempDirPath(t)
	defer require.NoError(t, os.Remove(metadataDir))

	sealedSectorDir := requireTempDirPath(t)
	defer require.NoError(t, os.Remove(sealedSectorDir))

	stagedSectorDir := requireTempDirPath(t)
	defer require.NoError(t, os.Remove(stagedSectorDir))

55
56
57
	sectorCacheRootDir := requireTempDirPath(t)
	defer require.NoError(t, os.Remove(sectorCacheRootDir))

58
	ptr, err := sb.InitSectorBuilder(1024, 2, 0, metadataDir, proverID, sealedSectorDir, stagedSectorDir, sectorCacheRootDir, 1, 2)
59
60
61
	require.NoError(t, err)
	defer sb.DestroySectorBuilder(ptr)

62
63
64
65
66
	// verify that we've not yet sealed a sector
	sealedSectors, err := sb.GetAllSealedSectorsWithHealth(ptr)
	require.NoError(t, err)
	require.Equal(t, 0, len(sealedSectors), "expected to see zero sealed sectors")

67
68
69
70
71
72
73
	// compute the max user-bytes that can fit into a staged sector such that
	// bit-padding ("preprocessing") expands the file to $SECTOR_SIZE
	maxPieceSize := sb.GetMaxUserBytesPerStagedSector(1024)

	// create a piece which consumes all available space in a new, staged
	// sector
	pieceBytes := make([]byte, maxPieceSize)
74
75
76
	read, err := io.ReadFull(rand.Reader, pieceBytes)
	require.Equal(t, uint64(read), maxPieceSize)

77
	require.NoError(t, err)
78
79
80
81
	pieceFileA := requireTempFile(t, bytes.NewReader(pieceBytes), maxPieceSize)

	require.NoError(t, err)
	pieceFileB := requireTempFile(t, bytes.NewReader(pieceBytes), maxPieceSize)
82
83

	// generate piece commitment
84
	commP, err := sb.GeneratePieceCommitmentFromFile(pieceFileA, maxPieceSize)
85
86
	require.NoError(t, err)

87
88
89
90
91
	publicPieceInfoA := []sb.PublicPieceInfo{{
		Size:  maxPieceSize,
		CommP: commP,
	}}

92
93
94
	preComputedCommD, err := sb.GenerateDataCommitment(1024, publicPieceInfoA)
	require.NoError(t, err)

95
	// seek to the beginning
96
	_, err = pieceFileA.Seek(0, 0)
97
98
	require.NoError(t, err)

99
100
	// write a piece to a staged sector, reducing remaining space to 0
	sectorIDA, err := sb.AddPieceFromFile(ptr, "snoqualmie", maxPieceSize, pieceFileA)
101
102
	require.NoError(t, err)

Sidney Keese's avatar
Sidney Keese committed
103
	stagedSectors, err := sb.GetAllStagedSectors(ptr)
104
	require.NoError(t, err)
Sidney Keese's avatar
Sidney Keese committed
105
106
107
108
	require.Equal(t, 1, len(stagedSectors))
	stagedSector := stagedSectors[0]
	require.Equal(t, uint64(1), stagedSector.SectorID)

109
	// block until the sector is ready for us to begin sealing
110
	statusA, err := pollForSectorSealingStatus(ptr, sectorIDA, sealing_state.FullyPacked, time.Minute)
111
112
	require.NoError(t, err)

113
	// pre-commit sector to a ticket (in a non-blocking fashion)
114
	go func() {
115
		out, err := sb.SealPreCommit(ptr, statusA.SectorID, ticketA)
116
		require.NoError(t, err)
117
118
		require.Equal(t, sectorIDA, out.SectorID)
		require.Equal(t, ticketA.TicketBytes, out.Ticket.TicketBytes)
119
		require.True(t, bytes.Equal(preComputedCommD[:], out.CommD[:]))
120
121
122
123
124
125
	}()

	// write a second piece to a staged sector, reducing remaining space to 0
	sectorIDB, err := sb.AddPieceFromFile(ptr, "duvall", maxPieceSize, pieceFileB)
	require.NoError(t, err)

126
	// pre-commit second sector to a ticket too
127
	go func() {
128
		_, err := sb.SealPreCommit(ptr, sectorIDB, ticketB)
129
130
131
		require.NoError(t, err)
	}()

132
133
	// block until both sectors have successfully pre-committed
	statusA, err = pollForSectorSealingStatus(ptr, sectorIDA, sealing_state.PreCommitted, 30*time.Minute)
134
135
	require.NoError(t, err)

136
	statusB, err := pollForSectorSealingStatus(ptr, sectorIDB, sealing_state.PreCommitted, 30*time.Minute)
137
	require.NoError(t, err)
138

139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
	// commit both sectors concurrently
	go func() {
		out, err := sb.SealCommit(ptr, sectorIDA, seedA)
		require.NoError(t, err)
		require.Equal(t, sectorIDA, out.SectorID)
		require.Equal(t, ticketA.TicketBytes, out.Ticket.TicketBytes)
		require.Equal(t, seedA.TicketBytes, out.Seed.TicketBytes)
	}()

	go func() {
		out, err := sb.SealCommit(ptr, sectorIDB, seedB)
		require.NoError(t, err)
		require.Equal(t, sectorIDB, out.SectorID)
	}()

	// block until both sectors have finished sealing (successfully)
	statusA, err = pollForSectorSealingStatus(ptr, sectorIDA, sealing_state.Committed, 30*time.Minute)
156
157
	require.NoError(t, err)

158
159
160
161
162
163
164
165
166
167
	statusB, err = pollForSectorSealingStatus(ptr, sectorIDB, sealing_state.Committed, 30*time.Minute)
	require.NoError(t, err)

	// verify that we used the tickets and seeds we'd intended to use
	require.Equal(t, ticketA.TicketBytes, statusA.Ticket.TicketBytes)
	require.Equal(t, ticketB.TicketBytes, statusB.Ticket.TicketBytes)
	require.Equal(t, seedA.TicketBytes, statusA.Seed.TicketBytes)
	require.Equal(t, seedB.TicketBytes, statusB.Seed.TicketBytes)

	// verify the seal proof
168
	isValid, err := sb.VerifySeal(1024, statusA.CommR, statusA.CommD, proverID, ticketA.TicketBytes, seedA.TicketBytes, sectorIDA, statusA.Proof)
169
170
171
	require.NoError(t, err)
	require.True(t, isValid)

172
173
	// enforces sort ordering of SectorInfo tuples
	sectorInfo := sb.NewSortedSectorInfo(sb.SectorInfo{
174
175
		SectorID: statusA.SectorID,
		CommR:    statusA.CommR,
176
177
	})

178
	// generate a PoSt
179
	proofs, err := sb.GeneratePoSt(ptr, sectorInfo, [32]byte{}, []uint64{})
180
181
182
	require.NoError(t, err)

	// verify the PoSt
183
	isValid, err = sb.VerifyPoSt(1024, sectorInfo, [32]byte{}, proofs, []uint64{})
184
	require.NoError(t, err)
185
186
	require.True(t, isValid)

187
	sealedSectors, err = sb.GetAllSealedSectorsWithHealth(ptr)
188
	require.NoError(t, err)
189
190
191
192
193
194
195
	require.Equal(t, 2, len(sealedSectors), "expected to see two sealed sectors")
	for _, sealedSector := range sealedSectors {
		require.Equal(t, sealed_sector_health.Ok, sealedSector.Health)
	}

	// both sealed sectors contain the same data, so either will suffice
	require.Equal(t, commP, sealedSectors[0].CommD)
Sidney Keese's avatar
Sidney Keese committed
196

197
198
199
	// unseal the sector and retrieve the client's piece, verifying that the
	// retrieved bytes match what we originally wrote to the staged sector
	unsealedPieceBytes, err := sb.ReadPieceFromSealedSector(ptr, "snoqualmie")
200
	require.NoError(t, err)
201
202
203
	require.Equal(t, pieceBytes, unsealedPieceBytes)
}

204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
func TestJsonMarshalSymmetry(t *testing.T) {
	for i := 0; i < 100; i++ {
		xs := make([]sb.SectorInfo, 10)
		for j := 0; j < 10; j++ {
			var x sb.SectorInfo
			_, err := io.ReadFull(rand.Reader, x.CommR[:])
			require.NoError(t, err)

			n, err := rand.Int(rand.Reader, big.NewInt(500))
			require.NoError(t, err)
			x.SectorID = n.Uint64()
			xs[j] = x
		}
		toSerialize := sb.NewSortedSectorInfo(xs...)

		serialized, err := toSerialize.MarshalJSON()
		require.NoError(t, err)

		var fromSerialized sb.SortedSectorInfo
		err = fromSerialized.UnmarshalJSON(serialized)
		require.NoError(t, err)

		require.Equal(t, toSerialize, fromSerialized)
	}
}

230
func pollForSectorSealingStatus(ptr unsafe.Pointer, sectorID uint64, targetState sealing_state.State, timeout time.Duration) (status sb.SectorSealingStatus, retErr error) {
231
	timeoutCh := time.After(timeout)
232
	lastState := sealing_state.Unknown
233

234
	tick := time.Tick(1 * time.Second)
235
236
237
238

	for {
		select {
		case <-timeoutCh:
239
			retErr = fmt.Errorf("timed out waiting for sector hit desired state (last state: %s)", lastState)
240
241
242
243
244
245
246
247
			return
		case <-tick:
			sealingStatus, err := sb.GetSectorSealingStatusByID(ptr, sectorID)
			if err != nil {
				retErr = err
				return
			}

248
249
			lastState = sealingStatus.State

250
			if sealingStatus.State == targetState {
251
252
				status = sealingStatus
				return
253
254
255
			} else if sealingStatus.State == sealing_state.Failed {
				retErr = errors.New(sealingStatus.SealErrorMsg)
				return
256
257
258
259
260
			}
		}
	}
}

261
func requireTempFile(t *testing.T, fileContentsReader io.Reader, size uint64) *os.File {
262
263
264
	file, err := ioutil.TempFile("", "")
	require.NoError(t, err)

265
266
267
268
269
270
271
272
273
	written, err := io.Copy(file, fileContentsReader)
	require.NoError(t, err)
	// check that we wrote everything
	require.Equal(t, uint64(written), size)

	require.NoError(t, file.Sync())

	// seek to the beginning
	_, err = file.Seek(0, 0)
274
275
	require.NoError(t, err)

276
	return file
277
278
279
280
281
282
283
284
}

func requireTempDirPath(t *testing.T) string {
	dir, err := ioutil.TempDir("", "")
	require.NoError(t, err)

	return dir
}