Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions share/eds/byzantine/bad_encoding.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"bytes"
"errors"
"fmt"
"slices"

"github.com/celestiaorg/celestia-app/v6/pkg/wrapper"
"github.com/celestiaorg/go-fraud"
Expand Down Expand Up @@ -177,6 +178,7 @@
return errIncorrectAmountOfShares
}

indexes := make([]int, 0) // nolint:prealoc

Check failure on line 181 in share/eds/byzantine/bad_encoding.go

View workflow job for this annotation

GitHub Actions / go-ci / Lint

directive `// nolint:prealoc` should be written without leading space as `//nolint:prealoc` (nolintlint)
// verify that Merkle proofs correspond to particular shares.
shares := make([][]byte, width)
for index, shr := range p.Shares {
Expand All @@ -189,6 +191,14 @@
return errIncorrectShare
}
shares[index] = shr.ToBytes()
indexes = append(indexes, shr.Proof.Start())
}

isSorted := slices.IsSorted(indexes)
if !isSorted {
err := errors.New("provided share list is not sorted")
log.Debugf("%v", err)
return err
}

codec := share.DefaultRSMT2DCodec()
Expand Down
66 changes: 66 additions & 0 deletions share/eds/byzantine/bad_encoding_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,32 @@ func TestBEFP_Validate(t *testing.T) {
require.ErrorIs(t, err, errNMTTreeRootsMatch)
},
},
{
name: "invalid BEFP with unordered shares list",
prepareFn: func() error {
validSquare := edstest.RandEDS(t, 2)
validRoots, err := share.NewAxisRoots(validSquare)
require.NoError(t, err)
err = ipld.ImportEDS(ctx, validSquare, bServ)
require.NoError(t, err)
validShares := validSquare.Flattened()
errInvalidByz := NewErrByzantine(ctx, bServ.Blockstore(), validRoots,
&rsmt2d.ErrByzantineData{
Axis: rsmt2d.Row,
Index: 0,
Shares: validShares[0:4],
},
)
var errInvalid *ErrByzantine
require.ErrorAs(t, errInvalidByz, &errInvalid)
errInvalid.Shares[0], errInvalid.Shares[1] = errInvalid.Shares[1], errInvalid.Shares[0]
invalidBefp := CreateBadEncodingProof([]byte("hash"), 0, errInvalid)
return invalidBefp.Validate(&header.ExtendedHeader{DAH: validRoots})
},
expectedResult: func(err error) {
require.Error(t, err)
},
},
{
name: "incorrect share with Proof",
prepareFn: func() error {
Expand Down Expand Up @@ -308,3 +334,43 @@ func (n *namespacedBlockService) GetBlocks(ctx context.Context, cids []cid.Cid)
}()
return resultCh
}

// TestBEFP_ForgedByReorderedShares checks whether a valid row can be made to look
// byzantine by reordering otherwise valid shares with valid inclusion proofs.
func TestBEFP_ForgedByReorderedShares(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
t.Cleanup(cancel)
bServ := ipld.NewMemBlockservice()
size := 4
shares, err := libshare.RandShares(size * size)
require.NoError(t, err)
eds, err := ipld.AddShares(ctx, shares, bServ)
require.NoError(t, err)
roots, err := share.NewAxisRoots(eds)
require.NoError(t, err)
rowIdx := 0
width := int(eds.Width())
row := eds.Row(uint(rowIdx))
shareProofs := make([]*ShareWithProof, width)
for i := 0; i < width; i++ {
sh, err := libshare.NewShare(row[i])
require.NoError(t, err)
proof, err := GetShareWithProof(ctx, bServ, roots, *sh, rsmt2d.Row, rowIdx, i)
require.NoError(t, err)
shareProofs[i] = proof
}
// Reorder two shares while keeping their proofs intact.
shareProofs[0], shareProofs[1] = shareProofs[1], shareProofs[0]
fakeErr := ErrByzantine{
Index: uint32(rowIdx),
Shares: shareProofs,
Axis: rsmt2d.Row,
}
h := &header.ExtendedHeader{
RawHeader: core.Header{Height: 1},
DAH: roots,
}
proof := CreateBadEncodingProof([]byte("hash"), h.Height(), &fakeErr)
err = proof.Validate(h)
require.Error(t, err)
}
Loading