public override UInt160[] GetScriptHashesForVerifying() { HashSet <UInt160> hashes = new HashSet <UInt160>(base.GetScriptHashesForVerifying()); foreach (TransactionResult result in GetTransactionResults().Where(p => p.Amount < Fixed8.Zero)) { AssetState asset = Blockchain.Default.GetAssetState(result.AssetId); if (asset == null) { throw new InvalidOperationException(); } hashes.Add(asset.Issuer); } return(hashes.OrderBy(p => p).ToArray()); }
public virtual UInt160[] GetScriptHashesForVerifying() { if (References == null) { throw new InvalidOperationException(); } HashSet <UInt160> hashes = new HashSet <UInt160>(Inputs.Select(p => References[p].ScriptHash)); hashes.UnionWith(Attributes.Where(p => p.Usage == TransactionAttributeUsage.Script).Select(p => new UInt160(p.Data))); foreach (var group in Outputs.GroupBy(p => p.AssetId)) { AssetState asset = Blockchain.Default.GetAssetState(group.Key); if (asset == null) { throw new InvalidOperationException(); } if (asset.AssetType.HasFlag(AssetType.DutyFlag)) { hashes.UnionWith(group.Select(p => p.ScriptHash)); } } return(hashes.OrderBy(p => p).ToArray()); }
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); } }
public static Dictionary <UInt256, Fixed8> CalculateNetFee(IEnumerable <Transaction> transactions) { Dictionary <UInt256, Fixed8> ret = new Dictionary <UInt256, Fixed8>(); #region Calculate QRG fee { Transaction[] ats = transactions.Where(p => p.Type == TransactionType.AnonymousContractTransaction).ToArray(); Transaction[] ars = transactions.Where(p => p.Type == TransactionType.RingConfidentialTransaction).ToArray(); foreach (var arx in ars) { if (arx is RingConfidentialTransaction) { var ringTR = arx as RingConfidentialTransaction; if (ringTR.RingCTSig.Count > 0) { AssetState asset = Blockchain.Default.GetAssetState(ringTR.RingCTSig[0].AssetID); if (ret.ContainsKey(asset.AssetId)) { ret[asset.AssetId] += asset.AFee; } else { ret[asset.AssetId] = asset.AFee; } } } } foreach (var atx in ats) { if (atx is AnonymousContractTransaction) { var formatedTx = atx as AnonymousContractTransaction; if (formatedTx.Outputs.Length > 0) { AssetState asset = Blockchain.Default.GetAssetState(formatedTx.Outputs[0].AssetId); if (ret.ContainsKey(asset.AssetId)) { ret[asset.AssetId] += asset.AFee; } else { ret[asset.AssetId] = asset.AFee; } } else { AssetState asset = Blockchain.Default.GetAssetState(((AnonymousContractTransaction)formatedTx).Asset_ID(0)); if (ret.ContainsKey(asset.AssetId)) { ret[asset.AssetId] += asset.AFee; } else { ret[asset.AssetId] = asset.AFee; } } } } Transaction[] invocationTxs = transactions.Where(p => p.Type == TransactionType.InvocationTransaction).ToArray(); foreach (var tx in invocationTxs) { if (ret.ContainsKey(Blockchain.UtilityToken.Hash)) { ret[Blockchain.UtilityToken.Hash] += ((InvocationTransaction)tx).Gas.Equals(Fixed8.Zero) ? tx.NetworkFee : ((InvocationTransaction)tx).Gas; } else { ret[Blockchain.UtilityToken.Hash] = ((InvocationTransaction)tx).Gas.Equals(Fixed8.Zero) ? tx.NetworkFee : ((InvocationTransaction)tx).Gas; } } Transaction[] issueTxs = transactions.Where(p => p.Type == TransactionType.IssueTransaction).ToArray(); foreach (var tx in issueTxs) { IssueTransaction tempTx = (IssueTransaction)tx; if (ret.ContainsKey(Blockchain.UtilityToken.Hash)) { ret[Blockchain.UtilityToken.Hash] += tempTx.NetworkFee + tempTx.SystemFee; } else { ret[Blockchain.UtilityToken.Hash] = tempTx.NetworkFee + tempTx.SystemFee; } } Transaction[] feeTxs = transactions.Where(p => p.Type != TransactionType.MinerTransaction && p.Type != TransactionType.ClaimTransaction && p.Type != TransactionType.AnonymousContractTransaction && p.Type != TransactionType.RingConfidentialTransaction && p.Type != TransactionType.InvocationTransaction && p.Type != TransactionType.IssueTransaction).ToArray(); foreach (var tx in feeTxs) { Dictionary <UInt256, Fixed8> fee = new Dictionary <UInt256, Fixed8>(); foreach (var txOut in tx.Outputs) { if (!ret.ContainsKey(txOut.AssetId)) { ret[txOut.AssetId] = txOut.Fee; } else { ret[txOut.AssetId] += txOut.Fee; } } } } #endregion Dictionary <UInt256, Fixed8> retFees = new Dictionary <UInt256, Fixed8>(); foreach (var key in ret.Keys) { if (ret[key] > Fixed8.Zero) { retFees[key] = ret[key]; } } return(retFees); }
public bool Verify(bool completely) { if (!Verify()) { return(false); } if (Transactions[0].Type != TransactionType.MinerTransaction || Transactions.Skip(1).Any(p => p.Type == TransactionType.MinerTransaction)) { return(false); } if (completely) { if (NextConsensus != Blockchain.GetConsensusAddress(Blockchain.Default.GetValidators(Transactions).ToArray())) { return(false); } foreach (Transaction tx in Transactions) { if (!tx.Verify(Transactions.Where(p => !p.Hash.Equals(tx.Hash)))) { return(false); } } Transaction tx_gen = Transactions.FirstOrDefault(p => p.Type == TransactionType.MinerTransaction); Dictionary <UInt256, Fixed8> assetFee = CalculateNetFee(Transactions); foreach (var key in assetFee.Keys) { AssetState asset = Blockchain.Default.GetAssetState(key); if (asset.AssetId == Blockchain.GoverningToken.Hash) { if (tx_gen?.Outputs.Where(p => p.AssetId == Blockchain.GoverningToken.Hash).Sum(p => p.Value) != assetFee[key]) { return(false); } } else if (asset.AssetId == Blockchain.UtilityToken.Hash) { if (tx_gen?.Outputs.Where(p => p.AssetId == Blockchain.UtilityToken.Hash).Sum(p => p.Value) != assetFee.Where(p => p.Key != Blockchain.GoverningToken.Hash).Sum(p => p.Value)) { return(false); } } else { Fixed8 consensusFee = Fixed8.Zero; Fixed8 assetOwnerFee = Fixed8.Zero; if (assetFee[key] <= Fixed8.Satoshi * 10000000) { consensusFee = assetFee[key] * 8 / 10; assetOwnerFee = assetFee[key] * 2 / 10; } else if (assetFee[key] < Fixed8.FromDecimal(1)) { consensusFee = assetFee[key] * 75 / 100; assetOwnerFee = assetFee[key] * 25 / 100; } else if (assetFee[key] < Fixed8.FromDecimal(5)) { consensusFee = assetFee[key] * 7 / 10; assetOwnerFee = assetFee[key] * 3 / 10; } else if (assetFee[key] < Fixed8.FromDecimal(10)) { consensusFee = assetFee[key] * 65 / 100; assetOwnerFee = assetFee[key] * 35 / 100; } else { consensusFee = assetFee[key] * 6 / 10; assetOwnerFee = assetFee[key] * 4 / 10; } if (tx_gen?.Outputs.Where(p => p.ScriptHash == asset.FeeAddress).Sum(p => p.Value) != assetOwnerFee) { return(false); } } } // if (tx_gen?.Outputs.Sum(p => p.Value) != CalculateNetFee(Transactions)) return false; } return(true); }
public override bool Verify(IEnumerable <Transaction> mempool) { switch (GetTxType()) { case RingConfidentialTransactionType.S_S_Transaction: for (int i = 0; i < RingCTSig.Count; i++) { if (!RingCT.Impls.RingCTSignature.Verify(RingCTSig[i], Fixed8.Zero)) { return(false); } // Double Spending Check if (Blockchain.Default.IsDoubleRingCTCommitment(this)) { return(false); } // Check Mix Rings Link & Asset UInt256 assetID = RingCTSig[i].AssetID; for (int j = 0; j < RingCTSig[i].mixRing.Count; j++) { for (int k = 0; k < RingCTSig[i].mixRing[j].Count; k++) { Transaction tx = Blockchain.Default.GetTransaction(RingCTSig[i].mixRing[j][k].txHash); if (tx is RingConfidentialTransaction rtx) { if (rtx.RingCTSig[RingCTSig[i].mixRing[j][k].RingCTIndex].AssetID != assetID) { //AssetState asset_open = Blockchain.Default.GetAssetState(rtx.RingCTSig[RingCTSig[i].mixRing[j][k].RingCTIndex].AssetID); AssetState asset_send = Blockchain.Default.GetAssetState(assetID); if (!(asset_send.AssetType == AssetType.StealthToken && rtx.RingCTSig[RingCTSig[i].mixRing[j][k].RingCTIndex].AssetID == Blockchain.StealthNormalHash)) { return(false); } } } else { return(false); } } } } break; case RingConfidentialTransactionType.S_T_Transaction: // Check Amount try { Dictionary <UInt256, Fixed8> amount = new Dictionary <UInt256, Fixed8>(); UInt256 mainAssetId = null; for (int i = 0; i < Outputs.Length; i++) { if (amount.ContainsKey(Outputs[i].AssetId)) { amount[Outputs[i].AssetId] += Outputs[i].Value; } else { mainAssetId = Outputs[i].AssetId; amount[Outputs[i].AssetId] = Outputs[i].Value; } } for (int i = 0; i < RingCTSig.Count; i++) { if (!RingCT.Impls.RingCTSignature.Verify(RingCTSig[i], Fixed8.Zero)) { return(false); } // Double Spending Check if (Blockchain.Default.IsDoubleRingCTCommitment(this)) { return(false); } if (amount.ContainsKey(RingCTSig[i].AssetID)) { amount[RingCTSig[i].AssetID] -= RingCTSig[i].vPub; } else if (i == RingCTSig.Count - 1 && RingCTSig[i].AssetID == Blockchain.UtilityToken.Hash && RingCTSig[i].vPub != Fixed8.Zero) { amount[RingCTSig[i].AssetID] = Fixed8.Zero - RingCTSig[i].vPub; } else { return(false); } } if (SystemFee > Fixed8.Zero) { if (mainAssetId.Equals(Blockchain.UtilityToken.Hash) || mainAssetId.Equals(Blockchain.GoverningToken.Hash)) { amount[Blockchain.UtilityToken.Hash] += Blockchain.UtilityToken.A_Fee; } else { AssetState asset = Blockchain.Default.GetAssetState(mainAssetId); amount[Blockchain.UtilityToken.Hash] += asset.AFee; } } foreach (var key in amount.Keys) { if (amount[key] > Fixed8.Zero) { return(false); } } } catch (Exception ex) { return(false); } break; case RingConfidentialTransactionType.T_S_Transaction: for (int i = 0; i < RingCTSig.Count; i++) { Fixed8 vPubOld = Fixed8.Zero; for (int j = 0; j < Inputs.Length; j++) { Transaction tx = Blockchain.Default.GetTransaction(Inputs[j].PrevHash); if (tx.Outputs[Inputs[j].PrevIndex].AssetId.ToString() == RingCTSig[i].AssetID.ToString()) { vPubOld += tx.Outputs[Inputs[j].PrevIndex].Value; } } if (!RingCT.Impls.RingCTSignature.Verify(RingCTSig[i], vPubOld)) { return(false); } // Check the amount Fixed8 vPubOut = Fixed8.Zero; for (int j = 0; j < Outputs.Length; j++) { if (Outputs[j].AssetId == RingCTSig[i].AssetID) { vPubOut += Outputs[j].Value; } } Fixed8 vPrivOut = Fixed8.Zero; for (int j = 0; j < RingCTSig[i].ecdhInfo.Count; j++) { vPrivOut += RingCTSig[i].ecdhInfo[j].amount.ToFixed8(); } if (RingCTSig[i].AssetID == Blockchain.GoverningToken.Hash) { if (vPubOld < vPrivOut + vPubOut + QrsSystemFee) { return(false); } } else if (RingCTSig[i].AssetID == Blockchain.UtilityToken.Hash) { if (vPubOld < vPrivOut + vPubOut + SystemFee) { return(false); } } else { if (vPubOld < vPrivOut + vPubOut) { return(false); } } } break; default: break; } return(true); }
public virtual bool Verify(IEnumerable <Transaction> mempool) { if (Type == TransactionType.ContractTransaction) { UInt160 toAddress = Outputs[0].ScriptHash; bool isSelf = true; for (int i = 1; i < Outputs.Length; i++) { if (toAddress != Outputs[i].ScriptHash) { isSelf = false; break; } } if (isSelf) { if (toAddress == References[Inputs[0]].ScriptHash) { return(false); } } } 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 (is_consensus_mempool == false) { if (Blockchain.Default.IsDoubleSpend(this)) { Console.WriteLine("double spent error"); return(false); } Fixed8 assetFee = Fixed8.Zero; 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); } } if (Type == TransactionType.ContractTransaction) { foreach (TransactionOutput output in group) { bool isOwn = false; foreach (var refOutput in References.Values) { if (output.ScriptHash == refOutput.ScriptHash) { isOwn = true; break; } } if (isOwn == false) { assetFee += asset.Fee; } } } } TransactionResult[] results = GetTransactionResults()?.ToArray(); if (results == null) { return(false); } TransactionResult[] results_destroy = results.Where(p => p.Amount > Fixed8.Zero).ToArray(); if (results_destroy.Length > 1) { return(false); } if (results_destroy.Length == 1 && results_destroy[0].AssetId != Blockchain.UtilityToken.Hash) { return(false); } if (SystemFee + assetFee > Fixed8.Zero && (results_destroy.Length == 0 || results_destroy[0].Amount < SystemFee)) { return(false); } TransactionResult[] results_issue = results.Where(p => p.Amount < Fixed8.Zero).ToArray(); switch (Type) { case TransactionType.MinerTransaction: if (results_issue.Any(p => p.AssetId != Blockchain.UtilityToken.Hash && p.AssetId != Blockchain.GoverningToken.Hash)) { return(false); } break; 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; case TransactionType.InvocationTransaction: if (Outputs.Length <= 0) { 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); } Console.WriteLine("Verifying Scripts"); return(this.VerifyScripts()); } return(true); }