-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implementation of the state rewind feature for the RocksDB (#1996)
Closes #451 ## Overview Added support for the state rewind feature. The feature allows the execution of the blocks in the past and the same execution results to be received. Together with forkless upgrades, execution of any block from the past is possible if historical data exist for the target block height. The default size of historical/rewind window is 7 days. Also added support for rollback command when state rewind feature is enabled. The command allows the rollback of the state of the blockchain several blocks behind until the end of the historical window. ## Implementation details The change adds a new `HistoricalRocksDB` type that is the wrapper around regular `RocksDB`. This type has inside another RocksDB instance that is used to duplicate all tables plus has one more column to store the reverse modifications at each block height. The reverse modification is the opposite to the operation that was done during transition from block height X to X + 1. The screenshot below should describe the idea: <img width="723" alt="image" src="https://github.com/FuelLabs/fuel-core/assets/18346821/c4becce0-1669-4938-8dd7-87d274efa224"> The key of duplicated tables is extended with block height, and the value is the reverse operation to reach the state of entry at the previous height. Having the history of reverse operations, we can iterate back from the latest version of the entry to the previous one. Using the main property of the RocksDB(sorting keys by default), lookup operations are fast and we don't need to iterate all modifications. It is just enough to find the nearest reverse operation to the target height. ## Checklist - [x] New behavior is reflected in tests ### Before requesting review - [x] I have reviewed the code myself - [x] I have created follow-up issues caused by this PR and linked them here - #1997 - #1995 - #1993
- Loading branch information
Showing
49 changed files
with
2,010 additions
and
108 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file modified
BIN
+11 Bytes
(100%)
bin/fuel-core/chainspec/local-testnet/state_transition_bytecode.wasm
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
use crate::cli::default_db_path; | ||
use anyhow::Context; | ||
use clap::Parser; | ||
use fuel_core::{ | ||
combined_database::CombinedDatabase, | ||
service::genesis::NotifyCancel, | ||
state::historical_rocksdb::StateRewindPolicy, | ||
}; | ||
use std::path::PathBuf; | ||
|
||
/// Rollbacks the state of the blockchain to a specific block height. | ||
#[derive(Debug, Clone, Parser)] | ||
pub struct Command { | ||
/// The path to the database. | ||
#[clap( | ||
name = "DB_PATH", | ||
long = "db-path", | ||
value_parser, | ||
default_value = default_db_path().into_os_string() | ||
)] | ||
pub database_path: PathBuf, | ||
|
||
/// The path to the database. | ||
#[clap(long = "target-block-height")] | ||
pub target_block_height: u32, | ||
} | ||
|
||
pub async fn exec(command: Command) -> anyhow::Result<()> { | ||
use crate::cli::ShutdownListener; | ||
|
||
let path = command.database_path.as_path(); | ||
let db = CombinedDatabase::open( | ||
path, | ||
64 * 1024 * 1024, | ||
StateRewindPolicy::RewindFullRange, | ||
) | ||
.map_err(Into::<anyhow::Error>::into) | ||
.context(format!("failed to open combined database at path {path:?}"))?; | ||
|
||
let shutdown_listener = ShutdownListener::spawn(); | ||
let target_block_height = command.target_block_height.into(); | ||
|
||
while !shutdown_listener.is_cancelled() { | ||
let on_chain_height = db | ||
.on_chain() | ||
.latest_height()? | ||
.ok_or(anyhow::anyhow!("on-chain database doesn't have height"))?; | ||
|
||
let off_chain_height = db | ||
.off_chain() | ||
.latest_height()? | ||
.ok_or(anyhow::anyhow!("on-chain database doesn't have height"))?; | ||
|
||
if on_chain_height == target_block_height | ||
&& off_chain_height == target_block_height | ||
{ | ||
break; | ||
} | ||
|
||
if off_chain_height == target_block_height | ||
&& on_chain_height < target_block_height | ||
{ | ||
return Err(anyhow::anyhow!( | ||
"on-chain database height is less than target height" | ||
)); | ||
} | ||
|
||
if on_chain_height == target_block_height | ||
&& off_chain_height < target_block_height | ||
{ | ||
return Err(anyhow::anyhow!( | ||
"off-chain database height is less than target height" | ||
)); | ||
} | ||
|
||
if on_chain_height > target_block_height { | ||
db.on_chain().rollback_last_block()?; | ||
tracing::info!( | ||
"Rolled back on-chain database to height {:?}", | ||
on_chain_height.pred() | ||
); | ||
} | ||
|
||
if off_chain_height > target_block_height { | ||
db.off_chain().rollback_last_block()?; | ||
tracing::info!( | ||
"Rolled back off-chain database to height {:?}", | ||
on_chain_height.pred() | ||
); | ||
} | ||
} | ||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.