diff --git a/src/Gatekeeper.sol b/src/Gatekeeper.sol index 57cc807..e9f264e 100644 --- a/src/Gatekeeper.sol +++ b/src/Gatekeeper.sol @@ -1,36 +1,20 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -import {Hashes} from "./libraries/Hashes.sol"; import {IGatekeeper} from "./interfaces/IGatekeeper.sol"; -import {IGatekeeperWeaver} from "./interfaces/IGatekeeperWeaver.sol"; - -import {IGatekeeperMetadata} from "./interfaces/IGatekeeperMetadata.sol"; -import {Checkpoints} from "./libraries/Checkpoints.sol"; - -contract Gatekeeper is IGatekeeper, IGatekeeperMetadata, IGatekeeperWeaver { - using Checkpoints for Checkpoints.Trace256; - using Checkpoints for Checkpoints.Trace160; +import {Metadata} from "./types/Metadata.sol"; +import {Weaver} from "./types/Weaver.sol"; +contract Gatekeeper is IGatekeeper, Metadata, Weaver { uint256 public constant DIVISOR = 1e6; - uint256 public constant DEPTH = 8; - uint256 public constant SLOTS = 2 ** DEPTH; - uint256 public constant ENTRIES = DEPTH * SLOTS; uint256 public override existentialDeposit; uint256 public override ghostedSupply; - uint256 public currentSession; address public immutable staking; // forge-lint: disable-line(screaming-snake-case-immutable) - GatekeeperMetadata private _metadata; uint256 private _aggregatedPublicKey; mapping(uint256 => mapping(uint256 => bool)) private _executedTransaction; - mapping(uint256 => mapping(uint256 => Checkpoints.Trace256)) internal _treeNodes; - mapping(uint256 => mapping(uint256 => Checkpoints.Trace160)) internal _slotLenghts; - mapping(uint256 => mapping(uint256 => bytes32[])) internal _slotValues; - mapping(uint256 => uint256) internal _filledEntries; - constructor( address _staking, uint256 _ghostedSupply, @@ -38,109 +22,10 @@ contract Gatekeeper is IGatekeeper, IGatekeeperMetadata, IGatekeeperWeaver { uint48 _deployedAt, uint104 _amountIn, uint104 _amountOut - ) { + ) Metadata(_deployedAt, _amountIn, _amountOut) Weaver() { existentialDeposit = _existential; ghostedSupply = _ghostedSupply; staking = _staking; - _metadata = GatekeeperMetadata({ - deployedAt: _deployedAt, - amountIn: _amountIn, - amountOut: _amountOut - }); - } - - function metadata() external view returns (GatekeeperMetadata memory) { - return _metadata; - } - - function getSlotValues( - uint256 globalIndex, - uint256 session, - uint256 atBlock - ) external view returns (bytes32[] memory) { - uint256 slotIndex = globalIndex % SLOTS; - // forge-lint: disable-next-line(unsafe-typecast) - uint256 length = _slotLenghts[session][slotIndex].upperLookupRecent(uint96(atBlock)); - bytes32[] memory values = new bytes32[](length); - - uint256 i; - for (; i < length; ) { - values[i] = _slotValues[session][slotIndex][i]; - unchecked { ++i; } - } - - return values; - } - - function getRoot(uint256 session, uint256 atBlock) public view returns (bytes32, uint256) { - uint256 currentLevelCount = SLOTS >> 1; - bytes32[] memory currentLevel = new bytes32[](currentLevelCount); - - uint256 i; - for (; i < currentLevelCount;) { - uint256 j = i << 1; - bytes32 left = bytes32(_treeNodes[session][j].upperLookup(atBlock)); - bytes32 right = bytes32(_treeNodes[session][j | 1].upperLookup(atBlock)); - - currentLevel[i] = Hashes.efficientKeccak256(left, right); - unchecked { ++i; } - } - - while (currentLevelCount > 1) { - currentLevelCount >>= 1; - i = 0; - for (; i < currentLevelCount;) { - uint256 j = i << 1; - bytes32 left = currentLevel[j]; - bytes32 right = currentLevel[j | 1]; - - currentLevel[i] = Hashes.efficientKeccak256(left, right); - unchecked { ++i; } - } - } - - return (currentLevel[0], _filledEntries[session]); - } - - function getProof( - uint256 globalIndex, - uint256 session, - uint256 atBlock - ) external view returns (bytes32[] memory) { - uint256 currentLevelCount = SLOTS; - uint256 currentIndex = globalIndex % SLOTS; - - bytes32[] memory proof = new bytes32[](DEPTH); - bytes32[] memory currentLevel = new bytes32[](currentLevelCount); - uint256 i; - - for (; i < currentLevelCount;) { - currentLevel[i] = bytes32(_treeNodes[session][i].upperLookup(atBlock)); - unchecked { ++i; } - } - - uint256 proofIndex; - while (currentLevelCount > 1) { - currentLevelCount >>= 1; - uint256 siblingIndex = currentIndex ^ 1; - - proof[proofIndex] = currentLevel[siblingIndex]; - i = 0; - - for (; i < currentLevelCount;) { - uint256 j = i << 1; - bytes32 left = currentLevel[j]; - bytes32 right = currentLevel[j | 1]; - - currentLevel[i] = Hashes.efficientKeccak256(left, right); - unchecked { ++i; } - } - - currentIndex >>= 1; - unchecked { ++proofIndex; } - } - - return proof; } function ghost(bytes32 receiver, uint256 amount) external override returns (uint256) { @@ -216,34 +101,4 @@ contract Gatekeeper is IGatekeeper, IGatekeeperMetadata, IGatekeeperWeaver { // always bad signature for now return true; } - - function _insertTreeNode(bytes32 who, uint256 amount) private returns (uint256 globalIndex) { - uint256 session = currentSession; - globalIndex = _filledEntries[session]; - if (globalIndex >= ENTRIES) { - unchecked { ++session; } - currentSession = session; - globalIndex = 0; - } - - uint256 slotIndex = globalIndex % SLOTS; - uint256 valueIndex = globalIndex / SLOTS; - uint160 length = uint160(_slotValues[session][slotIndex].length + 1); - - bytes32 preimage = _computeArgumentsHash(valueIndex, amount, who); - bytes32 currHash = Hashes.efficientKeccak256(preimage); - bytes32 slotHash = bytes32(_treeNodes[session][slotIndex].latest()); - - slotHash = Hashes.efficientKeccak256(slotHash, currHash); - - _treeNodes[session][slotIndex].push(block.number, uint256(slotHash)); - _slotLenghts[session][slotIndex].push(uint96(block.number), length); - _slotValues[session][slotIndex].push(preimage); - - unchecked { ++_filledEntries[session]; } - } - - function _computeArgumentsHash(uint256 i, uint256 a, bytes32 r) internal pure returns (bytes32) { - return Hashes.efficientKeccak256(bytes32(i), Hashes.efficientKeccak256(bytes32(a), r)); - } } diff --git a/src/interfaces/IGatekeeperMetadata.sol b/src/interfaces/IMetadata.sol similarity index 88% rename from src/interfaces/IGatekeeperMetadata.sol rename to src/interfaces/IMetadata.sol index f277da2..4428c63 100644 --- a/src/interfaces/IGatekeeperMetadata.sol +++ b/src/interfaces/IMetadata.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -interface IGatekeeperMetadata { +interface IMetadata { struct GatekeeperMetadata { uint48 deployedAt; uint104 amountIn; diff --git a/src/interfaces/IGatekeeperWeaver.sol b/src/interfaces/IWeaver.sol similarity index 92% rename from src/interfaces/IGatekeeperWeaver.sol rename to src/interfaces/IWeaver.sol index b077e11..b8f3600 100644 --- a/src/interfaces/IGatekeeperWeaver.sol +++ b/src/interfaces/IWeaver.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -interface IGatekeeperWeaver { +interface IWeaver { function getSlotValues(uint256 globalIndex, uint256 session, uint256 atBlock) external returns (bytes32[] memory); function getProof(uint256 globalIndex, uint256 session, uint256 atBlock) external returns (bytes32[] memory); function getRoot(uint256 session, uint256 atBlock) external returns (bytes32, uint256); diff --git a/src/types/Metadata.sol b/src/types/Metadata.sol new file mode 100644 index 0000000..6c51de7 --- /dev/null +++ b/src/types/Metadata.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {IMetadata} from "../interfaces/IMetadata.sol"; + +abstract contract Metadata is IMetadata { + GatekeeperMetadata internal _metadata; + + constructor( + uint48 _deployedAt, + uint104 _amountIn, + uint104 _amountOut + ) { + _metadata = GatekeeperMetadata({ + deployedAt: _deployedAt, + amountIn: _amountIn, + amountOut: _amountOut + }); + } + + function metadata() external view returns (GatekeeperMetadata memory) { + return _metadata; + } +} diff --git a/src/types/Weaver.sol b/src/types/Weaver.sol new file mode 100644 index 0000000..8dfe6e4 --- /dev/null +++ b/src/types/Weaver.sol @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {IWeaver} from "../interfaces/IWeaver.sol"; + +import {Hashes} from "../libraries/Hashes.sol"; +import {Checkpoints} from "../libraries/Checkpoints.sol"; + +abstract contract Weaver is IWeaver { + using Checkpoints for Checkpoints.Trace256; + using Checkpoints for Checkpoints.Trace160; + + uint256 public constant DEPTH = 8; + uint256 public constant SLOTS = 2 ** DEPTH; + uint256 public constant ENTRIES = DEPTH * SLOTS; + + uint256 public currentSession; + + mapping(uint256 => mapping(uint256 => Checkpoints.Trace256)) internal _treeNodes; + mapping(uint256 => mapping(uint256 => Checkpoints.Trace160)) internal _slotLenghts; + mapping(uint256 => mapping(uint256 => bytes32[])) internal _slotValues; + mapping(uint256 => uint256) internal _filledEntries; + + constructor() {} + + function getSlotValues( + uint256 globalIndex, + uint256 session, + uint256 atBlock + ) external view returns (bytes32[] memory) { + uint256 slotIndex = globalIndex % SLOTS; + // forge-lint: disable-next-line(unsafe-typecast) + uint256 length = _slotLenghts[session][slotIndex].upperLookupRecent(uint96(atBlock)); + bytes32[] memory values = new bytes32[](length); + + uint256 i; + for (; i < length; ) { + values[i] = _slotValues[session][slotIndex][i]; + unchecked { ++i; } + } + + return values; + } + + function getRoot(uint256 session, uint256 atBlock) public view returns (bytes32, uint256) { + uint256 currentLevelCount = SLOTS >> 1; + bytes32[] memory currentLevel = new bytes32[](currentLevelCount); + + uint256 i; + for (; i < currentLevelCount;) { + uint256 j = i << 1; + bytes32 left = bytes32(_treeNodes[session][j].upperLookup(atBlock)); + bytes32 right = bytes32(_treeNodes[session][j | 1].upperLookup(atBlock)); + + currentLevel[i] = Hashes.efficientKeccak256(left, right); + unchecked { ++i; } + } + + while (currentLevelCount > 1) { + currentLevelCount >>= 1; + i = 0; + for (; i < currentLevelCount;) { + uint256 j = i << 1; + bytes32 left = currentLevel[j]; + bytes32 right = currentLevel[j | 1]; + + currentLevel[i] = Hashes.efficientKeccak256(left, right); + unchecked { ++i; } + } + } + + return (currentLevel[0], _filledEntries[session]); + } + + function getProof( + uint256 globalIndex, + uint256 session, + uint256 atBlock + ) external view returns (bytes32[] memory) { + uint256 currentLevelCount = SLOTS; + uint256 currentIndex = globalIndex % SLOTS; + + bytes32[] memory proof = new bytes32[](DEPTH); + bytes32[] memory currentLevel = new bytes32[](currentLevelCount); + uint256 i; + + for (; i < currentLevelCount;) { + currentLevel[i] = bytes32(_treeNodes[session][i].upperLookup(atBlock)); + unchecked { ++i; } + } + + uint256 proofIndex; + while (currentLevelCount > 1) { + currentLevelCount >>= 1; + uint256 siblingIndex = currentIndex ^ 1; + + proof[proofIndex] = currentLevel[siblingIndex]; + i = 0; + + for (; i < currentLevelCount;) { + uint256 j = i << 1; + bytes32 left = currentLevel[j]; + bytes32 right = currentLevel[j | 1]; + + currentLevel[i] = Hashes.efficientKeccak256(left, right); + unchecked { ++i; } + } + + currentIndex >>= 1; + unchecked { ++proofIndex; } + } + + return proof; + } + + function _insertTreeNode(bytes32 who, uint256 amount) internal returns (uint256 globalIndex) { + uint256 session = currentSession; + globalIndex = _filledEntries[session]; + if (globalIndex >= ENTRIES) { + unchecked { ++session; } + currentSession = session; + globalIndex = 0; + } + + uint256 slotIndex = globalIndex % SLOTS; + uint256 valueIndex = globalIndex / SLOTS; + uint160 length = uint160(_slotValues[session][slotIndex].length + 1); + + bytes32 preimage = _computeArgumentsHash(valueIndex, amount, who); + bytes32 currHash = Hashes.efficientKeccak256(preimage); + bytes32 slotHash = bytes32(_treeNodes[session][slotIndex].latest()); + + slotHash = Hashes.efficientKeccak256(slotHash, currHash); + + _treeNodes[session][slotIndex].push(block.number, uint256(slotHash)); + _slotLenghts[session][slotIndex].push(uint96(block.number), length); + _slotValues[session][slotIndex].push(preimage); + + unchecked { ++_filledEntries[session]; } + } + + function _computeArgumentsHash(uint256 i, uint256 a, bytes32 r) internal pure returns (bytes32) { + return Hashes.efficientKeccak256(bytes32(i), Hashes.efficientKeccak256(bytes32(a), r)); + } +}