• 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

61.3
/clarity/src/vm/database/sqlite.rs
1
// Copyright (C) 2013-2020 Blockstack PBC, a public benefit corporation
2
// Copyright (C) 2020-2026 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_types::errors::IncomparableError;
18
use rusqlite::{Connection, OptionalExtension, params};
19
use stacks_common::types::chainstate::{BlockHeaderHash, StacksBlockId, TrieHash};
20
use stacks_common::types::sqlite::NO_PARAMS;
21
use stacks_common::util::db::tx_busy_handler;
22
use stacks_common::util::hash::Sha512Trunc256Sum;
23

24
use super::clarity_store::{ContractCommitment, make_contract_hash_key};
25
use super::{
26
    ClarityBackingStore, ClarityDatabase, ClarityDeserializable, NULL_BURN_STATE_DB,
27
    NULL_HEADER_DB, SpecialCaseHandler,
28
};
29
use crate::vm::analysis::{AnalysisDatabase, RuntimeCheckErrorKind};
30
use crate::vm::errors::{RuntimeError, VmExecutionError, VmInternalError};
31
use crate::vm::types::QualifiedContractIdentifier;
32

33
const SQL_FAIL_MESSAGE: &str = "PANIC: SQL Failure in Smart Contract VM.";
34

35
pub struct SqliteConnection {
36
    conn: Connection,
37
}
38

39
fn sqlite_put(conn: &Connection, key: &str, value: &str) -> Result<(), VmExecutionError> {
19,865,580✔
40
    let params = params![key, value];
19,865,580✔
41
    match conn.execute("REPLACE INTO data_table (key, value) VALUES (?, ?)", params) {
19,865,580✔
42
        Ok(_) => Ok(()),
19,865,580✔
43
        Err(e) => {
×
44
            error!("Failed to insert/replace ({key},{value}): {e:?}");
×
45
            Err(VmInternalError::DBError(SQL_FAIL_MESSAGE.into()).into())
×
46
        }
47
    }
48
}
19,865,580✔
49

50
fn sqlite_get(conn: &Connection, key: &str) -> Result<Option<String>, VmExecutionError> {
238,929,096✔
51
    trace!("sqlite_get {key}");
238,929,096✔
52
    let params = params![key];
238,929,096✔
53
    let res = match conn
238,929,096✔
54
        .query_row(
238,929,096✔
55
            "SELECT value FROM data_table WHERE key = ?",
238,929,096✔
56
            params,
238,929,096✔
57
            |row| row.get(0),
238,929,096✔
58
        )
59
        .optional()
238,929,096✔
60
    {
61
        Ok(x) => Ok(x),
238,929,096✔
62
        Err(e) => {
×
63
            error!("Failed to query '{key}': {e:?}");
×
64
            Err(VmInternalError::DBError(SQL_FAIL_MESSAGE.into()).into())
×
65
        }
66
    };
67

68
    trace!("sqlite_get {key}: {res:?}");
238,929,096✔
69
    res
238,929,096✔
70
}
238,929,096✔
71

72
fn sqlite_has_entry(conn: &Connection, key: &str) -> Result<bool, VmExecutionError> {
×
73
    Ok(sqlite_get(conn, key)?.is_some())
×
74
}
×
75

76
pub fn sqlite_get_contract_hash(
59,019,108✔
77
    store: &mut dyn ClarityBackingStore,
59,019,108✔
78
    contract: &QualifiedContractIdentifier,
59,019,108✔
79
) -> Result<(StacksBlockId, Sha512Trunc256Sum), VmExecutionError> {
59,019,108✔
80
    let key = make_contract_hash_key(contract);
59,019,108✔
81
    let contract_commitment = store
59,019,108✔
82
        .get_data(&key)?
59,019,108✔
83
        .map(|x| ContractCommitment::deserialize(&x))
59,019,108✔
84
        .ok_or_else(|| RuntimeCheckErrorKind::NoSuchContract(contract.to_string()))?;
59,019,108✔
85
    let ContractCommitment {
86
        block_height,
52,589,340✔
87
        hash: contract_hash,
52,589,340✔
88
    } = contract_commitment?;
52,589,340✔
89
    let bhh = store.get_block_at_height(block_height)
52,589,340✔
90
            .ok_or_else(|| VmInternalError::Expect("Should always be able to map from height to block hash when looking up contract information.".into()))?;
52,589,340✔
91
    Ok((bhh, contract_hash))
52,589,340✔
92
}
59,019,108✔
93

94
pub fn sqlite_insert_metadata(
2,123,856✔
95
    store: &mut dyn ClarityBackingStore,
2,123,856✔
96
    contract: &QualifiedContractIdentifier,
2,123,856✔
97
    key: &str,
2,123,856✔
98
    value: &str,
2,123,856✔
99
) -> Result<(), VmExecutionError> {
2,123,856✔
100
    let bhh = store.get_open_chain_tip();
2,123,856✔
101
    SqliteConnection::insert_metadata(
2,123,856✔
102
        store.get_side_store(),
2,123,856✔
103
        &bhh,
2,123,856✔
104
        &contract.to_string(),
2,123,856✔
105
        key,
2,123,856✔
106
        value,
2,123,856✔
107
    )
108
}
2,123,856✔
109

110
pub fn sqlite_get_metadata(
59,019,108✔
111
    store: &mut dyn ClarityBackingStore,
59,019,108✔
112
    contract: &QualifiedContractIdentifier,
59,019,108✔
113
    key: &str,
59,019,108✔
114
) -> Result<Option<String>, VmExecutionError> {
59,019,108✔
115
    let (bhh, _) = store.get_contract_hash(contract)?;
59,019,108✔
116
    SqliteConnection::get_metadata(store.get_side_store(), &bhh, &contract.to_string(), key)
52,589,340✔
117
}
59,019,108✔
118

119
pub fn sqlite_get_metadata_manual(
60✔
120
    store: &mut dyn ClarityBackingStore,
60✔
121
    at_height: u32,
60✔
122
    contract: &QualifiedContractIdentifier,
60✔
123
    key: &str,
60✔
124
) -> Result<Option<String>, VmExecutionError> {
60✔
125
    let bhh = store.get_block_at_height(at_height).ok_or_else(|| {
60✔
126
        warn!("Unknown block height when manually querying metadata"; "block_height" => at_height);
×
127
        RuntimeError::BadBlockHeight(at_height.to_string())
×
128
    })?;
×
129
    SqliteConnection::get_metadata(store.get_side_store(), &bhh, &contract.to_string(), key)
60✔
130
}
60✔
131

132
impl SqliteConnection {
133
    pub fn put(conn: &Connection, key: &str, value: &str) -> Result<(), VmExecutionError> {
19,865,580✔
134
        sqlite_put(conn, key, value)
19,865,580✔
135
    }
19,865,580✔
136

137
    pub fn get(conn: &Connection, key: &str) -> Result<Option<String>, VmExecutionError> {
238,929,096✔
138
        sqlite_get(conn, key)
238,929,096✔
139
    }
238,929,096✔
140

141
    pub fn insert_metadata(
2,123,856✔
142
        conn: &Connection,
2,123,856✔
143
        bhh: &StacksBlockId,
2,123,856✔
144
        contract_hash: &str,
2,123,856✔
145
        key: &str,
2,123,856✔
146
        value: &str,
2,123,856✔
147
    ) -> Result<(), VmExecutionError> {
2,123,856✔
148
        let key = format!("clr-meta::{contract_hash}::{key}");
2,123,856✔
149
        let params = params![bhh, key, value];
2,123,856✔
150

151
        if let Err(e) = conn.execute(
2,123,856✔
152
            "INSERT INTO metadata_table (blockhash, key, value) VALUES (?, ?, ?)",
2,123,856✔
153
            params,
2,123,856✔
154
        ) {
2,123,856✔
155
            error!("Failed to insert ({bhh},{key},{value}): {e:?}");
×
156
            return Err(VmInternalError::DBError(SQL_FAIL_MESSAGE.into()).into());
×
157
        }
2,123,856✔
158
        Ok(())
2,123,856✔
159
    }
2,123,856✔
160

161
    pub fn commit_metadata_to(
137,232✔
162
        conn: &Connection,
137,232✔
163
        from: &StacksBlockId,
137,232✔
164
        to: &StacksBlockId,
137,232✔
165
    ) -> Result<(), VmExecutionError> {
137,232✔
166
        let params = params![to, from];
137,232✔
167
        if let Err(e) = conn.execute(
137,232✔
168
            "UPDATE metadata_table SET blockhash = ? WHERE blockhash = ?",
137,232✔
169
            params,
137,232✔
170
        ) {
137,232✔
171
            error!("Failed to update {from} to {to}: {e:?}");
×
172
            return Err(VmInternalError::DBError(SQL_FAIL_MESSAGE.into()).into());
×
173
        }
137,232✔
174
        Ok(())
137,232✔
175
    }
137,232✔
176

177
    pub fn drop_metadata(conn: &Connection, from: &StacksBlockId) -> Result<(), VmExecutionError> {
378,468✔
178
        if let Err(e) = conn.execute(
378,468✔
179
            "DELETE FROM metadata_table WHERE blockhash = ?",
378,468✔
180
            params![from],
378,468✔
181
        ) {
378,468✔
182
            error!("Failed to drop metadata from {from}: {e:?}");
×
183
            return Err(VmInternalError::DBError(SQL_FAIL_MESSAGE.into()).into());
×
184
        }
378,468✔
185
        Ok(())
378,468✔
186
    }
378,468✔
187

188
    pub fn get_metadata(
52,589,388✔
189
        conn: &Connection,
52,589,388✔
190
        bhh: &StacksBlockId,
52,589,388✔
191
        contract_hash: &str,
52,589,388✔
192
        key: &str,
52,589,388✔
193
    ) -> Result<Option<String>, VmExecutionError> {
52,589,388✔
194
        let key = format!("clr-meta::{contract_hash}::{key}");
52,589,388✔
195
        let params = params![bhh, key];
52,589,388✔
196

197
        match conn
52,589,388✔
198
            .query_row(
52,589,388✔
199
                "SELECT value FROM metadata_table WHERE blockhash = ? AND key = ?",
52,589,388✔
200
                params,
52,589,388✔
201
                |row| row.get(0),
52,589,388✔
202
            )
203
            .optional()
52,589,388✔
204
        {
205
            Ok(x) => Ok(x),
52,589,388✔
206
            Err(e) => {
×
207
                error!("Failed to query ({bhh},{key}): {e:?}");
×
208
                Err(VmInternalError::DBError(SQL_FAIL_MESSAGE.into()).into())
×
209
            }
210
        }
211
    }
52,589,388✔
212

213
    pub fn has_entry(conn: &Connection, key: &str) -> Result<bool, VmExecutionError> {
×
214
        sqlite_has_entry(conn, key)
×
215
    }
×
216
}
217

218
impl SqliteConnection {
219
    pub fn initialize_conn(conn: &Connection) -> Result<(), VmExecutionError> {
5,856✔
220
        conn.query_row("PRAGMA journal_mode = WAL;", NO_PARAMS, |_row| Ok(()))
5,856✔
221
            .map_err(|x| VmInternalError::SqliteError(IncomparableError { err: x }))?;
5,856✔
222

223
        conn.execute(
5,856✔
224
            "CREATE TABLE IF NOT EXISTS data_table
5,856✔
225
                      (key TEXT PRIMARY KEY, value TEXT)",
5,856✔
226
            NO_PARAMS,
227
        )
228
        .map_err(|x| VmInternalError::SqliteError(IncomparableError { err: x }))?;
5,856✔
229

230
        conn.execute(
5,856✔
231
            "CREATE TABLE IF NOT EXISTS metadata_table
5,856✔
232
                      (key TEXT NOT NULL, blockhash TEXT, value TEXT,
5,856✔
233
                       UNIQUE (key, blockhash))",
5,856✔
234
            NO_PARAMS,
235
        )
236
        .map_err(|x| VmInternalError::SqliteError(IncomparableError { err: x }))?;
5,856✔
237

238
        conn.execute(
5,856✔
239
            "CREATE INDEX IF NOT EXISTS md_blockhashes ON metadata_table(blockhash)",
5,856✔
240
            NO_PARAMS,
241
        )
242
        .map_err(|x| VmInternalError::SqliteError(IncomparableError { err: x }))?;
5,856✔
243

244
        Self::check_schema(conn)?;
5,856✔
245

246
        Ok(())
5,856✔
247
    }
5,856✔
248

249
    pub fn memory() -> Result<Connection, VmExecutionError> {
60✔
250
        let contract_db = SqliteConnection::inner_open(":memory:")?;
60✔
251
        SqliteConnection::initialize_conn(&contract_db)?;
60✔
252
        Ok(contract_db)
60✔
253
    }
60✔
254

255
    pub fn check_schema(conn: &Connection) -> Result<(), VmExecutionError> {
17,008,368✔
256
        let sql = "SELECT sql FROM sqlite_master WHERE name=?";
17,008,368✔
257
        let _: String = conn
17,008,368✔
258
            .query_row(sql, params!["data_table"], |row| row.get(0))
17,008,368✔
259
            .map_err(|x| VmInternalError::SqliteError(IncomparableError { err: x }))?;
17,008,368✔
260
        let _: String = conn
17,002,572✔
261
            .query_row(sql, params!["metadata_table"], |row| row.get(0))
17,002,572✔
262
            .map_err(|x| VmInternalError::SqliteError(IncomparableError { err: x }))?;
17,002,572✔
263
        let _: String = conn
17,002,572✔
264
            .query_row(sql, params!["md_blockhashes"], |row| row.get(0))
17,002,572✔
265
            .map_err(|x| VmInternalError::SqliteError(IncomparableError { err: x }))?;
17,002,572✔
266
        Ok(())
17,002,572✔
267
    }
17,008,368✔
268

269
    fn inner_open(filename: &str) -> Result<Connection, VmExecutionError> {
60✔
270
        let conn = Connection::open(filename)
60✔
271
            .map_err(|x| VmInternalError::SqliteError(IncomparableError { err: x }))?;
60✔
272

273
        conn.busy_handler(Some(tx_busy_handler))
60✔
274
            .map_err(|x| VmInternalError::SqliteError(IncomparableError { err: x }))?;
60✔
275

276
        Ok(conn)
60✔
277
    }
60✔
278
}
279

280
pub struct MemoryBackingStore {
281
    side_store: Connection,
282
}
283

284
impl Default for MemoryBackingStore {
285
    fn default() -> Self {
×
286
        MemoryBackingStore::new()
×
287
    }
×
288
}
289

290
impl MemoryBackingStore {
291
    #[allow(clippy::unwrap_used)]
292
    pub fn new() -> MemoryBackingStore {
60✔
293
        let side_store = SqliteConnection::memory().unwrap();
60✔
294

295
        let mut memory_marf = MemoryBackingStore { side_store };
60✔
296

297
        memory_marf.as_clarity_db().initialize();
60✔
298

299
        memory_marf
60✔
300
    }
60✔
301

302
    pub fn as_clarity_db(&mut self) -> ClarityDatabase<'_> {
108✔
303
        ClarityDatabase::new(self, &NULL_HEADER_DB, &NULL_BURN_STATE_DB)
108✔
304
    }
108✔
305

306
    pub fn as_analysis_db(&mut self) -> AnalysisDatabase<'_> {
12✔
307
        AnalysisDatabase::new(self)
12✔
308
    }
12✔
309
}
310

311
impl ClarityBackingStore for MemoryBackingStore {
312
    fn set_block_hash(&mut self, bhh: StacksBlockId) -> Result<StacksBlockId, VmExecutionError> {
×
313
        Err(RuntimeError::UnknownBlockHeaderHash(BlockHeaderHash(bhh.0)).into())
×
314
    }
×
315

316
    fn get_data(&mut self, key: &str) -> Result<Option<String>, VmExecutionError> {
×
317
        SqliteConnection::get(self.get_side_store(), key)
×
318
    }
×
319

320
    fn get_data_from_path(&mut self, hash: &TrieHash) -> Result<Option<String>, VmExecutionError> {
×
321
        SqliteConnection::get(self.get_side_store(), hash.to_string().as_str())
×
322
    }
×
323

324
    fn get_data_with_proof(
×
325
        &mut self,
×
326
        key: &str,
×
327
    ) -> Result<Option<(String, Vec<u8>)>, VmExecutionError> {
×
328
        Ok(SqliteConnection::get(self.get_side_store(), key)?.map(|x| (x, vec![])))
×
329
    }
×
330

331
    fn get_data_with_proof_from_path(
×
332
        &mut self,
×
333
        hash: &TrieHash,
×
334
    ) -> Result<Option<(String, Vec<u8>)>, VmExecutionError> {
×
335
        self.get_data_with_proof(&hash.to_string())
×
336
    }
×
337

338
    fn get_side_store(&mut self) -> &Connection {
×
339
        &self.side_store
×
340
    }
×
341

342
    fn get_block_at_height(&mut self, height: u32) -> Option<StacksBlockId> {
×
343
        if height == 0 {
×
344
            Some(StacksBlockId([255; 32]))
×
345
        } else {
346
            None
×
347
        }
348
    }
×
349

350
    fn get_open_chain_tip(&mut self) -> StacksBlockId {
×
351
        StacksBlockId([255; 32])
×
352
    }
×
353

354
    fn get_open_chain_tip_height(&mut self) -> u32 {
×
355
        0
×
356
    }
×
357

358
    fn get_current_block_height(&mut self) -> u32 {
×
359
        1
×
360
    }
×
361

362
    fn get_cc_special_cases_handler(&self) -> Option<SpecialCaseHandler> {
×
363
        None
×
364
    }
×
365

366
    fn put_all_data(&mut self, items: Vec<(String, String)>) -> Result<(), VmExecutionError> {
×
367
        for (key, value) in items.into_iter() {
×
368
            SqliteConnection::put(self.get_side_store(), &key, &value)?;
×
369
        }
370
        Ok(())
×
371
    }
×
372

373
    fn get_contract_hash(
×
374
        &mut self,
×
375
        contract: &QualifiedContractIdentifier,
×
376
    ) -> Result<(StacksBlockId, Sha512Trunc256Sum), VmExecutionError> {
×
377
        sqlite_get_contract_hash(self, contract)
×
378
    }
×
379

380
    fn insert_metadata(
×
381
        &mut self,
×
382
        contract: &QualifiedContractIdentifier,
×
383
        key: &str,
×
384
        value: &str,
×
385
    ) -> Result<(), VmExecutionError> {
×
386
        sqlite_insert_metadata(self, contract, key, value)
×
387
    }
×
388

389
    fn get_metadata(
×
390
        &mut self,
×
391
        contract: &QualifiedContractIdentifier,
×
392
        key: &str,
×
393
    ) -> Result<Option<String>, VmExecutionError> {
×
394
        sqlite_get_metadata(self, contract, key)
×
395
    }
×
396

397
    fn get_metadata_manual(
×
398
        &mut self,
×
399
        at_height: u32,
×
400
        contract: &QualifiedContractIdentifier,
×
401
        key: &str,
×
402
    ) -> Result<Option<String>, VmExecutionError> {
×
403
        sqlite_get_metadata_manual(self, at_height, contract, key)
×
404
    }
×
405
}
406

407
#[test]
408
fn trigger_bad_block_height() {
×
409
    let mut store = MemoryBackingStore::default();
×
410
    let contract_id = QualifiedContractIdentifier::transient();
×
411
    // Use a block height that does NOT exist in MemoryBackingStore
412
    // MemoryBackingStore::get_block_at_height returns None for any height != 0
413
    let nonexistent_height = 42;
×
414
    let key = "some-metadata-key";
×
415

416
    let err =
×
417
        sqlite_get_metadata_manual(&mut store, nonexistent_height, &contract_id, key).unwrap_err();
×
418

419
    assert!(
×
420
        matches!(
×
421
            err,
×
422
            VmExecutionError::Runtime(RuntimeError::BadBlockHeight(_), _)
423
        ),
424
        "Expected BadBlockHeight. Got {err}"
425
    );
426
}
×
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