Skip to content
Closed
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
170 changes: 170 additions & 0 deletions NBitcoin.Tests/Generators/AbstractPolicyGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
using System;
using System.Collections.Generic;
using System.Linq;
using FsCheck;
using NBitcoin.Scripting;

namespace NBitcoin.Tests.Generators
{
public class AbstractPolicyGenerator
{

/// <summary>
/// Ideally This should be able to specify the size from the callers side. But who cares.
/// </summary>
/// <returns></returns>
public static Arbitrary<AbstractPolicy> AbstractPolicyArb()
=> new ArbitraryAbstractPolicy();

public class ArbitraryAbstractPolicy : Arbitrary<AbstractPolicy>
{
public override Gen<AbstractPolicy> Generator { get { return Gen.Sized(s => AbstractPolicyGen(s)); } }
public override IEnumerable<AbstractPolicy> Shrinker(AbstractPolicy parent)
{
switch (parent)
{
case AbstractPolicy.And p:
{
yield return p.Item1;
yield return p.Item2;
foreach (var t in Shrinker(p.Item1).SelectMany(shrinkedItem1 => Shrinker(p.Item2), (i1, i2) => Tuple.Create(i1, i2)))
yield return AbstractPolicy.NewAnd(t.Item1, t.Item2);
foreach (var subShrinked in Shrinker(p.Item1).Select(shrinkedItem1 => AbstractPolicy.NewAnd(shrinkedItem1, p.Item2)))
yield return subShrinked;
foreach (var subShrinked in Shrinker(p.Item2).Select(shrinkedItem2 => AbstractPolicy.NewAnd(p.Item1, shrinkedItem2)))
yield return subShrinked;
break;
}
case AbstractPolicy.Or p:
{
yield return p.Item1;
yield return p.Item2;
foreach (var t in Shrinker(p.Item1).SelectMany(shrinkedItem1 => Shrinker(p.Item2), (i1, i2) => Tuple.Create(i1, i2)))
yield return AbstractPolicy.NewOr(t.Item1, t.Item2);
foreach (var subShrinked in Shrinker(p.Item1).Select(shrinkedItem1 => AbstractPolicy.NewOr(shrinkedItem1, p.Item2)))
yield return subShrinked;
foreach (var subShrinked in Shrinker(p.Item2).Select(shrinkedItem2 => AbstractPolicy.NewOr(p.Item1, shrinkedItem2)))
yield return subShrinked;
break;
}
case AbstractPolicy.AsymmetricOr p:
{
yield return p.Item1;
yield return p.Item2;
foreach (var t in Shrinker(p.Item1).SelectMany(shrinkedItem1 => Shrinker(p.Item2), (i1, i2) => Tuple.Create(i1, i2)))
yield return AbstractPolicy.NewAsymmetricOr(t.Item1, t.Item2);
foreach (var subShrinked in Shrinker(p.Item1).Select(shrinkedItem1 => AbstractPolicy.NewAsymmetricOr(shrinkedItem1, p.Item2)))
yield return subShrinked;
foreach (var subShrinked in Shrinker(p.Item2).Select(shrinkedItem2 => AbstractPolicy.NewAsymmetricOr(p.Item1, shrinkedItem2)))
yield return subShrinked;
break;
}
case AbstractPolicy.Threshold p:
{
foreach (var subP in p.Item2)
{
yield return subP;
}
foreach (var i in Arb.Shrink(p.Item2).Select(subs => subs.Select(sub => Shrinker(sub))))
{
foreach (var i2 in i)
{
if (1 < i2.Count())
yield return AbstractPolicy.NewThreshold(1, i2.ToArray());
}
}

if (p.Item2.Length == 2)
yield break;

foreach (var i in Arb.Shrink(p.Item2))
{
if (1 < i.Length)
yield return AbstractPolicy.NewThreshold(1, i);
}
yield break;
}
case AbstractPolicy.Multi p:
{
yield return AbstractPolicy.NewCheckSig(p.Item2[0]);
if (p.Item2.Length > 2)
yield return AbstractPolicy.NewMulti(2, p.Item2.Take(2).ToArray());
foreach (var i in Arb.Shrink(p.Item2))
{
if (i.Length > 2)
yield return AbstractPolicy.NewMulti(2, i.ToArray());
}
break;
}
default:
{
yield break;
}
}
}

}

private static Gen<AbstractPolicy> AbstractPolicyGen(int size)
{
if (size == 0)
{
return NonRecursivePolicyGen();
}
else
{
return Gen.Frequency
(
Tuple.Create(3, NonRecursivePolicyGen()),
Tuple.Create(2, RecursivePolicyGen(AbstractPolicyGen(size / 2)))
);
}
}

private static Gen<AbstractPolicy> NonRecursivePolicyGen()
=>
Gen.OneOf(
new[]{
CheckSigGen(),
MultiSigGen(),
TimeGen(),
HashGen()
}
);
private static Gen<AbstractPolicy> CheckSigGen()
=> CryptoGenerator.PublicKey().Select(pk => AbstractPolicy.NewCheckSig(pk));

private static Gen<AbstractPolicy> MultiSigGen()
=>
from m in Gen.Choose(2, 16)
from numKeys in Gen.Choose(m, 16)
from pks in Gen.ArrayOf(numKeys, CryptoGenerator.PublicKey())
select AbstractPolicy.NewMulti((uint)m, pks);

private static Gen<AbstractPolicy> TimeGen()
=>
from t in Gen.Choose(0, 65535)
select AbstractPolicy.NewTime((uint)t);

private static Gen<AbstractPolicy> HashGen()
=>
from t in CryptoGenerator.Hash256()
select AbstractPolicy.NewHash(t);

private static Gen<AbstractPolicy> RecursivePolicyGen(Gen<AbstractPolicy> subGen)
=> Gen.OneOf
(
subGen.Two().Select(t => AbstractPolicy.NewAnd(t.Item1, t.Item2)),
subGen.Two().Select(t => AbstractPolicy.NewOr(t.Item1, t.Item2)),
subGen.Two().Select(t => AbstractPolicy.NewAsymmetricOr(t.Item1, t.Item2)),
ThresholdContentsGen(subGen).Select(t => AbstractPolicy.NewThreshold(t.Item1, t.Item2))
);

private static Gen<Tuple<UInt32, AbstractPolicy[]>> ThresholdContentsGen(Gen<AbstractPolicy> subGen)
=>
from num in Gen.Choose(1, 6)
from actualNum in num == 1 ? Gen.Choose(2, 6) : Gen.Choose(num ,6)
from subPolicies in Gen.ArrayOf(actualNum, subGen)
select Tuple.Create((uint)num, subPolicies);
}
}
3 changes: 3 additions & 0 deletions NBitcoin.Tests/Generators/CryptoGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ public static Arbitrary<List<Key>> KeysListArb() =>
public static Arbitrary<KeyPath> ExtPathArb() =>
Arb.From(KeyPath());

public static Arbitrary<PubKey> PubKeyArb() =>
Arb.From(PublicKey());

public static Gen<Key> PrivateKey() => Gen.Fresh(() => new Key());

public static Gen<List<Key>> PrivateKeys(int n) =>
Expand Down
3 changes: 3 additions & 0 deletions NBitcoin.Tests/Generators/ScriptGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ namespace NBitcoin.Tests.Generators
public class ScriptGenerator
{

public static Arbitrary<Script> RandomScriptArb()
=> Arb.From(RandomScriptSig());

#region script sig
// -------- legacy -------
// 1. p2pkh scriptSig
Expand Down
18 changes: 7 additions & 11 deletions NBitcoin.Tests/Generators/StringGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,16 @@ static StringGenerator()
{
ValidHexChars = ValidHex.ToCharArray();
}
public static Gen<char> HexChar() => Gen.Choose(0, ValidHexChars.Length - 1).Select(i => ValidHexChars[i]);
public static Gen<char> HexChar()
=> Gen.Choose(0, ValidHexChars.Length - 1).Select(i => ValidHexChars[i]);

public static Gen<string> HexString(int length)
{
var res = from i in Gen.Choose(0, length)
where (i % 2 == 0)
from cl in Gen.ListOf(i, HexChar())
select String.Join("", cl.ToList());
return res;
}
=> from i in Gen.Choose(0, length)
where (i % 2 == 0)
from cl in Gen.ListOf(i, HexChar())
select String.Join("", cl.ToList());

public static Gen<string> HexString()
{
return Gen.Choose(0, 100).SelectMany(n => HexString(n));
}
=> Gen.Choose(0, 100).SelectMany(n => HexString(n));
}
}
4 changes: 4 additions & 0 deletions NBitcoin.Tests/Generators/Utils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,9 @@ from values in Gen.ListOf(itemNum, PrimitiveGenerator.RandomBytes())
public static Gen<HDFingerprint> HDFingerprint() =>
from bytes in PrimitiveGenerator.RandomBytes(4)
select new HDFingerprint(bytes);

public static Gen<T> Resize<T>(Gen<T> gen)
=> Gen.Sized(s => gen.Resize(Convert.ToInt32(Math.Sqrt(s))));

}
}
43 changes: 43 additions & 0 deletions NBitcoin.Tests/Helpers/PrimitiveUtils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@

using System.Collections.Generic;

namespace NBitcoin.Tests.Helpers
{
internal static class PrimitiveUtils
{
internal static Coin RandomCoin(Money amount, Script scriptPubKey, bool p2sh)
{
var outpoint = RandOutpoint();
if(!p2sh)
return new Coin(outpoint, new TxOut(amount, scriptPubKey));
return new ScriptCoin(outpoint, new TxOut(amount, scriptPubKey.Hash), scriptPubKey);
}
internal static Coin RandomCoin(Money amount, Key receiver)
{
return RandomCoin(amount, receiver.PubKey.GetAddress(ScriptPubKeyType.Legacy, Network.Main));
}
internal static Coin RandomCoin(Money amount, IDestination receiver)
{
var outpoint = RandOutpoint();
return new Coin(outpoint, new TxOut(amount, receiver));
}

internal static List<ScriptCoin> GetRandomCoinsForAllScriptType(Money amount, Script scriptPubKey)
{
return new List<ScriptCoin> {
RandomCoin(Money.Coins(0.5m), scriptPubKey, true) as ScriptCoin,
new ScriptCoin(RandomCoin(Money.Coins(0.5m), scriptPubKey.WitHash), scriptPubKey),
new ScriptCoin(RandomCoin(Money.Coins(0.5m), scriptPubKey.WitHash.ScriptPubKey.Hash), scriptPubKey)
};
}

internal static OutPoint RandOutpoint()
{
return new OutPoint(Rand(), 0);
}
internal static uint256 Rand()
{
return new uint256(RandomUtils.GetBytes(32));
}
}
}
Loading