Cryptography Utilities
Overview
The Cryptography Utilities provide a set of cryptographic tools for Soroban smart contracts, including hash functions, Merkle tree verification, and Merkle-based distribution systems. These utilities enable secure data verification and efficient token distribution mechanisms. The Cryptography Utilities consist of two main packages:
-
Crypto: A set of cryptographic primitives and utilities for Soroban contracts.
-
Merkle Distributor: A system for distributing tokens or other assets using Merkle proofs for verification.
Crypto Package
The crypto package provides fundamental cryptographic primitives and utilities for Soroban contracts, with a focus on hashing and Merkle tree operations.
Key Components
Hashers
Provides a generic Hasher
trait and implementations for common hash functions:
-
Sha256
: Implementation of the SHA-256 hash function -
Keccak256
: Implementation of the Keccak-256 hash function (used in Ethereum)
Each hasher follows the same interface:
pub trait Hasher {
type Output;
fn new(e: &Env) -> Self;
fn update(&mut self, input: Bytes);
fn finalize(self) -> Self::Output;
}
Hashable
The Hashable
trait allows types to be hashed with any Hasher
implementation:
pub trait Hashable {
fn hash<H: Hasher>(&self, hasher: &mut H);
}
Built-in implementations are provided for BytesN<32>
and Bytes
.
Utility Functions
-
hash_pair
: Hashes two values together -
commutative_hash_pair
: Hashes two values in a deterministic order (important for Merkle trees)
Merkle Tree Verification
The Verifier
struct provides functionality to verify Merkle proofs:
impl<H> Verifier<H>
where
H: Hasher<Output = Bytes32>,
{
pub fn verify(e: &Env, proof: Vec<Bytes32>, root: Bytes32, leaf: Bytes32) -> bool {
// Implementation verifies that the leaf is part of the tree defined by root
}
}
Usage Examples
Hashing Data
use soroban_sdk::{Bytes, Env};
use stellar_crypto::keccak::Keccak256;
use stellar_crypto::hasher::Hasher;
// Hash some data with Keccak256
let e = Env::default();
let data = Bytes::from_slice(&e, "Hello, world!".as_bytes());
let mut hasher = Keccak256::new(&e);
hasher.update(data);
let hash = hasher.finalize();
Verifying a Merkle Proof
use soroban_sdk::{BytesN, Env, Vec};
use stellar_crypto::keccak::Keccak256;
use stellar_crypto::merkle::Verifier;
// Verify that a leaf is part of a Merkle tree
let e = Env::default();
let root = /* merkle root as BytesN<32> */;
let leaf = /* leaf to verify as BytesN<32> */;
let proof = /* proof as Vec<BytesN<32>> */;
let is_valid = Verifier::<Keccak256>::verify(&e, proof, root, leaf);
Merkle Distributor
The Merkle Distributor package builds on the crypto package to provide a system for distributing tokens or other assets using Merkle proofs for verification.
Key Concepts
Usage Example
use soroban_sdk::{contract, contractimpl, contracttype, Address, BytesN, Env, Vec};
use stellar_crypto::keccak::Keccak256;
use stellar_merkle_distributor::{IndexableNode, MerkleDistributor};
// Define a leaf node structure
#[contracttype]
struct LeafData {
pub index: u32,
pub address: Address,
pub amount: i128,
}
// Implement IndexableNode for the leaf structure
impl IndexableNode for LeafData {
fn index(&self) -> u32 {
self.index
}
}
#[contract]
pub struct TokenDistributor;
#[contractimpl]
impl TokenDistributor {
// Initialize the distributor with a Merkle root
pub fn initialize(e: &Env, root: BytesN<32>) {
MerkleDistributor::<Keccak256>::set_root(e, root);
}
// Claim tokens by providing a proof
pub fn claim(e: &Env, leaf: LeafData, proof: Vec<BytesN<32>>) {
// Verify the proof and mark as claimed
MerkleDistributor::<Keccak256>::verify_and_set_claimed(e, leaf.clone(), proof);
// Transfer tokens or perform other actions based on leaf data
// ...
}
// Check if an index has been claimed
pub fn is_claimed(e: &Env, index: u32) -> bool {
MerkleDistributor::<Keccak256>::is_claimed(e, index)
}
}
Use Cases
Token Airdrops
Efficiently distribute tokens to a large number of recipients without requiring individual transactions for each recipient.
NFT Distributions
Distribute NFTs to a whitelist of addresses, with each address potentially receiving different NFTs.