From c94dc8aa7e20b2fd7f938bda354513f0a059fd28 Mon Sep 17 00:00:00 2001 From: NicolasDorier Date: Tue, 11 Jul 2017 21:33:14 +0900 Subject: [PATCH 1/4] Can generate blocks through RPC --- .../NodeBuilder.cs | 4 +- .../WalletTests.cs | 13 ++++ Stratis.Bitcoin/Miner/PowMining.cs | 8 +++ .../RPC/Controllers/FullNodeController.cs | 8 +++ .../RPC/Controllers/WalletRPCController.cs | 72 +++++++++++++++++++ Stratis.Bitcoin/RPC/RPCMiddleware.cs | 19 +++++ Stratis.Bitcoin/RPC/RPCServerException.cs | 20 ++++++ Stratis.Bitcoin/RPC/WebHostExtensions.cs | 6 ++ Stratis.Bitcoin/Wallet/IWalletManager.cs | 6 ++ Stratis.Bitcoin/Wallet/WalletFeature.cs | 2 + Stratis.Bitcoin/Wallet/WalletManager.cs | 5 ++ 11 files changed, 162 insertions(+), 1 deletion(-) create mode 100644 Stratis.Bitcoin/RPC/Controllers/WalletRPCController.cs create mode 100644 Stratis.Bitcoin/RPC/RPCServerException.cs 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. /// From 5a3f0b9c6a095ce882a8b0ec54f48c12ecb22734 Mon Sep 17 00:00:00 2001 From: NicolasDorier Date: Wed, 12 Jul 2017 21:44:58 +0900 Subject: [PATCH 2/4] Add compile to net462, for better debug experience and profiler --- .../Stratis.Bitcoin.Common.csproj | 2 +- Stratis.Bitcoin.IntegrationTests/Program.cs | 14 ++++++++++++++ .../Stratis.Bitcoin.IntegrationTests.csproj | 9 +++++++-- Stratis.Bitcoin.Tests/Stratis.Bitcoin.Tests.csproj | 2 +- .../Consensus/CoinViews/CachedCoinView.cs | 6 +++--- Stratis.Bitcoin/MemoryPool/MemPoolCoinView.cs | 2 +- Stratis.Bitcoin/Stratis.Bitcoin.csproj | 5 ++++- Stratis.Bitcoin/Utilities/FullNodeExtensions.cs | 9 ++++++--- 8 files changed, 37 insertions(+), 12 deletions(-) create mode 100644 Stratis.Bitcoin.IntegrationTests/Program.cs diff --git a/Stratis.Bitcoin.Common/Stratis.Bitcoin.Common.csproj b/Stratis.Bitcoin.Common/Stratis.Bitcoin.Common.csproj index 1d24377fc5b..35ce29df7ea 100644 --- a/Stratis.Bitcoin.Common/Stratis.Bitcoin.Common.csproj +++ b/Stratis.Bitcoin.Common/Stratis.Bitcoin.Common.csproj @@ -1,7 +1,7 @@  - netstandard1.6 + net462;netstandard1.6 diff --git a/Stratis.Bitcoin.IntegrationTests/Program.cs b/Stratis.Bitcoin.IntegrationTests/Program.cs new file mode 100644 index 00000000000..82a2095ee3f --- /dev/null +++ b/Stratis.Bitcoin.IntegrationTests/Program.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Stratis.Bitcoin.IntegrationTests +{ + class Program + { + public static void Main(string[] args) + { + new WalletTests().CanMineBlocks(); + } + } +} diff --git a/Stratis.Bitcoin.IntegrationTests/Stratis.Bitcoin.IntegrationTests.csproj b/Stratis.Bitcoin.IntegrationTests/Stratis.Bitcoin.IntegrationTests.csproj index 2a88d8a8d7d..5951b37b6b8 100644 --- a/Stratis.Bitcoin.IntegrationTests/Stratis.Bitcoin.IntegrationTests.csproj +++ b/Stratis.Bitcoin.IntegrationTests/Stratis.Bitcoin.IntegrationTests.csproj @@ -1,7 +1,11 @@  + + exe + + - netcoreapp1.1 + net462;netcoreapp1.1 Stratis.Bitcoin.IntegrationTests Stratis.Bitcoin.IntegrationTests true @@ -14,7 +18,8 @@ false false false - false + false + false diff --git a/Stratis.Bitcoin.Tests/Stratis.Bitcoin.Tests.csproj b/Stratis.Bitcoin.Tests/Stratis.Bitcoin.Tests.csproj index 436f5e1f338..bd0d4b4afba 100644 --- a/Stratis.Bitcoin.Tests/Stratis.Bitcoin.Tests.csproj +++ b/Stratis.Bitcoin.Tests/Stratis.Bitcoin.Tests.csproj @@ -1,7 +1,7 @@  - netcoreapp1.1 + net462;netcoreapp1.1 Stratis.Bitcoin.Tests Stratis.Bitcoin.Tests true diff --git a/Stratis.Bitcoin/Consensus/CoinViews/CachedCoinView.cs b/Stratis.Bitcoin/Consensus/CoinViews/CachedCoinView.cs index 87cc3399ffc..c3d17a8d785 100644 --- a/Stratis.Bitcoin/Consensus/CoinViews/CachedCoinView.cs +++ b/Stratis.Bitcoin/Consensus/CoinViews/CachedCoinView.cs @@ -19,7 +19,7 @@ class CacheItem public TxOut[] OriginalOutputs; } - private readonly ReaderWriterLock lockobj; + private readonly Stratis.Bitcoin.Utilities.ReaderWriterLock lockobj; private readonly Dictionary unspents; private uint256 blockHash; private uint256 innerBlockHash; @@ -33,7 +33,7 @@ public CachedCoinView(DBreezeCoinView inner, StakeChainStore stakeChainStore = n this.inner = inner; this.stakeChainStore = stakeChainStore; this.MaxItems = 100000; - this.lockobj = new ReaderWriterLock(); + this.lockobj = new Stratis.Bitcoin.Utilities.ReaderWriterLock(); this.unspents = new Dictionary(); this.PerformanceCounter = new CachePerformanceCounter(); } @@ -49,7 +49,7 @@ public CachedCoinView(InMemoryCoinView inner, StakeChainStore stakeChainStore = this.inner = inner; this.stakeChainStore = stakeChainStore; this.MaxItems = 100000; - this.lockobj = new ReaderWriterLock(); + this.lockobj = new Stratis.Bitcoin.Utilities.ReaderWriterLock(); this.unspents = new Dictionary(); this.PerformanceCounter = new CachePerformanceCounter(); } diff --git a/Stratis.Bitcoin/MemoryPool/MemPoolCoinView.cs b/Stratis.Bitcoin/MemoryPool/MemPoolCoinView.cs index 029c2ca1dc9..c0974423ebb 100644 --- a/Stratis.Bitcoin/MemoryPool/MemPoolCoinView.cs +++ b/Stratis.Bitcoin/MemoryPool/MemPoolCoinView.cs @@ -31,7 +31,7 @@ public MempoolCoinView(CoinView inner, TxMempool memPool, AsyncLock mempoolSched public async Task LoadView(Transaction trx) { // lookup all ids (duplicate ids are ignored in case a trx spends outputs from the same parent) - var ids = trx.Inputs.Select(n => n.PrevOut.Hash).Distinct().Append(trx.GetHash()).ToList(); + var ids = trx.Inputs.Select(n => n.PrevOut.Hash).Distinct().Concat(new[] { trx.GetHash() }).ToList(); var coins = await this.Inner.FetchCoinsAsync(ids.ToArray()); // find coins currently in the mempool var mempoolcoins = await this.mempoolScheduler.ReadAsync(() => diff --git a/Stratis.Bitcoin/Stratis.Bitcoin.csproj b/Stratis.Bitcoin/Stratis.Bitcoin.csproj index 997f5adb4fa..da9d5a64fcd 100644 --- a/Stratis.Bitcoin/Stratis.Bitcoin.csproj +++ b/Stratis.Bitcoin/Stratis.Bitcoin.csproj @@ -3,7 +3,7 @@ Stratis Bitcoin FullNode Stratis.Bitcoin - netstandard1.6 + net462;netstandard1.6 Stratis.Bitcoin Stratis.Bitcoin $(PackageTargetFallback);netcore50 @@ -47,6 +47,9 @@ $(DefineConstants);NETCORE + + $(DefineConstants);NOASSEMBLYCONTEXT + 1701;1702;1705;IDE0008; diff --git a/Stratis.Bitcoin/Utilities/FullNodeExtensions.cs b/Stratis.Bitcoin/Utilities/FullNodeExtensions.cs index 9a57c82e730..da8be42048d 100644 --- a/Stratis.Bitcoin/Utilities/FullNodeExtensions.cs +++ b/Stratis.Bitcoin/Utilities/FullNodeExtensions.cs @@ -1,6 +1,8 @@ using System; using System.Reflection; +#if !NOASSEMBLYCONTEXT using System.Runtime.Loader; +#endif using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; @@ -35,10 +37,11 @@ public static void Run(this IFullNode node) done.Wait(); }; - - var assemblyLoadContext = AssemblyLoadContext.GetLoadContext(typeof(FullNode).GetTypeInfo().Assembly); +#if !NOASSEMBLYCONTEXT + var assemblyLoadContext = AssemblyLoadContext.GetLoadContext(typeof(FullNode).GetTypeInfo().Assembly); assemblyLoadContext.Unloading += context => shutdown(); - Console.CancelKeyPress += (sender, eventArgs) => +#endif + Console.CancelKeyPress += (sender, eventArgs) => { shutdown(); // Don't terminate the process immediately, wait for the Main thread to exit gracefully. From efaaa1eb20adb534ae5ce2d41bd7821fa42ab91b Mon Sep 17 00:00:00 2001 From: NicolasDorier Date: Wed, 12 Jul 2017 22:02:49 +0900 Subject: [PATCH 3/4] Make tests faster by using a preloaded test wallet --- .../Data/test.wallet.json | 681 ++++++++++++++++++ .../NodeBuilder.cs | 4 +- .../Stratis.Bitcoin.IntegrationTests.csproj | 3 + Stratis.Bitcoin/Miner/PowMining.cs | 1 + .../RPC/Controllers/WalletRPCController.cs | 9 +- 5 files changed, 696 insertions(+), 2 deletions(-) create mode 100644 Stratis.Bitcoin.IntegrationTests/Data/test.wallet.json diff --git a/Stratis.Bitcoin.IntegrationTests/Data/test.wallet.json b/Stratis.Bitcoin.IntegrationTests/Data/test.wallet.json new file mode 100644 index 00000000000..0b37525cc25 --- /dev/null +++ b/Stratis.Bitcoin.IntegrationTests/Data/test.wallet.json @@ -0,0 +1,681 @@ +{ + "name": "test", + "encryptedSeed": "6PYV1yNR3KB4N1wVA3MWicjBerhFRDARij5u9BREGsw2SGnkwqoVQdgezu", + "chainCode": "ZPhy7sZb/8AcqXRkb4pqDSQhHfSZdbOaIvTm8HdNhdY=", + "blockLocator": [ + "0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206" + ], + "network": "RegTest", + "creationTime": "1499864344", + "accountsRoot": [ + { + "coinType": 0, + "lastBlockSyncedHeight": 0, + "lastBlockSyncedHash": "0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206", + "accounts": [ + { + "index": 0, + "name": "account 0", + "hdPath": "m/44'/0'/0'", + "extPubKey": "tpubDDYSPVVe5aEsYkB1wDAtUXrziWQxn474KC3Th7TsFeUCnqyNEB2UwKS3AtW8ndSqfvEWwc7bEqo4YFo4od1G6BhQGzq1QUNqC5anwWCSCL6", + "creationTime": "1499864345", + "externalAddresses": [ + { + "index": 0, + "scriptPubKey": "76a9149dd26a3b52da3addf29479f391899322fb415fa688ac", + "pubkey": "2103f22d8a3c8e03d0132294e80c5f34b6b3cfb9021f08fbde1b85b39601a1f6a10bac", + "address": "muuST4QwU21UBYJLvf134hFvLi7QasyM46", + "hdPath": "m/44'/0'/0'/0/0", + "transactions": [] + }, + { + "index": 1, + "scriptPubKey": "76a91403255df7c1ff5619dd5b45e162dd5d9aebffb3d288ac", + "pubkey": "210377e8ba086b4ca7ed10b0401e95f1060ac6db1d7fdd957739d0c8d2d6b5c07d90ac", + "address": "mfob5wPHxvqWBpYutuHUd9snmAMPd1VLAu", + "hdPath": "m/44'/0'/0'/0/1", + "transactions": [] + }, + { + "index": 2, + "scriptPubKey": "76a9147fa8e9d418544374cd6d93354c13c88e3a8a62e088ac", + "pubkey": "21038a268053b10b3ec22bcbb566897fa5af4dc70e8121bfd6727adb52bb3b641156ac", + "address": "ms9xTizKGJpzUaV9YbwDtSKDqGGqb5Lqn3", + "hdPath": "m/44'/0'/0'/0/2", + "transactions": [] + }, + { + "index": 3, + "scriptPubKey": "76a914c6dd01bfe120bbfdd755ee96674f4b43a6bb060988ac", + "pubkey": "2103a78b1c0427fe4af6e2d2f953a932be93d9b8223ac926feeaf7be8cc96252bcb4ac", + "address": "myeSqrrvCMZ7kVm8ENobT3WFrF6jCiiZev", + "hdPath": "m/44'/0'/0'/0/3", + "transactions": [] + }, + { + "index": 4, + "scriptPubKey": "76a914e616f0b94a01ff2b6ea07bf7caf30269e45af6d288ac", + "pubkey": "2103f42f9200008efd2594c054ae1c2fca402d28c31c5435cbb1e87680037e1fa8ecac", + "address": "n2VZC6aEAdVVoQSoXtPzLrjUtRkWGewQzf", + "hdPath": "m/44'/0'/0'/0/4", + "transactions": [] + }, + { + "index": 5, + "scriptPubKey": "76a91426fd312153aadcb253c99bee8f3c9ac54d4fa91288ac", + "pubkey": "2102c6d1e2889569b595952241ad12238514a23a387627cb85e4029a2dfdb67830e0ac", + "address": "mj57HRg5PebvCLjkxWUe6h53mU5HtnAoSM", + "hdPath": "m/44'/0'/0'/0/5", + "transactions": [] + }, + { + "index": 6, + "scriptPubKey": "76a9143ad2ef4d212f4317f837d95b3a3e63015ca7e46588ac", + "pubkey": "2103f66081d3323757a99532c68e2bb619c99f122a02fc9b53040839242324c968dfac", + "address": "mkszBBgYwMfC3BXY2yRTSEoWQ6Sgqu5CEf", + "hdPath": "m/44'/0'/0'/0/6", + "transactions": [] + }, + { + "index": 7, + "scriptPubKey": "76a914b18dd882fa888c9614bc93c8cfaf5f665f0e902888ac", + "pubkey": "21027848389589905c2068a79becc0c67e8f92579ec73ad3eb12fe90d51b0bf826beac", + "address": "mwhmpd7MsXaCKoQsixiqDbkoeTZDxktzC9", + "hdPath": "m/44'/0'/0'/0/7", + "transactions": [] + }, + { + "index": 8, + "scriptPubKey": "76a9146b3490a2a96b7d2d0ce4a6654ef4c3a092ca534e88ac", + "pubkey": "210251e96656447dd6b6f7f2e0fbd6081cc4f8122cf74ed1a459c978366be7138e32ac", + "address": "mqHoZrmSgvTWMSSwdqm3k1iCquCXWunWUo", + "hdPath": "m/44'/0'/0'/0/8", + "transactions": [] + }, + { + "index": 9, + "scriptPubKey": "76a9147c0d75c7cada0a8389bb645783a72ac2dae818e288ac", + "pubkey": "210361c723df14c8096e44c14f98dab1d2b4a4d6cb40e0ad5af78a11fec279415305ac", + "address": "mrptD4efJJWWXrJ51CSxdtZZeWKTByGJBP", + "hdPath": "m/44'/0'/0'/0/9", + "transactions": [] + }, + { + "index": 10, + "scriptPubKey": "76a914920c995a2f829d4c5dcfef4c435956de1325665e88ac", + "pubkey": "2102390fb2e3f2f361f3a223181f1d51803ef8400885a1793302cb0591276e533b08ac", + "address": "mtqC3TUseovR2yW1kUnZBbtGscJK5bhdZS", + "hdPath": "m/44'/0'/0'/0/10", + "transactions": [] + }, + { + "index": 11, + "scriptPubKey": "76a91476ce8afcb0458b0d2eaba659d6bc2b00ea746a9088ac", + "pubkey": "21039a451171e1c7b6f6f9bc7c70e3e246a6396a580b9238943afdbdb259692b308dac", + "address": "mrM9TYUmwqNBmDEa79EbbH4YwcUQpxeFAi", + "hdPath": "m/44'/0'/0'/0/11", + "transactions": [] + }, + { + "index": 12, + "scriptPubKey": "76a914bb7211c193ae5a0e1494cf642f1a41dfffe9ade888ac", + "pubkey": "2102a5ac50cd5884dcc2bb575123afabde56d8ad5fc7ed9f94d79e1dd2ade01d39bdac", + "address": "mxc5Jc1R9Qe7B2AwGcSjx6PPFZiRqBNEDd", + "hdPath": "m/44'/0'/0'/0/12", + "transactions": [] + }, + { + "index": 13, + "scriptPubKey": "76a914266360a01b2c72d496b9f36a78686f65dd669dbb88ac", + "pubkey": "2103f4cb09aa34114a6b0afcb83d705bdf098056785bef698a4f8b68503c52b9f107ac", + "address": "mj1w2D6a2xxDdhx9pWwyNf3RktdtfBuKZJ", + "hdPath": "m/44'/0'/0'/0/13", + "transactions": [] + }, + { + "index": 14, + "scriptPubKey": "76a914d970f05c40e476637115399bd78f7025d947223e88ac", + "pubkey": "210260e41c3a681c817d27b4601122f31f6ce3b023b38d37d9b0367a821dbaebedc7ac", + "address": "n1LgDpAsjwr7FG73JVnqU7AXXZhNusKFb5", + "hdPath": "m/44'/0'/0'/0/14", + "transactions": [] + }, + { + "index": 15, + "scriptPubKey": "76a9142c928832bd7256b22c3927e59e5326edb8ffe5dd88ac", + "pubkey": "210367b2f83fca4b02b273747c1f3ef7283d97fb9fc401896741e25d002febc4cebdac", + "address": "mjadZibv1ftGdQu5vK66DWvaWFhU9oCzeu", + "hdPath": "m/44'/0'/0'/0/15", + "transactions": [] + }, + { + "index": 16, + "scriptPubKey": "76a914f7b5089f660cf5307fe8f11ab1e1b165311653c788ac", + "pubkey": "2103a85ce64f8e2802b534978c4b142840654e31c1f71a428ee34c8b74f9de33db4dac", + "address": "n46i4sp7bjXDYtvWd7Ld1awCemxShCMSJi", + "hdPath": "m/44'/0'/0'/0/16", + "transactions": [] + }, + { + "index": 17, + "scriptPubKey": "76a9144cf59119e7cdfa19c82b91934f0f3d8d64aa4db388ac", + "pubkey": "2103dce4c1caa5f8f661a70a86df5e57cc807f2825494b3bd907afaa90eeb50c0156ac", + "address": "mnXspuwgy1UHsNQDjqL83NLLGyXaQCatuQ", + "hdPath": "m/44'/0'/0'/0/17", + "transactions": [] + }, + { + "index": 18, + "scriptPubKey": "76a91487656e5d150b140584d3d4ca6dffc701337e103788ac", + "pubkey": "21020b512a78cf68b78c0fa7276b70d51768f5b89c0e4d751042b8d1072119cde5f1ac", + "address": "msrs2WRLXX7Vqt1bc82SNivi2dqjus59M6", + "hdPath": "m/44'/0'/0'/0/18", + "transactions": [] + }, + { + "index": 19, + "scriptPubKey": "76a914da86afdc1ee0d3b50b0bdd3fcc737108d1c80f1688ac", + "pubkey": "21031a8371bf26728cf50c9f77f8374610a1c7e0fb51f43757c33d14d52b0894d8eaac", + "address": "n1SQx5yVMxnzkDXTH2VBf9XqX5VpMZood9", + "hdPath": "m/44'/0'/0'/0/19", + "transactions": [] + } + ], + "internalAddresses": [ + { + "index": 0, + "scriptPubKey": "76a914f5fe6643c117c33f04750428a83c1893943b16fe88ac", + "pubkey": "2103404c432e54713c407f9caf960a49720164829ead0d854529d57a081895f79f9eac", + "address": "n3wec38pYSnY1MsoaKmWZjvPmRwCcPMbq2", + "hdPath": "m/44'/0'/0'/1/0", + "transactions": [] + }, + { + "index": 1, + "scriptPubKey": "76a914d4773a8f1e102f59f0628aff127238ddd6319b5c88ac", + "pubkey": "21024a309026efd0fa471008884096f107604955dd5810c6e7e4faa911b57c5bf9daac", + "address": "mztNNt2a6RVTMo6ztrSeqxCxJBaGS21Az9", + "hdPath": "m/44'/0'/0'/1/1", + "transactions": [] + }, + { + "index": 2, + "scriptPubKey": "76a9145c3aaf195da264b786a9fd232c1dbc164753b25088ac", + "pubkey": "2103f08a9e7c80a6c9d64b88e6d723e84a565f71e0c0221a0d10f70fd8a04ff5b794ac", + "address": "movcm8tk8edAt1H4E2JSCfDZqtabtb6taJ", + "hdPath": "m/44'/0'/0'/1/2", + "transactions": [] + }, + { + "index": 3, + "scriptPubKey": "76a91453fca447a009c6ca7ea84f48b56b6e8fc967fd2088ac", + "pubkey": "2103f3e52e9a338381b82b1e5a6fe2c2ec47be1b572f4106cdf7af5db035dbe1db07ac", + "address": "moB32q7Cf1ayWULe3JoXNtgeiEK7FjYDhJ", + "hdPath": "m/44'/0'/0'/1/3", + "transactions": [] + }, + { + "index": 4, + "scriptPubKey": "76a91467695b98e015d4ab0ff8cc1267d79674273039bf88ac", + "pubkey": "2103ac8f7a5f2779e6aa5c28449675d7428d9cee6b03acd6527447f058562b89ec61ac", + "address": "mpwk7CcCCSrT8vbMrYhEcWQ4uBes4Txrqx", + "hdPath": "m/44'/0'/0'/1/4", + "transactions": [] + }, + { + "index": 5, + "scriptPubKey": "76a91454faecb7ef82a92d9f15338d6269ade575c9c4d088ac", + "pubkey": "210228f02d4b252d131f70892428040ce232c0df635bec34ba4d425330dad91bccd1ac", + "address": "moGHeijMhujA9ouUDyX8dyYWgKxHs9Wkxs", + "hdPath": "m/44'/0'/0'/1/5", + "transactions": [] + }, + { + "index": 6, + "scriptPubKey": "76a914fe7543fb3b45555fbd086dc4626224c4b298271488ac", + "pubkey": "210290cecd68c9c56cf591394d78cb15e7c1dcf9e31dfcc07fc4519206c314ab74c5ac", + "address": "n4iQQXq6sqKGwWMKehVwFYapVS9goXM6wf", + "hdPath": "m/44'/0'/0'/1/6", + "transactions": [] + }, + { + "index": 7, + "scriptPubKey": "76a91449e6dc50591bfc8d97dd3db3908dc73c9eb948fc88ac", + "pubkey": "21027571a2cbadaf0dcf672d3dc5f5bca84d90d1956780c1b1b155e84c8dbf721d39ac", + "address": "mnFiBXte5Hu4pM2KjpwHssfbNdpAgb8Stc", + "hdPath": "m/44'/0'/0'/1/7", + "transactions": [] + }, + { + "index": 8, + "scriptPubKey": "76a914b51fe74d0e2e615a78732ce593a02eb1dd25fc7788ac", + "pubkey": "210261292c2dbb9d50fd68d561a264d2094d0a53e122386da447c9b320939aa8a148ac", + "address": "mx2epUFmgvts1WDRsK9x1KwUVsNyHstVFY", + "hdPath": "m/44'/0'/0'/1/8", + "transactions": [] + }, + { + "index": 9, + "scriptPubKey": "76a9144679c370d48a21c01b9a9bc736382ac0940aec2688ac", + "pubkey": "210346a8870b0927141b9912777f38f2e9e97f134e1a874e5ddbcda8195d413bc83cac", + "address": "mmwbTkhkE5F4j2KRS7431nJPqtret2NuAL", + "hdPath": "m/44'/0'/0'/1/9", + "transactions": [] + }, + { + "index": 10, + "scriptPubKey": "76a914d313e21c746699bf6a5692939e81d7323f21359088ac", + "pubkey": "21020f76a7804a1e5c96bef7d223a8446b0e765087c8859a504c900ef591699bbafcac", + "address": "mzm2h3R1xsxQiNWW5mjpyk2t8zcHTUwhy4", + "hdPath": "m/44'/0'/0'/1/10", + "transactions": [] + }, + { + "index": 11, + "scriptPubKey": "76a91445dd12fd24b90aa2ad4860ae997c9f5f158a9bbe88ac", + "pubkey": "2103a34d341e2b8111a08a39286e50d1aa1e1db2d70bb2a32265bd5601ee3e710028ac", + "address": "mmtMknxucgKrsjjCnRzNa9kLN78P2DMM2o", + "hdPath": "m/44'/0'/0'/1/11", + "transactions": [] + }, + { + "index": 12, + "scriptPubKey": "76a9149d9af9a718ac14100ea4d6db92150a7fef8aa28688ac", + "pubkey": "2102dae8d1bb18d53c00cd83ef1e043aa47332405f02196013f7f2a69180d751b67aac", + "address": "mutJ338LEsuxVuxSwk2vU1a2htuUSAabu8", + "hdPath": "m/44'/0'/0'/1/12", + "transactions": [] + }, + { + "index": 13, + "scriptPubKey": "76a91450fa368a90a5ddfe72768d111d2d0d73bb787cef88ac", + "pubkey": "2102270946ac64fcd2465058ba243ad1a55762180c8cf623b2db9b7129c7d942d74fac", + "address": "mnu86WRRfzhVJ1KEmK5ZQNQbN1YoVDypqP", + "hdPath": "m/44'/0'/0'/1/13", + "transactions": [] + }, + { + "index": 14, + "scriptPubKey": "76a9141b70ce0a811391703e2b0b2f084fcb0972939cbd88ac", + "pubkey": "21028a0595f85a32382db7951199e9496afa2cc79676d36f0b9d7a4ed4a81526e174ac", + "address": "mi23g3x5ZkPHTjDMntoXCDedWz61PTvjzt", + "hdPath": "m/44'/0'/0'/1/14", + "transactions": [] + }, + { + "index": 15, + "scriptPubKey": "76a91430b3f0f1db0743213fbd91077286d902593ab58b88ac", + "pubkey": "2103ed4b201fe6bc916e614a0114b0393fddae854f323c71e0bd07045257d20e4d48ac", + "address": "mjxUHo4Yzxk1tPFg7dmPqVvHNN2NZfJw7E", + "hdPath": "m/44'/0'/0'/1/15", + "transactions": [] + }, + { + "index": 16, + "scriptPubKey": "76a9145af35c1bedaba4bcff28d41e8f06c9a515c44e8688ac", + "pubkey": "2102aa553d8c3393356deb5ed0e093ea95e902a1ae1dada925e0160b54bfa2414becac", + "address": "mooreFhJYaHvTC3eByZqqKr1FVayk57FG6", + "hdPath": "m/44'/0'/0'/1/16", + "transactions": [] + }, + { + "index": 17, + "scriptPubKey": "76a914dc54045036afdbe306b00f876ff7ea639c5f293688ac", + "pubkey": "2103504db2f02104338ea0acb6d4528b63867e2bdea50a6fb903673474d71f566dfeac", + "address": "n1bwbrEi4j8tVHiqSP22EDkGMAw8feqE8D", + "hdPath": "m/44'/0'/0'/1/17", + "transactions": [] + }, + { + "index": 18, + "scriptPubKey": "76a9148187aa035fb4cd40ecf1dc8af2271fff37beab2688ac", + "pubkey": "2102e6f78f8be73870f90cf9d4b61feda499b62f8e1c4d71aff27bc916a3cc9f7a3aac", + "address": "msKqyuctFQy3ZhaC8g5wDNhgcz2gp5Zntw", + "hdPath": "m/44'/0'/0'/1/18", + "transactions": [] + }, + { + "index": 19, + "scriptPubKey": "76a9148a1e34b62554ae72c3b90e2f7697d6f14f25015988ac", + "pubkey": "2102ee7bcae6ccb7a561e108cf08dd34aea458977279822e5db635b2eec453a2407dac", + "address": "mt7FjGbLcCKN7M4oChaNhqVprw4bAHwCVz", + "hdPath": "m/44'/0'/0'/1/19", + "transactions": [] + } + ] + }, + { + "index": 1, + "name": "account 1", + "hdPath": "m/44'/0'/1'", + "extPubKey": "tpubDDYSPVVe5aEsbtqpfJrvCsC2Q7BHo69zuUZfUkNjxkYB7bKBMFM7UeiHEu76KVpNtXhMUSruVn3RHauHfCUhP5Ppd1MECwqj6yNtRNULxs8", + "creationTime": "1499864347", + "externalAddresses": [ + { + "index": 0, + "scriptPubKey": "76a9145b4ff4a0eb0cb6d5905ca2edc9801fdd8b6e9cc588ac", + "pubkey": "210351180c97dbebc8cfe6b3e56d0e264ab2c6a883bc2d9dc60657ef1792ae4b0754ac", + "address": "moqmZvQtETJr4Bfs8v5LVPTShw5DBjPQaa", + "hdPath": "m/44'/0'/1'/0/0", + "transactions": [] + }, + { + "index": 1, + "scriptPubKey": "76a91400b8be36e33dad5bebcb86b182121bb40658af5388ac", + "pubkey": "2103b0e57d310e407a582d437d4f0888909a673ee2953db7a0f125ee0b35e7d8fafdac", + "address": "mfamcDyF9JWEovgUFKoM9phezdzbbM836c", + "hdPath": "m/44'/0'/1'/0/1", + "transactions": [] + }, + { + "index": 2, + "scriptPubKey": "76a914663c7b65b04cff534e22442ba51d2258812e6dad88ac", + "pubkey": "2103b8b012385b19e202caba05b21e982ab87edda246eeae9b85912c76f838977bfeac", + "address": "mpqXfz6NoVysNaBB2tbe9CFhAWCfP6ps66", + "hdPath": "m/44'/0'/1'/0/2", + "transactions": [] + }, + { + "index": 3, + "scriptPubKey": "76a914776547ef394f6f10aef7b8814e7becdbf000cd4a88ac", + "pubkey": "21039c712879899b428c7ca4bca819e5cd49e2c3c2457c46c5c04e39bf605062a23aac", + "address": "mrQG31TS8aZqBWbXZHgd3S1opeLs8B9EHL", + "hdPath": "m/44'/0'/1'/0/3", + "transactions": [] + }, + { + "index": 4, + "scriptPubKey": "76a914c244e57741c85de9194414993910e4cd27f734ad88ac", + "pubkey": "21030c11945e621030c36e75980c23dc5a44e8c6742d1afbf0146b9a24320ad09f7dac", + "address": "myE9vGhcQMawJEh9sEpm9EwgfiVdyNhPTC", + "hdPath": "m/44'/0'/1'/0/4", + "transactions": [] + }, + { + "index": 5, + "scriptPubKey": "76a914dcd91143105f140e193238e3c0d0cc645d6875d988ac", + "pubkey": "2103007414d1518efc3c0f0d62c5341f9f0c62bf306ec2cf67f933f7513266c92b37ac", + "address": "n1egzNBuMwrTYo98DNHRsa4sbvcraxt2eh", + "hdPath": "m/44'/0'/1'/0/5", + "transactions": [] + }, + { + "index": 6, + "scriptPubKey": "76a9140ee00cb6949631b6476e0b4abc421e908c37deaa88ac", + "pubkey": "21023e0b8c5964e1850f2b76595dee47a069455d7617edb0b4b3e0233ed015def994ac", + "address": "mgsc9yy5CBcKmsGRvNrDJYHNrP4JcuCDCn", + "hdPath": "m/44'/0'/1'/0/6", + "transactions": [] + }, + { + "index": 7, + "scriptPubKey": "76a914c39095793282b4abcb027db7e079523bbdb3dbb388ac", + "pubkey": "210388fe56ec562b4d04d55eb6d14885f4720d3f39dae7a1697111c3815837335f5fac", + "address": "myM1GKorp24xpCCiZ546AX6ZDvmuRTqP9c", + "hdPath": "m/44'/0'/1'/0/7", + "transactions": [] + }, + { + "index": 8, + "scriptPubKey": "76a914f9869dc66c6ef8eeda9972d11154a4f50750ab0d88ac", + "pubkey": "21032c87ce4f82882b9932cf54129a6eee71e4c52220b10737d6ac81f4ac074b4b72ac", + "address": "n4GKp8CUHGmjcLavVwJc2bYBbUzf288kCd", + "hdPath": "m/44'/0'/1'/0/8", + "transactions": [] + }, + { + "index": 9, + "scriptPubKey": "76a914aab1d572c846ae152049aea07e23ee3a78eb82e788ac", + "pubkey": "210392e1446f25a306a0595dd6dcb0a2b6a1516170eac2fba496ff475ed22f5aff63ac", + "address": "mw5WCmnjEaxLnMpBcnAmJSpcYnRLiUq7mr", + "hdPath": "m/44'/0'/1'/0/9", + "transactions": [] + }, + { + "index": 10, + "scriptPubKey": "76a91488f2f0fe78ef41dbd7c25023ec4d61d9eb1a68e688ac", + "pubkey": "210324a61b09ce5aa64276516a57999a5101a65339b2c297de459ecabe630fcb1aa7ac", + "address": "mt15E1K1FKbWVMtiG7SN6rd8598umBuf65", + "hdPath": "m/44'/0'/1'/0/10", + "transactions": [] + }, + { + "index": 11, + "scriptPubKey": "76a9142e3f5af5462e89aaf88367d9f8c33a7050d37dcf88ac", + "pubkey": "21035387e7a7da9e9e1052aa24e651021357bab900d8b0c18c020912e629050a75deac", + "address": "mjjVGt7GW9hsrn8W6peL3Jgf8RHZ7amnVa", + "hdPath": "m/44'/0'/1'/0/11", + "transactions": [] + }, + { + "index": 12, + "scriptPubKey": "76a914ce8785a84030d01f9142cdbd67cacdfe785131c188ac", + "pubkey": "210336d2ab62c11ca3028f5832dc258f58add25363f315a4119b43bf15d4ceea2f60ac", + "address": "mzLyqp2PERVdyeF1VrBQamnbnRptgRWvic", + "hdPath": "m/44'/0'/1'/0/12", + "transactions": [] + }, + { + "index": 13, + "scriptPubKey": "76a914023f5745d4f7c66ee2829e704c6f2f99390709d488ac", + "pubkey": "21024b5c5643ef0e04c75a109e2d8a604c7214ea1371f38dd6788530f0d1d43764c4ac", + "address": "mfiqXSYVy7isVa1sEqUHJaXT6bE32BfuPJ", + "hdPath": "m/44'/0'/1'/0/13", + "transactions": [] + }, + { + "index": 14, + "scriptPubKey": "76a91432bcf1dd2d3da83ef18734a32a9569e2e83db60488ac", + "pubkey": "2103368db9e9de66d155ff3116194b3a2000f40fc303727229c891bebe6868115624ac", + "address": "mk9ERkypcQPBxhsd6sMoG87XXJNYxxZNss", + "hdPath": "m/44'/0'/1'/0/14", + "transactions": [] + }, + { + "index": 15, + "scriptPubKey": "76a914353ab6c8849f53f3093b2e449bf52f7e12565f4488ac", + "pubkey": "21035b866e5170a471e74bc592faf748234f576e25b4db672835f5464c2d4fb42e42ac", + "address": "mkNQSjwEGhojQVUSpMswmhRYSfepBWAb4W", + "hdPath": "m/44'/0'/1'/0/15", + "transactions": [] + }, + { + "index": 16, + "scriptPubKey": "76a914c08ed1527423a30ec75bfe7ea8b698f973db41f688ac", + "pubkey": "2103699a851dcd5e96d44d879d48147f319f41232216b07ded838eeeb39665366eddac", + "address": "my5782jgyXpgVXZ1uRXXGZStEeNATfU9r5", + "hdPath": "m/44'/0'/1'/0/16", + "transactions": [] + }, + { + "index": 17, + "scriptPubKey": "76a9145990a720448db3774454badfd4e1b1395b63a2b888ac", + "pubkey": "21031b40247645ef1dd6f30837f7ea51ae6807b061eb88cedee8a012eb8bb3f5d527ac", + "address": "mogXinKWgbDYUgBcE384wUnw3NBpdz82oS", + "hdPath": "m/44'/0'/1'/0/17", + "transactions": [] + }, + { + "index": 18, + "scriptPubKey": "76a914d94f9d5790c9fe1f6c6df626f04430df0e3aee9188ac", + "pubkey": "2102dd726b71513a6d386ae5f17fe3e3d213345c66647023291ad3821afc0130192eac", + "address": "n1KzJQFxqHq6nhihB1X1nrsxzLVrtU9dVe", + "hdPath": "m/44'/0'/1'/0/18", + "transactions": [] + }, + { + "index": 19, + "scriptPubKey": "76a91401724f3a69ac4877a38a5e4ba7cabd6e2ef126fc88ac", + "pubkey": "2102d0ab0991041fb26fa772a87c2527744dfd0e51966db1be1da74dc775b5ede570ac", + "address": "mfebucDwfJxNxvBWZ34tn3Bmk4fykymqs4", + "hdPath": "m/44'/0'/1'/0/19", + "transactions": [] + } + ], + "internalAddresses": [ + { + "index": 0, + "scriptPubKey": "76a914edd12c67f47865f9acf2d5c7de5391252f21899488ac", + "pubkey": "2102bb0f74863a4b6572c55a800b6dfe97a58512955953600a67b348a044336649e3ac", + "address": "n3CR29M3x739UyXZoDu4xTCCz5u3RNZK9T", + "hdPath": "m/44'/0'/1'/1/0", + "transactions": [] + }, + { + "index": 1, + "scriptPubKey": "76a9143c3c4aac4c3767bb3490f31cec707b88dd0c4dea88ac", + "pubkey": "2103018f626d0e97c747496b117f1a610e94b693e5997879459fe29b60048a2733beac", + "address": "mm1T4hnZdHCXyPP3RAdqa1v9sa5xh6gwdK", + "hdPath": "m/44'/0'/1'/1/1", + "transactions": [] + }, + { + "index": 2, + "scriptPubKey": "76a9148141fde76b739447129187ea4c1e9a63ed93665988ac", + "pubkey": "210326135dc577f51a53e29f4d101de5efb2c2a0fe356068ae734531ec681260cb1cac", + "address": "msJQWzUBq7SVw66dvAPT5dq5APRY6NXetq", + "hdPath": "m/44'/0'/1'/1/2", + "transactions": [] + }, + { + "index": 3, + "scriptPubKey": "76a9142f3bc11656896f94558d001848f463eda73f659f88ac", + "pubkey": "210336040a7417c17c657125f10b011a37614d8a908a1d86a0c4f6fbf066f5d804baac", + "address": "mjphdsHxJ19UBAYpCKfryvT3kZNCtJZLhK", + "hdPath": "m/44'/0'/1'/1/3", + "transactions": [] + }, + { + "index": 4, + "scriptPubKey": "76a9142d703f83c78fb5ca50a61ad753149d65ab7581b888ac", + "pubkey": "21028b7beb8256f753cf3a25ce295de1b6b660c82d112d23f1eee4c155dfc4d0b229ac", + "address": "mjfDApbWunpu83rmNMCE1HRXvrr4b6xGaW", + "hdPath": "m/44'/0'/1'/1/4", + "transactions": [] + }, + { + "index": 5, + "scriptPubKey": "76a91446b6a39853f8a1a8e82e3cf3706f7eb25f0febda88ac", + "pubkey": "21025b7a9a1fdb837f52317975e53c5bf32a111455d6c099af514774bf62c3b4cde5ac", + "address": "mmxrPTwe3ix4TGrgdYH6n86bzE3XfCXGY4", + "hdPath": "m/44'/0'/1'/1/5", + "transactions": [] + }, + { + "index": 6, + "scriptPubKey": "76a914393eb60d29062ad6456960b618d574178eb1f9fc88ac", + "pubkey": "21031df457e11dc3175a419d2a58f5afb3e5d3a55fc99df84eaf0194ff5fc082f459ac", + "address": "mkjdwEZZFK9hMj6VWiBC5s8KNgm4kWxjE6", + "hdPath": "m/44'/0'/1'/1/6", + "transactions": [] + }, + { + "index": 7, + "scriptPubKey": "76a914a8238fe38143865ae8541fcc6b0d89769c10a6a388ac", + "pubkey": "210244e5496665fad027bc357a7f831adf94f1983ac4295c15b6ea17f166a9d9d92aac", + "address": "mvqzRBE9fhZYiocAGUsiomos5g6oQjLcH2", + "hdPath": "m/44'/0'/1'/1/7", + "transactions": [] + }, + { + "index": 8, + "scriptPubKey": "76a914db46cdb04d3829d2b35a5fb21b779db64b5660f488ac", + "pubkey": "2102e66e9aadc4daba2e9e5c6b136e459aec1b006c614b8b717108ee553b54d727cdac", + "address": "n1WP6aNzizB3pGehVAoBuz628yQXs1ELnu", + "hdPath": "m/44'/0'/1'/1/8", + "transactions": [] + }, + { + "index": 9, + "scriptPubKey": "76a914db1eccf166abbed778f811b0dbabba9328d2e27388ac", + "pubkey": "210367c80548b9c8c0cf23c74150b535849d8a1c975dc87a7148366cf6ff196c3f82ac", + "address": "n1VZB8DSYpfJEpcv967TWsAeJoWa9wQ5Rv", + "hdPath": "m/44'/0'/1'/1/9", + "transactions": [] + }, + { + "index": 10, + "scriptPubKey": "76a914c5c0fc5de7602d4af8d1558a98dff529a65723a888ac", + "pubkey": "210379a0b3132a2b52e52bd16c3f2b353a549479f6effadbd8f6be7271e1c3b3642fac", + "address": "myYabjW9ChkzENxCbpEoQpGBCLBqamKajQ", + "hdPath": "m/44'/0'/1'/1/10", + "transactions": [] + }, + { + "index": 11, + "scriptPubKey": "76a9142d8b29ac8c45622fd43ee1d675f634a392a498a888ac", + "pubkey": "2103f6e495b79c0f998c5d53c34a721d4120788720435119cb3fc40377f2fc5ada37ac", + "address": "mjfmQtNQciA6khaUJjuMz6dWC4w1axWKar", + "hdPath": "m/44'/0'/1'/1/11", + "transactions": [] + }, + { + "index": 12, + "scriptPubKey": "76a914c2dcc485b742b9026ed5440a442e6ced657f097c88ac", + "pubkey": "2103c37c5aad9029d90c43064c01692d8cb0cf23c6cd15fe28430106dcd27673c404ac", + "address": "myHHrUXajfN82wDexT2QXfmCY7vhWzq3Fb", + "hdPath": "m/44'/0'/1'/1/12", + "transactions": [] + }, + { + "index": 13, + "scriptPubKey": "76a914db62f0674ae3dd2ab86a3049af3c485fbc40730f88ac", + "pubkey": "2102078f629c4a189d1a89205817b6bc3fa108cc0bf18c397946f452841cff5984a0ac", + "address": "n1WxoUMSA7XQf5G69kknuqWMUNeSnWGttR", + "hdPath": "m/44'/0'/1'/1/13", + "transactions": [] + }, + { + "index": 14, + "scriptPubKey": "76a914714bbdd9b242b81751c29824d601dcee00cdfa8d88ac", + "pubkey": "2103eb10be2037d6663b2358fc7b07472988e5b95fc41472c4e11a14cf38bcfbf64dac", + "address": "mqr1PMEdkTu35St4m2SYM6vUpQTb8KppqW", + "hdPath": "m/44'/0'/1'/1/14", + "transactions": [] + }, + { + "index": 15, + "scriptPubKey": "76a91439767f9cc0a16031ea087a428fffea4878b882e988ac", + "pubkey": "2102b9327313f4929762ed34bec3f91c01c082cfd97f8d04b453f840f60f8bafca21ac", + "address": "mkknmQb19cZjpy7GpCmV2VkbnNPpDMJzJt", + "hdPath": "m/44'/0'/1'/1/15", + "transactions": [] + }, + { + "index": 16, + "scriptPubKey": "76a91448fb5d7bc23f7544bc4829671bf5aec4b21734d688ac", + "pubkey": "2103651da4df70cc157a72151fbbfafc915d4953ff6207d69b9789bcca17946e64e7ac", + "address": "mnAr52LwDY3vS3B8dUhsNWQsgx8k94fZfo", + "hdPath": "m/44'/0'/1'/1/16", + "transactions": [] + }, + { + "index": 17, + "scriptPubKey": "76a9144f2c4f78364f652d5b6c91c7c55711547c681fb788ac", + "pubkey": "21020bd2e8dbde5d0162101f94eb196e3d15afcae618677028478025ad90ab4a9611ac", + "address": "mnjakx7tCPWh7drKTbaxnFiCE65KQMP6Y3", + "hdPath": "m/44'/0'/1'/1/17", + "transactions": [] + }, + { + "index": 18, + "scriptPubKey": "76a9146e662f3f4938e09a4dbc7f457a6d0c2961b4364588ac", + "pubkey": "21022ef28bb58b89f197be626e5d46cd2a341b59437144b99c55eb420d5834de3fbcac", + "address": "mqah34cGeptoWf1wZ97fP6emPFKWppaTx8", + "hdPath": "m/44'/0'/1'/1/18", + "transactions": [] + }, + { + "index": 19, + "scriptPubKey": "76a914a352f6df87c546c99f6d92ccf3416727d5c3905888ac", + "pubkey": "21020c8073142f73adfb5f68492a27d60e5c8bbff821edc35f66822d894f17151639ac", + "address": "mvQXpnZZQrW3X59s7KhA4QCTJhYNsxBeXJ", + "hdPath": "m/44'/0'/1'/1/19", + "transactions": [] + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/Stratis.Bitcoin.IntegrationTests/NodeBuilder.cs b/Stratis.Bitcoin.IntegrationTests/NodeBuilder.cs index a4efb81ef62..543d7263ac7 100644 --- a/Stratis.Bitcoin.IntegrationTests/NodeBuilder.cs +++ b/Stratis.Bitcoin.IntegrationTests/NodeBuilder.cs @@ -81,7 +81,9 @@ public static FullNode BuildFullNode(NodeSettings args) .UseWallet() .AddRPC() .Build(); - node.WalletManager.CreateWallet("blabla", "test"); + var testWalletPath = Path.Combine(node.DataFolder.WalletPath, "test.wallet.json"); + File.Copy("Data/test.wallet.json", testWalletPath); + //node.WalletManager.CreateWallet("blabla", "test"); return node; } diff --git a/Stratis.Bitcoin.IntegrationTests/Stratis.Bitcoin.IntegrationTests.csproj b/Stratis.Bitcoin.IntegrationTests/Stratis.Bitcoin.IntegrationTests.csproj index 5951b37b6b8..4ae704f195e 100644 --- a/Stratis.Bitcoin.IntegrationTests/Stratis.Bitcoin.IntegrationTests.csproj +++ b/Stratis.Bitcoin.IntegrationTests/Stratis.Bitcoin.IntegrationTests.csproj @@ -43,6 +43,9 @@ + + Always + PreserveNewest diff --git a/Stratis.Bitcoin/Miner/PowMining.cs b/Stratis.Bitcoin/Miner/PowMining.cs index f7973ace5af..ba3d51d6f6b 100644 --- a/Stratis.Bitcoin/Miner/PowMining.cs +++ b/Stratis.Bitcoin/Miner/PowMining.cs @@ -167,6 +167,7 @@ public List GenerateBlocks(ReserveScript reserveScript, ulong generate, // ensure the block is written to disk ulong retry = 0; while (++retry < maxTries && + nHeight != nHeight && !this.blockRepository.ExistAsync(blockResult.ChainedBlock.HashBlock).GetAwaiter().GetResult()) Thread.Sleep(100); diff --git a/Stratis.Bitcoin/RPC/Controllers/WalletRPCController.cs b/Stratis.Bitcoin/RPC/Controllers/WalletRPCController.cs index e24ae2b2d4c..31eefc2212f 100644 --- a/Stratis.Bitcoin/RPC/Controllers/WalletRPCController.cs +++ b/Stratis.Bitcoin/RPC/Controllers/WalletRPCController.cs @@ -37,10 +37,17 @@ public IWalletManager WalletManager get; set; } + + [ActionName("sendtoaddress")] + public uint256 SendToAddress(BitcoinAddress bitcoinAddress, Money amount) + { + return uint256.Zero; + } + [ActionName("generate")] public List Generate(int nBlock) { - var mining = serviceProvider.GetRequiredService(); + var mining = this.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); From 5d45cb113fdcedf46d3bc129095e135e4996f5d7 Mon Sep 17 00:00:00 2001 From: NicolasDorier Date: Wed, 12 Jul 2017 22:30:57 +0900 Subject: [PATCH 4/4] Create model binders for the RPC controller --- Stratis.Bitcoin.IntegrationTests/Program.cs | 2 +- .../WalletTests.cs | 15 +++++ .../ModelBinders/DestinationModelBinder.cs | 65 +++++++++++++++++++ .../RPC/ModelBinders/MoneyModelBinder.cs | 43 ++++++++++++ Stratis.Bitcoin/RPC/WebHostExtensions.cs | 6 ++ 5 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 Stratis.Bitcoin/RPC/ModelBinders/DestinationModelBinder.cs create mode 100644 Stratis.Bitcoin/RPC/ModelBinders/MoneyModelBinder.cs diff --git a/Stratis.Bitcoin.IntegrationTests/Program.cs b/Stratis.Bitcoin.IntegrationTests/Program.cs index 82a2095ee3f..79b05d84008 100644 --- a/Stratis.Bitcoin.IntegrationTests/Program.cs +++ b/Stratis.Bitcoin.IntegrationTests/Program.cs @@ -8,7 +8,7 @@ class Program { public static void Main(string[] args) { - new WalletTests().CanMineBlocks(); + new WalletTests().CanSendToAddress(); } } } diff --git a/Stratis.Bitcoin.IntegrationTests/WalletTests.cs b/Stratis.Bitcoin.IntegrationTests/WalletTests.cs index 5ff2522f535..9c4b3ccbdd8 100644 --- a/Stratis.Bitcoin.IntegrationTests/WalletTests.cs +++ b/Stratis.Bitcoin.IntegrationTests/WalletTests.cs @@ -90,6 +90,21 @@ public void CanMineBlocks() } } + [Fact] + public void CanSendToAddress() + { + using(NodeBuilder builder = NodeBuilder.Create()) + { + var stratisNodeSync = builder.CreateStratisNode(); + builder.StartAll(); + var rpc = stratisNodeSync.CreateRPCClient(); + rpc.SendCommand(NBitcoin.RPC.RPCOperations.generate, 101); + var address = new Key().PubKey.GetAddress(rpc.Network); + var tx = rpc.SendToAddress(address, Money.Coins(1.0m)); + Assert.NotNull(tx); + } + } + [Fact] public void WalletCanReorg() { diff --git a/Stratis.Bitcoin/RPC/ModelBinders/DestinationModelBinder.cs b/Stratis.Bitcoin/RPC/ModelBinders/DestinationModelBinder.cs new file mode 100644 index 00000000000..4dbd0c3b7e8 --- /dev/null +++ b/Stratis.Bitcoin/RPC/ModelBinders/DestinationModelBinder.cs @@ -0,0 +1,65 @@ +using Microsoft.AspNetCore.Mvc.ModelBinding; +using NBitcoin; +using System.Reflection; +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc.Internal; + +namespace Stratis.Bitcoin.RPC.ModelBinders +{ + public class DestinationModelBinder : IModelBinder, IModelBinderProvider + { + public DestinationModelBinder() + { + + } + + #region IModelBinder Members + + public Task BindModelAsync(ModelBindingContext bindingContext) + { + if(!SupportType(bindingContext.ModelType)) + { + return TaskCache.CompletedTask; + } + + ValueProviderResult val = bindingContext.ValueProvider.GetValue( + bindingContext.ModelName); + if(val == null) + { + return TaskCache.CompletedTask; + } + + string key = val.FirstValue as string; + if(key == null) + { + return TaskCache.CompletedTask; + } + + var network = (Network)bindingContext.HttpContext.RequestServices.GetService(typeof(Network)); + //TODO: Use var data = Network.Parse(key, network); when NBitcoin is updated to latest version + var data = BitcoinAddress.Create(key, network); + if(!bindingContext.ModelType.IsInstanceOfType(data)) + { + throw new FormatException("Invalid destination type"); + } + bindingContext.Result = ModelBindingResult.Success(data); + return TaskCache.CompletedTask; + } + + private static bool SupportType(Type type) + { + return (typeof(Base58Data).GetTypeInfo().IsAssignableFrom(type) || + typeof(IDestination).GetTypeInfo().IsAssignableFrom(type)); + } + + public IModelBinder GetBinder(ModelBinderProviderContext context) + { + if(SupportType(context.Metadata.ModelType)) + return this; + return null; + } + + #endregion + } +} diff --git a/Stratis.Bitcoin/RPC/ModelBinders/MoneyModelBinder.cs b/Stratis.Bitcoin/RPC/ModelBinders/MoneyModelBinder.cs new file mode 100644 index 00000000000..5db2b8dee9b --- /dev/null +++ b/Stratis.Bitcoin/RPC/ModelBinders/MoneyModelBinder.cs @@ -0,0 +1,43 @@ +using Microsoft.AspNetCore.Mvc.Internal; +using Microsoft.AspNetCore.Mvc.ModelBinding; +using NBitcoin; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Stratis.Bitcoin.RPC.ModelBinders +{ + public class MoneyModelBinder : IModelBinder, IModelBinderProvider + { + public Task BindModelAsync(ModelBindingContext bindingContext) + { + if(bindingContext.ModelType != typeof(Money)) + { + return TaskCache.CompletedTask; + } + + ValueProviderResult val = bindingContext.ValueProvider.GetValue( + bindingContext.ModelName); + if(val == null) + { + return TaskCache.CompletedTask; + } + + string key = val.FirstValue as string; + if(key == null) + { + return TaskCache.CompletedTask; + } + return Task.FromResult(Money.Parse(key)); + } + + public IModelBinder GetBinder(ModelBinderProviderContext context) + { + if(context.Metadata.ModelType == typeof(Money)) + return this; + return null; + } + } +} diff --git a/Stratis.Bitcoin/RPC/WebHostExtensions.cs b/Stratis.Bitcoin/RPC/WebHostExtensions.cs index 675d4e0d46d..30e90cf8ba8 100644 --- a/Stratis.Bitcoin/RPC/WebHostExtensions.cs +++ b/Stratis.Bitcoin/RPC/WebHostExtensions.cs @@ -1,6 +1,7 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Stratis.Bitcoin.Miner; +using Stratis.Bitcoin.RPC.ModelBinders; using Stratis.Bitcoin.Wallet; using System; using System.Collections.Generic; @@ -15,6 +16,11 @@ public static IWebHostBuilder ForFullNode(this IWebHostBuilder hostBuilder, Full { hostBuilder.ConfigureServices(s => { + s.AddMvcCore(o => + { + o.ModelBinderProviders.Insert(0, new DestinationModelBinder()); + o.ModelBinderProviders.Insert(0, new MoneyModelBinder()); + }); s.AddSingleton(fullNode); s.AddSingleton(fullNode as Builder.IFullNode); s.AddSingleton(fullNode.Network);