Skip to content

Commit

Permalink
Add the code for claims and proof generation for the merkle trees
Browse files Browse the repository at this point in the history
  • Loading branch information
motechFR committed Oct 31, 2024
1 parent ee9f7d2 commit 896ed59
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 22 deletions.
51 changes: 37 additions & 14 deletions packages/scoutgame/src/claims/__tests__/verifyClaim.spec.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,66 @@
import { generateTree, getProofs, verifyClaim, type ProvableClaim } from '../root';
import { generateMerkleTree, getMerkleProofs, verifyMerkleClaim, type ProvableClaim } from '../merkleTree';

const claimsInput: ProvableClaim[] = [
{
address: '0x36446eF671954753801f9d73C415a80C0e550b32',
amount: '100'
amount: 100
},
{
address: '0xC82ee528AC8BFd7087e0DE6548955601dFcac99d',
amount: '200'
amount: 200
},
{
address: '0xB20C9b7e6b9cbcDed9819F88D68938D0B149887f',
amount: '300'
amount: 300
},
{
address: '0x36446eF671954753801f9d73C415a80C0e550b32',
amount: '400'
amount: 400
},
{
address: '0xD02953857250D32EC72064d9E2320B43296E52C0',
amount: '500'
amount: 500
}
];

describe('verifyClaim', () => {
describe('verifyMerkleClaim', () => {
it('should return true if the claim is valid', () => {
const tree = generateTree(claimsInput);
const { tree } = generateMerkleTree(claimsInput);
const claim = claimsInput[0];
const proofs = getProofs(tree, claim);
expect(verifyClaim(tree, claim, proofs)).toBe(true);
const proofs = getMerkleProofs(tree, claim);
expect(verifyMerkleClaim(tree, claim, proofs)).toBe(true);
});

it('should return false if the claim is invalid', () => {
const tree = generateTree(claimsInput);
const { tree } = generateMerkleTree(claimsInput);
const claim: ProvableClaim = {
address: '0x36446eF671954753801f9d73C415a80C0e550b32',
amount: '200'
amount: 200
};
const proof = getProofs(tree, claim);
expect(verifyClaim(tree, claim, proof)).toBe(false);
const proof = getMerkleProofs(tree, claim);
expect(verifyMerkleClaim(tree, claim, proof)).toBe(false);
});

it('should sort inputs so that it is not reliant on ordering of the claims', () => {
function shuffleArray<T>(array: T[]): T[] {
const newArray = [...array]; // Create a copy of the array to avoid mutating the original
for (let i = newArray.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[newArray[i], newArray[j]] = [newArray[j], newArray[i]]; // Swap elements
}
return newArray;
}

const shuffledOne = shuffleArray(claimsInput);

const shuffledTwo = shuffleArray(claimsInput);

// Make sure sorting worked
expect(JSON.stringify(shuffledOne)).not.toEqual(JSON.stringify(shuffledTwo));

const { rootHash: rootHashOne } = generateMerkleTree(shuffledOne);
const { rootHash: rootHashTwo } = generateMerkleTree(shuffledTwo);

expect(rootHashOne).toEqual(rootHashTwo);
});
});
50 changes: 46 additions & 4 deletions packages/scoutgame/src/claims/generateWeeklyClaims.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
import type { WeeklyClaims } from '@charmverse/core/prisma-client';
import { prisma } from '@charmverse/core/prisma-client';
import type { Address } from 'viem';

import { currentSeason } from '../dates';
import { dividePointsBetweenBuilderAndScouts } from '../points/dividePointsBetweenBuilderAndScouts';
import { getWeeklyPointsPoolAndBuilders } from '../points/getWeeklyPointsPoolAndBuilders';

import type { ProvableClaim } from './root';
import { generateMerkleTree, type ProvableClaim } from './merkleTree';

export async function generateWeeklyClaims({ week }: { week: string }): Promise<ProvableClaim[]> {
const { normalisationFactor, topWeeklyBuilders, totalPoints, weeklyAllocatedPoints } =
await getWeeklyPointsPoolAndBuilders({ week });
type ClaimsBody = {
leaves: ProvableClaim[];
};

type WeeklyClaimsTyped = Omit<WeeklyClaims, 'claims'> & {
claims: ClaimsBody;
};

export async function calculateWeeklyClaims({ week }: { week: string }): Promise<ProvableClaim[]> {
const { normalisationFactor, topWeeklyBuilders, weeklyAllocatedPoints } = await getWeeklyPointsPoolAndBuilders({
week
});

const allClaims = await Promise.all(
topWeeklyBuilders.map(async (builder) => {
Expand Down Expand Up @@ -84,3 +94,35 @@ export async function generateWeeklyClaims({ week }: { week: string }): Promise<

return claimsByAddress;
}

export async function generateWeeklyClaims({ week }: { week: string }): Promise<WeeklyClaimsTyped> {
const existingClaim = await prisma.weeklyClaims.findUnique({
where: {
week
}
});

if (existingClaim) {
throw new Error(`Claims for week ${week} already exist`);
}

const claims = await calculateWeeklyClaims({ week });

const { rootHash } = generateMerkleTree(claims);

const claimsBody: ClaimsBody = {
leaves: claims
};

const weeklyClaim = await prisma.weeklyClaims.create({
data: {
week,
merkleTreeRoot: rootHash,
season: currentSeason,
totalClaimable: claims.reduce((acc, claim) => acc + claim.amount, 0),
claims: claimsBody
}
});

return weeklyClaim as WeeklyClaimsTyped;
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,22 @@ function claimToString(claim: ProvableClaim): string {
return `${claim.address}:${claim.amount}`;
}

export function generateTree(claims: ProvableClaim[]): MerkleTree {
return new MerkleTree(claims.map(claimToString), SHA256);
export function generateMerkleTree(claims: ProvableClaim[]): { tree: MerkleTree; rootHash: string } {
const tree = new MerkleTree(claims.map(claimToString), SHA256, {
sort: true
});

return {
tree,
rootHash: tree.getRoot().toString('hex')
};
}

export function getProofs(tree: MerkleTree, claim: ProvableClaim): any {
export function getMerkleProofs(tree: MerkleTree, claim: ProvableClaim): any {
return tree.getProof(claimToString(claim));
}

export function verifyClaim(tree: MerkleTree, claim: ProvableClaim, proof: string[]): boolean {
export function verifyMerkleClaim(tree: MerkleTree, claim: ProvableClaim, proof: string[]): boolean {
const root = tree.getRoot().toString('hex');
return tree.verify(proof, claimToString(claim), root);
}

0 comments on commit 896ed59

Please sign in to comment.