# MACI v1.0: top-up voice credits ## Introduction In previous design, when an user signup, she will get an initial voice credit balance. This info is stored in corresponding stateLeaf. For multiple polls, an user can spend credits upto this initial balance. e.g. If an user has initial 100 credits. Then she can spend a total of 100 credits for each poll. If she wants to spend more than initial balance, she has to signup again. The purpose of top-up feature is to allow users to add more credits without signup again. ## Design One approach is to modify the voiceCreditBalance on the stateLeaf each time when user deposit new credits. This is the current approach we are going to take. ### Proposal Notice that an user cannot just arbitrarily send a topup command to add credits. Instead, we add an ERC20 contract to store voting credits. Then the user can use the topup function in poll.sol to deposit tokens into the contract address. In the current design, we don't allow users to withdraw their tokens. The reason is that withdraw feature can allow malicious one to bribe others offline to transfer their keys. Without withdraw function, the malicious cannot distinguish whether key transferred is valid or not. Since now the poll.sol contract address will hold all the funds deposit from users for the current pollID. At the end of poll, the coordinator (or anyone) can transfer funds in poll.sol into hardcoded address which is used for public goods. When an user deposit tokens by calling topup, she will also need to specify the stateTree index. The topup function will insert a topup message into the message queue for her. When the voting period is end, any call of topup function will be rejected. Both voting and topup messages have the same ending time, which ensures we have a well-defined ending state for each poll. Notice in this approach, the initialCredit is still shared across multiple polls, and the actual credit an user can spend in a given poll is the following: `totalCredit=initialCredit+topupCredit` where the topupCredit is the voice credit deposit by the user during the voting period of the given pollID. ### domainobjs We have two types of messages: voting and topup. In previous design, the voting message is encrypted and stored in smart contracts. For topup message, which is created by topup function, we don't need to encrypt. However, when we process messages, we need distinguish these two type of messages. The idea is to add a new field to identify type of messages. ``` struct MessageLeaf { uint256 type; // 0x00 voting, 0x01 topup uint256[N] message; } ``` The message field will be interpret differently for different types. ### core core/ts/MaciState.ts: Now there are two types of messages: voting and topup. Add logic to process topup type message. ### circuits Add circuit logic to process topup message, update balance in stateTree leaf for topup message. Add necessary conditions check, for example: `totalCredit=initialCredit+topupCredit >= spentCredit` ### contracts contracts/contracts/ERC20.sol: standard ERC20 tokens contracts/contracts/Poll.sol: `function topup(amount uint256, stateIndex uint256)`. This function will do the following: transfer ERC20 tokens from an user into this contract address (poll.sol), insert "topup" type message into messageAq. We don't allow people to withdraw the funds from the Poll.sol, because the attacker can ask people to withdraw funds to make sure their keys are registered and valid before bribing them. This will make the bribe attack possible. contracts/ts/genMaciState.ts: Add action of topup. `action['type'] === "topup"`. This can be done by filtering the event logs from poll.sol, and construct the state from there. ### cli Add topup command which will send transaction to poll.sol. ### Remarks With above design, the order of vote type and topup type message matters. MACI process the message queue in reverse order. Suppose the initial credit balance for any user is $100$. Consider the following two scenarios in message queue: ``` # case 1 [topup(balance=350), vote(weight=20,nonce=2), vote(weight=9,nonce=1)] # case 2 [vote(weight=20,nonce=2), topup(balance=350), vote(weight=9,nonce=1)] ``` The first case, the topup message is processed at last, so the `vote(weight=20,nonce=2)` will fail because $20*20 > 100$. `vote(weight=9,nonce=1)` is the final result. In the second case, the topup message is processed before the second vote, so `vote(weight=20,nonce=2)` will invalidate the first vote and become the final result. ## reference https://hackmd.io/@ef-zkp/rk6uaQBrI ###### tags: `public` `maci`