Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 32 additions & 12 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 10 additions & 12 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,29 +30,27 @@ soroban-token-spec = { version = "23.4.0", path = "soroban-token-spec" }
stellar-asset-spec = { version = "23.4.0", path = "stellar-asset-spec" }

[workspace.dependencies.soroban-env-common]
version = "=23.0.1"
#git = "https://github.com/stellar/rs-soroban-env"
#rev = "bd0c80a1fe171e75f8d745f17975a73927d44ecd"
version = "=25.0.0"
# git = "https://github.com/stellar/rs-soroban-env"
# rev = "cf58d535ab05d02802a5e804a95524650f8c62c7"

[workspace.dependencies.soroban-env-guest]
version = "=23.0.1"
#git = "https://github.com/stellar/rs-soroban-env"
#rev = "bd0c80a1fe171e75f8d745f17975a73927d44ecd"
version = "=25.0.0"
# git = "https://github.com/stellar/rs-soroban-env"
# rev = "cf58d535ab05d02802a5e804a95524650f8c62c7"

[workspace.dependencies.soroban-env-host]
version = "=23.0.1"
#git = "https://github.com/stellar/rs-soroban-env"
#rev = "bd0c80a1fe171e75f8d745f17975a73927d44ecd"
version = "=25.0.0"
# git = "https://github.com/stellar/rs-soroban-env"
# rev = "cf58d535ab05d02802a5e804a95524650f8c62c7"

[workspace.dependencies.stellar-strkey]
version = "=0.0.13"

[workspace.dependencies.stellar-xdr]
version = "=23.0.0"
version = "=25.0.0"
default-features = false
features = ["curr"]
#git = "https://github.com/stellar/rs-stellar-xdr"
#rev = "67be5955a15f1d3a4df83fe86e6ae107f687141b"

#[patch.crates-io]
#soroban-env-common = { path = "../rs-soroban-env/soroban-env-common" }
Expand Down
2 changes: 1 addition & 1 deletion soroban-ledger-snapshot/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ impl LedgerSnapshot {
impl Default for LedgerSnapshot {
fn default() -> Self {
Self {
protocol_version: 23,
protocol_version: 25,
sequence_number: Default::default(),
timestamp: Default::default(),
network_id: Default::default(),
Expand Down
14 changes: 2 additions & 12 deletions soroban-sdk-macros/src/derive_spec_fn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use proc_macro2::TokenStream as TokenStream2;
use quote::{format_ident, quote};
use stellar_xdr::curr as stellar_xdr;
use stellar_xdr::{
ScSpecEntry, ScSpecFunctionInputV0, ScSpecFunctionV0, ScSpecTypeDef, ScSymbol, StringM, VecM,
ScSpecEntry, ScSpecFunctionInputV0, ScSpecFunctionV0, ScSpecTypeDef, ScSymbol, StringM,
WriteXdr, SCSYMBOL_LIMIT,
};
use syn::TypeReference;
Expand Down Expand Up @@ -153,17 +153,7 @@ pub fn derive_fn_spec(
));
ScSymbol::default()
}),
inputs: spec_args.try_into().unwrap_or_else(|_| {
const MAX: u32 = 10;
errors.push(Error::new(
inputs.iter().nth(MAX as usize).span(),
format!(
"contract function has too many parameters, max count {} parameters",
MAX,
),
));
VecM::<_, MAX>::default()
}),
inputs: spec_args.try_into().unwrap(),
outputs: spec_result.try_into().unwrap(),
});
let spec_xdr = spec_entry.to_xdr(DEFAULT_XDR_RW_LIMITS).unwrap();
Expand Down
37 changes: 34 additions & 3 deletions soroban-sdk-macros/src/map_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ pub const FP2_SERIALIZED_SIZE: u32 = FP_SERIALIZED_SIZE * 2;
pub const G1_SERIALIZED_SIZE: u32 = FP_SERIALIZED_SIZE * 2;
pub const G2_SERIALIZED_SIZE: u32 = FP2_SERIALIZED_SIZE * 2;

// BN254 constants - values must match soroban_sdk::crypto::bn254
pub const BN254_FP_SERIALIZED_SIZE: u32 = 32;
pub const BN254_G1_SERIALIZED_SIZE: u32 = BN254_FP_SERIALIZED_SIZE * 2; // 64
pub const BN254_G2_SERIALIZED_SIZE: u32 = BN254_G1_SERIALIZED_SIZE * 2; // 128

#[allow(clippy::too_many_lines)]
pub fn map_type(t: &Type, allow_ref: bool, allow_hash: bool) -> Result<ScSpecTypeDef, Error> {
match t {
Expand Down Expand Up @@ -53,13 +58,13 @@ pub fn map_type(t: &Type, allow_ref: bool, allow_hash: bool) -> Result<ScSpecTyp
"MuxedAddress" => Ok(ScSpecTypeDef::MuxedAddress),
"Timepoint" => Ok(ScSpecTypeDef::Timepoint),
"Duration" => Ok(ScSpecTypeDef::Duration),
// The BLS types defined below are represented in the contract's
// The BLS and BN types defined below are represented in the contract's
// interface by their underlying data types, i.e.
// Fp/Fp2/G1Affine/G2Affine => BytesN<N>, Fr => U256. This approach
// simplifies integration with contract development tooling, as it
// avoids introducing new spec types for these BLS constructs.
// avoids introducing new spec types for these constructs.
//
// While this is functionally sound because the BLS types are
// While this is functionally sound because the types are
// essentially newtypes over their inner representations, it means
// that the specific semantic meaning of `G1Affine`, `G2Affine`, or
// `Fr` is not directly visible in the compiled WASM interface. For
Expand All @@ -71,6 +76,9 @@ pub fn map_type(t: &Type, allow_ref: bool, allow_hash: bool) -> Result<ScSpecTyp
// Idiom. For more details, see the tracking issue for supporting
// type aliases:
// https://github.com/stellar/rs-soroban-sdk/issues/1063

// These BLS12-381 unprefixed type names
// will be removed in a future release.
"Fp" => Ok(ScSpecTypeDef::BytesN(ScSpecTypeBytesN {
n: FP_SERIALIZED_SIZE,
})),
Expand All @@ -84,6 +92,29 @@ pub fn map_type(t: &Type, allow_ref: bool, allow_hash: bool) -> Result<ScSpecTyp
n: G2_SERIALIZED_SIZE,
})),
"Fr" => Ok(ScSpecTypeDef::U256),
// BLS12-381 prefixed type names
"Bls12381Fp" => Ok(ScSpecTypeDef::BytesN(ScSpecTypeBytesN {
n: FP_SERIALIZED_SIZE,
})),
"Bls12381Fp2" => Ok(ScSpecTypeDef::BytesN(ScSpecTypeBytesN {
n: FP2_SERIALIZED_SIZE,
})),
"Bls12381G1Affine" => Ok(ScSpecTypeDef::BytesN(ScSpecTypeBytesN {
n: G1_SERIALIZED_SIZE,
})),
"Bls12381G2Affine" => Ok(ScSpecTypeDef::BytesN(ScSpecTypeBytesN {
n: G2_SERIALIZED_SIZE,
})),
// BN254 prefixed type names
"Bn254Fp" => Ok(ScSpecTypeDef::BytesN(ScSpecTypeBytesN {
n: BN254_FP_SERIALIZED_SIZE,
})),
"Bn254G1Affine" => Ok(ScSpecTypeDef::BytesN(ScSpecTypeBytesN {
n: BN254_G1_SERIALIZED_SIZE,
})),
"Bn254G2Affine" => Ok(ScSpecTypeDef::BytesN(ScSpecTypeBytesN {
n: BN254_G2_SERIALIZED_SIZE,
})),
s => Ok(ScSpecTypeDef::Udt(ScSpecTypeUdt {
name: s.try_into().map_err(|e| {
Error::new(
Expand Down
123 changes: 122 additions & 1 deletion soroban-sdk/src/crypto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,20 @@
use crate::{
env::internal::{self, BytesObject},
unwrap::UnwrapInfallible,
Bytes, BytesN, ConversionError, Env, IntoVal, TryFromVal, Val,
Bytes, BytesN, ConversionError, Env, IntoVal, Symbol, TryFromVal, TryIntoVal, Val, Vec, U256,
};

pub mod bls12_381;
pub mod bn254;
pub(crate) mod poseidon2_params;
pub mod poseidon2_sponge;
pub(crate) mod poseidon_params;
pub mod poseidon_sponge;
pub(crate) mod utils;
pub use bn254::Fr as BnScalar;
pub use poseidon2_sponge::{Poseidon2Config, Poseidon2Sponge};
pub use poseidon_sponge::{PoseidonConfig, PoseidonSponge};

/// A `BytesN<N>` generated by a cryptographic hash function.
///
/// The `Hash<N>` type contains a `BytesN<N>` and can only be constructed in
Expand Down Expand Up @@ -182,6 +192,53 @@ impl Crypto {
pub fn bls12_381(&self) -> bls12_381::Bls12_381 {
bls12_381::Bls12_381::new(self.env())
}

/// Get a [Bn254][bn254::Bn254] for accessing the bn254
/// functions.
pub fn bn254(&self) -> bn254::Bn254 {
bn254::Bn254::new(self.env())
}

/// Computes a Poseidon hash matching circom's
/// [implementation](https://github.com/iden3/circomlib/blob/35e54ea21da3e8762557234298dbb553c175ea8d/circuits/poseidon.circom)
/// for input lengths up to 5 (`t ≤ 6`).
///
/// Internally it picks the state size `t` to match the input length, i.e.
/// `t = N + 1` (rate = N, capacity = 1). For example, hashing 2 elements
/// uses t=3.
///
/// Note: use [`poseidon_sponge::hash`] with a pre-constructed
/// [`PoseidonConfig`] directly if:
/// - You want to repeatedly hash with the same input size. Pre-constructing
/// the config saves the cost of re-initialization.
/// - You want to hash larger input sizes. The sponge will repeatedly
/// permute and absorb until the entire input is consumed. This is a valid
/// (and secure) sponge operation, even though it may not match circom's
/// output, which always picks a larger state size (N+1) to hash inputs in
/// one shot (up to t=17). If you need parameter support for larger `t`,
/// please file an issue.
pub fn poseidon_hash(&self, field_type: Symbol, inputs: &Vec<U256>) -> U256 {
let config = PoseidonConfig::new(&self.env, field_type, inputs.len() as u32);
poseidon_sponge::hash(&self.env, inputs, config)
}

/// Computes a Poseidon2 hash matching noir's
/// [implementation](https://github.com/noir-lang/noir/blob/abfee1f54b20984172ba23482f4af160395cfba5/noir_stdlib/src/hash/poseidon2.nr).
///
/// Internally it always initializes the state with `t = 4`, regardless of
/// input length. It alternates between absorbing and permuting until all
/// input elements are consumed.
///
/// Note: use [`poseidon2_sponge::hash`] with a pre-constructed
/// [`Poseidon2Config`] directly if:
/// - You need to hash multiple times. Pre-constructing the config saves the
/// cost of re-initialization.
/// - You want to use a different state size (`t ≤ 4`).
pub fn poseidon2_hash(&self, field_type: Symbol, inputs: &Vec<U256>) -> U256 {
const INTERNAL_RATE: u32 = 3;
let config = Poseidon2Config::new(&self.env, field_type, INTERNAL_RATE);
poseidon2_sponge::hash(&self.env, inputs, config)
}
}

/// # ⚠️ Hazardous Materials
Expand Down Expand Up @@ -254,4 +311,68 @@ impl CryptoHazmat {
)
.unwrap_infallible();
}

/// Performs a Poseidon permutation on the input state vector.
///
/// WARNING: This is a low-level permutation function. Most users should use
/// the higher-level `poseidon_hash` function instead.
pub fn poseidon_permutation(
&self,
input: &Vec<U256>,
field: Symbol,
t: u32,
d: u32,
rounds_f: u32,
rounds_p: u32,
mds: &Vec<Vec<U256>>,
round_constants: &Vec<Vec<U256>>,
) -> Vec<U256> {
let env = self.env();
let result = internal::Env::poseidon_permutation(
env,
input.to_object(),
field.to_symbol_val(),
t.into(),
d.into(),
rounds_f.into(),
rounds_p.into(),
mds.to_object(),
round_constants.to_object(),
)
.unwrap_infallible();

result.try_into_val(env).unwrap_infallible()
}

/// Performs a Poseidon2 permutation on the input state vector.
///
/// WARNING: This is a low-level permutation function. Most users should use
/// the higher-level `poseidon2_hash` function instead.
pub fn poseidon2_permutation(
&self,
input: &Vec<U256>,
field: Symbol,
t: u32,
d: u32,
rounds_f: u32,
rounds_p: u32,
mat_internal_diag_m_1: &Vec<U256>,
round_constants: &Vec<Vec<U256>>,
) -> Vec<U256> {
let env = self.env();
let result = internal::Env::poseidon2_permutation(
env,
input.to_object(),
field.to_symbol_val(),
t.into(),
d.into(),
rounds_f.into(),
rounds_p.into(),
mat_internal_diag_m_1.to_object(),
round_constants.to_object(),
)
.unwrap_infallible();

result.try_into_val(env).unwrap_infallible()
}
}
Loading
Loading