mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-11 21:49:26 +00:00
Smash out monero-bulletproofs
Removes usage of dalek-ff-group/multiexp for curve25519-dalek. Makes compiling in the generators an optional feature. Adds a structured batch verifier which should be notably more performant. Documentation and clean up still necessary.
This commit is contained in:
18
Cargo.lock
generated
18
Cargo.lock
generated
@@ -4751,6 +4751,22 @@ dependencies = [
|
|||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "monero-bulletproofs"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"curve25519-dalek",
|
||||||
|
"hex-literal",
|
||||||
|
"monero-generators",
|
||||||
|
"monero-io",
|
||||||
|
"monero-primitives",
|
||||||
|
"rand_core",
|
||||||
|
"std-shims",
|
||||||
|
"subtle",
|
||||||
|
"thiserror",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "monero-clsag"
|
name = "monero-clsag"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -4819,6 +4835,7 @@ dependencies = [
|
|||||||
"hex",
|
"hex",
|
||||||
"hex-literal",
|
"hex-literal",
|
||||||
"modular-frost",
|
"modular-frost",
|
||||||
|
"monero-bulletproofs",
|
||||||
"monero-clsag",
|
"monero-clsag",
|
||||||
"monero-generators",
|
"monero-generators",
|
||||||
"monero-io",
|
"monero-io",
|
||||||
@@ -8079,6 +8096,7 @@ dependencies = [
|
|||||||
"dleq",
|
"dleq",
|
||||||
"flexible-transcript",
|
"flexible-transcript",
|
||||||
"minimal-ed448",
|
"minimal-ed448",
|
||||||
|
"monero-bulletproofs",
|
||||||
"monero-clsag",
|
"monero-clsag",
|
||||||
"monero-generators",
|
"monero-generators",
|
||||||
"monero-io",
|
"monero-io",
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ members = [
|
|||||||
"coins/monero/generators",
|
"coins/monero/generators",
|
||||||
"coins/monero/primitives",
|
"coins/monero/primitives",
|
||||||
"coins/monero/ringct/clsag",
|
"coins/monero/ringct/clsag",
|
||||||
|
"coins/monero/ringct/bulletproofs",
|
||||||
"coins/monero",
|
"coins/monero",
|
||||||
|
|
||||||
"message-queue",
|
"message-queue",
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ rand_distr = { version = "0.4", default-features = false }
|
|||||||
sha3 = { version = "0.10", default-features = false }
|
sha3 = { version = "0.10", default-features = false }
|
||||||
pbkdf2 = { version = "0.12", features = ["simple"], default-features = false }
|
pbkdf2 = { version = "0.12", features = ["simple"], default-features = false }
|
||||||
|
|
||||||
curve25519-dalek = { version = "4", default-features = false, features = ["alloc", "zeroize", "precomputed-tables"] }
|
curve25519-dalek = { version = "4", default-features = false, features = ["alloc", "zeroize"] }
|
||||||
|
|
||||||
# Used for the hash to curve, along with the more complicated proofs
|
# Used for the hash to curve, along with the more complicated proofs
|
||||||
group = { version = "0.13", default-features = false }
|
group = { version = "0.13", default-features = false }
|
||||||
@@ -49,6 +49,7 @@ monero-io = { path = "io", version = "0.1", default-features = false }
|
|||||||
monero-generators = { path = "generators", version = "0.4", default-features = false }
|
monero-generators = { path = "generators", version = "0.4", default-features = false }
|
||||||
monero-primitives = { path = "primitives", version = "0.1", default-features = false }
|
monero-primitives = { path = "primitives", version = "0.1", default-features = false }
|
||||||
monero-clsag = { path = "ringct/clsag", version = "0.1", default-features = false }
|
monero-clsag = { path = "ringct/clsag", version = "0.1", default-features = false }
|
||||||
|
monero-bulletproofs = { path = "ringct/bulletproofs", version = "0.1", default-features = false }
|
||||||
|
|
||||||
hex-literal = "0.4"
|
hex-literal = "0.4"
|
||||||
hex = { version = "0.4", default-features = false, features = ["alloc"] }
|
hex = { version = "0.4", default-features = false, features = ["alloc"] }
|
||||||
@@ -96,6 +97,7 @@ std = [
|
|||||||
"monero-generators/std",
|
"monero-generators/std",
|
||||||
"monero-primitives/std",
|
"monero-primitives/std",
|
||||||
"monero-clsag/std",
|
"monero-clsag/std",
|
||||||
|
"monero-bulletproofs/std",
|
||||||
|
|
||||||
"hex/std",
|
"hex/std",
|
||||||
"serde/std",
|
"serde/std",
|
||||||
@@ -104,6 +106,7 @@ std = [
|
|||||||
"base58-monero/std",
|
"base58-monero/std",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
compile-time-generators = ["curve25519-dalek/precomputed-tables", "monero-bulletproofs/compile-time-generators"]
|
||||||
http-rpc = ["digest_auth", "simple-request", "tokio"]
|
http-rpc = ["digest_auth", "simple-request", "tokio"]
|
||||||
multisig = ["transcript", "frost", "monero-clsag/multisig", "std"]
|
multisig = ["transcript", "frost", "monero-clsag/multisig", "std"]
|
||||||
binaries = ["tokio/rt-multi-thread", "tokio/macros", "http-rpc"]
|
binaries = ["tokio/rt-multi-thread", "tokio/macros", "http-rpc"]
|
||||||
|
|||||||
@@ -1,67 +0,0 @@
|
|||||||
use std::{
|
|
||||||
io::Write,
|
|
||||||
env,
|
|
||||||
path::Path,
|
|
||||||
fs::{File, remove_file},
|
|
||||||
};
|
|
||||||
|
|
||||||
use dalek_ff_group::EdwardsPoint;
|
|
||||||
|
|
||||||
use monero_generators::bulletproofs_generators;
|
|
||||||
|
|
||||||
fn serialize(generators_string: &mut String, points: &[EdwardsPoint]) {
|
|
||||||
for generator in points {
|
|
||||||
generators_string.extend(
|
|
||||||
format!(
|
|
||||||
"
|
|
||||||
dalek_ff_group::EdwardsPoint(
|
|
||||||
curve25519_dalek::edwards::CompressedEdwardsY({:?}).decompress().unwrap()
|
|
||||||
),
|
|
||||||
",
|
|
||||||
generator.compress().to_bytes()
|
|
||||||
)
|
|
||||||
.chars(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generators(prefix: &'static str, path: &str) {
|
|
||||||
let generators = bulletproofs_generators(prefix.as_bytes());
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
let mut G_str = String::new();
|
|
||||||
serialize(&mut G_str, &generators.G);
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
let mut H_str = String::new();
|
|
||||||
serialize(&mut H_str, &generators.H);
|
|
||||||
|
|
||||||
let path = Path::new(&env::var("OUT_DIR").unwrap()).join(path);
|
|
||||||
let _ = remove_file(&path);
|
|
||||||
File::create(&path)
|
|
||||||
.unwrap()
|
|
||||||
.write_all(
|
|
||||||
format!(
|
|
||||||
"
|
|
||||||
pub(crate) static GENERATORS_CELL: OnceLock<Generators> = OnceLock::new();
|
|
||||||
pub fn GENERATORS() -> &'static Generators {{
|
|
||||||
GENERATORS_CELL.get_or_init(|| Generators {{
|
|
||||||
G: vec![
|
|
||||||
{G_str}
|
|
||||||
],
|
|
||||||
H: vec![
|
|
||||||
{H_str}
|
|
||||||
],
|
|
||||||
}})
|
|
||||||
}}
|
|
||||||
",
|
|
||||||
)
|
|
||||||
.as_bytes(),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
println!("cargo:rerun-if-changed=build.rs");
|
|
||||||
|
|
||||||
generators("bulletproof", "generators.rs");
|
|
||||||
generators("bulletproof_plus", "generators_plus.rs");
|
|
||||||
}
|
|
||||||
@@ -37,7 +37,6 @@ std = [
|
|||||||
"subtle/std",
|
"subtle/std",
|
||||||
|
|
||||||
"sha3/std",
|
"sha3/std",
|
||||||
"curve25519-dalek/precomputed-tables",
|
|
||||||
|
|
||||||
"group/alloc",
|
"group/alloc",
|
||||||
"dalek-ff-group/std",
|
"dalek-ff-group/std",
|
||||||
|
|||||||
@@ -6,10 +6,7 @@ use std_shims::{sync::OnceLock, vec::Vec};
|
|||||||
|
|
||||||
use sha3::{Digest, Keccak256};
|
use sha3::{Digest, Keccak256};
|
||||||
|
|
||||||
use curve25519_dalek::edwards::{EdwardsPoint as DalekPoint};
|
use curve25519_dalek::{constants::ED25519_BASEPOINT_POINT, edwards::EdwardsPoint};
|
||||||
|
|
||||||
use group::{Group, GroupEncoding};
|
|
||||||
use dalek_ff_group::EdwardsPoint;
|
|
||||||
|
|
||||||
use monero_io::{write_varint, decompress_point};
|
use monero_io::{write_varint, decompress_point};
|
||||||
|
|
||||||
@@ -23,24 +20,26 @@ fn keccak256(data: &[u8]) -> [u8; 32] {
|
|||||||
Keccak256::digest(data).into()
|
Keccak256::digest(data).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
static H_CELL: OnceLock<DalekPoint> = OnceLock::new();
|
static H_CELL: OnceLock<EdwardsPoint> = OnceLock::new();
|
||||||
/// Monero's `H` generator.
|
/// Monero's `H` generator.
|
||||||
///
|
///
|
||||||
/// Contrary to convention (`G` for values, `H` for randomness), `H` is used by Monero for amounts
|
/// Contrary to convention (`G` for values, `H` for randomness), `H` is used by Monero for amounts
|
||||||
/// within Pedersen commitments.
|
/// within Pedersen commitments.
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
pub fn H() -> DalekPoint {
|
pub fn H() -> EdwardsPoint {
|
||||||
*H_CELL.get_or_init(|| {
|
*H_CELL.get_or_init(|| {
|
||||||
decompress_point(keccak256(&EdwardsPoint::generator().to_bytes())).unwrap().mul_by_cofactor()
|
decompress_point(keccak256(&ED25519_BASEPOINT_POINT.compress().to_bytes()))
|
||||||
|
.unwrap()
|
||||||
|
.mul_by_cofactor()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
static H_POW_2_CELL: OnceLock<[DalekPoint; 64]> = OnceLock::new();
|
static H_POW_2_CELL: OnceLock<[EdwardsPoint; 64]> = OnceLock::new();
|
||||||
/// Monero's `H` generator, multiplied by 2**i for i in 1 ..= 64.
|
/// Monero's `H` generator, multiplied by 2**i for i in 1 ..= 64.
|
||||||
///
|
///
|
||||||
/// This table is useful when working with amounts, which are u64s.
|
/// This table is useful when working with amounts, which are u64s.
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
pub fn H_pow_2() -> &'static [DalekPoint; 64] {
|
pub fn H_pow_2() -> &'static [EdwardsPoint; 64] {
|
||||||
H_POW_2_CELL.get_or_init(|| {
|
H_POW_2_CELL.get_or_init(|| {
|
||||||
let mut res = [H(); 64];
|
let mut res = [H(); 64];
|
||||||
for i in 1 .. 64 {
|
for i in 1 .. 64 {
|
||||||
@@ -79,11 +78,11 @@ pub fn bulletproofs_generators(dst: &'static [u8]) -> Generators {
|
|||||||
|
|
||||||
let mut even = preimage.clone();
|
let mut even = preimage.clone();
|
||||||
write_varint(&i, &mut even).unwrap();
|
write_varint(&i, &mut even).unwrap();
|
||||||
res.H.push(EdwardsPoint(hash_to_point(keccak256(&even))));
|
res.H.push(hash_to_point(keccak256(&even)));
|
||||||
|
|
||||||
let mut odd = preimage.clone();
|
let mut odd = preimage.clone();
|
||||||
write_varint(&(i + 1), &mut odd).unwrap();
|
write_varint(&(i + 1), &mut odd).unwrap();
|
||||||
res.G.push(EdwardsPoint(hash_to_point(keccak256(&odd))));
|
res.G.push(hash_to_point(keccak256(&odd)));
|
||||||
}
|
}
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ std = [
|
|||||||
"zeroize/std",
|
"zeroize/std",
|
||||||
|
|
||||||
"sha3/std",
|
"sha3/std",
|
||||||
"curve25519-dalek/precomputed-tables",
|
|
||||||
|
|
||||||
"monero-generators/std",
|
"monero-generators/std",
|
||||||
]
|
]
|
||||||
|
|||||||
57
coins/monero/ringct/bulletproofs/Cargo.toml
Normal file
57
coins/monero/ringct/bulletproofs/Cargo.toml
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
[package]
|
||||||
|
name = "monero-bulletproofs"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Bulletproofs(+) range proofs, as defined by the Monero protocol"
|
||||||
|
license = "MIT"
|
||||||
|
repository = "https://github.com/serai-dex/serai/tree/develop/coins/monero/ringct/bulletproofs"
|
||||||
|
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||||
|
edition = "2021"
|
||||||
|
rust-version = "1.79"
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
all-features = true
|
||||||
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
std-shims = { path = "../../../../common/std-shims", version = "^0.1.1", default-features = false }
|
||||||
|
|
||||||
|
thiserror = { version = "1", default-features = false, optional = true }
|
||||||
|
|
||||||
|
rand_core = { version = "0.6", default-features = false }
|
||||||
|
zeroize = { version = "^1.5", default-features = false, features = ["zeroize_derive"] }
|
||||||
|
subtle = { version = "^2.4", default-features = false }
|
||||||
|
|
||||||
|
# Cryptographic dependencies
|
||||||
|
curve25519-dalek = { version = "4", default-features = false, features = ["alloc", "zeroize"] }
|
||||||
|
|
||||||
|
# Other Monero dependencies
|
||||||
|
monero-io = { path = "../../io", version = "0.1", default-features = false }
|
||||||
|
monero-generators = { path = "../../generators", version = "0.4", default-features = false }
|
||||||
|
monero-primitives = { path = "../../primitives", version = "0.1", default-features = false }
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
curve25519-dalek = { version = "4", default-features = false, features = ["alloc", "zeroize"] }
|
||||||
|
monero-generators = { path = "../../generators", version = "0.4", default-features = false }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
hex-literal = "0.4"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
std = [
|
||||||
|
"std-shims/std",
|
||||||
|
|
||||||
|
"thiserror",
|
||||||
|
|
||||||
|
"rand_core/std",
|
||||||
|
"zeroize/std",
|
||||||
|
"subtle/std",
|
||||||
|
|
||||||
|
"monero-io/std",
|
||||||
|
"monero-generators/std",
|
||||||
|
"monero-primitives/std",
|
||||||
|
]
|
||||||
|
compile-time-generators = ["curve25519-dalek/precomputed-tables"]
|
||||||
|
default = ["std"]
|
||||||
21
coins/monero/ringct/bulletproofs/LICENSE
Normal file
21
coins/monero/ringct/bulletproofs/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2022-2024 Luke Parker
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
6
coins/monero/ringct/bulletproofs/README.md
Normal file
6
coins/monero/ringct/bulletproofs/README.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# Monero Bulletproofs(+)
|
||||||
|
|
||||||
|
Bulletproofs(+) range proofs, as defined by the Monero protocol.
|
||||||
|
|
||||||
|
This library is usable under no-std when the `std` feature (on by default) is
|
||||||
|
disabled.
|
||||||
88
coins/monero/ringct/bulletproofs/build.rs
Normal file
88
coins/monero/ringct/bulletproofs/build.rs
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
use std::{
|
||||||
|
io::Write,
|
||||||
|
env,
|
||||||
|
path::Path,
|
||||||
|
fs::{File, remove_file},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "compile-time-generators")]
|
||||||
|
fn generators(prefix: &'static str, path: &str) {
|
||||||
|
use curve25519_dalek::EdwardsPoint;
|
||||||
|
|
||||||
|
use monero_generators::bulletproofs_generators;
|
||||||
|
|
||||||
|
fn serialize(generators_string: &mut String, points: &[EdwardsPoint]) {
|
||||||
|
for generator in points {
|
||||||
|
generators_string.extend(
|
||||||
|
format!(
|
||||||
|
"
|
||||||
|
curve25519_dalek::edwards::CompressedEdwardsY({:?}).decompress().unwrap(),
|
||||||
|
",
|
||||||
|
generator.compress().to_bytes()
|
||||||
|
)
|
||||||
|
.chars(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let generators = bulletproofs_generators(prefix.as_bytes());
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
let mut G_str = String::new();
|
||||||
|
serialize(&mut G_str, &generators.G);
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
let mut H_str = String::new();
|
||||||
|
serialize(&mut H_str, &generators.H);
|
||||||
|
|
||||||
|
let path = Path::new(&env::var("OUT_DIR").unwrap()).join(path);
|
||||||
|
let _ = remove_file(&path);
|
||||||
|
File::create(&path)
|
||||||
|
.unwrap()
|
||||||
|
.write_all(
|
||||||
|
format!(
|
||||||
|
"
|
||||||
|
static GENERATORS_CELL: OnceLock<Generators> = OnceLock::new();
|
||||||
|
pub(crate) fn GENERATORS() -> &'static Generators {{
|
||||||
|
GENERATORS_CELL.get_or_init(|| Generators {{
|
||||||
|
G: vec![
|
||||||
|
{G_str}
|
||||||
|
],
|
||||||
|
H: vec![
|
||||||
|
{H_str}
|
||||||
|
],
|
||||||
|
}})
|
||||||
|
}}
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.as_bytes(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "compile-time-generators"))]
|
||||||
|
fn generators(prefix: &'static str, path: &str) {
|
||||||
|
let path = Path::new(&env::var("OUT_DIR").unwrap()).join(path);
|
||||||
|
let _ = remove_file(&path);
|
||||||
|
File::create(&path)
|
||||||
|
.unwrap()
|
||||||
|
.write_all(
|
||||||
|
format!(
|
||||||
|
r#"
|
||||||
|
static GENERATORS_CELL: OnceLock<Generators> = OnceLock::new();
|
||||||
|
pub(crate) fn GENERATORS() -> &'static Generators {{
|
||||||
|
GENERATORS_CELL.get_or_init(|| {{
|
||||||
|
monero_generators::bulletproofs_generators(b"{prefix}")
|
||||||
|
}})
|
||||||
|
}}
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.as_bytes(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println!("cargo:rerun-if-changed=build.rs");
|
||||||
|
|
||||||
|
generators("bulletproof", "generators.rs");
|
||||||
|
generators("bulletproof_plus", "generators_plus.rs");
|
||||||
|
}
|
||||||
94
coins/monero/ringct/bulletproofs/src/batch_verifier.rs
Normal file
94
coins/monero/ringct/bulletproofs/src/batch_verifier.rs
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
use curve25519_dalek::{
|
||||||
|
constants::ED25519_BASEPOINT_POINT,
|
||||||
|
traits::{IsIdentity, VartimeMultiscalarMul},
|
||||||
|
scalar::Scalar,
|
||||||
|
edwards::EdwardsPoint,
|
||||||
|
};
|
||||||
|
|
||||||
|
use monero_generators::{H, Generators};
|
||||||
|
|
||||||
|
use crate::{original, plus};
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub(crate) struct InternalBatchVerifier {
|
||||||
|
pub(crate) g: Scalar,
|
||||||
|
pub(crate) h: Scalar,
|
||||||
|
pub(crate) g_bold: Vec<Scalar>,
|
||||||
|
pub(crate) h_bold: Vec<Scalar>,
|
||||||
|
pub(crate) other: Vec<(Scalar, EdwardsPoint)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InternalBatchVerifier {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self { g: Scalar::ZERO, h: Scalar::ZERO, g_bold: vec![], h_bold: vec![], other: vec![] }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn verify(self, G: EdwardsPoint, H: EdwardsPoint, generators: &Generators) -> bool {
|
||||||
|
let capacity = 2 + self.g_bold.len() + self.h_bold.len() + self.other.len();
|
||||||
|
let mut scalars = Vec::with_capacity(capacity);
|
||||||
|
let mut points = Vec::with_capacity(capacity);
|
||||||
|
|
||||||
|
scalars.push(self.g);
|
||||||
|
points.push(G);
|
||||||
|
|
||||||
|
scalars.push(self.h);
|
||||||
|
points.push(H);
|
||||||
|
|
||||||
|
for (i, g_bold) in self.g_bold.into_iter().enumerate() {
|
||||||
|
scalars.push(g_bold);
|
||||||
|
points.push(generators.G[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i, h_bold) in self.h_bold.into_iter().enumerate() {
|
||||||
|
scalars.push(h_bold);
|
||||||
|
points.push(generators.H[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (scalar, point) in self.other {
|
||||||
|
scalars.push(scalar);
|
||||||
|
points.push(point);
|
||||||
|
}
|
||||||
|
|
||||||
|
EdwardsPoint::vartime_multiscalar_mul(scalars, points).is_identity()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub(crate) struct BulletproofsBatchVerifier(pub(crate) InternalBatchVerifier);
|
||||||
|
impl BulletproofsBatchVerifier {
|
||||||
|
#[must_use]
|
||||||
|
pub fn verify(self) -> bool {
|
||||||
|
self.0.verify(ED25519_BASEPOINT_POINT, H(), original::GENERATORS())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub(crate) struct BulletproofsPlusBatchVerifier(pub(crate) InternalBatchVerifier);
|
||||||
|
impl BulletproofsPlusBatchVerifier {
|
||||||
|
#[must_use]
|
||||||
|
pub fn verify(self) -> bool {
|
||||||
|
// Bulletproofs+ is written as per the paper, with G for the value and H for the mask
|
||||||
|
// Monero uses H for the value and G for the mask
|
||||||
|
self.0.verify(H(), ED25519_BASEPOINT_POINT, plus::GENERATORS())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct BatchVerifier {
|
||||||
|
pub(crate) original: BulletproofsBatchVerifier,
|
||||||
|
pub(crate) plus: BulletproofsPlusBatchVerifier,
|
||||||
|
}
|
||||||
|
impl BatchVerifier {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
original: BulletproofsBatchVerifier(InternalBatchVerifier::new()),
|
||||||
|
plus: BulletproofsPlusBatchVerifier(InternalBatchVerifier::new()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn verify(self) -> bool {
|
||||||
|
self.original.verify() && self.plus.verify()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,42 +1,44 @@
|
|||||||
use std_shims::{vec::Vec, sync::OnceLock};
|
use std_shims::{vec::Vec, sync::OnceLock};
|
||||||
|
|
||||||
use rand_core::{RngCore, CryptoRng};
|
use rand_core::{RngCore, CryptoRng};
|
||||||
|
|
||||||
use subtle::{Choice, ConditionallySelectable};
|
use subtle::{Choice, ConditionallySelectable};
|
||||||
|
|
||||||
use curve25519_dalek::edwards::EdwardsPoint as DalekPoint;
|
use curve25519_dalek::{
|
||||||
|
constants::ED25519_BASEPOINT_TABLE,
|
||||||
use group::{ff::Field, Group};
|
traits::{MultiscalarMul, VartimeMultiscalarMul},
|
||||||
use dalek_ff_group::{Scalar, EdwardsPoint};
|
scalar::Scalar,
|
||||||
|
edwards::EdwardsPoint,
|
||||||
use multiexp::multiexp as multiexp_const;
|
};
|
||||||
|
|
||||||
pub(crate) use monero_generators::Generators;
|
pub(crate) use monero_generators::Generators;
|
||||||
|
use monero_primitives::{INV_EIGHT, Commitment, keccak256_to_scalar};
|
||||||
|
|
||||||
use crate::{INV_EIGHT as DALEK_INV_EIGHT, H as DALEK_H, Commitment, hash_to_scalar as dalek_hash};
|
pub(crate) use crate::scalar_vector::*;
|
||||||
pub(crate) use crate::ringct::bulletproofs::scalar_vector::*;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn INV_EIGHT() -> Scalar {
|
|
||||||
Scalar(DALEK_INV_EIGHT())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn H() -> EdwardsPoint {
|
|
||||||
EdwardsPoint(DALEK_H())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn hash_to_scalar(data: &[u8]) -> Scalar {
|
|
||||||
Scalar(dalek_hash(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Components common between variants
|
// Components common between variants
|
||||||
|
// TODO: Move to generators? primitives?
|
||||||
pub(crate) const MAX_M: usize = 16;
|
pub(crate) const MAX_M: usize = 16;
|
||||||
pub(crate) const LOG_N: usize = 6; // 2 << 6 == N
|
pub(crate) const LOG_N: usize = 6; // 2 << 6 == N
|
||||||
pub(crate) const N: usize = 64;
|
pub(crate) const N: usize = 64;
|
||||||
|
|
||||||
pub(crate) fn prove_multiexp(pairs: &[(Scalar, EdwardsPoint)]) -> EdwardsPoint {
|
pub(crate) fn multiexp(pairs: &[(Scalar, EdwardsPoint)]) -> EdwardsPoint {
|
||||||
multiexp_const(pairs) * INV_EIGHT()
|
let mut buf_scalars = Vec::with_capacity(pairs.len());
|
||||||
|
let mut buf_points = Vec::with_capacity(pairs.len());
|
||||||
|
for (scalar, point) in pairs {
|
||||||
|
buf_scalars.push(scalar);
|
||||||
|
buf_points.push(point);
|
||||||
|
}
|
||||||
|
EdwardsPoint::multiscalar_mul(buf_scalars, buf_points)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn multiexp_vartime(pairs: &[(Scalar, EdwardsPoint)]) -> EdwardsPoint {
|
||||||
|
let mut buf_scalars = Vec::with_capacity(pairs.len());
|
||||||
|
let mut buf_points = Vec::with_capacity(pairs.len());
|
||||||
|
for (scalar, point) in pairs {
|
||||||
|
buf_scalars.push(scalar);
|
||||||
|
buf_points.push(point);
|
||||||
|
}
|
||||||
|
EdwardsPoint::vartime_multiscalar_mul(buf_scalars, buf_points)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn vector_exponent(
|
pub(crate) fn vector_exponent(
|
||||||
@@ -52,7 +54,7 @@ pub(crate) fn hash_cache(cache: &mut Scalar, mash: &[[u8; 32]]) -> Scalar {
|
|||||||
let slice =
|
let slice =
|
||||||
&[cache.to_bytes().as_ref(), mash.iter().copied().flatten().collect::<Vec<_>>().as_ref()]
|
&[cache.to_bytes().as_ref(), mash.iter().copied().flatten().collect::<Vec<_>>().as_ref()]
|
||||||
.concat();
|
.concat();
|
||||||
*cache = hash_to_scalar(slice);
|
*cache = keccak256_to_scalar(slice);
|
||||||
*cache
|
*cache
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,11 +90,11 @@ pub(crate) fn bit_decompose(commitments: &[Commitment]) -> (ScalarVector, Scalar
|
|||||||
(aL, aR)
|
(aL, aR)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn hash_commitments<C: IntoIterator<Item = DalekPoint>>(
|
pub(crate) fn hash_commitments<C: IntoIterator<Item = EdwardsPoint>>(
|
||||||
commitments: C,
|
commitments: C,
|
||||||
) -> (Scalar, Vec<EdwardsPoint>) {
|
) -> (Scalar, Vec<EdwardsPoint>) {
|
||||||
let V = commitments.into_iter().map(|c| EdwardsPoint(c) * INV_EIGHT()).collect::<Vec<_>>();
|
let V = commitments.into_iter().map(|c| c * INV_EIGHT()).collect::<Vec<_>>();
|
||||||
(hash_to_scalar(&V.iter().flat_map(|V| V.compress().to_bytes()).collect::<Vec<_>>()), V)
|
(keccak256_to_scalar(V.iter().flat_map(|V| V.compress().to_bytes()).collect::<Vec<_>>()), V)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn alpha_rho<R: RngCore + CryptoRng>(
|
pub(crate) fn alpha_rho<R: RngCore + CryptoRng>(
|
||||||
@@ -102,7 +104,7 @@ pub(crate) fn alpha_rho<R: RngCore + CryptoRng>(
|
|||||||
aR: &ScalarVector,
|
aR: &ScalarVector,
|
||||||
) -> (Scalar, EdwardsPoint) {
|
) -> (Scalar, EdwardsPoint) {
|
||||||
let ar = Scalar::random(rng);
|
let ar = Scalar::random(rng);
|
||||||
(ar, (vector_exponent(generators, aL, aR) + (EdwardsPoint::generator() * ar)) * INV_EIGHT())
|
(ar, (vector_exponent(generators, aL, aR) + (ED25519_BASEPOINT_TABLE * &ar)) * INV_EIGHT())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn LR_statements(
|
pub(crate) fn LR_statements(
|
||||||
@@ -144,7 +146,7 @@ pub(crate) fn challenge_products(w: &[Scalar], winv: &[Scalar]) -> Vec<Scalar> {
|
|||||||
|
|
||||||
// Sanity check as if the above failed to populate, it'd be critical
|
// Sanity check as if the above failed to populate, it'd be critical
|
||||||
for w in &products {
|
for w in &products {
|
||||||
debug_assert!(!bool::from(w.is_zero()));
|
debug_assert!(*w != Scalar::ZERO);
|
||||||
}
|
}
|
||||||
|
|
||||||
products
|
products
|
||||||
@@ -1,3 +1,6 @@
|
|||||||
|
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||||
|
#![doc = include_str!("../README.md")]
|
||||||
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
use std_shims::{
|
use std_shims::{
|
||||||
@@ -6,25 +9,39 @@ use std_shims::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use rand_core::{RngCore, CryptoRng};
|
use rand_core::{RngCore, CryptoRng};
|
||||||
|
use zeroize::Zeroizing;
|
||||||
use zeroize::{Zeroize, Zeroizing};
|
|
||||||
|
|
||||||
use curve25519_dalek::edwards::EdwardsPoint;
|
use curve25519_dalek::edwards::EdwardsPoint;
|
||||||
use multiexp::BatchVerifier;
|
|
||||||
|
|
||||||
use crate::{Commitment, wallet::TransactionError, serialize::*};
|
use monero_io::*;
|
||||||
|
use monero_primitives::Commitment;
|
||||||
|
|
||||||
pub(crate) mod scalar_vector;
|
pub(crate) mod scalar_vector;
|
||||||
pub(crate) mod core;
|
pub(crate) mod core;
|
||||||
use self::core::LOG_N;
|
use crate::core::LOG_N;
|
||||||
|
|
||||||
|
pub mod batch_verifier;
|
||||||
|
use batch_verifier::{InternalBatchVerifier, BulletproofsPlusBatchVerifier, BatchVerifier};
|
||||||
|
|
||||||
pub(crate) mod original;
|
pub(crate) mod original;
|
||||||
use self::original::OriginalStruct;
|
use crate::original::OriginalStruct;
|
||||||
|
|
||||||
pub(crate) mod plus;
|
pub(crate) mod plus;
|
||||||
use self::plus::*;
|
use crate::plus::*;
|
||||||
|
|
||||||
pub(crate) const MAX_OUTPUTS: usize = self::core::MAX_M;
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
|
||||||
|
pub const MAX_COMMITMENTS: usize = crate::core::MAX_M;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||||
|
#[cfg_attr(feature = "std", derive(thiserror::Error))]
|
||||||
|
pub enum BulletproofError {
|
||||||
|
#[cfg_attr(feature = "std", error("no commitments to prove the range for"))]
|
||||||
|
NoCommitments,
|
||||||
|
#[cfg_attr(feature = "std", error("too many commitments to prove the range for"))]
|
||||||
|
TooManyCommitments,
|
||||||
|
}
|
||||||
|
|
||||||
/// Bulletproof enum, encapsulating both Bulletproofs and Bulletproofs+.
|
/// Bulletproof enum, encapsulating both Bulletproofs and Bulletproofs+.
|
||||||
#[allow(clippy::large_enum_variant)]
|
#[allow(clippy::large_enum_variant)]
|
||||||
@@ -45,7 +62,7 @@ impl Bulletproof {
|
|||||||
|
|
||||||
// https://github.com/monero-project/monero/blob/94e67bf96bbc010241f29ada6abc89f49a81759c/
|
// https://github.com/monero-project/monero/blob/94e67bf96bbc010241f29ada6abc89f49a81759c/
|
||||||
// src/cryptonote_basic/cryptonote_format_utils.cpp#L106-L124
|
// src/cryptonote_basic/cryptonote_format_utils.cpp#L106-L124
|
||||||
pub(crate) fn calculate_bp_clawback(plus: bool, n_outputs: usize) -> (usize, usize) {
|
pub fn calculate_bp_clawback(plus: bool, n_outputs: usize) -> (usize, usize) {
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
let mut LR_len = 0;
|
let mut LR_len = 0;
|
||||||
let mut n_padded_outputs = 1;
|
let mut n_padded_outputs = 1;
|
||||||
@@ -66,7 +83,7 @@ impl Bulletproof {
|
|||||||
(bp_clawback, LR_len)
|
(bp_clawback, LR_len)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn fee_weight(plus: bool, outputs: usize) -> usize {
|
pub fn fee_weight(plus: bool, outputs: usize) -> usize {
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
let (bp_clawback, LR_len) = Bulletproof::calculate_bp_clawback(plus, outputs);
|
let (bp_clawback, LR_len) = Bulletproof::calculate_bp_clawback(plus, outputs);
|
||||||
32 * (Bulletproof::bp_fields(plus) + (2 * LR_len)) + 2 + bp_clawback
|
32 * (Bulletproof::bp_fields(plus) + (2 * LR_len)) + 2 + bp_clawback
|
||||||
@@ -76,12 +93,12 @@ impl Bulletproof {
|
|||||||
pub fn prove<R: RngCore + CryptoRng>(
|
pub fn prove<R: RngCore + CryptoRng>(
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
outputs: &[Commitment],
|
outputs: &[Commitment],
|
||||||
) -> Result<Bulletproof, TransactionError> {
|
) -> Result<Bulletproof, BulletproofError> {
|
||||||
if outputs.is_empty() {
|
if outputs.is_empty() {
|
||||||
Err(TransactionError::NoOutputs)?;
|
Err(BulletproofError::NoCommitments)?;
|
||||||
}
|
}
|
||||||
if outputs.len() > MAX_OUTPUTS {
|
if outputs.len() > MAX_COMMITMENTS {
|
||||||
Err(TransactionError::TooManyOutputs)?;
|
Err(BulletproofError::TooManyCommitments)?;
|
||||||
}
|
}
|
||||||
Ok(Bulletproof::Original(OriginalStruct::prove(rng, outputs)))
|
Ok(Bulletproof::Original(OriginalStruct::prove(rng, outputs)))
|
||||||
}
|
}
|
||||||
@@ -90,12 +107,12 @@ impl Bulletproof {
|
|||||||
pub fn prove_plus<R: RngCore + CryptoRng>(
|
pub fn prove_plus<R: RngCore + CryptoRng>(
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
outputs: Vec<Commitment>,
|
outputs: Vec<Commitment>,
|
||||||
) -> Result<Bulletproof, TransactionError> {
|
) -> Result<Bulletproof, BulletproofError> {
|
||||||
if outputs.is_empty() {
|
if outputs.is_empty() {
|
||||||
Err(TransactionError::NoOutputs)?;
|
Err(BulletproofError::NoCommitments)?;
|
||||||
}
|
}
|
||||||
if outputs.len() > MAX_OUTPUTS {
|
if outputs.len() > MAX_COMMITMENTS {
|
||||||
Err(TransactionError::TooManyOutputs)?;
|
Err(BulletproofError::TooManyCommitments)?;
|
||||||
}
|
}
|
||||||
Ok(Bulletproof::Plus(
|
Ok(Bulletproof::Plus(
|
||||||
AggregateRangeStatement::new(outputs.iter().map(Commitment::calculate).collect())
|
AggregateRangeStatement::new(outputs.iter().map(Commitment::calculate).collect())
|
||||||
@@ -111,14 +128,14 @@ impl Bulletproof {
|
|||||||
match self {
|
match self {
|
||||||
Bulletproof::Original(bp) => bp.verify(rng, commitments),
|
Bulletproof::Original(bp) => bp.verify(rng, commitments),
|
||||||
Bulletproof::Plus(bp) => {
|
Bulletproof::Plus(bp) => {
|
||||||
let mut verifier = BatchVerifier::new(1);
|
let mut verifier = BulletproofsPlusBatchVerifier(InternalBatchVerifier::new());
|
||||||
let Some(statement) = AggregateRangeStatement::new(commitments.to_vec()) else {
|
let Some(statement) = AggregateRangeStatement::new(commitments.to_vec()) else {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
if !statement.verify(rng, &mut verifier, (), bp.clone()) {
|
if !statement.verify(rng, &mut verifier, bp.clone()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
verifier.verify_vartime()
|
verifier.verify()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -129,20 +146,19 @@ impl Bulletproof {
|
|||||||
/// state.
|
/// state.
|
||||||
/// Returns true if the Bulletproof is sane, regardless of their validity.
|
/// Returns true if the Bulletproof is sane, regardless of their validity.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn batch_verify<ID: Copy + Zeroize, R: RngCore + CryptoRng>(
|
pub fn batch_verify<R: RngCore + CryptoRng>(
|
||||||
&self,
|
&self,
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
verifier: &mut BatchVerifier<ID, dalek_ff_group::EdwardsPoint>,
|
verifier: &mut BatchVerifier,
|
||||||
id: ID,
|
|
||||||
commitments: &[EdwardsPoint],
|
commitments: &[EdwardsPoint],
|
||||||
) -> bool {
|
) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Bulletproof::Original(bp) => bp.batch_verify(rng, verifier, id, commitments),
|
Bulletproof::Original(bp) => bp.batch_verify(rng, &mut verifier.original, commitments),
|
||||||
Bulletproof::Plus(bp) => {
|
Bulletproof::Plus(bp) => {
|
||||||
let Some(statement) = AggregateRangeStatement::new(commitments.to_vec()) else {
|
let Some(statement) = AggregateRangeStatement::new(commitments.to_vec()) else {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
statement.verify(rng, verifier, id, bp.clone())
|
statement.verify(rng, &mut verifier.plus, bp.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -158,7 +174,7 @@ impl Bulletproof {
|
|||||||
write_point(&bp.S, w)?;
|
write_point(&bp.S, w)?;
|
||||||
write_point(&bp.T1, w)?;
|
write_point(&bp.T1, w)?;
|
||||||
write_point(&bp.T2, w)?;
|
write_point(&bp.T2, w)?;
|
||||||
write_scalar(&bp.taux, w)?;
|
write_scalar(&bp.tau_x, w)?;
|
||||||
write_scalar(&bp.mu, w)?;
|
write_scalar(&bp.mu, w)?;
|
||||||
specific_write_vec(&bp.L, w)?;
|
specific_write_vec(&bp.L, w)?;
|
||||||
specific_write_vec(&bp.R, w)?;
|
specific_write_vec(&bp.R, w)?;
|
||||||
@@ -168,19 +184,19 @@ impl Bulletproof {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Bulletproof::Plus(bp) => {
|
Bulletproof::Plus(bp) => {
|
||||||
write_point(&bp.A.0, w)?;
|
write_point(&bp.A, w)?;
|
||||||
write_point(&bp.wip.A.0, w)?;
|
write_point(&bp.wip.A, w)?;
|
||||||
write_point(&bp.wip.B.0, w)?;
|
write_point(&bp.wip.B, w)?;
|
||||||
write_scalar(&bp.wip.r_answer.0, w)?;
|
write_scalar(&bp.wip.r_answer, w)?;
|
||||||
write_scalar(&bp.wip.s_answer.0, w)?;
|
write_scalar(&bp.wip.s_answer, w)?;
|
||||||
write_scalar(&bp.wip.delta_answer.0, w)?;
|
write_scalar(&bp.wip.delta_answer, w)?;
|
||||||
specific_write_vec(&bp.wip.L.iter().copied().map(|L| L.0).collect::<Vec<_>>(), w)?;
|
specific_write_vec(&bp.wip.L, w)?;
|
||||||
specific_write_vec(&bp.wip.R.iter().copied().map(|R| R.0).collect::<Vec<_>>(), w)
|
specific_write_vec(&bp.wip.R, w)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn signature_write<W: Write>(&self, w: &mut W) -> io::Result<()> {
|
pub fn signature_write<W: Write>(&self, w: &mut W) -> io::Result<()> {
|
||||||
self.write_core(w, |points, w| write_raw_vec(write_point, points, w))
|
self.write_core(w, |points, w| write_raw_vec(write_point, points, w))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,7 +219,7 @@ impl Bulletproof {
|
|||||||
S: read_point(r)?,
|
S: read_point(r)?,
|
||||||
T1: read_point(r)?,
|
T1: read_point(r)?,
|
||||||
T2: read_point(r)?,
|
T2: read_point(r)?,
|
||||||
taux: read_scalar(r)?,
|
tau_x: read_scalar(r)?,
|
||||||
mu: read_scalar(r)?,
|
mu: read_scalar(r)?,
|
||||||
L: read_vec(read_point, r)?,
|
L: read_vec(read_point, r)?,
|
||||||
R: read_vec(read_point, r)?,
|
R: read_vec(read_point, r)?,
|
||||||
@@ -215,18 +231,16 @@ impl Bulletproof {
|
|||||||
|
|
||||||
/// Read a Bulletproof+.
|
/// Read a Bulletproof+.
|
||||||
pub fn read_plus<R: Read>(r: &mut R) -> io::Result<Bulletproof> {
|
pub fn read_plus<R: Read>(r: &mut R) -> io::Result<Bulletproof> {
|
||||||
use dalek_ff_group::{Scalar as DfgScalar, EdwardsPoint as DfgPoint};
|
|
||||||
|
|
||||||
Ok(Bulletproof::Plus(AggregateRangeProof {
|
Ok(Bulletproof::Plus(AggregateRangeProof {
|
||||||
A: DfgPoint(read_point(r)?),
|
A: read_point(r)?,
|
||||||
wip: WipProof {
|
wip: WipProof {
|
||||||
A: DfgPoint(read_point(r)?),
|
A: read_point(r)?,
|
||||||
B: DfgPoint(read_point(r)?),
|
B: read_point(r)?,
|
||||||
r_answer: DfgScalar(read_scalar(r)?),
|
r_answer: read_scalar(r)?,
|
||||||
s_answer: DfgScalar(read_scalar(r)?),
|
s_answer: read_scalar(r)?,
|
||||||
delta_answer: DfgScalar(read_scalar(r)?),
|
delta_answer: read_scalar(r)?,
|
||||||
L: read_vec(read_point, r)?.into_iter().map(DfgPoint).collect(),
|
L: read_vec(read_point, r)?.into_iter().collect(),
|
||||||
R: read_vec(read_point, r)?.into_iter().map(DfgPoint).collect(),
|
R: read_vec(read_point, r)?.into_iter().collect(),
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
@@ -1,17 +1,17 @@
|
|||||||
use std_shims::{vec::Vec, sync::OnceLock};
|
use std_shims::{vec::Vec, sync::OnceLock};
|
||||||
|
|
||||||
use rand_core::{RngCore, CryptoRng};
|
use rand_core::{RngCore, CryptoRng};
|
||||||
|
|
||||||
use zeroize::Zeroize;
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
use curve25519_dalek::{scalar::Scalar as DalekScalar, edwards::EdwardsPoint as DalekPoint};
|
use curve25519_dalek::{constants::ED25519_BASEPOINT_POINT, scalar::Scalar, edwards::EdwardsPoint};
|
||||||
|
|
||||||
use group::{ff::Field, Group};
|
use monero_generators::H;
|
||||||
use dalek_ff_group::{ED25519_BASEPOINT_POINT as G, Scalar, EdwardsPoint};
|
use monero_primitives::{INV_EIGHT, Commitment, keccak256_to_scalar};
|
||||||
|
|
||||||
use multiexp::{BatchVerifier, multiexp};
|
use crate::{
|
||||||
|
core::*,
|
||||||
use crate::{Commitment, ringct::bulletproofs::core::*};
|
batch_verifier::{InternalBatchVerifier, BulletproofsBatchVerifier},
|
||||||
|
};
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/generators.rs"));
|
include!(concat!(env!("OUT_DIR"), "/generators.rs"));
|
||||||
|
|
||||||
@@ -36,17 +36,17 @@ pub(crate) fn hadamard_fold(
|
|||||||
/// Internal structure representing a Bulletproof.
|
/// Internal structure representing a Bulletproof.
|
||||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
pub struct OriginalStruct {
|
pub struct OriginalStruct {
|
||||||
pub(crate) A: DalekPoint,
|
pub(crate) A: EdwardsPoint,
|
||||||
pub(crate) S: DalekPoint,
|
pub(crate) S: EdwardsPoint,
|
||||||
pub(crate) T1: DalekPoint,
|
pub(crate) T1: EdwardsPoint,
|
||||||
pub(crate) T2: DalekPoint,
|
pub(crate) T2: EdwardsPoint,
|
||||||
pub(crate) taux: DalekScalar,
|
pub(crate) tau_x: Scalar,
|
||||||
pub(crate) mu: DalekScalar,
|
pub(crate) mu: Scalar,
|
||||||
pub(crate) L: Vec<DalekPoint>,
|
pub(crate) L: Vec<EdwardsPoint>,
|
||||||
pub(crate) R: Vec<DalekPoint>,
|
pub(crate) R: Vec<EdwardsPoint>,
|
||||||
pub(crate) a: DalekScalar,
|
pub(crate) a: Scalar,
|
||||||
pub(crate) b: DalekScalar,
|
pub(crate) b: Scalar,
|
||||||
pub(crate) t: DalekScalar,
|
pub(crate) t: Scalar,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OriginalStruct {
|
impl OriginalStruct {
|
||||||
@@ -68,7 +68,7 @@ impl OriginalStruct {
|
|||||||
let (mut rho, S) = alpha_rho(&mut *rng, generators, &sL, &sR);
|
let (mut rho, S) = alpha_rho(&mut *rng, generators, &sL, &sR);
|
||||||
|
|
||||||
let y = hash_cache(&mut cache, &[A.compress().to_bytes(), S.compress().to_bytes()]);
|
let y = hash_cache(&mut cache, &[A.compress().to_bytes(), S.compress().to_bytes()]);
|
||||||
let mut cache = hash_to_scalar(&y.to_bytes());
|
let mut cache = keccak256_to_scalar(y.to_bytes());
|
||||||
let z = cache;
|
let z = cache;
|
||||||
|
|
||||||
let l0 = aL - z;
|
let l0 = aL - z;
|
||||||
@@ -86,32 +86,32 @@ impl OriginalStruct {
|
|||||||
let r0 = ((aR + z) * &yMN) + &ScalarVector(zero_twos);
|
let r0 = ((aR + z) * &yMN) + &ScalarVector(zero_twos);
|
||||||
let r1 = yMN * &sR;
|
let r1 = yMN * &sR;
|
||||||
|
|
||||||
let (T1, T2, x, mut taux) = {
|
let (T1, T2, x, mut tau_x) = {
|
||||||
let t1 = l0.clone().inner_product(&r1) + r0.clone().inner_product(&l1);
|
let t1 = l0.clone().inner_product(&r1) + r0.clone().inner_product(&l1);
|
||||||
let t2 = l1.clone().inner_product(&r1);
|
let t2 = l1.clone().inner_product(&r1);
|
||||||
|
|
||||||
let mut tau1 = Scalar::random(&mut *rng);
|
let mut tau1 = Scalar::random(&mut *rng);
|
||||||
let mut tau2 = Scalar::random(&mut *rng);
|
let mut tau2 = Scalar::random(&mut *rng);
|
||||||
|
|
||||||
let T1 = prove_multiexp(&[(t1, H()), (tau1, EdwardsPoint::generator())]);
|
let T1 = multiexp(&[(t1, H()), (tau1, ED25519_BASEPOINT_POINT)]) * INV_EIGHT();
|
||||||
let T2 = prove_multiexp(&[(t2, H()), (tau2, EdwardsPoint::generator())]);
|
let T2 = multiexp(&[(t2, H()), (tau2, ED25519_BASEPOINT_POINT)]) * INV_EIGHT();
|
||||||
|
|
||||||
let x =
|
let x =
|
||||||
hash_cache(&mut cache, &[z.to_bytes(), T1.compress().to_bytes(), T2.compress().to_bytes()]);
|
hash_cache(&mut cache, &[z.to_bytes(), T1.compress().to_bytes(), T2.compress().to_bytes()]);
|
||||||
|
|
||||||
let taux = (tau2 * (x * x)) + (tau1 * x);
|
let tau_x = (tau2 * (x * x)) + (tau1 * x);
|
||||||
|
|
||||||
tau1.zeroize();
|
tau1.zeroize();
|
||||||
tau2.zeroize();
|
tau2.zeroize();
|
||||||
(T1, T2, x, taux)
|
(T1, T2, x, tau_x)
|
||||||
};
|
};
|
||||||
|
|
||||||
let mu = (x * rho) + alpha;
|
let mu = (x * rho) + alpha;
|
||||||
alpha.zeroize();
|
alpha.zeroize();
|
||||||
rho.zeroize();
|
rho.zeroize();
|
||||||
|
|
||||||
for (i, gamma) in commitments.iter().map(|c| Scalar(c.mask)).enumerate() {
|
for (i, gamma) in commitments.iter().map(|c| c.mask).enumerate() {
|
||||||
taux += zpow[i + 2] * gamma;
|
tau_x += zpow[i + 2] * gamma;
|
||||||
}
|
}
|
||||||
|
|
||||||
let l = l0 + &(l1 * x);
|
let l = l0 + &(l1 * x);
|
||||||
@@ -120,12 +120,12 @@ impl OriginalStruct {
|
|||||||
let t = l.clone().inner_product(&r);
|
let t = l.clone().inner_product(&r);
|
||||||
|
|
||||||
let x_ip =
|
let x_ip =
|
||||||
hash_cache(&mut cache, &[x.to_bytes(), taux.to_bytes(), mu.to_bytes(), t.to_bytes()]);
|
hash_cache(&mut cache, &[x.to_bytes(), tau_x.to_bytes(), mu.to_bytes(), t.to_bytes()]);
|
||||||
|
|
||||||
let mut a = l;
|
let mut a = l;
|
||||||
let mut b = r;
|
let mut b = r;
|
||||||
|
|
||||||
let yinv = y.invert().unwrap();
|
let yinv = y.invert();
|
||||||
let yinvpow = ScalarVector::powers(yinv, MN);
|
let yinvpow = ScalarVector::powers(yinv, MN);
|
||||||
|
|
||||||
let mut G_proof = generators.G[.. a.len()].to_vec();
|
let mut G_proof = generators.G[.. a.len()].to_vec();
|
||||||
@@ -146,13 +146,13 @@ impl OriginalStruct {
|
|||||||
let (G_L, G_R) = G_proof.split_at(aL.len());
|
let (G_L, G_R) = G_proof.split_at(aL.len());
|
||||||
let (H_L, H_R) = H_proof.split_at(aL.len());
|
let (H_L, H_R) = H_proof.split_at(aL.len());
|
||||||
|
|
||||||
let L_i = prove_multiexp(&LR_statements(&aL, G_R, &bR, H_L, cL, U));
|
let L_i = multiexp(&LR_statements(&aL, G_R, &bR, H_L, cL, U)) * INV_EIGHT();
|
||||||
let R_i = prove_multiexp(&LR_statements(&aR, G_L, &bL, H_R, cR, U));
|
let R_i = multiexp(&LR_statements(&aR, G_L, &bL, H_R, cR, U)) * INV_EIGHT();
|
||||||
L.push(L_i);
|
L.push(L_i);
|
||||||
R.push(R_i);
|
R.push(R_i);
|
||||||
|
|
||||||
let w = hash_cache(&mut cache, &[L_i.compress().to_bytes(), R_i.compress().to_bytes()]);
|
let w = hash_cache(&mut cache, &[L_i.compress().to_bytes(), R_i.compress().to_bytes()]);
|
||||||
let winv = w.invert().unwrap();
|
let winv = w.invert();
|
||||||
|
|
||||||
a = (aL * w) + &(aR * winv);
|
a = (aL * w) + &(aR * winv);
|
||||||
b = (bL * winv) + &(bR * w);
|
b = (bL * winv) + &(bR * w);
|
||||||
@@ -163,30 +163,17 @@ impl OriginalStruct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let res = OriginalStruct {
|
let res = OriginalStruct { A, S, T1, T2, tau_x, mu, L, R, a: a[0], b: b[0], t };
|
||||||
A: *A,
|
|
||||||
S: *S,
|
|
||||||
T1: *T1,
|
|
||||||
T2: *T2,
|
|
||||||
taux: *taux,
|
|
||||||
mu: *mu,
|
|
||||||
L: L.drain(..).map(|L| *L).collect(),
|
|
||||||
R: R.drain(..).map(|R| *R).collect(),
|
|
||||||
a: *a[0],
|
|
||||||
b: *b[0],
|
|
||||||
t: *t,
|
|
||||||
};
|
|
||||||
debug_assert!(res.verify(rng, &commitments_points));
|
debug_assert!(res.verify(rng, &commitments_points));
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn verify_core<ID: Copy + Zeroize, R: RngCore + CryptoRng>(
|
fn verify_core<R: RngCore + CryptoRng>(
|
||||||
&self,
|
&self,
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
verifier: &mut BatchVerifier<ID, EdwardsPoint>,
|
verifier: &mut BulletproofsBatchVerifier,
|
||||||
id: ID,
|
commitments: &[EdwardsPoint],
|
||||||
commitments: &[DalekPoint],
|
|
||||||
) -> bool {
|
) -> bool {
|
||||||
// Verify commitments are valid
|
// Verify commitments are valid
|
||||||
if commitments.is_empty() || (commitments.len() > MAX_M) {
|
if commitments.is_empty() || (commitments.len() > MAX_M) {
|
||||||
@@ -207,7 +194,7 @@ impl OriginalStruct {
|
|||||||
let (mut cache, commitments) = hash_commitments(commitments.iter().copied());
|
let (mut cache, commitments) = hash_commitments(commitments.iter().copied());
|
||||||
let y = hash_cache(&mut cache, &[self.A.compress().to_bytes(), self.S.compress().to_bytes()]);
|
let y = hash_cache(&mut cache, &[self.A.compress().to_bytes(), self.S.compress().to_bytes()]);
|
||||||
|
|
||||||
let z = hash_to_scalar(&y.to_bytes());
|
let z = keccak256_to_scalar(y.to_bytes());
|
||||||
cache = z;
|
cache = z;
|
||||||
|
|
||||||
let x = hash_cache(
|
let x = hash_cache(
|
||||||
@@ -217,18 +204,18 @@ impl OriginalStruct {
|
|||||||
|
|
||||||
let x_ip = hash_cache(
|
let x_ip = hash_cache(
|
||||||
&mut cache,
|
&mut cache,
|
||||||
&[x.to_bytes(), self.taux.to_bytes(), self.mu.to_bytes(), self.t.to_bytes()],
|
&[x.to_bytes(), self.tau_x.to_bytes(), self.mu.to_bytes(), self.t.to_bytes()],
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut w = Vec::with_capacity(logMN);
|
let mut w = Vec::with_capacity(logMN);
|
||||||
let mut winv = Vec::with_capacity(logMN);
|
let mut winv = Vec::with_capacity(logMN);
|
||||||
for (L, R) in self.L.iter().zip(&self.R) {
|
for (L, R) in self.L.iter().zip(&self.R) {
|
||||||
w.push(hash_cache(&mut cache, &[L.compress().to_bytes(), R.compress().to_bytes()]));
|
w.push(hash_cache(&mut cache, &[L.compress().to_bytes(), R.compress().to_bytes()]));
|
||||||
winv.push(cache.invert().unwrap());
|
winv.push(cache.invert());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert the proof from * INV_EIGHT to its actual form
|
// Convert the proof from * INV_EIGHT to its actual form
|
||||||
let normalize = |point: &DalekPoint| EdwardsPoint(point.mul_by_cofactor());
|
let normalize = |point: &EdwardsPoint| point.mul_by_cofactor();
|
||||||
|
|
||||||
let L = self.L.iter().map(normalize).collect::<Vec<_>>();
|
let L = self.L.iter().map(normalize).collect::<Vec<_>>();
|
||||||
let R = self.R.iter().map(normalize).collect::<Vec<_>>();
|
let R = self.R.iter().map(normalize).collect::<Vec<_>>();
|
||||||
@@ -240,58 +227,69 @@ impl OriginalStruct {
|
|||||||
let commitments = commitments.iter().map(EdwardsPoint::mul_by_cofactor).collect::<Vec<_>>();
|
let commitments = commitments.iter().map(EdwardsPoint::mul_by_cofactor).collect::<Vec<_>>();
|
||||||
|
|
||||||
// Verify it
|
// Verify it
|
||||||
let mut proof = Vec::with_capacity(4 + commitments.len());
|
|
||||||
|
|
||||||
let zpow = ScalarVector::powers(z, M + 3);
|
let zpow = ScalarVector::powers(z, M + 3);
|
||||||
|
|
||||||
|
// First multiexp
|
||||||
|
{
|
||||||
|
let verifier_weight = Scalar::random(rng);
|
||||||
|
|
||||||
let ip1y = ScalarVector::powers(y, M * N).sum();
|
let ip1y = ScalarVector::powers(y, M * N).sum();
|
||||||
let mut k = -(zpow[2] * ip1y);
|
let mut k = -(zpow[2] * ip1y);
|
||||||
for j in 1 ..= M {
|
for j in 1 ..= M {
|
||||||
k -= zpow[j + 2] * IP12();
|
k -= zpow[j + 2] * IP12();
|
||||||
}
|
}
|
||||||
let y1 = Scalar(self.t) - ((z * ip1y) + k);
|
let y1 = self.t - ((z * ip1y) + k);
|
||||||
proof.push((-y1, H()));
|
verifier.0.h -= verifier_weight * y1;
|
||||||
|
|
||||||
proof.push((-Scalar(self.taux), G));
|
verifier.0.g -= verifier_weight * self.tau_x;
|
||||||
|
|
||||||
for (j, commitment) in commitments.iter().enumerate() {
|
for (j, commitment) in commitments.iter().enumerate() {
|
||||||
proof.push((zpow[j + 2], *commitment));
|
verifier.0.other.push((verifier_weight * zpow[j + 2], *commitment));
|
||||||
}
|
}
|
||||||
|
|
||||||
proof.push((x, T1));
|
verifier.0.other.push((verifier_weight * x, T1));
|
||||||
proof.push((x * x, T2));
|
verifier.0.other.push((verifier_weight * (x * x), T2));
|
||||||
verifier.queue(&mut *rng, id, proof);
|
}
|
||||||
|
|
||||||
proof = Vec::with_capacity(4 + (2 * (MN + logMN)));
|
// Second multiexp
|
||||||
let z3 = (Scalar(self.t) - (Scalar(self.a) * Scalar(self.b))) * x_ip;
|
{
|
||||||
proof.push((z3, H()));
|
let verifier_weight = Scalar::random(rng);
|
||||||
proof.push((-Scalar(self.mu), G));
|
let z3 = (self.t - (self.a * self.b)) * x_ip;
|
||||||
|
verifier.0.h += verifier_weight * z3;
|
||||||
|
verifier.0.g -= verifier_weight * self.mu;
|
||||||
|
|
||||||
proof.push((Scalar::ONE, A));
|
verifier.0.other.push((verifier_weight, A));
|
||||||
proof.push((x, S));
|
verifier.0.other.push((verifier_weight * x, S));
|
||||||
|
|
||||||
{
|
{
|
||||||
let ypow = ScalarVector::powers(y, MN);
|
let ypow = ScalarVector::powers(y, MN);
|
||||||
let yinv = y.invert().unwrap();
|
let yinv = y.invert();
|
||||||
let yinvpow = ScalarVector::powers(yinv, MN);
|
let yinvpow = ScalarVector::powers(yinv, MN);
|
||||||
|
|
||||||
let w_cache = challenge_products(&w, &winv);
|
let w_cache = challenge_products(&w, &winv);
|
||||||
|
|
||||||
let generators = GENERATORS();
|
while verifier.0.g_bold.len() < MN {
|
||||||
for i in 0 .. MN {
|
verifier.0.g_bold.push(Scalar::ZERO);
|
||||||
let g = (Scalar(self.a) * w_cache[i]) + z;
|
}
|
||||||
proof.push((-g, generators.G[i]));
|
while verifier.0.h_bold.len() < MN {
|
||||||
|
verifier.0.h_bold.push(Scalar::ZERO);
|
||||||
|
}
|
||||||
|
|
||||||
let mut h = Scalar(self.b) * yinvpow[i] * w_cache[(!i) & (MN - 1)];
|
for i in 0 .. MN {
|
||||||
|
let g = (self.a * w_cache[i]) + z;
|
||||||
|
verifier.0.g_bold[i] -= verifier_weight * g;
|
||||||
|
|
||||||
|
let mut h = self.b * yinvpow[i] * w_cache[(!i) & (MN - 1)];
|
||||||
h -= ((zpow[(i / N) + 2] * TWO_N()[i % N]) + (z * ypow[i])) * yinvpow[i];
|
h -= ((zpow[(i / N) + 2] * TWO_N()[i % N]) + (z * ypow[i])) * yinvpow[i];
|
||||||
proof.push((-h, generators.H[i]));
|
verifier.0.h_bold[i] -= verifier_weight * h;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for i in 0 .. logMN {
|
for i in 0 .. logMN {
|
||||||
proof.push((w[i] * w[i], L[i]));
|
verifier.0.other.push((verifier_weight * (w[i] * w[i]), L[i]));
|
||||||
proof.push((winv[i] * winv[i], R[i]));
|
verifier.0.other.push((verifier_weight * (winv[i] * winv[i]), R[i]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
verifier.queue(rng, id, proof);
|
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@@ -300,24 +298,23 @@ impl OriginalStruct {
|
|||||||
pub(crate) fn verify<R: RngCore + CryptoRng>(
|
pub(crate) fn verify<R: RngCore + CryptoRng>(
|
||||||
&self,
|
&self,
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
commitments: &[DalekPoint],
|
commitments: &[EdwardsPoint],
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let mut verifier = BatchVerifier::new(1);
|
let mut verifier = BulletproofsBatchVerifier(InternalBatchVerifier::new());
|
||||||
if self.verify_core(rng, &mut verifier, (), commitments) {
|
if self.verify_core(rng, &mut verifier, commitments) {
|
||||||
verifier.verify_vartime()
|
verifier.verify()
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) fn batch_verify<ID: Copy + Zeroize, R: RngCore + CryptoRng>(
|
pub(crate) fn batch_verify<R: RngCore + CryptoRng>(
|
||||||
&self,
|
&self,
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
verifier: &mut BatchVerifier<ID, EdwardsPoint>,
|
verifier: &mut BulletproofsBatchVerifier,
|
||||||
id: ID,
|
commitments: &[EdwardsPoint],
|
||||||
commitments: &[DalekPoint],
|
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.verify_core(rng, verifier, id, commitments)
|
self.verify_core(rng, verifier, commitments)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,35 +1,28 @@
|
|||||||
use std_shims::vec::Vec;
|
use std_shims::vec::Vec;
|
||||||
|
|
||||||
use rand_core::{RngCore, CryptoRng};
|
use rand_core::{RngCore, CryptoRng};
|
||||||
|
|
||||||
use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
|
use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
|
||||||
|
|
||||||
use multiexp::{multiexp, multiexp_vartime, BatchVerifier};
|
use curve25519_dalek::{traits::Identity, scalar::Scalar, edwards::EdwardsPoint};
|
||||||
use group::{
|
|
||||||
ff::{Field, PrimeField},
|
use monero_primitives::{INV_EIGHT, Commitment, keccak256_to_scalar};
|
||||||
Group, GroupEncoding,
|
|
||||||
};
|
|
||||||
use curve25519_dalek::EdwardsPoint as DalekPoint;
|
|
||||||
use dalek_ff_group::{Scalar, EdwardsPoint};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Commitment,
|
batch_verifier::BulletproofsPlusBatchVerifier,
|
||||||
ringct::{
|
core::{MAX_M, N, multiexp, multiexp_vartime},
|
||||||
bulletproofs::core::{MAX_M, N},
|
plus::{
|
||||||
bulletproofs::plus::{
|
ScalarVector, PointVector, GeneratorsList, BpPlusGenerators,
|
||||||
ScalarVector, PointVector, GeneratorsList, Generators,
|
|
||||||
transcript::*,
|
transcript::*,
|
||||||
weighted_inner_product::{WipStatement, WipWitness, WipProof},
|
weighted_inner_product::{WipStatement, WipWitness, WipProof},
|
||||||
padded_pow_of_2, u64_decompose,
|
padded_pow_of_2, u64_decompose,
|
||||||
},
|
},
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Figure 3 of the Bulletproofs+ Paper
|
// Figure 3 of the Bulletproofs+ Paper
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub(crate) struct AggregateRangeStatement {
|
pub(crate) struct AggregateRangeStatement {
|
||||||
generators: Generators,
|
generators: BpPlusGenerators,
|
||||||
V: Vec<DalekPoint>,
|
V: Vec<EdwardsPoint>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Zeroize for AggregateRangeStatement {
|
impl Zeroize for AggregateRangeStatement {
|
||||||
@@ -58,18 +51,29 @@ pub struct AggregateRangeProof {
|
|||||||
pub(crate) wip: WipProof,
|
pub(crate) wip: WipProof,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct AHatComputation {
|
||||||
|
y: Scalar,
|
||||||
|
d_descending_y_plus_z: ScalarVector,
|
||||||
|
y_mn_plus_one: Scalar,
|
||||||
|
z: Scalar,
|
||||||
|
z_pow: ScalarVector,
|
||||||
|
A_hat: EdwardsPoint,
|
||||||
|
}
|
||||||
|
|
||||||
impl AggregateRangeStatement {
|
impl AggregateRangeStatement {
|
||||||
pub(crate) fn new(V: Vec<DalekPoint>) -> Option<Self> {
|
pub(crate) fn new(V: Vec<EdwardsPoint>) -> Option<Self> {
|
||||||
if V.is_empty() || (V.len() > MAX_M) {
|
if V.is_empty() || (V.len() > MAX_M) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(Self { generators: Generators::new(), V })
|
Some(Self { generators: BpPlusGenerators::new(), V })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transcript_A(transcript: &mut Scalar, A: EdwardsPoint) -> (Scalar, Scalar) {
|
fn transcript_A(transcript: &mut Scalar, A: EdwardsPoint) -> (Scalar, Scalar) {
|
||||||
let y = hash_to_scalar(&[transcript.to_repr().as_ref(), A.to_bytes().as_ref()].concat());
|
let y = keccak256_to_scalar(
|
||||||
let z = hash_to_scalar(y.to_bytes().as_ref());
|
[transcript.to_bytes().as_ref(), A.compress().to_bytes().as_ref()].concat(),
|
||||||
|
);
|
||||||
|
let z = keccak256_to_scalar(y.to_bytes().as_ref());
|
||||||
*transcript = z;
|
*transcript = z;
|
||||||
(y, z)
|
(y, z)
|
||||||
}
|
}
|
||||||
@@ -88,10 +92,10 @@ impl AggregateRangeStatement {
|
|||||||
|
|
||||||
fn compute_A_hat(
|
fn compute_A_hat(
|
||||||
mut V: PointVector,
|
mut V: PointVector,
|
||||||
generators: &Generators,
|
generators: &BpPlusGenerators,
|
||||||
transcript: &mut Scalar,
|
transcript: &mut Scalar,
|
||||||
mut A: EdwardsPoint,
|
mut A: EdwardsPoint,
|
||||||
) -> (Scalar, ScalarVector, Scalar, Scalar, ScalarVector, EdwardsPoint) {
|
) -> AHatComputation {
|
||||||
let (y, z) = Self::transcript_A(transcript, A);
|
let (y, z) = Self::transcript_A(transcript, A);
|
||||||
A = A.mul_by_cofactor();
|
A = A.mul_by_cofactor();
|
||||||
|
|
||||||
@@ -133,23 +137,23 @@ impl AggregateRangeStatement {
|
|||||||
let neg_z = -z;
|
let neg_z = -z;
|
||||||
let mut A_terms = Vec::with_capacity((generators.len() * 2) + 2);
|
let mut A_terms = Vec::with_capacity((generators.len() * 2) + 2);
|
||||||
for (i, d_y_z) in d_descending_y_plus_z.0.iter().enumerate() {
|
for (i, d_y_z) in d_descending_y_plus_z.0.iter().enumerate() {
|
||||||
A_terms.push((neg_z, generators.generator(GeneratorsList::GBold1, i)));
|
A_terms.push((neg_z, generators.generator(GeneratorsList::GBold, i)));
|
||||||
A_terms.push((*d_y_z, generators.generator(GeneratorsList::HBold1, i)));
|
A_terms.push((*d_y_z, generators.generator(GeneratorsList::HBold, i)));
|
||||||
}
|
}
|
||||||
A_terms.push((y_mn_plus_one, commitment_accum));
|
A_terms.push((y_mn_plus_one, commitment_accum));
|
||||||
A_terms.push((
|
A_terms.push((
|
||||||
((y_pows * z) - (d.sum() * y_mn_plus_one * z) - (y_pows * z.square())),
|
((y_pows * z) - (d.sum() * y_mn_plus_one * z) - (y_pows * (z * z))),
|
||||||
Generators::g(),
|
BpPlusGenerators::g(),
|
||||||
));
|
));
|
||||||
|
|
||||||
(
|
AHatComputation {
|
||||||
y,
|
y,
|
||||||
d_descending_y_plus_z,
|
d_descending_y_plus_z,
|
||||||
y_mn_plus_one,
|
y_mn_plus_one,
|
||||||
z,
|
z,
|
||||||
ScalarVector(z_pow),
|
z_pow: ScalarVector(z_pow),
|
||||||
A + multiexp_vartime(&A_terms),
|
A_hat: A + multiexp_vartime(&A_terms),
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn prove<R: RngCore + CryptoRng>(
|
pub(crate) fn prove<R: RngCore + CryptoRng>(
|
||||||
@@ -175,9 +179,9 @@ impl AggregateRangeStatement {
|
|||||||
// Commitments aren't transmitted INV_EIGHT though, so this multiplies by INV_EIGHT to enable
|
// Commitments aren't transmitted INV_EIGHT though, so this multiplies by INV_EIGHT to enable
|
||||||
// clearing its cofactor without mutating the value
|
// clearing its cofactor without mutating the value
|
||||||
// For some reason, these values are transcripted * INV_EIGHT, not as transmitted
|
// For some reason, these values are transcripted * INV_EIGHT, not as transmitted
|
||||||
let V = V.into_iter().map(|V| V * crate::INV_EIGHT()).collect::<Vec<_>>();
|
let V = V.into_iter().map(|V| V * INV_EIGHT()).collect::<Vec<_>>();
|
||||||
let mut transcript = initial_transcript(V.iter());
|
let mut transcript = initial_transcript(V.iter());
|
||||||
let mut V = V.into_iter().map(|V| EdwardsPoint(V.mul_by_cofactor())).collect::<Vec<_>>();
|
let mut V = V.iter().map(EdwardsPoint::mul_by_cofactor).collect::<Vec<_>>();
|
||||||
|
|
||||||
// Pad V
|
// Pad V
|
||||||
while V.len() < padded_pow_of_2(V.len()) {
|
while V.len() < padded_pow_of_2(V.len()) {
|
||||||
@@ -205,26 +209,26 @@ impl AggregateRangeStatement {
|
|||||||
|
|
||||||
let mut A_terms = Vec::with_capacity((generators.len() * 2) + 1);
|
let mut A_terms = Vec::with_capacity((generators.len() * 2) + 1);
|
||||||
for (i, a_l) in a_l.0.iter().enumerate() {
|
for (i, a_l) in a_l.0.iter().enumerate() {
|
||||||
A_terms.push((*a_l, generators.generator(GeneratorsList::GBold1, i)));
|
A_terms.push((*a_l, generators.generator(GeneratorsList::GBold, i)));
|
||||||
}
|
}
|
||||||
for (i, a_r) in a_r.0.iter().enumerate() {
|
for (i, a_r) in a_r.0.iter().enumerate() {
|
||||||
A_terms.push((*a_r, generators.generator(GeneratorsList::HBold1, i)));
|
A_terms.push((*a_r, generators.generator(GeneratorsList::HBold, i)));
|
||||||
}
|
}
|
||||||
A_terms.push((alpha, Generators::h()));
|
A_terms.push((alpha, BpPlusGenerators::h()));
|
||||||
let mut A = multiexp(&A_terms);
|
let mut A = multiexp(&A_terms);
|
||||||
A_terms.zeroize();
|
A_terms.zeroize();
|
||||||
|
|
||||||
// Multiply by INV_EIGHT per earlier commentary
|
// Multiply by INV_EIGHT per earlier commentary
|
||||||
A.0 *= crate::INV_EIGHT();
|
A *= INV_EIGHT();
|
||||||
|
|
||||||
let (y, d_descending_y_plus_z, y_mn_plus_one, z, z_pow, A_hat) =
|
let AHatComputation { y, d_descending_y_plus_z, y_mn_plus_one, z, z_pow, A_hat } =
|
||||||
Self::compute_A_hat(PointVector(V), &generators, &mut transcript, A);
|
Self::compute_A_hat(PointVector(V), &generators, &mut transcript, A);
|
||||||
|
|
||||||
let a_l = a_l - z;
|
let a_l = a_l - z;
|
||||||
let a_r = a_r + &d_descending_y_plus_z;
|
let a_r = a_r + &d_descending_y_plus_z;
|
||||||
let mut alpha = alpha;
|
let mut alpha = alpha;
|
||||||
for j in 1 ..= witness.0.len() {
|
for j in 1 ..= witness.0.len() {
|
||||||
alpha += z_pow[j - 1] * Scalar(witness.0[j - 1].mask) * y_mn_plus_one;
|
alpha += z_pow[j - 1] * witness.0[j - 1].mask * y_mn_plus_one;
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(AggregateRangeProof {
|
Some(AggregateRangeProof {
|
||||||
@@ -235,25 +239,22 @@ impl AggregateRangeStatement {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn verify<Id: Copy + Zeroize, R: RngCore + CryptoRng>(
|
pub(crate) fn verify<R: RngCore + CryptoRng>(
|
||||||
self,
|
self,
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
verifier: &mut BatchVerifier<Id, EdwardsPoint>,
|
verifier: &mut BulletproofsPlusBatchVerifier,
|
||||||
id: Id,
|
|
||||||
proof: AggregateRangeProof,
|
proof: AggregateRangeProof,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let Self { generators, V } = self;
|
let Self { generators, V } = self;
|
||||||
|
|
||||||
let V = V.into_iter().map(|V| V * crate::INV_EIGHT()).collect::<Vec<_>>();
|
let V = V.into_iter().map(|V| V * INV_EIGHT()).collect::<Vec<_>>();
|
||||||
let mut transcript = initial_transcript(V.iter());
|
let mut transcript = initial_transcript(V.iter());
|
||||||
// With the torsion clear, wrap it into a EdwardsPoint from dalek-ff-group
|
let V = V.iter().map(EdwardsPoint::mul_by_cofactor).collect::<Vec<_>>();
|
||||||
// (which is prime-order)
|
|
||||||
let V = V.into_iter().map(|V| EdwardsPoint(V.mul_by_cofactor())).collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let generators = generators.reduce(V.len() * N);
|
let generators = generators.reduce(V.len() * N);
|
||||||
|
|
||||||
let (y, _, _, _, _, A_hat) =
|
let AHatComputation { y, A_hat, .. } =
|
||||||
Self::compute_A_hat(PointVector(V), &generators, &mut transcript, proof.A);
|
Self::compute_A_hat(PointVector(V), &generators, &mut transcript, proof.A);
|
||||||
WipStatement::new(generators, A_hat, y).verify(rng, verifier, id, transcript, proof.wip)
|
WipStatement::new(generators, A_hat, y).verify(rng, verifier, transcript, proof.wip)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,13 @@
|
|||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
use group::Group;
|
use std_shims::sync::OnceLock;
|
||||||
use dalek_ff_group::{Scalar, EdwardsPoint};
|
|
||||||
|
use curve25519_dalek::{constants::ED25519_BASEPOINT_POINT, scalar::Scalar, edwards::EdwardsPoint};
|
||||||
|
|
||||||
|
use monero_generators::{H, Generators};
|
||||||
|
|
||||||
|
pub(crate) use crate::scalar_vector::ScalarVector;
|
||||||
|
|
||||||
pub(crate) use crate::ringct::bulletproofs::scalar_vector::ScalarVector;
|
|
||||||
mod point_vector;
|
mod point_vector;
|
||||||
pub(crate) use point_vector::PointVector;
|
pub(crate) use point_vector::PointVector;
|
||||||
|
|
||||||
@@ -23,55 +27,51 @@ pub(crate) fn padded_pow_of_2(i: usize) -> usize {
|
|||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
||||||
pub(crate) enum GeneratorsList {
|
pub(crate) enum GeneratorsList {
|
||||||
GBold1,
|
GBold,
|
||||||
HBold1,
|
HBold,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Table these
|
// TODO: Table these
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub(crate) struct Generators {
|
pub(crate) struct BpPlusGenerators {
|
||||||
g_bold1: &'static [EdwardsPoint],
|
g_bold: &'static [EdwardsPoint],
|
||||||
h_bold1: &'static [EdwardsPoint],
|
h_bold: &'static [EdwardsPoint],
|
||||||
}
|
}
|
||||||
|
|
||||||
mod generators {
|
|
||||||
use std_shims::sync::OnceLock;
|
|
||||||
use monero_generators::Generators;
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/generators_plus.rs"));
|
include!(concat!(env!("OUT_DIR"), "/generators_plus.rs"));
|
||||||
}
|
|
||||||
|
|
||||||
impl Generators {
|
impl BpPlusGenerators {
|
||||||
#[allow(clippy::new_without_default)]
|
#[allow(clippy::new_without_default)]
|
||||||
pub(crate) fn new() -> Self {
|
pub(crate) fn new() -> Self {
|
||||||
let gens = generators::GENERATORS();
|
let gens = GENERATORS();
|
||||||
Generators { g_bold1: &gens.G, h_bold1: &gens.H }
|
BpPlusGenerators { g_bold: &gens.G, h_bold: &gens.H }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn len(&self) -> usize {
|
pub(crate) fn len(&self) -> usize {
|
||||||
self.g_bold1.len()
|
self.g_bold.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn g() -> EdwardsPoint {
|
pub(crate) fn g() -> EdwardsPoint {
|
||||||
dalek_ff_group::EdwardsPoint(crate::H())
|
H()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn h() -> EdwardsPoint {
|
pub(crate) fn h() -> EdwardsPoint {
|
||||||
EdwardsPoint::generator()
|
ED25519_BASEPOINT_POINT
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn generator(&self, list: GeneratorsList, i: usize) -> EdwardsPoint {
|
pub(crate) fn generator(&self, list: GeneratorsList, i: usize) -> EdwardsPoint {
|
||||||
match list {
|
match list {
|
||||||
GeneratorsList::GBold1 => self.g_bold1[i],
|
GeneratorsList::GBold => self.g_bold[i],
|
||||||
GeneratorsList::HBold1 => self.h_bold1[i],
|
GeneratorsList::HBold => self.h_bold[i],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn reduce(&self, generators: usize) -> Self {
|
pub(crate) fn reduce(&self, generators: usize) -> Self {
|
||||||
// Round to the nearest power of 2
|
// Round to the nearest power of 2
|
||||||
let generators = padded_pow_of_2(generators);
|
let generators = padded_pow_of_2(generators);
|
||||||
assert!(generators <= self.g_bold1.len());
|
assert!(generators <= self.g_bold.len());
|
||||||
|
|
||||||
Generators { g_bold1: &self.g_bold1[.. generators], h_bold1: &self.h_bold1[.. generators] }
|
BpPlusGenerators { g_bold: &self.g_bold[.. generators], h_bold: &self.h_bold[.. generators] }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3,12 +3,10 @@ use std_shims::vec::Vec;
|
|||||||
|
|
||||||
use zeroize::{Zeroize, ZeroizeOnDrop};
|
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||||
|
|
||||||
use dalek_ff_group::EdwardsPoint;
|
use curve25519_dalek::edwards::EdwardsPoint;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use multiexp::multiexp;
|
use crate::{core::multiexp, plus::ScalarVector};
|
||||||
#[cfg(test)]
|
|
||||||
use crate::ringct::bulletproofs::plus::ScalarVector;
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, Zeroize, ZeroizeOnDrop)]
|
#[derive(Clone, PartialEq, Eq, Debug, Zeroize, ZeroizeOnDrop)]
|
||||||
pub(crate) struct PointVector(pub(crate) Vec<EdwardsPoint>);
|
pub(crate) struct PointVector(pub(crate) Vec<EdwardsPoint>);
|
||||||
20
coins/monero/ringct/bulletproofs/src/plus/transcript.rs
Normal file
20
coins/monero/ringct/bulletproofs/src/plus/transcript.rs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
use std_shims::{sync::OnceLock, vec::Vec};
|
||||||
|
|
||||||
|
use curve25519_dalek::{scalar::Scalar, edwards::EdwardsPoint};
|
||||||
|
|
||||||
|
use monero_generators::hash_to_point;
|
||||||
|
use monero_primitives::{keccak256, keccak256_to_scalar};
|
||||||
|
|
||||||
|
// Monero starts BP+ transcripts with the following constant.
|
||||||
|
static TRANSCRIPT_CELL: OnceLock<[u8; 32]> = OnceLock::new();
|
||||||
|
pub(crate) fn TRANSCRIPT() -> [u8; 32] {
|
||||||
|
// Why this uses a hash_to_point is completely unknown.
|
||||||
|
*TRANSCRIPT_CELL
|
||||||
|
.get_or_init(|| hash_to_point(keccak256(b"bulletproof_plus_transcript")).compress().to_bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn initial_transcript(commitments: core::slice::Iter<'_, EdwardsPoint>) -> Scalar {
|
||||||
|
let commitments_hash =
|
||||||
|
keccak256_to_scalar(commitments.flat_map(|V| V.compress().to_bytes()).collect::<Vec<_>>());
|
||||||
|
keccak256_to_scalar([TRANSCRIPT().as_ref(), &commitments_hash.to_bytes()].concat())
|
||||||
|
}
|
||||||
@@ -1,24 +1,21 @@
|
|||||||
use std_shims::vec::Vec;
|
use std_shims::vec::Vec;
|
||||||
|
|
||||||
use rand_core::{RngCore, CryptoRng};
|
use rand_core::{RngCore, CryptoRng};
|
||||||
|
|
||||||
use zeroize::{Zeroize, ZeroizeOnDrop};
|
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||||
|
|
||||||
use multiexp::{BatchVerifier, multiexp, multiexp_vartime};
|
use curve25519_dalek::{scalar::Scalar, edwards::EdwardsPoint};
|
||||||
use group::{
|
|
||||||
ff::{Field, PrimeField},
|
|
||||||
GroupEncoding,
|
|
||||||
};
|
|
||||||
use dalek_ff_group::{Scalar, EdwardsPoint};
|
|
||||||
|
|
||||||
use crate::ringct::bulletproofs::plus::{
|
use monero_primitives::{INV_EIGHT, keccak256_to_scalar};
|
||||||
ScalarVector, PointVector, GeneratorsList, Generators, padded_pow_of_2, transcript::*,
|
use crate::{
|
||||||
|
core::{multiexp, multiexp_vartime},
|
||||||
|
batch_verifier::BulletproofsPlusBatchVerifier,
|
||||||
|
plus::{ScalarVector, PointVector, GeneratorsList, BpPlusGenerators, padded_pow_of_2},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Figure 1 of the Bulletproofs+ paper
|
// Figure 1 of the Bulletproofs+ paper
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub(crate) struct WipStatement {
|
pub(crate) struct WipStatement {
|
||||||
generators: Generators,
|
generators: BpPlusGenerators,
|
||||||
P: EdwardsPoint,
|
P: EdwardsPoint,
|
||||||
y: ScalarVector,
|
y: ScalarVector,
|
||||||
}
|
}
|
||||||
@@ -68,7 +65,7 @@ pub(crate) struct WipProof {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl WipStatement {
|
impl WipStatement {
|
||||||
pub(crate) fn new(generators: Generators, P: EdwardsPoint, y: Scalar) -> Self {
|
pub(crate) fn new(generators: BpPlusGenerators, P: EdwardsPoint, y: Scalar) -> Self {
|
||||||
debug_assert_eq!(generators.len(), padded_pow_of_2(generators.len()));
|
debug_assert_eq!(generators.len(), padded_pow_of_2(generators.len()));
|
||||||
|
|
||||||
// y ** n
|
// y ** n
|
||||||
@@ -82,16 +79,26 @@ impl WipStatement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn transcript_L_R(transcript: &mut Scalar, L: EdwardsPoint, R: EdwardsPoint) -> Scalar {
|
fn transcript_L_R(transcript: &mut Scalar, L: EdwardsPoint, R: EdwardsPoint) -> Scalar {
|
||||||
let e = hash_to_scalar(
|
let e = keccak256_to_scalar(
|
||||||
&[transcript.to_repr().as_ref(), L.to_bytes().as_ref(), R.to_bytes().as_ref()].concat(),
|
[
|
||||||
|
transcript.to_bytes().as_ref(),
|
||||||
|
L.compress().to_bytes().as_ref(),
|
||||||
|
R.compress().to_bytes().as_ref(),
|
||||||
|
]
|
||||||
|
.concat(),
|
||||||
);
|
);
|
||||||
*transcript = e;
|
*transcript = e;
|
||||||
e
|
e
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transcript_A_B(transcript: &mut Scalar, A: EdwardsPoint, B: EdwardsPoint) -> Scalar {
|
fn transcript_A_B(transcript: &mut Scalar, A: EdwardsPoint, B: EdwardsPoint) -> Scalar {
|
||||||
let e = hash_to_scalar(
|
let e = keccak256_to_scalar(
|
||||||
&[transcript.to_repr().as_ref(), A.to_bytes().as_ref(), B.to_bytes().as_ref()].concat(),
|
[
|
||||||
|
transcript.to_bytes().as_ref(),
|
||||||
|
A.compress().to_bytes().as_ref(),
|
||||||
|
B.compress().to_bytes().as_ref(),
|
||||||
|
]
|
||||||
|
.concat(),
|
||||||
);
|
);
|
||||||
*transcript = e;
|
*transcript = e;
|
||||||
e
|
e
|
||||||
@@ -119,7 +126,7 @@ impl WipStatement {
|
|||||||
debug_assert_eq!(g_bold1.len(), h_bold1.len());
|
debug_assert_eq!(g_bold1.len(), h_bold1.len());
|
||||||
|
|
||||||
let e = Self::transcript_L_R(transcript, L, R);
|
let e = Self::transcript_L_R(transcript, L, R);
|
||||||
let inv_e = e.invert().unwrap();
|
let inv_e = e.invert();
|
||||||
|
|
||||||
// This vartime is safe as all of these arguments are public
|
// This vartime is safe as all of these arguments are public
|
||||||
let mut new_g_bold = Vec::with_capacity(g_bold1.len());
|
let mut new_g_bold = Vec::with_capacity(g_bold1.len());
|
||||||
@@ -133,8 +140,8 @@ impl WipStatement {
|
|||||||
new_h_bold.push(multiexp_vartime(&[(e, h_bold.0), (inv_e, h_bold.1)]));
|
new_h_bold.push(multiexp_vartime(&[(e, h_bold.0), (inv_e, h_bold.1)]));
|
||||||
}
|
}
|
||||||
|
|
||||||
let e_square = e.square();
|
let e_square = e * e;
|
||||||
let inv_e_square = inv_e.square();
|
let inv_e_square = inv_e * inv_e;
|
||||||
|
|
||||||
(e, inv_e, e_square, inv_e_square, PointVector(new_g_bold), PointVector(new_h_bold))
|
(e, inv_e, e_square, inv_e_square, PointVector(new_g_bold), PointVector(new_h_bold))
|
||||||
}
|
}
|
||||||
@@ -177,7 +184,7 @@ impl WipStatement {
|
|||||||
|
|
||||||
// Sanity check since if the above failed to populate, it'd be critical
|
// Sanity check since if the above failed to populate, it'd be critical
|
||||||
for product in &products {
|
for product in &products {
|
||||||
debug_assert!(!bool::from(product.is_zero()));
|
debug_assert!(*product != Scalar::ZERO);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,12 +204,12 @@ impl WipStatement {
|
|||||||
if generators.len() != witness.a.len() {
|
if generators.len() != witness.a.len() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let (g, h) = (Generators::g(), Generators::h());
|
let (g, h) = (BpPlusGenerators::g(), BpPlusGenerators::h());
|
||||||
let mut g_bold = vec![];
|
let mut g_bold = vec![];
|
||||||
let mut h_bold = vec![];
|
let mut h_bold = vec![];
|
||||||
for i in 0 .. generators.len() {
|
for i in 0 .. generators.len() {
|
||||||
g_bold.push(generators.generator(GeneratorsList::GBold1, i));
|
g_bold.push(generators.generator(GeneratorsList::GBold, i));
|
||||||
h_bold.push(generators.generator(GeneratorsList::HBold1, i));
|
h_bold.push(generators.generator(GeneratorsList::HBold, i));
|
||||||
}
|
}
|
||||||
let mut g_bold = PointVector(g_bold);
|
let mut g_bold = PointVector(g_bold);
|
||||||
let mut h_bold = PointVector(h_bold);
|
let mut h_bold = PointVector(h_bold);
|
||||||
@@ -261,7 +268,7 @@ impl WipStatement {
|
|||||||
let c_r = (a2.clone() * y_n_hat).weighted_inner_product(&b1, &y);
|
let c_r = (a2.clone() * y_n_hat).weighted_inner_product(&b1, &y);
|
||||||
|
|
||||||
// TODO: Calculate these with a batch inversion
|
// TODO: Calculate these with a batch inversion
|
||||||
let y_inv_n_hat = y_n_hat.invert().unwrap();
|
let y_inv_n_hat = y_n_hat.invert();
|
||||||
|
|
||||||
let mut L_terms = (a1.clone() * y_inv_n_hat)
|
let mut L_terms = (a1.clone() * y_inv_n_hat)
|
||||||
.0
|
.0
|
||||||
@@ -271,7 +278,7 @@ impl WipStatement {
|
|||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
L_terms.push((c_l, g));
|
L_terms.push((c_l, g));
|
||||||
L_terms.push((d_l, h));
|
L_terms.push((d_l, h));
|
||||||
let L = multiexp(&L_terms) * Scalar(crate::INV_EIGHT());
|
let L = multiexp(&L_terms) * INV_EIGHT();
|
||||||
L_vec.push(L);
|
L_vec.push(L);
|
||||||
L_terms.zeroize();
|
L_terms.zeroize();
|
||||||
|
|
||||||
@@ -283,7 +290,7 @@ impl WipStatement {
|
|||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
R_terms.push((c_r, g));
|
R_terms.push((c_r, g));
|
||||||
R_terms.push((d_r, h));
|
R_terms.push((d_r, h));
|
||||||
let R = multiexp(&R_terms) * Scalar(crate::INV_EIGHT());
|
let R = multiexp(&R_terms) * INV_EIGHT();
|
||||||
R_vec.push(R);
|
R_vec.push(R);
|
||||||
R_terms.zeroize();
|
R_terms.zeroize();
|
||||||
|
|
||||||
@@ -316,33 +323,32 @@ impl WipStatement {
|
|||||||
|
|
||||||
let mut A_terms =
|
let mut A_terms =
|
||||||
vec![(r, g_bold[0]), (s, h_bold[0]), ((ry * b[0]) + (s * y[0] * a[0]), g), (delta, h)];
|
vec![(r, g_bold[0]), (s, h_bold[0]), ((ry * b[0]) + (s * y[0] * a[0]), g), (delta, h)];
|
||||||
let A = multiexp(&A_terms) * Scalar(crate::INV_EIGHT());
|
let A = multiexp(&A_terms) * INV_EIGHT();
|
||||||
A_terms.zeroize();
|
A_terms.zeroize();
|
||||||
|
|
||||||
let mut B_terms = vec![(ry * s, g), (eta, h)];
|
let mut B_terms = vec![(ry * s, g), (eta, h)];
|
||||||
let B = multiexp(&B_terms) * Scalar(crate::INV_EIGHT());
|
let B = multiexp(&B_terms) * INV_EIGHT();
|
||||||
B_terms.zeroize();
|
B_terms.zeroize();
|
||||||
|
|
||||||
let e = Self::transcript_A_B(&mut transcript, A, B);
|
let e = Self::transcript_A_B(&mut transcript, A, B);
|
||||||
|
|
||||||
let r_answer = r + (a[0] * e);
|
let r_answer = r + (a[0] * e);
|
||||||
let s_answer = s + (b[0] * e);
|
let s_answer = s + (b[0] * e);
|
||||||
let delta_answer = eta + (delta * e) + (alpha * e.square());
|
let delta_answer = eta + (delta * e) + (alpha * (e * e));
|
||||||
|
|
||||||
Some(WipProof { L: L_vec, R: R_vec, A, B, r_answer, s_answer, delta_answer })
|
Some(WipProof { L: L_vec, R: R_vec, A, B, r_answer, s_answer, delta_answer })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn verify<Id: Copy + Zeroize, R: RngCore + CryptoRng>(
|
pub(crate) fn verify<R: RngCore + CryptoRng>(
|
||||||
self,
|
self,
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
verifier: &mut BatchVerifier<Id, EdwardsPoint>,
|
verifier: &mut BulletproofsPlusBatchVerifier,
|
||||||
id: Id,
|
|
||||||
mut transcript: Scalar,
|
mut transcript: Scalar,
|
||||||
mut proof: WipProof,
|
mut proof: WipProof,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let WipStatement { generators, P, y } = self;
|
let verifier_weight = Scalar::random(rng);
|
||||||
|
|
||||||
let (g, h) = (Generators::g(), Generators::h());
|
let WipStatement { generators, P, y } = self;
|
||||||
|
|
||||||
// Verify the L/R lengths
|
// Verify the L/R lengths
|
||||||
{
|
{
|
||||||
@@ -359,7 +365,7 @@ impl WipStatement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let inv_y = {
|
let inv_y = {
|
||||||
let inv_y = y[0].invert().unwrap();
|
let inv_y = y[0].invert();
|
||||||
let mut res = Vec::with_capacity(y.len());
|
let mut res = Vec::with_capacity(y.len());
|
||||||
res.push(inv_y);
|
res.push(inv_y);
|
||||||
while res.len() < y.len() {
|
while res.len() < y.len() {
|
||||||
@@ -368,51 +374,49 @@ impl WipStatement {
|
|||||||
res
|
res
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut P_terms = vec![(Scalar::ONE, P)];
|
let mut e_is = Vec::with_capacity(proof.L.len());
|
||||||
P_terms.reserve(6 + (2 * generators.len()) + proof.L.len());
|
|
||||||
|
|
||||||
let mut challenges = Vec::with_capacity(proof.L.len());
|
|
||||||
let product_cache = {
|
|
||||||
let mut es = Vec::with_capacity(proof.L.len());
|
|
||||||
for (L, R) in proof.L.iter_mut().zip(proof.R.iter_mut()) {
|
for (L, R) in proof.L.iter_mut().zip(proof.R.iter_mut()) {
|
||||||
es.push(Self::transcript_L_R(&mut transcript, *L, *R));
|
e_is.push(Self::transcript_L_R(&mut transcript, *L, *R));
|
||||||
*L = L.mul_by_cofactor();
|
*L = L.mul_by_cofactor();
|
||||||
*R = R.mul_by_cofactor();
|
*R = R.mul_by_cofactor();
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut inv_es = es.clone();
|
let e = Self::transcript_A_B(&mut transcript, proof.A, proof.B);
|
||||||
let mut scratch = vec![Scalar::ZERO; es.len()];
|
proof.A = proof.A.mul_by_cofactor();
|
||||||
group::ff::BatchInverter::invert_with_external_scratch(&mut inv_es, &mut scratch);
|
proof.B = proof.B.mul_by_cofactor();
|
||||||
drop(scratch);
|
let neg_e_square = verifier_weight * -(e * e);
|
||||||
|
|
||||||
debug_assert_eq!(es.len(), inv_es.len());
|
verifier.0.other.push((neg_e_square, P));
|
||||||
debug_assert_eq!(es.len(), proof.L.len());
|
|
||||||
debug_assert_eq!(es.len(), proof.R.len());
|
let mut challenges = Vec::with_capacity(proof.L.len());
|
||||||
for ((e, inv_e), (L, R)) in
|
let product_cache = {
|
||||||
es.drain(..).zip(inv_es.drain(..)).zip(proof.L.iter().zip(proof.R.iter()))
|
let mut inv_e_is = e_is.clone();
|
||||||
|
Scalar::batch_invert(&mut inv_e_is);
|
||||||
|
|
||||||
|
debug_assert_eq!(e_is.len(), inv_e_is.len());
|
||||||
|
debug_assert_eq!(e_is.len(), proof.L.len());
|
||||||
|
debug_assert_eq!(e_is.len(), proof.R.len());
|
||||||
|
for ((e_i, inv_e_i), (L, R)) in
|
||||||
|
e_is.drain(..).zip(inv_e_is.drain(..)).zip(proof.L.iter().zip(proof.R.iter()))
|
||||||
{
|
{
|
||||||
debug_assert_eq!(e.invert().unwrap(), inv_e);
|
debug_assert_eq!(e_i.invert(), inv_e_i);
|
||||||
|
|
||||||
challenges.push((e, inv_e));
|
challenges.push((e_i, inv_e_i));
|
||||||
|
|
||||||
let e_square = e.square();
|
let e_i_square = e_i * e_i;
|
||||||
let inv_e_square = inv_e.square();
|
let inv_e_i_square = inv_e_i * inv_e_i;
|
||||||
P_terms.push((e_square, *L));
|
verifier.0.other.push((neg_e_square * e_i_square, *L));
|
||||||
P_terms.push((inv_e_square, *R));
|
verifier.0.other.push((neg_e_square * inv_e_i_square, *R));
|
||||||
}
|
}
|
||||||
|
|
||||||
Self::challenge_products(&challenges)
|
Self::challenge_products(&challenges)
|
||||||
};
|
};
|
||||||
|
|
||||||
let e = Self::transcript_A_B(&mut transcript, proof.A, proof.B);
|
while verifier.0.g_bold.len() < generators.len() {
|
||||||
proof.A = proof.A.mul_by_cofactor();
|
verifier.0.g_bold.push(Scalar::ZERO);
|
||||||
proof.B = proof.B.mul_by_cofactor();
|
}
|
||||||
let neg_e_square = -e.square();
|
while verifier.0.h_bold.len() < generators.len() {
|
||||||
|
verifier.0.h_bold.push(Scalar::ZERO);
|
||||||
let mut multiexp = P_terms;
|
|
||||||
multiexp.reserve(4 + (2 * generators.len()));
|
|
||||||
for (scalar, _) in &mut multiexp {
|
|
||||||
*scalar *= neg_e_square;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let re = proof.r_answer * e;
|
let re = proof.r_answer * e;
|
||||||
@@ -421,23 +425,18 @@ impl WipStatement {
|
|||||||
if i > 0 {
|
if i > 0 {
|
||||||
scalar *= inv_y[i - 1];
|
scalar *= inv_y[i - 1];
|
||||||
}
|
}
|
||||||
multiexp.push((scalar, generators.generator(GeneratorsList::GBold1, i)));
|
verifier.0.g_bold[i] += verifier_weight * scalar;
|
||||||
}
|
}
|
||||||
|
|
||||||
let se = proof.s_answer * e;
|
let se = proof.s_answer * e;
|
||||||
for i in 0 .. generators.len() {
|
for i in 0 .. generators.len() {
|
||||||
multiexp.push((
|
verifier.0.h_bold[i] += verifier_weight * (se * product_cache[product_cache.len() - 1 - i]);
|
||||||
se * product_cache[product_cache.len() - 1 - i],
|
|
||||||
generators.generator(GeneratorsList::HBold1, i),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
multiexp.push((-e, proof.A));
|
verifier.0.other.push((verifier_weight * -e, proof.A));
|
||||||
multiexp.push((proof.r_answer * y[0] * proof.s_answer, g));
|
verifier.0.g += verifier_weight * (proof.r_answer * y[0] * proof.s_answer);
|
||||||
multiexp.push((proof.delta_answer, h));
|
verifier.0.h += verifier_weight * proof.delta_answer;
|
||||||
multiexp.push((-Scalar::ONE, proof.B));
|
verifier.0.other.push((-verifier_weight, proof.B));
|
||||||
|
|
||||||
verifier.queue(rng, id, multiexp);
|
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@@ -6,9 +6,9 @@ use std_shims::vec::Vec;
|
|||||||
|
|
||||||
use zeroize::{Zeroize, ZeroizeOnDrop};
|
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||||
|
|
||||||
use group::ff::Field;
|
use curve25519_dalek::{scalar::Scalar, edwards::EdwardsPoint};
|
||||||
use dalek_ff_group::{Scalar, EdwardsPoint};
|
|
||||||
use multiexp::multiexp;
|
use crate::core::multiexp;
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, Zeroize, ZeroizeOnDrop)]
|
#[derive(Clone, PartialEq, Eq, Debug, Zeroize, ZeroizeOnDrop)]
|
||||||
pub(crate) struct ScalarVector(pub(crate) Vec<Scalar>);
|
pub(crate) struct ScalarVector(pub(crate) Vec<Scalar>);
|
||||||
@@ -2,14 +2,11 @@ use hex_literal::hex;
|
|||||||
use rand_core::OsRng;
|
use rand_core::OsRng;
|
||||||
|
|
||||||
use curve25519_dalek::scalar::Scalar;
|
use curve25519_dalek::scalar::Scalar;
|
||||||
use monero_io::decompress_point;
|
|
||||||
use multiexp::BatchVerifier;
|
|
||||||
|
|
||||||
use crate::{
|
use monero_io::decompress_point;
|
||||||
Commitment,
|
use monero_primitives::Commitment;
|
||||||
ringct::bulletproofs::{Bulletproof, original::OriginalStruct},
|
|
||||||
wallet::TransactionError,
|
use crate::{batch_verifier::BatchVerifier, original::OriginalStruct, Bulletproof, BulletproofError};
|
||||||
};
|
|
||||||
|
|
||||||
mod plus;
|
mod plus;
|
||||||
|
|
||||||
@@ -24,7 +21,7 @@ fn bulletproofs_vector() {
|
|||||||
S: point(hex!("e1285960861783574ee2b689ae53622834eb0b035d6943103f960cd23e063fa0")),
|
S: point(hex!("e1285960861783574ee2b689ae53622834eb0b035d6943103f960cd23e063fa0")),
|
||||||
T1: point(hex!("4ea07735f184ba159d0e0eb662bac8cde3eb7d39f31e567b0fbda3aa23fe5620")),
|
T1: point(hex!("4ea07735f184ba159d0e0eb662bac8cde3eb7d39f31e567b0fbda3aa23fe5620")),
|
||||||
T2: point(hex!("b8390aa4b60b255630d40e592f55ec6b7ab5e3a96bfcdcd6f1cd1d2fc95f441e")),
|
T2: point(hex!("b8390aa4b60b255630d40e592f55ec6b7ab5e3a96bfcdcd6f1cd1d2fc95f441e")),
|
||||||
taux: scalar(hex!("5957dba8ea9afb23d6e81cc048a92f2d502c10c749dc1b2bd148ae8d41ec7107")),
|
tau_x: scalar(hex!("5957dba8ea9afb23d6e81cc048a92f2d502c10c749dc1b2bd148ae8d41ec7107")),
|
||||||
mu: scalar(hex!("923023b234c2e64774b820b4961f7181f6c1dc152c438643e5a25b0bf271bc02")),
|
mu: scalar(hex!("923023b234c2e64774b820b4961f7181f6c1dc152c438643e5a25b0bf271bc02")),
|
||||||
L: vec![
|
L: vec![
|
||||||
point(hex!("c45f656316b9ebf9d357fb6a9f85b5f09e0b991dd50a6e0ae9b02de3946c9d99")),
|
point(hex!("c45f656316b9ebf9d357fb6a9f85b5f09e0b991dd50a6e0ae9b02de3946c9d99")),
|
||||||
@@ -65,7 +62,7 @@ macro_rules! bulletproofs_tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn $name() {
|
fn $name() {
|
||||||
// Create Bulletproofs for all possible output quantities
|
// Create Bulletproofs for all possible output quantities
|
||||||
let mut verifier = BatchVerifier::new(16);
|
let mut verifier = BatchVerifier::new();
|
||||||
for i in 1 ..= 16 {
|
for i in 1 ..= 16 {
|
||||||
let commitments = (1 ..= i)
|
let commitments = (1 ..= i)
|
||||||
.map(|i| Commitment::new(Scalar::random(&mut OsRng), u64::try_from(i).unwrap()))
|
.map(|i| Commitment::new(Scalar::random(&mut OsRng), u64::try_from(i).unwrap()))
|
||||||
@@ -79,9 +76,9 @@ macro_rules! bulletproofs_tests {
|
|||||||
|
|
||||||
let commitments = commitments.iter().map(Commitment::calculate).collect::<Vec<_>>();
|
let commitments = commitments.iter().map(Commitment::calculate).collect::<Vec<_>>();
|
||||||
assert!(bp.verify(&mut OsRng, &commitments));
|
assert!(bp.verify(&mut OsRng, &commitments));
|
||||||
assert!(bp.batch_verify(&mut OsRng, &mut verifier, i, &commitments));
|
assert!(bp.batch_verify(&mut OsRng, &mut verifier, &commitments));
|
||||||
}
|
}
|
||||||
assert!(verifier.verify_vartime());
|
assert!(verifier.verify());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -98,7 +95,7 @@ macro_rules! bulletproofs_tests {
|
|||||||
Bulletproof::prove(&mut OsRng, &commitments)
|
Bulletproof::prove(&mut OsRng, &commitments)
|
||||||
})
|
})
|
||||||
.unwrap_err(),
|
.unwrap_err(),
|
||||||
TransactionError::TooManyOutputs,
|
BulletproofError::TooManyCommitments,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -1,30 +1,28 @@
|
|||||||
use rand_core::{RngCore, OsRng};
|
use rand_core::{RngCore, OsRng};
|
||||||
|
|
||||||
use multiexp::BatchVerifier;
|
use curve25519_dalek::Scalar;
|
||||||
use group::ff::Field;
|
|
||||||
use dalek_ff_group::Scalar;
|
use monero_primitives::Commitment;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Commitment,
|
batch_verifier::BulletproofsPlusBatchVerifier,
|
||||||
ringct::bulletproofs::plus::aggregate_range_proof::{
|
plus::aggregate_range_proof::{AggregateRangeStatement, AggregateRangeWitness},
|
||||||
AggregateRangeStatement, AggregateRangeWitness,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_aggregate_range_proof() {
|
fn test_aggregate_range_proof() {
|
||||||
let mut verifier = BatchVerifier::new(16);
|
let mut verifier = BulletproofsPlusBatchVerifier::default();
|
||||||
for m in 1 ..= 16 {
|
for m in 1 ..= 16 {
|
||||||
let mut commitments = vec![];
|
let mut commitments = vec![];
|
||||||
for _ in 0 .. m {
|
for _ in 0 .. m {
|
||||||
commitments.push(Commitment::new(*Scalar::random(&mut OsRng), OsRng.next_u64()));
|
commitments.push(Commitment::new(Scalar::random(&mut OsRng), OsRng.next_u64()));
|
||||||
}
|
}
|
||||||
let commitment_points = commitments.iter().map(Commitment::calculate).collect();
|
let commitment_points = commitments.iter().map(Commitment::calculate).collect();
|
||||||
let statement = AggregateRangeStatement::new(commitment_points).unwrap();
|
let statement = AggregateRangeStatement::new(commitment_points).unwrap();
|
||||||
let witness = AggregateRangeWitness::new(commitments).unwrap();
|
let witness = AggregateRangeWitness::new(commitments).unwrap();
|
||||||
|
|
||||||
let proof = statement.clone().prove(&mut OsRng, &witness).unwrap();
|
let proof = statement.clone().prove(&mut OsRng, &witness).unwrap();
|
||||||
statement.verify(&mut OsRng, &mut verifier, (), proof);
|
statement.verify(&mut OsRng, &mut verifier, proof);
|
||||||
}
|
}
|
||||||
assert!(verifier.verify_vartime());
|
assert!(verifier.verify());
|
||||||
}
|
}
|
||||||
@@ -2,13 +2,14 @@
|
|||||||
|
|
||||||
use rand_core::OsRng;
|
use rand_core::OsRng;
|
||||||
|
|
||||||
use multiexp::BatchVerifier;
|
use curve25519_dalek::{traits::Identity, scalar::Scalar, edwards::EdwardsPoint};
|
||||||
use group::{ff::Field, Group};
|
|
||||||
use dalek_ff_group::{Scalar, EdwardsPoint};
|
|
||||||
|
|
||||||
use crate::ringct::bulletproofs::plus::{
|
use crate::{
|
||||||
ScalarVector, PointVector, GeneratorsList, Generators,
|
batch_verifier::BulletproofsPlusBatchVerifier,
|
||||||
|
plus::{
|
||||||
|
ScalarVector, PointVector, GeneratorsList, BpPlusGenerators,
|
||||||
weighted_inner_product::{WipStatement, WipWitness},
|
weighted_inner_product::{WipStatement, WipWitness},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -17,33 +18,33 @@ fn test_zero_weighted_inner_product() {
|
|||||||
let P = EdwardsPoint::identity();
|
let P = EdwardsPoint::identity();
|
||||||
let y = Scalar::random(&mut OsRng);
|
let y = Scalar::random(&mut OsRng);
|
||||||
|
|
||||||
let generators = Generators::new().reduce(1);
|
let generators = BpPlusGenerators::new().reduce(1);
|
||||||
let statement = WipStatement::new(generators, P, y);
|
let statement = WipStatement::new(generators, P, y);
|
||||||
let witness = WipWitness::new(ScalarVector::new(1), ScalarVector::new(1), Scalar::ZERO).unwrap();
|
let witness = WipWitness::new(ScalarVector::new(1), ScalarVector::new(1), Scalar::ZERO).unwrap();
|
||||||
|
|
||||||
let transcript = Scalar::random(&mut OsRng);
|
let transcript = Scalar::random(&mut OsRng);
|
||||||
let proof = statement.clone().prove(&mut OsRng, transcript, &witness).unwrap();
|
let proof = statement.clone().prove(&mut OsRng, transcript, &witness).unwrap();
|
||||||
|
|
||||||
let mut verifier = BatchVerifier::new(1);
|
let mut verifier = BulletproofsPlusBatchVerifier::default();
|
||||||
statement.verify(&mut OsRng, &mut verifier, (), transcript, proof);
|
statement.verify(&mut OsRng, &mut verifier, transcript, proof);
|
||||||
assert!(verifier.verify_vartime());
|
assert!(verifier.verify());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_weighted_inner_product() {
|
fn test_weighted_inner_product() {
|
||||||
// P = sum(g_bold * a, h_bold * b, g * (a * y * b), h * alpha)
|
// P = sum(g_bold * a, h_bold * b, g * (a * y * b), h * alpha)
|
||||||
let mut verifier = BatchVerifier::new(6);
|
let mut verifier = BulletproofsPlusBatchVerifier::default();
|
||||||
let generators = Generators::new();
|
let generators = BpPlusGenerators::new();
|
||||||
for i in [1, 2, 4, 8, 16, 32] {
|
for i in [1, 2, 4, 8, 16, 32] {
|
||||||
let generators = generators.reduce(i);
|
let generators = generators.reduce(i);
|
||||||
let g = Generators::g();
|
let g = BpPlusGenerators::g();
|
||||||
let h = Generators::h();
|
let h = BpPlusGenerators::h();
|
||||||
assert_eq!(generators.len(), i);
|
assert_eq!(generators.len(), i);
|
||||||
let mut g_bold = vec![];
|
let mut g_bold = vec![];
|
||||||
let mut h_bold = vec![];
|
let mut h_bold = vec![];
|
||||||
for i in 0 .. i {
|
for i in 0 .. i {
|
||||||
g_bold.push(generators.generator(GeneratorsList::GBold1, i));
|
g_bold.push(generators.generator(GeneratorsList::GBold, i));
|
||||||
h_bold.push(generators.generator(GeneratorsList::HBold1, i));
|
h_bold.push(generators.generator(GeneratorsList::HBold, i));
|
||||||
}
|
}
|
||||||
let g_bold = PointVector(g_bold);
|
let g_bold = PointVector(g_bold);
|
||||||
let h_bold = PointVector(h_bold);
|
let h_bold = PointVector(h_bold);
|
||||||
@@ -75,7 +76,7 @@ fn test_weighted_inner_product() {
|
|||||||
|
|
||||||
let transcript = Scalar::random(&mut OsRng);
|
let transcript = Scalar::random(&mut OsRng);
|
||||||
let proof = statement.clone().prove(&mut OsRng, transcript, &witness).unwrap();
|
let proof = statement.clone().prove(&mut OsRng, transcript, &witness).unwrap();
|
||||||
statement.verify(&mut OsRng, &mut verifier, (), transcript, proof);
|
statement.verify(&mut OsRng, &mut verifier, transcript, proof);
|
||||||
}
|
}
|
||||||
assert!(verifier.verify_vartime());
|
assert!(verifier.verify());
|
||||||
}
|
}
|
||||||
@@ -52,8 +52,6 @@ std = [
|
|||||||
"zeroize/std",
|
"zeroize/std",
|
||||||
"subtle/std",
|
"subtle/std",
|
||||||
|
|
||||||
"curve25519-dalek/precomputed-tables",
|
|
||||||
|
|
||||||
"rand_chacha?/std",
|
"rand_chacha?/std",
|
||||||
"transcript?/std",
|
"transcript?/std",
|
||||||
"group?/alloc",
|
"group?/alloc",
|
||||||
|
|||||||
@@ -4,14 +4,12 @@ mod binaries {
|
|||||||
|
|
||||||
pub(crate) use curve25519_dalek::{scalar::Scalar, edwards::EdwardsPoint};
|
pub(crate) use curve25519_dalek::{scalar::Scalar, edwards::EdwardsPoint};
|
||||||
|
|
||||||
pub(crate) use multiexp::BatchVerifier;
|
|
||||||
|
|
||||||
pub(crate) use serde::Deserialize;
|
pub(crate) use serde::Deserialize;
|
||||||
pub(crate) use serde_json::json;
|
pub(crate) use serde_json::json;
|
||||||
|
|
||||||
pub(crate) use monero_serai::{
|
pub(crate) use monero_serai::{
|
||||||
Commitment,
|
Commitment,
|
||||||
ringct::RctPrunable,
|
ringct::{RctPrunable, bulletproofs::batch_verifier::BatchVerifier},
|
||||||
transaction::{Input, Transaction},
|
transaction::{Input, Transaction},
|
||||||
block::Block,
|
block::Block,
|
||||||
rpc::{RpcError, Rpc, HttpRpc},
|
rpc::{RpcError, Rpc, HttpRpc},
|
||||||
@@ -95,7 +93,7 @@ mod binaries {
|
|||||||
all_txs.extend(txs.txs);
|
all_txs.extend(txs.txs);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut batch = BatchVerifier::new(block.txs.len());
|
let mut batch = BatchVerifier::new();
|
||||||
for (tx_hash, tx_res) in block.txs.into_iter().zip(all_txs) {
|
for (tx_hash, tx_res) in block.txs.into_iter().zip(all_txs) {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
tx_res.tx_hash,
|
tx_res.tx_hash,
|
||||||
@@ -135,7 +133,6 @@ mod binaries {
|
|||||||
assert!(bulletproofs.batch_verify(
|
assert!(bulletproofs.batch_verify(
|
||||||
&mut rand_core::OsRng,
|
&mut rand_core::OsRng,
|
||||||
&mut batch,
|
&mut batch,
|
||||||
(),
|
|
||||||
&tx.rct_signatures.base.commitments
|
&tx.rct_signatures.base.commitments
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@@ -143,7 +140,6 @@ mod binaries {
|
|||||||
assert!(bulletproofs.batch_verify(
|
assert!(bulletproofs.batch_verify(
|
||||||
&mut rand_core::OsRng,
|
&mut rand_core::OsRng,
|
||||||
&mut batch,
|
&mut batch,
|
||||||
(),
|
|
||||||
&tx.rct_signatures.base.commitments
|
&tx.rct_signatures.base.commitments
|
||||||
));
|
));
|
||||||
|
|
||||||
@@ -234,7 +230,7 @@ mod binaries {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert!(batch.verify_vartime());
|
assert!(batch.verify());
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("Deserialized, hashed, and reserialized {block_i} with {txs_len} TXs");
|
println!("Deserialized, hashed, and reserialized {block_i} with {txs_len} TXs");
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
use std_shims::{sync::OnceLock, vec::Vec};
|
|
||||||
|
|
||||||
use curve25519_dalek::EdwardsPoint;
|
|
||||||
use dalek_ff_group::Scalar;
|
|
||||||
|
|
||||||
use monero_generators::{hash_to_point as raw_hash_to_point};
|
|
||||||
use crate::{hash, hash_to_scalar as dalek_hash};
|
|
||||||
|
|
||||||
// Monero starts BP+ transcripts with the following constant.
|
|
||||||
static TRANSCRIPT_CELL: OnceLock<[u8; 32]> = OnceLock::new();
|
|
||||||
pub(crate) fn TRANSCRIPT() -> [u8; 32] {
|
|
||||||
// Why this uses a hash_to_point is completely unknown.
|
|
||||||
*TRANSCRIPT_CELL
|
|
||||||
.get_or_init(|| raw_hash_to_point(hash(b"bulletproof_plus_transcript")).compress().to_bytes())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn hash_to_scalar(data: &[u8]) -> Scalar {
|
|
||||||
Scalar(dalek_hash(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn initial_transcript(commitments: core::slice::Iter<'_, EdwardsPoint>) -> Scalar {
|
|
||||||
let commitments_hash =
|
|
||||||
hash_to_scalar(&commitments.flat_map(|V| V.compress().to_bytes()).collect::<Vec<_>>());
|
|
||||||
hash_to_scalar(&[TRANSCRIPT().as_ref(), &commitments_hash.to_bytes()].concat())
|
|
||||||
}
|
|
||||||
@@ -18,7 +18,7 @@ pub use monero_clsag as clsag;
|
|||||||
/// BorromeanRange struct, along with verifying functionality.
|
/// BorromeanRange struct, along with verifying functionality.
|
||||||
pub mod borromean;
|
pub mod borromean;
|
||||||
/// Bulletproofs(+) structs, along with proving and verifying functionality.
|
/// Bulletproofs(+) structs, along with proving and verifying functionality.
|
||||||
pub mod bulletproofs;
|
pub use monero_bulletproofs as bulletproofs;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Protocol,
|
Protocol,
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
mod unreduced_scalar;
|
mod unreduced_scalar;
|
||||||
mod bulletproofs;
|
|
||||||
mod address;
|
mod address;
|
||||||
mod seed;
|
mod seed;
|
||||||
mod extra;
|
mod extra;
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ use crate::{
|
|||||||
ringct::{
|
ringct::{
|
||||||
generate_key_image,
|
generate_key_image,
|
||||||
clsag::{ClsagError, ClsagContext, Clsag},
|
clsag::{ClsagError, ClsagContext, Clsag},
|
||||||
bulletproofs::{MAX_OUTPUTS, Bulletproof},
|
bulletproofs::{MAX_COMMITMENTS, Bulletproof},
|
||||||
RctBase, RctPrunable, RctSignatures,
|
RctBase, RctPrunable, RctSignatures,
|
||||||
},
|
},
|
||||||
transaction::{Input, Output, Timelock, TransactionPrefix, Transaction},
|
transaction::{Input, Output, Timelock, TransactionPrefix, Transaction},
|
||||||
@@ -504,7 +504,7 @@ impl SignableTransaction {
|
|||||||
let out_amount = payments.iter().map(|payment| payment.1).sum::<u64>();
|
let out_amount = payments.iter().map(|payment| payment.1).sum::<u64>();
|
||||||
|
|
||||||
let outputs = payments.len() + usize::from(change.address.is_some());
|
let outputs = payments.len() + usize::from(change.address.is_some());
|
||||||
if outputs > MAX_OUTPUTS {
|
if outputs > MAX_COMMITMENTS {
|
||||||
Err(TransactionError::TooManyOutputs)?;
|
Err(TransactionError::TooManyOutputs)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -803,7 +803,7 @@ impl SignableTransaction {
|
|||||||
let commitments = outputs.iter().map(|output| output.commitment.clone()).collect::<Vec<_>>();
|
let commitments = outputs.iter().map(|output| output.commitment.clone()).collect::<Vec<_>>();
|
||||||
let sum = commitments.iter().map(|commitment| commitment.mask).sum();
|
let sum = commitments.iter().map(|commitment| commitment.mask).sum();
|
||||||
|
|
||||||
// Safe due to the constructor checking MAX_OUTPUTS
|
// Safe due to the constructor checking MAX_COMMITMENTS
|
||||||
let bp = if self.protocol.bp_plus() {
|
let bp = if self.protocol.bp_plus() {
|
||||||
Bulletproof::prove_plus(rng, commitments.clone()).unwrap()
|
Bulletproof::prove_plus(rng, commitments.clone()).unwrap()
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -39,4 +39,5 @@ monero-io = { path = "../../coins/monero/io", default-features = false }
|
|||||||
monero-generators = { path = "../../coins/monero/generators", default-features = false }
|
monero-generators = { path = "../../coins/monero/generators", default-features = false }
|
||||||
monero-primitives = { path = "../../coins/monero/primitives", default-features = false }
|
monero-primitives = { path = "../../coins/monero/primitives", default-features = false }
|
||||||
monero-clsag = { path = "../../coins/monero/ringct/clsag", default-features = false }
|
monero-clsag = { path = "../../coins/monero/ringct/clsag", default-features = false }
|
||||||
|
monero-bulletproofs = { path = "../../coins/monero/ringct/bulletproofs", default-features = false }
|
||||||
monero-serai = { path = "../../coins/monero", default-features = false }
|
monero-serai = { path = "../../coins/monero", default-features = false }
|
||||||
|
|||||||
Reference in New Issue
Block a user