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

Fix blank leaderboard view for canceled rounds #769

Merged
merged 69 commits into from
Jul 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
7d31cd8
remove brightid sponsorship as sponsorship is done in the BrightID ap…
yuetloo Apr 24, 2024
45fe06d
remove brightid sponsorship logic
yuetloo Apr 24, 2024
c638d80
fix missing duration error
yuetloo Apr 24, 2024
4ee0e4f
Merge pull request #747 from clrfund/fix/deploy-local-duration-missin…
yuetloo Apr 24, 2024
bb2a915
Merge remote-tracking branch 'origin/develop' into chore/remove-brigh…
yuetloo Apr 24, 2024
9dafeb6
remove goerli and add sepolia
yuetloo Apr 24, 2024
7b097b0
remove BrightId sponsorship instructions
yuetloo Apr 24, 2024
f04320d
update numbering
yuetloo Apr 24, 2024
6a84556
remove special netlify lambda settings
yuetloo Apr 24, 2024
d5bf859
do not show verify page if using semaphore user registry
yuetloo Apr 25, 2024
e4d8d20
add sepolia and remove goerli
yuetloo Apr 25, 2024
350232f
Merge remote-tracking branch 'origin/feat/add-sepolia' into feat/add-…
yuetloo Apr 25, 2024
5102e77
handle user not registered case for semaphore user registry
yuetloo Apr 25, 2024
880e96b
Merge pull request #750 from clrfund/feat/add-sepolia
yuetloo Apr 25, 2024
08dc551
add semaphore user registry
yuetloo Apr 25, 2024
9ff8f87
Merge pull request #751 from clrfund/feat/add-semaphore-user-registry
yuetloo Apr 25, 2024
ef12b74
use maci 0.0.0-ci.153326b: do not allow invalid pub key
yuetloo May 6, 2024
65b4c96
Merge pull request #748 from clrfund/chore/remove-brightid-sponsorshi…
yuetloo May 8, 2024
217049b
Merge pull request #742 from clrfund/feat/no-subgraph
yuetloo May 8, 2024
3969bac
verify validity of public key using babyjubjub function
yuetloo May 9, 2024
bcdd62c
use qualified contract name
yuetloo May 9, 2024
4e3d72a
v5.2.0
May 9, 2024
2ff9b0b
use qualified name for Poll
yuetloo May 9, 2024
e78fc12
make script maci-pubkey available
yuetloo May 9, 2024
6bbd012
validate key using the serialize function
yuetloo May 10, 2024
0ff9b2b
add test for common package
yuetloo May 13, 2024
b031c8e
add test common to pre push script
yuetloo May 13, 2024
2f980e2
add test:common script
yuetloo May 13, 2024
3724763
fix contract verification error
yuetloo May 13, 2024
d8cd50c
use MACI v1.2.1
yuetloo May 16, 2024
bb48206
use hardhat ^v2.22.3 to fix Invalid Chai property: revertedWith
yuetloo May 16, 2024
4194fea
use MACI v1.2.2
yuetloo May 16, 2024
e3ff605
remove unused variable
yuetloo May 17, 2024
5d5d67e
remove lowdb since it is added in maci-contracts now
yuetloo May 17, 2024
3a51436
use ethers 6.12.1
yuetloo May 17, 2024
916e5e2
update waffle-mock-contract to v0.0.11
yuetloo May 17, 2024
8f79a27
use ethers v6.12.1
yuetloo May 19, 2024
98611ce
update for MACI v1.2.2 interface change
yuetloo May 19, 2024
d717356
fix decode recipient metadata error
yuetloo May 19, 2024
43b161d
remove an issue already fixed in MACI
yuetloo May 20, 2024
36743e4
Merge pull request #754 from clrfund/fix/invalid-pub-key
yuetloo May 20, 2024
30a38a3
fix incorrect maxContributors, maxMessages and maxVoteOptions
yuetloo May 21, 2024
465a116
votingDuration is obsolete post MACI v1
yuetloo May 21, 2024
f9d90ab
remove hardcoding of TREE_ARITY as its value can change between MACI …
yuetloo May 22, 2024
dd028f9
fix incorrect entity id mapping when contract was called from another…
yuetloo May 22, 2024
c71e354
update recipient registry address
yuetloo May 22, 2024
3d47629
update clrfund with recipient registry address
yuetloo May 22, 2024
729eadc
refactor code for readability
yuetloo May 22, 2024
6212a32
Merge pull request #755 from clrfund/fix/incorrect-max-contributor
yuetloo May 24, 2024
9aea7fe
fix typescript error by building on the same job
yuetloo May 24, 2024
47ae670
remove test common due to mocha error
yuetloo May 24, 2024
418f53b
v5.2.1
May 24, 2024
dc27136
Merge pull request #756 from clrfund/fix/create-version-error
yuetloo May 24, 2024
58b93b4
v6.0.0
May 24, 2024
1d8a6eb
fix missing arguments
yuetloo Jun 4, 2024
29900ee
add missing user and recipient registries
yuetloo Jun 4, 2024
1d08b13
add missing sepolia etherscan url
yuetloo Jun 4, 2024
12a4643
fix reallocation error due to incorrect subgraph mapping for public key
yuetloo Jun 4, 2024
07644d2
generate subgraph pubKeyId using maciAddress
yuetloo Jun 4, 2024
65dc41b
remove incorrect subgraph id calculation
yuetloo Jun 4, 2024
3171017
Merge pull request #759 from clrfund/fix/verify-export-issue
yuetloo Jun 17, 2024
d535d90
redirect to projects view if no tally data
yuetloo Jul 8, 2024
ecd6155
redirect to projects view if no tally data
yuetloo Jul 8, 2024
547561d
Merge remote-tracking branch 'origin/fix/blank-leaderboard' into merg…
yuetloo Jul 8, 2024
b17fed6
refactor to get data from static rounds if subgraph is not available
yuetloo Jul 10, 2024
b4416e0
remove obsolete functions
yuetloo Jul 10, 2024
b627ae2
use wrapper to handle subgraph error in graphql sdk
yuetloo Jul 10, 2024
0ddf280
get recipient data statically if subgraph is not available
yuetloo Jul 10, 2024
6d0ae77
refactor static round recipient finding logic
yuetloo Jul 10, 2024
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: 2 additions & 6 deletions .github/workflows/create-version.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,14 @@ jobs:
node-version: ${{ env.NODE_VERSION }}
- name: Checkout source code
uses: actions/checkout@v3
- name: Install dependencies
- name: Create new version
run: |
# use https to avoid error: unable to connect to github.com
git config --global url."https://".insteadOf git://
yarn && yarn build
- name: setup git config
run: |
# setup the username and email. I tend to use 'GitHub Actions Bot' with no email by default
git config user.name "GitHub Actions Bot"
git config user.email "<>"
- name: Create new version
run: |
yarn && yarn build
echo "Version: ${{ github.event.inputs.version }}"
cd contracts
npm version ${{ github.event.inputs.version }}
Expand Down
6 changes: 3 additions & 3 deletions common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@
},
"dependencies": {
"@openzeppelin/merkle-tree": "^1.0.5",
"ethers": "^6.11.1",
"maci-crypto": "^1.2.0",
"maci-domainobjs": "^1.2.0"
"ethers": "^6.12.1",
"maci-crypto": "1.2.2",
"maci-domainobjs": "1.2.2"
},
"repository": {
"type": "git",
Expand Down
21 changes: 21 additions & 0 deletions common/src/__tests__/keypair.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { expect } from 'chai'
import { Keypair, PubKey } from '../keypair'
import { Wallet, sha256, randomBytes } from 'ethers'

describe('keypair', function () {
for (let i = 0; i < 10; i++) {
it(`should generate key ${i} from seed successfully`, function () {
const wallet = Wallet.createRandom()
const signature = wallet.signMessageSync(randomBytes(32).toString())
const seed = sha256(signature)
const keypair = Keypair.createFromSeed(seed)
expect(keypair.pubKey.serialize()).to.match(/^macipk./)
})
}

it('should throw if pubKey is invalid', () => {
expect(() => {
new PubKey([1n, 1n])
}).to.throw('PubKey not on curve')
})
})
38 changes: 16 additions & 22 deletions common/src/keypair.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,31 @@
import { keccak256, isBytesLike, concat, toBeArray } from 'ethers'
import { Keypair as MaciKeypair, PrivKey, PubKey } from 'maci-domainobjs'

const SNARK_FIELD_SIZE = BigInt(
'21888242871839275222246405745257275088548364400416034343698204186575808495617'
)

/**
* Returns a BabyJub-compatible value. This function is modified from
* the MACI's genRandomBabyJubValue(). Instead of returning random value
* for the private key, it derives the private key from the users
* signature hash
* Derives the MACI private key from the users signature hash
* @param hash - user's signature hash
* @return The MACI private key
*/
function genPrivKey(hash: string): PrivKey {
// Prevent modulo bias
//const lim = BigInt('0x10000000000000000000000000000000000000000000000000000000000000000')
//const min = (lim - SNARK_FIELD_SIZE) % SNARK_FIELD_SIZE
const min = BigInt(
'6350874878119819312338956282401532410528162663560392320966563075034087161851'
)

if (!isBytesLike(hash)) {
throw new Error(`Hash must be a hex string: ${hash}`)
throw new Error(`genPrivKey() error. Hash must be a hex string: ${hash}`)
}

let hashBN = BigInt(hash)
// don't think we'll enter the for loop below, but, just in case
for (let counter = 1; hashBN < min; counter++) {
const data = concat([toBeArray(hashBN), toBeArray(counter)])
hashBN = BigInt(keccak256(data))
let rawPrivKey = BigInt(hash)
let pubKey: PubKey | null = null

for (let counter = 1; pubKey === null; counter++) {
try {
const privKey = new PrivKey(rawPrivKey)
// this will throw 'Invalid public key' if key is not on the Baby Jubjub elliptic curve
const keypair = new Keypair(privKey)
pubKey = keypair.pubKey
} catch {
const data = concat([toBeArray(rawPrivKey), toBeArray(counter)])
rawPrivKey = BigInt(keccak256(data))
}
}

const rawPrivKey = hashBN % SNARK_FIELD_SIZE
return new PrivKey(rawPrivKey)
}

Expand Down
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,
}
2 changes: 1 addition & 1 deletion contracts/contracts/AnyOldERC20Token.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.10;
pragma solidity 0.8.20;

import '@openzeppelin/contracts/token/ERC20/ERC20.sol';

Expand Down
2 changes: 1 addition & 1 deletion contracts/contracts/CloneFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

pragma solidity 0.8.10;
pragma solidity 0.8.20;

contract CloneFactory { // implementation of eip-1167 - see https://eips.ethereum.org/EIPS/eip-1167
function createClone(address target) internal returns (address result) {
Expand Down
35 changes: 15 additions & 20 deletions contracts/contracts/ClrFund.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.10;
pragma solidity 0.8.20;

import '@openzeppelin/contracts/token/ERC20/ERC20.sol';
import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
Expand Down Expand Up @@ -60,8 +60,14 @@ contract ClrFund is OwnableUpgradeable, DomainObjs, Params {
error InvalidFundingRoundFactory();
error InvalidMaciFactory();
error RecipientRegistryNotSet();
error MaxRecipientsNotSet();
error NotInitialized();
error VoteOptionTreeDepthNotSet();

modifier maciFactoryInitialized() {
if (address(maciFactory) == address(0)) revert InvalidMaciFactory();
if (maciFactory.maxRecipients() == 0) revert MaxRecipientsNotSet();
_;
}


/**
Expand Down Expand Up @@ -128,20 +134,6 @@ contract ClrFund is OwnableUpgradeable, DomainObjs, Params {
_setFundingRoundFactory(_roundFactory);
}

/**
* @dev Get the maximum recipients allowed in the recipient registry
*/
function getMaxRecipients() public view returns (uint256 _maxRecipients) {
TreeDepths memory treeDepths = maciFactory.treeDepths();
if (treeDepths.voteOptionTreeDepth == 0) revert VoteOptionTreeDepthNotSet();

uint256 maxVoteOption = maciFactory.TREE_ARITY() ** treeDepths.voteOptionTreeDepth;

// -1 because the first slot of the recipients array is not used
// and maxRecipients is used to generate 0 based index to the array
_maxRecipients = maxVoteOption - 1;
}

/**
* @dev Set registry of verified users.
* @param _userRegistry Address of a user registry.
Expand All @@ -162,10 +154,13 @@ contract ClrFund is OwnableUpgradeable, DomainObjs, Params {
function setRecipientRegistry(IRecipientRegistry _recipientRegistry)
external
onlyOwner
maciFactoryInitialized
{

recipientRegistry = _recipientRegistry;
uint256 maxRecipients = getMaxRecipients();
recipientRegistry.setMaxRecipients(maxRecipients);

// Make sure that the max number of recipients is set correctly
recipientRegistry.setMaxRecipients(maciFactory.maxRecipients());

emit RecipientRegistryChanged(address(_recipientRegistry));
}
Expand Down Expand Up @@ -220,6 +215,7 @@ contract ClrFund is OwnableUpgradeable, DomainObjs, Params {
)
external
onlyOwner
maciFactoryInitialized
{
IFundingRound currentRound = getCurrentRound();
if (address(currentRound) != address(0) && !currentRound.isFinalized()) {
Expand All @@ -229,8 +225,7 @@ contract ClrFund is OwnableUpgradeable, DomainObjs, Params {
if (address(recipientRegistry) == address(0)) revert RecipientRegistryNotSet();

// Make sure that the max number of recipients is set correctly
uint256 maxRecipients = getMaxRecipients();
recipientRegistry.setMaxRecipients(maxRecipients);
recipientRegistry.setMaxRecipients(maciFactory.maxRecipients());

// Deploy funding round and MACI contracts
address newRound = roundFactory.deploy(duration, address(this));
Expand Down
4 changes: 2 additions & 2 deletions contracts/contracts/ClrFundDeployer.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.10;
pragma solidity 0.8.20;

import {MACIFactory} from './MACIFactory.sol';
import {ClrFund} from './ClrFund.sol';
Expand All @@ -9,7 +9,7 @@ import {SignUpGatekeeper} from "maci-contracts/contracts/gatekeepers/SignUpGatek
import {InitialVoiceCreditProxy} from "maci-contracts/contracts/initialVoiceCreditProxy/InitialVoiceCreditProxy.sol";
import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';

contract ClrFundDeployer is CloneFactory, Ownable {
contract ClrFundDeployer is CloneFactory, Ownable(msg.sender) {
address public clrfundTemplate;
address public maciFactory;
address public roundFactory;
Expand Down
3 changes: 1 addition & 2 deletions contracts/contracts/ExternalContacts.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.10;
pragma solidity 0.8.20;

/*
* These imports are just for hardhat to find the contracts for deployment
Expand All @@ -9,5 +9,4 @@ pragma solidity ^0.8.10;
import {Poll} from 'maci-contracts/contracts/Poll.sol';
import {PollFactory} from 'maci-contracts/contracts/PollFactory.sol';
import {TallyFactory} from 'maci-contracts/contracts/TallyFactory.sol';
import {SubsidyFactory} from 'maci-contracts/contracts/SubsidyFactory.sol';
import {MessageProcessorFactory} from 'maci-contracts/contracts/MessageProcessorFactory.sol';
45 changes: 19 additions & 26 deletions contracts/contracts/FundingRound.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.10;
pragma solidity 0.8.20;

import '@openzeppelin/contracts/access/Ownable.sol';
import '@openzeppelin/contracts/token/ERC20/ERC20.sol';
Expand All @@ -15,7 +15,7 @@ import {SignUpGatekeeper} from 'maci-contracts/contracts/gatekeepers/SignUpGatek
import {InitialVoiceCreditProxy} from 'maci-contracts/contracts/initialVoiceCreditProxy/InitialVoiceCreditProxy.sol';
import {CommonUtilities} from 'maci-contracts/contracts/utilities/CommonUtilities.sol';
import {SnarkCommon} from 'maci-contracts/contracts/crypto/SnarkCommon.sol';
import {ITallySubsidyFactory} from 'maci-contracts/contracts/interfaces/ITallySubsidyFactory.sol';
import {ITallyFactory} from 'maci-contracts/contracts/interfaces/ITallyFactory.sol';
import {IMessageProcessorFactory} from 'maci-contracts/contracts/interfaces/IMPFactory.sol';
import {IClrFund} from './interfaces/IClrFund.sol';
import {IMACIFactory} from './interfaces/IMACIFactory.sol';
Expand All @@ -25,7 +25,7 @@ import './userRegistry/IUserRegistry.sol';
import './recipientRegistry/IRecipientRegistry.sol';

contract FundingRound is
Ownable,
Ownable(msg.sender),
SignUpGatekeeper,
InitialVoiceCreditProxy,
DomainObjs,
Expand Down Expand Up @@ -66,6 +66,7 @@ contract FundingRound is
error TallyHashNotPublished();
error IncompleteTallyResults(uint256 total, uint256 actual);
error NoVotes();
error NoSignUps();
error MaciNotSet();
error PollNotSet();
error InvalidMaci();
Expand Down Expand Up @@ -175,19 +176,6 @@ contract FundingRound is
return (addressValue == address(0));
}

/**
* @dev Have the votes been tallied
*/
function isTallied() private view returns (bool) {
(uint256 numSignUps, ) = poll.numSignUpsAndMessages();
(uint8 intStateTreeDepth, , , ) = poll.treeDepths();
uint256 tallyBatchSize = TREE_ARITY ** uint256(intStateTreeDepth);
uint256 tallyBatchNum = tally.tallyBatchNum();
uint256 totalTallied = tallyBatchNum * tallyBatchSize;

return numSignUps > 0 && totalTallied >= numSignUps;
}

/**
* @dev Set the tally contract
* @param _tally The tally contract address
Expand Down Expand Up @@ -221,10 +209,10 @@ contract FundingRound is
address vkRegistry = address(tally.vkRegistry());

IMessageProcessorFactory messageProcessorFactory = maci.messageProcessorFactory();
ITallySubsidyFactory tallyFactory = maci.tallyFactory();
ITallyFactory tallyFactory = maci.tallyFactory();

address mp = messageProcessorFactory.deploy(verifier, vkRegistry, address(poll), coordinator);
address newTally = tallyFactory.deploy(verifier, vkRegistry, address(poll), mp, coordinator);
address mp = messageProcessorFactory.deploy(verifier, vkRegistry, address(poll), coordinator, Mode.QV);
address newTally = tallyFactory.deploy(verifier, vkRegistry, address(poll), mp, coordinator, Mode.QV);
_setTally(newTally);
}

Expand Down Expand Up @@ -470,18 +458,18 @@ contract FundingRound is

_votingPeriodOver(poll);

if (!isTallied()) {
if (!tally.isTallied()) {
revert VotesNotTallied();
}

if (bytes(tallyHash).length == 0) {
revert TallyHashNotPublished();
}

// make sure we have received all the tally results
(,,, uint8 voteOptionTreeDepth) = poll.treeDepths();
uint256 totalResults = uint256(TREE_ARITY) ** uint256(voteOptionTreeDepth);
if ( totalTallyResults != totalResults ) {
revert IncompleteTallyResults(totalResults, totalTallyResults);
(, uint256 maxVoteOptions) = poll.maxValues();
if (totalTallyResults != maxVoteOptions) {
revert IncompleteTallyResults(maxVoteOptions, totalTallyResults);
}

// If nobody voted, the round should be cancelled to avoid locking of matching funds
Expand All @@ -494,7 +482,6 @@ contract FundingRound is
revert IncorrectSpentVoiceCredits();
}


totalSpent = _totalSpent;
// Total amount of spent voice credits is the size of the pool of direct rewards.
// Everything else, including unspent voice credits and downscaling error,
Expand Down Expand Up @@ -675,9 +662,15 @@ contract FundingRound is
{
if (isAddressZero(address(maci))) revert MaciNotSet();

if (!isTallied()) {
if (maci.numSignUps() == 0) {
// no sign ups, so no tally results
revert NoSignUps();
}

if (!tally.isTallied()) {
revert VotesNotTallied();
}

if (isFinalized) {
revert RoundAlreadyFinalized();
}
Expand Down
Loading