@@ -21,12 +21,12 @@ use std::time::{Duration, SystemTime};
21
21
22
22
use blockstack_lib:: chainstate:: nakamoto:: NakamotoBlock ;
23
23
use blockstack_lib:: chainstate:: stacks:: TransactionPayload ;
24
+ #[ cfg( any( test, feature = "testing" ) ) ]
25
+ use blockstack_lib:: util_lib:: db:: FromColumn ;
24
26
use blockstack_lib:: util_lib:: db:: {
25
27
query_row, query_rows, sqlite_open, table_exists, tx_begin_immediate, u64_to_sql,
26
- Error as DBError ,
28
+ Error as DBError , FromRow ,
27
29
} ;
28
- #[ cfg( any( test, feature = "testing" ) ) ]
29
- use blockstack_lib:: util_lib:: db:: { FromColumn , FromRow } ;
30
30
use clarity:: types:: chainstate:: { BurnchainHeaderHash , StacksAddress } ;
31
31
use clarity:: types:: Address ;
32
32
use libsigner:: v0:: messages:: { RejectReason , RejectReasonPrefix , StateMachineUpdate } ;
@@ -72,6 +72,34 @@ impl StacksMessageCodec for NakamotoBlockVote {
72
72
}
73
73
}
74
74
75
+ #[ derive( Serialize , Deserialize , Debug , PartialEq ) ]
76
+ /// Struct for storing information about a burn block
77
+ pub struct BurnBlockInfo {
78
+ /// The hash of the burn block
79
+ pub block_hash : BurnchainHeaderHash ,
80
+ /// The height of the burn block
81
+ pub block_height : u64 ,
82
+ /// The consensus hash of the burn block
83
+ pub consensus_hash : ConsensusHash ,
84
+ /// The hash of the parent burn block
85
+ pub parent_burn_block_hash : BurnchainHeaderHash ,
86
+ }
87
+
88
+ impl FromRow < BurnBlockInfo > for BurnBlockInfo {
89
+ fn from_row ( row : & rusqlite:: Row ) -> Result < Self , DBError > {
90
+ let block_hash: BurnchainHeaderHash = row. get ( 0 ) ?;
91
+ let block_height: u64 = row. get ( 1 ) ?;
92
+ let consensus_hash: ConsensusHash = row. get ( 2 ) ?;
93
+ let parent_burn_block_hash: BurnchainHeaderHash = row. get ( 3 ) ?;
94
+ Ok ( BurnBlockInfo {
95
+ block_hash,
96
+ block_height,
97
+ consensus_hash,
98
+ parent_burn_block_hash,
99
+ } )
100
+ }
101
+ }
102
+
75
103
#[ derive( Serialize , Deserialize , Debug , PartialEq , Default ) ]
76
104
/// Store extra version-specific info in `BlockInfo`
77
105
pub enum ExtraBlockInfo {
@@ -566,6 +594,15 @@ CREATE TABLE IF NOT EXISTS signer_state_machine_updates (
566
594
PRIMARY KEY (signer_addr, reward_cycle)
567
595
) STRICT;"# ;
568
596
597
+ static ADD_PARENT_BURN_BLOCK_HASH : & str = r#"
598
+ ALTER TABLE burn_blocks
599
+ ADD COLUMN parent_burn_block_hash TEXT;
600
+ "# ;
601
+
602
+ static ADD_PARENT_BURN_BLOCK_HASH_INDEX : & str = r#"
603
+ CREATE INDEX IF NOT EXISTS burn_blocks_parent_burn_block_hash_idx on burn_blocks (parent_burn_block_hash);
604
+ "# ;
605
+
569
606
static SCHEMA_1 : & [ & str ] = & [
570
607
DROP_SCHEMA_0 ,
571
608
CREATE_DB_CONFIG ,
@@ -652,6 +689,12 @@ static SCHEMA_12: &[&str] = &[
652
689
"INSERT OR REPLACE INTO db_config (version) VALUES (12);" ,
653
690
] ;
654
691
692
+ static SCHEMA_13 : & [ & str ] = & [
693
+ ADD_PARENT_BURN_BLOCK_HASH ,
694
+ ADD_PARENT_BURN_BLOCK_HASH_INDEX ,
695
+ "INSERT INTO db_config (version) VALUES (13);" ,
696
+ ] ;
697
+
655
698
impl SignerDb {
656
699
/// The current schema version used in this build of the signer binary.
657
700
pub const SCHEMA_VERSION : u32 = 12 ;
@@ -852,6 +895,20 @@ impl SignerDb {
852
895
Ok ( ( ) )
853
896
}
854
897
898
+ /// Migrate from schema 12 to schema 13
899
+ fn schema_13_migration ( tx : & Transaction ) -> Result < ( ) , DBError > {
900
+ if Self :: get_schema_version ( tx) ? >= 13 {
901
+ // no migration necessary
902
+ return Ok ( ( ) ) ;
903
+ }
904
+
905
+ for statement in SCHEMA_13 . iter ( ) {
906
+ tx. execute_batch ( statement) ?;
907
+ }
908
+
909
+ Ok ( ( ) )
910
+ }
911
+
855
912
/// Register custom scalar functions used by the database
856
913
fn register_scalar_functions ( & self ) -> Result < ( ) , DBError > {
857
914
// Register helper function for determining if a block is a tenure change transaction
@@ -897,7 +954,8 @@ impl SignerDb {
897
954
9 => Self :: schema_10_migration ( & sql_tx) ?,
898
955
10 => Self :: schema_11_migration ( & sql_tx) ?,
899
956
11 => Self :: schema_12_migration ( & sql_tx) ?,
900
- 12 => break ,
957
+ 12 => Self :: schema_13_migration ( & sql_tx) ?,
958
+ 13 => break ,
901
959
x => return Err ( DBError :: Other ( format ! (
902
960
"Database schema is newer than supported by this binary. Expected version = {}, Database version = {x}" ,
903
961
Self :: SCHEMA_VERSION ,
@@ -1032,19 +1090,27 @@ impl SignerDb {
1032
1090
consensus_hash : & ConsensusHash ,
1033
1091
burn_height : u64 ,
1034
1092
received_time : & SystemTime ,
1093
+ parent_burn_block_hash : & BurnchainHeaderHash ,
1035
1094
) -> Result < ( ) , DBError > {
1036
1095
let received_ts = received_time
1037
1096
. duration_since ( std:: time:: UNIX_EPOCH )
1038
1097
. map_err ( |e| DBError :: Other ( format ! ( "Bad system time: {e}" ) ) ) ?
1039
1098
. as_secs ( ) ;
1040
- debug ! ( "Inserting burn block info" ; "burn_block_height" => burn_height, "burn_hash" => %burn_hash, "received" => received_ts, "ch" => %consensus_hash) ;
1099
+ debug ! ( "Inserting burn block info" ;
1100
+ "burn_block_height" => burn_height,
1101
+ "burn_hash" => %burn_hash,
1102
+ "received" => received_ts,
1103
+ "ch" => %consensus_hash,
1104
+ "parent_burn_block_hash" => %parent_burn_block_hash
1105
+ ) ;
1041
1106
self . db . execute (
1042
- "INSERT OR REPLACE INTO burn_blocks (block_hash, consensus_hash, block_height, received_time) VALUES (?1, ?2, ?3, ?4)" ,
1107
+ "INSERT OR REPLACE INTO burn_blocks (block_hash, consensus_hash, block_height, received_time, parent_burn_block_hash ) VALUES (?1, ?2, ?3, ?4, ?5 )" ,
1043
1108
params ! [
1044
1109
burn_hash,
1045
1110
consensus_hash,
1046
1111
u64_to_sql( burn_height) ?,
1047
1112
u64_to_sql( received_ts) ?,
1113
+ parent_burn_block_hash,
1048
1114
] ,
1049
1115
) ?;
1050
1116
Ok ( ( ) )
@@ -1084,6 +1150,26 @@ impl SignerDb {
1084
1150
Ok ( Some ( receive_time) )
1085
1151
}
1086
1152
1153
+ /// Lookup the burn block for a given burn block hash.
1154
+ pub fn get_burn_block_by_hash (
1155
+ & self ,
1156
+ burn_block_hash : & BurnchainHeaderHash ,
1157
+ ) -> Result < BurnBlockInfo , DBError > {
1158
+ let query =
1159
+ "SELECT block_hash, block_height, consensus_hash, parent_burn_block_hash FROM burn_blocks WHERE block_hash = ?" ;
1160
+ let args = params ! [ burn_block_hash] ;
1161
+
1162
+ query_row ( & self . db , query, args) ?. ok_or ( DBError :: NotFoundError )
1163
+ }
1164
+
1165
+ /// Lookup the burn block for a given consensus hash.
1166
+ pub fn get_burn_block_by_ch ( & self , ch : & ConsensusHash ) -> Result < BurnBlockInfo , DBError > {
1167
+ let query = "SELECT block_hash, block_height, consensus_hash, parent_burn_block_hash FROM burn_blocks WHERE consensus_hash = ?" ;
1168
+ let args = params ! [ ch] ;
1169
+
1170
+ query_row ( & self . db , query, args) ?. ok_or ( DBError :: NotFoundError )
1171
+ }
1172
+
1087
1173
/// Insert or replace a block into the database.
1088
1174
/// Preserves the `broadcast` column if replacing an existing block.
1089
1175
pub fn insert_block ( & mut self , block_info : & BlockInfo ) -> Result < ( ) , DBError > {
@@ -1717,8 +1803,14 @@ pub mod tests {
1717
1803
. duration_since ( SystemTime :: UNIX_EPOCH )
1718
1804
. unwrap ( )
1719
1805
. as_secs ( ) ;
1720
- db. insert_burn_block ( & test_burn_hash, & test_consensus_hash, 10 , & stime)
1721
- . unwrap ( ) ;
1806
+ db. insert_burn_block (
1807
+ & test_burn_hash,
1808
+ & test_consensus_hash,
1809
+ 10 ,
1810
+ & stime,
1811
+ & test_burn_hash,
1812
+ )
1813
+ . unwrap ( ) ;
1722
1814
1723
1815
let stored_time = db
1724
1816
. get_burn_block_receive_time ( & test_burn_hash)
0 commit comments