diff --git a/src/index/reorg.rs b/src/index/reorg.rs index 4b8ac59d4a..cedbc48892 100644 --- a/src/index/reorg.rs +++ b/src/index/reorg.rs @@ -19,10 +19,6 @@ impl Display for Error { impl std::error::Error for Error {} -const MAX_SAVEPOINTS: u32 = 2; -const SAVEPOINT_INTERVAL: u32 = 10; -const CHAIN_TIP_DISTANCE: u32 = 21; - pub(crate) struct Reorg {} impl Reorg { @@ -32,8 +28,10 @@ impl Reorg { match index.block_hash(height.checked_sub(1))? { Some(index_prev_blockhash) if index_prev_blockhash == bitcoind_prev_blockhash => Ok(()), Some(index_prev_blockhash) if index_prev_blockhash != bitcoind_prev_blockhash => { + let savepoint_interval = u32::try_from(index.settings.savepoint_interval()).unwrap(); + let max_savepoints = u32::try_from(index.settings.max_savepoints()).unwrap(); let max_recoverable_reorg_depth = - (MAX_SAVEPOINTS - 1) * SAVEPOINT_INTERVAL + height % SAVEPOINT_INTERVAL; + (max_savepoints - 1) * savepoint_interval + height % savepoint_interval; for depth in 1..max_recoverable_reorg_depth { let index_block_hash = index.block_hash(height.checked_sub(depth))?; @@ -94,9 +92,13 @@ impl Reorg { .unwrap_or(0); let blocks = index.client.get_blockchain_info()?.headers; - let result = (height < SAVEPOINT_INTERVAL.into() - || height.saturating_sub(last_savepoint_height) >= SAVEPOINT_INTERVAL.into()) - && blocks.saturating_sub(height) <= CHAIN_TIP_DISTANCE.into(); + + let savepoint_interval = u64::try_from(index.settings.savepoint_interval()).unwrap(); + let max_savepoints = u64::try_from(index.settings.max_savepoints()).unwrap(); + + let result = (height < savepoint_interval + || height.saturating_sub(last_savepoint_height) >= savepoint_interval) + && blocks.saturating_sub(height) <= savepoint_interval * max_savepoints + 1; log::trace!( "is_savepoint_required={}: height={}, last_savepoint_height={}, blocks={}", @@ -119,7 +121,11 @@ impl Reorg { let savepoints = wtx.list_persistent_savepoints()?.collect::>(); - if savepoints.len() >= usize::try_from(MAX_SAVEPOINTS).unwrap() { + if savepoints.len() >= index.settings.max_savepoints() { + log::info!( + "Cleaning up savepoints, keeping max {}", + index.settings.max_savepoints() + ); wtx.delete_persistent_savepoint(savepoints.into_iter().min().unwrap())?; } @@ -128,7 +134,8 @@ impl Reorg { let wtx = index.begin_write()?; - log::info!("creating savepoint at height {}", height); + log::info!("Creating savepoint at height {}", height); + wtx.persistent_savepoint()?; wtx diff --git a/src/options.rs b/src/options.rs index 8ce04861f7..5eea5de4db 100644 --- a/src/options.rs +++ b/src/options.rs @@ -30,6 +30,13 @@ pub struct Options { help = "Commit to index every blocks. [default: 5000]" )] pub(crate) commit_interval: Option, + #[arg( + long, + help = "Create a savepoint every blocks. [default: 10]" + )] + pub(crate) savepoint_interval: Option, + #[arg(long, help = "Store maximum blocks. [default: 2]")] + pub(crate) max_savepoints: Option, #[arg(long, help = "Load configuration from .")] pub(crate) config: Option, #[arg(long, help = "Load configuration from .")] diff --git a/src/settings.rs b/src/settings.rs index 149aaf30e4..9f814eac7a 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -10,6 +10,8 @@ pub struct Settings { bitcoin_rpc_username: Option, chain: Option, commit_interval: Option, + savepoint_interval: Option, + max_savepoints: Option, config: Option, config_dir: Option, cookie_file: Option, @@ -115,6 +117,8 @@ impl Settings { bitcoin_rpc_username: self.bitcoin_rpc_username.or(source.bitcoin_rpc_username), chain: self.chain.or(source.chain), commit_interval: self.commit_interval.or(source.commit_interval), + savepoint_interval: self.savepoint_interval.or(source.savepoint_interval), + max_savepoints: self.max_savepoints.or(source.max_savepoints), config: self.config.or(source.config), config_dir: self.config_dir.or(source.config_dir), cookie_file: self.cookie_file.or(source.cookie_file), @@ -159,6 +163,8 @@ impl Settings { .or(options.testnet4.then_some(Chain::Testnet4)) .or(options.chain_argument), commit_interval: options.commit_interval, + savepoint_interval: options.savepoint_interval, + max_savepoints: options.max_savepoints, config: options.config, config_dir: options.config_dir, cookie_file: options.cookie_file, @@ -247,6 +253,8 @@ impl Settings { bitcoin_rpc_username: get_string("BITCOIN_RPC_USERNAME"), chain: get_chain("CHAIN")?, commit_interval: get_usize("COMMIT_INTERVAL")?, + savepoint_interval: get_usize("SAVEPOINT_INTERVAL")?, + max_savepoints: get_usize("MAX_SAVEPOINTS")?, config: get_path("CONFIG"), config_dir: get_path("CONFIG_DIR"), cookie_file: get_path("COOKIE_FILE"), @@ -277,6 +285,8 @@ impl Settings { bitcoin_rpc_limit: None, chain: Some(Chain::Regtest), commit_interval: None, + savepoint_interval: None, + max_savepoints: None, config: None, config_dir: None, cookie_file: None, @@ -344,6 +354,8 @@ impl Settings { bitcoin_rpc_username: self.bitcoin_rpc_username, chain: Some(chain), commit_interval: Some(self.commit_interval.unwrap_or(5000)), + savepoint_interval: Some(self.savepoint_interval.unwrap_or(10)), + max_savepoints: Some(self.max_savepoints.unwrap_or(2)), config: None, config_dir: None, cookie_file: Some(cookie_file), @@ -470,6 +482,14 @@ impl Settings { self.commit_interval.unwrap() } + pub fn savepoint_interval(&self) -> usize { + self.savepoint_interval.unwrap() + } + + pub fn max_savepoints(&self) -> usize { + self.max_savepoints.unwrap() + } + pub fn cookie_file(&self) -> Result { if let Some(cookie_file) = &self.cookie_file { return Ok(cookie_file.clone()); @@ -933,6 +953,20 @@ mod tests { assert_eq!(arguments.options.commit_interval, Some(500)); } + #[test] + fn setting_savepoint_interval() { + let arguments = + Arguments::try_parse_from(["ord", "--savepoint-interval", "500", "index", "update"]).unwrap(); + assert_eq!(arguments.options.savepoint_interval, Some(500)); + } + + #[test] + fn setting_max_savepoints() { + let arguments = + Arguments::try_parse_from(["ord", "--max-savepoints", "10", "index", "update"]).unwrap(); + assert_eq!(arguments.options.max_savepoints, Some(10)); + } + #[test] fn index_runes() { assert!(parse(&["--chain=signet", "--index-runes"]).index_runes_raw()); @@ -1032,6 +1066,8 @@ mod tests { ("BITCOIN_RPC_USERNAME", "bitcoin username"), ("CHAIN", "signet"), ("COMMIT_INTERVAL", "1"), + ("SAVEPOINT_INTERVAL", "10"), + ("MAX_SAVEPOINTS", "2"), ("CONFIG", "config"), ("CONFIG_DIR", "config dir"), ("COOKIE_FILE", "cookie file"), @@ -1065,6 +1101,8 @@ mod tests { bitcoin_rpc_username: Some("bitcoin username".into()), chain: Some(Chain::Signet), commit_interval: Some(1), + savepoint_interval: Some(10), + max_savepoints: Some(2), config: Some("config".into()), config_dir: Some("config dir".into()), cookie_file: Some("cookie file".into()), @@ -1111,6 +1149,8 @@ mod tests { "--bitcoin-rpc-username=bitcoin username", "--chain=signet", "--commit-interval=1", + "--savepoint-interval=10", + "--max-savepoints=2", "--config=config", "--config-dir=config dir", "--cookie-file=cookie file", @@ -1137,6 +1177,8 @@ mod tests { bitcoin_rpc_username: Some("bitcoin username".into()), chain: Some(Chain::Signet), commit_interval: Some(1), + savepoint_interval: Some(10), + max_savepoints: Some(2), config: Some("config".into()), config_dir: Some("config dir".into()), cookie_file: Some("cookie file".into()), diff --git a/tests/settings.rs b/tests/settings.rs index 4591092611..0280e16df8 100644 --- a/tests/settings.rs +++ b/tests/settings.rs @@ -13,6 +13,8 @@ fn default() { "bitcoin_rpc_username": null, "chain": "mainnet", "commit_interval": 5000, + "savepoint_interval": 10, + "max_savepoints": 2, "config": null, "config_dir": null, "cookie_file": ".*\.cookie",