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.
- Struct
Pending - Struct
Available - Enum
CompressedBalance - Enum
Balance - Constants
- Function
get_P - Function
get_R - Function
get_R_aud - Function
get_compressed_P - Function
get_compressed_R - Function
get_compressed_R_aud - Function
compress - Function
decompress - Function
is_zero - Function
add_mut_base - Function
new_balance - Function
new_compressed_balance - Function
new_zero_compressed - Function
new_pending_from_p_and_r - Function
new_zero_pending_compressed - Function
new_pending_u64_no_randomness - Function
add_assign_pending - Function
get_num_pending_chunks - Function
new_available_from_p_r_r_aud - Function
new_compressed_available_from_p_r_r_aud - Function
new_zero_available_compressed - Function
new_compressed_available_from_bytes - Function
set_available_R - Function
add_assign_available_excluding_auditor - Function
get_num_available_chunks - Function
get_chunk_size_bits - Function
get_chunk_upper_bound - Function
split_into_chunks - Function
get_b_powers - Function
get_encryption_key_basepoint_compressed - Function
assert_correct_num_chunks
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
Enum Balance
enum Balance<T> has drop
Variants
V1
Fields
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>
Function get_R
public(friend) fun get_R<T>(self: &confidential_balance::Balance<T>): &vector<ristretto255::RistrettoPoint>
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
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
));
}