Skip to content

feat(backup)_: local user data backups #6722

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from

Conversation

jrainville
Copy link
Member

@jrainville jrainville commented Jun 27, 2025

Fixes status-im/status-desktop#18248 status-im/status-desktop#18107 status-im/status-desktop#18120 status-im/status-desktop#18122 status-im/status-desktop#18141 status-im/status-desktop#18121

This is the big PR replacing all the smaller ones, since a lot of the early PRs are now semi-obsolete.

Implements all the backend for the local user data backups.

Uses the scaffold @osmaczko created to make it more extendable. Instead of using the old Messenger code for backups, we now use a new controller that let's other modules register to be backed up. This includes the messenger for chats, contacts and communities, the accounts service for settings and the wallet service for watch-only accounts.

I also created new Protobufs for those backups, so that we no longer user the super generic Waku Backup protobuf.

Finally, I added some integration tests and a functional test that does the whole flow.

A lot of cleanups can still be done to reduce duplication, but they can be done in another commit/issue, since the internal of the services can be modified without the backup process being affected, since the protobufs are in place already.

@status-im-auto
Copy link
Member

status-im-auto commented Jun 27, 2025

Jenkins Builds

Click to see older builds (194)
Commit #️⃣ Finished (UTC) Duration Platform Result
✖️ 129ee6c #1 2025-06-27 18:43:31 ~2 min tests 📄log
✔️ 129ee6c #1 2025-06-27 18:43:36 ~2 min macos 📦zip
✔️ 129ee6c #1 2025-06-27 18:44:34 ~3 min android 📦aar
✔️ 129ee6c #1 2025-06-27 18:44:55 ~4 min linux 📦zip
✔️ 129ee6c #1 2025-06-27 18:44:55 ~4 min macos 📦zip
✔️ 129ee6c #1 2025-06-27 18:45:09 ~4 min ios 📦zip
✔️ 129ee6c #1 2025-06-27 18:47:20 ~6 min windows 📦zip
✔️ 129ee6c #1 2025-06-27 18:48:00 ~7 min tests-rpc 📄log
✔️ 129ee6c #1 2025-06-27 18:52:31 ~11 min linux 📦zip
✖️ 771a49b #2 2025-07-02 20:52:40 ~1 min tests 📄log
✔️ 771a49b #2 2025-07-02 20:53:37 ~2 min macos 📦zip
✔️ 771a49b #2 2025-07-02 20:53:57 ~3 min android 📦aar
✔️ 771a49b #2 2025-07-02 20:53:58 ~3 min linux 📦zip
✔️ 771a49b #2 2025-07-02 20:54:41 ~3 min macos 📦zip
✔️ 771a49b #2 2025-07-02 20:55:02 ~4 min ios 📦zip
✔️ 771a49b #2 2025-07-02 20:56:18 ~5 min windows 📦zip
✔️ 771a49b #2 2025-07-02 20:56:30 ~5 min tests-rpc 📄log
✔️ 771a49b #2 2025-07-02 21:01:01 ~10 min linux 📦zip
✖️ f535310 #3 2025-07-03 17:24:52 ~1 min tests 📄log
✔️ f535310 #3 2025-07-03 17:25:22 ~2 min android 📦aar
✔️ f535310 #3 2025-07-03 17:25:41 ~2 min macos 📦zip
✔️ f535310 #3 2025-07-03 17:26:10 ~3 min linux 📦zip
✔️ f535310 #3 2025-07-03 17:26:45 ~3 min macos 📦zip
✔️ f535310 #3 2025-07-03 17:27:02 ~4 min ios 📦zip
✔️ f535310 #3 2025-07-03 17:28:45 ~5 min windows 📦zip
✖️ f535310 #3 2025-07-03 17:28:56 ~5 min tests-rpc 📄log
✔️ f535310 #3 2025-07-03 17:33:08 ~10 min linux 📦zip
✖️ 0b308c6 #4 2025-07-04 13:38:31 ~1 min tests 📄log
✔️ 0b308c6 #4 2025-07-04 13:38:45 ~2 min android 📦aar
✔️ 0b308c6 #4 2025-07-04 13:39:34 ~3 min linux 📦zip
✔️ 0b308c6 #4 2025-07-04 13:39:57 ~3 min macos 📦zip
✔️ 0b308c6 #4 2025-07-04 13:40:36 ~4 min macos 📦zip
✔️ 0b308c6 #4 2025-07-04 13:40:43 ~4 min ios 📦zip
✔️ 0b308c6 #4 2025-07-04 13:41:53 ~5 min windows 📦zip
✖️ 0b308c6 #4 2025-07-04 13:43:05 ~6 min tests-rpc 📄log
✔️ 0b308c6 #4 2025-07-04 13:46:00 ~9 min linux 📦zip
✖️ d2c58d3 #5 2025-07-04 15:43:39 ~1 min tests 📄log
✔️ d2c58d3 #5 2025-07-04 15:44:23 ~2 min android 📦aar
✔️ d2c58d3 #5 2025-07-04 15:44:29 ~2 min macos 📦zip
✔️ d2c58d3 #5 2025-07-04 15:44:46 ~3 min linux 📦zip
✔️ d2c58d3 #5 2025-07-04 15:45:42 ~3 min macos 📦zip
✔️ d2c58d3 #5 2025-07-04 15:45:57 ~4 min ios 📦zip
✔️ d2c58d3 #5 2025-07-04 15:47:33 ~5 min tests-rpc 📄log
✔️ d2c58d3 #5 2025-07-04 15:49:11 ~7 min linux 📦zip
✖️ d471f83 #6 2025-07-04 17:55:57 ~1 min tests 📄log
✔️ d471f83 #6 2025-07-04 17:56:43 ~2 min macos 📦zip
✔️ d471f83 #6 2025-07-04 17:57:03 ~3 min android 📦aar
✔️ d471f83 #6 2025-07-04 17:57:12 ~3 min linux 📦zip
✔️ d471f83 #6 2025-07-04 17:57:51 ~3 min macos 📦zip
✔️ d471f83 #6 2025-07-04 17:58:33 ~4 min ios 📦zip
✔️ d471f83 #5 2025-07-04 17:59:02 ~5 min windows 📦zip
✖️ d471f83 #6 2025-07-04 17:59:29 ~5 min tests-rpc 📄log
✔️ d471f83 #6 2025-07-04 18:01:26 ~7 min linux 📦zip
✖️ 77be02e #7 2025-07-04 20:07:47 ~1 min tests 📄log
✔️ 77be02e #7 2025-07-04 20:08:16 ~2 min android 📦aar
✔️ 77be02e #7 2025-07-04 20:08:38 ~2 min macos 📦zip
✔️ 77be02e #7 2025-07-04 20:09:04 ~3 min linux 📦zip
✔️ 77be02e #7 2025-07-04 20:09:47 ~3 min macos 📦zip
✔️ 77be02e #6 2025-07-04 20:10:38 ~4 min windows 📦zip
✔️ 77be02e #7 2025-07-04 20:10:40 ~4 min ios 📦zip
✖️ 77be02e #7 2025-07-04 20:11:24 ~5 min tests-rpc 📄log
✔️ 77be02e #7 2025-07-04 20:13:20 ~7 min linux 📦zip
✖️ d8b7ba4 #8 2025-07-08 16:35:53 ~1 min tests 📄log
✔️ d8b7ba4 #8 2025-07-08 16:36:39 ~2 min macos 📦zip
✔️ d8b7ba4 #8 2025-07-08 16:37:13 ~3 min android 📦aar
✔️ d8b7ba4 #8 2025-07-08 16:37:17 ~3 min linux 📦zip
✔️ d8b7ba4 #8 2025-07-08 16:37:43 ~3 min macos 📦zip
✔️ d8b7ba4 #8 2025-07-08 16:38:12 ~4 min ios 📦zip
✖️ d8b7ba4 #8 2025-07-08 16:39:30 ~5 min tests-rpc 📄log
✔️ d8b7ba4 #7 2025-07-08 16:43:33 ~9 min windows 📦zip
✔️ d8b7ba4 #8 2025-07-08 16:43:38 ~9 min linux 📦zip
✖️ 2b3c64d #9 2025-07-08 18:44:53 ~1 min tests 📄log
✔️ 2b3c64d #9 2025-07-08 18:45:19 ~2 min android 📦aar
✔️ 2b3c64d #9 2025-07-08 18:45:39 ~2 min macos 📦zip
✔️ 2b3c64d #9 2025-07-08 18:46:14 ~3 min linux 📦zip
✔️ 2b3c64d #9 2025-07-08 18:46:46 ~3 min macos 📦zip
✔️ 2b3c64d #9 2025-07-08 18:47:03 ~4 min ios 📦zip
✖️ 2b3c64d #9 2025-07-08 18:47:54 ~4 min tests-rpc 📄log
✔️ 2b3c64d #8 2025-07-08 18:51:34 ~8 min windows 📦zip
✔️ 2b3c64d #9 2025-07-08 18:52:54 ~9 min linux 📦zip
✖️ 2f83424 #10 2025-07-08 19:27:50 ~1 min tests 📄log
✔️ 2f83424 #10 2025-07-08 19:28:31 ~2 min android 📦aar
✔️ 2f83424 #10 2025-07-08 19:28:39 ~2 min macos 📦zip
✔️ 2f83424 #10 2025-07-08 19:29:01 ~3 min linux 📦zip
✔️ 2f83424 #10 2025-07-08 19:30:12 ~4 min macos 📦zip
✔️ 2f83424 #10 2025-07-08 19:30:13 ~4 min ios 📦zip
✖️ 2f83424 #10 2025-07-08 19:31:21 ~5 min tests-rpc 📄log
✔️ 2f83424 #9 2025-07-08 19:33:07 ~7 min windows 📦zip
✔️ 2f83424 #10 2025-07-08 19:35:30 ~9 min linux 📦zip
✖️ a457699 #11 2025-07-08 20:04:10 ~1 min tests 📄log
✔️ a457699 #11 2025-07-08 20:04:57 ~2 min macos 📦zip
✔️ a457699 #11 2025-07-08 20:05:27 ~3 min linux 📦zip
✔️ a457699 #11 2025-07-08 20:05:31 ~3 min android 📦aar
✔️ a457699 #11 2025-07-08 20:06:02 ~3 min macos 📦zip
✔️ a457699 #11 2025-07-08 20:06:31 ~4 min ios 📦zip
✖️ a457699 #11 2025-07-08 20:07:41 ~5 min tests-rpc 📄log
✔️ a457699 #10 2025-07-08 20:08:53 ~6 min windows 📦zip
✔️ a457699 #11 2025-07-08 20:09:46 ~7 min linux 📦zip
✖️ a86c42c #12 2025-07-09 15:34:06 ~1 min tests 📄log
✔️ a86c42c #12 2025-07-09 15:34:46 ~2 min android 📦aar
✔️ a86c42c #12 2025-07-09 15:34:47 ~2 min linux 📦zip
✔️ a86c42c #12 2025-07-09 15:34:54 ~2 min macos 📦zip
✔️ a86c42c #12 2025-07-09 15:36:20 ~4 min ios 📦zip
✖️ a86c42c #12 2025-07-09 15:37:24 ~5 min tests-rpc 📄log
✔️ a86c42c #11 2025-07-09 15:39:34 ~7 min windows 📦zip
✔️ a86c42c #12 2025-07-09 15:40:17 ~8 min linux 📦zip
✔️ 6cc985b #13 2025-07-09 15:47:51 ~2 min android 📦aar
✖️ 6cc985b #13 2025-07-09 15:47:59 ~2 min tests 📄log
✔️ 6cc985b #13 2025-07-09 15:48:11 ~2 min linux 📦zip
✔️ 6cc985b #13 2025-07-09 15:49:38 ~4 min macos 📦zip
✔️ 6cc985b #13 2025-07-09 15:50:04 ~4 min ios 📦zip
✔️ 6cc985b #12 2025-07-09 15:50:33 ~4 min windows 📦zip
✔️ 6cc985b #14 2025-07-09 15:50:33 ~2 min android 📦aar
✖️ 6cc985b #14 2025-07-09 15:50:40 ~2 min tests 📄log
✔️ 6cc985b #14 2025-07-09 15:50:50 ~2 min linux 📦zip
✖️ 6cc985b #13 2025-07-09 15:51:57 ~6 min tests-rpc 📄log
✔️ 6cc985b #14 2025-07-09 15:52:56 ~3 min macos 📦zip
✔️ 6cc985b #13 2025-07-09 15:54:23 ~8 min linux 📦zip
✔️ 6cc985b #14 2025-07-09 15:55:01 ~4 min ios 📦zip
✔️ 6cc985b #13 2025-07-09 15:55:22 ~4 min windows 📦zip
✖️ 6cc985b #14 2025-07-09 15:56:29 ~4 min tests-rpc 📄log
✔️ 6cc985b #14 2025-07-09 16:02:06 ~7 min linux 📦zip
✔️ b54e6a5 #15 2025-07-09 17:06:51 ~2 min linux 📦zip
✔️ b54e6a5 #15 2025-07-09 17:07:11 ~3 min android 📦aar
✖️ b54e6a5 #15 2025-07-09 17:07:20 ~3 min tests 📄log
✔️ b54e6a5 #15 2025-07-09 17:08:15 ~4 min macos 📦zip
✔️ b54e6a5 #15 2025-07-09 17:08:48 ~4 min ios 📦zip
✔️ b54e6a5 #14 2025-07-09 17:09:18 ~4 min windows 📦zip
✖️ b54e6a5 #15 2025-07-09 17:10:00 ~5 min tests-rpc 📄log
✔️ b54e6a5 #15 2025-07-09 17:12:23 ~8 min linux 📦zip
✔️ 96899be #16 2025-07-09 17:16:36 ~2 min android 📦aar
✖️ 96899be #16 2025-07-09 17:18:15 ~3 min tests 📄log
✔️ 96899be #16 2025-07-09 17:18:55 ~4 min linux 📦zip
✔️ 96899be #16 2025-07-09 17:19:12 ~4 min ios 📦zip
✔️ 96899be #15 2025-07-09 17:19:38 ~5 min windows 📦zip
✖️ 96899be #16 2025-07-09 17:19:43 ~5 min tests-rpc 📄log
✔️ 96899be #16 2025-07-09 17:21:57 ~7 min macos 📦zip
✔️ 96899be #16 2025-07-09 17:22:13 ~7 min linux 📦zip
✖️ 97fb139 #17 2025-07-09 17:58:04 ~1 min tests 📄log
✔️ 97fb139 #17 2025-07-09 17:58:21 ~2 min android 📦aar
✔️ 97fb139 #17 2025-07-09 17:58:39 ~2 min linux 📦zip
✔️ 97fb139 #17 2025-07-09 17:59:56 ~3 min macos 📦zip
✔️ 97fb139 #17 2025-07-09 18:00:24 ~4 min ios 📦zip
✔️ 97fb139 #16 2025-07-09 18:00:52 ~4 min windows 📦zip
✖️ 97fb139 #17 2025-07-09 18:01:18 ~5 min tests-rpc 📄log
✔️ 97fb139 #17 2025-07-09 18:03:58 ~8 min linux 📦zip
✖️ 9d9322c #18 2025-07-09 18:17:01 ~2 min tests 📄log
✔️ 9d9322c #18 2025-07-09 18:17:11 ~2 min android 📦aar
✔️ 9d9322c #18 2025-07-09 18:17:29 ~2 min linux 📦zip
✔️ 9d9322c #18 2025-07-09 18:18:56 ~4 min macos 📦zip
✔️ 9d9322c #18 2025-07-09 18:19:22 ~4 min ios 📦zip
✔️ 9d9322c #17 2025-07-09 18:19:42 ~4 min windows 📦zip
✖️ 9d9322c #18 2025-07-09 18:20:05 ~5 min tests-rpc 📄log
✔️ 9d9322c #18 2025-07-09 18:22:14 ~7 min linux 📦zip
✖️ 5bacf9d #19 2025-07-09 18:26:44 ~1 min tests 📄log
✔️ 5bacf9d #19 2025-07-09 18:26:57 ~2 min android 📦aar
✔️ 5bacf9d #19 2025-07-09 18:27:25 ~2 min linux 📦zip
✔️ 5bacf9d #19 2025-07-09 18:28:55 ~4 min macos 📦zip
✔️ 5bacf9d #19 2025-07-09 18:29:07 ~4 min ios 📦zip
✔️ 5bacf9d #18 2025-07-09 18:29:40 ~4 min windows 📦zip
✖️ 5bacf9d #19 2025-07-09 18:30:25 ~5 min tests-rpc 📄log
✔️ 5bacf9d #19 2025-07-09 18:33:25 ~8 min linux 📦zip
✔️ c16b32a #20 2025-07-09 19:00:46 ~2 min android 📦aar
✔️ c16b32a #20 2025-07-09 19:00:58 ~2 min linux 📦zip
✔️ c16b32a #20 2025-07-09 19:02:22 ~4 min macos 📦zip
✔️ c16b32a #20 2025-07-09 19:02:47 ~4 min ios 📦zip
✔️ c16b32a #19 2025-07-09 19:03:08 ~4 min windows 📦zip
✔️ c16b32a #20 2025-07-09 19:04:05 ~5 min tests-rpc 📄log
✔️ c16b32a #20 2025-07-09 19:06:09 ~7 min linux 📦zip
✖️ c16b32a #20 2025-07-09 19:23:31 ~25 min tests 📄log
✔️ 5d97dd8 #21 2025-07-10 16:38:15 ~2 min linux 📦zip
✔️ 5d97dd8 #21 2025-07-10 16:38:48 ~3 min android 📦aar
✔️ 5d97dd8 #21 2025-07-10 16:38:54 ~3 min macos 📦zip
✔️ 5d97dd8 #21 2025-07-10 16:40:18 ~4 min ios 📦zip
✔️ 5d97dd8 #20 2025-07-10 16:40:55 ~5 min windows 📦zip
✔️ 5d97dd8 #21 2025-07-10 16:41:37 ~5 min tests-rpc 📄log
✔️ 5d97dd8 #21 2025-07-10 16:46:11 ~10 min linux 📦zip
✖️ 5d97dd8 #21 2025-07-10 17:00:49 ~25 min tests 📄log
✔️ 7c8f3fb #22 2025-07-10 18:13:37 ~2 min linux 📦zip
✔️ 7c8f3fb #22 2025-07-10 18:13:49 ~2 min android 📦aar
✔️ 7c8f3fb #22 2025-07-10 18:14:07 ~3 min macos 📦zip
✔️ 7c8f3fb #22 2025-07-10 18:15:37 ~4 min ios 📦zip
✔️ 7c8f3fb #21 2025-07-10 18:15:51 ~4 min windows 📦zip
✔️ 7c8f3fb #22 2025-07-10 18:16:45 ~5 min tests-rpc 📄log
✔️ 7c8f3fb #22 2025-07-10 18:20:20 ~9 min linux 📦zip
✖️ 7c8f3fb #22 2025-07-10 18:35:45 ~24 min tests 📄log
✔️ 2013e7d #23 2025-07-10 19:04:38 ~2 min linux 📦zip
✖️ 2013e7d #23 2025-07-10 19:04:45 ~2 min tests 📄log
✔️ 2013e7d #23 2025-07-10 19:05:01 ~2 min android 📦aar
✔️ 2013e7d #23 2025-07-10 19:06:14 ~4 min macos 📦zip
✔️ 2013e7d #23 2025-07-10 19:06:41 ~4 min ios 📦zip
✔️ 2013e7d #22 2025-07-10 19:06:58 ~4 min windows 📦zip
✔️ 2013e7d #23 2025-07-10 19:07:37 ~5 min tests-rpc 📄log
✔️ 2013e7d #23 2025-07-10 19:10:12 ~8 min linux 📦zip
Commit #️⃣ Finished (UTC) Duration Platform Result
✖️ 45cf37a #24 2025-07-11 14:03:34 ~2 min tests 📄log
✔️ 45cf37a #24 2025-07-11 14:03:52 ~2 min linux 📦zip
✔️ 45cf37a #24 2025-07-11 14:03:54 ~2 min android 📦aar
✔️ 45cf37a #24 2025-07-11 14:04:20 ~3 min macos 📦zip
✔️ 45cf37a #24 2025-07-11 14:05:27 ~4 min ios 📦zip
✔️ 45cf37a #23 2025-07-11 14:06:11 ~4 min windows 📦zip
✖️ 45cf37a #24 2025-07-11 14:09:37 ~8 min tests-rpc 📄log
✔️ 45cf37a #24 2025-07-11 14:10:32 ~9 min linux 📦zip
✔️ 4f23465 #25 2025-07-11 14:12:33 ~2 min linux 📦zip
✔️ 4f23465 #25 2025-07-11 14:12:58 ~3 min android 📦aar
✔️ 4f23465 #25 2025-07-11 14:13:11 ~3 min macos 📦zip
✔️ 4f23465 #25 2025-07-11 14:14:04 ~4 min ios 📦zip
✔️ 4f23465 #24 2025-07-11 14:14:52 ~4 min windows 📦zip
✔️ 4f23465 #25 2025-07-11 14:16:45 ~6 min tests-rpc 📄log
✔️ 4f23465 #25 2025-07-11 14:17:58 ~7 min linux 📦zip
✖️ 4f23465 #25 2025-07-11 14:35:39 ~25 min tests 📄log
✖️ 4f23465 #26 2025-07-11 15:01:08 ~24 min tests 📄log

if rowsAffected == 0 {
// If no rows were affected, it means the setting does not exist
return errors.New("settings not initalized, please call CreateSettings first")
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Bug" we found with @osmaczko . If the settings are not created at the start (of a test for example), then saveSetting still "works", ie doesn't send an error. It makes debugging the failing test very annoying.

backup := &protobuf.Backup{} // TODO: fill me in more efficient way than waku backup

// TODO is using this for the clock ok?
settings, err := s.prepareSyncSettingsMessages(uint64(time.Now().UnixMilli()), true)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The old code used the clock from clock, chat := m.getLastClockWithRelatedChat().

I think using the time for Now should be ok, since we're exporting Now.


for _, setting := range backup.Settings {
// TODO is it ok to use the messenger here? Otherwise, I have to duplicate a lot of code
err = s.messenger.HandleBackedUpSettings(setting)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm using the messenger's HandleBackedUpSettings here because I'd need to duplicate a lot of code.

In theory HandleBackedUpSettings I could just move, because once we remove the old backup code, only the local backup will use it.

However, HandleBackedUpSettings calls extractAndSaveSyncSetting, which is used fro Sync messages too (paired devices), so the Messenger needs it too.

Thankfully, extractAndSaveSyncSetting doesn't rely on anything Messenger related, so maybe we could extract it to a common place where we only have to pass the needed function to save the setting?

Finally, I'll also be lacking the messengerSignalsHandler. There is probably another way to do it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll create a new issue to modify the code to not use the messenger here.
I'll also address the duplication there

}

// TODO this is a duplicate of the code in messenger_handler. Should it be moved to a common place?
func (s *Service) handleSyncWatchOnlyAccount(message *protobuf.SyncAccount) (*accounts.Account, error) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to the Account Service, I had the issue of having to duplicate code. This Service doesn't have access to the Messenger, so I guess creating a share function would do the trick?

Let me know where and how is the best way to do it

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll create a separate issue to remove the duplication

@igor-sirotin
Copy link
Collaborator

I think it will be simpler to review the full final PR, though it will be a lot indeed.

@jrainville jrainville force-pushed the feat/local-backup-implement-controller branch 4 times, most recently from a86c42c to 6cc985b Compare July 9, 2025 15:45
@jrainville jrainville changed the base branch from feat/backup-path-in-settings to develop July 9, 2025 15:47
Copy link

codecov bot commented Jul 9, 2025

Codecov Report

Attention: Patch coverage is 12.05074% with 416 lines in your changes missing coverage. Please review.

Project coverage is 28.79%. Comparing base (10a9200) to head (6cc985b).

Files with missing lines Patch % Lines
services/wallet/localbackup/service.go 3.70% 104 Missing ⚠️
node/backup/core.go 11.66% 53 Missing ⚠️
node/backup/controller.go 23.43% 47 Missing and 2 partials ⚠️
protocol/messenger_local_backup.go 0.00% 46 Missing ⚠️
services/accounts/service.go 0.00% 40 Missing ⚠️
protocol/messenger_backup_handler.go 0.00% 31 Missing ⚠️
node/get_status_node.go 35.55% 22 Missing and 7 partials ⚠️
mobile/status.go 0.00% 22 Missing ⚠️
protocol/messenger_backup.go 0.00% 12 Missing ⚠️
multiaccounts/settings/database.go 46.66% 7 Missing and 1 partial ⚠️
... and 7 more

❗ There is a different number of reports uploaded between BASE (10a9200) and HEAD (6cc985b). Click for more details.

HEAD has 1 upload less than BASE
Flag BASE (10a9200) HEAD (6cc985b)
unit 1 0
Additional details and impacted files
@@             Coverage Diff              @@
##           develop    #6722       +/-   ##
============================================
- Coverage    59.06%   28.79%   -30.28%     
============================================
  Files          828      796       -32     
  Lines       101562    98874     -2688     
============================================
- Hits         59991    28469    -31522     
- Misses       33998    66047    +32049     
+ Partials      7573     4358     -3215     
Flag Coverage Δ
functional 28.79% <12.05%> (+0.35%) ⬆️
unit ?

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
multiaccounts/settings/columns.go 66.66% <ø> (-22.23%) ⬇️
multiaccounts/settings/structs.go 85.71% <ø> (-7.15%) ⬇️
protocol/messenger_handler.go 25.78% <100.00%> (-33.82%) ⬇️
protocol/protobuf/service.go 66.66% <ø> (ø)
services/accounts/settings.go 7.95% <0.00%> (-0.19%) ⬇️
services/wakuv2ext/service.go 75.00% <0.00%> (-10.72%) ⬇️
services/wallet/service.go 80.59% <50.00%> (-3.53%) ⬇️
api/geth_backend.go 31.73% <0.00%> (-23.52%) ⬇️
protocol/messenger.go 42.95% <20.00%> (-20.22%) ⬇️
protocol/messenger_sync_settings.go 45.45% <50.00%> (-16.59%) ⬇️
... and 11 more

... and 574 files with indirect coverage changes

@jrainville jrainville force-pushed the feat/local-backup-implement-controller branch from 6cc985b to b54e6a5 Compare July 9, 2025 17:03
@jrainville jrainville changed the title WIP: use new controller scaffold for the local backup feat(backup)_: local user data backups Jul 9, 2025
@jrainville jrainville marked this pull request as ready for review July 9, 2025 17:06
@jrainville
Copy link
Member Author

@igor-sirotin @osmaczko ready for review. One PR to rule them all 😅

Copy link
Contributor

@osmaczko osmaczko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🆒 massive work

} else {
backupDir = filepath.Join(n.config.RootDataDir, "backups")
}
fullPath := filepath.Join(backupDir, fmt.Sprintf("%x_user_data.bkp", accountIdentifier[:4]))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is overwriting the backup file each time intentional?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Old ones don't serve any purpose and only clutter. Like the community description, only the latest is useful

continue
}
if err := fn(raw); err != nil {
return fmt.Errorf("load %q failed: %w", name, err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something to reconsider: should the entire process fail if one of the modules cannot be restored?

PrivateKey: crypto.Keccak256(crypto.FromECDSA(privateKey)),
FileNameGetter: filenameGetter,
// TODO set to true to enable the local backup
BackupEnabled: false,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at it now, this can probably be removed. The condition for running the loop should be based on the Interval, if it's provided, the loop should run, otherwise, it shouldn't. In other words, BackupEnabled is redundant and doesn’t sound right in retrospect (I know it’s my own code, lol).

Re: original comment // TODO: check from database when last saved, this still holds, if it is decided to do backup once per week or so.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re: original comment // TODO: check from database when last saved, this still holds, if it is decided to do backup once per week or so.

I don't think it matters to know the last time it was saved. This process is way smaller than the current Waku backup. I currently put it at every 30 minutes even.

We used to backup very rarely because it polluted the network, but now it's local and in the background, so it doesn't affect much. So backing up as often as is realistic is a positive thing

Comment on lines 243 to 253
n.localBackup.Register("settings",
func() ([]byte, error) { return n.accountsSrvc.ExportBackup() },
func(data []byte) error { return n.accountsSrvc.ImportBackup(data) })

n.localBackup.Register("wallet",
func() ([]byte, error) { return n.walletSrvc.LocalBackup().ExportBackup() },
func(data []byte) error { return n.walletSrvc.LocalBackup().ImportBackup(data) })

n.localBackup.Register("messenger",
func() ([]byte, error) { return n.wakuV2ExtSrvc.API().Messenger().ExportBackup() },
func(data []byte) error { return n.wakuV2ExtSrvc.API().Messenger().ImportBackup(data) })
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice

@@ -223,6 +223,10 @@ func (m *Messenger) GetOwnPrimaryName() (string, error) {
return m.settings.DisplayName()
}

func (m *Messenger) GetBackupPath() (string, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should not belong to messenger.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I put it in the Accounts Service instead. Is that ok?

@@ -580,6 +580,7 @@ func (m *Messenger) handleSyncChats(messageState *ReceivedMessageState, chats []
if err != nil {
return err
}
messageState.AllChats.Store(chat.ID, chat)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is already done in m.saveChat.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the messageState, not the m (messenger). I need the messageState to be updated for the response to contains the chat correctly.

I don,t recall the exact use case, but if I didn't add this, I think the chat didn't appear after the backup, I had to reload the app.

Comment on lines +38 to +42
chat.LastClockValue = clock
err = m.saveChat(chat)
if err != nil {
return nil, err
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why ExportBackup is modifying chat's clock?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I copied the code from BackupData.

As I can see, that clock is used to be put inside the backup protobufs of the communities and chats.
So it should help to not import an old backup.

However, I'm pretty sure if the clock is the same as an old one, we also ignore, so I can probably safely delete it. What do you think?

Copy link
Member Author

@jrainville jrainville left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the comments @osmaczko I addressed some of them. I asked a couple of further questions too and I'll start working on the backwards compatibility test

} else {
backupDir = filepath.Join(n.config.RootDataDir, "backups")
}
fullPath := filepath.Join(backupDir, fmt.Sprintf("%x_user_data.bkp", accountIdentifier[:4]))
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Old ones don't serve any purpose and only clutter. Like the community description, only the latest is useful

PrivateKey: crypto.Keccak256(crypto.FromECDSA(privateKey)),
FileNameGetter: filenameGetter,
// TODO set to true to enable the local backup
BackupEnabled: false,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re: original comment // TODO: check from database when last saved, this still holds, if it is decided to do backup once per week or so.

I don't think it matters to know the last time it was saved. This process is way smaller than the current Waku backup. I currently put it at every 30 minutes even.

We used to backup very rarely because it polluted the network, but now it's local and in the background, so it doesn't affect much. So backing up as often as is realistic is a positive thing

@@ -223,6 +223,10 @@ func (m *Messenger) GetOwnPrimaryName() (string, error) {
return m.settings.DisplayName()
}

func (m *Messenger) GetBackupPath() (string, error) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I put it in the Accounts Service instead. Is that ok?

@@ -580,6 +580,7 @@ func (m *Messenger) handleSyncChats(messageState *ReceivedMessageState, chats []
if err != nil {
return err
}
messageState.AllChats.Store(chat.ID, chat)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the messageState, not the m (messenger). I need the messageState to be updated for the response to contains the chat correctly.

I don,t recall the exact use case, but if I didn't add this, I think the chat didn't appear after the backup, I had to reload the app.

Comment on lines +38 to +42
chat.LastClockValue = clock
err = m.saveChat(chat)
if err != nil {
return nil, err
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I copied the code from BackupData.

As I can see, that clock is used to be put inside the backup protobufs of the communities and chats.
So it should help to not import an old backup.

However, I'm pretty sure if the clock is the same as an old one, we also ignore, so I can probably safely delete it. What do you think?

)

// Service is a wallet local backup service.
type Service struct {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe manager would be better? or just backuper even if it's not a real word? 😄

@jrainville jrainville force-pushed the feat/local-backup-implement-controller branch from 2013e7d to 45cf37a Compare July 11, 2025 14:00
Fixes #18248 #18107 #18120 #18122 #18141 #18121

Implements all the backend for the local user data backups.

Uses the scaffold @osmaczko created to make it more extendable. Instead of using the old Messenger code for backups, we now use a new controller that let's other modules register to be backed up. This includes the messenger for chats, contacts and communities, the accounts service for settings and the wallet service for watch-only accounts.

I also created new Protobufs for those backups, so that we no longer user the super generic Waku Backup protobuf.

Finally, I added some integration tests and a functional test that does the whole flow.

A lot of cleanups can still be done to reduce duplication, but they can be done in another commit/issue, since the internal of the services can be modified without the backup process being affected, since the protobufs are in place already.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[LocalBackup] Implement Backup Controller following Patryk's scaffold
4 participants