codex-contracts-eth/contracts/AccountLocks.sol
Eric Mastro ad040cfee6 [marketplace] extend proof ending
Allow proof ending to be extending once a contract is started, so that all filled slots share an ending time that is equal to the contract end time. Added tests.

Add a mapping for proof id to endId so that proof expectations can be extended for all proofs that share a given endId.

Add function modifiers that require the request state to allow proofs, with accompanying tests.

General clean up of each function’s request state context, with accompanying tests.

General clean up of all tests, including state change “wait” functions and normalising the time advancement functions.
2022-10-25 12:38:19 +11:00

96 lines
3.6 KiB
Solidity

// 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;
removeInactiveLocks(accountLocks);
require(accountLocks.length < MAX_LOCKS_PER_ACCOUNT, "Max locks reached");
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");
require(lock.owner == msg.sender, "Only lock creator can unlock");
lock.unlocked = true;
}
/// Extends the locks expiry time. Lock must not have already expired.
/// NOTE: We do not need to check that msg.sender is the lock.owner because
/// this function is internal, and is only called after all checks have been
/// performed in Marketplace.fillSlot.
function _extendLockExpiryTo(bytes32 lockId, uint256 expiry) internal {
Lock storage lock = locks[lockId];
require(lock.owner != address(0), "Lock does not exist");
// require(lock.owner == msg.sender, "Only lock creator can extend expiry");
require(lock.expiry >= block.timestamp, "Lock already expired");
lock.expiry = expiry;
}
/// 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);
require(accountLocks.length == 0, "Account locked");
}
function removeInactiveLocks(bytes32[] storage lockIds) private {
uint256 index = 0;
while (true) {
if (index >= lockIds.length) {
return;
}
if (isInactive(locks[lockIds[index]])) {
lockIds[index] = lockIds[lockIds.length - 1];
lockIds.pop();
} else {
index++;
}
}
}
function isInactive(Lock storage lock) private view returns (bool) {
return lock.unlocked || lock.expiry <= block.timestamp;
}
struct Lock {
address owner;
uint256 expiry;
bool unlocked;
}
struct Account {
bytes32[] locks;
}
}