Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose a hashing function that takes byteslices #10

Merged
merged 3 commits into from
May 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 35 additions & 1 deletion hash.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
MIT License

Copyright (c) 2021 Prysmatic Labs
# Copyright (c) 2021 Prysmatic Labs
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is my linter doing this change?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤷


Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand All @@ -25,6 +25,8 @@ package gohashtree

import (
"fmt"
"reflect"
"unsafe"
)

func _hash(digests *byte, p [][32]byte, count uint32)
Expand All @@ -51,3 +53,35 @@ func Hash(digests [][32]byte, chunks [][32]byte) error {
func HashChunks(digests [][32]byte, chunks [][32]byte) {
_hash(&digests[0][0], chunks, uint32(len(chunks)/2))
}

func HashByteSlice(digests []byte, chunks []byte) error {
if len(chunks) == 0 {
return nil
}
if len(chunks)%64 != 0 {
return fmt.Errorf("chunks not multiple of 64 bytes")
}
if len(digests)%32 != 0 {
return fmt.Errorf("digests not multiple of 32 bytes")
}
if len(digests) < len(chunks)/2 {
return fmt.Errorf("not enough digest length, need at least %d, got %d", len(chunks)/2, len(digests))
}
// We use an unsafe pointer to cast []byte to [][32]byte. The length and
// capacity of the slice need to be divided accordingly by 32.
header := *(*reflect.SliceHeader)(unsafe.Pointer(&chunks))
header.Len <<= 5
header.Cap <<= 5
prestonvanloon marked this conversation as resolved.
Show resolved Hide resolved
chunkedChunks := *(*[][32]byte)(unsafe.Pointer(&header))

if supportedCPU {
_hash(&digests[0], chunkedChunks, uint32(len(chunks)/64))
} else {
headerDigest := *(*reflect.SliceHeader)(unsafe.Pointer(&digests))
headerDigest.Len <<= 5
headerDigest.Cap <<= 5
chunkedDigest := *(*[][32]byte)(unsafe.Pointer(&headerDigest))
sha256_1_generic(chunkedDigest, chunkedChunks)
}
return nil
}
71 changes: 70 additions & 1 deletion hash_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
MIT License

Copyright (c) 2021 Prysmatic Labs
# Copyright (c) 2021 Prysmatic Labs

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down Expand Up @@ -199,6 +199,75 @@ func TestHash(t *testing.T) {
}
}

func TestHashByteSlice(t *testing.T) {
tests := []struct {
name string
count uint32
}{
{
name: "hash 1 block",
count: 1,
},
{
name: "hash 4 blocks",
count: 4,
},
{
name: "hash 8 blocks",
count: 8,
},
{
name: "hash 16 blocks",
count: 16,
},
{
name: "hash 18 blocks",
count: 18,
},
{
name: "hash 24 blocks",
count: 24,
},
{
name: "hash 32 blocks",
count: 32,
},
{
name: "hash 31 blocks",
count: 31,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
digests := make([]byte, 32*tt.count)
chunks := make([]byte, 64*tt.count)
for i := 0; i < int(2*tt.count); i += 2 {
if n := copy(chunks[32*i:32*i+32], _test_32_block[i][:]); n != 32 {
t.Logf("copied wrong number of bytes")
t.Fail()
}
if n := copy(chunks[32*i+32:32*i+64], _test_32_block[i+1][:]); n != 32 {
t.Logf("copied wrong number of bytes")
t.Fail()
}
}

err := gohashtree.HashByteSlice(digests, chunks)
if err != nil {
t.Log(err)
t.Fail()
}
for i := 0; i < int(tt.count); i++ {
if !reflect.DeepEqual(digests[32*i:32*i+32], _test_32_digests[i][:]) {
t.Logf("Digests are different\n Expected: %x\n Produced: %x\n",
_test_32_digests[i][:], digests[32*i:32*i+32])
t.Fail()
}
}
})
}
}

func TestOddChunks(t *testing.T) {
digests := make([][32]byte, 1)
chunks := make([][32]byte, 1)
Expand Down