예제 #1
0
        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
            _();
        }
예제 #2
0
        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);
        }
예제 #3
0
        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));
            }
        }
예제 #4
0
        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
                });
                /////////////
            }
        }
예제 #5
0
        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);
        }
예제 #6
0
        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()));
            /////
        }
예제 #7
0
        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);
            }
        }
예제 #8
0
 public SPVBehavior(BlockchainBuilder builder)
 {
     _Builder = builder;
 }