mirror of
https://github.com/serai-dex/serai.git
synced 2025-02-02 03:06:31 +00:00
Remove Monero as a dependency
Introduces missing CLSAG checks. The only difference now should be the additional rejection of torsioned points, which is relevant to https://github.com/serai-dex/serai/issues/25. Considering this is only currently used for FROST verification, this should be fine. Closes https://github.com/serai-dex/serai/issues/19 by making it irrelevant. Increases priority of https://github.com/serai-dex/serai/issues/68, as now it's used for the BP generators which are done at first-proof. Also merges BP's stricter hash_to_point with the library's, since CLSAG has the same bound.
This commit is contained in:
parent
ee29f6d6d8
commit
696da8228e
14 changed files with 33 additions and 403 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -4569,7 +4569,6 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"base58-monero 1.0.0",
|
||||
"blake2",
|
||||
"cc",
|
||||
"curve25519-dalek 3.2.0",
|
||||
"dalek-ff-group",
|
||||
"dleq-serai",
|
||||
|
|
|
@ -6,9 +6,6 @@ license = "MIT"
|
|||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||
edition = "2021"
|
||||
|
||||
[build-dependencies]
|
||||
cc = "1.0"
|
||||
|
||||
[dependencies]
|
||||
hex-literal = "0.3"
|
||||
lazy_static = "1"
|
||||
|
@ -45,7 +42,6 @@ monero = "0.16"
|
|||
reqwest = { version = "0.11", features = ["json"] }
|
||||
|
||||
[features]
|
||||
experimental = []
|
||||
multisig = ["rand_chacha", "blake2", "transcript", "frost", "dleq"]
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
use std::process::Command;
|
||||
|
||||
fn main() {
|
||||
if !Command::new("git")
|
||||
.args(&["submodule", "update", "--init", "--recursive"])
|
||||
.status()
|
||||
.unwrap()
|
||||
.success()
|
||||
{
|
||||
panic!("git failed to init submodules");
|
||||
}
|
||||
|
||||
println!("cargo:rerun-if-changed=c/wrapper.cpp");
|
||||
#[rustfmt::skip]
|
||||
cc::Build::new()
|
||||
.static_flag(true)
|
||||
.warnings(false)
|
||||
.extra_warnings(false)
|
||||
.flag("-Wno-deprecated-declarations")
|
||||
|
||||
.include("c/monero/external/supercop/include")
|
||||
.include("c/monero/contrib/epee/include")
|
||||
.include("c/monero/src")
|
||||
.include("c/monero/build/release/generated_include")
|
||||
|
||||
.define("AUTO_INITIALIZE_EASYLOGGINGPP", None)
|
||||
.include("c/monero/external/easylogging++")
|
||||
.file("c/monero/external/easylogging++/easylogging++.cc")
|
||||
|
||||
.file("c/monero/src/common/aligned.c")
|
||||
.file("c/monero/src/common/perf_timer.cpp")
|
||||
|
||||
.include("c/monero/src/crypto")
|
||||
.file("c/monero/src/crypto/crypto-ops-data.c")
|
||||
.file("c/monero/src/crypto/crypto-ops.c")
|
||||
.file("c/monero/src/crypto/keccak.c")
|
||||
.file("c/monero/src/crypto/hash.c")
|
||||
|
||||
.include("c/monero/src/ringct")
|
||||
.file("c/monero/src/ringct/rctCryptoOps.c")
|
||||
.file("c/monero/src/ringct/rctTypes.cpp")
|
||||
.file("c/monero/src/ringct/rctOps.cpp")
|
||||
.file("c/monero/src/ringct/multiexp.cc")
|
||||
.file("c/monero/src/ringct/bulletproofs.cc")
|
||||
.file("c/monero/src/ringct/rctSigs.cpp")
|
||||
|
||||
.file("c/wrapper.cpp")
|
||||
.compile("wrapper");
|
||||
|
||||
println!("cargo:rustc-link-lib=wrapper");
|
||||
println!("cargo:rustc-link-lib=stdc++");
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
Subproject commit 424e4de16b98506170db7b0d7d87a79ccf541744
|
|
@ -1,156 +0,0 @@
|
|||
#include <mutex>
|
||||
|
||||
#include "ringct/bulletproofs.h"
|
||||
#include "ringct/rctSigs.h"
|
||||
|
||||
typedef std::lock_guard<std::mutex> lock;
|
||||
|
||||
std::mutex rng_mutex;
|
||||
uint8_t rng_entropy[64];
|
||||
|
||||
extern "C" {
|
||||
void rng(uint8_t* seed) {
|
||||
// Set the first half to the seed
|
||||
memcpy(rng_entropy, seed, 32);
|
||||
// Set the second half to the hash of a DST to ensure a lack of collisions
|
||||
crypto::cn_fast_hash("RNG_entropy_seed", 16, (char*) &rng_entropy[32]);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void monero_wide_reduce(uint8_t* value);
|
||||
namespace crypto {
|
||||
void generate_random_bytes_not_thread_safe(size_t n, void* value) {
|
||||
size_t written = 0;
|
||||
while (written != n) {
|
||||
uint8_t hash[32];
|
||||
crypto::cn_fast_hash(rng_entropy, 64, (char*) hash);
|
||||
// Step the RNG by setting the latter half to the most recent result
|
||||
// Does not leak the RNG, even if the values are leaked (which they are
|
||||
// expected to be) due to the first half remaining constant and
|
||||
// undisclosed
|
||||
memcpy(&rng_entropy[32], hash, 32);
|
||||
|
||||
size_t next = n - written;
|
||||
if (next > 32) {
|
||||
next = 32;
|
||||
}
|
||||
memcpy(&((uint8_t*) value)[written], hash, next);
|
||||
written += next;
|
||||
}
|
||||
}
|
||||
|
||||
void random32_unbiased(unsigned char *bytes) {
|
||||
uint8_t value[64];
|
||||
generate_random_bytes_not_thread_safe(64, value);
|
||||
monero_wide_reduce(value);
|
||||
memcpy(bytes, value, 32);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
void c_hash_to_point(uint8_t* point) {
|
||||
rct::key key_point;
|
||||
ge_p3 e_p3;
|
||||
memcpy(key_point.bytes, point, 32);
|
||||
rct::hash_to_p3(e_p3, key_point);
|
||||
ge_p3_tobytes(point, &e_p3);
|
||||
}
|
||||
|
||||
uint8_t* c_generate_bp(uint8_t* seed, uint8_t len, uint64_t* a, uint8_t* m) {
|
||||
lock guard(rng_mutex);
|
||||
rng(seed);
|
||||
|
||||
rct::keyV masks;
|
||||
std::vector<uint64_t> amounts;
|
||||
masks.resize(len);
|
||||
amounts.resize(len);
|
||||
for (uint8_t i = 0; i < len; i++) {
|
||||
memcpy(masks[i].bytes, m + (i * 32), 32);
|
||||
amounts[i] = a[i];
|
||||
}
|
||||
|
||||
rct::Bulletproof bp = rct::bulletproof_PROVE(amounts, masks);
|
||||
|
||||
std::stringstream ss;
|
||||
binary_archive<true> ba(ss);
|
||||
::serialization::serialize(ba, bp);
|
||||
uint8_t* res = (uint8_t*) calloc(ss.str().size(), 1);
|
||||
memcpy(res, ss.str().data(), ss.str().size());
|
||||
return res;
|
||||
}
|
||||
|
||||
bool c_verify_bp(
|
||||
uint8_t* seed,
|
||||
uint s_len,
|
||||
uint8_t* s,
|
||||
uint8_t c_len,
|
||||
uint8_t* c
|
||||
) {
|
||||
// BPs are batch verified which use RNG based weights to ensure individual
|
||||
// integrity
|
||||
// That's why this must also have control over RNG, to prevent interrupting
|
||||
// multisig signing while not using known seeds. Considering this doesn't
|
||||
// actually define a batch, and it's only verifying a single BP,
|
||||
// it'd probably be fine, but...
|
||||
lock guard(rng_mutex);
|
||||
rng(seed);
|
||||
|
||||
rct::Bulletproof bp;
|
||||
std::stringstream ss;
|
||||
std::string str;
|
||||
str.assign((char*) s, (size_t) s_len);
|
||||
ss << str;
|
||||
binary_archive<false> ba(ss);
|
||||
::serialization::serialize(ba, bp);
|
||||
if (!ss.good()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bp.V.resize(c_len);
|
||||
for (uint8_t i = 0; i < c_len; i++) {
|
||||
memcpy(bp.V[i].bytes, &c[i * 32], 32);
|
||||
}
|
||||
|
||||
try { return rct::bulletproof_VERIFY(bp); } catch(...) { return false; }
|
||||
}
|
||||
|
||||
bool c_verify_clsag(
|
||||
uint s_len,
|
||||
uint8_t* s,
|
||||
uint8_t k_len,
|
||||
uint8_t* k,
|
||||
uint8_t* I,
|
||||
uint8_t* p,
|
||||
uint8_t* m
|
||||
) {
|
||||
rct::clsag clsag;
|
||||
std::stringstream ss;
|
||||
std::string str;
|
||||
str.assign((char*) s, (size_t) s_len);
|
||||
ss << str;
|
||||
binary_archive<false> ba(ss);
|
||||
::serialization::serialize(ba, clsag);
|
||||
if (!ss.good()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
rct::ctkeyV keys;
|
||||
keys.resize(k_len);
|
||||
for (uint8_t i = 0; i < k_len; i++) {
|
||||
memcpy(keys[i].dest.bytes, &k[(i * 2) * 32], 32);
|
||||
memcpy(keys[i].mask.bytes, &k[((i * 2) + 1) * 32], 32);
|
||||
}
|
||||
|
||||
memcpy(clsag.I.bytes, I, 32);
|
||||
|
||||
rct::key pseudo_out;
|
||||
memcpy(pseudo_out.bytes, p, 32);
|
||||
|
||||
rct::key msg;
|
||||
memcpy(msg.bytes, m, 32);
|
||||
|
||||
try {
|
||||
return verRctCLSAGSimple(msg, clsag, keys, pseudo_out);
|
||||
} catch(...) { return false; }
|
||||
}
|
||||
}
|
|
@ -1,10 +1,6 @@
|
|||
use std::slice;
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use rand_core::{RngCore, CryptoRng};
|
||||
|
||||
use subtle::ConstantTimeEq;
|
||||
|
||||
use tiny_keccak::{Hasher, Keccak};
|
||||
|
||||
use curve25519_dalek::{
|
||||
|
@ -38,26 +34,6 @@ lazy_static! {
|
|||
static ref H_TABLE: EdwardsBasepointTable = EdwardsBasepointTable::create(&H);
|
||||
}
|
||||
|
||||
// Function from libsodium our subsection of Monero relies on. Implementing it here means we don't
|
||||
// need to link against libsodium
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn crypto_verify_32(a: *const u8, b: *const u8) -> isize {
|
||||
isize::from(slice::from_raw_parts(a, 32).ct_eq(slice::from_raw_parts(b, 32)).unwrap_u8()) - 1
|
||||
}
|
||||
|
||||
// Offer a wide reduction to C. Our seeded RNG prevented Monero from defining an unbiased scalar
|
||||
// generation function, and in order to not use Monero code (which would require propagating its
|
||||
// license), the function was rewritten. It was rewritten with wide reduction, instead of rejection
|
||||
// sampling however, hence the need for this function
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn monero_wide_reduce(value: *mut u8) {
|
||||
let res =
|
||||
Scalar::from_bytes_mod_order_wide(std::slice::from_raw_parts(value, 64).try_into().unwrap());
|
||||
for (i, b) in res.to_bytes().iter().enumerate() {
|
||||
value.add(i).write(*b);
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub struct Commitment {
|
||||
|
@ -95,5 +71,11 @@ pub fn hash(data: &[u8]) -> [u8; 32] {
|
|||
}
|
||||
|
||||
pub fn hash_to_scalar(data: &[u8]) -> Scalar {
|
||||
Scalar::from_bytes_mod_order(hash(data))
|
||||
let scalar = Scalar::from_bytes_mod_order(hash(data));
|
||||
// Monero will explicitly error in this case
|
||||
// This library acknowledges its practical impossibility of it occurring, and doesn't bother to
|
||||
// code in logic to handle it. That said, if it ever occurs, something must happen in order to
|
||||
// not generate/verify a proof we believe to be valid when it isn't
|
||||
assert!(scalar != Scalar::zero(), "ZERO HASH: {:?}", data);
|
||||
scalar
|
||||
}
|
||||
|
|
|
@ -28,13 +28,7 @@ fn random_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Scalar {
|
|||
}
|
||||
|
||||
fn hash_to_scalar(data: &[u8]) -> Scalar {
|
||||
let scalar = Scalar(dalek_hash(data));
|
||||
// Monero will explicitly retry on these cases, as them occurring breaks the proof
|
||||
// This library acknowledges their practical impossibility of them occurring, and doesn't bother
|
||||
// to code in logic to handle it. That said, if they ever occur, something must happen in order
|
||||
// to not generate a proof we believe to be valid when it isn't
|
||||
assert!(!bool::from(scalar.is_zero()), "ZERO HASH: {:?}", data);
|
||||
scalar
|
||||
Scalar(dalek_hash(data))
|
||||
}
|
||||
|
||||
fn generator(i: usize) -> EdwardsPoint {
|
||||
|
|
|
@ -54,44 +54,6 @@ impl Bulletproofs {
|
|||
Ok(prove(rng, outputs))
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn verify<R: RngCore + CryptoRng>(&self, rng: &mut R, commitments: &[EdwardsPoint]) -> bool {
|
||||
if commitments.len() > 16 {
|
||||
return false;
|
||||
}
|
||||
|
||||
let mut seed = [0; 32];
|
||||
rng.fill_bytes(&mut seed);
|
||||
|
||||
let mut serialized = Vec::with_capacity((9 + (2 * self.L.len())) * 32);
|
||||
self.serialize(&mut serialized).unwrap();
|
||||
let commitments: Vec<[u8; 32]> = commitments
|
||||
.iter()
|
||||
.map(|commitment| (commitment * Scalar::from(8u8).invert()).compress().to_bytes())
|
||||
.collect();
|
||||
|
||||
unsafe {
|
||||
#[link(name = "wrapper")]
|
||||
extern "C" {
|
||||
fn c_verify_bp(
|
||||
seed: *const u8,
|
||||
serialized_len: usize,
|
||||
serialized: *const u8,
|
||||
commitments_len: u8,
|
||||
commitments: *const [u8; 32],
|
||||
) -> bool;
|
||||
}
|
||||
|
||||
c_verify_bp(
|
||||
seed.as_ptr(),
|
||||
serialized.len(),
|
||||
serialized.as_ptr(),
|
||||
u8::try_from(commitments.len()).unwrap(),
|
||||
commitments.as_ptr(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize_core<W: std::io::Write, F: Fn(&[EdwardsPoint], &mut W) -> std::io::Result<()>>(
|
||||
&self,
|
||||
w: &mut W,
|
||||
|
|
|
@ -7,7 +7,7 @@ use rand_core::{RngCore, CryptoRng};
|
|||
use curve25519_dalek::{
|
||||
constants::ED25519_BASEPOINT_TABLE,
|
||||
scalar::Scalar,
|
||||
traits::VartimePrecomputedMultiscalarMul,
|
||||
traits::{IsIdentity, VartimePrecomputedMultiscalarMul},
|
||||
edwards::{EdwardsPoint, VartimeEdwardsPrecomputation},
|
||||
};
|
||||
|
||||
|
@ -29,10 +29,14 @@ lazy_static! {
|
|||
pub enum ClsagError {
|
||||
#[error("internal error ({0})")]
|
||||
InternalError(String),
|
||||
#[error("invalid ring")]
|
||||
InvalidRing,
|
||||
#[error("invalid ring member (member {0}, ring size {1})")]
|
||||
InvalidRingMember(u8, u8),
|
||||
#[error("invalid commitment")]
|
||||
InvalidCommitment,
|
||||
#[error("invalid key image")]
|
||||
InvalidImage,
|
||||
#[error("invalid D")]
|
||||
InvalidD,
|
||||
#[error("invalid s")]
|
||||
|
@ -72,7 +76,6 @@ impl ClsagInput {
|
|||
#[allow(clippy::large_enum_variant)]
|
||||
enum Mode {
|
||||
Sign(usize, EdwardsPoint, EdwardsPoint),
|
||||
#[cfg(feature = "experimental")]
|
||||
Verify(Scalar),
|
||||
}
|
||||
|
||||
|
@ -150,7 +153,6 @@ fn core(
|
|||
c = hash_to_scalar(&to_hash);
|
||||
}
|
||||
|
||||
#[cfg(feature = "experimental")]
|
||||
Mode::Verify(c1) => {
|
||||
start = 0;
|
||||
end = n;
|
||||
|
@ -259,17 +261,31 @@ impl Clsag {
|
|||
res
|
||||
}
|
||||
|
||||
// Not extensively tested nor guaranteed to have expected parity with Monero
|
||||
#[cfg(feature = "experimental")]
|
||||
pub fn rust_verify(
|
||||
pub fn verify(
|
||||
&self,
|
||||
ring: &[[EdwardsPoint; 2]],
|
||||
I: &EdwardsPoint,
|
||||
pseudo_out: &EdwardsPoint,
|
||||
msg: &[u8; 32],
|
||||
) -> Result<(), ClsagError> {
|
||||
let (_, c1) =
|
||||
core(ring, I, pseudo_out, msg, &self.D.mul_by_cofactor(), &self.s, Mode::Verify(self.c1));
|
||||
// Preliminary checks. s, c1, and points must also be encoded canonically, which isn't checked
|
||||
// here
|
||||
if ring.len() == 0 {
|
||||
Err(ClsagError::InvalidRing)?;
|
||||
}
|
||||
if ring.len() != self.s.len() {
|
||||
Err(ClsagError::InvalidS)?;
|
||||
}
|
||||
if I.is_identity() {
|
||||
Err(ClsagError::InvalidImage)?;
|
||||
}
|
||||
|
||||
let D = self.D.mul_by_cofactor();
|
||||
if D.is_identity() {
|
||||
Err(ClsagError::InvalidD)?;
|
||||
}
|
||||
|
||||
let (_, c1) = core(ring, I, pseudo_out, msg, &D, &self.s, Mode::Verify(self.c1));
|
||||
if c1 != self.c1 {
|
||||
Err(ClsagError::InvalidC1)?;
|
||||
}
|
||||
|
@ -289,58 +305,4 @@ impl Clsag {
|
|||
pub fn deserialize<R: std::io::Read>(decoys: usize, r: &mut R) -> std::io::Result<Clsag> {
|
||||
Ok(Clsag { s: read_raw_vec(read_scalar, decoys, r)?, c1: read_scalar(r)?, D: read_point(r)? })
|
||||
}
|
||||
|
||||
pub fn verify(
|
||||
&self,
|
||||
ring: &[[EdwardsPoint; 2]],
|
||||
I: &EdwardsPoint,
|
||||
pseudo_out: &EdwardsPoint,
|
||||
msg: &[u8; 32],
|
||||
) -> Result<(), ClsagError> {
|
||||
// Serialize it to pass the struct to Monero without extensive FFI
|
||||
let mut serialized = Vec::with_capacity(1 + ((self.s.len() + 2) * 32));
|
||||
write_varint(&self.s.len().try_into().unwrap(), &mut serialized).unwrap();
|
||||
self.serialize(&mut serialized).unwrap();
|
||||
|
||||
let I_bytes = I.compress().to_bytes();
|
||||
|
||||
let mut ring_bytes = vec![];
|
||||
for member in ring {
|
||||
ring_bytes.extend(&member[0].compress().to_bytes());
|
||||
ring_bytes.extend(&member[1].compress().to_bytes());
|
||||
}
|
||||
|
||||
let pseudo_out_bytes = pseudo_out.compress().to_bytes();
|
||||
|
||||
unsafe {
|
||||
// Uses Monero's C verification function to ensure compatibility with Monero
|
||||
#[link(name = "wrapper")]
|
||||
extern "C" {
|
||||
pub(crate) fn c_verify_clsag(
|
||||
serialized_len: usize,
|
||||
serialized: *const u8,
|
||||
ring_size: u8,
|
||||
ring: *const u8,
|
||||
I: *const u8,
|
||||
pseudo_out: *const u8,
|
||||
msg: *const u8,
|
||||
) -> bool;
|
||||
}
|
||||
|
||||
if c_verify_clsag(
|
||||
serialized.len(),
|
||||
serialized.as_ptr(),
|
||||
u8::try_from(ring.len())
|
||||
.map_err(|_| ClsagError::InternalError("too large ring".to_string()))?,
|
||||
ring_bytes.as_ptr(),
|
||||
I_bytes.as_ptr(),
|
||||
pseudo_out_bytes.as_ptr(),
|
||||
msg.as_ptr(),
|
||||
) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ClsagError::InvalidC1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,23 +7,8 @@ use dalek_ff_group::field::FieldElement;
|
|||
|
||||
use crate::hash;
|
||||
|
||||
pub(crate) fn raw_hash_to_point(mut bytes: [u8; 32]) -> EdwardsPoint {
|
||||
unsafe {
|
||||
#[link(name = "wrapper")]
|
||||
extern "C" {
|
||||
fn c_hash_to_point(key: *const u8);
|
||||
}
|
||||
|
||||
c_hash_to_point(bytes.as_mut_ptr());
|
||||
}
|
||||
CompressedEdwardsY::from_slice(&bytes).decompress().unwrap()
|
||||
}
|
||||
|
||||
// This works without issue. It's also 140 times slower (@ 3.5ms), and despite checking it passes
|
||||
// for all branches, there still could be *some* discrepancy somewhere. There's no reason to use it
|
||||
// unless we're trying to purge that section of the C static library, which we aren't right now
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn rust_hash_to_point(bytes: [u8; 32]) -> EdwardsPoint {
|
||||
pub(crate) fn raw_hash_to_point(bytes: [u8; 32]) -> EdwardsPoint {
|
||||
#[allow(non_snake_case)]
|
||||
let A = FieldElement::from(486662u64);
|
||||
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
use rand::rngs::OsRng;
|
||||
|
||||
use crate::{Commitment, random_scalar, ringct::bulletproofs::Bulletproofs};
|
||||
|
||||
#[test]
|
||||
fn bulletproofs() {
|
||||
// Create Bulletproofs for all possible output quantities
|
||||
for i in 1 .. 17 {
|
||||
let commitments =
|
||||
(1 ..= i).map(|i| Commitment::new(random_scalar(&mut OsRng), i)).collect::<Vec<_>>();
|
||||
|
||||
assert!(Bulletproofs::new(&mut OsRng, &commitments)
|
||||
.unwrap()
|
||||
.verify(&mut OsRng, &commitments.iter().map(Commitment::calculate).collect::<Vec<_>>()));
|
||||
}
|
||||
|
||||
// Check it errors if we try to create too many
|
||||
assert!(
|
||||
Bulletproofs::new(&mut OsRng, &[Commitment::new(random_scalar(&mut OsRng), 1); 17]).is_err()
|
||||
);
|
||||
}
|
|
@ -74,8 +74,6 @@ fn clsag() {
|
|||
)
|
||||
.swap_remove(0);
|
||||
clsag.verify(&ring, &image, &pseudo_out, &msg).unwrap();
|
||||
#[cfg(feature = "experimental")]
|
||||
clsag.rust_verify(&ring, &image, &pseudo_out, &msg).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
use rand::rngs::OsRng;
|
||||
|
||||
use curve25519_dalek::constants::ED25519_BASEPOINT_TABLE;
|
||||
|
||||
use crate::{
|
||||
random_scalar,
|
||||
ringct::hash_to_point::{hash_to_point as c_hash_to_point, rust_hash_to_point},
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn hash_to_point() {
|
||||
for _ in 0 .. 50 {
|
||||
let point = &random_scalar(&mut OsRng) * &ED25519_BASEPOINT_TABLE;
|
||||
assert_eq!(rust_hash_to_point(point.compress().to_bytes()), c_hash_to_point(point));
|
||||
}
|
||||
}
|
|
@ -1,4 +1,2 @@
|
|||
mod hash_to_point;
|
||||
mod clsag;
|
||||
mod bulletproofs;
|
||||
mod address;
|
||||
|
|
Loading…
Reference in a new issue