• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

stacks-network / stacks-core / 25903914664-1

15 May 2026 06:28AM UTC coverage: 47.122% (-38.8%) from 85.959%
25903914664-1

Pull #7199

github

94e391
web-flow
Merge 109f2828c into 1c7b8e6ac
Pull Request #7199: Feat: L1 and L2 early unlocks, updating signer

103343 of 219309 relevant lines covered (47.12%)

12880462.62 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

57.6
/stackslib/src/net/api/getblockbyheight.rs
1
// Copyright (C) 2013-2020 Blockstack PBC, a public benefit corporation
2
// Copyright (C) 2020-2024 Stacks Open Internet Foundation
3
//
4
// This program is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// This program is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

17
use regex::{Captures, Regex};
18
use stacks_common::codec::MAX_MESSAGE_LEN;
19
use stacks_common::types::net::PeerHost;
20

21
use crate::chainstate::stacks::Error as ChainError;
22
use crate::net::api::getblock_v3::NakamotoBlockStream;
23
use crate::net::http::{
24
    parse_bytes, Error, HttpBadRequest, HttpContentType, HttpNotFound, HttpRequest,
25
    HttpRequestContents, HttpRequestPreamble, HttpResponse, HttpResponseContents,
26
    HttpResponsePayload, HttpResponsePreamble, HttpServerError,
27
};
28
use crate::net::httpcore::{
29
    HttpRequestContentsExtensions as _, RPCRequestHandler, StacksHttpRequest, StacksHttpResponse,
30
};
31
use crate::net::{Error as NetError, StacksNodeState, TipRequest};
32
use crate::util_lib::db::Error as DBError;
33

34
#[derive(Clone)]
35
pub struct RPCNakamotoBlockByHeightRequestHandler {
36
    pub block_height: Option<u64>,
37
}
38

39
impl RPCNakamotoBlockByHeightRequestHandler {
40
    pub fn new() -> Self {
1,059,041✔
41
        Self { block_height: None }
1,059,041✔
42
    }
1,059,041✔
43
}
44

45
/// Decode the HTTP request
46
impl HttpRequest for RPCNakamotoBlockByHeightRequestHandler {
47
    fn verb(&self) -> &'static str {
1,059,041✔
48
        "GET"
1,059,041✔
49
    }
1,059,041✔
50

51
    fn path_regex(&self) -> Regex {
2,118,082✔
52
        Regex::new(r#"^/v3/blocks/height/(?P<block_height>[0-9]{1,20})$"#).unwrap()
2,118,082✔
53
    }
2,118,082✔
54

55
    fn metrics_identifier(&self) -> &str {
9✔
56
        "/v3/blocks/height/:block_height"
9✔
57
    }
9✔
58

59
    /// Try to decode this request.
60
    /// There's nothing to load here, so just make sure the request is well-formed.
61
    fn try_parse_request(
9✔
62
        &mut self,
9✔
63
        preamble: &HttpRequestPreamble,
9✔
64
        captures: &Captures,
9✔
65
        query: Option<&str>,
9✔
66
        _body: &[u8],
9✔
67
    ) -> Result<HttpRequestContents, Error> {
9✔
68
        if preamble.get_content_length() != 0 {
9✔
69
            return Err(Error::DecodeError(
×
70
                "Invalid Http request: expected 0-length body".to_string(),
×
71
            ));
×
72
        }
9✔
73

74
        let block_height_str = captures
9✔
75
            .name("block_height")
9✔
76
            .ok_or_else(|| {
9✔
77
                Error::DecodeError("Failed to match path to block height group".to_string())
×
78
            })?
×
79
            .as_str();
9✔
80

81
        let block_height = block_height_str.parse::<u64>().map_err(|_| {
9✔
82
            Error::DecodeError("Invalid path: unparseable block height".to_string())
×
83
        })?;
×
84
        self.block_height = Some(block_height);
9✔
85

86
        Ok(HttpRequestContents::new().query_string(query))
9✔
87
    }
9✔
88
}
89

90
impl RPCRequestHandler for RPCNakamotoBlockByHeightRequestHandler {
91
    /// Reset internal state
92
    fn restart(&mut self) {
9✔
93
        self.block_height = None;
9✔
94
    }
9✔
95

96
    /// Make the response
97
    fn try_handle_request(
9✔
98
        &mut self,
9✔
99
        preamble: HttpRequestPreamble,
9✔
100
        contents: HttpRequestContents,
9✔
101
        node: &mut StacksNodeState,
9✔
102
    ) -> Result<(HttpResponsePreamble, HttpResponseContents), NetError> {
9✔
103
        let block_height = self
9✔
104
            .block_height
9✔
105
            .take()
9✔
106
            .ok_or(NetError::SendError("Missing `block_height`".into()))?;
9✔
107

108
        let tip = match node.load_stacks_chain_tip(&preamble, &contents) {
9✔
109
            Ok(tip) => tip,
9✔
110
            Err(error_resp) => {
×
111
                return error_resp.try_into_contents().map_err(NetError::from);
×
112
            }
113
        };
114

115
        let index_block_hash_res =
9✔
116
            node.with_node_state(|_network, _sortdb, chainstate, _mempool, _rpc_args| {
9✔
117
                chainstate
9✔
118
                    .index_conn()
9✔
119
                    .get_ancestor_block_hash(block_height, &tip)
9✔
120
            });
9✔
121

122
        let block_id = match index_block_hash_res {
9✔
123
            Ok(index_block_hash_opt) => match index_block_hash_opt {
9✔
124
                Some(index_block_hash) => index_block_hash,
9✔
125
                None => {
126
                    // block hash not found
127
                    let msg = format!("No such block #{:?}\n", block_height);
×
128
                    warn!("{}", &msg);
×
129
                    return StacksHttpResponse::new_error(&preamble, &HttpNotFound::new(msg))
×
130
                        .try_into_contents()
×
131
                        .map_err(NetError::from);
×
132
                }
133
            },
134
            Err(e) => {
×
135
                // error querying the db
136
                let msg = format!("Failed to load block #{}: {:?}\n", block_height, &e);
×
137
                warn!("{}", &msg);
×
138
                let resp = if matches!(e, DBError::BlockHeightOutOfRange) {
×
139
                    StacksHttpResponse::new_error(&preamble, &HttpBadRequest::new(msg))
×
140
                } else {
141
                    StacksHttpResponse::new_error(&preamble, &HttpServerError::new(msg))
×
142
                };
143
                return resp.try_into_contents().map_err(NetError::from);
×
144
            }
145
        };
146

147
        let stream_res =
9✔
148
            node.with_node_state(|_network, _sortdb, chainstate, _mempool, _rpc_args| {
9✔
149
                let Some((tenure_id, parent_block_id)) = chainstate
9✔
150
                    .nakamoto_blocks_db()
9✔
151
                    .get_tenure_and_parent_block_id(&block_id)?
9✔
152
                else {
153
                    return Err(ChainError::NoSuchBlockError);
×
154
                };
155
                NakamotoBlockStream::new(chainstate, block_id, tenure_id, parent_block_id)
9✔
156
            });
9✔
157

158
        // start loading up the block
159
        let stream = match stream_res {
9✔
160
            Ok(stream) => stream,
9✔
161
            Err(ChainError::NoSuchBlockError) => {
162
                return StacksHttpResponse::new_error(
×
163
                    &preamble,
×
164
                    &HttpNotFound::new(format!("No such block #{:?}\n", &block_height)),
×
165
                )
166
                .try_into_contents()
×
167
                .map_err(NetError::from)
×
168
            }
169
            Err(e) => {
×
170
                // nope -- error trying to check
171
                let msg = format!("Failed to load block #{}: {:?}\n", block_height, &e);
×
172
                warn!("{}", &msg);
×
173
                return StacksHttpResponse::new_error(&preamble, &HttpServerError::new(msg))
×
174
                    .try_into_contents()
×
175
                    .map_err(NetError::from);
×
176
            }
177
        };
178

179
        let resp_preamble = HttpResponsePreamble::from_http_request_preamble(
9✔
180
            &preamble,
9✔
181
            200,
182
            "OK",
9✔
183
            None,
9✔
184
            HttpContentType::Bytes,
9✔
185
        );
186
        Ok((
9✔
187
            resp_preamble,
9✔
188
            HttpResponseContents::from_stream(Box::new(stream)),
9✔
189
        ))
9✔
190
    }
9✔
191
}
192

193
/// Decode the HTTP response
194
impl HttpResponse for RPCNakamotoBlockByHeightRequestHandler {
195
    /// Decode this response from a byte stream.  This is called by the client to decode this
196
    /// message
197
    fn try_parse_response(
×
198
        &self,
×
199
        preamble: &HttpResponsePreamble,
×
200
        body: &[u8],
×
201
    ) -> Result<HttpResponsePayload, Error> {
×
202
        let bytes = parse_bytes(preamble, body, MAX_MESSAGE_LEN.into())?;
×
203
        Ok(HttpResponsePayload::Bytes(bytes))
×
204
    }
×
205
}
206

207
impl StacksHttpRequest {
208
    pub fn new_get_nakamoto_block_by_height(
×
209
        host: PeerHost,
×
210
        block_height: u64,
×
211
        tip: TipRequest,
×
212
    ) -> StacksHttpRequest {
×
213
        StacksHttpRequest::new_for_peer(
×
214
            host,
×
215
            "GET".into(),
×
216
            format!("/v3/blocks/height/{}", block_height),
×
217
            HttpRequestContents::new().for_tip(tip),
×
218
        )
219
        .expect("FATAL: failed to construct request from infallible data")
×
220
    }
×
221
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc