public static bool CheckWalletBalance(CoreNode node, Money amount) { var total = node.FullNode.WalletManager().GetSpendableTransactionsInWallet(Name).Sum(s => s.Transaction.Amount); return(total == amount); }
public void TxMempoolBlockDoublespend() { using (NodeBuilder builder = NodeBuilder.Create(this)) { CoreNode stratisNodeSync = builder.CreateStratisPowNode(this.network).WithDummyWallet().Start(); stratisNodeSync.FullNode.NodeService <MempoolSettings>().RequireStandard = true; // make sure to test standard tx TestHelper.MineBlocks(stratisNodeSync, 100); // coinbase maturity = 100 // Make sure skipping validation of transctions that were // validated going into the memory pool does not allow // double-spends in blocks to pass validation when they should not. Script scriptPubKey = PayToPubkeyHashTemplate.Instance.GenerateScriptPubKey(stratisNodeSync.MinerSecret.PubKey); Block genBlock = stratisNodeSync.FullNode.BlockStore().GetBlockAsync(stratisNodeSync.FullNode.Chain.GetBlock(1).HashBlock).Result; // Create a double-spend of mature coinbase txn: var spends = new List <Transaction>(2); foreach (int index in Enumerable.Range(1, 2)) { Transaction trx = stratisNodeSync.FullNode.Network.CreateTransaction(); trx.AddInput(new TxIn(new OutPoint(genBlock.Transactions[0].GetHash(), 0), scriptPubKey)); trx.AddOutput(Money.Cents(11), new Key().PubKey.Hash); // Sign: trx.Sign(stratisNodeSync.FullNode.Network, stratisNodeSync.MinerSecret, false); spends.Add(trx); } // Test 1: block with both of those transactions should be rejected. var tipBeforeBlockCreation = stratisNodeSync.FullNode.Chain.Tip; Assert.Throws <ConsensusException>(() => { Task.Delay(2).Wait(); Block block = TestHelper.GenerateBlockManually(stratisNodeSync, spends); }); Assert.True(stratisNodeSync.FullNode.Chain.Tip.HashBlock == tipBeforeBlockCreation.HashBlock); // Test 2: ... and should be rejected if spend1 is in the memory pool tipBeforeBlockCreation = stratisNodeSync.FullNode.Chain.Tip; Assert.True(stratisNodeSync.AddToStratisMempool(spends[0])); Assert.Throws <ConsensusException>(() => { Task.Delay(2).Wait(); Block block = TestHelper.GenerateBlockManually(stratisNodeSync, spends, 100_000); }); Assert.True(stratisNodeSync.FullNode.Chain.Tip.HashBlock == tipBeforeBlockCreation.HashBlock); stratisNodeSync.FullNode.MempoolManager().Clear().Wait(); // Test 3: ... and should be rejected if spend2 is in the memory pool tipBeforeBlockCreation = stratisNodeSync.FullNode.Chain.Tip; Assert.True(stratisNodeSync.AddToStratisMempool(spends[1])); Assert.Throws <ConsensusException>(() => { Task.Delay(2).Wait(); Block block = TestHelper.GenerateBlockManually(stratisNodeSync, spends, 100_000_000); }); Assert.True(stratisNodeSync.FullNode.Chain.Tip.HashBlock == tipBeforeBlockCreation.HashBlock); stratisNodeSync.FullNode.MempoolManager().Clear().Wait(); // Final sanity test: first spend in mempool, second in block, that's OK: var oneSpend = new List <Transaction>(); oneSpend.Add(spends[0]); Assert.True(stratisNodeSync.AddToStratisMempool(spends[1])); var validBlock = TestHelper.GenerateBlockManually(stratisNodeSync, oneSpend); TestHelper.WaitLoop(() => stratisNodeSync.FullNode.ConsensusManager().Tip.HashBlock == stratisNodeSync.FullNode.Chain.Tip.HashBlock); Assert.True(stratisNodeSync.FullNode.Chain.Tip.HashBlock == validBlock.GetHash()); // spends[1] should have been removed from the mempool when the // block with spends[0] is accepted: TestHelper.WaitLoop(() => stratisNodeSync.FullNode.MempoolManager().MempoolSize().Result == 0); } }
/// <summary> /// Ensures a node is internally synced and at a given height. /// </summary> /// <param name="node">This node.</param> /// <param name="height">At which height should it be synced to.</param> /// <returns>Returns <c>true</c> if the node is synced at a given height.</returns> public static bool IsNodeSyncedAtHeight(CoreNode node, int height, int waitTimeSeconds = 60) { TestBase.WaitLoopMessage(() => { return(node.FullNode.ConsensusManager().Tip.Height == height, $"Node height: {node.FullNode.ConsensusManager().Tip.Height}; Expected height: {height}"); }, waitTimeSeconds); return(true); }
public static void EnableBlockPropagation(CoreNode from, CoreNode to) { from.FullNode.ConnectionManager.ConnectedPeers.FindByEndpoint(to.Endpoint).Behavior <BlockStoreBehavior>().CanRespondToGetDataPayload = true; }
public async Task RemoveTransactionsFromWalletCalledWithMoreThanOneFilter() { using (NodeBuilder builder = NodeBuilder.Create(this)) { // Arrange. CoreNode node = builder.CreateStratisPosNode(this.network).Start(); this.AddAndLoadWalletFileToWalletFolder(node); // Make sure the account is used, i.e, it has transactions. WalletHistoryModel history = await $"http://localhost:{node.ApiPort}/api" .AppendPathSegment("wallet/history") .SetQueryParams(new { walletName = this.walletWithFundsName, accountName = "account 0" }) .GetJsonAsync <WalletHistoryModel>(); history.AccountsHistoryModel.Should().NotBeEmpty(); history.AccountsHistoryModel.First().TransactionsHistory.Should().NotBeEmpty(); // Act. Func <Task> act1 = async() => await $"http://localhost:{node.ApiPort}/api" .AppendPathSegment("wallet/remove-transactions") .SetQueryParams(new { walletName = this.walletWithFundsName, all = true, ids = new[] { "3ccdfdce538eabe4d18efd2ee8fdc2554dc95cae6ce082aa4b5c7b9074b2e0a3" } }) .DeleteAsync() .ReceiveJson <HashSet <(uint256 transactionId, DateTimeOffset creationTime)> >(); Func <Task> act2 = async() => await $"http://localhost:{node.ApiPort}/api" .AppendPathSegment("wallet/remove-transactions") .SetQueryParams(new { walletName = this.walletWithFundsName, fromDate = "06/06/2018", ids = new[] { "3ccdfdce538eabe4d18efd2ee8fdc2554dc95cae6ce082aa4b5c7b9074b2e0a3" } }) .DeleteAsync() .ReceiveJson <HashSet <(uint256 transactionId, DateTimeOffset creationTime)> >(); Func <Task> act3 = async() => await $"http://localhost:{node.ApiPort}/api" .AppendPathSegment("wallet/remove-transactions") .SetQueryParams(new { walletName = this.walletWithFundsName, fromDate = "06/06/2018", all = true }) .DeleteAsync() .ReceiveJson <HashSet <(uint256 transactionId, DateTimeOffset creationTime)> >(); Func <Task> act4 = async() => await $"http://localhost:{node.ApiPort}/api" .AppendPathSegment("wallet/remove-transactions") .SetQueryParams(new { walletName = this.walletWithFundsName, fromDate = "06/06/2018", all = true, ids = new[] { "3ccdfdce538eabe4d18efd2ee8fdc2554dc95cae6ce082aa4b5c7b9074b2e0a3" } }) .DeleteAsync() .ReceiveJson <HashSet <(uint256 transactionId, DateTimeOffset creationTime)> >(); // Assert. FlurlHttpException exception1 = act1.Should().Throw <FlurlHttpException>().Which; HttpResponseMessage response1 = exception1.Call.Response; FlurlHttpException exception2 = act2.Should().Throw <FlurlHttpException>().Which; HttpResponseMessage response2 = exception2.Call.Response; FlurlHttpException exception3 = act3.Should().Throw <FlurlHttpException>().Which; HttpResponseMessage response3 = exception3.Call.Response; FlurlHttpException exception4 = act4.Should().Throw <FlurlHttpException>().Which; HttpResponseMessage response4 = exception4.Call.Response; ErrorResponseLists errorResponse1 = JsonConvert.DeserializeObject <ErrorResponseLists>(await response1.Content.ReadAsStringAsync()); ErrorResponseLists errorResponse2 = JsonConvert.DeserializeObject <ErrorResponseLists>(await response2.Content.ReadAsStringAsync()); ErrorResponseLists errorResponse3 = JsonConvert.DeserializeObject <ErrorResponseLists>(await response3.Content.ReadAsStringAsync()); ErrorResponseLists errorResponse4 = JsonConvert.DeserializeObject <ErrorResponseLists>(await response4.Content.ReadAsStringAsync()); response1.StatusCode.Should().Be(HttpStatusCode.BadRequest); response1.StatusCode.Should().Be(response2.StatusCode); response2.StatusCode.Should().Be(response3.StatusCode); response3.StatusCode.Should().Be(response4.StatusCode); ErrorModel errors = errorResponse1.Errors; errors.DeleteAll.Should().ContainSingle(); errors.DeleteAll.First().Should().Be($"Only one out of the query parameters '{nameof(RemoveTransactionsModel.DeleteAll)}', '{nameof(RemoveTransactionsModel.TransactionsIds)}' or '{nameof(RemoveTransactionsModel.FromDate)}' can be set."); errorResponse1.Should().BeEquivalentTo(errorResponse2); errorResponse2.Should().BeEquivalentTo(errorResponse3); errorResponse3.Should().BeEquivalentTo(errorResponse4); } }
public async Task SendingFromManyAddressesToOneAddress() { int sendingAccountBalanceOnStart = 98000596; int receivingAccountBalanceOnStart = 0; using (NodeBuilder builder = NodeBuilder.Create(this)) { // Arrange. // Create a sending and a receiving node. CoreNode sendingNode = builder.CreateStratisPosNode(this.network).WithWallet().Start(); CoreNode receivingNode = builder.CreateStratisPosNode(this.network).WithWallet().Start(); // Mine a few blocks to fund the sending node and connect the nodes. IEnumerable <string> addressesToFund = await $"http://localhost:{sendingNode.ApiPort}/api" .AppendPathSegment("wallet/unusedAddresses") .SetQueryParams(new { walletName = "mywallet", accountName = "account 0", count = 150 }) .GetJsonAsync <IEnumerable <string> >(); foreach (string address in addressesToFund) { TestHelper.MineBlocks(sendingNode, 1, syncNode: false, miningAddress: address); } TestHelper.ConnectAndSync(sendingNode, receivingNode); // Check balances. WalletBalanceModel sendingNodeBalances = await $"http://localhost:{sendingNode.ApiPort}/api" .AppendPathSegment("wallet/balance") .SetQueryParams(new { walletName = "mywallet" }) .GetJsonAsync <WalletBalanceModel>(); AccountBalanceModel sendingAccountBalance = sendingNodeBalances.AccountsBalances.Single(); (sendingAccountBalance.AmountConfirmed + sendingAccountBalance.AmountUnconfirmed).Should().Be(new Money(sendingAccountBalanceOnStart, MoneyUnit.BTC)); WalletBalanceModel receivingNodeBalances = await $"http://localhost:{receivingNode.ApiPort}/api" .AppendPathSegment("wallet/balance") .SetQueryParams(new { walletName = "mywallet" }) .GetJsonAsync <WalletBalanceModel>(); AccountBalanceModel receivingAccountBalance = receivingNodeBalances.AccountsBalances.Single(); (receivingAccountBalance.AmountConfirmed + receivingAccountBalance.AmountUnconfirmed).Should().Be(new Money(receivingAccountBalanceOnStart)); // Check max spendable amount. var maxBalanceResponse = await $"http://localhost:{sendingNode.ApiPort}/api" .AppendPathSegment("wallet/maxbalance") .SetQueryParams(new { walletName = "mywallet", accountName = "account 0", feetype = "low", allowunconfirmed = true }) .GetJsonAsync <MaxSpendableAmountModel>(); Money totalToSpend = maxBalanceResponse.MaxSpendableAmount + maxBalanceResponse.Fee; // Act. // Get an address to send to. IEnumerable <string> unusedaddresses = await $"http://localhost:{receivingNode.ApiPort}/api" .AppendPathSegment("wallet/unusedAddresses") .SetQueryParams(new { walletName = "mywallet", accountName = "account 0", count = 1 }) .GetJsonAsync <IEnumerable <string> >(); // Build and send the transaction with 50 recipients. WalletBuildTransactionModel buildTransactionModel = await $"http://localhost:{sendingNode.ApiPort}/api" .AppendPathSegment("wallet/build-transaction") .PostJsonAsync(new BuildTransactionRequest { WalletName = "mywallet", AccountName = "account 0", FeeAmount = maxBalanceResponse.Fee.ToString(), Password = "******", ShuffleOutputs = true, AllowUnconfirmed = true, Recipients = unusedaddresses.Select(address => new RecipientModel { DestinationAddress = address, Amount = maxBalanceResponse.MaxSpendableAmount.ToString() }).ToList() }) .ReceiveJson <WalletBuildTransactionModel>(); await $"http://localhost:{sendingNode.ApiPort}/api" .AppendPathSegment("wallet/send-transaction") .PostJsonAsync(new SendTransactionRequest { Hex = buildTransactionModel.Hex }) .ReceiveJson <WalletSendTransactionModel>(); // Assert. // The sending node should have 50 (+ fee) fewer coins. sendingNodeBalances = await $"http://localhost:{sendingNode.ApiPort}/api" .AppendPathSegment("wallet/balance") .SetQueryParams(new { walletName = "mywallet" }) .GetJsonAsync <WalletBalanceModel>(); sendingAccountBalance = sendingNodeBalances.AccountsBalances.Single(); (sendingAccountBalance.AmountConfirmed + sendingAccountBalance.AmountUnconfirmed).Should().Be(new Money(sendingAccountBalanceOnStart, MoneyUnit.BTC) - totalToSpend); // Mine and sync so that we make sure the receiving node is up to date. TestHelper.MineBlocks(sendingNode, 1); TestHelper.WaitForNodeToSync(sendingNode, receivingNode); // The receiving node should have 50 more coins. receivingNodeBalances = await $"http://localhost:{receivingNode.ApiPort}/api" .AppendPathSegment("wallet/balance") .SetQueryParams(new { walletName = "mywallet" }) .GetJsonAsync <WalletBalanceModel>(); receivingAccountBalance = receivingNodeBalances.AccountsBalances.Single(); (receivingAccountBalance.AmountConfirmed + receivingAccountBalance.AmountUnconfirmed).Should().Be(new Money(receivingAccountBalanceOnStart) + maxBalanceResponse.MaxSpendableAmount); } }
private void Mine100Coins(CoreNode node) { this.sharedSteps.MineBlocks(this.CoinBaseMaturity + 2, node, WalletAccountName, WalletName, WalletPassword); }
public void MustNotAnnounceABlock_WhenNotInBestChain() { using (NodeBuilder builder = NodeBuilder.Create(this)) { CoreNode stratisNodeSync = builder.CreateStratisPowNode(this.network, "bss-2-stratisNodeSync").WithReadyBlockchainData(ReadyBlockchain.BitcoinRegTest10Miner).Start(); CoreNode stratisNode1 = builder.CreateStratisPowNode(this.network, "bss-2-stratisNode1").WithReadyBlockchainData(ReadyBlockchain.BitcoinRegTest10Listener).Start(); CoreNode stratisNode2 = builder.CreateStratisPowNode(this.network, "bss-2-stratisNode2").WithReadyBlockchainData(ReadyBlockchain.BitcoinRegTest10NoWallet).Start(); // Store block 1 of chain0 for later usage ChainedHeader firstBlock = null; foreach (ChainedHeader chainedHeader in stratisNodeSync.FullNode.ChainIndexer.EnumerateToTip(this.network.GenesisHash)) { if (chainedHeader.Height == 1) { firstBlock = chainedHeader; } } Assert.NotNull(firstBlock); // Mine longer chain1 using node1 TestHelper.MineBlocks(stratisNode1, 15); IConnectionManager node1ConnectionManager = stratisNode1.FullNode.NodeService <IConnectionManager>(); node1ConnectionManager.Parameters.TemplateBehaviors.Add(new TestBehavior()); IConnectionManager node2ConnectionManager = stratisNode2.FullNode.NodeService <IConnectionManager>(); node2ConnectionManager.Parameters.TemplateBehaviors.Add(new TestBehavior()); // Connect node0 and node1 TestHelper.Connect(stratisNode1, stratisNodeSync); INetworkPeer connectedPeer = node1ConnectionManager.ConnectedPeers.FindByEndpoint(stratisNodeSync.Endpoint); TestBehavior testBehavior = connectedPeer.Behavior <TestBehavior>(); // We expect that node0 will abandon the 10 block chain and use the 15 block chain from node1 TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisNode1, stratisNodeSync)); // Connect all nodes together TestHelper.Connect(stratisNode2, stratisNodeSync); TestHelper.Connect(stratisNode1, stratisNode2); INetworkPeer connectedPeer2 = node2ConnectionManager.ConnectedPeers.FindByEndpoint(stratisNodeSync.Endpoint); TestBehavior testBehavior2 = connectedPeer2.Behavior <TestBehavior>(); // Wait for node2 to sync; it should have the 15 block chain TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisNode2, stratisNodeSync)); // Insert block 1 from chain0 into node1's announce queue BlockStoreSignaled node1BlockStoreSignaled = stratisNode1.FullNode.NodeService <BlockStoreSignaled>(); IAsyncQueue <ChainedHeader> node1BlocksToAnnounce = (IAsyncQueue <ChainedHeader>)node1BlockStoreSignaled.GetMemberValue("blocksToAnnounce"); Queue <ChainedHeader> node1QueueItems = (Queue <ChainedHeader>)node1BlocksToAnnounce.GetMemberValue("items"); TestHelper.WaitLoop(() => node1QueueItems.Count == 0); // Check that node2 does not have block 1 in test behaviour advertised list foreach (IncomingMessage message in testBehavior2.receivedMessageTracker["headers"]) { if (message.Message.Payload is HeadersPayload) { foreach (BlockHeader header in ((HeadersPayload)message.Message.Payload).Headers) { if (header.GetHash() == firstBlock.Header.GetHash()) { throw new Exception("Should not have received payload announcing block from wrong chain"); } } } } } }
public async Task InitializeNoWalletAsync() { InitializationStarted = true; AddressManager = null; var cancel = StoppingCts.Token; try { await SingleInstanceChecker.CheckAsync().ConfigureAwait(false); Cache = new MemoryCache(new MemoryCacheOptions { SizeLimit = 1_000, ExpirationScanFrequency = TimeSpan.FromSeconds(30) }); var bstoreInitTask = BitcoinStore.InitializeAsync(); var addressManagerFolderPath = Path.Combine(DataDir, "AddressManager"); AddressManagerFilePath = Path.Combine(addressManagerFolderPath, $"AddressManager{Network}.dat"); var addrManTask = InitializeAddressManagerBehaviorAsync(); var userAgent = Constants.UserAgents.RandomElement(); var connectionParameters = new NodeConnectionParameters { UserAgent = userAgent }; HostedServices.Register(new UpdateChecker(TimeSpan.FromMinutes(7), Synchronizer), "Software Update Checker"); HostedServices.Register(new SystemAwakeChecker(WalletManager), "System Awake Checker"); #region ProcessKillSubscription AppDomain.CurrentDomain.ProcessExit += async(s, e) => await DisposeAsync().ConfigureAwait(false); Console.CancelKeyPress += async(s, e) => { e.Cancel = true; Logger.LogWarning("Process was signaled for killing.", nameof(Global)); await DisposeAsync().ConfigureAwait(false); }; #endregion ProcessKillSubscription cancel.ThrowIfCancellationRequested(); #region TorProcessInitialization if (Config.UseTor) { using (BenchmarkLogger.Measure(operationName: "TorProcessManager.Start")) { TorManager = new TorProcessManager(TorSettings, Config.TorSocks5EndPoint); await TorManager.StartAsync(ensureRunning : true).ConfigureAwait(false); } var fallbackRequestTestUri = new Uri(Config.GetFallbackBackendUri(), "/api/software/versions"); TorManager.StartMonitor(TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(7), fallbackRequestTestUri); } else { TorSocks5EndPoint = null; } Logger.LogInfo($"{nameof(TorProcessManager)} is initialized."); #endregion TorProcessInitialization cancel.ThrowIfCancellationRequested(); #region BitcoinStoreInitialization await bstoreInitTask.ConfigureAwait(false); // Make sure that the height of the wallets will not be better than the current height of the filters. WalletManager.SetMaxBestHeight(BitcoinStore.IndexStore.SmartHeaderChain.TipHeight); #endregion BitcoinStoreInitialization cancel.ThrowIfCancellationRequested(); #region BitcoinCoreInitialization try { if (Config.StartLocalBitcoinCoreOnStartup) { BitcoinCoreNode = await CoreNode .CreateAsync( new CoreNodeParams( Network, BitcoinStore.MempoolService, HostedServices, Config.LocalBitcoinCoreDataDir, tryRestart : false, tryDeleteDataDir : false, EndPointStrategy.Default(Network, EndPointType.P2p), EndPointStrategy.Default(Network, EndPointType.Rpc), txIndex : null, prune : null, mempoolReplacement : "fee,optin", userAgent : $"/WasabiClient:{Constants.ClientVersion}/", fallbackFee : null, // ToDo: Maybe we should have it, not only for tests? Cache), cancel) .ConfigureAwait(false); } } catch (Exception ex) { Logger.LogError(ex); } await HostedServices.StartAllAsync(cancel).ConfigureAwait(false); var rpcFeeProvider = HostedServices.FirstOrDefault <RpcFeeProvider>(); FeeProviders = new FeeProviders(Synchronizer, rpcFeeProvider); #endregion BitcoinCoreInitialization cancel.ThrowIfCancellationRequested(); #region MempoolInitialization connectionParameters.TemplateBehaviors.Add(BitcoinStore.CreateUntrustedP2pBehavior()); #endregion MempoolInitialization cancel.ThrowIfCancellationRequested(); #region AddressManagerInitialization AddressManagerBehavior addressManagerBehavior = await addrManTask.ConfigureAwait(false); connectionParameters.TemplateBehaviors.Add(addressManagerBehavior); #endregion AddressManagerInitialization cancel.ThrowIfCancellationRequested(); #region P2PInitialization if (Network == Network.RegTest) { Nodes = new NodesGroup(Network, requirements: Constants.NodeRequirements); try { EndPoint bitcoinCoreEndpoint = Config.GetBitcoinP2pEndPoint(); Node node = await Node.ConnectAsync(Network.RegTest, bitcoinCoreEndpoint).ConfigureAwait(false); Nodes.ConnectedNodes.Add(node); RegTestMempoolServingNode = await Node.ConnectAsync(Network.RegTest, bitcoinCoreEndpoint).ConfigureAwait(false); RegTestMempoolServingNode.Behaviors.Add(BitcoinStore.CreateUntrustedP2pBehavior()); } catch (SocketException ex) { Logger.LogError(ex); } } else { if (Config.UseTor) { // onlyForOnionHosts: false - Connect to clearnet IPs through Tor, too. connectionParameters.TemplateBehaviors.Add(new SocksSettingsBehavior(Config.TorSocks5EndPoint, onlyForOnionHosts: false, networkCredential: null, streamIsolation: true)); // allowOnlyTorEndpoints: true - Connect only to onions and do not connect to clearnet IPs at all. // This of course makes the first setting unnecessary, but it's better if that's around, in case someone wants to tinker here. connectionParameters.EndpointConnector = new DefaultEndpointConnector(allowOnlyTorEndpoints: Network == Network.Main); await AddKnownBitcoinFullNodeAsHiddenServiceAsync(AddressManager).ConfigureAwait(false); } Nodes = new NodesGroup(Network, connectionParameters, requirements: Constants.NodeRequirements); Nodes.MaximumNodeConnection = 12; RegTestMempoolServingNode = null; } Nodes.Connect(); Logger.LogInfo("Start connecting to nodes..."); var regTestMempoolServingNode = RegTestMempoolServingNode; if (regTestMempoolServingNode is { })
private HdAddress GetSecondUnusedAddressToAvoidClashWithMiningAddress(CoreNode node) { return(node.FullNode.WalletManager() .GetUnusedAddresses(new WalletAccountReference(WalletZero, AccountZero), 2) .Skip(1).First()); }
private void a_second_proof_of_work_node_with_api_enabled() { this.secondStratisPowApiNode = this.powNodeBuilder.CreateStratisPowNode(this.powNetwork).WithWallet().Start(); this.secondStratisPowApiNode.Mnemonic = this.secondStratisPowApiNode.Mnemonic; }
public async Task InitializeNoWalletAsync() { InitializationStarted = true; AddressManager = null; var cancel = StoppingCts.Token; try { await SingleInstanceChecker.CheckAsync().ConfigureAwait(false); Cache = new MemoryCache(new MemoryCacheOptions { SizeLimit = 1_000, ExpirationScanFrequency = TimeSpan.FromSeconds(30) }); var bstoreInitTask = BitcoinStore.InitializeAsync(); var addressManagerFolderPath = Path.Combine(DataDir, "AddressManager"); AddressManagerFilePath = Path.Combine(addressManagerFolderPath, $"AddressManager{Network}.dat"); var addrManTask = InitializeAddressManagerBehaviorAsync(); var userAgent = Constants.UserAgents.RandomElement(); var connectionParameters = new NodeConnectionParameters { UserAgent = userAgent }; HostedServices.Register(new UpdateChecker(TimeSpan.FromMinutes(7), Synchronizer), "Software Update Checker"); #region ProcessKillSubscription AppDomain.CurrentDomain.ProcessExit += async(s, e) => await DisposeAsync().ConfigureAwait(false); Console.CancelKeyPress += async(s, e) => { e.Cancel = true; Logger.LogWarning("Process was signaled for killing.", nameof(Global)); await DisposeAsync().ConfigureAwait(false); }; #endregion ProcessKillSubscription cancel.ThrowIfCancellationRequested(); #region TorProcessInitialization if (Config.UseTor) { TorManager = new TorProcessManager(TorSettings, Config.TorSocks5EndPoint); TorManager.Start(ensureRunning: false); var fallbackRequestTestUri = new Uri(Config.GetFallbackBackendUri(), "/api/software/versions"); TorManager.StartMonitor(TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(7), fallbackRequestTestUri); } else { TorSocks5EndPoint = null; } Logger.LogInfo($"{nameof(TorProcessManager)} is initialized."); #endregion TorProcessInitialization cancel.ThrowIfCancellationRequested(); #region BitcoinStoreInitialization await bstoreInitTask.ConfigureAwait(false); // Make sure that the height of the wallets will not be better than the current height of the filters. WalletManager.SetMaxBestHeight(BitcoinStore.IndexStore.SmartHeaderChain.TipHeight); #endregion BitcoinStoreInitialization cancel.ThrowIfCancellationRequested(); #region BitcoinCoreInitialization try { if (Config.StartLocalBitcoinCoreOnStartup) { BitcoinCoreNode = await CoreNode .CreateAsync( new CoreNodeParams( Network, BitcoinStore.MempoolService, HostedServices, Config.LocalBitcoinCoreDataDir, tryRestart : false, tryDeleteDataDir : false, EndPointStrategy.Default(Network, EndPointType.P2p), EndPointStrategy.Default(Network, EndPointType.Rpc), txIndex : null, prune : null, mempoolReplacement : "fee,optin", userAgent : $"/WasabiClient:{Constants.ClientVersion}/", fallbackFee : null, // ToDo: Maybe we should have it, not only for tests? Cache), cancel) .ConfigureAwait(false); } } catch (Exception ex) { Logger.LogError(ex); } await HostedServices.StartAllAsync(cancel).ConfigureAwait(false); var feeProviderList = new List <IFeeProvider> { Synchronizer }; var rpcFeeProvider = HostedServices.FirstOrDefault <RpcFeeProvider>(); if (rpcFeeProvider is { })
public void TxMempoolMapOrphans() { var rand = new Random(); var randByte = new byte[32]; uint256 randHash() { rand.NextBytes(randByte); return(new uint256(randByte)); } using (NodeBuilder builder = NodeBuilder.Create(this)) { CoreNode stratisNode = builder.CreateStratisPowNode(this.network).WithDummyWallet().Start(); // 50 orphan transactions: for (ulong i = 0; i < 50; i++) { Transaction tx = stratisNode.FullNode.Network.CreateTransaction(); tx.AddInput(new TxIn(new OutPoint(randHash(), 0), new Script(OpcodeType.OP_1))); tx.AddOutput(new TxOut(new Money(1 * Money.CENT), stratisNode.MinerSecret.ScriptPubKey)); stratisNode.FullNode.NodeService <MempoolOrphans>().AddOrphanTx(i, tx); } Assert.Equal(50, stratisNode.FullNode.NodeService <MempoolOrphans>().OrphansList().Count); // ... and 50 that depend on other orphans: for (ulong i = 0; i < 50; i++) { MempoolOrphans.OrphanTx txPrev = stratisNode.FullNode.NodeService <MempoolOrphans>().OrphansList().ElementAt(rand.Next(stratisNode.FullNode.NodeService <MempoolOrphans>().OrphansList().Count)); Transaction tx = stratisNode.FullNode.Network.CreateTransaction(); tx.AddInput(new TxIn(new OutPoint(txPrev.Tx.GetHash(), 0), new Script(OpcodeType.OP_1))); tx.AddOutput(new TxOut(new Money((1 + i + 100) * Money.CENT), stratisNode.MinerSecret.ScriptPubKey)); stratisNode.FullNode.NodeService <MempoolOrphans>().AddOrphanTx(i, tx); } Assert.Equal(100, stratisNode.FullNode.NodeService <MempoolOrphans>().OrphansList().Count); // This really-big orphan should be ignored: for (ulong i = 0; i < 10; i++) { MempoolOrphans.OrphanTx txPrev = stratisNode.FullNode.NodeService <MempoolOrphans>().OrphansList().ElementAt(rand.Next(stratisNode.FullNode.NodeService <MempoolOrphans>().OrphansList().Count)); Transaction tx = stratisNode.FullNode.Network.CreateTransaction(); tx.AddOutput(new TxOut(new Money(1 * Money.CENT), stratisNode.MinerSecret.ScriptPubKey)); foreach (int index in Enumerable.Range(0, 2777)) { tx.AddInput(new TxIn(new OutPoint(txPrev.Tx.GetHash(), index), new Script(OpcodeType.OP_1))); } Assert.False(stratisNode.FullNode.NodeService <MempoolOrphans>().AddOrphanTx(i, tx)); } Assert.Equal(100, stratisNode.FullNode.NodeService <MempoolOrphans>().OrphansList().Count); // Test EraseOrphansFor: for (ulong i = 0; i < 3; i++) { int sizeBefore = stratisNode.FullNode.NodeService <MempoolOrphans>().OrphansList().Count; stratisNode.FullNode.NodeService <MempoolOrphans>().EraseOrphansFor(i); Assert.True(stratisNode.FullNode.NodeService <MempoolOrphans>().OrphansList().Count < sizeBefore); } // Test LimitOrphanTxSize() function: stratisNode.FullNode.NodeService <MempoolOrphans>().LimitOrphanTxSize(40); Assert.True(stratisNode.FullNode.NodeService <MempoolOrphans>().OrphansList().Count <= 40); stratisNode.FullNode.NodeService <MempoolOrphans>().LimitOrphanTxSize(10); Assert.True(stratisNode.FullNode.NodeService <MempoolOrphans>().OrphansList().Count <= 10); stratisNode.FullNode.NodeService <MempoolOrphans>().LimitOrphanTxSize(0); Assert.True(!stratisNode.FullNode.NodeService <MempoolOrphans>().OrphansList().Any()); } }
private void a_pow_node_running() { this.node = this.builder.CreateStratisPowNode(this.network).NotInIBD(); this.node.Start(); this.node.WithWallet(); }
public void SBFNMinesTransaction_XSyncs() { // TODO: Currently fails due to issue #2468 (coinbase // reward on stratisX cannot be >4. No fees are allowed) if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { // TODO: Add the necessary executables for Linux & OSX return; } using (NodeBuilder builder = NodeBuilder.Create(this)) { var network = new StratisRegTest(); CoreNode stratisXNode = builder.CreateStratisXNode(version: "2.0.0.5").Start(); // We do not want the datetime provider to be substituted, // so a custom builder callback has to be used. var callback = new Action <IFullNodeBuilder>(build => build .UseBlockStore() .UsePosConsensus() .UseMempool() .UseWallet() .AddPowPosMining() .AddRPC() .UseTestChainedHeaderTree() .MockIBD()); CoreNode stratisNode = builder.CreateCustomNode(callback, network, protocolVersion: ProtocolVersion.ALT_PROTOCOL_VERSION).WithWallet().Start(); RPCClient stratisXRpc = stratisXNode.CreateRPCClient(); RPCClient stratisNodeRpc = stratisNode.CreateRPCClient(); stratisXRpc.AddNode(stratisNode.Endpoint, false); stratisNodeRpc.AddNode(stratisXNode.Endpoint, false); TestHelper.MineBlocks(stratisNode, 11); // It takes a reasonable amount of time for blocks to be generated without // the datetime provider substitution. var longCancellationToken = new CancellationTokenSource(TimeSpan.FromMinutes(15)).Token; var shortCancellationToken = new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token; TestBase.WaitLoop(() => stratisNodeRpc.GetBestBlockHash() == stratisXRpc.GetBestBlockHash(), cancellationToken: longCancellationToken); // Send transaction to arbitrary address from SBFN side. var alice = new Key().GetBitcoinSecret(network); var aliceAddress = alice.GetAddress(); stratisNodeRpc.WalletPassphrase("password", 60); stratisNodeRpc.SendToAddress(aliceAddress, Money.Coins(1.0m)); TestBase.WaitLoop(() => stratisNodeRpc.GetRawMempool().Length == 1, cancellationToken: shortCancellationToken); // Transaction should percolate through to X's mempool. TestBase.WaitLoop(() => stratisXRpc.GetRawMempool().Length == 1, cancellationToken: shortCancellationToken); // Now SBFN must mine the block. TestHelper.MineBlocks(stratisNode, 1); // We expect that X will sync correctly. TestBase.WaitLoop(() => stratisNodeRpc.GetBestBlockHash() == stratisXRpc.GetBestBlockHash(), cancellationToken: shortCancellationToken); // Sanity check - mempools should both become empty. TestBase.WaitLoop(() => stratisNodeRpc.GetRawMempool().Length == 0, cancellationToken: shortCancellationToken); TestBase.WaitLoop(() => stratisXRpc.GetRawMempool().Length == 0, cancellationToken: shortCancellationToken); } }
public void CheckBlocksAnnounced_AndQueueEmptiesOverTime_ForMultiplePeers_WhenOneIsDisconnected() { using (NodeBuilder builder = NodeBuilder.Create(this)) { CoreNode stratisNodeSync = builder.CreateStratisPowNode(this.network, "bss-2-stratisNodeSync").WithReadyBlockchainData(ReadyBlockchain.BitcoinRegTest10Miner).Start(); CoreNode stratisNode1 = builder.CreateStratisPowNode(this.network, "bss-2-stratisNode1").Start(); CoreNode stratisNode2 = builder.CreateStratisPowNode(this.network, "bss-2-stratisNode2").Start(); CoreNode stratisNode3 = builder.CreateStratisPowNode(this.network, "bss-2-stratisNode3").Start(); // Change the other nodes' lists of default behaviours include the test behaviour in it. // We leave the other behaviors alone for this test because we want to see what messages the node gets under normal operation. IConnectionManager node1ConnectionManager = stratisNode1.FullNode.NodeService <IConnectionManager>(); node1ConnectionManager.Parameters.TemplateBehaviors.Add(new TestBehavior()); IConnectionManager node2ConnectionManager = stratisNode2.FullNode.NodeService <IConnectionManager>(); node2ConnectionManager.Parameters.TemplateBehaviors.Add(new TestBehavior()); // Connect other nodes to initial node. TestHelper.Connect(stratisNode1, stratisNodeSync); TestHelper.Connect(stratisNode2, stratisNodeSync); TestHelper.Connect(stratisNode3, stratisNodeSync); // Make node3 unable to respond to anything, effectively disconnecting it. IConnectionManager node3ConnectionManager = stratisNode3.FullNode.NodeService <IConnectionManager>(); node3ConnectionManager.Parameters.TemplateBehaviors.Clear(); node3ConnectionManager.Parameters.TemplateBehaviors.Add(new TestBehavior()); INetworkPeer connectedPeer1 = node1ConnectionManager.ConnectedPeers.FindByEndpoint(stratisNodeSync.Endpoint); TestBehavior testBehavior1 = connectedPeer1.Behavior <TestBehavior>(); INetworkPeer connectedPeer2 = node2ConnectionManager.ConnectedPeers.FindByEndpoint(stratisNodeSync.Endpoint); TestBehavior testBehavior2 = connectedPeer2.Behavior <TestBehavior>(); INetworkPeer connectedPeer3 = node3ConnectionManager.ConnectedPeers.FindByEndpoint(stratisNodeSync.Endpoint); TestBehavior testBehavior3 = connectedPeer3.Behavior <TestBehavior>(); // If the announce queue is not getting stalled, the other 2 nodes should sync properly. TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisNode1, stratisNodeSync)); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisNode2, stratisNodeSync)); HashSet <uint256> advertised = new HashSet <uint256>(); // Check to see that all blocks got advertised to node1 via the "headers" payload. foreach (IncomingMessage message in testBehavior1.receivedMessageTracker["headers"]) { if (message.Message.Payload is HeadersPayload) { foreach (BlockHeader header in ((HeadersPayload)message.Message.Payload).Headers) { advertised.Add(header.GetHash()); } } } foreach (ChainedHeader chainedHeader in stratisNodeSync.FullNode.ChainIndexer.EnumerateToTip(this.network.GenesisHash)) { if ((!advertised.Contains(chainedHeader.HashBlock)) && (!(chainedHeader.HashBlock == this.network.GenesisHash))) { throw new Exception($"An expected block was not advertised to peer 1: {chainedHeader.HashBlock}"); } } advertised.Clear(); // Check to see that all blocks got advertised to node1 via the "headers" payload. foreach (IncomingMessage message in testBehavior2.receivedMessageTracker["headers"]) { if (message.Message.Payload is HeadersPayload) { foreach (BlockHeader header in ((HeadersPayload)message.Message.Payload).Headers) { advertised.Add(header.GetHash()); } } } foreach (ChainedHeader chainedHeader in stratisNodeSync.FullNode.ChainIndexer.EnumerateToTip(this.network.GenesisHash)) { if ((!advertised.Contains(chainedHeader.HashBlock)) && (!(chainedHeader.HashBlock == this.network.GenesisHash))) { throw new Exception($"An expected block was not advertised to peer 2: {chainedHeader.HashBlock}"); } } // Check current state of announce queue. BlockStoreSignaled blockStoreSignaled = stratisNodeSync.FullNode.NodeService <BlockStoreSignaled>(); IAsyncQueue <ChainedHeader> blocksToAnnounce = (IAsyncQueue <ChainedHeader>)blockStoreSignaled.GetMemberValue("blocksToAnnounce"); Queue <ChainedHeader> queueItems = (Queue <ChainedHeader>)blocksToAnnounce.GetMemberValue("items"); // It should still eventually empty despite not being able to communicate with node3. TestHelper.WaitLoop(() => queueItems.Count == 0); } }
public void SBFNCreatesOpReturnTransaction_XSyncs() { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { // TODO: Add the necessary executables for Linux & OSX return; } using (NodeBuilder builder = NodeBuilder.Create(this)) { var network = new StratisRegTest(); CoreNode stratisXNode = builder.CreateStratisXNode(version: "2.0.0.5").Start(); // We do not want the datetime provider to be substituted, // so a custom builder callback has to be used. var callback = new Action <IFullNodeBuilder>(build => build .UseBlockStore() .UsePosConsensus() .UseMempool() .UseWallet() .AddPowPosMining() .AddRPC() .UseTestChainedHeaderTree() .MockIBD()); CoreNode stratisNode = builder.CreateCustomNode(callback, network, protocolVersion: ProtocolVersion.ALT_PROTOCOL_VERSION, minProtocolVersion: ProtocolVersion.ALT_PROTOCOL_VERSION).WithWallet().Start(); RPCClient stratisXRpc = stratisXNode.CreateRPCClient(); RPCClient stratisNodeRpc = stratisNode.CreateRPCClient(); stratisXRpc.AddNode(stratisNode.Endpoint, false); stratisNodeRpc.AddNode(stratisXNode.Endpoint, false); TestHelper.MineBlocks(stratisNode, 11); // It takes a reasonable amount of time for blocks to be generated without // the datetime provider substitution. var longCancellationToken = new CancellationTokenSource(TimeSpan.FromMinutes(15)).Token; var shortCancellationToken = new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token; TestBase.WaitLoop(() => stratisNodeRpc.GetBestBlockHash() == stratisXRpc.GetBestBlockHash(), cancellationToken: longCancellationToken); // Send transaction to arbitrary address from SBFN side. var alice = new Key().GetBitcoinSecret(network); var aliceAddress = alice.GetAddress(); //stratisNodeRpc.WalletPassphrase("password", 60); var transactionBuildContext = new TransactionBuildContext(stratisNode.FullNode.Network) { AccountReference = new WalletAccountReference("mywallet", "account 0"), MinConfirmations = 1, OpReturnData = "test", OpReturnAmount = Money.Coins(0.01m), WalletPassword = "******", Recipients = new List <Recipient>() { new Recipient() { Amount = Money.Coins(1), ScriptPubKey = aliceAddress.ScriptPubKey } } }; var transaction = stratisNode.FullNode.WalletTransactionHandler().BuildTransaction(transactionBuildContext); stratisNode.FullNode.NodeController <WalletController>().SendTransaction(new SendTransactionRequest(transaction.ToHex())); TestBase.WaitLoop(() => stratisNodeRpc.GetRawMempool().Length == 1, cancellationToken: shortCancellationToken); // Transaction should percolate through to X's mempool. TestBase.WaitLoop(() => stratisXRpc.GetRawMempool().Length == 1, cancellationToken: shortCancellationToken); } }
public void MakeNode() { using (NodeBuilder builder = NodeBuilder.Create(version: "0.15.1")) { var core3 = builder.CreateNode(true); var rpc3 = core3.CreateRPCClient(); // Replicate portions of BreezeServer's Program.cs. Maybe refactor it into a class/function in future var serviceProvider = new ServiceCollection() .AddLogging() .AddSingleton <Breeze.BreezeServer.Services.ITumblerService, Breeze.BreezeServer.Services.TumblerService>() .BuildServiceProvider(); serviceProvider .GetService <ILoggerFactory>() .AddConsole(LogLevel.Debug); // Skip the registration code - that can be tested separately string configPath = Path.Combine(core3.DataFolder, "breeze.conf"); string[] breezeServerConfig = { "network=regtest", // Only the network setting is currently used from this file "rpc.user=dummy", "rpc.password=dummy", "rpc.url=http://127.0.0.1:26174/", "breeze.ipv4=127.0.0.1", "breeze.ipv6=2001:0db8:85a3:0000:0000:8a2e:0370:7334", "breeze.onion=0123456789ABCDEF", "breeze.port=37123", "breeze.regtxfeevalue=10000", "breeze.regtxoutputvalue=1000", "tumbler.url=http://127.0.0.1:37123/api/v1/", "tumbler.rsakeyfile=/Users/username/.ntumblebitserver/RegTest/Tumbler.pem", "tumbler.ecdsakeyaddress=TVwRFmEKRCnQAgShf3QshBjp1Tmucm1e87" }; File.WriteAllLines(configPath, breezeServerConfig); BreezeConfiguration config = new BreezeConfiguration(configPath); string ntbServerConfigPath = Path.Combine(core3.DataFolder, "server.config"); string[] ntbServerConfig = { "regtest=1", "rpc.url=http://127.0.0.1:" + rpc3.Address.Port + "/", "rpc.user="******"rpc.password="******"tor.enabled=true", "tor.server=127.0.0.1:9051" // We assume for now that tor has been manually started }; File.WriteAllLines(ntbServerConfigPath, ntbServerConfig); // TODO: Maybe move this to after the initial block generation so they don't have to be processed // TODO: Also need to see if NTB interactive console interferes with later parts of the test var tumbler = serviceProvider.GetService <Breeze.BreezeServer.Services.ITumblerService>(); tumbler.StartTumbler(config, false, "server.config", Path.GetFullPath(core3.DataFolder)); //var node1 = builder.CreateStratisPowNode(); CoreNode node1 = builder.CreateStratisPowNode(true, fullNodeBuilder => { fullNodeBuilder .UseConsensus() .UseBlockStore() .UseMempool() .AddMining() .UseWallet() .UseApi() .AddRPC(); //.UseTumbleBit(); }); node1.NotInIBD(); // Create the source and destination wallets var wm1 = node1.FullNode.NodeService <IWalletManager>() as WalletManager; //var wm2 = node2.FullNode.NodeService<IWalletManager>() as WalletManager; wm1.CreateWallet("TumbleBit1", "alice"); wm1.CreateWallet("TumbleBit1", "bob"); // Mined coins only mature after 100 blocks on regtest core3.FindBlock(101); var rpc1 = node1.CreateRPCClient(); //var rpc2 = node2.CreateRPCClient(); rpc1.AddNode(core3.Endpoint, false); TestHelper.WaitLoop(() => rpc1.GetBestBlockHash() == rpc3.GetBestBlockHash()); var amount = new Money(5.0m, MoneyUnit.BTC); var destination = wm1.GetUnusedAddress(new WalletAccountReference("alice", "account 0")); rpc3.SendToAddress(BitcoinAddress.Create(destination.Address, Network.RegTest), amount); core3.FindBlock(1); var unspent = rpc1.ListUnspent(); // TODO: Move forward specific numbers of blocks and check interim states? TB tests should already do that for (int i = 0; i < 100; i++) { core3.FindBlock(1); Thread.Sleep(30); // <- is TumblerService in its own thread? If not, move it into one and we wait for it } // Check destination wallet for tumbled coins // TODO: Need to amend TumblerService so that it can be shut down within the test core3.Kill(false); node1.Kill(false); } }
public void XMinesTransaction_SBFNSyncs() { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { // TODO: Add the necessary executables for Linux & OSX return; } using (NodeBuilder builder = NodeBuilder.Create(this)) { var network = new StratisRegTest(); CoreNode stratisXNode = builder.CreateStratisXNode(version: "2.0.0.5").Start(); var callback = new Action <IFullNodeBuilder>(build => build .UseBlockStore() .UsePosConsensus() .UseMempool() .UseWallet() .AddPowPosMining() .AddRPC()); var config = new NodeConfigParameters(); config.Add("whitelist", stratisXNode.Endpoint.ToString()); config.Add("gateway", "1"); CoreNode stratisNode = builder .CreateCustomNode(callback, network, protocolVersion: ProtocolVersion.PROVEN_HEADER_VERSION, minProtocolVersion: ProtocolVersion.ALT_PROTOCOL_VERSION, configParameters: config) .WithWallet().Start(); RPCClient stratisXRpc = stratisXNode.CreateRPCClient(); RPCClient stratisNodeRpc = stratisNode.CreateRPCClient(); stratisXRpc.AddNode(stratisNode.Endpoint, false); stratisNodeRpc.AddNode(stratisXNode.Endpoint, false); stratisXRpc.SendCommand(RPCOperations.generate, 11); var shortCancellationToken = new CancellationTokenSource(TimeSpan.FromMinutes(2)).Token; // Without this there seems to be a race condition between the blocks all getting generated and SBFN syncing high enough to fall through the getbestblockhash check. TestBase.WaitLoop(() => stratisXRpc.GetBlockCount() >= 11, cancellationToken: shortCancellationToken); TestBase.WaitLoop(() => stratisNodeRpc.GetBestBlockHash() == stratisXRpc.GetBestBlockHash(), cancellationToken: shortCancellationToken); // Send transaction to arbitrary address from X side. var alice = new Key().GetBitcoinSecret(network); var aliceAddress = alice.GetAddress(); stratisXRpc.SendCommand(RPCOperations.sendtoaddress, aliceAddress.ToString(), 1); TestBase.WaitLoop(() => stratisXRpc.GetRawMempool().Length == 1, cancellationToken: shortCancellationToken); // Transaction should percolate through to SBFN's mempool. TestBase.WaitLoop(() => stratisNodeRpc.GetRawMempool().Length == 1, cancellationToken: shortCancellationToken); // Now X must mine the block. stratisXRpc.SendCommand(RPCOperations.generate, 1); TestBase.WaitLoop(() => stratisXRpc.GetBlockCount() >= 12, cancellationToken: shortCancellationToken); // We expect that SBFN will sync correctly. TestBase.WaitLoop(() => stratisNodeRpc.GetBestBlockHash() == stratisXRpc.GetBestBlockHash(), cancellationToken: shortCancellationToken); // Sanity check - mempools should both become empty. TestBase.WaitLoop(() => stratisNodeRpc.GetRawMempool().Length == 0, cancellationToken: shortCancellationToken); TestBase.WaitLoop(() => stratisXRpc.GetRawMempool().Length == 0, cancellationToken: shortCancellationToken); } }
public async Task SendingFromOneAddressToFiftyAddresses() { int sendingAccountBalanceOnStart = 98000596; int receivingAccountBalanceOnStart = 0; using (NodeBuilder builder = NodeBuilder.Create(this)) { // Arrange. // Create a sending and a receiving node. CoreNode sendingNode = builder.CreateStratisPosNode(this.network).WithReadyBlockchainData(ReadyBlockchain.StratisRegTest150Miner).Start(); CoreNode receivingNode = builder.CreateStratisPosNode(this.network).WithReadyBlockchainData(ReadyBlockchain.StratisRegTest150Listener).Start(); TestHelper.ConnectAndSync(sendingNode, receivingNode); // Check balances. WalletBalanceModel sendingNodeBalances = await $"http://localhost:{sendingNode.ApiPort}/api" .AppendPathSegment("wallet/balance") .SetQueryParams(new { walletName = "mywallet" }) .GetJsonAsync <WalletBalanceModel>(); AccountBalanceModel sendingAccountBalance = sendingNodeBalances.AccountsBalances.Single(); (sendingAccountBalance.AmountConfirmed + sendingAccountBalance.AmountUnconfirmed).Should().Be(new Money(sendingAccountBalanceOnStart, MoneyUnit.BTC)); WalletBalanceModel receivingNodeBalances = await $"http://localhost:{receivingNode.ApiPort}/api" .AppendPathSegment("wallet/balance") .SetQueryParams(new { walletName = "mywallet" }) .GetJsonAsync <WalletBalanceModel>(); AccountBalanceModel receivingAccountBalance = receivingNodeBalances.AccountsBalances.Single(); (receivingAccountBalance.AmountConfirmed + receivingAccountBalance.AmountUnconfirmed).Should().Be(new Money(receivingAccountBalanceOnStart)); // Act. // Get 50 addresses to send to. IEnumerable <string> unusedaddresses = await $"http://localhost:{receivingNode.ApiPort}/api" .AppendPathSegment("wallet/unusedAddresses") .SetQueryParams(new { walletName = "mywallet", accountName = "account 0", count = 50 }) .GetJsonAsync <IEnumerable <string> >(); // Build and send the transaction with 50 recipients. WalletBuildTransactionModel buildTransactionModel = await $"http://localhost:{sendingNode.ApiPort}/api" .AppendPathSegment("wallet/build-transaction") .PostJsonAsync(new BuildTransactionRequest { WalletName = "mywallet", AccountName = "account 0", FeeType = "low", Password = "******", ShuffleOutputs = true, AllowUnconfirmed = true, Recipients = unusedaddresses.Select(address => new RecipientModel { DestinationAddress = address, Amount = "1" }).ToList() }) .ReceiveJson <WalletBuildTransactionModel>(); await $"http://localhost:{sendingNode.ApiPort}/api" .AppendPathSegment("wallet/send-transaction") .PostJsonAsync(new SendTransactionRequest { Hex = buildTransactionModel.Hex }) .ReceiveJson <WalletSendTransactionModel>(); // Assert. // The sending node should have 50 (+ fee) fewer coins. sendingNodeBalances = await $"http://localhost:{sendingNode.ApiPort}/api" .AppendPathSegment("wallet/balance") .SetQueryParams(new { walletName = "mywallet" }) .GetJsonAsync <WalletBalanceModel>(); sendingAccountBalance = sendingNodeBalances.AccountsBalances.Single(); (sendingAccountBalance.AmountConfirmed + sendingAccountBalance.AmountUnconfirmed).Should().Be(new Money(sendingAccountBalanceOnStart - 50 - buildTransactionModel.Fee.ToDecimal(MoneyUnit.BTC), MoneyUnit.BTC)); // Mine and sync so that we make sure the receiving node is up to date. TestHelper.MineBlocks(sendingNode, 1); // The receiving node should have 50 more coins. receivingNodeBalances = await $"http://localhost:{receivingNode.ApiPort}/api" .AppendPathSegment("wallet/balance") .SetQueryParams(new { walletName = "mywallet" }) .GetJsonAsync <WalletBalanceModel>(); receivingAccountBalance = receivingNodeBalances.AccountsBalances.Single(); (receivingAccountBalance.AmountConfirmed + receivingAccountBalance.AmountUnconfirmed).Should().Be(new Money(receivingAccountBalanceOnStart + 50, MoneyUnit.BTC)); } }
public void Transaction_TraversesNodes_AndIsMined_AndNodesSync() { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { // TODO: Add the necessary executables for Linux & OSX return; } using (NodeBuilder builder = NodeBuilder.Create(this)) { var network = new StratisRegTest(); CoreNode xNode1 = builder.CreateStratisXNode(version: "2.0.0.5").Start(); var callback = new Action <IFullNodeBuilder>(build => build .UseBlockStore() .UsePosConsensus() .UseMempool() .UseWallet() .AddPowPosMining() .AddRPC()); var config = new NodeConfigParameters(); config.Add("whitelist", xNode1.Endpoint.ToString()); config.Add("gateway", "1"); CoreNode sbfnNode2 = builder .CreateCustomNode(callback, network, protocolVersion: ProtocolVersion.PROVEN_HEADER_VERSION, minProtocolVersion: ProtocolVersion.ALT_PROTOCOL_VERSION, configParameters: config) .WithWallet().Start(); CoreNode xNode3 = builder.CreateStratisXNode(version: "2.0.0.5").Start(); RPCClient xRpc1 = xNode1.CreateRPCClient(); RPCClient sbfnRpc2 = sbfnNode2.CreateRPCClient(); RPCClient xRpc3 = xNode3.CreateRPCClient(); sbfnRpc2.AddNode(xNode1.Endpoint, false); sbfnRpc2.AddNode(xNode3.Endpoint, false); xRpc1.SendCommand(RPCOperations.generate, 11); var shortCancellationToken = new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token; TestBase.WaitLoop(() => xRpc1.GetBlockCount() >= 11, cancellationToken: shortCancellationToken); TestBase.WaitLoop(() => xRpc1.GetBestBlockHash() == sbfnRpc2.GetBestBlockHash(), cancellationToken: shortCancellationToken); TestBase.WaitLoop(() => xRpc1.GetBestBlockHash() == xRpc3.GetBestBlockHash(), cancellationToken: shortCancellationToken); // Send transaction to arbitrary address. var alice = new Key().GetBitcoinSecret(network); var aliceAddress = alice.GetAddress(); xRpc1.SendCommand(RPCOperations.sendtoaddress, aliceAddress.ToString(), 1); TestBase.WaitLoop(() => xRpc1.GetRawMempool().Length == 1, cancellationToken: shortCancellationToken); TestBase.WaitLoop(() => sbfnRpc2.GetRawMempool().Length == 1, cancellationToken: shortCancellationToken); TestBase.WaitLoop(() => xRpc3.GetRawMempool().Length == 1, cancellationToken: shortCancellationToken); // TODO: Until #2468 is fixed we need an X node to mine the block so it doesn't get rejected. xRpc1.SendCommand(RPCOperations.generate, 1); TestBase.WaitLoop(() => xRpc1.GetBlockCount() >= 12, cancellationToken: shortCancellationToken); // We expect that SBFN and the other X node will sync correctly. TestBase.WaitLoop(() => sbfnRpc2.GetBestBlockHash() == xRpc1.GetBestBlockHash(), cancellationToken: shortCancellationToken); TestBase.WaitLoop(() => xRpc3.GetBestBlockHash() == xRpc1.GetBestBlockHash(), cancellationToken: shortCancellationToken); // Sanity check - mempools should all become empty. TestBase.WaitLoop(() => xRpc1.GetRawMempool().Length == 0, cancellationToken: shortCancellationToken); TestBase.WaitLoop(() => sbfnRpc2.GetRawMempool().Length == 0, cancellationToken: shortCancellationToken); TestBase.WaitLoop(() => xRpc3.GetRawMempool().Length == 0, cancellationToken: shortCancellationToken); } }
public async Task SendingATransactionWithAnOpReturn() { int sendingAccountBalanceOnStart = 98000596; int receivingAccountBalanceOnStart = 0; using (NodeBuilder builder = NodeBuilder.Create(this)) { // Arrange. // Create a sending and a receiving node. CoreNode sendingNode = builder.CreateStratisPosNode(this.network).WithReadyBlockchainData(ReadyBlockchain.StratisRegTest150Miner).Start(); CoreNode receivingNode = builder.CreateStratisPosNode(this.network).WithReadyBlockchainData(ReadyBlockchain.StratisRegTest150Listener).Start(); TestHelper.ConnectAndSync(sendingNode, receivingNode); // Check balances. WalletBalanceModel sendingNodeBalances = await $"http://localhost:{sendingNode.ApiPort}/api" .AppendPathSegment("wallet/balance") .SetQueryParams(new { walletName = "mywallet" }) .GetJsonAsync <WalletBalanceModel>(); AccountBalanceModel sendingAccountBalance = sendingNodeBalances.AccountsBalances.Single(); (sendingAccountBalance.AmountConfirmed + sendingAccountBalance.AmountUnconfirmed).Should().Be(new Money(sendingAccountBalanceOnStart, MoneyUnit.BTC)); WalletBalanceModel receivingNodeBalances = await $"http://localhost:{receivingNode.ApiPort}/api" .AppendPathSegment("wallet/balance") .SetQueryParams(new { walletName = "mywallet" }) .GetJsonAsync <WalletBalanceModel>(); AccountBalanceModel receivingAccountBalance = receivingNodeBalances.AccountsBalances.Single(); (receivingAccountBalance.AmountConfirmed + receivingAccountBalance.AmountUnconfirmed).Should().Be(new Money(receivingAccountBalanceOnStart)); // Act. // Get an address to send to. IEnumerable <string> unusedaddresses = await $"http://localhost:{receivingNode.ApiPort}/api" .AppendPathSegment("wallet/unusedAddresses") .SetQueryParams(new { walletName = "mywallet", accountName = "account 0", count = 1 }) .GetJsonAsync <IEnumerable <string> >(); // Build and send the transaction with an Op_Return. WalletBuildTransactionModel buildTransactionModel = await $"http://localhost:{sendingNode.ApiPort}/api" .AppendPathSegment("wallet/build-transaction") .PostJsonAsync(new BuildTransactionRequest { WalletName = "mywallet", AccountName = "account 0", FeeType = "low", Password = "******", ShuffleOutputs = true, AllowUnconfirmed = true, Recipients = unusedaddresses.Select(address => new RecipientModel { DestinationAddress = address, Amount = "1" }).ToList(), OpReturnData = "some data to send", OpReturnAmount = "1" }) .ReceiveJson <WalletBuildTransactionModel>(); await $"http://localhost:{sendingNode.ApiPort}/api" .AppendPathSegment("wallet/send-transaction") .PostJsonAsync(new SendTransactionRequest { Hex = buildTransactionModel.Hex }) .ReceiveJson <WalletSendTransactionModel>(); // Assert. // Mine and sync so that we make sure the receiving node is up to date. TestHelper.MineBlocks(sendingNode, 1); TestHelper.WaitForNodeToSync(sendingNode, receivingNode); // The receiving node should have coins. receivingNodeBalances = await $"http://localhost:{receivingNode.ApiPort}/api" .AppendPathSegment("wallet/balance") .SetQueryParams(new { walletName = "mywallet" }) .GetJsonAsync <WalletBalanceModel>(); receivingAccountBalance = receivingNodeBalances.AccountsBalances.Single(); (receivingAccountBalance.AmountConfirmed).Should().Be(new Money(receivingAccountBalanceOnStart + 1, MoneyUnit.BTC)); // The sending node should have fewer coins. sendingNodeBalances = await $"http://localhost:{sendingNode.ApiPort}/api" .AppendPathSegment("wallet/balance") .SetQueryParams(new { walletName = "mywallet" }) .GetJsonAsync <WalletBalanceModel>(); sendingAccountBalance = sendingNodeBalances.AccountsBalances.Single(); (sendingAccountBalance.AmountConfirmed).Should().Be(new Money(sendingAccountBalanceOnStart + 4 - 2, MoneyUnit.BTC)); // Check the transaction. string lastBlockHash = await $"http://localhost:{receivingNode.ApiPort}/api" .AppendPathSegment("consensus/getbestblockhash") .GetJsonAsync <string>(); BlockTransactionDetailsModel block = await $"http://localhost:{receivingNode.ApiPort}/api" .AppendPathSegment("blockstore/block") .SetQueryParams(new { hash = lastBlockHash, showTransactionDetails = true, outputJson = true }) .GetJsonAsync <BlockTransactionDetailsModel>(); TransactionVerboseModel trx = block.Transactions.SingleOrDefault(t => t.TxId == buildTransactionModel.TransactionId.ToString()); trx.Should().NotBeNull(); Vout opReturnOutputFromBlock = trx.VOut.Single(t => t.ScriptPubKey.Type == "nulldata"); opReturnOutputFromBlock.Value.Should().Be(1); var script = opReturnOutputFromBlock.ScriptPubKey.Asm; string[] ops = script.Split(" "); ops[0].Should().Be("OP_RETURN"); Encoders.Hex.DecodeData(ops[1]).Should().BeEquivalentTo(System.Text.Encoding.UTF8.GetBytes("some data to send")); } }
public async Task TransactionSentFeesReceivedByMinerAsync() { TestPoANetwork network = new TestPoANetwork(); using (PoANodeBuilder builder = PoANodeBuilder.CreatePoANodeBuilder(this)) { string walletName = "mywallet"; string walletPassword = "******"; string walletAccount = "account 0"; Money transferAmount = Money.Coins(1m); Money feeAmount = Money.Coins(0.0001m); CoreNode nodeA = builder.CreatePoANode(network, network.FederationKey1).WithWallet(walletPassword, walletName).Start(); CoreNode nodeB = builder.CreatePoANode(network, network.FederationKey2).WithWallet(walletPassword, walletName).Start(); TestHelper.Connect(nodeA, nodeB); long toMineCount = network.Consensus.PremineHeight + network.Consensus.CoinbaseMaturity + 1 - nodeA.GetTip().Height; // Get coins on nodeA via the premine. await nodeA.MineBlocksAsync((int)toMineCount).ConfigureAwait(false); CoreNodePoAExtensions.WaitTillSynced(nodeA, nodeB); // Will send funds to one of nodeB's addresses. Script destination = nodeB.FullNode.WalletManager().GetUnusedAddress().ScriptPubKey; var context = new TransactionBuildContext(network) { AccountReference = new WalletAccountReference(walletName, walletAccount), MinConfirmations = 0, FeeType = FeeType.High, WalletPassword = walletPassword, Recipients = new[] { new Recipient { Amount = transferAmount, ScriptPubKey = destination } }.ToList() }; Transaction trx = nodeA.FullNode.WalletTransactionHandler().BuildTransaction(context); Assert.True(context.TransactionBuilder.Verify(trx, out _)); await nodeA.FullNode.NodeController <WalletController>().SendTransaction(new SendTransactionRequest(trx.ToHex())); TestBase.WaitLoop(() => nodeA.CreateRPCClient().GetRawMempool().Length == 1 && nodeB.CreateRPCClient().GetRawMempool().Length == 1); await nodeB.MineBlocksAsync((int)toMineCount).ConfigureAwait(false); TestBase.WaitLoop(() => nodeA.CreateRPCClient().GetRawMempool().Length == 0 && nodeB.CreateRPCClient().GetRawMempool().Length == 0); IWalletManager walletManager = nodeB.FullNode.NodeService <IWalletManager>(); TestBase.WaitLoop(() => { long balance = walletManager.GetBalances(walletName, walletAccount).Sum(x => x.AmountConfirmed); return(balance == (transferAmount + feeAmount)); }); } }
/// <summary> /// Determines whether or not the node has any connections. /// </summary> /// <param name="node">The node to check.</param> /// <returns>Returns <c>true</c> if the node does not have any connected peers.</returns> public static bool IsNodeConnected(CoreNode node) { return(node.FullNode.ConnectionManager.ConnectedPeers.Any()); }
public void WalletCanReorg() { // This test has 4 parts: // Send first transaction from one wallet to another and wait for it to be confirmed // Send a second transaction and wait for it to be confirmed // Connect to a longer chain that causes a reorg so that the second trasnaction is undone // Mine the second transaction back in to the main chain using (NodeBuilder builder = NodeBuilder.Create(this)) { CoreNode stratisSender = builder.CreateStratisPowNode(this.network).WithWallet().Start(); CoreNode stratisReceiver = builder.CreateStratisPowNode(this.network).WithWallet().Start(); CoreNode stratisReorg = builder.CreateStratisPowNode(this.network).WithWallet().Start(); int maturity = (int)stratisSender.FullNode.Network.Consensus.CoinbaseMaturity; TestHelper.MineBlocks(stratisSender, maturity + 1 + 15); int currentBestHeight = maturity + 1 + 15; // The mining should add coins to the wallet. long total = stratisSender.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName).Sum(s => s.Transaction.Amount); Assert.Equal(Money.COIN * 16 * 50, total); // Sync all nodes. TestHelper.ConnectAndSync(stratisReceiver, stratisSender); TestHelper.ConnectAndSync(stratisReceiver, stratisReorg); TestHelper.ConnectAndSync(stratisSender, stratisReorg); // Build Transaction 1. // Send coins to the receiver. HdAddress sendto = stratisReceiver.FullNode.WalletManager().GetUnusedAddress(new WalletAccountReference(WalletName, Account)); Transaction transaction1 = stratisSender.FullNode.WalletTransactionHandler().BuildTransaction(CreateContext(stratisSender.FullNode.Network, new WalletAccountReference(WalletName, Account), Password, sendto.ScriptPubKey, Money.COIN * 100, FeeType.Medium, 101)); // Broadcast to the other node. stratisSender.FullNode.NodeController <WalletController>().SendTransactionAsync(new SendTransactionRequest(transaction1.ToHex())); // Wait for the transaction to arrive. TestBase.WaitLoop(() => stratisReceiver.CreateRPCClient().GetRawMempool().Length > 0); Assert.NotNull(stratisReceiver.CreateRPCClient().GetRawTransaction(transaction1.GetHash(), null, false)); TestBase.WaitLoop(() => stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName).Any()); long receivetotal = stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName).Sum(s => s.Transaction.Amount); Assert.Equal(Money.COIN * 100, receivetotal); Assert.Null(stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName).First().Transaction.BlockHeight); // Generate two new blocks so the transaction is confirmed. TestHelper.MineBlocks(stratisSender, 1); int transaction1MinedHeight = currentBestHeight + 1; TestHelper.MineBlocks(stratisSender, 1); currentBestHeight = currentBestHeight + 2; // Wait for block repo for block sync to work. TestBase.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisSender)); TestBase.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisReorg)); Assert.Equal(currentBestHeight, stratisReceiver.FullNode.ChainIndexer.Tip.Height); TestBase.WaitLoop(() => transaction1MinedHeight == stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName).First().Transaction.BlockHeight); // Build Transaction 2. // Remove the reorg node. TestHelper.Disconnect(stratisReceiver, stratisReorg); TestHelper.Disconnect(stratisSender, stratisReorg); ChainedHeader forkblock = stratisReceiver.FullNode.ChainIndexer.Tip; // Send more coins to the wallet sendto = stratisReceiver.FullNode.WalletManager().GetUnusedAddress(new WalletAccountReference(WalletName, Account)); Transaction transaction2 = stratisSender.FullNode.WalletTransactionHandler().BuildTransaction(CreateContext(stratisSender.FullNode.Network, new WalletAccountReference(WalletName, Account), Password, sendto.ScriptPubKey, Money.COIN * 10, FeeType.Medium, 101)); stratisSender.FullNode.NodeController <WalletController>().SendTransactionAsync(new SendTransactionRequest(transaction2.ToHex())); // Wait for the transaction to arrive TestBase.WaitLoop(() => stratisReceiver.CreateRPCClient().GetRawMempool().Length > 0); Assert.NotNull(stratisReceiver.CreateRPCClient().GetRawTransaction(transaction2.GetHash(), null, false)); TestBase.WaitLoop(() => stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName).Any()); long newamount = stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName).Sum(s => s.Transaction.Amount); Assert.Equal(Money.COIN * 110, newamount); Assert.Contains(stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName), b => b.Transaction.BlockHeight == null); // Mine more blocks so it gets included in the chain. TestHelper.MineBlocks(stratisSender, 1); int transaction2MinedHeight = currentBestHeight + 1; TestHelper.MineBlocks(stratisSender, 1); currentBestHeight = currentBestHeight + 2; TestBase.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisSender)); Assert.Equal(currentBestHeight, stratisReceiver.FullNode.ChainIndexer.Tip.Height); TestBase.WaitLoop(() => stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName).Any(b => b.Transaction.BlockHeight == transaction2MinedHeight)); // Create a reorg by mining on two different chains. // Advance both chains, one chain is longer. TestHelper.MineBlocks(stratisSender, 2); TestHelper.MineBlocks(stratisReorg, 10); currentBestHeight = forkblock.Height + 10; // Connect the reorg chain. TestHelper.Connect(stratisReceiver, stratisReorg); TestHelper.Connect(stratisSender, stratisReorg); // Wait for the chains to catch up. TestBase.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisSender)); TestBase.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisReorg, true)); Assert.Equal(currentBestHeight, stratisReceiver.FullNode.ChainIndexer.Tip.Height); // Ensure wallet reorg completes. TestBase.WaitLoop(() => stratisReceiver.FullNode.WalletManager().WalletTipHash == stratisReorg.CreateRPCClient().GetBestBlockHash()); // Check the wallet amount was rolled back. long newtotal = stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName).Sum(s => s.Transaction.Amount); Assert.Equal(receivetotal, newtotal); TestBase.WaitLoop(() => maturity + 1 + 16 == stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName).First().Transaction.BlockHeight); // ReBuild Transaction 2. // After the reorg transaction2 was returned back to mempool. stratisSender.FullNode.NodeController <WalletController>().SendTransactionAsync(new SendTransactionRequest(transaction2.ToHex())); TestBase.WaitLoop(() => stratisReceiver.CreateRPCClient().GetRawMempool().Length > 0); // Mine the transaction again. TestHelper.MineBlocks(stratisSender, 1); transaction2MinedHeight = currentBestHeight + 1; TestHelper.MineBlocks(stratisSender, 1); currentBestHeight = currentBestHeight + 2; TestBase.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisSender)); TestBase.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisReorg)); Assert.Equal(currentBestHeight, stratisReceiver.FullNode.ChainIndexer.Tip.Height); long newsecondamount = stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName).Sum(s => s.Transaction.Amount); Assert.Equal(newamount, newsecondamount); TestBase.WaitLoop(() => stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName).Any(b => b.Transaction.BlockHeight == transaction2MinedHeight)); } }
/// <summary> /// Connects a node to a set of other nodes and waits for all the nodes to sync. /// </summary> /// <param name="thisNode">The node the connection will be established from.</param> /// <param name="to">The nodes to connect to.</param> public static void ConnectAndSync(CoreNode thisNode, params CoreNode[] to) { ConnectAndSync(thisNode, false, to); }
public void WalletBalanceCorrectWhenOnlySomeUnconfirmedAreIncludedInABlock() { using (NodeBuilder builder = NodeBuilder.Create(this)) { CoreNode stratisSender = builder.CreateStratisPowNode(this.network).WithWallet().Start(); CoreNode stratisReceiver = builder.CreateStratisPowNode(this.network).WithWallet().Start(); int maturity = (int)stratisSender.FullNode.Network.Consensus.CoinbaseMaturity; TestHelper.MineBlocks(stratisSender, maturity + 1 + 5); // The mining should add coins to the wallet long total = stratisSender.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName).Sum(s => s.Transaction.Amount); Assert.Equal(Money.COIN * 6 * 50, total); // Sync both nodes TestHelper.ConnectAndSync(stratisSender, stratisReceiver); // Send coins to the receiver HdAddress sendto = stratisReceiver.FullNode.WalletManager().GetUnusedAddress(new WalletAccountReference(WalletName, Account)); Transaction trx = stratisSender.FullNode.WalletTransactionHandler().BuildTransaction(CreateContext(stratisSender.FullNode.Network, new WalletAccountReference(WalletName, Account), Password, sendto.ScriptPubKey, Money.COIN * 100, FeeType.Medium, 101)); // Broadcast to the other node stratisSender.FullNode.NodeController <WalletController>().SendTransactionAsync(new SendTransactionRequest(trx.ToHex())); // Wait for the transaction to arrive TestBase.WaitLoop(() => stratisReceiver.CreateRPCClient().GetRawMempool().Length > 0); TestBase.WaitLoop(() => stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName).Any()); long receivetotal = stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName).Sum(s => s.Transaction.Amount); Assert.Equal(Money.COIN * 100, receivetotal); // Generate two new blocks so the transaction is confirmed TestHelper.MineBlocks(stratisSender, 2); TestBase.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisSender)); // Send 1 transaction from the second node and let it get to the first. sendto = stratisSender.FullNode.WalletManager().GetUnusedAddress(new WalletAccountReference(WalletName, Account)); Transaction testTx1 = stratisReceiver.FullNode.WalletTransactionHandler().BuildTransaction(CreateContext(stratisSender.FullNode.Network, new WalletAccountReference(WalletName, Account), Password, sendto.ScriptPubKey, Money.COIN * 10, FeeType.Medium, 0)); stratisReceiver.FullNode.NodeController <WalletController>().SendTransactionAsync(new SendTransactionRequest(testTx1.ToHex())); TestBase.WaitLoop(() => stratisSender.CreateRPCClient().GetRawMempool().Length > 0); // Disconnect so the first node doesn't get any more transactions. TestHelper.Disconnect(stratisReceiver, stratisSender); // Send a second unconfirmed transaction on the second node which consumes the first. Transaction testTx2 = stratisReceiver.FullNode.WalletTransactionHandler().BuildTransaction(CreateContext(stratisSender.FullNode.Network, new WalletAccountReference(WalletName, Account), Password, sendto.ScriptPubKey, Money.COIN * 10, FeeType.Medium, 0)); stratisReceiver.FullNode.NodeService <IBroadcasterManager>().BroadcastTransactionAsync(testTx2); // Now we can mine a block on the first node with only 1 of the transactions in it. TestHelper.MineBlocks(stratisSender, 1); // Connect the nodes again. TestHelper.Connect(stratisSender, stratisReceiver); // Second node receives a block with only one transaction in it. TestBase.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisSender, true)); // Now lets see what is in the second node's wallet! IEnumerable <UnspentOutputReference> spendableTxs = stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName); // There should be one spendable transaction. And it should be testTx2. Assert.Single(spendableTxs); Assert.Equal(testTx2.GetHash(), spendableTxs.First().Transaction.Id); // It follows that if the above assert was violated we would have conflicts when we build a transaction. // Specifically what we don't want is to have testTx1 in our spendable transactions, which was causing the known issue. } }
public static (bool Passed, string Message) AreNodesSyncedMessage(CoreNode node1, CoreNode node2, bool ignoreMempool = false) { if (node1.runner is BitcoinCoreRunner || node2.runner is BitcoinCoreRunner) { return(node1.CreateRPCClient().GetBestBlockHash() == node2.CreateRPCClient().GetBestBlockHash(), "[BEST_BLOCK_HASH_DOES_MATCH]"); } // If the nodes are at genesis they are considered synced. if (node1.FullNode.ChainIndexer.Tip.Height == 0 && node2.FullNode.ChainIndexer.Tip.Height == 0) { return(true, "[TIPS_ARE_AT_GENESIS]"); } if (node1.FullNode.ChainIndexer.Tip.HashBlock != node2.FullNode.ChainIndexer.Tip.HashBlock) { return(false, $"[CHAIN_TIP_HASH_DOES_NOT_MATCH_{node1.FullNode.ChainIndexer.Tip}_{node2.FullNode.ChainIndexer.Tip}]"); } if (node1.FullNode.ChainBehaviorState.ConsensusTip.HashBlock != node2.FullNode.ChainBehaviorState.ConsensusTip.HashBlock) { return(false, $"[CONSENSUS_TIP_HASH_DOES_MATCH]_{node1.FullNode.ChainBehaviorState.ConsensusTip}_{node2.FullNode.ChainBehaviorState.ConsensusTip}]"); } // Check that node1 tip exists in node2 store (either in disk or in the pending list) if (node1.FullNode.BlockStore().GetBlock(node2.FullNode.ChainBehaviorState.ConsensusTip.HashBlock) == null) { return(false, "[NODE2_TIP_NOT_IN_NODE1_STORE]"); } // Check that node2 tip exists in node1 store (either in disk or in the pending list) if (node2.FullNode.BlockStore().GetBlock(node1.FullNode.ChainBehaviorState.ConsensusTip.HashBlock) == null) { return(false, "[NODE1_TIP_NOT_IN_NODE2_STORE]"); } if (!ignoreMempool) { if (node1.FullNode.MempoolManager().InfoAll().Count != node2.FullNode.MempoolManager().InfoAll().Count) { return(false, "[NODE1_MEMPOOL_COUNT_NOT_EQUAL_NODE2_MEMPOOL_COUNT]"); } } if ((node1.FullNode.WalletManager().ContainsWallets) && (node2.FullNode.WalletManager().ContainsWallets)) { if (node1.FullNode.WalletManager().WalletTipHash != node2.FullNode.WalletManager().WalletTipHash) { return(false, "[WALLET_TIP_HASH_DOESNOT_MATCH]"); } } if (node1.CreateRPCClient().GetBestBlockHash() != node2.CreateRPCClient().GetBestBlockHash()) { return(false, "[RPC_CLIENT_BEST_BLOCK_HASH_DOES_NOT_MATCH]"); } return(true, string.Empty); }
public void MempoolSyncTransactions() { using (NodeBuilder builder = NodeBuilder.Create(this)) { CoreNode stratisNodeSync = builder.CreateStratisPowNode(); CoreNode stratisNode1 = builder.CreateStratisPowNode(); CoreNode stratisNode2 = builder.CreateStratisPowNode(); builder.StartAll(); stratisNodeSync.NotInIBD(); stratisNode1.NotInIBD(); stratisNode2.NotInIBD(); // generate blocks and wait for the downloader to pickup stratisNodeSync.SetDummyMinerSecret(new BitcoinSecret(new Key(), stratisNodeSync.FullNode.Network)); stratisNodeSync.GenerateStratisWithMiner(105); // coinbase maturity = 100 // wait for block repo for block sync to work TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(stratisNodeSync)); // sync both nodes stratisNode1.CreateRPCClient().AddNode(stratisNodeSync.Endpoint, true); stratisNode2.CreateRPCClient().AddNode(stratisNodeSync.Endpoint, true); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisNode1, stratisNodeSync)); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisNode2, stratisNodeSync)); // create some transactions and push them to the pool var trxs = new List <Transaction>(); foreach (int index in Enumerable.Range(1, 5)) { Block block = stratisNodeSync.FullNode.BlockStoreManager().BlockRepository.GetAsync(stratisNodeSync.FullNode.Chain.GetBlock(index).HashBlock).Result; Transaction prevTrx = block.Transactions.First(); var dest = new BitcoinSecret(new Key(), stratisNodeSync.FullNode.Network); Transaction tx = stratisNodeSync.FullNode.Network.CreateTransaction(); tx.AddInput(new TxIn(new OutPoint(prevTrx.GetHash(), 0), PayToPubkeyHashTemplate.Instance.GenerateScriptPubKey(stratisNodeSync.MinerSecret.PubKey))); tx.AddOutput(new TxOut("25", dest.PubKey.Hash)); tx.AddOutput(new TxOut("24", new Key().PubKey.Hash)); // 1 btc fee tx.Sign(stratisNodeSync.FullNode.Network, stratisNodeSync.MinerSecret, false); trxs.Add(tx); } var options = new ParallelOptions { MaxDegreeOfParallelism = 5 }; Parallel.ForEach(trxs, options, transaction => { stratisNodeSync.Broadcast(transaction); }); // wait for all nodes to have all trx TestHelper.WaitLoop(() => stratisNodeSync.CreateRPCClient().GetRawMempool().Length == 5); // the full node should be connected to both nodes Assert.True(stratisNodeSync.FullNode.ConnectionManager.ConnectedPeers.Count() >= 2); TestHelper.WaitLoop(() => stratisNode1.CreateRPCClient().GetRawMempool().Length == 5); TestHelper.WaitLoop(() => stratisNode2.CreateRPCClient().GetRawMempool().Length == 5); // mine the transactions in the mempool stratisNodeSync.GenerateStratisWithMiner(1); TestHelper.WaitLoop(() => stratisNodeSync.CreateRPCClient().GetRawMempool().Length == 0); // wait for block and mempool to change TestHelper.WaitLoop(() => stratisNode1.CreateRPCClient().GetBestBlockHash() == stratisNodeSync.CreateRPCClient().GetBestBlockHash()); TestHelper.WaitLoop(() => stratisNode2.CreateRPCClient().GetBestBlockHash() == stratisNodeSync.CreateRPCClient().GetBestBlockHash()); TestHelper.WaitLoop(() => stratisNode1.CreateRPCClient().GetRawMempool().Length == 0); TestHelper.WaitLoop(() => stratisNode2.CreateRPCClient().GetRawMempool().Length == 0); } }