2022-12-24 23:49:50 +00:00
// Copyright (c) 2011-2022 The Bitcoin Core developers
2014-12-13 12:09:33 +08:00
// Distributed under the MIT software license, see the accompanying
2013-11-04 16:20:43 +01:00
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
2017-11-10 13:57:53 +13:00
# include <qt/walletmodel.h>
# include <qt/addresstablemodel.h>
2018-08-01 13:38:45 -04:00
# include <qt/clientmodel.h>
2017-11-10 13:57:53 +13:00
# include <qt/guiconstants.h>
2019-11-22 15:32:09 -05:00
# include <qt/guiutil.h>
2017-11-10 13:57:53 +13:00
# include <qt/optionsmodel.h>
# include <qt/paymentserver.h>
# include <qt/recentrequeststablemodel.h>
# include <qt/sendcoinsdialog.h>
# include <qt/transactiontablemodel.h>
2023-03-23 12:23:29 +01:00
# include <common/args.h> // for GetBoolArg
2018-04-07 03:42:02 -04:00
# include <interfaces/handler.h>
# include <interfaces/node.h>
2017-09-19 18:12:25 -07:00
# include <key_io.h>
2022-06-14 10:38:51 +02:00
# include <node/interface_ui.h>
2023-12-07 17:11:27 -05:00
# include <node/types.h>
2020-01-24 17:02:41 -05:00
# include <psbt.h>
2020-04-18 15:18:17 -04:00
# include <util/translation.h>
2017-11-10 13:57:53 +13:00
# include <wallet/coincontrol.h>
2017-12-05 15:57:12 -05:00
# include <wallet/wallet.h> // for CRecipient
2011-06-30 18:05:29 +02:00
2013-04-13 00:13:08 -05:00
# include <stdint.h>
2021-01-13 02:05:00 +01:00
# include <functional>
2013-04-13 00:13:08 -05:00
# include <QDebug>
2017-02-03 22:04:39 +01:00
# include <QMessageBox>
2011-07-16 19:01:05 +02:00
# include <QSet>
2012-07-06 01:43:28 +10:00
# include <QTimer>
2011-06-30 18:05:29 +02:00
2021-11-12 11:13:29 -05:00
using wallet : : CCoinControl ;
using wallet : : CRecipient ;
using wallet : : DEFAULT_DISABLE_WALLET ;
2015-07-05 14:17:46 +02:00
2018-08-01 13:38:45 -04:00
WalletModel : : WalletModel ( std : : unique_ptr < interfaces : : Wallet > wallet , ClientModel & client_model , const PlatformStyle * platformStyle , QObject * parent ) :
QObject ( parent ) ,
m_wallet ( std : : move ( wallet ) ) ,
2020-04-24 18:06:26 -04:00
m_client_model ( & client_model ) ,
2018-08-01 13:38:45 -04:00
m_node ( client_model . node ( ) ) ,
optionsModel ( client_model . getOptionsModel ( ) ) ,
2020-04-24 18:06:26 -04:00
timer ( new QTimer ( this ) )
2011-06-30 18:05:29 +02:00
{
2017-04-17 18:56:44 -04:00
fHaveWatchOnly = m_wallet - > haveWatchOnly ( ) ;
2017-04-18 13:01:23 -04:00
addressTableModel = new AddressTableModel ( this ) ;
2017-04-18 16:42:30 -04:00
transactionTableModel = new TransactionTableModel ( platformStyle , this ) ;
recentRequestsTableModel = new RecentRequestsTableModel ( this ) ;
2012-05-06 19:40:58 +02:00
subscribeToCoreSignals ( ) ;
}
WalletModel : : ~ WalletModel ( )
{
unsubscribeFromCoreSignals ( ) ;
2011-06-30 18:05:29 +02:00
}
2019-10-12 22:26:47 +01:00
void WalletModel : : startPollBalance ( )
{
2022-05-09 10:59:57 -03:00
// Update the cached balance right away, so every view can make use of it,
// so them don't need to waste resources recalculating it.
pollBalanceChanged ( ) ;
2019-10-12 22:26:47 +01:00
// This timer will be fired repeatedly to update the balance
2021-03-27 19:31:37 +02:00
// Since the QTimer::timeout is a private signal, it cannot be used
// in the GUIUtil::ExceptionSafeConnect directly.
connect ( timer , & QTimer : : timeout , this , & WalletModel : : timerTimeout ) ;
GUIUtil : : ExceptionSafeConnect ( this , & WalletModel : : timerTimeout , this , & WalletModel : : pollBalanceChanged ) ;
2019-10-12 22:26:47 +01:00
timer - > start ( MODEL_UPDATE_DELAY ) ;
}
2020-04-24 18:06:26 -04:00
void WalletModel : : setClientModel ( ClientModel * client_model )
{
m_client_model = client_model ;
if ( ! m_client_model ) timer - > stop ( ) ;
}
2012-05-05 16:07:14 +02:00
void WalletModel : : updateStatus ( )
2011-06-30 18:05:29 +02:00
{
2012-05-05 16:07:14 +02:00
EncryptionStatus newEncryptionStatus = getEncryptionStatus ( ) ;
2016-10-24 07:21:51 +00:00
if ( cachedEncryptionStatus ! = newEncryptionStatus ) {
Q_EMIT encryptionStatusChanged ( ) ;
}
2012-05-05 16:07:14 +02:00
}
2012-07-06 01:43:28 +10:00
void WalletModel : : pollBalanceChanged ( )
2012-05-05 16:07:14 +02:00
{
2020-04-02 07:42:01 -04:00
// Avoid recomputing wallet balances unless a TransactionChanged or
// BlockTip notification was received.
2020-06-22 23:35:48 +03:00
if ( ! fForceCheckBalanceChanged & & m_cached_last_update_tip = = getLastBlockProcessed ( ) ) return ;
2020-04-02 07:42:01 -04:00
2017-04-17 18:56:44 -04:00
// Try to get balances and return early if locks can't be acquired. This
// avoids the GUI from getting stuck on periodical polls if the core is
// holding the locks for a longer time - for example, during a wallet
// rescan.
2018-04-07 03:42:02 -04:00
interfaces : : WalletBalances new_balances ;
2020-01-23 19:31:16 -03:00
uint256 block_hash ;
if ( ! m_wallet - > tryGetBalances ( new_balances , block_hash ) ) {
2014-04-23 08:40:48 +02:00
return ;
2017-04-17 18:56:44 -04:00
}
2014-04-23 08:40:48 +02:00
2020-01-23 19:31:16 -03:00
if ( fForceCheckBalanceChanged | | block_hash ! = m_cached_last_update_tip ) {
2020-04-02 07:55:14 -04:00
fForceCheckBalanceChanged = false ;
2014-08-17 02:34:42 +02:00
2020-04-02 07:55:14 -04:00
// Balance and number of transactions might have changed
2020-01-23 19:31:16 -03:00
m_cached_last_update_tip = block_hash ;
2014-04-23 08:40:48 +02:00
2020-04-02 07:55:14 -04:00
checkBalanceChanged ( new_balances ) ;
if ( transactionTableModel )
transactionTableModel - > updateConfirmations ( ) ;
}
2012-07-06 01:43:28 +10:00
}
2018-04-07 03:42:02 -04:00
void WalletModel : : checkBalanceChanged ( const interfaces : : WalletBalances & new_balances )
2012-07-06 01:43:28 +10:00
{
2022-05-09 11:24:20 -03:00
if ( new_balances . balanceChanged ( m_cached_balances ) ) {
2017-04-17 18:56:44 -04:00
m_cached_balances = new_balances ;
2018-03-31 06:41:33 -04:00
Q_EMIT balanceChanged ( new_balances ) ;
2012-07-06 01:43:28 +10:00
}
}
2012-02-14 22:08:00 +11:00
2022-05-09 11:24:20 -03:00
interfaces : : WalletBalances WalletModel : : getCachedBalance ( ) const
{
return m_cached_balances ;
}
2014-10-28 19:52:21 +01:00
void WalletModel : : updateTransaction ( )
2012-07-06 01:43:28 +10:00
{
// Balance and number of transactions might have changed
2014-08-17 02:34:42 +02:00
fForceCheckBalanceChanged = true ;
2011-06-30 18:05:29 +02:00
}
2013-09-04 11:52:45 +02:00
void WalletModel : : updateAddressBook ( const QString & address , const QString & label ,
2023-02-20 17:45:32 -05:00
bool isMine , wallet : : AddressPurpose purpose , int status )
2012-03-24 18:48:18 +01:00
{
2012-05-05 16:07:14 +02:00
if ( addressTableModel )
2013-08-29 16:19:43 +02:00
addressTableModel - > updateEntry ( address , label , isMine , purpose , status ) ;
2012-03-24 18:48:18 +01:00
}
2014-07-26 21:05:11 +02:00
void WalletModel : : updateWatchOnlyFlag ( bool fHaveWatchonly )
{
fHaveWatchOnly = fHaveWatchonly ;
2015-07-14 13:59:05 +02:00
Q_EMIT notifyWatchonlyChanged ( fHaveWatchonly ) ;
2014-07-26 21:05:11 +02:00
}
2022-04-23 14:13:05 +02:00
bool WalletModel : : validateAddress ( const QString & address ) const
2011-06-30 18:05:29 +02:00
{
2017-08-22 18:02:33 -07:00
return IsValidDestinationString ( address . toStdString ( ) ) ;
2011-07-16 19:01:05 +02:00
}
2017-06-28 16:41:55 -04:00
WalletModel : : SendCoinsReturn WalletModel : : prepareTransaction ( WalletModelTransaction & transaction , const CCoinControl & coinControl )
2011-07-16 19:01:05 +02:00
{
2014-04-22 15:46:19 -07:00
CAmount total = 0 ;
2014-07-23 14:34:36 +02:00
bool fSubtractFeeFromAmount = false ;
2013-08-30 20:04:48 +02:00
QList < SendCoinsRecipient > recipients = transaction . getRecipients ( ) ;
2014-07-23 14:34:36 +02:00
std : : vector < CRecipient > vecSend ;
2011-07-16 19:01:05 +02:00
if ( recipients . empty ( ) )
2011-06-30 18:05:29 +02:00
{
2011-07-16 19:01:05 +02:00
return OK ;
2011-06-30 18:05:29 +02:00
}
2013-07-22 16:50:39 +10:00
QSet < QString > setAddress ; // Used to detect duplicates
int nAddresses = 0 ;
2011-07-16 19:01:05 +02:00
// Pre-check input data for validity
2017-06-02 03:25:02 +02:00
for ( const SendCoinsRecipient & rcp : recipients )
2011-06-30 18:05:29 +02:00
{
2014-07-23 14:34:36 +02:00
if ( rcp . fSubtractFeeFromAmount )
fSubtractFeeFromAmount = true ;
2013-07-22 16:50:39 +10:00
{ // User-entered bitcoin address / amount:
if ( ! validateAddress ( rcp . address ) )
{
return InvalidAddress ;
}
if ( rcp . amount < = 0 )
{
return InvalidAmount ;
}
setAddress . insert ( rcp . address ) ;
+ + nAddresses ;
2011-07-16 19:01:05 +02:00
2023-10-25 12:13:44 +02:00
CRecipient recipient { DecodeDestination ( rcp . address . toStdString ( ) ) , rcp . amount , rcp . fSubtractFeeFromAmount } ;
2014-07-23 14:34:36 +02:00
vecSend . push_back ( recipient ) ;
2013-07-22 16:50:39 +10:00
total + = rcp . amount ;
2011-07-16 19:01:05 +02:00
}
2011-06-30 18:05:29 +02:00
}
2013-07-22 16:50:39 +10:00
if ( setAddress . size ( ) ! = nAddresses )
2011-07-16 19:01:05 +02:00
{
return DuplicateAddress ;
}
2022-04-27 11:26:15 -03:00
// If no coin was manually selected, use the cached balance
// Future: can merge this call with 'createTransaction'.
CAmount nBalance = getAvailableBalance ( & coinControl ) ;
2013-08-12 17:03:03 +02:00
if ( total > nBalance )
2011-06-30 18:05:29 +02:00
{
return AmountExceedsBalance ;
}
2022-12-15 10:19:23 -03:00
try {
2014-04-22 15:46:19 -07:00
CAmount nFeeRequired = 0 ;
2014-07-23 14:34:36 +02:00
int nChangePosRet = - 1 ;
2013-08-30 20:04:48 +02:00
2017-04-17 18:56:44 -04:00
auto & newTx = transaction . getWtx ( ) ;
2022-10-03 09:37:27 +01:00
const auto & res = m_wallet - > createTransaction ( vecSend , coinControl , /*sign=*/ ! wallet ( ) . privateKeysDisabled ( ) , nChangePosRet , nFeeRequired ) ;
2022-07-21 08:30:39 -04:00
newTx = res ? * res : nullptr ;
2013-08-30 20:04:48 +02:00
transaction . setTransactionFee ( nFeeRequired ) ;
2017-04-17 18:56:44 -04:00
if ( fSubtractFeeFromAmount & & newTx )
2014-07-23 14:34:36 +02:00
transaction . reassignAmounts ( nChangePosRet ) ;
2011-06-30 18:05:29 +02:00
2017-04-17 18:56:44 -04:00
if ( ! newTx )
2011-06-30 18:05:29 +02:00
{
2014-07-23 14:34:36 +02:00
if ( ! fSubtractFeeFromAmount & & ( total + nFeeRequired ) > nBalance )
2011-07-16 19:01:05 +02:00
{
2013-08-30 20:04:48 +02:00
return SendCoinsReturn ( AmountWithFeeExceedsBalance ) ;
2011-07-16 19:01:05 +02:00
}
2022-07-21 08:30:39 -04:00
Q_EMIT message ( tr ( " Send Coins " ) , QString : : fromStdString ( util : : ErrorString ( res ) . translated ) ,
2020-04-18 15:18:17 -04:00
CClientUIInterface : : MSG_ERROR ) ;
2011-07-16 19:01:05 +02:00
return TransactionCreationFailed ;
2011-06-30 18:05:29 +02:00
}
2014-11-02 00:14:47 +01:00
2019-07-02 15:16:36 +01:00
// Reject absurdly high fee. (This can never happen because the
// wallet never creates transactions with fee greater than
// m_default_max_tx_fee. This merely a belt-and-suspenders check).
if ( nFeeRequired > m_wallet - > getDefaultMaxTxFee ( ) ) {
2015-01-31 02:54:55 +00:00
return AbsurdFee ;
2019-07-02 15:16:36 +01:00
}
2022-12-15 10:19:23 -03:00
} catch ( const std : : runtime_error & err ) {
// Something unexpected happened, instruct user to report this bug.
Q_EMIT message ( tr ( " Send Coins " ) , QString : : fromStdString ( err . what ( ) ) ,
CClientUIInterface : : MSG_ERROR ) ;
return TransactionCreationFailed ;
2013-08-30 20:04:48 +02:00
}
return SendCoinsReturn ( OK ) ;
}
2022-05-29 17:40:30 +02:00
void WalletModel : : sendCoins ( WalletModelTransaction & transaction )
2013-08-30 20:04:48 +02:00
{
QByteArray transaction_array ; /* store serialized transaction */
{
2017-02-02 15:30:03 -05:00
std : : vector < std : : pair < std : : string , std : : string > > vOrderForm ;
2017-06-02 03:25:02 +02:00
for ( const SendCoinsRecipient & rcp : transaction . getRecipients ( ) )
2013-07-22 16:50:39 +10:00
{
2017-11-06 19:12:47 +01:00
if ( ! rcp . message . isEmpty ( ) ) // Message from normal bitcoin:URI (bitcoin:123...?message=example)
2017-02-02 15:30:03 -05:00
vOrderForm . emplace_back ( " Message " , rcp . message . toStdString ( ) ) ;
2011-06-30 18:05:29 +02:00
}
2013-08-30 20:04:48 +02:00
2017-04-17 18:56:44 -04:00
auto & newTx = transaction . getWtx ( ) ;
2022-10-03 09:37:27 +01:00
wallet ( ) . commitTransaction ( newTx , /*value_map=*/ { } , std : : move ( vOrderForm ) ) ;
2013-07-22 16:50:39 +10:00
2023-09-07 19:16:57 +10:00
DataStream ssTx ;
ssTx < < TX_WITH_WITNESS ( * newTx ) ;
2021-04-30 20:03:35 +02:00
transaction_array . append ( ( const char * ) ssTx . data ( ) , ssTx . size ( ) ) ;
2011-06-30 18:05:29 +02:00
}
2017-06-20 01:57:31 +03:00
// Add addresses / update labels that we've sent to the address book,
2013-08-30 20:04:48 +02:00
// and emit coinsSent signal for each recipient
2017-06-02 03:25:02 +02:00
for ( const SendCoinsRecipient & rcp : transaction . getRecipients ( ) )
2011-06-30 18:05:29 +02:00
{
2011-07-16 19:01:05 +02:00
{
2013-10-08 14:23:57 +02:00
std : : string strAddress = rcp . address . toStdString ( ) ;
2017-08-22 18:02:33 -07:00
CTxDestination dest = DecodeDestination ( strAddress ) ;
2013-10-08 14:23:57 +02:00
std : : string strLabel = rcp . label . toStdString ( ) ;
2012-05-03 14:52:15 +02:00
{
2013-10-08 14:23:57 +02:00
// Check if we have a new address or an updated label
2017-04-17 18:56:44 -04:00
std : : string name ;
2018-04-10 11:50:10 -04:00
if ( ! m_wallet - > getAddress (
dest , & name , /* is_mine= */ nullptr , /* purpose= */ nullptr ) )
2013-10-08 14:23:57 +02:00
{
2023-02-20 17:45:32 -05:00
m_wallet - > setAddressBook ( dest , strLabel , wallet : : AddressPurpose : : SEND ) ;
2013-10-08 14:23:57 +02:00
}
2017-04-17 18:56:44 -04:00
else if ( name ! = strLabel )
2013-10-08 14:23:57 +02:00
{
2023-02-20 17:45:32 -05:00
m_wallet - > setAddressBook ( dest , strLabel , { } ) ; // {} means don't change purpose
2013-10-08 14:23:57 +02:00
}
2012-05-03 14:52:15 +02:00
}
2011-07-16 19:01:05 +02:00
}
2018-03-23 17:14:39 -04:00
Q_EMIT coinsSent ( this , rcp , transaction_array ) ;
2011-06-30 18:05:29 +02:00
}
2017-04-17 18:56:44 -04:00
checkBalanceChanged ( m_wallet - > getBalances ( ) ) ; // update balance immediately, otherwise there could be a short noticeable delay until pollBalanceChanged hits
2011-06-30 18:05:29 +02:00
}
2022-04-23 14:13:05 +02:00
OptionsModel * WalletModel : : getOptionsModel ( ) const
2011-06-30 18:05:29 +02:00
{
return optionsModel ;
}
2022-04-23 14:13:05 +02:00
AddressTableModel * WalletModel : : getAddressTableModel ( ) const
2011-06-30 18:05:29 +02:00
{
return addressTableModel ;
}
2022-04-23 14:13:05 +02:00
TransactionTableModel * WalletModel : : getTransactionTableModel ( ) const
2011-06-30 18:05:29 +02:00
{
return transactionTableModel ;
}
2011-07-02 13:45:59 +02:00
2022-04-23 14:13:05 +02:00
RecentRequestsTableModel * WalletModel : : getRecentRequestsTableModel ( ) const
2013-11-05 18:03:05 +01:00
{
return recentRequestsTableModel ;
}
2011-08-23 20:08:42 +02:00
WalletModel : : EncryptionStatus WalletModel : : getEncryptionStatus ( ) const
{
2017-04-17 18:56:44 -04:00
if ( ! m_wallet - > isCrypted ( ) )
2011-08-23 20:08:42 +02:00
{
2022-07-15 11:34:21 -04:00
// A previous bug allowed for watchonly wallets to be encrypted (encryption keys set, but nothing is actually encrypted).
// To avoid misrepresenting the encryption status of such wallets, we only return NoKeys for watchonly wallets that are unencrypted.
if ( m_wallet - > privateKeysDisabled ( ) ) {
return NoKeys ;
}
2011-08-23 20:08:42 +02:00
return Unencrypted ;
}
2017-04-17 18:56:44 -04:00
else if ( m_wallet - > isLocked ( ) )
2011-08-23 20:08:42 +02:00
{
return Locked ;
}
else
{
return Unlocked ;
}
}
2011-08-24 22:07:26 +02:00
2020-10-28 18:43:42 +02:00
bool WalletModel : : setWalletEncrypted ( const SecureString & passphrase )
2011-08-24 22:07:26 +02:00
{
2020-10-28 18:43:42 +02:00
return m_wallet - > encryptWallet ( passphrase ) ;
2011-08-24 22:07:26 +02:00
}
2011-11-26 06:02:04 +00:00
bool WalletModel : : setWalletLocked ( bool locked , const SecureString & passPhrase )
2011-08-24 22:07:26 +02:00
{
if ( locked )
{
// Lock
2017-04-17 18:56:44 -04:00
return m_wallet - > lock ( ) ;
2011-08-24 22:07:26 +02:00
}
else
{
// Unlock
2017-04-17 18:56:44 -04:00
return m_wallet - > unlock ( passPhrase ) ;
2011-08-24 22:07:26 +02:00
}
}
2011-11-26 06:02:04 +00:00
bool WalletModel : : changePassphrase ( const SecureString & oldPass , const SecureString & newPass )
2011-08-24 22:07:26 +02:00
{
2017-04-17 18:56:44 -04:00
m_wallet - > lock ( ) ; // Make sure wallet is locked before attempting pass change
return m_wallet - > changeWalletPassphrase ( oldPass , newPass ) ;
2012-02-14 23:14:43 +11:00
}
2012-05-06 19:40:58 +02:00
// Handlers for core signals
2018-06-05 11:17:28 +01:00
static void NotifyUnload ( WalletModel * walletModel )
{
qDebug ( ) < < " NotifyUnload " ;
2019-07-06 17:16:01 +01:00
bool invoked = QMetaObject : : invokeMethod ( walletModel , " unload " ) ;
assert ( invoked ) ;
2018-06-05 11:17:28 +01:00
}
2017-04-17 18:56:44 -04:00
static void NotifyKeyStoreStatusChanged ( WalletModel * walletmodel )
2012-05-06 19:40:58 +02:00
{
2013-09-04 11:52:45 +02:00
qDebug ( ) < < " NotifyKeyStoreStatusChanged " ;
2019-07-06 17:16:01 +01:00
bool invoked = QMetaObject : : invokeMethod ( walletmodel , " updateStatus " , Qt : : QueuedConnection ) ;
assert ( invoked ) ;
2012-05-06 19:40:58 +02:00
}
2017-04-17 18:56:44 -04:00
static void NotifyAddressBookChanged ( WalletModel * walletmodel ,
2013-08-29 16:19:43 +02:00
const CTxDestination & address , const std : : string & label , bool isMine ,
2023-02-20 17:45:32 -05:00
wallet : : AddressPurpose purpose , ChangeType status )
2012-05-06 19:40:58 +02:00
{
2017-08-22 18:02:33 -07:00
QString strAddress = QString : : fromStdString ( EncodeDestination ( address ) ) ;
2013-09-04 11:52:45 +02:00
QString strLabel = QString : : fromStdString ( label ) ;
2023-02-20 17:45:32 -05:00
qDebug ( ) < < " NotifyAddressBookChanged: " + strAddress + " " + strLabel + " isMine= " + QString : : number ( isMine ) + " purpose= " + QString : : number ( static_cast < uint8_t > ( purpose ) ) + " status= " + QString : : number ( status ) ;
2022-04-23 15:19:11 +02:00
bool invoked = QMetaObject : : invokeMethod ( walletmodel , " updateAddressBook " ,
2013-09-04 11:52:45 +02:00
Q_ARG ( QString , strAddress ) ,
Q_ARG ( QString , strLabel ) ,
2012-05-06 22:41:35 +02:00
Q_ARG ( bool , isMine ) ,
2023-02-20 17:45:32 -05:00
Q_ARG ( wallet : : AddressPurpose , purpose ) ,
2012-05-06 19:40:58 +02:00
Q_ARG ( int , status ) ) ;
2019-07-06 17:16:01 +01:00
assert ( invoked ) ;
2012-05-06 19:40:58 +02:00
}
2017-04-17 18:56:44 -04:00
static void NotifyTransactionChanged ( WalletModel * walletmodel , const uint256 & hash , ChangeType status )
2012-05-06 19:40:58 +02:00
{
2014-10-28 19:52:21 +01:00
Q_UNUSED ( hash ) ;
Q_UNUSED ( status ) ;
2019-07-06 17:16:01 +01:00
bool invoked = QMetaObject : : invokeMethod ( walletmodel , " updateTransaction " , Qt : : QueuedConnection ) ;
assert ( invoked ) ;
2012-05-06 19:40:58 +02:00
}
2014-03-19 00:26:14 +01:00
static void ShowProgress ( WalletModel * walletmodel , const std : : string & title , int nProgress )
{
2014-08-17 02:34:42 +02:00
// emits signal "showProgress"
2019-07-06 17:16:01 +01:00
bool invoked = QMetaObject : : invokeMethod ( walletmodel , " showProgress " , Qt : : QueuedConnection ,
2014-08-17 02:34:42 +02:00
Q_ARG ( QString , QString : : fromStdString ( title ) ) ,
Q_ARG ( int , nProgress ) ) ;
2019-07-06 17:16:01 +01:00
assert ( invoked ) ;
2014-03-19 00:26:14 +01:00
}
2014-07-26 21:05:11 +02:00
static void NotifyWatchonlyChanged ( WalletModel * walletmodel , bool fHaveWatchonly )
{
2019-07-06 17:16:01 +01:00
bool invoked = QMetaObject : : invokeMethod ( walletmodel , " updateWatchOnlyFlag " , Qt : : QueuedConnection ,
2014-07-26 21:05:11 +02:00
Q_ARG ( bool , fHaveWatchonly ) ) ;
2019-07-06 17:16:01 +01:00
assert ( invoked ) ;
2014-07-26 21:05:11 +02:00
}
2019-01-18 17:05:32 -05:00
static void NotifyCanGetAddressesChanged ( WalletModel * walletmodel )
{
2019-07-06 17:16:01 +01:00
bool invoked = QMetaObject : : invokeMethod ( walletmodel , " canGetAddressesChanged " ) ;
assert ( invoked ) ;
2019-01-18 17:05:32 -05:00
}
2012-05-06 19:40:58 +02:00
void WalletModel : : subscribeToCoreSignals ( )
{
// Connect signals to wallet
2018-10-17 23:51:17 +08:00
m_handler_unload = m_wallet - > handleUnload ( std : : bind ( & NotifyUnload , this ) ) ;
m_handler_status_changed = m_wallet - > handleStatusChanged ( std : : bind ( & NotifyKeyStoreStatusChanged , this ) ) ;
m_handler_address_book_changed = m_wallet - > handleAddressBookChanged ( std : : bind ( NotifyAddressBookChanged , this , std : : placeholders : : _1 , std : : placeholders : : _2 , std : : placeholders : : _3 , std : : placeholders : : _4 , std : : placeholders : : _5 ) ) ;
m_handler_transaction_changed = m_wallet - > handleTransactionChanged ( std : : bind ( NotifyTransactionChanged , this , std : : placeholders : : _1 , std : : placeholders : : _2 ) ) ;
m_handler_show_progress = m_wallet - > handleShowProgress ( std : : bind ( ShowProgress , this , std : : placeholders : : _1 , std : : placeholders : : _2 ) ) ;
m_handler_watch_only_changed = m_wallet - > handleWatchOnlyChanged ( std : : bind ( NotifyWatchonlyChanged , this , std : : placeholders : : _1 ) ) ;
2020-08-31 19:34:57 +08:00
m_handler_can_get_addrs_changed = m_wallet - > handleCanGetAddressesChanged ( std : : bind ( NotifyCanGetAddressesChanged , this ) ) ;
2012-05-06 19:40:58 +02:00
}
void WalletModel : : unsubscribeFromCoreSignals ( )
{
// Disconnect signals from wallet
2018-06-05 11:17:28 +01:00
m_handler_unload - > disconnect ( ) ;
2017-04-17 18:56:44 -04:00
m_handler_status_changed - > disconnect ( ) ;
m_handler_address_book_changed - > disconnect ( ) ;
m_handler_transaction_changed - > disconnect ( ) ;
m_handler_show_progress - > disconnect ( ) ;
m_handler_watch_only_changed - > disconnect ( ) ;
2019-01-18 17:05:32 -05:00
m_handler_can_get_addrs_changed - > disconnect ( ) ;
2012-05-06 19:40:58 +02:00
}
2011-08-24 22:07:26 +02:00
// WalletModel::UnlockContext implementation
WalletModel : : UnlockContext WalletModel : : requestUnlock ( )
{
2023-10-24 17:23:36 -04:00
// Bugs in earlier versions may have resulted in wallets with private keys disabled to become "encrypted"
// (encryption keys are present, but not actually doing anything).
// To avoid issues with such wallets, check if the wallet has private keys disabled, and if so, return a context
// that indicates the wallet is not encrypted.
if ( m_wallet - > privateKeysDisabled ( ) ) {
return UnlockContext ( this , /*valid=*/ true , /*relock=*/ false ) ;
}
2011-08-24 22:07:26 +02:00
bool was_locked = getEncryptionStatus ( ) = = Locked ;
if ( was_locked )
{
// Request UI to unlock wallet
2015-07-14 13:59:05 +02:00
Q_EMIT requireUnlock ( ) ;
2011-08-24 22:07:26 +02:00
}
// If wallet is still locked, unlock was failed or cancelled, mark context as invalid
bool valid = getEncryptionStatus ( ) ! = Locked ;
return UnlockContext ( this , valid , was_locked ) ;
}
2016-09-09 13:43:29 +02:00
WalletModel : : UnlockContext : : UnlockContext ( WalletModel * _wallet , bool _valid , bool _relock ) :
wallet ( _wallet ) ,
valid ( _valid ) ,
relock ( _relock )
2011-08-24 22:07:26 +02:00
{
}
WalletModel : : UnlockContext : : ~ UnlockContext ( )
{
if ( valid & & relock )
{
wallet - > setWalletLocked ( true ) ;
}
}
2018-08-01 18:43:46 +02:00
bool WalletModel : : bumpFee ( uint256 hash , uint256 & new_hash )
2017-02-03 22:04:39 +01:00
{
2017-06-15 10:34:17 -04:00
CCoinControl coin_control ;
2018-04-07 12:12:46 -04:00
coin_control . m_signal_bip125_rbf = true ;
2020-04-18 15:18:17 -04:00
std : : vector < bilingual_str > errors ;
2017-06-15 10:34:17 -04:00
CAmount old_fee ;
CAmount new_fee ;
CMutableTransaction mtx ;
2020-03-10 18:40:57 +01:00
if ( ! m_wallet - > createBumpTransaction ( hash , coin_control , errors , old_fee , new_fee , mtx ) ) {
2018-07-31 14:02:34 -04:00
QMessageBox : : critical ( nullptr , tr ( " Fee bump error " ) , tr ( " Increasing transaction fee failed " ) + " <br />( " +
2020-04-18 15:18:17 -04:00
( errors . size ( ) ? QString : : fromStdString ( errors [ 0 ] . translated ) : " " ) + " ) " ) ;
return false ;
2017-02-03 22:04:39 +01:00
}
// allow a user based fee verification
2020-04-27 16:57:39 -04:00
/*: Asks a user if they would like to manually increase the fee of a transaction that has already been created. */
QString questionString = tr ( " Do you want to increase the fee? " ) ;
2017-04-26 09:55:54 +02:00
questionString . append ( " <br /> " ) ;
questionString . append ( " <table style= \" text-align: left; \" > " ) ;
questionString . append ( " <tr><td> " ) ;
questionString . append ( tr ( " Current fee: " ) ) ;
questionString . append ( " </td><td> " ) ;
2017-06-15 10:34:17 -04:00
questionString . append ( BitcoinUnits : : formatHtmlWithUnit ( getOptionsModel ( ) - > getDisplayUnit ( ) , old_fee ) ) ;
2017-04-26 09:55:54 +02:00
questionString . append ( " </td></tr><tr><td> " ) ;
questionString . append ( tr ( " Increase: " ) ) ;
questionString . append ( " </td><td> " ) ;
2017-06-15 10:34:17 -04:00
questionString . append ( BitcoinUnits : : formatHtmlWithUnit ( getOptionsModel ( ) - > getDisplayUnit ( ) , new_fee - old_fee ) ) ;
2017-04-26 09:55:54 +02:00
questionString . append ( " </td></tr><tr><td> " ) ;
questionString . append ( tr ( " New fee: " ) ) ;
questionString . append ( " </td><td> " ) ;
2017-06-15 10:34:17 -04:00
questionString . append ( BitcoinUnits : : formatHtmlWithUnit ( getOptionsModel ( ) - > getDisplayUnit ( ) , new_fee ) ) ;
2017-04-26 09:55:54 +02:00
questionString . append ( " </td></tr></table> " ) ;
2021-01-17 05:14:09 +05:30
// Display warning in the "Confirm fee bump" window if the "Coin Control Features" option is enabled
if ( getOptionsModel ( ) - > getCoinControlFeatures ( ) ) {
questionString . append ( " <br><br> " ) ;
questionString . append ( tr ( " Warning: This may pay the additional fee by reducing change outputs or adding inputs, when necessary. It may add a new change output if one does not already exist. These changes may potentially leak privacy. " ) ) ;
}
2022-04-25 18:14:28 +02:00
const bool enable_send { ! wallet ( ) . privateKeysDisabled ( ) | | wallet ( ) . hasExternalSigner ( ) } ;
const bool always_show_unsigned { getOptionsModel ( ) - > getEnablePSBTControls ( ) } ;
auto confirmationDialog = new SendConfirmationDialog ( tr ( " Confirm fee bump " ) , questionString , " " , " " , SEND_CONFIRM_DELAY , enable_send , always_show_unsigned , nullptr ) ;
2021-06-07 18:43:13 +03:00
confirmationDialog - > setAttribute ( Qt : : WA_DeleteOnClose ) ;
// TODO: Replace QDialog::exec() with safer QDialog::show().
const auto retval = static_cast < QMessageBox : : StandardButton > ( confirmationDialog - > exec ( ) ) ;
2017-02-03 22:04:39 +01:00
2018-03-18 16:26:45 +02:00
// cancel sign&broadcast if user doesn't want to bump the fee
2020-04-27 16:57:39 -04:00
if ( retval ! = QMessageBox : : Yes & & retval ! = QMessageBox : : Save ) {
2017-02-03 22:04:39 +01:00
return false ;
}
2019-11-22 15:32:09 -05:00
// Short-circuit if we are returning a bumped transaction PSBT to clipboard
2020-04-27 16:57:39 -04:00
if ( retval = = QMessageBox : : Save ) {
2022-04-25 18:14:28 +02:00
// "Create Unsigned" clicked
2019-11-22 15:32:09 -05:00
PartiallySignedTransaction psbtx ( mtx ) ;
bool complete = false ;
2023-12-13 11:43:16 -05:00
const auto err { wallet ( ) . fillPSBT ( SIGHASH_ALL , /*sign=*/ false , /*bip32derivs=*/ true , nullptr , psbtx , complete ) } ;
if ( err | | complete ) {
2020-01-14 09:27:08 -05:00
QMessageBox : : critical ( nullptr , tr ( " Fee bump error " ) , tr ( " Can't draft transaction. " ) ) ;
return false ;
}
2019-11-22 15:32:09 -05:00
// Serialize the PSBT
2023-11-19 14:58:00 +01:00
DataStream ssTx { } ;
2019-11-22 15:32:09 -05:00
ssTx < < psbtx ;
GUIUtil : : setClipboard ( EncodeBase64 ( ssTx . str ( ) ) . c_str ( ) ) ;
2024-03-29 11:40:38 -03:00
Q_EMIT message ( tr ( " PSBT copied " ) , tr ( " Fee-bump PSBT copied to clipboard " ) , CClientUIInterface : : MSG_INFORMATION | CClientUIInterface : : MODAL ) ;
2019-11-22 15:32:09 -05:00
return true ;
}
2024-03-29 11:40:38 -03:00
WalletModel : : UnlockContext ctx ( requestUnlock ( ) ) ;
if ( ! ctx . isValid ( ) ) {
return false ;
}
2022-04-25 18:14:28 +02:00
assert ( ! m_wallet - > privateKeysDisabled ( ) | | wallet ( ) . hasExternalSigner ( ) ) ;
2020-04-27 16:57:39 -04:00
2017-02-03 22:04:39 +01:00
// sign bumped transaction
2017-04-17 18:56:44 -04:00
if ( ! m_wallet - > signBumpTransaction ( mtx ) ) {
2018-07-31 14:02:34 -04:00
QMessageBox : : critical ( nullptr , tr ( " Fee bump error " ) , tr ( " Can't sign transaction. " ) ) ;
2017-02-03 22:04:39 +01:00
return false ;
}
// commit the bumped transaction
2018-08-01 18:43:46 +02:00
if ( ! m_wallet - > commitBumpTransaction ( hash , std : : move ( mtx ) , errors , new_hash ) ) {
2018-07-31 14:02:34 -04:00
QMessageBox : : critical ( nullptr , tr ( " Fee bump error " ) , tr ( " Could not commit transaction " ) + " <br />( " +
2020-04-18 15:18:17 -04:00
QString : : fromStdString ( errors [ 0 ] . translated ) + " ) " ) ;
return false ;
2017-02-03 22:04:39 +01:00
}
return true ;
}
2024-02-13 13:25:59 +01:00
void WalletModel : : displayAddress ( std : : string sAddress ) const
2019-09-16 19:01:04 +02:00
{
CTxDestination dest = DecodeDestination ( sAddress ) ;
try {
2024-02-13 13:25:59 +01:00
util : : Result < void > result = m_wallet - > displayAddress ( dest ) ;
if ( ! result ) {
QMessageBox : : warning ( nullptr , tr ( " Signer error " ) , QString : : fromStdString ( util : : ErrorString ( result ) . translated ) ) ;
}
2019-09-16 19:01:04 +02:00
} catch ( const std : : runtime_error & e ) {
QMessageBox : : critical ( nullptr , tr ( " Can't display address " ) , e . what ( ) ) ;
}
}
2016-09-21 12:37:00 +02:00
bool WalletModel : : isWalletEnabled ( )
{
2017-08-01 21:17:40 +02:00
return ! gArgs . GetBoolArg ( " -disablewallet " , DEFAULT_DISABLE_WALLET ) ;
2016-09-21 12:37:00 +02:00
}
2018-03-06 12:26:40 +08:00
QString WalletModel : : getWalletName ( ) const
{
2017-04-17 18:56:44 -04:00
return QString : : fromStdString ( m_wallet - > getWalletName ( ) ) ;
2018-03-06 12:26:40 +08:00
}
2018-06-25 22:22:01 +01:00
QString WalletModel : : getDisplayName ( ) const
{
2024-06-10 15:39:35 -04:00
return GUIUtil : : WalletDisplayName ( getWalletName ( ) ) ;
2018-06-25 22:22:01 +01:00
}
2022-04-23 14:13:05 +02:00
bool WalletModel : : isMultiwallet ( ) const
2018-03-06 12:26:40 +08:00
{
2021-12-22 13:44:55 -05:00
return m_node . walletLoader ( ) . getWallets ( ) . size ( ) > 1 ;
2018-03-06 12:26:40 +08:00
}
2020-01-13 19:50:44 +01:00
void WalletModel : : refresh ( bool pk_hash_only )
{
addressTableModel = new AddressTableModel ( this , pk_hash_only ) ;
}
2020-06-22 23:35:48 +03:00
uint256 WalletModel : : getLastBlockProcessed ( ) const
{
return m_client_model ? m_client_model - > getBestBlockHash ( ) : uint256 { } ;
}
2022-04-27 11:26:15 -03:00
CAmount WalletModel : : getAvailableBalance ( const CCoinControl * control )
{
2022-12-14 22:30:59 -03:00
// No selected coins, return the cached balance
if ( ! control | | ! control - > HasSelected ( ) ) {
const interfaces : : WalletBalances & balances = getCachedBalance ( ) ;
CAmount available_balance = balances . balance ;
// if wallet private keys are disabled, this is a watch-only wallet
// so, let's include the watch-only balance.
if ( balances . have_watch_only & & m_wallet - > privateKeysDisabled ( ) ) {
available_balance + = balances . watch_only_balance ;
}
return available_balance ;
}
// Fetch balance from the wallet, taking into account the selected coins
return wallet ( ) . getAvailableBalance ( * control ) ;
2022-04-27 11:26:15 -03:00
}