Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Module 0x1::confidential_balance

Balance types for the confidential asset protocol.

A balances $a$ is chunked into vectors of $b$-bit chunks $[a_0, a_1, \ldots, a_{n-1}]$ such that $a = \sum_{i = 0}^{n-1} a_i B^i$ where $B = 2^b$. Then, each chunk $i$ is encrypted as a tuple $(P_i = a_i G + r_i H, R_i = r_i \mathsf{ek})$ under an encryption key $\mathsf{ek}$.

The pending balance has $n$ chunks while the available balance has $\ell$ such chunks. For Aptos, we need $b n = 64$ and $b \ell = 128$.

CompressedBalance<T> and Balance<T> are parameterized by phantom markers Pending and Available.

use 0x1::error;
use 0x1::ristretto255;
use 0x1::sigma_protocol_utils;
use 0x1::vector;

Struct Pending

struct Pending has drop
Fields
dummy_field: bool

Struct Available

struct Available has drop
Fields
dummy_field: bool

Enum CompressedBalance

enum CompressedBalance<T> has copy, drop, store
Variants
V1
Fields
P: vector<ristretto255::CompressedRistretto>
R: vector<ristretto255::CompressedRistretto>
R_aud: vector<ristretto255::CompressedRistretto>

Enum Balance

enum Balance<T> has drop
Variants
V1
Fields
P: vector<ristretto255::RistrettoPoint>
R: vector<ristretto255::RistrettoPoint>
R_aud: vector<ristretto255::RistrettoPoint>

Constants

The number of chunks $\ell$ in an available balance.

const AVAILABLE_BALANCE_CHUNKS: u64 = 8;

The number of bits $b$ in a single chunk.

const CHUNK_SIZE_BITS: u64 = 16;

All chunks are < than this value

const CHUNK_UPPER_BOUND: u64 = 65536;

Expected the P or R components to have the wrong number of chunks.

const E_WRONG_NUM_CHUNKS: u64 = 1;

Expected the auditor R-component to be either empty or have the correct number of chunks.

const E_WRONG_NUM_CHUNKS_FOR_AUDITOR: u64 = 2;

The number of chunks $n$ in a pending balance.

const PENDING_BALANCE_CHUNKS: u64 = 4;

Function get_P

public(friend) fun get_P<T>(self: &confidential_balance::Balance<T>): &vector<ristretto255::RistrettoPoint>
Implementation
public(friend) fun get_P<T>(self: &Balance<T>): &vector<RistrettoPoint> { &self.P }

Function get_R

public(friend) fun get_R<T>(self: &confidential_balance::Balance<T>): &vector<ristretto255::RistrettoPoint>
Implementation
public(friend) fun get_R<T>(self: &Balance<T>): &vector<RistrettoPoint> { &self.R }

Function get_R_aud

public(friend) fun get_R_aud<T>(self: &confidential_balance::Balance<T>): &vector<ristretto255::RistrettoPoint>
Implementation
public(friend) fun get_R_aud<T>(self: &Balance<T>): &vector<RistrettoPoint> { &self.R_aud }

Function get_compressed_P

public(friend) fun get_compressed_P<T>(self: &confidential_balance::CompressedBalance<T>): &vector<ristretto255::CompressedRistretto>
Implementation
public(friend) fun get_compressed_P<T>(self: &CompressedBalance<T>): &vector<CompressedRistretto> { &self.P }

Function get_compressed_R

public(friend) fun get_compressed_R<T>(self: &confidential_balance::CompressedBalance<T>): &vector<ristretto255::CompressedRistretto>
Implementation
public(friend) fun get_compressed_R<T>(self: &CompressedBalance<T>): &vector<CompressedRistretto> { &self.R }

Function get_compressed_R_aud

public(friend) fun get_compressed_R_aud<T>(self: &confidential_balance::CompressedBalance<T>): &vector<ristretto255::CompressedRistretto>
Implementation
public(friend) fun get_compressed_R_aud<T>(self: &CompressedBalance<T>): &vector<CompressedRistretto> { &self.R_aud }

Function compress

public(friend) fun compress<T>(self: &confidential_balance::Balance<T>): confidential_balance::CompressedBalance<T>
Implementation
public(friend) fun compress<T>(self: &Balance<T>): CompressedBalance<T> {
    CompressedBalance::V1 {
        P: self.P.map_ref(|p| p.point_compress()),
        R: self.R.map_ref(|r| r.point_compress()),
        R_aud: self.R_aud.map_ref(|r| r.point_compress()),
    }
}

Function decompress

public(friend) fun decompress<T>(self: &confidential_balance::CompressedBalance<T>): confidential_balance::Balance<T>
Implementation
public(friend) fun decompress<T>(self: &CompressedBalance<T>): Balance<T> {
    Balance::V1 {
        P: self.P.map_ref(|p| p.point_decompress()),
        R: self.R.map_ref(|r| r.point_decompress()),
        R_aud: self.R_aud.map_ref(|r| r.point_decompress()),
    }
}

Function is_zero

public(friend) fun is_zero<T>(self: &confidential_balance::CompressedBalance<T>): bool
Implementation
public(friend) fun is_zero<T>(self: &CompressedBalance<T>): bool {
    self.P.all(|p| p.is_identity()) &&
    self.R.all(|r| r.is_identity())
}

Function add_mut_base

Element-wise P and R addition. R_aud is NOT touched.

public(friend) fun add_mut_base<T>(self: &mut confidential_balance::Balance<T>, rhs_P: &vector<ristretto255::RistrettoPoint>, rhs_R: &vector<ristretto255::RistrettoPoint>)
Implementation
public(friend) fun add_mut_base<T>(self: &mut Balance<T>, rhs_P: &vector<RistrettoPoint>, rhs_R: &vector<RistrettoPoint>) {
    vector::range(0, rhs_P.length()).for_each(|i| {
        self.P[i].point_add_assign(&rhs_P[i]);
        self.R[i].point_add_assign(&rhs_R[i]);
    });
}

Function new_balance

fun new_balance<T>(p: vector<ristretto255::RistrettoPoint>, r: vector<ristretto255::RistrettoPoint>, r_aud: vector<ristretto255::RistrettoPoint>, expected_chunks: u64): confidential_balance::Balance<T>
Implementation
fun new_balance<T>(
    p: vector<RistrettoPoint>, r: vector<RistrettoPoint>, r_aud: vector<RistrettoPoint>,
    expected_chunks: u64,
): Balance<T> {
    assert_correct_num_chunks(&p, &r, &r_aud, expected_chunks);
    Balance::V1 { P: p, R: r, R_aud: r_aud }
}

Function new_compressed_balance

fun new_compressed_balance<T>(p: vector<ristretto255::CompressedRistretto>, r: vector<ristretto255::CompressedRistretto>, r_aud: vector<ristretto255::CompressedRistretto>, expected_chunks: u64): confidential_balance::CompressedBalance<T>
Implementation
fun new_compressed_balance<T>(
    p: vector<CompressedRistretto>, r: vector<CompressedRistretto>, r_aud: vector<CompressedRistretto>,
    expected_chunks: u64,
): CompressedBalance<T> {
    assert_correct_num_chunks(&p, &r, &r_aud, expected_chunks);
    CompressedBalance::V1 { P: p, R: r, R_aud: r_aud }
}

Function new_zero_compressed

fun new_zero_compressed<T>(num_chunks: u64): confidential_balance::CompressedBalance<T>
Implementation
fun new_zero_compressed<T>(num_chunks: u64): CompressedBalance<T> {
    let identity = point_identity_compressed();
    new_compressed_balance<T>(
        vector::range(0, num_chunks).map(|_| identity),
        vector::range(0, num_chunks).map(|_| identity),
        vector[],
        num_chunks
    )
}

Function new_pending_from_p_and_r

public(friend) fun new_pending_from_p_and_r(p: vector<ristretto255::RistrettoPoint>, r: vector<ristretto255::RistrettoPoint>): confidential_balance::Balance<confidential_balance::Pending>
Implementation
public(friend) fun new_pending_from_p_and_r(p: vector<RistrettoPoint>, r: vector<RistrettoPoint>): Balance<Pending> {
    new_balance(p, r, vector[], PENDING_BALANCE_CHUNKS)
}

Function new_zero_pending_compressed

public(friend) fun new_zero_pending_compressed(): confidential_balance::CompressedBalance<confidential_balance::Pending>
Implementation
public(friend) fun new_zero_pending_compressed(): CompressedBalance<Pending> {
    new_zero_compressed(PENDING_BALANCE_CHUNKS)
}

Function new_pending_u64_no_randomness

Creates a pending balance from a 64-bit amount with no randomness (R = identity).

public(friend) fun new_pending_u64_no_randomness(amount: u64): confidential_balance::Balance<confidential_balance::Pending>
Implementation
public(friend) fun new_pending_u64_no_randomness(amount: u64): Balance<Pending> {
    let identity = point_identity();
    new_pending_from_p_and_r(
        split_into_chunks((amount as u128), PENDING_BALANCE_CHUNKS).map(|chunk| chunk.basepoint_mul()),
        vector::range(0, PENDING_BALANCE_CHUNKS).map(|_| identity.point_clone()),
    )
}

Function add_assign_pending

Adds a pending balance to a compressed pending balance in place.

public(friend) fun add_assign_pending(balance: &mut confidential_balance::CompressedBalance<confidential_balance::Pending>, rhs: &confidential_balance::Balance<confidential_balance::Pending>): confidential_balance::CompressedBalance<confidential_balance::Pending>
Implementation
public(friend) fun add_assign_pending(balance: &mut CompressedBalance<Pending>, rhs: &Balance<Pending>): CompressedBalance<Pending> {
    let decompressed = balance.decompress();
    decompressed.add_mut_base(rhs.get_P(), rhs.get_R());
    *balance = decompressed.compress();
    *balance
}

Function get_num_pending_chunks

#[view]
public fun get_num_pending_chunks(): u64
Implementation
public fun get_num_pending_chunks(): u64 { PENDING_BALANCE_CHUNKS }

Function new_available_from_p_r_r_aud

public(friend) fun new_available_from_p_r_r_aud(p: vector<ristretto255::RistrettoPoint>, r: vector<ristretto255::RistrettoPoint>, r_aud: vector<ristretto255::RistrettoPoint>): confidential_balance::Balance<confidential_balance::Available>
Implementation
public(friend) fun new_available_from_p_r_r_aud(
    p: vector<RistrettoPoint>, r: vector<RistrettoPoint>, r_aud: vector<RistrettoPoint>
): Balance<Available> {
    new_balance(p, r, r_aud, AVAILABLE_BALANCE_CHUNKS)
}

Function new_compressed_available_from_p_r_r_aud

public(friend) fun new_compressed_available_from_p_r_r_aud(p: vector<ristretto255::CompressedRistretto>, r: vector<ristretto255::CompressedRistretto>, r_aud: vector<ristretto255::CompressedRistretto>): confidential_balance::CompressedBalance<confidential_balance::Available>
Implementation
public(friend) fun new_compressed_available_from_p_r_r_aud(
    p: vector<CompressedRistretto>, r: vector<CompressedRistretto>, r_aud: vector<CompressedRistretto>
): CompressedBalance<Available> {
    new_compressed_balance(p, r, r_aud, AVAILABLE_BALANCE_CHUNKS)
}

Function new_zero_available_compressed

public(friend) fun new_zero_available_compressed(): confidential_balance::CompressedBalance<confidential_balance::Available>
Implementation
public(friend) fun new_zero_available_compressed(): CompressedBalance<Available> {
    new_zero_compressed(AVAILABLE_BALANCE_CHUNKS)
}

Function new_compressed_available_from_bytes

Deserializes raw byte vectors into a CompressedBalance (without decompressing).

public(friend) fun new_compressed_available_from_bytes(p_bytes: vector<vector<u8>>, r_bytes: vector<vector<u8>>, r_aud_bytes: vector<vector<u8>>): confidential_balance::CompressedBalance<confidential_balance::Available>
Implementation
public(friend) fun new_compressed_available_from_bytes(
    p_bytes: vector<vector<u8>>,
    r_bytes: vector<vector<u8>>,
    r_aud_bytes: vector<vector<u8>>,
): CompressedBalance<Available> {
    new_compressed_available_from_p_r_r_aud(
        deserialize_compressed_points(p_bytes),
        deserialize_compressed_points(r_bytes),
        deserialize_compressed_points(r_aud_bytes),
    )
}

Function set_available_R

Sets only the R component (EK component) of a compressed available balance.

public(friend) fun set_available_R(balance: &mut confidential_balance::CompressedBalance<confidential_balance::Available>, new_R: vector<ristretto255::CompressedRistretto>)
Implementation
public(friend) fun set_available_R(balance: &mut CompressedBalance<Available>, new_R: vector<CompressedRistretto>) {
    assert!(new_R.length() == AVAILABLE_BALANCE_CHUNKS, error::invalid_argument(E_WRONG_NUM_CHUNKS));
    balance.R = new_R;
}

Function add_assign_available_excluding_auditor

Adds a pending balance to an available balance in place. R_aud is NOT touched (stale after rollover).

public(friend) fun add_assign_available_excluding_auditor(self: &mut confidential_balance::CompressedBalance<confidential_balance::Available>, rhs: &confidential_balance::CompressedBalance<confidential_balance::Pending>)
Implementation
public(friend) fun add_assign_available_excluding_auditor(self: &mut CompressedBalance<Available>, rhs: &CompressedBalance<Pending>) {
    let lhs_P = self.P.map_ref(|p| p.point_decompress());
    let lhs_R = self.R.map_ref(|r| r.point_decompress());
    let rhs_P = rhs.P.map_ref(|p| p.point_decompress());
    let rhs_R = rhs.R.map_ref(|r| r.point_decompress());

    vector::range(0, rhs_P.length()).for_each(|i| {
        lhs_P[i].point_add_assign(&rhs_P[i]);
        lhs_R[i].point_add_assign(&rhs_R[i]);
    });

    self.P = lhs_P.map_ref(|p| p.point_compress());
    self.R = lhs_R.map_ref(|r| r.point_compress());
}

Function get_num_available_chunks

#[view]
public fun get_num_available_chunks(): u64
Implementation
public fun get_num_available_chunks(): u64 { AVAILABLE_BALANCE_CHUNKS }

Function get_chunk_size_bits

public(friend) fun get_chunk_size_bits(): u64
Implementation
public(friend) fun get_chunk_size_bits(): u64 { CHUNK_SIZE_BITS }

Function get_chunk_upper_bound

Every balance chunk is $<$ than this bound (i.e., $< 2^{16}$).

public(friend) fun get_chunk_upper_bound(): u64
Implementation
public(friend) fun get_chunk_upper_bound(): u64 { CHUNK_UPPER_BOUND }

Function split_into_chunks

Splits amount into num_chunks 16-bit chunks as Scalar values.

fun split_into_chunks(amount: u128, num_chunks: u64): vector<ristretto255::Scalar>
Implementation
fun split_into_chunks(amount: u128, num_chunks: u64): vector<Scalar> {
    vector::range(0, num_chunks).map(|i| {
        new_scalar_from_u128(amount >> (i * CHUNK_SIZE_BITS as u8) & 0xffff)
    })
}

Function get_b_powers

Returns [B^0, B^1, …, B^{count-1}] where B = 2^chunk_size_bits.

public(friend) fun get_b_powers(count: u64): vector<ristretto255::Scalar>
Implementation
public(friend) fun get_b_powers(count: u64): vector<Scalar> {
    let b = new_scalar_from_u128((CHUNK_UPPER_BOUND as u128));
    let powers = vector[scalar_one()];
    let prev = scalar_one();
    for (i in 1..count) {
        prev = prev.scalar_mul(&b);
        powers.push_back(prev);
    };
    powers
}

Function get_encryption_key_basepoint_compressed

Returns the compressed generator H used to derive the encryption key as EK = DK^(-1) * H.

public(friend) fun get_encryption_key_basepoint_compressed(): ristretto255::CompressedRistretto
Implementation
public(friend) fun get_encryption_key_basepoint_compressed(): CompressedRistretto {
    ristretto255::basepoint_H_compressed()
}

Function assert_correct_num_chunks

fun assert_correct_num_chunks<T>(p: &vector<T>, r: &vector<T>, r_aud: &vector<T>, expected_chunks: u64)
Implementation
fun assert_correct_num_chunks<T>(p: &vector<T>, r: &vector<T>, r_aud: &vector<T>, expected_chunks: u64) {
    assert!(p.length() == expected_chunks, error::invalid_argument(E_WRONG_NUM_CHUNKS));
    assert!(r.length() == expected_chunks, error::invalid_argument(E_WRONG_NUM_CHUNKS));
    assert!(r_aud.is_empty() || r_aud.length() == expected_chunks, error::invalid_argument(
        E_WRONG_NUM_CHUNKS_FOR_AUDITOR
    ));
}