Skip to content

Commit 3262678

Browse files
authored
Merge pull request #4692 from stacks-network/hotfix/update-sortdb-epochs
Update SortitionDB epochs on connect
2 parents 8ef0d61 + fcaf971 commit 3262678

File tree

1 file changed

+125
-0
lines changed

1 file changed

+125
-0
lines changed

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

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2848,6 +2848,80 @@ impl SortitionDB {
28482848
Ok(())
28492849
}
28502850

2851+
/// Validates given StacksEpochs (will runtime panic if there is any invalid StacksEpoch structuring) and
2852+
/// replaces them into the SortitionDB's epochs table
2853+
fn validate_and_replace_epochs(
2854+
db_tx: &Transaction,
2855+
epochs: &[StacksEpoch],
2856+
) -> Result<(), db_error> {
2857+
let epochs = StacksEpoch::validate_epochs(epochs);
2858+
let existing_epochs = Self::get_stacks_epochs(db_tx)?;
2859+
if existing_epochs == epochs {
2860+
return Ok(());
2861+
}
2862+
2863+
let tip = SortitionDB::get_canonical_burn_chain_tip(db_tx)?;
2864+
let existing_epoch_idx = StacksEpoch::find_epoch(&existing_epochs, tip.block_height)
2865+
.unwrap_or_else(|| {
2866+
panic!(
2867+
"FATAL: Sortition tip {} has no epoch in its existing epochs table",
2868+
tip.block_height
2869+
);
2870+
});
2871+
2872+
let new_epoch_idx =
2873+
StacksEpoch::find_epoch(&epochs, tip.block_height).unwrap_or_else(|| {
2874+
panic!(
2875+
"FATAL: Sortition tip {} has no epoch in the configured epochs list",
2876+
tip.block_height
2877+
);
2878+
});
2879+
2880+
// can't retcon epochs -- all epochs up to (but excluding) the tip's epoch in both epoch
2881+
// lists must be the same.
2882+
for i in 0..existing_epoch_idx.min(new_epoch_idx) {
2883+
if existing_epochs[i] != epochs[i] {
2884+
panic!(
2885+
"FATAL: tried to retcon epoch {:?} into epoch {:?}",
2886+
&existing_epochs[i], &epochs[i]
2887+
);
2888+
}
2889+
}
2890+
2891+
// can't change parameters of the current epoch in either epoch list,
2892+
// except for the end height (and only if it hasn't been reached yet)
2893+
let mut diff_epoch = existing_epochs[existing_epoch_idx].clone();
2894+
diff_epoch.end_height = epochs[new_epoch_idx].end_height;
2895+
2896+
if diff_epoch != epochs[new_epoch_idx] {
2897+
panic!(
2898+
"FATAL: tried to change current epoch {:?} into {:?}",
2899+
&existing_epochs[existing_epoch_idx], &epochs[new_epoch_idx]
2900+
);
2901+
}
2902+
2903+
if tip.block_height >= epochs[new_epoch_idx].end_height {
2904+
panic!("FATAL: tip has reached or passed the end of the configured epoch");
2905+
}
2906+
2907+
info!("Replace existing epochs with new epochs");
2908+
db_tx.execute("DELETE FROM epochs;", NO_PARAMS)?;
2909+
for epoch in epochs.into_iter() {
2910+
let args: &[&dyn ToSql] = &[
2911+
&(epoch.epoch_id as u32),
2912+
&u64_to_sql(epoch.start_height)?,
2913+
&u64_to_sql(epoch.end_height)?,
2914+
&epoch.block_limit,
2915+
&epoch.network_epoch,
2916+
];
2917+
db_tx.execute(
2918+
"INSERT INTO epochs (epoch_id,start_block_height,end_block_height,block_limit,network_epoch) VALUES (?1,?2,?3,?4,?5)",
2919+
args
2920+
)?;
2921+
}
2922+
Ok(())
2923+
}
2924+
28512925
/// Get a block commit by its content-addressed location in a specific sortition.
28522926
pub fn get_block_commit(
28532927
conn: &Connection,
@@ -3322,6 +3396,10 @@ impl SortitionDB {
33223396

33233397
self.apply_schema_8_migration(migrator.take())?;
33243398
} else if version == expected_version {
3399+
let tx = self.tx_begin()?;
3400+
SortitionDB::validate_and_replace_epochs(&tx, epochs)?;
3401+
tx.commit()?;
3402+
33253403
return Ok(());
33263404
} else {
33273405
panic!("The schema version of the sortition DB is invalid.")
@@ -10629,4 +10707,51 @@ pub mod tests {
1062910707
good_ops_2[3]
1063010708
);
1063110709
}
10710+
10711+
#[test]
10712+
fn test_validate_and_replace_epochs() {
10713+
use crate::core::STACKS_EPOCHS_MAINNET;
10714+
10715+
let path_root = "/tmp/test_validate_and_replace_epochs";
10716+
if fs::metadata(path_root).is_ok() {
10717+
fs::remove_dir_all(path_root).unwrap();
10718+
}
10719+
10720+
fs::create_dir_all(path_root).unwrap();
10721+
10722+
let mut bad_epochs = STACKS_EPOCHS_MAINNET.to_vec();
10723+
let idx = bad_epochs.len() - 2;
10724+
bad_epochs[idx].end_height += 1;
10725+
bad_epochs[idx + 1].start_height += 1;
10726+
10727+
let sortdb = SortitionDB::connect(
10728+
&format!("{}/sortdb.sqlite", &path_root),
10729+
0,
10730+
&BurnchainHeaderHash([0x00; 32]),
10731+
0,
10732+
&bad_epochs,
10733+
PoxConstants::mainnet_default(),
10734+
None,
10735+
true,
10736+
)
10737+
.unwrap();
10738+
10739+
let db_epochs = SortitionDB::get_stacks_epochs(sortdb.conn()).unwrap();
10740+
assert_eq!(db_epochs, bad_epochs);
10741+
10742+
let fixed_sortdb = SortitionDB::connect(
10743+
&format!("{}/sortdb.sqlite", &path_root),
10744+
0,
10745+
&BurnchainHeaderHash([0x00; 32]),
10746+
0,
10747+
&STACKS_EPOCHS_MAINNET.to_vec(),
10748+
PoxConstants::mainnet_default(),
10749+
None,
10750+
true,
10751+
)
10752+
.unwrap();
10753+
10754+
let db_epochs = SortitionDB::get_stacks_epochs(sortdb.conn()).unwrap();
10755+
assert_eq!(db_epochs, STACKS_EPOCHS_MAINNET.to_vec());
10756+
}
1063210757
}

0 commit comments

Comments
 (0)