feat(sbf): Add Poseidon syscall (#32680)

Computing Poseidon[0] hashes is too expensive to be done in a Solana
program in one transaction. Poseidon is a zero-knowlege proof friendly
hash function, used by the majority of ZK-based projects, including the
ones built on top of Solana.

This change introduces the `sol_poseidon` syscall which takes 2D byte
slice as an input and then calculates a Poseidon hash using a BN254
curve and the following Poseidon parameters:

* x^5 S-boxes
* width - 2 ≤ t ≤ 13
* inputs - 1 ≤ n ≤ 12
* 8 full rounds and partial rounds depending on t: [56, 57, 56, 60, 60,
  63, 64, 63, 60, 66, 60, 65]

Computation of Poseidon hashes is done with the light-poseidon[1]
crate, which is audited[2] and compatible with Circom[3] (BN254 curve,
the same parameters and constants).

Proposed compute costs depend on number of inputs and are based on
light-poseidon benchmarks[4].

[0] https://www.poseidon-hash.info/
[1] https://crates.io/crates/light-poseidon
[2] https://github.com/Lightprotocol/light-poseidon/blob/main/assets/audit.pdf
[3] https://docs.circom.io/
[4] https://github.com/Lightprotocol/light-poseidon/tree/main#performance
This commit is contained in:
vadorovsky 2023-08-30 18:43:31 +02:00 committed by GitHub
parent 01dbe4970c
commit e6dc3dac45
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 1372 additions and 7 deletions

12
Cargo.lock generated
View File

@ -2963,6 +2963,17 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "light-poseidon"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "949bdd22e4ed93481d45e9a6badb34b99132bcad0c8a8d4f05c42f7dcc7b90bc"
dependencies = [
"ark-bn254",
"ark-ff",
"thiserror",
]
[[package]]
name = "linked-hash-map"
version = "0.5.4"
@ -6478,6 +6489,7 @@ dependencies = [
"lazy_static",
"libc",
"libsecp256k1",
"light-poseidon",
"log",
"memoffset 0.9.0",
"num-bigint 0.4.4",

View File

@ -227,6 +227,7 @@ lazy_static = "1.4.0"
libc = "0.2.147"
libloading = "0.7.4"
libsecp256k1 = "0.6.0"
light-poseidon = "0.1.1"
log = "0.4.20"
lru = "0.7.7"
lz4 = "1.24.0"

View File

@ -122,6 +122,14 @@ pub struct ComputeBudget {
/// Maximum accounts data size, in bytes, that a transaction is allowed to load; The
/// value is capped by MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES to prevent overuse of memory.
pub loaded_accounts_data_size_limit: usize,
/// Coefficient `a` of the quadratic function which determines the number
/// of compute units consumed to call poseidon syscall for a given number
/// of inputs.
pub poseidon_cost_coefficient_a: u64,
/// Coefficient `c` of the quadratic function which determines the number
/// of compute units consumed to call poseidon syscall for a given number
/// of inputs.
pub poseidon_cost_coefficient_c: u64,
}
impl Default for ComputeBudget {
@ -171,6 +179,8 @@ impl ComputeBudget {
alt_bn128_pairing_one_pair_cost_other: 12_121,
big_modular_exponentiation_cost: 33,
loaded_accounts_data_size_limit: MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES,
poseidon_cost_coefficient_a: 61,
poseidon_cost_coefficient_c: 542,
}
}
@ -308,6 +318,35 @@ impl ComputeBudget {
prioritization_fee: prioritization_fee_details.get_fee(),
}
}
/// Returns cost of the Poseidon hash function for the given number of
/// inputs is determined by the following quadratic function:
///
/// 61*n^2 + 542
///
/// Which aproximates the results of benchmarks of light-posiedon
/// library[0]. These results assume 1 CU per 33 ns. Examples:
///
/// * 1 input
/// * light-poseidon benchmark: `18,303 / 33 ≈ 555`
/// * function: `61*1^2 + 542 = 603`
/// * 2 inputs
/// * light-poseidon benchmark: `25,866 / 33 ≈ 784`
/// * function: `61*2^2 + 542 = 786`
/// * 3 inputs
/// * light-poseidon benchmark: `37,549 / 33 ≈ 1,138`
/// * function; `61*3^2 + 542 = 1091`
///
/// [0] https://github.com/Lightprotocol/light-poseidon#performance
pub fn poseidon_cost(&self, nr_inputs: u64) -> Option<u64> {
let squared_inputs = nr_inputs.checked_pow(2)?;
let mul_result = self
.poseidon_cost_coefficient_a
.checked_mul(squared_inputs)?;
let final_result = mul_result.checked_add(self.poseidon_cost_coefficient_c)?;
Some(final_result)
}
}
#[cfg(test)]

View File

@ -37,8 +37,8 @@ use {
disable_cpi_setting_executable_and_rent_epoch, disable_deploy_of_alloc_free_syscall,
disable_fees_sysvar, enable_alt_bn128_syscall, enable_big_mod_exp_syscall,
enable_early_verification_of_account_modifications, enable_partitioned_epoch_reward,
error_on_syscall_bpf_function_hash_collisions, last_restart_slot_sysvar,
libsecp256k1_0_5_upgrade_enabled, reject_callx_r10,
enable_poseidon_syscall, error_on_syscall_bpf_function_hash_collisions,
last_restart_slot_sysvar, libsecp256k1_0_5_upgrade_enabled, reject_callx_r10,
stop_sibling_instruction_search_at_parent, stop_truncating_strings_in_syscalls,
switch_to_new_elf_parser,
},
@ -47,7 +47,7 @@ use {
AccountMeta, InstructionError, ProcessedSiblingInstruction,
TRANSACTION_LEVEL_STACK_HEIGHT,
},
keccak, native_loader,
keccak, native_loader, poseidon,
precompiles::is_precompile,
program::MAX_RETURN_DATA,
program_stubs::is_nonoverlapping,
@ -125,6 +125,8 @@ pub enum SyscallError {
InvalidAttribute,
#[error("Invalid pointer")]
InvalidPointer,
#[error("Arithmetic overflow")]
ArithmeticOverflow,
}
type Error = Box<dyn std::error::Error>;
@ -160,6 +162,7 @@ pub fn create_program_runtime_environment_v1<'a>(
let disable_deploy_of_alloc_free_syscall = reject_deployment_of_broken_elfs
&& feature_set.is_active(&disable_deploy_of_alloc_free_syscall::id());
let last_restart_slot_syscall_enabled = feature_set.is_active(&last_restart_slot_sysvar::id());
let enable_poseidon_syscall = feature_set.is_active(&enable_poseidon_syscall::id());
// !!! ATTENTION !!!
// When adding new features for RBPF here,
// also add them to `Bank::apply_builtin_program_feature_transitions()`.
@ -327,6 +330,14 @@ pub fn create_program_runtime_environment_v1<'a>(
SyscallBigModExp::call,
)?;
// Poseidon
register_feature_gated_function!(
result,
enable_poseidon_syscall,
b"sol_poseidon",
SyscallPoseidon::call,
)?;
// Log data
result.register_function(b"sol_log_data", SyscallLogData::call)?;
@ -1795,6 +1806,78 @@ declare_syscall!(
}
);
declare_syscall!(
// Poseidon
SyscallPoseidon,
fn inner_call(
invoke_context: &mut InvokeContext,
parameters: u64,
endianness: u64,
vals_addr: u64,
vals_len: u64,
result_addr: u64,
memory_mapping: &mut MemoryMapping,
) -> Result<u64, Error> {
let parameters: poseidon::Parameters = parameters.try_into()?;
let endianness: poseidon::Endianness = endianness.try_into()?;
if vals_len > 12 {
ic_msg!(
invoke_context,
"Poseidon hashing {} sequences is not supported",
vals_len,
);
return Err(SyscallError::InvalidLength.into());
}
let budget = invoke_context.get_compute_budget();
let Some(cost) = budget.poseidon_cost(vals_len) else {
ic_msg!(
invoke_context,
"Overflow while calculating the compute cost"
);
return Err(SyscallError::ArithmeticOverflow.into());
};
consume_compute_meter(invoke_context, cost.to_owned())?;
let hash_result = translate_slice_mut::<u8>(
memory_mapping,
result_addr,
poseidon::HASH_BYTES as u64,
invoke_context.get_check_aligned(),
invoke_context.get_check_size(),
)?;
let inputs = translate_slice::<&[u8]>(
memory_mapping,
vals_addr,
vals_len,
invoke_context.get_check_aligned(),
invoke_context.get_check_size(),
)?;
let inputs = inputs
.iter()
.map(|input| {
translate_slice::<u8>(
memory_mapping,
input.as_ptr() as *const _ as u64,
input.len() as u64,
invoke_context.get_check_aligned(),
invoke_context.get_check_size(),
)
})
.collect::<Result<Vec<_>, Error>>()?;
let hash = match poseidon::hashv(parameters, endianness, inputs.as_slice()) {
Ok(hash) => hash,
Err(e) => {
return Ok(e.into());
}
};
hash_result.copy_from_slice(&hash.to_bytes());
Ok(SUCCESS)
}
);
#[cfg(test)]
#[allow(clippy::integer_arithmetic)]
#[allow(clippy::indexing_slicing)]

View File

@ -2603,6 +2603,17 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "light-poseidon"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "949bdd22e4ed93481d45e9a6badb34b99132bcad0c8a8d4f05c42f7dcc7b90bc"
dependencies = [
"ark-bn254",
"ark-ff",
"thiserror",
]
[[package]]
name = "linux-raw-sys"
version = "0.4.3"
@ -5248,6 +5259,7 @@ dependencies = [
"lazy_static",
"libc",
"libsecp256k1 0.6.0",
"light-poseidon",
"log",
"memoffset 0.9.0",
"num-bigint 0.4.4",
@ -5848,6 +5860,14 @@ dependencies = [
"solana-program",
]
[[package]]
name = "solana-sbf-rust-poseidon"
version = "1.17.0"
dependencies = [
"array-bytes",
"solana-program",
]
[[package]]
name = "solana-sbf-rust-rand"
version = "1.17.0"

View File

@ -140,6 +140,7 @@ members = [
"rust/panic",
"rust/param_passing",
"rust/param_passing_dep",
"rust/poseidon",
"rust/rand",
"rust/realloc",
"rust/realloc_invoke",

View File

@ -92,6 +92,7 @@ fn main() {
"noop",
"panic",
"param_passing",
"poseidon",
"rand",
"realloc",
"realloc_invoke",

View File

@ -0,0 +1,359 @@
/**
* @brief Poseidon syscall test
*/
#include <sol/assert.h>
#include <sol/poseidon.h>
#include <sol/string.h>
extern uint64_t entrypoint(const uint8_t *input) {
// Two inputs: ones and twos (big-endian).
{
uint8_t result[POSEIDON_RESULT_LENGTH];
uint8_t expected[] = {13, 84, 225, 147, 143, 138, 140, 28, 125, 235, 94,
3, 85, 242, 99, 25, 32, 123, 132, 254, 156, 162,
206, 27, 38, 231, 53, 200, 41, 130, 25, 144};
uint8_t input1[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
uint8_t input2[] = {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2};
const SolBytes inputs[] = {{input1, SOL_ARRAY_SIZE(input1)},
{input2, SOL_ARRAY_SIZE(input2)}};
sol_poseidon(
POSEIDON_PARAMETERS_BN254_X5,
POSEIDON_ENDIANNESS_BIG_ENDIAN,
inputs,
SOL_ARRAY_SIZE(inputs),
result
);
sol_assert(0 == sol_memcmp(result, expected, POSEIDON_RESULT_LENGTH));
}
// Two inputs: ones and twos (little-endian).
{
uint8_t result[POSEIDON_RESULT_LENGTH];
uint8_t expected[] = {144, 25, 130, 41, 200, 53, 231, 38, 27, 206, 162,
156, 254, 132, 123, 32, 25, 99, 242, 85, 3, 94,
235, 125, 28, 140, 138, 143, 147, 225, 84, 13};
uint8_t input1[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
uint8_t input2[] = {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2};
const SolBytes inputs[] = {{input1, SOL_ARRAY_SIZE(input1)},
{input2, SOL_ARRAY_SIZE(input2)}};
sol_poseidon(
POSEIDON_PARAMETERS_BN254_X5,
POSEIDON_ENDIANNESS_LITTLE_ENDIAN,
inputs,
SOL_ARRAY_SIZE(inputs),
result
);
sol_assert(0 == sol_memcmp(result, expected, POSEIDON_RESULT_LENGTH));
}
uint8_t input1[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
};
// 1 input.
{
uint8_t result[POSEIDON_RESULT_LENGTH];
uint8_t expected[] = {
41, 23, 97, 0, 234, 169, 98, 189, 193, 254, 108,
101, 77, 106, 60, 19, 14, 150, 164, 209, 22, 139,
51, 132, 139, 137, 125, 197, 2, 130, 1, 51,
};
const SolBytes inputs[] = {{input1, SOL_ARRAY_SIZE(input1)}};
sol_poseidon(
POSEIDON_PARAMETERS_BN254_X5,
POSEIDON_ENDIANNESS_BIG_ENDIAN,
inputs,
SOL_ARRAY_SIZE(inputs),
result
);
sol_assert(0 == sol_memcmp(result, expected, POSEIDON_RESULT_LENGTH));
}
// 2 inputs.
{
uint8_t result[POSEIDON_RESULT_LENGTH];
uint8_t expected[] = {
0, 122, 243, 70, 226, 211, 4, 39, 158, 121, 224,
169, 243, 2, 63, 119, 18, 148, 167, 138, 203, 112,
231, 63, 144, 175, 226, 124, 173, 64, 30, 129,
};
const SolBytes inputs[] = {{input1, SOL_ARRAY_SIZE(input1)},
{input1, SOL_ARRAY_SIZE(input1)}};
sol_poseidon(
POSEIDON_PARAMETERS_BN254_X5,
POSEIDON_ENDIANNESS_BIG_ENDIAN,
inputs,
SOL_ARRAY_SIZE(inputs),
result
);
sol_assert(0 == sol_memcmp(result, expected, POSEIDON_RESULT_LENGTH));
}
// 3 inputs.
{
uint8_t result[POSEIDON_RESULT_LENGTH];
uint8_t expected[] = {
2, 192, 6, 110, 16, 167, 42, 189, 43, 51, 195,
178, 20, 203, 62, 129, 188, 177, 182, 227, 9, 97,
205, 35, 194, 2, 177, 134, 115, 191, 37, 67,
};
const SolBytes inputs[] = {{input1, SOL_ARRAY_SIZE(input1)},
{input1, SOL_ARRAY_SIZE(input1)},
{input1, SOL_ARRAY_SIZE(input1)}};
sol_poseidon(
POSEIDON_PARAMETERS_BN254_X5,
POSEIDON_ENDIANNESS_BIG_ENDIAN,
inputs,
SOL_ARRAY_SIZE(inputs),
result
);
sol_assert(0 == sol_memcmp(result, expected, POSEIDON_RESULT_LENGTH));
}
// 4 inputs.
{
uint8_t result[POSEIDON_RESULT_LENGTH];
uint8_t expected[] = {
8, 44, 156, 55, 10, 13, 36, 244, 65, 111, 188,
65, 74, 55, 104, 31, 120, 68, 45, 39, 216, 99,
133, 153, 28, 23, 214, 252, 12, 75, 125, 113,
};
const SolBytes inputs[] = {{input1, SOL_ARRAY_SIZE(input1)},
{input1, SOL_ARRAY_SIZE(input1)},
{input1, SOL_ARRAY_SIZE(input1)},
{input1, SOL_ARRAY_SIZE(input1)}};
sol_poseidon(
POSEIDON_PARAMETERS_BN254_X5,
POSEIDON_ENDIANNESS_BIG_ENDIAN,
inputs,
SOL_ARRAY_SIZE(inputs),
result
);
sol_assert(0 == sol_memcmp(result, expected, POSEIDON_RESULT_LENGTH));
}
// 5 inputs.
{
uint8_t result[POSEIDON_RESULT_LENGTH];
uint8_t expected[] = {
16, 56, 150, 5, 174, 104, 141, 79, 20, 219, 133,
49, 34, 196, 125, 102, 168, 3, 199, 43, 65, 88,
156, 177, 191, 134, 135, 65, 178, 6, 185, 187,
};
const SolBytes inputs[] = {{input1, SOL_ARRAY_SIZE(input1)},
{input1, SOL_ARRAY_SIZE(input1)},
{input1, SOL_ARRAY_SIZE(input1)},
{input1, SOL_ARRAY_SIZE(input1)},
{input1, SOL_ARRAY_SIZE(input1)}};
sol_poseidon(
POSEIDON_PARAMETERS_BN254_X5,
POSEIDON_ENDIANNESS_BIG_ENDIAN,
inputs,
SOL_ARRAY_SIZE(inputs),
result
);
sol_assert(0 == sol_memcmp(result, expected, POSEIDON_RESULT_LENGTH));
}
// 6 inputs.
{
uint8_t result[POSEIDON_RESULT_LENGTH];
uint8_t expected[] = {
42, 115, 246, 121, 50, 140, 62, 171, 114, 74, 163,
229, 189, 191, 80, 179, 144, 53, 215, 114, 159, 19,
91, 151, 9, 137, 15, 133, 197, 220, 94, 118,
};
const SolBytes inputs[] = {
{input1, SOL_ARRAY_SIZE(input1)}, {input1, SOL_ARRAY_SIZE(input1)},
{input1, SOL_ARRAY_SIZE(input1)}, {input1, SOL_ARRAY_SIZE(input1)},
{input1, SOL_ARRAY_SIZE(input1)}, {input1, SOL_ARRAY_SIZE(input1)}};
sol_poseidon(
POSEIDON_PARAMETERS_BN254_X5,
POSEIDON_ENDIANNESS_BIG_ENDIAN,
inputs,
SOL_ARRAY_SIZE(inputs),
result
);
sol_assert(0 == sol_memcmp(result, expected, POSEIDON_RESULT_LENGTH));
}
// 7 inputs.
{
uint8_t result[POSEIDON_RESULT_LENGTH];
uint8_t expected[] = {
34, 118, 49, 10, 167, 243, 52, 58, 40, 66, 20,
19, 157, 157, 169, 89, 190, 42, 49, 178, 199, 8,
165, 248, 25, 84, 178, 101, 229, 58, 48, 184,
};
const SolBytes inputs[] = {
{input1, SOL_ARRAY_SIZE(input1)}, {input1, SOL_ARRAY_SIZE(input1)},
{input1, SOL_ARRAY_SIZE(input1)}, {input1, SOL_ARRAY_SIZE(input1)},
{input1, SOL_ARRAY_SIZE(input1)}, {input1, SOL_ARRAY_SIZE(input1)},
{input1, SOL_ARRAY_SIZE(input1)}};
sol_poseidon(
POSEIDON_PARAMETERS_BN254_X5,
POSEIDON_ENDIANNESS_BIG_ENDIAN,
inputs,
SOL_ARRAY_SIZE(inputs),
result
);
sol_assert(0 == sol_memcmp(result, expected, POSEIDON_RESULT_LENGTH));
}
// 8 inputs.
{
uint8_t result[POSEIDON_RESULT_LENGTH];
uint8_t expected[] = {
23, 126, 20, 83, 196, 70, 225, 176, 125, 43, 66,
51, 66, 81, 71, 9, 92, 79, 202, 187, 35, 61,
35, 11, 109, 70, 162, 20, 217, 91, 40, 132,
};
const SolBytes inputs[] = {
{input1, SOL_ARRAY_SIZE(input1)}, {input1, SOL_ARRAY_SIZE(input1)},
{input1, SOL_ARRAY_SIZE(input1)}, {input1, SOL_ARRAY_SIZE(input1)},
{input1, SOL_ARRAY_SIZE(input1)}, {input1, SOL_ARRAY_SIZE(input1)},
{input1, SOL_ARRAY_SIZE(input1)}, {input1, SOL_ARRAY_SIZE(input1)}};
sol_poseidon(
POSEIDON_PARAMETERS_BN254_X5,
POSEIDON_ENDIANNESS_BIG_ENDIAN,
inputs,
SOL_ARRAY_SIZE(inputs),
result
);
sol_assert(0 == sol_memcmp(result, expected, POSEIDON_RESULT_LENGTH));
}
// 9 inputs.
{
uint8_t result[POSEIDON_RESULT_LENGTH];
uint8_t expected[] = {
14, 143, 238, 47, 228, 157, 163, 15, 222, 235, 72,
196, 46, 187, 68, 204, 110, 231, 5, 95, 97, 251,
202, 94, 49, 59, 138, 95, 202, 131, 76, 71,
};
const SolBytes inputs[] = {
{input1, SOL_ARRAY_SIZE(input1)}, {input1, SOL_ARRAY_SIZE(input1)},
{input1, SOL_ARRAY_SIZE(input1)}, {input1, SOL_ARRAY_SIZE(input1)},
{input1, SOL_ARRAY_SIZE(input1)}, {input1, SOL_ARRAY_SIZE(input1)},
{input1, SOL_ARRAY_SIZE(input1)}, {input1, SOL_ARRAY_SIZE(input1)},
{input1, SOL_ARRAY_SIZE(input1)}};
sol_poseidon(
POSEIDON_PARAMETERS_BN254_X5,
POSEIDON_ENDIANNESS_BIG_ENDIAN,
inputs,
SOL_ARRAY_SIZE(inputs),
result
);
sol_assert(0 == sol_memcmp(result, expected, POSEIDON_RESULT_LENGTH));
}
// 10 inputs.
{
uint8_t result[POSEIDON_RESULT_LENGTH];
uint8_t expected[] = {
46, 196, 198, 94, 99, 120, 171, 140, 115, 48, 133,
79, 74, 112, 119, 193, 255, 146, 96, 228, 72, 133,
196, 184, 29, 209, 49, 173, 58, 134, 205, 150,
};
const SolBytes inputs[] = {
{input1, SOL_ARRAY_SIZE(input1)}, {input1, SOL_ARRAY_SIZE(input1)},
{input1, SOL_ARRAY_SIZE(input1)}, {input1, SOL_ARRAY_SIZE(input1)},
{input1, SOL_ARRAY_SIZE(input1)}, {input1, SOL_ARRAY_SIZE(input1)},
{input1, SOL_ARRAY_SIZE(input1)}, {input1, SOL_ARRAY_SIZE(input1)},
{input1, SOL_ARRAY_SIZE(input1)}, {input1, SOL_ARRAY_SIZE(input1)}};
sol_poseidon(
POSEIDON_PARAMETERS_BN254_X5,
POSEIDON_ENDIANNESS_BIG_ENDIAN,
inputs,
SOL_ARRAY_SIZE(inputs),
result
);
sol_assert(0 == sol_memcmp(result, expected, POSEIDON_RESULT_LENGTH));
}
// 11 inputs.
{
uint8_t result[POSEIDON_RESULT_LENGTH];
uint8_t expected[] = {
0, 113, 61, 65, 236, 166, 53, 241, 23, 212, 236,
188, 235, 95, 58, 102, 220, 65, 66, 235, 112, 181,
103, 101, 188, 53, 143, 27, 236, 64, 187, 155,
};
const SolBytes inputs[] = {
{input1, SOL_ARRAY_SIZE(input1)}, {input1, SOL_ARRAY_SIZE(input1)},
{input1, SOL_ARRAY_SIZE(input1)}, {input1, SOL_ARRAY_SIZE(input1)},
{input1, SOL_ARRAY_SIZE(input1)}, {input1, SOL_ARRAY_SIZE(input1)},
{input1, SOL_ARRAY_SIZE(input1)}, {input1, SOL_ARRAY_SIZE(input1)},
{input1, SOL_ARRAY_SIZE(input1)}, {input1, SOL_ARRAY_SIZE(input1)},
{input1, SOL_ARRAY_SIZE(input1)}};
sol_poseidon(
POSEIDON_PARAMETERS_BN254_X5,
POSEIDON_ENDIANNESS_BIG_ENDIAN,
inputs,
SOL_ARRAY_SIZE(inputs),
result
);
sol_assert(0 == sol_memcmp(result, expected, POSEIDON_RESULT_LENGTH));
}
// 12 inputs.
{
uint8_t result[POSEIDON_RESULT_LENGTH];
uint8_t expected[] = {
20, 57, 11, 224, 186, 239, 36, 155, 212, 124, 101,
221, 172, 101, 194, 229, 46, 133, 19, 192, 129, 193,
205, 114, 201, 128, 6, 9, 142, 154, 143, 190,
};
const SolBytes inputs[] = {
{input1, SOL_ARRAY_SIZE(input1)}, {input1, SOL_ARRAY_SIZE(input1)},
{input1, SOL_ARRAY_SIZE(input1)}, {input1, SOL_ARRAY_SIZE(input1)},
{input1, SOL_ARRAY_SIZE(input1)}, {input1, SOL_ARRAY_SIZE(input1)},
{input1, SOL_ARRAY_SIZE(input1)}, {input1, SOL_ARRAY_SIZE(input1)},
{input1, SOL_ARRAY_SIZE(input1)}, {input1, SOL_ARRAY_SIZE(input1)},
{input1, SOL_ARRAY_SIZE(input1)}, {input1, SOL_ARRAY_SIZE(input1)}};
sol_poseidon(
POSEIDON_PARAMETERS_BN254_X5,
POSEIDON_ENDIANNESS_BIG_ENDIAN,
inputs,
SOL_ARRAY_SIZE(inputs),
result
);
sol_assert(0 == sol_memcmp(result, expected, POSEIDON_RESULT_LENGTH));
}
return SUCCESS;
}

View File

@ -0,0 +1,19 @@
[package]
name = "solana-sbf-rust-poseidon"
description = "Solana SBF test program written in Rust"
version = { workspace = true }
authors = { workspace = true }
repository = { workspace = true }
homepage = { workspace = true }
license = { workspace = true }
edition = { workspace = true }
[dependencies]
array-bytes = { workspace = true }
solana-program = { workspace = true }
[lib]
crate-type = ["cdylib"]
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@ -0,0 +1,230 @@
//! Example SBF program using Poseidon syscall
use solana_program::{
custom_heap_default, custom_panic_default, msg,
poseidon::{hashv, Endianness, Parameters, PoseidonSyscallError},
};
fn test_poseidon_input_ones_twos() -> Result<(), PoseidonSyscallError> {
let input1 = [1u8; 32];
let input2 = [2u8; 32];
let hash = hashv(
Parameters::Bn254X5,
Endianness::BigEndian,
&[&input1, &input2],
)?;
assert_eq!(
hash.to_bytes(),
[
13, 84, 225, 147, 143, 138, 140, 28, 125, 235, 94, 3, 85, 242, 99, 25, 32, 123, 132,
254, 156, 162, 206, 27, 38, 231, 53, 200, 41, 130, 25, 144
]
);
let hash = hashv(
Parameters::Bn254X5,
Endianness::LittleEndian,
&[&input1, &input2],
)?;
assert_eq!(
hash.to_bytes(),
[
144, 25, 130, 41, 200, 53, 231, 38, 27, 206, 162, 156, 254, 132, 123, 32, 25, 99, 242,
85, 3, 94, 235, 125, 28, 140, 138, 143, 147, 225, 84, 13
],
);
Ok(())
}
fn test_poseidon_input_one() -> Result<(), PoseidonSyscallError> {
let input = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1,
];
let hash = hashv(Parameters::Bn254X5, Endianness::BigEndian, &[&input])?;
assert_eq!(
hash.to_bytes(),
[
41, 23, 97, 0, 234, 169, 98, 189, 193, 254, 108, 101, 77, 106, 60, 19, 14, 150, 164,
209, 22, 139, 51, 132, 139, 137, 125, 197, 2, 130, 1, 51,
],
);
let hash = hashv(
Parameters::Bn254X5,
Endianness::BigEndian,
&[&input, &input],
)?;
assert_eq!(
hash.to_bytes(),
[
0, 122, 243, 70, 226, 211, 4, 39, 158, 121, 224, 169, 243, 2, 63, 119, 18, 148, 167,
138, 203, 112, 231, 63, 144, 175, 226, 124, 173, 64, 30, 129,
],
);
let hash = hashv(
Parameters::Bn254X5,
Endianness::BigEndian,
&[&input, &input, &input],
)?;
assert_eq!(
hash.to_bytes(),
[
2, 192, 6, 110, 16, 167, 42, 189, 43, 51, 195, 178, 20, 203, 62, 129, 188, 177, 182,
227, 9, 97, 205, 35, 194, 2, 177, 134, 115, 191, 37, 67,
],
);
let hash = hashv(
Parameters::Bn254X5,
Endianness::BigEndian,
&[&input, &input, &input, &input],
)?;
assert_eq!(
hash.to_bytes(),
[
8, 44, 156, 55, 10, 13, 36, 244, 65, 111, 188, 65, 74, 55, 104, 31, 120, 68, 45, 39,
216, 99, 133, 153, 28, 23, 214, 252, 12, 75, 125, 113,
],
);
let hash = hashv(
Parameters::Bn254X5,
Endianness::BigEndian,
&[&input, &input, &input, &input, &input],
)?;
assert_eq!(
hash.to_bytes(),
[
16, 56, 150, 5, 174, 104, 141, 79, 20, 219, 133, 49, 34, 196, 125, 102, 168, 3, 199,
43, 65, 88, 156, 177, 191, 134, 135, 65, 178, 6, 185, 187,
],
);
let hash = hashv(
Parameters::Bn254X5,
Endianness::BigEndian,
&[&input, &input, &input, &input, &input, &input],
)?;
assert_eq!(
hash.to_bytes(),
[
42, 115, 246, 121, 50, 140, 62, 171, 114, 74, 163, 229, 189, 191, 80, 179, 144, 53,
215, 114, 159, 19, 91, 151, 9, 137, 15, 133, 197, 220, 94, 118,
],
);
let hash = hashv(
Parameters::Bn254X5,
Endianness::BigEndian,
&[&input, &input, &input, &input, &input, &input, &input],
)?;
assert_eq!(
hash.to_bytes(),
[
34, 118, 49, 10, 167, 243, 52, 58, 40, 66, 20, 19, 157, 157, 169, 89, 190, 42, 49, 178,
199, 8, 165, 248, 25, 84, 178, 101, 229, 58, 48, 184,
],
);
let hash = hashv(
Parameters::Bn254X5,
Endianness::BigEndian,
&[
&input, &input, &input, &input, &input, &input, &input, &input,
],
)?;
assert_eq!(
hash.to_bytes(),
[
23, 126, 20, 83, 196, 70, 225, 176, 125, 43, 66, 51, 66, 81, 71, 9, 92, 79, 202, 187,
35, 61, 35, 11, 109, 70, 162, 20, 217, 91, 40, 132,
],
);
let hash = hashv(
Parameters::Bn254X5,
Endianness::BigEndian,
&[
&input, &input, &input, &input, &input, &input, &input, &input, &input,
],
)?;
assert_eq!(
hash.to_bytes(),
[
14, 143, 238, 47, 228, 157, 163, 15, 222, 235, 72, 196, 46, 187, 68, 204, 110, 231, 5,
95, 97, 251, 202, 94, 49, 59, 138, 95, 202, 131, 76, 71,
],
);
let hash = hashv(
Parameters::Bn254X5,
Endianness::BigEndian,
&[
&input, &input, &input, &input, &input, &input, &input, &input, &input, &input,
],
)?;
assert_eq!(
hash.to_bytes(),
[
46, 196, 198, 94, 99, 120, 171, 140, 115, 48, 133, 79, 74, 112, 119, 193, 255, 146, 96,
228, 72, 133, 196, 184, 29, 209, 49, 173, 58, 134, 205, 150,
],
);
let hash = hashv(
Parameters::Bn254X5,
Endianness::BigEndian,
&[
&input, &input, &input, &input, &input, &input, &input, &input, &input, &input, &input,
],
)?;
assert_eq!(
hash.to_bytes(),
[
0, 113, 61, 65, 236, 166, 53, 241, 23, 212, 236, 188, 235, 95, 58, 102, 220, 65, 66,
235, 112, 181, 103, 101, 188, 53, 143, 27, 236, 64, 187, 155,
],
);
let hash = hashv(
Parameters::Bn254X5,
Endianness::BigEndian,
&[
&input, &input, &input, &input, &input, &input, &input, &input, &input, &input, &input,
&input,
],
)?;
assert_eq!(
hash.to_bytes(),
[
20, 57, 11, 224, 186, 239, 36, 155, 212, 124, 101, 221, 172, 101, 194, 229, 46, 133,
19, 192, 129, 193, 205, 114, 201, 128, 6, 9, 142, 154, 143, 190,
],
);
Ok(())
}
#[no_mangle]
pub extern "C" fn entrypoint(_input: *mut u8) -> u64 {
msg!("poseidon_hash");
if let Err(e) = test_poseidon_input_ones_twos() {
return e.into();
}
if let Err(e) = test_poseidon_input_one() {
return e.into();
}
0
}
custom_heap_default!();
custom_panic_default!();

View File

@ -285,6 +285,7 @@ fn test_program_sbf_sanity() {
("noop", true),
("noop++", true),
("panic", false),
("poseidon", true),
("relative_call", true),
("return_data", true),
("sanity", true),
@ -312,6 +313,7 @@ fn test_program_sbf_sanity() {
("solana_sbf_rust_noop", true),
("solana_sbf_rust_panic", false),
("solana_sbf_rust_param_passing", true),
("solana_sbf_rust_poseidon", true),
("solana_sbf_rust_rand", true),
("solana_sbf_rust_sanity", true),
("solana_sbf_rust_secp256k1_recover", true),

View File

@ -58,6 +58,7 @@ curve25519-dalek = { workspace = true, features = ["serde"] }
itertools = { workspace = true }
libc = { workspace = true, features = ["extra_traits"] }
libsecp256k1 = { workspace = true }
light-poseidon = { workspace = true }
num-bigint = { workspace = true }
rand = { workspace = true }
tiny-bip39 = { workspace = true }

View File

@ -506,6 +506,7 @@ pub mod log;
pub mod message;
pub mod native_token;
pub mod nonce;
pub mod poseidon;
pub mod program;
pub mod program_error;
pub mod program_memory;

444
sdk/program/src/poseidon.rs Normal file
View File

@ -0,0 +1,444 @@
//! Hashing with the [Poseidon] hash function.
//!
//! [Poseidon]: https://www.poseidon-hash.info/
use thiserror::Error;
/// Length of Poseidon hash result.
pub const HASH_BYTES: usize = 32;
#[derive(Error, Debug)]
pub enum PoseidonSyscallError {
#[error("Invalid parameters.")]
InvalidParameters,
#[error("Invalid endianness.")]
InvalidEndianness,
#[error("Invalid number of inputs. Maximum allowed is 12.")]
InvalidNumberOfInputs,
#[error("Input is an empty slice.")]
EmptyInput,
#[error(
"Invalid length of the input. The length matching the modulus of the prime field is 32."
)]
InvalidInputLength,
#[error("Input is larger than the modulus of the prime field.")]
InputLargerThanModulus,
#[error("Failed to convert a vector of bytes into an array.")]
VecToArray,
#[error("Failed to convert the number of inputs from u64 to u8.")]
U64Tou8,
#[error("Invalid width. Choose a width between 2 and 16 for 1 to 15 inputs.")]
InvalidWidthCircom,
#[error("Unexpected error")]
Unexpected,
}
impl From<u64> for PoseidonSyscallError {
fn from(error: u64) -> Self {
match error {
1 => PoseidonSyscallError::InvalidParameters,
2 => PoseidonSyscallError::InvalidEndianness,
3 => PoseidonSyscallError::InvalidNumberOfInputs,
4 => PoseidonSyscallError::EmptyInput,
5 => PoseidonSyscallError::InvalidInputLength,
6 => PoseidonSyscallError::InputLargerThanModulus,
7 => PoseidonSyscallError::VecToArray,
8 => PoseidonSyscallError::U64Tou8,
9 => PoseidonSyscallError::InvalidWidthCircom,
_ => PoseidonSyscallError::Unexpected,
}
}
}
impl From<PoseidonSyscallError> for u64 {
fn from(error: PoseidonSyscallError) -> Self {
match error {
PoseidonSyscallError::InvalidParameters => 1,
PoseidonSyscallError::InvalidEndianness => 2,
PoseidonSyscallError::InvalidNumberOfInputs => 3,
PoseidonSyscallError::EmptyInput => 4,
PoseidonSyscallError::InvalidInputLength => 5,
PoseidonSyscallError::InputLargerThanModulus => 6,
PoseidonSyscallError::VecToArray => 7,
PoseidonSyscallError::U64Tou8 => 8,
PoseidonSyscallError::InvalidWidthCircom => 9,
PoseidonSyscallError::Unexpected => 10,
}
}
}
/// Configuration parameters for the Poseidon hash function.
///
/// The parameters of each configuration consist of:
///
/// - **Elliptic curve type**: This defines the prime field in which the
/// cryptographic operations are conducted.
/// - **S-Box**: The substitution box used in the cryptographic rounds.
/// - **Full rounds**: The number of full transformation rounds in the hash
/// function.
/// - **Partial rounds**: The number of partial transformation rounds in the
/// hash function.
///
/// Each configuration variant's name is composed of its elliptic curve type
/// followed by its S-Box specification.
#[repr(u64)]
pub enum Parameters {
/// Configuration using the BarretoNaehrig curve with an embedding degree
/// of 12, defined over a 254-bit prime field.
///
/// Configuration Details:
/// - **S-Box**: \( x^5 \)
/// - **Width**: \( 2 \leq t \leq 13 \)
/// - **Inputs**: \( 1 \leq n \leq 12 \)
/// - **Full rounds**: 8
/// - **Partial rounds**: Depending on width: [56, 57, 56, 60, 60, 63, 64,
/// 63, 60, 66, 60, 65]
Bn254X5 = 0,
}
impl TryFrom<u64> for Parameters {
type Error = PoseidonSyscallError;
fn try_from(value: u64) -> Result<Self, Self::Error> {
match value {
x if x == Parameters::Bn254X5 as u64 => Ok(Parameters::Bn254X5),
_ => Err(PoseidonSyscallError::InvalidParameters),
}
}
}
impl From<Parameters> for u64 {
fn from(value: Parameters) -> Self {
match value {
Parameters::Bn254X5 => 0,
}
}
}
/// Endianness of inputs and result.
#[repr(u64)]
pub enum Endianness {
/// Big-endian inputs and result.
BigEndian = 0,
/// Little-endian inputs and result.
LittleEndian,
}
impl TryFrom<u64> for Endianness {
type Error = PoseidonSyscallError;
fn try_from(value: u64) -> Result<Self, Self::Error> {
match value {
x if x == Endianness::BigEndian as u64 => Ok(Endianness::BigEndian),
x if x == Endianness::LittleEndian as u64 => Ok(Endianness::LittleEndian),
_ => Err(PoseidonSyscallError::InvalidEndianness),
}
}
}
impl From<Endianness> for u64 {
fn from(value: Endianness) -> Self {
match value {
Endianness::BigEndian => 0,
Endianness::LittleEndian => 1,
}
}
}
/// Poseidon hash result.
#[repr(transparent)]
pub struct PoseidonHash(pub [u8; HASH_BYTES]);
impl PoseidonHash {
pub fn new(hash_array: [u8; HASH_BYTES]) -> Self {
Self(hash_array)
}
pub fn to_bytes(&self) -> [u8; HASH_BYTES] {
self.0
}
}
/// Return a Poseidon hash for the given data with the given elliptic curve and
/// endianness.
///
/// # Examples
///
/// ```rust
/// use solana_program::poseidon::{hashv, Endianness, Parameters};
///
/// # fn test() {
/// let input1 = [1u8; 32];
/// let input2 = [2u8; 32];
///
/// let hash = hashv(Parameters::Bn254X5, Endianness::BigEndian, &[&input1, &input2]).unwrap();
/// assert_eq!(
/// hash.to_bytes(),
/// [
/// 13, 84, 225, 147, 143, 138, 140, 28, 125, 235, 94, 3, 85, 242, 99, 25, 32, 123,
/// 132, 254, 156, 162, 206, 27, 38, 231, 53, 200, 41, 130, 25, 144
/// ]
/// );
///
/// let hash = hashv(Parameters::Bn254X5, Endianness::LittleEndian, &[&input1, &input2]).unwrap();
/// assert_eq!(
/// hash.to_bytes(),
/// [
/// 144, 25, 130, 41, 200, 53, 231, 38, 27, 206, 162, 156, 254, 132, 123, 32, 25, 99,
/// 242, 85, 3, 94, 235, 125, 28, 140, 138, 143, 147, 225, 84, 13
/// ]
/// );
/// # }
/// ```
#[allow(unused_variables)]
pub fn hashv(
// This parameter is not used currently, because we support only one curve
// (BN254). It should be used in case we add more curves in the future.
parameters: Parameters,
endianness: Endianness,
vals: &[&[u8]],
) -> Result<PoseidonHash, PoseidonSyscallError> {
// Perform the calculation inline, calling this from within a program is
// not supported.
#[cfg(not(target_os = "solana"))]
{
use {
ark_bn254::Fr,
light_poseidon::{Poseidon, PoseidonBytesHasher, PoseidonError},
};
impl From<PoseidonError> for PoseidonSyscallError {
fn from(error: PoseidonError) -> Self {
match error {
PoseidonError::InvalidNumberOfInputs {
inputs: _,
max_limit: _,
width: _,
} => PoseidonSyscallError::InvalidNumberOfInputs,
PoseidonError::EmptyInput => PoseidonSyscallError::EmptyInput,
PoseidonError::InvalidInputLength {
len: _,
modulus_bytes_len: _,
} => PoseidonSyscallError::InvalidInputLength,
PoseidonError::InputLargerThanModulus => {
PoseidonSyscallError::InputLargerThanModulus
}
PoseidonError::VecToArray => PoseidonSyscallError::VecToArray,
PoseidonError::U64Tou8 => PoseidonSyscallError::U64Tou8,
PoseidonError::InvalidWidthCircom {
width: _,
max_limit: _,
} => PoseidonSyscallError::InvalidWidthCircom,
}
}
}
let mut hasher =
Poseidon::<Fr>::new_circom(vals.len()).map_err(PoseidonSyscallError::from)?;
let res = match endianness {
Endianness::BigEndian => hasher.hash_bytes_be(vals),
Endianness::LittleEndian => hasher.hash_bytes_le(vals),
}
.map_err(PoseidonSyscallError::from)?;
Ok(PoseidonHash(res))
}
// Call via a system call to perform the calculation.
#[cfg(target_os = "solana")]
{
let mut hash_result = [0; HASH_BYTES];
let result = unsafe {
crate::syscalls::sol_poseidon(
parameters.into(),
endianness.into(),
vals as *const _ as *const u8,
vals.len() as u64,
&mut hash_result as *mut _ as *mut u8,
)
};
match result {
0 => Ok(PoseidonHash::new(hash_result)),
e => Err(PoseidonSyscallError::from(e)),
}
}
}
/// Return a Poseidon hash for the given data with the given elliptic curve and
/// endianness.
///
/// # Examples
///
/// ```rust
/// use solana_program::poseidon::{hash, Endianness, Parameters};
///
/// # fn test() {
/// let input = [1u8; 32];
///
/// let result = hash(Parameters::Bn254X5, Endianness::BigEndian, &input).unwrap();
/// assert_eq!(
/// result.to_bytes(),
/// [
/// 5, 191, 172, 229, 129, 238, 97, 119, 204, 25, 198, 197, 99, 99, 166, 136, 130, 241,
/// 30, 132, 7, 172, 99, 157, 185, 145, 224, 210, 127, 27, 117, 230
/// ],
/// );
///
/// let hash = hash(Parameters::Bn254X5, Endianness::LittleEndian, &input).unwrap();
/// assert_eq!(
/// hash.to_bytes(),
/// [
/// 230, 117, 27, 127, 210, 224, 145, 185, 157, 99, 172, 7, 132, 30, 241, 130, 136,
/// 166, 99, 99, 197, 198, 25, 204, 119, 97, 238, 129, 229, 172, 191, 5
/// ],
/// );
/// # }
/// ```
pub fn hash(
parameters: Parameters,
endianness: Endianness,
val: &[u8],
) -> Result<PoseidonHash, PoseidonSyscallError> {
hashv(parameters, endianness, &[val])
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_poseidon_input_ones_be() {
let input = [1u8; 32];
let hash = hash(Parameters::Bn254X5, Endianness::BigEndian, &input).unwrap();
assert_eq!(
hash.to_bytes(),
[
5, 191, 172, 229, 129, 238, 97, 119, 204, 25, 198, 197, 99, 99, 166, 136, 130, 241,
30, 132, 7, 172, 99, 157, 185, 145, 224, 210, 127, 27, 117, 230
]
);
}
#[test]
fn test_poseidon_input_ones_le() {
let input = [1u8; 32];
let hash = hash(Parameters::Bn254X5, Endianness::LittleEndian, &input).unwrap();
assert_eq!(
hash.to_bytes(),
[
230, 117, 27, 127, 210, 224, 145, 185, 157, 99, 172, 7, 132, 30, 241, 130, 136,
166, 99, 99, 197, 198, 25, 204, 119, 97, 238, 129, 229, 172, 191, 5
],
);
}
#[test]
fn test_poseidon_input_ones_twos_be() {
let input1 = [1u8; 32];
let input2 = [2u8; 32];
let hash = hashv(
Parameters::Bn254X5,
Endianness::BigEndian,
&[&input1, &input2],
)
.unwrap();
assert_eq!(
hash.to_bytes(),
[
13, 84, 225, 147, 143, 138, 140, 28, 125, 235, 94, 3, 85, 242, 99, 25, 32, 123,
132, 254, 156, 162, 206, 27, 38, 231, 53, 200, 41, 130, 25, 144
]
);
}
#[test]
fn test_poseidon_input_ones_twos_le() {
let input1 = [1u8; 32];
let input2 = [2u8; 32];
let hash = hashv(
Parameters::Bn254X5,
Endianness::LittleEndian,
&[&input1, &input2],
)
.unwrap();
assert_eq!(
hash.to_bytes(),
[
144, 25, 130, 41, 200, 53, 231, 38, 27, 206, 162, 156, 254, 132, 123, 32, 25, 99,
242, 85, 3, 94, 235, 125, 28, 140, 138, 143, 147, 225, 84, 13
]
);
}
#[test]
fn test_poseidon_input_one() {
let input = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1,
];
let expected_hashes = [
[
41, 23, 97, 0, 234, 169, 98, 189, 193, 254, 108, 101, 77, 106, 60, 19, 14, 150,
164, 209, 22, 139, 51, 132, 139, 137, 125, 197, 2, 130, 1, 51,
],
[
0, 122, 243, 70, 226, 211, 4, 39, 158, 121, 224, 169, 243, 2, 63, 119, 18, 148,
167, 138, 203, 112, 231, 63, 144, 175, 226, 124, 173, 64, 30, 129,
],
[
2, 192, 6, 110, 16, 167, 42, 189, 43, 51, 195, 178, 20, 203, 62, 129, 188, 177,
182, 227, 9, 97, 205, 35, 194, 2, 177, 134, 115, 191, 37, 67,
],
[
8, 44, 156, 55, 10, 13, 36, 244, 65, 111, 188, 65, 74, 55, 104, 31, 120, 68, 45,
39, 216, 99, 133, 153, 28, 23, 214, 252, 12, 75, 125, 113,
],
[
16, 56, 150, 5, 174, 104, 141, 79, 20, 219, 133, 49, 34, 196, 125, 102, 168, 3,
199, 43, 65, 88, 156, 177, 191, 134, 135, 65, 178, 6, 185, 187,
],
[
42, 115, 246, 121, 50, 140, 62, 171, 114, 74, 163, 229, 189, 191, 80, 179, 144, 53,
215, 114, 159, 19, 91, 151, 9, 137, 15, 133, 197, 220, 94, 118,
],
[
34, 118, 49, 10, 167, 243, 52, 58, 40, 66, 20, 19, 157, 157, 169, 89, 190, 42, 49,
178, 199, 8, 165, 248, 25, 84, 178, 101, 229, 58, 48, 184,
],
[
23, 126, 20, 83, 196, 70, 225, 176, 125, 43, 66, 51, 66, 81, 71, 9, 92, 79, 202,
187, 35, 61, 35, 11, 109, 70, 162, 20, 217, 91, 40, 132,
],
[
14, 143, 238, 47, 228, 157, 163, 15, 222, 235, 72, 196, 46, 187, 68, 204, 110, 231,
5, 95, 97, 251, 202, 94, 49, 59, 138, 95, 202, 131, 76, 71,
],
[
46, 196, 198, 94, 99, 120, 171, 140, 115, 48, 133, 79, 74, 112, 119, 193, 255, 146,
96, 228, 72, 133, 196, 184, 29, 209, 49, 173, 58, 134, 205, 150,
],
[
0, 113, 61, 65, 236, 166, 53, 241, 23, 212, 236, 188, 235, 95, 58, 102, 220, 65,
66, 235, 112, 181, 103, 101, 188, 53, 143, 27, 236, 64, 187, 155,
],
[
20, 57, 11, 224, 186, 239, 36, 155, 212, 124, 101, 221, 172, 101, 194, 229, 46,
133, 19, 192, 129, 193, 205, 114, 201, 128, 6, 9, 142, 154, 143, 190,
],
];
for (i, expected_hash) in expected_hashes.iter().enumerate() {
let inputs = vec![&input; i + 1]
.into_iter()
.map(|arr| &arr[..])
.collect::<Vec<_>>();
let hash = hashv(Parameters::Bn254X5, Endianness::BigEndian, &inputs).unwrap();
assert_eq!(hash.to_bytes(), *expected_hash);
}
}
}

View File

@ -69,6 +69,7 @@ define_syscall!(fn sol_curve_pairing_map(curve_id: u64, point: *const u8, result
define_syscall!(fn sol_alt_bn128_group_op(group_op: u64, input: *const u8, input_size: u64, result: *mut u8) -> u64);
define_syscall!(fn sol_big_mod_exp(params: *const u8, result: *mut u8) -> u64);
define_syscall!(fn sol_get_epoch_rewards_sysvar(addr: *mut u8) -> u64);
define_syscall!(fn sol_poseidon(parameters: u64, endianness: u64, vals: *const u8, val_len: u64, hash_result: *mut u8) -> u64);
#[cfg(target_feature = "static-syscalls")]
pub const fn sys_hash(name: &str) -> usize {

View File

@ -0,0 +1,62 @@
#pragma once
/**
* @brief Solana poseidon system call
**/
#include <sol/types.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* Length of a Poseidon hash result
*/
#define POSEIDON_RESULT_LENGTH 32
/**
* Configuration using the BarretoNaehrig curve with an embedding degree of
* 12, defined over a 254-bit prime field.
*
* Configuration Details:
* - S-Box: x^5
* - Width: 2 <= t <= 13
* - Inputs: 1 <= n <= 12
* - Full rounds: 8
* - Partial rounds: Depending on width: [56, 57, 56, 60, 60, 63, 64, 63,
* 60, 66, 60, 65]
*/
#define POSEIDON_PARAMETERS_BN254_X5 0
/**
* Big-endian inputs and output
*/
#define POSEIDON_ENDIANNESS_BIG_ENDIAN 0
/**
* Little-endian inputs and output
*/
#define POSEIDON_ENDIANNESS_LITTLE_ENDIAN 1
/**
* Poseidon
*
* @param parameters Configuration parameters for the hash function
* @param endianness Endianness of inputs and result
* @param bytes Array of byte arrays
* @param bytes_len Number of byte arrays
* @param result 32 byte array to hold the result
*/
@SYSCALL uint64_t sol_poseidon(
const uint64_t parameters,
const uint64_t endianness,
const SolBytes *bytes,
const uint64_t bytes_len,
uint8_t *result
);
#ifdef __cplusplus
}
#endif
/**@}*/

View File

@ -0,0 +1,83 @@
#pragma once
/**
* @brief Solana poseidon system call
**/
#include <sol/types.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* Length of a Poseidon hash result
*/
#define POSEIDON_RESULT_LENGTH 32
/**
* Configuration using the BarretoNaehrig curve with an embedding degree of
* 12, defined over a 254-bit prime field.
*
* Configuration Details:
* - S-Box: x^5
* - Width: 2 <= t <= 13
* - Inputs: 1 <= n <= 12
* - Full rounds: 8
* - Partial rounds: Depending on width: [56, 57, 56, 60, 60, 63, 64, 63,
* 60, 66, 60, 65]
*/
#define POSEIDON_PARAMETERS_BN254_X5 0
/**
* Big-endian inputs and output
*/
#define POSEIDON_ENDIANNESS_BIG_ENDIAN 0
/**
* Little-endian inputs and output
*/
#define POSEIDON_ENDIANNESS_LITTLE_ENDIAN 1
/**
* Poseidon
*
* @param parameters Configuration parameters for the hash function
* @param endianness Endianness of inputs and result
* @param bytes Array of byte arrays
* @param bytes_len Number of byte arrays
* @param result 32 byte array to hold the result
*/
/* DO NOT MODIFY THIS GENERATED FILE. INSTEAD CHANGE sdk/sbf/c/inc/sol/inc/poseidon.inc AND RUN `cargo run --bin gen-headers` */
#ifndef SOL_SBFV2
uint64_t sol_poseidon(
const uint64_t parameters,
const uint64_t endianness,
const SolBytes *bytes,
const uint64_t bytes_len,
uint8_t *result
);
#else
typedef uint64_t(*sol_poseidon_pointer_type)(
const uint64_t parameters,
const uint64_t endianness,
const SolBytes *bytes,
const uint64_t bytes_len,
uint8_t *result
);
static uint64_t sol_poseidon(
const uint64_t parameters arg1,
const uint64_t endianness arg2,
const SolBytes *bytes arg3,
const uint64_t bytes_len arg4,
uint8_t *result
arg5) {
sol_poseidon_pointer_type sol_poseidon_pointer = (sol_poseidon_pointer_type) 3298065441;
return sol_poseidon_pointer(arg1, arg2, arg3, arg4, arg5);
}
#endif
#ifdef __cplusplus
}
#endif
/**@}*/

View File

@ -691,6 +691,10 @@ pub mod revise_turbine_epoch_stakes {
solana_sdk::declare_id!("BTWmtJC8U5ZLMbBUUA1k6As62sYjPEjAiNAT55xYGdJU");
}
pub mod enable_poseidon_syscall {
solana_sdk::declare_id!("Pos1111111111111111111111111111111111111111");
}
lazy_static! {
/// Map of feature identifiers to user-visible description
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
@ -856,6 +860,7 @@ lazy_static! {
(last_restart_slot_sysvar::id(), "enable new sysvar last_restart_slot"),
(reduce_stake_warmup_cooldown::id(), "reduce stake warmup cooldown from 25% to 9%"),
(revise_turbine_epoch_stakes::id(), "revise turbine epoch stakes"),
(enable_poseidon_syscall::id(), "Enable Poseidon syscall"),
/*************** ADD NEW FEATURES HERE ***************/
]
.iter()

View File

@ -49,10 +49,11 @@ pub use solana_program::{
declare_sysvar_id, decode_error, ed25519_program, epoch_rewards, epoch_schedule,
fee_calculator, impl_sysvar_get, incinerator, instruction, keccak, lamports,
loader_instruction, loader_upgradeable_instruction, loader_v4, loader_v4_instruction, message,
msg, native_token, nonce, program, program_error, program_memory, program_option, program_pack,
rent, sanitize, sdk_ids, secp256k1_program, secp256k1_recover, serde_varint, serialize_utils,
short_vec, slot_hashes, slot_history, stable_layout, stake, stake_history, syscalls,
system_instruction, system_program, sysvar, unchecked_div_by_const, vote, wasm_bindgen,
msg, native_token, nonce, poseidon, program, program_error, program_memory, program_option,
program_pack, rent, sanitize, sdk_ids, secp256k1_program, secp256k1_recover, serde_varint,
serialize_utils, short_vec, slot_hashes, slot_history, stable_layout, stake, stake_history,
syscalls, system_instruction, system_program, sysvar, unchecked_div_by_const, vote,
wasm_bindgen,
};
pub mod account;