From 7789054f53961c9762cb0e9fcae682b1d88ab9b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Fri, 4 Jul 2025 01:11:54 -0500 Subject: [PATCH 1/7] feat(fc-pallet-black-hole): introduce `fc-pallet-black-hole` --- Cargo.lock | 10 ++ pallets/black-hole/Cargo.toml | 36 ++++++ pallets/black-hole/src/benchmarking.rs | 77 +++++++++++++ pallets/black-hole/src/lib.rs | 152 +++++++++++++++++++++++++ pallets/black-hole/src/mock.rs | 78 +++++++++++++ pallets/black-hole/src/tests.rs | 85 ++++++++++++++ pallets/black-hole/src/weights.rs | 40 +++++++ 7 files changed, 478 insertions(+) create mode 100644 pallets/black-hole/Cargo.toml create mode 100644 pallets/black-hole/src/benchmarking.rs create mode 100644 pallets/black-hole/src/lib.rs create mode 100644 pallets/black-hole/src/mock.rs create mode 100644 pallets/black-hole/src/tests.rs create mode 100644 pallets/black-hole/src/weights.rs diff --git a/Cargo.lock b/Cargo.lock index 7e02a29..0c3555f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1143,6 +1143,16 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "fc-pallet-black-hole" +version = "1.0.0" +dependencies = [ + "pallet-balances", + "parity-scale-codec", + "polkadot-sdk-frame", + "scale-info", +] + [[package]] name = "fc-pallet-communities" version = "1.0.0" diff --git a/pallets/black-hole/Cargo.toml b/pallets/black-hole/Cargo.toml new file mode 100644 index 0000000..f5d7953 --- /dev/null +++ b/pallets/black-hole/Cargo.toml @@ -0,0 +1,36 @@ +[package] +authors.workspace = true +description = "A pallet that periodically burns the balance on its account" +edition.workspace = true +license.workspace = true +name = "fc-pallet-black-hole" +repository.workspace = true +version = "1.0.0" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec.workspace = true +frame.workspace = true +scale-info.workspace = true + +[dev-dependencies] +pallet-balances.workspace = true + +[features] +default = ["std"] +runtime-benchmarks = [ + "frame/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", +] +std = [ + "codec/std", + "frame/std", + "pallet-balances/std", + "scale-info/std", +] +try-runtime = [ + "frame/try-runtime", + "pallet-balances/try-runtime", +] diff --git a/pallets/black-hole/src/benchmarking.rs b/pallets/black-hole/src/benchmarking.rs new file mode 100644 index 0000000..50b3f89 --- /dev/null +++ b/pallets/black-hole/src/benchmarking.rs @@ -0,0 +1,77 @@ +use crate::{Config, Event, Pallet}; +use alloc::vec; +use frame::{ + benchmarking::prelude::*, + prelude::fungible::{Inspect, Mutate}, +}; + +fn assert_last_event(generic_event: ::RuntimeEvent) { + frame_system::Pallet::::assert_last_event(generic_event.into()); +} + +#[benchmarks] +mod benchmarks { + use super::*; + use crate::frame_system::Call as SystemCall; + + #[benchmark] + pub fn initialize() -> Result<(), BenchmarkError> { + #[block] + { + Pallet::::initialize(); + } + + Ok(()) + } + + #[benchmark] + pub fn burn() -> Result<(), BenchmarkError> { + // Setup code + Pallet::::initialize(); + T::Balances::mint_into( + &Pallet::::event_horizon(), + T::Balances::minimum_balance(), + )?; + + #[block] + { + Pallet::::burn(); + } + + // Verify code + assert_last_event::(Event::::BalanceBurned.into()); + + Ok(()) + } + + #[benchmark] + pub fn dispatch_as_event_horizon() -> Result<(), BenchmarkError> { + // Setup code + let origin = T::EventHorizonDispatchOrigin::try_successful_origin() + .map_err(|_| DispatchError::BadOrigin)?; + let call: Box = + Box::new(SystemCall::remark_with_event { remark: vec![] }.into()); + + #[block] + { + Pallet::::dispatch_as_event_horizon(origin, call)?; + } + + // Verify code + frame_system::Pallet::::assert_last_event( + frame_system::Event::::Remarked { + sender: Pallet::::event_horizon(), + hash: T::Hashing::hash(&[]), + } + .into(), + ); + + Ok(()) + } + + impl_benchmark_test_suite!( + Pallet, + frame::deps::sp_io::TestExternalities::default(), + crate::mock::Test + ); +} diff --git a/pallets/black-hole/src/lib.rs b/pallets/black-hole/src/lib.rs new file mode 100644 index 0000000..5fe87d6 --- /dev/null +++ b/pallets/black-hole/src/lib.rs @@ -0,0 +1,152 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +//! # Black Hole Pallet +//! +//! This pallet owns an account, which receives transfers from other accounts. Then, periodically +//! it burns the balance the pallet account owns. + +extern crate alloc; + +use frame::prelude::*; + +#[cfg(feature = "runtime-benchmarks")] +pub mod benchmarking; + +#[cfg(test)] +mod mock; +#[cfg(test)] +mod tests; + +pub mod weights; +pub use weights::*; + +pub use pallet::*; + +#[frame::pallet] +pub mod pallet { + use super::*; + use frame::traits::{Block, Header}; + use fungible::{Inspect, Mutate}; + + pub(crate) type SystemBlockNumberFor = + <<::Block as Block>::Header as Header>::Number; + pub(crate) type BlockNumberFor = + <::BlockNumberProvider as BlockNumberProvider>::BlockNumber; + + #[pallet::config] + pub trait Config: frame_system::Config { + // Primitives: Some overarching types that come from the system (or the system depends on). + + /// The overarching runtime event type + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// The Weight info + type WeightInfo: WeightInfo; + + // Origins: Types that manage authorization rules to allow or deny some caller origins to + // execute a method. + + /// The origin allowed dispatching a call on behalf of the pallet account (a.k.a. the event + /// horizon). + type EventHorizonDispatchOrigin: EnsureOrigin; + + // Dependencies: The external components this pallet depends on. + + /// The native balance system. + type Balances: Mutate; + /// The provider of the block number. + type BlockNumberProvider: BlockNumberProvider; + + // Parameters: A set of constant parameters to configure limits. + + /// An id for this pallet. + #[pallet::constant] + type PalletId: Get; + /// The burn period. After at least the given number of blocks since the last burn elapsed, + /// the burn mechanism will take place. + #[pallet::constant] + type BurnPeriod: Get>; + } + + #[pallet::storage] + pub type LastBurn = StorageValue<_, BlockNumberFor, ValueQuery>; + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + BalanceBurned, + } + + #[pallet::hooks] + impl Hooks> for Pallet { + fn on_initialize(_: SystemBlockNumberFor) -> Weight { + Self::initialize() + } + + fn on_idle(_: SystemBlockNumberFor, remaining_weight: Weight) -> Weight { + if remaining_weight.all_lt(T::WeightInfo::burn()) { + return Zero::zero(); + } + Self::burn() + } + } + + #[pallet::call] + impl Pallet { + #[pallet::call_index(0)] + #[pallet::weight({ + let di = call.get_dispatch_info(); + let weight = T::WeightInfo::dispatch_as_event_horizon() + .saturating_add(T::DbWeight::get().reads_writes(1, 1)) + .saturating_add(di.call_weight); + (weight, di.class) + })] + pub fn dispatch_as_event_horizon( + origin: OriginFor, + call: Box, + ) -> DispatchResult { + T::EventHorizonDispatchOrigin::ensure_origin(origin)?; + + call.dispatch(frame_system::RawOrigin::Signed(Self::event_horizon()).into()) + .map(|_| ()) + .map_err(|e| e.error) + } + } + + impl Pallet { + pub fn event_horizon() -> T::AccountId { + T::PalletId::get().into_account_truncating() + } + + pub(crate) fn initialize() -> Weight { + if !frame_system::Pallet::::account_exists(&Self::event_horizon()) { + frame_system::Pallet::::inc_providers(&Self::event_horizon()); + } + T::WeightInfo::initialize() + } + + pub(crate) fn burn() -> Weight { + if LastBurn::::get().le(&T::BlockNumberProvider::current_block_number() + .saturating_sub(T::BurnPeriod::get())) + { + let burn_account = Self::event_horizon(); + + // Just burn it. + let _ = T::Balances::burn_from( + &burn_account, + T::Balances::total_balance(&burn_account), + Preservation::Expendable, + Precision::Exact, + Fortitude::Force, + ); + + LastBurn::::set(T::BlockNumberProvider::current_block_number()); + Self::deposit_event(Event::::BalanceBurned); + } + + T::WeightInfo::burn() + } + } +} diff --git a/pallets/black-hole/src/mock.rs b/pallets/black-hole/src/mock.rs new file mode 100644 index 0000000..fc5f425 --- /dev/null +++ b/pallets/black-hole/src/mock.rs @@ -0,0 +1,78 @@ +pub use crate as fc_pallet_black_hole; + +use frame::{ + deps::sp_runtime::{traits::Verify, MultiSignature}, + testing_prelude::*, +}; + +#[frame_construct_runtime] +pub mod runtime { + #[runtime::runtime] + #[runtime::derive( + RuntimeCall, + RuntimeEvent, + RuntimeError, + RuntimeOrigin, + RuntimeTask, + RuntimeHoldReason, + RuntimeFreezeReason + )] + pub struct Test; + + #[runtime::pallet_index(0)] + pub type System = frame_system; + #[runtime::pallet_index(10)] + pub type Balances = pallet_balances; + #[runtime::pallet_index(11)] + pub type BlackHole = fc_pallet_black_hole; +} + +pub type Block = MockBlock; +pub type AccountPublic = ::Signer; +pub type AccountId = ::AccountId; +pub type Balance = ::Balance; + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl frame_system::Config for Test { + type Block = Block; + type Lookup = IdentityLookup; + type AccountId = AccountId; + type AccountData = pallet_balances::AccountData; +} + +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] +impl pallet_balances::Config for Test { + type AccountStore = System; +} + +parameter_types! { + pub BlackHolePalletId: PalletId = PalletId(*b"burn____"); +} + +impl fc_pallet_black_hole::Config for Test { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); + type EventHorizonDispatchOrigin = EnsureRoot; + type Balances = Balances; + type BlockNumberProvider = System; + type PalletId = BlackHolePalletId; + type BurnPeriod = ConstU64<10>; +} + +pub fn new_test_ext() -> TestExternalities { + let mut t = TestExternalities::default(); + t.execute_with(|| System::set_block_number(1)); + t +} + +pub fn run_to_block(n: u64) { + while System::block_number() < n { + BlackHole::on_idle(System::block_number(), Weight::MAX); + System::on_finalize(System::block_number()); + + System::set_block_number(System::block_number() + 1); + + System::on_initialize(System::block_number()); + BlackHole::on_initialize(System::block_number()); + } +} diff --git a/pallets/black-hole/src/tests.rs b/pallets/black-hole/src/tests.rs new file mode 100644 index 0000000..7888e14 --- /dev/null +++ b/pallets/black-hole/src/tests.rs @@ -0,0 +1,85 @@ +use crate::mock::*; +use frame::prelude::DispatchError; +use frame::testing_prelude::{ + assert_noop, assert_ok, frame_system, + fungible::{Inspect, Mutate}, +}; +use frame::token::Preservation::Preserve; + +const ALICE: AccountId = AccountId::new([1u8; 32]); + +#[test] +fn burning_works() { + new_test_ext().execute_with(|| { + // ALICE has funds + assert_ok!(Balances::mint_into(&ALICE, 10)); + assert_eq!(Balances::total_balance(&ALICE), 10); + assert_eq!(Balances::total_issuance(), 10); + + // When the pallet is installed, the account does not exist. + assert!(!System::account_exists(&BlackHole::event_horizon())); + + run_to_block(2); + // The account exists after `on_initialize` + assert!(System::account_exists(&BlackHole::event_horizon())); + + run_to_block(10); + // If the pallet receives some funds to burn… + assert_ok!(Balances::transfer( + &ALICE, + &BlackHole::event_horizon(), + 5, + Preserve + )); + assert_eq!(Balances::total_balance(&ALICE), 5); + assert_eq!(Balances::total_balance(&BlackHole::event_horizon()), 5); + assert_eq!(Balances::total_issuance(), 10); + + run_to_block(12); + // Poof! Funds are now burned. + assert_eq!(Balances::total_balance(&ALICE), 5); + assert_eq!(Balances::total_balance(&BlackHole::event_horizon()), 0); + assert_eq!(Balances::total_issuance(), 5); + + System::assert_has_event( + pallet_balances::Event::::Burned { + who: BlackHole::event_horizon(), + amount: 5, + } + .into(), + ); + System::assert_last_event(fc_pallet_black_hole::Event::::BalanceBurned.into()); + }) +} + +#[test] +fn dispatch_as_event_horizon_fails_if_bad_origin() { + new_test_ext().execute_with(|| { + assert_noop!( + BlackHole::dispatch_as_event_horizon( + RuntimeOrigin::signed(ALICE), + Box::new(frame_system::Call::remark_with_event { remark: vec![] }.into()), + ), + DispatchError::BadOrigin + ); + }) +} + +#[test] +fn dispatch_as_event_horizon_works() { + new_test_ext().execute_with(|| { + assert_ok!(Balances::mint_into(&BlackHole::event_horizon(), 100)); + assert_ok!(BlackHole::dispatch_as_event_horizon( + RuntimeOrigin::root(), + Box::new( + pallet_balances::Call::transfer_keep_alive { + dest: ALICE, + value: 50 + } + .into() + ), + )); + + assert_eq!(Balances::total_balance(&ALICE), 50); + }) +} diff --git a/pallets/black-hole/src/weights.rs b/pallets/black-hole/src/weights.rs new file mode 100644 index 0000000..a164c27 --- /dev/null +++ b/pallets/black-hole/src/weights.rs @@ -0,0 +1,40 @@ +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame::weights_prelude::*; + +/// Weight functions needed for fc_pallet_black_hole. +pub trait WeightInfo { + fn dispatch_as_event_horizon() -> Weight; + fn initialize() -> Weight; + fn burn() -> Weight; +} + +/// Weights for fc_pallet_black_hole using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + fn dispatch_as_event_horizon() -> Weight { + Weight::from_parts(8_586_000, 0) + } + fn initialize() -> Weight { + Weight::from_parts(8_586_000, 0) + } + fn burn() -> Weight { + Weight::from_parts(1_359, 0) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + fn dispatch_as_event_horizon() -> Weight { + Weight::from_parts(8_586_000, 0) + } + fn initialize() -> Weight { + Weight::from_parts(8_586_000, 0) + } + fn burn() -> Weight { + Weight::from_parts(1_359, 0) + } +} \ No newline at end of file From 425142bc9eb9a9c531b9fb716ca10d58ae90055b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Fri, 4 Jul 2025 01:22:15 -0500 Subject: [PATCH 2/7] feat(fc-pallet-black-hole): count the accumulate burned balance --- pallets/black-hole/src/lib.rs | 10 ++++++- pallets/black-hole/src/tests.rs | 47 +++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/pallets/black-hole/src/lib.rs b/pallets/black-hole/src/lib.rs index 5fe87d6..3b3d799 100644 --- a/pallets/black-hole/src/lib.rs +++ b/pallets/black-hole/src/lib.rs @@ -28,6 +28,8 @@ pub mod pallet { use frame::traits::{Block, Header}; use fungible::{Inspect, Mutate}; + pub(crate) type AccountIdOf = ::AccountId; + pub(crate) type BalanceOf = <::Balances as Inspect>>::Balance; pub(crate) type SystemBlockNumberFor = <<::Block as Block>::Header as Header>::Number; pub(crate) type BlockNumberFor = @@ -67,8 +69,12 @@ pub mod pallet { type BurnPeriod: Get>; } + /// The last time a burn happened (0 if never). #[pallet::storage] pub type LastBurn = StorageValue<_, BlockNumberFor, ValueQuery>; + /// Counts the accumulated balance that's been burned so far. + #[pallet::storage] + pub type BlackHoleMass = StorageValue<_, BalanceOf, ValueQuery>; #[pallet::pallet] pub struct Pallet(_); @@ -132,16 +138,18 @@ pub mod pallet { .saturating_sub(T::BurnPeriod::get())) { let burn_account = Self::event_horizon(); + let burn_balance = T::Balances::total_balance(&burn_account); // Just burn it. let _ = T::Balances::burn_from( &burn_account, - T::Balances::total_balance(&burn_account), + burn_balance, Preservation::Expendable, Precision::Exact, Fortitude::Force, ); + BlackHoleMass::::set(BlackHoleMass::::get().saturating_add(burn_balance)); LastBurn::::set(T::BlockNumberProvider::current_block_number()); Self::deposit_event(Event::::BalanceBurned); } diff --git a/pallets/black-hole/src/tests.rs b/pallets/black-hole/src/tests.rs index 7888e14..88dd8a4 100644 --- a/pallets/black-hole/src/tests.rs +++ b/pallets/black-hole/src/tests.rs @@ -1,4 +1,5 @@ use crate::mock::*; +use crate::BlackHoleMass; use frame::prelude::DispatchError; use frame::testing_prelude::{ assert_noop, assert_ok, frame_system, @@ -52,6 +53,52 @@ fn burning_works() { }) } +#[test] +fn counts_the_burned_mass() { + new_test_ext().execute_with(|| { + // ALICE has funds + assert_ok!(Balances::mint_into(&ALICE, 21)); + + run_to_block(2); + assert_ok!(Balances::transfer( + &ALICE, + &BlackHole::event_horizon(), + 5, + Preserve + )); + + run_to_block(12); + assert_ok!(Balances::transfer( + &ALICE, + &BlackHole::event_horizon(), + 5, + Preserve + )); + + run_to_block(22); + assert_ok!(Balances::transfer( + &ALICE, + &BlackHole::event_horizon(), + 5, + Preserve + )); + + run_to_block(32); + assert_ok!(Balances::transfer( + &ALICE, + &BlackHole::event_horizon(), + 5, + Preserve + )); + + assert_eq!(Balances::total_issuance(), 6); + assert_eq!(Balances::total_balance(&ALICE), 1); + assert_eq!(Balances::total_balance(&BlackHole::event_horizon()), 5); + + assert_eq!(BlackHoleMass::::get(), 15); + }) +} + #[test] fn dispatch_as_event_horizon_fails_if_bad_origin() { new_test_ext().execute_with(|| { From ff8e15b16662b538fd5f2d2f611bc446c3758b47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Fri, 4 Jul 2025 01:25:56 -0500 Subject: [PATCH 3/7] chore(fc-pallet-black-hole): initialize is not really necessary --- pallets/black-hole/src/benchmarking.rs | 11 ----------- pallets/black-hole/src/lib.rs | 11 ----------- pallets/black-hole/src/tests.rs | 7 ------- pallets/black-hole/src/weights.rs | 7 ------- 4 files changed, 36 deletions(-) diff --git a/pallets/black-hole/src/benchmarking.rs b/pallets/black-hole/src/benchmarking.rs index 50b3f89..bbe782e 100644 --- a/pallets/black-hole/src/benchmarking.rs +++ b/pallets/black-hole/src/benchmarking.rs @@ -14,20 +14,9 @@ mod benchmarks { use super::*; use crate::frame_system::Call as SystemCall; - #[benchmark] - pub fn initialize() -> Result<(), BenchmarkError> { - #[block] - { - Pallet::::initialize(); - } - - Ok(()) - } - #[benchmark] pub fn burn() -> Result<(), BenchmarkError> { // Setup code - Pallet::::initialize(); T::Balances::mint_into( &Pallet::::event_horizon(), T::Balances::minimum_balance(), diff --git a/pallets/black-hole/src/lib.rs b/pallets/black-hole/src/lib.rs index 3b3d799..4f708a1 100644 --- a/pallets/black-hole/src/lib.rs +++ b/pallets/black-hole/src/lib.rs @@ -87,10 +87,6 @@ pub mod pallet { #[pallet::hooks] impl Hooks> for Pallet { - fn on_initialize(_: SystemBlockNumberFor) -> Weight { - Self::initialize() - } - fn on_idle(_: SystemBlockNumberFor, remaining_weight: Weight) -> Weight { if remaining_weight.all_lt(T::WeightInfo::burn()) { return Zero::zero(); @@ -126,13 +122,6 @@ pub mod pallet { T::PalletId::get().into_account_truncating() } - pub(crate) fn initialize() -> Weight { - if !frame_system::Pallet::::account_exists(&Self::event_horizon()) { - frame_system::Pallet::::inc_providers(&Self::event_horizon()); - } - T::WeightInfo::initialize() - } - pub(crate) fn burn() -> Weight { if LastBurn::::get().le(&T::BlockNumberProvider::current_block_number() .saturating_sub(T::BurnPeriod::get())) diff --git a/pallets/black-hole/src/tests.rs b/pallets/black-hole/src/tests.rs index 88dd8a4..a2fb74e 100644 --- a/pallets/black-hole/src/tests.rs +++ b/pallets/black-hole/src/tests.rs @@ -17,13 +17,6 @@ fn burning_works() { assert_eq!(Balances::total_balance(&ALICE), 10); assert_eq!(Balances::total_issuance(), 10); - // When the pallet is installed, the account does not exist. - assert!(!System::account_exists(&BlackHole::event_horizon())); - - run_to_block(2); - // The account exists after `on_initialize` - assert!(System::account_exists(&BlackHole::event_horizon())); - run_to_block(10); // If the pallet receives some funds to burn… assert_ok!(Balances::transfer( diff --git a/pallets/black-hole/src/weights.rs b/pallets/black-hole/src/weights.rs index a164c27..562b913 100644 --- a/pallets/black-hole/src/weights.rs +++ b/pallets/black-hole/src/weights.rs @@ -8,7 +8,6 @@ use frame::weights_prelude::*; /// Weight functions needed for fc_pallet_black_hole. pub trait WeightInfo { fn dispatch_as_event_horizon() -> Weight; - fn initialize() -> Weight; fn burn() -> Weight; } @@ -18,9 +17,6 @@ impl WeightInfo for SubstrateWeight { fn dispatch_as_event_horizon() -> Weight { Weight::from_parts(8_586_000, 0) } - fn initialize() -> Weight { - Weight::from_parts(8_586_000, 0) - } fn burn() -> Weight { Weight::from_parts(1_359, 0) } @@ -31,9 +27,6 @@ impl WeightInfo for () { fn dispatch_as_event_horizon() -> Weight { Weight::from_parts(8_586_000, 0) } - fn initialize() -> Weight { - Weight::from_parts(8_586_000, 0) - } fn burn() -> Weight { Weight::from_parts(1_359, 0) } From 7007913896248745737351206a4641e1b92b893f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Fri, 4 Jul 2025 12:14:29 -0500 Subject: [PATCH 4/7] chore(fc-pallet-black-hole): apply requested changes from @olanod --- pallets/black-hole/src/lib.rs | 8 ++++++++ pallets/black-hole/src/tests.rs | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/pallets/black-hole/src/lib.rs b/pallets/black-hole/src/lib.rs index 4f708a1..d10fd38 100644 --- a/pallets/black-hole/src/lib.rs +++ b/pallets/black-hole/src/lib.rs @@ -110,6 +110,7 @@ pub mod pallet { call: Box, ) -> DispatchResult { T::EventHorizonDispatchOrigin::ensure_origin(origin)?; + Self::do_initialize(); call.dispatch(frame_system::RawOrigin::Signed(Self::event_horizon()).into()) .map(|_| ()) @@ -122,6 +123,13 @@ pub mod pallet { T::PalletId::get().into_account_truncating() } + #[inline] + fn do_initialize() { + if !frame_system::Pallet::::account_exists(&Self::event_horizon()) { + frame_system::Pallet::::inc_providers(&Self::event_horizon()); + } + } + pub(crate) fn burn() -> Weight { if LastBurn::::get().le(&T::BlockNumberProvider::current_block_number() .saturating_sub(T::BurnPeriod::get())) diff --git a/pallets/black-hole/src/tests.rs b/pallets/black-hole/src/tests.rs index a2fb74e..3bb3eb1 100644 --- a/pallets/black-hole/src/tests.rs +++ b/pallets/black-hole/src/tests.rs @@ -29,6 +29,10 @@ fn burning_works() { assert_eq!(Balances::total_balance(&BlackHole::event_horizon()), 5); assert_eq!(Balances::total_issuance(), 10); + // Assert that the pallet still has the funds before running `on_idle`. + run_to_block(11); + assert_eq!(Balances::total_balance(&BlackHole::event_horizon()), 5); + run_to_block(12); // Poof! Funds are now burned. assert_eq!(Balances::total_balance(&ALICE), 5); From 0b44b38d00b55da6a26ac6a14905d9e01124fb94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Fri, 4 Jul 2025 12:53:04 -0500 Subject: [PATCH 5/7] chore(fc-pallet-black-hole): Add README.md --- pallets/black-hole/Cargo.toml | 1 + pallets/black-hole/README.md | 64 +++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 pallets/black-hole/README.md diff --git a/pallets/black-hole/Cargo.toml b/pallets/black-hole/Cargo.toml index f5d7953..2be5018 100644 --- a/pallets/black-hole/Cargo.toml +++ b/pallets/black-hole/Cargo.toml @@ -5,6 +5,7 @@ edition.workspace = true license.workspace = true name = "fc-pallet-black-hole" repository.workspace = true +readme = "README.md" version = "1.0.0" [package.metadata.docs.rs] diff --git a/pallets/black-hole/README.md b/pallets/black-hole/README.md new file mode 100644 index 0000000..cd558a8 --- /dev/null +++ b/pallets/black-hole/README.md @@ -0,0 +1,64 @@ +# 🕳️ Black Hole Pallet + +The **Black Hole** pallet defines a sink account that receives token transfers and periodically burns its entire +balance, removing tokens from circulation. This mechanism can be useful for implementing economic sinks or deflationary +behaviors within a runtime. + +## ✨ Overview + +- The pallet owns an account known as the **event horizon**. +- Any user or module can transfer tokens to this account. +- Periodically (after a configurable number of blocks), the balance of the account is **burned** automatically. +- Authorized origins can dispatch extrinsic calls **as if** signed by the event horizon account. + +## 🔧 Features + +- **Automated burning** of all tokens in the event horizon account every `BurnPeriod` blocks. +- **Total burned balance tracking** via `BlackHoleMass`. +- **Dispatch proxying** to allow controlled usage of the event horizon identity. + +## ⚙️ Configuration + +This pallet requires the following types and parameters to be configured in the runtime: + +| Type / Constant | Description | +|------------------------------|-------------------------------------------------------------| +| `RuntimeEvent` | Event type for this pallet | +| `WeightInfo` | Weight cost functions | +| `EventHorizonDispatchOrigin` | Origin authorized to proxy calls as the event horizon | +| `Balances` | A `fungible::Mutate` implementation (e.g., native balances) | +| `BlockNumberProvider` | Source of current block number | +| `PalletId` | Unique identifier for deriving the event horizon account | +| `BurnPeriod` | Number of blocks between automatic burns | + +## 🧠 Storage + +| Item | Description | +|-----------------|----------------------------------------------| +| `LastBurn` | Block number of the last burn event | +| `BlackHoleMass` | Total amount of tokens burned by this pallet | + +## 📦 Dispatchable Functions + +### `dispatch_as_event_horizon` + +```rust +fn dispatch_as_event_horizon(origin, call) +``` + +Allows an authorized origin to dispatch a call **on behalf of the event horizon account**. This enables tightly +controlled usage of the burn account in more complex workflows. + +## 🔁 Runtime Hooks + +- The pallet implements `on_idle`, and will attempt to burn the balance in the event horizon account whenever sufficient + weight is available. + +## 🔒 Security Notes + +- The pallet does not restrict who can transfer funds to the event horizon account. +- Only the `EventHorizonDispatchOrigin` can perform calls as the event horizon, preventing abuse. + +## 🧪 Testing and Benchmarking + +- Includes unit tests and benchmarking support behind `runtime-benchmarks`. From 643de0246f3323ad222ae8df35dc66209aded999 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Fri, 4 Jul 2025 13:13:40 -0500 Subject: [PATCH 6/7] feat(fc-pallet-black-hole): add a mapper that includes the black hole address as 0x0000...00 --- Cargo.lock | 670 +++++++++++++++++++++-- Cargo.toml | 1 + pallets/black-hole/Cargo.toml | 23 +- pallets/black-hole/src/address_mapper.rs | 39 ++ pallets/black-hole/src/lib.rs | 2 + 5 files changed, 685 insertions(+), 50 deletions(-) create mode 100644 pallets/black-hole/src/address_mapper.rs diff --git a/Cargo.lock b/Cargo.lock index 0c3555f..74da461 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -65,6 +65,91 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +[[package]] +name = "alloy-core" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d8bcce99ad10fe02640cfaec1c6bc809b837c783c1d52906aa5af66e2a196f6" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", +] + +[[package]] +name = "alloy-primitives" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c77490fe91a0ce933a1f219029521f20fc28c2c0ca95d53fa4da9c00b8d9d4e" +dependencies = [ + "bytes", + "cfg-if", + "const-hex", + "derive_more 2.0.1", + "itoa", + "paste", + "ruint", + "tiny-keccak", +] + +[[package]] +name = "alloy-sol-macro" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10ae8e9a91d328ae954c22542415303919aabe976fe7a92eb06db1b68fd59f2" +dependencies = [ + "alloy-sol-macro-expander", + "alloy-sol-macro-input", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "alloy-sol-macro-expander" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83ad5da86c127751bc607c174d6c9fe9b85ef0889a9ca0c641735d77d4f98f26" +dependencies = [ + "alloy-sol-macro-input", + "const-hex", + "heck", + "indexmap", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.101", + "syn-solidity", + "tiny-keccak", +] + +[[package]] +name = "alloy-sol-macro-input" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3d30f0d3f9ba3b7686f3ff1de9ee312647aac705604417a2f40c604f409a9e" +dependencies = [ + "const-hex", + "dunce", + "heck", + "macro-string", + "proc-macro2", + "quote", + "syn 2.0.101", + "syn-solidity", +] + +[[package]] +name = "alloy-sol-types" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d43d5e60466a440230c07761aa67671d4719d46f43be8ea6e7ed334d8db4a9ab" +dependencies = [ + "alloy-primitives", + "alloy-sol-macro", + "const-hex", +] + [[package]] name = "anstream" version = "0.6.18" @@ -397,7 +482,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" dependencies = [ "num-traits", - "rand", + "rand 0.8.5", ] [[package]] @@ -407,7 +492,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "246a225cc6131e9ee4f24619af0f19d67761fff15d7ccc22e42b80846e69449a" dependencies = [ "num-traits", - "rand", + "rand 0.8.5", ] [[package]] @@ -420,7 +505,7 @@ dependencies = [ "ark-serialize 0.5.0", "ark-std 0.5.0", "digest 0.10.7", - "rand_core", + "rand_core 0.6.4", "sha3", ] @@ -437,7 +522,7 @@ dependencies = [ "ark-serialize 0.5.0", "ark-std 0.5.0", "digest 0.10.7", - "rand_chacha", + "rand_chacha 0.3.1", "sha2 0.10.9", "w3f-ring-proof", "zeroize", @@ -687,6 +772,19 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2382f75942f4b3be3690fe4f86365e9c853c1587d6ee58212cebf6e2a9ccd101" +[[package]] +name = "const-hex" +version = "1.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83e22e0ed40b96a48d3db274f72fd365bd78f67af39b6bbd47e8a15e1c6207ff" +dependencies = [ + "cfg-if", + "cpufeatures", + "hex", + "proptest", + "serde", +] + [[package]] name = "const-oid" version = "0.9.6" @@ -739,6 +837,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + [[package]] name = "cpufeatures" version = "0.2.17" @@ -761,7 +865,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ "generic-array", - "rand_core", + "rand_core 0.6.4", "subtle", "zeroize", ] @@ -773,7 +877,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", - "rand_core", + "rand_core 0.6.4", "typenum", ] @@ -866,13 +970,35 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "derive_more" +version = "0.99.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.101", +] + [[package]] name = "derive_more" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" dependencies = [ - "derive_more-impl", + "derive_more-impl 1.0.0", +] + +[[package]] +name = "derive_more" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +dependencies = [ + "derive_more-impl 2.0.1", ] [[package]] @@ -886,6 +1012,18 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "derive_more-impl" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", + "unicode-xid", +] + [[package]] name = "digest" version = "0.9.0" @@ -934,6 +1072,12 @@ dependencies = [ "walkdir", ] +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + [[package]] name = "dyn-clonable" version = "0.9.2" @@ -1010,7 +1154,7 @@ dependencies = [ "ed25519", "hashbrown 0.14.5", "hex", - "rand_core", + "rand_core 0.6.4", "sha2 0.10.9", "zeroize", ] @@ -1046,7 +1190,7 @@ dependencies = [ "generic-array", "group", "pkcs8", - "rand_core", + "rand_core 0.6.4", "sec1", "serdect", "subtle", @@ -1128,6 +1272,47 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +[[package]] +name = "ethabi-decode" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52029c4087f9f01108f851d0d02df9c21feb5660a19713466724b7f95bd2d773" +dependencies = [ + "ethereum-types", + "tiny-keccak", +] + +[[package]] +name = "ethbloom" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c321610643004cf908ec0f5f2aa0d8f1f8e14b540562a2887a1111ff1ecbf7b" +dependencies = [ + "crunchy", + "fixed-hash", + "impl-codec", + "impl-rlp", + "impl-serde", + "scale-info", + "tiny-keccak", +] + +[[package]] +name = "ethereum-types" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ab15ed80916029f878e0267c3a9f92b67df55e79af370bf66199059ae2b4ee3" +dependencies = [ + "ethbloom", + "fixed-hash", + "impl-codec", + "impl-rlp", + "impl-serde", + "primitive-types", + "scale-info", + "uint", +] + [[package]] name = "expander" version = "2.2.1" @@ -1148,6 +1333,7 @@ name = "fc-pallet-black-hole" version = "1.0.0" dependencies = [ "pallet-balances", + "pallet-revive", "parity-scale-codec", "polkadot-sdk-frame", "scale-info", @@ -1406,7 +1592,7 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" dependencies = [ - "rand_core", + "rand_core 0.6.4", "subtle", ] @@ -1449,7 +1635,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" dependencies = [ "byteorder", - "rand", + "rand 0.8.5", "rustc-hex", "static_assertions", ] @@ -1824,8 +2010,8 @@ version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ea1015b5a70616b688dc230cfe50c8af89d972cb132d5a622814d29773b10b9" dependencies = [ - "rand", - "rand_core", + "rand 0.8.5", + "rand_core 0.6.4", ] [[package]] @@ -1841,7 +2027,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ "ff", - "rand_core", + "rand_core 0.6.4", "subtle", ] @@ -1968,6 +2154,15 @@ dependencies = [ "uint", ] +[[package]] +name = "impl-rlp" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54ed8ad1f3877f7e775b8cbf30ed1bd3209a95401817f19a0eb4402d13f8cf90" +dependencies = [ + "rlp", +] + [[package]] name = "impl-serde" version = "0.5.0" @@ -2117,6 +2312,9 @@ name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] [[package]] name = "libc" @@ -2124,6 +2322,12 @@ version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" +[[package]] +name = "libm" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" + [[package]] name = "libsecp256k1" version = "0.7.2" @@ -2137,7 +2341,7 @@ dependencies = [ "libsecp256k1-core", "libsecp256k1-gen-ecmult", "libsecp256k1-gen-genmult", - "rand", + "rand 0.8.5", "serde", "sha2 0.9.9", "typenum", @@ -2197,6 +2401,17 @@ version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +[[package]] +name = "macro-string" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "macro_magic" version = "0.5.1" @@ -2287,7 +2502,7 @@ checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" dependencies = [ "byteorder", "keccak", - "rand_core", + "rand_core 0.6.4", "zeroize", ] @@ -2406,6 +2621,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -2445,6 +2661,24 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "pallet-asset-conversion" +version = "22.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e063e39ad8ecd3c2b00c963f50cdf79e614c819a01e1c1ce9993287075b1b4d9" +dependencies = [ + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", +] + [[package]] name = "pallet-assets" version = "42.0.0" @@ -2600,6 +2834,69 @@ dependencies = [ "sp-runtime", ] +[[package]] +name = "pallet-revive" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ff67ac7b1053411a2bd2f5438cc0fa0c58757ec0c51efa551f1cfcd9ebc7ee3" +dependencies = [ + "alloy-core", + "derive_more 0.99.20", + "environmental", + "ethabi-decode", + "ethereum-types", + "frame-support", + "frame-system", + "hex-literal", + "impl-trait-for-tuples", + "log", + "num-bigint", + "num-integer", + "num-traits", + "pallet-revive-proc-macro", + "pallet-revive-uapi", + "pallet-transaction-payment", + "parity-scale-codec", + "paste", + "polkavm", + "ripemd", + "rlp", + "scale-info", + "serde", + "sp-api", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", + "staging-xcm", + "staging-xcm-builder", + "substrate-bn", +] + +[[package]] +name = "pallet-revive-proc-macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63c2dc2fc6961da23fefc54689ce81a8e006f6988bc465dcc9ab9db905d31766" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "pallet-revive-uapi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cb8f45102c6279f59f55e0051fc6c26b996619d7842800dfaf3a2583459a1c7" +dependencies = [ + "bitflags 1.3.2", + "pallet-revive-proc-macro", + "parity-scale-codec", + "polkavm-derive 0.21.0", + "scale-info", +] + [[package]] name = "pallet-scheduler" version = "41.0.0" @@ -2684,8 +2981,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e69bf016dc406eff7d53a7d3f7cf1c2e72c82b9088aac1118591e36dd2cd3e9" dependencies = [ "bitcoin_hashes", - "rand", - "rand_core", + "rand 0.8.5", + "rand_core 0.6.4", "serde", "unicode-normalization", ] @@ -2755,7 +3052,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" dependencies = [ "base64ct", - "rand_core", + "rand_core 0.6.4", "subtle", ] @@ -2797,6 +3094,35 @@ dependencies = [ "spki", ] +[[package]] +name = "polkadot-core-primitives" +version = "17.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7c519ee804fd08d7464871bd2fe164e8f0683501ea59d2a10f5ef214dacb3b" +dependencies = [ + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", +] + +[[package]] +name = "polkadot-parachain-primitives" +version = "16.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72943c0948c686b47bacb1a03e59baff63bfba2e16e208d77f0f8615827f8564" +dependencies = [ + "bounded-collections", + "derive_more 0.99.20", + "parity-scale-codec", + "polkadot-core-primitives", + "scale-info", + "serde", + "sp-core", + "sp-runtime", + "sp-weights", +] + [[package]] name = "polkadot-sdk-frame" version = "0.9.1" @@ -2833,19 +3159,60 @@ dependencies = [ "sp-version", ] +[[package]] +name = "polkavm" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfd34e2f74206fff33482ae1718e275f11365ef8c4de7f0e69217f8845303867" +dependencies = [ + "libc", + "log", + "polkavm-assembler", + "polkavm-common 0.21.0", + "polkavm-linux-raw", +] + +[[package]] +name = "polkavm-assembler" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f512bc80cb10439391a7c13a9eb2d37cf66b7305e7df0a06d662eff4f5b07625" +dependencies = [ + "log", +] + [[package]] name = "polkavm-common" version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31ff33982a807d8567645d4784b9b5d7ab87bcb494f534a57cadd9012688e102" +[[package]] +name = "polkavm-common" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c16b809cfd398f861261c045a8745e6c78b71ea7e0d3ef6f7cc553eb27bc17e" +dependencies = [ + "log", + "polkavm-assembler", +] + [[package]] name = "polkavm-derive" version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2eb703f3b6404c13228402e98a5eae063fd16b8f58afe334073ec105ee4117e" dependencies = [ - "polkavm-derive-impl-macro", + "polkavm-derive-impl-macro 0.18.0", +] + +[[package]] +name = "polkavm-derive" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47239245f87329541932c0d7fec750a66a75b13aa87dfe4fbfd637bab86ad387" +dependencies = [ + "polkavm-derive-impl-macro 0.21.0", ] [[package]] @@ -2854,7 +3221,19 @@ version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f2116a92e6e96220a398930f4c8a6cda1264206f3e2034fc9982bfd93f261f7" dependencies = [ - "polkavm-common", + "polkavm-common 0.18.0", + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "polkavm-derive-impl" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24fd6c6215450c3e57511df5c38a82eb4bde208de15ee15046ac33852f3c3eaa" +dependencies = [ + "polkavm-common 0.21.0", "proc-macro2", "quote", "syn 2.0.101", @@ -2866,10 +3245,26 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c16669ddc7433e34c1007d31080b80901e3e8e523cb9d4b441c3910cf9294b" dependencies = [ - "polkavm-derive-impl", + "polkavm-derive-impl 0.18.1", + "syn 2.0.101", +] + +[[package]] +name = "polkavm-derive-impl-macro" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36837f6b7edfd6f4498f8d25d81da16cf03bd6992c3e56f3d477dfc90f4fefca" +dependencies = [ + "polkavm-derive-impl 0.21.0", "syn 2.0.101", ] +[[package]] +name = "polkavm-linux-raw" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be6cd1d48c5e7814d287a3e12a339386a5dfa2f3ac72f932335f4cf56467f1b3" + [[package]] name = "portable-atomic" version = "1.11.0" @@ -2919,6 +3314,7 @@ dependencies = [ "fixed-hash", "impl-codec", "impl-num-traits", + "impl-rlp", "impl-serde", "scale-info", "uint", @@ -2957,6 +3353,28 @@ dependencies = [ "version_check", ] +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "proc-macro-warning" version = "1.84.1" @@ -2977,6 +3395,20 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "proptest" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fcdab19deb5195a31cf7726a210015ff1496ba1464fd42cb4f537b8b01b471f" +dependencies = [ + "bitflags 2.9.0", + "num-traits", + "rand 0.9.1", + "rand_chacha 0.9.0", + "rand_xorshift", + "unarray", +] + [[package]] name = "quote" version = "1.0.40" @@ -3005,8 +3437,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +dependencies = [ + "rand_core 0.9.3", ] [[package]] @@ -3016,7 +3457,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", ] [[package]] @@ -3028,6 +3479,21 @@ dependencies = [ "getrandom 0.2.16", ] +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" + +[[package]] +name = "rand_xorshift" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" +dependencies = [ + "rand_core 0.9.3", +] + [[package]] name = "rawpointer" version = "0.2.1" @@ -3117,6 +3583,46 @@ dependencies = [ "subtle", ] +[[package]] +name = "ripemd" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "rlp" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa24e92bb2a83198bb76d661a71df9f7076b8c420b8696e4d3d97d50d94479e3" +dependencies = [ + "bytes", + "rustc-hex", +] + +[[package]] +name = "ruint" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11256b5fe8c68f56ac6f39ef0720e592f33d2367a4782740d9c9142e889c7fb4" +dependencies = [ + "proptest", + "rand 0.8.5", + "rand 0.9.1", + "ruint-macro", + "serde", + "valuable", + "zeroize", +] + +[[package]] +name = "ruint-macro" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" + [[package]] name = "rustc-demangle" version = "0.1.24" @@ -3176,7 +3682,7 @@ checksum = "346a3b32eba2640d17a9cb5927056b08f3de90f65b72fe09402c2ad07d684d0b" dependencies = [ "bitvec", "cfg-if", - "derive_more", + "derive_more 1.0.0", "parity-scale-codec", "scale-info-derive", "serde", @@ -3217,7 +3723,7 @@ dependencies = [ "curve25519-dalek", "getrandom_or_panic", "merlin", - "rand_core", + "rand_core 0.6.4", "serde_bytes", "sha2 0.10.9", "subtle", @@ -3394,7 +3900,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ "digest 0.10.7", - "rand_core", + "rand_core 0.6.4", ] [[package]] @@ -3602,7 +4108,7 @@ dependencies = [ "parking_lot", "paste", "primitive-types", - "rand", + "rand 0.8.5", "scale-info", "schnorrkel", "secp256k1", @@ -3708,7 +4214,7 @@ dependencies = [ "libsecp256k1", "log", "parity-scale-codec", - "polkavm-derive", + "polkavm-derive 0.18.0", "rustversion", "secp256k1", "sp-core", @@ -3793,7 +4299,7 @@ dependencies = [ "num-traits", "parity-scale-codec", "paste", - "rand", + "rand 0.8.5", "scale-info", "serde", "simple-mermaid", @@ -3817,7 +4323,7 @@ dependencies = [ "bytes", "impl-trait-for-tuples", "parity-scale-codec", - "polkavm-derive", + "polkavm-derive 0.18.0", "primitive-types", "sp-externalities", "sp-runtime-interface-proc-macro", @@ -3881,7 +4387,7 @@ dependencies = [ "log", "parity-scale-codec", "parking_lot", - "rand", + "rand 0.8.5", "smallvec", "sp-core", "sp-externalities", @@ -3958,7 +4464,7 @@ dependencies = [ "nohash-hasher", "parity-scale-codec", "parking_lot", - "rand", + "rand 0.8.5", "scale-info", "schnellru", "sp-core", @@ -4027,6 +4533,12 @@ dependencies = [ "sp-debug-derive", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + [[package]] name = "spki" version = "0.7.3" @@ -4054,9 +4566,9 @@ dependencies = [ [[package]] name = "staging-xcm" -version = "16.1.0" +version = "16.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dead7481ba2dec11b0df89745cef3a76f3eef9c9df20155426cd7e9651b4c799" +checksum = "d0126278d7fc6d7dec55e5a109f838bbf401dd084aecf2597e4e11ea07515a0a" dependencies = [ "array-bytes", "bounded-collections", @@ -4074,6 +4586,51 @@ dependencies = [ "xcm-procedural", ] +[[package]] +name = "staging-xcm-builder" +version = "20.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f031952c1496cf7f86d19ab38e3264be9a54b7d8eecb25ba69f977cc7549d08" +dependencies = [ + "environmental", + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "pallet-asset-conversion", + "pallet-transaction-payment", + "parity-scale-codec", + "polkadot-parachain-primitives", + "scale-info", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", + "sp-weights", + "staging-xcm", + "staging-xcm-executor", + "tracing", +] + +[[package]] +name = "staging-xcm-executor" +version = "19.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af9bc315e8c7018fcfe0371ce4b7e726fb699e37b2acc3e5effb87a7d131a3ff" +dependencies = [ + "environmental", + "frame-support", + "impl-trait-for-tuples", + "parity-scale-codec", + "scale-info", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", + "sp-weights", + "staging-xcm", + "tracing", +] + [[package]] name = "static_assertions" version = "1.1.0" @@ -4115,6 +4672,19 @@ dependencies = [ "zeroize", ] +[[package]] +name = "substrate-bn" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b5bbfa79abbae15dd642ea8176a21a635ff3c00059961d1ea27ad04e5b441c" +dependencies = [ + "byteorder", + "crunchy", + "lazy_static", + "rand 0.8.5", + "rustc-hex", +] + [[package]] name = "subtle" version = "2.6.1" @@ -4143,6 +4713,18 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn-solidity" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4560533fbd6914b94a8fb5cc803ed6801c3455668db3b810702c57612bac9412" +dependencies = [ + "paste", + "proc-macro2", + "quote", + "syn 2.0.101", +] + [[package]] name = "tap" version = "1.0.1" @@ -4388,7 +4970,7 @@ checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ "cfg-if", "digest 0.10.7", - "rand", + "rand 0.8.5", "static_assertions", ] @@ -4410,6 +4992,12 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + [[package]] name = "unicode-ident" version = "1.0.18" @@ -4463,9 +5051,9 @@ dependencies = [ "ark-serialize-derive 0.4.2", "arrayref", "digest 0.10.7", - "rand", - "rand_chacha", - "rand_core", + "rand 0.8.5", + "rand_chacha 0.3.1", + "rand_core 0.6.4", "sha2 0.10.9", "sha3", "zeroize", @@ -4497,7 +5085,7 @@ dependencies = [ "ark-serialize 0.5.0", "ark-std 0.5.0", "getrandom_or_panic", - "rand_core", + "rand_core 0.6.4", "w3f-pcs", ] diff --git a/Cargo.toml b/Cargo.toml index cf725dc..394d89a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,6 +46,7 @@ pallet-balances = { version = "41.1.0", default-features = false } pallet-nfts = { version = "34.1.0", default-features = false } pallet-preimage = { version = "40.0.0", default-features = false } pallet-referenda = { version = "40.1.0", default-features = false } +pallet-revive = { version = "0.6.1", default-features = false } pallet-scheduler = { version = "41.0.0", default-features = false } pallet-timestamp = { version = "39.0.0", default-features = false } pallet-transaction-payment = { version = "40.0.0", default-features = false } diff --git a/pallets/black-hole/Cargo.toml b/pallets/black-hole/Cargo.toml index 2be5018..5cac668 100644 --- a/pallets/black-hole/Cargo.toml +++ b/pallets/black-hole/Cargo.toml @@ -15,23 +15,28 @@ targets = ["x86_64-unknown-linux-gnu"] codec.workspace = true frame.workspace = true scale-info.workspace = true +pallet-revive = { workspace = true, optional = true } [dev-dependencies] pallet-balances.workspace = true [features] -default = ["std"] +default = ["std", "revive"] +revive = ["pallet-revive"] runtime-benchmarks = [ - "frame/runtime-benchmarks", - "pallet-balances/runtime-benchmarks", + "frame/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-revive?/runtime-benchmarks" ] std = [ - "codec/std", - "frame/std", - "pallet-balances/std", - "scale-info/std", + "codec/std", + "frame/std", + "pallet-balances/std", + "scale-info/std", + "pallet-revive?/std" ] try-runtime = [ - "frame/try-runtime", - "pallet-balances/try-runtime", + "frame/try-runtime", + "pallet-balances/try-runtime", + "pallet-revive?/try-runtime" ] diff --git a/pallets/black-hole/src/address_mapper.rs b/pallets/black-hole/src/address_mapper.rs new file mode 100644 index 0000000..2be87d3 --- /dev/null +++ b/pallets/black-hole/src/address_mapper.rs @@ -0,0 +1,39 @@ +use super::*; +use pallet_revive::{AddressMapper, Config as ReviveConfig}; + +pub struct AddressMapperWithBlackHole(PhantomData<(T, M)>); + +impl> AddressMapper + for AddressMapperWithBlackHole +{ + fn to_address(account_id: &T::AccountId) -> H160 { + if account_id.eq(&Pallet::::event_horizon()) { + return H160([0; 20]); + } + + M::to_address(account_id) + } + + fn to_account_id(address: &H160) -> T::AccountId { + if address.is_zero() { + return Pallet::::event_horizon(); + } + M::to_account_id(&address) + } + + fn to_fallback_account_id(address: &H160) -> T::AccountId { + M::to_fallback_account_id(&address) + } + + fn map(account_id: &T::AccountId) -> frame::deps::sp_runtime::DispatchResult { + M::map(account_id) + } + + fn unmap(account_id: &T::AccountId) -> frame::deps::sp_runtime::DispatchResult { + M::unmap(account_id) + } + + fn is_mapped(account_id: &T::AccountId) -> bool { + account_id.eq(&Pallet::::event_horizon()) || M::is_mapped(account_id) + } +} diff --git a/pallets/black-hole/src/lib.rs b/pallets/black-hole/src/lib.rs index d10fd38..105a24c 100644 --- a/pallets/black-hole/src/lib.rs +++ b/pallets/black-hole/src/lib.rs @@ -17,7 +17,9 @@ mod mock; #[cfg(test)] mod tests; +mod address_mapper; pub mod weights; + pub use weights::*; pub use pallet::*; From a2361d8b65afe537fd6c214e2c8c7d66ad1bfa08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Andr=C3=A9s=20Dorado=20Su=C3=A1rez?= Date: Fri, 4 Jul 2025 13:14:05 -0500 Subject: [PATCH 7/7] feat(fc-pallet-black-hole): add a mapper that includes the black hole address as 0x0000...00 --- pallets/black-hole/src/address_mapper.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pallets/black-hole/src/address_mapper.rs b/pallets/black-hole/src/address_mapper.rs index 2be87d3..46645bf 100644 --- a/pallets/black-hole/src/address_mapper.rs +++ b/pallets/black-hole/src/address_mapper.rs @@ -10,7 +10,6 @@ impl> AddressMapper if account_id.eq(&Pallet::::event_horizon()) { return H160([0; 20]); } - M::to_address(account_id) } @@ -22,6 +21,9 @@ impl> AddressMapper } fn to_fallback_account_id(address: &H160) -> T::AccountId { + if address.is_zero() { + return Pallet::::event_horizon(); + } M::to_fallback_account_id(&address) }