diff --git a/beacon-chain/state/stateutil/block_header_root.go b/beacon-chain/state/stateutil/block_header_root.go index 4f23c3131a..87f9098acb 100644 --- a/beacon-chain/state/stateutil/block_header_root.go +++ b/beacon-chain/state/stateutil/block_header_root.go @@ -13,22 +13,22 @@ import ( // a BeaconBlockHeader struct according to the Ethereum // Simple Serialize specification. func BlockHeaderRoot(header *ethpb.BeaconBlockHeader) ([32]byte, error) { - fieldRoots := make([][]byte, 5) + fieldRoots := make([][32]byte, 5) if header != nil { headerSlotBuf := make([]byte, 8) binary.LittleEndian.PutUint64(headerSlotBuf, uint64(header.Slot)) headerSlotRoot := bytesutil.ToBytes32(headerSlotBuf) - fieldRoots[0] = headerSlotRoot[:] + fieldRoots[0] = headerSlotRoot proposerIdxBuf := make([]byte, 8) binary.LittleEndian.PutUint64(proposerIdxBuf, uint64(header.ProposerIndex)) proposerIndexRoot := bytesutil.ToBytes32(proposerIdxBuf) - fieldRoots[1] = proposerIndexRoot[:] + fieldRoots[1] = proposerIndexRoot parentRoot := bytesutil.ToBytes32(header.ParentRoot) - fieldRoots[2] = parentRoot[:] + fieldRoots[2] = parentRoot stateRoot := bytesutil.ToBytes32(header.StateRoot) - fieldRoots[3] = stateRoot[:] + fieldRoots[3] = stateRoot bodyRoot := bytesutil.ToBytes32(header.BodyRoot) - fieldRoots[4] = bodyRoot[:] + fieldRoots[4] = bodyRoot } return ssz.BitwiseMerkleize(hash.CustomSHA256Hasher(), fieldRoots, uint64(len(fieldRoots)), uint64(len(fieldRoots))) } diff --git a/beacon-chain/state/stateutil/eth1_root.go b/beacon-chain/state/stateutil/eth1_root.go index 32cf23362f..aaebac8bd6 100644 --- a/beacon-chain/state/stateutil/eth1_root.go +++ b/beacon-chain/state/stateutil/eth1_root.go @@ -18,21 +18,20 @@ func Eth1DataRootWithHasher(hasher ssz.HashFn, eth1Data *ethpb.Eth1Data) ([32]by return [32]byte{}, errors.New("nil eth1 data") } - fieldRoots := make([][]byte, 3) + fieldRoots := make([][32]byte, 3) for i := 0; i < len(fieldRoots); i++ { - fieldRoots[i] = make([]byte, 32) + fieldRoots[i] = [32]byte{} } + if len(eth1Data.DepositRoot) > 0 { - depRoot := bytesutil.ToBytes32(eth1Data.DepositRoot) - fieldRoots[0] = depRoot[:] + fieldRoots[0] = bytesutil.ToBytes32(eth1Data.DepositRoot) } + eth1DataCountBuf := make([]byte, 8) binary.LittleEndian.PutUint64(eth1DataCountBuf, eth1Data.DepositCount) - eth1CountRoot := bytesutil.ToBytes32(eth1DataCountBuf) - fieldRoots[1] = eth1CountRoot[:] + fieldRoots[1] = bytesutil.ToBytes32(eth1DataCountBuf) if len(eth1Data.BlockHash) > 0 { - blockHash := bytesutil.ToBytes32(eth1Data.BlockHash) - fieldRoots[2] = blockHash[:] + fieldRoots[2] = bytesutil.ToBytes32(eth1Data.BlockHash) } root, err := ssz.BitwiseMerkleize(hasher, fieldRoots, uint64(len(fieldRoots)), uint64(len(fieldRoots))) if err != nil { @@ -44,23 +43,19 @@ func Eth1DataRootWithHasher(hasher ssz.HashFn, eth1Data *ethpb.Eth1Data) ([32]by // Eth1DatasRoot returns the hash tree root of input `eth1Datas`. func Eth1DatasRoot(eth1Datas []*ethpb.Eth1Data) ([32]byte, error) { hasher := hash.CustomSHA256Hasher() - eth1VotesRoots := make([][]byte, 0) + eth1VotesRoots := make([][32]byte, 0, len(eth1Datas)) for i := 0; i < len(eth1Datas); i++ { eth1, err := Eth1DataRootWithHasher(hasher, eth1Datas[i]) if err != nil { return [32]byte{}, errors.Wrap(err, "could not compute eth1data merkleization") } - eth1VotesRoots = append(eth1VotesRoots, eth1[:]) - } - eth1Chunks, err := ssz.Pack(eth1VotesRoots) - if err != nil { - return [32]byte{}, errors.Wrap(err, "could not chunk eth1 votes roots") + eth1VotesRoots = append(eth1VotesRoots, eth1) } eth1VotesRootsRoot, err := ssz.BitwiseMerkleize( hasher, - eth1Chunks, - uint64(len(eth1Chunks)), + eth1VotesRoots, + uint64(len(eth1VotesRoots)), fieldparams.Eth1DataVotesLength, ) if err != nil { diff --git a/beacon-chain/state/stateutil/field_root_attestation.go b/beacon-chain/state/stateutil/field_root_attestation.go index 4b87781b72..713dbcce65 100644 --- a/beacon-chain/state/stateutil/field_root_attestation.go +++ b/beacon-chain/state/stateutil/field_root_attestation.go @@ -25,13 +25,13 @@ func epochAttestationsRoot(atts []*ethpb.PendingAttestation) ([32]byte, error) { } hasher := hash.CustomSHA256Hasher() - roots := make([][]byte, len(atts)) + roots := make([][32]byte, len(atts)) for i := 0; i < len(atts); i++ { pendingRoot, err := pendingAttestationRoot(hasher, atts[i]) if err != nil { return [32]byte{}, errors.Wrap(err, "could not attestation merkleization") } - roots[i] = pendingRoot[:] + roots[i] = pendingRoot } attsRootsRoot, err := ssz.BitwiseMerkleize( diff --git a/beacon-chain/state/stateutil/field_root_validator.go b/beacon-chain/state/stateutil/field_root_validator.go index 8f64d63b60..2c27010319 100644 --- a/beacon-chain/state/stateutil/field_root_validator.go +++ b/beacon-chain/state/stateutil/field_root_validator.go @@ -48,7 +48,7 @@ func validatorRegistryRoot(validators []*ethpb.Validator) ([32]byte, error) { } } - validatorsRootsRoot, err := ssz.BitwiseMerkleizeArrays(hasher, roots, uint64(len(roots)), fieldparams.ValidatorRegistryLimit) + validatorsRootsRoot, err := ssz.BitwiseMerkleize(hasher, roots, uint64(len(roots)), fieldparams.ValidatorRegistryLimit) if err != nil { return [32]byte{}, errors.Wrap(err, "could not compute validator registry merkleization") } diff --git a/beacon-chain/state/stateutil/participation_bit_root.go b/beacon-chain/state/stateutil/participation_bit_root.go index 4849dbf300..2e914ec6fa 100644 --- a/beacon-chain/state/stateutil/participation_bit_root.go +++ b/beacon-chain/state/stateutil/participation_bit_root.go @@ -6,6 +6,7 @@ import ( "github.com/pkg/errors" fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams" "github.com/prysmaticlabs/prysm/crypto/hash" + "github.com/prysmaticlabs/prysm/encoding/bytesutil" "github.com/prysmaticlabs/prysm/encoding/ssz" ) @@ -19,13 +20,6 @@ func ParticipationBitsRoot(bits []byte) ([32]byte, error) { } limit := (uint64(fieldparams.ValidatorRegistryLimit + 31)) / 32 - if limit == 0 { - if len(bits) == 0 { - limit = 1 - } else { - limit = uint64(len(bits)) - } - } bytesRoot, err := ssz.BitwiseMerkleize(hasher, chunkedRoots, uint64(len(chunkedRoots)), limit) if err != nil { @@ -39,9 +33,9 @@ func ParticipationBitsRoot(bits []byte) ([32]byte, error) { // packParticipationBits into chunks. It'll pad the last chunk with zero bytes if // it does not have length bytes per chunk. -func packParticipationBits(bytes []byte) ([][]byte, error) { +func packParticipationBits(bytes []byte) ([][32]byte, error) { numItems := len(bytes) - var chunks [][]byte + var chunks [][32]byte for i := 0; i < numItems; i += 32 { j := i + 32 // We create our upper bound index of the chunk, if it is greater than numItems, @@ -51,19 +45,12 @@ func packParticipationBits(bytes []byte) ([][]byte, error) { } // We create chunks from the list of items based on the // indices determined above. - chunks = append(chunks, bytes[i:j]) + chunks = append(chunks, bytesutil.ToBytes32(bytes[i:j])) } if len(chunks) == 0 { return chunks, nil } - // Right-pad the last chunk with zero bytes if it does not - // have length bytes. - lastChunk := chunks[len(chunks)-1] - for len(lastChunk) < 32 { - lastChunk = append(lastChunk, 0) - } - chunks[len(chunks)-1] = lastChunk return chunks, nil } diff --git a/beacon-chain/state/stateutil/pending_attestation_root.go b/beacon-chain/state/stateutil/pending_attestation_root.go index 96f1228949..d0a853d827 100644 --- a/beacon-chain/state/stateutil/pending_attestation_root.go +++ b/beacon-chain/state/stateutil/pending_attestation_root.go @@ -37,42 +37,38 @@ func PendingAttRootWithHasher(hasher ssz.HashFn, att *ethpb.PendingAttestation) fieldRoots = [][32]byte{aggregationRoot, attDataRoot, inclusionRoot, proposerRoot} - return ssz.BitwiseMerkleizeArrays(hasher, fieldRoots, uint64(len(fieldRoots)), uint64(len(fieldRoots))) + return ssz.BitwiseMerkleize(hasher, fieldRoots, uint64(len(fieldRoots)), uint64(len(fieldRoots))) } func attDataRootWithHasher(hasher ssz.HashFn, data *ethpb.AttestationData) ([32]byte, error) { - fieldRoots := make([][]byte, 5) + fieldRoots := make([][32]byte, 5) if data != nil { // Slot. slotBuf := make([]byte, 8) binary.LittleEndian.PutUint64(slotBuf, uint64(data.Slot)) - slotRoot := bytesutil.ToBytes32(slotBuf) - fieldRoots[0] = slotRoot[:] + fieldRoots[0] = bytesutil.ToBytes32(slotBuf) // CommitteeIndex. indexBuf := make([]byte, 8) binary.LittleEndian.PutUint64(indexBuf, uint64(data.CommitteeIndex)) - interRoot := bytesutil.ToBytes32(indexBuf) - fieldRoots[1] = interRoot[:] + fieldRoots[1] = bytesutil.ToBytes32(indexBuf) // Beacon block root. - blockRoot := bytesutil.ToBytes32(data.BeaconBlockRoot) - fieldRoots[2] = blockRoot[:] + fieldRoots[2] = bytesutil.ToBytes32(data.BeaconBlockRoot) // Source sourceRoot, err := ssz.CheckpointRoot(hasher, data.Source) if err != nil { return [32]byte{}, errors.Wrap(err, "could not compute source checkpoint merkleization") } - fieldRoots[3] = sourceRoot[:] + fieldRoots[3] = sourceRoot // Target - targetRoot, err := ssz.CheckpointRoot(hasher, data.Target) + fieldRoots[4], err = ssz.CheckpointRoot(hasher, data.Target) if err != nil { return [32]byte{}, errors.Wrap(err, "could not compute target checkpoint merkleization") } - fieldRoots[4] = targetRoot[:] } return ssz.BitwiseMerkleize(hasher, fieldRoots, uint64(len(fieldRoots)), uint64(len(fieldRoots))) diff --git a/beacon-chain/state/stateutil/sync_committee.root.go b/beacon-chain/state/stateutil/sync_committee.root.go index 492818a6ff..524c6672e5 100644 --- a/beacon-chain/state/stateutil/sync_committee.root.go +++ b/beacon-chain/state/stateutil/sync_committee.root.go @@ -1,7 +1,9 @@ package stateutil import ( + "github.com/prysmaticlabs/prysm/config/features" "github.com/prysmaticlabs/prysm/crypto/hash" + "github.com/prysmaticlabs/prysm/crypto/hash/htr" "github.com/prysmaticlabs/prysm/encoding/ssz" ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" ) @@ -25,7 +27,7 @@ func SyncCommitteeRoot(committee *ethpb.SyncCommittee) ([32]byte, error) { } pubKeyRoots = append(pubKeyRoots, r) } - pubkeyRoot, err := ssz.BitwiseMerkleizeArrays(hasher, pubKeyRoots, uint64(len(pubKeyRoots)), uint64(len(pubKeyRoots))) + pubkeyRoot, err := ssz.BitwiseMerkleize(hasher, pubKeyRoots, uint64(len(pubKeyRoots)), uint64(len(pubKeyRoots))) if err != nil { return [32]byte{}, err } @@ -37,13 +39,24 @@ func SyncCommitteeRoot(committee *ethpb.SyncCommittee) ([32]byte, error) { } fieldRoots = [][32]byte{pubkeyRoot, aggregateKeyRoot} - return ssz.BitwiseMerkleizeArrays(hasher, fieldRoots, uint64(len(fieldRoots)), uint64(len(fieldRoots))) + return ssz.BitwiseMerkleize(hasher, fieldRoots, uint64(len(fieldRoots)), uint64(len(fieldRoots))) } func merkleizePubkey(hasher ssz.HashFn, pubkey []byte) ([32]byte, error) { - chunks, err := ssz.Pack([][]byte{pubkey}) + chunks, err := ssz.PackByChunk([][]byte{pubkey}) if err != nil { return [32]byte{}, err } - return ssz.BitwiseMerkleize(hasher, chunks, uint64(len(chunks)), uint64(len(chunks))) + var pubKeyRoot [32]byte + if features.Get().EnableVectorizedHTR { + outputChunk := make([][32]byte, 1) + htr.VectorizedSha256(chunks, outputChunk) + pubKeyRoot = outputChunk[0] + } else { + pubKeyRoot, err = ssz.BitwiseMerkleize(hasher, chunks, uint64(len(chunks)), uint64(len(chunks))) + if err != nil { + return [32]byte{}, err + } + } + return pubKeyRoot, nil } diff --git a/beacon-chain/state/stateutil/validator_root.go b/beacon-chain/state/stateutil/validator_root.go index 8705b23627..21eb8edc0c 100644 --- a/beacon-chain/state/stateutil/validator_root.go +++ b/beacon-chain/state/stateutil/validator_root.go @@ -4,10 +4,8 @@ import ( "encoding/binary" "github.com/pkg/errors" - "github.com/prysmaticlabs/prysm/config/features" fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams" "github.com/prysmaticlabs/prysm/crypto/hash" - "github.com/prysmaticlabs/prysm/crypto/hash/htr" "github.com/prysmaticlabs/prysm/encoding/bytesutil" "github.com/prysmaticlabs/prysm/encoding/ssz" ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1" @@ -20,7 +18,7 @@ func ValidatorRootWithHasher(hasher ssz.HashFn, validator *ethpb.Validator) ([32 if err != nil { return [32]byte{}, err } - return ssz.BitwiseMerkleizeArrays(hasher, fieldRoots, uint64(len(fieldRoots)), uint64(len(fieldRoots))) + return ssz.BitwiseMerkleize(hasher, fieldRoots, uint64(len(fieldRoots)), uint64(len(fieldRoots))) } // ValidatorFieldRoots describes a method from which the hash tree root @@ -52,21 +50,10 @@ func ValidatorFieldRoots(hasher ssz.HashFn, validator *ethpb.Validator) ([][32]b binary.LittleEndian.PutUint64(withdrawalBuf[:8], uint64(validator.WithdrawableEpoch)) // Public key. - pubKeyChunks, err := ssz.PackByChunk([][]byte{pubkey[:]}) + pubKeyRoot, err := merkleizePubkey(hasher, pubkey[:]) if err != nil { return [][32]byte{}, err } - var pubKeyRoot [32]byte - if features.Get().EnableVectorizedHTR { - outputChunk := make([][32]byte, 1) - htr.VectorizedSha256(pubKeyChunks, outputChunk) - pubKeyRoot = outputChunk[0] - } else { - pubKeyRoot, err = ssz.BitwiseMerkleizeArrays(hasher, pubKeyChunks, uint64(len(pubKeyChunks)), uint64(len(pubKeyChunks))) - if err != nil { - return [][32]byte{}, err - } - } fieldRoots = [][32]byte{pubKeyRoot, withdrawCreds, effectiveBalanceBuf, slashBuf, activationEligibilityBuf, activationBuf, exitBuf, withdrawalBuf} } @@ -83,20 +70,14 @@ func Uint64ListRootWithRegistryLimit(balances []uint64) ([32]byte, error) { binary.LittleEndian.PutUint64(balanceBuf, balances[i]) balancesMarshaling = append(balancesMarshaling, balanceBuf) } - balancesChunks, err := ssz.Pack(balancesMarshaling) + balancesChunks, err := ssz.PackByChunk(balancesMarshaling) if err != nil { return [32]byte{}, errors.Wrap(err, "could not pack balances into chunks") } maxBalCap := uint64(fieldparams.ValidatorRegistryLimit) elemSize := uint64(8) balLimit := (maxBalCap*elemSize + 31) / 32 - if balLimit == 0 { - if len(balances) == 0 { - balLimit = 1 - } else { - balLimit = uint64(len(balances)) - } - } + balancesRootsRoot, err := ssz.BitwiseMerkleize(hasher, balancesChunks, uint64(len(balancesChunks)), balLimit) if err != nil { return [32]byte{}, errors.Wrap(err, "could not compute balances merkleization") diff --git a/encoding/ssz/helpers.go b/encoding/ssz/helpers.go index 83eba70c5d..60d8a746d0 100644 --- a/encoding/ssz/helpers.go +++ b/encoding/ssz/helpers.go @@ -19,13 +19,13 @@ func BitlistRoot(hasher HashFn, bfield bitfield.Bitfield, maxCapacity uint64) ([ limit := (maxCapacity + 255) / 256 if bfield == nil || bfield.Len() == 0 { length := make([]byte, 32) - root, err := BitwiseMerkleize(hasher, [][]byte{}, 0, limit) + root, err := BitwiseMerkleize(hasher, [][32]byte{}, 0, limit) if err != nil { return [32]byte{}, err } return MixInLength(root, length), nil } - chunks, err := Pack([][]byte{bfield.Bytes()}) + chunks, err := PackByChunk([][]byte{bfield.Bytes()}) if err != nil { return [32]byte{}, err } @@ -47,22 +47,7 @@ func BitlistRoot(hasher HashFn, bfield bitfield.Bitfield, maxCapacity uint64) ([ // and return the root. // Note that merkleize on a single chunk is simply that chunk, i.e. the identity // when the number of chunks is one. -func BitwiseMerkleize(hasher HashFn, chunks [][]byte, count, limit uint64) ([32]byte, error) { - if count > limit { - return [32]byte{}, errors.New("merkleizing list that is too large, over limit") - } - if features.Get().EnableVectorizedHTR { - return MerkleizeList(chunks, limit), nil - } - hashFn := NewHasherFunc(hasher) - leafIndexer := func(i uint64) []byte { - return chunks[i] - } - return Merkleize(hashFn, count, limit, leafIndexer), nil -} - -// BitwiseMerkleizeArrays is used when a set of 32-byte root chunks are provided. -func BitwiseMerkleizeArrays(hasher HashFn, chunks [][32]byte, count, limit uint64) ([32]byte, error) { +func BitwiseMerkleize(hasher HashFn, chunks [][32]byte, count, limit uint64) ([32]byte, error) { if count > limit { return [32]byte{}, errors.New("merkleizing list that is too large, over limit") } @@ -76,51 +61,6 @@ func BitwiseMerkleizeArrays(hasher HashFn, chunks [][32]byte, count, limit uint6 return Merkleize(hashFn, count, limit, leafIndexer), nil } -// Pack a given byte array's final chunk with zeroes if needed. -func Pack(serializedItems [][]byte) ([][]byte, error) { - areAllEmpty := true - for _, item := range serializedItems { - if !bytes.Equal(item, []byte{}) { - areAllEmpty = false - break - } - } - // If there are no items, we return an empty chunk. - if len(serializedItems) == 0 || areAllEmpty { - emptyChunk := make([]byte, bytesPerChunk) - return [][]byte{emptyChunk}, nil - } else if len(serializedItems[0]) == bytesPerChunk { - // If each item has exactly BYTES_PER_CHUNK length, we return the list of serialized items. - return serializedItems, nil - } - // We flatten the list in order to pack its items into byte chunks correctly. - var orderedItems []byte - for _, item := range serializedItems { - orderedItems = append(orderedItems, item...) - } - numItems := len(orderedItems) - var chunks [][]byte - for i := 0; i < numItems; i += bytesPerChunk { - j := i + bytesPerChunk - // We create our upper bound index of the chunk, if it is greater than numItems, - // we set it as numItems itself. - if j > numItems { - j = numItems - } - // We create chunks from the list of items based on the - // indices determined above. - chunks = append(chunks, orderedItems[i:j]) - } - // Right-pad the last chunk with zero bytes if it does not - // have length bytesPerChunk. - lastChunk := chunks[len(chunks)-1] - for len(lastChunk) < bytesPerChunk { - lastChunk = append(lastChunk, 0) - } - chunks[len(chunks)-1] = lastChunk - return chunks, nil -} - // PackByChunk a given byte array's final chunk with zeroes if needed. func PackByChunk(serializedItems [][]byte) ([][bytesPerChunk]byte, error) { emptyChunk := [bytesPerChunk]byte{} diff --git a/encoding/ssz/helpers_test.go b/encoding/ssz/helpers_test.go index ce24f5e639..634c8b3111 100644 --- a/encoding/ssz/helpers_test.go +++ b/encoding/ssz/helpers_test.go @@ -23,24 +23,9 @@ func TestBitlistRoot(t *testing.T) { assert.Equal(t, expected, result) } -func TestBitwiseMerkleize(t *testing.T) { - hasher := hash.CustomSHA256Hasher() - chunks := [][]byte{ - {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, - {11, 12, 13, 14, 15, 16, 17, 18, 19, 20}, - } - count := uint64(2) - limit := uint64(2) - expected := [32]byte{194, 32, 213, 52, 220, 127, 18, 240, 43, 151, 19, 79, 188, 175, 142, 177, 208, 46, 96, 20, 18, 231, 208, 29, 120, 102, 122, 17, 46, 31, 155, 30} - - result, err := ssz.BitwiseMerkleize(hasher, chunks, count, limit) - require.NoError(t, err) - assert.Equal(t, expected, result) -} - func TestBitwiseMerkleizeOverLimit(t *testing.T) { hasher := hash.CustomSHA256Hasher() - chunks := [][]byte{ + chunks := [][32]byte{ {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, {11, 12, 13, 14, 15, 16, 17, 18, 19, 20}, } @@ -61,7 +46,19 @@ func TestBitwiseMerkleizeArrays(t *testing.T) { limit := uint64(2) expected := [32]byte{138, 81, 210, 194, 151, 231, 249, 241, 64, 118, 209, 58, 145, 109, 225, 89, 118, 110, 159, 220, 193, 183, 203, 124, 166, 24, 65, 26, 160, 215, 233, 219} - result, err := ssz.BitwiseMerkleizeArrays(hasher, chunks, count, limit) + result, err := ssz.BitwiseMerkleize(hasher, chunks, count, limit) + require.NoError(t, err) + assert.Equal(t, expected, result) + + chunks = [][32]byte{ + {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, + {11, 12, 13, 14, 15, 16, 17, 18, 19, 20}, + } + count = uint64(2) + limit = uint64(2) + expected = [32]byte{194, 32, 213, 52, 220, 127, 18, 240, 43, 151, 19, 79, 188, 175, 142, 177, 208, 46, 96, 20, 18, 231, 208, 29, 120, 102, 122, 17, 46, 31, 155, 30} + + result, err = ssz.BitwiseMerkleize(hasher, chunks, count, limit) require.NoError(t, err) assert.Equal(t, expected, result) } @@ -75,25 +72,10 @@ func TestBitwiseMerkleizeArraysOverLimit(t *testing.T) { count := uint64(2) limit := uint64(1) - _, err := ssz.BitwiseMerkleizeArrays(hasher, chunks, count, limit) + _, err := ssz.BitwiseMerkleize(hasher, chunks, count, limit) assert.ErrorContains(t, merkleizingListLimitError, err) } -func TestPack(t *testing.T) { - byteSlice2D := [][]byte{ - {1, 2, 3, 4, 5, 6, 7, 8, 9}, - {1, 1, 2, 3, 5, 8, 13, 21, 34}, - } - expected := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 1, 2, 3, 5, 8, 13, 21, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} - - result, err := ssz.Pack(byteSlice2D) - require.NoError(t, err) - assert.Equal(t, len(expected), len(result[0])) - for i, v := range expected { - assert.DeepEqual(t, v, result[0][i]) - } -} - func TestPackByChunk(t *testing.T) { byteSlice2D := [][]byte{ {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 2, 5, 2, 6, 2, 7}, diff --git a/encoding/ssz/htrutils.go b/encoding/ssz/htrutils.go index 358f2446a6..05ee69acdf 100644 --- a/encoding/ssz/htrutils.go +++ b/encoding/ssz/htrutils.go @@ -25,16 +25,13 @@ func Uint64Root(val uint64) [32]byte { // a Fork struct value according to the Ethereum // Simple Serialize specification. func ForkRoot(fork *ethpb.Fork) ([32]byte, error) { - fieldRoots := make([][]byte, 3) + fieldRoots := make([][32]byte, 3) if fork != nil { - prevRoot := bytesutil.ToBytes32(fork.PreviousVersion) - fieldRoots[0] = prevRoot[:] - currRoot := bytesutil.ToBytes32(fork.CurrentVersion) - fieldRoots[1] = currRoot[:] + fieldRoots[0] = bytesutil.ToBytes32(fork.PreviousVersion) + fieldRoots[1] = bytesutil.ToBytes32(fork.CurrentVersion) forkEpochBuf := make([]byte, 8) binary.LittleEndian.PutUint64(forkEpochBuf, uint64(fork.Epoch)) - epochRoot := bytesutil.ToBytes32(forkEpochBuf) - fieldRoots[2] = epochRoot[:] + fieldRoots[2] = bytesutil.ToBytes32(forkEpochBuf) } return BitwiseMerkleize(hash.CustomSHA256Hasher(), fieldRoots, uint64(len(fieldRoots)), uint64(len(fieldRoots))) } @@ -43,14 +40,12 @@ func ForkRoot(fork *ethpb.Fork) ([32]byte, error) { // a InitWithReset struct value according to the Ethereum // Simple Serialize specification. func CheckpointRoot(hasher HashFn, checkpoint *ethpb.Checkpoint) ([32]byte, error) { - fieldRoots := make([][]byte, 2) + fieldRoots := make([][32]byte, 2) if checkpoint != nil { epochBuf := make([]byte, 8) binary.LittleEndian.PutUint64(epochBuf, uint64(checkpoint.Epoch)) - epochRoot := bytesutil.ToBytes32(epochBuf) - fieldRoots[0] = epochRoot[:] - ckpRoot := bytesutil.ToBytes32(checkpoint.Root) - fieldRoots[1] = ckpRoot[:] + fieldRoots[0] = bytesutil.ToBytes32(epochBuf) + fieldRoots[1] = bytesutil.ToBytes32(checkpoint.Root) } return BitwiseMerkleize(hasher, fieldRoots, uint64(len(fieldRoots)), uint64(len(fieldRoots))) } @@ -59,12 +54,16 @@ func CheckpointRoot(hasher HashFn, checkpoint *ethpb.Checkpoint) ([32]byte, erro // a list of [32]byte roots according to the Ethereum Simple Serialize // specification. func ByteArrayRootWithLimit(roots [][]byte, limit uint64) ([32]byte, error) { - result, err := BitwiseMerkleize(hash.CustomSHA256Hasher(), roots, uint64(len(roots)), limit) + newRoots := make([][32]byte, len(roots)) + for i, r := range roots { + copy(newRoots[i][:], r) + } + result, err := BitwiseMerkleize(hash.CustomSHA256Hasher(), newRoots, uint64(len(newRoots)), limit) if err != nil { return [32]byte{}, errors.Wrap(err, "could not compute byte array merkleization") } buf := new(bytes.Buffer) - if err := binary.Write(buf, binary.LittleEndian, uint64(len(roots))); err != nil { + if err := binary.Write(buf, binary.LittleEndian, uint64(len(newRoots))); err != nil { return [32]byte{}, errors.Wrap(err, "could not marshal byte array length") } // We need to mix in the length of the slice. @@ -84,7 +83,7 @@ func SlashingsRoot(slashings []uint64) ([32]byte, error) { binary.LittleEndian.PutUint64(slashBuf, slashings[i]) slashingMarshaling[i] = slashBuf } - slashingChunks, err := Pack(slashingMarshaling) + slashingChunks, err := PackByChunk(slashingMarshaling) if err != nil { return [32]byte{}, errors.Wrap(err, "could not pack slashings into chunks") } @@ -96,16 +95,16 @@ func SlashingsRoot(slashings []uint64) ([32]byte, error) { // ExecutionPayload. func TransactionsRoot(txs [][]byte) ([32]byte, error) { hasher := hash.CustomSHA256Hasher() - listMarshaling := make([][]byte, 0) + txRoots := make([][32]byte, 0) for i := 0; i < len(txs); i++ { rt, err := transactionRoot(txs[i]) if err != nil { return [32]byte{}, err } - listMarshaling = append(listMarshaling, rt[:]) + txRoots = append(txRoots, rt) } - bytesRoot, err := BitwiseMerkleize(hasher, listMarshaling, uint64(len(listMarshaling)), fieldparams.MaxTxsPerPayloadLength) + bytesRoot, err := BitwiseMerkleize(hasher, txRoots, uint64(len(txRoots)), fieldparams.MaxTxsPerPayloadLength) if err != nil { return [32]byte{}, errors.Wrap(err, "could not compute merkleization") } @@ -120,7 +119,7 @@ func TransactionsRoot(txs [][]byte) ([32]byte, error) { func transactionRoot(tx []byte) ([32]byte, error) { hasher := hash.CustomSHA256Hasher() - chunkedRoots, err := PackChunks(tx) + chunkedRoots, err := PackByChunk([][]byte{tx}) if err != nil { return [32]byte{}, err } @@ -138,34 +137,3 @@ func transactionRoot(tx []byte) ([32]byte, error) { copy(bytesRootBufRoot, bytesRootBuf.Bytes()) return MixInLength(bytesRoot, bytesRootBufRoot), nil } - -// PackChunks a given byte array into chunks. It'll pad the last chunk with zero bytes if -// it does not have length bytes per chunk. -func PackChunks(bytes []byte) ([][]byte, error) { - numItems := len(bytes) - var chunks [][]byte - for i := 0; i < numItems; i += 32 { - j := i + 32 - // We create our upper bound index of the chunk, if it is greater than numItems, - // we set it as numItems itself. - if j > numItems { - j = numItems - } - // We create chunks from the list of items based on the - // indices determined above. - chunks = append(chunks, bytes[i:j]) - } - - if len(chunks) == 0 { - return chunks, nil - } - - // Right-pad the last chunk with zero bytes if it does not - // have length bytes. - lastChunk := chunks[len(chunks)-1] - for len(lastChunk) < 32 { - lastChunk = append(lastChunk, 0) - } - chunks[len(chunks)-1] = lastChunk - return chunks, nil -} diff --git a/encoding/ssz/htrutils_fuzz_test.go b/encoding/ssz/htrutils_fuzz_test.go index 7577807fec..25dae6d295 100644 --- a/encoding/ssz/htrutils_fuzz_test.go +++ b/encoding/ssz/htrutils_fuzz_test.go @@ -48,7 +48,7 @@ func FuzzForkRoot(f *testing.F) { func FuzzPackChunks(f *testing.F) { f.Fuzz(func(t *testing.T, b []byte) { - if _, err := ssz.PackChunks(b); err != nil { + if _, err := ssz.PackByChunk([][]byte{b}); err != nil { t.Fatal(err) } }) diff --git a/encoding/ssz/htrutils_test.go b/encoding/ssz/htrutils_test.go index 6153557887..d3b8029fdc 100644 --- a/encoding/ssz/htrutils_test.go +++ b/encoding/ssz/htrutils_test.go @@ -123,36 +123,36 @@ func TestTransactionsRoot(t *testing.T) { } } -func TestPackChunks(t *testing.T) { +func TestPackByChunk_SingleList(t *testing.T) { tests := []struct { name string input []byte - want [][]byte + want [][32]byte }{ { name: "nil", input: nil, - want: [][]byte{}, + want: [][32]byte{{}}, }, { name: "empty", input: []byte{}, - want: [][]byte{}, + want: [][32]byte{{}}, }, { name: "one", input: []byte{1}, - want: [][]byte{{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, + want: [][32]byte{{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, }, { name: "one, two", input: []byte{1, 2}, - want: [][]byte{{1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, + want: [][32]byte{{1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := ssz.PackChunks(tt.input) + got, err := ssz.PackByChunk([][]byte{tt.input}) require.NoError(t, err) require.DeepSSZEqual(t, tt.want, got) }) diff --git a/encoding/ssz/merkleize.go b/encoding/ssz/merkleize.go index 2118004450..d9126f8722 100644 --- a/encoding/ssz/merkleize.go +++ b/encoding/ssz/merkleize.go @@ -219,18 +219,3 @@ func MerkleizeVector(elements [][32]byte, length uint64) [32]byte { } return elements[0] } - -// MerkleizeList uses our optimized routine to hash a 2d-list of -// elements. -func MerkleizeList(elements [][]byte, length uint64) [32]byte { - depth := Depth(length) - // Return zerohash at depth - if len(elements) == 0 { - return trie.ZeroHashes[depth] - } - newElems := make([][32]byte, len(elements)) - for i := range elements { - copy(newElems[i][:], elements[i]) - } - return MerkleizeVector(newElems, length) -}