1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
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
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
use std::cmp::Ordering;
use std::fmt::Display;

use serde::{Deserialize, Deserializer, Serialize};

use crate::consts::appconsts::SHARE_SIZE;
use crate::consts::data_availability_header::{
    MAX_EXTENDED_SQUARE_WIDTH, MIN_EXTENDED_SQUARE_WIDTH,
};
use crate::namespaced_data::{NamespacedData, NamespacedDataId};
use crate::nmt::{Namespace, NamespacedSha2Hasher, Nmt, NmtExt, NS_SIZE};
use crate::{bail_validation, DataAvailabilityHeader, Error, InfoByte, Result};

/// Represents either column or row of the [`ExtendedDataSquare`].
///
/// [`ExtendedDataSquare`]: crate::rsmt2d::ExtendedDataSquare
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum AxisType {
    /// A row of the data square.
    Row = 0,
    /// A column of the data square.
    Col,
}

impl TryFrom<i32> for AxisType {
    type Error = Error;

    fn try_from(value: i32) -> Result<Self, Self::Error> {
        match value {
            0 => Ok(AxisType::Row),
            1 => Ok(AxisType::Col),
            n => Err(Error::InvalidAxis(n)),
        }
    }
}

impl Display for AxisType {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            AxisType::Row => write!(f, "Row"),
            AxisType::Col => write!(f, "Column"),
        }
    }
}

/// The data matrix in Celestia blocks extended with parity data.
///
/// It is created by a fixed size chunks of data, called [`Share`]s.

/// Each share is a cell of the [`ExtendedDataSquare`].
///
/// # Structure
///
/// The [`ExtendedDataSquare`] consists of four quadrants. The first
/// quadrant (upper-left) is the original data submitted to the network,
/// referred to as `OriginalDataSquare`. The other three quadrants are
/// the parity data encoded row-wise or column-wise using Reed-Solomon
/// `codec` specified in `EDS`.
///
/// The below diagram shows how the `EDS` is constructed. First, the 2nd
/// and 3rd quadrants are created by computing Reed-Solomon parity data
/// of the original data square, row-wise for 2nd and column-wise for
/// 3rd quadrant. Then, the 4th quadrant is computed either row-wise
/// from 3rd or column-wise from 2nd quadrant.
///
/// ```text
///  ---------------------------
/// |             |             |
/// |           --|->           |
/// |      1    --|->    2      |
/// |           --|->           |
/// |    | | |    |             |
///  -------------+-------------
/// |    v v v    |             |
/// |           --|->           |
/// |      3    --|->    4      |
/// |           --|->           |
/// |             |             |
///  ---------------------------
/// ```
///
/// # Data availability
///
/// The [`DataAvailabilityHeader`] is created by computing [`Nmt`] merkle
/// roots of each row and column of [`ExtendedDataSquare`].
/// By putting those together there are some key
/// properties those have in terms of data availability.
///
/// Thanks to the parity data, to make original data unrecoverable, a malicious
/// actor would need to hide more than a half of the data from each row and column.
/// If we take `k` as the width of the `OriginalDataSquare`, then the attacker
/// needs to hide more than `(k + 1)^2` shares from the [`ExtendedDataSquare`].
/// For the `EDS` with a width of 4, the attacker needs to hide more than 50% of
/// all the shares and that value approaches 25% as the square grows.
///
/// This allows for really efficient data sampling, as the sampling node can reach
/// very high confidence that whole data is available by taking only a few samples.
///
/// # Example
///
/// This example shows rebuilding the merkle trees for each row of the EDS and compares
/// them with the root hashes stored in data availability header.
///
/// ```no_run
/// use celestia_types::Share;
/// # use celestia_types::{ExtendedDataSquare, ExtendedHeader};
/// # fn get_header(_: usize) -> ExtendedHeader {
/// #     unimplemented!()
/// # }
/// # fn get_eds(_: usize) -> ExtendedDataSquare {
/// #     unimplemented!()
/// # }
///
/// let block_height = 15;
/// let header = get_header(block_height);
/// let eds = get_eds(block_height);
/// let width = header.dah.square_width();
///
/// // for each row of the data square, build an nmt
/// for row in 0..eds.square_width() {
///     // check if the root corresponds to the one from the dah
///     let root = eds.row_nmt(row).unwrap().root();
///     assert_eq!(root, header.dah.row_root(row).unwrap());
/// }
/// ```
///
/// [`Nmt`]: crate::nmt::Nmt
/// [`Share`]: crate::share::Share
/// [`DataAvailabilityHeader`]: crate::DataAvailabilityHeader
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct ExtendedDataSquare {
    /// The raw data of the EDS.
    #[serde(with = "celestia_tendermint_proto::serializers::bytes::vec_base64string")]
    data_square: Vec<Vec<u8>>,
    /// The codec used to encode parity shares.
    codec: String,
    /// pre-calculated square length
    #[serde(skip)]
    square_width: u16,
}

impl ExtendedDataSquare {
    /// Create a new EDS out of the provided shares.
    ///
    /// Shares should be provided in a row-major order, i.e. first shares of the first row,
    /// then of the second row and so on.
    ///
    /// # Errors
    ///
    /// Returns an error if:
    ///  - shares are of sizes different than [`SHARE_SIZE`]
    ///  - amount of shares doesn't allow for forming a square
    ///  - width of the square is smaller than [`MIN_EXTENDED_SQUARE_WIDTH`]
    ///  - width of the square is bigger than [`MAX_EXTENDED_SQUARE_WIDTH`]
    ///  - width of the square isn't a power of 2
    ///  - namespaces of shares aren't in non-decreasing order row and column wise
    pub fn new(shares: Vec<Vec<u8>>, codec: String) -> Result<Self> {
        const MIN_SHARES: usize = MIN_EXTENDED_SQUARE_WIDTH * MIN_EXTENDED_SQUARE_WIDTH;
        const MAX_SHARES: usize = MAX_EXTENDED_SQUARE_WIDTH * MAX_EXTENDED_SQUARE_WIDTH;

        if shares.len() < MIN_SHARES {
            bail_validation!(
                "shares len ({}) < MIN_SHARES ({})",
                shares.len(),
                MIN_SHARES
            );
        }
        if shares.len() > MAX_SHARES {
            bail_validation!(
                "shares len ({}) > MAX_SHARES ({})",
                shares.len(),
                MAX_SHARES
            );
        }

        let square_width = f64::sqrt(shares.len() as f64) as usize;

        if square_width * square_width != shares.len() {
            return Err(Error::EdsInvalidDimentions);
        }

        let square_width = u16::try_from(square_width).map_err(|_| Error::EdsInvalidDimentions)?;

        // must be a power of 2
        if square_width.count_ones() != 1 {
            return Err(Error::EdsInvalidDimentions);
        }

        let eds = ExtendedDataSquare {
            data_square: shares,
            codec,
            square_width,
        };

        let check_share = |row, col, prev_ns: Option<Namespace>, axis| -> Result<Namespace> {
            let share = eds.share(row, col)?;

            if share.len() != SHARE_SIZE {
                bail_validation!("share len ({}) != SHARE_SIZE ({})", share.len(), SHARE_SIZE);
            }

            let ns = if is_ods_square(row, col, eds.square_width()) {
                Namespace::from_raw(&share[..NS_SIZE])?
            } else {
                Namespace::PARITY_SHARE
            };

            if prev_ns.map_or(false, |prev_ns| ns < prev_ns) {
                let axis_idx = match axis {
                    AxisType::Row => row,
                    AxisType::Col => col,
                };
                bail_validation!("Shares of {axis} {axis_idx} are not sorted by their namespace");
            }

            Ok(ns)
        };

        // Validate that namespaces of each row are sorted
        for row in 0..eds.square_width() {
            let mut prev_ns = None;

            for col in 0..eds.square_width() {
                prev_ns = Some(check_share(row, col, prev_ns, AxisType::Row)?);
            }
        }
        // Validate that namespaces of each column are sorted
        for col in 0..eds.square_width() {
            let mut prev_ns = None;

            for row in 0..eds.square_width() {
                prev_ns = Some(check_share(row, col, prev_ns, AxisType::Col)?);
            }
        }

        Ok(eds)
    }

    /// Crate a new EDS that represents an empty block
    pub fn empty() -> ExtendedDataSquare {
        // ODS in this case it is just one tail padded share.
        let ods = vec![[
            Namespace::TAIL_PADDING.as_bytes(),
            &[InfoByte::new(0, true).unwrap().as_u8()],
            &[0; SHARE_SIZE - NS_SIZE - 1],
        ]
        .concat()];

        ExtendedDataSquare::from_ods(ods).expect("invalid EDS")
    }

    /// Create a new EDS out of the provided original data square shares.
    ///
    /// This method is similar to the [`ExtendedDataSquare::new`] but parity data
    /// will be encoded automatically using the [`leopard_codec`]
    ///
    /// Shares should be provided in a row-major order.
    ///
    /// # Errors
    ///
    /// The same errors as in [`ExtendedDataSquare::new`] applies. The constrain
    /// will be checked after the parity data is generated.
    ///
    /// Additionally, this function will propagate any error from encoding parity data.
    pub fn from_ods(mut ods_shares: Vec<Vec<u8>>) -> Result<ExtendedDataSquare> {
        let ods_width = f64::sqrt(ods_shares.len() as f64) as usize;
        // this couldn't be detected later in `new()`
        if ods_width * ods_width != ods_shares.len() {
            return Err(Error::EdsInvalidDimentions);
        }

        let eds_width = ods_width * 2;
        let mut eds_shares = Vec::with_capacity(eds_width * eds_width);
        // take rows of ods and interleave them with parity shares
        for _ in 0..ods_width {
            eds_shares.extend(ods_shares.drain(..ods_width));
            for _ in 0..ods_width {
                eds_shares.push(vec![0; SHARE_SIZE]);
            }
        }
        // fill bottom half of the square with parity data
        eds_shares.resize(eds_width * eds_width, vec![0; SHARE_SIZE]);

        // 2nd quadrant - encode parity of rows of 1st quadrant
        for row in eds_shares.chunks_mut(eds_width).take(ods_width) {
            leopard_codec::encode(row, ods_width)?;
        }
        // 3rd quadrant - encode parity of columns of 1st quadrant
        for col in 0..ods_width {
            let mut col: Vec<_> = eds_shares.iter_mut().skip(col).step_by(eds_width).collect();
            leopard_codec::encode(&mut col, ods_width)?;
        }
        // 4th quadrant - encode parity of rows of 3rd quadrant
        for row in eds_shares.chunks_mut(eds_width).skip(ods_width) {
            leopard_codec::encode(row, ods_width)?;
        }

        ExtendedDataSquare::new(eds_shares, "Leopard".to_string())
    }

    /// The raw data of the EDS.
    pub fn data_square(&self) -> &[Vec<u8>] {
        &self.data_square
    }

    /// The codec used to encode parity shares.
    pub fn codec(&self) -> &str {
        self.codec.as_str()
    }

    /// Returns the share of the provided coordinates.
    pub fn share(&self, row: u16, column: u16) -> Result<&[u8]> {
        let index = usize::from(row) * usize::from(self.square_width) + usize::from(column);

        self.data_square
            .get(index)
            .map(Vec::as_slice)
            .ok_or(Error::EdsIndexOutOfRange(row, column))
    }

    /// Returns the mutable share of the provided coordinates.
    #[cfg(any(test, feature = "test-utils"))]
    pub(crate) fn share_mut(&mut self, row: u16, column: u16) -> Result<&mut [u8]> {
        let index = usize::from(row) * usize::from(self.square_width) + usize::from(column);

        self.data_square
            .get_mut(index)
            .map(Vec::as_mut_slice)
            .ok_or(Error::EdsIndexOutOfRange(row, column))
    }

    /// Returns the shares of a row.
    pub fn row(&self, index: u16) -> Result<Vec<Vec<u8>>> {
        self.axis(AxisType::Row, index)
    }

    /// Returns the [`Nmt`] of a row.
    pub fn row_nmt(&self, index: u16) -> Result<Nmt> {
        self.axis_nmt(AxisType::Row, index)
    }

    /// Returns the shares of a column.
    pub fn column(&self, index: u16) -> Result<Vec<Vec<u8>>> {
        self.axis(AxisType::Col, index)
    }

    /// Returns the [`Nmt`] of a column.
    pub fn column_nmt(&self, index: u16) -> Result<Nmt> {
        self.axis_nmt(AxisType::Col, index)
    }

    /// Returns the shares of column or row.
    pub fn axis(&self, axis: AxisType, index: u16) -> Result<Vec<Vec<u8>>> {
        (0..self.square_width)
            .map(|i| {
                let (row, col) = match axis {
                    AxisType::Row => (index, i),
                    AxisType::Col => (i, index),
                };

                self.share(row, col).map(ToOwned::to_owned)
            })
            .collect()
    }

    /// Returns the [`Nmt`] of column or row.
    pub fn axis_nmt(&self, axis: AxisType, index: u16) -> Result<Nmt> {
        let mut tree = Nmt::default();

        for i in 0..self.square_width {
            let (row, col) = match axis {
                AxisType::Row => (index, i),
                AxisType::Col => (i, index),
            };

            let share = self.share(row, col)?;

            let ns = if is_ods_square(col, row, self.square_width) {
                Namespace::from_raw(&share[..NS_SIZE])?
            } else {
                Namespace::PARITY_SHARE
            };

            tree.push_leaf(share, *ns).map_err(Error::Nmt)?;
        }

        Ok(tree)
    }

    /// Get EDS square length.
    pub fn square_width(&self) -> u16 {
        self.square_width
    }

    /// Return all the shares that belong to the provided namespace in the EDS.
    /// Results are returned as a list of rows of shares with the inclusion proof.
    pub fn get_namespaced_data(
        &self,
        namespace: Namespace,
        dah: &DataAvailabilityHeader,
        height: u64,
    ) -> Result<Vec<NamespacedData>> {
        let mut data = Vec::new();

        for row in 0..self.square_width {
            let Some(row_root) = dah.row_root(row) else {
                break;
            };

            if !row_root.contains::<NamespacedSha2Hasher>(*namespace) {
                continue;
            }

            let mut shares = Vec::with_capacity(self.square_width.into());

            for col in 0..self.square_width {
                let share = self.share(row, col)?;

                let ns = if is_ods_square(row, col, self.square_width) {
                    Namespace::from_raw(&share[..NS_SIZE])?
                } else {
                    Namespace::PARITY_SHARE
                };

                // Shares in each row of EDS are sorted by namespace, so we
                // can stop search the row if we reach to a bigger namespace.
                match ns.cmp(&namespace) {
                    Ordering::Less => {}
                    Ordering::Equal => shares.push(share.to_owned()),
                    Ordering::Greater => break,
                }
            }

            let proof = self.row_nmt(row)?.get_namespace_proof(*namespace);
            let id = NamespacedDataId::new(namespace, row, height)?;

            data.push(NamespacedData {
                id,
                proof: proof.into(),
                shares,
            })
        }

        Ok(data)
    }
}

#[derive(Deserialize)]
struct RawExtendedDataSquare {
    #[serde(with = "celestia_tendermint_proto::serializers::bytes::vec_base64string")]
    pub data_square: Vec<Vec<u8>>,
    pub codec: String,
}

impl<'de> Deserialize<'de> for ExtendedDataSquare {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let eds = RawExtendedDataSquare::deserialize(deserializer)?;
        let share_number = eds.data_square.len();
        ExtendedDataSquare::new(eds.data_square, eds.codec).map_err(|_| {
            <D::Error as serde::de::Error>::invalid_length(
                share_number,
                &"number of shares must be a perfect square",
            )
        })
    }
}

/// Returns true if and only if the provided coordinates belongs to Original Data Square
/// (i.e. first quadrant of Extended Data Square).
pub(crate) fn is_ods_square(row: u16, column: u16, square_width: u16) -> bool {
    let ods_width = square_width / 2;
    row < ods_width && column < ods_width
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::ExtendedHeader;

    #[test]
    fn axis_type_serialization() {
        assert_eq!(AxisType::Row as u8, 0);
        assert_eq!(AxisType::Col as u8, 1);
    }

    #[test]
    fn axis_type_deserialization() {
        assert_eq!(AxisType::try_from(0).unwrap(), AxisType::Row);
        assert_eq!(AxisType::try_from(1).unwrap(), AxisType::Col);

        let axis_type_err = AxisType::try_from(2).unwrap_err();
        assert!(matches!(axis_type_err, Error::InvalidAxis(2)));
        let axis_type_err = AxisType::try_from(99).unwrap_err();
        assert!(matches!(axis_type_err, Error::InvalidAxis(99)));
    }

    #[test]
    fn get_namespaced_data() {
        let eds_json = include_str!("../test_data/shwap_samples/eds.json");
        let eds: ExtendedDataSquare = serde_json::from_str(eds_json).unwrap();

        let dah_json = include_str!("../test_data/shwap_samples/dah.json");
        let dah: DataAvailabilityHeader = serde_json::from_str(dah_json).unwrap();

        let height = 45577;

        let rows = eds
            .get_namespaced_data(Namespace::new_v0(&[1, 170]).unwrap(), &dah, height)
            .unwrap();
        assert_eq!(rows.len(), 1);
        let row = &rows[0];
        row.verify(&dah).unwrap();
        assert_eq!(row.shares.len(), 2);

        let rows = eds
            .get_namespaced_data(Namespace::new_v0(&[1, 187]).unwrap(), &dah, height)
            .unwrap();
        assert_eq!(rows.len(), 2);
        assert_eq!(rows[0].shares.len(), 1);
        assert_eq!(rows[1].shares.len(), 4);
        for row in rows {
            row.verify(&dah).unwrap();
        }
    }

    #[test]
    fn nmt_roots() {
        let eds_json = include_str!("../test_data/shwap_samples/eds.json");
        let eds: ExtendedDataSquare = serde_json::from_str(eds_json).unwrap();

        let dah_json = include_str!("../test_data/shwap_samples/dah.json");
        let dah: DataAvailabilityHeader = serde_json::from_str(dah_json).unwrap();

        assert_eq!(dah.row_roots().len(), usize::from(eds.square_width()));
        assert_eq!(dah.column_roots().len(), usize::from(eds.square_width()));

        for (i, root) in dah.row_roots().iter().enumerate() {
            let mut tree = eds.row_nmt(i as u16).unwrap();
            assert_eq!(*root, tree.root());

            let mut tree = eds.axis_nmt(AxisType::Row, i as u16).unwrap();
            assert_eq!(*root, tree.root());
        }

        for (i, root) in dah.column_roots().iter().enumerate() {
            let mut tree = eds.column_nmt(i as u16).unwrap();
            assert_eq!(*root, tree.root());

            let mut tree = eds.axis_nmt(AxisType::Col, i as u16).unwrap();
            assert_eq!(*root, tree.root());
        }
    }

    #[test]
    fn ods_square() {
        assert!(is_ods_square(0, 0, 4));
        assert!(is_ods_square(0, 1, 4));
        assert!(is_ods_square(1, 0, 4));
        assert!(is_ods_square(1, 1, 4));

        assert!(!is_ods_square(0, 2, 4));
        assert!(!is_ods_square(0, 3, 4));
        assert!(!is_ods_square(1, 2, 4));
        assert!(!is_ods_square(1, 3, 4));

        assert!(!is_ods_square(2, 0, 4));
        assert!(!is_ods_square(2, 1, 4));
        assert!(!is_ods_square(3, 0, 4));
        assert!(!is_ods_square(3, 1, 4));

        assert!(!is_ods_square(2, 2, 4));
        assert!(!is_ods_square(2, 3, 4));
        assert!(!is_ods_square(3, 2, 4));
        assert!(!is_ods_square(3, 3, 4));
    }

    #[test]
    fn get_row_and_col() {
        let share = |x, y| {
            [
                Namespace::new_v0(&[x, y]).unwrap().as_bytes(),
                &[0u8; SHARE_SIZE - NS_SIZE][..],
            ]
            .concat()
        };

        #[rustfmt::skip]
        let shares = vec![
            share(0, 0), share(0, 1), share(0, 2), share(0, 3),
            share(1, 0), share(1, 1), share(1, 2), share(1, 3),
            share(2, 0), share(2, 1), share(2, 2), share(2, 3),
            share(3, 0), share(3, 1), share(3, 2), share(3, 3),
        ];

        let eds = ExtendedDataSquare::new(shares, "fake".to_string()).unwrap();

        assert_eq!(
            eds.row(0).unwrap(),
            vec![share(0, 0), share(0, 1), share(0, 2), share(0, 3)]
        );
        assert_eq!(
            eds.row(1).unwrap(),
            vec![share(1, 0), share(1, 1), share(1, 2), share(1, 3)]
        );
        assert_eq!(
            eds.row(2).unwrap(),
            vec![share(2, 0), share(2, 1), share(2, 2), share(2, 3)]
        );
        assert_eq!(
            eds.row(3).unwrap(),
            vec![share(3, 0), share(3, 1), share(3, 2), share(3, 3)]
        );

        assert_eq!(
            eds.axis(AxisType::Row, 0).unwrap(),
            vec![share(0, 0), share(0, 1), share(0, 2), share(0, 3)]
        );
        assert_eq!(
            eds.axis(AxisType::Row, 1).unwrap(),
            vec![share(1, 0), share(1, 1), share(1, 2), share(1, 3)]
        );
        assert_eq!(
            eds.axis(AxisType::Row, 2).unwrap(),
            vec![share(2, 0), share(2, 1), share(2, 2), share(2, 3)]
        );
        assert_eq!(
            eds.axis(AxisType::Row, 3).unwrap(),
            vec![share(3, 0), share(3, 1), share(3, 2), share(3, 3)]
        );

        assert_eq!(
            eds.column(0).unwrap(),
            vec![share(0, 0), share(1, 0), share(2, 0), share(3, 0)]
        );
        assert_eq!(
            eds.column(1).unwrap(),
            vec![share(0, 1), share(1, 1), share(2, 1), share(3, 1)]
        );
        assert_eq!(
            eds.column(2).unwrap(),
            vec![share(0, 2), share(1, 2), share(2, 2), share(3, 2)]
        );
        assert_eq!(
            eds.column(3).unwrap(),
            vec![share(0, 3), share(1, 3), share(2, 3), share(3, 3)]
        );

        assert_eq!(
            eds.axis(AxisType::Col, 0).unwrap(),
            vec![share(0, 0), share(1, 0), share(2, 0), share(3, 0)]
        );
        assert_eq!(
            eds.axis(AxisType::Col, 1).unwrap(),
            vec![share(0, 1), share(1, 1), share(2, 1), share(3, 1)]
        );
        assert_eq!(
            eds.axis(AxisType::Col, 2).unwrap(),
            vec![share(0, 2), share(1, 2), share(2, 2), share(3, 2)]
        );
        assert_eq!(
            eds.axis(AxisType::Col, 3).unwrap(),
            vec![share(0, 3), share(1, 3), share(2, 3), share(3, 3)]
        );
    }

    #[test]
    fn validation() {
        ExtendedDataSquare::new(vec![], "fake".to_string()).unwrap_err();
        ExtendedDataSquare::new(vec![vec![]], "fake".to_string()).unwrap_err();
        ExtendedDataSquare::new(vec![vec![]; 4], "fake".to_string()).unwrap_err();

        ExtendedDataSquare::new(vec![vec![0u8; SHARE_SIZE]; 4], "fake".to_string()).unwrap();
        ExtendedDataSquare::new(vec![vec![0u8; SHARE_SIZE]; 6], "fake".to_string()).unwrap_err();
        ExtendedDataSquare::new(vec![vec![0u8; SHARE_SIZE]; 16], "fake".to_string()).unwrap();

        let share = |n| {
            [
                Namespace::new_v0(&[n]).unwrap().as_bytes(),
                &[0u8; SHARE_SIZE - NS_SIZE][..],
            ]
            .concat()
        };

        ExtendedDataSquare::from_ods(vec![
            // row 0
            share(0), // ODS
        ])
        .unwrap();

        ExtendedDataSquare::from_ods(vec![
            // row 0
            share(1),
            share(2),
            // row 1
            share(1),
            share(3),
        ])
        .unwrap();

        ExtendedDataSquare::from_ods(vec![
            // row 0
            share(1),
            share(2),
            // row 1
            share(1),
            share(1), // error: smaller namespace in 2nd column
        ])
        .unwrap_err();

        ExtendedDataSquare::from_ods(vec![
            // row 0
            share(1),
            share(1),
            // row 1
            share(2),
            share(1), // error: smaller namespace in 2nd row
        ])
        .unwrap_err();

        // not a power of 2
        ExtendedDataSquare::new(vec![share(1); 6 * 6], "fake".to_string()).unwrap_err();

        // too big
        // we need to go to the next power of 2 or we just hit other checks
        let square_width = MAX_EXTENDED_SQUARE_WIDTH * 2;
        ExtendedDataSquare::new(vec![share(1); square_width.pow(2)], "fake".to_string())
            .unwrap_err();
    }

    #[test]
    fn empty_block_eds() {
        let s = include_str!("../test_data/chain1/extended_header_block_1.json");
        let genesis: ExtendedHeader = serde_json::from_str(s).unwrap();

        let eds = ExtendedDataSquare::empty();
        let dah = DataAvailabilityHeader::from_eds(&eds);
        assert_eq!(dah, genesis.dah);
    }
}