Complete reference for supported operations, expressions, and patterns in Arcis MPC circuits
Arcis supports many of Rust’s native operations and extends them for encrypted data, allowing you to write private computations using familiar Rust syntax. See the tables below for a detailed list of supported and unsupported operations.
Use this page when you need to check if a specific operation is supported in Arcis circuits.
Cannot be supported as the number of iterations is not known.
Why both branches count: In MPC, both if and else branches are always evaluated—the condition only selects which result to use. This ensures the execution pattern does not leak information about the condition value. See Thinking in MPC for details.
User-defined binary operations are currently unsupported.
Example
Supported types
a + b
Integers, floats, BaseField25519
a - b
Integers, floats, BaseField25519
a * b
Integers, floats, BaseField25519
a / b
Integers, floats
a % b
Integers
a && b
Booleans
a || b
Booleans
a ^ b
Booleans
a & b
Booleans
a | b
Booleans
a << b
None
a >> b
Integers, if b is known at compile time.
a == b
All. Use derive(PartialEq) for structs.
a != b
All. Use derive(PartialEq) for structs.
a < b
Booleans, integers, floats, BaseField25519
a <= b
Booleans, integers, floats, BaseField25519
a >= b
Booleans, integers, floats, BaseField25519
a > b
Booleans, integers, floats, BaseField25519
a += b
Integers, floats, BaseField25519
a -= b
Integers, floats, BaseField25519
a *= b
Integers, floats, BaseField25519
a /= b
Integers, floats
a %= b
Integers
a ^= b
Booleans
a &= b
Booleans
a |= b
Booleans
a <<= b
None
a >>= b
Integers, if b is known at compile time
BaseField25519 does not support /, %, >>, or <<. Note that &, |, ^ are booleans-only across all types. Use .field_division() or .euclidean_division() for division on field elements. See BaseField25519 Operations.
ArcisRNG::gen_uniform::<T>() to generate a uniform value of type T (bool, integer, or combination). Requires explicit type parameter.
ArcisRNG::gen_integer_from_width(width: usize) -> u128. Generates a secret integer between 0 and 2^width - 1 included.
ArcisRNG::gen_public_integer_from_width(width: usize) -> u128. Generates a public integer between 0 and 2^width - 1 included.
ArcisRNG::gen_integer_in_range(min: u128, max: u128, n_attempts: usize) -> (u128, bool). Generates a random integer in [min, max] using rejection sampling. n_attempts must be compile-time known. Returns (result, success) where success=false indicates all attempts were rejected. With n_attempts=24, failure probability is <2^-24.
ArcisRNG::shuffle(slice) on slices. Complexity is in O(n*log³(n) + n*log²(n)*sizeof(T)).
Mxe::get() to be able to create MXE-owned secret data.
Shared::new(arcis_public_key) to share private data with arcis_public_key.
ArcisX25519Pubkey::from_base58(base58_byte_string) to create a public key from a base58-encoded address.
ArcisX25519Pubkey::from_uint8(u8_byte_slice) to create a public key from a Uint8 array.
SolanaPublicKey::from_serialized(value) to create a Solana public key from serialized form.
SolanaPublicKey::from_base58(byte_string) to create a Solana public key from base58.
ArcisMath::sigmoid(x) for the sigmoid activation function.
LogisticRegression::new(coef, intercept) for logistic regression models.
LinearRegression::new(coef, intercept) for linear regression models.
Pack::new(value) to bit-pack data for onchain storage (multiple small values fit into fewer field elements).
ArcisX25519Pubkey::new_from_x(x: BaseField25519) to create a public key from its Curve25519 Montgomery X-coordinate.
ArcisX25519Pubkey::to_x() -> BaseField25519 to extract the Montgomery X-coordinate from a public key.
BaseField25519::from_u8(x) … BaseField25519::from_u128(x) to convert unsigned integers to field elements. Signed variants (from_i8 … from_i128) and from_bool, from_usize, from_isize also available.
BaseField25519::power_of_two(exp) to compute 2^exp as a field element.
debug_assert!, debug_assert_ne!, debug_assert_eq! to assert conditions during debugging. They do not change instruction behavior.
eprint!, eprintln!, print!, println! to print debug output. They do not change instruction behavior.
arcis_static_panic!(message) to fail compilation when the branch is reached. Useful for enforcing constraints that must be known before circuit generation.
include_bytes!("file_path") to include raw bytes from a file in Arcis circuits.
include!("file_path") to include a file in item position (not expression position).
assert_current_module!(crate::path::to::module) to enable crate:: absolute paths within the current module. Place at the top of any module that needs to reference items via absolute paths.
encrypted_mod!("path/to/module.rs") or encrypted_mod!("path/to/module.rs", alias_name) to use another file as a module within an #[encrypted] module. The target file must use the #[encrypted_library] attribute. Items are accessible via the filename stem (or alias) as a namespace, e.g., module_name::ITEM.
Example usage:
const ARRAY_LEN: usize = 3; // Change to 1 and the example will not compile.fn second_element(arr: &[u8]) -> u8 { if arr.len() < 2 { arcis_static_panic!("Array must have at least 2 elements"); } arr[1]}#[instruction]fn reveal_second_element(input: Enc<Shared, Pack<[u8; ARRAY_LEN]>>) -> u8 { let array = input.to_arcis().unpack(); second_element(&array).reveal()}
arcis_static_panic! triggers at compile time when the Arcis compiler evaluates the branch. Try changing ARRAY_LEN to 1 above—the compile error demonstrates how this macro enforces constraints that must be validated before circuit generation.
.take(n), .skip(n), .step_by(n) on iterators when n is compile-time known.
.reveal() if not inside an if or else block where the condition is not a compile-time constant
.to_arcis() on Encs
.from_arcis(x) on Owners (objects of types Mxe or Shared) if not inside an if or else block where the condition is not a compile-time constant
.abs(), .min(x), .max(x) on integers and floats
.abs_diff(other), .is_positive(), .is_negative(), .div_ceil(other) on integers
.to_le_bytes(), .to_be_bytes() on typed integers (does not work on integers whose type the interpreter does not know)
.exp(), .exp2(), .ln(), .log2(), .sqrt() on floats.
.unpack() on Pack<T> to extract the original value from packed storage.
.to_arcis_with_pubkey_and_nonce(pubkey, nonce) on EncData<T> to decrypt when the key is shared across inputs (avoids duplicate decryption gates). See EncData for details.
.data on Enc<Owner, T> to extract only the EncData<T> ciphertext for smaller callback payloads.
.safe_inverse() on BaseField25519 to get the field inverse (returns 0 for inverse of 0).
.field_division(divisor) on BaseField25519 for field division (returns 0 for division by 0).
.euclidean_division(divisor) on BaseField25519 for signed Euclidean division (panics on division by 0).
.to_u8_unchecked() … .to_u128_unchecked() on BaseField25519 to extract as unsigned int. Silently produces incorrect results if value exceeds target range. Signed variants (to_i8_unchecked … to_i128_unchecked) and to_bool_unchecked also available.
IntType::BITS, IntType::MIN and IntType::MAX where IntType is an integer type.
Paths to user-defined constants, functions and structs, as long as they are inside the #[encrypted] area. Both super:: paths and crate:: paths (when assert_current_module! is declared) are supported.
Arcis supports nested modules, super:: parent references, crate:: absolute paths, and multi-file projects. #[instruction] functions can be placed at any module depth, not just at the top level.Submodules with super:: and crate:: paths:
use arcis::*;#[encrypted]mod my_mxe { use arcis::*; assert_current_module!(crate::my_mxe); const THRESHOLD: u64 = 1000; // No assert_current_module! needed — this module only uses super::, not crate:: mod validation { use arcis::*; pub fn is_above_threshold(val: u64) -> bool { val > super::THRESHOLD // Access parent constant via super:: } } mod processing { use arcis::*; assert_current_module!(crate::my_mxe::processing); // #[instruction] works in nested modules #[instruction] pub fn process(vals: [u64; 3]) -> [u64; 3] { let mut result = [0u64; 3]; for i in 0..3 { // Use crate:: to reference a sibling module's function let valid = crate::my_mxe::validation::is_above_threshold(vals[i]); result[i] = if valid { vals[i] * 2 } else { vals[i] }; } result } }}
Each file imported via encrypted_mod! must use #[encrypted_library] (not #[encrypted]). Items are accessible through the filename stem as a namespace (e.g., helpers::Config), or you can provide an alias: encrypted_mod!("helpers.rs", utils) makes items accessible as utils::Config.
#[instruction]fn iterator_examples(arr: [u8; 10]) -> u16 { // Basic iteration let mut sum = 0u16; for val in arr.iter() { sum += *val as u16; } // Method chaining arr.iter() .map(|x| *x as u16) .map(|x| x * 2) .sum()}
Since .filter() is not supported (it produces variable-length output), use a manual loop with conditionals:
// ✗ Not supportedarr.iter().filter(|x| **x > threshold).sum()// ✓ Manual filter pattern#[instruction]fn filter_sum(arr: [u8; 10], threshold: u8) -> u16 { let mut sum = 0u16; for val in arr.iter() { if *val > threshold { sum += *val as u16; } } sum}
This pattern checks all elements but only accumulates those meeting the condition—same result, fixed execution structure.