Skip to content

Commit f04ad0b

Browse files
authored
Merge pull request #5912 from csgui/fix/clarity-wasm-context-rollback
Ensure context rollback in an error situation
2 parents dcf3fae + cd8a302 commit f04ad0b

File tree

28 files changed

+499
-102
lines changed

28 files changed

+499
-102
lines changed

.github/workflows/github-release.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,3 +142,21 @@ jobs:
142142
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
143143
DOCKERHUB_PASSWORD: ${{ secrets.DOCKERHUB_PASSWORD }}
144144
dist: ${{ matrix.dist }}
145+
146+
## Create the downstream PR for the release branch to master,develop
147+
create-pr:
148+
if: |
149+
inputs.node_tag != '' ||
150+
inputs.signer_tag != ''
151+
name: Create Downstream PR (${{ github.ref_name }})
152+
runs-on: ubuntu-latest
153+
needs:
154+
- build-binaries
155+
- create-release
156+
- docker-image
157+
steps:
158+
- name: Open Downstream PR
159+
id: create-pr
160+
uses: stacks-network/actions/stacks-core/release/downstream-pr@main
161+
with:
162+
token: ${{ secrets.GH_TOKEN }}

.rustfmt.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
group_imports = "StdExternalCrate"
2+
imports_granularity = "Module"

.vscode/settings.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
2-
"lldb.adapterType": "native",
3-
"lldb.launch.sourceLanguages": ["rust"],
4-
"rust-analyzer.runnables.extraEnv": {
5-
"BITCOIND_TEST": "1"
6-
}
2+
"lldb.launch.sourceLanguages": ["rust"],
3+
"rust-analyzer.runnables.extraEnv": {
4+
"BITCOIND_TEST": "1"
5+
},
6+
"rust-analyzer.rustfmt.extraArgs": ["+nightly"]
77
}

CHANGELOG.md

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

88
## [Unreleased]
99

10+
### Added"
11+
- Add fee information to transaction log ending with "success" or "skipped", while building a new block
12+
1013
### Changed
1114

1215
- When a miner times out waiting for signatures, it will re-propose the same block instead of building a new block ([#5877](https://github.com/stacks-network/stacks-core/pull/5877))
16+
- Improve tenure downloader trace verbosity applying proper logging level depending on the tenure state ("debug" if unconfirmed, "info" otherwise) ([#5871](https://github.com/stacks-network/stacks-core/issues/5871))
1317

1418
## [3.1.0.0.7]
1519

Cargo.lock

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

clarity/src/vm/clarity_wasm.rs

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -428,11 +428,28 @@ pub fn initialize_contract(
428428
results.push(placeholder_for_type(result_ty));
429429
}
430430

431-
top_level
432-
.call(&mut store, &[], results.as_mut_slice())
433-
.map_err(|e| {
434-
error_mapping::resolve_error(e, instance, &mut store, &epoch, &clarity_version)
435-
})?;
431+
let top_level_result = top_level.call(&mut store, &[], results.as_mut_slice());
432+
match top_level_result {
433+
Ok(_) => {}
434+
Err(e) => {
435+
// Before propagating the error, attempt to roll back the function context.
436+
// If the rollback fails, immediately return a rollback-specific error.
437+
if store.data_mut().global_context.roll_back().is_err() {
438+
return Err(Error::Wasm(WasmError::Expect(
439+
"Expected entry to rollback".into(),
440+
)));
441+
}
442+
443+
// Rollback succeeded, so resolve and return the original runtime error.
444+
return Err(error_mapping::resolve_error(
445+
e,
446+
instance,
447+
&mut store,
448+
&epoch,
449+
&clarity_version,
450+
));
451+
}
452+
}
436453

437454
// Save the compiled Wasm module into the contract context
438455
store.data_mut().contract_context_mut()?.set_wasm_module(

clarity/src/vm/tests/assets.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,11 @@ const FIRST_CLASS_TOKENS: &str = "(define-fungible-token stackaroos)
4242
(define-public (mint-after (block-to-release uint))
4343
(if (>= block-height block-to-release)
4444
(faucet)
45-
(err \"must be in the future\")))
45+
(err u100)))
4646
(define-public (burn (amount uint) (p principal))
4747
(ft-burn? stackaroos amount p))
48-
(begin (ft-mint? stackaroos u10000 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR)
49-
(ft-mint? stackaroos u200 'SM2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQVX8X0G)
48+
(begin (is-ok (ft-mint? stackaroos u10000 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR))
49+
(is-ok (ft-mint? stackaroos u200 'SM2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQVX8X0G))
5050
(ft-mint? stackaroos u4 .tokens))";
5151

5252
const ASSET_NAMES: &str =
@@ -80,15 +80,15 @@ const ASSET_NAMES: &str =
8080
(nft-burn? names name p))
8181
(define-public (try-bad-transfers)
8282
(begin
83-
(contract-call? .tokens my-token-transfer burn-address u50000)
84-
(contract-call? .tokens my-token-transfer burn-address u1000)
85-
(contract-call? .tokens my-token-transfer burn-address u1)
83+
(is-ok (contract-call? .tokens my-token-transfer burn-address u50000))
84+
(is-ok (contract-call? .tokens my-token-transfer burn-address u1000))
85+
(is-ok (contract-call? .tokens my-token-transfer burn-address u1))
8686
(err u0)))
8787
(define-public (try-bad-transfers-but-ok)
8888
(begin
89-
(contract-call? .tokens my-token-transfer burn-address u50000)
90-
(contract-call? .tokens my-token-transfer burn-address u1000)
91-
(contract-call? .tokens my-token-transfer burn-address u1)
89+
(is-ok (contract-call? .tokens my-token-transfer burn-address u50000))
90+
(is-ok (contract-call? .tokens my-token-transfer burn-address u1000))
91+
(is-ok (contract-call? .tokens my-token-transfer burn-address u1))
9292
(ok 0)))
9393
(define-public (transfer (name int) (recipient principal))
9494
(let ((transfer-name-result (nft-transfer? names name tx-sender recipient))
@@ -97,7 +97,7 @@ const ASSET_NAMES: &str =
9797
(begin (unwrap! transfer-name-result transfer-name-result)
9898
(unwrap! token-to-contract-result token-to-contract-result)
9999
(unwrap! contract-to-burn-result contract-to-burn-result)
100-
(ok 0))))
100+
(ok true))))
101101
(define-public (register
102102
(recipient-principal principal)
103103
(name int)

libsigner/src/v0/messages.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ use clarity::util::hash::Sha256Sum;
5151
use clarity::util::retry::BoundReader;
5252
use clarity::util::secp256k1::MessageSignature;
5353
use clarity::vm::types::serialization::SerializationError;
54-
use clarity::vm::types::{QualifiedContractIdentifier, TupleData};
54+
use clarity::vm::types::{QualifiedContractIdentifier, ResponseData, TupleData};
5555
use clarity::vm::Value;
5656
use hashbrown::{HashMap, HashSet};
5757
use serde::{Deserialize, Serialize};
@@ -875,11 +875,11 @@ impl BlockResponse {
875875
}
876876
}
877877

878-
/// The signer signature hash for the block response
879-
pub fn signer_signature_hash(&self) -> Sha512Trunc256Sum {
878+
/// Get the block response data from the block response
879+
pub fn get_response_data(&self) -> &BlockResponseData {
880880
match self {
881-
BlockResponse::Accepted(accepted) => accepted.signer_signature_hash,
882-
BlockResponse::Rejected(rejection) => rejection.signer_signature_hash,
881+
BlockResponse::Accepted(accepted) => &accepted.response_data,
882+
BlockResponse::Rejected(rejection) => &rejection.response_data,
883883
}
884884
}
885885

stacks-signer/CHANGELOG.md

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

88
## [Unreleased]
99

10+
### Changed
11+
12+
- For some rejection reasons, a signer will reconsider a block proposal that it previously rejected ([#5880](https://github.com/stacks-network/stacks-core/pull/5880))
13+
1014
## [3.1.0.0.7.0]
1115

12-
## Changed
16+
### Changed
1317

1418
- Add new reject codes to the signer response for better visibility into why a block was rejected.
1519
- When allowing a reorg within the `reorg_attempts_activity_timeout_ms`, the signer will now watch the responses from other signers and if >30% of them reject this reorg attempt, then the signer will mark the miner as invalid, reject further attempts to reorg and allow the previous miner to extend their tenure.
@@ -20,7 +24,7 @@ and this project adheres to the versioning scheme outlined in the [README.md](RE
2024

2125
## [3.1.0.0.6.0]
2226

23-
## Added
27+
### Added
2428

2529
- Introduced the `reorg_attempts_activity_timeout_ms` configuration option for signers which is used to determine the length of time after the last block of a tenure is confirmed that an incoming miner's attempts to reorg it are considered valid miner activity.
2630
- Add signer configuration option `tenure_idle_timeout_buffer_secs` to specify the number of seconds of buffer the signer will add to its tenure extend time that it sends to miners. The idea is to allow for some clock skew between the miner and signers, preventing the case where the miner attempts to tenure extend too early.

stacks-signer/src/signerdb.rs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,8 @@ pub struct BlockInfo {
167167
pub validation_time_ms: Option<u64>,
168168
/// Extra data specific to v0, v1, etc.
169169
pub ext: ExtraBlockInfo,
170+
/// If this signer rejected this block, what was the reason
171+
pub reject_reason: Option<RejectReason>,
170172
}
171173

172174
impl From<BlockProposal> for BlockInfo {
@@ -184,6 +186,7 @@ impl From<BlockProposal> for BlockInfo {
184186
ext: ExtraBlockInfo::default(),
185187
state: BlockState::Unprocessed,
186188
validation_time_ms: None,
189+
reject_reason: None,
187190
}
188191
}
189192
}
@@ -2193,4 +2196,77 @@ mod tests {
21932196
.unwrap()
21942197
.is_none());
21952198
}
2199+
2200+
/// BlockInfo without the `reject_reason` field for backwards compatibility testing
2201+
#[derive(Serialize, Deserialize, Debug, PartialEq)]
2202+
pub struct BlockInfoPrev {
2203+
/// The block we are considering
2204+
pub block: NakamotoBlock,
2205+
/// The burn block height at which the block was proposed
2206+
pub burn_block_height: u64,
2207+
/// The reward cycle the block belongs to
2208+
pub reward_cycle: u64,
2209+
/// Our vote on the block if we have one yet
2210+
pub vote: Option<NakamotoBlockVote>,
2211+
/// Whether the block contents are valid
2212+
pub valid: Option<bool>,
2213+
/// Whether this block is already being signed over
2214+
pub signed_over: bool,
2215+
/// Time at which the proposal was received by this signer (epoch time in seconds)
2216+
pub proposed_time: u64,
2217+
/// Time at which the proposal was signed by this signer (epoch time in seconds)
2218+
pub signed_self: Option<u64>,
2219+
/// Time at which the proposal was signed by a threshold in the signer set (epoch time in seconds)
2220+
pub signed_group: Option<u64>,
2221+
/// The block state relative to the signer's view of the stacks blockchain
2222+
pub state: BlockState,
2223+
/// Consumed processing time in milliseconds to validate this block
2224+
pub validation_time_ms: Option<u64>,
2225+
/// Extra data specific to v0, v1, etc.
2226+
pub ext: ExtraBlockInfo,
2227+
}
2228+
2229+
/// Verify that we can deserialize the old BlockInfo struct into the new version
2230+
#[test]
2231+
fn deserialize_old_block_info() {
2232+
let block_info_prev = BlockInfoPrev {
2233+
block: NakamotoBlock {
2234+
header: NakamotoBlockHeader::genesis(),
2235+
txs: vec![],
2236+
},
2237+
burn_block_height: 2,
2238+
reward_cycle: 3,
2239+
vote: None,
2240+
valid: None,
2241+
signed_over: true,
2242+
proposed_time: 4,
2243+
signed_self: None,
2244+
signed_group: None,
2245+
state: BlockState::Unprocessed,
2246+
validation_time_ms: Some(5),
2247+
ext: ExtraBlockInfo::default(),
2248+
};
2249+
2250+
let block_info: BlockInfo =
2251+
serde_json::from_value(serde_json::to_value(&block_info_prev).unwrap()).unwrap();
2252+
assert_eq!(block_info.block, block_info_prev.block);
2253+
assert_eq!(
2254+
block_info.burn_block_height,
2255+
block_info_prev.burn_block_height
2256+
);
2257+
assert_eq!(block_info.reward_cycle, block_info_prev.reward_cycle);
2258+
assert_eq!(block_info.vote, block_info_prev.vote);
2259+
assert_eq!(block_info.valid, block_info_prev.valid);
2260+
assert_eq!(block_info.signed_over, block_info_prev.signed_over);
2261+
assert_eq!(block_info.proposed_time, block_info_prev.proposed_time);
2262+
assert_eq!(block_info.signed_self, block_info_prev.signed_self);
2263+
assert_eq!(block_info.signed_group, block_info_prev.signed_group);
2264+
assert_eq!(block_info.state, block_info_prev.state);
2265+
assert_eq!(
2266+
block_info.validation_time_ms,
2267+
block_info_prev.validation_time_ms
2268+
);
2269+
assert_eq!(block_info.ext, block_info_prev.ext);
2270+
assert!(block_info.reject_reason.is_none());
2271+
}
21962272
}

0 commit comments

Comments
 (0)