Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Fix treasury spend origin #3130

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
16 changes: 4 additions & 12 deletions runtime/moonbase/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -558,9 +558,10 @@ parameter_types! {
pub const ProposalBond: Permill = Permill::from_percent(5);
pub const TreasuryId: PalletId = PalletId(*b"pc/trsry");
pub TreasuryAccount: AccountId = Treasury::account_id();
pub const MaxSpendBalance: crate::Balance = crate::Balance::max_value();
}

type TreasuryRejectOrigin = EitherOfDiverse<
type RootOrTreasuryCouncilOrigin = EitherOfDiverse<
EnsureRoot<AccountId>,
pallet_collective::EnsureProportionMoreThan<AccountId, TreasuryCouncilInstance, 1, 2>,
>;
Expand All @@ -569,19 +570,16 @@ impl pallet_treasury::Config for Runtime {
type PalletId = TreasuryId;
type Currency = Balances;
// More than half of the council is required (or root) to reject a proposal
type RejectOrigin = TreasuryRejectOrigin;
type RejectOrigin = RootOrTreasuryCouncilOrigin;
type RuntimeEvent = RuntimeEvent;
type SpendPeriod = ConstU32<{ 6 * DAYS }>;
type Burn = ();
type BurnDestination = ();
type MaxApprovals = ConstU32<100>;
type WeightInfo = moonbase_weights::pallet_treasury::WeightInfo<Runtime>;
type SpendFunds = ();
#[cfg(not(feature = "runtime-benchmarks"))]
type SpendOrigin = frame_support::traits::NeverEnsureOrigin<Balance>; // Disabled, no spending
#[cfg(feature = "runtime-benchmarks")]
type SpendOrigin =
frame_system::EnsureWithSuccess<EnsureRoot<AccountId>, AccountId, benches::MaxBalance>;
frame_system::EnsureWithSuccess<RootOrTreasuryCouncilOrigin, AccountId, MaxSpendBalance>;
type AssetKind = ();
type Beneficiary = AccountId;
type BeneficiaryLookup = IdentityLookup<AccountId>;
Expand Down Expand Up @@ -1190,12 +1188,6 @@ impl Contains<RuntimeCall> for NormalFilter {
// Note: It is also assumed that EVM calls are only allowed through `Origin::Root` so
// this can be seen as an additional security
RuntimeCall::EVM(_) => false,
RuntimeCall::Treasury(
pallet_treasury::Call::spend { .. }
| pallet_treasury::Call::payout { .. }
| pallet_treasury::Call::check_status { .. }
| pallet_treasury::Call::void_spend { .. },
) => false,
_ => true,
}
}
Expand Down
172 changes: 170 additions & 2 deletions runtime/moonbase/tests/integration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ use frame_support::{
assert_noop, assert_ok,
dispatch::DispatchClass,
traits::{
fungible::Inspect, Currency as CurrencyT, EnsureOrigin, PalletInfo, StorageInfo,
StorageInfoTrait,
fungible::Inspect, Currency as CurrencyT, EnsureOrigin, OnInitialize, PalletInfo,
StorageInfo, StorageInfoTrait,
},
weights::{constants::WEIGHT_REF_TIME_PER_SECOND, Weight},
StorageHasher, Twox128,
Expand All @@ -56,6 +56,7 @@ use moonbase_runtime::{
System,
TransactionPayment,
TransactionPaymentAsGasPrice,
Treasury,
TreasuryCouncilCollective,
XcmTransactor,
FOREIGN_ASSET_PRECOMPILE_ADDRESS_PREFIX,
Expand Down Expand Up @@ -2973,6 +2974,173 @@ fn validate_transaction_fails_on_filtered_call() {
});
}

#[cfg(test)]
mod treasury_tests {
use super::*;
use sp_runtime::traits::Hash;

fn expect_events(events: Vec<RuntimeEvent>) {
let block_events: Vec<RuntimeEvent> =
System::events().into_iter().map(|r| r.event).collect();

dbg!(events.clone());
dbg!(block_events.clone());

assert!(events.iter().all(|evt| block_events.contains(evt)))
}

fn next_block() {
System::reset_events();
System::set_block_number(System::block_number() + 1u32);
System::on_initialize(System::block_number());
Treasury::on_initialize(System::block_number());
}

#[test]
fn test_treasury_spend_local_with_root_origin() {
let initial_treasury_balance = 1_000 * UNIT;
ExtBuilder::default()
.with_balances(vec![
(AccountId::from(ALICE), 2_000 * UNIT),
(Treasury::account_id(), initial_treasury_balance),
])
.build()
.execute_with(|| {
let spend_amount = 100u128 * UNIT;
let spend_beneficiary = AccountId::from(BOB);

next_block();

// Perform treasury spending

assert_ok!(moonbase_runtime::Sudo::sudo(
root_origin(),
Box::new(RuntimeCall::Treasury(pallet_treasury::Call::spend {
amount: spend_amount,
asset_kind: Box::new(()),
beneficiary: Box::new(AccountId::from(BOB)),
valid_from: Some(5u32),
}))
));

let payout_period =
<<Runtime as pallet_treasury::Config>::PayoutPeriod as Get<u32>>::get();
let expected_events = [RuntimeEvent::Treasury(
pallet_treasury::Event::AssetSpendApproved {
index: 0,
asset_kind: (),
amount: spend_amount,
beneficiary: spend_beneficiary,
valid_from: 5u32,
expire_at: payout_period + 5u32,
},
)]
.to_vec();
expect_events(expected_events);

while System::block_number() < 5u32 {
next_block();
}

assert_ok!(Treasury::payout(origin_of(spend_beneficiary), 0));

let expected_events = [
RuntimeEvent::Treasury(pallet_treasury::Event::Paid {
index: 0,
payment_id: (),
}),
RuntimeEvent::Balances(pallet_balances::Event::Transfer {
from: Treasury::account_id(),
to: spend_beneficiary,
amount: spend_amount,
}),
]
.to_vec();
expect_events(expected_events);
});
}

#[test]
fn test_treasury_spend_local_with_council_origin() {
let initial_treasury_balance = 1_000 * UNIT;
ExtBuilder::default()
.with_balances(vec![
(AccountId::from(ALICE), 2_000 * UNIT),
(Treasury::account_id(), initial_treasury_balance),
])
.build()
.execute_with(|| {
let spend_amount = 100u128 * UNIT;
let spend_beneficiary = AccountId::from(BOB);

next_block();

// TreasuryCouncilCollective
assert_ok!(TreasuryCouncilCollective::set_members(
root_origin(),
vec![AccountId::from(ALICE)],
Some(AccountId::from(ALICE)),
1
));

next_block();

// Perform treasury spending
let proposal = RuntimeCall::Treasury(pallet_treasury::Call::spend {
amount: spend_amount,
asset_kind: Box::new(()),
beneficiary: Box::new(AccountId::from(BOB)),
valid_from: Some(5u32),
});
assert_ok!(TreasuryCouncilCollective::propose(
origin_of(AccountId::from(ALICE)),
1,
Box::new(proposal.clone()),
1_000
));

let payout_period =
<<Runtime as pallet_treasury::Config>::PayoutPeriod as Get<u32>>::get();
let expected_events = [
RuntimeEvent::Treasury(pallet_treasury::Event::AssetSpendApproved {
index: 0,
asset_kind: (),
amount: spend_amount,
beneficiary: spend_beneficiary,
valid_from: 5u32,
expire_at: payout_period + 5u32,
}),
RuntimeEvent::TreasuryCouncilCollective(pallet_collective::Event::Executed {
proposal_hash: sp_runtime::traits::BlakeTwo256::hash_of(&proposal),
result: Ok(()),
}),
]
.to_vec();
expect_events(expected_events);

while System::block_number() < 5u32 {
next_block();
}

assert_ok!(Treasury::payout(origin_of(spend_beneficiary), 0));

let expected_events = [
RuntimeEvent::Treasury(pallet_treasury::Event::Paid {
index: 0,
payment_id: (),
}),
RuntimeEvent::Balances(pallet_balances::Event::Transfer {
from: Treasury::account_id(),
to: spend_beneficiary,
amount: spend_amount,
}),
]
.to_vec();
expect_events(expected_events);
});
}
}

#[cfg(test)]
mod fee_tests {
use super::*;
Expand Down
16 changes: 4 additions & 12 deletions runtime/moonbeam/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -546,9 +546,10 @@ parameter_types! {
pub const ProposalBond: Permill = Permill::from_percent(5);
pub const TreasuryId: PalletId = PalletId(*b"py/trsry");
pub TreasuryAccount: AccountId = Treasury::account_id();
pub const MaxSpendBalance: crate::Balance = crate::Balance::max_value();
}

type TreasuryRejectOrigin = EitherOfDiverse<
type RootOrTreasuryCouncilOrigin = EitherOfDiverse<
EnsureRoot<AccountId>,
pallet_collective::EnsureProportionMoreThan<AccountId, TreasuryCouncilInstance, 1, 2>,
>;
Expand All @@ -557,19 +558,16 @@ impl pallet_treasury::Config for Runtime {
type PalletId = TreasuryId;
type Currency = Balances;
// More than half of the council is required (or root) to reject a proposal
type RejectOrigin = TreasuryRejectOrigin;
type RejectOrigin = RootOrTreasuryCouncilOrigin;
type RuntimeEvent = RuntimeEvent;
type SpendPeriod = ConstU32<{ 6 * DAYS }>;
type Burn = ();
type BurnDestination = ();
type MaxApprovals = ConstU32<100>;
type WeightInfo = moonbeam_weights::pallet_treasury::WeightInfo<Runtime>;
type SpendFunds = ();
#[cfg(not(feature = "runtime-benchmarks"))]
type SpendOrigin = frame_support::traits::NeverEnsureOrigin<Balance>; // Disabled, no spending
#[cfg(feature = "runtime-benchmarks")]
type SpendOrigin =
frame_system::EnsureWithSuccess<EnsureRoot<AccountId>, AccountId, benches::MaxBalance>;
frame_system::EnsureWithSuccess<RootOrTreasuryCouncilOrigin, AccountId, MaxSpendBalance>;
type AssetKind = ();
type Beneficiary = AccountId;
type BeneficiaryLookup = IdentityLookup<AccountId>;
Expand Down Expand Up @@ -1193,12 +1191,6 @@ impl Contains<RuntimeCall> for NormalFilter {
// Note: It is also assumed that EVM calls are only allowed through `Origin::Root` so
// this can be seen as an additional security
RuntimeCall::EVM(_) => false,
RuntimeCall::Treasury(
pallet_treasury::Call::spend { .. }
| pallet_treasury::Call::payout { .. }
| pallet_treasury::Call::check_status { .. }
| pallet_treasury::Call::void_spend { .. },
) => false,
_ => true,
}
}
Expand Down
Loading
Loading