Exemplo n.º 1
0
        public void Load(string password)
        {
            Safe safe = Safe.Load(password, Config.WalletFilePath);

            if (safe.Network != Config.Network)
            {
                throw new NotSupportedException("Network in the config file differs from the netwrok in the wallet file");
            }

            if (!_walletJobTask.IsCompleted)
            {
                // then it's already running, because the default walletJobTask is completedtask
                if (_password != password)
                {
                    throw new NotSupportedException("Passwords don't match");
                }
            }
            else
            {
                // it's not running yet, let's run it
                _password = password;

                _walletJob = new WalletJob(Tor.SocksPortHandler, Tor.ControlPortClient, safe, trackDefaultSafe: false, accountsToTrack: new SafeAccount[] { AliceAccount, BobAccount });

                _walletJob.StateChanged += _walletJob_StateChanged;
                _walletJob.Tracker.TrackedTransactions.CollectionChanged += TrackedTransactions_CollectionChanged;

                _receiveResponseAlice.ExtPubKey = _walletJob.Safe.GetBitcoinExtPubKey(index: null, hdPathType: HdPathType.NonHardened, account: AliceAccount).ToWif();
                _receiveResponseBob.ExtPubKey   = _walletJob.Safe.GetBitcoinExtPubKey(index: null, hdPathType: HdPathType.NonHardened, account: BobAccount).ToWif();

                _walletJobTask = _walletJob.StartAsync(_cts.Token);

                UpdateHistoryRelatedMembers();
            }
        }
Exemplo n.º 2
0
        public void HaveFundsTest()
        {
            // load wallet
            Network      network  = Network.TestNet;
            string       path     = Path.Combine(Helpers.CommittedWalletsFolderPath, $"HaveFunds{network}.json");
            const string password = "";
            Safe         safe     = Safe.Load(password, path);

            Assert.Equal(network, safe.Network);
            Debug.WriteLine($"Unique Safe ID: {safe.UniqueId}");

            // create walletjob
            WalletJob walletJob = new WalletJob(Helpers.SocksPortHandler, Helpers.ControlPortClient, safe);

            // note some event
            WalletJob.ConnectedNodeCountChanged += WalletJob_ConnectedNodeCountChanged;
            walletJob.StateChanged += WalletJob_StateChanged;

            // start syncing
            var  cts           = new CancellationTokenSource();
            var  walletJobTask = walletJob.StartAsync(cts.Token);
            Task reportTask    = Helpers.ReportAsync(cts.Token, walletJob);

            try
            {
                // wait until synced enough to have our transaction
                while (walletJob.BestHeight.Type != HeightType.Chain || walletJob.BestHeight < 1092448)
                {
                    Task.Delay(1000).Wait();
                }

                var hasMoneyAddress = BitcoinAddress.Create("mmVZjqZjmLvxc3YFhWqYWoe5anrWVcoJcc");
                Debug.WriteLine($"Checking proper balance on {hasMoneyAddress.ToString()}");

                var record = walletJob.GetSafeHistory().FirstOrDefault();
                Assert.True(record != default(WalletHistoryRecord));

                Assert.True(record.Confirmed);
                Assert.True(record.Amount == new Money(0.1m, MoneyUnit.BTC));
                DateTimeOffset expTime;
                DateTimeOffset.TryParse("2017.03.06. 16:47:15 +00:00", out expTime);
                Assert.True(record.TimeStamp == expTime);
                Assert.True(record.TransactionId == new uint256("50898694f281ed059fa6b9d37ccf099ab261540be14fd43ce1a6d6684fbd4e94"));
            }
            finally
            {
                cts.Cancel();
                Task.WhenAll(reportTask, walletJobTask).Wait();

                WalletJob.ConnectedNodeCountChanged -= WalletJob_ConnectedNodeCountChanged;
                walletJob.StateChanged -= WalletJob_StateChanged;
            }
        }
Exemplo n.º 3
0
        public async Task LoadAsync(string password, Network network)
        {
            Safe safe = await Safe.LoadAsync(password, Global.Config.WalletFilePath, network);

            if (safe.Network != Global.Config.Network)
            {
                throw new NotSupportedException("Network in the config file differs from the network in the wallet file");
            }

            if (!_walletJobTask.IsCompleted)
            {
                // then it's already running, because the default walletJobTask is completedtask
                if (_password != password)
                {
                    throw new NotSupportedException("Passwords don't match");
                }
            }
            else
            {
                // it's not running yet, let's run it
                _password = password;

                WalletJob = new WalletJob();
                await WalletJob.InitializeAsync(Tor.SocksPortHandler, Tor.ControlPortClient, safe, trackDefaultSafe : false, accountsToTrack : new SafeAccount[] { AliceAccount, BobAccount });

                WalletJob.StateChanged += WalletJob_StateChangedAsync;
                WalletJob.MemPoolJob.NewTransaction += MemPoolJob_NewTransactionAsync;
                WalletJob.HeaderHeightChanged       += WalletJob_HeaderHeightChangedAsync;
                (await WalletJob.GetTrackerAsync()).BestHeightChanged += WalletWrapper_BestHeightChangedAsync;
                WalletJob.ConnectedNodeCountChanged         += WalletJob_ConnectedNodeCountChangedAsync;
                WalletJob.CoinJoinService.ParametersChanged += CoinJoinService_ParametersChangedAsync;


                (await WalletJob.GetTrackerAsync()).TrackedTransactions.CollectionChanged += TrackedTransactions_CollectionChangedAsync;

                _receiveResponseAlice.ExtPubKey = WalletJob.Safe.GetBitcoinExtPubKey(index: null, hdPathType: HdPathType.NonHardened, account: AliceAccount).ToWif();
                _receiveResponseBob.ExtPubKey   = WalletJob.Safe.GetBitcoinExtPubKey(index: null, hdPathType: HdPathType.NonHardened, account: BobAccount).ToWif();

                _walletJobTask = WalletJob.StartAsync(_cts.Token);

                await UpdateHistoryRelatedMembersAsync();
                await InitializeCoinJoinServiceAsync(network);
            }
        }
Exemplo n.º 4
0
        public static async Task ReportAsync(CancellationToken ctsToken, WalletJob walletJob)
        {
            while (true)
            {
                if (ctsToken.IsCancellationRequested)
                {
                    return;
                }
                try
                {
                    await Task.Delay(1000, ctsToken).ContinueWith(t => { });

                    var result = await walletJob.TryGetHeaderHeightAsync();

                    var currHeaderHeight = result.Height;
                    if (result.Success)
                    {
                        // HEADERCHAIN
                        if (currHeaderHeight.Type == HeightType.Chain &&
                            (_prevHeaderHeight == Height.Unknown || currHeaderHeight > _prevHeaderHeight))
                        {
                            Debug.WriteLine($"HeaderChain height: {currHeaderHeight}");
                            _prevHeaderHeight = currHeaderHeight;
                        }

                        // TRACKER
                        var currHeight = await walletJob.GetBestHeightAsync();

                        if (currHeight.Type == HeightType.Chain &&
                            (_prevHeight == Height.Unknown || currHeight > _prevHeight))
                        {
                            Debug.WriteLine($"Tracker height: {currHeight} left: {currHeaderHeight.Value - currHeight.Value}");
                            _prevHeight = currHeight;
                        }
                    }
                }
                catch
                {
                    // ignored
                }
            }
        }
Exemplo n.º 5
0
        public static void ReportFullHistory(WalletJob walletJob)
        {
            var history = walletJob.GetSafeHistory();

            if (!history.Any())
            {
                Debug.WriteLine("Wallet has no history...");
                return;
            }

            Debug.WriteLine("");
            Debug.WriteLine("---------------------------------------------------------------------------");
            Debug.WriteLine(@"Date			Amount		Confirmed	Transaction Id");
            Debug.WriteLine("---------------------------------------------------------------------------");

            foreach (var record in history)
            {
                Debug.WriteLine($@"{record.TimeStamp.DateTime}	{record.Amount}	{record.Confirmed}		{record.TransactionId}");
            }
            Debug.WriteLine("");
        }
Exemplo n.º 6
0
        public void SendsFailGracefullyTest()
        {
            Network      network  = Network.TestNet;
            SafeAccount  account  = new SafeAccount(1);
            string       path     = Path.Combine(Helpers.CommittedWalletsFolderPath, $"Sending{network}.json");
            const string password = "";
            Safe         safe     = Safe.Load(password, path);

            Assert.Equal(network, safe.Network);
            Debug.WriteLine($"Unique Safe ID: {safe.UniqueId}");

            // create walletjob
            WalletJob walletJob = new WalletJob(Helpers.SocksPortHandler, Helpers.ControlPortClient, safe, trackDefaultSafe: false, accountsToTrack: account);

            // note some event
            WalletJob.ConnectedNodeCountChanged += delegate
            {
                if (WalletJob.MaxConnectedNodeCount == WalletJob.ConnectedNodeCount)
                {
                    Debug.WriteLine(
                        $"{nameof(WalletJob.MaxConnectedNodeCount)} reached: {WalletJob.MaxConnectedNodeCount}");
                }
                else
                {
                    Debug.WriteLine($"{nameof(WalletJob.ConnectedNodeCount)}: {WalletJob.ConnectedNodeCount}");
                }
            };
            walletJob.StateChanged += delegate
            {
                Debug.WriteLine($"{nameof(walletJob.State)}: {walletJob.State}");
            };

            // start syncing
            var  cts           = new CancellationTokenSource();
            var  walletJobTask = walletJob.StartAsync(cts.Token);
            Task reportTask    = Helpers.ReportAsync(cts.Token, walletJob);

            try
            {
                // wait until blocks are synced
                while (walletJob.State <= WalletState.SyncingMemPool)
                {
                    Task.Delay(1000).Wait();
                }

                var history = walletJob.GetSafeHistory(account);
                foreach (var record in history)
                {
                    Debug.WriteLine($"{record.TransactionId} {record.Amount} {record.Confirmed}");
                }

                var receive = walletJob.GetUnusedScriptPubKeys(account, HdPathType.Receive).FirstOrDefault();

                IDictionary <Coin, bool> unspentCoins;
                var bal = walletJob.GetBalance(out unspentCoins, account);

                // Not enough fee
                Money amountToSend = (bal.Confirmed + bal.Unconfirmed) - new Money(1m, MoneyUnit.Satoshi);
                var   res          = walletJob.BuildTransactionAsync(receive, amountToSend, FeeType.Low, account,
                                                                     allowUnconfirmed: true).Result;
                Assert.True(res.Success == false);
                Assert.True(res.FailingReason != "");
                Debug.WriteLine($"Expected FailingReason: {res.FailingReason}");

                // That's not how you spend all
                amountToSend = (bal.Confirmed + bal.Unconfirmed);
                res          = walletJob.BuildTransactionAsync(receive, amountToSend, FeeType.Low, account,
                                                               allowUnconfirmed: true).Result;
                Assert.True(res.Success == false);
                Assert.True(res.FailingReason != "");
                Debug.WriteLine($"Expected FailingReason: {res.FailingReason}");

                // Too much
                amountToSend = (bal.Confirmed + bal.Unconfirmed) + new Money(1, MoneyUnit.BTC);
                res          = walletJob.BuildTransactionAsync(receive, amountToSend, FeeType.Low, account,
                                                               allowUnconfirmed: true).Result;
                Assert.True(res.Success == false);
                Assert.True(res.FailingReason != "");
                Debug.WriteLine($"Expected FailingReason: {res.FailingReason}");

                // Minus
                amountToSend = new Money(-1m, MoneyUnit.BTC);
                res          = walletJob.BuildTransactionAsync(receive, amountToSend, FeeType.Low, account,
                                                               allowUnconfirmed: true).Result;
                Assert.True(res.Success == false);
                Assert.True(res.FailingReason != "");
                Debug.WriteLine($"Expected FailingReason: {res.FailingReason}");

                // Default account is disabled
                amountToSend = (bal.Confirmed + bal.Unconfirmed) / 2;
                Assert.ThrowsAsync <NotSupportedException>(async() => await walletJob.BuildTransactionAsync(receive, amountToSend, FeeType.Low,
                                                                                                            allowUnconfirmed: true).ConfigureAwait(false)).ContinueWith(t => {}).Wait();

                // No such account
                amountToSend = (bal.Confirmed + bal.Unconfirmed) / 2;
                Assert.ThrowsAsync <NotSupportedException>(async() => await walletJob.BuildTransactionAsync(receive, amountToSend, FeeType.Low, new SafeAccount(23421),
                                                                                                            allowUnconfirmed: true).ConfigureAwait(false)).ContinueWith(t => { }).Wait();
            }
            finally
            {
                cts.Cancel();
                Task.WhenAll(reportTask, walletJobTask).Wait();
            }
        }
Exemplo n.º 7
0
        public void MaxAmountTest()
        {
            Network      network  = Network.TestNet;
            SafeAccount  account  = new SafeAccount(1);
            string       path     = Path.Combine(Helpers.CommittedWalletsFolderPath, $"Sending{network}.json");
            const string password = "";
            Safe         safe     = Safe.Load(password, path);

            Assert.Equal(network, safe.Network);
            Debug.WriteLine($"Unique Safe ID: {safe.UniqueId}");

            // create walletjob
            WalletJob walletJob = new WalletJob(Helpers.SocksPortHandler, Helpers.ControlPortClient, safe, trackDefaultSafe: false, accountsToTrack: account);

            // note some event
            WalletJob.ConnectedNodeCountChanged += delegate
            {
                if (WalletJob.MaxConnectedNodeCount == WalletJob.ConnectedNodeCount)
                {
                    Debug.WriteLine(
                        $"{nameof(WalletJob.MaxConnectedNodeCount)} reached: {WalletJob.MaxConnectedNodeCount}");
                }
                else
                {
                    Debug.WriteLine($"{nameof(WalletJob.ConnectedNodeCount)}: {WalletJob.ConnectedNodeCount}");
                }
            };
            walletJob.StateChanged += delegate
            {
                Debug.WriteLine($"{nameof(walletJob.State)}: {walletJob.State}");
            };

            // start syncing
            var  cts           = new CancellationTokenSource();
            var  walletJobTask = walletJob.StartAsync(cts.Token);
            Task reportTask    = Helpers.ReportAsync(cts.Token, walletJob);

            try
            {
                // wait until blocks are synced
                while (walletJob.State <= WalletState.SyncingMemPool)
                {
                    Task.Delay(1000).Wait();
                }

                var receive = walletJob.GetUnusedScriptPubKeys(account, HdPathType.Receive).FirstOrDefault();

                IDictionary <Coin, bool> unspentCoins;
                var bal = walletJob.GetBalance(out unspentCoins, account);

                var res = walletJob.BuildTransactionAsync(receive, Money.Zero, FeeType.Low, account,
                                                          allowUnconfirmed: true).Result;

                Assert.True(res.Success);
                Assert.True(res.FailingReason == "");
                Debug.WriteLine($"Fee: {res.Fee}");
                Debug.WriteLine($"FeePercentOfSent: {res.FeePercentOfSent} %");
                Debug.WriteLine($"SpendsUnconfirmed: {res.SpendsUnconfirmed}");
                Debug.WriteLine($"Transaction: {res.Transaction}");

                var foundReceive = false;
                Assert.InRange(res.Transaction.Outputs.Count, 1, 2);
                foreach (var output in res.Transaction.Outputs)
                {
                    if (output.ScriptPubKey == receive)
                    {
                        foundReceive = true;
                        Assert.True(bal.Confirmed + bal.Unconfirmed - res.Fee == output.Value);
                    }
                }
                Assert.True(foundReceive);

                var txProbArrived = false;
                var prevCount     = walletJob.Tracker.TrackedTransactions.Count;
                walletJob.Tracker.TrackedTransactions.CollectionChanged += delegate
                {
                    var actCount = walletJob.Tracker.TrackedTransactions.Count;
                    // if arrived
                    if (actCount > prevCount)
                    {
                        txProbArrived = true;
                    }
                    else
                    {
                        prevCount = actCount;
                    }
                };

                var sendRes = walletJob.SendTransactionAsync(res.Transaction).Result;
                Assert.True(sendRes.Success);
                Assert.True(sendRes.FailingReason == "");

                while (txProbArrived == false)
                {
                    Debug.WriteLine("Waiting for transaction...");
                    Task.Delay(1000).Wait();
                }

                Debug.WriteLine("TrackedTransactions collection changed");
                Assert.True(walletJob.Tracker.TrackedTransactions.Any(x => x.Transaction.GetHash() == res.Transaction.GetHash()));
                Debug.WriteLine("Transaction arrived");
            }
            finally
            {
                cts.Cancel();
                Task.WhenAll(reportTask, walletJobTask).Wait();
            }
        }
Exemplo n.º 8
0
        public void SyncingTest(string networkString)
        {
            // load wallet
            Network      network  = networkString == "TestNet"? Network.TestNet:Network.Main;
            string       path     = $"Wallets/Empty{network}.json";
            const string password = "";
            Safe         safe;

            if (File.Exists(path))
            {
                safe = Safe.Load(password, path);
            }
            else
            {
                Mnemonic mnemonic;
                safe = Safe.Create(out mnemonic, password, path, network);
            }

            Debug.WriteLine($"Unique Safe ID: {safe.UniqueId}");

            // create walletjob
            WalletJob walletJob = new WalletJob(Helpers.SocksPortHandler, Helpers.ControlPortClient, safe);

            // note some event
            _fullyConnected = false;
            _syncedOnce     = false;
            WalletJob.ConnectedNodeCountChanged += WalletJob_ConnectedNodeCountChanged;
            walletJob.StateChanged += WalletJob_StateChanged;

            Assert.True(walletJob.SafeAccounts.Count == 0);
            Assert.True(WalletJob.ConnectedNodeCount == 0);
            var allTxCount = walletJob.Tracker.TrackedTransactions.Count;

            Assert.True(allTxCount == 0);
            Assert.True(!walletJob.GetSafeHistory().Any());
            Assert.True(walletJob.State == WalletState.NotStarted);
            Assert.True(walletJob.TracksDefaultSafe);

            // start syncing
            var cts           = new CancellationTokenSource();
            var walletJobTask = walletJob.StartAsync(cts.Token);

            Assert.True(walletJob.State != WalletState.NotStarted);
            Task reportTask = Helpers.ReportAsync(cts.Token, walletJob);

            try
            {
                // wait until fully synced and connected
                while (!_fullyConnected)
                {
                    Task.Delay(10).Wait();
                }

                while (!_syncedOnce)
                {
                    Task.Delay(1000).Wait();
                }

                Assert.True(walletJob.State == WalletState.Synced);
                Assert.True(walletJob.CreationHeight != Height.Unknown);
                Assert.True(walletJob.Tracker.TrackedTransactions.Count == 0);
                Assert.True(!walletJob.GetSafeHistory().Any());
                Height headerHeight;
                Assert.True(WalletJob.TryGetHeaderHeight(out headerHeight));
                var expectedBlockCount = headerHeight.Value - walletJob.CreationHeight.Value + 1;
                Assert.True(walletJob.Tracker.BlockCount == expectedBlockCount);
                Assert.True(walletJob.Tracker.TrackedScriptPubKeys.Count > 0);
                Assert.True(walletJob.Tracker.TrackedTransactions.Count == 0);
            }
            finally
            {
                cts.Cancel();
                Task.WhenAll(reportTask, walletJobTask).Wait();

                WalletJob.ConnectedNodeCountChanged -= WalletJob_ConnectedNodeCountChanged;
                walletJob.StateChanged -= WalletJob_StateChanged;
            }
        }
Exemplo n.º 9
0
        public void RealHistoryTest()
        {
            // load wallet
            Network      network  = Network.TestNet;
            string       path     = Path.Combine(Helpers.CommittedWalletsFolderPath, $"HiddenWallet.json");
            const string password = "";

            // I change it because I am using a very old wallet to test
            Safe.EarliestPossibleCreationTime = DateTimeOffset.ParseExact("2016-12-18", "yyyy-MM-dd", CultureInfo.InvariantCulture);
            Safe safe = Safe.Load(password, path);

            Assert.Equal(network, safe.Network);
            Debug.WriteLine($"Unique Safe ID: {safe.UniqueId}");

            // create walletjob
            WalletJob walletJob = new WalletJob(Helpers.SocksPortHandler, Helpers.ControlPortClient, safe)
            {
                MaxCleanAddressCount = 79
            };

            // note some event
            WalletJob.ConnectedNodeCountChanged += WalletJob_ConnectedNodeCountChanged;
            _syncedOnce             = false;
            walletJob.StateChanged += WalletJob_StateChanged;

            // start syncing
            var  cts           = new CancellationTokenSource();
            var  walletJobTask = walletJob.StartAsync(cts.Token);
            Task reportTask    = Helpers.ReportAsync(cts.Token, walletJob);

            try
            {
                // wait until fully synced
                while (!_syncedOnce)
                {
                    Task.Delay(1000).Wait();
                }

                Helpers.ReportFullHistory(walletJob);

                // 0. Query all operations, grouped our used safe addresses
                int MinUnusedKeyNum = 37;
                Dictionary <BitcoinAddress, List <BalanceOperation> > operationsPerAddresses = Helpers.QueryOperationsPerSafeAddressesAsync(new QBitNinjaClient(safe.Network), safe, MinUnusedKeyNum).Result;

                Dictionary <uint256, List <BalanceOperation> > operationsPerTransactions = QBitNinjaJutsus.GetOperationsPerTransactions(operationsPerAddresses);

                // 3. Create history records from the transactions
                // History records is arbitrary data we want to show to the user
                var txHistoryRecords = new List <Tuple <DateTimeOffset, Money, int, uint256> >();
                foreach (var elem in operationsPerTransactions)
                {
                    var amount = Money.Zero;
                    foreach (var op in elem.Value)
                    {
                        amount += op.Amount;
                    }

                    var firstOp = elem.Value.First();

                    txHistoryRecords
                    .Add(new Tuple <DateTimeOffset, Money, int, uint256>(
                             firstOp.FirstSeen,
                             amount,
                             firstOp.Confirmations,
                             elem.Key));
                }

                // 4. Order the records by confirmations and time (Simply time does not work, because of a QBitNinja issue)
                var qBitHistoryRecords = txHistoryRecords
                                         .OrderByDescending(x => x.Item3) // Confirmations
                                         .ThenBy(x => x.Item1);           // FirstSeen

                var fullSpvHistoryRecords = walletJob.GetSafeHistory();

                // This won't be equal QBit doesn't show us this transaction: 2017.01.04. 16:24:49	0.00000000	True		77b10ff78aab2e41764a05794c4c464922c73f0c23356190429833ce68fd7be9
                // Assert.Equal(qBitHistoryRecords.Count(), fullSpvHistoryRecords.Count());

                HashSet <WalletHistoryRecord> qBitFoundItToo = new HashSet <WalletHistoryRecord>();
                // Assert all record found by qbit also found by spv and they are identical
                foreach (var record in qBitHistoryRecords)
                {
                    // Item2 is the Amount
                    WalletHistoryRecord found = fullSpvHistoryRecords.FirstOrDefault(x => x.TransactionId == record.Item4);
                    Assert.True(found != default(WalletHistoryRecord));
                    Assert.True(found.TimeStamp.Equals(record.Item1));
                    Assert.True(found.Confirmed.Equals(record.Item3 > 0));
                    Assert.True(found.Amount.Equals(record.Item2));
                    qBitFoundItToo.Add(found);
                }

                foreach (var record in fullSpvHistoryRecords)
                {
                    if (!qBitFoundItToo.Contains(record))
                    {
                        Assert.True(null == qBitHistoryRecords.FirstOrDefault(x => x.Item4 == record.TransactionId));
                        Debug.WriteLine($@"QBitNinja failed to find, but SPV found it: {record.TimeStamp.DateTime}	{record.Amount}	{record.Confirmed}		{record.TransactionId}");
                    }
                }
            }
            finally
            {
                cts.Cancel();
                Task.WhenAll(reportTask, walletJobTask).Wait();

                WalletJob.ConnectedNodeCountChanged -= WalletJob_ConnectedNodeCountChanged;
                walletJob.StateChanged -= WalletJob_StateChanged;
            }
        }
Exemplo n.º 10
0
        public async Task SyncingTestAsync()
        {
            // load wallet
            Network      network  = Network.Main;
            string       path     = $"Wallets/Empty{network}.json";
            const string password = "";
            Safe         safe     = File.Exists(path) ? await Safe.LoadAsync(password, path) : (await Safe.CreateAsync(password, path, network)).Safe;

            Debug.WriteLine($"Unique Safe ID: {safe.UniqueId}");

            // create walletjob
            WalletJob walletJob = new WalletJob();
            await walletJob.InitializeAsync(Helpers.SocksPortHandler, Helpers.ControlPortClient, safe);

            // note some event
            _fullyConnected = false;
            _syncedOnce     = false;
            walletJob.ConnectedNodeCountChanged += WalletJob_ConnectedNodeCountChanged;
            walletJob.StateChanged += WalletJob_StateChanged;

            Assert.Empty(walletJob.SafeAccounts);
            Assert.Equal(0, walletJob.ConnectedNodeCount);
            Assert.Empty((await walletJob.GetTrackerAsync()).TrackedTransactions);
            Assert.Empty(await walletJob.GetSafeHistoryAsync());
            Assert.Equal(WalletState.NotStarted, walletJob.State);
            Assert.True(walletJob.TracksDefaultSafe);

            // start syncing
            var cts           = new CancellationTokenSource();
            var walletJobTask = walletJob.StartAsync(cts.Token);

            Assert.NotEqual(WalletState.NotStarted, walletJob.State);
            Task reportTask = Helpers.ReportAsync(cts.Token, walletJob);

            try
            {
                // wait until fully synced and connected
                while (!_fullyConnected)
                {
                    await Task.Delay(10);
                }

                while (!_syncedOnce)
                {
                    await Task.Delay(1000);
                }

                Assert.Equal(WalletState.Synced, walletJob.State);
                Assert.NotEqual(Height.Unknown, await walletJob.GetCreationHeightAsync());
                Assert.Empty((await walletJob.GetTrackerAsync()).TrackedTransactions);
                Assert.Empty(await walletJob.GetSafeHistoryAsync());
                var headerHeightResult = await walletJob.TryGetHeaderHeightAsync();

                Assert.True(headerHeightResult.Success);
                var expectedBlockCount = headerHeightResult.Height.Value - (await walletJob.GetCreationHeightAsync()).Value + 1;
                Assert.Equal(expectedBlockCount, (await walletJob.GetTrackerAsync()).BlockCount);
                Assert.NotEmpty((await walletJob.GetTrackerAsync()).TrackedScriptPubKeys);
                Assert.Empty((await walletJob.GetTrackerAsync()).TrackedTransactions);
            }
            finally
            {
                cts?.Cancel();
                await Task.WhenAll(reportTask, walletJobTask);

                walletJob.ConnectedNodeCountChanged -= WalletJob_ConnectedNodeCountChanged;
                walletJob.StateChanged -= WalletJob_StateChanged;
                cts?.Dispose();
                reportTask?.Dispose();
                walletJobTask?.Dispose();
            }
        }
Exemplo n.º 11
0
        public async Task RealHistoryTestAsync()
        {
            // load wallet
            Network      network  = Network.TestNet;
            string       path     = Path.Combine(Helpers.CommittedWalletsFolderPath, $"HiddenWallet.json");
            const string password = "";

            // I change it because I am using a very old wallet to test
            Safe.EarliestPossibleCreationTime = DateTimeOffset.ParseExact("2016-12-18", "yyyy-MM-dd", CultureInfo.InvariantCulture);
            Safe safe = await Safe.LoadAsync(password, path);

            Assert.Equal(network, safe.Network);
            Debug.WriteLine($"Unique Safe ID: {safe.UniqueId}");

            // create walletjob
            var walletJob = new WalletJob
            {
                MaxCleanAddressCount = 79
            };
            await walletJob.InitializeAsync(Helpers.SocksPortHandler, Helpers.ControlPortClient, safe);

            // note some event
            walletJob.ConnectedNodeCountChanged += WalletJob_ConnectedNodeCountChanged;
            _syncedOnce             = false;
            walletJob.StateChanged += WalletJob_StateChanged;

            Assert.Empty(walletJob.SafeAccounts);
            Assert.Equal(0, walletJob.ConnectedNodeCount);
            Assert.Equal(WalletState.NotStarted, walletJob.State);
            Assert.True(walletJob.TracksDefaultSafe);

            // start syncing
            var cts           = new CancellationTokenSource();
            var walletJobTask = walletJob.StartAsync(cts.Token);

            Assert.NotEqual(WalletState.NotStarted, walletJob.State);
            Task reportTask = Helpers.ReportAsync(cts.Token, walletJob);


            try
            {
                // wait until fully synced
                while (!_syncedOnce)
                {
                    await Task.Delay(1000);
                }

                Assert.Equal(WalletState.Synced, walletJob.State);
                Assert.NotEqual(Height.Unknown, await walletJob.GetCreationHeightAsync());
                var headerHeightResult = await walletJob.TryGetHeaderHeightAsync();

                Assert.True(headerHeightResult.Success);
                Assert.NotEmpty((await walletJob.GetTrackerAsync()).TrackedScriptPubKeys);

                await Helpers.ReportFullHistoryAsync(walletJob);

                // 0. Query all operations, grouped our used safe addresses
                int MinUnusedKeyNum = 74;
                Dictionary <BitcoinAddress, List <BalanceOperation> > operationsPerAddresses = await Helpers.QueryOperationsPerSafeAddressesAsync(new QBitNinjaClient(safe.Network), safe, MinUnusedKeyNum);

                Dictionary <uint256, List <BalanceOperation> > operationsPerTransactions = QBitNinjaJutsus.GetOperationsPerTransactions(operationsPerAddresses);

                // 3. Create history records from the transactions
                // History records is arbitrary data we want to show to the user
                var txHistoryRecords = new List <(DateTimeOffset FirstSeen, Money Amount, int Confirmations, uint256 TxId)>();
                foreach (var elem in operationsPerTransactions)
                {
                    var amount = Money.Zero;
                    foreach (var op in elem.Value)
                    {
                        amount += op.Amount;
                    }

                    var firstOp = elem.Value.First();

                    txHistoryRecords
                    .Add((
                             firstOp.FirstSeen,
                             amount,
                             firstOp.Confirmations,
                             elem.Key));
                }

                // 4. Order the records by confirmations and time (Simply time does not work, because of a QBitNinja issue)
                var qBitHistoryRecords = txHistoryRecords
                                         .OrderByDescending(x => x.Confirmations) // Confirmations
                                         .ThenBy(x => x.FirstSeen);               // FirstSeen

                var fullSpvHistoryRecords = await walletJob.GetSafeHistoryAsync();

                // This won't be equal QBit doesn't show us this transaction: 2017.01.04. 16:24:49	0.00000000	True		77b10ff78aab2e41764a05794c4c464922c73f0c23356190429833ce68fd7be9
                // Assert.Equal(qBitHistoryRecords.Count(), fullSpvHistoryRecords.Count());

                HashSet <WalletHistoryRecord> qBitFoundItToo = new HashSet <WalletHistoryRecord>();
                // Assert all record found by qbit also found by spv and they are identical
                foreach (var record in qBitHistoryRecords)
                {
                    WalletHistoryRecord found = fullSpvHistoryRecords.FirstOrDefault(x => x.TransactionId == record.TxId);

                    Assert.NotEqual(default, found);
Exemplo n.º 12
0
        public async Task SendTestAsync()
        {
            Network      network  = Network.TestNet;
            SafeAccount  account  = new SafeAccount(1);
            string       path     = Path.Combine(Helpers.CommittedWalletsFolderPath, $"Sending{network}.json");
            const string password = "";
            Safe         safe     = await Safe.LoadAsync(password, path);

            Assert.Equal(network, safe.Network);
            Debug.WriteLine($"Unique Safe ID: {safe.UniqueId}");

            // create walletjob
            WalletJob walletJob = new WalletJob();
            await walletJob.InitializeAsync(Helpers.SocksPortHandler, Helpers.ControlPortClient, safe, trackDefaultSafe : false, accountsToTrack : account);

            // note some event
            walletJob.ConnectedNodeCountChanged += WalletJob_ConnectedNodeCountChanged;
            walletJob.StateChanged += WalletJob_StateChanged;

            // start syncing
            var  cts           = new CancellationTokenSource();
            var  walletJobTask = walletJob.StartAsync(cts.Token);
            Task reportTask    = Helpers.ReportAsync(cts.Token, walletJob);

            try
            {
                // wait until blocks are synced
                while (walletJob.State <= WalletState.SyncingMemPool)
                {
                    await Task.Delay(1000);
                }

                foreach (var r in await walletJob.GetSafeHistoryAsync(account))
                {
                    Debug.WriteLine(r.TransactionId);
                }

                # region Basic

                var receive = (await walletJob.GetUnusedScriptPubKeysAsync(AddressType.Pay2WitnessPublicKeyHash, account, HdPathType.Receive)).FirstOrDefault();

                var getBalanceResult = await walletJob.GetBalanceAsync(account);

                var   bal          = getBalanceResult.Available;
                Money amountToSend = (bal.Confirmed + bal.Unconfirmed) / 2;
                var   res          = await walletJob.BuildTransactionAsync(receive, amountToSend, FeeType.Low, account,
                                                                           allowUnconfirmed : true);

                Assert.True(res.Success);
                Assert.Empty(res.FailingReason);
                Assert.Equal(receive, res.ActiveOutput.ScriptPubKey);
                Assert.Equal(amountToSend, res.ActiveOutput.Value);
                Assert.NotNull(res.ChangeOutput);
                Assert.Contains(res.Transaction.Outputs, x => x.Value == res.ChangeOutput.Value);
                Debug.WriteLine($"Fee: {res.Fee}");
                Debug.WriteLine($"FeePercentOfSent: {res.FeePercentOfSent} %");
                Debug.WriteLine($"SpendsUnconfirmed: {res.SpendsUnconfirmed}");
                Debug.WriteLine($"Active Output: {res.ActiveOutput.Value.ToString(false, true)} {res.ActiveOutput.ScriptPubKey.GetDestinationAddress(network)}");
                Debug.WriteLine($"Change Output: {res.ChangeOutput.Value.ToString(false, true)} {res.ChangeOutput.ScriptPubKey.GetDestinationAddress(network)}");
                Debug.WriteLine($"TxId: {res.Transaction.GetHash()}");

                var foundReceive = false;
                Assert.InRange(res.Transaction.Outputs.Count, 1, 2);
                foreach (var output in res.Transaction.Outputs)
                {
                    if (output.ScriptPubKey == receive)
                    {
                        foundReceive = true;
                        Assert.Equal(amountToSend, output.Value);
                    }
                }
                Assert.True(foundReceive);

                _txProbArrived    = false;
                _prevCount        = (await walletJob.GetTrackerAsync()).TrackedTransactions.Count;
                _currentWalletJob = walletJob;
                (await walletJob.GetTrackerAsync()).TrackedTransactions.CollectionChanged += TrackedTransactions_CollectionChangedAsync;

                var sendRes = await walletJob.SendTransactionAsync(res.Transaction);

                Assert.True(sendRes.Success);
                Assert.Empty(sendRes.FailingReason);

                while (!_txProbArrived)
                {
                    Debug.WriteLine("Waiting for transaction...");
                    await Task.Delay(1000);
                }

                Debug.WriteLine("TrackedTransactions collection changed");
                Assert.Contains((await walletJob.GetTrackerAsync()).TrackedTransactions, x => x.Transaction.GetHash() == res.Transaction.GetHash());
                Debug.WriteLine("Transaction arrived");


                receive = (await walletJob.GetUnusedScriptPubKeysAsync(AddressType.Pay2WitnessPublicKeyHash, account, HdPathType.Receive)).FirstOrDefault();

                bal          = (await walletJob.GetBalanceAsync(account)).Available;
                amountToSend = (bal.Confirmed + bal.Unconfirmed) / 2;

                #endregion

                #region SubtractFeeFromAmount

                receive = (await walletJob.GetUnusedScriptPubKeysAsync(AddressType.Pay2WitnessPublicKeyHash, account, HdPathType.Receive)).FirstOrDefault();

                getBalanceResult = await walletJob.GetBalanceAsync(account);

                bal          = getBalanceResult.Available;
                amountToSend = (bal.Confirmed + bal.Unconfirmed) / 2;
                res          = await walletJob.BuildTransactionAsync(receive, amountToSend, FeeType.Low, account,
                                                                     allowUnconfirmed : true,
                                                                     subtractFeeFromAmount : true);

                Assert.True(res.Success);
                Assert.Empty(res.FailingReason);
                Assert.Equal(receive, res.ActiveOutput.ScriptPubKey);
                Assert.Equal(amountToSend - res.Fee, res.ActiveOutput.Value);
                Assert.NotNull(res.ChangeOutput);
                Assert.Contains(res.Transaction.Outputs, x => x.Value == res.ChangeOutput.Value);
                Debug.WriteLine($"Fee: {res.Fee}");
                Debug.WriteLine($"FeePercentOfSent: {res.FeePercentOfSent} %");
                Debug.WriteLine($"SpendsUnconfirmed: {res.SpendsUnconfirmed}");
                Debug.WriteLine($"Active Output: {res.ActiveOutput.Value.ToString(false, true)} {res.ActiveOutput.ScriptPubKey.GetDestinationAddress(network)}");
                Debug.WriteLine($"Change Output: {res.ChangeOutput.Value.ToString(false, true)} {res.ChangeOutput.ScriptPubKey.GetDestinationAddress(network)}");
                Debug.WriteLine($"TxId: {res.Transaction.GetHash()}");

                foundReceive = false;
                Assert.InRange(res.Transaction.Outputs.Count, 1, 2);
                foreach (var output in res.Transaction.Outputs)
                {
                    if (output.ScriptPubKey == receive)
                    {
                        foundReceive = true;
                        Assert.Equal(amountToSend - res.Fee, output.Value);
                    }
                }
                Assert.True(foundReceive);

                #endregion

                #region CustomChange

                receive = (await walletJob.GetUnusedScriptPubKeysAsync(AddressType.Pay2WitnessPublicKeyHash, account, HdPathType.Receive)).FirstOrDefault();
                var customChange = new Key().ScriptPubKey;

                getBalanceResult = await walletJob.GetBalanceAsync(account);

                bal          = getBalanceResult.Available;
                amountToSend = (bal.Confirmed + bal.Unconfirmed) / 2;
                res          = await walletJob.BuildTransactionAsync(receive, amountToSend, FeeType.Low, account,
                                                                     allowUnconfirmed : true,
                                                                     subtractFeeFromAmount : true,
                                                                     customChangeScriptPubKey : customChange);

                Assert.True(res.Success);
                Assert.Empty(res.FailingReason);
                Assert.Equal(receive, res.ActiveOutput.ScriptPubKey);
                Assert.Equal(amountToSend - res.Fee, res.ActiveOutput.Value);
                Assert.NotNull(res.ChangeOutput);
                Assert.Equal(customChange, res.ChangeOutput.ScriptPubKey);
                Assert.Contains(res.Transaction.Outputs, x => x.Value == res.ChangeOutput.Value);
                Debug.WriteLine($"Fee: {res.Fee}");
                Debug.WriteLine($"FeePercentOfSent: {res.FeePercentOfSent} %");
                Debug.WriteLine($"SpendsUnconfirmed: {res.SpendsUnconfirmed}");
                Debug.WriteLine($"Active Output: {res.ActiveOutput.Value.ToString(false, true)} {res.ActiveOutput.ScriptPubKey.GetDestinationAddress(network)}");
                Debug.WriteLine($"Change Output: {res.ChangeOutput.Value.ToString(false, true)} {res.ChangeOutput.ScriptPubKey.GetDestinationAddress(network)}");
                Debug.WriteLine($"TxId: {res.Transaction.GetHash()}");

                foundReceive = false;
                Assert.InRange(res.Transaction.Outputs.Count, 1, 2);
                foreach (var output in res.Transaction.Outputs)
                {
                    if (output.ScriptPubKey == receive)
                    {
                        foundReceive = true;
                        Assert.Equal(amountToSend - res.Fee, output.Value);
                    }
                }
                Assert.True(foundReceive);

                #endregion

                #region LowFee

                res = await walletJob.BuildTransactionAsync(receive, amountToSend, FeeType.Low, account,
                                                            allowUnconfirmed : true);

                Assert.True(res.Success);
                Assert.Empty(res.FailingReason);
                Assert.Equal(receive, res.ActiveOutput.ScriptPubKey);
                Assert.Equal(amountToSend, res.ActiveOutput.Value);
                Assert.NotNull(res.ChangeOutput);
                Assert.Contains(res.Transaction.Outputs, x => x.Value == res.ChangeOutput.Value);
                Debug.WriteLine($"Fee: {res.Fee}");
                Debug.WriteLine($"FeePercentOfSent: {res.FeePercentOfSent} %");
                Debug.WriteLine($"SpendsUnconfirmed: {res.SpendsUnconfirmed}");
                Debug.WriteLine($"Active Output: {res.ActiveOutput.Value.ToString(false, true)} {res.ActiveOutput.ScriptPubKey.GetDestinationAddress(network)}");
                Debug.WriteLine($"Change Output: {res.ChangeOutput.Value.ToString(false, true)} {res.ChangeOutput.ScriptPubKey.GetDestinationAddress(network)}");
                Debug.WriteLine($"TxId: {res.Transaction.GetHash()}");

                foundReceive = false;
                Assert.InRange(res.Transaction.Outputs.Count, 1, 2);
                foreach (var output in res.Transaction.Outputs)
                {
                    if (output.ScriptPubKey == receive)
                    {
                        foundReceive = true;
                        Assert.Equal(amountToSend, output.Value);
                    }
                }
                Assert.True(foundReceive);

                #endregion

                #region MediumFee

                res = await walletJob.BuildTransactionAsync(receive, amountToSend, FeeType.Medium, account,
                                                            allowUnconfirmed : true);

                Assert.True(res.Success);
                Assert.Empty(res.FailingReason);
                Assert.Equal(receive, res.ActiveOutput.ScriptPubKey);
                Assert.Equal(amountToSend, res.ActiveOutput.Value);
                Assert.NotNull(res.ChangeOutput);
                Assert.Contains(res.Transaction.Outputs, x => x.Value == res.ChangeOutput.Value);
                Debug.WriteLine($"Fee: {res.Fee}");
                Debug.WriteLine($"FeePercentOfSent: {res.FeePercentOfSent} %");
                Debug.WriteLine($"SpendsUnconfirmed: {res.SpendsUnconfirmed}");
                Debug.WriteLine($"Active Output: {res.ActiveOutput.Value.ToString(false, true)} {res.ActiveOutput.ScriptPubKey.GetDestinationAddress(network)}");
                Debug.WriteLine($"Change Output: {res.ChangeOutput.Value.ToString(false, true)} {res.ChangeOutput.ScriptPubKey.GetDestinationAddress(network)}");
                Debug.WriteLine($"TxId: {res.Transaction.GetHash()}");

                foundReceive = false;
                Assert.InRange(res.Transaction.Outputs.Count, 1, 2);
                foreach (var output in res.Transaction.Outputs)
                {
                    if (output.ScriptPubKey == receive)
                    {
                        foundReceive = true;
                        Assert.Equal(amountToSend, output.Value);
                    }
                }
                Assert.True(foundReceive);

                #endregion

                #region HighFee

                res = await walletJob.BuildTransactionAsync(receive, amountToSend, FeeType.High, account,
                                                            allowUnconfirmed : true);

                Assert.True(res.Success);
                Assert.Empty(res.FailingReason);
                Assert.Equal(receive, res.ActiveOutput.ScriptPubKey);
                Assert.Equal(amountToSend, res.ActiveOutput.Value);
                Assert.NotNull(res.ChangeOutput);
                Assert.Contains(res.Transaction.Outputs, x => x.Value == res.ChangeOutput.Value);
                Debug.WriteLine($"Fee: {res.Fee}");
                Debug.WriteLine($"FeePercentOfSent: {res.FeePercentOfSent} %");
                Debug.WriteLine($"SpendsUnconfirmed: {res.SpendsUnconfirmed}");
                Debug.WriteLine($"Active Output: {res.ActiveOutput.Value.ToString(false, true)} {res.ActiveOutput.ScriptPubKey.GetDestinationAddress(network)}");
                Debug.WriteLine($"Change Output: {res.ChangeOutput.Value.ToString(false, true)} {res.ChangeOutput.ScriptPubKey.GetDestinationAddress(network)}");
                Debug.WriteLine($"TxId: {res.Transaction.GetHash()}");

                foundReceive = false;
                Assert.InRange(res.Transaction.Outputs.Count, 1, 2);
                foreach (var output in res.Transaction.Outputs)
                {
                    if (output.ScriptPubKey == receive)
                    {
                        foundReceive = true;
                        Assert.Equal(amountToSend, output.Value);
                    }
                }
                Assert.True(foundReceive);

                Assert.InRange(res.Fee, Money.Zero, res.Fee);
                Assert.InRange(res.Fee, res.Fee, res.Fee);

                _txProbArrived    = false;
                _prevCount        = (await walletJob.GetTrackerAsync()).TrackedTransactions.Count;
                _currentWalletJob = walletJob;
                (await walletJob.GetTrackerAsync()).TrackedTransactions.CollectionChanged += TrackedTransactions_CollectionChangedAsync;

                sendRes = await walletJob.SendTransactionAsync(res.Transaction);

                Assert.True(sendRes.Success);
                Assert.Empty(sendRes.FailingReason);

                while (_txProbArrived == false)
                {
                    Debug.WriteLine("Waiting for transaction...");
                    await Task.Delay(1000);
                }

                Debug.WriteLine("TrackedTransactions collection changed");
                Assert.Contains((await walletJob.GetTrackerAsync()).TrackedTransactions, x => x.Transaction.GetHash() == res.Transaction.GetHash());
                Debug.WriteLine("Transaction arrived");

                #endregion

                #region MaxAmount

                receive = (await walletJob.GetUnusedScriptPubKeysAsync(AddressType.Pay2WitnessPublicKeyHash, account, HdPathType.Receive)).FirstOrDefault();

                bal = (await walletJob.GetBalanceAsync(account)).Available;

                res = await walletJob.BuildTransactionAsync(receive, Money.Zero, FeeType.Low, account,
                                                            allowUnconfirmed : true);

                Assert.True(res.Success);
                Assert.Empty(res.FailingReason);
                Assert.Equal(receive, res.ActiveOutput.ScriptPubKey);
                Assert.Null(res.ChangeOutput);
                Debug.WriteLine($"Fee: {res.Fee}");
                Debug.WriteLine($"FeePercentOfSent: {res.FeePercentOfSent} %");
                Debug.WriteLine($"SpendsUnconfirmed: {res.SpendsUnconfirmed}");
                Debug.WriteLine($"Active Output: {res.ActiveOutput.Value.ToString(false, true)} {res.ActiveOutput.ScriptPubKey.GetDestinationAddress(network)}");
                Debug.WriteLine($"TxId: {res.Transaction.GetHash()}");

                Assert.Single(res.Transaction.Outputs);
                var maxBuiltTxOutput = res.Transaction.Outputs.Single();
                Assert.Equal(receive, maxBuiltTxOutput.ScriptPubKey);
                Assert.Equal(bal.Confirmed + bal.Unconfirmed - res.Fee, maxBuiltTxOutput.Value);

                #endregion

                #region InputSelection

                receive = (await walletJob.GetUnusedScriptPubKeysAsync(AddressType.Pay2WitnessPublicKeyHash, account, HdPathType.Receive)).FirstOrDefault();

                bal = (await walletJob.GetBalanceAsync(account)).Available;

                var inputCountBefore = res.SpentCoins.Count();
                res = await walletJob.BuildTransactionAsync(receive, Money.Zero, FeeType.Low, account,
                                                            allowUnconfirmed : true,
                                                            allowedInputs : res.SpentCoins.Where((x, i) => i == 0 || i % 2 == 0).Select(x => x.Outpoint));

                Assert.True(inputCountBefore >= res.SpentCoins.Count());
                Assert.Equal(res.SpentCoins.Count(), res.Transaction.Inputs.Count);
                Assert.True(res.Success);
                Assert.Empty(res.FailingReason);
                Assert.Equal(receive, res.ActiveOutput.ScriptPubKey);
                Assert.Null(res.ChangeOutput);
                Debug.WriteLine($"Fee: {res.Fee}");
                Debug.WriteLine($"FeePercentOfSent: {res.FeePercentOfSent} %");
                Debug.WriteLine($"SpendsUnconfirmed: {res.SpendsUnconfirmed}");
                Debug.WriteLine($"Active Output: {res.ActiveOutput.Value.ToString(false, true)} {res.ActiveOutput.ScriptPubKey.GetDestinationAddress(network)}");
                Debug.WriteLine($"TxId: {res.Transaction.GetHash()}");

                Assert.Single(res.Transaction.Outputs);

                res = await walletJob.BuildTransactionAsync(receive, Money.Zero, FeeType.Low, account,
                                                            allowUnconfirmed : true,
                                                            allowedInputs : new[] { res.SpentCoins.Select(x => x.Outpoint).First() });

                Assert.Single(res.Transaction.Inputs);
                Assert.Single(res.Transaction.Outputs);
                Assert.Single(res.SpentCoins);
                Assert.Null(res.ChangeOutput);

                #endregion
            }
Exemplo n.º 13
0
 public BlockDownloader(WalletJob walletJob)
 {
     _walletJob = walletJob ?? throw new ArgumentNullException(nameof(walletJob));
 }
Exemplo n.º 14
0
        public async Task SendsFailGracefullyTestAsync()
        {
            Network      network  = Network.TestNet;
            SafeAccount  account  = new SafeAccount(1);
            string       path     = Path.Combine(Helpers.CommittedWalletsFolderPath, $"Sending{network}.json");
            const string password = "";
            Safe         safe     = await Safe.LoadAsync(password, path, network);

            Assert.Equal(network, safe.Network);
            Debug.WriteLine($"Unique Safe ID: {safe.UniqueId}");

            // create walletjob
            WalletJob walletJob = new WalletJob();
            await walletJob.InitializeAsync(Helpers.SocksPortHandler, Helpers.ControlPortClient, safe, trackDefaultSafe : false, accountsToTrack : account);

            // note some event
            walletJob.ConnectedNodeCountChanged += WalletJob_ConnectedNodeCountChanged;
            walletJob.StateChanged += WalletJob_StateChanged;

            // start syncing
            var  cts           = new CancellationTokenSource();
            var  walletJobTask = walletJob.StartAsync(cts.Token);
            Task reportTask    = Helpers.ReportAsync(cts.Token, walletJob);

            try
            {
                // wait until blocks are synced
                while (walletJob.State <= WalletState.SyncingMemPool)
                {
                    await Task.Delay(1000);
                }

                var history = await walletJob.GetSafeHistoryAsync(account);

                foreach (var record in history)
                {
                    Debug.WriteLine($"{record.TransactionId} {record.Amount} {record.Confirmed}");
                }

                var receive = (await walletJob.GetUnusedScriptPubKeysAsync(AddressType.Pay2WitnessPublicKeyHash, account, HdPathType.Receive)).FirstOrDefault();

                var bal = (await walletJob.GetBalanceAsync(account)).Available;

                // Not enough fee
                Money amountToSend = (bal.Confirmed + bal.Unconfirmed) - new Money(1m, MoneyUnit.Satoshi);
                var   res          = await walletJob.BuildTransactionAsync(receive, amountToSend, FeeType.Low, account,
                                                                           allowUnconfirmed : true);

                Assert.False(res.Success);
                Assert.NotEmpty(res.FailingReason);
                Debug.WriteLine($"Expected FailingReason: {res.FailingReason}");

                // That's not how you spend all
                amountToSend = (bal.Confirmed + bal.Unconfirmed);
                res          = await walletJob.BuildTransactionAsync(receive, amountToSend, FeeType.Low, account,
                                                                     allowUnconfirmed : true);

                Assert.False(res.Success);
                Assert.NotEmpty(res.FailingReason);
                Debug.WriteLine($"Expected FailingReason: {res.FailingReason}");

                // Too much
                amountToSend = (bal.Confirmed + bal.Unconfirmed) + new Money(1, MoneyUnit.BTC);
                res          = await walletJob.BuildTransactionAsync(receive, amountToSend, FeeType.Low, account,
                                                                     allowUnconfirmed : true);

                Assert.True(res.Success == false);
                Assert.True(res.FailingReason != "");
                Debug.WriteLine($"Expected FailingReason: {res.FailingReason}");

                // Minus
                amountToSend = new Money(-1m, MoneyUnit.BTC);
                res          = await walletJob.BuildTransactionAsync(receive, amountToSend, FeeType.Low, account,
                                                                     allowUnconfirmed : true);

                Assert.False(res.Success);
                Assert.NotEmpty(res.FailingReason);
                Debug.WriteLine($"Expected FailingReason: {res.FailingReason}");

                // Default account is disabled
                amountToSend = (bal.Confirmed + bal.Unconfirmed) / 2;
                await Assert.ThrowsAsync <NotSupportedException>(async() => await walletJob.BuildTransactionAsync(receive, amountToSend, FeeType.Low,
                                                                                                                  allowUnconfirmed: true)).ContinueWith(t => {});

                // No such account
                amountToSend = (bal.Confirmed + bal.Unconfirmed) / 2;
                await Assert.ThrowsAsync <NotSupportedException>(async() => await walletJob.BuildTransactionAsync(receive, amountToSend, FeeType.Low, new SafeAccount(23421),
                                                                                                                  allowUnconfirmed: true)).ContinueWith(t => { });
            }
            finally
            {
                cts?.Cancel();
                await Task.WhenAll(reportTask, walletJobTask);

                walletJob.ConnectedNodeCountChanged -= WalletJob_ConnectedNodeCountChanged;
                walletJob.StateChanged -= WalletJob_StateChanged;

                cts?.Dispose();
                reportTask?.Dispose();
                walletJobTask?.Dispose();
            }
        }
Exemplo n.º 15
0
        private async Task DownloadNextBlocks(Node node, CancellationToken ctsToken, int maxBlocksToDownload = 1)
        {
            var heights = new List <Height>();

            try
            {
                await _sem.WaitAsync(ctsToken).ConfigureAwait(false);

                if (BlocksToDownload.Count == 0)
                {
                    await Task.Delay(100, ctsToken).ContinueWith(tsk => { }).ConfigureAwait(false);

                    return;
                }

                if (BlocksToDownload.Count < maxBlocksToDownload * 2)
                {
                    maxBlocksToDownload = 1;
                }

                for (int i = 0; i < maxBlocksToDownload; i++)
                {
                    // todo check if we have that much block
                    var height = BlocksToDownload.Min();
                    BlocksToDownload.TryRemove(height);
                    heights.Add(height);
                }
            }
            finally
            {
                _sem.Release();
            }
            try
            {
                var headers = new HashSet <ChainedBlock>();
                foreach (var height in heights)
                {
                    WalletJob.TryGetHeader(height, out ChainedBlock neededHeader);
                    headers.Add(neededHeader);
                }

                var delayMinutes     = heights.Count;
                var timeoutToken     = new CancellationTokenSource(TimeSpan.FromMinutes(delayMinutes)).Token;
                var downloadCtsToken = CancellationTokenSource.CreateLinkedTokenSource(timeoutToken, ctsToken).Token;

                HashSet <Block> blocks = null;
                try
                {
                    blocks = new HashSet <Block>(await Task.Run(() => node.GetBlocks(headers.ToArray(), downloadCtsToken)).ConfigureAwait(false));
                }
                catch
                {
                    if (timeoutToken.IsCancellationRequested)
                    {
                        node.DisconnectAsync($"Block download time > {delayMinutes}min");
                    }
                    else
                    {
                        node.DisconnectAsync("Block download failed");
                    }
                    blocks = null;
                }
                if (blocks == null)
                {
                    foreach (var height in heights)
                    {
                        BlocksToDownload.Add(height);
                    }
                }
                else
                {
                    int i = 0;
                    foreach (var block in blocks)
                    {
                        DownloadedBlocks.AddOrReplace(heights[i], block);
                        i++;
                    }
                }
            }
            catch
            {
                try
                {
                    await _sem.WaitAsync(ctsToken).ConfigureAwait(false);

                    foreach (var height in heights)
                    {
                        BlocksToDownload.Add(height);
                    }
                }
                finally
                {
                    _sem.Release();
                }
            }
        }
Exemplo n.º 16
0
        public async Task SendTestAsync()
        {
            Network      network  = Network.TestNet;
            SafeAccount  account  = new SafeAccount(1);
            string       path     = Path.Combine(Helpers.CommittedWalletsFolderPath, $"Sending{network}.json");
            const string password = "";
            Safe         safe     = await Safe.LoadAsync(password, path);

            Assert.Equal(network, safe.Network);
            Debug.WriteLine($"Unique Safe ID: {safe.UniqueId}");

            // create walletjob
            WalletJob walletJob = new WalletJob();
            await walletJob.InitializeAsync(Helpers.SocksPortHandler, Helpers.ControlPortClient, safe, trackDefaultSafe : false, accountsToTrack : account);

            // note some event
            walletJob.ConnectedNodeCountChanged += WalletJob_ConnectedNodeCountChanged;
            walletJob.StateChanged += WalletJob_StateChanged;

            // start syncing
            var  cts           = new CancellationTokenSource();
            var  walletJobTask = walletJob.StartAsync(cts.Token);
            Task reportTask    = Helpers.ReportAsync(cts.Token, walletJob);

            try
            {
                // wait until blocks are synced
                while (walletJob.State <= WalletState.SyncingMemPool)
                {
                    await Task.Delay(1000);
                }

                foreach (var r in await walletJob.GetSafeHistoryAsync(account))
                {
                    Debug.WriteLine(r.TransactionId);
                }

                var record = (await walletJob.GetSafeHistoryAsync(account)).FirstOrDefault();
                Debug.WriteLine(record.Confirmed);
                Debug.WriteLine(record.Amount);

                var receive = (await walletJob.GetUnusedScriptPubKeysAsync(AddressType.Pay2WitnessPublicKeyHash, account, HdPathType.Receive)).FirstOrDefault();

                var getBalanceResult = await walletJob.GetBalanceAsync(account);

                var   bal          = getBalanceResult.Available;
                Money amountToSend = (bal.Confirmed + bal.Unconfirmed) / 2;
                var   res          = await walletJob.BuildTransactionAsync(receive, amountToSend, FeeType.Low, account,
                                                                           allowUnconfirmed : true);

                Assert.True(res.Success);
                Assert.Empty(res.FailingReason);
                Debug.WriteLine($"Fee: {res.Fee}");
                Debug.WriteLine($"FeePercentOfSent: {res.FeePercentOfSent} %");
                Debug.WriteLine($"SpendsUnconfirmed: {res.SpendsUnconfirmed}");
                Debug.WriteLine($"Transaction: {res.Transaction}");

                var foundReceive = false;
                Assert.InRange(res.Transaction.Outputs.Count, 1, 2);
                foreach (var output in res.Transaction.Outputs)
                {
                    if (output.ScriptPubKey == receive)
                    {
                        foundReceive = true;
                        Assert.Equal(amountToSend, output.Value);
                    }
                }
                Assert.True(foundReceive);

                _txProbArrived    = false;
                _prevCount        = (await walletJob.GetTrackerAsync()).TrackedTransactions.Count;
                _currentWalletJob = walletJob;
                (await walletJob.GetTrackerAsync()).TrackedTransactions.CollectionChanged += TrackedTransactions_CollectionChangedAsync;

                var sendRes = await walletJob.SendTransactionAsync(res.Transaction);

                Assert.True(sendRes.Success);
                Assert.Empty(sendRes.FailingReason);

                while (!_txProbArrived)
                {
                    Debug.WriteLine("Waiting for transaction...");
                    await Task.Delay(1000);
                }

                Debug.WriteLine("TrackedTransactions collection changed");
                Assert.Contains((await walletJob.GetTrackerAsync()).TrackedTransactions, x => x.Transaction.GetHash() == res.Transaction.GetHash());
                Debug.WriteLine("Transaction arrived");


                receive = (await walletJob.GetUnusedScriptPubKeysAsync(AddressType.Pay2WitnessPublicKeyHash, account, HdPathType.Receive)).FirstOrDefault();

                bal          = (await walletJob.GetBalanceAsync(account)).Available;
                amountToSend = (bal.Confirmed + bal.Unconfirmed) / 2;

                #region LowFee

                var resLow = await walletJob.BuildTransactionAsync(receive, amountToSend, FeeType.Low, account,
                                                                   allowUnconfirmed : true);

                Assert.True(resLow.Success);
                Assert.Empty(resLow.FailingReason);
                Debug.WriteLine($"Fee: {resLow.Fee}");
                Debug.WriteLine($"FeePercentOfSent: {resLow.FeePercentOfSent} %");
                Debug.WriteLine($"SpendsUnconfirmed: {resLow.SpendsUnconfirmed}");
                Debug.WriteLine($"Transaction: {resLow.Transaction}");

                foundReceive = false;
                Assert.InRange(resLow.Transaction.Outputs.Count, 1, 2);
                foreach (var output in resLow.Transaction.Outputs)
                {
                    if (output.ScriptPubKey == receive)
                    {
                        foundReceive = true;
                        Assert.Equal(amountToSend, output.Value);
                    }
                }
                Assert.True(foundReceive);

                #endregion

                #region MediumFee

                var resMedium = await walletJob.BuildTransactionAsync(receive, amountToSend, FeeType.Medium, account,
                                                                      allowUnconfirmed : true);

                Assert.True(resMedium.Success);
                Assert.Empty(resMedium.FailingReason);
                Debug.WriteLine($"Fee: {resMedium.Fee}");
                Debug.WriteLine($"FeePercentOfSent: {resMedium.FeePercentOfSent} %");
                Debug.WriteLine($"SpendsUnconfirmed: {resMedium.SpendsUnconfirmed}");
                Debug.WriteLine($"Transaction: {resMedium.Transaction}");

                foundReceive = false;
                Assert.InRange(resMedium.Transaction.Outputs.Count, 1, 2);
                foreach (var output in resMedium.Transaction.Outputs)
                {
                    if (output.ScriptPubKey == receive)
                    {
                        foundReceive = true;
                        Assert.Equal(amountToSend, output.Value);
                    }
                }
                Assert.True(foundReceive);

                #endregion

                #region HighFee

                var resHigh = await walletJob.BuildTransactionAsync(receive, amountToSend, FeeType.High, account,
                                                                    allowUnconfirmed : true);

                Assert.True(resHigh.Success);
                Assert.Empty(resHigh.FailingReason);
                Debug.WriteLine($"Fee: {resHigh.Fee}");
                Debug.WriteLine($"FeePercentOfSent: {resHigh.FeePercentOfSent} %");
                Debug.WriteLine($"SpendsUnconfirmed: {resHigh.SpendsUnconfirmed}");
                Debug.WriteLine($"Transaction: {resHigh.Transaction}");

                foundReceive = false;
                Assert.InRange(resHigh.Transaction.Outputs.Count, 1, 2);
                foreach (var output in resHigh.Transaction.Outputs)
                {
                    if (output.ScriptPubKey == receive)
                    {
                        foundReceive = true;
                        Assert.Equal(amountToSend, output.Value);
                    }
                }
                Assert.True(foundReceive);

                #endregion

                Assert.InRange(resLow.Fee, Money.Zero, resMedium.Fee);
                Assert.InRange(resMedium.Fee, resLow.Fee, resHigh.Fee);

                _txProbArrived    = false;
                _prevCount        = (await walletJob.GetTrackerAsync()).TrackedTransactions.Count;
                _currentWalletJob = walletJob;
                (await walletJob.GetTrackerAsync()).TrackedTransactions.CollectionChanged += TrackedTransactions_CollectionChangedAsync;

                sendRes = await walletJob.SendTransactionAsync(resHigh.Transaction);

                Assert.True(sendRes.Success);
                Assert.Empty(sendRes.FailingReason);

                while (_txProbArrived == false)
                {
                    Debug.WriteLine("Waiting for transaction...");
                    await Task.Delay(1000);
                }

                Debug.WriteLine("TrackedTransactions collection changed");
                Assert.Contains((await walletJob.GetTrackerAsync()).TrackedTransactions, x => x.Transaction.GetHash() == resHigh.Transaction.GetHash());
                Debug.WriteLine("Transaction arrived");



                receive = (await walletJob.GetUnusedScriptPubKeysAsync(AddressType.Pay2WitnessPublicKeyHash, account, HdPathType.Receive)).FirstOrDefault();

                bal = (await walletJob.GetBalanceAsync(account)).Available;

                res = await walletJob.BuildTransactionAsync(receive, Money.Zero, FeeType.Low, account,
                                                            allowUnconfirmed : true);

                Assert.True(res.Success);
                Assert.Empty(res.FailingReason);
                Debug.WriteLine($"Fee: {res.Fee}");
                Debug.WriteLine($"FeePercentOfSent: {res.FeePercentOfSent} %");
                Debug.WriteLine($"SpendsUnconfirmed: {res.SpendsUnconfirmed}");
                Debug.WriteLine($"Transaction: {res.Transaction}");

                foundReceive = false;
                Assert.InRange(res.Transaction.Outputs.Count, 1, 2);
                foreach (var output in res.Transaction.Outputs)
                {
                    if (output.ScriptPubKey == receive)
                    {
                        foundReceive = true;
                        Assert.Equal(bal.Confirmed + bal.Unconfirmed - res.Fee, output.Value);
                    }
                }
                Assert.True(foundReceive);

                _txProbArrived    = false;
                _prevCount        = (await walletJob.GetTrackerAsync()).TrackedTransactions.Count;
                _currentWalletJob = walletJob;
                (await walletJob.GetTrackerAsync()).TrackedTransactions.CollectionChanged += TrackedTransactions_CollectionChangedAsync;

                sendRes = await walletJob.SendTransactionAsync(res.Transaction);

                Assert.True(sendRes.Success);
                Assert.Empty(sendRes.FailingReason);

                while (_txProbArrived == false)
                {
                    Debug.WriteLine("Waiting for transaction...");
                    await Task.Delay(1000);
                }

                Debug.WriteLine("TrackedTransactions collection changed");
                Assert.Contains((await walletJob.GetTrackerAsync()).TrackedTransactions, x => x.Transaction.GetHash() == res.Transaction.GetHash());
                Debug.WriteLine("Transaction arrived");
            }
            finally
            {
                (await walletJob.GetTrackerAsync()).TrackedTransactions.CollectionChanged -= TrackedTransactions_CollectionChangedAsync;

                cts.Cancel();
                await Task.WhenAll(reportTask, walletJobTask);

                walletJob.ConnectedNodeCountChanged -= WalletJob_ConnectedNodeCountChanged;
                walletJob.StateChanged -= WalletJob_StateChanged;

                cts?.Dispose();
                reportTask?.Dispose();
                walletJobTask?.Dispose();
            }
        }