public void CanGetMerkleRoot() { using (var builder = NodeBuilder.Create()) { var node = builder.CreateNode(true).CreateNodeClient(); builder.Nodes[0].Generate(101); var rpc = builder.Nodes[0].CreateRPCClient(); builder.Nodes[0].Split(Money.Coins(50m), 50); builder.Nodes[0].SelectMempoolTransactions(); builder.Nodes[0].Generate(1); for (int i = 0; i < 20; i++) { rpc.SendToAddress(new Key().PubKey.GetAddress(rpc.Network), Money.Coins(0.5m)); } builder.Nodes[0].SelectMempoolTransactions(); builder.Nodes[0].Generate(1); var block = builder.Nodes[0].CreateRPCClient().GetBlock(103); var knownTx = block.Transactions[0].GetHash(); var knownAddress = block.Transactions[0].Outputs[0].ScriptPubKey.GetDestination(); node.VersionHandshake(); using (var list = node.CreateListener() .Where(m => m.Message.Payload is MerkleBlockPayload || m.Message.Payload is TxPayload)) { BloomFilter filter = new BloomFilter(1, 0.005, 50, BloomFlags.UPDATE_NONE); filter.Insert(knownAddress.ToBytes()); node.SendMessageAsync(new FilterLoadPayload(filter)); node.SendMessageAsync(new GetDataPayload(new InventoryVector(InventoryType.MSG_FILTERED_BLOCK, block.GetHash()))); var merkle = list.ReceivePayload <MerkleBlockPayload>(); var tree = merkle.Object.PartialMerkleTree; Assert.True(tree.Check(block.Header.HashMerkleRoot)); Assert.True(tree.GetMatchedTransactions().Count() > 1); Assert.True(tree.GetMatchedTransactions().Contains(knownTx)); List <Transaction> matched = new List <Transaction>(); for (int i = 0; i < tree.GetMatchedTransactions().Count(); i++) { matched.Add(list.ReceivePayload <TxPayload>().Object); } Assert.True(matched.Count > 1); tree = tree.Trim(knownTx); Assert.True(tree.GetMatchedTransactions().Count() == 1); Assert.True(tree.GetMatchedTransactions().Contains(knownTx)); Action act = () => { foreach (var match in matched) { Assert.True(filter.IsRelevantAndUpdate(match)); } }; act(); filter = filter.Clone(); act(); var unknownBlock = uint256.Parse("00000000ad262227291eaf90cafdc56a8f8451e2d7653843122c5bb0bf2dfcdd"); node.SendMessageAsync(new GetDataPayload(new InventoryVector(InventoryType.MSG_FILTERED_BLOCK, Network.RegTest.GetGenesis().GetHash()))); merkle = list.ReceivePayload <MerkleBlockPayload>(); tree = merkle.Object.PartialMerkleTree; Assert.True(tree.Check(merkle.Object.Header.HashMerkleRoot)); Assert.True(!tree.GetMatchedTransactions().Contains(knownTx)); } } }
public void CanSyncWallet2() { using (var builder = NodeBuilder.Create()) { NodesGroup aliceConnection = CreateGroup(builder, 1); NodesGroup bobConnection = CreateGroup(builder, new[] { builder.Nodes[0] }, 1); builder.Nodes[0].Generate(101); var rpc = builder.Nodes[0].CreateRPCClient(); var aliceKey = new ExtKey(); Wallet alice = new Wallet(new WalletCreation() { Network = Network.RegTest, RootKeys = new[] { aliceKey.Neuter() }, SignatureRequired = 1, UseP2SH = false }, 11); Wallet bob = new Wallet(new WalletCreation() { Network = Network.RegTest, 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(); builder.Nodes[0].GiveMoney(addressAlice, Money.Coins(1.0m)); 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 txBuilder = new TransactionBuilder(); var tx = txBuilder .SetTransactionPolicy(new Policy.StandardTransactionPolicy() { MinRelayTxFee = new FeeRate(0) }) .AddCoins(coins) .AddKeys(keys) .Send(bob.GetNextScriptPubKey(), Money.Coins(0.4m)) .SetChange(alice.GetNextScriptPubKey(true)) .SendFees(Money.Coins(0.0001m)) .BuildTransaction(true); Assert.True(txBuilder.Verify(tx)); builder.Nodes[0].Broadcast(tx); //Alice get change TestUtils.Eventually(() => alice.GetTransactions().Count == 2); coins = alice.GetTransactions().GetSpendableCoins(); Assert.True(coins.Single().Amount == Money.Coins(0.5999m)); ////// //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), alice.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 ! builder.Nodes[0].SelectMempoolTransactions(); builder.Nodes[0].Generate(1); //Alice send tx to bob coins = alice.GetTransactions().GetSpendableCoins(); keys = coins.Select(c => alice.GetKeyPath(c.ScriptPubKey)) .Select(k => aliceKey.Derive(k)) .ToArray(); txBuilder = new TransactionBuilder(); tx = txBuilder .SetTransactionPolicy(new Policy.StandardTransactionPolicy() { MinRelayTxFee = new FeeRate(0) }) .AddCoins(coins) .AddKeys(keys) .Send(bob.GetNextScriptPubKey(), Money.Coins(0.1m)) .SetChange(alice.GetNextScriptPubKey(true)) .SendFees(Money.Coins(0.0001m)) .BuildTransaction(true); Assert.True(txBuilder.Verify(tx)); builder.Nodes[0].Broadcast(tx); //Bob still has coins TestUtils.Eventually(() => bob.GetTransactions().Count == 2); //Bob has both, old and new tx coins = bob.GetTransactions().GetSpendableCoins(); ////// } }