• 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

11.85
/stackslib/src/net/api/getdatavar.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::ast::parser::v1::CLARITY_NAME_REGEX;
18
use clarity::vm::clarity::ClarityConnection;
19
use clarity::vm::database::{ClarityDatabase, StoreType};
20
use clarity::vm::representations::{CONTRACT_NAME_REGEX_STRING, STANDARD_PRINCIPAL_REGEX_STRING};
21
use clarity::vm::types::QualifiedContractIdentifier;
22
use clarity::vm::{ClarityName, ContractName};
23
use regex::{Captures, Regex};
24
use stacks_common::types::chainstate::StacksAddress;
25
use stacks_common::types::net::PeerHost;
26
use stacks_common::util::hash::to_hex;
27

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

38
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
39
pub struct DataVarResponse {
40
    pub data: String,
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 RPCGetDataVarRequestHandler {
49
    pub contract_identifier: Option<QualifiedContractIdentifier>,
50
    pub varname: Option<ClarityName>,
51
}
52
impl RPCGetDataVarRequestHandler {
53
    pub fn new() -> Self {
1,059,041✔
54
        Self {
1,059,041✔
55
            contract_identifier: None,
1,059,041✔
56
            varname: None,
1,059,041✔
57
        }
1,059,041✔
58
    }
1,059,041✔
59
}
60

61
/// Decode the HTTP request
62
impl HttpRequest for RPCGetDataVarRequestHandler {
63
    fn verb(&self) -> &'static str {
1,059,032✔
64
        "GET"
1,059,032✔
65
    }
1,059,032✔
66

67
    fn path_regex(&self) -> Regex {
2,118,073✔
68
        Regex::new(&format!(
2,118,073✔
69
            "^/v2/data_var/(?P<address>{})/(?P<contract>{})/(?P<varname>{})$",
2,118,073✔
70
            *STANDARD_PRINCIPAL_REGEX_STRING, *CONTRACT_NAME_REGEX_STRING, *CLARITY_NAME_REGEX
2,118,073✔
71
        ))
2,118,073✔
72
        .unwrap()
2,118,073✔
73
    }
2,118,073✔
74

75
    fn metrics_identifier(&self) -> &str {
×
76
        "/v2/data_var/:principal/:contract_name/:var_name"
×
77
    }
×
78

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

94
        let contract_identifier = request::get_contract_address(captures, "address", "contract")?;
×
95
        let varname = request::get_clarity_name(captures, "varname")?;
×
96

97
        self.contract_identifier = Some(contract_identifier);
×
98
        self.varname = Some(varname);
×
99

100
        let contents = HttpRequestContents::new().query_string(query);
×
101
        Ok(contents)
×
102
    }
×
103
}
104

105
/// Handle the HTTP request
106
impl RPCRequestHandler for RPCGetDataVarRequestHandler {
107
    /// Reset internal state
108
    fn restart(&mut self) {
×
109
        self.contract_identifier = None;
×
110
        self.varname = None;
×
111
    }
×
112

113
    /// Make the response
114
    fn try_handle_request(
×
115
        &mut self,
×
116
        preamble: HttpRequestPreamble,
×
117
        contents: HttpRequestContents,
×
118
        node: &mut StacksNodeState,
×
119
    ) -> Result<(HttpResponsePreamble, HttpResponseContents), NetError> {
×
120
        let contract_identifier = self.contract_identifier.take().ok_or(NetError::SendError(
×
121
            "`contract_identifier` not set".to_string(),
×
122
        ))?;
×
123
        let var_name = self
×
124
            .varname
×
125
            .take()
×
126
            .ok_or(NetError::SendError("`varname` not set".to_string()))?;
×
127

128
        let tip = match node.load_stacks_chain_tip(&preamble, &contents) {
×
129
            Ok(tip) => tip,
×
130
            Err(error_resp) => {
×
131
                return error_resp.try_into_contents().map_err(NetError::from);
×
132
            }
133
        };
134

135
        let with_proof = contents.get_with_proof();
×
136
        let key = ClarityDatabase::make_key_for_trip(
×
137
            &contract_identifier,
×
138
            StoreType::Variable,
×
139
            &var_name,
×
140
        );
141

142
        let data_opt = node.with_node_state(|_network, sortdb, chainstate, _mempool, _rpc_args| {
×
143
            chainstate.maybe_read_only_clarity_tx(
×
144
                &sortdb.index_handle_at_block(chainstate, &tip)?,
×
145
                &tip,
×
146
                |clarity_tx| {
×
147
                    clarity_tx.with_clarity_db_readonly(|clarity_db| {
×
148
                        let (value_hex, marf_proof): (String, _) = if with_proof {
×
149
                            clarity_db
×
150
                                .get_data_with_proof(&key)
×
151
                                .ok()
×
152
                                .flatten()
×
153
                                .map(|(a, b)| (a, Some(format!("0x{}", to_hex(&b)))))?
×
154
                        } else {
155
                            clarity_db
×
156
                                .get_data(&key)
×
157
                                .ok()
×
158
                                .flatten()
×
159
                                .map(|a| (a, None))?
×
160
                        };
161

162
                        let data = format!("0x{}", value_hex);
×
163
                        Some(DataVarResponse { data, marf_proof })
×
164
                    })
×
165
                },
×
166
            )
167
        });
×
168

169
        let data_resp = match data_opt {
×
170
            Ok(Some(Some(data))) => data,
×
171
            Ok(Some(None)) => {
172
                return StacksHttpResponse::new_error(
×
173
                    &preamble,
×
174
                    &HttpNotFound::new("Data var not found".to_string()),
×
175
                )
176
                .try_into_contents()
×
177
                .map_err(NetError::from);
×
178
            }
179
            Ok(None) | Err(_) => {
180
                return StacksHttpResponse::new_error(
×
181
                    &preamble,
×
182
                    &HttpNotFound::new("Chain tip not found".to_string()),
×
183
                )
184
                .try_into_contents()
×
185
                .map_err(NetError::from);
×
186
            }
187
        };
188

189
        let preamble = HttpResponsePreamble::ok_json(&preamble);
×
190
        let body = HttpResponseContents::try_from_json(&data_resp)?;
×
191
        Ok((preamble, body))
×
192
    }
×
193
}
194

195
/// Decode the HTTP response
196
impl HttpResponse for RPCGetDataVarRequestHandler {
197
    fn try_parse_response(
×
198
        &self,
×
199
        preamble: &HttpResponsePreamble,
×
200
        body: &[u8],
×
201
    ) -> Result<HttpResponsePayload, Error> {
×
202
        let datavar: DataVarResponse = parse_json(preamble, body)?;
×
203
        Ok(HttpResponsePayload::try_from_json(datavar)?)
×
204
    }
×
205
}
206

207
impl StacksHttpRequest {
208
    /// Make a new request for a data var
209
    pub fn new_getdatavar(
×
210
        host: PeerHost,
×
211
        contract_addr: StacksAddress,
×
212
        contract_name: ContractName,
×
213
        var_name: ClarityName,
×
214
        tip_req: TipRequest,
×
215
        with_proof: bool,
×
216
    ) -> StacksHttpRequest {
×
217
        StacksHttpRequest::new_for_peer(
×
218
            host,
×
219
            "GET".into(),
×
220
            format!(
×
221
                "/v2/data_var/{}/{}/{}",
222
                &contract_addr, &contract_name, &var_name
×
223
            ),
224
            HttpRequestContents::new()
×
225
                .for_tip(tip_req)
×
226
                .query_arg("proof".into(), if with_proof { "1" } else { "0" }.into()),
×
227
        )
228
        .expect("FATAL: failed to construct request from infallible data")
×
229
    }
×
230
}
231

232
impl StacksHttpResponse {
233
    pub fn decode_data_var_response(self) -> Result<DataVarResponse, NetError> {
×
234
        let contents = self.get_http_payload_ok()?;
×
235
        let contents_json: serde_json::Value = contents.try_into()?;
×
236
        let resp: DataVarResponse = serde_json::from_value(contents_json)
×
237
            .map_err(|_e| NetError::DeserializeError("Failed to load from JSON".to_string()))?;
×
238
        Ok(resp)
×
239
    }
×
240
}
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