diff --git a/Stratis.Bitcoin.IntegrationTests/NodeBuilder.cs b/Stratis.Bitcoin.IntegrationTests/NodeBuilder.cs index 5a53c107cab..a4efb81ef62 100644 --- a/Stratis.Bitcoin.IntegrationTests/NodeBuilder.cs +++ b/Stratis.Bitcoin.IntegrationTests/NodeBuilder.cs @@ -1,4 +1,5 @@ using NBitcoin; +using Stratis.Bitcoin.Miner; using NBitcoin.DataEncoders; using NBitcoin.Protocol; using NBitcoin.RPC; @@ -76,10 +77,11 @@ public static FullNode BuildFullNode(NodeSettings args) .UseConsensus() .UseBlockStore() .UseMempool() + .AddMining() .UseWallet() .AddRPC() .Build(); - + node.WalletManager.CreateWallet("blabla", "test"); return node; } diff --git a/Stratis.Bitcoin.IntegrationTests/WalletTests.cs b/Stratis.Bitcoin.IntegrationTests/WalletTests.cs index c4ebbf9b39c..5ff2522f535 100644 --- a/Stratis.Bitcoin.IntegrationTests/WalletTests.cs +++ b/Stratis.Bitcoin.IntegrationTests/WalletTests.cs @@ -77,6 +77,19 @@ public void WalletCanReceiveAndSendCorrectly() } + [Fact] + public void CanMineBlocks() + { + using(NodeBuilder builder = NodeBuilder.Create()) + { + var stratisNodeSync = builder.CreateStratisNode(); + builder.StartAll(); + var rpc = stratisNodeSync.CreateRPCClient(); + rpc.SendCommand(NBitcoin.RPC.RPCOperations.generate, 10); + Assert.Equal(10, rpc.GetBlockCount()); + } + } + [Fact] public void WalletCanReorg() { diff --git a/Stratis.Bitcoin/Miner/PowMining.cs b/Stratis.Bitcoin/Miner/PowMining.cs index 30cc26de99a..f7973ace5af 100644 --- a/Stratis.Bitcoin/Miner/PowMining.cs +++ b/Stratis.Bitcoin/Miner/PowMining.cs @@ -17,6 +17,14 @@ namespace Stratis.Bitcoin.Miner { public class ReserveScript { + public ReserveScript() + { + + } + public ReserveScript(Script reserveSfullNodecript) + { + this.reserveSfullNodecript = reserveSfullNodecript; + } public Script reserveSfullNodecript { get; set; } } diff --git a/Stratis.Bitcoin/RPC/Controllers/FullNodeController.cs b/Stratis.Bitcoin/RPC/Controllers/FullNodeController.cs index e90b54ba110..c3a4cd22f0a 100644 --- a/Stratis.Bitcoin/RPC/Controllers/FullNodeController.cs +++ b/Stratis.Bitcoin/RPC/Controllers/FullNodeController.cs @@ -4,6 +4,7 @@ using Stratis.Bitcoin.RPC.Models; using System; using System.Collections.Generic; +using Microsoft.Extensions.DependencyInjection; using System.Linq; using System.Threading.Tasks; using Stratis.Bitcoin.BlockStore; @@ -73,6 +74,13 @@ public async Task GetRawTransaction(string txid, int verbose = return new TransactionBriefModel(trx); } + [ActionName("getblockcount")] + public int GetBlockCount() + { + var consensusLoop = this.FullNode.Services.ServiceProvider.GetRequiredService(); + return consensusLoop.Tip.Height; + } + [ActionName("getinfo")] public GetInfoModel GetInfo() { diff --git a/Stratis.Bitcoin/RPC/Controllers/WalletRPCController.cs b/Stratis.Bitcoin/RPC/Controllers/WalletRPCController.cs new file mode 100644 index 00000000000..e24ae2b2d4c --- /dev/null +++ b/Stratis.Bitcoin/RPC/Controllers/WalletRPCController.cs @@ -0,0 +1,72 @@ +using Microsoft.AspNetCore.Mvc; +using Stratis.Bitcoin.Miner; +using System; +using System.Linq; +using System.Collections.Generic; +using System.Text; +using Microsoft.Extensions.DependencyInjection; +using Stratis.Bitcoin.Wallet; +using NBitcoin; + +namespace Stratis.Bitcoin.RPC.Controllers +{ + public class WalletRPCController : BaseRPCController + { + public class UsedWallet + { + public string WalletName + { + get; set; + } + public HdAccount Account + { + get; + set; + } + } + public WalletRPCController(IServiceProvider serviceProvider, IWalletManager walletManager) + { + this.WalletManager = walletManager; + this.serviceProvider = serviceProvider; + } + + IServiceProvider serviceProvider; + + public IWalletManager WalletManager + { + get; set; + } + + [ActionName("generate")] + public List Generate(int nBlock) + { + var mining = serviceProvider.GetRequiredService(); + var wallet = GetWallet(); + var address = this.WalletManager.GetUnusedAddress(wallet.WalletName, wallet.Account.Name); + return mining.GenerateBlocks(new ReserveScript(address.Pubkey), (ulong)nBlock, int.MaxValue); + } + + private UsedWallet GetWallet() + { + var w = this.WalletManager.GetWallets().FirstOrDefault(); + if(w == null) + throw new RPCServerException(NBitcoin.RPC.RPCErrorCode.RPC_INVALID_REQUEST, "No wallet found"); + var account = this.WalletManager.GetAccounts(w).FirstOrDefault(); + return new UsedWallet() + { + Account = account, + WalletName = w + }; + } + + private string GetAccountName() + { + return this.WalletManager.GetAccounts(GetWalletName()).FirstOrDefault().Name; + } + + private string GetWalletName() + { + return this.WalletManager.GetWallets().FirstOrDefault(); + } + } +} diff --git a/Stratis.Bitcoin/RPC/RPCMiddleware.cs b/Stratis.Bitcoin/RPC/RPCMiddleware.cs index 63f9949934e..ee7364d7250 100644 --- a/Stratis.Bitcoin/RPC/RPCMiddleware.cs +++ b/Stratis.Bitcoin/RPC/RPCMiddleware.cs @@ -51,11 +51,22 @@ public async Task Invoke(HttpContext httpContext) JObject response = CreateError(RPCErrorCode.RPC_MISC_ERROR, "Argument error: " + ex.Message); await httpContext.Response.WriteAsync(response.ToString(Formatting.Indented)); } + else if(ex is RPCServerException) + { + var rpcEx = (RPCServerException)ex; + JObject response = CreateError(rpcEx.ErrorCode, ex.Message); + await httpContext.Response.WriteAsync(response.ToString(Formatting.Indented)); + } else if(httpContext.Response?.StatusCode == 404) { JObject response = CreateError(RPCErrorCode.RPC_METHOD_NOT_FOUND, "Method not found"); await httpContext.Response.WriteAsync(response.ToString(Formatting.Indented)); } + else if(IsDependencyFailure(ex)) + { + JObject response = CreateError(RPCErrorCode.RPC_METHOD_NOT_FOUND, ex.Message); + await httpContext.Response.WriteAsync(response.ToString(Formatting.Indented)); + } else if(httpContext.Response?.StatusCode == 500 || ex != null) { JObject response = CreateError(RPCErrorCode.RPC_INTERNAL_ERROR, "Internal error"); @@ -64,6 +75,14 @@ public async Task Invoke(HttpContext httpContext) } } + private bool IsDependencyFailure(Exception ex) + { + var invalidOp = ex as InvalidOperationException; + if(invalidOp == null) + return false; + return invalidOp.Source.Equals("Microsoft.Extensions.DependencyInjection.Abstractions", StringComparison.Ordinal); + } + private bool Authorized(HttpContext httpContext) { if(!this.authorization.IsAuthorized(httpContext.Connection.RemoteIpAddress)) diff --git a/Stratis.Bitcoin/RPC/RPCServerException.cs b/Stratis.Bitcoin/RPC/RPCServerException.cs new file mode 100644 index 00000000000..14bfc7216f6 --- /dev/null +++ b/Stratis.Bitcoin/RPC/RPCServerException.cs @@ -0,0 +1,20 @@ +using NBitcoin.RPC; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Stratis.Bitcoin.RPC +{ + public class RPCServerException : Exception + { + public RPCServerException(RPCErrorCode errorCode, string message) : base(message) + { + this.ErrorCode = errorCode; + } + + public RPCErrorCode ErrorCode + { + get; set; + } + } +} diff --git a/Stratis.Bitcoin/RPC/WebHostExtensions.cs b/Stratis.Bitcoin/RPC/WebHostExtensions.cs index f9da7d23c6a..675d4e0d46d 100644 --- a/Stratis.Bitcoin/RPC/WebHostExtensions.cs +++ b/Stratis.Bitcoin/RPC/WebHostExtensions.cs @@ -1,5 +1,7 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; +using Stratis.Bitcoin.Miner; +using Stratis.Bitcoin.Wallet; using System; using System.Collections.Generic; using System.Linq; @@ -24,6 +26,10 @@ public static IWebHostBuilder ForFullNode(this IWebHostBuilder hostBuilder, Full s.AddSingleton(fullNode.BlockStoreManager); s.AddSingleton(fullNode.MempoolManager); s.AddSingleton(fullNode.ConnectionManager); + s.AddSingleton(fullNode.Services.ServiceProvider.GetService()); + var pow = fullNode.Services.ServiceProvider.GetService(); + if(pow != null) + s.AddSingleton(pow); }); return hostBuilder; } diff --git a/Stratis.Bitcoin/Wallet/IWalletManager.cs b/Stratis.Bitcoin/Wallet/IWalletManager.cs index 8da49019fd9..9bd9735bfde 100644 --- a/Stratis.Bitcoin/Wallet/IWalletManager.cs +++ b/Stratis.Bitcoin/Wallet/IWalletManager.cs @@ -187,6 +187,12 @@ public interface IWalletManager : IDisposable /// /// string GetWalletFileExtension(); + + /// + /// Get all the wallets name + /// + /// + string[] GetWallets(); /// /// Updates the wallet with the height of the last block synced. diff --git a/Stratis.Bitcoin/Wallet/WalletFeature.cs b/Stratis.Bitcoin/Wallet/WalletFeature.cs index 9164e3a3921..6cdd9a7c680 100644 --- a/Stratis.Bitcoin/Wallet/WalletFeature.cs +++ b/Stratis.Bitcoin/Wallet/WalletFeature.cs @@ -5,6 +5,7 @@ using Stratis.Bitcoin.Builder; using Stratis.Bitcoin.Builder.Feature; using Stratis.Bitcoin.Wallet.Notifications; +using Stratis.Bitcoin.RPC.Controllers; namespace Stratis.Bitcoin.Wallet { @@ -57,6 +58,7 @@ public static IFullNodeBuilder UseWallet(this IFullNodeBuilder fullNodeBuilder) services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); }); }); diff --git a/Stratis.Bitcoin/Wallet/WalletManager.cs b/Stratis.Bitcoin/Wallet/WalletManager.cs index 5ae18e87f5c..c79abe3921d 100644 --- a/Stratis.Bitcoin/Wallet/WalletManager.cs +++ b/Stratis.Bitcoin/Wallet/WalletManager.cs @@ -1051,6 +1051,11 @@ internal void LoadKeysLookup() this.keysLookup = lookup; } + public string[] GetWallets() + { + return this.Wallets.Select(w => w.Name).ToArray(); + } + /// /// Gets a wallet given its name. ///