Re-enable gpu sigverify (#9870)

Add sigverify fuzz and scalar test
This commit is contained in:
sakridge 2020-05-06 15:44:55 -07:00 committed by GitHub
parent 445e6668c2
commit d5c889d6b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 161 additions and 2 deletions

1
Cargo.lock generated
View File

@ -4678,6 +4678,7 @@ name = "solana-perf"
version = "1.2.0"
dependencies = [
"bincode",
"curve25519-dalek 2.0.0",
"dlopen",
"dlopen_derive",
"lazy_static",

View File

@ -1,6 +1,6 @@
#!/usr/bin/env bash
PERF_LIBS_VERSION=v0.18.0
PERF_LIBS_VERSION=v0.18.1
VERSION=$PERF_LIBS_VERSION-1
set -e

View File

@ -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-logger = { path = "../logger", version = "1.2.0" }
solana-metrics = { path = "../metrics", version = "1.2.0" }
curve25519-dalek = { version = "2" }
[lib]
name = "solana_perf"

View File

@ -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>,
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;

View File

@ -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(
batches: &[Packets],
recycler: &Recycler<TxOffset>,
@ -312,7 +340,7 @@ pub fn ed25519_verify(
// power-of-two number around that accounting for the fact that the CPU
// may be busy doing other things while being a real validator
// TODO: dynamically adjust this crossover
if count < std::usize::MAX {
if count < 64 {
return ed25519_verify_cpu(batches);
}
@ -721,8 +749,131 @@ mod tests {
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]
fn test_verify_fail() {
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)
);
}
}