diff options
Diffstat (limited to 'src/selftest.cpp')
| -rw-r--r-- | src/selftest.cpp | 286 |
1 files changed, 285 insertions, 1 deletions
diff --git a/src/selftest.cpp b/src/selftest.cpp index 098eed6..90a30d2 100644 --- a/src/selftest.cpp +++ b/src/selftest.cpp | |||
| @@ -1231,6 +1231,43 @@ static int RunWalletSQLiteSelfTest() | |||
| 1231 | nFail += Check(!FileExists((strRuntimeDir + "/wallet.dat").c_str()), | 1231 | nFail += Check(!FileExists((strRuntimeDir + "/wallet.dat").c_str()), |
| 1232 | "active SQLite runtime does not create wallet.dat") ? 0 : 1; | 1232 | "active SQLite runtime does not create wallet.dat") ? 0 : 1; |
| 1233 | 1233 | ||
| 1234 | // Transaction bracket: a rolled-back group leaves nothing, a | ||
| 1235 | // committed group persists. This is the all-or-nothing that | ||
| 1236 | // WriteHDMaster relies on so a half-written seed cannot survive | ||
| 1237 | // a failure between its two records. | ||
| 1238 | bool fTxnBegan = WalletSQLiteRuntimeBeginTxn(); | ||
| 1239 | bool fTxnWrote = fTxnBegan && | ||
| 1240 | CWalletDB().WriteSetting("runtime-txn-rollback", (int64)111); | ||
| 1241 | if (fTxnBegan) | ||
| 1242 | WalletSQLiteRuntimeRollbackTxn(); | ||
| 1243 | nFail += Check(fTxnBegan && fTxnWrote, | ||
| 1244 | "a SQLite runtime transaction opens and rolls back") ? 0 : 1; | ||
| 1245 | int64 nRolledBack = 0; | ||
| 1246 | nFail += Check(!CWalletDB("r").ReadSetting("runtime-txn-rollback", | ||
| 1247 | nRolledBack), | ||
| 1248 | "a rolled-back SQLite runtime write leaves no record") ? 0 : 1; | ||
| 1249 | |||
| 1250 | bool fCommitOk = WalletSQLiteRuntimeBeginTxn() && | ||
| 1251 | CWalletDB().WriteSetting("runtime-txn-commit", (int64)222) && | ||
| 1252 | WalletSQLiteRuntimeCommitTxn(); | ||
| 1253 | int64 nCommitted = 0; | ||
| 1254 | nFail += Check(fCommitOk && | ||
| 1255 | CWalletDB("r").ReadSetting("runtime-txn-commit", | ||
| 1256 | nCommitted) && | ||
| 1257 | nCommitted == 222, | ||
| 1258 | "a committed SQLite runtime write persists") ? 0 : 1; | ||
| 1259 | |||
| 1260 | // WriteHDMaster brackets its two records in that transaction; | ||
| 1261 | // both halves of the seed must land. | ||
| 1262 | vector<unsigned char> vchTxnMaster(32, 0x5a); | ||
| 1263 | vector<unsigned char> vchTxnChainCode(32, 0xa5); | ||
| 1264 | vector<unsigned char> vchReadMaster, vchReadChainCode; | ||
| 1265 | nFail += Check(CWalletDB().WriteHDMaster(vchTxnMaster, vchTxnChainCode) && | ||
| 1266 | CWalletDB("r").ReadHDMaster(vchReadMaster, vchReadChainCode) && | ||
| 1267 | vchReadMaster == vchTxnMaster && | ||
| 1268 | vchReadChainCode == vchTxnChainCode, | ||
| 1269 | "WriteHDMaster commits both halves of the seed under SQLite") ? 0 : 1; | ||
| 1270 | |||
| 1234 | // Durability: close the backend (checkpoints the WAL), reopen | 1271 | // Durability: close the backend (checkpoints the WAL), reopen |
| 1235 | // the same file, and confirm what CWalletDB wrote is still | 1272 | // the same file, and confirm what CWalletDB wrote is still |
| 1236 | // there. This is the write-survives-a-restart property the real | 1273 | // there. This is the write-survives-a-restart property the real |
| @@ -2252,6 +2289,251 @@ static int RunWalletEncryptSelfTest() | |||
| 2252 | return nFail == 0 ? 0 : 1; | 2289 | return nFail == 0 ? 0 : 1; |
| 2253 | } | 2290 | } |
| 2254 | 2291 | ||
| 2292 | // Encrypting a wallet while it runs on the SQLite backend. Unlike the Berkeley | ||
| 2293 | // DB encrypt test above, this drives the real command-line path end to end in | ||
| 2294 | // child processes: build a Berkeley DB wallet, export it to SQLite, then run | ||
| 2295 | // -walletbackend=sqlite -encryptwallet and prove the SQLite wallet came out | ||
| 2296 | // encrypted, unlockable, and with wallet.dat left untouched. | ||
| 2297 | static int RunWalletSQLiteEncryptSelfTest() | ||
| 2298 | { | ||
| 2299 | fflush(stdout); | ||
| 2300 | printf("wallet-sqlite-encrypt self-test\n"); | ||
| 2301 | |||
| 2302 | std::string tmp; | ||
| 2303 | std::string cwd; | ||
| 2304 | if (!MakeTempDir(tmp)) | ||
| 2305 | { | ||
| 2306 | printf(" FAIL could not create a temporary data directory\n"); | ||
| 2307 | return 1; | ||
| 2308 | } | ||
| 2309 | if (!GetCurrentDir(cwd) || !SetCurrentDir(tmp)) | ||
| 2310 | { | ||
| 2311 | printf(" FAIL could not move into the temporary data directory\n"); | ||
| 2312 | RemoveTree(tmp); | ||
| 2313 | return 1; | ||
| 2314 | } | ||
| 2315 | |||
| 2316 | int nFail = 0; | ||
| 2317 | strSetDataDir = tmp; | ||
| 2318 | printf(" temp datadir: %s\n", tmp.c_str()); | ||
| 2319 | |||
| 2320 | try | ||
| 2321 | { | ||
| 2322 | // Build a normal Berkeley DB wallet with a recovery phrase, a key pool, | ||
| 2323 | // and a couple of public records, then release it. Everything after this | ||
| 2324 | // runs the real command-line paths in child processes. | ||
| 2325 | if (!LoadWallet()) | ||
| 2326 | throw std::runtime_error("LoadWallet failed"); | ||
| 2327 | |||
| 2328 | string strError; | ||
| 2329 | const string strMnemonic = | ||
| 2330 | "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; | ||
| 2331 | nFail += Check(SetHDSeedFromMnemonic(strMnemonic, strError), | ||
| 2332 | "a phrase can be installed before encryption") ? 0 : 1; | ||
| 2333 | TopUpKeyPool(); | ||
| 2334 | |||
| 2335 | vector<unsigned char> vchDefaultPubKey = keyUser.GetPubKey(); | ||
| 2336 | CKey publicLabelKey; | ||
| 2337 | publicLabelKey.MakeNewKey(); | ||
| 2338 | string strPublicLabelAddress = PubKeyToAddress(publicLabelKey.GetPubKey()); | ||
| 2339 | nFail += Check(CWalletDB().WriteName(strPublicLabelAddress, "rewrite-public-name") && | ||
| 2340 | CWalletDB().WriteSetting("rewrite-public-setting", (int64)424242), | ||
| 2341 | "public wallet records can be written before encryption") ? 0 : 1; | ||
| 2342 | |||
| 2343 | CPrivKey vchDefaultPrivKey; | ||
| 2344 | nFail += Check(GetWalletPrivKey(vchDefaultPubKey, vchDefaultPrivKey, strError), | ||
| 2345 | "the default private key is readable before encryption") ? 0 : 1; | ||
| 2346 | vector<unsigned char> vchDefaultPrivBytes(vchDefaultPrivKey.begin(), vchDefaultPrivKey.end()); | ||
| 2347 | vector<unsigned char> vchPlainHDMaster = vchHDMaster; | ||
| 2348 | vector<unsigned char> vchPlainHDChainCode = vchHDChainCode; | ||
| 2349 | |||
| 2350 | // Release wallet.dat so a child process can read it for the export. | ||
| 2351 | DBFlush(true); | ||
| 2352 | |||
| 2353 | #ifdef _WIN32 | ||
| 2354 | string strExe = cwd + "\\bitflash.exe"; | ||
| 2355 | #else | ||
| 2356 | string strExe = cwd + "/bitflash-node"; | ||
| 2357 | #endif | ||
| 2358 | string strSQLitePath = tmp + "/wallet.sqlite"; | ||
| 2359 | string strWalletDatPath = tmp + "/wallet.dat"; | ||
| 2360 | string strRightPassFile = tmp + "/right-pass.txt"; | ||
| 2361 | string strWrongPassFile = tmp + "/wrong-pass.txt"; | ||
| 2362 | nFail += Check(WriteTextFile(strRightPassFile, "btf-test-passphrase\n") && | ||
| 2363 | WriteTextFile(strWrongPassFile, "wrong-passphrase\n"), | ||
| 2364 | "passphrase files can be written for restarted commands") ? 0 : 1; | ||
| 2365 | |||
| 2366 | // Step 1: export the Berkeley DB wallet to SQLite. | ||
| 2367 | vector<string> vExportArgs; | ||
| 2368 | vExportArgs.push_back("-datadir=" + tmp); | ||
| 2369 | vExportArgs.push_back("-walletsqliteexport=" + strSQLitePath); | ||
| 2370 | vExportArgs.push_back("-nogui"); | ||
| 2371 | int nExportRet = RunBitflashChild(strExe, vExportArgs); | ||
| 2372 | nFail += Check(nExportRet == 0 && FileExists(strSQLitePath.c_str()), | ||
| 2373 | "the wallet exports to a SQLite file") ? 0 : 1; | ||
| 2374 | nFail += Check(FileContainsBytes(strSQLitePath, vchDefaultPrivBytes), | ||
| 2375 | "the exported SQLite wallet holds the plaintext key before encryption") ? 0 : 1; | ||
| 2376 | |||
| 2377 | // Step 2: encrypt through the real SQLite backend command path. | ||
| 2378 | string strEncryptOut = tmp + "/sqlite-encrypt-out.txt"; | ||
| 2379 | vector<string> vEncryptArgs; | ||
| 2380 | vEncryptArgs.push_back("-datadir=" + tmp); | ||
| 2381 | vEncryptArgs.push_back("-walletbackend=sqlite"); | ||
| 2382 | vEncryptArgs.push_back("-encryptwallet=@" + strRightPassFile); | ||
| 2383 | vEncryptArgs.push_back("-nogui"); | ||
| 2384 | int nEncryptRet = RunBitflashChild(strExe, vEncryptArgs, NULL, &strEncryptOut); | ||
| 2385 | nFail += Check(nEncryptRet == 0, | ||
| 2386 | "the SQLite wallet can be encrypted through -walletbackend=sqlite") ? 0 : 1; | ||
| 2387 | nFail += Check(FileContainsText(strEncryptOut, "Wallet backend: SQLite"), | ||
| 2388 | "encryption took the SQLite persistence path") ? 0 : 1; | ||
| 2389 | nFail += Check(FileContainsText(strEncryptOut, "wallet.sqlite.before-encrypt"), | ||
| 2390 | "the plaintext SQLite wallet is preserved as an explicit backup") ? 0 : 1; | ||
| 2391 | |||
| 2392 | // The encrypted SQLite wallet must no longer hold the plaintext secrets. | ||
| 2393 | nFail += Check(!FileContainsBytes(strSQLitePath, vchDefaultPrivBytes), | ||
| 2394 | "the encrypted SQLite wallet no longer contains the default private key bytes") ? 0 : 1; | ||
| 2395 | nFail += Check(!FileContainsBytes(strSQLitePath, vchPlainHDMaster), | ||
| 2396 | "the encrypted SQLite wallet no longer contains the HD master key bytes") ? 0 : 1; | ||
| 2397 | nFail += Check(!FileContainsBytes(strSQLitePath, vchPlainHDChainCode), | ||
| 2398 | "the encrypted SQLite wallet no longer contains the HD chain-code bytes") ? 0 : 1; | ||
| 2399 | |||
| 2400 | // The Berkeley DB wallet.dat is a separate file: the SQLite encrypt path | ||
| 2401 | // must never touch it, so its plaintext key is still exactly there. | ||
| 2402 | nFail += Check(FileContainsBytes(strWalletDatPath, vchDefaultPrivBytes), | ||
| 2403 | "the original Berkeley DB wallet.dat is left untouched by the SQLite encrypt") ? 0 : 1; | ||
| 2404 | |||
| 2405 | // Step 3: a locked encrypted SQLite wallet reports that phrase coverage | ||
| 2406 | // needs an unlock, exactly as the Berkeley DB path does (exit code 2). | ||
| 2407 | string strLockedAudit = tmp + "/locked-recovery-audit.txt"; | ||
| 2408 | vector<string> vLockedArgs; | ||
| 2409 | vLockedArgs.push_back("-datadir=" + tmp); | ||
| 2410 | vLockedArgs.push_back("-walletbackend=sqlite"); | ||
| 2411 | vLockedArgs.push_back("-recoveryaudit"); | ||
| 2412 | vLockedArgs.push_back("-nogui"); | ||
| 2413 | int nLockedRet = RunBitflashChild(strExe, vLockedArgs, NULL, &strLockedAudit); | ||
| 2414 | nFail += Check(nLockedRet == 2 && | ||
| 2415 | FileContainsText(strLockedAudit, "recovery phrase: encrypted, unlock wallet to audit"), | ||
| 2416 | "a locked encrypted SQLite wallet reports that phrase coverage needs unlock") ? 0 : 1; | ||
| 2417 | |||
| 2418 | // The wrong passphrase is rejected and reveals nothing. | ||
| 2419 | string strWrongDump = tmp + "/wrong-dump.txt"; | ||
| 2420 | vector<string> vWrongArgs; | ||
| 2421 | vWrongArgs.push_back("-datadir=" + tmp); | ||
| 2422 | vWrongArgs.push_back("-walletbackend=sqlite"); | ||
| 2423 | vWrongArgs.push_back("-walletpassphrase=@" + strWrongPassFile); | ||
| 2424 | vWrongArgs.push_back("-dumpwallet=" + strWrongDump); | ||
| 2425 | vWrongArgs.push_back("-nogui"); | ||
| 2426 | int nWrongRet = RunBitflashChild(strExe, vWrongArgs); | ||
| 2427 | nFail += Check(nWrongRet != 0 && !FileExists(strWrongDump.c_str()), | ||
| 2428 | "a restarted SQLite wallet rejects the wrong passphrase") ? 0 : 1; | ||
| 2429 | |||
| 2430 | // The right passphrase unlocks and can dump the original key. | ||
| 2431 | string strRightDump = tmp + "/right-dump.txt"; | ||
| 2432 | vector<string> vRightArgs; | ||
| 2433 | vRightArgs.push_back("-datadir=" + tmp); | ||
| 2434 | vRightArgs.push_back("-walletbackend=sqlite"); | ||
| 2435 | vRightArgs.push_back("-walletpassphrase=@" + strRightPassFile); | ||
| 2436 | vRightArgs.push_back("-dumpwallet=" + strRightDump); | ||
| 2437 | vRightArgs.push_back("-nogui"); | ||
| 2438 | int nRightRet = RunBitflashChild(strExe, vRightArgs); | ||
| 2439 | nFail += Check(nRightRet == 0 && FileExists(strRightDump.c_str()), | ||
| 2440 | "a restarted SQLite wallet unlocks with the right passphrase") ? 0 : 1; | ||
| 2441 | nFail += Check(FileContainsText(strRightDump, HexStrLocal(vchDefaultPrivBytes)), | ||
| 2442 | "the unlocked SQLite wallet decrypts and dumps the original key") ? 0 : 1; | ||
| 2443 | |||
| 2444 | // The recovery phrase survives the encryption. | ||
| 2445 | string strAudit = tmp + "/recovery-audit.txt"; | ||
| 2446 | vector<string> vAuditArgs; | ||
| 2447 | vAuditArgs.push_back("-datadir=" + tmp); | ||
| 2448 | vAuditArgs.push_back("-walletbackend=sqlite"); | ||
| 2449 | vAuditArgs.push_back("-walletpassphrase=@" + strRightPassFile); | ||
| 2450 | vAuditArgs.push_back("-recoveryaudit"); | ||
| 2451 | vAuditArgs.push_back("-nogui"); | ||
| 2452 | int nAuditRet = RunBitflashChild(strExe, vAuditArgs, NULL, &strAudit); | ||
| 2453 | nFail += Check(nAuditRet == 0 && | ||
| 2454 | FileContainsText(strAudit, "recovery phrase: present"), | ||
| 2455 | "the encrypted SQLite wallet still has its recovery phrase") ? 0 : 1; | ||
| 2456 | |||
| 2457 | // One-shot storage commands honour -walletbackend. The SQLite wallet is | ||
| 2458 | // encrypted; the wallet.dat beside it is still plaintext. Same datadir, | ||
| 2459 | // opposite answers -> the flag routes the diagnostic to the right file. | ||
| 2460 | string strSqliteAuditJson = tmp + "/sqlite-audit.json"; | ||
| 2461 | vector<string> vSqliteAuditArgs; | ||
| 2462 | vSqliteAuditArgs.push_back("-datadir=" + tmp); | ||
| 2463 | vSqliteAuditArgs.push_back("-walletbackend=sqlite"); | ||
| 2464 | vSqliteAuditArgs.push_back("-walletstorageauditjson=" + strSqliteAuditJson); | ||
| 2465 | vSqliteAuditArgs.push_back("-nogui"); | ||
| 2466 | int nSqliteAuditRet = RunBitflashChild(strExe, vSqliteAuditArgs); | ||
| 2467 | nFail += Check(nSqliteAuditRet == 0 && | ||
| 2468 | FileContainsText(strSqliteAuditJson, "\"encrypted_hd_seed\": \"complete\"") && | ||
| 2469 | FileContainsText(strSqliteAuditJson, "\"plain_hd_seed\": \"none\""), | ||
| 2470 | "-walletstorageaudit under SQLite reads the encrypted wallet.sqlite") ? 0 : 1; | ||
| 2471 | |||
| 2472 | string strBdbAuditJson = tmp + "/bdb-audit.json"; | ||
| 2473 | vector<string> vBdbAuditArgs; | ||
| 2474 | vBdbAuditArgs.push_back("-datadir=" + tmp); | ||
| 2475 | vBdbAuditArgs.push_back("-walletstorageauditjson=" + strBdbAuditJson); | ||
| 2476 | vBdbAuditArgs.push_back("-nogui"); | ||
| 2477 | int nBdbAuditRet = RunBitflashChild(strExe, vBdbAuditArgs); | ||
| 2478 | nFail += Check(nBdbAuditRet == 0 && | ||
| 2479 | FileContainsText(strBdbAuditJson, "\"plain_hd_seed\": \"complete\"") && | ||
| 2480 | FileContainsText(strBdbAuditJson, "\"encrypted_hd_seed\": \"none\""), | ||
| 2481 | "-walletstorageaudit without the flag still reads the plaintext wallet.dat") ? 0 : 1; | ||
| 2482 | |||
| 2483 | string strSqliteCheckOut = tmp + "/sqlite-check.txt"; | ||
| 2484 | vector<string> vSqliteCheckArgs; | ||
| 2485 | vSqliteCheckArgs.push_back("-datadir=" + tmp); | ||
| 2486 | vSqliteCheckArgs.push_back("-walletbackend=sqlite"); | ||
| 2487 | vSqliteCheckArgs.push_back("-walletstoragecheck"); | ||
| 2488 | vSqliteCheckArgs.push_back("-nogui"); | ||
| 2489 | int nSqliteCheckRet = RunBitflashChild(strExe, vSqliteCheckArgs, NULL, &strSqliteCheckOut); | ||
| 2490 | nFail += Check(nSqliteCheckRet == 0, | ||
| 2491 | "-walletstoragecheck under SQLite passes on the encrypted wallet.sqlite") ? 0 : 1; | ||
| 2492 | |||
| 2493 | // Backup under SQLite copies wallet.sqlite, not the plaintext wallet.dat. | ||
| 2494 | // The copy is a standalone SQLite wallet (loadcheck accepts it) and holds | ||
| 2495 | // none of the plaintext key that wallet.dat still contains. | ||
| 2496 | string strBackupDest = tmp + "/backup.sqlite"; | ||
| 2497 | vector<string> vBackupArgs; | ||
| 2498 | vBackupArgs.push_back("-datadir=" + tmp); | ||
| 2499 | vBackupArgs.push_back("-walletbackend=sqlite"); | ||
| 2500 | vBackupArgs.push_back("-backupwallet=" + strBackupDest); | ||
| 2501 | vBackupArgs.push_back("-nogui"); | ||
| 2502 | int nBackupRet = RunBitflashChild(strExe, vBackupArgs); | ||
| 2503 | nFail += Check(nBackupRet == 0 && FileExists(strBackupDest.c_str()), | ||
| 2504 | "-backupwallet under SQLite writes a backup file") ? 0 : 1; | ||
| 2505 | nFail += Check(!FileContainsBytes(strBackupDest, vchDefaultPrivBytes), | ||
| 2506 | "the SQLite backup holds no plaintext key (it copied wallet.sqlite, not wallet.dat)") ? 0 : 1; | ||
| 2507 | |||
| 2508 | string strBackupLoadCheck = tmp + "/backup-loadcheck.txt"; | ||
| 2509 | vector<string> vLoadCheckArgs; | ||
| 2510 | vLoadCheckArgs.push_back("-datadir=" + tmp); | ||
| 2511 | vLoadCheckArgs.push_back("-walletsqliteloadcheck=" + strBackupDest); | ||
| 2512 | vLoadCheckArgs.push_back("-nogui"); | ||
| 2513 | int nBackupLoadRet = RunBitflashChild(strExe, vLoadCheckArgs, NULL, &strBackupLoadCheck); | ||
| 2514 | nFail += Check(nBackupLoadRet == 0, | ||
| 2515 | "the SQLite backup is a valid standalone SQLite wallet") ? 0 : 1; | ||
| 2516 | } | ||
| 2517 | catch (const std::exception& e) | ||
| 2518 | { | ||
| 2519 | printf(" FAIL exception: %s\n", e.what()); | ||
| 2520 | nFail++; | ||
| 2521 | } | ||
| 2522 | catch (...) | ||
| 2523 | { | ||
| 2524 | printf(" FAIL unknown exception\n"); | ||
| 2525 | nFail++; | ||
| 2526 | } | ||
| 2527 | |||
| 2528 | DBFlush(true); | ||
| 2529 | SetCurrentDir(cwd); | ||
| 2530 | RemoveTree(tmp); | ||
| 2531 | printf("%s (%d failure%s)\n", nFail == 0 ? "ALL TESTS PASSED" : "TESTS FAILED", | ||
| 2532 | nFail, nFail == 1 ? "" : "s"); | ||
| 2533 | fflush(stdout); | ||
| 2534 | return nFail == 0 ? 0 : 1; | ||
| 2535 | } | ||
| 2536 | |||
| 2255 | static int RunWalletPortabilitySelfTest() | 2537 | static int RunWalletPortabilitySelfTest() |
| 2256 | { | 2538 | { |
| 2257 | fflush(stdout); | 2539 | fflush(stdout); |
| @@ -2981,6 +3263,8 @@ int RunSelfTest(const std::string& name) | |||
| 2981 | return RunWalletCryptoSelfTest(); | 3263 | return RunWalletCryptoSelfTest(); |
| 2982 | if (name == "wallet-encrypt") | 3264 | if (name == "wallet-encrypt") |
| 2983 | return RunWalletEncryptSelfTest(); | 3265 | return RunWalletEncryptSelfTest(); |
| 3266 | if (name == "wallet-sqlite-encrypt") | ||
| 3267 | return RunWalletSQLiteEncryptSelfTest(); | ||
| 2984 | if (name == "wallet-portability") | 3268 | if (name == "wallet-portability") |
| 2985 | return RunWalletPortabilitySelfTest(); | 3269 | return RunWalletPortabilitySelfTest(); |
| 2986 | if (name == "net-message") | 3270 | if (name == "net-message") |
| @@ -2997,6 +3281,6 @@ int RunSelfTest(const std::string& name) | |||
| 2997 | return RunManagedTorSelfTest(); | 3281 | return RunManagedTorSelfTest(); |
| 2998 | 3282 | ||
| 2999 | printf("Unknown self-test '%s'\n", name.c_str()); | 3283 | printf("Unknown self-test '%s'\n", name.c_str()); |
| 3000 | printf("Known self-tests: wallet-keypool, wallet-hd, wallet-format, wallet-storage-sanity, db-env-reopen, wallet-sqlite, wallet-sqlite-migration, wallet-crypto, wallet-encrypt, wallet-portability, net-message, consensus-limits, pool-stratum, parse-money, socks5-proxy, managed-tor\n"); | 3284 | printf("Known self-tests: wallet-keypool, wallet-hd, wallet-format, wallet-storage-sanity, db-env-reopen, wallet-sqlite, wallet-sqlite-migration, wallet-crypto, wallet-encrypt, wallet-sqlite-encrypt, wallet-portability, net-message, consensus-limits, pool-stratum, parse-money, socks5-proxy, managed-tor\n"); |
| 3001 | return 1; | 3285 | return 1; |
| 3002 | } | 3286 | } |