2016-12-31 19:01:21 +01:00
// Copyright (c) 2012-2016 The Bitcoin Core developers
2014-12-13 05:09:33 +01:00
// Distributed under the MIT software license, see the accompanying
2014-03-18 10:11:00 +01:00
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
2018-01-07 23:56:53 +01:00
# include "txmempool.h"
2015-02-03 21:09:47 +01:00
# include "wallet/wallet.h"
2012-02-27 13:19:32 +01:00
2013-04-13 07:13:08 +02:00
# include <set>
# include <stdint.h>
# include <utility>
# include <vector>
2017-02-16 16:49:03 +01:00
# include "rpc/server.h"
# include "test/test_bitcoin.h"
# include "validation.h"
2016-04-18 14:54:57 +02:00
# include "wallet/test/wallet_test_fixture.h"
2015-03-09 15:04:12 +01:00
2013-04-13 07:13:08 +02:00
# include <boost/foreach.hpp>
# include <boost/test/unit_test.hpp>
2017-02-16 16:49:03 +01:00
# include <univalue.h>
extern UniValue importmulti ( const JSONRPCRequest & request ) ;
2017-05-16 17:34:28 +02:00
extern UniValue dumpwallet ( const JSONRPCRequest & request ) ;
extern UniValue importwallet ( const JSONRPCRequest & request ) ;
2013-04-13 07:13:08 +02:00
2012-02-27 13:19:32 +01:00
// how many times to run all the tests to have a chance to catch errors that only show up with particular random shuffles
# define RUN_TESTS 100
2012-04-07 19:55:03 +02:00
// some tests fail 1% of the time due to bad luck.
// we repeat those tests this many times and only complain if all iterations of the test fail
# define RANDOM_REPEATS 5
2012-02-27 13:19:32 +01:00
using namespace std ;
2017-01-10 23:46:07 +01:00
std : : vector < std : : unique_ptr < CWalletTx > > wtxn ;
2012-04-07 19:55:03 +02:00
typedef set < pair < const CWalletTx * , unsigned int > > CoinSet ;
2021-08-07 08:44:32 +02:00
extern CAmount nDustLimit ;
2021-06-27 00:37:10 +02:00
2016-04-18 14:54:57 +02:00
BOOST_FIXTURE_TEST_SUITE ( wallet_tests , WalletTestingSetup )
2012-02-27 13:19:32 +01:00
2016-06-07 11:22:21 +02:00
static const CWallet wallet ;
2012-02-27 13:19:32 +01:00
static vector < COutput > vCoins ;
2014-04-23 00:46:19 +02:00
static void add_coin ( const CAmount & nValue , int nAge = 6 * 24 , bool fIsFromMe = false , int nInput = 0 )
2012-02-27 13:19:32 +01:00
{
2013-04-25 00:27:00 +02:00
static int nextLockTime = 0 ;
2014-06-07 13:53:27 +02:00
CMutableTransaction tx ;
2013-04-25 00:27:00 +02:00
tx . nLockTime = nextLockTime + + ; // so all transactions get different hashes
tx . vout . resize ( nInput + 1 ) ;
tx . vout [ nInput ] . nValue = nValue ;
2014-06-07 13:53:27 +02:00
if ( fIsFromMe ) {
// IsFromMe() returns (GetDebit() > 0), and GetDebit() is 0 if vin.empty(),
// so stop vin being empty, and cache a non-zero Debit to fake out IsFromMe()
tx . vin . resize ( 1 ) ;
}
2017-01-10 23:46:07 +01:00
std : : unique_ptr < CWalletTx > wtx ( new CWalletTx ( & wallet , MakeTransactionRef ( std : : move ( tx ) ) ) ) ;
2012-02-27 13:19:32 +01:00
if ( fIsFromMe )
{
wtx - > fDebitCached = true ;
wtx - > nDebitCached = 1 ;
}
2017-01-10 23:46:07 +01:00
COutput output ( wtx . get ( ) , nInput , nAge , true , true ) ;
2012-02-27 13:19:32 +01:00
vCoins . push_back ( output ) ;
2017-01-10 23:46:07 +01:00
wtxn . emplace_back ( std : : move ( wtx ) ) ;
2012-02-27 13:19:32 +01:00
}
static void empty_wallet ( void )
{
vCoins . clear ( ) ;
2017-01-10 23:46:07 +01:00
wtxn . clear ( ) ;
2012-02-27 13:19:32 +01:00
}
2012-04-07 19:55:03 +02:00
static bool equal_sets ( CoinSet a , CoinSet b )
{
pair < CoinSet : : iterator , CoinSet : : iterator > ret = mismatch ( a . begin ( ) , a . end ( ) , b . begin ( ) ) ;
return ret . first = = a . end ( ) & & ret . second = = b . end ( ) ;
}
2012-02-27 13:19:32 +01:00
BOOST_AUTO_TEST_CASE ( coin_selection_tests )
{
2013-04-25 00:27:00 +02:00
CoinSet setCoinsRet , setCoinsRet2 ;
2014-04-23 00:46:19 +02:00
CAmount nValueRet ;
2012-02-27 13:19:32 +01:00
2014-01-23 20:18:26 +01:00
LOCK ( wallet . cs_wallet ) ;
2012-02-27 13:19:32 +01:00
// test multiple times to allow for differences in the shuffle order
for ( int i = 0 ; i < RUN_TESTS ; i + + )
{
empty_wallet ( ) ;
2020-07-20 18:14:13 +02:00
// with an empty wallet we can't even pay one coin
BOOST_CHECK ( ! wallet . SelectCoinsMinConf ( 1 * COIN , 1 , 6 , 0 , vCoins , setCoinsRet , nValueRet ) ) ;
2012-02-27 13:19:32 +01:00
2021-10-09 18:53:40 +02:00
add_coin ( 10 * COIN , 4 ) ; // add a new 10 coin output
2012-02-27 13:19:32 +01:00
2021-10-09 18:53:40 +02:00
// with only a new 1 coin output, we still can't find a mature 10 coin output
BOOST_CHECK ( ! wallet . SelectCoinsMinConf ( 10 * COIN , 1 , 6 , 0 , vCoins , setCoinsRet , nValueRet ) ) ;
2012-02-27 13:19:32 +01:00
2021-10-09 18:53:40 +02:00
// but we can find a new 10 coin output
BOOST_CHECK ( wallet . SelectCoinsMinConf ( 10 * COIN , 1 , 1 , 0 , vCoins , setCoinsRet , nValueRet ) ) ;
BOOST_CHECK_EQUAL ( nValueRet , 10 * COIN ) ;
2012-02-27 13:19:32 +01:00
2021-10-09 18:53:40 +02:00
add_coin ( 20 * COIN ) ; // add a mature 20 coin output
2012-02-27 13:19:32 +01:00
2021-10-09 18:53:40 +02:00
// we can't make 30 coins of mature outputs
BOOST_CHECK ( ! wallet . SelectCoinsMinConf ( 30 * COIN , 1 , 6 , 0 , vCoins , setCoinsRet , nValueRet ) ) ;
2012-02-27 13:19:32 +01:00
2021-10-09 18:53:40 +02:00
// we can make 30 coin of new outputs
BOOST_CHECK ( wallet . SelectCoinsMinConf ( 30 * COIN , 1 , 1 , 0 , vCoins , setCoinsRet , nValueRet ) ) ;
BOOST_CHECK_EQUAL ( nValueRet , 30 * COIN ) ;
2012-02-27 13:19:32 +01:00
2021-10-09 18:53:40 +02:00
add_coin ( 50 * COIN ) ; // add a mature 50 coin output,
add_coin ( 100 * COIN , 3 , true ) ; // a new 100 coin output sent from one of our own addresses
add_coin ( 200 * COIN ) ; // and a mature 200 coin output
2012-02-27 13:19:32 +01:00
2021-10-09 18:53:40 +02:00
// now we have new: 10+100=110 (of which 100 was self-sent), and mature: 20+50+200=270. total = 380
2012-02-27 13:19:32 +01:00
2021-10-09 18:53:40 +02:00
// we can't make 380 coins only if we disallow new output:
BOOST_CHECK ( ! wallet . SelectCoinsMinConf ( 380 * COIN , 1 , 6 , 0 , vCoins , setCoinsRet , nValueRet ) ) ;
// we can't even make 370 coins if we don't allow new output even if they're from us
BOOST_CHECK ( ! wallet . SelectCoinsMinConf ( 380 * COIN , 6 , 6 , 0 , vCoins , setCoinsRet , nValueRet ) ) ;
// but we can make 370 coins if we accept new output from ourself
BOOST_CHECK ( wallet . SelectCoinsMinConf ( 370 * COIN , 1 , 6 , 0 , vCoins , setCoinsRet , nValueRet ) ) ;
BOOST_CHECK_EQUAL ( nValueRet , 370 * COIN ) ;
// and we can make 380 coins if we accept all new output
BOOST_CHECK ( wallet . SelectCoinsMinConf ( 380 * COIN , 1 , 1 , 0 , vCoins , setCoinsRet , nValueRet ) ) ;
BOOST_CHECK_EQUAL ( nValueRet , 380 * COIN ) ;
2020-07-20 18:14:13 +02:00
2021-10-09 18:53:40 +02:00
// try making 340 coins from 10,20,50,100,200 - we can't do it exactly
BOOST_CHECK ( wallet . SelectCoinsMinConf ( 340 * COIN , 1 , 1 , 0 , vCoins , setCoinsRet , nValueRet ) ) ;
BOOST_CHECK_EQUAL ( nValueRet , 350 * COIN ) ; // but 350 coins is closest
BOOST_CHECK_EQUAL ( setCoinsRet . size ( ) , 3U ) ; // the best should be 200+100+50. it's incredibly unlikely the 10 or 20 got included (but possible)
2012-02-27 13:19:32 +01:00
2021-10-09 18:53:40 +02:00
// when we try making 70 coins, the smaller outputs (10,20,50) are enough. We should see just 20+50
BOOST_CHECK ( wallet . SelectCoinsMinConf ( 70 * COIN , 1 , 1 , 0 , vCoins , setCoinsRet , nValueRet ) ) ;
BOOST_CHECK_EQUAL ( nValueRet , 70 * COIN ) ;
2013-03-07 18:38:25 +01:00
BOOST_CHECK_EQUAL ( setCoinsRet . size ( ) , 2U ) ;
2012-02-27 13:19:32 +01:00
2021-10-09 18:53:40 +02:00
// when we try making 80 coins, the smaller outputs (10,20,50) are exactly enough.
BOOST_CHECK ( wallet . SelectCoinsMinConf ( 80 * COIN , 1 , 1 , 0 , vCoins , setCoinsRet , nValueRet ) ) ;
BOOST_CHECK ( nValueRet = = 80 * COIN ) ;
2013-03-07 18:38:25 +01:00
BOOST_CHECK_EQUAL ( setCoinsRet . size ( ) , 3U ) ;
2012-02-27 13:19:32 +01:00
2021-10-09 18:53:40 +02:00
// when we try making 90 coins, no subset of smaller outputs is enough, and we get the next bigger output (100)
BOOST_CHECK ( wallet . SelectCoinsMinConf ( 90 * COIN , 1 , 1 , 0 , vCoins , setCoinsRet , nValueRet ) ) ;
BOOST_CHECK_EQUAL ( nValueRet , 100 * COIN ) ;
2013-03-07 18:38:25 +01:00
BOOST_CHECK_EQUAL ( setCoinsRet . size ( ) , 1U ) ;
2012-02-27 13:19:32 +01:00
2012-06-30 11:05:28 +02:00
// now clear out the wallet and start again to test choosing between subsets of smaller coins and the next biggest coin
2012-02-27 13:19:32 +01:00
empty_wallet ( ) ;
2021-10-09 18:53:40 +02:00
add_coin ( 60 * COIN ) ;
add_coin ( 70 * COIN ) ;
add_coin ( 80 * COIN ) ;
add_coin ( 200 * COIN ) ;
add_coin ( 300 * COIN ) ; // now we have 60+70+80+200+300 = 710 coins total
2012-02-27 13:19:32 +01:00
2021-10-09 18:53:40 +02:00
// check that we have 710 and not 711
BOOST_CHECK ( wallet . SelectCoinsMinConf ( 710 * COIN , 1 , 1 , 0 , vCoins , setCoinsRet , nValueRet ) ) ;
BOOST_CHECK ( ! wallet . SelectCoinsMinConf ( 711 * COIN , 1 , 1 , 0 , vCoins , setCoinsRet , nValueRet ) ) ;
2012-02-27 13:19:32 +01:00
2021-10-09 18:53:40 +02:00
// now try making 160 coins. the best smaller outputs can do is 60+70+80 = 210; not as good at the next biggest output, 200
BOOST_CHECK ( wallet . SelectCoinsMinConf ( 160 * COIN , 1 , 1 , 0 , vCoins , setCoinsRet , nValueRet ) ) ;
BOOST_CHECK_EQUAL ( nValueRet , 200 * COIN ) ; // we should get 200 in one output
2013-03-07 18:38:25 +01:00
BOOST_CHECK_EQUAL ( setCoinsRet . size ( ) , 1U ) ;
2012-02-27 13:19:32 +01:00
2021-10-09 18:53:40 +02:00
add_coin ( 50 * COIN ) ; // now we have 50+60+70+80+200+300 = 750 coins total
2012-02-27 13:19:32 +01:00
2021-10-09 18:53:40 +02:00
// now if we try making 160 coins again, the smaller outputs can make 50+60+70 = 18 coins, better than the next biggest output, 200
BOOST_CHECK ( wallet . SelectCoinsMinConf ( 160 * COIN , 1 , 1 , 0 , vCoins , setCoinsRet , nValueRet ) ) ;
BOOST_CHECK_EQUAL ( nValueRet , 180 * COIN ) ; // we should get 180 in 3 outputs
2013-03-07 18:38:25 +01:00
BOOST_CHECK_EQUAL ( setCoinsRet . size ( ) , 3U ) ;
2012-02-27 13:19:32 +01:00
2021-10-09 18:53:40 +02:00
add_coin ( 180 * COIN ) ; // now we have 50+60+70+80+180+200+300
2012-02-27 13:19:32 +01:00
2021-10-09 18:53:40 +02:00
// and now if we try making 16 coins again, the smaller outputs can make 50+60+70 = 180 coins, the same as the next biggest output, 180
BOOST_CHECK ( wallet . SelectCoinsMinConf ( 160 * COIN , 1 , 1 , 0 , vCoins , setCoinsRet , nValueRet ) ) ;
BOOST_CHECK_EQUAL ( nValueRet , 180 * COIN ) ; // we should get 180 in 1 output
2020-07-20 18:14:13 +02:00
BOOST_CHECK_EQUAL ( setCoinsRet . size ( ) , 1U ) ; // because in the event of a tie, the biggest output wins
2012-02-27 13:19:32 +01:00
2021-10-09 18:53:40 +02:00
// now try making 110 coins. we should get 50+60
BOOST_CHECK ( wallet . SelectCoinsMinConf ( 110 * COIN , 1 , 1 , 0 , vCoins , setCoinsRet , nValueRet ) ) ;
BOOST_CHECK_EQUAL ( nValueRet , 110 * COIN ) ;
2013-03-07 18:38:25 +01:00
BOOST_CHECK_EQUAL ( setCoinsRet . size ( ) , 2U ) ;
2012-02-27 13:19:32 +01:00
2020-07-20 18:14:13 +02:00
// check that the smallest bigger output is used
2021-10-09 18:53:40 +02:00
add_coin ( 1000 * COIN ) ;
add_coin ( 2000 * COIN ) ;
add_coin ( 3000 * COIN ) ;
add_coin ( 4000 * COIN ) ; // now we have 50+60+70+80+180+200+300+1000+2000+3000+4000 = 10940 coins
BOOST_CHECK ( wallet . SelectCoinsMinConf ( 950 * COIN , 1 , 1 , 0 , vCoins , setCoinsRet , nValueRet ) ) ;
BOOST_CHECK_EQUAL ( nValueRet , 1000 * COIN ) ; // we should get 1000 coins in 1 output
2013-03-07 18:38:25 +01:00
BOOST_CHECK_EQUAL ( setCoinsRet . size ( ) , 1U ) ;
2012-02-27 13:19:32 +01:00
2021-10-09 18:53:40 +02:00
BOOST_CHECK ( wallet . SelectCoinsMinConf ( 1950 * COIN , 1 , 1 , 0 , vCoins , setCoinsRet , nValueRet ) ) ;
BOOST_CHECK_EQUAL ( nValueRet , 2000 * COIN ) ; // we should get 2000 coins in 1 output
2013-03-07 18:38:25 +01:00
BOOST_CHECK_EQUAL ( setCoinsRet . size ( ) , 1U ) ;
2012-02-27 13:19:32 +01:00
2020-07-20 18:14:13 +02:00
// empty the wallet and start again, now with fractions of a coin, to test small change avoidance
2015-09-13 23:23:59 +02:00
2012-02-27 13:19:32 +01:00
empty_wallet ( ) ;
2016-06-07 11:22:21 +02:00
add_coin ( MIN_CHANGE * 1 / 10 ) ;
add_coin ( MIN_CHANGE * 2 / 10 ) ;
add_coin ( MIN_CHANGE * 3 / 10 ) ;
add_coin ( MIN_CHANGE * 4 / 10 ) ;
add_coin ( MIN_CHANGE * 5 / 10 ) ;
2012-02-27 13:19:32 +01:00
2015-09-13 23:23:59 +02:00
// try making 1 * MIN_CHANGE from the 1.5 * MIN_CHANGE
// we'll get change smaller than MIN_CHANGE whatever happens, so can expect MIN_CHANGE exactly
2016-12-02 21:29:20 +01:00
BOOST_CHECK ( wallet . SelectCoinsMinConf ( MIN_CHANGE , 1 , 1 , 0 , vCoins , setCoinsRet , nValueRet ) ) ;
2015-09-13 23:23:59 +02:00
BOOST_CHECK_EQUAL ( nValueRet , MIN_CHANGE ) ;
2012-02-27 13:19:32 +01:00
2020-07-20 18:14:13 +02:00
// but if we add a bigger output, small change is avoided
2015-09-13 23:23:59 +02:00
add_coin ( 1111 * MIN_CHANGE ) ;
2012-02-27 13:19:32 +01:00
2015-09-13 23:23:59 +02:00
// try making 1 from 0.1 + 0.2 + 0.3 + 0.4 + 0.5 + 1111 = 1112.5
2016-12-02 21:29:20 +01:00
BOOST_CHECK ( wallet . SelectCoinsMinConf ( 1 * MIN_CHANGE , 1 , 1 , 0 , vCoins , setCoinsRet , nValueRet ) ) ;
2015-09-13 23:23:59 +02:00
BOOST_CHECK_EQUAL ( nValueRet , 1 * MIN_CHANGE ) ; // we should get the exact amount
2012-02-27 13:19:32 +01:00
2020-07-20 18:14:13 +02:00
// if we add more small output:
2016-06-07 11:22:21 +02:00
add_coin ( MIN_CHANGE * 6 / 10 ) ;
add_coin ( MIN_CHANGE * 7 / 10 ) ;
2015-09-13 23:23:59 +02:00
// and try again to make 1.0 * MIN_CHANGE
2016-12-02 21:29:20 +01:00
BOOST_CHECK ( wallet . SelectCoinsMinConf ( 1 * MIN_CHANGE , 1 , 1 , 0 , vCoins , setCoinsRet , nValueRet ) ) ;
2015-09-13 23:23:59 +02:00
BOOST_CHECK_EQUAL ( nValueRet , 1 * MIN_CHANGE ) ; // we should get the exact amount
2012-02-27 13:19:32 +01:00
// run the 'mtgox' test (see http://blockexplorer.com/tx/29a3efd3ef04f9153d47a990bd7b048a4b2d213daaa5fb8ed670fb85f13bdbcf)
2020-07-20 18:14:13 +02:00
// they tried to consolidate 10 50k outputs into one 500k output, and ended up with 50k in change
2012-02-27 13:19:32 +01:00
empty_wallet ( ) ;
2016-06-11 16:35:19 +02:00
for ( int j = 0 ; j < 20 ; j + + )
2012-02-27 13:19:32 +01:00
add_coin ( 50000 * COIN ) ;
2016-12-02 21:29:20 +01:00
BOOST_CHECK ( wallet . SelectCoinsMinConf ( 500000 * COIN , 1 , 1 , 0 , vCoins , setCoinsRet , nValueRet ) ) ;
2012-04-13 01:22:15 +02:00
BOOST_CHECK_EQUAL ( nValueRet , 500000 * COIN ) ; // we should get the exact amount
2020-07-20 18:14:13 +02:00
BOOST_CHECK_EQUAL ( setCoinsRet . size ( ) , 10U ) ; // in ten outputs
2012-02-27 13:19:32 +01:00
2015-09-13 23:23:59 +02:00
// if there's not enough in the smaller coins to make at least 1 * MIN_CHANGE change (0.5+0.6+0.7 < 1.0+1.0),
2012-02-27 13:19:32 +01:00
// we need to try finding an exact subset anyway
2020-07-20 18:14:13 +02:00
// sometimes it will fail, and so we use the next biggest output:
2012-02-27 13:19:32 +01:00
empty_wallet ( ) ;
2016-06-07 11:22:21 +02:00
add_coin ( MIN_CHANGE * 5 / 10 ) ;
add_coin ( MIN_CHANGE * 6 / 10 ) ;
add_coin ( MIN_CHANGE * 7 / 10 ) ;
2015-09-13 23:23:59 +02:00
add_coin ( 1111 * MIN_CHANGE ) ;
2016-12-02 21:29:20 +01:00
BOOST_CHECK ( wallet . SelectCoinsMinConf ( 1 * MIN_CHANGE , 1 , 1 , 0 , vCoins , setCoinsRet , nValueRet ) ) ;
2020-07-20 18:14:13 +02:00
BOOST_CHECK_EQUAL ( nValueRet , 1111 * MIN_CHANGE ) ; // we get the bigger output
2013-03-07 18:38:25 +01:00
BOOST_CHECK_EQUAL ( setCoinsRet . size ( ) , 1U ) ;
2012-02-27 13:19:32 +01:00
// but sometimes it's possible, and we use an exact subset (0.4 + 0.6 = 1.0)
empty_wallet ( ) ;
2016-06-07 11:22:21 +02:00
add_coin ( MIN_CHANGE * 4 / 10 ) ;
add_coin ( MIN_CHANGE * 6 / 10 ) ;
add_coin ( MIN_CHANGE * 8 / 10 ) ;
2015-09-13 23:23:59 +02:00
add_coin ( 1111 * MIN_CHANGE ) ;
2016-12-02 21:29:20 +01:00
BOOST_CHECK ( wallet . SelectCoinsMinConf ( MIN_CHANGE , 1 , 1 , 0 , vCoins , setCoinsRet , nValueRet ) ) ;
2015-09-13 23:23:59 +02:00
BOOST_CHECK_EQUAL ( nValueRet , MIN_CHANGE ) ; // we should get the exact amount
2020-07-20 18:14:13 +02:00
BOOST_CHECK_EQUAL ( setCoinsRet . size ( ) , 2U ) ; // in two outputs 0.4+0.6
2012-04-07 19:55:03 +02:00
2015-09-13 23:23:59 +02:00
// test avoiding small change
2012-04-07 19:55:03 +02:00
empty_wallet ( ) ;
2016-06-07 11:22:21 +02:00
add_coin ( MIN_CHANGE * 5 / 100 ) ;
add_coin ( MIN_CHANGE * 1 ) ;
add_coin ( MIN_CHANGE * 100 ) ;
2012-04-07 19:55:03 +02:00
2020-07-20 18:14:13 +02:00
// trying to make 100.01 from these three outputs
2016-12-02 21:29:20 +01:00
BOOST_CHECK ( wallet . SelectCoinsMinConf ( MIN_CHANGE * 10001 / 100 , 1 , 1 , 0 , vCoins , setCoinsRet , nValueRet ) ) ;
2020-07-20 18:14:13 +02:00
BOOST_CHECK_EQUAL ( nValueRet , MIN_CHANGE * 10105 / 100 ) ; // we should get all outputs
2013-03-07 18:38:25 +01:00
BOOST_CHECK_EQUAL ( setCoinsRet . size ( ) , 3U ) ;
2012-04-07 19:55:03 +02:00
2020-07-20 18:14:13 +02:00
// but if we try to make 99.9, we should take the bigger of the two small outputs to avoid small change
2016-12-02 21:29:20 +01:00
BOOST_CHECK ( wallet . SelectCoinsMinConf ( MIN_CHANGE * 9990 / 100 , 1 , 1 , 0 , vCoins , setCoinsRet , nValueRet ) ) ;
2015-09-13 23:23:59 +02:00
BOOST_CHECK_EQUAL ( nValueRet , 101 * MIN_CHANGE ) ;
2013-03-07 18:38:25 +01:00
BOOST_CHECK_EQUAL ( setCoinsRet . size ( ) , 2U ) ;
2012-04-07 19:55:03 +02:00
2015-09-13 23:23:59 +02:00
// test with many inputs
2020-07-20 18:14:13 +02:00
for ( CAmount amt = 15 * CENT ; amt < 10000 * COIN ; amt * = 10 ) {
2015-09-13 23:23:59 +02:00
empty_wallet ( ) ;
2016-11-14 07:38:07 +01:00
// Create 676 inputs (= (old MAX_STANDARD_TX_SIZE == 100000) / 148 bytes per input)
2015-09-13 23:23:59 +02:00
for ( uint16_t j = 0 ; j < 676 ; j + + )
add_coin ( amt ) ;
2020-07-20 18:14:13 +02:00
BOOST_CHECK ( wallet . SelectCoinsMinConf ( 20 * CENT , 1 , 1 , 0 , vCoins , setCoinsRet , nValueRet ) ) ;
if ( amt - 20 * CENT < MIN_CHANGE ) {
2015-09-13 23:23:59 +02:00
// needs more than one input:
2020-07-20 18:14:13 +02:00
uint16_t returnSize = std : : ceil ( ( 20.0 * CENT + MIN_CHANGE ) / amt ) ;
2015-09-13 23:23:59 +02:00
CAmount returnValue = amt * returnSize ;
BOOST_CHECK_EQUAL ( nValueRet , returnValue ) ;
BOOST_CHECK_EQUAL ( setCoinsRet . size ( ) , returnSize ) ;
} else {
// one input is sufficient:
BOOST_CHECK_EQUAL ( nValueRet , amt ) ;
BOOST_CHECK_EQUAL ( setCoinsRet . size ( ) , 1U ) ;
}
}
2012-04-07 19:55:03 +02:00
// test randomness
{
empty_wallet ( ) ;
for ( int i2 = 0 ; i2 < 100 ; i2 + + )
add_coin ( COIN ) ;
2020-07-20 18:14:13 +02:00
// picking 50 from 100 outputs doesn't depend on the shuffle,
2012-04-07 19:55:03 +02:00
// but does depend on randomness in the stochastic approximation code
2016-12-02 21:29:20 +01:00
BOOST_CHECK ( wallet . SelectCoinsMinConf ( 50 * COIN , 1 , 6 , 0 , vCoins , setCoinsRet , nValueRet ) ) ;
BOOST_CHECK ( wallet . SelectCoinsMinConf ( 50 * COIN , 1 , 6 , 0 , vCoins , setCoinsRet2 , nValueRet ) ) ;
2012-04-07 19:55:03 +02:00
BOOST_CHECK ( ! equal_sets ( setCoinsRet , setCoinsRet2 ) ) ;
int fails = 0 ;
2016-06-11 16:35:19 +02:00
for ( int j = 0 ; j < RANDOM_REPEATS ; j + + )
2012-04-07 19:55:03 +02:00
{
2020-07-20 18:14:13 +02:00
// selecting 1 from 100 identical outputs depends on the shuffle; this test will fail 1% of the time
2012-04-07 19:55:03 +02:00
// run the test RANDOM_REPEATS times and only complain if all of them fail
2016-12-02 21:29:20 +01:00
BOOST_CHECK ( wallet . SelectCoinsMinConf ( COIN , 1 , 6 , 0 , vCoins , setCoinsRet , nValueRet ) ) ;
BOOST_CHECK ( wallet . SelectCoinsMinConf ( COIN , 1 , 6 , 0 , vCoins , setCoinsRet2 , nValueRet ) ) ;
2012-04-07 19:55:03 +02:00
if ( equal_sets ( setCoinsRet , setCoinsRet2 ) )
fails + + ;
}
BOOST_CHECK_NE ( fails , RANDOM_REPEATS ) ;
2020-07-20 18:14:13 +02:00
// add 75 coins in small change. not enough to make 90 coins,
// then try making 90 coins. there are multiple competing "smallest bigger" outputs,
2012-04-07 19:55:03 +02:00
// one of which should be picked at random
2020-07-20 18:14:13 +02:00
add_coin ( 5 * COIN ) ;
add_coin ( 10 * COIN ) ;
add_coin ( 15 * COIN ) ;
add_coin ( 20 * COIN ) ;
add_coin ( 25 * COIN ) ;
2012-04-07 19:55:03 +02:00
fails = 0 ;
2016-06-11 16:35:19 +02:00
for ( int j = 0 ; j < RANDOM_REPEATS ; j + + )
2012-04-07 19:55:03 +02:00
{
2020-07-20 18:14:13 +02:00
// selecting 1 from 100 identical outputs depends on the shuffle; this test will fail 1% of the time
2012-04-07 19:55:03 +02:00
// run the test RANDOM_REPEATS times and only complain if all of them fail
2020-07-20 18:14:13 +02:00
BOOST_CHECK ( wallet . SelectCoinsMinConf ( 90 * COIN , 1 , 6 , 0 , vCoins , setCoinsRet , nValueRet ) ) ;
BOOST_CHECK ( wallet . SelectCoinsMinConf ( 90 * COIN , 1 , 6 , 0 , vCoins , setCoinsRet2 , nValueRet ) ) ;
2012-04-07 19:55:03 +02:00
if ( equal_sets ( setCoinsRet , setCoinsRet2 ) )
fails + + ;
}
BOOST_CHECK_NE ( fails , RANDOM_REPEATS ) ;
}
2012-02-27 13:19:32 +01:00
}
2013-09-19 00:01:36 +02:00
empty_wallet ( ) ;
2012-02-27 13:19:32 +01:00
}
2016-01-05 20:35:17 +01:00
BOOST_AUTO_TEST_CASE ( ApproximateBestSubset )
2015-12-07 00:15:36 +01:00
{
CoinSet setCoinsRet ;
CAmount nValueRet ;
LOCK ( wallet . cs_wallet ) ;
empty_wallet ( ) ;
2016-01-05 20:35:17 +01:00
// Test vValue sort order
2016-01-05 00:38:12 +01:00
for ( int i = 0 ; i < 1000 ; i + + )
add_coin ( 1000 * COIN ) ;
add_coin ( 3 * COIN ) ;
2016-12-02 21:29:20 +01:00
BOOST_CHECK ( wallet . SelectCoinsMinConf ( 1003 * COIN , 1 , 6 , 0 , vCoins , setCoinsRet , nValueRet ) ) ;
2016-01-05 00:38:12 +01:00
BOOST_CHECK_EQUAL ( nValueRet , 1003 * COIN ) ;
BOOST_CHECK_EQUAL ( setCoinsRet . size ( ) , 2U ) ;
2017-01-10 23:46:07 +01:00
empty_wallet ( ) ;
2015-12-07 00:15:36 +01:00
}
2017-04-18 17:52:01 +02:00
BOOST_FIXTURE_TEST_CASE ( rescan , TestChain240Setup )
2017-02-16 16:49:03 +01:00
{
LOCK ( cs_main ) ;
// Cap last block file size, and mine new block in a new block file.
CBlockIndex * oldTip = chainActive . Tip ( ) ;
GetBlockFileInfo ( oldTip - > GetBlockPos ( ) . nFile ) - > nSize = MAX_BLOCKFILE_SIZE ;
CreateAndProcessBlock ( { } , GetScriptForRawPubKey ( coinbaseKey . GetPubKey ( ) ) ) ;
CBlockIndex * newTip = chainActive . Tip ( ) ;
// Verify ScanForWalletTransactions picks up transactions in both the old
// and new block files.
{
CWallet wallet ;
LOCK ( wallet . cs_wallet ) ;
wallet . AddKeyPubKey ( coinbaseKey , coinbaseKey . GetPubKey ( ) ) ;
BOOST_CHECK_EQUAL ( oldTip , wallet . ScanForWalletTransactions ( oldTip ) ) ;
2017-04-18 17:52:01 +02:00
BOOST_CHECK ( wallet . GetImmatureBalance ( ) < ( 240000000 * COIN ) ) ;
2017-02-16 16:49:03 +01:00
}
// Prune the older block file.
PruneOneBlockFile ( oldTip - > GetBlockPos ( ) . nFile ) ;
UnlinkPrunedFiles ( { oldTip - > GetBlockPos ( ) . nFile } ) ;
// Verify ScanForWalletTransactions only picks transactions in the new block
// file.
{
CWallet wallet ;
LOCK ( wallet . cs_wallet ) ;
wallet . AddKeyPubKey ( coinbaseKey , coinbaseKey . GetPubKey ( ) ) ;
BOOST_CHECK_EQUAL ( newTip , wallet . ScanForWalletTransactions ( oldTip ) ) ;
2017-04-18 17:52:01 +02:00
BOOST_CHECK ( wallet . GetImmatureBalance ( ) < ( 120000000 * COIN ) ) ;
2017-02-16 16:49:03 +01:00
}
2017-02-22 20:11:44 +01:00
// Verify importmulti RPC returns failure for a key whose creation time is
// before the missing block, and success for a key whose creation time is
// after.
2017-02-16 16:49:03 +01:00
{
CWallet wallet ;
2017-02-27 13:15:39 +01:00
CWallet * backup = : : pwalletMain ;
2017-02-16 16:49:03 +01:00
: : pwalletMain = & wallet ;
2017-02-22 20:11:44 +01:00
UniValue keys ;
keys . setArray ( ) ;
2017-02-16 16:49:03 +01:00
UniValue key ;
key . setObject ( ) ;
key . pushKV ( " scriptPubKey " , HexStr ( GetScriptForRawPubKey ( coinbaseKey . GetPubKey ( ) ) ) ) ;
key . pushKV ( " timestamp " , 0 ) ;
key . pushKV ( " internal " , UniValue ( true ) ) ;
2017-02-22 20:11:44 +01:00
keys . push_back ( key ) ;
key . clear ( ) ;
key . setObject ( ) ;
CKey futureKey ;
futureKey . MakeNewKey ( true ) ;
key . pushKV ( " scriptPubKey " , HexStr ( GetScriptForRawPubKey ( futureKey . GetPubKey ( ) ) ) ) ;
key . pushKV ( " timestamp " , newTip - > GetBlockTimeMax ( ) + 7200 ) ;
key . pushKV ( " internal " , UniValue ( true ) ) ;
2017-02-16 16:49:03 +01:00
keys . push_back ( key ) ;
JSONRPCRequest request ;
request . params . setArray ( ) ;
request . params . push_back ( keys ) ;
UniValue response = importmulti ( request ) ;
2017-02-22 20:11:44 +01:00
BOOST_CHECK_EQUAL ( response . write ( ) , strprintf ( " [{ \" success \" :false, \" error \" :{ \" code \" :-1, \" message \" : \" Failed to rescan before time %d, transactions may be missing. \" }},{ \" success \" :true}] " , newTip - > GetBlockTimeMax ( ) ) ) ;
2017-02-27 13:15:39 +01:00
: : pwalletMain = backup ;
2017-02-16 16:49:03 +01:00
}
}
2017-05-16 17:34:28 +02:00
// Verify importwallet RPC starts rescan at earliest block with timestamp
// greater or equal than key birthday. Previously there was a bug where
// importwallet RPC would start the scan at the latest block with timestamp less
// than or equal to key birthday.
2018-01-07 23:56:53 +01:00
BOOST_FIXTURE_TEST_CASE ( importwallet_rescan , TestChain240Setup )
2017-05-16 17:34:28 +02:00
{
CWallet * pwalletMainBackup = : : pwalletMain ;
LOCK ( cs_main ) ;
// Create two blocks with same timestamp to verify that importwallet rescan
// will pick up both blocks, not just the first.
const int64_t BLOCK_TIME = chainActive . Tip ( ) - > GetBlockTimeMax ( ) + 5 ;
SetMockTime ( BLOCK_TIME ) ;
coinbaseTxns . emplace_back ( * CreateAndProcessBlock ( { } , GetScriptForRawPubKey ( coinbaseKey . GetPubKey ( ) ) ) . vtx [ 0 ] ) ;
coinbaseTxns . emplace_back ( * CreateAndProcessBlock ( { } , GetScriptForRawPubKey ( coinbaseKey . GetPubKey ( ) ) ) . vtx [ 0 ] ) ;
// Set key birthday to block time increased by the timestamp window, so
// rescan will start at the block time.
const int64_t KEY_TIME = BLOCK_TIME + 7200 ;
SetMockTime ( KEY_TIME ) ;
coinbaseTxns . emplace_back ( * CreateAndProcessBlock ( { } , GetScriptForRawPubKey ( coinbaseKey . GetPubKey ( ) ) ) . vtx [ 0 ] ) ;
// Import key into wallet and call dumpwallet to create backup file.
{
CWallet wallet ;
LOCK ( wallet . cs_wallet ) ;
wallet . mapKeyMetadata [ coinbaseKey . GetPubKey ( ) . GetID ( ) ] . nCreateTime = KEY_TIME ;
wallet . AddKeyPubKey ( coinbaseKey , coinbaseKey . GetPubKey ( ) ) ;
JSONRPCRequest request ;
request . params . setArray ( ) ;
request . params . push_back ( " wallet.backup " ) ;
: : pwalletMain = & wallet ;
: : dumpwallet ( request ) ;
}
// Call importwallet RPC and verify all blocks with timestamps >= BLOCK_TIME
// were scanned, and no prior blocks were scanned.
{
CWallet wallet ;
JSONRPCRequest request ;
request . params . setArray ( ) ;
request . params . push_back ( " wallet.backup " ) ;
: : pwalletMain = & wallet ;
: : importwallet ( request ) ;
BOOST_CHECK_EQUAL ( wallet . mapWallet . size ( ) , 3 ) ;
2018-01-11 22:05:58 +01:00
BOOST_CHECK_EQUAL ( coinbaseTxns . size ( ) , 243 ) ;
2017-05-16 17:34:28 +02:00
for ( size_t i = 0 ; i < coinbaseTxns . size ( ) ; + + i ) {
bool found = wallet . GetWalletTx ( coinbaseTxns [ i ] . GetHash ( ) ) ;
2018-01-11 22:05:58 +01:00
bool expected = i > = 240 ;
2017-05-16 17:34:28 +02:00
BOOST_CHECK_EQUAL ( found , expected ) ;
}
}
SetMockTime ( 0 ) ;
: : pwalletMain = pwalletMainBackup ;
}
2018-01-07 23:56:53 +01:00
BOOST_AUTO_TEST_CASE ( GetMinimumFee_test )
{
uint64_t value = 1000 * COIN ; // 1,000 DOGE
CMutableTransaction tx ;
CTxMemPool pool ( payTxFee ) ;
CTxOut txout1 ( value , ( CScript ) vector < unsigned char > ( 24 , 0 ) ) ;
tx . vout . push_back ( txout1 ) ;
2021-08-15 17:50:18 +02:00
int64_t nMinTxFee = COIN / 100 ;
2018-01-07 23:56:53 +01:00
2021-08-15 17:50:18 +02:00
BOOST_CHECK_EQUAL ( CWallet : : GetMinimumFee ( tx , 250 , 0 , pool ) , nMinTxFee * 0.25 ) ;
BOOST_CHECK_EQUAL ( CWallet : : GetMinimumFee ( tx , 1000 , 0 , pool ) , nMinTxFee * 1.0 ) ;
BOOST_CHECK_EQUAL ( CWallet : : GetMinimumFee ( tx , 1999 , 0 , pool ) , nMinTxFee * 1.999 ) ;
2018-01-07 23:56:53 +01:00
}
BOOST_AUTO_TEST_CASE ( GetMinimumFee_dust_test )
{
// Derived from main net TX 3d6ec3ae2aca3ae0a6c65074fd8ee888cd7ed262f2cbaa25d33861989324a14e
CMutableTransaction tx ;
CTxMemPool pool ( payTxFee ) ;
CTxOut txout1 ( 139496846 , ( CScript ) vector < unsigned char > ( 24 , 0 ) ) ; // Regular output
2021-08-15 17:50:18 +02:00
CTxOut txout2 ( 154996 , ( CScript ) vector < unsigned char > ( 24 , 0 ) ) ; // Dust output
2018-01-07 23:56:53 +01:00
tx . vout . push_back ( txout1 ) ;
tx . vout . push_back ( txout2 ) ;
2021-08-15 17:50:18 +02:00
CAmount nMinTxFee = COIN / 100 ;
2018-01-07 23:56:53 +01:00
// Confirm dust penalty fees are added on
2021-08-15 17:50:18 +02:00
CAmount nDustPenalty = COIN / 100 ;
2018-01-07 23:56:53 +01:00
2021-08-15 17:50:18 +02:00
BOOST_CHECK_EQUAL ( CWallet : : GetMinimumFee ( tx , 963 , 0 , pool ) , nDustPenalty + ( nMinTxFee * 0.963 ) ) ;
BOOST_CHECK_EQUAL ( CWallet : : GetMinimumFee ( tx , 1000 , 0 , pool ) , nDustPenalty + ( nMinTxFee * 1.000 ) ) ;
BOOST_CHECK_EQUAL ( CWallet : : GetMinimumFee ( tx , 1999 , 0 , pool ) , nDustPenalty + ( nMinTxFee * 1.999 ) ) ;
2021-06-27 00:37:10 +02:00
2021-10-09 00:17:36 +02:00
// change the discard threshold
2021-06-27 00:37:10 +02:00
2021-10-09 00:17:36 +02:00
CWallet : : discardThreshold = COIN / 1000 ;
2021-06-27 00:37:10 +02:00
// Confirm dust penalty fees are not added
2021-08-15 17:50:18 +02:00
BOOST_CHECK_EQUAL ( CWallet : : GetMinimumFee ( tx , 963 , 0 , pool ) , nMinTxFee * 0.963 ) ;
BOOST_CHECK_EQUAL ( CWallet : : GetMinimumFee ( tx , 1000 , 0 , pool ) , nMinTxFee * 1.000 ) ;
BOOST_CHECK_EQUAL ( CWallet : : GetMinimumFee ( tx , 1999 , 0 , pool ) , nMinTxFee * 1.999 ) ;
2021-06-27 00:37:10 +02:00
2021-10-09 00:17:36 +02:00
CWallet : : discardThreshold = COIN / 100 ;
2018-01-07 23:56:53 +01:00
}
2012-02-27 13:19:32 +01:00
BOOST_AUTO_TEST_SUITE_END ( )