Commit 6c9f3ea7 authored by Sergei Zubov's avatar Sergei Zubov

Merge wallet

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