/// <summary> /// Execute the Vote action,save the VoteRecords and update the VotingResults and the VotedItems /// Before Voting,the VotingItem's token must be locked,except the votes delegated to a contract. /// </summary> /// <param name="input">VoteInput</param> /// <returns></returns> public override Empty Vote(VoteInput input) { var votingItem = AssertValidVoteInput(input); var votingRecord = new VotingRecord { VotingItemId = input.VotingItemId, Amount = input.Amount, SnapshotNumber = votingItem.CurrentSnapshotNumber, Option = input.Option, IsWithdrawn = false, VoteTimestamp = Context.CurrentBlockTime, Voter = input.Voter, IsChangeTarget = input.IsChangeTarget }; //save the VotingRecords into the state. State.VotingRecords[input.VoteId] = votingRecord; UpdateVotingResult(votingItem, input.Option, input.Amount); UpdateVotedItems(input.VoteId, votingRecord.Voter, votingItem); if (votingItem.IsLockToken) { // Lock voted token. State.TokenContract.Lock.Send(new LockInput { Address = votingRecord.Voter, Symbol = votingItem.AcceptedCurrency, LockId = input.VoteId, Amount = input.Amount, Usage = $"Voting for {input.VotingItemId}" }); } Context.Fire(new Voted { VoteId = input.VoteId, VotingItemId = votingRecord.VotingItemId, Voter = votingRecord.Voter, Amount = votingRecord.Amount, Option = votingRecord.Option, SnapshotNumber = votingRecord.SnapshotNumber, VoteTimestamp = votingRecord.VoteTimestamp }); return(new Empty()); }
private ElectionVotingRecord TransferVotingRecordToElectionVotingRecord(VotingRecord votingRecord, Hash voteId) { var lockSeconds = State.LockTimeMap[voteId]; return(new ElectionVotingRecord { Voter = votingRecord.Voter, Candidate = votingRecord.Option, Amount = votingRecord.Amount, TermNumber = votingRecord.SnapshotNumber, VoteId = voteId, LockTime = lockSeconds, VoteTimestamp = votingRecord.VoteTimestamp, WithdrawTimestamp = votingRecord.WithdrawTimestamp, UnlockTimestamp = votingRecord.VoteTimestamp.AddSeconds(lockSeconds), IsWithdrawn = votingRecord.IsWithdrawn, Weight = GetVotesWeight(votingRecord.Amount, lockSeconds) }); }
/// <summary> /// Execute the Vote action,save the VoteRecords and update the VotingResults and the VotedItems /// Before Voting,the VotingItem's token must be locked,except the votes delegated to a contract. /// </summary> /// <param name="input">VoteInput</param> /// <returns></returns> public override Empty Vote(VoteInput input) { //the VotingItem is exist in state. var votingItem = AssertVotingItem(input.VotingItemId); Assert(votingItem.Options.Contains(input.Option), $"Option {input.Option} not found."); Assert(votingItem.CurrentSnapshotNumber <= votingItem.TotalSnapshotNumber, "Current voting item already ended."); if (!votingItem.IsLockToken) { Assert(votingItem.Sponsor == Context.Sender, "Sender of delegated voting event must be the Sponsor."); Assert(input.Voter != null, "Voter cannot be null if voting event is delegated."); Assert(input.VoteId != null, "Vote Id cannot be null if voting event is delegated."); } else { //Voter just is the transaction sponsor input.Voter = Context.Sender; //VoteId just is the transaction ID; input.VoteId = Context.TransactionId; } var votingRecord = new VotingRecord { VotingItemId = input.VotingItemId, Amount = input.Amount, SnapshotNumber = votingItem.CurrentSnapshotNumber, Option = input.Option, IsWithdrawn = false, VoteTimestamp = Context.CurrentBlockTime, Voter = input.Voter }; //save the VotingRecords into the state. State.VotingRecords[input.VoteId] = votingRecord; UpdateVotingResult(votingItem, input.Option, input.Amount); UpdateVotedItems(input.VoteId, votingRecord.Voter, votingItem); if (votingItem.IsLockToken) { // Lock voted token. State.TokenContract.Lock.Send(new LockInput { Address = votingRecord.Voter, Symbol = votingItem.AcceptedCurrency, LockId = input.VoteId, Amount = input.Amount, Usage = $"Voting for {input.VotingItemId}" }); } Context.Fire(new Voted { VoteId = input.VoteId, VotingItemId = votingRecord.VotingItemId, Voter = votingRecord.Voter, Amount = votingRecord.Amount, Option = votingRecord.Option, SnapshotNumber = votingRecord.SnapshotNumber, VoteTimestamp = votingRecord.VoteTimestamp }); return(new Empty()); }
private async Task <bool> ProcessRecord(VotingRecord record) { try { _logger.LogDebug($"Processing voting record {record.Sequence} for user {record?.Vote?.UserId} on poll {record?.Vote?.PollId}"); var vote = record.Vote; if (vote == null) { _logger.LogDebug($"Not processing record, vote = null"); return(false); } var pollSummary = await _storageService.GetPollSummary(vote.PollId); if (pollSummary == null) { _logger.LogWarning($"Not processing record for poll {vote.PollId}, summary could not be identified"); return(false); } pollSummary.TotalVotes++; var pollOption = pollSummary.Options.FirstOrDefault(x => x.Option.Id == vote.OptionId); if (pollOption == null) { _logger.LogWarning($"Specified poll option {vote.OptionId} not found"); return(false); } pollOption.VoteCounts++; // ReRank votes and compute ratios var rank = 0; foreach (var option in pollSummary.Options.OrderBy(x => x.VoteCounts)) { option.Rank = rank; option.Ratio = option.VoteCounts / pollSummary.TotalVotes; rank++; } var didUpsert = await _storageService.UpsertPollSummary(pollSummary); if (!didUpsert) { _logger.LogWarning("Error while upserting PollSummary"); return(false); } return(true); } catch (Exception e) { _logger.LogError(e, "Error while processing voting record"); return(false); } finally { _logger.LogDebug($"Finished processing voting record {record.Sequence} for user {record.Vote.UserId} on poll {record.Vote.PollId}"); } }
//TODO: User cannot vote when Event CurrentEpoch >= EpochNumber + 1 public override Empty Vote(VoteInput input) { input.Topic = input.Topic.Trim(); var votingEvent = AssertVotingEvent(input.Topic, input.Sponsor); Assert(votingEvent.Options.Contains(input.Option), $"Option {input.Option} not found."); if (votingEvent.Delegated) { Assert(input.Sponsor == Context.Sender, "Sender of delegated voting event must be the Sponsor."); Assert(input.Voter != null, "Voter cannot be null if voting event is delegated."); Assert(input.VoteId != null, "Vote Id cannot be null if voting event is delegated."); } else { input.Voter = Context.Sender; input.VoteId = Context.TransactionId; } var votingRecord = new VotingRecord { Topic = input.Topic, Sponsor = input.Sponsor, Amount = input.Amount, EpochNumber = votingEvent.CurrentEpoch, Option = input.Option, IsWithdrawn = false, VoteTimestamp = Context.CurrentBlockTime.ToTimestamp(), Voter = input.Voter, Currency = votingEvent.AcceptedCurrency }; // Update VotingResult based on this voting behaviour. var votingResultHash = Hash.FromMessage(new GetVotingResultInput { Sponsor = input.Sponsor, Topic = input.Topic, EpochNumber = votingEvent.CurrentEpoch }); var votingResult = State.VotingResults[votingResultHash]; if (!votingResult.Results.ContainsKey(input.Option)) { votingResult.Results.Add(input.Option, 0); } var currentVotes = votingResult.Results[input.Option]; votingResult.Results[input.Option] = currentVotes + input.Amount; // Update voting history var votingHistories = State.VotingHistoriesMap[votingRecord.Voter] ?? new VotingHistories { Voter = votingRecord.Voter }; var votingEventHash = votingEvent.GetHash().ToHex(); if (!votingHistories.Votes.ContainsKey(votingEventHash)) { votingHistories.Votes[votingEventHash] = new VotingHistory { ActiveVotes = { input.VoteId } }; votingResult.VotersCount += 1; } else { votingHistories.Votes[votingEventHash].ActiveVotes.Add(input.VoteId); } State.VotingRecords[input.VoteId] = votingRecord; State.VotingResults[votingResultHash] = votingResult; State.VotingHistoriesMap[votingRecord.Voter] = votingHistories; if (!votingEvent.Delegated) { // Lock voted token. State.TokenContract.Lock.Send(new LockInput { From = votingRecord.Voter, Symbol = votingEvent.AcceptedCurrency, LockId = input.VoteId, Amount = input.Amount, To = Context.Self, Usage = $"Voting for {input.Topic}" }); } return(new Empty()); }