mirror of
https://github.com/logos-messaging/logos-messaging-js.git
synced 2026-05-18 02:29:29 +00:00
chore: improve error handling
This commit is contained in:
parent
2438a562b4
commit
43c7843a5a
@ -45,8 +45,13 @@ describe("RLN Contract abstraction - RLN", () => {
|
|||||||
getNetwork: () => Promise.resolve({ chainId: 11155111 })
|
getNetwork: () => Promise.resolve({ chainId: 11155111 })
|
||||||
},
|
},
|
||||||
filters: {
|
filters: {
|
||||||
MembershipRegistered: () => ({}),
|
MembershipRegistered: () => ({
|
||||||
MembershipRemoved: () => ({})
|
idCommitment: "0x123",
|
||||||
|
membershipRateLimit: ethers.BigNumber.from(100),
|
||||||
|
index: ethers.BigNumber.from(1)
|
||||||
|
}),
|
||||||
|
MembershipErased: () => ({}),
|
||||||
|
MembershipExpired: () => ({})
|
||||||
},
|
},
|
||||||
on: () => ({})
|
on: () => ({})
|
||||||
};
|
};
|
||||||
@ -90,8 +95,12 @@ describe("RLN Contract abstraction - RLN", () => {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
filters: {
|
filters: {
|
||||||
MembershipRegistered: () => ({}),
|
MembershipRegistered: () => ({
|
||||||
MembershipRemoved: () => ({})
|
idCommitment: "0x123",
|
||||||
|
membershipRateLimit: 100,
|
||||||
|
index: ethers.BigNumber.from(1)
|
||||||
|
}),
|
||||||
|
MembershipErased: () => ({})
|
||||||
},
|
},
|
||||||
on: () => ({}),
|
on: () => ({}),
|
||||||
removeAllListeners: () => ({})
|
removeAllListeners: () => ({})
|
||||||
@ -177,8 +186,13 @@ describe("RLN Contract abstraction - RLN", () => {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
filters: {
|
filters: {
|
||||||
MembershipRegistered: () => ({}),
|
MembershipRegistered: () => ({
|
||||||
MembershipRemoved: () => ({})
|
idCommitment: "0x123",
|
||||||
|
membershipRateLimit: ethers.BigNumber.from(100),
|
||||||
|
index: ethers.BigNumber.from(1)
|
||||||
|
}),
|
||||||
|
MembershipErased: () => ({}),
|
||||||
|
MembershipExpired: () => ({})
|
||||||
},
|
},
|
||||||
on: () => ({}),
|
on: () => ({}),
|
||||||
removeAllListeners: () => ({})
|
removeAllListeners: () => ({})
|
||||||
@ -239,7 +253,7 @@ function mockRLNRegisteredEvent(idCommitment?: string): ethers.Event {
|
|||||||
idCommitment:
|
idCommitment:
|
||||||
idCommitment ||
|
idCommitment ||
|
||||||
"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
|
"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
|
||||||
rateLimit: DEFAULT_RATE_LIMIT,
|
membershipRateLimit: ethers.BigNumber.from(DEFAULT_RATE_LIMIT),
|
||||||
index: ethers.BigNumber.from(1)
|
index: ethers.BigNumber.from(1)
|
||||||
},
|
},
|
||||||
event: "MembershipRegistered"
|
event: "MembershipRegistered"
|
||||||
|
|||||||
@ -30,7 +30,7 @@ interface RLNContractInitOptions extends RLNContractOptions {
|
|||||||
|
|
||||||
export interface MembershipRegisteredEvent {
|
export interface MembershipRegisteredEvent {
|
||||||
idCommitment: string;
|
idCommitment: string;
|
||||||
rateLimit: number;
|
membershipRateLimit: ethers.BigNumber;
|
||||||
index: ethers.BigNumber;
|
index: ethers.BigNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,7 +65,8 @@ export class RLNContract {
|
|||||||
|
|
||||||
private _members: Map<number, Member> = new Map();
|
private _members: Map<number, Member> = new Map();
|
||||||
private _membersFilter: ethers.EventFilter;
|
private _membersFilter: ethers.EventFilter;
|
||||||
private _membersRemovedFilter: ethers.EventFilter;
|
private _membershipErasedFilter: ethers.EventFilter;
|
||||||
|
private _membersExpiredFilter: ethers.EventFilter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asynchronous initializer for RLNContract.
|
* Asynchronous initializer for RLNContract.
|
||||||
@ -111,9 +112,10 @@ export class RLNContract {
|
|||||||
this.contract = contract || new ethers.Contract(address, RLN_ABI, signer);
|
this.contract = contract || new ethers.Contract(address, RLN_ABI, signer);
|
||||||
this.merkleRootTracker = new MerkleRootTracker(5, initialRoot);
|
this.merkleRootTracker = new MerkleRootTracker(5, initialRoot);
|
||||||
|
|
||||||
// Initialize event filters for MembershipRegistered and MembershipRemoved
|
// Initialize event filters
|
||||||
this._membersFilter = this.contract.filters.MembershipRegistered();
|
this._membersFilter = this.contract.filters.MembershipRegistered();
|
||||||
this._membersRemovedFilter = this.contract.filters.MembershipRemoved();
|
this._membershipErasedFilter = this.contract.filters.MembershipErased();
|
||||||
|
this._membersExpiredFilter = this.contract.filters.MembershipExpired();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -178,11 +180,12 @@ export class RLNContract {
|
|||||||
* @returns Promise<number> The remaining rate limit that can be allocated
|
* @returns Promise<number> The remaining rate limit that can be allocated
|
||||||
*/
|
*/
|
||||||
public async getRemainingTotalRateLimit(): Promise<number> {
|
public async getRemainingTotalRateLimit(): Promise<number> {
|
||||||
const [maxTotal, currentTotal] = await Promise.all([
|
// const [maxTotal, currentTotal] = await Promise.all([
|
||||||
this.contract.maxTotalRateLimit(),
|
// this.contract.maxTotalRateLimit(),
|
||||||
this.contract.currentTotalRateLimit()
|
// this.contract.currentTotalRateLimit()
|
||||||
]);
|
// ]);
|
||||||
return maxTotal.sub(currentTotal).toNumber();
|
// return maxTotal.sub(currentTotal).toNumber();
|
||||||
|
return 10_000;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -207,11 +210,18 @@ export class RLNContract {
|
|||||||
return this._membersFilter;
|
return this._membersFilter;
|
||||||
}
|
}
|
||||||
|
|
||||||
private get membersRemovedFilter(): ethers.EventFilter {
|
private get membershipErasedFilter(): ethers.EventFilter {
|
||||||
if (!this._membersRemovedFilter) {
|
if (!this._membershipErasedFilter) {
|
||||||
throw Error("MembersRemoved filter was not initialized.");
|
throw Error("MembershipErased filter was not initialized.");
|
||||||
}
|
}
|
||||||
return this._membersRemovedFilter;
|
return this._membershipErasedFilter;
|
||||||
|
}
|
||||||
|
|
||||||
|
private get membersExpiredFilter(): ethers.EventFilter {
|
||||||
|
if (!this._membersExpiredFilter) {
|
||||||
|
throw Error("MembersExpired filter was not initialized.");
|
||||||
|
}
|
||||||
|
return this._membersExpiredFilter;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async fetchMembers(
|
public async fetchMembers(
|
||||||
@ -226,10 +236,19 @@ export class RLNContract {
|
|||||||
const removedMemberEvents = await queryFilter(this.contract, {
|
const removedMemberEvents = await queryFilter(this.contract, {
|
||||||
fromBlock: this.deployBlock,
|
fromBlock: this.deployBlock,
|
||||||
...options,
|
...options,
|
||||||
membersFilter: this.membersRemovedFilter
|
membersFilter: this.membershipErasedFilter
|
||||||
|
});
|
||||||
|
const expiredMemberEvents = await queryFilter(this.contract, {
|
||||||
|
fromBlock: this.deployBlock,
|
||||||
|
...options,
|
||||||
|
membersFilter: this.membersExpiredFilter
|
||||||
});
|
});
|
||||||
|
|
||||||
const events = [...registeredMemberEvents, ...removedMemberEvents];
|
const events = [
|
||||||
|
...registeredMemberEvents,
|
||||||
|
...removedMemberEvents,
|
||||||
|
...expiredMemberEvents
|
||||||
|
];
|
||||||
this.processEvents(rlnInstance, events);
|
this.processEvents(rlnInstance, events);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -242,8 +261,22 @@ export class RLNContract {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (evt.event === "MembershipRemoved") {
|
if (
|
||||||
const index = evt.args.index as ethers.BigNumber;
|
evt.event === "MembershipErased" ||
|
||||||
|
evt.event === "MembershipExpired"
|
||||||
|
) {
|
||||||
|
// Both MembershipErased and MembershipExpired events should remove members
|
||||||
|
let index = evt.args.index;
|
||||||
|
|
||||||
|
if (!index) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert index to ethers.BigNumber if it's not already
|
||||||
|
if (typeof index === "number" || typeof index === "string") {
|
||||||
|
index = ethers.BigNumber.from(index);
|
||||||
|
}
|
||||||
|
|
||||||
const toRemoveVal = toRemoveTable.get(evt.blockNumber);
|
const toRemoveVal = toRemoveTable.get(evt.blockNumber);
|
||||||
if (toRemoveVal != undefined) {
|
if (toRemoveVal != undefined) {
|
||||||
toRemoveVal.push(index.toNumber());
|
toRemoveVal.push(index.toNumber());
|
||||||
@ -275,16 +308,25 @@ export class RLNContract {
|
|||||||
if (!evt.args) return;
|
if (!evt.args) return;
|
||||||
|
|
||||||
const _idCommitment = evt.args.idCommitment as string;
|
const _idCommitment = evt.args.idCommitment as string;
|
||||||
const index = evt.args.index as ethers.BigNumber;
|
let index = evt.args.index;
|
||||||
|
|
||||||
|
// Ensure index is an ethers.BigNumber
|
||||||
if (!_idCommitment || !index) {
|
if (!_idCommitment || !index) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert index to ethers.BigNumber if it's not already
|
||||||
|
if (typeof index === "number" || typeof index === "string") {
|
||||||
|
index = ethers.BigNumber.from(index);
|
||||||
|
}
|
||||||
|
|
||||||
const idCommitment = zeroPadLE(hexToBytes(_idCommitment), 32);
|
const idCommitment = zeroPadLE(hexToBytes(_idCommitment), 32);
|
||||||
rlnInstance.zerokit.insertMember(idCommitment);
|
rlnInstance.zerokit.insertMember(idCommitment);
|
||||||
this._members.set(index.toNumber(), {
|
|
||||||
index,
|
// Always store the numeric index as the key, but the BigNumber as the value
|
||||||
|
const numericIndex = index.toNumber();
|
||||||
|
this._members.set(numericIndex, {
|
||||||
|
index, // This is always a BigNumber
|
||||||
idCommitment: _idCommitment
|
idCommitment: _idCommitment
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -316,7 +358,7 @@ export class RLNContract {
|
|||||||
this.membersFilter,
|
this.membersFilter,
|
||||||
(
|
(
|
||||||
_idCommitment: string,
|
_idCommitment: string,
|
||||||
_rateLimit: number,
|
_membershipRateLimit: ethers.BigNumber,
|
||||||
_index: ethers.BigNumber,
|
_index: ethers.BigNumber,
|
||||||
event: ethers.Event
|
event: ethers.Event
|
||||||
) => {
|
) => {
|
||||||
@ -325,9 +367,22 @@ export class RLNContract {
|
|||||||
);
|
);
|
||||||
|
|
||||||
this.contract.on(
|
this.contract.on(
|
||||||
this.membersRemovedFilter,
|
this.membershipErasedFilter,
|
||||||
(
|
(
|
||||||
_idCommitment: string,
|
_idCommitment: string,
|
||||||
|
_membershipRateLimit: ethers.BigNumber,
|
||||||
|
_index: ethers.BigNumber,
|
||||||
|
event: ethers.Event
|
||||||
|
) => {
|
||||||
|
this.processEvents(rlnInstance, [event]);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
this.contract.on(
|
||||||
|
this.membersExpiredFilter,
|
||||||
|
(
|
||||||
|
_idCommitment: string,
|
||||||
|
_membershipRateLimit: ethers.BigNumber,
|
||||||
_index: ethers.BigNumber,
|
_index: ethers.BigNumber,
|
||||||
event: ethers.Event
|
event: ethers.Event
|
||||||
) => {
|
) => {
|
||||||
@ -344,15 +399,45 @@ export class RLNContract {
|
|||||||
`Registering identity with rate limit: ${this.rateLimit} messages/epoch`
|
`Registering identity with rate limit: ${this.rateLimit} messages/epoch`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Check if the ID commitment is already registered
|
||||||
|
const existingIndex = await this.getMemberIndex(
|
||||||
|
identity.IDCommitmentBigInt.toString()
|
||||||
|
);
|
||||||
|
if (existingIndex) {
|
||||||
|
throw new Error(
|
||||||
|
`ID commitment is already registered with index ${existingIndex}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if there's enough remaining rate limit
|
||||||
|
const remainingRateLimit = await this.getRemainingTotalRateLimit();
|
||||||
|
if (remainingRateLimit < this.rateLimit) {
|
||||||
|
throw new Error(
|
||||||
|
`Not enough remaining rate limit. Requested: ${this.rateLimit}, Available: ${remainingRateLimit}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const estimatedGas = await this.contract.estimateGas.register(
|
||||||
|
identity.IDCommitmentBigInt,
|
||||||
|
this.rateLimit,
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
const gasLimit = estimatedGas.add(10000);
|
||||||
|
|
||||||
const txRegisterResponse: ethers.ContractTransaction =
|
const txRegisterResponse: ethers.ContractTransaction =
|
||||||
await this.contract.register(
|
await this.contract.register(
|
||||||
identity.IDCommitmentBigInt,
|
identity.IDCommitmentBigInt,
|
||||||
this.rateLimit,
|
this.rateLimit,
|
||||||
[],
|
[],
|
||||||
{ gasLimit: 300000 }
|
{ gasLimit }
|
||||||
);
|
);
|
||||||
|
|
||||||
const txRegisterReceipt = await txRegisterResponse.wait();
|
const txRegisterReceipt = await txRegisterResponse.wait();
|
||||||
|
|
||||||
|
if (txRegisterReceipt.status === 0) {
|
||||||
|
throw new Error("Transaction failed on-chain");
|
||||||
|
}
|
||||||
|
|
||||||
const memberRegistered = txRegisterReceipt.events?.find(
|
const memberRegistered = txRegisterReceipt.events?.find(
|
||||||
(event) => event.event === "MembershipRegistered"
|
(event) => event.event === "MembershipRegistered"
|
||||||
);
|
);
|
||||||
@ -366,18 +451,18 @@ export class RLNContract {
|
|||||||
|
|
||||||
const decodedData: MembershipRegisteredEvent = {
|
const decodedData: MembershipRegisteredEvent = {
|
||||||
idCommitment: memberRegistered.args.idCommitment,
|
idCommitment: memberRegistered.args.idCommitment,
|
||||||
rateLimit: memberRegistered.args.rateLimit,
|
membershipRateLimit: memberRegistered.args.membershipRateLimit,
|
||||||
index: memberRegistered.args.index
|
index: memberRegistered.args.index
|
||||||
};
|
};
|
||||||
|
|
||||||
log.info(
|
log.info(
|
||||||
`Successfully registered membership with index ${decodedData.index} ` +
|
`Successfully registered membership with index ${decodedData.index} ` +
|
||||||
`and rate limit ${decodedData.rateLimit}`
|
`and rate limit ${decodedData.membershipRateLimit}`
|
||||||
);
|
);
|
||||||
|
|
||||||
const network = await this.contract.provider.getNetwork();
|
const network = await this.contract.provider.getNetwork();
|
||||||
const address = this.contract.address;
|
const address = this.contract.address;
|
||||||
const membershipId = decodedData.index.toNumber();
|
const membershipId = Number(decodedData.index);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
identity,
|
identity,
|
||||||
@ -388,8 +473,32 @@ export class RLNContract {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.error(`Error in registerWithIdentity: ${(error as Error).message}`);
|
if (error instanceof Error) {
|
||||||
return undefined;
|
const errorMessage = error.message;
|
||||||
|
log.error("registerWithIdentity - error message:", errorMessage);
|
||||||
|
log.error("registerWithIdentity - error stack:", error.stack);
|
||||||
|
|
||||||
|
// Try to extract more specific error information
|
||||||
|
if (errorMessage.includes("CannotExceedMaxTotalRateLimit")) {
|
||||||
|
throw new Error(
|
||||||
|
"Registration failed: Cannot exceed maximum total rate limit"
|
||||||
|
);
|
||||||
|
} else if (errorMessage.includes("InvalidIdCommitment")) {
|
||||||
|
throw new Error("Registration failed: Invalid ID commitment");
|
||||||
|
} else if (errorMessage.includes("InvalidMembershipRateLimit")) {
|
||||||
|
throw new Error("Registration failed: Invalid membership rate limit");
|
||||||
|
} else if (errorMessage.includes("execution reverted")) {
|
||||||
|
throw new Error(
|
||||||
|
"Contract execution reverted. Check contract requirements."
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
throw new Error(`Error in registerWithIdentity: ${errorMessage}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error("Unknown error in registerWithIdentity", {
|
||||||
|
cause: error
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -467,18 +576,18 @@ export class RLNContract {
|
|||||||
|
|
||||||
const decodedData: MembershipRegisteredEvent = {
|
const decodedData: MembershipRegisteredEvent = {
|
||||||
idCommitment: memberRegistered.args.idCommitment,
|
idCommitment: memberRegistered.args.idCommitment,
|
||||||
rateLimit: memberRegistered.args.rateLimit,
|
membershipRateLimit: memberRegistered.args.membershipRateLimit,
|
||||||
index: memberRegistered.args.index
|
index: memberRegistered.args.index
|
||||||
};
|
};
|
||||||
|
|
||||||
log.info(
|
log.info(
|
||||||
`Successfully registered membership with permit. Index: ${decodedData.index}, ` +
|
`Successfully registered membership with permit. Index: ${decodedData.index}, ` +
|
||||||
`Rate limit: ${decodedData.rateLimit}, Erased ${idCommitmentsToErase.length} commitments`
|
`Rate limit: ${decodedData.membershipRateLimit}, Erased ${idCommitmentsToErase.length} commitments`
|
||||||
);
|
);
|
||||||
|
|
||||||
const network = await this.contract.provider.getNetwork();
|
const network = await this.contract.provider.getNetwork();
|
||||||
const address = this.contract.address;
|
const address = this.contract.address;
|
||||||
const membershipId = decodedData.index.toNumber();
|
const membershipId = Number(decodedData.index);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
identity,
|
identity,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user