예제 #1
0
        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);
        }
    }
}