Skip to content

Commit

Permalink
hashing
Browse files Browse the repository at this point in the history
  • Loading branch information
rkapka committed Oct 5, 2022
1 parent 54e519c commit ae78e88
Show file tree
Hide file tree
Showing 12 changed files with 167 additions and 8 deletions.
7 changes: 5 additions & 2 deletions beacon-chain/state/state-native/hasher.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,8 +247,11 @@ func ComputeFieldRootsWithHasher(ctx context.Context, state *BeaconState) ([][]b
fieldRoots[nativetypes.LatestExecutionPayloadHeaderCapella.RealPosition()] = executionPayloadRoot[:]

// Withdrawal queue root.
withdrawalQueueRoot := []byte("stub")
fieldRoots[nativetypes.WithdrawalQueue.RealPosition()] = withdrawalQueueRoot
withdrawalQueueRoot, err := ssz.WithdrawalSliceRoot(hasher, state.withdrawalQueue, fieldparams.WithdrawalQueueLimit)
if err != nil {
return nil, err
}
fieldRoots[nativetypes.WithdrawalQueue.RealPosition()] = withdrawalQueueRoot[:]

// Next withdrawal index root.
nextWithdrawalIndexRoot := make([]byte, 32)
Expand Down
6 changes: 3 additions & 3 deletions beacon-chain/state/state-native/hasher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ func TestComputeFieldRootsWithHasher_Capella(t *testing.T) {
{0x3d, 0xf3, 0x66, 0xd4, 0x12, 0x40, 0x3f, 0x28, 0xeb, 0xe4, 0x19, 0x59, 0xae, 0xab, 0x4d, 0xf3, 0x98, 0x88, 0x7f, 0x1e, 0x58, 0xa, 0x5d, 0xd4, 0xeb, 0xe5, 0x5d, 0x3d, 0x11, 0x70, 0x24, 0x76},
{0xd6, 0x4c, 0xb1, 0xac, 0x61, 0x7, 0x26, 0xbb, 0xd3, 0x27, 0x2a, 0xcd, 0xdd, 0x55, 0xf, 0x2b, 0x6a, 0xe8, 0x1, 0x31, 0x48, 0x66, 0x2f, 0x98, 0x7b, 0x6d, 0x27, 0x69, 0xd9, 0x40, 0xcc, 0x37},
{0x39, 0x29, 0x16, 0xe8, 0x5a, 0xd2, 0xb, 0xbb, 0x1f, 0xef, 0x6a, 0xe0, 0x2d, 0xa6, 0x6a, 0x46, 0x81, 0xba, 0xcf, 0x86, 0xfc, 0x16, 0x22, 0x2a, 0x9b, 0x72, 0x96, 0x71, 0x2b, 0xc7, 0x5b, 0x9d},
{0x73, 0x74, 0x75, 0x62},
{0xf2, 0x10, 0x89, 0x31, 0xdb, 0x23, 0xec, 0x27, 0xbf, 0x60, 0x34, 0x68, 0x23, 0x62, 0xfa, 0xb1, 0xbd, 0x41, 0x71, 0xb9, 0x33, 0x6b, 0x73, 0x1a, 0xe5, 0xa8, 0x28, 0xbd, 0xc8, 0x98, 0xe1, 0x18},
{0x7b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
{0x7b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
}
Expand Down Expand Up @@ -469,8 +469,8 @@ func executionPayloadHeaderCapella() *enginev1.ExecutionPayloadHeaderCapella {

func withdrawal() *enginev1.Withdrawal {
return &enginev1.Withdrawal{
WithdrawalIndex: 0,
WithdrawalIndex: 123,
ExecutionAddress: bytesutil.PadTo([]byte("address"), 20),
Amount: 0,
Amount: 123,
}
}
2 changes: 1 addition & 1 deletion beacon-chain/state/state-native/state_trie.go
Original file line number Diff line number Diff line change
Expand Up @@ -834,7 +834,7 @@ func (b *BeaconState) rootSelector(ctx context.Context, field nativetypes.FieldI
case nativetypes.LatestExecutionPayloadHeaderCapella:
return b.latestExecutionPayloadHeaderCapella.HashTreeRoot()
case nativetypes.WithdrawalQueue:
return [32]byte{}, errors.New("not implemented")
return ssz.WithdrawalSliceRoot(hasher, b.withdrawalQueue, fieldparams.WithdrawalQueueLimit)
case nativetypes.NextWithdrawalIndex:
return ssz.Uint64Root(b.nextWithdrawalIndex), nil
case nativetypes.NextPartialWithdrawalValidatorIndex:
Expand Down
2 changes: 2 additions & 0 deletions config/fieldparams/mainnet.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,6 @@ const (
SlotsPerEpoch = 32 // SlotsPerEpoch defines the number of slots per epoch.
SyncCommitteeAggregationBytesLength = 16 // SyncCommitteeAggregationBytesLength defines the length of sync committee aggregate bytes.
SyncAggregateSyncCommitteeBytesLength = 64 // SyncAggregateSyncCommitteeBytesLength defines the length of sync committee bytes in a sync aggregate.
MaxWithdrawalsPerPayload = 16 // MaxWithdrawalsPerPayloadLength defines the maximum number of withdrawals that can be included in a payload.
WithdrawalQueueLimit = 1099511627776 // WithdrawalQueueLimit defines the maximum number of withdrawals queued in the state.
)
2 changes: 2 additions & 0 deletions config/fieldparams/minimal.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,6 @@ const (
SlotsPerEpoch = 8 // SlotsPerEpoch defines the number of slots per epoch.
SyncCommitteeAggregationBytesLength = 1 // SyncCommitteeAggregationBytesLength defines the sync committee aggregate bytes.
SyncAggregateSyncCommitteeBytesLength = 4 // SyncAggregateSyncCommitteeBytesLength defines the length of sync committee bytes in a sync aggregate.
MaxWithdrawalsPerPayload = 16 // MaxWithdrawalsPerPayloadLength defines the maximum number of withdrawals that can be included in a payload.
WithdrawalQueueLimit = 1099511627776 // WithdrawalQueueLimit defines the maximum number of withdrawals queued in the state.
)
1 change: 1 addition & 0 deletions consensus-types/blocks/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ go_library(
"//config/fieldparams:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/primitives:go_default_library",
"//crypto/hash:go_default_library",
"//encoding/bytesutil:go_default_library",
"//encoding/ssz:go_default_library",
"//proto/engine/v1:go_default_library",
Expand Down
34 changes: 32 additions & 2 deletions consensus-types/blocks/execution.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
fastssz "github.com/prysmaticlabs/fastssz"
fieldparams "github.com/prysmaticlabs/prysm/v3/config/fieldparams"
"github.com/prysmaticlabs/prysm/v3/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v3/crypto/hash"
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/v3/encoding/ssz"
enginev1 "github.com/prysmaticlabs/prysm/v3/proto/engine/v1"
Expand Down Expand Up @@ -141,6 +142,11 @@ func (e executionPayload) Transactions() ([][]byte, error) {
return e.p.Transactions, nil
}

// Withdrawals --
func (e executionPayload) Withdrawals() ([]*enginev1.Withdrawal, error) {
return nil, ErrUnsupportedGetter
}

// executionPayloadHeader is a convenience wrapper around a blinded beacon block body's execution header data structure
// This wrapper allows us to conform to a common interface so that beacon
// blocks for future forks can also be applied across Prysm without issues.
Expand Down Expand Up @@ -267,6 +273,11 @@ func (executionPayloadHeader) Transactions() ([][]byte, error) {
return nil, ErrUnsupportedGetter
}

// Withdrawals --
func (e executionPayloadHeader) Withdrawals() ([]*enginev1.Withdrawal, error) {
return nil, ErrUnsupportedGetter
}

// PayloadToHeader converts `payload` into execution payload header format.
func PayloadToHeader(payload interfaces.ExecutionData) (*enginev1.ExecutionPayloadHeader, error) {
txs, err := payload.Transactions()
Expand Down Expand Up @@ -421,6 +432,11 @@ func (e executionPayloadCapella) Transactions() ([][]byte, error) {
return e.p.Transactions, nil
}

// Withdrawals --
func (e executionPayloadCapella) Withdrawals() ([]*enginev1.Withdrawal, error) {
return e.p.Withdrawals, nil
}

// executionPayloadHeaderCapella is a convenience wrapper around a blinded beacon block body's execution header data structure
// This wrapper allows us to conform to a common interface so that beacon
// blocks for future forks can also be applied across Prysm without issues.
Expand Down Expand Up @@ -544,7 +560,12 @@ func (e executionPayloadHeaderCapella) BlockHash() []byte {

// Transactions --
func (executionPayloadHeaderCapella) Transactions() ([][]byte, error) {
return nil, errUnsupportedExecutionField
return nil, ErrUnsupportedGetter
}

// Withdrawals --
func (e executionPayloadHeaderCapella) Withdrawals() ([]*enginev1.Withdrawal, error) {
return nil, ErrUnsupportedGetter
}

// PayloadToHeaderCapella converts `payload` into execution payload header format.
Expand All @@ -557,6 +578,15 @@ func PayloadToHeaderCapella(payload interfaces.ExecutionData) (*enginev1.Executi
if err != nil {
return nil, err
}
withdrawals, err := payload.Withdrawals()
if err != nil {
return nil, err
}
withdrawalsRoot, err := ssz.WithdrawalSliceRoot(hash.CustomSHA256Hasher(), withdrawals, fieldparams.MaxWithdrawalsPerPayload)
if err != nil {
return nil, err
}

return &enginev1.ExecutionPayloadHeaderCapella{
ParentHash: bytesutil.SafeCopyBytes(payload.ParentHash()),
FeeRecipient: bytesutil.SafeCopyBytes(payload.FeeRecipient()),
Expand All @@ -572,7 +602,7 @@ func PayloadToHeaderCapella(payload interfaces.ExecutionData) (*enginev1.Executi
BaseFeePerGas: bytesutil.SafeCopyBytes(payload.BaseFeePerGas()),
BlockHash: bytesutil.SafeCopyBytes(payload.BlockHash()),
TransactionsRoot: txRoot[:],
WithdrawalsRoot: []byte("stub"),
WithdrawalsRoot: withdrawalsRoot[:],
}, nil
}

Expand Down
1 change: 1 addition & 0 deletions consensus-types/interfaces/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ go_library(
deps = [
"//config/fieldparams:go_default_library",
"//consensus-types/primitives:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//proto/prysm/v1alpha1/validator-client:go_default_library",
"@com_github_pkg_errors//:go_default_library",
Expand Down
2 changes: 2 additions & 0 deletions consensus-types/interfaces/beacon_block.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
ssz "github.com/prysmaticlabs/fastssz"
field_params "github.com/prysmaticlabs/prysm/v3/config/fieldparams"
types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
enginev1 "github.com/prysmaticlabs/prysm/v3/proto/engine/v1"
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
validatorpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1/validator-client"
"google.golang.org/protobuf/proto"
Expand Down Expand Up @@ -89,4 +90,5 @@ type ExecutionData interface {
BaseFeePerGas() []byte
BlockHash() []byte
Transactions() ([][]byte, error)
Withdrawals() ([]*enginev1.Withdrawal, error)
}
3 changes: 3 additions & 0 deletions encoding/ssz/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ go_library(
"//crypto/hash:go_default_library",
"//crypto/hash/htr:go_default_library",
"//encoding/bytesutil:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"@com_github_minio_sha256_simd//:go_default_library",
"@com_github_pkg_errors//:go_default_library",
Expand All @@ -39,6 +40,8 @@ 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",
"//testing/require:go_default_library",
Expand Down
40 changes: 40 additions & 0 deletions encoding/ssz/htrutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
fieldparams "github.com/prysmaticlabs/prysm/v3/config/fieldparams"
"github.com/prysmaticlabs/prysm/v3/crypto/hash"
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
enginev1 "github.com/prysmaticlabs/prysm/v3/proto/engine/v1"
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
)

Expand Down Expand Up @@ -117,6 +118,31 @@ func TransactionsRoot(txs [][]byte) ([32]byte, error) {
return MixInLength(bytesRoot, bytesRootBufRoot), nil
}

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

bytesRoot, err := BitwiseMerkleize(hasher, 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(withdrawals))); 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
}

func transactionRoot(tx []byte) ([32]byte, error) {
hasher := hash.CustomSHA256Hasher()
chunkedRoots, err := PackByChunk([][]byte{tx})
Expand All @@ -137,3 +163,17 @@ func transactionRoot(tx []byte) ([32]byte, error) {
copy(bytesRootBufRoot, bytesRootBuf.Bytes())
return MixInLength(bytesRoot, bytesRootBufRoot), nil
}

func withdrawalRoot(hasher HashFn, w *enginev1.Withdrawal) ([32]byte, error) {
fieldRoots := make([][32]byte, 3)
if w != nil {
indexBuf := make([]byte, 32)
binary.LittleEndian.PutUint64(indexBuf, w.WithdrawalIndex)
fieldRoots[0] = bytesutil.ToBytes32(indexBuf)
fieldRoots[1] = bytesutil.ToBytes32(w.ExecutionAddress)
amountBuf := make([]byte, 8)
binary.LittleEndian.PutUint64(amountBuf, w.Amount)
fieldRoots[2] = bytesutil.ToBytes32(amountBuf)
}
return BitwiseMerkleize(hasher, fieldRoots, uint64(len(fieldRoots)), uint64(len(fieldRoots)))
}
75 changes: 75 additions & 0 deletions encoding/ssz/htrutils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ package ssz_test

import (
"reflect"
"strconv"
"testing"

fieldparams "github.com/prysmaticlabs/prysm/v3/config/fieldparams"
"github.com/prysmaticlabs/prysm/v3/crypto/hash"
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/v3/encoding/ssz"
enginev1 "github.com/prysmaticlabs/prysm/v3/proto/engine/v1"
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v3/testing/assert"
"github.com/prysmaticlabs/prysm/v3/testing/require"
Expand Down Expand Up @@ -123,6 +126,78 @@ func TestTransactionsRoot(t *testing.T) {
}
}

func TestWithdrawalQueueRoot(t *testing.T) {
const limit = 16
tests := []struct {
name string
ws []*enginev1.Withdrawal
want [32]byte
wantErr bool
}{
{
name: "nil",
ws: nil,
want: [32]byte{121, 41, 48, 187, 213, 186, 172, 67, 188, 199, 152, 238, 73, 170, 129, 133, 239, 118, 187, 59, 68, 186, 98, 185, 29, 134, 174, 86, 158, 75, 181, 53},
},
{
name: "empty",
ws: []*enginev1.Withdrawal{},
want: [32]byte{121, 41, 48, 187, 213, 186, 172, 67, 188, 199, 152, 238, 73, 170, 129, 133, 239, 118, 187, 59, 68, 186, 98, 185, 29, 134, 174, 86, 158, 75, 181, 53},
},
{
name: "one",
ws: []*enginev1.Withdrawal{{
WithdrawalIndex: 123,
ExecutionAddress: bytesutil.PadTo([]byte("address"), 20),
Amount: 123,
}},
want: [32]byte{242, 16, 137, 49, 219, 35, 236, 39, 191, 96, 52, 104, 35, 98, 250, 177, 189, 65, 113, 185, 51, 107, 115, 26, 229, 168, 40, 189, 200, 152, 225, 24},
},
{
name: "max withdrawals",
ws: func() []*enginev1.Withdrawal {
var ws []*enginev1.Withdrawal
for i := 0; i < limit; i++ {
ws = append(ws, &enginev1.Withdrawal{
WithdrawalIndex: uint64(i),
ExecutionAddress: bytesutil.PadTo([]byte("address"+strconv.Itoa(i)), 20),
Amount: uint64(i),
})
}
return ws
}(),
want: [32]byte{252, 43, 118, 69, 222, 73, 222, 196, 52, 234, 247, 49, 98, 54, 19, 146, 204, 246, 12, 139, 179, 167, 117, 216, 77, 159, 84, 154, 9, 103, 28, 226},
},
{
name: "exceed max withdrawals",
ws: func() []*enginev1.Withdrawal {
var ws []*enginev1.Withdrawal
for i := 0; i < limit+1; i++ {
ws = append(ws, &enginev1.Withdrawal{
WithdrawalIndex: uint64(i),
ExecutionAddress: bytesutil.PadTo([]byte("address"+strconv.Itoa(i)), 20),
Amount: uint64(i),
})
}
return ws
}(),
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := ssz.WithdrawalSliceRoot(hash.CustomSHA256Hasher(), tt.ws, limit)
if (err != nil) != tt.wantErr {
t.Errorf("error = %v, wantErr = %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("got = %v, want %v", got, tt.want)
}
})
}
}

func TestPackByChunk_SingleList(t *testing.T) {
tests := []struct {
name string
Expand Down

0 comments on commit ae78e88

Please sign in to comment.