public void UnconfirmedTransactionsWithoutReceivedCoinsShouldNotShowUp() { BlockchainBuilder builder = new BlockchainBuilder(); Tracker tracker = new Tracker(); Key bob = new Key(); tracker.Add(bob); Key alice = new Key(); var tx1 = builder.GiveMoney(bob, Money.Coins(1.0m)); var b = builder.FindBlock(); tracker.NotifyTransaction(tx1, builder.Chain.Tip, b); var tx2 = builder.SpendCoin(tx1.Outputs.AsCoins().First(), alice, Money.Coins(0.1m)); b = builder.FindBlock(); tracker.NotifyTransaction(tx2, builder.Chain.Tip, b); var tx3 = builder.SpendCoin(tx2.Outputs.AsCoins().Skip(1).First(), alice, Money.Coins(0.2m)); Assert.True(tracker.NotifyTransaction(tx3)); var transactions = tracker.GetWalletTransactions(builder.Chain); Assert.True(transactions.Count == 3); Assert.True(transactions.Summary.UnConfirmed.TransactionCount == 1); Assert.True(transactions.Summary.UnConfirmed.Amount == -Money.Coins(0.2m)); Assert.True(transactions.Summary.Confirmed.TransactionCount == 2); Assert.True(transactions.Summary.Confirmed.Amount == Money.Coins(0.9m)); Assert.True(transactions.Summary.Spendable.TransactionCount == 3); Assert.True(transactions.Summary.Spendable.Amount == Money.Coins(0.7m)); builder.Chain.SetTip(builder.Chain.GetBlock(1)); transactions = tracker.GetWalletTransactions(builder.Chain); Action _ = () => { Assert.True(transactions.Count == 3); Assert.True(transactions.Summary.Confirmed.TransactionCount == 1); Assert.True(transactions.Summary.Confirmed.Amount == Money.Coins(1.0m)); Assert.True(transactions.Summary.Spendable.TransactionCount == 3); Assert.True(transactions.Summary.Spendable.Amount == Money.Coins(0.7m)); Assert.True(transactions.Summary.UnConfirmed.TransactionCount == 2); Assert.True(transactions.Summary.UnConfirmed.Amount == -Money.Coins(0.3m)); }; _(); tracker.NotifyTransaction(tx2); //Notifying tx2 should have no effect, since it already is accounted because it was orphaned _(); }
public void UseFilterAddIfKeyPoolSizeIsZero() { using (NodeServerTester servers = new NodeServerTester()) { var chainBuilder = new BlockchainBuilder(); chainBuilder.RealPoW = true; SetupSPVBehavior(servers, chainBuilder); var connected = CreateGroup(servers, 1); Wallet wallet = new Wallet(new WalletCreation() { Network = servers.Network, RootKeys = new[] { new ExtKey().Neuter() }, UseP2SH = true }, keyPoolSize: 0); Assert.True(wallet.State == WalletState.Created); wallet.Configure(connected); wallet.Connect(); Assert.True(wallet.State == WalletState.Disconnected); TestUtils.Eventually(() => connected.ConnectedNodes.Count == 1); Assert.True(wallet.State == WalletState.Connected); var script = wallet.GetNextScriptPubKey(new KeyPath("0/1/2")); Thread.Sleep(1000); chainBuilder.GiveMoney(script, Money.Coins(0.001m)); TestUtils.Eventually(() => wallet.GetTransactions().Count == 1); wallet.Disconnect(); TestUtils.Eventually(() => connected.ConnectedNodes.Count == 0); MemoryStream ms = new MemoryStream(); wallet.Save(ms); ms.Position = 0; var wallet2 = Wallet.Load(ms); wallet2.Configure(connected); wallet2.Connect(); var script2 = wallet2.GetNextScriptPubKey(new KeyPath("0/1/2")); Thread.Sleep(1000); Assert.NotEqual(script, script2); Assert.NotNull(wallet2.GetRedeemScript(script)); Assert.NotNull(wallet2.GetRedeemScript(script2)); TestUtils.Eventually(() => connected.ConnectedNodes.Count == 1); var spv = servers.Server1.ConnectedNodes.First().Behaviors.Find <SPVBehavior>(); TestUtils.Eventually(() => spv._Filter != null); chainBuilder.GiveMoney(script2, Money.Coins(0.001m)); TestUtils.Eventually(() => wallet.GetTransactions().Count == 2); chainBuilder.GiveMoney(script, Money.Coins(0.002m)); TestUtils.Eventually(() => wallet.GetTransactions().Count == 3); chainBuilder.FindBlock(); TestUtils.Eventually(() => wallet.GetTransactions().Count == 3 && wallet.GetTransactions().All(t => t.BlockInformation != null)); } }
public void CanTrackScriptCoins() { BlockchainBuilder builder = new BlockchainBuilder(); Tracker tracker = new Tracker(); Key bob = new Key(); tracker.Add(bob.PubKey, true); var tx1 = builder.GiveMoney(bob.PubKey.ScriptPubKey.Hash, Money.Coins(1.0m)); Assert.True(tracker.NotifyTransaction(tx1)); Assert.True(tracker.GetWalletTransactions(builder.Chain)[0].ReceivedCoins[0] is ScriptCoin); }
private static void SetupSPVBehavior(NodeServerTester servers, BlockchainBuilder chainBuilder) { foreach (var server in new[] { servers.Server1, servers.Server2 }) { server.InboundNodeConnectionParameters.Services = NodeServices.Network; //Simulate SPV compatible server server.InboundNodeConnectionParameters.TemplateBehaviors.Add(new ChainBehavior(chainBuilder.Chain) { AutoSync = false }); server.InboundNodeConnectionParameters.TemplateBehaviors.Add(new SPVBehavior(chainBuilder)); ///////////// } }
private static void SetupSPVBehavior(NodeServerTester servers, BlockchainBuilder chainBuilder) { List <Node> nodes = new List <Node>(); ConcurrentDictionary <uint256, Transaction> receivedTxs = new ConcurrentDictionary <uint256, Transaction>(); foreach (var server in new[] { servers.Server1, servers.Server2 }) { server.InboundNodeConnectionParameters.Services = NodeServices.Network | NodeServices.NODE_BLOOM; //Simulate SPV compatible server server.InboundNodeConnectionParameters.TemplateBehaviors.Add(new ChainBehavior(chainBuilder.Chain) { AutoSync = false }); server.InboundNodeConnectionParameters.TemplateBehaviors.Add(new SPVBehavior(chainBuilder) { Nodes = nodes, _ReceivedTransactions = receivedTxs }); ///////////// } }
public void CanSyncWallet2() { using(NodeServerTester servers = new NodeServerTester(Network.TestNet)) { var chainBuilder = new BlockchainBuilder(); SetupSPVBehavior(servers, chainBuilder); NodesGroup aliceConnection = CreateGroup(servers, 1); NodesGroup bobConnection = CreateGroup(servers, 1); var aliceKey = new ExtKey(); Wallet alice = new Wallet(new WalletCreation() { Network = Network.TestNet, RootKeys = new[] { aliceKey.Neuter() }, SignatureRequired = 1, UseP2SH = false }, 11); Wallet bob = new Wallet(new WalletCreation() { Network = Network.TestNet, RootKeys = new[] { new ExtKey().Neuter() }, SignatureRequired = 1, UseP2SH = false }, 11); alice.Configure(aliceConnection); alice.Connect(); bob.Configure(bobConnection); bob.Connect(); TestUtils.Eventually(() => aliceConnection.ConnectedNodes.Count == 1); //New address tracked var addressAlice = alice.GetNextScriptPubKey(); chainBuilder.GiveMoney(addressAlice, Money.Coins(1.0m)); TestUtils.Eventually(() => aliceConnection.ConnectedNodes.Count == 0); //Purge TestUtils.Eventually(() => aliceConnection.ConnectedNodes.Count == 1); //Reconnect ////// TestUtils.Eventually(() => alice.GetTransactions().Count == 1); //Alice send tx to bob var coins = alice.GetTransactions().GetSpendableCoins(); var keys = coins.Select(c => alice.GetKeyPath(c.ScriptPubKey)) .Select(k => aliceKey.Derive(k)) .ToArray(); var builder = new TransactionBuilder(); var tx = builder .SetTransactionPolicy(new Policy.StandardTransactionPolicy() { MinRelayTxFee = new FeeRate(0) }) .AddCoins(coins) .AddKeys(keys) .Send(bob.GetNextScriptPubKey(), Money.Coins(0.4m)) .SetChange(alice.GetNextScriptPubKey(true)) .BuildTransaction(true); Assert.True(builder.Verify(tx)); chainBuilder.BroadcastTransaction(tx); //Alice get change TestUtils.Eventually(() => alice.GetTransactions().Count == 2); coins = alice.GetTransactions().GetSpendableCoins(); Assert.True(coins.Single().Amount == Money.Coins(0.6m)); ////// //Bob get coins TestUtils.Eventually(() => bob.GetTransactions().Count == 1); coins = bob.GetTransactions().GetSpendableCoins(); Assert.True(coins.Single().Amount == Money.Coins(0.4m)); ////// MemoryStream bobWalletBackup = new MemoryStream(); bob.Save(bobWalletBackup); bobWalletBackup.Position = 0; MemoryStream bobTrakerBackup = new MemoryStream(); bob.Tracker.Save(bobTrakerBackup); bobTrakerBackup.Position = 0; bob.Disconnect(); //Restore bob bob = Wallet.Load(bobWalletBackup); bobConnection.NodeConnectionParameters.TemplateBehaviors.Remove<TrackerBehavior>(); bobConnection.NodeConnectionParameters.TemplateBehaviors.Add(new TrackerBehavior(Tracker.Load(bobTrakerBackup), chainBuilder.Chain)); ///// bob.Configure(bobConnection); //Bob still has coins TestUtils.Eventually(() => bob.GetTransactions().Count == 1); coins = bob.GetTransactions().GetSpendableCoins(); Assert.True(coins.Single().Amount == Money.Coins(0.4m)); ////// bob.Connect(); TestUtils.Eventually(() => bobConnection.ConnectedNodes.Count == 1); //New block found ! chainBuilder.FindBlock(); //Alice send tx to bob coins = alice.GetTransactions().GetSpendableCoins(); keys = coins.Select(c => alice.GetKeyPath(c.ScriptPubKey)) .Select(k => aliceKey.Derive(k)) .ToArray(); builder = new TransactionBuilder(); tx = builder .SetTransactionPolicy(new Policy.StandardTransactionPolicy() { MinRelayTxFee = new FeeRate(0) }) .AddCoins(coins) .AddKeys(keys) .Send(bob.GetNextScriptPubKey(), Money.Coins(0.1m)) .SetChange(alice.GetNextScriptPubKey(true)) .BuildTransaction(true); Assert.True(builder.Verify(tx)); chainBuilder.BroadcastTransaction(tx); //Bob still has coins TestUtils.Eventually(() => bob.GetTransactions().Count == 2); //Bob has both, old and new tx coins = bob.GetTransactions().GetSpendableCoins(); ////// } }
public void CanPrune() { BlockchainBuilder builder = new BlockchainBuilder(); Tracker tracker = new Tracker(); Key bob = new Key(); Key alice = new Key(); tracker.Add(bob); var oldUnconf = builder.GiveMoney(bob, Money.Coins(1.0m)); Assert.True(tracker.NotifyTransaction(oldUnconf)); builder.Mempool.Clear(); var oldConf = builder.GiveMoney(bob, Money.Coins(0.9m)); var oldConfSpent = builder.SpendCoin(oldConf.Outputs.AsCoins().First(), alice, Money.Coins(0.01m)); var block = builder.FindBlock(); Assert.True(tracker.NotifyTransaction(oldConf, builder.Chain.Tip, block)); Assert.True(tracker.NotifyTransaction(oldConfSpent, builder.Chain.Tip, block)); for(int i = 0 ; i < 9 ; i++) { builder.FindBlock(); } Assert.True(tracker.Prune(builder.Chain, 10).Count == 0); builder.FindBlock(); //Prune tracked outpoint var pruned = tracker.Prune(builder.Chain, 10); Assert.Equal(1, pruned.Count); Assert.True(pruned.First() is Tracker.TrackedOutpoint); //Prune old unconf pruned = tracker.Prune(builder.Chain, timeExpiration: TimeSpan.Zero); Assert.Equal(1, pruned.Count); var op = pruned.OfType<Tracker.Operation>().First(); Assert.True(op.BlockId == null); var conf = builder.GiveMoney(bob, Money.Coins(0.9m)); block = builder.FindBlock(); Assert.True(tracker.NotifyTransaction(conf, builder.Chain.Tip, block)); var oldSpentForked = builder.SpendCoin(conf.Outputs.AsCoins().First(), alice, Money.Coins(0.021m)); block = builder.FindBlock(); Assert.True(tracker.NotifyTransaction(oldSpentForked, builder.Chain.Tip, block)); var forked = builder.Chain.Tip; builder.Chain.SetTip(builder.Chain.Tip.Previous); for(int i = 0 ; i < 10 ; i++) { builder.FindBlock(); } pruned = tracker.Prune(builder.Chain, 10); Assert.True(pruned.Count == 1); //Tracked outpoint of conf Assert.True(pruned.First() is Tracker.TrackedOutpoint); block = builder.FindBlock(); pruned = tracker.Prune(builder.Chain, 10); //Old forked spent Assert.Equal(1, pruned.Count); op = pruned.OfType<Tracker.Operation>().First(); Assert.Equal(forked.HashBlock, op.BlockId); }
public void CanTrackKey() { BlockchainBuilder builder = new BlockchainBuilder(); Key bob = new Key(); Tracker tracker = new Tracker(); tracker.Add(bob); var tx1 = builder.GiveMoney(bob, Money.Coins(1.0m)); var coin = tx1.Outputs.AsCoins().First(); Assert.True(tracker.NotifyTransaction(tx1)); Thread.Sleep(10); Key alice = new Key(); var tx2 = builder.SpendCoin(coin, alice, Money.Coins(0.6m)); Assert.True(tracker.NotifyTransaction(tx2)); var block = builder.FindBlock(); foreach(var btx in block.Transactions) { if(!btx.IsCoinBase) { Assert.True(tracker.NotifyTransaction(btx, builder.Chain.GetBlock(block.GetHash()), block)); Assert.True(tracker.NotifyTransaction(btx, builder.Chain.GetBlock(block.GetHash()), block)); //Idempotent } } var transactions = tracker.GetWalletTransactions(builder.Chain); Assert.True(transactions.Count == 2); Assert.True(transactions[0].Transaction.GetHash() == tx2.GetHash()); Assert.True(transactions[1].Transaction.GetHash() == tx1.GetHash()); Assert.True(transactions[0].Balance == -Money.Coins(0.6m)); var tx3 = builder.GiveMoney(bob, Money.Coins(0.01m)); coin = tx3.Outputs.AsCoins().First(); block = builder.FindBlock(); Assert.True(tracker.NotifyTransaction(block.Transactions[1], builder.Chain.GetBlock(block.GetHash()), block)); transactions = tracker.GetWalletTransactions(builder.Chain); Assert.True(transactions.Count == 3); Assert.True(transactions.Summary.UnConfirmed.TransactionCount == 0); Assert.True(transactions[0].Transaction.GetHash() == block.Transactions[1].GetHash()); Assert.Equal(2, transactions.GetSpendableCoins().Count()); // the 1 change + 1 gift builder.Chain.SetTip(builder.Chain.Tip.Previous); transactions = tracker.GetWalletTransactions(builder.Chain); Assert.True(transactions.Count == 3); Assert.True(transactions.Summary.UnConfirmed.TransactionCount == 1); //Test roundtrip serialization var filterBefore = tracker.CreateBloomFilter(0.005); MemoryStream ms = new MemoryStream(); tracker.Save(ms); tracker = new Tracker(); ms.Position = 0; tracker = Tracker.Load(ms); transactions = tracker.GetWalletTransactions(builder.Chain); Assert.True(transactions.Count == 3); Assert.True(transactions.Summary.UnConfirmed.TransactionCount == 1); var filterAfter = tracker.CreateBloomFilter(0.005); Assert.True(filterBefore.ToBytes().SequenceEqual(filterAfter.ToBytes())); ///// }
private static void SetupSPVBehavior(NodeServerTester servers, BlockchainBuilder chainBuilder) { foreach(var server in new[] { servers.Server1, servers.Server2 }) { server.InboundNodeConnectionParameters.Services = NodeServices.Network; //Simulate SPV compatible server server.InboundNodeConnectionParameters.TemplateBehaviors.Add(new ChainBehavior(chainBuilder.Chain) { AutoSync = false }); server.InboundNodeConnectionParameters.TemplateBehaviors.Add(new SPVBehavior(chainBuilder)); ///////////// } }
public void UseFilterAddIfKeyPoolSizeIsZero() { using(NodeServerTester servers = new NodeServerTester(Network.TestNet)) { var chainBuilder = new BlockchainBuilder(); SetupSPVBehavior(servers, chainBuilder); var connected = CreateGroup(servers, 1); Wallet wallet = new Wallet(new WalletCreation() { Network = Network.TestNet, RootKeys = new[] { new ExtKey().Neuter() }, UseP2SH = true }, keyPoolSize: 0); Assert.True(wallet.State == WalletState.Created); wallet.Configure(connected); wallet.Connect(); Assert.True(wallet.State == WalletState.Disconnected); TestUtils.Eventually(() => connected.ConnectedNodes.Count == 1); Assert.True(wallet.State == WalletState.Connected); var script = wallet.GetNextScriptPubKey(new KeyPath("0/1/2")); Thread.Sleep(1000); chainBuilder.GiveMoney(script, Money.Coins(0.001m)); TestUtils.Eventually(() => wallet.GetTransactions().Count == 1); wallet.Disconnect(); TestUtils.Eventually(() => connected.ConnectedNodes.Count == 0); MemoryStream ms = new MemoryStream(); wallet.Save(ms); ms.Position = 0; var wallet2 = Wallet.Load(ms); wallet2.Configure(connected); wallet2.Connect(); var script2 = wallet2.GetNextScriptPubKey(new KeyPath("0/1/2")); Thread.Sleep(1000); Assert.NotEqual(script, script2); Assert.NotNull(wallet2.GetRedeemScript(script)); Assert.NotNull(wallet2.GetRedeemScript(script2)); TestUtils.Eventually(() => connected.ConnectedNodes.Count == 1); var spv = servers.Server1.ConnectedNodes.First().Behaviors.Find<SPVBehavior>(); TestUtils.Eventually(() => spv._Filter != null); chainBuilder.GiveMoney(script2, Money.Coins(0.001m)); TestUtils.Eventually(() => wallet.GetTransactions().Count == 2); chainBuilder.GiveMoney(script, Money.Coins(0.002m)); TestUtils.Eventually(() => wallet.GetTransactions().Count == 3); chainBuilder.FindBlock(); TestUtils.Eventually(() => wallet.GetTransactions().Count == 3 && wallet.GetTransactions().All(t => t.BlockInformation != null)); } }
public void CanBroadcastTransaction() { using (NodeServerTester servers = new NodeServerTester(Network.TestNet)) { var notifiedTransactions = new List <WalletTransaction>(); var chainBuilder = new BlockchainBuilder(); SetupSPVBehavior(servers, chainBuilder); var tx = new Transaction(); Wallet wallet = new Wallet(new WalletCreation() { Network = Network.TestNet, RootKeys = new[] { new ExtKey().Neuter() }, UseP2SH = false }, keyPoolSize: 11); NodesGroup connected = CreateGroup(servers, 2); wallet.Configure(connected); wallet.Connect(); AutoResetEvent evt = new AutoResetEvent(false); bool passed = false; bool rejected = false; var broadcasting = wallet.BroadcastTransactionAsync(tx); wallet.TransactionBroadcasted += (t) => { passed = true; evt.Set(); }; wallet.TransactionRejected += (t, r) => { rejected = true; evt.Set(); }; BroadcastHub hub = BroadcastHub.GetBroadcastHub(connected.NodeConnectionParameters); BroadcastHubBehavior behavior = null; while (behavior == null || behavior.AttachedNode.State != NodeState.HandShaked) { behavior = connected.ConnectedNodes.Select(n => n.Behaviors.Find <BroadcastHubBehavior>()).FirstOrDefault(); Thread.Sleep(1); } if (broadcasting.Status != TaskStatus.RanToCompletion) { Assert.Equal(1, behavior.Broadcasts.Count()); Assert.Equal(1, hub.BroadcastingTransactions.Count()); } Assert.True(evt.WaitOne(20000)); Assert.True(broadcasting.Status == TaskStatus.RanToCompletion); Assert.True(passed); evt.Reset(); Assert.Equal(0, behavior.Broadcasts.Count()); Assert.Equal(0, hub.BroadcastingTransactions.Count()); Assert.Null(broadcasting.Result); broadcasting = wallet.BroadcastTransactionAsync(tx); if (broadcasting.Status != TaskStatus.RanToCompletion) { Assert.Equal(1, behavior.Broadcasts.Count()); } Assert.True(evt.WaitOne(20000)); Assert.True(rejected); Assert.Equal(0, behavior.Broadcasts.Count()); Assert.Equal(0, hub.BroadcastingTransactions.Count()); Assert.NotNull(broadcasting.Result); } }
public void CanSyncWalletCore(WalletCreation creation) { using(NodeServerTester servers = new NodeServerTester(Network.TestNet)) { var chainBuilder = new BlockchainBuilder(); //Simulate SPV compatible server servers.Server1.InboundNodeConnectionParameters.Services = NodeServices.Network; servers.Server1.InboundNodeConnectionParameters.TemplateBehaviors.Add(new ChainBehavior(chainBuilder.Chain) { AutoSync = false }); servers.Server1.InboundNodeConnectionParameters.TemplateBehaviors.Add(new SPVBehavior(chainBuilder)); ///////////// //The SPV client does not verify the chain and keep one connection alive with Server1 NodeConnectionParameters parameters = new NodeConnectionParameters(); Wallet.ConfigureDefaultNodeConnectionParameters(parameters); parameters.IsTrusted = true; AddressManagerBehavior addrman = new AddressManagerBehavior(new AddressManager()); addrman.AddressManager.Add(new NetworkAddress(servers.Server1.ExternalEndpoint), IPAddress.Parse("127.0.0.1")); parameters.TemplateBehaviors.Add(addrman); NodesGroup connected = new NodesGroup(Network.TestNet, parameters); connected.AllowSameGroup = true; connected.MaximumNodeConnection = 1; ///////////// Wallet wallet = new Wallet(creation, keyPoolSize: 11); Assert.True(wallet.State == WalletState.Created); wallet.Connect(connected); Assert.True(wallet.State == WalletState.Disconnected); TestUtils.Eventually(() => connected.ConnectedNodes.Count == 1); Assert.True(wallet.State == WalletState.Connected); chainBuilder.FindBlock(); TestUtils.Eventually(() => wallet.Chain.Height == 1); for(int i = 0 ; i < 9 ; i++) { wallet.GetNextScriptPubKey(); } wallet.GetNextScriptPubKey(); //Should provoke purge TestUtils.Eventually(() => wallet.State == WalletState.Disconnected && wallet.ConnectedNodes == 0); TestUtils.Eventually(() => wallet.ConnectedNodes == 1); TestUtils.Eventually(() => servers.Server1.ConnectedNodes.Count == 1); var spv = servers.Server1.ConnectedNodes.First().Behaviors.Find<SPVBehavior>(); TestUtils.Eventually(() => spv._Filter != null); var k = wallet.GetNextScriptPubKey(); Assert.Equal(creation.UseP2SH, k.GetDestinationAddress(Network.TestNet) is BitcoinScriptAddress); chainBuilder.GiveMoney(k, Money.Coins(1.0m)); TestUtils.Eventually(() => wallet.GetTransactions().Count == 1); chainBuilder.FindBlock(); TestUtils.Eventually(() => wallet.GetTransactions().Where(t => t.BlockInformation != null).Count() == 1); chainBuilder.Broadcast = false; chainBuilder.GiveMoney(k, Money.Coins(1.5m)); chainBuilder.Broadcast = true; chainBuilder.FindBlock(); TestUtils.Eventually(() => wallet.GetTransactions().Summary.Confirmed.TransactionCount == 2); chainBuilder.Broadcast = false; for(int i = 0 ; i < 30 ; i++) { chainBuilder.FindBlock(); } chainBuilder.GiveMoney(k, Money.Coins(0.001m)); chainBuilder.FindBlock(); chainBuilder.Broadcast = true; chainBuilder.FindBlock(); //Sync automatically TestUtils.Eventually(() => wallet.GetTransactions().Summary.Confirmed.TransactionCount == 3); MemoryStream ms = new MemoryStream(); wallet.Save(ms); ms.Position = 0; var wallet2 = Wallet.Load(ms); wallet2.Connect(connected); Assert.Equal(wallet.Created, wallet2.Created); Assert.Equal(wallet.GetNextScriptPubKey(), wallet2.GetNextScriptPubKey()); Assert.True(wallet.GetKnownScripts().Length == wallet2.GetKnownScripts().Length); var fork = wallet.Chain.FindFork(wallet2._ScanLocation); Assert.True(fork.Height == chainBuilder.Chain.Height - 5); } }
public SPVBehavior(BlockchainBuilder builder) { _Builder = builder; }
public void CanSyncWalletCore(WalletCreation creation) { using (NodeServerTester servers = new NodeServerTester(Network.TestNet)) { var notifiedTransactions = new List <WalletTransaction>(); var chainBuilder = new BlockchainBuilder(); SetupSPVBehavior(servers, chainBuilder); NodesGroup connected = CreateGroup(servers, 1); Wallet wallet = new Wallet(creation, keyPoolSize: 11); wallet.NewWalletTransaction += (s, a) => notifiedTransactions.Add(a); Assert.True(wallet.State == WalletState.Created); wallet.Configure(connected); wallet.Connect(); Assert.True(wallet.State == WalletState.Disconnected); TestUtils.Eventually(() => connected.ConnectedNodes.Count == 1); Assert.True(wallet.State == WalletState.Connected); chainBuilder.FindBlock(); TestUtils.Eventually(() => wallet.Chain.Height == 1); for (int i = 0; i < 9; i++) { wallet.GetNextScriptPubKey(); } wallet.GetNextScriptPubKey(); //Should provoke purge TestUtils.Eventually(() => wallet.State == WalletState.Disconnected && wallet.ConnectedNodes == 0); TestUtils.Eventually(() => wallet.ConnectedNodes == 1); TestUtils.Eventually(() => servers.Server1.ConnectedNodes.Count == 1); var spv = servers.Server1.ConnectedNodes.First().Behaviors.Find <SPVBehavior>(); TestUtils.Eventually(() => spv._Filter != null); var k = wallet.GetNextScriptPubKey(); Assert.NotNull(wallet.GetKeyPath(k)); if (creation.UseP2SH) { var p2sh = k.GetDestinationAddress(Network.TestNet) as BitcoinScriptAddress; Assert.NotNull(p2sh); var redeem = wallet.GetRedeemScript(p2sh); Assert.NotNull(redeem); Assert.Equal(redeem.Hash, p2sh.Hash); } Assert.Equal(creation.UseP2SH, k.GetDestinationAddress(Network.TestNet) is BitcoinScriptAddress); chainBuilder.GiveMoney(k, Money.Coins(1.0m)); TestUtils.Eventually(() => wallet.GetTransactions().Count == 1 && notifiedTransactions.Count == 1); chainBuilder.FindBlock(); TestUtils.Eventually(() => wallet.GetTransactions().Where(t => t.BlockInformation != null).Count() == 1 && notifiedTransactions.Count == 2); chainBuilder.Broadcast = false; chainBuilder.GiveMoney(k, Money.Coins(1.5m)); chainBuilder.Broadcast = true; chainBuilder.FindBlock(); TestUtils.Eventually(() => wallet.GetTransactions().Summary.Confirmed.TransactionCount == 2 && notifiedTransactions.Count == 3); chainBuilder.Broadcast = false; for (int i = 0; i < 30; i++) { chainBuilder.FindBlock(); } chainBuilder.GiveMoney(k, Money.Coins(0.001m)); chainBuilder.FindBlock(); chainBuilder.Broadcast = true; chainBuilder.FindBlock(); //Sync automatically TestUtils.Eventually(() => wallet.GetTransactions().Summary.Confirmed.TransactionCount == 3); //Save and restore wallet MemoryStream ms = new MemoryStream(); wallet.Save(ms); ms.Position = 0; var wallet2 = Wallet.Load(ms); ////// //Save and restore tracker ms = new MemoryStream(); var tracker = connected.NodeConnectionParameters.TemplateBehaviors.Find <TrackerBehavior>(); connected.NodeConnectionParameters.TemplateBehaviors.Remove(tracker); tracker.Tracker.Save(ms); ms.Position = 0; tracker = new TrackerBehavior(Tracker.Load(ms), wallet.Chain); connected.NodeConnectionParameters.TemplateBehaviors.Add(tracker); ////// wallet2.Configure(connected); wallet2.Connect(); Assert.Equal(wallet.Created, wallet2.Created); Assert.Equal(wallet.GetNextScriptPubKey(), wallet2.GetNextScriptPubKey()); Assert.True(wallet.GetKnownScripts().Length == wallet2.GetKnownScripts().Length); TestUtils.Eventually(() => wallet2.GetTransactions().Summary.Confirmed.TransactionCount == 3); var fork = wallet.Chain.FindFork(wallet2._ScanLocation); Assert.True(fork.Height == chainBuilder.Chain.Height); } }
public void CanSyncWallet2() { using (NodeServerTester servers = new NodeServerTester(Network.TestNet)) { var chainBuilder = new BlockchainBuilder(); SetupSPVBehavior(servers, chainBuilder); NodesGroup aliceConnection = CreateGroup(servers, 1); NodesGroup bobConnection = CreateGroup(servers, 1); var aliceKey = new ExtKey(); Wallet alice = new Wallet(new WalletCreation() { Network = Network.TestNet, RootKeys = new[] { aliceKey.Neuter() }, SignatureRequired = 1, UseP2SH = false }, 11); Wallet bob = new Wallet(new WalletCreation() { Network = Network.TestNet, RootKeys = new[] { new ExtKey().Neuter() }, SignatureRequired = 1, UseP2SH = false }, 11); alice.Configure(aliceConnection); alice.Connect(); bob.Configure(bobConnection); bob.Connect(); TestUtils.Eventually(() => aliceConnection.ConnectedNodes.Count == 1); //New address tracked var addressAlice = alice.GetNextScriptPubKey(); chainBuilder.GiveMoney(addressAlice, Money.Coins(1.0m)); TestUtils.Eventually(() => aliceConnection.ConnectedNodes.Count == 0); //Purge TestUtils.Eventually(() => aliceConnection.ConnectedNodes.Count == 1); //Reconnect ////// TestUtils.Eventually(() => alice.GetTransactions().Count == 1); //Alice send tx to bob var coins = alice.GetTransactions().GetSpendableCoins(); var keys = coins.Select(c => alice.GetKeyPath(c.ScriptPubKey)) .Select(k => aliceKey.Derive(k)) .ToArray(); var builder = new TransactionBuilder(); var tx = builder .SetTransactionPolicy(new Policy.StandardTransactionPolicy() { MinRelayTxFee = new FeeRate(0) }) .AddCoins(coins) .AddKeys(keys) .Send(bob.GetNextScriptPubKey(), Money.Coins(0.4m)) .SetChange(alice.GetNextScriptPubKey(true)) .BuildTransaction(true); Assert.True(builder.Verify(tx)); chainBuilder.BroadcastTransaction(tx); //Alice get change TestUtils.Eventually(() => alice.GetTransactions().Count == 2); coins = alice.GetTransactions().GetSpendableCoins(); Assert.True(coins.Single().Amount == Money.Coins(0.6m)); ////// //Bob get coins TestUtils.Eventually(() => bob.GetTransactions().Count == 1); coins = bob.GetTransactions().GetSpendableCoins(); Assert.True(coins.Single().Amount == Money.Coins(0.4m)); ////// MemoryStream bobWalletBackup = new MemoryStream(); bob.Save(bobWalletBackup); bobWalletBackup.Position = 0; MemoryStream bobTrakerBackup = new MemoryStream(); bob.Tracker.Save(bobTrakerBackup); bobTrakerBackup.Position = 0; bob.Disconnect(); //Restore bob bob = Wallet.Load(bobWalletBackup); bobConnection.NodeConnectionParameters.TemplateBehaviors.Remove <TrackerBehavior>(); bobConnection.NodeConnectionParameters.TemplateBehaviors.Add(new TrackerBehavior(Tracker.Load(bobTrakerBackup), chainBuilder.Chain)); ///// bob.Configure(bobConnection); //Bob still has coins TestUtils.Eventually(() => bob.GetTransactions().Count == 1); coins = bob.GetTransactions().GetSpendableCoins(); Assert.True(coins.Single().Amount == Money.Coins(0.4m)); ////// bob.Connect(); TestUtils.Eventually(() => bobConnection.ConnectedNodes.Count == 1); //New block found ! chainBuilder.FindBlock(); //Alice send tx to bob coins = alice.GetTransactions().GetSpendableCoins(); keys = coins.Select(c => alice.GetKeyPath(c.ScriptPubKey)) .Select(k => aliceKey.Derive(k)) .ToArray(); builder = new TransactionBuilder(); tx = builder .SetTransactionPolicy(new Policy.StandardTransactionPolicy() { MinRelayTxFee = new FeeRate(0) }) .AddCoins(coins) .AddKeys(keys) .Send(bob.GetNextScriptPubKey(), Money.Coins(0.1m)) .SetChange(alice.GetNextScriptPubKey(true)) .BuildTransaction(true); Assert.True(builder.Verify(tx)); chainBuilder.BroadcastTransaction(tx); //Bob still has coins TestUtils.Eventually(() => bob.GetTransactions().Count == 2); //Bob has both, old and new tx coins = bob.GetTransactions().GetSpendableCoins(); ////// } }
public void CanSyncWalletCore(WalletCreation creation) { using (NodeServerTester servers = new NodeServerTester(Network.TestNet)) { var chainBuilder = new BlockchainBuilder(); //Simulate SPV compatible server servers.Server1.InboundNodeConnectionParameters.Services = NodeServices.Network; servers.Server1.InboundNodeConnectionParameters.TemplateBehaviors.Add(new ChainBehavior(chainBuilder.Chain) { AutoSync = false }); servers.Server1.InboundNodeConnectionParameters.TemplateBehaviors.Add(new SPVBehavior(chainBuilder)); ///////////// //The SPV client does not verify the chain and keep one connection alive with Server1 NodeConnectionParameters parameters = new NodeConnectionParameters(); Wallet.ConfigureDefaultNodeConnectionParameters(parameters); parameters.IsTrusted = true; AddressManagerBehavior addrman = new AddressManagerBehavior(new AddressManager()); addrman.AddressManager.Add(new NetworkAddress(servers.Server1.ExternalEndpoint), IPAddress.Parse("127.0.0.1")); parameters.TemplateBehaviors.Add(addrman); NodesGroup connected = new NodesGroup(Network.TestNet, parameters); connected.AllowSameGroup = true; connected.MaximumNodeConnection = 1; ///////////// Wallet wallet = new Wallet(creation, keyPoolSize: 11); Assert.True(wallet.State == WalletState.Created); wallet.Connect(connected); Assert.True(wallet.State == WalletState.Disconnected); TestUtils.Eventually(() => connected.ConnectedNodes.Count == 1); Assert.True(wallet.State == WalletState.Connected); chainBuilder.FindBlock(); TestUtils.Eventually(() => wallet.Chain.Height == 1); for (int i = 0; i < 9; i++) { wallet.GetNextScriptPubKey(); } wallet.GetNextScriptPubKey(); //Should provoke purge TestUtils.Eventually(() => wallet.State == WalletState.Disconnected && wallet.ConnectedNodes == 0); TestUtils.Eventually(() => wallet.ConnectedNodes == 1); TestUtils.Eventually(() => servers.Server1.ConnectedNodes.Count == 1); var spv = servers.Server1.ConnectedNodes.First().Behaviors.Find <SPVBehavior>(); TestUtils.Eventually(() => spv._Filter != null); var k = wallet.GetNextScriptPubKey(); Assert.Equal(creation.UseP2SH, k.GetDestinationAddress(Network.TestNet) is BitcoinScriptAddress); chainBuilder.GiveMoney(k, Money.Coins(1.0m)); TestUtils.Eventually(() => wallet.GetTransactions().Count == 1); chainBuilder.FindBlock(); TestUtils.Eventually(() => wallet.GetTransactions().Where(t => t.BlockInformation != null).Count() == 1); chainBuilder.Broadcast = false; chainBuilder.GiveMoney(k, Money.Coins(1.5m)); chainBuilder.Broadcast = true; chainBuilder.FindBlock(); TestUtils.Eventually(() => wallet.GetTransactions().Summary.Confirmed.TransactionCount == 2); chainBuilder.Broadcast = false; for (int i = 0; i < 30; i++) { chainBuilder.FindBlock(); } chainBuilder.GiveMoney(k, Money.Coins(0.001m)); chainBuilder.FindBlock(); chainBuilder.Broadcast = true; chainBuilder.FindBlock(); //Sync automatically TestUtils.Eventually(() => wallet.GetTransactions().Summary.Confirmed.TransactionCount == 3); MemoryStream ms = new MemoryStream(); wallet.Save(ms); ms.Position = 0; var wallet2 = Wallet.Load(ms); wallet2.Connect(connected); Assert.Equal(wallet.Created, wallet2.Created); Assert.Equal(wallet.GetNextScriptPubKey(), wallet2.GetNextScriptPubKey()); Assert.True(wallet.GetKnownScripts().Length == wallet2.GetKnownScripts().Length); var fork = wallet.Chain.FindFork(wallet2._ScanLocation); Assert.True(fork.Height == chainBuilder.Chain.Height - 5); } }
public void CanSyncWalletCore(WalletCreation creation) { using(NodeServerTester servers = new NodeServerTester(Network.TestNet)) { var notifiedTransactions = new List<WalletTransaction>(); var chainBuilder = new BlockchainBuilder(); SetupSPVBehavior(servers, chainBuilder); NodesGroup connected = CreateGroup(servers, 1); Wallet wallet = new Wallet(creation, keyPoolSize: 11); wallet.NewWalletTransaction += (s, a) => notifiedTransactions.Add(a); Assert.True(wallet.State == WalletState.Created); wallet.Configure(connected); wallet.Connect(); Assert.True(wallet.State == WalletState.Disconnected); TestUtils.Eventually(() => connected.ConnectedNodes.Count == 1); Assert.True(wallet.State == WalletState.Connected); chainBuilder.FindBlock(); TestUtils.Eventually(() => wallet.Chain.Height == 1); for(int i = 0 ; i < 9 ; i++) { wallet.GetNextScriptPubKey(); } wallet.GetNextScriptPubKey(); //Should provoke purge TestUtils.Eventually(() => wallet.State == WalletState.Disconnected && wallet.ConnectedNodes == 0); TestUtils.Eventually(() => wallet.ConnectedNodes == 1); TestUtils.Eventually(() => servers.Server1.ConnectedNodes.Count == 1); var spv = servers.Server1.ConnectedNodes.First().Behaviors.Find<SPVBehavior>(); TestUtils.Eventually(() => spv._Filter != null); var k = wallet.GetNextScriptPubKey(); Assert.NotNull(wallet.GetKeyPath(k)); if(creation.UseP2SH) { var p2sh = k.GetDestinationAddress(Network.TestNet) as BitcoinScriptAddress; Assert.NotNull(p2sh); var redeem = wallet.GetRedeemScript(p2sh); Assert.NotNull(redeem); Assert.Equal(redeem.Hash, p2sh.Hash); } Assert.Equal(creation.UseP2SH, k.GetDestinationAddress(Network.TestNet) is BitcoinScriptAddress); chainBuilder.GiveMoney(k, Money.Coins(1.0m)); TestUtils.Eventually(() => wallet.GetTransactions().Count == 1); Assert.Equal(1, notifiedTransactions.Count); chainBuilder.FindBlock(); TestUtils.Eventually(() => wallet.GetTransactions().Where(t => t.BlockInformation != null).Count() == 1); Assert.Equal(2, notifiedTransactions.Count); chainBuilder.Broadcast = false; chainBuilder.GiveMoney(k, Money.Coins(1.5m)); chainBuilder.Broadcast = true; chainBuilder.FindBlock(); TestUtils.Eventually(() => wallet.GetTransactions().Summary.Confirmed.TransactionCount == 2); Assert.Equal(3, notifiedTransactions.Count); chainBuilder.Broadcast = false; for(int i = 0 ; i < 30 ; i++) { chainBuilder.FindBlock(); } chainBuilder.GiveMoney(k, Money.Coins(0.001m)); chainBuilder.FindBlock(); chainBuilder.Broadcast = true; chainBuilder.FindBlock(); //Sync automatically TestUtils.Eventually(() => wallet.GetTransactions().Summary.Confirmed.TransactionCount == 3); //Save and restore wallet MemoryStream ms = new MemoryStream(); wallet.Save(ms); ms.Position = 0; var wallet2 = Wallet.Load(ms); ////// //Save and restore tracker ms = new MemoryStream(); var tracker = connected.NodeConnectionParameters.TemplateBehaviors.Find<TrackerBehavior>(); connected.NodeConnectionParameters.TemplateBehaviors.Remove(tracker); tracker.Tracker.Save(ms); ms.Position = 0; tracker = new TrackerBehavior(Tracker.Load(ms), wallet.Chain); connected.NodeConnectionParameters.TemplateBehaviors.Add(tracker); ////// wallet2.Configure(connected); wallet2.Connect(); Assert.Equal(wallet.Created, wallet2.Created); Assert.Equal(wallet.GetNextScriptPubKey(), wallet2.GetNextScriptPubKey()); Assert.True(wallet.GetKnownScripts().Length == wallet2.GetKnownScripts().Length); TestUtils.Eventually(() => wallet2.GetTransactions().Summary.Confirmed.TransactionCount == 3); var fork = wallet.Chain.FindFork(wallet2._ScanLocation); Assert.True(fork.Height == chainBuilder.Chain.Height); } }
private static void SetupSPVBehavior(NodeServerTester servers, BlockchainBuilder chainBuilder) { List<Node> nodes = new List<Node>(); ConcurrentDictionary<uint256, Transaction> receivedTxs = new ConcurrentDictionary<uint256, Transaction>(); foreach(var server in new[] { servers.Server1, servers.Server2 }) { server.InboundNodeConnectionParameters.Services = NodeServices.Network; //Simulate SPV compatible server server.InboundNodeConnectionParameters.TemplateBehaviors.Add(new ChainBehavior(chainBuilder.Chain) { AutoSync = false }); server.InboundNodeConnectionParameters.TemplateBehaviors.Add(new SPVBehavior(chainBuilder) { Nodes = nodes, _ReceivedTransactions = receivedTxs }); ///////////// } }
public void CanTrackKey() { BlockchainBuilder builder = new BlockchainBuilder(); Key bob = new Key(); Tracker tracker = new Tracker(); tracker.Add(bob); var tx1 = builder.GiveMoney(bob, Money.Coins(1.0m)); var coin = tx1.Outputs.AsCoins().First(); Assert.True(tracker.NotifyTransaction(tx1)); Thread.Sleep(10); Key alice = new Key(); var tx2 = builder.SpendCoin(coin, alice, Money.Coins(0.6m)); Assert.True(tracker.NotifyTransaction(tx2)); var block = builder.FindBlock(); foreach (var btx in block.Transactions) { if (!btx.IsCoinBase) { Assert.True(tracker.NotifyTransaction(btx, builder.Chain.GetBlock(block.GetHash()), block)); Assert.True(tracker.NotifyTransaction(btx, builder.Chain.GetBlock(block.GetHash()), block)); //Idempotent } } var transactions = tracker.GetWalletTransactions(builder.Chain); Assert.True(transactions.Count == 2); Assert.True(transactions[0].Transaction.GetHash() == tx2.GetHash()); Assert.True(transactions[1].Transaction.GetHash() == tx1.GetHash()); Assert.True(transactions[0].Balance == -Money.Coins(0.6m)); var tx3 = builder.GiveMoney(bob, Money.Coins(0.01m)); coin = tx3.Outputs.AsCoins().First(); block = builder.FindBlock(); Assert.True(tracker.NotifyTransaction(block.Transactions[1], builder.Chain.GetBlock(block.GetHash()), block)); transactions = tracker.GetWalletTransactions(builder.Chain); Assert.True(transactions.Count == 3); Assert.True(transactions.Summary.UnConfirmed.TransactionCount == 0); Assert.True(transactions[0].Transaction.GetHash() == block.Transactions[1].GetHash()); Assert.Equal(2, transactions.GetSpendableCoins().Count()); // the 1 change + 1 gift builder.Chain.SetTip(builder.Chain.Tip.Previous); transactions = tracker.GetWalletTransactions(builder.Chain); Assert.True(transactions.Count == 3); Assert.True(transactions.Summary.UnConfirmed.TransactionCount == 1); //Test roundtrip serialization var filterBefore = tracker.CreateBloomFilter(0.005); MemoryStream ms = new MemoryStream(); tracker.Save(ms); tracker = new Tracker(); ms.Position = 0; tracker = Tracker.Load(ms); transactions = tracker.GetWalletTransactions(builder.Chain); Assert.True(transactions.Count == 3); Assert.True(transactions.Summary.UnConfirmed.TransactionCount == 1); var filterAfter = tracker.CreateBloomFilter(0.005); Assert.True(filterBefore.ToBytes().SequenceEqual(filterAfter.ToBytes())); ///// }
public void CanBroadcastTransaction() { using(NodeServerTester servers = new NodeServerTester(Network.TestNet)) { var notifiedTransactions = new List<WalletTransaction>(); var chainBuilder = new BlockchainBuilder(); SetupSPVBehavior(servers, chainBuilder); var tx = new Transaction(); Wallet wallet = new Wallet(new WalletCreation() { Network = Network.TestNet, RootKeys = new[] { new ExtKey().Neuter() }, UseP2SH = false }, keyPoolSize: 11); NodesGroup connected = CreateGroup(servers, 2); wallet.Configure(connected); wallet.Connect(); AutoResetEvent evt = new AutoResetEvent(false); bool passed = false; bool rejected = false; var broadcasting = wallet.BroadcastTransactionAsync(tx); wallet.TransactionBroadcasted += (t) => { evt.Set(); passed = true; }; wallet.TransactionRejected += (t, r) => { evt.Set(); rejected = true; }; BroadcastHub hub = BroadcastHub.GetBroadcastHub(connected.NodeConnectionParameters); BroadcastHubBehavior behavior = null; while(behavior == null || behavior.AttachedNode.State != NodeState.HandShaked) { behavior = connected.ConnectedNodes.Select(n => n.Behaviors.Find<BroadcastHubBehavior>()).FirstOrDefault(); Thread.Sleep(1); } Assert.Equal(1, behavior.Broadcasts.Count()); Assert.Equal(1, hub.BroadcastingTransactions.Count()); Assert.True(evt.WaitOne(20000)); Assert.True(passed); evt.Reset(); Assert.Equal(0, behavior.Broadcasts.Count()); Assert.Equal(0, hub.BroadcastingTransactions.Count()); Assert.Null(broadcasting.Result); broadcasting = wallet.BroadcastTransactionAsync(tx); Assert.Equal(1, behavior.Broadcasts.Count()); Assert.True(evt.WaitOne(20000)); Assert.True(rejected); Assert.Equal(0, behavior.Broadcasts.Count()); Assert.Equal(0, hub.BroadcastingTransactions.Count()); Assert.NotNull(broadcasting.Result); } }
public void CanPrune() { BlockchainBuilder builder = new BlockchainBuilder(); Tracker tracker = new Tracker(); Key bob = new Key(); Key alice = new Key(); tracker.Add(bob); var oldUnconf = builder.GiveMoney(bob, Money.Coins(1.0m)); Assert.True(tracker.NotifyTransaction(oldUnconf)); builder.Mempool.Clear(); var oldConf = builder.GiveMoney(bob, Money.Coins(0.9m)); var oldConfSpent = builder.SpendCoin(oldConf.Outputs.AsCoins().First(), alice, Money.Coins(0.01m)); var block = builder.FindBlock(); Assert.True(tracker.NotifyTransaction(oldConf, builder.Chain.Tip, block)); Assert.True(tracker.NotifyTransaction(oldConfSpent, builder.Chain.Tip, block)); for (int i = 0; i < 9; i++) { builder.FindBlock(); } Assert.True(tracker.Prune(builder.Chain, 10).Count == 0); builder.FindBlock(); //Prune tracked outpoint var pruned = tracker.Prune(builder.Chain, 10); Assert.Equal(1, pruned.Count); Assert.True(pruned.First() is Tracker.TrackedOutpoint); //Prune old unconf pruned = tracker.Prune(builder.Chain, timeExpiration: TimeSpan.Zero); Assert.Equal(1, pruned.Count); var op = pruned.OfType <Tracker.Operation>().First(); Assert.True(op.BlockId == null); var conf = builder.GiveMoney(bob, Money.Coins(0.9m)); block = builder.FindBlock(); Assert.True(tracker.NotifyTransaction(conf, builder.Chain.Tip, block)); var oldSpentForked = builder.SpendCoin(conf.Outputs.AsCoins().First(), alice, Money.Coins(0.021m)); block = builder.FindBlock(); Assert.True(tracker.NotifyTransaction(oldSpentForked, builder.Chain.Tip, block)); var forked = builder.Chain.Tip; builder.Chain.SetTip(builder.Chain.Tip.Previous); for (int i = 0; i < 10; i++) { builder.FindBlock(); } pruned = tracker.Prune(builder.Chain, 10); Assert.True(pruned.Count == 1); //Tracked outpoint of conf Assert.True(pruned.First() is Tracker.TrackedOutpoint); block = builder.FindBlock(); pruned = tracker.Prune(builder.Chain, 10); //Old forked spent Assert.Equal(1, pruned.Count); op = pruned.OfType <Tracker.Operation>().First(); Assert.Equal(forked.HashBlock, op.BlockId); }
//[Trait("UnitTest", "UnitTest")] public void CanBroadcastTransaction() { using (NodeServerTester servers = new NodeServerTester()) { var notifiedTransactions = new List <WalletTransaction>(); var chainBuilder = new BlockchainBuilder(); SetupSPVBehavior(servers, chainBuilder); var tx = new Transaction(); Wallet wallet = new Wallet(new WalletCreation() { Network = servers.Network, RootKeys = new[] { new ExtKey().Neuter() }, UseP2SH = false }, keyPoolSize: 11); NodesGroup connected = CreateGroup(servers, 2); wallet.Configure(connected); wallet.Connect(); AutoResetEvent evt = new AutoResetEvent(false); bool passed = false; bool rejected = false; BroadcastHub hub = BroadcastHub.GetBroadcastHub(wallet.Group.NodeConnectionParameters); hub.ManualBroadcast = true; var broadcasting = wallet.BroadcastTransactionAsync(tx); wallet.TransactionBroadcasted += (t) => { passed = true; evt.Set(); }; wallet.TransactionRejected += (t, r) => { rejected = true; evt.Set(); }; while (connected.ConnectedNodes.Count != 2 && connected.ConnectedNodes.All(n => n.State == NodeState.HandShaked)) { Thread.Sleep(10); } var behaviors = connected.ConnectedNodes.Select(n => n.Behaviors.Find <BroadcastHubBehavior>()).ToArray(); TestUtils.Eventually(() => behaviors.All(b => b.Broadcasts.Count() == 1)); Assert.Equal(1, hub.BroadcastingTransactions.Count()); hub.BroadcastTransactions(); Assert.True(evt.WaitOne(20000)); Assert.True(broadcasting.Wait(2000)); Assert.True(passed); evt.Reset(); TestUtils.Eventually(() => behaviors.All(b => b.Broadcasts.Count() == 0)); Assert.Equal(0, hub.BroadcastingTransactions.Count()); Assert.Null(broadcasting.Result); broadcasting = wallet.BroadcastTransactionAsync(tx); TestUtils.Eventually(() => behaviors.All(b => b.Broadcasts.Count() == 1)); hub.BroadcastTransactions(); Assert.True(evt.WaitOne(20000)); Assert.True(broadcasting.Wait(2000)); Assert.True(rejected); TestUtils.Eventually(() => behaviors.All(b => b.Broadcasts.Count() == 0)); Assert.Equal(0, hub.BroadcastingTransactions.Count()); Assert.NotNull(broadcasting.Result); } }