Skip to content

Commit c488de2

Browse files
committed
Merge branch 'develop' of https://github.com/stacks-network/stacks-core into feat/miner-constructs-replay-block-feature-gated
2 parents 38e4856 + 38e389f commit c488de2

File tree

31 files changed

+1284
-291
lines changed

31 files changed

+1284
-291
lines changed

CHANGELOG.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,23 @@ and this project adheres to the versioning scheme outlined in the [README.md](RE
99

1010
### Added
1111

12+
- Added field `vm_error` to EventObserver transaction outputs
1213
- Added new `ValidateRejectCode` values to the `/v3/block_proposal` endpoint
1314
- Added `StateMachineUpdateContent::V1` to support a vector of `StacksTransaction` expected to be replayed in subsequent Stacks blocks
15+
- 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.
1416
- Updated `StackerDBListener` to monitor signer state machine updates and store replay information separately, enabling miners to perform transaction replays.
15-
- Added a `replay_transactions` flag to the miner configuration to feature-gate transaction replay. When enabled, the miner will construct a replay block if a threshold of signers signals that a transaction set requires replay.
17+
- Added a testnet `replay_transactions` flag to the miner configuration to feature-gate transaction replay. When enabled, the miner will construct a replay block if a threshold of signers signals that a transaction set requires replay.
1618

1719
### Changed
1820

1921
- Reduce the default `block_rejection_timeout_steps` configuration so that miners will retry faster when blocks fail to reach 70% approved or 30% rejected.
2022
- Added index for `next_ready_nakamoto_block()` which improves block processing performance.
2123
- Added a new field, `parent_burn_block_hash`, to the payload that is included in the `/new_burn_block` event observer payload.
2224

25+
### Fixed
26+
27+
- Fix regression in mock-mining, allowing the mock miner to continue mining blocks throughout a tenure instead of failing after mining the tenure change block.
28+
2329
## [3.1.0.0.8]
2430

2531
### Added

clarity/src/vm/clarity.rs

Lines changed: 49 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,30 @@ pub enum Error {
2020
Interpreter(InterpreterError),
2121
BadTransaction(String),
2222
CostError(ExecutionCost, ExecutionCost),
23-
AbortedByCallback(Option<Value>, AssetMap, Vec<StacksTransactionEvent>),
23+
AbortedByCallback {
24+
/// What the output value of the transaction would have been.
25+
/// This will be a Some for contract-calls, and None for contract initialization txs.
26+
output: Option<Value>,
27+
/// The asset map which was evaluated by the abort callback
28+
assets_modified: AssetMap,
29+
/// The events from the transaction processing
30+
tx_events: Vec<StacksTransactionEvent>,
31+
/// A human-readable explanation for aborting the transaction
32+
reason: String,
33+
},
2434
}
2535

2636
impl fmt::Display for Error {
2737
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
28-
match *self {
38+
match self {
2939
Error::CostError(ref a, ref b) => {
30-
write!(f, "Cost Error: {} cost exceeded budget of {} cost", a, b)
40+
write!(f, "Cost Error: {a} cost exceeded budget of {b} cost")
3141
}
3242
Error::Analysis(ref e) => fmt::Display::fmt(e, f),
3343
Error::Parse(ref e) => fmt::Display::fmt(e, f),
34-
Error::AbortedByCallback(..) => write!(f, "Post condition aborted transaction"),
44+
Error::AbortedByCallback { reason, .. } => {
45+
write!(f, "Post condition aborted transaction: {reason}")
46+
}
3547
Error::Interpreter(ref e) => fmt::Display::fmt(e, f),
3648
Error::BadTransaction(ref s) => fmt::Display::fmt(s, f),
3749
}
@@ -42,7 +54,7 @@ impl std::error::Error for Error {
4254
fn cause(&self) -> Option<&dyn std::error::Error> {
4355
match *self {
4456
Error::CostError(ref _a, ref _b) => None,
45-
Error::AbortedByCallback(..) => None,
57+
Error::AbortedByCallback { .. } => None,
4658
Error::Analysis(ref e) => Some(e),
4759
Error::Parse(ref e) => Some(e),
4860
Error::Interpreter(ref e) => Some(e),
@@ -168,16 +180,17 @@ pub trait TransactionConnection: ClarityConnection {
168180
/// * the asset changes during `to_do` in an `AssetMap`
169181
/// * the Stacks events during the transaction
170182
///
171-
/// and a `bool` value which is `true` if the `abort_call_back` caused the changes to abort.
183+
/// and an optional string value which is the result of `abort_call_back`,
184+
/// containing a human-readable reason for aborting the transaction.
172185
///
173186
/// If `to_do` returns an `Err` variant, then the changes are aborted.
174187
fn with_abort_callback<F, A, R, E>(
175188
&mut self,
176189
to_do: F,
177190
abort_call_back: A,
178-
) -> Result<(R, AssetMap, Vec<StacksTransactionEvent>, bool), E>
191+
) -> Result<(R, AssetMap, Vec<StacksTransactionEvent>, Option<String>), E>
179192
where
180-
A: FnOnce(&AssetMap, &mut ClarityDatabase) -> bool,
193+
A: FnOnce(&AssetMap, &mut ClarityDatabase) -> Option<String>,
181194
F: FnOnce(&mut OwnedEnvironment) -> Result<(R, AssetMap, Vec<StacksTransactionEvent>), E>,
182195
E: From<InterpreterError>;
183196

@@ -283,16 +296,16 @@ pub trait TransactionConnection: ClarityConnection {
283296
.stx_transfer(from, to, amount, memo)
284297
.map_err(Error::from)
285298
},
286-
|_, _| false,
299+
|_, _| None,
287300
)
288301
.map(|(value, assets, events, _)| (value, assets, events))
289302
}
290303

291304
/// Execute a contract call in the current block.
292-
/// If an error occurs while processing the transaction, its modifications will be rolled back.
293-
/// abort_call_back is called with an AssetMap and a ClarityDatabase reference,
294-
/// if abort_call_back returns true, all modifications from this transaction will be rolled back.
295-
/// otherwise, they will be committed (though they may later be rolled back if the block itself is rolled back).
305+
/// If an error occurs while processing the transaction, its modifications will be rolled back.
306+
/// `abort_call_back` is called with an `AssetMap` and a `ClarityDatabase` reference,
307+
/// If `abort_call_back` returns `Some(reason)`, all modifications from this transaction will be rolled back.
308+
/// Otherwise, they will be committed (though they may later be rolled back if the block itself is rolled back).
296309
#[allow(clippy::too_many_arguments)]
297310
fn run_contract_call<F>(
298311
&mut self,
@@ -305,7 +318,7 @@ pub trait TransactionConnection: ClarityConnection {
305318
max_execution_time: Option<std::time::Duration>,
306319
) -> Result<(Value, AssetMap, Vec<StacksTransactionEvent>), Error>
307320
where
308-
F: FnOnce(&AssetMap, &mut ClarityDatabase) -> bool,
321+
F: FnOnce(&AssetMap, &mut ClarityDatabase) -> Option<String>,
309322
{
310323
let expr_args: Vec<_> = args
311324
.iter()
@@ -331,20 +344,25 @@ pub trait TransactionConnection: ClarityConnection {
331344
},
332345
abort_call_back,
333346
)
334-
.and_then(|(value, assets, events, aborted)| {
335-
if aborted {
336-
Err(Error::AbortedByCallback(Some(value), assets, events))
347+
.and_then(|(value, assets_modified, tx_events, reason)| {
348+
if let Some(reason) = reason {
349+
Err(Error::AbortedByCallback {
350+
output: Some(value),
351+
assets_modified,
352+
tx_events,
353+
reason,
354+
})
337355
} else {
338-
Ok((value, assets, events))
356+
Ok((value, assets_modified, tx_events))
339357
}
340358
})
341359
}
342360

343361
/// Initialize a contract in the current block.
344362
/// If an error occurs while processing the initialization, it's modifications will be rolled back.
345-
/// abort_call_back is called with an AssetMap and a ClarityDatabase reference,
346-
/// if abort_call_back returns true, all modifications from this transaction will be rolled back.
347-
/// otherwise, they will be committed (though they may later be rolled back if the block itself is rolled back).
363+
/// `abort_call_back` is called with an `AssetMap` and a `ClarityDatabase` reference,
364+
/// If `abort_call_back` returns `Some(reason)`, all modifications from this transaction will be rolled back.
365+
/// Otherwise, they will be committed (though they may later be rolled back if the block itself is rolled back).
348366
#[allow(clippy::too_many_arguments)]
349367
fn initialize_smart_contract<F>(
350368
&mut self,
@@ -357,9 +375,9 @@ pub trait TransactionConnection: ClarityConnection {
357375
max_execution_time: Option<std::time::Duration>,
358376
) -> Result<(AssetMap, Vec<StacksTransactionEvent>), Error>
359377
where
360-
F: FnOnce(&AssetMap, &mut ClarityDatabase) -> bool,
378+
F: FnOnce(&AssetMap, &mut ClarityDatabase) -> Option<String>,
361379
{
362-
let (_, asset_map, events, aborted) = self.with_abort_callback(
380+
let (_, assets_modified, tx_events, reason) = self.with_abort_callback(
363381
|vm_env| {
364382
if let Some(max_execution_time_duration) = max_execution_time {
365383
vm_env
@@ -378,10 +396,15 @@ pub trait TransactionConnection: ClarityConnection {
378396
},
379397
abort_call_back,
380398
)?;
381-
if aborted {
382-
Err(Error::AbortedByCallback(None, asset_map, events))
399+
if let Some(reason) = reason {
400+
Err(Error::AbortedByCallback {
401+
output: None,
402+
assets_modified,
403+
tx_events,
404+
reason,
405+
})
383406
} else {
384-
Ok((asset_map, events))
407+
Ok((assets_modified, tx_events))
385408
}
386409
}
387410
}

stacks-signer/src/monitoring/mod.rs

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,21 @@ SignerAgreementStateChangeReason {
3333
InactiveMiner("inactive_miner"),
3434
/// Signer agreement protocol version has been upgraded
3535
ProtocolUpgrade("protocol_upgrade"),
36+
/// An update related to the Miner view
37+
MinerViewUpdate("miner_view_update"),
38+
/// A specific Miner View update related to the parent tenure
39+
MinerParentTenureUpdate("miner_parent_tenure_update"),
40+
});
41+
42+
define_named_enum!(
43+
/// Represent different conflict types on signer agreement protocol
44+
SignerAgreementStateConflict {
45+
/// Waiting for burn block propagation to be aligned with the signer set
46+
BurnBlockDelay("burn_block_delay"),
47+
/// Waiting for stacks block propagation to be aligned with the signer set
48+
StacksBlockDelay("stacks_block_delay"),
49+
/// No agreement on miner view with the signer set
50+
MinerView("miner_view"),
3651
});
3752

3853
/// Actions for updating metrics
@@ -44,7 +59,7 @@ pub mod actions {
4459

4560
use crate::config::GlobalConfig;
4661
use crate::monitoring::prometheus::*;
47-
use crate::monitoring::SignerAgreementStateChangeReason;
62+
use crate::monitoring::{SignerAgreementStateChangeReason, SignerAgreementStateConflict};
4863
use crate::v0::signer_state::LocalStateMachine;
4964

5065
/// Update stacks tip height gauge
@@ -134,6 +149,21 @@ pub mod actions {
134149
.inc();
135150
}
136151

152+
/// Increment signer agreement state conflict counter
153+
pub fn increment_signer_agreement_state_conflict(conflict: SignerAgreementStateConflict) {
154+
let label_value = conflict.get_name();
155+
SIGNER_AGREEMENT_STATE_CONFLICTS
156+
.with_label_values(&[&label_value])
157+
.inc();
158+
}
159+
160+
/// Record the time (seconds) taken for a signer to agree with the signer set
161+
pub fn record_signer_agreement_capitulation_latency(latency_s: u64) {
162+
SIGNER_AGREEMENT_CAPITULATION_LATENCIES_HISTOGRAM
163+
.with_label_values(&[])
164+
.observe(latency_s as f64);
165+
}
166+
137167
/// Start serving monitoring metrics.
138168
/// This will only serve the metrics if the `monitoring_prom` feature is enabled.
139169
pub fn start_serving_monitoring_metrics(config: GlobalConfig) -> Result<(), String> {
@@ -157,7 +187,7 @@ pub mod actions {
157187
use blockstack_lib::chainstate::nakamoto::NakamotoBlock;
158188
use stacks_common::info;
159189

160-
use crate::monitoring::SignerAgreementStateChangeReason;
190+
use crate::monitoring::{SignerAgreementStateChangeReason, SignerAgreementStateConflict};
161191
use crate::v0::signer_state::LocalStateMachine;
162192
use crate::GlobalConfig;
163193

@@ -212,6 +242,12 @@ pub mod actions {
212242
) {
213243
}
214244

245+
/// Increment signer agreement state conflict counter
246+
pub fn increment_signer_agreement_state_conflict(_conflict: SignerAgreementStateConflict) {}
247+
248+
/// Record the time (seconds) taken for a signer to agree with the signer set
249+
pub fn record_signer_agreement_capitulation_latency(_latency_s: u64) {}
250+
215251
/// Start serving monitoring metrics.
216252
/// This will only serve the metrics if the `monitoring_prom` feature is enabled.
217253
pub fn start_serving_monitoring_metrics(config: GlobalConfig) -> Result<(), String> {

stacks-signer/src/monitoring/prometheus.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,22 @@ lazy_static! {
8181

8282
pub static ref SIGNER_AGREEMENT_STATE_CHANGE_REASONS: IntCounterVec = register_int_counter_vec!(
8383
"stacks_signer_agreement_state_change_reasons",
84-
"The number of state machine changes in signer agreement protocol. `reason` can be one of: 'burn_block_arrival', 'stacks_block_arrival', 'inactive_miner', 'protocol_upgrade'",
84+
"The number of state machine changes in signer agreement protocol. `reason` can be one of: 'burn_block_arrival', 'stacks_block_arrival', 'inactive_miner', 'protocol_upgrade', 'miner_view_update', 'miner_parent_tenure_update'",
8585
&["reason"]
8686
).unwrap();
8787

88+
pub static ref SIGNER_AGREEMENT_STATE_CONFLICTS: IntCounterVec = register_int_counter_vec!(
89+
"stacks_signer_agreement_state_conflicts",
90+
"The number of state machine conflicts in signer agreement protocol. `conflict` can be one of: 'burn_block_delay', 'stacks_block_delay', 'miner_view'",
91+
&["conflict"]
92+
).unwrap();
93+
94+
pub static ref SIGNER_AGREEMENT_CAPITULATION_LATENCIES_HISTOGRAM: HistogramVec = register_histogram_vec!(histogram_opts!(
95+
"stacks_signer_agreement_capitulation_latencies_histogram",
96+
"Measuring the time (in seconds) for the signer to agree (capitulate) with the signer set",
97+
vec![0.0, 1.0, 3.0, 5.0, 10.0, 20.0, 30.0, 60.0, 120.0]
98+
), &[]).unwrap();
99+
88100
pub static ref SIGNER_LOCAL_STATE_MACHINE: Mutex<Option<LocalStateMachine>> = Mutex::new(None);
89101
}
90102

0 commit comments

Comments
 (0)