Example #1
0
        public void CanDownloadLastBlocks()
        {
            using (var builder = NodeBuilder.Create())
            {
                var node = builder.CreateNode(true).CreateNodeClient();
                builder.Nodes[0].Generate(150);
                var chain = node.GetChain();

                Assert.True(node.PeerVersion.StartHeight <= chain.Height);

                var subChain = chain.ToEnumerable(true).Take(100).Select(s => s.HashBlock).ToArray();

                var begin  = node.Counter.Snapshot();
                var blocks = node.GetBlocks(subChain).Select(_ => 1).ToList();
                var end    = node.Counter.Snapshot();
                var diff   = end - begin;
                Assert.True(diff.Start == begin.Taken);
                Assert.True(diff.Taken == end.Taken);
                Assert.True(diff.TotalReadenBytes == end.TotalReadenBytes - begin.TotalReadenBytes);
                Assert.True(diff.TotalWrittenBytes == end.TotalWrittenBytes - begin.TotalWrittenBytes);

                Assert.True(blocks.Count == 100);
            }
        }
Example #2
0
 public void CanGetChainsConcurrenty()
 {
     using (var builder = NodeBuilder.Create())
     {
         bool generating = true;
         builder.CreateNode(true);
         Task.Run(() =>
         {
             builder.Nodes[0].Generate(600);
             generating = false;
         });
         var node = builder.Nodes[0].CreateNodeClient();
         node.PollHeaderDelay = TimeSpan.FromSeconds(2);
         node.VersionHandshake();
         Random rand = new Random();
         Thread.Sleep(1000);
         var chains =
             Enumerable.Range(0, 5)
             .Select(_ => Task.Factory.StartNew(() =>
         {
             Thread.Sleep(rand.Next(0, 1000));
             return(node.GetChain());
         }))
             .Select(t => t.Result)
             .ToArray();
         while (generating)
         {
             SyncAll(node, rand, chains);
         }
         SyncAll(node, rand, chains);
         foreach (var c in chains)
         {
             Assert.Equal(600, c.Height);
         }
     }
 }
Example #3
0
        public void CanImportMultiAddresses()
        {
            // Test cases borrowed from: https://github.com/bitcoin/bitcoin/blob/master/test/functional/importmulti.py
            using (var builder = NodeBuilder.Create())
            {
                var rpc = builder.CreateNode().CreateRPCClient();
                builder.StartAll();

                Key          key;
                RPCException response;
                List <ImportMultiAddress> multiAddresses;
                Network network = Network.RegTest;

                // 20 total test cases

                #region Bitcoin Address
                Console.WriteLine("Should import an address");
                key            = new Key();
                multiAddresses = new List <ImportMultiAddress>
                {
                    new ImportMultiAddress
                    {
                        ScriptPubKey = new ImportMultiAddress.ScriptPubKeyObject {
                            Address = key.PubKey.GetAddress(network)
                        },
                        Timestamp = Utils.UnixTimeToDateTime(0)
                    }
                };

                rpc.ImportMulti(multiAddresses.ToArray(), false);

                #endregion

                #region ScriptPubKey + internal
                Console.WriteLine("Should import a scriptPubKey with internal flag");
                key            = new Key();
                multiAddresses = new List <ImportMultiAddress>
                {
                    new ImportMultiAddress
                    {
                        ScriptPubKey = new ImportMultiAddress.ScriptPubKeyObject(key.ScriptPubKey),
                        Internal     = true
                    }
                };

                rpc.ImportMulti(multiAddresses.ToArray(), false);
                #endregion

                #region ScriptPubKey + !internal
                Console.WriteLine("Should not import a scriptPubKey without internal flag");
                key            = new Key();
                multiAddresses = new List <ImportMultiAddress>
                {
                    new ImportMultiAddress
                    {
                        ScriptPubKey = new ImportMultiAddress.ScriptPubKeyObject {
                            ScriptPubKey = key.ScriptPubKey
                        },
                    }
                };

                response = Assert.Throws <RPCException>(() => rpc.ImportMulti(multiAddresses.ToArray(), false));
                Assert.Equal(response.RPCCode, RPCErrorCode.RPC_INVALID_PARAMETER);
                Assert.Equal(response.Message, "Internal must be set for hex scriptPubKey");
                #endregion

                #region Address + Public key + !internal
                Console.WriteLine("Should import an address with public key");
                key            = new Key();
                multiAddresses = new List <ImportMultiAddress>
                {
                    new ImportMultiAddress
                    {
                        ScriptPubKey = new ImportMultiAddress.ScriptPubKeyObject(key.PubKey.GetAddress(network)),
                        PubKeys      = new string[] { key.PubKey.ToString() }
                    }
                };

                rpc.ImportMulti(multiAddresses.ToArray(), false);
                #endregion

                #region ScriptPubKey + Public key + internal
                Console.WriteLine("Should import a scriptPubKey with internal and with public key");
                key            = new Key();
                multiAddresses = new List <ImportMultiAddress>
                {
                    new ImportMultiAddress
                    {
                        ScriptPubKey = new ImportMultiAddress.ScriptPubKeyObject {
                            ScriptPubKey = key.ScriptPubKey
                        },
                        PubKeys  = new string[] { key.PubKey.ToString() },
                        Internal = true
                    }
                };

                rpc.ImportMulti(multiAddresses.ToArray(), false);
                #endregion

                #region ScriptPubKey + Public key + !internal
                // Console.WriteLine("Should not import a scriptPubKey without internal and with public key");
                key            = new Key();
                multiAddresses = new List <ImportMultiAddress>
                {
                    new ImportMultiAddress
                    {
                        ScriptPubKey = new ImportMultiAddress.ScriptPubKeyObject {
                            ScriptPubKey = key.ScriptPubKey
                        },
                        PubKeys = new string[] { key.PubKey.ToString() }
                    }
                };

                response = Assert.Throws <RPCException>(() => rpc.ImportMulti(multiAddresses.ToArray(), false));
                Assert.Equal(response.RPCCode, RPCErrorCode.RPC_INVALID_PARAMETER);
                Assert.Equal(response.Message, "Internal must be set for hex scriptPubKey");
                #endregion

                #region Address + Private key + !watchonly
                // Console.WriteLine("Should import an address with private key");
                key            = new Key();
                multiAddresses = new List <ImportMultiAddress>
                {
                    new ImportMultiAddress
                    {
                        ScriptPubKey = new ImportMultiAddress.ScriptPubKeyObject {
                            Address = key.PubKey.GetAddress(network)
                        },
                        Keys = new string[] { key.GetWif(network).ToString() }
                    }
                };

                rpc.ImportMulti(multiAddresses.ToArray(), false);

                Console.WriteLine("Should not import an address with private key if is already imported");
                multiAddresses = new List <ImportMultiAddress>
                {
                    new ImportMultiAddress
                    {
                        ScriptPubKey = new ImportMultiAddress.ScriptPubKeyObject {
                            Address = key.PubKey.GetAddress(network)
                        },
                        Keys = new string[] { key.GetWif(network).ToString() }
                    }
                };

                response = Assert.Throws <RPCException>(() => rpc.ImportMulti(multiAddresses.ToArray(), false));

                //Assert.False(response.Result[0].Value<bool>());

                #endregion

                #region Address + Private key + watchonly
                Console.WriteLine("Should not import an address with private key and with watchonly");
                key            = new Key();
                multiAddresses = new List <ImportMultiAddress>
                {
                    new ImportMultiAddress
                    {
                        ScriptPubKey = new ImportMultiAddress.ScriptPubKeyObject {
                            Address = key.PubKey.GetAddress(network)
                        },
                        Keys      = new string[] { key.GetWif(network).ToString() },
                        WatchOnly = true
                    }
                };

                response = Assert.Throws <RPCException>(() => rpc.ImportMulti(multiAddresses.ToArray(), false));
                Assert.Equal(response.RPCCode, RPCErrorCode.RPC_INVALID_PARAMETER);
                Assert.Equal(response.Message, "Incompatibility found between watchonly and keys");
                #endregion

                #region ScriptPubKey + Private key + internal
                Console.WriteLine("Should import a scriptPubKey with internal and with private key");
                key            = new Key();
                multiAddresses = new List <ImportMultiAddress>
                {
                    new ImportMultiAddress
                    {
                        ScriptPubKey = new ImportMultiAddress.ScriptPubKeyObject {
                            ScriptPubKey = key.ScriptPubKey
                        },
                        Keys     = new string[] { key.GetWif(network).ToString() },
                        Internal = true
                    }
                };

                rpc.ImportMulti(multiAddresses.ToArray(), false);
                #endregion

                #region ScriptPubKey + Private key + !internal
                Console.WriteLine("Should not import a scriptPubKey without internal and with private key");
                key            = new Key();
                multiAddresses = new List <ImportMultiAddress>
                {
                    new ImportMultiAddress
                    {
                        ScriptPubKey = new ImportMultiAddress.ScriptPubKeyObject {
                            ScriptPubKey = key.ScriptPubKey
                        },
                        Keys = new string[] { key.GetWif(network).ToString() }
                    }
                };

                response = Assert.Throws <RPCException>(() => rpc.ImportMulti(multiAddresses.ToArray(), false));
                #endregion

                #region P2SH address
                //Blocked : Dependent on implementation of rpc.CreateMultiSig()
                #endregion

                #region P2SH + Redeem script
                //Blocked : Dependent on implementation of rpc.CreateMultiSig()
                #endregion

                #region P2SH + Redeem script + Private Keys + !Watchonly
                //Blocked : Dependent on implementation of rpc.CreateMultiSig()
                #endregion

                #region P2SH + Redeem script + Private Keys + Watchonly
                //Blocked : Dependent on implementation of rpc.CreateMultiSig()
                #endregion

                #region Address + Public key + !Internal + Wrong pubkey
                Console.WriteLine("Should not import an address with a wrong public key");
                key            = new Key();
                multiAddresses = new List <ImportMultiAddress>
                {
                    new ImportMultiAddress
                    {
                        ScriptPubKey = new ImportMultiAddress.ScriptPubKeyObject {
                            Address = key.PubKey.GetAddress(network)
                        },
                        PubKeys = new string[] { new Key().PubKey.ToString() }
                    }
                };

                response = Assert.Throws <RPCException>(() => rpc.ImportMulti(multiAddresses.ToArray(), false));
                Assert.Equal(response.RPCCode, RPCErrorCode.RPC_INVALID_ADDRESS_OR_KEY);
                Assert.Equal(response.Message, "Consistency check failed");
                #endregion

                #region ScriptPubKey + Public key + internal + Wrong pubkey
                Console.WriteLine("Should not import a scriptPubKey with internal and with a wrong public key");
                key            = new Key();
                multiAddresses = new List <ImportMultiAddress>
                {
                    new ImportMultiAddress
                    {
                        ScriptPubKey = new ImportMultiAddress.ScriptPubKeyObject {
                            ScriptPubKey = key.ScriptPubKey
                        },
                        PubKeys  = new string[] { new Key().PubKey.ToString() },
                        Internal = true
                    }
                };

                response = Assert.Throws <RPCException>(() => rpc.ImportMulti(multiAddresses.ToArray(), false));
                Assert.Equal(response.RPCCode, RPCErrorCode.RPC_INVALID_ADDRESS_OR_KEY);
                Assert.Equal(response.Message, "Consistency check failed");
                #endregion

                #region Address + Private key + !watchonly + Wrong private key
                Console.WriteLine("Should not import an address with a wrong private key");
                key            = new Key();
                multiAddresses = new List <ImportMultiAddress>
                {
                    new ImportMultiAddress
                    {
                        ScriptPubKey = new ImportMultiAddress.ScriptPubKeyObject {
                            Address = key.PubKey.GetAddress(network)
                        },
                        Keys = new string[] { new Key().GetWif(network).ToString() }
                    }
                };

                response = Assert.Throws <RPCException>(() => rpc.ImportMulti(multiAddresses.ToArray(), false));
                Assert.Equal(response.RPCCode, RPCErrorCode.RPC_INVALID_ADDRESS_OR_KEY);
                Assert.Equal(response.Message, "Consistency check failed");
                #endregion

                #region ScriptPubKey + Private key + internal + Wrong private key
                Console.WriteLine("Should not import a scriptPubKey with internal and with a wrong private key");
                key            = new Key();
                multiAddresses = new List <ImportMultiAddress>
                {
                    new ImportMultiAddress
                    {
                        ScriptPubKey = new ImportMultiAddress.ScriptPubKeyObject {
                            ScriptPubKey = key.ScriptPubKey
                        },
                        Keys     = new string[] { new Key().GetWif(network).ToString() },
                        Internal = true
                    }
                };

                response = Assert.Throws <RPCException>(() => rpc.ImportMulti(multiAddresses.ToArray(), false));
                Assert.Equal(response.RPCCode, RPCErrorCode.RPC_INVALID_ADDRESS_OR_KEY);
                Assert.Equal(response.Message, "Consistency check failed");
                #endregion

                #region Importing existing watch only address with new timestamp should replace saved timestamp.
                //TODO
                #endregion

                #region restart nodes to check for proper serialization/deserialization of watch only address
                //TODO
                #endregion
            }
        }
Example #4
0
        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));
                }
            }
        }
Example #5
0
        // todo: revisit this test when fixing the node tests
        //[Fact]
        //[Trait("CoreBeta", "CoreBeta")]
        public void CanAskCmpctBlock()
        {
            var alice   = new BitcoinSecret("KypycJyxP5yA4gSedEBRse5q5f8RwYKG8xi8z4SRe2rdaioL3YNc").PrivateKey;
            var satoshi = new BitcoinSecret("KypycJyxP5yA4gSedEBRse5q5f8RwYKG8xi8z4SRe2rdaioL3YNc").ToNetwork(Network.RegTest);

            using (var builder = NodeBuilder.Create(version: "C:\\Bitcoin\\bitcoind.exe"))
            {
                var now = new DateTimeOffset(2015, 07, 18, 0, 0, 0, TimeSpan.Zero);
                builder.ConfigParameters.Add("mocktime", Utils.DateTimeToUnixTime(now).ToString());
                var bitcoind  = builder.CreateNode(false);
                var bitcoind2 = builder.CreateNode(false);
                builder.StartAll();

                bitcoind.SetMinerSecret(satoshi);
                bitcoind.MockTime = now;

                bitcoind2.SetMinerSecret(satoshi);
                bitcoind2.MockTime = now;

                var rpc = bitcoind.CreateRPCClient();
                rpc.AddNode(bitcoind2.Endpoint, true);

                var client1 = bitcoind.CreateNodeClient(new NodeConnectionParameters()
                {
                    Version = ProtocolVersion.SHORT_IDS_BLOCKS_VERSION
                });
                using (var listener = client1.CreateListener())
                {
                    client1.VersionHandshake();
                    var sendCmpct = listener.ReceivePayload <SendCmpctPayload>();
                    Assert.Equal(1U, sendCmpct.Version);
                    Assert.Equal(false, sendCmpct.PreferHeaderAndIDs);

                    //Announcement
                    client1.SendMessage(new SendCmpctPayload(false));
                    bitcoind.Generate(1);
                    var inv = listener.ReceivePayload <InvPayload>();
                    Assert.True(inv.First().Type == InventoryType.MSG_BLOCK);

                    //Request block
                    inv.First().Type = InventoryType.MSG_CMPCT_BLOCK;
                    client1.SendMessage(new GetDataPayload(inv.First()));
                    var blk = listener.ReceivePayload <CmpctBlockPayload>();

                    //Request transaction
                    var getTxn = new GetBlockTxnPayload();
                    getTxn.BlockId = blk.Header.GetHash();
                    getTxn.Indices.Add(0);
                    client1.SendMessage(getTxn);
                    var blockTxn = listener.ReceivePayload <BlockTxnPayload>();
                    Assert.True(blockTxn.BlockId == blk.Header.GetHash());
                    Assert.True(blockTxn.Transactions[0].GetHash() == blk.PrefilledTransactions[0].Transaction.GetHash());

                    bitcoind.Generate(100);
                    var tx      = bitcoind.GiveMoney(alice.ScriptPubKey, Money.Coins(1), false);
                    var lastBlk = bitcoind.Generate(1)[0];

                    while (true)
                    {
                        var invv = listener.ReceivePayload <InvPayload>().First();
                        if (invv.Hash == lastBlk.GetHash())
                        {
                            invv.Type = InventoryType.MSG_CMPCT_BLOCK;
                            client1.SendMessage(new GetDataPayload(invv));
                            break;
                        }
                    }

                    blk = listener.ReceivePayload <CmpctBlockPayload>();
                    Assert.Equal(1, blk.ShortIds.Count);
                    Assert.Equal(blk.ShortIds[0], blk.GetShortID(tx.GetHash()));

                    //Let the node know which is the last block that we know
                    client1.SendMessage(new InvPayload(new InventoryVector(InventoryType.MSG_BLOCK, blk.Header.GetHash())));
                    bitcoind.Generate(1);
                    inv = listener.ReceivePayload <InvPayload>();
                    inv.First().Type = InventoryType.MSG_CMPCT_BLOCK;
                    client1.SendMessage(new GetDataPayload(inv.First()));
                    blk = listener.ReceivePayload <CmpctBlockPayload>();

                    //Prefer being notified with cmpctblock
                    client1.SendMessage(new SendCmpctPayload(true));
                    //Let the node know which is the last block that we know
                    client1.SendMessage(new InvPayload(new InventoryVector(InventoryType.MSG_BLOCK, blk.Header.GetHash())));
                    bitcoind.Generate(1);
                    blk = listener.ReceivePayload <CmpctBlockPayload>();

                    //The node ask to connect to use in high bandwidth mode
                    var blocks = bitcoind.Generate(1, broadcast: false);
                    client1.SendMessage(new HeadersPayload(blocks[0].Header));
                    var cmpct = listener.ReceivePayload <SendCmpctPayload>();                    //Should become one of the three high bandwidth node
                    Assert.True(cmpct.PreferHeaderAndIDs);
                    var getdata = listener.ReceivePayload <GetDataPayload>();
                    Assert.True(getdata.Inventory[0].Type == InventoryType.MSG_CMPCT_BLOCK);
                    client1.SendMessage(new CmpctBlockPayload(blocks[0]));

                    //Should be able to get a compact block with Inv
                    blocks = bitcoind.Generate(1, broadcast: false);
                    client1.SendMessage(new InvPayload(blocks[0]));
                    getdata = listener.ReceivePayload <GetDataPayload>();
                    Assert.True(getdata.Inventory[0].Type == InventoryType.MSG_CMPCT_BLOCK);
                    client1.SendMessage(new CmpctBlockPayload(blocks[0]));


                    //Send as prefilled transaction 0 and 2
                    var tx1 = bitcoind.GiveMoney(satoshi.ScriptPubKey, Money.Coins(1.0m), broadcast: false);
                    var tx2 = bitcoind.GiveMoney(satoshi.ScriptPubKey, Money.Coins(2.0m), broadcast: false);
                    var tx3 = bitcoind.GiveMoney(satoshi.ScriptPubKey, Money.Coins(3.0m), broadcast: false);
                    blocks = bitcoind.Generate(1, broadcast: false);
                    Assert.True(blocks[0].Transactions.Count == 4);
                    var cmpctBlk = new CmpctBlockPayload();
                    cmpctBlk.Nonce  = RandomUtils.GetUInt64();
                    cmpctBlk.Header = blocks[0].Header;
                    cmpctBlk.PrefilledTransactions.Add(new PrefilledTransaction()
                    {
                        Index = 0, Transaction = blocks[0].Transactions[0]
                    });
                    cmpctBlk.PrefilledTransactions.Add(new PrefilledTransaction()
                    {
                        Index = 2, Transaction = blocks[0].Transactions[2]
                    });
                    cmpctBlk.AddTransactionShortId(blocks[0].Transactions[1]);
                    cmpctBlk.AddTransactionShortId(blocks[0].Transactions[3]);
                    client1.SendMessage(cmpctBlk);

                    //Check that node ask for 1 and 3
                    var gettxn = listener.ReceivePayload <GetBlockTxnPayload>();
                    Assert.Equal(2, gettxn.Indices.Count);
                    Assert.Equal(1, gettxn.Indices[0]);
                    Assert.Equal(3, gettxn.Indices[1]);

                    client1.SendMessage(new BlockTxnPayload()
                    {
                        BlockId      = blocks[0].GetHash(),
                        Transactions =
                        {
                            blocks[0].Transactions[1],
                            blocks[0].Transactions[3],
                        }
                    });

                    //Both nodes updated ?
                    var chain1 = client1.GetChain();
                    Assert.Equal(blocks[0].GetHash(), chain1.Tip.HashBlock);
                    using (var client2 = bitcoind2.CreateNodeClient())
                    {
                        client2.VersionHandshake();
                        var chain2 = client2.GetChain();
                        Assert.Equal(chain1.Tip.HashBlock, chain2.Tip.HashBlock);
                    }

                    //Block with coinbase only
                    blocks = bitcoind.Generate(1, broadcast: false);
                    client1.SendMessage(new CmpctBlockPayload(blocks[0]));
                    client1.SynchronizeChain(chain1);
                    Assert.Equal(chain1.Tip.HashBlock, blocks[0].GetHash());
                }
            }
        }
Example #6
0
        public void CanImportMultiAddresses()
        {
            // Test cases borrowed from: https://github.com/bitcoin/bitcoin/blob/master/test/functional/importmulti.py
            using (var builder = NodeBuilder.Create(version: "0.15.1"))
            {
                var rpc = builder.CreateNode().CreateRPCClient();
                builder.StartAll();

                Key key;
                List <ImportMultiAddress> multiAddresses;
                RPCResponse response;
                Network     network = Network.RegTest;

                // 20 total test cases

                #region Bitcoin Address
                Console.WriteLine("Should import an address");
                key            = new Key();
                multiAddresses = new List <ImportMultiAddress>
                {
                    new ImportMultiAddress
                    {
                        ScriptPubKey = new ImportMultiAddress.ScriptPubKeyObject {
                            Address = key.PubKey.GetAddress(network).ToString()
                        },
                        Timestamp = "now"
                    }
                };

                response = rpc.ImportMulti(multiAddresses.ToArray(), false);
                Assert.True(response.Result[0]["success"].Value <bool>());

                Console.WriteLine("Should not import an invalid address");
                multiAddresses = new List <ImportMultiAddress>
                {
                    new ImportMultiAddress
                    {
                        ScriptPubKey = new ImportMultiAddress.ScriptPubKeyObject {
                            Address = "not valid address"
                        },
                        Timestamp = DateTimeOffset.Now.AddDays(-1)
                    }
                };

                response = rpc.ImportMulti(multiAddresses.ToArray(), false);
                Assert.False(response.Result[0]["success"].Value <bool>());
                #endregion

                #region ScriptPubKey + internal
                Console.WriteLine("Should import a scriptPubKey with internal flag");
                key            = new Key();
                multiAddresses = new List <ImportMultiAddress>
                {
                    new ImportMultiAddress
                    {
                        ScriptPubKey = new ImportMultiAddress.ScriptPubKeyObject {
                            ScriptPubKey = key.ScriptPubKey.ToHex()
                        },
                        Timestamp = "now",
                        Internal  = true
                    }
                };

                response = rpc.ImportMulti(multiAddresses.ToArray(), false);
                Assert.True(response.Result[0]["success"].Value <bool>());
                #endregion

                #region ScriptPubKey + !internal
                Console.WriteLine("Should not import a scriptPubKey without internal flag");
                key            = new Key();
                multiAddresses = new List <ImportMultiAddress>
                {
                    new ImportMultiAddress
                    {
                        ScriptPubKey = new ImportMultiAddress.ScriptPubKeyObject {
                            ScriptPubKey = key.ScriptPubKey.ToHex()
                        },
                        Timestamp = "now"
                    }
                };

                response = rpc.ImportMulti(multiAddresses.ToArray(), false);
                Assert.False(response.Result[0]["success"].Value <bool>());
                Assert.Equal(response.Result[0]["error"]["code"].Value <int>(), -8);
                Assert.Equal(response.Result[0]["error"]["message"].Value <string>(), "Internal must be set for hex scriptPubKey");
                #endregion

                #region Address + Public key + !internal
                Console.WriteLine("Should import an address with public key");
                key            = new Key();
                multiAddresses = new List <ImportMultiAddress>
                {
                    new ImportMultiAddress
                    {
                        ScriptPubKey = new ImportMultiAddress.ScriptPubKeyObject {
                            Address = key.PubKey.GetAddress(network).ToString()
                        },
                        Timestamp = "now",
                        PubKeys   = new string[] { key.PubKey.ToString() }
                    }
                };

                response = rpc.ImportMulti(multiAddresses.ToArray(), false);
                Assert.True(response.Result[0]["success"].Value <bool>());
                #endregion

                #region ScriptPubKey + Public key + internal
                Console.WriteLine("Should import a scriptPubKey with internal and with public key");
                key            = new Key();
                multiAddresses = new List <ImportMultiAddress>
                {
                    new ImportMultiAddress
                    {
                        ScriptPubKey = new ImportMultiAddress.ScriptPubKeyObject {
                            ScriptPubKey = key.ScriptPubKey.ToHex()
                        },
                        Timestamp = "now",
                        PubKeys   = new string[] { key.PubKey.ToString() },
                        Internal  = true
                    }
                };

                response = rpc.ImportMulti(multiAddresses.ToArray(), false);
                Assert.True(response.Result[0]["success"].Value <bool>());
                #endregion

                #region ScriptPubKey + Public key + !internal
                Console.WriteLine("Should not import a scriptPubKey without internal and with public key");
                key            = new Key();
                multiAddresses = new List <ImportMultiAddress>
                {
                    new ImportMultiAddress
                    {
                        ScriptPubKey = new ImportMultiAddress.ScriptPubKeyObject {
                            ScriptPubKey = key.ScriptPubKey.ToHex()
                        },
                        Timestamp = "now",
                        PubKeys   = new string[] { key.PubKey.ToString() }
                    }
                };

                response = rpc.ImportMulti(multiAddresses.ToArray(), false);
                Assert.False(response.Result[0]["success"].Value <bool>());
                Assert.Equal(response.Result[0]["error"]["code"].Value <int>(), -8);
                Assert.Equal(response.Result[0]["error"]["message"].Value <string>(), "Internal must be set for hex scriptPubKey");
                #endregion

                #region Address + Private key + !watchonly
                Console.WriteLine("Should import an address with private key");
                key            = new Key();
                multiAddresses = new List <ImportMultiAddress>
                {
                    new ImportMultiAddress
                    {
                        ScriptPubKey = new ImportMultiAddress.ScriptPubKeyObject {
                            Address = key.PubKey.GetAddress(network).ToString()
                        },
                        Timestamp = "now",
                        Keys      = new string[] { key.GetWif(network).ToString() }
                    }
                };

                response = rpc.ImportMulti(multiAddresses.ToArray(), false);
                Assert.True(response.Result[0]["success"].Value <bool>());

                Console.WriteLine("Should not import an address with private key if is already imported");
                multiAddresses = new List <ImportMultiAddress>
                {
                    new ImportMultiAddress
                    {
                        ScriptPubKey = new ImportMultiAddress.ScriptPubKeyObject {
                            Address = key.PubKey.GetAddress(network).ToString()
                        },
                        Timestamp = "now",
                        Keys      = new string[] { key.GetWif(network).ToString() }
                    }
                };

                response = rpc.ImportMulti(multiAddresses.ToArray(), false);

                //Assert.False(response.Result[0].Value<bool>());
                Assert.False(response.Result[0]["success"].Value <bool>());

                #endregion

                #region Address + Private key + watchonly
                Console.WriteLine("Should not import an address with private key and with watchonly");
                key            = new Key();
                multiAddresses = new List <ImportMultiAddress>
                {
                    new ImportMultiAddress
                    {
                        ScriptPubKey = new ImportMultiAddress.ScriptPubKeyObject {
                            Address = key.PubKey.GetAddress(network).ToString()
                        },
                        Timestamp = "now",
                        Keys      = new string[] { key.GetWif(network).ToString() },
                        WatchOnly = true
                    }
                };

                response = rpc.ImportMulti(multiAddresses.ToArray(), false);
                Assert.False(response.Result[0]["success"].Value <bool>());
                Assert.Equal(response.Result[0]["error"]["code"].Value <int>(), -8);
                Assert.Equal(response.Result[0]["error"]["message"].Value <string>(), "Incompatibility found between watchonly and keys");
                #endregion

                #region ScriptPubKey + Private key + internal
                Console.WriteLine("Should import a scriptPubKey with internal and with private key");
                key            = new Key();
                multiAddresses = new List <ImportMultiAddress>
                {
                    new ImportMultiAddress
                    {
                        ScriptPubKey = new ImportMultiAddress.ScriptPubKeyObject {
                            ScriptPubKey = key.ScriptPubKey.ToHex()
                        },
                        Timestamp = "now",
                        Keys      = new string[] { key.GetWif(network).ToString() },
                        Internal  = true
                    }
                };

                response = rpc.ImportMulti(multiAddresses.ToArray(), false);
                Assert.True(response.Result[0]["success"].Value <bool>());
                #endregion

                #region ScriptPubKey + Private key + !internal
                Console.WriteLine("Should not import a scriptPubKey without internal and with private key");
                key            = new Key();
                multiAddresses = new List <ImportMultiAddress>
                {
                    new ImportMultiAddress
                    {
                        ScriptPubKey = new ImportMultiAddress.ScriptPubKeyObject {
                            ScriptPubKey = key.ScriptPubKey.ToHex()
                        },
                        Timestamp = "now",
                        Keys      = new string[] { key.GetWif(network).ToString() }
                    }
                };

                response = rpc.ImportMulti(multiAddresses.ToArray(), false);
                Assert.False(response.Result[0]["success"].Value <bool>());
                #endregion

                #region P2SH address
                //Blocked : Dependent on implementation of rpc.CreateMultiSig()
                #endregion

                #region P2SH + Redeem script
                //Blocked : Dependent on implementation of rpc.CreateMultiSig()
                #endregion

                #region P2SH + Redeem script + Private Keys + !Watchonly
                //Blocked : Dependent on implementation of rpc.CreateMultiSig()
                #endregion

                #region P2SH + Redeem script + Private Keys + Watchonly
                //Blocked : Dependent on implementation of rpc.CreateMultiSig()
                #endregion

                #region Address + Public key + !Internal + Wrong pubkey
                Console.WriteLine("Should not import an address with a wrong public key");
                key            = new Key();
                multiAddresses = new List <ImportMultiAddress>
                {
                    new ImportMultiAddress
                    {
                        ScriptPubKey = new ImportMultiAddress.ScriptPubKeyObject {
                            Address = key.PubKey.GetAddress(network).ToString()
                        },
                        Timestamp = "now",
                        PubKeys   = new string[] { new Key().PubKey.ToString() }
                    }
                };

                response = rpc.ImportMulti(multiAddresses.ToArray(), false);
                Assert.False(response.Result[0]["success"].Value <bool>());
                Assert.Equal(response.Result[0]["error"]["code"].Value <int>(), -5);
                Assert.Equal(response.Result[0]["error"]["message"].Value <string>(), "Consistency check failed");
                #endregion

                #region ScriptPubKey + Public key + internal + Wrong pubkey
                Console.WriteLine("Should not import a scriptPubKey with internal and with a wrong public key");
                key            = new Key();
                multiAddresses = new List <ImportMultiAddress>
                {
                    new ImportMultiAddress
                    {
                        ScriptPubKey = new ImportMultiAddress.ScriptPubKeyObject {
                            ScriptPubKey = key.ScriptPubKey.ToHex()
                        },
                        Timestamp = "now",
                        PubKeys   = new string[] { new Key().PubKey.ToString() },
                        Internal  = true
                    }
                };

                response = rpc.ImportMulti(multiAddresses.ToArray(), false);
                Assert.False(response.Result[0]["success"].Value <bool>());
                Assert.Equal(response.Result[0]["error"]["code"].Value <int>(), -5);
                Assert.Equal(response.Result[0]["error"]["message"].Value <string>(), "Consistency check failed");
                #endregion

                #region Address + Private key + !watchonly + Wrong private key
                Console.WriteLine("Should not import an address with a wrong private key");
                key            = new Key();
                multiAddresses = new List <ImportMultiAddress>
                {
                    new ImportMultiAddress
                    {
                        ScriptPubKey = new ImportMultiAddress.ScriptPubKeyObject {
                            Address = key.PubKey.GetAddress(network).ToString()
                        },
                        Timestamp = "now",
                        Keys      = new string[] { new Key().GetWif(network).ToString() }
                    }
                };

                response = rpc.ImportMulti(multiAddresses.ToArray(), false);
                Assert.False(response.Result[0]["success"].Value <bool>());
                Assert.Equal(response.Result[0]["error"]["code"].Value <int>(), -5);
                Assert.Equal(response.Result[0]["error"]["message"].Value <string>(), "Consistency check failed");
                #endregion

                #region ScriptPubKey + Private key + internal + Wrong private key
                Console.WriteLine("Should not import a scriptPubKey with internal and with a wrong private key");
                key            = new Key();
                multiAddresses = new List <ImportMultiAddress>
                {
                    new ImportMultiAddress
                    {
                        ScriptPubKey = new ImportMultiAddress.ScriptPubKeyObject {
                            ScriptPubKey = key.ScriptPubKey.ToHex()
                        },
                        Timestamp = "now",
                        Keys      = new string[] { new Key().GetWif(network).ToString() },
                        Internal  = true
                    }
                };

                response = rpc.ImportMulti(multiAddresses.ToArray(), false);
                Assert.False(response.Result[0]["success"].Value <bool>());
                Assert.Equal(response.Result[0]["error"]["code"].Value <int>(), -5);
                Assert.Equal(response.Result[0]["error"]["message"].Value <string>(), "Consistency check failed");
                #endregion

                #region Importing existing watch only address with new timestamp should replace saved timestamp.
                //TODO
                #endregion

                #region restart nodes to check for proper serialization/deserialization of watch only address
                //TODO
                #endregion
            }
        }
Example #7
0
        public static NodeBuilder Create([CallerMemberName] string caller = null)
        {
            //var builder = NodeBuilder.Create(NodeDownloadData.Litecoin.v0_17_1, Altcoins.Litecoin.Instance.Regtest, caller);

            //var builder = NodeBuilder.Create(NodeDownloadData.Viacoin.v0_15_1, Altcoins.Viacoin.Instance.Regtest, caller);

            //var builder = NodeBuilder.Create(NodeDownloadData.BCash.v0_16_2, Altcoins.BCash.Instance.Regtest, caller);

            //var builder = NodeBuilder.Create(NodeDownloadData.Dogecoin.v1_10_0, Altcoins.Dogecoin.Instance.Regtest, caller);

            //var builder = NodeBuilder.Create(NodeDownloadData.Verge.v6_0_2, Altcoins.Verge.Instance.Regtest, caller);

            //var builder = NodeBuilder.Create(NodeDownloadData.Dash.v0_13_0, Altcoins.Dash.Instance.Regtest, caller);

            //var builder = NodeBuilder.Create(NodeDownloadData.Terracoin.v0_12_2, Altcoins.Terracoin.Instance.Regtest, caller);

            //var builder = NodeBuilder.Create(NodeDownloadData.BGold.v0_15_0, Altcoins.BGold.Instance.Regtest, caller);

            //var builder = NodeBuilder.Create(NodeDownloadData.Polis.v1_4_3, Altcoins.Polis.Instance.Regtest, caller);

            //var builder = NodeBuilder.Create(NodeDownloadData.Monoeci.v0_12_2_3, Altcoins.Monoeci.Instance.Regtest, caller);

            //var builder = NodeBuilder.Create(NodeDownloadData.Colossus.v1_1_1, Altcoins.Colossus.Instance.Regtest, caller);

            //var builder = NodeBuilder.Create(NodeDownloadData.GoByte.v0_12_2_4, Altcoins.GoByte.Instance.Regtest, caller);

            //var builder = NodeBuilder.Create(NodeDownloadData.Monacoin.v0_15_1, Altcoins.Monacoin.Regtest, caller);

            //var builder = NodeBuilder.Create(NodeDownloadData.Feathercoin.v0_16_0, Altcoins.AltNetworkSets.Feathercoin.Regtest, caller);

            //var builder = NodeBuilder.Create(NodeDownloadData.Ufo.v0_16_0, Altcoins.AltNetworkSets.Ufo.Regtest, caller);

            //var builder = NodeBuilder.Create(NodeDownloadData.Groestlcoin.v2_19_1, Altcoins.AltNetworkSets.Groestlcoin.Regtest, caller);

            //var builder = NodeBuilder.Create(NodeDownloadData.Mogwai.v0_12_2, Altcoins.AltNetworkSets.Mogwai.Regtest, caller);

            //var builder = NodeBuilder.Create(NodeDownloadData.Dystem.v1_0_9_9, Altcoins.Dystem.Instance.Regtest, caller);
            //var builder = NodeBuilder.Create(NodeDownloadData.Bitcoinplus.v2_7_0, Altcoins.AltNetworkSets.Bitcoinplus.Regtest, caller);

            //var builder = NodeBuilder.Create(NodeDownloadData.Liquid.v3_14_1_21, Altcoins.AltNetworkSets.Liquid.Regtest, caller);
            //var builder = NodeBuilder.Create(NodeDownloadData.Bitcore.v0_90_9_1, Altcoins.Bitcore.Instance.Regtest, caller);
            //var builder = NodeBuilder.Create(NodeDownloadData.Gincoin.v1_1_0_0, Altcoins.Gincoin.Instance.Regtest, caller);
            //var builder = NodeBuilder.Create(NodeDownloadData.Koto.v2_0_0, Altcoins.Koto.Regtest, caller);
            //var builder = NodeBuilder.Create(NodeDownloadData.Chaincoin.v0_16_4 , Altcoins.AltNetworkSets.Chaincoin.Regtest, caller);
            //var builder = NodeBuilder.Create(NodeDownloadData.Stratis.v3_0_0, Altcoins.AltNetworkSets.Stratis.Regtest, caller);
            //var builder = NodeBuilder.Create(NodeDownloadData.ZCoin.v0_13_8_3, Altcoins.ZCoin.Instance.Regtest, caller);
            //var builder = NodeBuilder.Create(NodeDownloadData.DogeCash.v5_1_1, Altcoins.DogeCash.Instance.Regtest, caller);

            //var builder = NodeBuilder.Create(NodeDownloadData.Elements.v0_18_1_1, Altcoins.AltNetworkSets.Liquid.Regtest, caller);

            //var builder = NodeBuilder.Create(NodeDownloadData.Argoneum.v1_4_1, Altcoins.Argoneum.Instance.Regtest, caller);

            //var builder = NodeBuilder.Create(NodeDownloadData.Qtum.v0_18_3, Altcoins.Qtum.Instance.Regtest, caller);

            //var builder = NodeBuilder.Create(NodeDownloadData.MonetaryUnit.v2_1_6, Altcoins.MonetaryUnit.Instance.Regtest, caller);

            var builder = NodeBuilder.Create(NodeDownloadData.Blocknet.v4_3_0, Altcoins.Blocknet.Instance.Regtest, caller);

            //var builder = Create(NodeDownloadData.Bitcoin.v0_19_0_1, caller);

            //var builder = Create(NodeDownloadData.Bitcoin.v0_20_0, caller);
            return(builder);
        }
Example #8
0
        public void CanSyncWalletCore(NodeBuilder builder, CoreNode walletNode, WalletCreation creation)
        {
            var        rpc = builder.Nodes[0].CreateRPCClient();
            var        notifiedTransactions = new List <WalletTransaction>();
            NodesGroup connected            = CreateGroup(builder, new List <CoreNode>(new[] { walletNode }), 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);

            TestUtils.Eventually(() => wallet.Chain.Height == rpc.GetBlockCount());
            for (int i = 0; i < 9; i++)
            {
                wallet.GetNextScriptPubKey();
            }
            wallet.GetNextScriptPubKey();             //Should provoke purge
            TestUtils.Eventually(() => wallet.State == WalletState.Disconnected && wallet.ConnectedNodes == 0);
            Thread.Sleep(100);
            TestUtils.Eventually(() => wallet.ConnectedNodes == 1);

            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);
            builder.Nodes[0].GiveMoney(k, Money.Coins(1.0m));
            TestUtils.Eventually(() => wallet.GetTransactions().Count == 1 &&
                                 notifiedTransactions.Count == 1);
            builder.Nodes[0].FindBlock();
            TestUtils.Eventually(() => wallet.GetTransactions().Where(t => t.BlockInformation != null).Count() == 1 &&
                                 notifiedTransactions.Count == 2);
            builder.Nodes[0].GiveMoney(k, Money.Coins(1.5m), false);
            builder.Nodes[0].FindBlock();
            TestUtils.Eventually(() => wallet.GetTransactions().Summary.Confirmed.TransactionCount == 2 &&
                                 notifiedTransactions.Count == 3);

            builder.Nodes[0].FindBlock(30);
            Assert.True(wallet.GetTransactions().Summary.Confirmed.TransactionCount == 2);
            builder.Nodes[0].GiveMoney(k, Money.Coins(0.001m), false);
            Assert.True(wallet.GetTransactions().Summary.Confirmed.TransactionCount == 2);
            builder.Nodes[0].FindBlock(1, false);
            Assert.True(wallet.GetTransactions().Summary.Confirmed.TransactionCount == 2);
            builder.Nodes[0].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>();

            tracker.Tracker.Save(ms);
            ms.Position = 0;
            connected   = CreateGroup(builder, new List <CoreNode>(new[] { walletNode }), 1);
            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);

            //TestUtils.Eventually(() =>
            //{
            //    var fork = wallet.Chain.FindFork(wallet2._ScanLocation);
            //    return fork.Height == rpc.GetBlockCount();
            //});

            wallet2.Disconnect();
            wallet.Disconnect();
            connected.Disconnect();
        }
Example #9
0
        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();
                //////
            }
        }