From 82ca9c1be6dae0c2049e8ecb92f320d03365b214 Mon Sep 17 00:00:00 2001 From: str-mv <51784859+str-mv@users.noreply.github.com> Date: Wed, 02 Dec 2020 17:16:27 +0000 Subject: [PATCH] Merge pull request #25 from usetech-llc/feature/NFTPAR-110_contract_spam Feature/nftpar 110 contract spam protection --- --- a/Cargo.lock +++ b/Cargo.lock @@ -3737,7 +3737,10 @@ "frame-support", "frame-system", "log", + "pallet-balances", "pallet-contracts", + "pallet-randomness-collective-flip", + "pallet-timestamp", "pallet-transaction-payment", "parity-scale-codec", "serde", --- a/pallets/nft/src/default_weights.rs +++ b/pallets/nft/src/default_weights.rs @@ -107,9 +107,19 @@ .saturating_add(DbWeight::get().reads(2 as Weight)) .saturating_add(DbWeight::get().writes(1 as Weight)) } + // fn set_chain_limits() -> Weight { + // (0 as Weight) + // .saturating_add(DbWeight::get().reads(1 as Weight)) + // .saturating_add(DbWeight::get().writes(1 as Weight)) + // } // fn enable_contract_sponsoring() -> Weight { // (0 as Weight) // .saturating_add(DbWeight::get().reads(1 as Weight)) // .saturating_add(DbWeight::get().writes(1 as Weight)) // } + // fn set_contract_sponsoring_rate_limit() -> Weight { + // (0 as Weight) + // .saturating_add(DbWeight::get().reads(1 as Weight)) + // .saturating_add(DbWeight::get().writes(1 as Weight)) + // } } --- a/pallets/nft/src/lib.rs +++ b/pallets/nft/src/lib.rs @@ -366,8 +366,10 @@ pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber; // Contract Sponsorship and Ownership - pub ContractOwner get(fn contract_owner): map hasher(identity) T::AccountId => T::AccountId; - pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(identity) T::AccountId => bool; + pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId; + pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool; + pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) T::AccountId => T::BlockNumber; + pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber; } add_extra_genesis { build(|config: &GenesisConfig| { @@ -1323,6 +1325,41 @@ Ok(()) } + /// Set the rate limit for contract sponsoring to specified number of blocks. + /// + /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. + /// If set to the number B (for blocks), the transactions will be sponsored with a rate + /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid + /// from contract endowment if there are at least B blocks between such transactions. + /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender. + /// + /// # Permissions + /// + /// * Contract Owner + /// + /// # Arguments + /// + /// -`contract_address`: Address of the contract to sponsor + /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed + /// + #[weight = 0] + pub fn set_contract_sponsoring_rate_limit( + origin, + contract_address: T::AccountId, + rate_limit: T::BlockNumber + ) -> DispatchResult { + let sender = ensure_signed(origin)?; + let mut is_owner = false; + if >::contains_key(contract_address.clone()) { + let owner = >::get(&contract_address); + is_owner = sender == owner; + } + ensure!(is_owner, Error::::NoPermission); + + >::insert(contract_address, rate_limit); + Ok(()) + } + } } @@ -2230,11 +2267,30 @@ // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => { + let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default()); + + let mut sponsor_transfer = false; + if >::contains_key(called_contract.clone()) { + let last_tx_block = >::get(&called_contract); + let block_number = >::block_number() as T::BlockNumber; + let rate_limit = >::get(&called_contract); + let limit_time = last_tx_block + rate_limit; + + if block_number >= limit_time { + >::insert(called_contract.clone(), block_number); + sponsor_transfer = true; + } + } else { + sponsor_transfer = false; + } + + let mut sp = T::AccountId::default(); - let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default()); - if >::contains_key(called_contract.clone()) { - if >::get(called_contract.clone()) { - sp = called_contract; + if sponsor_transfer { + if >::contains_key(called_contract.clone()) { + if >::get(called_contract.clone()) { + sp = called_contract; + } } } --- a/runtime/src/nft_weights.rs +++ b/runtime/src/nft_weights.rs @@ -108,4 +108,19 @@ .saturating_add(DbWeight::get().reads(2 as Weight)) .saturating_add(DbWeight::get().writes(1 as Weight)) } + // fn set_chain_limits() -> Weight { + // (0 as Weight) + // .saturating_add(DbWeight::get().reads(1 as Weight)) + // .saturating_add(DbWeight::get().writes(1 as Weight)) + // } + // fn enable_contract_sponsoring() -> Weight { + // (0 as Weight) + // .saturating_add(DbWeight::get().reads(1 as Weight)) + // .saturating_add(DbWeight::get().writes(1 as Weight)) + // } + // fn set_contract_sponsoring_rate_limit() -> Weight { + // (0 as Weight) + // .saturating_add(DbWeight::get().reads(1 as Weight)) + // .saturating_add(DbWeight::get().writes(1 as Weight)) + // } } -- gitstuff