Skip to content

Commit

Permalink
feat(ssz): Implement basic generalized index stuff (#837)
Browse files Browse the repository at this point in the history
* bet

* bet

* bet

* bet

* bet

* bet

* bet

* bet

* bet

* bet

* bet

* bet

* bet

* bet

* bet

* bet

* bet

* bet

* bet

* bet

* bet

* bet

* bet

* bet

* bet

* bet

* bet

* bet

* bet

* bet

* bet

* bet
  • Loading branch information
itsdevbear committed Apr 22, 2024
1 parent 40c9b10 commit 2d97f6a
Show file tree
Hide file tree
Showing 11 changed files with 588 additions and 30 deletions.
1 change: 1 addition & 0 deletions beacond/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ cosmossdk.io/client/v2 v2.0.0-20240412212305-037cf98f7eea/go.mod h1:ss8ttONeqslO
cosmossdk.io/collections v0.4.0 h1:PFmwj2W8szgpD5nOd8GWH6AbYNi1f2J6akWXJ7P5t9s=
cosmossdk.io/collections v0.4.0/go.mod h1:oa5lUING2dP+gdDquow+QjlF45eL1t4TJDypgGd+tv0=
cosmossdk.io/core v0.12.1-0.20240421190920-f31a6a3024ba h1:OUspyF1W6EKwdi48i49mzHsSfzDxa0vCz6rX4CuBT4E=
cosmossdk.io/core v0.12.1-0.20240421190920-f31a6a3024ba/go.mod h1:8mgmE18tDaLNGrg8G3j7+oatgjPZsYYb6lmelFo7pMw=
cosmossdk.io/depinject v1.0.0-alpha.4.0.20240221095859-541df89f2bb4 h1:tumGWD0L/gZSgnWAkpI92QzTkaStuTKyVYTuARhxHJ4=
cosmossdk.io/depinject v1.0.0-alpha.4.0.20240221095859-541df89f2bb4/go.mod h1:V+6XKqoP4cOzR4OKk3TuapjecnoQ6s8XJeh9Bo05FsE=
cosmossdk.io/errors v1.0.1 h1:bzu+Kcr0kS/1DuPBtUFdWjzLqyUuCiyHjyJB6srBV/0=
Expand Down
25 changes: 20 additions & 5 deletions mod/da/types/sidecar.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,26 @@ func BuildBlobSidecar(

// HasValidInclusionProof verifies the inclusion proof of the
// blob in the beacon body.
func (b *BlobSidecar) HasValidInclusionProof(kzgOffset uint64) bool {
return merkle.VerifyProof(
b.BeaconBlockHeader.BodyRoot,
b.KzgCommitment.ToHashChunks()[0],
kzgOffset+b.Index,
func (b *BlobSidecar) HasValidInclusionProof(
kzgOffset uint64,
) bool {
// Calculate the hash tree root of the KZG commitment.
leaf, err := b.KzgCommitment.HashTreeRoot()
if err != nil {
return false
}

gIndex := kzgOffset + b.Index

// Verify the inclusion proof.
return merkle.IsValidMerkleBranch(
leaf,
b.InclusionProof,
//#nosec:G701 // safe.
uint8(
len(b.InclusionProof),
), // TODO: use KZG_INCLUSION_PROOF_DEPTH calculation.
gIndex,
b.BeaconBlockHeader.BodyRoot,
)
}
4 changes: 3 additions & 1 deletion mod/da/types/sidecars.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ func (bs *BlobSidecars) ValidateBlockRoots() error {
}

// VerifyInclusionProofs verifies the inclusion proofs for all sidecars.
func (bs *BlobSidecars) VerifyInclusionProofs(kzgOffset uint64) error {
func (bs *BlobSidecars) VerifyInclusionProofs(
kzgOffset uint64,
) error {
return errors.Join(iter.Map(
bs.Sidecars,
func(sidecar **BlobSidecar) error {
Expand Down
4 changes: 3 additions & 1 deletion mod/da/verifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,9 @@ func (bv *BlobVerifier) VerifyBlobs(
g.Go(func() error {
// TODO: KZGOffset needs to be configurable and not
// passed in.
return bv.VerifyInclusionProofs(sidecars, kzgOffset)
return bv.VerifyInclusionProofs(
sidecars, kzgOffset,
)
})

// Verify the KZG proofs on the blobs concurrently.
Expand Down
6 changes: 4 additions & 2 deletions mod/primitives/math/u256_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,8 @@ func TestU256L_UnmarshalSSZ(t *testing.T) {
if tt.err != nil {
require.ErrorIs(t, err, tt.err)
} else {
expected, err := math.NewU256L(tt.expected)
var expected math.U256L
expected, err = math.NewU256L(tt.expected)
require.NoError(t, err)
require.Equal(t, expected, u)
}
Expand All @@ -206,7 +207,8 @@ func TestNewU256L_SilentTruncation(t *testing.T) {
13, 14, 15, 16, 16, 17, 18, 19, 20, 21, 22,
23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34},
[32]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
14, 15, 16, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}},
14, 15, 16, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31}},
}

for _, tc := range testCases {
Expand Down
50 changes: 29 additions & 21 deletions mod/primitives/math/u64.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,14 @@ func (u U64) Unwrap() uint64 {
return uint64(u)
}

// NextPowerOfTwo returns the next power of two greater than or equal to the.
// Get the power of 2 for given input, or the closest higher power of 2 if the
// input is not a power of 2. Commonly used for "how many nodes do I need for a
// bottom tree layer fitting x elements?"
// Example: 0->1, 1->1, 2->2, 3->4, 4->4, 5->8, 6->8, 7->8, 8->8, 9->16.
//
//nolint:mnd // powers of 2.
// https://github.com/ethereum/consensus-specs/blob/dev/ssz/merkle-proofs.md#helper-functions
//
//nolint:mnd,lll // powers of 2.
func (u U64) NextPowerOfTwo() U64 {
u--
u |= u >> 1
Expand All @@ -143,33 +148,26 @@ func (u U64) NextPowerOfTwo() U64 {
return u
}

// Get the power of 2 for given input, or the closest higher power of 2 if the
// input is not a power of 2. Commonly used for "how many nodes do I need for a
// bottom tree layer fitting x elements?"
// Example: 0->1, 1->1, 2->2, 3->4, 4->4, 5->8, 6->8, 7->8, 8->8, 9->16.
//
//nolint:mnd // From Ethereum 2.0 spec.
func (u U64) IPow2Ceil() U64 {
if u <= 1 {
return 1
} else if u == 2 {
return 2
}
return 2 * ((u + 1) / 2).IPow2Ceil()
}

// Get the power of 2 for given input, or the closest lower power of 2 if the
// input is not a power of 2. The zero case is a placeholder and not used for
// math with generalized indices. Commonly used for "what power of two makes up
// the root bit of the generalized index?"
// Example: 0->1, 1->1, 2->2, 3->2, 4->4, 5->4, 6->4, 7->4, 8->8, 9->8.
//
//nolint:mnd // From Ethereum 2.0 spec.
func (u U64) IPow2Floor() U64 {
if u <= 1 {
// https://github.com/ethereum/consensus-specs/blob/dev/ssz/merkle-proofs.md#helper-functions
//
//nolint:mnd,lll // From Ethereum 2.0 spec.
func (u U64) PrevPowerOfTwo() U64 {
if u == 0 {
return 1
}
return 2 * (u / 2).IPow2Floor()
u |= u >> 1
u |= u >> 2
u |= u >> 4
u |= u >> 8
u |= u >> 16
u |= u >> 32
return u - (u >> 1)
}

// ILog2Ceil returns the ceiling of the base 2 logarithm of the U64.
Expand All @@ -182,6 +180,16 @@ func (u U64) ILog2Ceil() uint8 {
return U64NumBits - uint8(bits.LeadingZeros64(uint64(u-1)))
}

// ILog2Floor returns the floor of the base 2 logarithm of the U64.
func (u U64) ILog2Floor() uint8 {
// Log2(0) is undefined, should we panic?
if u == 0 {
return 0
}
//#nosec:G701 // we handle the case of u == 0 above, so this is safe.
return U64NumBits - uint8(bits.LeadingZeros64(uint64(u)))
}

// type U64Vector[U ~uint64] []U

// func (v U64Vector[U]) HashTreeRoot() ([32]byte, error) {
Expand Down
65 changes: 65 additions & 0 deletions mod/primitives/math/u64_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,71 @@ func TestU64_ILog2Ceil(t *testing.T) {
})
}
}
func TestU64_PrevPowerOfTwo(t *testing.T) {
tests := []struct {
name string
value math.U64
expected math.U64
}{
{
name: "zero",
value: math.U64(0),
expected: 1,
},
{
name: "one",
value: math.U64(1),
expected: 1,
},
{
name: "two",
value: math.U64(2),
expected: 2,
},
{
name: "three",
value: math.U64(3),
expected: 2,
},
{
name: "four",
value: math.U64(4),
expected: 4,
},
{
name: "five",
value: math.U64(5),
expected: 4,
},
{
name: "eight",
value: math.U64(8),
expected: 8,
},
{
name: "nine",
value: math.U64(9),
expected: 8,
},
{
name: "thirty-two",
value: math.U64(32),
expected: 32,
},
{
name: "thirty-three",
value: math.U64(33),
expected: 32,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := tt.value.PrevPowerOfTwo()
require.Equal(t, tt.expected, result)
})
}
}

// func TestU64List_HashTreeRoot(t *testing.T) {
// tests := []struct {
Expand Down
Loading

0 comments on commit 2d97f6a

Please sign in to comment.