protected override void DeserializeExclusiveData(BinaryReader reader) { this.AssetId = reader.ReadSerializable<UInt256>(); this.ValueAssetId = reader.ReadSerializable<UInt256>(); if (AssetId == ValueAssetId) throw new FormatException(); this.Agent = reader.ReadSerializable<UInt160>(); this.Orders = new Order[reader.ReadVarInt()]; for (int i = 0; i < Orders.Length; i++) { Orders[i] = new Order(); Orders[i].DeserializeInTransaction(reader, this); } ulong count = reader.ReadVarInt(); if (count > 1) throw new FormatException(); if (count == 0) { this.SplitOrder = null; } else { this.SplitOrder = reader.ReadSerializable<SplitOrder>(); } }
//TODO: 此处需要较多的测试来证明它的正确性 //因为委托交易的验证算法有点太复杂了, //考虑未来是否可以优化这个算法 public override VerificationResult Verify() { VerificationResult result = base.Verify(); foreach (Order order in Orders) { result |= order.Verify(); if (result.HasFlag(VerificationResult.InvalidSignature)) break; } RegisterTransaction asset_value = Blockchain.Default.GetTransaction(ValueAssetId) as RegisterTransaction; if (asset_value?.AssetType != AssetType.Currency) { result |= VerificationResult.IncorrectFormat; return result; } List<Order> orders = new List<Order>(Orders); foreach (var group in Inputs.GroupBy(p => p.PrevTxId)) { Transaction tx = Blockchain.Default.GetTransaction(group.Key); if (tx == null) { result |= VerificationResult.LackOfInformation; return result; } AgencyTransaction tx_agency = tx as AgencyTransaction; if (tx_agency?.SplitOrder == null || tx_agency.AssetId != AssetId || tx_agency.ValueAssetId != ValueAssetId || tx_agency.Agent != Agent) continue; var outputs = group.Select(p => new { Input = p, Output = tx_agency.Outputs[p.PrevIndex] }).Where(p => p.Output.ScriptHash == tx_agency.SplitOrder.Client).ToDictionary(p => p.Input, p => p.Output); if (outputs.Count == 0) continue; if (outputs.Count != tx_agency.Outputs.Count(p => p.ScriptHash == tx_agency.SplitOrder.Client)) { result |= VerificationResult.IncorrectFormat; return result; } orders.Add(new Order { AssetId = this.AssetId, ValueAssetId = this.ValueAssetId, Agent = this.Agent, Amount = tx_agency.SplitOrder.Amount, Price = tx_agency.SplitOrder.Price, Client = tx_agency.SplitOrder.Client, Inputs = outputs.Keys.ToArray() }); } if (orders.Count < 2) { result |= VerificationResult.IncorrectFormat; return result; } if (orders.Count(p => p.Amount > Fixed8.Zero) == 0 || orders.Count(p => p.Amount < Fixed8.Zero) == 0) { result |= VerificationResult.IncorrectFormat; return result; } Fixed8 amount_unmatched = orders.Sum(p => p.Amount); if (amount_unmatched == Fixed8.Zero) { if (SplitOrder != null) { result |= VerificationResult.IncorrectFormat; return result; } } else { if (SplitOrder?.Amount != amount_unmatched) { result |= VerificationResult.IncorrectFormat; return result; } } foreach (Order order in orders) { TransactionOutput[] inputs = order.Inputs.Select(p => References[p]).ToArray(); if (order.Amount > Fixed8.Zero) { if (inputs.Any(p => p.AssetId != order.ValueAssetId)) { result |= VerificationResult.IncorrectFormat; return result; } if (inputs.Sum(p => p.Value) < order.Amount * order.Price) { result |= VerificationResult.Imbalanced; return result; } } else { if (inputs.Any(p => p.AssetId != order.AssetId)) { result |= VerificationResult.IncorrectFormat; return result; } if (inputs.Sum(p => p.Value) < order.Amount) { result |= VerificationResult.Imbalanced; return result; } } } if (SplitOrder != null) { Fixed8 price_worst = amount_unmatched > Fixed8.Zero ? orders.Min(p => p.Price) : orders.Max(p => p.Price); if (SplitOrder.Price != price_worst) { result |= VerificationResult.IncorrectFormat; return result; } Order[] orders_worst = orders.Where(p => p.Price == price_worst && p.Client == SplitOrder.Client).ToArray(); if (orders_worst.Length == 0) { result |= VerificationResult.IncorrectFormat; return result; } Fixed8 amount_worst = orders_worst.Sum(p => p.Amount); if (amount_worst.Abs() < amount_unmatched.Abs()) { result |= VerificationResult.IncorrectFormat; return result; } Order order_combine = new Order { AssetId = this.AssetId, ValueAssetId = this.ValueAssetId, Agent = this.Agent, Amount = amount_worst - amount_unmatched, Price = price_worst, Client = SplitOrder.Client, Inputs = orders_worst.SelectMany(p => p.Inputs).ToArray() }; foreach (Order order_worst in orders_worst) { orders.Remove(order_worst); } orders.Add(order_combine); } foreach (var group in orders.GroupBy(p => p.Client)) { TransactionOutput[] inputs = group.SelectMany(p => p.Inputs).Select(p => References[p]).ToArray(); TransactionOutput[] outputs = Outputs.Where(p => p.ScriptHash == group.Key).ToArray(); Fixed8 money_spent = inputs.Where(p => p.AssetId == ValueAssetId).Sum(p => p.Value) - outputs.Where(p => p.AssetId == ValueAssetId).Sum(p => p.Value); Fixed8 amount_changed = outputs.Where(p => p.AssetId == AssetId).Sum(p => p.Value) - inputs.Where(p => p.AssetId == AssetId).Sum(p => p.Value); if (amount_changed != group.Sum(p => p.Amount)) { result |= VerificationResult.Imbalanced; break; } if (money_spent > group.Sum(p => p.Amount * p.Price)) { result |= VerificationResult.Imbalanced; break; } } return result; }