diff --git a/crates/env/src/api.rs b/crates/env/src/api.rs index 96f3567473..945ab8e5d4 100644 --- a/crates/env/src/api.rs +++ b/crates/env/src/api.rs @@ -34,7 +34,10 @@ use crate::{ HashOutput, }, topics::Topics, - types::RentParams, + types::{ + RentParams, + RentStatus, + }, Environment, Result, }; @@ -170,6 +173,28 @@ where }) } +/// Returns information about the required deposit and resulting rent. +/// +/// # Parameters +/// +/// - `at_refcount`: The refcount assumed for the returned `custom_refcount_*` fields. +/// If `None` is supplied the `custom_refcount_*` fields will also be `None`. +/// +/// The `current_*` fields of `RentStatus` do **not** consider changes to the code's +/// refcount made during the currently running call. +/// +/// # Errors +/// +/// If the returned value cannot be properly decoded. +pub fn rent_status(at_refcount: Option) -> Result> +where + T: Environment, +{ + ::on_instance(|instance| { + TypedEnvBackend::rent_status::(instance, at_refcount) + }) +} + /// Returns the current block number. /// /// # Errors diff --git a/crates/env/src/backend.rs b/crates/env/src/backend.rs index 96f23cae23..feaffca187 100644 --- a/crates/env/src/backend.rs +++ b/crates/env/src/backend.rs @@ -23,7 +23,10 @@ use crate::{ HashOutput, }, topics::Topics, - types::RentParams, + types::{ + RentParams, + RentStatus, + }, Environment, Result, }; @@ -230,6 +233,16 @@ pub trait TypedEnvBackend: EnvBackend { /// For more details visit: [`RentParams`][`crate::RentParams`] fn rent_params(&mut self) -> Result>; + /// Returns information about the required deposit and resulting rent. + /// + /// # Note + /// + /// For more details visit: [`RentStatus`][`crate::RentStatus`] + fn rent_status( + &mut self, + at_refcount: Option, + ) -> Result>; + /// Returns the current block number. /// /// # Note diff --git a/crates/env/src/engine/experimental_off_chain/impls.rs b/crates/env/src/engine/experimental_off_chain/impls.rs index e89f063d52..0781e0670f 100644 --- a/crates/env/src/engine/experimental_off_chain/impls.rs +++ b/crates/env/src/engine/experimental_off_chain/impls.rs @@ -35,6 +35,7 @@ use crate::{ EnvBackend, Environment, RentParams, + RentStatus, Result, ReturnFlags, TypedEnvBackend, @@ -309,6 +310,16 @@ impl TypedEnvBackend for EnvInstance { unimplemented!("off-chain environment does not support rent params") } + fn rent_status( + &mut self, + _at_refcount: Option, + ) -> Result> + where + T: Environment, + { + unimplemented!("off-chain environment does not support rent status") + } + fn block_number(&mut self) -> Result { self.get_property::(Engine::block_number) } diff --git a/crates/env/src/engine/off_chain/impls.rs b/crates/env/src/engine/off_chain/impls.rs index 51168ac693..abf6790816 100644 --- a/crates/env/src/engine/off_chain/impls.rs +++ b/crates/env/src/engine/off_chain/impls.rs @@ -32,7 +32,10 @@ use crate::{ Sha2x256, }, topics::Topics, - types::RentParams, + types::{ + RentParams, + RentStatus, + }, EnvBackend, Environment, Error, @@ -391,6 +394,16 @@ impl TypedEnvBackend for EnvInstance { }) } + fn rent_status( + &mut self, + _at_refcount: Option, + ) -> Result> + where + T: Environment, + { + unimplemented!("off-chain environment does not support rent status") + } + fn block_number(&mut self) -> Result { self.current_block() .expect(UNINITIALIZED_EXEC_CONTEXT) diff --git a/crates/env/src/engine/on_chain/ext.rs b/crates/env/src/engine/on_chain/ext.rs index 7cfb0808ac..dfbd3dd167 100644 --- a/crates/env/src/engine/on_chain/ext.rs +++ b/crates/env/src/engine/on_chain/ext.rs @@ -348,6 +348,12 @@ mod sys { output_ptr: Ptr32Mut<[u8]>, output_len_ptr: Ptr32Mut, ); + + pub fn seal_rent_status( + at_refcount: u32, + output_ptr: Ptr32Mut<[u8]>, + output_len_ptr: Ptr32Mut, + ); } } @@ -610,6 +616,20 @@ pub fn rent_params(output: &mut &mut [u8]) { extract_from_slice(output, output_len as usize); } +pub fn rent_status(at_refcount: Option, output: &mut &mut [u8]) { + let mut output_len = output.len() as u32; + { + unsafe { + sys::seal_rent_status( + at_refcount.map_or(0, |rc| rc.get()), + Ptr32Mut::from_slice(output), + Ptr32Mut::from_ref(&mut output_len), + ) + }; + } + extract_from_slice(output, output_len as usize); +} + pub fn random(subject: &[u8], output: &mut &mut [u8]) { let mut output_len = output.len() as u32; { diff --git a/crates/env/src/engine/on_chain/impls.rs b/crates/env/src/engine/on_chain/impls.rs index 6946f372d2..eacc2d2769 100644 --- a/crates/env/src/engine/on_chain/impls.rs +++ b/crates/env/src/engine/on_chain/impls.rs @@ -36,7 +36,10 @@ use crate::{ Topics, TopicsBuilderBackend, }, - types::RentParams, + types::{ + RentParams, + RentStatus, + }, Clear, EnvBackend, Environment, @@ -363,6 +366,18 @@ impl TypedEnvBackend for EnvInstance { self.get_property::>(ext::rent_params) } + fn rent_status( + &mut self, + at_refcount: Option, + ) -> Result> + where + T: Environment, + { + let output = &mut self.scoped_buffer().take_rest(); + ext::rent_status(at_refcount, output); + scale::Decode::decode(&mut &output[..]).map_err(Into::into) + } + fn invoke_contract( &mut self, call_params: &CallParams, diff --git a/crates/env/src/lib.rs b/crates/env/src/lib.rs index f61bf4b50c..9c2f21d513 100644 --- a/crates/env/src/lib.rs +++ b/crates/env/src/lib.rs @@ -99,5 +99,6 @@ pub use self::{ NoChainExtension, Perbill, RentParams, + RentStatus, }, }; diff --git a/crates/env/src/types.rs b/crates/env/src/types.rs index 775252a734..922f9e716b 100644 --- a/crates/env/src/types.rs +++ b/crates/env/src/types.rs @@ -314,3 +314,37 @@ pub struct RentParams { /// Reserved for backwards compatible changes to this data structure. pub _reserved: Option<()>, } + +/// Information about the required deposit and resulting rent. +/// +/// The easiest way to guarantee that a contract stays alive is to assert that +/// `max_rent == 0` at the **end** of a contract's execution. +/// +/// # Note +/// +/// The `current_*` fields do **not** consider changes to the code's refcount made during +/// the currently running call. +#[derive(scale::Decode)] +#[cfg_attr(test, derive(Debug, PartialEq))] +pub struct RentStatus { + /// Required deposit assuming that this contract is the only user of its code. + pub max_deposit: T::Balance, + + /// Required deposit assuming the code's current refcount. + pub current_deposit: T::Balance, + + /// Required deposit assuming the specified refcount (`None` if `0` is supplied). + pub custom_refcount_deposit: Option, + + /// Rent that is paid assuming that the contract is the only user of its code. + pub max_rent: T::Balance, + + /// Rent that is paid given the code's current refcount. + pub current_rent: T::Balance, + + /// Rent that is paid assuming the specified refcount (`None` if `0` is supplied). + pub custom_refcount_rent: Option, + + /// Reserved for backwards compatible changes to this data structure. + pub _reserved: Option<()>, +} diff --git a/crates/lang/src/env_access.rs b/crates/lang/src/env_access.rs index c4e492fe5c..c59c6b72ce 100644 --- a/crates/lang/src/env_access.rs +++ b/crates/lang/src/env_access.rs @@ -25,6 +25,7 @@ use ink_env::{ }, Environment, RentParams, + RentStatus, Result, }; use ink_primitives::Key; @@ -190,6 +191,27 @@ where ink_env::rent_params::().expect("couldn't decode contract rent params") } + /// Returns information about the required deposit and resulting rent. + /// + /// # Parameters + /// + /// - `at_refcount`: The refcount assumed for the returned `custom_refcount_*` fields. + /// If `None` is supplied the `custom_refcount_*` fields will also be `None`. + /// + /// The `current_*` fields of `RentStatus` do **not** consider changes to the code's + /// refcount made during the currently running call. + /// + /// # Note + /// + /// For more details visit: [`ink_env::RentStatus`] + pub fn rent_status( + self, + at_refcount: Option, + ) -> RentStatus { + ink_env::rent_status::(at_refcount) + .expect("couldn't decode contract rent params") + } + /// Returns the current block number. /// /// # Note