Skip to content

Commit 61317d2

Browse files
committed
Add method to vkey store to get registered stake distribution for an epoch
1 parent d4686f5 commit 61317d2

File tree

2 files changed

+86
-3
lines changed

2 files changed

+86
-3
lines changed

mithril-aggregator/src/database/provider/signer_registration.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use mithril_common::{
88
crypto_helper::KESPeriod,
99
entities::{
1010
Epoch, HexEncodedOpCert, HexEncodedVerificationKey, HexEncodedVerificationKeySignature,
11-
PartyId, Signer, SignerWithStake, Stake,
11+
PartyId, Signer, SignerWithStake, Stake, StakeDistribution,
1212
},
1313
sqlite::{
1414
EntityCursor, HydrationError, Projection, Provider, SourceAlias, SqLiteEntity,
@@ -458,6 +458,26 @@ impl VerificationKeyStorer for SignerRegistrationStore {
458458

459459
Ok(())
460460
}
461+
462+
async fn get_stake_distribution_for_epoch(
463+
&self,
464+
epoch: Epoch,
465+
) -> Result<Option<StakeDistribution>, StoreError> {
466+
let connection = &*self.connection.lock().await;
467+
let provider = SignerRegistrationRecordProvider::new(connection);
468+
let cursor = provider
469+
.get_by_epoch(&epoch)
470+
.map_err(|e| AdapterError::GeneralError(format!("{e}")))?;
471+
472+
let stake_distribution = StakeDistribution::from_iter(
473+
cursor.map(|r| (r.signer_id, r.stake.unwrap_or_default())),
474+
);
475+
476+
match stake_distribution.is_empty() {
477+
true => Ok(None),
478+
false => Ok(Some(stake_distribution)),
479+
}
480+
}
461481
}
462482

463483
#[cfg(test)]

mithril-aggregator/src/store/verification_key_store.rs

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@ use async_trait::async_trait;
22
use std::collections::HashMap;
33
use tokio::sync::RwLock;
44

5-
use mithril_common::entities::{Epoch, PartyId, Signer, SignerWithStake};
5+
use mithril_common::entities::{Epoch, PartyId, Signer, SignerWithStake, StakeDistribution};
66
use mithril_common::store::{adapter::StoreAdapter, StoreError};
77

88
type Adapter = Box<dyn StoreAdapter<Key = Epoch, Record = HashMap<PartyId, SignerWithStake>>>;
99

1010
/// Store and get signers verification keys for given epoch.
11+
///
12+
/// Important note: This store works on the **recording** epoch, the epoch at which the signers
13+
/// are signed into a certificate so they can sign single signatures at the next epoch.
1114
#[async_trait]
1215
pub trait VerificationKeyStorer: Sync + Send {
1316
/// Save the verification key, for the given [Signer] for the given [Epoch], returns the
@@ -26,6 +29,12 @@ pub trait VerificationKeyStorer: Sync + Send {
2629

2730
/// Prune all verification keys that are at or below the given epoch.
2831
async fn prune_verification_keys(&self, max_epoch_to_prune: Epoch) -> Result<(), StoreError>;
32+
33+
/// Return the parties that are stored at the given epoch.
34+
async fn get_stake_distribution_for_epoch(
35+
&self,
36+
epoch: Epoch,
37+
) -> Result<Option<StakeDistribution>, StoreError>;
2938
}
3039

3140
/// Store for the `VerificationKey`.
@@ -85,6 +94,19 @@ impl VerificationKeyStorer for VerificationKeyStore {
8594

8695
Ok(())
8796
}
97+
98+
async fn get_stake_distribution_for_epoch(
99+
&self,
100+
epoch: Epoch,
101+
) -> Result<Option<StakeDistribution>, StoreError> {
102+
let record = self.adapter.read().await.get_record(&epoch).await?;
103+
Ok(record.map(|r| {
104+
StakeDistribution::from_iter(
105+
r.into_iter()
106+
.map(|(party_id, signer)| (party_id, signer.stake)),
107+
)
108+
}))
109+
}
88110
}
89111

90112
/// Macro that generate tests that a [VerificationKeyStorer] must pass
@@ -110,11 +132,21 @@ macro_rules! test_verification_key_storer {
110132
test_suite::get_verification_keys_for_empty_epoch(&$store_builder).await;
111133
}
112134

135+
#[tokio::test]
136+
async fn get_stake_distribution_for_empty_epoch() {
137+
test_suite::get_stake_distribution_for_empty_epoch(&$store_builder).await;
138+
}
139+
113140
#[tokio::test]
114141
async fn get_verification_keys_for_existing_epoch() {
115142
test_suite::get_verification_keys_for_existing_epoch(&$store_builder).await;
116143
}
117144

145+
#[tokio::test]
146+
async fn get_stake_distribution_for_existing_epoch() {
147+
test_suite::get_stake_distribution_for_existing_epoch(&$store_builder).await;
148+
}
149+
118150
#[tokio::test]
119151
async fn can_prune_keys_from_given_epoch_retention_limit() {
120152
test_suite::can_prune_keys_from_given_epoch_retention_limit(&$store_builder).await;
@@ -129,7 +161,7 @@ pub(crate) use test_verification_key_storer;
129161
#[macro_use]
130162
#[cfg(test)]
131163
pub mod test_suite {
132-
use mithril_common::entities::{Epoch, PartyId, Signer, SignerWithStake};
164+
use mithril_common::entities::{Epoch, PartyId, Signer, SignerWithStake, StakeDistribution};
133165
use mithril_common::test_utils::fake_keys;
134166
use std::collections::{BTreeMap, HashMap};
135167
use std::sync::Arc;
@@ -233,6 +265,17 @@ pub mod test_suite {
233265
assert!(res.is_none());
234266
}
235267

268+
pub async fn get_stake_distribution_for_empty_epoch(store_builder: &StoreBuilder) {
269+
let signers = build_signers(2, 1);
270+
let store = store_builder(signers);
271+
let res = store
272+
.get_stake_distribution_for_epoch(Epoch(0))
273+
.await
274+
.unwrap();
275+
276+
assert!(res.is_none());
277+
}
278+
236279
pub async fn get_verification_keys_for_existing_epoch(store_builder: &StoreBuilder) {
237280
let signers = build_signers(2, 2);
238281
let store = store_builder(signers.clone());
@@ -253,6 +296,26 @@ pub mod test_suite {
253296
assert_eq!(expected_signers, res);
254297
}
255298

299+
pub async fn get_stake_distribution_for_existing_epoch(store_builder: &StoreBuilder) {
300+
let signers = build_signers(2, 2);
301+
let store = store_builder(signers.clone());
302+
303+
let expected: Option<StakeDistribution> = signers
304+
.into_iter()
305+
.filter(|(e, _)| e == 1)
306+
.map(|(_, signers)| {
307+
StakeDistribution::from_iter(signers.into_iter().map(|(p, s)| (p, s.stake)))
308+
})
309+
.next();
310+
let res = store
311+
.get_stake_distribution_for_epoch(Epoch(1))
312+
.await
313+
.unwrap()
314+
.map(|x| BTreeMap::from_iter(x.into_iter()));
315+
316+
assert_eq!(expected, res);
317+
}
318+
256319
pub async fn can_prune_keys_from_given_epoch_retention_limit(store_builder: &StoreBuilder) {
257320
let signers = build_signers(6, 2);
258321
let store = store_builder(signers);

0 commit comments

Comments
 (0)