2015-12-13 14:51:43 +01:00
// Copyright (c) 2011-2015 The Bitcoin Core developers
2016-12-20 14:26:45 +01:00
// Copyright (c) 2014-2017 The Dash Core developers
2014-12-13 05:09:33 +01: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.
2011-06-30 18:05:29 +02:00
# include "walletmodel.h"
2013-04-13 07:13:08 +02:00
2011-06-30 18:05:29 +02:00
# include "addresstablemodel.h"
2013-04-13 07:13:08 +02:00
# include "guiconstants.h"
2014-07-23 14:34:36 +02:00
# include "guiutil.h"
2015-01-08 14:42:04 +01:00
# include "paymentserver.h"
2013-11-05 18:03:05 +01:00
# include "recentrequeststablemodel.h"
2013-12-16 22:54:02 +01:00
# include "transactiontablemodel.h"
2011-06-30 18:05:29 +02:00
2013-04-13 07:13:08 +02:00
# include "base58.h"
# include "keystore.h"
2017-08-09 02:19:06 +02:00
# include "validation.h"
# include "net.h" // for g_connman
2013-04-13 07:13:08 +02:00
# include "sync.h"
2012-04-16 14:56:45 +02:00
# include "ui_interface.h"
2017-08-09 02:19:06 +02:00
# include "util.h" // for GetBoolArg
2015-02-03 21:09:47 +01:00
# include "wallet/wallet.h"
# include "wallet/walletdb.h" // for BackupWallet
2011-06-30 18:05:29 +02:00
2016-12-20 14:27:59 +01:00
# include "instantx.h"
# include "spork.h"
2017-05-05 13:26:27 +02:00
# include "privatesend-client.h"
2016-12-20 14:27:59 +01:00
2013-04-13 07:13:08 +02:00
# include <stdint.h>
# include <QDebug>
2011-07-16 19:01:05 +02:00
# include <QSet>
2012-07-05 17:43:28 +02:00
# include <QTimer>
2011-06-30 18:05:29 +02:00
2015-07-05 14:17:46 +02:00
# include <boost/foreach.hpp>
2014-06-16 16:30:38 +02:00
2015-07-28 15:20:14 +02:00
WalletModel : : WalletModel ( const PlatformStyle * platformStyle , CWallet * wallet , OptionsModel * optionsModel , QObject * parent ) :
2011-07-29 14:36:35 +02:00
QObject ( parent ) , wallet ( wallet ) , optionsModel ( optionsModel ) , addressTableModel ( 0 ) ,
2011-07-17 14:06:43 +02:00
transactionTableModel ( 0 ) ,
2013-11-05 18:03:05 +01:00
recentRequestsTableModel ( 0 ) ,
2017-02-09 06:29:00 +01:00
cachedBalance ( 0 ) ,
cachedUnconfirmedBalance ( 0 ) ,
cachedImmatureBalance ( 0 ) ,
cachedAnonymizedBalance ( 0 ) ,
cachedWatchOnlyBalance ( 0 ) ,
cachedWatchUnconfBalance ( 0 ) ,
cachedWatchImmatureBalance ( 0 ) ,
2012-07-05 17:43:28 +02:00
cachedEncryptionStatus ( Unencrypted ) ,
2017-02-09 06:29:00 +01:00
cachedNumBlocks ( 0 ) ,
cachedTxLocks ( 0 ) ,
cachedPrivateSendRounds ( 0 )
2011-06-30 18:05:29 +02:00
{
2014-07-26 21:05:11 +02:00
fHaveWatchOnly = wallet - > HaveWatchOnly ( ) ;
2015-02-10 14:24:05 +01:00
fForceCheckBalanceChanged = false ;
2011-06-30 18:05:29 +02:00
addressTableModel = new AddressTableModel ( wallet , this ) ;
2015-07-28 15:20:14 +02:00
transactionTableModel = new TransactionTableModel ( platformStyle , wallet , this ) ;
2013-11-05 18:03:05 +01:00
recentRequestsTableModel = new RecentRequestsTableModel ( wallet , this ) ;
2012-05-06 19:40:58 +02:00
2012-07-10 15:19:57 +02:00
// This timer will be fired repeatedly to update the balance
2012-07-05 17:43:28 +02:00
pollTimer = new QTimer ( this ) ;
connect ( pollTimer , SIGNAL ( timeout ( ) ) , this , SLOT ( pollBalanceChanged ( ) ) ) ;
2012-07-10 15:19:57 +02:00
pollTimer - > start ( MODEL_UPDATE_DELAY ) ;
2012-07-05 17:43:28 +02:00
2012-05-06 19:40:58 +02:00
subscribeToCoreSignals ( ) ;
}
WalletModel : : ~ WalletModel ( )
{
unsubscribeFromCoreSignals ( ) ;
2011-06-30 18:05:29 +02:00
}
2014-04-23 00:46:19 +02:00
CAmount WalletModel : : getBalance ( const CCoinControl * coinControl ) const
2011-06-30 18:05:29 +02:00
{
2013-08-12 17:03:03 +02:00
if ( coinControl )
{
2014-04-23 00:46:19 +02:00
CAmount nBalance = 0 ;
2013-08-12 17:03:03 +02:00
std : : vector < COutput > vCoins ;
wallet - > AvailableCoins ( vCoins , true , coinControl ) ;
BOOST_FOREACH ( const COutput & out , vCoins )
2013-12-10 15:27:53 +01:00
if ( out . fSpendable )
nBalance + = out . tx - > vout [ out . i ] . nValue ;
2013-08-12 17:03:03 +02:00
return nBalance ;
}
2011-06-30 18:05:29 +02:00
return wallet - > GetBalance ( ) ;
}
2014-12-09 02:17:57 +01:00
2015-04-14 10:51:14 +02:00
CAmount WalletModel : : getAnonymizedBalance ( ) const
2014-12-09 02:17:57 +01:00
{
2015-02-11 00:50:35 +01:00
return wallet - > GetAnonymizedBalance ( ) ;
2014-12-09 02:17:57 +01:00
}
2014-04-23 00:46:19 +02:00
CAmount WalletModel : : getUnconfirmedBalance ( ) const
2011-07-11 20:42:10 +02:00
{
return wallet - > GetUnconfirmedBalance ( ) ;
}
2014-04-23 00:46:19 +02:00
CAmount WalletModel : : getImmatureBalance ( ) const
2012-02-14 12:08:00 +01:00
{
return wallet - > GetImmatureBalance ( ) ;
}
2014-07-26 21:05:11 +02:00
bool WalletModel : : haveWatchOnly ( ) const
2011-06-30 18:05:29 +02:00
{
2014-07-26 21:05:11 +02:00
return fHaveWatchOnly ;
}
2014-04-23 00:46:19 +02:00
CAmount WalletModel : : getWatchBalance ( ) const
2014-03-29 05:15:28 +01:00
{
return wallet - > GetWatchOnlyBalance ( ) ;
}
2014-04-23 00:46:19 +02:00
CAmount WalletModel : : getWatchUnconfirmedBalance ( ) const
2014-03-29 05:15:28 +01:00
{
return wallet - > GetUnconfirmedWatchOnlyBalance ( ) ;
}
2014-04-23 00:46:19 +02:00
CAmount WalletModel : : getWatchImmatureBalance ( ) const
2014-03-29 05:15:28 +01:00
{
return wallet - > GetImmatureWatchOnlyBalance ( ) ;
2011-06-30 18:05:29 +02:00
}
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 ( ) ;
if ( cachedEncryptionStatus ! = newEncryptionStatus )
2015-07-14 13:59:05 +02:00
Q_EMIT encryptionStatusChanged ( newEncryptionStatus ) ;
2012-05-05 16:07:14 +02:00
}
2012-07-05 17:43:28 +02:00
void WalletModel : : pollBalanceChanged ( )
2012-05-05 16:07:14 +02:00
{
2014-04-23 08:40:48 +02:00
// Get required locks upfront. 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.
TRY_LOCK ( cs_main , lockMain ) ;
if ( ! lockMain )
return ;
TRY_LOCK ( wallet - > cs_wallet , lockWallet ) ;
if ( ! lockWallet )
return ;
2017-05-05 13:26:27 +02:00
if ( fForceCheckBalanceChanged | | chainActive . Height ( ) ! = cachedNumBlocks | | privateSendClient . nPrivateSendRounds ! = cachedPrivateSendRounds | | cachedTxLocks ! = nCompleteTXLocks )
2012-07-10 15:19:57 +02:00
{
2014-08-17 02:34:42 +02:00
fForceCheckBalanceChanged = false ;
2014-04-23 08:40:48 +02:00
// Balance and number of transactions might have changed
cachedNumBlocks = chainActive . Height ( ) ;
2017-05-05 13:26:27 +02:00
cachedPrivateSendRounds = privateSendClient . nPrivateSendRounds ;
2014-04-23 08:40:48 +02:00
2012-07-05 17:43:28 +02:00
checkBalanceChanged ( ) ;
2016-02-14 05:51:06 +01:00
if ( transactionTableModel )
2014-04-15 17:38:25 +02:00
transactionTableModel - > updateConfirmations ( ) ;
2012-07-05 17:43:28 +02:00
}
}
void WalletModel : : checkBalanceChanged ( )
{
2014-04-23 00:46:19 +02:00
CAmount newBalance = getBalance ( ) ;
CAmount newUnconfirmedBalance = getUnconfirmedBalance ( ) ;
CAmount newImmatureBalance = getImmatureBalance ( ) ;
2015-04-03 00:51:08 +02:00
CAmount newAnonymizedBalance = getAnonymizedBalance ( ) ;
2014-04-23 00:46:19 +02:00
CAmount newWatchOnlyBalance = 0 ;
CAmount newWatchUnconfBalance = 0 ;
CAmount newWatchImmatureBalance = 0 ;
2014-07-26 21:05:11 +02:00
if ( haveWatchOnly ( ) )
{
newWatchOnlyBalance = getWatchBalance ( ) ;
newWatchUnconfBalance = getWatchUnconfirmedBalance ( ) ;
newWatchImmatureBalance = getWatchImmatureBalance ( ) ;
}
2011-07-17 14:06:43 +02:00
2014-03-29 05:15:28 +01:00
if ( cachedBalance ! = newBalance | | cachedUnconfirmedBalance ! = newUnconfirmedBalance | | cachedImmatureBalance ! = newImmatureBalance | |
2015-04-03 00:51:08 +02:00
cachedAnonymizedBalance ! = newAnonymizedBalance | | cachedTxLocks ! = nCompleteTXLocks | |
2014-03-29 05:15:28 +01:00
cachedWatchOnlyBalance ! = newWatchOnlyBalance | | cachedWatchUnconfBalance ! = newWatchUnconfBalance | | cachedWatchImmatureBalance ! = newWatchImmatureBalance )
2012-07-10 15:19:57 +02:00
{
2012-07-05 17:43:28 +02:00
cachedBalance = newBalance ;
cachedUnconfirmedBalance = newUnconfirmedBalance ;
cachedImmatureBalance = newImmatureBalance ;
2014-12-09 02:17:57 +01:00
cachedAnonymizedBalance = newAnonymizedBalance ;
2015-02-02 16:04:09 +01:00
cachedTxLocks = nCompleteTXLocks ;
2014-03-29 05:15:28 +01:00
cachedWatchOnlyBalance = newWatchOnlyBalance ;
cachedWatchUnconfBalance = newWatchUnconfBalance ;
cachedWatchImmatureBalance = newWatchImmatureBalance ;
2016-02-02 16:28:56 +01:00
Q_EMIT balanceChanged ( newBalance , newUnconfirmedBalance , newImmatureBalance , newAnonymizedBalance ,
2014-03-29 05:15:28 +01:00
newWatchOnlyBalance , newWatchUnconfBalance , newWatchImmatureBalance ) ;
2012-07-05 17:43:28 +02:00
}
}
2012-02-14 12:08:00 +01:00
2015-02-10 14:24:05 +01:00
void WalletModel : : updateTransaction ( )
2012-07-05 17:43:28 +02: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 ,
2013-08-29 16:19:43 +02:00
bool isMine , const QString & 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
}
2011-07-16 19:01:05 +02:00
bool WalletModel : : validateAddress ( const QString & address )
2011-06-30 18:05:29 +02:00
{
2011-07-26 15:38:31 +02:00
CBitcoinAddress addressParsed ( address . toStdString ( ) ) ;
return addressParsed . IsValid ( ) ;
2011-07-16 19:01:05 +02:00
}
2013-08-12 17:03:03 +02:00
WalletModel : : SendCoinsReturn WalletModel : : prepareTransaction ( WalletModelTransaction & transaction , const CCoinControl * coinControl )
2011-07-16 19:01:05 +02:00
{
2014-04-23 00:46:19 +02: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
}
2016-09-11 11:02:54 +02:00
// This should never really happen, yet another safety check, just in case.
if ( wallet - > IsLocked ( ) ) {
return TransactionCreationFailed ;
2014-12-09 02:17:57 +01:00
}
2013-07-22 08:50:39 +02: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
2015-07-14 13:59:05 +02:00
Q_FOREACH ( 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 08:50:39 +02:00
if ( rcp . paymentRequest . IsInitialized ( ) )
2013-09-28 19:29:44 +02:00
{ // PaymentRequest...
2014-04-23 00:46:19 +02:00
CAmount subtotal = 0 ;
2013-07-22 08:50:39 +02:00
const payments : : PaymentDetails & details = rcp . paymentRequest . getDetails ( ) ;
for ( int i = 0 ; i < details . outputs_size ( ) ; i + + )
{
const payments : : Output & out = details . outputs ( i ) ;
if ( out . amount ( ) < = 0 ) continue ;
subtotal + = out . amount ( ) ;
const unsigned char * scriptStr = ( const unsigned char * ) out . script ( ) . data ( ) ;
CScript scriptPubKey ( scriptStr , scriptStr + out . script ( ) . size ( ) ) ;
2014-07-23 14:34:36 +02:00
CAmount nAmount = out . amount ( ) ;
CRecipient recipient = { scriptPubKey , nAmount , rcp . fSubtractFeeFromAmount } ;
vecSend . push_back ( recipient ) ;
2013-07-22 08:50:39 +02:00
}
if ( subtotal < = 0 )
{
return InvalidAmount ;
}
total + = subtotal ;
2011-07-16 19:01:05 +02:00
}
2013-07-22 08:50:39 +02:00
else
2015-03-19 15:15:08 +01:00
{ // User-entered dash address / amount:
2013-07-22 08:50:39 +02:00
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
2014-09-11 19:15:29 +02:00
CScript scriptPubKey = GetScriptForDestination ( CBitcoinAddress ( rcp . address . toStdString ( ) ) . Get ( ) ) ;
2014-07-23 14:34:36 +02:00
CRecipient recipient = { scriptPubKey , rcp . amount , rcp . fSubtractFeeFromAmount } ;
vecSend . push_back ( recipient ) ;
2013-07-22 08:50:39 +02:00
total + = rcp . amount ;
2011-07-16 19:01:05 +02:00
}
2011-06-30 18:05:29 +02:00
}
2013-07-22 08:50:39 +02:00
if ( setAddress . size ( ) ! = nAddresses )
2011-07-16 19:01:05 +02:00
{
return DuplicateAddress ;
}
2014-04-23 00:46:19 +02:00
CAmount nBalance = getBalance ( coinControl ) ;
2013-08-12 17:03:03 +02:00
if ( total > nBalance )
2011-06-30 18:05:29 +02:00
{
return AmountExceedsBalance ;
}
{
2012-04-06 18:39:12 +02:00
LOCK2 ( cs_main , wallet - > cs_wallet ) ;
2013-08-30 20:04:48 +02:00
transaction . newPossibleKeyChange ( wallet ) ;
2014-07-23 14:34:36 +02:00
2014-04-23 00:46:19 +02:00
CAmount nFeeRequired = 0 ;
2014-07-23 14:34:36 +02:00
int nChangePosRet = - 1 ;
2013-04-25 23:31:22 +02:00
std : : string strFailReason ;
2013-08-30 20:04:48 +02:00
CWalletTx * newTx = transaction . getTransaction ( ) ;
CReserveKey * keyChange = transaction . getPossibleKeyChange ( ) ;
2014-12-09 02:17:57 +01:00
2016-09-02 14:17:32 +02:00
if ( recipients [ 0 ] . fUseInstantSend & & total > sporkManager . GetSporkValue ( SPORK_5_INSTANTSEND_MAX_VALUE ) * COIN ) {
2016-09-01 16:55:25 +02:00
Q_EMIT message ( tr ( " Send Coins " ) , tr ( " InstantSend doesn't support sending values that high yet. Transactions are currently limited to %1 DASH. " ) . arg ( sporkManager . GetSporkValue ( SPORK_5_INSTANTSEND_MAX_VALUE ) ) ,
2015-02-11 15:47:21 +01:00
CClientUIInterface : : MSG_ERROR ) ;
return TransactionCreationFailed ;
}
2016-09-02 14:17:32 +02:00
bool fCreated = wallet - > CreateTransaction ( vecSend , * newTx , * keyChange , nFeeRequired , nChangePosRet , strFailReason , coinControl , true , recipients [ 0 ] . inputType , recipients [ 0 ] . fUseInstantSend ) ;
2013-08-30 20:04:48 +02:00
transaction . setTransactionFee ( nFeeRequired ) ;
2014-07-23 14:34:36 +02:00
if ( fSubtractFeeFromAmount & & fCreated )
transaction . reassignAmounts ( nChangePosRet ) ;
2011-06-30 18:05:29 +02:00
2017-01-29 09:22:14 +01:00
if ( recipients [ 0 ] . fUseInstantSend ) {
if ( newTx - > GetValueOut ( ) > sporkManager . GetSporkValue ( SPORK_5_INSTANTSEND_MAX_VALUE ) * COIN ) {
Q_EMIT message ( tr ( " Send Coins " ) , tr ( " InstantSend doesn't support sending values that high yet. Transactions are currently limited to %1 DASH. " ) . arg ( sporkManager . GetSporkValue ( SPORK_5_INSTANTSEND_MAX_VALUE ) ) ,
CClientUIInterface : : MSG_ERROR ) ;
return TransactionCreationFailed ;
}
2017-02-02 23:38:33 +01:00
if ( newTx - > vin . size ( ) > CTxLockRequest : : WARN_MANY_INPUTS ) {
Q_EMIT message ( tr ( " Send Coins " ) , tr ( " Used way too many inputs (>%1) for this InstantSend transaction, fees could be huge. " ) . arg ( CTxLockRequest : : WARN_MANY_INPUTS ) ,
CClientUIInterface : : MSG_WARNING ) ;
2017-01-29 09:22:14 +01:00
}
2015-09-11 20:19:14 +02:00
}
2011-07-16 19:01:05 +02:00
if ( ! fCreated )
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
}
2015-07-14 13:59:05 +02:00
Q_EMIT message ( tr ( " Send Coins " ) , QString : : fromStdString ( strFailReason ) ,
2013-04-25 23:31:22 +02: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
2015-10-25 01:27:24 +02:00
// reject absurdly high fee. (This can never happen because the
// wallet caps the fee at maxTxFee. This merely serves as a
// belt-and-suspenders check)
if ( nFeeRequired > maxTxFee )
2015-01-31 03:54:55 +01:00
return AbsurdFee ;
2013-08-30 20:04:48 +02:00
}
return SendCoinsReturn ( OK ) ;
}
WalletModel : : SendCoinsReturn WalletModel : : sendCoins ( WalletModelTransaction & transaction )
{
QByteArray transaction_array ; /* store serialized transaction */
{
LOCK2 ( cs_main , wallet - > cs_wallet ) ;
CWalletTx * newTx = transaction . getTransaction ( ) ;
2015-02-16 00:42:08 +01:00
QList < SendCoinsRecipient > recipients = transaction . getRecipients ( ) ;
2013-08-30 20:04:48 +02:00
2016-02-02 16:28:56 +01:00
Q_FOREACH ( const SendCoinsRecipient & rcp , recipients )
2013-07-22 08:50:39 +02:00
{
if ( rcp . paymentRequest . IsInitialized ( ) )
{
2015-01-08 14:42:04 +01:00
// Make sure any payment requests involved are still valid.
if ( PaymentServer : : verifyExpired ( rcp . paymentRequest . getDetails ( ) ) ) {
return PaymentRequestExpired ;
}
// Store PaymentRequests in wtx.vOrderForm in wallet.
2013-07-22 08:50:39 +02:00
std : : string key ( " PaymentRequest " ) ;
std : : string value ;
rcp . paymentRequest . SerializeToString ( & value ) ;
2013-08-30 20:04:48 +02:00
newTx - > vOrderForm . push_back ( make_pair ( key , value ) ) ;
2013-07-22 08:50:39 +02:00
}
2015-03-19 15:15:08 +01:00
else if ( ! rcp . message . isEmpty ( ) ) // Message from normal dash:URI (dash:XyZ...?message=example)
2014-12-09 02:17:57 +01:00
{
2014-01-21 23:39:29 +01:00
newTx - > vOrderForm . push_back ( make_pair ( " Message " , rcp . message . toStdString ( ) ) ) ;
2014-12-09 02:17:57 +01:00
}
2011-06-30 18:05:29 +02:00
}
2013-08-30 20:04:48 +02:00
CReserveKey * keyChange = transaction . getPossibleKeyChange ( ) ;
2014-12-09 02:17:57 +01:00
Backport Bitcoin PR#8085: p2p: Begin encapsulation (#1537)
* net: move CBanDB and CAddrDB out of net.h/cpp
This will eventually solve a circular dependency
* net: Create CConnman to encapsulate p2p connections
* net: Move socket binding into CConnman
* net: move OpenNetworkConnection into CConnman
* net: move ban and addrman functions into CConnman
* net: Add oneshot functions to CConnman
* net: move added node functions to CConnman
* net: Add most functions needed for vNodes to CConnman
* net: handle nodesignals in CConnman
* net: Pass CConnection to wallet rather than using the global
* net: Add rpc error for missing/disabled p2p functionality
* net: Pass CConnman around as needed
* gui: add NodeID to the peer table
* net: create generic functor accessors and move vNodes to CConnman
* net: move whitelist functions into CConnman
* net: move nLastNodeId to CConnman
* net: move nLocalHostNonce to CConnman
This behavior seems to have been quite racy and broken.
Move nLocalHostNonce into CNode, and check received nonces against all
non-fully-connected nodes. If there's a match, assume we've connected
to ourself.
* net: move messageHandlerCondition to CConnman
* net: move send/recv statistics to CConnman
* net: move SendBufferSize/ReceiveFloodSize to CConnman
* net: move nLocalServices/nRelevantServices to CConnman
These are in-turn passed to CNode at connection time. This allows us to offer
different services to different peers (or test the effects of doing so).
* net: move semOutbound and semMasternodeOutbound to CConnman
* net: SocketSendData returns written size
* net: move max/max-outbound to CConnman
* net: Pass best block known height into CConnman
CConnman then passes the current best height into CNode at creation time.
This way CConnman/CNode have no dependency on main for height, and the signals
only move in one direction.
This also helps to prevent identity leakage a tiny bit. Before this change, an
attacker could theoretically make 2 connections on different interfaces. They
would connect fully on one, and only establish the initial connection on the
other. Once they receive a new block, they would relay it to your first
connection, and immediately commence the version handshake on the second. Since
the new block height is reflected immediately, they could attempt to learn
whether the two connections were correlated.
This is, of course, incredibly unlikely to work due to the small timings
involved and receipt from other senders. But it doesn't hurt to lock-in
nBestHeight at the time of connection, rather than letting the remote choose
the time.
* net: pass CClientUIInterface into CConnman
* net: Drop StartNode/StopNode and use CConnman directly
* net: Introduce CConnection::Options to avoid passing so many params
* net: add nSendBufferMaxSize/nReceiveFloodSize to CConnection::Options
* net: move vNodesDisconnected into CConnman
* Made the ForEachNode* functions in src/net.cpp more pragmatic and self documenting
* Convert ForEachNode* functions to take a templated function argument rather than a std::function to eliminate std::function overhead
* net: move MAX_FEELER_CONNECTIONS into connman
2017-07-21 11:35:19 +02:00
if ( ! wallet - > CommitTransaction ( * newTx , * keyChange , g_connman . get ( ) , recipients [ 0 ] . fUseInstantSend ? NetMsgType : : TXLOCKREQUEST : NetMsgType : : TX ) )
2011-07-16 19:01:05 +02:00
return TransactionCommitFailed ;
2013-07-22 08:50:39 +02:00
2013-08-30 20:04:48 +02:00
CTransaction * t = ( CTransaction * ) newTx ;
2013-07-22 08:50:39 +02:00
CDataStream ssTx ( SER_NETWORK , PROTOCOL_VERSION ) ;
ssTx < < * t ;
2013-08-30 20:04:48 +02:00
transaction_array . append ( & ( ssTx [ 0 ] ) , ssTx . size ( ) ) ;
2011-06-30 18:05:29 +02:00
}
2013-07-22 08:50:39 +02:00
// Add addresses / update labels that we've sent to to the address book,
2013-08-30 20:04:48 +02:00
// and emit coinsSent signal for each recipient
2015-07-14 13:59:05 +02:00
Q_FOREACH ( const SendCoinsRecipient & rcp , transaction . getRecipients ( ) )
2011-06-30 18:05:29 +02:00
{
2013-10-24 16:02:39 +02:00
// Don't touch the address book when we have a payment request
if ( ! rcp . paymentRequest . IsInitialized ( ) )
2011-07-16 19:01:05 +02:00
{
2013-10-08 14:23:57 +02:00
std : : string strAddress = rcp . address . toStdString ( ) ;
CTxDestination dest = CBitcoinAddress ( strAddress ) . Get ( ) ;
std : : string strLabel = rcp . label . toStdString ( ) ;
2012-05-03 14:52:15 +02:00
{
2013-10-08 14:23:57 +02:00
LOCK ( wallet - > cs_wallet ) ;
std : : map < CTxDestination , CAddressBookData > : : iterator mi = wallet - > mapAddressBook . find ( dest ) ;
// Check if we have a new address or an updated label
if ( mi = = wallet - > mapAddressBook . end ( ) )
{
wallet - > SetAddressBook ( dest , strLabel , " send " ) ;
}
else if ( mi - > second . name ! = strLabel )
{
wallet - > SetAddressBook ( dest , strLabel , " " ) ; // "" means don't change purpose
}
2012-05-03 14:52:15 +02:00
}
2011-07-16 19:01:05 +02:00
}
2015-07-14 13:59:05 +02:00
Q_EMIT coinsSent ( wallet , rcp , transaction_array ) ;
2011-06-30 18:05:29 +02:00
}
2014-08-17 02:34:42 +02:00
checkBalanceChanged ( ) ; // update balance immediately, otherwise there could be a short noticeable delay until pollBalanceChanged hits
2011-07-30 19:28:41 +02:00
2013-08-30 20:04:48 +02:00
return SendCoinsReturn ( OK ) ;
2011-06-30 18:05:29 +02:00
}
OptionsModel * WalletModel : : getOptionsModel ( )
{
return optionsModel ;
}
AddressTableModel * WalletModel : : getAddressTableModel ( )
{
return addressTableModel ;
}
TransactionTableModel * WalletModel : : getTransactionTableModel ( )
{
return transactionTableModel ;
}
2011-07-02 13:45:59 +02:00
2013-11-05 18:03:05 +01:00
RecentRequestsTableModel * WalletModel : : getRecentRequestsTableModel ( )
{
return recentRequestsTableModel ;
}
2011-08-23 20:08:42 +02:00
WalletModel : : EncryptionStatus WalletModel : : getEncryptionStatus ( ) const
{
if ( ! wallet - > IsCrypted ( ) )
{
return Unencrypted ;
}
2016-09-11 11:02:54 +02:00
else if ( wallet - > IsLocked ( true ) )
2011-08-23 20:08:42 +02:00
{
return Locked ;
}
2016-09-11 11:02:54 +02:00
else if ( wallet - > IsLocked ( ) )
2014-12-09 02:17:57 +01:00
{
2016-09-11 11:02:54 +02:00
return UnlockedForMixingOnly ;
2014-12-09 02:17:57 +01:00
}
2011-08-23 20:08:42 +02:00
else
{
return Unlocked ;
}
}
2011-08-24 22:07:26 +02:00
2011-11-26 07:02:04 +01:00
bool WalletModel : : setWalletEncrypted ( bool encrypted , const SecureString & passphrase )
2011-08-24 22:07:26 +02:00
{
if ( encrypted )
{
// Encrypt
return wallet - > EncryptWallet ( passphrase ) ;
}
else
{
// Decrypt -- TODO; not supported yet
return false ;
}
}
2016-09-11 11:02:54 +02:00
bool WalletModel : : setWalletLocked ( bool locked , const SecureString & passPhrase , bool fMixing )
2011-08-24 22:07:26 +02:00
{
if ( locked )
{
// Lock
2016-09-11 11:02:54 +02:00
return wallet - > Lock ( fMixing ) ;
2011-08-24 22:07:26 +02:00
}
else
{
// Unlock
2016-09-11 11:02:54 +02:00
return wallet - > Unlock ( passPhrase , fMixing ) ;
2011-08-24 22:07:26 +02:00
}
}
2011-11-26 07:02:04 +01:00
bool WalletModel : : changePassphrase ( const SecureString & oldPass , const SecureString & newPass )
2011-08-24 22:07:26 +02:00
{
bool retval ;
{
2012-04-06 18:39:12 +02:00
LOCK ( wallet - > cs_wallet ) ;
2011-08-24 22:07:26 +02:00
wallet - > Lock ( ) ; // Make sure wallet is locked before attempting pass change
retval = wallet - > ChangeWalletPassphrase ( oldPass , newPass ) ;
}
return retval ;
}
2012-02-14 13:14:43 +01:00
bool WalletModel : : backupWallet ( const QString & filename )
{
2016-05-25 18:06:23 +02:00
return wallet - > BackupWallet ( filename . toLocal8Bit ( ) . data ( ) ) ;
2012-02-14 13:14:43 +01:00
}
2012-05-06 19:40:58 +02:00
// Handlers for core signals
static void NotifyKeyStoreStatusChanged ( WalletModel * walletmodel , CCryptoKeyStore * wallet )
{
2013-09-04 11:52:45 +02:00
qDebug ( ) < < " NotifyKeyStoreStatusChanged " ;
2012-05-06 19:40:58 +02:00
QMetaObject : : invokeMethod ( walletmodel , " updateStatus " , Qt : : QueuedConnection ) ;
}
2013-08-29 16:19:43 +02:00
static void NotifyAddressBookChanged ( WalletModel * walletmodel , CWallet * wallet ,
const CTxDestination & address , const std : : string & label , bool isMine ,
const std : : string & purpose , ChangeType status )
2012-05-06 19:40:58 +02:00
{
2013-09-04 11:52:45 +02:00
QString strAddress = QString : : fromStdString ( CBitcoinAddress ( address ) . ToString ( ) ) ;
QString strLabel = QString : : fromStdString ( label ) ;
QString strPurpose = QString : : fromStdString ( purpose ) ;
2015-01-08 11:44:25 +01:00
qDebug ( ) < < " NotifyAddressBookChanged: " + strAddress + " " + strLabel + " isMine= " + QString : : number ( isMine ) + " purpose= " + strPurpose + " status= " + QString : : number ( status ) ;
2012-05-06 19:40:58 +02:00
QMetaObject : : invokeMethod ( walletmodel , " updateAddressBook " , Qt : : QueuedConnection ,
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 ) ,
2013-09-04 11:52:45 +02:00
Q_ARG ( QString , strPurpose ) ,
2012-05-06 19:40:58 +02:00
Q_ARG ( int , status ) ) ;
}
static void NotifyTransactionChanged ( WalletModel * walletmodel , CWallet * wallet , const uint256 & hash , ChangeType status )
{
2016-02-14 05:51:06 +01:00
Q_UNUSED ( wallet ) ;
Q_UNUSED ( hash ) ;
Q_UNUSED ( status ) ;
QMetaObject : : invokeMethod ( walletmodel , " updateTransaction " , Qt : : QueuedConnection ) ;
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 )
{
// emits signal "showProgress"
QMetaObject : : invokeMethod ( walletmodel , " showProgress " , Qt : : QueuedConnection ,
Q_ARG ( QString , QString : : fromStdString ( title ) ) ,
Q_ARG ( int , nProgress ) ) ;
}
2014-07-26 21:05:11 +02:00
static void NotifyWatchonlyChanged ( WalletModel * walletmodel , bool fHaveWatchonly )
{
QMetaObject : : invokeMethod ( walletmodel , " updateWatchOnlyFlag " , Qt : : QueuedConnection ,
Q_ARG ( bool , fHaveWatchonly ) ) ;
}
2012-05-06 19:40:58 +02:00
void WalletModel : : subscribeToCoreSignals ( )
{
// Connect signals to wallet
wallet - > NotifyStatusChanged . connect ( boost : : bind ( & NotifyKeyStoreStatusChanged , this , _1 ) ) ;
2013-08-29 16:19:43 +02:00
wallet - > NotifyAddressBookChanged . connect ( boost : : bind ( NotifyAddressBookChanged , this , _1 , _2 , _3 , _4 , _5 , _6 ) ) ;
2012-05-06 19:40:58 +02:00
wallet - > NotifyTransactionChanged . connect ( boost : : bind ( NotifyTransactionChanged , this , _1 , _2 , _3 ) ) ;
2014-03-19 00:26:14 +01:00
wallet - > ShowProgress . connect ( boost : : bind ( ShowProgress , this , _1 , _2 ) ) ;
2014-07-26 21:05:11 +02:00
wallet - > NotifyWatchonlyChanged . connect ( boost : : bind ( NotifyWatchonlyChanged , this , _1 ) ) ;
2012-05-06 19:40:58 +02:00
}
void WalletModel : : unsubscribeFromCoreSignals ( )
{
// Disconnect signals from wallet
wallet - > NotifyStatusChanged . disconnect ( boost : : bind ( & NotifyKeyStoreStatusChanged , this , _1 ) ) ;
2013-08-29 16:19:43 +02:00
wallet - > NotifyAddressBookChanged . disconnect ( boost : : bind ( NotifyAddressBookChanged , this , _1 , _2 , _3 , _4 , _5 , _6 ) ) ;
2012-05-06 19:40:58 +02:00
wallet - > NotifyTransactionChanged . disconnect ( boost : : bind ( NotifyTransactionChanged , this , _1 , _2 , _3 ) ) ;
2014-03-19 00:26:14 +01:00
wallet - > ShowProgress . disconnect ( boost : : bind ( ShowProgress , this , _1 , _2 ) ) ;
2014-07-26 21:05:11 +02:00
wallet - > NotifyWatchonlyChanged . disconnect ( boost : : bind ( NotifyWatchonlyChanged , this , _1 ) ) ;
2012-05-06 19:40:58 +02:00
}
2011-08-24 22:07:26 +02:00
// WalletModel::UnlockContext implementation
2016-09-11 11:02:54 +02:00
WalletModel : : UnlockContext WalletModel : : requestUnlock ( bool fForMixingOnly )
2011-08-24 22:07:26 +02:00
{
2016-09-11 11:02:54 +02:00
EncryptionStatus encStatusOld = getEncryptionStatus ( ) ;
2014-12-09 02:17:57 +01:00
2016-09-11 11:02:54 +02:00
// Wallet was completely locked
bool was_locked = ( encStatusOld = = Locked ) ;
// Wallet was unlocked for mixing
bool was_mixing = ( encStatusOld = = UnlockedForMixingOnly ) ;
// Wallet was unlocked for mixing and now user requested to fully unlock it
bool fMixingToFullRequested = ! fForMixingOnly & & was_mixing ;
2014-12-09 02:17:57 +01:00
2016-09-11 11:02:54 +02:00
if ( was_locked | | fMixingToFullRequested ) {
2011-08-24 22:07:26 +02:00
// Request UI to unlock wallet
2016-09-11 11:02:54 +02:00
Q_EMIT requireUnlock ( fForMixingOnly ) ;
2011-08-24 22:07:26 +02:00
}
2016-09-11 11:02:54 +02:00
EncryptionStatus encStatusNew = getEncryptionStatus ( ) ;
// Wallet was locked, user requested to unlock it for mixing and failed to do so
bool fMixingUnlockFailed = fForMixingOnly & & ! ( encStatusNew = = UnlockedForMixingOnly ) ;
// Wallet was unlocked for mixing, user requested to fully unlock it and failed
bool fMixingToFullFailed = fMixingToFullRequested & & ! ( encStatusNew = = Unlocked ) ;
2016-03-15 22:15:18 +01:00
// If wallet is still locked, unlock failed or was cancelled, mark context as invalid
2016-09-11 11:02:54 +02:00
bool fInvalid = ( encStatusNew = = Locked ) | | fMixingUnlockFailed | | fMixingToFullFailed ;
// Wallet was not locked in any way or user tried to unlock it for mixing only and succeeded, keep it unlocked
bool fKeepUnlocked = ! was_locked | | ( fForMixingOnly & & ! fMixingUnlockFailed ) ;
2011-08-24 22:07:26 +02:00
2016-09-11 11:02:54 +02:00
return UnlockContext ( this , ! fInvalid , ! fKeepUnlocked , was_mixing ) ;
2011-08-24 22:07:26 +02:00
}
2016-09-11 11:02:54 +02:00
WalletModel : : UnlockContext : : UnlockContext ( WalletModel * wallet , bool valid , bool was_locked , bool was_mixing ) :
2011-08-24 22:07:26 +02:00
wallet ( wallet ) ,
valid ( valid ) ,
2016-09-11 11:02:54 +02:00
was_locked ( was_locked ) ,
was_mixing ( was_mixing )
2011-08-24 22:07:26 +02:00
{
}
WalletModel : : UnlockContext : : ~ UnlockContext ( )
{
2016-09-11 11:02:54 +02:00
if ( valid & & ( was_locked | | was_mixing ) )
2011-08-24 22:07:26 +02:00
{
2016-09-11 11:02:54 +02:00
wallet - > setWalletLocked ( true , " " , was_mixing ) ;
2011-08-24 22:07:26 +02:00
}
}
void WalletModel : : UnlockContext : : CopyFrom ( const UnlockContext & rhs )
{
// Transfer context; old object no longer relocks wallet
* this = rhs ;
2016-09-11 11:02:54 +02:00
rhs . was_locked = false ;
rhs . was_mixing = false ;
2011-08-24 22:07:26 +02:00
}
2013-08-12 17:03:03 +02:00
bool WalletModel : : getPubKey ( const CKeyID & address , CPubKey & vchPubKeyOut ) const
{
return wallet - > GetPubKey ( address , vchPubKeyOut ) ;
}
2015-06-10 10:04:08 +02:00
bool WalletModel : : havePrivKey ( const CKeyID & address ) const
{
return wallet - > HaveKey ( address ) ;
}
2013-08-12 17:03:03 +02:00
// returns a list of COutputs from COutPoints
void WalletModel : : getOutputs ( const std : : vector < COutPoint > & vOutpoints , std : : vector < COutput > & vOutputs )
{
2014-04-15 17:38:25 +02:00
LOCK2 ( cs_main , wallet - > cs_wallet ) ;
2013-08-12 17:03:03 +02:00
BOOST_FOREACH ( const COutPoint & outpoint , vOutpoints )
{
if ( ! wallet - > mapWallet . count ( outpoint . hash ) ) continue ;
2014-02-12 19:43:07 +01:00
int nDepth = wallet - > mapWallet [ outpoint . hash ] . GetDepthInMainChain ( ) ;
if ( nDepth < 0 ) continue ;
2017-09-09 09:04:02 +02:00
COutput out ( & wallet - > mapWallet [ outpoint . hash ] , outpoint . n , nDepth , true , true ) ;
2013-08-12 17:03:03 +02:00
vOutputs . push_back ( out ) ;
}
}
2014-02-15 22:38:28 +01:00
bool WalletModel : : isSpent ( const COutPoint & outpoint ) const
{
2014-04-15 17:38:25 +02:00
LOCK2 ( cs_main , wallet - > cs_wallet ) ;
2014-02-15 22:38:28 +01:00
return wallet - > IsSpent ( outpoint . hash , outpoint . n ) ;
}
2013-08-12 17:03:03 +02:00
// AvailableCoins + LockedCoins grouped by wallet address (put change in one group with wallet address)
void WalletModel : : listCoins ( std : : map < QString , std : : vector < COutput > > & mapCoins ) const
{
std : : vector < COutput > vCoins ;
wallet - > AvailableCoins ( vCoins ) ;
2014-04-15 17:38:25 +02:00
LOCK2 ( cs_main , wallet - > cs_wallet ) ; // ListLockedCoins, mapWallet
2013-08-12 17:03:03 +02:00
std : : vector < COutPoint > vLockedCoins ;
wallet - > ListLockedCoins ( vLockedCoins ) ;
// add locked coins
BOOST_FOREACH ( const COutPoint & outpoint , vLockedCoins )
{
if ( ! wallet - > mapWallet . count ( outpoint . hash ) ) continue ;
2014-02-12 19:43:07 +01:00
int nDepth = wallet - > mapWallet [ outpoint . hash ] . GetDepthInMainChain ( ) ;
if ( nDepth < 0 ) continue ;
2017-09-09 09:04:02 +02:00
COutput out ( & wallet - > mapWallet [ outpoint . hash ] , outpoint . n , nDepth , true , true ) ;
2014-07-26 21:05:11 +02:00
if ( outpoint . n < out . tx - > vout . size ( ) & & wallet - > IsMine ( out . tx - > vout [ outpoint . n ] ) = = ISMINE_SPENDABLE )
vCoins . push_back ( out ) ;
2013-08-12 17:03:03 +02:00
}
BOOST_FOREACH ( const COutput & out , vCoins )
{
COutput cout = out ;
while ( wallet - > IsChange ( cout . tx - > vout [ cout . i ] ) & & cout . tx - > vin . size ( ) > 0 & & wallet - > IsMine ( cout . tx - > vin [ 0 ] ) )
{
if ( ! wallet - > mapWallet . count ( cout . tx - > vin [ 0 ] . prevout . hash ) ) break ;
2017-09-09 09:04:02 +02:00
cout = COutput ( & wallet - > mapWallet [ cout . tx - > vin [ 0 ] . prevout . hash ] , cout . tx - > vin [ 0 ] . prevout . n , 0 , true , true ) ;
2013-08-12 17:03:03 +02:00
}
CTxDestination address ;
2013-12-10 15:27:53 +01:00
if ( ! out . fSpendable | | ! ExtractDestination ( cout . tx - > vout [ cout . i ] . scriptPubKey , address ) )
continue ;
2014-09-08 12:25:52 +02:00
mapCoins [ QString : : fromStdString ( CBitcoinAddress ( address ) . ToString ( ) ) ] . push_back ( out ) ;
2013-08-12 17:03:03 +02:00
}
}
bool WalletModel : : isLockedCoin ( uint256 hash , unsigned int n ) const
{
2014-04-15 17:38:25 +02:00
LOCK2 ( cs_main , wallet - > cs_wallet ) ;
2013-08-12 17:03:03 +02:00
return wallet - > IsLockedCoin ( hash , n ) ;
}
void WalletModel : : lockCoin ( COutPoint & output )
{
2014-04-15 17:38:25 +02:00
LOCK2 ( cs_main , wallet - > cs_wallet ) ;
2013-08-12 17:03:03 +02:00
wallet - > LockCoin ( output ) ;
}
void WalletModel : : unlockCoin ( COutPoint & output )
{
2014-04-15 17:38:25 +02:00
LOCK2 ( cs_main , wallet - > cs_wallet ) ;
2013-08-12 17:03:03 +02:00
wallet - > UnlockCoin ( output ) ;
}
void WalletModel : : listLockedCoins ( std : : vector < COutPoint > & vOutpts )
{
2014-04-15 17:38:25 +02:00
LOCK2 ( cs_main , wallet - > cs_wallet ) ;
2013-08-12 17:03:03 +02:00
wallet - > ListLockedCoins ( vOutpts ) ;
}
2014-01-14 05:05:43 +01:00
void WalletModel : : loadReceiveRequests ( std : : vector < std : : string > & vReceiveRequests )
{
LOCK ( wallet - > cs_wallet ) ;
BOOST_FOREACH ( const PAIRTYPE ( CTxDestination , CAddressBookData ) & item , wallet - > mapAddressBook )
BOOST_FOREACH ( const PAIRTYPE ( std : : string , std : : string ) & item2 , item . second . destdata )
if ( item2 . first . size ( ) > 2 & & item2 . first . substr ( 0 , 2 ) = = " rr " ) // receive request
vReceiveRequests . push_back ( item2 . second ) ;
}
bool WalletModel : : saveReceiveRequest ( const std : : string & sAddress , const int64_t nId , const std : : string & sRequest )
{
CTxDestination dest = CBitcoinAddress ( sAddress ) . Get ( ) ;
std : : stringstream ss ;
ss < < nId ;
std : : string key = " rr " + ss . str ( ) ; // "rr" prefix = "receive request" in destdata
LOCK ( wallet - > cs_wallet ) ;
if ( sRequest . empty ( ) )
return wallet - > EraseDestData ( dest , key ) ;
else
return wallet - > AddDestData ( dest , key , sRequest ) ;
}
2017-05-29 13:51:40 +02:00
2017-09-09 09:04:02 +02:00
bool WalletModel : : transactionCanBeAbandoned ( uint256 hash ) const
{
LOCK2 ( cs_main , wallet - > cs_wallet ) ;
const CWalletTx * wtx = wallet - > GetWalletTx ( hash ) ;
if ( ! wtx | | wtx - > isAbandoned ( ) | | wtx - > GetDepthInMainChain ( ) > 0 | | wtx - > InMempool ( ) )
return false ;
return true ;
}
bool WalletModel : : abandonTransaction ( uint256 hash ) const
{
LOCK2 ( cs_main , wallet - > cs_wallet ) ;
return wallet - > AbandonTransaction ( hash ) ;
}
2017-05-29 13:51:40 +02:00
bool WalletModel : : hdEnabled ( ) const
{
return wallet - > IsHDEnabled ( ) ;
}