Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion chain-metadata.json

Large diffs are not rendered by default.

10 changes: 9 additions & 1 deletion runtime-modules/content/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,9 +297,13 @@ decl_error! {
InsufficientBalanceForTransfer,

/// Cannot create the channel: channel creator has insufficient balance
/// (budget for state bloat bond + data objs statebloat bonds + data objs storage fees)
/// (budget for channel state bloat bond + channel data objs state bloat bonds + data objs storage fees)
InsufficientBalanceForChannelCreation,

/// Cannot create the video: video creator has insufficient balance
/// (budget for video state bloat bond + video data objs state bloat bonds + data objs storage fees)
InsufficientBalanceForVideoCreation,

// Insufficient council budget to cover channel reward claim
InsufficientCouncilBudget,

Expand Down Expand Up @@ -333,7 +337,11 @@ decl_error! {

/// Patronage can only be claimed if channel is owned by a member
PatronageCanOnlyBeClaimedForMemberOwnedChannels,

/// Invalid extrinsic call: Channel state bloat bond changed.
ChannelStateBloatBondChanged,

/// Invalid extrinsic call: video state bloat bond changed.
VideoStateBloatBondChanged
}
}
75 changes: 68 additions & 7 deletions runtime-modules/content/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,13 +155,16 @@ decl_storage! {

pub Commitment get(fn commitment): <T as frame_system::Config>::Hash;

/// The state bloat bond for the channel (helps preventing the state bloat).
pub ChannelStateBloatBondValue get (fn channel_state_bloat_bond_value): BalanceOf<T>;

///The state bloat bond for the video (helps preventing the state bloat).
pub VideoStateBloatBondValue get (fn video_state_bloat_bond_value): BalanceOf<T>;

pub MaxCashoutAllowed get(fn max_cashout_allowed) config(): BalanceOf<T>;

pub MinCashoutAllowed get(fn min_cashout_allowed) config(): BalanceOf<T>;

/// The state bloat bond for the channel (helps preventing the state bloat).
pub ChannelStateBloatBondValue get (fn channel_state_bloat_bond_value): BalanceOf<T>;

pub ChannelCashoutsEnabled get(fn channel_cashouts_enabled) config(): bool;

/// Min auction duration
Expand Down Expand Up @@ -474,7 +477,7 @@ decl_module! {
let funds_needed = <T as Config>::DataObjectStorage::funds_needed_for_upload(num_objs, total_size);
let total_funds_needed = channel_state_bloat_bond.saturating_add(funds_needed);

Self::ensure_sufficient_balance(&sender, total_funds_needed)?;
Self::ensure_channel_creation_sufficient_balance(&sender, total_funds_needed)?;

let bag_creation_params = DynBagCreationParameters::<T> {
bag_id: DynBagId::<T>::Channel(channel_id),
Expand Down Expand Up @@ -827,10 +830,25 @@ decl_module! {
}
)?;

let video_state_bloat_bond = Self::video_state_bloat_bond_value();

// ensure expected video state bloat bond
ensure!(
params.expected_video_state_bloat_bond
== video_state_bloat_bond,
Error::<T>::VideoStateBloatBondChanged,
);

let storage_assets = params.assets.clone().unwrap_or_default();
let num_objs = storage_assets.object_creation_list.len();

let data_objects_ids = Storage::<T>::get_next_data_object_ids(num_objs);

let total_size = storage_assets.object_creation_list.iter().fold(0, |acc, obj_param| acc.saturating_add(obj_param.size));
let funds_needed = <T as Config>::DataObjectStorage::funds_needed_for_upload(num_objs, total_size);
let total_funds_needed = video_state_bloat_bond.saturating_add(funds_needed);

// assets ids
let data_objects_ids = Storage::<T>::get_next_data_object_ids(storage_assets.object_creation_list.len());
Self::ensure_video_creation_sufficient_balance(&sender, total_funds_needed)?;

if nft_status.is_some() {
Self::check_nft_limits(&channel)?;
Expand All @@ -841,6 +859,7 @@ decl_module! {
in_channel: channel_id,
nft_status: nft_status.clone(),
data_objects: data_objects_ids.clone(),
video_state_bloat_bond
};

if let Some(upload_assets) = params.assets.as_ref() {
Expand All @@ -857,6 +876,8 @@ decl_module! {
// == MUTATION SAFE ==
//

let _ = Balances::<T>::slash(&sender, video_state_bloat_bond);
Copy link
Member

Choose a reason for hiding this comment

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

Do we not put the bloat bond in some treasury account or something? What did we do for bounty pallet bloat bonds, or for storage?


// add it to the onchain state
VideoById::<T>::insert(video_id, video);

Expand Down Expand Up @@ -1003,6 +1024,9 @@ decl_module! {
// == MUTATION SAFE ==
//

//rewards the sender a state bloat bond amount for the work to delete the video.
let _ = Balances::<T>::deposit_creating(&sender, video.video_state_bloat_bond);

Self::deposit_event(RawEvent::VideoDeleted(actor, video_id));
}

Expand Down Expand Up @@ -1087,6 +1111,9 @@ decl_module! {
// == MUTATION SAFE ==
//

//rewards the sender a state bloat bond amount for the work to delete the video.
let _ = Balances::<T>::deposit_creating(&sender, video.video_state_bloat_bond);

Self::deposit_event(RawEvent::VideoDeletedByModerator(actor, video_id, rationale));
}

Expand Down Expand Up @@ -1275,6 +1302,26 @@ decl_module! {
new_channel_state_bloat_bond));
}

/// Updates video state bloat bond value.
/// Only lead can upload this value
#[weight = 10_000_000] // TODO: adjust weight
pub fn update_video_state_bloat_bond(
origin,
new_video_state_bloat_bond: BalanceOf<T>,
) {
let sender = ensure_signed(origin)?;
ensure_authorized_to_update_video_state_bloat_bond::<T>(&sender)?;

//
// == MUTATION_SAFE ==
//

VideoStateBloatBondValue::<T>::put(new_video_state_bloat_bond);
Self::deposit_event(
RawEvent::VideoStateBloatBondValueUpdated(
new_video_state_bloat_bond));
}

#[weight = 10_000_000] // TODO: adjust Weight
pub fn claim_and_withdraw_channel_reward(
origin,
Expand Down Expand Up @@ -3171,7 +3218,7 @@ impl<T: Config> Module<T> {
.collect::<BTreeSet<_>>()
}

fn ensure_sufficient_balance(
fn ensure_channel_creation_sufficient_balance(
account_id: &T::AccountId,
amount: BalanceOf<T>,
) -> DispatchResult {
Expand All @@ -3184,6 +3231,19 @@ impl<T: Config> Module<T> {
Ok(())
}

fn ensure_video_creation_sufficient_balance(
account_id: &T::AccountId,
amount: BalanceOf<T>,
) -> DispatchResult {
let balance = Balances::<T>::usable_balance(account_id);

ensure!(
balance >= amount,
Error::<T>::InsufficientBalanceForVideoCreation
);
Ok(())
}

fn ensure_sufficient_balance_for_channel_transfer(
owner: &ChannelOwner<T::MemberId, T::CuratorGroupId>,
transfer_cost: BalanceOf<T>,
Expand Down Expand Up @@ -3491,6 +3551,7 @@ decl_event!(
),
ChannelPrivilegeLevelUpdated(ChannelId, ChannelPrivilegeLevel),
ChannelStateBloatBondValueUpdated(Balance),
VideoStateBloatBondValueUpdated(Balance),
ChannelAssetsRemoved(ContentActor, ChannelId, BTreeSet<DataObjectId>, Channel),
ChannelDeleted(ContentActor, ChannelId),
ChannelDeletedByModerator(ContentActor, ChannelId, Vec<u8> /* rationale */),
Expand Down
6 changes: 6 additions & 0 deletions runtime-modules/content/src/permissions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,12 @@ pub fn ensure_authorized_to_update_channel_state_bloat_bond<T: Config>(
) -> DispatchResult {
ensure_lead_auth_success::<T>(sender)
}

pub fn ensure_authorized_to_update_video_state_bloat_bond<T: Config>(
sender: &T::AccountId,
) -> DispatchResult {
ensure_lead_auth_success::<T>(sender)
}
/// Moderation actions (curator/lead)

pub fn ensure_actor_authorized_to_perform_moderation_actions<T: Config>(
Expand Down
86 changes: 79 additions & 7 deletions runtime-modules/content/src/tests/fixtures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,7 @@ impl CreateVideoFixture {
meta: None,
auto_issue_nft: None,
expected_data_object_state_bloat_bond: DEFAULT_DATA_OBJECT_STATE_BLOAT_BOND,
expected_video_state_bloat_bond: DEFAULT_VIDEO_STATE_BLOAT_BOND,
},
channel_id: ChannelId::one(), // channel index starts at 1
}
Expand All @@ -313,6 +314,18 @@ impl CreateVideoFixture {
..self
}
}
pub fn with_expected_video_state_bloat_bond(
self,
expected_video_state_bloat_bond: u64,
) -> Self {
Self {
params: VideoCreationParameters::<Test> {
expected_video_state_bloat_bond,
..self.params.clone()
},
..self
}
}

pub fn with_nft_in_sale(self, nft_price: u64) -> Self {
Self {
Expand Down Expand Up @@ -369,6 +382,10 @@ impl CreateVideoFixture {
}
}

fn get_balance(&self) -> BalanceOf<Test> {
Balances::<Test>::usable_balance(&self.sender)
}

pub fn call(self) {
let origin = Origin::signed(self.sender.clone());
assert_ok!(Content::create_video(
Expand All @@ -381,7 +398,7 @@ impl CreateVideoFixture {

pub fn call_and_assert(&self, expected_result: DispatchResult) {
let origin = Origin::signed(self.sender.clone());
let balance_pre = Balances::<Test>::usable_balance(self.sender);
let balance_pre = self.get_balance();
let channel_bag_id = Content::bag_id_for_channel(&self.channel_id);
let video_id = Content::next_video_id();
let beg_obj_id = storage::NextDataObjectId::<Test>::get();
Expand All @@ -393,7 +410,7 @@ impl CreateVideoFixture {
self.params.clone(),
);

let balance_post = Balances::<Test>::usable_balance(self.sender);
let balance_post = self.get_balance();
let end_obj_id = storage::NextDataObjectId::<Test>::get();

assert_eq!(actual_result, expected_result);
Expand Down Expand Up @@ -428,7 +445,7 @@ impl CreateVideoFixture {

assert_eq!(
balance_pre.saturating_sub(balance_post),
objects_state_bloat_bond,
objects_state_bloat_bond + Content::video_state_bloat_bond_value()
);

assert!((beg_obj_id..end_obj_id).all(|id| {
Expand Down Expand Up @@ -1499,11 +1516,15 @@ pub trait VideoDeletion {
fn execute_call(&self) -> DispatchResult;
fn expected_event_on_success(&self) -> MetaEvent;

fn get_balance(&self) -> BalanceOf<Test> {
Balances::<Test>::usable_balance(self.get_sender())
}

fn call_and_assert(&self, expected_result: DispatchResult) {
let balance_pre = Balances::<Test>::usable_balance(self.get_sender());
let balance_pre = self.get_balance();
let video_pre = <VideoById<Test>>::get(&self.get_video_id());
let channel_bag_id = Content::bag_id_for_channel(&video_pre.in_channel);
let state_bloat_bond =
let data_obj_state_bloat_bond =
video_pre
.data_objects
.iter()
Expand All @@ -1514,7 +1535,7 @@ pub trait VideoDeletion {

let actual_result = self.execute_call();

let balance_post = Balances::<Test>::usable_balance(self.get_sender());
let balance_post = self.get_balance();

assert_eq!(actual_result, expected_result);

Expand All @@ -1525,7 +1546,10 @@ pub trait VideoDeletion {
self.expected_event_on_success()
);

assert_eq!(balance_post.saturating_sub(balance_pre), state_bloat_bond);
assert_eq!(
balance_post.saturating_sub(balance_pre),
data_obj_state_bloat_bond + Content::video_state_bloat_bond_value()
);

assert!(!video_pre.data_objects.iter().any(|obj_id| {
storage::DataObjectsById::<Test>::contains_key(&channel_bag_id, obj_id)
Expand Down Expand Up @@ -2733,6 +2757,54 @@ impl UpdateChannelStateBloatBondFixture {
}
}

pub struct UpdateVideoStateBloatBondFixture {
sender: AccountId,
new_video_state_bloat_bond: BalanceOf<Test>,
}

impl UpdateVideoStateBloatBondFixture {
pub fn default() -> Self {
Self {
sender: LEAD_ACCOUNT_ID,
new_video_state_bloat_bond: DEFAULT_VIDEO_STATE_BLOAT_BOND,
}
}

pub fn with_sender(self, sender: AccountId) -> Self {
Self { sender, ..self }
}

pub fn with_video_state_bloat_bond(self, new_video_state_bloat_bond: BalanceOf<Test>) -> Self {
Self {
new_video_state_bloat_bond,
..self
}
}

pub fn call_and_assert(&self, expected_result: DispatchResult) {
let origin = Origin::signed(self.sender.clone());
let video_state_bloat_bond_pre = Content::video_state_bloat_bond_value();

let actual_result =
Content::update_video_state_bloat_bond(origin, self.new_video_state_bloat_bond.clone());

let video_state_bloat_bond_post = Content::video_state_bloat_bond_value();

assert_eq!(actual_result, expected_result);
if actual_result.is_ok() {
assert_eq!(
System::events().last().unwrap().event,
MetaEvent::Content(RawEvent::VideoStateBloatBondValueUpdated(
self.new_video_state_bloat_bond
))
);
assert_eq!(video_state_bloat_bond_post, self.new_video_state_bloat_bond);
} else {
assert_eq!(video_state_bloat_bond_post, video_state_bloat_bond_pre);
}
}
}

pub struct DeissueCreatorTokenFixture {
sender: AccountId,
actor: ContentActor<CuratorGroupId, CuratorId, MemberId>,
Expand Down
20 changes: 20 additions & 0 deletions runtime-modules/content/src/tests/merkle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,26 @@ fn unsuccessful_channel_state_bloat_bond_update_by_non_lead_account() {
})
}

#[test]
fn successful_video_state_bloat_bond_update_by_lead_account() {
with_default_mock_builder(|| {
run_to_block(1);
UpdateVideoStateBloatBondFixture::default()
.with_video_state_bloat_bond(20)
.call_and_assert(Ok(()))
})
}

#[test]
fn unsuccessful_video_state_bloat_bond_update_by_non_lead_account() {
with_default_mock_builder(|| {
run_to_block(1);
UpdateVideoStateBloatBondFixture::default()
.with_sender(UNAUTHORIZED_LEAD_ACCOUNT_ID)
.call_and_assert(Err(Error::<Test>::LeadAuthFailed.into()))
})
}

#[test]
fn unsuccessful_reward_claim_with_unsufficient_cashout() {
with_default_mock_builder(|| {
Expand Down
1 change: 1 addition & 0 deletions runtime-modules/content/src/tests/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ pub const UNAUTHORIZED_CURATOR_MEMBER_ID: u64 = 220;

pub const DEFAULT_DATA_OBJECT_STATE_BLOAT_BOND: u64 = 0;
pub const DEFAULT_CHANNEL_STATE_BLOAT_BOND: u64 = 0;
pub const DEFAULT_VIDEO_STATE_BLOAT_BOND: u64 = 0;
pub const DEFAULT_OBJECT_SIZE: u64 = 5;
pub const DATA_OBJECTS_NUMBER: u64 = 10;
pub const OUTSTANDING_VIDEOS: u64 = 5;
Expand Down
Loading