-
Notifications
You must be signed in to change notification settings - Fork 871
Support Miniscript (with C#) #684
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
Closed
Closed
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 64e1220
Finish AbstractPolicy
joemphilips a6d3dda
Move everything to separate dir
joemphilips 81aa59b
Finish basic DSLParser stuffs
joemphilips 46b56a3
Finish AstElem impl.
joemphilips f1ae519
Finish Compiler
joemphilips ff57058
Add test for AST -> Script
joemphilips 0b61869
Prepare ScriptToken
joemphilips de46b08
Generalize Parser against input type
joemphilips a3db33e
Split Parsers into separate files. And Implement Script.ToTokens()
joemphilips f30bf08
WIP: finish decompiler
joemphilips e8dd596
Prepare Shrinker
joemphilips 1b9dd35
Improve Deserializer and its tests
joemphilips 1744173
Update deserialization test
joemphilips d0d7a95
WIP: Write Satisfy
joemphilips 0293749
Refactor Satisfy to return bool
joemphilips 7bebf77
Slightly modify TrySatisfy API
joemphilips 4042385
Improve Performance on DSLParser
joemphilips 587e36f
use ToCharArray instead of string.AsEnumerable()
joemphilips f302d28
Prepare OutputDescriptor
joemphilips 539567f
Add test to assert Satisfy does not at least throw error
joemphilips eee55dd
Add testcase for ScriptParser
joemphilips 41dd18d
Move reuseable helpers in transaction_tests to separate class
joemphilips 2be27bd
* Add method to calculate max push items size for Miniscript.
joemphilips 9faf00d
Finish working with HTLC in TransactionBuilder
joemphilips 85903c1
Improve performance for OutputDescriptor.[GetHashCode|Equals]
joemphilips 7116817
Add validation in OutputDescriptor constructor
joemphilips 036cfe7
Improve OP_CSV related behaviour of Miniscript
joemphilips a4ed336
Support HTLC in TransactionBuilder
joemphilips db3ba2e
Improve Shrinker
joemphilips 1248504
Improve TransactionBuilder.SetRelativeLocktime() so that it can set t…
joemphilips 6b52675
Improve compiler and deserializer
joemphilips 316765c
Assure miniscript holds only T expression
joemphilips 84cdf27
Remove unused Tuck from ScriptToken
joemphilips 69de96a
Optimize Miniscript by using Debug.Assert
joemphilips 0ee78db
Make ScriptParser.TryParse more safe
joemphilips c0b37e3
nits
joemphilips cd063e3
Rename NBitcoin.Miniscript to NBitcoin.Scripting
joemphilips 0cb0e0a
WIP: Update output descriptor according to bitcoin core
joemphilips 3913920
Update OutputDescriptor Parser
joemphilips f23e17a
Update OutputDescriptor
joemphilips File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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(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)); | ||
| } | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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.