Skip to content

Commit

Permalink
chore(ipld): extract byzantine error and constructor for it into a se…
Browse files Browse the repository at this point in the history
…parate file
  • Loading branch information
Wondertan committed May 20, 2022
1 parent 471277c commit aead38b
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 62 deletions.
15 changes: 0 additions & 15 deletions ipld/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package ipld

import (
"context"
"fmt"

"github.com/ipfs/go-cid"
ipld "github.com/ipfs/go-ipld-format"
Expand All @@ -13,20 +12,6 @@ import (
"github.com/celestiaorg/nmt/namespace"
)

// ErrByzantine is an error converted from rsmt2d.ByzantineRow/Col +
// Merkle Proof of each share.
type ErrByzantine struct {
Index uint8
Shares []*ShareWithProof
// TODO(@vgokivs): Change to enum type and rename to Axis after
// updating rsmt2d
IsRow bool
}

func (e *ErrByzantine) Error() string {
return fmt.Sprintf("byzantine error. isRow:%v, Index:%v", e.IsRow, e.Index)
}

// GetShare fetches and returns the data for leaf `leafIndex` of root `rootCid`.
func GetShare(
ctx context.Context,
Expand Down
53 changes: 6 additions & 47 deletions ipld/retriever.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import (
"github.com/tendermint/tendermint/pkg/da"
"github.com/tendermint/tendermint/pkg/wrapper"

"github.com/celestiaorg/celestia-node/ipld/plugin"
"github.com/celestiaorg/nmt"
"github.com/celestiaorg/rsmt2d"
)
Expand Down Expand Up @@ -58,8 +57,12 @@ func (r *Retriever) Retrieve(ctx context.Context, dah *da.DataAvailabilityHeader
if err == nil {
return eds, nil
}
if byzErr := r.checkForByzantineError(ctx, dah, err); byzErr != nil {
return nil, byzErr
var (
errRow *rsmt2d.ErrByzantineRow
errCol *rsmt2d.ErrByzantineCol
)
if errors.As(err, &errRow) || errors.As(err, &errCol) {
return nil, NewErrByzantine(ctx, r.dag, dah, errRow, errCol)
}
log.Warnw("not enough shares to reconstruct data square, requesting more...", "err", err)
}
Expand All @@ -69,50 +72,6 @@ func (r *Retriever) Retrieve(ctx context.Context, dah *da.DataAvailabilityHeader
return nil, format.ErrNotFound
}

// checkForByzantineError ensures that passed error is rsmt2d.ErrByzantineRow/Col,
// fetches proof for every share from row or col and converts error to ErrByzantine
func (r *Retriever) checkForByzantineError(
ctx context.Context,
dah *da.DataAvailabilityHeader,
byzErr error,
) error {
var (
errRow *rsmt2d.ErrByzantineRow
errCol *rsmt2d.ErrByzantineCol
)
if !errors.As(byzErr, &errRow) && !errors.As(byzErr, &errCol) {
return nil
}
var (
errShares [][]byte
root []byte
index uint8
)
isRow := false
if errRow != nil {
errShares = errRow.Shares
root = dah.RowsRoots[errRow.RowNumber]
index = uint8(errRow.RowNumber)
isRow = true
} else {
errShares = errCol.Shares
root = dah.ColumnRoots[errCol.ColNumber]
index = uint8(errCol.ColNumber)
}

sharesWithProof, err := GetProofsForShares(
ctx,
r.dag,
plugin.MustCidFromNamespacedSha256(root),
errShares,
)
if err != nil {
return err
}

return &ErrByzantine{Index: index, Shares: sharesWithProof, IsRow: isRow}
}

type retrieverSession struct {
dag format.NodeGetter
adder *NmtNodeAdder
Expand Down
73 changes: 73 additions & 0 deletions ipld/retriever_byzantine.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package ipld

import (
"context"
"fmt"

format "github.com/ipfs/go-ipld-format"
"github.com/tendermint/tendermint/pkg/da"

"github.com/celestiaorg/celestia-node/ipld/plugin"
"github.com/celestiaorg/rsmt2d"
)

// ErrByzantine is a thrown when recovered data square is not correct
// (merkle proofs do not match parity erasure-coding data).
//
// It is converted from rsmt2d.ByzantineRow/Col +
// Merkle Proof for each share.
type ErrByzantine struct {
Index uint8
Shares []*ShareWithProof
// TODO(@vgokivs): Change to enum type and rename to Axis after
// updating rsmt2d
IsRow bool
}

func (e *ErrByzantine) Error() string {
return fmt.Sprintf("byzantine error. isRow:%v, Index:%v", e.IsRow, e.Index)
}

// NewErrByzantine creates new ErrByzantine from rsmt2d error.
// If error happens during proof collection, it terminates the process with os.Exit(1).
// TODO(@Wondertan): Migrate to ErrByzantineData in the newest rsmt2d
func NewErrByzantine(
ctx context.Context,
dag format.NodeGetter,
dah *da.DataAvailabilityHeader,
errRow *rsmt2d.ErrByzantineRow,
errCol *rsmt2d.ErrByzantineCol) *ErrByzantine {
var (
errShares [][]byte
root []byte
index uint8
)
isRow := false
if errRow != nil {
errShares = errRow.Shares
root = dah.RowsRoots[errRow.RowNumber]
index = uint8(errRow.RowNumber)
isRow = true
} else {
errShares = errCol.Shares
root = dah.ColumnRoots[errCol.ColNumber]
index = uint8(errCol.ColNumber)
}

sharesWithProof, err := GetProofsForShares(
ctx,
dag,
plugin.MustCidFromNamespacedSha256(root),
errShares,
)
if err != nil {
// Fatal as rsmt2d proved that error is byzantine,
// but we cannot properly collect the proof,
// so verification will fail and thus services won't be stopped
// while we still have to stop them.
// TODO(@Wondertan): Find a better way to handle
log.Fatalw("getting proof for ErrByzantine", "err", err)
}

return &ErrByzantine{Index: index, Shares: sharesWithProof, IsRow: isRow}
}

0 comments on commit aead38b

Please sign in to comment.