From 01f3bdc5579b2a88e68997686437d5bdb0bc733e Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Thu, 7 Mar 2019 17:53:30 -0500 Subject: [PATCH] Make vote result json deterministic --- .../main/java/bisq/core/util/BSFormatter.java | 16 ++++-- .../dao/governance/result/CycleListItem.java | 12 +++-- .../governance/result/ProposalListItem.java | 9 +++- .../dao/governance/result/VoteResultView.java | 53 +++++++++++-------- 4 files changed, 59 insertions(+), 31 deletions(-) diff --git a/core/src/main/java/bisq/core/util/BSFormatter.java b/core/src/main/java/bisq/core/util/BSFormatter.java index 8d6677b2a4..3f87a0f991 100644 --- a/core/src/main/java/bisq/core/util/BSFormatter.java +++ b/core/src/main/java/bisq/core/util/BSFormatter.java @@ -51,6 +51,7 @@ import java.math.BigDecimal; import java.util.Date; import java.util.List; import java.util.Locale; +import java.util.TimeZone; import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; @@ -482,9 +483,18 @@ public class BSFormatter { } public String formatDateTime(Date date) { - return formatDateTime(date, - DateFormat.getDateInstance(DateFormat.DEFAULT, getLocale()), - DateFormat.getTimeInstance(DateFormat.DEFAULT, getLocale())); + return formatDateTime(date, true); + } + + public String formatDateTime(Date date, boolean useLocaleAndLocalTimezone) { + Locale locale = useLocaleAndLocalTimezone ? getLocale() : Locale.US; + DateFormat dateInstance = DateFormat.getDateInstance(DateFormat.DEFAULT, locale); + DateFormat timeInstance = DateFormat.getTimeInstance(DateFormat.DEFAULT, locale); + if (!useLocaleAndLocalTimezone) { + dateInstance.setTimeZone(TimeZone.getTimeZone("UTC")); + timeInstance.setTimeZone(TimeZone.getTimeZone("UTC")); + } + return formatDateTime(date, dateInstance, timeInstance); } public String formatDateTime(Date date, DateFormat dateFormatter, DateFormat timeFormatter) { diff --git a/desktop/src/main/java/bisq/desktop/main/dao/governance/result/CycleListItem.java b/desktop/src/main/java/bisq/desktop/main/dao/governance/result/CycleListItem.java index 31c117f649..108a993f93 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/governance/result/CycleListItem.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/governance/result/CycleListItem.java @@ -46,10 +46,16 @@ public class CycleListItem { } public String getCycle() { - int displayIndex = resultsOfCycle.getCycleIndex() + 1; + return Res.get("dao.results.results.table.item.cycle", getCycleIndex(), getCycleDateTime(true)); + } + + public String getCycleDateTime(boolean useLocaleAndLocalTimezone) { long cycleStartTime = resultsOfCycle.getCycleStartTime(); - String dateTime = cycleStartTime > 0 ? bsqFormatter.formatDateTime(new Date(cycleStartTime)) : Res.get("shared.na"); - return Res.get("dao.results.results.table.item.cycle", displayIndex, dateTime); + return cycleStartTime > 0 ? bsqFormatter.formatDateTime(new Date(cycleStartTime), useLocaleAndLocalTimezone) : Res.get("shared.na"); + } + + public int getCycleIndex() { + return resultsOfCycle.getCycleIndex() + 1; } public String getNumProposals() { diff --git a/desktop/src/main/java/bisq/desktop/main/dao/governance/result/ProposalListItem.java b/desktop/src/main/java/bisq/desktop/main/dao/governance/result/ProposalListItem.java index eee333dfbd..ecd15d4955 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/governance/result/ProposalListItem.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/governance/result/ProposalListItem.java @@ -109,6 +109,10 @@ public class ProposalListItem { } public static String getProposalDetails(EvaluatedProposal evaluatedProposal, BsqFormatter bsqFormatter) { + return getProposalDetails(evaluatedProposal, bsqFormatter, true); + } + + public static String getProposalDetails(EvaluatedProposal evaluatedProposal, BsqFormatter bsqFormatter, boolean useDisplayString) { Proposal proposal = evaluatedProposal.getProposal(); switch (proposal.getType()) { case COMPENSATION_REQUEST: @@ -121,11 +125,12 @@ public class ProposalListItem { return bsqFormatter.formatCoinWithCode(requestedBsq); case CHANGE_PARAM: ChangeParamProposal changeParamProposal = (ChangeParamProposal) proposal; - return changeParamProposal.getParam().getDisplayString(); + return useDisplayString ? changeParamProposal.getParam().getDisplayString() : changeParamProposal.getParam().name(); case BONDED_ROLE: RoleProposal roleProposal = (RoleProposal) proposal; Role role = roleProposal.getRole(); - return Res.get("dao.bond.bondedRoleType." + role.getBondedRoleType().name()); + String name = role.getBondedRoleType().name(); + return useDisplayString ? Res.get("dao.bond.bondedRoleType." + name) : name; case CONFISCATE_BOND: ConfiscateBondProposal confiscateBondProposal = (ConfiscateBondProposal) proposal; // TODO add info to bond diff --git a/desktop/src/main/java/bisq/desktop/main/dao/governance/result/VoteResultView.java b/desktop/src/main/java/bisq/desktop/main/dao/governance/result/VoteResultView.java index b58f8afa28..c4495229ba 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/governance/result/VoteResultView.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/governance/result/VoteResultView.java @@ -952,13 +952,13 @@ public class VoteResultView extends ActivatableView implements D sortedCycleListItemList.sorted(Comparator.comparing(CycleListItem::getCycleStartTime)).forEach(cycle -> { JsonObject cycleJson = new JsonObject(); - cycleJson.addProperty("cycleIndex", cycle.getResultsOfCycle().getCycleIndex()); - cycleJson.addProperty("cycle", cycle.getCycle()); + cycleJson.addProperty("cycleIndex", cycle.getCycleIndex()); + cycleJson.addProperty("cycleDateTime", cycle.getCycleDateTime(false)); cycleJson.addProperty("proposalsCount", cycle.getNumProposals()); cycleJson.addProperty("votesCount", cycle.getNumVotesAsString()); cycleJson.addProperty("voteWeight", cycle.getMeritAndStake()); cycleJson.addProperty("issuance", cycle.getIssuance()); - cycleJson.addProperty("startDate", cycle.getCycleStartTime()); + cycleJson.addProperty("startTime", cycle.getCycleStartTime()); cycleJson.addProperty("totalAcceptedVotes", cycle.getResultsOfCycle().getNumAcceptedVotes()); cycleJson.addProperty("totalRejectedVotes", cycle.getResultsOfCycle().getNumRejectedVotes()); @@ -969,11 +969,11 @@ public class VoteResultView extends ActivatableView implements D evaluatedProposals.forEach(proposal -> { JsonObject proposalJson = new JsonObject(); - proposalJson.addProperty("dateTime", bsqFormatter.formatDateTime(proposal.getProposal().getCreationDate())); + proposalJson.addProperty("dateTime", bsqFormatter.formatDateTime(proposal.getProposal().getCreationDate(), false)); proposalJson.addProperty("name", proposal.getProposal().getName()); proposalJson.addProperty("link", proposal.getProposal().getLink()); - proposalJson.addProperty("proposalType", proposal.getProposal().getType().getShortDisplayName()); - proposalJson.addProperty("details", ProposalListItem.getProposalDetails(proposal, bsqFormatter)); + proposalJson.addProperty("proposalType", proposal.getProposal().getType().name()); + proposalJson.addProperty("details", ProposalListItem.getProposalDetails(proposal, bsqFormatter, false)); proposalJson.addProperty("voteResult", proposal.isAccepted() ? "Accepted" : "Rejected"); proposalJson.addProperty("txId", proposal.getProposalTxId()); proposalJson.addProperty("requiredQuorum", proposal.getRequiredQuorum()); @@ -990,7 +990,7 @@ public class VoteResultView extends ActivatableView implements D switch (proposal.getProposal().getType()) { case BONDED_ROLE: RoleProposal roleProposal = (RoleProposal) proposal.getProposal(); - proposalJson.addProperty("roleType", roleProposal.getRole().getBondedRoleType().getDisplayString()); + proposalJson.addProperty("roleType", roleProposal.getRole().getBondedRoleType().name()); proposalJson.addProperty("requiredBond", roleProposal.getRole().getBondedRoleType().getRequiredBond()); proposalJson.addProperty("allowMultipleHolders", roleProposal.getRole().getBondedRoleType().isAllowMultipleHolders()); proposalJson.addProperty("unlockTimeInBlocks", roleProposal.getRole().getBondedRoleType().getUnlockTimeInBlocks()); @@ -998,7 +998,7 @@ public class VoteResultView extends ActivatableView implements D break; case CHANGE_PARAM: ChangeParamProposal changeParamProposal = (ChangeParamProposal) proposal.getProposal(); - proposalJson.addProperty("param", changeParamProposal.getParam().getDisplayString()); + proposalJson.addProperty("param", changeParamProposal.getParam().name()); proposalJson.addProperty("paramValue", changeParamProposal.getParamValue()); proposalJson.addProperty("paramDefaultValue", changeParamProposal.getParam().getDefaultValue()); proposalJson.addProperty("paramMaxDecrease", changeParamProposal.getParam().getMaxDecrease()); @@ -1027,26 +1027,33 @@ public class VoteResultView extends ActivatableView implements D JsonArray votesArray = new JsonArray(); evaluatedProposals.stream() .filter(evaluatedProposal -> evaluatedProposal.getProposal().equals(proposal.getProposal())) - .forEach(evaluatedProposal -> - cycle.getResultsOfCycle().getDecryptedVotesForCycle().forEach(decryptedBallotsWithMerits -> { - JsonObject voteJson = new JsonObject(); - Optional vote = decryptedBallotsWithMerits.getVote(proposal.getProposalTxId()); - if (vote.isPresent()) - voteJson.addProperty("vote", vote.get().isAccepted() ? "Accepted" : "Rejected"); - else - voteJson.addProperty("vote", "Ignored"); + .forEach(evaluatedProposal -> { + List decryptedVotesForCycle = cycle.getResultsOfCycle().getDecryptedVotesForCycle(); + // Make sure the votes are sorted so we can easier compare json files from different users + decryptedVotesForCycle.sort(Comparator.comparing(DecryptedBallotsWithMerits::getBlindVoteTxId)); + decryptedVotesForCycle.forEach(decryptedBallotsWithMerits -> { + JsonObject voteJson = new JsonObject(); + Optional vote = decryptedBallotsWithMerits.getVote(proposal.getProposalTxId()); + if (vote.isPresent()) + voteJson.addProperty("vote", vote.get().isAccepted() ? "Accepted" : "Rejected"); + else + voteJson.addProperty("vote", "Ignored"); - voteJson.addProperty("voteWeight", decryptedBallotsWithMerits.getStake()); - voteJson.addProperty("stake", decryptedBallotsWithMerits.getStake()); - voteJson.addProperty("blindTxId", decryptedBallotsWithMerits.getBlindVoteTxId()); - voteJson.addProperty("revealTxId", decryptedBallotsWithMerits.getVoteRevealTxId()); + voteJson.addProperty("voteWeight", decryptedBallotsWithMerits.getMerit(daoStateService)); + voteJson.addProperty("stake", decryptedBallotsWithMerits.getStake()); + voteJson.addProperty("blindTxId", decryptedBallotsWithMerits.getBlindVoteTxId()); + voteJson.addProperty("revealTxId", decryptedBallotsWithMerits.getVoteRevealTxId()); - votesArray.add(voteJson); - })); + votesArray.add(voteJson); + }); + }); + + proposalJson.addProperty("numberOfVotes", votesArray.size()); proposalJson.add("votes", votesArray); - proposalsArray.add(proposalJson); + proposalsArray.add(proposalJson); }); + cycleJson.addProperty("numberOfProposals", proposalsArray.size()); cycleJson.add("proposals", proposalsArray); cyclesArray.add(cycleJson); });