From e66f10af961b240988fc9a675cb3c13a5f9ee848 Mon Sep 17 00:00:00 2001 From: Giovanni Gargiulo Date: Mon, 18 Dec 2023 14:58:41 +0000 Subject: [PATCH] Feat/publish on chain results (#543) * chore: drafting onchain tx w/ tally results * chore: implemented all the wiring for a new command to submit centralised tally results * chore: added mainnet results --- .../voting/domain/CategoryResult.java | 18 +++++++ .../domain/CreateTallyResultCommand.java | 24 +++++++++ .../voting/domain/OnChainEventType.java | 2 +- .../voting/domain/ProposalResult.java | 20 ++++++++ .../L1SubmissionService.java | 7 +++ .../L1TransactionCreator.java | 12 ++++- .../MetadataSerialiser.java | 36 ++++++++++++++ .../voting/shell/CIP1694PreProdCommands.java | 49 +++++++++++++++++-- .../shell/CIP1694ProductionCommands.java | 49 +++++++++++++++++-- 9 files changed, 206 insertions(+), 11 deletions(-) create mode 100644 backend-services/voting-admin-app/src/main/java/org/cardano/foundation/voting/domain/CategoryResult.java create mode 100644 backend-services/voting-admin-app/src/main/java/org/cardano/foundation/voting/domain/CreateTallyResultCommand.java create mode 100644 backend-services/voting-admin-app/src/main/java/org/cardano/foundation/voting/domain/ProposalResult.java diff --git a/backend-services/voting-admin-app/src/main/java/org/cardano/foundation/voting/domain/CategoryResult.java b/backend-services/voting-admin-app/src/main/java/org/cardano/foundation/voting/domain/CategoryResult.java new file mode 100644 index 000000000..793cf1390 --- /dev/null +++ b/backend-services/voting-admin-app/src/main/java/org/cardano/foundation/voting/domain/CategoryResult.java @@ -0,0 +1,18 @@ +package org.cardano.foundation.voting.domain; + +import lombok.Builder; +import lombok.Getter; +import lombok.ToString; + +import java.util.List; + +@Getter +@Builder +@ToString +public class CategoryResult { + + private String id; + + private List proposalResults; + +} diff --git a/backend-services/voting-admin-app/src/main/java/org/cardano/foundation/voting/domain/CreateTallyResultCommand.java b/backend-services/voting-admin-app/src/main/java/org/cardano/foundation/voting/domain/CreateTallyResultCommand.java new file mode 100644 index 000000000..05d87947d --- /dev/null +++ b/backend-services/voting-admin-app/src/main/java/org/cardano/foundation/voting/domain/CreateTallyResultCommand.java @@ -0,0 +1,24 @@ +package org.cardano.foundation.voting.domain; + +import lombok.Builder; +import lombok.Getter; +import lombok.ToString; + +import java.util.List; + +@Getter +@Builder +@ToString +public class CreateTallyResultCommand { + + private String id; + + @Builder.Default + private boolean gdprProtection = false; + + @Builder.Default + private boolean showVoteCount = false; + + private List categoryResults; + +} diff --git a/backend-services/voting-admin-app/src/main/java/org/cardano/foundation/voting/domain/OnChainEventType.java b/backend-services/voting-admin-app/src/main/java/org/cardano/foundation/voting/domain/OnChainEventType.java index 89554e07e..69940e144 100644 --- a/backend-services/voting-admin-app/src/main/java/org/cardano/foundation/voting/domain/OnChainEventType.java +++ b/backend-services/voting-admin-app/src/main/java/org/cardano/foundation/voting/domain/OnChainEventType.java @@ -5,6 +5,6 @@ public enum OnChainEventType { COMMITMENTS, EVENT_REGISTRATION, CATEGORY_REGISTRATION, - VOTE_TALLY // TODO + VOTE_TALLY // TODO VOTE_TALLY_RESULTS } \ No newline at end of file diff --git a/backend-services/voting-admin-app/src/main/java/org/cardano/foundation/voting/domain/ProposalResult.java b/backend-services/voting-admin-app/src/main/java/org/cardano/foundation/voting/domain/ProposalResult.java new file mode 100644 index 000000000..e20857256 --- /dev/null +++ b/backend-services/voting-admin-app/src/main/java/org/cardano/foundation/voting/domain/ProposalResult.java @@ -0,0 +1,20 @@ +package org.cardano.foundation.voting.domain; + +import lombok.Builder; +import lombok.Getter; +import lombok.ToString; + +@Getter +@Builder +@ToString +public class ProposalResult { + + private String id; + + private String name; + + private String voteCount; + + private String votingPower; + +} diff --git a/backend-services/voting-admin-app/src/main/java/org/cardano/foundation/voting/service/transaction_submit/L1SubmissionService.java b/backend-services/voting-admin-app/src/main/java/org/cardano/foundation/voting/service/transaction_submit/L1SubmissionService.java index 5c56e60d9..b90b4cb89 100644 --- a/backend-services/voting-admin-app/src/main/java/org/cardano/foundation/voting/service/transaction_submit/L1SubmissionService.java +++ b/backend-services/voting-admin-app/src/main/java/org/cardano/foundation/voting/service/transaction_submit/L1SubmissionService.java @@ -3,6 +3,7 @@ import lombok.extern.slf4j.Slf4j; import org.cardano.foundation.voting.domain.CreateCategoryCommand; import org.cardano.foundation.voting.domain.CreateEventCommand; +import org.cardano.foundation.voting.domain.CreateTallyResultCommand; import org.cardano.foundation.voting.domain.HydraTallyConfig; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -96,4 +97,10 @@ public String submitCategory(CreateCategoryCommand category) { return transactionSubmissionService.submitTransaction(txData); } + public String submitTallyResults(CreateTallyResultCommand createTallyResultCommand) { + byte[] txData = l1TransactionCreator.submitCentralisedTally(createTallyResultCommand); + + return transactionSubmissionService.submitTransaction(txData); + } + } diff --git a/backend-services/voting-admin-app/src/main/java/org/cardano/foundation/voting/service/transaction_submit/L1TransactionCreator.java b/backend-services/voting-admin-app/src/main/java/org/cardano/foundation/voting/service/transaction_submit/L1TransactionCreator.java index 65c08e4d3..6227cb11b 100644 --- a/backend-services/voting-admin-app/src/main/java/org/cardano/foundation/voting/service/transaction_submit/L1TransactionCreator.java +++ b/backend-services/voting-admin-app/src/main/java/org/cardano/foundation/voting/service/transaction_submit/L1TransactionCreator.java @@ -18,6 +18,7 @@ import lombok.extern.slf4j.Slf4j; import org.cardano.foundation.voting.domain.CreateCategoryCommand; import org.cardano.foundation.voting.domain.CreateEventCommand; +import org.cardano.foundation.voting.domain.CreateTallyResultCommand; import org.cardano.foundation.voting.domain.OnChainEventType; import org.cardano.foundation.voting.service.blockchain_state.BlockchainDataChainTipService; import org.springframework.beans.factory.annotation.Autowired; @@ -26,8 +27,7 @@ import org.springframework.stereotype.Service; import static com.bloxbean.cardano.client.crypto.Blake2bUtil.blake2bHash224; -import static org.cardano.foundation.voting.domain.OnChainEventType.CATEGORY_REGISTRATION; -import static org.cardano.foundation.voting.domain.OnChainEventType.EVENT_REGISTRATION; +import static org.cardano.foundation.voting.domain.OnChainEventType.*; @Service @Slf4j @@ -70,6 +70,14 @@ public byte[] submitCategory(CreateCategoryCommand category) { return serialiseTransaction(metadata); } + public byte[] submitCentralisedTally(CreateTallyResultCommand createTallyResultCommand) { + + MetadataMap eventMetadataMap = metadataSerialiser.serialise(createTallyResultCommand); + Metadata metadata = serialiseMetadata(eventMetadataMap, VOTE_TALLY); + + return serialiseTransaction(metadata); + } + @SneakyThrows protected Metadata serialiseMetadata(MetadataMap childMetadata, OnChainEventType metadataType) { var stakeAddress = organiserAccount.stakeAddress(); 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 a100d2169..17c4382f7 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 @@ -150,4 +150,40 @@ private static MetadataMap createCategoryOptions(CreateCategoryCommand createCat return optionsMap; } + public MetadataMap serialise(CreateTallyResultCommand createTallyResultCommand) { + var categoryResults = MetadataBuilder.createList(); + var tallyResultsMap = MetadataBuilder.createMap(); + tallyResultsMap.put("id", createTallyResultCommand.getId()); + tallyResultsMap.put("categories", categoryResults); + + for (CategoryResult category : createTallyResultCommand.getCategoryResults()) { + var proposalResults = MetadataBuilder.createList(); + var categoryResult = MetadataBuilder.createMap(); + categoryResults.add(categoryResult); + categoryResult.put("id", category.getId()); + categoryResult.put("proposals", proposalResults); + + for (ProposalResult proposal : category.getProposalResults()) { + var proposalResultsMap = getProposalResultsMap(createTallyResultCommand, proposal); + proposalResults.add(proposalResultsMap); + } + + } + + return tallyResultsMap; + } + + private static MetadataMap getProposalResultsMap(CreateTallyResultCommand createTallyResultCommand, ProposalResult proposal) { + var proposalResultsMap = MetadataBuilder.createMap(); + proposalResultsMap.put("id", proposal.getId()); + if (!createTallyResultCommand.isGdprProtection()) { + proposalResultsMap.put("name", proposal.getName()); + } + if (createTallyResultCommand.isShowVoteCount()) { + proposalResultsMap.put("voteCount", proposal.getVoteCount()); + } + proposalResultsMap.put("votingPower", proposal.getVotingPower()); + return proposalResultsMap; + } + } diff --git a/backend-services/voting-admin-app/src/main/java/org/cardano/foundation/voting/shell/CIP1694PreProdCommands.java b/backend-services/voting-admin-app/src/main/java/org/cardano/foundation/voting/shell/CIP1694PreProdCommands.java index 716cbb927..bd131860d 100644 --- a/backend-services/voting-admin-app/src/main/java/org/cardano/foundation/voting/shell/CIP1694PreProdCommands.java +++ b/backend-services/voting-admin-app/src/main/java/org/cardano/foundation/voting/shell/CIP1694PreProdCommands.java @@ -2,10 +2,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.cardano.foundation.voting.domain.CardanoNetwork; -import org.cardano.foundation.voting.domain.CreateCategoryCommand; -import org.cardano.foundation.voting.domain.CreateEventCommand; -import org.cardano.foundation.voting.domain.Proposal; +import org.cardano.foundation.voting.domain.*; import org.cardano.foundation.voting.service.transaction_submit.L1SubmissionService; import org.springframework.shell.standard.ShellComponent; import org.springframework.shell.standard.ShellMethod; @@ -96,4 +93,48 @@ public String createCIP1694ApprovalCategoryOnPreProd(@ShellOption String event) return "Created CIP-1694 category: " + createCategoryCommand; } + @ShellMethod(key = "03_submit-cip-1694-tally-results-pre-prod", value = "Submit CIP1694_APPROVAL tally results on the PRE-PROD network.") + public String submitCIP1694TallyResultsOnPreProd() { + if (network != PREPROD) { + return "This command can only be run on a PRE-PROD network!"; + } + + log.info("Creating CIP-1694 CIP1694_APPROVAL Structure category..."); + + ProposalResult yesProposal = ProposalResult.builder() + .id("1f082124-ee46-4deb-9140-84a4529f98be") + .name("YES") + .voteCount("120") + .votingPower("123000000000") + .build(); + + ProposalResult noProposal = ProposalResult.builder() + .id("ed9f03e8-8ee9-4de5-93a3-30779216f150") + .name("NO") + .voteCount("20") + .votingPower("4560000") + .build(); + + ProposalResult abstainProposal = ProposalResult.builder() + .id("fd9f03e8-8ee9-4de5-93a3-40779216f151") + .name("ABSTAIN") + .voteCount("10") + .votingPower("789009") + .build(); + + CreateTallyResultCommand createTallyResultCommand = CreateTallyResultCommand.builder() + .id("CIP1694_APPROVAL") + .gdprProtection(false) + .showVoteCount(true) + .categoryResults(List.of(CategoryResult.builder() + .id("CIP1694_APPROVAL") + .proposalResults(List.of(yesProposal, noProposal, abstainProposal)) + .build())) + .build(); + + l1SubmissionService.submitTallyResults(createTallyResultCommand); + + return "Submitted CIP-1694 tally results: " + createTallyResultCommand; + } + } diff --git a/backend-services/voting-admin-app/src/main/java/org/cardano/foundation/voting/shell/CIP1694ProductionCommands.java b/backend-services/voting-admin-app/src/main/java/org/cardano/foundation/voting/shell/CIP1694ProductionCommands.java index 591abd8a0..6e09ffca9 100644 --- a/backend-services/voting-admin-app/src/main/java/org/cardano/foundation/voting/shell/CIP1694ProductionCommands.java +++ b/backend-services/voting-admin-app/src/main/java/org/cardano/foundation/voting/shell/CIP1694ProductionCommands.java @@ -2,10 +2,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.cardano.foundation.voting.domain.CardanoNetwork; -import org.cardano.foundation.voting.domain.CreateCategoryCommand; -import org.cardano.foundation.voting.domain.CreateEventCommand; -import org.cardano.foundation.voting.domain.Proposal; +import org.cardano.foundation.voting.domain.*; import org.cardano.foundation.voting.service.transaction_submit.L1SubmissionService; import org.springframework.shell.standard.ShellComponent; import org.springframework.shell.standard.ShellMethod; @@ -102,4 +99,48 @@ public String createCIP1694ApprovalCategoryOnMainnet(@ShellOption String event) return "Created CIP-1694 category: " + createCategoryCommand; } + @ShellMethod(key = "03_submit-cip-1694-tally-results-mainnet", value = "Submit CIP-1694_Pre_Ratification tally results on the MAINNET network.") + public String submitCIP1694TallyResultsOnMainnet() { + if (network != MAIN) { + return "This command can only be run on a MAINNET network!"; + } + + log.info("Creating CIP-1694 CIP1694_APPROVAL Structure category..."); + + ProposalResult yesProposal = ProposalResult.builder() + .id("1f082124-ee46-4deb-9140-84a4529f98be") + .name("YES") + .voteCount("720") + .votingPower("110744335923027") + .build(); + + ProposalResult noProposal = ProposalResult.builder() + .id("ed9f03e8-8ee9-4de5-93a3-30779216f150") + .name("NO") + .voteCount("30") + .votingPower("148003932083") + .build(); + + ProposalResult abstainProposal = ProposalResult.builder() + .id("fd9f03e8-8ee9-4de5-93a3-40779216f151") + .name("ABSTAIN") + .voteCount("21") + .votingPower("835765197287") + .build(); + + CreateTallyResultCommand createTallyResultCommand = CreateTallyResultCommand.builder() + .id("CIP-1694_Pre_Ratification") + .gdprProtection(false) + .showVoteCount(true) + .categoryResults(List.of(CategoryResult.builder() + .id("CIP1694_APPROVAL") + .proposalResults(List.of(yesProposal, noProposal, abstainProposal)) + .build())) + .build(); + + l1SubmissionService.submitTallyResults(createTallyResultCommand); + + return "Submitted CIP-1694 tally results: " + createTallyResultCommand; + } + }