Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(blob)!: update the blob.Proof to return a proof to the data root #3610

Merged
Merged
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
2005566
fix: use a proof to the data root of blob
rach-id Jul 30, 2024
93f9998
fix: row indexes
rach-id Jul 31, 2024
cdac209
fix: end share index
rach-id Jul 31, 2024
474e242
Merge branch 'main' into update-the-blob-proof
rach-id Jul 31, 2024
5e01836
docs: verify
rach-id Jul 31, 2024
c6db926
docs: verify
rach-id Jul 31, 2024
adafd92
docs: verify
rach-id Jul 31, 2024
1e41c42
docs: verify
rach-id Jul 31, 2024
bc813dd
Merge remote-tracking branch 'origin/update-the-blob-proof' into upda…
rach-id Jul 31, 2024
45623fa
chore: lint
rach-id Jul 31, 2024
9465b5d
chore: revert unnecessary change
rach-id Jul 31, 2024
3d071a5
fix: blobsub tests
rach-id Jul 31, 2024
b97e2dd
chore: lint
rach-id Jul 31, 2024
469cc8c
chore: remove TODOS
rach-id Jul 31, 2024
0ca01c3
fix: update Included implementation too
rach-id Jul 31, 2024
b60fbed
chore: update the blob proof example
rach-id Aug 1, 2024
5ca260a
chore: use the blob as a parameter for the proof.Verify
rach-id Aug 1, 2024
afb2293
chore: remove errEmptyShares
rach-id Aug 1, 2024
052a797
chore: new line
rach-id Aug 1, 2024
a006f94
chore: godoc
rach-id Aug 1, 2024
698d906
chore: remove comment
rach-id Aug 1, 2024
ccb5799
chore: wrap errors
rach-id Aug 1, 2024
8f79b51
Revert "chore: remove errEmptyShares"
rach-id Aug 1, 2024
952b4ad
chore: getter -> header
rach-id Aug 1, 2024
82d30cc
chore: remove equal method
rach-id Aug 1, 2024
85e2391
fix: check namespace when getting blob
rach-id Aug 1, 2024
8e68693
chore: refactor blob index calculation
rach-id Aug 1, 2024
2c9e2f5
fix: revert removing import
rach-id Aug 1, 2024
2d209ad
chore: use getEDS to get the shares
rach-id Aug 2, 2024
ae65e8d
feat: check if the commitment is valid as suggested by @vgonkivs
rach-id Aug 2, 2024
e473057
feat: check the row index of the end of the namespace
rach-id Aug 2, 2024
e5677a8
chore: refactor getting shares from eds
rach-id Aug 2, 2024
2bd0da0
refactor: use correct variable for calculation
rach-id Aug 2, 2024
d7bb13b
refactor: to odsShares
rach-id Aug 2, 2024
11bfc48
refactor: to edsShares
rach-id Aug 2, 2024
500f0b8
refactor: to exclusiveEndShareIndex
rach-id Aug 2, 2024
11d425c
refactor: move prove row roots to blob.go
rach-id Aug 2, 2024
5b054bc
refactor: rename to RowToDataRootProof
rach-id Aug 5, 2024
1fb124d
Update blob/blob.go
rach-id Aug 5, 2024
28cb03f
refactor: move if before
rach-id Aug 5, 2024
c825724
Merge remote-tracking branch 'origin/update-the-blob-proof' into upda…
rach-id Aug 5, 2024
8a28b3f
test: add verification tests
rach-id Aug 5, 2024
1757300
chore: refactor the eds shares range
rach-id Aug 5, 2024
857e3ed
test: invalid row proof test
rach-id Aug 5, 2024
712c272
fix: add row proof validation
rach-id Aug 5, 2024
c6b1404
Merge branch 'main' into update-the-blob-proof
rach-id Aug 5, 2024
2718e4a
fix: row proof generation and validation
rach-id Aug 5, 2024
14fe9be
Merge remote-tracking branch 'origin/update-the-blob-proof' into upda…
rach-id Aug 5, 2024
3630f33
chore: use a different error for mismatch commitment
rach-id Aug 6, 2024
26de33e
test: increase the blob size for the test case to pass
rach-id Aug 6, 2024
a717e4d
chore: lint
rach-id Aug 6, 2024
10de41b
Merge branch 'feature_branch_blob_proof' into update-the-blob-proof
rach-id Aug 7, 2024
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
78 changes: 50 additions & 28 deletions api/docgen/exampledata/blobProof.json
Original file line number Diff line number Diff line change
@@ -1,31 +1,53 @@
[
{
"end": 8,
"nodes": [
"/////////////////////////////////////////////////////////////////////////////wuxStDHcZ7+b5byNQMVLJbzBT3wmObsThoQ0sCTjTCP"
{
"ShareToRowRootProof": [
{
"start": 3,
"end": 4,
"nodes": [
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQ72eTVOUxB9THxFjAEwtTePJQA1b0xcz2f6TJc400Uw",
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBD0CYbGYoGN4q9VfSmeGZeg/h1NDBA/jtXjZrrKRHE6",
"/////////////////////////////////////////////////////////////////////////////8KDE4JDf0N2lZB7DW1Fpasdk/wz4jHOxuBPAk5Vf5ZI"
]
},
{
"end": 1,
"nodes": [
"//////////////////////////////////////7//////////////////////////////////////plEqgR/c4IAVkNdYRWOYOAESD4whneKR54Dz5Dfe4p2",
"//////////////////////////////////////7//////////////////////////////////////lrD0qJ9dspxSO1Yl8NDioZfgOm8Yj63Y+BGDRHlKCRj",
"/////////////////////////////////////////////////////////////////////////////xQyI+g89aM6rhy9rl2eKr0Uc2NPauf3fkLY3Z+gBtuM"
]
}
],
"RowProof": {
"row_roots": [
"00000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000808080808080808BC517066A5A8C81E2A4353DB500EBB3410047A93D2EE8ADF0B6797B9A5519557",
"0000000000000000000000000000000000000000000808080808080808FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE015AB6AAC6FAF0ABF26F9453AF390FDA3B39EB384F0B71D0170D84CF69CBA2BC"
],
"is_max_namespace_ignored": true
},
{
"end": 8,
"nodes": [
"//////////////////////////////////////////////////////////////////////////////n1NeJxPU2bZUAccKZZ+LAu2Wj5ajbVYURV9ojhSKwp"
"proofs": [
{
"total": 16,
"index": 1,
"leaf_hash": "lJek/BHnKH6PyRB8jlk69F6EY9Tfx2LRanaF74JVciU=",
"aunts": [
"bLjvftajE6jVsgQQBkV4RUPESRc+v4bhP0Ljf36858Q=",
"QaF9mNskaURxk98S3BExB1PzRAjOqVydrDLvUu0B5/M=",
"K2xW8JJ3Ff4FvtbfZi5ZD/ygnswaNCNIKXsSzbO2Jrc=",
"uySRG/gINLAgGgywJCTiXMlFkfQivF1O1zLg5+RRUP8="
]
},
{
"total": 16,
"index": 2,
"leaf_hash": "h+4ND52kT4qkc9nWW22dIMAK/4YjkC6fBoD01WF0+Uo=",
"aunts": [
"2x8OISRBMLYJRV8NfTNtVvZUg2F7MtCK5xCZuE9fQwQ=",
"Xvr5IalE2y3pxHjxh5kcHFSRaz4g5MxdOj4NIGwRXY0=",
"K2xW8JJ3Ff4FvtbfZi5ZD/ygnswaNCNIKXsSzbO2Jrc=",
"uySRG/gINLAgGgywJCTiXMlFkfQivF1O1zLg5+RRUP8="
]
}
],
"is_max_namespace_ignored": true
},
{
"end": 8,
"nodes": [
"/////////////////////////////////////////////////////////////////////////////0xK8BKnzDmwK0HR4ZJvyB4kh3jPPXGxaGPFoga8vPxF"
],
"is_max_namespace_ignored": true
},
{
"end": 7,
"nodes": [
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAMJ/xGlNMdEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwn/EaU0x0UTO9HUGKjyjcv5U2gHeSjJ8S1rftqv6k8kxlVWW8e/7",
"/////////////////////////////////////////////////////////////////////////////wexh4khLQ9HQ2X6nh9wU5B+m6r+LWwPTEDTa5/CosDF"
],
"is_max_namespace_ignored": true
"start_row": 1,
"end_row": 3
}
]
}
92 changes: 65 additions & 27 deletions blob/blob.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ import (
"errors"
"fmt"

"github.com/tendermint/tendermint/crypto/merkle"
"github.com/tendermint/tendermint/pkg/consts"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
coretypes "github.com/tendermint/tendermint/types"

"github.com/celestiaorg/celestia-app/pkg/appconsts"
"github.com/celestiaorg/celestia-app/pkg/shares"
Expand All @@ -16,42 +19,70 @@ import (
"github.com/celestiaorg/celestia-node/share"
)

//nolint:unused
Wondertan marked this conversation as resolved.
Show resolved Hide resolved
var errEmptyShares = errors.New("empty shares")
rach-id marked this conversation as resolved.
Show resolved Hide resolved

// The Proof is a set of nmt proofs that can be verified only through
// the included method (due to limitation of the nmt https://github.com/celestiaorg/nmt/issues/218).
// Proof proves the WHOLE namespaced data to the row roots.
// TODO (@vgonkivs): rework `Proof` in order to prove a particular blob.
// https://github.com/celestiaorg/celestia-node/issues/2303
type Proof []*nmt.Proof

func (p Proof) Len() int { return len(p) }
// Proof constructs the proof of a blob to the data root.
type Proof struct {
// ShareToRowRootProof the proofs of the shares to the row roots they belong to.
// If the blob spans across multiple rows, then this will contain multiple proofs.
ShareToRowRootProof []*tmproto.NMTProof
// RowProof the proofs of the row roots containing the blob shares
// to the data root.
RowProof coretypes.RowProof
rach-id marked this conversation as resolved.
Show resolved Hide resolved
}

// equal is a temporary method that compares two proofs.
// should be removed in BlobService V1.
func (p Proof) equal(input Proof) error {
if p.Len() != input.Len() {
return ErrInvalidProof
// Verify takes a blob and a data root and verifies if the
// provided blob was committed to by the data root.
rach-id marked this conversation as resolved.
Show resolved Hide resolved
func (p *Proof) Verify(blob *Blob, dataRoot []byte) (bool, error) {
blobCommitment, err := types.CreateCommitment(ToAppBlobs(blob)[0])
if err != nil {
return false, err
}
if !blob.Commitment.Equal(blobCommitment) {
rach-id marked this conversation as resolved.
Show resolved Hide resolved
return false, fmt.Errorf("%w: generated commitment does not match the provided blob commitment", ErrInvalidProof)
}
rawShares, err := BlobsToShares(blob)
if err != nil {
return false, err
}
return p.VerifyShares(rawShares, blob.namespace, dataRoot)
}

for i, proof := range p {
pNodes := proof.Nodes()
inputNodes := input[i].Nodes()
for i, node := range pNodes {
if !bytes.Equal(node, inputNodes[i]) {
return ErrInvalidProof
}
}
// VerifyShares takes a set of shares, a namespace and a data root, and verifies if the
// provided shares are committed to by the data root.
func (p *Proof) VerifyShares(rawShares [][]byte, namespace share.Namespace, dataRoot []byte) (bool, error) {
// verify the row proof
if !p.RowProof.VerifyProof(dataRoot) {
return false, fmt.Errorf("%w: invalid row root to data root proof", ErrInvalidProof)
}

if proof.Start() != input[i].Start() || proof.End() != input[i].End() {
return ErrInvalidProof
// verify the share proof
ns := append([]byte{namespace.Version()}, namespace.ID()...)
cursor := int32(0)
for i, proof := range p.ShareToRowRootProof {
nmtProof := nmt.NewInclusionProof(
int(proof.Start),
int(proof.End),
proof.Nodes,
true,
)
sharesUsed := proof.End - proof.Start
rach-id marked this conversation as resolved.
Show resolved Hide resolved
if len(rawShares) < int(sharesUsed+cursor) {
return false, fmt.Errorf("%w: invalid number of shares", ErrInvalidProof)
}

if !bytes.Equal(proof.LeafHash(), input[i].LeafHash()) {
return ErrInvalidProof
valid := nmtProof.VerifyInclusion(
consts.NewBaseHashFunc(),
ns,
rawShares[cursor:sharesUsed+cursor],
p.RowProof.RowRoots[i],
)
if !valid {
return false, ErrInvalidProof
}
cursor += sharesUsed
}
return nil
return true, nil
}

// Blob represents any application-specific binary data that anyone can submit to Celestia.
Expand Down Expand Up @@ -172,3 +203,10 @@ func (b *Blob) UnmarshalJSON(data []byte) error {
b.index = blob.Index
return nil
}

// proveRowRootsToDataRoot creates a set of binary merkle proofs for all the
// roots defined by the range [start, end).
func proveRowRootsToDataRoot(roots [][]byte, start, end int) []*merkle.Proof {
_, proofs := merkle.ProofsFromByteSlices(roots)
return proofs[start:end]
}
2 changes: 1 addition & 1 deletion blob/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func BlobsToShares(blobs ...*Blob) ([]share.Share, error) {

// ToAppBlobs converts node's blob type to the blob type from celestia-app.
func ToAppBlobs(blobs ...*Blob) []*apptypes.Blob {
appBlobs := make([]*apptypes.Blob, 0, len(blobs))
appBlobs := make([]*apptypes.Blob, len(blobs))
rach-id marked this conversation as resolved.
Show resolved Hide resolved
for i := range blobs {
appBlobs[i] = &blobs[i].Blob
}
Expand Down
5 changes: 5 additions & 0 deletions blob/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ type parser struct {

// set tries to find the first blob's share by skipping padding shares and
// sets the metadata of the blob(index and length)
//
//nolint:unused
rach-id marked this conversation as resolved.
Show resolved Hide resolved
func (p *parser) set(index int, shrs []shares.Share) ([]shares.Share, error) {
if len(shrs) == 0 {
return nil, errEmptyShares
Expand Down Expand Up @@ -113,6 +115,8 @@ func (p *parser) parse() (*Blob, error) {

// skipPadding iterates through the shares until non-padding share will be found. It guarantees that
// the returned set of shares will start with non-padding share(or empty set of shares).
//
//nolint:unused
func (p *parser) skipPadding(shares []shares.Share) ([]shares.Share, error) {
if len(shares) == 0 {
return nil, errEmptyShares
Expand Down Expand Up @@ -144,6 +148,7 @@ func (p *parser) verify(blob *Blob) bool {
return p.verifyFn(blob)
}

//nolint:unused
func (p *parser) isEmpty() bool {
return p.index == 0 && p.length == 0 && len(p.shares) == 0
}
Expand Down
Loading
Loading