From 48fcb3c951ad7fe200cbc39766f271f7608fd641 Mon Sep 17 00:00:00 2001 From: Wondertan Date: Mon, 27 May 2024 13:30:39 +0200 Subject: [PATCH] rearrange methods and missing comments --- share/shwap/eds_id.go | 14 ++-- share/shwap/namespace_data.go | 48 +++++------ share/shwap/row.go | 90 ++++++++++----------- share/shwap/row_id.go | 12 +-- share/shwap/row_namespace_data.go | 116 ++++++++++++++------------- share/shwap/row_namespace_data_id.go | 18 ++--- share/shwap/sample.go | 92 ++++++++++----------- share/shwap/sample_id.go | 18 ++--- share/shwap/share.go | 1 + 9 files changed, 207 insertions(+), 202 deletions(-) diff --git a/share/shwap/eds_id.go b/share/shwap/eds_id.go index 59771cac29..fe0734ad75 100644 --- a/share/shwap/eds_id.go +++ b/share/shwap/eds_id.go @@ -25,13 +25,6 @@ func NewEdsID(height uint64, root *share.Root) (EdsID, error) { return eid, eid.Validate(root) } -// MarshalBinary encodes an EdsID into its binary form, primarily for storage or network -// transmission. -func (eid EdsID) MarshalBinary() ([]byte, error) { - data := make([]byte, 0, EdsIDSize) - return eid.appendTo(data), nil -} - // EdsIDFromBinary decodes a byte slice into an EdsID, validating the length of the data. // It returns an error if the data slice does not match the expected size of an EdsID. func EdsIDFromBinary(data []byte) (EdsID, error) { @@ -44,6 +37,13 @@ func EdsIDFromBinary(data []byte) (EdsID, error) { return rid, nil } +// MarshalBinary encodes an EdsID into its binary form, primarily for storage or network +// transmission. +func (eid EdsID) MarshalBinary() ([]byte, error) { + data := make([]byte, 0, EdsIDSize) + return eid.appendTo(data), nil +} + // Validate checks the integrity of an EdsID's fields against the provided Root. // It ensures that the EdsID is not constructed with a zero Height and that the root is not nil. func (eid EdsID) Validate(root *share.Root) error { diff --git a/share/shwap/namespace_data.go b/share/shwap/namespace_data.go index 53fffbdca9..852c087896 100644 --- a/share/shwap/namespace_data.go +++ b/share/shwap/namespace_data.go @@ -12,30 +12,6 @@ import ( // within a namespace. type NamespacedData []RowNamespaceData -// Flatten combines all shares from all rows within the namespace into a single slice. -func (ns NamespacedData) Flatten() []share.Share { - var shares []share.Share - for _, row := range ns { - shares = append(shares, row.Shares...) - } - return shares -} - -// Validate checks the integrity of the NamespacedData against a provided root and namespace. -func (ns NamespacedData) Validate(root *share.Root, namespace share.Namespace) error { - rowIdxs := share.RowsWithNamespace(root, namespace) - if len(rowIdxs) != len(ns) { - return fmt.Errorf("expected %d rows, found %d rows", len(rowIdxs), len(ns)) - } - - for i, row := range ns { - if err := row.Validate(root, namespace, rowIdxs[i]); err != nil { - return fmt.Errorf("validating row: %w", err) - } - } - return nil -} - // NamespacedDataFromEDS extracts shares for a specific namespace from an EDS, considering // each row independently. func NamespacedDataFromEDS( @@ -64,3 +40,27 @@ func NamespacedDataFromEDS( return rows, nil } + +// Flatten combines all shares from all rows within the namespace into a single slice. +func (ns NamespacedData) Flatten() []share.Share { + var shares []share.Share + for _, row := range ns { + shares = append(shares, row.Shares...) + } + return shares +} + +// Validate checks the integrity of the NamespacedData against a provided root and namespace. +func (ns NamespacedData) Validate(root *share.Root, namespace share.Namespace) error { + rowIdxs := share.RowsWithNamespace(root, namespace) + if len(rowIdxs) != len(ns) { + return fmt.Errorf("expected %d rows, found %d rows", len(rowIdxs), len(ns)) + } + + for i, row := range ns { + if err := row.Validate(root, namespace, rowIdxs[i]); err != nil { + return fmt.Errorf("validating row: %w", err) + } + } + return nil +} diff --git a/share/shwap/row.go b/share/shwap/row.go index 6cf1d0da96..046ff5eceb 100644 --- a/share/shwap/row.go +++ b/share/shwap/row.go @@ -33,6 +33,51 @@ func NewRow(halfShares []share.Share, side RowSide) Row { } } +// RowFromEDS constructs a new Row from an Extended Data Square based on the specified index and +// side. +func RowFromEDS(square *rsmt2d.ExtendedDataSquare, idx int, side RowSide) Row { + sqrLn := int(square.Width()) + shares := square.Row(uint(idx)) + var halfShares []share.Share + if side == Right { + halfShares = shares[sqrLn/2:] // Take the right half of the shares. + } else { + halfShares = shares[:sqrLn/2] // Take the left half of the shares. + } + + return NewRow(halfShares, side) +} + +// RowFromProto converts a protobuf Row to a Row structure. +func RowFromProto(r *pb.Row) Row { + return Row{ + halfShares: SharesFromProto(r.SharesHalf), + side: sideFromProto(r.GetHalfSide()), + } +} + +// Shares reconstructs the complete row shares from the half provided, using RSMT2D for data +// recovery if needed. +func (r Row) Shares() ([]share.Share, error) { + shares := make([]share.Share, len(r.halfShares)*2) + offset := 0 + if r.side == Right { + offset = len(r.halfShares) // Position the halfShares in the second half if it's the right side. + } + for i, share := range r.halfShares { + shares[i+offset] = share + } + return share.DefaultRSMT2DCodec().Decode(shares) +} + +// ToProto converts the Row to its protobuf representation. +func (r Row) ToProto() *pb.Row { + return &pb.Row{ + SharesHalf: SharesToProto(r.halfShares), + HalfSide: r.side.ToProto(), + } +} + // Validate checks if the row's shares match the expected number from the root data and validates // the side of the row. func (r Row) Validate(dah *share.Root, idx int) error { @@ -80,51 +125,6 @@ func (r Row) verifyInclusion(dah *share.Root, idx int) error { return nil } -// Shares reconstructs the complete row shares from the half provided, using RSMT2D for data -// recovery if needed. -func (r Row) Shares() ([]share.Share, error) { - shares := make([]share.Share, len(r.halfShares)*2) - offset := 0 - if r.side == Right { - offset = len(r.halfShares) // Position the halfShares in the second half if it's the right side. - } - for i, share := range r.halfShares { - shares[i+offset] = share - } - return share.DefaultRSMT2DCodec().Decode(shares) -} - -// ToProto converts the Row to its protobuf representation. -func (r Row) ToProto() *pb.Row { - return &pb.Row{ - SharesHalf: SharesToProto(r.halfShares), - HalfSide: r.side.ToProto(), - } -} - -// RowFromProto converts a protobuf Row to a Row structure. -func RowFromProto(r *pb.Row) Row { - return Row{ - halfShares: SharesFromProto(r.SharesHalf), - side: sideFromProto(r.GetHalfSide()), - } -} - -// NewRowFromEDS constructs a new Row from an Extended Data Square based on the specified index and -// side. -func NewRowFromEDS(square *rsmt2d.ExtendedDataSquare, idx int, side RowSide) Row { - sqrLn := int(square.Width()) - shares := square.Row(uint(idx)) - var halfShares []share.Share - if side == Right { - halfShares = shares[sqrLn/2:] // Take the right half of the shares. - } else { - halfShares = shares[:sqrLn/2] // Take the left half of the shares. - } - - return NewRow(halfShares, side) -} - // ToProto converts a RowSide to its protobuf representation. func (s RowSide) ToProto() pb.Row_HalfSide { if s == Left { diff --git a/share/shwap/row_id.go b/share/shwap/row_id.go index f7cfa74a46..cfdde4943f 100644 --- a/share/shwap/row_id.go +++ b/share/shwap/row_id.go @@ -31,12 +31,6 @@ func NewRowID(height uint64, rowIdx int, root *share.Root) (RowID, error) { return rid, rid.Validate(root) } -// MarshalBinary encodes the RowID into a binary form for storage or network transmission. -func (rid RowID) MarshalBinary() ([]byte, error) { - data := make([]byte, 0, RowIDSize) - return rid.appendTo(data), nil -} - // RowIDFromBinary decodes a RowID from its binary representation. // It returns an error if the input data does not conform to the expected size or content format. func RowIDFromBinary(data []byte) (RowID, error) { @@ -53,6 +47,12 @@ func RowIDFromBinary(data []byte) (RowID, error) { }, nil } +// MarshalBinary encodes the RowID into a binary form for storage or network transmission. +func (rid RowID) MarshalBinary() ([]byte, error) { + data := make([]byte, 0, RowIDSize) + return rid.appendTo(data), nil +} + // Validate ensures the RowID's fields are valid given the specified root structure, particularly // that the row index is within bounds. func (rid RowID) Validate(root *share.Root) error { diff --git a/share/shwap/row_namespace_data.go b/share/shwap/row_namespace_data.go index 7fe93a89e6..88b8de7e6c 100644 --- a/share/shwap/row_namespace_data.go +++ b/share/shwap/row_namespace_data.go @@ -19,62 +19,8 @@ type RowNamespaceData struct { Proof *nmt.Proof `json:"proof"` // Proof of the shares' inclusion in the namespace. } -func (rnd RowNamespaceData) Validate(dah *share.Root, namespace share.Namespace, rowIdx int) error { - if rnd.Proof == nil || rnd.Proof.IsEmptyProof() { - return fmt.Errorf("nil proof") - } - if len(rnd.Shares) == 0 && !rnd.Proof.IsOfAbsence() { - return fmt.Errorf("empty shares with non-absence proof for row %d", rowIdx) - } - - if len(rnd.Shares) > 0 && rnd.Proof.IsOfAbsence() { - return fmt.Errorf("non-empty shares with absence proof for row %d", rowIdx) - } - - if err := ValidateShares(rnd.Shares); err != nil { - return fmt.Errorf("invalid shares: %w", err) - } - - rowRoot := dah.RowRoots[rowIdx] - if namespace.IsOutsideRange(rowRoot, rowRoot) { - return fmt.Errorf("namespace out of range for row %d", rowIdx) - } - - if !rnd.verifyInclusion(rowRoot, namespace) { - return fmt.Errorf("inclusion proof failed for row %d", rowIdx) - } - return nil -} - -// verifyInclusion checks the inclusion of the row's shares in the provided root using NMT. -func (rnd RowNamespaceData) verifyInclusion(rowRoot []byte, namespace share.Namespace) bool { - leaves := make([][]byte, 0, len(rnd.Shares)) - for _, shr := range rnd.Shares { - namespaceBytes := share.GetNamespace(shr) - leaves = append(leaves, append(namespaceBytes, shr...)) - } - return rnd.Proof.VerifyNamespace( - sha256.New(), - namespace.ToNMT(), - leaves, - rowRoot, - ) -} - -// ToProto converts RowNamespaceData to its protobuf representation for serialization. -func (rnd RowNamespaceData) ToProto() *pb.RowNamespaceData { - return &pb.RowNamespaceData{ - Shares: SharesToProto(rnd.Shares), - Proof: &nmt_pb.Proof{ - Start: int64(rnd.Proof.Start()), - End: int64(rnd.Proof.End()), - Nodes: rnd.Proof.Nodes(), - LeafHash: rnd.Proof.LeafHash(), - IsMaxNamespaceIgnored: rnd.Proof.IsMaxNamespaceIDIgnored(), - }, - } -} - +// RowNamespaceDataFromEDS extracts and constructs a RowNamespaceData from the row of given EDS +// identified by the index and the namespace. func RowNamespaceDataFromEDS( eds *rsmt2d.ExtendedDataSquare, namespace share.Namespace, @@ -134,6 +80,7 @@ func RowNamespaceDataFromShares( }, nil } +// RowNamespaceDataFromProto constructs RowNamespaceData out of its protobuf representation. func RowNamespaceDataFromProto(row *pb.RowNamespaceData) RowNamespaceData { var proof nmt.Proof if row.GetProof().GetLeafHash() != nil { @@ -158,3 +105,60 @@ func RowNamespaceDataFromProto(row *pb.RowNamespaceData) RowNamespaceData { Proof: &proof, } } + +// ToProto converts RowNamespaceData to its protobuf representation for serialization. +func (rnd RowNamespaceData) ToProto() *pb.RowNamespaceData { + return &pb.RowNamespaceData{ + Shares: SharesToProto(rnd.Shares), + Proof: &nmt_pb.Proof{ + Start: int64(rnd.Proof.Start()), + End: int64(rnd.Proof.End()), + Nodes: rnd.Proof.Nodes(), + LeafHash: rnd.Proof.LeafHash(), + IsMaxNamespaceIgnored: rnd.Proof.IsMaxNamespaceIDIgnored(), + }, + } +} + +// Validate checks validity of the RowNamespaceData against the Root, Namespace and Row index. +func (rnd RowNamespaceData) Validate(dah *share.Root, namespace share.Namespace, rowIdx int) error { + if rnd.Proof == nil || rnd.Proof.IsEmptyProof() { + return fmt.Errorf("nil proof") + } + if len(rnd.Shares) == 0 && !rnd.Proof.IsOfAbsence() { + return fmt.Errorf("empty shares with non-absence proof for row %d", rowIdx) + } + + if len(rnd.Shares) > 0 && rnd.Proof.IsOfAbsence() { + return fmt.Errorf("non-empty shares with absence proof for row %d", rowIdx) + } + + if err := ValidateShares(rnd.Shares); err != nil { + return fmt.Errorf("invalid shares: %w", err) + } + + rowRoot := dah.RowRoots[rowIdx] + if namespace.IsOutsideRange(rowRoot, rowRoot) { + return fmt.Errorf("namespace out of range for row %d", rowIdx) + } + + if !rnd.verifyInclusion(rowRoot, namespace) { + return fmt.Errorf("inclusion proof failed for row %d", rowIdx) + } + return nil +} + +// verifyInclusion checks the inclusion of the row's shares in the provided root using NMT. +func (rnd RowNamespaceData) verifyInclusion(rowRoot []byte, namespace share.Namespace) bool { + leaves := make([][]byte, 0, len(rnd.Shares)) + for _, shr := range rnd.Shares { + namespaceBytes := share.GetNamespace(shr) + leaves = append(leaves, append(namespaceBytes, shr...)) + } + return rnd.Proof.VerifyNamespace( + sha256.New(), + namespace.ToNMT(), + leaves, + rowRoot, + ) +} diff --git a/share/shwap/row_namespace_data_id.go b/share/shwap/row_namespace_data_id.go index 1405c20a95..e31b39636e 100644 --- a/share/shwap/row_namespace_data_id.go +++ b/share/shwap/row_namespace_data_id.go @@ -41,15 +41,6 @@ func NewRowNamespaceDataID( return did, nil } -// MarshalBinary encodes RowNamespaceDataID into binary form. -// NOTE: Proto is avoided because -// * Its size is not deterministic which is required for IPLD. -// * No support for uint16 -func (s RowNamespaceDataID) MarshalBinary() ([]byte, error) { - data := make([]byte, 0, RowNamespaceDataIDSize) - return s.appendTo(data), nil -} - // RowNamespaceDataIDFromBinary deserializes a RowNamespaceDataID from its binary form. It returns // an error if the binary data's length does not match the expected size. func RowNamespaceDataIDFromBinary(data []byte) (RowNamespaceDataID, error) { @@ -75,6 +66,15 @@ func RowNamespaceDataIDFromBinary(data []byte) (RowNamespaceDataID, error) { }, nil } +// MarshalBinary encodes RowNamespaceDataID into binary form. +// NOTE: Proto is avoided because +// * Its size is not deterministic which is required for IPLD. +// * No support for uint16 +func (s RowNamespaceDataID) MarshalBinary() ([]byte, error) { + data := make([]byte, 0, RowNamespaceDataIDSize) + return s.appendTo(data), nil +} + // Validate checks the validity of RowNamespaceDataID's fields, including the RowID and the // namespace. func (s RowNamespaceDataID) Validate(root *share.Root) error { diff --git a/share/shwap/sample.go b/share/shwap/sample.go index 12f3bb10b6..ec1185eb1a 100644 --- a/share/shwap/sample.go +++ b/share/shwap/sample.go @@ -21,52 +21,6 @@ type Sample struct { ProofType rsmt2d.Axis // ProofType indicates whether the proof is against a row or a column. } -// Validate checks the inclusion of the share using its Merkle proof under the specified root. -// Returns an error if the proof is invalid or does not correspond to the indicated proof type. -func (s Sample) Validate(dah *share.Root, rowIdx, colIdx int) error { - if s.Proof == nil || s.Proof.IsEmptyProof() { - return errors.New("nil proof") - } - if err := share.ValidateShare(s.Share); err != nil { - return err - } - if s.ProofType != rsmt2d.Row && s.ProofType != rsmt2d.Col { - return fmt.Errorf("invalid SampleProofType: %d", s.ProofType) - } - if !s.verifyInclusion(dah, rowIdx, colIdx) { - return fmt.Errorf("share proof is invalid") - } - return nil -} - -// verifyInclusion checks if the share is included in the given root hash at the specified indices. -func (s Sample) verifyInclusion(dah *share.Root, rowIdx, colIdx int) bool { - size := len(dah.RowRoots) - namespace := inclusionNamespace(s.Share, rowIdx, colIdx, size) - rootHash := share.RootHashForCoordinates(dah, s.ProofType, uint(rowIdx), uint(colIdx)) - return s.Proof.VerifyInclusion( - share.NewSHA256Hasher(), - namespace.ToNMT(), - [][]byte{s.Share}, - rootHash, - ) -} - -// ToProto converts a Sample into its protobuf representation for serialization purposes. -func (s Sample) ToProto() *pb.Sample { - return &pb.Sample{ - Share: &pb.Share{Data: s.Share}, - Proof: &nmt_pb.Proof{ - Start: int64(s.Proof.Start()), - End: int64(s.Proof.End()), - Nodes: s.Proof.Nodes(), - LeafHash: s.Proof.LeafHash(), - IsMaxNamespaceIgnored: s.Proof.IsMaxNamespaceIDIgnored(), - }, - ProofType: pb.AxisType(s.ProofType), - } -} - // SampleFromEDS samples a share from an Extended Data Square based on the provided index and axis. // This function generates a Merkle tree proof for the specified share. func SampleFromEDS( @@ -122,6 +76,52 @@ func SampleFromProto(s *pb.Sample) Sample { } } +// ToProto converts a Sample into its protobuf representation for serialization purposes. +func (s Sample) ToProto() *pb.Sample { + return &pb.Sample{ + Share: &pb.Share{Data: s.Share}, + Proof: &nmt_pb.Proof{ + Start: int64(s.Proof.Start()), + End: int64(s.Proof.End()), + Nodes: s.Proof.Nodes(), + LeafHash: s.Proof.LeafHash(), + IsMaxNamespaceIgnored: s.Proof.IsMaxNamespaceIDIgnored(), + }, + ProofType: pb.AxisType(s.ProofType), + } +} + +// Validate checks the inclusion of the share using its Merkle proof under the specified root. +// Returns an error if the proof is invalid or does not correspond to the indicated proof type. +func (s Sample) Validate(dah *share.Root, rowIdx, colIdx int) error { + if s.Proof == nil || s.Proof.IsEmptyProof() { + return errors.New("nil proof") + } + if err := share.ValidateShare(s.Share); err != nil { + return err + } + if s.ProofType != rsmt2d.Row && s.ProofType != rsmt2d.Col { + return fmt.Errorf("invalid SampleProofType: %d", s.ProofType) + } + if !s.verifyInclusion(dah, rowIdx, colIdx) { + return fmt.Errorf("share proof is invalid") + } + return nil +} + +// verifyInclusion checks if the share is included in the given root hash at the specified indices. +func (s Sample) verifyInclusion(dah *share.Root, rowIdx, colIdx int) bool { + size := len(dah.RowRoots) + namespace := inclusionNamespace(s.Share, rowIdx, colIdx, size) + rootHash := share.RootHashForCoordinates(dah, s.ProofType, uint(rowIdx), uint(colIdx)) + return s.Proof.VerifyInclusion( + share.NewSHA256Hasher(), + namespace.ToNMT(), + [][]byte{s.Share}, + rootHash, + ) +} + // inclusionNamespace returns the namespace for the share based on its position in the square. // Shares from extended part of the square are considered parity shares. It means that // parity shares are located outside of first quadrant of the square. According to the nmt diff --git a/share/shwap/sample_id.go b/share/shwap/sample_id.go index 366f70e3b4..33e83fe12d 100644 --- a/share/shwap/sample_id.go +++ b/share/shwap/sample_id.go @@ -40,15 +40,6 @@ func NewSampleID(height uint64, rowIdx, colIdx int, root *share.Root) (SampleID, return sid, nil } -// MarshalBinary encodes SampleID into binary form. -// NOTE: Proto is avoided because -// * Its size is not deterministic which is required for IPLD. -// * No support for uint16 -func (sid SampleID) MarshalBinary() ([]byte, error) { - data := make([]byte, 0, SampleIDSize) - return sid.appendTo(data), nil -} - // SampleIDFromBinary deserializes a SampleID from binary data, ensuring the data length matches // the expected size. func SampleIDFromBinary(data []byte) (SampleID, error) { @@ -67,6 +58,15 @@ func SampleIDFromBinary(data []byte) (SampleID, error) { }, nil } +// MarshalBinary encodes SampleID into binary form. +// NOTE: Proto is avoided because +// * Its size is not deterministic which is required for IPLD. +// * No support for uint16 +func (sid SampleID) MarshalBinary() ([]byte, error) { + data := make([]byte, 0, SampleIDSize) + return sid.appendTo(data), nil +} + // Validate checks the validity of the SampleID by ensuring the ShareIndex is within the bounds of // the square size. func (sid SampleID) Validate(root *share.Root) error { diff --git a/share/shwap/share.go b/share/shwap/share.go index de15e15adb..a7f7ef67b7 100644 --- a/share/shwap/share.go +++ b/share/shwap/share.go @@ -38,6 +38,7 @@ func SharesFromProto(shrs []*pb.Share) []share.Share { return shares } +// ValidateShares takes the slice of shares and checks their conformance to share format. func ValidateShares(shares []share.Share) error { for i, shr := range shares { if err := share.ValidateShare(shr); err != nil {