diff --git a/src/AntShares/Core/AgencyTransaction.cs b/src/AntShares/Core/AgencyTransaction.cs
deleted file mode 100644
index e0aa061587..0000000000
--- a/src/AntShares/Core/AgencyTransaction.cs
+++ /dev/null
@@ -1,242 +0,0 @@
-using AntShares.IO;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-
-namespace AntShares.Core
-{
- ///
- /// 委托交易
- /// 交易规则:
- /// 1. 单个交易中,所有订单的代理人必须是同一人;
- /// 2. 单个交易中,所有订单的交易商品必须完全相同,交易货币也必须完全相同;
- /// 3. 交易商品不能和交易货币相同;
- /// 4. 买盘和卖盘两者都至少需要包含一笔订单;
- /// 5. 交易中不能包含完全未成交的订单,且至多只能包含一笔部分成交的订单;
- /// 6. 如果存在部分成交的订单,则该订单的价格必须是最差的,即:对于买单,它的价格是最低价格;对于卖单,它的价格是最高价格;
- /// 7. 对于买单,需以不高于委托方所指定的价格成交;
- /// 8. 对于卖单,需以不低于委托方所指定的价格成交;
- /// 9. 交易数量精确到10^-4,交易价格精确到10^-4;
- ///
- public class AgencyTransaction : Transaction
- {
- ///
- /// 资产编号
- ///
- public UInt256 AssetId;
- ///
- /// 货币编号
- ///
- public UInt256 ValueAssetId;
- ///
- /// 代理人的合约散列
- ///
- public UInt160 Agent;
- ///
- /// 订单列表
- ///
- public Order[] Orders;
- ///
- /// 部分成交的订单
- ///
- public SplitOrder SplitOrder;
-
- public override int Size => base.Size + AssetId.Size + ValueAssetId.Size + Agent.Size + Orders.Length.GetVarSize() + Orders.Sum(p => p.SizeInTransaction) + SplitOrder.Size;
-
- public AgencyTransaction()
- : base(TransactionType.AgencyTransaction)
- {
- }
-
- ///
- /// 反序列化交易中的额外数据
- ///
- /// 数据来源
- protected override void DeserializeExclusiveData(BinaryReader reader)
- {
- this.AssetId = reader.ReadSerializable();
- this.ValueAssetId = reader.ReadSerializable();
- this.Agent = reader.ReadSerializable();
- this.Orders = new Order[reader.ReadVarInt(0x1000)];
- for (int i = 0; i < Orders.Length; i++)
- {
- Orders[i] = new Order();
- Orders[i].DeserializeInTransaction(reader, this);
- }
- if (reader.ReadVarInt(1) == 0)
- {
- this.SplitOrder = null;
- }
- else
- {
- this.SplitOrder = reader.ReadSerializable();
- }
- }
-
- ///
- /// 获取交易中所有的输入
- ///
- /// 返回交易中所有的输入以及订单中的所有输入
- public override IEnumerable GetAllInputs()
- {
- return Orders.SelectMany(p => p.Inputs).Concat(base.GetAllInputs());
- }
-
- ///
- /// 获得需要校验的脚本Hash
- ///
- /// 返回需要校验的脚本Hash
- public override UInt160[] GetScriptHashesForVerifying()
- {
- HashSet hashes = new HashSet();
- foreach (var group in Inputs.GroupBy(p => p.PrevHash))
- {
- Transaction tx = Blockchain.Default.GetTransaction(group.Key);
- if (tx == null) throw new InvalidOperationException();
- AgencyTransaction tx_agency = tx as AgencyTransaction;
- if (tx_agency?.SplitOrder == null || tx_agency.AssetId != AssetId || tx_agency.ValueAssetId != ValueAssetId || tx_agency.Agent != Agent)
- {
- hashes.UnionWith(group.Select(p => tx.Outputs[p.PrevIndex].ScriptHash));
- }
- else
- {
- hashes.UnionWith(group.Select(p => tx.Outputs[p.PrevIndex].ScriptHash).Where(p => p != tx_agency.SplitOrder.Client));
- }
- }
- hashes.Add(Agent);
- return hashes.OrderBy(p => p).ToArray();
- }
-
- ///
- /// 序列化交易中的额外数据
- ///
- /// 存放序列化后的结果
- protected override void SerializeExclusiveData(BinaryWriter writer)
- {
- writer.Write(AssetId);
- writer.Write(ValueAssetId);
- writer.Write(Agent);
- writer.WriteVarInt(Orders.Length);
- for (int i = 0; i < Orders.Length; i++)
- {
- Orders[i].SerializeInTransaction(writer);
- }
- if (SplitOrder == null)
- {
- writer.WriteVarInt(0);
- }
- else
- {
- writer.WriteVarInt(1);
- writer.Write(SplitOrder);
- }
- }
-
- //TODO: 此处需要较多的测试来证明它的正确性
- //因为委托交易的验证算法有点太复杂了,
- //考虑未来是否可以优化这个算法
- ///
- /// 验证交易
- ///
- /// 返回验证的结果
- public override bool Verify(IEnumerable mempool)
- {
- if (!base.Verify(mempool)) return false;
- foreach (Order order in Orders)
- if (!order.VerifySignature())
- return false;
- if (AssetId == ValueAssetId) return false;
- RegisterTransaction asset_value = Blockchain.Default.GetTransaction(ValueAssetId) as RegisterTransaction;
- if (asset_value?.AssetType != AssetType.Currency)
- return false;
- List orders = new List(Orders);
- foreach (var group in Inputs.GroupBy(p => p.PrevHash))
- {
- Transaction tx = Blockchain.Default.GetTransaction(group.Key);
- if (tx == null) return false;
- 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))
- return false;
- 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) return false;
- if (orders.Count(p => p.Amount > Fixed8.Zero) == 0 || orders.Count(p => p.Amount < Fixed8.Zero) == 0)
- return false;
- Fixed8 amount_unmatched = orders.Sum(p => p.Amount);
- if (amount_unmatched == Fixed8.Zero)
- {
- if (SplitOrder != null) return false;
- }
- else
- {
- if (SplitOrder?.Amount != amount_unmatched) return false;
- }
- 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)) return false;
- if (inputs.Sum(p => p.Value) < order.Amount * order.Price) return false;
- }
- else
- {
- if (inputs.Any(p => p.AssetId != order.AssetId)) return false;
- if (inputs.Sum(p => p.Value) < order.Amount) return false;
- }
- }
- 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) return false;
- Order[] orders_worst = orders.Where(p => p.Price == price_worst && p.Client == SplitOrder.Client).ToArray();
- if (orders_worst.Length == 0) return false;
- Fixed8 amount_worst = orders_worst.Sum(p => p.Amount);
- if (amount_worst.Abs() < amount_unmatched.Abs()) return false;
- 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)) return false;
- if (money_spent > group.Sum(p => p.Amount * p.Price)) return false;
- }
- return true;
- }
- }
-}
diff --git a/src/AntShares/Core/Order.cs b/src/AntShares/Core/Order.cs
deleted file mode 100644
index 870d422788..0000000000
--- a/src/AntShares/Core/Order.cs
+++ /dev/null
@@ -1,139 +0,0 @@
-using AntShares.IO;
-using AntShares.VM;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-
-namespace AntShares.Core
-{
- ///
- /// 订单
- ///
- public class Order : ISignable
- {
- ///
- /// 资产编号
- ///
- public UInt256 AssetId;
- ///
- /// 货币编号
- ///
- public UInt256 ValueAssetId;
- ///
- /// 代理人的合约散列
- ///
- public UInt160 Agent;
- ///
- /// 买入或卖出的数量,正数表示买入,负数表示卖出
- ///
- public Fixed8 Amount;
- ///
- /// 价格
- ///
- public Fixed8 Price;
- ///
- /// 委托人的合约散列
- ///
- public UInt160 Client;
- ///
- /// 输入列表
- ///
- public CoinReference[] Inputs;
- ///
- /// 用于验证该订单的脚本列表
- ///
- public Witness[] Scripts { get; set; }
-
- public int Size => AssetId.Size + ValueAssetId.Size + Agent.Size + SizeInTransaction;
-
- public int SizeInTransaction => Amount.Size + Price.Size + Client.Size + Inputs.Length.GetVarSize() + Inputs.Sum(p => p.Size) + Scripts.Length.GetVarSize() + Scripts.Sum(p => p.Size);
-
- void ISerializable.Deserialize(BinaryReader reader)
- {
- ((ISignable)this).DeserializeUnsigned(reader);
- Scripts = reader.ReadSerializableArray();
- }
-
- internal void DeserializeInTransaction(BinaryReader reader, AgencyTransaction tx)
- {
- DeserializeUnsignedInternal(reader, tx.AssetId, tx.ValueAssetId, tx.Agent);
- Scripts = reader.ReadSerializableArray();
- }
-
- void ISignable.DeserializeUnsigned(BinaryReader reader)
- {
- UInt256 asset_id = reader.ReadSerializable();
- UInt256 value_asset_id = reader.ReadSerializable();
- if (asset_id == value_asset_id) throw new FormatException();
- UInt160 agent = reader.ReadSerializable();
- DeserializeUnsignedInternal(reader, asset_id, value_asset_id, agent);
- }
-
- private void DeserializeUnsignedInternal(BinaryReader reader, UInt256 asset_id, UInt256 value_asset_id, UInt160 agent)
- {
- AssetId = asset_id;
- ValueAssetId = value_asset_id;
- Agent = agent;
- Amount = reader.ReadSerializable();
- if (Amount == Fixed8.Zero) throw new FormatException();
- if (Amount.GetData() % 10000 != 0) throw new FormatException();
- Price = reader.ReadSerializable();
- if (Price <= Fixed8.Zero) throw new FormatException();
- if (Price.GetData() % 10000 != 0) throw new FormatException();
- Client = reader.ReadSerializable();
- Inputs = reader.ReadSerializableArray();
- if (Inputs.Distinct().Count() != Inputs.Length)
- throw new FormatException();
- }
-
- byte[] IScriptContainer.GetMessage()
- {
- return this.GetHashData();
- }
-
- UInt160[] ISignable.GetScriptHashesForVerifying()
- {
- HashSet hashes = new HashSet();
- RegisterTransaction asset = Blockchain.Default.GetTransaction(AssetId) as RegisterTransaction;
- if (asset == null) throw new InvalidOperationException();
- if (asset.AssetType == AssetType.Share)
- {
- hashes.Add(Client);
- }
- foreach (var group in Inputs.GroupBy(p => p.PrevHash))
- {
- Transaction tx = Blockchain.Default.GetTransaction(group.Key);
- if (tx == null) throw new InvalidOperationException();
- hashes.UnionWith(group.Select(p => tx.Outputs[p.PrevIndex].ScriptHash));
- }
- return hashes.OrderBy(p => p).ToArray();
- }
-
- void ISerializable.Serialize(BinaryWriter writer)
- {
- ((ISignable)this).SerializeUnsigned(writer);
- writer.Write(Scripts);
- }
-
- internal void SerializeInTransaction(BinaryWriter writer)
- {
- writer.Write(Amount);
- writer.Write(Price);
- writer.Write(Client);
- writer.Write(Inputs);
- writer.Write(Scripts);
- }
-
- void ISignable.SerializeUnsigned(BinaryWriter writer)
- {
- writer.Write(AssetId);
- writer.Write(ValueAssetId);
- writer.Write(Agent);
- writer.Write(Amount);
- writer.Write(Price);
- writer.Write(Client);
- writer.Write(Inputs);
- }
- }
-}
diff --git a/src/AntShares/Core/SplitOrder.cs b/src/AntShares/Core/SplitOrder.cs
deleted file mode 100644
index bc9fabe007..0000000000
--- a/src/AntShares/Core/SplitOrder.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-using AntShares.IO;
-using System;
-using System.IO;
-
-namespace AntShares.Core
-{
- ///
- /// 部分成交的订单
- ///
- public class SplitOrder : ISerializable
- {
- ///
- /// 买入或卖出的数量
- ///
- public Fixed8 Amount;
- ///
- /// 价格
- ///
- public Fixed8 Price;
- ///
- /// 委托人的合约散列
- ///
- public UInt160 Client;
-
- public int Size => Amount.Size + Price.Size + Client.Size;
-
- void ISerializable.Deserialize(BinaryReader reader)
- {
- this.Amount = reader.ReadSerializable();
- if (Amount == Fixed8.Zero) throw new FormatException();
- if (Amount.GetData() % 10000 != 0) throw new FormatException();
- this.Price = reader.ReadSerializable();
- if (Price <= Fixed8.Zero) throw new FormatException();
- if (Price.GetData() % 10000 != 0) throw new FormatException();
- this.Client = reader.ReadSerializable();
- }
-
- void ISerializable.Serialize(BinaryWriter writer)
- {
- writer.Write(Amount);
- writer.Write(Price);
- writer.Write(Client);
- }
- }
-}
diff --git a/src/AntShares/Core/Transaction.cs b/src/AntShares/Core/Transaction.cs
index d8b1d9c5a3..1ebc05395c 100644
--- a/src/AntShares/Core/Transaction.cs
+++ b/src/AntShares/Core/Transaction.cs
@@ -72,7 +72,7 @@ public IReadOnlyDictionary References
if (_references == null)
{
Dictionary dictionary = new Dictionary();
- foreach (var group in GetAllInputs().GroupBy(p => p.PrevHash))
+ foreach (var group in Inputs.GroupBy(p => p.PrevHash))
{
Transaction tx = Blockchain.Default.GetTransaction(group.Key);
if (tx == null) return null;
@@ -188,15 +188,6 @@ public override bool Equals(object obj)
return Equals(obj as Transaction);
}
- ///
- /// 获取交易的所有输入
- ///
- /// 返回交易的所有输入
- public virtual IEnumerable GetAllInputs()
- {
- return Inputs;
- }
-
public override int GetHashCode()
{
return Hash.GetHashCode();
@@ -322,12 +313,11 @@ public virtual bool Verify(IEnumerable mempool)
if (Blockchain.Default.ContainsTransaction(Hash)) return true;
if (!Blockchain.Default.Ability.HasFlag(BlockchainAbility.UnspentIndexes) || !Blockchain.Default.Ability.HasFlag(BlockchainAbility.TransactionIndexes))
return false;
- CoinReference[] inputs = GetAllInputs().ToArray();
- for (int i = 1; i < inputs.Length; i++)
+ 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)
+ if (Inputs[i].PrevHash == Inputs[j].PrevHash && Inputs[i].PrevIndex == Inputs[j].PrevIndex)
return false;
- if (mempool.SelectMany(p => p.GetAllInputs()).Intersect(GetAllInputs()).Count() > 0)
+ if (mempool.SelectMany(p => p.Inputs).Intersect(Inputs).Count() > 0)
return false;
if (Blockchain.Default.IsDoubleSpend(this))
return false;
diff --git a/src/AntShares/Implementations/Blockchains/LevelDB/LevelDBBlockchain.cs b/src/AntShares/Implementations/Blockchains/LevelDB/LevelDBBlockchain.cs
index 9da5652d34..dfc3647e5d 100644
--- a/src/AntShares/Implementations/Blockchains/LevelDB/LevelDBBlockchain.cs
+++ b/src/AntShares/Implementations/Blockchains/LevelDB/LevelDBBlockchain.cs
@@ -235,7 +235,7 @@ public override IEnumerable GetEnrollments(IEnumerable k))
{
UInt256 hash = new UInt256(key.ToArray().Skip(1).Take(32).ToArray());
- if (others.SelectMany(p => p.GetAllInputs()).Any(p => p.PrevHash == hash && p.PrevIndex == 0))
+ if (others.SelectMany(p => p.Inputs).Any(p => p.PrevHash == hash && p.PrevIndex == 0))
continue;
yield return (EnrollmentTransaction)GetTransaction(options, hash, out height);
}
@@ -380,7 +380,7 @@ public override IEnumerable GetVotes(IEnumerable others)
foreach (var kv in db.Find(options, SliceBuilder.Begin(DataEntryPrefix.IX_Vote), (k, v) => new { Key = k, Value = v }))
{
UInt256 hash = new UInt256(kv.Key.ToArray().Skip(1).ToArray());
- ushort[] indexes = kv.Value.ToArray().GetUInt16Array().Except(others.SelectMany(p => p.GetAllInputs()).Where(p => p.PrevHash == hash).Select(p => p.PrevIndex)).ToArray();
+ ushort[] indexes = kv.Value.ToArray().GetUInt16Array().Except(others.SelectMany(p => p.Inputs).Where(p => p.PrevHash == hash).Select(p => p.PrevIndex)).ToArray();
if (indexes.Length == 0) continue;
Transaction tx = GetTransaction(options, hash, out height);
yield return new Vote
@@ -404,12 +404,11 @@ public override IEnumerable GetVotes(IEnumerable others)
public override bool IsDoubleSpend(Transaction tx)
{
- CoinReference[] inputs = tx.GetAllInputs().ToArray();
- if (inputs.Length == 0) return false;
+ if (tx.Inputs.Length == 0) return false;
ReadOptions options = new ReadOptions();
using (options.Snapshot = db.GetSnapshot())
{
- foreach (var group in inputs.GroupBy(p => p.PrevHash))
+ foreach (var group in tx.Inputs.GroupBy(p => p.PrevHash))
{
Slice value;
if (!db.TryGet(options, SliceBuilder.Begin(DataEntryPrefix.IX_Unspent).Add(group.Key), out value))
@@ -523,7 +522,7 @@ private void Persist(Block block)
unspents.Add(tx.Hash, index);
}
}
- foreach (var group in block.Transactions.SelectMany(p => p.GetAllInputs()).GroupBy(p => p.PrevHash))
+ foreach (var group in block.Transactions.SelectMany(p => p.Inputs).GroupBy(p => p.PrevHash))
{
int height;
Transaction tx = GetTransaction(ReadOptions.Default, group.Key, out height);
diff --git a/src/AntShares/Network/LocalNode.cs b/src/AntShares/Network/LocalNode.cs
index 9ba7e51d4f..7d8246068c 100644
--- a/src/AntShares/Network/LocalNode.cs
+++ b/src/AntShares/Network/LocalNode.cs
@@ -116,7 +116,7 @@ public static void AllowHashes(IEnumerable hashes)
private static void Blockchain_PersistCompleted(object sender, Block block)
{
- HashSet inputs = new HashSet(block.Transactions.SelectMany(p => p.GetAllInputs()));
+ HashSet inputs = new HashSet(block.Transactions.SelectMany(p => p.Inputs));
lock (MemoryPool)
{
foreach (Transaction tx in block.Transactions)
@@ -125,7 +125,7 @@ private static void Blockchain_PersistCompleted(object sender, Block block)
}
foreach (Transaction tx in MemoryPool.Values.ToArray())
{
- foreach (CoinReference input in tx.GetAllInputs())
+ foreach (CoinReference input in tx.Inputs)
if (inputs.Contains(input))
{
MemoryPool.Remove(tx.Hash);
diff --git a/src/AntShares/Wallets/Wallet.cs b/src/AntShares/Wallets/Wallet.cs
index 159643b76c..f79c801760 100644
--- a/src/AntShares/Wallets/Wallet.cs
+++ b/src/AntShares/Wallets/Wallet.cs
@@ -679,7 +679,7 @@ private void ProcessNewBlock(Block block)
}
foreach (Transaction tx in block.Transactions)
{
- foreach (CoinReference input in tx.GetAllInputs())
+ foreach (CoinReference input in tx.Inputs)
{
if (coins.Contains(input))
{
@@ -728,9 +728,9 @@ public bool SaveTransaction(Transaction tx)
lock (contracts)
lock (coins)
{
- if (tx.GetAllInputs().Any(p => !coins.Contains(p) || coins[p].State.HasFlag(CoinState.Spent) || !coins[p].State.HasFlag(CoinState.Confirmed)))
+ if (tx.Inputs.Any(p => !coins.Contains(p) || coins[p].State.HasFlag(CoinState.Spent) || !coins[p].State.HasFlag(CoinState.Confirmed)))
return false;
- foreach (CoinReference input in tx.GetAllInputs())
+ foreach (CoinReference input in tx.Inputs)
{
coins[input].State |= CoinState.Spent;
coins[input].State &= ~CoinState.Confirmed;