Skip to content

Commit f0e9cfe

Browse files
Merge branch 'stx-develop' into test/improve-vm-tests
2 parents b3e9510 + 7e8d141 commit f0e9cfe

13 files changed

Lines changed: 1459 additions & 5 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add snapshot copy of canonical burnchain.sqlite tables into squashed output, using the squashed sortition DB as the canonical set.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add snapshot copy of Bitcoin SPV headers and complete chain-work intervals up to the squash height into squashed output.

stacks-profiler/src/platform.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ mod linux {
105105
mod windows {
106106
/// Win32 FILETIME (100-nanosecond intervals).
107107
#[repr(C)]
108+
#[allow(clippy::upper_case_acronyms)]
108109
struct FILETIME {
109110
low: u32,
110111
high: u32,

stackslib/src/burnchains/bitcoin/spv.rs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,14 @@ pub const BITCOIN_GENESIS_BLOCK_HASH_REGTEST: &str =
5454
pub const BLOCK_DIFFICULTY_CHUNK_SIZE: u64 = 2016;
5555
const BLOCK_DIFFICULTY_INTERVAL: u32 = 14 * 24 * 60 * 60; // two weeks, in seconds
5656

57+
/// Number of complete difficulty intervals at `burn_height`: interval `k`
58+
/// is complete iff its last header height,
59+
/// `(k + 1) * BLOCK_DIFFICULTY_CHUNK_SIZE - 1`, is at or below
60+
/// `burn_height`.
61+
pub fn num_complete_chain_work_intervals(burn_height: u64) -> u64 {
62+
burn_height.saturating_add(1) / BLOCK_DIFFICULTY_CHUNK_SIZE
63+
}
64+
5765
pub const SPV_DB_VERSION: &str = "3";
5866

5967
const SPV_INITIAL_SCHEMA: &[&str] = &[
@@ -743,7 +751,7 @@ impl SpvClient {
743751
header: BlockHeader,
744752
height: u64,
745753
) -> Result<(), btc_error> {
746-
let sql = "INSERT OR REPLACE INTO headers
754+
let sql = "INSERT OR REPLACE INTO headers
747755
(version, prev_blockhash, merkle_root, time, bits, nonce, height, hash)
748756
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)";
749757
let args = params![
@@ -915,6 +923,22 @@ impl SpvClient {
915923
self.write_block_headers(height, headers)
916924
}
917925

926+
/// Insert a `chain_work` interval row directly (test fixtures only;
927+
/// production rows go through [`SpvClient::update_chain_work`]).
928+
#[cfg(test)]
929+
pub fn test_insert_chain_work(
930+
conn: &DBConn,
931+
interval: u64,
932+
work: &str,
933+
) -> Result<(), btc_error> {
934+
conn.execute(
935+
"INSERT INTO chain_work (interval, work) VALUES (?1, ?2)",
936+
params![u64_to_sql(interval)?, work],
937+
)
938+
.map_err(|e| btc_error::from(db_error::SqliteError(e)))?;
939+
Ok(())
940+
}
941+
918942
/// Insert block headers into the headers DB.
919943
/// Verify that the first header's parent exists and connects with this header chain, and verify that
920944
/// the headers are themselves contiguous.
@@ -994,7 +1018,7 @@ impl SpvClient {
9941018
Some(child_header) => {
9951019
// contiguous?
9961020
if last_block_header.header.bitcoin_hash() != child_header.header.prev_blockhash {
997-
warn!("Received discontiguous headers at height {}: we have child {:?} ({}), but were given {:?} ({})",
1021+
warn!("Received discontiguous headers at height {}: we have child {:?} ({}), but were given {:?} ({})",
9981022
end_height, &child_header, child_header.header.bitcoin_hash(), &last_block_header, &last_block_header.header.bitcoin_hash());
9991023
return Err(btc_error::NoncontiguousHeader);
10001024
}

stackslib/src/burnchains/db.rs

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -945,6 +945,114 @@ impl BurnchainDB {
945945
}
946946
}
947947

948+
/// Count the burn header hashes yielded by `canonical_sql` (a SELECT
949+
/// producing one TEXT column) that have no `burnchain_db_block_headers`
950+
/// row in `headers_schema`. Both arguments are interpolated into SQL;
951+
/// pass only trusted fixed fragments.
952+
pub(crate) fn count_canonical_burn_hashes_missing_from(
953+
conn: &Connection,
954+
headers_schema: &str,
955+
canonical_sql: &str,
956+
) -> Result<u64, DBError> {
957+
conn.query_row(
958+
&format!(
959+
"SELECT COUNT(*) FROM ({canonical_sql}) \
960+
WHERE burn_header_hash NOT IN \
961+
(SELECT block_hash FROM {headers_schema}.burnchain_db_block_headers)"
962+
),
963+
NO_PARAMS,
964+
|row| row.get(0),
965+
)
966+
.map_err(DBError::from)
967+
}
968+
}
969+
970+
// Raw-row test fixture writers. Each helper owns its table's column
971+
// list so fixtures can't drift from the schema; values are raw
972+
// TEXT/ints because fixtures use readable labels, not valid hashes
973+
// (which the typed write paths would reject).
974+
#[cfg(test)]
975+
impl BurnchainDB {
976+
/// Insert a `burnchain_db_block_headers` row with no transactions.
977+
pub fn test_insert_block_header_row(
978+
conn: &Connection,
979+
block_height: u64,
980+
block_hash: &str,
981+
parent_block_hash: &str,
982+
) -> Result<(), DBError> {
983+
conn.execute(
984+
"INSERT INTO burnchain_db_block_headers \
985+
(block_height, block_hash, parent_block_hash, num_txs, timestamp) \
986+
VALUES (?1, ?2, ?3, 0, 0)",
987+
params![u64_to_sql(block_height)?, block_hash, parent_block_hash],
988+
)?;
989+
Ok(())
990+
}
991+
992+
/// Insert a `burnchain_db_block_ops` row with an opaque op payload.
993+
pub fn test_insert_block_ops_row(
994+
conn: &Connection,
995+
block_hash: &str,
996+
op: &str,
997+
txid: &str,
998+
) -> rusqlite::Result<()> {
999+
conn.execute(
1000+
"INSERT INTO burnchain_db_block_ops (block_hash, op, txid) VALUES (?1, ?2, ?3)",
1001+
params![block_hash, op, txid],
1002+
)?;
1003+
Ok(())
1004+
}
1005+
1006+
/// Insert a minimal `block_commit_metadata` row.
1007+
pub fn test_insert_block_commit_metadata_row(
1008+
conn: &Connection,
1009+
burn_block_hash: &str,
1010+
txid: &str,
1011+
block_height: u64,
1012+
anchor_block: Option<u64>,
1013+
) -> Result<(), DBError> {
1014+
conn.execute(
1015+
"INSERT INTO block_commit_metadata \
1016+
(burn_block_hash, txid, block_height, vtxindex, anchor_block, \
1017+
anchor_block_descendant) \
1018+
VALUES (?1, ?2, ?3, 0, ?4, NULL)",
1019+
params![
1020+
burn_block_hash,
1021+
txid,
1022+
u64_to_sql(block_height)?,
1023+
opt_u64_to_sql(anchor_block)?,
1024+
],
1025+
)?;
1026+
Ok(())
1027+
}
1028+
1029+
/// Insert an `anchor_blocks` row.
1030+
pub fn test_insert_anchor_block_row(
1031+
conn: &Connection,
1032+
reward_cycle: u64,
1033+
) -> Result<(), DBError> {
1034+
conn.execute(
1035+
"INSERT INTO anchor_blocks (reward_cycle) VALUES (?1)",
1036+
params![u64_to_sql(reward_cycle)?],
1037+
)?;
1038+
Ok(())
1039+
}
1040+
1041+
/// Insert an `overrides` row.
1042+
pub fn test_insert_override_row(
1043+
conn: &Connection,
1044+
reward_cycle: u64,
1045+
affirmation_map: &str,
1046+
) -> Result<(), DBError> {
1047+
conn.execute(
1048+
"INSERT INTO overrides (reward_cycle, affirmation_map) VALUES (?1, ?2)",
1049+
params![u64_to_sql(reward_cycle)?, affirmation_map],
1050+
)?;
1051+
Ok(())
1052+
}
1053+
}
1054+
1055+
impl BurnchainDB {
9481056
// do NOT call directly; only call directly in tests.
9491057
// This is only `pub` because the tests for it live in a different file.
9501058
pub fn store_new_burnchain_block_ops_unchecked(

stackslib/src/chainstate/burn/db/sortdb.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4554,10 +4554,21 @@ impl SortitionDB {
45544554
query_row(conn, qry, NO_PARAMS).map(|opt| opt.expect("CORRUPTION: No burnchain tips"))
45554555
}
45564556

4557+
/// Get the distinct burn header hashes of all snapshots, forks included.
4558+
/// Only on a squashed sortition DB is this exactly the canonical
4559+
/// burnchain.
4560+
pub fn get_all_snapshot_burn_header_hashes(
4561+
conn: &Connection,
4562+
) -> Result<Vec<BurnchainHeaderHash>, db_error> {
4563+
let mut stmt = conn.prepare("SELECT DISTINCT burn_header_hash FROM snapshots")?;
4564+
let rows = stmt.query_map(NO_PARAMS, |row| row.get(0))?;
4565+
rows.collect::<Result<Vec<_>, _>>().map_err(db_error::from)
4566+
}
4567+
45574568
/// Get the burn header hash of the snapshot with the given sortition
45584569
/// ID, or `None` if no such snapshot exists. Returns the raw stored
45594570
/// TEXT so callers can preserve the value byte-for-byte.
4560-
pub(crate) fn get_snapshot_burn_header_hash(
4571+
pub fn get_snapshot_burn_header_hash(
45614572
conn: &Connection,
45624573
sortition_id: &SortitionId,
45634574
) -> Result<Option<String>, db_error> {

0 commit comments

Comments
 (0)