Crate dalek_rangeproofs [] [src]

A pure-Rust implementation of the Back-Maxwell rangeproof scheme defined in Confidential Assets (2017) by Poelstra, Back, Friedenbach, Maxwell, Wuille.

The scheme is instantiated using the Decaf group on Curve25519, as implemented in curve25519-dalek. Note that since the Decaf implementation in curve25519-dalek is currently UNFINISHED, UNREVIEWED, AND EXPERIMENTAL, so is this library.

This implementation hardcodes the ring size m = 3, as this is the most efficient choice. The number of rings n determines the range [0,3^n], as well as the size and speed of the proof.


Suppose we want to prove that 134492616741 is within [0,3^40]. Since 340 ≅ 0.66 (264 ), this is just slightly narrower than the range of a u64.

To construct a proof that 134492616741 is within [0,3^40], first choose orthogonal basepoints. Usually the basepoints would be set or distributed as system-wide parameters. Here we choose G to be the Decaf coset containing the ed25519 basepoint and generate the second basepoint as H = Hash(G). (Technically, G is actually a DecafBasepointTable, which implements Mul<&Scalar> using a precomputed table).

use sha2::Sha256;

use curve25519_dalek::constants as dalek_constants;
use curve25519_dalek::decaf::{DecafBasepointTable, DecafPoint};
use curve25519_dalek::scalar::Scalar;

let G = &dalek_constants::DECAF_ED25519_BASEPOINT_TABLE;
let H = DecafPoint::hash_from_bytes::<Sha256>(G.basepoint().compress().as_bytes());

You'll also need your value and a CSPRNG:

use rand::OsRng;

let mut csprng = OsRng::new().unwrap();
let value = 134492616741;

We can now create the rangeproof, like so:

use dalek_rangeproofs::RangeProof;

let (proof, commitment, blinding) =
    RangeProof::create(40, value, G, &H, &mut csprng).unwrap();

The output is the proof proof, as well as commitment = blinding*G + value*H.

We can serialize the proof using Serde. Here, we use CBOR.

extern crate serde_cbor;
let proof_bytes: Vec<u8> = serde_cbor::ser::to_vec_packed(&proof).unwrap();
assert_eq!(proof_bytes.len(), 4125);

In this case, the serde_cbor-encoded proof statement is approximately 6.5% larger than the optimal 3872 = 32(1+3*40) bytes. Another party can verify the proof statement from proof_bytes by doing:

extern crate serde_cbor;
use dalek_rangeproofs::RangeProof;
let proof: RangeProof = serde_cbor::from_slice(&proof_bytes).unwrap();

let C_option = proof.verify(40, G, &H);

let C = C_option.unwrap();

If the proof is well-formed, verify returns the commitment to the value. Since the commitment is the output of the verification, the verifier is assured it opens to a value in the range [0,3^n]. However, without knowing both blinding and the actual value, the verifier cannot open this commitment, because Pedersen commitments are computationally binding and perfectly hiding (in addition to being additively homomorphic, a feature used within this scheme).

Later, to open this commitment, the prover could reveal the value and the blinding, allowing others to check that commitment = blinding*G + value*H.

let C_hat = &(G * &blinding) + &(&H * &Scalar::from_u64(value));

assert_eq!(C_hat, C);



A Back-Maxwell rangeproof, which proves in zero knowledge that a number is in a range [0,m^n]. We hardcode m = 3 as this is the most efficient.



The maximum allowed bound for the rangeproof. Currently this is set to 41, because we only implement conversion to base 3 digits for u64s, and 341 is the least power of 3 greater than 2^64.