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