From a121628b352a2e7b670a09988566d9e10f09509b Mon Sep 17 00:00:00 2001 From: matiwinnetou Date: Thu, 14 Sep 2023 13:06:39 +0200 Subject: [PATCH] Feature: keep memory low even for large number of votes. (#204) * Feature: keep memory low even for large number of votes. * Fix: bug fix. * Fix: name change * fix: thread pool config for 1 thread. --------- Co-authored-by: Mateusz Czeladka --- .../MetadataSerialiser.java | 4 +- .../cardano/foundation/voting/VotingApp.java | 2 + .../voting/config/ThreadPoolsConfig.java | 36 ------ .../voting/domain/L1MerkleCommitment.java | 6 +- .../voting/domain/VoteSerialisations.java | 26 +++++ .../foundation/voting/domain/entity/Vote.java | 19 --- .../voting/jobs/VoteCommitmentJob.java | 22 ++-- .../voting/repository/VoteRepository.java | 10 +- .../DefaultLeaderBoardService.java | 14 +-- .../merkle_tree/VoteCommitmentService.java | 110 +++++++++++------- .../MetadataSerialiser.java | 2 +- .../service/vote/DefaultVoteService.java | 5 +- .../voting/service/vote/VoteService.java | 7 +- .../src/main/resources/application.properties | 8 +- .../voting/domain/CoseWrappedVote.java | 13 --- .../verify/VoteVerificationService.java | 2 +- .../voting/utils/VoteSerialisations.java | 26 +++++ 17 files changed, 165 insertions(+), 147 deletions(-) delete mode 100644 backend-services/voting-app/src/main/java/org/cardano/foundation/voting/config/ThreadPoolsConfig.java create mode 100644 backend-services/voting-app/src/main/java/org/cardano/foundation/voting/domain/VoteSerialisations.java create mode 100644 backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/utils/VoteSerialisations.java diff --git a/backend-services/voting-admin-app/src/main/java/org/cardano/foundation/voting/service/transaction_submit/MetadataSerialiser.java b/backend-services/voting-admin-app/src/main/java/org/cardano/foundation/voting/service/transaction_submit/MetadataSerialiser.java index 6d151b8c1..d937bfd28 100644 --- a/backend-services/voting-admin-app/src/main/java/org/cardano/foundation/voting/service/transaction_submit/MetadataSerialiser.java +++ b/backend-services/voting-admin-app/src/main/java/org/cardano/foundation/voting/service/transaction_submit/MetadataSerialiser.java @@ -18,7 +18,8 @@ @Service public class MetadataSerialiser { - public MetadataMap serialise(CreateEventCommand createEventCommand, long slot) { + public MetadataMap serialise(CreateEventCommand createEventCommand, + long slot) { var map = MetadataBuilder.createMap(); map.put("type", EVENT_REGISTRATION.name()); @@ -28,6 +29,7 @@ public MetadataMap serialise(CreateEventCommand createEventCommand, long slot) { map.put("votingEventType", createEventCommand.getVotingEventType().name()); map.put("schemaVersion", createEventCommand.getSchemaVersion().getSemVer()); map.put("creationSlot", BigInteger.valueOf(slot)); + map.put("votesHashAlgo", "BLAKE2b-256"); if (List.of(STAKE_BASED, BALANCE_BASED).contains(createEventCommand.getVotingEventType())) { map.put("startEpoch", BigInteger.valueOf(createEventCommand.getStartEpoch().orElseThrow())); diff --git a/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/VotingApp.java b/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/VotingApp.java index 4b8f1858b..73c7e7663 100644 --- a/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/VotingApp.java +++ b/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/VotingApp.java @@ -19,6 +19,7 @@ import org.springframework.context.annotation.ImportRuntimeHints; import org.springframework.core.io.ClassPathResource; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.transaction.annotation.EnableTransactionManagement; @@ -37,6 +38,7 @@ @EnableScheduling @Slf4j @ImportRuntimeHints(VotingApp.Hints.class) +@EnableAsync public class VotingApp { public static void main(String[] args) { diff --git a/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/config/ThreadPoolsConfig.java b/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/config/ThreadPoolsConfig.java deleted file mode 100644 index 94d775f56..000000000 --- a/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/config/ThreadPoolsConfig.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.cardano.foundation.voting.config; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; - -import java.util.concurrent.Executor; - -@Configuration -public class ThreadPoolsConfig { - - @Bean(name = "taskExecutor") - public Executor taskExecutor() { - ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor(); - threadPoolTaskExecutor.setQueueCapacity(100); - - threadPoolTaskExecutor.setCorePoolSize(2); - - threadPoolTaskExecutor.setMaxPoolSize(5); - - return threadPoolTaskExecutor; - } - - @Bean(name = "asyncExecutor") - public Executor threadPoolTaskExecutor() { - ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor(); - threadPoolTaskExecutor.setQueueCapacity(100); - - threadPoolTaskExecutor.setCorePoolSize(2); - - threadPoolTaskExecutor.setMaxPoolSize(5); - - return threadPoolTaskExecutor; - } - -} diff --git a/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/domain/L1MerkleCommitment.java b/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/domain/L1MerkleCommitment.java index 8bb3c71fc..5bd5c2ccd 100644 --- a/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/domain/L1MerkleCommitment.java +++ b/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/domain/L1MerkleCommitment.java @@ -1,9 +1,11 @@ package org.cardano.foundation.voting.domain; -import org.cardano.foundation.voting.domain.entity.Vote; +import org.cardano.foundation.voting.repository.VoteRepository; import org.cardanofoundation.merkle.MerkleElement; import java.util.List; // L1 Merkle commitment for an event -public record L1MerkleCommitment(List votes, MerkleElement root, String eventId) { } \ No newline at end of file +public record L1MerkleCommitment(List signedVotes, + MerkleElement root, + String eventId) { } \ No newline at end of file diff --git a/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/domain/VoteSerialisations.java b/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/domain/VoteSerialisations.java new file mode 100644 index 000000000..558fb8425 --- /dev/null +++ b/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/domain/VoteSerialisations.java @@ -0,0 +1,26 @@ +package org.cardano.foundation.voting.domain; + +import org.cardano.foundation.voting.repository.VoteRepository; +import org.cardanofoundation.cip30.CIP30Verifier; + +import java.util.Optional; +import java.util.function.Function; + +import static com.bloxbean.cardano.client.crypto.Blake2bUtil.blake2bHash256; + +public final class VoteSerialisations { + + public static final Function VOTE_SERIALISER = createSerialiserFunction(); + + public static Function createSerialiserFunction() { + return vote -> { + var cip30Verifier = new CIP30Verifier(vote.getCoseSignature(), vote.getCosePublicKey()); + var verificationResult = cip30Verifier.verify(); + + var bytes = Optional.ofNullable(verificationResult.getMessage()).orElse(new byte[0]); + + return blake2bHash256(bytes); + }; + } + +} diff --git a/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/domain/entity/Vote.java b/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/domain/entity/Vote.java index 625c70706..2b76eb49a 100644 --- a/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/domain/entity/Vote.java +++ b/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/domain/entity/Vote.java @@ -9,11 +9,9 @@ import lombok.NoArgsConstructor; import lombok.ToString; import lombok.extern.slf4j.Slf4j; -import org.cardanofoundation.cip30.CIP30Verifier; import javax.annotation.Nullable; import java.util.Optional; -import java.util.function.Function; @NoArgsConstructor @AllArgsConstructor @@ -24,8 +22,6 @@ @ToString(exclude = { "coseSignature", "cosePublicKey"} ) public class Vote extends AbstractTimestampEntity { - public static final Function VOTE_SERIALISER = createSerialiserFunction(); - @Id @Column(name = "id", nullable = false) private String id; @@ -130,19 +126,4 @@ public void setVotedAtSlot(long votedAtSlot) { this.votedAtSlot = votedAtSlot; } - private static Function createSerialiserFunction() { - return vote -> { - var cip30Verifier = new CIP30Verifier(vote.getCoseSignature(), vote.getCosePublicKey()); - var verificationResult = cip30Verifier.verify(); - - if (!verificationResult.isValid()) { - log.info("Verifying vote failed: {}", verificationResult.getMessage()); - - return new byte[0]; - } - - return Optional.ofNullable(verificationResult.getMessage()).orElse(new byte[0]); - }; - } - } diff --git a/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/jobs/VoteCommitmentJob.java b/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/jobs/VoteCommitmentJob.java index aa4e5dd16..303d0bbe1 100644 --- a/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/jobs/VoteCommitmentJob.java +++ b/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/jobs/VoteCommitmentJob.java @@ -1,35 +1,33 @@ package org.cardano.foundation.voting.jobs; +import io.micrometer.core.annotation.Timed; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.cardano.foundation.voting.service.merkle_tree.VoteCommitmentService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.springframework.util.StopWatch; @Slf4j @Service +@RequiredArgsConstructor +@ConditionalOnProperty(prefix = "vote.commitment", value = "enabled", havingValue = "true") public class VoteCommitmentJob implements Runnable { - @Autowired - private VoteCommitmentService voteCommitmentService; - - @Value("${vote.commitment.enabled}") - private boolean isVoteCommitmentEnabled; + private final VoteCommitmentService voteCommitmentService; @Override @Scheduled(cron = "${vote.commitment.cron.expression}") + @Timed(value = "vote.commitment.cron.job", histogram = true) public void run() { - if (!isVoteCommitmentEnabled) { - log.info("L1 votes commitment disabled on this instance."); - return; - } - log.info("Starting VoteCommitmentJob..."); + var startStop = new StopWatch(); startStop.start(); + voteCommitmentService.processVotesForAllEvents(); + startStop.stop(); log.info("VoteCommitmentJob completed, running time:{} secs", startStop.getTotalTimeSeconds()); diff --git a/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/repository/VoteRepository.java b/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/repository/VoteRepository.java index 4e6143c1b..9ea472c89 100644 --- a/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/repository/VoteRepository.java +++ b/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/repository/VoteRepository.java @@ -14,7 +14,7 @@ public interface VoteRepository extends JpaRepository { @Query("SELECT v FROM Vote v WHERE v.eventId = :eventId ORDER BY v.votedAtSlot, v.createdAt DESC") - List findAllByEventId(@Param("eventId") String eventId); + List findAllCompactVotesByEventId(@Param("eventId") String eventId); Optional findByEventIdAndCategoryIdAndVoterStakingAddress(String eventId, String categoryId, String voterStakeAddress); @@ -63,4 +63,12 @@ interface CategoryLevelStats { } + interface CompactVote { + + String getCoseSignature(); + + Optional getCosePublicKey(); + + } + } diff --git a/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/service/leader_board/DefaultLeaderBoardService.java b/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/service/leader_board/DefaultLeaderBoardService.java index 7d7f75949..4c703b587 100644 --- a/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/service/leader_board/DefaultLeaderBoardService.java +++ b/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/service/leader_board/DefaultLeaderBoardService.java @@ -293,15 +293,15 @@ public Either getCategoryLeader Map proposalResultsMap = votes.stream() .collect(toMap(VoteRepository.CategoryLevelStats::getProposalId, v -> { - var totalVotesCount = Optional.ofNullable(v.getTotalVoteCount()).orElse(0L); - var totalVotingPower = Optional.ofNullable(v.getTotalVotingPower()).map(String::valueOf).orElse("0"); + var totalVotesCount = Optional.ofNullable(v.getTotalVoteCount()).orElse(0L); + var totalVotingPower = Optional.ofNullable(v.getTotalVotingPower()).map(String::valueOf).orElse("0"); - var b = Leaderboard.Votes.builder(); - b.votes(totalVotesCount); + var b = Leaderboard.Votes.builder(); + b.votes(totalVotesCount); - switch (eventDetails.votingEventType()) { - case BALANCE_BASED, STAKE_BASED -> b.votingPower(totalVotingPower); - } + switch (eventDetails.votingEventType()) { + case BALANCE_BASED, STAKE_BASED -> b.votingPower(totalVotingPower); + } return b.build(); })); diff --git a/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/service/merkle_tree/VoteCommitmentService.java b/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/service/merkle_tree/VoteCommitmentService.java index d513bec6d..b0378c6b2 100644 --- a/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/service/merkle_tree/VoteCommitmentService.java +++ b/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/service/merkle_tree/VoteCommitmentService.java @@ -1,74 +1,71 @@ package org.cardano.foundation.voting.service.merkle_tree; import io.vavr.Value; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.cardano.foundation.voting.client.ChainFollowerClient; import org.cardano.foundation.voting.domain.L1MerkleCommitment; +import org.cardano.foundation.voting.domain.L1SubmissionData; import org.cardano.foundation.voting.domain.entity.VoteMerkleProof; +import org.cardano.foundation.voting.service.json.JsonService; import org.cardano.foundation.voting.service.transaction_submit.L1SubmissionService; import org.cardano.foundation.voting.service.vote.VoteService; +import org.cardanofoundation.cip30.CIP30Verifier; import org.cardanofoundation.merkle.MerkleTree; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.scheduling.annotation.Async; -import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.stereotype.Service; import org.springframework.util.StopWatch; import java.util.List; import static com.bloxbean.cardano.client.util.HexUtil.encodeHexString; -import static org.cardano.foundation.voting.domain.entity.Vote.VOTE_SERIALISER; +import static org.cardano.foundation.voting.domain.VoteSerialisations.VOTE_SERIALISER; +import static org.cardano.foundation.voting.domain.VoteSerialisations.createSerialiserFunction; +import static org.cardanofoundation.cip30.MessageFormat.TEXT; @Service @Slf4j -@EnableAsync +@RequiredArgsConstructor public class VoteCommitmentService { - @Autowired - private VoteService voteService; + private final VoteService voteService; - @Autowired - private L1SubmissionService l1SubmissionService; + private final L1SubmissionService l1SubmissionService; - @Autowired - private ChainFollowerClient chainFollowerClient; + private final ChainFollowerClient chainFollowerClient; - @Autowired - private VoteMerkleProofService voteMerkleProofService; + private final VoteMerkleProofService voteMerkleProofService; - @Autowired - private MerkleProofSerdeService merkleProofSerdeService; + private final MerkleProofSerdeService merkleProofSerdeService; + + private final JsonService jsonService; - @Async("asyncExecutor") public void processVotesForAllEvents() { - var l1MerkleCommitments = getL1MerkleCommitments(); + var l1MerkleCommitments = getValidL1MerkleCommitments(); if (l1MerkleCommitments.isEmpty()) { log.info("No l1 commitments to process."); return; } - // Event maybe active but it makes no sense spamming L1 when there are no votes to process - if (l1MerkleCommitments.stream().allMatch(l1MerkleCommitment -> l1MerkleCommitment.votes().isEmpty())) { - log.info("No votes to process."); + // Event maybe active but it makes no sense spamming L1 when there are no signedVotes to process + if (l1MerkleCommitments.stream().allMatch(l1MerkleCommitment -> l1MerkleCommitment.signedVotes().isEmpty())) { + log.info("No signedVotes to process."); return; } var l1TransactionDataE = l1SubmissionService.submitMerkleCommitments(l1MerkleCommitments); if (l1TransactionDataE.isEmpty()) { - var issue = l1TransactionDataE.swap().get(); + var problem = l1TransactionDataE.swap().get(); - log.error("Transaction submission failed, issue:{}, will try to submit again in some time...", issue.toString()); + log.error("Transaction submission failed, problem:{}, will try to submit again in some time...", problem.toString()); return; } var l1SubmissionData = l1TransactionDataE.get(); - var l1TransactionHash = l1SubmissionData.txHash(); - var l1TransactionSlot = l1SubmissionData.slot(); - generateAndStoreMerkleProofs(l1MerkleCommitments, l1TransactionHash, l1TransactionSlot); + generateAndStoreMerkleProofs(l1MerkleCommitments, l1SubmissionData); } - private List getL1MerkleCommitments() { + private List getValidL1MerkleCommitments() { var activeEventsE = chainFollowerClient.findAllActiveEvents(); if (activeEventsE.isEmpty()) { var issue = activeEventsE.swap().get(); @@ -81,22 +78,32 @@ private List getL1MerkleCommitments() { return activeEvents.stream() .map(event -> { - // TODO caching or paging or both? Maybe we use Redis??? - log.info("Loading votes from db for active event:{}", event.id()); + // TODO caching or paging or both or neither? Maybe we use Redis??? + log.info("Loading signedVotes from db for active event:{}", event.id()); var stopWatch = new StopWatch(); stopWatch.start(); - var allVotes = voteService.findAll(event.id()); + + var votes = voteService.findAllCompactVotesByEventId(event.id()) + .stream() + .filter(signedWeb3Request -> { + var cip30Result = new CIP30Verifier(signedWeb3Request.getCoseSignature(), signedWeb3Request.getCosePublicKey()); + + return cip30Result.verify().isValid(); + }) + .toList(); + stopWatch.stop(); - log.info("Loaded votes, count:{}, time: {} secs", allVotes.size(), stopWatch.getTotalTimeSeconds()); + log.info("Loaded signedVotes, count:{}, time: {} secs", votes.size(), stopWatch.getTotalTimeSeconds()); - var root = MerkleTree.fromList(allVotes, VOTE_SERIALISER); + var root = MerkleTree.fromList(votes, VOTE_SERIALISER); - return new L1MerkleCommitment(allVotes, root, event.id()); + return new L1MerkleCommitment(votes, root, event.id()); }) .toList(); } - private void generateAndStoreMerkleProofs(List l1MerkleCommitments, String l1TransactionHash, long l1TransactionSlot) { + private void generateAndStoreMerkleProofs(List l1MerkleCommitments, + L1SubmissionData l1SubmissionData) { log.info("Storing vote merkle proofs..."); for (var l1MerkleCommitment : l1MerkleCommitments) { @@ -106,24 +113,43 @@ private void generateAndStoreMerkleProofs(List l1MerkleCommi var storeProofsStartStop = new StopWatch(); storeProofsStartStop.start(); - for (var vote : l1MerkleCommitment.votes()) { - var maybeMerkleProof = MerkleTree.getProof(root, vote, VOTE_SERIALISER).map(Value::toJavaList); + for (var vote : l1MerkleCommitment.signedVotes()) { + var maybeMerkleProof = MerkleTree.getProof(root, vote, createSerialiserFunction()).map(Value::toJavaList); if (maybeMerkleProof.isEmpty()) { - log.error("Merkle proof is empty for vote: {}, this should never ever happen", vote.getId()); - throw new RuntimeException("Merkle proof is empty for vote: " + vote.getId()); + log.error("Merkle proof is empty for vote: {}, this should never ever happen", vote.getCoseSignature()); + throw new RuntimeException("Merkle proof is empty for vote: " + vote.getCoseSignature()); } var proofItems = maybeMerkleProof.orElseThrow(); var merkleRootHash = encodeHexString(root.itemHash()); var proofItemsJson = merkleProofSerdeService.serialiseAsString(proofItems); + var cip30Verifier = new CIP30Verifier(vote.getCoseSignature(), vote.getCosePublicKey()); + + var cip30VerificationResult = cip30Verifier.verify(); + if (!cip30VerificationResult.isValid()) { + log.error("Invalid CIP 30 signature for vote:{}", vote.getCoseSignature()); + continue; + } + + var voteSignedJsonPayload = cip30VerificationResult.getMessage(TEXT); + + var cip93EnvelopeE = jsonService.decodeCIP93VoteEnvelope(voteSignedJsonPayload); + + if (cip93EnvelopeE.isEmpty()) { + log.error("Invalid voteSignedJsonPayload for vote:{}", vote.getCoseSignature()); + continue; + } + + var voteEnvelopeCIP93Envelope = cip93EnvelopeE.get(); + var voteMerkleProof = VoteMerkleProof.builder() - .voteId(vote.getId()) - .eventId(vote.getEventId()) + .voteId(voteEnvelopeCIP93Envelope.getData().getId()) + .eventId(voteEnvelopeCIP93Envelope.getData().getEvent()) .rootHash(merkleRootHash) - .absoluteSlot(l1TransactionSlot) + .absoluteSlot(l1SubmissionData.slot()) .proofItemsJson(proofItemsJson) - .l1TransactionHash(l1TransactionHash) + .l1TransactionHash(l1SubmissionData.txHash()) .invalidated(false) .build(); @@ -131,7 +157,7 @@ private void generateAndStoreMerkleProofs(List l1MerkleCommi } storeProofsStartStop.stop(); - log.info("Storing merkle proofs: {}, completed for event: {}, time: {} secs", l1MerkleCommitment.votes().size(), l1MerkleCommitment.eventId(), storeProofsStartStop.getTotalTimeSeconds()); + log.info("Storing merkle proofs: {}, completed for event: {}, time: {} secs", l1MerkleCommitment.signedVotes().size(), l1MerkleCommitment.eventId(), storeProofsStartStop.getTotalTimeSeconds()); } log.info("Storing vote merkle proofs for all events completed."); diff --git a/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/service/transaction_submit/MetadataSerialiser.java b/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/service/transaction_submit/MetadataSerialiser.java index 27988192a..5a5581a62 100644 --- a/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/service/transaction_submit/MetadataSerialiser.java +++ b/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/service/transaction_submit/MetadataSerialiser.java @@ -25,7 +25,7 @@ public MetadataMap serialise(List l1MerkleCommitments, long var l1CommitmentMap = MetadataBuilder.createMap(); for (var l1MerkleCommitment : l1MerkleCommitments) { - if (l1MerkleCommitment.votes().isEmpty()) { + if (l1MerkleCommitment.signedVotes().isEmpty()) { continue; } diff --git a/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/service/vote/DefaultVoteService.java b/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/service/vote/DefaultVoteService.java index 3f06bf59e..c3e7853b4 100644 --- a/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/service/vote/DefaultVoteService.java +++ b/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/service/vote/DefaultVoteService.java @@ -3,7 +3,6 @@ import com.nimbusds.jwt.SignedJWT; import io.micrometer.core.annotation.Timed; import io.vavr.control.Either; -import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.cardano.foundation.voting.client.ChainFollowerClient; @@ -58,8 +57,8 @@ public class DefaultVoteService implements VoteService { @Override @Transactional(readOnly = true) - public List findAll(String eventId) { - return voteRepository.findAllByEventId(eventId); + public List findAllCompactVotesByEventId(String eventId) { + return voteRepository.findAllCompactVotesByEventId(eventId); } @Transactional(readOnly = true) diff --git a/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/service/vote/VoteService.java b/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/service/vote/VoteService.java index 54716a889..dbe3cf2a7 100644 --- a/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/service/vote/VoteService.java +++ b/backend-services/voting-app/src/main/java/org/cardano/foundation/voting/service/vote/VoteService.java @@ -3,7 +3,7 @@ import io.vavr.control.Either; import org.cardano.foundation.voting.domain.VoteReceipt; import org.cardano.foundation.voting.domain.entity.Vote; -import org.cardano.foundation.voting.domain.web3.SignedWeb3Request; +import org.cardano.foundation.voting.repository.VoteRepository; import org.cardano.foundation.voting.service.auth.jwt.JwtAuthenticationToken; import org.cardano.foundation.voting.service.auth.web3.Web3AuthenticationToken; import org.zalando.problem.Problem; @@ -12,10 +12,7 @@ public interface VoteService { - /** - * Return true if the slot is within permissible range - */ - List findAll(String eventId); + List findAllCompactVotesByEventId(String eventId); Either isVoteCastingStillPossible(String eventId, String voteId); diff --git a/backend-services/voting-app/src/main/resources/application.properties b/backend-services/voting-app/src/main/resources/application.properties index d2f3ea73e..5b4ed39e6 100644 --- a/backend-services/voting-app/src/main/resources/application.properties +++ b/backend-services/voting-app/src/main/resources/application.properties @@ -50,9 +50,9 @@ cardano.tx.submit.api.url=${TX_SUBMIT_URL:https://submit-api.pro.dandelion-prepr transaction.submission.timeout.minutes=${TRANSACTION_SUBMISSION_TIMEOUT_MINUTES:5} transaction.submission.sleep.seconds=${TRANSACTION_SUBMISSION_SLEEP_SECONDS:10} -# run every 15 minutes, in prod should be more like every 30 minutes -vote.commitment.cron.expression=0 */15 * * * ? -#vote.commitment.cron.expression=0 0/30 * * * ? +# run every 25 minutes (dev setup), in prod should be more like every 30 minutes +#vote.commitment.cron.expression=0 */15 * * * ? +vote.commitment.cron.expression=0 0/25 * * * ? vote.commitment.enabled=${VOTE_COMMITMENT_ENABLED:true} # value very specific to the network @@ -60,7 +60,7 @@ expiration.slot.buffer=${EXPIRATION_SLOT_BUFFER:300} server.port=9091 -spring.task.scheduling.pool.size=${SCHEDULING_POOL_SIZE:5} +spring.task.scheduling.pool.size=${SCHEDULING_POOL_SIZE:1} # must be false in production(!) leaderboard.force.results=${LEADERBOARD_FORCE_RESULTS:true} diff --git a/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/domain/CoseWrappedVote.java b/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/domain/CoseWrappedVote.java index 28cbca2bd..1394b38ba 100644 --- a/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/domain/CoseWrappedVote.java +++ b/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/domain/CoseWrappedVote.java @@ -3,30 +3,17 @@ import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; -import org.cardanofoundation.cip30.CIP30Verifier; import java.util.Optional; -import java.util.function.Function; @Getter @Builder @AllArgsConstructor public class CoseWrappedVote { - public static final Function VOTE_SERIALISER = createSerialiserFunction(); - private String coseSignature; @Builder.Default private Optional cosePublicKey = Optional.empty(); - private static Function createSerialiserFunction() { - return vote -> { - var cip30Verifier = new CIP30Verifier(vote.getCoseSignature(), vote.getCosePublicKey()); - var verificationResult = cip30Verifier.verify(); - - return Optional.ofNullable(verificationResult.getMessage()).orElse(new byte[0]); - }; - } - } diff --git a/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/service/verify/VoteVerificationService.java b/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/service/verify/VoteVerificationService.java index 4507e442d..f8f042d7e 100644 --- a/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/service/verify/VoteVerificationService.java +++ b/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/service/verify/VoteVerificationService.java @@ -21,7 +21,7 @@ import static com.bloxbean.cardano.client.util.HexUtil.decodeHexString; import static com.bloxbean.cardano.client.util.JsonUtil.parseJson; -import static org.cardano.foundation.voting.domain.CoseWrappedVote.VOTE_SERIALISER; +import static org.cardano.foundation.voting.utils.VoteSerialisations.VOTE_SERIALISER; import static org.cardanofoundation.cip30.MessageFormat.TEXT; import static org.zalando.problem.Status.BAD_REQUEST; diff --git a/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/utils/VoteSerialisations.java b/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/utils/VoteSerialisations.java new file mode 100644 index 000000000..a841188f1 --- /dev/null +++ b/backend-services/voting-verification-app/src/main/java/org/cardano/foundation/voting/utils/VoteSerialisations.java @@ -0,0 +1,26 @@ +package org.cardano.foundation.voting.utils; + +import org.cardano.foundation.voting.domain.CoseWrappedVote; +import org.cardanofoundation.cip30.CIP30Verifier; + +import java.util.Optional; +import java.util.function.Function; + +import static com.bloxbean.cardano.client.crypto.Blake2bUtil.blake2bHash256; + +public final class VoteSerialisations { + + public static final Function VOTE_SERIALISER = createSerialiserFunction(); + + private static Function createSerialiserFunction() { + return vote -> { + var cip30Verifier = new CIP30Verifier(vote.getCoseSignature(), vote.getCosePublicKey()); + var verificationResult = cip30Verifier.verify(); + + var bytes = Optional.ofNullable(verificationResult.getMessage()).orElse(new byte[0]); + + return blake2bHash256(bytes); + }; + } + +}