@@ -104,23 +104,18 @@ func (r *Retriever) Retrieve(ctx context.Context, dah *da.DataAvailabilityHeader
104
104
// quadrant request retries. Also, provides an API
105
105
// to reconstruct the block once enough shares are fetched.
106
106
type retrievalSession struct {
107
+ dah * da.DataAvailabilityHeader
107
108
bget blockservice.BlockGetter
108
109
adder * ipld.NmtNodeAdder
109
110
110
- treeFn rsmt2d.TreeConstructorFn
111
- codec rsmt2d.Codec
112
-
113
- dah * da.DataAvailabilityHeader
114
- squareImported * rsmt2d.ExtendedDataSquare
115
-
116
- quadrants []* quadrant
117
- sharesLks []sync.Mutex
118
- sharesCount uint32
119
-
120
- squareLk sync.RWMutex
121
- square [][]byte
122
- squareSig chan struct {}
123
- squareDn chan struct {}
111
+ // TODO(@Wondertan): Extract into a separate data structure https://github.com/celestiaorg/rsmt2d/issues/135
112
+ squareQuadrants []* quadrant
113
+ squareCellsLks [][]sync.Mutex
114
+ squareCellsCount uint32
115
+ squareSig chan struct {}
116
+ squareDn chan struct {}
117
+ squareLk sync.RWMutex
118
+ square * rsmt2d.ExtendedDataSquare
124
119
125
120
span trace.Span
126
121
}
@@ -133,29 +128,31 @@ func (r *Retriever) newSession(ctx context.Context, dah *da.DataAvailabilityHead
133
128
r .bServ ,
134
129
ipld .MaxSizeBatchOption (size ),
135
130
)
136
- ses := & retrievalSession {
137
- bget : blockservice .NewSession (ctx , r .bServ ),
138
- adder : adder ,
139
- treeFn : func (_ rsmt2d.Axis , index uint ) rsmt2d.Tree {
140
- tree := wrapper .NewErasuredNamespacedMerkleTree (uint64 (size )/ 2 , index , nmt .NodeVisitor (adder .Visit ))
141
- return & tree
142
- },
143
- codec : share .DefaultRSMT2DCodec (),
144
- dah : dah ,
145
- quadrants : newQuadrants (dah ),
146
- sharesLks : make ([]sync.Mutex , size * size ),
147
- square : make ([][]byte , size * size ),
148
- squareSig : make (chan struct {}, 1 ),
149
- squareDn : make (chan struct {}),
150
- span : trace .SpanFromContext (ctx ),
131
+
132
+ treeFn := func (_ rsmt2d.Axis , index uint ) rsmt2d.Tree {
133
+ tree := wrapper .NewErasuredNamespacedMerkleTree (uint64 (size )/ 2 , index , nmt .NodeVisitor (adder .Visit ))
134
+ return & tree
151
135
}
152
136
153
- square , err := rsmt2d .ImportExtendedDataSquare (ses . square , ses . codec , ses . treeFn )
137
+ square , err := rsmt2d .ImportExtendedDataSquare (make ([][] byte , size * size ), share . DefaultRSMT2DCodec (), treeFn )
154
138
if err != nil {
155
139
return nil , err
156
140
}
157
141
158
- ses .squareImported = square
142
+ ses := & retrievalSession {
143
+ dah : dah ,
144
+ bget : blockservice .NewSession (ctx , r .bServ ),
145
+ adder : adder ,
146
+ squareQuadrants : newQuadrants (dah ),
147
+ squareCellsLks : make ([][]sync.Mutex , size ),
148
+ squareSig : make (chan struct {}, 1 ),
149
+ squareDn : make (chan struct {}),
150
+ square : square ,
151
+ span : trace .SpanFromContext (ctx ),
152
+ }
153
+ for i := range ses .squareCellsLks {
154
+ ses .squareCellsLks [i ] = make ([]sync.Mutex , size )
155
+ }
159
156
go ses .request (ctx )
160
157
return ses , nil
161
158
}
@@ -170,36 +167,24 @@ func (rs *retrievalSession) Done() <-chan struct{} {
170
167
// Reconstruct tries to reconstruct the data square and returns it on success.
171
168
func (rs * retrievalSession ) Reconstruct (ctx context.Context ) (* rsmt2d.ExtendedDataSquare , error ) {
172
169
if rs .isReconstructed () {
173
- return rs .squareImported , nil
170
+ return rs .square , nil
174
171
}
175
172
// prevent further writes to the square
176
173
rs .squareLk .Lock ()
177
174
defer rs .squareLk .Unlock ()
178
175
179
- // TODO(@Wondertan): This is bad!
180
- // * We should not reimport the square multiple times
181
- // * We should set shares into imported square via
182
- // SetShare(https://github.com/celestiaorg/rsmt2d/issues/83) to accomplish the above point.
183
- {
184
- squareImported , err := rsmt2d .ImportExtendedDataSquare (rs .square , rs .codec , rs .treeFn )
185
- if err != nil {
186
- return nil , err
187
- }
188
- rs .squareImported = squareImported
189
- }
190
-
191
176
_ , span := tracer .Start (ctx , "reconstruct-square" )
192
177
defer span .End ()
193
178
194
179
// and try to repair with what we have
195
- err := rs .squareImported .Repair (rs .dah .RowsRoots , rs .dah .ColumnRoots )
180
+ err := rs .square .Repair (rs .dah .RowsRoots , rs .dah .ColumnRoots )
196
181
if err != nil {
197
182
span .RecordError (err )
198
183
return nil , err
199
184
}
200
185
log .Infow ("data square reconstructed" , "data_hash" , hex .EncodeToString (rs .dah .Hash ()), "size" , len (rs .dah .RowsRoots ))
201
186
close (rs .squareDn )
202
- return rs .squareImported , nil
187
+ return rs .square , nil
203
188
}
204
189
205
190
// isReconstructed report true whether the square attached to the session
@@ -232,16 +217,16 @@ func (rs *retrievalSession) Close() error {
232
217
func (rs * retrievalSession ) request (ctx context.Context ) {
233
218
t := time .NewTicker (RetrieveQuadrantTimeout )
234
219
defer t .Stop ()
235
- for retry := 0 ; retry < len (rs .quadrants ); retry ++ {
236
- q := rs .quadrants [retry ]
220
+ for retry := 0 ; retry < len (rs .squareQuadrants ); retry ++ {
221
+ q := rs .squareQuadrants [retry ]
237
222
log .Debugw ("requesting quadrant" ,
238
223
"axis" , q .source ,
239
224
"x" , q .x ,
240
225
"y" , q .y ,
241
226
"size" , len (q .roots ),
242
227
)
243
228
rs .span .AddEvent ("requesting quadrant" , trace .WithAttributes (
244
- attribute .Int ("axis" , q .source ),
229
+ attribute .Int ("axis" , int ( q .source ) ),
245
230
attribute .Int ("x" , q .x ),
246
231
attribute .Int ("y" , q .y ),
247
232
attribute .Int ("size" , len (q .roots )),
@@ -260,7 +245,7 @@ func (rs *retrievalSession) request(ctx context.Context) {
260
245
"size" , len (q .roots ),
261
246
)
262
247
rs .span .AddEvent ("quadrant request timeout" , trace .WithAttributes (
263
- attribute .Int ("axis" , q .source ),
248
+ attribute .Int ("axis" , int ( q .source ) ),
264
249
attribute .Int ("x" , q .x ),
265
250
attribute .Int ("y" , q .y ),
266
251
attribute .Int ("size" , len (q .roots )),
@@ -292,10 +277,10 @@ func (rs *retrievalSession) doRequest(ctx context.Context, q *quadrant) {
292
277
// in the square.
293
278
// NOTE-2: We never actually fetch shares from the network *twice*.
294
279
// Once a share is downloaded from the network it is cached on the IPLD(blockservice) level.
295
- // calc index of the share
296
- idx := q .index (i , j )
280
+ // calc position of the share
281
+ x , y := q .pos (i , j )
297
282
// try to lock the share
298
- ok := rs .sharesLks [ idx ].TryLock ()
283
+ ok := rs.squareCellsLks [ x ][ y ].TryLock ()
299
284
if ! ok {
300
285
// if already locked and written - do nothing
301
286
return
@@ -312,14 +297,17 @@ func (rs *retrievalSession) doRequest(ctx context.Context, q *quadrant) {
312
297
if rs .isReconstructed () {
313
298
return
314
299
}
315
- rs .square [idx ] = share
300
+ if rs .square .GetCell (uint (x ), uint (y )) != nil {
301
+ return
302
+ }
303
+ rs .square .SetCell (uint (x ), uint (y ), share )
316
304
// if we have >= 1/4 of the square we can start trying to Reconstruct
317
305
// TODO(@Wondertan): This is not an ideal way to know when to start
318
306
// reconstruction and can cause idle reconstruction tries in some cases,
319
307
// but it is totally fine for the happy case and for now.
320
308
// The earlier we correctly know that we have the full square - the earlier
321
309
// we cancel ongoing requests - the less data is being wastedly transferred.
322
- if atomic .AddUint32 (& rs .sharesCount , 1 ) >= uint32 (size * size ) {
310
+ if atomic .AddUint32 (& rs .squareCellsCount , 1 ) >= uint32 (size * size ) {
323
311
select {
324
312
case rs .squareSig <- struct {}{}:
325
313
default :
0 commit comments