Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementation of the state rewind feature for the RocksDB #1996

Merged
merged 45 commits into from
Jul 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
fea24a3
Extract `HistoricalView` trait from the `AtomicView`
xgreenx Jun 26, 2024
4c20273
Updated changelog
xgreenx Jun 26, 2024
5119dbe
Removed not related code to the chagne
xgreenx Jun 26, 2024
4b3aecb
Fixed tests
xgreenx Jun 26, 2024
b06c622
Fixed the column
xgreenx Jun 26, 2024
61f1059
Move all functionality from the `Database` to the views
xgreenx Jun 26, 2024
c5ec5bf
Moved `latest_height` to `HistoricalView` trait
xgreenx Jun 26, 2024
db90520
Merge branch 'refs/heads/feature/historical-view-trait' into feature/…
xgreenx Jun 26, 2024
2b9eadc
Improved readability
xgreenx Jun 26, 2024
c61477f
Merge branch 'refs/heads/master' into feature/iteratable-view
xgreenx Jun 26, 2024
c3d6f66
Merge branch 'master' into feature/iteratable-view
xgreenx Jun 26, 2024
29d543e
Updated CHANGELOG.md
xgreenx Jun 26, 2024
682d256
Simplify imports
xgreenx Jun 26, 2024
11d1d28
Make CI happy
xgreenx Jun 26, 2024
187ec91
Added the actual implementation for the `AtomicView::latest_view`
xgreenx Jun 26, 2024
ec86d61
Updated CHANGELOG.md
xgreenx Jun 26, 2024
22f6a63
Merge branch 'master' into feature/iteratable-view
xgreenx Jun 27, 2024
46d0eca
Merge branch 'master' into feature/iteratable-view
xgreenx Jun 27, 2024
19f7433
Merge branch 'feature/iteratable-view' into feature/atomic-latest-view
xgreenx Jun 27, 2024
f642a4a
Simplify signature of the constracutor
xgreenx Jun 27, 2024
c9f4ca2
Merge branch 'refs/heads/feature/iteratable-view' into feature/atomic…
xgreenx Jun 27, 2024
7ad0907
The actual implementation of the historical view for RocksDB
xgreenx Jun 27, 2024
58c698e
Renamed `IterableView` into `IterableKeyValueView`
xgreenx Jun 27, 2024
e06ca6e
Merge branch 'refs/heads/feature/atomic-latest-view' into feature/his…
xgreenx Jun 27, 2024
e6a02d6
Merged base branch
xgreenx Jun 27, 2024
d5b6535
Renamed `IterableView` to `IterableKeyValueView`
xgreenx Jun 27, 2024
0bf402b
Merge branch 'refs/heads/feature/iteratable-view' into feature/atomic…
xgreenx Jun 27, 2024
ffc6481
Merge branch 'refs/heads/feature/atomic-latest-view' into feature/his…
xgreenx Jun 27, 2024
97983c5
Merge branch 'refs/heads/master' into feature/atomic-latest-view
xgreenx Jun 28, 2024
ccc0a2c
Added more tets and rollback fucntionality
xgreenx Jun 28, 2024
8513901
Use explicit getter instead of deref
xgreenx Jun 28, 2024
f4e8bbf
Merge branch 'feature/atomic-latest-view' into feature/historical-vie…
xgreenx Jun 28, 2024
c733a48
Merge branch 'refs/heads/master' into feature/historical-view-impleme…
xgreenx Jun 28, 2024
eeb75ac
Added rollback command and e2e tests to verify the whole behaviour.
xgreenx Jun 29, 2024
c43006a
Updated CHANGELOG.md
xgreenx Jun 29, 2024
f06508c
Merge branch 'master' into feature/historical-view-implementation
xgreenx Jul 1, 2024
5bd9e44
Fixed the comment
xgreenx Jul 2, 2024
0020b2f
Merge branch 'master' into feature/historical-view-implementation
xgreenx Jul 2, 2024
af88cc9
Merge branch 'master' into feature/historical-view-implementation
xgreenx Jul 2, 2024
4d56031
Merge branch 'master' into feature/historical-view-implementation
xgreenx Jul 2, 2024
c466c13
Merge branch 'master' into feature/historical-view-implementation
xgreenx Jul 3, 2024
ee634e7
Use better naming for variable=)
xgreenx Jul 3, 2024
2633cec
Merge branch 'master' into feature/historical-view-implementation
xgreenx Jul 4, 2024
562171c
Merge `history` and original databases into one
xgreenx Jul 4, 2024
6885cb9
Return error instead of panic
xgreenx Jul 4, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased]

### Added
- [#1996](https://github.com/FuelLabs/fuel-core/pull/1996): 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. The default historical window it 7 days.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this might also have breaking changes, at least some pub fields were made pub(crate).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If they were added since the last release, then it should be fine.

- [#1996](https://github.com/FuelLabs/fuel-core/pull/1996): 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.
- [#1994](https://github.com/FuelLabs/fuel-core/pull/1994): Added the actual implementation for the `AtomicView::latest_view`.
- [#1972](https://github.com/FuelLabs/fuel-core/pull/1972): Implement `AlgorithmUpdater` for `GasPriceService`
- [#1948](https://github.com/FuelLabs/fuel-core/pull/1948): Add new `AlgorithmV1` and `AlgorithmUpdaterV1` for the gas price. Include tools for analysis
Expand Down
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 10 additions & 5 deletions benches/benches/vm_set/blockchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ use fuel_core::{
GenesisDatabase,
},
service::Config,
state::rocks_db::{
RocksDb,
ShallowTempDir,
state::{
historical_rocksdb::HistoricalRocksDB,
rocks_db::ShallowTempDir,
},
};
use fuel_core_benches::*;
Expand Down Expand Up @@ -74,8 +74,13 @@ impl BenchDb {
fn new(contract_id: &ContractId) -> anyhow::Result<Self> {
let tmp_dir = ShallowTempDir::new();

let db =
Arc::new(RocksDb::<OnChain>::default_open(tmp_dir.path(), None).unwrap());
let db = HistoricalRocksDB::<OnChain>::default_open(
tmp_dir.path(),
None,
Default::default(),
)
.unwrap();
let db = Arc::new(db);
let mut storage_key = primitive_types::U256::zero();
let mut key_bytes = Bytes32::zeroed();

Expand Down
2 changes: 1 addition & 1 deletion bin/fuel-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,6 @@ p2p = ["fuel-core/p2p", "const_format"]
relayer = ["fuel-core/relayer", "dep:url"]
parquet = ["fuel-core-chain-config/parquet", "fuel-core-types/serde"]
rocksdb = ["fuel-core/rocksdb"]
rocksdb-production = ["fuel-core/rocksdb-production"]
rocksdb-production = ["fuel-core/rocksdb-production", "rocksdb"]
# features to enable in production, but increase build times
production = ["env", "relayer", "rocksdb-production", "p2p", "parquet"]
Binary file not shown.
14 changes: 10 additions & 4 deletions bin/fuel-core/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,12 @@ pub fn default_db_path() -> PathBuf {
}

pub mod fee_contract;
#[cfg(feature = "rocksdb")]
pub mod rollback;
pub mod run;
#[cfg(any(feature = "rocksdb", feature = "rocksdb-production"))]
#[cfg(feature = "rocksdb")]
pub mod snapshot;

// Default database cache is 1 GB
pub const DEFAULT_DATABASE_CACHE_SIZE: usize = 1024 * 1024 * 1024;

Expand All @@ -48,8 +51,10 @@ pub struct Opt {
#[derive(Debug, Parser)]
pub enum Fuel {
Run(run::Command),
#[cfg(any(feature = "rocksdb", feature = "rocksdb-production"))]
#[cfg(feature = "rocksdb")]
Snapshot(snapshot::Command),
#[cfg(feature = "rocksdb")]
Rollback(rollback::Command),
GenerateFeeContract(fee_contract::Command),
}

Expand Down Expand Up @@ -128,9 +133,10 @@ pub async fn run_cli() -> anyhow::Result<()> {
match opt {
Ok(opt) => match opt.command {
Fuel::Run(command) => run::exec(command).await,
#[cfg(any(feature = "rocksdb", feature = "rocksdb-production"))]
#[cfg(feature = "rocksdb")]
Fuel::Snapshot(command) => snapshot::exec(command).await,
Fuel::GenerateFeeContract(command) => fee_contract::exec(command).await,
Fuel::Rollback(command) => rollback::exec(command).await,
},
Err(e) => {
// Prints the error and exits.
Expand Down Expand Up @@ -213,7 +219,7 @@ impl NotifyCancel for ShutdownListener {
}
}

#[cfg(any(feature = "rocksdb", feature = "rocksdb-production"))]
#[cfg(feature = "rocksdb")]
#[cfg(test)]
mod tests {
use anyhow::anyhow;
Expand Down
93 changes: 93 additions & 0 deletions bin/fuel-core/src/cli/rollback.rs
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() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: "While not is_cancelled" is kinda a confusing double negative. Maybe add a helper that is just like

fn is_still_live(&self) -> bool {
    !self.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(())
}
48 changes: 47 additions & 1 deletion bin/fuel-core/src/cli/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ use pyroscope_pprofrs::{
use std::{
env,
net,
num::NonZeroU64,
path::PathBuf,
str::FromStr,
};
Expand All @@ -67,6 +68,9 @@ use tracing::{
warn,
};

#[cfg(feature = "rocksdb")]
use fuel_core::state::historical_rocksdb::StateRewindPolicy;

use super::DEFAULT_DATABASE_CACHE_SIZE;

pub const CONSENSUS_KEY_ENV: &str = "CONSENSUS_KEY_SECRET";
Expand Down Expand Up @@ -114,6 +118,20 @@ pub struct Command {
)]
pub database_type: DbType,

#[cfg(feature = "rocksdb")]
/// Defines the state rewind policy for the database when RocksDB is enabled.
///
/// The duration defines how many blocks back the rewind feature works.
/// Assuming each block requires one second to produce.
///
/// The default value is 7 days = 604800 blocks.
///
/// The `BlockHeight` is `u32`, meaning the maximum possible number of blocks
/// is less than 137 years. `2^32 / 24 / 60 / 60/ 365` = `136.1925195332` years.
/// If the value is 136 years or more, the rewind feature is enabled for all blocks.
#[clap(long = "state-rewind-duration", default_value = "7d", env)]
pub state_rewind_duration: humantime::Duration,
Voxelot marked this conversation as resolved.
Show resolved Hide resolved

/// Snapshot from which to do (re)genesis. Defaults to local testnet configuration.
#[arg(name = "SNAPSHOT", long = "snapshot", env)]
pub snapshot: Option<PathBuf>,
Expand Down Expand Up @@ -214,6 +232,8 @@ impl Command {
max_database_cache_size,
database_path,
database_type,
#[cfg(feature = "rocksdb")]
state_rewind_duration,
db_prune,
snapshot,
vm_backtrace,
Expand Down Expand Up @@ -297,10 +317,36 @@ impl Command {
max_wait_time: max_wait_time.into(),
};

#[cfg(feature = "rocksdb")]
let state_rewind_policy = {
if database_type != DbType::RocksDb {
tracing::warn!("State rewind policy is only supported with RocksDB");
}

let blocks = state_rewind_duration.as_secs();

if blocks == 0 {
StateRewindPolicy::NoRewind
} else {
let maximum_blocks: humantime::Duration = "136y".parse()?;

if blocks >= maximum_blocks.as_secs() {
StateRewindPolicy::RewindFullRange
} else {
StateRewindPolicy::RewindRange {
size: NonZeroU64::new(blocks)
.expect("The value is not zero above"),
}
}
}
};

let combined_db_config = CombinedDatabaseConfig {
database_path,
database_type,
max_database_cache_size,
#[cfg(feature = "rocksdb")]
state_rewind_policy,
};

let block_importer =
Expand Down Expand Up @@ -376,7 +422,7 @@ impl Command {
}

pub fn get_service(command: Command) -> anyhow::Result<FuelService> {
#[cfg(any(feature = "rocksdb", feature = "rocksdb-production"))]
#[cfg(feature = "rocksdb")]
if command.db_prune && command.database_path.exists() {
fuel_core::combined_database::CombinedDatabase::prune(&command.database_path)?;
}
Expand Down
14 changes: 9 additions & 5 deletions bin/fuel-core/src/cli/snapshot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ use clap::{
};
use fuel_core::{
combined_database::CombinedDatabase,
state::historical_rocksdb::StateRewindPolicy,
types::fuel_types::ContractId,
};
use fuel_core_chain_config::ChainConfig;

use std::path::{
Path,
PathBuf,
Expand Down Expand Up @@ -112,7 +112,7 @@ pub enum SubCommands {
},
}

#[cfg(any(feature = "rocksdb", feature = "rocksdb-production"))]
#[cfg(feature = "rocksdb")]
pub async fn exec(command: Command) -> anyhow::Result<()> {
use fuel_core::service::genesis::Exporter;
use fuel_core_chain_config::{
Expand Down Expand Up @@ -181,9 +181,13 @@ fn load_chain_config_or_use_testnet(path: Option<&Path>) -> anyhow::Result<Chain
}

fn open_db(path: &Path, capacity: Option<usize>) -> anyhow::Result<CombinedDatabase> {
CombinedDatabase::open(path, capacity.unwrap_or(1024 * 1024 * 1024))
.map_err(Into::<anyhow::Error>::into)
.context(format!("failed to open combined database at path {path:?}",))
CombinedDatabase::open(
path,
capacity.unwrap_or(1024 * 1024 * 1024),
StateRewindPolicy::NoRewind,
)
.map_err(Into::<anyhow::Error>::into)
.context(format!("failed to open combined database at path {path:?}",))
}

#[cfg(test)]
Expand Down
4 changes: 2 additions & 2 deletions ci_checks.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
# - `cargo install cargo-insta`
# - `npm install prettier prettier-plugin-toml`

npx prettier --check "**/Cargo.toml" &&
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we rename ci_checks.sh since this isn't "checking" anymore?

cargo +nightly fmt --all -- --check &&
npx prettier --write "**/Cargo.toml" &&
cargo +nightly fmt --all &&
cargo sort -w --check &&
source .github/workflows/scripts/verify_openssl.sh &&
cargo clippy -p fuel-core-wasm-executor --target wasm32-unknown-unknown --no-default-features &&
Expand Down
2 changes: 1 addition & 1 deletion crates/chain-config/src/config/state/reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ impl SnapshotReader {
use anyhow::Context;
use fuel_core_storage::kv_store::StorageColumn;
let name = T::column().name();
let Some(path) = tables.get(name) else {
let Some(path) = tables.get(name.as_str()) else {
return Ok(Groups {
iter: GroupIter::InMemory {
groups: vec![].into_iter(),
Expand Down
13 changes: 13 additions & 0 deletions crates/database/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use fuel_core_types::services::executor::Error as ExecutorError;
/// The error occurred during work with any of databases.
#[derive(Debug, derive_more::Display, derive_more::From)]
#[non_exhaustive]
#[allow(missing_docs)]
pub enum Error {
/// Error occurred during serialization or deserialization of the entity.
#[display(fmt = "error performing serialization or deserialization")]
Expand Down Expand Up @@ -58,6 +59,18 @@ pub enum Error {
/// The old height known by the database.
prev_height: u64,
},
#[display(fmt = "The historical database doesn't have any history yet")]
NoHistoryIsAvailable,
#[display(
fmt = "The historical database doesn't have history for the requested height {requested_height:#x}, \
the oldest available height is {oldest_available_height:#x}"
)]
NoHistoryForRequestedHeight {
requested_height: u64,
oldest_available_height: u64,
},
#[display(fmt = "Reached the end of the history")]
ReachedEndOfHistory,

/// Not related to database error.
#[from]
Expand Down
2 changes: 2 additions & 0 deletions crates/fuel-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ hyper = { workspace = true }
indicatif = { workspace = true, default-features = true }
itertools = { workspace = true }
num_cpus = { version = "1.16.0", optional = true }
postcard = { workspace = true }
rand = { workspace = true }
rocksdb = { version = "0.21", default-features = false, features = [
"lz4",
Expand Down Expand Up @@ -91,6 +92,7 @@ test-helpers = [
"fuel-core-chain-config/test-helpers",
"fuel-core-txpool/test-helpers",
"fuel-core-services/test-helpers",
"fuel-core-importer/test-helpers",
]
# features to enable in production, but increase build times
rocksdb-production = ["rocksdb", "rocksdb/jemalloc"]
Expand Down
Loading
Loading