Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
6ea9dae
WIP: rewrite miniscript in C# from scratch
joemphilips Apr 21, 2019
64e1220
Finish AbstractPolicy
joemphilips Apr 21, 2019
a6d3dda
Move everything to separate dir
joemphilips Apr 21, 2019
81aa59b
Finish basic DSLParser stuffs
joemphilips Apr 22, 2019
46b56a3
Finish AstElem impl.
joemphilips Apr 23, 2019
f1ae519
Finish Compiler
joemphilips Apr 24, 2019
ff57058
Add test for AST -> Script
joemphilips Apr 24, 2019
0b61869
Prepare ScriptToken
joemphilips Apr 25, 2019
de46b08
Generalize Parser against input type
joemphilips Apr 25, 2019
a3db33e
Split Parsers into separate files. And Implement Script.ToTokens()
joemphilips Apr 25, 2019
f30bf08
WIP: finish decompiler
joemphilips Apr 26, 2019
e8dd596
Prepare Shrinker
joemphilips Apr 27, 2019
1b9dd35
Improve Deserializer and its tests
joemphilips Apr 28, 2019
1744173
Update deserialization test
joemphilips Apr 29, 2019
d0d7a95
WIP: Write Satisfy
joemphilips Apr 30, 2019
0293749
Refactor Satisfy to return bool
joemphilips Apr 30, 2019
7bebf77
Slightly modify TrySatisfy API
joemphilips Apr 30, 2019
4042385
Improve Performance on DSLParser
joemphilips Apr 30, 2019
587e36f
use ToCharArray instead of string.AsEnumerable()
joemphilips Apr 30, 2019
f302d28
Prepare OutputDescriptor
joemphilips May 1, 2019
539567f
Add test to assert Satisfy does not at least throw error
joemphilips May 1, 2019
eee55dd
Add testcase for ScriptParser
joemphilips May 1, 2019
41dd18d
Move reuseable helpers in transaction_tests to separate class
joemphilips May 1, 2019
2be27bd
* Add method to calculate max push items size for Miniscript.
joemphilips May 1, 2019
9faf00d
Finish working with HTLC in TransactionBuilder
joemphilips May 1, 2019
85903c1
Improve performance for OutputDescriptor.[GetHashCode|Equals]
joemphilips May 2, 2019
7116817
Add validation in OutputDescriptor constructor
joemphilips May 2, 2019
036cfe7
Improve OP_CSV related behaviour of Miniscript
joemphilips May 3, 2019
a4ed336
Support HTLC in TransactionBuilder
joemphilips May 3, 2019
db3ba2e
Improve Shrinker
joemphilips May 5, 2019
1248504
Improve TransactionBuilder.SetRelativeLocktime() so that it can set t…
joemphilips May 7, 2019
6b52675
Improve compiler and deserializer
joemphilips May 7, 2019
316765c
Assure miniscript holds only T expression
joemphilips May 7, 2019
84cdf27
Remove unused Tuck from ScriptToken
joemphilips May 9, 2019
69de96a
Optimize Miniscript by using Debug.Assert
joemphilips May 9, 2019
0ee78db
Make ScriptParser.TryParse more safe
joemphilips May 10, 2019
c0b37e3
nits
joemphilips May 11, 2019
cd063e3
Rename NBitcoin.Miniscript to NBitcoin.Scripting
joemphilips May 17, 2019
0cb0e0a
WIP: Update output descriptor according to bitcoin core
joemphilips May 19, 2019
3913920
Update OutputDescriptor Parser
joemphilips May 19, 2019
f23e17a
Update OutputDescriptor
joemphilips May 20, 2019
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);
}
}
21 changes: 21 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 Expand Up @@ -94,5 +97,23 @@ from raw in Gen.NonEmptyListOf(PrimitiveGenerator.RandomBytes(4))
select NBitcoin.KeyPath.FromBytes(flattenBytes);

public static Gen<ExtPubKey> ExtPubKey() => ExtKey().Select(ek => ek.Neuter());
public static Gen<BitcoinExtPubKey> BitcoinExtPubKey() =>
from extKey in ExtPubKey()
from network in ChainParamsGenerator.NetworkGen()
select new BitcoinExtPubKey(extKey, network);

public static Gen<BitcoinExtKey> BitcoinExtKey() =>
from extKey in ExtKey()
from network in ChainParamsGenerator.NetworkGen()
select new BitcoinExtKey(extKey, network);

public static Gen<RootedKeyPath> RootedKeyPath() =>
from parentFingerPrint in HDFingerPrint()
from kp in KeyPath()
select new RootedKeyPath(parentFingerPrint, kp);

public static Gen<HDFingerprint> HDFingerPrint() =>
from x in PrimitiveGenerator.UInt32()
select new HDFingerprint(x);
}
}
88 changes: 88 additions & 0 deletions NBitcoin.Tests/Generators/OutputDescriptorGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
using FsCheck;
using NBitcoin.Scripting;

namespace NBitcoin.Tests.Generators
{
public class OutputDescriptorGenerator
{
public static Arbitrary<OutputDescriptor> OutputDescriptorArb() =>
Arb.From(OutputDescriptorGen());

public static Gen<OutputDescriptor> OutputDescriptorGen() =>
Gen.OneOf(
AddrOutputDescriptorGen(),
RawOutputDescriptorGen(),
PKOutputDescriptorGen(),
PKHOutputDescriptorGen(),
WPKHOutputDescriptorGen(),
ComboOutputDescriptorGen(),
MultisigOutputDescriptorGen(),
SHOutputDescriptorGen(),
WSHOutputDescriptorGen()
);
private static Gen<OutputDescriptor> AddrOutputDescriptorGen() =>
from addr in AddressGenerator.RandomAddress()
select OutputDescriptor.NewAddr(addr);

private static Gen<OutputDescriptor> RawOutputDescriptorGen() =>
from addr in ScriptGenerator.RandomScriptSig()
select OutputDescriptor.NewRaw(addr);
private static Gen<OutputDescriptor> PKOutputDescriptorGen() =>
from pkProvider in PubKeyProviderGen()
select OutputDescriptor.NewPK(pkProvider);

private static Gen<OutputDescriptor> PKHOutputDescriptorGen() =>
from pkProvider in PubKeyProviderGen()
select OutputDescriptor.NewPKH(pkProvider);

private static Gen<OutputDescriptor> WPKHOutputDescriptorGen() =>
from pkProvider in PubKeyProviderGen()
select OutputDescriptor.NewWPKH(pkProvider);

private static Gen<OutputDescriptor> ComboOutputDescriptorGen() =>
from pkProvider in PubKeyProviderGen()
select OutputDescriptor.NewCombo(pkProvider);

private static Gen<OutputDescriptor> 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<OutputDescriptor> InnerOutputDescriptorGen() =>
Gen.OneOf(
PKOutputDescriptorGen(),
PKHOutputDescriptorGen(),
WPKHOutputDescriptorGen(),
MultisigOutputDescriptorGen()
);
private static Gen<OutputDescriptor> SHOutputDescriptorGen() =>
from inner in Gen.OneOf(InnerOutputDescriptorGen(), WSHOutputDescriptorGen())
select OutputDescriptor.NewSH(inner);

private static Gen<OutputDescriptor> WSHOutputDescriptorGen() =>
from inner in InnerOutputDescriptorGen()
select OutputDescriptor.NewWSH(inner);

#region pubkey providers

private static Gen<PubKeyProvider> PubKeyProviderGen() =>
Gen.OneOf(OriginPubKeyProviderGen(), ConstPubKeyProviderGen(), HDPubKeyProviderGen());

private static Gen<PubKeyProvider> OriginPubKeyProviderGen() =>
from keyOrigin in CryptoGenerator.RootedKeyPath()
from inner in Gen.OneOf(ConstPubKeyProviderGen(), HDPubKeyProviderGen())
select PubKeyProvider.NewOrigin(keyOrigin, inner);

private static Gen<PubKeyProvider> ConstPubKeyProviderGen() =>
from pk in CryptoGenerator.PublicKey()
select PubKeyProvider.NewConst(pk);

private static Gen<PubKeyProvider> HDPubKeyProviderGen() =>
from extPk in CryptoGenerator.BitcoinExtPubKey()
from kp in CryptoGenerator.KeyPath()
from t in Arb.Generate<PubKeyProvider.DeriveType>()
select PubKeyProvider.NewHD(extPk, kp, t);

# endregion
}
}
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 @@

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This class contains a method used to to be in transaction_test .
Some functions (e.g. RandomCoin ) is quite reusable so I've moved it to here.

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(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