Пример #1
0
        public override bool Verify(IEnumerable <Transaction> mempool)
        {
            // Check Token Type
            for (int i = 0; i < byJoinSplit.Count; i++)
            {
                UInt256    AssetID = Asset_ID(i);
                AssetState asset   = Blockchain.Default.GetAssetState(AssetID);

                if (asset.AssetType == AssetType.TransparentToken)
                {
                    return(false);
                }
            }

            // Check double spend about tranparent input
            for (int i = 1; i < Inputs.Length; i++)
            {
                for (int j = 0; j < i; j++)
                {
                    if (Inputs[i].PrevHash == Inputs[j].PrevHash && Inputs[i].PrevIndex == Inputs[j].PrevIndex)
                    {
                        return(false);
                    }
                }
            }

            if (mempool.Where(p => p != this).SelectMany(p => p.Inputs).Intersect(Inputs).Count() > 0)
            {
                return(false);
            }

            if (Blockchain.Default.IsDoubleSpend(this))
            {
                return(false);
            }

            if (Blockchain.Default.IsDoubleNullifier(this))
            {
                return(false);
            }

            Fixed8 assetFee    = Fixed8.Zero;
            Fixed8 qrsAssetFee = Fixed8.Zero;

            // Check output format
            foreach (var group in Outputs.GroupBy(p => p.AssetId))
            {
                AssetState asset = Blockchain.Default.GetAssetState(group.Key);
                if (asset == null)
                {
                    return(false);
                }
                if (asset.Expiration <= Blockchain.Default.Height + 1 && asset.AssetType != AssetType.GoverningToken && asset.AssetType != AssetType.UtilityToken)
                {
                    return(false);
                }
                foreach (TransactionOutput output in group)
                {
                    if (output.Value.GetData() % (long)Math.Pow(10, 8 - asset.Precision) != 0)
                    {
                        return(false);
                    }
                }
            }

            TransactionResult[]      results_transparent = GetTransactionResults()?.ToArray();
            List <TransactionResult> results_anonymous   = new List <TransactionResult>();

            for (int i = 0; i < this.byJoinSplit.Count; i++)
            {
                bool isAdded = false;
                for (int j = 0; j < results_anonymous.Count; j++)
                {
                    if (results_anonymous[j].AssetId == this.Asset_ID(i))
                    {
                        results_anonymous[j].Amount -= this.vPub_Old(i);
                        results_anonymous[j].Amount += this.vPub_New(i);
                        isAdded = true;
                        break;
                    }
                }

                if (isAdded == false)
                {
                    TransactionResult anonyItem = new TransactionResult();
                    anonyItem.AssetId = this.Asset_ID(i);
                    anonyItem.Amount  = this.vPub_New(i) - this.vPub_Old(i);
                    results_anonymous.Add(anonyItem);
                }
                AssetState asset = Blockchain.Default.GetAssetState(this.Asset_ID(i));
            }

            TransactionResult[] results = results_anonymous.Select(p => new
            {
                AssetId = p.AssetId,
                Value   = p.Amount
            }).Concat(results_transparent.Select(p => new
            {
                AssetId = p.AssetId,
                Value   = p.Amount
            })).GroupBy(p => p.AssetId, (k, g) => new TransactionResult
            {
                AssetId = k,
                Amount  = g.Sum(p => p.Value)
            }).Where(p => p.Amount != Fixed8.Zero)?.ToArray();

            if (results == null)
            {
                return(false);
            }
            TransactionResult[] results_destroy_temp = results.Where(p => p.Amount > Fixed8.Zero).ToArray();

            TransactionResult[] results_destroy = results_destroy_temp.Where(p => p.Amount > Fixed8.Zero).ToArray();

            Fixed8 result_qrs_fee   = Fixed8.Zero;
            Fixed8 result_qrg_fee   = Fixed8.Zero;
            Fixed8 result_other_fee = Fixed8.Zero;

            for (int i = 0; i < results_destroy.Length; i++)
            {
                if (results_destroy[i].AssetId == Blockchain.GoverningToken.Hash)
                {
                    result_qrs_fee = results_destroy[i].Amount;
                }
                else if (results_destroy[i].AssetId == Blockchain.UtilityToken.Hash)
                {
                    result_qrg_fee = results_destroy[i].Amount;
                }
                else
                {
                    result_other_fee = results_destroy[i].Amount;
                }
            }

            if (result_other_fee > Fixed8.Zero || (SystemFee > result_qrg_fee && assetFee > Fixed8.Zero))
            {
                return(false);
            }

            TransactionResult[] results_issue = results.Where(p => p.Amount < Fixed8.Zero).ToArray();
            switch (Type)
            {
            case TransactionType.MinerTransaction:
            case TransactionType.ClaimTransaction:
                if (results_issue.Any(p => p.AssetId != Blockchain.UtilityToken.Hash))
                {
                    return(false);
                }
                break;

            case TransactionType.IssueTransaction:
                if (results_issue.Any(p => p.AssetId == Blockchain.UtilityToken.Hash))
                {
                    return(false);
                }
                break;

            default:
                if (results_issue.Length > 0)
                {
                    return(false);
                }
                break;
            }
            if (Attributes.Count(p => p.Usage == TransactionAttributeUsage.ECDH02 || p.Usage == TransactionAttributeUsage.ECDH03) > 1)
            {
                return(false);
            }

            if (!Sodium.PublicKeyAuth.VerifyDetached(joinSplitSig, JsHash.ToArray(), joinSplitPubKey.ToArray()))
            {
                return(false);
            }

            for (int i = 0; i < byJoinSplit.Count; i++)
            {
                if (!SnarkDllApi.Snark_JSVerify(byJoinSplit[i], joinSplitPubKey.ToArray()))
                {
                    return(false);
                }
            }

            if (Inputs.Length > 0)
            {
                return(this.VerifyScripts());
            }
            else
            {
                return(true);
            }
        }