refactor: move brother_node development artifact to dev/test-nodes subdirectory
Development Artifact Cleanup: ✅ BROTHER_NODE REORGANIZATION: Moved development test node to appropriate location - dev/test-nodes/brother_node/: Moved from root directory for better organization - Contains development configuration, test logs, and test chain data - No impact on production systems - purely development/testing artifact ✅ DEVELOPMENT ARTIFACTS IDENTIFIED: - Chain ID: aitbc-brother-chain (test/development chain) - Ports: 8010 (P2P) and 8011 (RPC) - different from production - Environment: .env file with test configuration - Logs: rpc.log and node.log from development testing session (March 15, 2026) ✅ ROOT DIRECTORY CLEANUP: Removed development clutter from production directory - brother_node/ moved to dev/test-nodes/brother_node/ - Root directory now contains only production-ready components - Development artifacts properly organized in dev/ subdirectory DIRECTORY STRUCTURE IMPROVEMENT: 📁 dev/test-nodes/: Development and testing node configurations 🏗️ Root Directory: Clean production structure with only essential components 🧪 Development Isolation: Test environments separated from production BENEFITS: ✅ Clean Production Directory: No development artifacts in root ✅ Better Organization: Development nodes grouped in dev/ subdirectory ✅ Clear Separation: Production vs development environments clearly distinguished ✅ Maintainability: Easier to identify and manage development components RESULT: Successfully moved brother_node development artifact to dev/test-nodes/ subdirectory, cleaning up the root directory while preserving development testing environment for future use.
This commit is contained in:
60
dev/env/node_modules/@openzeppelin/contracts/utils/structs/BitMaps.sol
generated
vendored
Executable file
60
dev/env/node_modules/@openzeppelin/contracts/utils/structs/BitMaps.sol
generated
vendored
Executable file
@@ -0,0 +1,60 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/BitMaps.sol)
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
/**
|
||||
* @dev Library for managing uint256 to bool mapping in a compact and efficient way, provided the keys are sequential.
|
||||
* Largely inspired by Uniswap's https://github.com/Uniswap/merkle-distributor/blob/master/contracts/MerkleDistributor.sol[merkle-distributor].
|
||||
*
|
||||
* BitMaps pack 256 booleans across each bit of a single 256-bit slot of `uint256` type.
|
||||
* Hence booleans corresponding to 256 _sequential_ indices would only consume a single slot,
|
||||
* unlike the regular `bool` which would consume an entire slot for a single value.
|
||||
*
|
||||
* This results in gas savings in two ways:
|
||||
*
|
||||
* - Setting a zero value to non-zero only once every 256 times
|
||||
* - Accessing the same warm slot for every 256 _sequential_ indices
|
||||
*/
|
||||
library BitMaps {
|
||||
struct BitMap {
|
||||
mapping(uint256 bucket => uint256) _data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns whether the bit at `index` is set.
|
||||
*/
|
||||
function get(BitMap storage bitmap, uint256 index) internal view returns (bool) {
|
||||
uint256 bucket = index >> 8;
|
||||
uint256 mask = 1 << (index & 0xff);
|
||||
return bitmap._data[bucket] & mask != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Sets the bit at `index` to the boolean `value`.
|
||||
*/
|
||||
function setTo(BitMap storage bitmap, uint256 index, bool value) internal {
|
||||
if (value) {
|
||||
set(bitmap, index);
|
||||
} else {
|
||||
unset(bitmap, index);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Sets the bit at `index`.
|
||||
*/
|
||||
function set(BitMap storage bitmap, uint256 index) internal {
|
||||
uint256 bucket = index >> 8;
|
||||
uint256 mask = 1 << (index & 0xff);
|
||||
bitmap._data[bucket] |= mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Unsets the bit at `index`.
|
||||
*/
|
||||
function unset(BitMap storage bitmap, uint256 index) internal {
|
||||
uint256 bucket = index >> 8;
|
||||
uint256 mask = 1 << (index & 0xff);
|
||||
bitmap._data[bucket] &= ~mask;
|
||||
}
|
||||
}
|
||||
630
dev/env/node_modules/@openzeppelin/contracts/utils/structs/Checkpoints.sol
generated
vendored
Executable file
630
dev/env/node_modules/@openzeppelin/contracts/utils/structs/Checkpoints.sol
generated
vendored
Executable file
@@ -0,0 +1,630 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v5.4.0) (utils/structs/Checkpoints.sol)
|
||||
// This file was procedurally generated from scripts/generate/templates/Checkpoints.js.
|
||||
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import {Math} from "../math/Math.sol";
|
||||
|
||||
/**
|
||||
* @dev This library defines the `Trace*` struct, for checkpointing values as they change at different points in
|
||||
* time, and later looking up past values by block number. See {Votes} as an example.
|
||||
*
|
||||
* To create a history of checkpoints define a variable type `Checkpoints.Trace*` in your contract, and store a new
|
||||
* checkpoint for the current transaction block using the {push} function.
|
||||
*/
|
||||
library Checkpoints {
|
||||
/**
|
||||
* @dev A value was attempted to be inserted on a past checkpoint.
|
||||
*/
|
||||
error CheckpointUnorderedInsertion();
|
||||
|
||||
struct Trace224 {
|
||||
Checkpoint224[] _checkpoints;
|
||||
}
|
||||
|
||||
struct Checkpoint224 {
|
||||
uint32 _key;
|
||||
uint224 _value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Pushes a (`key`, `value`) pair into a Trace224 so that it is stored as the checkpoint.
|
||||
*
|
||||
* Returns previous value and new value.
|
||||
*
|
||||
* IMPORTANT: Never accept `key` as a user input, since an arbitrary `type(uint32).max` key set will disable the
|
||||
* library.
|
||||
*/
|
||||
function push(
|
||||
Trace224 storage self,
|
||||
uint32 key,
|
||||
uint224 value
|
||||
) internal returns (uint224 oldValue, uint224 newValue) {
|
||||
return _insert(self._checkpoints, key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the value in the first (oldest) checkpoint with key greater or equal than the search key, or zero if
|
||||
* there is none.
|
||||
*/
|
||||
function lowerLookup(Trace224 storage self, uint32 key) internal view returns (uint224) {
|
||||
uint256 len = self._checkpoints.length;
|
||||
uint256 pos = _lowerBinaryLookup(self._checkpoints, key, 0, len);
|
||||
return pos == len ? 0 : _unsafeAccess(self._checkpoints, pos)._value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero
|
||||
* if there is none.
|
||||
*/
|
||||
function upperLookup(Trace224 storage self, uint32 key) internal view returns (uint224) {
|
||||
uint256 len = self._checkpoints.length;
|
||||
uint256 pos = _upperBinaryLookup(self._checkpoints, key, 0, len);
|
||||
return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero
|
||||
* if there is none.
|
||||
*
|
||||
* NOTE: This is a variant of {upperLookup} that is optimized to find "recent" checkpoint (checkpoints with high
|
||||
* keys).
|
||||
*/
|
||||
function upperLookupRecent(Trace224 storage self, uint32 key) internal view returns (uint224) {
|
||||
uint256 len = self._checkpoints.length;
|
||||
|
||||
uint256 low = 0;
|
||||
uint256 high = len;
|
||||
|
||||
if (len > 5) {
|
||||
uint256 mid = len - Math.sqrt(len);
|
||||
if (key < _unsafeAccess(self._checkpoints, mid)._key) {
|
||||
high = mid;
|
||||
} else {
|
||||
low = mid + 1;
|
||||
}
|
||||
}
|
||||
|
||||
uint256 pos = _upperBinaryLookup(self._checkpoints, key, low, high);
|
||||
|
||||
return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the value in the most recent checkpoint, or zero if there are no checkpoints.
|
||||
*/
|
||||
function latest(Trace224 storage self) internal view returns (uint224) {
|
||||
uint256 pos = self._checkpoints.length;
|
||||
return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns whether there is a checkpoint in the structure (i.e. it is not empty), and if so the key and value
|
||||
* in the most recent checkpoint.
|
||||
*/
|
||||
function latestCheckpoint(Trace224 storage self) internal view returns (bool exists, uint32 _key, uint224 _value) {
|
||||
uint256 pos = self._checkpoints.length;
|
||||
if (pos == 0) {
|
||||
return (false, 0, 0);
|
||||
} else {
|
||||
Checkpoint224 storage ckpt = _unsafeAccess(self._checkpoints, pos - 1);
|
||||
return (true, ckpt._key, ckpt._value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the number of checkpoints.
|
||||
*/
|
||||
function length(Trace224 storage self) internal view returns (uint256) {
|
||||
return self._checkpoints.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns checkpoint at given position.
|
||||
*/
|
||||
function at(Trace224 storage self, uint32 pos) internal view returns (Checkpoint224 memory) {
|
||||
return self._checkpoints[pos];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Pushes a (`key`, `value`) pair into an ordered list of checkpoints, either by inserting a new checkpoint,
|
||||
* or by updating the last one.
|
||||
*/
|
||||
function _insert(
|
||||
Checkpoint224[] storage self,
|
||||
uint32 key,
|
||||
uint224 value
|
||||
) private returns (uint224 oldValue, uint224 newValue) {
|
||||
uint256 pos = self.length;
|
||||
|
||||
if (pos > 0) {
|
||||
Checkpoint224 storage last = _unsafeAccess(self, pos - 1);
|
||||
uint32 lastKey = last._key;
|
||||
uint224 lastValue = last._value;
|
||||
|
||||
// Checkpoint keys must be non-decreasing.
|
||||
if (lastKey > key) {
|
||||
revert CheckpointUnorderedInsertion();
|
||||
}
|
||||
|
||||
// Update or push new checkpoint
|
||||
if (lastKey == key) {
|
||||
last._value = value;
|
||||
} else {
|
||||
self.push(Checkpoint224({_key: key, _value: value}));
|
||||
}
|
||||
return (lastValue, value);
|
||||
} else {
|
||||
self.push(Checkpoint224({_key: key, _value: value}));
|
||||
return (0, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Return the index of the first (oldest) checkpoint with key strictly bigger than the search key, or `high`
|
||||
* if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and exclusive
|
||||
* `high`.
|
||||
*
|
||||
* WARNING: `high` should not be greater than the array's length.
|
||||
*/
|
||||
function _upperBinaryLookup(
|
||||
Checkpoint224[] storage self,
|
||||
uint32 key,
|
||||
uint256 low,
|
||||
uint256 high
|
||||
) private view returns (uint256) {
|
||||
while (low < high) {
|
||||
uint256 mid = Math.average(low, high);
|
||||
if (_unsafeAccess(self, mid)._key > key) {
|
||||
high = mid;
|
||||
} else {
|
||||
low = mid + 1;
|
||||
}
|
||||
}
|
||||
return high;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Return the index of the first (oldest) checkpoint with key greater or equal than the search key, or `high`
|
||||
* if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and exclusive
|
||||
* `high`.
|
||||
*
|
||||
* WARNING: `high` should not be greater than the array's length.
|
||||
*/
|
||||
function _lowerBinaryLookup(
|
||||
Checkpoint224[] storage self,
|
||||
uint32 key,
|
||||
uint256 low,
|
||||
uint256 high
|
||||
) private view returns (uint256) {
|
||||
while (low < high) {
|
||||
uint256 mid = Math.average(low, high);
|
||||
if (_unsafeAccess(self, mid)._key < key) {
|
||||
low = mid + 1;
|
||||
} else {
|
||||
high = mid;
|
||||
}
|
||||
}
|
||||
return high;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds.
|
||||
*/
|
||||
function _unsafeAccess(
|
||||
Checkpoint224[] storage self,
|
||||
uint256 pos
|
||||
) private pure returns (Checkpoint224 storage result) {
|
||||
assembly {
|
||||
mstore(0, self.slot)
|
||||
result.slot := add(keccak256(0, 0x20), pos)
|
||||
}
|
||||
}
|
||||
|
||||
struct Trace208 {
|
||||
Checkpoint208[] _checkpoints;
|
||||
}
|
||||
|
||||
struct Checkpoint208 {
|
||||
uint48 _key;
|
||||
uint208 _value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Pushes a (`key`, `value`) pair into a Trace208 so that it is stored as the checkpoint.
|
||||
*
|
||||
* Returns previous value and new value.
|
||||
*
|
||||
* IMPORTANT: Never accept `key` as a user input, since an arbitrary `type(uint48).max` key set will disable the
|
||||
* library.
|
||||
*/
|
||||
function push(
|
||||
Trace208 storage self,
|
||||
uint48 key,
|
||||
uint208 value
|
||||
) internal returns (uint208 oldValue, uint208 newValue) {
|
||||
return _insert(self._checkpoints, key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the value in the first (oldest) checkpoint with key greater or equal than the search key, or zero if
|
||||
* there is none.
|
||||
*/
|
||||
function lowerLookup(Trace208 storage self, uint48 key) internal view returns (uint208) {
|
||||
uint256 len = self._checkpoints.length;
|
||||
uint256 pos = _lowerBinaryLookup(self._checkpoints, key, 0, len);
|
||||
return pos == len ? 0 : _unsafeAccess(self._checkpoints, pos)._value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero
|
||||
* if there is none.
|
||||
*/
|
||||
function upperLookup(Trace208 storage self, uint48 key) internal view returns (uint208) {
|
||||
uint256 len = self._checkpoints.length;
|
||||
uint256 pos = _upperBinaryLookup(self._checkpoints, key, 0, len);
|
||||
return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero
|
||||
* if there is none.
|
||||
*
|
||||
* NOTE: This is a variant of {upperLookup} that is optimized to find "recent" checkpoint (checkpoints with high
|
||||
* keys).
|
||||
*/
|
||||
function upperLookupRecent(Trace208 storage self, uint48 key) internal view returns (uint208) {
|
||||
uint256 len = self._checkpoints.length;
|
||||
|
||||
uint256 low = 0;
|
||||
uint256 high = len;
|
||||
|
||||
if (len > 5) {
|
||||
uint256 mid = len - Math.sqrt(len);
|
||||
if (key < _unsafeAccess(self._checkpoints, mid)._key) {
|
||||
high = mid;
|
||||
} else {
|
||||
low = mid + 1;
|
||||
}
|
||||
}
|
||||
|
||||
uint256 pos = _upperBinaryLookup(self._checkpoints, key, low, high);
|
||||
|
||||
return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the value in the most recent checkpoint, or zero if there are no checkpoints.
|
||||
*/
|
||||
function latest(Trace208 storage self) internal view returns (uint208) {
|
||||
uint256 pos = self._checkpoints.length;
|
||||
return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns whether there is a checkpoint in the structure (i.e. it is not empty), and if so the key and value
|
||||
* in the most recent checkpoint.
|
||||
*/
|
||||
function latestCheckpoint(Trace208 storage self) internal view returns (bool exists, uint48 _key, uint208 _value) {
|
||||
uint256 pos = self._checkpoints.length;
|
||||
if (pos == 0) {
|
||||
return (false, 0, 0);
|
||||
} else {
|
||||
Checkpoint208 storage ckpt = _unsafeAccess(self._checkpoints, pos - 1);
|
||||
return (true, ckpt._key, ckpt._value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the number of checkpoints.
|
||||
*/
|
||||
function length(Trace208 storage self) internal view returns (uint256) {
|
||||
return self._checkpoints.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns checkpoint at given position.
|
||||
*/
|
||||
function at(Trace208 storage self, uint32 pos) internal view returns (Checkpoint208 memory) {
|
||||
return self._checkpoints[pos];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Pushes a (`key`, `value`) pair into an ordered list of checkpoints, either by inserting a new checkpoint,
|
||||
* or by updating the last one.
|
||||
*/
|
||||
function _insert(
|
||||
Checkpoint208[] storage self,
|
||||
uint48 key,
|
||||
uint208 value
|
||||
) private returns (uint208 oldValue, uint208 newValue) {
|
||||
uint256 pos = self.length;
|
||||
|
||||
if (pos > 0) {
|
||||
Checkpoint208 storage last = _unsafeAccess(self, pos - 1);
|
||||
uint48 lastKey = last._key;
|
||||
uint208 lastValue = last._value;
|
||||
|
||||
// Checkpoint keys must be non-decreasing.
|
||||
if (lastKey > key) {
|
||||
revert CheckpointUnorderedInsertion();
|
||||
}
|
||||
|
||||
// Update or push new checkpoint
|
||||
if (lastKey == key) {
|
||||
last._value = value;
|
||||
} else {
|
||||
self.push(Checkpoint208({_key: key, _value: value}));
|
||||
}
|
||||
return (lastValue, value);
|
||||
} else {
|
||||
self.push(Checkpoint208({_key: key, _value: value}));
|
||||
return (0, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Return the index of the first (oldest) checkpoint with key strictly bigger than the search key, or `high`
|
||||
* if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and exclusive
|
||||
* `high`.
|
||||
*
|
||||
* WARNING: `high` should not be greater than the array's length.
|
||||
*/
|
||||
function _upperBinaryLookup(
|
||||
Checkpoint208[] storage self,
|
||||
uint48 key,
|
||||
uint256 low,
|
||||
uint256 high
|
||||
) private view returns (uint256) {
|
||||
while (low < high) {
|
||||
uint256 mid = Math.average(low, high);
|
||||
if (_unsafeAccess(self, mid)._key > key) {
|
||||
high = mid;
|
||||
} else {
|
||||
low = mid + 1;
|
||||
}
|
||||
}
|
||||
return high;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Return the index of the first (oldest) checkpoint with key greater or equal than the search key, or `high`
|
||||
* if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and exclusive
|
||||
* `high`.
|
||||
*
|
||||
* WARNING: `high` should not be greater than the array's length.
|
||||
*/
|
||||
function _lowerBinaryLookup(
|
||||
Checkpoint208[] storage self,
|
||||
uint48 key,
|
||||
uint256 low,
|
||||
uint256 high
|
||||
) private view returns (uint256) {
|
||||
while (low < high) {
|
||||
uint256 mid = Math.average(low, high);
|
||||
if (_unsafeAccess(self, mid)._key < key) {
|
||||
low = mid + 1;
|
||||
} else {
|
||||
high = mid;
|
||||
}
|
||||
}
|
||||
return high;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds.
|
||||
*/
|
||||
function _unsafeAccess(
|
||||
Checkpoint208[] storage self,
|
||||
uint256 pos
|
||||
) private pure returns (Checkpoint208 storage result) {
|
||||
assembly {
|
||||
mstore(0, self.slot)
|
||||
result.slot := add(keccak256(0, 0x20), pos)
|
||||
}
|
||||
}
|
||||
|
||||
struct Trace160 {
|
||||
Checkpoint160[] _checkpoints;
|
||||
}
|
||||
|
||||
struct Checkpoint160 {
|
||||
uint96 _key;
|
||||
uint160 _value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Pushes a (`key`, `value`) pair into a Trace160 so that it is stored as the checkpoint.
|
||||
*
|
||||
* Returns previous value and new value.
|
||||
*
|
||||
* IMPORTANT: Never accept `key` as a user input, since an arbitrary `type(uint96).max` key set will disable the
|
||||
* library.
|
||||
*/
|
||||
function push(
|
||||
Trace160 storage self,
|
||||
uint96 key,
|
||||
uint160 value
|
||||
) internal returns (uint160 oldValue, uint160 newValue) {
|
||||
return _insert(self._checkpoints, key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the value in the first (oldest) checkpoint with key greater or equal than the search key, or zero if
|
||||
* there is none.
|
||||
*/
|
||||
function lowerLookup(Trace160 storage self, uint96 key) internal view returns (uint160) {
|
||||
uint256 len = self._checkpoints.length;
|
||||
uint256 pos = _lowerBinaryLookup(self._checkpoints, key, 0, len);
|
||||
return pos == len ? 0 : _unsafeAccess(self._checkpoints, pos)._value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero
|
||||
* if there is none.
|
||||
*/
|
||||
function upperLookup(Trace160 storage self, uint96 key) internal view returns (uint160) {
|
||||
uint256 len = self._checkpoints.length;
|
||||
uint256 pos = _upperBinaryLookup(self._checkpoints, key, 0, len);
|
||||
return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero
|
||||
* if there is none.
|
||||
*
|
||||
* NOTE: This is a variant of {upperLookup} that is optimized to find "recent" checkpoint (checkpoints with high
|
||||
* keys).
|
||||
*/
|
||||
function upperLookupRecent(Trace160 storage self, uint96 key) internal view returns (uint160) {
|
||||
uint256 len = self._checkpoints.length;
|
||||
|
||||
uint256 low = 0;
|
||||
uint256 high = len;
|
||||
|
||||
if (len > 5) {
|
||||
uint256 mid = len - Math.sqrt(len);
|
||||
if (key < _unsafeAccess(self._checkpoints, mid)._key) {
|
||||
high = mid;
|
||||
} else {
|
||||
low = mid + 1;
|
||||
}
|
||||
}
|
||||
|
||||
uint256 pos = _upperBinaryLookup(self._checkpoints, key, low, high);
|
||||
|
||||
return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the value in the most recent checkpoint, or zero if there are no checkpoints.
|
||||
*/
|
||||
function latest(Trace160 storage self) internal view returns (uint160) {
|
||||
uint256 pos = self._checkpoints.length;
|
||||
return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns whether there is a checkpoint in the structure (i.e. it is not empty), and if so the key and value
|
||||
* in the most recent checkpoint.
|
||||
*/
|
||||
function latestCheckpoint(Trace160 storage self) internal view returns (bool exists, uint96 _key, uint160 _value) {
|
||||
uint256 pos = self._checkpoints.length;
|
||||
if (pos == 0) {
|
||||
return (false, 0, 0);
|
||||
} else {
|
||||
Checkpoint160 storage ckpt = _unsafeAccess(self._checkpoints, pos - 1);
|
||||
return (true, ckpt._key, ckpt._value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the number of checkpoints.
|
||||
*/
|
||||
function length(Trace160 storage self) internal view returns (uint256) {
|
||||
return self._checkpoints.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns checkpoint at given position.
|
||||
*/
|
||||
function at(Trace160 storage self, uint32 pos) internal view returns (Checkpoint160 memory) {
|
||||
return self._checkpoints[pos];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Pushes a (`key`, `value`) pair into an ordered list of checkpoints, either by inserting a new checkpoint,
|
||||
* or by updating the last one.
|
||||
*/
|
||||
function _insert(
|
||||
Checkpoint160[] storage self,
|
||||
uint96 key,
|
||||
uint160 value
|
||||
) private returns (uint160 oldValue, uint160 newValue) {
|
||||
uint256 pos = self.length;
|
||||
|
||||
if (pos > 0) {
|
||||
Checkpoint160 storage last = _unsafeAccess(self, pos - 1);
|
||||
uint96 lastKey = last._key;
|
||||
uint160 lastValue = last._value;
|
||||
|
||||
// Checkpoint keys must be non-decreasing.
|
||||
if (lastKey > key) {
|
||||
revert CheckpointUnorderedInsertion();
|
||||
}
|
||||
|
||||
// Update or push new checkpoint
|
||||
if (lastKey == key) {
|
||||
last._value = value;
|
||||
} else {
|
||||
self.push(Checkpoint160({_key: key, _value: value}));
|
||||
}
|
||||
return (lastValue, value);
|
||||
} else {
|
||||
self.push(Checkpoint160({_key: key, _value: value}));
|
||||
return (0, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Return the index of the first (oldest) checkpoint with key strictly bigger than the search key, or `high`
|
||||
* if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and exclusive
|
||||
* `high`.
|
||||
*
|
||||
* WARNING: `high` should not be greater than the array's length.
|
||||
*/
|
||||
function _upperBinaryLookup(
|
||||
Checkpoint160[] storage self,
|
||||
uint96 key,
|
||||
uint256 low,
|
||||
uint256 high
|
||||
) private view returns (uint256) {
|
||||
while (low < high) {
|
||||
uint256 mid = Math.average(low, high);
|
||||
if (_unsafeAccess(self, mid)._key > key) {
|
||||
high = mid;
|
||||
} else {
|
||||
low = mid + 1;
|
||||
}
|
||||
}
|
||||
return high;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Return the index of the first (oldest) checkpoint with key greater or equal than the search key, or `high`
|
||||
* if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and exclusive
|
||||
* `high`.
|
||||
*
|
||||
* WARNING: `high` should not be greater than the array's length.
|
||||
*/
|
||||
function _lowerBinaryLookup(
|
||||
Checkpoint160[] storage self,
|
||||
uint96 key,
|
||||
uint256 low,
|
||||
uint256 high
|
||||
) private view returns (uint256) {
|
||||
while (low < high) {
|
||||
uint256 mid = Math.average(low, high);
|
||||
if (_unsafeAccess(self, mid)._key < key) {
|
||||
low = mid + 1;
|
||||
} else {
|
||||
high = mid;
|
||||
}
|
||||
}
|
||||
return high;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds.
|
||||
*/
|
||||
function _unsafeAccess(
|
||||
Checkpoint160[] storage self,
|
||||
uint256 pos
|
||||
) private pure returns (Checkpoint160 storage result) {
|
||||
assembly {
|
||||
mstore(0, self.slot)
|
||||
result.slot := add(keccak256(0, 0x20), pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
140
dev/env/node_modules/@openzeppelin/contracts/utils/structs/CircularBuffer.sol
generated
vendored
Executable file
140
dev/env/node_modules/@openzeppelin/contracts/utils/structs/CircularBuffer.sol
generated
vendored
Executable file
@@ -0,0 +1,140 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v5.3.0) (utils/structs/CircularBuffer.sol)
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import {Math} from "../math/Math.sol";
|
||||
import {Arrays} from "../Arrays.sol";
|
||||
import {Panic} from "../Panic.sol";
|
||||
|
||||
/**
|
||||
* @dev A fixed-size buffer for keeping `bytes32` items in storage.
|
||||
*
|
||||
* This data structure allows for pushing elements to it, and when its length exceeds the specified fixed size,
|
||||
* new items take the place of the oldest element in the buffer, keeping at most `N` elements in the
|
||||
* structure.
|
||||
*
|
||||
* Elements can't be removed but the data structure can be cleared. See {clear}.
|
||||
*
|
||||
* Complexity:
|
||||
* - insertion ({push}): O(1)
|
||||
* - lookup ({last}): O(1)
|
||||
* - inclusion ({includes}): O(N) (worst case)
|
||||
* - reset ({clear}): O(1)
|
||||
*
|
||||
* * The struct is called `Bytes32CircularBuffer`. Other types can be cast to and from `bytes32`. This data structure
|
||||
* can only be used in storage, and not in memory.
|
||||
*
|
||||
* Example usage:
|
||||
*
|
||||
* ```solidity
|
||||
* contract Example {
|
||||
* // Add the library methods
|
||||
* using CircularBuffer for CircularBuffer.Bytes32CircularBuffer;
|
||||
*
|
||||
* // Declare a buffer storage variable
|
||||
* CircularBuffer.Bytes32CircularBuffer private myBuffer;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* _Available since v5.1._
|
||||
*/
|
||||
library CircularBuffer {
|
||||
/**
|
||||
* @dev Error emitted when trying to setup a buffer with a size of 0.
|
||||
*/
|
||||
error InvalidBufferSize();
|
||||
|
||||
/**
|
||||
* @dev Counts the number of items that have been pushed to the buffer. The residuo modulo _data.length indicates
|
||||
* where the next value should be stored.
|
||||
*
|
||||
* Struct members have an underscore prefix indicating that they are "private" and should not be read or written to
|
||||
* directly. Use the functions provided below instead. Modifying the struct manually may violate assumptions and
|
||||
* lead to unexpected behavior.
|
||||
*
|
||||
* In a full buffer:
|
||||
* - The most recently pushed item (last) is at data[(index - 1) % data.length]
|
||||
* - The oldest item (first) is at data[index % data.length]
|
||||
*/
|
||||
struct Bytes32CircularBuffer {
|
||||
uint256 _count;
|
||||
bytes32[] _data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Initialize a new CircularBuffer of a given size.
|
||||
*
|
||||
* If the CircularBuffer was already setup and used, calling that function again will reset it to a blank state.
|
||||
*
|
||||
* NOTE: The size of the buffer will affect the execution of {includes} function, as it has a complexity of O(N).
|
||||
* Consider a large buffer size may render the function unusable.
|
||||
*/
|
||||
function setup(Bytes32CircularBuffer storage self, uint256 size) internal {
|
||||
if (size == 0) revert InvalidBufferSize();
|
||||
clear(self);
|
||||
Arrays.unsafeSetLength(self._data, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Clear all data in the buffer without resetting memory, keeping the existing size.
|
||||
*/
|
||||
function clear(Bytes32CircularBuffer storage self) internal {
|
||||
self._count = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Push a new value to the buffer. If the buffer is already full, the new value replaces the oldest value in
|
||||
* the buffer.
|
||||
*/
|
||||
function push(Bytes32CircularBuffer storage self, bytes32 value) internal {
|
||||
uint256 index = self._count++;
|
||||
uint256 modulus = self._data.length;
|
||||
Arrays.unsafeAccess(self._data, index % modulus).value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Number of values currently in the buffer. This value is 0 for an empty buffer, and cannot exceed the size of
|
||||
* the buffer.
|
||||
*/
|
||||
function count(Bytes32CircularBuffer storage self) internal view returns (uint256) {
|
||||
return Math.min(self._count, self._data.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Length of the buffer. This is the maximum number of elements kept in the buffer.
|
||||
*/
|
||||
function length(Bytes32CircularBuffer storage self) internal view returns (uint256) {
|
||||
return self._data.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Getter for the i-th value in the buffer, from the end.
|
||||
*
|
||||
* Reverts with {Panic-ARRAY_OUT_OF_BOUNDS} if trying to access an element that was not pushed, or that was
|
||||
* dropped to make room for newer elements.
|
||||
*/
|
||||
function last(Bytes32CircularBuffer storage self, uint256 i) internal view returns (bytes32) {
|
||||
uint256 index = self._count;
|
||||
uint256 modulus = self._data.length;
|
||||
uint256 total = Math.min(index, modulus); // count(self)
|
||||
if (i >= total) {
|
||||
Panic.panic(Panic.ARRAY_OUT_OF_BOUNDS);
|
||||
}
|
||||
return Arrays.unsafeAccess(self._data, (index - i - 1) % modulus).value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Check if a given value is in the buffer.
|
||||
*/
|
||||
function includes(Bytes32CircularBuffer storage self, bytes32 value) internal view returns (bool) {
|
||||
uint256 index = self._count;
|
||||
uint256 modulus = self._data.length;
|
||||
uint256 total = Math.min(index, modulus); // count(self)
|
||||
for (uint256 i = 0; i < total; ++i) {
|
||||
if (Arrays.unsafeAccess(self._data, (index - i - 1) % modulus).value == value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
156
dev/env/node_modules/@openzeppelin/contracts/utils/structs/DoubleEndedQueue.sol
generated
vendored
Executable file
156
dev/env/node_modules/@openzeppelin/contracts/utils/structs/DoubleEndedQueue.sol
generated
vendored
Executable file
@@ -0,0 +1,156 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v5.1.0) (utils/structs/DoubleEndedQueue.sol)
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import {Panic} from "../Panic.sol";
|
||||
|
||||
/**
|
||||
* @dev A sequence of items with the ability to efficiently push and pop items (i.e. insert and remove) on both ends of
|
||||
* the sequence (called front and back). Among other access patterns, it can be used to implement efficient LIFO and
|
||||
* FIFO queues. Storage use is optimized, and all operations are O(1) constant time. This includes {clear}, given that
|
||||
* the existing queue contents are left in storage.
|
||||
*
|
||||
* The struct is called `Bytes32Deque`. Other types can be cast to and from `bytes32`. This data structure can only be
|
||||
* used in storage, and not in memory.
|
||||
* ```solidity
|
||||
* DoubleEndedQueue.Bytes32Deque queue;
|
||||
* ```
|
||||
*/
|
||||
library DoubleEndedQueue {
|
||||
/**
|
||||
* @dev Indices are 128 bits so begin and end are packed in a single storage slot for efficient access.
|
||||
*
|
||||
* Struct members have an underscore prefix indicating that they are "private" and should not be read or written to
|
||||
* directly. Use the functions provided below instead. Modifying the struct manually may violate assumptions and
|
||||
* lead to unexpected behavior.
|
||||
*
|
||||
* The first item is at data[begin] and the last item is at data[end - 1]. This range can wrap around.
|
||||
*/
|
||||
struct Bytes32Deque {
|
||||
uint128 _begin;
|
||||
uint128 _end;
|
||||
mapping(uint128 index => bytes32) _data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Inserts an item at the end of the queue.
|
||||
*
|
||||
* Reverts with {Panic-RESOURCE_ERROR} if the queue is full.
|
||||
*/
|
||||
function pushBack(Bytes32Deque storage deque, bytes32 value) internal {
|
||||
unchecked {
|
||||
uint128 backIndex = deque._end;
|
||||
if (backIndex + 1 == deque._begin) Panic.panic(Panic.RESOURCE_ERROR);
|
||||
deque._data[backIndex] = value;
|
||||
deque._end = backIndex + 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Removes the item at the end of the queue and returns it.
|
||||
*
|
||||
* Reverts with {Panic-EMPTY_ARRAY_POP} if the queue is empty.
|
||||
*/
|
||||
function popBack(Bytes32Deque storage deque) internal returns (bytes32 value) {
|
||||
unchecked {
|
||||
uint128 backIndex = deque._end;
|
||||
if (backIndex == deque._begin) Panic.panic(Panic.EMPTY_ARRAY_POP);
|
||||
--backIndex;
|
||||
value = deque._data[backIndex];
|
||||
delete deque._data[backIndex];
|
||||
deque._end = backIndex;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Inserts an item at the beginning of the queue.
|
||||
*
|
||||
* Reverts with {Panic-RESOURCE_ERROR} if the queue is full.
|
||||
*/
|
||||
function pushFront(Bytes32Deque storage deque, bytes32 value) internal {
|
||||
unchecked {
|
||||
uint128 frontIndex = deque._begin - 1;
|
||||
if (frontIndex == deque._end) Panic.panic(Panic.RESOURCE_ERROR);
|
||||
deque._data[frontIndex] = value;
|
||||
deque._begin = frontIndex;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Removes the item at the beginning of the queue and returns it.
|
||||
*
|
||||
* Reverts with {Panic-EMPTY_ARRAY_POP} if the queue is empty.
|
||||
*/
|
||||
function popFront(Bytes32Deque storage deque) internal returns (bytes32 value) {
|
||||
unchecked {
|
||||
uint128 frontIndex = deque._begin;
|
||||
if (frontIndex == deque._end) Panic.panic(Panic.EMPTY_ARRAY_POP);
|
||||
value = deque._data[frontIndex];
|
||||
delete deque._data[frontIndex];
|
||||
deque._begin = frontIndex + 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the item at the beginning of the queue.
|
||||
*
|
||||
* Reverts with {Panic-ARRAY_OUT_OF_BOUNDS} if the queue is empty.
|
||||
*/
|
||||
function front(Bytes32Deque storage deque) internal view returns (bytes32 value) {
|
||||
if (empty(deque)) Panic.panic(Panic.ARRAY_OUT_OF_BOUNDS);
|
||||
return deque._data[deque._begin];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the item at the end of the queue.
|
||||
*
|
||||
* Reverts with {Panic-ARRAY_OUT_OF_BOUNDS} if the queue is empty.
|
||||
*/
|
||||
function back(Bytes32Deque storage deque) internal view returns (bytes32 value) {
|
||||
if (empty(deque)) Panic.panic(Panic.ARRAY_OUT_OF_BOUNDS);
|
||||
unchecked {
|
||||
return deque._data[deque._end - 1];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Return the item at a position in the queue given by `index`, with the first item at 0 and last item at
|
||||
* `length(deque) - 1`.
|
||||
*
|
||||
* Reverts with {Panic-ARRAY_OUT_OF_BOUNDS} if the index is out of bounds.
|
||||
*/
|
||||
function at(Bytes32Deque storage deque, uint256 index) internal view returns (bytes32 value) {
|
||||
if (index >= length(deque)) Panic.panic(Panic.ARRAY_OUT_OF_BOUNDS);
|
||||
// By construction, length is a uint128, so the check above ensures that index can be safely downcast to uint128
|
||||
unchecked {
|
||||
return deque._data[deque._begin + uint128(index)];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Resets the queue back to being empty.
|
||||
*
|
||||
* NOTE: The current items are left behind in storage. This does not affect the functioning of the queue, but misses
|
||||
* out on potential gas refunds.
|
||||
*/
|
||||
function clear(Bytes32Deque storage deque) internal {
|
||||
deque._begin = 0;
|
||||
deque._end = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the number of items in the queue.
|
||||
*/
|
||||
function length(Bytes32Deque storage deque) internal view returns (uint256) {
|
||||
unchecked {
|
||||
return uint256(deque._end - deque._begin);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns true if the queue is empty.
|
||||
*/
|
||||
function empty(Bytes32Deque storage deque) internal view returns (bool) {
|
||||
return deque._end == deque._begin;
|
||||
}
|
||||
}
|
||||
1319
dev/env/node_modules/@openzeppelin/contracts/utils/structs/EnumerableMap.sol
generated
vendored
Executable file
1319
dev/env/node_modules/@openzeppelin/contracts/utils/structs/EnumerableMap.sol
generated
vendored
Executable file
File diff suppressed because it is too large
Load Diff
792
dev/env/node_modules/@openzeppelin/contracts/utils/structs/EnumerableSet.sol
generated
vendored
Executable file
792
dev/env/node_modules/@openzeppelin/contracts/utils/structs/EnumerableSet.sol
generated
vendored
Executable file
@@ -0,0 +1,792 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v5.4.0) (utils/structs/EnumerableSet.sol)
|
||||
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
|
||||
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import {Arrays} from "../Arrays.sol";
|
||||
import {Math} from "../math/Math.sol";
|
||||
|
||||
/**
|
||||
* @dev Library for managing
|
||||
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
|
||||
* types.
|
||||
*
|
||||
* Sets have the following properties:
|
||||
*
|
||||
* - Elements are added, removed, and checked for existence in constant time
|
||||
* (O(1)).
|
||||
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
|
||||
* - Set can be cleared (all elements removed) in O(n).
|
||||
*
|
||||
* ```solidity
|
||||
* contract Example {
|
||||
* // Add the library methods
|
||||
* using EnumerableSet for EnumerableSet.AddressSet;
|
||||
*
|
||||
* // Declare a set state variable
|
||||
* EnumerableSet.AddressSet private mySet;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* The following types are supported:
|
||||
*
|
||||
* - `bytes32` (`Bytes32Set`) since v3.3.0
|
||||
* - `address` (`AddressSet`) since v3.3.0
|
||||
* - `uint256` (`UintSet`) since v3.3.0
|
||||
* - `string` (`StringSet`) since v5.4.0
|
||||
* - `bytes` (`BytesSet`) since v5.4.0
|
||||
*
|
||||
* [WARNING]
|
||||
* ====
|
||||
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
|
||||
* unusable.
|
||||
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
|
||||
*
|
||||
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
|
||||
* array of EnumerableSet.
|
||||
* ====
|
||||
*/
|
||||
library EnumerableSet {
|
||||
// To implement this library for multiple types with as little code
|
||||
// repetition as possible, we write it in terms of a generic Set type with
|
||||
// bytes32 values.
|
||||
// The Set implementation uses private functions, and user-facing
|
||||
// implementations (such as AddressSet) are just wrappers around the
|
||||
// underlying Set.
|
||||
// This means that we can only create new EnumerableSets for types that fit
|
||||
// in bytes32.
|
||||
|
||||
struct Set {
|
||||
// Storage of set values
|
||||
bytes32[] _values;
|
||||
// Position is the index of the value in the `values` array plus 1.
|
||||
// Position 0 is used to mean a value is not in the set.
|
||||
mapping(bytes32 value => uint256) _positions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Add a value to a set. O(1).
|
||||
*
|
||||
* Returns true if the value was added to the set, that is if it was not
|
||||
* already present.
|
||||
*/
|
||||
function _add(Set storage set, bytes32 value) private returns (bool) {
|
||||
if (!_contains(set, value)) {
|
||||
set._values.push(value);
|
||||
// The value is stored at length-1, but we add 1 to all indexes
|
||||
// and use 0 as a sentinel value
|
||||
set._positions[value] = set._values.length;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Removes a value from a set. O(1).
|
||||
*
|
||||
* Returns true if the value was removed from the set, that is if it was
|
||||
* present.
|
||||
*/
|
||||
function _remove(Set storage set, bytes32 value) private returns (bool) {
|
||||
// We cache the value's position to prevent multiple reads from the same storage slot
|
||||
uint256 position = set._positions[value];
|
||||
|
||||
if (position != 0) {
|
||||
// Equivalent to contains(set, value)
|
||||
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
|
||||
// the array, and then remove the last element (sometimes called as 'swap and pop').
|
||||
// This modifies the order of the array, as noted in {at}.
|
||||
|
||||
uint256 valueIndex = position - 1;
|
||||
uint256 lastIndex = set._values.length - 1;
|
||||
|
||||
if (valueIndex != lastIndex) {
|
||||
bytes32 lastValue = set._values[lastIndex];
|
||||
|
||||
// Move the lastValue to the index where the value to delete is
|
||||
set._values[valueIndex] = lastValue;
|
||||
// Update the tracked position of the lastValue (that was just moved)
|
||||
set._positions[lastValue] = position;
|
||||
}
|
||||
|
||||
// Delete the slot where the moved value was stored
|
||||
set._values.pop();
|
||||
|
||||
// Delete the tracked position for the deleted slot
|
||||
delete set._positions[value];
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Removes all the values from a set. O(n).
|
||||
*
|
||||
* WARNING: This function has an unbounded cost that scales with set size. Developers should keep in mind that
|
||||
* using it may render the function uncallable if the set grows to the point where clearing it consumes too much
|
||||
* gas to fit in a block.
|
||||
*/
|
||||
function _clear(Set storage set) private {
|
||||
uint256 len = _length(set);
|
||||
for (uint256 i = 0; i < len; ++i) {
|
||||
delete set._positions[set._values[i]];
|
||||
}
|
||||
Arrays.unsafeSetLength(set._values, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns true if the value is in the set. O(1).
|
||||
*/
|
||||
function _contains(Set storage set, bytes32 value) private view returns (bool) {
|
||||
return set._positions[value] != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the number of values on the set. O(1).
|
||||
*/
|
||||
function _length(Set storage set) private view returns (uint256) {
|
||||
return set._values.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the value stored at position `index` in the set. O(1).
|
||||
*
|
||||
* Note that there are no guarantees on the ordering of values inside the
|
||||
* array, and it may change when more values are added or removed.
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - `index` must be strictly less than {length}.
|
||||
*/
|
||||
function _at(Set storage set, uint256 index) private view returns (bytes32) {
|
||||
return set._values[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Return the entire set in an array
|
||||
*
|
||||
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
|
||||
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
|
||||
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
|
||||
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
|
||||
*/
|
||||
function _values(Set storage set) private view returns (bytes32[] memory) {
|
||||
return set._values;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Return a slice of the set in an array
|
||||
*
|
||||
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
|
||||
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
|
||||
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
|
||||
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
|
||||
*/
|
||||
function _values(Set storage set, uint256 start, uint256 end) private view returns (bytes32[] memory) {
|
||||
unchecked {
|
||||
end = Math.min(end, _length(set));
|
||||
start = Math.min(start, end);
|
||||
|
||||
uint256 len = end - start;
|
||||
bytes32[] memory result = new bytes32[](len);
|
||||
for (uint256 i = 0; i < len; ++i) {
|
||||
result[i] = Arrays.unsafeAccess(set._values, start + i).value;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// Bytes32Set
|
||||
|
||||
struct Bytes32Set {
|
||||
Set _inner;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Add a value to a set. O(1).
|
||||
*
|
||||
* Returns true if the value was added to the set, that is if it was not
|
||||
* already present.
|
||||
*/
|
||||
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
|
||||
return _add(set._inner, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Removes a value from a set. O(1).
|
||||
*
|
||||
* Returns true if the value was removed from the set, that is if it was
|
||||
* present.
|
||||
*/
|
||||
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
|
||||
return _remove(set._inner, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Removes all the values from a set. O(n).
|
||||
*
|
||||
* WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
|
||||
* function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
|
||||
*/
|
||||
function clear(Bytes32Set storage set) internal {
|
||||
_clear(set._inner);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns true if the value is in the set. O(1).
|
||||
*/
|
||||
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
|
||||
return _contains(set._inner, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the number of values in the set. O(1).
|
||||
*/
|
||||
function length(Bytes32Set storage set) internal view returns (uint256) {
|
||||
return _length(set._inner);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the value stored at position `index` in the set. O(1).
|
||||
*
|
||||
* Note that there are no guarantees on the ordering of values inside the
|
||||
* array, and it may change when more values are added or removed.
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - `index` must be strictly less than {length}.
|
||||
*/
|
||||
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
|
||||
return _at(set._inner, index);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Return the entire set in an array
|
||||
*
|
||||
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
|
||||
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
|
||||
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
|
||||
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
|
||||
*/
|
||||
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
|
||||
bytes32[] memory store = _values(set._inner);
|
||||
bytes32[] memory result;
|
||||
|
||||
assembly ("memory-safe") {
|
||||
result := store
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Return a slice of the set in an array
|
||||
*
|
||||
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
|
||||
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
|
||||
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
|
||||
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
|
||||
*/
|
||||
function values(Bytes32Set storage set, uint256 start, uint256 end) internal view returns (bytes32[] memory) {
|
||||
bytes32[] memory store = _values(set._inner, start, end);
|
||||
bytes32[] memory result;
|
||||
|
||||
assembly ("memory-safe") {
|
||||
result := store
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// AddressSet
|
||||
|
||||
struct AddressSet {
|
||||
Set _inner;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Add a value to a set. O(1).
|
||||
*
|
||||
* Returns true if the value was added to the set, that is if it was not
|
||||
* already present.
|
||||
*/
|
||||
function add(AddressSet storage set, address value) internal returns (bool) {
|
||||
return _add(set._inner, bytes32(uint256(uint160(value))));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Removes a value from a set. O(1).
|
||||
*
|
||||
* Returns true if the value was removed from the set, that is if it was
|
||||
* present.
|
||||
*/
|
||||
function remove(AddressSet storage set, address value) internal returns (bool) {
|
||||
return _remove(set._inner, bytes32(uint256(uint160(value))));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Removes all the values from a set. O(n).
|
||||
*
|
||||
* WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
|
||||
* function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
|
||||
*/
|
||||
function clear(AddressSet storage set) internal {
|
||||
_clear(set._inner);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns true if the value is in the set. O(1).
|
||||
*/
|
||||
function contains(AddressSet storage set, address value) internal view returns (bool) {
|
||||
return _contains(set._inner, bytes32(uint256(uint160(value))));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the number of values in the set. O(1).
|
||||
*/
|
||||
function length(AddressSet storage set) internal view returns (uint256) {
|
||||
return _length(set._inner);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the value stored at position `index` in the set. O(1).
|
||||
*
|
||||
* Note that there are no guarantees on the ordering of values inside the
|
||||
* array, and it may change when more values are added or removed.
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - `index` must be strictly less than {length}.
|
||||
*/
|
||||
function at(AddressSet storage set, uint256 index) internal view returns (address) {
|
||||
return address(uint160(uint256(_at(set._inner, index))));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Return the entire set in an array
|
||||
*
|
||||
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
|
||||
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
|
||||
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
|
||||
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
|
||||
*/
|
||||
function values(AddressSet storage set) internal view returns (address[] memory) {
|
||||
bytes32[] memory store = _values(set._inner);
|
||||
address[] memory result;
|
||||
|
||||
assembly ("memory-safe") {
|
||||
result := store
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Return a slice of the set in an array
|
||||
*
|
||||
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
|
||||
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
|
||||
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
|
||||
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
|
||||
*/
|
||||
function values(AddressSet storage set, uint256 start, uint256 end) internal view returns (address[] memory) {
|
||||
bytes32[] memory store = _values(set._inner, start, end);
|
||||
address[] memory result;
|
||||
|
||||
assembly ("memory-safe") {
|
||||
result := store
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// UintSet
|
||||
|
||||
struct UintSet {
|
||||
Set _inner;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Add a value to a set. O(1).
|
||||
*
|
||||
* Returns true if the value was added to the set, that is if it was not
|
||||
* already present.
|
||||
*/
|
||||
function add(UintSet storage set, uint256 value) internal returns (bool) {
|
||||
return _add(set._inner, bytes32(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Removes a value from a set. O(1).
|
||||
*
|
||||
* Returns true if the value was removed from the set, that is if it was
|
||||
* present.
|
||||
*/
|
||||
function remove(UintSet storage set, uint256 value) internal returns (bool) {
|
||||
return _remove(set._inner, bytes32(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Removes all the values from a set. O(n).
|
||||
*
|
||||
* WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
|
||||
* function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
|
||||
*/
|
||||
function clear(UintSet storage set) internal {
|
||||
_clear(set._inner);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns true if the value is in the set. O(1).
|
||||
*/
|
||||
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
|
||||
return _contains(set._inner, bytes32(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the number of values in the set. O(1).
|
||||
*/
|
||||
function length(UintSet storage set) internal view returns (uint256) {
|
||||
return _length(set._inner);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the value stored at position `index` in the set. O(1).
|
||||
*
|
||||
* Note that there are no guarantees on the ordering of values inside the
|
||||
* array, and it may change when more values are added or removed.
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - `index` must be strictly less than {length}.
|
||||
*/
|
||||
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
|
||||
return uint256(_at(set._inner, index));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Return the entire set in an array
|
||||
*
|
||||
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
|
||||
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
|
||||
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
|
||||
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
|
||||
*/
|
||||
function values(UintSet storage set) internal view returns (uint256[] memory) {
|
||||
bytes32[] memory store = _values(set._inner);
|
||||
uint256[] memory result;
|
||||
|
||||
assembly ("memory-safe") {
|
||||
result := store
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Return a slice of the set in an array
|
||||
*
|
||||
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
|
||||
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
|
||||
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
|
||||
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
|
||||
*/
|
||||
function values(UintSet storage set, uint256 start, uint256 end) internal view returns (uint256[] memory) {
|
||||
bytes32[] memory store = _values(set._inner, start, end);
|
||||
uint256[] memory result;
|
||||
|
||||
assembly ("memory-safe") {
|
||||
result := store
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
struct StringSet {
|
||||
// Storage of set values
|
||||
string[] _values;
|
||||
// Position is the index of the value in the `values` array plus 1.
|
||||
// Position 0 is used to mean a value is not in the set.
|
||||
mapping(string value => uint256) _positions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Add a value to a set. O(1).
|
||||
*
|
||||
* Returns true if the value was added to the set, that is if it was not
|
||||
* already present.
|
||||
*/
|
||||
function add(StringSet storage set, string memory value) internal returns (bool) {
|
||||
if (!contains(set, value)) {
|
||||
set._values.push(value);
|
||||
// The value is stored at length-1, but we add 1 to all indexes
|
||||
// and use 0 as a sentinel value
|
||||
set._positions[value] = set._values.length;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Removes a value from a set. O(1).
|
||||
*
|
||||
* Returns true if the value was removed from the set, that is if it was
|
||||
* present.
|
||||
*/
|
||||
function remove(StringSet storage set, string memory value) internal returns (bool) {
|
||||
// We cache the value's position to prevent multiple reads from the same storage slot
|
||||
uint256 position = set._positions[value];
|
||||
|
||||
if (position != 0) {
|
||||
// Equivalent to contains(set, value)
|
||||
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
|
||||
// the array, and then remove the last element (sometimes called as 'swap and pop').
|
||||
// This modifies the order of the array, as noted in {at}.
|
||||
|
||||
uint256 valueIndex = position - 1;
|
||||
uint256 lastIndex = set._values.length - 1;
|
||||
|
||||
if (valueIndex != lastIndex) {
|
||||
string memory lastValue = set._values[lastIndex];
|
||||
|
||||
// Move the lastValue to the index where the value to delete is
|
||||
set._values[valueIndex] = lastValue;
|
||||
// Update the tracked position of the lastValue (that was just moved)
|
||||
set._positions[lastValue] = position;
|
||||
}
|
||||
|
||||
// Delete the slot where the moved value was stored
|
||||
set._values.pop();
|
||||
|
||||
// Delete the tracked position for the deleted slot
|
||||
delete set._positions[value];
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Removes all the values from a set. O(n).
|
||||
*
|
||||
* WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
|
||||
* function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
|
||||
*/
|
||||
function clear(StringSet storage set) internal {
|
||||
uint256 len = length(set);
|
||||
for (uint256 i = 0; i < len; ++i) {
|
||||
delete set._positions[set._values[i]];
|
||||
}
|
||||
Arrays.unsafeSetLength(set._values, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns true if the value is in the set. O(1).
|
||||
*/
|
||||
function contains(StringSet storage set, string memory value) internal view returns (bool) {
|
||||
return set._positions[value] != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the number of values on the set. O(1).
|
||||
*/
|
||||
function length(StringSet storage set) internal view returns (uint256) {
|
||||
return set._values.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the value stored at position `index` in the set. O(1).
|
||||
*
|
||||
* Note that there are no guarantees on the ordering of values inside the
|
||||
* array, and it may change when more values are added or removed.
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - `index` must be strictly less than {length}.
|
||||
*/
|
||||
function at(StringSet storage set, uint256 index) internal view returns (string memory) {
|
||||
return set._values[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Return the entire set in an array
|
||||
*
|
||||
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
|
||||
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
|
||||
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
|
||||
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
|
||||
*/
|
||||
function values(StringSet storage set) internal view returns (string[] memory) {
|
||||
return set._values;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Return a slice of the set in an array
|
||||
*
|
||||
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
|
||||
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
|
||||
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
|
||||
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
|
||||
*/
|
||||
function values(StringSet storage set, uint256 start, uint256 end) internal view returns (string[] memory) {
|
||||
unchecked {
|
||||
end = Math.min(end, length(set));
|
||||
start = Math.min(start, end);
|
||||
|
||||
uint256 len = end - start;
|
||||
string[] memory result = new string[](len);
|
||||
for (uint256 i = 0; i < len; ++i) {
|
||||
result[i] = Arrays.unsafeAccess(set._values, start + i).value;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
struct BytesSet {
|
||||
// Storage of set values
|
||||
bytes[] _values;
|
||||
// Position is the index of the value in the `values` array plus 1.
|
||||
// Position 0 is used to mean a value is not in the set.
|
||||
mapping(bytes value => uint256) _positions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Add a value to a set. O(1).
|
||||
*
|
||||
* Returns true if the value was added to the set, that is if it was not
|
||||
* already present.
|
||||
*/
|
||||
function add(BytesSet storage set, bytes memory value) internal returns (bool) {
|
||||
if (!contains(set, value)) {
|
||||
set._values.push(value);
|
||||
// The value is stored at length-1, but we add 1 to all indexes
|
||||
// and use 0 as a sentinel value
|
||||
set._positions[value] = set._values.length;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Removes a value from a set. O(1).
|
||||
*
|
||||
* Returns true if the value was removed from the set, that is if it was
|
||||
* present.
|
||||
*/
|
||||
function remove(BytesSet storage set, bytes memory value) internal returns (bool) {
|
||||
// We cache the value's position to prevent multiple reads from the same storage slot
|
||||
uint256 position = set._positions[value];
|
||||
|
||||
if (position != 0) {
|
||||
// Equivalent to contains(set, value)
|
||||
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
|
||||
// the array, and then remove the last element (sometimes called as 'swap and pop').
|
||||
// This modifies the order of the array, as noted in {at}.
|
||||
|
||||
uint256 valueIndex = position - 1;
|
||||
uint256 lastIndex = set._values.length - 1;
|
||||
|
||||
if (valueIndex != lastIndex) {
|
||||
bytes memory lastValue = set._values[lastIndex];
|
||||
|
||||
// Move the lastValue to the index where the value to delete is
|
||||
set._values[valueIndex] = lastValue;
|
||||
// Update the tracked position of the lastValue (that was just moved)
|
||||
set._positions[lastValue] = position;
|
||||
}
|
||||
|
||||
// Delete the slot where the moved value was stored
|
||||
set._values.pop();
|
||||
|
||||
// Delete the tracked position for the deleted slot
|
||||
delete set._positions[value];
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Removes all the values from a set. O(n).
|
||||
*
|
||||
* WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
|
||||
* function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
|
||||
*/
|
||||
function clear(BytesSet storage set) internal {
|
||||
uint256 len = length(set);
|
||||
for (uint256 i = 0; i < len; ++i) {
|
||||
delete set._positions[set._values[i]];
|
||||
}
|
||||
Arrays.unsafeSetLength(set._values, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns true if the value is in the set. O(1).
|
||||
*/
|
||||
function contains(BytesSet storage set, bytes memory value) internal view returns (bool) {
|
||||
return set._positions[value] != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the number of values on the set. O(1).
|
||||
*/
|
||||
function length(BytesSet storage set) internal view returns (uint256) {
|
||||
return set._values.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the value stored at position `index` in the set. O(1).
|
||||
*
|
||||
* Note that there are no guarantees on the ordering of values inside the
|
||||
* array, and it may change when more values are added or removed.
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - `index` must be strictly less than {length}.
|
||||
*/
|
||||
function at(BytesSet storage set, uint256 index) internal view returns (bytes memory) {
|
||||
return set._values[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Return the entire set in an array
|
||||
*
|
||||
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
|
||||
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
|
||||
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
|
||||
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
|
||||
*/
|
||||
function values(BytesSet storage set) internal view returns (bytes[] memory) {
|
||||
return set._values;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Return a slice of the set in an array
|
||||
*
|
||||
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
|
||||
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
|
||||
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
|
||||
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
|
||||
*/
|
||||
function values(BytesSet storage set, uint256 start, uint256 end) internal view returns (bytes[] memory) {
|
||||
unchecked {
|
||||
end = Math.min(end, length(set));
|
||||
start = Math.min(start, end);
|
||||
|
||||
uint256 len = end - start;
|
||||
bytes[] memory result = new bytes[](len);
|
||||
for (uint256 i = 0; i < len; ++i) {
|
||||
result[i] = Arrays.unsafeAccess(set._values, start + i).value;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
256
dev/env/node_modules/@openzeppelin/contracts/utils/structs/Heap.sol
generated
vendored
Executable file
256
dev/env/node_modules/@openzeppelin/contracts/utils/structs/Heap.sol
generated
vendored
Executable file
@@ -0,0 +1,256 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v5.1.0) (utils/structs/Heap.sol)
|
||||
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import {Math} from "../math/Math.sol";
|
||||
import {SafeCast} from "../math/SafeCast.sol";
|
||||
import {Comparators} from "../Comparators.sol";
|
||||
import {Arrays} from "../Arrays.sol";
|
||||
import {Panic} from "../Panic.sol";
|
||||
import {StorageSlot} from "../StorageSlot.sol";
|
||||
|
||||
/**
|
||||
* @dev Library for managing https://en.wikipedia.org/wiki/Binary_heap[binary heap] that can be used as
|
||||
* https://en.wikipedia.org/wiki/Priority_queue[priority queue].
|
||||
*
|
||||
* Heaps are represented as a tree of values where the first element (index 0) is the root, and where the node at
|
||||
* index i is the child of the node at index (i-1)/2 and the parent of nodes at index 2*i+1 and 2*i+2. Each node
|
||||
* stores an element of the heap.
|
||||
*
|
||||
* The structure is ordered so that each node is bigger than its parent. An immediate consequence is that the
|
||||
* highest priority value is the one at the root. This value can be looked up in constant time (O(1)) at
|
||||
* `heap.tree[0]`
|
||||
*
|
||||
* The structure is designed to perform the following operations with the corresponding complexities:
|
||||
*
|
||||
* * peek (get the highest priority value): O(1)
|
||||
* * insert (insert a value): O(log(n))
|
||||
* * pop (remove the highest priority value): O(log(n))
|
||||
* * replace (replace the highest priority value with a new value): O(log(n))
|
||||
* * length (get the number of elements): O(1)
|
||||
* * clear (remove all elements): O(1)
|
||||
*
|
||||
* IMPORTANT: This library allows for the use of custom comparator functions. Given that manipulating
|
||||
* memory can lead to unexpected behavior. Consider verifying that the comparator does not manipulate
|
||||
* the Heap's state directly and that it follows the Solidity memory safety rules.
|
||||
*
|
||||
* _Available since v5.1._
|
||||
*/
|
||||
library Heap {
|
||||
using Arrays for *;
|
||||
using Math for *;
|
||||
using SafeCast for *;
|
||||
|
||||
/**
|
||||
* @dev Binary heap that supports values of type uint256.
|
||||
*
|
||||
* Each element of that structure uses one storage slot.
|
||||
*/
|
||||
struct Uint256Heap {
|
||||
uint256[] tree;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Lookup the root element of the heap.
|
||||
*/
|
||||
function peek(Uint256Heap storage self) internal view returns (uint256) {
|
||||
// self.tree[0] will `ARRAY_ACCESS_OUT_OF_BOUNDS` panic if heap is empty.
|
||||
return self.tree[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Remove (and return) the root element for the heap using the default comparator.
|
||||
*
|
||||
* NOTE: All inserting and removal from a heap should always be done using the same comparator. Mixing comparator
|
||||
* during the lifecycle of a heap will result in undefined behavior.
|
||||
*/
|
||||
function pop(Uint256Heap storage self) internal returns (uint256) {
|
||||
return pop(self, Comparators.lt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Remove (and return) the root element for the heap using the provided comparator.
|
||||
*
|
||||
* NOTE: All inserting and removal from a heap should always be done using the same comparator. Mixing comparator
|
||||
* during the lifecycle of a heap will result in undefined behavior.
|
||||
*/
|
||||
function pop(
|
||||
Uint256Heap storage self,
|
||||
function(uint256, uint256) view returns (bool) comp
|
||||
) internal returns (uint256) {
|
||||
unchecked {
|
||||
uint256 size = length(self);
|
||||
if (size == 0) Panic.panic(Panic.EMPTY_ARRAY_POP);
|
||||
|
||||
// cache
|
||||
uint256 rootValue = self.tree.unsafeAccess(0).value;
|
||||
uint256 lastValue = self.tree.unsafeAccess(size - 1).value;
|
||||
|
||||
// swap last leaf with root, shrink tree and re-heapify
|
||||
self.tree.pop();
|
||||
self.tree.unsafeAccess(0).value = lastValue;
|
||||
_siftDown(self, size - 1, 0, lastValue, comp);
|
||||
|
||||
return rootValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Insert a new element in the heap using the default comparator.
|
||||
*
|
||||
* NOTE: All inserting and removal from a heap should always be done using the same comparator. Mixing comparator
|
||||
* during the lifecycle of a heap will result in undefined behavior.
|
||||
*/
|
||||
function insert(Uint256Heap storage self, uint256 value) internal {
|
||||
insert(self, value, Comparators.lt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Insert a new element in the heap using the provided comparator.
|
||||
*
|
||||
* NOTE: All inserting and removal from a heap should always be done using the same comparator. Mixing comparator
|
||||
* during the lifecycle of a heap will result in undefined behavior.
|
||||
*/
|
||||
function insert(
|
||||
Uint256Heap storage self,
|
||||
uint256 value,
|
||||
function(uint256, uint256) view returns (bool) comp
|
||||
) internal {
|
||||
uint256 size = length(self);
|
||||
|
||||
// push new item and re-heapify
|
||||
self.tree.push(value);
|
||||
_siftUp(self, size, value, comp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Return the root element for the heap, and replace it with a new value, using the default comparator.
|
||||
* This is equivalent to using {pop} and {insert}, but requires only one rebalancing operation.
|
||||
*
|
||||
* NOTE: All inserting and removal from a heap should always be done using the same comparator. Mixing comparator
|
||||
* during the lifecycle of a heap will result in undefined behavior.
|
||||
*/
|
||||
function replace(Uint256Heap storage self, uint256 newValue) internal returns (uint256) {
|
||||
return replace(self, newValue, Comparators.lt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Return the root element for the heap, and replace it with a new value, using the provided comparator.
|
||||
* This is equivalent to using {pop} and {insert}, but requires only one rebalancing operation.
|
||||
*
|
||||
* NOTE: All inserting and removal from a heap should always be done using the same comparator. Mixing comparator
|
||||
* during the lifecycle of a heap will result in undefined behavior.
|
||||
*/
|
||||
function replace(
|
||||
Uint256Heap storage self,
|
||||
uint256 newValue,
|
||||
function(uint256, uint256) view returns (bool) comp
|
||||
) internal returns (uint256) {
|
||||
uint256 size = length(self);
|
||||
if (size == 0) Panic.panic(Panic.EMPTY_ARRAY_POP);
|
||||
|
||||
// cache
|
||||
uint256 oldValue = self.tree.unsafeAccess(0).value;
|
||||
|
||||
// replace and re-heapify
|
||||
self.tree.unsafeAccess(0).value = newValue;
|
||||
_siftDown(self, size, 0, newValue, comp);
|
||||
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the number of elements in the heap.
|
||||
*/
|
||||
function length(Uint256Heap storage self) internal view returns (uint256) {
|
||||
return self.tree.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Removes all elements in the heap.
|
||||
*/
|
||||
function clear(Uint256Heap storage self) internal {
|
||||
self.tree.unsafeSetLength(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Swap node `i` and `j` in the tree.
|
||||
*/
|
||||
function _swap(Uint256Heap storage self, uint256 i, uint256 j) private {
|
||||
StorageSlot.Uint256Slot storage ni = self.tree.unsafeAccess(i);
|
||||
StorageSlot.Uint256Slot storage nj = self.tree.unsafeAccess(j);
|
||||
(ni.value, nj.value) = (nj.value, ni.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Perform heap maintenance on `self`, starting at `index` (with the `value`), using `comp` as a
|
||||
* comparator, and moving toward the leaves of the underlying tree.
|
||||
*
|
||||
* NOTE: This is a private function that is called in a trusted context with already cached parameters. `size`
|
||||
* and `value` could be extracted from `self` and `index`, but that would require redundant storage read. These
|
||||
* parameters are not verified. It is the caller role to make sure the parameters are correct.
|
||||
*/
|
||||
function _siftDown(
|
||||
Uint256Heap storage self,
|
||||
uint256 size,
|
||||
uint256 index,
|
||||
uint256 value,
|
||||
function(uint256, uint256) view returns (bool) comp
|
||||
) private {
|
||||
unchecked {
|
||||
// Check if there is a risk of overflow when computing the indices of the child nodes. If that is the case,
|
||||
// there cannot be child nodes in the tree, so sifting is done.
|
||||
if (index >= type(uint256).max / 2) return;
|
||||
|
||||
// Compute the indices of the potential child nodes
|
||||
uint256 lIndex = 2 * index + 1;
|
||||
uint256 rIndex = 2 * index + 2;
|
||||
|
||||
// Three cases:
|
||||
// 1. Both children exist: sifting may continue on one of the branch (selection required)
|
||||
// 2. Only left child exist: sifting may continue on the left branch (no selection required)
|
||||
// 3. Neither child exist: sifting is done
|
||||
if (rIndex < size) {
|
||||
uint256 lValue = self.tree.unsafeAccess(lIndex).value;
|
||||
uint256 rValue = self.tree.unsafeAccess(rIndex).value;
|
||||
if (comp(lValue, value) || comp(rValue, value)) {
|
||||
uint256 cIndex = comp(lValue, rValue).ternary(lIndex, rIndex);
|
||||
_swap(self, index, cIndex);
|
||||
_siftDown(self, size, cIndex, value, comp);
|
||||
}
|
||||
} else if (lIndex < size) {
|
||||
uint256 lValue = self.tree.unsafeAccess(lIndex).value;
|
||||
if (comp(lValue, value)) {
|
||||
_swap(self, index, lIndex);
|
||||
_siftDown(self, size, lIndex, value, comp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Perform heap maintenance on `self`, starting at `index` (with the `value`), using `comp` as a
|
||||
* comparator, and moving toward the root of the underlying tree.
|
||||
*
|
||||
* NOTE: This is a private function that is called in a trusted context with already cached parameters. `value`
|
||||
* could be extracted from `self` and `index`, but that would require redundant storage read. These parameters are not
|
||||
* verified. It is the caller role to make sure the parameters are correct.
|
||||
*/
|
||||
function _siftUp(
|
||||
Uint256Heap storage self,
|
||||
uint256 index,
|
||||
uint256 value,
|
||||
function(uint256, uint256) view returns (bool) comp
|
||||
) private {
|
||||
unchecked {
|
||||
while (index > 0) {
|
||||
uint256 parentIndex = (index - 1) / 2;
|
||||
uint256 parentValue = self.tree.unsafeAccess(parentIndex).value;
|
||||
if (comp(parentValue, value)) break;
|
||||
_swap(self, index, parentIndex);
|
||||
index = parentIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
267
dev/env/node_modules/@openzeppelin/contracts/utils/structs/MerkleTree.sol
generated
vendored
Executable file
267
dev/env/node_modules/@openzeppelin/contracts/utils/structs/MerkleTree.sol
generated
vendored
Executable file
@@ -0,0 +1,267 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v5.3.0) (utils/structs/MerkleTree.sol)
|
||||
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import {Hashes} from "../cryptography/Hashes.sol";
|
||||
import {Arrays} from "../Arrays.sol";
|
||||
import {Panic} from "../Panic.sol";
|
||||
import {StorageSlot} from "../StorageSlot.sol";
|
||||
|
||||
/**
|
||||
* @dev Library for managing https://wikipedia.org/wiki/Merkle_Tree[Merkle Tree] data structures.
|
||||
*
|
||||
* Each tree is a complete binary tree with the ability to sequentially insert leaves, changing them from a zero to a
|
||||
* non-zero value and updating its root. This structure allows inserting commitments (or other entries) that are not
|
||||
* stored, but can be proven to be part of the tree at a later time if the root is kept. See {MerkleProof}.
|
||||
*
|
||||
* A tree is defined by the following parameters:
|
||||
*
|
||||
* * Depth: The number of levels in the tree, it also defines the maximum number of leaves as 2**depth.
|
||||
* * Zero value: The value that represents an empty leaf. Used to avoid regular zero values to be part of the tree.
|
||||
* * Hashing function: A cryptographic hash function used to produce internal nodes. Defaults to {Hashes-commutativeKeccak256}.
|
||||
*
|
||||
* NOTE: Building trees using non-commutative hashing functions (i.e. `H(a, b) != H(b, a)`) is supported. However,
|
||||
* proving the inclusion of a leaf in such trees is not possible with the {MerkleProof} library since it only supports
|
||||
* _commutative_ hashing functions.
|
||||
*
|
||||
* _Available since v5.1._
|
||||
*/
|
||||
library MerkleTree {
|
||||
/// @dev Error emitted when trying to update a leaf that was not previously pushed.
|
||||
error MerkleTreeUpdateInvalidIndex(uint256 index, uint256 length);
|
||||
|
||||
/// @dev Error emitted when the proof used during an update is invalid (could not reproduce the side).
|
||||
error MerkleTreeUpdateInvalidProof();
|
||||
|
||||
/**
|
||||
* @dev A complete `bytes32` Merkle tree.
|
||||
*
|
||||
* The `sides` and `zero` arrays are set to have a length equal to the depth of the tree during setup.
|
||||
*
|
||||
* Struct members have an underscore prefix indicating that they are "private" and should not be read or written to
|
||||
* directly. Use the functions provided below instead. Modifying the struct manually may violate assumptions and
|
||||
* lead to unexpected behavior.
|
||||
*
|
||||
* NOTE: The `root` and the updates history is not stored within the tree. Consider using a secondary structure to
|
||||
* store a list of historical roots from the values returned from {setup} and {push} (e.g. a mapping, {BitMaps} or
|
||||
* {Checkpoints}).
|
||||
*
|
||||
* WARNING: Updating any of the tree's parameters after the first insertion will result in a corrupted tree.
|
||||
*/
|
||||
struct Bytes32PushTree {
|
||||
uint256 _nextLeafIndex;
|
||||
bytes32[] _sides;
|
||||
bytes32[] _zeros;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Initialize a {Bytes32PushTree} using {Hashes-commutativeKeccak256} to hash internal nodes.
|
||||
* The capacity of the tree (i.e. number of leaves) is set to `2**treeDepth`.
|
||||
*
|
||||
* Calling this function on MerkleTree that was already setup and used will reset it to a blank state.
|
||||
*
|
||||
* Once a tree is setup, any push to it must use the same hashing function. This means that values
|
||||
* should be pushed to it using the default {xref-MerkleTree-push-struct-MerkleTree-Bytes32PushTree-bytes32-}[push] function.
|
||||
*
|
||||
* IMPORTANT: The zero value should be carefully chosen since it will be stored in the tree representing
|
||||
* empty leaves. It should be a value that is not expected to be part of the tree.
|
||||
*/
|
||||
function setup(Bytes32PushTree storage self, uint8 treeDepth, bytes32 zero) internal returns (bytes32 initialRoot) {
|
||||
return setup(self, treeDepth, zero, Hashes.commutativeKeccak256);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Same as {xref-MerkleTree-setup-struct-MerkleTree-Bytes32PushTree-uint8-bytes32-}[setup], but allows to specify a custom hashing function.
|
||||
*
|
||||
* Once a tree is setup, any push to it must use the same hashing function. This means that values
|
||||
* should be pushed to it using the custom push function, which should be the same one as used during the setup.
|
||||
*
|
||||
* IMPORTANT: Providing a custom hashing function is a security-sensitive operation since it may
|
||||
* compromise the soundness of the tree.
|
||||
*
|
||||
* NOTE: Consider verifying that the hashing function does not manipulate the memory state directly and that it
|
||||
* follows the Solidity memory safety rules. Otherwise, it may lead to unexpected behavior.
|
||||
*/
|
||||
function setup(
|
||||
Bytes32PushTree storage self,
|
||||
uint8 treeDepth,
|
||||
bytes32 zero,
|
||||
function(bytes32, bytes32) view returns (bytes32) fnHash
|
||||
) internal returns (bytes32 initialRoot) {
|
||||
// Store depth in the dynamic array
|
||||
Arrays.unsafeSetLength(self._sides, treeDepth);
|
||||
Arrays.unsafeSetLength(self._zeros, treeDepth);
|
||||
|
||||
// Build each root of zero-filled subtrees
|
||||
bytes32 currentZero = zero;
|
||||
for (uint256 i = 0; i < treeDepth; ++i) {
|
||||
Arrays.unsafeAccess(self._zeros, i).value = currentZero;
|
||||
currentZero = fnHash(currentZero, currentZero);
|
||||
}
|
||||
|
||||
// Set the first root
|
||||
self._nextLeafIndex = 0;
|
||||
|
||||
return currentZero;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Insert a new leaf in the tree, and compute the new root. Returns the position of the inserted leaf in the
|
||||
* tree, and the resulting root.
|
||||
*
|
||||
* Hashing the leaf before calling this function is recommended as a protection against
|
||||
* second pre-image attacks.
|
||||
*
|
||||
* This variant uses {Hashes-commutativeKeccak256} to hash internal nodes. It should only be used on merkle trees
|
||||
* that were setup using the same (default) hashing function (i.e. by calling
|
||||
* {xref-MerkleTree-setup-struct-MerkleTree-Bytes32PushTree-uint8-bytes32-}[the default setup] function).
|
||||
*/
|
||||
function push(Bytes32PushTree storage self, bytes32 leaf) internal returns (uint256 index, bytes32 newRoot) {
|
||||
return push(self, leaf, Hashes.commutativeKeccak256);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Insert a new leaf in the tree, and compute the new root. Returns the position of the inserted leaf in the
|
||||
* tree, and the resulting root.
|
||||
*
|
||||
* Hashing the leaf before calling this function is recommended as a protection against
|
||||
* second pre-image attacks.
|
||||
*
|
||||
* This variant uses a custom hashing function to hash internal nodes. It should only be called with the same
|
||||
* function as the one used during the initial setup of the merkle tree.
|
||||
*/
|
||||
function push(
|
||||
Bytes32PushTree storage self,
|
||||
bytes32 leaf,
|
||||
function(bytes32, bytes32) view returns (bytes32) fnHash
|
||||
) internal returns (uint256 index, bytes32 newRoot) {
|
||||
// Cache read
|
||||
uint256 treeDepth = depth(self);
|
||||
|
||||
// Get leaf index
|
||||
index = self._nextLeafIndex++;
|
||||
|
||||
// Check if tree is full.
|
||||
if (index >= 1 << treeDepth) {
|
||||
Panic.panic(Panic.RESOURCE_ERROR);
|
||||
}
|
||||
|
||||
// Rebuild branch from leaf to root
|
||||
uint256 currentIndex = index;
|
||||
bytes32 currentLevelHash = leaf;
|
||||
for (uint256 i = 0; i < treeDepth; i++) {
|
||||
// Reaching the parent node, is currentLevelHash the left child?
|
||||
bool isLeft = currentIndex % 2 == 0;
|
||||
|
||||
// If so, next time we will come from the right, so we need to save it
|
||||
if (isLeft) {
|
||||
Arrays.unsafeAccess(self._sides, i).value = currentLevelHash;
|
||||
}
|
||||
|
||||
// Compute the current node hash by using the hash function
|
||||
// with either its sibling (side) or the zero value for that level.
|
||||
currentLevelHash = fnHash(
|
||||
isLeft ? currentLevelHash : Arrays.unsafeAccess(self._sides, i).value,
|
||||
isLeft ? Arrays.unsafeAccess(self._zeros, i).value : currentLevelHash
|
||||
);
|
||||
|
||||
// Update node index
|
||||
currentIndex >>= 1;
|
||||
}
|
||||
|
||||
return (index, currentLevelHash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Change the value of the leaf at position `index` from `oldValue` to `newValue`. Returns the recomputed "old"
|
||||
* root (before the update) and "new" root (after the update). The caller must verify that the reconstructed old
|
||||
* root is the last known one.
|
||||
*
|
||||
* The `proof` must be an up-to-date inclusion proof for the leaf being updated. This means that this function is
|
||||
* vulnerable to front-running. Any {push} or {update} operation (that changes the root of the tree) would render
|
||||
* all "in flight" updates invalid.
|
||||
*
|
||||
* This variant uses {Hashes-commutativeKeccak256} to hash internal nodes. It should only be used on merkle trees
|
||||
* that were setup using the same (default) hashing function (i.e. by calling
|
||||
* {xref-MerkleTree-setup-struct-MerkleTree-Bytes32PushTree-uint8-bytes32-}[the default setup] function).
|
||||
*/
|
||||
function update(
|
||||
Bytes32PushTree storage self,
|
||||
uint256 index,
|
||||
bytes32 oldValue,
|
||||
bytes32 newValue,
|
||||
bytes32[] memory proof
|
||||
) internal returns (bytes32 oldRoot, bytes32 newRoot) {
|
||||
return update(self, index, oldValue, newValue, proof, Hashes.commutativeKeccak256);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Change the value of the leaf at position `index` from `oldValue` to `newValue`. Returns the recomputed "old"
|
||||
* root (before the update) and "new" root (after the update). The caller must verify that the reconstructed old
|
||||
* root is the last known one.
|
||||
*
|
||||
* The `proof` must be an up-to-date inclusion proof for the leaf being update. This means that this function is
|
||||
* vulnerable to front-running. Any {push} or {update} operation (that changes the root of the tree) would render
|
||||
* all "in flight" updates invalid.
|
||||
*
|
||||
* This variant uses a custom hashing function to hash internal nodes. It should only be called with the same
|
||||
* function as the one used during the initial setup of the merkle tree.
|
||||
*/
|
||||
function update(
|
||||
Bytes32PushTree storage self,
|
||||
uint256 index,
|
||||
bytes32 oldValue,
|
||||
bytes32 newValue,
|
||||
bytes32[] memory proof,
|
||||
function(bytes32, bytes32) view returns (bytes32) fnHash
|
||||
) internal returns (bytes32 oldRoot, bytes32 newRoot) {
|
||||
unchecked {
|
||||
// Check index range
|
||||
uint256 length = self._nextLeafIndex;
|
||||
if (index >= length) revert MerkleTreeUpdateInvalidIndex(index, length);
|
||||
|
||||
// Cache read
|
||||
uint256 treeDepth = depth(self);
|
||||
|
||||
// Workaround stack too deep
|
||||
bytes32[] storage sides = self._sides;
|
||||
|
||||
// This cannot overflow because: 0 <= index < length
|
||||
uint256 lastIndex = length - 1;
|
||||
uint256 currentIndex = index;
|
||||
bytes32 currentLevelHashOld = oldValue;
|
||||
bytes32 currentLevelHashNew = newValue;
|
||||
for (uint32 i = 0; i < treeDepth; i++) {
|
||||
bool isLeft = currentIndex % 2 == 0;
|
||||
|
||||
lastIndex >>= 1;
|
||||
currentIndex >>= 1;
|
||||
|
||||
if (isLeft && currentIndex == lastIndex) {
|
||||
StorageSlot.Bytes32Slot storage side = Arrays.unsafeAccess(sides, i);
|
||||
if (side.value != currentLevelHashOld) revert MerkleTreeUpdateInvalidProof();
|
||||
side.value = currentLevelHashNew;
|
||||
}
|
||||
|
||||
bytes32 sibling = proof[i];
|
||||
currentLevelHashOld = fnHash(
|
||||
isLeft ? currentLevelHashOld : sibling,
|
||||
isLeft ? sibling : currentLevelHashOld
|
||||
);
|
||||
currentLevelHashNew = fnHash(
|
||||
isLeft ? currentLevelHashNew : sibling,
|
||||
isLeft ? sibling : currentLevelHashNew
|
||||
);
|
||||
}
|
||||
return (currentLevelHashOld, currentLevelHashNew);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Tree's depth (set at initialization)
|
||||
*/
|
||||
function depth(Bytes32PushTree storage self) internal view returns (uint256) {
|
||||
return self._zeros.length;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user