Skip to content

Commit

Permalink
Merge pull request #10 from prysmaticlabs/hash_byteslice
Browse files Browse the repository at this point in the history
Expose a hashing function that takes byteslices
  • Loading branch information
potuz authored May 2, 2023
2 parents 4118d9b + d41b010 commit aafd8b3
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 2 deletions.
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
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
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

0 comments on commit aafd8b3

Please sign in to comment.