CAP: 0075
Title: Cryptographic Primitives for Poseidon/Poseidon2 Hash Functions
Working Group:
Owner: Jay Geng <@jayz22>
Authors: Jay Geng <@jayz22>
Consulted: Antonio <@Fantoni0>, Yan <@ymcrcat>, Tomer Weller <@tomerweller>
Status: Final
Created: 2025-10-08
Discussion: https://github.com/orgs/stellar/discussions/1780
Protocol version: 25
This CAP proposes host functions for Poseidon and Poseidon2 permutation primitives.
TBD
Poseidon is a family of hash functions designed specifically for efficient zero-knowledge proof systems, due to its operations being native to the prime fields. Even though proofs are generated off-chain (and proof verification does not require rehashing), having consistent hash functions in the contract logic with the prove-system choice is crucial. Supporting Poseidon as host functions in Soroban can facilitate adoption of ZK applications and interoperability with other ecosystems.
This CAP is aligned with the following Stellar Network Goals:
- The Stellar Network should make it easy for developers of Stellar projects to create highly usable products
- The Stellar Network should facilitate simplicity and interoperability with other protocols and networks
This CAP adds two host functions implementing the Poseidon and Poseidon2 permutation primitives. These primitives operate on field elements from BLS12-381 Fr or BN254 Fr scalar fields. The permutation functions accept configurable parameters, allowing developers to construct hash functions tailored to specific use cases, thus maintaining interoperability with other ZK systems.
Two new functions with export names p and q in the crypto module (c) are added to the Soroban environment's exported interface.
{
"export": "p",
"name": "poseidon_permutation",
"args": [
{ "name": "input", "type": "VecObject" },
{ "name": "field", "type": "U32Val" },
{ "name": "t", "type": "U32Val" },
{ "name": "d", "type": "U32Val" },
{ "name": "rounds_f", "type": "U32Val" },
{ "name": "rounds_p", "type": "U32Val" },
{ "name": "mds", "type": "VecObject" },
{ "name": "round_constants", "type": "VecObject" }
],
"return": "VecObject",
"docs": "Performs Poseidon permutation on input vector. input: vector of field elements (length t). field: 0=BLS12-381 Fr, 1=BN254 Fr. t: state size. d: S-box degree (5 for BLS12-381/BN254). rounds_f: number of full rounds (must be even). rounds_p: number of partial rounds. mds: t-by-t MDS matrix as Vec<Vec<Scalar>>. round_constants: (rounds_f+rounds_p)-by-t round constants matrix as Vec<Vec<Scalar>>. Returns output vector after permutation.",
"min_supported_protocol": 25
},
{
"export": "q",
"name": "poseidon2_permutation",
"args": [
{ "name": "input", "type": "VecObject" },
{ "name": "field", "type": "U32Val" },
{ "name": "t", "type": "U32Val" },
{ "name": "d", "type": "U32Val" },
{ "name": "rounds_f", "type": "U32Val" },
{ "name": "rounds_p", "type": "U32Val" },
{ "name": "mat_internal_diag_m_1", "type": "VecObject" },
{ "name": "round_constants", "type": "VecObject" }
],
"return": "VecObject",
"docs": "Performs Poseidon2 permutation on input vector. input: vector of field elements (length t). field: 0=BLS12-381 Fr, 1=BN254 Fr. t: state size. d: S-box degree (3, 5, 7, or 11). rounds_f: number of full rounds (must be even). rounds_p: number of partial rounds. mat_internal_diag_m_1: internal matrix diagonal minus 1 as Vec<Scalar> (length t). round_constants: (rounds_f+rounds_p)-by-t round constants matrix as Vec<Vec<Scalar>>. Returns output vector after permutation.",
"min_supported_protocol": 25
}diff --git a/Stellar-contract-config-setting.x b/Stellar-contract-config-setting.x
--- a/Stellar-contract-config-setting.x
+++ b/Stellar-contract-config-setting.x
@@ -281,7 +281,17 @@ enum ContractCostType {
// Cost of performing BN254 pairing operation
Bn254Pairing = 78,
// Cost of converting a BN254 scalar element from U256
- Bn254FrFromU256 = 79
+ Bn254FrFromU256 = 79,
+ // Cost of converting a BN254 scalar element to U256
+ Bn254FrToU256 = 80,
+ // // Cost of performing BN254 scalar element addition/subtraction
+ Bn254FrAddSub = 81,
+ // Cost of performing BN254 scalar element multiplication
+ Bn254FrMul = 82,
+ // Cost of performing BN254 scalar element exponentiation
+ Bn254FrPow = 83,
+ // Cost of performing BN254 scalar element inversion
+ Bn254FrInv = 84
};The field parameter specifies the scalar field:
0: BLS12-381 Fr (scalar field order r = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001)1: BN254 Fr (scalar field order r = 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001)
Scalar field elements are represented as U256Val objects as specified in CAP-0059.
Both Poseidon and Poseidon2 are based on the HADES design strategy, which alternates between full rounds and partial rounds:
-
t: State size. The internal state containstfield elements. Common values are 2, 3, 4, or 5 depending on the use case (e.g., binary tree hashing uses t=3). -
d: S-box degree. The S-box is defined as S(x) = x^d. For BLS12-381 and BN254, d=5 is standard. Poseidon2 supports additional S-box degrees (3, 7, 11). -
rounds_f: Number of full rounds. Must be even. In full rounds, the S-box is applied to alltstate elements. -
rounds_p: Number of partial rounds. In partial rounds, the S-box is applied to only one state element.
The total number of rounds is rounds_f + rounds_p.
Poseidon uses an MDS (Maximum Distance Separable) matrix for the linear layer:
mds: At-by-tMDS matrix represented asVec<Vec<Scalar>>. The matrix is typically a Cauchy matrix. During each round, the state vector is multiplied by this matrix.
Poseidon2 uses an optimized internal linear layer:
mat_internal_diag_m_1: Internal matrix diagonal minus 1, represented asVec<Scalar>of lengtht. Poseidon2 replaces the MDS matrix in partial rounds with a more efficient construction that reduces the number of field multiplications.
round_constants: A matrix of size(rounds_f + rounds_p)byt, represented asVec<Vec<Scalar>>. Each round adds a different constant to each state element.
-
input: Vector oftfield elements representing the initial state. Elements areU256Valobjects. -
Return value: Vector of
tfield elements representing the output state after applying the permutation.
The host function will trap if:
inputvector length does not equaltmdsmatrix is nottbyt(Poseidon only)mat_internal_diag_m_1vector length does not equalt(Poseidon2 only)round_constantsmatrix dimensions are not(rounds_f + rounds_p)bytfieldparameter is not 0 or 1
The new cost types Bn254FrFromU256, Bn254FrToU256, Bn254FrAddSub, Bn254FrMul, Bn254FrPow, and Bn254FrInv meter BN254 scalar field operations. For BLS12-381 operations, existing cost types from CAP-0059 are used.
The permutation cost is dominated by field multiplications. The cost scales linearly with the number of rounds and quadratically with the state size t due to matrix operations.
Poseidon is a family of hash functions built using the sponge construction. The sponge construction consists of two phases:
- Absorb: Input data is absorbed into the state, followed by applying the permutation
- Squeeze: Output is extracted from the state after final permutation
The permutation function
This CAP exposes the permutation primitives rather than complete hash functions for the following reasons:
Flexibility: Different applications require different configurations. For Merkle tree hashing in a binary tree, the rate r=2 and capacity c=1 (state size t=3). For Merkle trees with higher arity or different use cases, different parameters are required. By exposing the permutation, developers can construct hash functions with arbitrary input sizes and security parameters.
Interoperability: Different ZK systems use different parameter sets generated according to their security requirements. Exposing the permutation allows Soroban contracts to maintain compatibility with external systems without requiring multiple specialized hash function variants.
Reduced maintenance surface: The permutation contains all cryptographic complexity. The sponge construction is a simple state machine that can be efficiently implemented in the SDK guest code. This minimizes the host function surface area that requires cryptographic review.
Composability: The Poseidon papers describe multiple constructions beyond basic hashing, including compression functions and variable-length hashing modes. Permutation primitives enable all these constructions without requiring additional host functions.
Example SDK usage for a 2-to-1 hash (pseudo-code):
pub fn poseidon2_hash_two(input1: Fr, input2: Fr) -> Fr {
// Initialize state with capacity
let mut state = vec![Fr::zero(); 3];
// Absorb inputs
state[1] = input1;
state[2] = input2;
// Apply permutation
state = poseidon2_permutation(
state,
field_type,
params.t,
params.d,
params.rounds_f,
params.rounds_p,
params.mat_internal_diag_m_1,
params.round_constants
);
// Squeeze output
state[0]
}Parameter selection (state size, rounds, matrices, constants) must follow the security analysis in the Poseidon and Poseidon2 papers. The original papers provide reference scripts for generating secure parameter sets. Users are responsible for selecting parameters appropriate to their security level and use case.
Common parameter sets have been published by the Poseidon authors and adopted by various ZK systems. Developers should use established parameter sets when interoperability with existing systems is required.
This CAP does not introduce backward incompatibilities. The new host functions are only available in protocol version 24 and later.
The security of Poseidon relies on the correctness of the permutation implementation. Implementation must be carefully vetted and verified against official test vectors. Any deviation from the specification could compromise the desired hash properties.
The host function performs minimal parameter validation (vector lengths, field bounds). It does not validate whether the provided parameters (rounds, matrices, constants) constitute a secure configuration. Users are responsible for selecting cryptographically sound parameters.
Insecure parameter selection could result in weakened hash functions vulnerable to collision attacks or preimage attacks. Users should use parameter sets generated according to the methodologies described in the Poseidon and Poseidon2 papers or use widely-adopted standard parameter sets. To mitigate such risk, we will provide presets of securely generated parameters used in production in existing production proof systems (e.g. circom, Noir).
The permutation cost scales with the number of rounds and state size. Improper metering could allow attackers to consume excessive resources. The metering implementation must accurately measure the cost of all field operations within the permutation.
The host function does not impose upper bounds on parameter values (beyond vector length consistency). Resource limits are enforced through the standard Soroban metering framework as specified in CAP-0046-10.
Test vectors will be sourced from:
-
HorizenLabs Poseidon2 implementation (https://github.com/HorizenLabs/poseidon2): Provides reference test vectors for Poseidon2 with multiple field types and parameter configurations.
-
Original Poseidon implementation (https://extgit.isec.tugraz.at/krypto/hadeshash/): Reference implementation from the original Poseidon paper authors.
Draft implementation: stellar/rs-soroban-env#1608