Skip to content

Commit

Permalink
Electra: HTR util for DepositReceipt and ExecutionLayerWithdrawalRequ…
Browse files Browse the repository at this point in the history
…est (#13924)

* Electra: HTR utils for DepositReceipts and ExecutionLayerWithdrawalRequests

* Tests for HTR utils
  • Loading branch information
prestonvanloon authored Apr 26, 2024
1 parent 0e5c2bd commit bf5e667
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 0 deletions.
1 change: 1 addition & 0 deletions encoding/ssz/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ go_test(
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//crypto/hash:go_default_library",
"//encoding/bytesutil:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//testing/assert:go_default_library",
Expand Down
50 changes: 50 additions & 0 deletions encoding/ssz/htrutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,56 @@ func WithdrawalSliceRoot(withdrawals []*enginev1.Withdrawal, limit uint64) ([32]
return MixInLength(bytesRoot, bytesRootBufRoot), nil
}

// DepositReceiptSliceRoot computes the HTR of a slice of deposit receipts.
// The limit parameter is used as input to the bitwise merkleization algorithm.
func DepositReceiptSliceRoot(depositReceipts []*enginev1.DepositReceipt, limit uint64) ([32]byte, error) {
roots := make([][32]byte, len(depositReceipts))
for i := 0; i < len(depositReceipts); i++ {
r, err := depositReceipts[i].HashTreeRoot()
if err != nil {
return [32]byte{}, err
}
roots[i] = r
}

bytesRoot, err := BitwiseMerkleize(roots, uint64(len(roots)), limit)
if err != nil {
return [32]byte{}, errors.Wrap(err, "could not compute merkleization")
}
bytesRootBuf := new(bytes.Buffer)
if err := binary.Write(bytesRootBuf, binary.LittleEndian, uint64(len(depositReceipts))); err != nil {
return [32]byte{}, errors.Wrap(err, "could not marshal length")
}
bytesRootBufRoot := make([]byte, 32)
copy(bytesRootBufRoot, bytesRootBuf.Bytes())
return MixInLength(bytesRoot, bytesRootBufRoot), nil
}

// WithdrawalRequestSliceRoot computes the HTR of a slice of withdrawal requests from the EL.
// The limit parameter is used as input to the bitwise merkleization algorithm.
func WithdrawalRequestSliceRoot(withdrawalRequests []*enginev1.ExecutionLayerWithdrawalRequest, limit uint64) ([32]byte, error) {
roots := make([][32]byte, len(withdrawalRequests))
for i := 0; i < len(withdrawalRequests); i++ {
r, err := withdrawalRequests[i].HashTreeRoot()
if err != nil {
return [32]byte{}, err
}
roots[i] = r
}

bytesRoot, err := BitwiseMerkleize(roots, uint64(len(roots)), limit)
if err != nil {
return [32]byte{}, errors.Wrap(err, "could not compute merkleization")
}
bytesRootBuf := new(bytes.Buffer)
if err := binary.Write(bytesRootBuf, binary.LittleEndian, uint64(len(withdrawalRequests))); err != nil {
return [32]byte{}, errors.Wrap(err, "could not marshal length")
}
bytesRootBufRoot := make([]byte, 32)
copy(bytesRootBufRoot, bytesRootBuf.Bytes())
return MixInLength(bytesRoot, bytesRootBufRoot), nil
}

// ByteSliceRoot is a helper func to merkleize an arbitrary List[Byte, N]
// this func runs Chunkify + MerkleizeVector
// max length is dividable by 32 ( root length )
Expand Down
73 changes: 73 additions & 0 deletions encoding/ssz/htrutils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"testing"

fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/v5/encoding/ssz"
enginev1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
Expand Down Expand Up @@ -279,3 +280,75 @@ func TestWithrawalSliceRoot(t *testing.T) {
})
}
}

func TestDepositReceiptSliceRoot(t *testing.T) {
tests := []struct {
name string
input []*enginev1.DepositReceipt
limit uint64
want [32]byte
}{
{
name: "empty",
input: make([]*enginev1.DepositReceipt, 0),
want: [32]byte{0xf5, 0xa5, 0xfd, 0x42, 0xd1, 0x6a, 0x20, 0x30, 0x27, 0x98, 0xef, 0x6e, 0xd3, 0x9, 0x97, 0x9b, 0x43, 0x0, 0x3d, 0x23, 0x20, 0xd9, 0xf0, 0xe8, 0xea, 0x98, 0x31, 0xa9, 0x27, 0x59, 0xfb, 0x4b},
},
{
name: "non-empty",
input: []*enginev1.DepositReceipt{
{
Pubkey: bytesutil.PadTo([]byte{0x01, 0x02}, 48),
WithdrawalCredentials: bytesutil.PadTo([]byte{0x03, 0x04}, 32),
Amount: 5,
Signature: bytesutil.PadTo([]byte{0x06, 0x07}, 96),
Index: 8,
},
},
limit: 16,
want: [32]byte{0x34, 0xe3, 0x76, 0x5, 0xe5, 0x12, 0xe4, 0x75, 0x14, 0xf6, 0x72, 0x1c, 0x56, 0x5a, 0xa7, 0xf8, 0x8d, 0xaf, 0x84, 0xb7, 0xd7, 0x3e, 0xe6, 0x5f, 0x3f, 0xb1, 0x9f, 0x41, 0xf0, 0x10, 0x2b, 0xe6},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := ssz.DepositReceiptSliceRoot(tt.input, tt.limit)
require.NoError(t, err)
require.DeepSSZEqual(t, tt.want, got)
})
}
}

func TestWithdrawalRequestSliceRoot(t *testing.T) {
tests := []struct {
name string
input []*enginev1.ExecutionLayerWithdrawalRequest
limit uint64
want [32]byte
}{
{
name: "empty",
input: make([]*enginev1.ExecutionLayerWithdrawalRequest, 0),
want: [32]byte{0xf5, 0xa5, 0xfd, 0x42, 0xd1, 0x6a, 0x20, 0x30, 0x27, 0x98, 0xef, 0x6e, 0xd3, 0x9, 0x97, 0x9b, 0x43, 0x0, 0x3d, 0x23, 0x20, 0xd9, 0xf0, 0xe8, 0xea, 0x98, 0x31, 0xa9, 0x27, 0x59, 0xfb, 0x4b},
},
{
name: "non-empty",
input: []*enginev1.ExecutionLayerWithdrawalRequest{
{
SourceAddress: bytesutil.PadTo([]byte{0x01, 0x02}, 20),
ValidatorPubkey: bytesutil.PadTo([]byte{0x03, 0x04}, 48),
Amount: 5,
},
},
limit: 16,
want: [32]byte{0xa8, 0xab, 0xb2, 0x20, 0xe6, 0xd6, 0x5a, 0x7e, 0x56, 0x60, 0xe4, 0x9d, 0xae, 0x36, 0x17, 0x3d, 0x8b, 0xd, 0xde, 0x28, 0x96, 0x5, 0x82, 0x72, 0x18, 0xda, 0xc7, 0x5a, 0x53, 0xe0, 0x35, 0xf7},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := ssz.WithdrawalRequestSliceRoot(tt.input, tt.limit)
require.NoError(t, err)
require.DeepSSZEqual(t, tt.want, got)
})
}
}

0 comments on commit bf5e667

Please sign in to comment.