parent
445e6668c2
commit
d5c889d6b0
|
@ -4678,6 +4678,7 @@ name = "solana-perf"
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bincode",
|
"bincode",
|
||||||
|
"curve25519-dalek 2.0.0",
|
||||||
"dlopen",
|
"dlopen",
|
||||||
"dlopen_derive",
|
"dlopen_derive",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
PERF_LIBS_VERSION=v0.18.0
|
PERF_LIBS_VERSION=v0.18.1
|
||||||
VERSION=$PERF_LIBS_VERSION-1
|
VERSION=$PERF_LIBS_VERSION-1
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
|
@ -22,6 +22,7 @@ solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "1.2.0" }
|
||||||
solana-budget-program = { path = "../programs/budget", version = "1.2.0" }
|
solana-budget-program = { path = "../programs/budget", version = "1.2.0" }
|
||||||
solana-logger = { path = "../logger", version = "1.2.0" }
|
solana-logger = { path = "../logger", version = "1.2.0" }
|
||||||
solana-metrics = { path = "../metrics", version = "1.2.0" }
|
solana-metrics = { path = "../metrics", version = "1.2.0" }
|
||||||
|
curve25519-dalek = { version = "2" }
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "solana_perf"
|
name = "solana_perf"
|
||||||
|
|
|
@ -91,6 +91,12 @@ pub struct Api<'a> {
|
||||||
Symbol<'a, unsafe extern "C" fn(ptr: *mut c_void, size: usize, flags: c_uint) -> c_int>,
|
Symbol<'a, unsafe extern "C" fn(ptr: *mut c_void, size: usize, flags: c_uint) -> c_int>,
|
||||||
|
|
||||||
pub cuda_host_unregister: Symbol<'a, unsafe extern "C" fn(ptr: *mut c_void) -> c_int>,
|
pub cuda_host_unregister: Symbol<'a, unsafe extern "C" fn(ptr: *mut c_void) -> c_int>,
|
||||||
|
|
||||||
|
pub ed25519_get_checked_scalar:
|
||||||
|
Symbol<'a, unsafe extern "C" fn(out_scalar: *mut u8, in_scalar: *const u8) -> c_int>,
|
||||||
|
|
||||||
|
pub ed25519_check_packed_ge_small_order:
|
||||||
|
Symbol<'a, unsafe extern "C" fn(packed_ge: *const u8) -> c_int>,
|
||||||
}
|
}
|
||||||
|
|
||||||
static mut API: Option<Container<Api>> = None;
|
static mut API: Option<Container<Api>> = None;
|
||||||
|
|
|
@ -293,6 +293,34 @@ pub fn copy_return_values(sig_lens: &[Vec<u32>], out: &PinnedVec<u8>, rvs: &mut
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// return true for success, i.e ge unpacks and !ge.is_small_order()
|
||||||
|
pub fn check_packed_ge_small_order(ge: &[u8; 32]) -> bool {
|
||||||
|
if let Some(api) = perf_libs::api() {
|
||||||
|
unsafe {
|
||||||
|
// Returns 1 == fail, 0 == success
|
||||||
|
let res = (api.ed25519_check_packed_ge_small_order)(ge.as_ptr());
|
||||||
|
|
||||||
|
return res == 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_checked_scalar(scalar: &[u8; 32]) -> Result<[u8; 32], PacketError> {
|
||||||
|
let mut out = [0u8; 32];
|
||||||
|
if let Some(api) = perf_libs::api() {
|
||||||
|
unsafe {
|
||||||
|
let res = (api.ed25519_get_checked_scalar)(out.as_mut_ptr(), scalar.as_ptr());
|
||||||
|
if res == 0 {
|
||||||
|
return Ok(out);
|
||||||
|
} else {
|
||||||
|
return Err(PacketError::InvalidLen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(out)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn ed25519_verify(
|
pub fn ed25519_verify(
|
||||||
batches: &[Packets],
|
batches: &[Packets],
|
||||||
recycler: &Recycler<TxOffset>,
|
recycler: &Recycler<TxOffset>,
|
||||||
|
@ -312,7 +340,7 @@ pub fn ed25519_verify(
|
||||||
// power-of-two number around that accounting for the fact that the CPU
|
// power-of-two number around that accounting for the fact that the CPU
|
||||||
// may be busy doing other things while being a real validator
|
// may be busy doing other things while being a real validator
|
||||||
// TODO: dynamically adjust this crossover
|
// TODO: dynamically adjust this crossover
|
||||||
if count < std::usize::MAX {
|
if count < 64 {
|
||||||
return ed25519_verify_cpu(batches);
|
return ed25519_verify_cpu(batches);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -721,8 +749,131 @@ mod tests {
|
||||||
assert_eq!(ans, ref_vec);
|
assert_eq!(ans, ref_vec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_verify_fuzz() {
|
||||||
|
use rand::{thread_rng, Rng};
|
||||||
|
solana_logger::setup();
|
||||||
|
|
||||||
|
let tx = test_multisig_tx();
|
||||||
|
let packet = sigverify::make_packet_from_transaction(tx);
|
||||||
|
|
||||||
|
let recycler = Recycler::default();
|
||||||
|
let recycler_out = Recycler::default();
|
||||||
|
for _ in 0..50 {
|
||||||
|
let n = thread_rng().gen_range(1, 30);
|
||||||
|
let num_batches = thread_rng().gen_range(2, 30);
|
||||||
|
let mut batches = generate_packet_vec(&packet, n, num_batches);
|
||||||
|
|
||||||
|
let num_modifications = thread_rng().gen_range(0, 5);
|
||||||
|
for _ in 0..num_modifications {
|
||||||
|
let batch = thread_rng().gen_range(0, batches.len());
|
||||||
|
let packet = thread_rng().gen_range(0, batches[batch].packets.len());
|
||||||
|
let offset = thread_rng().gen_range(0, batches[batch].packets[packet].meta.size);
|
||||||
|
let add = thread_rng().gen_range(0, 255);
|
||||||
|
batches[batch].packets[packet].data[offset] =
|
||||||
|
batches[batch].packets[packet].data[offset].wrapping_add(add);
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify packets
|
||||||
|
let ans = sigverify::ed25519_verify(&batches, &recycler, &recycler_out);
|
||||||
|
|
||||||
|
let cpu_ref = ed25519_verify_cpu(&batches);
|
||||||
|
|
||||||
|
debug!("ans: {:?} ref: {:?}", ans, cpu_ref);
|
||||||
|
// check result
|
||||||
|
assert_eq!(ans, cpu_ref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_verify_fail() {
|
fn test_verify_fail() {
|
||||||
test_verify_n(5, true);
|
test_verify_n(5, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_checked_scalar() {
|
||||||
|
solana_logger::setup();
|
||||||
|
use curve25519_dalek::scalar::Scalar;
|
||||||
|
use rand::{thread_rng, Rng};
|
||||||
|
use rayon::prelude::*;
|
||||||
|
use std::sync::atomic::{AtomicU64, Ordering};
|
||||||
|
|
||||||
|
if perf_libs::api().is_none() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let passed_g = AtomicU64::new(0);
|
||||||
|
let failed_g = AtomicU64::new(0);
|
||||||
|
(0..4).into_par_iter().for_each(|_| {
|
||||||
|
let mut input = [0u8; 32];
|
||||||
|
let mut passed = 0;
|
||||||
|
let mut failed = 0;
|
||||||
|
for _ in 0..1_000_000 {
|
||||||
|
thread_rng().fill(&mut input);
|
||||||
|
let ans = get_checked_scalar(&input);
|
||||||
|
let ref_ans = Scalar::from_canonical_bytes(input);
|
||||||
|
if let Some(ref_ans) = ref_ans {
|
||||||
|
passed += 1;
|
||||||
|
assert_eq!(ans.unwrap(), ref_ans.to_bytes());
|
||||||
|
} else {
|
||||||
|
failed += 1;
|
||||||
|
assert!(ans.is_err());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
passed_g.fetch_add(passed, Ordering::Relaxed);
|
||||||
|
failed_g.fetch_add(failed, Ordering::Relaxed);
|
||||||
|
});
|
||||||
|
info!(
|
||||||
|
"passed: {} failed: {}",
|
||||||
|
passed_g.load(Ordering::Relaxed),
|
||||||
|
failed_g.load(Ordering::Relaxed)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ge_small_order() {
|
||||||
|
solana_logger::setup();
|
||||||
|
use curve25519_dalek::edwards::CompressedEdwardsY;
|
||||||
|
use rand::{thread_rng, Rng};
|
||||||
|
use rayon::prelude::*;
|
||||||
|
use std::sync::atomic::{AtomicU64, Ordering};
|
||||||
|
|
||||||
|
if perf_libs::api().is_none() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let passed_g = AtomicU64::new(0);
|
||||||
|
let failed_g = AtomicU64::new(0);
|
||||||
|
(0..4).into_par_iter().for_each(|_| {
|
||||||
|
let mut input = [0u8; 32];
|
||||||
|
let mut passed = 0;
|
||||||
|
let mut failed = 0;
|
||||||
|
for _ in 0..1_000_000 {
|
||||||
|
thread_rng().fill(&mut input);
|
||||||
|
let ans = check_packed_ge_small_order(&input);
|
||||||
|
let ref_ge = CompressedEdwardsY::from_slice(&input);
|
||||||
|
if let Some(ref_element) = ref_ge.decompress() {
|
||||||
|
if ref_element.is_small_order() {
|
||||||
|
assert!(!ans);
|
||||||
|
} else {
|
||||||
|
assert!(ans);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
assert!(!ans);
|
||||||
|
}
|
||||||
|
if ans {
|
||||||
|
passed += 1;
|
||||||
|
} else {
|
||||||
|
failed += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
passed_g.fetch_add(passed, Ordering::Relaxed);
|
||||||
|
failed_g.fetch_add(failed, Ordering::Relaxed);
|
||||||
|
});
|
||||||
|
info!(
|
||||||
|
"passed: {} failed: {}",
|
||||||
|
passed_g.load(Ordering::Relaxed),
|
||||||
|
failed_g.load(Ordering::Relaxed)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue