diff --git a/Cargo.lock b/Cargo.lock index a653340c7f93..de4d3e986333 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -741,6 +741,7 @@ checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" name = "beacon" version = "0.1.0" dependencies = [ + "ahash 0.4.4", "async-std", "async-trait", "base64 0.12.3", diff --git a/blockchain/beacon/Cargo.toml b/blockchain/beacon/Cargo.toml index 1a9e242a1921..dba4f833f5d1 100644 --- a/blockchain/beacon/Cargo.toml +++ b/blockchain/beacon/Cargo.toml @@ -8,6 +8,8 @@ edition = "2018" features = ["json"] [dependencies] +ahash = "0.4" +async-std = { version = "1.6.0", features = ["unstable"] } grpc = "0.8" grpc-protobuf = "0.8" protobuf = "2.14.0" diff --git a/blockchain/beacon/src/drand.rs b/blockchain/beacon/src/drand.rs index e8183bd3c391..a8b48931910f 100644 --- a/blockchain/beacon/src/drand.rs +++ b/blockchain/beacon/src/drand.rs @@ -7,6 +7,8 @@ use super::drand_api::api_grpc::PublicClient; use super::drand_api::common::GroupRequest; use super::group::Group; +use ahash::AHashMap; +use async_std::sync::RwLock; use async_trait::async_trait; use bls_signatures::{PublicKey, Serialize, Signature}; use byteorder::{BigEndian, WriteBytesExt}; @@ -39,7 +41,7 @@ where Self: Sized, { /// Verify a new beacon entry against the most recent one before it. - fn verify_entry( + async fn verify_entry( &self, curr: &BeaconEntry, prev: &BeaconEntry, @@ -59,6 +61,9 @@ pub struct DrandBeacon { drand_gen_time: u64, fil_gen_time: u64, fil_round_time: u64, + + /// Keeps track of computed beacon entries. + local_cache: RwLock>, } impl DrandBeacon { @@ -95,6 +100,7 @@ impl DrandBeacon { drand_gen_time: group.genesis_time, fil_round_time: interval, fil_gen_time: genesis_ts, + local_cache: Default::default(), }) } } @@ -102,7 +108,7 @@ impl DrandBeacon { /// Use this to source randomness and to verify Drand beacon entries. #[async_trait] impl Beacon for DrandBeacon { - fn verify_entry( + async fn verify_entry( &self, curr: &BeaconEntry, prev: &BeaconEntry, @@ -123,21 +129,32 @@ impl Beacon for DrandBeacon { // Signature let sig = Signature::from_bytes(curr.data())?; let sig_match = bls_signatures::verify(&sig, &[digest], &[self.pub_key.key()]); - // TODO: Cache this result + + // Cache the result + if sig_match && !self.local_cache.read().await.contains_key(&curr.round()) { + self.local_cache + .write() + .await + .insert(curr.round(), curr.clone()); + } Ok(sig_match) } async fn entry(&self, round: u64) -> Result> { - // TODO: Cache values into a database - let mut req = PublicRandRequest::new(); - req.round = round; - let resp = self - .client - .public_rand(grpc::RequestOptions::new(), req) - .drop_metadata() - .await?; + match self.local_cache.read().await.get(&round) { + Some(cached_entry) => Ok(cached_entry.clone()), + None => { + let mut req = PublicRandRequest::new(); + req.round = round; + let resp = self + .client + .public_rand(grpc::RequestOptions::new(), req) + .drop_metadata() + .await?; - Ok(BeaconEntry::new(resp.round, resp.signature)) + Ok(BeaconEntry::new(resp.round, resp.signature)) + } + } } fn max_beacon_round_for_epoch(&self, fil_epoch: ChainEpoch) -> u64 { diff --git a/blockchain/beacon/src/mock_beacon.rs b/blockchain/beacon/src/mock_beacon.rs index 8349eda9fbac..9a42cd93e926 100644 --- a/blockchain/beacon/src/mock_beacon.rs +++ b/blockchain/beacon/src/mock_beacon.rs @@ -29,7 +29,11 @@ impl MockBeacon { #[async_trait] impl Beacon for MockBeacon { - fn verify_entry(&self, curr: &BeaconEntry, prev: &BeaconEntry) -> Result> { + async fn verify_entry( + &self, + curr: &BeaconEntry, + prev: &BeaconEntry, + ) -> Result> { let oe = Self::entry_for_index(prev.round()); Ok(oe.data() == curr.data()) } diff --git a/blockchain/beacon/tests/drand.rs b/blockchain/beacon/tests/drand.rs index d440e65012bf..9c7915c4e4ee 100644 --- a/blockchain/beacon/tests/drand.rs +++ b/blockchain/beacon/tests/drand.rs @@ -30,7 +30,7 @@ async fn ask_and_verify_beacon_entry() { let e2 = beacon.entry(2).await.unwrap(); let e3 = beacon.entry(3).await.unwrap(); - assert!(beacon.verify_entry(&e3, &e2).unwrap()); + assert!(beacon.verify_entry(&e3, &e2).await.unwrap()); } #[ignore] @@ -40,5 +40,5 @@ async fn ask_and_verify_beacon_entry_fail() { let e2 = beacon.entry(2).await.unwrap(); let e3 = beacon.entry(3).await.unwrap(); - assert!(!beacon.verify_entry(&e2, &e3).unwrap()); + assert!(!beacon.verify_entry(&e2, &e3).await.unwrap()); } diff --git a/blockchain/blocks/src/header/mod.rs b/blockchain/blocks/src/header/mod.rs index 83eb63d6ce6c..1ed6f4fb205a 100644 --- a/blockchain/blocks/src/header/mod.rs +++ b/blockchain/blocks/src/header/mod.rs @@ -390,21 +390,21 @@ impl BlockHeader { last.round() ))); } - self.beacon_entries.iter().try_fold( - &prev_entry, - |prev, curr| -> Result<&BeaconEntry, Error> { - if !beacon - .verify_entry(curr, &prev) - .map_err(|e| Error::Validation(e.to_string()))? - { - return Err(Error::Validation(format!( - "beacon entry was invalid: curr:{:?}, prev: {:?}", - curr, prev - ))); - } - Ok(curr) - }, - )?; + + let mut prev = &prev_entry; + for curr in &self.beacon_entries { + if !beacon + .verify_entry(&curr, &prev) + .await + .map_err(|e| Error::Validation(e.to_string()))? + { + return Err(Error::Validation(format!( + "beacon entry was invalid: curr:{:?}, prev: {:?}", + curr, prev + ))); + } + prev = &curr; + } Ok(()) } }