安全性

以下文档提供了在 openzeppelin_security 下找到的模块的上下文、推理和示例。

注意:请预计这些模块会不断演变。

可初始化

Initializable 组件提供了一种简单的机制,可以模仿构造函数的功能。 更具体地说,它使逻辑能够执行一次且仅一次,这对于在无法使用构造函数时(例如,在构造时存在循环依赖关系时)设置合约的初始状态非常有用。

用法

你可以在你的合约中使用该组件,如下所示:

#[starknet::contract]
mod MyInitializableContract {
    use openzeppelin_security::InitializableComponent;

    component!(path: InitializableComponent, storage: initializable, event: InitializableEvent);

    impl InternalImpl = InitializableComponent::InternalImpl<ContractState>;

    #[storage]
    struct Storage {
        #[substorage(v0)]
        initializable: InitializableComponent::Storage,
        param: felt252
    }

    #[event]
    #[derive(Drop, starknet::Event)]
    enum Event {
        #[flat]
        InitializableEvent: InitializableComponent::Event
    }

    fn initializer(ref self: ContractState, some_param: felt252) {
        // 使该方法只能调用一次
        self.initializable.initialize();

        // 初始化逻辑
        self.param.write(some_param);
    }
}

注意:此 Initializable 模式应仅在一个函数中使用。

接口

该组件提供了以下外部函数作为 InitializableImpl 实现的一部分:

#[starknet::interface]
pub trait InitializableABI {
    fn is_initialized() -> bool;
}

可暂停

Pausable 组件允许合约实现紧急停止机制。 这对于诸如在评估期结束前防止交易或在发生重大错误时具有紧急开关来冻结所有交易之类的情况可能很有用。

要变为可暂停,合约应包含 pauseunpause 函数(应受到保护)。 对于仅当暂停或未暂停时才应可用的方法,请分别插入对 assert_pausedassert_not_paused 的调用。

用法

例如(使用 Ownable 组件进行访问控制):

#[starknet::contract]
mod MyPausableContract {
    use openzeppelin_access::ownable::OwnableComponent;
    use openzeppelin_security::PausableComponent;
    use starknet::ContractAddress;

    component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);
    component!(path: PausableComponent, storage: pausable, event: PausableEvent);

    // Ownable Mixin
    #[abi(embed_v0)]
    impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl<ContractState>;
    impl OwnableInternalImpl = OwnableComponent::InternalImpl<ContractState>;

    // Pausable
    #[abi(embed_v0)]
    impl PausableImpl = PausableComponent::PausableImpl<ContractState>;
    impl PausableInternalImpl = PausableComponent::InternalImpl<ContractState>;

    #[storage]
    struct Storage {
        #[substorage(v0)]
        ownable: OwnableComponent::Storage,
        #[substorage(v0)]
        pausable: PausableComponent::Storage
    }

    #[event]
    #[derive(Drop, starknet::Event)]
    enum Event {
        #[flat]
        OwnableEvent: OwnableComponent::Event,
        #[flat]
        PausableEvent: PausableComponent::Event
    }

    #[constructor]
    fn constructor(ref self: ContractState, owner: ContractAddress) {
        self.ownable.initializer(owner);
    }

    #[external(v0)]
    fn pause(ref self: ContractState) {
        self.ownable.assert_only_owner();
        self.pausable.pause();
    }

    #[external(v0)]
    fn unpause(ref self: ContractState) {
        self.ownable.assert_only_owner();
        self.pausable.unpause();
    }

    #[external(v0)]
    fn when_not_paused(ref self: ContractState) {
        self.pausable.assert_not_paused();
        // 执行某些操作
    }

    #[external(v0)]
    fn when_paused(ref self: ContractState) {
        self.pausable.assert_paused();
        // 执行某些操作
    }
}

接口

该组件提供了以下外部函数作为 PausableImpl 实现的一部分:

#[starknet::interface]
pub trait PausableABI {
    fn is_paused() -> bool;
}

重入保护

当调用者能够通过递归调用目标的函数来获得比允许的更多的资源时,就会发生 重入攻击

用法

由于 Cairo 不支持像 Solidity 这样的修饰符,因此 ReentrancyGuard 组件公开了两个方法 startend 来保护函数免受重入攻击。 受保护的函数必须在第一个函数语句之前调用 start,并在返回语句之前调用 end,如下所示:

#[starknet::contract]
mod MyReentrancyContract {
    use openzeppelin_security::ReentrancyGuardComponent;

    component!(
        path: ReentrancyGuardComponent, storage: reentrancy_guard, event: ReentrancyGuardEvent
    );

    impl InternalImpl = ReentrancyGuardComponent::InternalImpl<ContractState>;

    #[storage]
    struct Storage {
        #[substorage(v0)]
        reentrancy_guard: ReentrancyGuardComponent::Storage
    }

    #[event]
    #[derive(Drop, starknet::Event)]
    enum Event {
        #[flat]
        ReentrancyGuardEvent: ReentrancyGuardComponent::Event
    }

    #[external(v0)]
    fn protected_function(ref self: ContractState) {
        self.reentrancy_guard.start();

        // 执行某些操作

        self.reentrancy_guard.end();
    }

    #[external(v0)]
    fn another_protected_function(ref self: ContractState) {
        self.reentrancy_guard.start();

        // 执行某些操作

        self.reentrancy_guard.end();
    }
}

注意:该保护措施可防止发生在 protected_function 内部的执行流程 调用其自身或 another_protected_function,反之亦然。