Skip to content

Commit

Permalink
Add crypto.Digest
Browse files Browse the repository at this point in the history
allows us to calculate hashes of arrays without allocating
  • Loading branch information
omerfirmak committed Sep 13, 2023
1 parent 1e4e484 commit 5669db9
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 9 deletions.
8 changes: 8 additions & 0 deletions core/crypto/digest.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package crypto

import "github.com/NethermindEth/juno/core/felt"

type Digest interface {
Update(...*felt.Felt) Digest
Finish() *felt.Felt
}
28 changes: 22 additions & 6 deletions core/crypto/pedersen_hash.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,8 @@ import (
//
// [Pedersen array hashing]: https://docs.starknet.io/documentation/develop/Hashing/hash-functions/#array_hashing
func PedersenArray(elems ...*felt.Felt) *felt.Felt {
fpElements := make([]*fp.Element, len(elems))
for i, elem := range elems {
fpElements[i] = elem.Impl()
}
hash := pedersenhash.PedersenArray(fpElements...)
return felt.NewFelt(&hash)
var digest PedersenDigest
return digest.Update(elems...).Finish()
}

// Pedersen implements the [Pedersen hash].
Expand All @@ -25,3 +21,23 @@ func Pedersen(a, b *felt.Felt) *felt.Felt {
hash := pedersenhash.Pedersen(a.Impl(), b.Impl())
return felt.NewFelt(&hash)
}

var _ Digest = (*PedersenDigest)(nil)

type PedersenDigest struct {
digest fp.Element
count uint64
}

func (d *PedersenDigest) Update(elems ...*felt.Felt) Digest {
for idx := range elems {
d.digest = pedersenhash.Pedersen(&d.digest, elems[idx].Impl())
}
d.count += uint64(len(elems))
return d
}

func (d *PedersenDigest) Finish() *felt.Felt {
d.digest = pedersenhash.Pedersen(&d.digest, new(fp.Element).SetUint64(d.count))
return felt.NewFelt(&d.digest)
}
10 changes: 7 additions & 3 deletions core/crypto/pedersen_hash_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/NethermindEth/juno/core/crypto"
"github.com/NethermindEth/juno/core/felt"
"github.com/NethermindEth/juno/utils"
"github.com/stretchr/testify/assert"
)

func TestPedersen(t *testing.T) {
Expand Down Expand Up @@ -103,16 +104,19 @@ func TestPedersenArray(t *testing.T) {
},
}
for _, test := range tests {
var digest, digestWhole crypto.PedersenDigest
var data []*felt.Felt
for _, item := range test.input {
elem := utils.HexToFelt(t, item)
digest.Update(elem)
data = append(data, elem)
}
digestWhole.Update(data...)
want := utils.HexToFelt(t, test.want)
got := crypto.PedersenArray(data...)
if !got.Equal(want) {
t.Errorf("PedersenArray(%x) = %x, want %x", data, got, want)
}
assert.Equal(t, want, got)
assert.Equal(t, want, digest.Finish())
assert.Equal(t, want, digestWhole.Finish())
}
}

Expand Down
32 changes: 32 additions & 0 deletions core/crypto/poseidon_hash.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,38 @@ func setRoundKeys() {
}
}

var _ Digest = (*PedersenDigest)(nil)

type PoseidonDigest struct {
state [3]felt.Felt
lastElem *felt.Felt
}

func (d *PoseidonDigest) Update(elems ...*felt.Felt) Digest {
for idx := range elems {
if d.lastElem == nil {
d.lastElem = &(*elems[idx])
} else {
d.state[0].Add(&d.state[0], d.lastElem)
d.state[1].Add(&d.state[1], elems[idx])
hadesPermutation(d.state[:])
d.lastElem = nil
}
}
return d
}

func (d *PoseidonDigest) Finish() *felt.Felt {
if d.lastElem == nil {
d.state[0].Add(&d.state[0], one)
} else {
d.state[0].Add(&d.state[0], d.lastElem)
d.state[1].Add(&d.state[1], one)
}
hadesPermutation(d.state[:])
return &d.state[0]
}

// https://github.com/starkware-industries/poseidon/blob/main/poseidon3.txt
var roundKeysSpec = [][]string{
{
Expand Down
7 changes: 7 additions & 0 deletions core/crypto/poseidon_hash_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,14 @@ func TestPoseidonArray(t *testing.T) {
},
} {
t.Run(name, func(t *testing.T) {
var digest, digestWhole crypto.PoseidonDigest
assert.Equal(t, test.expected, crypto.PoseidonArray(test.elems...).String())
assert.Equal(t, test.expected, digestWhole.Update(test.elems...).Finish().String())

for _, elem := range test.elems {
digest.Update(elem)
}
assert.Equal(t, test.expected, digest.Finish().String())
})
}
}
Expand Down

0 comments on commit 5669db9

Please sign in to comment.