mirror of
https://github.com/0xbow-io/privacy-pools-core.git
synced 2026-01-10 09:58:00 -05:00
chore: add timestamps, make mnemonic required, export services (#36)
This commit is contained in:
@@ -17,6 +17,15 @@ const typescriptConfig = {
|
||||
outputToFilesystem: false,
|
||||
}
|
||||
|
||||
// External dependencies that should not be bundled
|
||||
const external = [
|
||||
'@envio-dev/hypersync-client',
|
||||
'viem',
|
||||
'viem/accounts',
|
||||
'viem/chains',
|
||||
'maci-crypto',
|
||||
];
|
||||
|
||||
export default [
|
||||
|
||||
{
|
||||
@@ -29,6 +38,7 @@ export default [
|
||||
entryFileNames: "[name].mjs"
|
||||
},
|
||||
],
|
||||
external,
|
||||
plugins: [
|
||||
nodeResolve({
|
||||
exportConditions: ["umd"],
|
||||
@@ -55,6 +65,7 @@ export default [
|
||||
entryFileNames: "[name].mjs"
|
||||
},
|
||||
],
|
||||
external,
|
||||
plugins: [
|
||||
nodeResolve({
|
||||
exportConditions: ["node"],
|
||||
@@ -83,6 +94,7 @@ export default [
|
||||
sourcemap: false,
|
||||
},
|
||||
],
|
||||
external,
|
||||
plugins: [
|
||||
nodeResolve({
|
||||
exportConditions: ["node"],
|
||||
@@ -103,6 +115,7 @@ export default [
|
||||
{
|
||||
input: path.join(rootOutDir, "types", "index.d.ts"),
|
||||
output: [{ file: path.join(rootOutDir, "index.d.mts"), format: "esm" }],
|
||||
external,
|
||||
plugins: [dts()],
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Hex, bytesToNumber } from "viem";
|
||||
import { english, generateMnemonic, mnemonicToAccount } from "viem/accounts";
|
||||
import { DataService } from "./data.service.js";
|
||||
import {
|
||||
Commitment,
|
||||
AccountCommitment,
|
||||
PoolAccount,
|
||||
PoolInfo,
|
||||
PrivacyPoolAccount,
|
||||
@@ -35,16 +35,15 @@ export class AccountService {
|
||||
*/
|
||||
constructor(
|
||||
private readonly dataService: DataService,
|
||||
mnemonic: string,
|
||||
account?: PrivacyPoolAccount,
|
||||
mnemonic?: string,
|
||||
) {
|
||||
this.logger = new Logger({ prefix: "Account" });
|
||||
this.account = account || this._initializeAccount(mnemonic);
|
||||
}
|
||||
|
||||
private _initializeAccount(mnemonic?: string): PrivacyPoolAccount {
|
||||
private _initializeAccount(mnemonic: string): PrivacyPoolAccount {
|
||||
try {
|
||||
mnemonic = mnemonic || generateMnemonic(english, 128);
|
||||
this.logger.debug("Initializing account with mnemonic");
|
||||
|
||||
let key1 = bytesToNumber(
|
||||
@@ -59,9 +58,10 @@ export class AccountService {
|
||||
let masterKey2 = poseidon([BigInt(key2)]) as Secret;
|
||||
|
||||
return {
|
||||
mnemonic,
|
||||
masterKeys: [masterKey1, masterKey2],
|
||||
poolAccounts: new Map(),
|
||||
creationTimestamp: 0n,
|
||||
lastUpdateTimestamp: 0n
|
||||
};
|
||||
} catch (error) {
|
||||
throw AccountError.accountInitializationFailed(
|
||||
@@ -103,11 +103,11 @@ export class AccountService {
|
||||
*
|
||||
* @returns A map of scope to array of spendable commitments
|
||||
*/
|
||||
public getSpendableCommitments(): Map<bigint, Commitment[]> {
|
||||
const result = new Map<bigint, Commitment[]>();
|
||||
public getSpendableCommitments(): Map<bigint, AccountCommitment[]> {
|
||||
const result = new Map<bigint, AccountCommitment[]>();
|
||||
|
||||
for (const [scope, accounts] of this.account.poolAccounts.entries()) {
|
||||
const nonZeroCommitments: Commitment[] = [];
|
||||
const nonZeroCommitments: AccountCommitment[] = [];
|
||||
|
||||
for (const account of accounts) {
|
||||
const lastCommitment =
|
||||
@@ -159,7 +159,7 @@ export class AccountService {
|
||||
* @returns The nullifier and secret for the new commitment
|
||||
* @throws {AccountError} If no account is found for the commitment
|
||||
*/
|
||||
public createWithdrawalSecrets(commitment: Commitment): {
|
||||
public createWithdrawalSecrets(commitment: AccountCommitment): {
|
||||
nullifier: Secret;
|
||||
secret: Secret;
|
||||
} {
|
||||
@@ -192,6 +192,7 @@ export class AccountService {
|
||||
* @param secret - The secret used for the deposit
|
||||
* @param label - The label for the commitment
|
||||
* @param blockNumber - The block number of the deposit
|
||||
* @param timestamp - The timestamp of the deposit
|
||||
* @param txHash - The transaction hash of the deposit
|
||||
* @returns The new pool account
|
||||
*/
|
||||
@@ -202,11 +203,20 @@ export class AccountService {
|
||||
secret: Secret,
|
||||
label: Hash,
|
||||
blockNumber: bigint,
|
||||
timestamp: bigint,
|
||||
txHash: Hash,
|
||||
): PoolAccount {
|
||||
const precommitment = this._hashPrecommitment(nullifier, secret);
|
||||
const commitment = this._hashCommitment(value, label, precommitment);
|
||||
|
||||
// Update account timestamps
|
||||
if (this.account.creationTimestamp === 0n || timestamp < this.account.creationTimestamp) {
|
||||
this.account.creationTimestamp = timestamp;
|
||||
}
|
||||
if (timestamp > this.account.lastUpdateTimestamp) {
|
||||
this.account.lastUpdateTimestamp = timestamp;
|
||||
}
|
||||
|
||||
const newAccount: PoolAccount = {
|
||||
label,
|
||||
deposit: {
|
||||
@@ -216,6 +226,7 @@ export class AccountService {
|
||||
nullifier,
|
||||
secret,
|
||||
blockNumber,
|
||||
timestamp,
|
||||
txHash,
|
||||
},
|
||||
children: [],
|
||||
@@ -242,18 +253,25 @@ export class AccountService {
|
||||
* @param nullifier - The nullifier used for spending
|
||||
* @param secret - The secret used for spending
|
||||
* @param blockNumber - The block number of the withdrawal
|
||||
* @param timestamp - The timestamp of the withdrawal
|
||||
* @param txHash - The transaction hash of the withdrawal
|
||||
* @returns The new commitment
|
||||
* @throws {AccountError} If no account is found for the commitment
|
||||
*/
|
||||
public addWithdrawalCommitment(
|
||||
parentCommitment: Commitment,
|
||||
parentCommitment: AccountCommitment,
|
||||
value: bigint,
|
||||
nullifier: Secret,
|
||||
secret: Secret,
|
||||
blockNumber: bigint,
|
||||
timestamp: bigint,
|
||||
txHash: Hash,
|
||||
): Commitment {
|
||||
): AccountCommitment {
|
||||
// Update last update timestamp
|
||||
if (timestamp > this.account.lastUpdateTimestamp) {
|
||||
this.account.lastUpdateTimestamp = timestamp;
|
||||
}
|
||||
|
||||
let foundAccount: PoolAccount | undefined;
|
||||
let foundScope: bigint | undefined;
|
||||
|
||||
@@ -276,13 +294,14 @@ export class AccountService {
|
||||
}
|
||||
|
||||
const precommitment = this._hashPrecommitment(nullifier, secret);
|
||||
const newCommitment: Commitment = {
|
||||
const newCommitment: AccountCommitment = {
|
||||
hash: this._hashCommitment(value, parentCommitment.label, precommitment),
|
||||
value,
|
||||
label: parentCommitment.label,
|
||||
nullifier,
|
||||
secret,
|
||||
blockNumber,
|
||||
timestamp,
|
||||
txHash,
|
||||
};
|
||||
|
||||
@@ -313,13 +332,13 @@ export class AccountService {
|
||||
|
||||
for (const withdrawal of withdrawals) {
|
||||
for (const account of foundAccounts.values()) {
|
||||
const isParentCommitment =
|
||||
const isParentCommitment =
|
||||
BigInt(account.deposit.nullifier) === BigInt(withdrawal.spentNullifier) ||
|
||||
account.children.some(child => BigInt(child.nullifier) === BigInt(withdrawal.spentNullifier));
|
||||
|
||||
if (isParentCommitment) {
|
||||
const parentCommitment = account.children.length > 0
|
||||
? account.children[account.children.length - 1]
|
||||
const parentCommitment = account.children.length > 0
|
||||
? account.children[account.children.length - 1]
|
||||
: account.deposit;
|
||||
|
||||
if (!parentCommitment) {
|
||||
@@ -333,6 +352,7 @@ export class AccountService {
|
||||
withdrawal.spentNullifier as unknown as Secret,
|
||||
parentCommitment.secret,
|
||||
withdrawal.blockNumber,
|
||||
withdrawal.timestamp,
|
||||
withdrawal.transactionHash,
|
||||
);
|
||||
break;
|
||||
@@ -429,6 +449,7 @@ export class AccountService {
|
||||
secret,
|
||||
deposit.label,
|
||||
deposit.blockNumber,
|
||||
deposit.timestamp,
|
||||
deposit.transactionHash,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -2,6 +2,8 @@ import {
|
||||
HypersyncClient,
|
||||
presetQueryLogsOfEvent,
|
||||
Query,
|
||||
QueryResponse,
|
||||
Log
|
||||
} from "@envio-dev/hypersync-client";
|
||||
import {
|
||||
ChainConfig,
|
||||
@@ -73,8 +75,7 @@ export class DataService {
|
||||
const toBlock = options.toBlock ?? undefined;
|
||||
|
||||
this.logger.debug(
|
||||
`Fetching deposits for chain ${chainId} from block ${fromBlock}${
|
||||
toBlock ? ` to ${toBlock}` : ""
|
||||
`Fetching deposits for chain ${chainId} from block ${fromBlock}${toBlock ? ` to ${toBlock}` : ""
|
||||
}`,
|
||||
);
|
||||
|
||||
@@ -101,7 +102,16 @@ export class DataService {
|
||||
|
||||
const res = await client.get(query);
|
||||
|
||||
// Create a map of block numbers to timestamps
|
||||
const blockTimestamps = new Map(
|
||||
res.data.blocks.map(block => [
|
||||
block.number,
|
||||
block.timestamp ? BigInt(block.timestamp) : 0n
|
||||
])
|
||||
);
|
||||
|
||||
return res.data.logs.map((log) => {
|
||||
let a: Log = log;
|
||||
if (!log.topics || log.topics.length < 2) {
|
||||
throw DataError.invalidLog("deposit", "missing topics");
|
||||
}
|
||||
@@ -137,13 +147,20 @@ export class DataService {
|
||||
throw DataError.invalidLog("deposit", "missing required fields");
|
||||
}
|
||||
|
||||
const blockNumber = BigInt(log.blockNumber);
|
||||
const timestamp = blockTimestamps.get(Number(blockNumber));
|
||||
if (!timestamp) {
|
||||
throw DataError.invalidLog("deposit", "missing block timestamp");
|
||||
}
|
||||
|
||||
return {
|
||||
depositor: `0x${depositor.toString(16).padStart(40, "0")}`,
|
||||
commitment: bigintToHash(commitment),
|
||||
label: bigintToHash(label),
|
||||
value,
|
||||
precommitment: bigintToHash(precommitment),
|
||||
blockNumber: BigInt(log.blockNumber),
|
||||
blockNumber,
|
||||
timestamp,
|
||||
transactionHash: log.transactionHash as unknown as Hash,
|
||||
};
|
||||
});
|
||||
@@ -173,8 +190,7 @@ export class DataService {
|
||||
const toBlock = options.toBlock ?? undefined;
|
||||
|
||||
this.logger.debug(
|
||||
`Fetching withdrawals for chain ${chainId} from block ${fromBlock}${
|
||||
toBlock ? ` to ${toBlock}` : ""
|
||||
`Fetching withdrawals for chain ${chainId} from block ${fromBlock}${toBlock ? ` to ${toBlock}` : ""
|
||||
}`,
|
||||
);
|
||||
|
||||
@@ -188,6 +204,14 @@ export class DataService {
|
||||
|
||||
const res = await client.get(query);
|
||||
|
||||
// Create a map of block numbers to timestamps
|
||||
const blockTimestamps = new Map(
|
||||
res.data.blocks.map(block => [
|
||||
block.number,
|
||||
block.timestamp ? BigInt(block.timestamp) : 0n
|
||||
])
|
||||
);
|
||||
|
||||
return res.data.logs.map((log) => {
|
||||
if (!log.topics || log.topics.length < 2) {
|
||||
throw DataError.invalidLog("withdrawal", "missing topics");
|
||||
@@ -222,11 +246,18 @@ export class DataService {
|
||||
throw DataError.invalidLog("withdrawal", "missing required fields");
|
||||
}
|
||||
|
||||
const blockNumber = BigInt(log.blockNumber);
|
||||
const timestamp = blockTimestamps.get(Number(blockNumber));
|
||||
if (!timestamp) {
|
||||
throw DataError.invalidLog("withdrawal", "missing block timestamp");
|
||||
}
|
||||
|
||||
return {
|
||||
withdrawn: value,
|
||||
spentNullifier: bigintToHash(spentNullifier),
|
||||
newCommitment: bigintToHash(newCommitment),
|
||||
blockNumber: BigInt(log.blockNumber),
|
||||
blockNumber,
|
||||
timestamp,
|
||||
transactionHash: log.transactionHash as unknown as Hash,
|
||||
};
|
||||
});
|
||||
@@ -256,8 +287,7 @@ export class DataService {
|
||||
const toBlock = options.toBlock ?? undefined;
|
||||
|
||||
this.logger.debug(
|
||||
`Fetching ragequits for chain ${chainId} from block ${fromBlock}${
|
||||
toBlock ? ` to ${toBlock}` : ""
|
||||
`Fetching ragequits for chain ${chainId} from block ${fromBlock}${toBlock ? ` to ${toBlock}` : ""
|
||||
}`,
|
||||
);
|
||||
|
||||
@@ -271,6 +301,14 @@ export class DataService {
|
||||
|
||||
const res = await client.get(query);
|
||||
|
||||
// Create a map of block numbers to timestamps
|
||||
const blockTimestamps = new Map(
|
||||
res.data.blocks.map(block => [
|
||||
block.number,
|
||||
block.timestamp ? BigInt(block.timestamp) : 0n
|
||||
])
|
||||
);
|
||||
|
||||
return res.data.logs.map((log) => {
|
||||
if (!log.topics || log.topics.length < 2) {
|
||||
throw DataError.invalidLog("ragequit", "missing topics");
|
||||
@@ -306,12 +344,19 @@ export class DataService {
|
||||
throw DataError.invalidLog("ragequit", "missing required fields");
|
||||
}
|
||||
|
||||
const blockNumber = BigInt(log.blockNumber);
|
||||
const timestamp = blockTimestamps.get(Number(blockNumber));
|
||||
if (!timestamp) {
|
||||
throw DataError.invalidLog("ragequit", "missing block timestamp");
|
||||
}
|
||||
|
||||
return {
|
||||
ragequitter: `0x${ragequitter.toString(16).padStart(40, "0")}`,
|
||||
commitment: bigintToHash(commitment),
|
||||
label: bigintToHash(label),
|
||||
value,
|
||||
blockNumber: BigInt(log.blockNumber),
|
||||
blockNumber,
|
||||
timestamp,
|
||||
transactionHash: log.transactionHash as unknown as Hash,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -3,12 +3,13 @@ export * from "./crypto.js";
|
||||
export * from "./external.js";
|
||||
export { PrivacyPoolSDK } from "./core/sdk.js";
|
||||
|
||||
// Types
|
||||
export * from "./types/commitment.js";
|
||||
export * from "./types/withdrawal.js";
|
||||
// Additional Types (not included in types/index.js)
|
||||
export * from "./types/account.js";
|
||||
export * from "./types/events.js";
|
||||
|
||||
// Errors
|
||||
export * from "./errors/base.error.js";
|
||||
export * from "./errors/account.error.js";
|
||||
|
||||
// Interfaces
|
||||
export * from "./interfaces/circuits.interface.js";
|
||||
@@ -16,3 +17,5 @@ export * from "./interfaces/circuits.interface.js";
|
||||
// Services (exported for advanced usage)
|
||||
export { CommitmentService } from "./core/commitment.service.js";
|
||||
export { WithdrawalService } from "./core/withdrawal.service.js";
|
||||
export { AccountService } from "./core/account.service.js";
|
||||
export { DataService } from "./core/data.service.js";
|
||||
|
||||
@@ -3,24 +3,26 @@ import { Address } from "viem";
|
||||
|
||||
export interface PoolAccount {
|
||||
label: Hash;
|
||||
deposit: Commitment;
|
||||
children: Commitment[];
|
||||
deposit: AccountCommitment;
|
||||
children: AccountCommitment[];
|
||||
}
|
||||
|
||||
export interface Commitment {
|
||||
export interface AccountCommitment {
|
||||
hash: Hash;
|
||||
value: bigint;
|
||||
label: Hash;
|
||||
nullifier: Secret;
|
||||
secret: Secret;
|
||||
blockNumber: bigint;
|
||||
timestamp: bigint;
|
||||
txHash: Hash;
|
||||
}
|
||||
|
||||
export interface PrivacyPoolAccount {
|
||||
mnemonic: string;
|
||||
masterKeys: [Secret, Secret];
|
||||
poolAccounts: Map<bigint, PoolAccount[]>;
|
||||
creationTimestamp: bigint;
|
||||
lastUpdateTimestamp: bigint;
|
||||
}
|
||||
|
||||
export interface PoolInfo {
|
||||
|
||||
@@ -11,6 +11,7 @@ export interface DepositEvent {
|
||||
value: bigint;
|
||||
precommitment: Hash;
|
||||
blockNumber: bigint;
|
||||
timestamp: bigint;
|
||||
transactionHash: Hash;
|
||||
}
|
||||
|
||||
@@ -22,6 +23,7 @@ export interface WithdrawalEvent {
|
||||
spentNullifier: Hash;
|
||||
newCommitment: Hash;
|
||||
blockNumber: bigint;
|
||||
timestamp: bigint;
|
||||
transactionHash: Hash;
|
||||
}
|
||||
|
||||
@@ -34,6 +36,7 @@ export interface RagequitEvent {
|
||||
label: Hash;
|
||||
value: bigint;
|
||||
blockNumber: bigint;
|
||||
timestamp: bigint;
|
||||
transactionHash: Hash;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,9 +3,10 @@ import { AccountService } from "../../src/core/account.service.js";
|
||||
import { DataService } from "../../src/core/data.service.js";
|
||||
import { Hash, Secret } from "../../src/types/commitment.js";
|
||||
import { DepositEvent, WithdrawalEvent } from "../../src/types/events.js";
|
||||
import { PoolInfo, Commitment } from "../../src/types/account.js";
|
||||
import { PoolInfo, AccountCommitment } from "../../src/types/account.js";
|
||||
import { poseidon } from "maci-crypto/build/ts/hashing.js";
|
||||
import { Address } from "viem";
|
||||
import { english, generateMnemonic } from "viem/accounts";
|
||||
|
||||
function randomBigInt(): bigint {
|
||||
return BigInt(Math.floor(Math.random() * Number.MAX_SAFE_INTEGER));
|
||||
@@ -29,6 +30,7 @@ describe("AccountService", () => {
|
||||
let masterKeys: [Secret, Secret];
|
||||
let depositEvents: DepositEvent[] = [];
|
||||
let withdrawalEvents: WithdrawalEvent[] = [];
|
||||
const testMnemonic = generateMnemonic(english);
|
||||
|
||||
beforeEach(() => {
|
||||
// Reset test data arrays
|
||||
@@ -46,7 +48,7 @@ describe("AccountService", () => {
|
||||
} as unknown as DataService;
|
||||
|
||||
// Initialize account service with mocked data service
|
||||
accountService = new AccountService(dataService);
|
||||
accountService = new AccountService(dataService, testMnemonic);
|
||||
masterKeys = accountService.account.masterKeys;
|
||||
|
||||
// Generate test data
|
||||
@@ -67,6 +69,7 @@ describe("AccountService", () => {
|
||||
|
||||
const precommitment = poseidon([nullifier, secret]) as Hash;
|
||||
const commitment = poseidon([value, label, precommitment]) as Hash;
|
||||
const timestamp = BigInt(Math.floor(Date.now() / 1000));
|
||||
|
||||
const deposit: DepositEvent = {
|
||||
depositor: POOL.address,
|
||||
@@ -75,6 +78,7 @@ describe("AccountService", () => {
|
||||
value,
|
||||
precommitment,
|
||||
blockNumber: POOL.deploymentBlock + BigInt(i * 100),
|
||||
timestamp,
|
||||
transactionHash: BigInt(i + 1) as Hash,
|
||||
};
|
||||
|
||||
@@ -88,6 +92,7 @@ describe("AccountService", () => {
|
||||
nullifier,
|
||||
secret,
|
||||
blockNumber: deposit.blockNumber,
|
||||
timestamp,
|
||||
txHash: deposit.transactionHash,
|
||||
};
|
||||
let remainingValue = value;
|
||||
@@ -125,6 +130,7 @@ describe("AccountService", () => {
|
||||
spentNullifier: poseidon([withdrawalNullifier]) as Hash,
|
||||
newCommitment,
|
||||
blockNumber: currentCommitment.blockNumber + BigInt((j + 1) * 100),
|
||||
timestamp: currentCommitment.timestamp + BigInt((j + 1) * 60), // Add 1 minute per withdrawal
|
||||
transactionHash: BigInt(i * 100 + j + 2) as Hash,
|
||||
};
|
||||
|
||||
@@ -138,6 +144,7 @@ describe("AccountService", () => {
|
||||
nullifier: withdrawalNullifier,
|
||||
secret: withdrawalSecret,
|
||||
blockNumber: withdrawal.blockNumber,
|
||||
timestamp: withdrawal.timestamp,
|
||||
txHash: withdrawal.transactionHash,
|
||||
};
|
||||
}
|
||||
@@ -160,7 +167,7 @@ describe("AccountService", () => {
|
||||
|
||||
const spendable = accountService
|
||||
.getSpendableCommitments()
|
||||
.get(POOL.scope) as Commitment[];
|
||||
.get(POOL.scope) as AccountCommitment[];
|
||||
|
||||
if (spendable) {
|
||||
console.log("Spendable:", spendable);
|
||||
|
||||
Reference in New Issue
Block a user