consensus: add a static redjubjub::VERIFIER with fallback
This commit is contained in:
parent
8d4f154c9f
commit
2efb4eb262
|
@ -2872,6 +2872,8 @@ dependencies = [
|
||||||
"tokio",
|
"tokio",
|
||||||
"tower",
|
"tower",
|
||||||
"tower-batch",
|
"tower-batch",
|
||||||
|
"tower-fallback",
|
||||||
|
"tower-util",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-error",
|
"tracing-error",
|
||||||
"tracing-futures",
|
"tracing-futures",
|
||||||
|
|
|
@ -8,7 +8,6 @@ edition = "2018"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chrono = "0.4.13"
|
chrono = "0.4.13"
|
||||||
color-eyre = "0.5"
|
color-eyre = "0.5"
|
||||||
once_cell = "1.4"
|
|
||||||
rand = "0.7"
|
rand = "0.7"
|
||||||
redjubjub = "0.2"
|
redjubjub = "0.2"
|
||||||
|
|
||||||
|
@ -17,9 +16,12 @@ futures = "0.3.5"
|
||||||
futures-util = "0.3.5"
|
futures-util = "0.3.5"
|
||||||
tokio = { version = "0.2.22", features = ["time", "sync", "stream", "tracing"] }
|
tokio = { version = "0.2.22", features = ["time", "sync", "stream", "tracing"] }
|
||||||
tower = "0.3"
|
tower = "0.3"
|
||||||
|
tower-util = "0.3"
|
||||||
tracing = "0.1.18"
|
tracing = "0.1.18"
|
||||||
tracing-futures = "0.2.4"
|
tracing-futures = "0.2.4"
|
||||||
|
once_cell = "1.4"
|
||||||
|
|
||||||
|
tower-fallback = { path = "../tower-fallback/" }
|
||||||
tower-batch = { path = "../tower-batch/" }
|
tower-batch = { path = "../tower-batch/" }
|
||||||
zebra-chain = { path = "../zebra-chain" }
|
zebra-chain = { path = "../zebra-chain" }
|
||||||
zebra-state = { path = "../zebra-state" }
|
zebra-state = { path = "../zebra-state" }
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
//! Asynchronous verification of cryptographic primitives.
|
//! Asynchronous verification of cryptographic primitives.
|
||||||
|
|
||||||
pub mod redjubjub;
|
pub mod redjubjub;
|
||||||
|
|
||||||
|
/// The maximum batch size for any of the batch verifiers.
|
||||||
|
const MAX_BATCH_SIZE: usize = 64;
|
||||||
|
/// The maximum latency bound for any of the batch verifiers.
|
||||||
|
const MAX_BATCH_LATENCY: std::time::Duration = std::time::Duration::from_millis(100);
|
||||||
|
|
|
@ -10,11 +10,44 @@ use std::{
|
||||||
task::{Context, Poll},
|
task::{Context, Poll},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use futures::future::{ready, Ready};
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
use rand::thread_rng;
|
use rand::thread_rng;
|
||||||
use redjubjub::{batch, *};
|
use redjubjub::{batch, *};
|
||||||
use tokio::sync::broadcast::{channel, RecvError, Sender};
|
use tokio::sync::broadcast::{channel, RecvError, Sender};
|
||||||
use tower::Service;
|
use tower::Service;
|
||||||
use tower_batch::BatchControl;
|
use tower_batch::{Batch, BatchControl};
|
||||||
|
use tower_fallback::Fallback;
|
||||||
|
use tower_util::ServiceFn;
|
||||||
|
|
||||||
|
/// Global batch verification context for RedJubjub signatures.
|
||||||
|
///
|
||||||
|
/// This service transparently batches contemporaneous signature verifications,
|
||||||
|
/// handling batch failures by falling back to individual verification.
|
||||||
|
///
|
||||||
|
/// Note that making a `Service` call requires mutable access to the service, so
|
||||||
|
/// you should call `.clone()` on the global handle to create a local, mutable
|
||||||
|
/// handle.
|
||||||
|
pub static VERIFIER: Lazy<
|
||||||
|
Fallback<Batch<Verifier, Item>, ServiceFn<fn(Item) -> Ready<Result<(), Error>>>>,
|
||||||
|
> = Lazy::new(|| {
|
||||||
|
Fallback::new(
|
||||||
|
Batch::new(
|
||||||
|
Verifier::default(),
|
||||||
|
super::MAX_BATCH_SIZE,
|
||||||
|
super::MAX_BATCH_LATENCY,
|
||||||
|
),
|
||||||
|
// We want to fallback to individual verification if batch verification
|
||||||
|
// fails, so we need a Service to use. The obvious way to do this would
|
||||||
|
// be to write a closure that returns an async block. But because we
|
||||||
|
// have to specify the type of a static, we need to be able to write the
|
||||||
|
// type of the closure and its return value, and both closures and async
|
||||||
|
// blocks have eldritch types whose names cannot be written. So instead,
|
||||||
|
// we use a Ready to avoid an async block and cast the closure to a
|
||||||
|
// function (which is possible because it doesn't capture any state).
|
||||||
|
tower::service_fn((|item: Item| ready(item.verify_single())) as fn(_) -> _),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
/// RedJubjub signature verifier service
|
/// RedJubjub signature verifier service
|
||||||
pub struct Verifier {
|
pub struct Verifier {
|
||||||
|
@ -26,10 +59,8 @@ pub struct Verifier {
|
||||||
tx: Sender<Result<(), Error>>,
|
tx: Sender<Result<(), Error>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::new_without_default)]
|
impl Default for Verifier {
|
||||||
impl Verifier {
|
fn default() -> Self {
|
||||||
/// Create a new RedJubjubVerifier instance
|
|
||||||
pub fn new() -> Self {
|
|
||||||
let batch = batch::Verifier::default();
|
let batch = batch::Verifier::default();
|
||||||
// XXX(hdevalence) what's a reasonable choice here?
|
// XXX(hdevalence) what's a reasonable choice here?
|
||||||
let (tx, _) = channel(10);
|
let (tx, _) = channel(10);
|
||||||
|
|
|
@ -56,7 +56,7 @@ async fn batch_flushes_on_max_items() -> Result<()> {
|
||||||
|
|
||||||
// Use a very long max_latency and a short timeout to check that
|
// Use a very long max_latency and a short timeout to check that
|
||||||
// flushing is happening based on hitting max_items.
|
// flushing is happening based on hitting max_items.
|
||||||
let verifier = Batch::new(Verifier::new(), 10, Duration::from_secs(1000));
|
let verifier = Batch::new(Verifier::default(), 10, Duration::from_secs(1000));
|
||||||
timeout(Duration::from_secs(5), sign_and_verify(verifier, 100))
|
timeout(Duration::from_secs(5), sign_and_verify(verifier, 100))
|
||||||
.await?
|
.await?
|
||||||
.map_err(|e| eyre!(e))?;
|
.map_err(|e| eyre!(e))?;
|
||||||
|
@ -75,7 +75,7 @@ async fn batch_flushes_on_max_latency() -> Result<()> {
|
||||||
|
|
||||||
// Use a very high max_items and a short timeout to check that
|
// Use a very high max_items and a short timeout to check that
|
||||||
// flushing is happening based on hitting max_latency.
|
// flushing is happening based on hitting max_latency.
|
||||||
let verifier = Batch::new(Verifier::new(), 100, Duration::from_millis(500));
|
let verifier = Batch::new(Verifier::default(), 100, Duration::from_millis(500));
|
||||||
timeout(Duration::from_secs(5), sign_and_verify(verifier, 10))
|
timeout(Duration::from_secs(5), sign_and_verify(verifier, 10))
|
||||||
.await?
|
.await?
|
||||||
.map_err(|e| eyre!(e))?;
|
.map_err(|e| eyre!(e))?;
|
||||||
|
|
Loading…
Reference in New Issue