public override async Task <APIResultCodes> PreSendAuthAsync(DagSystem sys, SendTransferBlock send, TransactionBlock last) { if (send.Tags.Count != 3 || !send.Tags.ContainsKey("data") || string.IsNullOrWhiteSpace(send.Tags["data"]) || !send.Tags.ContainsKey("voteid") ) { return(APIResultCodes.InvalidBlockTags); } // dao must exists var dao = await sys.Storage.FindLatestBlockAsync(send.DestinationAccountId) as IDao; if (dao == null) { return(APIResultCodes.InvalidDAO); } // verify it var change = JsonConvert.DeserializeObject <DAOChange>(send.Tags["data"]); if (change == null || change.creator != dao.OwnerAccountId || send.AccountID != dao.OwnerAccountId) { return(APIResultCodes.Unauthorized); } var voteid = send.Tags["voteid"]; if (string.IsNullOrWhiteSpace(voteid)) { return(APIResultCodes.Unauthorized); } else { var vs = await sys.Storage.GetVoteSummaryAsync(voteid); if (vs == null || !vs.IsDecided) { return(APIResultCodes.Unauthorized); } if (vs.Spec.Proposal.data != send.Tags["data"]) { return(APIResultCodes.ArgumentOutOfRange); } // should not execute more than once var exec = await sys.Storage.FindExecForVoteAsync(voteid); if (exec != null) { return(APIResultCodes.AlreadyExecuted); } } if (change.settings == null || change.settings.Count == 0) { return(APIResultCodes.InvalidArgument); } return(WFChangeDAO.VerifyDaoChanges(change)); }
public override async Task <APIResultCodes> PreSendAuthAsync(DagSystem sys, SendTransferBlock send, TransactionBlock last) { if (send.Tags.Count != 4 || !send.Tags.ContainsKey("data") || string.IsNullOrWhiteSpace(send.Tags["data"]) || !send.Tags.ContainsKey("pptype") || string.IsNullOrWhiteSpace(send.Tags["pptype"]) || !send.Tags.ContainsKey("ppdata") || string.IsNullOrWhiteSpace(send.Tags["ppdata"]) ) { return(APIResultCodes.InvalidBlockTags); } var subject = JsonConvert.DeserializeObject <VotingSubject>(send.Tags["data"]); // verify subject if (string.IsNullOrEmpty(subject.DaoId) || string.IsNullOrEmpty(subject.Issuer) || string.IsNullOrEmpty(subject.Title) || string.IsNullOrEmpty(subject.Description) || subject.Options == null || subject.Options.Length < 2 || subject.TimeSpan < 1 || subject.Type == SubjectType.None ) { return(APIResultCodes.InvalidArgument); } if (subject.Title == null || subject.Title.Length > 100 || subject.Description == null || subject.Description.Length > 300) { return(APIResultCodes.ArgumentOutOfRange); } var pptype = (ProposalType)Enum.Parse(typeof(ProposalType), send.Tags["pptype"]); object?proposal = pptype switch { ProposalType.DisputeResolution => JsonConvert.DeserializeObject <ODRResolution>(send.Tags["ppdata"]), ProposalType.DAOSettingChanges => JsonConvert.DeserializeObject <DAOChange>(send.Tags["ppdata"]), _ => null }; if (pptype == ProposalType.None || proposal == null) { return(APIResultCodes.InvalidArgument); } // issuer should be the owner of DAO var dao = await sys.Storage.FindLatestBlockAsync(subject.DaoId) as IDao; if (dao == null || (dao as IBrokerAccount).OwnerAccountId != subject.Issuer) { return(APIResultCodes.Unauthorized); } var resolution = proposal as ODRResolution; if (resolution != null) { // verify ODR resolution subject if (string.IsNullOrEmpty(resolution.tradeid) || string.IsNullOrEmpty(resolution.creator) || resolution.actions == null || resolution.actions.Length < 2 ) { return(APIResultCodes.InvalidArgument); } // trade's dao == subject' dao var tradeblk = await sys.Storage.FindLatestBlockAsync(resolution.tradeid) as IOtcTrade; if (tradeblk == null) { return(APIResultCodes.InvalidArgument); } if (tradeblk.Trade.daoId != subject.DaoId) { return(APIResultCodes.InvalidDAO); } if (tradeblk.OTStatus != OTCTradeStatus.Dispute) { return(APIResultCodes.InvalidTradeStatus); } } var daochg = proposal as DAOChange; if (daochg != null) { if (dao.OwnerAccountId != daochg.creator || daochg.creator != subject.Issuer) { return(APIResultCodes.Unauthorized); } var vrfychg = WFChangeDAO.VerifyDaoChanges(daochg); if (APIResultCodes.Success != vrfychg) { return(vrfychg); } } if (proposal != null && resolution == null && daochg == null) { return(APIResultCodes.Unsupported); } // title can't repeat var votes = await sys.Storage.FindAllVotesByDaoAsync(subject.DaoId, false); if (votes.Any(a => a is VotingGenesisBlock vg && vg.Subject.Title == subject.Title)) { return(APIResultCodes.InvalidArgument); } // options can't repeat if (subject.Options.Length != subject.Options.Distinct().Count()) { return(APIResultCodes.InvalidArgument); } // must has enough voter if (dao.Treasure == null || dao.Treasure.Count < 1) { return(APIResultCodes.NotEnoughVoters); } return(APIResultCodes.Success); } async Task <TransactionBlock> CreateGenesisAsync(DagSystem sys, SendTransferBlock send) { var blocks = await sys.Storage.FindBlocksByRelatedTxAsync(send.Hash); var subject = JsonConvert.DeserializeObject <VotingSubject>(send.Tags["data"]); var pptype = (ProposalType)Enum.Parse(typeof(ProposalType), send.Tags["pptype"]); object?proposal = pptype switch { ProposalType.DisputeResolution => JsonConvert.DeserializeObject <ODRResolution>(send.Tags["ppdata"]), ProposalType.DAOSettingChanges => JsonConvert.DeserializeObject <DAOChange>(send.Tags["ppdata"]), _ => null }; var keyStr = $"{send.Hash.Substring(0, 16)},{subject.Title},{send.AccountID}"; var AccountId = Base58Encoding.EncodeAccountId(Encoding.ASCII.GetBytes(keyStr).Take(64).ToArray()); var sb = await sys.Storage.GetLastServiceBlockAsync(); var gens = new VotingGenesisBlock { ServiceHash = sb.Hash, Fee = 0, FeeCode = LyraGlobal.OFFICIALTICKERCODE, FeeType = AuthorizationFeeTypes.NoFee, // transaction AccountType = AccountTypes.Voting, AccountID = AccountId, Balances = new Dictionary <string, long>(), // recv SourceHash = null, // broker Name = "no name", OwnerAccountId = send.AccountID, RelatedTx = send.Hash, // voting VoteState = VoteStatus.InProgress, Subject = subject, Proposal = new VoteProposal { pptype = pptype, data = send.Tags["ppdata"] }, }; gens.AddTag(Block.MANAGEDTAG, ""); // value is always ignored // pool blocks are service block so all service block signed by leader node gens.InitializeBlock(null, NodeService.Dag.PosWallet.PrivateKey, AccountId: NodeService.Dag.PosWallet.AccountId); return(gens); } } }