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

feat(poll): add message batch submission #1226

Merged
merged 1 commit into from
Feb 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
21 changes: 21 additions & 0 deletions contracts/contracts/Poll.sol
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
error MaciPubKeyLargerThanSnarkFieldSize();
error StateAqAlreadyMerged();
error StateAqSubtreesNeedMerge();
error InvalidBatchLength();

event PublishMessage(Message _message, PubKey _encPubKey);
event TopupMessage(Message _message);
Expand Down Expand Up @@ -197,6 +198,26 @@
emit PublishMessage(_message, _encPubKey);
}

/// @notice submit a message batch
/// @dev Can only be submitted before the voting deadline
/// @param _messages the messages
/// @param _encPubKeys the encrypted public keys
function publishMessageBatch(Message[] calldata _messages, PubKey[] calldata _encPubKeys) external {

Check warning

Code scanning / Slither

Conformance to Solidity naming conventions Warning

Check warning

Code scanning / Slither

Conformance to Solidity naming conventions Warning

if (_messages.length != _encPubKeys.length) {
revert InvalidBatchLength();
}

uint256 len = _messages.length;
for (uint256 i = 0; i < len; ) {
// an event will be published by this function already
publishMessage(_messages[i], _encPubKeys[i]);

unchecked {
i++;
}
}
}

/// @inheritdoc IPoll
function mergeMaciStateAqSubRoots(uint256 _numSrQueueOps, uint256 _pollId) public onlyOwner isAfterVotingDeadline {
// This function cannot be called after the stateAq was merged
Expand Down
56 changes: 50 additions & 6 deletions contracts/tests/Poll.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ describe("Poll", () => {

const maciState = new MaciState(STATE_TREE_DEPTH);

const keypair = new Keypair();

describe("deployment", () => {
before(async () => {
signer = await getDefaultSigner();
Expand Down Expand Up @@ -175,8 +177,6 @@ describe("Poll", () => {

describe("publishMessage", () => {
it("should publish a message to the Poll contract", async () => {
const keypair = new Keypair();

const command = new PCommand(1n, keypair.pubKey, 0n, 9n, 1n, pollId, 0n);

const signature = command.sign(keypair.privKey);
Expand All @@ -190,8 +190,6 @@ describe("Poll", () => {
});

it("should emit an event when publishing a message", async () => {
const keypair = new Keypair();

const command = new PCommand(1n, keypair.pubKey, 0n, 9n, 1n, pollId, 0n);

const signature = command.sign(keypair.privKey);
Expand All @@ -204,11 +202,45 @@ describe("Poll", () => {
maciState.polls.get(pollId)?.publishMessage(message, keypair.pubKey);
});

it("shold not allow to publish a message after the voting period ends", async () => {
it("should allow to publish a message batch", async () => {
const messages: [Message, PubKey][] = [];
for (let i = 0; i < 2; i += 1) {
const command = new PCommand(1n, keypair.pubKey, 0n, 9n, 1n, pollId, BigInt(i));
const signature = command.sign(keypair.privKey);
const sharedKey = Keypair.genEcdhSharedKey(keypair.privKey, coordinator.pubKey);
const message = command.encrypt(signature, sharedKey);
messages.push([message, keypair.pubKey]);
}

const tx = await pollContract.publishMessageBatch(
messages.map(([m]) => m.asContractParam()),
messages.map(([, k]) => k.asContractParam()),
);
const receipt = await tx.wait();
expect(receipt?.status).to.eq(1);

messages.forEach(([message, key]) => {
maciState.polls.get(pollId)?.publishMessage(message, key);
});
});

it("should throw when the message batch has messages length != encPubKeys length", async () => {
const command = new PCommand(1n, keypair.pubKey, 0n, 9n, 1n, pollId, 0n);
const signature = command.sign(keypair.privKey);
const sharedKey = Keypair.genEcdhSharedKey(keypair.privKey, coordinator.pubKey);
const message = command.encrypt(signature, sharedKey);
await expect(
pollContract.publishMessageBatch(
[message.asContractParam(), message.asContractParam()],
[keypair.pubKey.asContractParam()],
),
).to.be.revertedWithCustomError(pollContract, "InvalidBatchLength");
});

it("should not allow to publish a message after the voting period ends", async () => {
const dd = await pollContract.getDeployTimeAndDuration();
await timeTravel(signer.provider as unknown as EthereumProvider, Number(dd[0]) + 1);

const keypair = new Keypair();
const command = new PCommand(1n, keypair.pubKey, 0n, 9n, 1n, pollId, 0n);

const signature = command.sign(keypair.privKey);
Expand All @@ -219,6 +251,18 @@ describe("Poll", () => {
pollContract.publishMessage(message.asContractParam(), keypair.pubKey.asContractParam(), { gasLimit: 300000 }),
).to.be.revertedWithCustomError(pollContract, "VotingPeriodOver");
});

it("should not allow to publish a message batch after the voting period ends", async () => {
const command = new PCommand(1n, keypair.pubKey, 0n, 9n, 1n, pollId, 0n);
const signature = command.sign(keypair.privKey);
const sharedKey = Keypair.genEcdhSharedKey(keypair.privKey, coordinator.pubKey);
const message = command.encrypt(signature, sharedKey);
await expect(
pollContract.publishMessageBatch([message.asContractParam()], [keypair.pubKey.asContractParam()], {
gasLimit: 300000,
}),
).to.be.revertedWithCustomError(pollContract, "VotingPeriodOver");
});
});

describe("Merge messages", () => {
Expand Down
Loading