Skip to content
Draft
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
61 changes: 24 additions & 37 deletions src/staking/staking.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ pub mod Staking {
use staking::staking::utils::{
assert_caller_is_not_zero, balance_at_epoch, calculate_staker_total_staking_power,
claim_from_reward_supplier, compute_new_delegated_stake, deploy_delegation_pool_contract,
get_undelegate_intent_token, is_btc_active, split_rewards_with_commission,
strk_token_dispatcher,
get_undelegate_intent_token, is_btc_active, resolve_delayed_field,
split_rewards_with_commission, strk_token_dispatcher, validate_delayed_field_update,
};
use staking::types::{
Amount, BlockNumber, Commission, Epoch, InternalStakerInfoLatest, PeerId, PublicKey,
Expand Down Expand Up @@ -838,16 +838,19 @@ pub mod Staking {
let staker_info = self.internal_staker_info(:staker_address);
assert!(staker_info.unstake_time.is_none(), "{}", Error::UNSTAKE_IN_PROGRESS);

let (curr_activation_epoch, _, prev_public_key) = self.public_key.read(staker_address);
let curr_epoch = self.get_current_epoch();
// TODO: Confirm with product this set period is ok.
assert!(curr_epoch >= curr_activation_epoch, "{}", Error::PUBLIC_KEY_SET_IN_PROGRESS);
assert!(prev_public_key != public_key, "{}", Error::PUBLIC_KEY_MUST_DIFFER);
let delayed = self.public_key.read(staker_address);
validate_delayed_field_update(
delayed_field: delayed,
new_value: public_key,
curr_epoch: self.get_current_epoch(),
err_set_in_progress: Error::PUBLIC_KEY_SET_IN_PROGRESS,
err_must_differ: Error::PUBLIC_KEY_MUST_DIFFER,
);

let new_activation_epoch = self.get_epoch_plus_k();
let (_, _, prev_public_key) = delayed;
self
.public_key
.write(staker_address, (new_activation_epoch, prev_public_key, public_key));
.write(staker_address, (self.get_epoch_plus_k(), prev_public_key, public_key));
self.emit(Events::PublicKeySet { staker_address, public_key });
}

Expand All @@ -868,13 +871,17 @@ pub mod Staking {
let staker_info = self.internal_staker_info(:staker_address);
assert!(staker_info.unstake_time.is_none(), "{}", Error::UNSTAKE_IN_PROGRESS);

let (curr_activation_epoch, _, prev_peer_id) = self.peer_id.read(staker_address);
let curr_epoch = self.get_current_epoch();
assert!(curr_epoch >= curr_activation_epoch, "{}", Error::PEER_ID_SET_IN_PROGRESS);
assert!(prev_peer_id != peer_id, "{}", Error::PEER_ID_MUST_DIFFER);
let delayed = self.peer_id.read(staker_address);
validate_delayed_field_update(
delayed_field: delayed,
new_value: peer_id,
curr_epoch: self.get_current_epoch(),
err_set_in_progress: Error::PEER_ID_SET_IN_PROGRESS,
err_must_differ: Error::PEER_ID_MUST_DIFFER,
);

let new_activation_epoch = self.get_epoch_plus_k();
self.peer_id.write(staker_address, (new_activation_epoch, prev_peer_id, peer_id));
let (_, _, prev_peer_id) = delayed;
self.peer_id.write(staker_address, (self.get_epoch_plus_k(), prev_peer_id, peer_id));
self.emit(Events::PeerIdSet { staker_address, peer_id });
}

Expand Down Expand Up @@ -2254,17 +2261,7 @@ pub mod Staking {
fn get_public_key_at_epoch(
self: @ContractState, staker_address: ContractAddress, epoch_id: Epoch,
) -> Option<PublicKey> {
let (activation_epoch, old_pk, new_pk) = self.public_key.read(staker_address);
let current_pk = if epoch_id >= activation_epoch {
new_pk
} else {
old_pk
};
if current_pk.is_non_zero() {
Some(current_pk)
} else {
None
}
resolve_delayed_field(delayed_field: self.public_key.read(staker_address), :epoch_id)
}

/// Returns the peer ID for `staker_address` at `epoch_id`,
Expand All @@ -2276,17 +2273,7 @@ pub mod Staking {
fn get_peer_id_at_epoch(
self: @ContractState, staker_address: ContractAddress, epoch_id: Epoch,
) -> Option<PeerId> {
let (activation_epoch, old_pid, new_pid) = self.peer_id.read(staker_address);
let current_pid = if epoch_id >= activation_epoch {
new_pid
} else {
old_pid
};
if current_pid.is_non_zero() {
Some(current_pid)
} else {
None
}
resolve_delayed_field(delayed_field: self.peer_id.read(staker_address), :epoch_id)
}

/// Calculates rewards for the given staker and his pools, updates the staker's
Expand Down
36 changes: 36 additions & 0 deletions src/staking/utils.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,42 @@ pub(crate) fn is_btc_active(active_status: (Epoch, bool), epoch_id: Epoch) -> bo
(epoch_id >= epoch) == is_active
}

/// Resolves a delayed field value at a given epoch.
/// `delayed_field` is a tuple of (activation_epoch, old_value, new_value).
/// Returns `Some(value)` if the resolved value is non-zero, `None` otherwise.
///
/// Precondition: `get_current_epoch() <= epoch_id < get_current_epoch() + K`.
pub(crate) fn resolve_delayed_field<T, +Zero<T>, +Copy<T>, +Drop<T>>(
delayed_field: (Epoch, T, T), epoch_id: Epoch,
) -> Option<T> {
let (activation_epoch, old_value, new_value) = delayed_field;
let current_value = if epoch_id >= activation_epoch {
new_value
} else {
old_value
};
if current_value.is_non_zero() {
Option::Some(current_value)
} else {
Option::None
}
}

/// Validates that a delayed field can be updated.
/// Checks that the current epoch is past the activation epoch and the new value differs
/// from the previous value.
pub(crate) fn validate_delayed_field_update<T, +PartialEq<T>, +Copy<T>, +Drop<T>>(
delayed_field: (Epoch, T, T),
new_value: T,
curr_epoch: Epoch,
err_set_in_progress: Error,
err_must_differ: Error,
) {
let (curr_activation_epoch, _, prev_value) = delayed_field;
assert!(curr_epoch >= curr_activation_epoch, "{}", err_set_in_progress);
assert!(prev_value != new_value, "{}", err_must_differ);
}

/// Returns the staking power for the given staker.
/// The staking power is calculated by:
/// ((staker_strk_total_amount / strk_total_amount) * (1 - ALPHA) +
Expand Down
Loading