Skip to content

Commit e4a042b

Browse files
committed
Merge branch 'develop' into feat/sip-029
2 parents 45383fc + 583b857 commit e4a042b

File tree

12 files changed

+233
-75
lines changed

12 files changed

+233
-75
lines changed

CHANGELOG.md

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

88
## [Unreleased]
99

10+
### Added
11+
12+
- New RPC endpoints
13+
- `/v2/clarity/marf/:marf_key_hash`
14+
- `/v2/clarity/metadata/:principal/:contract_name/:clarity_metadata_key`
15+
16+
### Changed
17+
18+
## [3.0.0.0.4]
19+
20+
### Added
21+
22+
### Changed
23+
24+
- Use the same burn view loader in both block validation and block processing
25+
26+
## [3.0.0.0.3]
27+
28+
### Added
29+
1030
### Changed
1131
- Add index for StacksBlockId to nakamoto block headers table (improves node performance)
1232
- Remove the panic for reporting DB deadlocks (just error and continue waiting)
@@ -16,6 +36,15 @@ and this project adheres to the versioning scheme outlined in the [README.md](RE
1636
- Add `/v3/blocks/height/:block_height` rpc endpoint
1737
- If the winning miner of a sortition is committed to the wrong parent tenure, the previous miner can immediately tenure extend and continue mining since the winning miner would never be able to propose a valid block. (#5361)
1838

39+
## [3.0.0.0.2]
40+
41+
### Added
42+
43+
### Changed
44+
- Fixes a few bugs in the relayer and networking stack
45+
- detects and deprioritizes unhealthy replicas
46+
- fixes an issue in the p2p stack which was preventing it from caching the reward set.
47+
1948
## [3.0.0.0.1]
2049

2150
### Changed

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -579,7 +579,7 @@ _Do_ document things that are not clear, e.g.:
579579
Keep in mind that better variable names can reduce the need for comments, e.g.:
580580

581581
- `burnblock_height` instead of `height` may eliminate the need to comment that `height` refers to a burnblock height
582-
- `process_microblocks` instead of `process_blocks` is more correct, and may eliminate the need to to explain that the inputs are microblocks
582+
- `process_microblocks` instead of `process_blocks` is more correct, and may eliminate the need to explain that the inputs are microblocks
583583
- `add_transaction_to_microblock` explains more than `handle_transaction`, and reduces the need to even read the comment
584584

585585
# Licensing and contributor license agreement

clarity/src/vm/docs/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -529,7 +529,7 @@ const LOG2_API: SimpleFunctionAPI = SimpleFunctionAPI {
529529
snippet: "log2 ${1:expr-1}",
530530
signature: "(log2 n)",
531531
description:
532-
"Returns the power to which the number 2 must be raised to to obtain the value `n`, rounded
532+
"Returns the power to which the number 2 must be raised to obtain the value `n`, rounded
533533
down to the nearest integer. Fails on a negative numbers.
534534
",
535535
example: "(log2 u8) ;; Returns u3

clarity/src/vm/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -575,7 +575,7 @@ pub fn execute(program: &str) -> Result<Option<Value>> {
575575
)
576576
}
577577

578-
/// Execute for test in in Clarity2, Epoch21, testnet.
578+
/// Execute for test in Clarity2, Epoch21, testnet.
579579
#[cfg(any(test, feature = "testing"))]
580580
pub fn execute_v2(program: &str) -> Result<Option<Value>> {
581581
execute_with_parameters(

stacks-signer/CHANGELOG.md

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

1212
### Changed
1313

14+
## [3.0.0.0.4.0]
15+
16+
### Added
17+
18+
### Changed
19+
20+
- Use the same burn view loader in both block validation and block processing
21+
22+
## [3.0.0.0.3.0]
23+
24+
### Added
25+
26+
### Changed
27+
1428
- Allow a miner to extend their tenure immediately if the winner of the next tenure has committed to the wrong parent tenure (#5361)
1529

30+
## [3.0.0.0.2.0]
31+
32+
### Added
33+
- Adds `tenure_last_block_proposal_timeout_secs` option to account for delayed global block acceptance. default to 30s
34+
35+
### Changed
36+
1637
## [3.0.0.0.1.0]
1738

39+
### Added
40+
1841
### Changed
1942

2043
- Change block rejection message to generic block response

stackslib/src/chainstate/nakamoto/mod.rs

Lines changed: 69 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1788,6 +1788,73 @@ impl NakamotoChainState {
17881788
}
17891789
}
17901790

1791+
/// Get the current burnchain view
1792+
/// This is either:
1793+
/// (1) set by the tenure change tx if one exists
1794+
/// (2) the same as parent block id
1795+
pub fn get_block_burn_view(
1796+
sort_db: &SortitionDB,
1797+
next_ready_block: &NakamotoBlock,
1798+
parent_header_info: &StacksHeaderInfo,
1799+
) -> Result<ConsensusHash, ChainstateError> {
1800+
let burnchain_view = if let Some(tenure_change) = next_ready_block.get_tenure_tx_payload() {
1801+
if let Some(ref parent_burn_view) = parent_header_info.burn_view {
1802+
// check that the tenure_change's burn view descends from the parent
1803+
let parent_burn_view_sn = SortitionDB::get_block_snapshot_consensus(
1804+
sort_db.conn(),
1805+
parent_burn_view,
1806+
)?
1807+
.ok_or_else(|| {
1808+
warn!(
1809+
"Cannot process Nakamoto block: could not find parent block's burnchain view";
1810+
"consensus_hash" => %next_ready_block.header.consensus_hash,
1811+
"stacks_block_hash" => %next_ready_block.header.block_hash(),
1812+
"stacks_block_id" => %next_ready_block.header.block_id(),
1813+
"parent_block_id" => %next_ready_block.header.parent_block_id
1814+
);
1815+
ChainstateError::InvalidStacksBlock("Failed to load burn view of parent block ID".into())
1816+
})?;
1817+
let handle = sort_db.index_handle_at_ch(&tenure_change.burn_view_consensus_hash)?;
1818+
let connected_sort_id = get_ancestor_sort_id(&handle, parent_burn_view_sn.block_height, &handle.context.chain_tip)?
1819+
.ok_or_else(|| {
1820+
warn!(
1821+
"Cannot process Nakamoto block: could not find parent block's burnchain view";
1822+
"consensus_hash" => %next_ready_block.header.consensus_hash,
1823+
"stacks_block_hash" => %next_ready_block.header.block_hash(),
1824+
"stacks_block_id" => %next_ready_block.header.block_id(),
1825+
"parent_block_id" => %next_ready_block.header.parent_block_id
1826+
);
1827+
ChainstateError::InvalidStacksBlock("Failed to load burn view of parent block ID".into())
1828+
})?;
1829+
if connected_sort_id != parent_burn_view_sn.sortition_id {
1830+
warn!(
1831+
"Cannot process Nakamoto block: parent block's burnchain view does not connect to own burn view";
1832+
"consensus_hash" => %next_ready_block.header.consensus_hash,
1833+
"stacks_block_hash" => %next_ready_block.header.block_hash(),
1834+
"stacks_block_id" => %next_ready_block.header.block_id(),
1835+
"parent_block_id" => %next_ready_block.header.parent_block_id
1836+
);
1837+
return Err(ChainstateError::InvalidStacksBlock(
1838+
"Does not connect to burn view of parent block ID".into(),
1839+
));
1840+
}
1841+
}
1842+
tenure_change.burn_view_consensus_hash
1843+
} else {
1844+
parent_header_info.burn_view.clone().ok_or_else(|| {
1845+
warn!(
1846+
"Cannot process Nakamoto block: parent block does not have a burnchain view and current block has no tenure tx";
1847+
"consensus_hash" => %next_ready_block.header.consensus_hash,
1848+
"stacks_block_hash" => %next_ready_block.header.block_hash(),
1849+
"stacks_block_id" => %next_ready_block.header.block_id(),
1850+
"parent_block_id" => %next_ready_block.header.parent_block_id
1851+
);
1852+
ChainstateError::InvalidStacksBlock("Failed to load burn view of parent block ID".into())
1853+
})?
1854+
};
1855+
Ok(burnchain_view)
1856+
}
1857+
17911858
/// Process the next ready block.
17921859
/// If there exists a ready Nakamoto block, then this method returns Ok(Some(..)) with the
17931860
/// receipt. Otherwise, it returns Ok(None).
@@ -1920,62 +1987,8 @@ impl NakamotoChainState {
19201987
// this is either:
19211988
// (1) set by the tenure change tx if one exists
19221989
// (2) the same as parent block id
1923-
1924-
let burnchain_view = if let Some(tenure_change) = next_ready_block.get_tenure_tx_payload() {
1925-
if let Some(ref parent_burn_view) = parent_header_info.burn_view {
1926-
// check that the tenure_change's burn view descends from the parent
1927-
let parent_burn_view_sn = SortitionDB::get_block_snapshot_consensus(
1928-
sort_db.conn(),
1929-
parent_burn_view,
1930-
)?
1931-
.ok_or_else(|| {
1932-
warn!(
1933-
"Cannot process Nakamoto block: could not find parent block's burnchain view";
1934-
"consensus_hash" => %next_ready_block.header.consensus_hash,
1935-
"stacks_block_hash" => %next_ready_block.header.block_hash(),
1936-
"stacks_block_id" => %next_ready_block.header.block_id(),
1937-
"parent_block_id" => %next_ready_block.header.parent_block_id
1938-
);
1939-
ChainstateError::InvalidStacksBlock("Failed to load burn view of parent block ID".into())
1940-
})?;
1941-
let handle = sort_db.index_handle_at_ch(&tenure_change.burn_view_consensus_hash)?;
1942-
let connected_sort_id = get_ancestor_sort_id(&handle, parent_burn_view_sn.block_height, &handle.context.chain_tip)?
1943-
.ok_or_else(|| {
1944-
warn!(
1945-
"Cannot process Nakamoto block: could not find parent block's burnchain view";
1946-
"consensus_hash" => %next_ready_block.header.consensus_hash,
1947-
"stacks_block_hash" => %next_ready_block.header.block_hash(),
1948-
"stacks_block_id" => %next_ready_block.header.block_id(),
1949-
"parent_block_id" => %next_ready_block.header.parent_block_id
1950-
);
1951-
ChainstateError::InvalidStacksBlock("Failed to load burn view of parent block ID".into())
1952-
})?;
1953-
if connected_sort_id != parent_burn_view_sn.sortition_id {
1954-
warn!(
1955-
"Cannot process Nakamoto block: parent block's burnchain view does not connect to own burn view";
1956-
"consensus_hash" => %next_ready_block.header.consensus_hash,
1957-
"stacks_block_hash" => %next_ready_block.header.block_hash(),
1958-
"stacks_block_id" => %next_ready_block.header.block_id(),
1959-
"parent_block_id" => %next_ready_block.header.parent_block_id
1960-
);
1961-
return Err(ChainstateError::InvalidStacksBlock(
1962-
"Does not connect to burn view of parent block ID".into(),
1963-
));
1964-
}
1965-
}
1966-
tenure_change.burn_view_consensus_hash
1967-
} else {
1968-
parent_header_info.burn_view.clone().ok_or_else(|| {
1969-
warn!(
1970-
"Cannot process Nakamoto block: parent block does not have a burnchain view and current block has no tenure tx";
1971-
"consensus_hash" => %next_ready_block.header.consensus_hash,
1972-
"stacks_block_hash" => %next_ready_block.header.block_hash(),
1973-
"stacks_block_id" => %next_ready_block.header.block_id(),
1974-
"parent_block_id" => %next_ready_block.header.parent_block_id
1975-
);
1976-
ChainstateError::InvalidStacksBlock("Failed to load burn view of parent block ID".into())
1977-
})?
1978-
};
1990+
let burnchain_view =
1991+
Self::get_block_burn_view(sort_db, &next_ready_block, &parent_header_info)?;
19791992
let Some(burnchain_view_sn) =
19801993
SortitionDB::get_block_snapshot_consensus(sort_db.conn(), &burnchain_view)?
19811994
else {

stackslib/src/net/api/postblock_proposal.rs

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -383,9 +383,30 @@ impl NakamotoBlockProposal {
383383
"expected" => %NAKAMOTO_BLOCK_VERSION);
384384
}
385385

386-
let sort_tip = SortitionDB::get_canonical_sortition_tip(sortdb.conn())?;
387-
let burn_dbconn: SortitionHandleConn = sortdb.index_handle(&sort_tip);
388-
let mut db_handle = sortdb.index_handle(&sort_tip);
386+
// open sortition view to the current burn view.
387+
// If the block has a TenureChange with an Extend cause, then the burn view is whatever is
388+
// indicated in the TenureChange.
389+
// Otherwise, it's the same as the block's parent's burn view.
390+
let parent_stacks_header = NakamotoChainState::get_block_header(
391+
chainstate.db(),
392+
&self.block.header.parent_block_id,
393+
)?
394+
.ok_or_else(|| BlockValidateRejectReason {
395+
reason_code: ValidateRejectCode::InvalidBlock,
396+
reason: "Invalid parent block".into(),
397+
})?;
398+
399+
let burn_view_consensus_hash =
400+
NakamotoChainState::get_block_burn_view(sortdb, &self.block, &parent_stacks_header)?;
401+
let sort_tip =
402+
SortitionDB::get_block_snapshot_consensus(sortdb.conn(), &burn_view_consensus_hash)?
403+
.ok_or_else(|| BlockValidateRejectReason {
404+
reason_code: ValidateRejectCode::NoSuchTenure,
405+
reason: "Failed to find sortition for block tenure".to_string(),
406+
})?;
407+
408+
let burn_dbconn: SortitionHandleConn = sortdb.index_handle(&sort_tip.sortition_id);
409+
let mut db_handle = sortdb.index_handle(&sort_tip.sortition_id);
389410

390411
// (For the signer)
391412
// Verify that the block's tenure is on the canonical sortition history
@@ -422,14 +443,6 @@ impl NakamotoBlockProposal {
422443
)?;
423444

424445
// Validate txs against chainstate
425-
let parent_stacks_header = NakamotoChainState::get_block_header(
426-
chainstate.db(),
427-
&self.block.header.parent_block_id,
428-
)?
429-
.ok_or_else(|| BlockValidateRejectReason {
430-
reason_code: ValidateRejectCode::InvalidBlock,
431-
reason: "Invalid parent block".into(),
432-
})?;
433446

434447
// Validate the block's timestamp. It must be:
435448
// - Greater than the parent block's timestamp

stackslib/src/net/api/postblock_v3.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ impl HttpRequest for RPCPostBlockRequestHandler {
7070
}
7171

7272
fn path_regex(&self) -> Regex {
73-
Regex::new(&format!("^{PATH}$")).unwrap()
73+
Regex::new(&format!("^{}(/)?$", PATH.trim_end_matches('/'))).unwrap()
7474
}
7575

7676
fn metrics_identifier(&self) -> &str {

stackslib/src/net/api/tests/postblock_v3.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,66 @@ fn handle_req_accepted() {
214214
assert_eq!(resp.stacks_block_id, next_block_id);
215215
}
216216

217+
#[test]
218+
fn handle_req_without_trailing_accepted() {
219+
let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 33333);
220+
let path_without_slash: &str = "/v3/blocks/upload";
221+
let observer = TestEventObserver::new();
222+
let mut rpc_test = TestRPC::setup_nakamoto(function_name!(), &observer);
223+
let (next_block, ..) = rpc_test.peer_1.single_block_tenure(
224+
&rpc_test.privk1,
225+
|_| {},
226+
|burn_ops| {
227+
rpc_test.peer_2.next_burnchain_block(burn_ops.clone());
228+
},
229+
|_| true,
230+
);
231+
let next_block_id = next_block.block_id();
232+
let mut requests = vec![];
233+
234+
// post the block
235+
requests.push(
236+
StacksHttpRequest::new_for_peer(
237+
addr.into(),
238+
"POST".into(),
239+
path_without_slash.into(),
240+
HttpRequestContents::new().payload_stacks(&next_block),
241+
)
242+
.unwrap(),
243+
);
244+
245+
// idempotent
246+
requests.push(
247+
StacksHttpRequest::new_for_peer(
248+
addr.into(),
249+
"POST".into(),
250+
path_without_slash.into(),
251+
HttpRequestContents::new().payload_stacks(&next_block),
252+
)
253+
.unwrap(),
254+
);
255+
let mut responses = rpc_test.run(requests);
256+
257+
let response = responses.remove(0);
258+
info!(
259+
"Response for the request that has the path without the last '/': {}",
260+
std::str::from_utf8(&response.try_serialize().unwrap()).unwrap()
261+
);
262+
263+
let resp = response.decode_stacks_block_accepted().unwrap();
264+
assert_eq!(resp.accepted, true);
265+
assert_eq!(resp.stacks_block_id, next_block_id);
266+
267+
let response = responses.remove(0);
268+
info!(
269+
"Response for the request that has the path without the last '/': {}",
270+
std::str::from_utf8(&response.try_serialize().unwrap()).unwrap()
271+
);
272+
let resp = response.decode_stacks_block_accepted().unwrap();
273+
assert_eq!(resp.accepted, false);
274+
assert_eq!(resp.stacks_block_id, next_block_id);
275+
}
276+
217277
#[test]
218278
fn handle_req_unknown_burn_block() {
219279
let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 33333);

0 commit comments

Comments
 (0)