Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Blockcore.Features.Wallet;
using Blockcore.Features.Wallet.Interfaces;
using Blockcore.Interfaces;
using Blockcore.Signals;
using Blockcore.Utilities;
using Microsoft.Extensions.Logging;
using NBitcoin;
Expand Down Expand Up @@ -79,6 +80,7 @@ public ColdStakingManager(
IScriptAddressReader scriptAddressReader,
ILoggerFactory loggerFactory,
IDateTimeProvider dateTimeProvider,
ISignals signals = null,
IBroadcasterManager broadcasterManager = null) : base(
loggerFactory,
network,
Expand All @@ -90,6 +92,7 @@ public ColdStakingManager(
nodeLifeTime,
dateTimeProvider,
scriptAddressReader,
signals,
broadcasterManager
)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ protected override IEnumerable<IClientEvent> GetMessages()
SpendableAmount = balance.SpendableAmount,
Addresses = account.GetCombinedAddresses().Select(address =>
{
(Money confirmedAmount, Money unConfirmedAmount) = address.GetBalances();
(Money confirmedAmount, Money unConfirmedAmount) = address.GetBalances(account.IsNormalAccount());
return new AddressModel
{
Address = address.Address,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -661,7 +661,7 @@ public IActionResult GetBalance([FromQuery] WalletBalanceRequest request)
SpendableAmount = balance.SpendableAmount,
Addresses = account.GetCombinedAddresses().Select(address =>
{
(Money confirmedAmount, Money unConfirmedAmount) = address.GetBalances();
(Money confirmedAmount, Money unConfirmedAmount) = address.GetBalances(account.IsNormalAccount());
return new AddressModel
{
Address = address.Address,
Expand Down Expand Up @@ -1197,7 +1197,7 @@ public IActionResult GetAllAddresses([FromQuery]GetAllAddressesModel request)
{
Addresses = account.GetCombinedAddresses().Select(address =>
{
(Money confirmedAmount, Money unConfirmedAmount) = address.GetBalances();
(Money confirmedAmount, Money unConfirmedAmount) = address.GetBalances(account.IsNormalAccount());

return new AddressModel
{
Expand Down Expand Up @@ -1271,19 +1271,20 @@ public IActionResult RemoveTransactions([FromQuery]RemoveTransactionsModel reque
}

// If the user chose to resync the wallet after removing transactions.
if (result.Any() && request.ReSync)
if (request.ReSync)
{
// From the list of removed transactions, check which one is the oldest and retrieve the block right before that time.
DateTimeOffset earliestDate = result.Min(r => r.creationTime);
ChainedHeader chainedHeader = this.chainIndexer.GetHeader(this.chainIndexer.GetHeightAtTime(earliestDate.DateTime));
Wallet wallet = this.walletManager.GetWallet(request.WalletName);

// Initiate the scan one day ahead of wallet creation.
// If the creation time is DateTime.MinValue, don't remove one day as that throws exception.
ChainedHeader chainedHeader = this.chainIndexer.GetHeader(this.chainIndexer.GetHeightAtTime(wallet.CreationTime.DateTime != DateTime.MinValue ? wallet.CreationTime.DateTime.AddDays(-1) : wallet.CreationTime.DateTime));

// Update the wallet and save it to the file system.
Wallet wallet = this.walletManager.GetWallet(request.WalletName);
wallet.SetLastBlockDetails(chainedHeader);
this.walletManager.SaveWallet(wallet);

// Start the syncing process from the block before the earliest transaction was seen.
this.walletSyncManager.SyncFromHeight(chainedHeader.Height - 1);
// Start the sync from the day before it was created.
this.walletSyncManager.SyncFromHeight(chainedHeader.Height);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why remove the -1?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It previously synced just minus 1 block, now it syncs minus 1 day, see 3rd line from above this line.

}

IEnumerable<RemovedTransactionModel> model = result.Select(r => new RemovedTransactionModel
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ public class WalletRPCController : FeatureController
/// <summary>Wallet related configuration.</summary>
private readonly WalletSettings walletSettings;

/// <summary>
/// The wallet name set by selectwallet method. This is static since the controller is a stateless type. This value should probably be cached by an injected service in the future.
/// </summary>
private static string CurrentWalletName;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should fix this now, it will be a fast fix maybe use as a hack put the field on the WallletManager just to avoid it being static?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I realize this is on RPC, it less crucial for me so this is fine (if it does not break tests)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't break anything, and it is to give a custom support for multi-wallet in the RPC. It's a fairly important extra RPC method for anyone who relies on the RPC and have multiple wallets, but I don't think there are many that will ever use this. It is very useful when debugging and working with multiple wallets at the same time (normal wallet and special wallets).


public WalletRPCController(
IBlockStore blockStore,
IBroadcasterManager broadcasterManager,
Expand All @@ -70,6 +75,14 @@ public WalletRPCController(
this.walletTransactionHandler = walletTransactionHandler;
}

[ActionName("setwallet")]
[ActionDescription("Selects the active wallet on RPC based on the name of the wallet supplied.")]
public bool SetWallet(string walletname)
{
WalletRPCController.CurrentWalletName = walletname;
return true;
}

[ActionName("walletpassphrase")]
[ActionDescription("Stores the wallet decryption key in memory for the indicated number of seconds. Issuing the walletpassphrase command while the wallet is already unlocked will set a new unlock time that overrides the old one.")]
public bool UnlockWallet(string passphrase, int timeout)
Expand Down Expand Up @@ -419,10 +432,22 @@ public GetTransactionModel GetTransaction(string txid)
}
}

// Get the ColdStaking script template if available.
Dictionary<string, ScriptTemplate> templates = this.walletManager.GetValidStakingTemplates();
ScriptTemplate coldStakingTemplate = templates.ContainsKey("ColdStaking") ? templates["ColdStaking"] : null;

// Receive transactions details.
foreach (TransactionData trxInWallet in receivedTransactions)
{
// Skip the details if the script pub key is cold staking.
// TODO: Verify if we actually need this any longer, after changing the internals to recognice account type!
if (coldStakingTemplate != null && coldStakingTemplate.CheckScriptPubKey(trxInWallet.ScriptPubKey))
{
continue;
}

GetTransactionDetailsCategoryModel category;

if (isGenerated)
{
category = model.Confirmations > this.FullNode.Network.Consensus.CoinbaseMaturity ? GetTransactionDetailsCategoryModel.Generate : GetTransactionDetailsCategoryModel.Immature;
Expand Down Expand Up @@ -792,6 +817,43 @@ public async Task<uint256> SendManyAsync(string fromAccount, string addressesJso
}
}

[ActionName("getwalletinfo")]
[ActionDescription("Provides information about the wallet.")]
public GetWalletInfoModel GetWalletInfo()
{
var accountReference = this.GetWalletAccountReference();
var account = this.walletManager.GetAccounts(accountReference.WalletName)
.Where(i => i.Name.Equals(accountReference.AccountName))
.Single();

(Money confirmedAmount, Money unconfirmedAmount) = account.GetBalances(account.IsNormalAccount());

var balance = Money.Coins(GetBalance(string.Empty));
var immature = Money.Coins(balance.ToDecimal(MoneyUnit.BTC) - GetBalance(string.Empty, (int)this.FullNode.Network.Consensus.CoinbaseMaturity)); // Balance - Balance(AtHeight)

var model = new GetWalletInfoModel
{
Balance = balance,
WalletName = accountReference.WalletName + ".wallet.json",
WalletVersion = 1,
UnConfirmedBalance = unconfirmedAmount,
ImmatureBalance = immature
};

return model;
}

private int GetConformationCount(TransactionData transaction)
{
if (transaction.BlockHeight.HasValue)
{
var blockCount = this.ConsensusManager?.Tip.Height ?? -1; // TODO: This is available in FullNodeController, should refactor and reuse the logic.
return blockCount - transaction.BlockHeight.Value;
}

return -1;
}

/// <summary>
/// Gets the first account from the "default" wallet if it specified,
/// otherwise returns the first available account in the existing wallets.
Expand All @@ -801,16 +863,27 @@ private WalletAccountReference GetWalletAccountReference()
{
string walletName = null;

if (this.walletSettings.IsDefaultWalletEnabled())
walletName = this.walletManager.GetWalletsNames().FirstOrDefault(w => w == this.walletSettings.DefaultWalletName);
// If the global override is null or empty.
if (string.IsNullOrWhiteSpace(WalletRPCController.CurrentWalletName))
{
if (this.walletSettings.IsDefaultWalletEnabled())
walletName = this.walletManager.GetWalletsNames().FirstOrDefault(w => w == this.walletSettings.DefaultWalletName);
else
{
//TODO: Support multi wallet like core by mapping passed RPC credentials to a wallet/account
walletName = this.walletManager.GetWalletsNames().FirstOrDefault();
}
}
else
{
//TODO: Support multi wallet like core by mapping passed RPC credentials to a wallet/account
walletName = this.walletManager.GetWalletsNames().FirstOrDefault();
// Read from class instance the wallet name.
walletName = WalletRPCController.CurrentWalletName;
}

if (walletName == null)
{
throw new RPCServerException(RPCErrorCode.RPC_INVALID_REQUEST, "No wallet found");
}

HdAccount account = this.walletManager.GetAccounts(walletName).First();
return new WalletAccountReference(walletName, account.Name);
Expand Down
22 changes: 22 additions & 0 deletions src/Features/Blockcore.Features.Wallet/Events/TransactionFound.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Text;
using Blockcore.EventBus;
using NBitcoin;

namespace Blockcore.Features.Wallet.Events
{
/// <summary>
/// Event that is executed when a transaction is found in the wallet.
/// </summary>
/// <seealso cref="Blockcore.EventBus.EventBase" />
public class TransactionFound : EventBase
{
public Transaction FoundTransaction { get; }

public TransactionFound(Transaction foundTransaction)
{
this.FoundTransaction = foundTransaction;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using Blockcore.Utilities.JsonConverters;
using NBitcoin;
using Newtonsoft.Json;

namespace Blockcore.Features.Wallet.Models
{
/// <summary>Model for RPC method getwalletinfo.</summary>
public class GetWalletInfoModel
{
[JsonProperty("walletname")]
public string WalletName { get; set; }

[JsonProperty("walletversion")]
public int WalletVersion { get; set; }

[JsonProperty("balance")]
[JsonConverter(typeof(MoneyInCoinsJsonConverter))]
public Money Balance { get; set; }

[JsonProperty("unconfirmed_balance")]
[JsonConverter(typeof(MoneyInCoinsJsonConverter))]
public Money UnConfirmedBalance { get; set; }

[JsonProperty("immature_balance")]
[JsonConverter(typeof(MoneyInCoinsJsonConverter))]
public Money ImmatureBalance { get; set; }
}
}
Loading