@@ -45,7 +45,7 @@ mod wrapped_shard;
4545const MAX_VALIDATORS : usize = <galois_16:: Field as reed_solomon:: Field >:: ORDER ;
4646
4747/// Errors in erasure coding.
48- #[ derive( Debug , Clone ) ]
48+ #[ derive( Debug , Clone , PartialEq ) ]
4949pub enum Error {
5050 /// Returned when there are too many validators.
5151 TooManyValidators ,
@@ -71,6 +71,7 @@ pub enum Error {
7171 BranchOutOfBounds ,
7272}
7373
74+ #[ derive( Debug , PartialEq ) ]
7475struct CodeParams {
7576 data_shards : usize ,
7677 parity_shards : usize ,
@@ -79,36 +80,27 @@ struct CodeParams {
7980impl CodeParams {
8081 // the shard length needed for a payload with initial size `base_len`.
8182 fn shard_len ( & self , base_len : usize ) -> usize {
82- ( base_len / self . data_shards ) + ( base_len % self . data_shards )
83+ // how many bytes we actually need.
84+ let needed_shard_len = base_len / self . data_shards
85+ + ( base_len % self . data_shards != 0 ) as usize ;
86+
87+ // round up to next even number
88+ // (no actual space overhead since we are working in GF(2^16)).
89+ needed_shard_len + needed_shard_len % 2
8390 }
8491
8592 fn make_shards_for ( & self , payload : & [ u8 ] ) -> Vec < WrappedShard > {
8693 let shard_len = self . shard_len ( payload. len ( ) ) ;
8794 let mut shards = vec ! [
88- WrappedShard :: new( vec![ 0 ; shard_len + 4 ] ) ;
95+ WrappedShard :: new( vec![ 0 ; shard_len] ) ;
8996 self . data_shards + self . parity_shards
9097 ] ;
9198
9299 for ( data_chunk, blank_shard) in payload. chunks ( shard_len) . zip ( & mut shards) {
93- let blank_shard: & mut [ u8 ] = blank_shard. as_mut ( ) ;
94- let ( len_slice, blank_shard) = blank_shard. split_at_mut ( 4 ) ;
95- let len = :: std:: cmp:: min ( data_chunk. len ( ) , blank_shard. len ( ) ) ;
96-
97- // prepend the length to each data shard. this will tell us how much
98- // we need to read.
99- //
100- // this is necessary because we are doing RS encoding with 16-bit words,
101- // but the payload is a byte-slice. We need to know how much data
102- // to read from each shard when reconstructing.
103- //
104- // TODO: could be done more efficiently by pushing extra bytes onto the
105- // end. https://github.com/paritytech/polkadot/issues/88
106- ( len as u32 ) . using_encoded ( |s| {
107- len_slice. copy_from_slice ( s)
108- } ) ;
109-
110100 // fill the empty shards with the corresponding piece of the payload,
111101 // zero-padded to fit in the shards.
102+ let len = std:: cmp:: min ( shard_len, data_chunk. len ( ) ) ;
103+ let blank_shard: & mut [ u8 ] = blank_shard. as_mut ( ) ;
112104 blank_shard[ ..len] . copy_from_slice ( & data_chunk[ ..len] ) ;
113105 }
114106
@@ -137,7 +129,7 @@ fn code_params(n_validators: usize) -> Result<CodeParams, Error> {
137129
138130/// Obtain erasure-coded chunks, one for each validator.
139131///
140- /// Works only up to 256 validators, and `n_validators` must be non-zero.
132+ /// Works only up to 65536 validators, and `n_validators` must be non-zero.
141133pub fn obtain_chunks ( n_validators : usize , block_data : & BlockData , extrinsic : & Extrinsic )
142134 -> Result < Vec < Vec < u8 > > , Error >
143135{
@@ -162,7 +154,7 @@ pub fn obtain_chunks(n_validators: usize, block_data: &BlockData, extrinsic: &Ex
162154/// The indices of the present chunks must be indicated. If too few chunks
163155/// are provided, recovery is not possible.
164156///
165- /// Works only up to 256 validators, and `n_validators` must be non-zero.
157+ /// Works only up to 65536 validators, and `n_validators` must be non-zero.
166158pub fn reconstruct < ' a , I : ' a > ( n_validators : usize , chunks : I )
167159 -> Result < ( BlockData , Extrinsic ) , Error >
168160 where I : IntoIterator < Item =( & ' a [ u8 ] , usize ) >
@@ -201,22 +193,12 @@ pub fn reconstruct<'a, I: 'a>(n_validators: usize, chunks: I)
201193
202194 // lazily decode from the data shards.
203195 Decode :: decode ( & mut ShardInput {
196+ cur_shard : None ,
204197 shards : shards. iter ( )
205198 . map ( |x| x. as_ref ( ) )
206199 . take ( params. data_shards )
207200 . map ( |x| x. expect ( "all data shards have been recovered; qed" ) )
208- . filter_map ( |x| {
209- let mut s: & [ u8 ] = x. as_ref ( ) ;
210- let data_len = u32:: decode ( & mut s) ? as usize ;
211-
212- // NOTE: s has been mutated to point forward by `decode`.
213- if s. len ( ) < data_len {
214- None
215- } else {
216- Some ( & s[ ..data_len] )
217- }
218- } ) ,
219- cur_shard : None ,
201+ . map ( |x| x. as_ref ( ) ) ,
220202 } ) . ok_or_else ( || Error :: BadPayload )
221203}
222204
@@ -357,6 +339,58 @@ mod tests {
357339 assert_eq ! ( MAX_VALIDATORS , 65536 ) ;
358340 }
359341
342+ #[ test]
343+ fn test_code_params ( ) {
344+ assert_eq ! ( code_params( 0 ) , Err ( Error :: EmptyValidators ) ) ;
345+
346+ assert_eq ! ( code_params( 1 ) , Ok ( CodeParams {
347+ data_shards: 1 ,
348+ parity_shards: 0 ,
349+ } ) ) ;
350+
351+ assert_eq ! ( code_params( 2 ) , Ok ( CodeParams {
352+ data_shards: 1 ,
353+ parity_shards: 1 ,
354+ } ) ) ;
355+
356+ assert_eq ! ( code_params( 3 ) , Ok ( CodeParams {
357+ data_shards: 1 ,
358+ parity_shards: 2 ,
359+ } ) ) ;
360+
361+ assert_eq ! ( code_params( 4 ) , Ok ( CodeParams {
362+ data_shards: 2 ,
363+ parity_shards: 2 ,
364+ } ) ) ;
365+
366+ assert_eq ! ( code_params( 100 ) , Ok ( CodeParams {
367+ data_shards: 34 ,
368+ parity_shards: 66 ,
369+ } ) ) ;
370+ }
371+
372+ #[ test]
373+ fn shard_len_is_reasonable ( ) {
374+ let mut params = CodeParams {
375+ data_shards : 5 ,
376+ parity_shards : 0 , // doesn't affect calculation.
377+ } ;
378+
379+ assert_eq ! ( params. shard_len( 100 ) , 20 ) ;
380+ assert_eq ! ( params. shard_len( 99 ) , 20 ) ;
381+
382+ // see if it rounds up to 2.
383+ assert_eq ! ( params. shard_len( 95 ) , 20 ) ;
384+ assert_eq ! ( params. shard_len( 94 ) , 20 ) ;
385+
386+ assert_eq ! ( params. shard_len( 89 ) , 18 ) ;
387+
388+ params. data_shards = 7 ;
389+
390+ // needs 3 bytes to fit, rounded up to next even number.
391+ assert_eq ! ( params. shard_len( 19 ) , 4 ) ;
392+ }
393+
360394 #[ test]
361395 fn round_trip_block_data ( ) {
362396 let block_data = BlockData ( ( 0 ..255 ) . collect ( ) ) ;
0 commit comments