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::ristretto255_bulletproofs

This module implements a Bulletproof range proof verifier on the Ristretto255 curve.

A Bulletproof-based zero-knowledge range proof is a proof that a Pedersen commitment $c = v G + r H$ commits to an $n$-bit value $v$ (i.e., $v \in [0, 2^n)$). Currently, this module only supports $n \in {8, 16, 32, 64}$ for the number of bits.

The module also supports batch range proofs, allowing verification of multiple commitments in a single proof. Each commitment in the batch must satisfy the same range constraint $v \in [0, 2^n)$, and the supported batch sizes are limited to ${1, 2, 4, 8, 16}$.

use 0x1::error;
use 0x1::features;
use 0x1::ristretto255;
use 0x1::ristretto255_pedersen;

Struct RangeProof

Represents a zero-knowledge range proof that a value committed inside a Pedersen commitment lies in [0, 2^{MAX_RANGE_BITS}).

struct RangeProof has copy, drop, store
Fields
bytes: vector<u8>

Constants

The native functions have not been rolled out yet.

const E_NATIVE_FUN_NOT_AVAILABLE: u64 = 7;

The range proof system only supports batch sizes of 1, 2, 4, 8, and 16.

const E_BATCH_SIZE_NOT_SUPPORTED: u64 = 4;

There was an error deserializing the range proof.

const E_DESERIALIZE_RANGE_PROOF: u64 = 1;

The domain separation tag exceeded the 256-byte limit.

const E_DST_TOO_LONG: u64 = 6;

The range proof system only supports proving ranges of type $[0, 2^b)$ where $b \in {8, 16, 32, 64}$.

const E_RANGE_NOT_SUPPORTED: u64 = 3;

The committed value given to the prover is too large.

const E_VALUE_OUTSIDE_RANGE: u64 = 2;

The vector lengths of values and blinding factors do not match.

const E_VECTOR_LENGTHS_MISMATCH: u64 = 5;

The maximum range supported by the Bulletproofs library is $[0, 2^{64})$.

const MAX_RANGE_BITS: u64 = 64;

Function get_max_range_bits

Returns the maximum # of bits that the range proof system can verify proofs for.

public fun get_max_range_bits(): u64
Implementation
public fun get_max_range_bits(): u64 {
    MAX_RANGE_BITS
}

Function range_proof_from_bytes

Deserializes a range proof from a sequence of bytes. The serialization format is the same as the format in the zkcrypto’s bulletproofs library (https://docs.rs/bulletproofs/4.0.0/bulletproofs/struct.RangeProof.html#method.from_bytes).

public fun range_proof_from_bytes(bytes: vector<u8>): ristretto255_bulletproofs::RangeProof
Implementation
public fun range_proof_from_bytes(bytes: vector<u8>): RangeProof {
    RangeProof {
        bytes
    }
}

Function range_proof_to_bytes

Returns the byte-representation of a range proof.

public fun range_proof_to_bytes(proof: &ristretto255_bulletproofs::RangeProof): vector<u8>
Implementation
public fun range_proof_to_bytes(proof: &RangeProof): vector<u8> {
    proof.bytes
}

Function verify_range_proof_pedersen

Verifies a zero-knowledge range proof that the value v committed in com (under the default Bulletproofs commitment key; see pedersen::new_commitment_for_bulletproof) satisfies $v \in [0, 2^b)$. Only works for $b \in {8, 16, 32, 64}$. Additionally, checks that the prover used dst as the domain-separation tag (DST).

WARNING: The DST check is VERY important for security as it prevents proofs computed for one application (a.k.a., a domain) with dst_1 from verifying in a different application with dst_2 != dst_1.

NOTE: currently, domain separation tags of size larger than 256 bytes are not supported.

public fun verify_range_proof_pedersen(com: &ristretto255_pedersen::Commitment, proof: &ristretto255_bulletproofs::RangeProof, num_bits: u64, dst: vector<u8>): bool
Implementation
public fun verify_range_proof_pedersen(com: &pedersen::Commitment, proof: &RangeProof, num_bits: u64, dst: vector<u8>): bool {
    verify_range_proof(
        pedersen::commitment_as_point(com),
        &ristretto255::basepoint(), &ristretto255::hash_to_point_base(),
        proof,
        num_bits,
        dst
    )
}

Function verify_range_proof

Verifies a zero-knowledge range proof that the value v committed in com (as v * val_base + r * rand_base, for some randomness r) satisfies v in [0, 2^num_bits).

Only works for num_bits in {8, 16, 32, 64}.

NOTE: currently, domain separation tags of size larger than 256 bytes are not supported.

public fun verify_range_proof(com: &ristretto255::RistrettoPoint, val_base: &ristretto255::RistrettoPoint, rand_base: &ristretto255::RistrettoPoint, proof: &ristretto255_bulletproofs::RangeProof, num_bits: u64, dst: vector<u8>): bool
Implementation
public fun verify_range_proof(
    com: &RistrettoPoint,
    val_base: &RistrettoPoint, rand_base: &RistrettoPoint,
    proof: &RangeProof, num_bits: u64, dst: vector<u8>): bool
{
    assert!(features::bulletproofs_enabled(), error::invalid_state(E_NATIVE_FUN_NOT_AVAILABLE));
    assert!(dst.length() <= 256, error::invalid_argument(E_DST_TOO_LONG));

    verify_range_proof_internal(
        ristretto255::point_to_bytes(&ristretto255::point_compress(com)),
        val_base, rand_base,
        proof.bytes, num_bits, dst
    )
}

Function verify_batch_range_proof_pedersen

Verifies a zero-knowledge range proof for a batch of Pedersen commitments comms (under the default Bulletproofs commitment key; see pedersen::new_commitment_for_bulletproof), ensuring that all values v satisfy v in [0, 2^num_bits). Only works for num_bits in {8, 16, 32, 64} and batch size (length of comms) in {1, 2, 4, 8, 16}.

NOTE: currently, domain separation tags of size larger than 256 bytes are not supported.

public fun verify_batch_range_proof_pedersen(_comms: &vector<ristretto255_pedersen::Commitment>, _proof: &ristretto255_bulletproofs::RangeProof, _num_bits: u64, _dst: vector<u8>): bool
Implementation
public fun verify_batch_range_proof_pedersen(
    _comms: &vector<pedersen::Commitment>, _proof: &RangeProof,
    _num_bits: u64, _dst: vector<u8>): bool
{
    abort error::unavailable(E_NATIVE_FUN_NOT_AVAILABLE)
}

Function verify_batch_range_proof_pedersen_helper

fun verify_batch_range_proof_pedersen_helper(comms: &vector<ristretto255_pedersen::Commitment>, proof: &ristretto255_bulletproofs::RangeProof, num_bits: u64, dst: vector<u8>): bool
Implementation
fun verify_batch_range_proof_pedersen_helper(
    comms: &vector<pedersen::Commitment>, proof: &RangeProof,
    num_bits: u64, dst: vector<u8>): bool
{
    verify_batch_range_proof_helper(
        &comms.map_ref(|com| ristretto255::point_clone(pedersen::commitment_as_point(com))),
        &ristretto255::basepoint(), &ristretto255::hash_to_point_base(),
        proof,
        num_bits,
        dst
    )
}

Function verify_batch_range_proof

Verifies a zero-knowledge range proof for a batch of commitments comms (each of the form v * val_base + r * rand_base), ensuring that all values v satisfy v in [0, 2^num_bits). Only works for num_bits in {8, 16, 32, 64} and batch size (length of the comms) in {1, 2, 4, 8, 16}.

NOTE: currently, domain separation tags of size larger than 256 bytes are not supported.

public fun verify_batch_range_proof(_comms: &vector<ristretto255::RistrettoPoint>, _val_base: &ristretto255::RistrettoPoint, _rand_base: &ristretto255::RistrettoPoint, _proof: &ristretto255_bulletproofs::RangeProof, _num_bits: u64, _dst: vector<u8>): bool
Implementation
public fun verify_batch_range_proof(
    _comms: &vector<RistrettoPoint>,
    _val_base: &RistrettoPoint, _rand_base: &RistrettoPoint,
    _proof: &RangeProof, _num_bits: u64, _dst: vector<u8>): bool
{
    abort error::unavailable(E_NATIVE_FUN_NOT_AVAILABLE)
}

Function verify_batch_range_proof_helper

fun verify_batch_range_proof_helper(comms: &vector<ristretto255::RistrettoPoint>, val_base: &ristretto255::RistrettoPoint, rand_base: &ristretto255::RistrettoPoint, proof: &ristretto255_bulletproofs::RangeProof, num_bits: u64, dst: vector<u8>): bool
Implementation
fun verify_batch_range_proof_helper(
    comms: &vector<RistrettoPoint>,
    val_base: &RistrettoPoint, rand_base: &RistrettoPoint,
    proof: &RangeProof, num_bits: u64, dst: vector<u8>): bool
{
    assert!(features::bulletproofs_batch_enabled(), error::invalid_state(E_NATIVE_FUN_NOT_AVAILABLE));
    assert!(dst.length() <= 256, error::invalid_argument(E_DST_TOO_LONG));

    let comms = comms.map_ref(|com| ristretto255::point_to_bytes(&ristretto255::point_compress(com)));

    verify_batch_range_proof_internal(
        comms,
        val_base, rand_base,
        proof.bytes, num_bits, dst
    )
}

Function verify_range_proof_internal

Aborts with error::invalid_argument(E_DESERIALIZE_RANGE_PROOF) if proof is not a valid serialization of a range proof. Aborts with error::invalid_argument(E_RANGE_NOT_SUPPORTED) if an unsupported num_bits is provided.

fun verify_range_proof_internal(com: vector<u8>, val_base: &ristretto255::RistrettoPoint, rand_base: &ristretto255::RistrettoPoint, proof: vector<u8>, num_bits: u64, dst: vector<u8>): bool
Implementation
native fun verify_range_proof_internal(
    com: vector<u8>,
    val_base: &RistrettoPoint,
    rand_base: &RistrettoPoint,
    proof: vector<u8>,
    num_bits: u64,
    dst: vector<u8>): bool;

Function verify_batch_range_proof_internal

Aborts with error::invalid_argument(E_DESERIALIZE_RANGE_PROOF) if proof is not a valid serialization of a range proof. Aborts with error::invalid_argument(E_RANGE_NOT_SUPPORTED) if an unsupported num_bits is provided. Aborts with error::invalid_argument(E_BATCH_SIZE_NOT_SUPPORTED) if an unsupported batch size is provided. Aborts with error::invalid_argument(E_VECTOR_LENGTHS_MISMATCH) if the vector lengths of comms and proof do not match.

fun verify_batch_range_proof_internal(comms: vector<vector<u8>>, val_base: &ristretto255::RistrettoPoint, rand_base: &ristretto255::RistrettoPoint, proof: vector<u8>, num_bits: u64, dst: vector<u8>): bool
Implementation
native fun verify_batch_range_proof_internal(
    comms: vector<vector<u8>>,
    val_base: &RistrettoPoint,
    rand_base: &RistrettoPoint,
    proof: vector<u8>,
    num_bits: u64,
    dst: vector<u8>): bool;

Specification

Function verify_range_proof_internal

fun verify_range_proof_internal(com: vector<u8>, val_base: &ristretto255::RistrettoPoint, rand_base: &ristretto255::RistrettoPoint, proof: vector<u8>, num_bits: u64, dst: vector<u8>): bool
pragma opaque;

Function verify_batch_range_proof_internal

fun verify_batch_range_proof_internal(comms: vector<vector<u8>>, val_base: &ristretto255::RistrettoPoint, rand_base: &ristretto255::RistrettoPoint, proof: vector<u8>, num_bits: u64, dst: vector<u8>): bool
pragma opaque;