diff --git a/.github/actions/librocksdb/action.yaml b/.github/actions/librocksdb/action.yaml new file mode 100644 index 0000000000..895185f43d --- /dev/null +++ b/.github/actions/librocksdb/action.yaml @@ -0,0 +1,55 @@ +--- +# This action builds and caches librocksdb. If we find that this solution consumes too much time, we can consider +# prebuilding librocksdb outside of the pipeline (eg. in the grovedb release process), publish as an artifact, and +# download it in the pipeline. +name: "librocksdb" +description: "Build and install librocksdb" +inputs: + version: + description: RocksDB version, eg. "8.10.2" + required: false + default: "8.10.2" + bucket: + description: S3 bucket to use for caching + required: false + default: multi-runner-cache-x1xibo9c + force: + description: Force rebuild + required: false + default: "false" + +runs: + using: composite + steps: + # Cache librocksdb using s3 bucket + - name: Restore cached librocksdb from S3 + id: librocksdb-cache + uses: strophy/actions-cache@opendal-update + with: + bucket: ${{ inputs.bucket }} + path: /opt/rocksdb + key: librocksdb/${{ inputs.version }}/${{ runner.os }}/${{ runner.arch }} + + - if: ${{ steps.librocksdb-cache.outputs.cache-hit != 'true' || inputs.force == 'true' }} + shell: bash + name: Build librocksdb + run: | + set -ex + WORKDIR=/tmp/rocksdb-build + mkdir -p ${WORKDIR}/rocksdb + mkdir -p /opt/rocksdb/usr/local/lib/ + pushd ${WORKDIR}/rocksdb + + # building rocksdb + git clone https://github.com/facebook/rocksdb.git -b v${{ inputs.version }} --depth 1 . + make -j$(nproc) static_lib + make DESTDIR=/opt/rocksdb install-static + set +x + + echo Done. + echo Configuration: + echo + echo "ROCKSDB_STATIC='/opt/rocksdb/usr/local/lib/librocksdb.a'" + echo "ROCKSDB_LIB_DIR='/opt/rocksdb/usr/local/lib'" + + popd diff --git a/.github/workflows/cached.yml b/.github/workflows/cached.yml new file mode 100644 index 0000000000..12eed8dd3e --- /dev/null +++ b/.github/workflows/cached.yml @@ -0,0 +1,23 @@ +--- +name: Rebuild cached dependencies + +on: + workflow_dispatch: +jobs: + build-rust-deps: + name: Prebuild and cache some Rust dependencies + runs-on: ubuntu-24.04 + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_REGION: ${{ secrets.AWS_REGION }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 1 + - name: Precompile librocksdb + uses: ./.github/actions/librocksdb + with: + force: true diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 12f512b720..d8e24ef706 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -7,7 +7,7 @@ on: jobs: build: name: Deploy docs - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - name: Checkout main uses: actions/checkout@v4 diff --git a/.github/workflows/manage-runs.yml b/.github/workflows/manage-runs.yml index 2c07af5b4f..29bc43aa53 100644 --- a/.github/workflows/manage-runs.yml +++ b/.github/workflows/manage-runs.yml @@ -7,7 +7,7 @@ on: jobs: cancel-merged-or-closed-pr-runs: name: Cancel runs for merged or closed PRs - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - uses: octokit/request-action@v2.x id: get_active_workflows diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index d475a3eef8..e75151c841 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -10,7 +10,7 @@ on: jobs: pr-title: name: PR title - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Validate conventional PR title uses: amannn/action-semantic-pull-request@v5 diff --git a/.github/workflows/prebuild-devcontainers.yml b/.github/workflows/prebuild-devcontainers.yml index 794fa3d4a5..1825985c82 100644 --- a/.github/workflows/prebuild-devcontainers.yml +++ b/.github/workflows/prebuild-devcontainers.yml @@ -7,6 +7,8 @@ on: - '.github/workflows/prebuild-devcontainers.yml' - rust-toolchain.toml - Dockerfile + branches: + - master workflow_dispatch: concurrency: diff --git a/.github/workflows/release-docker-image.yml b/.github/workflows/release-docker-image.yml index 5c56ade272..54e4cb465a 100644 --- a/.github/workflows/release-docker-image.yml +++ b/.github/workflows/release-docker-image.yml @@ -89,7 +89,7 @@ jobs: publish-manifest: name: Publish image tags needs: build-image - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - name: Download digests uses: actions/download-artifact@v3 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index dba5d592f5..4c43639d3a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -208,11 +208,11 @@ jobs: matrix: include: - package_type: tarballs - os: ubuntu-22.04 + os: ubuntu-24.04 - package_type: win - os: ubuntu-22.04 + os: ubuntu-24.04 - package_type: deb - os: ubuntu-22.04 + os: ubuntu-24.04 - package_type: macos os: macos-14 steps: diff --git a/.github/workflows/tests-codeql.yml b/.github/workflows/tests-codeql.yml index d00a66c8df..78a3f53aeb 100644 --- a/.github/workflows/tests-codeql.yml +++ b/.github/workflows/tests-codeql.yml @@ -4,7 +4,7 @@ on: jobs: codeql: name: Run Code QL - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 permissions: actions: read contents: read diff --git a/.github/workflows/tests-js-package.yml b/.github/workflows/tests-js-package.yml index ef508ec073..bdffc8cd41 100644 --- a/.github/workflows/tests-js-package.yml +++ b/.github/workflows/tests-js-package.yml @@ -17,7 +17,7 @@ on: jobs: lint: name: Linting - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 permissions: id-token: write contents: read @@ -51,7 +51,7 @@ jobs: test: name: Tests - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 permissions: id-token: write contents: read diff --git a/.github/workflows/tests-rs-package.yml b/.github/workflows/tests-rs-package.yml index e666491ebc..a05e68ee6c 100644 --- a/.github/workflows/tests-rs-package.yml +++ b/.github/workflows/tests-rs-package.yml @@ -12,7 +12,7 @@ on: lint-runner: description: Runner for linting. Must be JSON valid string. type: string - default: '"ubuntu-22.04"' + default: '"ubuntu-24.04"' check-each-feature: description: If true, try to build each individual feature for this crate type: boolean @@ -42,6 +42,9 @@ jobs: with: components: clippy + - name: Install librocksdb + uses: ./.github/actions/librocksdb + - uses: clechasseur/rs-clippy-check@v3 with: args: --package ${{ inputs.package }} --all-features --locked -- --no-deps @@ -50,10 +53,12 @@ jobs: SCCACHE_BUCKET: multi-runner-cache-x1xibo9c SCCACHE_REGION: ${{ secrets.AWS_REGION }} SCCACHE_S3_KEY_PREFIX: ${{ runner.os }}/sccache/${{ runner.arch }}/linux-gnu + ROCKSDB_STATIC: "/opt/rocksdb/usr/local/lib/librocksdb.a" + ROCKSDB_LIB_DIR: "/opt/rocksdb/usr/local/lib" formatting: name: Formatting - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 timeout-minutes: 5 steps: - name: Check out repo @@ -65,12 +70,19 @@ jobs: components: rustfmt cache: false + # This step doesn't need librocksdb, so we don't install it + - name: Check formatting + env: + RUSTC_WRAPPER: sccache + SCCACHE_BUCKET: multi-runner-cache-x1xibo9c + SCCACHE_REGION: ${{ secrets.AWS_REGION }} + SCCACHE_S3_KEY_PREFIX: ${{ runner.os }}/sccache/${{ runner.arch }}/linux-gnu run: cargo fmt --check --package=${{ inputs.package }} unused_deps: name: Unused dependencies - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 permissions: id-token: write contents: read @@ -89,6 +101,9 @@ jobs: - name: Setup Rust uses: ./.github/actions/rust + - name: Install librocksdb + uses: ./.github/actions/librocksdb + - name: Get crate ${{ inputs.package }} info id: crate_info uses: ./.github/actions/crate_info @@ -102,12 +117,14 @@ jobs: SCCACHE_BUCKET: multi-runner-cache-x1xibo9c SCCACHE_REGION: ${{ secrets.AWS_REGION }} SCCACHE_S3_KEY_PREFIX: ${{ runner.os }}/sccache/${{ runner.arch }}/linux-gnu + ROCKSDB_STATIC: "/opt/rocksdb/usr/local/lib/librocksdb.a" + ROCKSDB_LIB_DIR: "/opt/rocksdb/usr/local/lib" with: args: ${{ steps.crate_info.outputs.cargo_manifest_dir }} detect_structure_changes: name: Detect immutable structure changes - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 # FIXME: as we use `gh pr view` below, this check can only # run on pull requests. We should find a way to run it # when manual triggers are used. @@ -184,6 +201,9 @@ jobs: - name: Setup Rust uses: ./.github/actions/rust + - name: Install librocksdb + uses: ./.github/actions/librocksdb + - name: Run tests run: cargo test --package=${{ inputs.package }} --all-features --locked env: @@ -191,6 +211,8 @@ jobs: SCCACHE_BUCKET: multi-runner-cache-x1xibo9c SCCACHE_REGION: ${{ secrets.AWS_REGION }} SCCACHE_S3_KEY_PREFIX: ${{ runner.os }}/sccache/${{ runner.arch }}/linux-gnu + ROCKSDB_STATIC: "/opt/rocksdb/usr/local/lib/librocksdb.a" + ROCKSDB_LIB_DIR: "/opt/rocksdb/usr/local/lib" check_each_feature: name: Check each feature @@ -199,7 +221,7 @@ jobs: if: ${{ inputs.check-each-feature }} steps: - name: Check out repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Configure AWS credentials and bucket region uses: aws-actions/configure-aws-credentials@v4 @@ -211,6 +233,9 @@ jobs: - name: Setup Rust uses: ./.github/actions/rust + - name: Install librocksdb + uses: ./.github/actions/librocksdb + - name: Get crate ${{ runner.arch }} info id: crate_info uses: ./.github/actions/crate_info @@ -223,6 +248,8 @@ jobs: SCCACHE_BUCKET: multi-runner-cache-x1xibo9c SCCACHE_REGION: ${{ secrets.AWS_REGION }} SCCACHE_S3_KEY_PREFIX: ${{ runner.os }}/sccache/${{ runner.arch }}/linux-gnu + ROCKSDB_STATIC: "/opt/rocksdb/usr/local/lib/librocksdb.a" + ROCKSDB_LIB_DIR: "/opt/rocksdb/usr/local/lib" run: | echo Verify all features disabled set -ex diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c823d0cd06..5f6cec2c08 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -2,17 +2,16 @@ name: Tests on: workflow_dispatch: + inputs: + rebuild-deps: + description: "Rebuild cached Rust dependencies" + required: false + default: "false" pull_request: types: [opened, synchronize, reopened, ready_for_review] branches: - master - 'v[0-9]+\.[0-9]+-dev' - push: - branches: - - master - - 'v[0-9]+\.[0-9]+-dev' - schedule: - - cron: "30 4 * * *" concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -22,13 +21,13 @@ jobs: changes: name: Determine changed packages if: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' || github.event_name == 'schedule' || !github.event.pull_request.draft }} - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 outputs: js-packages: ${{ steps.filter-js.outputs.changes }} rs-packages: ${{ steps.filter-rs.outputs.changes }} steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 @@ -42,6 +41,22 @@ jobs: with: filters: .github/package-filters/rs-packages.yml + build-rust-deps: + name: Prebuild and cache some Rust dependencies + runs-on: ubuntu-24.04 + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_REGION: ${{ secrets.AWS_REGION }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 1 + - name: Precompile librocksdb + uses: ./.github/actions/librocksdb + build-js: name: Build JS packages if: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' || github.event_name == 'schedule' || !github.event.pull_request.draft }} @@ -75,6 +90,7 @@ jobs: name: Rust packages needs: - changes + - build-rust-deps secrets: inherit strategy: fail-fast: false @@ -83,17 +99,19 @@ jobs: uses: ./.github/workflows/tests-rs-package.yml with: package: ${{ matrix.rs-package }} - # lint-runner: ${{ contains(fromJSON('["drive-abci", "drive"]'), matrix.rs-package) && '[ "self-hosted", "linux", "arm64", "ubuntu-platform" ]' || '"ubuntu-22.04"' }} + # lint-runner: ${{ contains(fromJSON('["drive-abci", "drive"]'), matrix.rs-package) && '[ "self-hosted", "linux", "arm64", "ubuntu-platform" ]' || '"ubuntu-24.04"' }} # FIXME: Clippy fails on github hosted runners, most likely due to RAM usage. Using self-hosted runners for now. - lint-runner: '[ "self-hosted", "linux", "arm64", "ubuntu-platform" ]' + # lint-runner: '[ "self-hosted", "linux", "arm64", "ubuntu-platform" ]' + lint-runner: '["ubuntu-24.04"]' # Run drive tests on self-hosted 4x - test-runner: '[ "self-hosted", "linux", "arm64", "ubuntu-platform" ]' + # test-runner: '[ "self-hosted", "linux", "arm64", "ubuntu-platform" ]' + test-runner: '["ubuntu-24.04"]' check-each-feature: ${{ contains(fromJSON('["dash-sdk","rs-dapi-client","dapi-grpc","dpp","drive-abci"]'), matrix.rs-package) }} rs-crates-security: name: Rust crates security audit if: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' || github.event_name == 'schedule' || !github.event.pull_request.draft }} - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - name: Check out repo uses: actions/checkout@v4 @@ -122,7 +140,7 @@ jobs: js-deps-versions: name: JS dependency versions check if: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' || github.event_name == 'schedule' || !github.event.pull_request.draft }} - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - name: Check out repo uses: actions/checkout@v4 @@ -141,7 +159,7 @@ jobs: js-npm-security: name: JS NPM security audit if: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' || github.event_name == 'schedule' || !github.event.pull_request.draft }} - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - name: Check out repo uses: actions/checkout@v4 diff --git a/packages/rs-dpp/src/withdrawal/daily_withdrawal_limit/mod.rs b/packages/rs-dpp/src/withdrawal/daily_withdrawal_limit/mod.rs index b1cd0a477d..997bd42262 100644 --- a/packages/rs-dpp/src/withdrawal/daily_withdrawal_limit/mod.rs +++ b/packages/rs-dpp/src/withdrawal/daily_withdrawal_limit/mod.rs @@ -4,6 +4,7 @@ use crate::ProtocolError; use platform_version::version::PlatformVersion; mod v0; +mod v1; pub fn daily_withdrawal_limit( total_credits_in_platform: Credits, @@ -11,6 +12,7 @@ pub fn daily_withdrawal_limit( ) -> Result { match platform_version.dpp.methods.daily_withdrawal_limit { 0 => Ok(daily_withdrawal_limit_v0(total_credits_in_platform)), + 1 => Ok(v1::daily_withdrawal_limit_v1()), v => Err(ProtocolError::UnknownVersionError(format!( "Unknown daily_withdrawal_limit version {v}" ))), diff --git a/packages/rs-dpp/src/withdrawal/daily_withdrawal_limit/v1/mod.rs b/packages/rs-dpp/src/withdrawal/daily_withdrawal_limit/v1/mod.rs new file mode 100644 index 0000000000..b02b8fe352 --- /dev/null +++ b/packages/rs-dpp/src/withdrawal/daily_withdrawal_limit/v1/mod.rs @@ -0,0 +1,12 @@ +use crate::fee::Credits; + +/// Set constant withdrawal daily limit to 2000 Dash +/// +/// # Returns +/// +/// * `Credits`: The calculated daily withdrawal limit based on the available credits. +/// +pub fn daily_withdrawal_limit_v1() -> Credits { + // 2000 Dash + 200_000_000_000_000 +} diff --git a/packages/rs-drive-abci/src/abci/handler/finalize_block.rs b/packages/rs-drive-abci/src/abci/handler/finalize_block.rs index 9653391c7d..852f85cc6b 100644 --- a/packages/rs-drive-abci/src/abci/handler/finalize_block.rs +++ b/packages/rs-drive-abci/src/abci/handler/finalize_block.rs @@ -5,6 +5,7 @@ use crate::execution::types::block_execution_context::v0::BlockExecutionContextV 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 dpp::dashcore::Network; use std::sync::atomic::Ordering; use tenderdash_abci::proto::abci as proto; @@ -66,7 +67,30 @@ where )); } - app.commit_transaction(platform_version)?; + let result = app.commit_transaction(platform_version); + + // We had a sequence of errors on the mainnet started since block 32326. + // We got RocksDB's "transaction is busy" error because of a bug (https://github.com/dashpay/platform/pull/2309). + // Due to another bug in Tenderdash (https://github.com/dashpay/tenderdash/pull/966), + // validators just proceeded to the next block partially committing the state and updating the cache. + // Full nodes are stuck and proceeded after re-sync. + // For the mainnet chain, we enable these fixes at the block when we consider the state is consistent. + let config = &app.platform().config; + + if app.platform().config.network == Network::Dash + && config.abci.chain_id == "evo1" + && block_height < 33000 + { + // Old behavior on mainnet below block 33000 + result?; + } else { + // In case if transaction commit failed we still have caches in memory that + // corresponds to the data that we weren't able to commit. + // The simplified solution is to restart the Drive, so all caches + // will be restored from the disk and try to process this block again. + // TODO: We need a better handling of the transaction is busy error with retry logic. + result.expect("commit transaction"); + } app.platform() .committed_block_height_guard diff --git a/packages/rs-drive-abci/src/abci/handler/info.rs b/packages/rs-drive-abci/src/abci/handler/info.rs index dbb8501891..9ac9d31626 100644 --- a/packages/rs-drive-abci/src/abci/handler/info.rs +++ b/packages/rs-drive-abci/src/abci/handler/info.rs @@ -3,6 +3,7 @@ use crate::abci::AbciError; use crate::error::Error; use crate::platform_types::platform_state::v0::PlatformStateV0Methods; use crate::rpc::core::CoreRPCLike; +use dpp::dashcore::Network; use dpp::version::DESIRED_PLATFORM_VERSION; use tenderdash_abci::proto::abci as proto; @@ -21,19 +22,58 @@ where let platform_state = app.platform().state.load(); - let state_app_hash = platform_state + let last_block_height = platform_state.last_committed_block_height() as i64; + + // Verify that Platform State corresponds to Drive commited state + let platform_state_app_hash = platform_state .last_committed_block_app_hash() - .map(|app_hash| app_hash.to_vec()) .unwrap_or_default(); + let grove_version = &platform_state + .current_platform_version()? + .drive + .grove_version; + + let drive_storage_root_hash = app + .platform() + .drive + .grove + .root_hash(None, grove_version) + .unwrap()?; + + // We had a sequence of errors on the mainnet started since block 32326. + // We got RocksDB's "transaction is busy" error because of a bug (https://github.com/dashpay/platform/pull/2309). + // Due to another bug in Tenderdash (https://github.com/dashpay/tenderdash/pull/966), + // validators just proceeded to the next block partially committing the state and updating the cache. + // Full nodes are stuck and proceeded after re-sync. + // For the mainnet chain, we enable these fixes at the block when we consider the state is consistent. + let config = &app.platform().config; + + #[allow(clippy::collapsible_if)] + if !(config.network == Network::Dash + && config.abci.chain_id == "evo1" + && last_block_height < 33000) + { + // App hash in memory must be equal to app hash on disk + if drive_storage_root_hash != platform_state_app_hash { + // We panic because we can't recover from this situation. + // Better to restart the Drive, so we might self-heal the node + // reloading state form the disk + panic!( + "drive and platform state app hash mismatch: drive_storage_root_hash: {:?}, platform_state_app_hash: {:?}", + drive_storage_root_hash, platform_state_app_hash + ); + } + } + let desired_protocol_version = DESIRED_PLATFORM_VERSION.protocol_version; let response = proto::ResponseInfo { data: "".to_string(), app_version: desired_protocol_version as u64, - last_block_height: platform_state.last_committed_block_height() as i64, + last_block_height, version: env!("CARGO_PKG_VERSION").to_string(), - last_block_app_hash: state_app_hash.clone(), + last_block_app_hash: platform_state_app_hash.to_vec(), }; tracing::debug!( @@ -41,8 +81,8 @@ where software_version = env!("CARGO_PKG_VERSION"), block_version = request.block_version, p2p_version = request.p2p_version, - app_hash = hex::encode(state_app_hash), - height = platform_state.last_committed_block_height(), + app_hash = hex::encode(platform_state_app_hash), + last_block_height, "Handshake with consensus engine", ); diff --git a/packages/rs-drive-abci/src/abci/handler/prepare_proposal.rs b/packages/rs-drive-abci/src/abci/handler/prepare_proposal.rs index 18252d0d45..61f58a0196 100644 --- a/packages/rs-drive-abci/src/abci/handler/prepare_proposal.rs +++ b/packages/rs-drive-abci/src/abci/handler/prepare_proposal.rs @@ -11,6 +11,7 @@ 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::dashcore::Network; use dpp::version::TryIntoPlatformVersioned; use drive::grovedb_storage::Error::RocksDBError; use tenderdash_abci::proto::abci as proto; @@ -35,6 +36,48 @@ where let platform_state = app.platform().state.load(); + // Verify that Platform State corresponds to Drive commited state + let platform_state_app_hash = platform_state + .last_committed_block_app_hash() + .unwrap_or_default(); + + let grove_version = &platform_state + .current_platform_version()? + .drive + .grove_version; + + let drive_storage_root_hash = app + .platform() + .drive + .grove + .root_hash(None, grove_version) + .unwrap()?; + + // We had a sequence of errors on the mainnet started since block 32326. + // We got RocksDB's "transaction is busy" error because of a bug (https://github.com/dashpay/platform/pull/2309). + // Due to another bug in Tenderdash (https://github.com/dashpay/tenderdash/pull/966), + // validators just proceeded to the next block partially committing the state and updating the cache. + // Full nodes are stuck and proceeded after re-sync. + // For the mainnet chain, we enable these fixes at the block when we consider the state is consistent. + let config = &app.platform().config; + + #[allow(clippy::collapsible_if)] + if !(config.network == Network::Dash + && config.abci.chain_id == "evo1" + && request.height < 33000) + { + // App hash in memory must be equal to app hash on disk + if drive_storage_root_hash != platform_state_app_hash { + // We panic because we can't recover from this situation. + // Better to restart the Drive, so we might self-heal the node + // reloading state form the disk + panic!( + "drive and platform state app hash mismatch: drive_storage_root_hash: {:?}, platform_state_app_hash: {:?}", + drive_storage_root_hash, platform_state_app_hash + ); + } + } + let last_committed_core_height = platform_state.last_committed_core_height(); let starting_platform_version = platform_state.current_platform_version()?; diff --git a/packages/rs-drive-abci/src/abci/handler/process_proposal.rs b/packages/rs-drive-abci/src/abci/handler/process_proposal.rs index 5bf547e14a..d40567d3db 100644 --- a/packages/rs-drive-abci/src/abci/handler/process_proposal.rs +++ b/packages/rs-drive-abci/src/abci/handler/process_proposal.rs @@ -12,6 +12,7 @@ use crate::platform_types::block_execution_outcome; 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::Network; use dpp::version::TryIntoPlatformVersioned; use drive::grovedb_storage::Error::RocksDBError; use tenderdash_abci::proto::abci as proto; @@ -179,6 +180,48 @@ where let platform_state = app.platform().state.load(); + // Verify that Platform State corresponds to Drive commited state + let platform_state_app_hash = platform_state + .last_committed_block_app_hash() + .unwrap_or_default(); + + let grove_version = &platform_state + .current_platform_version()? + .drive + .grove_version; + + let drive_storage_root_hash = app + .platform() + .drive + .grove + .root_hash(None, grove_version) + .unwrap()?; + + // We had a sequence of errors on the mainnet started since block 32326. + // We got RocksDB's "transaction is busy" error because of a bug (https://github.com/dashpay/platform/pull/2309). + // Due to another bug in Tenderdash (https://github.com/dashpay/tenderdash/pull/966), + // validators just proceeded to the next block partially committing the state and updating the cache. + // Full nodes are stuck and proceeded after re-sync. + // For the mainnet chain, we enable these fixes at the block when we consider the state is consistent. + let config = &app.platform().config; + + #[allow(clippy::collapsible_if)] + if !(app.platform().config.network == Network::Dash + && config.abci.chain_id == "evo1" + && request.height < 33000) + { + // App hash in memory must be equal to app hash on disk + if drive_storage_root_hash != platform_state_app_hash { + // We panic because we can't recover from this situation. + // Better to restart the Drive, so we might self-heal the node + // reloading state form the disk + panic!( + "drive and platform state app hash mismatch: drive_storage_root_hash: {:?}, platform_state_app_hash: {:?}", + drive_storage_root_hash, platform_state_app_hash + ); + } + } + let starting_platform_version = platform_state.current_platform_version()?; // Running the proposal executes all the state transitions for the block diff --git a/packages/rs-drive-abci/src/execution/engine/run_block_proposal/v0/mod.rs b/packages/rs-drive-abci/src/execution/engine/run_block_proposal/v0/mod.rs index 9c45694f6c..6eb4fd82bb 100644 --- a/packages/rs-drive-abci/src/execution/engine/run_block_proposal/v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/engine/run_block_proposal/v0/mod.rs @@ -270,9 +270,13 @@ where })?; // This is a system error // Rebroadcast expired withdrawals if they exist + // We do that before we mark withdrawals as expired + // to rebroadcast them on the next block but not the same + // one + // TODO: It must be also only on core height change self.rebroadcast_expired_withdrawal_documents( &block_info, - &last_committed_platform_state, + last_committed_platform_state, transaction, platform_version, )?; diff --git a/packages/rs-drive-abci/src/execution/platform_events/voting/remove_votes_for_removed_masternodes/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/voting/remove_votes_for_removed_masternodes/mod.rs index 8328eb0fc1..ffd97c4292 100644 --- a/packages/rs-drive-abci/src/execution/platform_events/voting/remove_votes_for_removed_masternodes/mod.rs +++ b/packages/rs-drive-abci/src/execution/platform_events/voting/remove_votes_for_removed_masternodes/mod.rs @@ -3,6 +3,7 @@ use crate::error::Error; use crate::platform_types::platform::Platform; use crate::platform_types::platform_state::PlatformState; use crate::rpc::core::CoreRPCLike; +use dpp::block::block_info::BlockInfo; use dpp::version::PlatformVersion; use drive::grovedb::TransactionArg; @@ -14,6 +15,7 @@ where /// Removes the votes for removed masternodes pub(in crate::execution) fn remove_votes_for_removed_masternodes( &self, + block_info: &BlockInfo, last_committed_platform_state: &PlatformState, block_platform_state: &PlatformState, transaction: TransactionArg, @@ -26,6 +28,7 @@ where .remove_votes_for_removed_masternodes { 0 => self.remove_votes_for_removed_masternodes_v0( + block_info, last_committed_platform_state, block_platform_state, transaction, diff --git a/packages/rs-drive-abci/src/execution/platform_events/voting/remove_votes_for_removed_masternodes/v0/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/voting/remove_votes_for_removed_masternodes/v0/mod.rs index b0081570c7..04931a3928 100644 --- a/packages/rs-drive-abci/src/execution/platform_events/voting/remove_votes_for_removed_masternodes/v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/platform_events/voting/remove_votes_for_removed_masternodes/v0/mod.rs @@ -3,6 +3,7 @@ use crate::platform_types::platform::Platform; use crate::platform_types::platform_state::v0::PlatformStateV0Methods; use crate::platform_types::platform_state::PlatformState; use crate::rpc::core::CoreRPCLike; +use dpp::block::block_info::BlockInfo; use dpp::dashcore::hashes::Hash; use dpp::version::PlatformVersion; use drive::grovedb::TransactionArg; @@ -14,6 +15,7 @@ where /// Removes the votes for removed masternodes pub(super) fn remove_votes_for_removed_masternodes_v0( &self, + block_info: &BlockInfo, last_committed_platform_state: &PlatformState, block_platform_state: &PlatformState, transaction: TransactionArg, @@ -29,6 +31,9 @@ where .iter() .map(|pro_tx_hash| pro_tx_hash.as_byte_array().to_vec()) .collect(), + block_info.height, + self.config.network, + self.config.abci.chain_id.as_str(), transaction, platform_version, )?; diff --git a/packages/rs-drive-abci/src/execution/platform_events/voting/run_dao_platform_events/v0/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/voting/run_dao_platform_events/v0/mod.rs index 2ea9357af1..57fbe635b2 100644 --- a/packages/rs-drive-abci/src/execution/platform_events/voting/run_dao_platform_events/v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/platform_events/voting/run_dao_platform_events/v0/mod.rs @@ -21,6 +21,7 @@ where // Remove any votes that self.remove_votes_for_removed_masternodes( + block_info, last_committed_platform_state, block_platform_state, transaction, diff --git a/packages/rs-drive-abci/src/execution/platform_events/withdrawals/append_signatures_and_broadcast_withdrawal_transactions/v0/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/withdrawals/append_signatures_and_broadcast_withdrawal_transactions/v0/mod.rs index 1f6cacbc2b..c8f8dda8e5 100644 --- a/packages/rs-drive-abci/src/execution/platform_events/withdrawals/append_signatures_and_broadcast_withdrawal_transactions/v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/platform_events/withdrawals/append_signatures_and_broadcast_withdrawal_transactions/v0/mod.rs @@ -1,10 +1,7 @@ use crate::error::execution::ExecutionError; use crate::error::Error; use crate::platform_types::platform::Platform; -use crate::rpc::core::{ - CoreRPCLike, CORE_RPC_ERROR_ASSET_UNLOCK_EXPIRED, CORE_RPC_ERROR_ASSET_UNLOCK_NO_ACTIVE_QUORUM, - CORE_RPC_TX_ALREADY_IN_CHAIN, -}; +use crate::rpc::core::{CoreRPCLike, CORE_RPC_TX_ALREADY_IN_CHAIN}; use dashcore_rpc::jsonrpc; use dashcore_rpc::Error as CoreRPCError; use dpp::dashcore::bls_sig_utils::BLSSignature; @@ -18,6 +15,14 @@ use std::path::Path; use std::time::{SystemTime, UNIX_EPOCH}; use tenderdash_abci::proto::types::VoteExtension; +// This error is returned when Core can't find a quorum for the asset unlock transaction in Core 21 +const CORE_RPC_ERROR_ASSET_UNLOCK_NO_ACTIVE_QUORUM: &str = "bad-assetunlock-not-active-quorum"; + +// This error replaced the previous since Core 22 to make it more verbose +const CORE_RPC_ERROR_ASSET_UNLOCK_TOO_OLD_QUORUM: &str = "bad-assetunlock-too-old-quorum"; + +const CORE_RPC_ERROR_ASSET_UNLOCK_EXPIRED: &str = "bad-assetunlock-too-late"; + impl Platform where C: CoreRPCLike, @@ -90,7 +95,8 @@ where } Err(CoreRPCError::JsonRpc(jsonrpc::error::Error::Rpc(e))) if e.message == CORE_RPC_ERROR_ASSET_UNLOCK_NO_ACTIVE_QUORUM - || e.message == CORE_RPC_ERROR_ASSET_UNLOCK_EXPIRED => + || e.message == CORE_RPC_ERROR_ASSET_UNLOCK_EXPIRED + || e.message == CORE_RPC_ERROR_ASSET_UNLOCK_TOO_OLD_QUORUM => { tracing::debug!( tx_id = transaction.txid().to_string(), diff --git a/packages/rs-drive-abci/src/execution/platform_events/withdrawals/pool_withdrawals_into_transactions_queue/v1/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/withdrawals/pool_withdrawals_into_transactions_queue/v1/mod.rs index b0b0f99e65..7b36b29d68 100644 --- a/packages/rs-drive-abci/src/execution/platform_events/withdrawals/pool_withdrawals_into_transactions_queue/v1/mod.rs +++ b/packages/rs-drive-abci/src/execution/platform_events/withdrawals/pool_withdrawals_into_transactions_queue/v1/mod.rs @@ -77,9 +77,14 @@ where // If adding this withdrawal would exceed the limit, stop processing further. if potential_total_withdrawal_amount > current_withdrawal_limit { + let available_credits = current_withdrawal_limit - total_withdrawal_amount; + tracing::debug!( + available_credits, + daily_maximum = withdrawals_info.daily_maximum, + withdrawn = withdrawals_info.withdrawals_amount + total_withdrawal_amount, "Pooling is limited due to daily withdrawals limit. {} credits left", - current_withdrawal_limit + available_credits ); break; } diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/masternode_vote/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/masternode_vote/mod.rs index fe7d8095b8..9d0394c442 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/masternode_vote/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/masternode_vote/mod.rs @@ -11287,6 +11287,7 @@ mod tests { platform .remove_votes_for_removed_masternodes( + &BlockInfo::default(), &platform_state_before_masternode_identity_removals, &block_platform_state, Some(&transaction), diff --git a/packages/rs-drive-abci/src/rpc/core.rs b/packages/rs-drive-abci/src/rpc/core.rs index a16f0341dc..91ded6f6fe 100644 --- a/packages/rs-drive-abci/src/rpc/core.rs +++ b/packages/rs-drive-abci/src/rpc/core.rs @@ -152,11 +152,6 @@ pub const CORE_RPC_INVALID_ADDRESS_OR_KEY: i32 = -5; /// Invalid, missing or duplicate parameter pub const CORE_RPC_INVALID_PARAMETER: i32 = -8; -/// Asset Unlock consensus error "bad-assetunlock-not-active-quorum" -pub const CORE_RPC_ERROR_ASSET_UNLOCK_NO_ACTIVE_QUORUM: &str = "bad-assetunlock-not-active-quorum"; -/// Asset Unlock consensus error "bad-assetunlock-not-active-quorum" -pub const CORE_RPC_ERROR_ASSET_UNLOCK_EXPIRED: &str = "bad-assetunlock-too-late"; - macro_rules! retry { ($action:expr) => {{ /// Maximum number of retry attempts diff --git a/packages/rs-drive-abci/tests/strategy_tests/main.rs b/packages/rs-drive-abci/tests/strategy_tests/main.rs index 2312241cc6..03bb92bc1a 100644 --- a/packages/rs-drive-abci/tests/strategy_tests/main.rs +++ b/packages/rs-drive-abci/tests/strategy_tests/main.rs @@ -2602,7 +2602,10 @@ mod tests { &simple_signer, &mut rng, platform_version, - ); + ) + .into_iter() + .map(|(identity, transition)| (identity, Some(transition))) + .collect(); let strategy = NetworkStrategy { strategy: Strategy { @@ -3910,7 +3913,7 @@ mod tests { strategy: Strategy { start_contracts: vec![], operations: vec![Operation { - op_type: OperationType::IdentityTransfer, + op_type: OperationType::IdentityTransfer(None), frequency: Frequency { times_per_block_range: 1..3, chance_per_block: None, diff --git a/packages/rs-drive-abci/tests/strategy_tests/strategy.rs b/packages/rs-drive-abci/tests/strategy_tests/strategy.rs index 667b846868..bf3235ea78 100644 --- a/packages/rs-drive-abci/tests/strategy_tests/strategy.rs +++ b/packages/rs-drive-abci/tests/strategy_tests/strategy.rs @@ -41,7 +41,7 @@ use drive_abci::rpc::core::MockCoreRPCLike; use rand::prelude::{IteratorRandom, SliceRandom, StdRng}; use rand::Rng; use strategy_tests::Strategy; -use strategy_tests::transitions::{create_state_transitions_for_identities, create_state_transitions_for_identities_and_proofs, instant_asset_lock_proof_fixture, instant_asset_lock_proof_fixture_with_dynamic_range}; +use strategy_tests::transitions::{create_state_transitions_for_identities, create_state_transitions_for_identities_and_proofs, instant_asset_lock_proof_fixture_with_dynamic_range}; use std::borrow::Cow; use std::collections::{BTreeMap, HashMap, HashSet}; use std::ops::RangeInclusive; @@ -404,8 +404,18 @@ impl NetworkStrategy { ); state_transitions.append(&mut new_transitions); } + // Extend the state transitions with the strategy's hard coded start identities + // Filtering out the ones that have no create transition if !self.strategy.start_identities.hard_coded.is_empty() { - state_transitions.extend(self.strategy.start_identities.hard_coded.clone()); + state_transitions.extend( + self.strategy.start_identities.hard_coded.iter().filter_map( + |(identity, transition)| { + transition.as_ref().map(|create_transition| { + (identity.clone(), create_transition.clone()) + }) + }, + ), + ); } } let frequency = &self.strategy.identity_inserts.frequency; @@ -1196,7 +1206,7 @@ impl NetworkStrategy { operations.push(state_transition); } } - OperationType::IdentityTransfer if current_identities.len() > 1 => { + OperationType::IdentityTransfer(_) if current_identities.len() > 1 => { let identities_clone = current_identities.clone(); // Sender is the first in the list, which should be loaded_identity diff --git a/packages/rs-drive-abci/tests/strategy_tests/voting_tests.rs b/packages/rs-drive-abci/tests/strategy_tests/voting_tests.rs index e14f8d7b1b..83834520c0 100644 --- a/packages/rs-drive-abci/tests/strategy_tests/voting_tests.rs +++ b/packages/rs-drive-abci/tests/strategy_tests/voting_tests.rs @@ -79,13 +79,17 @@ mod tests { simple_signer.add_keys(keys1); - let start_identities = create_state_transitions_for_identities( - vec![identity1], - &(dash_to_duffs!(1)..=dash_to_duffs!(1)), - &simple_signer, - &mut rng, - platform_version, - ); + let start_identities: Vec<(Identity, Option)> = + create_state_transitions_for_identities( + vec![identity1], + &(dash_to_duffs!(1)..=dash_to_duffs!(1)), + &simple_signer, + &mut rng, + platform_version, + ) + .into_iter() + .map(|(identity, transition)| (identity, Some(transition))) + .collect(); let dpns_contract = platform .drive @@ -363,13 +367,17 @@ mod tests { simple_signer.add_keys(keys2); - let start_identities = create_state_transitions_for_identities( - vec![identity1, identity2], - &(dash_to_duffs!(1)..=dash_to_duffs!(1)), - &simple_signer, - &mut rng, - platform_version, - ); + let start_identities: Vec<(Identity, Option)> = + create_state_transitions_for_identities( + vec![identity1, identity2], + &(dash_to_duffs!(1)..=dash_to_duffs!(1)), + &simple_signer, + &mut rng, + platform_version, + ) + .into_iter() + .map(|(identity, transition)| (identity, Some(transition))) + .collect(); let dpns_contract = platform .drive @@ -635,13 +643,17 @@ mod tests { simple_signer.add_keys(keys2); - let start_identities = create_state_transitions_for_identities( - vec![identity1, identity2], - &(dash_to_duffs!(1)..=dash_to_duffs!(1)), - &simple_signer, - &mut rng, - platform_version, - ); + let start_identities: Vec<(Identity, Option)> = + create_state_transitions_for_identities( + vec![identity1, identity2], + &(dash_to_duffs!(1)..=dash_to_duffs!(1)), + &simple_signer, + &mut rng, + platform_version, + ) + .into_iter() + .map(|(identity, transition)| (identity, Some(transition))) + .collect(); let dpns_contract = platform .drive @@ -988,13 +1000,17 @@ mod tests { simple_signer.add_keys(keys2); - let start_identities = create_state_transitions_for_identities( - vec![identity1, identity2], - &(dash_to_duffs!(1)..=dash_to_duffs!(1)), - &simple_signer, - &mut rng, - platform_version, - ); + let start_identities: Vec<(Identity, Option)> = + create_state_transitions_for_identities( + vec![identity1, identity2], + &(dash_to_duffs!(1)..=dash_to_duffs!(1)), + &simple_signer, + &mut rng, + platform_version, + ) + .into_iter() + .map(|(identity, transition)| (identity, Some(transition))) + .collect(); let dpns_contract = platform .drive @@ -1353,13 +1369,17 @@ mod tests { simple_signer.add_keys(keys2); - let start_identities = create_state_transitions_for_identities( - vec![identity1, identity2], - &(dash_to_duffs!(1)..=dash_to_duffs!(1)), - &simple_signer, - &mut rng, - platform_version, - ); + let start_identities: Vec<(Identity, Option)> = + create_state_transitions_for_identities( + vec![identity1, identity2], + &(dash_to_duffs!(1)..=dash_to_duffs!(1)), + &simple_signer, + &mut rng, + platform_version, + ) + .into_iter() + .map(|(identity, transition)| (identity, Some(transition))) + .collect(); let dpns_contract = platform .drive diff --git a/packages/rs-drive/src/drive/votes/cleanup/remove_all_votes_given_by_identities/mod.rs b/packages/rs-drive/src/drive/votes/cleanup/remove_all_votes_given_by_identities/mod.rs index 0f5e0d9604..f93d92a424 100644 --- a/packages/rs-drive/src/drive/votes/cleanup/remove_all_votes_given_by_identities/mod.rs +++ b/packages/rs-drive/src/drive/votes/cleanup/remove_all_votes_given_by_identities/mod.rs @@ -5,6 +5,8 @@ use crate::drive::Drive; use crate::error::drive::DriveError; use crate::error::Error; +use dpp::dashcore::Network; +use dpp::prelude::BlockHeight; use dpp::version::PlatformVersion; use grovedb::TransactionArg; @@ -14,6 +16,9 @@ impl Drive { pub fn remove_all_votes_given_by_identities( &self, identity_ids_as_byte_arrays: Vec>, + block_height: BlockHeight, + network: Network, + chain_id: &str, transaction: TransactionArg, platform_version: &PlatformVersion, ) -> Result<(), Error> { @@ -26,6 +31,9 @@ impl Drive { { 0 => self.remove_all_votes_given_by_identities_v0( identity_ids_as_byte_arrays, + block_height, + network, + chain_id, transaction, platform_version, ), diff --git a/packages/rs-drive/src/drive/votes/cleanup/remove_all_votes_given_by_identities/v0/mod.rs b/packages/rs-drive/src/drive/votes/cleanup/remove_all_votes_given_by_identities/v0/mod.rs index 3c36b0ec64..81b3d0fab7 100644 --- a/packages/rs-drive/src/drive/votes/cleanup/remove_all_votes_given_by_identities/v0/mod.rs +++ b/packages/rs-drive/src/drive/votes/cleanup/remove_all_votes_given_by_identities/v0/mod.rs @@ -11,7 +11,8 @@ use crate::drive::votes::paths::{ use crate::drive::votes::storage_form::contested_document_resource_reference_storage_form::ContestedDocumentResourceVoteReferenceStorageForm; use crate::query::QueryItem; use crate::util::grove_operations::BatchDeleteApplyType; -use dpp::prelude::Identifier; +use dpp::dashcore::Network; +use dpp::prelude::{BlockHeight, Identifier}; use dpp::version::PlatformVersion; use grovedb::query_result_type::QueryResultType::QueryPathKeyElementTrioResultType; use grovedb::{PathQuery, Query, SizedQuery, TransactionArg}; @@ -22,6 +23,9 @@ impl Drive { pub(super) fn remove_all_votes_given_by_identities_v0( &self, identity_ids_as_byte_arrays: Vec>, + block_height: BlockHeight, + network: Network, + chain_id: &str, transaction: TransactionArg, platform_version: &PlatformVersion, ) -> Result<(), Error> { @@ -112,9 +116,25 @@ impl Drive { } if !deletion_batch.is_empty() { + // We had a sequence of errors on the mainnet started since block 32326. + // We got RocksDB's "transaction is busy" error because of a bug (https://github.com/dashpay/platform/pull/2309). + // Due to another bug in Tenderdash (https://github.com/dashpay/tenderdash/pull/966), + // validators just proceeded to the next block partially committing the state + // and updating the cache (https://github.com/dashpay/platform/pull/2305). + // Full nodes are stuck and proceeded after re-sync. + // For the mainnet chain, we enable this fix at the block when we consider the state is consistent. + let transaction = + if network == Network::Dash && chain_id == "evo1" && block_height < 33000 { + // Old behaviour on mainnet + None + } else { + // We should use transaction + transaction + }; + self.apply_batch_low_level_drive_operations( None, - None, + transaction, deletion_batch, &mut vec![], &platform_version.drive, diff --git a/packages/rs-platform-version/src/version/dpp_versions/dpp_method_versions/mod.rs b/packages/rs-platform-version/src/version/dpp_versions/dpp_method_versions/mod.rs index a39362f90e..e541023efc 100644 --- a/packages/rs-platform-version/src/version/dpp_versions/dpp_method_versions/mod.rs +++ b/packages/rs-platform-version/src/version/dpp_versions/dpp_method_versions/mod.rs @@ -1,6 +1,7 @@ use versioned_feature_core::FeatureVersion; pub mod v1; +pub mod v2; #[derive(Clone, Debug, Default)] pub struct DPPMethodVersions { diff --git a/packages/rs-platform-version/src/version/dpp_versions/dpp_method_versions/v2.rs b/packages/rs-platform-version/src/version/dpp_versions/dpp_method_versions/v2.rs new file mode 100644 index 0000000000..d15ed3eaaf --- /dev/null +++ b/packages/rs-platform-version/src/version/dpp_versions/dpp_method_versions/v2.rs @@ -0,0 +1,5 @@ +use crate::version::dpp_versions::dpp_method_versions::DPPMethodVersions; +pub const DPP_METHOD_VERSIONS_V2: DPPMethodVersions = DPPMethodVersions { + epoch_core_reward_credits_for_distribution: 0, + daily_withdrawal_limit: 1, +}; diff --git a/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_method_versions/mod.rs b/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_method_versions/mod.rs index 48caab4b3c..8d671176f4 100644 --- a/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_method_versions/mod.rs +++ b/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_method_versions/mod.rs @@ -4,6 +4,7 @@ pub mod v1; pub mod v2; pub mod v3; pub mod v4; +pub mod v5; #[derive(Clone, Debug, Default)] pub struct DriveAbciMethodVersions { diff --git a/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_method_versions/v5.rs b/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_method_versions/v5.rs new file mode 100644 index 0000000000..900c0c153b --- /dev/null +++ b/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_method_versions/v5.rs @@ -0,0 +1,124 @@ +use crate::version::drive_abci_versions::drive_abci_method_versions::{ + DriveAbciBlockEndMethodVersions, DriveAbciBlockFeeProcessingMethodVersions, + DriveAbciBlockStartMethodVersions, DriveAbciCoreBasedUpdatesMethodVersions, + DriveAbciCoreChainLockMethodVersionsAndConstants, DriveAbciCoreInstantSendLockMethodVersions, + DriveAbciEngineMethodVersions, DriveAbciEpochMethodVersions, + DriveAbciFeePoolInwardsDistributionMethodVersions, + DriveAbciFeePoolOutwardsDistributionMethodVersions, + DriveAbciIdentityCreditWithdrawalMethodVersions, DriveAbciInitializationMethodVersions, + DriveAbciMasternodeIdentitiesUpdatesMethodVersions, DriveAbciMethodVersions, + DriveAbciPlatformStateStorageMethodVersions, DriveAbciProtocolUpgradeMethodVersions, + DriveAbciStateTransitionProcessingMethodVersions, DriveAbciVotingMethodVersions, +}; + +pub const DRIVE_ABCI_METHOD_VERSIONS_V5: DriveAbciMethodVersions = DriveAbciMethodVersions { + engine: DriveAbciEngineMethodVersions { + init_chain: 0, + check_tx: 0, + run_block_proposal: 0, + finalize_block_proposal: 0, + consensus_params_update: 1, + }, + initialization: DriveAbciInitializationMethodVersions { + initial_core_height_and_time: 0, + create_genesis_state: 0, + }, + core_based_updates: DriveAbciCoreBasedUpdatesMethodVersions { + update_core_info: 0, + update_masternode_list: 0, + update_quorum_info: 0, + masternode_updates: DriveAbciMasternodeIdentitiesUpdatesMethodVersions { + get_voter_identity_key: 0, + get_operator_identity_keys: 0, + get_owner_identity_withdrawal_key: 0, + get_owner_identity_owner_key: 0, + get_voter_identifier_from_masternode_list_item: 0, + get_operator_identifier_from_masternode_list_item: 0, + create_operator_identity: 0, + create_owner_identity: 1, + create_voter_identity: 0, + disable_identity_keys: 0, + update_masternode_identities: 0, + update_operator_identity: 0, + update_owner_withdrawal_address: 1, + update_voter_identity: 0, + }, + }, + protocol_upgrade: DriveAbciProtocolUpgradeMethodVersions { + check_for_desired_protocol_upgrade: 1, + upgrade_protocol_version_on_epoch_change: 0, + perform_events_on_first_block_of_protocol_change: Some(0), + protocol_version_upgrade_percentage_needed: 67, + }, + block_fee_processing: DriveAbciBlockFeeProcessingMethodVersions { + add_process_epoch_change_operations: 0, + process_block_fees: 0, + }, + core_chain_lock: DriveAbciCoreChainLockMethodVersionsAndConstants { + choose_quorum: 0, + verify_chain_lock: 0, + verify_chain_lock_locally: 0, + verify_chain_lock_through_core: 0, + make_sure_core_is_synced_to_chain_lock: 0, + recent_block_count_amount: 2, + }, + core_instant_send_lock: DriveAbciCoreInstantSendLockMethodVersions { + verify_recent_signature_locally: 0, + }, + fee_pool_inwards_distribution: DriveAbciFeePoolInwardsDistributionMethodVersions { + add_distribute_block_fees_into_pools_operations: 0, + add_distribute_storage_fee_to_epochs_operations: 0, + }, + fee_pool_outwards_distribution: DriveAbciFeePoolOutwardsDistributionMethodVersions { + add_distribute_fees_from_oldest_unpaid_epoch_pool_to_proposers_operations: 0, + add_epoch_pool_to_proposers_payout_operations: 0, + find_oldest_epoch_needing_payment: 0, + fetch_reward_shares_list_for_masternode: 0, + }, + withdrawals: DriveAbciIdentityCreditWithdrawalMethodVersions { + build_untied_withdrawal_transactions_from_documents: 0, + dequeue_and_build_unsigned_withdrawal_transactions: 0, + fetch_transactions_block_inclusion_status: 0, + // We changed `pool_withdrawals_into_transactions_queue` to v1 in order to add pool + // withdrawals on any validator quorums. v0 allowed us to pool only on the first two + // quorums as workaround for Core v21 bug. + pool_withdrawals_into_transactions_queue: 1, + update_broadcasted_withdrawal_statuses: 0, + rebroadcast_expired_withdrawal_documents: 0, + append_signatures_and_broadcast_withdrawal_transactions: 0, + cleanup_expired_locks_of_withdrawal_amounts: 0, + }, + voting: DriveAbciVotingMethodVersions { + keep_record_of_finished_contested_resource_vote_poll: 0, + clean_up_after_vote_poll_end: 0, + clean_up_after_contested_resources_vote_poll_end: 0, + check_for_ended_vote_polls: 0, + tally_votes_for_contested_document_resource_vote_poll: 0, + award_document_to_winner: 0, + delay_vote_poll: 0, + run_dao_platform_events: 0, + remove_votes_for_removed_masternodes: 0, + }, + state_transition_processing: DriveAbciStateTransitionProcessingMethodVersions { + execute_event: 0, + process_raw_state_transitions: 0, + decode_raw_state_transitions: 0, + validate_fees_of_event: 0, + }, + epoch: DriveAbciEpochMethodVersions { + gather_epoch_info: 0, + get_genesis_time: 0, + }, + block_start: DriveAbciBlockStartMethodVersions { + clear_drive_block_cache: 0, + }, + block_end: DriveAbciBlockEndMethodVersions { + update_state_cache: 0, + update_drive_cache: 0, + validator_set_update: 2, + }, + platform_state_storage: DriveAbciPlatformStateStorageMethodVersions { + fetch_platform_state: 0, + store_platform_state: 0, + }, +}; diff --git a/packages/rs-platform-version/src/version/mod.rs b/packages/rs-platform-version/src/version/mod.rs index 5c9f2d2942..fad3f99e1e 100644 --- a/packages/rs-platform-version/src/version/mod.rs +++ b/packages/rs-platform-version/src/version/mod.rs @@ -1,5 +1,6 @@ mod protocol_version; use crate::version::v5::PROTOCOL_VERSION_5; +use crate::version::v6::PROTOCOL_VERSION_6; pub use protocol_version::*; mod consensus_versions; @@ -17,8 +18,9 @@ pub mod v2; pub mod v3; pub mod v4; pub mod v5; +pub mod v6; pub type ProtocolVersion = u32; -pub const LATEST_VERSION: ProtocolVersion = PROTOCOL_VERSION_5; +pub const LATEST_VERSION: ProtocolVersion = PROTOCOL_VERSION_6; pub const INITIAL_PROTOCOL_VERSION: ProtocolVersion = 1; diff --git a/packages/rs-platform-version/src/version/protocol_version.rs b/packages/rs-platform-version/src/version/protocol_version.rs index 67f1b96754..f3929b7f56 100644 --- a/packages/rs-platform-version/src/version/protocol_version.rs +++ b/packages/rs-platform-version/src/version/protocol_version.rs @@ -20,6 +20,7 @@ use crate::version::v2::PLATFORM_V2; use crate::version::v3::PLATFORM_V3; use crate::version::v4::PLATFORM_V4; use crate::version::v5::PLATFORM_V5; +use crate::version::v6::PLATFORM_V6; use crate::version::ProtocolVersion; pub use versioned_feature_core::*; @@ -41,6 +42,7 @@ pub const PLATFORM_VERSIONS: &[PlatformVersion] = &[ PLATFORM_V3, PLATFORM_V4, PLATFORM_V5, + PLATFORM_V6, ]; #[cfg(feature = "mock-versions")] @@ -49,7 +51,7 @@ pub static PLATFORM_TEST_VERSIONS: OnceLock> = OnceLock::ne #[cfg(feature = "mock-versions")] const DEFAULT_PLATFORM_TEST_VERSIONS: &[PlatformVersion] = &[TEST_PLATFORM_V2, TEST_PLATFORM_V3]; -pub const LATEST_PLATFORM_VERSION: &PlatformVersion = &PLATFORM_V5; +pub const LATEST_PLATFORM_VERSION: &PlatformVersion = &PLATFORM_V6; pub const DESIRED_PLATFORM_VERSION: &PlatformVersion = LATEST_PLATFORM_VERSION; diff --git a/packages/rs-platform-version/src/version/v6.rs b/packages/rs-platform-version/src/version/v6.rs new file mode 100644 index 0000000000..c6b6ddcdf8 --- /dev/null +++ b/packages/rs-platform-version/src/version/v6.rs @@ -0,0 +1,68 @@ +use crate::version::consensus_versions::ConsensusVersions; +use crate::version::dpp_versions::dpp_asset_lock_versions::v1::DPP_ASSET_LOCK_VERSIONS_V1; +use crate::version::dpp_versions::dpp_contract_versions::v1::CONTRACT_VERSIONS_V1; +use crate::version::dpp_versions::dpp_costs_versions::v1::DPP_COSTS_VERSIONS_V1; +use crate::version::dpp_versions::dpp_document_versions::v1::DOCUMENT_VERSIONS_V1; +use crate::version::dpp_versions::dpp_factory_versions::v1::DPP_FACTORY_VERSIONS_V1; +use crate::version::dpp_versions::dpp_identity_versions::v1::IDENTITY_VERSIONS_V1; +use crate::version::dpp_versions::dpp_method_versions::v2::DPP_METHOD_VERSIONS_V2; +use crate::version::dpp_versions::dpp_state_transition_conversion_versions::v2::STATE_TRANSITION_CONVERSION_VERSIONS_V2; +use crate::version::dpp_versions::dpp_state_transition_method_versions::v1::STATE_TRANSITION_METHOD_VERSIONS_V1; +use crate::version::dpp_versions::dpp_state_transition_serialization_versions::v1::STATE_TRANSITION_SERIALIZATION_VERSIONS_V1; +use crate::version::dpp_versions::dpp_state_transition_versions::v2::STATE_TRANSITION_VERSIONS_V2; +use crate::version::dpp_versions::dpp_validation_versions::v2::DPP_VALIDATION_VERSIONS_V2; +use crate::version::dpp_versions::dpp_voting_versions::v2::VOTING_VERSION_V2; +use crate::version::dpp_versions::DPPVersion; +use crate::version::drive_abci_versions::drive_abci_method_versions::v5::DRIVE_ABCI_METHOD_VERSIONS_V5; +use crate::version::drive_abci_versions::drive_abci_query_versions::v1::DRIVE_ABCI_QUERY_VERSIONS_V1; +use crate::version::drive_abci_versions::drive_abci_structure_versions::v1::DRIVE_ABCI_STRUCTURE_VERSIONS_V1; +use crate::version::drive_abci_versions::drive_abci_validation_versions::v3::DRIVE_ABCI_VALIDATION_VERSIONS_V3; +use crate::version::drive_abci_versions::drive_abci_withdrawal_constants::v2::DRIVE_ABCI_WITHDRAWAL_CONSTANTS_V2; +use crate::version::drive_abci_versions::DriveAbciVersion; +use crate::version::drive_versions::v2::DRIVE_VERSION_V2; +use crate::version::fee::v1::FEE_VERSION1; +use crate::version::protocol_version::PlatformVersion; +use crate::version::system_data_contract_versions::v1::SYSTEM_DATA_CONTRACT_VERSIONS_V1; +use crate::version::system_limits::v1::SYSTEM_LIMITS_V1; +use crate::version::ProtocolVersion; + +pub const PROTOCOL_VERSION_6: ProtocolVersion = 6; + +/// This version contains some fixes for withdrawals. +pub const PLATFORM_V6: PlatformVersion = PlatformVersion { + protocol_version: PROTOCOL_VERSION_6, + drive: DRIVE_VERSION_V2, + drive_abci: DriveAbciVersion { + structs: DRIVE_ABCI_STRUCTURE_VERSIONS_V1, + // We changed `pool_withdrawals_into_transactions_queue` to v1 in order to add pool + // withdrawals on any validator quorums. v0 allowed us to pool only on the first two + // quorums as workaround for Core v21 bug. + methods: DRIVE_ABCI_METHOD_VERSIONS_V5, + validation_and_processing: DRIVE_ABCI_VALIDATION_VERSIONS_V3, + withdrawal_constants: DRIVE_ABCI_WITHDRAWAL_CONSTANTS_V2, + query: DRIVE_ABCI_QUERY_VERSIONS_V1, + }, + dpp: DPPVersion { + costs: DPP_COSTS_VERSIONS_V1, + validation: DPP_VALIDATION_VERSIONS_V2, + state_transition_serialization_versions: STATE_TRANSITION_SERIALIZATION_VERSIONS_V1, + state_transition_conversion_versions: STATE_TRANSITION_CONVERSION_VERSIONS_V2, + state_transition_method_versions: STATE_TRANSITION_METHOD_VERSIONS_V1, + state_transitions: STATE_TRANSITION_VERSIONS_V2, + contract_versions: CONTRACT_VERSIONS_V1, + document_versions: DOCUMENT_VERSIONS_V1, + identity_versions: IDENTITY_VERSIONS_V1, + voting_versions: VOTING_VERSION_V2, + asset_lock_versions: DPP_ASSET_LOCK_VERSIONS_V1, + // We changed `daily_withdrawal_limit` to v1 to increase daily withdrawal limit + // to 2000 Dash. + methods: DPP_METHOD_VERSIONS_V2, + factory_versions: DPP_FACTORY_VERSIONS_V1, + }, + system_data_contracts: SYSTEM_DATA_CONTRACT_VERSIONS_V1, + fee_version: FEE_VERSION1, + system_limits: SYSTEM_LIMITS_V1, + consensus: ConsensusVersions { + tenderdash_consensus_version: 1, + }, +}; diff --git a/packages/strategy-tests/src/lib.rs b/packages/strategy-tests/src/lib.rs index 61395d99f2..efdb702a48 100644 --- a/packages/strategy-tests/src/lib.rs +++ b/packages/strategy-tests/src/lib.rs @@ -44,6 +44,7 @@ use platform_version::TryFromPlatformVersioned; use rand::prelude::StdRng; use rand::seq::{IteratorRandom, SliceRandom}; use rand::Rng; +use transitions::create_identity_credit_transfer_transition; use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use std::ops::RangeInclusive; use bincode::{Decode, Encode}; @@ -146,7 +147,7 @@ pub struct StartIdentities { pub keys_per_identity: u8, pub starting_balances: u64, // starting balance in duffs pub extra_keys: KeyMaps, - pub hard_coded: Vec<(Identity, StateTransition)>, + pub hard_coded: Vec<(Identity, Option)>, } /// Identities to register on the first block of the strategy @@ -1287,38 +1288,65 @@ impl Strategy { } // Generate state transition for identity transfer operation - OperationType::IdentityTransfer if current_identities.len() > 1 => { + OperationType::IdentityTransfer(identity_transfer_info) => { for _ in 0..count { - let identities_count = current_identities.len(); - if identities_count == 0 { - break; - } + // Handle the case where specific sender, recipient, and amount are provided + if let Some(transfer_info) = identity_transfer_info { + let sender = current_identities + .iter() + .find(|identity| identity.id() == transfer_info.from) + .expect( + "Expected to find sender identity in hardcoded start identities", + ); + let recipient = current_identities + .iter() + .find(|identity| identity.id() == transfer_info.to) + .expect( + "Expected to find recipient identity in hardcoded start identities", + ); - // Select a random identity from the current_identities for the sender - let random_index_sender = rng.gen_range(0..identities_count); + let state_transition = create_identity_credit_transfer_transition( + &sender, + &recipient, + identity_nonce_counter, + signer, // This means in the TUI, the loaded identity must always be the sender since we're always signing with it for now + transfer_info.amount, + ); + operations.push(state_transition); + } else if current_identities.len() > 1 { + // Handle the case where no sender, recipient, and amount are provided - // Clone current_identities to a Vec for manipulation - let mut unused_identities: Vec<_> = - current_identities.iter().cloned().collect(); - unused_identities.remove(random_index_sender); // Remove the sender - let unused_identities_count = unused_identities.len(); + let identities_count = current_identities.len(); + if identities_count == 0 { + break; + } - // Select a random identity from the remaining ones for the recipient - let random_index_recipient = rng.gen_range(0..unused_identities_count); - let recipient = &unused_identities[random_index_recipient]; + // Select a random identity from the current_identities for the sender + let random_index_sender = rng.gen_range(0..identities_count); - // Use the sender index on the original slice - let sender = &mut current_identities[random_index_sender]; + // Clone current_identities to a Vec for manipulation + let mut unused_identities: Vec<_> = + current_identities.iter().cloned().collect(); + unused_identities.remove(random_index_sender); // Remove the sender + let unused_identities_count = unused_identities.len(); - let state_transition = - crate::transitions::create_identity_credit_transfer_transition( + // Select a random identity from the remaining ones for the recipient + let random_index_recipient = + rng.gen_range(0..unused_identities_count); + let recipient = &unused_identities[random_index_recipient]; + + // Use the sender index on the original slice + let sender = &mut current_identities[random_index_sender]; + + let state_transition = create_identity_credit_transfer_transition( sender, recipient, identity_nonce_counter, signer, 300000, ); - operations.push(state_transition); + operations.push(state_transition); + } } } diff --git a/packages/strategy-tests/src/operations.rs b/packages/strategy-tests/src/operations.rs index 675e996843..d35fc9f503 100644 --- a/packages/strategy-tests/src/operations.rs +++ b/packages/strategy-tests/src/operations.rs @@ -497,6 +497,13 @@ impl VoteAction { pub type AmountRange = RangeInclusive; +#[derive(Clone, Debug, PartialEq, Encode, Decode)] +pub struct IdentityTransferInfo { + pub from: Identifier, + pub to: Identifier, + pub amount: Credits, +} + #[derive(Clone, Debug, PartialEq)] pub enum OperationType { Document(DocumentOp), @@ -505,7 +512,7 @@ pub enum OperationType { IdentityWithdrawal(AmountRange), ContractCreate(RandomDocumentTypeParameters, DocumentTypeCount), ContractUpdate(DataContractUpdateOp), - IdentityTransfer, + IdentityTransfer(Option), ResourceVote(ResourceVoteOp), } @@ -517,7 +524,7 @@ enum OperationTypeInSerializationFormat { IdentityWithdrawal(AmountRange), ContractCreate(RandomDocumentTypeParameters, DocumentTypeCount), ContractUpdate(Vec), - IdentityTransfer, + IdentityTransfer(Option), ResourceVote(ResourceVoteOpSerializable), } @@ -563,7 +570,9 @@ impl PlatformSerializableWithPlatformVersion for OperationType { contract_op_in_serialization_format, ) } - OperationType::IdentityTransfer => OperationTypeInSerializationFormat::IdentityTransfer, + OperationType::IdentityTransfer(identity_transfer_info) => { + OperationTypeInSerializationFormat::IdentityTransfer(identity_transfer_info) + } OperationType::ResourceVote(resource_vote_op) => { let vote_op_in_serialization_format = resource_vote_op.try_into_platform_versioned(platform_version)?; @@ -626,7 +635,9 @@ impl PlatformDeserializableWithPotentialValidationFromVersionedStructure for Ope )?; OperationType::ContractUpdate(update_op) } - OperationTypeInSerializationFormat::IdentityTransfer => OperationType::IdentityTransfer, + OperationTypeInSerializationFormat::IdentityTransfer(identity_transfer_info) => { + OperationType::IdentityTransfer(identity_transfer_info) + } OperationTypeInSerializationFormat::ResourceVote(resource_vote_op) => { let vote_op = resource_vote_op.try_into_platform_versioned(platform_version)?; OperationType::ResourceVote(vote_op) diff --git a/packages/strategy-tests/src/transitions.rs b/packages/strategy-tests/src/transitions.rs index 85d03eb333..c77b51e290 100644 --- a/packages/strategy-tests/src/transitions.rs +++ b/packages/strategy-tests/src/transitions.rs @@ -802,7 +802,7 @@ pub fn create_identity_withdrawal_transition_with_output_address( /// - If the sender's identity does not have a suitable authentication key available for signing. /// - If there's an error during the signing process. pub fn create_identity_credit_transfer_transition( - identity: &mut Identity, + identity: &Identity, recipient: &Identity, identity_nonce_counter: &mut BTreeMap, signer: &mut SimpleSigner,