Compare commits

..

2 Commits

Author SHA1 Message Date
379cc331e2
make metadata historically dependant
Signed-off-by: Uncle Fatso <uncle.fatso@ghostchain.io>
2026-08-02 22:13:31 +03:00
15e93e7f32
make weaving historically dependant
Signed-off-by: Uncle Fatso <uncle.fatso@ghostchain.io>
2026-08-02 21:23:43 +03:00
12 changed files with 112 additions and 64 deletions

View File

@ -104,18 +104,12 @@ GOVERNOR_VOTING_PERIOD=
GOVERNOR_PROPOSAL_THRESHOLD= GOVERNOR_PROPOSAL_THRESHOLD=
GOVERNOR_QUORUM_FRACTION= GOVERNOR_QUORUM_FRACTION=
###################### Initial ghosted supply on gatekeeper ########################## ###################### Initial ghosted supply on gatekeeper ###########################
## ghostedSupply - supply that is currently locked inside the gatekeeper ##
## existential - minimum amount that could be reflected inside other chain ## ## existential - minimum amount that could be reflected inside other chain ##
## ghostedIn - historical supply that was bridged in (max value is 2^104) ## ## previousWeaver - previous weaver address if any to make linked list of weavers ##
## ghostedOut - historical supply that was bridged out (max value is 2^104) ## #######################################################################################
## deployedAt - time when gatekeepers was deployed, could be inherited if needed ##
######################################################################################
INITIAL_GHOSTED_SUPPLY=
INITIAL_EXISTENTIAL_DEPOSIT= INITIAL_EXISTENTIAL_DEPOSIT=
INITIAL_GHOSTED_IN= PREVIOUS_WEAVER_ADDRESS=
INITIAL_GHOSTED_OUT=
GATEKEEPER_DEPLOYED_AT=
SEPOLIA_TEST_RPC_URL= SEPOLIA_TEST_RPC_URL=
SEPOLIA_TEST_API_KEY= SEPOLIA_TEST_API_KEY=

View File

@ -10,23 +10,25 @@ contract Gatekeeper is IGatekeeper, Metadata, Weaver {
uint256 public override existentialDeposit; uint256 public override existentialDeposit;
uint256 public override ghostedSupply; uint256 public override ghostedSupply;
address public immutable staking; // forge-lint: disable-line(screaming-snake-case-immutable) address public override staking;
uint256 private _aggregatedPublicKey; uint256 private _aggregatedPublicKey;
mapping(uint256 => mapping(uint256 => bool)) private _executedTransaction; mapping(uint256 => mapping(uint256 => bool)) private _executedTransaction;
constructor( constructor(
address _staking, address _staking,
uint256 _ghostedSupply, uint256 _existentialDeposit,
uint256 _existential, address _previousGatekeeper
uint48 _deployedAt, ) Metadata(_previousGatekeeper) Weaver(_previousGatekeeper) {
uint104 _amountIn, existentialDeposit = _existentialDeposit;
uint104 _amountOut if (_previousGatekeeper != address(0)) {
) Metadata(_deployedAt, _amountIn, _amountOut) Weaver() { IGatekeeper previousGatekeeper = IGatekeeper(_previousGatekeeper);
existentialDeposit = _existential; ghostedSupply = previousGatekeeper.ghostedSupply();
ghostedSupply = _ghostedSupply; staking = previousGatekeeper.staking();
} else {
staking = _staking; staking = _staking;
} }
}
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();

View File

@ -13,6 +13,7 @@ interface IGatekeeper {
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 staking() external view returns (address);
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 materialize(address receiver, uint256 amount, uint256 rx, uint256 s) external;
function rotate(uint256 aggregatedPublicKey, uint256 rx, uint256 s) external; function rotate(uint256 aggregatedPublicKey, uint256 rx, uint256 s) external;

View File

@ -2,7 +2,9 @@
pragma solidity ^0.8.20; pragma solidity ^0.8.20;
interface IWeaver { interface IWeaver {
function getSlotValues(uint256 globalIndex, uint256 session, uint256 atBlock) external returns (bytes32[] memory); function currentSession() external view returns (uint256);
function getProof(uint256 globalIndex, uint256 session, uint256 atBlock) external returns (bytes32[] memory); function startSession() external view returns (uint256);
function getRoot(uint256 session, uint256 atBlock) external returns (bytes32, uint256); 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 getRoot(uint256 session, uint256 atBlock) external view returns (bytes32, uint256);
} }

View File

@ -4,7 +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 {
constructor() Weaver() {} constructor(address previousWeaver) Weaver(previousWeaver) {}
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);

View File

@ -6,16 +6,21 @@ import {IMetadata} from "../interfaces/IMetadata.sol";
abstract contract Metadata is IMetadata { abstract contract Metadata is IMetadata {
GatekeeperMetadata internal _metadata; GatekeeperMetadata internal _metadata;
constructor( constructor(address previosMetadata) {
uint48 _deployedAt, if (previosMetadata == address(0)) {
uint104 _amountIn,
uint104 _amountOut
) {
_metadata = GatekeeperMetadata({ _metadata = GatekeeperMetadata({
deployedAt: _deployedAt, deployedAt: uint48(block.number),
amountIn: _amountIn, amountIn: 0,
amountOut: _amountOut 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) { function metadata() external view returns (GatekeeperMetadata memory) {

View File

@ -14,20 +14,33 @@ 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 currentSession; uint256 public override currentSession;
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() {} constructor(address _previousWeaver) {
previousWeaver = _previousWeaver;
if (_previousWeaver != address(0)) {
uint256 newSession = IWeaver(_previousWeaver).currentSession() + 1;
currentSession = newSession;
startSession = newSession;
}
}
function getSlotValues( function getSlotValues(
uint256 globalIndex, uint256 globalIndex,
uint256 session, uint256 session,
uint256 atBlock uint256 atBlock
) external view returns (bytes32[] memory) { ) external override view returns (bytes32[] memory) {
if (session < startSession) {
return IWeaver(previousWeaver).getSlotValues(globalIndex, session, atBlock);
}
uint256 slotIndex = globalIndex % SLOTS; uint256 slotIndex = globalIndex % SLOTS;
// forge-lint: disable-next-line(unsafe-typecast) // forge-lint: disable-next-line(unsafe-typecast)
uint256 length = _slotLenghts[session][slotIndex].upperLookupRecent(uint96(atBlock)); uint256 length = _slotLenghts[session][slotIndex].upperLookupRecent(uint96(atBlock));
@ -42,7 +55,11 @@ abstract contract Weaver is IWeaver {
return values; return values;
} }
function getRoot(uint256 session, uint256 atBlock) public view returns (bytes32, uint256) { function getRoot(uint256 session, uint256 atBlock) public override view returns (bytes32, uint256) {
if (session < startSession) {
return IWeaver(previousWeaver).getRoot(session, atBlock);
}
uint256 currentLevelCount = SLOTS >> 1; uint256 currentLevelCount = SLOTS >> 1;
bytes32[] memory currentLevel = new bytes32[](currentLevelCount); bytes32[] memory currentLevel = new bytes32[](currentLevelCount);
@ -83,7 +100,11 @@ abstract contract Weaver is IWeaver {
uint256 globalIndex, uint256 globalIndex,
uint256 session, uint256 session,
uint256 atBlock uint256 atBlock
) external view returns (bytes32[] memory) { ) external override view returns (bytes32[] memory) {
if (session < startSession) {
return IWeaver(previousWeaver).getProof(globalIndex, session, atBlock);
}
uint256 currentLevelCount = SLOTS; uint256 currentLevelCount = SLOTS;
uint256 currentIndex = globalIndex % SLOTS; uint256 currentIndex = globalIndex % SLOTS;

View File

@ -324,7 +324,7 @@ 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, 0, 0, 0, 0); Gatekeeper gatekeeper = new Gatekeeper(address(staking), 0, address(0));
staking.setGatekeeperAddress(address(gatekeeper)); staking.setGatekeeperAddress(address(gatekeeper));
staking.setWarmupPeriod(1); staking.setWarmupPeriod(1);
vm.stopPrank(); vm.stopPrank();
@ -509,7 +509,7 @@ contract GhostBondDepositoryTest is Test {
} }
vm.startPrank(GOVERNOR); vm.startPrank(GOVERNOR);
Gatekeeper gatekeeper = new Gatekeeper(address(staking), 0, 0, 0, 0, 0); Gatekeeper gatekeeper = new Gatekeeper(address(staking), 0, address(0));
staking.setGatekeeperAddress(address(gatekeeper)); staking.setGatekeeperAddress(address(gatekeeper));
staking.setWarmupPeriod(10); staking.setWarmupPeriod(10);
vm.stopPrank(); vm.stopPrank();

View File

@ -14,18 +14,21 @@ 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 {
gatekeeper = new Gatekeeper(ALICE, 0, EXISTENTIAL, 0, 0, 0); gatekeeper = new Gatekeeper(ALICE, EXISTENTIAL, address(0));
} }
function test_correctInitialization() public { function test_correctInitialization() public {
assertEq(gatekeeper.staking(), ALICE); assertEq(gatekeeper.staking(), ALICE);
assertEq(gatekeeper.ghostedSupply(), 0); assertEq(gatekeeper.ghostedSupply(), 0);
Gatekeeper anotherGatekeeper = new Gatekeeper(BOB, INIT_AMOUNT, EXISTENTIAL, 0, 0, 0); bytes32 receiver = bytes32(abi.encodePacked(ALICE));
assertEq(anotherGatekeeper.staking(), BOB); vm.prank(ALICE);
gatekeeper.ghost(receiver, INIT_AMOUNT);
Gatekeeper anotherGatekeeper = new Gatekeeper(BOB, EXISTENTIAL, address(gatekeeper));
assertEq(anotherGatekeeper.staking(), ALICE);
assertEq(anotherGatekeeper.ghostedSupply(), INIT_AMOUNT); assertEq(anotherGatekeeper.ghostedSupply(), INIT_AMOUNT);
assertEq(anotherGatekeeper.existentialDeposit(), EXISTENTIAL); assertEq(anotherGatekeeper.existentialDeposit(), EXISTENTIAL);
assertEq(anotherGatekeeper.ghostedSupply(), INIT_AMOUNT);
} }
function test_ghostTokensWork(uint256 ghostAmount) public { function test_ghostTokensWork(uint256 ghostAmount) public {

View File

@ -9,22 +9,19 @@ contract GatekeeperMetadataTest is Test {
uint256 constant INIT_AMOUNT = 69 * 1e18; uint256 constant INIT_AMOUNT = 69 * 1e18;
uint256 constant INIT_GHOSTED = type(uint104).max / 2; uint256 constant INIT_GHOSTED = type(uint104).max / 2;
uint256 constant EXISTENTIAL = 0; uint256 constant EXISTENTIAL = 0;
uint48 constant DEPLOYED_AT = 1337;
uint104 constant AMOUNT_IN = 69;
uint104 constant AMOUNT_OUT = 420;
Gatekeeper gatekeeper; Gatekeeper gatekeeper;
function setUp() public { function setUp() public {
gatekeeper = new Gatekeeper(ALICE, INIT_GHOSTED, EXISTENTIAL, DEPLOYED_AT, AMOUNT_IN, AMOUNT_OUT); gatekeeper = new Gatekeeper(ALICE, EXISTENTIAL, address(0));
} }
function test_correctMetadataInitialization() public view { function test_correctMetadataInitialization() public view {
Gatekeeper.GatekeeperMetadata memory metadata = gatekeeper.metadata(); Gatekeeper.GatekeeperMetadata memory metadata = gatekeeper.metadata();
assertEq(metadata.deployedAt, DEPLOYED_AT); assertEq(metadata.deployedAt, block.number);
assertEq(metadata.amountIn, AMOUNT_IN); assertEq(metadata.amountIn, 0);
assertEq(metadata.amountOut, AMOUNT_OUT); assertEq(metadata.amountOut, 0);
assertEq(gatekeeper.ghostedSupply(), INIT_GHOSTED); assertEq(gatekeeper.ghostedSupply(), 0);
} }
function test_historicalAmountsOnlyIncrease(uint256 ghostAmount) public { function test_historicalAmountsOnlyIncrease(uint256 ghostAmount) public {

View File

@ -12,18 +12,12 @@ contract GatekeeperVerification is Gatekeeper {
constructor( constructor(
address staking, address staking,
uint256 ghostedSupply,
uint256 existential, uint256 existential,
uint48 deployedAt, address previousWeaver
uint104 amountIn,
uint104 amountOut
) Gatekeeper( ) Gatekeeper(
staking, staking,
ghostedSupply,
existential, existential,
deployedAt, previousWeaver
amountIn,
amountOut
) {} ) {}
function filledEntries(uint256 session) public view returns (uint256) { function filledEntries(uint256 session) public view returns (uint256) {
@ -102,7 +96,7 @@ contract GatekeeperWeaverTest is Test {
GatekeeperVerification gatekeeper; GatekeeperVerification gatekeeper;
function setUp() public { function setUp() public {
gatekeeper = new GatekeeperVerification(ALICE, 0, EXISTENTIAL, 0, 0, 0); gatekeeper = new GatekeeperVerification(ALICE, EXISTENTIAL, address(0));
} }
function test_insertationWorksAsExpected() public { function test_insertationWorksAsExpected() public {
@ -138,6 +132,35 @@ contract GatekeeperWeaverTest is Test {
assertFalse(_verifyProof(globalIndex, newSession, 100, amounts[69], whos[69])); assertFalse(_verifyProof(globalIndex, newSession, 100, amounts[69], whos[69]));
assertFalse(_verifyProof(1337, currentSession, 100, amounts[1337], whos[1337])); assertFalse(_verifyProof(1337, currentSession, 100, amounts[1337], whos[1337]));
assertFalse(_verifyProof(2047, currentSession, 100, amounts[2047], whos[2047])); assertFalse(_verifyProof(2047, currentSession, 100, amounts[2047], whos[2047]));
vm.roll(block.number + 420);
gatekeeper = new GatekeeperVerification(ALICE, EXISTENTIAL, address(gatekeeper));
uint256 finalSession = gatekeeper.currentSession();
for (uint256 i = 0; i < 69; i++) {
if (i % 2 == 0) { vm.roll(block.number + 1); }
vm.prank(ALICE);
gatekeeper.ghost(whos[i], amounts[i]);
}
assertTrue(_verifyProof(globalIndex, newSession, block.number, amounts[69], whos[69]));
assertTrue(_verifyProof(0, currentSession, block.number, amounts[0], whos[0]));
assertTrue(_verifyProof(69, currentSession, block.number, amounts[69], whos[69]));
assertTrue(_verifyProof(420, currentSession, block.number, amounts[420], whos[420]));
assertTrue(_verifyProof(1337, currentSession, block.number, amounts[1337], whos[1337]));
assertTrue(_verifyProof(2047, currentSession, block.number, amounts[2047], whos[2047]));
assertTrue(_verifyProof(0, currentSession, 100, amounts[0], whos[0]));
assertTrue(_verifyProof(69, currentSession, 100, amounts[69], whos[69]));
assertTrue(_verifyProof(420, currentSession, 100, amounts[420], whos[420]));
assertFalse(_verifyProof(globalIndex, newSession, 100, amounts[69], whos[69]));
assertFalse(_verifyProof(1337, currentSession, 100, amounts[1337], whos[1337]));
assertFalse(_verifyProof(2047, currentSession, 100, amounts[2047], whos[2047]));
assertTrue(_verifyProof(0, finalSession, block.number, amounts[0], whos[0]));
assertTrue(_verifyProof(34, finalSession, block.number, amounts[34], whos[34]));
assertTrue(_verifyProof(35, finalSession, block.number, amounts[35], whos[35]));
} }
function _verifyProof( function _verifyProof(

View File

@ -110,7 +110,7 @@ contract StakingTest is Test {
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, 0, 0, 0, 0); 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);