Commit c30cdaab authored by Sergei Zubov's avatar Sergei Zubov

Merge wallet

parent 3854e8a3
......@@ -9,6 +9,7 @@ using Stratis.Bitcoin.Features.Wallet.Interfaces;
using Stratis.Bitcoin.Features.Wallet.Models;
using Stratis.Bitcoin.Utilities;
using Stratis.Bitcoin.Utilities.JsonErrors;
using Stratis.Bitcoin.Utilities.ModelStateErrors;
namespace Stratis.Bitcoin.Features.Wallet.Controllers
{
......@@ -19,15 +20,16 @@ namespace Stratis.Bitcoin.Features.Wallet.Controllers
public class DeStreamWalletController : Controller
{
private readonly ILogger _logger;
private readonly Network _network;
private readonly DeStreamNetwork _network;
private readonly IWalletTransactionHandler _walletTransactionHandler;
public DeStreamWalletController(Network network, IWalletTransactionHandler walletTransactionHandler,
ILoggerFactory loggerFactory)
{
this._network = network;
this._network = (DeStreamNetwork) network ??
throw new NotSupportedException($"Network must be {nameof(NBitcoin.DeStreamNetwork)}");
this._walletTransactionHandler = walletTransactionHandler;
this._logger = loggerFactory.CreateLogger(this.GetType().FullName);
this._logger = loggerFactory.CreateLogger(GetType().FullName);
}
/// <summary>
......@@ -42,24 +44,36 @@ namespace Stratis.Bitcoin.Features.Wallet.Controllers
Guard.NotNull(request, nameof(request));
// checks the request is valid
if (!this.ModelState.IsValid) return WalletController.BuildErrorResponse(this.ModelState);
if (!this.ModelState.IsValid) return ModelStateErrors.BuildErrorResponse(this.ModelState);
try
{
Script destination = BitcoinAddress.Create(request.DestinationAddress, this._network).ScriptPubKey;
var context = new TransactionBuildContext(
new WalletAccountReference(request.WalletName, request.AccountName),
new[] {new Recipient {Amount = request.Amount, ScriptPubKey = destination}}.ToList(),
request.Password, request.OpReturnData)
var recipients = new List<Recipient>();
foreach (RecipientModel recipientModel in request.Recipients)
recipients.Add(new Recipient
{
ScriptPubKey = BitcoinAddress.Create(recipientModel.DestinationAddress, this._network)
.ScriptPubKey,
Amount = recipientModel.Amount
});
var context = new DeStreamTransactionBuildContext(new DeStreamTransactionBuilder(this._network))
{
AccountReference = new WalletAccountReference(request.WalletName, request.AccountName),
TransactionFee = string.IsNullOrEmpty(request.FeeAmount) ? null : Money.Parse(request.FeeAmount),
MinConfirmations = request.AllowUnconfirmed ? 0 : 1,
Shuffle =
request.ShuffleOutputs ??
true // We shuffle transaction outputs by default as it's better for anonymity.
true, // We shuffle transaction outputs by default as it's better for anonymity.
OpReturnData = request.OpReturnData,
WalletPassword = request.Password,
SelectedInputs =
request.Outpoints?.Select(u => new OutPoint(uint256.Parse(u.TransactionId), u.Index)).ToList(),
AllowOtherInputs = false,
Recipients = recipients
};
this.ProcessFeeType(request.FeeType, context.Recipients);
ProcessFeeType(request.FeeType, context.Recipients);
Transaction transactionResult = this._walletTransactionHandler.BuildTransaction(context);
......@@ -70,7 +84,7 @@ namespace Stratis.Bitcoin.Features.Wallet.Controllers
TransactionId = transactionResult.GetHash()
};
return this.Json(model);
return Json(model);
}
catch (Exception e)
{
......@@ -93,21 +107,30 @@ namespace Stratis.Bitcoin.Features.Wallet.Controllers
Guard.NotNull(request, nameof(request));
// checks the request is valid
if (!this.ModelState.IsValid) return WalletController.BuildErrorResponse(this.ModelState);
if (!this.ModelState.IsValid) return ModelStateErrors.BuildErrorResponse(this.ModelState);
try
{
Script destination = BitcoinAddress.Create(request.DestinationAddress, this._network).ScriptPubKey;
var context = new TransactionBuildContext(
new WalletAccountReference(request.WalletName, request.AccountName),
new[] {new Recipient {Amount = request.Amount, ScriptPubKey = destination}}.ToList())
var recipients = new List<Recipient>();
foreach (RecipientModel recipientModel in request.Recipients)
recipients.Add(new Recipient
{
ScriptPubKey = BitcoinAddress.Create(recipientModel.DestinationAddress, this._network)
.ScriptPubKey,
Amount = recipientModel.Amount
});
var context = new TransactionBuildContext(this._network)
{
MinConfirmations = request.AllowUnconfirmed ? 0 : 1
AccountReference = new WalletAccountReference(request.WalletName, request.AccountName),
FeeType = FeeParser.Parse(request.FeeType),
MinConfirmations = request.AllowUnconfirmed ? 0 : 1,
Recipients = recipients
};
this.ProcessFeeType(request.FeeType, context.Recipients);
ProcessFeeType(request.FeeType, context.Recipients);
return this.Json(this._walletTransactionHandler.EstimateFee(context));
return Json(this._walletTransactionHandler.EstimateFee(context));
}
catch (Exception e)
{
......@@ -134,7 +157,9 @@ namespace Stratis.Bitcoin.Features.Wallet.Controllers
recipient.Amount = this._network.SubtractFee(recipient.Amount);
}
else
{
throw new FormatException($"FeeType {requestFeeType} is not a valid DeStreamFeeType");
}
}
}
}
\ No newline at end of file
......@@ -34,7 +34,7 @@ namespace Stratis.Bitcoin.Features.Wallet.Controllers
private readonly CoinType coinType;
/// <summary>Specification of the network the node runs on - regtest/testnet/mainnet.</summary>
/// <summary>Specification of the network the node runs on - regtest/testnet/mainnet.</summary>summary
private readonly Network network;
private readonly IConnectionManager connectionManager;
......
......@@ -5,6 +5,7 @@ using Microsoft.Extensions.Logging;
using NBitcoin;
using Stratis.Bitcoin.Configuration;
using Stratis.Bitcoin.Features.Wallet.Interfaces;
using Stratis.Bitcoin.Interfaces;
using Stratis.Bitcoin.Utilities;
namespace Stratis.Bitcoin.Features.Wallet
......@@ -13,12 +14,11 @@ namespace Stratis.Bitcoin.Features.Wallet
public class DeStreamWalletManager : WalletManager, IDeStreamWalletManager
{
public DeStreamWalletManager(ILoggerFactory loggerFactory, Network network, ConcurrentChain chain,
NodeSettings settings, WalletSettings walletSettings,
DataFolder dataFolder, IWalletFeePolicy walletFeePolicy, IAsyncLoopFactory asyncLoopFactory,
INodeLifetime nodeLifetime, IDateTimeProvider dateTimeProvider,
IBroadcasterManager broadcasterManager = null) :
base(loggerFactory, network, chain, settings, walletSettings, dataFolder, walletFeePolicy, asyncLoopFactory,
nodeLifetime, dateTimeProvider, broadcasterManager)
WalletSettings walletSettings, DataFolder dataFolder, IWalletFeePolicy walletFeePolicy,
IAsyncLoopFactory asyncLoopFactory, INodeLifetime nodeLifetime, IDateTimeProvider dateTimeProvider,
IScriptAddressReader scriptAddressReader, IBroadcasterManager broadcasterManager = null) : base(
loggerFactory, network, chain, walletSettings, dataFolder, walletFeePolicy, asyncLoopFactory, nodeLifetime,
dateTimeProvider, scriptAddressReader, broadcasterManager)
{
}
......@@ -27,7 +27,7 @@ namespace Stratis.Bitcoin.Features.Wallet
{
Wallet result = base.LoadWallet(password, name);
this.LoadKeysLookupLock();
LoadKeysLookupLock();
return result;
}
......@@ -37,12 +37,10 @@ namespace Stratis.Bitcoin.Features.Wallet
{
foreach (var transactionWithOutput in this.network.GetGenesis().Transactions.SelectMany(p =>
p.Outputs.Select(q => new {Transaction = p, Output = q}).Where(q =>
this.keysLookup.TryGetValue(q.Output.ScriptPubKey, out HdAddress _))))
{
this.AddTransactionToWallet(transactionWithOutput.Transaction, transactionWithOutput.Output, 0,
this.scriptToAddressLookup.TryGetValue(q.Output.ScriptPubKey, out HdAddress _))))
AddTransactionToWallet(transactionWithOutput.Transaction, transactionWithOutput.Output, 0,
this.network.GetGenesis());
}
}
protected override void AddSpendingTransactionToWallet(Transaction transaction,
IEnumerable<TxOut> paidToOutputs, uint256 spendingTransactionId,
......@@ -51,12 +49,9 @@ namespace Stratis.Bitcoin.Features.Wallet
Guard.NotNull(transaction, nameof(transaction));
Guard.NotNull(paidToOutputs, nameof(paidToOutputs));
this.logger.LogTrace("({0}:'{1}',{2}:'{3}',{4}:{5},{6}:'{7}')", nameof(transaction), transaction.GetHash(),
nameof(spendingTransactionId), spendingTransactionId, nameof(spendingTransactionIndex),
spendingTransactionIndex, nameof(blockHeight), blockHeight);
// Get the transaction being spent.
TransactionData spentTransaction = this.keysLookup.Values.Distinct().SelectMany(v => v.Transactions)
TransactionData spentTransaction = this.scriptToAddressLookup.Values.Distinct()
.SelectMany(v => v.Transactions)
.SingleOrDefault(t => t.Id == spendingTransactionId && t.Index == spendingTransactionIndex);
if (spentTransaction == null)
{
......@@ -64,6 +59,7 @@ namespace Stratis.Bitcoin.Features.Wallet
this.logger.LogTrace("(-)[TX_NULL]");
return;
}
this.logger.LogTrace(spentTransaction.SpendingDetails == null
? $"Spending UTXO '{spendingTransactionId}-{spendingTransactionIndex}' is new."
: $"Spending transaction ID '{spendingTransactionId}' is being confirmed, updating.");
......@@ -72,27 +68,11 @@ namespace Stratis.Bitcoin.Features.Wallet
foreach (TxOut paidToOutput in paidToOutputs)
{
// Figure out how to retrieve the destination address.
string destinationAddress = string.Empty;
ScriptTemplate scriptTemplate = paidToOutput.ScriptPubKey.FindTemplate(this.network);
switch (scriptTemplate.Type)
{
// Pay to PubKey can be found in outputs of staking transactions.
case TxOutType.TX_PUBKEY:
PubKey pubKey =
PayToPubkeyTemplate.Instance.ExtractScriptPubKeyParameters(paidToOutput.ScriptPubKey);
destinationAddress = pubKey.GetAddress(this.network).ToString();
break;
// Pay to PubKey hash is the regular, most common type of output.
case TxOutType.TX_PUBKEYHASH:
destinationAddress = paidToOutput.ScriptPubKey.GetDestinationAddress(this.network).ToString();
break;
case TxOutType.TX_NONSTANDARD:
case TxOutType.TX_SCRIPTHASH:
case TxOutType.TX_MULTISIG:
case TxOutType.TX_NULL_DATA:
case TxOutType.TX_SEGWIT:
break;
}
string destinationAddress =
this.scriptAddressReader.GetAddressFromScriptPubKey(this.network, paidToOutput.ScriptPubKey);
if (destinationAddress == string.Empty)
if (this.scriptToAddressLookup.TryGetValue(paidToOutput.ScriptPubKey, out HdAddress destination))
destinationAddress = destination.Address;
payments.Add(new PaymentDetails
{
......@@ -114,8 +94,6 @@ namespace Stratis.Bitcoin.Features.Wallet
spentTransaction.SpendingDetails = spendingDetails;
spentTransaction.MerkleProof = null;
this.logger.LogTrace("(-)");
}
}
}
\ No newline at end of file
......@@ -2,6 +2,7 @@
using NBitcoin;
using Stratis.Bitcoin.Features.BlockStore;
using Stratis.Bitcoin.Features.Wallet.Interfaces;
using Stratis.Bitcoin.Interfaces;
using Stratis.Bitcoin.Utilities;
namespace Stratis.Bitcoin.Features.Wallet
......@@ -11,9 +12,9 @@ namespace Stratis.Bitcoin.Features.Wallet
private readonly IDeStreamWalletManager _deStreamWalletManager;
public DeStreamWalletSyncManager(ILoggerFactory loggerFactory, IDeStreamWalletManager walletManager,
ConcurrentChain chain, Network network, IBlockStoreCache blockStoreCache, StoreSettings storeSettings,
INodeLifetime nodeLifetime) : base(loggerFactory, walletManager, chain, network, blockStoreCache,
storeSettings, nodeLifetime)
ConcurrentChain chain, Network network, IBlockStore blockStore, StoreSettings storeSettings,
INodeLifetime nodeLifetime) : base(loggerFactory, walletManager, chain, network, blockStore, storeSettings,
nodeLifetime)
{
this._deStreamWalletManager = walletManager;
}
......
......@@ -5,7 +5,7 @@ using System.Security;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
using NBitcoin;
using Stratis.Bitcoin.Features.Consensus;
using NBitcoin.Policy;
using Stratis.Bitcoin.Features.Wallet.Interfaces;
using Stratis.Bitcoin.Utilities;
using Stratis.Bitcoin.Utilities.Extensions;
......@@ -15,15 +15,25 @@ namespace Stratis.Bitcoin.Features.Wallet
public class DeStreamWalletTransactionHandler : WalletTransactionHandler
{
public DeStreamWalletTransactionHandler(ILoggerFactory loggerFactory, IWalletManager walletManager,
IWalletFeePolicy walletFeePolicy, Network network) : base(loggerFactory, walletManager, walletFeePolicy,
network)
IWalletFeePolicy walletFeePolicy, Network network, StandardTransactionPolicy transactionPolicy) : base(
loggerFactory, walletManager, walletFeePolicy, network, transactionPolicy)
{
}
private DeStreamNetwork DeStreamNetwork
{
get
{
if (!(this.network is DeStreamNetwork))
throw new NotSupportedException($"Network must be {nameof(NBitcoin.DeStreamNetwork)}");
return (DeStreamNetwork) this.network;
}
}
/// <inheritdoc />
protected override void AddFee(TransactionBuildContext context)
{
long fee = Convert.ToInt64(context.Recipients.Sum(p => p.Amount) * this.Network.FeeRate);
long fee = Convert.ToInt64(context.Recipients.Sum(p => p.Amount) * this.DeStreamNetwork.FeeRate);
context.TransactionFee = fee;
context.TransactionBuilder.SendFees(fee);
}
......@@ -56,16 +66,16 @@ namespace Stratis.Bitcoin.Features.Wallet
// Here we try to create a transaction that contains all the spendable coins, leaving no room for the fee.
// When the transaction builder throws an exception informing us that we have insufficient funds,
// we use the amount we're missing as the fee.
var context = new TransactionBuildContext(accountReference, recipients, null)
var context =
new DeStreamTransactionBuildContext(new DeStreamTransactionBuilder(this.DeStreamNetwork))
{
FeeType = feeType,
MinConfirmations = allowUnconfirmed ? 0 : 1,
TransactionBuilder = new DeStreamTransactionBuilder(this.Network)
MinConfirmations = allowUnconfirmed ? 0 : 1
};
this.AddRecipients(context);
this.AddCoins(context);
this.AddFee(context);
AddRecipients(context);
AddCoins(context);
AddFee(context);
// Throw an exception if this code is reached, as building a transaction without any funds for the fee should always throw an exception.
throw new WalletException(
......@@ -97,6 +107,9 @@ namespace Stratis.Bitcoin.Features.Wallet
}
else
{
if (string.IsNullOrEmpty(context.WalletPassword))
return;
privateKey = Key.Parse(wallet.EncryptedSeed, context.WalletPassword, wallet.Network);
this.privateKeyCache.Set(cacheKey, privateKey.ToString(wallet.Network).ToSecureString(),
new TimeSpan(0, 5, 0));
......@@ -143,8 +156,8 @@ namespace Stratis.Bitcoin.Features.Wallet
// Add all coinstake transactions with enough confirmations
context.UnspentOutputs.AddRange(this.walletManager
.GetSpendableTransactionsInAccount(context.AccountReference,
this.Network.Consensus.Option<PosConsensusOptions>()
.GetStakeMinConfirmations(this.walletManager.LastBlockHeight(), this.Network))
((PosConsensusOptions) this.DeStreamNetwork.Consensus.Options)
.GetStakeMinConfirmations(this.walletManager.LastBlockHeight(), this.DeStreamNetwork))
.Where(p => p.Transaction.IsCoinStake ?? false)
.ToList());
......@@ -154,6 +167,9 @@ namespace Stratis.Bitcoin.Features.Wallet
.FirstOrDefault(p => p.Transaction.IsCoinStake ?? false)
?.Address.Pubkey;
context.UnspentOutputs = this.walletManager
.GetSpendableTransactionsInAccount(context.AccountReference, context.MinConfirmations).ToList();
if (context.UnspentOutputs.Count == 0) throw new WalletException("No spendable transactions found.");
// Get total spendable balance in the account.
......@@ -162,7 +178,7 @@ namespace Stratis.Bitcoin.Features.Wallet
if (balance < totalToSend)
throw new WalletException("Not enough funds.");
if (context.SelectedInputs.Any())
if (context.SelectedInputs != null && context.SelectedInputs.Any())
{
// 'SelectedInputs' are inputs that must be included in the
// current transaction. At this point we check the given
......@@ -176,14 +192,10 @@ namespace Stratis.Bitcoin.Features.Wallet
throw new WalletException("Not all the selected inputs were found on the wallet.");
if (!context.AllowOtherInputs)
{
foreach (KeyValuePair<OutPoint, UnspentOutputReference> unspentOutputsItem in availableHashList)
{
if (!context.SelectedInputs.Contains(unspentOutputsItem.Key))
context.UnspentOutputs.Remove(unspentOutputsItem.Value);
}
}
}
Money sum = 0;
int index = 0;
......@@ -197,7 +209,7 @@ namespace Stratis.Bitcoin.Features.Wallet
// If threshold is reached and the total value is above the target
// then its safe to stop adding UTXOs to the coin list.
// The primery goal is to reduce the time it takes to build a trx
// The primary goal is to reduce the time it takes to build a trx
// when the wallet is bloated with UTXOs.
if (index > SendCountThresholdLimit && sum > totalToSend)
break;
......@@ -211,22 +223,17 @@ namespace Stratis.Bitcoin.Features.Wallet
context.TransactionBuilder.AddCoins(coins);
}
}
/// <inheritdoc />
protected override void InitializeTransactionBuilder(TransactionBuildContext context)
public class DeStreamTransactionBuildContext : TransactionBuildContext
{
Guard.NotNull(context, nameof(context));
Guard.NotNull(context.Recipients, nameof(context.Recipients));
Guard.NotNull(context.AccountReference, nameof(context.AccountReference));
context.TransactionBuilder = new DeStreamTransactionBuilder(this.Network);
private DeStreamTransactionBuildContext(Network network) : base(network)
{
this.TransactionBuilder = new DeStreamTransactionBuilder(network);
}
this.AddRecipients(context);
this.AddOpReturnOutput(context);
this.AddCoins(context);
this.FindChangeAddress(context);
this.AddSecrets(context);
this.AddFee(context);
public DeStreamTransactionBuildContext(TransactionBuilder transactionBuilder) : base(transactionBuilder)
{
}
}
}
\ No newline at end of file
......@@ -63,7 +63,7 @@ namespace Stratis.Bitcoin.Features.Wallet
private readonly INodeLifetime nodeLifetime;
/// <summary>Instance logger.</summary>
private readonly ILogger logger;
protected readonly ILogger logger;
/// <summary>An object capable of storing <see cref="Wallet"/>s to the file system.</summary>
private readonly FileStorage<Wallet> fileStorage;
......@@ -75,10 +75,10 @@ namespace Stratis.Bitcoin.Features.Wallet
private readonly IDateTimeProvider dateTimeProvider;
/// <summary>The settings for the wallet feature.</summary>
private readonly WalletSettings walletSettings;
protected readonly WalletSettings walletSettings;
/// <summary>The settings for the wallet feature.</summary>
private readonly IScriptAddressReader scriptAddressReader;
protected readonly IScriptAddressReader scriptAddressReader;
public uint256 WalletTipHash { get; set; }
......@@ -268,7 +268,7 @@ namespace Stratis.Bitcoin.Features.Wallet
}
/// <inheritdoc />
public Wallet LoadWallet(string password, string name)
public virtual Wallet LoadWallet(string password, string name)
{
Guard.NotEmpty(password, nameof(password));
Guard.NotEmpty(name, nameof(name));
......@@ -938,7 +938,7 @@ namespace Stratis.Bitcoin.Features.Wallet
/// <param name="blockHeight">Height of the block.</param>
/// <param name="block">The block containing the transaction to add.</param>
/// <param name="isPropagated">Propagation state of the transaction.</param>
private void AddTransactionToWallet(Transaction transaction, TxOut utxo, int? blockHeight = null, Block block = null, bool isPropagated = true)
protected void AddTransactionToWallet(Transaction transaction, TxOut utxo, int? blockHeight = null, Block block = null, bool isPropagated = true)
{
Guard.NotNull(transaction, nameof(transaction));
Guard.NotNull(utxo, nameof(utxo));
......@@ -1022,7 +1022,7 @@ namespace Stratis.Bitcoin.Features.Wallet
/// <param name="spendingTransactionIndex">The index of the output in the transaction being referenced, if this is a spending transaction.</param>
/// <param name="blockHeight">Height of the block.</param>
/// <param name="block">The block containing the transaction to add.</param>
private void AddSpendingTransactionToWallet(Transaction transaction, IEnumerable<TxOut> paidToOutputs,
protected virtual void AddSpendingTransactionToWallet(Transaction transaction, IEnumerable<TxOut> paidToOutputs,
uint256 spendingTransactionId, int? spendingTransactionIndex, int? blockHeight = null, Block block = null)
{
Guard.NotNull(transaction, nameof(transaction));
......
......@@ -19,7 +19,7 @@ namespace Stratis.Bitcoin.Features.Wallet
private readonly ConcurrentChain chain;
/// <summary>Instance logger.</summary>
private readonly ILogger logger;
protected readonly ILogger logger;
private readonly IBlockStore blockStore;
......
......@@ -30,17 +30,17 @@ namespace Stratis.Bitcoin.Features.Wallet
/// 500 is a safe number that if reached ensures the coin selector will not take too long to complete,
/// most regular wallets will never reach such a high number of UTXO.
/// </remarks>
private const int SendCountThresholdLimit = 500;
protected const int SendCountThresholdLimit = 500;
private readonly ILogger logger;
private readonly Network network;
protected readonly Network network;
private readonly MemoryCache privateKeyCache;
protected readonly MemoryCache privateKeyCache;
protected readonly StandardTransactionPolicy TransactionPolicy;
private readonly IWalletManager walletManager;
protected readonly IWalletManager walletManager;
private readonly IWalletFeePolicy walletFeePolicy;
......@@ -129,7 +129,7 @@ namespace Stratis.Bitcoin.Features.Wallet
}
/// <inheritdoc />
public (Money maximumSpendableAmount, Money Fee) GetMaximumSpendableAmount(WalletAccountReference accountReference, FeeType feeType, bool allowUnconfirmed)
public virtual (Money maximumSpendableAmount, Money Fee) GetMaximumSpendableAmount(WalletAccountReference accountReference, FeeType feeType, bool allowUnconfirmed)
{
Guard.NotNull(accountReference, nameof(accountReference));
Guard.NotEmpty(accountReference.WalletName, nameof(accountReference.WalletName));
......@@ -235,8 +235,8 @@ namespace Stratis.Bitcoin.Features.Wallet
this.AddRecipients(context);
this.AddOpReturnOutput(context);
this.AddCoins(context);
this.AddSecrets(context);
this.FindChangeAddress(context);
this.AddSecrets(context);
this.AddFee(context);
}
......@@ -244,7 +244,7 @@ namespace Stratis.Bitcoin.Features.Wallet
/// Load's all the private keys for each of the <see cref="HdAddress"/> in <see cref="TransactionBuildContext.UnspentOutputs"/>
/// </summary>
/// <param name="context">The context associated with the current transaction being built.</param>
protected void AddSecrets(TransactionBuildContext context)
protected virtual void AddSecrets(TransactionBuildContext context)
{
if (!context.Sign)
return;
......@@ -303,7 +303,7 @@ namespace Stratis.Bitcoin.Features.Wallet
/// Then add them to the <see cref="TransactionBuildContext.UnspentOutputs"/>.
/// </summary>
/// <param name="context">The context associated with the current transaction being built.</param>
protected void AddCoins(TransactionBuildContext context)
protected virtual void AddCoins(TransactionBuildContext context)
{
context.UnspentOutputs = this.walletManager.GetSpendableTransactionsInAccount(context.AccountReference, context.MinConfirmations).ToList();
......@@ -389,7 +389,7 @@ namespace Stratis.Bitcoin.Features.Wallet
/// Use the <see cref="FeeRate"/> from the <see cref="walletFeePolicy"/>.
/// </summary>
/// <param name="context">The context associated with the current transaction being built.</param>
protected void AddFee(TransactionBuildContext context)
protected virtual void AddFee(TransactionBuildContext context)
{
Money fee;
Money minTrxFee = new Money(this.network.MinTxFee, MoneyUnit.Satoshi);
......@@ -449,6 +449,18 @@ namespace Stratis.Bitcoin.Features.Wallet
this.Sign = true;
}
protected TransactionBuildContext(TransactionBuilder transactionBuilder)
{
this.TransactionBuilder = transactionBuilder;
this.Recipients = new List<Recipient>();
this.WalletPassword = string.Empty;
this.FeeType = FeeType.Medium;
this.MinConfirmations = 1;
this.SelectedInputs = new List<OutPoint>();
this.AllowOtherInputs = false;
this.Sign = true;
}
/// <summary>
/// The wallet account to use for building a transaction.
/// </summary>
......@@ -480,7 +492,7 @@ namespace Stratis.Bitcoin.Features.Wallet
/// <summary>
/// The builder used to build the current transaction.
/// </summary>
public readonly TransactionBuilder TransactionBuilder;
public TransactionBuilder TransactionBuilder;
/// <summary>
/// The change address, where any remaining funds will be sent to.
......
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