Skip to content

Commit

Permalink
fix incorrect maxContributors, maxMessages and maxVoteOptions
Browse files Browse the repository at this point in the history
  • Loading branch information
yuetloo committed May 21, 2024
1 parent 36743e4 commit 30a38a3
Show file tree
Hide file tree
Showing 12 changed files with 209 additions and 64 deletions.
16 changes: 13 additions & 3 deletions common/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ import { Keypair } from './keypair'
import { Tally } from './tally'
import { bnSqrt } from './math'

const LEAVES_PER_NODE = 5
// This has to match the MACI TREE_ARITY at:
// github.com/privacy-scaling-explorations/maci/blob/0c18913d4c84bfa9fbfd66dc017e338df9fdda96/contracts/contracts/MACI.sol#L31
export const MACI_TREE_ARITY = 5

export function createMessage(
userStateIndex: number,
Expand Down Expand Up @@ -65,7 +67,7 @@ export function getRecipientClaimData(
const spentTree = new IncrementalQuinTree(
recipientTreeDepth,
BigInt(0),
LEAVES_PER_NODE,
MACI_TREE_ARITY,
hash5
)
for (const leaf of tally.perVOSpentVoiceCredits.tally) {
Expand Down Expand Up @@ -94,6 +96,15 @@ export function getRecipientClaimData(
]
}

/**
* Returns the maximum MACI users allowed by the state tree
* @param stateTreeDepth MACI state tree depth
* @returns the maximum number of contributors allowed by MACI circuit
*/
export function getMaxContributors(stateTreeDepth: number): number {
return MACI_TREE_ARITY ** stateTreeDepth - 1
}

export {
genTallyResultCommitment,
Message,
Expand All @@ -103,5 +114,4 @@ export {
hash2,
hash3,
hashLeftRight,
LEAVES_PER_NODE,
}
80 changes: 69 additions & 11 deletions contracts/tests/round.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ import {
randomBytes,
hexlify,
toNumber,
Wallet,
TransactionResponse,
} from 'ethers'
import { genRandomSalt } from 'maci-crypto'
import { Keypair } from '@clrfund/common'
import { getMaxContributors, Keypair, MACI_TREE_ARITY } from '@clrfund/common'
import { time } from '@nomicfoundation/hardhat-network-helpers'

import {
Expand All @@ -29,11 +31,14 @@ import {
getRecipientClaimData,
mergeMaciSubtrees,
} from '../utils/maci'
import { deployTestFundingRound } from '../utils/testutils'
import {
deployTestFundingRound,
DeployTestFundingRoundOutput,
} from '../utils/testutils'

// ethStaker test vectors for Quadratic Funding with alpha
import smallTallyTestData from './data/testTallySmall.json'
import { FundingRound } from '../typechain-types'
import { AnyOldERC20Token, FundingRound } from '../typechain-types'
import { EContracts } from '../utils/types'

const newResultCommitment = hexlify(randomBytes(32))
Expand Down Expand Up @@ -66,6 +71,33 @@ function calcAllocationAmount(tally: string, voiceCredit: string): bigint {
return allocation / ALPHA_PRECISION
}

/**
* Simulate contribution by a random user
* @param contracts list of contracts returned from the deployTestFundingRound function
* @param deployer the account that owns the contracts
* @returns contribute transaction response
*/
async function contributeByRandomUser(
contracts: DeployTestFundingRoundOutput,
deployer: HardhatEthersSigner
): Promise<TransactionResponse> {
const amount = ethers.parseEther('0.1')
const keypair = new Keypair()
const user = Wallet.createRandom(ethers.provider)
await contracts.token.transfer(user.address, amount)
await deployer.sendTransaction({ to: user.address, value: amount })
const tokenAsUser = contracts.token.connect(user) as AnyOldERC20Token
await tokenAsUser.approve(contracts.fundingRound.target, amount)
const fundingRoundAsUser = contracts.fundingRound.connect(
user
) as FundingRound
const tx = await fundingRoundAsUser.contribute(
keypair.pubKey.asContractParam(),
amount
)
return tx
}

describe('Funding Round', () => {
const coordinatorPubKey = new Keypair().pubKey
const roundDuration = 86400 * 7
Expand Down Expand Up @@ -101,21 +133,21 @@ describe('Funding Round', () => {

beforeEach(async () => {
const tokenInitialSupply = UNIT * BigInt(1000000)
const deployed = await deployTestFundingRound(
tokenInitialSupply + budget,
coordinator.address,
coordinatorPubKey,
const deployed = await deployTestFundingRound({
tokenSupply: tokenInitialSupply + budget,
coordinatorAddress: coordinator.address,
coordinatorPubKey: coordinatorPubKey,
roundDuration,
deployer
)
deployer,
})
token = deployed.token
fundingRound = deployed.fundingRound
userRegistry = deployed.mockUserRegistry
recipientRegistry = deployed.mockRecipientRegistry
tally = deployed.mockTally
const mockVerifier = deployed.mockVerifier

// make the verifier to alwasy returns true
// make the verifier to always returns true
await mockVerifier.mock.verify.returns(true)
await userRegistry.mock.isVerifiedUser.returns(true)
await tally.mock.tallyBatchNum.returns(1)
Expand Down Expand Up @@ -205,8 +237,34 @@ describe('Funding Round', () => {
).to.equal(expectedVoiceCredits)
})

it('calculates max contributors correctly', async () => {
const stateTreeDepth = toNumber(await maci.stateTreeDepth())
const maxUsers = MACI_TREE_ARITY ** stateTreeDepth - 1
expect(getMaxContributors(stateTreeDepth)).to.eq(maxUsers)
})

it('limits the number of contributors', async () => {
// TODO: add test later
// use a smaller stateTreeDepth to run the test faster
const stateTreeDepth = 1
const contracts = await deployTestFundingRound({
stateTreeDepth,
tokenSupply: UNIT * BigInt(1000000),
coordinatorAddress: coordinator.address,
coordinatorPubKey,
roundDuration,
deployer,
})
await contracts.mockUserRegistry.mock.isVerifiedUser.returns(true)

const maxUsers = getMaxContributors(stateTreeDepth)
for (let i = 0; i < maxUsers; i++) {
await contributeByRandomUser(contracts, deployer)
}

// this should throw TooManySignups
await expect(
contributeByRandomUser(contracts, deployer)
).to.be.revertedWithCustomError(maci, 'TooManySignups')
})

it('rejects contributions if funding round has been finalized', async () => {
Expand Down
4 changes: 2 additions & 2 deletions contracts/utils/maci.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
hash5,
hash3,
hashLeftRight,
LEAVES_PER_NODE,
MACI_TREE_ARITY,
genTallyResultCommitment,
Keypair,
Tally as TallyData,
Expand Down Expand Up @@ -50,7 +50,7 @@ export function getTallyResultProof(
const resultTree = new IncrementalQuinTree(
recipientTreeDepth,
BigInt(0),
LEAVES_PER_NODE,
MACI_TREE_ARITY,
hash5
)
for (const leaf of tally.results.tally) {
Expand Down
26 changes: 20 additions & 6 deletions contracts/utils/testutils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,13 +169,21 @@ export type DeployTestFundingRoundOutput = {
* @param deployer singer for the contract deployment
* @returns all the deployed objects in DeployTestFundingRoundOutput
*/
export async function deployTestFundingRound(
tokenSupply: bigint,
coordinatorAddress: string,
coordinatorPubKey: PubKey,
roundDuration: number,
export async function deployTestFundingRound({
stateTreeDepth,
tokenSupply,
coordinatorAddress,
coordinatorPubKey,
roundDuration,
deployer,
}: {
stateTreeDepth?: number
tokenSupply: bigint
coordinatorAddress: string
coordinatorPubKey: PubKey
roundDuration: number
deployer: Signer
): Promise<DeployTestFundingRoundOutput> {
}): Promise<DeployTestFundingRoundOutput> {
const token = await ethers.deployContract(
EContracts.AnyOldERC20Token,
[tokenSupply],
Expand Down Expand Up @@ -208,6 +216,12 @@ export async function deployTestFundingRound(
})

const maciParameters = MaciParameters.mock()

// use the stateTreeDepth from input
if (stateTreeDepth != undefined) {
maciParameters.stateTreeDepth = stateTreeDepth
}

const maciFactory = await deployMaciFactory({
libraries,
ethers,
Expand Down
40 changes: 7 additions & 33 deletions subgraph/generated/ClrFund/MACIFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,6 @@ export class MACIFactory__deployMaciResult_pollContractsStruct extends ethereum.
get tally(): Address {
return this[2].toAddress();
}

get subsidy(): Address {
return this[3].toAddress();
}
}

export class MACIFactory__deployMaciResult {
Expand Down Expand Up @@ -124,26 +120,18 @@ export class MACIFactory__factoriesResult {
value0: Address;
value1: Address;
value2: Address;
value3: Address;

constructor(
value0: Address,
value1: Address,
value2: Address,
value3: Address,
) {
constructor(value0: Address, value1: Address, value2: Address) {
this.value0 = value0;
this.value1 = value1;
this.value2 = value2;
this.value3 = value3;
}

toMap(): TypedMap<string, ethereum.Value> {
let map = new TypedMap<string, ethereum.Value>();
map.set("value0", ethereum.Value.fromAddress(this.value0));
map.set("value1", ethereum.Value.fromAddress(this.value1));
map.set("value2", ethereum.Value.fromAddress(this.value2));
map.set("value3", ethereum.Value.fromAddress(this.value3));
return map;
}

Expand All @@ -155,12 +143,8 @@ export class MACIFactory__factoriesResult {
return this.value1;
}

getSubsidyFactory(): Address {
return this.value2;
}

getMessageProcessorFactory(): Address {
return this.value3;
return this.value2;
}
}

Expand Down Expand Up @@ -269,7 +253,7 @@ export class MACIFactory extends ethereum.SmartContract {
): MACIFactory__deployMaciResult {
let result = super.call(
"deployMaci",
"deployMaci(address,address,address,uint256,address,(uint256,uint256),address):(address,(address,address,address,address))",
"deployMaci(address,address,address,uint256,address,(uint256,uint256),address):(address,(address,address,address))",
[
ethereum.Value.fromAddress(signUpGatekeeper),
ethereum.Value.fromAddress(initialVoiceCreditProxy),
Expand Down Expand Up @@ -300,7 +284,7 @@ export class MACIFactory extends ethereum.SmartContract {
): ethereum.CallResult<MACIFactory__deployMaciResult> {
let result = super.tryCall(
"deployMaci",
"deployMaci(address,address,address,uint256,address,(uint256,uint256),address):(address,(address,address,address,address))",
"deployMaci(address,address,address,uint256,address,(uint256,uint256),address):(address,(address,address,address))",
[
ethereum.Value.fromAddress(signUpGatekeeper),
ethereum.Value.fromAddress(initialVoiceCreditProxy),
Expand Down Expand Up @@ -328,22 +312,21 @@ export class MACIFactory extends ethereum.SmartContract {
factories(): MACIFactory__factoriesResult {
let result = super.call(
"factories",
"factories():(address,address,address,address)",
"factories():(address,address,address)",
[],
);

return new MACIFactory__factoriesResult(
result[0].toAddress(),
result[1].toAddress(),
result[2].toAddress(),
result[3].toAddress(),
);
}

try_factories(): ethereum.CallResult<MACIFactory__factoriesResult> {
let result = super.tryCall(
"factories",
"factories():(address,address,address,address)",
"factories():(address,address,address)",
[],
);
if (result.reverted) {
Expand All @@ -355,7 +338,6 @@ export class MACIFactory extends ethereum.SmartContract {
value[0].toAddress(),
value[1].toAddress(),
value[2].toAddress(),
value[3].toAddress(),
),
);
}
Expand Down Expand Up @@ -534,12 +516,8 @@ export class ConstructorCall_factoriesStruct extends ethereum.Tuple {
return this[1].toAddress();
}

get subsidyFactory(): Address {
return this[2].toAddress();
}

get messageProcessorFactory(): Address {
return this[3].toAddress();
return this[2].toAddress();
}
}

Expand Down Expand Up @@ -631,10 +609,6 @@ export class DeployMaciCall_pollContractsStruct extends ethereum.Tuple {
get tally(): Address {
return this[2].toAddress();
}

get subsidy(): Address {
return this[3].toAddress();
}
}

export class RenounceOwnershipCall extends ethereum.Call {
Expand Down
34 changes: 34 additions & 0 deletions subgraph/generated/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1020,6 +1020,40 @@ export class FundingRound extends Entity {
this.set("voteOptionTreeDepth", Value.fromI32(value));
}

get maxMessages(): BigInt | null {
let value = this.get("maxMessages");
if (!value || value.kind == ValueKind.NULL) {
return null;
} else {
return value.toBigInt();
}
}

set maxMessages(value: BigInt | null) {
if (!value) {
this.unset("maxMessages");
} else {
this.set("maxMessages", Value.fromBigInt(<BigInt>value));
}
}

get maxVoteOptions(): BigInt | null {
let value = this.get("maxVoteOptions");
if (!value || value.kind == ValueKind.NULL) {
return null;
} else {
return value.toBigInt();
}
}

set maxVoteOptions(value: BigInt | null) {
if (!value) {
this.unset("maxVoteOptions");
} else {
this.set("maxVoteOptions", Value.fromBigInt(<BigInt>value));
}
}

get coordinatorPubKeyX(): BigInt | null {
let value = this.get("coordinatorPubKeyX");
if (!value || value.kind == ValueKind.NULL) {
Expand Down
Loading

0 comments on commit 30a38a3

Please sign in to comment.