Tutorial: Poseidon2 Hash Function
This tutorial walks you through building a Poseidon2 hash application using cuPQC-Hash. You'll learn how to use GPU-accelerated Poseidon2, a zero-knowledge-friendly hash function designed for efficient use in ZK proof systems.
API reference
Configurations & constants: Supported parameter combinations (field, width, capacity) and the round constants and MDS matrix for BabyBear and KoalaBear are in the Poseidon2 operators section of the cuPQC-Hash API docs.
Step 1: Project Setup
Clone the cuPQC repository to get the example files:
Step 2: Include Required Headers
Start by including the necessary cuPQC SDK headers:
The hash.hpp header provides access to Poseidon2 and other cryptographic hash primitives.
Step 3: Define Hash Function Type
Define the Poseidon2 hash function type, specifying the field and width:
POSEIDON2_KB_8_16() chooses Poseidon2 with KoalaBear field, capacity 8, and width 16.
Thread() specifies that the operator will execute independently for each thread.
Step 4: Create Hash Kernel
Create a CUDA kernel to compute the Poseidon2 hash:
__global__ void hash_poseidon2_kernel(
uint32_t* digest,
const uint32_t* msg,
size_t inbuf_len,
size_t out_len)
{
HASH hash {};
hash.reset();
hash.update(msg, inbuf_len);
hash.finalize();
hash.digest(digest, out_len);
}
hash.reset(): Initialize the hash state to begin a new hash computation
hash.update(msg, inbuf_len): Add input message data (field elements) to be hashed, can be called multiple times for streaming data
hash.finalize(): Complete the hash process and prepare for digest extraction
hash.digest(digest, out_len): Extract the final hash value (digest) into the output buffer, using out_len to specify the output read size
Only single thread 0 runs it
The hash computation follows a standard pattern: reset the state, update with input message data (field elements), finalize the computation, and then extract the digest. Note that Poseidon2 works with field elements (uint32_t) rather than bytes.
Step 5: Implement Host Function
Create a host function to manage GPU memory and launch the kernel. Only thread 0 performs the hash (single message per block):
void hash_poseidon2(
std::vector<uint32_t>& digest,
std::vector<uint32_t>& msg,
size_t out_len)
{
uint32_t* d_msg;
uint32_t* d_digest;
// Allocate device memory
cudaMalloc(reinterpret_cast<void**>(&d_msg), msg.size() * sizeof(uint32_t));
cudaMalloc(reinterpret_cast<void**>(&d_digest), digest.size() * sizeof(uint32_t));
// Copy input message to device
cudaMemcpy(d_msg, msg.data(), msg.size() * sizeof(uint32_t), cudaMemcpyHostToDevice);
// Launch kernel (1 block, 1 thread)
hash_poseidon2_kernel<<<1, 1>>>(d_digest, d_msg, msg.size(), out_len);
// Copy result back to host
cudaMemcpy(digest.data(), d_digest,
digest.size() * sizeof(uint32_t),
cudaMemcpyDeviceToHost);
// Cleanup
cudaFree(d_msg);
cudaFree(d_digest);
}
Step 6: Create the Main Application
Implement the main function with example usage. It will take a sample message (field elements), compute the Poseidon2 hash, and display the result:
int main(int argc, char* argv[]) {
std::cout << "================================================================\n";
std::cout << "Poseidon2 Hash Function Example\n";
std::cout << "================================================================\n\n";
std::cout << "This example demonstrates Poseidon2 hashing using cuPQC SDK.\n";
std::cout << "Poseidon2 is a zero-knowledge-friendly hash function designed for "
<< "efficient use in zero-knowledge proof systems. It uses arithmetic "
<< "operations native to ZK circuits.\n\n";
std::cout << "Configuration: Capacity 8, Width 16, KoalaBear field\n\n";
constexpr size_t in_len = 64;
constexpr size_t out_len = 16;
std::vector<uint32_t> msg(in_len, 0);
// Generate sample input data (field elements)
for (size_t i = 0; i < in_len; i++) {
msg[i] = i % cupqc_common::BabyBearPrime;
}
std::cout << "========================================\n";
std::cout << "Input Data\n";
std::cout << "========================================\n";
std::cout << "Input size: " << in_len << " field elements ("
<< (in_len * sizeof(uint32_t)) << " bytes)\n";
std::cout << "Input data (first 8 values):\n";
for (size_t i = 0; i < std::min(in_len, size_t(8)); i++) {
std::cout << " Input[" << i << "]: 0x" << std::hex
<< std::setw(8) << std::setfill('0') << msg[i]
<< std::dec << " (" << msg[i] << ")\n";
}
if (in_len > 8) {
std::cout << " ... (showing first 8 of " << in_len << " values)\n";
}
std::cout << "\n";
// Compute hash
std::cout << "========================================\n";
std::cout << "Computing Poseidon2 Hash\n";
std::cout << "========================================\n";
std::vector<uint32_t> digest(out_len, 0);
hash_poseidon2(digest, msg, out_len);
// Display results
std::cout << "Computed Hash (" << out_len << " field elements):\n";
std::cout << " ";
for (size_t i = 0; i < digest.size(); i++) {
std::cout << "0x" << std::hex << std::setw(8)
<< std::setfill('0') << digest[i] << std::dec;
if (i < digest.size() - 1) {
std::cout << " ";
}
}
std::cout << "\n\n";
std::cout << "Hash values (decimal):\n";
std::cout << " ";
for (size_t i = 0; i < digest.size(); i++) {
std::cout << digest[i];
if (i < digest.size() - 1) {
std::cout << " ";
}
}
std::cout << "\n\n";
std::cout << "========================================\n";
std::cout << "Hash Information\n";
std::cout << "========================================\n";
std::cout << "Hash size: " << out_len << " field elements ("
<< (out_len * sizeof(uint32_t)) << " bytes)\n";
std::cout << "Field: KoalaBear\n";
std::cout << "Poseidon2 parameters: Capacity=8, Width=16\n\n";
std::cout << "Example completed successfully.\n";
return 0;
}
Step 7: Build and Run
The Makefile will build all examples in the folder. Run the Poseidon2 example:
Step 8: Understanding the Output
Expected output:
================================================================
Poseidon2 Hash Function Example
================================================================
This example demonstrates Poseidon2 hash using cuPQC SDK.
Poseidon2 is a zero-knowledge-friendly hash function designed for
efficient use in zero-knowledge proof systems. It uses arithmetic
operations native to ZK circuits.
Configuration: Capacity 8, Width 16, KoalaBear field
========================================
Input Data
========================================
Input size: 64 field elements (256 bytes)
Input data (first 8 values):
Input[0]: 0x00000000 (0)
Input[1]: 0x00000001 (1)
Input[2]: 0x00000002 (2)
Input[3]: 0x00000003 (3)
Input[4]: 0x00000004 (4)
Input[5]: 0x00000005 (5)
Input[6]: 0x00000006 (6)
Input[7]: 0x00000007 (7)
... (showing first 8 of 64 values)
========================================
Computing Poseidon2 Hash
========================================
Computed Hash (16 field elements):
0x17fe7d2c 0x0e1f2486 0x26230fb8 0x40272398 0x27f4ab68 0x72080cbe 0x1bbb0dbf 0x06732192
0x49661043 0x5f2bfe0e 0x3dd644f7 0x5b5fb4fa 0x49266af5 0x670d04aa 0x2a6edc71 0x47526886
Hash values (decimal):
402554156 236921990 639831992 1076306840 670346088 1913130174 465243583 108208530
1231425603 1596718606 1037452535 1532998906 1227254517 1728906410 711908465 1196583046
========================================
Hash Information
========================================
Hash size: 16 field elements (64 bytes)
Field: KoalaBear
Poseidon2 parameters: Capacity=8, Width=16
Example completed successfully.
Customization Tips
Use Different Fields and Parameters
Poseidon2 supports different prime fields (BabyBear, KoalaBear) and widths (16, 24). You can change these parameters in the HASH descriptor:
// BabyBear field, Capacity 8, Width 24
using HASH_BB_24 = decltype(POSEIDON2_BB_8_24() + Thread());
// KoalaBear field, Capacity 8, Width 16 (as used in this tutorial)
using HASH_KB_16 = decltype(POSEIDON2_KB_8_16() + Thread());
Learn More
- Example Source Code - Complete Poseidon2 hash example implementation
- cuPQC-Hash User Guide - Usage guide with examples
- cuPQC-Hash API Reference - Complete API documentation
- Hash Operators — Poseidon2 (configurations & constants) - Supported parameter combinations and round constants / MDS matrix per field
- cuPQC-Hash Features - Supported hash functions and capabilities