Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OCT-1730: Improvements for the epoch verifier #368

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion epoch-verifier/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,14 @@ Check usage with:
yarn verify:help
```

If you want to use the script before the end of the AW, you can use the `--simulated` option. This will use the current state of the BE and simulate the end of the AW.

Script has already defined several, most important deployments (`--deployment` option), but one can connect to any other octant backend using `--url` option.
e.g
```
yarn verify:mainnet <epoch> --simulated
```

Script has already defined several, most important deployments (`--deployment` option), but one can connect to any other octant backend using `--url` option.



37 changes: 27 additions & 10 deletions epoch-verifier/src/data/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@ import {
Reward,
UserBudget,
UserDonation,
EpochUqs
EpochUqs,
SimulatedReward
} from "./models"

export interface Context {
allocations: Allocation[]
budgets: Map<Address, bigint>
epochInfo: EpochInfo
rewards: Reward[]
uqs: Map<Address, Double>
allocations: Allocation[];
budgets: Map<Address, bigint>;
epochInfo: EpochInfo;
rewards: Reward[];
uqs: Map<Address, Double>;
isSimulated: boolean;
}

function transformToAllocations(allocationRecords: AllocationRecord[]): Allocation[] {
Expand All @@ -29,7 +31,7 @@ function transformToAllocations(allocationRecords: AllocationRecord[]): Allocati
return Array.from(allocations.values())
}

export function buildContext(userBudgets: UserBudget[], allocations: AllocationRecord[], rewards: Reward[], epochInfo: EpochInfo, epochUqs: EpochUqs[]): Context {
export function buildContext(userBudgets: UserBudget[], allocations: AllocationRecord[], rewards: Reward[], epochInfo: EpochInfo, epochUqs: EpochUqs[], isSimulated: boolean): Context {

const positiveUserBudgets = userBudgets.filter(positiveUserBudget => positiveUserBudget.amount !== BigInt(0));

Expand All @@ -38,7 +40,8 @@ export function buildContext(userBudgets: UserBudget[], allocations: AllocationR
budgets: new Map(positiveUserBudgets.map(value => [value.user, value.amount] as const)),
epochInfo,
rewards,
epochUqs: new Map(epochUqs.map(value => [value.userAddress, value.uniquenessQuotient] as const))
epochUqs: new Map(epochUqs.map(value => [value.userAddress, value.uniquenessQuotient] as const)),
isSimulated
}
}

Expand Down Expand Up @@ -81,9 +84,23 @@ export function individualDonationsAggregatedByProjectsWithUQs(context: Context)
return individualDonations;
}

export function rewardsByProject(context: Context): Map<Address, Reward> {
return new Map(context.rewards
function _getSimulatedRewardsByProject(rewards: SimulatedReward[]): Map<Address, SimulatedReward> {
return new Map(rewards
.filter(r => r.matched !== BigInt(0))
.map((r) => [r.address, r] as const)
);
}

function _getRewardsByProject(rewards: Reward[]): Map<Address, Reward> {
return new Map(rewards
.filter(r => r.matched !== BigInt(0))
.map((r) => [r.project, r] as const)
);
}

export function rewardsByProject(context: Context): Map<Address, IReward> {
if (context.isSimulated) {
return _getSimulatedRewardsByProject(context.rewards.projectsRewards);
}
return _getRewardsByProject(context.rewards);
}
15 changes: 10 additions & 5 deletions epoch-verifier/src/data/fetcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import {
UserBudget,
AllocationRecord,
ApiRewardsBudgets, ApiAllocations, ApiRewards,
ApiEpochUqs, EpochUqsImpl
ApiEpochUqs, EpochUqsImpl,
FinalizedSimulationImpl
} from "./models";

const REQUEST_TIMEOUT = 150_000;
Expand Down Expand Up @@ -60,15 +61,19 @@ export class HttpFetcher {
return this._get_array(`/allocations/epoch/${epoch}?includeZeroAllocations=true`, "users' allocations", AllocationImpl, (data: ApiAllocations) => data.allocations)
}

async apiGetRewards(epoch: number): Promise<Reward[] | null> {
return this._get_array(`/rewards/projects/epoch/${epoch}`, "projects rewards", RewardImpl, (data: ApiRewards) => data.rewards)
}

async apiGetEpochInfo(epoch: number): Promise<EpochInfo | null> {
return this._get(`/epochs/info/${epoch}`, "epoch info", EpochInfoImpl)
}

async apiGetEpochUqs(epoch: number): Promise<EpochUqs | null> {
return this._get_array(`/user/uq/${epoch}/all`, "epoch uqs", EpochUqsImpl, (data: ApiEpochUqs) => data.uqsInfo);
}

async apiGetRewards(epoch: number): Promise<Reward[] | null> {
return this._get_array(`/rewards/projects/epoch/${epoch}`, "projects rewards", RewardImpl, (data: ApiRewards) => data.rewards)
}

async apiGetFinalizedSimulated() {
return this._get('/snapshots/finalized/simulate', 'finalized simulated snapshot', FinalizedSimulationImpl);
}
}
49 changes: 47 additions & 2 deletions epoch-verifier/src/data/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,20 @@ export interface Allocation {
user: Address;
}

export interface Reward {
allocated: bigint;
interface IReward {
matched: bigint;
}

export interface Reward extends IReward {
allocated: bigint;
project: Address;
}

export interface SimulatedReward extends IReward {
amount: bigint; // allocated + matched
address: Address;
}

export interface EpochInfo {
communityFund: bigint;
individualRewards: bigint;
Expand All @@ -83,6 +91,12 @@ export interface EpochUqs {
uniquenessQuotient: number;
}

export interface FinalizedSimulation {
patronsRewards: bigint;
matchedRewards: bigint;
projectRewards: SimulatedReward[];
}

export class UserBudgetImpl implements Deserializable<UserBudget> {
user: Address;

Expand Down Expand Up @@ -128,6 +142,20 @@ export class RewardImpl implements Deserializable<Reward> {
}
}

export class SimulatedRewardImpl implements Deserializable<SimulatedReward> {
amount: bigint;
matched: bigint;
address: Address;

from(input: any) {
this.address = input.address;
this.amount = BigInt(input.amount);
this.matched = BigInt(input.matched);

return this
}
}

export class EpochInfoImpl implements Deserializable<EpochInfo> {
individualRewards: bigint;

Expand Down Expand Up @@ -180,3 +208,20 @@ export class EpochUqsImpl implements Deserializable<EpochUqs>{
}
}

export class FinalizedSimulationImpl implements Deserializable<FinalizedSimulation>{
patronsRewards: bigint;
matchedRewards: bigint;
projectsRewards: SimulatedReward[];

from(input: any) {
aziolek marked this conversation as resolved.
Show resolved Hide resolved
this.patronsRewards = BigInt(input.patronsRewards);
this.matchedRewards = BigInt(input.matchedRewards);
this.projectsRewards = input.projectsRewards.map((reward: any) => {
aziolek marked this conversation as resolved.
Show resolved Hide resolved
const simulatedReward = new SimulatedRewardImpl();
return simulatedReward.from(reward);
});

return this;
}
}

26 changes: 20 additions & 6 deletions epoch-verifier/src/verifications/donations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ function _groupAllocationsByProjects(aggregatedDonations: Map<Address, UserDonat

aggregatedDonations.forEach((values, project) => {
const sumOfSqrt = values.reduce((sum, value) => sum + sqrt(multiplyFloatByBigInt(value.uq, value.amount)), BigInt(0));
const resultValue = sumOfSqrt ** BigInt(2);

let resultValue = sumOfSqrt ** BigInt(2);
totalPlainQF += resultValue;

result.set(project, resultValue);
Expand Down Expand Up @@ -74,22 +73,29 @@ function _getUserAllocationsForProjects(context: Context): Map<Address, bigint>
export function verifyUserDonationsVsRewards(context: Context): VerificationResult {
const projectsAllocations = Array.from(_getUserAllocationsForProjects(context).entries());
const rewards = rewardsByProject(context);

if (context.isSimulated) {
return assertAll(projectsAllocations, ([proposal, allocated]) => assertEq(allocated, rewards.get(proposal)!.amount - rewards.get(proposal)!.matched, BigInt(100), true));
}

return assertAll(projectsAllocations, ([proposal, allocated]) => assertEq(allocated, rewards.get(proposal)!.allocated, BigInt(100), true));
return assertEq(10, 10, BigInt(100), true);
}

export function verifyRewardsVsUserDonations(context: Context): VerificationResult {
const projectsAllocations = _getUserAllocationsForProjects(context);
const rewards = Array.from(rewardsByProject(context).entries());
return assertAll(rewards, ([project, reward]: [Address, Reward]) => assertEq(reward.allocated, projectsAllocations.get(project)!, BigInt(100), true));
return assertEq(10, 10, BigInt(100), true);

if (context.isSimulated) {
return assertAll(rewards, ([project, reward]: [Address, Reward]) => assertEq(reward.amount - reward.matched, projectsAllocations.get(project)!, BigInt(100), true));
}

return assertAll(rewards, ([project, reward]: [Address, Reward]) => assertEq(reward.allocated, projectsAllocations.get(project)!, BigInt(100), true));
}

export function verifyMatchedFunds(context: Context): VerificationResult {
const rewards = rewardsByProject(context)
const totalMatchedFunding = context.epochInfo.matchedRewards;
const aggregatedDonations = individualDonationsAggregatedByProjectsWithUQs(context)
const aggregatedDonations = individualDonationsAggregatedByProjectsWithUQs(context);
const [aggregatedCappedMatchedFunding, leftover] = _computeMatchingFundQFAndCapAndUQ(aggregatedDonations, totalMatchedFunding);

return assertAll(aggregatedCappedMatchedFunding, ([project, matched]) => {
Expand All @@ -116,6 +122,14 @@ export function verifyTotalWithdrawals(context: Context): VerificationResult {
.map(([user, donationsSum]) => context.budgets.get(user)! - donationsSum)
.reduce((acc, user_claimed) => acc + user_claimed, BigInt(0))

if (context.isSimulated) {
const rewards = context.rewards.projectsRewards
.filter((reward) => reward.matched !== BigInt(0))
.reduce((acc, reward) => acc + reward.amount, BigInt(0))

return assertEq(claimed + rewards, context.epochInfo.totalWithdrawals)
}

const rewards = context.rewards
.filter((reward) => reward.matched !== BigInt(0))
.reduce((acc, reward) => acc + reward.allocated + reward.matched, BigInt(0))
Expand Down
2 changes: 1 addition & 1 deletion epoch-verifier/src/verifications/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { VerificationResult } from "../runner";

type Predicate<T> = (elem: T) => boolean | VerificationResult

export const SCALE_FACTOR = 10n ** 18n;
export const SCALE_FACTOR = 10n ** 36n;
aziolek marked this conversation as resolved.
Show resolved Hide resolved

function abs(v: bigint): bigint {
if (v < BigInt(0)) {
Expand Down
37 changes: 26 additions & 11 deletions epoch-verifier/src/verifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import { registerVerifications } from "./verifications";


interface Options {
deployment?: string
epoch: number
url?: string
deployment?: string;
epoch: number;
url?: string;
simulated?: boolean;
}

const DEPLOYMENTS: { [key: string]: string } = {
Expand Down Expand Up @@ -39,20 +40,33 @@ function getDeploymentUrl(options: Options): string {
}

async function run(epoch: string, opts: any) {
const options: Options = { epoch: parseInt(epoch, 10), ...opts }
const options: Options = { epoch: parseInt(epoch, 10), simulated: opts.simulated, ...opts }
const baseUrl = getDeploymentUrl(options)

console.log(`Using url: ${baseUrl}`)
console.log(`Running verification scripts for epoch: ${options.epoch}`)

if (options.simulated) {
console.log(`Running verification scripts for epoch: ${options.epoch} in simulated mode.`)
}
else {
console.log(`Running verification scripts for epoch: ${options.epoch}.`)
}

const fetcher = new HttpFetcher(baseUrl)
const results = await Promise.all([
const fetchPromises = [
fetcher.apiGetUserBudgets(options.epoch),
fetcher.apiGetAllocations(options.epoch),
fetcher.apiGetRewards(options.epoch),
fetcher.apiGetEpochInfo(options.epoch),
fetcher.apiGetEpochUqs(options.epoch)
])
];

if (options.simulated) {
fetchPromises.push(fetcher.apiGetFinalizedSimulated());
} else {
fetchPromises.push(fetcher.apiGetRewards(options.epoch));
}

const results = await Promise.all(fetchPromises);

if (results.some(isNull)) {
process.exit(1)
Expand All @@ -61,11 +75,11 @@ async function run(epoch: string, opts: any) {
const [
userBudgets,
allocations,
rewards,
epochInfo,
epochUqs
epochUqs,
rewards
] = results;
const context = buildContext(userBudgets!, allocations!, rewards!, epochInfo!, epochUqs!)
const context = buildContext(userBudgets!, allocations!, rewards!, epochInfo!, epochUqs!, options.simulated);

const runner = new Runner()
registerVerifications(runner)
Expand All @@ -81,6 +95,7 @@ program
.description("Epoch verifier script.")
.addOption(new Option("--deployment <deployment>", "specify deployment to connect to").choices(Object.keys(DEPLOYMENTS)))
.option("--url <url>", "custom deployment url. Do not use with --deployment option")
.option("--simulated", "run the script in simulated mode")
.argument("<epoch>", "Epoch number for which the verification should be done.")
.action(run)
.parse(process.argv);