Compare commits
No commits in common. "0d043ea964e7cc377cbb3a05396c02871bb1b665" and "379cc331e253a4db3370d342f426da0866aa7d31" have entirely different histories.
0d043ea964
...
379cc331e2
@ -1,237 +1,106 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
pragma solidity ^0.8.20;
|
pragma solidity ^0.8.20;
|
||||||
|
|
||||||
import {IStaking} from "./interfaces/IStaking.sol";
|
|
||||||
import {IGatekeeper} from "./interfaces/IGatekeeper.sol";
|
import {IGatekeeper} from "./interfaces/IGatekeeper.sol";
|
||||||
import {IStorageHistory} from "./interfaces/IStorageHistory.sol";
|
import {Metadata} from "./types/Metadata.sol";
|
||||||
|
|
||||||
import {StorageHistory} from "./types/StorageHistory.sol";
|
|
||||||
import {Weaver} from "./types/Weaver.sol";
|
import {Weaver} from "./types/Weaver.sol";
|
||||||
|
|
||||||
import {FullMath} from "./libraries/FullMath.sol";
|
contract Gatekeeper is IGatekeeper, Metadata, Weaver {
|
||||||
import {Checkpoints} from "./libraries/Checkpoints.sol";
|
uint256 public constant DIVISOR = 1e6;
|
||||||
import {ReentrancyGuard} from "@openzeppelin-contracts/utils/ReentrancyGuard.sol";
|
|
||||||
|
|
||||||
contract Gatekeeper is IGatekeeper, Weaver, ReentrancyGuard {
|
|
||||||
using Checkpoints for Checkpoints.Trace256;
|
|
||||||
|
|
||||||
uint256 private constant COMMISSION_DIVISOR = 2**32;
|
|
||||||
uint256 private constant SECP256K1_N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141;
|
|
||||||
uint256 private constant SECP256K1_Q = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141;
|
|
||||||
uint256 private constant SHIFT_FACTOR = (2**128) % SECP256K1_N;
|
|
||||||
|
|
||||||
address public override staking;
|
|
||||||
address public override storageHistory;
|
|
||||||
uint256 public override existentialDeposit;
|
uint256 public override existentialDeposit;
|
||||||
|
uint256 public override ghostedSupply;
|
||||||
|
address public override staking;
|
||||||
|
|
||||||
address public deployer;
|
uint256 private _aggregatedPublicKey;
|
||||||
address private _previousAddress;
|
mapping(uint256 => mapping(uint256 => bool)) private _executedTransaction;
|
||||||
|
|
||||||
Checkpoints.Trace256 private _aggregatedPublicKeys;
|
|
||||||
mapping(bytes32 => RotationState) private _publicKeyMetadatas;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
address _staking,
|
||||||
uint256 _existentialDeposit,
|
uint256 _existentialDeposit,
|
||||||
address _previousGatekeeperAddress
|
address _previousGatekeeper
|
||||||
) Weaver(_previousGatekeeperAddress) {
|
) Metadata(_previousGatekeeper) Weaver(_previousGatekeeper) {
|
||||||
existentialDeposit = _existentialDeposit;
|
existentialDeposit = _existentialDeposit;
|
||||||
|
if (_previousGatekeeper != address(0)) {
|
||||||
if (_previousGatekeeperAddress != address(0)) {
|
IGatekeeper previousGatekeeper = IGatekeeper(_previousGatekeeper);
|
||||||
require(_previousGatekeeperAddress != address(0));
|
ghostedSupply = previousGatekeeper.ghostedSupply();
|
||||||
require(_previousGatekeeperAddress != address(this));
|
staking = previousGatekeeper.staking();
|
||||||
_previousAddress = _previousGatekeeperAddress;
|
|
||||||
|
|
||||||
storageHistory = IGatekeeper(_previousGatekeeperAddress).storageHistory();
|
|
||||||
staking = IGatekeeper(_previousGatekeeperAddress).staking();
|
|
||||||
} else {
|
} else {
|
||||||
StorageHistory newStorageHistory = new StorageHistory(msg.sender);
|
staking = _staking;
|
||||||
storageHistory = address(newStorageHistory);
|
|
||||||
staking = msg.sender;
|
|
||||||
deployer = tx.origin;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updatePublicKeyMetadata(uint256 exodusSession, bytes32 publicKey, uint8 parity) external {
|
|
||||||
require(msg.sender == deployer);
|
|
||||||
// TODO: uncomment line below, needed only for testing purposes
|
|
||||||
// and should push to hardcoded exodusSession 0 in any case
|
|
||||||
// deployer = address(0);
|
|
||||||
|
|
||||||
RotationState memory rotationState = RotationState({
|
|
||||||
parity: parity,
|
|
||||||
// forge-lint: disable-next-line(unsafe-typecast)
|
|
||||||
session: uint64(exodusSession)
|
|
||||||
});
|
|
||||||
|
|
||||||
_aggregatedPublicKeys.push(exodusSession, uint256(publicKey));
|
|
||||||
_publicKeyMetadatas[publicKey] = rotationState;
|
|
||||||
}
|
|
||||||
|
|
||||||
function previousAddress() external override view returns (address) {
|
|
||||||
return _previousAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
function ghostedSupply() external override view returns (uint256) {
|
|
||||||
return IStorageHistory(storageHistory).bridgeImbalance();
|
|
||||||
}
|
|
||||||
|
|
||||||
function latestPublicKeyInfo() external override view returns (bytes32, uint8, uint64) {
|
|
||||||
bytes32 latestPublicKey = bytes32(_aggregatedPublicKeys.latest());
|
|
||||||
RotationState memory state = _publicKeyMetadatas[latestPublicKey];
|
|
||||||
return (latestPublicKey, state.parity, state.session);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getRotationInfoAt(uint256 exodusSession) public override view returns (bytes32, uint8, uint64) {
|
|
||||||
bytes32 publicKey = bytes32(_aggregatedPublicKeys.upperLookup(exodusSession));
|
|
||||||
RotationState memory rotationState = _publicKeyMetadatas[publicKey];
|
|
||||||
|
|
||||||
if (exodusSession < rotationState.session) {
|
|
||||||
return IGatekeeper(_previousAddress).getRotationInfoAt(exodusSession);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (publicKey, rotationState.parity, rotationState.session);
|
|
||||||
}
|
|
||||||
|
|
||||||
function verify(
|
|
||||||
bytes calldata call,
|
|
||||||
bytes calldata nonce,
|
|
||||||
bytes32 s
|
|
||||||
) external nonReentrant returns (bytes memory) {
|
|
||||||
(bytes32 px, uint8 p) = _extractPublicKey(call);
|
|
||||||
|
|
||||||
uint8 v = p == 0 ? 27 : 28;
|
|
||||||
bytes32 e = _challenge(call, nonce, px, p);
|
|
||||||
|
|
||||||
bytes32 sp = bytes32(SECP256K1_Q - mulmod(uint256(s), uint256(px), SECP256K1_Q));
|
|
||||||
bytes32 ep = bytes32(SECP256K1_Q - mulmod(uint256(e), uint256(px), SECP256K1_Q));
|
|
||||||
address recoveredR = ecrecover(sp, v, px, ep);
|
|
||||||
|
|
||||||
if (sp == 0) revert BadSignature();
|
|
||||||
if (recoveredR == address(0)) revert BadSignature();
|
|
||||||
if (recoveredR != _uncompressedPointToAddress(nonce)) revert BadSignature();
|
|
||||||
|
|
||||||
(bool success, bytes memory data) = address(this).call(call);
|
|
||||||
if (!success) revert ExecutionReverted();
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
function ghost(bytes32 receiver, uint256 amount) external override returns (uint256) {
|
function ghost(bytes32 receiver, uint256 amount) external override returns (uint256) {
|
||||||
if (msg.sender != staking) revert NotStaking();
|
if (msg.sender != staking) revert NotStaking();
|
||||||
if (amount < existentialDeposit) revert NonExistentAmount();
|
if (amount < existentialDeposit) revert NonExistentAmount();
|
||||||
|
|
||||||
IStorageHistory(storageHistory).increaseBridgeIn(amount);
|
ghostedSupply += amount;
|
||||||
|
_metadata.amountIn += uint104(amount); // forge-lint: disable-line(unsafe-typecast)
|
||||||
|
|
||||||
emit Ghosted(receiver, amount);
|
emit Ghosted(receiver, amount);
|
||||||
|
|
||||||
return _insertTreeNode(receiver, amount);
|
return _insertTreeNode(receiver, amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
function materialize(
|
function materialize(
|
||||||
uint256 exodusSession,
|
address receiver,
|
||||||
uint256 amount,
|
uint256 amount,
|
||||||
uint256 commission,
|
uint256 rx,
|
||||||
address receiver
|
uint256 s
|
||||||
) external override {
|
) external override {
|
||||||
if (msg.sender != address(this)) revert NotGatekeeper();
|
if (msg.sender != staking) revert NotStaking();
|
||||||
|
_checkTransactionExistence(rx, s);
|
||||||
|
|
||||||
StorageHistory(storageHistory).trySetTransactionExecuted(exodusSession);
|
bytes4 selector = bytes4(keccak256("materialize(address,uint256)"));
|
||||||
IStorageHistory(storageHistory).tryIncreaseBridgeOut(amount);
|
bytes32 message;
|
||||||
|
assembly {
|
||||||
uint256 commissionAmount = FullMath.mulDiv(amount, commission, COMMISSION_DIVISOR);
|
let ptr := mload(0x40)
|
||||||
uint256 receiverAmount = amount - commissionAmount;
|
mstore(ptr, selector)
|
||||||
IStaking(staking).materialize(receiver, receiverAmount);
|
mstore(add(ptr, 4), receiver)
|
||||||
|
mstore(add(ptr, 36), amount)
|
||||||
if (commissionAmount > 0) {
|
message := keccak256(ptr, 68)
|
||||||
IStaking(staking).materialize(tx.origin, commission);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_incorrectSignature(rx, s, message)) revert WrongSignature();
|
||||||
|
ghostedSupply -= amount;
|
||||||
|
_metadata.amountOut += uint104(amount); // forge-lint: disable-line(unsafe-typecast)
|
||||||
emit Materialized(receiver, amount);
|
emit Materialized(receiver, amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
function rotate(
|
function rotate(
|
||||||
uint256 exodusSession,
|
uint256 aggregatedPublicKey,
|
||||||
bytes32 newPublicKey,
|
uint256 rx,
|
||||||
uint8 newParity
|
uint256 s
|
||||||
) external override {
|
) external override {
|
||||||
if (msg.sender != address(this)) revert NotGatekeeper();
|
_checkTransactionExistence(rx, s);
|
||||||
StorageHistory(storageHistory).trySetTransactionExecuted(exodusSession);
|
|
||||||
|
|
||||||
RotationState memory rotationState = RotationState({
|
bytes4 selector = bytes4(keccak256("rotate(uint256)"));
|
||||||
parity: newParity,
|
bytes32 message;
|
||||||
// forge-lint: disable-next-line(unsafe-typecast)
|
|
||||||
session: uint64(exodusSession)
|
|
||||||
});
|
|
||||||
|
|
||||||
_aggregatedPublicKeys.push(exodusSession, uint256(newPublicKey));
|
|
||||||
_publicKeyMetadatas[newPublicKey] = rotationState;
|
|
||||||
|
|
||||||
emit Rotated(newPublicKey, newParity);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _uncompressedPointToAddress(bytes calldata nonce) internal pure returns (address) {
|
|
||||||
if (nonce.length != 65) revert BadSignature();
|
|
||||||
if (nonce[0] != 0x04) revert BadSignature();
|
|
||||||
|
|
||||||
bytes32 rx = bytes32(nonce[1:33]);
|
|
||||||
bytes32 ry = bytes32(nonce[33:65]);
|
|
||||||
|
|
||||||
return address(uint160(uint256(keccak256(abi.encodePacked(rx, ry)))));
|
|
||||||
}
|
|
||||||
|
|
||||||
function _extractPublicKey(bytes calldata call) internal view returns (bytes32, uint8) {
|
|
||||||
if (call.length < 36) revert InvalidCalldata();
|
|
||||||
uint256 exodusSession;
|
|
||||||
assembly {
|
assembly {
|
||||||
exodusSession := calldataload(add(call.offset, 4))
|
let ptr := mload(0x40)
|
||||||
|
mstore(ptr, selector)
|
||||||
|
mstore(add(ptr, 4), aggregatedPublicKey)
|
||||||
|
message := keccak256(ptr, 36)
|
||||||
}
|
}
|
||||||
|
|
||||||
(bytes32 publicKey, uint8 parity,) = getRotationInfoAt(exodusSession);
|
if (_incorrectSignature(rx, s, message)) revert WrongSignature();
|
||||||
|
_aggregatedPublicKey = aggregatedPublicKey;
|
||||||
|
|
||||||
return (publicKey, parity);
|
emit Rotated(aggregatedPublicKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
function _challenge(
|
function _checkTransactionExistence(uint256 rx, uint256 s) private {
|
||||||
bytes calldata call,
|
if (_executedTransaction[rx][s]) revert AlreadyExecuted();
|
||||||
bytes calldata r,
|
_executedTransaction[rx][s] = true;
|
||||||
bytes32 pk,
|
}
|
||||||
uint8 v
|
|
||||||
) internal pure returns (bytes32) {
|
|
||||||
if (r.length != 65) revert BadSignature();
|
|
||||||
|
|
||||||
bytes32 rx = bytes32(r[1:33]);
|
function _incorrectSignature(uint256 rx, uint256 s, bytes32 m) private pure returns (bool) {
|
||||||
bytes1 rv;
|
// no logic below, just to suppress warnings from solc
|
||||||
|
uint256 void = rx;
|
||||||
|
void = s;
|
||||||
|
void = uint256(m);
|
||||||
|
|
||||||
{
|
// always bad signature for now
|
||||||
bytes32 ry = bytes32(r[33:65]);
|
return true;
|
||||||
uint8 rParityByte = (uint256(ry) & 1) == 0 ? 0x02 : 0x03;
|
|
||||||
rv = bytes1(rParityByte);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8 pv = v == 0 ? 0x02 : 0x03;
|
|
||||||
|
|
||||||
bytes memory message = abi.encodePacked(rv, rx, pv, pk, call);
|
|
||||||
bytes memory domain = "FROST-secp256k1-SHA256-v1chal";
|
|
||||||
uint8 dstlen = uint8(domain.length);
|
|
||||||
|
|
||||||
bytes32 b0 = sha256(abi.encodePacked(
|
|
||||||
bytes32(0),
|
|
||||||
bytes32(0),
|
|
||||||
message,
|
|
||||||
hex"0030",
|
|
||||||
uint8(0),
|
|
||||||
domain,
|
|
||||||
dstlen
|
|
||||||
));
|
|
||||||
|
|
||||||
bytes32 b1 = sha256(abi.encodePacked(b0, uint8(1), domain, dstlen));
|
|
||||||
bytes32 b2 = sha256(abi.encodePacked(b0 ^ b1, uint8(2), domain, dstlen));
|
|
||||||
|
|
||||||
uint256 high32 = uint256(b1);
|
|
||||||
uint256 lowPart = uint256(b2) >> 128;
|
|
||||||
|
|
||||||
uint256 highReduced = mulmod(high32, SHIFT_FACTOR, SECP256K1_N);
|
|
||||||
uint256 finalChallenge = addmod(highReduced, lowPart, SECP256K1_N);
|
|
||||||
|
|
||||||
return bytes32(finalChallenge);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,15 +5,13 @@ import {SafeERC20} from "@openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol
|
|||||||
import {IERC20} from "@openzeppelin-contracts/token/ERC20/IERC20.sol";
|
import {IERC20} from "@openzeppelin-contracts/token/ERC20/IERC20.sol";
|
||||||
|
|
||||||
import {GhostWarmup} from "./Warmup.sol";
|
import {GhostWarmup} from "./Warmup.sol";
|
||||||
import {Gatekeeper} from "./Gatekeeper.sol";
|
|
||||||
import {GhostAccessControlled} from "./types/GhostAccessControlled.sol";
|
import {GhostAccessControlled} from "./types/GhostAccessControlled.sol";
|
||||||
|
|
||||||
import {ISTNK} from "./interfaces/ISTNK.sol";
|
import {ISTNK} from "./interfaces/ISTNK.sol";
|
||||||
import {IGHST} from "./interfaces/IGHST.sol";
|
import {IGHST} from "./interfaces/IGHST.sol";
|
||||||
|
import {IDistributor} from "./interfaces/IDistributor.sol";
|
||||||
import {IStaking} from "./interfaces/IStaking.sol";
|
import {IStaking} from "./interfaces/IStaking.sol";
|
||||||
import {IGatekeeper} from "./interfaces/IGatekeeper.sol";
|
import {IGatekeeper} from "./interfaces/IGatekeeper.sol";
|
||||||
import {IDistributor} from "./interfaces/IDistributor.sol";
|
|
||||||
import {IStorageHistory} from "./interfaces/IStorageHistory.sol";
|
|
||||||
import {IGhostAuthority} from "./interfaces/IGhostAuthority.sol";
|
import {IGhostAuthority} from "./interfaces/IGhostAuthority.sol";
|
||||||
import {IGhostWarmup} from "./interfaces/IGhostWarmup.sol";
|
import {IGhostWarmup} from "./interfaces/IGhostWarmup.sol";
|
||||||
|
|
||||||
@ -44,8 +42,7 @@ contract GhostStaking is IStaking, GhostAccessControlled {
|
|||||||
uint48 _epochLength,
|
uint48 _epochLength,
|
||||||
uint48 _firstEpochNumber,
|
uint48 _firstEpochNumber,
|
||||||
uint48 _firstEpochTime,
|
uint48 _firstEpochTime,
|
||||||
address _authority,
|
address _authority
|
||||||
uint256 _existentialDeposit
|
|
||||||
) GhostAccessControlled(IGhostAuthority(_authority)) {
|
) GhostAccessControlled(IGhostAuthority(_authority)) {
|
||||||
ftso = _ftso;
|
ftso = _ftso;
|
||||||
stnk = _stnk;
|
stnk = _stnk;
|
||||||
@ -60,10 +57,6 @@ contract GhostStaking is IStaking, GhostAccessControlled {
|
|||||||
|
|
||||||
GhostWarmup newWarmup = new GhostWarmup(_ghst);
|
GhostWarmup newWarmup = new GhostWarmup(_ghst);
|
||||||
warmup = address(newWarmup);
|
warmup = address(newWarmup);
|
||||||
|
|
||||||
Gatekeeper newGatekeeper = new Gatekeeper(_existentialDeposit, address(0));
|
|
||||||
gatekeeper = address(newGatekeeper);
|
|
||||||
|
|
||||||
_lastRebaseBlock = 1;
|
_lastRebaseBlock = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,13 +149,21 @@ contract GhostStaking is IStaking, GhostAccessControlled {
|
|||||||
ISTNK(stnk).safeTransfer(to, balance);
|
ISTNK(stnk).safeTransfer(to, balance);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ghost(bytes32 receiver, uint256 amount) external override {
|
function ghost(
|
||||||
|
bytes32 receiver,
|
||||||
|
uint256 amount
|
||||||
|
) external override {
|
||||||
IGHST(ghst).burn(msg.sender, amount);
|
IGHST(ghst).burn(msg.sender, amount);
|
||||||
IGatekeeper(gatekeeper).ghost(receiver, amount);
|
IGatekeeper(gatekeeper).ghost(receiver, amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
function materialize(address receiver, uint256 amount) external override {
|
function materialize(
|
||||||
if (gatekeeper != msg.sender) revert NotGatekeeper();
|
address receiver,
|
||||||
|
uint256 amount,
|
||||||
|
uint256 rx,
|
||||||
|
uint256 s
|
||||||
|
) external override {
|
||||||
|
IGatekeeper(gatekeeper).materialize(receiver, amount, rx, s);
|
||||||
IGHST(ghst).mint(receiver, amount);
|
IGHST(ghst).mint(receiver, amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,13 +200,9 @@ contract GhostStaking is IStaking, GhostAccessControlled {
|
|||||||
emit WarmupSet(_warmupPeriod);
|
emit WarmupSet(_warmupPeriod);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setGatekeeperAddress(uint256 existentialDeposit) external onlyGovernor {
|
function setGatekeeperAddress(address _gatekeeper) external onlyGovernor {
|
||||||
Gatekeeper newGatekeeper = new Gatekeeper(existentialDeposit, gatekeeper);
|
gatekeeper = _gatekeeper;
|
||||||
address storageHistory = IGatekeeper(gatekeeper).storageHistory();
|
emit GatekeeperSet(_gatekeeper);
|
||||||
IStorageHistory(storageHistory).setOwner(address(newGatekeeper));
|
|
||||||
|
|
||||||
gatekeeper = address(newGatekeeper);
|
|
||||||
emit GatekeeperSet(address(newGatekeeper));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function index() public view override returns (uint256) {
|
function index() public view override returns (uint256) {
|
||||||
|
|||||||
@ -3,30 +3,18 @@ pragma solidity ^0.8.20;
|
|||||||
|
|
||||||
interface IGatekeeper {
|
interface IGatekeeper {
|
||||||
error NotStaking();
|
error NotStaking();
|
||||||
error NotGatekeeper();
|
error WrongSignature();
|
||||||
error BadSignature();
|
|
||||||
error AlreadyExecuted();
|
error AlreadyExecuted();
|
||||||
error NonExistentAmount();
|
error NonExistentAmount();
|
||||||
error InvalidCalldata();
|
|
||||||
error ExecutionReverted();
|
|
||||||
|
|
||||||
struct RotationState {
|
|
||||||
uint8 parity;
|
|
||||||
uint64 session;
|
|
||||||
}
|
|
||||||
|
|
||||||
event Ghosted(bytes32 indexed receiver, uint256 indexed amount);
|
event Ghosted(bytes32 indexed receiver, uint256 indexed amount);
|
||||||
event Materialized(address indexed receiver, uint256 indexed amount);
|
event Materialized(address indexed receiver, uint256 indexed amount);
|
||||||
event Rotated(bytes32 indexed aggregatedPublicKey, uint8 indexed parity);
|
event Rotated(uint256 indexed aggregatedPublicKey);
|
||||||
|
|
||||||
function staking() external view returns (address);
|
|
||||||
function storageHistory() external view returns (address);
|
|
||||||
function ghostedSupply() external view returns (uint256);
|
function ghostedSupply() external view returns (uint256);
|
||||||
function existentialDeposit() external view returns (uint256);
|
function existentialDeposit() external view returns (uint256);
|
||||||
function latestPublicKeyInfo() external view returns (bytes32, uint8, uint64);
|
function staking() external view returns (address);
|
||||||
function getRotationInfoAt(uint256 session) external view returns (bytes32, uint8, uint64);
|
|
||||||
|
|
||||||
function materialize(uint256 session, uint256 amount, uint256 commission, address receiver) external;
|
|
||||||
function rotate(uint256 session, bytes32 publicKey, uint8 parity) external;
|
|
||||||
function ghost(bytes32 receiver, uint256 amount) external returns (uint256);
|
function ghost(bytes32 receiver, uint256 amount) external returns (uint256);
|
||||||
|
function materialize(address receiver, uint256 amount, uint256 rx, uint256 s) external;
|
||||||
|
function rotate(uint256 aggregatedPublicKey, uint256 rx, uint256 s) external;
|
||||||
}
|
}
|
||||||
|
|||||||
12
src/interfaces/IMetadata.sol
Normal file
12
src/interfaces/IMetadata.sol
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
pragma solidity ^0.8.20;
|
||||||
|
|
||||||
|
interface IMetadata {
|
||||||
|
struct GatekeeperMetadata {
|
||||||
|
uint48 deployedAt;
|
||||||
|
uint104 amountIn;
|
||||||
|
uint104 amountOut;
|
||||||
|
}
|
||||||
|
|
||||||
|
function metadata() external view returns (GatekeeperMetadata memory);
|
||||||
|
}
|
||||||
@ -5,7 +5,6 @@ interface IStaking {
|
|||||||
error ExternalDepositsLocked();
|
error ExternalDepositsLocked();
|
||||||
error ExternalClaimsLocked();
|
error ExternalClaimsLocked();
|
||||||
error InsufficientBalance();
|
error InsufficientBalance();
|
||||||
error NotGatekeeper();
|
|
||||||
|
|
||||||
event Staked(
|
event Staked(
|
||||||
address sender,
|
address sender,
|
||||||
@ -68,7 +67,7 @@ interface IStaking {
|
|||||||
function wrap(address _to, uint256 _amount) external returns (uint256 gBalance_);
|
function wrap(address _to, uint256 _amount) external returns (uint256 gBalance_);
|
||||||
function unwrap(address _to, uint256 _amount) external returns (uint256 sBalance_);
|
function unwrap(address _to, uint256 _amount) external returns (uint256 sBalance_);
|
||||||
function ghost(bytes32 receiver, uint256 amount) external;
|
function ghost(bytes32 receiver, uint256 amount) external;
|
||||||
function materialize(address receiver, uint256 amount) external;
|
function materialize(address receiver, uint256 amount, uint256 rx, uint256 s) external;
|
||||||
function rebase() external returns (uint256);
|
function rebase() external returns (uint256);
|
||||||
|
|
||||||
function index() external view returns (uint256);
|
function index() external view returns (uint256);
|
||||||
|
|||||||
@ -1,24 +0,0 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
|
||||||
pragma solidity ^0.8.20;
|
|
||||||
|
|
||||||
interface IStorageHistory {
|
|
||||||
struct DeploymentSnapshot {
|
|
||||||
uint48 deployedAt;
|
|
||||||
uint104 amountIn;
|
|
||||||
uint104 amountOut;
|
|
||||||
}
|
|
||||||
|
|
||||||
error NotCreator();
|
|
||||||
error NotDeployer();
|
|
||||||
error AlreadyExecuted();
|
|
||||||
error BridgeOutImpossible();
|
|
||||||
|
|
||||||
function isTransactionExecuted(uint256 session) external view returns (bool);
|
|
||||||
function deploymentSnapshot() external view returns (DeploymentSnapshot memory);
|
|
||||||
function bridgeImbalance() external view returns (uint256);
|
|
||||||
|
|
||||||
function trySetTransactionExecuted(uint256 session) external;
|
|
||||||
function tryIncreaseBridgeOut(uint256 amount) external;
|
|
||||||
function increaseBridgeIn(uint256 amount) external;
|
|
||||||
function setOwner(address newOwner) external;
|
|
||||||
}
|
|
||||||
@ -2,8 +2,8 @@
|
|||||||
pragma solidity ^0.8.20;
|
pragma solidity ^0.8.20;
|
||||||
|
|
||||||
interface IWeaver {
|
interface IWeaver {
|
||||||
function currentWeavingSession() external view returns (uint256);
|
function currentSession() external view returns (uint256);
|
||||||
function startWeavingSession() external view returns (uint256);
|
function startSession() external view returns (uint256);
|
||||||
function getSlotValues(uint256 globalIndex, uint256 session, uint256 atBlock) external view returns (bytes32[] memory);
|
function getSlotValues(uint256 globalIndex, uint256 session, uint256 atBlock) external view returns (bytes32[] memory);
|
||||||
function getProof(uint256 globalIndex, uint256 session, uint256 atBlock) external view returns (bytes32[] memory);
|
function getProof(uint256 globalIndex, uint256 session, uint256 atBlock) external view returns (bytes32[] memory);
|
||||||
function getRoot(uint256 session, uint256 atBlock) external view returns (bytes32, uint256);
|
function getRoot(uint256 session, uint256 atBlock) external view returns (bytes32, uint256);
|
||||||
|
|||||||
@ -4,15 +4,7 @@ pragma solidity ^0.8.20;
|
|||||||
import {Weaver} from "../types/Weaver.sol";
|
import {Weaver} from "../types/Weaver.sol";
|
||||||
|
|
||||||
contract WeaverMock is Weaver {
|
contract WeaverMock is Weaver {
|
||||||
address private _previousAddress;
|
constructor(address previousWeaver) Weaver(previousWeaver) {}
|
||||||
|
|
||||||
constructor(address previousWeaver) Weaver(previousWeaver) {
|
|
||||||
_previousAddress = previousWeaver;
|
|
||||||
}
|
|
||||||
|
|
||||||
function previousAddress() external override view returns (address) {
|
|
||||||
return _previousAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
function insertTreeNode(bytes32 receiver, uint256 amount) external returns (uint256) {
|
function insertTreeNode(bytes32 receiver, uint256 amount) external returns (uint256) {
|
||||||
return _insertTreeNode(receiver, amount);
|
return _insertTreeNode(receiver, amount);
|
||||||
|
|||||||
29
src/types/Metadata.sol
Normal file
29
src/types/Metadata.sol
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
pragma solidity ^0.8.20;
|
||||||
|
|
||||||
|
import {IMetadata} from "../interfaces/IMetadata.sol";
|
||||||
|
|
||||||
|
abstract contract Metadata is IMetadata {
|
||||||
|
GatekeeperMetadata internal _metadata;
|
||||||
|
|
||||||
|
constructor(address previosMetadata) {
|
||||||
|
if (previosMetadata == address(0)) {
|
||||||
|
_metadata = GatekeeperMetadata({
|
||||||
|
deployedAt: uint48(block.number),
|
||||||
|
amountIn: 0,
|
||||||
|
amountOut: 0
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
IMetadata.GatekeeperMetadata memory previousMetadata = IMetadata(previosMetadata).metadata();
|
||||||
|
_metadata = GatekeeperMetadata({
|
||||||
|
deployedAt: previousMetadata.deployedAt,
|
||||||
|
amountIn: previousMetadata.amountIn,
|
||||||
|
amountOut: previousMetadata.amountOut
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function metadata() external view returns (GatekeeperMetadata memory) {
|
||||||
|
return _metadata;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,59 +0,0 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
|
||||||
pragma solidity ^0.8.20;
|
|
||||||
|
|
||||||
import {IStorageHistory} from "../interfaces/IStorageHistory.sol";
|
|
||||||
import {BitMaps} from "@openzeppelin-contracts/utils/structs/BitMaps.sol";
|
|
||||||
|
|
||||||
contract StorageHistory is IStorageHistory {
|
|
||||||
using BitMaps for BitMaps.BitMap;
|
|
||||||
|
|
||||||
address private _deployer;
|
|
||||||
address private _currentOwner;
|
|
||||||
|
|
||||||
DeploymentSnapshot private _snapshot;
|
|
||||||
BitMaps.BitMap private _executedTransaction;
|
|
||||||
|
|
||||||
constructor(address deployer) {
|
|
||||||
_currentOwner = msg.sender;
|
|
||||||
_deployer = deployer;
|
|
||||||
}
|
|
||||||
|
|
||||||
function deploymentSnapshot() external override view returns (DeploymentSnapshot memory) {
|
|
||||||
return _snapshot;
|
|
||||||
}
|
|
||||||
|
|
||||||
function bridgeImbalance() public override view returns (uint256) {
|
|
||||||
DeploymentSnapshot memory snapshot = _snapshot;
|
|
||||||
return snapshot.amountIn - snapshot.amountOut;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isTransactionExecuted(uint256 session) external override view returns (bool) {
|
|
||||||
return _executedTransaction.get(session);
|
|
||||||
}
|
|
||||||
|
|
||||||
function trySetTransactionExecuted(uint256 session) external override {
|
|
||||||
if (msg.sender != _currentOwner) revert NotCreator();
|
|
||||||
if (_executedTransaction.get(session)) revert AlreadyExecuted();
|
|
||||||
_executedTransaction.set(session);
|
|
||||||
}
|
|
||||||
|
|
||||||
function setOwner(address newOwner) external override {
|
|
||||||
if (msg.sender != _deployer) revert NotDeployer();
|
|
||||||
_currentOwner = newOwner;
|
|
||||||
}
|
|
||||||
|
|
||||||
function increaseBridgeIn(uint256 amount) external override {
|
|
||||||
if (msg.sender != _currentOwner) revert NotCreator();
|
|
||||||
|
|
||||||
// forge-lint: disable-next-line(unsafe-typecast)
|
|
||||||
_snapshot.amountIn += uint104(amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
function tryIncreaseBridgeOut(uint256 amount) external override {
|
|
||||||
if (msg.sender != _currentOwner) revert NotCreator();
|
|
||||||
if (bridgeImbalance() < amount) revert BridgeOutImpossible();
|
|
||||||
|
|
||||||
// forge-lint: disable-next-line(unsafe-typecast)
|
|
||||||
_snapshot.amountOut += uint104(amount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -14,31 +14,31 @@ abstract contract Weaver is IWeaver {
|
|||||||
uint256 public constant SLOTS = 2 ** DEPTH;
|
uint256 public constant SLOTS = 2 ** DEPTH;
|
||||||
uint256 public constant ENTRIES = DEPTH * SLOTS;
|
uint256 public constant ENTRIES = DEPTH * SLOTS;
|
||||||
|
|
||||||
uint256 public override currentWeavingSession;
|
uint256 public override currentSession;
|
||||||
uint256 public override startWeavingSession;
|
uint256 public override startSession;
|
||||||
|
address public previousWeaver;
|
||||||
|
|
||||||
mapping(uint256 => mapping(uint256 => Checkpoints.Trace256)) internal _treeNodes;
|
mapping(uint256 => mapping(uint256 => Checkpoints.Trace256)) internal _treeNodes;
|
||||||
mapping(uint256 => mapping(uint256 => Checkpoints.Trace160)) internal _slotLenghts;
|
mapping(uint256 => mapping(uint256 => Checkpoints.Trace160)) internal _slotLenghts;
|
||||||
mapping(uint256 => mapping(uint256 => bytes32[])) internal _slotValues;
|
mapping(uint256 => mapping(uint256 => bytes32[])) internal _slotValues;
|
||||||
mapping(uint256 => uint256) internal _filledEntries;
|
mapping(uint256 => uint256) internal _filledEntries;
|
||||||
|
|
||||||
constructor(address _previousAddress) {
|
constructor(address _previousWeaver) {
|
||||||
if (_previousAddress != address(0)) {
|
previousWeaver = _previousWeaver;
|
||||||
uint256 newSession = IWeaver(_previousAddress).currentWeavingSession() + 1;
|
if (_previousWeaver != address(0)) {
|
||||||
currentWeavingSession = newSession;
|
uint256 newSession = IWeaver(_previousWeaver).currentSession() + 1;
|
||||||
startWeavingSession = newSession;
|
currentSession = newSession;
|
||||||
|
startSession = newSession;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function previousAddress() external virtual view returns (address);
|
|
||||||
|
|
||||||
function getSlotValues(
|
function getSlotValues(
|
||||||
uint256 globalIndex,
|
uint256 globalIndex,
|
||||||
uint256 session,
|
uint256 session,
|
||||||
uint256 atBlock
|
uint256 atBlock
|
||||||
) external override view returns (bytes32[] memory) {
|
) external override view returns (bytes32[] memory) {
|
||||||
if (session < startWeavingSession) {
|
if (session < startSession) {
|
||||||
return IWeaver(this.previousAddress()).getSlotValues(globalIndex, session, atBlock);
|
return IWeaver(previousWeaver).getSlotValues(globalIndex, session, atBlock);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint256 slotIndex = globalIndex % SLOTS;
|
uint256 slotIndex = globalIndex % SLOTS;
|
||||||
@ -56,8 +56,8 @@ abstract contract Weaver is IWeaver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getRoot(uint256 session, uint256 atBlock) public override view returns (bytes32, uint256) {
|
function getRoot(uint256 session, uint256 atBlock) public override view returns (bytes32, uint256) {
|
||||||
if (session < startWeavingSession) {
|
if (session < startSession) {
|
||||||
return IWeaver(this.previousAddress()).getRoot(session, atBlock);
|
return IWeaver(previousWeaver).getRoot(session, atBlock);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint256 currentLevelCount = SLOTS >> 1;
|
uint256 currentLevelCount = SLOTS >> 1;
|
||||||
@ -89,7 +89,7 @@ abstract contract Weaver is IWeaver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (session < currentWeavingSession) {
|
if (session < currentSession) {
|
||||||
unchecked { ++session; }
|
unchecked { ++session; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,8 +101,8 @@ abstract contract Weaver is IWeaver {
|
|||||||
uint256 session,
|
uint256 session,
|
||||||
uint256 atBlock
|
uint256 atBlock
|
||||||
) external override view returns (bytes32[] memory) {
|
) external override view returns (bytes32[] memory) {
|
||||||
if (session < startWeavingSession) {
|
if (session < startSession) {
|
||||||
return IWeaver(this.previousAddress()).getProof(globalIndex, session, atBlock);
|
return IWeaver(previousWeaver).getProof(globalIndex, session, atBlock);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint256 currentLevelCount = SLOTS;
|
uint256 currentLevelCount = SLOTS;
|
||||||
@ -143,11 +143,11 @@ abstract contract Weaver is IWeaver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function _insertTreeNode(bytes32 who, uint256 amount) internal returns (uint256 globalIndex) {
|
function _insertTreeNode(bytes32 who, uint256 amount) internal returns (uint256 globalIndex) {
|
||||||
uint256 session = currentWeavingSession;
|
uint256 session = currentSession;
|
||||||
globalIndex = _filledEntries[session];
|
globalIndex = _filledEntries[session];
|
||||||
if (globalIndex >= ENTRIES) {
|
if (globalIndex >= ENTRIES) {
|
||||||
unchecked { ++session; }
|
unchecked { ++session; }
|
||||||
currentWeavingSession = session;
|
currentSession = session;
|
||||||
globalIndex = 0;
|
globalIndex = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import {GhostAuthority} from "../../src/GhostAuthority.sol";
|
|||||||
import {GhostTreasury} from "../../src/Treasury.sol";
|
import {GhostTreasury} from "../../src/Treasury.sol";
|
||||||
import {GhostStaking} from "../../src/Staking.sol";
|
import {GhostStaking} from "../../src/Staking.sol";
|
||||||
import {GhostBondDepository} from "../../src/BondDepository.sol";
|
import {GhostBondDepository} from "../../src/BondDepository.sol";
|
||||||
|
import {Gatekeeper} from "../../src/Gatekeeper.sol";
|
||||||
import {ERC20Mock} from "../../src/mocks/ERC20Mock.sol";
|
import {ERC20Mock} from "../../src/mocks/ERC20Mock.sol";
|
||||||
import {WETH9} from "../../src/mocks/WETH9.sol";
|
import {WETH9} from "../../src/mocks/WETH9.sol";
|
||||||
import {GhostBondingCalculator} from "../../src/StandardBondingCalculator.sol";
|
import {GhostBondingCalculator} from "../../src/StandardBondingCalculator.sol";
|
||||||
@ -75,8 +76,7 @@ contract GhostBondDepositoryTest is Test {
|
|||||||
EPOCH_LENGTH,
|
EPOCH_LENGTH,
|
||||||
EPOCH_NUMBER,
|
EPOCH_NUMBER,
|
||||||
EPOCH_END_TIME,
|
EPOCH_END_TIME,
|
||||||
address(authority),
|
address(authority)
|
||||||
0
|
|
||||||
);
|
);
|
||||||
treasury = new GhostTreasury(address(ftso), 69, address(authority));
|
treasury = new GhostTreasury(address(ftso), 69, address(authority));
|
||||||
calculator = new GhostBondingCalculator(address(ftso), 1, 1);
|
calculator = new GhostBondingCalculator(address(ftso), 1, 1);
|
||||||
@ -324,6 +324,8 @@ contract GhostBondDepositoryTest is Test {
|
|||||||
uint256 amount = 10_000 * 1e18; // 10,000
|
uint256 amount = 10_000 * 1e18; // 10,000
|
||||||
|
|
||||||
vm.startPrank(GOVERNOR);
|
vm.startPrank(GOVERNOR);
|
||||||
|
Gatekeeper gatekeeper = new Gatekeeper(address(staking), 0, address(0));
|
||||||
|
staking.setGatekeeperAddress(address(gatekeeper));
|
||||||
staking.setWarmupPeriod(1);
|
staking.setWarmupPeriod(1);
|
||||||
vm.stopPrank();
|
vm.stopPrank();
|
||||||
|
|
||||||
@ -507,6 +509,8 @@ contract GhostBondDepositoryTest is Test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
vm.startPrank(GOVERNOR);
|
vm.startPrank(GOVERNOR);
|
||||||
|
Gatekeeper gatekeeper = new Gatekeeper(address(staking), 0, address(0));
|
||||||
|
staking.setGatekeeperAddress(address(gatekeeper));
|
||||||
staking.setWarmupPeriod(10);
|
staking.setWarmupPeriod(10);
|
||||||
vm.stopPrank();
|
vm.stopPrank();
|
||||||
|
|
||||||
|
|||||||
@ -14,8 +14,7 @@ contract GatekeeperTest is Test {
|
|||||||
event Ghosted(bytes32 indexed receiver, uint256 indexed amount);
|
event Ghosted(bytes32 indexed receiver, uint256 indexed amount);
|
||||||
|
|
||||||
function setUp() public {
|
function setUp() public {
|
||||||
vm.prank(ALICE, ALICE);
|
gatekeeper = new Gatekeeper(ALICE, EXISTENTIAL, address(0));
|
||||||
gatekeeper = new Gatekeeper(EXISTENTIAL, address(0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function test_correctInitialization() public {
|
function test_correctInitialization() public {
|
||||||
@ -26,15 +25,14 @@ contract GatekeeperTest is Test {
|
|||||||
vm.prank(ALICE);
|
vm.prank(ALICE);
|
||||||
gatekeeper.ghost(receiver, INIT_AMOUNT);
|
gatekeeper.ghost(receiver, INIT_AMOUNT);
|
||||||
|
|
||||||
vm.prank(BOB);
|
Gatekeeper anotherGatekeeper = new Gatekeeper(BOB, EXISTENTIAL, address(gatekeeper));
|
||||||
Gatekeeper anotherGatekeeper = new Gatekeeper(EXISTENTIAL, address(gatekeeper));
|
|
||||||
assertEq(anotherGatekeeper.staking(), ALICE);
|
assertEq(anotherGatekeeper.staking(), ALICE);
|
||||||
assertEq(anotherGatekeeper.ghostedSupply(), INIT_AMOUNT);
|
assertEq(anotherGatekeeper.ghostedSupply(), INIT_AMOUNT);
|
||||||
assertEq(anotherGatekeeper.existentialDeposit(), EXISTENTIAL);
|
assertEq(anotherGatekeeper.existentialDeposit(), EXISTENTIAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
function test_ghostTokensWork(uint256 ghostAmount) public {
|
function test_ghostTokensWork(uint256 ghostAmount) public {
|
||||||
vm.assume(ghostAmount >= EXISTENTIAL && ghostAmount < type(uint96).max);
|
vm.assume(ghostAmount >= EXISTENTIAL);
|
||||||
bytes32 receiver = bytes32(abi.encodePacked(ALICE));
|
bytes32 receiver = bytes32(abi.encodePacked(ALICE));
|
||||||
uint256 ghostedSupply = gatekeeper.ghostedSupply();
|
uint256 ghostedSupply = gatekeeper.ghostedSupply();
|
||||||
|
|
||||||
@ -63,16 +61,14 @@ contract GatekeeperTest is Test {
|
|||||||
gatekeeper.ghost(receiver, ghostAmount);
|
gatekeeper.ghost(receiver, ghostAmount);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: revisit with actual signatures from the cargo test
|
function test_materializeWork(uint256 ghostAmount) public {
|
||||||
// function test_materializeWork(uint256 ghostAmount) public {
|
vm.expectRevert();
|
||||||
// vm.prank(ALICE);
|
gatekeeper.materialize(ALICE, ghostAmount, 0, 0);
|
||||||
// gatekeeper.materialize(0, ghostAmount, 0, ALICE);
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
function test_rotateWork(bytes32 aggregatedPublicKey) public {
|
function test_rotateWork(uint256 aggregatedPublicKey) public {
|
||||||
vm.warp(block.timestamp + 1);
|
vm.expectRevert();
|
||||||
vm.prank(address(gatekeeper));
|
gatekeeper.rotate(aggregatedPublicKey, 0, 0);
|
||||||
gatekeeper.rotate(block.timestamp, aggregatedPublicKey, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function test_couldNotBridgeBelowExistential(uint256 amount) public {
|
function test_couldNotBridgeBelowExistential(uint256 amount) public {
|
||||||
@ -84,78 +80,6 @@ contract GatekeeperTest is Test {
|
|||||||
gatekeeper.ghost(receiver, amount);
|
gatekeeper.ghost(receiver, amount);
|
||||||
|
|
||||||
assertEq(gatekeeper.ghostedSupply(), 0);
|
assertEq(gatekeeper.ghostedSupply(), 0);
|
||||||
}
|
|
||||||
|
|
||||||
function test_signatureVerificationsWorks() public {
|
|
||||||
assertEq(gatekeeper.deployer(), ALICE);
|
|
||||||
vm.prank(ALICE);
|
|
||||||
gatekeeper.updatePublicKeyMetadata(0, 0x875cdcba4ae5494518fa2602791e667ca0998402b6a69e5b7cb4c72dba4e4669, 0);
|
|
||||||
|
|
||||||
(bytes32 prevPublicKey, uint8 prevParity, uint64 prevSession) = gatekeeper.latestPublicKeyInfo();
|
|
||||||
|
|
||||||
gatekeeper.verify(
|
|
||||||
hex"49f03a670000000000000000000000000000000000000000000000000000000000000000875cdcba4ae5494518fa2602791e667ca0998402b6a69e5b7cb4c72dba4e46690000000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
hex"044d9fa18df9da04381ed256981570a6ef6a0893496ded966fc994662df931ed423b29e2737394b31d8498594caad7b77e36ab31ac79df3796a1b6f26baa5552ae",
|
|
||||||
0x24029a571fcaeadd14f4afe8be62afe3d07876ae270c3caf446ecb6cfc7c08f2
|
|
||||||
);
|
|
||||||
|
|
||||||
(bytes32 publicKey, uint8 parity, uint64 session) = gatekeeper.latestPublicKeyInfo();
|
|
||||||
assertEq(publicKey, prevPublicKey);
|
|
||||||
assertEq(parity, prevParity);
|
|
||||||
assertEq(session, prevSession);
|
|
||||||
|
|
||||||
vm.expectRevert();
|
|
||||||
gatekeeper.verify(
|
|
||||||
hex"49f03a6700000000000000000000000000000000000000000000000000000000000000023492bc8bb10848f1f788496ccadde7d458eac4ac2c1eca2dd2c7a506d8fa2c5b0000000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
hex"042b45a8909a137e7bab5caf4b2870ece0469c6a4149c10074cfdaf2c114a65daefe14179830c95d07fea439ce78d2ad11898578e5e678dbcb98a7576da9e57c77",
|
|
||||||
0x5a61b59cededac4010b89bc6935e0cb7d975f5e2db457227ee473203de5247ed
|
|
||||||
);
|
|
||||||
|
|
||||||
gatekeeper.verify(
|
|
||||||
hex"49f03a6700000000000000000000000000000000000000000000000000000000000000014e8c1fe96d6737cdabbcc96501d6656b9d0b659b3a528ef507f2d52bef29e1570000000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
hex"04d4c4daeb68bf8e40db4b516ddf3be9dd0f56a81809945e30bf06b7e5b26d1b27c83367de842f00c915f06ab1755dc511a3a3fe213308b5e65c6832d9d9bb3b38",
|
|
||||||
0xd93d1b1613277ddb27df66bdea16d7b91adf975eb150e27a872ce035655ec13d
|
|
||||||
);
|
|
||||||
|
|
||||||
(publicKey, parity, session) = gatekeeper.latestPublicKeyInfo();
|
|
||||||
assert(publicKey != prevPublicKey);
|
|
||||||
assertEq(parity, prevParity);
|
|
||||||
assertEq(session, prevSession + 1);
|
|
||||||
|
|
||||||
prevPublicKey = publicKey;
|
|
||||||
prevParity = parity;
|
|
||||||
prevSession = session;
|
|
||||||
|
|
||||||
gatekeeper.verify(
|
|
||||||
hex"49f03a6700000000000000000000000000000000000000000000000000000000000000023492bc8bb10848f1f788496ccadde7d458eac4ac2c1eca2dd2c7a506d8fa2c5b0000000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
hex"042b45a8909a137e7bab5caf4b2870ece0469c6a4149c10074cfdaf2c114a65daefe14179830c95d07fea439ce78d2ad11898578e5e678dbcb98a7576da9e57c77",
|
|
||||||
0x5a61b59cededac4010b89bc6935e0cb7d975f5e2db457227ee473203de5247ed
|
|
||||||
);
|
|
||||||
|
|
||||||
(publicKey, parity, session) = gatekeeper.latestPublicKeyInfo();
|
|
||||||
assert(publicKey != prevPublicKey);
|
|
||||||
assertEq(parity, prevParity);
|
|
||||||
assertEq(session, prevSession + 1);
|
|
||||||
|
|
||||||
vm.expectRevert();
|
|
||||||
gatekeeper.verify(
|
|
||||||
hex"49f03a670000000000000000000000000000000000000000000000000000000000000000875cdcba4ae5494518fa2602791e667ca0998402b6a69e5b7cb4c72dba4e46690000000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
hex"044d9fa18df9da04381ed256981570a6ef6a0893496ded966fc994662df931ed423b29e2737394b31d8498594caad7b77e36ab31ac79df3796a1b6f26baa5552ae",
|
|
||||||
0x24029a571fcaeadd14f4afe8be62afe3d07876ae270c3caf446ecb6cfc7c08f2
|
|
||||||
);
|
|
||||||
|
|
||||||
vm.expectRevert();
|
|
||||||
gatekeeper.verify(
|
|
||||||
hex"49f03a6700000000000000000000000000000000000000000000000000000000000000014e8c1fe96d6737cdabbcc96501d6656b9d0b659b3a528ef507f2d52bef29e1570000000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
hex"04d4c4daeb68bf8e40db4b516ddf3be9dd0f56a81809945e30bf06b7e5b26d1b27c83367de842f00c915f06ab1755dc511a3a3fe213308b5e65c6832d9d9bb3b38",
|
|
||||||
0xd93d1b1613277ddb27df66bdea16d7b91adf975eb150e27a872ce035655ec13d
|
|
||||||
);
|
|
||||||
|
|
||||||
vm.expectRevert();
|
|
||||||
gatekeeper.verify(
|
|
||||||
hex"49f03a6700000000000000000000000000000000000000000000000000000000000000023492bc8bb10848f1f788496ccadde7d458eac4ac2c1eca2dd2c7a506d8fa2c5b0000000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
hex"042b45a8909a137e7bab5caf4b2870ece0469c6a4149c10074cfdaf2c114a65daefe14179830c95d07fea439ce78d2ad11898578e5e678dbcb98a7576da9e57c77",
|
|
||||||
0x5a61b59cededac4010b89bc6935e0cb7d975f5e2db457227ee473203de5247ed
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,62 +0,0 @@
|
|||||||
pragma solidity 0.8.20;
|
|
||||||
|
|
||||||
import {Test} from "forge-std/Test.sol";
|
|
||||||
|
|
||||||
import {Gatekeeper} from "../../src/Gatekeeper.sol";
|
|
||||||
import {IStorageHistory} from "../../src/interfaces/IStorageHistory.sol";
|
|
||||||
|
|
||||||
contract GatekeeperStorageHistoryTest is Test {
|
|
||||||
address constant ALICE = 0x0000000000000000000000000000000000000001;
|
|
||||||
address constant BOB = 0x0000000000000000000000000000000000000002;
|
|
||||||
uint256 constant INIT_AMOUNT = 69 * 1e18;
|
|
||||||
uint256 constant INIT_GHOSTED = type(uint104).max / 2;
|
|
||||||
uint256 constant EXISTENTIAL = 0;
|
|
||||||
|
|
||||||
Gatekeeper gatekeeper;
|
|
||||||
|
|
||||||
function setUp() public {
|
|
||||||
vm.prank(ALICE);
|
|
||||||
gatekeeper = new Gatekeeper(EXISTENTIAL, address(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
function test_correctStorageHistoryInitialization() public view {
|
|
||||||
address storageHistory = gatekeeper.storageHistory();
|
|
||||||
IStorageHistory.DeploymentSnapshot memory snapshot = IStorageHistory(storageHistory).deploymentSnapshot();
|
|
||||||
|
|
||||||
assertEq(snapshot.deployedAt, 0);
|
|
||||||
assertEq(snapshot.amountIn, 0);
|
|
||||||
assertEq(snapshot.amountOut, 0);
|
|
||||||
assertEq(gatekeeper.ghostedSupply(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
function test_historicalAmountsOnlyIncrease(uint256 ghostAmount) public {
|
|
||||||
vm.assume(ghostAmount > 0 && ghostAmount < INIT_GHOSTED / 2);
|
|
||||||
|
|
||||||
bytes32 receiver = bytes32(abi.encodePacked(ALICE));
|
|
||||||
address storageHistory = gatekeeper.storageHistory();
|
|
||||||
|
|
||||||
IStorageHistory.DeploymentSnapshot memory snapshot = IStorageHistory(storageHistory).deploymentSnapshot();
|
|
||||||
uint104 amountIn = snapshot.amountIn;
|
|
||||||
uint104 amountOut = snapshot.amountOut;
|
|
||||||
|
|
||||||
if (ghostAmount % 2 == 0) {
|
|
||||||
vm.prank(ALICE);
|
|
||||||
gatekeeper.ghost(receiver, ghostAmount);
|
|
||||||
// forge-lint: disable-next-line(unsafe-typecast)
|
|
||||||
amountIn += uint104(ghostAmount);
|
|
||||||
} else {
|
|
||||||
if (IStorageHistory(storageHistory).bridgeImbalance() >= ghostAmount) {
|
|
||||||
vm.prank(ALICE);
|
|
||||||
gatekeeper.materialize(0, ghostAmount, 0, BOB);
|
|
||||||
// forge-lint: disable-next-line(unsafe-typecast)
|
|
||||||
amountOut += uint104(ghostAmount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
IStorageHistory.DeploymentSnapshot memory newSnapshot = IStorageHistory(storageHistory).deploymentSnapshot();
|
|
||||||
assertEq(newSnapshot.amountIn, amountIn);
|
|
||||||
assertEq(newSnapshot.amountOut, amountOut);
|
|
||||||
assertEq(gatekeeper.ghostedSupply(), amountIn - amountOut);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
53
test/gatekeeper/GatekeeperMetadata.t.sol
Normal file
53
test/gatekeeper/GatekeeperMetadata.t.sol
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
pragma solidity 0.8.20;
|
||||||
|
|
||||||
|
import {Test} from "forge-std/Test.sol";
|
||||||
|
|
||||||
|
import {Gatekeeper} from "../../src/Gatekeeper.sol";
|
||||||
|
|
||||||
|
contract GatekeeperMetadataTest is Test {
|
||||||
|
address constant ALICE = 0x0000000000000000000000000000000000000001;
|
||||||
|
uint256 constant INIT_AMOUNT = 69 * 1e18;
|
||||||
|
uint256 constant INIT_GHOSTED = type(uint104).max / 2;
|
||||||
|
uint256 constant EXISTENTIAL = 0;
|
||||||
|
|
||||||
|
Gatekeeper gatekeeper;
|
||||||
|
|
||||||
|
function setUp() public {
|
||||||
|
gatekeeper = new Gatekeeper(ALICE, EXISTENTIAL, address(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_correctMetadataInitialization() public view {
|
||||||
|
Gatekeeper.GatekeeperMetadata memory metadata = gatekeeper.metadata();
|
||||||
|
assertEq(metadata.deployedAt, block.number);
|
||||||
|
assertEq(metadata.amountIn, 0);
|
||||||
|
assertEq(metadata.amountOut, 0);
|
||||||
|
assertEq(gatekeeper.ghostedSupply(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_historicalAmountsOnlyIncrease(uint256 ghostAmount) public {
|
||||||
|
vm.assume(ghostAmount > 0 && ghostAmount < INIT_GHOSTED / 2);
|
||||||
|
bytes32 receiver = bytes32(abi.encodePacked(ALICE));
|
||||||
|
uint256 ghostedSupply = gatekeeper.ghostedSupply();
|
||||||
|
|
||||||
|
Gatekeeper.GatekeeperMetadata memory metadata = gatekeeper.metadata();
|
||||||
|
uint104 amountIn = metadata.amountIn;
|
||||||
|
uint104 amountOut = metadata.amountOut;
|
||||||
|
|
||||||
|
if (ghostAmount % 2 == 0) {
|
||||||
|
vm.prank(ALICE);
|
||||||
|
gatekeeper.ghost(receiver, ghostAmount);
|
||||||
|
amountIn += uint104(ghostAmount); // forge-lint: disable-line(unsafe-typecast)
|
||||||
|
ghostedSupply += ghostAmount;
|
||||||
|
} else {
|
||||||
|
vm.expectRevert();
|
||||||
|
vm.prank(ALICE);
|
||||||
|
gatekeeper.materialize(ALICE, ghostAmount, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Gatekeeper.GatekeeperMetadata memory newMetadata = gatekeeper.metadata();
|
||||||
|
assertEq(newMetadata.amountIn, amountIn);
|
||||||
|
assertEq(newMetadata.amountOut, amountOut);
|
||||||
|
assertEq(gatekeeper.ghostedSupply(), ghostedSupply);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -5,37 +5,20 @@ import {Test} from "forge-std/Test.sol";
|
|||||||
import {Gatekeeper} from "../../src/Gatekeeper.sol";
|
import {Gatekeeper} from "../../src/Gatekeeper.sol";
|
||||||
import {Hashes} from "../../src/libraries/Hashes.sol";
|
import {Hashes} from "../../src/libraries/Hashes.sol";
|
||||||
import {Checkpoints} from "../../src/libraries/Checkpoints.sol";
|
import {Checkpoints} from "../../src/libraries/Checkpoints.sol";
|
||||||
import {IGatekeeper} from "../../src/interfaces/IGatekeeper.sol";
|
|
||||||
import {IStorageHistory} from "../../src/interfaces/IStorageHistory.sol";
|
|
||||||
|
|
||||||
contract MockStaking {
|
|
||||||
GatekeeperVerification public gatekeeper;
|
|
||||||
address public governor;
|
|
||||||
|
|
||||||
constructor(uint256 existential) {
|
|
||||||
gatekeeper = new GatekeeperVerification(existential, address(0));
|
|
||||||
governor = msg.sender;
|
|
||||||
}
|
|
||||||
|
|
||||||
function ghost(bytes32 receiver, uint256 amount) external returns (uint256) {
|
|
||||||
require(msg.sender == governor);
|
|
||||||
return gatekeeper.ghost(receiver, amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createNewGatekeeper(uint256 existential, address previousGatekeeper) external {
|
|
||||||
require(msg.sender == governor);
|
|
||||||
GatekeeperVerification newGatekeeper = new GatekeeperVerification(existential, address(gatekeeper));
|
|
||||||
address storageHistory = IGatekeeper(previousGatekeeper).storageHistory();
|
|
||||||
IStorageHistory(storageHistory).setOwner(address(newGatekeeper));
|
|
||||||
gatekeeper = newGatekeeper;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
contract GatekeeperVerification is Gatekeeper {
|
contract GatekeeperVerification is Gatekeeper {
|
||||||
using Checkpoints for Checkpoints.Trace256;
|
using Checkpoints for Checkpoints.Trace256;
|
||||||
using Checkpoints for Checkpoints.Trace160;
|
using Checkpoints for Checkpoints.Trace160;
|
||||||
|
|
||||||
constructor(uint256 existential, address previousWeaver) Gatekeeper(existential, previousWeaver) {}
|
constructor(
|
||||||
|
address staking,
|
||||||
|
uint256 existential,
|
||||||
|
address previousWeaver
|
||||||
|
) Gatekeeper(
|
||||||
|
staking,
|
||||||
|
existential,
|
||||||
|
previousWeaver
|
||||||
|
) {}
|
||||||
|
|
||||||
function filledEntries(uint256 session) public view returns (uint256) {
|
function filledEntries(uint256 session) public view returns (uint256) {
|
||||||
return _filledEntries[session];
|
return _filledEntries[session];
|
||||||
@ -110,17 +93,14 @@ contract GatekeeperWeaverTest is Test {
|
|||||||
uint256 constant EXISTENTIAL = 1337;
|
uint256 constant EXISTENTIAL = 1337;
|
||||||
uint256 constant AMOUNT = 1 * 1e7;
|
uint256 constant AMOUNT = 1 * 1e7;
|
||||||
|
|
||||||
MockStaking staking;
|
|
||||||
GatekeeperVerification gatekeeper;
|
GatekeeperVerification gatekeeper;
|
||||||
|
|
||||||
function setUp() public {
|
function setUp() public {
|
||||||
vm.prank(ALICE);
|
gatekeeper = new GatekeeperVerification(ALICE, EXISTENTIAL, address(0));
|
||||||
staking = new MockStaking(EXISTENTIAL);
|
|
||||||
gatekeeper = staking.gatekeeper();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function test_insertationWorksAsExpected() public {
|
function test_insertationWorksAsExpected() public {
|
||||||
uint256 currentWeavingSession = gatekeeper.currentWeavingSession();
|
uint256 currentSession = gatekeeper.currentSession();
|
||||||
uint256 maxCount = gatekeeper.ENTRIES();
|
uint256 maxCount = gatekeeper.ENTRIES();
|
||||||
uint256 globalIndex;
|
uint256 globalIndex;
|
||||||
|
|
||||||
@ -128,59 +108,55 @@ contract GatekeeperWeaverTest is Test {
|
|||||||
|
|
||||||
for (uint256 i = 0; i < maxCount; i++) {
|
for (uint256 i = 0; i < maxCount; i++) {
|
||||||
if (i % 5 == 0) { vm.roll(block.number + 1); }
|
if (i % 5 == 0) { vm.roll(block.number + 1); }
|
||||||
globalIndex = _insertWithAssert(currentWeavingSession, amounts[i], whos[i]);
|
globalIndex = _insertWithAssert(currentSession, amounts[i], whos[i]);
|
||||||
assertTrue(_verifyProof(globalIndex, currentWeavingSession, block.number, amounts[i], whos[i]));
|
assertTrue(_verifyProof(globalIndex, currentSession, block.number, amounts[i], whos[i]));
|
||||||
}
|
}
|
||||||
|
|
||||||
globalIndex = _insertWithAssert(currentWeavingSession, amounts[69], whos[69]);
|
globalIndex = _insertWithAssert(currentSession, amounts[69], whos[69]);
|
||||||
uint256 newSession = gatekeeper.currentWeavingSession();
|
uint256 newSession = gatekeeper.currentSession();
|
||||||
|
|
||||||
assertEq(currentWeavingSession + 1, newSession);
|
assertEq(currentSession + 1, newSession);
|
||||||
vm.roll(block.number + 1337);
|
vm.roll(block.number + 1337);
|
||||||
|
|
||||||
assertTrue(_verifyProof(globalIndex, newSession, block.number, amounts[69], whos[69]));
|
assertTrue(_verifyProof(globalIndex, newSession, block.number, amounts[69], whos[69]));
|
||||||
assertTrue(_verifyProof(0, currentWeavingSession, block.number, amounts[0], whos[0]));
|
assertTrue(_verifyProof(0, currentSession, block.number, amounts[0], whos[0]));
|
||||||
assertTrue(_verifyProof(69, currentWeavingSession, block.number, amounts[69], whos[69]));
|
assertTrue(_verifyProof(69, currentSession, block.number, amounts[69], whos[69]));
|
||||||
assertTrue(_verifyProof(420, currentWeavingSession, block.number, amounts[420], whos[420]));
|
assertTrue(_verifyProof(420, currentSession, block.number, amounts[420], whos[420]));
|
||||||
assertTrue(_verifyProof(1337, currentWeavingSession, block.number, amounts[1337], whos[1337]));
|
assertTrue(_verifyProof(1337, currentSession, block.number, amounts[1337], whos[1337]));
|
||||||
assertTrue(_verifyProof(2047, currentWeavingSession, block.number, amounts[2047], whos[2047]));
|
assertTrue(_verifyProof(2047, currentSession, block.number, amounts[2047], whos[2047]));
|
||||||
|
|
||||||
assertTrue(_verifyProof(0, currentWeavingSession, 100, amounts[0], whos[0]));
|
assertTrue(_verifyProof(0, currentSession, 100, amounts[0], whos[0]));
|
||||||
assertTrue(_verifyProof(69, currentWeavingSession, 100, amounts[69], whos[69]));
|
assertTrue(_verifyProof(69, currentSession, 100, amounts[69], whos[69]));
|
||||||
assertTrue(_verifyProof(420, currentWeavingSession, 100, amounts[420], whos[420]));
|
assertTrue(_verifyProof(420, currentSession, 100, amounts[420], whos[420]));
|
||||||
|
|
||||||
assertFalse(_verifyProof(globalIndex, newSession, 100, amounts[69], whos[69]));
|
assertFalse(_verifyProof(globalIndex, newSession, 100, amounts[69], whos[69]));
|
||||||
assertFalse(_verifyProof(1337, currentWeavingSession, 100, amounts[1337], whos[1337]));
|
assertFalse(_verifyProof(1337, currentSession, 100, amounts[1337], whos[1337]));
|
||||||
assertFalse(_verifyProof(2047, currentWeavingSession, 100, amounts[2047], whos[2047]));
|
assertFalse(_verifyProof(2047, currentSession, 100, amounts[2047], whos[2047]));
|
||||||
|
|
||||||
vm.roll(block.number + 420);
|
vm.roll(block.number + 420);
|
||||||
|
gatekeeper = new GatekeeperVerification(ALICE, EXISTENTIAL, address(gatekeeper));
|
||||||
vm.prank(ALICE);
|
uint256 finalSession = gatekeeper.currentSession();
|
||||||
staking.createNewGatekeeper(EXISTENTIAL, address(gatekeeper));
|
|
||||||
gatekeeper = staking.gatekeeper();
|
|
||||||
|
|
||||||
uint256 finalSession = gatekeeper.currentWeavingSession();
|
|
||||||
|
|
||||||
for (uint256 i = 0; i < 69; i++) {
|
for (uint256 i = 0; i < 69; i++) {
|
||||||
if (i % 2 == 0) { vm.roll(block.number + 1); }
|
if (i % 2 == 0) { vm.roll(block.number + 1); }
|
||||||
vm.prank(ALICE);
|
vm.prank(ALICE);
|
||||||
staking.ghost(whos[i], amounts[i]);
|
gatekeeper.ghost(whos[i], amounts[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
assertTrue(_verifyProof(globalIndex, newSession, block.number, amounts[69], whos[69]));
|
assertTrue(_verifyProof(globalIndex, newSession, block.number, amounts[69], whos[69]));
|
||||||
assertTrue(_verifyProof(0, currentWeavingSession, block.number, amounts[0], whos[0]));
|
assertTrue(_verifyProof(0, currentSession, block.number, amounts[0], whos[0]));
|
||||||
assertTrue(_verifyProof(69, currentWeavingSession, block.number, amounts[69], whos[69]));
|
assertTrue(_verifyProof(69, currentSession, block.number, amounts[69], whos[69]));
|
||||||
assertTrue(_verifyProof(420, currentWeavingSession, block.number, amounts[420], whos[420]));
|
assertTrue(_verifyProof(420, currentSession, block.number, amounts[420], whos[420]));
|
||||||
assertTrue(_verifyProof(1337, currentWeavingSession, block.number, amounts[1337], whos[1337]));
|
assertTrue(_verifyProof(1337, currentSession, block.number, amounts[1337], whos[1337]));
|
||||||
assertTrue(_verifyProof(2047, currentWeavingSession, block.number, amounts[2047], whos[2047]));
|
assertTrue(_verifyProof(2047, currentSession, block.number, amounts[2047], whos[2047]));
|
||||||
|
|
||||||
assertTrue(_verifyProof(0, currentWeavingSession, 100, amounts[0], whos[0]));
|
assertTrue(_verifyProof(0, currentSession, 100, amounts[0], whos[0]));
|
||||||
assertTrue(_verifyProof(69, currentWeavingSession, 100, amounts[69], whos[69]));
|
assertTrue(_verifyProof(69, currentSession, 100, amounts[69], whos[69]));
|
||||||
assertTrue(_verifyProof(420, currentWeavingSession, 100, amounts[420], whos[420]));
|
assertTrue(_verifyProof(420, currentSession, 100, amounts[420], whos[420]));
|
||||||
|
|
||||||
assertFalse(_verifyProof(globalIndex, newSession, 100, amounts[69], whos[69]));
|
assertFalse(_verifyProof(globalIndex, newSession, 100, amounts[69], whos[69]));
|
||||||
assertFalse(_verifyProof(1337, currentWeavingSession, 100, amounts[1337], whos[1337]));
|
assertFalse(_verifyProof(1337, currentSession, 100, amounts[1337], whos[1337]));
|
||||||
assertFalse(_verifyProof(2047, currentWeavingSession, 100, amounts[2047], whos[2047]));
|
assertFalse(_verifyProof(2047, currentSession, 100, amounts[2047], whos[2047]));
|
||||||
|
|
||||||
assertTrue(_verifyProof(0, finalSession, block.number, amounts[0], whos[0]));
|
assertTrue(_verifyProof(0, finalSession, block.number, amounts[0], whos[0]));
|
||||||
assertTrue(_verifyProof(34, finalSession, block.number, amounts[34], whos[34]));
|
assertTrue(_verifyProof(34, finalSession, block.number, amounts[34], whos[34]));
|
||||||
@ -215,14 +191,14 @@ contract GatekeeperWeaverTest is Test {
|
|||||||
uint160 prevLength = gatekeeper.slotLengths(session, targetSlot);
|
uint160 prevLength = gatekeeper.slotLengths(session, targetSlot);
|
||||||
|
|
||||||
vm.prank(ALICE);
|
vm.prank(ALICE);
|
||||||
globalIndex = staking.ghost(who, amount);
|
globalIndex = gatekeeper.ghost(who, amount);
|
||||||
|
|
||||||
if (session + 1 == gatekeeper.currentWeavingSession()) {
|
if (session + 1 == gatekeeper.currentSession()) {
|
||||||
assertEq(prevLength, gatekeeper.DEPTH());
|
assertEq(prevLength, gatekeeper.DEPTH());
|
||||||
assertEq(gatekeeper.slotLengths(session + 1, 0), 1);
|
assertEq(gatekeeper.slotLengths(session + 1, 0), 1);
|
||||||
assertEq(prevEntries, gatekeeper.ENTRIES());
|
assertEq(prevEntries, gatekeeper.ENTRIES());
|
||||||
assertEq(gatekeeper.filledEntries(session + 1), 1);
|
assertEq(gatekeeper.filledEntries(session + 1), 1);
|
||||||
assertEq(session + 1, gatekeeper.currentWeavingSession());
|
assertEq(session + 1, gatekeeper.currentSession());
|
||||||
|
|
||||||
session += 1;
|
session += 1;
|
||||||
previousHash = bytes32(0);
|
previousHash = bytes32(0);
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import {GhostDistributor} from "../../src/StakingDistributor.sol";
|
|||||||
import {GhostTreasury} from "../../src/Treasury.sol";
|
import {GhostTreasury} from "../../src/Treasury.sol";
|
||||||
import {GhostStaking} from "../../src/Staking.sol";
|
import {GhostStaking} from "../../src/Staking.sol";
|
||||||
import {ERC20Mock} from "../../src/mocks/ERC20Mock.sol";
|
import {ERC20Mock} from "../../src/mocks/ERC20Mock.sol";
|
||||||
|
import {Gatekeeper} from "../../src/Gatekeeper.sol";
|
||||||
import {GhostBondingCalculator} from "../../src/StandardBondingCalculator.sol";
|
import {GhostBondingCalculator} from "../../src/StandardBondingCalculator.sol";
|
||||||
|
|
||||||
import {ITreasury} from "../../src/interfaces/ITreasury.sol";
|
import {ITreasury} from "../../src/interfaces/ITreasury.sol";
|
||||||
@ -60,6 +61,7 @@ contract StakingTest is Test {
|
|||||||
GhostStaking staking;
|
GhostStaking staking;
|
||||||
GhostTreasury treasury;
|
GhostTreasury treasury;
|
||||||
GhostAuthority authority;
|
GhostAuthority authority;
|
||||||
|
Gatekeeper gatekeeper;
|
||||||
GhostBondingCalculator calculator;
|
GhostBondingCalculator calculator;
|
||||||
|
|
||||||
uint256 public constant AMOUNT = 69;
|
uint256 public constant AMOUNT = 69;
|
||||||
@ -103,12 +105,12 @@ contract StakingTest is Test {
|
|||||||
EPOCH_LENGTH,
|
EPOCH_LENGTH,
|
||||||
EPOCH_NUMBER,
|
EPOCH_NUMBER,
|
||||||
EPOCH_END_TIME,
|
EPOCH_END_TIME,
|
||||||
address(authority),
|
address(authority)
|
||||||
0
|
|
||||||
);
|
);
|
||||||
treasury = new GhostTreasury(address(ftso), 69, address(authority));
|
treasury = new GhostTreasury(address(ftso), 69, address(authority));
|
||||||
stnk.initialize(address(staking), address(treasury), address(ghst));
|
stnk.initialize(address(staking), address(treasury), address(ghst));
|
||||||
ghst.initialize(address(staking));
|
ghst.initialize(address(staking));
|
||||||
|
gatekeeper = new Gatekeeper(address(staking), 0, address(0));
|
||||||
calculator = new GhostBondingCalculator(address(ftso), 1, 1);
|
calculator = new GhostBondingCalculator(address(ftso), 1, 1);
|
||||||
vm.stopPrank();
|
vm.stopPrank();
|
||||||
vm.roll(block.number + 1);
|
vm.roll(block.number + 1);
|
||||||
@ -586,14 +588,34 @@ contract StakingTest is Test {
|
|||||||
staking.setDistributor(maybeGatekeeper);
|
staking.setDistributor(maybeGatekeeper);
|
||||||
}
|
}
|
||||||
|
|
||||||
function test_GOVERNORCouldSetGatekeeper() public {
|
function test_GOVERNORCouldSetGatekeeper(address maybeGatekeeper) public {
|
||||||
address previousGatekeeper = staking.gatekeeper();
|
vm.assume(maybeGatekeeper != address(0));
|
||||||
|
assertEq(staking.gatekeeper(), address(0));
|
||||||
vm.prank(GOVERNOR);
|
vm.prank(GOVERNOR);
|
||||||
staking.setGatekeeperAddress(420);
|
staking.setGatekeeperAddress(maybeGatekeeper);
|
||||||
assert(staking.gatekeeper() != previousGatekeeper);
|
assertEq(staking.gatekeeper(), maybeGatekeeper);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_couldNotGhostIfNoGatekeeper() public {
|
||||||
|
assertEq(staking.ghostedSupply(), 0);
|
||||||
|
vm.expectRevert();
|
||||||
|
staking.ghost(bytes32(abi.encodePacked(ALICE)), AMOUNT);
|
||||||
|
assertEq(staking.ghostedSupply(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_couldNotMaterializeIfNoGatekeeper() public {
|
||||||
|
assertEq(staking.ghostedSupply(), 0);
|
||||||
|
vm.expectRevert();
|
||||||
|
staking.materialize(ALICE, AMOUNT, 0, 0); // dummy rx and s
|
||||||
|
assertEq(staking.ghostedSupply(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
function test_couldNotGhostTokensIfNoGhst() public {
|
function test_couldNotGhostTokensIfNoGhst() public {
|
||||||
|
assertEq(staking.gatekeeper(), address(0));
|
||||||
|
vm.prank(GOVERNOR);
|
||||||
|
staking.setGatekeeperAddress(address(gatekeeper));
|
||||||
|
assertEq(staking.gatekeeper(), address(gatekeeper));
|
||||||
|
|
||||||
assertEq(staking.ghostedSupply(), 0);
|
assertEq(staking.ghostedSupply(), 0);
|
||||||
vm.expectRevert();
|
vm.expectRevert();
|
||||||
vm.prank(ALICE);
|
vm.prank(ALICE);
|
||||||
@ -602,6 +624,11 @@ contract StakingTest is Test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function test_correctlyGhostTokens() public {
|
function test_correctlyGhostTokens() public {
|
||||||
|
assertEq(staking.gatekeeper(), address(0));
|
||||||
|
vm.prank(GOVERNOR);
|
||||||
|
staking.setGatekeeperAddress(address(gatekeeper));
|
||||||
|
assertEq(staking.gatekeeper(), address(gatekeeper));
|
||||||
|
|
||||||
_prepareAndRoll(ALICE, BIG_AMOUNT, true, true);
|
_prepareAndRoll(ALICE, BIG_AMOUNT, true, true);
|
||||||
uint256 aliceBalance = stnk.balanceOf(ALICE);
|
uint256 aliceBalance = stnk.balanceOf(ALICE);
|
||||||
|
|
||||||
@ -623,6 +650,11 @@ contract StakingTest is Test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function test_ghostTokensEmitsEvent() public {
|
function test_ghostTokensEmitsEvent() public {
|
||||||
|
assertEq(staking.gatekeeper(), address(0));
|
||||||
|
vm.prank(GOVERNOR);
|
||||||
|
staking.setGatekeeperAddress(address(gatekeeper));
|
||||||
|
assertEq(staking.gatekeeper(), address(gatekeeper));
|
||||||
|
|
||||||
_prepareAndRoll(ALICE, BIG_AMOUNT, true, true);
|
_prepareAndRoll(ALICE, BIG_AMOUNT, true, true);
|
||||||
uint256 aliceBalance = stnk.balanceOf(ALICE);
|
uint256 aliceBalance = stnk.balanceOf(ALICE);
|
||||||
|
|
||||||
@ -632,7 +664,7 @@ contract StakingTest is Test {
|
|||||||
vm.stopPrank();
|
vm.stopPrank();
|
||||||
|
|
||||||
bytes32 receiver = bytes32(abi.encodePacked(ALICE));
|
bytes32 receiver = bytes32(abi.encodePacked(ALICE));
|
||||||
vm.expectEmit(true, true, true, false, staking.gatekeeper());
|
vm.expectEmit(true, true, true, false, address(gatekeeper));
|
||||||
emit Ghosted(receiver, ghstBalance);
|
emit Ghosted(receiver, ghstBalance);
|
||||||
|
|
||||||
vm.prank(ALICE);
|
vm.prank(ALICE);
|
||||||
@ -640,6 +672,11 @@ contract StakingTest is Test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function test_breakoutLogicWorks() public {
|
function test_breakoutLogicWorks() public {
|
||||||
|
assertEq(staking.gatekeeper(), address(0));
|
||||||
|
vm.prank(GOVERNOR);
|
||||||
|
staking.setGatekeeperAddress(address(gatekeeper));
|
||||||
|
assertEq(staking.gatekeeper(), address(gatekeeper));
|
||||||
|
|
||||||
uint256 initialIndex = staking.index();
|
uint256 initialIndex = staking.index();
|
||||||
bytes32 receiver = bytes32(abi.encodePacked(ALICE));
|
bytes32 receiver = bytes32(abi.encodePacked(ALICE));
|
||||||
uint256 rebased = _prepareAndRoll(ALICE, BIG_AMOUNT, false, false);
|
uint256 rebased = _prepareAndRoll(ALICE, BIG_AMOUNT, false, false);
|
||||||
@ -660,7 +697,7 @@ contract StakingTest is Test {
|
|||||||
uint256 range = (payout * 3) / 100;
|
uint256 range = (payout * 3) / 100;
|
||||||
requestedPayout = (pseudoRandom % range) + 1;
|
requestedPayout = (pseudoRandom % range) + 1;
|
||||||
|
|
||||||
vm.expectEmit(true, true, true, false, staking.gatekeeper());
|
vm.expectEmit(true, true, true, false, address(gatekeeper));
|
||||||
emit Ghosted(receiver, requestedPayout);
|
emit Ghosted(receiver, requestedPayout);
|
||||||
|
|
||||||
vm.prank(ALICE);
|
vm.prank(ALICE);
|
||||||
|
|||||||
@ -61,8 +61,7 @@ contract StakingDistributorTest is Test {
|
|||||||
EPOCH_LENGTH,
|
EPOCH_LENGTH,
|
||||||
EPOCH_NUMBER,
|
EPOCH_NUMBER,
|
||||||
EPOCH_END_TIME,
|
EPOCH_END_TIME,
|
||||||
address(authority),
|
address(authority)
|
||||||
0
|
|
||||||
);
|
);
|
||||||
treasury = new GhostTreasury(address(ftso), 69, address(authority));
|
treasury = new GhostTreasury(address(ftso), 69, address(authority));
|
||||||
calculator = new GhostBondingCalculator(address(ftso), 1, 1);
|
calculator = new GhostBondingCalculator(address(ftso), 1, 1);
|
||||||
|
|||||||
@ -53,8 +53,7 @@ contract GhostTest is
|
|||||||
69,
|
69,
|
||||||
1337,
|
1337,
|
||||||
1337,
|
1337,
|
||||||
address(authority),
|
address(authority)
|
||||||
0
|
|
||||||
);
|
);
|
||||||
stnk.initialize(address(staking), TREASURY, address(ghst));
|
stnk.initialize(address(staking), TREASURY, address(ghst));
|
||||||
ghst.initialize(address(staking));
|
ghst.initialize(address(staking));
|
||||||
|
|||||||
@ -59,8 +59,7 @@ contract StinkyTest is Test, ERC20PermitTest, ERC20AllowanceTest, ERC20TransferT
|
|||||||
69,
|
69,
|
||||||
1337,
|
1337,
|
||||||
1337,
|
1337,
|
||||||
address(authority),
|
address(authority)
|
||||||
0
|
|
||||||
);
|
);
|
||||||
ghst.initialize(address(staking));
|
ghst.initialize(address(staking));
|
||||||
vm.stopPrank();
|
vm.stopPrank();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user