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 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 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 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); } }
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 WalletCanReceiveAndSendCorrectly() { using (NodeBuilder builder = NodeBuilder.Create()) { var stratisSender = builder.CreateStratisNode(); var stratisReceiver = builder.CreateStratisNode(); 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>().COINBASE_MATURITY; 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.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 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 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.CreateStratisNode(); var stratisReceiver = builder.CreateStratisNode(); var stratisReorg = builder.CreateStratisNode(); 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>().COINBASE_MATURITY; 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.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 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 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 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); } } }