Skip to content
6 changes: 3 additions & 3 deletions NBitcoin.Tests/AltcoinTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public void NoCrashQuickTest()

foreach (var n in new[] { network.Mainnet, network.Testnet, network.Regtest })
{
n.Parse(new Key().PubKey.GetAddress(n).ToString());
n.Parse(new Key().PubKey.GetAddress(ScriptPubKeyType.Legacy, n).ToString());
}
}
}
Expand Down Expand Up @@ -116,7 +116,7 @@ public void CanSignTransactions()
var rpc = node.CreateRPCClient();

var alice = new Key().GetBitcoinSecret(builder.Network);
BitcoinAddress aliceAddress = alice.GetAddress();
BitcoinAddress aliceAddress = alice.GetAddress(ScriptPubKeyType.Legacy);
var txid = rpc.SendToAddress(aliceAddress, Money.Coins(1.0m));
var tx = rpc.GetRawTransaction(txid);
var coin = tx.Outputs.AsCoins().First(c => c.ScriptPubKey == aliceAddress.ScriptPubKey);
Expand Down Expand Up @@ -172,7 +172,7 @@ public void CanParseAddress()
var addr2 = BitcoinAddress.Create(addr, builder.Network).ToString();
Assert.Equal(addr, addr2);

var address = (BitcoinAddress)new Key().PubKey.GetAddress(builder.Network);
var address = (BitcoinAddress)new Key().PubKey.GetAddress(ScriptPubKeyType.Legacy, builder.Network);

// Test normal address
var isValid = ((JObject)node.CreateRPCClient().SendCommand("validateaddress", address.ToString()).Result)["isvalid"].Value<bool>();
Expand Down
8 changes: 4 additions & 4 deletions NBitcoin.Tests/BIP38Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ public void EncryptedSecretECmultiplyNoLot()
Assert.Null(encryptedKey.LotSequence);
var actualKey = encryptedKey.GetKey(test.Passphrase);
Assert.Equal(test.Unencrypted, actualKey.GetBitcoinSecret(Network.Main).ToString());
Assert.Equal(test.Address, actualKey.PubKey.GetAddress(Network.Main).ToString());
Assert.Equal(test.Address, actualKey.PubKey.GetAddress(ScriptPubKeyType.Legacy, Network.Main).ToString());
Assert.Equal(test.Compressed, actualKey.IsCompressed);


Expand Down Expand Up @@ -163,7 +163,7 @@ public void EncryptedSecretECmultiplyLotSequence()
AssertSequenceEquals(test.LotSequence, encryptedKey.LotSequence);
var actualKey = encryptedKey.GetKey(test.Passphrase);
Assert.Equal(test.Unencrypted, actualKey.GetBitcoinSecret(Network.Main).ToString());
Assert.Equal(test.Address, actualKey.PubKey.GetAddress(Network.Main).ToString());
Assert.Equal(test.Address, actualKey.PubKey.GetAddress(ScriptPubKeyType.Legacy, Network.Main).ToString());
Assert.Equal(test.Compressed, actualKey.IsCompressed);


Expand Down Expand Up @@ -214,10 +214,10 @@ public void EncryptedSecretECmultiplyNoLotSimple()
var result = code.GenerateEncryptedSecret(compressed);
Assert.True(result.ConfirmationCode.Check("test", result.GeneratedAddress));
Assert.False(result.ConfirmationCode.Check("toto", result.GeneratedAddress));
Assert.False(result.ConfirmationCode.Check("test", new Key().PubKey.GetAddress(Network.Main)));
Assert.False(result.ConfirmationCode.Check("test", new Key().PubKey.GetAddress(ScriptPubKeyType.Legacy, Network.Main)));

var decryptedKey = result.EncryptedKey.GetKey("test");
Assert.Equal(result.GeneratedAddress.ToString(), decryptedKey.PubKey.GetAddress(Network.Main).ToString());
Assert.Equal(result.GeneratedAddress.ToString(), decryptedKey.PubKey.GetAddress(ScriptPubKeyType.Legacy, Network.Main).ToString());

Assert.Throws<SecurityException>(() => result.EncryptedKey.GetKey("wrong"));

Expand Down
8 changes: 4 additions & 4 deletions NBitcoin.Tests/ColoredCoinsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ class AssetKey
public AssetKey()
{
Key = new Key();
ScriptPubKey = Key.PubKey.GetAddress(Network.Main).ScriptPubKey;
ScriptPubKey = Key.PubKey.GetAddress(ScriptPubKeyType.Legacy, Network.Main).ScriptPubKey;
Id = ScriptPubKey.Hash.ToAssetId();
}
public Key Key
Expand Down Expand Up @@ -126,8 +126,8 @@ public void CanColorizeSpecScenario()
var a1 = new AssetKey();
var a2 = new AssetKey();
var h = new AssetKey();
var sender = new Key().PubKey.GetAddress(Network.Main);
var receiver = new Key().PubKey.GetAddress(Network.Main);
var sender = new Key().PubKey.GetAddress(ScriptPubKeyType.Legacy, Network.Main);
var receiver = new Key().PubKey.GetAddress(ScriptPubKeyType.Legacy, Network.Main);

colored.Marker = new ColorMarker(new ulong[] { 0, 10, 6, 0, 7, 3 });
colored.Inputs.Add(new ColoredEntry(0, new AssetMoney(a1.Id, 3UL)));
Expand Down Expand Up @@ -445,7 +445,7 @@ public void CanCreateAssetAddress()
//The issuer first generates a private key: 18E14A7B6A307F426A94F8114701E7C8E774E7F9A47E2C2035DB29A206321725.
var key = new Key(TestUtils.ParseHex("18E14A7B6A307F426A94F8114701E7C8E774E7F9A47E2C2035DB29A206321725"));
//He calculates the corresponding address: 16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM.
var address = key.PubKey.Decompress().GetAddress(Network.Main);
var address = key.PubKey.Decompress().GetAddress(ScriptPubKeyType.Legacy, Network.Main);
Assert.Equal("16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM", address.ToString());

//Next, he builds the Pay-to-PubKey-Hash script associated to that address: OP_DUP OP_HASH160 010966776006953D5567439E5E39F86A0D273BEE OP_EQUALVERIFY OP_CHECKSIG
Expand Down
2 changes: 1 addition & 1 deletion NBitcoin.Tests/Generators/AddressGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ from addr in P2PKHAddress(network)

public static Gen<BitcoinAddress> P2PKHAddress(Network network) =>
from pk in PublicKey()
select (BitcoinAddress) pk.GetAddress(network);
select (BitcoinAddress) pk.GetAddress(ScriptPubKeyType.Legacy, network);

public static Gen<BitcoinAddress> P2SHAddress() =>
from network in NetworkGen()
Expand Down
43 changes: 43 additions & 0 deletions NBitcoin.Tests/ParserTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using NBitcoin.Scripting.Parser;
using Xunit;

namespace NBitcoin.Tests
{
/// <summary>
/// Super basic parser just for checking sanity of parser combinator
/// </summary>
internal static class ParserForTest
{
internal enum TokenType
{
Foo,
Bar
}

public static Parser<char, TokenType> PToken() =>
from _prefix in Parse.String("prefix")
from _l in Parse.Char('(').Token()
from foo in Parse.String("foo").Return(TokenType.Foo)
from _r in Parse.Char(')').Token()
select foo;
}
public class ParserTests
{
[Fact]
[Trait("UnitTest", "UnitTest")]
public void BasicParserTest()
{
ParserForTest.PToken().Parse("prefix(foo)");
ParserForTest.PToken().Parse("prefix (foo)");
ParserForTest.PToken().Parse("prefix (foo) ");
ParserForTest.PToken().Parse("prefix ( foo ) ");

// input must be enumerable
foreach (var p in new StringInput("123"))
Assert.Contains(p, "123".ToCharArray());

foreach (var p in new ScriptInput(new Script("OP_ADD OP_EQUALVERIFY")))
Assert.NotNull(p);
}
}
}
2 changes: 1 addition & 1 deletion NBitcoin.Tests/key_tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ public void CanGeneratePubKeysAndAddress()
var address = (BitcoinPubKeyAddress)Network.Main.CreateBitcoinAddress(test.Address);
Assert.Equal(new KeyId(test.Hash160), address.Hash);
Assert.Equal(new KeyId(test.Hash160), secret.PrivateKey.PubKey.Hash);
Assert.Equal(address.Hash, secret.PrivateKey.PubKey.GetAddress(Network.Main).Hash);
Assert.Equal(address, secret.PrivateKey.PubKey.GetAddress(ScriptPubKeyType.Legacy, Network.Main));

var compressedSec = secret.Copy(true);

Expand Down
2 changes: 1 addition & 1 deletion NBitcoin/BIP38/BitcoinConfirmationCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ public bool Check(string passphrase, BitcoinAddress expectedAddress)
//and hash it into address using either compressed or uncompressed public key methodology as specifid in flagbyte.
pubkey = IsCompressed ? pubkey.Compress() : pubkey.Decompress();

var actualhash = BitcoinEncryptedSecretEC.HashAddress(pubkey.GetAddress(Network));
var actualhash = BitcoinEncryptedSecretEC.HashAddress(pubkey.GetAddress(ScriptPubKeyType.Legacy, Network));
var expectedhash = BitcoinEncryptedSecretEC.HashAddress(expectedAddress);

return Utils.ArrayEqual(actualhash, expectedhash);
Expand Down
6 changes: 3 additions & 3 deletions NBitcoin/BIP38/BitcoinEncryptedSecret.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ private static string GenerateWif(Key key, string password, Network network)
{
var vch = key.ToBytes();
//Compute the Bitcoin address (ASCII),
var addressBytes = Encoders.ASCII.DecodeData(key.PubKey.GetAddress(network).ToString());
var addressBytes = Encoders.ASCII.DecodeData(key.PubKey.GetAddress(ScriptPubKeyType.Legacy, network).ToString());
// and take the first four bytes of SHA256(SHA256()) of it. Let's call this "addresshash".
var addresshash = Hashes.Hash256(addressBytes).ToBytes().SafeSubarray(0, 4);

Expand Down Expand Up @@ -91,7 +91,7 @@ public override Key GetKey(string password)

var key = new Key(bitcoinprivkey, fCompressedIn: IsCompressed);

var addressBytes = Encoders.ASCII.DecodeData(key.PubKey.GetAddress(Network).ToString());
var addressBytes = Encoders.ASCII.DecodeData(key.PubKey.GetAddress(ScriptPubKeyType.Legacy, Network).ToString());
var salt = Hashes.Hash256(addressBytes).ToBytes().SafeSubarray(0, 4);

if(!Utils.ArrayEqual(salt, AddressHash))
Expand Down Expand Up @@ -199,7 +199,7 @@ public override Key GetKey(string password)

var key = new Key(keyBytes, fCompressedIn: IsCompressed);

var generatedaddress = key.PubKey.GetAddress(Network);
var generatedaddress = key.PubKey.GetAddress(ScriptPubKeyType.Legacy, Network);
var addresshash = HashAddress(generatedaddress);

if(!Utils.ArrayEqual(addresshash, AddressHash))
Expand Down
2 changes: 1 addition & 1 deletion NBitcoin/BIP38/BitcoinPassphraseCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ public EncryptedKeyResult GenerateEncryptedSecret(bool isCompressed = true, byte
pubKey = isCompressed ? pubKey.Compress() : pubKey.Decompress();

//call it generatedaddress.
var generatedaddress = pubKey.GetAddress(Network);
var generatedaddress = pubKey.GetAddress(ScriptPubKeyType.Legacy, Network);

//Take the first four bytes of SHA256(SHA256(generatedaddress)) and call it addresshash.
var addresshash = BitcoinEncryptedSecretEC.HashAddress(generatedaddress);
Expand Down
16 changes: 16 additions & 0 deletions NBitcoin/Scripting/Parser/IInput.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;

namespace NBitcoin.Scripting.Parser
{
internal interface IInput<out T> : IEnumerable<T>
{
IInput<T> Advance();

T GetCurrent();

bool AtEnd { get; }
int Position { get; }
IDictionary<object, object> Memos { get; }
}
}
125 changes: 125 additions & 0 deletions NBitcoin/Scripting/Parser/Parse.Char.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;

namespace NBitcoin.Scripting.Parser
{
internal static partial class Parse
{
private const string ValidHex = "0123456789abcdef";
private const string ValidBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
private readonly static char[] ValidHexChars;
private readonly static char[] ValidBase58Chars;

static Parse()
{
ValidHexChars = ValidHex.ToCharArray();
ValidBase58Chars = ValidBase58.ToCharArray();
}
public static Parser<char, char> Char(Func<char, bool> predicate, string expected)
{
if (predicate == null)
throw new ArgumentNullException(nameof(predicate));

return i =>
{
if (i.AtEnd)
{
return ParserResult<char, char>.Failure(i, new [] {expected}, "Unexpected end of input");
}

if (predicate(i.GetCurrent()))
return ParserResult<char, char>.Success(i.Advance(), i.GetCurrent());

return ParserResult<char, char>.Failure(i, new [] {expected}, $"Unexpected '{i.GetCurrent()}'");
};
}
public static Parser<char, char> CharExcept(Func<char, bool> predicate, string description)
=> Char(c => !predicate(c), $"any character except {description}");

public static Parser<char, char> Char(char c)
=> Char(ch => c == ch, char.ToString(c));

public static Parser<char, char> Chars(params char[] c)
=> Char(c.Contains, string.Join("|", c));

public static Parser<char, char> Chars(string c)
=> Char(c.ToCharArray().Contains, string.Join("|", c));

public static Parser<char, char> CharExcept(char c)
=> CharExcept(ch => c == ch, c.ToString());

public static readonly Parser<char, char> AnyChar = Char(c => true, "any charactor");
public static readonly Parser<char, char> WhiteSpace = Char(char.IsWhiteSpace, "whitespace");
public static readonly Parser<char, char> Digit = Char(char.IsDigit, "digit");
public static readonly Parser<char, char> Letter = Char(char.IsLetter, "letter");
public static readonly Parser<char, char> LetterOrDigit = Char(char.IsLetterOrDigit, "letter or digit");
public static readonly Parser<char, char> Hex =
Char(c => ValidHexChars.Any(ca => c == ca), "hex");
public static readonly Parser<char, char> Base58 =
Char(c => ValidBase58Chars.Any(ca => c == ca), "hex");

public static readonly Parser<char, char> Numeric = Char(char.IsNumber, "numeric character");

/// <summary>
/// Parse a string of characters.
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
public static Parser<char, IEnumerable<char>> String(string s)
{
if (s == null) throw new ArgumentNullException(nameof(s));

return s
.ToCharArray()
.Select(Char)
.Sequence();
}

public static Parser<char, T> Token<T>(this Parser<char, T> parser)
{
if (parser == null) throw new ArgumentNullException(nameof(parser));

return from leading in WhiteSpace.Many()
from item in parser
from trailing in WhiteSpace.Many()
select item;
}

/// <summary>
/// Convert a stream of characters to a string.
/// </summary>
/// <param name="characters"></param>
/// <returns></returns>
public static Parser<char, string> Text(this Parser<char, IEnumerable<char>> characters)
{
return characters.Select(chs => new string(chs.ToArray()));
}

/// <summary>
/// Parse a number.
/// </summary>
public static readonly Parser<char, string> Number = Numeric.AtLeastOnce().Text();

static Parser<char, string> DecimalWithoutLeadingDigits(CultureInfo ci = null)
{
return from nothing in Return<char, string>("")
// dummy so that CultureInfo.CurrentCulture is evaluated later
from dot in String((ci ?? CultureInfo.InvariantCulture).NumberFormat.NumberDecimalSeparator).Text()
from fraction in Number
select dot + fraction;
}

static Parser<char, string> DecimalWithLeadingDigits(CultureInfo ci = null)
{
return Number.Then(n => DecimalWithoutLeadingDigits(ci).XOr(Return<char, string>("")).Select(f => n + f));
}

/// <summary>
/// Parse a decimal number with separator '.'.
/// </summary>
public static readonly Parser<char, string> Decimal = DecimalWithLeadingDigits()
.XOr(DecimalWithoutLeadingDigits());
}
}
30 changes: 30 additions & 0 deletions NBitcoin/Scripting/Parser/Parse.ScriptToken.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System;

namespace NBitcoin.Scripting.Parser
{
internal static partial class Parse
{
public static Parser<ScriptToken, ScriptToken> ScriptToken(Func<int, bool> predicate, string expected)
{
return i =>
{
if (i.AtEnd)
{
return ParserResult<ScriptToken, ScriptToken>.Failure(i, new [] {expected}, "Unexpected end of input");
}

if (predicate(i.GetCurrent().Tag))
return ParserResult<ScriptToken, ScriptToken>.Success(i.Advance(), i.GetCurrent());
return ParserResult<ScriptToken, ScriptToken>.Failure(i, $"Unexpected {i.GetCurrent()}");
};
}

public static Parser<ScriptToken, ScriptToken> ScriptToken(ScriptToken sct, string expected)
=> ScriptToken((tag) => tag == sct.Tag, expected);

public static Parser<ScriptToken, ScriptToken> ScriptToken(int tag)
=> ScriptToken(actualTag => actualTag == tag, $"ScriptToken for tag: {tag}");
public static Parser<ScriptToken, ScriptToken> ScriptToken(ScriptToken sct)
=> ScriptToken(sct, sct.ToString());
}
}
Loading