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

stacks-network / stacks-core / 23772489092

30 Mar 2026 11:20PM UTC coverage: 46.338% (-39.4%) from 85.712%
23772489092

Pull #7060

github

cb823d
web-flow
Merge 7f94df24b into 7a9774e50
Pull Request #7060: feat: marf squash engine

380 of 3210 new or added lines in 14 files covered. (11.84%)

85248 existing lines in 334 files now uncovered.

102201 of 220557 relevant lines covered (46.34%)

13134054.38 hits per line

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

67.46
/stackslib/src/net/api/getcontractsrc.rs
1
// Copyright (C) 2013-2020 Blockstack PBC, a public benefit corporation
2
// Copyright (C) 2020-2023 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 clarity::vm::clarity::ClarityConnection;
18
use clarity::vm::database::clarity_store::{make_contract_hash_key, ContractCommitment};
19
use clarity::vm::representations::{CONTRACT_NAME_REGEX_STRING, STANDARD_PRINCIPAL_REGEX_STRING};
20
use clarity::vm::types::QualifiedContractIdentifier;
21
use clarity::vm::ContractName;
22
use regex::{Captures, Regex};
23
use stacks_common::types::chainstate::StacksAddress;
24
use stacks_common::types::net::PeerHost;
25
use stacks_common::util::hash::to_hex;
26

27
use crate::net::http::{
28
    parse_json, Error, HttpNotFound, HttpRequest, HttpRequestContents, HttpRequestPreamble,
29
    HttpResponse, HttpResponseContents, HttpResponsePayload, HttpResponsePreamble,
30
};
31
use crate::net::httpcore::{
32
    request, HttpRequestContentsExtensions as _, RPCRequestHandler, StacksHttpRequest,
33
    StacksHttpResponse,
34
};
35
use crate::net::{Error as NetError, StacksNodeState, TipRequest};
36

37
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
38
pub struct ContractSrcResponse {
39
    pub source: String,
40
    pub publish_height: u32,
41
    #[serde(rename = "proof")]
42
    #[serde(default)]
43
    #[serde(skip_serializing_if = "Option::is_none")]
44
    pub marf_proof: Option<String>,
45
}
46

47
#[derive(Clone)]
48
pub struct RPCGetContractSrcRequestHandler {
49
    pub contract_identifier: Option<QualifiedContractIdentifier>,
50
}
51

52
impl RPCGetContractSrcRequestHandler {
53
    pub fn new() -> Self {
967,797✔
54
        Self {
967,797✔
55
            contract_identifier: None,
967,797✔
56
        }
967,797✔
57
    }
967,797✔
58
}
59

60
/// Decode the HTTP request
61
impl HttpRequest for RPCGetContractSrcRequestHandler {
62
    fn verb(&self) -> &'static str {
967,797✔
63
        "GET"
967,797✔
64
    }
967,797✔
65

66
    fn path_regex(&self) -> Regex {
1,935,594✔
67
        Regex::new(&format!(
1,935,594✔
68
            "^/v2/contracts/source/(?P<address>{})/(?P<contract>{})$",
1,935,594✔
69
            *STANDARD_PRINCIPAL_REGEX_STRING, *CONTRACT_NAME_REGEX_STRING
1,935,594✔
70
        ))
1,935,594✔
71
        .unwrap()
1,935,594✔
72
    }
1,935,594✔
73

74
    fn metrics_identifier(&self) -> &str {
54✔
75
        "/v2/contracts/source/:principal/:contract_name"
54✔
76
    }
54✔
77

78
    /// Try to decode this request.
79
    /// There's nothing to load here, so just make sure the request is well-formed.
80
    fn try_parse_request(
54✔
81
        &mut self,
54✔
82
        preamble: &HttpRequestPreamble,
54✔
83
        captures: &Captures,
54✔
84
        query: Option<&str>,
54✔
85
        _body: &[u8],
54✔
86
    ) -> Result<HttpRequestContents, Error> {
54✔
87
        if preamble.get_content_length() != 0 {
54✔
88
            return Err(Error::DecodeError(
×
89
                "Invalid Http request: expected 0-length body".to_string(),
×
90
            ));
×
91
        }
54✔
92

93
        let contract_identifier = request::get_contract_address(captures, "address", "contract")?;
54✔
94
        self.contract_identifier = Some(contract_identifier);
54✔
95

96
        let contents = HttpRequestContents::new().query_string(query);
54✔
97
        Ok(contents)
54✔
98
    }
54✔
99
}
100

101
/// Handle the HTTP request
102
impl RPCRequestHandler for RPCGetContractSrcRequestHandler {
103
    /// Reset internal state
104
    fn restart(&mut self) {
54✔
105
        self.contract_identifier = None;
54✔
106
    }
54✔
107

108
    /// Make the response
109
    fn try_handle_request(
54✔
110
        &mut self,
54✔
111
        preamble: HttpRequestPreamble,
54✔
112
        contents: HttpRequestContents,
54✔
113
        node: &mut StacksNodeState,
54✔
114
    ) -> Result<(HttpResponsePreamble, HttpResponseContents), NetError> {
54✔
115
        let contract_identifier = self.contract_identifier.take().ok_or(NetError::SendError(
54✔
116
            "`contract_identifier` not set".to_string(),
54✔
117
        ))?;
54✔
118
        let tip = match node.load_stacks_chain_tip(&preamble, &contents) {
54✔
119
            Ok(tip) => tip,
54✔
120
            Err(error_resp) => {
×
121
                return error_resp.try_into_contents().map_err(NetError::from);
×
122
            }
123
        };
124
        let with_proof = contents.get_with_proof();
54✔
125

126
        let data_resp =
54✔
127
            node.with_node_state(|_network, sortdb, chainstate, _mempool, _rpc_args| {
54✔
128
                chainstate.maybe_read_only_clarity_tx(
54✔
129
                    &sortdb.index_handle_at_block(chainstate, &tip)?,
54✔
130
                    &tip,
54✔
131
                    |clarity_tx| {
54✔
132
                        clarity_tx.with_clarity_db_readonly(|db| {
54✔
133
                            let source = db.get_contract_src(&contract_identifier)?;
54✔
134
                            let contract_commit_key = make_contract_hash_key(&contract_identifier);
36✔
135
                            let (contract_commit, proof) = if with_proof {
36✔
136
                                db.get_data_with_proof::<ContractCommitment>(&contract_commit_key)
27✔
137
                                    .ok()
27✔
138
                                    .flatten()
27✔
139
                                    .map(|(a, b)| (a, Some(format!("0x{}", to_hex(&b)))))?
27✔
140
                            } else {
141
                                db.get_data::<ContractCommitment>(&contract_commit_key)
9✔
142
                                    .ok()
9✔
143
                                    .flatten()
9✔
144
                                    .map(|a| (a, None))?
9✔
145
                            };
146

147
                            let publish_height = contract_commit.block_height;
36✔
148
                            Some(ContractSrcResponse {
36✔
149
                                source,
36✔
150
                                publish_height,
36✔
151
                                marf_proof: proof,
36✔
152
                            })
36✔
153
                        })
54✔
154
                    },
54✔
155
                )
156
            });
54✔
157

158
        let data_resp = match data_resp {
54✔
159
            Ok(Some(Some(data))) => data,
36✔
160
            Ok(Some(None)) => {
161
                return StacksHttpResponse::new_error(
18✔
162
                    &preamble,
18✔
163
                    &HttpNotFound::new("No contract source data found".to_string()),
18✔
164
                )
165
                .try_into_contents()
18✔
166
                .map_err(NetError::from);
18✔
167
            }
168
            Ok(None) | Err(_) => {
169
                return StacksHttpResponse::new_error(
×
170
                    &preamble,
×
171
                    &HttpNotFound::new("Chain tip not found".to_string()),
×
172
                )
173
                .try_into_contents()
×
174
                .map_err(NetError::from);
×
175
            }
176
        };
177

178
        let preamble = HttpResponsePreamble::ok_json(&preamble);
36✔
179
        let body = HttpResponseContents::try_from_json(&data_resp)?;
36✔
180
        Ok((preamble, body))
36✔
181
    }
54✔
182
}
183

184
/// Decode the HTTP response
185
impl HttpResponse for RPCGetContractSrcRequestHandler {
UNCOV
186
    fn try_parse_response(
×
UNCOV
187
        &self,
×
UNCOV
188
        preamble: &HttpResponsePreamble,
×
UNCOV
189
        body: &[u8],
×
UNCOV
190
    ) -> Result<HttpResponsePayload, Error> {
×
UNCOV
191
        let contract_src: ContractSrcResponse = parse_json(preamble, body)?;
×
UNCOV
192
        Ok(HttpResponsePayload::try_from_json(contract_src)?)
×
UNCOV
193
    }
×
194
}
195

196
impl StacksHttpRequest {
197
    /// Make a new request for a contract's source code
UNCOV
198
    pub fn new_getcontractsrc(
×
UNCOV
199
        host: PeerHost,
×
UNCOV
200
        contract_addr: StacksAddress,
×
UNCOV
201
        contract_name: ContractName,
×
UNCOV
202
        tip_req: TipRequest,
×
UNCOV
203
        with_proof: bool,
×
UNCOV
204
    ) -> StacksHttpRequest {
×
UNCOV
205
        StacksHttpRequest::new_for_peer(
×
UNCOV
206
            host,
×
UNCOV
207
            "GET".into(),
×
UNCOV
208
            format!("/v2/contracts/source/{}/{}", &contract_addr, &contract_name),
×
UNCOV
209
            HttpRequestContents::new()
×
UNCOV
210
                .for_tip(tip_req)
×
UNCOV
211
                .query_arg("proof".into(), if with_proof { "1" } else { "0" }.into()),
×
212
        )
UNCOV
213
        .expect("FATAL: failed to construct request from infallible data")
×
UNCOV
214
    }
×
215
}
216

217
impl StacksHttpResponse {
UNCOV
218
    pub fn decode_contract_src_response(self) -> Result<ContractSrcResponse, NetError> {
×
UNCOV
219
        let contents = self.get_http_payload_ok()?;
×
UNCOV
220
        let contents_json: serde_json::Value = contents.try_into()?;
×
UNCOV
221
        let resp: ContractSrcResponse = serde_json::from_value(contents_json)
×
UNCOV
222
            .map_err(|_e| NetError::DeserializeError("Failed to load from JSON".to_string()))?;
×
UNCOV
223
        Ok(resp)
×
UNCOV
224
    }
×
225
}
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