diff --git a/NBitcoin.Tests/Generators/ConcretePolicyGenerator.cs b/NBitcoin.Tests/Generators/ConcretePolicyGenerator.cs new file mode 100644 index 0000000000..48b01ec65c --- /dev/null +++ b/NBitcoin.Tests/Generators/ConcretePolicyGenerator.cs @@ -0,0 +1,141 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using System.Threading; +using FsCheck; +using NBitcoin.Scripting.Miniscript; +using NBitcoin.Scripting.Miniscript.Policy; + +namespace NBitcoin.Tests.Generators +{ + public class ConcretePolicyGenerator + { + public static Arbitrary> PubKeyConcretePolicyArb() + => new ArbitraryPubKeyConcretePolicy(); + + public class ArbitraryPubKeyConcretePolicy : Arbitrary> + { + public override Gen> Generator => Gen.Sized(ConcretePolicyGen); + + /* + public override IEnumerable> Shrinker(ConcretePolicy parent) + { + switch (parent) + { + case ConcretePolicy.And p: + foreach (var i in p.Item) + yield return i; + foreach (var i in Arb.Shrink(p.Item)) + if (i.Count == 2) + yield return ConcretePolicy.NewAnd(i); + break; + case ConcretePolicy.Or p: + foreach (var i in p.Item) + yield return i.Item2; + foreach (var i in Arb.Shrink(p.Item)) + if (i.Count == 2) + yield return ConcretePolicy.NewOr(i); + break; + case ConcretePolicy.Threshold p: + foreach (var subP in p.Item2) + { + yield return subP; + } + foreach (var i in Arb.Shrink(p.Item2).Select(s => s.Select(sub => Shrinker(sub)))) + { + foreach (var i2 in i) + if (1 < i2.Count()) + yield return ConcretePolicy.NewThreshold(1, i2); + } + + foreach (var subP in Arb.Shrink(p.Item2)) + { + if (1 < subP.Count()) + yield return ConcretePolicy.NewThreshold(1, subP); + } + yield break; + } + } + */ + + private static Gen> ConcretePolicyGen(int size) + { + if (size == 0) return NonRecursivePolicyGen(); + return Gen.Frequency( + Tuple.Create(3, NonRecursivePolicyGen()), + Tuple.Create(2, RecursivePolicyGen(size / 2)) + ); + } + + private static Gen> NonRecursivePolicyGen() + => Gen.OneOf(new[] + { + KeyGen(), + AfterGen(), + OlderGen(), + Sha256Gen(), + Hash256Gen(), + Ripemd160Gen(), + Hash160Gen() + }); + + private static Gen> KeyGen() + => + from inner in CryptoGenerator.PublicKey() + select ConcretePolicy.NewKey(inner); + + private static Gen> AfterGen() + => + from t in Arb.Generate() + select ConcretePolicy.NewAfter((uint)t.Get); + private static Gen> OlderGen() + => + from t in Arb.Generate() + select ConcretePolicy.NewOlder((uint)t.Get); + + private static Gen> Sha256Gen() + => + from t in CryptoGenerator.Hash256() + select ConcretePolicy.NewSha256(t); + + private static Gen> Hash256Gen() + => + from t in CryptoGenerator.Hash256() + select ConcretePolicy.NewHash256(t); + + private static Gen> Ripemd160Gen() + => + from t in CryptoGenerator.Hash160() + select ConcretePolicy.NewRipemd160(t); + + private static Gen> Hash160Gen() + => + from t in CryptoGenerator.Hash160() + select ConcretePolicy.NewHash160(t); + + private static Gen> RecursivePolicyGen(int size) + => + Gen.OneOf( + (from sub in ConcretePolicyGen(size).Two() + select ConcretePolicy.NewAnd(new[] {sub.Item1, sub.Item2})), + (from prob in Arb.Generate().Two() + from sub in ConcretePolicyGen(size).Two() + select ConcretePolicy.NewOr( + new []{ + Tuple.Create((uint)prob.Item1.Get, sub.Item1), + Tuple.Create((uint)prob.Item2.Get, sub.Item2) + })), + (from t in ThresholdContentsGen(size) + select ConcretePolicy.NewThreshold(t.Item1, t.Item2)) + ); + + private static Gen[]>> ThresholdContentsGen(int size) + => + from n in Gen.Choose(1, 6) + from actualN in n == 1 ? Gen.Choose(2, 6) : Gen.Choose(n, 6) + from a in Gen.ArrayOf(actualN, ConcretePolicyGen(size)) + select Tuple.Create((uint) n, a); + } + } + +} diff --git a/NBitcoin.Tests/Generators/CryptoGenerator.cs b/NBitcoin.Tests/Generators/CryptoGenerator.cs index f3f8f2bfa8..fe13bd8959 100644 --- a/NBitcoin.Tests/Generators/CryptoGenerator.cs +++ b/NBitcoin.Tests/Generators/CryptoGenerator.cs @@ -1,98 +1,102 @@ -using System; -using NBitcoin; -using FsCheck; -using System.Collections.Generic; -using Microsoft.FSharp.Collections; -using System.Linq; -using NBitcoin.Crypto; - -namespace NBitcoin.Tests.Generators -{ - public class CryptoGenerator - { - #region PrivateKey - public static Arbitrary KeysArb() => - Arb.From(PrivateKey()); - - public static Arbitrary ExtKeysArb() => - Arb.From(ExtKey()); - - public static Arbitrary> KeysListArb() => - Arb.From(PrivateKeys(15)); - - public static Arbitrary ExtPathArb() => - Arb.From(KeyPath()); - - public static Gen PrivateKey() => Gen.Fresh(() => new Key()); - - public static Gen> PrivateKeys(int n) => - from pk in Gen.ListOf(n, PrivateKey()) - select pk.ToList(); - - #endregion - - public static Gen PublicKey() => - PrivateKey().Select(p => p.PubKey); - - public static Gen> PublicKeys() => - from n in Gen.Choose(0, 15) - from pks in PublicKeys(n) - select pks; - - public static Gen> PublicKeys(int n) => - from pks in Gen.ListOf(n, PublicKey()) - select pks.ToList(); - - #region hash - public static Gen Hash256() => - from bytes in PrimitiveGenerator.RandomBytes(32) - select new uint256(bytes); - - public static Gen Hash160() => - from bytes in PrimitiveGenerator.RandomBytes(20) - select new uint160(bytes); - #endregion - - #region ECDSASignature - public static Gen ECDSA() => - from hash in Hash256() - from priv in PrivateKey() - select priv.Sign(hash); - - public static Gen> ECDSAs() => - from n in Gen.Choose(0, 20) - from sigs in ECDSAs(n) - select sigs.ToList(); - - public static Gen> ECDSAs(int n) => - from sigs in Gen.ListOf(n, ECDSA()) - select sigs.ToList(); - #endregion - - #region TransactionSignature - public static Gen TransactionSignature() => - from rawsig in ECDSA() - from sighash in SigHashType() - select new TransactionSignature(rawsig, sighash); - - public static Gen SigHashType() => - Gen.OneOf(new List> { - Gen.Constant(SigHash.All), - Gen.Constant(SigHash.Single), - Gen.Constant(SigHash.None), - Gen.Constant(SigHash.AnyoneCanPay | SigHash.All), - Gen.Constant(SigHash.AnyoneCanPay | SigHash.Single), - Gen.Constant(SigHash.AnyoneCanPay | SigHash.None) - }); - #endregion - - public static Gen ExtKey() => Gen.Fresh(() => new ExtKey()); - - public static Gen KeyPath() => - from raw in Gen.NonEmptyListOf(PrimitiveGenerator.RandomBytes(4)) - let flattenBytes = raw.ToList().Aggregate((a, b) => a.Concat(b)) - select NBitcoin.KeyPath.FromBytes(flattenBytes); - - public static Gen ExtPubKey() => ExtKey().Select(ek => ek.Neuter()); - } -} \ No newline at end of file +using System; +using NBitcoin; +using FsCheck; +using System.Collections.Generic; +using Microsoft.FSharp.Collections; +using System.Linq; +using NBitcoin.Crypto; + +namespace NBitcoin.Tests.Generators +{ + public class CryptoGenerator + { + #region PrivateKey + public static Arbitrary KeysArb() => + Arb.From(PrivateKey()); + + public static Arbitrary ExtKeysArb() => + Arb.From(ExtKey()); + + public static Arbitrary> KeysListArb() => + Arb.From(PrivateKeys(15)); + + 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) => + from pk in Gen.ListOf(n, PrivateKey()) + select pk.ToList(); + + #endregion + + public static Gen PublicKey() => + PrivateKey().Select(p => p.PubKey); + + public static Gen> PublicKeys() => + from n in Gen.Choose(0, 15) + from pks in PublicKeys(n) + select pks; + + public static Gen> PublicKeys(int n) => + from pks in Gen.ListOf(n, PublicKey()) + select pks.ToList(); + + #region hash + public static Gen Hash256() => + from bytes in PrimitiveGenerator.RandomBytes(32) + select new uint256(bytes); + + public static Gen Hash160() => + from bytes in PrimitiveGenerator.RandomBytes(20) + select new uint160(bytes); + #endregion + + #region ECDSASignature + public static Gen ECDSA() => + from hash in Hash256() + from priv in PrivateKey() + select priv.Sign(hash); + + public static Gen> ECDSAs() => + from n in Gen.Choose(0, 20) + from sigs in ECDSAs(n) + select sigs.ToList(); + + public static Gen> ECDSAs(int n) => + from sigs in Gen.ListOf(n, ECDSA()) + select sigs.ToList(); + #endregion + + #region TransactionSignature + public static Gen TransactionSignature() => + from rawsig in ECDSA() + from sighash in SigHashType() + select new TransactionSignature(rawsig, sighash); + + public static Gen SigHashType() => + Gen.OneOf(new List> { + Gen.Constant(SigHash.All), + Gen.Constant(SigHash.Single), + Gen.Constant(SigHash.None), + Gen.Constant(SigHash.AnyoneCanPay | SigHash.All), + Gen.Constant(SigHash.AnyoneCanPay | SigHash.Single), + Gen.Constant(SigHash.AnyoneCanPay | SigHash.None) + }); + #endregion + + public static Gen ExtKey() => Gen.Fresh(() => new ExtKey()); + + public static Gen KeyPath() => + from raw in Gen.NonEmptyListOf(PrimitiveGenerator.RandomBytes(4)) + let flattenBytes = raw.ToList().Aggregate((a, b) => a.Concat(b)) + select NBitcoin.KeyPath.FromBytes(flattenBytes); + + public static Gen ExtPubKey() => ExtKey().Select(ek => ek.Neuter()); + } +} diff --git a/NBitcoin.Tests/Generators/PSBTGenerator.cs b/NBitcoin.Tests/Generators/PSBTGenerator.cs index 639f220060..e1962786da 100644 --- a/NBitcoin.Tests/Generators/PSBTGenerator.cs +++ b/NBitcoin.Tests/Generators/PSBTGenerator.cs @@ -1,109 +1,109 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using FsCheck; -using NBitcoin.Crypto; -using Xunit; - -namespace NBitcoin.Tests.Generators -{ - using HDKeyPathKVMap = Dictionary>; - using PartialSigKVMap = Dictionary>; - using UnknownKVMap = Dictionary; - public class PSBTGenerator - { - public static Arbitrary PSBTArb() => Arb.From(SanePSBT()); - - #region PSBTInput - - public static Gen>> PartialSigs() => - from itemNum in Gen.Choose(0, 15) - from sigs in CryptoGenerator.ECDSAs(itemNum) - from pks in CryptoGenerator.PublicKeys(itemNum) - let items = pks.ToList().Zip(sigs.ToList(), (pk, sig) => Tuple.Create(pk, sig)).ToList() - select Utils.DictionaryFromList>(pks.Select(pk => pk.Hash).ToList(), items); - - #endregion - - #region PSBT - - public static Gen SanePSBT() => - from network in ChainParamsGenerator.NetworkGen() - from psbt in SanePSBT(network) - select psbt; - - /// - /// This is slow, provably because `Add*` methods will iterate over inputs. - /// - /// - /// - public static Gen SanePSBT(Network network) => - from inputN in Gen.Choose(0, 8) - from scripts in Gen.ListOf(inputN, ScriptGenerator.RandomScriptSig()) - from txOuts in Gen.Sequence(scripts.Select(sc => OutputFromRedeem(sc))) - from prevN in Gen.Choose(0, 5) - from prevTxs in Gen.Sequence(txOuts.Select(o => TXFromOutput(o, network, prevN))) - let txins = prevTxs.Select(tx => new TxIn(new OutPoint(tx.GetHash(), prevN))) - from locktime in PrimitiveGenerator.UInt32() - let tx = LegacyTransactionGenerators.ComposeTx(network.CreateTransaction(), txins.ToList(), txOuts.ToList(), locktime) - from TxsToAdd in Gen.SubListOf(prevTxs) - from CoinsToAdd in Gen.SubListOf(prevTxs.SelectMany(tx => tx.Outputs.AsCoins())) - from scriptsToAdd in Gen.SubListOf