Skip to content

Commit

Permalink
refactor: remove data root tuple type
Browse files Browse the repository at this point in the history
  • Loading branch information
rach-id committed Jul 19, 2024
1 parent 9d46758 commit b9d7da2
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 203 deletions.
72 changes: 22 additions & 50 deletions nodebuilder/header/data_root_tuple_root.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,6 @@ func to32PaddedHexBytes(number uint64) ([]byte, error) {
return paddedBytes, nil
}

// dataRootTuple contains the data that will be used to generate the Blobstream data root tuple
// roots. For more information:
// https://github.com/celestiaorg/blobstream-contracts/blob/master/src/DataRootTuple.sol
type dataRootTuple struct {
height uint64
dataRoot [32]byte
}

// encodeDataRootTuple takes a height and a data root, and returns the equivalent of
// `abi.encode(...)` in Ethereum.
// The encoded type is a dataRootTuple, which has the following ABI:
Expand Down Expand Up @@ -137,24 +129,13 @@ func (s *Service) validateDataRootTupleRootRange(ctx context.Context, start, end
return nil
}

// hashDataRootTuples hashes a list of blocks data root tuples, i.e., height, data root and square
// size, then returns their merkle root.
func hashDataRootTuples(tuples []dataRootTuple) ([]byte, error) {
if len(tuples) == 0 {
return nil, fmt.Errorf("cannot hash an empty list of data root tuples")
}
dataRootEncodedTuples := make([][]byte, 0, len(tuples))
for _, tuple := range tuples {
encodedTuple, err := encodeDataRootTuple(
tuple.height,
tuple.dataRoot,
)
if err != nil {
return nil, err
}
dataRootEncodedTuples = append(dataRootEncodedTuples, encodedTuple)
// hashDataRootTuples hashes a list of encoded blocks data root tuples, i.e., height, data root and
// square size, then returns their merkle root.
func hashDataRootTuples(encodedDataRootTuples [][]byte) ([]byte, error) {
if len(encodedDataRootTuples) == 0 {
return nil, fmt.Errorf("cannot hash an empty list of encoded data root tuples")
}
root := merkle.HashFromByteSlices(dataRootEncodedTuples)
root := merkle.HashFromByteSlices(encodedDataRootTuples)
return root, nil
}

Expand All @@ -180,33 +161,23 @@ func (s *Service) validateDataRootInclusionProofRequest(
}

// proveDataRootTuples returns the merkle inclusion proof for a height.
func proveDataRootTuples(tuples []dataRootTuple, height uint64) (*merkle.Proof, error) {
if len(tuples) == 0 {
return nil, fmt.Errorf("cannot prove an empty list of tuples")
// expects the list of encoded data root tuples to be ordered and the heights to be consecutive.
func proveDataRootTuples(encodedDataRootTuples [][]byte, rangeStartHeight, height uint64) (*merkle.Proof, error) {
if len(encodedDataRootTuples) == 0 {
return nil, fmt.Errorf("cannot prove an empty list of encoded data root tuples")
}
if height == 0 {
if height == 0 || rangeStartHeight == 0 {
return nil, ErrHeightZero
}
dataRootEncodedTuples := make([][]byte, 0, len(tuples))
for _, tuple := range tuples {
encodedTuple, err := encodeDataRootTuple(
tuple.height,
tuple.dataRoot,
)
if err != nil {
return nil, err
}
dataRootEncodedTuples = append(dataRootEncodedTuples, encodedTuple)
}
_, proofs := merkle.ProofsFromByteSlices(dataRootEncodedTuples)
return proofs[height-tuples[0].height], nil
_, proofs := merkle.ProofsFromByteSlices(encodedDataRootTuples)
return proofs[height-rangeStartHeight], nil
}

// fetchDataRootTuples takes an end exclusive range of heights and fetches its
// fetchEncodedDataRootTuples takes an end exclusive range of heights and fetches its
// corresponding data root tuples.
// end is not included in the range.
func (s *Service) fetchDataRootTuples(ctx context.Context, start, end uint64) ([]dataRootTuple, error) {
tuples := make([]dataRootTuple, 0, end-start)
func (s *Service) fetchEncodedDataRootTuples(ctx context.Context, start, end uint64) ([][]byte, error) {
encodedDataRootTuples := make([][]byte, 0, end-start)
startHeader, err := s.GetByHeight(ctx, start)
if err != nil {
return nil, err
Expand All @@ -216,10 +187,11 @@ func (s *Service) fetchDataRootTuples(ctx context.Context, start, end uint64) ([
return nil, err
}
for _, header := range headerRange {
tuples = append(tuples, dataRootTuple{
height: header.Height(),
dataRoot: *(*[32]byte)(header.DataHash),
})
encodedDataRootTuple, err := encodeDataRootTuple(header.Height(), *(*[32]byte)(header.DataHash))
if err != nil {
return nil, err
}
encodedDataRootTuples = append(encodedDataRootTuples, encodedDataRootTuple)
}
return tuples, nil
return encodedDataRootTuples, nil
}
10 changes: 5 additions & 5 deletions nodebuilder/header/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,12 +157,12 @@ func (s *Service) GetDataRootTupleRoot(ctx context.Context, start, end uint64) (
return nil, err
}
log.Debugw("fetching the data root tuples", "start", start, "end", end)
tuples, err := s.fetchDataRootTuples(ctx, start, end)
encodedDataRootTuples, err := s.fetchEncodedDataRootTuples(ctx, start, end)
if err != nil {
return nil, err
}
log.Debugw("hashing the data root tuples", "start", start, "end", end)
root, err := hashDataRootTuples(tuples)
root, err := hashDataRootTuples(encodedDataRootTuples)
if err != nil {
return nil, err
}
Expand All @@ -187,18 +187,18 @@ func (s *Service) GetDataRootTupleInclusionProof(
"height",
height,
)
err := s.validateDataRootInclusionProofRequest(ctx, uint64(height), start, end)
err := s.validateDataRootInclusionProofRequest(ctx, height, start, end)
if err != nil {
return nil, err
}
log.Debugw("fetching the data root tuples", "start", start, "end", end)

tuples, err := s.fetchDataRootTuples(ctx, start, end)
encodedDataRootTuples, err := s.fetchEncodedDataRootTuples(ctx, start, end)
if err != nil {
return nil, err
}
log.Debugw("proving the data root tuples", "start", start, "end", end)
proof, err := proveDataRootTuples(tuples, height)
proof, err := proveDataRootTuples(encodedDataRootTuples, start, height)
if err != nil {
return nil, err
}
Expand Down
170 changes: 22 additions & 148 deletions nodebuilder/header/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,22 +141,17 @@ func TestEncodeDataRootTuple(t *testing.T) {

func TestHashDataRootTuples(t *testing.T) {
tests := map[string]struct {
tuples []dataRootTuple
tuples [][]byte
expectedHash []byte
expectErr bool
}{
"empty tuples list": {tuples: nil, expectErr: true},
"valid list of data root tuples": {
tuples: []dataRootTuple{
{
height: 1,
dataRoot: [32]byte{0x1},
},
{
height: 2,
dataRoot: [32]byte{0x2},
},
},
"valid list of encoded data root tuples": {
tuples: func() [][]byte {
tuple1, _ := encodeDataRootTuple(1, [32]byte{0x1})
tuple2, _ := encodeDataRootTuple(2, [32]byte{0x2})
return [][]byte{tuple1, tuple2}
}(),
expectedHash: func() []byte {
tuple1, _ := encodeDataRootTuple(1, [32]byte{0x1})
tuple2, _ := encodeDataRootTuple(2, [32]byte{0x2})
Expand All @@ -181,146 +176,25 @@ func TestHashDataRootTuples(t *testing.T) {

func TestProveDataRootTuples(t *testing.T) {
tests := map[string]struct {
tuples []dataRootTuple
tuples [][]byte
height uint64
rangeStart uint64
expectedProof merkle.Proof
expectErr bool
}{
"empty tuples list": {tuples: nil, expectErr: true},
"non consecutive list of tuples at the beginning": {
tuples: []dataRootTuple{
{
height: 1,
dataRoot: [32]byte{0x1},
},
{
height: 3,
dataRoot: [32]byte{0x2},
},
{
height: 4,
dataRoot: [32]byte{0x4},
},
},
expectErr: true,
},
"non consecutive list of tuples in the middle": {
tuples: []dataRootTuple{
{
height: 1,
dataRoot: [32]byte{0x1},
},
{
height: 2,
dataRoot: [32]byte{0x2},
},
{
height: 3,
dataRoot: [32]byte{0x2},
},
{
height: 5,
dataRoot: [32]byte{0x4},
},
{
height: 6,
dataRoot: [32]byte{0x5},
},
},
expectErr: true,
},
"non consecutive list of tuples at the end": {
tuples: []dataRootTuple{
{
height: 1,
dataRoot: [32]byte{0x1},
},
{
height: 2,
dataRoot: [32]byte{0x2},
},
{
height: 4,
dataRoot: [32]byte{0x4},
},
},
expectErr: true,
},
"duplicate height at the beginning": {
tuples: []dataRootTuple{
{
height: 1,
dataRoot: [32]byte{0x1},
},
{
height: 1,
dataRoot: [32]byte{0x1},
},
{
height: 4,
dataRoot: [32]byte{0x4},
},
},
expectErr: true,
},
"duplicate height in the middle": {
tuples: []dataRootTuple{
{
height: 1,
dataRoot: [32]byte{0x1},
},
{
height: 2,
dataRoot: [32]byte{0x2},
},
{
height: 2,
dataRoot: [32]byte{0x2},
},
{
height: 3,
dataRoot: [32]byte{0x3},
},
},
expectErr: true,
},
"duplicate height at the end": {
tuples: []dataRootTuple{
{
height: 1,
dataRoot: [32]byte{0x1},
},
{
height: 2,
dataRoot: [32]byte{0x2},
},
{
height: 2,
dataRoot: [32]byte{0x2},
},
},
expectErr: true,
},
"empty tuples list": {tuples: [][]byte{{0x1}}, expectErr: true},
"start height == 0": {tuples: [][]byte{{0x1}}, expectErr: true},
"range start height == 0": {tuples: [][]byte{{0x1}}, expectErr: true},
"valid proof": {
height: 3,
tuples: []dataRootTuple{
{
height: 1,
dataRoot: [32]byte{0x1},
},
{
height: 2,
dataRoot: [32]byte{0x2},
},
{
height: 3,
dataRoot: [32]byte{0x3},
},
{
height: 4,
dataRoot: [32]byte{0x4},
},
},
height: 3,
rangeStart: 1,
tuples: func() [][]byte {
encodedTuple1, _ := encodeDataRootTuple(1, [32]byte{0x1})
encodedTuple2, _ := encodeDataRootTuple(2, [32]byte{0x2})
encodedTuple3, _ := encodeDataRootTuple(3, [32]byte{0x3})
encodedTuple4, _ := encodeDataRootTuple(4, [32]byte{0x4})
return [][]byte{encodedTuple1, encodedTuple2, encodedTuple3, encodedTuple4}
}(),
expectedProof: func() merkle.Proof {
encodedTuple1, _ := encodeDataRootTuple(1, [32]byte{0x1})
encodedTuple2, _ := encodeDataRootTuple(2, [32]byte{0x2})
Expand All @@ -334,7 +208,7 @@ func TestProveDataRootTuples(t *testing.T) {

for name, tc := range tests {
t.Run(name, func(t *testing.T) {
result, err := proveDataRootTuples(tc.tuples, tc.height)
result, err := proveDataRootTuples(tc.tuples, tc.rangeStart, tc.height)
if tc.expectErr {
assert.Error(t, err)
} else {
Expand Down

0 comments on commit b9d7da2

Please sign in to comment.