@@ -532,6 +532,18 @@ CREATE TABLE IF NOT EXISTS signer_state_machine_updates (
532
532
PRIMARY KEY (signer_addr, reward_cycle)
533
533
) STRICT;"# ;
534
534
535
+ static CREATE_BLOCK_PRE_COMMITS_TABLE : & str = r#"
536
+ CREATE TABLE IF NOT EXISTS block_pre_commits (
537
+ -- The block sighash commits to all of the stacks and burnchain state as of its parent,
538
+ -- as well as the tenure itself so there's no need to include the reward cycle. Just
539
+ -- the sighash is sufficient to uniquely identify the block across all burnchain, PoX,
540
+ -- and stacks forks.
541
+ signer_signature_hash TEXT NOT NULL,
542
+ -- signer address committing to sign the block
543
+ signer_addr TEXT NOT NULL,
544
+ PRIMARY KEY (signer_signature_hash, signer_addr)
545
+ ) STRICT;"# ;
546
+
535
547
static SCHEMA_1 : & [ & str ] = & [
536
548
DROP_SCHEMA_0 ,
537
549
CREATE_DB_CONFIG ,
@@ -613,9 +625,14 @@ static SCHEMA_11: &[&str] = &[
613
625
"INSERT INTO db_config (version) VALUES (11);" ,
614
626
] ;
615
627
628
+ static SCHEMA_12 : & [ & str ] = & [
629
+ CREATE_BLOCK_PRE_COMMITS_TABLE ,
630
+ "INSERT INTO db_config (version) VALUES (12);" ,
631
+ ] ;
632
+
616
633
impl SignerDb {
617
634
/// The current schema version used in this build of the signer binary.
618
- pub const SCHEMA_VERSION : u32 = 11 ;
635
+ pub const SCHEMA_VERSION : u32 = 12 ;
619
636
620
637
/// Create a new `SignerState` instance.
621
638
/// This will create a new SQLite database at the given path
@@ -799,6 +816,20 @@ impl SignerDb {
799
816
Ok ( ( ) )
800
817
}
801
818
819
+ /// Migrate from schema 11 to schema 12
820
+ fn schema_12_migration ( tx : & Transaction ) -> Result < ( ) , DBError > {
821
+ if Self :: get_schema_version ( tx) ? >= 12 {
822
+ // no migration necessary
823
+ return Ok ( ( ) ) ;
824
+ }
825
+
826
+ for statement in SCHEMA_12 . iter ( ) {
827
+ tx. execute_batch ( statement) ?;
828
+ }
829
+
830
+ Ok ( ( ) )
831
+ }
832
+
802
833
/// Register custom scalar functions used by the database
803
834
fn register_scalar_functions ( & self ) -> Result < ( ) , DBError > {
804
835
// Register helper function for determining if a block is a tenure change transaction
@@ -843,7 +874,8 @@ impl SignerDb {
843
874
8 => Self :: schema_9_migration ( & sql_tx) ?,
844
875
9 => Self :: schema_10_migration ( & sql_tx) ?,
845
876
10 => Self :: schema_11_migration ( & sql_tx) ?,
846
- 11 => break ,
877
+ 11 => Self :: schema_12_migration ( & sql_tx) ?,
878
+ 12 => break ,
847
879
x => return Err ( DBError :: Other ( format ! (
848
880
"Database schema is newer than supported by this binary. Expected version = {}, Database version = {x}" ,
849
881
Self :: SCHEMA_VERSION ,
@@ -1412,6 +1444,39 @@ impl SignerDb {
1412
1444
}
1413
1445
Ok ( result)
1414
1446
}
1447
+
1448
+ /// Record an observed block pre commit
1449
+ pub fn add_block_pre_commit (
1450
+ & self ,
1451
+ block_sighash : & Sha512Trunc256Sum ,
1452
+ address : & StacksAddress ,
1453
+ ) -> Result < ( ) , DBError > {
1454
+ let qry = "INSERT OR REPLACE INTO block_pre_commits (signer_signature_hash, signer_addr) VALUES (?1, ?2);" ;
1455
+ let args = params ! [ block_sighash, address. to_string( ) ] ;
1456
+
1457
+ debug ! ( "Inserting block pre commit." ;
1458
+ "signer_signature_hash" => %block_sighash,
1459
+ "signer_addr" => %address) ;
1460
+
1461
+ self . db . execute ( qry, args) ?;
1462
+ Ok ( ( ) )
1463
+ }
1464
+
1465
+ /// Get all pre committers for a block
1466
+ pub fn get_block_pre_committers (
1467
+ & self ,
1468
+ block_sighash : & Sha512Trunc256Sum ,
1469
+ ) -> Result < Vec < StacksAddress > , DBError > {
1470
+ let qry = "SELECT signer_addr FROM block_pre_commits WHERE signer_signature_hash = ?1" ;
1471
+ let args = params ! [ block_sighash] ;
1472
+ let addrs_txt: Vec < String > = query_rows ( & self . db , qry, args) ?;
1473
+
1474
+ let res: Result < Vec < _ > , _ > = addrs_txt
1475
+ . into_iter ( )
1476
+ . map ( |addr| StacksAddress :: from_string ( & addr) . ok_or ( DBError :: Corruption ) )
1477
+ . collect ( ) ;
1478
+ res
1479
+ }
1415
1480
}
1416
1481
1417
1482
fn try_deserialize < T > ( s : Option < String > ) -> Result < Option < T > , DBError >
@@ -2515,4 +2580,50 @@ pub mod tests {
2515
2580
assert_eq ! ( updates. get( & address_2) , None ) ;
2516
2581
assert_eq ! ( updates. get( & address_3) , Some ( & update_3) ) ;
2517
2582
}
2583
+
2584
+ #[ test]
2585
+ fn insert_and_get_state_block_pre_commits ( ) {
2586
+ let db_path = tmp_db_path ( ) ;
2587
+ let db = SignerDb :: new ( db_path) . expect ( "Failed to create signer db" ) ;
2588
+ let block_sighash1 = Sha512Trunc256Sum ( [ 1u8 ; 32 ] ) ;
2589
+ let address1 = StacksAddress :: p2pkh (
2590
+ false ,
2591
+ & StacksPublicKey :: from_private ( & StacksPrivateKey :: random ( ) ) ,
2592
+ ) ;
2593
+ let block_sighash2 = Sha512Trunc256Sum ( [ 2u8 ; 32 ] ) ;
2594
+ let address2 = StacksAddress :: p2pkh (
2595
+ false ,
2596
+ & StacksPublicKey :: from_private ( & StacksPrivateKey :: random ( ) ) ,
2597
+ ) ;
2598
+ let address3 = StacksAddress :: p2pkh (
2599
+ false ,
2600
+ & StacksPublicKey :: from_private ( & StacksPrivateKey :: random ( ) ) ,
2601
+ ) ;
2602
+ assert ! ( db
2603
+ . get_block_pre_committers( & block_sighash1)
2604
+ . unwrap( )
2605
+ . is_empty( ) ) ;
2606
+
2607
+ db. add_block_pre_commit ( & block_sighash1, & address1) . unwrap ( ) ;
2608
+ assert_eq ! (
2609
+ db. get_block_pre_committers( & block_sighash1) . unwrap( ) ,
2610
+ vec![ address1]
2611
+ ) ;
2612
+
2613
+ db. add_block_pre_commit ( & block_sighash1, & address2) . unwrap ( ) ;
2614
+ assert_eq ! (
2615
+ db. get_block_pre_committers( & block_sighash1) . unwrap( ) ,
2616
+ vec![ address2, address1]
2617
+ ) ;
2618
+
2619
+ db. add_block_pre_commit ( & block_sighash2, & address3) . unwrap ( ) ;
2620
+ assert_eq ! (
2621
+ db. get_block_pre_committers( & block_sighash1) . unwrap( ) ,
2622
+ vec![ address2, address1]
2623
+ ) ;
2624
+ assert_eq ! (
2625
+ db. get_block_pre_committers( & block_sighash2) . unwrap( ) ,
2626
+ vec![ address3]
2627
+ ) ;
2628
+ }
2518
2629
}
0 commit comments