Skip to content

Commit 1e31382

Browse files
committed
Add test for capitulate_miner_view
Signed-off-by: Jacinta Ferrant <[email protected]>
1 parent 93b2167 commit 1e31382

File tree

2 files changed

+96
-6
lines changed

2 files changed

+96
-6
lines changed

stacks-signer/src/signerdb.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1463,7 +1463,7 @@ impl SignerDb {
14631463

14641464
/// Tests for SignerDb
14651465
#[cfg(test)]
1466-
mod tests {
1466+
pub mod tests {
14671467
use std::fs;
14681468
use std::path::PathBuf;
14691469

@@ -1487,7 +1487,8 @@ mod tests {
14871487
}
14881488
}
14891489

1490-
fn create_block_override(
1490+
/// Override the creation of a block from a block proposal with the provided function
1491+
pub fn create_block_override(
14911492
overrides: impl FnOnce(&mut BlockProposal),
14921493
) -> (BlockInfo, BlockProposal) {
14931494
let header = NakamotoBlockHeader::empty();
@@ -1509,7 +1510,8 @@ mod tests {
15091510
create_block_override(|_| {})
15101511
}
15111512

1512-
fn tmp_db_path() -> PathBuf {
1513+
/// Create a temporary db path for testing purposes
1514+
pub fn tmp_db_path() -> PathBuf {
15131515
std::env::temp_dir().join(format!(
15141516
"stacks-signer-test-{}.sqlite",
15151517
rand::random::<u64>()

stacks-signer/src/v0/signer_state.rs

Lines changed: 91 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,9 +162,9 @@ impl GlobalStateEvaluator {
162162
None
163163
}
164164

165-
/// Determines whether a signer with the `local_address` and `local_update` should
166-
/// should capitulate its current miner view to a new state. This is not necessarily the same as the current global view
167-
/// of the miner as it is up to signers to capitulate before this becomes the finalized view.
165+
/// Determines whether a signer with the `local_address` and `local_update` should capitulate
166+
/// its current miner view to a new state. This is not necessarily the same as the current global
167+
/// view of the miner as it is up to signers to capitulate before this becomes the finalized view.
168168
pub fn capitulate_miner_view(
169169
&mut self,
170170
signerdb: &mut SignerDb,
@@ -825,6 +825,7 @@ mod tests {
825825
use libsigner::v0::messages::{StateMachineUpdateContent, StateMachineUpdateMinerState};
826826

827827
use super::*;
828+
use crate::signerdb::tests::{create_block_override, tmp_db_path};
828829

829830
fn generate_global_state_evaluator(num_addresses: u32) -> GlobalStateEvaluator {
830831
let address_weights = generate_random_address_with_equal_weights(num_addresses);
@@ -1094,4 +1095,91 @@ mod tests {
10941095
state_machine
10951096
)
10961097
}
1098+
1099+
#[test]
1100+
fn check_capitulate_miner_view() {
1101+
let mut global_eval = generate_global_state_evaluator(5);
1102+
1103+
let addresses: Vec<_> = global_eval.address_weights.keys().cloned().collect();
1104+
let local_address = addresses[0];
1105+
let local_update = global_eval
1106+
.address_updates
1107+
.get(&local_address)
1108+
.unwrap()
1109+
.clone();
1110+
let StateMachineUpdateMessage {
1111+
active_signer_protocol_version,
1112+
local_supported_signer_protocol_version,
1113+
content:
1114+
StateMachineUpdateContent::V0 {
1115+
burn_block,
1116+
burn_block_height,
1117+
current_miner,
1118+
},
1119+
..
1120+
} = local_update.clone();
1121+
// Let's create a new miner view
1122+
let new_tenure_id = ConsensusHash([0x00; 20]);
1123+
let new_miner = StateMachineUpdateMinerState::ActiveMiner {
1124+
current_miner_pkh: Hash160([0x00; 20]),
1125+
tenure_id: new_tenure_id,
1126+
parent_tenure_id: ConsensusHash([0x22; 20]),
1127+
parent_tenure_last_block: StacksBlockId([0x33; 32]),
1128+
parent_tenure_last_block_height: 1,
1129+
};
1130+
1131+
let new_update = StateMachineUpdateMessage::new(
1132+
active_signer_protocol_version,
1133+
local_supported_signer_protocol_version,
1134+
StateMachineUpdateContent::V0 {
1135+
burn_block,
1136+
burn_block_height,
1137+
current_miner: new_miner.clone(),
1138+
},
1139+
)
1140+
.unwrap();
1141+
1142+
let db_path = tmp_db_path();
1143+
let mut db = SignerDb::new(db_path).expect("Failed to create signer db");
1144+
let (mut block_info_1, _block_proposal) = create_block_override(|b| {
1145+
b.block.header.consensus_hash = new_tenure_id;
1146+
b.block.header.miner_signature = MessageSignature([0x01; 65]);
1147+
b.block.header.chain_length = 1;
1148+
b.burn_height = 1;
1149+
});
1150+
1151+
db.insert_block(&block_info_1).unwrap();
1152+
// Let's update only our own view: the evaluator will tell me to revert my viewpoint to the original miner
1153+
assert_eq!(
1154+
global_eval
1155+
.capitulate_miner_view(&mut db, local_address, new_update.clone())
1156+
.unwrap(),
1157+
current_miner
1158+
);
1159+
1160+
// Let's set a blocking minority to this different view: evaluator should see no global blocks for the blocking majority and return none
1161+
// I.e. only if the blocking minority is attempting to reject an reorg should it take priority over the rest.
1162+
// Let's update 1 other signer to some new miner key (60 percent)
1163+
for address in addresses.into_iter().skip(1).take(1) {
1164+
global_eval.insert_update(address, new_update.clone());
1165+
}
1166+
assert!(
1167+
global_eval
1168+
.capitulate_miner_view(&mut db, local_address, new_update.clone())
1169+
.is_none(),
1170+
"Evaluator should have been unable to determine a majority view and return none"
1171+
);
1172+
1173+
db.mark_block_globally_accepted(&mut block_info_1).unwrap();
1174+
1175+
db.insert_block(&block_info_1).unwrap();
1176+
1177+
// Now that the blocking minority references a tenure which would actually get reorged, lets capitulate to their view
1178+
assert_eq!(
1179+
global_eval
1180+
.capitulate_miner_view(&mut db, local_address, new_update)
1181+
.unwrap(),
1182+
new_miner
1183+
);
1184+
}
10971185
}

0 commit comments

Comments
 (0)