Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix claim #34

Merged
merged 21 commits into from
Dec 14, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions SimplePolicy.UnitTests/SimplePolicy.UnitTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,17 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="FluentAssertions" Version="4.19.4" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0" />
<PackageReference Include="MSTest.TestAdapter" Version="1.2.0" />
<PackageReference Include="MSTest.TestFramework" Version="1.2.0" />
<None Update="protocol.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks nice! Gives access to more information, very good.

</ItemGroup>

<ItemGroup>
<PackageReference Include="FluentAssertions" Version="5.5.3" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
<PackageReference Include="MSTest.TestAdapter" Version="1.4.0" />
<PackageReference Include="MSTest.TestFramework" Version="1.4.0" />
<PackageReference Include="Moq" Version="4.7.63" />
</ItemGroup>

<ItemGroup>
Expand Down
277 changes: 276 additions & 1 deletion SimplePolicy.UnitTests/UT_SimplePolicy.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Neo.Plugins;

using Neo.Network.P2P.Payloads;
using Neo;
using Settings = Neo.Plugins.Settings;
using System.Collections.Generic;
using Neo.Cryptography;
using System.Numerics;
using System.Collections;
using System.Linq;
using System;
using Moq;

namespace SimplePolicy.UnitTests
{
Expand All @@ -22,5 +31,271 @@ public void TestMaxTransactionsPerBlock()
Settings.Default.MaxTransactionsPerBlock.Should().Be(500);
Settings.Default.MaxFreeTransactionsPerBlock.Should().Be(20);
}


[TestMethod]
public void TestFilterForBlock_ClaimHasPriority()
{
// Should contain "ClaimTransaction" in "HighPriorityTxType"
Settings.Default.HighPriorityTxType.Contains(TransactionType.ClaimTransaction).Should().Be(true);

ClaimTransaction claimTxZero = GetClaimTransaction(0);
claimTxZero.Size.Should().Be(7); // 7
ClaimTransaction claimTxOne = GetClaimTransaction(1);
claimTxOne.Size.Should().Be(41); // 34 + 7
ClaimTransaction claimTxTwo = GetClaimTransaction(2);
claimTxTwo.Size.Should().Be(75); // 2*34 + 7

ClaimTransaction claimTx30 = GetClaimTransaction(30);
claimTx30.Size.Should().Be(1027); // 30*34 + 7
claimTx30.NetworkFee.Should().Be(Fixed8.Zero);
claimTx30.IsLowPriority.Should().Be(true); // by default is Low Priority, but plugin makes it High Priority
//uut.IsLowPriority -> cannot inspect because it's private... no problem!

List<Transaction> TxList = new List<Transaction>();
TxList.Insert(0, claimTxZero);
TxList.Insert(0, claimTxOne);
TxList.Insert(0, claimTxTwo);
TxList.Insert(0, claimTx30);

// ======================== BEGIN TESTS ============================

// insert 100 paid invocation transactions
for (var i = 0; i < 100; i++)
{
TxList.Insert(0, MockGenerateInvocationTransaction(Fixed8.One, 50).Object);
}

// insert 100 low priority invocation transactions (18 satoshi + 82 zero)
for (var i = 0; i < 100; i++)
{
if (i < 18)
TxList.Insert(0, MockGenerateInvocationTransaction(Fixed8.Satoshi, 50).Object);
else
TxList.Insert(0, MockGenerateInvocationTransaction(Fixed8.Zero, 50).Object);
}

TxList.Count().Should().Be(204); // 100 free + 100 paid + 4 claims
TxList.Where(tx => tx.NetworkFee == Fixed8.Zero).Count().Should().Be(100-18+4); // 82 fully free + 4 claims

IEnumerable<Transaction> filteredTxList = uut.FilterForBlock(TxList);
//filteredTxList.Count().Should().Be(124); // 20 free + 100 paid + 4 claims
filteredTxList.Count().Should().Be(120); // 20 free (including 2 claims) + 100 paid
filteredTxList.Where(tx => tx.NetworkFee == Fixed8.Zero).Count().Should().Be(2); // 2 fully free (2 claims)

// will select 20 low priority (including Claims)
var vx = filteredTxList.Where(tx => tx.IsLowPriority == true);
vx.Count().Should().Be(20);

// two Claim Transaction will survive
vx = filteredTxList.Where(tx => tx.Type == TransactionType.ClaimTransaction);
vx.Count().Should().Be(2);

// =================================================================

// insert more paid invocation transactions (400 of each)
for (var i = 0; i < 400; i++)
{
TxList.Insert(0, MockGenerateInvocationTransaction(Fixed8.One, 50).Object);
}

// insert more free invocation transactions (400 of each)
for (var i = 0; i < 400; i++)
{
TxList.Insert(0, MockGenerateInvocationTransaction(Fixed8.Zero, 50).Object);
igormcoelho marked this conversation as resolved.
Show resolved Hide resolved
}

TxList.Count().Should().Be(1004); // 500 free + 500 paid + 4 claims
TxList.Where(tx => tx.NetworkFee == Fixed8.Zero).Count().Should().Be(400+100-18+4); // 500-18 fully free + 4 claims

filteredTxList = uut.FilterForBlock(TxList);
filteredTxList.Count().Should().Be(499); // full block

// will select 20 low priority (including Claims)
vx = filteredTxList.Where(tx => tx.IsLowPriority == true);
vx.Count().Should().Be(20);

// will still select Claim Transactions
vx = filteredTxList.Where(tx => tx.Type == TransactionType.ClaimTransaction);
vx.Count().Should().Be(2);
}


[TestMethod]
public void FreeTxVerifySort_NoHighPriority()
{
List<Transaction> txList = new List<Transaction>();
// three different sizes, but it doesn't matter
for (var size = 10; size <= 20; size += 5)
{
for (var netFeeSatoshi = 0; netFeeSatoshi <= 90000; netFeeSatoshi += 10000)
{
var testTx = MockGenerateInvocationTransaction(new Fixed8(netFeeSatoshi), size).Object;
testTx.IsLowPriority.Should().Be(true); // "LowPriorityThreshold": 0.001
txList.Insert(0, testTx);
}
}

txList.Count.Should().Be(30);
// transactions => size: [10, 15, 20] x price: [0 ... 90000, step by 10000]
//foreach(var tx in txList)
// Console.WriteLine($"TX fee: {tx.NetworkFee} size: {tx.Size} ratio: {tx.FeePerByte}");
/*
TX fee: 0.0009 size: 20 ratio: 0.000045
TX fee: 0.0008 size: 20 ratio: 0.00004
TX fee: 0.0007 size: 20 ratio: 0.000035
TX fee: 0.0006 size: 20 ratio: 0.00003
TX fee: 0.0005 size: 20 ratio: 0.000025
TX fee: 0.0004 size: 20 ratio: 0.00002
TX fee: 0.0003 size: 20 ratio: 0.000015
TX fee: 0.0002 size: 20 ratio: 0.00001
TX fee: 0.0001 size: 20 ratio: 0.000005
TX fee: 0 size: 20 ratio: 0
TX fee: 0.0009 size: 15 ratio: 0.00006
TX fee: 0.0008 size: 15 ratio: 0.00005333
TX fee: 0.0007 size: 15 ratio: 0.00004666
TX fee: 0.0006 size: 15 ratio: 0.00004
TX fee: 0.0005 size: 15 ratio: 0.00003333
TX fee: 0.0004 size: 15 ratio: 0.00002666
TX fee: 0.0003 size: 15 ratio: 0.00002
TX fee: 0.0002 size: 15 ratio: 0.00001333
TX fee: 0.0001 size: 15 ratio: 0.00000666
TX fee: 0 size: 15 ratio: 0
TX fee: 0.0009 size: 10 ratio: 0.00009
TX fee: 0.0008 size: 10 ratio: 0.00008
TX fee: 0.0007 size: 10 ratio: 0.00007
TX fee: 0.0006 size: 10 ratio: 0.00006
TX fee: 0.0005 size: 10 ratio: 0.00005
TX fee: 0.0004 size: 10 ratio: 0.00004
TX fee: 0.0003 size: 10 ratio: 0.00003
TX fee: 0.0002 size: 10 ratio: 0.00002
TX fee: 0.0001 size: 10 ratio: 0.00001
*/

IEnumerable<Transaction> filteredTxList = uut.FilterForBlock(txList);
filteredTxList.Count().Should().Be(20);

// will select top 20
// part A: 18 transactions with ratio >= 0.000025
// part B: 2 transactions with ratio = 0.00002 (but one with this ratio will be discarded, with fee 0.0002)
//foreach(var tx in filteredTxList)
// Console.WriteLine($"TX20 fee: {tx.NetworkFee} size: {tx.Size} ratio: {tx.NetworkFee / tx.Size}");
/*
TX20 fee: 0.0009 size: 10 ratio: 0.00009
TX20 fee: 0.0008 size: 10 ratio: 0.00008
TX20 fee: 0.0007 size: 10 ratio: 0.00007
TX20 fee: 0.0009 size: 15 ratio: 0.00006
TX20 fee: 0.0006 size: 10 ratio: 0.00006
TX20 fee: 0.0008 size: 15 ratio: 0.00005333
TX20 fee: 0.0005 size: 10 ratio: 0.00005
TX20 fee: 0.0007 size: 15 ratio: 0.00004666
TX20 fee: 0.0009 size: 20 ratio: 0.000045
TX20 fee: 0.0008 size: 20 ratio: 0.00004
TX20 fee: 0.0006 size: 15 ratio: 0.00004
TX20 fee: 0.0004 size: 10 ratio: 0.00004
TX20 fee: 0.0007 size: 20 ratio: 0.000035
TX20 fee: 0.0005 size: 15 ratio: 0.00003333
TX20 fee: 0.0006 size: 20 ratio: 0.00003
TX20 fee: 0.0003 size: 10 ratio: 0.00003
TX20 fee: 0.0004 size: 15 ratio: 0.00002666
TX20 fee: 0.0005 size: 20 ratio: 0.000025
TX20 fee: 0.0004 size: 20 ratio: 0.00002
TX20 fee: 0.0003 size: 15 ratio: 0.00002
*/

// part A
filteredTxList.Where(tx => (tx.NetworkFee / tx.Size) >= new Fixed8(2500)).Count().Should().Be(18); // 18 enter in part A
txList.Where(tx => (tx.NetworkFee / tx.Size) >= new Fixed8(2500)).Count().Should().Be(18); // they also exist in main list
txList.Where(tx => (tx.NetworkFee / tx.Size) < new Fixed8(2500)).Count().Should().Be(30 - 18); // 12 not selected transactions in part A
// part B
filteredTxList.Where(tx => (tx.NetworkFee / tx.Size) < new Fixed8(2500)).Count().Should().Be(2); // only two enter in part B
filteredTxList.Where(tx => (tx.NetworkFee / tx.Size) == new Fixed8(2000)).Count().Should().Be(2); // only two enter in part B with ratio 0.00002
txList.Where(tx => (tx.NetworkFee / tx.Size) == new Fixed8(2000)).Count().Should().Be(3); // 3 in tie (ratio 0.00002)
txList.Where(tx => (tx.NetworkFee / tx.Size) == new Fixed8(2000) && (tx.NetworkFee > new Fixed8(20000))).Count().Should().Be(2); // only 2 survive (fee > 0.0002)
}


[TestMethod]
public void FreeTxVerifySortWithPriority()
{
List<Transaction> txList = new List<Transaction>();
// three different sizes, but it doesn't matter
for (var size = 10; size <= 15; size += 5)
{
for (var netFeeSatoshi = 0; netFeeSatoshi <= 90000; netFeeSatoshi += 10000)
{
var testTx = MockGenerateInvocationTransaction(new Fixed8(netFeeSatoshi), size).Object;
testTx.IsLowPriority.Should().Be(true); // "LowPriorityThreshold": 0.001
txList.Insert(0, testTx);
}
}

txList.Insert(0, GetClaimTransaction(1));
txList.Insert(0, GetClaimTransaction(10));
txList.Insert(0, GetClaimTransaction(20));
txList.Insert(0, GetClaimTransaction(30));

txList.Count.Should().Be(24); // 20 free + 4 claims

IEnumerable<Transaction> filteredTxList = uut.FilterForBlock(txList);
filteredTxList.Count().Should().Be(20);

filteredTxList.Where(tx => tx.Type == TransactionType.ClaimTransaction).Count().Should().Be(2); // 2 claims will be selected
}


[TestMethod]
public void TestMock_GenerateInvocationTransaction()
{
var txHighPriority = MockGenerateInvocationTransaction(Fixed8.One, 50);
// testing default values
Fixed8 txHighPriority_ratio = txHighPriority.Object.NetworkFee / txHighPriority.Object.Size;
txHighPriority_ratio.Should().Be(new Fixed8(2000000)); // 0.02
txHighPriority.Object.IsLowPriority.Should().Be(false);

var txLowPriority = MockGenerateInvocationTransaction(Fixed8.One / 10000, 50); // 0.00001
// testing default values
Fixed8 txLowPriority_ratio = txLowPriority.Object.NetworkFee / txLowPriority.Object.Size;
txLowPriority_ratio.Should().Be(new Fixed8(200)); // 0.000002 -> 200 satoshi / Byte
txLowPriority.Object.IsLowPriority.Should().Be(true);
}

// Generate Mock InvocationTransaction with different sizes and prices
public static Mock<Transaction> MockGenerateInvocationTransaction(Fixed8 networkFee, int size)
{
var mockTx = new Mock<Transaction>(TransactionType.InvocationTransaction);
mockTx.SetupGet(mr => mr.NetworkFee).Returns(networkFee);
mockTx.SetupGet(mr => mr.Size).Returns(size);
return mockTx;
}

// Create ClaimTransaction with 'countRefs' CoinReferences
public static ClaimTransaction GetClaimTransaction(int countRefs)
{
CoinReference[] refs = new CoinReference[countRefs];
for (var i = 0; i < countRefs; i++)
{
refs[i] = GetCoinReference(new UInt256(Crypto.Default.Hash256(new BigInteger(i).ToByteArray())));
}

return new ClaimTransaction
{
Claims = refs,
Attributes = new TransactionAttribute[0],
Inputs = new CoinReference[0],
Outputs = new TransactionOutput[0],
Witnesses = new Witness[0]
};
}

public static CoinReference GetCoinReference(UInt256 prevHash)
{
if (prevHash == null) prevHash = UInt256.Zero;
return new CoinReference
{
PrevHash = prevHash,
PrevIndex = 0
};
}
}
}
30 changes: 30 additions & 0 deletions SimplePolicy.UnitTests/protocol.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"ProtocolConfiguration": {
"Magic": 7630401,
"AddressVersion": 23,
"SecondsPerBlock": 15,
"LowPriorityThreshold": 0.001,
"StandbyValidators": [
"03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c",
"02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093",
"03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a",
"02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554",
"024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d",
"02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e",
"02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70"
],
"SeedList": [
"seed1.neo.org:10333",
"seed2.neo.org:10333",
"seed3.neo.org:10333",
"seed4.neo.org:10333",
"seed5.neo.org:10333"
],
"SystemFee": {
"EnrollmentTransaction": 1000,
"IssueTransaction": 500,
"PublishTransaction": 500,
"RegisterTransaction": 10000
}
}
}
18 changes: 17 additions & 1 deletion SimplePolicy/Settings.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration;
using Neo.Network.P2P.Payloads;
using Neo.Wallets;
using System;
using System.Collections.Generic;
Expand All @@ -12,6 +13,7 @@ internal class Settings
public int MaxFreeTransactionsPerBlock { get; }
public int MaxFreeTransactionSize { get; }
public Fixed8 FeePerExtraByte { get; }
public EnumSet<TransactionType> HighPriorityTxType { get; }
public BlockedAccounts BlockedAccounts { get; }

public static Settings Default { get; private set; }
Expand All @@ -22,6 +24,7 @@ private Settings(IConfigurationSection section)
this.MaxFreeTransactionsPerBlock = GetValueOrDefault(section.GetSection("MaxFreeTransactionsPerBlock"), 20, p => int.Parse(p));
this.MaxFreeTransactionSize = GetValueOrDefault(section.GetSection("MaxFreeTransactionSize"), 1024, p => int.Parse(p));
this.FeePerExtraByte = GetValueOrDefault(section.GetSection("FeePerExtraByte"), Fixed8.FromDecimal(0.00001M), p => Fixed8.Parse(p));
this.HighPriorityTxType = new EnumSet<TransactionType>(section.GetSection("HighPriorityTxType"), TransactionType.ClaimTransaction);
this.BlockedAccounts = new BlockedAccounts(section.GetSection("BlockedAccounts"));
}

Expand All @@ -37,6 +40,19 @@ public static void Load(IConfigurationSection section)
}
}

internal class EnumSet<T> : HashSet<T>
where T : Enum
{
public EnumSet(IConfigurationSection section, params T[] defaultValues)
{
if (section.Exists())
foreach (IConfigurationSection child in section.GetChildren())
Add((T)Enum.Parse(typeof(T), child.Value));
else
UnionWith(defaultValues);
}
}

internal enum PolicyType : byte
{
AllowAll,
Expand Down
1 change: 1 addition & 0 deletions SimplePolicy/SimplePolicy.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<Version>2.9.3</Version>
<TargetFrameworks>netstandard2.0;net47</TargetFrameworks>
<RootNamespace>Neo.Plugins</RootNamespace>
<LangVersion>latest</LangVersion>
</PropertyGroup>

<ItemGroup>
Expand Down
Loading