1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2018 The Bitcoin Core developers
3 // Distributed under the MIT software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 
6 #include <chainparams.h>
7 #include <init.h>
8 #include <interfaces/chain.h>
9 #include <net.h>
10 #include <scheduler.h>
11 #include <outputtype.h>
12 #include <util/system.h>
13 #include <util/moneystr.h>
14 #include <validation.h>
15 #include <walletinitinterface.h>
16 #include <wallet/rpcwallet.h>
17 #include <wallet/wallet.h>
18 #include <wallet/walletutil.h>
19 
20 class WalletInit : public WalletInitInterface {
21 public:
22 
23     //! Was the wallet component compiled in.
HasWalletSupport() const24     bool HasWalletSupport() const override {return true;}
25 
26     //! Return the wallets help message.
27     void AddWalletOptions() const override;
28 
29     //! Wallets parameter interaction
30     bool ParameterInteraction() const override;
31 
32     //! Add wallets that should be opened to list of init interfaces.
33     void Construct(InitInterfaces& interfaces) const override;
34 };
35 
36 const WalletInitInterface& g_wallet_init_interface = WalletInit();
37 
AddWalletOptions() const38 void WalletInit::AddWalletOptions() const
39 {
40     gArgs.AddArg("-addresstype", strprintf("What type of addresses to use (\"legacy\", \"p2sh-segwit\", or \"bech32\", default: \"%s\")", FormatOutputType(DEFAULT_ADDRESS_TYPE)), false, OptionsCategory::WALLET);
41     gArgs.AddArg("-avoidpartialspends", strprintf("Group outputs by address, selecting all or none, instead of selecting on a per-output basis. Privacy is improved as an address is only used once (unless someone sends to it after spending from it), but may result in slightly higher fees as suboptimal coin selection may result due to the added limitation (default: %u)", DEFAULT_AVOIDPARTIALSPENDS), false, OptionsCategory::WALLET);
42     gArgs.AddArg("-changetype", "What type of change to use (\"legacy\", \"p2sh-segwit\", or \"bech32\"). Default is same as -addresstype, except when -addresstype=p2sh-segwit a native segwit output is used when sending to a native segwit address)", false, OptionsCategory::WALLET);
43     gArgs.AddArg("-disablewallet", "Do not load the wallet and disable wallet RPC calls", false, OptionsCategory::WALLET);
44     gArgs.AddArg("-discardfee=<amt>", strprintf("The fee rate (in %s/kB) that indicates your tolerance for discarding change by adding it to the fee (default: %s). "
45                                                                 "Note: An output is discarded if it is dust at this rate, but we will always discard up to the dust relay fee and a discard fee above that is limited by the fee estimate for the longest target",
46                                                               CURRENCY_UNIT, FormatMoney(DEFAULT_DISCARD_FEE)), false, OptionsCategory::WALLET);
47     gArgs.AddArg("-fallbackfee=<amt>", strprintf("A fee rate (in %s/kB) that will be used when fee estimation has insufficient data (default: %s)",
48                                                                CURRENCY_UNIT, FormatMoney(DEFAULT_FALLBACK_FEE)), false, OptionsCategory::WALLET);
49     gArgs.AddArg("-keypool=<n>", strprintf("Set key pool size to <n> (default: %u)", DEFAULT_KEYPOOL_SIZE), false, OptionsCategory::WALLET);
50     gArgs.AddArg("-mintxfee=<amt>", strprintf("Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s)",
51                                                             CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MINFEE)), false, OptionsCategory::WALLET);
52     gArgs.AddArg("-paytxfee=<amt>", strprintf("Fee (in %s/kB) to add to transactions you send (default: %s)",
53                                                             CURRENCY_UNIT, FormatMoney(CFeeRate{DEFAULT_PAY_TX_FEE}.GetFeePerK())), false, OptionsCategory::WALLET);
54     gArgs.AddArg("-rescan", "Rescan the block chain for missing wallet transactions on startup", false, OptionsCategory::WALLET);
55     gArgs.AddArg("-salvagewallet", "Attempt to recover private keys from a corrupt wallet on startup", false, OptionsCategory::WALLET);
56     gArgs.AddArg("-spendzeroconfchange", strprintf("Spend unconfirmed change when sending transactions (default: %u)", DEFAULT_SPEND_ZEROCONF_CHANGE), false, OptionsCategory::WALLET);
57     gArgs.AddArg("-txconfirmtarget=<n>", strprintf("If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)", DEFAULT_TX_CONFIRM_TARGET), false, OptionsCategory::WALLET);
58     gArgs.AddArg("-upgradewallet", "Upgrade wallet to latest format on startup", false, OptionsCategory::WALLET);
59     gArgs.AddArg("-wallet=<path>", "Specify wallet database path. Can be specified multiple times to load multiple wallets. Path is interpreted relative to <walletdir> if it is not absolute, and will be created if it does not exist (as a directory containing a wallet.dat file and log files). For backwards compatibility this will also accept names of existing data files in <walletdir>.)", false, OptionsCategory::WALLET);
60     gArgs.AddArg("-walletbroadcast",  strprintf("Make the wallet broadcast transactions (default: %u)", DEFAULT_WALLETBROADCAST), false, OptionsCategory::WALLET);
61     gArgs.AddArg("-walletdir=<dir>", "Specify directory to hold wallets (default: <datadir>/wallets if it exists, otherwise <datadir>)", false, OptionsCategory::WALLET);
62     gArgs.AddArg("-walletnotify=<cmd>", "Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)", false, OptionsCategory::WALLET);
63     gArgs.AddArg("-walletrbf", strprintf("Send transactions with full-RBF opt-in enabled (RPC only, default: %u)", DEFAULT_WALLET_RBF), false, OptionsCategory::WALLET);
64     gArgs.AddArg("-zapwallettxes=<mode>", "Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup"
65                                " (1 = keep tx meta data e.g. payment request information, 2 = drop tx meta data)", false, OptionsCategory::WALLET);
66 
67     gArgs.AddArg("-dblogsize=<n>", strprintf("Flush wallet database activity from memory to disk log every <n> megabytes (default: %u)", DEFAULT_WALLET_DBLOGSIZE), true, OptionsCategory::WALLET_DEBUG_TEST);
68     gArgs.AddArg("-flushwallet", strprintf("Run a thread to flush wallet periodically (default: %u)", DEFAULT_FLUSHWALLET), true, OptionsCategory::WALLET_DEBUG_TEST);
69     gArgs.AddArg("-privdb", strprintf("Sets the DB_PRIVATE flag in the wallet db environment (default: %u)", DEFAULT_WALLET_PRIVDB), true, OptionsCategory::WALLET_DEBUG_TEST);
70     gArgs.AddArg("-walletrejectlongchains", strprintf("Wallet will not create transactions that violate mempool chain limits (default: %u)", DEFAULT_WALLET_REJECT_LONG_CHAINS), true, OptionsCategory::WALLET_DEBUG_TEST);
71 }
72 
ParameterInteraction() const73 bool WalletInit::ParameterInteraction() const
74 {
75     if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
76         for (const std::string& wallet : gArgs.GetArgs("-wallet")) {
77             LogPrintf("%s: parameter interaction: -disablewallet -> ignoring -wallet=%s\n", __func__, wallet);
78         }
79 
80         return true;
81     }
82 
83     const bool is_multiwallet = gArgs.GetArgs("-wallet").size() > 1;
84 
85     if (gArgs.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY) && gArgs.SoftSetBoolArg("-walletbroadcast", false)) {
86         LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting -walletbroadcast=0\n", __func__);
87     }
88 
89     if (gArgs.GetBoolArg("-salvagewallet", false)) {
90         if (is_multiwallet) {
91             return InitError(strprintf("%s is only allowed with a single wallet file", "-salvagewallet"));
92         }
93         // Rewrite just private keys: rescan to find transactions
94         if (gArgs.SoftSetBoolArg("-rescan", true)) {
95             LogPrintf("%s: parameter interaction: -salvagewallet=1 -> setting -rescan=1\n", __func__);
96         }
97     }
98 
99     bool zapwallettxes = gArgs.GetBoolArg("-zapwallettxes", false);
100     // -zapwallettxes implies dropping the mempool on startup
101     if (zapwallettxes && gArgs.SoftSetBoolArg("-persistmempool", false)) {
102         LogPrintf("%s: parameter interaction: -zapwallettxes enabled -> setting -persistmempool=0\n", __func__);
103     }
104 
105     // -zapwallettxes implies a rescan
106     if (zapwallettxes) {
107         if (is_multiwallet) {
108             return InitError(strprintf("%s is only allowed with a single wallet file", "-zapwallettxes"));
109         }
110         if (gArgs.SoftSetBoolArg("-rescan", true)) {
111             LogPrintf("%s: parameter interaction: -zapwallettxes enabled -> setting -rescan=1\n", __func__);
112         }
113     }
114 
115     if (is_multiwallet) {
116         if (gArgs.GetBoolArg("-upgradewallet", false)) {
117             return InitError(strprintf("%s is only allowed with a single wallet file", "-upgradewallet"));
118         }
119     }
120 
121     if (gArgs.GetBoolArg("-sysperms", false))
122         return InitError("-sysperms is not allowed in combination with enabled wallet functionality");
123     if (gArgs.GetArg("-prune", 0) && gArgs.GetBoolArg("-rescan", false))
124         return InitError(_("Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again."));
125 
126     if (::minRelayTxFee.GetFeePerK() > HIGH_TX_FEE_PER_KB)
127         InitWarning(AmountHighWarn("-minrelaytxfee") + " " +
128                     _("The wallet will avoid paying less than the minimum relay fee."));
129 
130     return true;
131 }
132 
VerifyWallets(interfaces::Chain & chain,const std::vector<std::string> & wallet_files)133 bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files)
134 {
135     if (gArgs.IsArgSet("-walletdir")) {
136         fs::path wallet_dir = gArgs.GetArg("-walletdir", "");
137         boost::system::error_code error;
138         // The canonical path cleans the path, preventing >1 Berkeley environment instances for the same directory
139         fs::path canonical_wallet_dir = fs::canonical(wallet_dir, error);
140         if (error || !fs::exists(wallet_dir)) {
141             return InitError(strprintf(_("Specified -walletdir \"%s\" does not exist"), wallet_dir.string()));
142         } else if (!fs::is_directory(wallet_dir)) {
143             return InitError(strprintf(_("Specified -walletdir \"%s\" is not a directory"), wallet_dir.string()));
144         // The canonical path transforms relative paths into absolute ones, so we check the non-canonical version
145         } else if (!wallet_dir.is_absolute()) {
146             return InitError(strprintf(_("Specified -walletdir \"%s\" is a relative path"), wallet_dir.string()));
147         }
148         gArgs.ForceSetArg("-walletdir", canonical_wallet_dir.string());
149     }
150 
151     LogPrintf("Using wallet directory %s\n", GetWalletDir().string());
152 
153     uiInterface.InitMessage(_("Verifying wallet(s)..."));
154 
155     // Parameter interaction code should have thrown an error if -salvagewallet
156     // was enabled with more than wallet file, so the wallet_files size check
157     // here should have no effect.
158     bool salvage_wallet = gArgs.GetBoolArg("-salvagewallet", false) && wallet_files.size() <= 1;
159 
160     // Keep track of each wallet absolute path to detect duplicates.
161     std::set<fs::path> wallet_paths;
162 
163     for (const auto& wallet_file : wallet_files) {
164         WalletLocation location(wallet_file);
165 
166         if (!wallet_paths.insert(location.GetPath()).second) {
167             return InitError(strprintf(_("Error loading wallet %s. Duplicate -wallet filename specified."), wallet_file));
168         }
169 
170         std::string error_string;
171         std::string warning_string;
172         bool verify_success = CWallet::Verify(chain, location, salvage_wallet, error_string, warning_string);
173         if (!error_string.empty()) InitError(error_string);
174         if (!warning_string.empty()) InitWarning(warning_string);
175         if (!verify_success) return false;
176     }
177 
178     return true;
179 }
180 
Construct(InitInterfaces & interfaces) const181 void WalletInit::Construct(InitInterfaces& interfaces) const
182 {
183     if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
184         LogPrintf("Wallet disabled!\n");
185         return;
186     }
187     gArgs.SoftSetArg("-wallet", "");
188     interfaces.chain_clients.emplace_back(interfaces::MakeWalletClient(*interfaces.chain, gArgs.GetArgs("-wallet")));
189 }
190 
LoadWallets(interfaces::Chain & chain,const std::vector<std::string> & wallet_files)191 bool LoadWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files)
192 {
193     for (const std::string& walletFile : wallet_files) {
194         std::shared_ptr<CWallet> pwallet = CWallet::CreateWalletFromFile(chain, WalletLocation(walletFile));
195         if (!pwallet) {
196             return false;
197         }
198         AddWallet(pwallet);
199     }
200 
201     return true;
202 }
203 
StartWallets(CScheduler & scheduler)204 void StartWallets(CScheduler& scheduler)
205 {
206     for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
207         pwallet->postInitProcess();
208     }
209 
210     // Run a thread to flush wallet periodically
211     scheduler.scheduleEvery(MaybeCompactWalletDB, 500);
212 }
213 
FlushWallets()214 void FlushWallets()
215 {
216     for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
217         pwallet->Flush(false);
218     }
219 }
220 
StopWallets()221 void StopWallets()
222 {
223     for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
224         pwallet->Flush(true);
225     }
226 }
227 
UnloadWallets()228 void UnloadWallets()
229 {
230     auto wallets = GetWallets();
231     while (!wallets.empty()) {
232         auto wallet = wallets.back();
233         wallets.pop_back();
234         RemoveWallet(wallet);
235         UnloadWallet(std::move(wallet));
236     }
237 }
238