diff --git a/NBitcoin.Tests/Generators/AbstractPolicyGenerator.cs b/NBitcoin.Tests/Generators/AbstractPolicyGenerator.cs
new file mode 100644
index 0000000000..efdb43ed81
--- /dev/null
+++ b/NBitcoin.Tests/Generators/AbstractPolicyGenerator.cs
@@ -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
+ {
+
+ ///
+ /// Ideally This should be able to specify the size from the callers side. But who cares.
+ ///
+ ///
+ public static Arbitrary AbstractPolicyArb()
+ => new ArbitraryAbstractPolicy();
+
+ public class ArbitraryAbstractPolicy : Arbitrary
+ {
+ public override Gen Generator { get { return Gen.Sized(s => AbstractPolicyGen(s)); } }
+ public override IEnumerable 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 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 NonRecursivePolicyGen()
+ =>
+ Gen.OneOf(
+ new[]{
+ CheckSigGen(),
+ MultiSigGen(),
+ TimeGen(),
+ HashGen()
+ }
+ );
+ private static Gen CheckSigGen()
+ => CryptoGenerator.PublicKey().Select(pk => AbstractPolicy.NewCheckSig(pk));
+
+ private static Gen 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 TimeGen()
+ =>
+ from t in Gen.Choose(0, 65535)
+ select AbstractPolicy.NewTime((uint)t);
+
+ private static Gen HashGen()
+ =>
+ from t in CryptoGenerator.Hash256()
+ select AbstractPolicy.NewHash(t);
+
+ private static Gen RecursivePolicyGen(Gen 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> ThresholdContentsGen(Gen 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);
+ }
+}
\ No newline at end of file
diff --git a/NBitcoin.Tests/Generators/CryptoGenerator.cs b/NBitcoin.Tests/Generators/CryptoGenerator.cs
index f3f8f2bfa8..f8318e4453 100644
--- a/NBitcoin.Tests/Generators/CryptoGenerator.cs
+++ b/NBitcoin.Tests/Generators/CryptoGenerator.cs
@@ -23,6 +23,9 @@ public static Arbitrary> KeysListArb() =>
public static Arbitrary ExtPathArb() =>
Arb.From(KeyPath());
+ public static Arbitrary PubKeyArb() =>
+ Arb.From(PublicKey());
+
public static Gen PrivateKey() => Gen.Fresh(() => new Key());
public static Gen> PrivateKeys(int n) =>
@@ -94,5 +97,23 @@ from raw in Gen.NonEmptyListOf(PrimitiveGenerator.RandomBytes(4))
select NBitcoin.KeyPath.FromBytes(flattenBytes);
public static Gen ExtPubKey() => ExtKey().Select(ek => ek.Neuter());
+ public static Gen BitcoinExtPubKey() =>
+ from extKey in ExtPubKey()
+ from network in ChainParamsGenerator.NetworkGen()
+ select new BitcoinExtPubKey(extKey, network);
+
+ public static Gen BitcoinExtKey() =>
+ from extKey in ExtKey()
+ from network in ChainParamsGenerator.NetworkGen()
+ select new BitcoinExtKey(extKey, network);
+
+ public static Gen RootedKeyPath() =>
+ from parentFingerPrint in HDFingerPrint()
+ from kp in KeyPath()
+ select new RootedKeyPath(parentFingerPrint, kp);
+
+ public static Gen HDFingerPrint() =>
+ from x in PrimitiveGenerator.UInt32()
+ select new HDFingerprint(x);
}
}
\ No newline at end of file
diff --git a/NBitcoin.Tests/Generators/OutputDescriptorGenerator.cs b/NBitcoin.Tests/Generators/OutputDescriptorGenerator.cs
new file mode 100644
index 0000000000..63590e3926
--- /dev/null
+++ b/NBitcoin.Tests/Generators/OutputDescriptorGenerator.cs
@@ -0,0 +1,88 @@
+using FsCheck;
+using NBitcoin.Scripting;
+
+namespace NBitcoin.Tests.Generators
+{
+ public class OutputDescriptorGenerator
+ {
+ public static Arbitrary OutputDescriptorArb() =>
+ Arb.From(OutputDescriptorGen());
+
+ public static Gen OutputDescriptorGen() =>
+ Gen.OneOf(
+ AddrOutputDescriptorGen(),
+ RawOutputDescriptorGen(),
+ PKOutputDescriptorGen(),
+ PKHOutputDescriptorGen(),
+ WPKHOutputDescriptorGen(),
+ ComboOutputDescriptorGen(),
+ MultisigOutputDescriptorGen(),
+ SHOutputDescriptorGen(),
+ WSHOutputDescriptorGen()
+ );
+ private static Gen AddrOutputDescriptorGen() =>
+ from addr in AddressGenerator.RandomAddress()
+ select OutputDescriptor.NewAddr(addr);
+
+ private static Gen RawOutputDescriptorGen() =>
+ from addr in ScriptGenerator.RandomScriptSig()
+ select OutputDescriptor.NewRaw(addr);
+ private static Gen PKOutputDescriptorGen() =>
+ from pkProvider in PubKeyProviderGen()
+ select OutputDescriptor.NewPK(pkProvider);
+
+ private static Gen PKHOutputDescriptorGen() =>
+ from pkProvider in PubKeyProviderGen()
+ select OutputDescriptor.NewPKH(pkProvider);
+
+ private static Gen WPKHOutputDescriptorGen() =>
+ from pkProvider in PubKeyProviderGen()
+ select OutputDescriptor.NewWPKH(pkProvider);
+
+ private static Gen ComboOutputDescriptorGen() =>
+ from pkProvider in PubKeyProviderGen()
+ select OutputDescriptor.NewCombo(pkProvider);
+
+ private static Gen MultisigOutputDescriptorGen() =>
+ from m in Gen.Choose(1, 20).Select(i => (uint)i)
+ from pkProviders in Gen.NonEmptyListOf(PubKeyProviderGen())
+ select OutputDescriptor.NewMulti(m, pkProviders);
+
+ private static Gen InnerOutputDescriptorGen() =>
+ Gen.OneOf(
+ PKOutputDescriptorGen(),
+ PKHOutputDescriptorGen(),
+ WPKHOutputDescriptorGen(),
+ MultisigOutputDescriptorGen()
+ );
+ private static Gen SHOutputDescriptorGen() =>
+ from inner in Gen.OneOf(InnerOutputDescriptorGen(), WSHOutputDescriptorGen())
+ select OutputDescriptor.NewSH(inner);
+
+ private static Gen WSHOutputDescriptorGen() =>
+ from inner in InnerOutputDescriptorGen()
+ select OutputDescriptor.NewWSH(inner);
+
+ #region pubkey providers
+
+ private static Gen PubKeyProviderGen() =>
+ Gen.OneOf(OriginPubKeyProviderGen(), ConstPubKeyProviderGen(), HDPubKeyProviderGen());
+
+ private static Gen OriginPubKeyProviderGen() =>
+ from keyOrigin in CryptoGenerator.RootedKeyPath()
+ from inner in Gen.OneOf(ConstPubKeyProviderGen(), HDPubKeyProviderGen())
+ select PubKeyProvider.NewOrigin(keyOrigin, inner);
+
+ private static Gen ConstPubKeyProviderGen() =>
+ from pk in CryptoGenerator.PublicKey()
+ select PubKeyProvider.NewConst(pk);
+
+ private static Gen HDPubKeyProviderGen() =>
+ from extPk in CryptoGenerator.BitcoinExtPubKey()
+ from kp in CryptoGenerator.KeyPath()
+ from t in Arb.Generate()
+ select PubKeyProvider.NewHD(extPk, kp, t);
+
+ # endregion
+ }
+}
diff --git a/NBitcoin.Tests/Generators/ScriptGenerator.cs b/NBitcoin.Tests/Generators/ScriptGenerator.cs
index 57e6568597..443d5fb73b 100644
--- a/NBitcoin.Tests/Generators/ScriptGenerator.cs
+++ b/NBitcoin.Tests/Generators/ScriptGenerator.cs
@@ -9,6 +9,9 @@ namespace NBitcoin.Tests.Generators
public class ScriptGenerator
{
+ public static Arbitrary