mirror of
https://github.com/logos-messaging/js-waku.git
synced 2026-01-02 13:53:12 +00:00
feat: refine work with membership info and other meta information (#2341)
* chore: update definitions to match the ABI * chore: improve exports and types * chore: use big endian instead of little endian * chore: improve logging * chore: update import * fix: use overloaded function call * chore: estimate gas * test * npm i * chore: regen locl * chore: update * chore: update playwright docker images * chore: cannot erase membership if it isn't expired/in grace period * chore: fix condition * chore: add `getMembershipStatus` method * refactor(rln): cache min/max rate limits at contract instantiation * chore: fix accessor * chore: rename arg * chore: allow chromebin path through env
This commit is contained in:
parent
a4dfd3455c
commit
3b23bceb9d
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@ -57,7 +57,7 @@ jobs:
|
|||||||
browser:
|
browser:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
container:
|
||||||
image: mcr.microsoft.com/playwright:v1.51.1-jammy
|
image: mcr.microsoft.com/playwright:v1.52.0-jammy
|
||||||
env:
|
env:
|
||||||
HOME: "/root"
|
HOME: "/root"
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
2
.github/workflows/playwright.yml
vendored
2
.github/workflows/playwright.yml
vendored
@ -20,7 +20,7 @@ jobs:
|
|||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
container:
|
||||||
image: mcr.microsoft.com/playwright:v1.51.1-jammy
|
image: mcr.microsoft.com/playwright:v1.52.0-jammy
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v3
|
||||||
|
|||||||
@ -1,7 +1,11 @@
|
|||||||
|
/* eslint-env node */
|
||||||
const playwright = require("playwright");
|
const playwright = require("playwright");
|
||||||
const webpack = require("webpack");
|
const webpack = require("webpack");
|
||||||
|
|
||||||
process.env.CHROME_BIN = playwright.chromium.executablePath();
|
if (!process.env.CHROME_BIN) {
|
||||||
|
process.env.CHROME_BIN = playwright.chromium.executablePath();
|
||||||
|
}
|
||||||
|
console.log("Using CHROME_BIN:", process.env.CHROME_BIN);
|
||||||
process.env.FIREFOX_BIN = playwright.firefox.executablePath();
|
process.env.FIREFOX_BIN = playwright.firefox.executablePath();
|
||||||
|
|
||||||
module.exports = function (config) {
|
module.exports = function (config) {
|
||||||
|
|||||||
5200
package-lock.json
generated
5200
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,2 +1,3 @@
|
|||||||
export { RLNContract } from "./rln_contract.js";
|
export { RLNContract } from "./rln_contract.js";
|
||||||
export * from "./constants.js";
|
export * from "./constants.js";
|
||||||
|
export * from "./types.js";
|
||||||
|
|||||||
@ -22,6 +22,8 @@ export class RLNBaseContract {
|
|||||||
public contract: ethers.Contract;
|
public contract: ethers.Contract;
|
||||||
private deployBlock: undefined | number;
|
private deployBlock: undefined | number;
|
||||||
private rateLimit: number;
|
private rateLimit: number;
|
||||||
|
private minRateLimit?: number;
|
||||||
|
private maxRateLimit?: number;
|
||||||
|
|
||||||
protected _members: Map<number, Member> = new Map();
|
protected _members: Map<number, Member> = new Map();
|
||||||
private _membersFilter: ethers.EventFilter;
|
private _membersFilter: ethers.EventFilter;
|
||||||
@ -29,10 +31,35 @@ export class RLNBaseContract {
|
|||||||
private _membersExpiredFilter: ethers.EventFilter;
|
private _membersExpiredFilter: ethers.EventFilter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for RLNBaseContract.
|
* Private constructor for RLNBaseContract. Use static create() instead.
|
||||||
* Allows injecting a mocked contract for testing purposes.
|
|
||||||
*/
|
*/
|
||||||
public constructor(options: RLNContractInitOptions) {
|
protected constructor(options: RLNContractInitOptions) {
|
||||||
|
const {
|
||||||
|
address,
|
||||||
|
signer,
|
||||||
|
rateLimit = DEFAULT_RATE_LIMIT,
|
||||||
|
contract
|
||||||
|
} = options;
|
||||||
|
|
||||||
|
log.info("Initializing RLNBaseContract", { address, rateLimit });
|
||||||
|
|
||||||
|
this.contract = contract || new ethers.Contract(address, RLN_ABI, signer);
|
||||||
|
this.rateLimit = rateLimit;
|
||||||
|
|
||||||
|
try {
|
||||||
|
log.info("Setting up event filters");
|
||||||
|
// Initialize event filters
|
||||||
|
this._membersFilter = this.contract.filters.MembershipRegistered();
|
||||||
|
this._membershipErasedFilter = this.contract.filters.MembershipErased();
|
||||||
|
this._membersExpiredFilter = this.contract.filters.MembershipExpired();
|
||||||
|
log.info("Event filters initialized successfully");
|
||||||
|
} catch (error) {
|
||||||
|
log.error("Failed to initialize event filters", { error });
|
||||||
|
throw new Error(
|
||||||
|
"Failed to initialize event filters: " + (error as Error).message
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize members and subscriptions
|
// Initialize members and subscriptions
|
||||||
this.fetchMembers()
|
this.fetchMembers()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
@ -41,23 +68,24 @@ export class RLNBaseContract {
|
|||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
log.error("Failed to initialize members", { error });
|
log.error("Failed to initialize members", { error });
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const {
|
/**
|
||||||
address,
|
* Static async factory to create and initialize RLNBaseContract
|
||||||
signer,
|
*/
|
||||||
rateLimit = DEFAULT_RATE_LIMIT,
|
public static async create(
|
||||||
contract
|
options: RLNContractInitOptions
|
||||||
} = options;
|
): Promise<RLNBaseContract> {
|
||||||
|
const instance = new RLNBaseContract(options);
|
||||||
|
const [min, max] = await Promise.all([
|
||||||
|
instance.contract.minMembershipRateLimit(),
|
||||||
|
instance.contract.maxMembershipRateLimit()
|
||||||
|
]);
|
||||||
|
instance.minRateLimit = ethers.BigNumber.from(min).toNumber();
|
||||||
|
instance.maxRateLimit = ethers.BigNumber.from(max).toNumber();
|
||||||
|
|
||||||
this.validateRateLimit(rateLimit);
|
instance.validateRateLimit(instance.rateLimit);
|
||||||
|
return instance;
|
||||||
this.contract = contract || new ethers.Contract(address, RLN_ABI, signer);
|
|
||||||
this.rateLimit = rateLimit;
|
|
||||||
|
|
||||||
// Initialize event filters
|
|
||||||
this._membersFilter = this.contract.filters.MembershipRegistered();
|
|
||||||
this._membershipErasedFilter = this.contract.filters.MembershipErased();
|
|
||||||
this._membersExpiredFilter = this.contract.filters.MembershipExpired();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -82,21 +110,21 @@ export class RLNBaseContract {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the minimum allowed rate limit from the contract
|
* Gets the minimum allowed rate limit (cached)
|
||||||
* @returns Promise<number> The minimum rate limit in messages per epoch
|
|
||||||
*/
|
*/
|
||||||
public async getMinRateLimit(): Promise<number> {
|
public getMinRateLimit(): number {
|
||||||
const minRate = await this.contract.minMembershipRateLimit();
|
if (this.minRateLimit === undefined)
|
||||||
return ethers.BigNumber.from(minRate).toNumber();
|
throw new Error("minRateLimit not initialized");
|
||||||
|
return this.minRateLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the maximum allowed rate limit from the contract
|
* Gets the maximum allowed rate limit (cached)
|
||||||
* @returns Promise<number> The maximum rate limit in messages per epoch
|
|
||||||
*/
|
*/
|
||||||
public async getMaxRateLimit(): Promise<number> {
|
public getMaxRateLimit(): number {
|
||||||
const maxRate = await this.contract.maxMembershipRateLimit();
|
if (this.maxRateLimit === undefined)
|
||||||
return ethers.BigNumber.from(maxRate).toNumber();
|
throw new Error("maxRateLimit not initialized");
|
||||||
|
return this.maxRateLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -133,7 +161,7 @@ export class RLNBaseContract {
|
|||||||
* Updates the rate limit for future registrations
|
* Updates the rate limit for future registrations
|
||||||
* @param newRateLimit The new rate limit to use
|
* @param newRateLimit The new rate limit to use
|
||||||
*/
|
*/
|
||||||
public async setRateLimit(newRateLimit: number): Promise<void> {
|
public setRateLimit(newRateLimit: number): void {
|
||||||
this.validateRateLimit(newRateLimit);
|
this.validateRateLimit(newRateLimit);
|
||||||
this.rateLimit = newRateLimit;
|
this.rateLimit = newRateLimit;
|
||||||
}
|
}
|
||||||
@ -324,7 +352,7 @@ export class RLNBaseContract {
|
|||||||
this.contract.on(
|
this.contract.on(
|
||||||
this.membersFilter,
|
this.membersFilter,
|
||||||
(
|
(
|
||||||
_idCommitment: string,
|
_idCommitment: bigint,
|
||||||
_membershipRateLimit: ethers.BigNumber,
|
_membershipRateLimit: ethers.BigNumber,
|
||||||
_index: ethers.BigNumber,
|
_index: ethers.BigNumber,
|
||||||
event: ethers.Event
|
event: ethers.Event
|
||||||
@ -336,7 +364,7 @@ export class RLNBaseContract {
|
|||||||
this.contract.on(
|
this.contract.on(
|
||||||
this.membershipErasedFilter,
|
this.membershipErasedFilter,
|
||||||
(
|
(
|
||||||
_idCommitment: string,
|
_idCommitment: bigint,
|
||||||
_membershipRateLimit: ethers.BigNumber,
|
_membershipRateLimit: ethers.BigNumber,
|
||||||
_index: ethers.BigNumber,
|
_index: ethers.BigNumber,
|
||||||
event: ethers.Event
|
event: ethers.Event
|
||||||
@ -348,7 +376,7 @@ export class RLNBaseContract {
|
|||||||
this.contract.on(
|
this.contract.on(
|
||||||
this.membersExpiredFilter,
|
this.membersExpiredFilter,
|
||||||
(
|
(
|
||||||
_idCommitment: string,
|
_idCommitment: bigint,
|
||||||
_membershipRateLimit: ethers.BigNumber,
|
_membershipRateLimit: ethers.BigNumber,
|
||||||
_index: ethers.BigNumber,
|
_index: ethers.BigNumber,
|
||||||
event: ethers.Event
|
event: ethers.Event
|
||||||
@ -358,94 +386,89 @@ export class RLNBaseContract {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper method to get remaining messages in current epoch
|
|
||||||
* @param membershipId The ID of the membership to check
|
|
||||||
* @returns number of remaining messages allowed in current epoch
|
|
||||||
*/
|
|
||||||
public async getRemainingMessages(membershipId: number): Promise<number> {
|
|
||||||
try {
|
|
||||||
const [startTime, , rateLimit] =
|
|
||||||
await this.contract.getMembershipInfo(membershipId);
|
|
||||||
|
|
||||||
// Calculate current epoch
|
|
||||||
const currentTime = Math.floor(Date.now() / 1000);
|
|
||||||
const epochsPassed = Math.floor(
|
|
||||||
(currentTime - startTime) / RATE_LIMIT_PARAMS.EPOCH_LENGTH
|
|
||||||
);
|
|
||||||
const currentEpochStart =
|
|
||||||
startTime + epochsPassed * RATE_LIMIT_PARAMS.EPOCH_LENGTH;
|
|
||||||
|
|
||||||
// Get message count in current epoch using contract's function
|
|
||||||
const messageCount = await this.contract.getMessageCount(
|
|
||||||
membershipId,
|
|
||||||
currentEpochStart
|
|
||||||
);
|
|
||||||
return Math.max(
|
|
||||||
0,
|
|
||||||
ethers.BigNumber.from(rateLimit)
|
|
||||||
.sub(ethers.BigNumber.from(messageCount))
|
|
||||||
.toNumber()
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
log.error(
|
|
||||||
`Error getting remaining messages: ${(error as Error).message}`
|
|
||||||
);
|
|
||||||
return 0; // Fail safe: assume no messages remaining on error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async getMembershipInfo(
|
public async getMembershipInfo(
|
||||||
idCommitment: string
|
idCommitmentBigInt: bigint
|
||||||
): Promise<MembershipInfo | undefined> {
|
): Promise<MembershipInfo | undefined> {
|
||||||
try {
|
try {
|
||||||
const [startBlock, endBlock, rateLimit] =
|
const membershipData =
|
||||||
await this.contract.getMembershipInfo(idCommitment);
|
await this.contract.memberships(idCommitmentBigInt);
|
||||||
const currentBlock = await this.contract.provider.getBlockNumber();
|
const currentBlock = await this.contract.provider.getBlockNumber();
|
||||||
|
const [
|
||||||
|
depositAmount,
|
||||||
|
activeDuration,
|
||||||
|
gracePeriodStartTimestamp,
|
||||||
|
gracePeriodDuration,
|
||||||
|
rateLimit,
|
||||||
|
index,
|
||||||
|
holder,
|
||||||
|
token
|
||||||
|
] = membershipData;
|
||||||
|
|
||||||
|
const gracePeriodEnd = gracePeriodStartTimestamp.add(gracePeriodDuration);
|
||||||
|
|
||||||
let state: MembershipState;
|
let state: MembershipState;
|
||||||
if (currentBlock < startBlock) {
|
if (currentBlock < gracePeriodStartTimestamp.toNumber()) {
|
||||||
state = MembershipState.Active;
|
state = MembershipState.Active;
|
||||||
} else if (currentBlock < endBlock) {
|
} else if (currentBlock < gracePeriodEnd.toNumber()) {
|
||||||
state = MembershipState.GracePeriod;
|
state = MembershipState.GracePeriod;
|
||||||
} else {
|
} else {
|
||||||
state = MembershipState.Expired;
|
state = MembershipState.Expired;
|
||||||
}
|
}
|
||||||
|
|
||||||
const index = await this.getMemberIndex(idCommitment);
|
|
||||||
if (!index) return undefined;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
index,
|
index,
|
||||||
idCommitment,
|
idCommitment: idCommitmentBigInt.toString(),
|
||||||
rateLimit: rateLimit.toNumber(),
|
rateLimit: Number(rateLimit),
|
||||||
startBlock: startBlock.toNumber(),
|
startBlock: gracePeriodStartTimestamp.toNumber(),
|
||||||
endBlock: endBlock.toNumber(),
|
endBlock: gracePeriodEnd.toNumber(),
|
||||||
state
|
state,
|
||||||
|
depositAmount,
|
||||||
|
activeDuration,
|
||||||
|
gracePeriodDuration,
|
||||||
|
holder,
|
||||||
|
token
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
log.error("Error in getMembershipInfo:", error);
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async extendMembership(
|
public async extendMembership(
|
||||||
idCommitment: string
|
idCommitmentBigInt: bigint
|
||||||
): Promise<ethers.ContractTransaction> {
|
): Promise<ethers.ContractTransaction> {
|
||||||
return this.contract.extendMemberships([idCommitment]);
|
const tx = await this.contract.extendMemberships([idCommitmentBigInt]);
|
||||||
|
await tx.wait();
|
||||||
|
return tx;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async eraseMembership(
|
public async eraseMembership(
|
||||||
idCommitment: string,
|
idCommitmentBigInt: bigint,
|
||||||
eraseFromMembershipSet: boolean = true
|
eraseFromMembershipSet: boolean = true
|
||||||
): Promise<ethers.ContractTransaction> {
|
): Promise<ethers.ContractTransaction> {
|
||||||
return this.contract.eraseMemberships(
|
if (
|
||||||
[idCommitment],
|
!(await this.isExpired(idCommitmentBigInt)) ||
|
||||||
eraseFromMembershipSet
|
!(await this.isInGracePeriod(idCommitmentBigInt))
|
||||||
|
) {
|
||||||
|
throw new Error("Membership is not expired or in grace period");
|
||||||
|
}
|
||||||
|
|
||||||
|
const estimatedGas = await this.contract.estimateGas[
|
||||||
|
"eraseMemberships(uint256[],bool)"
|
||||||
|
]([idCommitmentBigInt], eraseFromMembershipSet);
|
||||||
|
const gasLimit = estimatedGas.add(10000);
|
||||||
|
|
||||||
|
const tx = await this.contract["eraseMemberships(uint256[],bool)"](
|
||||||
|
[idCommitmentBigInt],
|
||||||
|
eraseFromMembershipSet,
|
||||||
|
{ gasLimit }
|
||||||
);
|
);
|
||||||
|
await tx.wait();
|
||||||
|
return tx;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async registerMembership(
|
public async registerMembership(
|
||||||
idCommitment: string,
|
idCommitmentBigInt: bigint,
|
||||||
rateLimit: number = DEFAULT_RATE_LIMIT
|
rateLimit: number = DEFAULT_RATE_LIMIT
|
||||||
): Promise<ethers.ContractTransaction> {
|
): Promise<ethers.ContractTransaction> {
|
||||||
if (
|
if (
|
||||||
@ -456,12 +479,12 @@ export class RLNBaseContract {
|
|||||||
`Rate limit must be between ${RATE_LIMIT_PARAMS.MIN_RATE} and ${RATE_LIMIT_PARAMS.MAX_RATE}`
|
`Rate limit must be between ${RATE_LIMIT_PARAMS.MIN_RATE} and ${RATE_LIMIT_PARAMS.MAX_RATE}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return this.contract.register(idCommitment, rateLimit, []);
|
return this.contract.register(idCommitmentBigInt, rateLimit, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async withdraw(token: string, holder: string): Promise<void> {
|
public async withdraw(token: string, walletAddress: string): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const tx = await this.contract.withdraw(token, { from: holder });
|
const tx = await this.contract.withdraw(token, walletAddress);
|
||||||
await tx.wait();
|
await tx.wait();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.error(`Error in withdraw: ${(error as Error).message}`);
|
log.error(`Error in withdraw: ${(error as Error).message}`);
|
||||||
@ -478,7 +501,7 @@ export class RLNBaseContract {
|
|||||||
|
|
||||||
// Check if the ID commitment is already registered
|
// Check if the ID commitment is already registered
|
||||||
const existingIndex = await this.getMemberIndex(
|
const existingIndex = await this.getMemberIndex(
|
||||||
identity.IDCommitmentBigInt.toString()
|
identity.IDCommitmentBigInt
|
||||||
);
|
);
|
||||||
if (existingIndex) {
|
if (existingIndex) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
@ -516,7 +539,7 @@ export class RLNBaseContract {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const memberRegistered = txRegisterReceipt.events?.find(
|
const memberRegistered = txRegisterReceipt.events?.find(
|
||||||
(event) => event.event === "MembershipRegistered"
|
(event: ethers.Event) => event.event === "MembershipRegistered"
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!memberRegistered || !memberRegistered.args) {
|
if (!memberRegistered || !memberRegistered.args) {
|
||||||
@ -610,7 +633,7 @@ export class RLNBaseContract {
|
|||||||
const txRegisterReceipt = await txRegisterResponse.wait();
|
const txRegisterReceipt = await txRegisterResponse.wait();
|
||||||
|
|
||||||
const memberRegistered = txRegisterReceipt.events?.find(
|
const memberRegistered = txRegisterReceipt.events?.find(
|
||||||
(event) => event.event === "MembershipRegistered"
|
(event: ethers.Event) => event.event === "MembershipRegistered"
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!memberRegistered || !memberRegistered.args) {
|
if (!memberRegistered || !memberRegistered.args) {
|
||||||
@ -653,16 +676,16 @@ export class RLNBaseContract {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates that the rate limit is within the allowed range
|
* Validates that the rate limit is within the allowed range (sync)
|
||||||
* @throws Error if the rate limit is outside the allowed range
|
* @throws Error if the rate limit is outside the allowed range
|
||||||
*/
|
*/
|
||||||
private validateRateLimit(rateLimit: number): void {
|
private validateRateLimit(rateLimit: number): void {
|
||||||
if (
|
if (this.minRateLimit === undefined || this.maxRateLimit === undefined) {
|
||||||
rateLimit < RATE_LIMIT_PARAMS.MIN_RATE ||
|
throw new Error("Rate limits not initialized");
|
||||||
rateLimit > RATE_LIMIT_PARAMS.MAX_RATE
|
}
|
||||||
) {
|
if (rateLimit < this.minRateLimit || rateLimit > this.maxRateLimit) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Rate limit must be between ${RATE_LIMIT_PARAMS.MIN_RATE} and ${RATE_LIMIT_PARAMS.MAX_RATE} messages per epoch`
|
`Rate limit must be between ${this.minRateLimit} and ${this.maxRateLimit} messages per epoch`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -689,11 +712,11 @@ export class RLNBaseContract {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async getMemberIndex(
|
private async getMemberIndex(
|
||||||
idCommitment: string
|
idCommitmentBigInt: bigint
|
||||||
): Promise<ethers.BigNumber | undefined> {
|
): Promise<ethers.BigNumber | undefined> {
|
||||||
try {
|
try {
|
||||||
const events = await this.contract.queryFilter(
|
const events = await this.contract.queryFilter(
|
||||||
this.contract.filters.MembershipRegistered(idCommitment)
|
this.contract.filters.MembershipRegistered(idCommitmentBigInt)
|
||||||
);
|
);
|
||||||
if (events.length === 0) return undefined;
|
if (events.length === 0) return undefined;
|
||||||
|
|
||||||
@ -704,4 +727,45 @@ export class RLNBaseContract {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async getMembershipStatus(
|
||||||
|
idCommitment: bigint
|
||||||
|
): Promise<"expired" | "grace" | "active"> {
|
||||||
|
const [isExpired, isInGrace] = await Promise.all([
|
||||||
|
this.contract.isExpired(idCommitment),
|
||||||
|
this.contract.isInGracePeriod(idCommitment)
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (isExpired) return "expired";
|
||||||
|
if (isInGrace) return "grace";
|
||||||
|
return "active";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a membership is expired for the given idCommitment
|
||||||
|
* @param idCommitmentBigInt The idCommitment as bigint
|
||||||
|
* @returns Promise<boolean> True if expired, false otherwise
|
||||||
|
*/
|
||||||
|
public async isExpired(idCommitmentBigInt: bigint): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
return await this.contract.isExpired(idCommitmentBigInt);
|
||||||
|
} catch (error) {
|
||||||
|
log.error("Error in isExpired:", error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a membership is in grace period for the given idCommitment
|
||||||
|
* @param idCommitmentBigInt The idCommitment as bigint
|
||||||
|
* @returns Promise<boolean> True if in grace period, false otherwise
|
||||||
|
*/
|
||||||
|
public async isInGracePeriod(idCommitmentBigInt: bigint): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
return await this.contract.isInGracePeriod(idCommitmentBigInt);
|
||||||
|
} catch (error) {
|
||||||
|
log.error("Error in isInGracePeriod:", error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -38,6 +38,11 @@ export interface MembershipInfo {
|
|||||||
startBlock: number;
|
startBlock: number;
|
||||||
endBlock: number;
|
endBlock: number;
|
||||||
state: MembershipState;
|
state: MembershipState;
|
||||||
|
depositAmount: ethers.BigNumber;
|
||||||
|
activeDuration: number;
|
||||||
|
gracePeriodDuration: number;
|
||||||
|
holder: string;
|
||||||
|
token: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum MembershipState {
|
export enum MembershipState {
|
||||||
|
|||||||
@ -80,7 +80,7 @@ export class RLNCredentialsManager {
|
|||||||
|
|
||||||
this.credentials = credentials;
|
this.credentials = credentials;
|
||||||
this.signer = signer!;
|
this.signer = signer!;
|
||||||
this.contract = new RLNBaseContract({
|
this.contract = await RLNBaseContract.create({
|
||||||
address: address!,
|
address: address!,
|
||||||
signer: signer!,
|
signer: signer!,
|
||||||
rateLimit: rateLimit ?? this.zerokit?.rateLimit
|
rateLimit: rateLimit ?? this.zerokit?.rateLimit
|
||||||
|
|||||||
@ -34,7 +34,9 @@ export type {
|
|||||||
Keccak256Hash,
|
Keccak256Hash,
|
||||||
KeystoreEntity,
|
KeystoreEntity,
|
||||||
MembershipHash,
|
MembershipHash,
|
||||||
MembershipInfo,
|
KeystoreMembershipInfo,
|
||||||
Password,
|
Password,
|
||||||
Sha256Hash
|
Sha256Hash
|
||||||
} from "./keystore/types.js";
|
} from "./keystore/types.js";
|
||||||
|
|
||||||
|
export * from "./contract/index.js";
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import { IdentityCredential } from "../identity.js";
|
|||||||
import { buildBigIntFromUint8Array } from "../utils/bytes.js";
|
import { buildBigIntFromUint8Array } from "../utils/bytes.js";
|
||||||
|
|
||||||
import { Keystore } from "./keystore.js";
|
import { Keystore } from "./keystore.js";
|
||||||
import type { MembershipInfo } from "./types.js";
|
import type { KeystoreMembershipInfo } from "./types.js";
|
||||||
|
|
||||||
const DEFAULT_PASSWORD = "sup3rsecure";
|
const DEFAULT_PASSWORD = "sup3rsecure";
|
||||||
const NWAKU_KEYSTORE = {
|
const NWAKU_KEYSTORE = {
|
||||||
@ -233,7 +233,7 @@ describe("Keystore", () => {
|
|||||||
treeIndex: 8,
|
treeIndex: 8,
|
||||||
address: "0x8e1F3742B987d8BA376c0CBbD7357fE1F003ED71",
|
address: "0x8e1F3742B987d8BA376c0CBbD7357fE1F003ED71",
|
||||||
rateLimit: undefined
|
rateLimit: undefined
|
||||||
} as unknown as MembershipInfo;
|
} as unknown as KeystoreMembershipInfo;
|
||||||
|
|
||||||
const store = Keystore.create();
|
const store = Keystore.create();
|
||||||
const hash = await store.addCredential(
|
const hash = await store.addCredential(
|
||||||
@ -284,7 +284,7 @@ describe("Keystore", () => {
|
|||||||
treeIndex: 8,
|
treeIndex: 8,
|
||||||
address: "0x8e1F3742B987d8BA376c0CBbD7357fE1F003ED71",
|
address: "0x8e1F3742B987d8BA376c0CBbD7357fE1F003ED71",
|
||||||
rateLimit: undefined
|
rateLimit: undefined
|
||||||
} as unknown as MembershipInfo;
|
} as unknown as KeystoreMembershipInfo;
|
||||||
|
|
||||||
const store = Keystore.fromObject(NWAKU_KEYSTORE as any);
|
const store = Keystore.fromObject(NWAKU_KEYSTORE as any);
|
||||||
|
|
||||||
|
|||||||
@ -21,8 +21,8 @@ import { isCredentialValid, isKeystoreValid } from "./schema_validator.js";
|
|||||||
import type {
|
import type {
|
||||||
Keccak256Hash,
|
Keccak256Hash,
|
||||||
KeystoreEntity,
|
KeystoreEntity,
|
||||||
|
KeystoreMembershipInfo,
|
||||||
MembershipHash,
|
MembershipHash,
|
||||||
MembershipInfo,
|
|
||||||
Password,
|
Password,
|
||||||
Sha256Hash
|
Sha256Hash
|
||||||
} from "./types.js";
|
} from "./types.js";
|
||||||
@ -310,7 +310,9 @@ export class Keystore {
|
|||||||
|
|
||||||
// follows nwaku implementation
|
// follows nwaku implementation
|
||||||
// https://github.com/waku-org/nwaku/blob/f05528d4be3d3c876a8b07f9bb7dfaae8aa8ec6e/waku/waku_keystore/protocol_types.nim#L111
|
// https://github.com/waku-org/nwaku/blob/f05528d4be3d3c876a8b07f9bb7dfaae8aa8ec6e/waku/waku_keystore/protocol_types.nim#L111
|
||||||
private static computeMembershipHash(info: MembershipInfo): MembershipHash {
|
private static computeMembershipHash(
|
||||||
|
info: KeystoreMembershipInfo
|
||||||
|
): MembershipHash {
|
||||||
return bytesToHex(
|
return bytesToHex(
|
||||||
sha256(utf8ToBytes(`${info.chainId}${info.address}${info.treeIndex}`))
|
sha256(utf8ToBytes(`${info.chainId}${info.address}${info.treeIndex}`))
|
||||||
).toUpperCase();
|
).toUpperCase();
|
||||||
|
|||||||
@ -7,7 +7,7 @@ export type Password = string | Uint8Array;
|
|||||||
|
|
||||||
// see reference
|
// see reference
|
||||||
// https://github.com/waku-org/nwaku/blob/f05528d4be3d3c876a8b07f9bb7dfaae8aa8ec6e/waku/waku_keystore/protocol_types.nim#L111
|
// https://github.com/waku-org/nwaku/blob/f05528d4be3d3c876a8b07f9bb7dfaae8aa8ec6e/waku/waku_keystore/protocol_types.nim#L111
|
||||||
export type MembershipInfo = {
|
export type KeystoreMembershipInfo = {
|
||||||
chainId: string;
|
chainId: string;
|
||||||
address: string;
|
address: string;
|
||||||
treeIndex: number;
|
treeIndex: number;
|
||||||
@ -16,7 +16,7 @@ export type MembershipInfo = {
|
|||||||
|
|
||||||
export type KeystoreEntity = {
|
export type KeystoreEntity = {
|
||||||
identity: IdentityCredential;
|
identity: IdentityCredential;
|
||||||
membership: MembershipInfo;
|
membership: KeystoreMembershipInfo;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DecryptedCredentials = KeystoreEntity;
|
export type DecryptedCredentials = KeystoreEntity;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user