Reentrancy Guard
Contract component that helps prevent reentrant calls to a function.
#
Dependencymetis_reentrancy_guard = { git = "https://github.com/patractlabs/metis", default-features = false }
#
StorageIn most cases, developer not need to consider the storage of reentrancy guard.
const _NOT_ENTERED: u8 = 1;const _ENTERED: u8 = 2;
/// The Data of pausable component#[cfg_attr(feature = "std", derive(::ink_storage::traits::StorageLayout))]#[derive(Debug)]pub struct Data { /// is contract current paused status: Lazy<u8>,
key: ink_primitives::Key,}
Note the key
in module, in ink!
, all storage will flush to storage after the end of message proccess,
so if contract only modifies status
, the reentrancy_guard will not work:
/// set current status to entered pub fn set_entered(&mut self) { Lazy::set(&mut self.status, _ENTERED); self.flush() // flush to storage }
/// set current status to not entered pub fn set_not_entered(&mut self) { Lazy::set(&mut self.status, _NOT_ENTERED); self.flush() // flush to storage }
#
Internal FunctionThere is some internal function for reentrancy_guard, the marco will check it automatically:
#[ink(message)] #[metis_lang::reentrancy_guard] pub fn function_can_not_reentrancy(&mut self) { self._check_nonreentrant(); self._set_entered();
// logics
self._set_not_entered(); }
the functions:
_check_nonreentrant
: check if the current status is_NOT_ENTERED
fn _check_nonreentrant(&self) { assert!(!self.get().is_entered(), "ReentrancyGuard: reentrant call"); }
_set_entered
: set current status to_ENTERED
fn _set_entered(&mut self) { self.get_mut().set_entered(); }
_set_not_entered
: set current status to_NOT_ENTERED
fn _set_not_entered(&mut self) { self.get_mut().set_not_entered(); }
#
Usage ExampleTo use reentrant_guard
component, first is import the component:
use metis_lang::{ import, metis, }; use metis_reentrancy_guard as reentrancy_guard;
#[ink(storage)] #[import(reentrancy_guard)] pub struct Contracts { reentrancy_guard: reentrancy_guard::Data,
// others }
To use reentrancy guard, we can use a marco to check, it like the modifier in solidity:
#[ink(message)] #[metis_lang::reentrancy_guard] pub fn function_can_not_reentrancy(&mut self) { // logics }