Skip to main content
Version: 4.4.0

Standard Library

The Leo standard library (std) is implicitly available in every Leo program. You do not need a program.json entry or an import statement. Use a qualified path to access an item under std::*. Examples are std::hash::bhp256::hash_u64_to_field(x) and std::ctx::caller().

A program can opt out of the implicit injection by setting "no_std": true in its program.json. With no_std, the same operations remain available through the lower-level operator surface. See Cryptographic Operators and Intrinsics.

Scope and finalize context

Functions in std come in two flavors:

  • You can call a plain fn where its underlying operation is valid. You can call most plain functions in transition and finalize contexts. You can use on-chain context accessors in finalize contexts and views.
  • You can call a final fn only from another final fn body or a final { ... } block. These functions use on-chain operands. The off-chain prover cannot access these operands.

This page calls out the scope of every module that contains final fn entries.

Modules


std::hash

Cryptographic hash functions, grouped by algorithm family. Every algorithm exposes the same set of hash_<input>_to_<output> wrappers. The algorithm itself determines the security guarantees, the input constraints, and the proving cost.

Variants

Most algorithms expose two forms:

  • hash_<input>_to_<output>(x) prepends a 26-bit type discriminator to the input before it calculates the hash. Use this form when you store the hash, compare types, or put the hash in a commitment. The tag prevents type confusion between values that have the same bit pattern.
  • hash_<input>_to_<output>_raw(x) hashes the input's native bit representation directly with no tag. Use only when the type is fixed on both sides of the operation.

Keccak and SHA-3 also provide hash_<input>_to_bits(x) and hash_<input>_to_bits_raw(x). These functions return the complete digest as [bool; N]. The value of N is the algorithm output width: 256, 384, or 512.

std::hash::bhp256, bhp512, bhp768, bhp1024

BHP is the in-circuit hash family that Aleo uses for record commitments and state digests. It is collision-resistant and has a low proof cost in a zk-SNARK. Use BHP when a circuit or on-chain operation must calculate and verify the hash. The number in the algorithm name is the input-pad width in bits. Larger pads have a lower relative cost for long inputs, but a higher proof cost for short inputs.

Each algorithm accepts any non-mapping, non-tuple, non-unit input.

let h1: field = std::hash::bhp256::hash_u64_to_field(42u64);
let h2: address = std::hash::bhp256::hash_field_to_address(1field);
let h3: u32 = std::hash::bhp256::hash_bool_to_u32_raw(true);
InputOutput
bool, u8, u16, u32, u64, u128, i8, i16, i32, i64, i128,address, field, group, scalar,
field, group, scalar, addressu8, u16, u32, u64, u128, i8, i16, i32, i64, i128 (truncated to bit width)

std::hash::keccak256, keccak384, keccak512

Keccak is the pre-FIPS sponge construction from which SHA-3 was derived. Use it when interoperating with Ethereum or any ecosystem standardized on the pre-standardization variant. Output bit width matches the suffix (256, 384, or 512 bits).

// Keccak/SHA-3 raw and to_bits variants require byte-aligned input.
let h: field = std::hash::keccak256::hash_u64_to_field(0u64);
let digest: [bool; 256] = std::hash::keccak256::hash_u8_to_bits(0u8);

The non-raw hash_<input>_to_<output> functions accept any non-mapping input. The _raw, _to_bits, and _to_bits_raw variants require byte-aligned input. Only integer types and arrays of bytes satisfy this.

std::hash::sha3_256, sha3_384, sha3_512

The NIST-standardized SHA-3 variant of the Keccak sponge. Use SHA-3 when interop requires a FIPS-compliant hash. For EVM-style interop, prefer Keccak. Variants and input constraints are identical to the Keccak family.

std::hash::pedersen64, pedersen128

Pedersen hashing is homomorphic, collision-resistant, and very cheap to prove inside a zk-SNARK. Capacity is bounded by the suffix:

  • pedersen64 accepts bool and integer inputs of 32 bits or fewer.
  • pedersen128 accepts bool and integer inputs of 64 bits or fewer.

Inputs that exceed those bounds must use a BHP or Poseidon variant.

// Pedersen-64 only accepts inputs up to 64 bits.
let h: field = std::hash::pedersen64::hash_u32_to_field(7u32);

std::hash::poseidon2, poseidon4, poseidon8

Poseidon is the SNARK-friendliest hash in the stdlib. Every step is a field operation, so the hash costs an order of magnitude fewer circuit constraints to prove than Keccak or SHA-3. Use Poseidon whenever the hash is computed and verified in the same proof and EVM interop is not required.

The numeric suffix is the sponge rate (field elements absorbed per permutation). Higher rates absorb more data per step (cheaper per byte on long inputs) but provide proportionally less security margin. poseidon2 is the safest default.

// Poseidon is the cheapest hash to prove in-circuit.
let h: field = std::hash::poseidon2::hash_field_to_field(1field);

Inputs may be any non-mapping, non-tuple, non-unit value.


std::commit

A commitment to value x with randomizer r produces c = commit(x, r). The commitment hides x while r remains secret. It also binds the committer because a different x' cannot feasibly produce the same c.

The randomizer r is always a scalar. Sample r uniformly at random for every commitment. Reusing a randomizer across distinct values destroys hiding. Repeated calls with the same (x, r) produce the same output, which is what makes commitments useful for later revealing or membership checks.

// `randomizer` must be uniformly sampled and kept secret to preserve
// the commitment's hiding property.
let c1: field = std::commit::bhp256::commit_u64_to_field(value, randomizer);
let c2: group = std::commit::bhp256::commit_u64_to_group(value, randomizer);

Algorithms

  • std::commit::bhp256, bhp512, bhp768, bhp1024: accept any non-mapping input.
  • std::commit::pedersen64: bool and integers up to 32 bits.
  • std::commit::pedersen128: bool and integers up to 64 bits.

Each algorithm exposes commit_<input>_to_<output>(x, r) for every combination of input type and the three supported output types (address, field, group).


std::sig

Signature verification. Every function in this module is a final fn: signatures are checked on-chain so that validators reach consensus on the result. Off-chain authentication should be implemented at the transition level using account secrets, not via these functions.

Each verifier returns true if and only if the signature is cryptographically valid for the supplied message and signer. Replay protection, nonce tracking, and message-uniqueness checks remain the caller's responsibility.

Schnorr

// Schnorr verification. `message` must be a `field`; hash
// other values into a field before signing or verifying.
let valid: bool = std::sig::verify_schnorr(sig_value, signer, message);

verify_schnorr(sig: signature, signer: address, message: field) -> bool checks an Aleo Schnorr signature produced by the account at signer. The wrapper accepts a field message. Callers signing other primitive types should hash the value into a field first (for example with std::hash::bhp256::hash_to_field).

ECDSA (digest)

// ECDSA-over-prehash. `prehash` is the 32-byte digest the signer
// hashed before signing; the wrapper does no hashing of its own.
let valid: bool = std::sig::verify_ecdsa_digest(ecdsa_sig, vk, prehash);

Two digest-style verifiers are provided:

FunctionInputs
verify_ecdsa_digest(sig, verifying_key, prehash)sig: [u8; 65], verifying_key: [u8; 33] (compressed secp256k1 public key), prehash: [u8; 32]
verify_ecdsa_digest_eth(sig, eth_address, prehash)sig: [u8; 65], eth_address: [u8; 20], prehash: [u8; 32]

The caller is responsible for computing prehash with the same hash function the signer used. This verifier does no hashing of its own. Use the _eth variant when the signer is identified by their Ethereum address (for example, signatures produced by MetaMask).


std::rand

This module generates pseudorandom values in the finalize context. Each function is a final fn and uses a ChaCha stream cipher. The pre-finalize state of the current block supplies the seed. The randomness is deterministic for a given block. Two transactions with the same finalize logic and input get the same value sequence. Thus, all validators reach consensus.

// ChaCha randomness is deterministic per block: usable for
// jittering rewards or sampling, but observable by the block proposer.
let coin: bool = std::rand::chacha_bool();
let token_id: u128 = std::rand::chacha_u128();

Use these functions to select lottery winners, vary reward schedules, or generate on-chain randomness. Do not use them when the block proposer must not predict the result. The proposer can observe the seed and withhold or reorder transactions. Use an additional commit-reveal scheme when the randomness must resist a malicious proposer.

chacha_<type>() is defined for every Aleo primitive return type: address, bool, field, group, scalar, u8u128, i8i128.


std::serialize

Bit-level serialization and deserialization of primitive types. Every Leo primitive has a canonical bit representation that this module makes available as a fixed-size [bool; N] array. The encoding round-trips: from_bits(to_bits(x)) == x for any value x of a wrapped type, and likewise for the _raw variants.

// The tagged encoding (`to_bits`) is 26 bits wider than the raw form
// and includes a type discriminator. `from_bits` rejects mismatched
// tags, making round-trips type-safe.
let bits: [bool; 90] = std::serialize::to_bits_u64(x);
let restored: u64 = std::serialize::from_bits_u64(bits);

Tagged vs. Raw encoding

  • to_bits / from_bits prepend a 26-bit type discriminator to the value's native bit representation. The discriminator identifies the source type, so a deserializer rejects a bit string produced for a different type. Use the tagged form when you store the bits, put them in a commitment, or send them between programs. These operations can cause a type-confusion risk.
  • to_bits_raw / from_bits_raw use the native bit width of the type with no tag. The output array is shorter, but two values of different types may share the same bit pattern (for example 8u8 and 8i8). Use the raw form inside a single algorithm where the types are fixed and known on both sides.

Bit widths

Native widths: bool = 1, uN/iN = N, field = group = address = 253, scalar = 251. The tagged form adds 26 bits to each width, so to_bits_field returns [bool; 279] and to_bits_raw_field returns [bool; 253].


std::grp

Group operations on the Aleo curve. The curve's elements support addition, scalar multiplication, and conversion to and from affine (x, y) coordinates over the base field. Leo syntax provides the arithmetic operations, including +, scalar *, .double(), .neg(), and ==. This module provides two standard generators, the precalculated H powers, and coordinate extraction.

let g: group = std::grp::generator();
let h: group = std::grp::aleo_generator();
let x: field = std::grp::to_x_coordinate(g);
FunctionReturnsNotes
generator()groupThe Ed25519 base point G.
aleo_generator()groupThe Aleo account-key generator H (resolved at runtime and not constant-foldable).
aleo_generator_powers()[group; 251]Precomputed [H, 2H, 4H, ..., 2^250 · H] table used by Aleo's account derivation.
to_x_coordinate(g)fieldThe affine x coordinate of g.
to_y_coordinate(g)fieldThe affine y coordinate of g.

std::ctx

This module provides execution-context accessors for the current program. Its functions provide the caller, transaction signer, block height, block timestamp, and other program information.

Off-chain (usable from any transition body)

// Off-chain context: who is calling, who signed, who am I.
let me: address = std::ctx::addr();
let invoker: address = std::ctx::caller();
let originator: address = std::ctx::signer();
FunctionReturnsDescription
addr()addressThe Aleo address of the program this transition belongs to.
caller()addressThe immediate caller. Equal to signer() when invoked directly. Equal to another program's address on cross-program calls.
signer()addressThe address that signed the outer transaction. Unchanged across cross-program calls.
id()addressThe identifier of the current program.

Use caller() for decisions that depend on the immediate caller. Use signer() for decisions that depend on the transaction signer.

On-chain reads (finalize contexts and views)

// On-chain context; these are only accessible inside `final fn`
// bodies or `final { ... }` async blocks.
let height: u32 = std::ctx::block_height();
let when: i64 = std::ctx::block_timestamp();
let net: u16 = std::ctx::network_id();
let owner: address = std::ctx::program_owner();
FunctionReturnsDescription
checksum()[u8; 32]32-byte deployment checksum. Changes only when the program is upgraded with new bytecode.
edition()u16Deployment edition. 0 is the initial deployment, each upgrade increments it by one.
program_owner()addressThe address that owns this program (typically the deployer).
block_height()u32Height of the block containing the current transaction.
block_timestamp()i64Unix timestamp (seconds) of the block containing the current transaction.
network_id()u16Numeric identifier of the network (mainnet, testnet, canary, etc).

std::prog

This module provides on-chain metadata accessors for imported programs. Each function takes the program identifier as a const generic argument. Thus, the compiler fixes the target program during compilation. The AVM cannot select a target dynamically. Use these accessors to control logic with a dependency program's deployed version, checksum, or owner.

All functions in this module are final fns, so they can only be called from a final { ... } block, a final fn, or a constructor.

// Other-program metadata; the target is a compile-time const generic.
let cs: [u8; 32] = std::prog::checksum::[stdlib_demo.aleo]();
let ed: u16 = std::prog::edition::[stdlib_demo.aleo]();
let ow: address = std::prog::program_owner::[stdlib_demo.aleo]();
let fc: [u8; 32] = std::prog::function_checksum::[stdlib_demo.aleo, 'prog_demo']();
FunctionReturnsDescription
checksum::[PROG]()[u8; 32]32-byte deployment checksum of the program PROG. Changes only when the program is upgraded.
edition::[PROG]()u16Deployment edition of the program PROG. 0 is the initial deployment, each upgrade increments it.
program_owner::[PROG]()addressAddress that owns PROG (typically the deployer). Halts at runtime for pre-upgradability programs.
function_checksum::[PROG, FN_NAME]()[u8; 32]32-byte checksum of function FN_NAME inside PROG. Useful for pinning a dependency's function.

The PROG argument must be a program-ID literal (foo.aleo). FN_NAME must be an identifier literal ('bar').