Skip to content

Commit

Permalink
Merge pull request #5470 from stacks-network/chore/calc-tenure-idle-t…
Browse files Browse the repository at this point in the history
…imestamp-with-config

Add timestamp calculation to all block responses
  • Loading branch information
jferrant authored Nov 18, 2024
2 parents 9308df9 + 89eaa85 commit e138c37
Show file tree
Hide file tree
Showing 11 changed files with 241 additions and 17 deletions.
1 change: 1 addition & 0 deletions stacks-signer/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ and this project adheres to the versioning scheme outlined in the [README.md](RE
### Changed

- Add tenure extend timestamp to signer block responses
- Added tenure_idle_timeout_secs configuration option for determining when a tenure extend will be accepted

## [3.0.0.0.1.0]

Expand Down
3 changes: 3 additions & 0 deletions stacks-signer/src/chainstate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ pub struct ProposalEvalConfig {
/// Time to wait for the last block of a tenure to be globally accepted or rejected before considering
/// a new miner's block at the same height as valid.
pub tenure_last_block_proposal_timeout: Duration,
/// How much idle time must pass before allowing a tenure extend
pub tenure_idle_timeout: Duration,
}

impl From<&SignerConfig> for ProposalEvalConfig {
Expand All @@ -130,6 +132,7 @@ impl From<&SignerConfig> for ProposalEvalConfig {
first_proposal_burn_block_timing: value.first_proposal_burn_block_timing,
block_proposal_timeout: value.block_proposal_timeout,
tenure_last_block_proposal_timeout: value.tenure_last_block_proposal_timeout,
tenure_idle_timeout: value.tenure_idle_timeout,
}
}
}
Expand Down
1 change: 1 addition & 0 deletions stacks-signer/src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,7 @@ pub(crate) mod tests {
block_proposal_timeout: config.block_proposal_timeout,
tenure_last_block_proposal_timeout: config.tenure_last_block_proposal_timeout,
block_proposal_validation_timeout: config.block_proposal_validation_timeout,
tenure_idle_timeout: config.tenure_idle_timeout,
}
}

Expand Down
14 changes: 14 additions & 0 deletions stacks-signer/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const BLOCK_PROPOSAL_TIMEOUT_MS: u64 = 600_000;
const BLOCK_PROPOSAL_VALIDATION_TIMEOUT_MS: u64 = 120_000;
const DEFAULT_FIRST_PROPOSAL_BURN_BLOCK_TIMING_SECS: u64 = 60;
const DEFAULT_TENURE_LAST_BLOCK_PROPOSAL_TIMEOUT_SECS: u64 = 30;
const TENURE_IDLE_TIMEOUT_SECS: u64 = 300;

#[derive(thiserror::Error, Debug)]
/// An error occurred parsing the provided configuration
Expand Down Expand Up @@ -135,6 +136,8 @@ pub struct SignerConfig {
pub tenure_last_block_proposal_timeout: Duration,
/// How much time to wait for a block proposal validation response before marking the block invalid
pub block_proposal_validation_timeout: Duration,
/// How much idle time must pass before allowing a tenure extend
pub tenure_idle_timeout: Duration,
}

/// The parsed configuration for the signer
Expand Down Expand Up @@ -171,6 +174,8 @@ pub struct GlobalConfig {
/// How long to wait for a response from a block proposal validation response from the node
/// before marking that block as invalid and rejecting it
pub block_proposal_validation_timeout: Duration,
/// How much idle time must pass before allowing a tenure extend
pub tenure_idle_timeout: Duration,
}

/// Internal struct for loading up the config file
Expand Down Expand Up @@ -206,6 +211,8 @@ struct RawConfigFile {
/// How long to wait (in millisecs) for a response from a block proposal validation response from the node
/// before marking that block as invalid and rejecting it
pub block_proposal_validation_timeout_ms: Option<u64>,
/// How much idle time (in seconds) must pass before a tenure extend is allowed
pub tenure_idle_timeout_secs: Option<u64>,
}

impl RawConfigFile {
Expand Down Expand Up @@ -297,6 +304,12 @@ impl TryFrom<RawConfigFile> for GlobalConfig {
.unwrap_or(BLOCK_PROPOSAL_VALIDATION_TIMEOUT_MS),
);

let tenure_idle_timeout = Duration::from_secs(
raw_data
.tenure_idle_timeout_secs
.unwrap_or(TENURE_IDLE_TIMEOUT_SECS),
);

Ok(Self {
node_host: raw_data.node_host,
endpoint,
Expand All @@ -312,6 +325,7 @@ impl TryFrom<RawConfigFile> for GlobalConfig {
chain_id: raw_data.chain_id,
tenure_last_block_proposal_timeout,
block_proposal_validation_timeout,
tenure_idle_timeout,
})
}
}
Expand Down
1 change: 1 addition & 0 deletions stacks-signer/src/runloop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@ impl<Signer: SignerTrait<T>, T: StacksMessageCodec + Clone + Send + Debug> RunLo
block_proposal_timeout: self.config.block_proposal_timeout,
tenure_last_block_proposal_timeout: self.config.tenure_last_block_proposal_timeout,
block_proposal_validation_timeout: self.config.block_proposal_validation_timeout,
tenure_idle_timeout: self.config.tenure_idle_timeout,
}))
}

Expand Down
151 changes: 151 additions & 0 deletions stacks-signer/src/signerdb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ pub struct BlockInfo {
pub signed_group: Option<u64>,
/// The block state relative to the signer's view of the stacks blockchain
pub state: BlockState,
/// Consumed processing time in milliseconds to validate this block
pub validation_time_ms: Option<u64>,
/// Extra data specific to v0, v1, etc.
pub ext: ExtraBlockInfo,
}
Expand All @@ -175,6 +177,7 @@ impl From<BlockProposal> for BlockInfo {
signed_group: None,
ext: ExtraBlockInfo::default(),
state: BlockState::Unprocessed,
validation_time_ms: None,
}
}
}
Expand Down Expand Up @@ -809,6 +812,20 @@ impl SignerDb {
BlockState::try_from(state.as_str()).map_err(|_| DBError::Corruption)?,
))
}

/// Return the all globally accepted block in a tenure (identified by its consensus hash).
pub fn get_globally_accepted_blocks(
&self,
tenure: &ConsensusHash,
) -> Result<Vec<BlockInfo>, DBError> {
let query = "SELECT block_info FROM blocks WHERE consensus_hash = ?1 AND json_extract(block_info, '$.state') = ?2 ORDER BY stacks_height DESC";
let args = params![tenure, &BlockState::GloballyAccepted.to_string()];
let result: Vec<String> = query_rows(&self.db, query, args)?;
result
.iter()
.map(|info| serde_json::from_str(info).map_err(DBError::from))
.collect()
}
}

fn try_deserialize<T>(s: Option<String>) -> Result<Option<T>, DBError>
Expand Down Expand Up @@ -1185,6 +1202,7 @@ mod tests {
12345
);
}

#[test]
fn state_machine() {
let (mut block, _) = create_block();
Expand Down Expand Up @@ -1226,4 +1244,137 @@ mod tests {
assert!(!block.check_state(BlockState::GloballyAccepted));
assert!(block.check_state(BlockState::GloballyRejected));
}

#[test]
fn get_accepted_blocks() {
let db_path = tmp_db_path();
let mut db = SignerDb::new(db_path).expect("Failed to create signer db");
let consensus_hash_1 = ConsensusHash([0x01; 20]);
let consensus_hash_2 = ConsensusHash([0x02; 20]);
let consensus_hash_3 = ConsensusHash([0x03; 20]);
let (mut block_info_1, _block_proposal) = create_block_override(|b| {
b.block.header.consensus_hash = consensus_hash_1;
b.block.header.miner_signature = MessageSignature([0x01; 65]);
b.block.header.chain_length = 1;
b.burn_height = 1;
});
let (mut block_info_2, _block_proposal) = create_block_override(|b| {
b.block.header.consensus_hash = consensus_hash_1;
b.block.header.miner_signature = MessageSignature([0x02; 65]);
b.block.header.chain_length = 2;
b.burn_height = 2;
});
let (mut block_info_3, _block_proposal) = create_block_override(|b| {
b.block.header.consensus_hash = consensus_hash_1;
b.block.header.miner_signature = MessageSignature([0x03; 65]);
b.block.header.chain_length = 3;
b.burn_height = 3;
});
let (mut block_info_4, _block_proposal) = create_block_override(|b| {
b.block.header.consensus_hash = consensus_hash_2;
b.block.header.miner_signature = MessageSignature([0x03; 65]);
b.block.header.chain_length = 3;
b.burn_height = 4;
});
block_info_1.mark_globally_accepted().unwrap();
block_info_2.mark_locally_accepted(false).unwrap();
block_info_3.mark_locally_accepted(false).unwrap();
block_info_4.mark_globally_accepted().unwrap();

db.insert_block(&block_info_1).unwrap();
db.insert_block(&block_info_2).unwrap();
db.insert_block(&block_info_3).unwrap();
db.insert_block(&block_info_4).unwrap();

// Verify tenure consensus_hash_1
let block_info = db
.get_last_accepted_block(&consensus_hash_1)
.unwrap()
.unwrap();
assert_eq!(block_info, block_info_3);
let block_info = db
.get_last_globally_accepted_block(&consensus_hash_1)
.unwrap()
.unwrap();
assert_eq!(block_info, block_info_1);

// Verify tenure consensus_hash_2
let block_info = db
.get_last_accepted_block(&consensus_hash_2)
.unwrap()
.unwrap();
assert_eq!(block_info, block_info_4);
let block_info = db
.get_last_globally_accepted_block(&consensus_hash_2)
.unwrap()
.unwrap();
assert_eq!(block_info, block_info_4);

// Verify tenure consensus_hash_3
assert!(db
.get_last_accepted_block(&consensus_hash_3)
.unwrap()
.is_none());
assert!(db
.get_last_globally_accepted_block(&consensus_hash_3)
.unwrap()
.is_none());
}

#[test]
fn get_all_globally_accepted_blocks() {
let db_path = tmp_db_path();
let mut db = SignerDb::new(db_path).expect("Failed to create signer db");
let consensus_hash_1 = ConsensusHash([0x01; 20]);
let consensus_hash_2 = ConsensusHash([0x02; 20]);
let consensus_hash_3 = ConsensusHash([0x03; 20]);
let (mut block_info_1, _block_proposal) = create_block_override(|b| {
b.block.header.consensus_hash = consensus_hash_1;
b.block.header.miner_signature = MessageSignature([0x01; 65]);
b.block.header.chain_length = 1;
b.burn_height = 1;
});
let (mut block_info_2, _block_proposal) = create_block_override(|b| {
b.block.header.consensus_hash = consensus_hash_1;
b.block.header.miner_signature = MessageSignature([0x02; 65]);
b.block.header.chain_length = 2;
b.burn_height = 2;
});
let (mut block_info_3, _block_proposal) = create_block_override(|b| {
b.block.header.consensus_hash = consensus_hash_1;
b.block.header.miner_signature = MessageSignature([0x03; 65]);
b.block.header.chain_length = 3;
b.burn_height = 3;
});
let (mut block_info_4, _block_proposal) = create_block_override(|b| {
b.block.header.consensus_hash = consensus_hash_2;
b.block.header.miner_signature = MessageSignature([0x03; 65]);
b.block.header.chain_length = 3;
b.burn_height = 4;
});
block_info_1.mark_globally_accepted().unwrap();
block_info_2.mark_locally_accepted(false).unwrap();
block_info_3.mark_globally_accepted().unwrap();
block_info_4.mark_globally_accepted().unwrap();

db.insert_block(&block_info_1).unwrap();
db.insert_block(&block_info_2).unwrap();
db.insert_block(&block_info_3).unwrap();
db.insert_block(&block_info_4).unwrap();

// Verify tenure consensus_hash_1
let block_infos = db.get_globally_accepted_blocks(&consensus_hash_1).unwrap();
assert_eq!(block_infos, vec![block_info_3, block_info_1]);

// Verify tenure consensus_hash_2
let block_infos = db.get_globally_accepted_blocks(&consensus_hash_2).unwrap();
assert_eq!(block_infos.len(), 1);
assert_eq!(block_infos, vec![block_info_4]);

// Verify tenure consensus_hash_3
assert!(db
.get_globally_accepted_blocks(&consensus_hash_3)
.unwrap()
.is_empty());
}
}
1 change: 1 addition & 0 deletions stacks-signer/src/tests/chainstate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ fn setup_test_environment(
first_proposal_burn_block_timing: Duration::from_secs(30),
block_proposal_timeout: Duration::from_secs(5),
tenure_last_block_proposal_timeout: Duration::from_secs(30),
tenure_idle_timeout: Duration::from_secs(300),
},
};

Expand Down
Loading

0 comments on commit e138c37

Please sign in to comment.