Skip to content

Commit 7b56ed0

Browse files
committed
Fix version mismatch, send updates when state is Initialized, and add a test
1 parent e31481d commit 7b56ed0

File tree

7 files changed

+295
-157
lines changed

7 files changed

+295
-157
lines changed

libsigner/src/v0/messages.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1705,6 +1705,12 @@ impl From<BlockResponse> for SignerMessage {
17051705
}
17061706
}
17071707

1708+
impl From<StateMachineUpdate> for SignerMessage {
1709+
fn from(update: StateMachineUpdate) -> Self {
1710+
Self::StateMachineUpdate(update)
1711+
}
1712+
}
1713+
17081714
#[cfg(test)]
17091715
mod test {
17101716
use blockstack_lib::chainstate::nakamoto::NakamotoBlockHeader;

stacks-signer/src/v0/signer.rs

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ impl std::fmt::Display for Signer {
129129
impl SignerTrait<SignerMessage> for Signer {
130130
/// Create a new signer from the given configuration
131131
fn new(stacks_client: &StacksClient, signer_config: SignerConfig) -> Self {
132-
let stackerdb = StackerDB::from(&signer_config);
132+
let mut stackerdb = StackerDB::from(&signer_config);
133133
let mode = match signer_config.signer_mode {
134134
SignerConfigMode::DryRun => SignerMode::DryRun,
135135
SignerConfigMode::Normal { signer_id, .. } => SignerMode::Normal { signer_id },
@@ -141,11 +141,12 @@ impl SignerTrait<SignerMessage> for Signer {
141141
SignerDb::new(&signer_config.db_path).expect("Failed to connect to signer Db");
142142
let proposal_config = ProposalEvalConfig::from(&signer_config);
143143

144-
let signer_state = LocalStateMachine::new(&signer_db, stacks_client, &proposal_config)
145-
.unwrap_or_else(|e| {
146-
warn!("Failed to initialize local state machine for signer: {e:?}");
147-
LocalStateMachine::Uninitialized
148-
});
144+
let signer_state =
145+
LocalStateMachine::new(&signer_db, &mut stackerdb, stacks_client, &proposal_config)
146+
.unwrap_or_else(|e| {
147+
warn!("Failed to initialize local state machine for signer: {e:?}");
148+
LocalStateMachine::Uninitialized
149+
});
149150
Self {
150151
private_key: signer_config.stacks_private_key,
151152
stackerdb,
@@ -213,7 +214,7 @@ impl SignerTrait<SignerMessage> for Signer {
213214
}
214215

215216
if self.reward_cycle <= current_reward_cycle {
216-
self.local_state_machine.handle_pending_update(&self.signer_db, stacks_client, &self.proposal_config)
217+
self.local_state_machine.handle_pending_update(&self.signer_db, &mut self.stackerdb, stacks_client, &self.proposal_config)
217218
.unwrap_or_else(|e| error!("{self}: failed to update local state machine for pending update"; "err" => ?e));
218219
}
219220

@@ -299,6 +300,9 @@ impl SignerTrait<SignerMessage> for Signer {
299300
self.mock_sign(mock_proposal.clone());
300301
}
301302
}
303+
SignerMessage::StateMachineUpdate(_update) => {
304+
// TODO: should make note of this update view point to determine if there is an agreed upon global state
305+
}
302306
_ => {}
303307
}
304308
}
@@ -330,7 +334,7 @@ impl SignerTrait<SignerMessage> for Signer {
330334
panic!("{self} Failed to write burn block event to signerdb: {e}");
331335
});
332336
self.local_state_machine
333-
.bitcoin_block_arrival(&self.signer_db, stacks_client, &self.proposal_config, Some(*burn_height))
337+
.bitcoin_block_arrival(&self.signer_db, &mut self.stackerdb, stacks_client, &self.proposal_config, Some(*burn_height))
334338
.unwrap_or_else(|e| error!("{self}: failed to update local state machine for latest bitcoin block arrival"; "err" => ?e));
335339
*sortition_state = None;
336340
}
@@ -352,7 +356,7 @@ impl SignerTrait<SignerMessage> for Signer {
352356
"block_height" => block_height
353357
);
354358
self.local_state_machine
355-
.stacks_block_arrival(consensus_hash, *block_height, block_id)
359+
.stacks_block_arrival(&mut self.stackerdb, consensus_hash, *block_height, block_id)
356360
.unwrap_or_else(|e| error!("{self}: failed to update local state machine for latest stacks block arrival"; "err" => ?e));
357361

358362
if let Ok(Some(mut block_info)) = self

stacks-signer/src/v0/signer_state.rs

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ use std::time::{Duration, UNIX_EPOCH};
1818
use blockstack_lib::chainstate::burn::ConsensusHashExtensions;
1919
use blockstack_lib::chainstate::nakamoto::{NakamotoBlock, NakamotoBlockHeader};
2020
use libsigner::v0::messages::{
21-
StateMachineUpdate as StateMachineUpdateMessage, StateMachineUpdateContent,
22-
StateMachineUpdateMinerState,
21+
MessageSlotID, SignerMessage, StateMachineUpdate as StateMachineUpdateMessage,
22+
StateMachineUpdateContent, StateMachineUpdateMinerState,
2323
};
2424
use serde::{Deserialize, Serialize};
2525
use stacks_common::bitvec::BitVec;
@@ -32,11 +32,11 @@ use stacks_common::{info, warn};
3232
use crate::chainstate::{
3333
ProposalEvalConfig, SignerChainstateError, SortitionState, SortitionsView,
3434
};
35-
use crate::client::{ClientError, CurrentAndLastSortition, StacksClient};
35+
use crate::client::{ClientError, CurrentAndLastSortition, StackerDB, StacksClient};
3636
use crate::signerdb::SignerDb;
3737

3838
/// This is the latest supported protocol version for this signer binary
39-
pub static SUPPORTED_SIGNER_PROTOCOL_VERSION: u64 = 1;
39+
pub static SUPPORTED_SIGNER_PROTOCOL_VERSION: u64 = 0;
4040

4141
/// A signer state machine view. This struct can
4242
/// be used to encode the local signer's view or
@@ -144,11 +144,12 @@ impl LocalStateMachine {
144144
/// and signerdb for the current sortition information
145145
pub fn new(
146146
db: &SignerDb,
147+
stackerdb: &mut StackerDB<MessageSlotID>,
147148
client: &StacksClient,
148149
proposal_config: &ProposalEvalConfig,
149150
) -> Result<Self, SignerChainstateError> {
150151
let mut instance = Self::Uninitialized;
151-
instance.bitcoin_block_arrival(db, client, proposal_config, None)?;
152+
instance.bitcoin_block_arrival(db, stackerdb, client, proposal_config, None)?;
152153

153154
Ok(instance)
154155
}
@@ -158,24 +159,44 @@ impl LocalStateMachine {
158159
burn_block: ConsensusHash::empty(),
159160
burn_block_height: 0,
160161
current_miner: MinerState::NoValidMiner,
161-
active_signer_protocol_version: 1,
162+
active_signer_protocol_version: SUPPORTED_SIGNER_PROTOCOL_VERSION,
163+
}
164+
}
165+
166+
/// Send the local tate machine as an signer update message to stackerdb
167+
pub fn send_signer_update_message(&self, stackerdb: &mut StackerDB<MessageSlotID>) {
168+
let update: Result<StateMachineUpdateMessage, _> = self.try_into();
169+
match update {
170+
Ok(update) => {
171+
if let Err(e) = stackerdb.send_message_with_retry::<SignerMessage>(update.into()) {
172+
warn!("Failed to send signer update to stacker-db: {e:?}",);
173+
}
174+
}
175+
Err(e) => {
176+
warn!("Failed to convert local signer state to a signer message: {e:?}");
177+
}
162178
}
163179
}
164180

165181
/// If this local state machine has pending updates, process them
166182
pub fn handle_pending_update(
167183
&mut self,
168184
db: &SignerDb,
185+
stackerdb: &mut StackerDB<MessageSlotID>,
169186
client: &StacksClient,
170187
proposal_config: &ProposalEvalConfig,
171188
) -> Result<(), SignerChainstateError> {
172189
let LocalStateMachine::Pending { update, .. } = self else {
173-
return self.check_miner_inactivity(db, client, proposal_config);
190+
return self.check_miner_inactivity(db, stackerdb, client, proposal_config);
174191
};
175192
match update.clone() {
176-
StateMachineUpdate::BurnBlock(expected_burn_height) => {
177-
self.bitcoin_block_arrival(db, client, proposal_config, Some(expected_burn_height))
178-
}
193+
StateMachineUpdate::BurnBlock(expected_burn_height) => self.bitcoin_block_arrival(
194+
db,
195+
stackerdb,
196+
client,
197+
proposal_config,
198+
Some(expected_burn_height),
199+
),
179200
}
180201
}
181202

@@ -216,6 +237,7 @@ impl LocalStateMachine {
216237
fn check_miner_inactivity(
217238
&mut self,
218239
db: &SignerDb,
240+
stackerdb: &mut StackerDB<MessageSlotID>,
219241
client: &StacksClient,
220242
proposal_config: &ProposalEvalConfig,
221243
) -> Result<(), SignerChainstateError> {
@@ -256,7 +278,8 @@ impl LocalStateMachine {
256278
"inactive_tenure_ch" => %inactive_tenure_ch,
257279
"new_active_tenure_ch" => %new_active_tenure_ch
258280
);
259-
281+
// We have updated our state, so let other signers know.
282+
self.send_signer_update_message(stackerdb);
260283
Ok(())
261284
} else {
262285
warn!("Current miner timed out due to inactivity, but prior miner is not valid. Allowing current miner to continue");
@@ -324,6 +347,7 @@ impl LocalStateMachine {
324347
/// Handle a new stacks block arrival
325348
pub fn stacks_block_arrival(
326349
&mut self,
350+
stackerdb: &mut StackerDB<MessageSlotID>,
327351
ch: &ConsensusHash,
328352
height: u64,
329353
block_id: &StacksBlockId,
@@ -379,6 +403,8 @@ impl LocalStateMachine {
379403
*parent_tenure_last_block = *block_id;
380404
*parent_tenure_last_block_height = height;
381405
*self = LocalStateMachine::Initialized(prior_state_machine);
406+
// We updated the block id and/or the height. Let other signers know our view has changed
407+
self.send_signer_update_message(stackerdb);
382408
Ok(())
383409
}
384410

@@ -426,14 +452,15 @@ impl LocalStateMachine {
426452
pub fn bitcoin_block_arrival(
427453
&mut self,
428454
db: &SignerDb,
455+
stackerdb: &mut StackerDB<MessageSlotID>,
429456
client: &StacksClient,
430457
proposal_config: &ProposalEvalConfig,
431458
mut expected_burn_height: Option<u64>,
432459
) -> Result<(), SignerChainstateError> {
433460
// set self to uninitialized so that if this function errors,
434461
// self is left as uninitialized.
435462
let prior_state = std::mem::replace(self, Self::Uninitialized);
436-
let prior_state_machine = match prior_state {
463+
let prior_state_machine = match prior_state.clone() {
437464
// if the local state machine was uninitialized, just initialize it
438465
LocalStateMachine::Uninitialized => Self::place_holder(),
439466
LocalStateMachine::Initialized(signer_state_machine) => signer_state_machine,
@@ -512,6 +539,10 @@ impl LocalStateMachine {
512539
active_signer_protocol_version: prior_state_machine.active_signer_protocol_version,
513540
});
514541

542+
if prior_state != *self {
543+
self.send_signer_update_message(stackerdb);
544+
}
545+
515546
Ok(())
516547
}
517548
}

testnet/stacks-node/src/tests/epoch_205.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use std::collections::HashMap;
2-
use std::sync::atomic::Ordering;
32
use std::{env, thread};
43

54
use clarity::vm::costs::ExecutionCost;
@@ -21,14 +20,12 @@ use stacks::core::{
2120
use stacks_common::codec::StacksMessageCodec;
2221
use stacks_common::types::chainstate::{BlockHeaderHash, BurnchainHeaderHash, VRFSeed};
2322
use stacks_common::util::hash::hex_bytes;
24-
use stacks_common::util::sleep_ms;
2523

2624
use crate::tests::bitcoin_regtest::BitcoinCoreController;
2725
use crate::tests::neon_integrations::*;
2826
use crate::tests::{
2927
make_contract_call, make_contract_call_mblock_only, make_contract_publish,
30-
make_contract_publish_microblock_only, run_until_burnchain_height, select_transactions_where,
31-
to_addr,
28+
run_until_burnchain_height, select_transactions_where, to_addr,
3229
};
3330
use crate::{neon, BitcoinRegtestController, BurnchainController, Keychain};
3431

testnet/stacks-node/src/tests/neon_integrations.rs

Lines changed: 3 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ use clarity::vm::costs::ExecutionCost;
1111
use clarity::vm::types::serialization::SerializationError;
1212
use clarity::vm::types::PrincipalData;
1313
use clarity::vm::{ClarityName, ClarityVersion, ContractName, Value, MAX_CALL_STACK_DEPTH};
14-
use rand::Rng;
1514
use rusqlite::params;
1615
use serde::Deserialize;
1716
use serde_json::json;
@@ -30,12 +29,11 @@ use stacks::chainstate::stacks::address::PoxAddress;
3029
use stacks::chainstate::stacks::boot::POX_4_NAME;
3130
use stacks::chainstate::stacks::db::StacksChainState;
3231
use stacks::chainstate::stacks::miner::{
33-
signal_mining_blocked, signal_mining_ready, TransactionErrorEvent, TransactionEvent,
34-
TransactionSuccessEvent,
32+
TransactionErrorEvent, TransactionEvent, TransactionSuccessEvent,
3533
};
3634
use stacks::chainstate::stacks::{
37-
StacksBlock, StacksBlockHeader, StacksMicroblock, StacksMicroblockHeader, StacksPrivateKey,
38-
StacksPublicKey, StacksTransaction, TransactionContractCall, TransactionPayload,
35+
StacksBlock, StacksBlockHeader, StacksMicroblock, StacksPrivateKey, StacksPublicKey,
36+
StacksTransaction, TransactionContractCall, TransactionPayload,
3937
};
4038
use stacks::clarity_cli::vm_execute as execute;
4139
use stacks::cli;
@@ -90,8 +88,6 @@ use crate::stacks_common::types::PrivateKey;
9088
use crate::syncctl::PoxSyncWatchdogComms;
9189
use crate::tests::gen_random_port;
9290
use crate::tests::nakamoto_integrations::{get_key_for_cycle, wait_for};
93-
use crate::util::hash::{MerkleTree, Sha512Trunc256Sum};
94-
use crate::util::secp256k1::MessageSignature;
9591
use crate::{neon, BitcoinRegtestController, BurnchainController, Config, ConfigFile, Keychain};
9692

9793
fn inner_neon_integration_test_conf(seed: Option<Vec<u8>>) -> (Config, StacksAddress) {
@@ -3316,34 +3312,6 @@ fn should_fix_2771() {
33163312
channel.stop_chains_coordinator();
33173313
}
33183314

3319-
/// Returns a StacksMicroblock with the given transactions, sequence, and parent block that is
3320-
/// signed with the given private key.
3321-
fn make_signed_microblock(
3322-
block_privk: &StacksPrivateKey,
3323-
txs: Vec<StacksTransaction>,
3324-
parent_block: BlockHeaderHash,
3325-
seq: u16,
3326-
) -> StacksMicroblock {
3327-
let mut rng = rand::thread_rng();
3328-
3329-
let txid_vecs: Vec<_> = txs.iter().map(|tx| tx.txid().as_bytes().to_vec()).collect();
3330-
let merkle_tree = MerkleTree::<Sha512Trunc256Sum>::new(&txid_vecs);
3331-
let tx_merkle_root = merkle_tree.root();
3332-
3333-
let mut mblock = StacksMicroblock {
3334-
header: StacksMicroblockHeader {
3335-
version: rng.gen(),
3336-
sequence: seq,
3337-
prev_block: parent_block,
3338-
tx_merkle_root,
3339-
signature: MessageSignature([0u8; 65]),
3340-
},
3341-
txs,
3342-
};
3343-
mblock.sign(block_privk).unwrap();
3344-
mblock
3345-
}
3346-
33473315
#[test]
33483316
#[ignore]
33493317
fn filter_low_fee_tx_integration_test() {
@@ -8614,38 +8582,6 @@ pub fn make_random_tx_chain(
86148582
chain
86158583
}
86168584

8617-
fn make_mblock_tx_chain(privk: &StacksPrivateKey, fee_plus: u64, chain_id: u32) -> Vec<Vec<u8>> {
8618-
let addr = to_addr(privk);
8619-
let mut chain = vec![];
8620-
8621-
for nonce in 0..25 {
8622-
// N.B. private keys are 32-33 bytes, so this is always safe
8623-
let random_iters = privk.to_bytes()[nonce as usize] as usize;
8624-
8625-
let be_bytes = [
8626-
privk.to_bytes()[nonce as usize],
8627-
privk.to_bytes()[(nonce + 1) as usize],
8628-
];
8629-
8630-
let random_extra_fee = u16::from_be_bytes(be_bytes) as u64;
8631-
8632-
let mut addr_prefix = addr.to_string();
8633-
let _ = addr_prefix.split_off(12);
8634-
let contract_name = format!("crct-{nonce}-{addr_prefix}-{random_iters}");
8635-
eprintln!("Make tx {contract_name}");
8636-
let tx = make_contract_publish_microblock_only(
8637-
privk,
8638-
nonce,
8639-
1049230 + nonce + fee_plus + random_extra_fee,
8640-
chain_id,
8641-
&contract_name,
8642-
&make_runtime_sized_contract(1, nonce, &addr_prefix),
8643-
);
8644-
chain.push(tx);
8645-
}
8646-
chain
8647-
}
8648-
86498585
fn test_competing_miners_build_on_same_chain(
86508586
num_miners: usize,
86518587
conf_template: Config,

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1220,6 +1220,13 @@ impl<S: Signer<T> + Send + 'static, T: SignerEventTrait + 'static> SignerTest<Sp
12201220
.send_message_with_retry::<SignerMessage>(accepted.into())
12211221
.expect("Failed to send accept signature");
12221222
}
1223+
1224+
pub fn signer_public_keys(&self) -> Vec<StacksPublicKey> {
1225+
self.signer_stacks_private_keys
1226+
.iter()
1227+
.map(StacksPublicKey::from_private)
1228+
.collect()
1229+
}
12231230
}
12241231

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

0 commit comments

Comments
 (0)