Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix more sync issues on testnet #2540

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
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
89 changes: 61 additions & 28 deletions data_structures/src/staking/stakes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ where
capability: Capability,
epoch: Epoch,
strategy: CensusStrategy,
) -> Box<dyn Iterator<Item = StakeKey<Address>> + '_> {
) -> Box<dyn Iterator<Item = Address> + '_> {
let iterator = self.rank(capability, epoch).map(|(address, _)| address);

match strategy {
Expand Down Expand Up @@ -256,15 +256,26 @@ where
&self,
capability: Capability,
current_epoch: Epoch,
) -> impl Iterator<Item = (StakeKey<Address>, Power)> + '_ {
self.by_coins
) -> impl Iterator<Item = (Address, Power)> + '_ {
self.by_validator
.iter()
.map(move |(CoinsAndAddresses { addresses, .. }, stake)| {
let power = stake.read_value().power(capability, current_epoch);

(addresses.clone(), power)
.map(move |(address, stakes)| {
let power = stakes
.first()
.unwrap()
.read_value()
.power(capability, current_epoch);

(address.clone(), power)
})
.sorted_by(|(address_1, power_1), (address_2, power_2)| {
// Equal power, compare the addresses to achieve deterministic ordering
if power_1 == power_2 {
address_1.cmp(address_2)
} else {
power_1.cmp(power_2)
}
})
.sorted_by_key(|(_, power)| *power)
.rev()
}

Expand Down Expand Up @@ -312,6 +323,28 @@ where
// No need to keep the entry if the stake has gone to zero
if final_coins.is_zero() {
by_address_entry.remove();
if let Some(ref mut stakes) = self.by_validator.get_mut(&key.validator) {
stakes.remove(
stakes
.iter()
.position(|stake| stake.read_value().coins == 0.into())
.unwrap(),
);
if stakes.is_empty() {
self.by_validator.remove(&key.validator);
}
}
if let Some(ref mut stakes) = self.by_withdrawer.get_mut(&key.withdrawer) {
stakes.remove(
stakes
.iter()
.position(|stake| stake.read_value().coins == 0.into())
.unwrap(),
);
if stakes.is_empty() {
self.by_withdrawer.remove(&key.withdrawer);
}
}
self.by_coins.remove(&CoinsAndAddresses {
coins: initial_coins,
addresses: key,
Expand Down Expand Up @@ -1025,17 +1058,17 @@ mod tests {
assert_eq!(
stakes.rank(Capability::Mining, 100).collect::<Vec<_>>(),
[
(charlie_erin.into(), 2100),
(bob_david.into(), 1600),
(alice_charlie.into(), 1000)
(charlie.into(), 2100),
(bob.into(), 1600),
(alice.into(), 1000)
]
);
assert_eq!(
stakes.rank(Capability::Witnessing, 100).collect::<Vec<_>>(),
[
(charlie_erin.into(), 2100),
(bob_david.into(), 1600),
(alice_charlie.into(), 1000)
(charlie.into(), 2100),
(bob.into(), 1600),
(alice.into(), 1000)
]
);

Expand Down Expand Up @@ -1064,17 +1097,17 @@ mod tests {
assert_eq!(
stakes.rank(Capability::Mining, 101).collect::<Vec<_>>(),
[
(bob_david.into(), 1_620),
(alice_charlie.into(), 1_010),
(charlie_erin.into(), 0)
(bob.into(), 1_620),
(alice.into(), 1_010),
(charlie.into(), 0)
]
);
assert_eq!(
stakes.rank(Capability::Witnessing, 101).collect::<Vec<_>>(),
[
(charlie_erin.into(), 2_130),
(bob_david.into(), 1_620),
(alice_charlie.into(), 1_010)
(charlie.into(), 2_130),
(bob.into(), 1_620),
(alice.into(), 1_010)
]
);

Expand Down Expand Up @@ -1105,17 +1138,17 @@ mod tests {
assert_eq!(
stakes.rank(Capability::Mining, 300).collect::<Vec<_>>(),
[
(charlie_erin.into(), 5_970),
(bob_david.into(), 5_600),
(alice_charlie.into(), 3_000)
(charlie.into(), 5_970),
(bob.into(), 5_600),
(alice.into(), 3_000)
]
);
assert_eq!(
stakes.rank(Capability::Witnessing, 300).collect::<Vec<_>>(),
[
(charlie_erin.into(), 8_100),
(bob_david.into(), 5_600),
(alice_charlie.into(), 3_000)
(charlie.into(), 8_100),
(bob.into(), 5_600),
(alice.into(), 3_000)
]
);
}
Expand Down Expand Up @@ -1159,9 +1192,9 @@ mod tests {
// david_erin: 40 * (90 - 30) = 2400
// erin_alice: 50 * (90 - 40) = 2500
let rank_subset: Vec<_> = stakes.rank(Capability::Mining, 90).take(4).collect();
for (i, (stake_key, _)) in rank_subset.into_iter().enumerate() {
for (i, (validator, _)) in rank_subset.into_iter().enumerate() {
let _ = stakes.reset_age(
stake_key.validator,
validator,
Capability::Mining,
90,
(i + 1).try_into().unwrap(),
Expand Down
17 changes: 7 additions & 10 deletions node/src/actors/chain_manager/actor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,16 +192,6 @@ impl ChainManager {
if consensus_constants == &chain_info_from_storage.consensus_constants {
log::debug!("ChainInfo successfully obtained from storage");

// Load protocol info into global state (overwrites whatever was set
// through configuration). If possible, derives the current protocol
// version from that info and the current epoch. This essentially
// allows a node to catch up with a new protocol version if the
// transition happened while it was down.
load_protocol_info(chain_info_from_storage.protocol.clone());
if let Some(ChainInfo { highest_block_checkpoint, .. }) = chain_state_from_storage.chain_info {
refresh_protocol_version(highest_block_checkpoint.checkpoint);
}

chain_state_from_storage
} else {
// Mismatching consensus constants between config and storage
Expand Down Expand Up @@ -370,6 +360,13 @@ impl ChainManager {
chain_info.highest_block_checkpoint.hash_prev_block
);

// Load protocol info into global state (overwrites whatever was set through
// configuration). If possible, derives the current protocol version from that
// info and the current epoch. This essentially allows a node to catch up with
// a new protocol version if the transition happened while it was down.
load_protocol_info(chain_info.protocol.clone());
refresh_protocol_version(chain_info.highest_block_checkpoint.checkpoint);

// If hash_prev_block is the bootstrap hash, create and consolidate genesis block.
// Consolidating the genesis block is not needed if the chain state has been reset
// because of a rewind: the genesis block will be processed with the other blocks.
Expand Down
6 changes: 3 additions & 3 deletions node/src/actors/chain_manager/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,13 +201,13 @@ impl Handler<EpochNotification<EveryEpochPayload>> for ChainManager {
.rank(Capability::Mining, previous_epoch)
.take(replication_factor.into())
.collect();
for (i, (stake_key, _)) in rank_subset.into_iter().enumerate() {
for (i, (validator, _)) in rank_subset.into_iter().enumerate() {
log::warn!(
"Slashed the power of {} as it did not propose a block",
stake_key.validator
validator
);
let _ = stakes.reset_age(
stake_key.validator,
validator,
Capability::Mining,
msg.checkpoint,
// This should never fail
Expand Down
69 changes: 29 additions & 40 deletions node/src/actors/chain_manager/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -950,23 +950,6 @@ impl ChainManager {
}
};

let current_epoch = if let Some(epoch) = self.current_epoch {
epoch
} else {
// If there is no epoch set, it's because the chain is yet to be bootstrapped, or because of a data race
match self.chain_state.chain_info.as_ref() {
// If the chain is yet to be bootstrapped (the block we are processing is the genesis block), set the epoch to zero
Some(chain_info) if chain_info.consensus_constants.genesis_hash == block.hash() => {
0
}
// In case of data race, shortcut the function
_ => {
log::error!("Current epoch not loaded in ChainManager");
return;
}
}
};

match self.chain_state {
ChainState {
chain_info: Some(ref mut chain_info),
Expand Down Expand Up @@ -1203,7 +1186,7 @@ impl ChainManager {
transaction_fees += vt_transaction_fee(
vt_tx,
&utxo_diff_wit2,
current_epoch,
block_epoch,
epoch_constants,
)
.unwrap_or_default();
Expand All @@ -1212,7 +1195,7 @@ impl ChainManager {
transaction_fees += dr_transaction_fee(
dr_tx,
&utxo_diff_wit2,
current_epoch,
block_epoch,
epoch_constants,
)
.unwrap_or_default();
Expand All @@ -1221,7 +1204,7 @@ impl ChainManager {
transaction_fees += st_transaction_fee(
st_tx,
&utxo_diff_wit2,
current_epoch,
block_epoch,
epoch_constants,
)
.unwrap_or_default();
Expand Down Expand Up @@ -1252,20 +1235,24 @@ impl ChainManager {
log::debug!(
"Resetting mining age for {} to {}",
miner_pkh,
current_epoch,
block_epoch + 1,
);
let _ = stakes.reset_age(miner_pkh, Capability::Mining, current_epoch, 1);
let _ = stakes.reset_age(miner_pkh, Capability::Mining, block_epoch + 1, 1);

// Reset witnessing power
for co_tx in &block.txns.commit_txns {
let commit_pkh = co_tx.body.proof.proof.pkh();
log::debug!(
"Resetting witnessing age for {} to {}",
commit_pkh,
current_epoch,
block_epoch + 1,
);
let _ = stakes.reset_age(
commit_pkh,
Capability::Witnessing,
block_epoch + 1,
1,
);
let _ =
stakes.reset_age(commit_pkh, Capability::Witnessing, current_epoch, 1);
}

// Slash lieing validators
Expand Down Expand Up @@ -1308,24 +1295,26 @@ impl ChainManager {

// Do not update reputation or stakes when consolidating genesis block
if block_hash != chain_info.consensus_constants.genesis_hash {
update_reputation(
reputation_engine,
&mut self.chain_state.alt_keys,
&chain_info.consensus_constants,
miner_pkh,
rep_info,
log_level,
block_epoch,
self.own_pkh.unwrap_or_default(),
);
if ProtocolVersion::from_epoch(block_epoch) < ProtocolVersion::V2_0 {
update_reputation(
reputation_engine,
&mut self.chain_state.alt_keys,
&chain_info.consensus_constants,
miner_pkh,
rep_info,
log_level,
block_epoch,
self.own_pkh.unwrap_or_default(),
);
}

let stake_txns_count = block.txns.stake_txns.len();
if stake_txns_count > 0 {
log::debug!("Processing {stake_txns_count} stake transactions");

let minimum_stakeable = self
.consensus_constants_wit2
.get_validator_min_stake_nanowits(current_epoch);
.get_validator_min_stake_nanowits(block_epoch);

let _ = process_stake_transactions(
stakes,
Expand All @@ -1341,7 +1330,7 @@ impl ChainManager {

let minimum_stakeable = self
.consensus_constants_wit2
.get_validator_min_stake_nanowits(current_epoch);
.get_validator_min_stake_nanowits(block_epoch);

let _ = process_unstake_transactions(
stakes,
Expand Down Expand Up @@ -1381,7 +1370,7 @@ impl ChainManager {
let reveals = self
.chain_state
.data_request_pool
.update_data_request_stages(Some(validator_count), Some(current_epoch));
.update_data_request_stages(Some(validator_count), Some(block_epoch));

for reveal in reveals {
// Send AddTransaction message to self
Expand All @@ -1407,7 +1396,7 @@ impl ChainManager {
let reveals = self
.chain_state
.data_request_pool
.update_data_request_stages(Some(validator_count), Some(current_epoch));
.update_data_request_stages(Some(validator_count), Some(block_epoch));

for reveal in reveals {
// Send AddTransaction message to self
Expand All @@ -1433,7 +1422,7 @@ impl ChainManager {
let reveals = self
.chain_state
.data_request_pool
.update_data_request_stages(Some(validator_count), Some(current_epoch));
.update_data_request_stages(Some(validator_count), Some(block_epoch));

show_info_dr(&self.chain_state.data_request_pool, &block);

Expand Down
Loading