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

feat(drive): platform version patching and state migrations #1941

Merged
merged 38 commits into from
Jul 16, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
be2b0cc
feat(drive): platform version patching and state migrations
shumkov Jul 10, 2024
a4d0988
chore: remove duplicate dependency
shumkov Jul 10, 2024
b45a1db
refactor: unnecessary reference
shumkov Jul 10, 2024
bbad647
docs: update field documentation
shumkov Jul 10, 2024
82dbe71
refactor: fix review comments
shumkov Jul 12, 2024
4e5a790
refactor: move patches to version crate
shumkov Jul 13, 2024
3857883
refactor: rename patch function and move version setting to if condition
shumkov Jul 13, 2024
386cb65
docs: fix comment
shumkov Jul 13, 2024
90e984b
refactor: remove dbg
shumkov Jul 13, 2024
3fb9e7a
docs: reword patched_platform_version doc
shumkov Jul 13, 2024
17c7c72
Merge branch 'refs/heads/v1.0-dev' into feat/drive/version-patch-and-…
shumkov Jul 13, 2024
a8f2746
test: use default_minimal_verifications
shumkov Jul 13, 2024
fc90dac
chore: add logging to migration function
shumkov Jul 13, 2024
3c68376
modifed yaml file for AWS secrets
vivekgsharma Jul 13, 2024
b23e693
updated workflos to use AWS secrets from github secrets
vivekgsharma Jul 13, 2024
dacdf32
aws login credentials github secrets
vivekgsharma Jul 13, 2024
9e6312b
added aws account id
vivekgsharma Jul 14, 2024
a2e62b7
configre sccache env
vivekgsharma Jul 14, 2024
b644ac7
small fix
vivekgsharma Jul 14, 2024
975e57a
fix2
vivekgsharma Jul 14, 2024
eb972df
fix
vivekgsharma Jul 14, 2024
25eefa8
fix
vivekgsharma Jul 14, 2024
94bfc42
fix
vivekgsharma Jul 14, 2024
6cb211b
fix_fin
vivekgsharma Jul 14, 2024
f7ef6b3
fic
vivekgsharma Jul 14, 2024
febed5e
fix
vivekgsharma Jul 14, 2024
ffb2607
printing env varibles to see if aws creds are there during the build …
vivekgsharma Jul 15, 2024
a9621f3
ci: pass AWS creds to docker build
shumkov Jul 15, 2024
127ceae
build: remove aws debug call
shumkov Jul 15, 2024
4d7eb3e
Changed to github secrets for Rust packages workflows
vivekgsharma Jul 15, 2024
f98b441
proto fix
vivekgsharma Jul 15, 2024
c0552dc
chore: remove unused once_cell
shumkov Jul 15, 2024
26cd101
chore: kick off CI
shumkov Jul 16, 2024
cb923ac
Merge branch 'refs/heads/v1.0-dev' into feat/drive/version-patch-and-…
shumkov Jul 16, 2024
b67c6d0
revert: ci changes
shumkov Jul 16, 2024
29e5314
chore: fix code after merging v1
shumkov Jul 16, 2024
5139ca7
Merge branch 'v1.0-dev' into feat/drive/version-patch-and-migrations
shumkov Jul 16, 2024
31734aa
Merge branch 'v1.0-dev' into feat/drive/version-patch-and-migrations
QuantumExplorer Jul 16, 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
1 change: 1 addition & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions packages/rs-drive-abci/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ tokio-util = { version = "0.7.8" }
derive_more = "0.99.17"
async-trait = "0.1.77"
console-subscriber = { version = "0.2.0", optional = true }
once_cell = "1.19.0"

[dev-dependencies]
base64 = "0.22.1"
Expand Down
1 change: 1 addition & 0 deletions packages/rs-drive-abci/src/abci/handler/check_tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::error::execution::ExecutionError;
use crate::error::Error;
use crate::metrics::{LABEL_ABCI_RESPONSE_CODE, LABEL_CHECK_TX_MODE, LABEL_STATE_TRANSITION_NAME};
use crate::platform_types::platform::{Platform, PlatformRef};
use crate::platform_types::platform_state::v0::PlatformStateV0Methods;
use crate::rpc::core::CoreRPCLike;
use dpp::consensus::codes::ErrorWithCode;
use dpp::fee::SignedCredits;
Expand Down
1 change: 1 addition & 0 deletions packages/rs-drive-abci/src/abci/handler/finalize_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use crate::error::execution::ExecutionError;
use crate::error::Error;
use crate::execution::types::block_execution_context::v0::BlockExecutionContextV0Getters;
use crate::platform_types::cleaned_abci_messages::finalized_block_cleaned_request::v0::FinalizeBlockCleanedRequest;
use crate::platform_types::platform_state::v0::PlatformStateV0Methods;
use crate::rpc::core::CoreRPCLike;
use std::sync::atomic::Ordering;
use tenderdash_abci::proto::abci as proto;
Expand Down
8 changes: 2 additions & 6 deletions packages/rs-drive-abci/src/abci/handler/prepare_proposal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ use crate::platform_types::platform_state::v0::PlatformStateV0Methods;
use crate::platform_types::state_transitions_processing_result::StateTransitionExecutionResult;
use crate::rpc::core::CoreRPCLike;
use dpp::dashcore::hashes::Hash;
use dpp::version::PlatformVersion;
use dpp::version::TryIntoPlatformVersioned;
use tenderdash_abci::proto::abci as proto;
use tenderdash_abci::proto::abci::tx_record::TxAction;
Expand Down Expand Up @@ -117,13 +116,10 @@ where
app_hash,
state_transitions_result,
validator_set_update,
protocol_version,
platform_version,
mut block_execution_context,
} = run_result.into_data().map_err(Error::Protocol)?;

let platform_version = PlatformVersion::get(protocol_version)
.expect("must be set in run block proposal from existing protocol version");

// We need to let Tenderdash know about the transactions we should remove from execution
let valid_tx_count = state_transitions_result.valid_count();
let failed_tx_count = state_transitions_result.failed_count();
Expand Down Expand Up @@ -192,7 +188,7 @@ where
validator_set_update,
// TODO: implement consensus param updates
consensus_param_updates: None,
app_version: protocol_version as u64,
app_version: platform_version.protocol_version as u64,
};

block_execution_context.set_proposer_results(Some(response.clone()));
Expand Down
5 changes: 1 addition & 4 deletions packages/rs-drive-abci/src/abci/handler/process_proposal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
use crate::platform_types::block_execution_outcome;
use crate::platform_types::state_transitions_processing_result::StateTransitionExecutionResult;
use crate::rpc::core::CoreRPCLike;
use dpp::version::PlatformVersion;

Check warning on line 13 in packages/rs-drive-abci/src/abci/handler/process_proposal.rs

View workflow job for this annotation

GitHub Actions / Rust packages (drive-abci) / Linting

unused import: `dpp::version::PlatformVersion`

warning: unused import: `dpp::version::PlatformVersion` --> packages/rs-drive-abci/src/abci/handler/process_proposal.rs:13:5 | 13 | use dpp::version::PlatformVersion; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `#[warn(unused_imports)]` on by default
use dpp::version::TryIntoPlatformVersioned;
use tenderdash_abci::proto::abci as proto;
use tenderdash_abci::proto::abci::tx_record::TxAction;
Expand Down Expand Up @@ -202,13 +202,10 @@
app_hash,
state_transitions_result: state_transition_results,
validator_set_update,
protocol_version,
platform_version,
block_execution_context,
} = run_result.into_data().map_err(Error::Protocol)?;

let platform_version = PlatformVersion::get(protocol_version)
.expect("must be set in run block proposer from existing platform version");

app.block_execution_context()
.write()
.unwrap()
Expand Down
1 change: 1 addition & 0 deletions packages/rs-drive-abci/src/execution/check_tx/v0/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,10 @@
check_tx_result.unique_identifiers = state_transition.unique_identifiers();

let validation_result = state_transition_to_execution_event_for_check_tx(
&platform_ref,

Check warning on line 156 in packages/rs-drive-abci/src/execution/check_tx/v0/mod.rs

View workflow job for this annotation

GitHub Actions / Rust packages (drive-abci) / Linting

this expression creates a reference which is immediately dereferenced by the compiler

warning: this expression creates a reference which is immediately dereferenced by the compiler --> packages/rs-drive-abci/src/execution/check_tx/v0/mod.rs:156:13 | 156 | &platform_ref, | ^^^^^^^^^^^^^ help: change this to: `platform_ref` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow = note: `#[warn(clippy::needless_borrow)]` on by default
state_transition,
check_tx_level,
platform_version,
)?;

// If there are any validation errors happen we return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::abci::AbciError;
use crate::error::execution::ExecutionError;

use crate::error::Error;
use crate::execution::platform_events::block_start::patch_platform_version::patch_platform_version;
use crate::execution::types::block_execution_context::v0::{
BlockExecutionContextV0Getters, BlockExecutionContextV0MutableGetters,
};
Expand Down Expand Up @@ -65,7 +66,7 @@ where
epoch_info: EpochInfo,
transaction: &Transaction,
last_committed_platform_state: &PlatformState,
platform_version: &PlatformVersion,
platform_version: &'static PlatformVersion,
) -> Result<ValidationResult<block_execution_outcome::v0::BlockExecutionOutcome, Error>, Error>
{
tracing::trace!(
Expand Down Expand Up @@ -139,6 +140,12 @@ where
.expect("current epoch index should be in range"),
);

// Patch platform version to fix chain halt bugs
patch_platform_version(&block_info, platform_version, &mut block_platform_state)?;

// Perform state migration to fix bugs or support new features
self.migrate_state(&block_info, &mut block_platform_state)?;
Copy link
Member

Choose a reason for hiding this comment

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

This should be one call only. We should call it, check_for_chain_halt_hot_fix.

Copy link
Member

Choose a reason for hiding this comment

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

And it should do everything inside.

Copy link
Member Author

Choose a reason for hiding this comment

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

Migrations aren't only for chain halts. We might want to migrate data for features as well.


if epoch_info.is_epoch_change_but_not_genesis() {
tracing::info!(
epoch_index = epoch_info.current_epoch_index(),
Expand Down Expand Up @@ -384,7 +391,7 @@ where
app_hash: root_hash,
state_transitions_result,
validator_set_update,
protocol_version: platform_version.protocol_version,
platform_version,
block_execution_context,
},
))
Expand Down
2 changes: 2 additions & 0 deletions packages/rs-drive-abci/src/execution/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ pub mod storage;
pub mod types;
/// Validation module
pub mod validation;

pub use platform_events::block_start::patch_platform_version;
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use crate::platform_types::platform::Platform;
use crate::platform_types::platform_state::PlatformState;
use dpp::block::block_info::BlockInfo;

impl<C> Platform<C> {
pub(super) fn migration_42_example(
&self,
_block_info: &BlockInfo,
_block_platform_state: &mut PlatformState,
) {
// Use Drive or GroveDB directly to modify state
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
mod migration_42_example;
Copy link
Member

Choose a reason for hiding this comment

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

This should not be inside the build...

Copy link
Member Author

Choose a reason for hiding this comment

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

Ok


use crate::error::Error;

use dpp::block::block_info::BlockInfo;

use crate::platform_types::platform::Platform;

use crate::platform_types::platform_state::PlatformState;

impl<C> Platform<C> {
/// Perform state migration based on block information
pub fn migrate_state(
&self,
block_info: &BlockInfo,
block_platform_state: &mut PlatformState,
) -> Result<(), Error> {
// Implement functions in a separate modules with meaningful names and block height
match block_info.height {
42 => self.migration_42_example(block_info, block_platform_state),
52 => self.migration_42_example(block_info, block_platform_state),
_ => {}
Copy link
Member

Choose a reason for hiding this comment

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

Why did we leave in examples?

Copy link
Member Author

Choose a reason for hiding this comment

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

For people so they see how to follow. I will hide it for builds

Copy link
Member Author

Choose a reason for hiding this comment

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

Removed all test data from the code. Now it's defined in tests

}

Ok(())
}
}
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
/// Clearing the drive cache should happen when a new block is going to be run
pub(in crate::execution) mod clear_drive_block_cache;
/// State migration
pub(in crate::execution) mod migrate_state;
/// Patch platform version to apply fixes for urgent bugs, which aren't a part of the normal upgrade process
pub mod patch_platform_version;
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
mod patch_0_15_example;

use crate::error::Error;
use crate::execution::platform_events::block_start::patch_platform_version::patch_0_15_example::patch_0_15_example;
use dpp::block::block_info::BlockInfo;
use dpp::prelude::BlockHeight;
use dpp::util::deserializer::ProtocolVersion;
use dpp::version::PlatformVersion;
use indexmap::IndexMap;
use once_cell::sync::Lazy;
use std::collections::HashMap;
use std::ops::Range;
use std::sync::RwLock;

use crate::platform_types::platform_state::v0::PlatformStateV0Methods;
use crate::platform_types::platform_state::PlatformState;

static PREVIOUS_PATCH_PATTERN: RwLock<Option<(ProtocolVersion, Range<BlockHeight>)>> =
RwLock::new(None);

type PatchFn = fn(PlatformVersion) -> PlatformVersion;

type HeightToPatchRanges = IndexMap<Range<BlockHeight>, PatchFn>;

static PATCHES: Lazy<HashMap<ProtocolVersion, HeightToPatchRanges>> = Lazy::new(|| {
HashMap::from_iter(vec![
(
1,
IndexMap::from_iter(vec![
(15..30, patch_0_15_example as PatchFn),
(30..45, patch_0_15_example as PatchFn),
// (45..BlockHeight::MAX, patch_0_15_example as PatchFn),
]),
),
// (
// 2,
// IndexMap::from_iter(vec![(15..24, patch_0_15_example as PatchFn)]),
// ),
])
});

/// Patch platform version to change function versions to fix chain halt bugs
pub fn patch_platform_version(
block_info: &BlockInfo,
current_platform_version: &PlatformVersion,
block_platform_state: &mut PlatformState,
) -> Result<(), Error> {
// Check if a patch that matches protocol version and block height is already applied
Copy link
Member

Choose a reason for hiding this comment

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

We should be able to apply more than one patch.

Copy link
Member Author

Choose a reason for hiding this comment

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

More than one patch for the same version? You can.

Copy link
Member

Choose a reason for hiding this comment

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

Per block height

Copy link
Member Author

Choose a reason for hiding this comment

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

Done

if block_platform_state.patched_platform_version().is_some() {
let previous_patch_pattern = PREVIOUS_PATCH_PATTERN.read().unwrap();
if let Some((protocol_version, height_range)) = previous_patch_pattern.as_ref() {
if protocol_version == &current_platform_version.protocol_version
&& height_range.contains(&block_info.height)
{
tracing::trace!(
protocol_version = current_platform_version.protocol_version,
height = block_info.height,
"Continue using patched platform version {} and height range {:?}",
protocol_version,
height_range
);

return Ok(());
} else {
let height_range = height_range.clone();
drop(previous_patch_pattern);

let mut previous_path_pattern = PREVIOUS_PATCH_PATTERN.write().unwrap();
*previous_path_pattern = None;

tracing::debug!(
protocol_version = current_platform_version.protocol_version,
height = block_info.height,
"Disable patch for platform version {} and height range {:?}",
current_platform_version.protocol_version,
height_range
);
}
}
}

// Find a patch that matches protocol version first
let Some(height_to_patch_ranges) = PATCHES.get(&current_platform_version.protocol_version)
else {
// Drop patch
block_platform_state.set_patched_platform_version(None);

return Ok(());
};

// Find a patch that matches block height
let Some((height_range, patch_fn)) = height_to_patch_ranges
.iter()
.find(|(height_range, _)| height_range.contains(&block_info.height))
else {
// Drop patch
block_platform_state.set_patched_platform_version(None);

return Ok(());
};

// Apply the patch
let patched_version = patch_fn(current_platform_version.clone());

// Make patch version as static ref to transparently replace original version
let boxed_version = Box::new(patched_version);
let static_version: &'static PlatformVersion = Box::leak(boxed_version);

// Set patched version to the Platform (execution) state that will be used
// instead of the current version
block_platform_state.set_patched_platform_version(Some(static_version));

// Store the patch pattern to avoid applying the same patch multiple times
PREVIOUS_PATCH_PATTERN.write().unwrap().replace((
current_platform_version.protocol_version,
height_range.clone(),
));

tracing::debug!(
protocol_version = current_platform_version.protocol_version,
height = block_info.height,
"Apply patch for platform version {} and height range {:?}",
current_platform_version.protocol_version,
height_range
);

Ok(())
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use dpp::version::PlatformVersion;

pub fn patch_0_15_example(mut platform_version: PlatformVersion) -> PlatformVersion {
platform_version.drive_abci.methods.engine.check_tx = 0;

platform_version
}
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ impl<C> Platform<C> {
#[cfg(test)]
mod tests {
use super::*;
use crate::platform_types::platform_state::v0::PlatformStateV0Methods;

mod add_epoch_pool_to_proposers_payout_operations {
use super::*;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::platform_types::platform::PlatformRef;
use crate::rpc::core::CoreRPCLike;
use dpp::prelude::ConsensusValidationResult;
use dpp::state_transition::StateTransition;
use dpp::version::PlatformVersion;

use crate::execution::check_tx::CheckTxLevel;

Expand All @@ -25,8 +26,8 @@ pub(in crate::execution) fn state_transition_to_execution_event_for_check_tx<'a,
platform: &'a PlatformRef<C>,
state_transition: StateTransition,
check_tx_level: CheckTxLevel,
platform_version: &PlatformVersion,
) -> Result<ConsensusValidationResult<Option<ExecutionEvent<'a>>>, Error> {
let platform_version = platform.state.current_platform_version()?;
match platform_version
.drive_abci
.validation_and_processing
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use dpp::block::block_info::BlockInfo;
use dpp::prelude::ConsensusValidationResult;
use dpp::state_transition::StateTransition;

use crate::platform_types::platform_state::v0::PlatformStateV0Methods;
use drive::grovedb::TransactionArg;

/// There are multiple stages in a state transition processing:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use crate::execution::validation::state_transition::processor::v0::{
};
use crate::execution::validation::state_transition::transformer::StateTransitionActionTransformerV0;
use crate::execution::validation::state_transition::ValidationMode;
use crate::platform_types::platform_state::v0::PlatformStateV0Methods;

impl ValidationMode {
/// Returns if we should validate the contract when we transform it from its serialized form
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use crate::execution::validation::state_transition::data_contract_update::state:
use crate::execution::validation::state_transition::transformer::StateTransitionActionTransformerV0;
use crate::execution::validation::state_transition::ValidationMode;
use crate::platform_types::platform::PlatformRef;
use crate::platform_types::platform_state::v0::PlatformStateV0Methods;
use crate::rpc::core::CoreRPCLike;

impl StateTransitionActionTransformerV0 for DataContractUpdateTransition {
Expand Down Expand Up @@ -78,6 +79,7 @@ mod tests {
DataContractUpdateTransition, DataContractUpdateTransitionV0,
};

use crate::platform_types::platform_state::v0::PlatformStateV0Methods;
use crate::platform_types::state_transitions_processing_result::StateTransitionExecutionResult;
use dpp::tests::fixtures::get_data_contract_fixture;
use dpp::tests::json_document::json_document_to_contract;
Expand Down Expand Up @@ -577,9 +579,8 @@ mod tests {

let card_game_path = "tests/supporting_files/contract/crypto-card-game/crypto-card-game-direct-purchase-creation-restricted-to-owner.json";

let platform_version = platform
.state
.load()
let platform_state = platform.state.load();
let platform_version = platform_state
.current_platform_version()
.expect("expected to get current platform version");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::execution::validation::state_transition::data_contract_update::state:
use crate::execution::validation::state_transition::processor::v0::StateTransitionStateValidationV0;
use crate::execution::validation::state_transition::ValidationMode;
use crate::platform_types::platform::PlatformRef;
use crate::platform_types::platform_state::v0::PlatformStateV0Methods;
use crate::rpc::core::CoreRPCLike;
use dpp::block::block_info::BlockInfo;
use dpp::state_transition::data_contract_update_transition::DataContractUpdateTransition;
Expand Down
Loading
Loading