Skip to content

Commit 8a8c650

Browse files
committed
CRC: add config.supported_signer_protocol_version and allow it to be overwritten in a test
Signed-off-by: Jacinta Ferrant <[email protected]>
1 parent 5f9238b commit 8a8c650

File tree

10 files changed

+133
-117
lines changed

10 files changed

+133
-117
lines changed

stacks-signer/CHANGELOG.md

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,6 @@ and this project adheres to the versioning scheme outlined in the [README.md](RE
77

88
## [Unreleased]
99

10-
### Changed
11-
12-
- Upgraded `SUPPORTED_SIGNER_PROTOCOL_VERSION` to 2
13-
1410
### Added
1511

1612
- Introduced `capitulate_miner_view_timeout_secs`: the duration (in seconds) for the signer to wait between updating the local state machine viewpoint and capitulating to other signers' miner views.

stacks-signer/src/client/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,8 @@ pub(crate) mod tests {
156156

157157
use super::*;
158158
use crate::config::{GlobalConfig, SignerConfig, SignerConfigMode};
159+
#[cfg(any(test, feature = "testing"))]
160+
use crate::v0::signer_state::SUPPORTED_SIGNER_PROTOCOL_VERSION;
159161

160162
pub struct MockServerClient {
161163
pub server: TcpListener,
@@ -433,6 +435,8 @@ pub(crate) mod tests {
433435
proposal_wait_for_parent_time: config.proposal_wait_for_parent_time,
434436
validate_with_replay_tx: config.validate_with_replay_tx,
435437
capitulate_miner_view_timeout: config.capitulate_miner_view_timeout,
438+
#[cfg(any(test, feature = "testing"))]
439+
supported_signer_protocol_version: SUPPORTED_SIGNER_PROTOCOL_VERSION,
436440
}
437441
}
438442

stacks-signer/src/config.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ use stacks_common::types::chainstate::{StacksAddress, StacksPrivateKey, StacksPu
3333
use stacks_common::util::hash::Hash160;
3434

3535
use crate::client::SignerSlotID;
36+
#[cfg(any(test, feature = "testing"))]
37+
use crate::v0::signer_state::SUPPORTED_SIGNER_PROTOCOL_VERSION;
3638

3739
const EVENT_TIMEOUT_MS: u64 = 5000;
3840
const BLOCK_PROPOSAL_TIMEOUT_MS: u64 = 120_000;
@@ -189,6 +191,9 @@ pub struct SignerConfig {
189191
pub validate_with_replay_tx: bool,
190192
/// Time to wait between updating our local state machine view point and capitulating to other signers miner view
191193
pub capitulate_miner_view_timeout: Duration,
194+
#[cfg(any(test, feature = "testing"))]
195+
/// Only used for testing purposes to enable overriding the signer version
196+
pub supported_signer_protocol_version: u64,
192197
}
193198

194199
/// The parsed configuration for the signer
@@ -244,6 +249,9 @@ pub struct GlobalConfig {
244249
pub validate_with_replay_tx: bool,
245250
/// Time to wait between updating our local state machine view point and capitulating to other signers miner view
246251
pub capitulate_miner_view_timeout: Duration,
252+
#[cfg(any(test, feature = "testing"))]
253+
/// Only used for testing to enable specific signer protocol versions
254+
pub supported_signer_protocol_version: u64,
247255
}
248256

249257
/// Internal struct for loading up the config file
@@ -297,6 +305,9 @@ struct RawConfigFile {
297305
pub validate_with_replay_tx: Option<bool>,
298306
/// Time to wait (in secs) between updating our local state machine view point and capitulating to other signers miner view
299307
pub capitulate_miner_view_timeout_secs: Option<u64>,
308+
#[cfg(any(test, feature = "testing"))]
309+
/// Only used for testing to enable specific signer protocol versions
310+
pub supported_signer_protocol_version: Option<u64>,
300311
}
301312

302313
impl RawConfigFile {
@@ -428,6 +439,11 @@ impl TryFrom<RawConfigFile> for GlobalConfig {
428439
.unwrap_or(DEFAULT_CAPITULATE_MINER_VIEW_SECS),
429440
);
430441

442+
#[cfg(any(test, feature = "testing"))]
443+
let supported_signer_protocol_version = raw_data
444+
.supported_signer_protocol_version
445+
.unwrap_or(SUPPORTED_SIGNER_PROTOCOL_VERSION);
446+
431447
Ok(Self {
432448
node_host: raw_data.node_host,
433449
endpoint,
@@ -451,6 +467,8 @@ impl TryFrom<RawConfigFile> for GlobalConfig {
451467
proposal_wait_for_parent_time,
452468
validate_with_replay_tx,
453469
capitulate_miner_view_timeout,
470+
#[cfg(any(test, feature = "testing"))]
471+
supported_signer_protocol_version,
454472
})
455473
}
456474
}

stacks-signer/src/runloop.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,8 @@ impl<Signer: SignerTrait<T>, T: StacksMessageCodec + Clone + Send + Debug> RunLo
330330
proposal_wait_for_parent_time: self.config.proposal_wait_for_parent_time,
331331
validate_with_replay_tx: self.config.validate_with_replay_tx,
332332
capitulate_miner_view_timeout: self.config.capitulate_miner_view_timeout,
333+
#[cfg(any(test, feature = "testing"))]
334+
supported_signer_protocol_version: self.config.supported_signer_protocol_version,
333335
}))
334336
}
335337

stacks-signer/src/v0/signer.rs

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,15 +45,15 @@ use stacks_common::util::secp256k1::MessageSignature;
4545
use stacks_common::{debug, error, info, warn};
4646

4747
use super::signer_state::LocalStateMachine;
48-
#[cfg(not(any(test, feature = "testing")))]
49-
use super::signer_state::SUPPORTED_SIGNER_PROTOCOL_VERSION;
5048
use crate::chainstate::v1::{SortitionMinerStatus, SortitionsView};
5149
use crate::chainstate::v2::GlobalStateView;
5250
use crate::chainstate::{ProposalEvalConfig, SortitionData, SortitionStateVersion};
5351
use crate::client::{ClientError, SignerSlotID, StackerDB, StacksClient};
5452
use crate::config::{SignerConfig, SignerConfigMode};
5553
use crate::runloop::SignerResult;
5654
use crate::signerdb::{BlockInfo, BlockState, SignerDb};
55+
#[cfg(not(any(test, feature = "testing")))]
56+
use crate::v0::signer_state::SUPPORTED_SIGNER_PROTOCOL_VERSION;
5757
use crate::v0::signer_state::{NewBurnBlock, ReplayScopeOpt};
5858
use crate::Signer as SignerTrait;
5959

@@ -131,6 +131,9 @@ pub struct Signer {
131131
pub tx_replay_scope: ReplayScopeOpt,
132132
/// Time to wait between updating our local state machine view point and capitulating to other signers miner view
133133
pub capitulate_miner_view_timeout: Duration,
134+
/// The signer supported protocol version. used only in testing
135+
#[cfg(any(test, feature = "testing"))]
136+
pub supported_signer_protocol_version: u64,
134137
}
135138

136139
impl std::fmt::Display for SignerMode {
@@ -223,11 +226,16 @@ impl SignerTrait<SignerMessage> for Signer {
223226
updates,
224227
signer_config.signer_entries.signer_addr_to_weight.clone(),
225228
);
229+
#[cfg(any(test, feature = "testing"))]
230+
let version = signer_config.supported_signer_protocol_version;
231+
#[cfg(not(any(test, feature = "testing")))]
232+
let version = SUPPORTED_SIGNER_PROTOCOL_VERSION;
226233
let signer_state = LocalStateMachine::new(
227234
&signer_db,
228235
stacks_client,
229236
&proposal_config,
230237
&global_state_evaluator,
238+
version,
231239
)
232240
.unwrap_or_else(|e| {
233241
warn!("Failed to initialize local state machine for signer: {e:?}");
@@ -254,6 +262,8 @@ impl SignerTrait<SignerMessage> for Signer {
254262
validate_with_replay_tx: signer_config.validate_with_replay_tx,
255263
tx_replay_scope: None,
256264
capitulate_miner_view_timeout: signer_config.capitulate_miner_view_timeout,
265+
#[cfg(any(test, feature = "testing"))]
266+
supported_signer_protocol_version: signer_config.supported_signer_protocol_version,
257267
}
258268
}
259269

@@ -275,19 +285,19 @@ impl SignerTrait<SignerMessage> for Signer {
275285
self.check_pending_block_validations(stacks_client);
276286

277287
let mut prior_state = self.local_state_machine.clone();
288+
let local_signer_protocol_version = self.get_signer_protocol_version();
278289
if self.reward_cycle <= current_reward_cycle {
279290
self.local_state_machine.handle_pending_update(&self.signer_db, stacks_client,
280291
&self.proposal_config,
281-
&mut self.tx_replay_scope, &self.global_state_evaluator)
292+
&mut self.tx_replay_scope, &self.global_state_evaluator, local_signer_protocol_version)
282293
.unwrap_or_else(|e| error!("{self}: failed to update local state machine for pending update"; "err" => ?e));
283294
}
284295
// See if we should capitulate our viewpoint...
285-
let version = self.get_signer_protocol_version();
286296
self.local_state_machine.capitulate_viewpoint(
287297
stacks_client,
288298
&mut self.signer_db,
289299
&mut self.global_state_evaluator,
290-
version,
300+
local_signer_protocol_version,
291301
self.reward_cycle,
292302
sortition_state,
293303
self.capitulate_miner_view_timeout,
@@ -546,13 +556,14 @@ impl Signer {
546556
panic!("{self} Failed to write burn block event to signerdb: {e}");
547557
});
548558

559+
let active_signer_protocol_version = self.get_signer_protocol_version();
549560
self.local_state_machine
550561
.bitcoin_block_arrival(&self.signer_db, stacks_client, &self.proposal_config, Some(NewBurnBlock {
551562
burn_block_height: *burn_height,
552563
consensus_hash: *consensus_hash,
553564
}),
554565
&mut self.tx_replay_scope
555-
, &self.global_state_evaluator)
566+
, &self.global_state_evaluator, active_signer_protocol_version)
556567
.unwrap_or_else(|e| error!("{self}: failed to update local state machine for latest bitcoin block arrival"; "err" => ?e));
557568
*sortition_state = None;
558569
}
@@ -1809,12 +1820,20 @@ impl Signer {
18091820

18101821
#[cfg(not(any(test, feature = "testing")))]
18111822
fn get_signer_protocol_version(&self) -> u64 {
1812-
SUPPORTED_SIGNER_PROTOCOL_VERSION
1823+
crate::v0::signer_state::SUPPORTED_SIGNER_PROTOCOL_VERSION
18131824
}
18141825

18151826
#[cfg(any(test, feature = "testing"))]
18161827
fn get_signer_protocol_version(&self) -> u64 {
1817-
self.test_get_signer_protocol_version()
1828+
use crate::v0::tests::TEST_PIN_SUPPORTED_SIGNER_PROTOCOL_VERSION;
1829+
let public_keys = TEST_PIN_SUPPORTED_SIGNER_PROTOCOL_VERSION.get();
1830+
if let Some(version) = public_keys.get(
1831+
&stacks_common::types::chainstate::StacksPublicKey::from_private(&self.private_key),
1832+
) {
1833+
warn!("{self}: signer version is pinned to {version}");
1834+
return *version;
1835+
}
1836+
self.supported_signer_protocol_version
18181837
}
18191838
}
18201839

stacks-signer/src/v0/signer_state.rs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ use crate::client::{ClientError, CurrentAndLastSortition, StackerDB, StacksClien
4646
use crate::signerdb::{BlockValidatedByReplaySet, SignerDb};
4747

4848
/// This is the latest supported protocol version for this signer binary
49-
pub static SUPPORTED_SIGNER_PROTOCOL_VERSION: u64 = 2;
49+
pub static SUPPORTED_SIGNER_PROTOCOL_VERSION: u64 = 1;
5050
/// The version at which global signer state activates
5151
pub static GLOBAL_SIGNER_STATE_ACTIVATION_VERSION: u64 = u64::MAX;
5252

@@ -141,9 +141,18 @@ impl LocalStateMachine {
141141
client: &StacksClient,
142142
proposal_config: &ProposalEvalConfig,
143143
eval: &GlobalStateEvaluator,
144+
active_signer_protocol_version: u64,
144145
) -> Result<Self, SignerChainstateError> {
145146
let mut instance = Self::Uninitialized;
146-
instance.bitcoin_block_arrival(db, client, proposal_config, None, &mut None, eval)?;
147+
instance.bitcoin_block_arrival(
148+
db,
149+
client,
150+
proposal_config,
151+
None,
152+
&mut None,
153+
eval,
154+
active_signer_protocol_version,
155+
)?;
147156

148157
Ok(instance)
149158
}
@@ -207,12 +216,12 @@ impl LocalStateMachine {
207216
)
208217
}
209218

210-
fn place_holder() -> SignerStateMachine {
219+
fn place_holder(version: u64) -> SignerStateMachine {
211220
SignerStateMachine {
212221
burn_block: ConsensusHash::empty(),
213222
burn_block_height: 0,
214223
current_miner: MinerState::NoValidMiner,
215-
active_signer_protocol_version: SUPPORTED_SIGNER_PROTOCOL_VERSION,
224+
active_signer_protocol_version: version,
216225
tx_replay_set: ReplayTransactionSet::none(),
217226
update_time: UpdateTime::now(),
218227
}
@@ -266,6 +275,7 @@ impl LocalStateMachine {
266275
proposal_config: &ProposalEvalConfig,
267276
tx_replay_scope: &mut ReplayScopeOpt,
268277
eval: &GlobalStateEvaluator,
278+
local_signer_protocol_version: u64,
269279
) -> Result<(), SignerChainstateError> {
270280
let LocalStateMachine::Pending { update, .. } = self else {
271281
return self.check_miner_inactivity(db, client, proposal_config, eval);
@@ -278,6 +288,7 @@ impl LocalStateMachine {
278288
Some(expected_burn_height),
279289
tx_replay_scope,
280290
eval,
291+
local_signer_protocol_version,
281292
),
282293
}
283294
}
@@ -549,6 +560,7 @@ impl LocalStateMachine {
549560
}
550561

551562
/// Handle a new bitcoin block arrival
563+
#[allow(clippy::too_many_arguments)]
552564
pub fn bitcoin_block_arrival(
553565
&mut self,
554566
db: &SignerDb,
@@ -557,13 +569,14 @@ impl LocalStateMachine {
557569
mut expected_burn_block: Option<NewBurnBlock>,
558570
tx_replay_scope: &mut ReplayScopeOpt,
559571
eval: &GlobalStateEvaluator,
572+
local_signer_protocol_version: u64,
560573
) -> Result<(), SignerChainstateError> {
561574
// set self to uninitialized so that if this function errors,
562575
// self is left as uninitialized.
563576
let prior_state = std::mem::replace(self, Self::Uninitialized);
564577
let prior_state_machine = match prior_state.clone() {
565578
// if the local state machine was uninitialized, just initialize it
566-
LocalStateMachine::Uninitialized => Self::place_holder(),
579+
LocalStateMachine::Uninitialized => Self::place_holder(local_signer_protocol_version),
567580
LocalStateMachine::Initialized(signer_state_machine) => signer_state_machine,
568581
LocalStateMachine::Pending { update, prior } => {
569582
// This works as long as the pending updates are only burn blocks,

stacks-signer/src/v0/tests.rs

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ use stacks_common::util::tests::TestFlag;
2525
use stacks_common::{info, warn};
2626

2727
use super::signer::Signer;
28-
use super::signer_state::SUPPORTED_SIGNER_PROTOCOL_VERSION;
2928
use crate::signerdb::BlockInfo;
3029

3130
/// A global variable that can be used to pin a signer's highest supported protocol version if the signer's public key is in the provided list
@@ -158,16 +157,4 @@ impl Signer {
158157
warn!("{self}: Block validation submission is no longer stalled due to testing directive. Continuing...");
159158
}
160159
}
161-
162-
/// Get the pinned signer version for the signer
163-
pub fn test_get_signer_protocol_version(&self) -> u64 {
164-
let public_keys = TEST_PIN_SUPPORTED_SIGNER_PROTOCOL_VERSION.get();
165-
if let Some(version) = public_keys.get(
166-
&stacks_common::types::chainstate::StacksPublicKey::from_private(&self.private_key),
167-
) {
168-
warn!("{self}: signer version is pinned to {version}");
169-
return *version;
170-
}
171-
SUPPORTED_SIGNER_PROTOCOL_VERSION
172-
}
173160
}

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1343,6 +1343,20 @@ impl<Z: SpawnedSignerTrait> SignerTest<Z> {
13431343
.collect()
13441344
}
13451345

1346+
/// Get the signer addresses and corresponding versions
1347+
pub fn signer_addresses_versions(&self) -> Vec<(StacksAddress, u64)> {
1348+
self.signer_stacks_private_keys
1349+
.iter()
1350+
.zip(self.signer_configs.clone())
1351+
.map(|(privk, config)| {
1352+
(
1353+
StacksAddress::p2pkh(false, &StacksPublicKey::from_private(privk)),
1354+
config.supported_signer_protocol_version,
1355+
)
1356+
})
1357+
.collect()
1358+
}
1359+
13461360
/// Get the signer public keys for the given reward cycle
13471361
fn get_signer_public_keys(&self, reward_cycle: u64) -> Vec<StacksPublicKey> {
13481362
let entries = self.get_reward_set_signers(reward_cycle);

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

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use stacks::util::secp256k1::Secp256k1PrivateKey;
2626
use stacks_common::types::chainstate::{ConsensusHash, StacksBlockId};
2727
use stacks_common_v3_1_00_13::codec::StacksMessageCodec as OldStacksMessageCodec;
2828
use stacks_signer::runloop::{RewardCycleInfo, State, StateInfo};
29-
use stacks_signer::v0::signer_state::{LocalStateMachine, SUPPORTED_SIGNER_PROTOCOL_VERSION};
29+
use stacks_signer::v0::signer_state::LocalStateMachine;
3030
use stacks_signer::v0::SpawnedSigner;
3131
use {libsigner_v3_1_0_0_13, signer_v3_1_0_0_13, stacks_common_v3_1_00_13, stacks_v3_1_00_13};
3232

@@ -271,10 +271,10 @@ impl SpawnedSignerTrait for MultiverSpawnedSigner {
271271
#[test]
272272
#[ignore]
273273
fn with_new_miner_and_old_signers() {
274-
with_new_miners::<MultiverSpawnedSigner>();
274+
with_new_miners::<MultiverSpawnedSigner>(2);
275275
}
276276

277-
fn with_new_miners<S: SpawnedSignerTrait>() {
277+
fn with_new_miners<S: SpawnedSignerTrait>(supported_signer_protocol_version: u64) {
278278
let sender_sk = Secp256k1PrivateKey::from_seed(&[0xde, 0xad, 0xbe, 0xef, 0xaa, 0xbb]);
279279
let sender_addr = tests::to_addr(&sender_sk);
280280
let send_amt = 1000;
@@ -297,7 +297,9 @@ fn with_new_miners<S: SpawnedSignerTrait>() {
297297
let signer_test: SignerTest<S> = SignerTest::new_with_config_modifications(
298298
num_signers,
299299
initial_balances,
300-
|_| {},
300+
|signer_config| {
301+
signer_config.supported_signer_protocol_version = supported_signer_protocol_version;
302+
},
301303
|config| {
302304
config.node.rpc_bind = format!("{localhost}:{node_1_rpc}");
303305
config.node.p2p_bind = format!("{localhost}:{node_1_p2p}");
@@ -375,7 +377,7 @@ fn with_new_miners<S: SpawnedSignerTrait>() {
375377
else {
376378
return false;
377379
};
378-
local_supported_signer_protocol_version == SUPPORTED_SIGNER_PROTOCOL_VERSION
380+
local_supported_signer_protocol_version == supported_signer_protocol_version
379381
})
380382
.count();
381383

0 commit comments

Comments
 (0)