Skip to content

Commit

Permalink
add test
Browse files Browse the repository at this point in the history
  • Loading branch information
andresaiello committed Sep 25, 2024
1 parent 0247c3b commit 8ef87b0
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 21 deletions.
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "hardhat/console.sol";

contract ProofOfLiveness {
uint256 constant PROOF_PERIOD = 24 hours;
uint256 constant LAST_PERIODS_LENGTH = 5;

// Mapping to track the last time the user proved liveness
mapping(address => uint256) public lastProofTimestamp;
// Mapping to track the proof history for each user (last 5 proof timestamps)
mapping(address => uint256[LAST_PERIODS_LENGTH]) public proofHistory;

// Custom error for when a user has already proved liveness within the last PROOF_PERIOD
error ProofWithinLast24Hours(uint256 lastProofTime);
Expand All @@ -17,47 +19,63 @@ contract ProofOfLiveness {
// The function to prove liveness, can only be called once every PROOF_PERIOD
function proveLiveness() external {
uint256 currentTime = block.timestamp;
uint256 lastProofTime = lastProofTimestamp[msg.sender];
uint256 lastProofTime = proofHistory[msg.sender][0]; // The most recent proof timestamp is always stored in the first position

// Check if the user has proved liveness within the last PROOF_PERIOD
if (currentTime < lastProofTime + PROOF_PERIOD) {
revert ProofWithinLast24Hours(lastProofTime);
}

// Update the last proof timestamp for the user
lastProofTimestamp[msg.sender] = currentTime;
// Shift the proof history and add the new timestamp
_updateProofHistory(msg.sender, currentTime);

// Emit an event to track the liveness proof
emit LivenessProved(msg.sender, currentTime);
}

Check notice

Code scanning / Slither

Block timestamp Low

ProofOfLiveness.proveLiveness() uses timestamp for comparisons
Dangerous comparisons:
- currentTime < lastProofTime + PROOF_PERIOD

// Helper function to check if a user can prove liveness (returns true if PROOF_PERIOD have passed)
// Helper function to check if a user can prove liveness (returns true if PROOF_PERIOD has passed)
function canProveLiveness(address user) external view returns (bool) {
uint256 currentTime = block.timestamp;
return currentTime >= lastProofTimestamp[user] + PROOF_PERIOD;
return currentTime >= proofHistory[user][0] + PROOF_PERIOD;
}

// View function to return the liveness proof status for the last LAST_PERIODS_LENGTH periods (each PROOF_PERIOD long)
function getLastPeriodsStatus(address user) external view returns (bool[LAST_PERIODS_LENGTH] memory) {
uint256 currentTime = block.timestamp;
uint256 lastProofTime = lastProofTimestamp[user];

bool[LAST_PERIODS_LENGTH] memory proofStatus;
uint256 periodDuration = PROOF_PERIOD;

for (uint256 i = 0; i < LAST_PERIODS_LENGTH; i++) {
// Calculate the start of the period (going back i * PROOF_PERIOD)
uint256 periodStart = currentTime - (i * periodDuration);
uint256 periodEnd = periodStart - periodDuration;

// If the last proof timestamp falls within this period, mark it as true
if (lastProofTime >= periodEnd && lastProofTime < periodStart) {
proofStatus[i] = true;
} else {
proofStatus[i] = false;
}
// Calculate the end of the period (going back i * PROOF_PERIOD)
uint256 periodEnd = currentTime - (i * PROOF_PERIOD);
uint256 periodStart = periodEnd - PROOF_PERIOD - 1;
// If the proof timestamp falls within this period, mark it as true
proofStatus[i] = hasProofedAt(user, periodStart, periodEnd);
}

return proofStatus;
}

function hasProofedAt(address user, uint256 periodStart, uint256 periodEnd) public view returns (bool) {
for (uint256 i = 0; i < LAST_PERIODS_LENGTH; i++) {
if (proofHistory[user][i] >= periodStart && proofHistory[user][i] < periodEnd) {
return true;
}
}
return false;
}

Check notice

Code scanning / Slither

Block timestamp Low


function getProofHistory(address user) external view returns (uint256[LAST_PERIODS_LENGTH] memory) {
return proofHistory[user];
}

// Internal function to update the user's proof history by shifting timestamps and adding the new proof
function _updateProofHistory(address user, uint256 newProofTimestamp) internal {
// Shift the history to the right
for (uint256 i = LAST_PERIODS_LENGTH - 1; i > 0; i--) {
proofHistory[user][i] = proofHistory[user][i - 1];
}

// Add the new timestamp in the first position
proofHistory[user][0] = newProofTimestamp;
}
}
2 changes: 1 addition & 1 deletion packages/zevm-app-contracts/data/addresses.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"withdrawERC20": "0xa349B9367cc54b47CAb8D09A95836AE8b4D1d84E",
"ZetaXP": "0x5c25b6f4D2b7a550a80561d3Bf274C953aC8be7d",
"InstantRewards": "0x10DfEd4ba9b8F6a1c998E829FfC0325D533c80E3",
"ProofOfLiveness": "0x2aD64D83827D102FCBBdEb92DEA91fCAB168Cdc2"
"ProofOfLiveness": "0x981EB6fD19717Faf293Fba0cBD05C6Ac97b8C808"
},
"zeta_mainnet": {
"disperse": "0x23ce409Ea60c3d75827d04D9db3d52F3af62e44d",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
import { expect } from "chai";
import { BigNumber, utils } from "ethers";
import { parseEther } from "ethers/lib/utils";
import { ethers } from "hardhat";

import { ProofOfLiveness } from "../../typechain-types";

const HARDHAT_CHAIN_ID = 1337;

describe("Proof Of Liveness Contract test", () => {
let proofOfLiveness: ProofOfLiveness,
owner: SignerWithAddress,
signer: SignerWithAddress,
user: SignerWithAddress,
addrs: SignerWithAddress[];

beforeEach(async () => {
[owner, signer, user, ...addrs] = await ethers.getSigners();
const ProofOfLivenessFactory = await ethers.getContractFactory("ProofOfLiveness");

proofOfLiveness = await ProofOfLivenessFactory.deploy();

await proofOfLiveness.deployed();
});

it("Should proof", async () => {
const tx = await proofOfLiveness.proveLiveness();

const receipt = await tx.wait();
const blockTimestamp = (await ethers.provider.getBlock(receipt.blockNumber)).timestamp;

await expect(tx).to.emit(proofOfLiveness, "LivenessProved").withArgs(owner.address, blockTimestamp);
});

it("Should proof 5 times every 24 hours and return correct view values", async () => {
const PROOF_PERIOD = 24 * 60 * 60; // 24 hours in seconds

// Prove liveness 5 times
for (let i = 0; i < 5; i++) {
// Call the proveLiveness function
const tx = await proofOfLiveness.proveLiveness();
await tx.wait();

// Increase the time by 24 hours in the EVM
await ethers.provider.send("evm_increaseTime", [PROOF_PERIOD]);
await ethers.provider.send("evm_mine", []); // Mine a new block to apply the time change
}

// Now check the getLastPeriodsStatus for the owner
const periodsStatus = await proofOfLiveness.getLastPeriodsStatus(owner.address);

// We expect that all 5 periods should return true
expect(periodsStatus).to.deep.equal([true, true, true, true, true]);
});

it("Should proof 5 times every 24 hours and return correct view values if one day is missing", async () => {
const PROOF_PERIOD = 24 * 60 * 60; // 24 hours in seconds

// Prove liveness 5 times
for (let i = 0; i < 5; i++) {
// Call the proveLiveness function if day is not 3
if (i !== 3) {
const tx = await proofOfLiveness.proveLiveness();
await tx.wait();
}

// Increase the time by 24 hours in the EVM
await ethers.provider.send("evm_increaseTime", [PROOF_PERIOD]);
await ethers.provider.send("evm_mine", []); // Mine a new block to apply the time change
}

// Now check the getLastPeriodsStatus for the owner
const periodsStatus = await proofOfLiveness.getLastPeriodsStatus(owner.address);

// We expect that all 5 periods should return true but 3
expect(periodsStatus).to.deep.equal([true, false, true, true, true]);
});

it("Should proof view return if only one day was proof", async () => {
const tx = await proofOfLiveness.proveLiveness();
await tx.wait();
await ethers.provider.send("evm_mine", []); // Mine a new block to apply the time change

// Now check the getLastPeriodsStatus for the owner
const periodsStatus = await proofOfLiveness.getLastPeriodsStatus(owner.address);

expect(periodsStatus).to.deep.equal([true, false, false, false, false]);
});

it("Should revert if trying to prove twice in less than 24 hours", async () => {
// Prove liveness for the first time
await proofOfLiveness.proveLiveness();

const tx = proofOfLiveness.proveLiveness();

await expect(tx).to.be.revertedWith("ProofWithinLast24Hours");
});
});

0 comments on commit 8ef87b0

Please sign in to comment.