diff --git a/contracts/AccountLocks.sol b/contracts/AccountLocks.sol index 9417fad..34e123a 100644 --- a/contracts/AccountLocks.sol +++ b/contracts/AccountLocks.sol @@ -1,17 +1,29 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; +/// Implements account locking. The main goal of this design is to allow +/// unlocking of multiple accounts in O(1). To achieve this we keep a list of +/// locks per account. Every time an account is locked or unlocked, this list is +/// checked for inactive locks, which are subsequently removed. To ensure that +/// this operation does not become too expensive in gas costs, a maximum amount +/// of active locks per account is enforced. contract AccountLocks { uint256 public constant MAX_LOCKS_PER_ACCOUNT = 128; mapping(bytes32 => Lock) private locks; mapping(address => Account) private accounts; + /// Creates a lock that can be used to lock accounts. The id needs to be + /// unique and collision resistant. The expiry time is given in unix time. function _createLock(bytes32 id, uint256 expiry) internal { require(locks[id].owner == address(0), "Lock already exists"); locks[id] = Lock(msg.sender, expiry, false); } + /// Attaches a lock to an account. Only when the lock is unlocked or expires + /// can the account be unlocked again. + /// Calling this function triggers a cleanup of inactive locks, making this + /// an O(N) operation, where N = MAX_LOCKS_PER_ACCOUNT. function _lock(address account, bytes32 lockId) internal { require(locks[lockId].owner != address(0), "Lock does not exist"); bytes32[] storage accountLocks = accounts[account].locks; @@ -20,6 +32,9 @@ contract AccountLocks { accountLocks.push(lockId); } + /// Unlocks a lock, thereby freeing any accounts that are attached to this + /// lock. This is an O(1) operation. Only the party that created the lock is + /// allowed to unlock it. function _unlock(bytes32 lockId) internal { Lock storage lock = locks[lockId]; require(lock.owner != address(0), "Lock does not exist"); @@ -27,6 +42,10 @@ contract AccountLocks { lock.unlocked = true; } + /// Unlocks an account. This will fail if there are any active locks attached + /// to this account. + /// Calling this function triggers a cleanup of inactive locks, making this + /// an O(N) operation, where N = MAX_LOCKS_PER_ACCOUNT. function _unlockAccount() internal { bytes32[] storage accountLocks = accounts[msg.sender].locks; removeInactiveLocks(accountLocks);