wallet: Add sqlite as an alternative wallet database and use it for new descriptor wallets #19077

pull achow101 wants to merge 26 commits into bitcoin:master from achow101:sqlite-wallet changing 34 files +934 −80
  1. achow101 commented at 2:42 am on May 27, 2020: member

    This PR adds a new class SQLiteDatabase which is a subclass of WalletDatabase. This provides access to a SQLite database that is used to store the wallet records. To keep compatibility with BDB and to complexity of the change down, we don’t make use of many SQLite’s features. We use it strictly as a key-value store. We create a table main which has two columns, key and value both with the type blob.

    For new descriptor wallets, we will create a SQLiteDatabase instead of a BerkeleyDatabase. There is no requirement that all SQLite wallets are descriptor wallets, nor is there a requirement that all descriptor wallets be SQLite wallets. This allows for existing descriptor wallets to work as well as keeping open the option to migrate existing wallets to SQLite.

    We keep the name wallet.dat for SQLite wallets. We are able to determine which database type to use by searching for specific magic bytes in the wallet.dat file. SQLite begins it’s files with a null terminated string SQLite format 3. BDB has 0x00053162 at byte 12 (note that the byte order of this integer depends on the system endianness). So when we see that there is a wallet.dat file that we want to open, we check for the magic bytes to determine which database system to use.

    I decided to keep the wallet.dat naming to keep things like backup script to continue to function as they won’t need to be modified to look for a different file name. It also simplifies a couple of things in the implementation and the tests as wallet.dat is something that is specifically being looked for. If we don’t want this behavior, then I do have another branch which creates wallet.sqlite files instead, but I find that this direction is easier.

  2. fanquake added the label Wallet on May 27, 2020
  3. achow101 force-pushed on May 27, 2020
  4. achow101 force-pushed on May 27, 2020
  5. achow101 commented at 3:19 am on May 27, 2020: member
    I’ve dropped the amalgamation file
  6. achow101 force-pushed on May 27, 2020
  7. achow101 force-pushed on May 27, 2020
  8. achow101 force-pushed on May 27, 2020
  9. jonasschnelli commented at 3:54 pm on May 27, 2020: contributor

    Pretty amazing work! Thanks for doing this. For testing purposes, would it make sense to add logdb (#5686, simple implementation) in order to test and benchmark?

    Concept ACK on a BDB replacement for descriptor wallets. Still unsure wether sqlite or an internal format should be chosen. Maybe a comparison(-matrix) of internal vs. sqlite could be done?

    As for concrete implementation steps, maybe it would make sense to PR the DB flexibility first, then additional storage engines later.

  10. achow101 force-pushed on May 27, 2020
  11. achow101 commented at 4:25 pm on May 27, 2020: member

    For testing purposes, would it make sense to add logdb (#5686, simple implementation) in order to test and benchmark?

    I don’t think it really makes sense to add a database system that we aren’t going to use.

    Still unsure wether sqlite or an internal format should be chosen. Maybe a comparison(-matrix) of internal vs. sqlite could be done?

    I think there’s two primary reasons to choose sqlite over an internal format.

    1. Review and implementation are much simpler The library already exists so implementation just means correctly using the API. Reviewers won’t have to review a file format implementation and convince themselves that that format won’t corrupt and is robust.

    2. Better guarantees of consistency and non-corruption. SQLite is very well tested and very widely used. I think they are able better guarantee that data will get written, won’t get lost, and won’t get corrupted, than we would be able to with an internal format.

    As for concrete implementation steps, maybe it would make sense to PR the DB flexibility first, then additional storage engines later.

    #18971 does the DB class stuff that gives us this flexibility. This PR is adding in the storage engine and the logic for CWallet to choose which storage to use.

  12. achow101 force-pushed on May 27, 2020
  13. achow101 force-pushed on May 27, 2020
  14. practicalswift commented at 7:21 pm on May 27, 2020: contributor

    Concept ACK

    Nice work! Very readable code!

  15. DrahtBot commented at 7:50 pm on May 27, 2020: member

    The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #20094 (wallet: Unify wallet directory lock error message by hebasto)
    • #19502 (Bugfix: Wallet: Soft-fail exceptions within ListWalletDir file checks by luke-jr)
    • #19419 (wallet: let Listwalletdir do not iterate through our blocksdata. by Saibato)
    • #19101 (refactor: remove ::vpwallets and related global variables by ryanofsky)
    • #18095 (Fix crashes and infinite loop in ListWalletDir() by uhliksk)
    • #18077 (net: Add NAT-PMP port forwarding support by hebasto)

    If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

  16. achow101 force-pushed on May 27, 2020
  17. achow101 force-pushed on May 27, 2020
  18. laanwj commented at 10:41 am on May 28, 2020: member
    CONCEPT ACK :tada: :partying_face: :tada: Very happy to move on from BerkeleyDB and I’ve always liked sqlite as a versatile but still minimalistic replacement.
  19. achow101 force-pushed on May 28, 2020
  20. achow101 force-pushed on May 28, 2020
  21. Sjors commented at 3:28 pm on June 1, 2020: member

    Concept ACK. I’m able to build and run the test suite (including feature_backwards_compatibility.py) on macOS 10.15.4 with Homebrew brew install sqlite3 (don’t forget to add). I’m also able to build with /depends. I’m able to load an existing descriptor wallet (bdb) and create a new one.

    Is there a particular reason to stick to .dat as the file extension, rather than .sqlite? If you do the latter, listwalletdir and the Open Wallet GUI need a trivial change.

    Would it make sense to switch some of the records over to a format that’s more easy to inspect with a SQLite viewer? As well as use tables like “transactions”? Or would that make this PR far too complicated?

  22. achow101 commented at 3:58 pm on June 1, 2020: member

    Is there a particular reason to stick to .dat as the file extension, rather than .sqlite? If you do the latter, listwalletdir and the Open Wallet GUI need a trivial change.

    There are 2 reasons. The first is that it’s easier on review on implementation to stick to one filename. As you mentioned, if I make it .sqlite, listwalletdir and other places need to be changed. There are several places throughout the codebase where we specifically look for wallet.dat and not changing those to also use wallet.sqlite could be very bad. Additionally, some of those places may need to know whether they are looking for wallet.dat or wallet.sqlite so they would need access to whether a wallet is a descriptor wallet. They may need to know what kind of storage to look for which exposes more information than we currently already do. It’s much simpler to just keep it wallet.dat. This avoids issues where the wrong filename could be used and makes review simpler.

    The second reason is that there are already lots of tooling, documentation, and discussion that use wallet.dat. Things like backup scripts or instructions telling people how to backup their wallet won’t be invalidated as we keep the same naming. For the most part, the end user doesn’t care about how the data is being stored within the file, they just need to know to preserve the wallet.dat file. So by keeping the naming, all of these things stay the same and make it less likely for people to lose their money.

    There are, of course, a few cases where tooling does need to be updated because of the format change. But this tooling is all for manual inspection of the wallet.dat file and most users aren’t using that tooling.

    Would it make sense to switch some of the records over to a format that’s more easy to inspect with a SQLite viewer? As well as use tables like “transactions”? Or would that make this PR far too complicated?

    I would like to do that in the future. But I think that is too complicated for right now.

  23. achow101 force-pushed on Jun 1, 2020
  24. achow101 force-pushed on Jun 2, 2020
  25. achow101 force-pushed on Jun 2, 2020
  26. luke-jr commented at 6:03 pm on June 3, 2020: member

    I don’t think it really makes sense to add a database system that we aren’t going to use.

    Maybe it’s time to use logdb.

    Review and implementation are much simpler

    Realistically, this should be phrased “review and implementation are behind closed doors by another team, and non-transparent”.

    While SQLite has a free license, it is not open development. I’m not sure if their review standards are even documented.

    I’ve always liked sqlite as a versatile but still minimalistic

    Not sure SQLite counts as minimalistic… :p

  27. achow101 force-pushed on Jun 9, 2020
  28. jamesob commented at 8:33 pm on June 11, 2020: member
    Concept ACK. Awesome.
  29. achow101 force-pushed on Jun 16, 2020
  30. achow101 force-pushed on Jun 16, 2020
  31. achow101 force-pushed on Jun 18, 2020
  32. achow101 force-pushed on Jun 19, 2020
  33. achow101 force-pushed on Jun 19, 2020
  34. meshcollider added this to the "Design" column in a project

  35. achow101 force-pushed on Jun 20, 2020
  36. achow101 force-pushed on Jun 22, 2020
  37. achow101 force-pushed on Jun 23, 2020
  38. achow101 force-pushed on Jul 1, 2020
  39. achow101 force-pushed on Jul 6, 2020
  40. achow101 force-pushed on Jul 9, 2020
  41. achow101 force-pushed on Jul 14, 2020
  42. achow101 force-pushed on Jul 23, 2020
  43. achow101 marked this as ready for review on Jul 23, 2020
  44. achow101 commented at 3:50 am on July 23, 2020: member
    Since #19334 has been merged, this is now ready for review.
  45. in src/wallet/walletutil.cpp:138 in 6e7ef52e35 outdated
    119+    if (path_type == fs::directory_file || (path_type == fs::symlink_file && fs::is_directory(path))) {
    120         path = fs::absolute("wallet.dat", m_path);
    121+        return fs::symlink_status(path).type() != fs::file_not_found;
    122     }
    123-    return fs::symlink_status(path).type() != fs::file_not_found;
    124+    // Something exists here but we don't know what it is... Just say something exists so an error can be caught later
    


    ryanofsky commented at 8:20 pm on July 24, 2020:

    In commit “Change WalletLocation::Exists to check for wallet file existence” (6e7ef52e359e55bc82a30207a701995f0ca255d7)

    Minor: I can’t really figure out what this commit and also the previous commit “wallet: Don’t Verify if database location doesn’t exist” (b64e40310742e3975ae704a801ccafa73d8617bf) are doing. Changes seem harmless, but they are making code more complicated. It would be good if commit messages mentioned motivations in commits like these where the motivations aren’t obvious.


    achow101 commented at 9:20 pm on July 24, 2020:

    The motivation is so that we check the wallet file itself as part of the db type checking later on. IIRC there were some issues with where the wrong file type was being used that necessitated these changes.

    I’ll try to expand the commit message

  46. in configure.ac:1180 in 31243f5482 outdated
    1137@@ -1138,6 +1138,9 @@ fi
    1138 if test x$enable_wallet != xno; then
    1139     dnl Check for libdb_cxx only if wallet enabled
    1140     BITCOIN_FIND_BDB48
    1141+
    1142+    dnl Check for sqlite3
    1143+    AC_CHECK_HEADERS([sqlite3.h], [AC_CHECK_LIB([sqlite3], [sqlite3_open], [SQLITE_LIBS=-lsqlite3], [AC_MSG_ERROR(sqlite3_open missing from libsqlite3)], [-pthread -lpthread])], [AC_MSG_ERROR(sqlite3.h headers missing)])
    


    ryanofsky commented at 8:34 pm on July 24, 2020:

    In commit “Add libsqlite3” (31243f5482bb1c8a71affbe7ced6653a09bd6829)

    Would suggest splitting this commit and other build and depends and travis related commits into a separate build PR so it can get feedback from bitcoin build aficionados (and so this PR can more approachable for regular and wallet reviewers).

    I think probably build reviewers will want a --with-sqlite configure option to allow sqlite to be disabled in the build even if it is present in the system. They might also want the sqlite location to be determined through pkgconfig instead of assumed to be in the system include directory.


    ryanofsky commented at 10:01 pm on September 30, 2020:

    re: #19077 (review)

    In commit “Add libsqlite3” (10124c3d6c5176d5df94964e530a4b0c6edd8381)

    Minor: Maybe someone more familiar with the build can weigh in, but it seems like it could be desirable to allow building bitcoin without sqlite even if sqlite is installed on the system. Also, I think other dependency checks have been switched to use pkg-config (#18307) instead ad-hoc methods like this.


    achow101 commented at 0:19 am on October 2, 2020:
    I attempted to make it so that BDB or sqlite could be disabled, but I wasn’t able to get it to work. I’ll leave that for a followup for someone more familiar with autotools.
  47. in src/wallet/sqlite.h:96 in 08847ca4de outdated
    91@@ -83,6 +92,8 @@ class SQLiteDatabase : public WalletDatabase
    92 
    93     /** Make a SQLiteBatch connected to this database */
    94     std::unique_ptr<DatabaseBatch> MakeBatch(const char* mode = "r+", bool flush_on_close = true) override;
    95+
    96+    sqlite3* m_db;
    


    ryanofsky commented at 8:37 pm on July 24, 2020:

    In commit “Constructors, destructors, and relevant private fields for SQLiteDatabase/Batch” (08847ca4de58c112da4b6455230f7b454afa543c)

    Minor: Suggest sqlite3* m_db{nullptr}; or sqlite3* m_db = nullptr; here to be sure this is safe without looking and even if someone adds another constructor.


    achow101 commented at 0:47 am on July 27, 2020:
    Done
  48. in src/wallet/sqlite.cpp:18 in d038d04a4e outdated
    13 #include <sqlite3.h>
    14 #include <stdint.h>
    15+#include <unordered_set>
    16+
    17+namespace {
    18+    RecursiveMutex cs_sqlite;
    


    ryanofsky commented at 8:41 pm on July 24, 2020:

    In commit “Introduce g_file_paths” (d038d04a4e3e0522a51cf0d39110749f8395f61a)

    Minor: Pretty sure we can get rid of these globals with more sane loading code in the wallet, but in any case could consider switching RecursiveMutex to Mutex if possible and switching cs_sqlite to g_sqlite_mutex to follow newer conventions


    achow101 commented at 0:47 am on July 27, 2020:
    Renamed
  49. in src/wallet/sqlite.cpp:592 in b2f2e71718 outdated
    124@@ -124,3 +125,8 @@ bool SQLiteBatch::TxnAbort()
    125 {
    126     return false;
    127 }
    128+
    129+std::string SQLiteDatabaseVersion()
    


    ryanofsky commented at 8:43 pm on July 24, 2020:

    In commit “Implement SQLiteDatabaseVersion” (b2f2e7171861fe2998f2c27af44b65b0e1f558aa)

    Minor: SQLiteLibraryVersion might be a more descriptive name


    achow101 commented at 0:48 am on July 27, 2020:
    Probably, but I’m also following the convention set by BerkeleyDatabaseVersion.
  50. in src/wallet/sqlite.cpp:37 in 5c78c9094a outdated
    33@@ -34,6 +34,10 @@ SQLiteDatabase::SQLiteDatabase(const fs::path& dir_path, const fs::path& file_pa
    34     LogPrintf("Using SQLite Version %s\n", SQLiteDatabaseVersion());
    35     LogPrintf("Using wallet %s\n", m_dir_path);
    36 
    37+    int ret = sqlite3_initialize();
    


    ryanofsky commented at 8:47 pm on July 24, 2020:

    In commit “Initialize and Shutdown sqlite3 globals” (5c78c9094a0158e4d466b696a3bbbcbd35b51b15)

    Minor: Would add comment saying this is a no-op if it is already called. Another option would be to only call it when g_file_paths is empty.


    achow101 commented at 0:48 am on July 27, 2020:
    Done
  51. in src/wallet/sqlite.cpp:52 in 5c78c9094a outdated
    47@@ -44,6 +48,9 @@ SQLiteDatabase::~SQLiteDatabase()
    48     Close();
    49     LOCK(cs_sqlite);
    50     g_file_paths.erase(m_file_path);
    51+    if (g_file_paths.empty()) {
    52+        sqlite3_shutdown();
    


    ryanofsky commented at 8:48 pm on July 24, 2020:

    In commit “Initialize and Shutdown sqlite3 globals” (5c78c9094a0158e4d466b696a3bbbcbd35b51b15)

    Return value isn’t checked here. Would suggest at least logging an error so we know if something has gone wrong.


    achow101 commented at 0:48 am on July 27, 2020:
    Done
  52. ryanofsky commented at 8:59 pm on July 24, 2020: member

    Started reviewing commit by commit (will update list below with progress), but also have looked over the final diff. Change is surprisingly simple and clean.

    If it’s available, I’d be curious to see a diff of the additional changes that switch wallet.dat to wallet.sqlite and how complicated they are. Do we know how current & previous versions of bitcoins react if they load a wallet.dat file that doesn’t contain berkeley db data? Hopefully they show a sensible error instead of crashing obscurely or doing something worse like modifying the file.

    • b64e40310742e3975ae704a801ccafa73d8617bf wallet: Don’t Verify if database location doesn’t exist (1/31)
    • 6e7ef52e359e55bc82a30207a701995f0ca255d7 Change WalletLocation::Exists to check for wallet file existence (2/31)
    • 31243f5482bb1c8a71affbe7ced6653a09bd6829 Add libsqlite3 (3/31)
    • 786f79916a09727404de376882e4bf704f620362 Add sqlite to travis and depends (4/31)
    • 88e1bacfb7c5e9633239a35ad29cd71eb6f473fd Add SQLiteDatabase and SQLiteBatch dummy classes (5/31)
    • b2f2e7171861fe2998f2c27af44b65b0e1f558aa Implement SQLiteDatabaseVersion (6/31)
    • 08847ca4de58c112da4b6455230f7b454afa543c Constructors, destructors, and relevant private fields for SQLiteDatabase/Batch (7/31)
    • d038d04a4e3e0522a51cf0d39110749f8395f61a Introduce g_file_paths (8/31)
    • b52981b011d449d9374aabc54ae42d2ca9107e05 Add/Remove m_file_path to/from g_file_paths in con/destructor (9/31)
    • a9a07df70f2ea2ca85f5680349194127eef9739b Implement IsSQLiteWalletLoaded (10/31)
    • 5c78c9094a0158e4d466b696a3bbbcbd35b51b15 Initialize and Shutdown sqlite3 globals (11/31)
    • a321a2641ec99903a65ed313b133204e44e073a1 Setup SQLite error logging (12/31)
    • 3e2ce083f1e538ae4e7d442a2195f811ffa75e3b Implement SQLiteDatabase::Open (13/31)
    • bce13091a4f3160ac78235bc9bb5cf90d50fa125 Implement SQLiteDatabase::Close (14/31)
    • a9a2b2bec632c1e3aa27bea735ebe1b344914b80 Implement SQLiteBatch::Close (15/31)
    • 29f7a044972a92c2f4c988e10fc5e855cfa25b9a sqlitedb: Create and lock the wallet directory (16/31)
    • 3e2423fa7b9fccf56a7d53c4fecb08825f9e55df Implement SQLiteDatabase::RemoveRef and AddRef and add m_ref_count (17/31)
    • 18d921ffb93e5b2b2c3509fd689f8b51cd4a8347 Add SetupSQLStatements (18/31)
    • 071d7326703ee5eccd9aa33a6cad9f6787aa2e5d Implement SQLiteBatch::ReadKey, WriteKey, EraseKey, and HasKey (19/31)
    • 8b6f00df0e9a4c6411124910f09c8690fcab7e3e Implement SQLiteBatch::StartCursor, ReadAtCursor, and CloseCursor (20/31)
    • feb32f64d408da672b0c5249c94fca2b614e6ad4 Implement SQLiteDatabase::Backup (21/31)
    • 92aebab51bf03587c3f389771b8da808e5a1d675 Implement SQLiteDatabase::TxnBegin, TxnCommit, and TxnAbort (22/31)
    • ace969a38c1fd5d3c03a82bb36b7c20e641050e8 Implement SQLiteDatabase::Flush, PeriodicFlush, and ReloadDbEnv as No-ops (23/31)
    • 75ca8bac4db69fed4ac04ec9484fc94988e13dac Implement SQLiteDatabase::Rewrite (24/31)
    • cf8873757806a65868d0452545866df7fba8a54c Implement SQLiteDatabase::Verify (25/31)
    • 0ec07243dd8168b4d8f32692d9340085d17d46e4 Implement SQLiteDatabase::MakeBatch (26/31)
    • 3d50995df3b0086caec3463f06a639d53de7a4f3 Add StorageType enum (27/31)
    • 92727778ac3afda03aaeb2ad8ac09364df006dc8 Determine wallet file type based on file magic (28/31)
    • cb3ee8971120989be66066605fa03ed2c56e0bd6 walletutil: Wallets can also be sqlite (29/31)
    • 5ec9a9be158556aa94da4d8d2224776baab05044 Have CreateWalletDatabase functions take the StorageType (30/31)
    • ed80553046e47a96be27c8e12f69e98f4e8d3d74 Use SQLite for descriptor wallets (31/31)
  53. achow101 commented at 9:17 pm on July 24, 2020: member

    If it’s available, I’d be curious to see a diff of the additional changes that switch wallet.dat to wallet.sqlite and how complicated they are.

    https://github.com/achow101/bitcoin/tree/sqlite-wallet-w-rename is a copy of the original branch with the rename. It’s a bit outdated, but it should give you an idea of what was needed for renaming.

    Do we know how current & previous versions of bitcoins react if they load a wallet.dat file that doesn’t contain berkeley db data? Hopefully they show a sensible error instead of crashing obscurely or doing something worse like modifying the file.

    The wallets won’t be listed by listwalletdir as the bdb magic is checked in that function. BDB itself will error with Not a Berkeley DB (or something like that) when it tries to open the sqlite file and that error makes it’s way to the user.

  54. achow101 force-pushed on Jul 27, 2020
  55. DrahtBot added the label Needs rebase on Jul 27, 2020
  56. achow101 force-pushed on Jul 27, 2020
  57. DrahtBot removed the label Needs rebase on Jul 27, 2020
  58. ryanofsky referenced this in commit 8994145cb3 on Jul 29, 2020
  59. laanwj added this to the "Blockers" column in a project

  60. ryanofsky referenced this in commit 2553091ce5 on Jul 30, 2020
  61. ryanofsky referenced this in commit 0e4cb50e3c on Jul 30, 2020
  62. in src/wallet/walletutil.cpp:45 in e68484878b outdated
    52-    // Berkeley DB Btree magic bytes, from:
    53-    //  https://github.com/file/file/blob/5824af38469ec1ca9ac3ffd251e7afe9dc11e227/magic/Magdir/database#L74-L75
    54-    //  - big endian systems - 00 05 31 62
    55-    //  - little endian systems - 62 31 05 00
    56-    return data == 0x00053162 || data == 0x62310500;
    57+    return IsBDBFile(path) || IsSQLiteFile(path);
    


    promag commented at 2:41 pm on August 2, 2020:

    A couple of comments here:

    • file at path is opened twice.
    • nit, in DetermineStorageType you check IsSQLiteFile first, maybe change there for now as most is BDB.
    • maybe follow up, this could use DetermineStorageType.

    achow101 commented at 9:43 pm on August 4, 2020:

    file at path is opened twice.

    It is?

    Done the other 2 suggestions.


    promag commented at 9:46 pm on August 4, 2020:
    At worst case it is.
  63. promag commented at 8:44 pm on August 4, 2020: member
    Concept ACK.
  64. achow101 force-pushed on Aug 4, 2020
  65. achow101 force-pushed on Aug 4, 2020
  66. ryanofsky referenced this in commit 9b15bf35d9 on Aug 5, 2020
  67. DrahtBot added the label Needs rebase on Aug 5, 2020
  68. achow101 force-pushed on Aug 5, 2020
  69. DrahtBot removed the label Needs rebase on Aug 5, 2020
  70. ryanofsky referenced this in commit d5c8363b2e on Aug 7, 2020
  71. ryanofsky referenced this in commit d503001507 on Aug 7, 2020
  72. in test/functional/wallet_multiwallet.py:272 in 787047133d outdated
    268@@ -269,7 +269,7 @@ def wallet_file(name):
    269 
    270         # Fail to load if a directory is specified that doesn't contain a wallet
    271         os.mkdir(wallet_dir('empty_wallet_dir'))
    272-        assert_raises_rpc_error(-18, "Directory empty_wallet_dir does not contain a wallet.dat file", self.nodes[0].loadwallet, 'empty_wallet_dir')
    273+        assert_raises_rpc_error(-18, "Wallet empty_wallet_dir not found.", self.nodes[0].loadwallet, 'empty_wallet_dir')
    


    Sjors commented at 6:09 pm on August 7, 2020:
    787047133d22f014bea2646d3b23cd18801f19c2: you can drop the else if (fs::is_directory(location.GetPath() branch from rpcwallet.cpp
  73. Sjors commented at 6:43 pm on August 7, 2020: member

    Reviewed down to b459ada6fd3a5a2ba53cdd132a66ecdf4a574033

    I think probably build reviewers will want a –with-sqlite configure option to allow sqlite to be disabled in the build even if it is present in the system.

    Conversely, requiring sqlite3 is overkill until descriptor wallets are the default. I suggest disabling descriptor wallets when sqlite3 is either missing or opted out. Later on we’ll need similar code to allow opting out of BDB (disabling legacy wallets).

    Would suggest splitting this commit and other build and depends and travis related commits into a separate build PR so it can get feedback from bitcoin build aficionados (and so this PR can more approachable for regular and wallet reviewers).

    Worth considering, from my experience with #15382. In that case you can also extract the first two commits and put them in a trivial refactor PR, to make number go down :-)

    Depends could also have an opt-out, but no strong feelings there, as long as you can opt-out in the configure phase.

    I would also seperate the CI commit from the depends stuff.

    Should we constrain the sqlite3 minimum version, perhaps based on their CVE list?

  74. in src/wallet/sqlite.cpp:22 in fe790158c8 outdated
    15+#include <unordered_set>
    16+
    17+namespace {
    18+    Mutex g_sqlite_mutex;
    19+    //! Set of wallet file paths in use
    20+    std::unordered_set<std::string> g_file_paths GUARDED_BY(g_sqlite_mutex);
    


    ryanofsky commented at 10:02 pm on August 7, 2020:

    In commit “Add/Remove m_file_path to/from g_file_paths in con/destructor” (316002da052cad68c54a48e918d1d5f2ca58c0d0)

    Minor: Developer notes and current clang-format config don’t indent namespace contents

    Also, I wonder if these variables are even necessary. If sqlite supports opening databases in an exclusive mode, there should be no need for our code to maintain this additional list of database files.


    achow101 commented at 11:24 pm on August 10, 2020:
    sqlite does have a way to open databases in an exclusive mode but we don’t use it. But we also do that ourselves with the .walletlock file. So I think this is just unnecessary anyways and thus I’ve removed it.
  75. ryanofsky commented at 1:32 am on August 8, 2020: member

    I took a shot at rebasing this PR on top of #19619:

    Change is essentially the same but simpler and I could drop a number of commits. Also changing the database filename from wallet.dat to wallet.sqlite or something else is a one-line change (excluding tests).

  76. achow101 referenced this in commit 39866a9302 on Aug 10, 2020
  77. achow101 force-pushed on Aug 10, 2020
  78. achow101 commented at 10:11 pm on August 10, 2020: member
    I’ve taken @ryanofsky’s rebase and made a few changes. Notably I removed the filename change and the related tests. Also I made a slight change to CreateWallet` so that the GUI would also make sqlite wallets for descriptor wallets.
  79. achow101 force-pushed on Aug 10, 2020
  80. achow101 force-pushed on Aug 11, 2020
  81. achow101 force-pushed on Aug 12, 2020
  82. DrahtBot added the label Needs rebase on Aug 14, 2020
  83. achow101 referenced this in commit 72eaf4c29d on Aug 14, 2020
  84. achow101 force-pushed on Aug 14, 2020
  85. ryanofsky referenced this in commit 134f1807e5 on Aug 14, 2020
  86. ryanofsky referenced this in commit 717c4b290f on Aug 14, 2020
  87. DrahtBot removed the label Needs rebase on Aug 14, 2020
  88. ryanofsky referenced this in commit db66aae6ba on Aug 17, 2020
  89. ryanofsky referenced this in commit 44f3cf2ba5 on Aug 17, 2020
  90. laanwj removed this from the "Blockers" column in a project

  91. DrahtBot added the label Needs rebase on Aug 31, 2020
  92. ryanofsky referenced this in commit 2619e6545e on Sep 1, 2020
  93. ryanofsky referenced this in commit a574f21418 on Sep 1, 2020
  94. achow101 force-pushed on Sep 1, 2020
  95. DrahtBot removed the label Needs rebase on Sep 1, 2020
  96. DrahtBot added the label Needs rebase on Sep 3, 2020
  97. ryanofsky referenced this in commit c8e9cd742e on Sep 3, 2020
  98. ryanofsky referenced this in commit b5b414151a on Sep 3, 2020
  99. achow101 force-pushed on Sep 4, 2020
  100. DrahtBot removed the label Needs rebase on Sep 4, 2020
  101. meshcollider referenced this in commit 56d47e19ed on Sep 6, 2020
  102. meshcollider commented at 11:48 pm on September 6, 2020: contributor
    This can now be rebased after #19619 merge
  103. achow101 force-pushed on Sep 6, 2020
  104. achow101 commented at 11:55 pm on September 6, 2020: member
    Rebased
  105. pstratem commented at 5:18 pm on September 7, 2020: contributor

    Should probably be setting the application_id pragma to something constant and random.

    It’s also important that fullfsync be set because Mac OS X is a liar.

    Could also be setting user_version, which is a way of versioning the schema.

  106. achow101 commented at 10:05 pm on September 7, 2020: member

    Should probably be setting the application_id pragma to something constant and random.

    Could just set it to the network magic bytes? I think that might even let us deal with #12805 by ensuring that we only open a wallet that was created with the correct network magic as the application_id.

    Could also be setting user_version, which is a way of versioning the schema.

    Would it be useful to set it to the wallet version number or should this just be a new schema version number?

  107. sidhujag referenced this in commit 53b6e698d1 on Sep 9, 2020
  108. achow101 commented at 0:23 am on September 10, 2020: member
    Added application_id as the network magic. Also added user_version.
  109. achow101 force-pushed on Sep 10, 2020
  110. laanwj added this to the "Blockers" column in a project

  111. DrahtBot added the label Needs rebase on Sep 15, 2020
  112. achow101 force-pushed on Sep 15, 2020
  113. DrahtBot removed the label Needs rebase on Sep 15, 2020
  114. in src/wallet/sqlite.h:106 in 69f7321f8d outdated
    101+    /** Make a SQLiteBatch connected to this database */
    102+    std::unique_ptr<DatabaseBatch> MakeBatch(const char* mode = "r+", bool flush_on_close = true) override;
    103+
    104+    sqlite3* m_db{nullptr};
    105+
    106+    sqlite3_stmt* m_read_stmt = nullptr;
    


    S3RK commented at 8:37 am on September 19, 2020:
    1. I suppose the locking is done in the calling code. Do I understand correctly that it’s managed by cs_wallet?

    2. nit: I believe it’s better to have all members private. Why not make SQLiteBatch a friend class or just pass the statement handlers when we construct SQLiteBatch object.


    achow101 commented at 11:14 pm on September 21, 2020:

    I suppose the locking is done in the calling code. Do I understand correctly that it’s managed by cs_wallet?

    For the most part, yes. Sometimes we do need to handle concurrency withing SQLiteDatabase but those should already be handled by m_refcount and sqlite itself.

    nit: I believe it’s better to have all members private. Why not make SQLiteBatch a friend class or just pass the statement handlers when we construct SQLiteBatch object.

    I suppose now we could pass them in. In a previous revision, I don’t think that was possible.

    But it’s easier to just let them be public members. It would be 6 extra arguments to pass them in and we are already giving the SQLiteDatabase container.


    S3RK commented at 11:35 am on September 22, 2020:

    For the most part, yes. Sometimes we do need to handle concurrency withing SQLiteDatabase but those should already be handled by m_refcount and sqlite itself.

    Could you please elaborate on this part? It looks like there are indeed places when we access WalletDatabase instance from another thread without acquiring lock first. For example CWallet::chainStateFlushed. From my understanding of sqlite it’s safe to use by multiple threads, but they should be using different connections. Which is not the case in this example. And I’m not sure how m_refcount will help with that.

    I guess in BDB this was handled by cs_db within BerkeleyDatabase::Open


    achow101 commented at 4:57 pm on September 22, 2020:

    According to https://sqlite.org/threadsafe.html, the default multithreading mode is serialized which means that a single database connection can be used from multiple threads safely. So no locking is needed with that.

    I suppose we should enforce that when opening by setting SQLITE_OPEN_FULLMUTEX?


    S3RK commented at 10:03 am on September 23, 2020:

    Thanks for clarification.

    I suppose we should enforce that when opening by setting SQLITE_OPEN_FULLMUTEX?

    I think that’s a great idea. I would say that SQLITE_CONFIG_SERIALIZED is even better. It explicitly say that it’s safe to use both connection and prepared statement objects.


    achow101 commented at 4:17 pm on September 23, 2020:
    DOne
  115. in src/wallet/sqlite.cpp:142 in 69f7321f8d outdated
    121+        return false;
    122+    }
    123+
    124+    // Check the application ID matches our network magic
    125+    sqlite3_stmt* app_id_stmt;
    126+    ret = sqlite3_prepare_v2(db, "PRAGMA application_id", -1, &app_id_stmt, nullptr);
    


    S3RK commented at 9:45 am on September 19, 2020:
    I double pstratem’s comment regarding fullsync and checkpoint_fullsync

    achow101 commented at 1:13 am on September 22, 2020:
    Added fullfsync enabling. checkpoint_fullsync is not needed as we don’t use WAL mode.
  116. in src/wallet/sqlite.cpp:208 in 69f7321f8d outdated
    222+    }
    223+    if (m_mock) {
    224+        flags |= SQLITE_OPEN_MEMORY; // In memory database for mock db
    225+    }
    226+
    227+    if (m_db == nullptr) {
    


    S3RK commented at 10:03 am on September 19, 2020:
    What if we create read-only batch first and then a read-write batch? Looks like it’s going to fail since the connection will remain read-only.

    achow101 commented at 0:25 am on September 22, 2020:
    I think we always open in read-write mode first so that doesn’t have any effect. But I suppose we should still have a check for that.

    achow101 commented at 1:14 am on September 22, 2020:
    Done

    S3RK commented at 11:51 am on September 22, 2020:
    Actually, I couldn’t find a single place when we create read-only batch at all. Maybe I’m missing something. Do you know what was the original purpose to add read-only mode?

    achow101 commented at 4:53 pm on September 22, 2020:
    It’s pretty much just a leftover from BDB. The single place a readonly batch is used is in BerkeleyDatabase::Rewrite. Maybe we should just remove the readonly stuff, but that could be done in a followup.

    S3RK commented at 10:24 am on October 1, 2020:
    @achow101 I’d like to follow up on this if you don’t have plans to do it yourself

    achow101 commented at 4:12 pm on October 1, 2020:
    @S3RK Go ahead
  117. in src/wallet/sqlite.cpp:212 in 69f7321f8d outdated
    226+
    227+    if (m_db == nullptr) {
    228+        sqlite3* db = nullptr;
    229+        int ret = sqlite3_open_v2(m_file_path.c_str(), &db, flags, nullptr);
    230+        if (ret != SQLITE_OK) {
    231+            throw std::runtime_error(strprintf("SQLiteDatabase: Failed to open database: %s\n", sqlite3_errstr(ret)));
    


    S3RK commented at 10:06 am on September 19, 2020:
    nit: IIUC we need to close db handler even in the case of an error.

    achow101 commented at 0:27 am on September 22, 2020:
    It will be closed when the SQLiteDatabase is destructed.

    ryanofsky commented at 1:55 am on October 7, 2020:

    In commit “Implement SQLiteDatabase::Open” (d1559561a68dd6604efd91baa5dbd28c995d0074)

    re: #19077 (review)

    It will be closed when the SQLiteDatabase is destructed.

    It does looks like db pointer can be leaked here if ret is not OK.

  118. in src/wallet/sqlite.cpp:509 in 69f7321f8d outdated
    500+    return true;
    501+}
    502+
    503+bool SQLiteBatch::ReadAtCursor(CDataStream& key, CDataStream& value, bool& complete)
    504+{
    505+    complete = false;
    


    S3RK commented at 4:46 am on September 20, 2020:
    nit: why not verify m_cursor_init just in case?

    achow101 commented at 1:14 am on September 22, 2020:
    Done
  119. in src/wallet/sqlite.cpp:293 in ceac790fb2 outdated
    169@@ -170,7 +170,16 @@ void SQLiteDatabase::Open(const char* mode)
    170 
    171 bool SQLiteDatabase::Rewrite(const char* skip)
    172 {
    173-    return false;
    174+    while (true) {
    


    S3RK commented at 7:22 am on September 20, 2020:
    nit: maybe add an upper limit to avoid hanging the thread in case something went wrong?

    achow101 commented at 0:29 am on September 22, 2020:
    I think it’s fine

    ryanofsky commented at 3:57 am on October 6, 2020:

    re: #19077 (review)

    In commit “Implement SQLiteDatabase::Rewrite” (0d75b4013400b5a6bfa1f5c75082a8ff0701bd84)

    I think it’s fine

    This whole loop doesn’t seem like a good idea. If it actually serves a purpose, there should be a comment about it with specifics. Otherwise I think Rewrite should directly vacuum, and this sleepy loop thing just looks like voodoo.


    achow101 commented at 6:25 pm on October 6, 2020:
    Removed the loop
  120. S3RK commented at 7:45 am on September 20, 2020: member

    Reviewed all the code. I don’t understand the build system much, but the rest looks solid to me. The only concern that I have is regarding the behaviour when we use multiple batches over one shared connection. Besides that, there are just a few small nits.

    I’m going to build the code and test it in upcoming days.

  121. achow101 force-pushed on Sep 22, 2020
  122. in depends/Makefile:138 in 5ece9f577a outdated
    133@@ -134,7 +134,10 @@ qrencode_packages_$(NO_QR) = $(qrencode_packages)
    134 
    135 qt_packages_$(NO_QT) = $(qt_packages) $(qt_$(host_os)_packages) $(qt_$(host_arch)_$(host_os)_packages) $(qrencode_packages_)
    136 
    137-wallet_packages_$(NO_WALLET) = $(wallet_packages)
    138+bdb_packages_$(NO_BDB) = $(bdb_packages)
    139+sqlite_packages_$(NO_SQLITE) = $(sqlite_packages)
    


    Sjors commented at 10:33 am on September 22, 2020:
    NO_SQLITE and NO_BDB (hooray!) needs a mention in depends/README

    achow101 commented at 4:32 pm on October 5, 2020:
    Done
  123. S3RK commented at 1:54 pm on September 24, 2020: member

    ACK 46db9cd Code reviewed and lightly tested. I built it on macOS 10.13.6 with sqlite (3.19.3) installed from brew.

    • Created, loaded and unloaded a wallet; loaded multiple sqlite wallets
    • Verified checks for application_id and schema version (user_version)
    • Verified SQLite global log configuration; errors appear in the log file as expected
    • Integrity check gives a proper error while loading wallet from a corrupted file
    • Wallet encryption; this involves transactions, so I verified it works and no records are visible before commit
    • Backup
    • Opened wallet file with another tool and explored the contents of the database
    • Triggered wallet rewrite and vacuum, verified db size shrinks as expected
  124. fjahr commented at 2:00 pm on September 25, 2020: member
    Concept ACK and Approach ACK. From my initial shallow pass, the code looks very good already. Local build, automated tests, and some manual testing were all successful. Will keep going and dig into SQLite documentation at the same time since I am not experienced with it.
  125. jonatack commented at 7:25 pm on September 25, 2020: member
    Concept ACK. On my review shortlist.
  126. fjahr commented at 6:24 pm on September 26, 2020: member
    I have run coverage checks on the current state of this PR here (these are the total coverage number, i.e. including functional tests). The wallet/sqlite.cpp numbers are low because of many lines dedicated to error checking but also HasKey() and TxnAbort() are not covered while they are covered on the BDB class. I suppose the reason for that is that these functions are not covered by wallet tests but other tests where a BDB based wallet is used. We currently only run the wallet tests with a descriptor wallet AFAICT. Should this be changed now? Maybe there are already plans for this were I missed the discussion.
  127. achow101 commented at 7:50 pm on September 26, 2020: member

    TxnAbort isn’t covered by either SQLite or BDB, and that is expected as it is only called in an failure case.

    HasKey seems to be only used by DatabaseBatch::Exists which is currently only used by BerkeleyBatch for a backwards compatibility case that SQLite doesn’t need.

    We currently only run the wallet tests with a descriptor wallet AFAICT. Should this be changed now? Maybe there are already plans for this were I missed the discussion.

    With #18788, more tests will be enabled for descriptor wallets as well as the option to run all tests with descriptor wallets.

  128. in ci/test/00_setup_env_native_msan.sh:18 in 5ece9f577a outdated
    14@@ -15,7 +15,7 @@ export BDB_PREFIX="${BASE_ROOT_DIR}/db4"
    15 
    16 export CONTAINER_NAME="ci_native_msan"
    17 export PACKAGES="clang-9 llvm-9 cmake"
    18-export DEP_OPTS="NO_WALLET=1 NO_QT=1 CC='clang' CXX='clang++' CFLAGS='${MSAN_FLAGS}' CXXFLAGS='${MSAN_AND_LIBCXX_FLAGS}' boost_cxxflags='-std=c++11 -fvisibility=hidden -fPIC ${MSAN_AND_LIBCXX_FLAGS}' zeromq_cxxflags='-std=c++11 ${MSAN_AND_LIBCXX_FLAGS}'"
    19+export DEP_OPTS="NO_BDB=1 NO_QT=1 CC='clang' CXX='clang++' CFLAGS='${MSAN_FLAGS}' CXXFLAGS='${MSAN_AND_LIBCXX_FLAGS}' boost_cxxflags='-std=c++11 -fvisibility=hidden -fPIC ${MSAN_AND_LIBCXX_FLAGS}' zeromq_cxxflags='-std=c++11 ${MSAN_AND_LIBCXX_FLAGS}'"
    


    fjahr commented at 3:40 pm on September 27, 2020:

    5ece9f577a6e6463b15b0d88f35a7f18808936f2

    Why not keep it as NO_WALLET?


    achow101 commented at 1:54 am on September 28, 2020:
    NO_WALLET means both no BDB and no SQLite

    fjahr commented at 5:36 pm on September 28, 2020:
    My question was unclear: I meant to ask why are we adding SQLite to this environment while we are excluding BDB here?

    achow101 commented at 5:54 pm on September 28, 2020:

    This test environment is a little weird.

    The wallet is actually enabled and BDB is being used. However the BDB being used needs to be built with some special flags, so it can’t be built via depends. What this option does is disable BDB in depends so that BDB can be built separately with whatever it needs to work in this env. Because SQLite doesn’t need this special building, it can be built by depends. So we don’t want NO_WALLET because we actually still want the wallet here. We just want NO_BDB so we can deal with it later.

  129. in src/wallet/sqlite.cpp:27 in 8c7832a45f outdated
    14@@ -15,16 +15,42 @@
    15 
    16 static const char* DATABASE_FILENAME = "wallet.dat";
    17 
    18+std::atomic<int> g_dbs_open{0};
    19+
    20+static void ErrorLogCallback(void* arg, int code, const char* msg)
    21+{
    22+    assert(arg == nullptr); // That's what we tell it to do during the setup
    


    fjahr commented at 4:25 pm on September 27, 2020:

    8c7832a45f9acb088a87689573c2be47870b5c8a

    It feels strange to me that the log callback makes everything blow up in a special case. I think we use asserts for developer errors but I don’t think that’s the case here. Also doesn’t this mean that the throw std::runtime_error on L36 will never be used?


    achow101 commented at 1:59 am on September 28, 2020:

    It feels strange to me that the log callback makes everything blow up in a special case. I think we use asserts for developer errors but I don’t think that’s the case here.

    This would only be hit in a developer error or a sqlite bug.

    Also doesn’t this mean that the throw std::runtime_error on L36 will never be used?

    I don’t think so?


    fjahr commented at 5:36 pm on September 28, 2020:

    Hm, the docs aren’t very clear on this (or I can’t find the right place) so I can not say yet if there are cases where ret == SQLITE_OK but the log callback is still being called. That would be the only case when this seems valuable because otherwise the runtime_error would be hit in case there is a problem and the error message would be clearer I think? Yes, this could be a bug in SQLite but in general, we are trusting that the ret/res codes are not lying so I don’t know why we would do it differently here.

    On the flip side, the runtime_error will currently only be hit if ret != SQLITE_OK but the ErrorLogCallback is not being called. Again, I couldn’t find details/guarantees on this in the docs but at least they say the callback will be used ‘whenever anomalies occur’, so my expectation is that it would always be called as whenever ret != SQLITE_OK is true. So I think the runtime_error may never be hit unless there is a bug in SQLite.

    Overall it just feels strange to do anything but logging in a function that is called *LogCallback.


    achow101 commented at 5:59 pm on September 28, 2020:
    I think if ret != SQLITE_OK both ErrorLogCallback and runtime_error get called. The ErrorLogCallback doesn’t change ret to SQLITE_OK and it doesn’t throw it’s own exception. The assertion should never fail so that won’t cause a program exit either.

    ryanofsky commented at 5:56 pm on October 1, 2020:

    re: #19077 (review)

    Agree with achow that this is an appropriate place to assert. We’re passing along a context value, and asserting we are passed back the same value. We’re not checking for a runtime error, just documenting an assumption about how the code should is supposed to work, and adding a sanity check to detect if the assumption is wrong.


    Sjors commented at 11:10 am on October 5, 2020:

    Perhaps citing from the sqlite3 manual in the comment is more clear:

    0// From sqlite3_config() documentation for the SQLITE_CONFIG_LOG option: 
    1// "The void pointer that is the second argument to SQLITE_CONFIG_LOG is passed through as
    2// the first parameter to the application-defined logger function whenever that function is 
    3// invoked."
    4// Assert that this is the case:
    

    achow101 commented at 4:33 pm on October 5, 2020:
    Added this comment.
  130. in src/wallet/sqlite.cpp:234 in cf3a2373b0 outdated
    57+void SQLiteDatabase::Open(const char* mode)
    58 {
    59+    bool read_only = (!strchr(mode, '+') && !strchr(mode, 'w'));
    60+
    61+    bool create = strchr(mode, 'c') != nullptr;
    62+    int flags;
    


    fjahr commented at 4:41 pm on September 27, 2020:

    cf3a2373b0aa7a20ff15367730161ac18f3f302b

    nit

    0    const bool read_only = (!strchr(mode, '+') && !strchr(mode, 'w'));
    1    const bool create = strchr(mode, 'c') != nullptr;
    2    int flags;
    

    achow101 commented at 4:24 pm on September 30, 2020:
    Done
  131. in src/wallet/sqlite.cpp:291 in e4d88e26a5 outdated
    288 
    289 bool SQLiteBatch::WriteKey(CDataStream&& key, CDataStream&& value, bool overwrite)
    290 {
    291-    return false;
    292+    if (!m_database.m_db) return false;
    293+    if (m_read_only) assert(!"Write called on database in read-only mode");
    


    fjahr commented at 6:47 pm on September 27, 2020:

    e4d88e26a5da6f119b6a0124fb18ee9909e486a4

    Maybe use a runtime_error here instead of an assert? Same in EraseKey.


    achow101 commented at 4:24 pm on September 30, 2020:
    Done
  132. in src/wallet/sqlite.h:103 in 01b3cb0268 outdated
     98@@ -97,6 +99,12 @@ class SQLiteDatabase : public WalletDatabase
     99     std::unique_ptr<DatabaseBatch> MakeBatch(const char* mode = "r+", bool flush_on_close = true) override;
    100 
    101     sqlite3* m_db{nullptr};
    102+
    103+    sqlite3_stmt* m_read_stmt = nullptr;
    


    promag commented at 8:21 pm on September 27, 2020:

    01b3cb026806784ae20eb670bd9aa40c43a94e54

    nit, use initializer {nullptr}?


    achow101 commented at 4:24 pm on September 30, 2020:
    Done
  133. in src/wallet/walletdb.cpp:1049 in 94d5ee3067 outdated
    1042+        error = Untranslated(strprintf("Failed to load database path '%s'. Data is not in required format.", path.string()));
    1043+        status = DatabaseStatus::FAILED_BAD_FORMAT;
    1044+        return nullptr;
    1045+    }
    1046+
    1047+    if (!format && options.require_format) format = options.require_format;
    


    fjahr commented at 8:27 pm on September 27, 2020:

    94d5ee30677b2282e87f79a6f945bceff41faaad

    This is the case when neither DB exists and a new one is created, right? Would be worth a comment here I think.


    achow101 commented at 4:24 pm on September 30, 2020:
    Done
  134. in src/wallet/wallet.cpp:251 in 81d9ce910f outdated
    247+std::shared_ptr<CWallet> CreateWallet(interfaces::Chain& chain, const std::string& name, Optional<bool> load_on_start, DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings)
    248 {
    249     uint64_t wallet_creation_flags = options.create_flags;
    250     const SecureString& passphrase = options.create_passphrase;
    251 
    252+    if (wallet_creation_flags & WALLET_FLAG_DESCRIPTORS) options.require_format = DatabaseFormat::SQLITE;
    


    fjahr commented at 8:38 pm on September 27, 2020:

    81d9ce910f61aed16c7298150775b973102db498

    Maybe check and error if a different format was passed?


    achow101 commented at 4:07 pm on September 30, 2020:
    I don’t think that is necessary.
  135. promag commented at 8:45 pm on September 27, 2020: member

    Could add a dedicated statement for HasKey, see https://stackoverflow.com/a/9756276/1978589. Are you going to add any index to main table?

    BTW, could also have RAII for prepared statements.

    It’s not clear to me how concurrent accesses are handled, how sqlite guarantees a concurrent thread B waits until after thread A calls sqlite3_reset.

  136. fjahr commented at 9:46 pm on September 27, 2020: member

    Light code review ACK 46db9cdb6c706ee851386fb4afbac0bf2dbd1013

    I am still not an SQLite expert but at least I have sanity checked all the statements and dug a bit deeper on a few of them. I left a few comments but I don’t consider any of them blocking. Will do some further manual testing as well soon.

    There are no changes to the docs so far. Do you plan to do these in a follow-up? It might be good to work on this in parallel or add the most important bits here since when this gets merged some people might run into build issues while the doc changes are being bike-shedded.

  137. achow101 commented at 1:59 am on September 28, 2020: member

    It’s not clear to me how concurrent accesses are handled, how sqlite guarantees a concurrent thread B waits until after thread A calls sqlite3_reset.

    Have you read https://sqlite.org/lockingv3.html?

  138. achow101 commented at 4:21 pm on September 28, 2020: member

    I left a few comments but I don’t consider any of them blocking.

    Great! I’ll leave them until something blocking comes up.

    There are no changes to the docs so far. Do you plan to do these in a follow-up?

    Oh yeah, documentation… I’ll do them in a follow up.

  139. ryanofsky referenced this in commit c072c8585d on Sep 29, 2020
  140. ryanofsky referenced this in commit 5338dfaef0 on Sep 29, 2020
  141. DrahtBot added the label Needs rebase on Sep 30, 2020
  142. achow101 force-pushed on Sep 30, 2020
  143. achow101 commented at 4:25 pm on September 30, 2020: member
    Rebased, so addressed comments. Also added some basic documentation to the build docs.
  144. DrahtBot removed the label Needs rebase on Sep 30, 2020
  145. in build_msvc/vcpkg.json:18 in 443d029fb8 outdated
    13@@ -14,6 +14,7 @@
    14       "name": "libevent",
    15       "features": ["thread"]
    16     },
    17-    "zeromq"
    18+    "zeromq",
    19+    "sqlite3"
    


    ryanofsky commented at 10:02 pm on September 30, 2020:

    In commit “Add sqlite to travis and depends” (443d029fb859262c56b8be32205a2b4540dbb843)

    Minor: Would be nice to preserve alphabetical order and avoid need to change unrelated lines


    achow101 commented at 9:50 pm on October 1, 2020:
    Done
  146. in depends/packages/packages.mk:14 in 443d029fb8 outdated
     9@@ -10,7 +10,8 @@ qt_android_packages=qt
    10 qt_darwin_packages=qt
    11 qt_mingw32_packages=qt
    12 
    13-wallet_packages=bdb
    14+bdb_packages=bdb
    15+sqlite_packages = sqlite
    


    ryanofsky commented at 10:04 pm on September 30, 2020:

    In commit “Add sqlite to travis and depends” (443d029fb859262c56b8be32205a2b4540dbb843)

    Minor: Spacing on this line is inconsistent with the rest of the file


    achow101 commented at 9:50 pm on October 1, 2020:
    Done
  147. in src/wallet/sqlite.cpp:20 in f35f89f1ae outdated
     9+#include <util/translation.h>
    10+#include <wallet/db.h>
    11+
    12+#include <stdint.h>
    13+
    14+static const char* DATABASE_FILENAME = "wallet.dat";
    


    ryanofsky commented at 10:11 pm on September 30, 2020:

    In commit “Add SQLiteDatabase and SQLiteBatch dummy classes” (f35f89f1ae53914f6183ec3d7da6a1d0c27bed16)

    See main comment for reasoning, but this is awkward to change later so I think the commit introducing this should start with the appropriate final value (hopefully “wallet.sqlite” or similar)

  148. in src/wallet/sqlite.cpp:32 in 13cca0d881 outdated
    27     WalletDatabase(), m_mock(mock), m_dir_path(dir_path.string()), m_file_path(file_path.string())
    28 {
    29     LogPrintf("Using SQLite Version %s\n", SQLiteDatabaseVersion());
    30     LogPrintf("Using wallet %s\n", m_dir_path);
    31+
    32+    if (g_dbs_open.fetch_add(1) == 0) {
    


    ryanofsky commented at 10:21 pm on September 30, 2020:

    In commit “Initialize and Shutdown sqlite3 globals” (13cca0d881acc81dbf4a6496bb02d4fefc905a77)

    There’s still a race condition here if two databases are opened at the same time. sqlite3_config will only be called once for both databases, but only one of the databases will actually block waiting for the call to complete, so the other database open will most likely fail. Similar races can happen on close.

    Instead of trying to be clever and lock-free, it would be better to be dumb and mutex-full.

    0static Mutex g_sqlite_mutex;
    1static int g_sqlite_count GUARDED_BY(g_sqlite_mutex) = 0;
    
    0LOCK(g_sqlite_mutex);
    1if (++g_sqlite_count == 1) {
    2   sqlite_config...
    3}
    
    0LOCK(g_sqlite_mutex);
    1if (--g_sqlite_count == 0) {
    2   sqlite_shutdown...
    3}
    

    achow101 commented at 9:50 pm on October 1, 2020:
    Done
  149. ryanofsky referenced this in commit c1585bca8d on Oct 1, 2020
  150. in src/wallet/sqlite.cpp:233 in ecc06767e1 outdated
    52@@ -53,8 +53,74 @@ SQLiteDatabase::~SQLiteDatabase()
    53     }
    54 }
    55 
    56-void SQLiteDatabase::Open(const char* pszMode)
    57+void SQLiteDatabase::Open(const char* mode)
    58 {
    59+    const bool read_only = (!strchr(mode, '+') && !strchr(mode, 'w'));
    60+
    61+    const bool create = strchr(mode, 'c') != nullptr;
    


    ryanofsky commented at 6:22 pm on October 1, 2020:

    In commit “Implement SQLiteDatabase::Open” (ecc06767e1a91b11179ffb5b70808b69a9c6b552)

    Note for future improvement: We should probaby stop this non-standard use of flags. “w” or “a” normally creates a new file not “c”. https://www.gnu.org/software/libc/manual/html_node/Opening-Streams.html. It might also be better to switch away from modes strings to readonly / require_new / require_existing boolean options.


    achow101 commented at 8:03 pm on October 1, 2020:
    I agree that flags would probably be better. Also, I think there’s an argument for dropping read_only considering it is only used by BerkeleyDatagbase::Rewrite.

    S3RK commented at 7:49 am on October 3, 2020:
    I did an implementation based on this PR to move from modes string to bool flags; and removed read-only mode as well. I’ll open a PR once this one will be merged.

    ryanofsky commented at 1:09 pm on October 6, 2020:

    re: #19077 (review)

    I did an implementation based on this PR to move from modes string to bool flags; and removed read-only mode as well. I’ll open a PR once this one will be merged.

    Since these changes would only simplify this PR, and the more complicated parts don’t involve sqlite, I think you could open a separate PR based on master just omitting the sqlite parts. Also, the change can go further and drop the batch create option, just using the DatabaseOptions::require_existing option instead (storing it if needed).

  151. in src/wallet/sqlite.cpp:86 in ecc06767e1 outdated
    82+
    83+        if (create) {
    84+            bool table_exists;
    85+            // Check that the main table exists
    86+            sqlite3_stmt* check_main_stmt;
    87+            std::string check_main = "SELECT name FROM sqlite_master WHERE type='table' AND name='main'";
    


    ryanofsky commented at 6:27 pm on October 1, 2020:

    In commit “Implement SQLiteDatabase::Open” (ecc06767e1a91b11179ffb5b70808b69a9c6b552)

    Could avoid the need for this preamble using CREATE TABLE IF NOT EXISTS syntax: https://www.sqlite.org/draft/lang_createtable.html


    achow101 commented at 9:50 pm on October 1, 2020:
    Done

    achow101 commented at 3:06 pm on October 8, 2020:
    Had to revert back to this as it is necessary to know whether we are creating the wallet for other stuff.
  152. in src/wallet/sqlite.cpp:121 in ecc06767e1 outdated
    117+                throw std::runtime_error(strprintf("SQLiteDatabase: Failed to enable fullfsync: %s\n", sqlite3_errstr(ret)));
    118+            }
    119+        }
    120+
    121+        m_db = db;
    122+    } else if (!read_only && sqlite3_db_readonly(m_db, "main") != 0) {
    


    ryanofsky commented at 6:32 pm on October 1, 2020:

    In commit “Implement SQLiteDatabase::Open” (ecc06767e1a91b11179ffb5b70808b69a9c6b552)

    I think you need to do this check even in the (m_db == nullptr) case above in case the file or filesystem is read-only. According to https://www.oreilly.com/library/view/using-sqlite/9781449394592/re303.html SQLITE_OPEN_READWRITE will “Attempt to open the file read/write. If this is not possible, open the file read-only. Opening the file read-only will not result in an error.”


    achow101 commented at 9:51 pm on October 1, 2020:
    Done
  153. in src/wallet/sqlite.cpp:66 in af798a8fd9 outdated
    42@@ -42,6 +43,18 @@ SQLiteDatabase::SQLiteDatabase(const fs::path& dir_path, const fs::path& file_pa
    43     }
    44 }
    45 
    46+bool SQLiteDatabase::PrepareDirectory() const
    47+{
    48+    if (m_mock) return true;
    49+    // Try to create the directory containing the wallet file and lock it
    50+    TryCreateDirectories(m_dir_path);
    51+    if (!LockDirectory(m_dir_path, ".walletlock")) {
    


    ryanofsky commented at 7:02 pm on October 1, 2020:

    In commit “sqlitedb: Create and lock the wallet directory” (af798a8fd9b3b1a1e0a63a665f6d89c73e8614a3)

    This doesn’t seem like the most ideal approach. We were forced to use .walletlock in #11904 because the old version of BDB we use doesn’t support set_lk_exclusive. But with sqlite it would seem simpler and safer to use its exclusive locking functionality: https://www.sqlite.org/pragma.html#pragma_locking_mode instead of rolling our own which is probably less portable and reliable, requires code duplication, and is nonstandard not working with other sqlite database tools.


    achow101 commented at 8:07 pm on October 1, 2020:
    Since only an EXCLUSIVE lock is acquired after the first write, I’m not sure that the locking pragma provides strong enough guarantees for us. Another bitcoind could conceivably open the same wallet while only the SHARED lock is held.

    ryanofsky commented at 2:37 am on October 6, 2020:

    In commit “sqlitedb: Create and lock the wallet directory” (4f3e5569c3c0eb83d50f7f88e9b673fa857fa08a)

    re: #19077 (review)

    Since only an EXCLUSIVE lock is acquired after the first write, I’m not sure that the locking pragma provides strong enough guarantees for us. Another bitcoind could conceivably open the same wallet while only the SHARED lock is held.

    No bitcoind should open the wallet in shared mode. The simplest approach is for every bitcoind to open the wallet in exclusive mode and fail if exclusive lock cannot be acquired

    I did write another suggestion about opening the database earlier to simplify code and avoid closing, unlocking, and reopening the database after verifying, but this is orthogonal to type of locking used.


    achow101 commented at 4:09 pm on October 6, 2020:

    From https://sqlite.org/pragma.html#pragma_locking_mode

    The first time the database is read in EXCLUSIVE mode, a shared lock is obtained and held. The first time the database is written, an exclusive lock is obtained and held.

    So opening in exclusive mode means that there is a potential case where bitcoind has not yet written anything and thus only has a shared lock. In that case, another bitcoind could open the same file (and not write anything) and also acquire a shared lock. See https://sqlite.org/lockingv3.html for the definitions of shared and exclusive locks in sqlite3.


    ryanofsky commented at 3:24 am on October 7, 2020:

    In commit “sqlitedb: Create and lock the wallet directory” (a6415a4dc752db171bd842fb376c5c5a919c04e5)

    re: #19077 (review)

    From sqlite.org/pragma.html#pragma_locking_mode

    Oh, you are absolutely right. Just setting the pragma doesn’t get the lock, you have to do an empty transaction to acquire it. This just means you need 3 statements to open the database: pragma locking_mode=exclusive; begin exclusive transaction; commit; instead of 1 statement.

    Using the sqlite exclusive lock would be more reliable than .walletlock and simpler because it would be a part of database opening, not in some other code path separate from opening, and because it would no longer requiring extra code removing .walletlock at shutdown (this code already had a bug previously in this PR).

    In case it helps, I found it pretty straightforward to test locking from the command line:

    • Terminal 1

      0$ sqlite3 db.sqlite
      1sqlite> pragma locking_mode=exclusive;
      2exclusive
      3sqlite> begin exclusive transaction;
      4sqlite> commit;
      5sqlite> 
      
    • Terminal 2

      0$ sqlite3 db.sqlite
      1sqlite> pragma locking_mode=exclusive;
      2exclusive
      3sqlite> begin exclusive transaction;
      4Error: database is locked
      5sqlite> 
      

    achow101 commented at 4:39 pm on October 7, 2020:

    This just means you need 3 statements to open the database:

    Then there’s the potential for race conditions in 2 instances: before the pragma is executed, and after it is executed but before the transaction begins.

    Edit: Oh I guess we just exit with the database is in use error when begin transaction fails.


    ryanofsky commented at 4:49 pm on October 7, 2020:

    re: #19077 (review)

    Then there’s the potential for race conditions in 2 instances: before the pragma is executed, and after it is executed but before the transaction begins.

    There’s no race condition here:

    0db = sqlite_open(path); // Or error "sorry, couldn't open database"
    1sqlite_exec(db, "pragma locking_mode=exclusive"); // Or error "sorry, couldn't set locking mode"
    2sqlite_exec(db, "begin exclusive transaction"); // Or error "sorry, database is currently in use"
    3sqlite_exec(db, "commit"); // Or error "sorry, unexpected failure"
    

    There would be a race condition if the exclusive lock was released after the commit statement. But the point of the pragma is to extend the exclusive lock from point where it is first acquired until the point where the database is closed.


    achow101 commented at 7:07 pm on October 7, 2020:
    Done
  154. ryanofsky referenced this in commit 343b3e35e7 on Oct 1, 2020
  155. ryanofsky referenced this in commit 6427c68fce on Oct 1, 2020
  156. ryanofsky commented at 7:38 pm on October 1, 2020: member

    Started review (will update list below with progress).

    • 10124c3d6c5176d5df94964e530a4b0c6edd8381 Add libsqlite3 (1/27)
    • 443d029fb859262c56b8be32205a2b4540dbb843 Add sqlite to travis and depends (2/27)
    • f35f89f1ae53914f6183ec3d7da6a1d0c27bed16 Add SQLiteDatabase and SQLiteBatch dummy classes (3/27)
    • 00bf8b2bcd74b85013b7155fef0f8f8438d31ecd Implement SQLiteDatabaseVersion (4/27)
    • 55d1e2b3b443e3794deb8f20bde0072153f98525 Constructors, destructors, and relevant private fields for SQLiteDatabase/Batch (5/27)
    • 13cca0d881acc81dbf4a6496bb02d4fefc905a77 Initialize and Shutdown sqlite3 globals (6/27)
    • ecc06767e1a91b11179ffb5b70808b69a9c6b552 Implement SQLiteDatabase::Open (7/27)
    • 19033c4e9d9c150db0fba6841fa94ee78d994f31 Implement SQLiteDatabase::Close (8/27)
    • 429c8e18c2f66589bb75ef5c3827c2e7fd9a354b Implement SQLiteBatch::Close (9/27)
    • af798a8fd9b3b1a1e0a63a665f6d89c73e8614a3 sqlitedb: Create and lock the wallet directory (10/27)
    • 3ce06b5d6824e14e6cc270c6161dc41408f23eb8 Implement SQLiteDatabase::RemoveRef and AddRef (11/27)
    • e6ecebe03a62987ee70b17044c232b21afc6dc05 Add SetupSQLStatements (12/27)
    • 57b2da3bcd0bc64c3452b1208de6fca62d86ce31 Implement SQLiteBatch::ReadKey, WriteKey, EraseKey, and HasKey (13/27)
    • 009ef9f25717ca332be9adca9f2d4035c922388f Implement SQLiteBatch::StartCursor, ReadAtCursor, and CloseCursor (14/27)
    • d0cbf04178c47a4acce2028e6df3cfc95bd05d33 Implement SQLiteDatabase::Backup (15/27)
    • c3d1f091c283287f7fe495a38069023c3d8fd386 Implement SQLiteDatabase::TxnBegin, TxnCommit, and TxnAbort (16/27)
    • 91730109505aec8698e0ecb26a056583e2376ec1 Implement SQLiteDatabase::Flush, PeriodicFlush, and ReloadDbEnv as No-ops (17/27)
    • fae19b88936d4183508047d133d8ce4b11f537fc Implement SQLiteDatabase::Rewrite (18/27)
    • 41c87fa836036a3b9c030dc97bd1ce3dc34fe4e0 Implement SQLiteDatabase::Verify (19/27)
    • 99b48f327a1b4a35289b6aec37e312adb2244e9c Implement SQLiteDatabase::MakeBatch (20/27)
    • 45ba0aa7b8e0408ca479c6bf29b27d3d8f1148ef Determine wallet file type based on file magic (21/27)
    • cb086c05b5e400aeffc0e94c52ac807df1445ef9 walletutil: Wallets can also be sqlite (22/27)
    • f9d3ec85af95422fd47b3e87964aff6309a47b80 Use SQLite for descriptor wallets (23/27)
    • 5d26f1f96f60cf18252b507946d47f588db4db81 Use network magic as sqlite wallet application ID (24/27)
    • b5dbcd1ecbf10a24ae5e56e169ded9bc9b113672 Set and check the sqlite user version (25/27)
    • 2596696de3c43b7017784b2b40650056566dbb8a wallet: Enforce sqlite serialized threading mode (26/27)
    • c76a25c125214cf69ddcd5493c9457195a9d768f Include sqlite3 in documentation (27/27)

    Main two pieces of feedback are:

    • I think this change should be merged with more tests enabled. Particularly wallet_backup.py and wallet_multiwallet.py tests would be good to have running, but probably others would be good as well. Doing this is pretty straightforward after #20034, and I took a shot at in a branch: https://github.com/bitcoin/bitcoin/compare/master...ryanofsky:pr/sql (branch)
    • I think this change should start off on the right foot using sqlite naming and locking conventions. We don’t disguise sqlite journal or WAL log files as BDB log files, and I don’t think there’s a reason to disguise the new data file with the old name. Using “wallet.sqlite” clearly identifies file format and wallet directory type to provide transparency and help debugging, and should avoid tools that aren’t equipped to access these files from trying to access them
  157. achow101 commented at 7:54 pm on October 1, 2020: member

    I think this change should be merged with more tests enabled. Particularly wallet_backup.py and wallet_multiwallet.py tests would be good to have running, but probably others would be good as well. Doing this is pretty straightforward after #20034, and I took a shot at in a branch: master…ryanofsky:pr/sql (branch)

    I think #18788 is needed to enable those tests.

    I think this change should start off on the right foot using sqlite naming and locking conventions. We don’t disguise sqlite journal or WAL log files as BDB log files, and I don’t think there’s a reason to disguise the new data file with the old name. Using “wallet.sqlite” clearly identifies file format and wallet directory type to provide transparency and help debugging, and should avoid tools that aren’t equipped to access these files from trying to access them

    I have two main arguments to retain the wallet.dat naming:

    1. Existing external backup tooling, documentation, and other information refer to the wallet file as wallet.dat. In the vast majority of those cases, the exact file format is irrelevant so they will still be applicable to a sqlite wallet.dat. In the cases where the file format matters (only pywallet AFAIK), the file format is checked so the tool will error if it isn’t correct.
    2. If the user has specified a sqlite wallet in their conf file, downgrading may result in a new bdb wallet.dat file being created in that directory. This can be confusing the user (they “lost” all of their money) and potentially issues with a subsequent upgrade to a version with sqlite (there would be both wallet.dat and wallet.sqlite in the same dir). I think it would be better to explicitly error (and a sqlite wallet.dat would cause an error for old software) rather than fail silently.
  158. achow101 force-pushed on Oct 1, 2020
  159. in depends/packages/sqlite.mk:2 in 61b650b6f7 outdated
    0@@ -0,0 +1,26 @@
    1+package=sqlite
    2+$(package)_version=3320100
    


    Sjors commented at 10:50 am on October 2, 2020:
    Maybe we can bump from 3.32.1 to 3.33.0 in a followup, but before the release.

    achow101 commented at 4:26 pm on October 5, 2020:
    sqlite has a fairly aggressive release cycle, so I think we can leave this for later.
  160. MarcoFalke referenced this in commit 171cd05ae3 on Oct 2, 2020
  161. sidhujag referenced this in commit d9630d6f44 on Oct 4, 2020
  162. in src/wallet/sqlite.cpp:407 in a09ce35e30 outdated
    70@@ -67,6 +71,14 @@ std::unique_ptr<DatabaseBatch> SQLiteDatabase::MakeBatch(const char* mode, bool
    71     return nullptr;
    72 }
    73 
    74+SQLiteBatch::SQLiteBatch(SQLiteDatabase& database, const char* mode)
    75+    : m_database(database)
    76+{
    77+    m_read_only = (!strchr(mode, '+') && !strchr(mode, 'w'));
    


    Sjors commented at 10:44 am on October 5, 2020:
    a09ce35e302f1da3b13e16b4837a6e4a1b3de6bb: using an enum to handle modes would seem more readable, especially because these mode strings are a BDB artefact. See also https://github.com/bitcoin/bitcoin/pull/19077/commits/0344ccea4dcc0692e736dd759fc3945272a3266c#r498435139

    achow101 commented at 4:17 pm on October 5, 2020:
    Yes, this should be done as a followup.
  163. in src/wallet/sqlite.cpp:254 in 0344ccea4d outdated
    84+        }
    85+        // TODO: Maybe(?) Check the file wasn't copied and a duplicate opened
    86+
    87+        if (create) {
    88+            // Make the table for our key-value pairs
    89+            std::string create_stmt = "CREATE TABLE IF NOT EXISTS main(key BLOB PRIMARY KEY, value BLOB)";
    


    Sjors commented at 11:36 am on October 5, 2020:
    0344ccea4dcc0692e736dd759fc3945272a3266c let’s drop IF NOT EXISTS. If create is only set for new database then no table should exist. Alternatively, if create is meant as “create if needed”, then it’s seems safer to explicitly check if the table doesn’t exist yet. In that case the error “Failed to create new database” is incorrect.

    Sjors commented at 2:31 pm on October 5, 2020:

    Just noticed @ryanofsky’s comment: #19077 (review)

    I think it’s safer to explicitly check if the table is present. In general it’s nice to have a clearly separate code path for stuff we only do on wallet creation (and upgrade).


    ryanofsky commented at 2:54 pm on October 5, 2020:

    re: #19077 (review)

    Just noticed @ryanofsky’s comment: #19077 (comment)

    I think it’s safer to explicitly check if the table is present. In general it’s nice to have a clearly separate code path for stuff we only do on wallet creation (and upgrade).

    Dropping ‘if not exists’ and adding an error sounds good (though it might be helpful to say what is unsafe or what dangerous cases you are thinking of). I only suggested using ‘if not exists’ SQL syntax to avoid reimplementing it in C++.


    achow101 commented at 4:10 pm on October 5, 2020:

    Alternatively, if create is meant as “create if needed”, then it’s seems safer to explicitly check if the table doesn’t exist yet. In that case the error “Failed to create new database” is incorrect.

    It is use as a “create if needed.” All wallet DBs are created with the mode “cr+”. I don’t think we should be giving an error if the table doesn’t exist in that case either.


    ryanofsky commented at 1:58 am on October 6, 2020:

    re: #19077 (review)

    It is use as a “create if needed.” All wallet DBs are created with the mode “cr+”. I don’t think we should be giving an error if the table doesn’t exist in that case either.

    It would require changes outside sqlite.cpp/sqlite.h but it should be possible to throw an error instead of defensively creating tables if the database already already exists and the tables are missing. I think it would be a little safer and probably preferable, but not critical.

  164. in src/wallet/sqlite.cpp:266 in 0344ccea4d outdated
    90+            ret = sqlite3_exec(db, create_stmt.c_str(), nullptr, nullptr, nullptr);
    91+            if (ret != SQLITE_OK) {
    92+                throw std::runtime_error(strprintf("SQLiteDatabase: Failed to create new database: %s\n", sqlite3_errstr(ret)));
    93+            }
    94+
    95+            // Enable fullfysnc for the platforms that use it
    


    Sjors commented at 11:44 am on October 5, 2020:

    0344cce: is PRAGMA fullfsync ephemeral? Documentation isn’t very clear about that. If so, it shouldn’t be under create. On macOS 10.15.7 with sqlite 3.28.0 when I call PRAGMA fullfsync; it returns 0.

    Update: it’s fine, create is confusingly named, but this is code is run every time you load a wallet


    achow101 commented at 4:12 pm on October 5, 2020:
    AFAICT it is persistent.
  165. in src/wallet/sqlite.cpp:329 in 0344ccea4d outdated
    101+        }
    102+
    103+        m_db = db;
    104+    }
    105+    if (!read_only && sqlite3_db_readonly(m_db, "main") != 0) {
    106+        throw std::runtime_error(strprintf("SQLiteDatabase: SQLiteBatch requested read-write permission but database only has readonly"));
    


    Sjors commented at 12:09 pm on October 5, 2020:
    0344cce: you may want to add this check early in the create block too. E.g. if I make an existing wallet read-only, it will throw a confusing “Failed to set the application id” (its first attempt to write).

    achow101 commented at 4:32 pm on October 5, 2020:
    Done
  166. Sjors commented at 12:11 pm on October 5, 2020: member

    Reviewed the first couple of commits (to bb2406c9029ad66833f9d2fc62335872204ee94c)

    Lightly tested by creating a Signet descriptor wallet and sending some coins back and forth.

    macOS 10.15.7 seems to come with sqlite3 version 3.28.0 bundled. When installing sqlite3 via homebrew, you’ll get the latest 3.33.0 but it’s keg-only. This causes pkg-config to pick up version 3.28, unless you do:

    0PKG_CONFIG_PATH="/usr/local/opt/sqlite/lib/pkgconfig" pkg-config --modversion sqlite3
    

    This isn’t a big deal imo, but longer term having a --with-sqlite3 config option would be nice. We should also decide and enforce a minimum version.

    I was unable to test with depends due to #19411

  167. achow101 force-pushed on Oct 5, 2020
  168. in depends/README.md:104 in 8bfbad00d4 outdated
     98@@ -99,6 +99,10 @@ The following can be set when running make: `make FOO=bar`
     99 <dd>Don't download/build/cache packages needed for enabling zeromq</dd>
    100 <dt>NO_WALLET</dt>
    101 <dd>Don't download/build/cache libs needed to enable the wallet</dd>
    102+<dt>NO_BDB</dt>
    103+<dd>Don't download/build/cache BerkeleyDB</dd>
    104+<dt>NO_SQLITET</dt>
    


    ryanofsky commented at 11:51 pm on October 5, 2020:

    In commit “Add sqlite to travis and depends” (8bfbad00d4bd94f2b9006e6375ea7dc11adee6fb)

    s/NO_SQLITET/NO_SQLITE


    achow101 commented at 6:19 pm on October 6, 2020:
    Fixed
  169. in src/wallet/sqlite.h:65 in b6eeb2b5ee outdated
    60+    void Open(const char* mode) override;
    61+
    62+    /** Close the database */
    63+    void Close() override;
    64+
    65+    /** Indicate the a new database user has began using the database. Increments m_refcount */
    


    ryanofsky commented at 0:18 am on October 6, 2020:

    In commit “Add SQLiteDatabase and SQLiteBatch dummy classes” (b6eeb2b5ee6964a9034aca6f4be2b72d532589ca)

    I think the refcounting code in this PR is confusing and should be removed. Rewrite method can just vacuum without sleeping. Close method can unconditionally close m_db it’s not null. m_refcount doesn’t ever need be referenced, AddRef and RemoveRef don’t ever need to be called, and they be implemented in a transparent way to make it clear they are unused holdovers:

    0void AddRef() override { assert(0); /* unused */ }
    1void RemoveRef() override { assert(0); /* unused */ }
    

    achow101 commented at 6:19 pm on October 6, 2020:
    Done

    ryanofsky commented at 1:27 am on October 7, 2020:

    In commit “Add SQLiteDatabase and SQLiteBatch dummy classes” (f747380289b06ba1631e1cec30b96b5aecb61f36)

    Comment “Indicate the a new database user has began using the database. Increments m_refcount” isn’t accurate

  170. in src/wallet/sqlite.cpp:78 in 46aa20b62e outdated
    70@@ -67,6 +71,14 @@ std::unique_ptr<DatabaseBatch> SQLiteDatabase::MakeBatch(const char* mode, bool
    71     return nullptr;
    72 }
    73 
    74+SQLiteBatch::SQLiteBatch(SQLiteDatabase& database, const char* mode)
    75+    : m_database(database)
    76+{
    77+    m_read_only = (!strchr(mode, '+') && !strchr(mode, 'w'));
    78+    m_database.AddRef();
    


    ryanofsky commented at 0:20 am on October 6, 2020:

    In commit “Constructors, destructors, and relevant private fields for SQLiteDatabase/Batch” (46aa20b62e77b33e62e1e17ac6f6a6eb1a9efdce)

    Can drop reference counting (see dummy classes comment)


    achow101 commented at 6:19 pm on October 6, 2020:
    Done
  171. in src/wallet/sqlite.cpp:180 in fbf44184de outdated
    175@@ -176,6 +176,10 @@ void SQLiteBatch::Flush()
    176 
    177 void SQLiteBatch::Close()
    178 {
    179+    if (m_database.m_db && sqlite3_get_autocommit(m_database.m_db) == 0) {
    180+        TxnAbort();
    


    ryanofsky commented at 0:42 am on October 6, 2020:

    In commit “Implement SQLiteBatch::Close” (fbf44184de758271e759978cd5037efcb4ba4eb1)

    This should log an error if TxnAbort() fails. It might also be useful to log an error if TxnAbort succeeds, since this condition should never happen unless there is unpaired TxnBegin call or an unhandled exception.


    achow101 commented at 6:19 pm on October 6, 2020:
    Done
  172. in src/wallet/sqlite.cpp:182 in fbf44184de outdated
    175@@ -176,6 +176,10 @@ void SQLiteBatch::Flush()
    176 
    177 void SQLiteBatch::Close()
    178 {
    179+    if (m_database.m_db && sqlite3_get_autocommit(m_database.m_db) == 0) {
    180+        TxnAbort();
    181+    }
    182+    m_database.RemoveRef();
    


    ryanofsky commented at 0:44 am on October 6, 2020:

    In commit “Implement SQLiteBatch::Close” (fbf44184de758271e759978cd5037efcb4ba4eb1)

    Can drop reference counting (see dummy classes comment)


    achow101 commented at 6:19 pm on October 6, 2020:
    Done
  173. in src/wallet/sqlite.cpp:330 in 46aa20b62e outdated
    70@@ -67,6 +71,14 @@ std::unique_ptr<DatabaseBatch> SQLiteDatabase::MakeBatch(const char* mode, bool
    71     return nullptr;
    72 }
    73 
    74+SQLiteBatch::SQLiteBatch(SQLiteDatabase& database, const char* mode)
    75+    : m_database(database)
    76+{
    77+    m_read_only = (!strchr(mode, '+') && !strchr(mode, 'w'));
    78+    m_database.AddRef();
    79+    m_database.Open(mode);
    


    ryanofsky commented at 1:50 am on October 6, 2020:

    In commit “Constructors, destructors, and relevant private fields for SQLiteDatabase/Batch” (46aa20b62e77b33e62e1e17ac6f6a6eb1a9efdce)

    I don’t think it makes sense to open the database in the batch constructor instead of in the database constructor. It makes the batch constructor assymetric (there’s no database close in the destructor), forces database to be needlessly opened, closed, and reopened when verification is enabled, delays error checking done on open (making debugging and diagnosis more difficult), and prevents taking full advantages of sqlite’s exclusive locking to prevent the database from being opened in different processes.


    achow101 commented at 4:04 pm on October 6, 2020:

    The purpose of opening the database within a batch is to only open it when the database is going to be used. It is then left open to avoid constantly closing and reopening it.

    We also do it to set the mode on the database, but the usefulness of that is arguable but we can deal with that in a followup.


    ryanofsky commented at 1:43 am on October 7, 2020:

    re: #19077 (review)

    The purpose of opening the database within a batch is to only open it when the database is going to be used. It is then left open to avoid constantly closing and reopening it.

    It sounds like my suggestion was not clear. My suggestion is to open the database when the database object is created, and close it when it is destroyed. This opens and closes the database fewer times than the current implementation (due to not having to open close and reopen for verification) and is simpler (also due to not having to open close and reopen for verification). Feel free to ignore the suggestion or to follow it up later, but I hope I am getting it across clearly.

  174. in src/wallet/sqlite.cpp:89 in 96d2a5f8b8 outdated
    85+        sqlite3* db = nullptr;
    86+        int ret = sqlite3_open_v2(m_file_path.c_str(), &db, flags, nullptr);
    87+        if (ret != SQLITE_OK) {
    88+            throw std::runtime_error(strprintf("SQLiteDatabase: Failed to open database: %s\n", sqlite3_errstr(ret)));
    89+        }
    90+        // TODO: Maybe(?) Check the file wasn't copied and a duplicate opened
    


    ryanofsky commented at 2:14 am on October 6, 2020:

    In commit “Implement SQLiteDatabase::Open” (96d2a5f8b8ea7dd512deaac4d1d70702517af6c1)

    Should drop this comment. Copying a wallet may be a problem if you do it in a crazy way, or it may be perfectly safe. BDB code started checking for copies only as a kludge (initial kludge protected against ambiguity in the bdb log format when multiple databases were opened in the same environment, and later the kludge was extended to work around inability to lock BDB databases with set_lk_exclusive.)


    achow101 commented at 6:19 pm on October 6, 2020:
    Done
  175. in src/wallet/sqlite.cpp:97 in 96d2a5f8b8 outdated
    93+            if (sqlite3_db_readonly(db, "main") != 0) {
    94+                throw std::runtime_error("SQLiteDatabase: Database opened in readonly mode but read-write permissions are needed");
    95+            }
    96+
    97+            // Make the table for our key-value pairs
    98+            std::string create_stmt = "CREATE TABLE IF NOT EXISTS main(key BLOB PRIMARY KEY, value BLOB)";
    


    ryanofsky commented at 2:17 am on October 6, 2020:

    In commit “Implement SQLiteDatabase::Open” (96d2a5f8b8ea7dd512deaac4d1d70702517af6c1)

    Should add NOT NULL constraint to key and value columns. Code already has to deal with empty string values, so it would be better not to throw in NULL values and errors as well where we don’t need them.


    achow101 commented at 6:20 pm on October 6, 2020:
    PRIMARY KEY implies not null, so i’ve just added NOT NULL for value.
  176. in src/wallet/sqlite.cpp:25 in b6eeb2b5ee outdated
    20+
    21+SQLiteDatabase::~SQLiteDatabase()
    22+{
    23+}
    24+
    25+void SQLiteDatabase::Open(const char* pszMode)
    


    ryanofsky commented at 2:24 am on October 6, 2020:

    In commit “Add SQLiteDatabase and SQLiteBatch dummy classes” (b6eeb2b5ee6964a9034aca6f4be2b72d532589ca)

    param is renamed to mode in later commit, could just call it mode here to avoid rename


    achow101 commented at 6:20 pm on October 6, 2020:
    Done
  177. in src/wallet/sqlite.cpp:132 in fabd187ccb outdated
    116+    if (!PrepareDirectory()) return false;
    117+
    118+    sqlite3* db = nullptr;
    119+    int ret = sqlite3_open_v2(m_file_path.c_str(), &db, SQLITE_OPEN_READONLY, nullptr);
    120+    if (ret == SQLITE_NOTFOUND) {
    121+        sqlite3_close(db);
    


    ryanofsky commented at 2:50 am on October 6, 2020:

    In commit “Implement SQLiteDatabase::Verify” (fabd187ccb43d0cc266ae89cd2b2e3b3d098949a)

    I don’t think you can close the db if opening failed. I think even on cases where we don’t act on errors, we should at least log them. It would be best if all the sqlite_* calls in this PR logged errors so we are never blindly debugging or guessing about errors like this.


    S3RK commented at 5:54 am on October 6, 2020:

    From the docs

    Whether or not an error occurs when it is opened, resources associated with the database connection handle should be released by passing it to sqlite3_close() when it is no longer required.

    Logging is handled by global error log


    ryanofsky commented at 6:30 am on October 6, 2020:

    re: #19077 (review)

    Logging is handled by global error log

    Thanks for explaining sqlite3_close. How do you know whether or not something is logged to the error log? Does every function call that doesn’t return SQLITE_OK log to the error log? If so, then I guess there’s no technical reason to handle errors twice even if code looks like it is silently discarding them. I would prefer the uniform approach of handling all errors, but this would give more of excuse not to.


    S3RK commented at 9:47 am on October 6, 2020:

    Does every function call that doesn’t return SQLITE_OK log to the error log?

    This is my current understanding of how it works based on my tests and the following line from documentation.

    SQLite can be configured to invoke a callback function containing an error code and a terse error message whenever anomalies occur.

    I can check particular scenarios if you’d like.


    ryanofsky commented at 11:54 am on October 6, 2020:

    re: #19077 (review)

    This is my current understanding of how it works based on my tests and the following line from documentation.

    This looks like a misreading to me. The documentation could easily say every failing call is logged, but it doesn’t, and even goes out of it’s way to say it “strives to keep error logger traffic low.” But even all errors were guaranteed to be logged, would you expect someone else reading the code to know it? Or will the next person maintaining or debugging this code waste development time checking unchecked return values, instead of just being able to look at the code and see that every call is succeeding. That is why if I were writing this code, I would check for unexpected return values everywhere and print them. Fixing this is just a suggestion. It is fine to ignore the suggestion.


    S3RK commented at 1:59 pm on October 6, 2020:
    Indeed I assumed (maybe falsely) that the next maintainer is familiar with SQLite C api. My humble personal preference is not to complicate the code by duplicating the error messages, when the library can do it for us. I just tried to provide more context for the author and the reviewers, sorry if it wasn’t helpful.

    ryanofsky commented at 2:12 pm on October 6, 2020:

    re: #19077 (review)

    Indeed I assumed (maybe falsely) that the next maintainer is familiar with SQLite C api. My humble personal preference is not to complicate the code by duplicating the error messages, when the library can do it for us. I just tried to provide more context for the author and the reviewers, sorry if it wasn’t helpful.

    This was helpful, but it’s not just assuming some basic level of familiarity with sqlite, it is assuming knowledge of undocumented behavior of the API. Debug logging and error handling are two overlapping things code can do, but neither is a subset of the other. We aren’t using bash/go/C style programming, and we have good non-verbose error handling options available, so I wouldn’t ignore error codes just because sqlite has a debug feature that prints some subset of errors with less context.


    achow101 commented at 4:18 pm on October 6, 2020:

    From https://sqlite.org/c3ref/open.html

    Whether or not an error occurs when it is opened, resources associated with the database connection handle should be released by passing it to sqlite3_close() when it is no longer required.

    Since it is ok for a database to not exist when we do Verify, I think it is correct to not log anything on the NOTFOUND error here.

  178. in src/wallet/sqlite.cpp:170 in fabd187ccb outdated
    127+    }
    128+
    129+    sqlite3_stmt* stmt;
    130+    ret = sqlite3_prepare_v2(db, "PRAGMA integrity_check", -1, &stmt, nullptr);
    131+    if (ret != SQLITE_OK) {
    132+        sqlite3_finalize(stmt);
    


    ryanofsky commented at 2:52 am on October 6, 2020:

    In commit “Implement SQLiteDatabase::Verify” (fabd187ccb43d0cc266ae89cd2b2e3b3d098949a)

    This looks like a bug. You can’t delete a prepared statement that wasn’t created, and this might lead to a bad pointer dereference since since stmt pointer is not initialized.


    S3RK commented at 5:55 am on October 6, 2020:
    I’m not sure if we SHOULD finalize failed prepared statement, but it’s a no-op to finalize a NULL pointer.

    ryanofsky commented at 6:33 am on October 6, 2020:

    re: #19077 (review)

    I’m not sure if we SHOULD finalize failed prepared statement, but it’s a no-op to finalize a NULL pointer.

    The pointer looks uninitialized, not null. Even if this actually works, I wouldn’t code that looks so fragile and broken.


    S3RK commented at 9:57 am on October 6, 2020:
    As an out parameter It’s set to NULL in case of an error. This is documented and to me the code in question looks like an intended use of sqlite api. The sqlite3_finalize routine can be called at any point during the life cycle. I’m not sure whether it’s possible for an error to happen when statement is initialized. But it looks reasonable and safer to finalize whenever we encounter an error at any step of the life cycle.

    ryanofsky commented at 11:43 am on October 6, 2020:

    re: #19077 (review)

    As an out parameter It’s set to NULL in case of an error. This is documented and to me the code in question looks like an intended use of sqlite api.

    Sorry, where is this documented? Even assuming it is documented, would you expect anybody else reading the code to know this? Or will the next person maintaining or debugging this waste development time checking it, instead of just being able to look and see that the code won’t dereference an uninitialized pointer. If I were writing this, I would initialize the pointer to null. Fixing this is just a suggestion. It is fine to ignore the suggestion.


    S3RK commented at 1:39 pm on October 6, 2020:
    Just not to sound unfounded — here is the link https://www.sqlite.org/c3ref/prepare.html Maybe I misunderstood your point, I didn’t mean to say that I’m against initializing it to null, it’s a good idea. It was more to the fact that I believe calling finalize is fine in the case of an error.

    achow101 commented at 4:23 pm on October 6, 2020:

    From https://sqlite.org/c3ref/prepare.html

    *ppStmt is left pointing to a compiled prepared statement that can be executed using sqlite3_step(). If there is an error, *ppStmt is set to NULL.

    So stmt should be NULL after prepare if it fails.

    From https://sqlite.org/c3ref/finalize.html

    Invoking sqlite3_finalize() on a NULL pointer is a harmless no-op.

    The application must finalize every prepared statement in order to avoid resource leaks.

    Finalizing a nullptr is safe. It also seems like it is recommended to finalize on failure, just in case.

    Regardless, I will initialize stmt to nullptr.


    achow101 commented at 6:20 pm on October 6, 2020:
    Made stmt nullptr, and also done for other statements.
  179. in src/wallet/sqlite.cpp:147 in fabd187ccb outdated
    142+            error = strprintf(_("SQLiteDatabase: Failed to verify database: %s"), sqlite3_errstr(ret));
    143+            break;
    144+        }
    145+        const char* msg = (const char*)sqlite3_column_text(stmt, 0);
    146+        if (!msg) {
    147+            error = strprintf(_("SQLiteDatabase: Failed to verify database: %s"), sqlite3_errstr(ret));
    


    ryanofsky commented at 2:56 am on October 6, 2020:

    In commit “Implement SQLiteDatabase::Verify” (fabd187ccb43d0cc266ae89cd2b2e3b3d098949a)

    Would be good to disambiguate this error from error immediately above (step failed vs read failed)


    achow101 commented at 6:21 pm on October 6, 2020:
    Done
  180. in src/wallet/sqlite.cpp:154 in fabd187ccb outdated
    149+        }
    150+        std::string str_msg(msg);
    151+        if (str_msg == "ok") {
    152+            continue;
    153+        }
    154+        error += Untranslated("\n" + str_msg);
    


    ryanofsky commented at 2:58 am on October 6, 2020:

    In commit “Implement SQLiteDatabase::Verify” (fabd187ccb43d0cc266ae89cd2b2e3b3d098949a)

    Would be good to prefix sqlite error lines with “Failed to verify” text if error string is currently empty here.


    achow101 commented at 6:21 pm on October 6, 2020:
    Done
  181. in src/wallet/sqlite.cpp:116 in fabd187ccb outdated
    110@@ -111,6 +111,53 @@ SQLiteDatabase::~SQLiteDatabase()
    111     }
    112 }
    113 
    114+bool SQLiteDatabase::Verify(bilingual_str& error)
    115+{
    116+    if (!PrepareDirectory()) return false;
    


    ryanofsky commented at 3:10 am on October 6, 2020:

    In commit “Implement SQLiteDatabase::Verify” (fabd187ccb43d0cc266ae89cd2b2e3b3d098949a)

    It is odd to lock the wallet here because it means the wallet will be locked on opening when options.verify is true, but not locked until later when options.verify is false (later when the first batch is created).

    The simplest thing approach would seem to just open and lock the database once when the database object is created instead of opening and locking multiple times in different circumstances.


    achow101 commented at 6:21 pm on October 6, 2020:
    I’ve changed Verify to unlock the directory at the end of the function.
  182. in src/wallet/sqlite.cpp:342 in addc4630a2 outdated
    175@@ -176,7 +176,7 @@ bool SQLiteDatabase::Rewrite(const char* skip)
    176 
    177 bool SQLiteDatabase::PeriodicFlush()
    178 {
    179-    return false;
    180+    return true;
    


    ryanofsky commented at 3:17 am on October 6, 2020:

    In commit “Implement SQLiteDatabase::Flush, PeriodicFlush, and ReloadDbEnv as No-ops” (addc4630a2a6587b0e07b9c45644117542685fed)

    All the comments in the commit description should be comments in the code, so they are not lost in git history and so code makes sense. Would also suggest implementing these methods inline in the header file to avoid giving a misleading impression from reading the class definition that they are implemented to do things.


    achow101 commented at 6:21 pm on October 6, 2020:
    Done
  183. in src/wallet/sqlite.cpp:379 in 4f3e5569c3 outdated
    155@@ -139,6 +156,8 @@ void SQLiteDatabase::Close()
    156         throw std::runtime_error(strprintf("SQLiteDatabase: Failed to close database: %s\n", sqlite3_errstr(res)));
    157     }
    158     m_db = nullptr;
    159+
    160+    UnlockDirectory(m_dir_path, ".walletlock");
    


    ryanofsky commented at 3:21 am on October 6, 2020:

    In commit “sqlitedb: Create and lock the wallet directory” (4f3e5569c3c0eb83d50f7f88e9b673fa857fa08a)

    This fails to unlock the wallet if it was opened with options.verify = true, but m_db is null because no batch was created. In practice, I’m guessing this never happens, but this is the type of complication which would go away if we used sqlite locking instead of implementing our own locking.


    achow101 commented at 6:24 pm on October 6, 2020:
    I’ve removed if (!m_db) return; so this will always be run. This is safe because sqlite3_close on a nullptr is a no-op. This will unconditionally unlock the directory on db close.
  184. in src/wallet/sqlite.cpp:154 in e63b950425 outdated
    150@@ -151,6 +151,7 @@ void SQLiteDatabase::Close()
    151 {
    152     if (!m_db) return;
    153 
    154+    assert(m_refcount == 0);
    


    ryanofsky commented at 3:23 am on October 6, 2020:

    In commit “Implement SQLiteDatabase::RemoveRef and AddRef” (e63b9504257165780b2c56ec46bc842b8d680dbc)

    Can drop reference counting (see dummy classes comment) and this whole commit


    achow101 commented at 6:24 pm on October 6, 2020:
    Done
  185. in src/wallet/sqlite.cpp:194 in fcee39162e outdated
    188@@ -152,6 +189,14 @@ void SQLiteDatabase::Close()
    189     if (!m_db) return;
    190 
    191     assert(m_refcount == 0);
    192+
    193+    // Free all of the prepared statements
    194+    sqlite3_finalize(m_read_stmt);
    


    ryanofsky commented at 3:28 am on October 6, 2020:

    In commit “Add SetupSQLStatements” (fcee39162e5713f581443eb26d36c65c2c0b9cda)

    Should log errors if these fail so potential bugs don’t go undetected and so we are less likely to have to debug blindly.


    S3RK commented at 5:56 am on October 6, 2020:
    Logging is handled by global error log

    achow101 commented at 6:32 pm on October 6, 2020:
    Done
  186. in src/wallet/sqlite.cpp:258 in 21289de5fd outdated
    250@@ -251,22 +251,106 @@ void SQLiteBatch::Close()
    251 
    252 bool SQLiteBatch::ReadKey(CDataStream&& key, CDataStream& value)
    253 {
    254-    return false;
    255+    if (!m_database.m_db) return false;
    256+    assert(m_database.m_read_stmt);
    257+
    258+    // Bind: leftmost parameter in statement is index 1
    259+    int res = sqlite3_bind_blob(m_database.m_read_stmt, 1, key.data(), key.size(), SQLITE_STATIC);
    


    ryanofsky commented at 3:47 am on October 6, 2020:

    In commit “Implement SQLiteBatch::ReadKey, WriteKey, EraseKey, and HasKey” (21289de5fd85b4510032393902b0dec84704cf77)

    This whole commit does not seem thread safe. Wallet RPC calls can come in from multiple threads and validation events come in on their own thread, so I would think multiple batch objects and reads and writes could happen simultaneously. If this is the case bind_blob/step calls to the same statement objects from different threads would all interfere with each other.

    The simplest approach would seem to just create prepared statements on demand instead of trying to share them between different batches.


    S3RK commented at 5:45 am on October 6, 2020:
    I think we’ve discussed it already here #19077 (review)

    ryanofsky commented at 6:19 am on October 6, 2020:

    re: #19077 (review)

    I think we’ve discussed it already here #19077 (comment)

    You can use the same prepared statement from different threads but you can’t use the same statement for different purposes with different values at the same time from different threads. The only thing that might prevent bugs in practice is the fact that most of the database is copied into memory and not actually accessed through database lookups, and the fact that the cs_wallet mutex makes most wallet operations single threaded in practice. But these things only make the code accidentally correct instead of correct by design. The sqlite implementation shouldn’t have object sharing and reference counting code that makes it more complicated than it needs to be, and less safe than the bdb implementation (or any sane key/value API). You shouldn’t get undefined results, for example, simply reading two keys from two different threads.


    S3RK commented at 10:26 am on October 6, 2020:

    Now I think you’re correct. Looks like the locking in sqlite will guarantee safety for a single call, but we ourselves still have to take care for the sequence of the calls.

    You can use the same prepared statement from different threads but you can’t use the same statement for different purposes with different values at the same time from different threads.

    This is what I failed to articulate before and was to quick to agree it’s safe.


    achow101 commented at 6:25 pm on October 6, 2020:
    Moved all of the statements into SQLiteBatch.
  187. in src/wallet/sqlite.cpp:542 in d448c6c362 outdated
    413@@ -414,17 +414,23 @@ void SQLiteBatch::CloseCursor()
    414 
    415 bool SQLiteBatch::TxnBegin()
    416 {
    417-    return false;
    418+    if (!m_database.m_db || sqlite3_get_autocommit(m_database.m_db) == 0) return false;
    419+    int res = sqlite3_exec(m_database.m_db, "BEGIN TRANSACTION", nullptr, nullptr, nullptr);
    


    ryanofsky commented at 3:52 am on October 6, 2020:

    In commit “Implement SQLiteDatabase::TxnBegin, TxnCommit, and TxnAbort” (d448c6c362642f24b08b754cb09066ced847f867)

    It would seem best to log errors if these calls fail. TxnBegin, TxnCommit, TxnAbort should never return false unless there’s a bug in our code or a runtime error and in either case having more debug information would be useful


    achow101 commented at 6:25 pm on October 6, 2020:
    Done

    Sjors commented at 12:18 pm on October 8, 2020:
    4e8011a868d10650880cf5c5c01f005bc9fcc402 (maybe for followup): does it make sense to make this a prepared statement?

    achow101 commented at 3:10 pm on October 8, 2020:

    4e8011a (maybe for followup): does it make sense to make this a prepared statement?

    No. There’s nothing to prepare.

  188. ryanofsky commented at 4:22 am on October 6, 2020: member

    I’m think I’m like 75% done with this review. I’m posting comments I have so far just because I think one new comment below about prepared statement thread safety is critical and should be addressed before this PR is merged (see “This whole commit does not seem thread safe” below).

    Overall, there are a lot of things here I think should be cleaned up and simplified, but almost all of them could be implemented as followups after this is merged. The only three things I think are critical to change before this PR is merged are easy to patch in:

    • Not sharing prepared statements between threads
    • Enabling more functional tests for sqlite wallets (particularly multiwallet and wallet backup tests)
    • Setting data filename to avoid various forward & backward compatibility complications
  189. achow101 force-pushed on Oct 6, 2020
  190. achow101 force-pushed on Oct 6, 2020
  191. achow101 force-pushed on Oct 6, 2020
  192. achow101 force-pushed on Oct 6, 2020
  193. achow101 commented at 6:39 pm on October 6, 2020: member
    I’ve also made a few changes to pass a couple of tests with #18788 merged. https://github.com/achow101/bitcoin/tree/sqlite-master is a branch with this and #18788 merged in, based on master. That branch also has an additional commit so that tool_wallet works with sqlite.
  194. in src/wallet/sqlite.h:70 in f747380289 outdated
    65+    /** Indicate the a new database user has began using the database. Increments m_refcount */
    66+    void AddRef() override;
    67+    /** Indicate that database user has stopped using the database. Decrement m_refcount */
    68+    void RemoveRef() override;
    69+
    70+    /** Rewrite the entire database on disk, with the exception of key pszSkip if non-zero
    


    ryanofsky commented at 1:28 am on October 7, 2020:

    In commit “Add SQLiteDatabase and SQLiteBatch dummy classes” (f747380289b06ba1631e1cec30b96b5aecb61f36)

    Comment about pszSkip doesn’t apply here


    achow101 commented at 7:04 pm on October 7, 2020:
    Removed
  195. in src/wallet/sqlite.h:78 in f747380289 outdated
    73+
    74+    /** Back up the entire database to a file.
    75+     */
    76+    bool Backup(const std::string& dest) const override;
    77+
    78+    /** Make sure all changes are flushed to disk.
    


    ryanofsky commented at 1:33 am on October 7, 2020:

    In commit “Add SQLiteDatabase and SQLiteBatch dummy classes” (f747380289b06ba1631e1cec30b96b5aecb61f36)

    Comment isn’t true since method is a no-op. I think this method and other methods which are no-ops like PeriodicFlush ReloadDbEn or never called AddRef RemoveRef should be inlined here so it’s obvious what their role is in the sqlite implementation and how they are not normal methods


    achow101 commented at 7:04 pm on October 7, 2020:
    Removed and inlined these functions.
  196. in src/wallet/sqlite.h:81 in f747380289 outdated
    76+    bool Backup(const std::string& dest) const override;
    77+
    78+    /** Make sure all changes are flushed to disk.
    79+     */
    80+    void Flush() override;
    81+    /* flush the wallet passively (TRY_LOCK)
    


    ryanofsky commented at 1:33 am on October 7, 2020:

    In commit “Add SQLiteDatabase and SQLiteBatch dummy classes” (f747380289b06ba1631e1cec30b96b5aecb61f36)

    Comment is also incorrect

  197. in src/wallet/sqlite.cpp:92 in d1559561a6 outdated
    87+            throw std::runtime_error(strprintf("SQLiteDatabase: Failed to open database: %s\n", sqlite3_errstr(ret)));
    88+        }
    89+
    90+        if (create) {
    91+            if (sqlite3_db_readonly(db, "main") != 0) {
    92+                throw std::runtime_error("SQLiteDatabase: Database opened in readonly mode but read-write permissions are needed");
    


    ryanofsky commented at 1:56 am on October 7, 2020:

    In commit “Implement SQLiteDatabase::Open” (d1559561a68dd6604efd91baa5dbd28c995d0074)

    It looks like db pointer can be leaked here if this error is thrown


    achow101 commented at 7:05 pm on October 7, 2020:
    I’ve changed this to just use m_db so on failure it will be cleaned up by the destructor.
  198. in src/wallet/sqlite.cpp:99 in d1559561a6 outdated
    94+
    95+            // Make the table for our key-value pairs
    96+            std::string create_stmt = "CREATE TABLE IF NOT EXISTS main(key BLOB PRIMARY KEY, value BLOB NOT NULL)";
    97+            ret = sqlite3_exec(db, create_stmt.c_str(), nullptr, nullptr, nullptr);
    98+            if (ret != SQLITE_OK) {
    99+                throw std::runtime_error(strprintf("SQLiteDatabase: Failed to create new database: %s\n", sqlite3_errstr(ret)));
    


    ryanofsky commented at 1:57 am on October 7, 2020:

    In commit “Implement SQLiteDatabase::Open” (d1559561a68dd6604efd91baa5dbd28c995d0074)

    It looks like db pointer can be leaked here if ret is not OK.

  199. in src/wallet/sqlite.cpp:96 in d1559561a6 outdated
    91+            if (sqlite3_db_readonly(db, "main") != 0) {
    92+                throw std::runtime_error("SQLiteDatabase: Database opened in readonly mode but read-write permissions are needed");
    93+            }
    94+
    95+            // Make the table for our key-value pairs
    96+            std::string create_stmt = "CREATE TABLE IF NOT EXISTS main(key BLOB PRIMARY KEY, value BLOB NOT NULL)";
    


    ryanofsky commented at 2:21 am on October 7, 2020:

    In commit “Implement SQLiteDatabase::Open” (d1559561a68dd6604efd91baa5dbd28c995d0074)

    key needs NOT NULL constraint to prevent nulls (primary key isn’t enough).

    0$ sqlite3
    1SQLite version 3.30.1 2019-10-10 20:19:45
    2sqlite> CREATE TABLE IF NOT EXISTS main(key BLOB PRIMARY KEY, value BLOB NOT NULL);
    3sqlite> insert into main values (null, 'value');
    4sqlite> select * from main;
    5|value
    

    achow101 commented at 7:05 pm on October 7, 2020:
    done
  200. in src/wallet/sqlite.cpp:106 in d1559561a6 outdated
    101+
    102+            // Enable fullfysnc for the platforms that use it
    103+            std::string fullfsync_stmt = "PRAGMA fullfsync = true";
    104+            ret = sqlite3_exec(db, fullfsync_stmt.c_str(), nullptr, nullptr, nullptr);
    105+            if (ret != SQLITE_OK) {
    106+                throw std::runtime_error(strprintf("SQLiteDatabase: Failed to enable fullfsync: %s\n", sqlite3_errstr(ret)));
    


    ryanofsky commented at 2:22 am on October 7, 2020:

    In commit “Implement SQLiteDatabase::Open” (d1559561a68dd6604efd91baa5dbd28c995d0074)

    It looks like db pointer can be leaked here if ret is not OK.

  201. in src/wallet/sqlite.cpp:177 in 29395e2869 outdated
    171@@ -172,6 +172,13 @@ void SQLiteBatch::Flush()
    172 
    173 void SQLiteBatch::Close()
    174 {
    175+    if (m_database.m_db && sqlite3_get_autocommit(m_database.m_db) == 0) {
    176+        if (TxnAbort()) {
    177+            LogPrintf("SQLiteBatch: Batch closed and transaction was aborted\n");
    


    ryanofsky commented at 2:30 am on October 7, 2020:

    In commit “Implement SQLiteBatch::Close” (29395e28698978dc65e16c4eaf9bb8f8e7429e7d)

    Would be good to s/Batch closed/Batch closed unexpectedly without explicit commit or abort/ here and below to be clear this is an error, not an informational log


    achow101 commented at 7:05 pm on October 7, 2020:
    Done
  202. in src/wallet/sqlite.cpp:179 in 29395e2869 outdated
    171@@ -172,6 +172,13 @@ void SQLiteBatch::Flush()
    172 
    173 void SQLiteBatch::Close()
    174 {
    175+    if (m_database.m_db && sqlite3_get_autocommit(m_database.m_db) == 0) {
    176+        if (TxnAbort()) {
    177+            LogPrintf("SQLiteBatch: Batch closed and transaction was aborted\n");
    178+        } else {
    179+            LogPrintf("SQLiteBatch: Batch closed and could not abort transaction\n");
    


    ryanofsky commented at 2:42 am on October 7, 2020:

    In commit “Implement SQLiteBatch::Close” (29395e28698978dc65e16c4eaf9bb8f8e7429e7d)

    Would be good to s/could not abort/failed to abort/ to be clear abort call was made rather than considered


    achow101 commented at 7:05 pm on October 7, 2020:
    Done
  203. in src/wallet/sqlite.cpp:357 in f86afc5340 outdated
    238@@ -202,6 +239,28 @@ void SQLiteBatch::Close()
    239             LogPrintf("SQLiteBatch: Batch closed and could not abort transaction\n");
    240         }
    241     }
    242+
    243+    // Free all of the prepared statements
    244+    int ret = sqlite3_finalize(m_read_stmt);
    245+    if (ret != SQLITE_OK) {
    


    ryanofsky commented at 3:35 am on October 7, 2020:

    In commit “Add SetupSQLStatements” (f86afc53406448428dadaf59d0ccf78fb1421220)

    It looks like there are double-delete bugs in this method depending on how it’s called. Statement pointers are not set to null if finalize returns SQLITE_OK, so if Close is called twice sqlite3_finalize will be called with invalid deleted pointers and possibly segfault.

    One fix would be to set the pointers to null if finalize returns SQLITE_OK. Another fix would be move the code out of this method into the destructor.


    achow101 commented at 7:05 pm on October 7, 2020:
    Set the pointers to nullptr.
  204. in src/wallet/sqlite.cpp:392 in 36ea015032 outdated
    270+    assert(m_read_stmt);
    271+
    272+    // Bind: leftmost parameter in statement is index 1
    273+    int res = sqlite3_bind_blob(m_read_stmt, 1, key.data(), key.size(), SQLITE_STATIC);
    274+    if (res != SQLITE_OK) {
    275+        sqlite3_clear_bindings(m_read_stmt);
    


    ryanofsky commented at 3:44 am on October 7, 2020:

    In commit “Implement SQLiteBatch::ReadKey, WriteKey, EraseKey, and HasKey” (36ea015032ddf1aa7d424b463c2c6c531d832295)

    Throughout the PR would be good to check all return codes and log errors with context information whenever any sqlite3_* calls fail. But if the practice of checking return codes is going to be set aside when calling sqlite, feel free to ignore the suggestion.


    achow101 commented at 7:06 pm on October 7, 2020:
    I think it’s fine to skip checking the return codes for clear_bindings and reset.

    Sjors commented at 11:43 am on October 8, 2020:
    fac0cf3d1edff440cdbd19d2615cba701bb4a17d Agree, but logging why sqlite3_bind_blob and sqlite3_step fail does seem useful.

    achow101 commented at 3:08 pm on October 8, 2020:
    Added logging.
  205. in src/wallet/sqlite.cpp:486 in 36ea015032 outdated
    356+    if (!m_database.m_db) return false;
    357+    assert(m_read_stmt);
    358+
    359+    // Bind: leftmost parameter in statement is index 1
    360+    bool ret = false;
    361+    int res = sqlite3_bind_blob(m_read_stmt, 1, key.data(), key.size(), SQLITE_STATIC);
    


    ryanofsky commented at 3:52 am on October 7, 2020:

    In commit “Implement SQLiteBatch::ReadKey, WriteKey, EraseKey, and HasKey” (36ea015032ddf1aa7d424b463c2c6c531d832295)

    Theoretically it might be more efficient to use SELECT EXISTS(SELECT * FROM main WHERE key = ?) instead of SELECT value FROM main WHERE key = ? to avoid needing to read the table and just use the index. But probably does not matter in practice.


    promag commented at 10:17 pm on October 12, 2020:

    40b15923da624272830ab51ac30be892b9d5712b

    It was already suggested in #19077#pullrequestreview-497108156

    Could add a dedicated statement for HasKey, see https://stackoverflow.com/a/9756276/1978589. Are you going to add any index to main table?

    Anyway it could also log “Unable to bind value to statement” when binding fails.

  206. in src/wallet/sqlite.cpp:190 in 273e8adfe8 outdated
    186+        sqlite3_close(db_copy);
    187+        return false;
    188+    }
    189+    sqlite3_backup* backup = sqlite3_backup_init(db_copy, "main", m_db, "main");
    190+    if (!backup) {
    191+        sqlite3_backup_finish(backup);
    


    ryanofsky commented at 3:59 am on October 7, 2020:

    In commit “Implement SQLiteDatabase::Backup” (273e8adfe8e96d45dca0e44fe194bf2548dd6686)

    Should drop this line. “There should be exactly one call to sqlite3_backup_finish() for each successful call to sqlite3_backup_init().” https://www.sqlite.org/c3ref/backup_finish.html


    achow101 commented at 7:06 pm on October 7, 2020:
    Removed
  207. in src/wallet/sqlite.cpp:122 in 90a7a6b371 outdated
    117+        error = strprintf(_("SQLiteDatabase: Unable to obtain lock on wallet directory \"%s\""), m_dir_path);
    118+        return false;
    119+    }
    120+
    121+    sqlite3* db = nullptr;
    122+    int ret = sqlite3_open_v2(m_file_path.c_str(), &db, SQLITE_OPEN_READONLY, nullptr);
    


    ryanofsky commented at 4:35 am on October 7, 2020:

    In commit “Implement SQLiteDatabase::Verify” (90a7a6b371d1106f1cab02bcdda2454a828add1d)

    I know the history of the PR, but at this point post-#19619, I think it is a little nuts to keep using the sequence:

    1. Lock wallet directory
    2. Open database
    3. Verify database
    4. Unlock wallet directory
    5. Lock wallet directory
    6. Open database
    7. Use database
    8. Close database
    9. Unlock wallet directory

    when DatabaseOptions::verify is true, instead of simply:

    1. Open database in exclusive mode
    2. Verify database
    3. Use database
    4. Close database

    But feel free to ignore the suggested simplification or to follow up in a future PR.


    achow101 commented at 7:07 pm on October 7, 2020:

    Done.

    As a result of this, I’ve also dropped the usage of mode and the database will always be created if it does not exist. This is fine as we enforce existence during MakeDatabase, but it would be better to have a create flag for this.

  208. in src/wallet/sqlite.cpp:67 in d1559561a6 outdated
    63@@ -64,6 +64,54 @@ SQLiteDatabase::~SQLiteDatabase()
    64 
    65 void SQLiteDatabase::Open(const char* mode)
    66 {
    67+    const bool read_only = (!strchr(mode, '+') && !strchr(mode, 'w'));
    


    ryanofsky commented at 3:38 pm on October 7, 2020:

    In commit “Implement SQLiteDatabase::Open” (d1559561a68dd6604efd91baa5dbd28c995d0074)

    Looking at S3RK’s branch https://github.com/bitcoin/bitcoin/pull/19077/#discussion_r499125987, I don’t see a need to parse the mode argument at all. I think SQLiteDatabase::Open should ignore the mode argument just like SQLiteDatabase::Rewrite ignores the skip argument. It would simplify the implementation here in various ways. And later I think the mode argument should be dropped from BDB as well in favor of simply respecting the DatabaseOptions.require_existing value passed when creating the database object.

  209. ryanofsky changes_requested
  210. ryanofsky commented at 4:28 pm on October 7, 2020: member

    This is looking great! This is already much simpler and nicer than the BDB implementation, and I’d be ready to ACK d8e024121b54fc998c61140dfdb004134a388928 with the following minimal changes: diff (commits, branch) to add sufficient test coverage and avoid being trapped supporting a layout that seems opaque and misleading and I think likely to cause compatibility problems.

    Feel free to ignore all other suggestions from me below. I think the other suggestions would simplify the PR, make the code more reliable, and fix minor bugs, but are not critical for compatibility or correctness. Only the ambiguous layout and lack of test coverage fixed by the suggested diff above hold me back from ACKing d8e024121b54fc998c61140dfdb004134a388928.

  211. achow101 force-pushed on Oct 7, 2020
  212. achow101 commented at 7:08 pm on October 7, 2020: member

    I’ve done the suggested changes except for wallet.sqlite. I think we should discuss this during the wallet meeting this week.

    I’ve also pulled in the suggested test changes.

  213. achow101 force-pushed on Oct 7, 2020
  214. achow101 force-pushed on Oct 7, 2020
  215. achow101 commented at 8:36 pm on October 7, 2020: member

    I’ve done some testing with the behavior around a downgrading and different filenames.

    • Downgrading with the sqlite wallet named wallet.dat
      • Previous versions will attempt to open the wallet.dat file and fail to do so because the file magic does not match BDB’s. However on that failure, a salvage will be attempted. This will do nothing because the file magic does not match BDB’s. The file remains unchanged when this occurs and it fails with the error message Error: wallet.dat corrupt, salvage failed.
    • Downgrading with the sqlite wallet named wallet.sqlite
      • When attempting to load via loadwallet, the wallet will not be loaded as the RPC checks for existence of the wallet.dat file in the wallet’s directory. It fails with the error message Directory does not contain a wallet.dat file.
      • When specified with -wallet (either in cli options or bitcoin.conf), a new wallet.dat file will be created and used. This is a wholly new wallet unrelated to the wallet.sqlite file. There are no errors. This is dangerous.

    For that last case, we either must keep the wallet.dat name, or come up with some workaround that prevents previous versions from creating a wallet.dat file.

    The only possible solution I can think of is to create a dummy wallet.dat file to prevent previous versions from making a new file. However this is potentially dangerous because users may are expecting a wallet.dat file and may have tooling to interact with the wallet.dat file (such as creating and restoring backups) and thus they may be accidentally interacting with a useless dummy file instead of the real wallet.sqlite file. So I don’t think this is a good approach.

    Furthermore, as mentioned previously, there is a lot of tooling and documentation involving the name wallet.dat. Users are expecting their wallet file to be named wallet.dat. While we have a backupwallet RPC and backup option in the GUI, many users probably don’t use these because there is “common knowledge” of directly backing up the wallet.dat file. And while copying the wallet.dat file while Bitcoin Core is running is not a supported use case, copying it after a clean shutdown is. Users may be doing that for their backups. Retaining the wallet.dat naming means that those tools and documentation are still valid.

  216. in src/wallet/sqlite.cpp:347 in d993dee317 outdated
    145@@ -146,6 +146,13 @@ SQLiteBatch::SQLiteBatch(SQLiteDatabase& database, const char* mode)
    146 
    147 void SQLiteBatch::Close()
    148 {
    149+    if (m_database.m_db && sqlite3_get_autocommit(m_database.m_db) == 0) {
    


    Sjors commented at 10:48 am on October 8, 2020:

    d993dee317b93b11296282b9e97fa66e8c5ca152:

    0// If m_db is not in autocommit mode, try to abort the transaction in progress
    

    achow101 commented at 3:06 pm on October 8, 2020:
    Done
  217. in src/wallet/sqlite.cpp:354 in d993dee317 outdated
    145@@ -146,6 +146,13 @@ SQLiteBatch::SQLiteBatch(SQLiteDatabase& database, const char* mode)
    146 
    147 void SQLiteBatch::Close()
    148 {
    149+    if (m_database.m_db && sqlite3_get_autocommit(m_database.m_db) == 0) {
    150+        if (TxnAbort()) {
    151+            LogPrintf("SQLiteBatch: Batch closed unexpectedly without the transaction being explicitly commited or aborted\n");
    


    Sjors commented at 10:51 am on October 8, 2020:

    d993dee317b93b11296282b9e97fa66e8c5ca152: TxnAbort() returns true if and only if ROLLBACK TRANSACTION succeeds, so I’m confused what you mean by “without the transaction being explicitly commited or aborted”

    See also @ryanofsky: #19077 (review)


    achow101 commented at 2:23 pm on October 8, 2020:

    I’m confused what you mean by “without the transaction being explicitly commited or aborted”

    It means that the caller didn’t call TxnAbort or TxnCommit and instead it is being done on the batch close.

  218. in src/wallet/sqlite.cpp:82 in fe12f7946e outdated
    61+    std::string delete_sql = "DELETE FROM main WHERE key = ?";
    62+    std::string cursor_sql = "SELECT key, value FROM main";
    63+
    64+    int res;
    65+    if (!m_read_stmt) {
    66+        if ((res = sqlite3_prepare_v2(m_database.m_db, read_sql.c_str(), -1, &m_read_stmt, nullptr)) != SQLITE_OK) {
    


    Sjors commented at 11:04 am on October 8, 2020:

    Is it worth (in a followup) using sqlite3_prepare_v3 with the SQLITE_PREPARE_PERSISTENT flag? https://www.sqlite.org/c3ref/c_prepare_normalize.html#sqlitepreparepersistent

    I guess that’s only useful if these prepared statements can live longer than a single batch.


    achow101 commented at 2:24 pm on October 8, 2020:
    Since we’ve moved the statements to SQLiteBatch, I don’t think that makes sense to do.
  219. in src/wallet/sqlite.cpp:396 in fac0cf3d1e outdated
    230+    if (res != SQLITE_OK) {
    231+        sqlite3_clear_bindings(m_read_stmt);
    232+        sqlite3_reset(m_read_stmt);
    233+        return false;
    234+    }
    235+    res = sqlite3_step(m_read_stmt);
    


    Sjors commented at 11:55 am on October 8, 2020:
    fac0cf3d1edff440cdbd19d2615cba701bb4a17d: you could move the sqlite3_step to a common function (especially if it has more error handling)

    achow101 commented at 3:07 pm on October 8, 2020:
    I don’t think that is necessary.
  220. in src/wallet/sqlite.cpp:520 in 87d8fbfe58 outdated
    342+    int res = sqlite3_step(m_cursor_stmt);
    343+    if (res == SQLITE_DONE) {
    344+        complete = true;
    345+        return true;
    346+    } else if (res != SQLITE_ROW) {
    347+        return false;
    


    Sjors commented at 12:06 pm on October 8, 2020:
    87d8fbfe58c5daa61326f67c228f09e7758ea368: this warrants a log message

    achow101 commented at 3:07 pm on October 8, 2020:
    Done
  221. in src/wallet/sqlite.cpp:304 in 0584828e2a outdated
    164+        sqlite3_close(db_copy);
    165+        return false;
    166+    }
    167+    sqlite3_backup* backup = sqlite3_backup_init(db_copy, "main", m_db, "main");
    168+    if (!backup) {
    169+        sqlite3_close(db_copy);
    


    Sjors commented at 12:07 pm on October 8, 2020:
    0584828e2a9022d5b548ebda6a9a8329296bcafb: worth logging why opening the destination file failed, and why backup failed to initialize, step or finish

    achow101 commented at 3:07 pm on October 8, 2020:
    Done
  222. in src/wallet/sqlite.cpp:288 in dfa8673927 outdated
    151@@ -152,7 +152,9 @@ void SQLiteDatabase::Open(const char* mode)
    152 
    153 bool SQLiteDatabase::Rewrite(const char* skip)
    154 {
    155-    return false;
    156+    // Rewrite the database using the VACUUM command: https://sqlite.org/lang_vacuum.html
    


    Sjors commented at 12:31 pm on October 8, 2020:

    dfa867392725f999a331404205635ee7a331886f maybe explicitly mention in EncryptWallet that SQLite also leaves data behind when you delete a row (docs say: “This can allow deleted content to be recovered by a hacker or by forensic analysis.”)

    Maybe throw/assert that skip is not set, since the argument is ignored.


    achow101 commented at 3:07 pm on October 8, 2020:
    I don’t think that is necessary.
  223. in src/wallet/sqlite.cpp:142 in 4eaffb807d outdated
    124+    }
    125+    uint32_t app_id = (uint32_t)sqlite3_column_int(app_id_stmt, 0);
    126+    sqlite3_finalize(app_id_stmt);
    127+    uint32_t net_magic = ReadBE32(Params().MessageStart());
    128+    if (app_id != net_magic) {
    129+        error = strprintf(_("SQLiteDatabase: Unexpected application id. Expected %u, got %u"), net_magic, app_id);
    


    Sjors commented at 1:59 pm on October 8, 2020:
    4eaffb807d7050dd95a1939585cc4df3caf57118: I tried opening a signet wallet (loadwallet) in testnet mode. It failed with Data is not in recognized format., rather than this error. That’s probably because IsSQLiteFile also checks this.

    achow101 commented at 2:49 pm on October 8, 2020:
    :shrug: I guess that error is fine too. IsSQLiteFile needs to check the app id to avoid it showing up in listwallets.

    Sjors commented at 6:30 pm on October 8, 2020:
    Accidentally loading a wallet from the wrong network via RPC takes some effort, so I don’t mind punting on this :-)
  224. in src/wallet/sqlite.cpp:163 in 810f3de364 outdated
    146+        return false;
    147+    }
    148+    int32_t user_ver = sqlite3_column_int(user_ver_stmt, 0);
    149+    sqlite3_finalize(user_ver_stmt);
    150+    if (user_ver != WALLET_SCHEMA_VERSION) {
    151+        error = strprintf(_("SQLiteDatabase: Unknown sqlite wallet schema version %d. Only version %d is supported"), user_ver, WALLET_SCHEMA_VERSION);
    


    Sjors commented at 2:12 pm on October 8, 2020:
    I changed WALLET_SCHEMA_VERSION to 1, recompiled and was able to open an existing wallet… Ditto if I create a new wallet with version 10, set it back to 0 and load it.

    achow101 commented at 3:07 pm on October 8, 2020:
    I think this should be fixed now.

    Sjors commented at 6:40 pm on October 8, 2020:
    It is
  225. Sjors commented at 2:18 pm on October 8, 2020: member

    Reviewed all the things. 5f5c7741f0195a6d3fbd28b248ed547d1d39cb27 looks mostly good.

    We could also stick to wallet.dat for the main wallet, but use .sqlite for all freshly created ones.

    Regarding the application ID we could mail a patch to register the mainnet, testnet, regtest and default signet magic bytes.

  226. achow101 commented at 2:25 pm on October 8, 2020: member

    We could also stick to wallet.dat for the main wallet, but use .sqlite for all freshly created ones.

    Not sure what you mean by “main wallet”

  227. Sjors commented at 2:33 pm on October 8, 2020: member
    The default wallet.
  228. achow101 force-pushed on Oct 8, 2020
  229. achow101 commented at 3:08 pm on October 8, 2020: member

    The default wallet.

    No. That’s way more confusing. We also stopped making the default wallet.

  230. achow101 force-pushed on Oct 8, 2020
  231. achow101 force-pushed on Oct 8, 2020
  232. in src/wallet/sqlite.cpp:58 in 328ff35370 outdated
    51@@ -46,6 +52,12 @@ std::unique_ptr<DatabaseBatch> SQLiteDatabase::MakeBatch(const char* mode, bool
    52     return nullptr;
    53 }
    54 
    55+SQLiteBatch::SQLiteBatch(SQLiteDatabase& database, const char* mode)
    56+    : m_database(database)
    57+{
    58+    m_database.Open(mode);
    


    ryanofsky commented at 4:53 pm on October 8, 2020:

    In commit “Constructors, destructors, and relevant private fields for SQLiteDatabase/Batch” (328ff35370cc30b64006480ccc80418888e1d488)

    Is still keeping this Open call intentional? Should add a comment if so.


    achow101 commented at 7:43 pm on October 8, 2020:
    It’s there to ensure that the database is always open when a SQLiteBatch is active. Maybe it would be better to just assert(m_database.m_db)?

    achow101 commented at 7:57 pm on October 8, 2020:
    I’ve changed it to assert, along with a comment.
  233. in src/wallet/sqlite.cpp:500 in 7f15256fc0 outdated
    493@@ -457,7 +494,18 @@ bool ExistsSQLiteDatabase(const fs::path& path)
    494 
    495 std::unique_ptr<SQLiteDatabase> MakeSQLiteDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error)
    496 {
    497-    return MakeUnique<SQLiteDatabase>(path, path / DATABASE_FILENAME);
    498+    fs::path file = path / DATABASE_FILENAME;
    499+    try {
    500+        auto db = MakeUnique<SQLiteDatabase>(path, file);
    501+        if (options.verify && fs::is_regular_file(file) && !db->Verify(error)) {
    


    ryanofsky commented at 4:59 pm on October 8, 2020:

    In commit “Constructors, destructors, and relevant private fields for SQLiteDatabase/Batch” (328ff35370cc30b64006480ccc80418888e1d488)

    Is still keeping this fs::is_regular_file intentional? Should add a comment if so. It seems like the file should already exists at this point, and if it didn’t you would probably want to return an error not skip verification.


    achow101 commented at 7:57 pm on October 8, 2020:
    Removed.
  234. ryanofsky commented at 6:04 pm on October 8, 2020: member

    41af0d7a79322a25423f2ea9d1ffb2ce751c338b looks very good, and thanks for doing all those manual downgrade checks!

    I would still want to avoid using the bdb location to store sqlite data (now just requires a small change: diff, commits, branch) for the following reasons:

    • Transparency and straightforwardness. Choose a conventional name, do not disguise one type of file as another type, make it obvious to anybody to see what a wallet directory contains.
    • Future flexibility. Break away from the wallet.dat convention now so it is easier to do later. I think we should have flexibility about what files we store in wallet directories, because in the future we will want to add more things there: socket files, authentication cookies, debug logs, per-wallet static configuration files, and maybe other things. (Note that wallet.dat convention for newly created wallets was also not enforced between #1889 and #11687 so moving away from it again is not a new thing).
    • Compatibility and safety with external tools and documentation. It seems unlikely external tools or documentation would be able to cope correctly with data in a new format (I would be curious to see counterexamples). I would actually expect things written to deal with BDB files to handle SQLite data badly or even dangerously. If there is external documentation referencing wallet.dat files, and people are reading it expecting do something with sqlite descriptor wallets, I think it is good not bad they won’t be able to find wallet.dat files, so they can know what they are reading is probably out of date and inapplicable.

    re: #19077 (comment)

    The downgrading issue described #19077 (comment) does not seem like a very realistic problem that would actually happen. It’s also straightforward to see and fix, wouldn’t result in data loss if it did happen, and is actually just more obscure version of downgrading behavior that happened previously in #11687. In #11687 newly created wallets started having their own environments and having their data stored <wallet name>/wallet.dat files instead of <wallet name> files. So if you created a new wallet post-#11687, added the wallet to your static configuration, and downgraded to pre-#11687 software, the old software would fail to find the new wallet, and create an empty one in the old location. The same thing happens with downgrading in your example: old software fails to find new location referenced in static configuration and creates an empty one in the old location. But your case is much more obscure, because in #11687, the new wallets could actually be created directly from the static configuration, while in your case it requires active RPC or GUI interactions to create the incompatible new wallets. And your case also requires the user to load the wallet in a different way than they created it, instead of the same way they created it (GUI interaction, or load RPC or load through settings.json). It doesn’t seem like a real concern to me given how obscure, detectable, and recoverable it would be.

  235. meshcollider added this to the milestone 0.21.0 on Oct 8, 2020
  236. achow101 commented at 7:30 pm on October 8, 2020: member
    • Compatibility and safety with external tools and documentation. It seems unlikely external tools or documentation would be able to cope correctly with data in a new format (I would be curious to see counterexamples). I would actually expect things written to deal with BDB files to handle SQLite data badly or even dangerously. If there is external documentation referencing wallet.dat files, and people are reading it expecting do something with sqlite descriptor wallets, I think it is good not bad they won’t be able to find wallet.dat files, so they can know what they are reading is probably out of date and inapplicable.

    Most documentation that I have seen only discuss creating and restoring backups. In both cases, the file format does not matter. Scripts involved in both also wouldn’t care about the file format itself, just the filename. For example, a cronjob that copies a wallet.dat file to another location is something that I would expect someone to do, and that is completely agnostic to the data format. Furthermore, for restoring wallets, as we have no equivalent of the backupwallet RPC, users have to copy their wallet file back into the datadir, and everything regarding restoring will instruct users to copy the file back as wallet.dat. These operations are all unrelated to the format of the wallet.dat file, but do require the file to be named wallet.dat.

    In terms of actual tools that operate on the wallet.dat file, I don’t think many exist. The only ones I know of are pywallet and btcrecover. In both cases, they use BDB’s python bindings which will check the file magic before opening the file. In fact, I would expect that any tooling that attempts to modify the wallet.dat file to be using the BDB library which will check the file magic before opening the file. It doesn’t make sense to me to try to interact with the wallet.dat file without a library because that would interfere with the BDB system. So I don’t think that should really be a concern for us.

    But your case is much more obscure, because in #11687, the new wallets could actually be created directly from the static configuration, while in your case it requires active RPC or GUI interactions to create the incompatible new wallets. And your case also requires the user to load the wallet in a different way than they created it, instead of the same way they created it (GUI interaction, or load RPC or load through settings.json).

    I don’t think this case is really more obscure. Creating a wallet and then adding it to the configuration to always load it does not seem like an unusual case. Especially if people want to use a new feature, but then also always load the same wallet when they start the software.

  237. achow101 force-pushed on Oct 8, 2020
  238. hebasto commented at 2:24 pm on October 9, 2020: member
    Concept ACK.
  239. MarcoFalke commented at 3:37 pm on October 9, 2020: member

    There is a leak, which should be suppressed (or fixed) before this is merged:

     0==39670==ERROR: LeakSanitizer: detected memory leaks
     1Indirect leak of 16960 byte(s) in 147 object(s) allocated from:
     2    [#0](/bitcoin-bitcoin/0/) 0x55f2dce75a7d in malloc (/tmp/cirrus-ci-build/ci/scratch/build/bitcoin-x86_64-pc-linux-gnu/src/bitcoind+0x16a3a7d)
     3    [#1](/bitcoin-bitcoin/1/) 0x7fc222afacfa  (/lib/x86_64-linux-gnu/libsqlite3.so.0+0x45cfa)
     4Indirect leak of 800 byte(s) in 1 object(s) allocated from:
     5    [#0](/bitcoin-bitcoin/0/) 0x55f2dce75a7d in malloc (/tmp/cirrus-ci-build/ci/scratch/build/bitcoin-x86_64-pc-linux-gnu/src/bitcoind+0x16a3a7d)
     6    [#1](/bitcoin-bitcoin/1/) 0x7fc222afacfa  (/lib/x86_64-linux-gnu/libsqlite3.so.0+0x45cfa)
     7    [#2](/bitcoin-bitcoin/2/) 0x55f2dde14681 in SQLiteDatabase::SQLiteDatabase(boost::filesystem::path const&, boost::filesystem::path const&, bool) /tmp/cirrus-ci-build/ci/scratch/build/bitcoin-x86_64-pc-linux-gnu/src/wallet/sqlite.cpp:61:5
     8    [#3](/bitcoin-bitcoin/3/) 0x55f2dde2266b in std::unique_ptr<SQLiteDatabase, std::default_delete<SQLiteDatabase> > MakeUnique<SQLiteDatabase, boost::filesystem::path const&, boost::filesystem::path&>(boost::filesystem::path const&, boost::filesystem::path&) /tmp/cirrus-ci-build/ci/scratch/build/bitcoin-x86_64-pc-linux-gnu/src/./util/memory.h:16:35
     9    [#4](/bitcoin-bitcoin/4/) 0x55f2ddd07e96 in MakeDatabase(boost::filesystem::path const&, DatabaseOptions const&, DatabaseStatus&, bilingual_str&) /tmp/cirrus-ci-build/ci/scratch/build/bitcoin-x86_64-pc-linux-gnu/src/wallet/walletdb.cpp:1052:16
    10    [#5](/bitcoin-bitcoin/5/) 0x55f2ddbbed8d in MakeWalletDatabase(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, DatabaseOptions const&, DatabaseStatus&, bilingual_str&) /tmp/cirrus-ci-build/ci/scratch/build/bitcoin-x86_64-pc-linux-gnu/src/wallet/wallet.cpp:3807:12
    11    [#6](/bitcoin-bitcoin/6/) 0x55f2ddbbbe2f in (anonymous namespace)::LoadWalletInternal(interfaces::Chain&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, boost::optional<bool>, DatabaseOptions const&, DatabaseStatus&, bilingual_str&, std::vector<bilingual_str, std::allocator<bilingual_str> >&) /tmp/cirrus-ci-build/ci/scratch/build/bitcoin-x86_64-pc-linux-gnu/src/wallet/wallet.cpp:206:52
    12    [#7](/bitcoin-bitcoin/7/) 0x55f2ddbbb7f0 in LoadWallet(interfaces::Chain&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, boost::optional<bool>, DatabaseOptions const&, DatabaseStatus&, bilingual_str&, std::vector<bilingual_str, std::allocator<bilingual_str> >&) /tmp/cirrus-ci-build/ci/scratch/build/bitcoin-x86_64-pc-linux-gnu/src/wallet/wallet.cpp:241:19
    13    [#8](/bitcoin-bitcoin/8/) 0x55f2ddaafab0 in loadwallet()::$_33::operator()(RPCHelpMan const&, JSONRPCRequest const&) const /tmp/cirrus-ci-build/ci/scratch/build/bitcoin-x86_64-pc-linux-gnu/src/wallet/rpcwallet.cpp:2607:45
    14    [#9](/bitcoin-bitcoin/9/) 0x55f2ddaafab0 in std::_Function_handler<UniValue (RPCHelpMan const&, JSONRPCRequest const&), loadwallet()::$_33>::_M_invoke(std::_Any_data const&, RPCHelpMan const&, JSONRPCRequest const&) /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/std_function.h:285:9
    15    [#10](/bitcoin-bitcoin/10/) 0x55f2dd3507be in std::function<UniValue (RPCHelpMan const&, JSONRPCRequest const&)>::operator()(RPCHelpMan const&, JSONRPCRequest const&) const /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/std_function.h:688:14
    16    [#11](/bitcoin-bitcoin/11/) 0x55f2dd3e640c in std::_Function_handler<bool (JSONRPCRequest const&, UniValue&, bool), CRPCCommand::CRPCCommand(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, RPCHelpMan (*)(), std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >)::'lambda'(JSONRPCRequest const&, UniValue&, bool)>::_M_invoke(std::_Any_data const&, JSONRPCRequest const&, UniValue&, bool&&) /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/std_function.h:285:9
    17    [#12](/bitcoin-bitcoin/12/) 0x55f2dd953e43 in interfaces::(anonymous namespace)::WalletClientImpl::registerRpcs()::'lambda'(JSONRPCRequest const&, UniValue&, bool)::operator()(JSONRPCRequest const&, UniValue&, bool) const /tmp/cirrus-ci-build/ci/scratch/build/bitcoin-x86_64-pc-linux-gnu/src/interfaces/wallet.cpp:504:24
    18    [#13](/bitcoin-bitcoin/13/) 0x55f2dd953e43 in std::_Function_handler<bool (JSONRPCRequest const&, UniValue&, bool), interfaces::(anonymous namespace)::WalletClientImpl::registerRpcs()::'lambda'(JSONRPCRequest const&, UniValue&, bool)>::_M_invoke(std::_Any_data const&, JSONRPCRequest const&, UniValue&, bool&&) /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/std_function.h:285:9
    19    [#14](/bitcoin-bitcoin/14/) 0x55f2dcf9a5dc in std::function<bool (JSONRPCRequest const&, UniValue&, bool)>::operator()(JSONRPCRequest const&, UniValue&, bool) const /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/std_function.h:688:14
    20    [#15](/bitcoin-bitcoin/15/) 0x55f2dcf9264c in interfaces::(anonymous namespace)::RpcHandlerImpl::RpcHandlerImpl(CRPCCommand const&)::'lambda'(JSONRPCRequest const&, UniValue&, bool)::operator()(JSONRPCRequest const&, UniValue&, bool) const /tmp/cirrus-ci-build/ci/scratch/build/bitcoin-x86_64-pc-linux-gnu/src/interfaces/chain.cpp:113:24
    21    [#16](/bitcoin-bitcoin/16/) 0x55f2dcf9264c in std::_Function_handler<bool (JSONRPCRequest const&, UniValue&, bool), interfaces::(anonymous namespace)::RpcHandlerImpl::RpcHandlerImpl(CRPCCommand const&)::'lambda'(JSONRPCRequest const&, UniValue&, bool)>::_M_invoke(std::_Any_data const&, JSONRPCRequest const&, UniValue&, bool&&) /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/std_function.h:285:9
    22    [#17](/bitcoin-bitcoin/17/) 0x55f2dcf9a5dc in std::function<bool (JSONRPCRequest const&, UniValue&, bool)>::operator()(JSONRPCRequest const&, UniValue&, bool) const /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/std_function.h:688:14
    23    [#18](/bitcoin-bitcoin/18/) 0x55f2dd56d3cd in ExecuteCommand(CRPCCommand const&, JSONRPCRequest const&, UniValue&, bool) /tmp/cirrus-ci-build/ci/scratch/build/bitcoin-x86_64-pc-linux-gnu/src/rpc/server.cpp:466:20
    24    [#19](/bitcoin-bitcoin/19/) 0x55f2dd56ccb0 in CRPCTable::execute(JSONRPCRequest const&) const /tmp/cirrus-ci-build/ci/scratch/build/bitcoin-x86_64-pc-linux-gnu/src/rpc/server.cpp:449:17
    25    [#20](/bitcoin-bitcoin/20/) 0x55f2dd876d75 in HTTPReq_JSONRPC(util::Ref const&, HTTPRequest*) /tmp/cirrus-ci-build/ci/scratch/build/bitcoin-x86_64-pc-linux-gnu/src/httprpc.cpp:207:40
    26    [#21](/bitcoin-bitcoin/21/) 0x55f2dd8760b8 in StartHTTPRPC(util::Ref const&)::$_0::operator()(HTTPRequest*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) const /tmp/cirrus-ci-build/ci/scratch/build/bitcoin-x86_64-pc-linux-gnu/src/httprpc.cpp:293:81
    27    [#22](/bitcoin-bitcoin/22/) 0x55f2dd8760b8 in std::_Function_handler<bool (HTTPRequest*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&), StartHTTPRPC(util::Ref const&)::$_0>::_M_invoke(std::_Any_data const&, HTTPRequest*&&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/std_function.h:285:9
    28    [#23](/bitcoin-bitcoin/23/) 0x55f2dd897e76 in HTTPWorkItem::operator()() /tmp/cirrus-ci-build/ci/scratch/build/bitcoin-x86_64-pc-linux-gnu/src/httpserver.cpp:56:9
    29SUMMARY: AddressSanitizer: 17760 byte(s) leaked in 148 allocation(s). 
    
  240. MarcoFalke added the label Needs gitian build on Oct 9, 2020
  241. MarcoFalke added the label Needs Guix build on Oct 9, 2020
  242. achow101 force-pushed on Oct 9, 2020
  243. achow101 commented at 5:57 pm on October 9, 2020: member
    Memory leak should be fixed now.
  244. DrahtBot commented at 6:08 pm on October 10, 2020: member

    Guix builds

    File commit 12a1c3ad1a43634d2a98717e49e3f02c4acea2fe(master) commit c696e443e88ea1c0440bea0bc7e865ead6018afd(master and this pull)
    *-aarch64-linux-gnu-debug.tar.gz 4f65f0a878718595... 51227e2a2266294f...
    *-aarch64-linux-gnu.tar.gz 5390935f30893c37... de4251f2bbb72d00...
    *-arm-linux-gnueabihf-debug.tar.gz b4449a7611a9bf58... d53fe381ca62dbda...
    *-arm-linux-gnueabihf.tar.gz 780182080e0e5223... 29275b35c4d5ec6e...
    *-riscv64-linux-gnu-debug.tar.gz 3f189125750ba691... 402e51f6350e5a9b...
    *-riscv64-linux-gnu.tar.gz 850c06158d187692... 0c2a79f290d216f6...
    *-win-unsigned.tar.gz 28a7d896b720f78a... ce68b654fd0f4d9a...
    *-win64-debug.zip 6622a2eaf0e1d08a... 583d7f3bfab8493a...
    *-win64-setup-unsigned.exe 169a60a48ec3f8e8... 450e7858f19847e1...
    *-win64.zip 7fdfc6a4d7fad0ef... d85e9a10688099d5...
    *-x86_64-linux-gnu-debug.tar.gz bd049e814a77606f... 731f65ff20f9243f...
    *-x86_64-linux-gnu.tar.gz e4f88e9bc7c9238a... 128552606b64ca13...
    *.tar.gz 4ac97295a058753e... 2eeb127fa6d4392d...
    guix_build.log bd2e0fe51024ce8c... d8089e2ba4cd7606...
    guix_build.log.diff 4d744e23deababeb...
  245. DrahtBot removed the label Needs Guix build on Oct 10, 2020
  246. in configure.ac:1180 in 5fc97fa479 outdated
    1174@@ -1175,6 +1175,9 @@ fi
    1175 if test x$enable_wallet != xno; then
    1176     dnl Check for libdb_cxx only if wallet enabled
    1177     BITCOIN_FIND_BDB48
    1178+
    1179+    dnl Check for sqlite3
    1180+    AC_CHECK_HEADERS([sqlite3.h], [AC_CHECK_LIB([sqlite3], [sqlite3_open], [SQLITE_LIBS=-lsqlite3], [AC_MSG_ERROR(sqlite3_open missing from libsqlite3)], [-pthread -lpthread])], [AC_MSG_ERROR(sqlite3.h headers missing)])
    


    hebasto commented at 4:16 am on October 11, 2020:

    5fc97fa479136e61e8869b77ba731900c3233fcf, why not using pkg config:

    0    PKG_CHECK_MODULES([SQLITE], [sqlite3],, [AC_MSG_ERROR([sqlite3 not found.])])
    

    ?


    achow101 commented at 4:10 pm on October 11, 2020:
    Done
  247. in depends/packages/packages.mk:14 in bd18fe80ca outdated
     9@@ -10,7 +10,8 @@ qt_android_packages=qt
    10 qt_darwin_packages=qt
    11 qt_mingw32_packages=qt
    12 
    13-wallet_packages=bdb
    14+bdb_packages=bdb
    15+sqlite_packages=sqlite
    


    hebasto commented at 4:33 am on October 11, 2020:

    bd18fe80cacc25054c561aa8460752b59be0a78c nit: Spaces around = could improve readability (here and in other files in this commit):

    0bdb_packages = bdb
    1sqlite_packages = sqlite
    

    achow101 commented at 3:28 pm on October 11, 2020:
    Meh. The convention in all of these files is no spaces around =.
  248. in depends/packages/sqlite.mk:25 in bd18fe80ca outdated
    20+define $(package)_stage_cmds
    21+  $(MAKE) DESTDIR=$($(package)_staging_dir) install-libLTLIBRARIES install-includeHEADERS install-pkgconfigDATA
    22+endef
    23+
    24+define $(package)_postprocess_cmds
    25+  rm -rf bin share lib/*.la
    


    hebasto commented at 4:51 am on October 11, 2020:

    bd18fe80cacc25054c561aa8460752b59be0a78c As libsqlite3.la is the only target, bin and share subdirs are not created:

    0  rm lib/*.la
    

    achow101 commented at 4:10 pm on October 11, 2020:
    Done
  249. in doc/build-osx.md:82 in 05c427a065 outdated
    78@@ -79,7 +79,7 @@ compiled in `disable-wallet` mode with:
    79 ./configure --disable-wallet
    80 ```
    81 
    82-In this case there is no dependency on Berkeley DB 4.8.
    83+In this case there is no dependency on Berkeley DB 4.8 or on sqlite.
    


    hebasto commented at 4:55 am on October 11, 2020:

    05c427a0654d2fe0907d8a755e74a2e2d0a51f07 nit: Could make the wording consistent with other OS build docs (e.g., doc/build-unix.md):

    0In this case there is no dependency on Berkeley DB 4.8 and SQLite3.
    

    achow101 commented at 4:10 pm on October 11, 2020:
    Done
  250. in doc/build-unix.md:97 in 05c427a065 outdated
    91@@ -91,6 +92,10 @@ pass `--with-incompatible-bdb` to configure.
    92 
    93 Otherwise, you can build from self-compiled `depends` (see above).
    94 
    95+SQLite3 is required for the wallet:
    96+
    97+    sudo apt-get install libsqlite3-dev
    


    hebasto commented at 4:59 am on October 11, 2020:

    05c427a0654d2fe0907d8a755e74a2e2d0a51f07 nit: As this doc is for human, I’d prefer:

    0    sudo apt install libsqlite3-dev
    

    For Fedora the following works:

    0sudo dnf install sqlite-devel
    

    achow101 commented at 4:10 pm on October 11, 2020:
    Done
  251. in doc/dependencies.md:28 in 05c427a065 outdated
    24@@ -25,6 +25,7 @@ These are the dependencies currently used by Bitcoin Core. You can find instruct
    25 | xkbcommon |  |  |  |  | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk) (Linux only) |
    26 | ZeroMQ | [4.3.1](https://github.com/zeromq/libzmq/releases) | 4.0.0 | No |  |  |
    27 | zlib | [1.2.11](https://zlib.net/) |  |  |  | No |
    28+| SQLite 3 | [3.32.1](https://sqlite.org/download.html) |  |  |  |  |
    


    hebasto commented at 5:01 am on October 11, 2020:

    05c427a0654d2fe0907d8a755e74a2e2d0a51f07

    1. Mind keeping the lexicographic order?

    2. Why minimum version is not mentioned?


    achow101 commented at 3:28 pm on October 11, 2020:
    The minimum version is not known.
  252. in doc/files.md:73 in 05c427a065 outdated
    69@@ -70,7 +70,7 @@ Subdirectory | File(s)           | Description
    70 -------------|-------------------|------------
    71 `database/`  | BDB logging files | Part of BDB environment; created at start and deleted on shutdown; a user *must keep it as safe* as personal wallet `wallet.dat`
    72 `./`         | `db.log`          | BDB error file
    73-`./`         | `wallet.dat`      | Personal wallet (BDB) with keys and transactions
    74+`./`         | `wallet.dat`      | Personal wallet with keys and transactions. May be either a BDB or SQLite3 database file.
    


    hebasto commented at 5:12 am on October 11, 2020:

    05c427a0654d2fe0907d8a755e74a2e2d0a51f07

    0`./`         | `wallet.dat`      | Personal wallet with keys and transactions. May be either a Berkeley DB or SQLite3 database file
    

    achow101 commented at 4:10 pm on October 11, 2020:
    Done
  253. hebasto changes_requested
  254. hebasto commented at 5:14 am on October 11, 2020: member
    • 5fc97fa47 Add libsqlite3
    • bd18fe80c Add sqlite to travis and depends
    • 1a2eec03f Add SQLiteDatabase and SQLiteBatch dummy classes
    • e6aca3ab8 Implement SQLiteDatabaseVersion
    • 1ad2d6a39 Constructors, destructors, and relevant private fields for SQLiteDatabase/Batch
    • 70a2c6585 Initialize and Shutdown sqlite3 globals
    • 6720df53b Implement SQLiteDatabase::Open
    • ccfbbdb1d Implement SQLiteDatabase::Close
    • 011fa1e26 Implement SQLiteBatch::Close
    • 83952c599 Add SetupSQLStatements
    • 14b81e521 Implement SQLiteBatch::ReadKey, WriteKey, EraseKey, and HasKey
    • 293bdfcdc Implement SQLiteBatch::StartCursor, ReadAtCursor, and CloseCursor
    • 8eee65c46 Implement SQLiteDatabase::Backup
    • 7832646a7 Implement SQLiteDatabase::TxnBegin, TxnCommit, and TxnAbort
    • 336646136 Implement SQLiteDatabase::Rewrite
    • c3aeda3d2 Implement SQLiteDatabase::Verify
    • 9cf37ae6a Implement SQLiteDatabase::MakeBatch
    • c4b6cf7ed Determine wallet file type based on file magic
    • 5f17c73e8 walletutil: Wallets can also be sqlite
    • 66df08765 Use SQLite for descriptor wallets
    • 87e2d29ba Use network magic as sqlite wallet application ID
    • 10ceb039e Set and check the sqlite user version
    • 08420e0fb wallet: Enforce sqlite serialized threading mode
    • 05c427a06 Include sqlite3 in documentation
    • be8739b64 Run dumpwallet for legacy wallets only in wallet_backup.py
    • e680b23e2 Update wallet_multiwallet.py for descriptor and sqlite wallets
  255. in src/wallet/sqlite.cpp:188 in 011fa1e265 outdated
    181@@ -182,6 +182,14 @@ SQLiteBatch::SQLiteBatch(SQLiteDatabase& database, const char* mode)
    182 
    183 void SQLiteBatch::Close()
    184 {
    185+    // If m_db is in a transaction (i.e. not in autocommit mode), then abort the transaction in progress
    186+    if (m_database.m_db && sqlite3_get_autocommit(m_database.m_db) == 0) {
    187+        if (TxnAbort()) {
    188+            LogPrintf("SQLiteBatch: Batch closed unexpectedly without the transaction being explicitly commited or aborted\n");
    


    hebasto commented at 5:21 am on October 11, 2020:

    011fa1e2657d624593bb7f23f697a741f7b51577, typo:

    0            LogPrintf("SQLiteBatch: Batch closed unexpectedly without the transaction being explicitly committed or aborted\n");
    

    achow101 commented at 4:10 pm on October 11, 2020:
    Done
  256. in src/wallet/sqlite.h:47 in 1a2eec03f0 outdated
    42+/** An instance of this class represents one SQLite3 database.
    43+ **/
    44+class SQLiteDatabase : public WalletDatabase
    45+{
    46+private:
    47+    bool m_mock = false;
    


    hebasto commented at 5:39 am on October 11, 2020:

    1a2eec03f0dc7731d5c199a97db148534ce174a2

    0    const bool m_mock{false};
    

    achow101 commented at 4:10 pm on October 11, 2020:
    Done
  257. in src/wallet/sqlite.h:57 in 1a2eec03f0 outdated
    52+
    53+public:
    54+    SQLiteDatabase() = delete;
    55+
    56+    /** Create DB handle to real database */
    57+    SQLiteDatabase(const fs::path& dir_path, const fs::path& file_path, bool mock=false);
    


    hebasto commented at 5:41 am on October 11, 2020:

    1a2eec03f0dc7731d5c199a97db148534ce174a2, nit – style:

    0    SQLiteDatabase(const fs::path& dir_path, const fs::path& file_path, bool mock = false);
    

    achow101 commented at 4:10 pm on October 11, 2020:
    Done
  258. in src/wallet/sqlite.h:72 in 1a2eec03f0 outdated
    67+    /* These functions are unused */
    68+    void AddRef() override { assert(false); }
    69+    void RemoveRef() override { assert(false); }
    70+
    71+    /** Rewrite the entire database on disk */
    72+    bool Rewrite(const char* skip=nullptr) override;
    


    hebasto commented at 5:42 am on October 11, 2020:

    1a2eec03f0dc7731d5c199a97db148534ce174a2, nit – style:

    0    bool Rewrite(const char* skip = nullptr) override;
    

    achow101 commented at 4:10 pm on October 11, 2020:
    Done
  259. in src/wallet/sqlite.h:19 in 1a2eec03f0 outdated
    14+class SQLiteBatch : public DatabaseBatch
    15+{
    16+private:
    17+    SQLiteDatabase& m_database;
    18+
    19+    bool m_read_only = false;
    


    hebasto commented at 5:45 am on October 11, 2020:

    1a2eec03f0dc7731d5c199a97db148534ce174a2

    0    const bool m_read_only{false};
    

    achow101 commented at 4:10 pm on October 11, 2020:
    Removed this variable.
  260. in src/wallet/sqlite.h:22 in 1a2eec03f0 outdated
    17+    SQLiteDatabase& m_database;
    18+
    19+    bool m_read_only = false;
    20+
    21+    bool ReadKey(CDataStream&& key, CDataStream& value) override;
    22+    bool WriteKey(CDataStream&& key, CDataStream&& value, bool overwrite=true) override;
    


    hebasto commented at 5:46 am on October 11, 2020:

    1a2eec03f0dc7731d5c199a97db148534ce174a2, nit – style:

    0    bool WriteKey(CDataStream&& key, CDataStream&& value, bool overwrite = true) override;
    

    achow101 commented at 4:11 pm on October 11, 2020:
    Done
  261. hebasto changes_requested
  262. in src/wallet/sqlite.cpp:17 in 1a2eec03f0 outdated
    12+#include <stdint.h>
    13+
    14+static const char* DATABASE_FILENAME = "wallet.dat";
    15+
    16+SQLiteDatabase::SQLiteDatabase(const fs::path& dir_path, const fs::path& file_path, bool mock) :
    17+    WalletDatabase(), m_mock(mock), m_dir_path(dir_path.string()), m_file_path(file_path.string())
    


    hebasto commented at 6:04 am on October 11, 2020:

    1a2eec03f0dc7731d5c199a97db148534ce174a2, clang-format suggests

    0SQLiteDatabase::SQLiteDatabase(const fs::path& dir_path, const fs::path& file_path, bool mock)
    1    : WalletDatabase(), m_mock(mock), m_dir_path(dir_path.string()), m_file_path(file_path.string())
    

    achow101 commented at 4:11 pm on October 11, 2020:
    Done
  263. in src/wallet/sqlite.h:56 in 70a2c65854 outdated
    52@@ -53,6 +53,8 @@ class SQLiteDatabase : public WalletDatabase
    53 
    54     const std::string m_file_path;
    55 
    56+    void Cleanup();
    


    hebasto commented at 6:26 am on October 11, 2020:

    70a2c658549c8a0caa3e58dcc7894d2bb8872cd5

    0    void Cleanup() noexcept;
    

    achow101 commented at 4:11 pm on October 11, 2020:
    Done
  264. in src/wallet/sqlite.cpp:68 in 70a2c65854 outdated
    67 SQLiteDatabase::~SQLiteDatabase()
    68+{
    69+    Cleanup();
    70+}
    71+
    72+void SQLiteDatabase::Cleanup()
    


    hebasto commented at 6:26 am on October 11, 2020:

    70a2c658549c8a0caa3e58dcc7894d2bb8872cd5

    0void SQLiteDatabase::Cleanup() noexcept
    

    achow101 commented at 4:11 pm on October 11, 2020:
    Done
  265. in src/wallet/sqlite.cpp:33 in 70a2c65854 outdated
    24+    // From sqlite3_config() documentation for the SQLITE_CONFIG_LOG option:
    25+    // "The void pointer that is the second argument to SQLITE_CONFIG_LOG is passed through as
    26+    // the first parameter to the application-defined logger function whenever that function is
    27+    // invoked."
    28+    // Assert that this is the case:
    29+    assert(arg == nullptr);
    


    hebasto commented at 6:35 am on October 11, 2020:
    70a2c658549c8a0caa3e58dcc7894d2bb8872cd5 nit: Maybe use (here and in other commits) the new Assert (#19277, #19491)?

    achow101 commented at 3:33 pm on October 11, 2020:
    That does the opposite of what we want.
  266. hebasto changes_requested
  267. in src/wallet/sqlite.cpp:215 in 6720df53bd outdated
    91+        int ret = sqlite3_open_v2(m_file_path.c_str(), &m_db, flags, nullptr);
    92+        if (ret != SQLITE_OK) {
    93+            throw std::runtime_error(strprintf("SQLiteDatabase: Failed to open database: %s\n", sqlite3_errstr(ret)));
    94+        }
    95+    }
    96+
    


    hebasto commented at 6:41 am on October 11, 2020:
    6720df53bd5d4e551c101743c4050bd0c7682238 Assert(m_db); ?

    achow101 commented at 3:35 pm on October 11, 2020:
    I think that is unnecessary.
  268. in src/wallet/sqlite.cpp:119 in 6720df53bd outdated
    114+        throw std::runtime_error(strprintf("SQLiteDatabase: Unable to end exclusive lock transaction: %s\n", sqlite3_errstr(ret)));
    115+    }
    116+
    117+    // Make the table for our key-value pairs
    118+    // First check that the main table exists
    119+    bool table_exists;
    


    hebasto commented at 6:45 am on October 11, 2020:
    6720df53bd5d4e551c101743c4050bd0c7682238 nit: Mind moving the table_exists declaration 9 lines down to its first usage?

    achow101 commented at 4:11 pm on October 11, 2020:
    Done
  269. hebasto changes_requested
  270. in src/wallet/sqlite.cpp:70 in 83952c5994 outdated
    65+{
    66+    std::string read_sql = "SELECT value FROM main WHERE key = ?";
    67+    std::string insert_sql = "INSERT INTO main VALUES(?, ?)";
    68+    std::string overwrite_sql = "INSERT OR REPLACE INTO main VALUES(?, ?)";
    69+    std::string delete_sql = "DELETE FROM main WHERE key = ?";
    70+    std::string cursor_sql = "SELECT key, value FROM main";
    


    hebasto commented at 7:02 am on October 11, 2020:
    83952c59940e4b03bf63b48c0f83b460f988d4be Why not declare all strings as const char* const and drop all of the following c_str conversions?

    achow101 commented at 4:11 pm on October 11, 2020:
    Inlined all of these.
  271. in src/wallet/sqlite.cpp:203 in 8eee65c46d outdated
    199+        sqlite3_close(db_copy);
    200+        return false;
    201+    }
    202+    sqlite3_backup* backup = sqlite3_backup_init(db_copy, "main", m_db, "main");
    203+    if (!backup) {
    204+        LogPrintf("SQLiteDatabase::Backup: Unable to begin backup: %s\n", sqlite3_errmsg(m_db));
    


    hebasto commented at 7:06 am on October 11, 2020:

    8eee65c46d3b897fca9e07b092f3714e3bcdb79e

    0        LogPrintf("%s: Unable to begin backup: %s\n", __func__, sqlite3_errmsg(m_db));
    

    achow101 commented at 4:11 pm on October 11, 2020:
    Done
  272. in src/wallet/sqlite.cpp:210 in 8eee65c46d outdated
    206+        return false;
    207+    }
    208+    // Copy all of the pages using -1
    209+    res = sqlite3_backup_step(backup, -1);
    210+    if (res != SQLITE_DONE) {
    211+        LogPrintf("SQLiteDatabase::Backup: Unable to backup: %s\n", sqlite3_errstr(res));
    


    hebasto commented at 7:07 am on October 11, 2020:

    8eee65c46d3b897fca9e07b092f3714e3bcdb79e

    0        LogPrintf("%s: Unable to backup: %s\n", __func__, sqlite3_errstr(res));
    

    achow101 commented at 4:11 pm on October 11, 2020:
    Done
  273. hebasto changes_requested
  274. in src/wallet/sqlite.cpp:512 in c3aeda3d2f outdated
    508@@ -472,7 +509,19 @@ bool ExistsSQLiteDatabase(const fs::path& path)
    509 
    510 std::unique_ptr<SQLiteDatabase> MakeSQLiteDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error)
    511 {
    512-    return MakeUnique<SQLiteDatabase>(path, path / DATABASE_FILENAME);
    513+    fs::path file = path / DATABASE_FILENAME;
    


    hebasto commented at 7:14 am on October 11, 2020:

    c3aeda3d2f7d7b34b8a35ccea478161b0ca2c80d, nit

    0    const fs::path file = path / DATABASE_FILENAME;
    

    achow101 commented at 4:11 pm on October 11, 2020:
    Done

    achow101 commented at 4:11 pm on October 11, 2020:
    Done
  275. in src/wallet/sqlite.cpp:191 in c3aeda3d2f outdated
    141+        }
    142+        std::string str_msg(msg);
    143+        if (str_msg == "ok") {
    144+            continue;
    145+        }
    146+        if (error.original.empty()) {
    


    hebasto commented at 7:19 am on October 11, 2020:

    c3aeda3d2f7d7b34b8a35ccea478161b0ca2c80d

    0        if (error.empty()) {
    

    achow101 commented at 4:11 pm on October 11, 2020:
    Done

    hebasto commented at 4:34 pm on October 11, 2020:
    Not yet :)

    achow101 commented at 4:37 pm on October 11, 2020:
    Oops.
  276. in src/wallet/sqlite.cpp:147 in c3aeda3d2f outdated
    142+        std::string str_msg(msg);
    143+        if (str_msg == "ok") {
    144+            continue;
    145+        }
    146+        if (error.original.empty()) {
    147+            error = _("Failed to verify database\n");
    


    hebasto commented at 7:21 am on October 11, 2020:

    c3aeda3d2f7d7b34b8a35ccea478161b0ca2c80d

    Is it needed to include \n into translatable string?


    achow101 commented at 4:12 pm on October 11, 2020:
    No, made that Untranslated
  277. in src/wallet/sqlite.cpp:183 in c3aeda3d2f outdated
    132+            break;
    133+        } else if (ret != SQLITE_ROW) {
    134+            error = strprintf(_("SQLiteDatabase: Failed to execute statement to verify database: %s"), sqlite3_errstr(ret));
    135+            break;
    136+        }
    137+        const char* msg = (const char*)sqlite3_column_text(stmt, 0);
    


    hebasto commented at 7:30 am on October 11, 2020:

    c3aeda3d2f7d7b34b8a35ccea478161b0ca2c80d

    0        const char* msg = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0));
    

    achow101 commented at 4:12 pm on October 11, 2020:
    Done
  278. in src/wallet/sqlite.cpp:281 in 14b81e521c outdated
    277+        sqlite3_clear_bindings(m_read_stmt);
    278+        sqlite3_reset(m_read_stmt);
    279+        return false;
    280+    }
    281+    // Leftmost column in result is index 0
    282+    const char* data = (const char*)sqlite3_column_blob(m_read_stmt, 0);
    


    hebasto commented at 7:33 am on October 11, 2020:

    14b81e521c310cb616b7920b5625b704ef1e1899

    0    const char* data = reinterpret_cast<const char*>(sqlite3_column_blob(m_read_stmt, 0));
    

    achow101 commented at 4:12 pm on October 11, 2020:
    Done
  279. in src/wallet/sqlite.cpp:399 in 293bdfcdc1 outdated
    396+        LogPrintf("SQLiteBatch::ReadAtCursor: Unable to execute cursor step: %s\n", sqlite3_errstr(res));
    397+        return false;
    398+    }
    399+
    400+    // Leftmost column in result is index 0
    401+    const char* key_data = (const char*)sqlite3_column_blob(m_cursor_stmt, 0);
    


    hebasto commented at 7:34 am on October 11, 2020:

    293bdfcdc1825e1b288ee09584cf26979a4d5900

    0    const char* key_data = reinterpret_cast<const char*>(sqlite3_column_blob(m_cursor_stmt, 0));
    

    achow101 commented at 4:12 pm on October 11, 2020:
    Done
  280. in src/wallet/sqlite.cpp:402 in 293bdfcdc1 outdated
    399+
    400+    // Leftmost column in result is index 0
    401+    const char* key_data = (const char*)sqlite3_column_blob(m_cursor_stmt, 0);
    402+    int key_data_size = sqlite3_column_bytes(m_cursor_stmt, 0);
    403+    key.write(key_data, key_data_size);
    404+    const char* value_data = (const char*)sqlite3_column_blob(m_cursor_stmt, 1);
    


    hebasto commented at 7:35 am on October 11, 2020:

    293bdfcdc1825e1b288ee09584cf26979a4d5900

    0    const char* value_data = reinterpret_cast<const char*>(sqlite3_column_blob(m_cursor_stmt, 1));
    

    achow101 commented at 4:12 pm on October 11, 2020:
    Done
  281. hebasto changes_requested
  282. in src/wallet/sqlite.cpp:508 in c4b6cf7ed7 outdated
    504@@ -505,7 +505,8 @@ bool SQLiteBatch::TxnAbort()
    505 
    506 bool ExistsSQLiteDatabase(const fs::path& path)
    507 {
    508-    return false;
    509+    fs::path file = path / DATABASE_FILENAME;
    


    hebasto commented at 7:44 am on October 11, 2020:

    c4b6cf7ed72490df24a2e2d28d947002f3e6ef16

    0    const fs::path file = path / DATABASE_FILENAME;
    

    achow101 commented at 4:12 pm on October 11, 2020:
    Done
  283. in src/wallet/walletdb.cpp:1017 in c4b6cf7ed7 outdated
    1011@@ -1011,6 +1012,14 @@ std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path& path, const Databas
    1012         if (ExistsBerkeleyDatabase(path)) {
    1013             format = DatabaseFormat::BERKELEY;
    1014         }
    1015+        if (ExistsSQLiteDatabase(path)) {
    1016+            if (format) {
    1017+                error = Untranslated(strprintf("Failed to load database path '%s'. Data is in ambiguous format.", path.string()));
    


    hebasto commented at 7:53 am on October 11, 2020:

    c4b6cf7ed72490df24a2e2d28d947002f3e6ef16

    Drop Untranslated? I don’t think we should mimic the code around here.


    achow101 commented at 3:42 pm on October 11, 2020:
    This should be translated? Why not mimic the code?

    hebasto commented at 3:48 pm on October 11, 2020:

    This should be translated?

    I think so. And other user-faced messages in this function too, but this is out of this PR scope.


    hebasto commented at 3:54 pm on October 11, 2020:
    Probably, it would be better to leave it as is for now, and apply changes in the follow up pull.
  284. DrahtBot commented at 8:01 am on October 11, 2020: member

    Gitian builds

    File commit 12a1c3ad1a43634d2a98717e49e3f02c4acea2fe(master) commit b0ebf88b908191fc78591742ce447cb75a233986(master and this pull)
    *-aarch64-linux-gnu-debug.tar.gz fecb0fbc29aa125d...
    *-aarch64-linux-gnu.tar.gz e90c25982e611c5c...
    *-arm-linux-gnueabihf-debug.tar.gz 6559a6ae4dad8c82...
    *-arm-linux-gnueabihf.tar.gz eed6310fb4871ebf...
    *-osx-unsigned.dmg 35fdc8ee1eab8cc2... 2ea1af4909e756a4...
    *-osx64.tar.gz 1ef84caa4345fb7c... 74ded45146a54cbd...
    *-riscv64-linux-gnu-debug.tar.gz 17a6a35c296bff97...
    *-riscv64-linux-gnu.tar.gz a2b987fc61446248...
    *-win64-debug.zip dfd87b5c51d5eff9...
    *-win64-setup-unsigned.exe 178c07068d74c8f6...
    *-win64.zip 6f5cbab8cf6a5393...
    *-x86_64-linux-gnu-debug.tar.gz 36ecaedc1019ccf8...
    *-x86_64-linux-gnu.tar.gz 4582ea38dab8d285...
    *.tar.gz 4ac97295a058753e... 35d121f0ddba6d64...
    bitcoin-core-linux-0.21-res.yml 5b3aa4bc7fff3993...
    bitcoin-core-osx-0.21-res.yml 0957f7923504cb00... a2e79e272c71e553...
    bitcoin-core-win-0.21-res.yml 6290abc0172f4a35...
    linux-build.log 44539054a98891ad... 0e3c5a0d40f89435...
    osx-build.log bb31f8dd52696bba... aeb74c5954aff93b...
    win-build.log 201a92f648565719... 8ca2725205af2d76...
    bitcoin-core-osx-0.21-res.yml.diff 455118e6b5f334f4...
    linux-build.log.diff 90b3ac044b5f9109...
    osx-build.log.diff e9474113fe28d156...
    win-build.log.diff e05af0e847215789...
  285. DrahtBot removed the label Needs gitian build on Oct 11, 2020
  286. in src/wallet/walletdb.cpp:1042 in c4b6cf7ed7 outdated
    1037@@ -1029,6 +1038,20 @@ std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path& path, const Databas
    1038         return nullptr;
    1039     }
    1040 
    1041+    // A db already exists so format is set, but options also specifies the format, so make sure they agree
    1042+    if (format && options.require_format && *format != options.require_format) {
    


    hebasto commented at 8:02 am on October 11, 2020:

    c4b6cf7ed72490df24a2e2d28d947002f3e6ef16

    We could skip access to format value:

    0    if (format && options.require_format && format != options.require_format) {
    

    achow101 commented at 3:42 pm on October 11, 2020:
    I think it is clearer this way.

    hebasto commented at 3:51 pm on October 11, 2020:
    Is different approach to two comparing variables of the same type really clearer? Up to you :)

    achow101 commented at 4:28 pm on October 11, 2020:
    Oh right, they’re both Optional. Changed.
  287. hebasto changes_requested
  288. in src/wallet/walletutil.cpp:11 in 5f17c73e8b outdated
     7@@ -8,6 +8,7 @@
     8 #include <util/system.h>
     9 
    10 bool ExistsBerkeleyDatabase(const fs::path& path);
    11+bool ExistsSQLiteDatabase(const fs::path& path);
    


    hebasto commented at 8:10 am on October 11, 2020:

    5f17c73e8b890c92d2dc74e0e3bb34d217bc5160

    Why add another declaration instead of #include <wallet/sqlite.h>?


    achow101 commented at 3:43 pm on October 11, 2020:
    To avoid a circular dependency.

    hebasto commented at 3:46 pm on October 11, 2020:

    To avoid a circular dependency.

    Which one? test/lint/lint-circular-dependencies.sh is green for me.


    achow101 commented at 4:32 pm on October 11, 2020:
    I think there was one in a previous iteration of this PR. But I’m going to leave it as is for now.
  289. in src/wallet/sqlite.cpp:138 in 87e2d29bac outdated
    133+    if (ret != SQLITE_ROW) {
    134+        sqlite3_finalize(app_id_stmt);
    135+        error = strprintf(_("SQLiteDatabase: Failed to fetch the application id: %s"), sqlite3_errstr(ret));
    136+        return false;
    137+    }
    138+    uint32_t app_id = (uint32_t)sqlite3_column_int(app_id_stmt, 0);
    


    hebasto commented at 8:15 am on October 11, 2020:

    87e2d29baca9d08e90ed401bf360b9b7e866c62c

    0    uint32_t app_id = static_cast<uint32_t>(sqlite3_column_int(app_id_stmt, 0));
    

    achow101 commented at 4:12 pm on October 11, 2020:
    Done
  290. in src/wallet/sqlite.cpp:249 in 87e2d29bac outdated
    242@@ -219,6 +243,14 @@ void SQLiteDatabase::Open(const char* mode)
    243         if (ret != SQLITE_OK) {
    244             throw std::runtime_error(strprintf("SQLiteDatabase: Failed to enable fullfsync: %s\n", sqlite3_errstr(ret)));
    245         }
    246+
    247+        // Set the application id
    248+        uint32_t app_id = ReadBE32(Params().MessageStart());
    249+        std::string set_app_id = strprintf("PRAGMA application_id = %d", (int32_t)app_id);
    


    hebasto commented at 8:18 am on October 11, 2020:

    87e2d29baca9d08e90ed401bf360b9b7e866c62c

    0        std::string set_app_id = strprintf("PRAGMA application_id = %d", static_cast<int32_t>(app_id));
    

    achow101 commented at 4:12 pm on October 11, 2020:
    Done
  291. in src/wallet/sqlite.cpp:21 in 10ceb039e8 outdated
    17@@ -18,6 +18,7 @@
    18 #include <stdint.h>
    19 
    20 static const char* DATABASE_FILENAME = "wallet.dat";
    21+static const int32_t WALLET_SCHEMA_VERSION = 0;
    


    hebasto commented at 8:24 am on October 11, 2020:

    10ceb039e8361d91a9169da8f68872ad4b564403

    0static constexpr int32_t WALLET_SCHEMA_VERSION = 0;
    

    achow101 commented at 4:12 pm on October 11, 2020:
    Done
  292. in src/wallet/sqlite.cpp:14 in 1a2eec03f0 outdated
     9+#include <util/translation.h>
    10+#include <wallet/db.h>
    11+
    12+#include <stdint.h>
    13+
    14+static const char* DATABASE_FILENAME = "wallet.dat";
    


    hebasto commented at 8:29 am on October 11, 2020:

    1a2eec03f0dc7731d5c199a97db148534ce174a2

    0static const char* const DATABASE_FILENAME = "wallet.dat";
    

    achow101 commented at 4:12 pm on October 11, 2020:
    Done
  293. hebasto changes_requested
  294. hebasto commented at 8:33 am on October 11, 2020: member
    Approach ACK e680b23e22b1c15ee8e42a375a68248e821f76eb.
  295. in src/wallet/sqlite.h:46 in 1a2eec03f0 outdated
    30+    void Flush() override {}
    31+
    32+    void Close() override;
    33+
    34+    bool StartCursor() override;
    35+    bool ReadAtCursor(CDataStream& ssKey, CDataStream& ssValue, bool& complete) override;
    


    meshcollider commented at 8:38 am on October 11, 2020:
    nit: names here are not per style guideline and do not match the .cpp file

    achow101 commented at 4:14 pm on October 11, 2020:
    Done
  296. MarcoFalke commented at 9:12 am on October 11, 2020: member

    Both windows and linux cross builds fail with:

     0Building sqlite...
     1make[1]: Entering directory '/home/ubuntu/build/bitcoin/depends/work/build/x86_64-w64-mingw32/sqlite/3320100-a2b073ad5d5'
     2/bin/bash ./libtool  --tag=CC   --mode=compile x86_64-w64-mingw32-gcc -DPACKAGE_NAME=\"sqlite\" -DPACKAGE_TARNAME=\"sqlite\" -DPACKAGE_VERSION=\"3.32.1\" -DPACKAGE_STRING=\"sqlite\ 3.32.1\" -DPACKAGE_BUGREPORT=\"http://www.sqlite.org\" -DPACKAGE_URL=\"\" -DPACKAGE=\"sqlite\" -DVERSION=\"3.32.1\" -D_FILE_OFFSET_BITS=64 -DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DLT_OBJDIR=\".libs/\" -DHAVE_USLEEP=1 -DHAVE_DECL_STRERROR_R=0 -I.   -I/home/ubuntu/build/bitcoin/depends/x86_64-w64-mingw32/include     -D_REENTRANT=1 -DSQLITE_THREADSAFE=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_RTREE -DSQLITE_ENABLE_GEOPOLY  -pipe -O2     -MT sqlite3.lo -MD -MP -MF .deps/sqlite3.Tpo -c -o sqlite3.lo sqlite3.c
     3libtool: compile:  x86_64-w64-mingw32-gcc -DPACKAGE_NAME=\"sqlite\" -DPACKAGE_TARNAME=\"sqlite\" -DPACKAGE_VERSION=\"3.32.1\" "-DPACKAGE_STRING=\"sqlite 3.32.1\"" -DPACKAGE_BUGREPORT=\"http://www.sqlite.org\" -DPACKAGE_URL=\"\" -DPACKAGE=\"sqlite\" -DVERSION=\"3.32.1\" -D_FILE_OFFSET_BITS=64 -DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DLT_OBJDIR=\".libs/\" -DHAVE_USLEEP=1 -DHAVE_DECL_STRERROR_R=0 -I. -I/home/ubuntu/build/bitcoin/depends/x86_64-w64-mingw32/include -D_REENTRANT=1 -DSQLITE_THREADSAFE=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_RTREE -DSQLITE_ENABLE_GEOPOLY -pipe -O2 -MT sqlite3.lo -MD -MP -MF .deps/sqlite3.Tpo -c sqlite3.c -o sqlite3.o
     4x86_64-w64-mingw32-gcc-posix: error: 3.32.1": No such file or directory
     5Makefile:533: recipe for target 'sqlite3.lo' failed
     6make[1]: *** [sqlite3.lo] Error 1
     7make[1]: Leaving directory '/home/ubuntu/build/bitcoin/depends/work/build/x86_64-w64-mingw32/sqlite/3320100-a2b073ad5d5'
     8funcs.mk:265: recipe for target '/home/ubuntu/build/bitcoin/depends/work/build/x86_64-w64-mingw32/sqlite/3320100-a2b073ad5d5/./.stamp_built' failed
     9make: *** [/home/ubuntu/build/bitcoin/depends/work/build/x86_64-w64-mingw32/sqlite/3320100-a2b073ad5d5/./.stamp_built] Error 2
    10make: Leaving directory '/home/ubuntu/build/bitcoin/depends'
    
  297. in src/wallet/sqlite.cpp:65 in e680b23e22 outdated
    60+        }
    61+    }
    62+
    63+    try {
    64+        Open(""); // mode is unused
    65+    } catch (const std::runtime_error& e) {
    


    MarcoFalke commented at 9:13 am on October 11, 2020:
    0msbuild /p:TrackFileAccess=false build_msvc\bitcoin.sln /m /v:q /nologo
    1C:\projects\bitcoin\src\wallet\sqlite.cpp(65,40): error C2220: the following warning is treated as an error [C:\projects\bitcoin\build_msvc\libbitcoin_wallet\libbitcoin_wallet.vcxproj]
    2C:\projects\bitcoin\src\wallet\sqlite.cpp(65,40): warning C4101: 'e': unreferenced local variable [C:\projects\bitcoin\build_msvc\libbitcoin_wallet\libbitcoin_wallet.vcxproj]
    3Command exited with code 1
    

    hebasto commented at 9:38 am on October 11, 2020:

    This could be worked around with the following patch:

     0--- a/build_msvc/common.init.vcxproj
     1+++ b/build_msvc/common.init.vcxproj
     2@@ -117,7 +117,7 @@
     3       <WarningLevel>Level3</WarningLevel>
     4       <PrecompiledHeader>NotUsing</PrecompiledHeader>
     5       <AdditionalOptions>/utf-8 /std:c++17 %(AdditionalOptions)</AdditionalOptions>
     6-      <DisableSpecificWarnings>4018;4221;4244;4267;4334;4715;4805;4834</DisableSpecificWarnings>
     7+      <DisableSpecificWarnings>4018;4101;4221;4244;4267;4334;4715;4805;4834</DisableSpecificWarnings>
     8       <TreatWarningAsError>true</TreatWarningAsError>
     9       <PreprocessorDefinitions>_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;_SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING;ZMQ_STATIC;NOMINMAX;WIN32;HAVE_CONFIG_H;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_CONSOLE;_WIN32_WINNT=0x0601;_WIN32_IE=0x0501;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
    10       <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;..\..\src\secp256k1\include;..\..\src\leveldb\include;..\..\src\leveldb\helpers\memenv;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
    

    @sipsorcery ?


    sipsorcery commented at 10:50 am on October 11, 2020:

    What’s the reason to not get rid of the unused variable e?

    catch (const std::runtime_error&) { ... }


    achow101 commented at 4:12 pm on October 11, 2020:
    Removed e
  298. in src/wallet/sqlite.cpp:144 in 6720df53bd outdated
    139+        ret = sqlite3_exec(m_db, "CREATE TABLE main(key BLOB PRIMARY KEY NOT NULL, value BLOB NOT NULL)", nullptr, nullptr, nullptr);
    140+        if (ret != SQLITE_OK) {
    141+            throw std::runtime_error(strprintf("SQLiteDatabase: Failed to create new database: %s\n", sqlite3_errstr(ret)));
    142+        }
    143+
    144+        // Enable fullfysnc for the platforms that use it
    


    meshcollider commented at 9:16 am on October 11, 2020:
    nit: fullfsync

    achow101 commented at 4:13 pm on October 11, 2020:
    Done
  299. hebasto commented at 9:27 am on October 11, 2020: member

    @MarcoFalke

    Both windows and linux cross builds fail with:

    Mind providing your setup, as I cannot reproduce fail?

  300. in src/wallet/sqlite.cpp:293 in 14b81e521c outdated
    290 
    291 bool SQLiteBatch::WriteKey(CDataStream&& key, CDataStream&& value, bool overwrite)
    292 {
    293-    return false;
    294+    if (!m_database.m_db) return false;
    295+    if (m_read_only) throw std::runtime_error("Write called on database in read-only mode");
    


    meshcollider commented at 9:36 am on October 11, 2020:
    I thought the database was never in read-only mode after a successful opening?

    achow101 commented at 4:13 pm on October 11, 2020:
    Removed m_read_only and these checks.
  301. MarcoFalke commented at 9:38 am on October 11, 2020: member
    It is the gitian setup
  302. in src/wallet/sqlite.cpp:207 in 8eee65c46d outdated
    203+    if (!backup) {
    204+        LogPrintf("SQLiteDatabase::Backup: Unable to begin backup: %s\n", sqlite3_errmsg(m_db));
    205+        sqlite3_close(db_copy);
    206+        return false;
    207+    }
    208+    // Copy all of the pages using -1
    


    meshcollider commented at 9:53 am on October 11, 2020:
    Comment is a little ambiguously worded, IMO better to say “Specifying -1 causes all pages to be copied "

    achow101 commented at 4:13 pm on October 11, 2020:
    Done
  303. hebasto commented at 10:32 am on October 11, 2020: member

    It is the gitian setup

    Confirm fail, unfortunately.

  304. meshcollider commented at 10:37 am on October 11, 2020: contributor
    • 5fc97fa479136e61e8869b77ba731900c3233fcf Add libsqlite3
    • bd18fe80cacc25054c561aa8460752b59be0a78c Add sqlite to travis and depends
    • 1a2eec03f0dc7731d5c199a97db148534ce174a2 Add SQLiteDatabase and SQLiteBatch dummy classes
    • e6aca3ab82cbb878181db48bc628b2298e67cf26 Implement SQLiteDatabaseVersion
    • 1ad2d6a396017af98a90a667bf4e75e5d452ebb7 Constructors, destructors, and relevant private fields for SQLiteDatabase/Batch
    • 70a2c658549c8a0caa3e58dcc7894d2bb8872cd5 Initialize and Shutdown sqlite3 globals
    • 6720df53bd5d4e551c101743c4050bd0c7682238 Implement SQLiteDatabase::Open
    • ccfbbdb1d8fdedb6058599a6bffd7dff521f5b09 Implement SQLiteDatabase::Close
    • 011fa1e2657d624593bb7f23f697a741f7b51577 Implement SQLiteBatch::Close
    • 83952c59940e4b03bf63b48c0f83b460f988d4be Add SetupSQLStatements
    • 14b81e521c310cb616b7920b5625b704ef1e1899 Implement SQLiteBatch::ReadKey, WriteKey, EraseKey, and HasKey
    • 293bdfcdc1825e1b288ee09584cf26979a4d5900 Implement SQLiteBatch::StartCursor, ReadAtCursor, and CloseCursor
    • 8eee65c46d3b897fca9e07b092f3714e3bcdb79e Implement SQLiteDatabase::Backup
    • 7832646a72abcd6bdac681945fdf139c6666a693 Implement SQLiteDatabase::TxnBegin, TxnCommit, and TxnAbort
    • 336646136065d33d61bd8b336c77c44c609c922b Implement SQLiteDatabase::Rewrite
    • c3aeda3d2f7d7b34b8a35ccea478161b0ca2c80d Implement SQLiteDatabase::Verify
    • 9cf37ae6a6a54c8054c69841d04376bd5b7a3c7b Implement SQLiteDatabase::MakeBatch
    • c4b6cf7ed72490df24a2e2d28d947002f3e6ef16 Determine wallet file type based on file magic
    • 5f17c73e8b890c92d2dc74e0e3bb34d217bc5160 walletutil: Wallets can also be sqlite
    • 66df08765b9510f608307860a42664f02346f56f Use SQLite for descriptor wallets
    • 87e2d29baca9d08e90ed401bf360b9b7e866c62c Use network magic as sqlite wallet application ID
    • 10ceb039e8361d91a9169da8f68872ad4b564403 Set and check the sqlite user version
    • 08420e0fb5638dd3e5f4f1ba615c2935d14a441c wallet: Enforce sqlite serialized threading mode
    • 05c427a0654d2fe0907d8a755e74a2e2d0a51f07 Include sqlite3 in documentation
    • be8739b645132e66d47ed34b20e20fcb9972b0ca Run dumpwallet for legacy wallets only in wallet_backup.py
    • e680b23e22b1c15ee8e42a375a68248e821f76eb Update wallet_multiwallet.py for descriptor and sqlite wallets

    Code review + compiled + functional tests run ACK e680b23e22b1c15ee8e42a375a68248e821f76eb

    Take my review of the build system/lib stuff with a grain of salt as I don’t know much about how that works (mainly in terms of the first commits adding the sqlite3 library).

    There’s probably a little bit too much commit-split, I feel a few small ones could be squashed, but I appreciate the ease of review. Thanks for doing this achow101.

  305. hebasto commented at 10:54 am on October 11, 2020: member

    @MarcoFalke

    For HOST=x86_64-w64-mingw32 the diff between local and gitian logs:

     0@@ -4,7 +4,8 @@
     1 checking whether build environment is sane... yes
     2 checking for x86_64-w64-mingw32-strip... x86_64-w64-mingw32-strip
     3 checking for a thread-safe mkdir -p... /bin/mkdir -p
     4-checking for gawk... gawk
     5+checking for gawk... no
     6+checking for mawk... mawk
     7 checking whether make sets $(MAKE)... yes
     8 checking whether make supports nested variables... yes
     9 checking for style of include used by make... GNU
    10@@ -96,7 +97,7 @@
    11 checking editline/readline.h usability... no
    12 checking editline/readline.h presence... no
    13 checking for editline/readline.h... no
    14-checking for library containing pthread_create... -lpthread
    15+checking for library containing pthread_create... none required
    16 checking for library containing pthread_mutexattr_init... none required
    17 checking for whether to support dynamic extensions... no
    18 checking for library containing log... none required
    
  306. hebasto commented at 1:46 pm on October 11, 2020: member

    @MarcoFalke

    I’ve managed to fix gitian build for Windows with the following patch:

     0--- a/contrib/gitian-descriptors/gitian-win.yml
     1+++ b/contrib/gitian-descriptors/gitian-win.yml
     2@@ -81,7 +81,7 @@ script: |
     3         echo "REAL=\`which -a ${i}-${prog}-posix | grep -v ${WRAP_DIR}/${i}-${prog} | head -1\`" >> ${WRAP_DIR}/${i}-${prog}
     4         echo "export LD_PRELOAD='/usr/\$LIB/faketime/libfaketime.so.1'" >> ${WRAP_DIR}/${i}-${prog}
     5         echo "export FAKETIME=\"$1\"" >> ${WRAP_DIR}/${i}-${prog}
     6-        echo "\$REAL \$@" >> $WRAP_DIR/${i}-${prog}
     7+        echo "\$REAL \"\$@\"" >> $WRAP_DIR/${i}-${prog}
     8         chmod +x ${WRAP_DIR}/${i}-${prog}
     9     done
    10   done
    

    Testing analogous patch for Linux.

  307. hebasto commented at 2:02 pm on October 11, 2020: member
  308. hebasto commented at 2:59 pm on October 11, 2020: member

    The results of gitian builds with the fixup https://github.com/hebasto/bitcoin/commit/149af3c5eff6da055affc695baa063fac631d4ba commit.

    • Linux:
     0Generating report
     1036ba5ede9ee30e3d8bb093f1f66c574550f41385db973c34854a3d1ec38cc10  bitcoin-149af3c5eff6-aarch64-linux-gnu-debug.tar.gz
     266046c1e1cceab6183cf856fdfbf9a1b6df3bc88384aecfc6161e0f354a9ecb4  bitcoin-149af3c5eff6-aarch64-linux-gnu.tar.gz
     3cc8396b34216e3164c609848ea5213cd51e2e2cf4f583233ed1c5e86521cb71d  bitcoin-149af3c5eff6-arm-linux-gnueabihf-debug.tar.gz
     4901293fb993fec5893e7109379c3b6a5a6a6cf1f04251e2b60c42aa4c1244312  bitcoin-149af3c5eff6-arm-linux-gnueabihf.tar.gz
     5475f4c07e0888fadbab1a346e9b39ea2dc50b1825fce7483b67f47e4214f2844  bitcoin-149af3c5eff6-riscv64-linux-gnu-debug.tar.gz
     6fd3973a31bfaebf24c7d9224ed04896628e0b3dc82c2ad6ad86eaeaf4d6f9393  bitcoin-149af3c5eff6-riscv64-linux-gnu.tar.gz
     7dac57bab1455f57b21a64bedd85ed4a9dab176e7d86147b8ab9c5c838fab88de  bitcoin-149af3c5eff6-x86_64-linux-gnu-debug.tar.gz
     88881c1134d4b3739b4cc62cfbe038959b5245f26481c7cd8e5804abd0621cea4  bitcoin-149af3c5eff6-x86_64-linux-gnu.tar.gz
     91b74737382a231f86e4c68c48f91381e9f281465ade4fb684c9b49d38833338b  src/bitcoin-149af3c5eff6.tar.gz
    103bfc49c6a2a1115ed99519ece78f7d23aa13530f01d0c18bc8820eb3fe6afc63  bitcoin-core-linux-0.21-res.yml
    11Done.
    
    • Windows:
    0Generating report
    13f821d58cdef38caae5d4d373244e4d00180ac7308b9cf4802c7e7cf5e765ee6  bitcoin-149af3c5eff6-win-unsigned.tar.gz
    238c2a37be5c1b291ff9769cf9480925292b8d7d7406b8de14a062095dbf4f9b0  bitcoin-149af3c5eff6-win64-debug.zip
    3ddf6699effdb4393aee1c9284b051cd144f807a591a92eeee932d19d1369e637  bitcoin-149af3c5eff6-win64-setup-unsigned.exe
    42fa1f5bad41d7f13f36af3416240f19a50c9194986e1290ba7ef54749d8b29e7  bitcoin-149af3c5eff6-win64.zip
    51b74737382a231f86e4c68c48f91381e9f281465ade4fb684c9b49d38833338b  src/bitcoin-149af3c5eff6.tar.gz
    61001ce6772ef2ea53a232566c51b449ed284d3fc248a80c19b49aa5073e2babd  bitcoin-core-win-0.21-res.yml
    7Done.
    
    • macOS:
    0Generating report
    164b4dafb1c3a53087274d03445e07e6f08331c3eb73ebe3d268051cbfaa5bcce  bitcoin-149af3c5eff6-osx-unsigned.dmg
    257ae97d37eb05b92f9cb25a53e3103b4c2fe7cd6e6bfbcf3715a92c0a229c74d  bitcoin-149af3c5eff6-osx-unsigned.tar.gz
    32a66c1e4731414ba4a466966cf9efde7e4c4732c9906cd486beb24b83401c5d2  bitcoin-149af3c5eff6-osx64.tar.gz
    41b74737382a231f86e4c68c48f91381e9f281465ade4fb684c9b49d38833338b  src/bitcoin-149af3c5eff6.tar.gz
    5520dd59fc43e9f81bd41ef97bfc974cf04cbcc2b59f8e580696ab9ac4c2f602b  bitcoin-core-osx-0.21-res.yml
    6Done.
    
  309. achow101 force-pushed on Oct 11, 2020
  310. achow101 force-pushed on Oct 11, 2020
  311. achow101 commented at 4:14 pm on October 11, 2020: member
    Addressed comments and also pulled in @hebasto’s diff for the gitian build.
  312. achow101 force-pushed on Oct 11, 2020
  313. in src/wallet/sqlite.cpp:86 in 78eb7ade8a outdated
    81+        if ((res = sqlite3_prepare_v2(m_database.m_db, "INSERT INTO main VALUES(?, ?)", -1, &m_insert_stmt, nullptr)) != SQLITE_OK) {
    82+            throw std::runtime_error(strprintf("SQLiteDatabase: Failed to setup SQL statements: %s\n", sqlite3_errstr(res)));
    83+        }
    84+    }
    85+    if (!m_overwrite_stmt) {
    86+        if ((res = sqlite3_prepare_v2(m_database.m_db,"insert or replace into main values(?, ?)", -1, &m_overwrite_stmt, nullptr)) != SQLITE_OK) {
    


    hebasto commented at 4:35 pm on October 11, 2020:

    piconit: a missed space

    0        if ((res = sqlite3_prepare_v2(m_database.m_db, "insert or replace into main values(?, ?)", -1, &m_overwrite_stmt, nullptr)) != SQLITE_OK) {
    

    achow101 commented at 4:39 pm on October 11, 2020:
    Fixed
  314. achow101 force-pushed on Oct 11, 2020
  315. achow101 force-pushed on Oct 11, 2020
  316. hebasto approved
  317. hebasto commented at 6:10 pm on October 11, 2020: member

    ACK 06765509e1f1b1bc0d037eb16363aa5570a256c7, tested basic wallet functionality on Linux Mint 20 (x86_64, both the gitian build and the build against system libs).

    For the sqlite package verified the download path and the downloaded file hash.

    My gitian builds:

    • Linux
     0Generating report
     11feef4ad6511461092238368ae938bd6b1acc0a4a06a29e3a7a7f90418d3c0ca  bitcoin-06765509e1f1-aarch64-linux-gnu-debug.tar.gz
     23ee55bdf5e3d6e8c1bd7d714e1f6ceb878a3076732df0ae0ebb37706ac816b6a  bitcoin-06765509e1f1-aarch64-linux-gnu.tar.gz
     37365d73f6b6c77e9c12dbbceab87bd5c4c74b9cacae8c092151382fc3a5f1836  bitcoin-06765509e1f1-arm-linux-gnueabihf-debug.tar.gz
     436729122b8efade6edb934b89d73415ab3fbc189467a20acee337b67fef4fae7  bitcoin-06765509e1f1-arm-linux-gnueabihf.tar.gz
     572cac920a5df32813071fb4352a76716944252ad75e15f6a6036e73e89524795  bitcoin-06765509e1f1-riscv64-linux-gnu-debug.tar.gz
     66e0cc2651832028ecd03b411e25ca269a6070c566fc1f75918b29680db609a77  bitcoin-06765509e1f1-riscv64-linux-gnu.tar.gz
     755e0f572fa0b69917c332896c9aed9d83a727feebab35f08980c2e77ab002736  bitcoin-06765509e1f1-x86_64-linux-gnu-debug.tar.gz
     8b26ef2057752d18025156be6aba974154e1acbe703908b456e14b16671bfc34b  bitcoin-06765509e1f1-x86_64-linux-gnu.tar.gz
     92b663bb8d2468cfaa823cac67f9eca92b922aa0f90db8a4d0f41a3d5aae1542d  src/bitcoin-06765509e1f1.tar.gz
    1017091a6409fc0162f4f7e17fc4d23fcbed39e3c8f9a473a80132cdddc5a4dbca  bitcoin-core-linux-0.21-res.yml
    11Done.
    
    • Windows:
    0Generating report
    1eb1ee4dbae8ca5d0d75641cf9aaf3b446a85727a3a4ba0e04400356004974098  bitcoin-06765509e1f1-win-unsigned.tar.gz
    27119d7f6e9c244a30e5789df9138f72e6dc2b3492617df5de3f0c2b33eac03c4  bitcoin-06765509e1f1-win64-debug.zip
    328f39e9ecb9713beaccce0d53875ffc794b8f7d445db6c8f22c35c714e0538c5  bitcoin-06765509e1f1-win64-setup-unsigned.exe
    43f1c2cf561acaaacad962d7bc02c073c7cc51a4fd62dbb8856384f2fe8240258  bitcoin-06765509e1f1-win64.zip
    52b663bb8d2468cfaa823cac67f9eca92b922aa0f90db8a4d0f41a3d5aae1542d  src/bitcoin-06765509e1f1.tar.gz
    6ddf8a0c06e861aba537fa2296684cb272fafe0fdee0b3341d001b8eec53625dd  bitcoin-core-win-0.21-res.yml
    7Done.
    
    • macOS
    0Generating report
    1bb876375a152f7950acf52d5443a1bc8f740c31872efc3c3959c9b4ee6cd6b47  bitcoin-06765509e1f1-osx-unsigned.dmg
    22506a18f32aab05bbb9ce77a87cefb174148fd082319718218072dc758d7e892  bitcoin-06765509e1f1-osx-unsigned.tar.gz
    3037df154fc45314b483ad40cd714149dc89c1119c841172bc798473173624ba4  bitcoin-06765509e1f1-osx64.tar.gz
    42b663bb8d2468cfaa823cac67f9eca92b922aa0f90db8a4d0f41a3d5aae1542d  src/bitcoin-06765509e1f1.tar.gz
    5071185053a49c967be64d8fd9327c7033e6defc0b4e8b4c31112bb9c591704d4  bitcoin-core-osx-0.21-res.yml
    6Done.
    

    There are two non-blocking comments:

    1. It would be nice to mention the wallet.dat-journal file in the files.md doc.

    2. Every time a wallet is loaded I can see a database exception message in the log:

    0init message: Loading wallet...
    1...
    2ReadKey: Unable to execute statement :no more rows available
    

    2a. Is it intended to place a space before : rather after it?

  318. MarcoFalke added the label Needs gitian build on Oct 11, 2020
  319. MarcoFalke added the label Needs Guix build on Oct 11, 2020
  320. achow101 force-pushed on Oct 11, 2020
  321. achow101 commented at 6:49 pm on October 11, 2020: member
    1. It would be nice to mention the wallet.dat-journal file in the files.md doc.

    Done.

    1. Every time a wallet is loaded I can see a database exception message in the log:

    Apparently we do a read for a now unused record (read is done for backwards compatibility). Since this isn’t in new sqlite wallets, the read fails. I’ve changed the logging for ReadKey to not log an error when we get SQLITE_DONE which indicates that there were no records found.

    2a. Is it intended to place a space before : rather after it?

    Typo, fixed.

  322. in src/wallet/sqlite.h:90 in 720d017f41 outdated
    85+    bool PeriodicFlush() override { return false; }
    86+    void ReloadDbEnv() override {}
    87+
    88+    void IncrementUpdateCounter() override { ++nUpdateCounter; }
    89+
    90+    std::string Filename() override { return m_file_path; };
    


    promag commented at 10:04 pm on October 11, 2020:
    nit, extra ;.

    achow101 commented at 10:44 pm on October 11, 2020:
    Fixed.
  323. promag commented at 10:28 pm on October 11, 2020: member

    Code review and tested ACK 2978dada84f15ad577d76e146fc3adf18e5e3a19 on macos with sqlite3 from brew.

    I’ve pushed #20125 which I found useful.

  324. achow101 force-pushed on Oct 11, 2020
  325. DrahtBot commented at 11:45 pm on October 11, 2020: member

    Guix builds

    File commit 0b2abaa666d6f3331e3246ffd64dd47946e9dcdf(master) commit c568ff48dac2a2d49fc15a86aca7deb94e2f6723(master and this pull)
    *.tar.gz 026efb7dd6132c32... c63f6e8493c7df93...
    guix_build.log e074b865b6cd933e... 57be22ab3f07219c...
    guix_build.log.diff 2ec91ed3d12bcccd...
  326. DrahtBot removed the label Needs Guix build on Oct 11, 2020
  327. achow101 force-pushed on Oct 11, 2020
  328. achow101 commented at 0:08 am on October 12, 2020: member

    I’ve determined the minimum sqlite version to be 3.7.17, assuming that all versions between that and the latest work if the oldest works. configure.ac and doc/dependencies.md have been updated to reflect this. With 3.7.17, all tests pass and it was able to load a wallet created with 3.32.1.

    Fun fact, the oldest version I was able to download was 3.7.16 and it does not work as the application_id feature was added in 3.7.17.

  329. in configure.ac:1180 in 3eff1f9bec outdated
    1174@@ -1175,6 +1175,9 @@ fi
    1175 if test x$enable_wallet != xno; then
    1176     dnl Check for libdb_cxx only if wallet enabled
    1177     BITCOIN_FIND_BDB48
    1178+
    1179+    dnl Check for sqlite3
    1180+    PKG_CHECK_MODULES([SQLITE], [sqlite3 >= 3.7.17], ,[AC_MSG_ERROR([sqlite3 not found.])])
    


    hebasto commented at 7:53 am on October 12, 2020:

    3eff1f9bec5b92e3cb1e7fa5f0c7e0163b4f4ce2, pico-nit: It’s odd not seeing a space after ,

    0    PKG_CHECK_MODULES([SQLITE], [sqlite3 >= 3.7.17],, [AC_MSG_ERROR([sqlite3 not found.])])
    

    achow101 commented at 2:57 pm on October 12, 2020:
    Done
  330. in depends/packages/sqlite.mk:25 in f3b8ee19a0 outdated
    20+define $(package)_stage_cmds
    21+  $(MAKE) DESTDIR=$($(package)_staging_dir) install-libLTLIBRARIES install-includeHEADERS install-pkgconfigDATA
    22+endef
    23+
    24+define $(package)_postprocess_cmds
    25+  rm -rf lib/*.la
    


    hebasto commented at 7:55 am on October 12, 2020:

    f3b8ee19a09e676762d0584572aee927c4ecaa89, nit: both flags are redundant

    0  rm lib/*.la
    

    achow101 commented at 2:57 pm on October 12, 2020:
    Done
  331. in doc/dependencies.md:24 in 61079ce5f2 outdated
    20@@ -21,6 +21,7 @@ These are the dependencies currently used by Bitcoin Core. You can find instruct
    21 | Python (tests) |  | [3.5](https://www.python.org/downloads) |  |  |  |
    22 | qrencode | [3.4.4](https://fukuchi.org/works/qrencode) |  | No |  |  |
    23 | Qt | [5.9.8](https://download.qt.io/official_releases/qt/) | [5.5.1](https://github.com/bitcoin/bitcoin/issues/13478) | No |  |  |
    24+| SQLite 3 | [3.32.1](https://sqlite.org/download.html) | 3.7.17 |  |  |  |
    


    hebasto commented at 7:57 am on October 12, 2020:

    61079ce5f2ddd22d10edf500c3852fe04eedc912

    0| SQLite 3 | [3.32.1](https://sqlite.org/download.html) | [3.7.17](https://github.com/bitcoin/bitcoin/pull/19077) |  |  |  |
    

    achow101 commented at 2:57 pm on October 12, 2020:
    Done
  332. hebasto approved
  333. hebasto commented at 8:07 am on October 12, 2020: member

    re-ACK db8da464e8c08ee5b2d7fedef9834a3747b223ec, reviewed with git range-diff master 06765509e1f1b1bc0d037eb16363aa5570a256c7 db8da464e8c08ee5b2d7fedef9834a3747b223ec, only suggested changes applied.

    It is nice that we have the minimum required SQLite version now! Besides compatibility, another reasons to choose a particular minimum version are fixed bugs. I’ve skimmed https://sqlite.org/changes.html, and did not find any mention of a fatal bug that could affect our project. Maybe I’ve overlooked it though.

    There is no consistency in our docs about mentioning of SQLite: SQLite 3 and SQLite3 are used. Could we use the only naming, say SQLite?

  334. in src/wallet/sqlite.cpp:266 in f4fd2218d7 outdated
    143+
    144+        // Enable fullfsync for the platforms that use it
    145+        ret = sqlite3_exec(m_db, "PRAGMA fullfsync = true", nullptr, nullptr, nullptr);
    146+        if (ret != SQLITE_OK) {
    147+            throw std::runtime_error(strprintf("SQLiteDatabase: Failed to enable fullfsync: %s\n", sqlite3_errstr(ret)));
    148+        }
    


    hebasto commented at 10:06 am on October 12, 2020:

    f4fd2218d705ed08f8554f9cd1946c087335d2f2

    The F_FULLFSYNC syncing method is supported by macOS only. Do we really want to implement plarform-dependant behavior of a wallet database?


    achow101 commented at 2:54 pm on October 12, 2020:
    fullfsync ensures that the data is written to disk on macOS, so we definitely need it.
  335. meshcollider requested review from ryanofsky on Oct 12, 2020
  336. in src/wallet/sqlite.cpp:234 in f4fd2218d7 outdated
    110+        throw std::runtime_error("SQLiteDatabase: Unable to obtain an exclusive lock on the database, is it being used by another bitcoind?\n");
    111+    }
    112+    ret = sqlite3_exec(m_db, "COMMIT", nullptr, nullptr, nullptr);
    113+    if (ret != SQLITE_OK) {
    114+        throw std::runtime_error(strprintf("SQLiteDatabase: Unable to end exclusive lock transaction: %s\n", sqlite3_errstr(ret)));
    115+    }
    


    hebasto commented at 11:17 am on October 12, 2020:

    f4fd2218d705ed08f8554f9cd1946c087335d2f2

    According to SQLite docs the actual moment of EXCLUSIVE lock acquiring is a bit vague:

    Note that the BEGIN command does not acquire any locks on the database. … No EXCLUSIVE lock is acquired until either the memory cache fills up and must be spilled to disk or until the transaction commits.

    The SQL command “COMMIT” does not actually commit the changes to disk. It just turns autocommit back on. Then, at the conclusion of the command, the regular autocommit logic takes over and causes the actual commit to disk to occur.

    It’s just a note for other reviewers.

  337. DrahtBot commented at 2:22 pm on October 12, 2020: member

    Gitian builds

    File commit 0b2abaa666d6f3331e3246ffd64dd47946e9dcdf(master) commit 3cfdc4b038257ed68d498ed212fe00283a795bb3(master and this pull)
    *-osx-unsigned.dmg 04e4b56842a70884... 4727b379a6c815cf...
    *-osx64.tar.gz 02c2c85b54844873... 40c9080f7b1c2d26...
    *-win64-debug.zip d21a0672ba922e6e... aadee478982cade1...
    *-win64-setup-unsigned.exe 0db743f5f7a53621... e269ce197f50b95d...
    *-win64.zip 96818872c4f2c40a... 79195a2bc6c01b3e...
    *.tar.gz 026efb7dd6132c32... 94efa52d32d02a37...
    bitcoin-core-osx-0.21-res.yml 6201fa404788ede6... 7bbce076a4b6617e...
    bitcoin-core-win-0.21-res.yml b0f1da339914cef9... f401137fcdb4fb87...
    linux-build.log 0369a9837fb6ac9c... 6afc4d0da4f45256...
    osx-build.log 04458efe11ece038... 5c7e96ee77e7739a...
    win-build.log fea78210f07298da... 90693e547affb415...
    bitcoin-core-osx-0.21-res.yml.diff 4d6746fd95015d62...
    bitcoin-core-win-0.21-res.yml.diff 5346fd3000a3518c...
    linux-build.log.diff 70cad77216cc2424...
    osx-build.log.diff 6bc645dea0a146cb...
    win-build.log.diff be823794ecf5a170...
  338. DrahtBot removed the label Needs gitian build on Oct 12, 2020
  339. achow101 commented at 2:57 pm on October 12, 2020: member

    There is no consistency in our docs about mentioning of SQLite: SQLite 3 and SQLite3 are used. Could we use the only naming, say SQLite?

    Should be unified to SQLite now.

  340. achow101 force-pushed on Oct 12, 2020
  341. in src/wallet/sqlite.cpp:127 in 554dac60f1 outdated
    122+    }
    123+    while (true) {
    124+        ret = sqlite3_step(stmt);
    125+        if (ret == SQLITE_DONE) {
    126+            break;
    127+        } else if (ret != SQLITE_ROW) {
    


    hebasto commented at 3:18 pm on October 12, 2020:

    554dac60f160df08f94497a78e3e7a30217a5850, nit: is else required here

    0        if (ret == SQLITE_DONE) break;
    1        if (ret != SQLITE_ROW) {
    

    ?


    achow101 commented at 3:23 pm on October 12, 2020:
    I think that ret != SQLITE_ROW is a sufficient catch-all. We only want to special case SQLITE_DONE.

    hebasto commented at 3:28 pm on October 12, 2020:
    I agree with you. Why do we need else word for that?

    achow101 commented at 3:40 pm on October 12, 2020:
    I guess it isn’t strictly necessary, but it doesn’t really matter.

    hebasto commented at 3:41 pm on October 12, 2020:
    You are right again :)

    achow101 commented at 3:54 pm on October 13, 2020:
    Changed.
  342. hebasto approved
  343. hebasto commented at 3:19 pm on October 12, 2020: member
    re-ACK f00d666e85cee5b347f162cc2a9f56a40e543ee9
  344. in src/wallet/sqlite.cpp:86 in f00d666e85 outdated
    81+        if ((res = sqlite3_prepare_v2(m_database.m_db, "INSERT INTO main VALUES(?, ?)", -1, &m_insert_stmt, nullptr)) != SQLITE_OK) {
    82+            throw std::runtime_error(strprintf("SQLiteDatabase: Failed to setup SQL statements: %s\n", sqlite3_errstr(res)));
    83+        }
    84+    }
    85+    if (!m_overwrite_stmt) {
    86+        if ((res = sqlite3_prepare_v2(m_database.m_db, "insert or replace into main values(?, ?)", -1, &m_overwrite_stmt, nullptr)) != SQLITE_OK) {
    


    meshcollider commented at 9:31 pm on October 12, 2020:
    nit: capitalisation of SQL statement

    achow101 commented at 3:54 pm on October 13, 2020:
    Fixed
  345. meshcollider commented at 9:33 pm on October 12, 2020: contributor

    re-utACK f00d666e85cee5b347f162cc2a9f56a40e543ee9

    Getting very close to merge-ready, we have ACKs from myself, hebasto, promag, S3RK, fjahr, I think a near-ACK from Russ and Sjors, and some review from Marco. Looking forward to merging this in the next couple of days 🥳

  346. in src/wallet/sqlite.cpp:145 in 5d3391a430 outdated
    140+        if (ret != SQLITE_OK) {
    141+            throw std::runtime_error(strprintf("SQLiteDatabase: Failed to create new database: %s\n", sqlite3_errstr(ret)));
    142+        }
    143+
    144+        // Enable fullfsync for the platforms that use it
    145+        ret = sqlite3_exec(m_db, "PRAGMA fullfsync = true", nullptr, nullptr, nullptr);
    


    Sjors commented at 12:37 pm on October 13, 2020:
    It looks like most pragmas are not persistent: http://sqlite.1065341.n5.nabble.com/Which-pragmas-are-persistent-td95287.html (I tested that application_id is persisted, but fullfsync is 0)

    achow101 commented at 3:55 pm on October 13, 2020:
    Moved this to always be run right after the lock is acquired.
  347. Sjors commented at 12:42 pm on October 13, 2020: member

    ACK f00d666e85cee5b347f162cc2a9f56a40e543ee9 modulo PRAGMA non-persistence.

    I suggest we look into the file extension issue one more time before release (i.e. not in this PR).

  348. achow101 force-pushed on Oct 13, 2020
  349. hebasto approved
  350. hebasto commented at 7:02 pm on October 13, 2020: member
    re-ACK d18892dcc3149409296cfcde8a590b5a4d96ea57, reviewed with git range-diff master f00d666e8 d18892dcc.
  351. meshcollider commented at 2:32 am on October 14, 2020: contributor
    @Sjors it seems almost everyone has no preference regarding the extension, as there are pros and cons either way. It seems the easiest thing to do is leave this PR as-is to get it in to the release in time, unless anyone has a good reason to sway the decision.
  352. laanwj referenced this in commit 9efa55c715 on Oct 14, 2020
  353. DrahtBot added the label Needs rebase on Oct 14, 2020
  354. Add libsqlite3 54729f3f4e
  355. Add sqlite to travis and depends e87df82580
  356. Add SQLiteDatabase and SQLiteBatch dummy classes 7577b6e1c8
  357. Implement SQLiteDatabaseVersion ca8b7e04ab
  358. Constructors, destructors, and relevant private fields for SQLiteDatabase/Batch 5a488b3d77
  359. Initialize and Shutdown sqlite3 globals
    sqlite3 recommends that sqlite3_initialize be called when the
    application starts, and sqlite3_shutdown when it stops. Since we don't
    always use sqlite3, we initialize it when a SQLiteDatabse is constructed
    (calling sqlite3_initialize after initialized is a no-op). We call
    sqlite3_shutdown when we see that there are no databases opened. The
    number of open databases is tracked by an atomic g_dbs_open.
    3bfa0fe125
  360. Implement SQLiteDatabase::Open a0de83372b
  361. Implement SQLiteDatabase::Close 93825352a3
  362. Implement SQLiteBatch::Close 6636a2608a
  363. Add SetupSQLStatements 7aa45620e2
  364. Implement SQLiteBatch::ReadKey, WriteKey, EraseKey, and HasKey bf90e033f4
  365. Implement SQLiteBatch::StartCursor, ReadAtCursor, and CloseCursor f6f9cd6a64
  366. Implement SQLiteDatabase::Backup ac5c1617e7
  367. Implement SQLiteDatabase::TxnBegin, TxnCommit, and TxnAbort 010e365906
  368. Implement SQLiteDatabase::Rewrite
    Rewrite uses the VACUUM command which does exactly what we want. A
    specific advertised use case is to compact a database and ensure that
    any deleted data is actually deleted.
    b4df8fdb19
  369. Implement SQLiteDatabase::Verify 727e6b2a4e
  370. Implement SQLiteDatabase::MakeBatch 6045f77003
  371. Determine wallet file type based on file magic ac38a87225
  372. walletutil: Wallets can also be sqlite 9b78f3ce8e
  373. Use SQLite for descriptor wallets
    MakeWalletDatabase no longer has a default DatabaseFormat. Instead
    callers, like CWallet::Create, need to specify the database type to
    create if the file does not exist. If it exists and NONE is given, then
    CreateWalletDatabase will try to autodetect the type.
    9af5de3798
  374. Use network magic as sqlite wallet application ID 9d3d2d263c
  375. Set and check the sqlite user version 6173269866
  376. wallet: Enforce sqlite serialized threading mode f023b7cac0
  377. Include sqlite3 in documentation 6c6639ac9f
  378. Run dumpwallet for legacy wallets only in wallet_backup.py
    Descriptor wallets don't support dumpwallet, so make the tests that do
    dumpwallet legacy wallet only.
    310b0fde04
  379. Update wallet_multiwallet.py for descriptor and sqlite wallets c4a29d0a90
  380. achow101 force-pushed on Oct 14, 2020
  381. achow101 commented at 3:34 pm on October 14, 2020: member
    Rebased. There was a silent merge conflict with #20130. @hebasto @S3RK @promag @Sjors @ryanofsky @meshcollider @fjahr Could you all re-review/re-ACK this so we can merge it before the feature freeze tomorrow?
  382. hebasto approved
  383. hebasto commented at 3:44 pm on October 14, 2020: member
    re-ACK c4a29d0a90b821c443c10891d9326c534d15cf97, only rebased since my previous review, verified with git range-diff master d18892dcc c4a29d0a9.
  384. in doc/files.md:77 in 6c6639ac9f outdated
    71@@ -72,8 +72,9 @@ Subdirectory | File(s)           | Description
    72 -------------|-------------------|------------
    73 `database/`  | BDB logging files | Part of BDB environment; created at start and deleted on shutdown; a user *must keep it as safe* as personal wallet `wallet.dat`
    74 `./`         | `db.log`          | BDB error file
    75-`./`         | `wallet.dat`      | Personal wallet (BDB) with keys and transactions
    76+`./`         | `wallet.dat`      | Personal wallet with keys and transactions. May be either a Berkeley DB or SQLite database file.
    77 `./`         | `.walletlock`     | Wallet lock file
    78+`./`         | `wallet.dat-journal` | SQLite Rollback Journal file for `wallet.dat`. Usually created at start and deleted on shutdown. A user *must keep it as safe* as the `wallet.dat` file.
    


    ryanofsky commented at 4:15 pm on October 14, 2020:

    In commit “Include sqlite3 in documentation” (6c6639ac9f6e1677da066cf809f9e3fa4d2e7c32)

    If need to update, there are two corrections that could be made:

    • Line 69 “Wallets are Berkeley DB (BDB) databases” is no longer true
    • Line 76 “Wallet lock file” should say “BDB wallet lock file”

    hebasto commented at 8:42 am on October 15, 2020:
    Addressed in #20152.
  385. DrahtBot removed the label Needs rebase on Oct 14, 2020
  386. ryanofsky approved
  387. ryanofsky commented at 4:28 pm on October 14, 2020: member

    Code review ACK c4a29d0a90b821c443c10891d9326c534d15cf97. I am honestly confused about reasons for locking into wallet.dat again when it’s so easy now to use a clean format. I assume I’m just very dense, or there’s some unstated reason, because the only thing that’s been brought up are unrealistic compatibility scenarios (all require actively creating a wallet with non-default descriptor+sqlite option, then trying to using the descriptor+sqlite wallets with old software or scripts and ignoring the results) that we didn’t pay attention to with previous PRs like #11687, which did not require any active interfaction.

    Changes since previous review were: using pkg-config, naming/whitespace/documentation/quoting/const/log/assert changes, fixing leak if database constructor throws, enabling unconditional fullsync

  388. Sjors commented at 5:54 pm on October 14, 2020: member
    re-utACK c4a29d0a90b821c443c10891d9326c534d15cf97
  389. promag commented at 8:30 pm on October 14, 2020: member

    Tested ACK c4a29d0a90b821c443c10891d9326c534d15cf97.

    I am honestly confused about reasons for locking into wallet.dat again when it’s so easy now to use a clean format. @ryanofsky clean format as in different file extension? I think it can be discussed and changed even after feature freeze, just like @achow says in OP.

  390. jonatack commented at 10:11 pm on October 14, 2020: member

    ACK c4a29d0a90b821c443c10891d9326c534d15cf97, debug builds and test runs after rebase to latest master @ c2c4dbaebd9, some manual testing creating, using, unloading and reloading a few different new sqlite descriptor wallets over several node restarts/shutdowns.

    Nice work and reviewing getting this in shape. Looking forward to testing this further.

    02020-10-14T21:54:12Z Using SQLite Version 3.33.0
    
  391. fjahr commented at 11:40 pm on October 14, 2020: member
    reACK c4a29d0a90b821c443c10891d9326c534d15cf97
  392. S3RK commented at 2:11 am on October 15, 2020: member
    Re-review ACK c4a29d0a90b821c443c10891d9326c534d15cf97 And thanks for fixing the silent merge conflict
  393. fanquake assigned meshcollider on Oct 15, 2020
  394. fanquake commented at 3:40 am on October 15, 2020: member
    @meshcollider has declared that he’ll be sorely disappointed if he doesn’t get to pull the merge trigger on this PR.
  395. meshcollider commented at 7:11 am on October 15, 2020: contributor

    re-utACK c4a29d0a90b821c443c10891d9326c534d15cf97

    Let’s go 🚀

  396. meshcollider merged this on Oct 15, 2020
  397. meshcollider closed this on Oct 15, 2020

  398. meshcollider removed this from the "Blockers" column in a project

  399. meshcollider moved this from the "Design" to the "Done" column in a project

  400. sidhujag referenced this in commit ac1143b11d on Oct 16, 2020
  401. sidhujag referenced this in commit c7e926b4cd on Oct 16, 2020
  402. meshcollider referenced this in commit f5bd46a4cc on Oct 19, 2020
  403. sidhujag referenced this in commit b01329c2ec on Oct 19, 2020
  404. fanquake referenced this in commit 67d4643a1a on Oct 27, 2020
  405. sidhujag referenced this in commit 4e8d3dc74d on Oct 27, 2020
  406. in src/wallet/sqlite.cpp:584 in c4a29d0a90
    579+        auto db = MakeUnique<SQLiteDatabase>(path, file);
    580+        if (options.verify && !db->Verify(error)) {
    581+            status = DatabaseStatus::FAILED_VERIFY;
    582+            return nullptr;
    583+        }
    584+        return db;
    


    MarcoFalke commented at 5:21 pm on November 5, 2020:

    Does this return path need to set status = DatabaseStatus::SUCCESS;?

    I am asking because gcc seems to generate binaries that will inject false positives into valgrind. I haven’t found a bug in the code.

    Though, the bdb version also seems to set ::SUCCESS, so it might be appropriate here as well?


    achow101 commented at 5:33 pm on November 5, 2020:
    I suppose it should, but istm it shouldn’t matter. I’m pretty sure we never check status if there isn’t a failure.

    MarcoFalke commented at 5:39 pm on November 5, 2020:
    Jup, we don’t check status if there is no failure. It is just a style question.

    MarcoFalke commented at 7:38 pm on November 5, 2020:
    Also happening with clang, so I’ll try to fix this

    MarcoFalke commented at 7:43 pm on November 5, 2020:
  407. janus referenced this in commit 969bd258e5 on Dec 7, 2020
  408. baby636 approved
  409. deadalnix referenced this in commit 9c7a754d59 on Oct 5, 2021
  410. Fabcien referenced this in commit 67f1d37746 on Nov 26, 2021
  411. Fabcien referenced this in commit 347ff5cb23 on Nov 26, 2021
  412. Fabcien referenced this in commit 688eebd27d on Nov 26, 2021
  413. Fabcien referenced this in commit 57ccdc4b05 on Nov 26, 2021
  414. Fabcien referenced this in commit d5ebe09865 on Nov 26, 2021
  415. Fabcien referenced this in commit 7d374d61e6 on Nov 26, 2021
  416. Fabcien referenced this in commit 6df17019b1 on Nov 26, 2021
  417. Fabcien referenced this in commit cb6d8fadf6 on Nov 26, 2021
  418. Fabcien referenced this in commit 0b1a532696 on Nov 26, 2021
  419. Fabcien referenced this in commit 74697b4554 on Nov 26, 2021
  420. Fabcien referenced this in commit 9621980786 on Dec 6, 2021
  421. Fabcien referenced this in commit 27c1c3ad1f on Dec 6, 2021
  422. Fabcien referenced this in commit b35b9465a2 on Dec 6, 2021
  423. Fabcien referenced this in commit 67d1d3dc6b on Dec 6, 2021
  424. Fabcien referenced this in commit 4650c91b6b on Dec 6, 2021
  425. PiRK referenced this in commit 7a597058bb on Aug 16, 2022
  426. DrahtBot locked this on Oct 30, 2022

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2024-07-01 10:13 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me