Skip to content

Commit a630a8f

Browse files
renaynayvgonkivs
andauthored
PORT: refactor(blob): refactor blob's commitment proof verification method (#4648)
Co-authored-by: Viacheslav <[email protected]>
1 parent df2e63b commit a630a8f

File tree

4 files changed

+78
-141
lines changed

4 files changed

+78
-141
lines changed

blob/blob_fuzz_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ func FuzzBlobUnmarshal(f *testing.F) {
4343
type verifyCorpus struct {
4444
CP *CommitmentProof `json:"commitment_proof"`
4545
Root []byte `json:"root"`
46-
SThreshold int `json:"sub_threshold"`
46+
Commitment Commitment `json:"commitment"`
4747
}
4848

4949
func FuzzCommitmentProofVerify(f *testing.F) {
@@ -87,6 +87,6 @@ func FuzzCommitmentProofVerify(f *testing.F) {
8787
if commitProof == nil {
8888
return
8989
}
90-
_, _ = commitProof.Verify(val.Root, val.SThreshold)
90+
_ = commitProof.Verify(val.Root, val.Commitment)
9191
})
9292
}

blob/commitment_proof.go

Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99

1010
"github.com/celestiaorg/celestia-app/v6/pkg/appconsts"
1111
"github.com/celestiaorg/celestia-app/v6/pkg/proof"
12+
"github.com/celestiaorg/go-square/merkle"
1213
"github.com/celestiaorg/go-square/v3/inclusion"
1314
libshare "github.com/celestiaorg/go-square/v3/share"
1415
"github.com/celestiaorg/nmt"
@@ -83,17 +84,29 @@ func (commitmentProof *CommitmentProof) Validate() error {
8384
}
8485

8586
// Verify verifies that a commitment proof is valid, i.e., the subtree roots commit
86-
// to some data that was posted to a square.
87-
// Expects the commitment proof to be properly formulated and validated
88-
// using the Validate() function.
89-
func (commitmentProof *CommitmentProof) Verify(root []byte, subtreeRootThreshold int) (bool, error) {
90-
if len(root) == 0 {
91-
return false, errors.New("root must be non-empty")
87+
// to specific data that was posted to a square.
88+
func (commitmentProof *CommitmentProof) Verify(dataRoot, commitment []byte) error {
89+
if len(dataRoot) == 0 {
90+
return errors.New("root must be non-empty")
91+
}
92+
93+
if len(commitment) == 0 {
94+
return errors.New("commitment must be non-empty")
95+
}
96+
97+
err := commitmentProof.Validate()
98+
if err != nil {
99+
return err
100+
}
101+
102+
root := merkle.HashFromByteSlices(commitmentProof.SubtreeRoots)
103+
if !bytes.Equal(commitment, root) {
104+
return errors.New("current proof does not belong to the provided commitment")
92105
}
93106

94107
rp := commitmentProof.RowProof
95-
if err := rp.Validate(root); err != nil {
96-
return false, err
108+
if err := rp.Validate(dataRoot); err != nil {
109+
return err
97110
}
98111

99112
nmtHasher := nmt.NewNmtHasher(appconsts.NewBaseHashFunc(), libshare.NamespaceSize, true)
@@ -104,10 +117,6 @@ func (commitmentProof *CommitmentProof) Verify(root []byte, subtreeRootThreshold
104117
numberOfShares += proof.End() - proof.Start()
105118
}
106119

107-
if subtreeRootThreshold <= 0 {
108-
return false, errors.New("subtreeRootThreshold must be > 0")
109-
}
110-
111120
// use the computed total number of shares to calculate the subtree roots
112121
// width.
113122
// the subtree roots width is defined in ADR-013:
@@ -121,15 +130,15 @@ func (commitmentProof *CommitmentProof) Verify(root []byte, subtreeRootThreshold
121130
// calculate the share range that each subtree root commits to.
122131
ranges, err := nmt.ToLeafRanges(subtreeRootProof.Start(), subtreeRootProof.End(), subtreeRootsWidth)
123132
if err != nil {
124-
return false, err
133+
return err
125134
}
126135

127136
if len(commitmentProof.SubtreeRoots) < subtreeRootsCursor {
128-
return false, fmt.Errorf("len(commitmentProof.SubtreeRoots)=%d < subtreeRootsCursor=%d",
137+
return fmt.Errorf("len(commitmentProof.SubtreeRoots)=%d < subtreeRootsCursor=%d",
129138
len(commitmentProof.SubtreeRoots), subtreeRootsCursor)
130139
}
131140
if len(commitmentProof.SubtreeRoots) < subtreeRootsCursor+len(ranges) {
132-
return false, fmt.Errorf("len(commitmentProof.SubtreeRoots)=%d < subtreeRootsCursor+len(ranges)=%d",
141+
return fmt.Errorf("len(commitmentProof.SubtreeRoots)=%d < subtreeRootsCursor+len(ranges)=%d",
133142
len(commitmentProof.SubtreeRoots), subtreeRootsCursor+len(ranges))
134143
}
135144
valid, err := subtreeRootProof.VerifySubtreeRootInclusion(
@@ -139,21 +148,24 @@ func (commitmentProof *CommitmentProof) Verify(root []byte, subtreeRootThreshold
139148
commitmentProof.RowProof.RowRoots[i],
140149
)
141150
if err != nil {
142-
return false, err
151+
return err
143152
}
144153
if !valid {
145-
return false,
146-
fmt.Errorf(
147-
"subtree root proof for range [%d, %d) is invalid",
148-
subtreeRootProof.Start(),
149-
subtreeRootProof.End(),
150-
)
154+
return fmt.Errorf(
155+
"subtree root proof for range [%d, %d) is invalid",
156+
subtreeRootProof.Start(),
157+
subtreeRootProof.End(),
158+
)
151159
}
152160
subtreeRootsCursor += len(ranges)
153161
}
154162

155163
// verify row roots to data root proof
156-
return commitmentProof.RowProof.VerifyProof(root), nil
164+
valid := commitmentProof.RowProof.VerifyProof(dataRoot)
165+
if !valid {
166+
return fmt.Errorf("row roots were not included in the data root")
167+
}
168+
return nil
157169
}
158170

159171
// MarshalJSON marshals an CommitmentProof to JSON. Uses tendermint encoder for row proof for compatibility.

blob/repro_test.go

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,58 +3,73 @@ package blob
33
import (
44
"testing"
55

6+
"github.com/stretchr/testify/require"
7+
68
"github.com/celestiaorg/celestia-app/v6/pkg/proof"
9+
"github.com/celestiaorg/go-square/v3/inclusion"
10+
libshare "github.com/celestiaorg/go-square/v3/share"
711
"github.com/celestiaorg/nmt"
812
"github.com/celestiaorg/nmt/pb"
913
)
1014

11-
// Reported at https://github.com/celestiaorg/celestia-node/issues/3731.
1215
func TestCommitmentProofRowProofVerifyWithEmptyRoot(t *testing.T) {
16+
libBlob, err := libshare.GenerateV0Blobs([]int{8}, true)
17+
require.NoError(t, err)
18+
nodeBlob, err := ToNodeBlobs(libBlob...)
19+
require.NoError(t, err)
20+
roots, err := inclusion.GenerateSubtreeRoots(libBlob[0], subtreeRootThreshold)
21+
require.NoError(t, err)
22+
1323
cp := &CommitmentProof{
24+
SubtreeRoots: roots,
1425
RowProof: proof.RowProof{
1526
Proofs: []*proof.Proof{{}},
1627
},
1728
}
1829
root := []byte{0xd3, 0x4d, 0x34}
19-
if _, err := cp.Verify(root, 1); err == nil {
30+
if err := cp.Verify(root, nodeBlob[0].Commitment); err == nil {
2031
t.Fatal("expected a non-nil error")
2132
}
2233
}
2334

24-
// Reported at https://github.com/celestiaorg/celestia-node/issues/3730.
2535
func TestCommitmentProofRowProofVerify(t *testing.T) {
36+
libBlob, err := libshare.GenerateV0Blobs([]int{8}, true)
37+
require.NoError(t, err)
38+
nodeBlob, err := ToNodeBlobs(libBlob...)
39+
require.NoError(t, err)
40+
roots, err := inclusion.GenerateSubtreeRoots(libBlob[0], subtreeRootThreshold)
41+
require.NoError(t, err)
2642
cp := &CommitmentProof{
43+
SubtreeRoots: roots,
2744
RowProof: proof.RowProof{
2845
Proofs: []*proof.Proof{{}},
2946
},
3047
}
31-
if _, err := cp.Verify(nil, 1); err == nil {
48+
if err := cp.Verify(nil, nodeBlob[0].Commitment); err == nil {
3249
t.Fatal("expected a non-nil error")
3350
}
3451
}
3552

36-
// Reported at https://github.com/celestiaorg/celestia-node/issues/3729.
3753
func TestCommitmentProofVerifySliceBound(t *testing.T) {
54+
libBlob, err := libshare.GenerateV0Blobs([]int{8}, true)
55+
require.NoError(t, err)
56+
nodeBlob, err := ToNodeBlobs(libBlob...)
57+
require.NoError(t, err)
58+
roots, err := inclusion.GenerateSubtreeRoots(libBlob[0], subtreeRootThreshold)
59+
require.NoError(t, err)
60+
3861
proof := nmt.ProtoToProof(pb.Proof{End: 1})
3962
cp := &CommitmentProof{
63+
SubtreeRoots: roots,
4064
SubtreeRootProofs: []*nmt.Proof{
4165
&proof,
4266
},
4367
}
44-
if _, err := cp.Verify(nil, 1); err == nil {
45-
t.Fatal("expected a non-nil error")
46-
}
47-
}
48-
49-
// Reported at https://github.com/celestiaorg/celestia-node/issues/3728.
50-
func TestCommitmentProofVerifyZeroSubThreshold(t *testing.T) {
51-
cp := new(CommitmentProof)
52-
if _, err := cp.Verify(nil, 0); err == nil {
68+
if err := cp.Verify([]byte{0xd3, 0x4d, 0x34}, nodeBlob[0].Commitment); err == nil {
5369
t.Fatal("expected a non-nil error")
5470
}
5571
}
5672

57-
// Reported at https://github.com/celestiaorg/celestia-node/issues/3727.
5873
func TestBlobUnmarshalRepro(t *testing.T) {
5974
blob := new(Blob)
6075
if err := blob.UnmarshalJSON([]byte("{}")); err == nil {

blob/service_test.go

Lines changed: 11 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,9 @@ import (
1919
"github.com/stretchr/testify/assert"
2020
"github.com/stretchr/testify/require"
2121

22-
"github.com/celestiaorg/celestia-app/v6/pkg/appconsts"
23-
pkgproof "github.com/celestiaorg/celestia-app/v6/pkg/proof"
2422
"github.com/celestiaorg/celestia-app/v6/pkg/wrapper"
2523
"github.com/celestiaorg/go-header/store"
26-
"github.com/celestiaorg/go-square/merkle"
27-
"github.com/celestiaorg/go-square/v3/inclusion"
2824
libshare "github.com/celestiaorg/go-square/v3/share"
29-
"github.com/celestiaorg/nmt"
3025
"github.com/celestiaorg/rsmt2d"
3126

3227
"github.com/celestiaorg/celestia-node/header"
@@ -973,7 +968,12 @@ func TestProveCommitmentAllCombinations(t *testing.T) {
973968

974969
func proveAndVerifyShareCommitments(t *testing.T, blobSize int) {
975970
msgs, blobs, nss, eds, _, _, dataRoot := edstest.GenerateTestBlock(t, blobSize, 10)
976-
for msgIndex, msg := range msgs {
971+
randomBlob, err := libshare.GenerateV0Blobs([]int{10}, true)
972+
require.NoError(t, err)
973+
nodeBlob, err := ToNodeBlobs(randomBlob...)
974+
require.NoError(t, err)
975+
976+
for msgIndex := range msgs {
977977
t.Run(fmt.Sprintf("msgIndex=%d", msgIndex), func(t *testing.T) {
978978
blb, err := NewBlob(blobs[msgIndex].ShareVersion(), nss[msgIndex], blobs[msgIndex].Data(), nil)
979979
require.NoError(t, err)
@@ -985,111 +985,21 @@ func proveAndVerifyShareCommitments(t *testing.T, blobSize int) {
985985

986986
// make sure the actual commitment attests to the data
987987
require.NoError(t, actualCommitmentProof.Validate())
988-
valid, err := actualCommitmentProof.Verify(
988+
err = actualCommitmentProof.Verify(
989989
dataRoot,
990-
appconsts.SubtreeRootThreshold,
990+
blb.Commitment,
991991
)
992992
require.NoError(t, err)
993-
require.True(t, valid)
994993

995-
// generate an expected proof and verify it's valid
996-
expectedCommitmentProof := generateCommitmentProofFromBlock(t, eds, nss[msgIndex], blobs[msgIndex], dataRoot)
997-
require.NoError(t, expectedCommitmentProof.Validate())
998-
valid, err = expectedCommitmentProof.Verify(
994+
err = actualCommitmentProof.Verify(
999995
dataRoot,
1000-
appconsts.SubtreeRootThreshold,
996+
nodeBlob[0].Commitment,
1001997
)
1002-
require.NoError(t, err)
1003-
require.True(t, valid)
1004-
1005-
// make sure the expected proof is the same as the actual on
1006-
assert.Equal(t, expectedCommitmentProof, *actualCommitmentProof)
1007-
1008-
// make sure the expected commitment commits to the subtree roots in the result proof
1009-
actualCommitment, _ := merkle.ProofsFromByteSlices(actualCommitmentProof.SubtreeRoots)
1010-
assert.Equal(t, msg.ShareCommitments[0], actualCommitment)
998+
require.Error(t, err)
1011999
})
10121000
}
10131001
}
10141002

1015-
// generateCommitmentProofFromBlock takes a block and a PFB index and generates the commitment proof
1016-
// using the traditional way of doing, instead of using the API.
1017-
func generateCommitmentProofFromBlock(
1018-
t *testing.T,
1019-
eds *rsmt2d.ExtendedDataSquare,
1020-
ns libshare.Namespace,
1021-
blob *libshare.Blob,
1022-
dataRoot []byte,
1023-
) CommitmentProof {
1024-
// create the blob from the data
1025-
blb, err := NewBlob(blob.ShareVersion(),
1026-
ns,
1027-
blob.Data(),
1028-
nil,
1029-
)
1030-
require.NoError(t, err)
1031-
1032-
// convert the blob to a number of shares
1033-
blobShares, err := BlobsToShares(blb)
1034-
require.NoError(t, err)
1035-
1036-
// find the first share of the blob in the ODS
1037-
startShareIndex := -1
1038-
for i, sh := range eds.FlattenedODS() {
1039-
if bytes.Equal(sh, blobShares[0].ToBytes()) {
1040-
startShareIndex = i
1041-
break
1042-
}
1043-
}
1044-
require.Greater(t, startShareIndex, 0)
1045-
1046-
// create an inclusion proof of the blob using the share range instead of the commitment
1047-
sharesProof, err := pkgproof.NewShareInclusionProofFromEDS(
1048-
eds,
1049-
ns,
1050-
libshare.NewRange(startShareIndex, startShareIndex+len(blobShares)),
1051-
)
1052-
require.NoError(t, err)
1053-
require.NoError(t, sharesProof.Validate(dataRoot))
1054-
1055-
// calculate the subtree roots
1056-
subtreeRoots := make([][]byte, 0)
1057-
dataCursor := 0
1058-
for _, proof := range sharesProof.ShareProofs {
1059-
ranges, err := nmt.ToLeafRanges(
1060-
int(proof.Start),
1061-
int(proof.End),
1062-
inclusion.SubTreeWidth(len(blobShares), subtreeRootThreshold),
1063-
)
1064-
require.NoError(t, err)
1065-
roots, err := computeSubtreeRoots(
1066-
blobShares[dataCursor:int32(dataCursor)+proof.End-proof.Start],
1067-
ranges,
1068-
int(proof.Start),
1069-
)
1070-
require.NoError(t, err)
1071-
subtreeRoots = append(subtreeRoots, roots...)
1072-
dataCursor += int(proof.End - proof.Start)
1073-
}
1074-
1075-
// convert the nmt proof to be accepted by the commitment proof
1076-
nmtProofs := make([]*nmt.Proof, 0)
1077-
for _, proof := range sharesProof.ShareProofs {
1078-
nmtProof := nmt.NewInclusionProof(int(proof.Start), int(proof.End), proof.Nodes, true)
1079-
nmtProofs = append(nmtProofs, &nmtProof)
1080-
}
1081-
1082-
commitmentProof := CommitmentProof{
1083-
SubtreeRoots: subtreeRoots,
1084-
SubtreeRootProofs: nmtProofs,
1085-
NamespaceID: sharesProof.NamespaceId,
1086-
RowProof: *sharesProof.RowProof,
1087-
NamespaceVersion: uint8(sharesProof.NamespaceVersion),
1088-
}
1089-
1090-
return commitmentProof
1091-
}
1092-
10931003
func rawBlobSize(totalSize int) int {
10941004
return totalSize - delimLen(uint64(totalSize))
10951005
}

0 commit comments

Comments
 (0)