Skip to content

Commit 3ea6130

Browse files
committed
CRC: Fix determination of the active signer protocol version and make all global eval functions take the local address and local update
Signed-off-by: Jacinta Ferrant <[email protected]>
1 parent 04b1c34 commit 3ea6130

File tree

1 file changed

+84
-46
lines changed

1 file changed

+84
-46
lines changed

stacks-signer/src/v0/signer_state.rs

Lines changed: 84 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,12 @@ impl GlobalStateEvaluator {
6868
}
6969

7070
/// Determine what the active signer protocol version should be
71-
///
72-
/// NOTE: do not call this function unless the evaluator has already been updated with the current local state
73-
pub fn determine_active_signer_protocol_version(&self) -> Option<u64> {
71+
fn determine_active_signer_protocol_version(
72+
&mut self,
73+
local_address: StacksAddress,
74+
local_update: StateMachineUpdateMessage,
75+
) -> Option<u64> {
76+
self.insert_update(local_address.clone(), local_update);
7477
let mut protocol_versions = HashMap::new();
7578
for (address, update) in &self.address_updates {
7679
let Some(weight) = self.address_weights.get(address) else {
@@ -80,17 +83,27 @@ impl GlobalStateEvaluator {
8083
.entry(update.local_supported_signer_protocol_version)
8184
.or_insert_with(|| 0);
8285
*entry += weight;
83-
if *entry >= self.total_weight * 7 / 10 {
84-
return Some(update.local_supported_signer_protocol_version);
86+
}
87+
// find the highest version number supported by a threshold number of signers
88+
let mut protocol_versions: Vec<_> = protocol_versions.into_iter().collect();
89+
protocol_versions.sort_by_key(|(version, _)| *version);
90+
let mut total_weight_support = 0;
91+
for (version, weight_support) in protocol_versions.into_iter().rev() {
92+
total_weight_support += weight_support;
93+
if total_weight_support >= self.total_weight * 7 / 10 {
94+
return Some(version);
8595
}
8696
}
87-
None
97+
return None;
8898
}
8999

90100
/// Determine what the global burn view is if there is one
91-
///
92-
/// NOTE: do not call this function unless the evaluator has already been updated with the current local state
93-
pub fn determine_global_burn_view(&self) -> Option<(ConsensusHash, u64)> {
101+
pub fn determine_global_burn_view(
102+
&mut self,
103+
local_address: StacksAddress,
104+
local_update: StateMachineUpdateMessage,
105+
) -> Option<(ConsensusHash, u64)> {
106+
self.insert_update(local_address.clone(), local_update);
94107
let mut burn_blocks = HashMap::new();
95108
for (address, update) in &self.address_updates {
96109
let Some(weight) = self.address_weights.get(address) else {
@@ -112,10 +125,13 @@ impl GlobalStateEvaluator {
112125
}
113126

114127
/// Check if there is an agreed upon global current miner viewpoint
115-
///
116-
/// NOTE: do not call this function unless the evaluator has already been updated with the current local state
117-
pub fn determine_global_state(&self) -> Option<SignerStateMachine> {
118-
let active_signer_protocol_version = self.determine_active_signer_protocol_version()?;
128+
pub fn determine_global_state(
129+
&mut self,
130+
local_address: StacksAddress,
131+
local_update: StateMachineUpdateMessage,
132+
) -> Option<SignerStateMachine> {
133+
let active_signer_protocol_version =
134+
self.determine_active_signer_protocol_version(local_address, local_update)?;
119135
let mut state_views = HashMap::new();
120136
for (address, update) in &self.address_updates {
121137
let Some(weight) = self.address_weights.get(address) else {
@@ -145,10 +161,13 @@ impl GlobalStateEvaluator {
145161
}
146162

147163
/// Check if there is an agreed upon global current miner viewpoint
148-
///
149-
/// NOTE: do not call this function unless the evaluator has already been updated with the current local state
150-
pub fn determine_global_current_miner(&self) -> Option<StateMachineUpdateMinerState> {
151-
let (global_burn_block, _) = self.determine_global_burn_view()?;
164+
pub fn determine_global_current_miner(
165+
&mut self,
166+
local_address: StacksAddress,
167+
local_update: StateMachineUpdateMessage,
168+
) -> Option<StateMachineUpdateMinerState> {
169+
let (global_burn_block, _) =
170+
self.determine_global_burn_view(local_address, local_update)?;
152171
let mut miner_views = HashMap::new();
153172
for (address, update) in &self.address_updates {
154173
let Some(weight) = self.address_weights.get(address) else {
@@ -173,17 +192,20 @@ impl GlobalStateEvaluator {
173192
None
174193
}
175194

176-
/// Determines whether a signer, based on the provided burn block view (`current_burn_block`),
177-
/// should update (capitulate) its current miner view to a new state. This is not necessarily the same as the current global view
195+
/// Determines whether a signer with the `local_address` and `local_update` should
196+
/// should capitulate its current miner view to a new state. This is not necessarily the same as the current global view
178197
/// of the miner as it is up to signers to capitulate before this becomes the finalized view.
179-
///
180-
/// NOTE: do not call this function unless the evaluator has already been updated with the current local state
181198
pub fn capitulate_miner_view(
182-
&self,
183-
current_burn_block: ConsensusHash,
199+
&mut self,
184200
signerdb: &mut SignerDb,
201+
local_address: StacksAddress,
202+
local_update: StateMachineUpdateMessage,
185203
) -> Option<StateMachineUpdateMinerState> {
186-
let (global_burn_view, _) = self.determine_global_burn_view()?;
204+
let StateMachineUpdateContent::V0 {
205+
burn_block: current_burn_block,
206+
..
207+
} = local_update.content.clone();
208+
let (global_burn_view, _) = self.determine_global_burn_view(local_address, local_update)?;
187209
if current_burn_block != global_burn_view {
188210
return None;
189211
}
@@ -761,50 +783,66 @@ impl LocalStateMachine {
761783
eval: &mut GlobalStateEvaluator,
762784
local_address: StacksAddress,
763785
) {
764-
// Before we ever access eval...we should update with our own viewpoint
786+
// Before we ever access eval...we should make sure to include our own local state machine update message in the evaluation
765787
let local_update: Result<StateMachineUpdateMessage, _> = (&*self).try_into();
766-
let Ok(local_update) = local_update else {
788+
let Ok(mut local_update) = local_update else {
767789
return;
768790
};
791+
769792
let old_protocol_version = local_update.active_signer_protocol_version;
793+
// First check if we should update our active protocol version
794+
let active_signer_protocol_version = eval
795+
.determine_active_signer_protocol_version(local_address, local_update.clone())
796+
.unwrap_or(old_protocol_version);
797+
770798
let StateMachineUpdateContent::V0 {
771799
burn_block,
772800
burn_block_height,
773801
current_miner,
774802
..
775-
} = &local_update.content.clone();
803+
} = &local_update.content;
776804

777-
eval.insert_update(local_address, local_update);
805+
if active_signer_protocol_version != old_protocol_version {
806+
info!("Updating active signer protocol version from {old_protocol_version} to {active_signer_protocol_version}");
807+
*self = Self::Initialized(SignerStateMachine {
808+
burn_block: *burn_block,
809+
burn_block_height: *burn_block_height,
810+
current_miner: current_miner.into(),
811+
active_signer_protocol_version,
812+
});
813+
// Because we updated our active signer protocol version, update local_update so its included in the subsequent evaluations
814+
let update: Result<StateMachineUpdateMessage, _> = (&*self).try_into();
815+
let Ok(update) = update else {
816+
return;
817+
};
818+
local_update = update;
819+
}
778820

779-
let active_signer_protocol_version = eval
780-
.determine_active_signer_protocol_version()
781-
.unwrap_or(old_protocol_version);
782-
let Some(new_miner) = eval.capitulate_miner_view(*burn_block, signerdb) else {
821+
// Check if we should also capitulate our miner viewpoint
822+
let Some(new_miner) =
823+
eval.capitulate_miner_view(signerdb, local_address, local_update.clone())
824+
else {
783825
return;
784826
};
785827

786-
if current_miner != &new_miner {
787-
if active_signer_protocol_version != old_protocol_version {
788-
info!("Updating active signer protocol version from {old_protocol_version} to {active_signer_protocol_version}");
789-
}
828+
let StateMachineUpdateContent::V0 {
829+
burn_block,
830+
burn_block_height,
831+
current_miner,
832+
..
833+
} = local_update.content;
834+
835+
if current_miner != new_miner {
790836
info!("Capitulating local state machine's current miner viewpoint";
791837
"current_miner" => ?current_miner,
792838
"new_miner" => ?new_miner,
793839
);
794840
*self = Self::Initialized(SignerStateMachine {
795-
burn_block: *burn_block,
796-
burn_block_height: *burn_block_height,
841+
burn_block,
842+
burn_block_height,
797843
current_miner: (&new_miner).into(),
798844
active_signer_protocol_version,
799845
});
800-
} else if active_signer_protocol_version != old_protocol_version {
801-
info!("Updating active signer protocol version from {old_protocol_version} to {active_signer_protocol_version}");
802-
*self = Self::Initialized(SignerStateMachine {
803-
burn_block: *burn_block,
804-
burn_block_height: *burn_block_height,
805-
current_miner: current_miner.into(),
806-
active_signer_protocol_version,
807-
});
808846
}
809847
}
810848
}

0 commit comments

Comments
 (0)