public async Task ReorgChain_FailsFullValidation_Reconnect_OldChain_Nodes_ConnectedAsync() { using (var builder = NodeBuilder.Create(this)) { var bitcoinNoValidationRulesNetwork = new BitcoinRegTestNoValidationRules(); var minerA = builder.CreateStratisPowNode(this.powNetwork).WithDummyWallet().WithReadyBlockchainData(ReadyBlockchain.BitcoinRegTest10Miner); var minerB = builder.CreateStratisPowNode(bitcoinNoValidationRulesNetwork).NoValidation().WithDummyWallet().Start(); ChainedHeader minerBChainTip = null; bool interceptorsEnabled = false; bool minerA_Disconnected_ItsOwnChain_ToConnectTo_MinerBs_LongerChain = false; bool minerA_IsConnecting_To_MinerBChain = false; bool minerA_Disconnected_MinerBsChain = false; bool minerA_Reconnected_Its_OwnChain = false; // Configure the interceptor to intercept when Miner A connects Miner B's chain. void interceptorConnect(ChainedHeaderBlock chainedHeaderBlock) { if (!interceptorsEnabled) { return; } if (!minerA_IsConnecting_To_MinerBChain) { if (chainedHeaderBlock.ChainedHeader.Height == 12) { minerA_IsConnecting_To_MinerBChain = minerA.FullNode.ConsensusManager().Tip.HashBlock == minerBChainTip.GetAncestor(12).HashBlock; } return; } if (!minerA_Reconnected_Its_OwnChain) { if (chainedHeaderBlock.ChainedHeader.Height == 14) { minerA_Reconnected_Its_OwnChain = true; } return; } } // Configure the interceptor to intercept when Miner A disconnects Miner B's chain after the reorg. void interceptorDisconnect(ChainedHeaderBlock chainedHeaderBlock) { if (!interceptorsEnabled) { return; } if (!minerA_Disconnected_ItsOwnChain_ToConnectTo_MinerBs_LongerChain) { if (minerA.FullNode.ConsensusManager().Tip.Height == 10) { minerA_Disconnected_ItsOwnChain_ToConnectTo_MinerBs_LongerChain = true; } return; } if (!minerA_Disconnected_MinerBsChain) { if (minerA.FullNode.ConsensusManager().Tip.Height == 10) { minerA_Disconnected_MinerBsChain = true; } return; } } minerA.SetConnectInterceptor(interceptorConnect); minerA.SetDisconnectInterceptor(interceptorDisconnect); minerA.Start(); // Miner B syncs with Miner A TestHelper.ConnectAndSync(minerB, minerA); // Disable Miner A from sending blocks to Miner B TestHelper.DisableBlockPropagation(minerA, minerB); // Miner A continues to mine to height 14 TestHelper.MineBlocks(minerA, 4); TestHelper.WaitLoop(() => minerA.FullNode.ConsensusManager().Tip.Height == 14); Assert.Equal(10, minerB.FullNode.ConsensusManager().Tip.Height); // Enable the interceptors so that they are active during the reorg. interceptorsEnabled = true; // Miner B mines 5 more blocks: // Block 6,7,9,10 = valid // Block 8 = invalid minerBChainTip = await TestHelper.BuildBlocks.OnNode(minerB).Amount(5).Invalid(13, (coreNode, block) => BlockBuilder.InvalidCoinbaseReward(coreNode, block)).BuildAsync(); Assert.Equal(15, minerBChainTip.Height); Assert.Equal(15, minerB.FullNode.ConsensusManager().Tip.Height); // Wait until Miner A disconnected its own chain so that it can connect to // Miner B's longer chain. TestHelper.WaitLoop(() => minerA_Disconnected_ItsOwnChain_ToConnectTo_MinerBs_LongerChain); // Wait until Miner A has connected Miner B's chain (but failed) TestHelper.WaitLoop(() => minerA_IsConnecting_To_MinerBChain); // Wait until Miner A has disconnected Miner B's invalid chain. TestHelper.WaitLoop(() => minerA_Disconnected_MinerBsChain); // Wait until Miner A has reconnected its own chain. TestHelper.WaitLoop(() => minerA_Reconnected_Its_OwnChain); } }
public void MempoolSyncTransactions() { using (NodeBuilder builder = NodeBuilder.Create(this)) { var stratisNodeSync = builder.CreateStratisPowNode(); var stratisNode1 = builder.CreateStratisPowNode(); var 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 (var index in Enumerable.Range(1, 5)) { var block = stratisNodeSync.FullNode.BlockStoreManager().BlockRepository.GetAsync(stratisNodeSync.FullNode.Chain.GetBlock(index).HashBlock).Result; var prevTrx = block.Transactions.First(); var dest = new BitcoinSecret(new Key(), stratisNodeSync.FullNode.Network); Transaction tx = stratisNodeSync.FullNode.Network.Consensus.ConsensusFactory.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); // reset the trickle timer on the full node that has the transactions in the pool foreach (var node in stratisNodeSync.FullNode.ConnectionManager.ConnectedPeers) { node.Behavior <MempoolBehavior>().NextInvSend = 0; } 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); } }
public void TestSegwit_MinedOnCore_ActivatedOn_StratisNode() { using (NodeBuilder builder = NodeBuilder.Create(version: "0.15.1")) { CoreNode coreNode = builder.CreateNode(false); coreNode.ConfigParameters.AddOrReplace("debug", "1"); coreNode.ConfigParameters.AddOrReplace("printtoconsole", "0"); coreNode.Start(); CoreNode stratisNode = builder.CreateStratisPowNode(true, fullNodeBuilder => { fullNodeBuilder .UsePowConsensus() .UseBlockStore() .UseWallet() .UseMempool() .AddMining() .AddRPC(); }); RPCClient stratisNodeRpc = stratisNode.CreateRPCClient(); RPCClient coreRpc = coreNode.CreateRPCClient(); coreRpc.AddNode(stratisNode.Endpoint, false); stratisNodeRpc.AddNode(coreNode.Endpoint, false); // core (in version 0.15.1) only mines segwit blocks above a certain height on regtest // future versions of core will change that behaviour so this test may need to be changed in the future // see issue for more details https://github.com/stratisproject/StratisBitcoinFullNode/issues/1028 BIP9DeploymentsParameters prevSegwitDeployment = Network.RegTest.Consensus.BIP9Deployments[BIP9Deployments.Segwit]; Network.RegTest.Consensus.BIP9Deployments[BIP9Deployments.Segwit] = new BIP9DeploymentsParameters(1, 0, DateTime.Now.AddDays(50).ToUnixTimestamp()); try { // generate 450 blocks, block 431 will be segwit activated. coreRpc.Generate(450); TestHelper.WaitLoop(() => stratisNode.CreateRPCClient().GetBestBlockHash() == coreNode.CreateRPCClient().GetBestBlockHash()); // segwit activation on Bitcoin regtest. // - On regtest deployment state changes every 144 block, the threshold for activating a rule is 108 blocks. // segwit deployment status should be: // - Defined up to block 142. // - Started at block 143 to block 286 . // - LockedIn 287 (as segwit should already be signaled in blocks). // - Active at block 431. IConsensusLoop consensusLoop = stratisNode.FullNode.NodeService <IConsensusLoop>(); ThresholdState[] segwitDefinedState = consensusLoop.NodeDeployments.BIP9.GetStates(stratisNode.FullNode.Chain.GetBlock(142)); ThresholdState[] segwitStartedState = consensusLoop.NodeDeployments.BIP9.GetStates(stratisNode.FullNode.Chain.GetBlock(143)); ThresholdState[] segwitLockedInState = consensusLoop.NodeDeployments.BIP9.GetStates(stratisNode.FullNode.Chain.GetBlock(287)); ThresholdState[] segwitActiveState = consensusLoop.NodeDeployments.BIP9.GetStates(stratisNode.FullNode.Chain.GetBlock(431)); // check that segwit is got activated at block 431 Assert.Equal(ThresholdState.Defined, segwitDefinedState.GetValue((int)BIP9Deployments.Segwit)); Assert.Equal(ThresholdState.Started, segwitStartedState.GetValue((int)BIP9Deployments.Segwit)); Assert.Equal(ThresholdState.LockedIn, segwitLockedInState.GetValue((int)BIP9Deployments.Segwit)); Assert.Equal(ThresholdState.Active, segwitActiveState.GetValue((int)BIP9Deployments.Segwit)); } finally { Network.RegTest.Consensus.BIP9Deployments[BIP9Deployments.Segwit] = prevSegwitDeployment; } } }
public void SegwitWalletTransactionBuildingAndPropagationTest() { using (NodeBuilder builder = NodeBuilder.Create(this)) { CoreNode node = builder.CreateStratisPosNode(KnownNetworks.StraxRegTest).WithWallet().Start(); CoreNode listener = builder.CreateStratisPosNode(KnownNetworks.StraxRegTest).WithWallet().Start(); IConnectionManager listenerConnMan = listener.FullNode.NodeService <IConnectionManager>(); listenerConnMan.Parameters.TemplateBehaviors.Add(new TestBehavior()); // The listener node will have default settings, i.e. it should ask for witness data in P2P messages. Assert.True(listenerConnMan.Parameters.Services.HasFlag(NetworkPeerServices.NODE_WITNESS)); TestHelper.Connect(listener, node); var mineAddress = node.FullNode.WalletManager().GetUnusedAddress(); var miner = node.FullNode.NodeService <IPowMining>() as PowMining; miner.GenerateBlocks(new ReserveScript(mineAddress.ScriptPubKey), (ulong)(node.FullNode.Network.Consensus.CoinbaseMaturity + 1), int.MaxValue); // Wait for listener to sync to the same block height so that it won't reject the coinbase spend as being premature. TestHelper.WaitForNodeToSync(node, listener); // Send a transaction from first node to itself so that it has a proper segwit input to spend. var destinationAddress = node.FullNode.WalletManager().GetUnusedAddress(); var witAddress = destinationAddress.Bech32Address; IActionResult transactionResult = node.FullNode.NodeController <WalletController>() .BuildTransaction(new BuildTransactionRequest { AccountName = "account 0", AllowUnconfirmed = true, Recipients = new List <RecipientModel> { new RecipientModel { DestinationAddress = witAddress, Amount = Money.Coins(1).ToString() } }, Password = node.WalletPassword, WalletName = node.WalletName, FeeAmount = Money.Coins(0.001m).ToString() }).GetAwaiter().GetResult(); var walletBuildTransactionModel = (WalletBuildTransactionModel)(transactionResult as JsonResult)?.Value; node.FullNode.NodeController <WalletController>().SendTransaction(new SendTransactionRequest(walletBuildTransactionModel.Hex)); Transaction witFunds = node.FullNode.Network.CreateTransaction(walletBuildTransactionModel.Hex); uint witIndex = witFunds.Outputs.AsIndexedOutputs().First(o => o.TxOut.ScriptPubKey.IsScriptType(ScriptType.P2WPKH)).N; TestBase.WaitLoop(() => listener.CreateRPCClient().GetBlockCount() >= 1, cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token); TestBase.WaitLoop(() => node.CreateRPCClient().GetRawMempool().Length > 0, cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token); TestBase.WaitLoop(() => listener.CreateRPCClient().GetRawMempool().Length > 0, cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token); INetworkPeer connectedPeer = listenerConnMan.ConnectedPeers.FindByEndpoint(node.Endpoint); TestBehavior testBehavior = connectedPeer.Behavior <TestBehavior>(); miner.GenerateBlocks(new ReserveScript(mineAddress.ScriptPubKey), 1, int.MaxValue); TestBase.WaitLoop(() => node.CreateRPCClient().GetRawMempool().Length == 0, cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token); TestBase.WaitLoop(() => listener.CreateRPCClient().GetRawMempool().Length == 0, cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token); // Make sure wallet is synced. TestBase.WaitLoop(() => node.CreateRPCClient().GetBlockCount() == node.FullNode.WalletManager().LastBlockHeight(), cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token); // We need to capture a message on the witness-enabled destination node and see that it contains a transaction serialised with witness data. // However, the first transaction has no witness data since it was only being sent to a segwit scriptPubKey (i.e. no witness input data). // So clear all messages for now. testBehavior.receivedMessageTracker.Clear(); // Send a transaction that has a segwit input, to a segwit address. transactionResult = node.FullNode.NodeController <WalletController>() .BuildTransaction(new BuildTransactionRequest { AccountName = "account 0", AllowUnconfirmed = true, Outpoints = new List <OutpointRequest>() { new OutpointRequest() { Index = (int)witIndex, TransactionId = witFunds.GetHash().ToString() } }, Recipients = new List <RecipientModel> { new RecipientModel { DestinationAddress = witAddress, Amount = Money.Coins(0.5m).ToString() } }, Password = node.WalletPassword, WalletName = node.WalletName, FeeAmount = Money.Coins(0.001m).ToString() }).GetAwaiter().GetResult(); walletBuildTransactionModel = (WalletBuildTransactionModel)(transactionResult as JsonResult)?.Value; node.FullNode.NodeController <WalletController>().SendTransaction(new SendTransactionRequest(walletBuildTransactionModel.Hex)); TestBase.WaitLoop(() => node.CreateRPCClient().GetRawMempool().Length > 0, cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token); TestBase.WaitLoop(() => listener.CreateRPCClient().GetRawMempool().Length > 0, cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token); var txMessages = testBehavior.receivedMessageTracker["tx"]; var txMessage = txMessages.First(); var receivedTransaction = txMessage.Message.Payload as TxPayload; var parsedTransaction = receivedTransaction.Obj; var nonWitnessTransaction = parsedTransaction.WithOptions(TransactionOptions.None, listener.FullNode.Network.Consensus.ConsensusFactory); Assert.True(parsedTransaction.GetSerializedSize() > nonWitnessTransaction.GetSerializedSize()); miner.GenerateBlocks(new ReserveScript(mineAddress.ScriptPubKey), 1, int.MaxValue); TestBase.WaitLoop(() => node.CreateRPCClient().GetRawMempool().Length == 0, cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token); TestBase.WaitLoop(() => listener.CreateRPCClient().GetRawMempool().Length == 0, cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token); } }
public void TxMempoolMapOrphans() { var rand = new Random(); var randByte = new byte[32]; Func <uint256> randHash = () => { rand.NextBytes(randByte); return(new uint256(randByte)); }; using (NodeBuilder builder = NodeBuilder.Create(this)) { var stratisNode = builder.CreateStratisPowNode(); builder.StartAll(); stratisNode.SetDummyMinerSecret(new BitcoinSecret(new Key(), stratisNode.FullNode.Network)); // 50 orphan transactions: for (ulong i = 0; i < 50; i++) { Transaction tx = stratisNode.FullNode.Network.Consensus.ConsensusFactory.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.MempoolManager().Orphans.AddOrphanTx(i, tx).Wait(); } Assert.Equal(50, stratisNode.FullNode.MempoolManager().Orphans.OrphansList().Count); // ... and 50 that depend on other orphans: for (ulong i = 0; i < 50; i++) { var txPrev = stratisNode.FullNode.MempoolManager().Orphans.OrphansList().ElementAt(rand.Next(stratisNode.FullNode.MempoolManager().Orphans.OrphansList().Count)); Transaction tx = stratisNode.FullNode.Network.Consensus.ConsensusFactory.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.MempoolManager().Orphans.AddOrphanTx(i, tx).Wait(); } Assert.Equal(100, stratisNode.FullNode.MempoolManager().Orphans.OrphansList().Count); // This really-big orphan should be ignored: for (ulong i = 0; i < 10; i++) { var txPrev = stratisNode.FullNode.MempoolManager().Orphans.OrphansList().ElementAt(rand.Next(stratisNode.FullNode.MempoolManager().Orphans.OrphansList().Count)); Transaction tx = stratisNode.FullNode.Network.Consensus.ConsensusFactory.CreateTransaction(); tx.AddOutput(new TxOut(new Money(1 * Money.CENT), stratisNode.MinerSecret.ScriptPubKey)); foreach (var 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.MempoolManager().Orphans.AddOrphanTx(i, tx).Result); } Assert.Equal(100, stratisNode.FullNode.MempoolManager().Orphans.OrphansList().Count); // Test EraseOrphansFor: for (ulong i = 0; i < 3; i++) { var sizeBefore = stratisNode.FullNode.MempoolManager().Orphans.OrphansList().Count; stratisNode.FullNode.MempoolManager().Orphans.EraseOrphansFor(i).Wait(); Assert.True(stratisNode.FullNode.MempoolManager().Orphans.OrphansList().Count < sizeBefore); } // Test LimitOrphanTxSize() function: stratisNode.FullNode.MempoolManager().Orphans.LimitOrphanTxSizeAsync(40).Wait(); Assert.True(stratisNode.FullNode.MempoolManager().Orphans.OrphansList().Count <= 40); stratisNode.FullNode.MempoolManager().Orphans.LimitOrphanTxSizeAsync(10).Wait(); Assert.True(stratisNode.FullNode.MempoolManager().Orphans.OrphansList().Count <= 10); stratisNode.FullNode.MempoolManager().Orphans.LimitOrphanTxSizeAsync(0).Wait(); Assert.True(!stratisNode.FullNode.MempoolManager().Orphans.OrphansList().Any()); } }
public void SegwitWalletTransactionBuildingTest_SendToBech32AndNormalDestination() { using (NodeBuilder builder = NodeBuilder.Create(this)) { CoreNode node = builder.CreateStratisPosNode(KnownNetworks.StraxRegTest).WithWallet().Start(); CoreNode listener = builder.CreateStratisPosNode(KnownNetworks.StraxRegTest).WithWallet().Start(); TestHelper.Connect(listener, node); var mineAddress = node.FullNode.WalletManager().GetUnusedAddress(); int maturity = (int)node.FullNode.Network.Consensus.CoinbaseMaturity; var miner = node.FullNode.NodeService <IPowMining>() as PowMining; miner.GenerateBlocks(new ReserveScript(mineAddress.ScriptPubKey), (ulong)(maturity + 1), int.MaxValue); TestHelper.WaitForNodeToSync(node, listener); var destinationAddress = node.FullNode.WalletManager().GetUnusedAddress(); var witAddress = destinationAddress.Bech32Address; var nonWitAddress = destinationAddress.Address; IActionResult transactionResult = node.FullNode.NodeController <WalletController>() .BuildTransaction(new BuildTransactionRequest { AccountName = "account 0", AllowUnconfirmed = true, Recipients = new List <RecipientModel> { new RecipientModel { DestinationAddress = witAddress, Amount = Money.Coins(1).ToString() } }, Password = node.WalletPassword, WalletName = node.WalletName, FeeAmount = Money.Coins(0.001m).ToString() }).GetAwaiter().GetResult(); var walletBuildTransactionModel = (WalletBuildTransactionModel)(transactionResult as JsonResult)?.Value; node.FullNode.NodeController <WalletController>().SendTransaction(new SendTransactionRequest(walletBuildTransactionModel.Hex)); Transaction witFunds = node.FullNode.Network.CreateTransaction(walletBuildTransactionModel.Hex); uint witIndex = witFunds.Outputs.AsIndexedOutputs().First(o => o.TxOut.ScriptPubKey.IsScriptType(ScriptType.P2WPKH)).N; TestBase.WaitLoop(() => listener.CreateRPCClient().GetBlockCount() >= (maturity + 1), cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token); TestBase.WaitLoop(() => node.CreateRPCClient().GetRawMempool().Length > 0, cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token); TestBase.WaitLoop(() => listener.CreateRPCClient().GetRawMempool().Length > 0, cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token); miner.GenerateBlocks(new ReserveScript(mineAddress.ScriptPubKey), 1, int.MaxValue); TestBase.WaitLoop(() => node.CreateRPCClient().GetRawMempool().Length == 0, cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token); TestBase.WaitLoop(() => listener.CreateRPCClient().GetRawMempool().Length == 0, cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token); // Make sure wallet is synced. TestBase.WaitLoop(() => node.CreateRPCClient().GetBlockCount() == node.FullNode.WalletManager().LastBlockHeight(), cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token); // By sending more than the size of the P2WPKH UTXO, we guarantee that at least one non-P2WPKH UTXO gets included transactionResult = node.FullNode.NodeController <WalletController>() .BuildTransaction(new BuildTransactionRequest { AccountName = "account 0", AllowUnconfirmed = true, Outpoints = new List <OutpointRequest>() { new OutpointRequest() { Index = (int)witIndex, TransactionId = witFunds.GetHash().ToString() } }, Recipients = new List <RecipientModel> { new RecipientModel { DestinationAddress = witAddress, Amount = Money.Coins(0.4m).ToString() }, new RecipientModel { DestinationAddress = nonWitAddress, Amount = Money.Coins(0.4m).ToString() } }, Password = node.WalletPassword, WalletName = node.WalletName, FeeAmount = Money.Coins(0.001m).ToString() }).GetAwaiter().GetResult(); walletBuildTransactionModel = (WalletBuildTransactionModel)(transactionResult as JsonResult)?.Value; node.FullNode.NodeController <WalletController>().SendTransaction(new SendTransactionRequest(walletBuildTransactionModel.Hex)); TestBase.WaitLoop(() => node.CreateRPCClient().GetRawMempool().Length > 0, cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token); TestBase.WaitLoop(() => listener.CreateRPCClient().GetRawMempool().Length > 0, cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token); miner.GenerateBlocks(new ReserveScript(mineAddress.ScriptPubKey), 1, int.MaxValue); TestBase.WaitLoop(() => node.CreateRPCClient().GetRawMempool().Length == 0, cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token); TestBase.WaitLoop(() => listener.CreateRPCClient().GetRawMempool().Length == 0, cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token); } }
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 // connected to a longer chain that couse a reorg back so the second trasnaction is undone // mine the second transaction back in to the main chain using (NodeBuilder builder = NodeBuilder.Create()) { var stratisSender = builder.CreateStratisPowNode(); var stratisReceiver = builder.CreateStratisPowNode(); var stratisReorg = builder.CreateStratisPowNode(); builder.StartAll(); stratisSender.NotInIBD(); stratisReceiver.NotInIBD(); stratisReorg.NotInIBD(); // get a key from the wallet var mnemonic1 = stratisSender.FullNode.WalletManager().CreateWallet("123456", "mywallet"); var mnemonic2 = stratisReceiver.FullNode.WalletManager().CreateWallet("123456", "mywallet"); Assert.Equal(12, mnemonic1.Words.Length); Assert.Equal(12, mnemonic2.Words.Length); var addr = stratisSender.FullNode.WalletManager().GetUnusedAddress(new WalletAccountReference("mywallet", "account 0")); var wallet = stratisSender.FullNode.WalletManager().GetWalletByName("mywallet"); var key = wallet.GetExtendedPrivateKeyForAddress("123456", addr).PrivateKey; stratisSender.SetDummyMinerSecret(new BitcoinSecret(key, stratisSender.FullNode.Network)); stratisReorg.SetDummyMinerSecret(new BitcoinSecret(key, stratisSender.FullNode.Network)); var maturity = (int)stratisSender.FullNode.Network.Consensus.Option <PowConsensusOptions>().CoinbaseMaturity; stratisSender.GenerateStratisWithMiner(maturity + 15); var currentBestHeight = maturity + 15; // wait for block repo for block sync to work TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(stratisSender)); // the mining should add coins to the wallet var total = stratisSender.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").Sum(s => s.Transaction.Amount); Assert.Equal(Money.COIN * currentBestHeight * 50, total); // sync all nodes stratisReceiver.CreateRPCClient().AddNode(stratisSender.Endpoint, true); stratisReceiver.CreateRPCClient().AddNode(stratisReorg.Endpoint, true); stratisSender.CreateRPCClient().AddNode(stratisReorg.Endpoint, true); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisSender)); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisReorg)); // Build Transaction 1 // ==================== // send coins to the receiver var sendto = stratisReceiver.FullNode.WalletManager().GetUnusedAddress(new WalletAccountReference("mywallet", "account 0")); var transaction1 = stratisSender.FullNode.WalletTransactionHandler().BuildTransaction(CreateContext(new WalletAccountReference("mywallet", "account 0"), "123456", sendto.ScriptPubKey, Money.COIN * 100, FeeType.Medium, 101)); // broadcast to the other node stratisSender.FullNode.NodeService <WalletController>().SendTransaction(new SendTransactionRequest(transaction1.ToHex())); // wait for the trx to arrive TestHelper.WaitLoop(() => stratisReceiver.CreateRPCClient().GetRawMempool().Length > 0); Assert.NotNull(stratisReceiver.CreateRPCClient().GetRawTransaction(transaction1.GetHash(), false)); TestHelper.WaitLoop(() => stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").Any()); var receivetotal = stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").Sum(s => s.Transaction.Amount); Assert.Equal(Money.COIN * 100, receivetotal); Assert.Null(stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").First().Transaction.BlockHeight); // generate two new blocks so the trx is confirmed stratisSender.GenerateStratisWithMiner(1); var transaction1MinedHeight = currentBestHeight + 1; stratisSender.GenerateStratisWithMiner(1); currentBestHeight = currentBestHeight + 2; // wait for block repo for block sync to work TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(stratisSender)); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisSender)); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisReorg)); Assert.Equal(currentBestHeight, stratisReceiver.FullNode.Chain.Tip.Height); TestHelper.WaitLoop(() => transaction1MinedHeight == stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").First().Transaction.BlockHeight); // Build Transaction 2 // ==================== // remove the reorg node stratisReceiver.CreateRPCClient().RemoveNode(stratisReorg.Endpoint); stratisSender.CreateRPCClient().RemoveNode(stratisReorg.Endpoint); var forkblock = stratisReceiver.FullNode.Chain.Tip; // send more coins to the wallet sendto = stratisReceiver.FullNode.WalletManager().GetUnusedAddress(new WalletAccountReference("mywallet", "account 0")); var transaction2 = stratisSender.FullNode.WalletTransactionHandler().BuildTransaction(CreateContext(new WalletAccountReference("mywallet", "account 0"), "123456", sendto.ScriptPubKey, Money.COIN * 10, FeeType.Medium, 101)); stratisSender.FullNode.NodeService <WalletController>().SendTransaction(new SendTransactionRequest(transaction2.ToHex())); // wait for the trx to arrive TestHelper.WaitLoop(() => stratisReceiver.CreateRPCClient().GetRawMempool().Length > 0); Assert.NotNull(stratisReceiver.CreateRPCClient().GetRawTransaction(transaction2.GetHash(), false)); TestHelper.WaitLoop(() => stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").Any()); var newamount = stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").Sum(s => s.Transaction.Amount); Assert.Equal(Money.COIN * 110, newamount); Assert.Contains(stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet"), b => b.Transaction.BlockHeight == null); // mine more blocks so its included in the chain stratisSender.GenerateStratisWithMiner(1); var transaction2MinedHeight = currentBestHeight + 1; stratisSender.GenerateStratisWithMiner(1); currentBestHeight = currentBestHeight + 2; TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(stratisSender)); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisSender)); Assert.Equal(currentBestHeight, stratisReceiver.FullNode.Chain.Tip.Height); TestHelper.WaitLoop(() => stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").Any(b => b.Transaction.BlockHeight == transaction2MinedHeight)); // create a reorg by mining on two different chains // ================================================ // advance both chains, one chin is longer stratisSender.GenerateStratisWithMiner(2); stratisReorg.GenerateStratisWithMiner(10); currentBestHeight = forkblock.Height + 10; TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(stratisSender)); TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(stratisReorg)); // connect the reorg chain stratisReceiver.CreateRPCClient().AddNode(stratisReorg.Endpoint, true); stratisSender.CreateRPCClient().AddNode(stratisReorg.Endpoint, true); // wait for the chains to catch up TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisSender)); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisReorg)); Assert.Equal(currentBestHeight, stratisReceiver.FullNode.Chain.Tip.Height); // ensure wallet reorg complete TestHelper.WaitLoop(() => stratisReceiver.FullNode.WalletManager().WalletTipHash == stratisReorg.CreateRPCClient().GetBestBlockHash()); // check the wallet amount was rolled back var newtotal = stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").Sum(s => s.Transaction.Amount); Assert.Equal(receivetotal, newtotal); TestHelper.WaitLoop(() => maturity + 16 == stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").First().Transaction.BlockHeight); // ReBuild Transaction 2 // ==================== // After the reorg transaction2 was returned back to mempool stratisSender.FullNode.NodeService <WalletController>().SendTransaction(new SendTransactionRequest(transaction2.ToHex())); TestHelper.WaitLoop(() => stratisReceiver.CreateRPCClient().GetRawMempool().Length > 0); // mine the transaction again stratisSender.GenerateStratisWithMiner(1); transaction2MinedHeight = currentBestHeight + 1; stratisSender.GenerateStratisWithMiner(1); currentBestHeight = currentBestHeight + 2; TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(stratisSender)); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisSender)); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisReorg)); Assert.Equal(currentBestHeight, stratisReceiver.FullNode.Chain.Tip.Height); var newsecondamount = stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").Sum(s => s.Transaction.Amount); Assert.Equal(newamount, newsecondamount); TestHelper.WaitLoop(() => stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").Any(b => b.Transaction.BlockHeight == transaction2MinedHeight)); } }
public void MiningNodeWithOneConnectionAlwaysSynced() { string testFolderPath = Path.Combine(this.GetType().Name, nameof(MiningNodeWithOneConnectionAlwaysSynced)); using (NodeBuilder nodeBuilder = NodeBuilder.Create(testFolderPath)) { CoreNode minerNode = nodeBuilder.CreateStratisPowNode(this.powNetwork); minerNode.Start(); minerNode.NotInIBD().WithWallet(); CoreNode connectorNode = nodeBuilder.CreateStratisPowNode(this.powNetwork); connectorNode.Start(); connectorNode.NotInIBD().WithWallet(); CoreNode firstNode = nodeBuilder.CreateStratisPowNode(this.powNetwork); firstNode.Start(); firstNode.NotInIBD().WithWallet(); CoreNode secondNode = nodeBuilder.CreateStratisPowNode(this.powNetwork); secondNode.Start(); secondNode.NotInIBD().WithWallet(); TestHelper.Connect(minerNode, connectorNode); TestHelper.Connect(connectorNode, firstNode); TestHelper.Connect(connectorNode, secondNode); TestHelper.Connect(firstNode, secondNode); List <CoreNode> nodes = new List <CoreNode> { minerNode, connectorNode, firstNode, secondNode }; nodes.ForEach(n => { TestHelper.MineBlocks(n, 1); TestHelper.WaitForNodeToSync(nodes.ToArray()); }); int networkHeight = minerNode.FullNode.Chain.Height; Assert.Equal(networkHeight, nodes.Count); // Random node on network generates a block. TestHelper.MineBlocks(firstNode, 1); // Wait until connector get the hash of network's block. while ((connectorNode.FullNode.ChainBehaviorState.ConsensusTip.HashBlock != firstNode.FullNode.ChainBehaviorState.ConsensusTip.HashBlock) || (firstNode.FullNode.ChainBehaviorState.ConsensusTip.Height == networkHeight)) { Thread.Sleep(1); } Assert.Equal(connectorNode.FullNode.Chain.Tip.HashBlock, firstNode.FullNode.Chain.Tip.HashBlock); Assert.Equal(minerNode.FullNode.Chain.Tip.Height, networkHeight); Assert.Equal(connectorNode.FullNode.Chain.Tip.Height, networkHeight + 1); // Miner mines the block. TestHelper.MineBlocks(minerNode, 1); networkHeight++; Assert.Equal(connectorNode.FullNode.Chain.Tip.HashBlock, firstNode.FullNode.Chain.Tip.HashBlock); Assert.Equal(minerNode.FullNode.Chain.Tip.Height, networkHeight); Assert.Equal(connectorNode.FullNode.Chain.Tip.Height, networkHeight); TestHelper.MineBlocks(connectorNode, 1); networkHeight++; TestHelper.WaitForNodeToSync(nodes.ToArray()); nodes.All(n => n.FullNode.Chain.Height == networkHeight).Should() .BeTrue(because: "all nodes have synced to chain height"); Assert.Equal(firstNode.FullNode.Chain.Tip.HashBlock, minerNode.FullNode.Chain.Tip.HashBlock); } }
public void Given__NodesAreSynced__When__ABigReorgHappens__Then__TheReorgIsIgnored() { // Temporary fix so the Network static initialize will not break. var m = Network.Main; try { using (NodeBuilder builder = NodeBuilder.Create()) { var stratisMiner = builder.CreateStratisPosNode(); var stratisSyncer = builder.CreateStratisPosNode(); var stratisReorg = builder.CreateStratisPosNode(); builder.StartAll(); stratisMiner.NotInIBD(); stratisSyncer.NotInIBD(); stratisReorg.NotInIBD(); // TODO: set the max allowed reorg threshold here // assume a reorg of 10 blocks is not allowed. stratisMiner.FullNode.ChainBehaviorState.MaxReorgLength = 10; stratisSyncer.FullNode.ChainBehaviorState.MaxReorgLength = 10; stratisReorg.FullNode.ChainBehaviorState.MaxReorgLength = 10; stratisMiner.SetDummyMinerSecret(new BitcoinSecret(new Key(), stratisMiner.FullNode.Network)); stratisReorg.SetDummyMinerSecret(new BitcoinSecret(new Key(), stratisReorg.FullNode.Network)); stratisMiner.GenerateStratisWithMiner(1); // wait for block repo for block sync to work TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(stratisMiner)); stratisMiner.CreateRPCClient().AddNode(stratisReorg.Endpoint, true); stratisMiner.CreateRPCClient().AddNode(stratisSyncer.Endpoint, true); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisMiner, stratisSyncer)); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisMiner, stratisReorg)); // create a reorg by mining on two different chains // ================================================ stratisMiner.CreateRPCClient().RemoveNode(stratisReorg.Endpoint); stratisSyncer.CreateRPCClient().RemoveNode(stratisReorg.Endpoint); var t1 = Task.Run(() => stratisMiner.GenerateStratisWithMiner(11)); var t2 = Task.Delay(1000).ContinueWith(t => stratisReorg.GenerateStratisWithMiner(12)); Task.WaitAll(t1, t2); TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(stratisMiner)); TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(stratisReorg)); // make sure the nodes are actually on different chains. Assert.NotEqual(stratisMiner.FullNode.Chain.GetBlock(2).HashBlock, stratisReorg.FullNode.Chain.GetBlock(2).HashBlock); TestHelper.TriggerSync(stratisSyncer); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisMiner, stratisSyncer)); // The hash before the reorg node is connected. var hashBeforeReorg = stratisMiner.FullNode.Chain.Tip.HashBlock; // connect the reorg chain stratisMiner.CreateRPCClient().AddNode(stratisReorg.Endpoint, true); stratisSyncer.CreateRPCClient().AddNode(stratisReorg.Endpoint, true); // trigger nodes to sync TestHelper.TriggerSync(stratisMiner); TestHelper.TriggerSync(stratisReorg); TestHelper.TriggerSync(stratisSyncer); // wait for the synced chain to get headers updated. TestHelper.WaitLoop(() => !stratisReorg.FullNode.ConnectionManager.ConnectedNodes.Any()); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisMiner, stratisSyncer)); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReorg, stratisMiner) == false); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReorg, stratisSyncer) == false); // check that a reorg did not happen. Assert.Equal(hashBeforeReorg, stratisSyncer.FullNode.Chain.Tip.HashBlock); } } finally { Transaction.TimeStamp = false; Block.BlockSignature = false; } }
public ProofOfStakeSteps(string displayName) { this.nodeBuilder = NodeBuilder.Create(Path.Combine(this.GetType().Name, displayName)); }
public void Given_NodesAreSynced_When_ABigReorgHappens_Then_TheReorgIsIgnored() { using (NodeBuilder builder = NodeBuilder.Create(this)) { CoreNode stratisMiner = builder.CreateStratisPosNode(this.posNetwork); CoreNode stratisSyncer = builder.CreateStratisPosNode(this.posNetwork); CoreNode stratisReorg = builder.CreateStratisPosNode(this.posNetwork); builder.StartAll(); stratisMiner.NotInIBD().WithWallet(); stratisSyncer.NotInIBD(); stratisReorg.NotInIBD().WithWallet(); TestHelper.MineBlocks(stratisMiner, 1); // wait for block repo for block sync to work stratisMiner.CreateRPCClient().AddNode(stratisReorg.Endpoint, true); stratisMiner.CreateRPCClient().AddNode(stratisSyncer.Endpoint, true); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisMiner, stratisSyncer)); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisMiner, stratisReorg)); // create a reorg by mining on two different chains // ================================================ stratisMiner.CreateRPCClient().RemoveNode(stratisReorg.Endpoint); stratisSyncer.CreateRPCClient().RemoveNode(stratisReorg.Endpoint); TestHelper.WaitLoop(() => !TestHelper.IsNodeConnected(stratisReorg)); TestHelper.MineBlocks(stratisMiner, 11); TestHelper.MineBlocks(stratisReorg, 12); // make sure the nodes are actually on different chains. Assert.NotEqual(stratisMiner.FullNode.Chain.GetBlock(2).HashBlock, stratisReorg.FullNode.Chain.GetBlock(2).HashBlock); TestHelper.TriggerSync(stratisSyncer); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisMiner, stratisSyncer)); // The hash before the reorg node is connected. uint256 hashBeforeReorg = stratisMiner.FullNode.Chain.Tip.HashBlock; // connect the reorg chain stratisMiner.CreateRPCClient().AddNode(stratisReorg.Endpoint, true); stratisSyncer.CreateRPCClient().AddNode(stratisReorg.Endpoint, true); // trigger nodes to sync TestHelper.TriggerSync(stratisMiner); TestHelper.TriggerSync(stratisReorg); TestHelper.TriggerSync(stratisSyncer); // wait for the synced chain to get headers updated. TestHelper.WaitLoop(() => !stratisReorg.FullNode.ConnectionManager.ConnectedPeers.Any()); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisMiner, stratisSyncer)); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReorg, stratisMiner) == false); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReorg, stratisSyncer) == false); // check that a reorg did not happen. Assert.Equal(hashBeforeReorg, stratisSyncer.FullNode.Chain.Tip.HashBlock); } }
public void ReorgChain_AfterInitialRewind_ChainB_MinerB_Disconnects() { using (var builder = NodeBuilder.Create(this)) { var network = new BitcoinRegTest(); var minerA = builder.CreateStratisPowNode(network, "cmi-2-minerA").WithDummyWallet(); var minerB = builder.CreateStratisPowNode(network, "cmi-2-minerB").WithDummyWallet(); var syncer = builder.CreateStratisPowNode(network, "cmi-2-syncer").WithDummyWallet(); bool minerADisconnectedFromMinerB = false; // Configure the interceptor to disconnect a node after a certain block has been disconnected (rewound). void interceptor(ChainedHeaderBlock chainedHeaderBlock) { if (minerADisconnectedFromMinerB) { return; } if (chainedHeaderBlock.ChainedHeader.Previous.Height == 10) { // Ensure that minerA's tips has rewound to 10. TestBase.WaitLoop(() => TestHelper.IsNodeSyncedAtHeight(minerA, 10)); TestHelper.Disconnect(minerA, minerB); minerADisconnectedFromMinerB = true; } } // Start minerA and mine 10 blocks. We cannot use a premade chain as it adversely affects the max tip age calculation, causing sporadic sync errors. minerA.Start(); TestHelper.MineBlocks(minerA, 10); TestBase.WaitLoop(() => minerA.FullNode.ConsensusManager().Tip.Height == 10); // Start the other nodes. minerB.Start(); syncer.Start(); minerA.SetDisconnectInterceptor(interceptor); // MinerB/Syncer syncs with MinerA. TestHelper.ConnectAndSync(minerA, minerB, syncer); // Disable block propagation from MinerA to MinerB so that it can mine on its own and create a fork. TestHelper.DisableBlockPropagation(minerA, minerB); // MinerA continues to mine to height 14. TestHelper.MineBlocks(minerA, 4); TestBase.WaitLoop(() => minerA.FullNode.ConsensusManager().Tip.Height == 14); TestBase.WaitLoop(() => minerB.FullNode.ConsensusManager().Tip.Height == 10); TestBase.WaitLoop(() => syncer.FullNode.ConsensusManager().Tip.Height == 14); // MinerB mines 5 more blocks so that a reorg is triggered. TestHelper.MineBlocks(minerB, 5); TestBase.WaitLoop(() => TestHelper.IsNodeSyncedAtHeight(minerB, 15)); // MinerA and Syncer should have reorged to the longer chain. TestBase.WaitLoop(() => TestHelper.AreNodesSynced(minerA, minerB)); TestBase.WaitLoop(() => TestHelper.AreNodesSynced(syncer, minerB)); } }
public void WalletCanSendOneTransactionWithManyOutputs() { using (NodeBuilder builder = NodeBuilder.Create()) { CoreNode stratisSender = builder.CreateStratisPowNode(); CoreNode stratisReceiver = builder.CreateStratisPowNode(); builder.StartAll(); stratisSender.NotInIBD(); stratisReceiver.NotInIBD(); stratisSender.FullNode.WalletManager().CreateWallet("123456", "mywallet"); stratisReceiver.FullNode.WalletManager().CreateWallet("123456", "mywallet"); HdAddress addr = stratisSender.FullNode.WalletManager().GetUnusedAddress(new WalletAccountReference("mywallet", "account 0")); Wallet wallet = stratisSender.FullNode.WalletManager().GetWalletByName("mywallet"); Key key = wallet.GetExtendedPrivateKeyForAddress("123456", addr).PrivateKey; stratisSender.SetDummyMinerSecret(new BitcoinSecret(key, stratisSender.FullNode.Network)); int maturity = (int)stratisSender.FullNode.Network.Consensus.Option <PowConsensusOptions>().CoinbaseMaturity; stratisSender.GenerateStratisWithMiner(maturity + 51); // Wait for block repo for block sync to work. TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(stratisSender)); Assert.Equal(Money.COIN * 150 * 50, stratisSender.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").Sum(s => s.Transaction.Amount)); // Sync both nodes. stratisSender.CreateRPCClient().AddNode(stratisReceiver.Endpoint, true); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisSender)); // Get 50 unused addresses from the receiver. IEnumerable <HdAddress> recevierAddresses = stratisReceiver.FullNode.WalletManager() .GetUnusedAddresses(new WalletAccountReference("mywallet", "account 0"), 50); List <Recipient> recipients = recevierAddresses.Select(address => new Recipient { ScriptPubKey = address.ScriptPubKey, Amount = Money.COIN }) .ToList(); var transactionBuildContext = new TransactionBuildContext( new WalletAccountReference("mywallet", "account 0"), recipients, "123456") { FeeType = FeeType.Medium, MinConfirmations = 101 }; Transaction transaction = stratisSender.FullNode.WalletTransactionHandler().BuildTransaction(transactionBuildContext); Assert.Equal(51, transaction.Outputs.Count); // Broadcast to the other node. stratisSender.FullNode.NodeService <WalletController>().SendTransaction(new SendTransactionRequest(transaction.ToHex())); // Wait for the trx's to arrive. TestHelper.WaitLoop(() => stratisReceiver.CreateRPCClient().GetRawMempool().Length > 0); TestHelper.WaitLoop(() => stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").Any()); Assert.Equal(Money.COIN * 50, stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").Sum(s => s.Transaction.Amount)); Assert.Null(stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").First().Transaction.BlockHeight); // Generate new blocks so the trx is confirmed. stratisSender.GenerateStratisWithMiner(1); // Wait for block repo for block sync to work. TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(stratisSender)); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisSender)); // Confirm trx's have been committed to the block. Assert.Equal(maturity + 52, stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").First().Transaction.BlockHeight); } }
public void CanRegisterSingleValueIndexFromIndexStoreSettings() { using (NodeBuilder builder = NodeBuilder.Create()) { var node = builder.CreateStratisPowNode(false, fullNodeBuilder => { fullNodeBuilder .UseConsensus() .UseIndexStore(settings => { settings.RegisterIndex("Output", "(t,b,n) => t.Inputs.Select((i, N) => new object[] { new object[] { i.PrevOut.Hash, i.PrevOut.N }, t.GetHash() })", false); }) .AddRPC(); }); builder.StartAll(); // Transaction has outputs var block = new Block(); var trans = new Transaction(); Key key = new Key(); // generate a random private key var scriptPubKeyOut = PayToPubkeyHashTemplate.Instance.GenerateScriptPubKey(key.PubKey); trans.Outputs.Add(new TxOut(100, scriptPubKeyOut)); block.Transactions.Add(trans); var hash = trans.GetHash().ToBytes(); // Transaction has inputs (i.PrevOut) var block2 = new Block(); block2.Header.HashPrevBlock = block.GetHash(); var trans2 = new Transaction(); trans2.Inputs.Add(new TxIn(new OutPoint(trans, 0))); block2.Transactions.Add(trans2); var hash2 = trans2.GetHash().ToBytes(); var repository = node.FullNode.NodeService <IIndexRepository>() as IndexRepository; repository.PutAsync(block.GetHash(), new List <Block> { block, block2 }).GetAwaiter().GetResult(); var indexTable = repository.Indexes["Output"].Table; var expectedJSON = repository.Indexes["Output"].ToString(); repository.Dispose(); using (var engine = new DBreezeEngine(node.FullNode.DataFolder.IndexPath)) { var transaction = engine.GetTransaction(); var indexKeyRow = transaction.Select <string, string>("Common", indexTable); Assert.True(indexKeyRow.Exists && indexKeyRow.Value != null); Assert.Equal(expectedJSON, indexKeyRow.Value); // Block 2 has been indexed? var indexKey = new byte[hash.Length + 4]; hash.CopyTo(indexKey, 0); var IndexedRow = transaction.Select <byte[], byte[]>(indexTable, indexKey); Assert.True(IndexedRow.Exists); // Correct value indexed? var compare = new byte[32]; hash2.CopyTo(compare, 0); Assert.Equal(compare, IndexedRow.Value); } } }
public void PullerVsMinerRaceCondition() { // Temporary fix so the Network static initialize will not break. var m = Network.Main; Transaction.TimeStamp = true; Block.BlockSignature = true; try { using (NodeBuilder builder = NodeBuilder.Create()) { // This represents local node. var stratisMinerLocal = builder.CreateStratisPosNode(); // This represents remote, which blocks are received by local node using its puller. var stratisMinerRemote = builder.CreateStratisPosNode(); builder.StartAll(); stratisMinerLocal.NotInIBD(); stratisMinerRemote.NotInIBD(); stratisMinerLocal.SetDummyMinerSecret(new BitcoinSecret(new Key(), stratisMinerLocal.FullNode.Network)); stratisMinerRemote.SetDummyMinerSecret(new BitcoinSecret(new Key(), stratisMinerRemote.FullNode.Network)); // Let's mine block Ap and Bp. stratisMinerRemote.GenerateStratisWithMiner(2); // Wait for block repository for block sync to work. TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(stratisMinerRemote)); stratisMinerLocal.CreateRPCClient().AddNode(stratisMinerRemote.Endpoint, true); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisMinerLocal, stratisMinerRemote)); // Now disconnect the peers and mine block C2p on remote. stratisMinerLocal.CreateRPCClient().RemoveNode(stratisMinerRemote.Endpoint); // Mine block C2p. stratisMinerRemote.GenerateStratisWithMiner(1); Thread.Sleep(2000); // Now reconnect nodes and mine block C1s before C2p arrives. stratisMinerLocal.CreateRPCClient().AddNode(stratisMinerRemote.Endpoint, true); stratisMinerLocal.GenerateStratisWithMiner(1); // Mine block Dp. uint256 dpHash = stratisMinerRemote.GenerateStratisWithMiner(1)[0]; // Now we wait until the local node's chain tip has correct hash of Dp. TestHelper.WaitLoop(() => stratisMinerLocal.FullNode.Chain.Tip.HashBlock.Equals(dpHash)); // Then give it time to receive the block from the puller. Thread.Sleep(2500); // Check that local node accepted the Dp as consensus tip. Assert.Equal(stratisMinerLocal.FullNode.ChainBehaviorState.ConsensusTip.HashBlock, dpHash); } } finally { Transaction.TimeStamp = false; Block.BlockSignature = false; } }
public async Task ReorgChain_AfterInitialRewind_ChainA_Extension_MinerC_DisconnectsAsync() { using (var builder = NodeBuilder.Create(this)) { var network = new BitcoinRegTest(); var minerA = builder.CreateStratisPowNode(network).WithDummyWallet().WithReadyBlockchainData(ReadyBlockchain.BitcoinRegTest10Miner); var minerB = builder.CreateStratisPowNode(network).NoValidation().WithDummyWallet(); var syncer = builder.CreateStratisPowNode(network).WithDummyWallet(); bool minerADisconnectedFromSyncer = false; // Configure the interceptor to disconnect a node after a certain block has been disconnected (rewound). void interceptor(ChainedHeaderBlock chainedHeaderBlock) { if (minerADisconnectedFromSyncer) { return; } if (chainedHeaderBlock.ChainedHeader.Previous.Height == 10) { // Ensure that minerA's tip has rewound to 10. TestHelper.WaitLoop(() => TestHelper.IsNodeSyncedAtHeight(minerA, 10)); TestHelper.Disconnect(minerA, syncer); minerADisconnectedFromSyncer = true; return; } } // Start the nodes. minerA.Start(); minerB.Start(); syncer.Start(); minerA.SetDisconnectInterceptor(interceptor); // minerB and syncer syncs with minerA. TestHelper.ConnectAndSync(minerA, minerB, syncer); // Disconnect minerB from miner so that it can mine on its own and create a fork. TestHelper.Disconnect(minerA, minerB); // MinerA continues to mine to height 14. TestHelper.MineBlocks(minerA, 4); TestHelper.WaitLoop(() => minerA.FullNode.ConsensusManager().Tip.Height == 14); TestHelper.WaitLoop(() => minerB.FullNode.ConsensusManager().Tip.Height == 10); TestHelper.WaitLoop(() => syncer.FullNode.ConsensusManager().Tip.Height == 14); // minerB mines 5 more blocks: // Block 6,7,9,10 = valid // Block 8 = invalid Assert.False(TestHelper.IsNodeConnected(minerB)); await TestHelper.BuildBlocks.OnNode(minerB).Amount(5).Invalid(13, (node, block) => BlockBuilder.InvalidCoinbaseReward(node, block)).BuildAsync(); // Reconnect minerA to minerB. TestHelper.ConnectNoCheck(minerA, minerB); // minerB should be disconnected from minerA. TestHelper.WaitLoop(() => !TestHelper.IsNodeConnectedTo(minerA, minerB)); // syncer should be disconnected from minerA (via interceptor). TestHelper.WaitLoop(() => !TestHelper.IsNodeConnectedTo(minerA, syncer)); // The reorg will fail at block 8 and roll back any changes. TestHelper.WaitLoop(() => TestHelper.IsNodeSyncedAtHeight(minerA, 14)); TestHelper.WaitLoop(() => TestHelper.IsNodeSyncedAtHeight(minerB, 15)); TestHelper.WaitLoop(() => TestHelper.IsNodeSyncedAtHeight(syncer, 14)); } }
public void CheckSegwitP2PSerialisationForNonWitnessNode() { using (NodeBuilder builder = NodeBuilder.Create(this)) { // We have to name the networks differently because the NBitcoin network registration won't allow two identical networks to coexist otherwise. var network = new StraxRegTest(); network.SetPrivatePropertyValue("Name", "StraxRegTestWithDeployments"); Assert.NotNull(network.Consensus.BIP9Deployments[2]); var networkNoBIP9 = new StraxRegTest(); networkNoBIP9.SetPrivatePropertyValue("Name", "StraxRegTestWithoutDeployments"); Assert.NotNull(networkNoBIP9.Consensus.BIP9Deployments[2]); // Remove BIP9 deployments (i.e. segwit). for (int i = 0; i < networkNoBIP9.Consensus.BIP9Deployments.Length; i++) { networkNoBIP9.Consensus.BIP9Deployments[i] = null; } // Ensure the workaround had the desired effect. Assert.Null(networkNoBIP9.Consensus.BIP9Deployments[2]); Assert.NotNull(network.Consensus.BIP9Deployments[2]); // Explicitly use new & separate instances of StratisRegTest because we modified the BIP9 deployments on one instance. CoreNode node = builder.CreateStratisPosNode(network).Start(); CoreNode listener = builder.CreateStratisPosNode(networkNoBIP9).Start(); // Sanity check. Assert.Null(listener.FullNode.Network.Consensus.BIP9Deployments[2]); Assert.NotNull(node.FullNode.Network.Consensus.BIP9Deployments[2]); // By disabling Segwit on the listener node we also prevent the WitnessCommitments rule from rejecting the mining node's blocks once we modify the listener's peer services. IConnectionManager listenerConnMan = listener.FullNode.NodeService <IConnectionManager>(); listenerConnMan.Parameters.TemplateBehaviors.Add(new TestBehavior()); // Override the listener node's default settings, so that it will not ask for witness data in P2P messages. listenerConnMan.Parameters.Services &= ~NetworkPeerServices.NODE_WITNESS; TestHelper.Connect(listener, node); // Mine a Segwit block on the first node. It should have commitment data as its settings have not been modified. var script = new Key().PubKey.WitHash.ScriptPubKey; var miner = node.FullNode.NodeService <IPowMining>() as PowMining; List <uint256> res = miner.GenerateBlocks(new ReserveScript(script), 1, int.MaxValue); Block block = node.FullNode.ChainIndexer.GetHeader(res.First()).Block; Script commitment = WitnessCommitmentsRule.GetWitnessCommitment(node.FullNode.Network, block); Assert.NotNull(commitment); // The listener should sync the mined block without validation failures. var cancellationToken = new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token; TestBase.WaitLoop(() => listener.CreateRPCClient().GetBlockCount() >= 1, cancellationToken: cancellationToken); // We need to capture a message on the non-witness-enabled destination node and see that it contains a block serialised without witness data. INetworkPeer connectedPeer = listenerConnMan.ConnectedPeers.FindByEndpoint(node.Endpoint); TestBehavior testBehavior = connectedPeer.Behavior <TestBehavior>(); var blockMessages = testBehavior.receivedMessageTracker["block"]; var blockReceived = blockMessages.First(); var receivedBlock = blockReceived.Message.Payload as BlockPayload; var parsedBlock = receivedBlock.Obj; // The block mined on the mining node (witness) should be bigger than the one received by the listener (no witness). Assert.True(block.GetSerializedSize() > parsedBlock.GetSerializedSize()); // Reserialise the received block without witness data (this should have no effect on its size). var nonWitnessBlock = parsedBlock.WithOptions(listener.FullNode.Network.Consensus.ConsensusFactory, TransactionOptions.None); // We received a block without witness data in the first place. Assert.True(parsedBlock.GetSerializedSize() == nonWitnessBlock.GetSerializedSize()); } }
public void ReorgChain_AfterInitialRewind_ChainA_Extension_MinerC_Disconnects() { using (var builder = NodeBuilder.Create(this)) { var minerA = builder.CreateStratisPosNode(new StratisRegTest()).OverrideDateTimeProvider().WithDummyWallet(); var minerB = builder.CreateStratisPosNode(new StratisRegTest()).OverrideDateTimeProvider().NoValidation().WithDummyWallet(); var minerC = builder.CreateStratisPosNode(new StratisRegTest()).OverrideDateTimeProvider().WithDummyWallet(); // Configure the interceptor to disconnect a node after a certain block has been disconnected (rewound). bool interceptor(ChainedHeaderBlock chainedHeaderBlock) { if (chainedHeaderBlock.ChainedHeader.Previous.Height == 5) { // Ensure that minerA's tip has rewound to 5. TestHelper.WaitLoop(() => TestHelper.IsNodeSyncedAtHeight(minerA, 5)); TestHelper.Disconnect(minerA, minerC); return(true); } return(false); } minerA.BlockDisconnectInterceptor(interceptor); // Start the nodes. minerA.Start(); minerB.Start(); minerC.Start(); // MinerA mines 5 blocks. TestHelper.MineBlocks(minerA, 5); // MinerB and MinerC syncs with MinerA. TestHelper.ConnectAndSync(minerA, minerB, minerC); // Disconnect MinerB from MinerA so that is can mine on its own and create a fork. TestHelper.Disconnect(minerA, minerB); // MinerA continues to mine to height 9. TestHelper.MineBlocks(minerA, 4); TestHelper.WaitLoop(() => minerA.FullNode.ConsensusManager().Tip.Height == 9); TestHelper.WaitLoop(() => minerB.FullNode.ConsensusManager().Tip.Height == 5); TestHelper.WaitLoop(() => minerC.FullNode.ConsensusManager().Tip.Height == 9); // MinerB mines 5 more blocks: // Block 6,7,9,10 = valid // Block 8 = invalid Assert.False(TestHelper.IsNodeConnected(minerB)); TestHelper.BuildBlocks.OnNode(minerB).Amount(5).Invalid(8, (node, block) => BlockBuilder.InvalidCoinbaseReward(node, block)).BuildAsync(); // Reconnect MinerA to MinerB. TestHelper.Connect(minerA, minerB); // MinerC should be disconnected from MinerA. TestHelper.WaitLoop(() => !TestHelper.IsNodeConnectedTo(minerA, minerC)); // This will cause the reorg chain to fail at block 8 and roll back any changes. TestHelper.WaitLoop(() => TestHelper.IsNodeSyncedAtHeight(minerA, 9)); TestHelper.WaitLoop(() => TestHelper.IsNodeSyncedAtHeight(minerC, 9)); } }
public void Given_NodesAreSynced_When_ABigReorgHappens_Then_TheReorgIsIgnored() { using (NodeBuilder builder = NodeBuilder.Create(this)) { CoreNode stratisMiner = builder.CreateStratisPosNode(this.posNetwork); CoreNode stratisSyncer = builder.CreateStratisPosNode(this.posNetwork); CoreNode stratisReorg = builder.CreateStratisPosNode(this.posNetwork); builder.StartAll(); stratisMiner.NotInIBD().WithWallet(); stratisSyncer.NotInIBD().WithWallet(); stratisReorg.NotInIBD().WithWallet(); // TODO: set the max allowed reorg threshold here // assume a reorg of 10 blocks is not allowed. stratisMiner.FullNode.ChainBehaviorState.MaxReorgLength = 10; stratisSyncer.FullNode.ChainBehaviorState.MaxReorgLength = 10; stratisReorg.FullNode.ChainBehaviorState.MaxReorgLength = 10; stratisMiner.SetDummyMinerSecret(new BitcoinSecret(new Key(), stratisMiner.FullNode.Network)); stratisReorg.SetDummyMinerSecret(new BitcoinSecret(new Key(), stratisReorg.FullNode.Network)); stratisMiner.GenerateStratisWithMiner(1); // wait for block repo for block sync to work TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(stratisMiner)); stratisMiner.CreateRPCClient().AddNode(stratisReorg.Endpoint, true); stratisMiner.CreateRPCClient().AddNode(stratisSyncer.Endpoint, true); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisMiner, stratisSyncer)); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisMiner, stratisReorg)); // create a reorg by mining on two different chains // ================================================ stratisMiner.CreateRPCClient().RemoveNode(stratisReorg.Endpoint); stratisSyncer.CreateRPCClient().RemoveNode(stratisReorg.Endpoint); TestHelper.WaitLoop(() => !TestHelper.IsNodeConnected(stratisReorg)); Task <List <uint256> > t1 = Task.Run(() => stratisMiner.GenerateStratisWithMiner(11)); Task <List <uint256> > t2 = Task.Delay(1000).ContinueWith(t => stratisReorg.GenerateStratisWithMiner(12)); Task.WaitAll(t1, t2); TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(stratisMiner)); TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(stratisReorg)); // make sure the nodes are actually on different chains. Assert.NotEqual(stratisMiner.FullNode.Chain.GetBlock(2).HashBlock, stratisReorg.FullNode.Chain.GetBlock(2).HashBlock); TestHelper.TriggerSync(stratisSyncer); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisMiner, stratisSyncer)); // The hash before the reorg node is connected. uint256 hashBeforeReorg = stratisMiner.FullNode.Chain.Tip.HashBlock; // connect the reorg chain stratisMiner.CreateRPCClient().AddNode(stratisReorg.Endpoint, true); stratisSyncer.CreateRPCClient().AddNode(stratisReorg.Endpoint, true); // trigger nodes to sync TestHelper.TriggerSync(stratisMiner); TestHelper.TriggerSync(stratisReorg); TestHelper.TriggerSync(stratisSyncer); // wait for the synced chain to get headers updated. TestHelper.WaitLoop(() => !stratisReorg.FullNode.ConnectionManager.ConnectedPeers.Any()); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisMiner, stratisSyncer)); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReorg, stratisMiner) == false); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReorg, stratisSyncer) == false); // check that a reorg did not happen. Assert.Equal(hashBeforeReorg, stratisSyncer.FullNode.Chain.Tip.HashBlock); } }
public void TxMempoolBlockDoublespend() { using (NodeBuilder builder = NodeBuilder.Create(this)) { var stratisNodeSync = builder.CreateStratisPowNode(); builder.StartAll(); stratisNodeSync.NotInIBD(); stratisNodeSync.FullNode.Settings.RequireStandard = true; // make sure to test standard tx stratisNodeSync.SetDummyMinerSecret(new BitcoinSecret(new Key(), stratisNodeSync.FullNode.Network)); stratisNodeSync.GenerateStratisWithMiner(100); // coinbase maturity = 100 TestHelper.WaitLoop(() => stratisNodeSync.FullNode.ConsensusLoop().Tip.HashBlock == stratisNodeSync.FullNode.Chain.Tip.HashBlock); TestHelper.WaitLoop(() => stratisNodeSync.FullNode.HighestPersistedBlock().HashBlock == stratisNodeSync.FullNode.Chain.Tip.HashBlock); // 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. var scriptPubKey = PayToPubkeyHashTemplate.Instance.GenerateScriptPubKey(stratisNodeSync.MinerSecret.PubKey); var genBlock = stratisNodeSync.FullNode.BlockStoreManager().BlockRepository.GetAsync(stratisNodeSync.FullNode.Chain.GetBlock(1).HashBlock).Result; // Create a double-spend of mature coinbase txn: List <Transaction> spends = new List <Transaction>(2); foreach (var index in Enumerable.Range(1, 2)) { var trx = stratisNodeSync.FullNode.Network.Consensus.ConsensusFactory.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 block = stratisNodeSync.GenerateStratis(1, spends).Single(); TestHelper.WaitLoop(() => stratisNodeSync.FullNode.ConsensusLoop().Tip.HashBlock == stratisNodeSync.FullNode.Chain.Tip.HashBlock); Assert.True(stratisNodeSync.FullNode.Chain.Tip.HashBlock != block.GetHash()); // Test 2: ... and should be rejected if spend1 is in the memory pool Assert.True(stratisNodeSync.AddToStratisMempool(spends[0])); block = stratisNodeSync.GenerateStratis(1, spends).Single(); TestHelper.WaitLoop(() => stratisNodeSync.FullNode.ConsensusLoop().Tip.HashBlock == stratisNodeSync.FullNode.Chain.Tip.HashBlock); Assert.True(stratisNodeSync.FullNode.Chain.Tip.HashBlock != block.GetHash()); stratisNodeSync.FullNode.MempoolManager().Clear().Wait(); // Test 3: ... and should be rejected if spend2 is in the memory pool Assert.True(stratisNodeSync.AddToStratisMempool(spends[1])); block = stratisNodeSync.GenerateStratis(1, spends).Single(); TestHelper.WaitLoop(() => stratisNodeSync.FullNode.ConsensusLoop().Tip.HashBlock == stratisNodeSync.FullNode.Chain.Tip.HashBlock); Assert.True(stratisNodeSync.FullNode.Chain.Tip.HashBlock != block.GetHash()); stratisNodeSync.FullNode.MempoolManager().Clear().Wait(); // Final sanity test: first spend in mempool, second in block, that's OK: List <Transaction> oneSpend = new List <Transaction>(); oneSpend.Add(spends[0]); Assert.True(stratisNodeSync.AddToStratisMempool(spends[1])); block = stratisNodeSync.GenerateStratis(1, oneSpend).Single(); TestHelper.WaitLoop(() => stratisNodeSync.FullNode.ConsensusLoop().Tip.HashBlock == stratisNodeSync.FullNode.Chain.Tip.HashBlock); Assert.True(stratisNodeSync.FullNode.Chain.Tip.HashBlock == block.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); } }
public void WalletCanReceiveAndSendCorrectly() { using (NodeBuilder builder = NodeBuilder.Create()) { var stratisSender = builder.CreateStratisPowNode(); var stratisReceiver = builder.CreateStratisPowNode(); builder.StartAll(); stratisSender.NotInIBD(); stratisReceiver.NotInIBD(); // get a key from the wallet var mnemonic1 = stratisSender.FullNode.WalletManager().CreateWallet("123456", "mywallet"); var mnemonic2 = stratisReceiver.FullNode.WalletManager().CreateWallet("123456", "mywallet"); Assert.Equal(12, mnemonic1.Words.Length); Assert.Equal(12, mnemonic2.Words.Length); var addr = stratisSender.FullNode.WalletManager().GetUnusedAddress(new WalletAccountReference("mywallet", "account 0")); var wallet = stratisSender.FullNode.WalletManager().GetWalletByName("mywallet"); var key = wallet.GetExtendedPrivateKeyForAddress("123456", addr).PrivateKey; stratisSender.SetDummyMinerSecret(new BitcoinSecret(key, stratisSender.FullNode.Network)); var maturity = (int)stratisSender.FullNode.Network.Consensus.Option <PowConsensusOptions>().CoinbaseMaturity; stratisSender.GenerateStratis(maturity + 5); // wait for block repo for block sync to work TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(stratisSender)); // the mining should add coins to the wallet var total = stratisSender.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").Sum(s => s.Transaction.Amount); Assert.Equal(Money.COIN * 105 * 50, total); // sync both nodes stratisSender.CreateRPCClient().AddNode(stratisReceiver.Endpoint, true); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisSender)); // send coins to the receiver var sendto = stratisReceiver.FullNode.WalletManager().GetUnusedAddress(new WalletAccountReference("mywallet", "account 0")); var trx = stratisSender.FullNode.WalletTransactionHandler().BuildTransaction(CreateContext( new WalletAccountReference("mywallet", "account 0"), "123456", sendto.ScriptPubKey, Money.COIN * 100, FeeType.Medium, 101)); // broadcast to the other node stratisSender.FullNode.NodeService <WalletController>().SendTransaction(new SendTransactionRequest(trx.ToHex())); // wait for the trx to arrive TestHelper.WaitLoop(() => stratisReceiver.CreateRPCClient().GetRawMempool().Length > 0); TestHelper.WaitLoop(() => stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").Any()); var receivetotal = stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").Sum(s => s.Transaction.Amount); Assert.Equal(Money.COIN * 100, receivetotal); Assert.Null(stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").First().Transaction.BlockHeight); // generate two new blocks do the trx is confirmed stratisSender.GenerateStratis(1, new List <Transaction>(new[] { trx.Clone() })); stratisSender.GenerateStratis(1); // wait for block repo for block sync to work TestHelper.WaitLoop(() => TestHelper.IsNodeSynced(stratisSender)); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisSender)); TestHelper.WaitLoop(() => maturity + 6 == stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").First().Transaction.BlockHeight); } }
public void ConsensusManager_Fork_Occurs_Node_Gets_Disconnected_Due_To_InvalidStakeDepth() { using (NodeBuilder builder = NodeBuilder.Create(this)) { var minerA = builder.CreateStratisPosNode(this.network); var minerB = builder.CreateStratisPosNode(this.network); var syncer = builder.CreateStratisPosNode(this.network); builder.StartAll(); minerA.NotInIBD().WithWallet(); minerB.NotInIBD(); syncer.NotInIBD(); // MinerA mines to height 15. TestHelper.MineBlocks(minerA, 15); // Sync the network to height 15. TestHelper.Connect(syncer, minerA); TestHelper.Connect(syncer, minerB); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(syncer, minerA)); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(syncer, minerB)); // Disconnect Miner A and B. TestHelper.Disconnect(syncer, minerA); TestHelper.Disconnect(syncer, minerB); // Ensure syncer does not have any connections. TestHelper.WaitLoop(() => !TestHelper.IsNodeConnected(syncer)); // Miner A stakes a coin that increases the network height to 16. var minter = minerA.FullNode.NodeService <IPosMinting>(); minter.Stake(new WalletSecret() { WalletName = "mywallet", WalletPassword = "******" }); TestHelper.WaitLoop(() => { return(minerA.FullNode.ConsensusManager().Tip.Height == 16); }); minter.StopStake(); // Update the network consensus options so that the GetStakeMinConfirmations returns a higher value // to ensure that the InvalidStakeDepth exception can be thrown. minerA.FullNode.Network.Consensus.Options = new ConsensusOptionsTest(); minerB.FullNode.Network.Consensus.Options = new ConsensusOptionsTest(); syncer.FullNode.Network.Consensus.Options = new ConsensusOptionsTest(); // Syncer now connects to both miners causing a InvalidStakeDepth exception to be thrown // on Miner A. TestHelper.Connect(syncer, minerA); TestHelper.Connect(syncer, minerB); // Ensure that Syncer is synced with MinerB. TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(syncer, minerB)); // Ensure that Syncer is not connected to MinerA. TestHelper.WaitLoop(() => !TestHelper.IsNodeConnectedTo(syncer, minerA)); Assert.True(syncer.FullNode.ConsensusManager().Tip.Height == 15); Assert.True(minerB.FullNode.ConsensusManager().Tip.Height == 15); } }