Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 41 additions & 36 deletions api-server/scanner-lib/src/blockchain_state/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1453,16 +1453,12 @@ async fn update_tables_from_transaction_inputs<T: ApiServerStorageWrite>(
| TxOutput::CreateOrder(_) => {}
TxOutput::CreateStakePool(pool_id, _)
| TxOutput::ProduceBlockFromStake(_, pool_id) => {
let pool_data = db_tx
.get_pool_data(pool_id)
.await?
.expect("pool data should exist")
.decommission_pool();

db_tx
.set_pool_data_at_height(pool_id, &pool_data, block_height)
.await
.expect("unable to update pool data");
let pool_data = decommission_pool_and_decrement_staked(
db_tx,
pool_id,
block_height,
)
.await?;

let address = Address::<Destination>::new(
&chain_config,
Expand All @@ -1474,14 +1470,6 @@ async fn update_tables_from_transaction_inputs<T: ApiServerStorageWrite>(
.entry(address.clone())
.or_default()
.insert(tx.get_id());
decrease_statistic_amount(
db_tx,
CoinOrTokenStatistic::Staked,
&pool_data.pledge_amount(),
CoinOrTokenId::Coin,
block_height,
)
.await;
}
}
}
Expand All @@ -1507,24 +1495,8 @@ async fn update_tables_from_transaction_inputs<T: ApiServerStorageWrite>(
| TxOutput::CreateOrder(_) => {}
TxOutput::CreateStakePool(pool_id, _)
| TxOutput::ProduceBlockFromStake(_, pool_id) => {
let pool_data = db_tx
.get_pool_data(pool_id)
.await?
.expect("pool data should exist")
.decommission_pool();

db_tx
.set_pool_data_at_height(pool_id, &pool_data, block_height)
.await
.expect("unable to update pool data");
decrease_statistic_amount(
db_tx,
CoinOrTokenStatistic::Staked,
&pool_data.pledge_amount(),
CoinOrTokenId::Coin,
block_height,
)
.await;
decommission_pool_and_decrement_staked(db_tx, pool_id, block_height)
.await?;
}
TxOutput::IssueNft(token_id, _, destination) => {
let address = Address::<Destination>::new(&chain_config, destination)
Expand Down Expand Up @@ -2142,6 +2114,39 @@ async fn increase_statistic_amount<T: ApiServerStorageWrite>(
.expect("Unable to update statistic")
}

/// Decommissions a stake pool and removes its whole balance (pledge plus
/// accumulated staker rewards, matching what the creation and block-reward
/// paths added to the `Staked` statistic) from it. The balance is captured
/// before `decommission_pool` zeroes the record; the zeroed record is stored
/// and returned so callers can read the decommission destination.
async fn decommission_pool_and_decrement_staked<T: ApiServerStorageWrite>(
db_tx: &mut T,
pool_id: PoolId,
block_height: BlockHeight,
) -> Result<PoolDataWithExtraInfo, ApiServerStorageError> {
let pool_data = db_tx.get_pool_data(pool_id).await?.ok_or_else(|| {
ApiServerStorageError::LowLevelStorageError(format!("pool data for {pool_id} should exist"))
})?;
let staker_balance = pool_data.staker_balance().map_err(|error| {
ApiServerStorageError::LowLevelStorageError(format!(
"staker balance overflow for pool {pool_id}: {error}",
))
})?;
let pool_data = pool_data.decommission_pool();

db_tx.set_pool_data_at_height(pool_id, &pool_data, block_height).await?;

decrease_statistic_amount(
db_tx,
CoinOrTokenStatistic::Staked,
&staker_balance,
CoinOrTokenId::Coin,
block_height,
)
.await;
Ok(pool_data)
Comment on lines +2139 to +2147

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bug · medium
decrease_statistic_amount internally panics via expect("Balance should not underflow"). This change increases the subtracted amount from pledge-only to the full staker_balance (pledge + staker_rewards), so underflow is now more reachable if the Staked statistic is inconsistent with pool state (e.g., after a partial reorg, an interrupted statistic update, or a pre-existing drift). In that case this will panic mid-scan instead of surfacing a recoverable error, unlike the rest of this helper which propagates Result. Consider propagating the subtraction as a typed error (or at least logging the statistic/pool IDs before the panic) so a corrupted statistic is diagnosable rather than a bare arithmetic panic.

Suggestion:

Suggested change
decrease_statistic_amount(
db_tx,
CoinOrTokenStatistic::Staked,
&staker_balance,
CoinOrTokenId::Coin,
block_height,
)
.await;
Ok(pool_data)
decrease_statistic_amount(
db_tx,
CoinOrTokenStatistic::Staked,
&staker_balance,
CoinOrTokenId::Coin,
block_height,
)
.await
.map_err(|error| {
ApiServerStorageError::LowLevelStorageError(format!(
"staked statistic underflow decommissioning pool {pool_id}: {error}",
))
})?;
Ok(pool_data)

}

async fn decrease_statistic_amount<T: ApiServerStorageWrite>(
db_tx: &mut T,
statistic: CoinOrTokenStatistic,
Expand Down
Loading
Loading