Skip to content

Commit 50e90f0

Browse files
committed
feat: introduce StakeAddressDiffsMessage and StakeAddressDiff structures for tracking stake address changes
- Added StakeAddressDiffsMessage to encapsulate a vector of StakeAddressDiffs. - Implemented StakeAddressDiff to represent individual stake address changes, including registration, deregistration, and delegation. - Updated AccountsState to handle and publish stake address diffs during state transitions. - Refactored state handling methods to return StakeAddressDiffs for various operations, enhancing tracking of stake address changes.
1 parent 8d75647 commit 50e90f0

File tree

7 files changed

+297
-189
lines changed

7 files changed

+297
-189
lines changed

common/src/messages.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,14 @@ pub struct StakeAddressDeltasMessage {
102102
pub deltas: Vec<StakeAddressDelta>,
103103
}
104104

105+
/// Stake address Diffs message
106+
/// How stake address changes
107+
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
108+
pub struct StakeAddressDiffsMessage {
109+
/// Stake Address Changes
110+
pub diffs: Vec<StakeAddressDiff>,
111+
}
112+
105113
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
106114
pub struct BlockFeesMessage {
107115
/// Total fees
@@ -237,6 +245,7 @@ pub enum CardanoMessage {
237245
SPOStakeDistribution(SPOStakeDistributionMessage), // SPO delegation distribution (SPDD)
238246
SPORewards(SPORewardsMessage), // SPO rewards distribution (SPRD)
239247
StakeAddressDeltas(StakeAddressDeltasMessage), // Stake part of address deltas
248+
StakeAddressDiffs(StakeAddressDiffsMessage), // Stake Address Diffs
240249
}
241250

242251
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]

common/src/types.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,23 @@ pub struct StakeAddressDelta {
140140
pub delta: i64,
141141
}
142142

143+
/// Stake Address State Change
144+
#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
145+
pub struct StakeAddressDiff {
146+
pub hash: KeyHash,
147+
pub change: StakeAddressChange,
148+
}
149+
150+
/// Stake Address State Change
151+
#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
152+
pub enum StakeAddressChange {
153+
StakeAddressRegistered,
154+
StakeAddressDeregistered,
155+
DelegatedToSPO(KeyHash),
156+
DelegatedToDRep(DRepChoice),
157+
ValueDiff(LovelaceDelta),
158+
}
159+
143160
/// Value (lovelace + multiasset)
144161
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
145162
pub struct Value {
@@ -698,7 +715,7 @@ pub struct Deregistration {
698715
}
699716

700717
/// DRepChoice (=CDDL drep, badly named)
701-
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
718+
#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
702719
pub enum DRepChoice {
703720
/// Address key
704721
Key(KeyHash),

modules/accounts_state/src/accounts_state.rs

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use acropolis_common::{
66
queries::accounts::{PoolsLiveStakes, DEFAULT_ACCOUNTS_QUERY_TOPIC},
77
rest_helper::handle_rest,
88
state_history::{StateHistory, StateHistoryStore},
9-
BlockInfo, BlockStatus,
9+
BlockInfo, BlockStatus, StakeAddressDiff,
1010
};
1111
use anyhow::Result;
1212
use caryatid_sdk::{message_bus::Subscription, module, Context, Module};
@@ -21,7 +21,9 @@ mod spo_distribution_publisher;
2121
use spo_distribution_publisher::SPODistributionPublisher;
2222
mod spo_rewards_publisher;
2323
use spo_rewards_publisher::SPORewardsPublisher;
24+
mod stake_diffs_publisher;
2425
mod state;
26+
use stake_diffs_publisher::StakeDiffsPublisher;
2527
use state::State;
2628
mod monetary;
2729
mod rest;
@@ -43,6 +45,7 @@ const DEFAULT_DREP_DISTRIBUTION_TOPIC: &str = "cardano.drep.distribution";
4345
const DEFAULT_SPO_DISTRIBUTION_TOPIC: &str = "cardano.spo.distribution";
4446
const DEFAULT_SPO_REWARDS_TOPIC: &str = "cardano.spo.rewards";
4547
const DEFAULT_PROTOCOL_PARAMETERS_TOPIC: &str = "cardano.protocol.parameters";
48+
const DEFAULT_STAKE_DIFFS_TOPIC: &str = "cardano.stake.diffs";
4649

4750
const DEFAULT_HANDLE_POTS_TOPIC: (&str, &str) = ("handle-topic-pots", "rest.get.pots");
4851

@@ -61,6 +64,7 @@ impl AccountsState {
6164
mut drep_publisher: DRepDistributionPublisher,
6265
mut spo_publisher: SPODistributionPublisher,
6366
mut spo_rewards_publisher: SPORewardsPublisher,
67+
mut stake_diffs_publisher: StakeDiffsPublisher,
6468
mut spos_subscription: Box<dyn Subscription<Message>>,
6569
mut ea_subscription: Box<dyn Subscription<Message>>,
6670
mut certs_subscription: Box<dyn Subscription<Message>>,
@@ -113,6 +117,7 @@ impl AccountsState {
113117
let stake_message_f = stake_subscription.read();
114118
let withdrawals_message_f = withdrawals_subscription.read();
115119
let mut current_block: Option<BlockInfo> = None;
120+
let mut stake_diffs = Vec::<StakeAddressDiff>::new();
116121

117122
// Use certs_message as the synchroniser, but we have to handle it after the
118123
// epoch things, because they apply to the new epoch, not the last
@@ -202,13 +207,14 @@ impl AccountsState {
202207
.inspect_err(|e| error!("EpochActivity handling error: {e:#}"))
203208
.ok();
204209
// SPO rewards is for previous epoch
205-
if let Some(spo_rewards) = spo_rewards {
210+
if let Some((spo_rewards, diffs)) = spo_rewards {
206211
if let Err(e) = spo_rewards_publisher
207212
.publish_spo_rewards(block_info, spo_rewards)
208213
.await
209214
{
210215
error!("Error publishing SPO rewards: {e:#}")
211216
}
217+
stake_diffs.extend(diffs);
212218
}
213219
}
214220
.instrument(span)
@@ -258,10 +264,13 @@ impl AccountsState {
258264
let span = info_span!("account_state.handle_certs", block = block_info.number);
259265
async {
260266
Self::check_sync(&current_block, &block_info);
261-
state
267+
let diffs = state
262268
.handle_tx_certificates(tx_certs_msg)
263269
.inspect_err(|e| error!("TxCertificates handling error: {e:#}"))
264270
.ok();
271+
if let Some(diffs) = diffs {
272+
stake_diffs.extend(diffs);
273+
}
265274
}
266275
.instrument(span)
267276
.await;
@@ -280,10 +289,13 @@ impl AccountsState {
280289
);
281290
async {
282291
Self::check_sync(&current_block, &block_info);
283-
state
292+
let diffs = state
284293
.handle_withdrawals(withdrawals_msg)
285294
.inspect_err(|e| error!("Withdrawals handling error: {e:#}"))
286295
.ok();
296+
if let Some(diffs) = diffs {
297+
stake_diffs.extend(diffs);
298+
}
287299
}
288300
.instrument(span)
289301
.await;
@@ -302,10 +314,13 @@ impl AccountsState {
302314
);
303315
async {
304316
Self::check_sync(&current_block, &block_info);
305-
state
317+
let diffs = state
306318
.handle_stake_deltas(deltas_msg)
307319
.inspect_err(|e| error!("StakeAddressDeltas handling error: {e:#}"))
308320
.ok();
321+
if let Some(diffs) = diffs {
322+
stake_diffs.extend(diffs);
323+
}
309324
}
310325
.instrument(span)
311326
.await;
@@ -316,6 +331,13 @@ impl AccountsState {
316331

317332
// Commit the new state
318333
if let Some(block_info) = current_block {
334+
// publish stake address diffs message
335+
if let Err(e) =
336+
stake_diffs_publisher.publish_stake_diffs(&block_info, stake_diffs).await
337+
{
338+
error!("Error publishing stake diffs: {e:#}")
339+
}
340+
319341
history.lock().await.commit(block_info.number, state);
320342
}
321343
}
@@ -387,6 +409,11 @@ impl AccountsState {
387409
.get_string("publish-spo-rewards-topic")
388410
.unwrap_or(DEFAULT_SPO_REWARDS_TOPIC.to_string());
389411

412+
let stake_diffs_topic = config
413+
.get_string("publish-stake-diffs-topic")
414+
.unwrap_or(DEFAULT_STAKE_DIFFS_TOPIC.to_string());
415+
info!("Creating stake diffs subscriber on '{stake_diffs_topic}'");
416+
390417
// REST handler topics
391418
let handle_pots_topic = config
392419
.get_string(DEFAULT_HANDLE_POTS_TOPIC.0)
@@ -519,6 +546,7 @@ impl AccountsState {
519546
DRepDistributionPublisher::new(context.clone(), drep_distribution_topic);
520547
let spo_publisher = SPODistributionPublisher::new(context.clone(), spo_distribution_topic);
521548
let spo_rewards_publisher = SPORewardsPublisher::new(context.clone(), spo_rewards_topic);
549+
let stake_diffs_publisher = StakeDiffsPublisher::new(context.clone(), stake_diffs_topic);
522550

523551
// Subscribe
524552
let spos_subscription = context.subscribe(&spo_state_topic).await?;
@@ -537,6 +565,7 @@ impl AccountsState {
537565
drep_publisher,
538566
spo_publisher,
539567
spo_rewards_publisher,
568+
stake_diffs_publisher,
540569
spos_subscription,
541570
ea_subscription,
542571
certs_subscription,
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
use acropolis_common::messages::{CardanoMessage, Message, StakeAddressDiffsMessage};
2+
use acropolis_common::{BlockInfo, StakeAddressDiff};
3+
use caryatid_sdk::Context;
4+
use std::sync::Arc;
5+
6+
/// Message publisher for Stake Address Diffs
7+
pub struct StakeDiffsPublisher {
8+
/// Module context
9+
context: Arc<Context<Message>>,
10+
11+
/// Topic to publish on
12+
topic: String,
13+
}
14+
15+
impl StakeDiffsPublisher {
16+
/// Construct with context and topic to publish on
17+
pub fn new(context: Arc<Context<Message>>, topic: String) -> Self {
18+
Self { context, topic }
19+
}
20+
21+
/// Publish the Stake Diffs
22+
pub async fn publish_stake_diffs(
23+
&mut self,
24+
block: &BlockInfo,
25+
stake_diffs: Vec<StakeAddressDiff>,
26+
) -> anyhow::Result<()> {
27+
self.context
28+
.message_bus
29+
.publish(
30+
&self.topic,
31+
Arc::new(Message::Cardano((
32+
block.clone(),
33+
CardanoMessage::StakeAddressDiffs(StakeAddressDiffsMessage {
34+
diffs: stake_diffs,
35+
}),
36+
))),
37+
)
38+
.await
39+
}
40+
}

0 commit comments

Comments
 (0)