Commit 2cfa0f42 authored by Pavel Pavlov's avatar Pavel Pavlov

Modify project NBitcoin

parent 2edbb663
......@@ -16,7 +16,7 @@ namespace NBitcoin
public class Scope : IDisposable
{
Action close;
private Action close;
public Scope(Action open, Action close)
{
this.close = close;
......@@ -27,7 +27,7 @@ namespace NBitcoin
public void Dispose()
{
close();
this.close();
}
#endregion
......@@ -48,7 +48,7 @@ namespace NBitcoin
// TODO: Make NetworkOptions required in the constructors of this class.
public partial class BitcoinStream
{
int maxArraySize = 1024 * 1024;
private int maxArraySize = 1024 * 1024;
public int MaxArraySize
{
get
......@@ -62,7 +62,7 @@ namespace NBitcoin
}
//ReadWrite<T>(ref T data)
static MethodInfo readWriteTyped;
private static MethodInfo readWriteTyped;
static BitcoinStream()
{
readWriteTyped = typeof(BitcoinStream)
......@@ -95,6 +95,7 @@ namespace NBitcoin
public BitcoinStream(Stream inner, bool serializing)
{
this.ConsensusFactory = new DefaultConsensusFactory();
this.serializing = serializing;
this.inner = inner;
}
......@@ -138,12 +139,12 @@ namespace NBitcoin
{
if (this.Serializing)
{
VarString str = new VarString(bytes);
var str = new VarString(bytes);
str.ReadWrite(this);
}
else
{
VarString str = new VarString();
var str = new VarString();
str.ReadWrite(this);
bytes = str.GetString(true);
}
......@@ -193,12 +194,14 @@ namespace NBitcoin
public void ReadWrite<T>(ref T data) where T : IBitcoinSerializable
{
var obj = data;
T obj = data;
if (obj == null)
{
if (!this.ConsensusFactory.TryCreateNew<T>(out obj))
obj = this.ConsensusFactory.TryCreateNew<T>();
if (obj == null)
obj = Activator.CreateInstance<T>();
}
obj.ReadWrite(this);
if (!this.Serializing)
data = obj;
......@@ -220,7 +223,7 @@ namespace NBitcoin
where TList : List<TItem>, new()
where TItem : IBitcoinSerializable, new()
{
var dataArray = data == null ? null : data.ToArray();
TItem[] dataArray = data == null ? null : data.ToArray();
if (this.Serializing && dataArray == null)
{
......@@ -241,7 +244,7 @@ namespace NBitcoin
public void ReadWrite(ref byte[] arr)
{
this.ReadWriteBytes(ref arr);
ReadWriteBytes(ref arr);
}
public void ReadWrite(ref byte[] arr, int offset, int count)
......@@ -265,7 +268,7 @@ namespace NBitcoin
{
var bytes = new byte[size];
for(int i = 0; i < size; i++)
for (int i = 0; i < size; i++)
{
bytes[i] = (byte)(value >> i * 8);
}
......@@ -277,7 +280,7 @@ namespace NBitcoin
ulong valueTemp = 0;
for (int i = 0; i < bytes.Length; i++)
{
var v = (ulong)bytes[i];
ulong v = (ulong)bytes[i];
valueTemp += v << (i * 8);
}
value = valueTemp;
......@@ -300,7 +303,7 @@ namespace NBitcoin
}
else
{
var readen = this.Inner.ReadEx(data, offset, count, this.ReadCancellationToken);
int readen = this.Inner.ReadEx(data, offset, count, this.ReadCancellationToken);
if (readen == 0)
throw new EndOfStreamException("No more byte to read");
this.Counter.AddRead(readen);
......@@ -328,7 +331,7 @@ namespace NBitcoin
}
else
{
var readen = this.Inner.ReadByte();
int readen = this.Inner.ReadByte();
if (readen == -1)
throw new EndOfStreamException("No more byte to read");
data = (byte)readen;
......@@ -344,7 +347,7 @@ namespace NBitcoin
public IDisposable BigEndianScope()
{
var old = this.IsBigEndian;
bool old = this.IsBigEndian;
return new Scope(() =>
{
this.IsBigEndian = true;
......@@ -355,7 +358,7 @@ namespace NBitcoin
});
}
ProtocolVersion protocolVersion = ProtocolVersion.PROTOCOL_VERSION;
private ProtocolVersion protocolVersion = ProtocolVersion.PROTOCOL_VERSION;
public ProtocolVersion ProtocolVersion
{
get
......@@ -368,7 +371,7 @@ namespace NBitcoin
}
}
TransactionOptions transactionSupportedOptions = TransactionOptions.All;
private TransactionOptions transactionSupportedOptions = TransactionOptions.All;
public TransactionOptions TransactionOptions
{
get
......@@ -388,7 +391,7 @@ namespace NBitcoin
public IDisposable ProtocolVersionScope(ProtocolVersion version)
{
var old = this.ProtocolVersion;
ProtocolVersion old = this.ProtocolVersion;
return new Scope(() =>
{
this.ProtocolVersion = version;
......@@ -419,7 +422,7 @@ namespace NBitcoin
public IDisposable SerializationTypeScope(SerializationType value)
{
var old = this.Type;
SerializationType old = this.Type;
return new Scope(() =>
{
this.Type = value;
......
......@@ -23,16 +23,16 @@ namespace NBitcoin
}
// 20,000 items with fp rate < 0.1% or 10,000 items and <0.0001%
const uint MAX_BLOOM_FILTER_SIZE = 36000; // bytes
const uint MAX_HASH_FUNCS = 50;
const decimal LN2SQUARED = 0.4804530139182014246671025263266649717305529515945455M;
const decimal LN2 = 0.6931471805599453094172321214581765680755001343602552M;
private const uint MAX_BLOOM_FILTER_SIZE = 36000; // bytes
private const uint MAX_HASH_FUNCS = 50;
private const decimal LN2SQUARED = 0.4804530139182014246671025263266649717305529515945455M;
private const decimal LN2 = 0.6931471805599453094172321214581765680755001343602552M;
byte[] vData;
uint nHashFuncs;
uint nTweak;
byte nFlags;
private byte[] vData;
private uint nHashFuncs;
private uint nTweak;
private byte nFlags;
private bool isFull = false;
private bool isEmpty;
......@@ -48,49 +48,50 @@ namespace NBitcoin
// The ideal size for a bloom filter with a given number of elements and false positive rate is:
// - nElements * log(fp rate) / ln(2)^2
// We ignore filter parameters which will create a bloom filter larger than the protocol limits
vData = new byte[Math.Min((uint)(-1 / LN2SQUARED * nElements * (decimal)Math.Log(nFPRate)), MAX_BLOOM_FILTER_SIZE) / 8];
this.vData = new byte[Math.Min((uint)(-1 / LN2SQUARED * nElements * (decimal)Math.Log(nFPRate)), MAX_BLOOM_FILTER_SIZE) / 8];
//vData(min((unsigned int)(-1 / LN2SQUARED * nElements * log(nFPRate)), MAX_BLOOM_FILTER_SIZE * 8) / 8),
// The ideal number of hash functions is filter size * ln(2) / number of elements
// Again, we ignore filter parameters which will create a bloom filter with more hash functions than the protocol limits
// See http://en.wikipedia.org/wiki/Bloom_filter for an explanation of these formulas
this.nHashFuncs = Math.Min((uint)(vData.Length * 8 / nElements * LN2), MAX_HASH_FUNCS);
this.nHashFuncs = Math.Min((uint)(this.vData.Length * 8 / nElements * LN2), MAX_HASH_FUNCS);
this.nTweak = nTweakIn;
this.nFlags = (byte)nFlagsIn;
}
uint Hash(uint nHashNum, byte[] vDataToHash)
private uint Hash(uint nHashNum, byte[] vDataToHash)
{
// 0xFBA4C795 chosen as it guarantees a reasonable bit difference between nHashNum values.
return (uint)(Hashes.MurmurHash3(nHashNum * 0xFBA4C795 + nTweak, vDataToHash) % (vData.Length * 8));
return (uint)(Hashes.MurmurHash3(nHashNum * 0xFBA4C795 + this.nTweak, vDataToHash) % (this.vData.Length * 8));
}
public void Insert(byte[] vKey)
{
if(isFull)
if(this.isFull)
return;
for(uint i = 0; i < nHashFuncs; i++)
for(uint i = 0; i < this.nHashFuncs; i++)
{
uint nIndex = Hash(i, vKey);
// Sets bit nIndex of vData
vData[nIndex >> 3] |= (byte)(1 << (7 & (int)nIndex));
this.vData[nIndex >> 3] |= (byte)(1 << (7 & (int)nIndex));
}
isEmpty = false;
this.isEmpty = false;
}
public bool Contains(byte[] vKey)
{
if(isFull)
if(this.isFull)
return true;
if(isEmpty)
if(this.isEmpty)
return false;
for(uint i = 0; i < nHashFuncs; i++)
for(uint i = 0; i < this.nHashFuncs; i++)
{
uint nIndex = Hash(i, vKey);
// Checks bit nIndex of vData
if((vData[nIndex >> 3] & (byte)(1 << (7 & (int)nIndex))) == 0)
if((this.vData[nIndex >> 3] & (byte)(1 << (7 & (int)nIndex))) == 0)
return false;
}
return true;
......@@ -125,17 +126,17 @@ namespace NBitcoin
public bool IsWithinSizeConstraints()
{
return vData.Length <= MAX_BLOOM_FILTER_SIZE && nHashFuncs <= MAX_HASH_FUNCS;
return this.vData.Length <= MAX_BLOOM_FILTER_SIZE && this.nHashFuncs <= MAX_HASH_FUNCS;
}
#region IBitcoinSerializable Members
public void ReadWrite(BitcoinStream stream)
{
stream.ReadWriteAsVarString(ref vData);
stream.ReadWrite(ref nHashFuncs);
stream.ReadWrite(ref nTweak);
stream.ReadWrite(ref nFlags);
stream.ReadWriteAsVarString(ref this.vData);
stream.ReadWrite(ref this.nHashFuncs);
stream.ReadWrite(ref this.nTweak);
stream.ReadWrite(ref this.nFlags);
}
#endregion
......@@ -146,13 +147,13 @@ namespace NBitcoin
{
if(tx == null)
throw new ArgumentNullException("tx");
var hash = tx.GetHash();
uint256 hash = tx.GetHash();
bool fFound = false;
// Match if the filter contains the hash of tx
// for finding tx when they appear in a block
if(isFull)
if(this.isFull)
return true;
if(isEmpty)
if(this.isEmpty)
return false;
if(Contains(hash))
fFound = true;
......@@ -169,11 +170,11 @@ namespace NBitcoin
if(op.PushData != null && op.PushData.Length != 0 && Contains(op.PushData))
{
fFound = true;
if((nFlags & (byte)BloomFlags.UPDATE_MASK) == (byte)BloomFlags.UPDATE_ALL)
if((this.nFlags & (byte)BloomFlags.UPDATE_MASK) == (byte)BloomFlags.UPDATE_ALL)
Insert(new OutPoint(hash, i));
else if((nFlags & (byte)BloomFlags.UPDATE_MASK) == (byte)BloomFlags.UPDATE_P2PUBKEY_ONLY)
else if((this.nFlags & (byte)BloomFlags.UPDATE_MASK) == (byte)BloomFlags.UPDATE_P2PUBKEY_ONLY)
{
var template = StandardScripts.GetTemplateFromScriptPubKey(Network.Main, txout.ScriptPubKey); // this is only valid for Bitcoin.
ScriptTemplate template = StandardScripts.GetTemplateFromScriptPubKey(txout.ScriptPubKey); // this is only valid for Bitcoin.
if(template != null &&
(template.Type == TxOutType.TX_PUBKEY || template.Type == TxOutType.TX_MULTISIG))
Insert(new OutPoint(hash, i));
......
......@@ -8,7 +8,7 @@ namespace NBitcoin.BuilderExtensions
{
public override bool CanCombineScriptSig(Network network, Script scriptPubKey, Script a, Script b)
{
return PayToMultiSigTemplate.Instance.ExtractScriptPubKeyParameters(network, scriptPubKey) != null;
return PayToMultiSigTemplate.Instance.ExtractScriptPubKeyParameters(scriptPubKey) != null;
}
public override bool CanDeduceScriptPubKey(Network network, Script scriptSig)
......@@ -18,31 +18,31 @@ namespace NBitcoin.BuilderExtensions
public override bool CanEstimateScriptSigSize(Network network, Script scriptPubKey)
{
return PayToMultiSigTemplate.Instance.ExtractScriptPubKeyParameters(network, scriptPubKey) != null;
return PayToMultiSigTemplate.Instance.ExtractScriptPubKeyParameters(scriptPubKey) != null;
}
public override bool CanGenerateScriptSig(Network network, Script scriptPubKey)
{
return PayToMultiSigTemplate.Instance.ExtractScriptPubKeyParameters(network, scriptPubKey) != null;
return PayToMultiSigTemplate.Instance.ExtractScriptPubKeyParameters(scriptPubKey) != null;
}
public override Script CombineScriptSig(Network network, Script scriptPubKey, Script a, Script b)
{
var para = PayToMultiSigTemplate.Instance.ExtractScriptPubKeyParameters(network, scriptPubKey);
PayToMultiSigTemplateParameters para = PayToMultiSigTemplate.Instance.ExtractScriptPubKeyParameters(scriptPubKey);
// Combine all the signatures we've got:
var aSigs = PayToMultiSigTemplate.Instance.ExtractScriptSigParameters(network, a);
TransactionSignature[] aSigs = PayToMultiSigTemplate.Instance.ExtractScriptSigParameters(network, a);
if(aSigs == null)
return b;
var bSigs = PayToMultiSigTemplate.Instance.ExtractScriptSigParameters(network, b);
TransactionSignature[] bSigs = PayToMultiSigTemplate.Instance.ExtractScriptSigParameters(network, b);
if(bSigs == null)
return a;
int sigCount = 0;
TransactionSignature[] sigs = new TransactionSignature[para.PubKeys.Length];
var sigs = new TransactionSignature[para.PubKeys.Length];
for(int i = 0; i < para.PubKeys.Length; i++)
{
var aSig = i < aSigs.Length ? aSigs[i] : null;
var bSig = i < bSigs.Length ? bSigs[i] : null;
var sig = aSig ?? bSig;
TransactionSignature aSig = i < aSigs.Length ? aSigs[i] : null;
TransactionSignature bSig = i < bSigs.Length ? bSigs[i] : null;
TransactionSignature sig = aSig ?? bSig;
if(sig != null)
{
sigs[i] = sig;
......@@ -63,15 +63,15 @@ namespace NBitcoin.BuilderExtensions
public override int EstimateScriptSigSize(Network network, Script scriptPubKey)
{
var p2mk = PayToMultiSigTemplate.Instance.ExtractScriptPubKeyParameters(network, scriptPubKey);
PayToMultiSigTemplateParameters p2mk = PayToMultiSigTemplate.Instance.ExtractScriptPubKeyParameters(scriptPubKey);
return PayToMultiSigTemplate.Instance.GenerateScriptSig(Enumerable.Range(0, p2mk.SignatureCount).Select(o => DummySignature).ToArray()).Length;
}
public override Script GenerateScriptSig(Network network, Script scriptPubKey, IKeyRepository keyRepo, ISigner signer)
{
var multiSigParams = PayToMultiSigTemplate.Instance.ExtractScriptPubKeyParameters(network, scriptPubKey);
TransactionSignature[] signatures = new TransactionSignature[multiSigParams.PubKeys.Length];
var keys =
PayToMultiSigTemplateParameters multiSigParams = PayToMultiSigTemplate.Instance.ExtractScriptPubKeyParameters(scriptPubKey);
var signatures = new TransactionSignature[multiSigParams.PubKeys.Length];
Key[] keys =
multiSigParams
.PubKeys
.Select(p => keyRepo.FindKey(p.ScriptPubKey))
......@@ -84,7 +84,7 @@ namespace NBitcoin.BuilderExtensions
break;
if(keys[i] != null)
{
var sig = signer.Sign(keys[i]);
TransactionSignature sig = signer.Sign(keys[i]);
signatures[i] = sig;
sigCount++;
}
......
......@@ -4,29 +4,29 @@
{
public override bool CanCombineScriptSig(Network network, Script scriptPubKey, Script a, Script b)
{
return PayToPubkeyHashTemplate.Instance.CheckScriptPubKey(network, scriptPubKey);
return PayToPubkeyHashTemplate.Instance.CheckScriptPubKey(scriptPubKey);
}
public override bool CanDeduceScriptPubKey(Network network, Script scriptSig)
{
var para = PayToPubkeyHashTemplate.Instance.ExtractScriptSigParameters(network, scriptSig);
PayToPubkeyHashScriptSigParameters para = PayToPubkeyHashTemplate.Instance.ExtractScriptSigParameters(network, scriptSig);
return para != null && para.PublicKey != null;
}
public override bool CanEstimateScriptSigSize(Network network, Script scriptPubKey)
{
return PayToPubkeyHashTemplate.Instance.CheckScriptPubKey(network, scriptPubKey);
return PayToPubkeyHashTemplate.Instance.CheckScriptPubKey(scriptPubKey);
}
public override bool CanGenerateScriptSig(Network network, Script scriptPubKey)
{
return PayToPubkeyHashTemplate.Instance.CheckScriptPubKey(network, scriptPubKey);
return PayToPubkeyHashTemplate.Instance.CheckScriptPubKey(scriptPubKey);
}
public override Script CombineScriptSig(Network network, Script scriptPubKey, Script a, Script b)
{
var aSig = PayToPubkeyHashTemplate.Instance.ExtractScriptSigParameters(network, a);
var bSig = PayToPubkeyHashTemplate.Instance.ExtractScriptSigParameters(network, b);
PayToPubkeyHashScriptSigParameters aSig = PayToPubkeyHashTemplate.Instance.ExtractScriptSigParameters(network, a);
PayToPubkeyHashScriptSigParameters bSig = PayToPubkeyHashTemplate.Instance.ExtractScriptSigParameters(network, b);
if(aSig == null)
return b;
if(bSig == null)
......@@ -39,7 +39,7 @@
public override Script DeduceScriptPubKey(Network network, Script scriptSig)
{
var p2pkh = PayToPubkeyHashTemplate.Instance.ExtractScriptSigParameters(network, scriptSig);
PayToPubkeyHashScriptSigParameters p2pkh = PayToPubkeyHashTemplate.Instance.ExtractScriptSigParameters(network, scriptSig);
return p2pkh.PublicKey.Hash.ScriptPubKey;
}
......@@ -50,11 +50,11 @@
public override Script GenerateScriptSig(Network network, Script scriptPubKey, IKeyRepository keyRepo, ISigner signer)
{
var parameters = PayToPubkeyHashTemplate.Instance.ExtractScriptPubKeyParameters(scriptPubKey);
var key = keyRepo.FindKey(parameters.ScriptPubKey);
KeyId parameters = PayToPubkeyHashTemplate.Instance.ExtractScriptPubKeyParameters(scriptPubKey);
Key key = keyRepo.FindKey(parameters.ScriptPubKey);
if(key == null)
return null;
var sig = signer.Sign(key);
TransactionSignature sig = signer.Sign(key);
return PayToPubkeyHashTemplate.Instance.GenerateScriptSig(sig, key.PubKey);
}
}
......
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using NBitcoin.Protocol;
namespace NBitcoin
{
public class CachedNoSqlRepository : NoSqlRepository
{
class Raw : IBitcoinSerializable
private class Raw : IBitcoinSerializable
{
public Raw()
{
}
public Raw(byte[] data)
{
var str = new VarString();
str.FromBytes(data);
_Data = str.GetString(true);
}
private byte[] _Data = new byte[0];
public byte[] Data
{
get
{
return _Data;
return this._Data;
}
}
#region IBitcoinSerializable Members
public void ReadWrite(BitcoinStream stream)
{
stream.ReadWriteAsVarString(ref _Data);
stream.ReadWriteAsVarString(ref this._Data);
}
#endregion
}
public CachedNoSqlRepository(NoSqlRepository inner)
public CachedNoSqlRepository(NoSqlRepository inner) : base(inner.Network)
{
_InnerRepository = inner;
}
private readonly NoSqlRepository _InnerRepository;
public NoSqlRepository InnerRepository
{
get
{
return _InnerRepository;
}
this.InnerRepository = inner;
}
Dictionary<string, byte[]> _Table = new Dictionary<string, byte[]>();
HashSet<string> _Removed = new HashSet<string>();
HashSet<string> _Added = new HashSet<string>();
ReaderWriterLock @lock = new ReaderWriterLock();
public NoSqlRepository InnerRepository { get; }
private Dictionary<string, byte[]> _Table = new Dictionary<string, byte[]>();
private HashSet<string> _Removed = new HashSet<string>();
private HashSet<string> _Added = new HashSet<string>();
private ReaderWriterLock @lock = new ReaderWriterLock();
public override async Task PutBatch(IEnumerable<Tuple<string, IBitcoinSerializable>> values)
{
await base.PutBatch(values).ConfigureAwait(false);
await _InnerRepository.PutBatch(values).ConfigureAwait(false);
await this.InnerRepository.PutBatch(values).ConfigureAwait(false);
}
protected override Task PutBytesBatch(IEnumerable<Tuple<string, byte[]>> enumerable)
{
using(@lock.LockWrite())
using (this.@lock.LockWrite())
{
foreach(var data in enumerable)
foreach (Tuple<string, byte[]> data in enumerable)
{
if(data.Item2 == null)
if (data.Item2 == null)
{
_Table.Remove(data.Item1);
_Removed.Add(data.Item1);
_Added.Remove(data.Item1);
this._Table.Remove(data.Item1);
this._Removed.Add(data.Item1);
this._Added.Remove(data.Item1);
}
else
{
_Table.AddOrReplace(data.Item1, data.Item2);
_Removed.Remove(data.Item1);
_Added.Add(data.Item1);
this._Table.AddOrReplace(data.Item1, data.Item2);
this._Removed.Remove(data.Item1);
this._Added.Add(data.Item1);
}
}
}
......@@ -88,37 +77,23 @@ namespace NBitcoin
{
byte[] result = null;
bool found;
using(@lock.LockRead())
using (this.@lock.LockRead())
{
found = _Table.TryGetValue(key, out result);
found = this._Table.TryGetValue(key, out result);
}
if(!found)
if (!found)
{
var raw = await InnerRepository.GetAsync<Raw>(key).ConfigureAwait(false);
if(raw != null)
Raw raw = await this.InnerRepository.GetAsync<Raw>(key).ConfigureAwait(false);
if (raw != null)
{
result = raw.Data;
using(@lock.LockWrite())
using (this.@lock.LockWrite())
{
_Table.AddOrReplace(key, raw.Data);
this._Table.AddOrReplace(key, raw.Data);
}
}
}
return result;
}
public void Flush()
{
using(@lock.LockWrite())
{
InnerRepository
.PutBatch(_Removed.Select(k => Tuple.Create<string, IBitcoinSerializable>(k, null))
.Concat(_Added.Select(k => Tuple.Create<string, IBitcoinSerializable>(k, new Raw(_Table[k])))))
.GetAwaiter().GetResult();
_Removed.Clear();
_Added.Clear();
_Table.Clear();
}
}
}
}
}
\ No newline at end of file
......@@ -51,12 +51,12 @@ namespace NBitcoin
}
public IssuanceCoin(Coin bearer)
{
Bearer = bearer;
this.Bearer = bearer;
}
public IssuanceCoin(OutPoint outpoint, TxOut txout)
{
Bearer = new Coin(outpoint, txout);
this.Bearer = new Coin(outpoint, txout);
}
......@@ -64,7 +64,7 @@ namespace NBitcoin
{
get
{
return Bearer.TxOut.ScriptPubKey.Hash.ToAssetId();
return this.Bearer.TxOut.ScriptPubKey.Hash.ToAssetId();
}
}
......@@ -81,11 +81,11 @@ namespace NBitcoin
{
get
{
return Bearer.TxOut.Value;
return this.Bearer.TxOut.Value;
}
set
{
Bearer.TxOut.Value = value;
this.Bearer.TxOut.Value = value;
}
}
......@@ -93,7 +93,7 @@ namespace NBitcoin
{
get
{
return Bearer.TxOut;
return this.Bearer.TxOut;
}
}
......@@ -103,7 +103,7 @@ namespace NBitcoin
{
get
{
return Bearer.TxOut.ScriptPubKey;
return this.Bearer.TxOut.ScriptPubKey;
}
}
......@@ -121,7 +121,7 @@ namespace NBitcoin
{
get
{
return Bearer.Outpoint;
return this.Bearer.Outpoint;
}
}
......@@ -133,7 +133,7 @@ namespace NBitcoin
{
get
{
return AssetId;
return this.AssetId;
}
}
......@@ -141,7 +141,7 @@ namespace NBitcoin
{
get
{
return Bearer;
return this.Bearer;
}
}
......@@ -153,7 +153,7 @@ namespace NBitcoin
{
get
{
return Amount;
return this.Amount;
}
}
......@@ -161,7 +161,7 @@ namespace NBitcoin
{
get
{
return Outpoint;
return this.Outpoint;
}
}
......@@ -169,7 +169,7 @@ namespace NBitcoin
{
get
{
return TxOut;
return this.TxOut;
}
}
......@@ -209,8 +209,8 @@ namespace NBitcoin
}
public ColoredCoin(AssetMoney asset, Coin bearer)
{
Amount = asset;
Bearer = bearer;
this.Amount = asset;
this.Bearer = bearer;
}
public ColoredCoin(Transaction tx, ColoredEntry entry)
......@@ -226,7 +226,7 @@ namespace NBitcoin
{
get
{
return Amount.Id;
return this.Amount.Id;
}
}
......@@ -241,11 +241,11 @@ namespace NBitcoin
{
get
{
return Amount;
return this.Amount;
}
set
{
Amount = value;
this.Amount = value;
}
}
......@@ -259,7 +259,7 @@ namespace NBitcoin
{
get
{
return Bearer.TxOut;
return this.Bearer.TxOut;
}
}
......@@ -269,7 +269,7 @@ namespace NBitcoin
{
get
{
return Bearer.Outpoint;
return this.Bearer.Outpoint;
}
}
......@@ -277,7 +277,7 @@ namespace NBitcoin
{
get
{
return Bearer.ScriptPubKey;
return this.Bearer.ScriptPubKey;
}
}
......@@ -295,9 +295,9 @@ namespace NBitcoin
throw new ArgumentNullException("tx");
if(txId == null)
txId = tx.GetHash();
foreach(var entry in colored.Issuances.Concat(colored.Transfers))
foreach(ColoredEntry entry in colored.Issuances.Concat(colored.Transfers))
{
var txout = tx.Outputs[entry.Index];
TxOut txout = tx.Outputs[entry.Index];
yield return new ColoredCoin(entry.Asset, new Coin(new OutPoint(txId, entry.Index), txout));
}
}
......@@ -310,7 +310,7 @@ namespace NBitcoin
{
if(txId == null)
txId = tx.GetHash();
var colored = tx.GetColoredTransaction(repo);
ColoredTransaction colored = tx.GetColoredTransaction(repo);
return Find(txId, tx, colored);
}
......@@ -320,7 +320,7 @@ namespace NBitcoin
{
get
{
return AssetId;
return this.AssetId;
}
}
......@@ -328,7 +328,7 @@ namespace NBitcoin
{
get
{
return Bearer;
return this.Bearer;
}
}
......@@ -340,7 +340,7 @@ namespace NBitcoin
{
get
{
return Amount;
return this.Amount;
}
}
......@@ -348,7 +348,7 @@ namespace NBitcoin
{
get
{
return Outpoint;
return this.Outpoint;
}
}
......@@ -356,7 +356,7 @@ namespace NBitcoin
{
get
{
return TxOut;
return this.TxOut;
}
}
......@@ -390,16 +390,16 @@ namespace NBitcoin
}
public Coin(OutPoint fromOutpoint, TxOut fromTxOut)
{
Outpoint = fromOutpoint;
TxOut = fromTxOut;
this.Outpoint = fromOutpoint;
this.TxOut = fromTxOut;
}
public Coin(Transaction fromTx, uint fromOutputIndex)
{
if(fromTx == null)
throw new ArgumentNullException("fromTx");
Outpoint = new OutPoint(fromTx, fromOutputIndex);
TxOut = fromTx.Outputs[fromOutputIndex];
this.Outpoint = new OutPoint(fromTx, fromOutputIndex);
this.TxOut = fromTx.Outputs[fromOutputIndex];
}
public Coin(Transaction fromTx, TxOut fromOutput)
......@@ -408,42 +408,42 @@ namespace NBitcoin
throw new ArgumentNullException("fromTx");
if(fromOutput == null)
throw new ArgumentNullException("fromOutput");
uint outputIndex = (uint)fromTx.Outputs.FindIndex(r => Object.ReferenceEquals(fromOutput, r));
Outpoint = new OutPoint(fromTx, outputIndex);
TxOut = fromOutput;
uint outputIndex = (uint)fromTx.Outputs.FindIndex(r => ReferenceEquals(fromOutput, r));
this.Outpoint = new OutPoint(fromTx, outputIndex);
this.TxOut = fromOutput;
}
public Coin(IndexedTxOut txOut)
{
Outpoint = new OutPoint(txOut.Transaction.GetHash(), txOut.N);
TxOut = txOut.TxOut;
this.Outpoint = new OutPoint(txOut.Transaction.GetHash(), txOut.N);
this.TxOut = txOut.TxOut;
}
public Coin(uint256 fromTxHash, uint fromOutputIndex, Money amount, Script scriptPubKey)
{
Outpoint = new OutPoint(fromTxHash, fromOutputIndex);
TxOut = new TxOut(amount, scriptPubKey);
this.Outpoint = new OutPoint(fromTxHash, fromOutputIndex);
this.TxOut = new TxOut(amount, scriptPubKey);
}
public virtual Script GetScriptCode(Network network)
{
if(!CanGetScriptCode(network))
throw new InvalidOperationException("You need to provide P2WSH or P2SH redeem script with Coin.ToScriptCoin()");
if(_OverrideScriptCode != null)
return _OverrideScriptCode;
var key = PayToWitPubKeyHashTemplate.Instance.ExtractScriptPubKeyParameters(network, ScriptPubKey);
if(this._OverrideScriptCode != null)
return this._OverrideScriptCode;
WitKeyId key = PayToWitPubKeyHashTemplate.Instance.ExtractScriptPubKeyParameters(network, this.ScriptPubKey);
if(key != null)
return key.AsKeyId().ScriptPubKey;
return ScriptPubKey;
return this.ScriptPubKey;
}
public virtual bool CanGetScriptCode(Network network)
{
return _OverrideScriptCode != null || !ScriptPubKey.IsPayToScriptHash(network) && !PayToWitScriptHashTemplate.Instance.CheckScriptPubKey(network, ScriptPubKey);
return this._OverrideScriptCode != null || !this.ScriptPubKey.IsPayToScriptHash(network) && !PayToWitScriptHashTemplate.Instance.CheckScriptPubKey(this.ScriptPubKey);
}
public virtual HashVersion GetHashVersion(Network network)
{
if(PayToWitTemplate.Instance.CheckScriptPubKey(network, ScriptPubKey))
if(PayToWitTemplate.Instance.CheckScriptPubKey(this.ScriptPubKey))
return HashVersion.Witness;
return HashVersion.Original;
}
......@@ -489,27 +489,26 @@ namespace NBitcoin
{
get
{
if(TxOut == null)
if(this.TxOut == null)
return Money.Zero;
return TxOut.Value;
return this.TxOut.Value;
}
set
{
EnsureTxOut();
TxOut.Value = value;
this.TxOut.Value = value;
}
}
private void EnsureTxOut()
{
if(TxOut == null)
TxOut = new TxOut();
if(this.TxOut == null) this.TxOut = new TxOut();
}
protected Script _OverrideScriptCode;
public void OverrideScriptCode(Script scriptCode)
{
_OverrideScriptCode = scriptCode;
this._OverrideScriptCode = scriptCode;
}
#endregion
......@@ -518,14 +517,14 @@ namespace NBitcoin
{
get
{
if(TxOut == null)
if(this.TxOut == null)
return Script.Empty;
return TxOut.ScriptPubKey;
return this.TxOut.ScriptPubKey;
}
set
{
EnsureTxOut();
TxOut.ScriptPubKey = value;
this.TxOut.ScriptPubKey = value;
}
}
......@@ -535,7 +534,7 @@ namespace NBitcoin
{
get
{
return Amount;
return this.Amount;
}
}
......@@ -543,7 +542,7 @@ namespace NBitcoin
{
get
{
return Outpoint;
return this.Outpoint;
}
}
......@@ -551,7 +550,7 @@ namespace NBitcoin
{
get
{
return TxOut;
return this.TxOut;
}
}
......@@ -576,40 +575,40 @@ namespace NBitcoin
}
internal ScriptCoin(OutPoint fromOutpoint, TxOut fromTxOut, Script redeem)
public ScriptCoin(OutPoint fromOutpoint, TxOut fromTxOut, Script redeem)
: base(fromOutpoint, fromTxOut)
{
Redeem = redeem;
this.Redeem = redeem;
}
internal ScriptCoin(Transaction fromTx, uint fromOutputIndex, Script redeem)
: base(fromTx, fromOutputIndex)
{
Redeem = redeem;
this.Redeem = redeem;
}
internal ScriptCoin(Transaction fromTx, TxOut fromOutput, Script redeem)
: base(fromTx, fromOutput)
{
Redeem = redeem;
this.Redeem = redeem;
}
internal ScriptCoin(ICoin coin, Script redeem)
: base(coin.Outpoint, coin.TxOut)
{
Redeem = redeem;
this.Redeem = redeem;
}
internal ScriptCoin(IndexedTxOut txOut, Script redeem)
: base(txOut)
{
Redeem = redeem;
this.Redeem = redeem;
}
internal ScriptCoin(uint256 txHash, uint outputIndex, Money amount, Script scriptPubKey, Script redeem)
: base(txHash, outputIndex, amount, scriptPubKey)
{
Redeem = redeem;
this.Redeem = redeem;
}
public static ScriptCoin Create(Network network, OutPoint fromOutpoint, TxOut fromTxOut, Script redeem)
......@@ -646,16 +645,16 @@ namespace NBitcoin
{
get
{
return ScriptPubKey.ToBytes(true)[0] == (byte)OpcodeType.OP_HASH160;
return this.ScriptPubKey.ToBytes(true)[0] == (byte)OpcodeType.OP_HASH160;
}
}
public Script GetP2SHRedeem()
{
if(!IsP2SH)
if(!this.IsP2SH)
return null;
var p2shRedeem = RedeemType == RedeemType.P2SH ? Redeem :
RedeemType == RedeemType.WitnessV0 ? Redeem.WitHash.ScriptPubKey :
Script p2shRedeem = this.RedeemType == RedeemType.P2SH ? this.Redeem :
this.RedeemType == RedeemType.WitnessV0 ? this.Redeem.WitHash.ScriptPubKey :
null;
if(p2shRedeem == null)
throw new NotSupportedException("RedeemType not supported for getting the P2SH script, contact the library author");
......@@ -666,8 +665,7 @@ namespace NBitcoin
{
get
{
return
Redeem.Hash.ScriptPubKey == TxOut.ScriptPubKey ?
return this.Redeem.Hash.ScriptPubKey == this.TxOut.ScriptPubKey ?
RedeemType.P2SH :
RedeemType.WitnessV0;
}
......@@ -675,30 +673,30 @@ namespace NBitcoin
public ScriptCoin AssertCoherent(Network network)
{
if(Redeem == null)
if(this.Redeem == null)
throw new ArgumentException("redeem cannot be null", "redeem");
var expectedDestination = GetRedeemHash(network, TxOut.ScriptPubKey);
TxDestination expectedDestination = GetRedeemHash(network, this.TxOut.ScriptPubKey);
if(expectedDestination == null)
{
throw new ArgumentException("the provided scriptPubKey is not P2SH or P2WSH");
}
if(expectedDestination is ScriptId)
{
if(PayToWitScriptHashTemplate.Instance.CheckScriptPubKey(network, Redeem))
if(PayToWitScriptHashTemplate.Instance.CheckScriptPubKey(this.Redeem))
{
throw new ArgumentException("The redeem script provided must be the witness one, not the P2SH one");
}
if(expectedDestination.ScriptPubKey != Redeem.Hash.ScriptPubKey)
if(expectedDestination.ScriptPubKey != this.Redeem.Hash.ScriptPubKey)
{
if(Redeem.WitHash.ScriptPubKey.Hash.ScriptPubKey != expectedDestination.ScriptPubKey)
if(this.Redeem.WitHash.ScriptPubKey.Hash.ScriptPubKey != expectedDestination.ScriptPubKey)
throw new ArgumentException("The redeem provided does not match the scriptPubKey of the coin");
}
}
else if(expectedDestination is WitScriptId)
{
if(expectedDestination.ScriptPubKey != Redeem.WitHash.ScriptPubKey)
if(expectedDestination.ScriptPubKey != this.Redeem.WitHash.ScriptPubKey)
throw new ArgumentException("The redeem provided does not match the scriptPubKey of the coin");
}
else
......@@ -718,24 +716,23 @@ namespace NBitcoin
{
if(!CanGetScriptCode(network))
throw new InvalidOperationException("You need to provide the P2WSH redeem script with ScriptCoin.ToScriptCoin()");
if(_OverrideScriptCode != null)
return _OverrideScriptCode;
var key = PayToWitPubKeyHashTemplate.Instance.ExtractScriptPubKeyParameters(network, Redeem);
if(this._OverrideScriptCode != null)
return this._OverrideScriptCode;
WitKeyId key = PayToWitPubKeyHashTemplate.Instance.ExtractScriptPubKeyParameters(network, this.Redeem);
if(key != null)
return key.AsKeyId().ScriptPubKey;
return Redeem;
return this.Redeem;
}
public override bool CanGetScriptCode(Network network)
{
return _OverrideScriptCode != null || !IsP2SH || !PayToWitScriptHashTemplate.Instance.CheckScriptPubKey(network, Redeem);
return this._OverrideScriptCode != null || !this.IsP2SH || !PayToWitScriptHashTemplate.Instance.CheckScriptPubKey(this.Redeem);
}
public override HashVersion GetHashVersion(Network network)
{
var isWitness = PayToWitTemplate.Instance.CheckScriptPubKey(network, ScriptPubKey) ||
PayToWitTemplate.Instance.CheckScriptPubKey(network, Redeem) ||
RedeemType == NBitcoin.RedeemType.WitnessV0;
bool isWitness = PayToWitTemplate.Instance.CheckScriptPubKey(this.ScriptPubKey) ||
PayToWitTemplate.Instance.CheckScriptPubKey(this.Redeem) || this.RedeemType == RedeemType.WitnessV0;
return isWitness ? HashVersion.Witness : HashVersion.Original;
}
......@@ -762,9 +759,9 @@ namespace NBitcoin
public StealthCoin(OutPoint outpoint, TxOut txOut, Script redeem, StealthMetadata stealthMetadata, BitcoinStealthAddress address)
: base(outpoint, txOut)
{
StealthMetadata = stealthMetadata;
Address = address;
Redeem = redeem;
this.StealthMetadata = stealthMetadata;
this.Address = address;
this.Redeem = redeem;
}
public StealthMetadata StealthMetadata
{
......@@ -786,20 +783,20 @@ namespace NBitcoin
public override Script GetScriptCode(Network network)
{
if(_OverrideScriptCode != null)
return _OverrideScriptCode;
if(Redeem == null)
if(this._OverrideScriptCode != null)
return this._OverrideScriptCode;
if(this.Redeem == null)
return base.GetScriptCode(network);
else
return ScriptCoin.Create(network, this, Redeem).GetScriptCode(network);
return ScriptCoin.Create(network, this, this.Redeem).GetScriptCode(network);
}
public override HashVersion GetHashVersion(Network network)
{
if(Redeem == null)
if(this.Redeem == null)
return base.GetHashVersion(network);
else
return ScriptCoin.Create(network, this, Redeem).GetHashVersion(network);
return ScriptCoin.Create(network, this, this.Redeem).GetHashVersion(network);
}
/// <summary>
......@@ -811,17 +808,17 @@ namespace NBitcoin
/// <returns></returns>
public static StealthCoin Find(Transaction tx, BitcoinStealthAddress address, Key scan)
{
var payment = address.GetPayments(tx, scan).FirstOrDefault();
StealthPayment payment = address.GetPayments(tx, scan).FirstOrDefault();
if(payment == null)
return null;
var txId = tx.GetHash();
var txout = tx.Outputs.First(o => o.ScriptPubKey == payment.ScriptPubKey);
uint256 txId = tx.GetHash();
TxOut txout = tx.Outputs.First(o => o.ScriptPubKey == payment.ScriptPubKey);
return new StealthCoin(new OutPoint(txId, tx.Outputs.IndexOf(txout)), txout, payment.Redeem, payment.Metadata, address);
}
public StealthPayment GetPayment()
{
return new StealthPayment(TxOut.ScriptPubKey, Redeem, StealthMetadata);
return new StealthPayment(this.TxOut.ScriptPubKey, this.Redeem, this.StealthMetadata);
}
public PubKey[] Uncover(PubKey[] spendPubKeys, Key scanKey)
......@@ -829,7 +826,7 @@ namespace NBitcoin
var pubKeys = new PubKey[spendPubKeys.Length];
for(int i = 0; i < pubKeys.Length; i++)
{
pubKeys[i] = spendPubKeys[i].UncoverReceiver(scanKey, StealthMetadata.EphemKey);
pubKeys[i] = spendPubKeys[i].UncoverReceiver(scanKey, this.StealthMetadata.EphemKey);
}
return pubKeys;
}
......@@ -839,7 +836,7 @@ namespace NBitcoin
var keys = new Key[spendKeys.Length];
for(int i = 0; i < keys.Length; i++)
{
keys[i] = spendKeys[i].Uncover(scanKey, StealthMetadata.EphemKey);
keys[i] = spendKeys[i].Uncover(scanKey, this.StealthMetadata.EphemKey);
}
return keys;
}
......
using System;
namespace NBitcoin
{
/// <summary>
/// A default object factory to create instances that is not block, block header or transaction.
/// </summary>
public sealed class DefaultConsensusFactory : ConsensusFactory
{
/// <inheritdoc/>
public override T TryCreateNew<T>()
{
if (this.IsBlock<T>() || this.IsBlockHeader<T>() || this.IsTransaction<T>())
throw new Exception(string.Format("{0} cannot be created by this consensus factory, please use the appropriate one.", typeof(T).Name));
return default(T);
}
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Text;
using NBitcoin.BouncyCastle.Math;
using NBitcoin.DataEncoders;
namespace NBitcoin.Networks
{
class DeStreamRegTest
public class DeStreamRegTest : Network
{
public DeStreamRegTest()
{
// TODO: move this to Networks
var messageStart = new byte[4];
messageStart[0] = 0xcd;
messageStart[1] = 0xf2;
messageStart[2] = 0xc0;
messageStart[3] = 0xef;
var magic = BitConverter.ToUInt32(messageStart, 0); // 0xefc0f2cd
this.Name = "DeStreamRegTest";
this.RootFolderName = DeStreamRootFolderName;
this.DefaultConfigFilename = DeStreamDefaultConfigFilename;
this.Magic = magic;
this.DefaultPort = 18444;
this.RPCPort = 18442;
this.MaxTimeOffsetSeconds = StratisMaxTimeOffsetSeconds;
this.MaxTipAge = StratisDefaultMaxTipAgeInSeconds;
this.Consensus.SubsidyHalvingInterval = 210000;
this.Consensus.MajorityEnforceBlockUpgrade = 750;
this.Consensus.MajorityRejectBlockOutdated = 950;
this.Consensus.MajorityWindow = 1000;
this.Consensus.BuriedDeployments[BuriedDeployments.BIP34] = 0;
this.Consensus.BuriedDeployments[BuriedDeployments.BIP65] = 0;
this.Consensus.BuriedDeployments[BuriedDeployments.BIP66] = 0;
this.Consensus.BIP34Hash = new uint256("0x000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8");
this.Consensus.PowLimit = new Target(uint256.Parse("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"));
this.Consensus.PowTargetTimespan = TimeSpan.FromSeconds(14 * 24 * 60 * 60); // two weeks
this.Consensus.PowTargetSpacing = TimeSpan.FromSeconds(10 * 60);
this.Consensus.PowAllowMinDifficultyBlocks = true;
this.Consensus.PowNoRetargeting = true;
this.Consensus.RuleChangeActivationThreshold = 1916; // 95% of 2016
this.Consensus.MinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing
this.Consensus.LastPOWBlock = 12500;
this.Consensus.IsProofOfStake = true;
this.Consensus.ConsensusFactory = new PosConsensusFactory() { Consensus = this.Consensus };
this.Consensus.ProofOfStakeLimit = new BigInteger(uint256.Parse("00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").ToBytes(false));
this.Consensus.ProofOfStakeLimitV2 = new BigInteger(uint256.Parse("000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffff").ToBytes(false));
this.Consensus.CoinType = 105;
this.Consensus.DefaultAssumeValid = null; // turn off assumevalid for regtest.
Block genesis = CreateStratisGenesisBlock(this.Consensus.ConsensusFactory, 1470467000, 1831645, 0x1e0fffff, 1, Money.Zero);
genesis.Header.Time = 1494909211;
genesis.Header.Nonce = 2433759;
genesis.Header.Bits = this.Consensus.PowLimit;
this.Genesis = genesis;
this.Consensus.HashGenesisBlock = genesis.GetHash();
Assert(this.Consensus.HashGenesisBlock == uint256.Parse("0x93925104d664314f581bc7ecb7b4bad07bcfabd1cfce4256dbd2faddcf53bd1f"));
this.Base58Prefixes[(int)Base58Type.PUBKEY_ADDRESS] = new byte[] { (65) };
this.Base58Prefixes[(int)Base58Type.SCRIPT_ADDRESS] = new byte[] { (196) };
this.Base58Prefixes[(int)Base58Type.SECRET_KEY] = new byte[] { (65 + 128) };
this.Base58Prefixes[(int)Base58Type.ENCRYPTED_SECRET_KEY_NO_EC] = new byte[] { 0x01, 0x42 };
this.Base58Prefixes[(int)Base58Type.ENCRYPTED_SECRET_KEY_EC] = new byte[] { 0x01, 0x43 };
this.Base58Prefixes[(int)Base58Type.EXT_PUBLIC_KEY] = new byte[] { (0x04), (0x88), (0xB2), (0x1E) };
this.Base58Prefixes[(int)Base58Type.EXT_SECRET_KEY] = new byte[] { (0x04), (0x88), (0xAD), (0xE4) };
this.Base58Prefixes[(int)Base58Type.PASSPHRASE_CODE] = new byte[] { 0x2C, 0xE9, 0xB3, 0xE1, 0xFF, 0x39, 0xE2 };
this.Base58Prefixes[(int)Base58Type.CONFIRMATION_CODE] = new byte[] { 0x64, 0x3B, 0xF6, 0xA8, 0x9A };
this.Base58Prefixes[(int)Base58Type.STEALTH_ADDRESS] = new byte[] { 0x2a };
this.Base58Prefixes[(int)Base58Type.ASSET_ID] = new byte[] { 23 };
this.Base58Prefixes[(int)Base58Type.COLORED_ADDRESS] = new byte[] { 0x13 };
var encoder = new Bech32Encoder("bc");
this.Bech32Encoders[(int)Bech32Type.WITNESS_PUBKEY_ADDRESS] = encoder;
this.Bech32Encoders[(int)Bech32Type.WITNESS_SCRIPT_ADDRESS] = encoder;
}
}
}
......@@ -34,7 +34,7 @@ namespace NBitcoin.Networks
this.FallbackFee = 60000;
this.MinRelayTxFee = 10000;
this..Consensus.SubsidyHalvingInterval = 210000;
this.Consensus.SubsidyHalvingInterval = 210000;
this.Consensus.MajorityEnforceBlockUpgrade = 750;
this.Consensus.MajorityRejectBlockOutdated = 950;
this.Consensus.MajorityWindow = 1000;
......@@ -51,7 +51,7 @@ namespace NBitcoin.Networks
this.Consensus.MinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing
this.Consensus.LastPOWBlock = 12500;
this.Consensus.IsProofOfStake = true;
this.Consensus.ConsensusFactory = new PosConsensusFactory() { Consensus = network.Consensus };
this.Consensus.ConsensusFactory = new PosConsensusFactory() { Consensus = this.Consensus };
this.Consensus.ProofOfStakeLimit = new BigInteger(uint256.Parse("00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").ToBytes(false));
this.Consensus.ProofOfStakeLimitV2 = new BigInteger(uint256.Parse("000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffff").ToBytes(false));
this.Consensus.CoinType = 105;
......
......@@ -7,16 +7,16 @@ namespace NBitcoin
{
public abstract class NoSqlRepository
{
private readonly Network network;
public readonly Network Network;
protected NoSqlRepository(Network network = null)
protected NoSqlRepository(Network network)
{
this.network = network ?? Network.Main;
this.Network = network;
}
public Task PutAsync(string key, IBitcoinSerializable obj)
{
return PutBytes(key, obj == null ? null : obj.ToBytes(network: this.network));
return PutBytes(key, obj == null ? null : obj.ToBytes(this.Network.Consensus.ConsensusFactory));
}
public void Put(string key, IBitcoinSerializable obj)
......@@ -26,12 +26,13 @@ namespace NBitcoin
public async Task<T> GetAsync<T>(string key) where T : IBitcoinSerializable, new()
{
var data = await GetBytes(key).ConfigureAwait(false);
if(data == null)
byte[] data = await GetBytes(key).ConfigureAwait(false);
if (data == null)
return default(T);
if (!this.network.Consensus.ConsensusFactory.TryCreateNew<T>(out T obj))
T obj = this.Network.Consensus.ConsensusFactory.TryCreateNew<T>();
if (obj == null)
obj = Activator.CreateInstance<T>();
obj.ReadWrite(data, network: this.network);
obj.ReadWrite(data, consensusFactory: this.Network.Consensus.ConsensusFactory);
return obj;
}
......@@ -53,4 +54,4 @@ namespace NBitcoin
return PutBytesBatch(new[] { new Tuple<string, byte[]>(key, data) });
}
}
}
}
\ No newline at end of file
......@@ -11,13 +11,13 @@ namespace NBitcoin.Policy
public StandardTransactionPolicy(Network network)
{
this.network = network;
ScriptVerify = NBitcoin.ScriptVerify.Standard;
MaxTransactionSize = 100000;
this.ScriptVerify = NBitcoin.ScriptVerify.Standard;
this.MaxTransactionSize = 100000;
// TODO: replace fee params with whats in Network.
MaxTxFee = new FeeRate(Money.Coins(0.1m));
MinRelayTxFee = new FeeRate(Money.Satoshis(5000)); // TODO: new FeeRate(Money.Satoshis(network.MinRelayTxFee));
CheckFee = true;
CheckScriptPubKey = true;
this.MaxTxFee = new FeeRate(Money.Coins(0.1m));
this.MinRelayTxFee = new FeeRate(Money.Satoshis(5000)); // TODO: new FeeRate(Money.Satoshis(network.MinRelayTxFee));
this.CheckFee = true;
this.CheckScriptPubKey = true;
}
public int? MaxTransactionSize
......@@ -56,118 +56,118 @@ namespace NBitcoin.Policy
get;
set;
}
#if !NOCONSENSUSLIB
public bool UseConsensusLib
{
get;
set;
}
#endif
public const int MaxScriptSigLength = 1650;
#region ITransactionPolicy Members
public TransactionPolicyError[] Check(Transaction transaction, ICoin[] spentCoins)
{
if(transaction == null)
if (transaction == null)
throw new ArgumentNullException("transaction");
spentCoins = spentCoins ?? new ICoin[0];
List<TransactionPolicyError> errors = new List<TransactionPolicyError>();
var errors = new List<TransactionPolicyError>();
foreach(var input in transaction.Inputs.AsIndexedInputs())
foreach (IndexedTxIn input in transaction.Inputs.AsIndexedInputs())
{
var coin = spentCoins.FirstOrDefault(s => s.Outpoint == input.PrevOut);
if(coin != null)
ICoin coin = spentCoins.FirstOrDefault(s => s.Outpoint == input.PrevOut);
if (coin != null)
{
if(ScriptVerify != null)
if (this.ScriptVerify != null)
{
ScriptError error;
if(!this.VerifyScript(input, coin.TxOut.ScriptPubKey, coin.TxOut.Value, ScriptVerify.Value, out error))
if (!VerifyScript(input, coin.TxOut.ScriptPubKey, coin.TxOut.Value, this.ScriptVerify.Value, out error))
{
errors.Add(new ScriptPolicyError(input, error, ScriptVerify.Value, coin.TxOut.ScriptPubKey));
errors.Add(new ScriptPolicyError(input, error, this.ScriptVerify.Value, coin.TxOut.ScriptPubKey));
}
}
}
var txin = input.TxIn;
if(txin.ScriptSig.Length > MaxScriptSigLength)
TxIn txin = input.TxIn;
if (txin.ScriptSig.Length > MaxScriptSigLength)
{
errors.Add(new InputPolicyError("Max scriptSig length exceeded actual is " + txin.ScriptSig.Length + ", max is " + MaxScriptSigLength, input));
}
if(!txin.ScriptSig.IsPushOnly)
if (!txin.ScriptSig.IsPushOnly)
{
errors.Add(new InputPolicyError("All operation should be push", input));
}
if(!txin.ScriptSig.HasCanonicalPushes)
if (!txin.ScriptSig.HasCanonicalPushes)
{
errors.Add(new InputPolicyError("All operation should be canonical push", input));
}
}
if(CheckMalleabilitySafe)
if (this.CheckMalleabilitySafe)
{
foreach(var input in transaction.Inputs.AsIndexedInputs())
foreach (IndexedTxIn input in transaction.Inputs.AsIndexedInputs())
{
var coin = spentCoins.FirstOrDefault(s => s.Outpoint == input.PrevOut);
if(coin != null && coin.GetHashVersion(this.network) != HashVersion.Witness)
ICoin coin = spentCoins.FirstOrDefault(s => s.Outpoint == input.PrevOut);
if (coin != null && coin.GetHashVersion(this.network) != HashVersion.Witness)
errors.Add(new InputPolicyError("Malleable input detected", input));
}
}
if(CheckScriptPubKey)
if (this.CheckScriptPubKey)
{
foreach(var txout in transaction.Outputs.AsCoins())
foreach (Coin txout in transaction.Outputs.AsCoins())
{
var template = StandardScripts.GetTemplateFromScriptPubKey(this.network, txout.ScriptPubKey);
if(template == null)
ScriptTemplate template = StandardScripts.GetTemplateFromScriptPubKey(txout.ScriptPubKey);
if (template == null)
errors.Add(new OutputPolicyError("Non-Standard scriptPubKey", (int)txout.Outpoint.N));
}
}
int txSize = transaction.GetSerializedSize();
if(MaxTransactionSize != null)
if (this.MaxTransactionSize != null)
{
if(txSize >= MaxTransactionSize.Value)
errors.Add(new TransactionSizePolicyError(txSize, MaxTransactionSize.Value));
if (txSize >= this.MaxTransactionSize.Value)
errors.Add(new TransactionSizePolicyError(txSize, this.MaxTransactionSize.Value));
}
var fees = transaction.GetFee(spentCoins);
if(fees != null)
Money fees = transaction.GetFee(spentCoins);
if (fees != null)
{
if(CheckFee)
if (this.CheckFee)
{
if(MaxTxFee != null)
if (this.MaxTxFee != null)
{
var max = MaxTxFee.GetFee(txSize);
if(fees > max)
Money max = this.MaxTxFee.GetFee(txSize);
if (fees > max)
errors.Add(new FeeTooHighPolicyError(fees, max));
}
if(MinRelayTxFee != null)
if (this.MinRelayTxFee != null)
{
if(MinRelayTxFee != null)
if (this.MinRelayTxFee != null)
{
var min = MinRelayTxFee.GetFee(txSize);
if(fees < min)
Money min = this.MinRelayTxFee.GetFee(txSize);
if (fees < min)
errors.Add(new FeeTooLowPolicyError(fees, min));
}
}
}
}
if(MinRelayTxFee != null)
if (this.MinRelayTxFee != null)
{
foreach(var output in transaction.Outputs)
foreach (TxOut output in transaction.Outputs)
{
var bytes = output.ScriptPubKey.ToBytes(true);
if(output.IsDust(MinRelayTxFee) && !IsOpReturn(bytes))
errors.Add(new DustPolicyError(output.Value, output.GetDustThreshold(MinRelayTxFee)));
byte[] bytes = output.ScriptPubKey.ToBytes(true);
if (output.IsDust(this.MinRelayTxFee) && !IsOpReturn(bytes))
errors.Add(new DustPolicyError(output.Value, output.GetDustThreshold(this.MinRelayTxFee)));
}
}
var opReturnCount = transaction.Outputs.Select(o => o.ScriptPubKey.ToBytes(true)).Count(b => IsOpReturn(b));
if(opReturnCount > 1)
int opReturnCount = transaction.Outputs.Select(o => o.ScriptPubKey.ToBytes(true)).Count(b => IsOpReturn(b));
if (opReturnCount > 1)
errors.Add(new TransactionPolicyError("More than one op return detected"));
return errors.ToArray();
}
......@@ -179,17 +179,14 @@ namespace NBitcoin.Policy
private bool VerifyScript(IndexedTxIn input, Script scriptPubKey, Money value, ScriptVerify scriptVerify, out ScriptError error)
{
#if !NOCONSENSUSLIB
if(!UseConsensusLib)
#endif
if (!this.UseConsensusLib)
return input.VerifyScript(this.network, scriptPubKey, value, scriptVerify, out error);
#if !NOCONSENSUSLIB
else
{
var ok = Script.VerifyScriptConsensus(scriptPubKey, input.Transaction, input.Index, scriptVerify);
if(!ok)
bool ok = Script.VerifyScriptConsensus(scriptPubKey, input.Transaction, input.Index, scriptVerify);
if (!ok)
{
if(input.VerifyScript(this.network, scriptPubKey, scriptVerify, out error))
if (input.VerifyScript(this.network, scriptPubKey, scriptVerify, out error))
error = ScriptError.UnknownError;
return false;
}
......@@ -199,7 +196,6 @@ namespace NBitcoin.Policy
}
return true;
}
#endif
}
#endregion
......@@ -208,16 +204,14 @@ namespace NBitcoin.Policy
{
return new StandardTransactionPolicy(this.network)
{
MaxTransactionSize = MaxTransactionSize,
MaxTxFee = MaxTxFee,
MinRelayTxFee = MinRelayTxFee,
ScriptVerify = ScriptVerify,
#if !NOCONSENSUSLIB
UseConsensusLib = UseConsensusLib,
#endif
CheckMalleabilitySafe = CheckMalleabilitySafe,
CheckScriptPubKey = CheckScriptPubKey,
CheckFee = CheckFee
MaxTransactionSize = this.MaxTransactionSize,
MaxTxFee = this.MaxTxFee,
MinRelayTxFee = this.MinRelayTxFee,
ScriptVerify = this.ScriptVerify,
UseConsensusLib = this.UseConsensusLib,
CheckMalleabilitySafe = this.CheckMalleabilitySafe,
CheckScriptPubKey = this.CheckScriptPubKey,
CheckFee = this.CheckFee
};
}
......
#if !NOHTTPCLIENT
using System;
using System;
using System.Net.Http;
using System.Threading.Tasks;
......@@ -7,14 +6,8 @@ namespace NBitcoin
{
public class QBitNinjaTransactionRepository : ITransactionRepository
{
private readonly Uri _BaseUri;
public Uri BaseUri
{
get
{
return _BaseUri;
}
}
public readonly Uri BaseUri;
private readonly Network network;
/// <summary>
/// Use qbitninja public servers
......@@ -22,38 +15,35 @@ namespace NBitcoin
/// <param name="network"></param>
public QBitNinjaTransactionRepository(Network network)
{
if(network == null)
throw new ArgumentNullException("network");
_BaseUri = new Uri("http://" + (network == Network.Main ? "" : "t") + "api.qbit.ninja/");
this.network = network ?? throw new ArgumentNullException("network");
this.BaseUri = new Uri("http://" + (network == Network.Main ? "" : "t") + "api.qbit.ninja/");
}
public QBitNinjaTransactionRepository(Uri baseUri)
: this(baseUri.AbsoluteUri)
{
}
public QBitNinjaTransactionRepository(string baseUri)
{
if(!baseUri.EndsWith("/"))
baseUri += "/";
_BaseUri = new Uri(baseUri, UriKind.Absolute);
}
#region ITransactionRepository Members
this.BaseUri = new Uri(baseUri, UriKind.Absolute);
}
public async Task<Transaction> GetAsync(uint256 txId)
{
using(HttpClient client = new HttpClient())
using(var client = new HttpClient())
{
var tx = await client.GetAsync(BaseUri.AbsoluteUri + "transactions/" + txId + "?format=raw").ConfigureAwait(false);
HttpResponseMessage tx = await client.GetAsync(this.BaseUri.AbsoluteUri + "transactions/" + txId + "?format=raw").ConfigureAwait(false);
if(tx.StatusCode == System.Net.HttpStatusCode.NotFound)
return null;
tx.EnsureSuccessStatusCode();
var bytes = await tx.Content.ReadAsByteArrayAsync().ConfigureAwait(false);
return new Transaction(bytes);
byte[] bytes = await tx.Content.ReadAsByteArrayAsync().ConfigureAwait(false);
return this.network.CreateTransaction(bytes);
}
}
......@@ -61,8 +51,5 @@ namespace NBitcoin
{
return Task.FromResult(false);
}
#endregion
}
}
#endif
\ No newline at end of file
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Text;
namespace NBitcoin.RPC
{
public class AddressGrouping
{
public AddressGrouping()
{
this.ChangeAddresses = new List<ChangeAddress>();
}
public BitcoinAddress PublicAddress { get; set; }
public Money Amount { get; set; }
public string Account { get; set; }
public List<ChangeAddress> ChangeAddresses { get; set; }
}
}
......@@ -5,8 +5,12 @@ using Newtonsoft.Json.Linq;
namespace NBitcoin.RPC
{
class BlockExplorerFormatter : RawFormatter
internal class BlockExplorerFormatter : RawFormatter
{
internal BlockExplorerFormatter(Network network) : base(network)
{
}
protected override void BuildTransaction(JObject json, Transaction tx)
{
tx.Version = (uint)json.GetValue("ver");
......@@ -14,7 +18,7 @@ namespace NBitcoin.RPC
var vin = (JArray)json.GetValue("in");
int vinCount = (int)json.GetValue("vin_sz");
for(int i = 0; i < vinCount; i++)
for (int i = 0; i < vinCount; i++)
{
var jsonIn = (JObject)vin[i];
var txin = new TxIn();
......@@ -25,19 +29,19 @@ namespace NBitcoin.RPC
txin.PrevOut.N = (uint)prevout.GetValue("n");
var script = (string)jsonIn.GetValue("scriptSig");
if(script != null)
string script = (string)jsonIn.GetValue("scriptSig");
if (script != null)
{
txin.ScriptSig = new Script(script);
}
else
{
var coinbase = (string)jsonIn.GetValue("coinbase");
string coinbase = (string)jsonIn.GetValue("coinbase");
txin.ScriptSig = new Script(Encoders.Hex.DecodeData(coinbase));
}
var seq = jsonIn.GetValue("sequence");
if(seq != null)
JToken seq = jsonIn.GetValue("sequence");
if (seq != null)
{
txin.Sequence = (uint)seq;
}
......@@ -45,10 +49,10 @@ namespace NBitcoin.RPC
var vout = (JArray)json.GetValue("out");
int voutCount = (int)json.GetValue("vout_sz");
for(int i = 0; i < voutCount; i++)
for (int i = 0; i < voutCount; i++)
{
var jsonOut = (JObject)vout[i];
var txout = new NBitcoin.TxOut();
var txout = new TxOut();
tx.Outputs.Add(txout);
txout.Value = Money.Parse((string)jsonOut.GetValue("value"));
......@@ -70,9 +74,9 @@ namespace NBitcoin.RPC
writer.WritePropertyName("in");
writer.WriteStartArray();
foreach(var input in tx.Inputs.AsIndexedInputs())
foreach (IndexedTxIn input in tx.Inputs.AsIndexedInputs())
{
var txin = input.TxIn;
TxIn txin = input.TxIn;
writer.WriteStartObject();
writer.WritePropertyName("prev_out");
writer.WriteStartObject();
......@@ -80,7 +84,7 @@ namespace NBitcoin.RPC
WritePropertyValue(writer, "n", txin.PrevOut.N);
writer.WriteEndObject();
if(txin.PrevOut.Hash == uint256.Zero)
if (txin.PrevOut.Hash == uint256.Zero)
{
WritePropertyValue(writer, "coinbase", Encoders.Hex.EncodeData(txin.ScriptSig.ToBytes()));
}
......@@ -88,11 +92,11 @@ namespace NBitcoin.RPC
{
WritePropertyValue(writer, "scriptSig", txin.ScriptSig.ToString());
}
if(input.WitScript != WitScript.Empty)
if (input.WitScript != WitScript.Empty)
{
WritePropertyValue(writer, "witness", input.WitScript.ToString());
}
if(txin.Sequence != uint.MaxValue)
if (txin.Sequence != uint.MaxValue)
{
WritePropertyValue(writer, "sequence", (uint)txin.Sequence);
}
......@@ -102,7 +106,7 @@ namespace NBitcoin.RPC
writer.WritePropertyName("out");
writer.WriteStartArray();
foreach(var txout in tx.Outputs)
foreach (TxOut txout in tx.Outputs)
{
writer.WriteStartObject();
WritePropertyValue(writer, "value", txout.Value.ToString(false, false));
......
using System;
using System.Collections.Generic;
using System.Text;
namespace NBitcoin.RPC
{
public class ChangeAddress
{
public Money Amount { get; set; }
public BitcoinAddress Address { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace NBitcoin.RPC
{
public class RPCAccount
{
public Money Amount { get; set; }
public string AccountName { get; set; }
}
}
......@@ -8,31 +8,6 @@ using Newtonsoft.Json.Linq;
namespace NBitcoin.RPC
{
public class RPCAccount
{
public Money Amount { get; set; }
public string AccountName { get; set; }
}
public class ChangeAddress
{
public Money Amount { get; set; }
public BitcoinAddress Address { get; set; }
}
public class AddressGrouping
{
public AddressGrouping()
{
this.ChangeAddresses = new List<ChangeAddress>();
}
public BitcoinAddress PublicAddress { get; set; }
public Money Amount { get; set; }
public string Account { get; set; }
public List<ChangeAddress> ChangeAddresses { get; set; }
}
/*
Category Name Implemented
------------------ --------------------------- -----------------------
......@@ -214,8 +189,8 @@ namespace NBitcoin.RPC
if (options.SubtractFeeFromOutputs != null)
{
JArray array = new JArray();
foreach(var v in options.SubtractFeeFromOutputs)
var array = new JArray();
foreach (int v in options.SubtractFeeFromOutputs)
{
array.Add(new JValue(v));
}
......@@ -232,7 +207,7 @@ namespace NBitcoin.RPC
var r = (JObject)response.Result;
return new FundRawTransactionResponse()
{
Transaction = new Transaction(r["hex"].Value<string>()),
Transaction = this.network.CreateTransaction(r["hex"].Value<string>()),
Fee = Money.Coins(r["fee"].Value<decimal>()),
ChangePos = r["changepos"].Value<int>()
};
......@@ -246,7 +221,7 @@ namespace NBitcoin.RPC
return tx.ToHex();
// if there is, do this ACK so that NBitcoin does not change the version number
return Encoders.Hex.EncodeData(tx.ToBytes(NBitcoin.Protocol.ProtocolVersion.WITNESS_VERSION - 1));
return Encoders.Hex.EncodeData(tx.ToBytes(version: NBitcoin.Protocol.ProtocolVersion.WITNESS_VERSION - 1));
}
// getreceivedbyaddress
......@@ -587,7 +562,7 @@ namespace NBitcoin.RPC
parameters.Add(timeout);
await SendCommandAsync(RPCOperations.walletpassphrase, parameters.ToArray()).ConfigureAwait(false);
}
/// <summary>
/// Sign a transaction
/// </summary>
......@@ -609,7 +584,7 @@ namespace NBitcoin.RPC
public async Task<Transaction> SignRawTransactionAsync(Transaction tx)
{
RPCResponse result = await SendCommandAsync(RPCOperations.signrawtransaction, tx.ToHex()).ConfigureAwait(false);
return new Transaction(result.Result["hex"].Value<string>());
return this.network.CreateTransaction(result.Result["hex"].Value<string>());
}
}
}
......
......@@ -6,13 +6,13 @@ using Newtonsoft.Json.Linq;
namespace NBitcoin.RPC
{
abstract class RawFormatter
internal abstract class RawFormatter
{
public Network Network { get; set; }
protected RawFormatter()
protected RawFormatter(Network network)
{
this.Network = Network.Main;
this.Network = network;
}
protected abstract void BuildTransaction(JObject json, Transaction tx);
......@@ -34,7 +34,7 @@ namespace NBitcoin.RPC
public Transaction Parse(JObject obj)
{
Transaction tx = new Transaction();
var tx = new Transaction();
BuildTransaction(obj, tx);
return tx;
}
......@@ -44,7 +44,7 @@ namespace NBitcoin.RPC
writer.WritePropertyName(name);
writer.WriteValue(value);
}
public string ToString(Transaction transaction)
{
var strWriter = new StringWriter();
......@@ -57,7 +57,7 @@ namespace NBitcoin.RPC
jsonWriter.Flush();
return strWriter.ToString();
}
}
}
}
#endif
\ No newline at end of file
......@@ -27,44 +27,17 @@ namespace NBitcoin.RPC
private readonly Uri address;
private readonly Network network;
/// <summary>
/// Gets the <see cref="Network"/> instance for the client.
/// </summary>
public Network Network
{
get
{
return network;
}
}
/// <summary>
/// Initializes a new instance of the <see cref="RestClient"/> class.
/// </summary>
/// <param name="address">The rest API endpoint</param>
/// <exception cref="System.ArgumentNullException">Null rest API endpoint</exception>
/// <exception cref="System.ArgumentException">Invalid value for RestResponseFormat</exception>
public RestClient(Uri address)
: this(address, Network.Main)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="RestClient"/> class.
/// </summary>
/// <param name="address">The rest API endpoint</param>
/// <param name="network">The network to operate with</param>
/// <exception cref="System.ArgumentNullException">Null rest API endpoint</exception>
/// <exception cref="System.ArgumentException">Invalid value for RestResponseFormat</exception>
/// <exception cref="ArgumentNullException">Null rest API endpoint</exception>
/// <exception cref="ArgumentException">Invalid value for RestResponseFormat</exception>
public RestClient(Uri address, Network network)
{
if (address == null)
throw new ArgumentNullException("address");
if (network == null)
throw new ArgumentNullException("network");
this.address = address;
this.network = network;
this.address = address ?? throw new ArgumentNullException(nameof(address));
this.network = network ?? throw new ArgumentNullException(nameof(network));
}
/// <summary>
......@@ -72,14 +45,14 @@ namespace NBitcoin.RPC
/// </summary>
/// <param name="blockId">The block identifier.</param>
/// <returns>Given a block hash (id) returns the requested block object.</returns>
/// <exception cref="System.ArgumentNullException">blockId cannot be null.</exception>
/// <exception cref="ArgumentNullException">blockId cannot be null.</exception>
public async Task<Block> GetBlockAsync(uint256 blockId)
{
if (blockId == null)
throw new ArgumentNullException("blockId");
byte[] result = await SendRequestAsync("block", RestResponseFormat.Bin, blockId.ToString()).ConfigureAwait(false);
return new Block(result);
return Block.Load(result, this.network);
}
/// <summary>
......@@ -87,7 +60,7 @@ namespace NBitcoin.RPC
/// </summary>
/// <param name="blockId">The block identifier.</param>
/// <returns>Given a block hash (id) returns the requested block object.</returns>
/// <exception cref="System.ArgumentNullException">blockId cannot be null.</exception>
/// <exception cref="ArgumentNullException">blockId cannot be null.</exception>
public Block GetBlock(uint256 blockId)
{
return GetBlockAsync(blockId).GetAwaiter().GetResult();
......@@ -105,7 +78,7 @@ namespace NBitcoin.RPC
throw new ArgumentNullException("txId");
byte[] result = await SendRequestAsync("tx", RestResponseFormat.Bin, txId.ToString()).ConfigureAwait(false);
return new Transaction(result);
return this.network.CreateTransaction(result);
}
/// <summary>
......@@ -150,8 +123,8 @@ namespace NBitcoin.RPC
/// <param name="blockId">The initial block identifier.</param>
/// <param name="count">how many headers to get.</param>
/// <returns>Given a block hash (blockId) returns as much block headers as specified.</returns>
/// <exception cref="System.ArgumentNullException">blockId cannot be null</exception>
/// <exception cref="System.ArgumentOutOfRangeException">count must be greater or equal to one.</exception>
/// <exception cref="ArgumentNullException">blockId cannot be null</exception>
/// <exception cref="ArgumentOutOfRangeException">count must be greater or equal to one.</exception>
public IEnumerable<BlockHeader> GetBlockHeaders(uint256 blockId, int count)
{
return GetBlockHeadersAsync(blockId, count).GetAwaiter().GetResult();
......@@ -164,7 +137,7 @@ namespace NBitcoin.RPC
public async Task<ChainInfo> GetChainInfoAsync()
{
byte[] result = await SendRequestAsync("chaininfo", RestResponseFormat.Json).ConfigureAwait(false);
var o = JObject.Parse(Encoding.UTF8.GetString(result, 0, result.Length));
JObject o = JObject.Parse(Encoding.UTF8.GetString(result, 0, result.Length));
return new ChainInfo
{
......@@ -185,7 +158,7 @@ namespace NBitcoin.RPC
/// <param name="outPoints">The out points identifiers (TxIn-N).</param>
/// <param name="checkMempool">if set to <c>true</c> [check mempool].</param>
/// <returns>The unspent transaction outputs (UTXO) for the given outPoints.</returns>
/// <exception cref="System.ArgumentNullException">outPoints cannot be null.</exception>
/// <exception cref="ArgumentNullException">outPoints cannot be null.</exception>
public async Task<UTxOutputs> GetUnspentOutputsAsync(IEnumerable<OutPoint> outPoints, bool checkMempool)
{
if (outPoints == null)
......@@ -216,11 +189,11 @@ namespace NBitcoin.RPC
byte[] result = await SendRequestAsync($"gettxout/{txid.ToString()}/{vout.ToString() + (includeMemPool ? "/includemempool" : "")}",
RestResponseFormat.Json).ConfigureAwait(false);
var responseString = Encoding.UTF8.GetString(result, 0, result.Length);
string responseString = Encoding.UTF8.GetString(result, 0, result.Length);
if (string.IsNullOrEmpty(responseString))
return null;
var objectResult = JObject.Parse(responseString);
JObject objectResult = JObject.Parse(responseString);
return new UnspentTransaction(objectResult);
}
......@@ -232,23 +205,22 @@ namespace NBitcoin.RPC
using (WebResponse response = await GetWebResponseAsync(request).ConfigureAwait(false))
{
Stream stream = response.GetResponseStream();
var bytesToRead = (int)response.ContentLength;
int bytesToRead = (int)response.ContentLength;
byte[] buffer = await stream.ReadBytesAsync(bytesToRead).ConfigureAwait(false);
return buffer;
}
}
#region Private methods
private WebRequest BuildHttpRequest(string resource, RestResponseFormat format, params string[] parms)
{
var hasParams = parms != null && parms.Length > 0;
bool hasParams = parms != null && parms.Length > 0;
var uriBuilder = new UriBuilder(this.address);
uriBuilder.Path = "rest/" + resource + (hasParams ? "/" : "") + string.Join("/", parms) + "." + format.ToString().ToLowerInvariant();
HttpWebRequest request = WebRequest.CreateHttp(uriBuilder.Uri);
request.Method = "GET";
#if !(PORTABLE || NETCORE)
#if !NETCORE
request.KeepAlive = false;
#endif
return request;
......@@ -263,10 +235,8 @@ namespace NBitcoin.RPC
{
response = await request.GetResponseAsync().ConfigureAwait(false);
}
catch(WebException ex)
catch (WebException ex)
{
// "WebException status: {0}", ex.Status);
// Even if the request "failed" we need to continue reading the response from the router
response = ex.Response as HttpWebResponse;
......@@ -279,14 +249,13 @@ namespace NBitcoin.RPC
if (exception != null)
{
Stream stream = response.GetResponseStream();
var bytesToRead = (int)response.ContentLength;
int bytesToRead = (int)response.ContentLength;
byte[] buffer = await stream.ReadBytesAsync(bytesToRead).ConfigureAwait(false);
response.Dispose();
throw new RestApiException(Encoding.UTF8.GetString(buffer, 0, buffer.Length - 2), exception);
}
return response;
}
#endregion
}
public class RestApiException : Exception
......
......@@ -8,13 +8,10 @@ using Newtonsoft.Json.Linq;
namespace NBitcoin.RPC
{
class SatoshiFormatter : RawFormatter
internal class SatoshiFormatter : RawFormatter
{
private readonly Network network;
public SatoshiFormatter(Network network)
public SatoshiFormatter(Network network) : base(network)
{
this.network = network;
}
protected override void BuildTransaction(JObject json, Transaction tx)
......@@ -23,14 +20,14 @@ namespace NBitcoin.RPC
tx.LockTime = (uint)json.GetValue("locktime");
var vin = (JArray)json.GetValue("vin");
for(int i = 0; i < vin.Count; i++)
for (int i = 0; i < vin.Count; i++)
{
var jsonIn = (JObject)vin[i];
var txin = new TxIn();
tx.Inputs.Add(txin);
var script = (JObject)jsonIn.GetValue("scriptSig");
if(script != null)
if (script != null)
{
txin.ScriptSig = new Script(Encoders.Hex.DecodeData((string)script.GetValue("hex")));
txin.PrevOut.Hash = uint256.Parse((string)jsonIn.GetValue("txid"));
......@@ -38,7 +35,7 @@ namespace NBitcoin.RPC
}
else
{
var coinbase = (string)jsonIn.GetValue("coinbase");
string coinbase = (string)jsonIn.GetValue("coinbase");
txin.ScriptSig = new Script(Encoders.Hex.DecodeData(coinbase));
}
......@@ -47,14 +44,14 @@ namespace NBitcoin.RPC
}
var vout = (JArray)json.GetValue("vout");
for(int i = 0; i < vout.Count; i++)
for (int i = 0; i < vout.Count; i++)
{
var jsonOut = (JObject)vout[i];
var txout = new TxOut();
tx.Outputs.Add(txout);
var btc = (decimal)jsonOut.GetValue("value");
var satoshis = btc * Money.COIN;
decimal btc = (decimal)jsonOut.GetValue("value");
decimal satoshis = btc * Money.COIN;
txout.Value = new Money((long)(satoshis));
var script = (JObject)jsonOut.GetValue("scriptPubKey");
......@@ -70,11 +67,11 @@ namespace NBitcoin.RPC
writer.WritePropertyName("vin");
writer.WriteStartArray();
foreach(var txin in tx.Inputs)
foreach (TxIn txin in tx.Inputs)
{
writer.WriteStartObject();
if(txin.PrevOut.Hash == uint256.Zero)
if (txin.PrevOut.Hash == uint256.Zero)
{
WritePropertyValue(writer, "coinbase", Encoders.Hex.EncodeData(txin.ScriptSig.ToBytes()));
}
......@@ -99,7 +96,7 @@ namespace NBitcoin.RPC
writer.WriteStartArray();
int i = 0;
foreach(var txout in tx.Outputs)
foreach (TxOut txout in tx.Outputs)
{
writer.WriteStartObject();
writer.WritePropertyName("value");
......@@ -112,28 +109,28 @@ namespace NBitcoin.RPC
WritePropertyValue(writer, "asm", txout.ScriptPubKey.ToString());
WritePropertyValue(writer, "hex", Encoders.Hex.EncodeData(txout.ScriptPubKey.ToBytes()));
var destinations = new List<TxDestination>() { txout.ScriptPubKey.GetDestination(this.network) };
if(destinations[0] == null)
var destinations = new List<TxDestination>() { txout.ScriptPubKey.GetDestination(this.Network) };
if (destinations[0] == null)
{
destinations = txout.ScriptPubKey.GetDestinationPublicKeys(this.network)
destinations = txout.ScriptPubKey.GetDestinationPublicKeys(this.Network)
.Select(p => p.Hash)
.ToList<TxDestination>();
}
if(destinations.Count == 1)
if (destinations.Count == 1)
{
WritePropertyValue(writer, "reqSigs", 1);
WritePropertyValue(writer, "type", GetScriptType(txout.ScriptPubKey.FindTemplate(this.network)));
WritePropertyValue(writer, "type", GetScriptType(txout.ScriptPubKey.FindTemplate(this.Network)));
writer.WritePropertyName("addresses");
writer.WriteStartArray();
writer.WriteValue(destinations[0].GetAddress(Network).ToString());
writer.WriteValue(destinations[0].GetAddress(this.Network).ToString());
writer.WriteEndArray();
}
else
{
var multi = PayToMultiSigTemplate.Instance.ExtractScriptPubKeyParameters(this.network, txout.ScriptPubKey);
PayToMultiSigTemplateParameters multi = PayToMultiSigTemplate.Instance.ExtractScriptPubKeyParameters(txout.ScriptPubKey);
if (multi != null)
WritePropertyValue(writer, "reqSigs", multi.SignatureCount);
WritePropertyValue(writer, "type", GetScriptType(txout.ScriptPubKey.FindTemplate(this.network)));
WritePropertyValue(writer, "type", GetScriptType(txout.ScriptPubKey.FindTemplate(this.Network)));
if (multi != null)
{
writer.WritePropertyName("addresses");
......@@ -155,20 +152,20 @@ namespace NBitcoin.RPC
private string ValueFromAmount(Money money)
{
var satoshis = (decimal)money.Satoshi;
var btc = satoshis / Money.COIN;
decimal satoshis = (decimal)money.Satoshi;
decimal btc = satoshis / Money.COIN;
//return btc.ToString("0.###E+00", CultureInfo.InvariantCulture);
var result = ((double)btc).ToString(CultureInfo.InvariantCulture);
if(!result.ToCharArray().Contains('.'))
string result = ((double)btc).ToString(CultureInfo.InvariantCulture);
if (!result.ToCharArray().Contains('.'))
result = result + ".0";
return result;
}
private string GetScriptType(ScriptTemplate template)
{
if(template == null)
if (template == null)
return "nonstandard";
switch(template.Type)
switch (template.Type)
{
case TxOutType.TX_PUBKEY:
return "pubkey";
......
......@@ -326,7 +326,7 @@ namespace NBitcoin
{
public ScriptSigs()
{
WitSig = WitScript.Empty;
this.WitSig = WitScript.Empty;
}
public Script ScriptSig
{
......@@ -342,7 +342,7 @@ namespace NBitcoin
public class Script
{
static readonly Script _Empty = new Script();
private static readonly Script _Empty = new Script();
public static Script Empty
{
get
......@@ -363,23 +363,24 @@ namespace NBitcoin
public Script(IEnumerable<Op> ops)
{
MemoryStream ms = new MemoryStream();
foreach(var op in ops)
var ms = new MemoryStream();
foreach(Op op in ops)
{
op.WriteTo(ms);
}
_Script = ms.ToArray();
this._Script = ms.ToArray();
}
public Script(string script)
{
_Script = Parse(script);
this._Script = Parse(script);
}
private static byte[] Parse(string script)
{
var reader = new StringReader(script.Trim());
MemoryStream result = new MemoryStream();
var result = new MemoryStream();
while(reader.Peek() != -1)
{
Op.Read(reader).WriteTo(result);
......@@ -397,26 +398,25 @@ namespace NBitcoin
{
}
private Script(byte[] data, bool @unsafe, bool unused)
{
_Script = @unsafe ? data : data.ToArray();
this._Script = @unsafe ? data : data.ToArray();
}
public Script(IEnumerable<byte> data)
{
_Script = data.ToArray();
this._Script = data.ToArray();
}
public Script(byte[] data, bool compressed)
{
if(!compressed)
_Script = data.ToArray();
this._Script = data.ToArray();
else
{
ScriptCompressor compressor = new ScriptCompressor();
var compressor = new ScriptCompressor();
compressor.ReadWrite(data);
_Script = compressor.GetScript()._Script;
this._Script = compressor.GetScript()._Script;
}
}
......@@ -424,7 +424,7 @@ namespace NBitcoin
{
get
{
return _Script.Length;
return this._Script.Length;
}
}
......@@ -439,9 +439,9 @@ namespace NBitcoin
return this;
if(codeSeparatorIndex < -1)
throw new ArgumentOutOfRangeException("codeSeparatorIndex");
var separatorIndex = -1;
List<Op> ops = new List<Op>();
foreach(var op in ToOps())
int separatorIndex = -1;
var ops = new List<Op>();
foreach(Op op in ToOps())
{
if(op.Code == OpcodeType.OP_CODESEPARATOR)
separatorIndex++;
......@@ -456,7 +456,7 @@ namespace NBitcoin
public ScriptReader CreateReader()
{
return new ScriptReader(_Script);
return new ScriptReader(this._Script);
}
......@@ -476,7 +476,7 @@ namespace NBitcoin
{
if(pushedData.Length == 0)
return 0;
var standardOp = Op.GetPushOp(pushedData);
Op standardOp = Op.GetPushOp(pushedData);
return FindAndDelete(op =>
op.Code == standardOp.Code &&
op.PushData != null && Utils.ArrayEqual(op.PushData, pushedData));
......@@ -484,10 +484,10 @@ namespace NBitcoin
internal int FindAndDelete(Func<Op, bool> predicate)
{
int nFound = 0;
List<Op> operations = new List<Op>();
foreach(var op in ToOps())
var operations = new List<Op>();
foreach(Op op in ToOps())
{
var shouldDelete = predicate(op);
bool shouldDelete = predicate(op);
if(!shouldDelete)
{
operations.Add(op);
......@@ -497,16 +497,16 @@ namespace NBitcoin
}
if(nFound == 0)
return 0;
_Script = new Script(operations)._Script;
this._Script = new Script(operations)._Script;
return nFound;
}
public string ToHex()
{
return Encoders.Hex.EncodeData(_Script);
return Encoders.Hex.EncodeData(this._Script);
}
Script _PaymentScript;
private Script _PaymentScript;
/// <summary>
/// Get the P2SH scriptPubKey of this script
......@@ -515,7 +515,7 @@ namespace NBitcoin
{
get
{
return _PaymentScript ?? (_PaymentScript = PayToScriptHashTemplate.Instance.GenerateScriptPubKey(Hash));
return this._PaymentScript ?? (this._PaymentScript = PayToScriptHashTemplate.Instance.GenerateScriptPubKey(this.Hash));
}
}
......@@ -525,7 +525,7 @@ namespace NBitcoin
/// </summary>
public bool IsWitness(Network network)
{
return PayToWitTemplate.Instance.CheckScriptPubKey(network, this);
return PayToWitTemplate.Instance.CheckScriptPubKey(this);
}
public override string ToString()
......@@ -533,7 +533,7 @@ namespace NBitcoin
// by default StringBuilder capacity is 16 (too small)
// 300 is enough for P2PKH
var builder = new StringBuilder(300);
using (var reader = new ScriptReader(_Script))
using (var reader = new ScriptReader(this._Script))
{
Op op;
while ((op = reader.Read()) != null)
......@@ -552,7 +552,7 @@ namespace NBitcoin
{
using (ScriptReader reader = CreateReader())
{
foreach (var script in reader.ToEnumerable())
foreach (Op script in reader.ToEnumerable())
{
if (script.PushData == null)
return false;
......@@ -595,7 +595,7 @@ namespace NBitcoin
//https://en.bitcoin.it/wiki/OP_CHECKSIG
public static uint256 SignatureHash(Network network, ICoin coin, Transaction txTo, SigHash nHashType = SigHash.All)
{
var input = txTo.Inputs.AsIndexedInputs().FirstOrDefault(i => i.PrevOut == coin.Outpoint);
IndexedTxIn input = txTo.Inputs.AsIndexedInputs().FirstOrDefault(i => i.PrevOut == coin.Outpoint);
if(input == null)
throw new ArgumentException("coin should be spent spent in txTo", "coin");
return input.GetSignatureHash(network, coin, nHashType);
......@@ -664,9 +664,6 @@ namespace NBitcoin
return GetHash(sss);
}
if(nIn >= txTo.Inputs.Count)
{
Utils.log("ERROR: SignatureHash() : nIn=" + nIn + " out of range\n");
......@@ -688,10 +685,10 @@ namespace NBitcoin
var scriptCopy = new Script(scriptCode._Script);
scriptCopy.FindAndDelete(OpcodeType.OP_CODESEPARATOR);
var txCopy = Transaction.Load(txTo.ToBytes(), network);
Transaction txCopy = network.CreateTransaction(txTo.ToBytes());
//Set all TxIn script to empty string
foreach(var txin in txCopy.Inputs)
foreach(TxIn txin in txCopy.Inputs)
{
txin.ScriptSig = new Script();
}
......@@ -704,7 +701,7 @@ namespace NBitcoin
txCopy.Outputs.Clear();
//All other inputs aside from the current input in txCopy have their nSequence index set to zero
foreach(var input in txCopy.Inputs.Where((x, i) => i != nIn))
foreach(TxIn input in txCopy.Inputs.Where((x, i) => i != nIn))
input.Sequence = 0;
}
else if(hashType == SigHash.Single)
......@@ -712,14 +709,14 @@ namespace NBitcoin
//The output of txCopy is resized to the size of the current input index+1.
txCopy.Outputs.RemoveRange(nIn + 1, txCopy.Outputs.Count - (nIn + 1));
//All other txCopy outputs aside from the output that is the same as the current input index are set to a blank script and a value of (long) -1.
for(var i = 0; i < txCopy.Outputs.Count; i++)
for(int i = 0; i < txCopy.Outputs.Count; i++)
{
if(i == nIn)
continue;
txCopy.Outputs[i] = new TxOut();
}
//All other txCopy inputs aside from the current input are set to have an nSequence index of zero.
foreach(var input in txCopy.Inputs.Where((x, i) => i != nIn))
foreach(TxIn input in txCopy.Inputs.Where((x, i) => i != nIn))
input.Sequence = 0;
}
......@@ -727,7 +724,7 @@ namespace NBitcoin
if((nHashType & SigHash.AnyoneCanPay) != 0)
{
//The txCopy input vector is resized to a length of one.
var script = txCopy.Inputs[nIn];
TxIn script = txCopy.Inputs[nIn];
txCopy.Inputs.Clear();
txCopy.Inputs.Add(script);
//The subScript (lead in by its length as a var-integer encoded!) is set as the first and only member of this vector.
......@@ -736,7 +733,7 @@ namespace NBitcoin
//Serialize TxCopy, append 4 byte hashtypecode
var stream = CreateHashWriter(sigversion);
BitcoinStream stream = CreateHashWriter(sigversion);
txCopy.ReadWrite(stream);
stream.ReadWrite((uint)nHashType);
return GetHash(stream);
......@@ -744,7 +741,7 @@ namespace NBitcoin
private static uint256 GetHash(BitcoinStream stream)
{
var preimage = ((HashStream)stream.Inner).GetHash();
uint256 preimage = ((HashStream)stream.Inner).GetHash();
stream.Inner.Dispose();
return preimage;
}
......@@ -753,7 +750,7 @@ namespace NBitcoin
{
uint256 hashOutputs;
BitcoinStream ss = CreateHashWriter(HashVersion.Witness);
foreach(var txout in txTo.Outputs)
foreach(TxOut txout in txTo.Outputs)
{
ss.ReadWrite(txout);
}
......@@ -765,7 +762,7 @@ namespace NBitcoin
{
uint256 hashSequence;
BitcoinStream ss = CreateHashWriter(HashVersion.Witness);
foreach(var input in txTo.Inputs)
foreach(TxIn input in txTo.Inputs)
{
ss.ReadWrite((uint)input.Sequence);
}
......@@ -777,7 +774,7 @@ namespace NBitcoin
{
uint256 hashPrevouts;
BitcoinStream ss = CreateHashWriter(HashVersion.Witness);
foreach(var input in txTo.Inputs)
foreach(TxIn input in txTo.Inputs)
{
ss.ReadWrite(input.PrevOut);
}
......@@ -787,8 +784,8 @@ namespace NBitcoin
private static BitcoinStream CreateHashWriter(HashVersion version)
{
HashStream hs = new HashStream();
BitcoinStream stream = new BitcoinStream(hs, true);
var hs = new HashStream();
var stream = new BitcoinStream(hs, true);
stream.Type = SerializationType.Hash;
stream.TransactionOptions = version == HashVersion.Original ? TransactionOptions.None : TransactionOptions.Witness;
return stream;
......@@ -812,7 +809,7 @@ namespace NBitcoin
public IEnumerable<Op> ToOps()
{
using (ScriptReader reader = new ScriptReader(_Script))
using (var reader = new ScriptReader(this._Script))
{
return reader.ToEnumerable().ToList();
}
......@@ -822,7 +819,7 @@ namespace NBitcoin
{
uint n = 0;
Op lastOpcode = null;
foreach(var op in ToOps())
foreach(Op op in ToOps())
{
if(op.Code == OpcodeType.OP_CHECKSIG || op.Code == OpcodeType.OP_CHECKSIGVERIFY)
n++;
......@@ -838,53 +835,54 @@ namespace NBitcoin
return n;
}
ScriptId _Hash;
private ScriptId _Hash;
public ScriptId Hash
{
get
{
return _Hash ?? (_Hash = new ScriptId(this));
return this._Hash ?? (this._Hash = new ScriptId(this));
}
}
WitScriptId _WitHash;
private WitScriptId _WitHash;
public WitScriptId WitHash
{
get
{
return _WitHash ?? (_WitHash = new WitScriptId(this));
return this._WitHash ?? (this._WitHash = new WitScriptId(this));
}
}
public BitcoinScriptAddress GetScriptAddress(Network network)
{
return (BitcoinScriptAddress)Hash.GetAddress(network);
return (BitcoinScriptAddress) this.Hash.GetAddress(network);
}
public bool IsPayToScriptHash(Network network)
{
return PayToScriptHashTemplate.Instance.CheckScriptPubKey(network, this);
return PayToScriptHashTemplate.Instance.CheckScriptPubKey(this);
}
public BitcoinWitScriptAddress GetWitScriptAddress(Network network)
{
return (BitcoinWitScriptAddress)WitHash.GetAddress(network);
return (BitcoinWitScriptAddress) this.WitHash.GetAddress(network);
}
public uint GetSigOpCount(Network network, Script scriptSig)
{
if(!this.IsPayToScriptHash(network))
return this.GetSigOpCount(true);
if(!IsPayToScriptHash(network))
return GetSigOpCount(true);
// This is a pay-to-script-hash scriptPubKey;
// get the last item that the scriptSig
// pushes onto the stack:
var validSig = new PayToScriptHashTemplate().CheckScriptSig(network, scriptSig, this);
bool validSig = new PayToScriptHashTemplate().CheckScriptSig(network, scriptSig, this);
return !validSig ? 0 : new Script(scriptSig.ToOps().Last().PushData).GetSigOpCount(true);
// ... and return its opcount:
}
public ScriptTemplate FindTemplate(Network network)
{
return StandardScripts.GetTemplateFromScriptPubKey(network, this);
return StandardScripts.GetTemplateFromScriptPubKey(this);
}
/// <summary>
......@@ -894,7 +892,7 @@ namespace NBitcoin
/// <returns></returns>
public BitcoinAddress GetSignerAddress(Network network)
{
var sig = GetSigner(network);
TxDestination sig = GetSigner(network);
return sig == null ? null : sig.GetAddress(network);
}
......@@ -904,12 +902,12 @@ namespace NBitcoin
/// <returns>The network</returns>
public TxDestination GetSigner(Network network)
{
var pubKey = PayToPubkeyHashTemplate.Instance.ExtractScriptSigParameters(network, this);
PayToPubkeyHashScriptSigParameters pubKey = PayToPubkeyHashTemplate.Instance.ExtractScriptSigParameters(network, this);
if(pubKey != null)
{
return pubKey.PublicKey.Hash;
}
var p2sh = PayToScriptHashTemplate.Instance.ExtractScriptSigParameters(network, this);
PayToScriptHashSigParameters p2sh = PayToScriptHashTemplate.Instance.ExtractScriptSigParameters(network, this);
return p2sh != null ? p2sh.RedeemScript.Hash : null;
}
......@@ -920,7 +918,7 @@ namespace NBitcoin
/// <returns></returns>
public BitcoinAddress GetDestinationAddress(Network network)
{
var dest = GetDestination(network);
TxDestination dest = GetDestination(network);
return dest == null ? null : dest.GetAddress(network);
}
......@@ -931,13 +929,13 @@ namespace NBitcoin
/// <returns></returns>
public TxDestination GetDestination(Network network)
{
var pubKeyHashParams = PayToPubkeyHashTemplate.Instance.ExtractScriptPubKeyParameters(this);
KeyId pubKeyHashParams = PayToPubkeyHashTemplate.Instance.ExtractScriptPubKeyParameters(this);
if(pubKeyHashParams != null)
return pubKeyHashParams;
var scriptHashParams = PayToScriptHashTemplate.Instance.ExtractScriptPubKeyParameters(this);
ScriptId scriptHashParams = PayToScriptHashTemplate.Instance.ExtractScriptPubKeyParameters(this);
if(scriptHashParams != null)
return scriptHashParams;
var wit = PayToWitTemplate.Instance.ExtractScriptPubKeyParameters(network, this);
TxDestination wit = PayToWitTemplate.Instance.ExtractScriptPubKeyParameters(network, this);
return wit;
}
......@@ -948,15 +946,15 @@ namespace NBitcoin
/// <returns></returns>
public PubKey[] GetDestinationPublicKeys(Network network)
{
List<PubKey> result = new List<PubKey>();
var single = PayToPubkeyTemplate.Instance.ExtractScriptPubKeyParameters(this);
var result = new List<PubKey>();
PubKey single = PayToPubkeyTemplate.Instance.ExtractScriptPubKeyParameters(this);
if(single != null)
{
result.Add(single);
}
else
{
var multiSig = PayToMultiSigTemplate.Instance.ExtractScriptPubKeyParameters(network, this);
PayToMultiSigTemplateParameters multiSig = PayToMultiSigTemplate.Instance.ExtractScriptPubKeyParameters(this);
if(multiSig != null)
{
result.AddRange(multiSig.PubKeys);
......@@ -992,7 +990,7 @@ namespace NBitcoin
[Obsolete("Use ToBytes instead")]
public byte[] ToRawScript(bool @unsafe)
{
return @unsafe ? _Script : _Script.ToArray();
return @unsafe ? this._Script : this._Script.ToArray();
}
/// <summary>
......@@ -1002,7 +1000,7 @@ namespace NBitcoin
/// <returns></returns>
public byte[] ToBytes(bool @unsafe)
{
return @unsafe ? _Script : _Script.ToArray();
return @unsafe ? this._Script : this._Script.ToArray();
}
public byte[] ToCompressedBytes()
......@@ -1028,19 +1026,19 @@ namespace NBitcoin
public static bool VerifyScript(Network network, Script scriptPubKey, Transaction tx, int i, Money value, ScriptVerify scriptVerify = ScriptVerify.Standard, SigHash sigHash = SigHash.Undefined)
{
var scriptSig = tx.Inputs[i].ScriptSig;
Script scriptSig = tx.Inputs[i].ScriptSig;
return VerifyScript(network, scriptSig, scriptPubKey, tx, i, value, scriptVerify, sigHash, out ScriptError unused);
}
public static bool VerifyScript(Network network, Script scriptPubKey, Transaction tx, int i, Money value, out ScriptError error)
{
var scriptSig = tx.Inputs[i].ScriptSig;
Script scriptSig = tx.Inputs[i].ScriptSig;
return VerifyScript(network, scriptSig, scriptPubKey, tx, i, value, ScriptVerify.Standard, SigHash.Undefined, out error);
}
public static bool VerifyScript(Network network, Script scriptPubKey, Transaction tx, int i, Money value, ScriptVerify scriptVerify, SigHash sigHash, out ScriptError error)
{
var scriptSig = tx.Inputs[i].ScriptSig;
Script scriptSig = tx.Inputs[i].ScriptSig;
return VerifyScript(network, scriptSig, scriptPubKey, tx, i, value, scriptVerify, sigHash, out error);
}
......@@ -1051,13 +1049,11 @@ namespace NBitcoin
SigHash = sigHash,
ScriptVerify = scriptVerify
};
var result = eval.VerifyScript(scriptSig, scriptPubKey, tx, i, value);
bool result = eval.VerifyScript(scriptSig, scriptPubKey, tx, i, value);
error = eval.Error;
return result;
}
#if !NOCONSENSUSLIB
public const string LibConsensusDll = "libbitcoinconsensus-0.dll";
public enum BitcoinConsensusError
{
......@@ -1089,28 +1085,27 @@ namespace NBitcoin
public static bool VerifyScriptConsensus(Script scriptPubKey, Transaction tx, uint nIn, ScriptVerify flags, out BitcoinConsensusError err)
{
var scriptPubKeyBytes = scriptPubKey.ToBytes();
var txToBytes = tx.ToBytes();
byte[] scriptPubKeyBytes = scriptPubKey.ToBytes();
byte[] txToBytes = tx.ToBytes();
err = BitcoinConsensusError.ERR_OK;
var valid = VerifyScriptConsensus(scriptPubKeyBytes, (uint)scriptPubKeyBytes.Length, txToBytes, (uint)txToBytes.Length, nIn, flags, ref err);
int valid = VerifyScriptConsensus(scriptPubKeyBytes, (uint)scriptPubKeyBytes.Length, txToBytes, (uint)txToBytes.Length, nIn, flags, ref err);
return valid == 1;
}
public static bool VerifyScriptConsensus(Script scriptPubKey, Transaction tx, uint nIn, Money amount, ScriptVerify flags, out BitcoinConsensusError err)
{
var scriptPubKeyBytes = scriptPubKey.ToBytes();
var txToBytes = tx.ToBytes();
byte[] scriptPubKeyBytes = scriptPubKey.ToBytes();
byte[] txToBytes = tx.ToBytes();
err = BitcoinConsensusError.ERR_OK;
var valid = VerifyScriptConsensusWithAmount(scriptPubKeyBytes, (uint)scriptPubKeyBytes.Length, amount.Satoshi, txToBytes, (uint)txToBytes.Length, nIn, flags, ref err);
int valid = VerifyScriptConsensusWithAmount(scriptPubKeyBytes, (uint)scriptPubKeyBytes.Length, amount.Satoshi, txToBytes, (uint)txToBytes.Length, nIn, flags, ref err);
return valid == 1;
}
#endif
public bool IsUnspendable
{
get
{
return _Script.Length > 0 && _Script[0] == (byte)OpcodeType.OP_RETURN;
return this._Script.Length > 0 && this._Script[0] == (byte)OpcodeType.OP_RETURN;
}
}
......@@ -1121,8 +1116,8 @@ namespace NBitcoin
public override bool Equals(object obj)
{
Script item = obj as Script;
return item != null && Utils.ArrayEqual(item._Script, _Script);
var item = obj as Script;
return item != null && Utils.ArrayEqual(item._Script, this._Script);
}
public static bool operator ==(Script a, Script b)
{
......@@ -1140,12 +1135,12 @@ namespace NBitcoin
public override int GetHashCode()
{
return Utils.GetHashCode(_Script);
return Utils.GetHashCode(this._Script);
}
public Script Clone()
{
return new Script(_Script);
return new Script(this._Script);
}
public static Script CombineSignatures(Network network, Script scriptPubKey, Transaction transaction, int n, Script scriptSig1, Script scriptSig2)
......@@ -1163,10 +1158,10 @@ namespace NBitcoin
if(scriptPubKey == null)
scriptPubKey = new Script();
var scriptSig1 = input1.ScriptSig;
var scriptSig2 = input2.ScriptSig;
HashVersion hashVersion = HashVersion.Original;
var isWitness = input1.WitSig != WitScript.Empty || input2.WitSig != WitScript.Empty;
Script scriptSig1 = input1.ScriptSig;
Script scriptSig2 = input2.ScriptSig;
var hashVersion = HashVersion.Original;
bool isWitness = input1.WitSig != WitScript.Empty || input2.WitSig != WitScript.Empty;
if(isWitness)
{
scriptSig1 = input1.WitSig.ToScript();
......@@ -1178,21 +1173,23 @@ namespace NBitcoin
context.ScriptVerify = ScriptVerify.StrictEnc;
context.EvalScript(scriptSig1, checker, hashVersion);
var stack1 = context.Stack.AsInternalArray();
byte[][] stack1 = context.Stack.AsInternalArray();
context = new ScriptEvaluationContext(network);
context.ScriptVerify = ScriptVerify.StrictEnc;
context.EvalScript(scriptSig2, checker, hashVersion);
var stack2 = context.Stack.AsInternalArray();
var result = CombineSignatures(network, scriptPubKey, checker, stack1, stack2, hashVersion);
byte[][] stack2 = context.Stack.AsInternalArray();
Script result = CombineSignatures(network, scriptPubKey, checker, stack1, stack2, hashVersion);
if(result == null)
return scriptSig1.Length < scriptSig2.Length ? input2 : input1;
if(!isWitness)
{
return new ScriptSigs()
{
ScriptSig = result,
WitSig = WitScript.Empty
};
}
else
{
return new ScriptSigs()
......@@ -1205,21 +1202,24 @@ namespace NBitcoin
private static Script CombineSignatures(Network network, Script scriptPubKey, TransactionChecker checker, byte[][] sigs1, byte[][] sigs2, HashVersion hashVersion)
{
var template = StandardScripts.GetTemplateFromScriptPubKey(network, scriptPubKey);
ScriptTemplate template = StandardScripts.GetTemplateFromScriptPubKey(scriptPubKey);
if(template is PayToWitPubKeyHashTemplate)
{
scriptPubKey = new KeyId(scriptPubKey.ToBytes(true).SafeSubarray(1, 20)).ScriptPubKey;
template = StandardScripts.GetTemplateFromScriptPubKey(network, scriptPubKey);
template = StandardScripts.GetTemplateFromScriptPubKey(scriptPubKey);
}
if(template == null || template is TxNullDataTemplate)
return PushAll(Max(sigs1, sigs2));
if(template is PayToPubkeyTemplate || template is PayToPubkeyHashTemplate)
{
if(sigs1.Length == 0 || sigs1[0].Length == 0)
return PushAll(sigs2);
else
return PushAll(sigs1);
}
if(template is PayToScriptHashTemplate || template is PayToWitTemplate)
{
if(sigs1.Length == 0 || sigs1[sigs1.Length - 1].Length == 0)
......@@ -1228,7 +1228,7 @@ namespace NBitcoin
if(sigs2.Length == 0 || sigs2[sigs2.Length - 1].Length == 0)
return PushAll(sigs1);
var redeemBytes = sigs1[sigs1.Length - 1];
byte[] redeemBytes = sigs1[sigs1.Length - 1];
var redeem = new Script(redeemBytes);
sigs1 = sigs1.Take(sigs1.Length - 1).ToArray();
sigs2 = sigs2.Take(sigs2.Length - 1).ToArray();
......@@ -1248,8 +1248,8 @@ namespace NBitcoin
private static Script CombineMultisig(Network network, Script scriptPubKey, TransactionChecker checker, byte[][] sigs1, byte[][] sigs2, HashVersion hashVersion)
{
// Combine all the signatures we've got:
List<TransactionSignature> allsigs = new List<TransactionSignature>();
foreach(var v in sigs1)
var allsigs = new List<TransactionSignature>();
foreach(byte[] v in sigs1)
{
if(TransactionSignature.IsValid(network, v))
{
......@@ -1258,7 +1258,7 @@ namespace NBitcoin
}
foreach(var v in sigs2)
foreach(byte[] v in sigs2)
{
if(TransactionSignature.IsValid(network, v))
{
......@@ -1266,20 +1266,20 @@ namespace NBitcoin
}
}
var multiSigParams = PayToMultiSigTemplate.Instance.ExtractScriptPubKeyParameters(network, scriptPubKey);
PayToMultiSigTemplateParameters multiSigParams = PayToMultiSigTemplate.Instance.ExtractScriptPubKeyParameters(scriptPubKey);
if(multiSigParams == null)
throw new InvalidOperationException("The scriptPubKey is not a valid multi sig");
Dictionary<PubKey, TransactionSignature> sigs = new Dictionary<PubKey, TransactionSignature>();
var sigs = new Dictionary<PubKey, TransactionSignature>();
foreach(var sig in allsigs)
foreach(TransactionSignature sig in allsigs)
{
foreach(var pubkey in multiSigParams.PubKeys)
foreach(PubKey pubkey in multiSigParams.PubKeys)
{
if(sigs.ContainsKey(pubkey))
continue; // Already got a sig for this pubkey
ScriptEvaluationContext eval = new ScriptEvaluationContext(network);
var eval = new ScriptEvaluationContext(network);
if(eval.CheckSig(sig, pubkey, scriptPubKey, checker, hashVersion))
{
sigs.AddOrReplace(pubkey, sig);
......@@ -1290,8 +1290,8 @@ namespace NBitcoin
// Now build a merged CScript:
int nSigsHave = 0;
Script result = new Script(OpcodeType.OP_0); // pop-one-too-many workaround
foreach(var pubkey in multiSigParams.PubKeys)
var result = new Script(OpcodeType.OP_0); // pop-one-too-many workaround
foreach(PubKey pubkey in multiSigParams.PubKeys)
{
if(sigs.ContainsKey(pubkey))
{
......@@ -1311,8 +1311,8 @@ namespace NBitcoin
private static Script PushAll(byte[][] stack)
{
Script s = new Script();
foreach(var push in stack)
var s = new Script();
foreach(byte[] push in stack)
{
s += Op.GetPushOp(push);
}
......
......@@ -4,10 +4,9 @@ using NBitcoin.Policy;
namespace NBitcoin
{
public static class StandardScripts
{
static readonly ScriptTemplate[] _StandardTemplates = new ScriptTemplate[]
private static readonly ScriptTemplate[] _StandardTemplates = new ScriptTemplate[]
{
PayToPubkeyHashTemplate.Instance,
PayToPubkeyTemplate.Instance,
......@@ -17,10 +16,8 @@ namespace NBitcoin
PayToWitTemplate.Instance
};
public static bool IsStandardTransaction(Transaction tx, Network network = null)
public static bool IsStandardTransaction(Transaction tx, Network network)
{
network = network ?? Network.Main;
return new StandardTransactionPolicy(network).Check(tx, null).Length == 0;
}
......@@ -29,25 +26,25 @@ namespace NBitcoin
return tx.Outputs.All(vout => IsStandardScriptPubKey(network, vout.ScriptPubKey));
}
public static ScriptTemplate GetTemplateFromScriptPubKey(Network network, Script script)
public static ScriptTemplate GetTemplateFromScriptPubKey(Script script)
{
return _StandardTemplates.FirstOrDefault(t => t.CheckScriptPubKey(network, script));
return _StandardTemplates.FirstOrDefault(t => t.CheckScriptPubKey(script));
}
public static bool IsStandardScriptPubKey(Network network, Script scriptPubKey)
{
return _StandardTemplates.Any(template => template.CheckScriptPubKey(network, scriptPubKey));
return _StandardTemplates.Any(template => template.CheckScriptPubKey(scriptPubKey));
}
private static bool IsStandardScriptSig(Network network, Script scriptSig, Script scriptPubKey)
{
var template = GetTemplateFromScriptPubKey(network, scriptPubKey);
ScriptTemplate template = GetTemplateFromScriptPubKey(scriptPubKey);
if(template == null)
return false;
return template.CheckScriptSig(network, scriptSig, scriptPubKey);
}
//
// Check transaction inputs, and make sure any
// pay-to-script-hash transactions are evaluating IsStandard scripts
//
......@@ -57,13 +54,12 @@ namespace NBitcoin
// script can be anything; an attacker could use a very
// expensive-to-check-upon-redemption script like:
// DUP CHECKSIG DROP ... repeated 100 times... OP_1
//
public static bool AreInputsStandard(Network network, Transaction tx, CoinsView coinsView)
{
if(tx.IsCoinBase)
return true; // Coinbases don't use vin normally
foreach(var input in tx.Inputs)
foreach(TxIn input in tx.Inputs)
{
TxOut prev = coinsView.GetOutputFor(input);
if(prev == null)
......@@ -75,4 +71,4 @@ namespace NBitcoin
return true;
}
}
}
}
\ No newline at end of file
......@@ -16,9 +16,10 @@ namespace NBitcoin.Stealth
}
throw new ArgumentException("No nonce can satisfy the given bitfield, use another ephemKey");
}
public static StealthMetadata TryParse(Script metadata)
{
StealthMetadata result = new StealthMetadata();
var result = new StealthMetadata();
try
{
if(!Fill(result, metadata))
......@@ -30,9 +31,11 @@ namespace NBitcoin.Stealth
}
return result;
}
private StealthMetadata()
{
}
public StealthMetadata(Script metadata)
{
if(!Fill(this, metadata))
......@@ -43,19 +46,20 @@ namespace NBitcoin.Stealth
{
var data = new MemoryStream();
data.WriteByte(6);
var b = Utils.ToBytes(nonce, true);
byte[] b = Utils.ToBytes(nonce, true);
data.Write(b, 0, b.Length);
data.Write(ephemKey.PubKey.Compress().ToBytes(), 0, 33);
Fill(this, new Script(OpcodeType.OP_RETURN, Op.GetPushOp(data.ToArray())));
}
static TxNullDataTemplate _Template = new TxNullDataTemplate(1024 * 4);
private static TxNullDataTemplate _Template = new TxNullDataTemplate(1024 * 4);
private static bool Fill(StealthMetadata output, Script metadata)
{
var datas = _Template.ExtractScriptPubKeyParameters(Network.Main, metadata);
byte[][] datas = _Template.ExtractScriptPubKeyParameters(metadata);
if(datas == null)
return false;
foreach(var data in datas)
foreach(byte[] data in datas)
{
if(Fill(output, metadata, data))
return true;
......@@ -67,7 +71,7 @@ namespace NBitcoin.Stealth
{
if(data == null || data.Length != 1 + 4 + 33)
return false;
MemoryStream ms = new MemoryStream(data);
var ms = new MemoryStream(data);
output.Version = ms.ReadByte();
if(output.Version != 6)
return false;
......
......@@ -11,7 +11,7 @@ namespace NBitcoin.Stealth
{
get
{
return _Payment;
return this._Payment;
}
}
private readonly KeyId _ID;
......@@ -19,18 +19,18 @@ namespace NBitcoin.Stealth
{
get
{
return _ID;
return this._ID;
}
}
public StealthSpendKey(KeyId id, StealthPayment payment)
{
_ID = id;
_Payment = payment;
this._ID = id;
this._Payment = payment;
}
public BitcoinAddress GetAddress(Network network)
{
return new BitcoinPubKeyAddress(ID, network);
return new BitcoinPubKeyAddress(this.ID, network);
}
}
......@@ -38,13 +38,13 @@ namespace NBitcoin.Stealth
{
public StealthPayment(BitcoinStealthAddress address, Key ephemKey, StealthMetadata metadata)
{
Metadata = metadata;
ScriptPubKey = CreatePaymentScript(address.SignatureCount, address.SpendPubKeys, ephemKey, address.ScanPubKey);
this.Metadata = metadata;
this.ScriptPubKey = CreatePaymentScript(address.SignatureCount, address.SpendPubKeys, ephemKey, address.ScanPubKey);
if(address.SignatureCount > 1)
{
Redeem = ScriptPubKey;
ScriptPubKey = ScriptPubKey.Hash.ScriptPubKey;
this.Redeem = this.ScriptPubKey;
this.ScriptPubKey = this.ScriptPubKey.Hash.ScriptPubKey;
}
SetStealthKeys();
}
......@@ -71,17 +71,16 @@ namespace NBitcoin.Stealth
return CreatePaymentScript(address.SignatureCount, address.SpendPubKeys.Select(p => p.UncoverReceiver(scan, ephemKey)).ToArray());
}
public static KeyId[] ExtractKeyIDs(Script script)
{
var keyId = PayToPubkeyHashTemplate.Instance.ExtractScriptPubKeyParameters(script);
KeyId keyId = PayToPubkeyHashTemplate.Instance.ExtractScriptPubKeyParameters(script);
if(keyId != null)
{
return new[] { keyId };
}
else
{
var para = PayToMultiSigTemplate.Instance.ExtractScriptPubKeyParameters(Network.Main, script);
PayToMultiSigTemplateParameters para = PayToMultiSigTemplate.Instance.ExtractScriptPubKeyParameters(script);
if(para == null)
throw new ArgumentException("Invalid stealth spendable output script", "spendable");
return para.PubKeys.Select(k => k.Hash).ToArray();
......@@ -93,22 +92,23 @@ namespace NBitcoin.Stealth
get;
private set;
}
public BitcoinAddress[] GetAddresses(Network network)
{
return StealthKeys.Select(k => k.GetAddress(network)).ToArray();
return this.StealthKeys.Select(k => k.GetAddress(network)).ToArray();
}
public StealthPayment(Script scriptPubKey, Script redeem, StealthMetadata metadata)
{
Metadata = metadata;
ScriptPubKey = scriptPubKey;
Redeem = redeem;
this.Metadata = metadata;
this.ScriptPubKey = scriptPubKey;
this.Redeem = redeem;
SetStealthKeys();
}
private void SetStealthKeys()
{
StealthKeys = ExtractKeyIDs(Redeem ?? ScriptPubKey).Select(id => new StealthSpendKey(id, this)).ToArray();
this.StealthKeys = ExtractKeyIDs(this.Redeem ?? this.ScriptPubKey).Select(id => new StealthSpendKey(id, this)).ToArray();
}
......@@ -134,20 +134,20 @@ namespace NBitcoin.Stealth
throw new ArgumentNullException("transaction");
if(value == null)
throw new ArgumentNullException("value");
transaction.Outputs.Add(new TxOut(0, Metadata.Script));
transaction.Outputs.Add(new TxOut(value, ScriptPubKey));
transaction.Outputs.Add(new TxOut(0, this.Metadata.Script));
transaction.Outputs.Add(new TxOut(value, this.ScriptPubKey));
}
public static StealthPayment[] GetPayments(Transaction transaction, BitcoinStealthAddress address, Key scan)
{
List<StealthPayment> result = new List<StealthPayment>();
var result = new List<StealthPayment>();
for(int i = 0; i < transaction.Outputs.Count - 1; i++)
{
var metadata = StealthMetadata.TryParse(transaction.Outputs[i].ScriptPubKey);
StealthMetadata metadata = StealthMetadata.TryParse(transaction.Outputs[i].ScriptPubKey);
if(metadata != null && (address == null || address.Prefix.Match(metadata.BitField)))
{
var scriptPubKey = transaction.Outputs[i + 1].ScriptPubKey;
var scriptId = PayToScriptHashTemplate.Instance.ExtractScriptPubKeyParameters(scriptPubKey);
Script scriptPubKey = transaction.Outputs[i + 1].ScriptPubKey;
ScriptId scriptId = PayToScriptHashTemplate.Instance.ExtractScriptPubKeyParameters(scriptPubKey);
Script expectedScriptPubKey = address == null ? scriptPubKey : null;
Script redeem = null;
......
......@@ -33,75 +33,92 @@ namespace NBitcoin
{
}
Random _Rand = new Random();
private Random _Rand = new Random();
public DefaultCoinSelector(int seed)
{
_Rand = new Random(seed);
this._Rand = new Random(seed);
}
/// <summary>
/// Select all coins belonging to same scriptPubKey together to protect privacy. (Default: true)
/// </summary>
public bool GroupByScriptPubKey
{
get; set;
} = true;
#region ICoinSelector Members
public IEnumerable<ICoin> Select(IEnumerable<ICoin> coins, IMoney target)
{
var zero = target.Sub(target);
var targetCoin = coins
.FirstOrDefault(c => c.Amount.CompareTo(target) == 0);
//If any of your UTXO² matches the Target¹ it will be used.
if(targetCoin != null)
return new[] { targetCoin };
IMoney zero = target.Sub(target);
List<ICoin> result = new List<ICoin>();
var result = new List<ICoin>();
IMoney total = zero;
if(target.CompareTo(zero) == 0)
if (target.CompareTo(zero) == 0)
return result;
var orderedCoins = coins.OrderBy(s => s.Amount).ToArray();
var orderedCoinGroups = coins.GroupBy(c => this.GroupByScriptPubKey ? c.TxOut.ScriptPubKey : new Key().ScriptPubKey)
.Select(scriptPubKeyCoins => new
{
Amount = scriptPubKeyCoins.Select(c => c.Amount).Sum(zero),
Coins = scriptPubKeyCoins.ToList()
}).OrderBy(c => c.Amount);
var targetCoin = orderedCoinGroups
.FirstOrDefault(c => c.Amount.CompareTo(target) == 0);
//If any of your UTXO² matches the Target¹ it will be used.
if (targetCoin != null)
return targetCoin.Coins;
foreach(var coin in orderedCoins)
foreach (var coinGroup in orderedCoinGroups)
{
if(coin.Amount.CompareTo(target) == -1 && total.CompareTo(target) == -1)
if (coinGroup.Amount.CompareTo(target) == -1 && total.CompareTo(target) == -1)
{
total = total.Add(coin.Amount);
result.Add(coin);
total = total.Add(coinGroup.Amount);
result.AddRange(coinGroup.Coins);
//If the "sum of all your UTXO smaller than the Target" happens to match the Target, they will be used. (This is the case if you sweep a complete wallet.)
if(total.CompareTo(target) == 0)
if (total.CompareTo(target) == 0)
return result;
}
else
{
if(total.CompareTo(target) == -1 && coin.Amount.CompareTo(target) == 1)
if (total.CompareTo(target) == -1 && coinGroup.Amount.CompareTo(target) == 1)
{
//If the "sum of all your UTXO smaller than the Target" doesn't surpass the target, the smallest UTXO greater than your Target will be used.
return new[] { coin };
return coinGroup.Coins;
}
else
{
// Else Bitcoin Core does 1000 rounds of randomly combining unspent transaction outputs until their sum is greater than or equal to the Target. If it happens to find an exact match, it stops early and uses that.
// Else Bitcoin Core does 1000 rounds of randomly combining unspent transaction outputs until their sum is greater than or equal to the Target. If it happens to find an exact match, it stops early and uses that.
//Otherwise it finally settles for the minimum of
//the smallest UTXO greater than the Target
//the smallest combination of UTXO it discovered in Step 4.
var allCoins = orderedCoins.ToArray();
var allCoins = orderedCoinGroups.ToArray();
IMoney minTotal = null;
for(int _ = 0; _ < 1000; _++)
for (int _ = 0; _ < 1000; _++)
{
var selection = new List<ICoin>();
Utils.Shuffle(allCoins, _Rand);
Utils.Shuffle(allCoins, this._Rand);
total = zero;
for(int i = 0; i < allCoins.Length; i++)
for (int i = 0; i < allCoins.Length; i++)
{
selection.Add(allCoins[i]);
selection.AddRange(allCoins[i].Coins);
total = total.Add(allCoins[i].Amount);
if(total.CompareTo(target) == 0)
if (total.CompareTo(target) == 0)
return selection;
if(total.CompareTo(target) == 1)
if (total.CompareTo(target) == 1)
break;
}
if(total.CompareTo(target) == -1)
if (total.CompareTo(target) == -1)
{
return null;
}
if(minTotal == null || total.CompareTo(minTotal) == -1)
if (minTotal == null || total.CompareTo(minTotal) == -1)
{
minTotal = total;
}
......@@ -109,7 +126,7 @@ namespace NBitcoin
}
}
}
if(total.CompareTo(target) == -1)
if (total.CompareTo(target) == -1)
return null;
return result;
}
......@@ -125,13 +142,13 @@ namespace NBitcoin
public NotEnoughFundsException(string message, string group, IMoney missing)
: base(BuildMessage(message, group, missing))
{
Missing = missing;
Group = group;
this.Missing = missing;
this.Group = group;
}
private static string BuildMessage(string message, string group, IMoney missing)
{
StringBuilder builder = new StringBuilder();
var builder = new StringBuilder();
builder.Append(message);
if(group != null)
builder.Append(" in group " + group);
......@@ -170,10 +187,10 @@ namespace NBitcoin
{
internal class TransactionBuilderSigner : ISigner
{
ICoin coin;
SigHash sigHash;
IndexedTxIn txIn;
TransactionBuilder builder;
private ICoin coin;
private SigHash sigHash;
private IndexedTxIn txIn;
private TransactionBuilder builder;
public TransactionBuilderSigner(TransactionBuilder builder, ICoin coin, SigHash sigHash, IndexedTxIn txIn)
{
this.builder = builder;
......@@ -185,31 +202,31 @@ namespace NBitcoin
public TransactionSignature Sign(Key key)
{
return txIn.Sign(this.builder.Network, key, coin, sigHash);
return this.txIn.Sign(this.builder.Network, key, this.coin, this.sigHash);
}
#endregion
}
internal class TransactionBuilderKeyRepository : IKeyRepository
{
TransactionSigningContext _Ctx;
TransactionBuilder _TxBuilder;
private TransactionSigningContext _Ctx;
private TransactionBuilder _TxBuilder;
public TransactionBuilderKeyRepository(TransactionBuilder txBuilder, TransactionSigningContext ctx)
{
_Ctx = ctx;
_TxBuilder = txBuilder;
this._Ctx = ctx;
this._TxBuilder = txBuilder;
}
#region IKeyRepository Members
public Key FindKey(Script scriptPubkey)
{
return _TxBuilder.FindKey(_Ctx, scriptPubkey);
return this._TxBuilder.FindKey(this._Ctx, scriptPubkey);
}
#endregion
}
class KnownSignatureSigner : ISigner, IKeyRepository
private class KnownSignatureSigner : ISigner, IKeyRepository
{
private ICoin coin;
private SigHash sigHash;
......@@ -217,7 +234,7 @@ namespace NBitcoin
private List<Tuple<PubKey, ECDSASignature>> _KnownSignatures;
private Dictionary<KeyId, ECDSASignature> _VerifiedSignatures = new Dictionary<KeyId, ECDSASignature>();
private Dictionary<uint256, PubKey> _DummyToRealKey = new Dictionary<uint256, PubKey>();
TransactionBuilder builder;
private TransactionBuilder builder;
public KnownSignatureSigner(TransactionBuilder builder, List<Tuple<PubKey, ECDSASignature>> _KnownSignatures, ICoin coin, SigHash sigHash, IndexedTxIn txIn)
{
......@@ -230,14 +247,14 @@ namespace NBitcoin
public Key FindKey(Script scriptPubKey)
{
foreach(var tv in _KnownSignatures.Where(tv => IsCompatibleKey(tv.Item1, scriptPubKey)))
foreach(Tuple<PubKey, ECDSASignature> tv in this._KnownSignatures.Where(tv => IsCompatibleKey(tv.Item1, scriptPubKey)))
{
var hash = txIn.GetSignatureHash(this.builder.Network, coin, sigHash);
uint256 hash = this.txIn.GetSignatureHash(this.builder.Network, this.coin, this.sigHash);
if(tv.Item1.Verify(hash, tv.Item2))
{
var key = new Key();
this._DummyToRealKey.Add(Hashes.Hash256(key.PubKey.ToBytes()), tv.Item1);
_VerifiedSignatures.AddOrReplace(key.PubKey.Hash, tv.Item2);
this._VerifiedSignatures.AddOrReplace(key.PubKey.Hash, tv.Item2);
return key;
}
}
......@@ -246,23 +263,23 @@ namespace NBitcoin
public Script ReplaceDummyKeys(Script script)
{
var ops = script.ToOps().ToList();
List<Op> result = new List<Op>();
List<Op> ops = script.ToOps().ToList();
var result = new List<Op>();
foreach(Op op in ops)
{
uint256 h = Hashes.Hash256(op.PushData);
PubKey real;
if(this._DummyToRealKey.TryGetValue(h, out real))
result.Add(Op.GetPushOp(real.ToBytes()));
else
result.Add(op);
PubKey real;
if(this._DummyToRealKey.TryGetValue(h, out real))
result.Add(Op.GetPushOp(real.ToBytes()));
else
result.Add(op);
}
return new Script(result.ToArray());
}
public TransactionSignature Sign(Key key)
{
return new TransactionSignature(_VerifiedSignatures[key.PubKey.Hash], sigHash);
return new TransactionSignature(this._VerifiedSignatures[key.PubKey.Hash], this.sigHash);
}
}
......@@ -270,8 +287,8 @@ namespace NBitcoin
{
public TransactionSigningContext(TransactionBuilder builder, Transaction transaction)
{
Builder = builder;
Transaction = transaction;
this.Builder = builder;
this.Transaction = transaction;
}
public Transaction Transaction
......@@ -290,7 +307,7 @@ namespace NBitcoin
{
get
{
return _AdditionalKeys;
return this._AdditionalKeys;
}
}
......@@ -300,15 +317,17 @@ namespace NBitcoin
set;
}
}
internal class TransactionBuildingContext
{
public TransactionBuildingContext(TransactionBuilder builder)
{
Builder = builder;
Transaction = builder.Network.Consensus.ConsensusFactory.CreateTransaction();
AdditionalFees = Money.Zero;
this.Builder = builder;
this.Transaction = builder.Network.CreateTransaction();
this.AdditionalFees = Money.Zero;
}
public TransactionBuilder.BuilderGroup Group
public BuilderGroup Group
{
get;
set;
......@@ -319,14 +338,16 @@ namespace NBitcoin
{
get
{
return _ConsumedCoins;
return this._ConsumedCoins;
}
}
public TransactionBuilder Builder
{
get;
set;
}
public Transaction Transaction
{
get;
......@@ -344,35 +365,34 @@ namespace NBitcoin
{
get
{
return _AdditionalBuilders;
return this._AdditionalBuilders;
}
}
ColorMarker _Marker;
private ColorMarker _Marker;
public ColorMarker GetColorMarker(bool issuance)
{
if(_Marker == null)
_Marker = new ColorMarker();
if(this._Marker == null) this._Marker = new ColorMarker();
if(!issuance)
EnsureMarkerInserted();
return _Marker;
return this._Marker;
}
private TxOut EnsureMarkerInserted()
{
uint position;
var dummy = Transaction.AddInput(new TxIn(new OutPoint(new uint256(1), 0))); //Since a transaction without input will be considered without marker, insert a dummy
TxIn dummy = this.Transaction.AddInput(new TxIn(new OutPoint(new uint256(1), 0))); //Since a transaction without input will be considered without marker, insert a dummy
try
{
if(ColorMarker.Get(Transaction, out position) != null)
return Transaction.Outputs[position];
if(ColorMarker.Get(this.Transaction, out position) != null)
return this.Transaction.Outputs[position];
}
finally
{
Transaction.Inputs.Remove(dummy);
this.Transaction.Inputs.Remove(dummy);
}
var txout = Transaction.AddOutput(new TxOut()
TxOut txout = this.Transaction.AddOutput(new TxOut()
{
ScriptPubKey = new ColorMarker().GetScript()
});
......@@ -382,10 +402,10 @@ namespace NBitcoin
public void Finish()
{
if(_Marker != null)
if(this._Marker != null)
{
var txout = EnsureMarkerInserted();
txout.ScriptPubKey = _Marker.GetScript();
TxOut txout = EnsureMarkerInserted();
txout.ScriptPubKey = this._Marker.GetScript();
}
}
......@@ -403,16 +423,16 @@ namespace NBitcoin
public TransactionBuildingContext CreateMemento()
{
var memento = new TransactionBuildingContext(Builder);
var memento = new TransactionBuildingContext(this.Builder);
memento.RestoreMemento(this);
return memento;
}
public void RestoreMemento(TransactionBuildingContext memento)
{
_Marker = memento._Marker == null ? null : new ColorMarker(memento._Marker.GetScript());
Transaction = memento.Transaction.Clone(network: memento.Builder.Network);
AdditionalFees = memento.AdditionalFees;
this._Marker = memento._Marker == null ? null : new ColorMarker(memento._Marker.GetScript());
this.Transaction = memento.Builder.Network.CreateTransaction(memento.Transaction.ToBytes());
this.AdditionalFees = memento.AdditionalFees;
}
public bool NonFinalSequenceSet
......@@ -442,15 +462,15 @@ namespace NBitcoin
internal class BuilderGroup
{
TransactionBuilder _Parent;
private TransactionBuilder _Parent;
public BuilderGroup(TransactionBuilder parent)
{
_Parent = parent;
FeeWeight = 1.0m;
Builders.Add(SetChange);
this._Parent = parent;
this.FeeWeight = 1.0m;
this.Builders.Add(SetChange);
}
IMoney SetChange(TransactionBuildingContext ctx)
private IMoney SetChange(TransactionBuildingContext ctx)
{
var changeAmount = (Money)ctx.ChangeAmount;
if(changeAmount.Satoshi == 0)
......@@ -465,14 +485,14 @@ namespace NBitcoin
internal Script[] ChangeScript = new Script[3];
internal void Shuffle()
{
Shuffle(Builders);
foreach(var builders in BuildersByAsset)
Shuffle(this.Builders);
foreach(KeyValuePair<AssetId, List<Builder>> builders in this.BuildersByAsset)
Shuffle(builders.Value);
Shuffle(IssuanceBuilders);
Shuffle(this.IssuanceBuilders);
}
private void Shuffle(List<Builder> builders)
{
Utils.Shuffle(builders, _Parent._Rand);
Utils.Shuffle(builders, this._Parent._Rand);
}
public Money CoverOnly
......@@ -494,70 +514,50 @@ namespace NBitcoin
}
}
List<BuilderGroup> _BuilderGroups = new List<BuilderGroup>();
BuilderGroup _CurrentGroup = null;
private List<BuilderGroup> _BuilderGroups = new List<BuilderGroup>();
private BuilderGroup _CurrentGroup = null;
internal BuilderGroup CurrentGroup
{
get
{
if(_CurrentGroup == null)
if(this._CurrentGroup == null)
{
_CurrentGroup = new BuilderGroup(this);
_BuilderGroups.Add(_CurrentGroup);
this._CurrentGroup = new BuilderGroup(this);
this._BuilderGroups.Add(this._CurrentGroup);
}
return _CurrentGroup;
return this._CurrentGroup;
}
}
internal TransactionBuilder()
{
this.Network = Network.Main;
_Rand = new Random();
CoinSelector = new DefaultCoinSelector();
StandardTransactionPolicy = new StandardTransactionPolicy(this.Network);
DustPrevention = true;
InitExtensions();
}
public TransactionBuilder(Network network)
{
this.Network = network;
_Rand = new Random();
CoinSelector = new DefaultCoinSelector();
StandardTransactionPolicy = new StandardTransactionPolicy(this.Network);
DustPrevention = true;
this._Rand = new Random();
this.CoinSelector = new DefaultCoinSelector();
this.StandardTransactionPolicy = new StandardTransactionPolicy(this.Network);
this.DustPrevention = true;
InitExtensions();
}
private void InitExtensions()
{
Extensions.Add(new P2PKHBuilderExtension());
Extensions.Add(new P2MultiSigBuilderExtension());
Extensions.Add(new P2PKBuilderExtension());
Extensions.Add(new OPTrueExtension());
this.Extensions.Add(new P2PKHBuilderExtension());
this.Extensions.Add(new P2MultiSigBuilderExtension());
this.Extensions.Add(new P2PKBuilderExtension());
this.Extensions.Add(new OPTrueExtension());
}
internal Random _Rand;
internal TransactionBuilder(int seed)
{
this.Network = Network.Main;
_Rand = new Random(seed);
CoinSelector = new DefaultCoinSelector(seed);
StandardTransactionPolicy = new StandardTransactionPolicy(this.Network);
DustPrevention = true;
InitExtensions();
}
public TransactionBuilder(int seed, Network network)
{
this.Network = network;
_Rand = new Random(seed);
CoinSelector = new DefaultCoinSelector(seed);
StandardTransactionPolicy = new StandardTransactionPolicy(this.Network);
DustPrevention = true;
this._Rand = new Random(seed);
this.CoinSelector = new DefaultCoinSelector(seed);
this.StandardTransactionPolicy = new StandardTransactionPolicy(this.Network);
this.DustPrevention = true;
InitExtensions();
}
......@@ -578,6 +578,7 @@ namespace NBitcoin
/// <summary>
/// Will transform transfers below Dust, so the transaction get correctly relayed by the network.
/// If true, it will remove any TxOut below Dust, so the transaction get correctly relayed by the network. (Default: true)
/// </summary>
public bool DustPrevention
{
......@@ -585,6 +586,21 @@ namespace NBitcoin
set;
}
/// <summary>
/// If true, the TransactionBuilder will not select coins whose fee to spend is higher than its value. (Default: true)
/// The cost of spending a coin is based on the <see cref="FilterUneconomicalCoinsRate"/>.
/// </summary>
public bool FilterUneconomicalCoins { get; set; } = true;
/// <summary>
/// If <see cref="FilterUneconomicalCoins"/> is true, this rate is used to know if an output is economical.
/// This property is set automatically when calling <see cref="SendEstimatedFees(FeeRate)"/> or <see cref="SendEstimatedFeesSplit(FeeRate)"/>.
/// </summary>
public FeeRate FilterUneconomicalCoinsRate
{
get; set;
}
/// <summary>
/// A callback used by the TransactionBuilder when it does not find the coin for an input
/// </summary>
......@@ -603,24 +619,24 @@ namespace NBitcoin
set;
}
LockTime? _LockTime;
private LockTime? _LockTime;
public TransactionBuilder SetLockTime(LockTime lockTime)
{
_LockTime = lockTime;
this._LockTime = lockTime;
return this;
}
List<Key> _Keys = new List<Key>();
private List<Key> _Keys = new List<Key>();
public TransactionBuilder AddKeys(params ISecret[] keys)
{
this.AddKeys(keys.Select(k => k.PrivateKey).ToArray());
AddKeys(keys.Select(k => k.PrivateKey).ToArray());
return this;
}
public TransactionBuilder AddKeys(params Key[] keys)
{
_Keys.AddRange(keys);
this._Keys.AddRange(keys);
foreach (Key k in keys)
{
AddKnownRedeems(k.PubKey.ScriptPubKey);
......@@ -636,7 +652,7 @@ namespace NBitcoin
throw new ArgumentNullException("pubKey");
if(signature == null)
throw new ArgumentNullException("signature");
_KnownSignatures.Add(Tuple.Create(pubKey, signature.Signature));
this._KnownSignatures.Add(Tuple.Create(pubKey, signature.Signature));
return this;
}
......@@ -646,7 +662,7 @@ namespace NBitcoin
throw new ArgumentNullException("pubKey");
if(signature == null)
throw new ArgumentNullException("signature");
_KnownSignatures.Add(Tuple.Create(pubKey, signature));
this._KnownSignatures.Add(Tuple.Create(pubKey, signature));
return this;
}
......@@ -657,9 +673,9 @@ namespace NBitcoin
public TransactionBuilder AddCoins(IEnumerable<ICoin> coins)
{
foreach(var coin in coins)
foreach(ICoin coin in coins)
{
CurrentGroup.Coins.AddOrReplace(coin.Outpoint, coin);
this.CurrentGroup.Coins.AddOrReplace(coin.Outpoint, coin);
}
return this;
}
......@@ -671,7 +687,7 @@ namespace NBitcoin
/// <returns></returns>
public TransactionBuilder SetGroupName(string groupName)
{
CurrentGroup.Name = groupName;
this.CurrentGroup.Name = groupName;
return this;
}
......@@ -686,7 +702,7 @@ namespace NBitcoin
return Send(destination.ScriptPubKey, amount);
}
readonly static TxNullDataTemplate _OpReturnTemplate = new TxNullDataTemplate(1024 * 1024);
private readonly static TxNullDataTemplate _OpReturnTemplate = new TxNullDataTemplate(1024 * 1024);
/// <summary>
/// Send bitcoins to a destination
......@@ -698,35 +714,35 @@ namespace NBitcoin
{
if(amount < Money.Zero)
throw new ArgumentOutOfRangeException("amount", "amount can't be negative");
_LastSendBuilder = null; //If the amount is dust, we don't want the fee to be paid by the previous Send
if(DustPrevention && amount < GetDust(scriptPubKey) && !_OpReturnTemplate.CheckScriptPubKey(this.Network, scriptPubKey))
this._LastSendBuilder = null; //If the amount is dust, we don't want the fee to be paid by the previous Send
if(this.DustPrevention && amount < GetDust(scriptPubKey) && !_OpReturnTemplate.CheckScriptPubKey(scriptPubKey))
{
SendFees(amount);
return this;
}
var builder = new SendBuilder(new TxOut(amount, scriptPubKey));
CurrentGroup.Builders.Add(builder.Build);
_LastSendBuilder = builder;
this.CurrentGroup.Builders.Add(builder.Build);
this._LastSendBuilder = builder;
return this;
}
SendBuilder _LastSendBuilder;
SendBuilder _SubstractFeeBuilder;
private SendBuilder _LastSendBuilder;
private SendBuilder _SubstractFeeBuilder;
class SendBuilder
private class SendBuilder
{
internal TxOut _TxOut;
public SendBuilder(TxOut txout)
{
_TxOut = txout;
this._TxOut = txout;
}
public Money Build(TransactionBuildingContext ctx)
{
ctx.Transaction.Outputs.Add(_TxOut);
return _TxOut.Value;
ctx.Transaction.Outputs.Add(this._TxOut);
return this._TxOut.Value;
}
}
......@@ -736,9 +752,9 @@ namespace NBitcoin
/// <returns></returns>
public TransactionBuilder SubtractFees()
{
if(_LastSendBuilder == null)
if(this._LastSendBuilder == null)
throw new InvalidOperationException("No call to TransactionBuilder.Send has been done which can support the fees");
_SubstractFeeBuilder = _LastSendBuilder;
this._SubstractFeeBuilder = this._LastSendBuilder;
return this;
}
......@@ -762,17 +778,17 @@ namespace NBitcoin
/// <exception cref="System.NotSupportedException">The coin type is not supported</exception>
public TransactionBuilder Send(Script scriptPubKey, IMoney amount)
{
MoneyBag bag = amount as MoneyBag;
var bag = amount as MoneyBag;
if(bag != null)
{
foreach(var money in bag)
foreach(IMoney money in bag)
Send(scriptPubKey, amount);
return this;
}
Money coinAmount = amount as Money;
var coinAmount = amount as Money;
if(coinAmount != null)
return Send(scriptPubKey, coinAmount);
AssetMoney assetAmount = amount as AssetMoney;
var assetAmount = amount as AssetMoney;
if(assetAmount != null)
return SendAsset(scriptPubKey, assetAmount);
throw new NotSupportedException("Type of Money not supported");
......@@ -802,20 +818,20 @@ namespace NBitcoin
public TransactionBuilder Shuffle()
{
Utils.Shuffle(_BuilderGroups, _Rand);
foreach(var group in _BuilderGroups)
Utils.Shuffle(this._BuilderGroups, this._Rand);
foreach(BuilderGroup group in this._BuilderGroups)
group.Shuffle();
return this;
}
IMoney SetColoredChange(TransactionBuildingContext ctx)
private IMoney SetColoredChange(TransactionBuildingContext ctx)
{
var changeAmount = (AssetMoney)ctx.ChangeAmount;
if(changeAmount.Quantity == 0)
return changeAmount;
var marker = ctx.GetColorMarker(false);
var script = ctx.Group.ChangeScript[(int)ChangeType.Colored];
var txout = ctx.Transaction.AddOutput(new TxOut(GetDust(script), script));
ColorMarker marker = ctx.GetColorMarker(false);
Script script = ctx.Group.ChangeScript[(int)ChangeType.Colored];
TxOut txout = ctx.Transaction.AddOutput(new TxOut(GetDust(script), script));
marker.SetQuantity(ctx.Transaction.Outputs.Count - 2, changeAmount.Quantity);
ctx.AdditionalFees += txout.Value;
return changeAmount;
......@@ -833,17 +849,17 @@ namespace NBitcoin
if(asset.Quantity == 0)
return this;
AssertOpReturn("Colored Coin");
var builders = CurrentGroup.BuildersByAsset.TryGet(asset.Id);
List<Builder> builders = this.CurrentGroup.BuildersByAsset.TryGet(asset.Id);
if(builders == null)
{
builders = new List<Builder>();
CurrentGroup.BuildersByAsset.Add(asset.Id, builders);
this.CurrentGroup.BuildersByAsset.Add(asset.Id, builders);
builders.Add(SetColoredChange);
}
builders.Add(ctx =>
{
var marker = ctx.GetColorMarker(false);
var txout = ctx.Transaction.AddOutput(new TxOut(GetDust(scriptPubKey), scriptPubKey));
ColorMarker marker = ctx.GetColorMarker(false);
TxOut txout = ctx.Transaction.AddOutput(new TxOut(GetDust(scriptPubKey), scriptPubKey));
marker.SetQuantity(ctx.Transaction.Outputs.Count - 2, asset.Quantity);
ctx.AdditionalFees += txout.Value;
return asset;
......@@ -851,15 +867,16 @@ namespace NBitcoin
return this;
}
Money GetDust()
private Money GetDust()
{
return GetDust(new Script(new byte[25]));
}
Money GetDust(Script script)
private Money GetDust(Script script)
{
if(StandardTransactionPolicy == null || StandardTransactionPolicy.MinRelayTxFee == null)
if(this.StandardTransactionPolicy == null || this.StandardTransactionPolicy.MinRelayTxFee == null)
return Money.Zero;
return new TxOut(Money.Zero, script).GetDustThreshold(StandardTransactionPolicy.MinRelayTxFee);
return new TxOut(Money.Zero, script).GetDustThreshold(this.StandardTransactionPolicy.MinRelayTxFee);
}
/// <summary>
......@@ -869,7 +886,7 @@ namespace NBitcoin
/// <returns>this</returns>
public TransactionBuilder SetTransactionPolicy(StandardTransactionPolicy policy)
{
StandardTransactionPolicy = policy;
this.StandardTransactionPolicy = policy;
return this;
}
public StandardTransactionPolicy StandardTransactionPolicy
......@@ -879,17 +896,17 @@ namespace NBitcoin
}
string _OpReturnUser;
private string _OpReturnUser;
private void AssertOpReturn(string name)
{
if(_OpReturnUser == null)
if(this._OpReturnUser == null)
{
_OpReturnUser = name;
this._OpReturnUser = name;
}
else
{
if(_OpReturnUser != name)
throw new InvalidOperationException("Op return already used for " + _OpReturnUser);
if(this._OpReturnUser != name)
throw new InvalidOperationException("Op return already used for " + this._OpReturnUser);
}
}
......@@ -898,14 +915,14 @@ namespace NBitcoin
if(amount < Money.Zero)
throw new ArgumentOutOfRangeException("amount", "amount can't be negative");
if(_OpReturnUser == null)
_OpReturnUser = "Stealth Payment";
if(this._OpReturnUser == null)
this._OpReturnUser = "Stealth Payment";
else
throw new InvalidOperationException("Op return already used for " + _OpReturnUser);
throw new InvalidOperationException("Op return already used for " + this._OpReturnUser);
CurrentGroup.Builders.Add(ctx =>
this.CurrentGroup.Builders.Add(ctx =>
{
var payment = address.CreatePayment(ephemKey);
StealthPayment payment = address.CreatePayment(ephemKey);
payment.AddToTransaction(ctx.Transaction, amount);
return amount;
});
......@@ -917,22 +934,22 @@ namespace NBitcoin
return IssueAsset(destination.ScriptPubKey, asset);
}
AssetId _IssuedAsset;
private AssetId _IssuedAsset;
public TransactionBuilder IssueAsset(Script scriptPubKey, AssetMoney asset)
{
AssertOpReturn("Colored Coin");
if(_IssuedAsset == null)
_IssuedAsset = asset.Id;
else if(_IssuedAsset != asset.Id)
if(this._IssuedAsset == null)
this._IssuedAsset = asset.Id;
else if(this._IssuedAsset != asset.Id)
throw new InvalidOperationException("You can issue only one asset type in a transaction");
CurrentGroup.IssuanceBuilders.Add(ctx =>
this.CurrentGroup.IssuanceBuilders.Add(ctx =>
{
var marker = ctx.GetColorMarker(true);
ColorMarker marker = ctx.GetColorMarker(true);
if(ctx.IssuanceCoin == null)
{
var issuance = ctx.Group.Coins.Values.OfType<IssuanceCoin>().Where(i => i.AssetId == asset.Id).FirstOrDefault();
IssuanceCoin issuance = ctx.Group.Coins.Values.OfType<IssuanceCoin>().Where(i => i.AssetId == asset.Id).FirstOrDefault();
if(issuance == null)
throw new InvalidOperationException("No issuance coin for emitting asset found");
ctx.IssuanceCoin = issuance;
......@@ -956,12 +973,12 @@ namespace NBitcoin
{
if(fees == null)
throw new ArgumentNullException("fees");
CurrentGroup.Builders.Add(ctx => fees);
_TotalFee += fees;
this.CurrentGroup.Builders.Add(ctx => fees);
this._TotalFee += fees;
return this;
}
Money _TotalFee = Money.Zero;
private Money _TotalFee = Money.Zero;
/// <summary>
/// Split the estimated fees accross the several groups (separated by Then())
......@@ -970,7 +987,8 @@ namespace NBitcoin
/// <returns></returns>
public TransactionBuilder SendEstimatedFees(FeeRate feeRate)
{
var fee = EstimateFees(feeRate);
this.FilterUneconomicalCoinsRate = feeRate;
Money fee = EstimateFees(feeRate);
SendFees(fee);
return this;
}
......@@ -982,7 +1000,8 @@ namespace NBitcoin
/// <returns></returns>
public TransactionBuilder SendEstimatedFeesSplit(FeeRate feeRate)
{
var fee = EstimateFees(feeRate);
this.FilterUneconomicalCoinsRate = feeRate;
Money fee = EstimateFees(feeRate);
SendFeesSplit(fee);
return this;
}
......@@ -996,16 +1015,16 @@ namespace NBitcoin
{
if(fees == null)
throw new ArgumentNullException("fees");
var lastGroup = CurrentGroup; //Make sure at least one group exists
var totalWeight = _BuilderGroups.Select(b => b.FeeWeight).Sum();
BuilderGroup lastGroup = this.CurrentGroup; //Make sure at least one group exists
decimal totalWeight = this._BuilderGroups.Select(b => b.FeeWeight).Sum();
Money totalSent = Money.Zero;
foreach(var group in _BuilderGroups)
foreach(BuilderGroup group in this._BuilderGroups)
{
var groupFee = Money.Satoshis((group.FeeWeight / totalWeight) * fees.Satoshi);
Money groupFee = Money.Satoshis((group.FeeWeight / totalWeight) * fees.Satoshi);
totalSent += groupFee;
if(_BuilderGroups.Last() == group)
if(this._BuilderGroups.Last() == group)
{
var leftOver = fees - totalSent;
Money leftOver = fees - totalSent;
groupFee += leftOver;
}
group.Builders.Add(ctx => groupFee);
......@@ -1021,7 +1040,7 @@ namespace NBitcoin
/// <returns></returns>
public TransactionBuilder SetFeeWeight(decimal feeWeight)
{
CurrentGroup.FeeWeight = feeWeight;
this.CurrentGroup.FeeWeight = feeWeight;
return this;
}
......@@ -1034,11 +1053,11 @@ namespace NBitcoin
{
if((changeType & ChangeType.Colored) != 0)
{
CurrentGroup.ChangeScript[(int)ChangeType.Colored] = scriptPubKey;
this.CurrentGroup.ChangeScript[(int)ChangeType.Colored] = scriptPubKey;
}
if((changeType & ChangeType.Uncolored) != 0)
{
CurrentGroup.ChangeScript[(int)ChangeType.Uncolored] = scriptPubKey;
this.CurrentGroup.ChangeScript[(int)ChangeType.Uncolored] = scriptPubKey;
}
return this;
}
......@@ -1047,9 +1066,10 @@ namespace NBitcoin
{
if(selector == null)
throw new ArgumentNullException("selector");
CoinSelector = selector;
this.CoinSelector = selector;
return this;
}
/// <summary>
/// Build the transaction
/// </summary>
......@@ -1070,30 +1090,33 @@ namespace NBitcoin
/// <exception cref="NBitcoin.NotEnoughFundsException">Not enough funds are available</exception>
public Transaction BuildTransaction(bool sign, SigHash sigHash)
{
TransactionBuildingContext ctx = new TransactionBuildingContext(this);
if(_CompletedTransaction != null)
ctx.Transaction = _CompletedTransaction.Clone(network: this.Network);
if(_LockTime != null)
ctx.Transaction.LockTime = _LockTime.Value;
foreach(var group in _BuilderGroups)
var ctx = new TransactionBuildingContext(this);
if(this._CompletedTransaction != null)
ctx.Transaction = this.Network.CreateTransaction(this._CompletedTransaction.ToBytes());
if(this._LockTime != null)
ctx.Transaction.LockTime = this._LockTime.Value;
foreach(BuilderGroup group in this._BuilderGroups)
{
ctx.Group = group;
ctx.AdditionalBuilders.Clear();
ctx.AdditionalFees = Money.Zero;
ctx.ChangeType = ChangeType.Colored;
foreach(var builder in group.IssuanceBuilders)
foreach(Builder builder in group.IssuanceBuilders)
builder(ctx);
var buildersByAsset = group.BuildersByAsset.ToList();
foreach(var builders in buildersByAsset)
List<KeyValuePair<AssetId, List<Builder>>> buildersByAsset = group.BuildersByAsset.ToList();
foreach(KeyValuePair<AssetId, List<Builder>> builders in buildersByAsset)
{
var coins = group.Coins.Values.OfType<ColoredCoin>().Where(c => c.Amount.Id == builders.Key);
IEnumerable<ColoredCoin> coins = group.Coins.Values.OfType<ColoredCoin>().Where(c => c.Amount.Id == builders.Key);
ctx.Dust = new AssetMoney(builders.Key);
ctx.CoverOnly = null;
ctx.ChangeAmount = new AssetMoney(builders.Key);
var btcSpent = BuildTransaction(ctx, group, builders.Value, coins, new AssetMoney(builders.Key))
Money btcSpent = BuildTransaction(ctx, group, builders.Value, coins, new AssetMoney(builders.Key))
.OfType<IColoredCoin>().Select(c => c.Bearer.Amount).Sum();
ctx.AdditionalFees -= btcSpent;
}
......@@ -1103,17 +1126,32 @@ namespace NBitcoin
ctx.ChangeAmount = Money.Zero;
ctx.CoverOnly = group.CoverOnly;
ctx.ChangeType = ChangeType.Uncolored;
BuildTransaction(ctx, group, group.Builders, group.Coins.Values.OfType<Coin>(), Money.Zero);
BuildTransaction(ctx, group, group.Builders, group.Coins.Values.OfType<Coin>().Where(IsEconomical), Money.Zero);
}
ctx.Finish();
if(sign)
{
SignTransactionInPlace(ctx.Transaction, sigHash);
}
return ctx.Transaction;
}
private bool IsEconomical(Coin c)
{
if (!this.FilterUneconomicalCoins || this.FilterUneconomicalCoinsRate == null)
return true;
int witSize = 0;
int baseSize = 0;
EstimateScriptSigSize(c, ref witSize, ref baseSize);
var vSize = witSize / Transaction.WITNESS_SCALE_FACTOR + baseSize;
return c.Amount >= this.FilterUneconomicalCoinsRate.GetFee(vSize);
}
private IEnumerable<ICoin> BuildTransaction(
TransactionBuildingContext ctx,
BuilderGroup group,
......@@ -1121,48 +1159,56 @@ namespace NBitcoin
IEnumerable<ICoin> coins,
IMoney zero)
{
var originalCtx = ctx.CreateMemento();
var fees = _TotalFee + ctx.AdditionalFees;
TransactionBuildingContext originalCtx = ctx.CreateMemento();
Money fees = this._TotalFee + ctx.AdditionalFees;
// Replace the _SubstractFeeBuilder by another one with the fees substracts
var builderList = builders.ToList();
List<Builder> builderList = builders.ToList();
for(int i = 0; i < builderList.Count; i++)
{
if(builderList[i].Target == _SubstractFeeBuilder)
if(builderList[i].Target == this._SubstractFeeBuilder)
{
builderList.Remove(builderList[i]);
var newTxOut = _SubstractFeeBuilder._TxOut.Clone(network: this.Network);
TxOut newTxOut = this._SubstractFeeBuilder._TxOut.Clone();
newTxOut.Value -= fees;
builderList.Insert(i, new SendBuilder(newTxOut).Build);
}
}
////////////////////////////////////////////////////////
var target = builderList.Concat(ctx.AdditionalBuilders).Select(b => b(ctx)).Sum(zero);
IMoney target = builderList.Concat(ctx.AdditionalBuilders).Select(b => b(ctx)).Sum(zero);
if(ctx.CoverOnly != null)
{
target = ctx.CoverOnly.Add(ctx.ChangeAmount);
}
var unconsumed = coins.Where(c => ctx.ConsumedCoins.All(cc => cc.Outpoint != c.Outpoint));
var selection = CoinSelector.Select(unconsumed, target);
IEnumerable<ICoin> unconsumed = coins.Where(c => ctx.ConsumedCoins.All(cc => cc.Outpoint != c.Outpoint));
IEnumerable<ICoin> selection = this.CoinSelector.Select(unconsumed, target);
if(selection == null)
{
throw new NotEnoughFundsException("Not enough funds to cover the target",
group.Name,
target.Sub(unconsumed.Select(u => u.Amount).Sum(zero))
);
var total = selection.Select(s => s.Amount).Sum(zero);
var change = total.Sub(target);
);
}
IMoney total = selection.Select(s => s.Amount).Sum(zero);
IMoney change = total.Sub(target);
if(change.CompareTo(zero) == -1)
{
throw new NotEnoughFundsException("Not enough funds to cover the target",
group.Name,
change.Negate()
);
}
if(change.CompareTo(ctx.Dust) == 1)
{
var changeScript = group.ChangeScript[(int)ctx.ChangeType];
Script changeScript = group.ChangeScript[(int)ctx.ChangeType];
if(changeScript == null)
throw new InvalidOperationException("A change address should be specified (" + ctx.ChangeType + ")");
if(!(ctx.Dust is Money) || change.CompareTo(GetDust(changeScript)) == 1)
{
ctx.RestoreMemento(originalCtx);
......@@ -1177,24 +1223,26 @@ namespace NBitcoin
}
}
}
foreach(var coin in selection)
foreach(ICoin coin in selection)
{
ctx.ConsumedCoins.Add(coin);
var input = ctx.Transaction.Inputs.FirstOrDefault(i => i.PrevOut == coin.Outpoint);
TxIn input = ctx.Transaction.Inputs.FirstOrDefault(i => i.PrevOut == coin.Outpoint);
if(input == null)
input = ctx.Transaction.AddInput(new TxIn(coin.Outpoint));
if(_LockTime != null && !ctx.NonFinalSequenceSet)
if(this._LockTime != null && !ctx.NonFinalSequenceSet)
{
input.Sequence = 0;
ctx.NonFinalSequenceSet = true;
}
}
return selection;
}
public Transaction SignTransaction(Transaction transaction, SigHash sigHash)
{
var tx = transaction.Clone(network: this.Network);
Transaction tx = this.Network.CreateTransaction(transaction.ToBytes());
SignTransactionInPlace(tx, sigHash);
return tx;
}
......@@ -1203,17 +1251,22 @@ namespace NBitcoin
{
return SignTransaction(transaction, SigHash.All);
}
public Transaction SignTransactionInPlace(Transaction transaction)
{
return SignTransactionInPlace(transaction, SigHash.All);
}
public Transaction SignTransactionInPlace(Transaction transaction, SigHash sigHash)
{
TransactionSigningContext ctx = new TransactionSigningContext(this, transaction);
ctx.SigHash = sigHash;
foreach(var input in transaction.Inputs.AsIndexedInputs())
var ctx = new TransactionSigningContext(this, transaction)
{
SigHash = sigHash
};
foreach (IndexedTxIn input in transaction.Inputs.AsIndexedInputs())
{
var coin = FindSignableCoin(input);
ICoin coin = FindSignableCoin(input);
if(coin != null)
{
Sign(ctx, coin, input);
......@@ -1224,25 +1277,27 @@ namespace NBitcoin
public ICoin FindSignableCoin(IndexedTxIn txIn)
{
var coin = FindCoin(txIn.PrevOut);
ICoin coin = FindCoin(txIn.PrevOut);
if(coin is IColoredCoin)
coin = ((IColoredCoin)coin).Bearer;
if(coin == null || coin is ScriptCoin || coin is StealthCoin)
return coin;
var hash = ScriptCoin.GetRedeemHash(this.Network, coin.TxOut.ScriptPubKey);
TxDestination hash = ScriptCoin.GetRedeemHash(this.Network, coin.TxOut.ScriptPubKey);
if(hash != null)
{
var redeem = _ScriptPubKeyToRedeem.TryGet(coin.TxOut.ScriptPubKey);
if(redeem != null && PayToWitScriptHashTemplate.Instance.CheckScriptPubKey(this.Network, redeem))
redeem = _ScriptPubKeyToRedeem.TryGet(redeem);
Script redeem = this._ScriptPubKeyToRedeem.TryGet(coin.TxOut.ScriptPubKey);
if(redeem != null && PayToWitScriptHashTemplate.Instance.CheckScriptPubKey(redeem))
redeem = this._ScriptPubKeyToRedeem.TryGet(redeem);
if(redeem == null)
{
if(hash is WitScriptId)
redeem = PayToWitScriptHashTemplate.Instance.ExtractWitScriptParameters(txIn.WitScript, (WitScriptId)hash);
if(hash is ScriptId)
{
var parameters = PayToScriptHashTemplate.Instance.ExtractScriptSigParameters(this.Network, txIn.ScriptSig, (ScriptId)hash);
PayToScriptHashSigParameters parameters = PayToScriptHashTemplate.Instance.ExtractScriptSigParameters(this.Network, txIn.ScriptSig, (ScriptId)hash);
if(parameters != null)
redeem = parameters.RedeemScript;
}
......@@ -1308,19 +1363,19 @@ namespace NBitcoin
{
if(tx == null)
throw new ArgumentNullException("tx");
var coins = tx.Inputs.Select(i => FindCoin(i.PrevOut)).Where(c => c != null).ToArray();
List<TransactionPolicyError> exceptions = new List<TransactionPolicyError>();
var policyErrors = MinerTransactionPolicy.Instance.Check(tx, coins);
ICoin[] coins = tx.Inputs.Select(i => FindCoin(i.PrevOut)).Where(c => c != null).ToArray();
var exceptions = new List<TransactionPolicyError>();
TransactionPolicyError[] policyErrors = MinerTransactionPolicy.Instance.Check(tx, coins);
exceptions.AddRange(policyErrors);
policyErrors = StandardTransactionPolicy.Check(tx, coins);
policyErrors = this.StandardTransactionPolicy.Check(tx, coins);
exceptions.AddRange(policyErrors);
if(expectedFees != null)
{
var fees = tx.GetFee(coins);
Money fees = tx.GetFee(coins);
if(fees != null)
{
Money margin = Money.Zero;
if(DustPrevention)
if(this.DustPrevention)
margin = GetDust() * 2;
if(!fees.Almost(expectedFees, margin))
exceptions.Add(new NotEnoughFundsPolicyError("Fees different than expected", expectedFees - fees));
......@@ -1382,9 +1437,9 @@ namespace NBitcoin
public ICoin FindCoin(OutPoint outPoint)
{
var result = _BuilderGroups.Select(c => c.Coins.TryGet(outPoint)).FirstOrDefault(r => r != null);
if(result == null && CoinFinder != null)
result = CoinFinder(outPoint);
ICoin result = this._BuilderGroups.Select(c => c.Coins.TryGet(outPoint)).FirstOrDefault(r => r != null);
if(result == null && this.CoinFinder != null)
result = this.CoinFinder(outPoint);
return result;
}
......@@ -1420,56 +1475,55 @@ namespace NBitcoin
/// <returns></returns>
public int EstimateSize(Transaction tx, bool virtualSize)
{
if(tx == null)
if (tx == null)
throw new ArgumentNullException("tx");
var clone = tx.Clone(network:this.Network);
Transaction clone = this.Network.CreateTransaction(tx.ToHex());
clone.Inputs.Clear();
var baseSize = clone.GetSerializedSize();
int baseSize = clone.GetSerializedSize();
int vSize = 0;
int size = baseSize;
if(tx.HasWitness)
vSize += 2;
foreach(var txin in tx.Inputs.AsIndexedInputs())
int witSize = 0;
if (tx.HasWitness)
witSize += 2;
foreach (var txin in tx.Inputs.AsIndexedInputs())
{
var coin = FindSignableCoin(txin) ?? FindCoin(txin.PrevOut);
if(coin == null)
ICoin coin = FindSignableCoin(txin) ?? FindCoin(txin.PrevOut);
if (coin == null)
throw CoinNotFound(txin);
EstimateScriptSigSize(coin, ref vSize, ref size);
size += 41;
EstimateScriptSigSize(coin, ref witSize, ref baseSize);
baseSize += 41;
}
return (virtualSize ? vSize / Transaction.WITNESS_SCALE_FACTOR + size : vSize + size);
return (virtualSize ? witSize / Transaction.WITNESS_SCALE_FACTOR + baseSize : witSize + baseSize);
}
private void EstimateScriptSigSize(ICoin coin, ref int vSize, ref int size)
private void EstimateScriptSigSize(ICoin coin, ref int witSize, ref int baseSize)
{
if(coin is IColoredCoin)
if (coin is IColoredCoin)
coin = ((IColoredCoin)coin).Bearer;
if(coin is ScriptCoin)
if (coin is ScriptCoin scriptCoin)
{
var scriptCoin = (ScriptCoin)coin;
var p2sh = scriptCoin.GetP2SHRedeem();
if(p2sh != null)
Script p2sh = scriptCoin.GetP2SHRedeem();
if (p2sh != null)
{
coin = new Coin(scriptCoin.Outpoint, new TxOut(scriptCoin.Amount, p2sh));
size += new Script(Op.GetPushOp(p2sh.ToBytes(true))).Length;
if(scriptCoin.RedeemType == RedeemType.WitnessV0)
baseSize += new Script(Op.GetPushOp(p2sh.ToBytes(true))).Length;
if (scriptCoin.RedeemType == RedeemType.WitnessV0)
{
coin = new ScriptCoin(coin, scriptCoin.Redeem);
}
}
if(scriptCoin.RedeemType == RedeemType.WitnessV0)
if (scriptCoin.RedeemType == RedeemType.WitnessV0)
{
vSize += new Script(Op.GetPushOp(scriptCoin.Redeem.ToBytes(true))).Length;
witSize += new Script(Op.GetPushOp(scriptCoin.Redeem.ToBytes(true))).Length;
}
}
var scriptPubkey = coin.GetScriptCode(this.Network);
var scriptSigSize = -1;
foreach(var extension in Extensions)
Script scriptPubkey = coin.GetScriptCode(this.Network);
int scriptSigSize = -1;
foreach(BuilderExtension extension in this.Extensions)
{
if(extension.CanEstimateScriptSigSize(this.Network, scriptPubkey))
{
......@@ -1478,12 +1532,13 @@ namespace NBitcoin
}
}
if(scriptSigSize == -1)
if (scriptSigSize == -1)
scriptSigSize += coin.TxOut.ScriptPubKey.Length; //Using heurestic to approximate size of unknown scriptPubKey
if(coin.GetHashVersion(this.Network) == HashVersion.Witness)
vSize += scriptSigSize + 1; //Account for the push
witSize += scriptSigSize + 1; //Account for the push
if(coin.GetHashVersion(this.Network) == HashVersion.Original)
size += scriptSigSize;
baseSize += scriptSigSize;
}
/// <summary>
......@@ -1496,15 +1551,15 @@ namespace NBitcoin
if(feeRate == null)
throw new ArgumentNullException("feeRate");
int builderCount = CurrentGroup.Builders.Count;
int builderCount = this.CurrentGroup.Builders.Count;
Money feeSent = Money.Zero;
try
{
while(true)
{
var tx = BuildTransaction(false);
var shouldSend = EstimateFees(tx, feeRate);
var delta = shouldSend - feeSent;
Transaction tx = BuildTransaction(false);
Money shouldSend = EstimateFees(tx, feeRate);
Money delta = shouldSend - feeSent;
if(delta <= Money.Zero)
break;
SendFees(delta);
......@@ -1513,9 +1568,9 @@ namespace NBitcoin
}
finally
{
while(CurrentGroup.Builders.Count != builderCount)
while(this.CurrentGroup.Builders.Count != builderCount)
{
CurrentGroup.Builders.RemoveAt(CurrentGroup.Builders.Count - 1);
this.CurrentGroup.Builders.RemoveAt(this.CurrentGroup.Builders.Count - 1);
}
this._TotalFee -= feeSent;
}
......@@ -1535,30 +1590,30 @@ namespace NBitcoin
if(feeRate == null)
throw new ArgumentNullException("feeRate");
var estimation = EstimateSize(tx, true);
int estimation = EstimateSize(tx, true);
return feeRate.GetFee(estimation);
}
private void Sign(TransactionSigningContext ctx, ICoin coin, IndexedTxIn txIn)
{
var input = txIn.TxIn;
TxIn input = txIn.TxIn;
if(coin is StealthCoin)
{
var stealthCoin = (StealthCoin)coin;
var scanKey = FindKey(ctx, stealthCoin.Address.ScanPubKey.ScriptPubKey);
Key scanKey = FindKey(ctx, stealthCoin.Address.ScanPubKey.ScriptPubKey);
if(scanKey == null)
throw new KeyNotFoundException("Scan key for decrypting StealthCoin not found");
var spendKeys = stealthCoin.Address.SpendPubKeys.Select(p => FindKey(ctx, p.ScriptPubKey)).Where(p => p != null).ToArray();
Key[] spendKeys = stealthCoin.Address.SpendPubKeys.Select(p => FindKey(ctx, p.ScriptPubKey)).Where(p => p != null).ToArray();
ctx.AdditionalKeys.AddRange(stealthCoin.Uncover(spendKeys, scanKey));
var normalCoin = new Coin(coin.Outpoint, coin.TxOut);
if(stealthCoin.Redeem != null)
normalCoin = normalCoin.ToScriptCoin(stealthCoin.Redeem);
coin = normalCoin;
}
var scriptSig = CreateScriptSig(ctx, coin, txIn);
Script scriptSig = CreateScriptSig(ctx, coin, txIn);
if(scriptSig == null)
return;
ScriptCoin scriptCoin = coin as ScriptCoin;
var scriptCoin = coin as ScriptCoin;
Script signatures = null;
if(coin.GetHashVersion(this.Network) == HashVersion.Witness)
......@@ -1608,19 +1663,19 @@ namespace NBitcoin
{
if(script == Script.Empty)
return script;
var ops = script.ToOps().ToArray();
Op[] ops = script.ToOps().ToArray();
return new Script(ops.Take(ops.Length - 1));
}
private Script CombineScriptSigs(ICoin coin, Script a, Script b)
{
var scriptPubkey = coin.GetScriptCode(this.Network);
Script scriptPubkey = coin.GetScriptCode(this.Network);
if(Script.IsNullOrEmpty(a))
return b ?? Script.Empty;
if(Script.IsNullOrEmpty(b))
return a ?? Script.Empty;
foreach(var extension in Extensions)
foreach(BuilderExtension extension in this.Extensions)
{
if(extension.CanCombineScriptSig(this.Network, scriptPubkey, a, b))
{
......@@ -1632,25 +1687,25 @@ namespace NBitcoin
private Script CreateScriptSig(TransactionSigningContext ctx, ICoin coin, IndexedTxIn txIn)
{
var scriptPubKey = coin.GetScriptCode(this.Network);
Script scriptPubKey = coin.GetScriptCode(this.Network);
var keyRepo = new TransactionBuilderKeyRepository(this, ctx);
var signer = new TransactionBuilderSigner(this, coin, ctx.SigHash, txIn);
var signer2 = new KnownSignatureSigner(this, _KnownSignatures, coin, ctx.SigHash, txIn);
var signer2 = new KnownSignatureSigner(this, this._KnownSignatures, coin, ctx.SigHash, txIn);
foreach(var extension in Extensions)
foreach(BuilderExtension extension in this.Extensions)
{
if(extension.CanGenerateScriptSig(this.Network, scriptPubKey))
{
var scriptSig1 = extension.GenerateScriptSig(this.Network, scriptPubKey, keyRepo, signer);
var scriptSig2 = extension.GenerateScriptSig(this.Network, scriptPubKey, signer2, signer2);
Script scriptSig1 = extension.GenerateScriptSig(this.Network, scriptPubKey, keyRepo, signer);
Script scriptSig2 = extension.GenerateScriptSig(this.Network, scriptPubKey, signer2, signer2);
if (scriptSig2 != null)
{
scriptSig2 = signer2.ReplaceDummyKeys(scriptSig2);
}
if (scriptSig1 != null && scriptSig2 != null && extension.CanCombineScriptSig(this.Network, scriptPubKey, scriptSig1, scriptSig2))
{
var combined = extension.CombineScriptSig(this.Network, scriptPubKey, scriptSig1, scriptSig2);
Script combined = extension.CombineScriptSig(this.Network, scriptPubKey, scriptSig1, scriptSig2);
return combined;
}
return scriptSig1 ?? scriptSig2;
......@@ -1660,16 +1715,16 @@ namespace NBitcoin
throw new NotSupportedException("Unsupported scriptPubKey");
}
List<Tuple<PubKey, ECDSASignature>> _KnownSignatures = new List<Tuple<PubKey, ECDSASignature>>();
private List<Tuple<PubKey, ECDSASignature>> _KnownSignatures = new List<Tuple<PubKey, ECDSASignature>>();
private Key FindKey(TransactionSigningContext ctx, Script scriptPubKey)
{
var key = _Keys
Key key = this._Keys
.Concat(ctx.AdditionalKeys)
.FirstOrDefault(k => IsCompatibleKey(k.PubKey, scriptPubKey));
if(key == null && KeyFinder != null)
if(key == null && this.KeyFinder != null)
{
key = KeyFinder(scriptPubKey);
key = this.KeyFinder(scriptPubKey);
}
return key;
}
......@@ -1688,7 +1743,7 @@ namespace NBitcoin
/// <returns></returns>
public TransactionBuilder Then()
{
_CurrentGroup = null;
this._CurrentGroup = null;
return this;
}
......@@ -1698,14 +1753,15 @@ namespace NBitcoin
/// <returns></returns>
public TransactionBuilder Then(string groupName)
{
var group = _BuilderGroups.FirstOrDefault(g => g.Name == groupName);
BuilderGroup group = this._BuilderGroups.FirstOrDefault(g => g.Name == groupName);
if(group == null)
{
group = new BuilderGroup(this);
_BuilderGroups.Add(group);
this._BuilderGroups.Add(group);
group.Name = groupName;
}
_CurrentGroup = group;
this._CurrentGroup = group;
return this;
}
......@@ -1716,12 +1772,12 @@ namespace NBitcoin
/// <returns></returns>
public TransactionBuilder CoverOnly(Money amount)
{
CurrentGroup.CoverOnly = amount;
this.CurrentGroup.CoverOnly = amount;
return this;
}
Transaction _CompletedTransaction;
private Transaction _CompletedTransaction;
/// <summary>
/// Allows to keep building on the top of a partially built transaction
......@@ -1730,9 +1786,11 @@ namespace NBitcoin
/// <returns></returns>
public TransactionBuilder ContinueToBuild(Transaction transaction)
{
if(_CompletedTransaction != null)
if(this._CompletedTransaction != null)
throw new InvalidOperationException("Transaction to complete already set");
_CompletedTransaction = transaction.Clone(network:this.Network);
this._CompletedTransaction = this.Network.CreateTransaction(transaction.ToHex());
return this;
}
......@@ -1742,12 +1800,12 @@ namespace NBitcoin
/// <returns></returns>
public TransactionBuilder CoverTheRest()
{
if(_CompletedTransaction == null)
if(this._CompletedTransaction == null)
throw new InvalidOperationException("A partially built transaction should be specified by calling ContinueToBuild");
var spent = _CompletedTransaction.Inputs.AsIndexedInputs().Select(txin =>
Money spent = this._CompletedTransaction.Inputs.AsIndexedInputs().Select(txin =>
{
var c = FindCoin(txin.PrevOut);
ICoin c = FindCoin(txin.PrevOut);
if(c == null)
throw CoinNotFound(txin);
if(!(c is Coin))
......@@ -1758,8 +1816,8 @@ namespace NBitcoin
.Select(c => c.Amount)
.Sum();
var toComplete = _CompletedTransaction.TotalOut - spent;
CurrentGroup.Builders.Add(ctx =>
Money toComplete = this._CompletedTransaction.TotalOut - spent;
this.CurrentGroup.Builders.Add(ctx =>
{
if(toComplete < Money.Zero)
return Money.Zero;
......@@ -1770,19 +1828,19 @@ namespace NBitcoin
public TransactionBuilder AddCoins(Transaction transaction)
{
var txId = transaction.GetHash();
uint256 txId = transaction.GetHash();
AddCoins(transaction.Outputs.Select((o, i) => new Coin(txId, (uint)i, o.Value, o.ScriptPubKey)).ToArray());
return this;
}
Dictionary<Script, Script> _ScriptPubKeyToRedeem = new Dictionary<Script, Script>();
private Dictionary<Script, Script> _ScriptPubKeyToRedeem = new Dictionary<Script, Script>();
public TransactionBuilder AddKnownRedeems(params Script[] knownRedeems)
{
foreach(var redeem in knownRedeems)
foreach(Script redeem in knownRedeems)
{
_ScriptPubKeyToRedeem.AddOrReplace(redeem.WitHash.ScriptPubKey.Hash.ScriptPubKey, redeem); //Might be P2SH(PWSH)
_ScriptPubKeyToRedeem.AddOrReplace(redeem.Hash.ScriptPubKey, redeem); //Might be P2SH
_ScriptPubKeyToRedeem.AddOrReplace(redeem.WitHash.ScriptPubKey, redeem); //Might be PWSH
this._ScriptPubKeyToRedeem.AddOrReplace(redeem.WitHash.ScriptPubKey.Hash.ScriptPubKey, redeem); //Might be P2SH(PWSH)
this._ScriptPubKeyToRedeem.AddOrReplace(redeem.Hash.ScriptPubKey, redeem); //Might be P2SH
this._ScriptPubKeyToRedeem.AddOrReplace(redeem.WitHash.ScriptPubKey, redeem); //Might be PWSH
}
return this;
}
......@@ -1791,25 +1849,25 @@ namespace NBitcoin
{
if(transactions.Length == 1)
return transactions[0];
if(transactions.Length == 0)
return null;
Transaction tx = transactions[0].Clone(network: this.Network);
Transaction tx = this.Network.CreateTransaction(transactions[0].ToHex());
for(int i = 1; i < transactions.Length; i++)
{
var signed = transactions[i];
Transaction signed = transactions[i];
tx = CombineSignaturesCore(tx, signed);
}
return tx;
}
private readonly List<BuilderExtension> _Extensions = new List<BuilderExtension>();
public List<BuilderExtension> Extensions
{
get
{
return _Extensions;
return this._Extensions;
}
}
......@@ -1817,31 +1875,33 @@ namespace NBitcoin
{
if(signed1 == null)
return signed2;
if(signed2 == null)
return signed1;
var tx = signed1.Clone(network: this.Network);
Transaction tx = this.Network.CreateTransaction(signed1.ToHex());
for(int i = 0; i < tx.Inputs.Count; i++)
{
if(i >= signed2.Inputs.Count)
break;
var txIn = tx.Inputs[i];
TxIn txIn = tx.Inputs[i];
var coin = FindCoin(txIn.PrevOut);
var scriptPubKey = coin == null
ICoin coin = FindCoin(txIn.PrevOut);
Script scriptPubKey = coin == null
? (DeduceScriptPubKey(txIn.ScriptSig) ?? DeduceScriptPubKey(signed2.Inputs[i].ScriptSig))
: coin.TxOut.ScriptPubKey;
Money amount = null;
if(coin != null)
amount = coin is IColoredCoin ? ((IColoredCoin)coin).Bearer.Amount : ((Coin)coin).Amount;
var result = Script.CombineSignatures(
ScriptSigs result = Script.CombineSignatures(
this.Network,
scriptPubKey,
new TransactionChecker(tx, i, amount),
GetScriptSigs(signed1.Inputs.AsIndexedInputs().Skip(i).First()),
GetScriptSigs(signed2.Inputs.AsIndexedInputs().Skip(i).First()));
var input = tx.Inputs.AsIndexedInputs().Skip(i).First();
IndexedTxIn input = tx.Inputs.AsIndexedInputs().Skip(i).First();
input.WitScript = result.WitSig;
input.ScriptSig = result.ScriptSig;
}
......@@ -1859,12 +1919,12 @@ namespace NBitcoin
private Script DeduceScriptPubKey(Script scriptSig)
{
var p2sh = PayToScriptHashTemplate.Instance.ExtractScriptSigParameters(this.Network, scriptSig);
PayToScriptHashSigParameters p2sh = PayToScriptHashTemplate.Instance.ExtractScriptSigParameters(this.Network, scriptSig);
if(p2sh != null && p2sh.RedeemScript != null)
{
return p2sh.RedeemScript.Hash.ScriptPubKey;
}
foreach(var extension in Extensions)
foreach(BuilderExtension extension in this.Extensions)
{
if(extension.CanDeduceScriptPubKey(this.Network, scriptSig))
{
......@@ -1880,8 +1940,8 @@ namespace NBitcoin
public CoinNotFoundException(IndexedTxIn txIn)
: base("No coin matching " + txIn.PrevOut + " was found")
{
_OutPoint = txIn.PrevOut;
_InputIndex = txIn.Index;
this._OutPoint = txIn.PrevOut;
this._InputIndex = txIn.Index;
}
private readonly OutPoint _OutPoint;
......@@ -1889,7 +1949,7 @@ namespace NBitcoin
{
get
{
return _OutPoint;
return this._OutPoint;
}
}
......@@ -1898,7 +1958,7 @@ namespace NBitcoin
{
get
{
return _InputIndex;
return this._InputIndex;
}
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment