Skip to content

Commit e0875ed

Browse files
committed
Merge branch 'develop' into feat/propagate-vm-error
2 parents 0d0e3b2 + a1e0b25 commit e0875ed

File tree

32 files changed

+1771
-439
lines changed

32 files changed

+1771
-439
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to the versioning scheme outlined in the [README.md](RE
1010
### Added
1111

1212
- Added new `ValidateRejectCode` values to the `/v3/block_proposal` endpoint
13+
- Added `StateMachineUpdateContent::V1` to support a vector of `StacksTransaction` expected to be replayed in subsequent Stacks blocks
1314
- Include a reason string in the transaction receipt when a transaction is rolled back due to a post-condition. This should help users in understanding what went wrong.
1415

1516
### Changed

CONTRIBUTING.md

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -361,17 +361,31 @@ A non-exhaustive list of examples of consensus-critical changes include:
361361

362362
- Every consensus-critical change needs an integration test to verify that the feature activates only when the hard fork activates.
363363

364-
PRs must include test coverage. However, if your PR includes large tests or tests which cannot run in parallel
364+
- PRs must include test coverage. However, if your PR includes large tests or tests which cannot run in parallel
365365
(which is the default operation of the `cargo test` command), these tests should be decorated with `#[ignore]`.
366-
367366
A test should be marked `#[ignore]` if:
368367

369-
1. It does not _always_ pass `cargo test` in a vanilla environment
368+
1. It does not _always_ pass `cargo test` in a vanilla environment
370369
(i.e., it does not need to run with `--test-threads 1`).
371370

372-
2. Or, it runs for over a minute via a normal `cargo test` execution
371+
2. Or, it runs for over a minute via a normal `cargo test` execution
373372
(the `cargo test` command will warn if this is not the case).
374373

374+
- **Integration tests need to be properly tagged** using [pinny-rs](https://github.com/BitcoinL2-Labs/pinny-rs/) crate. Tagging requires two fundamental steps:
375+
1. Define allowed tags in the package `Cargo.toml` file (if needed).
376+
2. Apply relevant tags to the tests, picking from the allowed set.
377+
378+
Then it will be possible to run tests with filtering based on the tags using `cargo test` and `cargo nextest` runner.
379+
> For more information and examples on how tagging works, refer to the [pinny-rs](https://github.com/BitcoinL2-Labs/pinny-rs/) readme.
380+
381+
Below the tag set currently defined with related purpose:
382+
383+
| Tag | Description |
384+
|-----------------|----------------------------------------------|
385+
| `slow` | tests running over a minute |
386+
| `bitcoind` | tests requiring bitcoin daemon |
387+
| `flaky` | tests that exhibit flaky behavior |
388+
375389
## Formatting
376390

377391
PRs will be checked against `rustfmt` and will _fail_ if not properly formatted.

Cargo.lock

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

clarity/src/vm/database/sqlite.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -254,16 +254,13 @@ impl SqliteConnection {
254254

255255
Ok(())
256256
}
257+
257258
pub fn memory() -> Result<Connection> {
258259
let contract_db = SqliteConnection::inner_open(":memory:")?;
259260
SqliteConnection::initialize_conn(&contract_db)?;
260261
Ok(contract_db)
261262
}
262-
pub fn open(filename: &str) -> Result<Connection> {
263-
let contract_db = SqliteConnection::inner_open(filename)?;
264-
SqliteConnection::check_schema(&contract_db)?;
265-
Ok(contract_db)
266-
}
263+
267264
pub fn check_schema(conn: &Connection) -> Result<()> {
268265
let sql = "SELECT sql FROM sqlite_master WHERE name=?";
269266
let _: String = conn
@@ -272,10 +269,13 @@ impl SqliteConnection {
272269
let _: String = conn
273270
.query_row(sql, params!["metadata_table"], |row| row.get(0))
274271
.map_err(|x| InterpreterError::SqliteError(IncomparableError { err: x }))?;
272+
let _: String = conn
273+
.query_row(sql, params!["md_blockhashes"], |row| row.get(0))
274+
.map_err(|x| InterpreterError::SqliteError(IncomparableError { err: x }))?;
275275
Ok(())
276276
}
277277

278-
pub fn inner_open(filename: &str) -> Result<Connection> {
278+
fn inner_open(filename: &str) -> Result<Connection> {
279279
let conn = Connection::open(filename)
280280
.map_err(|x| InterpreterError::SqliteError(IncomparableError { err: x }))?;
281281

libsigner/src/events.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,7 @@ struct BlockEvent {
605605
#[serde(with = "prefix_hex")]
606606
index_block_hash: StacksBlockId,
607607
#[serde(with = "prefix_opt_hex")]
608+
#[serde(default)]
608609
signer_signature_hash: Option<Sha512Trunc256Sum>,
609610
#[serde(with = "prefix_hex")]
610611
consensus_hash: ConsensusHash,

libsigner/src/v0/messages.rs

Lines changed: 120 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,17 @@ pub enum StateMachineUpdateContent {
571571
/// The signer's view of who the current miner should be (and their tenure building info)
572572
current_miner: StateMachineUpdateMinerState,
573573
},
574+
/// Version 1
575+
V1 {
576+
/// The tip burn block (i.e., the latest bitcoin block) seen by this signer
577+
burn_block: ConsensusHash,
578+
/// The tip burn block height (i.e., the latest bitcoin block) seen by this signer
579+
burn_block_height: u64,
580+
/// The signer's view of who the current miner should be (and their tenure building info)
581+
current_miner: StateMachineUpdateMinerState,
582+
/// The replay transactions
583+
replay_transactions: Vec<StacksTransaction>,
584+
},
574585
}
575586

576587
/// Message for update the Signer State infos
@@ -676,6 +687,7 @@ impl StateMachineUpdateContent {
676687
fn is_protocol_version_compatible(&self, version: u64) -> bool {
677688
match self {
678689
Self::V0 { .. } => version == 0,
690+
Self::V1 { .. } => version == 1,
679691
}
680692
}
681693

@@ -690,6 +702,17 @@ impl StateMachineUpdateContent {
690702
burn_block_height.consensus_serialize(fd)?;
691703
current_miner.consensus_serialize(fd)?;
692704
}
705+
Self::V1 {
706+
burn_block,
707+
burn_block_height,
708+
current_miner,
709+
replay_transactions,
710+
} => {
711+
burn_block.consensus_serialize(fd)?;
712+
burn_block_height.consensus_serialize(fd)?;
713+
current_miner.consensus_serialize(fd)?;
714+
replay_transactions.consensus_serialize(fd)?;
715+
}
693716
}
694717
Ok(())
695718
}
@@ -705,6 +728,18 @@ impl StateMachineUpdateContent {
705728
current_miner,
706729
})
707730
}
731+
1 => {
732+
let burn_block = read_next(fd)?;
733+
let burn_block_height = read_next(fd)?;
734+
let current_miner = read_next(fd)?;
735+
let replay_transactions = read_next(fd)?;
736+
Ok(Self::V1 {
737+
burn_block,
738+
burn_block_height,
739+
current_miner,
740+
replay_transactions,
741+
})
742+
}
708743
other => Err(CodecError::DeserializeError(format!(
709744
"Unknown state machine update version: {other}"
710745
))),
@@ -1716,15 +1751,17 @@ impl From<StateMachineUpdate> for SignerMessage {
17161751
mod test {
17171752
use blockstack_lib::chainstate::nakamoto::NakamotoBlockHeader;
17181753
use blockstack_lib::chainstate::stacks::{
1719-
TransactionAnchorMode, TransactionAuth, TransactionPayload, TransactionPostConditionMode,
1720-
TransactionSmartContract, TransactionVersion,
1754+
TransactionAnchorMode, TransactionAuth, TransactionContractCall, TransactionPayload,
1755+
TransactionPostConditionMode, TransactionSmartContract, TransactionSpendingCondition,
1756+
TransactionVersion,
17211757
};
17221758
use blockstack_lib::util_lib::strings::StacksString;
17231759
use clarity::consts::CHAIN_ID_MAINNET;
1724-
use clarity::types::chainstate::{ConsensusHash, StacksBlockId, TrieHash};
1760+
use clarity::types::chainstate::{ConsensusHash, StacksAddress, StacksBlockId, TrieHash};
17251761
use clarity::types::PrivateKey;
17261762
use clarity::util::hash::{hex_bytes, MerkleTree};
17271763
use clarity::util::secp256k1::MessageSignature;
1764+
use clarity::vm::{ClarityName, ContractName};
17281765
use rand::rngs::mock;
17291766
use rand::{thread_rng, Rng, RngCore};
17301767
use rand_core::OsRng;
@@ -2316,4 +2353,84 @@ mod test {
23162353

23172354
assert_eq!(signer_message, signer_message_deserialized);
23182355
}
2356+
2357+
#[test]
2358+
fn deserialize_state_machine_update_v1() {
2359+
let signer_message = StateMachineUpdate::new(
2360+
1,
2361+
3,
2362+
StateMachineUpdateContent::V1 {
2363+
burn_block: ConsensusHash([0x55; 20]),
2364+
burn_block_height: 100,
2365+
current_miner: StateMachineUpdateMinerState::ActiveMiner {
2366+
current_miner_pkh: Hash160([0xab; 20]),
2367+
tenure_id: ConsensusHash([0x44; 20]),
2368+
parent_tenure_id: ConsensusHash([0x22; 20]),
2369+
parent_tenure_last_block: StacksBlockId([0x33; 32]),
2370+
parent_tenure_last_block_height: 1,
2371+
},
2372+
replay_transactions: vec![],
2373+
},
2374+
)
2375+
.unwrap();
2376+
2377+
let mut bytes = vec![];
2378+
signer_message.consensus_serialize(&mut bytes).unwrap();
2379+
2380+
// check for raw content for avoiding regressions when structure changes
2381+
let raw_signer_message: Vec<&[u8]> = vec![
2382+
/* active_signer_protocol_version*/ &[0, 0, 0, 0, 0, 0, 0, 1],
2383+
/* local_supported_signer_protocol_version*/ &[0, 0, 0, 0, 0, 0, 0, 3],
2384+
/* content_len*/ &[0, 0, 0, 133],
2385+
/* burn_block*/ &[0x55; 20],
2386+
/* burn_block_height*/ &[0, 0, 0, 0, 0, 0, 0, 100],
2387+
/* current_miner_variant */ &[0x01],
2388+
/* current_miner_pkh */ &[0xab; 20],
2389+
/* tenure_id*/ &[0x44; 20],
2390+
/* parent_tenure_id*/ &[0x22; 20],
2391+
/* parent_tenure_last_block */ &[0x33; 32],
2392+
/* parent_tenure_last_block_height*/ &[0, 0, 0, 0, 0, 0, 0, 1],
2393+
/* replay_transactions */ &[0, 0, 0, 0],
2394+
];
2395+
2396+
assert_eq!(bytes, raw_signer_message.concat());
2397+
2398+
let signer_message_deserialized =
2399+
StateMachineUpdate::consensus_deserialize(&mut &bytes[..]).unwrap();
2400+
2401+
assert_eq!(signer_message, signer_message_deserialized);
2402+
2403+
let signer_message = StateMachineUpdate::new(
2404+
1,
2405+
4,
2406+
StateMachineUpdateContent::V1 {
2407+
burn_block: ConsensusHash([0x55; 20]),
2408+
burn_block_height: 100,
2409+
current_miner: StateMachineUpdateMinerState::NoValidMiner,
2410+
replay_transactions: vec![],
2411+
},
2412+
)
2413+
.unwrap();
2414+
2415+
let mut bytes = vec![];
2416+
signer_message.consensus_serialize(&mut bytes).unwrap();
2417+
2418+
// check for raw content for avoiding regressions when structure changes
2419+
let raw_signer_message: Vec<&[u8]> = vec![
2420+
/* active_signer_protocol_version*/ &[0, 0, 0, 0, 0, 0, 0, 1],
2421+
/* local_supported_signer_protocol_version*/ &[0, 0, 0, 0, 0, 0, 0, 4],
2422+
/* content_len*/ &[0, 0, 0, 33],
2423+
/* burn_block*/ &[0x55; 20],
2424+
/* burn_block_height*/ &[0, 0, 0, 0, 0, 0, 0, 100],
2425+
/* current_miner_variant */ &[0x00],
2426+
/* replay_transactions */ &[0, 0, 0, 0],
2427+
];
2428+
2429+
assert_eq!(bytes, raw_signer_message.concat());
2430+
2431+
let signer_message_deserialized =
2432+
StateMachineUpdate::consensus_deserialize(&mut &bytes[..]).unwrap();
2433+
2434+
assert_eq!(signer_message, signer_message_deserialized);
2435+
}
23192436
}

stacks-signer/CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,19 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to the versioning scheme outlined in the [README.md](README.md).
77

8+
## [Unreleased]
9+
10+
### Changed
11+
12+
- Upgraded `SUPPORTED_SIGNER_PROTOCOL_VERSION` to 1
13+
14+
## [3.1.0.0.8.1]
15+
16+
### Added
17+
18+
- The signer will now check if their associated stacks-node has processed the parent block for a block proposal before submitting that block proposal. If it cannot confirm that the parent block has been processed, it waits a default time of 15s before submitting, configurable via `proposal_wait_for_parent_time_secs` in the signer config.toml.
19+
20+
821
## [3.1.0.0.8.0]
922

1023
### Changed

stacks-signer/src/chainstate.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,8 @@ pub struct ProposalEvalConfig {
141141
/// Time following the last block of the previous tenure's global acceptance that a signer will consider an attempt by
142142
/// the new miner to reorg it as valid towards miner activity
143143
pub reorg_attempts_activity_timeout: Duration,
144+
/// Time to wait before submitting a block proposal to the stacks-node
145+
pub proposal_wait_for_parent_time: Duration,
144146
}
145147

146148
impl From<&SignerConfig> for ProposalEvalConfig {
@@ -152,6 +154,7 @@ impl From<&SignerConfig> for ProposalEvalConfig {
152154
tenure_idle_timeout: value.tenure_idle_timeout,
153155
reorg_attempts_activity_timeout: value.reorg_attempts_activity_timeout,
154156
tenure_idle_timeout_buffer: value.tenure_idle_timeout_buffer,
157+
proposal_wait_for_parent_time: value.proposal_wait_for_parent_time,
155158
}
156159
}
157160
}

stacks-signer/src/client/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,7 @@ pub(crate) mod tests {
418418
tenure_idle_timeout_buffer: config.tenure_idle_timeout_buffer,
419419
block_proposal_max_age_secs: config.block_proposal_max_age_secs,
420420
reorg_attempts_activity_timeout: config.reorg_attempts_activity_timeout,
421+
proposal_wait_for_parent_time: config.proposal_wait_for_parent_time,
421422
}
422423
}
423424

stacks-signer/src/config.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ const DEFAULT_REORG_ATTEMPTS_ACTIVITY_TIMEOUT_MS: u64 = 200_000;
4545
/// Default number of seconds to add to the tenure extend time, after computing the idle timeout,
4646
/// to allow for clock skew between the signer and the miner
4747
const DEFAULT_TENURE_IDLE_TIMEOUT_BUFFER_SECS: u64 = 2;
48+
/// Default time (in ms) to wait before submitting a proposal if we
49+
/// cannot determine that our stacks-node has processed the parent
50+
/// block
51+
const DEFAULT_PROPOSAL_WAIT_TIME_FOR_PARENT_SECS: u64 = 15;
4852

4953
#[derive(thiserror::Error, Debug)]
5054
/// An error occurred parsing the provided configuration
@@ -175,6 +179,9 @@ pub struct SignerConfig {
175179
pub reorg_attempts_activity_timeout: Duration,
176180
/// The running mode for the signer (dry-run or normal)
177181
pub signer_mode: SignerConfigMode,
182+
/// Time to wait before submitting a block proposal to the stacks-node if we cannot
183+
/// determine that the stacks-node has processed the parent
184+
pub proposal_wait_for_parent_time: Duration,
178185
}
179186

180187
/// The parsed configuration for the signer
@@ -221,6 +228,9 @@ pub struct GlobalConfig {
221228
/// Time following the last block of the previous tenure's global acceptance that a signer will consider an attempt by
222229
/// the new miner to reorg it as valid towards miner activity
223230
pub reorg_attempts_activity_timeout: Duration,
231+
/// Time to wait before submitting a block proposal to the stacks-node if we cannot
232+
/// determine that the stacks-node has processed the parent
233+
pub proposal_wait_for_parent_time: Duration,
224234
/// Is this signer binary going to be running in dry-run mode?
225235
pub dry_run: bool,
226236
}
@@ -268,6 +278,8 @@ struct RawConfigFile {
268278
/// Time (in millisecs) following a block's global acceptance that a signer will consider an attempt by a miner
269279
/// to reorg the block as valid towards miner activity
270280
pub reorg_attempts_activity_timeout_ms: Option<u64>,
281+
/// Time to wait (in millisecs) before submitting a block proposal to the stacks-node
282+
pub proposal_wait_for_parent_time_secs: Option<u64>,
271283
/// Is this signer binary going to be running in dry-run mode?
272284
pub dry_run: Option<bool>,
273285
}
@@ -385,6 +397,12 @@ impl TryFrom<RawConfigFile> for GlobalConfig {
385397
.unwrap_or(DEFAULT_TENURE_IDLE_TIMEOUT_BUFFER_SECS),
386398
);
387399

400+
let proposal_wait_for_parent_time = Duration::from_secs(
401+
raw_data
402+
.proposal_wait_for_parent_time_secs
403+
.unwrap_or(DEFAULT_PROPOSAL_WAIT_TIME_FOR_PARENT_SECS),
404+
);
405+
388406
Ok(Self {
389407
node_host: raw_data.node_host,
390408
endpoint,
@@ -405,6 +423,7 @@ impl TryFrom<RawConfigFile> for GlobalConfig {
405423
reorg_attempts_activity_timeout,
406424
dry_run,
407425
tenure_idle_timeout_buffer,
426+
proposal_wait_for_parent_time,
408427
})
409428
}
410429
}

0 commit comments

Comments
 (0)