-
Notifications
You must be signed in to change notification settings - Fork 294
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor!: move all blob share commitment code to the inclusion packa…
…ge (#2770) ## Overview We currently have code relating to creating and proving commitment across the inclusion, x/blob/types, and shares package. This api breaking refactor moves all of the commitment code to the inclusion package. The reasoning behind this is that it will eventually allow for users to import commitment creation and proving code while not having to import the blob module, tendermint, or the sdk. The reasoning behind keeping it in the inclusion instead of shares is that entire reason for making a commitment is to prove inclusion. So the name seems to fit better. The reasoning behind note keeping it in the blob package was that it was difficult to not have an import cycle since this logic requires the share splitting logic for blobs. ## Checklist - [x] New and updated code has appropriate documentation - [x] New and updated code has new and/or updated testing - [x] Required CI checks are passing - [x] Visual proof for any user facing features like CLI or documentation updates - [ ] Linked issues closed with keywords
- Loading branch information
1 parent
36ecb01
commit 73942bf
Showing
13 changed files
with
279 additions
and
225 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
package inclusion | ||
|
||
import ( | ||
"crypto/sha256" | ||
|
||
"github.com/celestiaorg/celestia-app/pkg/appconsts" | ||
"github.com/celestiaorg/celestia-app/pkg/blob" | ||
appns "github.com/celestiaorg/celestia-app/pkg/namespace" | ||
appshares "github.com/celestiaorg/celestia-app/pkg/shares" | ||
"github.com/celestiaorg/nmt" | ||
"github.com/tendermint/tendermint/crypto/merkle" | ||
) | ||
|
||
// CreateCommitment generates the share commitment for a given blob. | ||
// See [data square layout rationale] and [blob share commitment rules]. | ||
// | ||
// [data square layout rationale]: ../../specs/src/specs/data_square_layout.md | ||
// [blob share commitment rules]: ../../specs/src/specs/data_square_layout.md#blob-share-commitment-rules | ||
func CreateCommitment(blob *blob.Blob) ([]byte, error) { | ||
if err := blob.Validate(); err != nil { | ||
return nil, err | ||
} | ||
namespace := blob.Namespace() | ||
|
||
shares, err := appshares.SplitBlobs(blob) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
// the commitment is the root of a merkle mountain range with max tree size | ||
// determined by the number of roots required to create a share commitment | ||
// over that blob. The size of the tree is only increased if the number of | ||
// subtree roots surpasses a constant threshold. | ||
subTreeWidth := SubTreeWidth(len(shares), appconsts.DefaultSubtreeRootThreshold) | ||
treeSizes, err := MerkleMountainRangeSizes(uint64(len(shares)), uint64(subTreeWidth)) | ||
if err != nil { | ||
return nil, err | ||
} | ||
leafSets := make([][][]byte, len(treeSizes)) | ||
cursor := uint64(0) | ||
for i, treeSize := range treeSizes { | ||
leafSets[i] = appshares.ToBytes(shares[cursor : cursor+treeSize]) | ||
cursor = cursor + treeSize | ||
} | ||
|
||
// create the commitments by pushing each leaf set onto an nmt | ||
subTreeRoots := make([][]byte, len(leafSets)) | ||
for i, set := range leafSets { | ||
// create the nmt todo(evan) use nmt wrapper | ||
tree := nmt.New(sha256.New(), nmt.NamespaceIDSize(appns.NamespaceSize), nmt.IgnoreMaxNamespace(true)) | ||
for _, leaf := range set { | ||
// the namespace must be added again here even though it is already | ||
// included in the leaf to ensure that the hash will match that of | ||
// the nmt wrapper (pkg/wrapper). Each namespace is added to keep | ||
// the namespace in the share, and therefore the parity data, while | ||
// also allowing for the manual addition of the parity namespace to | ||
// the parity data. | ||
nsLeaf := make([]byte, 0) | ||
nsLeaf = append(nsLeaf, namespace.Bytes()...) | ||
nsLeaf = append(nsLeaf, leaf...) | ||
|
||
err = tree.Push(nsLeaf) | ||
if err != nil { | ||
return nil, err | ||
} | ||
} | ||
// add the root | ||
root, err := tree.Root() | ||
if err != nil { | ||
return nil, err | ||
} | ||
subTreeRoots[i] = root | ||
} | ||
return merkle.HashFromByteSlices(subTreeRoots), nil | ||
} | ||
|
||
func CreateCommitments(blobs []*blob.Blob) ([][]byte, error) { | ||
commitments := make([][]byte, len(blobs)) | ||
for i, blob := range blobs { | ||
commitment, err := CreateCommitment(blob) | ||
if err != nil { | ||
return nil, err | ||
} | ||
commitments[i] = commitment | ||
} | ||
return commitments, nil | ||
} | ||
|
||
// MerkleMountainRangeSizes returns the sizes (number of leaf nodes) of the | ||
// trees in a merkle mountain range constructed for a given totalSize and | ||
// maxTreeSize. | ||
// | ||
// https://docs.grin.mw/wiki/chain-state/merkle-mountain-range/ | ||
// https://github.com/opentimestamps/opentimestamps-server/blob/master/doc/merkle-mountain-range.md | ||
func MerkleMountainRangeSizes(totalSize, maxTreeSize uint64) ([]uint64, error) { | ||
var treeSizes []uint64 | ||
|
||
for totalSize != 0 { | ||
switch { | ||
case totalSize >= maxTreeSize: | ||
treeSizes = append(treeSizes, maxTreeSize) | ||
totalSize = totalSize - maxTreeSize | ||
case totalSize < maxTreeSize: | ||
treeSize, err := appshares.RoundDownPowerOfTwo(totalSize) | ||
if err != nil { | ||
return treeSizes, err | ||
} | ||
treeSizes = append(treeSizes, treeSize) | ||
totalSize = totalSize - treeSize | ||
} | ||
} | ||
|
||
return treeSizes, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
package inclusion_test | ||
|
||
import ( | ||
"bytes" | ||
"testing" | ||
|
||
"github.com/celestiaorg/celestia-app/pkg/appconsts" | ||
"github.com/celestiaorg/celestia-app/pkg/blob" | ||
"github.com/celestiaorg/celestia-app/pkg/inclusion" | ||
appns "github.com/celestiaorg/celestia-app/pkg/namespace" | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func Test_MerkleMountainRangeHeights(t *testing.T) { | ||
type test struct { | ||
totalSize uint64 | ||
squareSize uint64 | ||
expected []uint64 | ||
} | ||
tests := []test{ | ||
{ | ||
totalSize: 11, | ||
squareSize: 4, | ||
expected: []uint64{4, 4, 2, 1}, | ||
}, | ||
{ | ||
totalSize: 2, | ||
squareSize: 64, | ||
expected: []uint64{2}, | ||
}, | ||
{ | ||
totalSize: 64, | ||
squareSize: 8, | ||
expected: []uint64{8, 8, 8, 8, 8, 8, 8, 8}, | ||
}, | ||
// Height | ||
// 3 x x | ||
// / \ / \ | ||
// / \ / \ | ||
// / \ / \ | ||
// / \ / \ | ||
// 2 x x x x | ||
// / \ / \ / \ / \ | ||
// 1 x x x x x x x x x | ||
// / \ / \ / \ / \ / \ / \ / \ / \ / \ | ||
// 0 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | ||
{ | ||
totalSize: 19, | ||
squareSize: 8, | ||
expected: []uint64{8, 8, 2, 1}, | ||
}, | ||
} | ||
for _, tt := range tests { | ||
res, err := inclusion.MerkleMountainRangeSizes(tt.totalSize, tt.squareSize) | ||
require.NoError(t, err) | ||
assert.Equal(t, tt.expected, res) | ||
} | ||
} | ||
|
||
// TestCreateCommitment will fail if a change is made to share encoding or how | ||
// the commitment is calculated. If this is the case, the expected commitment | ||
// bytes will need to be updated. | ||
func TestCreateCommitment(t *testing.T) { | ||
ns1 := appns.MustNewV0(bytes.Repeat([]byte{0x1}, appns.NamespaceVersionZeroIDSize)) | ||
|
||
type test struct { | ||
name string | ||
namespace appns.Namespace | ||
blob []byte | ||
expected []byte | ||
expectErr bool | ||
shareVersion uint8 | ||
} | ||
tests := []test{ | ||
{ | ||
name: "blob of 3 shares succeeds", | ||
namespace: ns1, | ||
blob: bytes.Repeat([]byte{0xFF}, 3*appconsts.ShareSize), | ||
expected: []byte{0x3b, 0x9e, 0x78, 0xb6, 0x64, 0x8e, 0xc1, 0xa2, 0x41, 0x92, 0x5b, 0x31, 0xda, 0x2e, 0xcb, 0x50, 0xbf, 0xc6, 0xf4, 0xad, 0x55, 0x2d, 0x32, 0x79, 0x92, 0x8c, 0xa1, 0x3e, 0xbe, 0xba, 0x8c, 0x2b}, | ||
shareVersion: appconsts.ShareVersionZero, | ||
}, | ||
{ | ||
name: "blob with unsupported share version should return error", | ||
namespace: ns1, | ||
blob: bytes.Repeat([]byte{0xFF}, 12*appconsts.ShareSize), | ||
expectErr: true, | ||
shareVersion: uint8(1), // unsupported share version | ||
}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
blob := &blob.Blob{ | ||
NamespaceId: tt.namespace.ID, | ||
Data: tt.blob, | ||
ShareVersion: uint32(tt.shareVersion), | ||
NamespaceVersion: uint32(tt.namespace.Version), | ||
} | ||
res, err := inclusion.CreateCommitment(blob) | ||
if tt.expectErr { | ||
assert.Error(t, err) | ||
return | ||
} | ||
assert.NoError(t, err) | ||
assert.Equal(t, tt.expected, res) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.