Skip to content

Commit

Permalink
Optimize TransactionAttribute (neo-project#1674)
Browse files Browse the repository at this point in the history
  • Loading branch information
erikzhang authored and Tommo-L committed Jun 22, 2020
1 parent aa047c3 commit dfeb53e
Show file tree
Hide file tree
Showing 6 changed files with 34 additions and 20 deletions.
6 changes: 1 addition & 5 deletions src/neo/Network/P2P/Payloads/Cosigner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,7 @@ public class Cosigner : TransactionAttribute
public ECPoint[] AllowedGroups;

public override TransactionAttributeType Type => TransactionAttributeType.Cosigner;

public Cosigner()
{
this.Scopes = WitnessScope.Global;
}
public override bool AllowMultiple => true;

public override int Size => base.Size +
/*Account*/ UInt160.Length +
Expand Down
33 changes: 25 additions & 8 deletions src/neo/Network/P2P/Payloads/Transaction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ public TransactionAttribute[] Attributes
set { attributes = value; _cosigners = null; _hash = null; _size = 0; }
}

private Cosigner[] _cosigners;
public Cosigner[] Cosigners => _cosigners ??= attributes.OfType<Cosigner>().ToArray();
private Dictionary<UInt160, Cosigner> _cosigners;
public IReadOnlyDictionary<UInt160, Cosigner> Cosigners => _cosigners ??= attributes.OfType<Cosigner>().ToDictionary(p => p.Account);

/// <summary>
/// The <c>NetworkFee</c> for the transaction divided by its <c>Size</c>.
Expand Down Expand Up @@ -155,6 +155,19 @@ void ISerializable.Deserialize(BinaryReader reader)
_size = (int)reader.BaseStream.Position - startPosition;
}

private static IEnumerable<TransactionAttribute> DeserializeAttributes(BinaryReader reader)
{
int count = (int)reader.ReadVarInt(MaxTransactionAttributes);
HashSet<TransactionAttributeType> hashset = new HashSet<TransactionAttributeType>();
while (count-- > 0)
{
TransactionAttribute attribute = TransactionAttribute.DeserializeFrom(reader);
if (!attribute.AllowMultiple && !hashset.Add(attribute.Type))
throw new FormatException();
yield return attribute;
}
}

public void DeserializeUnsigned(BinaryReader reader)
{
Version = reader.ReadByte();
Expand All @@ -167,10 +180,15 @@ public void DeserializeUnsigned(BinaryReader reader)
if (NetworkFee < 0) throw new FormatException();
if (SystemFee + NetworkFee < SystemFee) throw new FormatException();
ValidUntilBlock = reader.ReadUInt32();
Attributes = new TransactionAttribute[reader.ReadVarInt(MaxTransactionAttributes)];
for (int i = 0; i < Attributes.Length; i++)
Attributes[i] = TransactionAttribute.DeserializeFrom(reader);
if (Cosigners.Select(u => u.Account).Distinct().Count() != Cosigners.Length) throw new FormatException();
Attributes = DeserializeAttributes(reader).ToArray();
try
{
_ = Cosigners;
}
catch (ArgumentException)
{
throw new FormatException();
}
Script = reader.ReadVarBytes(ushort.MaxValue);
if (Script.Length == 0) throw new FormatException();
}
Expand Down Expand Up @@ -199,8 +217,7 @@ public override int GetHashCode()

public UInt160[] GetScriptHashesForVerifying(StoreView snapshot)
{
var hashes = new HashSet<UInt160> { Sender };
hashes.UnionWith(Cosigners.Select(p => p.Account));
var hashes = new HashSet<UInt160>(Cosigners.Keys) { Sender };
return hashes.OrderBy(p => p).ToArray();
}

Expand Down
1 change: 1 addition & 0 deletions src/neo/Network/P2P/Payloads/TransactionAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ namespace Neo.Network.P2P.Payloads
public abstract class TransactionAttribute : ISerializable
{
public abstract TransactionAttributeType Type { get; }
public abstract bool AllowMultiple { get; }
public virtual int Size => sizeof(TransactionAttributeType);

public void Deserialize(BinaryReader reader)
Expand Down
3 changes: 1 addition & 2 deletions src/neo/SmartContract/ApplicationEngine.Runtime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,7 @@ internal bool CheckWitnessInternal(UInt160 hash)
{
if (ScriptContainer is Transaction tx)
{
Cosigner cosigner = tx.Cosigners.FirstOrDefault(p => p.Account.Equals(hash));
if (cosigner is null) return false;
if (!tx.Cosigners.TryGetValue(hash, out Cosigner cosigner)) return false;
if (cosigner.Scopes == WitnessScope.Global) return true;
if (cosigner.Scopes.HasFlag(WitnessScope.CalledByEntry))
{
Expand Down
2 changes: 1 addition & 1 deletion tests/neo.UnitTests/Ledger/UT_PoolItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ public static Transaction GenerateTx(long networkFee, int size, byte[] overrideS
};

tx.Attributes.Length.Should().Be(0);
tx.Cosigners.Length.Should().Be(0);
tx.Cosigners.Count.Should().Be(0);

int diff = size - tx.Size;
if (diff < 0) throw new ArgumentException();
Expand Down
9 changes: 5 additions & 4 deletions tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using Neo.VM;
using Neo.Wallets;
using System;
using System.Linq;
using System.Numerics;

namespace Neo.UnitTests.Network.P2P.Payloads
Expand Down Expand Up @@ -250,10 +251,10 @@ public void FeeIsSignatureContractDetailed()
// Part II
Assert.AreEqual(23, tx.Attributes.GetVarSize());
Assert.AreEqual(1, tx.Attributes.Length);
Assert.AreEqual(1, tx.Cosigners.Length);
Assert.AreEqual(23, tx.Cosigners.GetVarSize());
Assert.AreEqual(1, tx.Cosigners.Count);
Assert.AreEqual(23, tx.Cosigners.Values.ToArray().GetVarSize());
// Note that Data size and Usage size are different (because of first byte on GetVarSize())
Assert.AreEqual(22, tx.Cosigners[0].Size);
Assert.AreEqual(22, tx.Cosigners.Values.First().Size);
// Part III
Assert.AreEqual(86, tx.Script.GetVarSize());
// Part IV
Expand Down Expand Up @@ -649,7 +650,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_GAS()
// no attributes must exist
tx.Attributes.Length.Should().Be(1);
// one cosigner must exist
tx.Cosigners.Length.Should().Be(1);
tx.Cosigners.Count.Should().Be(1);

// Fast check
Assert.IsTrue(tx.VerifyWitnesses(snapshot, tx.NetworkFee));
Expand Down

0 comments on commit dfeb53e

Please sign in to comment.