public void Miner_PosNetwork_CreatePowTransaction_AheadOfFutureDrift_ShouldNotBeIncludedInBlock() { var network = KnownNetworks.StratisRegTest; using (NodeBuilder builder = NodeBuilder.Create(this)) { CoreNode stratisMiner = builder.CreateStratisPosNode(network).WithWallet().Start(); int maturity = (int)network.Consensus.CoinbaseMaturity; TestHelper.MineBlocks(stratisMiner, maturity + 5); // Send coins to the receiver var context = WalletTests.CreateContext(network, new WalletAccountReference(WalletName, Account), Password, new Key().PubKey.GetAddress(network).ScriptPubKey, Money.COIN * 100, FeeType.Medium, 1); Transaction trx = stratisMiner.FullNode.WalletTransactionHandler().BuildTransaction(context); // This should make the mempool reject a POS trx. trx.Time = Utils.DateTimeToUnixTime(Utils.UnixTimeToDateTime(trx.Time).AddMinutes(5)); // Sign trx again after changing the time property. trx = context.TransactionBuilder.SignTransaction(trx); var broadcaster = stratisMiner.FullNode.NodeService <IBroadcasterManager>(); broadcaster.BroadcastTransactionAsync(trx).GetAwaiter().GetResult(); var entry = broadcaster.GetTransaction(trx.GetHash()); Assert.Equal(State.ToBroadcast, entry.State); Assert.NotNull(stratisMiner.FullNode.MempoolManager().GetTransaction(trx.GetHash()).Result); TestHelper.MineBlocks(stratisMiner, 1); Assert.NotNull(stratisMiner.FullNode.MempoolManager().GetTransaction(trx.GetHash()).Result); } }
public void LegacyNodesConnectsToProvenHeaderEnabledNode_AndLastOneIsDisconnectedToReserveSlot() { using (NodeBuilder builder = NodeBuilder.Create(this).WithLogsEnabled()) { // Create separate network parameters for this test. CoreNode phEnabledNode = this.CreateNode(builder, "ph-enabled", ProtocolVersion.PROVEN_HEADER_VERSION, new NodeConfigParameters { { "maxoutboundconnections", "3" } }).Start(); CoreNode legacyNode1 = this.CreateNode(builder, "legacy1", ProtocolVersion.ALT_PROTOCOL_VERSION).Start(); CoreNode legacyNode2 = this.CreateNode(builder, "legacy2", ProtocolVersion.ALT_PROTOCOL_VERSION).Start(); CoreNode legacyNode3 = this.CreateNode(builder, "legacy3", ProtocolVersion.ALT_PROTOCOL_VERSION).Start(); TestHelper.Connect(phEnabledNode, legacyNode1); TestHelper.Connect(phEnabledNode, legacyNode2); TestHelper.Connect(phEnabledNode, legacyNode3); // TODO: ProvenHeadersReservedSlotsBehavior kicks in only during peer discovery, so it doesn't trigger when we have an inbound connection or // when we are using addnode/connect. // We need to configure a peers.json file or mock the PeerDiscovery to let phEnabledNode try to connect to legacyNode1, legacyNode2 and legacyNode3 // With a maxoutboundconnections = 3, we expect the 3rd peer being disconnected to reserve a slot for a ph enabled node. // Assert.Equal(phEnabledNode.FullNode.ConnectionManager.ConnectedPeers.Count() == 2); } }
public void CanOverrideAllPorts() { var extraParams = new NodeConfigParameters { { "port", "123" }, { "rpcport", "456" }, { "apiport", "567" } }; using (var nodeBuilder = NodeBuilder.Create(this)) { var buildAction = new Action <IFullNodeBuilder>(builder => builder.UseBlockStore() .UsePowConsensus() .UseMempool() .AddMining() .UseWallet() .AddRPC() .UseApi() .MockIBD()); var coreNode = nodeBuilder.CreateCustomNode(buildAction, this.network, ProtocolVersion.PROVEN_HEADER_VERSION, configParameters: extraParams); coreNode.Start(); coreNode.ApiPort.Should().Be(567); coreNode.FullNode.NodeService <ApiSettings>().ApiPort.Should().Be(567); coreNode.RpcPort.Should().Be(456); coreNode.FullNode.NodeService <RpcSettings>().RPCPort.Should().Be(456); coreNode.ProtocolPort.Should().Be(123); coreNode.FullNode.ConnectionManager.ConnectionSettings.ExternalEndpoint.Port.Should().Be(123); } }
public void NodesCanConnectToEachOthers() { using (NodeBuilder builder = NodeBuilder.Create(this)) { CoreNode node1 = builder.CreateStratisPowNode(this.powNetwork); CoreNode node2 = builder.CreateStratisPowNode(this.powNetwork); builder.StartAll(); node1.NotInIBD(); node2.NotInIBD(); Assert.Empty(node1.FullNode.ConnectionManager.ConnectedPeers); Assert.Empty(node2.FullNode.ConnectionManager.ConnectedPeers); RPCClient rpc1 = node1.CreateRPCClient(); rpc1.AddNode(node2.Endpoint, true); Assert.Single(node1.FullNode.ConnectionManager.ConnectedPeers); Assert.Single(node2.FullNode.ConnectionManager.ConnectedPeers); var behavior = node1.FullNode.ConnectionManager.ConnectedPeers.First().Behaviors.Find <IConnectionManagerBehavior>(); Assert.False(behavior.Inbound); Assert.True(behavior.OneTry); behavior = node2.FullNode.ConnectionManager.ConnectedPeers.First().Behaviors.Find <IConnectionManagerBehavior>(); Assert.True(behavior.Inbound); Assert.False(behavior.OneTry); } }
public void CanHandleReorgs() { using (NodeBuilder builder = NodeBuilder.Create()) { var stratisNode = builder.CreateStratisNode(); var coreNode1 = builder.CreateNode(); var coreNode2 = builder.CreateNode(); builder.StartAll(); //Core1 discovers 10 blocks, sends to stratis var tip = coreNode1.FindBlock(10).Last(); stratisNode.CreateRPCClient().AddNode(coreNode1.Endpoint, true); Eventually(() => stratisNode.CreateRPCClient().GetBestBlockHash() == coreNode1.CreateRPCClient().GetBestBlockHash()); stratisNode.CreateRPCClient().RemoveNode(coreNode1.Endpoint); //Core2 discovers 20 blocks, sends to stratis tip = coreNode2.FindBlock(20).Last(); stratisNode.CreateRPCClient().AddNode(coreNode2.Endpoint, true); Eventually(() => stratisNode.CreateRPCClient().GetBestBlockHash() == coreNode2.CreateRPCClient().GetBestBlockHash()); stratisNode.CreateRPCClient().RemoveNode(coreNode2.Endpoint); ((CachedCoinView)stratisNode.FullNode.CoinView).FlushAsync().Wait(); //Core1 discovers 30 blocks, sends to stratis tip = coreNode1.FindBlock(30).Last(); stratisNode.CreateRPCClient().AddNode(coreNode1.Endpoint, true); Eventually(() => stratisNode.CreateRPCClient().GetBestBlockHash() == coreNode1.CreateRPCClient().GetBestBlockHash()); stratisNode.CreateRPCClient().RemoveNode(coreNode1.Endpoint); //Core2 discovers 50 blocks, sends to stratis tip = coreNode2.FindBlock(50).Last(); stratisNode.CreateRPCClient().AddNode(coreNode2.Endpoint, true); Eventually(() => stratisNode.CreateRPCClient().GetBestBlockHash() == coreNode2.CreateRPCClient().GetBestBlockHash()); stratisNode.CreateRPCClient().RemoveNode(coreNode2.Endpoint); ((CachedCoinView)stratisNode.FullNode.CoinView).FlushAsync().Wait(); } }
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()) { 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 = new Transaction(); 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 = new Transaction(); 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 = new Transaction(); 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 TxMempoolBlockDoublespend() { using (NodeBuilder builder = NodeBuilder.Create()) { 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.GenerateStratis(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 = new Transaction(); 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.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); } }
private void CleanFolder() { NodeBuilder.CleanupTestFolder(this.folder); }
public void ConsensusManager_Fork_Occurs_Node_Gets_Disconnected_Due_To_InvalidStakeDepth() { using (NodeBuilder builder = NodeBuilder.Create(this)) { var minerA = builder.CreateStratisPosNode(this.posNetwork); var minerB = builder.CreateStratisPosNode(this.posNetwork); var syncer = builder.CreateStratisPosNode(this.posNetwork); 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); } }
public ProofOfStakeSteps(string displayName) { this.nodeBuilder = NodeBuilder.Create(Path.Combine(this.GetType().Name, displayName)); }
public void Given__NodesAreSynced__When__ABigReorgHappens__Then__TheReorgIsIgnored() { // 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()) { 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 void MempoolSyncTransactions() { using (NodeBuilder builder = NodeBuilder.Create()) { 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 = new Transaction(); 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.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 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)); // [SENDTRANSACTION TODO] Conceptual changes had been introduced to tx sending // These tests don't make sense anymore // It must be either removed or refactored //// broadcast to the other node //stratisSender.FullNode.WalletManager().SendTransaction(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 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)); // [SENDTRANSACTION TODO] Conceptual changes had been introduced to tx sending // These tests don't make sense anymore // It must be either removed or refactored //// broadcast to the other node //stratisSender.FullNode.WalletManager().SendTransaction(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.WalletManager().SendTransaction(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.True(stratisReceiver.FullNode.WalletManager().GetSpendableTransactionsInWallet("mywallet").Any(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 //// ==================== //// TODO: Investigate when a reorg happens the store needs to put the rolled back transactions back to the mempool //stratisSender.FullNode.WalletManager().SendTransaction(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 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 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 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 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 MiningNodeWithOneConnectionAlwaysSynced() { string testFolderPath = Path.Combine(this.GetType().Name, nameof(MiningNodeWithOneConnectionAlwaysSynced)); using (NodeBuilder nodeBuilder = NodeBuilder.Create(testFolderPath)) { CoreNode minerNode = nodeBuilder.CreateStratisPowNode(this.powNetwork).NotInIBD(); minerNode.Start(); minerNode.WithWallet(); CoreNode connectorNode = nodeBuilder.CreateStratisPowNode(this.powNetwork).NotInIBD(); connectorNode.Start(); connectorNode.WithWallet(); CoreNode firstNode = nodeBuilder.CreateStratisPowNode(this.powNetwork).NotInIBD(); firstNode.Start(); firstNode.WithWallet(); CoreNode secondNode = nodeBuilder.CreateStratisPowNode(this.powNetwork); secondNode.Start(); secondNode.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 ReorgChain_AfterInitialRewind_ChainB_MinerB_Disconnects() { using (var builder = NodeBuilder.Create(this)) { var network = new BitcoinRegTest(); var minerA = builder.CreateStratisPowNode(network).WithDummyWallet(); var minerB = builder.CreateStratisPowNode(network).WithDummyWallet(); var syncer = builder.CreateStratisPowNode(network).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 == 5) { // Ensure that minerA's tips has rewound to 5. TestHelper.WaitLoop(() => TestHelper.IsNodeSyncedAtHeight(minerA, 5)); TestHelper.Disconnect(minerA, minerB); minerADisconnectedFromMinerB = true; return; } } minerA.SetDisconnectInterceptor(interceptor); // Start the nodes. minerA.Start(); minerB.Start(); syncer.Start(); // MinerA mines 5 blocks. TestHelper.MineBlocks(minerA, 5); // 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 9. TestHelper.MineBlocks(minerA, 4); TestHelper.WaitLoop(() => minerA.FullNode.ConsensusManager().Tip.Height == 9); TestHelper.WaitLoop(() => minerB.FullNode.ConsensusManager().Tip.Height == 5); TestHelper.WaitLoop(() => syncer.FullNode.ConsensusManager().Tip.Height == 9); // MinerB mines 5 more blocks so that a reorg is triggered. TestHelper.MineBlocks(minerB, 5); TestHelper.WaitLoop(() => TestHelper.IsNodeSyncedAtHeight(minerB, 10)); // MinerA and Syncer should have reorged to the longer chain. TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(minerA, minerB)); TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(syncer, minerB)); } }
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 void ReorgChain_AfterInitialRewind_ChainA_Extension_MinerC_Disconnects() { using (var builder = NodeBuilder.Create(this)) { var network = new BitcoinRegTest(); var minerA = builder.CreateStratisPowNode(network).WithDummyWallet(); var minerB = builder.CreateStratisPowNode(network).NoValidation().WithDummyWallet(); var syncer = builder.CreateStratisPowNode(network).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, syncer); return(true); } return(false); } minerA.BlockDisconnectInterceptor(interceptor); // Start the nodes. minerA.Start(); minerB.Start(); syncer.Start(); // MinerA mines 5 blocks. TestHelper.MineBlocks(minerA, 5); // 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 9. TestHelper.MineBlocks(minerA, 4); TestHelper.WaitLoop(() => minerA.FullNode.ConsensusManager().Tip.Height == 9); TestHelper.WaitLoop(() => minerB.FullNode.ConsensusManager().Tip.Height == 5); TestHelper.WaitLoop(() => syncer.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); // 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, 9)); TestHelper.WaitLoop(() => TestHelper.IsNodeSyncedAtHeight(minerB, 10)); TestHelper.WaitLoop(() => TestHelper.IsNodeSyncedAtHeight(syncer, 9)); } }
public async Task ReorgChain_AfterInitialRewind_ChainA_Extension_MinerC_DisconnectsAsync() { using (var builder = NodeBuilder.Create(this)) { var minerA = builder.CreateStratisPowNode(new BitcoinRegTest(), "cmi-1-minerA").WithDummyWallet(); var minerB = builder.CreateStratisPowNode(new BitcoinRegTestNoValidationRules(), "cmi-1-minerB").NoValidation().WithDummyWallet(); var syncer = builder.CreateStratisPowNode(new BitcoinRegTest(), "cmi-1-syncer").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. TestBase.WaitLoop(() => TestHelper.IsNodeSyncedAtHeight(minerA, 10)); TestHelper.Disconnect(minerA, syncer); minerADisconnectedFromSyncer = true; return; } } // 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 nodes. 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); 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: // 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. TestBase.WaitLoop(() => !TestHelper.IsNodeConnectedTo(minerA, minerB)); // syncer should be disconnected from minerA (via interceptor). TestBase.WaitLoop(() => !TestHelper.IsNodeConnectedTo(minerA, syncer)); // The reorg will fail at block 8 and roll back any changes. TestBase.WaitLoop(() => TestHelper.IsNodeSyncedAtHeight(minerA, 14)); TestBase.WaitLoop(() => TestHelper.IsNodeSyncedAtHeight(minerB, 15)); TestBase.WaitLoop(() => TestHelper.IsNodeSyncedAtHeight(syncer, 14)); } }
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(walletPassword, walletName, walletPassphrase); stratisSyncer.NotInIBD().WithWallet(walletPassword, walletName, walletPassphrase); stratisReorg.NotInIBD().WithWallet(walletPassword, walletName, walletPassphrase); TestHelper.MineBlocks(stratisMiner, walletName, walletPassword, walletAccount, 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)); TestHelper.MineBlocks(stratisMiner, walletName, walletPassword, walletAccount, 11); TestHelper.MineBlocks(stratisReorg, walletName, walletPassword, walletAccount, 12); 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); } }