Hashing Functions
The curve package provides several cryptographic hash functions used in Starknet. Understanding when to use each hash function is crucial for correct implementation.
Hash Function Overview
Starknet uses specialized hash functions optimized for zero-knowledge proofs:
- Pedersen Hash: Legacy hash function, widely used in existing contracts
- Poseidon Hash: Modern, more efficient hash function recommended for new implementations
- Starknet Keccak: Specialized variant of Keccak for selector computation
Pedersen Hash Functions
Pedersen
Computes the Pedersen hash of two field elements.
Signature:func Pedersen(a, b *felt.Felt) *felt.Felta- First felt element to hashb- Second felt element to hash
*felt.Felt- The resulting Pedersen hash
- Maintaining compatibility with existing contracts
- Working with legacy systems that use Pedersen
- Computing hashes for older Starknet protocols
package main
import (
"fmt"
"github.com/NethermindEth/juno/core/felt"
"github.com/NethermindEth/starknet.go/curve"
)
func main() {
// Hash two field elements
a := new(felt.Felt).SetUint64(123)
b := new(felt.Felt).SetUint64(456)
hash := curve.Pedersen(a, b)
fmt.Printf("Pedersen hash: %s\n", hash.String())
// Chaining hashes (common pattern)
c := new(felt.Felt).SetUint64(789)
chainedHash := curve.Pedersen(hash, c)
fmt.Printf("Chained hash: %s\n", chainedHash.String())
}PedersenArray
Computes the Pedersen hash of multiple field elements.
Signature:func PedersenArray(felts ...*felt.Felt) *felt.Feltfelts- Variadic number of felt elements to hash
*felt.Felt- The resulting Pedersen hash of all elements
- Hashing arrays or lists of values
- Computing cumulative hashes
- Working with variable-length data
package main
import (
"fmt"
"github.com/NethermindEth/juno/core/felt"
"github.com/NethermindEth/starknet.go/curve"
)
func main() {
// Hash multiple elements
elements := []*felt.Felt{
new(felt.Felt).SetUint64(100),
new(felt.Felt).SetUint64(200),
new(felt.Felt).SetUint64(300),
new(felt.Felt).SetUint64(400),
}
hash := curve.PedersenArray(elements...)
fmt.Printf("Array hash: %s\n", hash.String())
// Hash empty array (returns hash of zero)
emptyHash := curve.PedersenArray()
fmt.Printf("Empty array hash: %s\n", emptyHash.String())
}HashPedersenElements
Calculates the Pedersen hash of a list of big integers.
Signature:func HashPedersenElements(elems []*big.Int) *big.Intelems- Slice of big.Int pointers to be hashed
*big.Int- The hash of the elements
- Working with big.Int instead of felt.Felt
- Integrating with code that uses big.Int
- Computing hashes for numerical data
package main
import (
"fmt"
"math/big"
"github.com/NethermindEth/starknet.go/curve"
)
func main() {
// Hash big integers
elements := []*big.Int{
big.NewInt(123782376),
big.NewInt(213984),
big.NewInt(128763521321),
}
hash := curve.HashPedersenElements(elements)
fmt.Printf("Hash result: %s\n", hash.String())
fmt.Printf("Hash (hex): 0x%x\n", hash)
// Empty array handling
emptyHash := curve.HashPedersenElements([]*big.Int{})
fmt.Printf("Empty hash: 0x%x\n", emptyHash)
}ComputeHashOnElements
Computes the Pedersen hash on elements with length encoding.
Signature:func ComputeHashOnElements(elems []*big.Int) *big.Intelems- Slice of big.Int pointers to be hashed
*big.Int- The hash including the length
- When you need to include array length in the hash
- Computing hashes for contract calldata
- Ensuring hash integrity with variable-length data
Difference from HashPedersenElements: This function appends the length of the array to the elements before hashing, providing additional integrity.
Usage Example:package main
import (
"fmt"
"math/big"
"github.com/NethermindEth/starknet.go/curve"
)
func main() {
elements := []*big.Int{
big.NewInt(100),
big.NewInt(200),
big.NewInt(300),
}
// Hash with length encoding
hash := curve.ComputeHashOnElements(elements)
fmt.Printf("Hash with length: 0x%x\n", hash)
// Compare with HashPedersenElements
hashWithoutLength := curve.HashPedersenElements(elements)
fmt.Printf("Hash without length: 0x%x\n", hashWithoutLength)
// The results will be different because ComputeHashOnElements
// includes the array length (3) in the hash computation
}Poseidon Hash Functions
Poseidon
Computes the Poseidon hash of two field elements.
Signature:func Poseidon(a, b *felt.Felt) *felt.Felta- First felt element to hashb- Second felt element to hash
*felt.Felt- The resulting Poseidon hash
- New implementations (recommended)
- Performance-critical applications
- Modern Starknet contracts
- Faster computation
- More efficient in zero-knowledge proofs
- Better suited for STARK-friendly operations
package main
import (
"fmt"
"github.com/NethermindEth/juno/core/felt"
"github.com/NethermindEth/starknet.go/curve"
)
func main() {
// Hash two elements with Poseidon
a := new(felt.Felt).SetUint64(123)
b := new(felt.Felt).SetUint64(456)
hash := curve.Poseidon(a, b)
fmt.Printf("Poseidon hash: %s\n", hash.String())
// Compare with Pedersen (different results)
pedersenHash := curve.Pedersen(a, b)
fmt.Printf("Pedersen hash: %s\n", pedersenHash.String())
fmt.Printf("Hashes are different: %t\n", hash.String() != pedersenHash.String())
}PoseidonArray
Computes the Poseidon hash of multiple field elements.
Signature:func PoseidonArray(felts ...*felt.Felt) *felt.Feltfelts- Variadic number of felt elements to hash
*felt.Felt- The resulting Poseidon hash of all elements
- Hashing multiple values efficiently
- Modern contract implementations
- Performance-critical array hashing
package main
import (
"fmt"
"time"
"github.com/NethermindEth/juno/core/felt"
"github.com/NethermindEth/starknet.go/curve"
)
func main() {
// Create large array for performance comparison
elements := make([]*felt.Felt, 100)
for i := range elements {
elements[i] = new(felt.Felt).SetUint64(uint64(i))
}
// Time Poseidon hashing
start := time.Now()
poseidonHash := curve.PoseidonArray(elements...)
poseidonTime := time.Since(start)
fmt.Printf("Poseidon hash: %s\n", poseidonHash.String())
fmt.Printf("Poseidon time: %v\n", poseidonTime)
// Time Pedersen hashing (for comparison)
start = time.Now()
pedersenHash := curve.PedersenArray(elements...)
pedersenTime := time.Since(start)
fmt.Printf("Pedersen hash: %s\n", pedersenHash.String())
fmt.Printf("Pedersen time: %v\n", pedersenTime)
}Starknet Keccak
StarknetKeccak
Computes the Starknet Keccak hash of a byte slice.
Signature:func StarknetKeccak(b []byte) *felt.Feltb- Byte slice to hash
*felt.Felt- The resulting Starknet Keccak hash
- Computing function selectors
- Hashing string data
- Computing entry point selectors for contracts
package main
import (
"fmt"
"github.com/NethermindEth/starknet.go/curve"
)
func main() {
// Compute function selector
functionName := "transfer"
selector := curve.StarknetKeccak([]byte(functionName))
fmt.Printf("Function selector for '%s': %s\n", functionName, selector.String())
// Compute entry point selector
entryPoint := "constructor"
epSelector := curve.StarknetKeccak([]byte(entryPoint))
fmt.Printf("Entry point selector for '%s': %s\n", entryPoint, epSelector.String())
// Hash arbitrary data
data := []byte("Hello, Starknet!")
hash := curve.StarknetKeccak(data)
fmt.Printf("Data hash: %s\n", hash.String())
}Choosing the Right Hash Function
Use Pedersen When:
- Working with legacy contracts
- Maintaining compatibility with existing systems
- Following Cairo 0 patterns
- Required by contract specifications
Use Poseidon When:
- Building new applications (recommended)
- Performance is critical
- Working with Cairo 1+ contracts
- Optimizing gas costs
Use Starknet Keccak When:
- Computing selectors (function names, entry points)
- Hashing string or byte data
- Following Starknet conventions for naming
Performance Comparison
package main
import (
"fmt"
"time"
"github.com/NethermindEth/juno/core/felt"
"github.com/NethermindEth/starknet.go/curve"
)
func main() {
a := new(felt.Felt).SetUint64(12345)
b := new(felt.Felt).SetUint64(67890)
iterations := 10000
// Benchmark Pedersen
start := time.Now()
for i := 0; i < iterations; i++ {
_ = curve.Pedersen(a, b)
}
pedersenTime := time.Since(start)
// Benchmark Poseidon
start = time.Now()
for i := 0; i < iterations; i++ {
_ = curve.Poseidon(a, b)
}
poseidonTime := time.Since(start)
fmt.Printf("Pedersen: %v for %d iterations\n", pedersenTime, iterations)
fmt.Printf("Poseidon: %v for %d iterations\n", poseidonTime, iterations)
fmt.Printf("Poseidon is %.2fx faster\n", float64(pedersenTime)/float64(poseidonTime))
}Common Patterns
Computing Contract Address Hash
// Example: Hashing constructor calldata
constructorCalldata := []*big.Int{
big.NewInt(1000), // initial supply
big.NewInt(18), // decimals
}
calldataHash := curve.ComputeHashOnElements(constructorCalldata)
fmt.Printf("Constructor calldata hash: 0x%x\n", calldataHash)Merkle Tree Construction
// Hash pairs of leaves to build Merkle tree
leaf1 := new(felt.Felt).SetUint64(100)
leaf2 := new(felt.Felt).SetUint64(200)
// Use Poseidon for efficiency
parent := curve.Poseidon(leaf1, leaf2)
fmt.Printf("Parent node: %s\n", parent.String())State Commitment
// Compute state root from multiple values
stateValues := []*felt.Felt{
new(felt.Felt).SetUint64(1),
new(felt.Felt).SetUint64(2),
new(felt.Felt).SetUint64(3),
}
stateRoot := curve.PoseidonArray(stateValues...)
fmt.Printf("State root: %s\n", stateRoot.String())Security Considerations
- Hash Function Selection: Use Poseidon for new code unless compatibility requires Pedersen
- Input Validation: Ensure inputs are valid field elements
- Length Encoding: Use
ComputeHashOnElementswhen array length matters - Collision Resistance: All provided hash functions are cryptographically secure
Related Resources
- Cryptographic Functions - Signing and verification using hashes
- Key Operations - Key generation and management
- Starknet Hash Specification

