Skip to content

Commit

Permalink
feat(contracts): relay messages
Browse files Browse the repository at this point in the history
- [x] Add relay messages method to Poll contract
- [x] Use struct for deploy poll args
- [x] Add relayer addresses to Poll contract
  • Loading branch information
0xmad committed Jan 14, 2025
1 parent ea7eb10 commit abc2739
Show file tree
Hide file tree
Showing 16 changed files with 322 additions and 157 deletions.
24 changes: 14 additions & 10 deletions packages/cli/ts/commands/deployPoll.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export const deployPoll = async ({
gatekeeperAddress,
voiceCreditProxyAddress,
initialVoiceCreditsBalance,
relayers = [],
signer,
quiet = true,
useQuadraticVoting = false,
Expand Down Expand Up @@ -123,18 +124,21 @@ export const deployPoll = async ({
try {
// deploy the poll contract via the maci contract
const tx = await maciContract.deployPoll(
pollDuration,
{
intStateTreeDepth,
voteOptionTreeDepth,
duration: pollDuration,
treeDepths: {
intStateTreeDepth,
voteOptionTreeDepth,
},
messageBatchSize,
coordinatorPubKey: unserializedKey.asContractParam(),
verifier: verifierContractAddress,
vkRegistry,
mode: useQuadraticVoting ? EMode.QV : EMode.NON_QV,
gatekeeper: signupGatekeeperContractAddress,
initialVoiceCreditProxy: initialVoiceCreditProxyAddress,
relayers,
},
messageBatchSize,
unserializedKey.asContractParam(),
verifierContractAddress,
vkRegistry,
useQuadraticVoting ? EMode.QV : EMode.NON_QV,
signupGatekeeperContractAddress,
initialVoiceCreditProxyAddress,
{ gasLimit: 10000000 },
);

Expand Down
2 changes: 2 additions & 0 deletions packages/cli/ts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ program
true,
)
.option("-x, --maci-address <maciAddress>", "the MACI contract address")
.option("-ra, --relayers <relayers>", "the relayer addresses", (value) => value.split(",").map((item) => item.trim()))
.option("-q, --quiet <quiet>", "whether to print values to the console", (value) => value === "true", false)
.option("-r, --rpc-provider <provider>", "the rpc provider URL")
.action(async (cmdObj) => {
Expand All @@ -203,6 +204,7 @@ program
coordinatorPubkey: cmdObj.pubkey,
maciAddress: cmdObj.maciAddress,
vkRegistryAddress: cmdObj.vkRegistryAddress,
relayers: cmdObj.relayers,
quiet: cmdObj.quiet,
useQuadraticVoting: cmdObj.useQuadraticVoting,
signer,
Expand Down
5 changes: 5 additions & 0 deletions packages/cli/ts/utils/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,11 @@ export interface DeployPollArgs {
*/
signer: Signer;

/**
* The relayer addresses
*/
relayers?: string[];

/**
* The MACI contract address
*/
Expand Down
74 changes: 40 additions & 34 deletions packages/contracts/contracts/MACI.sol
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,29 @@ contract MACI is IMACI, DomainObjs, Params, Hasher {
address messageProcessor;
address tally;
}
/// @notice A struct holding the params for poll deployment
struct DeployPollArgs {
/// @param duration How long should the Poll last for
uint256 duration;
/// @param treeDepths The depth of the Merkle trees
TreeDepths treeDepths;
/// @param messageBatchSize The message batch size
uint8 messageBatchSize;
/// @param coordinatorPubKey The coordinator's public key
PubKey coordinatorPubKey;
/// @param verifier The Verifier Contract
address verifier;
/// @param vkRegistry The VkRegistry Contract
address vkRegistry;
/// @param mode Voting mode
Mode mode;
/// @param gatekeeper The gatekeeper contract
address gatekeeper;
/// @param initialVoiceCreditProxy The initial voice credit proxy contract
address initialVoiceCreditProxy;
/// @param relayer The message relayer (optional)
address[] relayers;
}

// Events
event SignUp(uint256 _stateIndex, uint256 _timestamp, uint256 indexed _userPubKeyX, uint256 indexed _userPubKeyY);
Expand Down Expand Up @@ -148,26 +171,8 @@ contract MACI is IMACI, DomainObjs, Params, Hasher {
}

/// @notice Deploy a new Poll contract.
/// @param _duration How long should the Poll last for
/// @param _treeDepths The depth of the Merkle trees
/// @param _messageBatchSize The message batch size
/// @param _coordinatorPubKey The coordinator's public key
/// @param _verifier The Verifier Contract
/// @param _vkRegistry The VkRegistry Contract
/// @param _mode Voting mode
/// @param _gatekeeper The gatekeeper contract
/// @param _initialVoiceCreditProxy The initial voice credit proxy contract
function deployPoll(
uint256 _duration,
TreeDepths memory _treeDepths,
uint8 _messageBatchSize,
PubKey memory _coordinatorPubKey,
address _verifier,
address _vkRegistry,
Mode _mode,
address _gatekeeper,
address _initialVoiceCreditProxy
) public virtual {
/// @param args The deploy poll args
function deployPoll(DeployPollArgs calldata args) public virtual {
// cache the poll to a local variable so we can increment it
uint256 pollId = nextPollId;

Expand All @@ -178,39 +183,40 @@ contract MACI is IMACI, DomainObjs, Params, Hasher {
}

// check coordinator key is a valid point on the curve
if (!CurveBabyJubJub.isOnCurve(_coordinatorPubKey.x, _coordinatorPubKey.y)) {
if (!CurveBabyJubJub.isOnCurve(args.coordinatorPubKey.x, args.coordinatorPubKey.y)) {
revert InvalidPubKey();
}

uint256 voteOptionTreeDepth = _treeDepths.voteOptionTreeDepth;
uint256 voteOptionTreeDepth = args.treeDepths.voteOptionTreeDepth;

ExtContracts memory extContracts = ExtContracts({
maci: IMACI(address(this)),
verifier: IVerifier(_verifier),
vkRegistry: IVkRegistry(_vkRegistry),
gatekeeper: ISignUpGatekeeper(_gatekeeper),
initialVoiceCreditProxy: IInitialVoiceCreditProxy(_initialVoiceCreditProxy)
verifier: IVerifier(args.verifier),
vkRegistry: IVkRegistry(args.vkRegistry),
gatekeeper: ISignUpGatekeeper(args.gatekeeper),
initialVoiceCreditProxy: IInitialVoiceCreditProxy(args.initialVoiceCreditProxy)
});

address p = pollFactory.deploy(
_duration,
_treeDepths,
_messageBatchSize,
_coordinatorPubKey,
args.duration,
args.treeDepths,
args.messageBatchSize,
args.coordinatorPubKey,
extContracts,
emptyBallotRoots[voteOptionTreeDepth - 1],
pollId
pollId,
args.relayers
);

address mp = messageProcessorFactory.deploy(_verifier, _vkRegistry, p, msg.sender, _mode);
address tally = tallyFactory.deploy(_verifier, _vkRegistry, p, mp, msg.sender, _mode);
address mp = messageProcessorFactory.deploy(args.verifier, args.vkRegistry, p, msg.sender, args.mode);
address tally = tallyFactory.deploy(args.verifier, args.vkRegistry, p, mp, msg.sender, args.mode);

// store the addresses in a struct so they can be returned
PollContracts memory pollAddr = PollContracts({ poll: p, messageProcessor: mp, tally: tally });

polls[pollId] = pollAddr;

emit DeployPoll(pollId, _coordinatorPubKey.x, _coordinatorPubKey.y, _mode);
emit DeployPoll(pollId, args.coordinatorPubKey.x, args.coordinatorPubKey.y, args.mode);
}

/// @inheritdoc IMACI
Expand Down
51 changes: 50 additions & 1 deletion packages/contracts/contracts/Poll.sol
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,12 @@ contract Poll is Params, Utilities, SnarkCommon, IPoll {
/// @notice Poll state tree for anonymous joining
LazyIMTData public pollStateTree;

/// @notice IPFS hashes of messages batches
bytes32[] public ipfsHashes;

/// @notice Relayer address
mapping(address => bool) public relayers;

/// @notice The hash of a blank state leaf
uint256 internal constant BLANK_STATE_LEAF_HASH =
uint256(6769006970205099520508948723718471724660867171122235270773600567925038008762);
Expand All @@ -103,6 +109,7 @@ contract Poll is Params, Utilities, SnarkCommon, IPoll {
error BatchHashesAlreadyPadded();
error UserAlreadyJoined();
error InvalidPollProof();
error NotRelayer();

event PublishMessage(Message _message, PubKey _encPubKey);
event MergeState(uint256 indexed _stateRoot, uint256 indexed _numSignups);
Expand Down Expand Up @@ -133,7 +140,8 @@ contract Poll is Params, Utilities, SnarkCommon, IPoll {
PubKey memory _coordinatorPubKey,
ExtContracts memory _extContracts,
uint256 _emptyBallotRoot,
uint256 _pollId
uint256 _pollId,
address[] memory _relayers
) payable {
// check that the coordinator public key is valid
if (!CurveBabyJubJub.isOnCurve(_coordinatorPubKey.x, _coordinatorPubKey.y)) {
Expand All @@ -160,6 +168,14 @@ contract Poll is Params, Utilities, SnarkCommon, IPoll {
emptyBallotRoot = _emptyBallotRoot;
// store the poll id
pollId = _pollId;
// set relayers
for (uint256 index = 0; index < _relayers.length; ) {
relayers[_relayers[index]] = true;

unchecked {
index++;
}
}

unchecked {
numMessages++;
Expand Down Expand Up @@ -189,11 +205,18 @@ contract Poll is Params, Utilities, SnarkCommon, IPoll {
_;
}

/// @notice A modifier that causes the function to revert if the batch hashes is padded
modifier isNotPadded() {
if (isBatchHashesPadded) revert BatchHashesAlreadyPadded();
_;
}

/// @notice A modifer that causes the function to revert if the caller is not a relayer

Check warning on line 214 in packages/contracts/contracts/Poll.sol

View workflow job for this annotation

GitHub Actions / Spell Check with Typos

"modifer" should be "modifier".
modifier onlyRelayer() {
if (!relayers[msg.sender]) revert NotRelayer();
_;
}

/// @notice A modifier that causes the function to revert if the voting period is
/// over
modifier isWithinVotingDeadline() virtual {
Expand Down Expand Up @@ -228,6 +251,32 @@ contract Poll is Params, Utilities, SnarkCommon, IPoll {
emit PublishMessage(_message, _encPubKey);
}

/// @notice Allows relayer to publish messages using IPFS.
/// @param _messageHashes The message hashes
/// @param _ipfsHash The IPFS hash of the messages batch
function relayMessagesBatch(
uint256[] calldata _messageHashes,

Check warning

Code scanning / Slither

Conformance to Solidity naming conventions Warning

bytes32 _ipfsHash

Check warning

Code scanning / Slither

Conformance to Solidity naming conventions Warning

) public virtual isWithinVotingDeadline onlyRelayer {
uint256 length = _messageHashes.length;

unchecked {
numMessages += length;
}

for (uint256 index = 0; index < length; ) {
updateChainHash(_messageHashes[index]);

unchecked {
index++;
}
}

ipfsHashes.push(_ipfsHash);

emit IpfsHashAdded(_ipfsHash);
}

/// @notice compute and update current message chain hash
/// @param messageHash hash of the current message
function updateChainHash(uint256 messageHash) internal {
Expand Down
6 changes: 4 additions & 2 deletions packages/contracts/contracts/PollFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ contract PollFactory is Params, DomainObjs, IPollFactory {
DomainObjs.PubKey calldata _coordinatorPubKey,
Params.ExtContracts calldata _extContracts,
uint256 _emptyBallotRoot,
uint256 _pollId
uint256 _pollId,

Check warning

Code scanning / Slither

Conformance to Solidity naming conventions Warning

address[] calldata _relayers

Check warning

Code scanning / Slither

Conformance to Solidity naming conventions Warning

) public virtual returns (address pollAddr) {
// deploy the poll
Poll poll = new Poll(
Expand All @@ -32,7 +33,8 @@ contract PollFactory is Params, DomainObjs, IPollFactory {
_coordinatorPubKey,
_extContracts,
_emptyBallotRoot,
_pollId
_pollId,
_relayers
);

pollAddr = address(poll);
Expand Down
4 changes: 3 additions & 1 deletion packages/contracts/contracts/interfaces/IPollFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ interface IPollFactory {
/// @param _extContracts The external contracts interface references
/// @param _emptyBallotRoot The root of the empty ballot tree
/// @param _pollId The poll id
/// @param _relayers The message relayers (optional)
/// @return The deployed Poll contract
function deploy(
uint256 _duration,
Expand All @@ -23,6 +24,7 @@ interface IPollFactory {
DomainObjs.PubKey calldata _coordinatorPubKey,
Params.ExtContracts calldata _extContracts,
uint256 _emptyBallotRoot,
uint256 _pollId
uint256 _pollId,
address[] calldata _relayers
) external returns (address);
}
Loading

0 comments on commit abc2739

Please sign in to comment.