diff --git a/.forklift/config-gitlab.toml b/.forklift/config-gitlab.toml new file mode 100644 index 000000000000..ab3b2729a46d --- /dev/null +++ b/.forklift/config-gitlab.toml @@ -0,0 +1,33 @@ +[compression] +type = "zstd" + +[compression.zstd] +compressionLevel = 3 + +[general] +jobNameVariable = "CI_JOB_NAME" +jobsBlackList = [] +logLevel = "warn" +threadsCount = 6 + +[cache] +extraEnv = ["RUNTIME_METADATA_HASH"] + +[metrics] +enabled = true +pushEndpoint = "placeholder" + +[metrics.extraLabels] +environment = "production" +job_name = "$CI_JOB_NAME" +project_name = "$CI_PROJECT_PATH" + +[storage] +type = "s3" + +[storage.s3] +accessKeyId = "placeholder" +bucketName = "placeholder" +concurrency = 10 +endpointUrl = "placeholder" +secretAccessKey = "placeholder" diff --git a/.forklift/config.toml b/.forklift/config.toml index ab3b2729a46d..6f8eed8882ea 100644 --- a/.forklift/config.toml +++ b/.forklift/config.toml @@ -23,11 +23,7 @@ job_name = "$CI_JOB_NAME" project_name = "$CI_PROJECT_PATH" [storage] -type = "s3" +type = "gcs" -[storage.s3] -accessKeyId = "placeholder" -bucketName = "placeholder" -concurrency = 10 -endpointUrl = "placeholder" -secretAccessKey = "placeholder" +[storage.gcs] +bucketName = "parity-ci-forklift" diff --git a/.github/review-bot.yml b/.github/review-bot.yml index ed719cefec8b..adbc480c6ba1 100644 --- a/.github/review-bot.yml +++ b/.github/review-bot.yml @@ -9,6 +9,7 @@ rules: - ^\.gitlab/.* - ^\.config/nextest.toml - ^\.cargo/.* + - ^\.forklift/.* exclude: - ^\.gitlab/pipeline/zombienet.* type: "or" @@ -33,6 +34,7 @@ rules: - ^docker/.* - ^\.github/.* - ^\.gitlab/.* + - ^\.forklift/.* - ^\.config/nextest.toml - ^\.cargo/.* minApprovals: 2 diff --git a/.github/workflows/check-runtime-migration.yml b/.github/workflows/check-runtime-migration.yml index 671673c02c09..33da5a8ecd59 100644 --- a/.github/workflows/check-runtime-migration.yml +++ b/.github/workflows/check-runtime-migration.yml @@ -11,13 +11,6 @@ concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true -env: - FORKLIFT_storage_s3_bucketName: ${{ secrets.FORKLIFT_storage_s3_bucketName }} - FORKLIFT_storage_s3_accessKeyId: ${{ secrets.FORKLIFT_storage_s3_accessKeyId }} - FORKLIFT_storage_s3_secretAccessKey: ${{ secrets.FORKLIFT_storage_s3_secretAccessKey }} - FORKLIFT_storage_s3_endpointUrl: ${{ secrets.FORKLIFT_storage_s3_endpointUrl }} - FORKLIFT_metrics_pushEndpoint: ${{ secrets.FORKLIFT_metrics_pushEndpoint }} - jobs: set-image: # GitHub Actions allows using 'env' in a container context. diff --git a/.github/workflows/tests-linux-stable.yml b/.github/workflows/tests-linux-stable.yml index 5fdfabc437fe..6f2ac87c3efb 100644 --- a/.github/workflows/tests-linux-stable.yml +++ b/.github/workflows/tests-linux-stable.yml @@ -12,15 +12,7 @@ concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true -env: - FORKLIFT_storage_s3_bucketName: ${{ secrets.FORKLIFT_storage_s3_bucketName }} - FORKLIFT_storage_s3_accessKeyId: ${{ secrets.FORKLIFT_storage_s3_accessKeyId }} - FORKLIFT_storage_s3_secretAccessKey: ${{ secrets.FORKLIFT_storage_s3_secretAccessKey }} - FORKLIFT_storage_s3_endpointUrl: ${{ secrets.FORKLIFT_storage_s3_endpointUrl }} - FORKLIFT_metrics_pushEndpoint: ${{ secrets.FORKLIFT_metrics_pushEndpoint }} - jobs: - changes: permissions: pull-requests: read @@ -31,7 +23,7 @@ jobs: # However, env variables don't work for forks: https://github.com/orgs/community/discussions/44322 # This workaround sets the container image for each job using 'set-image' job output. needs: changes - if: ${{ needs.changes.outputs.rust }} + if: ${{ needs.changes.outputs.rust }} runs-on: ubuntu-latest outputs: IMAGE: ${{ steps.set_image.outputs.IMAGE }} @@ -40,10 +32,10 @@ jobs: uses: actions/checkout@v4 - id: set_image run: cat .github/env >> $GITHUB_OUTPUT - + test-linux-stable-int: needs: [set-image, changes] - if: ${{ needs.changes.outputs.rust }} + if: ${{ needs.changes.outputs.rust }} runs-on: arc-runners-polkadot-sdk-beefy timeout-minutes: 30 container: @@ -60,11 +52,11 @@ jobs: uses: actions/checkout@v4 - name: script run: WASM_BUILD_NO_COLOR=1 time forklift cargo test -p staging-node-cli --release --locked -- --ignored - + # https://github.com/paritytech/ci_cd/issues/864 test-linux-stable-runtime-benchmarks: needs: [set-image, changes] - if: ${{ needs.changes.outputs.rust }} + if: ${{ needs.changes.outputs.rust }} runs-on: arc-runners-polkadot-sdk-beefy timeout-minutes: 30 container: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 293acadc4e6a..0c1447cba33a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -11,15 +11,7 @@ concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true -env: - FORKLIFT_storage_s3_bucketName: ${{ secrets.FORKLIFT_storage_s3_bucketName }} - FORKLIFT_storage_s3_accessKeyId: ${{ secrets.FORKLIFT_storage_s3_accessKeyId }} - FORKLIFT_storage_s3_secretAccessKey: ${{ secrets.FORKLIFT_storage_s3_secretAccessKey }} - FORKLIFT_storage_s3_endpointUrl: ${{ secrets.FORKLIFT_storage_s3_endpointUrl }} - FORKLIFT_metrics_pushEndpoint: ${{ secrets.FORKLIFT_metrics_pushEndpoint }} - jobs: - changes: permissions: pull-requests: read @@ -40,7 +32,7 @@ jobs: quick-benchmarks: needs: [set-image, changes] - if: ${{ needs.changes.outputs.rust }} + if: ${{ needs.changes.outputs.rust }} runs-on: arc-runners-polkadot-sdk-beefy timeout-minutes: 30 container: @@ -55,11 +47,11 @@ jobs: uses: actions/checkout@v4 - name: script run: time forklift cargo run --locked --release -p staging-node-cli --bin substrate-node --features runtime-benchmarks -- benchmark pallet --chain dev --pallet "*" --extrinsic "*" --steps 2 --repeat 1 --quiet - + # cf https://github.com/paritytech/polkadot-sdk/issues/1652 test-syscalls: needs: [set-image, changes] - if: ${{ needs.changes.outputs.rust }} + if: ${{ needs.changes.outputs.rust }} runs-on: arc-runners-polkadot-sdk-beefy timeout-minutes: 30 container: @@ -81,10 +73,10 @@ jobs: # - if [[ "$CI_JOB_STATUS" == "failed" ]]; then # printf "The x86_64 syscalls used by the worker binaries have changed. Please review if this is expected and update polkadot/scripts/list-syscalls/*-worker-syscalls as needed.\n"; # fi - + cargo-check-all-benches: needs: [set-image, changes] - if: ${{ needs.changes.outputs.rust }} + if: ${{ needs.changes.outputs.rust }} runs-on: arc-runners-polkadot-sdk-beefy timeout-minutes: 30 container: diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 73a8c52c448f..7f2babc6bd47 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -120,7 +120,8 @@ default: .forklift-cache: before_script: - mkdir ~/.forklift - - cp .forklift/config.toml ~/.forklift/config.toml + - cp .forklift/config-gitlab.toml ~/.forklift/config.toml + - cat .forklift/config-gitlab.toml > .forklift/config.toml - > if [ "$FORKLIFT_BYPASS" != "true" ]; then echo "FORKLIFT_BYPASS not set"; diff --git a/Cargo.lock b/Cargo.lock index bbb785a618a8..f86907030f84 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -823,6 +823,7 @@ dependencies = [ "pallet-asset-conversion-ops", "pallet-asset-conversion-tx-payment", "pallet-assets", + "pallet-assets-freezer", "pallet-aura", "pallet-authorship", "pallet-balances", @@ -956,6 +957,7 @@ dependencies = [ "pallet-asset-conversion-ops", "pallet-asset-conversion-tx-payment", "pallet-assets", + "pallet-assets-freezer", "pallet-aura", "pallet-authorship", "pallet-balances", @@ -4536,16 +4538,15 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "4.1.2" +version = "4.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" dependencies = [ "cfg-if", "cpufeatures", "curve25519-dalek-derive", "digest 0.10.7", "fiat-crypto", - "platforms", "rustc_version 0.4.0", "subtle 2.5.0", "zeroize", @@ -4693,9 +4694,12 @@ dependencies = [ [[package]] name = "deranged" -version = "0.3.8" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] [[package]] name = "derivative" @@ -5018,7 +5022,7 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" dependencies = [ - "curve25519-dalek 4.1.2", + "curve25519-dalek 4.1.3", "ed25519 2.2.2", "rand_core 0.6.4", "serde", @@ -5033,7 +5037,7 @@ version = "4.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d9ce6874da5d4415896cd45ffbc4d1cfc0c4f9c079427bd870742c30f2f65a9" dependencies = [ - "curve25519-dalek 4.1.2", + "curve25519-dalek 4.1.3", "ed25519 2.2.2", "hashbrown 0.14.3", "hex", @@ -8479,7 +8483,7 @@ dependencies = [ "bitflags 1.3.2", "blake2 0.10.6", "c2-chacha", - "curve25519-dalek 4.1.2", + "curve25519-dalek 4.1.3", "either", "hashlink", "lioness", @@ -9147,6 +9151,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-derive" version = "0.4.2" @@ -9547,6 +9557,23 @@ dependencies = [ "sp-std 14.0.0", ] +[[package]] +name = "pallet-assets-freezer" +version = "0.1.0" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-assets", + "pallet-balances", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", +] + [[package]] name = "pallet-atomic-swap" version = "28.0.0" @@ -14018,6 +14045,7 @@ dependencies = [ "pallet-asset-rate", "pallet-asset-tx-payment", "pallet-assets", + "pallet-assets-freezer", "pallet-atomic-swap", "pallet-aura", "pallet-authority-discovery", @@ -14993,6 +15021,12 @@ dependencies = [ "rand 0.8.5", ] +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "pprof" version = "0.12.1" @@ -17846,6 +17880,7 @@ dependencies = [ "ip_network", "jsonrpsee", "log", + "serde", "serde_json", "substrate-prometheus-endpoint", "tokio", @@ -18338,7 +18373,7 @@ dependencies = [ "aead", "arrayref", "arrayvec 0.7.4", - "curve25519-dalek 4.1.2", + "curve25519-dalek 4.1.3", "getrandom_or_panic", "merlin", "rand_core 0.6.4", @@ -19084,7 +19119,7 @@ dependencies = [ "aes-gcm", "blake2 0.10.6", "chacha20poly1305", - "curve25519-dalek 4.1.2", + "curve25519-dalek 4.1.3", "rand_core 0.6.4", "ring 0.17.7", "rustc_version 0.4.0", @@ -20355,7 +20390,7 @@ name = "sp-statement-store" version = "10.0.0" dependencies = [ "aes-gcm", - "curve25519-dalek 4.1.2", + "curve25519-dalek 4.1.3", "ed25519-dalek 2.1.1", "hkdf", "parity-scale-codec", @@ -21824,14 +21859,16 @@ dependencies = [ [[package]] name = "time" -version = "0.3.27" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb39ee79a6d8de55f48f2293a830e040392f1c5f16e336bdd1788cd0aadce07" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", "libc", + "num-conv", "num_threads", + "powerfmt", "serde", "time-core", "time-macros", @@ -21839,16 +21876,17 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.13" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "733d258752e9303d392b94b75230d07b0b9c489350c69b851fc6c065fde3e8f9" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ + "num-conv", "time-core", ] @@ -23780,7 +23818,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb66477291e7e8d2b0ff1bcb900bf29489a9692816d79874bea351e7a8b6de96" dependencies = [ - "curve25519-dalek 4.1.2", + "curve25519-dalek 4.1.3", "rand_core 0.6.4", "serde", "zeroize", diff --git a/Cargo.toml b/Cargo.toml index 2b2a1cdc17d5..ba52b0179ff8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -306,6 +306,7 @@ members = [ "substrate/frame/asset-conversion/ops", "substrate/frame/asset-rate", "substrate/frame/assets", + "substrate/frame/assets-freezer", "substrate/frame/atomic-swap", "substrate/frame/aura", "substrate/frame/authority-discovery", diff --git a/bridges/testing/framework/utils/generate_hex_encoded_call/package-lock.json b/bridges/testing/framework/utils/generate_hex_encoded_call/package-lock.json index b2dddaa19ed1..ca3abcc528cf 100644 --- a/bridges/testing/framework/utils/generate_hex_encoded_call/package-lock.json +++ b/bridges/testing/framework/utils/generate_hex_encoded_call/package-lock.json @@ -736,9 +736,9 @@ } }, "node_modules/ws": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", - "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", "engines": { "node": ">=10.0.0" }, diff --git a/cumulus/pallets/xcmp-queue/src/lib.rs b/cumulus/pallets/xcmp-queue/src/lib.rs index 5633f05f13bb..45126a9425d4 100644 --- a/cumulus/pallets/xcmp-queue/src/lib.rs +++ b/cumulus/pallets/xcmp-queue/src/lib.rs @@ -491,7 +491,7 @@ impl Pallet { let channel_info = T::ChannelInfo::get_channel_info(recipient).ok_or(MessageSendError::NoChannel)?; // Max message size refers to aggregates, or pages. Not to individual fragments. - let max_message_size = channel_info.max_message_size as usize; + let max_message_size = channel_info.max_message_size.min(T::MaxPageSize::get()) as usize; let format_size = format.encoded_size(); // We check the encoded fragment length plus the format size against the max message size // because the format is concatenated if a new page is needed. @@ -522,7 +522,7 @@ impl Pallet { // We return the size of the last page inside of the option, to not calculate it again. let appended_to_last_page = have_active .then(|| { - >::mutate( + >::try_mutate( recipient, channel_details.last_index - 1, |page| { @@ -532,17 +532,18 @@ impl Pallet { ) != Ok(format) { defensive!("Bad format in outbound queue; dropping message"); - return None + return Err(()) } if page.len() + encoded_fragment.len() > max_message_size { - return None + return Err(()) } for frag in encoded_fragment.iter() { - page.try_push(*frag).ok()?; + page.try_push(*frag)?; } - Some(page.len()) + Ok(page.len()) }, ) + .ok() }) .flatten(); diff --git a/cumulus/pallets/xcmp-queue/src/tests.rs b/cumulus/pallets/xcmp-queue/src/tests.rs index cdf41e27f0b2..5b02baf2310a 100644 --- a/cumulus/pallets/xcmp-queue/src/tests.rs +++ b/cumulus/pallets/xcmp-queue/src/tests.rs @@ -28,6 +28,7 @@ use frame_support::{ use mock::{new_test_ext, ParachainSystem, RuntimeOrigin as Origin, Test, XcmpQueue}; use sp_runtime::traits::{BadOrigin, Zero}; use std::iter::{once, repeat}; +use xcm_builder::InspectMessageQueues; #[test] fn empty_concatenated_works() { @@ -854,7 +855,6 @@ fn verify_fee_factor_increase_and_decrease() { #[test] fn get_messages_works() { new_test_ext().execute_with(|| { - use xcm_builder::InspectMessageQueues; let sibling_para_id = ParaId::from(2001); ParachainSystem::open_outbound_hrmp_channel_for_benchmarks_or_tests(sibling_para_id); let destination: Location = (Parent, Parachain(sibling_para_id.into())).into(); @@ -890,3 +890,32 @@ fn get_messages_works() { ); }); } + +/// We try to send a fragment that will not fit into the currently active page. This should +/// therefore not modify the current page but instead create a new one. +#[test] +fn page_not_modified_when_fragment_does_not_fit() { + new_test_ext().execute_with(|| { + let sibling = ParaId::from(2001); + ParachainSystem::open_outbound_hrmp_channel_for_benchmarks_or_tests(sibling); + + let destination: Location = (Parent, Parachain(sibling.into())).into(); + let message = Xcm(vec![ClearOrigin; 600]); + + loop { + let old_page_zero = OutboundXcmpMessages::::get(sibling, 0); + assert_ok!(send_xcm::(destination.clone(), message.clone())); + + // If a new page was created by this send_xcm call, then page_zero was not also + // modified: + let num_pages = OutboundXcmpMessages::::iter_prefix(sibling).count(); + if num_pages == 2 { + let new_page_zero = OutboundXcmpMessages::::get(sibling, 0); + assert_eq!(old_page_zero, new_page_zero); + break + } else if num_pages > 2 { + panic!("Too many pages created"); + } + } + }); +} diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml b/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml index a880730ddacf..b8670c2957d8 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml @@ -28,6 +28,7 @@ pallet-asset-conversion-tx-payment = { path = "../../../../../substrate/frame/tr pallet-assets = { path = "../../../../../substrate/frame/assets", default-features = false } pallet-asset-conversion-ops = { path = "../../../../../substrate/frame/asset-conversion/ops", default-features = false } pallet-asset-conversion = { path = "../../../../../substrate/frame/asset-conversion", default-features = false } +pallet-assets-freezer = { path = "../../../../../substrate/frame/assets-freezer", default-features = false } pallet-aura = { path = "../../../../../substrate/frame/aura", default-features = false } pallet-authorship = { path = "../../../../../substrate/frame/authorship", default-features = false } pallet-balances = { path = "../../../../../substrate/frame/balances", default-features = false } @@ -116,6 +117,7 @@ runtime-benchmarks = [ "frame-system/runtime-benchmarks", "pallet-asset-conversion-ops/runtime-benchmarks", "pallet-asset-conversion/runtime-benchmarks", + "pallet-assets-freezer/runtime-benchmarks", "pallet-assets/runtime-benchmarks", "pallet-balances/runtime-benchmarks", "pallet-collator-selection/runtime-benchmarks", @@ -151,6 +153,7 @@ try-runtime = [ "pallet-asset-conversion-ops/try-runtime", "pallet-asset-conversion-tx-payment/try-runtime", "pallet-asset-conversion/try-runtime", + "pallet-assets-freezer/try-runtime", "pallet-assets/try-runtime", "pallet-aura/try-runtime", "pallet-authorship/try-runtime", @@ -200,6 +203,7 @@ std = [ "pallet-asset-conversion-ops/std", "pallet-asset-conversion-tx-payment/std", "pallet-asset-conversion/std", + "pallet-assets-freezer/std", "pallet-assets/std", "pallet-aura/std", "pallet-authorship/std", diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs index 5bba1b568d9d..423f9549edcc 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs @@ -257,7 +257,7 @@ impl pallet_assets::Config for Runtime { type MetadataDepositPerByte = MetadataDepositPerByte; type ApprovalDeposit = ApprovalDeposit; type StringLimit = AssetsStringLimit; - type Freezer = (); + type Freezer = AssetsFreezer; type Extra = (); type WeightInfo = weights::pallet_assets_local::WeightInfo; type CallbackHandle = (); @@ -267,6 +267,13 @@ impl pallet_assets::Config for Runtime { type BenchmarkHelper = (); } +// Allow Freezes for the `Assets` pallet +pub type AssetsFreezerInstance = pallet_assets_freezer::Instance1; +impl pallet_assets_freezer::Config for Runtime { + type RuntimeFreezeReason = RuntimeFreezeReason; + type RuntimeEvent = RuntimeEvent; +} + parameter_types! { pub const AssetConversionPalletId: PalletId = PalletId(*b"py/ascon"); pub const LiquidityWithdrawalFee: Permill = Permill::from_percent(0); @@ -295,7 +302,7 @@ impl pallet_assets::Config for Runtime { type MetadataDepositPerByte = ConstU128<0>; type ApprovalDeposit = ApprovalDeposit; type StringLimit = ConstU32<50>; - type Freezer = (); + type Freezer = PoolAssetsFreezer; type Extra = (); type WeightInfo = weights::pallet_assets_pool::WeightInfo; type CallbackHandle = (); @@ -303,6 +310,13 @@ impl pallet_assets::Config for Runtime { type BenchmarkHelper = (); } +// Allow Freezes for the `PoolAssets` pallet +pub type PoolAssetsFreezerInstance = pallet_assets_freezer::Instance3; +impl pallet_assets_freezer::Config for Runtime { + type RuntimeFreezeReason = RuntimeFreezeReason; + type RuntimeEvent = RuntimeEvent; +} + /// Union fungibles implementation for `Assets` and `ForeignAssets`. pub type LocalAndForeignAssets = fungibles::UnionOf< Assets, @@ -411,7 +425,7 @@ impl pallet_assets::Config for Runtime { type MetadataDepositPerByte = ForeignAssetsMetadataDepositPerByte; type ApprovalDeposit = ForeignAssetsApprovalDeposit; type StringLimit = ForeignAssetsAssetsStringLimit; - type Freezer = (); + type Freezer = ForeignAssetsFreezer; type Extra = (); type WeightInfo = weights::pallet_assets_foreign::WeightInfo; type CallbackHandle = (); @@ -421,6 +435,13 @@ impl pallet_assets::Config for Runtime { type BenchmarkHelper = xcm_config::XcmBenchmarkHelper; } +// Allow Freezes for the `ForeignAssets` pallet +pub type ForeignAssetsFreezerInstance = pallet_assets_freezer::Instance2; +impl pallet_assets_freezer::Config for Runtime { + type RuntimeFreezeReason = RuntimeFreezeReason; + type RuntimeEvent = RuntimeEvent; +} + parameter_types! { // One storage item; key size is 32; value is size 4+4+16+32 bytes = 56 bytes. pub const DepositBase: Balance = deposit(1, 88); @@ -953,6 +974,9 @@ construct_runtime!( NftFractionalization: pallet_nft_fractionalization = 54, PoolAssets: pallet_assets:: = 55, AssetConversion: pallet_asset_conversion = 56, + AssetsFreezer: pallet_assets_freezer:: = 57, + ForeignAssetsFreezer: pallet_assets_freezer:: = 58, + PoolAssetsFreezer: pallet_assets_freezer:: = 59, // TODO: the pallet instance should be removed once all pools have migrated // to the new account IDs. diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml b/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml index 953f6a8b4009..f310b2e03133 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml @@ -28,6 +28,7 @@ pallet-asset-conversion-ops = { path = "../../../../../substrate/frame/asset-con pallet-asset-conversion-tx-payment = { path = "../../../../../substrate/frame/transaction-payment/asset-conversion-tx-payment", default-features = false } pallet-assets = { path = "../../../../../substrate/frame/assets", default-features = false } pallet-asset-conversion = { path = "../../../../../substrate/frame/asset-conversion", default-features = false } +pallet-assets-freezer = { path = "../../../../../substrate/frame/assets-freezer", default-features = false } pallet-aura = { path = "../../../../../substrate/frame/aura", default-features = false } pallet-authorship = { path = "../../../../../substrate/frame/authorship", default-features = false } pallet-balances = { path = "../../../../../substrate/frame/balances", default-features = false } @@ -115,6 +116,7 @@ runtime-benchmarks = [ "frame-system/runtime-benchmarks", "pallet-asset-conversion-ops/runtime-benchmarks", "pallet-asset-conversion/runtime-benchmarks", + "pallet-assets-freezer/runtime-benchmarks", "pallet-assets/runtime-benchmarks", "pallet-balances/runtime-benchmarks", "pallet-collator-selection/runtime-benchmarks", @@ -150,6 +152,7 @@ try-runtime = [ "pallet-asset-conversion-ops/try-runtime", "pallet-asset-conversion-tx-payment/try-runtime", "pallet-asset-conversion/try-runtime", + "pallet-assets-freezer/try-runtime", "pallet-assets/try-runtime", "pallet-aura/try-runtime", "pallet-authorship/try-runtime", @@ -200,6 +203,7 @@ std = [ "pallet-asset-conversion-ops/std", "pallet-asset-conversion-tx-payment/std", "pallet-asset-conversion/std", + "pallet-assets-freezer/std", "pallet-assets/std", "pallet-aura/std", "pallet-authorship/std", diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs index dcf9565f3300..6764242cc339 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -255,7 +255,7 @@ impl pallet_assets::Config for Runtime { type MetadataDepositPerByte = MetadataDepositPerByte; type ApprovalDeposit = ApprovalDeposit; type StringLimit = AssetsStringLimit; - type Freezer = (); + type Freezer = AssetsFreezer; type Extra = (); type WeightInfo = weights::pallet_assets_local::WeightInfo; type CallbackHandle = (); @@ -265,6 +265,13 @@ impl pallet_assets::Config for Runtime { type BenchmarkHelper = (); } +// Allow Freezes for the `Assets` pallet +pub type AssetsFreezerInstance = pallet_assets_freezer::Instance1; +impl pallet_assets_freezer::Config for Runtime { + type RuntimeFreezeReason = RuntimeFreezeReason; + type RuntimeEvent = RuntimeEvent; +} + parameter_types! { pub const AssetConversionPalletId: PalletId = PalletId(*b"py/ascon"); pub const LiquidityWithdrawalFee: Permill = Permill::from_percent(0); @@ -292,7 +299,7 @@ impl pallet_assets::Config for Runtime { type MetadataDepositPerByte = ConstU128<0>; type ApprovalDeposit = ConstU128<0>; type StringLimit = ConstU32<50>; - type Freezer = (); + type Freezer = PoolAssetsFreezer; type Extra = (); type WeightInfo = weights::pallet_assets_pool::WeightInfo; type CallbackHandle = (); @@ -300,6 +307,13 @@ impl pallet_assets::Config for Runtime { type BenchmarkHelper = (); } +// Allow Freezes for the `PoolAssets` pallet +pub type PoolAssetsFreezerInstance = pallet_assets_freezer::Instance3; +impl pallet_assets_freezer::Config for Runtime { + type RuntimeFreezeReason = RuntimeFreezeReason; + type RuntimeEvent = RuntimeEvent; +} + /// Union fungibles implementation for `Assets` and `ForeignAssets`. pub type LocalAndForeignAssets = fungibles::UnionOf< Assets, @@ -405,7 +419,7 @@ impl pallet_assets::Config for Runtime { type MetadataDepositPerByte = ForeignAssetsMetadataDepositPerByte; type ApprovalDeposit = ForeignAssetsApprovalDeposit; type StringLimit = ForeignAssetsAssetsStringLimit; - type Freezer = (); + type Freezer = ForeignAssetsFreezer; type Extra = (); type WeightInfo = weights::pallet_assets_foreign::WeightInfo; type CallbackHandle = (); @@ -415,6 +429,13 @@ impl pallet_assets::Config for Runtime { type BenchmarkHelper = xcm_config::XcmBenchmarkHelper; } +// Allow Freezes for the `ForeignAssets` pallet +pub type ForeignAssetsFreezerInstance = pallet_assets_freezer::Instance2; +impl pallet_assets_freezer::Config for Runtime { + type RuntimeFreezeReason = RuntimeFreezeReason; + type RuntimeEvent = RuntimeEvent; +} + parameter_types! { // One storage item; key size is 32; value is size 4+4+16+32 bytes = 56 bytes. pub const DepositBase: Balance = deposit(1, 88); @@ -943,6 +964,9 @@ construct_runtime!( NftFractionalization: pallet_nft_fractionalization = 54, PoolAssets: pallet_assets:: = 55, AssetConversion: pallet_asset_conversion = 56, + AssetsFreezer: pallet_assets_freezer:: = 57, + ForeignAssetsFreezer: pallet_assets_freezer:: = 58, + PoolAssetsFreezer: pallet_assets_freezer:: = 59, StateTrieMigration: pallet_state_trie_migration = 70, diff --git a/docker/dockerfiles/bridges_zombienet_tests_injected.Dockerfile b/docker/dockerfiles/bridges_zombienet_tests_injected.Dockerfile index 196ba861f503..e17952ccee80 100644 --- a/docker/dockerfiles/bridges_zombienet_tests_injected.Dockerfile +++ b/docker/dockerfiles/bridges_zombienet_tests_injected.Dockerfile @@ -1,7 +1,7 @@ # this image is built on top of existing Zombienet image ARG ZOMBIENET_IMAGE # this image uses substrate-relay image built elsewhere -ARG SUBSTRATE_RELAY_IMAGE=docker.io/paritytech/substrate-relay:v1.5.0 +ARG SUBSTRATE_RELAY_IMAGE=docker.io/paritytech/substrate-relay:v1.6.4 # metadata ARG VCS_REF diff --git a/prdoc/pr_3951.prdoc b/prdoc/pr_3951.prdoc new file mode 100644 index 000000000000..3a8096e6f448 --- /dev/null +++ b/prdoc/pr_3951.prdoc @@ -0,0 +1,30 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Pallet Assets Freezer + +doc: + - audience: Runtime Dev + description: | + This pallet is an extension of `pallet-assets`, supporting + freezes similar to `pallet-balances`. + To use this pallet, set `Freezer` of `pallet-assets` Config to the according instance of + `pallet-assets-freezer`. + - audience: Runtime User + description: | + The storage of this pallet contains a Vecs of account freezes. Applications UIs and Developer + Tools might benefit from observing it. + +crates: + - name: frame-support + bump: minor + - name: pallet-assets-freezer + bump: major + - name: pallet-assets + bump: patch + - name: pallet-balances + bump: patch + - name: asset-hub-rococo-runtime + bump: minor + - name: asset-hub-westend-runtime + bump: minor diff --git a/prdoc/pr_4596.prdoc b/prdoc/pr_4596.prdoc new file mode 100644 index 000000000000..d47aa3aedfb8 --- /dev/null +++ b/prdoc/pr_4596.prdoc @@ -0,0 +1,18 @@ +title: "Frame: `Consideration` trait generic over `Footprint` and handles zero cost" + +doc: + - audience: Runtime Dev + description: | + `Consideration` trait generic over `Footprint` and can handle zero cost for a give footprint. + + `Consideration` trait is generic over `Footprint` (currently defined over the type with the same name). This makes it possible to setup a custom footprint (e.g. current number of proposals in the storage). + + `Consideration::new` and `Consideration::update` return an `Option` instead `Self`, this make it possible to define no cost for a specific footprint (e.g. current number of proposals in the storage < max_proposal_count / 2). + +crates: + - name: frame-support + bump: major + - name: pallet-preimage + bump: major + - name: pallet-balances + bump: patch diff --git a/prdoc/pr_4710.prdoc b/prdoc/pr_4710.prdoc new file mode 100644 index 000000000000..d7d31d817208 --- /dev/null +++ b/prdoc/pr_4710.prdoc @@ -0,0 +1,11 @@ +title: "Dont partially modify HRMP pages" + +doc: + - audience: Runtime Dev + description: | + The xcmp-queue pallet now does not partially modify a page anymore when the next message does + not fully fit into it but instead cleanly creates a new one. + +crates: + - name: cumulus-pallet-xcmp-queue + bump: patch diff --git a/prdoc/pr_4756.prdoc b/prdoc/pr_4756.prdoc new file mode 100644 index 000000000000..064a79fb0664 --- /dev/null +++ b/prdoc/pr_4756.prdoc @@ -0,0 +1,15 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Do not make pallet-nfts benchmarks signature-dependent + +doc: + - audience: Runtime Dev + description: | + - Adds extra functionality to pallet-nfts's BenchmarkHelper to provide signers and sign message. + - Abstracts away the explicit link with Sr25519 schema in the benchmarks, allowing parachains with a different one to be able to run them and calculate the weights. + - Adds a default implementation for the empty tuple that leaves the code equivalent. + +crates: + - name: pallet-nfts + bump: minor diff --git a/prdoc/pr_4802.prdoc b/prdoc/pr_4802.prdoc new file mode 100644 index 000000000000..5757c4cbae18 --- /dev/null +++ b/prdoc/pr_4802.prdoc @@ -0,0 +1,16 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Add `health/readiness endpoint` to the rpc server + +doc: + - audience: Node Operator + description: | + Add `/health/readiness endpoint` to the rpc server which returns HTTP status code 200 if the chain is synced + and can connect to the rest of the network otherwise status code 500 is returned. + The endpoint can be reached by performing a HTTP GET request to the + endpoint such as `$ curl /health/readiness` + +crates: + - name: sc-rpc-server + bump: patch diff --git a/substrate/client/cli/src/params/pruning_params.rs b/substrate/client/cli/src/params/pruning_params.rs index 88ae006c638e..6b7b0e7ffa99 100644 --- a/substrate/client/cli/src/params/pruning_params.rs +++ b/substrate/client/cli/src/params/pruning_params.rs @@ -17,7 +17,7 @@ // along with this program. If not, see . use crate::error; -use clap::{builder::PossibleValue, Args, ValueEnum}; +use clap::Args; use sc_service::{BlocksPruning, PruningMode}; /// Parameters to define the pruning mode @@ -30,23 +30,38 @@ pub struct PruningParams { /// This setting can only be set on the first creation of the database. Every subsequent run /// will load the pruning mode from the database and will error if the stored mode doesn't /// match this CLI value. It is fine to drop this CLI flag for subsequent runs. The only - /// exception is that `` can change between subsequent runs (increasing it will not + /// exception is that `NUMBER` can change between subsequent runs (increasing it will not /// lead to restoring pruned state). /// + /// Possible values: + /// + /// - archive: Keep the data of all blocks. + /// + /// - archive-canonical: Keep only the data of finalized blocks. + /// + /// - NUMBER: Keep the data of the last NUMBER of finalized blocks. + /// /// [default: 256] - #[arg(alias = "pruning", long, value_name = "PRUNING_MODE", value_enum)] + #[arg(alias = "pruning", long, value_name = "PRUNING_MODE")] pub state_pruning: Option, /// Specify the blocks pruning mode. /// /// This mode specifies when the block's body (including justifications) /// should be pruned (ie, removed) from the database. + /// + /// Possible values: + /// + /// - archive: Keep the data of all blocks. + /// + /// - archive-canonical: Keep only the data of finalized blocks. + /// + /// - NUMBER: Keep the data of the last NUMBER of finalized blocks. #[arg( alias = "keep-blocks", long, value_name = "PRUNING_MODE", - default_value_t = DatabasePruningMode::ArchiveCanonical, - value_enum + default_value = "archive-canonical" )] pub blocks_pruning: DatabasePruningMode, } @@ -78,26 +93,6 @@ pub enum DatabasePruningMode { Custom(u32), } -impl ValueEnum for DatabasePruningMode { - fn value_variants<'a>() -> &'a [Self] { - &[Self::Archive, Self::ArchiveCanonical, Self::Custom(0)] - } - - fn to_possible_value(&self) -> Option { - Some(match self { - Self::Archive => PossibleValue::new("archive").help("Keep the data of all blocks."), - Self::ArchiveCanonical => PossibleValue::new("archive-canonical") - .help("Keep only the data of finalized blocks."), - Self::Custom(_) => PossibleValue::new("") - .help("Keep the data of the last of finalized blocks."), - }) - } - - fn from_str(input: &str, _: bool) -> Result { - ::from_str(input) - } -} - impl std::str::FromStr for DatabasePruningMode { type Err = String; @@ -132,3 +127,39 @@ impl Into for DatabasePruningMode { } } } + +#[cfg(test)] +mod tests { + use super::*; + use clap::Parser; + + #[derive(Parser)] + struct Cli { + #[clap(flatten)] + pruning: PruningParams, + } + + #[test] + fn pruning_params_parse_works() { + let Cli { pruning } = + Cli::parse_from(["", "--state-pruning=1000", "--blocks-pruning=1000"]); + + assert!(matches!(pruning.state_pruning, Some(DatabasePruningMode::Custom(1000)))); + assert!(matches!(pruning.blocks_pruning, DatabasePruningMode::Custom(1000))); + + let Cli { pruning } = + Cli::parse_from(["", "--state-pruning=archive", "--blocks-pruning=archive"]); + + assert!(matches!(dbg!(pruning.state_pruning), Some(DatabasePruningMode::Archive))); + assert!(matches!(pruning.blocks_pruning, DatabasePruningMode::Archive)); + + let Cli { pruning } = Cli::parse_from([ + "", + "--state-pruning=archive-canonical", + "--blocks-pruning=archive-canonical", + ]); + + assert!(matches!(dbg!(pruning.state_pruning), Some(DatabasePruningMode::ArchiveCanonical))); + assert!(matches!(pruning.blocks_pruning, DatabasePruningMode::ArchiveCanonical)); + } +} diff --git a/substrate/client/network/types/src/multiaddr.rs b/substrate/client/network/types/src/multiaddr.rs index 312bef9baab1..de317e2aa1cf 100644 --- a/substrate/client/network/types/src/multiaddr.rs +++ b/substrate/client/network/types/src/multiaddr.rs @@ -22,6 +22,7 @@ use litep2p::types::multiaddr::{ }; use std::{ fmt::{self, Debug, Display}, + net::{IpAddr, Ipv4Addr, Ipv6Addr}, str::FromStr, }; @@ -102,6 +103,27 @@ impl From for LiteP2pMultiaddr { } } +impl From for Multiaddr { + fn from(v: IpAddr) -> Multiaddr { + match v { + IpAddr::V4(a) => a.into(), + IpAddr::V6(a) => a.into(), + } + } +} + +impl From for Multiaddr { + fn from(v: Ipv4Addr) -> Multiaddr { + Protocol::Ip4(v).into() + } +} + +impl From for Multiaddr { + fn from(v: Ipv6Addr) -> Multiaddr { + Protocol::Ip6(v).into() + } +} + impl TryFrom> for Multiaddr { type Error = ParseError; diff --git a/substrate/client/network/types/src/multiaddr/protocol.rs b/substrate/client/network/types/src/multiaddr/protocol.rs index 800d08fe36bd..2a3b59e6a682 100644 --- a/substrate/client/network/types/src/multiaddr/protocol.rs +++ b/substrate/client/network/types/src/multiaddr/protocol.rs @@ -20,7 +20,7 @@ use crate::multihash::Multihash; use litep2p::types::multiaddr::Protocol as LiteP2pProtocol; use std::{ borrow::Cow, - net::{Ipv4Addr, Ipv6Addr}, + net::{IpAddr, Ipv4Addr, Ipv6Addr}, }; /// [`Protocol`] describes all possible multiaddress protocols. @@ -60,6 +60,30 @@ pub enum Protocol<'a> { Wss(Cow<'a, str>), } +impl<'a> From for Protocol<'a> { + #[inline] + fn from(addr: IpAddr) -> Self { + match addr { + IpAddr::V4(addr) => Protocol::Ip4(addr), + IpAddr::V6(addr) => Protocol::Ip6(addr), + } + } +} + +impl<'a> From for Protocol<'a> { + #[inline] + fn from(addr: Ipv4Addr) -> Self { + Protocol::Ip4(addr) + } +} + +impl<'a> From for Protocol<'a> { + #[inline] + fn from(addr: Ipv6Addr) -> Self { + Protocol::Ip6(addr) + } +} + impl<'a> From> for Protocol<'a> { fn from(protocol: LiteP2pProtocol<'a>) -> Self { match protocol { diff --git a/substrate/client/rpc-servers/Cargo.toml b/substrate/client/rpc-servers/Cargo.toml index 7837c852a1c9..19369e295fc4 100644 --- a/substrate/client/rpc-servers/Cargo.toml +++ b/substrate/client/rpc-servers/Cargo.toml @@ -25,6 +25,7 @@ ip_network = "0.4.1" jsonrpsee = { version = "0.22", features = ["server"] } log = { workspace = true, default-features = true } prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../utils/prometheus" } +serde = { workspace = true } serde_json = { workspace = true, default-features = true } tokio = { version = "1.22.0", features = ["parking_lot"] } tower = { version = "0.4.13", features = ["util"] } diff --git a/substrate/client/rpc-servers/src/lib.rs b/substrate/client/rpc-servers/src/lib.rs index ba1fcf5e3677..647a7ca7e435 100644 --- a/substrate/client/rpc-servers/src/lib.rs +++ b/substrate/client/rpc-servers/src/lib.rs @@ -32,12 +32,10 @@ use hyper::{ service::{make_service_fn, service_fn}, }; use jsonrpsee::{ - server::{ - middleware::http::ProxyGetRequestLayer, stop_channel, ws, PingConfig, StopHandle, - TowerServiceBuilder, - }, + server::{stop_channel, ws, PingConfig, StopHandle, TowerServiceBuilder}, Methods, RpcModule, }; +use middleware::NodeHealthProxyLayer; use tokio::net::TcpListener; use tower::Service; use utils::{build_rpc_api, format_cors, get_proxy_ip, host_filtering, try_into_cors}; @@ -132,8 +130,8 @@ where let http_middleware = tower::ServiceBuilder::new() .option_layer(host_filter) - // Proxy `GET /health` requests to internal `system_health` method. - .layer(ProxyGetRequestLayer::new("/health", "system_health")?) + // Proxy `GET /health, /health/readiness` requests to the internal `system_health` method. + .layer(NodeHealthProxyLayer::default()) .layer(try_into_cors(cors)?); let mut builder = jsonrpsee::server::Server::builder() diff --git a/substrate/client/rpc-servers/src/middleware/mod.rs b/substrate/client/rpc-servers/src/middleware/mod.rs index 88ed8b2f4335..0a14be4dacf5 100644 --- a/substrate/client/rpc-servers/src/middleware/mod.rs +++ b/substrate/client/rpc-servers/src/middleware/mod.rs @@ -32,9 +32,11 @@ use jsonrpsee::{ }; mod metrics; +mod node_health; mod rate_limit; pub use metrics::*; +pub use node_health::*; pub use rate_limit::*; const MAX_JITTER: Duration = Duration::from_millis(50); diff --git a/substrate/client/rpc-servers/src/middleware/node_health.rs b/substrate/client/rpc-servers/src/middleware/node_health.rs new file mode 100644 index 000000000000..d68ec14cb8fe --- /dev/null +++ b/substrate/client/rpc-servers/src/middleware/node_health.rs @@ -0,0 +1,199 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! Middleware for handling `/health` and `/health/readiness` endpoints. + +use std::{ + error::Error, + future::Future, + pin::Pin, + task::{Context, Poll}, +}; + +use futures::future::FutureExt; +use http::{HeaderValue, Method, StatusCode, Uri}; +use hyper::Body; +use jsonrpsee::types::{Response as RpcResponse, ResponseSuccess as RpcResponseSuccess}; +use tower::Service; + +const RPC_SYSTEM_HEALTH_CALL: &str = r#"{"jsonrpc":"2.0","method":"system_health","id":0}"#; +const HEADER_VALUE_JSON: HeaderValue = HeaderValue::from_static("application/json; charset=utf-8"); + +/// Layer that applies [`NodeHealthProxy`] which +/// proxies `/health` and `/health/readiness` endpoints. +#[derive(Debug, Clone, Default)] +pub struct NodeHealthProxyLayer; + +impl tower::Layer for NodeHealthProxyLayer { + type Service = NodeHealthProxy; + + fn layer(&self, service: S) -> Self::Service { + NodeHealthProxy::new(service) + } +} + +/// Middleware that proxies `/health` and `/health/readiness` endpoints. +pub struct NodeHealthProxy(S); + +impl NodeHealthProxy { + /// Creates a new [`NodeHealthProxy`]. + pub fn new(service: S) -> Self { + Self(service) + } +} + +impl tower::Service> for NodeHealthProxy +where + S: Service, Response = http::Response>, + S::Response: 'static, + S::Error: Into> + 'static, + S::Future: Send + 'static, +{ + type Response = S::Response; + type Error = Box; + type Future = + Pin> + Send + 'static>>; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + self.0.poll_ready(cx).map_err(Into::into) + } + + fn call(&mut self, mut req: http::Request) -> Self::Future { + let maybe_intercept = InterceptRequest::from_http(&req); + + // Modify the request and proxy it to `system_health` + if let InterceptRequest::Health | InterceptRequest::Readiness = maybe_intercept { + // RPC methods are accessed with `POST`. + *req.method_mut() = Method::POST; + // Precautionary remove the URI. + *req.uri_mut() = Uri::from_static("/"); + + // Requests must have the following headers: + req.headers_mut().insert(http::header::CONTENT_TYPE, HEADER_VALUE_JSON); + req.headers_mut().insert(http::header::ACCEPT, HEADER_VALUE_JSON); + + // Adjust the body to reflect the method call. + req = req.map(|_| Body::from(RPC_SYSTEM_HEALTH_CALL)); + } + + // Call the inner service and get a future that resolves to the response. + let fut = self.0.call(req); + + async move { + let res = fut.await.map_err(|err| err.into())?; + + Ok(match maybe_intercept { + InterceptRequest::Deny => + http_response(StatusCode::METHOD_NOT_ALLOWED, Body::empty()), + InterceptRequest::No => res, + InterceptRequest::Health => { + let health = parse_rpc_response(res.into_body()).await?; + http_ok_response(serde_json::to_string(&health)?) + }, + InterceptRequest::Readiness => { + let health = parse_rpc_response(res.into_body()).await?; + if (!health.is_syncing && health.peers > 0) || !health.should_have_peers { + http_ok_response(Body::empty()) + } else { + http_internal_error() + } + }, + }) + } + .boxed() + } +} + +// NOTE: This is duplicated here to avoid dependency to the `RPC API`. +#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)] +#[serde(rename_all = "camelCase")] +struct Health { + /// Number of connected peers + pub peers: usize, + /// Is the node syncing + pub is_syncing: bool, + /// Should this node have any peers + /// + /// Might be false for local chains or when running without discovery. + pub should_have_peers: bool, +} + +fn http_ok_response>(body: S) -> hyper::Response { + http_response(StatusCode::OK, body) +} + +fn http_response>( + status_code: StatusCode, + body: S, +) -> hyper::Response { + hyper::Response::builder() + .status(status_code) + .header(http::header::CONTENT_TYPE, HEADER_VALUE_JSON) + .body(body.into()) + .expect("Header is valid; qed") +} + +fn http_internal_error() -> hyper::Response { + http_response(hyper::StatusCode::INTERNAL_SERVER_ERROR, Body::empty()) +} + +async fn parse_rpc_response(body: Body) -> Result> { + let bytes = hyper::body::to_bytes(body).await?; + + let raw_rp = serde_json::from_slice::>(&bytes)?; + let rp = RpcResponseSuccess::::try_from(raw_rp)?; + + Ok(rp.result) +} + +/// Whether the request should be treated as ordinary RPC call or be modified. +enum InterceptRequest { + /// Proxy `/health` to `system_health`. + Health, + /// Checks if node has at least one peer and is not doing major syncing. + /// + /// Returns HTTP status code 200 on success otherwise HTTP status code 500 is returned. + Readiness, + /// Treat as a ordinary RPC call and don't modify the request or response. + No, + /// Deny health or readiness calls that is not HTTP GET request. + /// + /// Returns HTTP status code 405. + Deny, +} + +impl InterceptRequest { + fn from_http(req: &http::Request) -> InterceptRequest { + match req.uri().path() { + "/health" => + if req.method() == http::Method::GET { + InterceptRequest::Health + } else { + InterceptRequest::Deny + }, + "/health/readiness" => + if req.method() == http::Method::GET { + InterceptRequest::Readiness + } else { + InterceptRequest::Deny + }, + // Forward all other requests to the RPC server. + _ => InterceptRequest::No, + } + } +} diff --git a/substrate/frame/assets-freezer/Cargo.toml b/substrate/frame/assets-freezer/Cargo.toml new file mode 100644 index 000000000000..d7b5d2d40c4e --- /dev/null +++ b/substrate/frame/assets-freezer/Cargo.toml @@ -0,0 +1,61 @@ +[package] +name = "pallet-assets-freezer" +version = "0.1.0" +authors.workspace = true +edition.workspace = true +license = "MIT-0" +homepage = "https://substrate.io" +repository.workspace = true +description = "Provides freezing features to `pallet-assets`" + +[lints] +workspace = true + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } +log = { workspace = true } +scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } +frame-benchmarking = { path = "../benchmarking", default-features = false, optional = true } +frame-support = { path = "../support", default-features = false } +frame-system = { path = "../system", default-features = false } +pallet-assets = { path = "../assets", default-features = false } +sp-runtime = { path = "../../primitives/runtime", default-features = false } + +[dev-dependencies] +sp-io = { path = "../../primitives/io", default-features = false } +sp-core = { path = "../../primitives/core", default-features = false } +pallet-balances = { path = "../balances", default-features = false } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "log/std", + "pallet-assets/std", + "pallet-balances/std", + "scale-info/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", +] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-assets/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", + "pallet-assets/try-runtime", + "pallet-balances/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/substrate/frame/assets-freezer/src/impls.rs b/substrate/frame/assets-freezer/src/impls.rs new file mode 100644 index 000000000000..cd383f1c3cd1 --- /dev/null +++ b/substrate/frame/assets-freezer/src/impls.rs @@ -0,0 +1,158 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::*; + +use frame_support::traits::{ + fungibles::{Inspect, InspectFreeze, MutateFreeze}, + tokens::{DepositConsequence, Fortitude, Preservation, Provenance, WithdrawConsequence}, +}; +use pallet_assets::FrozenBalance; +use sp_runtime::traits::Zero; + +// Implements [`FrozenBalance`] from [`pallet-assets`], so it can understand how much of an +// account balance is frozen, and is able to signal to this pallet when to clear the state of an +// account. +impl, I: 'static> FrozenBalance + for Pallet +{ + fn frozen_balance(asset: T::AssetId, who: &T::AccountId) -> Option { + FrozenBalances::::get(asset, who) + } + + fn died(asset: T::AssetId, who: &T::AccountId) { + FrozenBalances::::remove(asset.clone(), who); + Freezes::::remove(asset, who); + } +} + +// Implement [`fungibles::Inspect`](frame_support::traits::fungibles::Inspect) as it is bound by +// [`fungibles::InspectFreeze`](frame_support::traits::fungibles::InspectFreeze) and +// [`fungibles::MutateFreeze`](frame_support::traits::fungibles::MutateFreeze). To do so, we'll +// re-export all of `pallet-assets` implementation of the same trait. +impl, I: 'static> Inspect for Pallet { + type AssetId = T::AssetId; + type Balance = T::Balance; + + fn total_issuance(asset: Self::AssetId) -> Self::Balance { + pallet_assets::Pallet::::total_issuance(asset) + } + + fn minimum_balance(asset: Self::AssetId) -> Self::Balance { + pallet_assets::Pallet::::minimum_balance(asset) + } + + fn total_balance(asset: Self::AssetId, who: &T::AccountId) -> Self::Balance { + pallet_assets::Pallet::::total_balance(asset, who) + } + + fn balance(asset: Self::AssetId, who: &T::AccountId) -> Self::Balance { + pallet_assets::Pallet::::balance(asset, who) + } + + fn reducible_balance( + asset: Self::AssetId, + who: &T::AccountId, + preservation: Preservation, + force: Fortitude, + ) -> Self::Balance { + pallet_assets::Pallet::::reducible_balance(asset, who, preservation, force) + } + + fn can_deposit( + asset: Self::AssetId, + who: &T::AccountId, + amount: Self::Balance, + provenance: Provenance, + ) -> DepositConsequence { + pallet_assets::Pallet::::can_deposit(asset, who, amount, provenance) + } + + fn can_withdraw( + asset: Self::AssetId, + who: &T::AccountId, + amount: Self::Balance, + ) -> WithdrawConsequence { + pallet_assets::Pallet::::can_withdraw(asset, who, amount) + } + + fn asset_exists(asset: Self::AssetId) -> bool { + pallet_assets::Pallet::::asset_exists(asset) + } +} + +impl, I: 'static> InspectFreeze for Pallet { + type Id = T::RuntimeFreezeReason; + + fn balance_frozen(asset: Self::AssetId, id: &Self::Id, who: &T::AccountId) -> Self::Balance { + let freezes = Freezes::::get(asset, who); + freezes.into_iter().find(|l| &l.id == id).map_or(Zero::zero(), |l| l.amount) + } + + fn can_freeze(asset: Self::AssetId, id: &Self::Id, who: &T::AccountId) -> bool { + let freezes = Freezes::::get(asset, who); + !freezes.is_full() || freezes.into_iter().any(|i| i.id == *id) + } +} + +impl, I: 'static> MutateFreeze for Pallet { + fn set_freeze( + asset: Self::AssetId, + id: &Self::Id, + who: &T::AccountId, + amount: Self::Balance, + ) -> sp_runtime::DispatchResult { + if amount.is_zero() { + return Self::thaw(asset, id, who); + } + let mut freezes = Freezes::::get(asset.clone(), who); + if let Some(i) = freezes.iter_mut().find(|i| &i.id == id) { + i.amount = amount; + } else { + freezes + .try_push(IdAmount { id: *id, amount }) + .map_err(|_| Error::::TooManyFreezes)?; + } + Self::update_freezes(asset, who, freezes.as_bounded_slice()) + } + + fn extend_freeze( + asset: Self::AssetId, + id: &Self::Id, + who: &T::AccountId, + amount: Self::Balance, + ) -> sp_runtime::DispatchResult { + if amount.is_zero() { + return Ok(()); + } + let mut freezes = Freezes::::get(asset.clone(), who); + if let Some(i) = freezes.iter_mut().find(|x| &x.id == id) { + i.amount = i.amount.max(amount); + } else { + freezes + .try_push(IdAmount { id: *id, amount }) + .map_err(|_| Error::::TooManyFreezes)?; + } + Self::update_freezes(asset, who, freezes.as_bounded_slice()) + } + + fn thaw(asset: Self::AssetId, id: &Self::Id, who: &T::AccountId) -> sp_runtime::DispatchResult { + let mut freezes = Freezes::::get(asset.clone(), who); + freezes.retain(|f| &f.id != id); + Self::update_freezes(asset, who, freezes.as_bounded_slice()) + } +} diff --git a/substrate/frame/assets-freezer/src/lib.rs b/substrate/frame/assets-freezer/src/lib.rs new file mode 100644 index 000000000000..b42d41ac1d92 --- /dev/null +++ b/substrate/frame/assets-freezer/src/lib.rs @@ -0,0 +1,176 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! # Assets Freezer Pallet +//! +//! A pallet capable of freezing fungibles from `pallet-assets`. This is an extension of +//! `pallet-assets`, wrapping [`fungibles::Inspect`](`frame_support::traits::fungibles::Inspect`). +//! It implements both +//! [`fungibles::freeze::Inspect`](frame_support::traits::fungibles::freeze::Inspect) and +//! [`fungibles::freeze::Mutate`](frame_support::traits::fungibles::freeze::Mutate). The complexity +//! of the operations is `O(n)`. where `n` is the variant count of `RuntimeFreezeReason`. +//! +//! ## Pallet API +//! +//! See the [`pallet`] module for more information about the interfaces this pallet exposes, +//! including its configuration trait, dispatchables, storage items, events and errors. +//! +//! ## Overview +//! +//! This pallet provides the following functionality: +//! +//! - Pallet hooks allowing [`pallet-assets`] to know the frozen balance for an account on a given +//! asset (see [`pallet_assets::FrozenBalance`]). +//! - An implementation of +//! [`fungibles::freeze::Inspect`](frame_support::traits::fungibles::freeze::Inspect) and +//! [`fungibles::freeze::Mutate`](frame_support::traits::fungibles::freeze::Mutate), allowing +//! other pallets to manage freezes for the `pallet-assets` assets. + +#![cfg_attr(not(feature = "std"), no_std)] + +use frame_support::{ + pallet_prelude::*, + traits::{tokens::IdAmount, VariantCount, VariantCountOf}, + BoundedVec, +}; +use frame_system::pallet_prelude::BlockNumberFor; +use sp_runtime::{ + traits::{Saturating, Zero}, + BoundedSlice, +}; + +pub use pallet::*; + +#[cfg(test)] +mod mock; +#[cfg(test)] +mod tests; + +mod impls; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + + #[pallet::config(with_default)] + pub trait Config: frame_system::Config + pallet_assets::Config { + /// The overarching freeze reason. + #[pallet::no_default_bounds] + type RuntimeFreezeReason: Parameter + Member + MaxEncodedLen + Copy + VariantCount; + + /// The overarching event type. + #[pallet::no_default_bounds] + type RuntimeEvent: From> + + IsType<::RuntimeEvent>; + } + + #[pallet::error] + pub enum Error { + /// Number of freezes on an account would exceed `MaxFreezes`. + TooManyFreezes, + } + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event, I: 'static = ()> { + // `who`s frozen balance was increased by `amount`. + Frozen { who: T::AccountId, asset_id: T::AssetId, amount: T::Balance }, + // `who`s frozen balance was decreased by `amount`. + Thawed { who: T::AccountId, asset_id: T::AssetId, amount: T::Balance }, + } + + /// A map that stores freezes applied on an account for a given AssetId. + #[pallet::storage] + pub(super) type Freezes, I: 'static = ()> = StorageDoubleMap< + _, + Blake2_128Concat, + T::AssetId, + Blake2_128Concat, + T::AccountId, + BoundedVec< + IdAmount, + VariantCountOf, + >, + ValueQuery, + >; + + /// A map that stores the current total frozen balance for every account on a given AssetId. + #[pallet::storage] + pub(super) type FrozenBalances, I: 'static = ()> = StorageDoubleMap< + _, + Blake2_128Concat, + T::AssetId, + Blake2_128Concat, + T::AccountId, + T::Balance, + >; + + #[pallet::hooks] + impl, I: 'static> Hooks> for Pallet { + #[cfg(feature = "try-runtime")] + fn try_state(_: BlockNumberFor) -> Result<(), sp_runtime::TryRuntimeError> { + Self::do_try_state() + } + } +} + +impl, I: 'static> Pallet { + fn update_freezes( + asset: T::AssetId, + who: &T::AccountId, + freezes: BoundedSlice< + IdAmount, + VariantCountOf, + >, + ) -> DispatchResult { + let prev_frozen = FrozenBalances::::get(asset.clone(), who).unwrap_or_default(); + let after_frozen = freezes.into_iter().map(|f| f.amount).max().unwrap_or_else(Zero::zero); + FrozenBalances::::set(asset.clone(), who, Some(after_frozen)); + if freezes.is_empty() { + Freezes::::remove(asset.clone(), who); + FrozenBalances::::remove(asset.clone(), who); + } else { + Freezes::::insert(asset.clone(), who, freezes); + } + if prev_frozen > after_frozen { + let amount = prev_frozen.saturating_sub(after_frozen); + Self::deposit_event(Event::Thawed { asset_id: asset, who: who.clone(), amount }); + } else if after_frozen > prev_frozen { + let amount = after_frozen.saturating_sub(prev_frozen); + Self::deposit_event(Event::Frozen { asset_id: asset, who: who.clone(), amount }); + } + Ok(()) + } + + #[cfg(any(test, feature = "try-runtime"))] + fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> { + for (asset, who, _) in FrozenBalances::::iter() { + let max_frozen_amount = + Freezes::::get(asset.clone(), who.clone()).iter().map(|l| l.amount).max(); + + frame_support::ensure!( + FrozenBalances::::get(asset, who) == max_frozen_amount, + "The `FrozenAmount` is not equal to the maximum amount in `Freezes` for (`asset`, `who`)" + ); + } + + Ok(()) + } +} diff --git a/substrate/frame/assets-freezer/src/mock.rs b/substrate/frame/assets-freezer/src/mock.rs new file mode 100644 index 000000000000..b4e8c857fba4 --- /dev/null +++ b/substrate/frame/assets-freezer/src/mock.rs @@ -0,0 +1,154 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Tests mock for `pallet-assets-freezer`. + +use crate as pallet_assets_freezer; +pub use crate::*; +use codec::{Compact, Decode, Encode, MaxEncodedLen}; +use frame_support::{ + derive_impl, + traits::{AsEnsureOriginWithArg, ConstU64}, +}; +use scale_info::TypeInfo; +use sp_core::{ConstU32, H256}; +use sp_runtime::{ + traits::{BlakeTwo256, IdentityLookup}, + BuildStorage, +}; + +pub type AccountId = u64; +pub type Balance = u64; +pub type AssetId = u32; +type Block = frame_system::mocking::MockBlock; + +frame_support::construct_runtime!( + pub enum Test + { + System: frame_system, + Assets: pallet_assets, + AssetsFreezer: pallet_assets_freezer, + Balances: pallet_balances, + } +); + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl frame_system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type Nonce = u64; + type Hash = H256; + type RuntimeCall = RuntimeCall; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Block = Block; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +impl pallet_balances::Config for Test { + type MaxLocks = (); + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; + type Balance = Balance; + type DustRemoval = (); + type RuntimeEvent = RuntimeEvent; + type ExistentialDeposit = ConstU64<1>; + type AccountStore = System; + type WeightInfo = (); + type FreezeIdentifier = (); + type MaxFreezes = (); + type RuntimeHoldReason = (); + type RuntimeFreezeReason = (); +} + +impl pallet_assets::Config for Test { + type AssetId = AssetId; + type AssetIdParameter = Compact; + type AssetDeposit = ConstU64<1>; + type Balance = Balance; + type AssetAccountDeposit = ConstU64<1>; + type MetadataDepositBase = (); + type MetadataDepositPerByte = (); + type ApprovalDeposit = (); + type CreateOrigin = AsEnsureOriginWithArg>; + type ForceOrigin = frame_system::EnsureRoot; + type StringLimit = ConstU32<32>; + type Extra = (); + type RemoveItemsLimit = ConstU32<10>; + type CallbackHandle = (); + type Currency = Balances; + type Freezer = AssetsFreezer; + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); +} + +#[derive( + Decode, Encode, MaxEncodedLen, PartialEq, Eq, Ord, PartialOrd, TypeInfo, Debug, Clone, Copy, +)] +pub enum DummyFreezeReason { + Governance, + Staking, + Other, +} + +impl VariantCount for DummyFreezeReason { + // Intentionally set below the actual count of variants, to allow testing for `can_freeze` + const VARIANT_COUNT: u32 = 2; +} + +impl Config for Test { + type RuntimeFreezeReason = DummyFreezeReason; + type RuntimeEvent = RuntimeEvent; +} + +pub fn new_test_ext(execute: impl FnOnce()) -> sp_io::TestExternalities { + let t = RuntimeGenesisConfig { + assets: pallet_assets::GenesisConfig { + assets: vec![(1, 0, true, 1)], + metadata: vec![], + accounts: vec![(1, 1, 100)], + }, + system: Default::default(), + balances: Default::default(), + } + .build_storage() + .unwrap(); + let mut ext: sp_io::TestExternalities = t.into(); + ext.execute_with(|| { + System::set_block_number(1); + execute(); + frame_support::assert_ok!(AssetsFreezer::do_try_state()); + }); + + ext +} diff --git a/substrate/frame/assets-freezer/src/tests.rs b/substrate/frame/assets-freezer/src/tests.rs new file mode 100644 index 000000000000..4f2dea79c705 --- /dev/null +++ b/substrate/frame/assets-freezer/src/tests.rs @@ -0,0 +1,304 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Tests for pallet-assets-freezer. + +use crate::mock::*; + +use codec::Compact; +use frame_support::{ + assert_ok, assert_storage_noop, + traits::{ + fungibles::{Inspect, InspectFreeze, MutateFreeze}, + tokens::{Fortitude, Preservation}, + }, +}; +use pallet_assets::FrozenBalance; + +const WHO: AccountId = 1; +const ASSET_ID: AssetId = 1; + +fn test_set_freeze(id: DummyFreezeReason, amount: Balance) { + let mut freezes = Freezes::::get(ASSET_ID, WHO); + + if let Some(i) = freezes.iter_mut().find(|l| l.id == id) { + i.amount = amount; + } else { + freezes + .try_push(IdAmount { id, amount }) + .expect("freeze is added without exceeding bounds; qed"); + } + + assert_ok!(AssetsFreezer::update_freezes(ASSET_ID, &WHO, freezes.as_bounded_slice())); +} + +fn test_thaw(id: DummyFreezeReason) { + let mut freezes = Freezes::::get(ASSET_ID, WHO); + freezes.retain(|l| l.id != id); + + assert_ok!(AssetsFreezer::update_freezes(ASSET_ID, &WHO, freezes.as_bounded_slice())); +} + +mod impl_frozen_balance { + use super::*; + + #[test] + fn frozen_balance_works() { + new_test_ext(|| { + assert_eq!(AssetsFreezer::frozen_balance(ASSET_ID, &WHO), None); + test_set_freeze(DummyFreezeReason::Governance, 1); + assert_eq!(AssetsFreezer::frozen_balance(ASSET_ID, &WHO), Some(1u64)); + test_set_freeze(DummyFreezeReason::Staking, 3); + assert_eq!(AssetsFreezer::frozen_balance(ASSET_ID, &WHO), Some(3u64)); + test_set_freeze(DummyFreezeReason::Governance, 2); + assert_eq!(AssetsFreezer::frozen_balance(ASSET_ID, &WHO), Some(3u64)); + // also test thawing works to reduce a balance, and finally thawing everything resets to + // None + test_thaw(DummyFreezeReason::Governance); + assert_eq!(AssetsFreezer::frozen_balance(ASSET_ID, &WHO), Some(3u64)); + test_thaw(DummyFreezeReason::Staking); + assert_eq!(AssetsFreezer::frozen_balance(ASSET_ID, &WHO), None); + }); + } + + #[test] + fn died_works() { + new_test_ext(|| { + test_set_freeze(DummyFreezeReason::Governance, 1); + AssetsFreezer::died(ASSET_ID, &WHO); + assert!(FrozenBalances::::get(ASSET_ID, WHO).is_none()); + assert!(Freezes::::get(ASSET_ID, WHO).is_empty()); + }); + } +} + +mod impl_inspect_freeze { + use super::*; + + #[test] + fn balance_frozen_works() { + new_test_ext(|| { + assert_eq!( + AssetsFreezer::balance_frozen(ASSET_ID, &DummyFreezeReason::Governance, &WHO), + 0u64 + ); + test_set_freeze(DummyFreezeReason::Governance, 1); + assert_eq!( + AssetsFreezer::balance_frozen(ASSET_ID, &DummyFreezeReason::Governance, &WHO), + 1u64 + ); + test_set_freeze(DummyFreezeReason::Staking, 3); + assert_eq!( + AssetsFreezer::balance_frozen(ASSET_ID, &DummyFreezeReason::Staking, &WHO), + 3u64 + ); + test_set_freeze(DummyFreezeReason::Staking, 2); + assert_eq!( + AssetsFreezer::balance_frozen(ASSET_ID, &DummyFreezeReason::Staking, &WHO), + 2u64 + ); + // also test thawing works to reduce a balance, and finally thawing everything resets to + // 0 + test_thaw(DummyFreezeReason::Governance); + assert_eq!( + AssetsFreezer::balance_frozen(ASSET_ID, &DummyFreezeReason::Governance, &WHO), + 0u64 + ); + test_thaw(DummyFreezeReason::Staking); + assert_eq!( + AssetsFreezer::balance_frozen(ASSET_ID, &DummyFreezeReason::Staking, &WHO), + 0u64 + ); + }); + } + + /// This tests it's not possible to freeze once the freezes [`BoundedVec`] is full. This is, + /// the lenght of the vec is equal to [`Config::MaxFreezes`]. + /// This test assumes a mock configuration where this parameter is set to `2`. + #[test] + fn can_freeze_works() { + new_test_ext(|| { + test_set_freeze(DummyFreezeReason::Governance, 1); + assert!(AssetsFreezer::can_freeze(ASSET_ID, &DummyFreezeReason::Staking, &WHO)); + test_set_freeze(DummyFreezeReason::Staking, 1); + assert!(!AssetsFreezer::can_freeze(ASSET_ID, &DummyFreezeReason::Other, &WHO)); + }); + } +} + +mod impl_mutate_freeze { + use super::*; + + #[test] + fn set_freeze_works() { + new_test_ext(|| { + assert_eq!( + Assets::reducible_balance( + ASSET_ID, + &WHO, + Preservation::Preserve, + Fortitude::Polite, + ), + 99 + ); + assert_ok!(AssetsFreezer::set_freeze( + ASSET_ID, + &DummyFreezeReason::Governance, + &WHO, + 10 + )); + assert_eq!( + Assets::reducible_balance( + ASSET_ID, + &WHO, + Preservation::Preserve, + Fortitude::Polite, + ), + 89 + ); + System::assert_last_event( + Event::::Frozen { asset_id: ASSET_ID, who: WHO, amount: 10 }.into(), + ); + assert_ok!(AssetsFreezer::set_freeze( + ASSET_ID, + &DummyFreezeReason::Governance, + &WHO, + 8 + )); + assert_eq!( + Assets::reducible_balance( + ASSET_ID, + &WHO, + Preservation::Preserve, + Fortitude::Polite, + ), + 91 + ); + System::assert_last_event( + Event::::Thawed { asset_id: ASSET_ID, who: WHO, amount: 2 }.into(), + ); + }); + } + + #[test] + fn extend_freeze_works() { + new_test_ext(|| { + assert_ok!(AssetsFreezer::set_freeze( + ASSET_ID, + &DummyFreezeReason::Governance, + &WHO, + 10 + )); + assert_storage_noop!(assert_ok!(AssetsFreezer::extend_freeze( + ASSET_ID, + &DummyFreezeReason::Governance, + &WHO, + 8 + ))); + System::assert_last_event( + Event::::Frozen { asset_id: ASSET_ID, who: WHO, amount: 10 }.into(), + ); + assert_eq!( + Assets::reducible_balance( + ASSET_ID, + &WHO, + Preservation::Preserve, + Fortitude::Polite, + ), + 89 + ); + assert_ok!(AssetsFreezer::extend_freeze( + ASSET_ID, + &DummyFreezeReason::Governance, + &WHO, + 11 + )); + System::assert_last_event( + Event::::Frozen { asset_id: ASSET_ID, who: WHO, amount: 1 }.into(), + ); + assert_eq!( + Assets::reducible_balance( + ASSET_ID, + &WHO, + Preservation::Preserve, + Fortitude::Polite, + ), + 88 + ); + }); + } + + #[test] + fn thaw_works() { + new_test_ext(|| { + assert_ok!(AssetsFreezer::set_freeze( + ASSET_ID, + &DummyFreezeReason::Governance, + &WHO, + 10 + )); + System::assert_has_event( + Event::::Frozen { asset_id: ASSET_ID, who: WHO, amount: 10 }.into(), + ); + assert_eq!( + Assets::reducible_balance( + ASSET_ID, + &WHO, + Preservation::Preserve, + Fortitude::Polite, + ), + 89 + ); + assert_ok!(AssetsFreezer::thaw(ASSET_ID, &DummyFreezeReason::Governance, &WHO)); + System::assert_has_event( + Event::::Thawed { asset_id: ASSET_ID, who: WHO, amount: 10 }.into(), + ); + assert_eq!( + Assets::reducible_balance( + ASSET_ID, + &WHO, + Preservation::Preserve, + Fortitude::Polite, + ), + 99 + ); + }); + } +} + +mod with_pallet_assets { + use frame_support::assert_noop; + + use super::*; + + #[test] + fn frozen_balance_affects_balance_transferring() { + new_test_ext(|| { + assert_ok!(AssetsFreezer::set_freeze( + ASSET_ID, + &DummyFreezeReason::Governance, + &WHO, + 20 + )); + assert_noop!( + Assets::transfer(RuntimeOrigin::signed(WHO), Compact(ASSET_ID), 2, 80), + pallet_assets::Error::::BalanceLow, + ); + assert_ok!(Assets::transfer(RuntimeOrigin::signed(WHO), Compact(ASSET_ID), 2, 79)); + }); + } +} diff --git a/substrate/frame/balances/src/lib.rs b/substrate/frame/balances/src/lib.rs index 4935323b3aa1..d01884293c09 100644 --- a/substrate/frame/balances/src/lib.rs +++ b/substrate/frame/balances/src/lib.rs @@ -158,6 +158,7 @@ use frame_support::{ tokens::{ fungible, BalanceStatus as Status, DepositConsequence, Fortitude::{self, Force, Polite}, + IdAmount, Preservation::{Expendable, Preserve, Protect}, WithdrawConsequence, }, @@ -177,8 +178,7 @@ use sp_runtime::{ }; use sp_std::{cmp, fmt::Debug, mem, prelude::*, result}; pub use types::{ - AccountData, AdjustmentDirection, BalanceLock, DustCleaner, ExtraFlags, IdAmount, Reasons, - ReserveData, + AccountData, AdjustmentDirection, BalanceLock, DustCleaner, ExtraFlags, Reasons, ReserveData, }; pub use weights::WeightInfo; diff --git a/substrate/frame/balances/src/tests/fungible_tests.rs b/substrate/frame/balances/src/tests/fungible_tests.rs index 52fbe10bedec..1a09303a6590 100644 --- a/substrate/frame/balances/src/tests/fungible_tests.rs +++ b/substrate/frame/balances/src/tests/fungible_tests.rs @@ -18,13 +18,20 @@ //! Tests regarding the functionality of the `fungible` trait set implementations. use super::*; -use frame_support::traits::tokens::{ - Fortitude::{Force, Polite}, - Precision::{BestEffort, Exact}, - Preservation::{Expendable, Preserve, Protect}, - Restriction::Free, +use frame_support::traits::{ + tokens::{ + Fortitude::{Force, Polite}, + Precision::{BestEffort, Exact}, + Preservation::{Expendable, Preserve, Protect}, + Restriction::Free, + }, + Consideration, Footprint, LinearStoragePrice, }; -use fungible::{Inspect, InspectFreeze, InspectHold, Mutate, MutateFreeze, MutateHold, Unbalanced}; +use fungible::{ + FreezeConsideration, HoldConsideration, Inspect, InspectFreeze, InspectHold, + LoneFreezeConsideration, LoneHoldConsideration, Mutate, MutateFreeze, MutateHold, Unbalanced, +}; +use sp_core::ConstU64; #[test] fn inspect_trait_reducible_balance_basic_works() { @@ -493,3 +500,161 @@ fn withdraw_precision_exact_works() { ); }); } + +#[test] +fn freeze_consideration_works() { + ExtBuilder::default() + .existential_deposit(1) + .monied(true) + .build_and_execute_with(|| { + type Consideration = FreezeConsideration< + u64, + Balances, + FooReason, + LinearStoragePrice, ConstU64<1>, u64>, + Footprint, + >; + + let who = 4; + // freeze amount taken somewhere outside of our (Consideration) scope. + let extend_freeze = 15; + assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 0); + + let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap().unwrap(); + assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 10); + + let ticket = ticket.update(&who, Footprint::from_parts(4, 1)).unwrap().unwrap(); + assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 4); + + assert_ok!(Balances::increase_frozen(&TestId::Foo, &who, extend_freeze)); + assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 4 + extend_freeze); + + let ticket = ticket.update(&who, Footprint::from_parts(8, 1)).unwrap().unwrap(); + assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 8 + extend_freeze); + + assert_eq!(ticket.update(&who, Footprint::from_parts(0, 0)).unwrap(), None); + assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 0 + extend_freeze); + + let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap().unwrap(); + assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 10 + extend_freeze); + + let _ = ticket.drop(&who).unwrap(); + assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 0 + extend_freeze); + }); +} + +#[test] +fn hold_consideration_works() { + ExtBuilder::default() + .existential_deposit(1) + .monied(true) + .build_and_execute_with(|| { + type Consideration = HoldConsideration< + u64, + Balances, + FooReason, + LinearStoragePrice, ConstU64<1>, u64>, + Footprint, + >; + + let who = 4; + // hold amount taken somewhere outside of our (Consideration) scope. + let extend_hold = 15; + assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 0); + + let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap().unwrap(); + assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 10); + + let ticket = ticket.update(&who, Footprint::from_parts(4, 1)).unwrap().unwrap(); + assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 4); + + assert_ok!(Balances::hold(&TestId::Foo, &who, extend_hold)); + assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 4 + extend_hold); + + let ticket = ticket.update(&who, Footprint::from_parts(8, 1)).unwrap().unwrap(); + assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 8 + extend_hold); + + assert_eq!(ticket.update(&who, Footprint::from_parts(0, 0)).unwrap(), None); + assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 0 + extend_hold); + + let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap().unwrap(); + assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 10 + extend_hold); + + let _ = ticket.drop(&who).unwrap(); + assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 0 + extend_hold); + }); +} + +#[test] +fn lone_freeze_consideration_works() { + ExtBuilder::default() + .existential_deposit(1) + .monied(true) + .build_and_execute_with(|| { + type Consideration = LoneFreezeConsideration< + u64, + Balances, + FooReason, + LinearStoragePrice, ConstU64<1>, u64>, + Footprint, + >; + + let who = 4; + assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 0); + + let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap().unwrap(); + assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 10); + + assert_ok!(Balances::increase_frozen(&TestId::Foo, &who, 5)); + assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 15); + + let ticket = ticket.update(&who, Footprint::from_parts(4, 1)).unwrap().unwrap(); + assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 4); + + assert_eq!(ticket.update(&who, Footprint::from_parts(0, 0)).unwrap(), None); + assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 0); + + let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap().unwrap(); + assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 10); + + let _ = ticket.drop(&who).unwrap(); + assert_eq!(Balances::balance_frozen(&TestId::Foo, &who), 0); + }); +} + +#[test] +fn lone_hold_consideration_works() { + ExtBuilder::default() + .existential_deposit(1) + .monied(true) + .build_and_execute_with(|| { + type Consideration = LoneHoldConsideration< + u64, + Balances, + FooReason, + LinearStoragePrice, ConstU64<1>, u64>, + Footprint, + >; + + let who = 4; + assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 0); + + let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap().unwrap(); + assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 10); + + assert_ok!(Balances::hold(&TestId::Foo, &who, 5)); + assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 15); + + let ticket = ticket.update(&who, Footprint::from_parts(4, 1)).unwrap().unwrap(); + assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 4); + + assert_eq!(ticket.update(&who, Footprint::from_parts(0, 0)).unwrap(), None); + assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 0); + + let ticket = Consideration::new(&who, Footprint::from_parts(10, 1)).unwrap().unwrap(); + assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 10); + + let _ = ticket.drop(&who).unwrap(); + assert_eq!(Balances::balance_on_hold(&TestId::Foo, &who), 0); + }); +} diff --git a/substrate/frame/balances/src/tests/mod.rs b/substrate/frame/balances/src/tests/mod.rs index 5ed37170407f..ba0cdabdabbb 100644 --- a/substrate/frame/balances/src/tests/mod.rs +++ b/substrate/frame/balances/src/tests/mod.rs @@ -107,6 +107,10 @@ impl pallet_transaction_payment::Config for Test { type FeeMultiplierUpdate = (); } +parameter_types! { + pub FooReason: TestId = TestId::Foo; +} + #[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] impl Config for Test { type DustRemoval = DustTrap; diff --git a/substrate/frame/balances/src/types.rs b/substrate/frame/balances/src/types.rs index 3e36a83575c8..917b7507d7c9 100644 --- a/substrate/frame/balances/src/types.rs +++ b/substrate/frame/balances/src/types.rs @@ -78,15 +78,6 @@ pub struct ReserveData { pub amount: Balance, } -/// An identifier and balance. -#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, MaxEncodedLen, TypeInfo)] -pub struct IdAmount { - /// An identifier for this item. - pub id: Id, - /// Some amount for this item. - pub amount: Balance, -} - /// All balance information for an account. #[derive(Encode, Decode, Clone, PartialEq, Eq, Default, RuntimeDebug, MaxEncodedLen, TypeInfo)] pub struct AccountData { diff --git a/substrate/frame/nfts/src/benchmarking.rs b/substrate/frame/nfts/src/benchmarking.rs index 8792af675fc1..80860bc5a53c 100644 --- a/substrate/frame/nfts/src/benchmarking.rs +++ b/substrate/frame/nfts/src/benchmarking.rs @@ -30,11 +30,7 @@ use frame_support::{ BoundedVec, }; use frame_system::{pallet_prelude::BlockNumberFor, RawOrigin as SystemOrigin}; -use sp_io::crypto::{sr25519_generate, sr25519_sign}; -use sp_runtime::{ - traits::{Bounded, IdentifyAccount, One}, - AccountId32, MultiSignature, MultiSigner, -}; +use sp_runtime::traits::{Bounded, One}; use sp_std::prelude::*; use crate::Pallet as Nfts; @@ -229,12 +225,6 @@ fn make_filled_vec(value: u16, length: usize) -> Vec { } benchmarks_instance_pallet! { - where_clause { - where - T::OffchainSignature: From, - T::AccountId: From, - } - create { let collection = T::Helper::collection(0); let origin = T::CreateOrigin::try_successful_origin(&collection) @@ -800,8 +790,7 @@ benchmarks_instance_pallet! { mint_pre_signed { let n in 0 .. T::MaxAttributesPerCall::get() as u32; - let caller_public = sr25519_generate(0.into(), None); - let caller = MultiSigner::Sr25519(caller_public).into_account().into(); + let (caller_public, caller) = T::Helper::signer(); T::Currency::make_free_balance_be(&caller, DepositBalanceOf::::max_value()); let caller_lookup = T::Lookup::unlookup(caller.clone()); @@ -830,7 +819,7 @@ benchmarks_instance_pallet! { mint_price: Some(DepositBalanceOf::::min_value()), }; let message = Encode::encode(&mint_data); - let signature = MultiSignature::Sr25519(sr25519_sign(0.into(), &caller_public, &message).unwrap()); + let signature = T::Helper::sign(&caller_public, &message); let target: T::AccountId = account("target", 0, SEED); T::Currency::make_free_balance_be(&target, DepositBalanceOf::::max_value()); @@ -848,8 +837,7 @@ benchmarks_instance_pallet! { let item_owner: T::AccountId = account("item_owner", 0, SEED); let item_owner_lookup = T::Lookup::unlookup(item_owner.clone()); - let signer_public = sr25519_generate(0.into(), None); - let signer: T::AccountId = MultiSigner::Sr25519(signer_public).into_account().into(); + let (signer_public, signer) = T::Helper::signer(); T::Currency::make_free_balance_be(&item_owner, DepositBalanceOf::::max_value()); @@ -876,7 +864,7 @@ benchmarks_instance_pallet! { deadline: One::one(), }; let message = Encode::encode(&pre_signed_data); - let signature = MultiSignature::Sr25519(sr25519_sign(0.into(), &signer_public, &message).unwrap()); + let signature = T::Helper::sign(&signer_public, &message); frame_system::Pallet::::set_block_number(One::one()); }: _(SystemOrigin::Signed(item_owner.clone()), pre_signed_data, signature.into(), signer.clone()) diff --git a/substrate/frame/nfts/src/lib.rs b/substrate/frame/nfts/src/lib.rs index 615720268fed..0406cac6e2c9 100644 --- a/substrate/frame/nfts/src/lib.rs +++ b/substrate/frame/nfts/src/lib.rs @@ -84,18 +84,42 @@ pub mod pallet { pub struct Pallet(PhantomData<(T, I)>); #[cfg(feature = "runtime-benchmarks")] - pub trait BenchmarkHelper { + pub trait BenchmarkHelper { fn collection(i: u16) -> CollectionId; fn item(i: u16) -> ItemId; + fn signer() -> (Public, AccountId); + fn sign(signer: &Public, message: &[u8]) -> Signature; } #[cfg(feature = "runtime-benchmarks")] - impl, ItemId: From> BenchmarkHelper for () { + impl + BenchmarkHelper< + CollectionId, + ItemId, + sp_runtime::MultiSigner, + sp_runtime::AccountId32, + sp_runtime::MultiSignature, + > for () + where + CollectionId: From, + ItemId: From, + { fn collection(i: u16) -> CollectionId { i.into() } fn item(i: u16) -> ItemId { i.into() } + fn signer() -> (sp_runtime::MultiSigner, sp_runtime::AccountId32) { + let public = sp_io::crypto::sr25519_generate(0.into(), None); + let account = sp_runtime::MultiSigner::Sr25519(public).into_account(); + (public.into(), account) + } + fn sign(signer: &sp_runtime::MultiSigner, message: &[u8]) -> sp_runtime::MultiSignature { + sp_runtime::MultiSignature::Sr25519( + sp_io::crypto::sr25519_sign(0.into(), &signer.clone().try_into().unwrap(), message) + .unwrap(), + ) + } } #[pallet::config] @@ -206,7 +230,13 @@ pub mod pallet { #[cfg(feature = "runtime-benchmarks")] /// A set of helper functions for benchmarking. - type Helper: BenchmarkHelper; + type Helper: BenchmarkHelper< + Self::CollectionId, + Self::ItemId, + Self::OffchainPublic, + Self::AccountId, + Self::OffchainSignature, + >; /// Weight information for extrinsics in this pallet. type WeightInfo: WeightInfo; diff --git a/substrate/frame/preimage/src/benchmarking.rs b/substrate/frame/preimage/src/benchmarking.rs index d0c3404f40a9..f2b76a7999d6 100644 --- a/substrate/frame/preimage/src/benchmarking.rs +++ b/substrate/frame/preimage/src/benchmarking.rs @@ -116,7 +116,7 @@ benchmarks! { T::ManagerOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?, hash ) verify { - let ticket = TicketOf::::new(¬er, Footprint { count: 1, size: MAX_SIZE as u64 }).unwrap(); + let ticket = TicketOf::::new(¬er, Footprint { count: 1, size: MAX_SIZE as u64 }).unwrap().unwrap(); let s = RequestStatus::Requested { maybe_ticket: Some((noter, ticket)), count: 1, maybe_len: Some(MAX_SIZE) }; assert_eq!(RequestStatusFor::::get(&hash), Some(s)); } diff --git a/substrate/frame/preimage/src/lib.rs b/substrate/frame/preimage/src/lib.rs index 4e4746851666..dd323a12b8f8 100644 --- a/substrate/frame/preimage/src/lib.rs +++ b/substrate/frame/preimage/src/lib.rs @@ -122,7 +122,9 @@ pub mod pallet { type ManagerOrigin: EnsureOrigin; /// A means of providing some cost while data is stored on-chain. - type Consideration: Consideration; + /// + /// Should never return a `None`, implying no cost for a non-empty preimage. + type Consideration: Consideration; } #[pallet::pallet] @@ -158,6 +160,8 @@ pub mod pallet { TooMany, /// Too few hashes were requested to be upgraded (i.e. zero). TooFew, + /// No ticket with a cost was returned by [`Config::Consideration`] to store the preimage. + NoCost, } /// A reason for this pallet placing a hold on funds. @@ -268,10 +272,10 @@ impl Pallet { // unreserve deposit T::Currency::unreserve(&who, amount); // take consideration - let Ok(ticket) = + let Ok(Some(ticket)) = T::Consideration::new(&who, Footprint::from_parts(1, len as usize)) - .defensive_proof("Unexpected inability to take deposit after unreserved") else { + defensive!("None ticket or inability to take deposit after unreserved"); return true }; RequestStatus::Unrequested { ticket: (who, ticket), len } @@ -282,12 +286,10 @@ impl Pallet { T::Currency::unreserve(&who, deposit); // take consideration if let Some(len) = maybe_len { - let Ok(ticket) = + let Ok(Some(ticket)) = T::Consideration::new(&who, Footprint::from_parts(1, len as usize)) - .defensive_proof( - "Unexpected inability to take deposit after unreserved", - ) else { + defensive!("None ticket or inability to take deposit after unreserved"); return true }; Some((who, ticket)) @@ -347,7 +349,8 @@ impl Pallet { RequestStatus::Requested { maybe_ticket: None, count: 1, maybe_len: Some(len) }, (None, Some(depositor)) => { let ticket = - T::Consideration::new(depositor, Footprint::from_parts(1, len as usize))?; + T::Consideration::new(depositor, Footprint::from_parts(1, len as usize))? + .ok_or(Error::::NoCost)?; RequestStatus::Unrequested { ticket: (depositor.clone(), ticket), len } }, }; diff --git a/substrate/frame/support/src/traits/storage.rs b/substrate/frame/support/src/traits/storage.rs index 9e467aea4220..875ff56bea19 100644 --- a/substrate/frame/support/src/traits/storage.rs +++ b/substrate/frame/support/src/traits/storage.rs @@ -194,7 +194,7 @@ where } /// Some sort of cost taken from account temporarily in order to offset the cost to the chain of -/// holding some data [`Footprint`] in state. +/// holding some data `Footprint` (e.g. [`Footprint`]) in state. /// /// The cost may be increased, reduced or dropped entirely as the footprint changes. /// @@ -206,16 +206,20 @@ where /// treated as one*. Don't type to duplicate it, and remember to drop it when you're done with /// it. #[must_use] -pub trait Consideration: Member + FullCodec + TypeInfo + MaxEncodedLen { +pub trait Consideration: + Member + FullCodec + TypeInfo + MaxEncodedLen +{ /// Create a ticket for the `new` footprint attributable to `who`. This ticket *must* ultimately - /// be consumed through `update` or `drop` once the footprint changes or is removed. - fn new(who: &AccountId, new: Footprint) -> Result; + /// be consumed through `update` or `drop` once the footprint changes or is removed. `None` + /// implies no cost for a given footprint. + fn new(who: &AccountId, new: Footprint) -> Result, DispatchError>; /// Optionally consume an old ticket and alter the footprint, enforcing the new cost to `who` - /// and returning the new ticket (or an error if there was an issue). + /// and returning the new ticket (or an error if there was an issue). `None` implies no cost for + /// a given footprint. /// /// For creating tickets and dropping them, you can use the simpler `new` and `drop` instead. - fn update(self, who: &AccountId, new: Footprint) -> Result; + fn update(self, who: &AccountId, new: Footprint) -> Result, DispatchError>; /// Consume a ticket for some `old` footprint attributable to `who` which should now been freed. fn drop(self, who: &AccountId) -> Result<(), DispatchError>; @@ -230,12 +234,12 @@ pub trait Consideration: Member + FullCodec + TypeInfo + MaxEncodedLe } } -impl Consideration for () { - fn new(_: &A, _: Footprint) -> Result { - Ok(()) +impl Consideration for () { + fn new(_: &A, _: F) -> Result, DispatchError> { + Ok(Some(())) } - fn update(self, _: &A, _: Footprint) -> Result<(), DispatchError> { - Ok(()) + fn update(self, _: &A, _: F) -> Result, DispatchError> { + Ok(Some(())) } fn drop(self, _: &A) -> Result<(), DispatchError> { Ok(()) diff --git a/substrate/frame/support/src/traits/tokens.rs b/substrate/frame/support/src/traits/tokens.rs index 8842b2058018..138703cf1d13 100644 --- a/substrate/frame/support/src/traits/tokens.rs +++ b/substrate/frame/support/src/traits/tokens.rs @@ -30,8 +30,8 @@ pub use imbalance::Imbalance; pub mod pay; pub use misc::{ AssetId, Balance, BalanceStatus, ConversionFromAssetBalance, ConversionToAssetBalance, - ConvertRank, DepositConsequence, ExistenceRequirement, Fortitude, GetSalary, Locker, Precision, - Preservation, Provenance, Restriction, UnityAssetBalanceConversion, UnityOrOuterConversion, - WithdrawConsequence, WithdrawReasons, + ConvertRank, DepositConsequence, ExistenceRequirement, Fortitude, GetSalary, IdAmount, Locker, + Precision, Preservation, Provenance, Restriction, UnityAssetBalanceConversion, + UnityOrOuterConversion, WithdrawConsequence, WithdrawReasons, }; pub use pay::{Pay, PayFromAccount, PaymentStatus}; diff --git a/substrate/frame/support/src/traits/tokens/fungible/mod.rs b/substrate/frame/support/src/traits/tokens/fungible/mod.rs index 01c3b9dfe46a..b8e985648983 100644 --- a/substrate/frame/support/src/traits/tokens/fungible/mod.rs +++ b/substrate/frame/support/src/traits/tokens/fungible/mod.rs @@ -198,31 +198,40 @@ use crate::{ MaxEncodedLen, RuntimeDebugNoBound, )] -#[scale_info(skip_type_params(A, F, R, D))] +#[scale_info(skip_type_params(A, F, R, D, Fp))] #[codec(mel_bound())] -pub struct FreezeConsideration(F::Balance, PhantomData (A, R, D)>) +pub struct FreezeConsideration(F::Balance, PhantomData (A, R, D, Fp)>) where F: MutateFreeze; impl< A: 'static, F: 'static + MutateFreeze, R: 'static + Get, - D: 'static + Convert, - > Consideration for FreezeConsideration + D: 'static + Convert, + Fp: 'static, + > Consideration for FreezeConsideration { - fn new(who: &A, footprint: Footprint) -> Result { + fn new(who: &A, footprint: Fp) -> Result, DispatchError> { let new = D::convert(footprint); - F::increase_frozen(&R::get(), who, new)?; - Ok(Self(new, PhantomData)) + if new.is_zero() { + Ok(None) + } else { + F::increase_frozen(&R::get(), who, new)?; + Ok(Some(Self(new, PhantomData))) + } } - fn update(self, who: &A, footprint: Footprint) -> Result { + fn update(self, who: &A, footprint: Fp) -> Result, DispatchError> { let new = D::convert(footprint); if self.0 > new { F::decrease_frozen(&R::get(), who, self.0 - new)?; } else if new > self.0 { F::increase_frozen(&R::get(), who, new - self.0)?; } - Ok(Self(new, PhantomData)) + if new.is_zero() { + Ok(None) + } else { + Ok(Some(Self(new, PhantomData))) + } } fn drop(self, who: &A) -> Result<(), DispatchError> { F::decrease_frozen(&R::get(), who, self.0).map(|_| ()) @@ -240,31 +249,43 @@ impl< MaxEncodedLen, RuntimeDebugNoBound, )] -#[scale_info(skip_type_params(A, F, R, D))] +#[scale_info(skip_type_params(A, F, R, D, Fp))] #[codec(mel_bound())] -pub struct HoldConsideration(F::Balance, PhantomData (A, R, D)>) +pub struct HoldConsideration( + F::Balance, + PhantomData (A, R, D, Fp)>, +) where F: MutateHold; impl< A: 'static, F: 'static + MutateHold, R: 'static + Get, - D: 'static + Convert, - > Consideration for HoldConsideration + D: 'static + Convert, + Fp: 'static, + > Consideration for HoldConsideration { - fn new(who: &A, footprint: Footprint) -> Result { + fn new(who: &A, footprint: Fp) -> Result, DispatchError> { let new = D::convert(footprint); - F::hold(&R::get(), who, new)?; - Ok(Self(new, PhantomData)) + if new.is_zero() { + Ok(None) + } else { + F::hold(&R::get(), who, new)?; + Ok(Some(Self(new, PhantomData))) + } } - fn update(self, who: &A, footprint: Footprint) -> Result { + fn update(self, who: &A, footprint: Fp) -> Result, DispatchError> { let new = D::convert(footprint); if self.0 > new { F::release(&R::get(), who, self.0 - new, BestEffort)?; } else if new > self.0 { F::hold(&R::get(), who, new - self.0)?; } - Ok(Self(new, PhantomData)) + if new.is_zero() { + Ok(None) + } else { + Ok(Some(Self(new, PhantomData))) + } } fn drop(self, who: &A) -> Result<(), DispatchError> { F::release(&R::get(), who, self.0, BestEffort).map(|_| ()) @@ -291,22 +312,34 @@ impl< MaxEncodedLen, RuntimeDebugNoBound, )] -#[scale_info(skip_type_params(A, Fx, Rx, D))] +#[scale_info(skip_type_params(A, Fx, Rx, D, Fp))] #[codec(mel_bound())] -pub struct LoneFreezeConsideration(PhantomData (A, Fx, Rx, D)>); +pub struct LoneFreezeConsideration(PhantomData (A, Fx, Rx, D, Fp)>); impl< A: 'static, Fx: 'static + MutateFreeze, Rx: 'static + Get, - D: 'static + Convert, - > Consideration for LoneFreezeConsideration + D: 'static + Convert, + Fp: 'static, + > Consideration for LoneFreezeConsideration { - fn new(who: &A, footprint: Footprint) -> Result { + fn new(who: &A, footprint: Fp) -> Result, DispatchError> { ensure!(Fx::balance_frozen(&Rx::get(), who).is_zero(), DispatchError::Unavailable); - Fx::set_frozen(&Rx::get(), who, D::convert(footprint), Polite).map(|_| Self(PhantomData)) + let new = D::convert(footprint); + if new.is_zero() { + Ok(None) + } else { + Fx::set_frozen(&Rx::get(), who, new, Polite).map(|_| Some(Self(PhantomData))) + } } - fn update(self, who: &A, footprint: Footprint) -> Result { - Fx::set_frozen(&Rx::get(), who, D::convert(footprint), Polite).map(|_| Self(PhantomData)) + fn update(self, who: &A, footprint: Fp) -> Result, DispatchError> { + let new = D::convert(footprint); + let _ = Fx::set_frozen(&Rx::get(), who, new, Polite)?; + if new.is_zero() { + Ok(None) + } else { + Ok(Some(Self(PhantomData))) + } } fn drop(self, who: &A) -> Result<(), DispatchError> { Fx::thaw(&Rx::get(), who).map(|_| ()) @@ -330,22 +363,34 @@ impl< MaxEncodedLen, RuntimeDebugNoBound, )] -#[scale_info(skip_type_params(A, Fx, Rx, D))] +#[scale_info(skip_type_params(A, Fx, Rx, D, Fp))] #[codec(mel_bound())] -pub struct LoneHoldConsideration(PhantomData (A, Fx, Rx, D)>); +pub struct LoneHoldConsideration(PhantomData (A, Fx, Rx, D, Fp)>); impl< A: 'static, F: 'static + MutateHold, R: 'static + Get, - D: 'static + Convert, - > Consideration for LoneHoldConsideration + D: 'static + Convert, + Fp: 'static, + > Consideration for LoneHoldConsideration { - fn new(who: &A, footprint: Footprint) -> Result { + fn new(who: &A, footprint: Fp) -> Result, DispatchError> { ensure!(F::balance_on_hold(&R::get(), who).is_zero(), DispatchError::Unavailable); - F::set_on_hold(&R::get(), who, D::convert(footprint)).map(|_| Self(PhantomData)) + let new = D::convert(footprint); + if new.is_zero() { + Ok(None) + } else { + F::set_on_hold(&R::get(), who, new).map(|_| Some(Self(PhantomData))) + } } - fn update(self, who: &A, footprint: Footprint) -> Result { - F::set_on_hold(&R::get(), who, D::convert(footprint)).map(|_| Self(PhantomData)) + fn update(self, who: &A, footprint: Fp) -> Result, DispatchError> { + let new = D::convert(footprint); + let _ = F::set_on_hold(&R::get(), who, new)?; + if new.is_zero() { + Ok(None) + } else { + Ok(Some(Self(PhantomData))) + } } fn drop(self, who: &A) -> Result<(), DispatchError> { F::release_all(&R::get(), who, BestEffort).map(|_| ()) diff --git a/substrate/frame/support/src/traits/tokens/misc.rs b/substrate/frame/support/src/traits/tokens/misc.rs index 424acb1d550b..e1ff1e058ae7 100644 --- a/substrate/frame/support/src/traits/tokens/misc.rs +++ b/substrate/frame/support/src/traits/tokens/misc.rs @@ -17,7 +17,7 @@ //! Miscellaneous types. -use crate::traits::Contains; +use crate::{traits::Contains, TypeInfo}; use codec::{Decode, Encode, FullCodec, MaxEncodedLen}; use sp_arithmetic::traits::{AtLeast32BitUnsigned, Zero}; use sp_core::RuntimeDebug; @@ -357,3 +357,12 @@ impl> GetSalary for ConvertRank { C::convert(rank) } } + +/// An identifier and balance. +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, MaxEncodedLen, TypeInfo)] +pub struct IdAmount { + /// An identifier for this item. + pub id: Id, + /// Some amount for this item. + pub amount: Balance, +} diff --git a/substrate/primitives/statement-store/Cargo.toml b/substrate/primitives/statement-store/Cargo.toml index bb893b25dc44..60919b7439ea 100644 --- a/substrate/primitives/statement-store/Cargo.toml +++ b/substrate/primitives/statement-store/Cargo.toml @@ -30,7 +30,7 @@ thiserror = { optional = true, workspace = true } # ECIES dependencies ed25519-dalek = { version = "2.1", optional = true } x25519-dalek = { version = "2.0", optional = true, features = ["static_secrets"] } -curve25519-dalek = { version = "4.1.1", optional = true } +curve25519-dalek = { version = "4.1.3", optional = true } aes-gcm = { version = "0.10", optional = true } hkdf = { version = "0.12.0", optional = true } sha2 = { version = "0.10.7", optional = true } diff --git a/umbrella/Cargo.toml b/umbrella/Cargo.toml index d790b4f5949c..93af28a7ddd9 100644 --- a/umbrella/Cargo.toml +++ b/umbrella/Cargo.toml @@ -68,6 +68,7 @@ std = [ "pallet-asset-conversion?/std", "pallet-asset-rate?/std", "pallet-asset-tx-payment?/std", + "pallet-assets-freezer?/std", "pallet-assets?/std", "pallet-atomic-swap?/std", "pallet-aura?/std", @@ -263,6 +264,7 @@ runtime-benchmarks = [ "pallet-asset-conversion?/runtime-benchmarks", "pallet-asset-rate?/runtime-benchmarks", "pallet-asset-tx-payment?/runtime-benchmarks", + "pallet-assets-freezer?/runtime-benchmarks", "pallet-assets?/runtime-benchmarks", "pallet-babe?/runtime-benchmarks", "pallet-bags-list?/runtime-benchmarks", @@ -385,6 +387,7 @@ try-runtime = [ "pallet-asset-conversion?/try-runtime", "pallet-asset-rate?/try-runtime", "pallet-asset-tx-payment?/try-runtime", + "pallet-assets-freezer?/try-runtime", "pallet-assets?/try-runtime", "pallet-atomic-swap?/try-runtime", "pallet-aura?/try-runtime", @@ -536,7 +539,7 @@ with-tracing = [ "sp-tracing?/with-tracing", "sp-tracing?/with-tracing", ] -runtime = ["assets-common", "binary-merkle-tree", "bp-asset-hub-rococo", "bp-asset-hub-westend", "bp-bridge-hub-cumulus", "bp-bridge-hub-kusama", "bp-bridge-hub-polkadot", "bp-bridge-hub-rococo", "bp-bridge-hub-westend", "bp-header-chain", "bp-kusama", "bp-messages", "bp-parachains", "bp-polkadot", "bp-polkadot-bulletin", "bp-polkadot-core", "bp-relayers", "bp-rococo", "bp-runtime", "bp-test-utils", "bp-westend", "bp-xcm-bridge-hub", "bp-xcm-bridge-hub-router", "bridge-hub-common", "bridge-runtime-common", "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", "cumulus-pallet-parachain-system-proc-macro", "cumulus-pallet-session-benchmarking", "cumulus-pallet-solo-to-para", "cumulus-pallet-xcm", "cumulus-pallet-xcmp-queue", "cumulus-ping", "cumulus-primitives-aura", "cumulus-primitives-core", "cumulus-primitives-parachain-inherent", "cumulus-primitives-proof-size-hostfunction", "cumulus-primitives-storage-weight-reclaim", "cumulus-primitives-timestamp", "cumulus-primitives-utility", "frame-benchmarking", "frame-benchmarking-pallet-pov", "frame-election-provider-solution-type", "frame-election-provider-support", "frame-executive", "frame-metadata-hash-extension", "frame-support", "frame-support-procedural", "frame-support-procedural-tools-derive", "frame-system", "frame-system-benchmarking", "frame-system-rpc-runtime-api", "frame-try-runtime", "pallet-alliance", "pallet-asset-conversion", "pallet-asset-conversion-ops", "pallet-asset-conversion-tx-payment", "pallet-asset-rate", "pallet-asset-tx-payment", "pallet-assets", "pallet-atomic-swap", "pallet-aura", "pallet-authority-discovery", "pallet-authorship", "pallet-babe", "pallet-bags-list", "pallet-balances", "pallet-beefy", "pallet-beefy-mmr", "pallet-bounties", "pallet-bridge-grandpa", "pallet-bridge-messages", "pallet-bridge-parachains", "pallet-bridge-relayers", "pallet-broker", "pallet-child-bounties", "pallet-collator-selection", "pallet-collective", "pallet-collective-content", "pallet-contracts", "pallet-contracts-proc-macro", "pallet-contracts-uapi", "pallet-conviction-voting", "pallet-core-fellowship", "pallet-delegated-staking", "pallet-democracy", "pallet-dev-mode", "pallet-election-provider-multi-phase", "pallet-election-provider-support-benchmarking", "pallet-elections-phragmen", "pallet-fast-unstake", "pallet-glutton", "pallet-grandpa", "pallet-identity", "pallet-im-online", "pallet-indices", "pallet-insecure-randomness-collective-flip", "pallet-lottery", "pallet-membership", "pallet-message-queue", "pallet-migrations", "pallet-mixnet", "pallet-mmr", "pallet-multisig", "pallet-nft-fractionalization", "pallet-nfts", "pallet-nfts-runtime-api", "pallet-nis", "pallet-node-authorization", "pallet-nomination-pools", "pallet-nomination-pools-benchmarking", "pallet-nomination-pools-runtime-api", "pallet-offences", "pallet-offences-benchmarking", "pallet-paged-list", "pallet-parameters", "pallet-preimage", "pallet-proxy", "pallet-ranked-collective", "pallet-recovery", "pallet-referenda", "pallet-remark", "pallet-root-offences", "pallet-root-testing", "pallet-safe-mode", "pallet-salary", "pallet-scheduler", "pallet-scored-pool", "pallet-session", "pallet-session-benchmarking", "pallet-skip-feeless-payment", "pallet-society", "pallet-staking", "pallet-staking-reward-curve", "pallet-staking-reward-fn", "pallet-staking-runtime-api", "pallet-state-trie-migration", "pallet-statement", "pallet-sudo", "pallet-timestamp", "pallet-tips", "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", "pallet-transaction-storage", "pallet-treasury", "pallet-tx-pause", "pallet-uniques", "pallet-utility", "pallet-vesting", "pallet-whitelist", "pallet-xcm", "pallet-xcm-benchmarks", "pallet-xcm-bridge-hub", "pallet-xcm-bridge-hub-router", "parachains-common", "polkadot-core-primitives", "polkadot-parachain-primitives", "polkadot-primitives", "polkadot-runtime-common", "polkadot-runtime-metrics", "polkadot-runtime-parachains", "polkadot-sdk-frame", "rococo-runtime-constants", "sc-chain-spec-derive", "sc-tracing-proc-macro", "slot-range-helper", "snowbridge-beacon-primitives", "snowbridge-core", "snowbridge-ethereum", "snowbridge-outbound-queue-merkle-tree", "snowbridge-outbound-queue-runtime-api", "snowbridge-pallet-ethereum-client", "snowbridge-pallet-ethereum-client-fixtures", "snowbridge-pallet-inbound-queue", "snowbridge-pallet-inbound-queue-fixtures", "snowbridge-pallet-outbound-queue", "snowbridge-pallet-system", "snowbridge-router-primitives", "snowbridge-runtime-common", "snowbridge-system-runtime-api", "sp-api", "sp-api-proc-macro", "sp-application-crypto", "sp-arithmetic", "sp-authority-discovery", "sp-block-builder", "sp-consensus-aura", "sp-consensus-babe", "sp-consensus-beefy", "sp-consensus-grandpa", "sp-consensus-pow", "sp-consensus-slots", "sp-core", "sp-crypto-ec-utils", "sp-crypto-hashing", "sp-crypto-hashing-proc-macro", "sp-debug-derive", "sp-externalities", "sp-genesis-builder", "sp-inherents", "sp-io", "sp-keyring", "sp-keystore", "sp-metadata-ir", "sp-mixnet", "sp-mmr-primitives", "sp-npos-elections", "sp-offchain", "sp-runtime", "sp-runtime-interface", "sp-runtime-interface-proc-macro", "sp-session", "sp-staking", "sp-state-machine", "sp-statement-store", "sp-std", "sp-storage", "sp-timestamp", "sp-tracing", "sp-transaction-pool", "sp-transaction-storage-proof", "sp-trie", "sp-version", "sp-version-proc-macro", "sp-wasm-interface", "sp-weights", "staging-parachain-info", "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", "substrate-bip39", "testnet-parachains-constants", "tracing-gum-proc-macro", "westend-runtime-constants", "xcm-fee-payment-runtime-api", "xcm-procedural"] +runtime = ["assets-common", "binary-merkle-tree", "bp-asset-hub-rococo", "bp-asset-hub-westend", "bp-bridge-hub-cumulus", "bp-bridge-hub-kusama", "bp-bridge-hub-polkadot", "bp-bridge-hub-rococo", "bp-bridge-hub-westend", "bp-header-chain", "bp-kusama", "bp-messages", "bp-parachains", "bp-polkadot", "bp-polkadot-bulletin", "bp-polkadot-core", "bp-relayers", "bp-rococo", "bp-runtime", "bp-test-utils", "bp-westend", "bp-xcm-bridge-hub", "bp-xcm-bridge-hub-router", "bridge-hub-common", "bridge-runtime-common", "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", "cumulus-pallet-parachain-system-proc-macro", "cumulus-pallet-session-benchmarking", "cumulus-pallet-solo-to-para", "cumulus-pallet-xcm", "cumulus-pallet-xcmp-queue", "cumulus-ping", "cumulus-primitives-aura", "cumulus-primitives-core", "cumulus-primitives-parachain-inherent", "cumulus-primitives-proof-size-hostfunction", "cumulus-primitives-storage-weight-reclaim", "cumulus-primitives-timestamp", "cumulus-primitives-utility", "frame-benchmarking", "frame-benchmarking-pallet-pov", "frame-election-provider-solution-type", "frame-election-provider-support", "frame-executive", "frame-metadata-hash-extension", "frame-support", "frame-support-procedural", "frame-support-procedural-tools-derive", "frame-system", "frame-system-benchmarking", "frame-system-rpc-runtime-api", "frame-try-runtime", "pallet-alliance", "pallet-asset-conversion", "pallet-asset-conversion-ops", "pallet-asset-conversion-tx-payment", "pallet-asset-rate", "pallet-asset-tx-payment", "pallet-assets", "pallet-assets-freezer", "pallet-atomic-swap", "pallet-aura", "pallet-authority-discovery", "pallet-authorship", "pallet-babe", "pallet-bags-list", "pallet-balances", "pallet-beefy", "pallet-beefy-mmr", "pallet-bounties", "pallet-bridge-grandpa", "pallet-bridge-messages", "pallet-bridge-parachains", "pallet-bridge-relayers", "pallet-broker", "pallet-child-bounties", "pallet-collator-selection", "pallet-collective", "pallet-collective-content", "pallet-contracts", "pallet-contracts-proc-macro", "pallet-contracts-uapi", "pallet-conviction-voting", "pallet-core-fellowship", "pallet-delegated-staking", "pallet-democracy", "pallet-dev-mode", "pallet-election-provider-multi-phase", "pallet-election-provider-support-benchmarking", "pallet-elections-phragmen", "pallet-fast-unstake", "pallet-glutton", "pallet-grandpa", "pallet-identity", "pallet-im-online", "pallet-indices", "pallet-insecure-randomness-collective-flip", "pallet-lottery", "pallet-membership", "pallet-message-queue", "pallet-migrations", "pallet-mixnet", "pallet-mmr", "pallet-multisig", "pallet-nft-fractionalization", "pallet-nfts", "pallet-nfts-runtime-api", "pallet-nis", "pallet-node-authorization", "pallet-nomination-pools", "pallet-nomination-pools-benchmarking", "pallet-nomination-pools-runtime-api", "pallet-offences", "pallet-offences-benchmarking", "pallet-paged-list", "pallet-parameters", "pallet-preimage", "pallet-proxy", "pallet-ranked-collective", "pallet-recovery", "pallet-referenda", "pallet-remark", "pallet-root-offences", "pallet-root-testing", "pallet-safe-mode", "pallet-salary", "pallet-scheduler", "pallet-scored-pool", "pallet-session", "pallet-session-benchmarking", "pallet-skip-feeless-payment", "pallet-society", "pallet-staking", "pallet-staking-reward-curve", "pallet-staking-reward-fn", "pallet-staking-runtime-api", "pallet-state-trie-migration", "pallet-statement", "pallet-sudo", "pallet-timestamp", "pallet-tips", "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", "pallet-transaction-storage", "pallet-treasury", "pallet-tx-pause", "pallet-uniques", "pallet-utility", "pallet-vesting", "pallet-whitelist", "pallet-xcm", "pallet-xcm-benchmarks", "pallet-xcm-bridge-hub", "pallet-xcm-bridge-hub-router", "parachains-common", "polkadot-core-primitives", "polkadot-parachain-primitives", "polkadot-primitives", "polkadot-runtime-common", "polkadot-runtime-metrics", "polkadot-runtime-parachains", "polkadot-sdk-frame", "rococo-runtime-constants", "sc-chain-spec-derive", "sc-tracing-proc-macro", "slot-range-helper", "snowbridge-beacon-primitives", "snowbridge-core", "snowbridge-ethereum", "snowbridge-outbound-queue-merkle-tree", "snowbridge-outbound-queue-runtime-api", "snowbridge-pallet-ethereum-client", "snowbridge-pallet-ethereum-client-fixtures", "snowbridge-pallet-inbound-queue", "snowbridge-pallet-inbound-queue-fixtures", "snowbridge-pallet-outbound-queue", "snowbridge-pallet-system", "snowbridge-router-primitives", "snowbridge-runtime-common", "snowbridge-system-runtime-api", "sp-api", "sp-api-proc-macro", "sp-application-crypto", "sp-arithmetic", "sp-authority-discovery", "sp-block-builder", "sp-consensus-aura", "sp-consensus-babe", "sp-consensus-beefy", "sp-consensus-grandpa", "sp-consensus-pow", "sp-consensus-slots", "sp-core", "sp-crypto-ec-utils", "sp-crypto-hashing", "sp-crypto-hashing-proc-macro", "sp-debug-derive", "sp-externalities", "sp-genesis-builder", "sp-inherents", "sp-io", "sp-keyring", "sp-keystore", "sp-metadata-ir", "sp-mixnet", "sp-mmr-primitives", "sp-npos-elections", "sp-offchain", "sp-runtime", "sp-runtime-interface", "sp-runtime-interface-proc-macro", "sp-session", "sp-staking", "sp-state-machine", "sp-statement-store", "sp-std", "sp-storage", "sp-timestamp", "sp-tracing", "sp-transaction-pool", "sp-transaction-storage-proof", "sp-trie", "sp-version", "sp-version-proc-macro", "sp-wasm-interface", "sp-weights", "staging-parachain-info", "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", "substrate-bip39", "testnet-parachains-constants", "tracing-gum-proc-macro", "westend-runtime-constants", "xcm-fee-payment-runtime-api", "xcm-procedural"] node = ["asset-test-utils", "bridge-hub-test-utils", "cumulus-client-cli", "cumulus-client-collator", "cumulus-client-consensus-aura", "cumulus-client-consensus-common", "cumulus-client-consensus-proposer", "cumulus-client-consensus-relay-chain", "cumulus-client-network", "cumulus-client-parachain-inherent", "cumulus-client-pov-recovery", "cumulus-client-service", "cumulus-relay-chain-inprocess-interface", "cumulus-relay-chain-interface", "cumulus-relay-chain-minimal-node", "cumulus-relay-chain-rpc-interface", "cumulus-test-relay-sproof-builder", "emulated-integration-tests-common", "fork-tree", "frame-benchmarking-cli", "frame-remote-externalities", "frame-support-procedural-tools", "generate-bags", "mmr-gadget", "mmr-rpc", "pallet-contracts-mock-network", "pallet-transaction-payment-rpc", "parachains-runtimes-test-utils", "polkadot-approval-distribution", "polkadot-availability-bitfield-distribution", "polkadot-availability-distribution", "polkadot-availability-recovery", "polkadot-cli", "polkadot-collator-protocol", "polkadot-dispute-distribution", "polkadot-erasure-coding", "polkadot-gossip-support", "polkadot-network-bridge", "polkadot-node-collation-generation", "polkadot-node-core-approval-voting", "polkadot-node-core-av-store", "polkadot-node-core-backing", "polkadot-node-core-bitfield-signing", "polkadot-node-core-candidate-validation", "polkadot-node-core-chain-api", "polkadot-node-core-chain-selection", "polkadot-node-core-dispute-coordinator", "polkadot-node-core-parachains-inherent", "polkadot-node-core-prospective-parachains", "polkadot-node-core-provisioner", "polkadot-node-core-pvf", "polkadot-node-core-pvf-checker", "polkadot-node-core-pvf-common", "polkadot-node-core-pvf-execute-worker", "polkadot-node-core-pvf-prepare-worker", "polkadot-node-core-runtime-api", "polkadot-node-jaeger", "polkadot-node-metrics", "polkadot-node-network-protocol", "polkadot-node-primitives", "polkadot-node-subsystem", "polkadot-node-subsystem-types", "polkadot-node-subsystem-util", "polkadot-overseer", "polkadot-rpc", "polkadot-service", "polkadot-statement-distribution", "polkadot-statement-table", "sc-allocator", "sc-authority-discovery", "sc-basic-authorship", "sc-block-builder", "sc-chain-spec", "sc-cli", "sc-client-api", "sc-client-db", "sc-consensus", "sc-consensus-aura", "sc-consensus-babe", "sc-consensus-babe-rpc", "sc-consensus-beefy", "sc-consensus-beefy-rpc", "sc-consensus-epochs", "sc-consensus-grandpa", "sc-consensus-grandpa-rpc", "sc-consensus-manual-seal", "sc-consensus-pow", "sc-consensus-slots", "sc-executor", "sc-executor-common", "sc-executor-polkavm", "sc-executor-wasmtime", "sc-informant", "sc-keystore", "sc-mixnet", "sc-network", "sc-network-common", "sc-network-gossip", "sc-network-light", "sc-network-statement", "sc-network-sync", "sc-network-transactions", "sc-network-types", "sc-offchain", "sc-proposer-metrics", "sc-rpc", "sc-rpc-api", "sc-rpc-server", "sc-rpc-spec-v2", "sc-service", "sc-state-db", "sc-statement-store", "sc-storage-monitor", "sc-sync-state-rpc", "sc-sysinfo", "sc-telemetry", "sc-tracing", "sc-transaction-pool", "sc-transaction-pool-api", "sc-utils", "snowbridge-runtime-test-common", "sp-blockchain", "sp-consensus", "sp-core-hashing", "sp-core-hashing-proc-macro", "sp-database", "sp-maybe-compressed-blob", "sp-panic-handler", "sp-rpc", "staging-chain-spec-builder", "staging-node-inspect", "staging-tracking-allocator", "std", "subkey", "substrate-build-script-utils", "substrate-frame-rpc-support", "substrate-frame-rpc-system", "substrate-prometheus-endpoint", "substrate-rpc-client", "substrate-state-trie-migration-rpc", "substrate-wasm-builder", "tracing-gum", "xcm-emulator", "xcm-simulator"] tuples-96 = [ "frame-support-procedural?/tuples-96", @@ -854,6 +857,11 @@ path = "../substrate/frame/assets" default-features = false optional = true +[dependencies.pallet-assets-freezer] +path = "../substrate/frame/assets-freezer" +default-features = false +optional = true + [dependencies.pallet-atomic-swap] path = "../substrate/frame/atomic-swap" default-features = false diff --git a/umbrella/src/lib.rs b/umbrella/src/lib.rs index 78b34ba179b7..87fcd4214dbc 100644 --- a/umbrella/src/lib.rs +++ b/umbrella/src/lib.rs @@ -360,6 +360,10 @@ pub use pallet_asset_tx_payment; #[cfg(feature = "pallet-assets")] pub use pallet_assets; +/// Provides freezing features to `pallet-assets`. +#[cfg(feature = "pallet-assets-freezer")] +pub use pallet_assets_freezer; + /// FRAME atomic swap pallet. #[cfg(feature = "pallet-atomic-swap")] pub use pallet_atomic_swap;