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); } }