Skip to content

Commit 644094d

Browse files
committed
Add injected_signatures_are_ignored_across_boundaries signer test
Signed-off-by: Jacinta Ferrant <[email protected]>
1 parent 6e4ce40 commit 644094d

File tree

3 files changed

+525
-55
lines changed

3 files changed

+525
-55
lines changed

.github/workflows/bitcoin-tests.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ jobs:
138138
- tests::signer::v0::no_reorg_due_to_successive_block_validation_ok
139139
- tests::signer::v0::incoming_signers_ignore_block_proposals
140140
- tests::signer::v0::outgoing_signers_ignore_block_proposals
141+
- tests::signer::v0::injected_signatures_are_ignored_across_boundaries
141142
- tests::nakamoto_integrations::burn_ops_integration_test
142143
- tests::nakamoto_integrations::check_block_heights
143144
- tests::nakamoto_integrations::clarity_burn_state

testnet/stacks-node/src/tests/signer/mod.rs

Lines changed: 106 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,16 @@ use libsigner::v0::messages::{
4242
use libsigner::{SignerEntries, SignerEventTrait};
4343
use stacks::chainstate::coordinator::comm::CoordinatorChannels;
4444
use stacks::chainstate::nakamoto::signer_set::NakamotoSigners;
45+
use stacks::chainstate::nakamoto::NakamotoBlock;
4546
use stacks::chainstate::stacks::boot::{NakamotoSignerEntry, SIGNERS_NAME};
4647
use stacks::chainstate::stacks::StacksPrivateKey;
4748
use stacks::config::{Config as NeonConfig, EventKeyType, EventObserverConfig, InitialBalance};
4849
use stacks::net::api::postblock_proposal::{
4950
BlockValidateOk, BlockValidateReject, BlockValidateResponse,
5051
};
5152
use stacks::types::chainstate::{StacksAddress, StacksPublicKey};
52-
use stacks::types::PublicKey;
53+
use stacks::types::{PrivateKey, PublicKey};
54+
use stacks::util::get_epoch_time_secs;
5355
use stacks::util::hash::MerkleHashFunc;
5456
use stacks::util::secp256k1::{MessageSignature, Secp256k1PublicKey};
5557
use stacks_common::codec::StacksMessageCodec;
@@ -262,6 +264,33 @@ impl<S: Signer<T> + Send + 'static, T: SignerEventTrait + 'static> SignerTest<Sp
262264
}).expect("Timed out while waiting for the signers to be registered");
263265
}
264266

267+
/// Send a status request to the signers to ensure they are registered for both reward cycles.
268+
pub fn wait_for_registered_both_reward_cycles(&mut self, timeout_secs: u64) {
269+
let mut finished_signers = HashSet::new();
270+
wait_for(timeout_secs, || {
271+
self.send_status_request(&finished_signers);
272+
thread::sleep(Duration::from_secs(1));
273+
let latest_states = self.get_states(&finished_signers);
274+
for (ix, state) in latest_states.iter().enumerate() {
275+
let Some(state) = state else {
276+
continue;
277+
};
278+
debug!("Signer #{ix} state info: {state:?}");
279+
if state.runloop_state == State::RegisteredSigners && state.running_signers.len() == 2 {
280+
finished_signers.insert(ix);
281+
} else {
282+
warn!(
283+
"Signer #{ix} returned state = {:?}, running signers = {:?}. Will try again",
284+
state.runloop_state, state.running_signers
285+
);
286+
}
287+
}
288+
debug!("Number of finished signers: {:?}", finished_signers.len());
289+
Ok(finished_signers.len() == self.spawned_signers.len())
290+
})
291+
.expect("Timed out while waiting for the signers to be registered for both reward cycles");
292+
}
293+
265294
pub fn wait_for_cycle(&mut self, timeout_secs: u64, reward_cycle: u64) {
266295
let mut finished_signers = HashSet::new();
267296
wait_for(timeout_secs, || {
@@ -517,6 +546,27 @@ impl<S: Signer<T> + Send + 'static, T: SignerEventTrait + 'static> SignerTest<Sp
517546
.get_stackerdb_signer_slots(&signer_stackerdb_contract_id, valid_signer_set)
518547
}
519548

549+
fn get_signer_slot_id(
550+
&self,
551+
reward_cycle: u64,
552+
signer_address: &StacksAddress,
553+
) -> Result<Option<SignerSlotID>, ClientError> {
554+
let valid_signer_set =
555+
u32::try_from(reward_cycle % 2).expect("FATAL: reward_cycle % 2 exceeds u32::MAX");
556+
let signer_stackerdb_contract_id = boot_code_id(SIGNERS_NAME, false);
557+
558+
let slots = self
559+
.stacks_client
560+
.get_stackerdb_signer_slots(&signer_stackerdb_contract_id, valid_signer_set)?;
561+
562+
Ok(slots
563+
.iter()
564+
.position(|(address, _)| address == signer_address)
565+
.map(|pos| {
566+
SignerSlotID(u32::try_from(pos).expect("FATAL: number of signers exceeds u32::MAX"))
567+
}))
568+
}
569+
520570
fn get_signer_indices(&self, reward_cycle: u64) -> Vec<SignerSlotID> {
521571
self.get_signer_slots(reward_cycle)
522572
.expect("FATAL: failed to get signer slots from stackerdb")
@@ -681,6 +731,61 @@ impl<S: Signer<T> + Send + 'static, T: SignerEventTrait + 'static> SignerTest<Sp
681731
.get_peer_info()
682732
.expect("Failed to get peer info")
683733
}
734+
735+
pub fn verify_no_block_response_found(
736+
&self,
737+
stackerdb: &mut StackerDB<MessageSlotID>,
738+
reward_cycle: u64,
739+
hash: Sha512Trunc256Sum,
740+
) {
741+
let slot_ids: Vec<_> = self
742+
.get_signer_indices(reward_cycle)
743+
.iter()
744+
.map(|id| id.0)
745+
.collect();
746+
747+
let latest_msgs = StackerDB::get_messages::<SignerMessage>(
748+
stackerdb
749+
.get_session_mut(&MessageSlotID::BlockResponse)
750+
.expect("Failed to get BlockResponse stackerdb session"),
751+
&slot_ids,
752+
)
753+
.expect("Failed to get messages from stackerdb");
754+
for msg in latest_msgs.iter() {
755+
if let SignerMessage::BlockResponse(response) = msg {
756+
assert_ne!(response.get_signer_signature_hash(), hash);
757+
}
758+
}
759+
}
760+
761+
pub fn inject_accept_signature(
762+
&self,
763+
block: &NakamotoBlock,
764+
private_key: &StacksPrivateKey,
765+
reward_cycle: u64,
766+
) {
767+
let mut stackerdb = StackerDB::new(
768+
&self.running_nodes.conf.node.rpc_bind,
769+
private_key.clone(),
770+
false,
771+
reward_cycle,
772+
self.get_signer_slot_id(reward_cycle, &to_addr(private_key))
773+
.expect("Failed to get signer slot id")
774+
.expect("Signer does not have a slot id"),
775+
);
776+
777+
let signature = private_key
778+
.sign(block.header.signer_signature_hash().bits())
779+
.expect("Failed to sign block");
780+
let accepted = BlockResponse::accepted(
781+
block.header.signer_signature_hash(),
782+
signature,
783+
get_epoch_time_secs().wrapping_add(u64::MAX),
784+
);
785+
stackerdb
786+
.send_message_with_retry::<SignerMessage>(accepted.into())
787+
.expect("Failed to send accept signature");
788+
}
684789
}
685790

686791
fn setup_stx_btc_node<G: FnMut(&mut NeonConfig)>(

0 commit comments

Comments
 (0)