Example #1
0
        public async Task <TupleValue <string, bool> > ImportScriptAsync(byte[] scriptBytes, bool rescan, int scanFrom,
                                                                         string passphrase, bool requireRedeemable)
        {
            if (scriptBytes == null)
            {
                throw new ArgumentNullException(nameof(scriptBytes));
            }
            if (passphrase == null)
            {
                throw new ArgumentNullException(nameof(passphrase));
            }

            var client  = new WalletService.WalletServiceClient(_channel);
            var request = new ImportScriptRequest
            {
                Script            = ByteString.CopyFrom(scriptBytes),
                Rescan            = rescan,
                Passphrase        = ByteString.CopyFromUtf8(passphrase), // Poorly named: this outputs UTF8 from a UTF16 System.String
                ScanFrom          = scanFrom,
                RequireRedeemable = requireRedeemable,
            };
            var resp = await client.ImportScriptAsync(request, cancellationToken : _tokenSource.Token);

            return(TupleValue.Create(resp.P2ShAddress, resp.Redeemable));
        }
Example #2
0
        public async Task <TransactionSet> GetTransactionsAsync(int minRecentTransactions, int minRecentBlocks)
        {
            var client  = new WalletService.WalletServiceClient(_channel);
            var request = new GetTransactionsRequest
            {
                // TODO: include these.  With these uncommented, all transactions are loaded.
                //StartingBlockHeight = -minRecentBlocks,
                //MinimumRecentTransactions = minRecentTransactions
            };

            var responseStream    = client.GetTransactions(request, cancellationToken: _tokenSource.Token).ResponseStream;
            var minedTransactions = new List <Block>();
            Dictionary <Blake256Hash, WalletTransaction> unminedTransactions = null;

            while (await responseStream.MoveNext())
            {
                var msg = responseStream.Current;
                if (msg.MinedTransactions != null)
                {
                    minedTransactions.Add(MarshalBlock(msg.MinedTransactions));
                }
                else
                {
                    unminedTransactions = msg.UnminedTransactions.Select(MarshalWalletTransaction).ToDictionary(tx => tx.Hash);
                }
            }
            return(new TransactionSet(minedTransactions, unminedTransactions ?? new Dictionary <Blake256Hash, WalletTransaction>()));
        }
Example #3
0
 public async Task RevokeTicketsAsync(string passphrase)
 {
     var client  = new WalletService.WalletServiceClient(_channel);
     var request = new RevokeTicketsRequest
     {
         Passphrase = ByteString.CopyFromUtf8(passphrase),
     };
     await client.RevokeTicketsAsync(request, cancellationToken : _tokenSource.Token);
 }
Example #4
0
        public async Task <Amount> TicketPriceAsync()
        {
            var client   = new WalletService.WalletServiceClient(_channel);
            var request  = new TicketPriceRequest {
            };
            var response = await client.TicketPriceAsync(request, cancellationToken : _tokenSource.Token);

            return(response.TicketPrice);
        }
Example #5
0
        public async Task <string> NextInternalAddressAsync(Account account)
        {
            var client  = new WalletService.WalletServiceClient(_channel);
            var request = new NextAddressRequest
            {
                Account = account.AccountNumber,
                Kind    = NextAddressRequest.Types.Kind.Bip0044Internal,
            };
            var resp = await client.NextAddressAsync(request, cancellationToken : _tokenSource.Token);

            return(resp.Address);
        }
Example #6
0
        /// <summary>
        /// Queries the RPC server for the next external BIP0044 address for an account
        /// </summary>
        /// <param name="account">Account to create address for</param>
        /// <returns>Tuple containing the address and pubkey address strings</returns>
        public async Task <TupleValue <string, string> > NextExternalAddressAsync(Account account)
        {
            var client  = new WalletService.WalletServiceClient(_channel);
            var request = new NextAddressRequest
            {
                Account = account.AccountNumber,
                Kind    = NextAddressRequest.Types.Kind.Bip0044External,
            };
            var resp = await client.NextAddressAsync(request, cancellationToken : _tokenSource.Token);

            return(TupleValue.Create(resp.Address, resp.PublicKey));
        }
Example #7
0
        public async Task RescanFromBlockHeightAsync(int beginHeight, Action <int> progressCallback = null)
        {
            var client  = new WalletService.WalletServiceClient(_channel);
            var request = new RescanRequest {
                BeginHeight = beginHeight
            };
            var responseServer = client.Rescan(request, cancellationToken: _tokenSource.Token);

            while (await responseServer.ResponseStream.MoveNext())
            {
                progressCallback?.Invoke(responseServer.ResponseStream.Current.RescannedThrough);
            }
        }
Example #8
0
        public async Task RenameAccountAsync(Account account, string newAccountName)
        {
            if (newAccountName == null)
            {
                throw new ArgumentNullException(nameof(newAccountName));
            }

            var client  = new WalletService.WalletServiceClient(_channel);
            var request = new RenameAccountRequest
            {
                AccountNumber = account.AccountNumber,
                NewName       = newAccountName,
            };
            await client.RenameAccountAsync(request, cancellationToken : _tokenSource.Token);
        }
Example #9
0
        public async Task <Tuple <Transaction, bool> > SignTransactionAsync(string passphrase, Transaction tx)
        {
            var client  = new WalletService.WalletServiceClient(_channel);
            var request = new SignTransactionRequest
            {
                Passphrase            = ByteString.CopyFromUtf8(passphrase),
                SerializedTransaction = ByteString.CopyFrom(tx.Serialize()),
            };
            var response = await client.SignTransactionAsync(request, cancellationToken : _tokenSource.Token);

            var signedTransaction = Transaction.Deserialize(response.Transaction.ToByteArray());
            var complete          = response.UnsignedInputIndexes.Count == 0;

            return(Tuple.Create(signedTransaction, complete));
        }
Example #10
0
        public async Task <Blake256Hash> PublishTransactionAsync(byte[] signedTransaction)
        {
            if (signedTransaction == null)
            {
                throw new ArgumentNullException(nameof(signedTransaction));
            }

            var client  = new WalletService.WalletServiceClient(_channel);
            var request = new PublishTransactionRequest
            {
                SignedTransaction = ByteString.CopyFrom(signedTransaction),
            };
            var response = await client.PublishTransactionAsync(request, cancellationToken : _tokenSource.Token);

            return(new Blake256Hash(response.TransactionHash.ToByteArray()));
        }
Example #11
0
        public async Task <Tuple <List <UnspentOutput>, Amount> > FundTransactionAsync(
            Account account, Amount targetAmount, int requiredConfirmations)
        {
            var client  = new WalletService.WalletServiceClient(_channel);
            var request = new FundTransactionRequest
            {
                Account                  = account.AccountNumber,
                TargetAmount             = targetAmount,
                RequiredConfirmations    = requiredConfirmations,
                IncludeImmatureCoinbases = false,
                IncludeChangeScript      = false,
            };
            var response = await client.FundTransactionAsync(request, cancellationToken : _tokenSource.Token);

            var outputs = response.SelectedOutputs.Select(MarshalUnspentOutput).ToList();
            var total   = (Amount)response.TotalAmount;

            return(Tuple.Create(outputs, total));
        }
Example #12
0
        private async Task ChangePassphrase(ChangePassphraseRequest.Types.Key which, string oldPassphrase, string newPassphrase)
        {
            if (oldPassphrase == null)
            {
                throw new ArgumentNullException(nameof(oldPassphrase));
            }
            if (newPassphrase == null)
            {
                throw new ArgumentNullException(nameof(newPassphrase));
            }

            var client  = new WalletService.WalletServiceClient(_channel);
            var request = new ChangePassphraseRequest
            {
                Key           = which,
                OldPassphrase = ByteString.CopyFromUtf8(oldPassphrase),
                NewPassphrase = ByteString.CopyFromUtf8(newPassphrase),
            };
            await client.ChangePassphraseAsync(request, cancellationToken : _tokenSource.Token);
        }
Example #13
0
        public async Task ImportScriptAsync(byte[] scriptBytes, bool rescan, int scanFrom, string passphrase)
        {
            if (scriptBytes == null)
            {
                throw new ArgumentNullException(nameof(scriptBytes));
            }
            if (passphrase == null)
            {
                throw new ArgumentNullException(nameof(passphrase));
            }

            var client  = new WalletService.WalletServiceClient(_channel);
            var request = new ImportScriptRequest
            {
                Script     = ByteString.CopyFrom(scriptBytes),
                Rescan     = rescan,
                Passphrase = ByteString.CopyFromUtf8(passphrase), // Poorly named: this outputs UTF8 from a UTF16 System.String
                ScanFrom   = scanFrom,
            };
            await client.ImportScriptAsync(request, cancellationToken : _tokenSource.Token);
        }
Example #14
0
        public async Task <Account> NextAccountAsync(string passphrase, string accountName)
        {
            if (passphrase == null)
            {
                throw new ArgumentNullException(nameof(passphrase));
            }
            if (accountName == null)
            {
                throw new ArgumentNullException(nameof(accountName));
            }

            var client  = new WalletService.WalletServiceClient(_channel);
            var request = new NextAccountRequest
            {
                Passphrase  = ByteString.CopyFromUtf8(passphrase),
                AccountName = accountName,
            };
            var resp = await client.NextAccountAsync(request, cancellationToken : _tokenSource.Token);

            return(new Account(resp.AccountNumber));
        }
Example #15
0
        public async Task ImportPrivateKeyAsync(Account account, string privateKeyWif, bool rescan, string passphrase)
        {
            if (privateKeyWif == null)
            {
                throw new ArgumentNullException(nameof(privateKeyWif));
            }
            if (passphrase == null)
            {
                throw new ArgumentNullException(nameof(passphrase));
            }

            var client  = new WalletService.WalletServiceClient(_channel);
            var request = new ImportPrivateKeyRequest
            {
                Account       = account.AccountNumber,
                PrivateKeyWif = privateKeyWif,
                Rescan        = rescan,
                Passphrase    = ByteString.CopyFromUtf8(passphrase), // Poorly named: this outputs UTF8 from a UTF16 System.String
            };
            await client.ImportPrivateKeyAsync(request, cancellationToken : _tokenSource.Token);
        }
Example #16
0
        public async Task <List <Blake256Hash> > PurchaseTicketsAsync(Account account, Amount spendLimit,
                                                                      int reqConfs, Address votingAddress, uint ticketCount, Address poolAddress, decimal poolFees,
                                                                      uint expiry, Amount txFee, Amount ticketFee, string passphrase)
        {
            var votingAddressStr = "";

            if (votingAddress != null)
            {
                votingAddressStr = votingAddress.ToString();
            }
            var poolAddressStr = "";

            if (poolAddress != null)
            {
                poolAddressStr = poolAddress.ToString();
            }
            if (poolAddressStr == "")
            {
                poolFees = 0m;
            }

            var client  = new WalletService.WalletServiceClient(_channel);
            var request = new PurchaseTicketsRequest
            {
                Passphrase            = ByteString.CopyFromUtf8(passphrase),
                Account               = account.AccountNumber,
                SpendLimit            = spendLimit,
                RequiredConfirmations = (uint)reqConfs,
                TicketAddress         = votingAddressStr,
                NumTickets            = ticketCount,
                PoolAddress           = poolAddressStr,
                PoolFees              = (double)(poolFees * 100),
                Expiry    = expiry,
                TxFee     = txFee,
                TicketFee = ticketFee,
            };
            var response = await client.PurchaseTicketsAsync(request, cancellationToken : _tokenSource.Token);

            return(response.TicketHashes.Select(h => new Blake256Hash(h.ToByteArray())).ToList());
        }
Example #17
0
        public async Task <StakeInfoProperties> StakeInfoAsync()
        {
            var client   = new WalletService.WalletServiceClient(_channel);
            var request  = new StakeInfoRequest {
            };
            var response = await client.StakeInfoAsync(request, cancellationToken : _tokenSource.Token);

            var properties = new StakeInfoProperties
            {
                PoolSize          = response.PoolSize,
                AllMempoolTickets = response.AllMempoolTix,
                OwnMempoolTickets = response.OwnMempoolTix,
                Immature          = response.Immature,
                Live         = response.Live,
                Voted        = response.Voted,
                Missed       = response.Missed,
                Revoked      = response.Revoked,
                Expired      = response.Expired,
                TotalSubsidy = response.TotalSubsidy,
            };

            return(properties);
        }
Example #18
0
        /// <summary>
        /// Begins synchronization of the client with the remote wallet process.
        /// A delegate must be passed to be connected to the wallet's ChangesProcessed event to avoid
        /// a race where additional notifications are processed in the sync task before the caller
        /// can connect the event.  The caller is responsible for disconnecting the delegate from the
        /// event handler when finished.
        /// </summary>
        /// <param name="walletEventHandler">Event handler for changes to wallet as new transactions are processed.</param>
        /// <returns>The synced Wallet and the Task that is keeping the wallet in sync.</returns>
        public async Task <Tuple <Mutex <Wallet>, Task> > Synchronize(EventHandler <Wallet.ChangesProcessedEventArgs> walletEventHandler)
        {
            if (walletEventHandler == null)
            {
                throw new ArgumentNullException(nameof(walletEventHandler));
            }

            TransactionNotifications notifications;
            Task notificationsTask;

            // TODO: Initialization requests need timeouts.

            // Loop until synchronization did not race on a reorg.
            while (true)
            {
                // Begin receiving notifications for new and removed wallet transactions before
                // old transactions are downloaded.  Any received notifications are saved to
                // a buffer and are processed after GetAllTransactionsAsync is awaited.
                notifications     = new TransactionNotifications(_channel, _tokenSource.Token);
                notificationsTask = notifications.ListenAndBuffer();

                var networkTask  = NetworkAsync();
                var accountsTask = AccountsAsync();

                var networkResp      = await networkTask;
                var activeBlockChain = BlockChainIdentity.FromNetworkBits(networkResp.ActiveNetwork);

                var txSetTask = GetTransactionsAsync(Wallet.MinRecentTransactions, Wallet.NumRecentBlocks(activeBlockChain));

                var txSet       = await txSetTask;
                var rpcAccounts = await accountsTask;

                var lastAccountBlockHeight = rpcAccounts.CurrentBlockHeight;
                var lastAccountBlockHash   = new Blake256Hash(rpcAccounts.CurrentBlockHash.ToByteArray());
                var lastTxBlock            = txSet.MinedTransactions.LastOrDefault();
                if (lastTxBlock != null)
                {
                    var lastTxBlockHeight = lastTxBlock.Height;
                    var lastTxBlockHash   = lastTxBlock.Hash;
                    if (lastTxBlockHeight > lastAccountBlockHeight ||
                        (lastTxBlockHeight == lastAccountBlockHeight && !lastTxBlockHash.Equals(lastAccountBlockHash)))
                    {
                        _tokenSource.Cancel();
                        continue;
                    }
                }

                // Read all received notifications thus far and determine if synchronization raced
                // on a chain reorganize.  Try again if so.
                IList <WalletChanges> transactionNotifications;
                if (notifications.Buffer.TryReceiveAll(out transactionNotifications))
                {
                    if (transactionNotifications.Any(r => r.DetachedBlocks.Count != 0))
                    {
                        _tokenSource.Cancel();
                        continue;
                    }

                    // Skip all attached block notifications that are in blocks lower than the
                    // block accounts notification.  If blocks exist at or past that height,
                    // the first's hash should equal that from the accounts notification.
                    //
                    // This ensures that both notifications contain data that is valid at this
                    // block.
                    var remainingNotifications = transactionNotifications
                                                 .SelectMany(r => r.AttachedBlocks)
                                                 .SkipWhile(b => b.Height < lastAccountBlockHeight)
                                                 .ToList();
                    if (remainingNotifications.Count != 0)
                    {
                        if (!remainingNotifications[0].Hash.Equals(lastAccountBlockHash))
                        {
                            _tokenSource.Cancel();
                            continue;
                        }
                    }

                    // TODO: Merge remaining notifications with the transaction set.
                    // For now, be lazy and start the whole sync over.
                    if (remainingNotifications.Count > 1)
                    {
                        _tokenSource.Cancel();
                        continue;
                    }
                }

                var accounts = rpcAccounts.Accounts.ToDictionary(
                    a => new Account(a.AccountNumber),
                    a => new AccountProperties
                {
                    AccountName  = a.AccountName,
                    TotalBalance = a.TotalBalance,
                    // TODO: uncomment when added to protospec and implemented by wallet.
                    //ImmatureCoinbaseReward = a.ImmatureBalance,
                    ExternalKeyCount = a.ExternalKeyCount,
                    InternalKeyCount = a.InternalKeyCount,
                    ImportedKeyCount = a.ImportedKeyCount,
                });
                Func <AccountsResponse.Types.Account, AccountProperties> createProperties = a => new AccountProperties
                {
                    AccountName  = a.AccountName,
                    TotalBalance = a.TotalBalance,
                    // TODO: uncomment when added to protospec and implemented by wallet.
                    //ImmatureCoinbaseReward = a.ImmatureBalance,
                    ExternalKeyCount = a.ExternalKeyCount,
                    InternalKeyCount = a.InternalKeyCount,
                    ImportedKeyCount = a.ImportedKeyCount,
                };
                // This assumes that all but the last account listed in the RPC response are
                // BIP0032 accounts, with the same account number as their List index.
                var bip0032Accounts = rpcAccounts.Accounts.Take(rpcAccounts.Accounts.Count - 1).Select(createProperties).ToList();
                var importedAccount = createProperties(rpcAccounts.Accounts.Last());
                var chainTip        = new BlockIdentity(lastAccountBlockHash, lastAccountBlockHeight);
                var wallet          = new Wallet(activeBlockChain, txSet, bip0032Accounts, importedAccount, chainTip);
                wallet.ChangesProcessed += walletEventHandler;
                var walletMutex = new Mutex <Wallet>(wallet);

                var syncTask = Task.Run(async() =>
                {
                    var client             = new WalletService.WalletServiceClient(_channel);
                    var accountsStream     = client.AccountNotifications(new AccountNotificationsRequest(), cancellationToken: _tokenSource.Token);
                    var accountChangesTask = accountsStream.ResponseStream.MoveNext();
                    var txChangesTask      = notifications.Buffer.OutputAvailableAsync();
                    while (true)
                    {
                        var completedTask = await Task.WhenAny(accountChangesTask, txChangesTask);
                        if (!await completedTask)
                        {
                            break;
                        }

                        using (var walletGuard = await walletMutex.LockAsync())
                        {
                            var w = walletGuard.Instance;
                            if (completedTask == accountChangesTask)
                            {
                                var accountProperties = accountsStream.ResponseStream.Current;
                                var account           = new Account(accountProperties.AccountNumber);
                                w.UpdateAccountProperties(account, accountProperties.AccountName,
                                                          accountProperties.ExternalKeyCount, accountProperties.InternalKeyCount,
                                                          accountProperties.ImportedKeyCount);
                                accountChangesTask = accountsStream.ResponseStream.MoveNext();
                            }
                            else if (completedTask == txChangesTask)
                            {
                                var changes = notifications.Buffer.Receive();
                                w.ApplyTransactionChanges(changes);
                                txChangesTask = notifications.Buffer.OutputAvailableAsync();
                            }
                        }
                    }

                    await notificationsTask;
                });

                return(Tuple.Create(walletMutex, syncTask));
            }
        }
Example #19
0
 public async Task LoadActiveDataFiltersAsync()
 {
     var client  = new WalletService.WalletServiceClient(_channel);
     var request = new LoadActiveDataFiltersRequest();
     await client.LoadActiveDataFiltersAsync(request, cancellationToken : _tokenSource.Token);
 }
Example #20
0
 public TransactionNotifications(Channel channel, CancellationToken cancelToken)
 {
     _client      = new WalletService.WalletServiceClient(channel);
     _buffer      = new BufferBlock <WalletChanges>();
     _cancelToken = cancelToken;
 }
Example #21
0
        public async Task <NetworkResponse> NetworkAsync()
        {
            var client = new WalletService.WalletServiceClient(_channel);

            return(await client.NetworkAsync(new NetworkRequest(), cancellationToken : _tokenSource.Token));
        }
Example #22
0
        public async Task <AccountsResponse> AccountsAsync()
        {
            var client = new WalletService.WalletServiceClient(_channel);

            return(await client.AccountsAsync(new AccountsRequest(), cancellationToken : _tokenSource.Token));
        }