diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 15c0ad966..89c86af2e 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -192,4 +192,4 @@ jobs:
     - uses: actions/upload-artifact@v4
       with:
         name: ${{ env.OUTPUT }}
-        path: /home/runner/work/monero/monero/${{ env.OUTPUT }}
+        path: /home/runner/work/${{ github.event.repository.name }}/${{ github.event.repository.name }}/${{ env.OUTPUT }}
diff --git a/.github/workflows/copyright.yml b/.github/workflows/copyright.yml
deleted file mode 100644
index 8d2f3cae2..000000000
--- a/.github/workflows/copyright.yml
+++ /dev/null
@@ -1,56 +0,0 @@
-name: ci/gh-actions/copyright
-on:
-  workflow_dispatch:
-  schedule:
-    - cron: '0 0 1 1 *'
-jobs:
-  createPullRequest:
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v4
-      - name: Make changes to pull request
-        continue-on-error: true
-        shell: bash
-        run: |
-          year=$(date +%Y)
-          echo "YEAR=$(echo $year)" >> $GITHUB_ENV
-          find . -print0 | while IFS= read -r -d '' file
-          do
-              if [[ -d $file ]] || [[ $file == *".git"* ]]; then
-                  continue
-              fi
-              line=$(grep .*Copyright.*Monero.* $file || true)
-              if [[ -z $line ]]; then
-                  continue
-              fi
-              fromTo=$(grep -o "[0-9]\{4\}-[0-9]\{4\}" <<< $line || true)
-              if [[ ! -z $fromTo ]]; then
-                  # string contains "FROM-TO"
-                  # we need to replace FROM with current year
-                  to=$(awk '{split($0, array, "-"); print array[2]}' <<< ${fromTo})
-                  repl=${line/"$to"/"$year"}
-              else
-                  # we only have a FROM year
-                  # find occurance of 4 digits
-                  from=$(grep -o "[0-9]\{4\}" <<< $line || true)
-                  fromTo="${from}-${year}"
-                  # replace FROM with FROM-TO
-                  repl=${line/"$from"/"$fromTo"}
-              fi
-              sed -i "s|${line}|${repl}|g" $file
-          done
-      - name: Create Pull Request
-        id: cpr
-        uses: peter-evans/create-pull-request@v4
-        with:
-          token: ${{ secrets.GITHUB_TOKEN }}
-          commit-message: "Copyright: Update to ${{ env.YEAR }}"
-          committer: GitHub <noreply@github.com>
-          author: copyCat <copy@Cat.com>
-          signoff: false
-          branch: bump-copyright-year
-          delete-branch: true
-          title: "Copyright: Update to ${{ env.YEAR }}"
-          body: |
-            Happy new year!
-          draft: false
diff --git a/.github/workflows/depends.yml b/.github/workflows/depends.yml
index 81f3c7b2b..3ee606748 100644
--- a/.github/workflows/depends.yml
+++ b/.github/workflows/depends.yml
@@ -105,9 +105,9 @@ jobs:
         ${{env.CCACHE_SETTINGS}}
         make depends target=${{ matrix.toolchain.host }} -j2
     - uses: actions/upload-artifact@v4
-      if: ${{ matrix.toolchain.host == 'x86_64-w64-mingw32' || matrix.toolchain.host == 'x86_64-apple-darwin' || matrix.toolchain.host == 'x86_64-unknown-linux-gnu' }}
+      if: ${{ matrix.toolchain.host == 'x86_64-w64-mingw32' || matrix.toolchain.host == 'x86_64-apple-darwin' || matrix.toolchain.host == 'aarch64-apple-darwin' || matrix.toolchain.host == 'x86_64-unknown-linux-gnu' }}
       with:
         name: ${{ matrix.toolchain.name }}
         path: |
-          /home/runner/work/monero/monero/build/${{ matrix.toolchain.host }}/release/bin/monero-wallet-cli*
-          /home/runner/work/monero/monero/build/${{ matrix.toolchain.host }}/release/bin/monerod*
+          /home/runner/work/${{ github.event.repository.name }}/${{ github.event.repository.name }}/build/${{ matrix.toolchain.host }}/release/bin/monero-wallet-cli*
+          /home/runner/work/${{ github.event.repository.name }}/${{ github.event.repository.name }}/build/${{ matrix.toolchain.host }}/release/bin/monerod*
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6ffdaeb55..97d6aee81 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 #
 # All rights reserved.
 #
@@ -338,6 +338,11 @@ set(RISCV   1)
 set(RISCV32 1)
 endif()
 
+if(ARCH_ID STREQUAL "loongarch64")
+set(LOONGARCH   1)
+set(LOONGARCH64 1)
+endif()
+
 if(WIN32 OR ARM OR PPC64LE OR PPC64 OR PPC)
   set(OPT_FLAGS_RELEASE "-O2")
 else()
@@ -747,7 +752,7 @@ else()
     message(STATUS "AES support explicitly disabled")
     set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DNO_AES")
     set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DNO_AES")
-  elseif(NOT ARM AND NOT PPC64LE AND NOT PPC64 AND NOT PPC AND NOT S390X AND NOT RISCV)
+  elseif(NOT ARM AND NOT PPC64LE AND NOT PPC64 AND NOT PPC AND NOT S390X AND NOT RISCV AND NOT LOONGARCH)
     message(STATUS "AES support enabled")
     set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -maes")
     set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -maes")
@@ -757,6 +762,8 @@ else()
     message(STATUS "AES support not available on s390x")
   elseif(RISCV)
     message(STATUS "AES support not available on RISC-V")
+  elseif(LOONGARCH)
+    message(STATUS "AES support not available on LOONGARCH")
   elseif(ARM6)
     message(STATUS "AES support not available on ARMv6")
   elseif(ARM7)
diff --git a/CMakeLists_IOS.txt b/CMakeLists_IOS.txt
index f2d44ac88..477e09ebf 100644
--- a/CMakeLists_IOS.txt
+++ b/CMakeLists_IOS.txt
@@ -1,4 +1,4 @@
-# Portions Copyright (c) 2017-2023, The Monero Project
+# Portions Copyright (c) 2017-2024, The Monero Project
 # This file is based off of the https://code.google.com/archive/p/ios-cmake/
 # It has been altered for Monero iOS development
 #
diff --git a/LICENSE b/LICENSE
index 74b9a8118..69cbb4d51 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2014-2023, The Monero Project
+Copyright (c) 2014-2024, The Monero Project
 
 All rights reserved.
 
diff --git a/Makefile b/Makefile
index c0476f0d6..4bf3327ea 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/README.md b/README.md
index 5bde48dd9..931207205 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
 # Monero
 
-Copyright (c) 2014-2023, The Monero Project
+Copyright (c) 2014-2024, The Monero Project
 Portions Copyright (c) 2012-2013 The Cryptonote developers.
 
 ## Table of Contents
@@ -102,9 +102,7 @@ The Bitcoin donation address is:
 Core development funding and/or some supporting services are also graciously provided by [sponsors](https://www.getmonero.org/community/sponsorships/):
 
 [<img width="150" src="https://www.getmonero.org/img/sponsors/tarilabs.png"/>](https://tarilabs.com/)
-[<img width="150" src="https://www.getmonero.org/img/sponsors/globee.png"/>](https://globee.com/)
 [<img width="150" src="https://www.getmonero.org/img/sponsors/symas.png"/>](https://symas.com/)
-[<img width="150" src="https://www.getmonero.org/img/sponsors/forked_logo.png"/>](http://www.forked.net/)
 [<img width="150" src="https://www.getmonero.org/img/sponsors/macstadium.png"/>](https://www.macstadium.com/)
 
 There are also several mining pools that kindly donate a portion of their fees, [a list of them can be found on our Bitcointalk post](https://bitcointalk.org/index.php?topic=583449.0).
diff --git a/cmake/32-bit-toolchain.cmake b/cmake/32-bit-toolchain.cmake
index 0ae8394ab..852b151f2 100644
--- a/cmake/32-bit-toolchain.cmake
+++ b/cmake/32-bit-toolchain.cmake
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 # 
 # All rights reserved.
 # 
diff --git a/cmake/64-bit-toolchain.cmake b/cmake/64-bit-toolchain.cmake
index 1c1a95efd..81e226e3c 100644
--- a/cmake/64-bit-toolchain.cmake
+++ b/cmake/64-bit-toolchain.cmake
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 # 
 # All rights reserved.
 # 
diff --git a/cmake/FindCcache.cmake b/cmake/FindCcache.cmake
index c2cfe9789..f3016ae55 100644
--- a/cmake/FindCcache.cmake
+++ b/cmake/FindCcache.cmake
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/cmake/FindUnbound.cmake b/cmake/FindUnbound.cmake
index e6bfc8e22..9bd38e5b8 100644
--- a/cmake/FindUnbound.cmake
+++ b/cmake/FindUnbound.cmake
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 # All rights reserved.
 # 
 # Redistribution and use in source and binary forms, with or without modification, are
diff --git a/cmake/GitVersion.cmake b/cmake/GitVersion.cmake
index 0dd731881..3934c091a 100644
--- a/cmake/GitVersion.cmake
+++ b/cmake/GitVersion.cmake
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 # 
 # All rights reserved.
 # 
diff --git a/cmake/SetClangTidy.cmake b/cmake/SetClangTidy.cmake
index 7288590bf..7a349b781 100644
--- a/cmake/SetClangTidy.cmake
+++ b/cmake/SetClangTidy.cmake
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/cmake/Version.cmake b/cmake/Version.cmake
index cfb0e32a7..c2c16488c 100644
--- a/cmake/Version.cmake
+++ b/cmake/Version.cmake
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 # 
 # All rights reserved.
 # 
diff --git a/cmake/test-libusb-version.c b/cmake/test-libusb-version.c
index f1e5d8ea8..dd622ebbe 100644
--- a/cmake/test-libusb-version.c
+++ b/cmake/test-libusb-version.c
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/cmake/test-protobuf.cpp b/cmake/test-protobuf.cpp
index 2b658a605..eab6dd90d 100644
--- a/cmake/test-protobuf.cpp
+++ b/cmake/test-protobuf.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/cmake/test-static-assert.c b/cmake/test-static-assert.c
index 6d05d69ff..4889ec0e2 100644
--- a/cmake/test-static-assert.c
+++ b/cmake/test-static-assert.c
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/cmake/test-static-assert.cpp b/cmake/test-static-assert.cpp
index 6d05d69ff..4889ec0e2 100644
--- a/cmake/test-static-assert.cpp
+++ b/cmake/test-static-assert.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/contrib/CMakeLists.txt b/contrib/CMakeLists.txt
index 631d89978..673c6b294 100644
--- a/contrib/CMakeLists.txt
+++ b/contrib/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 # 
 # All rights reserved.
 # 
diff --git a/contrib/depends/packages/openssl.mk b/contrib/depends/packages/openssl.mk
index a9734029d..8c9727fa7 100644
--- a/contrib/depends/packages/openssl.mk
+++ b/contrib/depends/packages/openssl.mk
@@ -41,6 +41,7 @@ $(package)_config_opts_arm_android=--static android-arm
 $(package)_config_opts_aarch64_android=--static android-arm64
 $(package)_config_opts_aarch64_darwin=darwin64-arm64-cc
 $(package)_config_opts_riscv64_linux=linux-generic64
+$(package)_config_opts_loongarch64_linux=linux-generic64
 $(package)_config_opts_mipsel_linux=linux-generic32
 $(package)_config_opts_mips_linux=linux-generic32
 $(package)_config_opts_powerpc_linux=linux-generic32
diff --git a/contrib/depends/toolchain.cmake.in b/contrib/depends/toolchain.cmake.in
index fc502c596..50eaaa593 100644
--- a/contrib/depends/toolchain.cmake.in
+++ b/contrib/depends/toolchain.cmake.in
@@ -147,6 +147,10 @@ if(ARCHITECTURE STREQUAL "riscv64")
   set(ARCH_ID "riscv64")
   set(ARCH "rv64gc")
 endif()
+if(ARCHITECTURE STREQUAL "loongarch64")
+    set(ARCH_ID "loongarch64")
+    set(ARCH "loongarch")
+endif()
 
 if(ARCHITECTURE STREQUAL "i686")
   SET(ARCH_ID "i386")
diff --git a/contrib/epee/CMakeLists.txt b/contrib/epee/CMakeLists.txt
index 9cfd09b5b..b9c2f5918 100644
--- a/contrib/epee/CMakeLists.txt
+++ b/contrib/epee/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 # 
 # All rights reserved.
 # 
diff --git a/contrib/epee/README.md b/contrib/epee/README.md
index 8157d3e56..2937c1264 100644
--- a/contrib/epee/README.md
+++ b/contrib/epee/README.md
@@ -1 +1 @@
-epee -  is a small library of helpers, wrappers, tools and and so on, used to make my life easier. 
+epee -  is a small library of helpers, wrappers, tools and so on, used to make my life easier. 
diff --git a/contrib/epee/include/byte_slice.h b/contrib/epee/include/byte_slice.h
index fa45613e4..0f2bb1158 100644
--- a/contrib/epee/include/byte_slice.h
+++ b/contrib/epee/include/byte_slice.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2023, The Monero Project
+// Copyright (c) 2019-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/contrib/epee/include/byte_stream.h b/contrib/epee/include/byte_stream.h
index 0904f9aa1..66e6d90fd 100644
--- a/contrib/epee/include/byte_stream.h
+++ b/contrib/epee/include/byte_stream.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2020-2023, The Monero Project
+// Copyright (c) 2020-2024, The Monero Project
 
 //
 // All rights reserved.
diff --git a/contrib/epee/include/fnv1.h b/contrib/epee/include/fnv1.h
index 8c11cd187..02659ff29 100644
--- a/contrib/epee/include/fnv1.h
+++ b/contrib/epee/include/fnv1.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 // 
 // All rights reserved.
diff --git a/contrib/epee/include/hex.h b/contrib/epee/include/hex.h
index d27c127c1..22289a8bb 100644
--- a/contrib/epee/include/hex.h
+++ b/contrib/epee/include/hex.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/contrib/epee/include/int-util.h b/contrib/epee/include/int-util.h
index d3fbd84ca..9593882e8 100644
--- a/contrib/epee/include/int-util.h
+++ b/contrib/epee/include/int-util.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/contrib/epee/include/memwipe.h b/contrib/epee/include/memwipe.h
index a27e0bb5e..8a830f837 100644
--- a/contrib/epee/include/memwipe.h
+++ b/contrib/epee/include/memwipe.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/contrib/epee/include/mlocker.h b/contrib/epee/include/mlocker.h
index 7d8e2c7a5..b423e6acb 100644
--- a/contrib/epee/include/mlocker.h
+++ b/contrib/epee/include/mlocker.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 // 
 // All rights reserved.
diff --git a/contrib/epee/include/net/buffer.h b/contrib/epee/include/net/buffer.h
index 8e53512ac..cf07015b0 100644
--- a/contrib/epee/include/net/buffer.h
+++ b/contrib/epee/include/net/buffer.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 // 
 // All rights reserved.
diff --git a/contrib/epee/include/net/connection_basic.hpp b/contrib/epee/include/net/connection_basic.hpp
index e3093de51..f2fbf09b3 100644
--- a/contrib/epee/include/net/connection_basic.hpp
+++ b/contrib/epee/include/net/connection_basic.hpp
@@ -8,7 +8,7 @@
 // ! (how ever if in some wonderful juristdictions that is not the case, then why not make another sub-class withat that members and licence it as epee part)
 // ! Working on above premise, IF this is valid in your juristdictions, then consider this code as released as:
 
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/contrib/epee/include/net/enums.h b/contrib/epee/include/net/enums.h
index 125ef0db0..cb5a5503c 100644
--- a/contrib/epee/include/net/enums.h
+++ b/contrib/epee/include/net/enums.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 //
 // All rights reserved.
diff --git a/contrib/epee/include/net/http_auth.h b/contrib/epee/include/net/http_auth.h
index ee5a79a7d..1a8333f6f 100644
--- a/contrib/epee/include/net/http_auth.h
+++ b/contrib/epee/include/net/http_auth.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/contrib/epee/include/net/network_throttle-detail.hpp b/contrib/epee/include/net/network_throttle-detail.hpp
index 596ef29f5..d97cb9d88 100644
--- a/contrib/epee/include/net/network_throttle-detail.hpp
+++ b/contrib/epee/include/net/network_throttle-detail.hpp
@@ -2,7 +2,7 @@
 /// @author rfree (current maintainer in monero.cc project)
 /// @brief implementaion for throttling of connection (count and rate-limit speed etc)
 
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/contrib/epee/include/net/network_throttle.hpp b/contrib/epee/include/net/network_throttle.hpp
index 90e214aa7..fb39f69fc 100644
--- a/contrib/epee/include/net/network_throttle.hpp
+++ b/contrib/epee/include/net/network_throttle.hpp
@@ -2,7 +2,7 @@
 /// @author rfree (current maintainer in monero.cc project)
 /// @brief interface for throttling of connection (count and rate-limit speed etc)
 
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/contrib/epee/include/rolling_median.h b/contrib/epee/include/rolling_median.h
index c9caddfec..a666303a9 100644
--- a/contrib/epee/include/rolling_median.h
+++ b/contrib/epee/include/rolling_median.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2023, The Monero Project
+// Copyright (c) 2019-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/contrib/epee/include/serialization/wire.h b/contrib/epee/include/serialization/wire.h
index 4dbb0b2f4..8aad677fe 100644
--- a/contrib/epee/include/serialization/wire.h
+++ b/contrib/epee/include/serialization/wire.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2021-2023, The Monero Project
+// Copyright (c) 2021-2024, The Monero Project
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without modification, are
diff --git a/contrib/epee/include/serialization/wire/adapted/list.h b/contrib/epee/include/serialization/wire/adapted/list.h
index 8884193ad..cedfaf5d8 100644
--- a/contrib/epee/include/serialization/wire/adapted/list.h
+++ b/contrib/epee/include/serialization/wire/adapted/list.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2023, The Monero Project
+// Copyright (c) 2023-2024, The Monero Project
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without modification, are
diff --git a/contrib/epee/include/serialization/wire/adapted/vector.h b/contrib/epee/include/serialization/wire/adapted/vector.h
index 1dd4b0ded..0283e9374 100644
--- a/contrib/epee/include/serialization/wire/adapted/vector.h
+++ b/contrib/epee/include/serialization/wire/adapted/vector.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2022-2023-2023, The Monero Project
+// Copyright (c) 2022-2024, The Monero Project
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without modification, are
diff --git a/contrib/epee/include/serialization/wire/error.h b/contrib/epee/include/serialization/wire/error.h
index b4920e53d..b5a3cedf6 100644
--- a/contrib/epee/include/serialization/wire/error.h
+++ b/contrib/epee/include/serialization/wire/error.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2022-2023, The Monero Project
+// Copyright (c) 2022-2024, The Monero Project
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without modification, are
diff --git a/contrib/epee/include/serialization/wire/field.h b/contrib/epee/include/serialization/wire/field.h
index 402fa9ad4..9c7646ec3 100644
--- a/contrib/epee/include/serialization/wire/field.h
+++ b/contrib/epee/include/serialization/wire/field.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2021-2023, The Monero Project
+// Copyright (c) 2021-2024, The Monero Project
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without modification, are
diff --git a/contrib/epee/include/serialization/wire/fwd.h b/contrib/epee/include/serialization/wire/fwd.h
index f9e79dd1a..f5ce34186 100644
--- a/contrib/epee/include/serialization/wire/fwd.h
+++ b/contrib/epee/include/serialization/wire/fwd.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2021-2023, The Monero Project
+// Copyright (c) 2021-2024, The Monero Project
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without modification, are
diff --git a/contrib/epee/include/serialization/wire/traits.h b/contrib/epee/include/serialization/wire/traits.h
index 284506a29..834be2f82 100644
--- a/contrib/epee/include/serialization/wire/traits.h
+++ b/contrib/epee/include/serialization/wire/traits.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2021, The Monero Project
+// Copyright (c) 2021-2024, The Monero Project
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without modification, are
diff --git a/contrib/epee/include/serialization/wire/wrapper/defaulted.h b/contrib/epee/include/serialization/wire/wrapper/defaulted.h
index f9a411c9e..b35567ed9 100644
--- a/contrib/epee/include/serialization/wire/wrapper/defaulted.h
+++ b/contrib/epee/include/serialization/wire/wrapper/defaulted.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2021-2023, The Monero Project
+// Copyright (c) 2021-2024, The Monero Project
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without modification, are
diff --git a/contrib/epee/include/serialization/wire/write.h b/contrib/epee/include/serialization/wire/write.h
index c2359918c..fd116f1a7 100644
--- a/contrib/epee/include/serialization/wire/write.h
+++ b/contrib/epee/include/serialization/wire/write.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2023, The Monero Project
+// Copyright (c) 2023-2024, The Monero Project
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without modification, are
diff --git a/contrib/epee/include/span.h b/contrib/epee/include/span.h
index bbd5d690c..c11e6dd2b 100644
--- a/contrib/epee/include/span.h
+++ b/contrib/epee/include/span.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/contrib/epee/include/storages/portable_storage_bin_utils.h b/contrib/epee/include/storages/portable_storage_bin_utils.h
index d43090251..ad6dc83ea 100644
--- a/contrib/epee/include/storages/portable_storage_bin_utils.h
+++ b/contrib/epee/include/storages/portable_storage_bin_utils.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2023, The Monero Project
+// Copyright (c) 2019-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/contrib/epee/include/string_tools.h b/contrib/epee/include/string_tools.h
index 887f4aa40..6f129908e 100644
--- a/contrib/epee/include/string_tools.h
+++ b/contrib/epee/include/string_tools.h
@@ -71,8 +71,6 @@ namespace string_tools
    std::string get_current_module_path();
 #endif
   void set_module_name_and_folder(const std::string& path_to_process_);
-  void trim_left(std::string& str);
-  void trim_right(std::string& str);
   //----------------------------------------------------------------------------
   inline std::string& trim(std::string& str)
   {
diff --git a/contrib/epee/include/wipeable_string.h b/contrib/epee/include/wipeable_string.h
index bd1e9f12b..0a324c159 100644
--- a/contrib/epee/include/wipeable_string.h
+++ b/contrib/epee/include/wipeable_string.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/contrib/epee/src/CMakeLists.txt b/contrib/epee/src/CMakeLists.txt
index 71ff94f00..bfde1e2e5 100644
--- a/contrib/epee/src/CMakeLists.txt
+++ b/contrib/epee/src/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 # 
 # All rights reserved.
 # 
diff --git a/contrib/epee/src/buffer.cpp b/contrib/epee/src/buffer.cpp
index 7d054c7c2..eb595bcea 100644
--- a/contrib/epee/src/buffer.cpp
+++ b/contrib/epee/src/buffer.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 // 
 // All rights reserved.
diff --git a/contrib/epee/src/byte_slice.cpp b/contrib/epee/src/byte_slice.cpp
index 82ca12f54..ebe3f1d70 100644
--- a/contrib/epee/src/byte_slice.cpp
+++ b/contrib/epee/src/byte_slice.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2023, The Monero Project
+// Copyright (c) 2019-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/contrib/epee/src/byte_stream.cpp b/contrib/epee/src/byte_stream.cpp
index 39166615c..7edd6ef79 100644
--- a/contrib/epee/src/byte_stream.cpp
+++ b/contrib/epee/src/byte_stream.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020-2023, The Monero Project
+// Copyright (c) 2020-2024, The Monero Project
 
 //
 // All rights reserved.
diff --git a/contrib/epee/src/connection_basic.cpp b/contrib/epee/src/connection_basic.cpp
index a42295d88..49d28ce1a 100644
--- a/contrib/epee/src/connection_basic.cpp
+++ b/contrib/epee/src/connection_basic.cpp
@@ -2,7 +2,7 @@
 /// @author rfree (current maintainer in monero.cc project)
 /// @brief base for connection, contains e.g. the ratelimit hooks
 
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/contrib/epee/src/hex.cpp b/contrib/epee/src/hex.cpp
index 625f37c2f..1c6d236dc 100644
--- a/contrib/epee/src/hex.cpp
+++ b/contrib/epee/src/hex.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/contrib/epee/src/http_auth.cpp b/contrib/epee/src/http_auth.cpp
index d6de6a0e1..77d9fe5ca 100644
--- a/contrib/epee/src/http_auth.cpp
+++ b/contrib/epee/src/http_auth.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/contrib/epee/src/int-util.cpp b/contrib/epee/src/int-util.cpp
index a330ff9ac..0cd5095b2 100644
--- a/contrib/epee/src/int-util.cpp
+++ b/contrib/epee/src/int-util.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2023, The Monero Project
+// Copyright (c) 2019-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/contrib/epee/src/levin_base.cpp b/contrib/epee/src/levin_base.cpp
index 111fe6651..963e33fb2 100644
--- a/contrib/epee/src/levin_base.cpp
+++ b/contrib/epee/src/levin_base.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2023, The Monero Project
+// Copyright (c) 2019-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/contrib/epee/src/memwipe.c b/contrib/epee/src/memwipe.c
index 27dfb28a9..d5d9ae1aa 100644
--- a/contrib/epee/src/memwipe.c
+++ b/contrib/epee/src/memwipe.c
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/contrib/epee/src/mlocker.cpp b/contrib/epee/src/mlocker.cpp
index 1bf764f18..1af08fdfb 100644
--- a/contrib/epee/src/mlocker.cpp
+++ b/contrib/epee/src/mlocker.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 // 
 // All rights reserved.
diff --git a/contrib/epee/src/mlog.cpp b/contrib/epee/src/mlog.cpp
index 4ca1a3632..46b535504 100644
--- a/contrib/epee/src/mlog.cpp
+++ b/contrib/epee/src/mlog.cpp
@@ -176,11 +176,12 @@ void mlog_configure(const std::string &filename_base, bool console, const std::s
       std::vector<boost::filesystem::path> found_files;
       const boost::filesystem::directory_iterator end_itr;
       const boost::filesystem::path filename_base_path(filename_base);
+      const std::string filename_base_name = filename_base_path.filename().string();
       const boost::filesystem::path parent_path = filename_base_path.has_parent_path() ? filename_base_path.parent_path() : ".";
       for (boost::filesystem::directory_iterator iter(parent_path); iter != end_itr; ++iter)
       {
-        const std::string filename = iter->path().string();
-        if (filename.size() >= filename_base.size() && std::memcmp(filename.data(), filename_base.data(), filename_base.size()) == 0)
+        const std::string filename = iter->path().filename().string();
+        if (filename.size() >= filename_base_name.size() && std::memcmp(filename.data(), filename_base_name.data(), filename_base_name.size()) == 0)
         {
           found_files.push_back(iter->path());
         }
diff --git a/contrib/epee/src/net_ssl.cpp b/contrib/epee/src/net_ssl.cpp
index 0ad71d9c0..d8a3728ed 100644
--- a/contrib/epee/src/net_ssl.cpp
+++ b/contrib/epee/src/net_ssl.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 // 
 // All rights reserved.
diff --git a/contrib/epee/src/network_throttle-detail.cpp b/contrib/epee/src/network_throttle-detail.cpp
index 56c869d8e..5812679ab 100644
--- a/contrib/epee/src/network_throttle-detail.cpp
+++ b/contrib/epee/src/network_throttle-detail.cpp
@@ -2,7 +2,7 @@
 /// @author rfree (current maintainer in monero.cc project)
 /// @brief implementaion for throttling of connection (count and rate-limit speed etc)
 
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/contrib/epee/src/network_throttle.cpp b/contrib/epee/src/network_throttle.cpp
index bbff08fe8..5561ac116 100644
--- a/contrib/epee/src/network_throttle.cpp
+++ b/contrib/epee/src/network_throttle.cpp
@@ -26,7 +26,7 @@ Throttling work by:
 
 */
 
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/contrib/epee/src/readline_buffer.cpp b/contrib/epee/src/readline_buffer.cpp
index cefde158c..ac68d1fdb 100644
--- a/contrib/epee/src/readline_buffer.cpp
+++ b/contrib/epee/src/readline_buffer.cpp
@@ -1,5 +1,4 @@
 #include "readline_buffer.h"
-#include "string_tools.h"
 #include <readline/readline.h>
 #include <readline/history.h>
 #include <iostream>
@@ -174,7 +173,7 @@ static void handle_line(char* line)
     line_stat = rdln::full;
     the_line = line;
     std::string test_line = line;
-    epee::string_tools::trim_right(test_line);
+    boost::trim_right(test_line);
     if(!test_line.empty())
     {
       if (!same_as_last_line(test_line))
diff --git a/contrib/epee/src/string_tools.cpp b/contrib/epee/src/string_tools.cpp
index d8580c216..525af1c46 100644
--- a/contrib/epee/src/string_tools.cpp
+++ b/contrib/epee/src/string_tools.cpp
@@ -164,19 +164,6 @@ namespace string_tools
   }
 
 	//----------------------------------------------------------------------------
-  void trim_left(std::string& str)
-  {
-    boost::trim_left(str);
-    return;
-  }
-
-	//----------------------------------------------------------------------------
-  void trim_right(std::string& str)
-  {
-    boost::trim_right(str);
-    return;
-  }
-
   std::string pad_string(std::string s, size_t n, char c, bool prepend)
   {
     if (s.size() < n)
diff --git a/contrib/epee/src/wipeable_string.cpp b/contrib/epee/src/wipeable_string.cpp
index aea1cc970..5d4d87d8e 100644
--- a/contrib/epee/src/wipeable_string.cpp
+++ b/contrib/epee/src/wipeable_string.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md
index 4fb92af47..724a3c1f4 100644
--- a/docs/CONTRIBUTING.md
+++ b/docs/CONTRIBUTING.md
@@ -56,7 +56,7 @@ the previous paragraph is here.
 ## License
 
 Copyright (c) 2009-2015 Pieter Hintjens.
-Copyright (c) 2017-2023, The Monero Project
+Copyright (c) 2017-2024, The Monero Project
 
 This Specification is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
 
diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt
index 8deadc7ba..45c0e4f14 100644
--- a/external/CMakeLists.txt
+++ b/external/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 # 
 # All rights reserved.
 # 
diff --git a/external/db_drivers/CMakeLists.txt b/external/db_drivers/CMakeLists.txt
index d4ce7aa26..deb22f83b 100644
--- a/external/db_drivers/CMakeLists.txt
+++ b/external/db_drivers/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 # 
 # All rights reserved.
 # 
diff --git a/external/db_drivers/liblmdb/CMakeLists.txt b/external/db_drivers/liblmdb/CMakeLists.txt
index 323437def..530c22603 100644
--- a/external/db_drivers/liblmdb/CMakeLists.txt
+++ b/external/db_drivers/liblmdb/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 # 
 # All rights reserved.
 # 
diff --git a/external/easylogging++/CMakeLists.txt b/external/easylogging++/CMakeLists.txt
index f3352d837..95c3499c1 100644
--- a/external/easylogging++/CMakeLists.txt
+++ b/external/easylogging++/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 # 
 # All rights reserved.
 # 
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 93643af24..6190b40f8 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/src/blockchain_db/CMakeLists.txt b/src/blockchain_db/CMakeLists.txt
index b1525d6d7..e94705b22 100644
--- a/src/blockchain_db/CMakeLists.txt
+++ b/src/blockchain_db/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/src/blockchain_db/blockchain_db.cpp b/src/blockchain_db/blockchain_db.cpp
index 3d52d5fb9..894eb15c7 100644
--- a/src/blockchain_db/blockchain_db.cpp
+++ b/src/blockchain_db/blockchain_db.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
@@ -449,579 +449,33 @@ void BlockchainDB::fixup()
 
   // There was a bug that would cause key images for transactions without
   // any outputs to not be added to the spent key image set. There are two
-  // instances of such transactions, in blocks 202612 and 685498.
-  // The key images below are those from the inputs in those transactions.
+  // instances of such transactions on mainnet, in blocks 202612 and 685498.
   // On testnet, there are no such transactions
   // See commit 533acc30eda7792c802ea8b6417917fa99b8bc2b for the fix
+  // Since its been 8 years since the fix was implemented, we can safely force
+  // nodes to re-sync if the bug is present in their chain.
   static const char * const mainnet_genesis_hex = "418015bb9ae982a1975da7d79277c2705727a56894ba0fb246adaabb1f4632e3";
   crypto::hash mainnet_genesis_hash;
   epee::string_tools::hex_to_pod(mainnet_genesis_hex, mainnet_genesis_hash );
-  set_batch_transactions(true);
-  batch_start();
 
   if (get_block_hash_from_height(0) == mainnet_genesis_hash)
   {
-    // block 202612 (511 key images in 511 transactions)
-    static const char * const key_images_202612[] =
-    {
-      "51fc647fb27439fbb3672197d2068e4110391edf80d822f58607bd5757cba7f3",
-      "d8cf1c1bd41f13c4553186e130e6e2c1cd80135ddb418f350088926997a95ca9",
-      "95d2556c8acd1457dce7bfd9c83b1d82b821a55a9c9588b04b7b5cf562a65949",
-      "4b5d987fee1bb563a162d23e41741ad73560c003e26a09b6655f09496538daac",
-      "1d25ea86323d1578579d3894a54b99ea1c3e2dca547c6726c44aef67db958b02",
-      "92e46fb70be5d9df39ca83c4fc6ae26c594118314bb75502a9c9752a781d0b33",
-      "924d0cb9060d429be7e59d164a0f80a4dabc3607d44401b26fb93e7182ab435d",
-      "f63e4a23fec860fd4c3734623891330ac1ff5af251e83a0e6247287818b8a72f",
-      "5b14c5ef13738d015619b61dacefc2ade3660d25b35ef96330a8f4e2afc26526",
-      "d5016b012a2fb6ca23fd56ece544d847962264b4aee15efe1465805fd824a8fb",
-      "0a7f3da1d9dd341cd96829e484b07163099763ac7bd60603b7ee14f7dbcb278d",
-      "d716c03d7447d2b693f6f61b6ad36bd57344033fc1a11feaf60d569f40014530",
-      "23154a812e99ce226f6a87087e0812f419aed51289f1c0c359b0b61303b53a36",
-      "03940341e1a99d5b0c68eacfdf5a20df90d7d0a3d6089d39709cdd964490755c",
-      "ef09648814cfe071f5d8e9cfde57247ad09409265c4b6c84697bbb046809bd7e",
-      "8843ec52b0496ca4e895813cfe00bb18ea777d3618e9bd2e200287e888e2f5c7",
-      "8558bf39baf3df62b5d33cdf97163a365e6c44f4d6deef920730b4982b66449f",
-      "504d9380ce581de0af97d5800d5ca9e61d78df368907151ab1e567eb6445332a",
-      "673797763593c23b3ee07b43bd8760365e2c251a8a60a275528ff34a477110cc",
-      "25178c95e4d402c58d79c160d2c52dd3c45db2c78e6aaa8d24d35c64f19d4957",
-      "407c3a05dcc8bdcb0446b5d562cf05b4536fc7337344765215130d5f1e7ee906",
-      "4e7fa771a5455d8ee8295f01181a360cdc6467cc185c2834c7daf9fbf85b6f1d",
-      "6beb64cb024f9c5c988f942177fc9c1ec5ecfa85b7db0f13a17f9f98e8e46fe7",
-      "6406bfc4e486e64c889ea15577d66e5835c65c6a39ec081af8ac5acdc153b4b5",
-      "1b1da638709f9f85898af70ffaa5b88d5a4c9f2663ca92113c400ab25caf553c",
-      "da49407a9e1ed27abd28076a647177157c42517e2542e9b6a4921fdadf4e8742",
-      "3c3fdba2a792fddaeb033605163991a09933e8f05c6c934d718e50a613b64d69",
-      "82c60429171173739fa67c4807cab359620299e6ed2a9da80139b5b1e23c5456",
-      "0a19e5767e1381ac16f57cfa5aabd8463551d19f069f7f3465c11a8583253f3e",
-      "d0fae6ffdd4818399eae6224978f170f696150eaf699f2f218abc00c68530f96",
-      "0937889aeb3af5c64608d9a9f88229a068e53417448f00f9aa5e28c570cca8f8",
-      "d6072d269753020912524961ce8c6930cf35abe5c4b2bdc7fd678338d20a68fb",
-      "0e8bc9b06fcc842bdaa7df029bfd1f252d5defc014d58a006c10ab678ecf543f",
-      "9d42f90520f02c9258be53b123d69ddbce5f33f92a903d3fb4cf3358ff0e07b5",
-      "1cc05416b12cbe719617158773c3e6173435fc64e1ee44310dc696baecaeaa95",
-      "266b15222913c11ef6403ee15dc42c8c0e16bc5fa2f49110447802236e045797",
-      "791b123af3b71ac9497a010610f72207ff8ec642969b5cf0d2891b21e7eee562",
-      "946d4d7b084dc32495f22b35fc30394144c8e0ba04f3ad6e2c2bfb0173a2266d",
-      "2c015cb990c1583228d08b2d5de9227b950c3f57364fc1033bca5c0fbfd08c58",
-      "13fdc41862fd85507f579c69accb9cc6a40f5971bfa41e3caff598a3dcffd2fc",
-      "64b06d9874a83917c583c9439d1c736083377d67fda2961b623f7124663134c3",
-      "2fa49cd19e0aa02989991a4c3760f44be800fe8fb4d58b23aca382e10dc0d2d6",
-      "377628f265f799772e9fb6065be8b6eee200c329f729fe36c25ee179e4d20df9",
-      "ba94fa79134ce383b6a98b04dc6ad3d1b950e410d50a292bc770f9685e59fe91",
-      "875c924329f0733e31fe8d8aed70dc1906335b8a9984932b6368ea24edb39765",
-      "f31f4abb3f5ee42a5aae86d70b3bd9a9c1934933641893864dd333f89719d608",
-      "2bcd629e125514a780f568d3c2e8b12a2e7fbbee06e652bbeed3e7825508e31c",
-      "918b43581163ca1963de21bb9ac401756e75c3f00ac8dcfafc139f1ad5d7d998",
-      "5730dd57fa52749a0d6502b11c9d802ac495875542431310c674a65655b7c2a3",
-      "03f84b990683e569e2f6143bb963a2a8de411e7c4b7923117b94c7afcb4b43ea",
-      "b298c8510d35bd2be0ff0753ad7d98d480f4c6490bb67fb93cd4632ea726e8a7",
-      "0a771afbf9be104c01b89eaeb57073297d35ac8fbbcc0816820fdb9a29d26625",
-      "713d90d6ca1be1a4e05a5f8441dc528c699caa09eda49c09072f5f8291354c2e",
-      "988827f45c19330d9404309f63d536a447803cca7cb182ef005b074def09ab7d",
-      "9dcaa105b4def895f3faee704c250bdc924316f153cb972f3fb565beec0b7942",
-      "1c06c30afe65b59e9e22d6bb454e4209a03efe53cdbf27b3945d5d75b1b90427",
-      "49e08c13d1da209ec1aea7b7fbe0daa648e30febeb2aa5ffbaaabdd71a278ac2",
-      "e1c2e49ab7b829854c46a64772ded35459908e0f563edbcf5c612913b7767046",
-      "e08bb7d133490ca85a6325d46807170cd07618b6a5f6e1d069e44890cc366fab",
-      "5c73ca0691cde2f35b7933d6db33f0b642ec70d0bd3f2b0ebbd97754ca67e248",
-      "6404399151872a521dae767311712225dba65d810ba2feba209204221b5d772d",
-      "4a0c3aa6cef36f44edf08ad8fb1533d7e1186e317da8a3afb3d81af072043233",
-      "104b3e1af37cf10b663a7ec8452ea882082018c4d5be4cd49e7f532e2fea64e5",
-      "e723a46bf9684b4476c3005eb5c26511c58b7eb3c708ddf7470ee30a40834b32",
-      "18e6f0fa3aa779a73ceefabea27bff3202003fd2c558ec5f5d07920528947d57",
-      "c97e73eb593ff39e63307220796cc64974c0c8adac860a2559ab47c49bc0c860",
-      "13c363a962955b00db6d5a68b8307cd900ae9202d9b2deb357b8d433545244ac",
-      "76a488865151fab977d3639bac6cba4ba9b52aa17d28ac3580775ed0bff393e4",
-      "a14de587c9f4cd50bb470ecffd10026de97b9b5e327168a0a8906891d39d4319",
-      "b1d38ee1c4ca8ae2754a719706e6f71865e8c512310061b8d26438fedf78707e",
-      "772bb8a3f74be96fa84be5fa8f9a8ef355e2df54869c2e8ae6ad2bf54ed5057e",
-      "3083a7011da36be63e3f7cacd70ab52e364dd58783302f1cb07535a66b5735f5",
-      "2b1d892e3002aa3201deb4ffe28c0c43b75b8f30c96b5d43f6d5829049ecbd94",
-      "cb738aabe44c6fb17ade284bf27db0169e309bf8cf9c91c4e4e62856619a4c64",
-      "1707e04b792f4953f460f217b9bb94c84cef60736a749fb01277cfe0eaaa48c7",
-      "ab8b6bac9b8a4f00b78acb4bd50ed2758e0fa100964b6f298d2a943eb2af2b30",
-      "dd317193fef72490f3be01293b29e9c2f94eda10824a76ca74bf39dd7cb40ab2",
-      "4fb3d995b087af7517fcb79e71f43bac0c4fbda64d89417a40ca4a708f2e8bc1",
-      "549ba38c31bf926b2cb7e8f7f15d15df6388dce477a3aff0245caa44606849fc",
-      "7585c14ab9abbffb89d0fa9f00c78ecae9f7c9062e5d4f1fae8453b3951fc60b",
-      "953f855323f72461b7167e3df0f4fd746a06f5a7f98aa42acdce2eef822a0b2f",
-      "0931932d57dde94dcfb017179a5a0954b7d671422149738260a365ca44f50eb8",
-      "a3d179d16a4a275a3bb0f260cee9284db243abad637a9dbe92d02940f1c7ee8c",
-      "959843f1e76ff0785dafe312c2ea66380fdc32b9d6180920f05f874c74599a80",
-      "fbc36b3e1718fe6c338968b04caa01a7adb315d206abc63e56768d69e008a65d",
-      "f054de7eac5e2ea48072e7fb4db93594c5f5d2dfa0afe8266042b6adc80dfdca",
-      "39dfc68dc6ba8c457b2995562c867cef2f2cf994e8d6776a6b20869e25053f70",
-      "19ad7ca7629758c22ac83643605c8a32a6665bae8e35dbc9b4ad90343756ebb3",
-      "e89e80ea5c64cf7840f614f26e35a12c9c3091fa873e63b298835d9eda31a9ea",
-      "572c1b9a83c947f62331b83009cc2ec9e62eab7260b49929388e6500c45cd917",
-      "df0b21f679e6c0bf97f7b874e9f07c93c3467b092f3d9e1484e5646fda6eca5f",
-      "8f3b7c0f4b403af62fe83d3cfac3f1e2572af8afa4cea3f3e2e04291efe84cf6",
-      "aae8b8db243009d021d8c9897d52ee8125a17212f0a8b85f681ad8950ae45f0e",
-      "3d45a4957d27447dea83d9ae2ef392a3a86619bfcf8dda2db405a7b304997797",
-      "a5b0a619a8e3030b691bdba1ed951cd54e4bc2063602eae26d9791fb18e60301",
-      "14650df217dd64a2905cd058114e761502dff37d40e80789200bc53af29b265f",
-      "fd6a245ab5e4e6e18d7ba9b37478ce38248f0ab864e5511d2208ae3d25017e5f",
-      "fbe0be6dd42a11feb5db5ae56fcbbac41041ab04a35f1df075580e960c8eeab0",
-      "72f3f1213d9bec92ba9705b447d99cd0a6a446e37a3c1c50bb8ece1090bfe56e",
-      "20df836554e1534f62b2a6df9ce58e11c1b9b4746ce8ee3c462300a8c01f5e76",
-      "5c3d2a81b7331c86420ad32b6e9a212b73b1c3413724a0f91bf073eba18e2f1f",
-      "63264ddfb29cd36fc18f3ee6614c4101ba7229bc5ac375f912590d3f0df982f4",
-      "5ec4eb637761c1c9dbc6aa6649d4410508ef8d25d61ad6caa40c6ee3236d5515",
-      "270c70940536017915e1cdbc003de7279ec1c94cba1ef6130f4236f7e306e4f0",
-      "c1d1d57a7c03f6ddeeab5230a4910db8355e2143f473dea6e1d57c2f8d882b76",
-      "218c030a7fdc9917e9f87e2921e258d34d7740a68b5bee48a392b8a2acf1f347",
-      "ac47861c01c89ea64abee14cf6e1f317859ed56b69ae66377dc63e6575b7b1eb",
-      "23bf549c8a03f9870983c8098e974308ec362354b0dcf636c242a88f24fc2718",
-      "a3ce8b817e5212c851c6b95e693849a396c79b0d04b2a554de9b78933fbea2b7",
-      "7310120c1cc1961b0d3fce13743c8a7075ae426fe6cccaf83600f24cee106236",
-      "8fa0630f193777dcc4f5eccd1ad9ceacf80acdf65e52e4e01bf3a2b2fdd0dac6",
-      "4a5f5c87f67d573d0673f01abaebc26eaa62e6d04627588549cc9e6c142dc994",
-      "78971cccacc645116f9d380c167f955d54b386a22af112233f7de004fc0c8316",
-      "badc67216868e1de1bbe044bf0e6070e6ee0353d05c13fa0c43b1897db5219a2",
-      "c45b2a168bc51cbb615a79f97432cc4bb6b104da9cdc1fc640c930657452f71b",
-      "c17eda13541d14554c5db542155b08b6bf9cb403d425745b662ebc2b2b9b3a3b",
-      "313210cd9d2efc1603f07859bae7bd5fb5914f4a631b943f2f6ff5927a4e681a",
-      "6ee94ec8af4e6828f9b46c590ea55da640ef50e810a247d3e8cdf4b91c42d2c2",
-      "505b7a4d9f1ba6577aa2a941843f86c205b23b1ea21035925e587022e2f0aeed",
-      "98e6a7cd687e8192e300a8202999ec31ad57bc34f656f2ae90d148607ff6d29f",
-      "1be5db002c0a446cc2c1da363e5d08ae045cd8f5e76c8cccd65d5166393c0bdf",
-      "17c02ac6d390c5c735e1e873c40294220e89071fca08a5da396a131fa1ba8670",
-      "2540507c39ae6fdcd90de826077f0ca390da126166a25c15c048a60606a27367",
-      "5ab9328e525c7a017ef4f591d995ad4595d74cbf8ff4112af33b08c70661a304",
-      "9c105587a96c51d81422f64e46016564d22329760648c95dcac7218f3733f915",
-      "525afb1b94a75f1edc2b55c700071e14a2166acda003370047c30dba8ea80278",
-      "745d4a5d9f95ca4efa6261b6bcd4ecacd504b5b901a2ce1353c522a5c0c15dcc",
-      "5a5a568cd87ba34252ba254e6a320e1a7f52f13e7451bb887efb34ff881785f2",
-      "1ec50a80198cd830b51f4f7a0222015a268d9b40f04e7f838c7b8dc7abf63b01",
-      "68836b662d79349cb42f5cef54e6a066157d398cc87d3b13f29fc04e5cf364a5",
-      "658db317f355a9cbd86f673775cac0c308fe14967428fd283a36e300a6a53b2f",
-      "677d79a8c467dd9db38f0ef45c2787dd368f701a6b47bf7a5f06112c38da643e",
-      "2baa455d4066f5d628f9ecd315cb57deca71069db5d0d112ae0aa18a84b6f0d7",
-      "5e7b0889f351560081360ac2b1429b48b2f7d886227f144e3b198e2f1fa56ed9",
-      "c3d317fbf26e15add8b4f8f93df9de9b22297b8e4945ebab9ee249d4f72f4e45",
-      "3c0b705a5c1e31abc7e46d8ff3c148a033f6875454cfb67f8d2a2b9a57a5ba7e",
-      "a0ab74663561af2adc2d38be3569fbe7aa2454346416ac96e5eb26b1e01b1e2f",
-      "53526cffdb74327670566c1dacacffb7d30a43a7f1862ff8bab87737bfa5edb6",
-      "24c5d36ab98d88f87b2c71afb4ea8562e05c7aa0b50f3bc0f9ed50a4cd52989b",
-      "c3ce4de5f94dff65d11e33a865855a4404259cf45263914c884f79db4f35169d",
-      "f1009b6dcf30030cff872d636fb96ed233eb6ecb8ffed003c7da64e4f5a02f4c",
-      "e3729f58614d3b42450d1599d863983ab7e3e5c29fb57aad7958c8923a2627c4",
-      "31cf4792f7b5ce01b217ec80184edd2a7c49c0b21701f5494ee2c9bac67c28ca",
-      "b42a5c9c92a656c5bb2b759ce160fdfd245243aeb1786338faea63b62e9a60ce",
-      "a1efc8d5d0855933d5ac8fe5960c7acacb92fcb09bfbc929e5002f168251e648",
-      "c4322c7f3682ec94b0dcb42f13b498c36cf575d505aacc8ec8bf67a6a2abf4c9",
-      "684ee5aa3c98357aeaddcc30c6620939b52aeef729e24b4a46ccafc44f24d831",
-      "36180f2ae11d105e0efbfbddb94e6b45b08609a383e4e1a8fa3b06d7a8051de9",
-      "96c2d183eacc87581a0b17b8d07878bc10d436728510715109a7565d9972f8b5",
-      "3068c9d04d561c7e29e3f621280b61a61885de0e9ec35a66a3116ca7a9e09627",
-      "2eb94b9673ad6f8f88288fddfceae4baaeccb37bed88a35611d826ba06a5363b",
-      "fc8cd5fae8b81121001f7767dcd5f185c0fdcc88cce1fbb184ddbcfad697ba54",
-      "51521da1ecedea6d588d774eb155d936b32a14913c2f11d989bcc5116e65bf41",
-      "3d23542e597a83dd6307700d79058b920f281c65f832333734d8a0adec510495",
-      "11d2e01913ff0d4bd21970d709d88e63289492c0bbad7bff99c0d36858a841ca",
-      "de674f1eee3068d2bc8c2f2897d8556e5deb872869652f7d3a4e5dbc6f1063c8",
-      "e722d7f728526110c0921791b417afde4af1e87ae48ccc01911786197843104b",
-      "aaba3a4e2a7d20ab76edfbcccefc27acfd509db3638582c28230e73ffd71d340",
-      "1385a7209dafb9622dd4274179832e40c7fae19445383c34ef79adb0e4de0c98",
-      "108408531fca288d74de4a2c596eab8569e355d9ab2f8380f4d24073a6b3fa95",
-      "294476a86fcd39351ae452cdb8af9584067ec4501ec6182d0062bb154559fed3",
-      "e64b175e0284c5cb69c8db46344ed43b5ced8abfe3cbf0c197103cfd116944cd",
-      "cdd73a0f1fa7c14ed2177ae2163035333718847e49dd5cca6775bd20fc7553ad",
-      "d423d2a374bc66a4587a5e3affa327ca75b8116051320759a3b88a868a7b80d4",
-      "f13ad1e5b1315557d5497b58516eb3b0759d909725ddd0eb8a0dee439c6c0a48",
-      "3a600b547a6061186a54e491344fd50cc7d4f0566a386a40aba6545254773728",
-      "37a6f3f221fe17cc04a65aa544d5135e8297ecaf0853ba784dffacb74feb481b",
-      "0ca42d67d0f84b28861d63e919e6ce5ad527447fdc53a03d8497a0241bee9376",
-      "c7dbda42459e6fadb92c416eaef3e04674fc57915a93f3be4e656634c9654075",
-      "0e34d728ae4fe347a5afecdf886fbd4d48a65c5d0dfab807da6ae95b6b2d7a3a",
-      "f1bc69257ed510db5b2ed370010b037f452a29c49e36337264b3011ce2516216",
-      "33f98f6b8a8e202463955998fba3b790199daa893e5471554826cfd9daa5c02f",
-      "f8a0a37a2c9ebd7022d7cded1ee0318fd363020070b4cdaea800e44dcc1300d2",
-      "6862714daedb908a4d86a3a3f1e65ec2c29ae61501b4ddcaf184243dd095d71b",
-      "555cd19a6d21941c1174129b8bbcc70edcf0d6874262ce9e1e542351990d523d",
-      "2cd6b44326828f23a2aa33699754bfa072c4883f39d53616f6a6b74149b664b6",
-      "127f45d2eacb565c21c1030fe8054fd0a3a75705bc368924712aa145d414fa47",
-      "19225e2dae6e1166f21cdab1290194470ded09df9b66f3faad3c1cc9ebcf340f",
-      "b7b3f53f0539b2b4837b8bb9dae0ccbd200c8d36126d9f50199d68a4293a46d3",
-      "6b6323be01f27d6d759d9670825e8ebb9c4cd8016351702328df91cef36cfec8",
-      "020c31cfdfc5b22b10235745b89b311d271cf82f2ba16d03fdf7a8bc8538694b",
-      "62573218530182b79e40d0113b7d281dace6da33bfcd0f9318558de5e5c76f08",
-      "37d928416b15982f5bb8be40c5b62fae0b664e412c25891f8860c4242927e610",
-      "b07ad11134a5c0542d2b418ef3863e8ea8477de68d9310681818ddd40825fdb0",
-      "4af77cb76bab845b56470c95ce7b8cd84ce49a095984c1f3eed67b0ee344316e",
-      "e3fdd4668d8726ba6adc401ac662c0cf6b5c1872082c488ed7da966d425fb1c0",
-      "3dec71c81c7e78e879abc8da8b30e2446edbe98eeb8df9dafe9201ebb4c6a834",
-      "7105467d9c5e855f1362fbddf820ed5e757997862efc9000317d3830a2f60ef3",
-      "2821df94b021d3e77e8d9c0f3972340210f5ea2c0935cbf125cfc578d4d6722f",
-      "114e5807accc337a22598bded30ebf3e0cfd75877e239f10cb043f829c315ab5",
-      "d658a1c0354628cd7312593ab25d5b9083de8f0def6e8425f188101d256cd136",
-      "4818d3be9b2a38fcc8c85d6c46f69b502943f79cf2462dfb0b6499e761bcc836",
-      "92b8c943cb017b5f2d39264640c069f1ecced1d3ce9b3fd755d6df2fddb99458",
-      "6edbd0fdf064fcbccd4a9e7a8ea520b87cb7faf867f7fe8a5f53625beb575119",
-      "bf3b49c477dafb06af65bf09851c0fbef9dbc3152a7268d31b55a8e7a9a95860",
-      "0e234dbadfda1393be2f068182615dbb83736f84f87710b5c7965bdce9f4a26a",
-      "df5ceae34429e47b92bbd5505ba27666552e0eb619997f359c55699c3252b1ff",
-      "08c1c7d940d699a91a83249bd578772e8958ffe23179e6150f07b96f1b47ce1e",
-      "6f919a429270da0022d70062537bdc1b21c43a8abc552d8e366647e5b719d310",
-      "63c66e5fd5d27f6fda87912ce46fa91a5e5b3634ed147fa2986330fc2696d3fa",
-      "bde070b75296bca3aa494e7f549cd2bd1ff003776712bc98a3164b139b2054ab",
-      "66694196dac5b60cf5e0ae05db8f3894fe04d65910686806551f471a0a0472e9",
-      "0d2e97524b7ce4cf30b54e61b2689df036d099c53d42e2977b1671834bac39e7",
-      "e081af76e923455f408127862be5c9baf7de6b19b952aa2a1da997d4dfe594c0",
-      "121bf6ae1596983b703d62fecf60ea7dd3c3909acf1e0911652e7dadb420ed12",
-      "a25e7b17464df71cd84ad08b17c5268520923bc33fe78c21b756f17353ea39a0",
-      "e985f078fb44dbfdf3f4f34388f0f233a4e413e02297ee9a7dcc3fcceacd44f9",
-      "b9184cf45e6e6b112cd863b1719de1bcab2137eb957e8028edca3a204a9ebb96",
-      "157d177d5e4bcce0040eb4bddb681eacf9e2942e1c542a57ce851b4742a9cc4f",
-      "0823e06635c9a1a69fd8833d1e48df98d711c1c47c06f27bb384932db1bbe9ee",
-      "8beeec1fd1bcdecba235b449cc49abca69b6486ed1c0861a2bfb6a43c970b86f",
-      "349f61a1cfc9112e537522858a0edae732a2f8434cf4780d3d2ec1e96f581cca",
-      "587cdf72b5914d364f7e214a70481cf1131ee4a09e6b43e52428d2e56b000c13",
-      "a6aa0c179316534c7b9ffb5f42a2af98d1d3a166bfb413199579f259c7b5e6df",
-      "f9f3bb1ba8da5899b79186008ecfbd416b49f3b86d94045b91e34a40e41d5cff",
-      "0cdda65a60b7b4d94e794c9397e01f69fb29309ce4fac83e7392dbba6bc497f9",
-      "8fa5fce5ad09d43af7218ea5724cff2c4849a59ff73caf3bbca466e3c8538ba8",
-      "8874ef46008753fcc0b77eb7a4a8560e35966bf5a12bcad4203ad2b4c1f8bfbe",
-      "a8ee9a3aa2d0c08a951439ffb0e6d10315fc4776997b275de1ec19663e88c2c2",
-      "9c184cbbff464ab4d5f6bfa78c39bf0880fb93b1574139306a97acb940d415c9",
-      "5493a38c255c91ca49b958ed417f6c57e5bc444779d8f536f290596a31bc63d3",
-      "3e1e82b96cc599d9fc55ae74330692ccbfb538a4cc923975fd8876efe4b81552",
-      "16aaaf820c24c2726e349b0e49bbab72ca6eef7b3a2835de88d0cececa4da684",
-      "7fa52ba349f7203c3dbc2249f9881101a3318d21869dd59f17abf953d234db65",
-      "713d8018bb9ba3ab55c3a110120b9d7593514111075ef05f0fdb233ff2ceddc8",
-      "56063afb495759a0942f1c33f28a4fb8320c6d376cb3c9513249453e45f24a04",
-      "f9a6bacd9e055749b45174ecf3c3db18b78f3474761948a68adf601f54e59660",
-      "7ddd7c6d41572f93fe07c0300c34e455b6d1f4372204933bf45035241c8b060c",
-      "f81021b893a36b201de71330a2ea050b59dbf7560c62fa9cbea9261ab47a0ba2",
-      "a01fbe4114c18fd534ae1621404d25c08e3b6775a2631ff40279bafd8c9304f4",
-      "350fad8ebc938c6eb508a1483f385f577794a002bc1229db70a1b0131d174b9d",
-      "570cb8bce87f532c5051d8c4c864012408e672a7d492669e660251fb1e066bec",
-      "8cb6efbb129c84eba26d894c4293b476a6e9a1fe969c6ad18b554d2a57885f36",
-      "f384a98467bf7f084ca31bea121a4ec76e530f523d3225c21ed25a18544a9916",
-      "da127ab58ce557c2c95c20d6a291c2e5d880fff09dc28927b7bdfec97b995d72",
-      "a4d95b4f74366ec920d0a0c5d81265688cc18733ffc444cac9b01ae2431568aa",
-      "5ae2a71470570733422468bb733d53c85b1c8a6e7e6df5c05941556bcf342d1a",
-      "65a2d161ff0e095d3afe37584dbbe649f1e9cd440755b5a3c5b2a252d5c0b8bc",
-      "25ef70a8e41bb422ed7996a41160294e33238d6af17a532232f0a50b123431a2",
-      "f1f0f76ee901664a65b97104296babb9c7422370e99bb677ae07c2ee420e7f40",
-      "c3c66dda180b0330e75c8139b9f315a8c6b937f55d87d7be42e172bbac60d71e",
-      "5881786695a9e58e19d79f790c7d9243a847c0269c5770bdd01f5149c2a62a88",
-      "f2f816d3c8ebc7df06ab68d39223181aacc7be04364d1d4e69a56c33949bb983",
-      "80a1c3b6b2778d4846ad9fe0bb2dd5afd99aa897f8231bfaac45fde43d602d9f",
-      "72ad67cb043aa5df0c3dcc2464953a66893259d81c9cc7778c12bca3447fbd58",
-      "ad72420a7963b8d4536d8eba00b4b989992247cd8c01660e242a8e71edaf0e19",
-      "999d603d1cf6068e3bb6abe1bca976fa0ab84c4660b29ea8973de8b5cf5fd283",
-      "e137a5910f02a764c3a3d8f1579ac0c7e3cc34e58933216868efe010332c1e6e",
-      "10e0fa2362f59317626ae989bd1f962c583339d8d74d76c3e585243d152b91e8",
-      "1951c652704962f5c6e33a4d4aadfee5d53ce2253644d1ed542da3e278524a07",
-      "c938bccb7ba6c0217d8ba35ed91502aee051c8ae5bff05e88aab3b322aec936f",
-      "4d6386c689785edd5beb55911a3a9fc8914838c8192184199589beef8b6ddf9f",
-      "26f6f45a6468bc4b1f085fd28d63c264ee17564f9e247fc03ee179a0b579dcda",
-      "235b7bb82b72c61acd5979ca5f2ca740aee805a780ba22e11aae5cd34f6ec760",
-      "c027ffb585a1e4844b4907490b621b08c7e40a5e6f93e97bd4bb1b615bba9908",
-      "aa77fc8053d139b998577319de29457b78b1cc8b35a5f3526c0621eaa42ce6e8",
-      "afd0af9a11c5ae2a7c4a4571ce39ad57d8df70ef146ed83ad8eaff97b2387fb8",
-      "a1f8fee9f1da9a2b306489d00edf754187b55e97f4fe6f249432fe6c7f44d6be",
-      "4f12e8a123465a862060efb656299e6bef732e5954b02194308817b243e84d32",
-      "6a1ca62f7d6952ad2eba1c64035260319baf03deabf856ca860744fc886b3d3a",
-      "c72dd1fe890d6e4c1f7325a4f224e85aef6cdca8bf9441d228efaf126e02ba63",
-      "2f6ddcea18d891ef4252e657989de68adcc43c2175d49c0c059c5e49b9dd5aed",
-      "24efac0f240ed183c30398ee40307623f52113598f66c5864c90fc62643a2aec",
-      "6ba3ebc935e7cf7fbb446e7f5c12b19c4455e6894412b0eedee4fc945e429e9a",
-      "3519d6e5bc9649f97d07a07ef5471a553ffce35c7619f4f63e91a2ba38cbb729",
-      "65e073df352fa9917e5c2475167e6c523b68c1406e1b6e81109e2d4cc87c740d",
-      "d73bf816c3648a7d53d34be938c454e515efb0c974d5a225f33635540b2db90d",
-      "bce167790fc86a273db011757d09e2d1148521ce242c2ded58f26cc49993aacb",
-      "2d4286ed4039916f29602e86f47ea4c5b05998c0198509ca7799fcadfb337e8d",
-      "9837c495b1af4f76b09177514a0f3e1dceb509c52b91712f3c7d29dc1b91d09b",
-      "5c848b8291f39759903ce77f151acf40f3ab5afa2d4a45af62b320371c29a697",
-      "b92df5016ee947ce6a21365d3361977f7f2f6c14025a983c44e13d3e8cc72152",
-      "71d2f57222a39b1a7ed2df5e6fb23a964439b5a8e7d49b49d87e5cd5354baa75",
-      "88b44d0198fb15b0c20a97f87e021c744606bfd35eae2874f35c447aa4ac3cd4",
-      "29bb4c2557714119cd684da2867e689e37e3ca9c912db83ab84746816f6092ab",
-      "b1836d98a288752675b133b9018fa1edf174f311921d01926c1e1a5900c21029",
-      "a00645e090c7d96f3155ffbcfc41e526a763b0f53a98151ac4a7d4a5b14066b6",
-      "78aab09919d17773b0d62799b37bd2e2970f72f5d1eb6472489c367b6135374f",
-      "eb6123aeb28608f1c97b2bf62ef69f977cd0658a0ab491deebb1e972caa938c5",
-      "8dd7ef1650b1b30cdf7814ae4d61a237eb0acc3ec3ce0f72b1c25780329c2d7d",
-      "b1998419be3172858b990eea84fe10bb24b76c219cde277cb4305955fc7e0b65",
-      "1b10560016c4bc506eef9056dedc2943a17179081e6eaf85b48d37dc20eac3cc",
-      "1fb1d9d4d408a6734234910f554d272739a0d6fa401918d79b57be62c3f23ba2",
-      "dec878f54ce36788289b61d32de0d9539032aba22cd15522752f729659c7cc5c",
-      "fdbfd0773f5a66637b093dabf812197940d1134619a7e60a931b19915b7dab0a",
-      "21bd2c9aae052a1c187947d2964f2be4afa7b30248409c41a75522e88a4e7977",
-      "59326adab03416ec1d36699c18e5e4fa13ca1f2212d48c23bfdecb0be7263616",
-      "bcf263d39457c0aef8ef42fd98f8131198ec4fb203155dd5bcd759e499a9ca5c",
-      "f1ad083bcd8c7630eef93336d8a971ae8ae37166a6a00ac39684420c5f9afef8",
-      "d82ee2ac41b36e3c1787a01835bf054070486dc20f6565efedbbc37cd3bf5fa5",
-      "eba91a0dcbd3986299b0a3e66c116f77bd3421829291fd769522f01da26f107b",
-      "11016558b7e8c6386c6a3e862691dcba47e33e257f0e7df38901ea7c0eba894c",
-      "04f02795e34a0030e5186c8e700da8a46b1aa6bc0abed6b35c9c1cd6a73776b9",
-      "2628dc8ad7fb731d59456b2755a99c6701467125fa69816c21bfccabc31edf6b",
-      "9b7ca249ee5b45cd264492f30df09f708a7d9caed7beb9a5c6292f22e4c31f85",
-      "5c42e7caedf382092faaf392174792b3cf5f2fe29cb586387ee55611af0617c9",
-      "373f2fd5940a01feb79659c8b9099477f6d3e7b570ebb749c2ac336ea4be583d",
-      "fea22887147adc3a659a14902080b03e86b4b8b16985fdf0bbacaed00d812422",
-      "6a3e51a1443cff62af9fa12fafc8ea78ae74dac7792c9ae0f436f570ab33eb71",
-      "796be21e213d6d0cd6fbe2de1555fb73d1cf9edc041a9f1ff1ad583c4ca92460",
-      "03fcbcb31d3fd17f0eedb45ac5a51678c7c8b2b8498225d05f74e2892f017f72",
-      "d28da07c6c22daf9ae987b3033c33d3140e5a56fa1ffd7dc5c7853d55a45bcc7",
-      "fbb0ce02f50018741a12fc08eea80a18067d7bb0fcd96153d40bb8c808473aae",
-      "2bf7c05a0209b4ea31314f04bd754cd01c58102d7cde8c496c655b6494924354",
-      "1968a9e6e14ae86a1e02e6078fc4631851fce5dbac6aa34f860defd1ccfd0ded",
-      "d886181329c9e06462a1407f547d77b38ff2c868b86d8976aa478e1cbb3d66d4",
-      "0d465e02ff2f8eb0b5fb2fa9a38579c5d66337d4a16b58f8ed28d2d89fc02392",
-      "3196419015289807880ef24b6781734822d654dc260c0560d00bac65eacd5219",
-      "fa08390ddc333a2a12248d5ec3e51fff9b782227069fe5a0afbd8eba967ae8d1",
-      "49ae36a791cb84516688d59a1ed3e5112851d65f265078aa2d433b45fa984c8a",
-      "35daa428e12c59da6730760979aca3444d8b31149c6febd99fbfefa4b2372082",
-      "5ef1d697beba612ff31d1dc139817c313a4e2ad3977775943b635c141ef0f2a1",
-      "674256037ec00edb66b9355fb1d33a30a47a5d1f4bce2dd5475d89f1ea6502db",
-      "7b6f017bc550933af91eec32a79464f540c5e0c208703e175843ee4e9ffc0a50",
-      "bf0eb91da1d18dbb18fd9ff36c837387887ba142961176a44889718b2becb9dc",
-      "3e5ac43a05164b074a2ff6353e9882917c5a3dbe168c2695de895f7decf1a56c",
-      "35e8f004965347c2b18a000a83dd3a8084b8a4bf00522061ed1179aa1107c413",
-      "fccb0fff3a24e555ec96f702ec11d420338910d148fc7b039d67568ad3a0e032",
-      "5cab231048032dbf921b4fafa1951dd2da93bc3740667f31d5a1d5665b141261",
-      "ffedb24be73441fbcd069f7785ebb865870e0f3ed228190732e4ffd5962bb82d",
-      "a4fcfec18adf92f4ed822f64d2da9f5ae630885a1bfa626530f641db99aa7a30",
-      "f98bcee41b0e3deafa1efaa1863750dbfd9bd7430b82529b670867d852230b5d",
-      "8ab8d5fca047a52364a737c1af57bf656c9ad5049f08ef4c5aa252e61aa72123",
-      "91318b39ad94c1d58143586b6d90dd6092a9d7487e321f4976967b6ac445ff43",
-      "fabfbd4569ab018e12d5ffa9b1a40ca8eb2ca60a685817351d90eaa770d5eccb",
-      "bbc5ef34428d980e2401942ceecfe07cdf21bfb1acae0596ea1d43fccf504f69",
-      "26943e4201ea407a5667103fe07ca6e08ef76940f274349b0e2e776bcfb0acb6",
-      "e3b305ffe33e72841f8e2a8688cc5cc27d42aee7624b33b7b6399b42db392437",
-      "17c5a763dd57e6bcc7c4cf2db0eb5cf3e97116b67fe0dd519c97e4a4d55d5a62",
-      "bbd260216879ce86af8318ffcf73c9e063ca76dd8bc35d3b6be45b2b4184888b",
-      "41285591d0595bc42ab663051b410d51af39fe1720592e27acb1a8af72360a76",
-      "f29acd6068ce494d0c0fe294cad91bb8968e3fff3f595a113227ab545c3ca3e6",
-      "ec9013c6394528e7dd788ce7cc085ca79fcdfbb37565999d5b4b5a4e39452ecc",
-      "27829bd7f1a8fcddcad0cc34a3b3fc67d62a2f3e09f8e75d35035c2281e83afd",
-      "666bea9db4e15087204d076294d221d4cf5864f5d94de38f29132b1934a17ace",
-      "a3a30924cad3dbda3446e5a6324e0a1390c70f795d5ecfe17ee5c70b14f7d87d",
-      "19567fe5fdb10711d60aa4d9843e1c49c2a6d2fd1b5cf662e2105606bb7815d3",
-      "b139f1c3a2f15596b9320334e37e4184d5d584c4a81e72d821a7edcad3aa62d5",
-      "08f1531e0e3e8f8bae313b2c60a72d5601bf8b60d7a4d2f60e8481650340d250",
-      "c5895669e1ff182bf1dd6c00dc955265e08ded0952b8ca62a1c63ba11c99f4ca",
-      "84d1c28153f66c1a4eb5fa0df695e936d83294df31a08d8d8e2d4798d19d8ce0",
-      "b8699f6af853fdbe897848feb46a05d733363f304eac4c8c1932e6ea4bc062cb",
-      "10eb3f6c1d0661468d9ed83593e5e9c0b43c6feec6a5405a498194905ea6ed48",
-      "509e215a600d9cadcbf5d62632ba321d7314764218db00ce8c907e425fccc445",
-      "e62119b7be84c8eaad41ba7f4a35da403f4ed96b967a3134e89ee8b78f8792c2",
-      "f790754a95d59ea5ffe6b9b5cc386c600a9e19e8bec997c192764365f1d05376",
-      "990121b5aa4d6badfb7154db4cdbb4512124bc2f442bebac71ea90b5cc86f203",
-      "b6983dedaa891eb14c964d84461e5cd37ed27b61771c64978ba83e3ecea194fa",
-      "00fba1ceaa6aa1e378cd5b22a771d6070250ac37f4e17d7bf1a70b3139e9a860",
-      "429854e7738abf2ecf46909454039e2fc5a080eb9a3c0c5ea13b07426dac3ad9",
-      "ceb3e017944b0dd787be219d8629930b3f2e20e22b12dc88fd838488ebb896f3",
-      "eb9e5d14424c63e675fe771b73ca865f7d38cf334d65e2426e12a0b88c1a2236",
-      "556ee713449e6e59ac4b8b2e8310180c8f6280484e9db23456526cceb9216168",
-      "bc89c3aa889e0144ac526a1f861227430dde7e439cc6a7e9b25c9a049c3ca7b3",
-      "56d070c62ea99be66fff741a8e45fafda5f9ff783e84d5395b251f356ce4e16f",
-      "ace15859c399e5ecd13b1420d3c3703c6a88dfb4a084f7225e7ba40a4b444fc8",
-      "f03f1261ab6eb879fe9c5b0028cd400b3ffdfac4344e4c75f6cde3c05ded1f26",
-      "955b2fda8d0068226f89270028b316b5adac871f1c1c85435479aba14a381b0f",
-      "422509a98d7461a6b8ec87cbb173b2486577b59ea9b269e01c20567b38b3b3b2",
-      "007d4de62ad89a4f5985f0cd9b76a7293acf383b4e9e734e813b9df1d57f986f",
-      "13a04e32948225b7e22aa0137341ebbb811e0743032fac16be9d691a652db2eb",
-      "8244b11d880a52f9f9e1830a822e6eeeaf0b12fc759f8261bc2f490cb0794f3b",
-      "27d3415f8f8fd3048a1ee0d9487256dd1b0f9e31be663778efa5b3add63868ec",
-      "0053f888db916a8905320e253fe2f0666355e6fb6de36f897231920a3edfe39f",
-      "0bc5c0a2ea47fa3bb8be107e3b9d6b7226b1c8bd16ca1bab8f51b8e1de08aa8b",
-      "4ca13aaa161c79025b5cd6c9a8ac33554f5ceb479fe293d9a342c865cd9c9948",
-      "333afbe82e2a3df38bd1ef998f39c5feef2554697aa21b5410c0e95ef9156249",
-      "587c4fcabd18ff890064171fce3d5be0c4aa1bba46893fb6a827a85ab54d20f3",
-      "964328e4d51d67c4e2f1fd102a66b861d98199f81d18d660b1b4b52504cd772a",
-      "196aad5594651efd679d30b9feb0f0d172cf977b4f89aa670ec741a8bf21e669",
-      "9137bfd66bbf47bfa0bfcbb9f6e71b6eb3fd9776530e9fd30d3dab19e182f35d",
-      "8217392c4ed2313188f295d94148a027a7f43824a5f5fba33a0b8c1045d509b4",
-      "be9e12761519a4836e73015544163254877e1c4912fcea67a10e7026421dde75",
-      "7b5220421a520b876cc6cdba0d3895104d7fac914dca5b93f9fe8b226566b71e",
-      "5c83fccfeb4bf0eb8a94d43ebc84a81639a32f26c7ef77d0a2b626b7de7befdb",
-      "132fd6c92cf176f975efdb5ded53470b462a48a2815c6f54a93ce4f935784cc7",
-      "46a3dba364022d11aa616a2bc20e3be5c4390f38b9446edfa464d90d9af5d48f",
-      "34b3f3fd8a83905a37762060f51d0b116377b4820b031b8e668e16f46c5b0285",
-      "f0e397e033dabec859a4b9a9043c5f1fb0dba504764d6bcf2fe9bf2ffd318474",
-      "85ecf59c7dd3b24ad17f591bc4737f32f1384c370a7a6f2add06a811dc824b6c",
-      "4d03cdb1e6ad8e066a83654626d8c221433e8d4fd901c671300af37e000177f2",
-      "61cb9c651893e6401b25f2bdf79c9f3ddc9ffe69cf6c791c420462bd73e347e1",
-      "85f2686a42158cd5ad327781ecccd1bdcd346941dd4b4edc45f717de6a011800",
-      "92de2ab82cac528e6d4ccd61e5b7c79591dcad9909c8ad3c8276ece6d48c0f08",
-      "23a878a06bb94bff33083349149f3c860f2b00bc3fb28f04cbaf717c08af19a3",
-      "1b1cce18ff0323566b192885d7ced30f9a9531a2580240f2c593a7d5b8580974",
-      "08fcdec7ea1376d84f3b13a47a4b73c7781c9c7890bb28f712b58af4fd3f24fe",
-      "03cc08fc4ece807c6495272c412be23b045622cc6b786ed8d5c94156ae678a0e",
-      "c4d8a61dc3f5dcf4b83f27a90cbc37e816cf4754e12309626ec5679c99087c46",
-      "b29d00681e29001cdd63c4bc50e5e25715faef692aeebb678c8050e1c095e888",
-      "ac154617e93f2bb1afa232675f2135437a9cc9700c14c51c40084946596ba11a",
-      "ce9549de8e68ae89f424dd9e1cde8a4eea2069da667cfcfbe837691d37366668",
-      "426c45a98e2af35cc9708149f6c086ff5a3972e77d62c627d5e20de5d731cad8",
-      "7e21bfe240a3d9b77a129c734a1d428dbc890379fbaf862853f48b2f7470b2b0",
-      "fa090a71f77223a7210de6db18d9aa809e89fb15253aea28131df6c5a7639140",
-      "7094ad044c5ab025e088b43aa0b947601fabe58ed700a412fd96e4b917ced0c8",
-      "936d5cdc4f081b6fe36c356af4378d472cd7990303f2ea44da645afd7d5d7f9c",
-      "05342037d3b69349dce7b95529d4b2a63ceb9d9393217a68f7cc8c958a96c3ea",
-      "ff9e1c414ef27b1178b1de296526f50520b7ddb06286bf9c47792bfb449e40b6",
-      "2f2b7bedb34d2854b17ccb702cddd8bc0157e39721d58be0b2ad54ee291fc9f1",
-      "0d8db1f34140bbf7eb809137018a74af08cb3345b8a3e368cdda8521dab45791",
-      "b109e4bfabcfe4a1c38be1156d9ca851c75e6aa2e57c0869e40cd9056f571e07",
-      "5cb363547ca077c806fc69bc8c2006831ab89e72fd778ac1a48fa810934e350e",
-      "85ee928bb110fd64eae54a91fc8548883e7fc4c60a3c61b505c31cea2d295c86",
-      "1ec3df7d10ee6fd5f0532ad4fe771e6befc28b0bed0250bf523695d6d49a8246",
-      "de9db2fc07c866bd7b885fb41522b63d550d0ce2e8ac5e14464a41733c2319e6",
-      "9a27136422a8f56768db29ba172a7ba26c3c7aa910324e78e5ab3a3268ac3674",
-      "60213c315119bf9005cb533d1a5b403b4a13c59982fe7773d30fdd8f519f4205",
-      "40eb61ef1812eb8a4d389599bf449fc86653b2c4986061b952f46fc049de53f4",
-      "658ef0d8140162b5f04591be13b47456245f531208bbca3260b857ca09b803c5",
-      "02270fa66255048d724894e2206b4e773cc6a7b6d17ca090cdc25f317d5f53b9",
-      "2ec6a0147f419161f7198d05be5f93152d9ccc10672db0ea47ff1687c0f0dd15",
-      "4be1d8ceb96eb80ef7ce30079ded31163272aeccff5c18fe3aaa32ea2f5bed9d",
-      "04ecaf48f44de87243b17b4c71ebff00020738639336010fa57435a54b623798",
-      "e313a9feb7cfd1d56ec87b1f1062ff9a80da498f7b761af4bef0cecd1b4c385b",
-      "ede3748f971f22341f7f5844dc60fc03cdb30c7cc720ebe13ae588c17a78aa94",
-      "d90c0faa70e39b7c0a8c55457ed6e6478a4e4bf3707b08104326a1ee8377c3ab",
-      "c79ffd0bbc8d004cc542e212990df6498abddd3deb50fd00ed00a2ff690974d6",
-      "35c37d88cf73a89c4124b0ee537347c37fdb47156c8b0ecc509efc58236ed3f0",
-      "a99182f343ccf05e557ffa6df71f03688b2afcf314c59daa774fe78db6f47add",
-      "01115397a78af8a4ae2727ca7a01843235b626bd3db80888d3dfd0020d4135e2",
-      "4a55aced578470d2f7280096d7fe8095f294095fba4778d1977d6db9270472f0",
-      "4624adf8a5633f65b213b8ca46b55cb0ee36c41495f39b1ae70cbd545779b1a0",
-      "d72bcd5b57a9c47e7bd5e9a1103657d10beb7b6c6d41f2b2985bc3bd3cc74860",
-      "48baadb9a46293c92f29e7617846171356a42c3b5d18d49a05a7e173993785f7",
-      "3da927737af8cb0e1c77097e35c54158d18aabfb3051c45bcc7ddfe00b157b1f",
-      "b4a24bfdb2cb802c8d48a3a18fbfe18622a767fe7eddfca57d4555550ccd1643",
-      "c58f82ac7c49dcba1721a88358f07636c9df60d3fd383e5789b808dc57a1dc9d",
-      "5e1f756eff5155df073d30f4452bdafc4adaf4f35960771bf2c1e30137fd7a79",
-      "be4a332f289338d67bd4834eae3128c488a61d255e972da484b6252b67a46b89",
-      "d496e4a36238d03a83d8b45cf33d9388aa7568a279b034d1cdd87b457356cc5b",
-      "a1c5212730ccda34de393210e276bbd44720dda777bcfd602315a3eee582f7dc",
-      "08914ec63f6ef7fe1d678937dc0f6178883440b26b4aca29fce79068947e8397",
-      "49e2cd2bd9b974074d9814f93eed371620bd4ea5fbf97a625065704e8fb382d7",
-      "047c194111818b48ce93a4b006e4a09b9a2650757a87357111796e11e847bf23",
-      "5955b0baa8265341f35a6f24fdc79066ba3ca9c5354c69b6b37a9ef3a26be556",
-      "d7c962f3ea1938c5267cca4072548acf3afcce4d438ed62027caa211a5f98e8b",
-      "c8cfba67cb4ce7e291b35154a50476d9a5c6bbb5d6cbaaa5d2408547fea7b02b",
-      "de8a940d8a69a64ce264d2ab7320662aef2e391c587cb2aae22a86718d5dffbb",
-      "94176f1310b26e54d4de48f87b74aa0b60532f184a2268508dece86dd7f85d36",
-      "9ce6ee3fca56c9256a69df404782301300a6e5e7f5a25a1f6d68c0e9e42584c9",
-      "7c423c4a220c6ff43ab6432f92b166323c58ee77f8c096ba0b00d52d7bd507e8",
-      "0b62b9c1ae4d4988720e8d41d980b334458189de0a3dd01699338d7b07c3894e",
-      "64f45f6f75110624506c53716f2fc1d5fe5f88f82a5bc6a7459ce70eae56dbb3",
-      "12ffbeb8e52fed161af4d8a015d1a5c45dcb8240e5c8933ce3a88ba2c58f97e8",
-      "2ee6b7b96043c8ded9fb52f87cdd0d0580ca6f8cef183c8a656394a11c0aa393",
-      "aaef26b1f5726258bf9ce305a3e54bca65cf68779f90f9d24287245c27362e27",
-      "ef59588dce57c35d010bea4d209f44c62f0b7c7e65bc0226c0e4971934da9435",
-      "0e606c2f6f8dcd579faf4739312bd7327ac7796fa44a81780fe0d66fc7761fb9",
-      "2f307198afdbde5f95989a17e06ce1bb9ff36c441cf3b2248431534fe13bb9fe",
-      "51418e6df23d450aaacc74ef2df53a6b1693727b70bda9ebc43acfc23d8fb5ea",
-      "6e9e3ac46705ed80520695b924435b00d2b3079598bf7faca7fd1524be777e5e",
-      "1e96241e2876aad29ce64f5d7e7fbb8db7265449df816c0d30a96633778c5cb6",
-      "81788f00eb72696d811f946e65d2c96528c45590874a1defdd46651e9b79a3a1",
-      "d9aa5a9f1df50e933d7105a5d72b5fe96bfbb9fd4b5b0eaa5e80af12e72d497a",
-      "e1b6976a732d27fc5d6a96b6d3d0d1d5eaa6ec46bae4665f17e7a43aefc75280",
-      "9151c75edcec1cc90aa2d2c240ff657b0eef3f5f1ec37418c8854b2493114f1d",
-      "3e7c12d0132421f08ea0a390cfa325e422a6b35120fb2eb650f108a165237934",
-      "1ee0e85c7d8a91089c03f37318cdc9127026bf789e3ce4b75046eaa3eebd3458",
-      "4ae64a3ef66cad847409ce175bd5365c9097fd21647a05730ac6b45841add3c8",
-      "f0ea0f334cf1d64678a6dab08c07e2f94f339e8389bd17ebc882b5c8b736cbf2",
-      "da904db96060546ff69e28993ce8183766da9402ac10fae9fc1f1d67ebd83c90",
-      "db11820615f7b5e47778c45d2e083e77f49b608b587dc09ec26f077aba07a242",
-      "ff5c726a83bd785484de75bb03b421f9e8e382bf2740120a2fcf72326aa01c75",
-      "f8643a7efd6304980db323303ab73a6fd4f4ee1047520d39d571580395b97f21",
-      "8facf9737d07838aedf6030593bfb247d8c29fe8d9b18b2913408627a4424d7e",
-      "f0672964aa6e4c7dd4768e18827023787386927f4db89fd661444979afb43c18",
-      "6fd3649c8401f2704ed2be18518b870eb6bf2b9a6689d1b336f05bd8b49017f6",
-      "ed172dac7de827493e0c0fdd8d3299333acb678e72ed499e0224b389cc1e0fba",
-      "7f9a8a8cc8e34add11934a1a5882be5978a6d28405cb0a053ec5699a502b1cef",
-      "6ca829ebd2a0a40994f68c1db7978ec274b45c46e9b351df869a2bfe0630bbd4",
-      "0bbb017c437573a55db88258a9d9a01188bdd23bb6b26903b137814871661f47",
-      "9a6358d2541f46b6d05b80fe25a2cb025fbe9e4b227a6275908d5ee31c948569",
-      "a75d26c6d4ce944024f10e6d23e8b5b888d680120e15dc0e4fee8d8833ee0c6a",
-      "1d43d33556699b42c124b46e41243abf48727fe488428056fdd174a3861c1e3c",
-      "7b5bd3fecbaa093f005c4f806ea67846c0df6b04df7729925cb14724f6a8b582",
-      "6bdf2b54f2f5ab90191261d33dee80fede75896994016422b28db8ba62327d82",
-      "1a16181b250085a91ccecc118473fa2ab98515e894a7b63b347c24b5be560c7a",
-      "22d24891755910b48ad632358c26245bdcc375abc41f7e2c9fb3c7773dbf4e22",
-      "5b70c5d4a373d541619c944fcc3b61259550b0e9fba3eca16f0879e5845b43b9",
-      "b78f9098c9d76987b7409e63426a8d49972bb4e75289576c680cf96513d44b6d",
-      "916b53b8e85eb7e0a2a76d6fc8d2163430e7183ccb103d6705f54af4bb070907",
-      "dc3d78f43110d2aa9df83c5485ec33663ad5452b8cdeb1aeeac9d6b1487fb781",
-      "3975539ed5402cb9f5ab503584524dd141cc4296b666ec66d807f94f62b1c026",
-      "bd1f97fd89183643423073f22733880616456ca41960699f18e868cb9ac35508",
-      "90b468ff0f83460c3dd8cfe778c39d32c6bb1eeff9ac5de7804a4050d3b8073e",
-      "0e886d49d88b82c9f8dbdd2f38a535992f35ab16629724d746394db3898235fa",
-      "76c22e965242d1ca5614e829d9028dcad9c4b09393bcbdd318b0365557335fb9",
-      "59b168488ec8629f820a1efd8fc5a0c2adec4253e61d6a2945a68a9a43be9035",
-      "ff172b42854eaf2865caa985d2fb6283c5ab19574126623ffd615a761a5bce72",
-      "cb46ac9ccc024ee74c96e3cf1c13a6949a432e855dfa881b6a307c0e6daac59e",
-      "9971574924c0f413bf4c0f96bb9c2fbdfea8f475e33a8fb6f15fa40903b63444",
-      "1a95567deb0f45a8941e2248f33286485984a5e9d86d16c37d42169ad864dc36",
-      "205fc7f7ec7a83f0bc22d5269c91762cd00adc7428456d799be5a0cd76f08b0f",
-      "849dd41ef59a722901b7a0deb2c1fd3c110a91a726120a0a119cb7a15cf98438",
-      "a32880917c714612101af95e5c8d8eb5fb046fdcc68bae76c05b829b3fa73c2e",
-      "70b38d6d510d13b359dffa910329952c620a4bce4ee7a8552b9bb3a14572394d",
-      "ef257ab2f4226faa6ba288a6793f026609068effb866c18496a847e8b60b102d",
-      "e5196ab42ff53c8352288bde6b6b7312cd6f39f7d21b556b0db178d8470d5790",
-      "ca98f128bf085f2b718f2b3c12da7c4d98887cc94251a2b1705b637611bd83bb",
-      "79508f0b93a49ec19c5cb05906ca1ba3d3db8ed4f9c6884873d0d7e3e985ea51",
-      "9088be3f47f9debc63e928739f7163182b49eab044518b151f0b89f6b6aefdd0",
-      "46b2782fd669b6288a4d7348cf6671360277ba4864cc69bce3497369ac2ec31e",
-      "0fa5131557db67b430d516530be939ff25882adf68a076602f3dfad8c77c963a",
-      "3404302cc097d5457244453a4c9990804201ee8161188df811bcb32404998c71",
-      "856939710dbb90a8eeda875a31f9a52af759bd932b88e7b08df35414c54d4721",
-      "72569573b9b41d0ac5ce17764a139c6b8b36ef3ca6d92cec625dbcdae758ba22",
-      "9746da344e435a008d6acb4847211bb676376ecc76c825b5d44a28b89ceeb40e",
-      "3eafded1595516f032e33ec975f4c9c3a1055d13aa5575cf8a801d6103fdbeb4",
-      "e88a6d2daa863c0787cc523a2cab45c546fad788951b10d75e2b0954db24cca7",
-      "38f531e67f88f66de44d3357c8e8f2db456160ca31dd2024c9562f6afd260278",
-    };
-    // block 685498 (13 key images in one transaction)
-    static const char * const key_images_685498[] =
-    {
-      "749b7277aa21c70c417f255fb181c3a30b44277edf657eaaebf28a2709dd2a90",
-      "5a9b3e1a87332d735cedaa2b5623a6a5e99d99f5a2887c8fc84293577e8bf25c",
-      "bea438768445eb3650cf619bf6758e94035abfe0ccda91d4a58c582df143d835",
-      "376e237ff4da5e5cbd6e1bba4b01412fa751f2959c0c57006589f382413df429",
-      "14ac2f2e044f8635a3a42ecb46c575424073c1f6e5ed5e905f516d57f63184b5",
-      "2d1d2ecb77b69a2901de00897d0509c1b7855a3b2f8eb1afe419008fc03cd15a",
-      "ea01658f0972b77ae9112d525ec073e3ec5c3b98d5ad912d95ab2636354b70b6",
-      "d3934864a46101d8c242415282e7fc9ee73ad16cd40355535d226ab45ecdb61a",
-      "ee379b05c5d02432330ebd4ea9c4f1c87d14c388568d526a0f8a22649a14e453",
-      "aeb7b842b410b13ca4af7a5ffd5ae6caddc8bfec653df1b945e478839a2e0057",
-      "451806929d9f5c3a7f365472703871abadc25b2a5a2d75472a45e86cd76c610b",
-      "272d9b9fcc9e253c08da9caf8233471150019582eaefef461c1f9ceff7e2c337",
-      "633cdedeb3b96ec4f234c670254c6f721e0b368d00b48c6b26759db7d62cf52d",
-    };
+    // From tx 17ce4c8feeb82a6d6adaa8a89724b32bf4456f6909c7f84c8ce3ee9ebba19163
+    static const char * const first_missing_key_image_202612 =
+      "121bf6ae1596983b703d62fecf60ea7dd3c3909acf1e0911652e7dadb420ed12";
 
     if (height() > 202612)
     {
-      for (const auto &kis: key_images_202612)
+      crypto::key_image ki;
+      epee::string_tools::hex_to_pod(first_missing_key_image_202612, ki);
+      if (!has_key_image(ki))
       {
-        crypto::key_image ki;
-        epee::string_tools::hex_to_pod(kis, ki);
-        if (!has_key_image(ki))
-        {
-          LOG_PRINT_L1("Fixup: adding missing spent key " << ki);
-          add_spent_key(ki);
-        }
-      }
-    }
-    if (height() > 685498)
-    {
-      for (const auto &kis: key_images_685498)
-      {
-        crypto::key_image ki;
-        epee::string_tools::hex_to_pod(kis, ki);
-        if (!has_key_image(ki))
-        {
-          LOG_PRINT_L1("Fixup: adding missing spent key " << ki);
-          add_spent_key(ki);
-        }
+        LOG_PRINT_L1("Fixup: detected missing key images from block 202612! Popping blocks...");
+        while (height() > 202612)
+          pop_block();
       }
     }
   }
-  batch_stop();
 }
 
 bool BlockchainDB::txpool_tx_matches_category(const crypto::hash& tx_hash, relay_category category)
diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h
index 9628a5c4d..3e953da30 100644
--- a/src/blockchain_db/blockchain_db.h
+++ b/src/blockchain_db/blockchain_db.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
@@ -718,41 +718,6 @@ public:
    */
   virtual std::string get_db_name() const = 0;
 
-
-  // FIXME: these are just for functionality mocking, need to implement
-  // RAII-friendly and multi-read one-write friendly locking mechanism
-  //
-  // acquire db lock
-  /**
-   * @brief acquires the BlockchainDB lock
-   *
-   * This function is a stub until such a time as locking is implemented at
-   * this level.
-   *
-   * The subclass implementation should return true unless implementing a
-   * locking scheme of some sort, in which case it should return true upon
-   * acquisition of the lock and block until then.
-   *
-   * If any of this cannot be done, the subclass should throw the corresponding
-   * subclass of DB_EXCEPTION
-   *
-   * @return true, unless at a future time false makes sense (timeout, etc)
-   */
-  virtual bool lock() = 0;
-
-  // release db lock
-  /**
-   * @brief This function releases the BlockchainDB lock
-   *
-   * The subclass, should it have implemented lock(), will release any lock
-   * held by the calling thread.  In the case of recursive locking, it should
-   * release one instance of a lock.
-   *
-   * If any of this cannot be done, the subclass should throw the corresponding
-   * subclass of DB_EXCEPTION
-   */
-  virtual void unlock() = 0;
-
   /**
    * @brief tells the BlockchainDB to start a new "batch" of blocks
    *
diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp
index 2c015faee..d01119249 100644
--- a/src/blockchain_db/lmdb/db_lmdb.cpp
+++ b/src/blockchain_db/lmdb/db_lmdb.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without modification, are
@@ -1688,22 +1688,6 @@ std::string BlockchainLMDB::get_db_name() const
   return std::string("lmdb");
 }
 
-// TODO: this?
-bool BlockchainLMDB::lock()
-{
-  LOG_PRINT_L3("BlockchainLMDB::" << __func__);
-  check_open();
-  return false;
-}
-
-// TODO: this?
-void BlockchainLMDB::unlock()
-{
-  LOG_PRINT_L3("BlockchainLMDB::" << __func__);
-  check_open();
-}
-
-
 // The below two macros are for DB access within block add/remove, whether
 // regular batch txn is in use or not. m_write_txn is used as a batch txn, even
 // if it's only within block add/remove.
diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h
index 95e7b2aa4..6eeb942dc 100644
--- a/src/blockchain_db/lmdb/db_lmdb.h
+++ b/src/blockchain_db/lmdb/db_lmdb.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without modification, are
@@ -202,10 +202,6 @@ public:
 
   virtual std::string get_db_name() const;
 
-  virtual bool lock();
-
-  virtual void unlock();
-
   virtual bool block_exists(const crypto::hash& h, uint64_t *height = NULL) const;
 
   virtual uint64_t get_block_height(const crypto::hash& h) const;
diff --git a/src/blockchain_db/locked_txn.h b/src/blockchain_db/locked_txn.h
index 6170d7b5a..88f9ecb0f 100644
--- a/src/blockchain_db/locked_txn.h
+++ b/src/blockchain_db/locked_txn.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/blockchain_db/testdb.h b/src/blockchain_db/testdb.h
index a27183b2c..308bdd4c2 100644
--- a/src/blockchain_db/testdb.h
+++ b/src/blockchain_db/testdb.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
@@ -50,8 +50,6 @@ public:
   virtual std::vector<std::string> get_filenames() const override { return std::vector<std::string>(); }
   virtual bool remove_data_file(const std::string& folder) const override { return true; }
   virtual std::string get_db_name() const override { return std::string(); }
-  virtual bool lock() override { return true; }
-  virtual void unlock() override { }
   virtual bool batch_start(uint64_t batch_num_blocks=0, uint64_t batch_bytes=0) override { return true; }
   virtual void batch_stop() override {}
   virtual void batch_abort() override {}
diff --git a/src/blockchain_utilities/CMakeLists.txt b/src/blockchain_utilities/CMakeLists.txt
index 224a9e63c..a41cd1e53 100644
--- a/src/blockchain_utilities/CMakeLists.txt
+++ b/src/blockchain_utilities/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/src/blockchain_utilities/README.md b/src/blockchain_utilities/README.md
index 4fcd138c4..ef691138c 100644
--- a/src/blockchain_utilities/README.md
+++ b/src/blockchain_utilities/README.md
@@ -1,6 +1,6 @@
 # Monero Blockchain Utilities
 
-Copyright (c) 2014-2023, The Monero Project
+Copyright (c) 2014-2024, The Monero Project
 
 ## Introduction
 
diff --git a/src/blockchain_utilities/blockchain_ancestry.cpp b/src/blockchain_utilities/blockchain_ancestry.cpp
index 36c17357a..bdf5a1219 100644
--- a/src/blockchain_utilities/blockchain_ancestry.cpp
+++ b/src/blockchain_utilities/blockchain_ancestry.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/blockchain_utilities/blockchain_blackball.cpp b/src/blockchain_utilities/blockchain_blackball.cpp
index 83051e290..522e40c3c 100644
--- a/src/blockchain_utilities/blockchain_blackball.cpp
+++ b/src/blockchain_utilities/blockchain_blackball.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/blockchain_utilities/blockchain_depth.cpp b/src/blockchain_utilities/blockchain_depth.cpp
index f49211233..c59052b76 100644
--- a/src/blockchain_utilities/blockchain_depth.cpp
+++ b/src/blockchain_utilities/blockchain_depth.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/blockchain_utilities/blockchain_export.cpp b/src/blockchain_utilities/blockchain_export.cpp
index 0611b3640..7702ba8e8 100644
--- a/src/blockchain_utilities/blockchain_export.cpp
+++ b/src/blockchain_utilities/blockchain_export.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/blockchain_utilities/blockchain_import.cpp b/src/blockchain_utilities/blockchain_import.cpp
index f75a1e827..73ca6ae0c 100644
--- a/src/blockchain_utilities/blockchain_import.cpp
+++ b/src/blockchain_utilities/blockchain_import.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/blockchain_utilities/blockchain_prune.cpp b/src/blockchain_utilities/blockchain_prune.cpp
index 1a9618617..a55d529c0 100644
--- a/src/blockchain_utilities/blockchain_prune.cpp
+++ b/src/blockchain_utilities/blockchain_prune.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 //
 // All rights reserved.
diff --git a/src/blockchain_utilities/blockchain_prune_known_spent_data.cpp b/src/blockchain_utilities/blockchain_prune_known_spent_data.cpp
index 4a459dc66..6e7dd2987 100644
--- a/src/blockchain_utilities/blockchain_prune_known_spent_data.cpp
+++ b/src/blockchain_utilities/blockchain_prune_known_spent_data.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/blockchain_utilities/blockchain_stats.cpp b/src/blockchain_utilities/blockchain_stats.cpp
index f0a8e5adc..3fbdae548 100644
--- a/src/blockchain_utilities/blockchain_stats.cpp
+++ b/src/blockchain_utilities/blockchain_stats.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/blockchain_utilities/blockchain_usage.cpp b/src/blockchain_utilities/blockchain_usage.cpp
index 0b9686765..9865478de 100644
--- a/src/blockchain_utilities/blockchain_usage.cpp
+++ b/src/blockchain_utilities/blockchain_usage.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/blockchain_utilities/blockchain_utilities.h b/src/blockchain_utilities/blockchain_utilities.h
index 13e2ef428..183461092 100644
--- a/src/blockchain_utilities/blockchain_utilities.h
+++ b/src/blockchain_utilities/blockchain_utilities.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/blockchain_utilities/blocksdat_file.cpp b/src/blockchain_utilities/blocksdat_file.cpp
index 6a469289d..3e015587e 100644
--- a/src/blockchain_utilities/blocksdat_file.cpp
+++ b/src/blockchain_utilities/blocksdat_file.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/blockchain_utilities/blocksdat_file.h b/src/blockchain_utilities/blocksdat_file.h
index b80440880..4e63361a7 100644
--- a/src/blockchain_utilities/blocksdat_file.h
+++ b/src/blockchain_utilities/blocksdat_file.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/blockchain_utilities/bootstrap_file.cpp b/src/blockchain_utilities/bootstrap_file.cpp
index 9375b63b0..a20a6245a 100644
--- a/src/blockchain_utilities/bootstrap_file.cpp
+++ b/src/blockchain_utilities/bootstrap_file.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/blockchain_utilities/bootstrap_file.h b/src/blockchain_utilities/bootstrap_file.h
index 808ed00bb..7e286d653 100644
--- a/src/blockchain_utilities/bootstrap_file.h
+++ b/src/blockchain_utilities/bootstrap_file.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/blockchain_utilities/bootstrap_serialization.h b/src/blockchain_utilities/bootstrap_serialization.h
index d6ab90a99..667ab3207 100644
--- a/src/blockchain_utilities/bootstrap_serialization.h
+++ b/src/blockchain_utilities/bootstrap_serialization.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/blocks/CMakeLists.txt b/src/blocks/CMakeLists.txt
index 60b27baf3..b2d208d45 100644
--- a/src/blocks/CMakeLists.txt
+++ b/src/blocks/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 # 
 # All rights reserved.
 # 
diff --git a/src/blocks/checkpoints.dat b/src/blocks/checkpoints.dat
index 2dc9ce3b3..2f1647ef5 100644
Binary files a/src/blocks/checkpoints.dat and b/src/blocks/checkpoints.dat differ
diff --git a/src/checkpoints/CMakeLists.txt b/src/checkpoints/CMakeLists.txt
index 680085757..cc7f261ed 100644
--- a/src/checkpoints/CMakeLists.txt
+++ b/src/checkpoints/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/src/checkpoints/checkpoints.cpp b/src/checkpoints/checkpoints.cpp
index 63ec6ede9..7007da820 100644
--- a/src/checkpoints/checkpoints.cpp
+++ b/src/checkpoints/checkpoints.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
@@ -250,6 +250,7 @@ namespace cryptonote
     ADD_CHECKPOINT2(2985000, "08f5e6b7301c1b6ed88268a28f8677a06e8ff943b3f9e48d3080f71f9c134bfb", "0x444b7b42a633c96");
     ADD_CHECKPOINT2(3088000, "bddf8ca09110d33d6d497f13a113630c2b6af1c84d4f3a6f35cb1446f2604ade", "0x4aed3615c2f8c3e");
     ADD_CHECKPOINT2(3102800, "083f4a34f9490403b564286e7f13fd1ed45c52c86fa47195f151594e5bc87504", "0x4bbed52d4da5dfb");
+    ADD_CHECKPOINT2(3198000, "1d685b39be51e4e84e0af69fa78e023c7cb21de7d33acd012d0371d5f78712d5", "0x517d415fee3a816");
     return true;
   }
 
diff --git a/src/checkpoints/checkpoints.h b/src/checkpoints/checkpoints.h
index 7a93213c9..53f06ad6d 100644
--- a/src/checkpoints/checkpoints.h
+++ b/src/checkpoints/checkpoints.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index cf38466fe..edbb2a27d 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/src/common/aligned.c b/src/common/aligned.c
index 9ffa01f92..c6d6e8e53 100644
--- a/src/common/aligned.c
+++ b/src/common/aligned.c
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/common/aligned.h b/src/common/aligned.h
index 7c201e000..703acb257 100644
--- a/src/common/aligned.h
+++ b/src/common/aligned.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 // 
 // All rights reserved.
diff --git a/src/common/apply_permutation.h b/src/common/apply_permutation.h
index 1f8fce66f..479dbc06d 100644
--- a/src/common/apply_permutation.h
+++ b/src/common/apply_permutation.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/common/base58.cpp b/src/common/base58.cpp
index efa1d3fc8..6eda78388 100644
--- a/src/common/base58.cpp
+++ b/src/common/base58.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/common/base58.h b/src/common/base58.h
index 7e37a57ea..04264c5d5 100644
--- a/src/common/base58.h
+++ b/src/common/base58.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/common/combinator.cpp b/src/common/combinator.cpp
index ea68f3414..c26c31358 100644
--- a/src/common/combinator.cpp
+++ b/src/common/combinator.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 //
 // All rights reserved.
diff --git a/src/common/combinator.h b/src/common/combinator.h
index 6ef244a1e..d08ac52b9 100644
--- a/src/common/combinator.h
+++ b/src/common/combinator.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 //
 // All rights reserved.
diff --git a/src/common/command_line.cpp b/src/common/command_line.cpp
index 1380622b2..50425536d 100644
--- a/src/common/command_line.cpp
+++ b/src/common/command_line.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/common/command_line.h b/src/common/command_line.h
index bd5a32edb..681eb903b 100644
--- a/src/common/command_line.h
+++ b/src/common/command_line.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/common/common_fwd.h b/src/common/common_fwd.h
index 04aa3ce0a..cd93ae09c 100644
--- a/src/common/common_fwd.h
+++ b/src/common/common_fwd.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/common/container_helpers.h b/src/common/container_helpers.h
index 5824c9c37..c42f65efb 100644
--- a/src/common/container_helpers.h
+++ b/src/common/container_helpers.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2022, The Monero Project
+// Copyright (c) 2022-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/common/data_cache.h b/src/common/data_cache.h
index 5e70da115..8b0f26ea8 100644
--- a/src/common/data_cache.h
+++ b/src/common/data_cache.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2022, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/common/dns_utils.cpp b/src/common/dns_utils.cpp
index 6fc784fb2..7cad1c176 100644
--- a/src/common/dns_utils.cpp
+++ b/src/common/dns_utils.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/common/dns_utils.h b/src/common/dns_utils.h
index 87c6ec140..334ac8a79 100644
--- a/src/common/dns_utils.h
+++ b/src/common/dns_utils.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/common/download.cpp b/src/common/download.cpp
index 7d23568e2..c7f87ad9d 100644
--- a/src/common/download.cpp
+++ b/src/common/download.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/common/download.h b/src/common/download.h
index ee7353089..88f5b4095 100644
--- a/src/common/download.h
+++ b/src/common/download.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/common/error.cpp b/src/common/error.cpp
index 0be5586ed..66dadd57b 100644
--- a/src/common/error.cpp
+++ b/src/common/error.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 // All rights reserved.
 //
diff --git a/src/common/error.h b/src/common/error.h
index b79731162..930b47d91 100644
--- a/src/common/error.h
+++ b/src/common/error.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 // All rights reserved.
 //
diff --git a/src/common/expect.h b/src/common/expect.h
index 64a753f45..4978fda9c 100644
--- a/src/common/expect.h
+++ b/src/common/expect.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 //
 // All rights reserved.
diff --git a/src/common/http_connection.h b/src/common/http_connection.h
index cf9a68d8d..6b4ecb083 100644
--- a/src/common/http_connection.h
+++ b/src/common/http_connection.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/common/i18n.cpp b/src/common/i18n.cpp
index 352c7879f..0dacb1fdc 100644
--- a/src/common/i18n.cpp
+++ b/src/common/i18n.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/common/i18n.h b/src/common/i18n.h
index 55506ce02..bc1287cf3 100644
--- a/src/common/i18n.h
+++ b/src/common/i18n.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/common/json_util.h b/src/common/json_util.h
index e512f4df8..145fb3eb2 100644
--- a/src/common/json_util.h
+++ b/src/common/json_util.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2016-2023, The Monero Project
+// Copyright (c) 2016-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/common/notify.cpp b/src/common/notify.cpp
index e1d6ee7f5..6d24b4d65 100644
--- a/src/common/notify.cpp
+++ b/src/common/notify.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 // 
 // All rights reserved.
diff --git a/src/common/notify.h b/src/common/notify.h
index fa7438585..95b74ada6 100644
--- a/src/common/notify.h
+++ b/src/common/notify.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 // 
 // All rights reserved.
diff --git a/src/common/password.cpp b/src/common/password.cpp
index 7ba4546d3..084e40d4b 100644
--- a/src/common/password.cpp
+++ b/src/common/password.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/common/password.h b/src/common/password.h
index 2d889f4e6..81100b385 100644
--- a/src/common/password.h
+++ b/src/common/password.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/common/perf_timer.cpp b/src/common/perf_timer.cpp
index 229748994..877b80d1c 100644
--- a/src/common/perf_timer.cpp
+++ b/src/common/perf_timer.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2016-2023, The Monero Project
+// Copyright (c) 2016-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/common/perf_timer.h b/src/common/perf_timer.h
index 31df89fee..2a3cad075 100644
--- a/src/common/perf_timer.h
+++ b/src/common/perf_timer.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2016-2023, The Monero Project
+// Copyright (c) 2016-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/common/pod-class.h b/src/common/pod-class.h
index 3cdde72a7..b285521c8 100644
--- a/src/common/pod-class.h
+++ b/src/common/pod-class.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/common/pruning.cpp b/src/common/pruning.cpp
index 48c365425..bb959f5e1 100644
--- a/src/common/pruning.cpp
+++ b/src/common/pruning.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 // 
 // All rights reserved.
diff --git a/src/common/pruning.h b/src/common/pruning.h
index a0d33bf66..c6ecb1e7c 100644
--- a/src/common/pruning.h
+++ b/src/common/pruning.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 // 
 // All rights reserved.
diff --git a/src/common/rpc_client.h b/src/common/rpc_client.h
index f6c1ea100..4bc75a1be 100644
--- a/src/common/rpc_client.h
+++ b/src/common/rpc_client.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/common/scoped_message_writer.h b/src/common/scoped_message_writer.h
index 5814d7ff5..fbaf47c54 100644
--- a/src/common/scoped_message_writer.h
+++ b/src/common/scoped_message_writer.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/common/sfinae_helpers.h b/src/common/sfinae_helpers.h
index fe9163c08..0419121d4 100644
--- a/src/common/sfinae_helpers.h
+++ b/src/common/sfinae_helpers.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2016-2023, The Monero Project
+// Copyright (c) 2016-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/common/spawn.cpp b/src/common/spawn.cpp
index 1bdf0ddda..de8e70bc3 100644
--- a/src/common/spawn.cpp
+++ b/src/common/spawn.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 // 
 // All rights reserved.
diff --git a/src/common/spawn.h b/src/common/spawn.h
index 55b2333b1..9ea957886 100644
--- a/src/common/spawn.h
+++ b/src/common/spawn.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 // 
 // All rights reserved.
diff --git a/src/common/stack_trace.cpp b/src/common/stack_trace.cpp
index bef14b244..ce46304c0 100644
--- a/src/common/stack_trace.cpp
+++ b/src/common/stack_trace.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2016-2023, The Monero Project
+// Copyright (c) 2016-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/common/stack_trace.h b/src/common/stack_trace.h
index 77af0c58f..a7cd4afd8 100644
--- a/src/common/stack_trace.h
+++ b/src/common/stack_trace.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2016-2023, The Monero Project
+// Copyright (c) 2016-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/common/threadpool.cpp b/src/common/threadpool.cpp
index ff6277af5..faa45e8bd 100644
--- a/src/common/threadpool.cpp
+++ b/src/common/threadpool.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/common/threadpool.h b/src/common/threadpool.h
index 321f6682a..8d32f4252 100644
--- a/src/common/threadpool.h
+++ b/src/common/threadpool.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/common/unordered_containers_boost_serialization.h b/src/common/unordered_containers_boost_serialization.h
index 49bbe1956..cbb86022b 100644
--- a/src/common/unordered_containers_boost_serialization.h
+++ b/src/common/unordered_containers_boost_serialization.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/common/updates.cpp b/src/common/updates.cpp
index 15efb94c1..8c8600645 100644
--- a/src/common/updates.cpp
+++ b/src/common/updates.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/common/updates.h b/src/common/updates.h
index da1c8353b..8fbc0f57d 100644
--- a/src/common/updates.h
+++ b/src/common/updates.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/common/utf8.h b/src/common/utf8.h
index d21509c9c..9a48be632 100644
--- a/src/common/utf8.h
+++ b/src/common/utf8.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2023, The Monero Project
+// Copyright (c) 2019-2024, The Monero Project
 
 // 
 // All rights reserved.
diff --git a/src/common/util.cpp b/src/common/util.cpp
index 05ddfc359..47d671057 100644
--- a/src/common/util.cpp
+++ b/src/common/util.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/common/util.h b/src/common/util.h
index ef591fc12..772881ecc 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/common/va_args.h b/src/common/va_args.h
new file mode 100644
index 000000000..3389b1cb9
--- /dev/null
+++ b/src/common/va_args.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2024, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+//    conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+//    of conditions and the following disclaimer in the documentation and/or other
+//    materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+//    used to endorse or promote products derived from this software without specific
+//    prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+// Check for __VA_OPT__ support
+// Apdated from cpplearner's StackOverflow answer: https://stackoverflow.com/a/48045656
+#define PP_THIRD_ARG(a,b,c,...) c
+#define VA_OPT_SUPPORTED_I(...) PP_THIRD_ARG(__VA_OPT__(,),true,false,)
+#define VA_OPT_SUPPORTED VA_OPT_SUPPORTED_I(?)
+
+// VA_ARGS_COMMAPREFIX(): VA_ARGS_COMMAPREFIX(__VA_ARGS__) expands to __VA_ARGS__ with a comma in
+// front if more than one argument, else nothing.
+// If __VA_OPT__ supported, use that. Else, use GCC's ,## hack
+#if VA_OPT_SUPPORTED
+#    define VA_ARGS_COMMAPREFIX(...) __VA_OPT__(,) __VA_ARGS__
+#else
+#    define VA_ARGS_COMMAPREFIX(...) , ## __VA_ARGS__
+#endif
+
diff --git a/src/common/variant.h b/src/common/variant.h
index ffb34e40a..a7e2637e5 100644
--- a/src/common/variant.h
+++ b/src/common/variant.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2022, The Monero Project
+// Copyright (c) 2022-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/common/varint.h b/src/common/varint.h
index 58d3bbb84..dfe980515 100644
--- a/src/common/varint.h
+++ b/src/common/varint.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/crypto/CMakeLists.txt b/src/crypto/CMakeLists.txt
index 2559c754e..b0006faba 100644
--- a/src/crypto/CMakeLists.txt
+++ b/src/crypto/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/src/crypto/blake256.c b/src/crypto/blake256.c
index 83161fbb5..23051ac7e 100644
--- a/src/crypto/blake256.c
+++ b/src/crypto/blake256.c
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/crypto/blake256.h b/src/crypto/blake256.h
index 5bde2cb2f..81c9e11f2 100644
--- a/src/crypto/blake256.h
+++ b/src/crypto/blake256.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/crypto/c_threads.h b/src/crypto/c_threads.h
index e6c846db6..033da25a6 100644
--- a/src/crypto/c_threads.h
+++ b/src/crypto/c_threads.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2023, The Monero Project
+// Copyright (c) 2019-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/crypto/chacha.h b/src/crypto/chacha.h
index 80170dea6..8f1edbdf1 100644
--- a/src/crypto/chacha.h
+++ b/src/crypto/chacha.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/crypto/crypto-ops-data.c b/src/crypto/crypto-ops-data.c
index e31cd6b9c..edaa4644f 100644
--- a/src/crypto/crypto-ops-data.c
+++ b/src/crypto/crypto-ops-data.c
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/crypto/crypto-ops.c b/src/crypto/crypto-ops.c
index e9a5cc6f6..314fe448a 100644
--- a/src/crypto/crypto-ops.c
+++ b/src/crypto/crypto-ops.c
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/crypto/crypto-ops.h b/src/crypto/crypto-ops.h
index 49bc8e081..c103f1f78 100644
--- a/src/crypto/crypto-ops.h
+++ b/src/crypto/crypto-ops.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/crypto/crypto.cpp b/src/crypto/crypto.cpp
index 0ab4cad36..f6c94fa03 100644
--- a/src/crypto/crypto.cpp
+++ b/src/crypto/crypto.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
index 81d6e1803..6b4126246 100644
--- a/src/crypto/crypto.h
+++ b/src/crypto/crypto.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/crypto/crypto_ops_builder/README.md b/src/crypto/crypto_ops_builder/README.md
index aae9e88cd..16b1fe4f8 100644
--- a/src/crypto/crypto_ops_builder/README.md
+++ b/src/crypto/crypto_ops_builder/README.md
@@ -1,6 +1,6 @@
 # Monero
 
-Copyright (c) 2014-2023, The Monero Project
+Copyright (c) 2014-2024, The Monero Project
 
 ## Crypto Ops Builder
 
diff --git a/src/crypto/crypto_ops_builder/crypto-ops-data.c b/src/crypto/crypto_ops_builder/crypto-ops-data.c
index ac1cf82e5..bab0ea176 100644
--- a/src/crypto/crypto_ops_builder/crypto-ops-data.c
+++ b/src/crypto/crypto_ops_builder/crypto-ops-data.c
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/crypto/crypto_ops_builder/crypto-ops-old.c b/src/crypto/crypto_ops_builder/crypto-ops-old.c
index 10c25bd1e..ab833cf18 100644
--- a/src/crypto/crypto_ops_builder/crypto-ops-old.c
+++ b/src/crypto/crypto_ops_builder/crypto-ops-old.c
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/crypto/crypto_ops_builder/crypto-ops.h b/src/crypto/crypto_ops_builder/crypto-ops.h
index 171343676..b90e6fd70 100644
--- a/src/crypto/crypto_ops_builder/crypto-ops.h
+++ b/src/crypto/crypto_ops_builder/crypto-ops.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/crypto/crypto_ops_builder/ref10CommentedCombined/MakeCryptoOps.py b/src/crypto/crypto_ops_builder/ref10CommentedCombined/MakeCryptoOps.py
index 5bb88f0a9..63511419b 100644
--- a/src/crypto/crypto_ops_builder/ref10CommentedCombined/MakeCryptoOps.py
+++ b/src/crypto/crypto_ops_builder/ref10CommentedCombined/MakeCryptoOps.py
@@ -15,7 +15,7 @@ print("maybe someone smart can replace the sed with perl..")
 a = ""
 
 license = textwrap.dedent("""\
-    // Copyright (c) 2014-2023, The Monero Project
+    // Copyright (c) 2014-2024, The Monero Project
     // 
     // All rights reserved.
     // 
diff --git a/src/crypto/crypto_ops_builder/ref10CommentedCombined/crypto-ops.h b/src/crypto/crypto_ops_builder/ref10CommentedCombined/crypto-ops.h
index 4badb59d6..3a233ed77 100644
--- a/src/crypto/crypto_ops_builder/ref10CommentedCombined/crypto-ops.h
+++ b/src/crypto/crypto_ops_builder/ref10CommentedCombined/crypto-ops.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/crypto/duration.h b/src/crypto/duration.h
index ad9f0a8c4..15c1add54 100644
--- a/src/crypto/duration.h
+++ b/src/crypto/duration.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2020-2023, The Monero Project
+// Copyright (c) 2020-2024, The Monero Project
 
 //
 // All rights reserved.
diff --git a/src/crypto/generators.cpp b/src/crypto/generators.cpp
index a4539f473..493d18334 100644
--- a/src/crypto/generators.cpp
+++ b/src/crypto/generators.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2022, The Monero Project
+// Copyright (c) 2022-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/crypto/generators.h b/src/crypto/generators.h
index 797336203..c7d5e693e 100644
--- a/src/crypto/generators.h
+++ b/src/crypto/generators.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2022, The Monero Project
+// Copyright (c) 2022-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/crypto/generic-ops.h b/src/crypto/generic-ops.h
index da19157f6..107f4cfdc 100644
--- a/src/crypto/generic-ops.h
+++ b/src/crypto/generic-ops.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/crypto/groestl.h b/src/crypto/groestl.h
index 6fb0a26f4..488dca89b 100644
--- a/src/crypto/groestl.h
+++ b/src/crypto/groestl.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/crypto/groestl_tables.h b/src/crypto/groestl_tables.h
index c553b98f5..fa04698a0 100644
--- a/src/crypto/groestl_tables.h
+++ b/src/crypto/groestl_tables.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/crypto/hash-extra-blake.c b/src/crypto/hash-extra-blake.c
index f3e91a0a8..9a2ab8e37 100644
--- a/src/crypto/hash-extra-blake.c
+++ b/src/crypto/hash-extra-blake.c
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/crypto/hash-extra-groestl.c b/src/crypto/hash-extra-groestl.c
index 7d8e84eaa..7ef21d3a6 100644
--- a/src/crypto/hash-extra-groestl.c
+++ b/src/crypto/hash-extra-groestl.c
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/crypto/hash-extra-jh.c b/src/crypto/hash-extra-jh.c
index f2180a5dc..4c0478e2e 100644
--- a/src/crypto/hash-extra-jh.c
+++ b/src/crypto/hash-extra-jh.c
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/crypto/hash-extra-skein.c b/src/crypto/hash-extra-skein.c
index 07bfdccd0..22403b41a 100644
--- a/src/crypto/hash-extra-skein.c
+++ b/src/crypto/hash-extra-skein.c
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/crypto/hash-ops.h b/src/crypto/hash-ops.h
index 83cc78ce2..782ab1957 100644
--- a/src/crypto/hash-ops.h
+++ b/src/crypto/hash-ops.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/crypto/hash.c b/src/crypto/hash.c
index b73a8587a..36fd7d7c7 100644
--- a/src/crypto/hash.c
+++ b/src/crypto/hash.c
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/crypto/hash.h b/src/crypto/hash.h
index c1fccc50c..e2788ef74 100644
--- a/src/crypto/hash.h
+++ b/src/crypto/hash.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/crypto/hmac-keccak.c b/src/crypto/hmac-keccak.c
index 0bdbf6d0f..b88417c2b 100644
--- a/src/crypto/hmac-keccak.c
+++ b/src/crypto/hmac-keccak.c
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/crypto/hmac-keccak.h b/src/crypto/hmac-keccak.h
index 9d92a693a..4953cb147 100644
--- a/src/crypto/hmac-keccak.h
+++ b/src/crypto/hmac-keccak.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/crypto/initializer.h b/src/crypto/initializer.h
index 57c15d3e7..55617297f 100644
--- a/src/crypto/initializer.h
+++ b/src/crypto/initializer.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/crypto/random.c b/src/crypto/random.c
index 854b29547..643d15801 100644
--- a/src/crypto/random.c
+++ b/src/crypto/random.c
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/crypto/random.h b/src/crypto/random.h
index 5bb80728a..41601e456 100644
--- a/src/crypto/random.h
+++ b/src/crypto/random.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/crypto/rx-slow-hash.c b/src/crypto/rx-slow-hash.c
index 4ba8f0561..d5bce71ac 100644
--- a/src/crypto/rx-slow-hash.c
+++ b/src/crypto/rx-slow-hash.c
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2023, The Monero Project
+// Copyright (c) 2019-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/crypto/skein_port.h b/src/crypto/skein_port.h
index 6ba83d3d7..bc11623c7 100644
--- a/src/crypto/skein_port.h
+++ b/src/crypto/skein_port.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c
index 6b975f627..47ae2dab8 100644
--- a/src/crypto/slow-hash.c
+++ b/src/crypto/slow-hash.c
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/crypto/tree-hash.c b/src/crypto/tree-hash.c
index 0959b5050..62fb68f6e 100644
--- a/src/crypto/tree-hash.c
+++ b/src/crypto/tree-hash.c
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/crypto/wallet/CMakeLists.txt b/src/crypto/wallet/CMakeLists.txt
index e724b3e96..da0766133 100644
--- a/src/crypto/wallet/CMakeLists.txt
+++ b/src/crypto/wallet/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2020-2023, The Monero Project
+# Copyright (c) 2020-2024, The Monero Project
 
 #
 # All rights reserved.
diff --git a/src/crypto/wallet/crypto.h b/src/crypto/wallet/crypto.h
index 27da956ec..be9aca46a 100644
--- a/src/crypto/wallet/crypto.h
+++ b/src/crypto/wallet/crypto.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2020-2023, The Monero Project
+// Copyright (c) 2020-2024, The Monero Project
 
 //
 // All rights reserved.
diff --git a/src/crypto/wallet/empty.h.in b/src/crypto/wallet/empty.h.in
index bf54f54c5..0a69f065f 100644
--- a/src/crypto/wallet/empty.h.in
+++ b/src/crypto/wallet/empty.h.in
@@ -1,4 +1,4 @@
-// Copyright (c) 2020-2023, The Monero Project
+// Copyright (c) 2020-2024, The Monero Project
 
 //
 // All rights reserved.
diff --git a/src/cryptonote_basic/CMakeLists.txt b/src/cryptonote_basic/CMakeLists.txt
index 864306cf7..639c5a654 100644
--- a/src/cryptonote_basic/CMakeLists.txt
+++ b/src/cryptonote_basic/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/src/cryptonote_basic/account.cpp b/src/cryptonote_basic/account.cpp
index 9dc6e387d..686796ce4 100644
--- a/src/cryptonote_basic/account.cpp
+++ b/src/cryptonote_basic/account.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
@@ -152,6 +152,17 @@ DISABLE_VS_WARNINGS(4244 4345)
     m_keys.m_multisig_keys.clear();
   }
   //-----------------------------------------------------------------
+  void account_base::set_spend_key(const crypto::secret_key& spend_secret_key)
+  {
+    // make sure derived spend public key matches saved public spend key
+    crypto::public_key spend_public_key;
+    crypto::secret_key_to_public_key(spend_secret_key, spend_public_key);
+    CHECK_AND_ASSERT_THROW_MES(m_keys.m_account_address.m_spend_public_key == spend_public_key,
+        "Unexpected derived public spend key");
+
+    m_keys.m_spend_secret_key = spend_secret_key;
+  }
+  //-----------------------------------------------------------------
   crypto::secret_key account_base::generate(const crypto::secret_key& recovery_key, bool recover, bool two_random)
   {
     crypto::secret_key first = generate_keys(m_keys.m_account_address.m_spend_public_key, m_keys.m_spend_secret_key, recovery_key, recover);
diff --git a/src/cryptonote_basic/account.h b/src/cryptonote_basic/account.h
index 7578aaf2a..de5912032 100644
--- a/src/cryptonote_basic/account.h
+++ b/src/cryptonote_basic/account.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
@@ -95,6 +95,7 @@ namespace cryptonote
     bool store(const std::string& file_path);
 
     void forget_spend_key();
+    void set_spend_key(const crypto::secret_key& spend_secret_key);
     const std::vector<crypto::secret_key> &get_multisig_keys() const { return m_keys.m_multisig_keys; }
 
     void encrypt_keys(const crypto::chacha_key &key) { m_keys.encrypt(key); }
diff --git a/src/cryptonote_basic/account_boost_serialization.h b/src/cryptonote_basic/account_boost_serialization.h
index ca1a1aa29..eed280d2c 100644
--- a/src/cryptonote_basic/account_boost_serialization.h
+++ b/src/cryptonote_basic/account_boost_serialization.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/cryptonote_basic/account_generators.h b/src/cryptonote_basic/account_generators.h
index c1102db60..00a31ae82 100644
--- a/src/cryptonote_basic/account_generators.h
+++ b/src/cryptonote_basic/account_generators.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2021, The Monero Project
+// Copyright (c) 2021-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/cryptonote_basic/blobdatatype.h b/src/cryptonote_basic/blobdatatype.h
index 98d05022c..a3c5610ec 100644
--- a/src/cryptonote_basic/blobdatatype.h
+++ b/src/cryptonote_basic/blobdatatype.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/cryptonote_basic/connection_context.cpp b/src/cryptonote_basic/connection_context.cpp
index 37cf31de4..4ad5951b3 100644
--- a/src/cryptonote_basic/connection_context.cpp
+++ b/src/cryptonote_basic/connection_context.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020-2023, The Monero Project
+// Copyright (c) 2020-2024, The Monero Project
 
 //
 // All rights reserved.
diff --git a/src/cryptonote_basic/connection_context.h b/src/cryptonote_basic/connection_context.h
index e88c4ba7e..ebd6f55d0 100644
--- a/src/cryptonote_basic/connection_context.h
+++ b/src/cryptonote_basic/connection_context.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/cryptonote_basic/cryptonote_basic.h b/src/cryptonote_basic/cryptonote_basic.h
index 0531439d6..a50ae9c32 100644
--- a/src/cryptonote_basic/cryptonote_basic.h
+++ b/src/cryptonote_basic/cryptonote_basic.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/cryptonote_basic/cryptonote_basic_impl.cpp b/src/cryptonote_basic/cryptonote_basic_impl.cpp
index 7fe398283..29afe2e01 100644
--- a/src/cryptonote_basic/cryptonote_basic_impl.cpp
+++ b/src/cryptonote_basic/cryptonote_basic_impl.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/cryptonote_basic/cryptonote_basic_impl.h b/src/cryptonote_basic/cryptonote_basic_impl.h
index 53dbc47d7..d8e954cfb 100644
--- a/src/cryptonote_basic/cryptonote_basic_impl.h
+++ b/src/cryptonote_basic/cryptonote_basic_impl.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/cryptonote_basic/cryptonote_boost_serialization.h b/src/cryptonote_basic/cryptonote_boost_serialization.h
index 954f429cb..8948c650c 100644
--- a/src/cryptonote_basic/cryptonote_boost_serialization.h
+++ b/src/cryptonote_basic/cryptonote_boost_serialization.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp
index c86e7d848..ca56c2bc3 100644
--- a/src/cryptonote_basic/cryptonote_format_utils.cpp
+++ b/src/cryptonote_basic/cryptonote_format_utils.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/cryptonote_basic/cryptonote_format_utils.h b/src/cryptonote_basic/cryptonote_format_utils.h
index bd4dcd83d..fc7dfcd85 100644
--- a/src/cryptonote_basic/cryptonote_format_utils.h
+++ b/src/cryptonote_basic/cryptonote_format_utils.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/cryptonote_basic/cryptonote_format_utils_basic.cpp b/src/cryptonote_basic/cryptonote_format_utils_basic.cpp
index fccfeccba..3f4e616c0 100644
--- a/src/cryptonote_basic/cryptonote_format_utils_basic.cpp
+++ b/src/cryptonote_basic/cryptonote_format_utils_basic.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/cryptonote_basic/difficulty.cpp b/src/cryptonote_basic/difficulty.cpp
index a9777c581..ca078827d 100644
--- a/src/cryptonote_basic/difficulty.cpp
+++ b/src/cryptonote_basic/difficulty.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/cryptonote_basic/difficulty.h b/src/cryptonote_basic/difficulty.h
index 93964646e..0e2e70dd6 100644
--- a/src/cryptonote_basic/difficulty.h
+++ b/src/cryptonote_basic/difficulty.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/cryptonote_basic/events.h b/src/cryptonote_basic/events.h
index 476ff6a22..62d0011b9 100644
--- a/src/cryptonote_basic/events.h
+++ b/src/cryptonote_basic/events.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2020-2023, The Monero Project
+// Copyright (c) 2020-2024, The Monero Project
 
 //
 // All rights reserved.
diff --git a/src/cryptonote_basic/fwd.h b/src/cryptonote_basic/fwd.h
index dd0d3aec3..9c618f5f2 100644
--- a/src/cryptonote_basic/fwd.h
+++ b/src/cryptonote_basic/fwd.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2020-2023, The Monero Project
+// Copyright (c) 2020-2024, The Monero Project
 
 //
 // All rights reserved.
diff --git a/src/cryptonote_basic/hardfork.cpp b/src/cryptonote_basic/hardfork.cpp
index aa10e4709..a779f859c 100644
--- a/src/cryptonote_basic/hardfork.cpp
+++ b/src/cryptonote_basic/hardfork.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/cryptonote_basic/hardfork.h b/src/cryptonote_basic/hardfork.h
index 9b2fef0e1..a9e5a63ab 100644
--- a/src/cryptonote_basic/hardfork.h
+++ b/src/cryptonote_basic/hardfork.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/cryptonote_basic/merge_mining.cpp b/src/cryptonote_basic/merge_mining.cpp
index 3e26c00e3..56d7a23b2 100644
--- a/src/cryptonote_basic/merge_mining.cpp
+++ b/src/cryptonote_basic/merge_mining.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020-2023, The Monero Project
+// Copyright (c) 2020-2024, The Monero Project
 
 // 
 // All rights reserved.
diff --git a/src/cryptonote_basic/merge_mining.h b/src/cryptonote_basic/merge_mining.h
index ef4c92d67..455b5b6d5 100644
--- a/src/cryptonote_basic/merge_mining.h
+++ b/src/cryptonote_basic/merge_mining.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2020-2023, The Monero Project
+// Copyright (c) 2020-2024, The Monero Project
 
 // 
 // All rights reserved.
diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp
index 4b0e43213..258e782a2 100644
--- a/src/cryptonote_basic/miner.cpp
+++ b/src/cryptonote_basic/miner.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
@@ -171,9 +171,10 @@ namespace cryptonote
       extra_nonce = m_extra_messages[m_config.current_extra_message_index];
     }
 
+    uint64_t cumulative_weight;
     uint64_t seed_height;
     crypto::hash seed_hash;
-    if(!m_phandler->get_block_template(bl, m_mine_address, di, height, expected_reward, extra_nonce, seed_height, seed_hash))
+    if(!m_phandler->get_block_template(bl, m_mine_address, di, height, expected_reward, cumulative_weight, extra_nonce, seed_height, seed_hash))
     {
       LOG_ERROR("Failed to get_block_template(), stopping mining");
       return false;
diff --git a/src/cryptonote_basic/miner.h b/src/cryptonote_basic/miner.h
index 35b988bd7..0a61606b3 100644
--- a/src/cryptonote_basic/miner.h
+++ b/src/cryptonote_basic/miner.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
@@ -47,7 +47,7 @@ namespace cryptonote
   struct i_miner_handler
   {
     virtual bool handle_block_found(block& b, block_verification_context &bvc) = 0;
-    virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash) = 0;
+    virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, uint64_t &cumulative_weight, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash) = 0;
   protected:
     ~i_miner_handler(){};
   };
diff --git a/src/cryptonote_basic/subaddress_index.h b/src/cryptonote_basic/subaddress_index.h
index 788040ae4..fdadf614a 100644
--- a/src/cryptonote_basic/subaddress_index.h
+++ b/src/cryptonote_basic/subaddress_index.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/cryptonote_basic/tx_extra.h b/src/cryptonote_basic/tx_extra.h
index cbf942d4c..74c319ec3 100644
--- a/src/cryptonote_basic/tx_extra.h
+++ b/src/cryptonote_basic/tx_extra.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/cryptonote_basic/verification_context.h b/src/cryptonote_basic/verification_context.h
index 8dcd55187..9dcacd7da 100644
--- a/src/cryptonote_basic/verification_context.h
+++ b/src/cryptonote_basic/verification_context.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h
index fec905928..88c8e0c20 100644
--- a/src/cryptonote_config.h
+++ b/src/cryptonote_config.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
@@ -241,6 +241,8 @@ namespace config
   const unsigned char HASH_KEY_ENCRYPTED_PAYMENT_ID = 0x8d;
   const unsigned char HASH_KEY_WALLET = 0x8c;
   const unsigned char HASH_KEY_WALLET_CACHE = 0x8d;
+  const unsigned char HASH_KEY_BACKGROUND_CACHE = 0x8e;
+  const unsigned char HASH_KEY_BACKGROUND_KEYS_FILE = 0x8f;
   const unsigned char HASH_KEY_RPC_PAYMENT_NONCE = 0x58;
   const unsigned char HASH_KEY_MEMORY = 'k';
   const unsigned char HASH_KEY_MULTISIG[] = {'M', 'u', 'l', 't' , 'i', 's', 'i', 'g', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
diff --git a/src/cryptonote_core/CMakeLists.txt b/src/cryptonote_core/CMakeLists.txt
index 373732b3b..50bc11de0 100644
--- a/src/cryptonote_core/CMakeLists.txt
+++ b/src/cryptonote_core/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index 3a7575119..13c470172 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
@@ -1544,7 +1544,7 @@ uint64_t Blockchain::get_current_cumulative_block_weight_median() const
 // in a lot of places.  That flag is not referenced in any of the code
 // nor any of the makefiles, howeve.  Need to look into whether or not it's
 // necessary at all.
-bool Blockchain::create_block_template(block& b, const crypto::hash *from_block, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash)
+bool Blockchain::create_block_template(block& b, const crypto::hash *from_block, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, uint64_t& cumulative_weight, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash)
 {
   LOG_PRINT_L3("Blockchain::" << __func__);
   size_t median_weight;
@@ -1571,6 +1571,7 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
       diffic = m_btc_difficulty;
       height = m_btc_height;
       expected_reward = m_btc_expected_reward;
+      cumulative_weight = m_btc_cumulative_weight;
       seed_height = m_btc_seed_height;
       seed_hash = m_btc_seed_hash;
       return true;
@@ -1754,7 +1755,7 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
   size_t max_outs = hf_version >= 4 ? 1 : 11;
   bool r = construct_miner_tx(height, median_weight, already_generated_coins, txs_weight, fee, miner_address, b.miner_tx, ex_nonce, max_outs, hf_version);
   CHECK_AND_ASSERT_MES(r, false, "Failed to construct miner tx, first chance");
-  size_t cumulative_weight = txs_weight + get_transaction_weight(b.miner_tx);
+  cumulative_weight = txs_weight + get_transaction_weight(b.miner_tx);
 #if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
   MDEBUG("Creating block template: miner tx weight " << get_transaction_weight(b.miner_tx) <<
       ", cumulative weight " << cumulative_weight);
@@ -1806,16 +1807,16 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
 #endif
 
     if (!from_block)
-      cache_block_template(b, miner_address, ex_nonce, diffic, height, expected_reward, seed_height, seed_hash, pool_cookie);
+      cache_block_template(b, miner_address, ex_nonce, diffic, height, expected_reward, cumulative_weight, seed_height, seed_hash, pool_cookie);
     return true;
   }
   LOG_ERROR("Failed to create_block_template with " << 10 << " tries");
   return false;
 }
 //------------------------------------------------------------------
-bool Blockchain::create_block_template(block& b, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash)
+bool Blockchain::create_block_template(block& b, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, uint64_t& cumulative_weight, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash)
 {
-  return create_block_template(b, NULL, miner_address, diffic, height, expected_reward, ex_nonce, seed_height, seed_hash);
+  return create_block_template(b, NULL, miner_address, diffic, height, expected_reward, cumulative_weight, ex_nonce, seed_height, seed_hash);
 }
 //------------------------------------------------------------------
 bool Blockchain::get_miner_data(uint8_t& major_version, uint64_t& height, crypto::hash& prev_id, crypto::hash& seed_hash, difficulty_type& difficulty, uint64_t& median_weight, uint64_t& already_generated_coins, std::vector<tx_block_template_backlog_entry>& tx_backlog)
@@ -2765,7 +2766,7 @@ bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qbloc
 // find split point between ours and foreign blockchain (or start at
 // blockchain height <req_start_block>), and return up to max_count FULL
 // blocks by reference.
-bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata> > > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, size_t max_block_count, size_t max_tx_count) const
+bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata> > > >& blocks, uint64_t& total_height, crypto::hash& top_hash, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, size_t max_block_count, size_t max_tx_count) const
 {
   LOG_PRINT_L3("Blockchain::" << __func__);
   CRITICAL_REGION_LOCAL(m_blockchain_lock);
@@ -2774,7 +2775,9 @@ bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, cons
   if(req_start_block > 0)
   {
     // if requested height is higher than our chain, return false -- we can't help
-    if (req_start_block >= m_db->height())
+    top_hash = m_db->top_block_hash(&total_height);
+    ++total_height;
+    if (req_start_block >= total_height)
     {
       return false;
     }
@@ -2789,7 +2792,8 @@ bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, cons
   }
 
   db_rtxn_guard rtxn_guard(m_db);
-  total_height = get_current_blockchain_height();
+  top_hash = m_db->top_block_hash(&total_height);
+  ++total_height;
   blocks.reserve(std::min(std::min(max_block_count, (size_t)10000), (size_t)(total_height - start_height)));
   CHECK_AND_ASSERT_MES(m_db->get_blocks_from(start_height, 3, max_block_count, max_tx_count, FIND_BLOCKCHAIN_SUPPLEMENT_MAX_SIZE, blocks, pruned, true, get_miner_tx_hash),
       false, "Error getting blocks");
@@ -5462,7 +5466,7 @@ void Blockchain::cancel()
 }
 
 #if defined(PER_BLOCK_CHECKPOINT)
-static const char expected_block_hashes_hash[] = "0046a0019beb6e697e27d834d6127851425f7ee09bfb8e9f8df7b1420131aca8";
+static const char expected_block_hashes_hash[] = "8ada865350270fd008397684d978dac75ea4029a8a1ffcaa9975c43be119ec19";
 void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get_checkpoints)
 {
   if (get_checkpoints == nullptr || !m_fast_sync)
@@ -5604,7 +5608,7 @@ void Blockchain::invalidate_block_template_cache()
   m_btc_valid = false;
 }
 
-void Blockchain::cache_block_template(const block &b, const cryptonote::account_public_address &address, const blobdata &nonce, const difficulty_type &diff, uint64_t height, uint64_t expected_reward, uint64_t seed_height, const crypto::hash &seed_hash, uint64_t pool_cookie)
+void Blockchain::cache_block_template(const block &b, const cryptonote::account_public_address &address, const blobdata &nonce, const difficulty_type &diff, uint64_t height, uint64_t expected_reward, uint64_t cumulative_weight, uint64_t seed_height, const crypto::hash &seed_hash, uint64_t pool_cookie)
 {
   MDEBUG("Setting block template cache");
   m_btc = b;
@@ -5613,6 +5617,7 @@ void Blockchain::cache_block_template(const block &b, const cryptonote::account_
   m_btc_difficulty = diff;
   m_btc_height = height;
   m_btc_expected_reward = expected_reward;
+  m_btc_cumulative_weight = cumulative_weight;
   m_btc_seed_hash = seed_hash;
   m_btc_seed_height = seed_height;
   m_btc_pool_cookie = pool_cookie;
diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h
index 2249d8cb2..2caad16a5 100644
--- a/src/cryptonote_core/blockchain.h
+++ b/src/cryptonote_core/blockchain.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
@@ -371,8 +371,8 @@ namespace cryptonote
      *
      * @return true if block template filled in successfully, else false
      */
-    bool create_block_template(block& b, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash);
-    bool create_block_template(block& b, const crypto::hash *from_block, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash);
+    bool create_block_template(block& b, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, uint64_t& expected_reward, uint64_t& cumulative_weight, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash);
+    bool create_block_template(block& b, const crypto::hash *from_block, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, uint64_t& expected_reward, uint64_t& cumulative_weight, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash);
 
     /**
      * @brief gets data required to create a block template and start mining on it
@@ -485,6 +485,7 @@ namespace cryptonote
      * @param qblock_ids the foreign chain's "short history" (see get_short_chain_history)
      * @param blocks return-by-reference the blocks and their transactions
      * @param total_height return-by-reference our current blockchain height
+     * @param top_hash return-by-reference top block hash
      * @param start_height return-by-reference the height of the first block returned
      * @param pruned whether to return full or pruned tx blobs
      * @param max_block_count the max number of blocks to get
@@ -492,7 +493,7 @@ namespace cryptonote
      *
      * @return true if a block found in common or req_start_block specified, else false
      */
-    bool find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata> > > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, size_t max_block_count, size_t max_tx_count) const;
+    bool find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata> > > >& blocks, uint64_t& total_height, crypto::hash& top_hash, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, size_t max_block_count, size_t max_tx_count) const;
 
     /**
      * @brief retrieves a set of blocks and their transactions, and possibly other transactions
@@ -1201,6 +1202,7 @@ namespace cryptonote
     uint64_t m_btc_height;
     uint64_t m_btc_pool_cookie;
     uint64_t m_btc_expected_reward;
+    uint64_t m_btc_cumulative_weight;
     crypto::hash m_btc_seed_hash;
     uint64_t m_btc_seed_height;
     bool m_btc_valid;
@@ -1593,7 +1595,7 @@ namespace cryptonote
      *
      * At some point, may be used to push an update to miners
      */
-    void cache_block_template(const block &b, const cryptonote::account_public_address &address, const blobdata &nonce, const difficulty_type &diff, uint64_t height, uint64_t expected_reward, uint64_t seed_height, const crypto::hash &seed_hash, uint64_t pool_cookie);
+    void cache_block_template(const block &b, const cryptonote::account_public_address &address, const blobdata &nonce, const difficulty_type &diff, uint64_t height, uint64_t expected_reward, uint64_t cumulative_weight, uint64_t seed_height, const crypto::hash &seed_hash, uint64_t pool_cookie);
 
     /**
      * @brief sends new block notifications to ZMQ `miner_data` subscribers
diff --git a/src/cryptonote_core/blockchain_and_pool.h b/src/cryptonote_core/blockchain_and_pool.h
index 497342aea..6d96effa9 100644
--- a/src/cryptonote_core/blockchain_and_pool.h
+++ b/src/cryptonote_core/blockchain_and_pool.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2023, The Monero Project
+// Copyright (c) 2023-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/cryptonote_core/blockchain_storage_boost_serialization.h b/src/cryptonote_core/blockchain_storage_boost_serialization.h
index 3c6f87886..7a80aef84 100644
--- a/src/cryptonote_core/blockchain_storage_boost_serialization.h
+++ b/src/cryptonote_core/blockchain_storage_boost_serialization.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index a5a59c892..954dc81e4 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
@@ -1468,14 +1468,14 @@ namespace cryptonote
       notify_txpool_event(tx_blobs, epee::to_span(tx_hashes), epee::to_span(txs), just_broadcasted);
   }
   //-----------------------------------------------------------------------------------------------
-  bool core::get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash)
+  bool core::get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, uint64_t& cumulative_weight, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash)
   {
-    return m_blockchain_storage.create_block_template(b, adr, diffic, height, expected_reward, ex_nonce, seed_height, seed_hash);
+    return m_blockchain_storage.create_block_template(b, adr, diffic, height, expected_reward, cumulative_weight, ex_nonce, seed_height, seed_hash);
   }
   //-----------------------------------------------------------------------------------------------
-  bool core::get_block_template(block& b, const crypto::hash *prev_block, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash)
+  bool core::get_block_template(block& b, const crypto::hash *prev_block, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, uint64_t& cumulative_weight, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash)
   {
-    return m_blockchain_storage.create_block_template(b, prev_block, adr, diffic, height, expected_reward, ex_nonce, seed_height, seed_hash);
+    return m_blockchain_storage.create_block_template(b, prev_block, adr, diffic, height, expected_reward, cumulative_weight, ex_nonce, seed_height, seed_hash);
   }
   //-----------------------------------------------------------------------------------------------
   bool core::get_miner_data(uint8_t& major_version, uint64_t& height, crypto::hash& prev_id, crypto::hash& seed_hash, difficulty_type& difficulty, uint64_t& median_weight, uint64_t& already_generated_coins, std::vector<tx_block_template_backlog_entry>& tx_backlog)
@@ -1488,9 +1488,9 @@ namespace cryptonote
     return m_blockchain_storage.find_blockchain_supplement(qblock_ids, clip_pruned, resp);
   }
   //-----------------------------------------------------------------------------------------------
-  bool core::find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata> > > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, size_t max_block_count, size_t max_tx_count) const
+  bool core::find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata> > > >& blocks, uint64_t& total_height, crypto::hash& top_hash, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, size_t max_block_count, size_t max_tx_count) const
   {
-    return m_blockchain_storage.find_blockchain_supplement(req_start_block, qblock_ids, blocks, total_height, start_height, pruned, get_miner_tx_hash, max_block_count, max_tx_count);
+    return m_blockchain_storage.find_blockchain_supplement(req_start_block, qblock_ids, blocks, total_height, top_hash, start_height, pruned, get_miner_tx_hash, max_block_count, max_tx_count);
   }
   //-----------------------------------------------------------------------------------------------
   bool core::get_outs(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMAND_RPC_GET_OUTPUTS_BIN::response& res) const
diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h
index 8108dfae0..2ecb88690 100644
--- a/src/cryptonote_core/cryptonote_core.h
+++ b/src/cryptonote_core/cryptonote_core.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
@@ -231,8 +231,8 @@ namespace cryptonote
       *
       * @note see Blockchain::create_block_template
       */
-     virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash) override;
-     virtual bool get_block_template(block& b, const crypto::hash *prev_block, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash);
+     virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, uint64_t &cumulative_weight, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash) override;
+     virtual bool get_block_template(block& b, const crypto::hash *prev_block, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, uint64_t &cumulative_weight, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash);
 
      /**
       * @copydoc Blockchain::get_miner_data
@@ -597,7 +597,7 @@ namespace cryptonote
       *
       * @note see Blockchain::find_blockchain_supplement(const uint64_t, const std::list<crypto::hash>&, std::vector<std::pair<cryptonote::blobdata, std::vector<transaction> > >&, uint64_t&, uint64_t&, size_t) const
       */
-     bool find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata> > > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, size_t max_block_count, size_t max_tx_count) const;
+     bool find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata> > > >& blocks, uint64_t& total_height, crypto::hash& top_hash, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, size_t max_block_count, size_t max_tx_count) const;
 
      /**
       * @copydoc Blockchain::get_tx_outputs_gindexs
diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp
index d5bb8fee7..ed5b285e8 100644
--- a/src/cryptonote_core/cryptonote_tx_utils.cpp
+++ b/src/cryptonote_core/cryptonote_tx_utils.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/cryptonote_core/cryptonote_tx_utils.h b/src/cryptonote_core/cryptonote_tx_utils.h
index 8f1073766..d3b8bfe2f 100644
--- a/src/cryptonote_core/cryptonote_tx_utils.h
+++ b/src/cryptonote_core/cryptonote_tx_utils.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/cryptonote_core/i_core_events.h b/src/cryptonote_core/i_core_events.h
index cbebd9efb..41e27b254 100644
--- a/src/cryptonote_core/i_core_events.h
+++ b/src/cryptonote_core/i_core_events.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2023, The Monero Project
+// Copyright (c) 2019-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp
index 96b229177..2d01b2bb2 100644
--- a/src/cryptonote_core/tx_pool.cpp
+++ b/src/cryptonote_core/tx_pool.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h
index 3a111ce91..69a123fc9 100644
--- a/src/cryptonote_core/tx_pool.h
+++ b/src/cryptonote_core/tx_pool.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/cryptonote_core/tx_sanity_check.cpp b/src/cryptonote_core/tx_sanity_check.cpp
index 99d5717cb..6925a2dfc 100644
--- a/src/cryptonote_core/tx_sanity_check.cpp
+++ b/src/cryptonote_core/tx_sanity_check.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2023, The Monero Project
+// Copyright (c) 2019-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/cryptonote_core/tx_sanity_check.h b/src/cryptonote_core/tx_sanity_check.h
index 4d7bf741c..1c883393b 100644
--- a/src/cryptonote_core/tx_sanity_check.h
+++ b/src/cryptonote_core/tx_sanity_check.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2023, The Monero Project
+// Copyright (c) 2019-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/cryptonote_core/tx_verification_utils.cpp b/src/cryptonote_core/tx_verification_utils.cpp
index a93ef2f25..aaf58339d 100644
--- a/src/cryptonote_core/tx_verification_utils.cpp
+++ b/src/cryptonote_core/tx_verification_utils.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2023, The Monero Project
+// Copyright (c) 2023-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/cryptonote_core/tx_verification_utils.h b/src/cryptonote_core/tx_verification_utils.h
index ccd401d2a..962a027d3 100644
--- a/src/cryptonote_core/tx_verification_utils.h
+++ b/src/cryptonote_core/tx_verification_utils.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2023, The Monero Project
+// Copyright (c) 2023-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/cryptonote_protocol/CMakeLists.txt b/src/cryptonote_protocol/CMakeLists.txt
index c52eaa802..99a5de432 100644
--- a/src/cryptonote_protocol/CMakeLists.txt
+++ b/src/cryptonote_protocol/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/src/cryptonote_protocol/block_queue.cpp b/src/cryptonote_protocol/block_queue.cpp
index ba22157b5..9f4a93723 100644
--- a/src/cryptonote_protocol/block_queue.cpp
+++ b/src/cryptonote_protocol/block_queue.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/cryptonote_protocol/block_queue.h b/src/cryptonote_protocol/block_queue.h
index 311275aa9..f897b53e3 100644
--- a/src/cryptonote_protocol/block_queue.h
+++ b/src/cryptonote_protocol/block_queue.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/cryptonote_protocol/cryptonote_protocol_defs.h b/src/cryptonote_protocol/cryptonote_protocol_defs.h
index 23d9144a5..b34276f48 100644
--- a/src/cryptonote_protocol/cryptonote_protocol_defs.h
+++ b/src/cryptonote_protocol/cryptonote_protocol_defs.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler-base.cpp b/src/cryptonote_protocol/cryptonote_protocol_handler-base.cpp
index 90f3c111a..1f37ca820 100644
--- a/src/cryptonote_protocol/cryptonote_protocol_handler-base.cpp
+++ b/src/cryptonote_protocol/cryptonote_protocol_handler-base.cpp
@@ -2,7 +2,7 @@
 /// @author rfree (current maintainer in monero.cc project)
 /// @brief This is the place to implement our handlers for protocol network actions, e.g. for ratelimit for download-requests
 
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.h b/src/cryptonote_protocol/cryptonote_protocol_handler.h
index 80582fad2..d7fe40d22 100644
--- a/src/cryptonote_protocol/cryptonote_protocol_handler.h
+++ b/src/cryptonote_protocol/cryptonote_protocol_handler.h
@@ -2,7 +2,7 @@
 /// @author rfree (current maintainer/user in monero.cc project - most of code is from CryptoNote)
 /// @brief This is the original cryptonote protocol network-events handler, modified by us
 
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
index 385c3e5c3..b4a2fda4f 100644
--- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl
+++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
@@ -2,7 +2,7 @@
 /// @author rfree (current maintainer/user in monero.cc project - most of code is from CryptoNote)
 /// @brief This is the original cryptonote protocol network-events handler, modified by us
 
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler_common.h b/src/cryptonote_protocol/cryptonote_protocol_handler_common.h
index 688ff2f37..3fb26eb34 100644
--- a/src/cryptonote_protocol/cryptonote_protocol_handler_common.h
+++ b/src/cryptonote_protocol/cryptonote_protocol_handler_common.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/cryptonote_protocol/enums.h b/src/cryptonote_protocol/enums.h
index 19cf8078c..6a015a1ee 100644
--- a/src/cryptonote_protocol/enums.h
+++ b/src/cryptonote_protocol/enums.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2023, The Monero Project
+// Copyright (c) 2019-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/cryptonote_protocol/fwd.h b/src/cryptonote_protocol/fwd.h
index 7182d9ef4..900417f91 100644
--- a/src/cryptonote_protocol/fwd.h
+++ b/src/cryptonote_protocol/fwd.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2023, The Monero Project
+// Copyright (c) 2019-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/cryptonote_protocol/levin_notify.cpp b/src/cryptonote_protocol/levin_notify.cpp
index 1c3a2901c..631a6c305 100644
--- a/src/cryptonote_protocol/levin_notify.cpp
+++ b/src/cryptonote_protocol/levin_notify.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2023, The Monero Project
+// Copyright (c) 2019-2024, The Monero Project
 //
 // All rights reserved.
 //
@@ -396,6 +396,8 @@ namespace levin
         for (auto& connection : connections)
         {
           std::sort(connection.first.begin(), connection.first.end()); // don't leak receive order
+          connection.first.erase(std::unique(connection.first.begin(), connection.first.end()),
+                                  connection.first.end());
           make_payload_send_txs(*zone_->p2p, std::move(connection.first), connection.second, zone_->pad_txs, true);
         }
 
diff --git a/src/cryptonote_protocol/levin_notify.h b/src/cryptonote_protocol/levin_notify.h
index 816170841..20a955689 100644
--- a/src/cryptonote_protocol/levin_notify.h
+++ b/src/cryptonote_protocol/levin_notify.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2023, The Monero Project
+// Copyright (c) 2019-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/daemon/CMakeLists.txt b/src/daemon/CMakeLists.txt
index 8a8d1d9a7..3e737ad46 100644
--- a/src/daemon/CMakeLists.txt
+++ b/src/daemon/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/src/daemon/command_line_args.h b/src/daemon/command_line_args.h
index 44505eb8f..2b8b28186 100644
--- a/src/daemon/command_line_args.h
+++ b/src/daemon/command_line_args.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp
index 25b15f3b1..a49c8a1ca 100644
--- a/src/daemon/command_parser_executor.cpp
+++ b/src/daemon/command_parser_executor.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/daemon/command_parser_executor.h b/src/daemon/command_parser_executor.h
index 7224e36f6..741fafdc4 100644
--- a/src/daemon/command_parser_executor.h
+++ b/src/daemon/command_parser_executor.h
@@ -6,7 +6,7 @@
 
 */
 
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/daemon/command_server.cpp b/src/daemon/command_server.cpp
index 6baaa7015..8170fa36b 100644
--- a/src/daemon/command_server.cpp
+++ b/src/daemon/command_server.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/daemon/command_server.h b/src/daemon/command_server.h
index 8e3972beb..ece0adf81 100644
--- a/src/daemon/command_server.h
+++ b/src/daemon/command_server.h
@@ -9,7 +9,7 @@ Passing RPC commands:
 
 */
 
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/daemon/core.h b/src/daemon/core.h
index c3a3116b1..1d6bec5ef 100644
--- a/src/daemon/core.h
+++ b/src/daemon/core.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp
index 1d4142c84..9a157711c 100644
--- a/src/daemon/daemon.cpp
+++ b/src/daemon/daemon.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 //
diff --git a/src/daemon/daemon.h b/src/daemon/daemon.h
index d652d9a4b..29a2dbeed 100644
--- a/src/daemon/daemon.h
+++ b/src/daemon/daemon.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/daemon/executor.cpp b/src/daemon/executor.cpp
index 14b0f86a3..2d5ee5354 100644
--- a/src/daemon/executor.cpp
+++ b/src/daemon/executor.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/daemon/executor.h b/src/daemon/executor.h
index 92fc5ca46..844c4c20e 100644
--- a/src/daemon/executor.h
+++ b/src/daemon/executor.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/daemon/main.cpp b/src/daemon/main.cpp
index e2ff27daa..4496f6d6e 100644
--- a/src/daemon/main.cpp
+++ b/src/daemon/main.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/daemon/p2p.h b/src/daemon/p2p.h
index 40d7ab695..b9036550c 100644
--- a/src/daemon/p2p.h
+++ b/src/daemon/p2p.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/daemon/protocol.h b/src/daemon/protocol.h
index 6e960168a..a1ed174ed 100644
--- a/src/daemon/protocol.h
+++ b/src/daemon/protocol.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/daemon/rpc.h b/src/daemon/rpc.h
index f7bdb921e..bacc10b21 100644
--- a/src/daemon/rpc.h
+++ b/src/daemon/rpc.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp
index fbf26db85..6529aaf3a 100644
--- a/src/daemon/rpc_command_executor.cpp
+++ b/src/daemon/rpc_command_executor.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/daemon/rpc_command_executor.h b/src/daemon/rpc_command_executor.h
index 3d4582422..f94c1415a 100644
--- a/src/daemon/rpc_command_executor.h
+++ b/src/daemon/rpc_command_executor.h
@@ -6,7 +6,7 @@
 
 */
 
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/daemonizer/CMakeLists.txt b/src/daemonizer/CMakeLists.txt
index 5be9fda42..9175d28c2 100644
--- a/src/daemonizer/CMakeLists.txt
+++ b/src/daemonizer/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/src/daemonizer/daemonizer.h b/src/daemonizer/daemonizer.h
index 8c76c0004..44833eb0f 100644
--- a/src/daemonizer/daemonizer.h
+++ b/src/daemonizer/daemonizer.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/daemonizer/posix_daemonizer.inl b/src/daemonizer/posix_daemonizer.inl
index 82b87cc97..d6e132c58 100644
--- a/src/daemonizer/posix_daemonizer.inl
+++ b/src/daemonizer/posix_daemonizer.inl
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/daemonizer/posix_fork.h b/src/daemonizer/posix_fork.h
index 497643699..0b4f34bb9 100644
--- a/src/daemonizer/posix_fork.h
+++ b/src/daemonizer/posix_fork.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/daemonizer/windows_daemonizer.inl b/src/daemonizer/windows_daemonizer.inl
index dbe8e4a02..0bdc3dda5 100644
--- a/src/daemonizer/windows_daemonizer.inl
+++ b/src/daemonizer/windows_daemonizer.inl
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/daemonizer/windows_service.cpp b/src/daemonizer/windows_service.cpp
index 04ea62d2b..335d01814 100644
--- a/src/daemonizer/windows_service.cpp
+++ b/src/daemonizer/windows_service.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/daemonizer/windows_service.h b/src/daemonizer/windows_service.h
index 55b097bf9..6df0f7ee9 100644
--- a/src/daemonizer/windows_service.h
+++ b/src/daemonizer/windows_service.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/daemonizer/windows_service_runner.h b/src/daemonizer/windows_service_runner.h
index 5aa1a8c2f..ce461486f 100644
--- a/src/daemonizer/windows_service_runner.h
+++ b/src/daemonizer/windows_service_runner.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/debug_utilities/CMakeLists.txt b/src/debug_utilities/CMakeLists.txt
index 58cb713df..2f6efaa3e 100644
--- a/src/debug_utilities/CMakeLists.txt
+++ b/src/debug_utilities/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/src/debug_utilities/cn_deserialize.cpp b/src/debug_utilities/cn_deserialize.cpp
index aa9ceec0e..9525bf88c 100644
--- a/src/debug_utilities/cn_deserialize.cpp
+++ b/src/debug_utilities/cn_deserialize.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/debug_utilities/dns_checks.cpp b/src/debug_utilities/dns_checks.cpp
index a59a08209..a7d172ebc 100644
--- a/src/debug_utilities/dns_checks.cpp
+++ b/src/debug_utilities/dns_checks.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2023, The Monero Project
+// Copyright (c) 2019-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/debug_utilities/object_sizes.cpp b/src/debug_utilities/object_sizes.cpp
index b6f9a3fbf..7e94ed4c2 100644
--- a/src/debug_utilities/object_sizes.cpp
+++ b/src/debug_utilities/object_sizes.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/device/CMakeLists.txt b/src/device/CMakeLists.txt
index eef49ffee..afbcebf47 100644
--- a/src/device/CMakeLists.txt
+++ b/src/device/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/src/device/device.cpp b/src/device/device.cpp
index 3d3a14fd5..e6c39c0b4 100644
--- a/src/device/device.cpp
+++ b/src/device/device.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/device/device.hpp b/src/device/device.hpp
index 1c29018e6..f91ae1ddf 100644
--- a/src/device/device.hpp
+++ b/src/device/device.hpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/device/device_cold.hpp b/src/device/device_cold.hpp
index 0738598b8..f6a55f810 100644
--- a/src/device/device_cold.hpp
+++ b/src/device/device_cold.hpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/device/device_default.cpp b/src/device/device_default.cpp
index d938087fe..5bfdc2a87 100644
--- a/src/device/device_default.cpp
+++ b/src/device/device_default.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/device/device_default.hpp b/src/device/device_default.hpp
index 7ee9f15b7..9c8dbb099 100644
--- a/src/device/device_default.hpp
+++ b/src/device/device_default.hpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/device/device_io.hpp b/src/device/device_io.hpp
index 20678568d..c24fa29fd 100644
--- a/src/device/device_io.hpp
+++ b/src/device/device_io.hpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/device/device_io_hid.cpp b/src/device/device_io_hid.cpp
index 472414d3b..db3a44561 100644
--- a/src/device/device_io_hid.cpp
+++ b/src/device/device_io_hid.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/device/device_io_hid.hpp b/src/device/device_io_hid.hpp
index 90d7d8553..5308fd263 100644
--- a/src/device/device_io_hid.hpp
+++ b/src/device/device_io_hid.hpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp
index 009ee16ca..bf5f61423 100644
--- a/src/device/device_ledger.cpp
+++ b/src/device/device_ledger.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
@@ -528,6 +528,7 @@ namespace hw {
         {0x2c97, 0x0004, 0, 0xffa0},       
         {0x2c97, 0x0005, 0, 0xffa0},
         {0x2c97, 0x0006, 0, 0xffa0},
+        {0x2c97, 0x0007, 0, 0xffa0},
     };
 
     bool device_ledger::connect(void) {
diff --git a/src/device/device_ledger.hpp b/src/device/device_ledger.hpp
index b65943f0c..9179e7933 100644
--- a/src/device/device_ledger.hpp
+++ b/src/device/device_ledger.hpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/device/log.cpp b/src/device/log.cpp
index 1a243c831..3995eac3f 100644
--- a/src/device/log.cpp
+++ b/src/device/log.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/device/log.hpp b/src/device/log.hpp
index 92d174da1..d8276e577 100644
--- a/src/device/log.hpp
+++ b/src/device/log.hpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
@@ -48,7 +48,7 @@ namespace hw {
      *     - All computation done by device are checked by default device.
      *       Required IODUMMYCRYPT_HWDEVICE or IONOCRYPT_HWDEVICE for fully working
      * #define IODUMMYCRYPT_HWDEVICE 1
-     *     - It assumes sensitive data encryption is is off on device side. a XOR with 0x55. This allow Ledger Class to make check on clear value
+     *     - It assumes sensitive data encryption is off on device side. a XOR with 0x55. This allow Ledger Class to make check on clear value
      * #define IONOCRYPT_HWDEVICE 1
      *     - It assumes sensitive data encryption is off on device side.
      */
diff --git a/src/device_trezor/CMakeLists.txt b/src/device_trezor/CMakeLists.txt
index b3107b2d1..8da075aec 100644
--- a/src/device_trezor/CMakeLists.txt
+++ b/src/device_trezor/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/src/device_trezor/device_trezor.cpp b/src/device_trezor/device_trezor.cpp
index fa1e7c088..03985b84a 100644
--- a/src/device_trezor/device_trezor.cpp
+++ b/src/device_trezor/device_trezor.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/device_trezor/device_trezor.hpp b/src/device_trezor/device_trezor.hpp
index 804ed62c8..aef7e5259 100644
--- a/src/device_trezor/device_trezor.hpp
+++ b/src/device_trezor/device_trezor.hpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/device_trezor/device_trezor_base.cpp b/src/device_trezor/device_trezor_base.cpp
index f65870be5..cbebb5ae4 100644
--- a/src/device_trezor/device_trezor_base.cpp
+++ b/src/device_trezor/device_trezor_base.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/device_trezor/device_trezor_base.hpp b/src/device_trezor/device_trezor_base.hpp
index e0fc2aafb..04431c706 100644
--- a/src/device_trezor/device_trezor_base.hpp
+++ b/src/device_trezor/device_trezor_base.hpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/device_trezor/trezor.hpp b/src/device_trezor/trezor.hpp
index d425b3209..f22696feb 100644
--- a/src/device_trezor/trezor.hpp
+++ b/src/device_trezor/trezor.hpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/device_trezor/trezor/debug_link.cpp b/src/device_trezor/trezor/debug_link.cpp
index 76adcb164..8d7f81e4b 100644
--- a/src/device_trezor/trezor/debug_link.cpp
+++ b/src/device_trezor/trezor/debug_link.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/device_trezor/trezor/debug_link.hpp b/src/device_trezor/trezor/debug_link.hpp
index 4249222dc..3665b65c7 100644
--- a/src/device_trezor/trezor/debug_link.hpp
+++ b/src/device_trezor/trezor/debug_link.hpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/device_trezor/trezor/exceptions.hpp b/src/device_trezor/trezor/exceptions.hpp
index b66386c47..717f44cfd 100644
--- a/src/device_trezor/trezor/exceptions.hpp
+++ b/src/device_trezor/trezor/exceptions.hpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/device_trezor/trezor/messages_map.cpp b/src/device_trezor/trezor/messages_map.cpp
index 527fe38ba..b2e7c42e3 100644
--- a/src/device_trezor/trezor/messages_map.cpp
+++ b/src/device_trezor/trezor/messages_map.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/device_trezor/trezor/messages_map.hpp b/src/device_trezor/trezor/messages_map.hpp
index f6820524e..448923a01 100644
--- a/src/device_trezor/trezor/messages_map.hpp
+++ b/src/device_trezor/trezor/messages_map.hpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/device_trezor/trezor/protocol.cpp b/src/device_trezor/trezor/protocol.cpp
index 49d3af0f5..98dde7d92 100644
--- a/src/device_trezor/trezor/protocol.cpp
+++ b/src/device_trezor/trezor/protocol.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/device_trezor/trezor/protocol.hpp b/src/device_trezor/trezor/protocol.hpp
index e9d84ace8..4e97a67c0 100644
--- a/src/device_trezor/trezor/protocol.hpp
+++ b/src/device_trezor/trezor/protocol.hpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/device_trezor/trezor/transport.cpp b/src/device_trezor/trezor/transport.cpp
index 23a04f9c7..3bade55ae 100644
--- a/src/device_trezor/trezor/transport.cpp
+++ b/src/device_trezor/trezor/transport.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/device_trezor/trezor/transport.hpp b/src/device_trezor/trezor/transport.hpp
index 3f280b104..a5066c463 100644
--- a/src/device_trezor/trezor/transport.hpp
+++ b/src/device_trezor/trezor/transport.hpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/device_trezor/trezor/trezor_defs.hpp b/src/device_trezor/trezor/trezor_defs.hpp
index 8e7f89031..18daf2d2c 100644
--- a/src/device_trezor/trezor/trezor_defs.hpp
+++ b/src/device_trezor/trezor/trezor_defs.hpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/gen_multisig/CMakeLists.txt b/src/gen_multisig/CMakeLists.txt
index e88d21902..455dc7dce 100644
--- a/src/gen_multisig/CMakeLists.txt
+++ b/src/gen_multisig/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2017-2023, The Monero Project
+# Copyright (c) 2017-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/src/gen_multisig/gen_multisig.cpp b/src/gen_multisig/gen_multisig.cpp
index fd82c66af..d17084333 100644
--- a/src/gen_multisig/gen_multisig.cpp
+++ b/src/gen_multisig/gen_multisig.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/gen_ssl_cert/CMakeLists.txt b/src/gen_ssl_cert/CMakeLists.txt
index e86518844..52cb99040 100644
--- a/src/gen_ssl_cert/CMakeLists.txt
+++ b/src/gen_ssl_cert/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2017-2023, The Monero Project
+# Copyright (c) 2017-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/src/gen_ssl_cert/gen_ssl_cert.cpp b/src/gen_ssl_cert/gen_ssl_cert.cpp
index b25d9a73d..8c5a29bc4 100644
--- a/src/gen_ssl_cert/gen_ssl_cert.cpp
+++ b/src/gen_ssl_cert/gen_ssl_cert.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2023, The Monero Project
+// Copyright (c) 2019-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/hardforks/CMakeLists.txt b/src/hardforks/CMakeLists.txt
index 20ea907c5..fd99e8d44 100644
--- a/src/hardforks/CMakeLists.txt
+++ b/src/hardforks/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/src/hardforks/hardforks.cpp b/src/hardforks/hardforks.cpp
index 3d1d53589..571ba3818 100644
--- a/src/hardforks/hardforks.cpp
+++ b/src/hardforks/hardforks.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/hardforks/hardforks.h b/src/hardforks/hardforks.h
index fbe0d630d..625880dc3 100644
--- a/src/hardforks/hardforks.h
+++ b/src/hardforks/hardforks.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/lmdb/CMakeLists.txt b/src/lmdb/CMakeLists.txt
index 50e08be73..1088a505d 100644
--- a/src/lmdb/CMakeLists.txt
+++ b/src/lmdb/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 # 
 # All rights reserved.
 # 
diff --git a/src/lmdb/database.cpp b/src/lmdb/database.cpp
index 22569fcf0..e1f2a56cb 100644
--- a/src/lmdb/database.cpp
+++ b/src/lmdb/database.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without modification, are
diff --git a/src/lmdb/database.h b/src/lmdb/database.h
index 0f12571da..0713476e5 100644
--- a/src/lmdb/database.h
+++ b/src/lmdb/database.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 // All rights reserved.
 //
diff --git a/src/lmdb/error.cpp b/src/lmdb/error.cpp
index 28659dddd..dff291fb4 100644
--- a/src/lmdb/error.cpp
+++ b/src/lmdb/error.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without modification, are
diff --git a/src/lmdb/error.h b/src/lmdb/error.h
index eaacc56e6..33fef89d9 100644
--- a/src/lmdb/error.h
+++ b/src/lmdb/error.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without modification, are
diff --git a/src/lmdb/key_stream.h b/src/lmdb/key_stream.h
index 1765c0e39..0ad4c553c 100644
--- a/src/lmdb/key_stream.h
+++ b/src/lmdb/key_stream.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 // All rights reserved.
 //
diff --git a/src/lmdb/table.cpp b/src/lmdb/table.cpp
index 239e80ef9..3fff79950 100644
--- a/src/lmdb/table.cpp
+++ b/src/lmdb/table.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 // All rights reserved.
 //
diff --git a/src/lmdb/transaction.h b/src/lmdb/transaction.h
index 4cb43aead..94d7916b0 100644
--- a/src/lmdb/transaction.h
+++ b/src/lmdb/transaction.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 // All rights reserved.
 //
diff --git a/src/lmdb/util.h b/src/lmdb/util.h
index 95057fe8d..64ad3752a 100644
--- a/src/lmdb/util.h
+++ b/src/lmdb/util.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 // All rights reserved.
 //
diff --git a/src/lmdb/value_stream.cpp b/src/lmdb/value_stream.cpp
index cccb74bc3..fad537829 100644
--- a/src/lmdb/value_stream.cpp
+++ b/src/lmdb/value_stream.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 // All rights reserved.
 //
diff --git a/src/lmdb/value_stream.h b/src/lmdb/value_stream.h
index 858a54f3e..d5a00ba02 100644
--- a/src/lmdb/value_stream.h
+++ b/src/lmdb/value_stream.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 // All rights reserved.
 //
diff --git a/src/mnemonics/CMakeLists.txt b/src/mnemonics/CMakeLists.txt
index 23e63dfa1..fd34bdd75 100644
--- a/src/mnemonics/CMakeLists.txt
+++ b/src/mnemonics/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/src/mnemonics/chinese_simplified.h b/src/mnemonics/chinese_simplified.h
index 79f211512..16a5b3e0d 100644
--- a/src/mnemonics/chinese_simplified.h
+++ b/src/mnemonics/chinese_simplified.h
@@ -21,7 +21,7 @@
 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
-// Code surrounding the word list is Copyright (c) 2014-2023, The Monero Project
+// Code surrounding the word list is Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/mnemonics/dutch.h b/src/mnemonics/dutch.h
index afa856f3b..89cde3918 100644
--- a/src/mnemonics/dutch.h
+++ b/src/mnemonics/dutch.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/mnemonics/electrum-words.cpp b/src/mnemonics/electrum-words.cpp
index 69816c43c..171dd750c 100644
--- a/src/mnemonics/electrum-words.cpp
+++ b/src/mnemonics/electrum-words.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/mnemonics/electrum-words.h b/src/mnemonics/electrum-words.h
index ab6a4a9f1..374ebef57 100644
--- a/src/mnemonics/electrum-words.h
+++ b/src/mnemonics/electrum-words.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/mnemonics/english.h b/src/mnemonics/english.h
index 3abb5d3b2..2ec9d7b1b 100644
--- a/src/mnemonics/english.h
+++ b/src/mnemonics/english.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/mnemonics/english_old.h b/src/mnemonics/english_old.h
index 71ed862cb..f0b0edd21 100644
--- a/src/mnemonics/english_old.h
+++ b/src/mnemonics/english_old.h
@@ -1,6 +1,6 @@
 // Word list originally created as part of the Electrum project, Copyright (C) 2014 Thomas Voegtlin
 // 
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/mnemonics/esperanto.h b/src/mnemonics/esperanto.h
index db8ccc99a..5d378a4db 100644
--- a/src/mnemonics/esperanto.h
+++ b/src/mnemonics/esperanto.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/mnemonics/french.h b/src/mnemonics/french.h
index e39bd5902..0bdf361df 100644
--- a/src/mnemonics/french.h
+++ b/src/mnemonics/french.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/mnemonics/german.h b/src/mnemonics/german.h
index 845bc729a..cb763c1e4 100644
--- a/src/mnemonics/german.h
+++ b/src/mnemonics/german.h
@@ -1,6 +1,6 @@
 // Word list created by Monero contributor Shrikez
 // 
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/mnemonics/italian.h b/src/mnemonics/italian.h
index e258811d3..d3077247f 100644
--- a/src/mnemonics/italian.h
+++ b/src/mnemonics/italian.h
@@ -1,6 +1,6 @@
 // Word list created by Monero contributor Shrikez
 // 
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/mnemonics/japanese.h b/src/mnemonics/japanese.h
index 65d81daf1..f1d730cc7 100644
--- a/src/mnemonics/japanese.h
+++ b/src/mnemonics/japanese.h
@@ -21,7 +21,7 @@
 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
-// Code surrounding the word list is Copyright (c) 2014-2023, The Monero Project
+// Code surrounding the word list is Copyright (c) 2014-2024, The Monero Project
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without modification, are
diff --git a/src/mnemonics/language_base.h b/src/mnemonics/language_base.h
index f6ed5bd57..a47b58d94 100644
--- a/src/mnemonics/language_base.h
+++ b/src/mnemonics/language_base.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/mnemonics/lojban.h b/src/mnemonics/lojban.h
index 9c127c0a0..4f804d393 100644
--- a/src/mnemonics/lojban.h
+++ b/src/mnemonics/lojban.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/mnemonics/portuguese.h b/src/mnemonics/portuguese.h
index f87464b27..b31e4d286 100644
--- a/src/mnemonics/portuguese.h
+++ b/src/mnemonics/portuguese.h
@@ -21,7 +21,7 @@
 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
-// Code surrounding the word list is Copyright (c) 2014-2023, The Monero Project
+// Code surrounding the word list is Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/mnemonics/russian.h b/src/mnemonics/russian.h
index 23b108daa..2f6771f56 100644
--- a/src/mnemonics/russian.h
+++ b/src/mnemonics/russian.h
@@ -1,6 +1,6 @@
 // Word list created by Monero contributor sammy007
 // 
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/mnemonics/singleton.h b/src/mnemonics/singleton.h
index 156db9e3d..a731dbc61 100644
--- a/src/mnemonics/singleton.h
+++ b/src/mnemonics/singleton.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/mnemonics/spanish.h b/src/mnemonics/spanish.h
index 312ac4adc..56bbf3671 100644
--- a/src/mnemonics/spanish.h
+++ b/src/mnemonics/spanish.h
@@ -21,7 +21,7 @@
 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
-// Code surrounding the word list is Copyright (c) 2014-2023, The Monero Project
+// Code surrounding the word list is Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/multisig/CMakeLists.txt b/src/multisig/CMakeLists.txt
index 62ae00e80..751909330 100644
--- a/src/multisig/CMakeLists.txt
+++ b/src/multisig/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2017-2023, The Monero Project
+# Copyright (c) 2017-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/src/multisig/multisig.cpp b/src/multisig/multisig.cpp
index 70baa7f19..9e57dd331 100644
--- a/src/multisig/multisig.cpp
+++ b/src/multisig/multisig.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/multisig/multisig.h b/src/multisig/multisig.h
index 2ec2f4e66..cca96ef6c 100644
--- a/src/multisig/multisig.h
+++ b/src/multisig/multisig.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/multisig/multisig_account.cpp b/src/multisig/multisig_account.cpp
index 4f9711b15..012fdc3a5 100644
--- a/src/multisig/multisig_account.cpp
+++ b/src/multisig/multisig_account.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2021-2023, The Monero Project
+// Copyright (c) 2021-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/multisig/multisig_account.h b/src/multisig/multisig_account.h
index 2ea8d0133..917f5c87a 100644
--- a/src/multisig/multisig_account.h
+++ b/src/multisig/multisig_account.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2021-2023, The Monero Project
+// Copyright (c) 2021-2024, The Monero Project
 // 
 // All rights reserved.
 // 
@@ -103,7 +103,8 @@ namespace multisig
     * 
     * - prepares a kex msg for the first round of multisig key construction.
     *    - the local account's kex msgs are signed with the base_privkey
-    *    - the first kex msg transmits the local base_common_privkey to other participants, for creating the group's common_privkey
+    *    - the first kex msg transmits the local base_common_privkey to other participants, for creating the group's
+    *      common_privkey
     */
     multisig_account(const crypto::secret_key &base_privkey,
       const crypto::secret_key &base_common_privkey);
@@ -190,24 +191,48 @@ namespace multisig
     *      - If force updating with maliciously-crafted messages, the resulting account will be invalid (either unable
     *        to complete signatures, or a 'hostage' to the malicious signer [i.e. can't sign without his participation]).
     */
-    void kex_update(const std::vector<multisig_kex_msg> &expanded_msgs,
-      const bool force_update_use_with_caution = false);
+    void kex_update(const std::vector<multisig_kex_msg> &expanded_msgs, const bool force_update_use_with_caution = false);
+    /**
+    * brief: get_multisig_kex_round_booster - Create a multisig kex msg for the kex round that follows the kex round this
+    *    account is currently working on, in order to 'boost' another participant's kex setup.
+    *    - A booster message is for the round after the in-progress round because get_next_kex_round_msg() provides access
+    *      to the in-progress round's message.
+    *    - Useful for 'jumpstarting' the following kex round when you don't have messages from all other signers to complete
+    *      the current round.
+    *    - Sanitizes input messages and produces a new kex msg for round 'num_completed_rounds + 2'.
+    *
+    *    - For example, in 2-of-3 escrowed purchasing, the [vendor, arbitrator] pair can boost the second round
+    *      of key exchange by calling this function with the 'round 1' messages of each other.
+    *      Then the [buyer] can use the resulting boost messages, in combination with [vender, arbitrator] round 1 messages,
+    *      to complete the address in one step. In other words, call initialize_kex() on the round 1 messages,
+    *      then call kex_update() on the round 2 booster messages to finish the multisig key.
+    * 
+    *    - Note: The 'threshold' and 'num_signers' are inputs here in case kex has not been initialized yet.
+    * param: threshold - threshold for multisig (M in M-of-N)
+    * param: num_signers - number of participants in multisig (N)
+    * param: expanded_msgs - set of multisig kex messages to process
+    * return: multisig kex message for next round
+    */
+    multisig_kex_msg get_multisig_kex_round_booster(const std::uint32_t threshold,
+      const std::uint32_t num_signers,
+      const std::vector<multisig_kex_msg> &expanded_msgs) const;
 
   private:
     // implementation of kex_update() (non-transactional)
     void kex_update_impl(const std::vector<multisig_kex_msg> &expanded_msgs, const bool incomplete_signer_set);
     /**
-    * brief: initialize_kex_update - Helper for kex_update_impl()
-    *    - Collect the local signer's shared keys to ignore in incoming messages, build the aggregate ancillary key
-    *      if appropriate.
+    * brief: get_kex_exclude_pubkeys - collect the local signer's shared keys to ignore in incoming messages
+    * return: keys held by the local account corresponding to the 'in-progress round'
+    *    - If 'in-progress round' is the final round, these are the local account's shares of the final aggregate key.
+    */
+    std::vector<crypto::public_key> get_kex_exclude_pubkeys() const;
+    /**
+    * brief: initialize_kex_update - initialize the multisig account for the first kex round
     * param: expanded_msgs - set of multisig kex messages to process
     * param: kex_rounds_required - number of rounds required for kex (not including post-kex verification round)
-    * outparam: exclude_pubkeys_out - keys held by the local account corresponding to round 'current_round'
-    *    - If 'current_round' is the final round, these are the local account's shares of the final aggregate key.
     */
     void initialize_kex_update(const std::vector<multisig_kex_msg> &expanded_msgs,
-      const std::uint32_t kex_rounds_required,
-      std::vector<crypto::public_key> &exclude_pubkeys_out);
+      const std::uint32_t kex_rounds_required);
     /**
     * brief: finalize_kex_update - Helper for kex_update_impl()
     * param: kex_rounds_required - number of rounds required for kex (not including post-kex verification round)
diff --git a/src/multisig/multisig_account_kex_impl.cpp b/src/multisig/multisig_account_kex_impl.cpp
index ef0acf307..5d2f362e5 100644
--- a/src/multisig/multisig_account_kex_impl.cpp
+++ b/src/multisig/multisig_account_kex_impl.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2021-2023, The Monero Project
+// Copyright (c) 2021-2024, The Monero Project
 //
 // All rights reserved.
 //
@@ -395,7 +395,7 @@ namespace multisig
   *    - Sanitizes input msgs.
   *    - Require uniqueness in: 'exclude_pubkeys'.
   *    - Requires each input pubkey be recommended by 'num_recommendations = expected_round' msg signers.
-  *      - For a final multisig key to be truly 'M-of-N', each of the the private key's components must be
+  *      - For a final multisig key to be truly 'M-of-N', each of the private key's components must be
   *        shared by (N - M + 1) signers.
   *    - Requires that msgs are signed by only keys in 'signers'.
   *    - Requires that each key in 'signers' recommends [num_signers - 2 CHOOSE (expected_round - 1)] pubkeys.
@@ -567,7 +567,7 @@ namespace multisig
 
     // note: do NOT remove the local signer from the pubkey origins map, since the post-kex round can be force-updated with
     //       just the local signer's post-kex message (if the local signer were removed, then the post-kex message's pubkeys
-    //       would be completely lost)
+    //       would be completely deleted)
 
     // evaluate pubkeys collected
 
@@ -608,7 +608,7 @@ namespace multisig
   * INTERNAL
   *
   * brief: multisig_kex_process_round_msgs - Process kex messages for the active kex round.
-  *    - A wrapper around evaluate_multisig_kex_round_msgs() -> multisig_kex_make_next_msg().
+  *    - A wrapper around evaluate_multisig_kex_round_msgs() -> multisig_kex_make_round_keys().
   *      - In other words, evaluate the input messages and try to make a message for the next round.
   *    - Note: Must be called on the final round's msgs to evaluate the final key components
   *            recommended by other participants.
@@ -623,7 +623,7 @@ namespace multisig
   * outparam: keys_to_origins_map_out - map between round keys and identity keys
   *    - If in the final round, these are key shares recommended by other signers for the final aggregate key.
   *    - Otherwise, these are the local account's DH derivations for the next round.
-  *      - See multisig_kex_make_next_msg() for an explanation.
+  *      - See multisig_kex_make_round_keys() for an explanation.
   * return: multisig kex message for next round, or empty message if 'current_round' is the final round
   */
   //----------------------------------------------------------------------------------------------------------------------
@@ -684,59 +684,67 @@ namespace multisig
   //----------------------------------------------------------------------------------------------------------------------
   // multisig_account: INTERNAL
   //----------------------------------------------------------------------------------------------------------------------
-  void multisig_account::initialize_kex_update(const std::vector<multisig_kex_msg> &expanded_msgs,
-    const std::uint32_t kex_rounds_required,
-    std::vector<crypto::public_key> &exclude_pubkeys_out)
+  std::vector<crypto::public_key> multisig_account::get_kex_exclude_pubkeys() const
   {
+    // exclude all keys the local account recommends
+    std::vector<crypto::public_key> exclude_pubkeys;
+
     if (m_kex_rounds_complete == 0)
     {
-      // the first round of kex msgs will contain each participant's base pubkeys and ancillary privkeys
-
-      // collect participants' base common privkey shares
-      // note: duplicate privkeys are acceptable, and duplicates due to duplicate signers
-      //       will be blocked by duplicate-signer errors after this function is called
-      std::vector<crypto::secret_key> participant_base_common_privkeys;
-      participant_base_common_privkeys.reserve(expanded_msgs.size() + 1);
-
-      // add local ancillary base privkey
-      participant_base_common_privkeys.emplace_back(m_base_common_privkey);
-
-      // add other signers' base common privkeys
-      for (const auto &expanded_msg : expanded_msgs)
-      {
-        if (expanded_msg.get_signing_pubkey() != m_base_pubkey)
-        {
-          participant_base_common_privkeys.emplace_back(expanded_msg.get_msg_privkey());
-        }
-      }
-
-      // make common privkey
-      make_multisig_common_privkey(std::move(participant_base_common_privkeys), m_common_privkey);
-
-      // set common pubkey
-      CHECK_AND_ASSERT_THROW_MES(crypto::secret_key_to_public_key(m_common_privkey, m_common_pubkey),
-        "Failed to derive public key");
-
-      // if N-of-N, then the base privkey will be used directly to make the account's share of the final key
-      if (kex_rounds_required == 1)
-      {
-        m_multisig_privkeys.clear();
-        m_multisig_privkeys.emplace_back(m_base_privkey);
-      }
-
-      // exclude all keys the local account recommends
-      // - in the first round, only the local pubkey is recommended by the local signer
-      exclude_pubkeys_out.emplace_back(m_base_pubkey);
+      // in the first round, only the local pubkey is recommended by the local signer
+      exclude_pubkeys.emplace_back(m_base_pubkey);
     }
     else
     {
-      // in other rounds, kex msgs will contain participants' shared keys
-
-      // ignore shared keys the account helped create for this round
+      // in other rounds, kex msgs will contain participants' shared keys, so ignore shared keys the account helped
+      //     create for this round
       for (const auto &shared_key_with_origins : m_kex_keys_to_origins_map)
-      {
-        exclude_pubkeys_out.emplace_back(shared_key_with_origins.first);
-      }
+        exclude_pubkeys.emplace_back(shared_key_with_origins.first);
+    }
+
+    return exclude_pubkeys;
+  }
+  //----------------------------------------------------------------------------------------------------------------------
+  // multisig_account: INTERNAL
+  //----------------------------------------------------------------------------------------------------------------------
+  void multisig_account::initialize_kex_update(const std::vector<multisig_kex_msg> &expanded_msgs,
+    const std::uint32_t kex_rounds_required)
+  {
+    // initialization is only needed during the first round
+    if (m_kex_rounds_complete > 0)
+      return;
+
+    // the first round of kex msgs will contain each participant's base pubkeys and ancillary privkeys, so we prepare
+    //    them here
+
+    // collect participants' base common privkey shares
+    // note: duplicate privkeys are acceptable, and duplicates due to duplicate signers
+    //       will be blocked by duplicate-signer errors after this function is called
+    std::vector<crypto::secret_key> participant_base_common_privkeys;
+    participant_base_common_privkeys.reserve(expanded_msgs.size() + 1);
+
+    // add local ancillary base privkey
+    participant_base_common_privkeys.emplace_back(m_base_common_privkey);
+
+    // add other signers' base common privkeys
+    for (const multisig_kex_msg &expanded_msg : expanded_msgs)
+    {
+      if (expanded_msg.get_signing_pubkey() != m_base_pubkey)
+        participant_base_common_privkeys.emplace_back(expanded_msg.get_msg_privkey());
+    }
+
+    // make common privkey
+    make_multisig_common_privkey(std::move(participant_base_common_privkeys), m_common_privkey);
+
+    // set common pubkey
+    CHECK_AND_ASSERT_THROW_MES(crypto::secret_key_to_public_key(m_common_privkey, m_common_pubkey),
+      "Failed to derive public key");
+
+    // if N-of-N, then the base privkey will be used directly to make the account's share of the final key
+    if (kex_rounds_required == 1)
+    {
+      m_multisig_privkeys.clear();
+      m_multisig_privkeys.emplace_back(m_base_privkey);
     }
   }
   //----------------------------------------------------------------------------------------------------------------------
@@ -771,9 +779,7 @@ namespace multisig
       result_keys.reserve(result_keys_to_origins_map.size());
 
       for (const auto &result_key_and_origins : result_keys_to_origins_map)
-      {
         result_keys.emplace_back(result_key_and_origins.first);
-      }
 
       // compute final aggregate key, update local multisig privkeys with aggregation coefficients applied
       m_multisig_pubkey = generate_multisig_aggregate_key(std::move(result_keys), m_multisig_privkeys);
@@ -811,7 +817,8 @@ namespace multisig
         // derived pubkey = multisig_key * G
         crypto::public_key_memsafe derived_pubkey;
         m_multisig_privkeys.push_back(
-          calculate_multisig_keypair_from_derivation(derivation_and_origins.first, derived_pubkey));
+            calculate_multisig_keypair_from_derivation(derivation_and_origins.first, derived_pubkey)
+          );
 
         // save the account's kex key mappings for this round [derived pubkey : other signers who will have the same key]
         m_kex_keys_to_origins_map[derived_pubkey] = std::move(derivation_and_origins.second);
@@ -863,8 +870,7 @@ namespace multisig
       "Multisig kex has already completed all required rounds (including post-kex verification).");
 
     // initialize account update
-    std::vector<crypto::public_key> exclude_pubkeys;
-    initialize_kex_update(expanded_msgs, kex_rounds_required, exclude_pubkeys);
+    this->initialize_kex_update(expanded_msgs, kex_rounds_required);
 
     // process messages into a [pubkey : {origins}] map
     multisig_keyset_map_memsafe_t result_keys_to_origins_map;
@@ -875,12 +881,75 @@ namespace multisig
       m_threshold,
       m_signers,
       expanded_msgs,
-      exclude_pubkeys,
+      this->get_kex_exclude_pubkeys(),
       incomplete_signer_set,
       result_keys_to_origins_map);
 
     // finish account update
-    finalize_kex_update(kex_rounds_required, std::move(result_keys_to_origins_map));
+    this->finalize_kex_update(kex_rounds_required, std::move(result_keys_to_origins_map));
+  }
+  //-----------------------------------------------------------------
+  // multisig_account: EXTERNAL
+  //-----------------------------------------------------------------
+  multisig_kex_msg multisig_account::get_multisig_kex_round_booster(const std::uint32_t threshold,
+    const std::uint32_t num_signers,
+    const std::vector<multisig_kex_msg> &expanded_msgs) const
+  {
+    // the messages passed in should be required for the next kex round of this account (the round it is currently
+    //   working on)
+    const std::uint32_t expected_msgs_round{m_kex_rounds_complete + 1};
+    const std::uint32_t kex_rounds_required{multisig_kex_rounds_required(num_signers, threshold)};
+
+    CHECK_AND_ASSERT_THROW_MES(num_signers > 1, "Must be at least one other multisig signer.");
+    CHECK_AND_ASSERT_THROW_MES(num_signers <= config::MULTISIG_MAX_SIGNERS, "Too many multisig signers specified.");
+    CHECK_AND_ASSERT_THROW_MES(expected_msgs_round < kex_rounds_required,
+      "Multisig kex booster: this account has already completed all intermediate kex rounds so it can't make a kex "
+      "booster (there is no round available to boost).");
+    CHECK_AND_ASSERT_THROW_MES(expanded_msgs.size() > 0, "At least one input kex message expected.");
+
+    // sanitize pubkeys from input msgs
+    multisig_keyset_map_memsafe_t pubkey_origins_map;
+    const std::uint32_t msgs_round{
+        multisig_kex_msgs_sanitize_pubkeys(expanded_msgs, this->get_kex_exclude_pubkeys(), pubkey_origins_map)
+      };
+    CHECK_AND_ASSERT_THROW_MES(msgs_round == expected_msgs_round, "Kex messages were not for expected round.");
+
+    // remove the local signer from sanitized messages
+    remove_key_from_mapped_sets(m_base_pubkey, pubkey_origins_map);
+
+    // make DH derivations for booster message
+    multisig_keyset_map_memsafe_t derivation_to_origins_map;
+    multisig_kex_make_round_keys(m_base_privkey, std::move(pubkey_origins_map), derivation_to_origins_map);
+
+    // collect keys for booster message
+    std::vector<crypto::public_key> next_msg_keys;
+    next_msg_keys.reserve(derivation_to_origins_map.size());
+
+    if (msgs_round + 1 == kex_rounds_required)
+    {
+      // final kex round: send DH derivation pubkeys in the message
+      for (const auto &derivation_and_origins : derivation_to_origins_map)
+      {
+        // multisig_privkey = H(derivation)
+        // derived pubkey = multisig_key * G
+        crypto::public_key_memsafe derived_pubkey;
+        calculate_multisig_keypair_from_derivation(derivation_and_origins.first, derived_pubkey);
+
+        // save keys that should be recommended to other signers
+        // - The keys multisig_key*G are sent to other participants in the message, so they can be used to produce the final
+        //   multisig key via generate_multisig_spend_public_key().
+        next_msg_keys.push_back(derived_pubkey);
+      }
+    }
+    else //(msgs_round + 1 < kex_rounds_required)
+    {
+      // intermediate kex round: send DH derivations directly in the message
+      for (const auto &derivation_and_origins : derivation_to_origins_map)
+        next_msg_keys.push_back(derivation_and_origins.first);
+    }
+
+    // produce a kex message for the round after the round this account is currently working on
+    return multisig_kex_msg{msgs_round + 1, m_base_privkey, std::move(next_msg_keys)}.get_msg();
   }
   //----------------------------------------------------------------------------------------------------------------------
 } //namespace multisig
diff --git a/src/multisig/multisig_clsag_context.cpp b/src/multisig/multisig_clsag_context.cpp
index 81e471287..8eec81719 100644
--- a/src/multisig/multisig_clsag_context.cpp
+++ b/src/multisig/multisig_clsag_context.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2021-2023, The Monero Project
+// Copyright (c) 2021-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/multisig/multisig_clsag_context.h b/src/multisig/multisig_clsag_context.h
index c184b83d6..20188bf3b 100644
--- a/src/multisig/multisig_clsag_context.h
+++ b/src/multisig/multisig_clsag_context.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2021-2023, The Monero Project
+// Copyright (c) 2021-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/multisig/multisig_kex_msg.cpp b/src/multisig/multisig_kex_msg.cpp
index 321305283..a6643e8fc 100644
--- a/src/multisig/multisig_kex_msg.cpp
+++ b/src/multisig/multisig_kex_msg.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2021-2023, The Monero Project
+// Copyright (c) 2021-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/multisig/multisig_kex_msg.h b/src/multisig/multisig_kex_msg.h
index 8cb8faa7c..180449a34 100644
--- a/src/multisig/multisig_kex_msg.h
+++ b/src/multisig/multisig_kex_msg.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2021-2023, The Monero Project
+// Copyright (c) 2021-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/multisig/multisig_kex_msg_serialization.h b/src/multisig/multisig_kex_msg_serialization.h
index d1e07ea8a..4d931d491 100644
--- a/src/multisig/multisig_kex_msg_serialization.h
+++ b/src/multisig/multisig_kex_msg_serialization.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2021-2023, The Monero Project
+// Copyright (c) 2021-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/multisig/multisig_tx_builder_ringct.cpp b/src/multisig/multisig_tx_builder_ringct.cpp
index 2653a70dd..e968fe00b 100644
--- a/src/multisig/multisig_tx_builder_ringct.cpp
+++ b/src/multisig/multisig_tx_builder_ringct.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2021-2023, The Monero Project
+// Copyright (c) 2021-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/multisig/multisig_tx_builder_ringct.h b/src/multisig/multisig_tx_builder_ringct.h
index a1b72b177..0a9cfd1bc 100644
--- a/src/multisig/multisig_tx_builder_ringct.h
+++ b/src/multisig/multisig_tx_builder_ringct.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2021-2023, The Monero Project
+// Copyright (c) 2021-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/net/CMakeLists.txt b/src/net/CMakeLists.txt
index 29807f50e..9483bdc11 100644
--- a/src/net/CMakeLists.txt
+++ b/src/net/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2018-2023, The Monero Project
+# Copyright (c) 2018-2024, The Monero Project
 
 #
 # All rights reserved.
diff --git a/src/net/dandelionpp.cpp b/src/net/dandelionpp.cpp
index b38427086..099951f28 100644
--- a/src/net/dandelionpp.cpp
+++ b/src/net/dandelionpp.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2023, The Monero Project
+// Copyright (c) 2019-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/net/dandelionpp.h b/src/net/dandelionpp.h
index 24e1fc625..4b192c181 100644
--- a/src/net/dandelionpp.h
+++ b/src/net/dandelionpp.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2023, The Monero Project
+// Copyright (c) 2019-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/net/error.cpp b/src/net/error.cpp
index 0f3de6a6c..7570aba50 100644
--- a/src/net/error.cpp
+++ b/src/net/error.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 //
 // All rights reserved.
diff --git a/src/net/error.h b/src/net/error.h
index 3bce227ee..56e7e074e 100644
--- a/src/net/error.h
+++ b/src/net/error.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 //
 // All rights reserved.
diff --git a/src/net/fwd.h b/src/net/fwd.h
index 871c13432..ee6a14aae 100644
--- a/src/net/fwd.h
+++ b/src/net/fwd.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 //
 // All rights reserved.
diff --git a/src/net/http.cpp b/src/net/http.cpp
index 8eaf5b4b4..b7d7adcac 100644
--- a/src/net/http.cpp
+++ b/src/net/http.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020-2023, The Monero Project
+// Copyright (c) 2020-2024, The Monero Project
 
 //
 // All rights reserved.
diff --git a/src/net/http.h b/src/net/http.h
index 72bd3f66d..3635e6a0b 100644
--- a/src/net/http.h
+++ b/src/net/http.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2020-2023, The Monero Project
+// Copyright (c) 2020-2024, The Monero Project
 
 //
 // All rights reserved.
diff --git a/src/net/i2p_address.cpp b/src/net/i2p_address.cpp
index d5ca8544f..dac056b95 100644
--- a/src/net/i2p_address.cpp
+++ b/src/net/i2p_address.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2023, The Monero Project
+// Copyright (c) 2019-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/net/i2p_address.h b/src/net/i2p_address.h
index f52ccbb51..44e268683 100644
--- a/src/net/i2p_address.h
+++ b/src/net/i2p_address.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2023, The Monero Project
+// Copyright (c) 2019-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/net/parse.cpp b/src/net/parse.cpp
index a6d665082..6d580f688 100644
--- a/src/net/parse.cpp
+++ b/src/net/parse.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 //
 // All rights reserved.
diff --git a/src/net/parse.h b/src/net/parse.h
index 695049268..9795cdf14 100644
--- a/src/net/parse.h
+++ b/src/net/parse.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 //
 // All rights reserved.
diff --git a/src/net/resolve.cpp b/src/net/resolve.cpp
index c4c8e8d08..4d8357112 100644
--- a/src/net/resolve.cpp
+++ b/src/net/resolve.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020-2023, The Monero Project
+// Copyright (c) 2020-2024, The Monero Project
 
 //
 // All rights reserved.
diff --git a/src/net/resolve.h b/src/net/resolve.h
index afbe5d879..d08e07e37 100644
--- a/src/net/resolve.h
+++ b/src/net/resolve.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2020-2023, The Monero Project
+// Copyright (c) 2020-2024, The Monero Project
 
 //
 // All rights reserved.
diff --git a/src/net/socks.cpp b/src/net/socks.cpp
index cc8e0be6d..734537b44 100644
--- a/src/net/socks.cpp
+++ b/src/net/socks.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/net/socks.h b/src/net/socks.h
index 920496c6e..352730576 100644
--- a/src/net/socks.h
+++ b/src/net/socks.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/net/socks_connect.cpp b/src/net/socks_connect.cpp
index f24569674..febcd218b 100644
--- a/src/net/socks_connect.cpp
+++ b/src/net/socks_connect.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2023, The Monero Project
+// Copyright (c) 2019-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/net/socks_connect.h b/src/net/socks_connect.h
index e036d9c31..2ef07a478 100644
--- a/src/net/socks_connect.h
+++ b/src/net/socks_connect.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2023, The Monero Project
+// Copyright (c) 2019-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/net/tor_address.cpp b/src/net/tor_address.cpp
index ad8b399c8..e9f1b7a97 100644
--- a/src/net/tor_address.cpp
+++ b/src/net/tor_address.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 //
 // All rights reserved.
diff --git a/src/net/tor_address.h b/src/net/tor_address.h
index d04bf5145..2c6320871 100644
--- a/src/net/tor_address.h
+++ b/src/net/tor_address.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 //
 // All rights reserved.
diff --git a/src/net/zmq.cpp b/src/net/zmq.cpp
index f57cbdac6..e89684980 100644
--- a/src/net/zmq.cpp
+++ b/src/net/zmq.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2023, The Monero Project
+// Copyright (c) 2019-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/net/zmq.h b/src/net/zmq.h
index 2b60432d7..93a31f2f3 100644
--- a/src/net/zmq.h
+++ b/src/net/zmq.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2023, The Monero Project
+// Copyright (c) 2019-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/p2p/CMakeLists.txt b/src/p2p/CMakeLists.txt
index bd2345979..a7330ca86 100644
--- a/src/p2p/CMakeLists.txt
+++ b/src/p2p/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/src/p2p/net_node.cpp b/src/p2p/net_node.cpp
index 13ff06c8b..d6681238f 100644
--- a/src/p2p/net_node.cpp
+++ b/src/p2p/net_node.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h
index 459a6a396..7b3477e1f 100644
--- a/src/p2p/net_node.h
+++ b/src/p2p/net_node.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl
index 505ba026e..662e598e8 100644
--- a/src/p2p/net_node.inl
+++ b/src/p2p/net_node.inl
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/p2p/net_node_common.h b/src/p2p/net_node_common.h
index 66ef40f1e..fd6ea4a99 100644
--- a/src/p2p/net_node_common.h
+++ b/src/p2p/net_node_common.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/p2p/net_peerlist.cpp b/src/p2p/net_peerlist.cpp
index 78cf13785..1ab07cdb7 100644
--- a/src/p2p/net_peerlist.cpp
+++ b/src/p2p/net_peerlist.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 //
 // All rights reserved.
diff --git a/src/p2p/net_peerlist.h b/src/p2p/net_peerlist.h
index c2f700598..7f3cd3c07 100644
--- a/src/p2p/net_peerlist.h
+++ b/src/p2p/net_peerlist.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/p2p/net_peerlist_boost_serialization.h b/src/p2p/net_peerlist_boost_serialization.h
index 0b7a54860..da1781420 100644
--- a/src/p2p/net_peerlist_boost_serialization.h
+++ b/src/p2p/net_peerlist_boost_serialization.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/p2p/p2p_protocol_defs.h b/src/p2p/p2p_protocol_defs.h
index 18b853d35..11d7d834c 100644
--- a/src/p2p/p2p_protocol_defs.h
+++ b/src/p2p/p2p_protocol_defs.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/ringct/CMakeLists.txt b/src/ringct/CMakeLists.txt
index 1f3d1913f..656ef494a 100644
--- a/src/ringct/CMakeLists.txt
+++ b/src/ringct/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2016-2023, The Monero Project
+# Copyright (c) 2016-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc
index 2cfaf18c0..cf1ddc879 100644
--- a/src/ringct/bulletproofs.cc
+++ b/src/ringct/bulletproofs.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/ringct/bulletproofs.h b/src/ringct/bulletproofs.h
index 7ff47e8a0..55e1e6741 100644
--- a/src/ringct/bulletproofs.h
+++ b/src/ringct/bulletproofs.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/ringct/bulletproofs_plus.cc b/src/ringct/bulletproofs_plus.cc
index 0619811cb..024ce8cf4 100644
--- a/src/ringct/bulletproofs_plus.cc
+++ b/src/ringct/bulletproofs_plus.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/ringct/bulletproofs_plus.h b/src/ringct/bulletproofs_plus.h
index 80a8de8fb..c718bf3fc 100644
--- a/src/ringct/bulletproofs_plus.h
+++ b/src/ringct/bulletproofs_plus.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/ringct/multiexp.cc b/src/ringct/multiexp.cc
index 5e241f2cc..fc24eed26 100644
--- a/src/ringct/multiexp.cc
+++ b/src/ringct/multiexp.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 
 // 
 // All rights reserved.
diff --git a/src/ringct/multiexp.h b/src/ringct/multiexp.h
index ae10fcc8e..2259fcdd9 100644
--- a/src/ringct/multiexp.h
+++ b/src/ringct/multiexp.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 
 // 
 // All rights reserved.
diff --git a/src/ringct/rctCryptoOps.c b/src/ringct/rctCryptoOps.c
index b4fcc64bd..0067cfae2 100644
--- a/src/ringct/rctCryptoOps.c
+++ b/src/ringct/rctCryptoOps.c
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/ringct/rctCryptoOps.h b/src/ringct/rctCryptoOps.h
index ec5708818..dbe62120e 100644
--- a/src/ringct/rctCryptoOps.h
+++ b/src/ringct/rctCryptoOps.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/ringct/rctOps.cpp b/src/ringct/rctOps.cpp
index 56dd53b2c..0e18cb461 100644
--- a/src/ringct/rctOps.cpp
+++ b/src/ringct/rctOps.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2016-2023, Monero Research Labs
+// Copyright (c) 2016-2024, Monero Research Labs
 //
 // Author: Shen Noether <shen.noether@gmx.com>
 //
diff --git a/src/ringct/rctOps.h b/src/ringct/rctOps.h
index eaab998cb..0edd0308c 100644
--- a/src/ringct/rctOps.h
+++ b/src/ringct/rctOps.h
@@ -1,5 +1,5 @@
 //#define DBG
-// Copyright (c) 2016-2023, Monero Research Labs
+// Copyright (c) 2016-2024, Monero Research Labs
 //
 // Author: Shen Noether <shen.noether@gmx.com>
 //
diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp
index 717bc27a3..2d92ba05d 100644
--- a/src/ringct/rctSigs.cpp
+++ b/src/ringct/rctSigs.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2016-2023, Monero Research Labs
+// Copyright (c) 2016-2024, Monero Research Labs
 //
 // Author: Shen Noether <shen.noether@gmx.com>
 // 
diff --git a/src/ringct/rctSigs.h b/src/ringct/rctSigs.h
index 5c4773eb9..035d866d6 100644
--- a/src/ringct/rctSigs.h
+++ b/src/ringct/rctSigs.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2016-2023, Monero Research Labs
+// Copyright (c) 2016-2024, Monero Research Labs
 //
 // Author: Shen Noether <shen.noether@gmx.com>
 //
diff --git a/src/ringct/rctTypes.cpp b/src/ringct/rctTypes.cpp
index 4ac75b78b..8b0345e97 100644
--- a/src/ringct/rctTypes.cpp
+++ b/src/ringct/rctTypes.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2016-2023, Monero Research Labs
+// Copyright (c) 2016-2024, Monero Research Labs
 //
 // Author: Shen Noether <shen.noether@gmx.com>
 // 
diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h
index dea95ace0..247f25fff 100644
--- a/src/ringct/rctTypes.h
+++ b/src/ringct/rctTypes.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2016-2023, Monero Research Labs
+// Copyright (c) 2016-2024, Monero Research Labs
 //
 // Author: Shen Noether <shen.noether@gmx.com>
 //
diff --git a/src/rpc/CMakeLists.txt b/src/rpc/CMakeLists.txt
index fe80d3083..d01a4c44a 100644
--- a/src/rpc/CMakeLists.txt
+++ b/src/rpc/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/src/rpc/bootstrap_node_selector.cpp b/src/rpc/bootstrap_node_selector.cpp
index d224c5c53..fab671d4e 100644
--- a/src/rpc/bootstrap_node_selector.cpp
+++ b/src/rpc/bootstrap_node_selector.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020-2023, The Monero Project
+// Copyright (c) 2020-2024, The Monero Project
 
 // 
 // All rights reserved.
diff --git a/src/rpc/bootstrap_node_selector.h b/src/rpc/bootstrap_node_selector.h
index 3e52c3538..5931ca9d7 100644
--- a/src/rpc/bootstrap_node_selector.h
+++ b/src/rpc/bootstrap_node_selector.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2020-2023, The Monero Project
+// Copyright (c) 2020-2024, The Monero Project
 
 // 
 // All rights reserved.
diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index 16d66e79d..f9afa4021 100644
--- a/src/rpc/core_rpc_server.cpp
+++ b/src/rpc/core_rpc_server.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
@@ -702,15 +702,24 @@ namespace cryptonote
     if (get_blocks)
     {
       // quick check for noop
-      if (!req.block_ids.empty())
+      if (req.start_height > 0 || !req.block_ids.empty())
       {
         uint64_t last_block_height;
         crypto::hash last_block_hash;
         m_core.get_blockchain_top(last_block_height, last_block_hash);
-        if (last_block_hash == req.block_ids.front())
+
+        if (!req.high_height_ok && req.start_height > last_block_height)
+        {
+          res.status = "Failed";
+          return true;
+        }
+
+        if (req.start_height > last_block_height ||
+           (!req.block_ids.empty() && last_block_hash == req.block_ids.front()))
         {
           res.start_height = 0;
           res.current_height = last_block_height + 1;
+          res.top_block_hash = last_block_hash;
           res.status = CORE_RPC_STATUS_OK;
           return true;
         }
@@ -730,7 +739,7 @@ namespace cryptonote
       }
 
       std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata> > > > bs;
-      if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, bs, res.current_height, res.start_height, req.prune, !req.no_miner_tx, max_blocks, COMMAND_RPC_GET_BLOCKS_FAST_MAX_TX_COUNT))
+      if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, bs, res.current_height, res.top_block_hash, res.start_height, req.prune, !req.no_miner_tx, max_blocks, COMMAND_RPC_GET_BLOCKS_FAST_MAX_TX_COUNT))
       {
         res.status = "Failed";
         add_host_fail(ctx);
@@ -1847,10 +1856,10 @@ namespace cryptonote
     return 0;
   }
   //------------------------------------------------------------------------------------------------------------------------------
-  bool core_rpc_server::get_block_template(const account_public_address &address, const crypto::hash *prev_block, const cryptonote::blobdata &extra_nonce, size_t &reserved_offset, cryptonote::difficulty_type  &difficulty, uint64_t &height, uint64_t &expected_reward, block &b, uint64_t &seed_height, crypto::hash &seed_hash, crypto::hash &next_seed_hash, epee::json_rpc::error &error_resp)
+  bool core_rpc_server::get_block_template(const account_public_address &address, const crypto::hash *prev_block, const cryptonote::blobdata &extra_nonce, size_t &reserved_offset, cryptonote::difficulty_type  &difficulty, uint64_t &height, uint64_t &expected_reward, uint64_t& cumulative_weight, block &b, uint64_t &seed_height, crypto::hash &seed_hash, crypto::hash &next_seed_hash, epee::json_rpc::error &error_resp)
   {
     b = boost::value_initialized<cryptonote::block>();
-    if(!m_core.get_block_template(b, prev_block, address, difficulty, height, expected_reward, extra_nonce, seed_height, seed_hash))
+    if(!m_core.get_block_template(b, prev_block, address, difficulty, height, expected_reward, cumulative_weight, extra_nonce, seed_height, seed_hash))
     {
       error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
       error_resp.message = "Internal error: failed to create block template";
@@ -1975,7 +1984,7 @@ namespace cryptonote
       }
     }
     crypto::hash seed_hash, next_seed_hash;
-    if (!get_block_template(info.address, req.prev_block.empty() ? NULL : &prev_block, blob_reserve, reserved_offset, wdiff, res.height, res.expected_reward, b, res.seed_height, seed_hash, next_seed_hash, error_resp))
+    if (!get_block_template(info.address, req.prev_block.empty() ? NULL : &prev_block, blob_reserve, reserved_offset, wdiff, res.height, res.expected_reward, res.cumulative_weight, b, res.seed_height, seed_hash, next_seed_hash, error_resp))
       return false;
     if (b.major_version >= RX_BLOCK_VERSION)
     {
@@ -3511,9 +3520,9 @@ namespace cryptonote
     crypto::hash seed_hash, next_seed_hash;
     if (!m_rpc_payment->get_info(client, [&](const cryptonote::blobdata &extra_nonce, cryptonote::block &b, uint64_t &seed_height, crypto::hash &seed_hash)->bool{
       cryptonote::difficulty_type difficulty;
-      uint64_t height, expected_reward;
+      uint64_t height, expected_reward, cumulative_weight;
       size_t reserved_offset;
-      if (!get_block_template(m_rpc_payment->get_payment_address(), NULL, extra_nonce, reserved_offset, difficulty, height, expected_reward, b, seed_height, seed_hash, next_seed_hash, error_resp))
+      if (!get_block_template(m_rpc_payment->get_payment_address(), NULL, extra_nonce, reserved_offset, difficulty, height, expected_reward, cumulative_weight, b, seed_height, seed_hash, next_seed_hash, error_resp))
         return false;
       return true;
     }, hashing_blob, res.seed_height, seed_hash, top_hash, res.diff, res.credits_per_hash_found, res.credits, res.cookie))
diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h
index 7c31d2539..6bd0fc25b 100644
--- a/src/rpc/core_rpc_server.h
+++ b/src/rpc/core_rpc_server.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
@@ -285,7 +285,7 @@ private:
     enum invoke_http_mode { JON, BIN, JON_RPC };
     template <typename COMMAND_TYPE>
     bool use_bootstrap_daemon_if_necessary(const invoke_http_mode &mode, const std::string &command_name, const typename COMMAND_TYPE::request& req, typename COMMAND_TYPE::response& res, bool &r);
-    bool get_block_template(const account_public_address &address, const crypto::hash *prev_block, const cryptonote::blobdata &extra_nonce, size_t &reserved_offset, cryptonote::difficulty_type &difficulty, uint64_t &height, uint64_t &expected_reward, block &b, uint64_t &seed_height, crypto::hash &seed_hash, crypto::hash &next_seed_hash, epee::json_rpc::error &error_resp);
+    bool get_block_template(const account_public_address &address, const crypto::hash *prev_block, const cryptonote::blobdata &extra_nonce, size_t &reserved_offset, cryptonote::difficulty_type &difficulty, uint64_t &height, uint64_t &expected_reward, uint64_t& cumulative_weight, block &b, uint64_t &seed_height, crypto::hash &seed_hash, crypto::hash &next_seed_hash, epee::json_rpc::error &error_resp);
     bool check_payment(const std::string &client, uint64_t payment, const std::string &rpc, bool same_ts, std::string &message, uint64_t &credits, std::string &top_hash);
     
     core& m_core;
diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h
index c634b8957..618bf107e 100644
--- a/src/rpc/core_rpc_server_commands_defs.h
+++ b/src/rpc/core_rpc_server_commands_defs.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
@@ -80,6 +80,19 @@ namespace cryptonote
 #define CORE_RPC_STATUS_NOT_MINING "NOT MINING"
 #define CORE_RPC_STATUS_PAYMENT_REQUIRED "PAYMENT REQUIRED"
 
+inline const std::string get_rpc_status(const bool trusted_daemon, const std::string &s)
+{
+  if (trusted_daemon)
+    return s;
+  if (s == CORE_RPC_STATUS_OK)
+    return s;
+  if (s == CORE_RPC_STATUS_BUSY)
+    return s;
+  if (s == CORE_RPC_STATUS_PAYMENT_REQUIRED)
+    return s;
+  return "<error>";
+}
+
 // When making *any* change here, bump minor
 // If the change is incompatible, then bump major and set minor to 0
 // This ensures CORE_RPC_VERSION always increases, that every change
@@ -88,7 +101,7 @@ namespace cryptonote
 // advance which version they will stop working with
 // Don't go over 32767 for any of these
 #define CORE_RPC_VERSION_MAJOR 3
-#define CORE_RPC_VERSION_MINOR 14
+#define CORE_RPC_VERSION_MINOR 16
 #define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor))
 #define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR)
 
@@ -176,6 +189,7 @@ namespace cryptonote
       uint64_t    start_height;
       bool        prune;
       bool        no_miner_tx;
+      bool        high_height_ok;
       uint64_t    pool_info_since;
       BEGIN_KV_SERIALIZE_MAP()
         KV_SERIALIZE_PARENT(rpc_access_request_base)
@@ -184,6 +198,7 @@ namespace cryptonote
         KV_SERIALIZE(start_height)
         KV_SERIALIZE(prune)
         KV_SERIALIZE_OPT(no_miner_tx, false)
+        KV_SERIALIZE_OPT(high_height_ok, false) // default false maintains backwards compatibility for clients that relied on failure on high height
         KV_SERIALIZE_OPT(pool_info_since, (uint64_t)0)
       END_KV_SERIALIZE_MAP()
     };
@@ -232,6 +247,7 @@ namespace cryptonote
       std::vector<block_complete_entry> blocks;
       uint64_t    start_height;
       uint64_t    current_height;
+      crypto::hash top_block_hash;
       std::vector<block_output_indices> output_indices;
       uint64_t    daemon_time;
       uint8_t     pool_info_extent;
@@ -244,6 +260,7 @@ namespace cryptonote
         KV_SERIALIZE(blocks)
         KV_SERIALIZE(start_height)
         KV_SERIALIZE(current_height)
+        KV_SERIALIZE_VAL_POD_AS_BLOB_OPT(top_block_hash, crypto::null_hash)
         KV_SERIALIZE(output_indices)
         KV_SERIALIZE_OPT(daemon_time, (uint64_t) 0)
         KV_SERIALIZE_OPT(pool_info_extent, (uint8_t) 0)
@@ -967,6 +984,7 @@ namespace cryptonote
       uint64_t height;
       uint64_t reserved_offset;
       uint64_t expected_reward;
+      uint64_t cumulative_weight;
       std::string prev_hash;
       uint64_t seed_height;
       std::string seed_hash;
@@ -982,6 +1000,7 @@ namespace cryptonote
         KV_SERIALIZE(height)
         KV_SERIALIZE(reserved_offset)
         KV_SERIALIZE(expected_reward)
+        KV_SERIALIZE_OPT(cumulative_weight, static_cast<uint64_t>(0))
         KV_SERIALIZE(prev_hash)
         KV_SERIALIZE(seed_height)
         KV_SERIALIZE(blocktemplate_blob)
@@ -1830,25 +1849,6 @@ namespace cryptonote
     typedef epee::misc_utils::struct_init<response_t> response;
   };
   
-  struct COMMAND_RPC_FAST_EXIT
-  {
-    struct request_t: public rpc_request_base
-    {
-      BEGIN_KV_SERIALIZE_MAP()
-        KV_SERIALIZE_PARENT(rpc_request_base)
-      END_KV_SERIALIZE_MAP()
-    };
-    typedef epee::misc_utils::struct_init<request_t> request;
-    
-    struct response_t: public rpc_response_base
-    {
-      BEGIN_KV_SERIALIZE_MAP()
-        KV_SERIALIZE_PARENT(rpc_response_base)
-      END_KV_SERIALIZE_MAP()
-    };
-    typedef epee::misc_utils::struct_init<response_t> response;
-  };
-  
   struct COMMAND_RPC_GET_LIMIT
   {
     struct request_t: public rpc_request_base
diff --git a/src/rpc/core_rpc_server_error_codes.h b/src/rpc/core_rpc_server_error_codes.h
index 2a4726a89..bccbb1d73 100644
--- a/src/rpc/core_rpc_server_error_codes.h
+++ b/src/rpc/core_rpc_server_error_codes.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp
index 4712de134..9aa014f1f 100644
--- a/src/rpc/daemon_handler.cpp
+++ b/src/rpc/daemon_handler.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
@@ -128,7 +128,7 @@ namespace rpc
   {
     std::vector<std::pair<std::pair<blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, blobdata> > > > blocks;
 
-    if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, blocks, res.current_height, res.start_height, req.prune, true, COMMAND_RPC_GET_BLOCKS_FAST_MAX_BLOCK_COUNT, COMMAND_RPC_GET_BLOCKS_FAST_MAX_TX_COUNT))
+    if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, blocks, res.current_height, res.top_block_hash, res.start_height, req.prune, true, COMMAND_RPC_GET_BLOCKS_FAST_MAX_BLOCK_COUNT, COMMAND_RPC_GET_BLOCKS_FAST_MAX_TX_COUNT))
     {
       res.status = Message::STATUS_FAILED;
       res.error_details = "core::find_blockchain_supplement() returned false";
@@ -520,6 +520,8 @@ namespace rpc
       res.info.target_height = res.info.height;
     }
 
+    m_core.get_blockchain_top(res.info.top_block_height, res.info.top_block_hash);
+
     auto& chain = m_core.get_blockchain_storage();
 
     res.info.wide_difficulty = chain.get_difficulty_for_next_block();
diff --git a/src/rpc/daemon_handler.h b/src/rpc/daemon_handler.h
index 11a6c2849..1da5419d2 100644
--- a/src/rpc/daemon_handler.h
+++ b/src/rpc/daemon_handler.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/rpc/daemon_messages.cpp b/src/rpc/daemon_messages.cpp
index 36528fcea..ad3896862 100644
--- a/src/rpc/daemon_messages.cpp
+++ b/src/rpc/daemon_messages.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2016-2023, The Monero Project
+// Copyright (c) 2016-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/rpc/daemon_messages.h b/src/rpc/daemon_messages.h
index d4b55f8fd..83546fce3 100644
--- a/src/rpc/daemon_messages.h
+++ b/src/rpc/daemon_messages.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2016-2023, The Monero Project
+// Copyright (c) 2016-2024, The Monero Project
 // 
 // All rights reserved.
 // 
@@ -97,6 +97,7 @@ BEGIN_RPC_MESSAGE_CLASS(GetBlocksFast);
     RPC_MESSAGE_MEMBER(std::vector<cryptonote::rpc::block_with_transactions>, blocks);
     RPC_MESSAGE_MEMBER(uint64_t, start_height);
     RPC_MESSAGE_MEMBER(uint64_t, current_height);
+    RPC_MESSAGE_MEMBER(crypto::hash, top_block_hash);
     RPC_MESSAGE_MEMBER(std::vector<cryptonote::rpc::block_output_indices>, output_indices);
   END_RPC_MESSAGE_RESPONSE;
 END_RPC_MESSAGE_CLASS;
diff --git a/src/rpc/daemon_rpc_version.h b/src/rpc/daemon_rpc_version.h
index d14b85ed9..a9e34b926 100644
--- a/src/rpc/daemon_rpc_version.h
+++ b/src/rpc/daemon_rpc_version.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2016-2023, The Monero Project
+// Copyright (c) 2016-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/rpc/fwd.h b/src/rpc/fwd.h
index 6227f5696..f312d6eda 100644
--- a/src/rpc/fwd.h
+++ b/src/rpc/fwd.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2023, The Monero Project
+// Copyright (c) 2019-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/rpc/instanciations.cpp b/src/rpc/instanciations.cpp
index d4f5231f3..352031695 100644
--- a/src/rpc/instanciations.cpp
+++ b/src/rpc/instanciations.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/rpc/message.cpp b/src/rpc/message.cpp
index 9fda96e71..e2d0270ca 100644
--- a/src/rpc/message.cpp
+++ b/src/rpc/message.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2016-2023, The Monero Project
+// Copyright (c) 2016-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/rpc/message.h b/src/rpc/message.h
index b7253e09b..e4e57ba05 100644
--- a/src/rpc/message.h
+++ b/src/rpc/message.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2016-2023, The Monero Project
+// Copyright (c) 2016-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/rpc/message_data_structs.h b/src/rpc/message_data_structs.h
index 542d71239..9cf6c908c 100644
--- a/src/rpc/message_data_structs.h
+++ b/src/rpc/message_data_structs.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2016-2023, The Monero Project
+// Copyright (c) 2016-2024, The Monero Project
 // 
 // All rights reserved.
 // 
@@ -176,6 +176,7 @@ namespace rpc
   {
     uint64_t height;
     uint64_t target_height;
+    uint64_t top_block_height;
     cryptonote::difficulty_type wide_difficulty;
     uint64_t difficulty;
     uint64_t target;
diff --git a/src/rpc/rpc_args.cpp b/src/rpc/rpc_args.cpp
index e6233fa98..18a3fb90c 100644
--- a/src/rpc/rpc_args.cpp
+++ b/src/rpc/rpc_args.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/rpc/rpc_args.h b/src/rpc/rpc_args.h
index ac6a5d7cd..0286db062 100644
--- a/src/rpc/rpc_args.h
+++ b/src/rpc/rpc_args.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/rpc/rpc_handler.h b/src/rpc/rpc_handler.h
index e13d3036f..f18f4afe2 100644
--- a/src/rpc/rpc_handler.h
+++ b/src/rpc/rpc_handler.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2016-2023, The Monero Project
+// Copyright (c) 2016-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/rpc/rpc_payment.cpp b/src/rpc/rpc_payment.cpp
index 71a00aca4..40f37b5bf 100644
--- a/src/rpc/rpc_payment.cpp
+++ b/src/rpc/rpc_payment.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/rpc/rpc_payment.h b/src/rpc/rpc_payment.h
index a4cc6db57..227d937dd 100644
--- a/src/rpc/rpc_payment.h
+++ b/src/rpc/rpc_payment.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/rpc/rpc_payment_costs.h b/src/rpc/rpc_payment_costs.h
index 36ee5ec1d..37d6635c0 100644
--- a/src/rpc/rpc_payment_costs.h
+++ b/src/rpc/rpc_payment_costs.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2023, The Monero Project
+// Copyright (c) 2019-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/rpc/rpc_payment_signature.cpp b/src/rpc/rpc_payment_signature.cpp
index ec7aeb715..3e7f5fd7d 100644
--- a/src/rpc/rpc_payment_signature.cpp
+++ b/src/rpc/rpc_payment_signature.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/rpc/rpc_payment_signature.h b/src/rpc/rpc_payment_signature.h
index f0539446f..dea6bac05 100644
--- a/src/rpc/rpc_payment_signature.h
+++ b/src/rpc/rpc_payment_signature.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/rpc/rpc_version_str.cpp b/src/rpc/rpc_version_str.cpp
index 07756d636..1d44df6f0 100644
--- a/src/rpc/rpc_version_str.cpp
+++ b/src/rpc/rpc_version_str.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2023, The Monero Project
+// Copyright (c) 2019-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/rpc/rpc_version_str.h b/src/rpc/rpc_version_str.h
index 2cfa9f867..40254d707 100644
--- a/src/rpc/rpc_version_str.h
+++ b/src/rpc/rpc_version_str.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2023, The Monero Project
+// Copyright (c) 2019-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/rpc/zmq_pub.cpp b/src/rpc/zmq_pub.cpp
index 4c620c59a..cc9705d35 100644
--- a/src/rpc/zmq_pub.cpp
+++ b/src/rpc/zmq_pub.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020-2023, The Monero Project
+// Copyright (c) 2020-2024, The Monero Project
 
 //
 // All rights reserved.
diff --git a/src/rpc/zmq_pub.h b/src/rpc/zmq_pub.h
index fe60527a6..add9481c6 100644
--- a/src/rpc/zmq_pub.h
+++ b/src/rpc/zmq_pub.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2020-2023, The Monero Project
+// Copyright (c) 2020-2024, The Monero Project
 
 //
 // All rights reserved.
diff --git a/src/rpc/zmq_server.cpp b/src/rpc/zmq_server.cpp
index d73ea3bc9..96aaee8f4 100644
--- a/src/rpc/zmq_server.cpp
+++ b/src/rpc/zmq_server.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2016-2023, The Monero Project
+// Copyright (c) 2016-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/rpc/zmq_server.h b/src/rpc/zmq_server.h
index b561fd580..c72730a63 100644
--- a/src/rpc/zmq_server.h
+++ b/src/rpc/zmq_server.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2016-2023, The Monero Project
+// Copyright (c) 2016-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/seraphis_crypto/CMakeLists.txt b/src/seraphis_crypto/CMakeLists.txt
index aeae61ad1..19b6ad7f5 100644
--- a/src/seraphis_crypto/CMakeLists.txt
+++ b/src/seraphis_crypto/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2021, The Monero Project
+# Copyright (c) 2021-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/src/seraphis_crypto/sp_transcript.h b/src/seraphis_crypto/sp_transcript.h
index c2fbd88c2..bb99b5662 100644
--- a/src/seraphis_crypto/sp_transcript.h
+++ b/src/seraphis_crypto/sp_transcript.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2022, The Monero Project
+// Copyright (c) 2022-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/serialization/CMakeLists.txt b/src/serialization/CMakeLists.txt
index 07076bae2..9bbba6a6c 100644
--- a/src/serialization/CMakeLists.txt
+++ b/src/serialization/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2016-2023, The Monero Project
+# Copyright (c) 2016-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/src/serialization/binary_archive.h b/src/serialization/binary_archive.h
index b3946a84b..bbab6ece9 100644
--- a/src/serialization/binary_archive.h
+++ b/src/serialization/binary_archive.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/serialization/binary_utils.h b/src/serialization/binary_utils.h
index 247c60beb..885decf88 100644
--- a/src/serialization/binary_utils.h
+++ b/src/serialization/binary_utils.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/serialization/container.h b/src/serialization/container.h
index 6f0c48069..2ed63f1be 100644
--- a/src/serialization/container.h
+++ b/src/serialization/container.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/serialization/containers.h b/src/serialization/containers.h
index 28eaa50fb..196bc5a12 100644
--- a/src/serialization/containers.h
+++ b/src/serialization/containers.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/serialization/crypto.h b/src/serialization/crypto.h
index 2da26abe7..57f05490e 100644
--- a/src/serialization/crypto.h
+++ b/src/serialization/crypto.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/serialization/debug_archive.h b/src/serialization/debug_archive.h
index f4df963db..b3f346f7e 100644
--- a/src/serialization/debug_archive.h
+++ b/src/serialization/debug_archive.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/serialization/difficulty_type.h b/src/serialization/difficulty_type.h
index c071f287b..7e2367b1e 100644
--- a/src/serialization/difficulty_type.h
+++ b/src/serialization/difficulty_type.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2023, The Monero Project
+// Copyright (c) 2019-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/serialization/json_archive.h b/src/serialization/json_archive.h
index 194ddaee9..c97125550 100644
--- a/src/serialization/json_archive.h
+++ b/src/serialization/json_archive.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/serialization/json_object.cpp b/src/serialization/json_object.cpp
index fb0967a89..134bf1c69 100644
--- a/src/serialization/json_object.cpp
+++ b/src/serialization/json_object.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2016-2023, The Monero Project
+// Copyright (c) 2016-2024, The Monero Project
 // 
 // All rights reserved.
 // 
@@ -273,7 +273,10 @@ void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::t
   {
     INSERT_INTO_JSON_OBJECT(dest, signatures, tx.signatures);
   }
-  INSERT_INTO_JSON_OBJECT(dest, ringct, tx.rct_signatures);
+  {
+    dest.Key("ringct");
+    toJsonValue(dest, tx.rct_signatures, tx.pruned);
+  }
 
   dest.EndObject();
 }
@@ -1111,7 +1114,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::BlockHeaderResp
   GET_FROM_JSON_OBJECT(val, response.reward, reward);
 }
 
-void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const rct::rctSig& sig)
+void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const rct::rctSig& sig, const bool prune)
 {
   using boost::adaptors::transform;
 
@@ -1131,7 +1134,7 @@ void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const rct::rctSig&
   }
 
   // prunable
-  if (!sig.p.bulletproofs.empty() || !sig.p.bulletproofs_plus.empty() || !sig.p.rangeSigs.empty() || !sig.p.MGs.empty() || !sig.get_pseudo_outs().empty())
+  if (!prune && (!sig.p.bulletproofs.empty() || !sig.p.bulletproofs_plus.empty() || !sig.p.rangeSigs.empty() || !sig.p.MGs.empty() || !sig.get_pseudo_outs().empty()))
   {
     dest.Key("prunable");
     dest.StartObject();
@@ -1423,9 +1426,14 @@ void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::r
 {
   dest.StartObject();
 
+  const uint64_t difficulty_top64 = (info.wide_difficulty >> 64).convert_to<std::uint64_t>();
+  const uint64_t cumulative_difficulty_top64 = (info.wide_cumulative_difficulty >> 64).convert_to<std::uint64_t>();
+
   INSERT_INTO_JSON_OBJECT(dest, height, info.height);
   INSERT_INTO_JSON_OBJECT(dest, target_height, info.target_height);
+  INSERT_INTO_JSON_OBJECT(dest, top_block_height, info.top_block_height);
   INSERT_INTO_JSON_OBJECT(dest, difficulty, info.difficulty);
+  INSERT_INTO_JSON_OBJECT(dest, difficulty_top64, difficulty_top64);
   INSERT_INTO_JSON_OBJECT(dest, target, info.target);
   INSERT_INTO_JSON_OBJECT(dest, tx_count, info.tx_count);
   INSERT_INTO_JSON_OBJECT(dest, tx_pool_size, info.tx_pool_size);
@@ -1440,12 +1448,14 @@ void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::r
   INSERT_INTO_JSON_OBJECT(dest, nettype, info.nettype);
   INSERT_INTO_JSON_OBJECT(dest, top_block_hash, info.top_block_hash);
   INSERT_INTO_JSON_OBJECT(dest, cumulative_difficulty, info.cumulative_difficulty);
+  INSERT_INTO_JSON_OBJECT(dest, cumulative_difficulty_top64, cumulative_difficulty_top64);
   INSERT_INTO_JSON_OBJECT(dest, block_size_limit, info.block_size_limit);
   INSERT_INTO_JSON_OBJECT(dest, block_weight_limit, info.block_weight_limit);
   INSERT_INTO_JSON_OBJECT(dest, block_size_median, info.block_size_median);
   INSERT_INTO_JSON_OBJECT(dest, block_weight_median, info.block_weight_median);
   INSERT_INTO_JSON_OBJECT(dest, adjusted_time, info.adjusted_time);
   INSERT_INTO_JSON_OBJECT(dest, start_time, info.start_time);
+  INSERT_INTO_JSON_OBJECT(dest, version, info.version);
 
   dest.EndObject();
 }
@@ -1457,9 +1467,14 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::DaemonInfo& inf
     throw WRONG_TYPE("json object");
   }
 
+  uint64_t difficulty_top64 = 0;
+  uint64_t cumulative_difficulty_top64 = 0;
+
   GET_FROM_JSON_OBJECT(val, info.height, height);
   GET_FROM_JSON_OBJECT(val, info.target_height, target_height);
+  GET_FROM_JSON_OBJECT(val, info.top_block_height, top_block_height);
   GET_FROM_JSON_OBJECT(val, info.difficulty, difficulty);
+  GET_FROM_JSON_OBJECT(val, difficulty_top64, difficulty_top64);
   GET_FROM_JSON_OBJECT(val, info.target, target);
   GET_FROM_JSON_OBJECT(val, info.tx_count, tx_count);
   GET_FROM_JSON_OBJECT(val, info.tx_pool_size, tx_pool_size);
@@ -1474,12 +1489,22 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::DaemonInfo& inf
   GET_FROM_JSON_OBJECT(val, info.nettype, nettype);
   GET_FROM_JSON_OBJECT(val, info.top_block_hash, top_block_hash);
   GET_FROM_JSON_OBJECT(val, info.cumulative_difficulty, cumulative_difficulty);
+  GET_FROM_JSON_OBJECT(val, cumulative_difficulty_top64, cumulative_difficulty_top64);
   GET_FROM_JSON_OBJECT(val, info.block_size_limit, block_size_limit);
   GET_FROM_JSON_OBJECT(val, info.block_weight_limit, block_weight_limit);
   GET_FROM_JSON_OBJECT(val, info.block_size_median, block_size_median);
   GET_FROM_JSON_OBJECT(val, info.block_weight_median, block_weight_median);
   GET_FROM_JSON_OBJECT(val, info.adjusted_time, adjusted_time);
   GET_FROM_JSON_OBJECT(val, info.start_time, start_time);
+  GET_FROM_JSON_OBJECT(val, info.version, version);
+
+  info.wide_difficulty = difficulty_top64;
+  info.wide_difficulty <<= 64;
+  info.wide_difficulty += info.difficulty;
+
+  info.wide_cumulative_difficulty = cumulative_difficulty_top64;
+  info.wide_cumulative_difficulty <<= 64;
+  info.wide_cumulative_difficulty += info.cumulative_difficulty;
 }
 
 void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::rpc::output_distribution& dist)
diff --git a/src/serialization/json_object.h b/src/serialization/json_object.h
index cb45b264d..3dfff3336 100644
--- a/src/serialization/json_object.h
+++ b/src/serialization/json_object.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2016-2023, The Monero Project
+// Copyright (c) 2016-2024, The Monero Project
 // 
 // All rights reserved.
 // 
@@ -281,7 +281,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::error& error);
 void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::rpc::BlockHeaderResponse& response);
 void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::BlockHeaderResponse& response);
 
-void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const rct::rctSig& i);
+void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const rct::rctSig& sig, bool prune);
 void fromJsonValue(const rapidjson::Value& val, rct::rctSig& sig);
 
 void fromJsonValue(const rapidjson::Value& val, rct::ctkey& key);
diff --git a/src/serialization/pair.h b/src/serialization/pair.h
index 888033fb9..a08583d0b 100644
--- a/src/serialization/pair.h
+++ b/src/serialization/pair.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/serialization/serialization.h b/src/serialization/serialization.h
index 33edf3f62..7ee92131b 100644
--- a/src/serialization/serialization.h
+++ b/src/serialization/serialization.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
@@ -50,6 +50,8 @@
 #include <boost/type_traits/integral_constant.hpp>
 #include <boost/mpl/bool.hpp>
 
+#include "common/va_args.h"
+
 /*! \struct is_blob_type / is_blob_forced
  *
  * \brief descriptors for dispatching serialize: whether to take byte-wise copy/store to type
@@ -93,6 +95,15 @@ inline bool do_serialize(Archive &ar, bool &v)
   ar.serialize_blob(&v, sizeof(v));
   return true;
 }
+template <class Archive, class T, typename... Args>
+inline auto do_serialize(Archive &ar, T &v, Args&&... args)
+  -> decltype(do_serialize_object(ar, v, args...), true)
+{
+  ar.begin_object();
+  const bool r = do_serialize_object(ar, v, args...);
+  ar.end_object();
+  return r && ar.good();
+}
 
 /* the following add a trait to a set and define the serialization DSL*/
 
@@ -180,18 +191,9 @@ inline bool do_serialize(Archive &ar, bool &v)
  * VARINT_FIELD_F(). Otherwise, this macro is similar to
  * BEGIN_SERIALIZE_OBJECT(), as you should list only field serializations.
  */
-#define BEGIN_SERIALIZE_OBJECT_FN(stype)               \
-  template <bool W, template <bool> class Archive>     \
-  bool do_serialize_object(Archive<W> &ar, stype &v);  \
-  template <bool W, template <bool> class Archive>     \
-  bool do_serialize(Archive<W> &ar, stype &v) {        \
-    ar.begin_object();                                 \
-    bool r = do_serialize_object(ar, v);               \
-    ar.end_object();                                   \
-    return r;                                          \
-  }                                                    \
-  template <bool W, template <bool> class Archive>     \
-  bool do_serialize_object(Archive<W> &ar, stype &v) { \
+#define BEGIN_SERIALIZE_OBJECT_FN(stype, ...)                                           \
+  template <bool W, template <bool> class Archive>                                      \
+  bool do_serialize_object(Archive<W> &ar, stype &v VA_ARGS_COMMAPREFIX(__VA_ARGS__)) {
 
 /*! \macro PREPARE_CUSTOM_VECTOR_SERIALIZATION
  */
@@ -209,10 +211,10 @@ inline bool do_serialize(Archive &ar, bool &v)
  *
  * \brief serializes a field \a f tagged \a t  
  */
-#define FIELD_N(t, f)					\
+#define FIELD_N(t, f, ...)                                    \
   do {							\
     ar.tag(t);						\
-    bool r = do_serialize(ar, f);			\
+    bool r = do_serialize(ar, f VA_ARGS_COMMAPREFIX(__VA_ARGS__)); \
     if (!r || !ar.good()) return false;			\
   } while(0);
 
@@ -231,7 +233,7 @@ inline bool do_serialize(Archive &ar, bool &v)
  *
  * \brief tags the field with the variable name and then serializes it (for use in a free function)
  */
-#define FIELD_F(f) FIELD_N(#f, v.f)
+#define FIELD_F(f, ...) FIELD_N(#f, v.f VA_ARGS_COMMAPREFIX(__VA_ARGS__))
 
 /*! \macro FIELDS(f)
  *
diff --git a/src/serialization/string.h b/src/serialization/string.h
index a5d68eb64..ec1d3e15b 100644
--- a/src/serialization/string.h
+++ b/src/serialization/string.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/serialization/variant.h b/src/serialization/variant.h
index 1cf93c8cf..0e4e1e7c8 100644
--- a/src/serialization/variant.h
+++ b/src/serialization/variant.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/simplewallet/CMakeLists.txt b/src/simplewallet/CMakeLists.txt
index 9c1d2a339..d4177e44d 100644
--- a/src/simplewallet/CMakeLists.txt
+++ b/src/simplewallet/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index 2f08ac025..153d5f2ac 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
@@ -148,6 +148,17 @@ typedef cryptonote::simple_wallet sw;
     } \
   } while(0)
 
+#define CHECK_IF_BACKGROUND_SYNCING(msg) \
+  do \
+  { \
+    if (m_wallet->is_background_wallet() || m_wallet->is_background_syncing()) \
+    { \
+      std::string type = m_wallet->is_background_wallet() ? "background wallet" : "background syncing wallet"; \
+      fail_msg_writer() << boost::format(tr("%s %s")) % type % msg; \
+      return false; \
+    } \
+  } while (0)
+
 static std::string get_human_readable_timespan(std::chrono::seconds seconds);
 static std::string get_human_readable_timespan(uint64_t seconds);
 
@@ -314,7 +325,7 @@ namespace
     auto pwd_container = tools::password_container::prompt(verify, prompt);
     if (!pwd_container)
     {
-      tools::fail_msg_writer() << sw::tr("failed to read wallet password");
+      tools::fail_msg_writer() << sw::tr("failed to read password");
     }
     return pwd_container;
   }
@@ -324,6 +335,11 @@ namespace
     return password_prompter(verify ? sw::tr("Enter a new password for the wallet") : sw::tr("Wallet password"), verify);
   }
 
+  boost::optional<tools::password_container> background_sync_cache_password_prompter(bool verify)
+  {
+    return password_prompter(verify ? sw::tr("Enter a custom password for the background sync cache") : sw::tr("Background sync cache password"), verify);
+  }
+
   inline std::string interpret_rpc_response(bool ok, const std::string& status)
   {
     std::string err;
@@ -441,6 +457,41 @@ namespace
     return "invalid";
   }
 
+  const struct
+  {
+    const char *name;
+    tools::wallet2::BackgroundSyncType background_sync_type;
+  } background_sync_type_names[] =
+  {
+    { "off", tools::wallet2::BackgroundSyncOff },
+    { "reuse-wallet-password", tools::wallet2::BackgroundSyncReusePassword },
+    { "custom-background-password", tools::wallet2::BackgroundSyncCustomPassword },
+  };
+
+  bool parse_background_sync_type(const std::string &s, tools::wallet2::BackgroundSyncType &background_sync_type)
+  {
+    for (size_t n = 0; n < sizeof(background_sync_type_names) / sizeof(background_sync_type_names[0]); ++n)
+    {
+      if (s == background_sync_type_names[n].name)
+      {
+        background_sync_type = background_sync_type_names[n].background_sync_type;
+        return true;
+      }
+    }
+    fail_msg_writer() << cryptonote::simple_wallet::tr("failed to parse background sync type");
+    return false;
+  }
+
+  std::string get_background_sync_type_name(tools::wallet2::BackgroundSyncType type)
+  {
+    for (size_t n = 0; n < sizeof(background_sync_type_names) / sizeof(background_sync_type_names[0]); ++n)
+    {
+      if (type == background_sync_type_names[n].background_sync_type)
+        return background_sync_type_names[n].name;
+    }
+    return "invalid";
+  }
+
   std::string get_version_string(uint32_t version)
   {
     return boost::lexical_cast<std::string>(version >> 16) + "." + boost::lexical_cast<std::string>(version & 0xffff);
@@ -793,6 +844,7 @@ bool simple_wallet::spendkey(const std::vector<std::string> &args/* = std::vecto
     fail_msg_writer() << tr("wallet is watch-only and has no spend key");
     return true;
   }
+  CHECK_IF_BACKGROUND_SYNCING("has no spend key");
   // don't log
   PAUSE_READLINE();
   if (m_wallet->key_on_device()) {
@@ -823,6 +875,7 @@ bool simple_wallet::print_seed(bool encrypted)
     fail_msg_writer() << tr("wallet is watch-only and has no seed");
     return true;
   }
+  CHECK_IF_BACKGROUND_SYNCING("has no seed");
 
   const multisig::multisig_account_status ms_status{m_wallet->get_multisig_status()};
   if (ms_status.multisig_is_active)
@@ -900,6 +953,7 @@ bool simple_wallet::seed_set_language(const std::vector<std::string> &args/* = s
     fail_msg_writer() << tr("wallet is watch-only and has no seed");
     return true;
   }
+  CHECK_IF_BACKGROUND_SYNCING("has no seed");
 
   epee::wipeable_string password;
   {
@@ -1046,6 +1100,7 @@ bool simple_wallet::prepare_multisig_main(const std::vector<std::string> &args,
     fail_msg_writer() << tr("wallet is watch-only and cannot be made multisig");
     return false;
   }
+  CHECK_IF_BACKGROUND_SYNCING("cannot be made multisig");
 
   if(m_wallet->get_num_transfer_details())
   {
@@ -2105,6 +2160,7 @@ bool simple_wallet::save_known_rings(const std::vector<std::string> &args)
 
 bool simple_wallet::freeze_thaw(const std::vector<std::string> &args, bool freeze)
 {
+  CHECK_IF_BACKGROUND_SYNCING("cannot freeze/thaw");
   if (args.empty())
   {
     fail_msg_writer() << boost::format(tr("usage: %s <key_image>|<pubkey>")) % (freeze ? "freeze" : "thaw");
@@ -2144,6 +2200,7 @@ bool simple_wallet::thaw(const std::vector<std::string> &args)
 
 bool simple_wallet::frozen(const std::vector<std::string> &args)
 {
+  CHECK_IF_BACKGROUND_SYNCING("cannot see frozen key images");
   if (args.empty())
   {
     size_t ntd = m_wallet->get_num_transfer_details();
@@ -2794,6 +2851,57 @@ bool simple_wallet::set_track_uses(const std::vector<std::string> &args/* = std:
   return true;
 }
 
+bool simple_wallet::setup_background_sync(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
+{
+  if (m_wallet->get_multisig_status().multisig_is_active)
+  {
+    fail_msg_writer() << tr("background sync not implemented for multisig wallet");
+    return true;
+  }
+  if (m_wallet->watch_only())
+  {
+    fail_msg_writer() << tr("background sync not implemented for watch only wallet");
+    return true;
+  }
+  if (m_wallet->key_on_device())
+  {
+    fail_msg_writer() << tr("command not supported by HW wallet");
+    return true;
+  }
+
+  tools::wallet2::BackgroundSyncType background_sync_type;
+  if (!parse_background_sync_type(args[1], background_sync_type))
+  {
+    fail_msg_writer() << tr("invalid option");
+    return true;
+  }
+
+  const auto pwd_container = get_and_verify_password();
+  if (!pwd_container)
+    return true;
+
+  try
+  {
+    boost::optional<epee::wipeable_string> background_cache_password = boost::none;
+    if (background_sync_type == tools::wallet2::BackgroundSyncCustomPassword)
+    {
+      const auto background_pwd_container = background_sync_cache_password_prompter(true);
+      if (!background_pwd_container)
+        return true;
+      background_cache_password = background_pwd_container->password();
+    }
+
+    LOCK_IDLE_SCOPE();
+    m_wallet->setup_background_sync(background_sync_type, pwd_container->password(), background_cache_password);
+  }
+  catch (const std::exception &e)
+  {
+    fail_msg_writer() << tr("Error setting background sync type: ") << e.what();
+  }
+
+  return true;
+}
+
 bool simple_wallet::set_show_wallet_name_when_locked(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
 {
   const auto pwd_container = get_and_verify_password();
@@ -3026,6 +3134,7 @@ bool simple_wallet::apropos(const std::vector<std::string> &args)
 
 bool simple_wallet::scan_tx(const std::vector<std::string> &args)
 {
+  CHECK_IF_BACKGROUND_SYNCING("cannot scan tx");
   if (args.empty())
   {
     PRINT_USAGE(USAGE_SCAN_TX);
@@ -3243,6 +3352,8 @@ simple_wallet::simple_wallet()
                                   "  Ignore outputs of amount below this threshold when spending.\n "
                                   "track-uses <1|0>\n "
                                   "  Whether to keep track of owned outputs uses.\n "
+                                  "background-sync <off|reuse-wallet-password|custom-background-password>\n "
+                                  "  Set this to enable scanning in the background with just the view key while the wallet is locked.\n "
                                   "setup-background-mining <1|0>\n "
                                   "  Whether to enable background mining. Set this to support the network and to get a chance to receive new monero.\n "
                                   "device-name <device_name[:device_spec]>\n "
@@ -3645,6 +3756,7 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args)
     success_msg_writer() << "ignore-outputs-above = " << cryptonote::print_money(m_wallet->ignore_outputs_above());
     success_msg_writer() << "ignore-outputs-below = " << cryptonote::print_money(m_wallet->ignore_outputs_below());
     success_msg_writer() << "track-uses = " << m_wallet->track_uses();
+    success_msg_writer() << "background-sync = " << get_background_sync_type_name(m_wallet->background_sync_type());
     success_msg_writer() << "setup-background-mining = " << setup_background_mining_string;
     success_msg_writer() << "device-name = " << m_wallet->device_name();
     success_msg_writer() << "export-format = " << (m_wallet->export_format() == tools::wallet2::ExportFormat::Ascii ? "ascii" : "binary");
@@ -3660,6 +3772,7 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args)
   }
   else
   {
+    CHECK_IF_BACKGROUND_SYNCING("cannot change wallet settings");
 
 #define CHECK_SIMPLE_VARIABLE(name, f, help) do \
   if (args[0] == name) { \
@@ -3713,6 +3826,7 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args)
     CHECK_SIMPLE_VARIABLE("ignore-outputs-above", set_ignore_outputs_above, tr("amount"));
     CHECK_SIMPLE_VARIABLE("ignore-outputs-below", set_ignore_outputs_below, tr("amount"));
     CHECK_SIMPLE_VARIABLE("track-uses", set_track_uses, tr("0 or 1"));
+    CHECK_SIMPLE_VARIABLE("background-sync", setup_background_sync, tr("off (default); reuse-wallet-password (reuse the wallet password to encrypt the background cache); custom-background-password (use a custom background password to encrypt the background cache)"));
     CHECK_SIMPLE_VARIABLE("show-wallet-name-when-locked", set_show_wallet_name_when_locked, tr("1 or 0"));
     CHECK_SIMPLE_VARIABLE("inactivity-lock-timeout", set_inactivity_lock_timeout, tr("unsigned integer (seconds, 0 to disable)"));
     CHECK_SIMPLE_VARIABLE("setup-background-mining", set_setup_background_mining, tr("1/yes or 0/no"));
@@ -4653,7 +4767,10 @@ std::string simple_wallet::get_mnemonic_language()
 //----------------------------------------------------------------------------------------------------
 boost::optional<tools::password_container> simple_wallet::get_and_verify_password() const
 {
-  auto pwd_container = default_password_prompter(m_wallet_file.empty());
+  const bool verify = m_wallet_file.empty();
+  auto pwd_container = (m_wallet->is_background_wallet() && m_wallet->background_sync_type() == tools::wallet2::BackgroundSyncCustomPassword)
+    ? background_sync_cache_password_prompter(verify)
+    : default_password_prompter(verify);
   if (!pwd_container)
     return boost::none;
 
@@ -4956,6 +5073,8 @@ boost::optional<epee::wipeable_string> simple_wallet::open_wallet(const boost::p
       prefix = tr("Opened watch-only wallet");
     else if (ms_status.multisig_is_active)
       prefix = (boost::format(tr("Opened %u/%u multisig wallet%s")) % ms_status.threshold % ms_status.total % (ms_status.is_ready ? "" : " (not yet finalized)")).str();
+    else if (m_wallet->is_background_wallet())
+      prefix = tr("Opened background wallet");
     else
       prefix = tr("Opened wallet");
     message_writer(console_color_white, true) <<
@@ -5163,6 +5282,10 @@ void simple_wallet::stop_background_mining()
 //----------------------------------------------------------------------------------------------------
 void simple_wallet::check_background_mining(const epee::wipeable_string &password)
 {
+  // Background mining can be toggled from the main wallet
+  if (m_wallet->is_background_wallet() || m_wallet->is_background_syncing())
+    return;
+
   tools::wallet2::BackgroundMiningSetupType setup = m_wallet->setup_background_mining();
   if (setup == tools::wallet2::BackgroundMiningNo)
   {
@@ -5978,6 +6101,7 @@ bool simple_wallet::show_blockchain_height(const std::vector<std::string>& args)
 //----------------------------------------------------------------------------------------------------
 bool simple_wallet::rescan_spent(const std::vector<std::string> &args)
 {
+  CHECK_IF_BACKGROUND_SYNCING("cannot rescan spent");
   if (!m_wallet->is_trusted_daemon())
   {
     fail_msg_writer() << tr("this command requires a trusted daemon. Enable with --trusted-daemon");
@@ -6233,10 +6357,27 @@ void simple_wallet::check_for_inactivity_lock(bool user)
           "                ||     ||" << std::endl <<
           "" << std::endl;
     }
+
+    bool started_background_sync = false;
+    if (!m_wallet->is_background_wallet() &&
+        m_wallet->background_sync_type() != tools::wallet2::BackgroundSyncOff)
+    {
+      LOCK_IDLE_SCOPE();
+      m_wallet->start_background_sync();
+      started_background_sync = true;
+    }
+
     while (1)
     {
       const char *inactivity_msg = user ? "" : tr("Locked due to inactivity.");
-      tools::msg_writer() << inactivity_msg << (inactivity_msg[0] ? " " : "") << tr("The wallet password is required to unlock the console.");
+      tools::msg_writer() << inactivity_msg << (inactivity_msg[0] ? " " : "") << (
+        (m_wallet->is_background_wallet() && m_wallet->background_sync_type() == tools::wallet2::BackgroundSyncCustomPassword)
+            ? tr("The background password is required to unlock the console.")
+            : tr("The wallet password is required to unlock the console.")
+      );
+
+      if (m_wallet->is_background_syncing())
+        tools::msg_writer() << tr("\nSyncing in the background while locked...") << std::endl;
 
       const bool show_wallet_name = m_wallet->show_wallet_name_when_locked();
       if (show_wallet_name)
@@ -6249,8 +6390,16 @@ void simple_wallet::check_for_inactivity_lock(bool user)
       }
       try
       {
-        if (get_and_verify_password())
+        const auto pwd_container = get_and_verify_password();
+        if (pwd_container)
+        {
+          if (started_background_sync)
+          {
+            LOCK_IDLE_SCOPE();
+            m_wallet->stop_background_sync(pwd_container->password());
+          }
           break;
+        }
       }
       catch (...) { /* do nothing, just let the loop loop */ }
     }
@@ -6277,6 +6426,7 @@ bool simple_wallet::on_command(bool (simple_wallet::*cmd)(const std::vector<std:
 bool simple_wallet::transfer_main(const std::vector<std::string> &args_, bool called_by_mms)
 {
 //  "transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> <amount> [<payment_id>]"
+  CHECK_IF_BACKGROUND_SYNCING("cannot transfer");
   if (!try_connect_to_daemon())
     return false;
 
@@ -6690,6 +6840,7 @@ bool simple_wallet::transfer_main(const std::vector<std::string> &args_, bool ca
 //----------------------------------------------------------------------------------------------------
 bool simple_wallet::transfer(const std::vector<std::string> &args_)
 {
+  CHECK_IF_BACKGROUND_SYNCING("cannot transfer");
   if (args_.size() < 1)
   {
     PRINT_USAGE(USAGE_TRANSFER);
@@ -6702,6 +6853,7 @@ bool simple_wallet::transfer(const std::vector<std::string> &args_)
 
 bool simple_wallet::sweep_unmixable(const std::vector<std::string> &args_)
 {
+  CHECK_IF_BACKGROUND_SYNCING("cannot sweep");
   if (!try_connect_to_daemon())
     return true;
 
@@ -6809,6 +6961,7 @@ bool simple_wallet::sweep_unmixable(const std::vector<std::string> &args_)
 //----------------------------------------------------------------------------------------------------
 bool simple_wallet::sweep_main(uint32_t account, uint64_t below, const std::vector<std::string> &args_)
 {
+  CHECK_IF_BACKGROUND_SYNCING("cannot sweep");
   auto print_usage = [this, account, below]()
   {
     if (below)
@@ -7090,6 +7243,7 @@ bool simple_wallet::sweep_main(uint32_t account, uint64_t below, const std::vect
 //----------------------------------------------------------------------------------------------------
 bool simple_wallet::sweep_single(const std::vector<std::string> &args_)
 {
+  CHECK_IF_BACKGROUND_SYNCING("cannot sweep");
   if (!try_connect_to_daemon())
     return true;
 
@@ -7328,12 +7482,14 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_)
 //----------------------------------------------------------------------------------------------------
 bool simple_wallet::sweep_all(const std::vector<std::string> &args_)
 {
+  CHECK_IF_BACKGROUND_SYNCING("cannot sweep");
   sweep_main(m_current_subaddress_account, 0, args_);
   return true;
 }
 //----------------------------------------------------------------------------------------------------
 bool simple_wallet::sweep_account(const std::vector<std::string> &args_)
 {
+  CHECK_IF_BACKGROUND_SYNCING("cannot sweep");
   auto local_args = args_;
   if (local_args.empty())
   {
@@ -7354,6 +7510,7 @@ bool simple_wallet::sweep_account(const std::vector<std::string> &args_)
 //----------------------------------------------------------------------------------------------------
 bool simple_wallet::sweep_below(const std::vector<std::string> &args_)
 {
+  CHECK_IF_BACKGROUND_SYNCING("cannot sweep");
   uint64_t below = 0;
   if (args_.size() < 1)
   {
@@ -7372,6 +7529,7 @@ bool simple_wallet::sweep_below(const std::vector<std::string> &args_)
 //----------------------------------------------------------------------------------------------------
 bool simple_wallet::donate(const std::vector<std::string> &args_)
 {
+  CHECK_IF_BACKGROUND_SYNCING("cannot donate");
   std::vector<std::string> local_args = args_;
   if(local_args.empty() || local_args.size() > 5)
   {
@@ -7433,6 +7591,7 @@ bool simple_wallet::donate(const std::vector<std::string> &args_)
 //----------------------------------------------------------------------------------------------------
 bool simple_wallet::accept_loaded_tx(const std::function<size_t()> get_num_txes, const std::function<const tools::wallet2::tx_construction_data&(size_t)> &get_tx, const std::string &extra_message)
 {
+  CHECK_IF_BACKGROUND_SYNCING("cannot load tx");
   // gather info to ask the user
   uint64_t amount = 0, amount_to_dests = 0, change = 0;
   size_t min_ring_size = ~0;
@@ -7613,6 +7772,7 @@ bool simple_wallet::sign_transfer(const std::vector<std::string> &args_)
      fail_msg_writer() << tr("This is a watch only wallet");
      return true;
   }
+  CHECK_IF_BACKGROUND_SYNCING("cannot sign transfer");
 
   bool export_raw = false;
   std::string unsigned_filename = "unsigned_monero_tx";
@@ -7720,6 +7880,8 @@ std::string get_tx_key_stream(crypto::secret_key tx_key, std::vector<crypto::sec
 
 bool simple_wallet::get_tx_key(const std::vector<std::string> &args_)
 {
+  CHECK_IF_BACKGROUND_SYNCING("cannot get tx key");
+
   std::vector<std::string> local_args = args_;
 
   if (m_wallet->key_on_device() && m_wallet->get_account().get_device().get_type() != hw::device::TREZOR)
@@ -7760,6 +7922,8 @@ bool simple_wallet::get_tx_key(const std::vector<std::string> &args_)
 //----------------------------------------------------------------------------------------------------
 bool simple_wallet::set_tx_key(const std::vector<std::string> &args_)
 {
+  CHECK_IF_BACKGROUND_SYNCING("cannot set tx key");
+
   std::vector<std::string> local_args = args_;
 
   if(local_args.size() != 2 && local_args.size() != 3) {
@@ -7836,6 +8000,8 @@ bool simple_wallet::set_tx_key(const std::vector<std::string> &args_)
 //----------------------------------------------------------------------------------------------------
 bool simple_wallet::get_tx_proof(const std::vector<std::string> &args)
 {
+  CHECK_IF_BACKGROUND_SYNCING("cannot get tx proof");
+
   if (args.size() != 2 && args.size() != 3)
   {
     PRINT_USAGE(USAGE_GET_TX_PROOF);
@@ -8042,6 +8208,7 @@ bool simple_wallet::check_tx_proof(const std::vector<std::string> &args)
 //----------------------------------------------------------------------------------------------------
 bool simple_wallet::get_spend_proof(const std::vector<std::string> &args)
 {
+  CHECK_IF_BACKGROUND_SYNCING("cannot get spend proof");
   if (m_wallet->key_on_device())
   {
     fail_msg_writer() << tr("command not supported by HW wallet");
@@ -8126,6 +8293,7 @@ bool simple_wallet::check_spend_proof(const std::vector<std::string> &args)
 //----------------------------------------------------------------------------------------------------
 bool simple_wallet::get_reserve_proof(const std::vector<std::string> &args)
 {
+  CHECK_IF_BACKGROUND_SYNCING("cannot get reserve proof");
   if (m_wallet->key_on_device())
   {
     fail_msg_writer() << tr("command not supported by HW wallet");
@@ -8721,6 +8889,7 @@ bool simple_wallet::unspent_outputs(const std::vector<std::string> &args_)
   uint64_t max_height = 0;
   uint64_t found_min_amount = std::numeric_limits<uint64_t>::max();
   uint64_t found_max_amount = 0;
+  uint64_t found_sum_amount = 0;
   uint64_t count = 0;
   for (const auto& td : transfers)
   {
@@ -8732,6 +8901,7 @@ bool simple_wallet::unspent_outputs(const std::vector<std::string> &args_)
     if (max_height < td.m_block_height) max_height = td.m_block_height;
     if (found_min_amount > amount) found_min_amount = amount;
     if (found_max_amount < amount) found_max_amount = amount;
+    found_sum_amount += amount;
     ++count;
   }
   if (amount_to_tds.empty())
@@ -8756,6 +8926,7 @@ bool simple_wallet::unspent_outputs(const std::vector<std::string> &args_)
     << tr("\nMax block height: ") << max_height
     << tr("\nMin amount found: ") << print_money(found_min_amount)
     << tr("\nMax amount found: ") << print_money(found_max_amount)
+    << tr("\nSum amount found: ") << print_money(found_sum_amount)
     << tr("\nTotal count: ") << count;
   const size_t histogram_height = 10;
   const size_t histogram_width  = 50;
@@ -8812,6 +8983,8 @@ bool simple_wallet::unspent_outputs(const std::vector<std::string> &args_)
 //----------------------------------------------------------------------------------------------------
 bool simple_wallet::rescan_blockchain(const std::vector<std::string> &args_)
 {
+  CHECK_IF_BACKGROUND_SYNCING("cannot rescan");
+
   uint64_t start_height = 0;
   ResetType reset_type = ResetSoft;
 
@@ -9036,6 +9209,7 @@ bool simple_wallet::account(const std::vector<std::string> &args/* = std::vector
   if (command == "new")
   {
     // create a new account and switch to it
+    CHECK_IF_BACKGROUND_SYNCING("cannot create new account");
     std::string label = boost::join(local_args, " ");
     if (label.empty())
       label = tr("(Untitled account)");
@@ -9066,6 +9240,7 @@ bool simple_wallet::account(const std::vector<std::string> &args/* = std::vector
   else if (command == "label" && local_args.size() >= 1)
   {
     // set label of the specified account
+    CHECK_IF_BACKGROUND_SYNCING("cannot modify account");
     uint32_t index_major;
     if (!epee::string_tools::get_xtype_from_string(index_major, local_args[0]))
     {
@@ -9087,6 +9262,7 @@ bool simple_wallet::account(const std::vector<std::string> &args/* = std::vector
   }
   else if (command == "tag" && local_args.size() >= 2)
   {
+    CHECK_IF_BACKGROUND_SYNCING("cannot modify account");
     const std::string tag = local_args[0];
     std::set<uint32_t> account_indices;
     for (size_t i = 1; i < local_args.size(); ++i)
@@ -9111,6 +9287,7 @@ bool simple_wallet::account(const std::vector<std::string> &args/* = std::vector
   }
   else if (command == "untag" && local_args.size() >= 1)
   {
+    CHECK_IF_BACKGROUND_SYNCING("cannot modify account");
     std::set<uint32_t> account_indices;
     for (size_t i = 0; i < local_args.size(); ++i)
     {
@@ -9134,6 +9311,7 @@ bool simple_wallet::account(const std::vector<std::string> &args/* = std::vector
   }
   else if (command == "tag_description" && local_args.size() >= 1)
   {
+    CHECK_IF_BACKGROUND_SYNCING("cannot modify account");
     const std::string tag = local_args[0];
     std::string description;
     if (local_args.size() > 1)
@@ -9251,6 +9429,7 @@ bool simple_wallet::print_address(const std::vector<std::string> &args/* = std::
   }
   else if (local_args[0] == "new")
   {
+    CHECK_IF_BACKGROUND_SYNCING("cannot add address");
     local_args.erase(local_args.begin());
     std::string label;
     if (local_args.size() > 0)
@@ -9263,6 +9442,7 @@ bool simple_wallet::print_address(const std::vector<std::string> &args/* = std::
   }
   else if (local_args[0] == "mnew")
   {
+    CHECK_IF_BACKGROUND_SYNCING("cannot add addresses");
     local_args.erase(local_args.begin());
     if (local_args.size() != 1)
     {
@@ -9288,6 +9468,7 @@ bool simple_wallet::print_address(const std::vector<std::string> &args/* = std::
   }
   else if (local_args[0] == "one-off")
   {
+    CHECK_IF_BACKGROUND_SYNCING("cannot add address");
     local_args.erase(local_args.begin());
     std::string label;
     if (local_args.size() != 2)
@@ -9306,6 +9487,7 @@ bool simple_wallet::print_address(const std::vector<std::string> &args/* = std::
   }
   else if (local_args.size() >= 2 && local_args[0] == "label")
   {
+    CHECK_IF_BACKGROUND_SYNCING("cannot modify address");
     if (!epee::string_tools::get_xtype_from_string(index, local_args[1]))
     {
       fail_msg_writer() << tr("failed to parse index: ") << local_args[1];
@@ -9452,6 +9634,8 @@ bool simple_wallet::print_integrated_address(const std::vector<std::string> &arg
 //----------------------------------------------------------------------------------------------------
 bool simple_wallet::address_book(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
 {
+  CHECK_IF_BACKGROUND_SYNCING("cannot get address book");
+
   if (args.size() == 0)
   {
   }
@@ -9512,6 +9696,8 @@ bool simple_wallet::address_book(const std::vector<std::string> &args/* = std::v
 //----------------------------------------------------------------------------------------------------
 bool simple_wallet::set_tx_note(const std::vector<std::string> &args)
 {
+  CHECK_IF_BACKGROUND_SYNCING("cannot set tx note");
+
   if (args.size() == 0)
   {
     PRINT_USAGE(USAGE_SET_TX_NOTE);
@@ -9540,6 +9726,8 @@ bool simple_wallet::set_tx_note(const std::vector<std::string> &args)
 //----------------------------------------------------------------------------------------------------
 bool simple_wallet::get_tx_note(const std::vector<std::string> &args)
 {
+  CHECK_IF_BACKGROUND_SYNCING("cannot get tx note");
+
   if (args.size() != 1)
   {
     PRINT_USAGE(USAGE_GET_TX_NOTE);
@@ -9565,6 +9753,8 @@ bool simple_wallet::get_tx_note(const std::vector<std::string> &args)
 //----------------------------------------------------------------------------------------------------
 bool simple_wallet::set_description(const std::vector<std::string> &args)
 {
+  CHECK_IF_BACKGROUND_SYNCING("cannot set description");
+
   // 0 arguments allowed, for setting the description to empty string
 
   std::string description = "";
@@ -9581,6 +9771,8 @@ bool simple_wallet::set_description(const std::vector<std::string> &args)
 //----------------------------------------------------------------------------------------------------
 bool simple_wallet::get_description(const std::vector<std::string> &args)
 {
+  CHECK_IF_BACKGROUND_SYNCING("cannot get description");
+
   if (args.size() != 0)
   {
     PRINT_USAGE(USAGE_GET_DESCRIPTION);
@@ -9639,6 +9831,8 @@ bool simple_wallet::wallet_info(const std::vector<std::string> &args)
     type = tr("Watch only");
   else if (ms_status.multisig_is_active)
     type = (boost::format(tr("%u/%u multisig%s")) % ms_status.threshold % ms_status.total % (ms_status.is_ready ? "" : " (not yet finalized)")).str();
+  else if (m_wallet->is_background_wallet())
+    type = tr("Background wallet");
   else
     type = tr("Normal");
   message_writer() << tr("Type: ") << type;
@@ -9650,6 +9844,7 @@ bool simple_wallet::wallet_info(const std::vector<std::string> &args)
 //----------------------------------------------------------------------------------------------------
 bool simple_wallet::sign(const std::vector<std::string> &args)
 {
+  CHECK_IF_BACKGROUND_SYNCING("cannot sign");
   if (m_wallet->key_on_device())
   {
     fail_msg_writer() << tr("command not supported by HW wallet");
@@ -9757,6 +9952,7 @@ bool simple_wallet::export_key_images(const std::vector<std::string> &args_)
     fail_msg_writer() << tr("command not supported by HW wallet");
     return true;
   }
+  CHECK_IF_BACKGROUND_SYNCING("cannot export key images");
   auto args = args_;
 
   if (m_wallet->watch_only())
@@ -9810,6 +10006,7 @@ bool simple_wallet::import_key_images(const std::vector<std::string> &args)
     fail_msg_writer() << tr("command not supported by HW wallet");
     return true;
   }
+  CHECK_IF_BACKGROUND_SYNCING("cannot import key images");
   if (!m_wallet->is_trusted_daemon())
   {
     fail_msg_writer() << tr("this command requires a trusted daemon. Enable with --trusted-daemon");
@@ -9918,6 +10115,7 @@ bool simple_wallet::export_outputs(const std::vector<std::string> &args_)
     fail_msg_writer() << tr("command not supported by HW wallet");
     return true;
   }
+  CHECK_IF_BACKGROUND_SYNCING("cannot export outputs");
   auto args = args_;
 
   bool all = false;
@@ -9967,6 +10165,7 @@ bool simple_wallet::import_outputs(const std::vector<std::string> &args)
     fail_msg_writer() << tr("command not supported by HW wallet");
     return true;
   }
+  CHECK_IF_BACKGROUND_SYNCING("cannot import outputs");
   if (args.size() != 1)
   {
     PRINT_USAGE(USAGE_IMPORT_OUTPUTS);
diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h
index 11b2be342..76ece1b38 100644
--- a/src/simplewallet/simplewallet.h
+++ b/src/simplewallet/simplewallet.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
@@ -147,6 +147,7 @@ namespace cryptonote
     bool set_ignore_outputs_above(const std::vector<std::string> &args = std::vector<std::string>());
     bool set_ignore_outputs_below(const std::vector<std::string> &args = std::vector<std::string>());
     bool set_track_uses(const std::vector<std::string> &args = std::vector<std::string>());
+    bool setup_background_sync(const std::vector<std::string> &args = std::vector<std::string>());
     bool set_show_wallet_name_when_locked(const std::vector<std::string> &args = std::vector<std::string>());
     bool set_inactivity_lock_timeout(const std::vector<std::string> &args = std::vector<std::string>());
     bool set_setup_background_mining(const std::vector<std::string> &args = std::vector<std::string>());
diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt
index 48c7f1c5b..b976c02d7 100644
--- a/src/wallet/CMakeLists.txt
+++ b/src/wallet/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/src/wallet/api/CMakeLists.txt b/src/wallet/api/CMakeLists.txt
index 35ce5144b..3428de27a 100644
--- a/src/wallet/api/CMakeLists.txt
+++ b/src/wallet/api/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/src/wallet/api/address_book.cpp b/src/wallet/api/address_book.cpp
index aeffe921e..0ccaef6e5 100644
--- a/src/wallet/api/address_book.cpp
+++ b/src/wallet/api/address_book.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/wallet/api/address_book.h b/src/wallet/api/address_book.h
index 8de7f95ff..39bf320de 100644
--- a/src/wallet/api/address_book.h
+++ b/src/wallet/api/address_book.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/wallet/api/pending_transaction.cpp b/src/wallet/api/pending_transaction.cpp
index 47eb7a243..9783da5bc 100644
--- a/src/wallet/api/pending_transaction.cpp
+++ b/src/wallet/api/pending_transaction.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/wallet/api/pending_transaction.h b/src/wallet/api/pending_transaction.h
index ea2831b25..9d8d754c0 100644
--- a/src/wallet/api/pending_transaction.h
+++ b/src/wallet/api/pending_transaction.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/wallet/api/subaddress.cpp b/src/wallet/api/subaddress.cpp
index 7a1460c8e..ad6b6cf6c 100644
--- a/src/wallet/api/subaddress.cpp
+++ b/src/wallet/api/subaddress.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/wallet/api/subaddress.h b/src/wallet/api/subaddress.h
index bcee10577..da4b3eb74 100644
--- a/src/wallet/api/subaddress.h
+++ b/src/wallet/api/subaddress.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/wallet/api/subaddress_account.cpp b/src/wallet/api/subaddress_account.cpp
index ebee80e7e..73d6f7d1e 100644
--- a/src/wallet/api/subaddress_account.cpp
+++ b/src/wallet/api/subaddress_account.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/wallet/api/subaddress_account.h b/src/wallet/api/subaddress_account.h
index 3934df3ef..cb420357a 100644
--- a/src/wallet/api/subaddress_account.h
+++ b/src/wallet/api/subaddress_account.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/wallet/api/transaction_history.cpp b/src/wallet/api/transaction_history.cpp
index ad797cc07..4dc869d5c 100644
--- a/src/wallet/api/transaction_history.cpp
+++ b/src/wallet/api/transaction_history.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/wallet/api/transaction_history.h b/src/wallet/api/transaction_history.h
index 04bc8a705..38d9882f7 100644
--- a/src/wallet/api/transaction_history.h
+++ b/src/wallet/api/transaction_history.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/wallet/api/transaction_info.cpp b/src/wallet/api/transaction_info.cpp
index 8f5ee39a0..909c77086 100644
--- a/src/wallet/api/transaction_info.cpp
+++ b/src/wallet/api/transaction_info.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/wallet/api/transaction_info.h b/src/wallet/api/transaction_info.h
index 33dc8a7f4..0510fb216 100644
--- a/src/wallet/api/transaction_info.h
+++ b/src/wallet/api/transaction_info.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/wallet/api/unsigned_transaction.cpp b/src/wallet/api/unsigned_transaction.cpp
index 07cf93f59..c549539e5 100644
--- a/src/wallet/api/unsigned_transaction.cpp
+++ b/src/wallet/api/unsigned_transaction.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/wallet/api/unsigned_transaction.h b/src/wallet/api/unsigned_transaction.h
index 4fd1a0b28..b07d43fb1 100644
--- a/src/wallet/api/unsigned_transaction.h
+++ b/src/wallet/api/unsigned_transaction.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/wallet/api/utils.cpp b/src/wallet/api/utils.cpp
index d02fdcaf6..1bef7e949 100644
--- a/src/wallet/api/utils.cpp
+++ b/src/wallet/api/utils.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp
index 58cb84947..c8257919d 100644
--- a/src/wallet/api/wallet.cpp
+++ b/src/wallet/api/wallet.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
@@ -58,6 +58,40 @@ using namespace cryptonote;
 #undef MONERO_DEFAULT_LOG_CATEGORY
 #define MONERO_DEFAULT_LOG_CATEGORY "WalletAPI"
 
+#define LOCK_REFRESH() \
+    bool refresh_enabled = m_refreshEnabled; \
+    m_refreshEnabled = false; \
+    m_wallet->stop(); \
+    m_refreshCV.notify_one(); \
+    boost::mutex::scoped_lock lock(m_refreshMutex); \
+    boost::mutex::scoped_lock lock2(m_refreshMutex2); \
+    epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){ \
+        /* m_refreshMutex's still locked here */ \
+        if (refresh_enabled) \
+            startRefresh(); \
+    })
+
+#define PRE_VALIDATE_BACKGROUND_SYNC() \
+  do \
+  { \
+    clearStatus(); \
+    if (m_wallet->key_on_device()) \
+    { \
+        setStatusError(tr("HW wallet cannot use background sync")); \
+        return false; \
+    } \
+    if (m_wallet->watch_only()) \
+    { \
+        setStatusError(tr("View only wallet cannot use background sync")); \
+        return false; \
+    } \
+    if (m_wallet->get_multisig_status().multisig_is_active) \
+    { \
+        setStatusError(tr("Multisig wallet cannot use background sync")); \
+        return false; \
+    } \
+  } while (0)
+
 namespace Monero {
 
 namespace {
@@ -766,6 +800,8 @@ bool WalletImpl::close(bool store)
 
 std::string WalletImpl::seed(const std::string& seed_offset) const
 {
+    if (checkBackgroundSync("cannot get seed"))
+        return std::string();
     epee::wipeable_string seed;
     if (m_wallet)
         m_wallet->get_seed(seed, seed_offset);
@@ -779,6 +815,8 @@ std::string WalletImpl::getSeedLanguage() const
 
 void WalletImpl::setSeedLanguage(const std::string &arg)
 {
+    if (checkBackgroundSync("cannot set seed language"))
+        return;
     m_wallet->set_seed_language(arg);
 }
 
@@ -802,6 +840,8 @@ void WalletImpl::statusWithErrorString(int& status, std::string& errorString) co
 
 bool WalletImpl::setPassword(const std::string &password)
 {
+    if (checkBackgroundSync("cannot change password"))
+        return false;
     clearStatus();
     try {
         m_wallet->change_password(m_wallet->get_wallet_file(), m_password, password);
@@ -931,6 +971,8 @@ bool WalletImpl::init(const std::string &daemon_address, uint64_t upper_transact
 
 void WalletImpl::setRefreshFromBlockHeight(uint64_t refresh_from_block_height)
 {
+    if (checkBackgroundSync("cannot change refresh height"))
+        return;
     m_wallet->set_refresh_from_block_height(refresh_from_block_height);
 }
 
@@ -1039,6 +1081,8 @@ void WalletImpl::refreshAsync()
 
 bool WalletImpl::rescanBlockchain()
 {
+    if (checkBackgroundSync("cannot rescan blockchain"))
+        return false;
     clearStatus();
     m_refreshShouldRescan = true;
     doRefresh();
@@ -1047,6 +1091,8 @@ bool WalletImpl::rescanBlockchain()
 
 void WalletImpl::rescanBlockchainAsync()
 {
+    if (checkBackgroundSync("cannot rescan blockchain"))
+        return;
     m_refreshShouldRescan = true;
     refreshAsync();
 }
@@ -1070,7 +1116,7 @@ int WalletImpl::autoRefreshInterval() const
 UnsignedTransaction *WalletImpl::loadUnsignedTx(const std::string &unsigned_filename) {
   clearStatus();
   UnsignedTransactionImpl * transaction = new UnsignedTransactionImpl(*this);
-  if (!m_wallet->load_unsigned_tx(unsigned_filename, transaction->m_unsigned_tx_set)){
+  if (checkBackgroundSync("cannot load tx") || !m_wallet->load_unsigned_tx(unsigned_filename, transaction->m_unsigned_tx_set)){
     setStatusError(tr("Failed to load unsigned transactions"));
     transaction->m_status = UnsignedTransaction::Status::Status_Error;
     transaction->m_errorString = errorString();
@@ -1090,6 +1136,8 @@ UnsignedTransaction *WalletImpl::loadUnsignedTx(const std::string &unsigned_file
 
 bool WalletImpl::submitTransaction(const string &fileName) {
   clearStatus();
+  if (checkBackgroundSync("cannot submit tx"))
+    return false;
   std::unique_ptr<PendingTransactionImpl> transaction(new PendingTransactionImpl(*this));
 
   bool r = m_wallet->load_tx(fileName, transaction->m_pending_tx);
@@ -1113,6 +1161,8 @@ bool WalletImpl::exportKeyImages(const string &filename, bool all)
     setStatusError(tr("Wallet is view only"));
     return false;
   }
+  if (checkBackgroundSync("cannot export key images"))
+    return false;
   
   try
   {
@@ -1133,6 +1183,8 @@ bool WalletImpl::exportKeyImages(const string &filename, bool all)
 
 bool WalletImpl::importKeyImages(const string &filename)
 {
+  if (checkBackgroundSync("cannot import key images"))
+    return false;
   if (!trustedDaemon()) {
     setStatusError(tr("Key images can only be imported with a trusted daemon"));
     return false;
@@ -1156,6 +1208,8 @@ bool WalletImpl::importKeyImages(const string &filename)
 
 bool WalletImpl::exportOutputs(const string &filename, bool all)
 {
+    if (checkBackgroundSync("cannot export outputs"))
+        return false;
     if (m_wallet->key_on_device())
     {
         setStatusError(string(tr("Not supported on HW wallets.")) + filename);
@@ -1186,6 +1240,8 @@ bool WalletImpl::exportOutputs(const string &filename, bool all)
 
 bool WalletImpl::importOutputs(const string &filename)
 {
+    if (checkBackgroundSync("cannot import outputs"))
+        return false;
     if (m_wallet->key_on_device())
     {
         setStatusError(string(tr("Not supported on HW wallets.")) + filename);
@@ -1218,6 +1274,8 @@ bool WalletImpl::importOutputs(const string &filename)
 
 bool WalletImpl::scanTransactions(const std::vector<std::string> &txids)
 {
+    if (checkBackgroundSync("cannot scan transactions"))
+        return false;
     if (txids.empty())
     {
         setStatusError(string(tr("Failed to scan transactions: no transaction ids provided.")));
@@ -1256,8 +1314,86 @@ bool WalletImpl::scanTransactions(const std::vector<std::string> &txids)
     return true;
 }
 
+bool WalletImpl::setupBackgroundSync(const Wallet::BackgroundSyncType background_sync_type, const std::string &wallet_password, const optional<std::string> &background_cache_password)
+{
+    try
+    {
+        PRE_VALIDATE_BACKGROUND_SYNC();
+
+        tools::wallet2::BackgroundSyncType bgs_type;
+        switch (background_sync_type)
+        {
+            case Wallet::BackgroundSync_Off: bgs_type = tools::wallet2::BackgroundSyncOff; break;
+            case Wallet::BackgroundSync_ReusePassword: bgs_type = tools::wallet2::BackgroundSyncReusePassword; break;
+            case Wallet::BackgroundSync_CustomPassword: bgs_type = tools::wallet2::BackgroundSyncCustomPassword; break;
+            default: setStatusError(tr("Unknown background sync type")); return false;
+        }
+
+        boost::optional<epee::wipeable_string> bgc_password = background_cache_password
+            ? boost::optional<epee::wipeable_string>(*background_cache_password)
+            : boost::none;
+
+        LOCK_REFRESH();
+        m_wallet->setup_background_sync(bgs_type, wallet_password, bgc_password);
+    }
+    catch (const std::exception &e)
+    {
+        LOG_ERROR("Failed to setup background sync: " << e.what());
+        setStatusError(string(tr("Failed to setup background sync: ")) + e.what());
+        return false;
+    }
+    return true;
+}
+
+Wallet::BackgroundSyncType WalletImpl::getBackgroundSyncType() const
+{
+    switch (m_wallet->background_sync_type())
+    {
+        case tools::wallet2::BackgroundSyncOff: return Wallet::BackgroundSync_Off;
+        case tools::wallet2::BackgroundSyncReusePassword: return Wallet::BackgroundSync_ReusePassword;
+        case tools::wallet2::BackgroundSyncCustomPassword: return Wallet::BackgroundSync_CustomPassword;
+        default: setStatusError(tr("Unknown background sync type")); return Wallet::BackgroundSync_Off;
+    }
+}
+
+bool WalletImpl::startBackgroundSync()
+{
+    try
+    {
+        PRE_VALIDATE_BACKGROUND_SYNC();
+        LOCK_REFRESH();
+        m_wallet->start_background_sync();
+    }
+    catch (const std::exception &e)
+    {
+        LOG_ERROR("Failed to start background sync: " << e.what());
+        setStatusError(string(tr("Failed to start background sync: ")) + e.what());
+        return false;
+    }
+    return true;
+}
+
+bool WalletImpl::stopBackgroundSync(const std::string &wallet_password)
+{
+    try
+    {
+        PRE_VALIDATE_BACKGROUND_SYNC();
+        LOCK_REFRESH();
+        m_wallet->stop_background_sync(epee::wipeable_string(wallet_password));
+    }
+    catch (const std::exception &e)
+    {
+        LOG_ERROR("Failed to stop background sync: " << e.what());
+        setStatusError(string(tr("Failed to stop background sync: ")) + e.what());
+        return false;
+    }
+    return true;
+}
+
 void WalletImpl::addSubaddressAccount(const std::string& label)
 {
+    if (checkBackgroundSync("cannot add account"))
+        return;
     m_wallet->add_subaddress_account(label);
 }
 size_t WalletImpl::numSubaddressAccounts() const
@@ -1270,10 +1406,14 @@ size_t WalletImpl::numSubaddresses(uint32_t accountIndex) const
 }
 void WalletImpl::addSubaddress(uint32_t accountIndex, const std::string& label)
 {
+    if (checkBackgroundSync("cannot add subbaddress"))
+        return;
     m_wallet->add_subaddress(accountIndex, label);
 }
 std::string WalletImpl::getSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex) const
 {
+    if (checkBackgroundSync("cannot get subbaddress label"))
+        return "";
     try
     {
         return m_wallet->get_subaddress_label({accountIndex, addressIndex});
@@ -1287,6 +1427,8 @@ std::string WalletImpl::getSubaddressLabel(uint32_t accountIndex, uint32_t addre
 }
 void WalletImpl::setSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex, const std::string &label)
 {
+    if (checkBackgroundSync("cannot set subbaddress label"))
+        return;
     try
     {
         return m_wallet->set_subaddress_label({accountIndex, addressIndex}, label);
@@ -1300,6 +1442,9 @@ void WalletImpl::setSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex
 
 MultisigState WalletImpl::multisig() const {
     MultisigState state;
+    if (checkBackgroundSync("cannot use multisig"))
+        return state;
+
     const multisig::multisig_account_status ms_status{m_wallet->get_multisig_status()};
 
     state.isMultisig = ms_status.multisig_is_active;
@@ -1312,6 +1457,8 @@ MultisigState WalletImpl::multisig() const {
 }
 
 string WalletImpl::getMultisigInfo() const {
+    if (checkBackgroundSync("cannot use multisig"))
+        return string();
     try {
         clearStatus();
         return m_wallet->get_multisig_first_kex_msg();
@@ -1324,6 +1471,8 @@ string WalletImpl::getMultisigInfo() const {
 }
 
 string WalletImpl::makeMultisig(const vector<string>& info, const uint32_t threshold) {
+    if (checkBackgroundSync("cannot make multisig"))
+        return string();
     try {
         clearStatus();
 
@@ -1354,6 +1503,21 @@ std::string WalletImpl::exchangeMultisigKeys(const std::vector<std::string> &inf
     return string();
 }
 
+std::string WalletImpl::getMultisigKeyExchangeBooster(const std::vector<std::string> &info,
+    const std::uint32_t threshold,
+    const std::uint32_t num_signers) {
+    try {
+        clearStatus();
+
+        return m_wallet->get_multisig_key_exchange_booster(epee::wipeable_string(m_password), info, threshold, num_signers);
+    } catch (const exception& e) {
+        LOG_ERROR("Error on boosting multisig key exchange: " << e.what());
+        setStatusError(string(tr("Failed to boost multisig key exchange: ")) + e.what());
+    }
+
+    return string();
+}
+
 bool WalletImpl::exportMultisigImages(string& images) {
     try {
         clearStatus();
@@ -1464,6 +1628,9 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector<stri
     PendingTransactionImpl * transaction = new PendingTransactionImpl(*this);
 
     do {
+        if (checkBackgroundSync("cannot create transactions"))
+            break;
+
         std::vector<uint8_t> extra;
         std::string extra_nonce;
         vector<cryptonote::tx_destination_entry> dsts;
@@ -1630,6 +1797,9 @@ PendingTransaction *WalletImpl::createSweepUnmixableTransaction()
     PendingTransactionImpl * transaction = new PendingTransactionImpl(*this);
 
     do {
+        if (checkBackgroundSync("cannot sweep"))
+            break;
+
         try {
             transaction->m_pending_tx = m_wallet->create_unmixable_sweep_transactions();
             pendingTxPostProcess(transaction);
@@ -1763,11 +1933,15 @@ uint32_t WalletImpl::defaultMixin() const
 
 void WalletImpl::setDefaultMixin(uint32_t arg)
 {
+    if (checkBackgroundSync("cannot set default mixin"))
+        return;
     m_wallet->default_mixin(arg);
 }
 
 bool WalletImpl::setCacheAttribute(const std::string &key, const std::string &val)
 {
+    if (checkBackgroundSync("cannot set cache attribute"))
+        return false;
     m_wallet->set_attribute(key, val);
     return true;
 }
@@ -1781,6 +1955,8 @@ std::string WalletImpl::getCacheAttribute(const std::string &key) const
 
 bool WalletImpl::setUserNote(const std::string &txid, const std::string &note)
 {
+    if (checkBackgroundSync("cannot set user note"))
+        return false;
     cryptonote::blobdata txid_data;
     if(!epee::string_tools::parse_hexstr_to_binbuff(txid, txid_data) || txid_data.size() != sizeof(crypto::hash))
       return false;
@@ -1792,6 +1968,8 @@ bool WalletImpl::setUserNote(const std::string &txid, const std::string &note)
 
 std::string WalletImpl::getUserNote(const std::string &txid) const
 {
+    if (checkBackgroundSync("cannot get user note"))
+        return "";
     cryptonote::blobdata txid_data;
     if(!epee::string_tools::parse_hexstr_to_binbuff(txid, txid_data) || txid_data.size() != sizeof(crypto::hash))
       return "";
@@ -1802,6 +1980,9 @@ std::string WalletImpl::getUserNote(const std::string &txid) const
 
 std::string WalletImpl::getTxKey(const std::string &txid_str) const
 {
+    if (checkBackgroundSync("cannot get tx key"))
+        return "";
+
     crypto::hash txid;
     if(!epee::string_tools::hex_to_pod(txid_str, txid))
     {
@@ -1886,6 +2067,9 @@ bool WalletImpl::checkTxKey(const std::string &txid_str, std::string tx_key_str,
 
 std::string WalletImpl::getTxProof(const std::string &txid_str, const std::string &address_str, const std::string &message) const
 {
+    if (checkBackgroundSync("cannot get tx proof"))
+        return "";
+
     crypto::hash txid;
     if (!epee::string_tools::hex_to_pod(txid_str, txid))
     {
@@ -1942,6 +2126,9 @@ bool WalletImpl::checkTxProof(const std::string &txid_str, const std::string &ad
 }
 
 std::string WalletImpl::getSpendProof(const std::string &txid_str, const std::string &message) const {
+    if (checkBackgroundSync("cannot get spend proof"))
+        return "";
+
     crypto::hash txid;
     if(!epee::string_tools::hex_to_pod(txid_str, txid))
     {
@@ -1984,6 +2171,9 @@ bool WalletImpl::checkSpendProof(const std::string &txid_str, const std::string
 }
 
 std::string WalletImpl::getReserveProof(bool all, uint32_t account_index, uint64_t amount, const std::string &message) const {
+    if (checkBackgroundSync("cannot get reserve proof"))
+        return "";
+
     try
     {
         clearStatus();
@@ -2030,6 +2220,9 @@ bool WalletImpl::checkReserveProof(const std::string &address, const std::string
 
 std::string WalletImpl::signMessage(const std::string &message, const std::string &address)
 {
+    if (checkBackgroundSync("cannot sign message"))
+        return "";
+
     if (address.empty()) {
         return m_wallet->sign(message, tools::wallet2::sign_with_spend_key);
     }
@@ -2156,6 +2349,16 @@ bool WalletImpl::isDeterministic() const
     return m_wallet->is_deterministic();
 }
 
+bool WalletImpl::isBackgroundSyncing() const
+{
+    return m_wallet->is_background_syncing();
+}
+
+bool WalletImpl::isBackgroundWallet() const
+{
+    return m_wallet->is_background_wallet();
+}
+
 void WalletImpl::clearStatus() const
 {
     boost::lock_guard<boost::mutex> l(m_statusMutex);
@@ -2224,9 +2427,7 @@ void WalletImpl::doRefresh()
             if(rescan)
                 m_wallet->rescan_blockchain(false);
             m_wallet->refresh(trustedDaemon());
-            if (!m_synchronized) {
-                m_synchronized = true;
-            }
+            m_synchronized = m_wallet->is_synced();
             // assuming if we have empty history, it wasn't initialized yet
             // for further history changes client need to update history in
             // "on_money_received" and "on_money_sent" callbacks
@@ -2329,6 +2530,24 @@ bool WalletImpl::doInit(const string &daemon_address, const std::string &proxy_a
     return true;
 }
 
+bool WalletImpl::checkBackgroundSync(const std::string &message) const
+{
+    clearStatus();
+    if (m_wallet->is_background_wallet())
+    {
+        LOG_ERROR("Background wallets " + message);
+        setStatusError(tr("Background wallets ") + message);
+        return true;
+    }
+    if (m_wallet->is_background_syncing())
+    {
+        LOG_ERROR(message + " while background syncing");
+        setStatusError(message + tr(" while background syncing. Stop background syncing first."));
+        return true;
+    }
+    return false;
+}
+
 bool WalletImpl::parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector<std::string> &unknown_parameters, std::string &error)
 {
     return m_wallet->parse_uri(uri, address, payment_id, amount, tx_description, recipient_name, unknown_parameters, error);
@@ -2347,6 +2566,8 @@ std::string WalletImpl::getDefaultDataDir() const
 bool WalletImpl::rescanSpent()
 {
   clearStatus();
+  if (checkBackgroundSync("cannot rescan spent"))
+    return false;
   if (!trustedDaemon()) {
     setStatusError(tr("Rescan spent can only be used with a trusted daemon"));
     return false;
diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h
index d1bf4f759..d48d7f130 100644
--- a/src/wallet/api/wallet.h
+++ b/src/wallet/api/wallet.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
@@ -148,6 +148,7 @@ public:
     std::string getMultisigInfo() const override;
     std::string makeMultisig(const std::vector<std::string>& info, uint32_t threshold) override;
     std::string exchangeMultisigKeys(const std::vector<std::string> &info, const bool force_update_use_with_caution = false) override;
+    std::string getMultisigKeyExchangeBooster(const std::vector<std::string> &info, const uint32_t threshold, const uint32_t num_signers) override;
     bool exportMultisigImages(std::string& images) override;
     size_t importMultisigImages(const std::vector<std::string>& images) override;
     bool hasMultisigPartialKeyImages() const override;
@@ -172,6 +173,13 @@ public:
     bool importOutputs(const std::string &filename) override;
     bool scanTransactions(const std::vector<std::string> &txids) override;
 
+    bool setupBackgroundSync(const BackgroundSyncType background_sync_type, const std::string &wallet_password, const optional<std::string> &background_cache_password = optional<std::string>()) override;
+    BackgroundSyncType getBackgroundSyncType() const override;
+    bool startBackgroundSync() override;
+    bool stopBackgroundSync(const std::string &wallet_password) override;
+    bool isBackgroundSyncing() const override;
+    bool isBackgroundWallet() const override;
+
     virtual void disposeTransaction(PendingTransaction * t) override;
     virtual uint64_t estimateTransactionFee(const std::vector<std::pair<std::string, uint64_t>> &destinations,
                                             PendingTransaction::Priority priority) const override;
@@ -238,6 +246,7 @@ private:
     bool isNewWallet() const;
     void pendingTxPostProcess(PendingTransactionImpl * pending);
     bool doInit(const std::string &daemon_address, const std::string &proxy_address, uint64_t upper_transaction_size_limit = 0, bool ssl = false);
+    bool checkBackgroundSync(const std::string &message) const;
 
 private:
     friend class PendingTransactionImpl;
@@ -253,6 +262,10 @@ private:
     mutable boost::mutex m_statusMutex;
     mutable int m_status;
     mutable std::string m_errorString;
+    // TODO: harden password handling in the wallet API, see relevant discussion
+    // https://github.com/monero-project/monero-gui/issues/1537
+    // https://github.com/feather-wallet/feather/issues/72#issuecomment-1405602142
+    // https://github.com/monero-project/monero/pull/8619#issuecomment-1632951461
     std::string m_password;
     std::unique_ptr<TransactionHistoryImpl> m_history;
     std::unique_ptr<Wallet2CallbackImpl> m_wallet2Callback;
diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h
index 53210832b..c374d1574 100644
--- a/src/wallet/api/wallet2_api.h
+++ b/src/wallet/api/wallet2_api.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
@@ -446,6 +446,12 @@ struct Wallet
         ConnectionStatus_WrongVersion
     };
 
+    enum BackgroundSyncType {
+        BackgroundSync_Off = 0,
+        BackgroundSync_ReusePassword = 1,
+        BackgroundSync_CustomPassword = 2
+    };
+
     virtual ~Wallet() = 0;
     virtual std::string seed(const std::string& seed_offset = "") const = 0;
     virtual std::string getSeedLanguage() const = 0;
@@ -802,6 +808,15 @@ struct Wallet
      * @return new info string if more rounds required or an empty string if wallet creation is done
      */
     virtual std::string exchangeMultisigKeys(const std::vector<std::string> &info, const bool force_update_use_with_caution) = 0;
+    /**
+     * @brief getMultisigKeyExchangeBooster - obtain partial information for the key exchange round after the in-progress round,
+     *                                        to speed up another signer's key exchange process
+     * @param info - base58 encoded key derivations returned by makeMultisig or exchangeMultisigKeys function call
+     * @param threshold - number of required signers to make valid transaction. Must be <= number of participants.
+     * @param num_signers - total number of multisig participants.
+     * @return new info string if more rounds required or exception if no more rounds (i.e. no rounds to boost)
+     */
+    virtual std::string getMultisigKeyExchangeBooster(const std::vector<std::string> &info, const uint32_t threshold, const uint32_t num_signers) = 0;
     /**
      * @brief exportMultisigImages - exports transfers' key images
      * @param images - output paramter for hex encoded array of images
@@ -937,6 +952,42 @@ struct Wallet
      */
     virtual bool scanTransactions(const std::vector<std::string> &txids) = 0;
 
+    /*!
+     * \brief setupBackgroundSync       - setup background sync mode with just a view key
+     * \param background_sync_type      - the mode the wallet background syncs in
+     * \param wallet_password
+     * \param background_cache_password - custom password to encrypt background cache, only needed for custom password background sync type
+     * \return                          - true on success
+     */
+    virtual bool setupBackgroundSync(const BackgroundSyncType background_sync_type, const std::string &wallet_password, const optional<std::string> &background_cache_password) = 0;
+
+    /*!
+     * \brief getBackgroundSyncType     - get mode the wallet background syncs in
+     * \return                          - the type, or off if type is unknown
+     */
+    virtual BackgroundSyncType getBackgroundSyncType() const = 0;
+
+    /**
+     * @brief startBackgroundSync - sync the chain in the background with just view key
+     */
+    virtual bool startBackgroundSync() = 0;
+
+    /**
+     * @brief stopBackgroundSync  - bring back spend key and process background synced txs
+     * \param wallet_password
+     */
+    virtual bool stopBackgroundSync(const std::string &wallet_password) = 0;
+
+    /**
+     * @brief isBackgroundSyncing - returns true if the wallet is background syncing
+     */
+    virtual bool isBackgroundSyncing() const = 0;
+
+    /**
+     * @brief isBackgroundWallet - returns true if the wallet is a background wallet
+     */
+    virtual bool isBackgroundWallet() const = 0;
+
     virtual TransactionHistory * history() = 0;
     virtual AddressBook * addressBook() = 0;
     virtual Subaddress * subaddress() = 0;
diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp
index 1bb4bc27c..97895990b 100644
--- a/src/wallet/api/wallet_manager.cpp
+++ b/src/wallet/api/wallet_manager.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h
index 46ec36297..45a9f010f 100644
--- a/src/wallet/api/wallet_manager.h
+++ b/src/wallet/api/wallet_manager.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/wallet/message_store.cpp b/src/wallet/message_store.cpp
index 1169d5269..87fa6936e 100644
--- a/src/wallet/message_store.cpp
+++ b/src/wallet/message_store.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 //
 // All rights reserved.
diff --git a/src/wallet/message_store.h b/src/wallet/message_store.h
index c0afa2afa..466c2acba 100644
--- a/src/wallet/message_store.h
+++ b/src/wallet/message_store.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 //
 // All rights reserved.
diff --git a/src/wallet/message_transporter.cpp b/src/wallet/message_transporter.cpp
index e6c26cba0..3259854ad 100644
--- a/src/wallet/message_transporter.cpp
+++ b/src/wallet/message_transporter.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 //
 // All rights reserved.
diff --git a/src/wallet/message_transporter.h b/src/wallet/message_transporter.h
index d24888333..342e41836 100644
--- a/src/wallet/message_transporter.h
+++ b/src/wallet/message_transporter.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 //
 // All rights reserved.
diff --git a/src/wallet/node_rpc_proxy.cpp b/src/wallet/node_rpc_proxy.cpp
index 1142e46ce..a8b8323c4 100644
--- a/src/wallet/node_rpc_proxy.cpp
+++ b/src/wallet/node_rpc_proxy.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/wallet/node_rpc_proxy.h b/src/wallet/node_rpc_proxy.h
index 0dcfd0f83..c9fe0a83a 100644
--- a/src/wallet/node_rpc_proxy.h
+++ b/src/wallet/node_rpc_proxy.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/src/wallet/ringdb.cpp b/src/wallet/ringdb.cpp
index 3b3a8303a..8802f731b 100644
--- a/src/wallet/ringdb.cpp
+++ b/src/wallet/ringdb.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 // 
 // All rights reserved.
diff --git a/src/wallet/ringdb.h b/src/wallet/ringdb.h
index 93be362d0..13412cdfd 100644
--- a/src/wallet/ringdb.h
+++ b/src/wallet/ringdb.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 // 
 // All rights reserved.
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 9b09d0920..fa9c51bb2 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
@@ -156,6 +156,8 @@ static const std::string MULTISIG_SIGNATURE_MAGIC = "SigMultisigPkV1";
 
 static const std::string ASCII_OUTPUT_MAGIC = "MoneroAsciiDataV1";
 
+static const std::string BACKGROUND_WALLET_SUFFIX = ".background";
+
 boost::mutex tools::wallet2::default_daemon_address_lock;
 std::string tools::wallet2::default_daemon_address = "";
 
@@ -1008,14 +1010,14 @@ uint64_t num_priv_multisig_keys_post_setup(uint64_t threshold, uint64_t total)
  * @param keys_data_key the chacha key that encrypts wallet keys files
  * @return crypto::chacha_key the chacha key that encrypts the wallet cache files
  */
-crypto::chacha_key derive_cache_key(const crypto::chacha_key& keys_data_key)
+crypto::chacha_key derive_cache_key(const crypto::chacha_key& keys_data_key, const unsigned char domain_separator)
 {
   static_assert(HASH_SIZE == sizeof(crypto::chacha_key), "Mismatched sizes of hash and chacha key");
 
   crypto::chacha_key cache_key;
   epee::mlocked<tools::scrubbed_arr<char, HASH_SIZE+1>> cache_key_data;
   memcpy(cache_key_data.data(), &keys_data_key, HASH_SIZE);
-  cache_key_data[HASH_SIZE] = config::HASH_KEY_WALLET_CACHE;
+  cache_key_data[HASH_SIZE] = domain_separator;
   cn_fast_hash(cache_key_data.data(), HASH_SIZE+1, (crypto::hash&) cache_key);
 
   return cache_key;
@@ -1103,7 +1105,7 @@ wallet_keys_unlocker::wallet_keys_unlocker(wallet2 &w, const boost::optional<too
   boost::lock_guard<boost::mutex> lock(lockers_lock);
   if (lockers++ > 0)
     locked = false;
-  if (!locked || w.is_unattended() || w.ask_password() != tools::wallet2::AskPasswordToDecrypt || w.watch_only())
+  if (!locked || w.is_unattended() || w.ask_password() != tools::wallet2::AskPasswordToDecrypt || w.watch_only() || w.is_background_syncing())
   {
     locked = false;
     return;
@@ -1219,6 +1221,11 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended, std
   m_ignore_outputs_above(MONEY_SUPPLY),
   m_ignore_outputs_below(0),
   m_track_uses(false),
+  m_is_background_wallet(false),
+  m_background_sync_type(BackgroundSyncOff),
+  m_background_syncing(false),
+  m_processing_background_cache(false),
+  m_custom_background_key(boost::none),
   m_show_wallet_name_when_locked(false),
   m_inactivity_lock_timeout(DEFAULT_INACTIVITY_LOCK_TIMEOUT),
   m_setup_background_mining(BackgroundMiningMaybe),
@@ -1854,6 +1861,9 @@ bool has_nonrequested_tx_at_height_or_above_requested(uint64_t height, const std
 //----------------------------------------------------------------------------------------------------
 void wallet2::scan_tx(const std::unordered_set<crypto::hash> &txids)
 {
+  THROW_WALLET_EXCEPTION_IF(m_background_syncing || m_is_background_wallet, error::wallet_internal_error,
+    "cannot scan tx from background wallet");
+
   // Get the transactions from daemon in batches sorted lowest height to highest
   tx_entry_data txs_to_scan = get_tx_entries(txids);
   if (txs_to_scan.tx_entries.empty())
@@ -2161,11 +2171,11 @@ void wallet2::scan_output(const cryptonote::transaction &tx, bool miner_tx, cons
   THROW_WALLET_EXCEPTION_IF(i >= tx.vout.size(), error::wallet_internal_error, "Invalid vout index");
 
   // if keys are encrypted, ask for password
-  if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only && !m_multisig_rescan_k)
+  if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only && !m_multisig_rescan_k && !m_background_syncing)
   {
     static critical_section password_lock;
     CRITICAL_REGION_LOCAL(password_lock);
-    if (!m_encrypt_keys_after_refresh)
+    if (!m_encrypt_keys_after_refresh && !m_processing_background_cache)
     {
       boost::optional<epee::wipeable_string> pwd = m_callback->on_get_password(pool ? "output found in pool" : "output received");
       THROW_WALLET_EXCEPTION_IF(!pwd, error::password_needed, tr("Password is needed to compute key image for incoming monero"));
@@ -2177,7 +2187,7 @@ void wallet2::scan_output(const cryptonote::transaction &tx, bool miner_tx, cons
   crypto::public_key output_public_key;
   THROW_WALLET_EXCEPTION_IF(!get_output_public_key(tx.vout[i], output_public_key), error::wallet_internal_error, "Failed to get output public key");
 
-  if (m_multisig)
+  if (m_multisig || m_background_syncing/*no spend key*/)
   {
     tx_scan_info.in_ephemeral.pub = output_public_key;
     tx_scan_info.in_ephemeral.sec = crypto::null_skey;
@@ -2434,6 +2444,22 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
         THROW_WALLET_EXCEPTION_IF(tx.vout.size() != o_indices.size(), error::wallet_internal_error,
             "transactions outputs size=" + std::to_string(tx.vout.size()) +
             " not match with daemon response size=" + std::to_string(o_indices.size()));
+
+        // we're going to re-process this receive when background sync is disabled
+        if (m_background_syncing && m_background_sync_data.txs.find(txid) == m_background_sync_data.txs.end())
+        {
+          size_t bgs_idx = m_background_sync_data.txs.size();
+          background_synced_tx_t bgs_tx = {
+            .index_in_background_sync_data = bgs_idx,
+            .tx                            = tx,
+            .output_indices                = o_indices,
+            .height                        = height,
+            .block_timestamp               = ts,
+            .double_spend_seen             = double_spend_seen
+          };
+          LOG_PRINT_L2("Adding received tx " << txid << " to background sync data (idx=" << bgs_idx << ")");
+          m_background_sync_data.txs.insert({txid, std::move(bgs_tx)});
+        }
       }
 
       for(size_t o: outs)
@@ -2459,7 +2485,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
 	    td.m_tx = (const cryptonote::transaction_prefix&)tx;
 	    td.m_txid = txid;
             td.m_key_image = tx_scan_info[o].ki;
-            td.m_key_image_known = !m_watch_only && !m_multisig;
+            td.m_key_image_known = !m_watch_only && !m_multisig && !m_background_syncing;
             if (!td.m_key_image_known)
             {
               // we might have cold signed, and have a mapping to key images
@@ -2649,10 +2675,25 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
         set_spent(it->second, height);
         if (!ignore_callbacks && 0 != m_callback)
           m_callback->on_money_spent(height, txid, tx, amount, tx, td.m_subaddr_index);
+
+        if (m_background_syncing && m_background_sync_data.txs.find(txid) == m_background_sync_data.txs.end())
+        {
+          size_t bgs_idx = m_background_sync_data.txs.size();
+          background_synced_tx_t bgs_tx = {
+            .index_in_background_sync_data = bgs_idx,
+            .tx                            = tx,
+            .output_indices                = o_indices,
+            .height                        = height,
+            .block_timestamp               = ts,
+            .double_spend_seen             = double_spend_seen
+          };
+          LOG_PRINT_L2("Adding spent tx " << txid << " to background sync data (idx=" << bgs_idx << ")");
+          m_background_sync_data.txs.insert({txid, std::move(bgs_tx)});
+        }
       }
     }
 
-    if (!pool && m_track_uses)
+    if (!pool && (m_track_uses || (m_background_syncing && it == m_key_images.end())))
     {
       PERF_TIMER(track_uses);
       const uint64_t amount = in_to_key.amount;
@@ -2666,7 +2707,27 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
           {
             size_t idx = i->second;
             THROW_WALLET_EXCEPTION_IF(idx >= m_transfers.size(), error::wallet_internal_error, "Output tracker cache index out of range");
-            m_transfers[idx].m_uses.push_back(std::make_pair(height, txid));
+
+            if (m_track_uses)
+              m_transfers[idx].m_uses.push_back(std::make_pair(height, txid));
+
+            // We'll re-process all txs which *might* be spends when we disable
+            // background sync and retrieve the spend key. We don't know if an
+            // output is a spend in this tx if we don't know its key image.
+            if (m_background_syncing && !m_transfers[idx].m_key_image_known && m_background_sync_data.txs.find(txid) == m_background_sync_data.txs.end())
+            {
+              size_t bgs_idx = m_background_sync_data.txs.size();
+              background_synced_tx_t bgs_tx = {
+                .index_in_background_sync_data = bgs_idx,
+                .tx                            = tx,
+                .output_indices                = o_indices,
+                .height                        = height,
+                .block_timestamp               = ts,
+                .double_spend_seen             = double_spend_seen
+              };
+              LOG_PRINT_L2("Adding plausible spent tx " << txid << " to background sync data (idx=" << bgs_idx << ")");
+              m_background_sync_data.txs.insert({txid, std::move(bgs_tx)});
+            }
           }
         }
       }
@@ -2676,7 +2737,24 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
           continue;
         for (uint64_t offset: offsets)
           if (offset == td.m_global_output_index)
-            td.m_uses.push_back(std::make_pair(height, txid));
+          {
+            if (m_track_uses)
+              td.m_uses.push_back(std::make_pair(height, txid));
+            if (m_background_syncing && !td.m_key_image_known && m_background_sync_data.txs.find(txid) == m_background_sync_data.txs.end())
+            {
+              size_t bgs_idx = m_background_sync_data.txs.size();
+              background_synced_tx_t bgs_tx = {
+                .index_in_background_sync_data = bgs_idx,
+                .tx                            = tx,
+                .output_indices                = o_indices,
+                .height                        = height,
+                .block_timestamp               = ts,
+                .double_spend_seen             = double_spend_seen
+              };
+              LOG_PRINT_L2("Adding plausible spent tx " << txid << " to background sync data (idx=" << bgs_idx << ")");
+              m_background_sync_data.txs.insert({txid, std::move(bgs_tx)});
+            }
+          }
       }
     }
   }
@@ -2891,6 +2969,10 @@ void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cry
       "block transactions=" + std::to_string(bche.txs.size()) +
       " not match with daemon response size=" + std::to_string(parsed_block.o_indices.indices.size()));
 
+  THROW_WALLET_EXCEPTION_IF(height != m_blockchain.size(), error::wallet_internal_error,
+      "New blockchain entry mismatch: block height " + std::to_string(height) +
+      " is not the expected next height " + std::to_string(m_blockchain.size()));
+
   //handle transactions from new block
     
   //optimization: seeking only for blocks that are not older then the wallet creation time plus 1 day. 1 day is for possible user incorrect time setup
@@ -3029,7 +3111,7 @@ void wallet2::process_pool_info_extent(const cryptonote::COMMAND_RPC_GET_BLOCKS_
       {
         read_pool_txs(req_t, resp_t, r, res.remaining_added_pool_txids, added_pool_txs);
         if (!r || resp_t.status != CORE_RPC_STATUS_OK)
-          LOG_PRINT_L0("Error calling gettransactions daemon RPC: r " << r << ", status " << get_rpc_status(resp_t.status));
+          LOG_PRINT_L0("Error calling gettransactions daemon RPC: r " << r << ", status " << get_rpc_status(m_trusted_daemon, resp_t.status));
       }
     );
   }
@@ -3049,14 +3131,14 @@ void wallet2::pull_blocks(bool first, bool try_incremental, uint64_t start_heigh
   req.start_height = start_height;
   req.no_miner_tx = m_refresh_type == RefreshNoCoinbase;
 
-  req.requested_info = first ? COMMAND_RPC_GET_BLOCKS_FAST::BLOCKS_AND_POOL : COMMAND_RPC_GET_BLOCKS_FAST::BLOCKS_ONLY;
-  if (try_incremental)
+  req.requested_info = (first && !m_background_syncing) ? COMMAND_RPC_GET_BLOCKS_FAST::BLOCKS_AND_POOL : COMMAND_RPC_GET_BLOCKS_FAST::BLOCKS_ONLY;
+  if (try_incremental && !m_background_syncing)
     req.pool_info_since = m_pool_info_query_time;
 
   {
     const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
     bool r = net_utils::invoke_http_bin("/getblocks.bin", req, res, *m_http_client, rpc_timeout);
-    THROW_ON_RPC_RESPONSE_ERROR(r, {}, res, "getblocks.bin", error::get_blocks_error, get_rpc_status(res.status));
+    THROW_ON_RPC_RESPONSE_ERROR(r, {}, res, "getblocks.bin", error::get_blocks_error, get_rpc_status(m_trusted_daemon, res.status));
     THROW_WALLET_EXCEPTION_IF(res.blocks.size() != res.output_indices.size(), error::wallet_internal_error,
         "mismatched blocks (" + boost::lexical_cast<std::string>(res.blocks.size()) + ") and output_indices (" +
         boost::lexical_cast<std::string>(res.output_indices.size()) + ") sizes from daemon");
@@ -3073,7 +3155,7 @@ void wallet2::pull_blocks(bool first, bool try_incremental, uint64_t start_heigh
       << ", height " << blocks_start_height + blocks.size() << ", node height " << res.current_height
       << ", pool info " << static_cast<unsigned int>(res.pool_info_extent));
 
-  if (first)
+  if (first && !m_background_syncing)
   {
     if (res.pool_info_extent != COMMAND_RPC_GET_BLOCKS_FAST::NONE)
     {
@@ -3101,20 +3183,19 @@ void wallet2::pull_hashes(uint64_t start_height, uint64_t &blocks_start_height,
   {
     const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
     bool r = net_utils::invoke_http_bin("/gethashes.bin", req, res, *m_http_client, rpc_timeout);
-    THROW_ON_RPC_RESPONSE_ERROR(r, {}, res, "gethashes.bin", error::get_hashes_error, get_rpc_status(res.status));
+    THROW_ON_RPC_RESPONSE_ERROR(r, {}, res, "gethashes.bin", error::get_hashes_error, get_rpc_status(m_trusted_daemon, res.status));
   }
 
   blocks_start_height = res.start_height;
   hashes = std::move(res.m_block_ids);
 }
 //----------------------------------------------------------------------------------------------------
-void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cryptonote::block_complete_entry> &blocks, const std::vector<parsed_block> &parsed_blocks, uint64_t& blocks_added, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache)
+void wallet2::process_parsed_blocks(const uint64_t start_height, const std::vector<cryptonote::block_complete_entry> &blocks, const std::vector<parsed_block> &parsed_blocks, uint64_t& blocks_added, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache)
 {
-  size_t current_index = start_height;
   blocks_added = 0;
 
   THROW_WALLET_EXCEPTION_IF(blocks.size() != parsed_blocks.size(), error::wallet_internal_error, "size mismatch");
-  THROW_WALLET_EXCEPTION_IF(!m_blockchain.is_in_bounds(current_index), error::out_of_hashchain_bounds_error);
+  THROW_WALLET_EXCEPTION_IF(!m_blockchain.is_in_bounds(start_height), error::out_of_hashchain_bounds_error);
 
   tools::threadpool& tpool = tools::threadpool::getInstanceForCompute();
   tools::threadpool::waiter waiter(tpool);
@@ -3125,8 +3206,22 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cry
     num_txes += 1 + parsed_blocks[i].txes.size();
   tx_cache_data.resize(num_txes);
   size_t txidx = 0;
+  crypto::hash prev_block_id;
+  bool has_prev_block = m_blockchain.is_in_bounds(start_height - 1);
+  if (has_prev_block) {
+    prev_block_id = m_blockchain[start_height - 1];
+  }
   for (size_t i = 0; i < blocks.size(); ++i)
   {
+    if (has_prev_block) {
+      THROW_WALLET_EXCEPTION_IF(prev_block_id != parsed_blocks[i].block.prev_id, error::wallet_internal_error,
+          "Parent block hash mismatch at height " + std::to_string(start_height + i) +
+          ": expected " + string_tools::pod_to_hex(prev_block_id) +
+          ", but received a new block with prev_id " + string_tools::pod_to_hex(parsed_blocks[i].block.prev_id));
+    }
+    prev_block_id = parsed_blocks[i].hash;
+    has_prev_block = true;
+
     THROW_WALLET_EXCEPTION_IF(parsed_blocks[i].txes.size() != parsed_blocks[i].block.tx_hashes.size(),
         error::wallet_internal_error, "Mismatched parsed_blocks[i].txes.size() and parsed_blocks[i].block.tx_hashes.size()");
     if (should_skip_block(parsed_blocks[i].block, start_height + i))
@@ -3266,6 +3361,7 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cry
 
   hwdev.set_mode(hw::device::NONE);
 
+  size_t current_index = start_height;
   size_t tx_cache_data_offset = 0;
   for (size_t i = 0; i < blocks.size(); ++i)
   {
@@ -3587,6 +3683,9 @@ void wallet2::process_unconfirmed_transfer(bool incremental, const crypto::hash
 // incremental update anymore, because with that we might miss some txs altogether.
 void wallet2::update_pool_state(std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> &process_txs, bool refreshed, bool try_incremental)
 {
+  process_txs.clear();
+  if (m_background_syncing)
+    return;
   bool updated = false;
   if (m_pool_info_query_time != 0 && try_incremental)
   {
@@ -3598,11 +3697,12 @@ void wallet2::update_pool_state(std::vector<std::tuple<cryptonote::transaction,
 
     req.requested_info = COMMAND_RPC_GET_BLOCKS_FAST::POOL_ONLY;
     req.pool_info_since = m_pool_info_query_time;
+    req.prune = true;
 
     {
       const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
       bool r = net_utils::invoke_http_bin("/getblocks.bin", req, res, *m_http_client, rpc_timeout);
-      THROW_ON_RPC_RESPONSE_ERROR(r, {}, res, "getblocks.bin", error::get_blocks_error, get_rpc_status(res.status));
+      THROW_ON_RPC_RESPONSE_ERROR(r, {}, res, "getblocks.bin", error::get_blocks_error, get_rpc_status(m_trusted_daemon, res.status));
     }
 
     m_pool_info_query_time = res.daemon_time;
@@ -3687,7 +3787,7 @@ void wallet2::update_pool_state_by_pool_query(std::vector<std::tuple<cryptonote:
     {
       read_pool_txs(req_t, resp_t, r, txids, process_txs);
       if (!r || resp_t.status != CORE_RPC_STATUS_OK)
-        LOG_PRINT_L0("Error calling gettransactions daemon RPC: r " << r << ", status " << get_rpc_status(resp_t.status));
+        LOG_PRINT_L0("Error calling gettransactions daemon RPC: r " << r << ", status " << get_rpc_status(m_trusted_daemon, resp_t.status));
     }
   );
 
@@ -4121,6 +4221,8 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
   }
 
   m_first_refresh_done = true;
+  if (m_background_syncing || m_is_background_wallet)
+    m_background_sync_data.first_refresh_done = true;
 
   LOG_PRINT_L1("Refresh done, blocks received: " << blocks_fetched << ", balance (all accounts): " << print_money(balance_all(false)) << ", unlocked: " << print_money(unlocked_balance_all(false)));
 }
@@ -4203,6 +4305,14 @@ wallet2::detached_blockchain_data wallet2::detach_blockchain(uint64_t height, st
       td.m_uses.pop_back();
   }
 
+  for (auto it = m_background_sync_data.txs.begin(); it != m_background_sync_data.txs.end(); )
+  {
+    if(height <= it->second.height)
+      it = m_background_sync_data.txs.erase(it);
+    else
+      ++it;
+  }
+
   if (output_tracker_cache)
     output_tracker_cache->clear();
 
@@ -4277,8 +4387,12 @@ void wallet2::handle_reorg(uint64_t height, std::map<std::pair<uint64_t, uint64_
   //               C
   THROW_WALLET_EXCEPTION_IF(height < m_blockchain.offset() && m_blockchain.size() > m_blockchain.offset(),
       error::wallet_internal_error, "Daemon claims reorg below last checkpoint");
+
   detached_blockchain_data dbd = detach_blockchain(height, output_tracker_cache);
 
+  if (m_background_syncing && height < m_background_sync_data.start_height)
+    m_background_sync_data.start_height = height;
+
   if (m_callback)
     m_callback->on_reorg(height, dbd.detached_blockchain.size(), dbd.detached_tx_hashes.size());
 }
@@ -4288,6 +4402,7 @@ bool wallet2::deinit()
   if(m_is_initialized) {
     m_is_initialized = false;
     unlock_keys_file();
+    unlock_background_keys_file();
     m_account.deinit();
   }
   return true;
@@ -4314,6 +4429,7 @@ bool wallet2::clear()
   m_device_last_key_image_sync = 0;
   m_pool_info_query_time = 0;
   m_skip_to_height = 0;
+  m_background_sync_data = background_sync_data_t{};
   return true;
 }
 //----------------------------------------------------------------------------------------------------
@@ -4332,13 +4448,30 @@ void wallet2::clear_soft(bool keep_key_images)
   m_scanned_pool_txs[1].clear();
   m_pool_info_query_time = 0;
   m_skip_to_height = 0;
+  m_background_sync_data = background_sync_data_t{};
 
   cryptonote::block b;
   generate_genesis(b);
   m_blockchain.push_back(get_block_hash(b));
   m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx);
 }
-
+//----------------------------------------------------------------------------------------------------
+void wallet2::clear_user_data()
+{
+  for (auto i = m_confirmed_txs.begin(); i != m_confirmed_txs.end(); ++i)
+    i->second.m_dests.clear();
+  for (auto i = m_unconfirmed_txs.begin(); i != m_unconfirmed_txs.end(); ++i)
+    i->second.m_dests.clear();
+  for (auto i = m_transfers.begin(); i != m_transfers.end(); ++i)
+    i->m_frozen = false;
+  m_tx_keys.clear();
+  m_tx_notes.clear();
+  m_address_book.clear();
+  m_subaddress_labels.clear();
+  m_attributes.clear();
+  m_account_tags = std::pair<std::map<std::string, std::string>, std::vector<std::string>>();
+}
+//----------------------------------------------------------------------------------------------------
 /*!
  * \brief Stores wallet information to wallet file.
  * \param  keys_file_name Name of wallet file
@@ -4350,16 +4483,35 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
 {
   boost::optional<wallet2::keys_file_data> keys_file_data = get_keys_file_data(password, watch_only);
   CHECK_AND_ASSERT_MES(keys_file_data != boost::none, false, "failed to generate wallet keys data");
-
+  return store_keys_file_data(keys_file_name, keys_file_data.get());
+}
+//----------------------------------------------------------------------------------------------------
+bool wallet2::store_keys(const std::string& keys_file_name, const crypto::chacha_key& key, bool watch_only, bool background_keys_file)
+{
+  boost::optional<wallet2::keys_file_data> keys_file_data = get_keys_file_data(key, watch_only, background_keys_file);
+  CHECK_AND_ASSERT_MES(keys_file_data != boost::none, false, "failed to generate wallet keys data");
+  return store_keys_file_data(keys_file_name, keys_file_data.get(), background_keys_file);
+}
+//----------------------------------------------------------------------------------------------------
+bool wallet2::store_keys_file_data(const std::string& keys_file_name, wallet2::keys_file_data &keys_file_data, bool background_keys_file)
+{
   std::string tmp_file_name = keys_file_name + ".new";
   std::string buf;
-  bool r = ::serialization::dump_binary(keys_file_data.get(), buf);
+  bool r = ::serialization::dump_binary(keys_file_data, buf);
   r = r && save_to_file(tmp_file_name, buf);
   CHECK_AND_ASSERT_MES(r, false, "failed to generate wallet keys file " << tmp_file_name);
 
-  unlock_keys_file();
+  if (!background_keys_file)
+    unlock_keys_file();
+  else
+    unlock_background_keys_file();
+
   std::error_code e = tools::replace_file(tmp_file_name, keys_file_name);
-  lock_keys_file();
+
+  if (!background_keys_file)
+    lock_keys_file();
+  else
+    lock_background_keys_file(keys_file_name);
 
   if (e) {
     boost::filesystem::remove(tmp_file_name);
@@ -4371,26 +4523,27 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
 }
 //----------------------------------------------------------------------------------------------------
 boost::optional<wallet2::keys_file_data> wallet2::get_keys_file_data(const epee::wipeable_string& password, bool watch_only)
+{
+  crypto::chacha_key key;
+  crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds);
+  verify_password_with_cached_key(key);
+  return get_keys_file_data(key, watch_only);
+}
+//----------------------------------------------------------------------------------------------------
+boost::optional<wallet2::keys_file_data> wallet2::get_keys_file_data(const crypto::chacha_key& key, bool watch_only, bool background_keys_file)
 {
   epee::byte_slice account_data;
   std::string multisig_signers;
   std::string multisig_derivations;
   cryptonote::account_base account = m_account;
 
-  crypto::chacha_key key;
-  crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds);
-
-  // We use m_cache_key as a deterministic test to see if given key corresponds to original password
-  const crypto::chacha_key cache_key = derive_cache_key(key);
-  THROW_WALLET_EXCEPTION_IF(cache_key != m_cache_key, error::invalid_password);
-
   if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
   {
     account.encrypt_viewkey(key);
     account.decrypt_keys(key);
   }
 
-  if (watch_only)
+  if (watch_only || background_keys_file)
     account.forget_spend_key();
 
   account.encrypt_keys(key);
@@ -4525,6 +4678,9 @@ boost::optional<wallet2::keys_file_data> wallet2::get_keys_file_data(const epee:
   value2.SetInt(m_track_uses ? 1 : 0);
   json.AddMember("track_uses", value2, json.GetAllocator());
 
+  value2.SetInt(m_background_sync_type);
+  json.AddMember("background_sync_type", value2, json.GetAllocator());
+
   value2.SetInt(m_show_wallet_name_when_locked ? 1 : 0);
   json.AddMember("show_wallet_name_when_locked", value2, json.GetAllocator());
 
@@ -4585,6 +4741,12 @@ boost::optional<wallet2::keys_file_data> wallet2::get_keys_file_data(const epee:
   value2.SetInt(m_enable_multisig ? 1 : 0);
   json.AddMember("enable_multisig", value2, json.GetAllocator());
 
+  if (m_background_sync_type == BackgroundSyncCustomPassword && !background_keys_file && m_custom_background_key)
+  {
+    value.SetString(reinterpret_cast<const char*>(m_custom_background_key.get().data()), m_custom_background_key.get().size());
+    json.AddMember("custom_background_key", value, json.GetAllocator());
+  }
+
   // Serialize the JSON object
   rapidjson::StringBuffer buffer;
   rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
@@ -4611,13 +4773,81 @@ void wallet2::setup_keys(const epee::wipeable_string &password)
     m_account.decrypt_viewkey(key);
   }
 
-  m_cache_key = derive_cache_key(key);
+  m_cache_key = derive_cache_key(key, config::HASH_KEY_WALLET_CACHE);
 
   get_ringdb_key();
 }
 //----------------------------------------------------------------------------------------------------
+void validate_background_cache_password_usage(const tools::wallet2::BackgroundSyncType background_sync_type, const boost::optional<epee::wipeable_string> &background_cache_password, const bool multisig, const bool watch_only, const bool key_on_device)
+{
+  THROW_WALLET_EXCEPTION_IF(multisig || watch_only || key_on_device, error::wallet_internal_error, multisig
+      ? "Background sync not implemented for multisig wallets" : watch_only
+      ? "Background sync not implemented for view only wallets"
+      : "Background sync not implemented for HW wallets");
+
+  switch (background_sync_type)
+  {
+    case tools::wallet2::BackgroundSyncOff:
+    {
+      THROW_WALLET_EXCEPTION(error::wallet_internal_error, "background sync is not enabled");
+      break;
+    }
+    case tools::wallet2::BackgroundSyncReusePassword:
+    {
+      THROW_WALLET_EXCEPTION_IF(background_cache_password, error::wallet_internal_error,
+          "unexpected custom background cache password");
+      break;
+    }
+    case tools::wallet2::BackgroundSyncCustomPassword:
+    {
+      THROW_WALLET_EXCEPTION_IF(!background_cache_password, error::wallet_internal_error,
+          "expected custom background cache password");
+      break;
+    }
+    default: THROW_WALLET_EXCEPTION(error::wallet_internal_error, "unknown background sync type");
+  }
+}
+//----------------------------------------------------------------------------------------------------
+void get_custom_background_key(const epee::wipeable_string &password, crypto::chacha_key &custom_background_key, const uint64_t kdf_rounds)
+{
+  crypto::chacha_key key;
+  crypto::generate_chacha_key(password.data(), password.size(), key, kdf_rounds);
+  custom_background_key = derive_cache_key(key, config::HASH_KEY_BACKGROUND_KEYS_FILE);
+}
+//----------------------------------------------------------------------------------------------------
+const crypto::chacha_key wallet2::get_cache_key()
+{
+  if (m_background_sync_type == BackgroundSyncCustomPassword && m_background_syncing)
+  {
+    THROW_WALLET_EXCEPTION_IF(!m_custom_background_key, error::wallet_internal_error, "Custom background key not set");
+    // Domain separate keys used to encrypt background keys file and cache
+    return derive_cache_key(m_custom_background_key.get(), config::HASH_KEY_BACKGROUND_CACHE);
+  }
+  else
+  {
+    return m_cache_key;
+  }
+}
+//----------------------------------------------------------------------------------------------------
+void wallet2::verify_password_with_cached_key(const epee::wipeable_string &password)
+{
+  crypto::chacha_key key;
+  crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds);
+  verify_password_with_cached_key(key);
+}
+//----------------------------------------------------------------------------------------------------
+void wallet2::verify_password_with_cached_key(const crypto::chacha_key &key)
+{
+  // We use m_cache_key as a deterministic test to see if given key corresponds to original password
+  const crypto::chacha_key cache_key = derive_cache_key(key, config::HASH_KEY_WALLET_CACHE);
+  THROW_WALLET_EXCEPTION_IF(cache_key != m_cache_key, error::invalid_password);
+}
+//----------------------------------------------------------------------------------------------------
 void wallet2::change_password(const std::string &filename, const epee::wipeable_string &original_password, const epee::wipeable_string &new_password)
 {
+  THROW_WALLET_EXCEPTION_IF(m_background_syncing || m_is_background_wallet, error::wallet_internal_error,
+      "cannot change password from background wallet");
+
   if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
     decrypt_keys(original_password);
   setup_keys(new_password);
@@ -4676,8 +4906,24 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st
   std::string account_data;
   account_data.resize(keys_file_data.account_data.size());
   crypto::chacha20(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]);
-  if (json.Parse(account_data.c_str()).HasParseError() || !json.IsObject())
+  const bool try_v0_format = json.Parse(account_data.c_str()).HasParseError() || !json.IsObject();
+  if (try_v0_format)
     crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]);
+
+  // Check if it's a background keys file if both of the above formats fail
+  {
+    m_is_background_wallet = false;
+    m_background_syncing = false;
+    cryptonote::account_base account_data_check;
+    if (try_v0_format && !epee::serialization::load_t_from_binary(account_data_check, account_data))
+    {
+      get_custom_background_key(password, key, m_kdf_rounds);
+      crypto::chacha20(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]);
+      m_is_background_wallet = !json.Parse(account_data.c_str()).HasParseError() && json.IsObject();
+      m_background_syncing = m_is_background_wallet; // start a background wallet background syncing
+    }
+  }
+
   // The contents should be JSON if the wallet follows the new format.
   if (json.Parse(account_data.c_str()).HasParseError())
   {
@@ -4714,6 +4960,7 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st
     m_ignore_outputs_above = MONEY_SUPPLY;
     m_ignore_outputs_below = 0;
     m_track_uses = false;
+    m_background_sync_type = BackgroundSyncOff;
     m_show_wallet_name_when_locked = false;
     m_inactivity_lock_timeout = DEFAULT_INACTIVITY_LOCK_TIMEOUT;
     m_setup_background_mining = BackgroundMiningMaybe;
@@ -4728,6 +4975,7 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st
     encrypted_secret_keys = false;
     m_enable_multisig = false;
     m_allow_mismatched_daemon_version = false;
+    m_custom_background_key = boost::none;
   }
   else if(json.IsObject())
   {
@@ -4955,6 +5203,39 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st
 
     GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, enable_multisig, int, Int, false, false);
     m_enable_multisig = field_enable_multisig;
+
+    GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, background_sync_type, BackgroundSyncType, Int, false, BackgroundSyncOff);
+    m_background_sync_type = field_background_sync_type;
+
+    // Load encryption key used to encrypt background cache
+    crypto::chacha_key custom_background_key;
+    m_custom_background_key = boost::none;
+    if (m_background_sync_type == BackgroundSyncCustomPassword && !m_is_background_wallet)
+    {
+      if (!json.HasMember("custom_background_key"))
+      {
+        LOG_ERROR("Field custom_background_key not found in JSON");
+        return false;
+      }
+      else if (!json["custom_background_key"].IsString())
+      {
+        LOG_ERROR("Field custom_background_key found in JSON, but not String");
+        return false;
+      }
+      else if (json["custom_background_key"].GetStringLength() != sizeof(crypto::chacha_key))
+      {
+        LOG_ERROR("Field custom_background_key found in JSON, but not correct length");
+        return false;
+      }
+      const char *field_custom_background_key = json["custom_background_key"].GetString();
+      memcpy(custom_background_key.data(), field_custom_background_key, sizeof(crypto::chacha_key));
+      m_custom_background_key = boost::optional<crypto::chacha_key>(custom_background_key);
+      LOG_PRINT_L1("Loaded custom background key derived from custom password");
+    }
+    else if (json.HasMember("custom_background_key"))
+    {
+      LOG_ERROR("Unexpected field custom_background_key found in JSON");
+    }
   }
   else
   {
@@ -5018,12 +5299,17 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st
   const cryptonote::account_keys& keys = m_account.get_keys();
   hw::device &hwdev = m_account.get_device();
   r = r && hwdev.verify_keys(keys.m_view_secret_key,  keys.m_account_address.m_view_public_key);
-  if (!m_watch_only && !m_multisig && hwdev.device_protocol() != hw::device::PROTOCOL_COLD)
+  if (!m_watch_only && !m_multisig && hwdev.device_protocol() != hw::device::PROTOCOL_COLD && !m_is_background_wallet)
     r = r && hwdev.verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spend_public_key);
   THROW_WALLET_EXCEPTION_IF(!r, error::wallet_files_doesnt_correspond, m_keys_file, m_wallet_file);
 
   if (r)
-    setup_keys(password);
+  {
+    if (!m_is_background_wallet)
+      setup_keys(password);
+    else
+      m_custom_background_key = boost::optional<crypto::chacha_key>(key);
+  }
 
   return true;
 }
@@ -5038,11 +5324,12 @@ bool wallet2::load_keys_buf(const std::string& keys_buf, const epee::wipeable_st
  * can be used prior to rewriting wallet keys file, to ensure user has entered the correct password
  *
  */
-bool wallet2::verify_password(const epee::wipeable_string& password)
+bool wallet2::verify_password(const epee::wipeable_string& password, crypto::secret_key &spend_key_out)
 {
   // this temporary unlocking is necessary for Windows (otherwise the file couldn't be loaded).
   unlock_keys_file();
-  bool r = verify_password(m_keys_file, password, m_account.get_device().device_protocol() == hw::device::PROTOCOL_COLD || m_watch_only || m_multisig, m_account.get_device(), m_kdf_rounds);
+  const bool no_spend_key = m_account.get_device().device_protocol() == hw::device::PROTOCOL_COLD || m_watch_only || m_multisig || m_is_background_wallet;
+  bool r = verify_password(m_keys_file, password, no_spend_key, m_account.get_device(), m_kdf_rounds, spend_key_out);
   lock_keys_file();
   return r;
 }
@@ -5060,7 +5347,7 @@ bool wallet2::verify_password(const epee::wipeable_string& password)
  * can be used prior to rewriting wallet keys file, to ensure user has entered the correct password
  *
  */
-bool wallet2::verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds)
+bool wallet2::verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds, crypto::secret_key &spend_key_out)
 {
   rapidjson::Document json;
   wallet2::keys_file_data keys_file_data;
@@ -5077,9 +5364,22 @@ bool wallet2::verify_password(const std::string& keys_file_name, const epee::wip
   std::string account_data;
   account_data.resize(keys_file_data.account_data.size());
   crypto::chacha20(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]);
-  if (json.Parse(account_data.c_str()).HasParseError() || !json.IsObject())
+  const bool try_v0_format = json.Parse(account_data.c_str()).HasParseError() || !json.IsObject();
+  if (try_v0_format)
     crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]);
 
+  // Check if it's a background keys file if both of the above formats fail
+  {
+    cryptonote::account_base account_data_check;
+    if (try_v0_format && !epee::serialization::load_t_from_binary(account_data_check, account_data))
+    {
+      get_custom_background_key(password, key, kdf_rounds);
+      crypto::chacha20(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]);
+      const bool is_background_wallet = json.Parse(account_data.c_str()).HasParseError() && json.IsObject();
+      no_spend_key = no_spend_key || is_background_wallet;
+    }
+  }
+
   // The contents should be JSON if the wallet follows the new format.
   if (json.Parse(account_data.c_str()).HasParseError())
   {
@@ -5104,6 +5404,7 @@ bool wallet2::verify_password(const std::string& keys_file_name, const epee::wip
   r = r && hwdev.verify_keys(keys.m_view_secret_key,  keys.m_account_address.m_view_public_key);
   if(!no_spend_key)
     r = r && hwdev.verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spend_public_key);
+  spend_key_out = (!no_spend_key && r) ? keys.m_spend_secret_key : crypto::null_skey;
   return r;
 }
 
@@ -5115,9 +5416,7 @@ void wallet2::encrypt_keys(const crypto::chacha_key &key)
 
 void wallet2::decrypt_keys(const crypto::chacha_key &key)
 {
-  // We use m_cache_key as a deterministic test to see if given key corresponds to original password
-  const crypto::chacha_key cache_key = derive_cache_key(key);
-  THROW_WALLET_EXCEPTION_IF(cache_key != m_cache_key, error::invalid_password);
+  verify_password_with_cached_key(key);
 
   m_account.encrypt_viewkey(key);
   m_account.decrypt_keys(key);
@@ -5509,32 +5808,77 @@ void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& p
   }
 }
 //----------------------------------------------------------------------------------------------------
-std::string wallet2::make_multisig(const epee::wipeable_string &password,
-  const std::vector<std::string> &initial_kex_msgs,
-  const std::uint32_t threshold)
+epee::misc_utils::auto_scope_leave_caller wallet2::decrypt_account_for_multisig(const epee::wipeable_string &password)
 {
   // decrypt account keys
+  // note: this conditional's clauses are old and undocumented
   epee::misc_utils::auto_scope_leave_caller keys_reencryptor;
   if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
   {
     crypto::chacha_key chacha_key;
     crypto::generate_chacha_key(password.data(), password.size(), chacha_key, m_kdf_rounds);
-    m_account.encrypt_viewkey(chacha_key);
-    m_account.decrypt_keys(chacha_key);
+    this->decrypt_keys(chacha_key);
     keys_reencryptor = epee::misc_utils::create_scope_leave_handler(
-        [&, this, chacha_key]()
+        [this, chacha_key]()
         {
-          m_account.encrypt_keys(chacha_key);
-          m_account.decrypt_viewkey(chacha_key);
+          this->encrypt_keys(chacha_key);
         }
       );
   }
 
-  // create multisig account
-  multisig::multisig_account multisig_account{
-      multisig::get_multisig_blinded_secret_key(get_account().get_keys().m_spend_secret_key),
-      multisig::get_multisig_blinded_secret_key(get_account().get_keys().m_view_secret_key)
+  return keys_reencryptor;
+}
+//----------------------------------------------------------------------------------------------------
+void wallet2::get_uninitialized_multisig_account(multisig::multisig_account &account_out) const
+{
+  // create uninitialized multisig account
+  account_out = multisig::multisig_account{
+      // k_base = H(normal private spend key)
+      multisig::get_multisig_blinded_secret_key(this->get_account().get_keys().m_spend_secret_key),
+      // k_view = H(normal private view key)
+      multisig::get_multisig_blinded_secret_key(this->get_account().get_keys().m_view_secret_key)
     };
+}
+//----------------------------------------------------------------------------------------------------
+void wallet2::get_reconstructed_multisig_account(multisig::multisig_account &account_out) const
+{
+  const multisig::multisig_account_status ms_status{this->get_multisig_status()};
+  CHECK_AND_ASSERT_THROW_MES(ms_status.multisig_is_active,
+    "The wallet is not multisig, so the multisig account couldn't be reconstructed");
+
+  // reconstruct multisig account
+  crypto::public_key common_pubkey;
+  crypto::secret_key_to_public_key(this->get_account().get_keys().m_view_secret_key, common_pubkey);
+
+  multisig::multisig_keyset_map_memsafe_t kex_origins_map;
+  for (const auto &derivation : m_multisig_derivations)
+    kex_origins_map[derivation];
+
+  account_out = multisig::multisig_account{
+      m_multisig_threshold,
+      m_multisig_signers,
+      this->get_account().get_keys().m_spend_secret_key,
+      this->get_account().get_keys().m_view_secret_key,
+      this->get_account().get_keys().m_multisig_keys,
+      this->get_account().get_keys().m_view_secret_key,
+      m_account_public_address.m_spend_public_key,
+      common_pubkey,
+      m_multisig_rounds_passed,
+      std::move(kex_origins_map),
+      ""
+    };
+}
+//----------------------------------------------------------------------------------------------------
+std::string wallet2::make_multisig(const epee::wipeable_string &password,
+  const std::vector<std::string> &initial_kex_msgs,
+  const std::uint32_t threshold)
+{
+  // decrypt account keys
+  epee::misc_utils::auto_scope_leave_caller keys_reencryptor{this->decrypt_account_for_multisig(password)};
+
+  // create multisig account
+  multisig::multisig_account multisig_account;
+  this->get_uninitialized_multisig_account(multisig_account);
 
   // open initial kex messages, validate them, extract signers
   std::vector<multisig::multisig_kex_msg> expanded_msgs;
@@ -5542,7 +5886,7 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
   expanded_msgs.reserve(initial_kex_msgs.size());
   signers.reserve(initial_kex_msgs.size() + 1);
 
-  for (const auto &msg : initial_kex_msgs)
+  for (const std::string &msg : initial_kex_msgs)
   {
     expanded_msgs.emplace_back(msg);
 
@@ -5551,17 +5895,19 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
     CHECK_AND_ASSERT_THROW_MES(expanded_msgs.back().get_round() == 1,
       "Trying to make multisig with message that has invalid multisig kex round (should be '1').");
 
-    // 2. duplicate signers not allowed
+    // 2. duplicate signers not allowed (the number of signers is implied by the number of initial kex messages passed
+    //    in, so we can't just ignore duplicates here)
     CHECK_AND_ASSERT_THROW_MES(std::find(signers.begin(), signers.end(), expanded_msgs.back().get_signing_pubkey()) == signers.end(),
       "Duplicate signers not allowed when converting a wallet to multisig.");
 
-    // add signer (skip self for now)
-    if (expanded_msgs.back().get_signing_pubkey() != multisig_account.get_base_pubkey())
-      signers.push_back(expanded_msgs.back().get_signing_pubkey());
+    // add signer
+    signers.push_back(expanded_msgs.back().get_signing_pubkey());
   }
 
-  // add self to signers
-  signers.push_back(multisig_account.get_base_pubkey());
+  // expect that self is in the input list (this guarantees that the input list size always equals the number of intended
+  //   signers for the account [when combined with duplicate checking])
+  CHECK_AND_ASSERT_THROW_MES(std::find(signers.begin(), signers.end(), multisig_account.get_base_pubkey()) != signers.end(),
+    "The local account's signer key was not found in initial multisig kex messages when converting a wallet to multisig.");
 
   // intialize key exchange
   multisig_account.initialize_kex(threshold, signers, expanded_msgs);
@@ -5572,12 +5918,13 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
   {
     // Save the original i.e. non-multisig keys so the MMS can continue to use them to encrypt and decrypt messages
     // (making a wallet multisig overwrites those keys, see account_base::make_multisig)
-    m_original_address = get_account().get_keys().m_account_address;
-    m_original_view_secret_key = get_account().get_keys().m_view_secret_key;
+    m_original_address = this->get_account().get_keys().m_account_address;
+    m_original_view_secret_key = this->get_account().get_keys().m_view_secret_key;
     m_original_keys_available = true;
   }
 
-  clear();
+  // clear wallet caches
+  this->clear();
 
   // account base
   MINFO("Creating multisig address...");
@@ -5587,14 +5934,14 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
     multisig_account.get_multisig_privkeys()),
       "Failed to create multisig wallet account due to bad keys");
 
-  init_type(hw::device::device_type::SOFTWARE);
+  this->init_type(hw::device::device_type::SOFTWARE);
   m_original_keys_available = true;
   m_multisig = true;
   m_multisig_threshold = threshold;
   m_multisig_signers = signers;
   m_multisig_rounds_passed = 1;
 
-  // derivations stored (should be empty in last round)
+  // derivations stored (note: should be empty in last kex round)
   m_multisig_derivations.clear();
   m_multisig_derivations.reserve(multisig_account.get_kex_keys_to_origins_map().size());
 
@@ -5608,12 +5955,12 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
   keys_reencryptor = epee::misc_utils::auto_scope_leave_caller();
 
   if (!m_wallet_file.empty())
-    create_keys_file(m_wallet_file, false, password, boost::filesystem::exists(m_wallet_file + ".address.txt"));
+    this->create_keys_file(m_wallet_file, false, password, boost::filesystem::exists(m_wallet_file + ".address.txt"));
 
-  setup_new_blockchain();
+  this->setup_new_blockchain();
 
   if (!m_wallet_file.empty())
-    store();
+    this->store();
 
   return multisig_account.get_next_kex_round_msg();
 }
@@ -5622,45 +5969,15 @@ std::string wallet2::exchange_multisig_keys(const epee::wipeable_string &passwor
   const std::vector<std::string> &kex_messages,
   const bool force_update_use_with_caution /*= false*/)
 {
-  const multisig::multisig_account_status ms_status{get_multisig_status()};
+  const multisig::multisig_account_status ms_status{this->get_multisig_status()};
   CHECK_AND_ASSERT_THROW_MES(ms_status.multisig_is_active, "The wallet is not multisig");
 
   // decrypt account keys
-  epee::misc_utils::auto_scope_leave_caller keys_reencryptor;
-  if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
-  {
-    crypto::chacha_key chacha_key;
-    crypto::generate_chacha_key(password.data(), password.size(), chacha_key, m_kdf_rounds);
-    m_account.encrypt_viewkey(chacha_key);
-    m_account.decrypt_keys(chacha_key);
-    keys_reencryptor = epee::misc_utils::create_scope_leave_handler(
-        [&, this, chacha_key]()
-        {
-          m_account.encrypt_keys(chacha_key);
-          m_account.decrypt_viewkey(chacha_key);
-        }
-      );
-  }
+  epee::misc_utils::auto_scope_leave_caller keys_reencryptor{this->decrypt_account_for_multisig(password)};
 
   // reconstruct multisig account
-  multisig::multisig_keyset_map_memsafe_t kex_origins_map;
-
-  for (const auto &derivation : m_multisig_derivations)
-    kex_origins_map[derivation];
-
-  multisig::multisig_account multisig_account{
-      m_multisig_threshold,
-      m_multisig_signers,
-      get_account().get_keys().m_spend_secret_key,
-      crypto::null_skey,  //base common privkey: not used
-      get_account().get_keys().m_multisig_keys,
-      get_account().get_keys().m_view_secret_key,
-      m_account_public_address.m_spend_public_key,
-      m_account_public_address.m_view_public_key,
-      m_multisig_rounds_passed,
-      std::move(kex_origins_map),
-      ""
-    };
+  multisig::multisig_account multisig_account;
+  this->get_reconstructed_multisig_account(multisig_account);
 
   // KLUDGE: early return if there are no kex messages and main kex is complete (will return the post-kex verification round
   //         message) (it's a kludge because this behavior would be more appropriate for a standalone wallet method)
@@ -5676,7 +5993,7 @@ std::string wallet2::exchange_multisig_keys(const epee::wipeable_string &passwor
   std::vector<multisig::multisig_kex_msg> expanded_msgs;
   expanded_msgs.reserve(kex_messages.size());
 
-  for (const auto &msg : kex_messages)
+  for (const std::string &msg : kex_messages)
     expanded_msgs.emplace_back(msg);
 
   // update multisig kex
@@ -5712,27 +6029,27 @@ std::string wallet2::exchange_multisig_keys(const epee::wipeable_string &passwor
 
     if (!m_wallet_file.empty())
     {
-      bool r = store_keys(m_keys_file, password, false);
+      bool r = this->store_keys(m_keys_file, password, false);
       THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file);
 
       if (boost::filesystem::exists(m_wallet_file + ".address.txt"))
       {
-        r = save_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype), true);
+        r = this->save_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_nettype), true);
         if(!r) MERROR("String with address text not saved");
       }
     }
 
     m_subaddresses.clear();
     m_subaddress_labels.clear();
-    add_subaddress_account(tr("Primary account"));
+    this->add_subaddress_account(tr("Primary account"));
 
     if (!m_wallet_file.empty())
-      store();
+      this->store();
   }
 
   // wallet/file relationship
   if (!m_wallet_file.empty())
-    create_keys_file(m_wallet_file, false, password, boost::filesystem::exists(m_wallet_file + ".address.txt"));
+    this->create_keys_file(m_wallet_file, false, password, boost::filesystem::exists(m_wallet_file + ".address.txt"));
 
   return multisig_account.get_next_kex_round_msg();
 }
@@ -5740,16 +6057,63 @@ std::string wallet2::exchange_multisig_keys(const epee::wipeable_string &passwor
 std::string wallet2::get_multisig_first_kex_msg() const
 {
   // create multisig account
-  multisig::multisig_account multisig_account{
-      // k_base = H(normal private spend key)
-      multisig::get_multisig_blinded_secret_key(get_account().get_keys().m_spend_secret_key),
-      // k_view = H(normal private view key)
-      multisig::get_multisig_blinded_secret_key(get_account().get_keys().m_view_secret_key)
-    };
+  multisig::multisig_account multisig_account;
+  this->get_uninitialized_multisig_account(multisig_account);
 
   return multisig_account.get_next_kex_round_msg();
 }
 //----------------------------------------------------------------------------------------------------
+std::string wallet2::get_multisig_key_exchange_booster(const epee::wipeable_string &password,
+  const std::vector<std::string> &kex_messages,
+  const std::uint32_t threshold,
+  const std::uint32_t num_signers)
+{
+  CHECK_AND_ASSERT_THROW_MES(kex_messages.size() > 0, "No key exchange messages passed in.");
+
+  // decrypt account keys
+  epee::misc_utils::auto_scope_leave_caller keys_reencryptor{this->decrypt_account_for_multisig(password)};
+
+  // prepare multisig account
+  multisig::multisig_account multisig_account;
+
+  const multisig::multisig_account_status ms_status{this->get_multisig_status()};
+  CHECK_AND_ASSERT_THROW_MES(!ms_status.is_ready, "Multisig wallet creation process has already been finished.");
+
+  if (ms_status.multisig_is_active)
+  {
+    // case: this wallet is in the middle of multisig key exchange
+    // - boost the round that comes after the in-progress round
+
+    CHECK_AND_ASSERT_THROW_MES(threshold == m_multisig_threshold,
+      "Expected threshold does not match multisig wallet setting.");
+    CHECK_AND_ASSERT_THROW_MES(num_signers == m_multisig_signers.size(),
+      "Expected number of signers does not match multisig wallet setting.");
+
+    // reconstruct multisig account
+    this->get_reconstructed_multisig_account(multisig_account);
+  }
+  else
+  {
+    // case: make_multisig() has not been called
+    // DANGER: If 'num_signers - threshold > 1', but this wallet's future multisig settings
+    //         will be 'num_signers - threshold == 1', then the booster message WILL leak the
+    //         future multisig wallet's private keys in this case where the wallet2 multisig wallet is uninitialized.
+
+    this->get_uninitialized_multisig_account(multisig_account);
+  }
+
+  // open kex messages
+  std::vector<multisig::multisig_kex_msg> expanded_msgs;
+  expanded_msgs.reserve(kex_messages.size());
+
+  for (const std::string &msg : kex_messages)
+    expanded_msgs.emplace_back(msg);
+
+  // get kex booster message
+  // note: booster does not change wallet state other than decrypting/reencrypting account keys
+  return multisig_account.get_multisig_kex_round_booster(threshold, num_signers, expanded_msgs).get_msg();
+}
+//----------------------------------------------------------------------------------------------------
 multisig::multisig_account_status wallet2::get_multisig_status() const
 {
   multisig::multisig_account_status ret;
@@ -5803,11 +6167,30 @@ void wallet2::rewrite(const std::string& wallet_name, const epee::wipeable_strin
 {
   if (wallet_name.empty())
     return;
+  THROW_WALLET_EXCEPTION_IF(m_background_syncing || m_is_background_wallet, error::wallet_internal_error,
+    "cannot change wallet settings from background wallet");
   prepare_file_names(wallet_name);
   boost::system::error_code ignored_ec;
   THROW_WALLET_EXCEPTION_IF(!boost::filesystem::exists(m_keys_file, ignored_ec), error::file_not_found, m_keys_file);
   bool r = store_keys(m_keys_file, password, m_watch_only);
   THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file);
+
+  // Update the background keys file when we rewrite the main wallet keys file
+  if (m_background_sync_type == BackgroundSyncCustomPassword && m_custom_background_key)
+  {
+    const std::string background_keys_filename = make_background_keys_file_name(wallet_name);
+    if (!lock_background_keys_file(background_keys_filename))
+    {
+      LOG_ERROR("Background keys file " << background_keys_filename << " is opened by another wallet program and cannot be rewritten");
+      return; // not fatal, background keys file will just have different wallet settings
+    }
+    store_background_keys(m_custom_background_key.get());
+    store_background_cache(m_custom_background_key.get(), true/*do_reset_background_sync_data*/);
+  }
+  else if (m_background_sync_type == BackgroundSyncReusePassword)
+  {
+    reset_background_sync_data(m_background_sync_data);
+  }
 }
 /*!
  * \brief Writes to a file named based on the normal wallet (doesn't generate key, assumes it's already there)
@@ -5841,6 +6224,16 @@ bool wallet2::wallet_valid_path_format(const std::string& file_path)
   return !file_path.empty();
 }
 //----------------------------------------------------------------------------------------------------
+std::string wallet2::make_background_wallet_file_name(const std::string &wallet_file)
+{
+  return wallet_file + BACKGROUND_WALLET_SUFFIX;
+}
+//----------------------------------------------------------------------------------------------------
+std::string wallet2::make_background_keys_file_name(const std::string &wallet_file)
+{
+  return make_background_wallet_file_name(wallet_file) + ".keys";
+}
+//----------------------------------------------------------------------------------------------------
 bool wallet2::parse_long_payment_id(const std::string& payment_id_str, crypto::hash& payment_id)
 {
   cryptonote::blobdata payment_id_data;
@@ -6066,10 +6459,78 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
     THROW_WALLET_EXCEPTION_IF(true, error::file_read_error, "failed to load keys from buffer");
   }
 
-  wallet_keys_unlocker unlocker(*this, m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only, password);
+  wallet_keys_unlocker unlocker(*this, m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only && !m_is_background_wallet, password);
 
   //keys loaded ok!
   //try to load wallet cache. but even if we failed, it is not big problem
+  load_wallet_cache(use_fs, cache_buf);
+
+  // Wallets used to wipe, but not erase, old unused multisig key info, which lead to huge memory leaks.
+  // Here we erase these multisig keys if they're zero'd out to free up space.
+  for (auto &td : m_transfers)
+  {
+    auto mk_it = td.m_multisig_k.begin();
+    while (mk_it != td.m_multisig_k.end())
+    {
+      if (*mk_it == rct::zero())
+        mk_it = td.m_multisig_k.erase(mk_it);
+      else
+        ++mk_it;
+    }
+  }
+
+  cryptonote::block genesis;
+  generate_genesis(genesis);
+  crypto::hash genesis_hash = get_block_hash(genesis);
+
+  if (m_blockchain.empty())
+  {
+    m_blockchain.push_back(genesis_hash);
+    m_last_block_reward = cryptonote::get_outs_money_amount(genesis.miner_tx);
+  }
+  else
+  {
+    check_genesis(genesis_hash);
+  }
+
+  trim_hashchain();
+
+  if (get_num_subaddress_accounts() == 0)
+    add_subaddress_account(tr("Primary account"));
+
+  try
+  {
+    find_and_save_rings(false);
+  }
+  catch (const std::exception &e)
+  {
+    MERROR("Failed to save rings, will try again next time");
+  }
+
+  try
+  {
+    if (use_fs)
+      m_message_store.read_from_file(get_multisig_wallet_state(), m_mms_file, m_load_deprecated_formats);
+  }
+  catch (const std::exception &e)
+  {
+    MERROR("Failed to initialize MMS, it will be unusable");
+  }
+
+  try
+  {
+    if (use_fs)
+      process_background_cache_on_open();
+  }
+  catch (const std::exception &e)
+  {
+    MERROR("Failed to process background cache on open: " << e.what());
+  }
+}
+//----------------------------------------------------------------------------------------------------
+void wallet2::load_wallet_cache(const bool use_fs, const std::string& cache_buf)
+{
+  boost::system::error_code e;
   bool cache_missing = use_fs ? (!boost::filesystem::exists(m_wallet_file, e) || e) : cache_buf.empty();
   if (cache_missing)
   {
@@ -6083,7 +6544,7 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
     bool r = true;
     if (use_fs)
     {
-      load_from_file(m_wallet_file, cache_file_buf, std::numeric_limits<size_t>::max());
+      r = load_from_file(m_wallet_file, cache_file_buf, std::numeric_limits<size_t>::max());
       THROW_WALLET_EXCEPTION_IF(!r, error::file_read_error, m_wallet_file);
     }
 
@@ -6096,7 +6557,7 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
       THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "internal error: failed to deserialize \"" + m_wallet_file + '\"');
       std::string cache_data;
       cache_data.resize(cache_file_data.cache_data.size());
-      crypto::chacha20(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), m_cache_key, cache_file_data.iv, &cache_data[0]);
+      crypto::chacha20(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), get_cache_key(), cache_file_data.iv, &cache_data[0]);
 
       try {
         bool loaded = false;
@@ -6186,57 +6647,76 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
       m_account_public_address.m_view_public_key  != m_account.get_keys().m_account_address.m_view_public_key,
       error::wallet_files_doesnt_correspond, m_keys_file, m_wallet_file);
   }
+}
+//----------------------------------------------------------------------------------------------------
+void wallet2::process_background_cache_on_open()
+{
+  if (m_wallet_file.empty())
+    return;
+  if (m_background_syncing || m_is_background_wallet)
+    return;
+  if (m_background_sync_type == BackgroundSyncOff)
+    return;
 
-  // Wallets used to wipe, but not erase, old unused multisig key info, which lead to huge memory leaks.
-  // Here we erase these multisig keys if they're zero'd out to free up space.
-  for (auto &td : m_transfers)
+  if (m_background_sync_type == BackgroundSyncReusePassword)
   {
-    auto mk_it = td.m_multisig_k.begin();
-    while (mk_it != td.m_multisig_k.end())
-    {
-      if (*mk_it == rct::zero())
-        mk_it = td.m_multisig_k.erase(mk_it);
-      else
-        ++mk_it;
-    }
+    const background_sync_data_t background_sync_data = m_background_sync_data;
+    const hashchain blockchain = m_blockchain;
+    process_background_cache(background_sync_data, blockchain, m_last_block_reward);
+
+    // Reset the background cache after processing
+    reset_background_sync_data(m_background_sync_data);
   }
-
-  cryptonote::block genesis;
-  generate_genesis(genesis);
-  crypto::hash genesis_hash = get_block_hash(genesis);
-
-  if (m_blockchain.empty())
+  else if (m_background_sync_type == BackgroundSyncCustomPassword)
   {
-    m_blockchain.push_back(genesis_hash);
-    m_last_block_reward = cryptonote::get_outs_money_amount(genesis.miner_tx);
+    // If the background wallet files don't exist, recreate them
+    const std::string background_keys_file = make_background_keys_file_name(m_wallet_file);
+    const std::string background_wallet_file = make_background_wallet_file_name(m_wallet_file);
+    const bool background_keys_file_exists = boost::filesystem::exists(background_keys_file);
+    const bool background_wallet_exists = boost::filesystem::exists(background_wallet_file);
+
+    THROW_WALLET_EXCEPTION_IF(!lock_background_keys_file(background_keys_file), error::background_wallet_already_open, background_wallet_file);
+    THROW_WALLET_EXCEPTION_IF(!m_custom_background_key, error::wallet_internal_error, "Custom background key not set");
+
+    if (!background_keys_file_exists)
+    {
+      MDEBUG("Background keys file not found, restoring");
+      store_background_keys(m_custom_background_key.get());
+    }
+
+    if (!background_wallet_exists)
+    {
+      MDEBUG("Background cache not found, restoring");
+      store_background_cache(m_custom_background_key.get(), true/*do_reset_background_sync_data*/);
+      return;
+    }
+
+    MDEBUG("Loading background cache");
+
+    // Set up a minimal background wallet2 instance
+    std::unique_ptr<wallet2> background_w2(new wallet2(m_nettype));
+    background_w2->m_is_background_wallet = true;
+    background_w2->m_background_syncing = true;
+    background_w2->m_background_sync_type = m_background_sync_type;
+    background_w2->m_custom_background_key = m_custom_background_key;
+
+    cryptonote::account_base account = m_account;
+    account.forget_spend_key();
+    background_w2->m_account = account;
+
+    // Load background cache from file
+    background_w2->clear();
+    background_w2->prepare_file_names(background_wallet_file);
+    background_w2->load_wallet_cache(true/*use_fs*/);
+
+    process_background_cache(background_w2->m_background_sync_data, background_w2->m_blockchain, background_w2->m_last_block_reward);
+
+    // Reset the background cache after processing
+    store_background_cache(m_custom_background_key.get(), true/*do_reset_background_sync_data*/);
   }
   else
   {
-    check_genesis(genesis_hash);
-  }
-
-  trim_hashchain();
-
-  if (get_num_subaddress_accounts() == 0)
-    add_subaddress_account(tr("Primary account"));
-
-  try
-  {
-    find_and_save_rings(false);
-  }
-  catch (const std::exception &e)
-  {
-    MERROR("Failed to save rings, will try again next time");
-  }
-  
-  try
-  {
-    if (use_fs)
-      m_message_store.read_from_file(get_multisig_wallet_state(), m_mms_file, m_load_deprecated_formats);
-  }
-  catch (const std::exception &e)
-  {
-    MERROR("Failed to initialize MMS, it will be unusable");
+    THROW_WALLET_EXCEPTION(error::wallet_internal_error, "unknown background sync type");
   }
 }
 //----------------------------------------------------------------------------------------------------
@@ -6318,6 +6798,8 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas
     same_file = canonical_old_path == canonical_new_path;
   }
 
+  THROW_WALLET_EXCEPTION_IF(m_is_background_wallet && !same_file, error::wallet_internal_error,
+    "Cannot save background wallet files to a different location");
 
   if (!same_file)
   {
@@ -6334,6 +6816,21 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas
       }
     }
   }
+  else if (m_background_sync_type == BackgroundSyncCustomPassword && m_background_syncing && !m_is_background_wallet)
+  {
+    // We're background syncing, so store the wallet cache as a background cache
+    // keeping the background sync data
+    try
+    {
+      THROW_WALLET_EXCEPTION_IF(!m_custom_background_key, error::wallet_internal_error, "Custom background key not set");
+      store_background_cache(m_custom_background_key.get(), false/*do_reset_background_sync_data*/);
+    }
+    catch (const std::exception &e)
+    {
+      MERROR("Failed to store background cache while background syncing: " << e.what());
+    }
+    return;
+  }
 
   // get wallet cache data
   boost::optional<wallet2::cache_file_data> cache_file_data = get_cache_file_data();
@@ -6427,6 +6924,22 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas
     // store should only exist if the MMS is really active
     m_message_store.write_to_file(get_multisig_wallet_state(), m_mms_file);
   }
+
+  if (m_background_sync_type == BackgroundSyncCustomPassword && !m_background_syncing && !m_is_background_wallet)
+  {
+    // Update the background wallet cache when we store the main wallet cache
+    // Note: if background syncing when this is called, it means the background
+    // wallet is open and was already stored above
+    try
+    {
+      THROW_WALLET_EXCEPTION_IF(!m_custom_background_key, error::wallet_internal_error, "Custom background key not set");
+      store_background_cache(m_custom_background_key.get(), true/*do_reset_background_sync_data*/);
+    }
+    catch (const std::exception &e)
+    {
+      MERROR("Failed to update background cache: " << e.what());
+    }
+  }
 }
 //----------------------------------------------------------------------------------------------------
 boost::optional<wallet2::cache_file_data> wallet2::get_cache_file_data()
@@ -6444,7 +6957,7 @@ boost::optional<wallet2::cache_file_data> wallet2::get_cache_file_data()
     std::string cipher;
     cipher.resize(cache_file_data.get().cache_data.size());
     cache_file_data.get().iv = crypto::rand<crypto::chacha_iv>();
-    crypto::chacha20(cache_file_data.get().cache_data.data(), cache_file_data.get().cache_data.size(), m_cache_key, cache_file_data.get().iv, &cipher[0]);
+    crypto::chacha20(cache_file_data.get().cache_data.data(), cache_file_data.get().cache_data.size(), get_cache_key(), cache_file_data.get().iv, &cipher[0]);
     cache_file_data.get().cache_data = cipher;
     return cache_file_data;
   }
@@ -6688,7 +7201,7 @@ void wallet2::rescan_spent()
     {
       const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
       bool r = epee::net_utils::invoke_http_json("/is_key_image_spent", req, daemon_resp, *m_http_client, rpc_timeout);
-      THROW_ON_RPC_RESPONSE_ERROR(r, {}, daemon_resp, "is_key_image_spent", error::is_key_image_spent_error, get_rpc_status(daemon_resp.status));
+      THROW_ON_RPC_RESPONSE_ERROR(r, {}, daemon_resp, "is_key_image_spent", error::is_key_image_spent_error, get_rpc_status(m_trusted_daemon, daemon_resp.status));
       THROW_WALLET_EXCEPTION_IF(daemon_resp.spent_status.size() != n_outputs, error::wallet_internal_error,
         "daemon returned wrong response for is_key_image_spent, wrong amounts count = " +
         std::to_string(daemon_resp.spent_status.size()) + ", expected " +  std::to_string(n_outputs));
@@ -7018,7 +7531,7 @@ void wallet2::commit_tx(pending_tx& ptx)
   {
     const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
     bool r = epee::net_utils::invoke_http_json("/sendrawtransaction", req, daemon_send_resp, *m_http_client, rpc_timeout);
-    THROW_ON_RPC_RESPONSE_ERROR(r, {}, daemon_send_resp, "sendrawtransaction", error::tx_rejected, ptx.tx, get_rpc_status(daemon_send_resp.status), get_text_reason(daemon_send_resp));
+    THROW_ON_RPC_RESPONSE_ERROR(r, {}, daemon_send_resp, "sendrawtransaction", error::tx_rejected, ptx.tx, get_rpc_status(m_trusted_daemon, daemon_send_resp.status), get_text_reason(daemon_send_resp));
   }
 
   // sanity checks
@@ -7938,7 +8451,7 @@ bool wallet2::sign_multisig_tx_from_file(const std::string &filename, std::vecto
   return sign_multisig_tx_to_file(exported_txs, filename, txids);
 }
 //----------------------------------------------------------------------------------------------------
-uint64_t wallet2::estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag, bool bulletproof_plus, bool use_view_tags, uint64_t base_fee, uint64_t fee_quantization_mask) const
+uint64_t wallet2::estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag, bool bulletproof_plus, bool use_view_tags, uint64_t base_fee, uint64_t fee_quantization_mask)
 {
   if (use_per_byte_fee)
   {
@@ -8158,7 +8671,7 @@ uint32_t wallet2::adjust_priority(uint32_t priority)
       {
         const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
         bool r = net_utils::invoke_http_json_rpc("/json_rpc", "getblockheadersrange", getbh_req, getbh_res, *m_http_client, rpc_timeout);
-        THROW_ON_RPC_RESPONSE_ERROR(r, {}, getbh_res, "getblockheadersrange", error::get_blocks_error, get_rpc_status(getbh_res.status));
+        THROW_ON_RPC_RESPONSE_ERROR(r, {}, getbh_res, "getblockheadersrange", error::get_blocks_error, get_rpc_status(m_trusted_daemon, getbh_res.status));
       }
 
       if (getbh_res.headers.size() != N)
@@ -8495,6 +9008,34 @@ bool wallet2::is_keys_file_locked() const
   return m_keys_file_locker->locked();
 }
 
+bool wallet2::lock_background_keys_file(const std::string &background_keys_file)
+{
+  if (background_keys_file.empty() || !boost::filesystem::exists(background_keys_file))
+    return true;
+  if (m_background_keys_file_locker && m_background_keys_file_locker->locked())
+    return true;
+  m_background_keys_file_locker.reset(new tools::file_locker(background_keys_file));
+  return m_background_keys_file_locker->locked();
+}
+
+bool wallet2::unlock_background_keys_file()
+{
+  if (!m_background_keys_file_locker)
+  {
+    MDEBUG("background keys file locker is not set");
+    return false;
+  }
+  m_background_keys_file_locker.reset();
+  return true;
+}
+
+bool wallet2::is_background_keys_file_locked() const
+{
+  if (!m_background_keys_file_locker)
+    return false;
+  return m_background_keys_file_locker->locked();
+}
+
 bool wallet2::tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, uint64_t global_index, const crypto::public_key& output_public_key, const rct::key& mask, uint64_t real_index, bool unlocked, std::unordered_set<crypto::public_key> &valid_public_keys_cache) const
 {
   if (!unlocked) // don't add locked outs
@@ -8628,7 +9169,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
       {
         const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
         bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, *m_http_client, rpc_timeout);
-        THROW_ON_RPC_RESPONSE_ERROR(r, {}, resp_t, "get_output_histogram", error::get_histogram_error, get_rpc_status(resp_t.status));
+        THROW_ON_RPC_RESPONSE_ERROR(r, {}, resp_t, "get_output_histogram", error::get_histogram_error, get_rpc_status(m_trusted_daemon, resp_t.status));
       }
     }
 
@@ -8652,7 +9193,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
       {
         const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
         bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_distribution", req_t, resp_t, *m_http_client, rpc_timeout * 1000);
-        THROW_ON_RPC_RESPONSE_ERROR(r, {}, resp_t, "get_output_distribution", error::get_output_distribution, get_rpc_status(resp_t.status));
+        THROW_ON_RPC_RESPONSE_ERROR(r, {}, resp_t, "get_output_distribution", error::get_output_distribution, get_rpc_status(m_trusted_daemon, resp_t.status));
       }
 
       // check we got all data
@@ -9059,7 +9600,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
 
       const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
       bool r = epee::net_utils::invoke_http_bin("/get_outs.bin", chunk_req, chunk_daemon_resp, *m_http_client, rpc_timeout);
-      THROW_ON_RPC_RESPONSE_ERROR(r, {}, chunk_daemon_resp, "get_outs.bin", error::get_outs_error, get_rpc_status(chunk_daemon_resp.status));
+      THROW_ON_RPC_RESPONSE_ERROR(r, {}, chunk_daemon_resp, "get_outs.bin", error::get_outs_error, get_rpc_status(m_trusted_daemon, chunk_daemon_resp.status));
       THROW_WALLET_EXCEPTION_IF(chunk_daemon_resp.outs.size() != chunk_req.outputs.size(), error::wallet_internal_error,
         "daemon returned wrong response for get_outs.bin, wrong amounts count = " +
         std::to_string(chunk_daemon_resp.outs.size()) + ", expected " +  std::to_string(chunk_req.outputs.size()));
@@ -13176,6 +13717,413 @@ bool wallet2::import_key_images(signed_tx_set & signed_tx, size_t offset, bool o
   return import_key_images(signed_tx.key_images, offset, only_selected_transfers ? boost::make_optional(selected_transfers) : boost::none);
 }
 
+/*
+  In background sync mode, we use just the view key when the wallet is scanning
+  to identify all txs where:
+
+    1. We received an output.
+    2. We spent an output.
+    3. We *may* have spent a received output but we didn't know for sure because
+       the spend key was not loaded while background sync was enabled.
+
+  When the user is ready to use the spend key again, we call this function to
+  process all those background synced transactions with the spend key loaded,
+  so that we can properly generate key images for the transactions which we
+  we were not able to do so for while background sync was enabled. This allows
+  us to determine *all* receives and spends the user completed while the wallet
+  had background sync enabled. Once this function completes, we can continue
+  scanning from where the background sync left off.
+
+  Txs of type 3 (txs which we *may* have spent received output(s)) are txs where
+  1+ rings contain an output that the user received and the wallet does not know
+  the associated key image for that output. We don't know if the user spent in
+  this type of tx or not. This function will generate key images for all outputs
+  we don't know key images for, and then check if those outputs were spent in
+  the txs of type 3.
+
+  By storing this type of "plausible spend tx" when scanning in background sync
+  mode, we avoid the need to query the daemon with key images when background
+  sync mode is disabled to see if those key images were spent. This would
+  reveal key images to 3rd party nodes for users who don't run their own.
+  Although this is not a perfect solution to avoid revealing key images to a 3rd
+  party node (since tx submission trivially reveals key images to a node), it's
+  probably better than revealing *unused* key images to a 3rd party node, which
+  would enable the 3rd party to deduce that a tx is spending an output at least
+  X old when the key image is included in the chain.
+*/
+void wallet2::process_background_cache(const background_sync_data_t &background_sync_data, const hashchain &background_synced_chain, uint64_t last_block_reward)
+{
+  // We expect the spend key to be in a decrypted state while
+  // m_processing_background_cache is true
+  m_processing_background_cache = true;
+  auto done_processing = epee::misc_utils::create_scope_leave_handler([&, this]() {
+    m_processing_background_cache = false;
+  });
+
+  if (m_background_syncing || m_multisig || m_watch_only || key_on_device())
+    return;
+
+  if (!background_sync_data.first_refresh_done)
+  {
+    MDEBUG("Skipping processing background cache, background cache has not synced yet");
+    return;
+  }
+
+  // Skip processing if wallet cache is synced higher than background cache
+  const uint64_t current_height = m_blockchain.size();
+  const uint64_t background_height = background_synced_chain.size();
+  MDEBUG("Background cache height " << background_height << " , wallet height " << current_height);
+  if (current_height > background_height)
+  {
+    MWARNING("Skipping processing background cache, synced height is higher than background cache");
+    return;
+  }
+
+  if (m_refresh_from_block_height  < background_sync_data.wallet_refresh_from_block_height ||
+      m_subaddress_lookahead_major > background_sync_data.subaddress_lookahead_major ||
+      m_subaddress_lookahead_minor > background_sync_data.subaddress_lookahead_minor ||
+      m_refresh_type               < background_sync_data.wallet_refresh_type)
+  {
+    MWARNING("Skipping processing background cache, background wallet sync settings did not match main wallet's");
+    MDEBUG("Wallet settings: " <<
+      ", m_refresh_from_block_height: "  << m_refresh_from_block_height  << " vs " << background_sync_data.wallet_refresh_from_block_height <<
+      ", m_subaddress_lookahead_major: " << m_subaddress_lookahead_major << " vs " << background_sync_data.subaddress_lookahead_major <<
+      ", m_subaddress_lookahead_minor: " << m_subaddress_lookahead_minor << " vs " << background_sync_data.subaddress_lookahead_minor <<
+      ", m_refresh_type: "               << m_refresh_type               << " vs " << background_sync_data.wallet_refresh_type);
+    return;
+  }
+
+  // Sort background synced txs in the order they appeared in the cache so that
+  // we process them in the order they appeared in the chain. Thus if tx2 spends
+  // from tx1, we will know because tx1 is processed before tx2.
+  std::vector<std::pair<crypto::hash, background_synced_tx_t>> sorted_bgs_cache(background_sync_data.txs.begin(), background_sync_data.txs.end());
+  std::sort(sorted_bgs_cache.begin(), sorted_bgs_cache.end(),
+    [](const std::pair<crypto::hash, background_synced_tx_t>& l, const std::pair<crypto::hash, background_synced_tx_t>& r)
+      {
+        uint64_t left_index = l.second.index_in_background_sync_data;
+        uint64_t right_index = r.second.index_in_background_sync_data;
+        THROW_WALLET_EXCEPTION_IF(
+            (left_index < right_index && l.second.height > r.second.height) ||
+            (left_index > right_index && l.second.height < r.second.height),
+            error::wallet_internal_error, "Unexpected background sync data order");
+        return left_index < right_index;
+      });
+
+  // All txs in the background cache should have height >= sync start height,
+  // but not fatal if not
+  if (!sorted_bgs_cache.empty() && sorted_bgs_cache[0].second.height < background_sync_data.start_height)
+    MWARNING("First tx in background cache has height (" << sorted_bgs_cache[0].second.height << ") lower than sync start height (" << background_sync_data.start_height << ")");
+
+  // We want to process all background synced txs in order to make sure
+  // the wallet state updates correctly. First we remove all txs from the wallet
+  // from before the background sync start height, then re-process them in
+  // chronological order. The background cache should contain a superset of
+  // *all* the wallet's txs from after the background sync start height.
+  MDEBUG("Processing " << background_sync_data.txs.size() << " background synced txs starting from height " << background_sync_data.start_height);
+  detached_blockchain_data dbd = detach_blockchain(background_sync_data.start_height);
+
+  for (const auto &bgs_tx : sorted_bgs_cache)
+  {
+    MDEBUG("Processing background synced tx " << bgs_tx.first);
+
+    process_new_transaction(bgs_tx.first, bgs_tx.second.tx, bgs_tx.second.output_indices, bgs_tx.second.height, 0, bgs_tx.second.block_timestamp,
+        cryptonote::is_coinbase(bgs_tx.second.tx), false/*pool*/, bgs_tx.second.double_spend_seen, {}, {}, true/*ignore_callbacks*/);
+
+    // Re-set destination addresses if they were previously set
+    if (m_confirmed_txs.find(bgs_tx.first) != m_confirmed_txs.end() &&
+        dbd.detached_confirmed_txs_dests.find(bgs_tx.first) != dbd.detached_confirmed_txs_dests.end())
+    {
+      m_confirmed_txs[bgs_tx.first].m_dests = std::move(dbd.detached_confirmed_txs_dests[bgs_tx.first]);
+    }
+  }
+
+  m_blockchain = background_synced_chain;
+  m_last_block_reward = last_block_reward;
+
+  MDEBUG("Finished processing background sync data");
+}
+//----------------------------------------------------------------------------------------------------
+void wallet2::reset_background_sync_data(background_sync_data_t &background_sync_data)
+{
+  background_sync_data.first_refresh_done = false;
+  background_sync_data.start_height = get_blockchain_current_height();
+  background_sync_data.txs.clear();
+
+  background_sync_data.wallet_refresh_from_block_height = m_refresh_from_block_height;
+  background_sync_data.subaddress_lookahead_major = m_subaddress_lookahead_major;
+  background_sync_data.subaddress_lookahead_minor = m_subaddress_lookahead_minor;
+  background_sync_data.wallet_refresh_type = m_refresh_type;
+}
+//----------------------------------------------------------------------------------------------------
+void wallet2::store_background_cache(const crypto::chacha_key &custom_background_key, const bool do_reset_background_sync_data)
+{
+  MDEBUG("Storing background cache (do_reset_background_sync_data=" << do_reset_background_sync_data << ")");
+
+  THROW_WALLET_EXCEPTION_IF(m_background_sync_type != BackgroundSyncCustomPassword, error::wallet_internal_error,
+      "Can only write a background cache when using a custom background password");
+  THROW_WALLET_EXCEPTION_IF(m_wallet_file.empty(), error::wallet_internal_error,
+      "No wallet file known, can't store background cache");
+
+  std::unique_ptr<wallet2> background_w2(new wallet2(m_nettype));
+  background_w2->prepare_file_names(make_background_wallet_file_name(m_wallet_file));
+
+  // Make sure background wallet is opened by this wallet
+  THROW_WALLET_EXCEPTION_IF(!lock_background_keys_file(background_w2->m_keys_file),
+      error::background_wallet_already_open, background_w2->m_wallet_file);
+
+  // Load a background wallet2 instance using this wallet2 instance
+  std::string this_wallet2;
+  bool r = ::serialization::dump_binary(*this, this_wallet2);
+  THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to serialize wallet cache");
+
+  background_w2->clear();
+  r = ::serialization::parse_binary(this_wallet2, *background_w2);
+  THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to deserialize wallet cache");
+
+  // Clear sensitive data from background cache not needed to sync
+  background_w2->clear_user_data();
+
+  background_w2->m_is_background_wallet = true;
+  if (do_reset_background_sync_data)
+    reset_background_sync_data(background_w2->m_background_sync_data);
+  else
+    background_w2->m_background_sync_data = m_background_sync_data;
+  background_w2->m_background_syncing = true;
+
+  background_w2->m_custom_background_key = boost::optional<crypto::chacha_key>(custom_background_key);
+  background_w2->m_background_sync_type = m_background_sync_type;
+  background_w2->store();
+
+  MDEBUG("Background cache stored (" << background_w2->m_transfers.size() << " transfers, " << background_w2->m_background_sync_data.txs.size() << " background synced txs)");
+}
+//----------------------------------------------------------------------------------------------------
+void wallet2::store_background_keys(const crypto::chacha_key &custom_background_key)
+{
+  MDEBUG("Storing background keys");
+
+  THROW_WALLET_EXCEPTION_IF(m_wallet_file.empty(), error::wallet_internal_error,
+      "No wallet file known, can't store background keys");
+
+  const std::string background_keys_file = make_background_keys_file_name(m_wallet_file);
+  bool r = store_keys(background_keys_file, custom_background_key, false/*watch_only*/, true/*background_keys_file*/);
+  THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, background_keys_file);
+  THROW_WALLET_EXCEPTION_IF(!is_background_keys_file_locked(), error::wallet_internal_error, background_keys_file + "\" should be locked");
+
+  // GUI uses the address file to differentiate non-mainnet wallets in the UI
+  const std::string background_address_file = make_background_wallet_file_name(m_wallet_file) + ".address.txt";
+  if (m_nettype != MAINNET && !boost::filesystem::exists(background_address_file))
+  {
+    r = save_to_file(background_address_file, m_account.get_public_address_str(m_nettype), true);
+    if (!r) MERROR("String with address text not saved");
+  }
+
+  MDEBUG("Background keys stored");
+}
+//----------------------------------------------------------------------------------------------------
+void wallet2::write_background_sync_wallet(const epee::wipeable_string &wallet_password, const epee::wipeable_string &background_cache_password)
+{
+  MDEBUG("Storing background sync wallet");
+  THROW_WALLET_EXCEPTION_IF(m_background_sync_type != BackgroundSyncCustomPassword, error::wallet_internal_error,
+      "Can only write a background sync wallet when using a custom background password");
+  THROW_WALLET_EXCEPTION_IF(m_background_syncing || m_is_background_wallet, error::wallet_internal_error,
+      "Can't write background sync wallet from an existing background cache");
+  THROW_WALLET_EXCEPTION_IF(wallet_password == background_cache_password,
+      error::background_custom_password_same_as_wallet_password);
+
+  // Set the background encryption key
+  crypto::chacha_key custom_background_key;
+  get_custom_background_key(background_cache_password, custom_background_key, m_kdf_rounds);
+
+  // Keep the background encryption key in memory so the main wallet can update
+  // the background cache when it stores the main wallet cache
+  m_custom_background_key = boost::optional<crypto::chacha_key>(custom_background_key);
+
+  if (m_wallet_file.empty() || m_keys_file.empty())
+    return;
+
+  // Save background keys file, then background cache, then update main wallet settings
+  store_background_keys(custom_background_key);
+  store_background_cache(custom_background_key, true/*do_reset_background_sync_data*/);
+  bool r = store_keys(m_keys_file, wallet_password, false/*watch_only*/);
+  THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file);
+
+  MDEBUG("Background sync wallet saved successfully");
+}
+//----------------------------------------------------------------------------------------------------
+void wallet2::setup_background_sync(BackgroundSyncType background_sync_type, const epee::wipeable_string &wallet_password, const boost::optional<epee::wipeable_string> &background_cache_password)
+{
+  MDEBUG("Setting background sync to type " << background_sync_type);
+  THROW_WALLET_EXCEPTION_IF(m_background_syncing || m_is_background_wallet, error::wallet_internal_error,
+      "Can't set background sync type from an existing background cache");
+  verify_password_with_cached_key(wallet_password);
+
+  if (background_sync_type != BackgroundSyncOff)
+    validate_background_cache_password_usage(background_sync_type, background_cache_password, m_multisig, m_watch_only, key_on_device());
+
+  THROW_WALLET_EXCEPTION_IF(background_sync_type == BackgroundSyncCustomPassword && wallet_password == background_cache_password,
+      error::background_custom_password_same_as_wallet_password);
+
+  if (m_background_sync_type == background_sync_type && background_sync_type != BackgroundSyncCustomPassword)
+    return; // No need to make any changes
+
+  if (!m_wallet_file.empty())
+  {
+    // Delete existing background files if they already exist
+    const std::string old_background_wallet_file = make_background_wallet_file_name(m_wallet_file);
+    const std::string old_background_keys_file = make_background_keys_file_name(m_wallet_file);
+    const std::string old_background_address_file = old_background_wallet_file + ".address.txt";
+
+    // Make sure no other program is using the background wallet
+    THROW_WALLET_EXCEPTION_IF(!lock_background_keys_file(old_background_keys_file),
+        error::background_wallet_already_open, old_background_wallet_file);
+
+    if (boost::filesystem::exists(old_background_wallet_file))
+      if (!boost::filesystem::remove(old_background_wallet_file))
+        LOG_ERROR("Error deleting background wallet file: " << old_background_wallet_file);
+
+    if (boost::filesystem::exists(old_background_keys_file))
+      if (!boost::filesystem::remove(old_background_keys_file))
+        LOG_ERROR("Error deleting background keys file: " << old_background_keys_file);
+
+    if (boost::filesystem::exists(old_background_address_file))
+      if (!boost::filesystem::remove(old_background_address_file))
+        LOG_ERROR("Error deleting background address file: " << old_background_address_file);
+  }
+
+  m_background_sync_type = background_sync_type;
+  m_custom_background_key = boost::none;
+
+  // Write the new files
+  switch (background_sync_type)
+  {
+    case BackgroundSyncOff:
+    case BackgroundSyncReusePassword: rewrite(m_wallet_file, wallet_password); break;
+    case BackgroundSyncCustomPassword: write_background_sync_wallet(wallet_password, background_cache_password.get()); break;
+    default: THROW_WALLET_EXCEPTION(error::wallet_internal_error, "unknown background sync type");
+  }
+
+  MDEBUG("Done setting background sync type");
+}
+//----------------------------------------------------------------------------------------------------
+/*
+  When background syncing, the wallet scans using just the view key, without
+  keeping the spend key in decrypted state. When a user returns to the wallet
+  and decrypts the spend key, the wallet processes the background synced txs,
+  then the wallet picks up scanning normally right where the background sync
+  left off.
+*/
+void wallet2::start_background_sync()
+{
+  THROW_WALLET_EXCEPTION_IF(m_background_sync_type == BackgroundSyncOff, error::wallet_internal_error,
+      "must setup background sync first before using background sync");
+  THROW_WALLET_EXCEPTION_IF(m_is_background_wallet, error::wallet_internal_error,
+      "Can't start background syncing from a background wallet (it is always background syncing)");
+
+  MDEBUG("Starting background sync");
+
+  if (m_background_syncing)
+  {
+    MDEBUG("Already background syncing");
+    return;
+  }
+
+  if (m_background_sync_type == BackgroundSyncCustomPassword && !m_wallet_file.empty())
+  {
+    // Save the current state of the wallet cache. Only necessary when using a
+    // custom background password which uses distinct background wallet to sync.
+    // When reusing wallet password to sync we reuse the main wallet cache.
+    store();
+
+    // Wipe user data from the background wallet cache not needed to sync.
+    // Only wipe user data from background cache if wallet cache is stored
+    // on disk; otherwise we could lose the data.
+    clear_user_data();
+
+    // Wipe m_cache_key since it can be used to decrypt main wallet cache
+    m_cache_key.scrub();
+  }
+
+  reset_background_sync_data(m_background_sync_data);
+  m_background_syncing = true;
+
+  // Wipe the spend key from memory
+  m_account.forget_spend_key();
+
+  MDEBUG("Background sync started at height " << m_background_sync_data.start_height);
+}
+//----------------------------------------------------------------------------------------------------
+void wallet2::stop_background_sync(const epee::wipeable_string &wallet_password, const crypto::secret_key &spend_secret_key)
+{
+  MDEBUG("Stopping background sync");
+
+  // Verify provided password and spend secret key. If no spend secret key is
+  // provided, recover it from the wallet keys file
+  crypto::secret_key recovered_spend_key = crypto::null_skey;
+  if (!m_wallet_file.empty())
+  {
+    THROW_WALLET_EXCEPTION_IF(!verify_password(wallet_password, recovered_spend_key), error::invalid_password);
+  }
+  else
+  {
+    verify_password_with_cached_key(wallet_password);
+  }
+
+  if (spend_secret_key != crypto::null_skey)
+  {
+    THROW_WALLET_EXCEPTION_IF(!m_wallet_file.empty() && spend_secret_key != recovered_spend_key,
+        error::invalid_spend_key);
+    MDEBUG("Setting spend secret key with the provided key");
+    recovered_spend_key = spend_secret_key;
+  }
+
+  // Verify private spend key derives to wallet's public spend key
+  const auto verify_spend_key = [this](crypto::secret_key &recovered_spend_key) -> bool
+  {
+    crypto::public_key spend_public_key;
+    return recovered_spend_key != crypto::null_skey &&
+        crypto::secret_key_to_public_key(recovered_spend_key, spend_public_key) &&
+        m_account.get_keys().m_account_address.m_spend_public_key == spend_public_key;
+  };
+  THROW_WALLET_EXCEPTION_IF(!verify_spend_key(recovered_spend_key), error::invalid_spend_key);
+
+  THROW_WALLET_EXCEPTION_IF(m_background_sync_type == BackgroundSyncOff, error::wallet_internal_error,
+      "must setup background sync first before using background sync");
+  THROW_WALLET_EXCEPTION_IF(m_is_background_wallet, error::wallet_internal_error,
+      "Can't stop background syncing from a background wallet");
+
+  if (!m_background_syncing)
+    return;
+
+  // Copy background cache, we're about to overwrite it
+  const background_sync_data_t background_sync_data = m_background_sync_data;
+  const hashchain background_synced_chain = m_blockchain;
+  const uint64_t last_block_reward = m_last_block_reward;
+
+  if (m_background_sync_type == BackgroundSyncCustomPassword && !m_wallet_file.empty())
+  {
+    // Reload the wallet from disk
+    load(m_wallet_file, wallet_password);
+    THROW_WALLET_EXCEPTION_IF(!verify_spend_key(recovered_spend_key), error::invalid_spend_key);
+  }
+  m_background_syncing = false;
+
+  // Set the plaintext spend key
+  m_account.set_spend_key(recovered_spend_key);
+
+  // Encrypt the spend key when done if needed
+  epee::misc_utils::auto_scope_leave_caller keys_reencryptor;
+  if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
+    keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this]{encrypt_keys(wallet_password);});
+
+  // Now we can use the decrypted spend key to process background cache
+  process_background_cache(background_sync_data, background_synced_chain, last_block_reward);
+
+  // Reset the background cache after processing
+  reset_background_sync_data(m_background_sync_data);
+
+  MDEBUG("Background sync stopped");
+}
+//----------------------------------------------------------------------------------------------------
 wallet2::payment_container wallet2::export_payments() const
 {
   payment_container payments;
@@ -14178,7 +15126,7 @@ uint64_t wallet2::get_blockchain_height_by_date(uint16_t year, uint8_t month, ui
       else if (res.status == CORE_RPC_STATUS_BUSY)
         oss << "daemon is busy";
       else
-        oss << get_rpc_status(res.status);
+        oss << get_rpc_status(m_trusted_daemon, res.status);
       throw std::runtime_error(oss.str());
     }
     cryptonote::block blk_min, blk_mid, blk_max;
@@ -14380,17 +15328,6 @@ void wallet2::on_device_progress(const hw::device_progress& event)
     m_callback->on_device_progress(event);
 }
 //----------------------------------------------------------------------------------------------------
-std::string wallet2::get_rpc_status(const std::string &s) const
-{
-  if (m_trusted_daemon)
-    return s;
-  if (s == CORE_RPC_STATUS_OK)
-    return s;
-  if (s == CORE_RPC_STATUS_BUSY || s == CORE_RPC_STATUS_PAYMENT_REQUIRED)
-    return s;
-  return "<error>";
-}
-//----------------------------------------------------------------------------------------------------
 void wallet2::throw_on_rpc_response_error(bool r, const epee::json_rpc::error &error, const std::string &status, const char *method) const
 {
   // Treat all RPC payment access errors the same, whether payment is actually required or not
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index 5f2f0e0c4..7228f1907 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
@@ -86,6 +86,7 @@
 
 class Serialization_portability_wallet_Test;
 class wallet_accessor_test;
+namespace multisig { class multisig_account; }
 
 namespace tools
 {
@@ -249,6 +250,20 @@ private:
       BackgroundMiningNo = 2,
     };
 
+    enum BackgroundSyncType {
+      BackgroundSyncOff = 0,
+      BackgroundSyncReusePassword = 1,
+      BackgroundSyncCustomPassword = 2,
+    };
+
+    static BackgroundSyncType background_sync_type_from_str(const std::string &background_sync_type_str)
+    {
+      if (background_sync_type_str == "off") return BackgroundSyncOff;
+      if (background_sync_type_str == "reuse-wallet-password") return BackgroundSyncReusePassword;
+      if (background_sync_type_str == "custom-background-password") return BackgroundSyncCustomPassword;
+      throw std::logic_error("Unknown background sync type");
+    };
+
     enum ExportFormat {
       Binary = 0,
       Ascii,
@@ -275,7 +290,12 @@ private:
     //! Just parses variables.
     static std::unique_ptr<wallet2> make_dummy(const boost::program_options::variables_map& vm, bool unattended, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
 
-    static bool verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds);
+    static bool verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds)
+    {
+      crypto::secret_key spend_key = crypto::null_skey;
+      return verify_password(keys_file_name, password, no_spend_key, hwdev, kdf_rounds, spend_key);
+    };
+    static bool verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds, crypto::secret_key &spend_key_out);
     static bool query_device(hw::device::device_type& device_type, const std::string& keys_file_name, const epee::wipeable_string& password, uint64_t kdf_rounds = 1);
 
     wallet2(cryptonote::network_type nettype = cryptonote::MAINNET, uint64_t kdf_rounds = 1, bool unattended = false, std::unique_ptr<epee::net_utils::http::http_client_factory> http_client_factory = std::unique_ptr<epee::net_utils::http::http_client_factory>(new net::http::client_factory()));
@@ -785,6 +805,54 @@ private:
       END_SERIALIZE()
     };
 
+    struct background_synced_tx_t
+    {
+      uint64_t index_in_background_sync_data;
+      cryptonote::transaction tx;
+      std::vector<uint64_t> output_indices;
+      uint64_t height;
+      uint64_t block_timestamp;
+      bool double_spend_seen;
+
+      BEGIN_SERIALIZE_OBJECT()
+        VERSION_FIELD(0)
+        VARINT_FIELD(index_in_background_sync_data)
+
+        // prune tx; don't need to keep signature data
+        if (!tx.serialize_base(ar))
+          return false;
+
+        FIELD(output_indices)
+        VARINT_FIELD(height)
+        VARINT_FIELD(block_timestamp)
+        FIELD(double_spend_seen)
+      END_SERIALIZE()
+    };
+
+    struct background_sync_data_t
+    {
+      bool first_refresh_done = false;
+      uint64_t start_height = 0;
+      std::unordered_map<crypto::hash, background_synced_tx_t> txs;
+
+      // Relevant wallet settings
+      uint64_t wallet_refresh_from_block_height;
+      size_t subaddress_lookahead_major;
+      size_t subaddress_lookahead_minor;
+      RefreshType wallet_refresh_type;
+
+      BEGIN_SERIALIZE_OBJECT()
+        VERSION_FIELD(0)
+        FIELD(first_refresh_done)
+        FIELD(start_height)
+        FIELD(txs)
+        FIELD(wallet_refresh_from_block_height)
+        VARINT_FIELD(subaddress_lookahead_major)
+        VARINT_FIELD(subaddress_lookahead_minor)
+        VARINT_FIELD(wallet_refresh_type)
+      END_SERIALIZE()
+    };
+
     typedef std::tuple<uint64_t, crypto::public_key, rct::key> get_outs_entry;
 
     struct parsed_block
@@ -892,6 +960,23 @@ private:
      */
     void restore(const std::string& wallet_, const epee::wipeable_string& password, const std::string &device_name, bool create_address_file = false);
 
+  private:
+    /*!
+     * \brief Decrypts the account keys
+     * \return an RAII reencryptor for the account keys
+     */
+    epee::misc_utils::auto_scope_leave_caller decrypt_account_for_multisig(const epee::wipeable_string &password);
+    /*!
+     * \brief Creates an uninitialized multisig account
+     * \outparam: the uninitialized multisig account
+     */
+    void get_uninitialized_multisig_account(multisig::multisig_account &account_out) const;
+    /*!
+     * \brief Reconstructs a multisig account from wallet2 state
+     * \outparam: the reconstructed multisig account
+     */
+    void get_reconstructed_multisig_account(multisig::multisig_account &account_out) const;
+  public:
     /*!
      * \brief Creates a multisig wallet
      * \return empty if done, non empty if we need to send another string
@@ -913,6 +998,13 @@ private:
      * \return string to send to other participants
      */
     std::string get_multisig_first_kex_msg() const;
+    /*!
+     * \brief Use multisig kex messages for an in-progress kex round to 'boost' the following round for another group member
+     */
+    std::string get_multisig_key_exchange_booster(const epee::wipeable_string &password,
+      const std::vector<std::string> &kex_messages,
+      const std::uint32_t threshold,
+      const std::uint32_t num_signers);
     /*!
      * Export multisig info
      * This will generate and remember new k values
@@ -973,7 +1065,8 @@ private:
     /*!
      * \brief verifies given password is correct for default wallet keys file
      */
-    bool verify_password(const epee::wipeable_string& password);
+    bool verify_password(const epee::wipeable_string& password) {crypto::secret_key key = crypto::null_skey; return verify_password(password, key);};
+    bool verify_password(const epee::wipeable_string& password, crypto::secret_key &spend_key_out);
     cryptonote::account_base& get_account(){return m_account;}
     const cryptonote::account_base& get_account()const{return m_account;}
 
@@ -1060,6 +1153,7 @@ private:
 
     cryptonote::network_type nettype() const { return m_nettype; }
     bool watch_only() const { return m_watch_only; }
+    bool is_background_wallet() const { return m_is_background_wallet; }
     multisig::multisig_account_status get_multisig_status() const;
     bool has_multisig_partial_key_images() const;
     bool has_unknown_key_images() const;
@@ -1269,11 +1363,17 @@ private:
         return;
       }
       a & m_has_ever_refreshed_from_node;
+      if(ver < 31)
+      {
+        m_background_sync_data = background_sync_data_t{};
+        return;
+      }
+      a & m_background_sync_data;
     }
 
     BEGIN_SERIALIZE_OBJECT()
       MAGIC_FIELD("monero wallet cache")
-      VERSION_FIELD(1)
+      VERSION_FIELD(2)
       FIELD(m_blockchain)
       FIELD(m_transfers)
       FIELD(m_account_public_address)
@@ -1306,6 +1406,12 @@ private:
         return true;
       }
       FIELD(m_has_ever_refreshed_from_node)
+      if (version < 2)
+      {
+        m_background_sync_data = background_sync_data_t{};
+        return true;
+      }
+      FIELD(m_background_sync_data)
     END_SERIALIZE()
 
     /*!
@@ -1321,6 +1427,8 @@ private:
      * \return                Whether path is valid format
      */
     static bool wallet_valid_path_format(const std::string& file_path);
+    static std::string make_background_wallet_file_name(const std::string &wallet_file);
+    static std::string make_background_keys_file_name(const std::string &wallet_file);
     static bool parse_long_payment_id(const std::string& payment_id_str, crypto::hash& payment_id);
     static bool parse_short_payment_id(const std::string& payment_id_str, crypto::hash8& payment_id);
     static bool parse_payment_id(const std::string& payment_id_str, crypto::hash& payment_id);
@@ -1367,6 +1475,9 @@ private:
     void ignore_outputs_below(uint64_t value) { m_ignore_outputs_below = value; }
     bool track_uses() const { return m_track_uses; }
     void track_uses(bool value) { m_track_uses = value; }
+    BackgroundSyncType background_sync_type() const { return m_background_sync_type; }
+    void setup_background_sync(BackgroundSyncType background_sync_type, const epee::wipeable_string &wallet_password, const boost::optional<epee::wipeable_string> &background_cache_password);
+    bool is_background_syncing() const { return m_background_syncing; }
     bool show_wallet_name_when_locked() const { return m_show_wallet_name_when_locked; }
     void show_wallet_name_when_locked(bool value) { m_show_wallet_name_when_locked = value; }
     BackgroundMiningSetupType setup_background_mining() const { return m_setup_background_mining; }
@@ -1549,7 +1660,7 @@ private:
     std::vector<std::pair<uint64_t, uint64_t>> estimate_backlog(const std::vector<std::pair<double, double>> &fee_levels);
     std::vector<std::pair<uint64_t, uint64_t>> estimate_backlog(uint64_t min_tx_weight, uint64_t max_tx_weight, const std::vector<uint64_t> &fees);
 
-    uint64_t estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag, bool bulletproof_plus, bool use_view_tags, uint64_t base_fee, uint64_t fee_quantization_mask) const;
+    static uint64_t estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, bool clsag, bool bulletproof_plus, bool use_view_tags, uint64_t base_fee, uint64_t fee_quantization_mask);
     uint64_t get_fee_multiplier(uint32_t priority, int fee_algorithm = -1);
     uint64_t get_base_fee(uint32_t priority);
     uint64_t get_base_fee();
@@ -1641,6 +1752,9 @@ private:
     uint64_t get_bytes_sent() const;
     uint64_t get_bytes_received() const;
 
+    void start_background_sync();
+    void stop_background_sync(const epee::wipeable_string &wallet_password, const crypto::secret_key &spend_secret_key = crypto::null_skey);
+
     // MMS -------------------------------------------------------------------------------------------------
     mms::message_store& get_message_store() { return m_message_store; };
     const mms::message_store& get_message_store() const { return m_message_store; };
@@ -1673,6 +1787,9 @@ private:
      * \return                Whether it was successful.
      */
     bool store_keys(const std::string& keys_file_name, const epee::wipeable_string& password, bool watch_only = false);
+    bool store_keys(const std::string& keys_file_name, const crypto::chacha_key& key, bool watch_only = false, bool background_keys_file = false);
+    boost::optional<wallet2::keys_file_data> get_keys_file_data(const crypto::chacha_key& key, bool watch_only = false, bool background_keys_file = false);
+    bool store_keys_file_data(const std::string& keys_file_name, wallet2::keys_file_data &keys_file_data, bool background_keys_file = false);
     /*!
      * \brief Load wallet keys information from wallet file.
      * \param keys_file_name Name of wallet file
@@ -1686,6 +1803,7 @@ private:
      */
     bool load_keys_buf(const std::string& keys_buf, const epee::wipeable_string& password);
     bool load_keys_buf(const std::string& keys_buf, const epee::wipeable_string& password, boost::optional<crypto::chacha_key>& keys_to_encrypt);
+    void load_wallet_cache(const bool use_fs, const std::string& cache_buf = "");
     void process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint8_t block_version, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen, const tx_cache_data &tx_cache_data, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL, bool ignore_callbacks = false);
     bool should_skip_block(const cryptonote::block &b, uint64_t height) const;
     void process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const parsed_block &parsed_block, const crypto::hash& bl_id, uint64_t height, const std::vector<tx_cache_data> &tx_cache_data, size_t tx_cache_data_offset, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL);
@@ -1694,11 +1812,20 @@ private:
     void get_short_chain_history(std::list<crypto::hash>& ids, uint64_t granularity = 1) const;
     bool clear();
     void clear_soft(bool keep_key_images=false);
+    /*
+     * clear_user_data clears data created by the user, which is mostly data
+     * that a view key cannot identify on chain. This function was initially
+     * added to ensure that a "background" wallet (a wallet that syncs with just
+     * a view key hot in memory) does not have any sensitive data loaded that it
+     * does not need in order to sync. Future devs should take care to ensure
+     * that this function deletes data that is not useful for background syncing
+     */
+    void clear_user_data();
     void pull_blocks(bool first, bool try_incremental, uint64_t start_height, uint64_t& blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::vector<cryptonote::block_complete_entry> &blocks, std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> &o_indices, uint64_t &current_height, std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>>& process_pool_txs);
     void pull_hashes(uint64_t start_height, uint64_t& blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::vector<crypto::hash> &hashes);
     void fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history, bool force = false);
     void pull_and_parse_next_blocks(bool first, bool try_incremental, uint64_t start_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history, const std::vector<cryptonote::block_complete_entry> &prev_blocks, const std::vector<parsed_block> &prev_parsed_blocks, std::vector<cryptonote::block_complete_entry> &blocks, std::vector<parsed_block> &parsed_blocks, std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>>& process_pool_txs, bool &last, bool &error, std::exception_ptr &exception);
-    void process_parsed_blocks(uint64_t start_height, const std::vector<cryptonote::block_complete_entry> &blocks, const std::vector<parsed_block> &parsed_blocks, uint64_t& blocks_added, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL);
+    void process_parsed_blocks(const uint64_t start_height, const std::vector<cryptonote::block_complete_entry> &blocks, const std::vector<parsed_block> &parsed_blocks, uint64_t& blocks_added, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL);
     bool accept_pool_tx_for_processing(const crypto::hash &txid);
     void process_unconfirmed_transfer(bool incremental, const crypto::hash &txid, wallet2::unconfirmed_transfer_details &tx_details, bool seen_in_pool, std::chrono::system_clock::time_point now, bool refreshed);
     void process_pool_info_extent(const cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::response &res, std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> &process_txs, bool refreshed);
@@ -1745,10 +1872,23 @@ private:
     bool get_ring(const crypto::chacha_key &key, const crypto::key_image &key_image, std::vector<uint64_t> &outs);
     crypto::chacha_key get_ringdb_key();
     void setup_keys(const epee::wipeable_string &password);
+    const crypto::chacha_key get_cache_key();
+    void verify_password_with_cached_key(const epee::wipeable_string &password);
+    void verify_password_with_cached_key(const crypto::chacha_key &key);
     size_t get_transfer_details(const crypto::key_image &ki) const;
     tx_entry_data get_tx_entries(const std::unordered_set<crypto::hash> &txids);
     void sort_scan_tx_entries(std::vector<process_tx_entry_t> &unsorted_tx_entries);
     void process_scan_txs(const tx_entry_data &txs_to_scan, const tx_entry_data &txs_to_reprocess, const std::unordered_set<crypto::hash> &tx_hashes_to_reprocess, detached_blockchain_data &dbd);
+    void write_background_sync_wallet(const epee::wipeable_string &wallet_password, const epee::wipeable_string &background_cache_password);
+    void process_background_cache_on_open();
+    void process_background_cache(const background_sync_data_t &background_sync_data, const hashchain &background_chain, uint64_t last_block_reward);
+    void reset_background_sync_data(background_sync_data_t &background_sync_data);
+    void store_background_cache(const crypto::chacha_key &custom_background_key, const bool do_reset_background_sync_data = true);
+    void store_background_keys(const crypto::chacha_key &custom_background_key);
+
+    bool lock_background_keys_file(const std::string &background_keys_file);
+    bool unlock_background_keys_file();
+    bool is_background_keys_file_locked() const;
 
     void register_devices();
     hw::device& lookup_device(const std::string & device_descriptor);
@@ -1771,7 +1911,6 @@ private:
     boost::optional<epee::wipeable_string> on_device_passphrase_request(bool & on_device);
     void on_device_progress(const hw::device_progress& event);
 
-    std::string get_rpc_status(const std::string &s) const;
     void throw_on_rpc_response_error(bool r, const epee::json_rpc::error &error, const std::string &status, const char *method) const;
 
     bool should_expand(const cryptonote::subaddress_index &index) const;
@@ -1860,6 +1999,8 @@ private:
     uint64_t m_ignore_outputs_above;
     uint64_t m_ignore_outputs_below;
     bool m_track_uses;
+    bool m_is_background_wallet;
+    BackgroundSyncType m_background_sync_type;
     bool m_show_wallet_name_when_locked;
     uint32_t m_inactivity_lock_timeout;
     BackgroundMiningSetupType m_setup_background_mining;
@@ -1887,6 +2028,7 @@ private:
 
     uint64_t m_last_block_reward;
     std::unique_ptr<tools::file_locker> m_keys_file_locker;
+    std::unique_ptr<tools::file_locker> m_background_keys_file_locker;
     
     mms::message_store m_message_store;
     bool m_original_keys_available;
@@ -1894,6 +2036,7 @@ private:
     crypto::secret_key m_original_view_secret_key;
 
     crypto::chacha_key m_cache_key;
+    boost::optional<crypto::chacha_key> m_custom_background_key = boost::none;
     std::shared_ptr<wallet_keys_unlocker> m_encrypt_keys_after_refresh;
 
     bool m_unattended;
@@ -1909,9 +2052,13 @@ private:
 
     static boost::mutex default_daemon_address_lock;
     static std::string default_daemon_address;
+
+    bool m_background_syncing;
+    bool m_processing_background_cache;
+    background_sync_data_t m_background_sync_data;
   };
 }
-BOOST_CLASS_VERSION(tools::wallet2, 30)
+BOOST_CLASS_VERSION(tools::wallet2, 31)
 BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 12)
 BOOST_CLASS_VERSION(tools::wallet2::multisig_info, 1)
 BOOST_CLASS_VERSION(tools::wallet2::multisig_info::LR, 0)
@@ -1927,6 +2074,8 @@ BOOST_CLASS_VERSION(tools::wallet2::signed_tx_set, 1)
 BOOST_CLASS_VERSION(tools::wallet2::tx_construction_data, 4)
 BOOST_CLASS_VERSION(tools::wallet2::pending_tx, 3)
 BOOST_CLASS_VERSION(tools::wallet2::multisig_sig, 1)
+BOOST_CLASS_VERSION(tools::wallet2::background_synced_tx_t, 0)
+BOOST_CLASS_VERSION(tools::wallet2::background_sync_data_t, 0)
 
 namespace boost
 {
@@ -2425,6 +2574,29 @@ namespace boost
         return;
       a & x.multisig_sigs;
     }
+
+    template <class Archive>
+    inline void serialize(Archive& a, tools::wallet2::background_synced_tx_t &x, const boost::serialization::version_type ver)
+    {
+      a & x.index_in_background_sync_data;
+      a & x.tx;
+      a & x.output_indices;
+      a & x.height;
+      a & x.block_timestamp;
+      a & x.double_spend_seen;
+    }
+
+    template <class Archive>
+    inline void serialize(Archive& a, tools::wallet2::background_sync_data_t &x, const boost::serialization::version_type ver)
+    {
+      a & x.first_refresh_done;
+      a & x.start_height;
+      a & x.txs;
+      a & x.wallet_refresh_from_block_height;
+      a & x.subaddress_lookahead_major;
+      a & x.subaddress_lookahead_minor;
+      a & x.wallet_refresh_type;
+    }
   }
 }
 
diff --git a/src/wallet/wallet_args.cpp b/src/wallet/wallet_args.cpp
index 9710f134c..45e9afa4c 100644
--- a/src/wallet/wallet_args.cpp
+++ b/src/wallet/wallet_args.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/wallet/wallet_args.h b/src/wallet/wallet_args.h
index e5b440fc3..c6a324eea 100644
--- a/src/wallet/wallet_args.h
+++ b/src/wallet/wallet_args.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h
index 2bbfc3167..4bbf05008 100644
--- a/src/wallet/wallet_errors.h
+++ b/src/wallet/wallet_errors.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
@@ -63,6 +63,7 @@ namespace tools
     //       invalid_password
     //       invalid_priority
     //       invalid_multisig_seed
+    //       invalid_spend_key
     //       refresh_error *
     //         acc_outs_lookup_error
     //         block_parse_error
@@ -97,6 +98,9 @@ namespace tools
     //       wallet_files_doesnt_correspond
     //       scan_tx_error *
     //         wont_reprocess_recent_txs_via_untrusted_daemon
+    //       background_sync_error *
+    //         background_wallet_already_open
+    //         background_custom_password_same_as_wallet_password
     //
     // * - class with protected ctor
 
@@ -304,6 +308,16 @@ namespace tools
       std::string to_string() const { return wallet_logic_error::to_string(); }
     };
 
+    struct invalid_spend_key : public wallet_logic_error
+    {
+      explicit invalid_spend_key(std::string&& loc)
+        : wallet_logic_error(std::move(loc), "invalid spend key")
+      {
+      }
+
+      std::string to_string() const { return wallet_logic_error::to_string(); }
+    };
+
     //----------------------------------------------------------------------------------------------------
     struct invalid_pregenerated_random : public wallet_logic_error
     {
@@ -948,6 +962,31 @@ namespace tools
       }
     };
     //----------------------------------------------------------------------------------------------------
+    struct background_sync_error : public wallet_logic_error
+    {
+    protected:
+      explicit background_sync_error(std::string&& loc, const std::string& message)
+        : wallet_logic_error(std::move(loc), message)
+      {
+      }
+    };
+    //----------------------------------------------------------------------------------------------------
+    struct background_wallet_already_open : public background_sync_error
+    {
+      explicit background_wallet_already_open(std::string&& loc, const std::string& background_wallet_file)
+        : background_sync_error(std::move(loc), "background wallet " + background_wallet_file + " is already opened by another wallet program")
+      {
+      }
+    };
+    //----------------------------------------------------------------------------------------------------
+    struct background_custom_password_same_as_wallet_password : public background_sync_error
+    {
+      explicit background_custom_password_same_as_wallet_password(std::string&& loc)
+        : background_sync_error(std::move(loc), "custom background password must be different than wallet password")
+      {
+      }
+    };
+    //----------------------------------------------------------------------------------------------------
 
 #if !defined(_MSC_VER)
 
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index 7976cb7af..5dcc8e47d 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
@@ -75,6 +75,54 @@ using namespace epee;
     } \
   } while(0)
 
+#define CHECK_IF_BACKGROUND_SYNCING() \
+  do \
+  { \
+    if (!m_wallet) { return not_open(er); } \
+    if (m_wallet->is_background_wallet()) \
+    { \
+      er.code = WALLET_RPC_ERROR_CODE_IS_BACKGROUND_WALLET; \
+      er.message = "This command is disabled for background wallets."; \
+      return false; \
+    } \
+    if (m_wallet->is_background_syncing()) \
+    { \
+      er.code = WALLET_RPC_ERROR_CODE_IS_BACKGROUND_SYNCING; \
+      er.message = "This command is disabled while background syncing. Stop background syncing to use this command."; \
+      return false; \
+    } \
+  } while(0)
+
+#define PRE_VALIDATE_BACKGROUND_SYNC() \
+  do \
+  { \
+    if (!m_wallet) { return not_open(er); } \
+    if (m_restricted) \
+    { \
+      er.code = WALLET_RPC_ERROR_CODE_DENIED; \
+      er.message = "Command unavailable in restricted mode."; \
+      return false; \
+    } \
+    if (m_wallet->key_on_device()) \
+    { \
+      er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; \
+      er.message = "Command not supported by HW wallet"; \
+      return false; \
+    } \
+    if (m_wallet->get_multisig_status().multisig_is_active) \
+    { \
+      er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; \
+      er.message = "Multisig wallet cannot enable background sync"; \
+      return false; \
+    } \
+    if (m_wallet->watch_only()) \
+    { \
+      er.code = WALLET_RPC_ERROR_CODE_WATCH_ONLY; \
+      er.message = "Watch-only wallet cannot enable background sync"; \
+      return false; \
+    } \
+  } while (0)
+
 namespace
 {
   const command_line::arg_descriptor<std::string, true> arg_rpc_bind_port = {"rpc-bind-port", "Sets bind port for server"};
@@ -293,6 +341,9 @@ namespace tools
   {
     if (!m_wallet)
       return;
+    // Background mining can be toggled from the main wallet
+    if (m_wallet->is_background_wallet() || m_wallet->is_background_syncing())
+      return;
 
     tools::wallet2::BackgroundMiningSetupType setup = m_wallet->setup_background_mining();
     if (setup == tools::wallet2::BackgroundMiningNo)
@@ -583,6 +634,7 @@ namespace tools
   bool wallet_rpc_server::on_create_address(const wallet_rpc::COMMAND_RPC_CREATE_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_CREATE_ADDRESS::response& res, epee::json_rpc::error& er, const connection_context *ctx)
   {
     if (!m_wallet) return not_open(er);
+    CHECK_IF_BACKGROUND_SYNCING();
     try
     {
       if (req.count < 1 || req.count > 65536) {
@@ -620,6 +672,7 @@ namespace tools
   bool wallet_rpc_server::on_label_address(const wallet_rpc::COMMAND_RPC_LABEL_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_LABEL_ADDRESS::response& res, epee::json_rpc::error& er, const connection_context *ctx)
   {
     if (!m_wallet) return not_open(er);
+    CHECK_IF_BACKGROUND_SYNCING();
     try
     {
       m_wallet->set_subaddress_label(req.index, req.label);
@@ -682,6 +735,7 @@ namespace tools
   bool wallet_rpc_server::on_create_account(const wallet_rpc::COMMAND_RPC_CREATE_ACCOUNT::request& req, wallet_rpc::COMMAND_RPC_CREATE_ACCOUNT::response& res, epee::json_rpc::error& er, const connection_context *ctx)
   {
     if (!m_wallet) return not_open(er);
+    CHECK_IF_BACKGROUND_SYNCING();
     try
     {
       m_wallet->add_subaddress_account(req.label);
@@ -699,6 +753,7 @@ namespace tools
   bool wallet_rpc_server::on_label_account(const wallet_rpc::COMMAND_RPC_LABEL_ACCOUNT::request& req, wallet_rpc::COMMAND_RPC_LABEL_ACCOUNT::response& res, epee::json_rpc::error& er, const connection_context *ctx)
   {
     if (!m_wallet) return not_open(er);
+    CHECK_IF_BACKGROUND_SYNCING();
     try
     {
       m_wallet->set_subaddress_label({req.account_index, 0}, req.label);
@@ -714,6 +769,7 @@ namespace tools
   bool wallet_rpc_server::on_get_account_tags(const wallet_rpc::COMMAND_RPC_GET_ACCOUNT_TAGS::request& req, wallet_rpc::COMMAND_RPC_GET_ACCOUNT_TAGS::response& res, epee::json_rpc::error& er, const connection_context *ctx)
   {
     if (!m_wallet) return not_open(er);
+    CHECK_IF_BACKGROUND_SYNCING();
     const std::pair<std::map<std::string, std::string>, std::vector<std::string>> account_tags = m_wallet->get_account_tags();
     for (const std::pair<const std::string, std::string>& p : account_tags.first)
     {
@@ -733,6 +789,7 @@ namespace tools
   bool wallet_rpc_server::on_tag_accounts(const wallet_rpc::COMMAND_RPC_TAG_ACCOUNTS::request& req, wallet_rpc::COMMAND_RPC_TAG_ACCOUNTS::response& res, epee::json_rpc::error& er, const connection_context *ctx)
   {
     if (!m_wallet) return not_open(er);
+    CHECK_IF_BACKGROUND_SYNCING();
     try
     {
       m_wallet->set_account_tag(req.accounts, req.tag);
@@ -748,6 +805,7 @@ namespace tools
   bool wallet_rpc_server::on_untag_accounts(const wallet_rpc::COMMAND_RPC_UNTAG_ACCOUNTS::request& req, wallet_rpc::COMMAND_RPC_UNTAG_ACCOUNTS::response& res, epee::json_rpc::error& er, const connection_context *ctx)
   {
     if (!m_wallet) return not_open(er);
+    CHECK_IF_BACKGROUND_SYNCING();
     try
     {
       m_wallet->set_account_tag(req.accounts, "");
@@ -763,6 +821,7 @@ namespace tools
   bool wallet_rpc_server::on_set_account_tag_description(const wallet_rpc::COMMAND_RPC_SET_ACCOUNT_TAG_DESCRIPTION::request& req, wallet_rpc::COMMAND_RPC_SET_ACCOUNT_TAG_DESCRIPTION::response& res, epee::json_rpc::error& er, const connection_context *ctx)
   {
     if (!m_wallet) return not_open(er);
+    CHECK_IF_BACKGROUND_SYNCING();
     try
     {
       m_wallet->set_account_tag_description(req.tag, req.description);
@@ -793,6 +852,7 @@ namespace tools
   bool wallet_rpc_server::on_freeze(const wallet_rpc::COMMAND_RPC_FREEZE::request& req, wallet_rpc::COMMAND_RPC_FREEZE::response& res, epee::json_rpc::error& er, const connection_context *ctx)
   {
     if (!m_wallet) return not_open(er);
+    CHECK_IF_BACKGROUND_SYNCING();
     try
     {
       if (req.key_image.empty())
@@ -821,6 +881,7 @@ namespace tools
   bool wallet_rpc_server::on_thaw(const wallet_rpc::COMMAND_RPC_THAW::request& req, wallet_rpc::COMMAND_RPC_THAW::response& res, epee::json_rpc::error& er, const connection_context *ctx)
   {
     if (!m_wallet) return not_open(er);
+    CHECK_IF_BACKGROUND_SYNCING();
     try
     {
       if (req.key_image.empty())
@@ -849,6 +910,7 @@ namespace tools
   bool wallet_rpc_server::on_frozen(const wallet_rpc::COMMAND_RPC_FROZEN::request& req, wallet_rpc::COMMAND_RPC_FROZEN::response& res, epee::json_rpc::error& er, const connection_context *ctx)
   {
     if (!m_wallet) return not_open(er);
+    CHECK_IF_BACKGROUND_SYNCING();
     try
     {
       if (req.key_image.empty())
@@ -876,6 +938,8 @@ namespace tools
   //------------------------------------------------------------------------------------------------------------------------------
   bool wallet_rpc_server::validate_transfer(const std::list<wallet_rpc::transfer_destination>& destinations, const std::string& payment_id, std::vector<cryptonote::tx_destination_entry>& dsts, std::vector<uint8_t>& extra, bool at_least_one_destination, epee::json_rpc::error& er)
   {
+    CHECK_IF_BACKGROUND_SYNCING();
+
     crypto::hash8 integrated_payment_id = crypto::null_hash8;
     std::string extra_nonce;
     for (auto it = destinations.begin(); it != destinations.end(); it++)
@@ -1205,6 +1269,7 @@ namespace tools
     }
 
     CHECK_MULTISIG_ENABLED();
+    CHECK_IF_BACKGROUND_SYNCING();
 
     cryptonote::blobdata blob;
     if (!epee::string_tools::parse_hexstr_to_binbuff(req.unsigned_txset, blob))
@@ -1286,6 +1351,7 @@ namespace tools
       er.message = "command not supported by watch-only wallet";
       return false;
     }
+    CHECK_IF_BACKGROUND_SYNCING();
     if(req.unsigned_txset.empty() && req.multisig_txset.empty())
     {
       er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
@@ -1555,6 +1621,7 @@ namespace tools
     }
 
     CHECK_MULTISIG_ENABLED();
+    CHECK_IF_BACKGROUND_SYNCING();
 
     try
     {
@@ -2117,6 +2184,7 @@ namespace tools
             er.message = "The wallet is watch-only. Cannot retrieve seed.";
             return false;
           }
+          CHECK_IF_BACKGROUND_SYNCING();
           if (!m_wallet->is_deterministic())
           {
             er.code = WALLET_RPC_ERROR_CODE_NON_DETERMINISTIC;
@@ -2145,6 +2213,7 @@ namespace tools
             er.message = "The wallet is watch-only. Cannot retrieve spend key.";
             return false;
           }
+          CHECK_IF_BACKGROUND_SYNCING();
           epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().m_spend_secret_key);
           res.key = std::string(key.data(), key.size());
       }
@@ -2166,6 +2235,7 @@ namespace tools
       er.message = "Command unavailable in restricted mode.";
       return false;
     }
+    CHECK_IF_BACKGROUND_SYNCING();
 
     try
     {
@@ -2179,6 +2249,79 @@ namespace tools
     return true;
   }
   //------------------------------------------------------------------------------------------------------------------------------
+  bool wallet_rpc_server::on_setup_background_sync(const wallet_rpc::COMMAND_RPC_SETUP_BACKGROUND_SYNC::request& req, wallet_rpc::COMMAND_RPC_SETUP_BACKGROUND_SYNC::response& res, epee::json_rpc::error& er, const connection_context *ctx)
+  {
+    try
+    {
+      PRE_VALIDATE_BACKGROUND_SYNC();
+      const tools::wallet2::BackgroundSyncType background_sync_type = tools::wallet2::background_sync_type_from_str(req.background_sync_type);
+      boost::optional<epee::wipeable_string> background_cache_password = boost::none;
+      if (background_sync_type == tools::wallet2::BackgroundSyncCustomPassword)
+        background_cache_password = boost::optional<epee::wipeable_string>(req.background_cache_password);
+      m_wallet->setup_background_sync(background_sync_type, req.wallet_password, background_cache_password);
+    }
+    catch (...)
+    {
+      handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
+      return false;
+    }
+    return true;
+  }
+  //------------------------------------------------------------------------------------------------------------------------------
+  bool wallet_rpc_server::on_start_background_sync(const wallet_rpc::COMMAND_RPC_START_BACKGROUND_SYNC::request& req, wallet_rpc::COMMAND_RPC_START_BACKGROUND_SYNC::response& res, epee::json_rpc::error& er, const connection_context *ctx)
+  {
+    try
+    {
+      PRE_VALIDATE_BACKGROUND_SYNC();
+      m_wallet->start_background_sync();
+    }
+    catch (...)
+    {
+      handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
+      return false;
+    }
+    return true;
+  }
+  //------------------------------------------------------------------------------------------------------------------------------
+  bool wallet_rpc_server::on_stop_background_sync(const wallet_rpc::COMMAND_RPC_STOP_BACKGROUND_SYNC::request& req, wallet_rpc::COMMAND_RPC_STOP_BACKGROUND_SYNC::response& res, epee::json_rpc::error& er, const connection_context *ctx)
+  {
+    try
+    {
+      PRE_VALIDATE_BACKGROUND_SYNC();
+      crypto::secret_key spend_secret_key = crypto::null_skey;
+
+      // Load the spend key from seed if seed is provided
+      if (!req.seed.empty())
+      {
+        crypto::secret_key recovery_key;
+        std::string language;
+
+        if (!crypto::ElectrumWords::words_to_bytes(req.seed, recovery_key, language))
+        {
+          er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+          er.message = "Electrum-style word list failed verification";
+          return false;
+        }
+
+        if (!req.seed_offset.empty())
+          recovery_key = cryptonote::decrypt_key(recovery_key, req.seed_offset);
+
+        // generate spend key
+        cryptonote::account_base account;
+        account.generate(recovery_key, true, false);
+        spend_secret_key = account.get_keys().m_spend_secret_key;
+      }
+
+      m_wallet->stop_background_sync(req.wallet_password, spend_secret_key);
+    }
+    catch (...)
+    {
+      handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
+      return false;
+    }
+    return true;
+  }
+  //------------------------------------------------------------------------------------------------------------------------------
   bool wallet_rpc_server::on_sign(const wallet_rpc::COMMAND_RPC_SIGN::request& req, wallet_rpc::COMMAND_RPC_SIGN::response& res, epee::json_rpc::error& er, const connection_context *ctx)
   {
     if (!m_wallet) return not_open(er);
@@ -2188,6 +2331,7 @@ namespace tools
       er.message = "Command unavailable in restricted mode.";
       return false;
     }
+    CHECK_IF_BACKGROUND_SYNCING();
 
     tools::wallet2::message_signature_type_t signature_type = tools::wallet2::sign_with_spend_key;
     if (req.signature_type == "spend" || req.signature_type == "")
@@ -2280,6 +2424,7 @@ namespace tools
       er.message = "Command unavailable in restricted mode.";
       return false;
     }
+    CHECK_IF_BACKGROUND_SYNCING();
 
     if (req.txids.size() != req.notes.size())
     {
@@ -2352,6 +2497,7 @@ namespace tools
       er.message = "Command unavailable in restricted mode.";
       return false;
     }
+    CHECK_IF_BACKGROUND_SYNCING();
 
     m_wallet->set_attribute(req.key, req.value);
 
@@ -2379,6 +2525,7 @@ namespace tools
   bool wallet_rpc_server::on_get_tx_key(const wallet_rpc::COMMAND_RPC_GET_TX_KEY::request& req, wallet_rpc::COMMAND_RPC_GET_TX_KEY::response& res, epee::json_rpc::error& er, const connection_context *ctx)
   {
     if (!m_wallet) return not_open(er);
+    CHECK_IF_BACKGROUND_SYNCING();
 
     crypto::hash txid;
     if (!epee::string_tools::hex_to_pod(req.txid, txid))
@@ -2470,6 +2617,7 @@ namespace tools
   bool wallet_rpc_server::on_get_tx_proof(const wallet_rpc::COMMAND_RPC_GET_TX_PROOF::request& req, wallet_rpc::COMMAND_RPC_GET_TX_PROOF::response& res, epee::json_rpc::error& er, const connection_context *ctx)
   {
     if (!m_wallet) return not_open(er);
+    CHECK_IF_BACKGROUND_SYNCING();
 
     crypto::hash txid;
     if (!epee::string_tools::hex_to_pod(req.txid, txid))
@@ -2586,6 +2734,7 @@ namespace tools
   bool wallet_rpc_server::on_get_reserve_proof(const wallet_rpc::COMMAND_RPC_GET_RESERVE_PROOF::request& req, wallet_rpc::COMMAND_RPC_GET_RESERVE_PROOF::response& res, epee::json_rpc::error& er, const connection_context *ctx)
   {
     if (!m_wallet) return not_open(er);
+    CHECK_IF_BACKGROUND_SYNCING();
 
     boost::optional<std::pair<uint32_t, uint64_t>> account_minreserve;
     if (!req.all)
@@ -2828,6 +2977,7 @@ namespace tools
       er.message = "command not supported by HW wallet";
       return false;
     }
+    CHECK_IF_BACKGROUND_SYNCING();
 
     try
     {
@@ -2857,6 +3007,7 @@ namespace tools
       er.message = "command not supported by HW wallet";
       return false;
     }
+    CHECK_IF_BACKGROUND_SYNCING();
 
     cryptonote::blobdata blob;
     if (!epee::string_tools::parse_hexstr_to_binbuff(req.outputs_data_hex, blob))
@@ -2882,6 +3033,7 @@ namespace tools
   bool wallet_rpc_server::on_export_key_images(const wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES::request& req, wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES::response& res, epee::json_rpc::error& er, const connection_context *ctx)
   {
     if (!m_wallet) return not_open(er);
+    CHECK_IF_BACKGROUND_SYNCING();
     try
     {
       std::pair<uint64_t, std::vector<std::pair<crypto::key_image, crypto::signature>>> ski = m_wallet->export_key_images(req.all);
@@ -2918,6 +3070,7 @@ namespace tools
       er.message = "This command requires a trusted daemon.";
       return false;
     }
+    CHECK_IF_BACKGROUND_SYNCING();
     try
     {
       std::vector<std::pair<crypto::key_image, crypto::signature>> ski;
@@ -3132,6 +3285,7 @@ bool wallet_rpc_server::on_import_encrypted_key_images(const wallet_rpc::COMMAND
   bool wallet_rpc_server::on_get_address_book(const wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er, const connection_context *ctx)
   {
     if (!m_wallet) return not_open(er);
+    CHECK_IF_BACKGROUND_SYNCING();
     const auto ab = m_wallet->get_address_book();
     if (req.entries.empty())
     {
@@ -3177,6 +3331,7 @@ bool wallet_rpc_server::on_import_encrypted_key_images(const wallet_rpc::COMMAND
       er.message = "Command unavailable in restricted mode.";
       return false;
     }
+    CHECK_IF_BACKGROUND_SYNCING();
 
     cryptonote::address_parse_info info;
     er.message = "";
@@ -3219,6 +3374,7 @@ bool wallet_rpc_server::on_import_encrypted_key_images(const wallet_rpc::COMMAND
       er.message = "Command unavailable in restricted mode.";
       return false;
     }
+    CHECK_IF_BACKGROUND_SYNCING();
 
     const auto ab = m_wallet->get_address_book();
     if (req.index >= ab.size())
@@ -3281,6 +3437,7 @@ bool wallet_rpc_server::on_import_encrypted_key_images(const wallet_rpc::COMMAND
       er.message = "Command unavailable in restricted mode.";
       return false;
     }
+    CHECK_IF_BACKGROUND_SYNCING();
 
     const auto ab = m_wallet->get_address_book();
     if (req.index >= ab.size())
@@ -3351,6 +3508,7 @@ bool wallet_rpc_server::on_import_encrypted_key_images(const wallet_rpc::COMMAND
           er.message = "Command unavailable in restricted mode.";
           return false;
       }
+      CHECK_IF_BACKGROUND_SYNCING();
 
       std::unordered_set<crypto::hash> txids;
       std::list<std::string>::const_iterator i = req.txids.begin();
@@ -3390,6 +3548,7 @@ bool wallet_rpc_server::on_import_encrypted_key_images(const wallet_rpc::COMMAND
       er.message = "Command unavailable in restricted mode.";
       return false;
     }
+    CHECK_IF_BACKGROUND_SYNCING();
     try
     {
       m_wallet->rescan_spent();
@@ -3654,6 +3813,7 @@ bool wallet_rpc_server::on_import_encrypted_key_images(const wallet_rpc::COMMAND
       er.message = "Command unavailable in restricted mode.";
       return false;
     }
+    CHECK_IF_BACKGROUND_SYNCING();
     if (m_wallet->verify_password(req.old_password))
     {
       try
@@ -4187,6 +4347,7 @@ bool wallet_rpc_server::on_import_encrypted_key_images(const wallet_rpc::COMMAND
       er.message = "wallet is watch-only and cannot be made multisig";
       return false;
     }
+    CHECK_IF_BACKGROUND_SYNCING();
 
     res.multisig_info = m_wallet->get_multisig_first_kex_msg();
     return true;
@@ -4214,6 +4375,7 @@ bool wallet_rpc_server::on_import_encrypted_key_images(const wallet_rpc::COMMAND
       er.message = "wallet is watch-only and cannot be made multisig";
       return false;
     }
+    CHECK_IF_BACKGROUND_SYNCING();
 
     try
     {
@@ -4396,6 +4558,47 @@ bool wallet_rpc_server::on_import_encrypted_key_images(const wallet_rpc::COMMAND
     return true;
   }
   //------------------------------------------------------------------------------------------------------------------------------
+  bool wallet_rpc_server::on_get_multisig_key_exchange_booster(const wallet_rpc::COMMAND_RPC_GET_MULTISIG_KEY_EXCHANGE_BOOSTER::request& req, wallet_rpc::COMMAND_RPC_GET_MULTISIG_KEY_EXCHANGE_BOOSTER::response& res, epee::json_rpc::error& er, const connection_context *ctx)
+  {
+    if (!m_wallet) return not_open(er);
+    if (m_restricted)
+    {
+      er.code = WALLET_RPC_ERROR_CODE_DENIED;
+      er.message = "Command unavailable in restricted mode.";
+      return false;
+    }
+    const multisig::multisig_account_status ms_status{m_wallet->get_multisig_status()};
+
+    if (ms_status.is_ready)
+    {
+      er.code = WALLET_RPC_ERROR_CODE_ALREADY_MULTISIG;
+      er.message = "This wallet is multisig, and already finalized";
+      return false;
+    }
+
+    if (req.multisig_info.size() == 0)
+    {
+      er.code = WALLET_RPC_ERROR_CODE_THRESHOLD_NOT_REACHED;
+      er.message = "Needs multisig info from more participants";
+      return false;
+    }
+
+    try
+    {
+      res.multisig_info = m_wallet->get_multisig_key_exchange_booster(req.password,
+        req.multisig_info,
+        req.threshold,
+        req.num_signers);
+    }
+    catch (const std::exception &e)
+    {
+      er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+      er.message = std::string("Error calling exchange_multisig_info_booster: ") + e.what();
+      return false;
+    }
+    return true;
+  }
+  //------------------------------------------------------------------------------------------------------------------------------
   bool wallet_rpc_server::on_sign_multisig(const wallet_rpc::COMMAND_RPC_SIGN_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_SIGN_MULTISIG::response& res, epee::json_rpc::error& er, const connection_context *ctx)
   {
     if (!m_wallet) return not_open(er);
diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h
index 246fe7c27..af8a5f234 100644
--- a/src/wallet/wallet_rpc_server.h
+++ b/src/wallet/wallet_rpc_server.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
@@ -153,6 +153,7 @@ namespace tools
         MAP_JON_RPC_WE("import_multisig_info", on_import_multisig,  wallet_rpc::COMMAND_RPC_IMPORT_MULTISIG)
         MAP_JON_RPC_WE("finalize_multisig",  on_finalize_multisig,  wallet_rpc::COMMAND_RPC_FINALIZE_MULTISIG)
         MAP_JON_RPC_WE("exchange_multisig_keys",  on_exchange_multisig_keys,  wallet_rpc::COMMAND_RPC_EXCHANGE_MULTISIG_KEYS)
+        MAP_JON_RPC_WE("get_multisig_key_exchange_booster",  on_get_multisig_key_exchange_booster,  wallet_rpc::COMMAND_RPC_GET_MULTISIG_KEY_EXCHANGE_BOOSTER)
         MAP_JON_RPC_WE("sign_multisig",      on_sign_multisig,      wallet_rpc::COMMAND_RPC_SIGN_MULTISIG)
         MAP_JON_RPC_WE("submit_multisig",    on_submit_multisig,    wallet_rpc::COMMAND_RPC_SUBMIT_MULTISIG)
         MAP_JON_RPC_WE("validate_address",   on_validate_address,   wallet_rpc::COMMAND_RPC_VALIDATE_ADDRESS)
@@ -161,6 +162,9 @@ namespace tools
         MAP_JON_RPC_WE("set_log_categories", on_set_log_categories, wallet_rpc::COMMAND_RPC_SET_LOG_CATEGORIES)
         MAP_JON_RPC_WE("estimate_tx_size_and_weight", on_estimate_tx_size_and_weight, wallet_rpc::COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT)
         MAP_JON_RPC_WE("get_version",        on_get_version,        wallet_rpc::COMMAND_RPC_GET_VERSION)
+        MAP_JON_RPC_WE("setup_background_sync", on_setup_background_sync, wallet_rpc::COMMAND_RPC_SETUP_BACKGROUND_SYNC)
+        MAP_JON_RPC_WE("start_background_sync", on_start_background_sync, wallet_rpc::COMMAND_RPC_START_BACKGROUND_SYNC)
+        MAP_JON_RPC_WE("stop_background_sync", on_stop_background_sync, wallet_rpc::COMMAND_RPC_STOP_BACKGROUND_SYNC)
       END_JSON_RPC_MAP()
     END_URI_MAP2()
 
@@ -246,6 +250,7 @@ namespace tools
       bool on_import_multisig(const wallet_rpc::COMMAND_RPC_IMPORT_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_IMPORT_MULTISIG::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
       bool on_finalize_multisig(const wallet_rpc::COMMAND_RPC_FINALIZE_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_FINALIZE_MULTISIG::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
       bool on_exchange_multisig_keys(const wallet_rpc::COMMAND_RPC_EXCHANGE_MULTISIG_KEYS::request& req, wallet_rpc::COMMAND_RPC_EXCHANGE_MULTISIG_KEYS::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
+      bool on_get_multisig_key_exchange_booster(const wallet_rpc::COMMAND_RPC_GET_MULTISIG_KEY_EXCHANGE_BOOSTER::request& req, wallet_rpc::COMMAND_RPC_GET_MULTISIG_KEY_EXCHANGE_BOOSTER::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
       bool on_sign_multisig(const wallet_rpc::COMMAND_RPC_SIGN_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_SIGN_MULTISIG::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
       bool on_submit_multisig(const wallet_rpc::COMMAND_RPC_SUBMIT_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_SUBMIT_MULTISIG::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
       bool on_validate_address(const wallet_rpc::COMMAND_RPC_VALIDATE_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_VALIDATE_ADDRESS::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
@@ -254,6 +259,9 @@ namespace tools
       bool on_set_log_categories(const wallet_rpc::COMMAND_RPC_SET_LOG_CATEGORIES::request& req, wallet_rpc::COMMAND_RPC_SET_LOG_CATEGORIES::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
       bool on_estimate_tx_size_and_weight(const wallet_rpc::COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT::request& req, wallet_rpc::COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
       bool on_get_version(const wallet_rpc::COMMAND_RPC_GET_VERSION::request& req, wallet_rpc::COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
+      bool on_setup_background_sync(const wallet_rpc::COMMAND_RPC_SETUP_BACKGROUND_SYNC::request& req, wallet_rpc::COMMAND_RPC_SETUP_BACKGROUND_SYNC::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
+      bool on_start_background_sync(const wallet_rpc::COMMAND_RPC_START_BACKGROUND_SYNC::request& req, wallet_rpc::COMMAND_RPC_START_BACKGROUND_SYNC::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
+      bool on_stop_background_sync(const wallet_rpc::COMMAND_RPC_STOP_BACKGROUND_SYNC::request& req, wallet_rpc::COMMAND_RPC_STOP_BACKGROUND_SYNC::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
 
       //json rpc v2
       bool on_query_key(const wallet_rpc::COMMAND_RPC_QUERY_KEY::request& req, wallet_rpc::COMMAND_RPC_QUERY_KEY::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h
index 42b43a26b..9ae3881ed 100644
--- a/src/wallet/wallet_rpc_server_commands_defs.h
+++ b/src/wallet/wallet_rpc_server_commands_defs.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
@@ -52,2706 +52,2800 @@
 #define WALLET_RPC_VERSION MAKE_WALLET_RPC_VERSION(WALLET_RPC_VERSION_MAJOR, WALLET_RPC_VERSION_MINOR)
 namespace tools
 {
-  namespace wallet_rpc
-  {
+namespace wallet_rpc
+{
 #define WALLET_RPC_STATUS_OK      "OK"
 #define WALLET_RPC_STATUS_BUSY    "BUSY"
 
-    struct COMMAND_RPC_GET_BALANCE
+  struct COMMAND_RPC_GET_BALANCE
+  {
+    struct request_t
     {
-      struct request_t
-      {
-	uint32_t account_index;
-	std::set<uint32_t> address_indices;
-	bool all_accounts;
-	bool strict;
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(account_index)
-	  KV_SERIALIZE(address_indices)
-	  KV_SERIALIZE_OPT(all_accounts, false)
-	  KV_SERIALIZE_OPT(strict, false)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct per_subaddress_info
-      {
-	uint32_t account_index;
-	uint32_t address_index;
-	std::string address;
-	uint64_t balance;
-	uint64_t unlocked_balance;
-	std::string label;
-	uint64_t num_unspent_outputs;
-	uint64_t blocks_to_unlock;
-	uint64_t time_to_unlock;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(account_index)
-	  KV_SERIALIZE(address_index)
-	  KV_SERIALIZE(address)
-	  KV_SERIALIZE(balance)
-	  KV_SERIALIZE(unlocked_balance)
-	  KV_SERIALIZE(label)
-	  KV_SERIALIZE(num_unspent_outputs)
-	  KV_SERIALIZE(blocks_to_unlock)
-	  KV_SERIALIZE(time_to_unlock)
-	  END_KV_SERIALIZE_MAP()
-      };
-
-      struct response_t
-      {
-	uint64_t 	 balance;
-	uint64_t 	 unlocked_balance;
-	bool       multisig_import_needed;
-	std::vector<per_subaddress_info> per_subaddress;
-	uint64_t   blocks_to_unlock;
-	uint64_t   time_to_unlock;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(balance)
-	  KV_SERIALIZE(unlocked_balance)
-	  KV_SERIALIZE(multisig_import_needed)
-	  KV_SERIALIZE(per_subaddress)
-	  KV_SERIALIZE(blocks_to_unlock)
-	  KV_SERIALIZE(time_to_unlock)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      uint32_t account_index;
+      std::set<uint32_t> address_indices;
+      bool all_accounts;
+      bool strict;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(account_index)
+        KV_SERIALIZE(address_indices)
+        KV_SERIALIZE_OPT(all_accounts, false)
+        KV_SERIALIZE_OPT(strict, false)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct per_subaddress_info
+    {
+      uint32_t account_index;
+      uint32_t address_index;
+      std::string address;
+      uint64_t balance;
+      uint64_t unlocked_balance;
+      std::string label;
+      uint64_t num_unspent_outputs;
+      uint64_t blocks_to_unlock;
+      uint64_t time_to_unlock;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(account_index)
+        KV_SERIALIZE(address_index)
+        KV_SERIALIZE(address)
+        KV_SERIALIZE(balance)
+        KV_SERIALIZE(unlocked_balance)
+        KV_SERIALIZE(label)
+        KV_SERIALIZE(num_unspent_outputs)
+        KV_SERIALIZE(blocks_to_unlock)
+        KV_SERIALIZE(time_to_unlock)
+      END_KV_SERIALIZE_MAP()
+    };
+
+    struct response_t
+    {
+      uint64_t 	 balance;
+      uint64_t 	 unlocked_balance;
+      bool       multisig_import_needed;
+      std::vector<per_subaddress_info> per_subaddress;
+      uint64_t   blocks_to_unlock;
+      uint64_t   time_to_unlock;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(balance)
+        KV_SERIALIZE(unlocked_balance)
+        KV_SERIALIZE(multisig_import_needed)
+        KV_SERIALIZE(per_subaddress)
+        KV_SERIALIZE(blocks_to_unlock)
+        KV_SERIALIZE(time_to_unlock)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
 
     struct COMMAND_RPC_GET_ADDRESS
+  {
+    struct request_t
     {
-      struct request_t
-      {
-	uint32_t account_index;
-	std::vector<uint32_t> address_index;
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(account_index)
-	  KV_SERIALIZE(address_index)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
+      uint32_t account_index;
+      std::vector<uint32_t> address_index;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(account_index)
+        KV_SERIALIZE(address_index)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
 
-      struct address_info
-      {
-	std::string address;
-	std::string label;
-	uint32_t address_index;
-	bool used;
+    struct address_info
+    {
+      std::string address;
+      std::string label;
+      uint32_t address_index;
+      bool used;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(address)
-	  KV_SERIALIZE(label)
-	  KV_SERIALIZE(address_index)
-	  KV_SERIALIZE(used)
-	  END_KV_SERIALIZE_MAP()
-      };
-
-      struct response_t
-      {
-	std::string address;                  // to remain compatible with older RPC format
-	std::vector<address_info> addresses;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(address)
-	  KV_SERIALIZE(addresses)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(address)
+        KV_SERIALIZE(label)
+        KV_SERIALIZE(address_index)
+        KV_SERIALIZE(used)
+      END_KV_SERIALIZE_MAP()
     };
 
-    struct COMMAND_RPC_GET_ADDRESS_INDEX
+    struct response_t
     {
-      struct request_t
-      {
-	std::string address;
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(address)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
+      std::string address;                  // to remain compatible with older RPC format
+      std::vector<address_info> addresses;
 
-      struct response_t
-      {
-	cryptonote::subaddress_index index;
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(index)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(address)
+        KV_SERIALIZE(addresses)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_GET_ADDRESS_INDEX
+  {
+    struct request_t
+    {
+      std::string address;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(address)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      cryptonote::subaddress_index index;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(index)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_CREATE_ADDRESS
+  {
+    struct request_t
+    {
+      uint32_t    account_index;
+      uint32_t    count;
+      std::string label;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(account_index)
+        KV_SERIALIZE_OPT(count, 1U)
+        KV_SERIALIZE(label)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      std::string              address;
+      uint32_t                 address_index;
+      std::vector<std::string> addresses;
+      std::vector<uint32_t>    address_indices;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(address)
+        KV_SERIALIZE(address_index)
+        KV_SERIALIZE(addresses)
+        KV_SERIALIZE(address_indices)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_LABEL_ADDRESS
+  {
+    struct request_t
+    {
+      cryptonote::subaddress_index index;
+      std::string label;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(index)
+        KV_SERIALIZE(label)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      BEGIN_KV_SERIALIZE_MAP()
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_GET_ACCOUNTS
+  {
+    struct request_t
+    {
+      std::string tag;      // all accounts if empty, otherwise those accounts with this tag
+      bool strict_balances;
+      bool regexp; // allow regular expression filters if set to true
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(tag)
+        KV_SERIALIZE_OPT(strict_balances, false)
+        KV_SERIALIZE_OPT(regexp, false)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct subaddress_account_info
+    {
+      uint32_t account_index;
+      std::string base_address;
+      uint64_t balance;
+      uint64_t unlocked_balance;
+      std::string label;
+      std::string tag;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(account_index)
+        KV_SERIALIZE(base_address)
+        KV_SERIALIZE(balance)
+        KV_SERIALIZE(unlocked_balance)
+        KV_SERIALIZE(label)
+        KV_SERIALIZE(tag)
+      END_KV_SERIALIZE_MAP()
     };
 
-    struct COMMAND_RPC_CREATE_ADDRESS
+    struct response_t
     {
-      struct request_t
-      {
-	uint32_t    account_index;
-	uint32_t    count;
-	std::string label;
+      uint64_t total_balance;
+      uint64_t total_unlocked_balance;
+      std::vector<subaddress_account_info> subaddress_accounts;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(account_index)
-	  KV_SERIALIZE_OPT(count, 1U)
-	  KV_SERIALIZE(label)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(total_balance)
+        KV_SERIALIZE(total_unlocked_balance)
+        KV_SERIALIZE(subaddress_accounts)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
 
-      struct response_t
-      {
-	std::string              address;
-	uint32_t                 address_index;
-	std::vector<std::string> addresses;
-	std::vector<uint32_t>    address_indices;
+  struct COMMAND_RPC_CREATE_ACCOUNT
+  {
+    struct request_t
+    {
+      std::string label;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(label)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(address)
-	  KV_SERIALIZE(address_index)
-	  KV_SERIALIZE(addresses)
-	  KV_SERIALIZE(address_indices)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+    struct response_t
+    {
+      uint32_t account_index;
+      std::string address;      // the 0-th address for convenience
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(account_index)
+        KV_SERIALIZE(address)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_LABEL_ACCOUNT
+  {
+    struct request_t
+    {
+      uint32_t account_index;
+      std::string label;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(account_index)
+        KV_SERIALIZE(label)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      BEGIN_KV_SERIALIZE_MAP()
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_GET_ACCOUNT_TAGS
+  {
+    struct request_t
+    {
+      BEGIN_KV_SERIALIZE_MAP()
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct account_tag_info
+    {
+      std::string tag;
+      std::string label;
+      std::vector<uint32_t> accounts;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(tag)
+        KV_SERIALIZE(label)
+        KV_SERIALIZE(accounts)
+      END_KV_SERIALIZE_MAP()
     };
 
-    struct COMMAND_RPC_LABEL_ADDRESS
+    struct response_t
     {
-      struct request_t
-      {
-	cryptonote::subaddress_index index;
-	std::string label;
+      std::vector<account_tag_info> account_tags;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(index)
-	  KV_SERIALIZE(label)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	BEGIN_KV_SERIALIZE_MAP()
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(account_tags)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
 
-    struct COMMAND_RPC_GET_ACCOUNTS
+  struct COMMAND_RPC_TAG_ACCOUNTS
+  {
+    struct request_t
     {
-      struct request_t
-      {
-	std::string tag;      // all accounts if empty, otherwise those accounts with this tag
-	bool strict_balances;
-	bool regexp; // allow regular expression filters if set to true
+      std::string tag;
+      std::set<uint32_t> accounts;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(tag)
-	  KV_SERIALIZE_OPT(strict_balances, false)
-	  KV_SERIALIZE_OPT(regexp, false)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct subaddress_account_info
-      {
-	uint32_t account_index;
-	std::string base_address;
-	uint64_t balance;
-	uint64_t unlocked_balance;
-	std::string label;
-	std::string tag;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(account_index)
-	  KV_SERIALIZE(base_address)
-	  KV_SERIALIZE(balance)
-	  KV_SERIALIZE(unlocked_balance)
-	  KV_SERIALIZE(label)
-	  KV_SERIALIZE(tag)
-	  END_KV_SERIALIZE_MAP()
-      };
-
-      struct response_t
-      {
-	uint64_t total_balance;
-	uint64_t total_unlocked_balance;
-	std::vector<subaddress_account_info> subaddress_accounts;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(total_balance)
-	  KV_SERIALIZE(total_unlocked_balance)
-	  KV_SERIALIZE(subaddress_accounts)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(tag)
+        KV_SERIALIZE(accounts)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<request_t> request;
 
-    struct COMMAND_RPC_CREATE_ACCOUNT
+    struct response_t
     {
-      struct request_t
-      {
-	std::string label;
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(label)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	uint32_t account_index;
-	std::string address;      // the 0-th address for convenience
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(account_index)
-	  KV_SERIALIZE(address)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
 
-    struct COMMAND_RPC_LABEL_ACCOUNT
+  struct COMMAND_RPC_UNTAG_ACCOUNTS
+  {
+    struct request_t
     {
-      struct request_t
-      {
-	uint32_t account_index;
-	std::string label;
+      std::set<uint32_t> accounts;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(account_index)
-	  KV_SERIALIZE(label)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	BEGIN_KV_SERIALIZE_MAP()
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(accounts)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<request_t> request;
 
-    struct COMMAND_RPC_GET_ACCOUNT_TAGS
+    struct response_t
     {
-      struct request_t
-      {
-	BEGIN_KV_SERIALIZE_MAP()
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct account_tag_info
-      {
-	std::string tag;
-	std::string label;
-	std::vector<uint32_t> accounts;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(tag)
-	  KV_SERIALIZE(label)
-	  KV_SERIALIZE(accounts)
-	  END_KV_SERIALIZE_MAP()
-      };
-
-      struct response_t
-      {
-	std::vector<account_tag_info> account_tags;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(account_tags)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
 
-    struct COMMAND_RPC_TAG_ACCOUNTS
+  struct COMMAND_RPC_SET_ACCOUNT_TAG_DESCRIPTION
+  {
+    struct request_t
     {
-      struct request_t
-      {
-	std::string tag;
-	std::set<uint32_t> accounts;
+      std::string tag;
+      std::string description;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(tag)
-	  KV_SERIALIZE(accounts)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	BEGIN_KV_SERIALIZE_MAP()
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(tag)
+        KV_SERIALIZE(description)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<request_t> request;
 
-    struct COMMAND_RPC_UNTAG_ACCOUNTS
+    struct response_t
     {
-      struct request_t
-      {
-	std::set<uint32_t> accounts;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(accounts)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	BEGIN_KV_SERIALIZE_MAP()
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
-    };
-
-    struct COMMAND_RPC_SET_ACCOUNT_TAG_DESCRIPTION
-    {
-      struct request_t
-      {
-	std::string tag;
-	std::string description;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(tag)
-	  KV_SERIALIZE(description)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	BEGIN_KV_SERIALIZE_MAP()
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
 
     struct COMMAND_RPC_GET_HEIGHT
     {
       struct request_t
       {
-	BEGIN_KV_SERIALIZE_MAP()
-	  END_KV_SERIALIZE_MAP()
+        BEGIN_KV_SERIALIZE_MAP()
+        END_KV_SERIALIZE_MAP()
       };
       typedef epee::misc_utils::struct_init<request_t> request;
 
       struct response_t
       {
-	uint64_t  height;
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(height)
-	  END_KV_SERIALIZE_MAP()
+        uint64_t  height;
+        BEGIN_KV_SERIALIZE_MAP()
+          KV_SERIALIZE(height)
+        END_KV_SERIALIZE_MAP()
       };
       typedef epee::misc_utils::struct_init<response_t> response;
     };
 
-    struct transfer_destination
+  struct transfer_destination
+  {
+    uint64_t amount;
+    std::string address;
+    BEGIN_KV_SERIALIZE_MAP()
+      KV_SERIALIZE(amount)
+      KV_SERIALIZE(address)
+    END_KV_SERIALIZE_MAP()
+  };
+
+  struct COMMAND_RPC_FREEZE
+  {
+    struct request_t
+    {
+      std::string key_image;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(key_image)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      BEGIN_KV_SERIALIZE_MAP()
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_THAW
+  {
+    struct request_t
+    {
+      std::string key_image;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(key_image)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      BEGIN_KV_SERIALIZE_MAP()
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_FROZEN
+  {
+    struct request_t
+    {
+      std::string key_image;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(key_image)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      bool frozen;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(frozen)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct key_image_list
+  {
+    std::list<std::string> key_images;
+
+    BEGIN_KV_SERIALIZE_MAP()
+      KV_SERIALIZE(key_images)
+    END_KV_SERIALIZE_MAP()
+  };
+
+  struct amounts_list
+  {
+    std::list<uint64_t> amounts;
+
+    bool operator==(const amounts_list& other) const { return amounts == other.amounts; }
+
+    BEGIN_KV_SERIALIZE_MAP()
+      KV_SERIALIZE(amounts)
+    END_KV_SERIALIZE_MAP()
+  };
+
+  struct single_transfer_response
+  {
+    std::string tx_hash;
+    std::string tx_key;
+    uint64_t amount;
+    amounts_list amounts_by_dest;
+    uint64_t fee;
+    uint64_t weight;
+    std::string tx_blob;
+    std::string tx_metadata;
+    std::string multisig_txset;
+    std::string unsigned_txset;
+    key_image_list spent_key_images;
+
+    BEGIN_KV_SERIALIZE_MAP()
+      KV_SERIALIZE(tx_hash)
+      KV_SERIALIZE(tx_key)
+      KV_SERIALIZE(amount)
+      KV_SERIALIZE_OPT(amounts_by_dest, decltype(amounts_by_dest)())
+      KV_SERIALIZE(fee)
+      KV_SERIALIZE(weight)
+      KV_SERIALIZE(tx_blob)
+      KV_SERIALIZE(tx_metadata)
+      KV_SERIALIZE(multisig_txset)
+      KV_SERIALIZE(unsigned_txset)
+      KV_SERIALIZE(spent_key_images)
+    END_KV_SERIALIZE_MAP()
+  };
+
+  struct COMMAND_RPC_TRANSFER
+  {
+    struct request_t
+    {
+      std::list<transfer_destination> destinations;
+      uint32_t account_index;
+      std::set<uint32_t> subaddr_indices;
+      std::set<uint32_t> subtract_fee_from_outputs;
+      uint32_t priority;
+      uint64_t ring_size;
+      uint64_t unlock_time;
+      std::string payment_id;
+      bool get_tx_key;
+      bool do_not_relay;
+      bool get_tx_hex;
+      bool get_tx_metadata;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(destinations)
+        KV_SERIALIZE(account_index)
+        KV_SERIALIZE(subaddr_indices)
+        KV_SERIALIZE_OPT(subtract_fee_from_outputs, decltype(subtract_fee_from_outputs)())
+        KV_SERIALIZE(priority)
+        KV_SERIALIZE_OPT(ring_size, (uint64_t)0)
+        KV_SERIALIZE(unlock_time)
+        KV_SERIALIZE(payment_id)
+        KV_SERIALIZE(get_tx_key)
+        KV_SERIALIZE_OPT(do_not_relay, false)
+        KV_SERIALIZE_OPT(get_tx_hex, false)
+        KV_SERIALIZE_OPT(get_tx_metadata, false)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    typedef single_transfer_response response_t;
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct split_transfer_response
+  {
+    std::list<std::string> tx_hash_list;
+    std::list<std::string> tx_key_list;
+    std::list<uint64_t> amount_list;
+    std::list<amounts_list> amounts_by_dest_list;
+    std::list<uint64_t> fee_list;
+    std::list<uint64_t> weight_list;
+    std::list<std::string> tx_blob_list;
+    std::list<std::string> tx_metadata_list;
+    std::string multisig_txset;
+    std::string unsigned_txset;
+    std::list<key_image_list> spent_key_images_list;
+
+    BEGIN_KV_SERIALIZE_MAP()
+      KV_SERIALIZE(tx_hash_list)
+      KV_SERIALIZE(tx_key_list)
+      KV_SERIALIZE(amount_list)
+      KV_SERIALIZE_OPT(amounts_by_dest_list, decltype(amounts_by_dest_list)())
+      KV_SERIALIZE(fee_list)
+      KV_SERIALIZE(weight_list)
+      KV_SERIALIZE(tx_blob_list)
+      KV_SERIALIZE(tx_metadata_list)
+      KV_SERIALIZE(multisig_txset)
+      KV_SERIALIZE(unsigned_txset)
+      KV_SERIALIZE(spent_key_images_list)
+    END_KV_SERIALIZE_MAP()
+  };
+
+  struct COMMAND_RPC_TRANSFER_SPLIT
+  {
+    struct request_t
+    {
+      std::list<transfer_destination> destinations;
+      uint32_t account_index;
+      std::set<uint32_t> subaddr_indices;
+      uint32_t priority;
+      uint64_t ring_size;
+      uint64_t unlock_time;
+      std::string payment_id;
+      bool get_tx_keys;
+      bool do_not_relay;
+      bool get_tx_hex;
+      bool get_tx_metadata;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(destinations)
+        KV_SERIALIZE(account_index)
+        KV_SERIALIZE(subaddr_indices)
+        KV_SERIALIZE(priority)
+        KV_SERIALIZE_OPT(ring_size, (uint64_t)0)
+        KV_SERIALIZE(unlock_time)
+        KV_SERIALIZE(payment_id)
+        KV_SERIALIZE(get_tx_keys)
+        KV_SERIALIZE_OPT(do_not_relay, false)
+        KV_SERIALIZE_OPT(get_tx_hex, false)
+        KV_SERIALIZE_OPT(get_tx_metadata, false)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    typedef split_transfer_response response_t;
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_DESCRIBE_TRANSFER
+  {
+    struct recipient
     {
-      uint64_t amount;
       std::string address;
-      BEGIN_KV_SERIALIZE_MAP()
-	KV_SERIALIZE(amount)
-	KV_SERIALIZE(address)
-	END_KV_SERIALIZE_MAP()
-    };
-
-    struct COMMAND_RPC_FREEZE
-    {
-      struct request_t
-      {
-	std::string key_image;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(key_image)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	BEGIN_KV_SERIALIZE_MAP()
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
-    };
-
-    struct COMMAND_RPC_THAW
-    {
-      struct request_t
-      {
-	std::string key_image;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(key_image)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	BEGIN_KV_SERIALIZE_MAP()
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
-    };
-
-    struct COMMAND_RPC_FROZEN
-    {
-      struct request_t
-      {
-	std::string key_image;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(key_image)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	bool frozen;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(frozen)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
-    };
-
-    struct key_image_list
-    {
-      std::list<std::string> key_images;
-
-      BEGIN_KV_SERIALIZE_MAP()
-	KV_SERIALIZE(key_images)
-	END_KV_SERIALIZE_MAP()
-    };
-
-    struct amounts_list
-    {
-      std::list<uint64_t> amounts;
-
-      bool operator==(const amounts_list& other) const { return amounts == other.amounts; }
-
-      BEGIN_KV_SERIALIZE_MAP()
-	KV_SERIALIZE(amounts)
-	END_KV_SERIALIZE_MAP()
-    };
-
-    struct single_transfer_response
-    {
-      std::string tx_hash;
-      std::string tx_key;
       uint64_t amount;
-      amounts_list amounts_by_dest;
-      uint64_t fee;
-      uint64_t weight;
-      std::string tx_blob;
-      std::string tx_metadata;
-      std::string multisig_txset;
-      std::string unsigned_txset;
-      key_image_list spent_key_images;
 
       BEGIN_KV_SERIALIZE_MAP()
-	KV_SERIALIZE(tx_hash)
-	KV_SERIALIZE(tx_key)
-	KV_SERIALIZE(amount)
-	KV_SERIALIZE_OPT(amounts_by_dest, decltype(amounts_by_dest)())
-	KV_SERIALIZE(fee)
-	KV_SERIALIZE(weight)
-	KV_SERIALIZE(tx_blob)
-	KV_SERIALIZE(tx_metadata)
-	KV_SERIALIZE(multisig_txset)
-	KV_SERIALIZE(unsigned_txset)
-	KV_SERIALIZE(spent_key_images)
-	END_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(address)
+        KV_SERIALIZE(amount)
+      END_KV_SERIALIZE_MAP()
     };
 
-    struct COMMAND_RPC_TRANSFER
+    struct transfer_description
     {
-      struct request_t
-      {
-	std::list<transfer_destination> destinations;
-	uint32_t account_index;
-	std::set<uint32_t> subaddr_indices;
-	std::set<uint32_t> subtract_fee_from_outputs;
-	uint32_t priority;
-	uint64_t ring_size;
-	uint64_t unlock_time;
-	std::string payment_id;
-	bool get_tx_key;
-	bool do_not_relay;
-	bool get_tx_hex;
-	bool get_tx_metadata;
+      uint64_t amount_in;
+      uint64_t amount_out;
+      uint32_t ring_size;
+      uint64_t unlock_time;
+      std::list<recipient> recipients;
+      std::string payment_id;
+      uint64_t change_amount;
+      std::string change_address;
+      uint64_t fee;
+      uint32_t dummy_outputs;
+      std::string extra;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(destinations)
-	  KV_SERIALIZE(account_index)
-	  KV_SERIALIZE(subaddr_indices)
-	  KV_SERIALIZE_OPT(subtract_fee_from_outputs, decltype(subtract_fee_from_outputs)())
-	  KV_SERIALIZE(priority)
-	  KV_SERIALIZE_OPT(ring_size, (uint64_t)0)
-	  KV_SERIALIZE(unlock_time)
-	  KV_SERIALIZE(payment_id)
-	  KV_SERIALIZE(get_tx_key)
-	  KV_SERIALIZE_OPT(do_not_relay, false)
-	  KV_SERIALIZE_OPT(get_tx_hex, false)
-	  KV_SERIALIZE_OPT(get_tx_metadata, false)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      typedef single_transfer_response response_t;
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(amount_in)
+        KV_SERIALIZE(amount_out)
+        KV_SERIALIZE(ring_size)
+        KV_SERIALIZE(unlock_time)
+        KV_SERIALIZE(recipients)
+        KV_SERIALIZE(payment_id)
+        KV_SERIALIZE(change_amount)
+        KV_SERIALIZE(change_address)
+        KV_SERIALIZE(fee)
+        KV_SERIALIZE(dummy_outputs)
+        KV_SERIALIZE(extra)
+      END_KV_SERIALIZE_MAP()
     };
 
-    struct split_transfer_response
+    struct txset_summary
+    {
+      uint64_t amount_in;
+      uint64_t amount_out;
+      std::list<recipient> recipients;
+      uint64_t change_amount;
+      std::string change_address;
+      uint64_t fee;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(amount_in)
+        KV_SERIALIZE(amount_out)
+        KV_SERIALIZE(recipients)
+        KV_SERIALIZE(change_amount)
+        KV_SERIALIZE(change_address)
+        KV_SERIALIZE(fee)
+      END_KV_SERIALIZE_MAP()
+    };
+
+    struct request_t
+    {
+      std::string unsigned_txset;
+      std::string multisig_txset;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(unsigned_txset)
+        KV_SERIALIZE(multisig_txset)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      std::list<transfer_description> desc;
+      struct txset_summary summary;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(summary)
+        KV_SERIALIZE(desc)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_SIGN_TRANSFER
+  {
+    struct request_t
+    {
+      std::string unsigned_txset;
+      bool export_raw;
+      bool get_tx_keys;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(unsigned_txset)
+        KV_SERIALIZE_OPT(export_raw, false)
+        KV_SERIALIZE_OPT(get_tx_keys, false)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      std::string signed_txset;
+      std::list<std::string> tx_hash_list;
+      std::list<std::string> tx_raw_list;
+      std::list<std::string> tx_key_list;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(signed_txset)
+        KV_SERIALIZE(tx_hash_list)
+        KV_SERIALIZE(tx_raw_list)
+        KV_SERIALIZE(tx_key_list)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_SUBMIT_TRANSFER
+  {
+    struct request_t
+    {
+      std::string tx_data_hex;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(tx_data_hex)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
     {
       std::list<std::string> tx_hash_list;
-      std::list<std::string> tx_key_list;
-      std::list<uint64_t> amount_list;
-      std::list<amounts_list> amounts_by_dest_list;
-      std::list<uint64_t> fee_list;
-      std::list<uint64_t> weight_list;
-      std::list<std::string> tx_blob_list;
-      std::list<std::string> tx_metadata_list;
-      std::string multisig_txset;
-      std::string unsigned_txset;
-      std::list<key_image_list> spent_key_images_list;
 
       BEGIN_KV_SERIALIZE_MAP()
-	KV_SERIALIZE(tx_hash_list)
-	KV_SERIALIZE(tx_key_list)
-	KV_SERIALIZE(amount_list)
-	KV_SERIALIZE_OPT(amounts_by_dest_list, decltype(amounts_by_dest_list)())
-	KV_SERIALIZE(fee_list)
-	KV_SERIALIZE(weight_list)
-	KV_SERIALIZE(tx_blob_list)
-	KV_SERIALIZE(tx_metadata_list)
-	KV_SERIALIZE(multisig_txset)
-	KV_SERIALIZE(unsigned_txset)
-	KV_SERIALIZE(spent_key_images_list)
-	END_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(tx_hash_list)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
 
-    struct COMMAND_RPC_TRANSFER_SPLIT
+  struct COMMAND_RPC_SWEEP_DUST
+  {
+    struct request_t
     {
-      struct request_t
-      {
-	std::list<transfer_destination> destinations;
-	uint32_t account_index;
-	std::set<uint32_t> subaddr_indices;
-	uint32_t priority;
-	uint64_t ring_size;
-	uint64_t unlock_time;
-	std::string payment_id;
-	bool get_tx_keys;
-	bool do_not_relay;
-	bool get_tx_hex;
-	bool get_tx_metadata;
+      bool get_tx_keys;
+      bool do_not_relay;
+      bool get_tx_hex;
+      bool get_tx_metadata;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(destinations)
-	  KV_SERIALIZE(account_index)
-	  KV_SERIALIZE(subaddr_indices)
-	  KV_SERIALIZE(priority)
-	  KV_SERIALIZE_OPT(ring_size, (uint64_t)0)
-	  KV_SERIALIZE(unlock_time)
-	  KV_SERIALIZE(payment_id)
-	  KV_SERIALIZE(get_tx_keys)
-	  KV_SERIALIZE_OPT(do_not_relay, false)
-	  KV_SERIALIZE_OPT(get_tx_hex, false)
-	  KV_SERIALIZE_OPT(get_tx_metadata, false)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      typedef split_transfer_response response_t;
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(get_tx_keys)
+        KV_SERIALIZE_OPT(do_not_relay, false)
+        KV_SERIALIZE_OPT(get_tx_hex, false)
+        KV_SERIALIZE_OPT(get_tx_metadata, false)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<request_t> request;
 
-    struct COMMAND_RPC_DESCRIBE_TRANSFER
+    typedef split_transfer_response response_t;
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_SWEEP_ALL
+  {
+    struct request_t
     {
-      struct recipient
-      {
-	std::string address;
-	uint64_t amount;
+      std::string address;
+      uint32_t account_index;
+      std::set<uint32_t> subaddr_indices;
+      bool subaddr_indices_all;
+      uint32_t priority;
+      uint64_t ring_size;
+      uint64_t outputs;
+      uint64_t unlock_time;
+      std::string payment_id;
+      bool get_tx_keys;
+      uint64_t below_amount;
+      bool do_not_relay;
+      bool get_tx_hex;
+      bool get_tx_metadata;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(address)
-	  KV_SERIALIZE(amount)
-	  END_KV_SERIALIZE_MAP()
-      };
-
-      struct transfer_description
-      {
-	uint64_t amount_in;
-	uint64_t amount_out;
-	uint32_t ring_size;
-	uint64_t unlock_time;
-	std::list<recipient> recipients;
-	std::string payment_id;
-	uint64_t change_amount;
-	std::string change_address;
-	uint64_t fee;
-	uint32_t dummy_outputs;
-	std::string extra;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(amount_in)
-	  KV_SERIALIZE(amount_out)
-	  KV_SERIALIZE(ring_size)
-	  KV_SERIALIZE(unlock_time)
-	  KV_SERIALIZE(recipients)
-	  KV_SERIALIZE(payment_id)
-	  KV_SERIALIZE(change_amount)
-	  KV_SERIALIZE(change_address)
-	  KV_SERIALIZE(fee)
-	  KV_SERIALIZE(dummy_outputs)
-	  KV_SERIALIZE(extra)
-	  END_KV_SERIALIZE_MAP()
-      };
-
-      struct txset_summary
-      {
-	uint64_t amount_in;
-	uint64_t amount_out;
-	std::list<recipient> recipients;
-	uint64_t change_amount;
-	std::string change_address;
-	uint64_t fee;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(amount_in)
-	  KV_SERIALIZE(amount_out)
-	  KV_SERIALIZE(recipients)
-	  KV_SERIALIZE(change_amount)
-	  KV_SERIALIZE(change_address)
-	  KV_SERIALIZE(fee)
-	  END_KV_SERIALIZE_MAP()
-      };
-
-      struct request_t
-      {
-	std::string unsigned_txset;
-	std::string multisig_txset;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(unsigned_txset)
-	  KV_SERIALIZE(multisig_txset)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	std::list<transfer_description> desc;
-	struct txset_summary summary;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(summary)
-	  KV_SERIALIZE(desc)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(address)
+        KV_SERIALIZE(account_index)
+        KV_SERIALIZE(subaddr_indices)
+        KV_SERIALIZE_OPT(subaddr_indices_all, false)
+        KV_SERIALIZE(priority)
+        KV_SERIALIZE_OPT(ring_size, (uint64_t)0)
+        KV_SERIALIZE_OPT(outputs, (uint64_t)1)
+        KV_SERIALIZE(unlock_time)
+        KV_SERIALIZE(payment_id)
+        KV_SERIALIZE(get_tx_keys)
+        KV_SERIALIZE(below_amount)
+        KV_SERIALIZE_OPT(do_not_relay, false)
+        KV_SERIALIZE_OPT(get_tx_hex, false)
+        KV_SERIALIZE_OPT(get_tx_metadata, false)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<request_t> request;
 
-    struct COMMAND_RPC_SIGN_TRANSFER
+    typedef split_transfer_response response_t;
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_SWEEP_SINGLE
+  {
+    struct request_t
     {
-      struct request_t
-      {
-	std::string unsigned_txset;
-	bool export_raw;
-	bool get_tx_keys;
+      std::string address;
+      uint32_t priority;
+      uint64_t ring_size;
+      uint64_t outputs;
+      uint64_t unlock_time;
+      std::string payment_id;
+      bool get_tx_key;
+      std::string key_image;
+      bool do_not_relay;
+      bool get_tx_hex;
+      bool get_tx_metadata;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(unsigned_txset)
-	  KV_SERIALIZE_OPT(export_raw, false)
-	  KV_SERIALIZE_OPT(get_tx_keys, false)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	std::string signed_txset;
-	std::list<std::string> tx_hash_list;
-	std::list<std::string> tx_raw_list;
-	std::list<std::string> tx_key_list;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(signed_txset)
-	  KV_SERIALIZE(tx_hash_list)
-	  KV_SERIALIZE(tx_raw_list)
-	  KV_SERIALIZE(tx_key_list)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(address)
+        KV_SERIALIZE(priority)
+        KV_SERIALIZE_OPT(ring_size, (uint64_t)0)
+        KV_SERIALIZE_OPT(outputs, (uint64_t)1)
+        KV_SERIALIZE(unlock_time)
+        KV_SERIALIZE(payment_id)
+        KV_SERIALIZE(get_tx_key)
+        KV_SERIALIZE(key_image)
+        KV_SERIALIZE_OPT(do_not_relay, false)
+        KV_SERIALIZE_OPT(get_tx_hex, false)
+        KV_SERIALIZE_OPT(get_tx_metadata, false)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<request_t> request;
 
-    struct COMMAND_RPC_SUBMIT_TRANSFER
+    typedef single_transfer_response response_t;
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_RELAY_TX
+  {
+    struct request_t
     {
-      struct request_t
-      {
-	std::string tx_data_hex;
+      std::string hex;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(tx_data_hex)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	std::list<std::string> tx_hash_list;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(tx_hash_list)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(hex)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<request_t> request;
 
-    struct COMMAND_RPC_SWEEP_DUST
+    struct response_t
     {
-      struct request_t
-      {
-	bool get_tx_keys;
-	bool do_not_relay;
-	bool get_tx_hex;
-	bool get_tx_metadata;
+      std::string tx_hash;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(get_tx_keys)
-	  KV_SERIALIZE_OPT(do_not_relay, false)
-	  KV_SERIALIZE_OPT(get_tx_hex, false)
-	  KV_SERIALIZE_OPT(get_tx_metadata, false)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      typedef split_transfer_response response_t;
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(tx_hash)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
 
-    struct COMMAND_RPC_SWEEP_ALL
+  struct COMMAND_RPC_STORE
+  {
+    struct request_t
     {
-      struct request_t
-      {
-	std::string address;
-	uint32_t account_index;
-	std::set<uint32_t> subaddr_indices;
-	bool subaddr_indices_all;
-	uint32_t priority;
-	uint64_t ring_size;
-	uint64_t outputs;
-	uint64_t unlock_time;
-	std::string payment_id;
-	bool get_tx_keys;
-	uint64_t below_amount;
-	bool do_not_relay;
-	bool get_tx_hex;
-	bool get_tx_metadata;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(address)
-	  KV_SERIALIZE(account_index)
-	  KV_SERIALIZE(subaddr_indices)
-	  KV_SERIALIZE_OPT(subaddr_indices_all, false)
-	  KV_SERIALIZE(priority)
-	  KV_SERIALIZE_OPT(ring_size, (uint64_t)0)
-	  KV_SERIALIZE_OPT(outputs, (uint64_t)1)
-	  KV_SERIALIZE(unlock_time)
-	  KV_SERIALIZE(payment_id)
-	  KV_SERIALIZE(get_tx_keys)
-	  KV_SERIALIZE(below_amount)
-	  KV_SERIALIZE_OPT(do_not_relay, false)
-	  KV_SERIALIZE_OPT(get_tx_hex, false)
-	  KV_SERIALIZE_OPT(get_tx_metadata, false)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      typedef split_transfer_response response_t;
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<request_t> request;
 
-    struct COMMAND_RPC_SWEEP_SINGLE
+    struct response_t
     {
-      struct request_t
-      {
-	std::string address;
-	uint32_t priority;
-	uint64_t ring_size;
-	uint64_t outputs;
-	uint64_t unlock_time;
-	std::string payment_id;
-	bool get_tx_key;
-	std::string key_image;
-	bool do_not_relay;
-	bool get_tx_hex;
-	bool get_tx_metadata;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(address)
-	  KV_SERIALIZE(priority)
-	  KV_SERIALIZE_OPT(ring_size, (uint64_t)0)
-	  KV_SERIALIZE_OPT(outputs, (uint64_t)1)
-	  KV_SERIALIZE(unlock_time)
-	  KV_SERIALIZE(payment_id)
-	  KV_SERIALIZE(get_tx_key)
-	  KV_SERIALIZE(key_image)
-	  KV_SERIALIZE_OPT(do_not_relay, false)
-	  KV_SERIALIZE_OPT(get_tx_hex, false)
-	  KV_SERIALIZE_OPT(get_tx_metadata, false)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      typedef single_transfer_response response_t;
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
 
-    struct COMMAND_RPC_RELAY_TX
-    {
-      struct request_t
-      {
-	std::string hex;
+  struct payment_details
+  {
+    std::string payment_id;
+    std::string tx_hash;
+    uint64_t amount;
+    uint64_t block_height;
+    uint64_t unlock_time;
+    bool locked;
+    cryptonote::subaddress_index subaddr_index;
+    std::string address;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(hex)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
+    BEGIN_KV_SERIALIZE_MAP()
+      KV_SERIALIZE(payment_id)
+      KV_SERIALIZE(tx_hash)
+      KV_SERIALIZE(amount)
+      KV_SERIALIZE(block_height)
+      KV_SERIALIZE(unlock_time)
+      KV_SERIALIZE(locked)
+      KV_SERIALIZE(subaddr_index)
+      KV_SERIALIZE(address)
+    END_KV_SERIALIZE_MAP()
+  };
 
-      struct response_t
-      {
-	std::string tx_hash;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(tx_hash)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
-    };
-
-    struct COMMAND_RPC_STORE
-    {
-      struct request_t
-      {
-	BEGIN_KV_SERIALIZE_MAP()
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	BEGIN_KV_SERIALIZE_MAP()
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
-    };
-
-    struct payment_details
+  struct COMMAND_RPC_GET_PAYMENTS
+  {
+    struct request_t
     {
       std::string payment_id;
-      std::string tx_hash;
-      uint64_t amount;
-      uint64_t block_height;
-      uint64_t unlock_time;
-      bool locked;
-      cryptonote::subaddress_index subaddr_index;
-      std::string address;
 
       BEGIN_KV_SERIALIZE_MAP()
-	KV_SERIALIZE(payment_id)
-	KV_SERIALIZE(tx_hash)
-	KV_SERIALIZE(amount)
-	KV_SERIALIZE(block_height)
-	KV_SERIALIZE(unlock_time)
-	KV_SERIALIZE(locked)
-	KV_SERIALIZE(subaddr_index)
-	KV_SERIALIZE(address)
-	END_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(payment_id)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<request_t> request;
 
-    struct COMMAND_RPC_GET_PAYMENTS
+    struct response_t
     {
-      struct request_t
-      {
-	std::string payment_id;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(payment_id)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	std::list<payment_details> payments;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(payments)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
-    };
-
-    struct COMMAND_RPC_GET_BULK_PAYMENTS
-    {
-      struct request_t
-      {
-	std::vector<std::string> payment_ids;
-	uint64_t min_block_height;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(payment_ids)
-	  KV_SERIALIZE(min_block_height)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	std::list<payment_details> payments;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(payments)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
-    };
-
-    struct transfer_details
-    {
-      uint64_t amount;
-      bool spent;
-      uint64_t global_index;
-      std::string tx_hash;
-      cryptonote::subaddress_index subaddr_index;
-      std::string key_image;
-      std::string pubkey; // owned output public key found
-      uint64_t block_height;
-      bool frozen;
-      bool unlocked;
+      std::list<payment_details> payments;
 
       BEGIN_KV_SERIALIZE_MAP()
-	KV_SERIALIZE(amount)
-	KV_SERIALIZE(spent)
-	KV_SERIALIZE(global_index)
-	KV_SERIALIZE(tx_hash)
-	KV_SERIALIZE(subaddr_index)
-	KV_SERIALIZE(key_image)
-	KV_SERIALIZE(pubkey)
-	KV_SERIALIZE(block_height)
-	KV_SERIALIZE(frozen)
-	KV_SERIALIZE(unlocked)
-	END_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(payments)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
 
-    struct COMMAND_RPC_INCOMING_TRANSFERS
+  struct COMMAND_RPC_GET_BULK_PAYMENTS
+  {
+    struct request_t
     {
-      struct request_t
-      {
-	std::string transfer_type;
-	uint32_t account_index;
-	std::set<uint32_t> subaddr_indices;
+      std::vector<std::string> payment_ids;
+      uint64_t min_block_height;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(transfer_type)
-	  KV_SERIALIZE(account_index)
-	  KV_SERIALIZE(subaddr_indices)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	std::list<transfer_details> transfers;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(transfers)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(payment_ids)
+        KV_SERIALIZE(min_block_height)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<request_t> request;
 
-    //JSON RPC V2
-    struct COMMAND_RPC_QUERY_KEY
+    struct response_t
     {
-      struct request_t
-      {
-	std::string key_type;
+      std::list<payment_details> payments;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(key_type)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	std::string key;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(key)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(payments)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
 
-    struct COMMAND_RPC_MAKE_INTEGRATED_ADDRESS
+  struct transfer_details
+  {
+    uint64_t amount;
+    bool spent;
+    uint64_t global_index;
+    std::string tx_hash;
+    cryptonote::subaddress_index subaddr_index;
+    std::string key_image;
+    std::string pubkey; // owned output public key found
+    uint64_t block_height;
+    bool frozen;
+    bool unlocked;
+
+    BEGIN_KV_SERIALIZE_MAP()
+      KV_SERIALIZE(amount)
+      KV_SERIALIZE(spent)
+      KV_SERIALIZE(global_index)
+      KV_SERIALIZE(tx_hash)
+      KV_SERIALIZE(subaddr_index)
+      KV_SERIALIZE(key_image)
+      KV_SERIALIZE(pubkey)
+      KV_SERIALIZE(block_height)
+      KV_SERIALIZE(frozen)
+      KV_SERIALIZE(unlocked)
+    END_KV_SERIALIZE_MAP()
+  };
+
+  struct COMMAND_RPC_INCOMING_TRANSFERS
+  {
+    struct request_t
     {
-      struct request_t
-      {
-	std::string standard_address;
-	std::string payment_id;
+      std::string transfer_type;
+      uint32_t account_index;
+      std::set<uint32_t> subaddr_indices;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(standard_address)
-	  KV_SERIALIZE(payment_id)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	std::string integrated_address;
-	std::string payment_id;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(integrated_address)
-	  KV_SERIALIZE(payment_id)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(transfer_type)
+        KV_SERIALIZE(account_index)
+        KV_SERIALIZE(subaddr_indices)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<request_t> request;
 
-    struct COMMAND_RPC_SPLIT_INTEGRATED_ADDRESS
+    struct response_t
     {
-      struct request_t
-      {
-	std::string integrated_address;
+      std::list<transfer_details> transfers;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(integrated_address)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	std::string standard_address;
-	std::string payment_id;
-	bool is_subaddress;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(standard_address)
-	  KV_SERIALIZE(payment_id)
-	  KV_SERIALIZE(is_subaddress)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(transfers)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
 
-    struct COMMAND_RPC_STOP_WALLET
+  //JSON RPC V2
+  struct COMMAND_RPC_QUERY_KEY
+  {
+    struct request_t
     {
-      struct request_t
-      {
-	BEGIN_KV_SERIALIZE_MAP()
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
+      std::string key_type;
 
-      struct response_t
-      {
-	BEGIN_KV_SERIALIZE_MAP()
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(key_type)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<request_t> request;
 
-    struct COMMAND_RPC_RESCAN_BLOCKCHAIN
+    struct response_t
     {
-      struct request_t
-      {
-	bool hard;
+      std::string key;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE_OPT(hard, false)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	BEGIN_KV_SERIALIZE_MAP()
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(key)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
 
-    struct COMMAND_RPC_SET_TX_NOTES
+  struct COMMAND_RPC_MAKE_INTEGRATED_ADDRESS
+  {
+    struct request_t
     {
-      struct request_t
-      {
-	std::list<std::string> txids;
-	std::list<std::string> notes;
+      std::string standard_address;
+      std::string payment_id;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(txids)
-	  KV_SERIALIZE(notes)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	BEGIN_KV_SERIALIZE_MAP()
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(standard_address)
+        KV_SERIALIZE(payment_id)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<request_t> request;
 
-    struct COMMAND_RPC_GET_TX_NOTES
+    struct response_t
     {
-      struct request_t
-      {
-	std::list<std::string> txids;
+      std::string integrated_address;
+      std::string payment_id;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(txids)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	std::list<std::string> notes;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(notes)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(integrated_address)
+        KV_SERIALIZE(payment_id)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
 
-    struct COMMAND_RPC_SET_ATTRIBUTE
+  struct COMMAND_RPC_SPLIT_INTEGRATED_ADDRESS
+  {
+    struct request_t
     {
-      struct request_t
-      {
-	std::string key;
-	std::string value;
+      std::string integrated_address;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(key)
-	  KV_SERIALIZE(value)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	BEGIN_KV_SERIALIZE_MAP()
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(integrated_address)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<request_t> request;
 
-    struct COMMAND_RPC_GET_ATTRIBUTE
+    struct response_t
     {
-      struct request_t
-      {
+      std::string standard_address;
+      std::string payment_id;
+      bool is_subaddress;
 
-	std::string key;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(key)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	std::string value;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(value)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(standard_address)
+        KV_SERIALIZE(payment_id)
+        KV_SERIALIZE(is_subaddress)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
 
-    struct COMMAND_RPC_GET_TX_KEY
+  struct COMMAND_RPC_STOP_WALLET
+  {
+    struct request_t
     {
-      struct request_t
-      {
-	std::string txid;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(txid)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	std::string tx_key;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(tx_key)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<request_t> request;
 
-    struct COMMAND_RPC_CHECK_TX_KEY
+    struct response_t
     {
-      struct request_t
-      {
-	std::string txid;
-	std::string tx_key;
-	std::string address;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(txid)
-	  KV_SERIALIZE(tx_key)
-	  KV_SERIALIZE(address)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	uint64_t received;
-	bool in_pool;
-	uint64_t confirmations;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(received)
-	  KV_SERIALIZE(in_pool)
-	  KV_SERIALIZE(confirmations)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
 
-    struct COMMAND_RPC_GET_TX_PROOF
+  struct COMMAND_RPC_RESCAN_BLOCKCHAIN
+  {
+    struct request_t
     {
-      struct request_t
-      {
-	std::string txid;
-	std::string address;
-	std::string message;
+      bool hard;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(txid)
-	  KV_SERIALIZE(address)
-	  KV_SERIALIZE(message)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	std::string signature;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(signature)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE_OPT(hard, false)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<request_t> request;
 
-    struct COMMAND_RPC_CHECK_TX_PROOF
+    struct response_t
     {
-      struct request_t
-      {
-	std::string txid;
-	std::string address;
-	std::string message;
-	std::string signature;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(txid)
-	  KV_SERIALIZE(address)
-	  KV_SERIALIZE(message)
-	  KV_SERIALIZE(signature)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	bool good;
-	uint64_t received;
-	bool in_pool;
-	uint64_t confirmations;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(good)
-	  KV_SERIALIZE(received)
-	  KV_SERIALIZE(in_pool)
-	  KV_SERIALIZE(confirmations)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
 
-    typedef std::vector<uint64_t> amounts_container;
-    struct transfer_entry
+  struct COMMAND_RPC_SET_TX_NOTES
+  {
+    struct request_t
+    {
+      std::list<std::string> txids;
+      std::list<std::string> notes;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(txids)
+        KV_SERIALIZE(notes)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      BEGIN_KV_SERIALIZE_MAP()
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_GET_TX_NOTES
+  {
+    struct request_t
+    {
+      std::list<std::string> txids;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(txids)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      std::list<std::string> notes;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(notes)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_SET_ATTRIBUTE
+  {
+    struct request_t
+    {
+      std::string key;
+      std::string value;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(key)
+        KV_SERIALIZE(value)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      BEGIN_KV_SERIALIZE_MAP()
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_GET_ATTRIBUTE
+  {
+    struct request_t
+    {
+
+      std::string key;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(key)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      std::string value;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(value)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_GET_TX_KEY
+  {
+    struct request_t
     {
       std::string txid;
-      std::string payment_id;
-      uint64_t height;
-      uint64_t timestamp;
-      uint64_t amount;
-      amounts_container amounts;
-      uint64_t fee;
-      std::string note;
-      std::list<transfer_destination> destinations;
-      std::string type;
-      uint64_t unlock_time;
-      bool locked;
-      cryptonote::subaddress_index subaddr_index;
-      std::vector<cryptonote::subaddress_index> subaddr_indices;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(txid)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      std::string tx_key;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(tx_key)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_CHECK_TX_KEY
+  {
+    struct request_t
+    {
+      std::string txid;
+      std::string tx_key;
       std::string address;
-      bool double_spend_seen;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(txid)
+        KV_SERIALIZE(tx_key)
+        KV_SERIALIZE(address)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      uint64_t received;
+      bool in_pool;
       uint64_t confirmations;
-      uint64_t suggested_confirmations_threshold;
 
       BEGIN_KV_SERIALIZE_MAP()
-	KV_SERIALIZE(txid)
-	KV_SERIALIZE(payment_id)
-	KV_SERIALIZE(height)
-	KV_SERIALIZE(timestamp)
-	KV_SERIALIZE(amount)
-	KV_SERIALIZE(amounts)
-	KV_SERIALIZE(fee)
-	KV_SERIALIZE(note)
-	KV_SERIALIZE(destinations)
-	KV_SERIALIZE(type)
-	KV_SERIALIZE(unlock_time)
-	KV_SERIALIZE(locked)
-	KV_SERIALIZE(subaddr_index)
-	KV_SERIALIZE(subaddr_indices)
-	KV_SERIALIZE(address)
-	KV_SERIALIZE(double_spend_seen)
-	KV_SERIALIZE_OPT(confirmations, (uint64_t)0)
-	KV_SERIALIZE_OPT(suggested_confirmations_threshold, (uint64_t)0)
-	END_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(received)
+        KV_SERIALIZE(in_pool)
+        KV_SERIALIZE(confirmations)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
 
-    struct COMMAND_RPC_GET_SPEND_PROOF
+  struct COMMAND_RPC_GET_TX_PROOF
+  {
+    struct request_t
     {
-      struct request_t
-      {
-	std::string txid;
-	std::string message;
+      std::string txid;
+      std::string address;
+      std::string message;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(txid)
-	  KV_SERIALIZE(message)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	std::string signature;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(signature)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(txid)
+        KV_SERIALIZE(address)
+        KV_SERIALIZE(message)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<request_t> request;
 
-    struct COMMAND_RPC_CHECK_SPEND_PROOF
+    struct response_t
     {
-      struct request_t
-      {
-	std::string txid;
-	std::string message;
-	std::string signature;
+      std::string signature;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(txid)
-	  KV_SERIALIZE(message)
-	  KV_SERIALIZE(signature)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	bool good;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(good)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(signature)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
 
-    struct COMMAND_RPC_GET_RESERVE_PROOF
+  struct COMMAND_RPC_CHECK_TX_PROOF
+  {
+    struct request_t
     {
-      struct request_t
-      {
-	bool all;
-	uint32_t account_index;     // ignored when `all` is true
-	uint64_t amount;            // ignored when `all` is true
-	std::string message;
+      std::string txid;
+      std::string address;
+      std::string message;
+      std::string signature;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(all)
-	  KV_SERIALIZE(account_index)
-	  KV_SERIALIZE(amount)
-	  KV_SERIALIZE(message)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	std::string signature;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(signature)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(txid)
+        KV_SERIALIZE(address)
+        KV_SERIALIZE(message)
+        KV_SERIALIZE(signature)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<request_t> request;
 
-    struct COMMAND_RPC_CHECK_RESERVE_PROOF
+    struct response_t
     {
-      struct request_t
-      {
-	std::string address;
-	std::string message;
-	std::string signature;
+      bool good;
+      uint64_t received;
+      bool in_pool;
+      uint64_t confirmations;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(address)
-	  KV_SERIALIZE(message)
-	  KV_SERIALIZE(signature)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	bool good;
-	uint64_t total;
-	uint64_t spent;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(good)
-	  KV_SERIALIZE(total)
-	  KV_SERIALIZE(spent)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(good)
+        KV_SERIALIZE(received)
+        KV_SERIALIZE(in_pool)
+        KV_SERIALIZE(confirmations)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
 
-    struct COMMAND_RPC_GET_TRANSFERS
+  typedef std::vector<uint64_t> amounts_container;
+  struct transfer_entry
+  {
+    std::string txid;
+    std::string payment_id;
+    uint64_t height;
+    uint64_t timestamp;
+    uint64_t amount;
+    amounts_container amounts;
+    uint64_t fee;
+    std::string note;
+    std::list<transfer_destination> destinations;
+    std::string type;
+    uint64_t unlock_time;
+    bool locked;
+    cryptonote::subaddress_index subaddr_index;
+    std::vector<cryptonote::subaddress_index> subaddr_indices;
+    std::string address;
+    bool double_spend_seen;
+    uint64_t confirmations;
+    uint64_t suggested_confirmations_threshold;
+
+    BEGIN_KV_SERIALIZE_MAP()
+      KV_SERIALIZE(txid)
+      KV_SERIALIZE(payment_id)
+      KV_SERIALIZE(height)
+      KV_SERIALIZE(timestamp)
+      KV_SERIALIZE(amount)
+      KV_SERIALIZE(amounts)
+      KV_SERIALIZE(fee)
+      KV_SERIALIZE(note)
+      KV_SERIALIZE(destinations)
+      KV_SERIALIZE(type)
+      KV_SERIALIZE(unlock_time)
+      KV_SERIALIZE(locked)
+      KV_SERIALIZE(subaddr_index)
+      KV_SERIALIZE(subaddr_indices)
+      KV_SERIALIZE(address)
+      KV_SERIALIZE(double_spend_seen)
+      KV_SERIALIZE_OPT(confirmations, (uint64_t)0)
+      KV_SERIALIZE_OPT(suggested_confirmations_threshold, (uint64_t)0)
+    END_KV_SERIALIZE_MAP()
+  };
+
+  struct COMMAND_RPC_GET_SPEND_PROOF
+  {
+    struct request_t
     {
-      struct request_t
-      {
-	bool in;
-	bool out;
-	bool pending;
-	bool failed;
-	bool pool;
+      std::string txid;
+      std::string message;
 
-	bool filter_by_height;
-	uint64_t min_height;
-	uint64_t max_height;
-	uint32_t account_index;
-	std::set<uint32_t> subaddr_indices;
-	bool all_accounts;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(in)
-	  KV_SERIALIZE(out)
-	  KV_SERIALIZE(pending)
-	  KV_SERIALIZE(failed)
-	  KV_SERIALIZE(pool)
-	  KV_SERIALIZE(filter_by_height)
-	  KV_SERIALIZE(min_height)
-	  KV_SERIALIZE_OPT(max_height, (uint64_t)CRYPTONOTE_MAX_BLOCK_NUMBER)
-	  KV_SERIALIZE(account_index)
-	  KV_SERIALIZE(subaddr_indices)
-	  KV_SERIALIZE_OPT(all_accounts, false)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	std::list<transfer_entry> in;
-	std::list<transfer_entry> out;
-	std::list<transfer_entry> pending;
-	std::list<transfer_entry> failed;
-	std::list<transfer_entry> pool;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(in)
-	  KV_SERIALIZE(out)
-	  KV_SERIALIZE(pending)
-	  KV_SERIALIZE(failed)
-	  KV_SERIALIZE(pool)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(txid)
+        KV_SERIALIZE(message)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<request_t> request;
 
-    struct COMMAND_RPC_GET_TRANSFER_BY_TXID
+    struct response_t
     {
-      struct request_t
-      {
-	std::string txid;
-	uint32_t account_index;
+      std::string signature;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(txid)
-	  KV_SERIALIZE_OPT(account_index, (uint32_t)0)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	transfer_entry transfer;
-	std::list<transfer_entry> transfers;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(transfer)
-	  KV_SERIALIZE(transfers)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(signature)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
 
-    struct COMMAND_RPC_SIGN
+  struct COMMAND_RPC_CHECK_SPEND_PROOF
+  {
+    struct request_t
     {
-      struct request_t
-      {
-	std::string data;
-	uint32_t account_index;
-	uint32_t address_index;
-	std::string signature_type;
+      std::string txid;
+      std::string message;
+      std::string signature;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(data)
-	  KV_SERIALIZE_OPT(account_index, 0u)
-	  KV_SERIALIZE_OPT(address_index, 0u)
-	  KV_SERIALIZE(signature_type)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	std::string signature;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(signature)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(txid)
+        KV_SERIALIZE(message)
+        KV_SERIALIZE(signature)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<request_t> request;
 
-    struct COMMAND_RPC_VERIFY
+    struct response_t
     {
-      struct request_t
-      {
-	std::string data;
-	std::string address;
-	std::string signature;
+      bool good;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(data)
-	  KV_SERIALIZE(address)
-	  KV_SERIALIZE(signature)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	bool good;
-	unsigned version;
-	bool old;
-	std::string signature_type;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(good)
-	  KV_SERIALIZE(version)
-	  KV_SERIALIZE(old)
-	  KV_SERIALIZE(signature_type)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(good)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
 
-    struct COMMAND_RPC_EXPORT_OUTPUTS
+  struct COMMAND_RPC_GET_RESERVE_PROOF
+  {
+    struct request_t
     {
-      struct request_t
-      {
-	bool all;
-	uint32_t start;
-	uint32_t count;
+      bool all;
+      uint32_t account_index;     // ignored when `all` is true
+      uint64_t amount;            // ignored when `all` is true
+      std::string message;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(all)
-	  KV_SERIALIZE_OPT(start, 0u)
-	  KV_SERIALIZE_OPT(count, 0xffffffffu)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	std::string outputs_data_hex;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(outputs_data_hex)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(all)
+        KV_SERIALIZE(account_index)
+        KV_SERIALIZE(amount)
+        KV_SERIALIZE(message)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<request_t> request;
 
-    struct COMMAND_RPC_IMPORT_OUTPUTS
+    struct response_t
     {
-      struct request_t
-      {
-	std::string outputs_data_hex;
+      std::string signature;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(outputs_data_hex)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	uint64_t num_imported;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(num_imported)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(signature)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
 
-    struct COMMAND_RPC_EXPORT_KEY_IMAGES
-    {
-      struct request_t
-      {
-	bool all;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE_OPT(all, false)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct signed_key_image
-      {
-	std::string key_image;
-	std::string signature;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(key_image)
-	  KV_SERIALIZE(signature)
-	  END_KV_SERIALIZE_MAP()
-      };
-
-      struct response_t
-      {
-	uint32_t offset;
-	std::vector<signed_key_image> signed_key_images;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(offset)
-	  KV_SERIALIZE(signed_key_images)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
-    };
-
-    struct COMMAND_RPC_IMPORT_KEY_IMAGES
-    {
-      struct signed_key_image
-      {
-	std::string key_image;
-	std::string signature;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(key_image)
-	  KV_SERIALIZE(signature)
-	  END_KV_SERIALIZE_MAP()
-      };
-
-      struct request_t
-      {
-	uint32_t offset;
-	std::vector<signed_key_image> signed_key_images;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE_OPT(offset, (uint32_t)0)
-	  KV_SERIALIZE(signed_key_images)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	uint64_t height;
-	uint64_t spent;
-	uint64_t unspent;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(height)
-	  KV_SERIALIZE(spent)
-	  KV_SERIALIZE(unspent)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
-    };
-    struct COMMAND_RPC_EXPORT_ENCRYPTED_KEY_IMAGES
-    {
-      struct request_t
-      {
-	bool all;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE_OPT(all, false)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	uint32_t offset;
-	std::string encrypted_key_images_blob;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(offset)
-	  KV_SERIALIZE(encrypted_key_images_blob)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
-    };
-
-    struct COMMAND_RPC_IMPORT_ENCRYPTED_KEY_IMAGES
-    {
-      struct request_t
-      {
-	uint32_t offset;
-	std::string encrypted_key_images_blob;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE_OPT(offset, (uint32_t)0)
-	  KV_SERIALIZE(encrypted_key_images_blob)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	uint64_t height;
-	uint64_t spent;
-	uint64_t unspent;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(height)
-	  KV_SERIALIZE(spent)
-	  KV_SERIALIZE(unspent)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
-    };
-
-    struct uri_spec
+  struct COMMAND_RPC_CHECK_RESERVE_PROOF
+  {
+    struct request_t
     {
       std::string address;
-      std::string payment_id;
-      uint64_t amount;
-      std::string tx_description;
-      std::string recipient_name;
+      std::string message;
+      std::string signature;
 
       BEGIN_KV_SERIALIZE_MAP()
-	KV_SERIALIZE(address)
-	KV_SERIALIZE(payment_id)
-	KV_SERIALIZE(amount)
-	KV_SERIALIZE(tx_description)
-	KV_SERIALIZE(recipient_name)
-	END_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(address)
+        KV_SERIALIZE(message)
+        KV_SERIALIZE(signature)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<request_t> request;
 
-    struct COMMAND_RPC_MAKE_URI
+    struct response_t
     {
-      struct request_t: public uri_spec
-      {
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
+      bool good;
+      uint64_t total;
+      uint64_t spent;
 
-      struct response_t
-      {
-	std::string uri;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(uri)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(good)
+        KV_SERIALIZE(total)
+        KV_SERIALIZE(spent)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
 
-    struct COMMAND_RPC_PARSE_URI
+  struct COMMAND_RPC_GET_TRANSFERS
+  {
+    struct request_t
     {
-      struct request_t
-      {
-	std::string uri;
+      bool in;
+      bool out;
+      bool pending;
+      bool failed;
+      bool pool;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(uri)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
+      bool filter_by_height;
+      uint64_t min_height;
+      uint64_t max_height;
+      uint32_t account_index;
+      std::set<uint32_t> subaddr_indices;
+      bool all_accounts;
 
-      struct response_t
-      {
-	uri_spec uri;
-	std::vector<std::string> unknown_parameters;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(uri)
-	  KV_SERIALIZE(unknown_parameters)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(in)
+        KV_SERIALIZE(out)
+        KV_SERIALIZE(pending)
+        KV_SERIALIZE(failed)
+        KV_SERIALIZE(pool)
+        KV_SERIALIZE(filter_by_height)
+        KV_SERIALIZE(min_height)
+        KV_SERIALIZE_OPT(max_height, (uint64_t)CRYPTONOTE_MAX_BLOCK_NUMBER)
+        KV_SERIALIZE(account_index)
+        KV_SERIALIZE(subaddr_indices)
+        KV_SERIALIZE_OPT(all_accounts, false)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<request_t> request;
 
-    struct COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY
+    struct response_t
     {
-      struct request_t
-      {
-	std::string address;
-	std::string description;
+      std::list<transfer_entry> in;
+      std::list<transfer_entry> out;
+      std::list<transfer_entry> pending;
+      std::list<transfer_entry> failed;
+      std::list<transfer_entry> pool;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(address)
-	  KV_SERIALIZE(description)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	uint64_t index;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(index)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(in)
+        KV_SERIALIZE(out)
+        KV_SERIALIZE(pending)
+        KV_SERIALIZE(failed)
+        KV_SERIALIZE(pool)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
 
-    struct COMMAND_RPC_EDIT_ADDRESS_BOOK_ENTRY
+  struct COMMAND_RPC_GET_TRANSFER_BY_TXID
+  {
+    struct request_t
     {
-      struct request_t
-      {
-	uint64_t index;
-	bool set_address;
-	std::string address;
-	bool set_description;
-	std::string description;
+      std::string txid;
+      uint32_t account_index;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(index)
-	  KV_SERIALIZE(set_address)
-	  KV_SERIALIZE(address)
-	  KV_SERIALIZE(set_description)
-	  KV_SERIALIZE(description)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	BEGIN_KV_SERIALIZE_MAP()
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(txid)
+        KV_SERIALIZE_OPT(account_index, (uint32_t)0)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<request_t> request;
 
-    struct COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY
+    struct response_t
     {
-      struct request_t
-      {
-	std::list<uint64_t> entries;
+      transfer_entry transfer;
+      std::list<transfer_entry> transfers;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(entries)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct entry
-      {
-	uint64_t index;
-	std::string address;
-	std::string description;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(index)
-	  KV_SERIALIZE(address)
-	  KV_SERIALIZE(description)
-	  END_KV_SERIALIZE_MAP()
-      };
-
-      struct response_t
-      {
-	std::vector<entry> entries;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(entries)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(transfer)
+        KV_SERIALIZE(transfers)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
 
-    struct COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY
+  struct COMMAND_RPC_SIGN
+  {
+    struct request_t
     {
-      struct request_t
-      {
-	uint64_t index;
+      std::string data;
+      uint32_t account_index;
+      uint32_t address_index;
+      std::string signature_type;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(index)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	BEGIN_KV_SERIALIZE_MAP()
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(data)
+        KV_SERIALIZE_OPT(account_index, 0u)
+        KV_SERIALIZE_OPT(address_index, 0u)
+        KV_SERIALIZE(signature_type)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<request_t> request;
 
-    struct COMMAND_RPC_RESCAN_SPENT
+    struct response_t
     {
-      struct request_t
-      {
-	BEGIN_KV_SERIALIZE_MAP()
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
+      std::string signature;
 
-      struct response_t
-      {
-	BEGIN_KV_SERIALIZE_MAP()
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(signature)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
 
-    struct COMMAND_RPC_REFRESH
+  struct COMMAND_RPC_VERIFY
+  {
+    struct request_t
     {
-      struct request_t
-      {
-	uint64_t start_height;
+      std::string data;
+      std::string address;
+      std::string signature;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE_OPT(start_height, (uint64_t) 0)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	uint64_t blocks_fetched;
-	bool received_money;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(blocks_fetched)
-	  KV_SERIALIZE(received_money)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(data)
+        KV_SERIALIZE(address)
+        KV_SERIALIZE(signature)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<request_t> request;
 
-    struct COMMAND_RPC_AUTO_REFRESH
+    struct response_t
     {
-      struct request_t
-      {
-	bool enable;
-	uint32_t period; // seconds
+      bool good;
+      unsigned version;
+      bool old;
+      std::string signature_type;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE_OPT(enable, true)
-	  KV_SERIALIZE_OPT(period, (uint32_t)0)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	BEGIN_KV_SERIALIZE_MAP()
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(good)
+        KV_SERIALIZE(version)
+        KV_SERIALIZE(old)
+        KV_SERIALIZE(signature_type)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
 
-    struct COMMAND_RPC_SCAN_TX
+  struct COMMAND_RPC_EXPORT_OUTPUTS
+  {
+    struct request_t
     {
-      struct request_t
-      {
-	std::list<std::string> txids;
+      bool all;
+      uint32_t start;
+      uint32_t count;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(txids)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	BEGIN_KV_SERIALIZE_MAP()
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(all)
+        KV_SERIALIZE_OPT(start, 0u)
+        KV_SERIALIZE_OPT(count, 0xffffffffu)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<request_t> request;
 
-    struct COMMAND_RPC_START_MINING
+    struct response_t
     {
-      struct request_t
-      {
-	uint64_t    threads_count;
-	bool        do_background_mining;
-	bool        ignore_battery;
+      std::string outputs_data_hex;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(threads_count)
-	  KV_SERIALIZE(do_background_mining)        
-	  KV_SERIALIZE(ignore_battery)        
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	BEGIN_KV_SERIALIZE_MAP()
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(outputs_data_hex)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
 
-    struct COMMAND_RPC_STOP_MINING
+  struct COMMAND_RPC_IMPORT_OUTPUTS
+  {
+    struct request_t
     {
-      struct request_t
-      {
-	BEGIN_KV_SERIALIZE_MAP()
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
+      std::string outputs_data_hex;
 
-      struct response_t
-      {
-	BEGIN_KV_SERIALIZE_MAP()
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(outputs_data_hex)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<request_t> request;
 
-    struct COMMAND_RPC_GET_LANGUAGES
+    struct response_t
     {
-      struct request_t
-      {
-	BEGIN_KV_SERIALIZE_MAP()
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
+      uint64_t num_imported;
 
-      struct response_t
-      {
-	std::vector<std::string> languages;
-	std::vector<std::string> languages_local;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(languages)
-	  KV_SERIALIZE(languages_local)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(num_imported)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
 
-    struct COMMAND_RPC_CREATE_WALLET
+  struct COMMAND_RPC_EXPORT_KEY_IMAGES
+  {
+    struct request_t
     {
-      struct request_t
-      {
-	std::string filename;
-	std::string password;
-	std::string language;
+      bool all;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(filename)
-	  KV_SERIALIZE(password)
-	  KV_SERIALIZE(language)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	BEGIN_KV_SERIALIZE_MAP()
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE_OPT(all, false)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<request_t> request;
 
-    struct COMMAND_RPC_OPEN_WALLET
+    struct signed_key_image
     {
-      struct request_t
-      {
-	std::string filename;
-	std::string password;
-	bool autosave_current;
+      std::string key_image;
+      std::string signature;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(filename)
-	  KV_SERIALIZE(password)
-	  KV_SERIALIZE_OPT(autosave_current, true)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	BEGIN_KV_SERIALIZE_MAP()
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(key_image)
+        KV_SERIALIZE(signature)
+      END_KV_SERIALIZE_MAP()
     };
 
-    struct COMMAND_RPC_CLOSE_WALLET
+    struct response_t
     {
-      struct request_t
-      {
-	bool autosave_current;
+      uint32_t offset;
+      std::vector<signed_key_image> signed_key_images;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE_OPT(autosave_current, true)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	BEGIN_KV_SERIALIZE_MAP()
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(offset)
+        KV_SERIALIZE(signed_key_images)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
 
-    struct COMMAND_RPC_CHANGE_WALLET_PASSWORD
+  struct COMMAND_RPC_IMPORT_KEY_IMAGES
+  {
+    struct signed_key_image
     {
-      struct request_t
-      {
-	std::string old_password;
-	std::string new_password;
+      std::string key_image;
+      std::string signature;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(old_password)
-	  KV_SERIALIZE(new_password)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	BEGIN_KV_SERIALIZE_MAP()
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(key_image)
+        KV_SERIALIZE(signature)
+      END_KV_SERIALIZE_MAP()
     };
 
-    struct COMMAND_RPC_GENERATE_FROM_KEYS
+    struct request_t
     {
-      struct request
-      {
-	uint64_t restore_height;
-	std::string filename;
-	std::string address;
-	std::string spendkey;
-	std::string viewkey;
-	std::string password;
-	bool autosave_current;
-	std::string language;
+      uint32_t offset;
+      std::vector<signed_key_image> signed_key_images;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE_OPT(restore_height, (uint64_t)0)
-	  KV_SERIALIZE(filename)
-	  KV_SERIALIZE(address)
-	  KV_SERIALIZE(spendkey)
-	  KV_SERIALIZE(viewkey)
-	  KV_SERIALIZE(password)
-	  KV_SERIALIZE_OPT(autosave_current, true)
-	  KV_SERIALIZE(language)
-	  END_KV_SERIALIZE_MAP()
-      };
-
-      struct response
-      {
-	std::string address;
-	std::string info;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(address)
-	  KV_SERIALIZE(info)
-	  END_KV_SERIALIZE_MAP()
-      };
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE_OPT(offset, (uint32_t)0)
+        KV_SERIALIZE(signed_key_images)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<request_t> request;
 
-    struct COMMAND_RPC_RESTORE_DETERMINISTIC_WALLET
+    struct response_t
     {
-      struct request_t
-      {
-	uint64_t restore_height;
-	std::string filename;
-	std::string seed;
-	std::string seed_offset;
-	std::string password;
-	std::string language;
-	bool autosave_current;
-	bool enable_multisig_experimental;
+      uint64_t height;
+      uint64_t spent;
+      uint64_t unspent;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE_OPT(restore_height, (uint64_t)0)
-	  KV_SERIALIZE(filename)
-	  KV_SERIALIZE(seed)
-	  KV_SERIALIZE(seed_offset)
-	  KV_SERIALIZE(password)
-	  KV_SERIALIZE(language)
-	  KV_SERIALIZE_OPT(autosave_current, true)
-	  KV_SERIALIZE_OPT(enable_multisig_experimental, false)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	std::string address;
-	std::string seed;
-	std::string info;
-	bool was_deprecated;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(address)
-	  KV_SERIALIZE(seed)
-	  KV_SERIALIZE(info)
-	  KV_SERIALIZE(was_deprecated)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(height)
+        KV_SERIALIZE(spent)
+        KV_SERIALIZE(unspent)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
 
-    struct COMMAND_RPC_IS_MULTISIG
+  struct COMMAND_RPC_EXPORT_ENCRYPTED_KEY_IMAGES
+  {
+    struct request_t
     {
-      struct request_t
-      {
-	BEGIN_KV_SERIALIZE_MAP()
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
+      bool all;
 
-      struct response_t
-      {
-	bool multisig;
-	bool kex_is_done;
-	bool ready;
-	uint32_t threshold;
-	uint32_t total;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(multisig)
-	  KV_SERIALIZE(kex_is_done)
-	  KV_SERIALIZE(ready)
-	  KV_SERIALIZE(threshold)
-	  KV_SERIALIZE(total)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE_OPT(all, false)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<request_t> request;
 
-    struct COMMAND_RPC_PREPARE_MULTISIG
+    struct response_t
     {
-      struct request_t
-      {
-	bool enable_multisig_experimental;
+      uint32_t offset;
+      std::string encrypted_key_images_blob;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE_OPT(enable_multisig_experimental, false)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	std::string multisig_info;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(multisig_info)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(offset)
+        KV_SERIALIZE(encrypted_key_images_blob)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
 
-    struct COMMAND_RPC_MAKE_MULTISIG
+  struct COMMAND_RPC_IMPORT_ENCRYPTED_KEY_IMAGES
+  {
+    struct request_t
     {
-      struct request_t
-      {
-	std::vector<std::string> multisig_info;
-	uint32_t threshold;
-	std::string password;
+      uint32_t offset;
+      std::string encrypted_key_images_blob;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(multisig_info)
-	  KV_SERIALIZE(threshold)
-	  KV_SERIALIZE(password)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	std::string address;
-	std::string multisig_info;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(address)
-	  KV_SERIALIZE(multisig_info)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE_OPT(offset, (uint32_t)0)
+        KV_SERIALIZE(encrypted_key_images_blob)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<request_t> request;
 
-    struct COMMAND_RPC_EXPORT_MULTISIG
+    struct response_t
     {
-      struct request_t
-      {
-	BEGIN_KV_SERIALIZE_MAP()
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
+      uint64_t height;
+      uint64_t spent;
+      uint64_t unspent;
 
-      struct response_t
-      {
-	std::string info;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(info)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(height)
+        KV_SERIALIZE(spent)
+        KV_SERIALIZE(unspent)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
 
-    struct COMMAND_RPC_IMPORT_MULTISIG
+  struct uri_spec
+  {
+    std::string address;
+    std::string payment_id;
+    uint64_t amount;
+    std::string tx_description;
+    std::string recipient_name;
+
+    BEGIN_KV_SERIALIZE_MAP()
+      KV_SERIALIZE(address)
+      KV_SERIALIZE(payment_id)
+      KV_SERIALIZE(amount)
+      KV_SERIALIZE(tx_description)
+      KV_SERIALIZE(recipient_name)
+    END_KV_SERIALIZE_MAP()
+  };
+
+  struct COMMAND_RPC_MAKE_URI
+  {
+    struct request_t: public uri_spec
     {
-      struct request_t
-      {
-	std::vector<std::string> info;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(info)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	uint64_t n_outputs;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(n_outputs)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
     };
+    typedef epee::misc_utils::struct_init<request_t> request;
 
-    struct COMMAND_RPC_FINALIZE_MULTISIG
+    struct response_t
     {
-      // NOP
-      struct request_t
-      {
-	BEGIN_KV_SERIALIZE_MAP()
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
+      std::string uri;
 
-      struct response_t
-      {
-	BEGIN_KV_SERIALIZE_MAP()
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(uri)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
 
-    struct COMMAND_RPC_EXCHANGE_MULTISIG_KEYS
+  struct COMMAND_RPC_PARSE_URI
+  {
+    struct request_t
     {
-      struct request_t
-      {
-	std::string password;
-	std::vector<std::string> multisig_info;
-	bool force_update_use_with_caution;
+      std::string uri;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(password)
-	  KV_SERIALIZE(multisig_info)
-	  KV_SERIALIZE_OPT(force_update_use_with_caution, false)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	std::string address;
-	std::string multisig_info;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(address)
-	  KV_SERIALIZE(multisig_info)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(uri)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<request_t> request;
 
-    struct COMMAND_RPC_SIGN_MULTISIG
+    struct response_t
     {
-      struct request_t
-      {
-	std::string tx_data_hex;
+      uri_spec uri;
+      std::vector<std::string> unknown_parameters;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(tx_data_hex)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	std::string tx_data_hex;
-	std::list<std::string> tx_hash_list;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(tx_data_hex)
-	  KV_SERIALIZE(tx_hash_list)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(uri)
+        KV_SERIALIZE(unknown_parameters)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
 
-    struct COMMAND_RPC_SUBMIT_MULTISIG
+  struct COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY
+  {
+    struct request_t
     {
-      struct request_t
-      {
-	std::string tx_data_hex;
+      std::string address;
+      std::string description;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(tx_data_hex)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	std::list<std::string> tx_hash_list;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(tx_hash_list)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(address)
+        KV_SERIALIZE(description)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<request_t> request;
 
-    struct COMMAND_RPC_GET_VERSION
+    struct response_t
     {
-      struct request_t
-      {
-	BEGIN_KV_SERIALIZE_MAP()
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
+      uint64_t index;
 
-      struct response_t
-      {
-	uint32_t version;
-	bool release;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(version)
-	  KV_SERIALIZE(release)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(index)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
 
-    struct COMMAND_RPC_VALIDATE_ADDRESS
+  struct COMMAND_RPC_EDIT_ADDRESS_BOOK_ENTRY
+  {
+    struct request_t
     {
-      struct request_t
-      {
-	std::string address;
-	bool any_net_type;
-	bool allow_openalias;
+      uint64_t index;
+      bool set_address;
+      std::string address;
+      bool set_description;
+      std::string description;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(address)
-	  KV_SERIALIZE_OPT(any_net_type, false)
-	  KV_SERIALIZE_OPT(allow_openalias, false)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	bool valid;
-	bool integrated;
-	bool subaddress;
-	std::string nettype;
-	std::string openalias_address;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(valid)
-	  KV_SERIALIZE(integrated)
-	  KV_SERIALIZE(subaddress)
-	  KV_SERIALIZE(nettype)
-	  KV_SERIALIZE(openalias_address)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(index)
+        KV_SERIALIZE(set_address)
+        KV_SERIALIZE(address)
+        KV_SERIALIZE(set_description)
+        KV_SERIALIZE(description)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<request_t> request;
 
-    struct COMMAND_RPC_SET_DAEMON
+    struct response_t
     {
-      struct request_t
-      {
-	std::string address;
-	std::string username;
-	std::string password;
-	bool trusted;
-	std::string ssl_support; // disabled, enabled, autodetect
-	std::string ssl_private_key_path;
-	std::string ssl_certificate_path;
-	std::string ssl_ca_file;
-	std::vector<std::string> ssl_allowed_fingerprints;
-	bool ssl_allow_any_cert;
-	std::string proxy;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(address)
-	  KV_SERIALIZE(username)
-	  KV_SERIALIZE(password)
-	  KV_SERIALIZE_OPT(trusted, false)
-	  KV_SERIALIZE_OPT(ssl_support, (std::string)"autodetect")
-	  KV_SERIALIZE(ssl_private_key_path)
-	  KV_SERIALIZE(ssl_certificate_path)
-	  KV_SERIALIZE(ssl_ca_file)
-	  KV_SERIALIZE(ssl_allowed_fingerprints)
-	  KV_SERIALIZE_OPT(ssl_allow_any_cert, false)
-	  KV_SERIALIZE_OPT(proxy, (std::string)"")
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	BEGIN_KV_SERIALIZE_MAP()
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
 
-    struct COMMAND_RPC_SET_LOG_LEVEL
+  struct COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY
+  {
+    struct request_t
     {
-      struct request_t
-      {
-	int8_t level;
+      std::list<uint64_t> entries;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(level)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	BEGIN_KV_SERIALIZE_MAP()
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(entries)
+      END_KV_SERIALIZE_MAP()
     };
+    typedef epee::misc_utils::struct_init<request_t> request;
 
-    struct COMMAND_RPC_SET_LOG_CATEGORIES
+    struct entry
     {
-      struct request_t
-      {
-	std::string categories;
+      uint64_t index;
+      std::string address;
+      std::string description;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(categories)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
-
-      struct response_t
-      {
-	std::string categories;
-
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(categories)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(index)
+        KV_SERIALIZE(address)
+        KV_SERIALIZE(description)
+      END_KV_SERIALIZE_MAP()
     };
 
-    struct COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT
+    struct response_t
     {
-      struct request_t
-      {
-	uint32_t n_inputs;
-	uint32_t n_outputs;
-	uint32_t ring_size;
-	bool rct;
+      std::vector<entry> entries;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(n_inputs)
-	  KV_SERIALIZE(n_outputs)
-	  KV_SERIALIZE_OPT(ring_size, 0u)
-	  KV_SERIALIZE_OPT(rct, true)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<request_t> request;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(entries)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
 
-      struct response_t
-      {
-	uint64_t size;
-	uint64_t weight;
+  struct COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY
+  {
+    struct request_t
+    {
+      uint64_t index;
 
-	BEGIN_KV_SERIALIZE_MAP()
-	  KV_SERIALIZE(size)
-	  KV_SERIALIZE(weight)
-	  END_KV_SERIALIZE_MAP()
-      };
-      typedef epee::misc_utils::struct_init<response_t> response;
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(index)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      BEGIN_KV_SERIALIZE_MAP()
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_RESCAN_SPENT
+  {
+    struct request_t
+    {
+      BEGIN_KV_SERIALIZE_MAP()
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      BEGIN_KV_SERIALIZE_MAP()
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_REFRESH
+  {
+    struct request_t
+    {
+      uint64_t start_height;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE_OPT(start_height, (uint64_t) 0)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      uint64_t blocks_fetched;
+      bool received_money;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(blocks_fetched)
+        KV_SERIALIZE(received_money)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_AUTO_REFRESH
+  {
+    struct request_t
+    {
+      bool enable;
+      uint32_t period; // seconds
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE_OPT(enable, true)
+        KV_SERIALIZE_OPT(period, (uint32_t)0)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      BEGIN_KV_SERIALIZE_MAP()
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_SCAN_TX
+  {
+    struct request_t
+    {
+      std::list<std::string> txids;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(txids)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      BEGIN_KV_SERIALIZE_MAP()
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_START_MINING
+  {
+    struct request_t
+    {
+      uint64_t    threads_count;
+      bool        do_background_mining;
+      bool        ignore_battery;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(threads_count)
+        KV_SERIALIZE(do_background_mining)        
+        KV_SERIALIZE(ignore_battery)        
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      BEGIN_KV_SERIALIZE_MAP()
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_STOP_MINING
+  {
+    struct request_t
+    {
+      BEGIN_KV_SERIALIZE_MAP()
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      BEGIN_KV_SERIALIZE_MAP()
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_GET_LANGUAGES
+  {
+    struct request_t
+    {
+      BEGIN_KV_SERIALIZE_MAP()
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      std::vector<std::string> languages;
+      std::vector<std::string> languages_local;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(languages)
+        KV_SERIALIZE(languages_local)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_CREATE_WALLET
+  {
+    struct request_t
+    {
+      std::string filename;
+      std::string password;
+      std::string language;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(filename)
+        KV_SERIALIZE(password)
+        KV_SERIALIZE(language)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      BEGIN_KV_SERIALIZE_MAP()
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_OPEN_WALLET
+  {
+    struct request_t
+    {
+      std::string filename;
+      std::string password;
+      bool autosave_current;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(filename)
+        KV_SERIALIZE(password)
+        KV_SERIALIZE_OPT(autosave_current, true)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      BEGIN_KV_SERIALIZE_MAP()
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_CLOSE_WALLET
+  {
+    struct request_t
+    {
+      bool autosave_current;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE_OPT(autosave_current, true)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      BEGIN_KV_SERIALIZE_MAP()
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_CHANGE_WALLET_PASSWORD
+  {
+    struct request_t
+    {
+      std::string old_password;
+      std::string new_password;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(old_password)
+        KV_SERIALIZE(new_password)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      BEGIN_KV_SERIALIZE_MAP()
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_GENERATE_FROM_KEYS
+  {
+    struct request
+    {
+      uint64_t restore_height;
+      std::string filename;
+      std::string address;
+      std::string spendkey;
+      std::string viewkey;
+      std::string password;
+      bool autosave_current;
+      std::string language;
+
+      BEGIN_KV_SERIALIZE_MAP()
+      KV_SERIALIZE_OPT(restore_height, (uint64_t)0)
+      KV_SERIALIZE(filename)
+      KV_SERIALIZE(address)
+      KV_SERIALIZE(spendkey)
+      KV_SERIALIZE(viewkey)
+      KV_SERIALIZE(password)
+      KV_SERIALIZE_OPT(autosave_current, true)
+      KV_SERIALIZE(language)
+      END_KV_SERIALIZE_MAP()
     };
 
-  }
+    struct response
+    {
+      std::string address;
+      std::string info;
+
+      BEGIN_KV_SERIALIZE_MAP()
+      KV_SERIALIZE(address)
+      KV_SERIALIZE(info)
+      END_KV_SERIALIZE_MAP()
+    };
+  };
+
+  struct COMMAND_RPC_RESTORE_DETERMINISTIC_WALLET
+  {
+    struct request_t
+    {
+      uint64_t restore_height;
+      std::string filename;
+      std::string seed;
+      std::string seed_offset;
+      std::string password;
+      std::string language;
+      bool autosave_current;
+      bool enable_multisig_experimental;
+
+      BEGIN_KV_SERIALIZE_MAP()
+      KV_SERIALIZE_OPT(restore_height, (uint64_t)0)
+      KV_SERIALIZE(filename)
+      KV_SERIALIZE(seed)
+      KV_SERIALIZE(seed_offset)
+      KV_SERIALIZE(password)
+      KV_SERIALIZE(language)
+      KV_SERIALIZE_OPT(autosave_current, true)
+      KV_SERIALIZE_OPT(enable_multisig_experimental, false)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      std::string address;
+      std::string seed;
+      std::string info;
+      bool was_deprecated;
+
+      BEGIN_KV_SERIALIZE_MAP()
+      KV_SERIALIZE(address)
+      KV_SERIALIZE(seed)
+      KV_SERIALIZE(info)
+      KV_SERIALIZE(was_deprecated)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_IS_MULTISIG
+  {
+    struct request_t
+    {
+      BEGIN_KV_SERIALIZE_MAP()
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      bool multisig;
+      bool kex_is_done;
+      bool ready;
+      uint32_t threshold;
+      uint32_t total;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(multisig)
+        KV_SERIALIZE(kex_is_done)
+        KV_SERIALIZE(ready)
+        KV_SERIALIZE(threshold)
+        KV_SERIALIZE(total)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_PREPARE_MULTISIG
+  {
+    struct request_t
+    {
+      bool enable_multisig_experimental;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE_OPT(enable_multisig_experimental, false)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      std::string multisig_info;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(multisig_info)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_MAKE_MULTISIG
+  {
+    struct request_t
+    {
+      std::vector<std::string> multisig_info;
+      uint32_t threshold;
+      std::string password;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(multisig_info)
+        KV_SERIALIZE(threshold)
+        KV_SERIALIZE(password)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      std::string address;
+      std::string multisig_info;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(address)
+        KV_SERIALIZE(multisig_info)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_EXPORT_MULTISIG
+  {
+    struct request_t
+    {
+      BEGIN_KV_SERIALIZE_MAP()
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      std::string info;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(info)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_IMPORT_MULTISIG
+  {
+    struct request_t
+    {
+      std::vector<std::string> info;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(info)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      uint64_t n_outputs;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(n_outputs)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_FINALIZE_MULTISIG
+  {
+    // NOP
+    struct request_t
+    {
+      BEGIN_KV_SERIALIZE_MAP()
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      BEGIN_KV_SERIALIZE_MAP()
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_EXCHANGE_MULTISIG_KEYS
+  {
+    struct request_t
+    {
+      std::string password;
+      std::vector<std::string> multisig_info;
+      bool force_update_use_with_caution;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(password)
+        KV_SERIALIZE(multisig_info)
+        KV_SERIALIZE_OPT(force_update_use_with_caution, false)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      std::string address;
+      std::string multisig_info;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(address)
+        KV_SERIALIZE(multisig_info)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_GET_MULTISIG_KEY_EXCHANGE_BOOSTER
+  {
+    struct request_t
+    {
+      std::string password;
+      std::vector<std::string> multisig_info;
+      uint32_t threshold;
+      uint32_t num_signers;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(password)
+        KV_SERIALIZE(multisig_info)
+        KV_SERIALIZE(threshold)
+        KV_SERIALIZE(num_signers)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      std::string multisig_info;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(multisig_info)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_SIGN_MULTISIG
+  {
+    struct request_t
+    {
+      std::string tx_data_hex;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(tx_data_hex)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      std::string tx_data_hex;
+      std::list<std::string> tx_hash_list;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(tx_data_hex)
+        KV_SERIALIZE(tx_hash_list)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_SUBMIT_MULTISIG
+  {
+    struct request_t
+    {
+      std::string tx_data_hex;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(tx_data_hex)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      std::list<std::string> tx_hash_list;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(tx_hash_list)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_GET_VERSION
+  {
+    struct request_t
+    {
+      BEGIN_KV_SERIALIZE_MAP()
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      uint32_t version;
+      bool release;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(version)
+        KV_SERIALIZE(release)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_VALIDATE_ADDRESS
+  {
+    struct request_t
+    {
+      std::string address;
+      bool any_net_type;
+      bool allow_openalias;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(address)
+        KV_SERIALIZE_OPT(any_net_type, false)
+        KV_SERIALIZE_OPT(allow_openalias, false)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      bool valid;
+      bool integrated;
+      bool subaddress;
+      std::string nettype;
+      std::string openalias_address;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(valid)
+        KV_SERIALIZE(integrated)
+        KV_SERIALIZE(subaddress)
+        KV_SERIALIZE(nettype)
+        KV_SERIALIZE(openalias_address)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_SET_DAEMON
+  {
+    struct request_t
+    {
+      std::string address;
+      std::string username;
+      std::string password;
+      bool trusted;
+      std::string ssl_support; // disabled, enabled, autodetect
+      std::string ssl_private_key_path;
+      std::string ssl_certificate_path;
+      std::string ssl_ca_file;
+      std::vector<std::string> ssl_allowed_fingerprints;
+      bool ssl_allow_any_cert;
+      std::string proxy;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(address)
+        KV_SERIALIZE(username)
+        KV_SERIALIZE(password)
+        KV_SERIALIZE_OPT(trusted, false)
+        KV_SERIALIZE_OPT(ssl_support, (std::string)"autodetect")
+        KV_SERIALIZE(ssl_private_key_path)
+        KV_SERIALIZE(ssl_certificate_path)
+        KV_SERIALIZE(ssl_ca_file)
+        KV_SERIALIZE(ssl_allowed_fingerprints)
+        KV_SERIALIZE_OPT(ssl_allow_any_cert, false)
+        KV_SERIALIZE_OPT(proxy, (std::string)"")
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      BEGIN_KV_SERIALIZE_MAP()
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_SET_LOG_LEVEL
+  {
+    struct request_t
+    {
+      int8_t level;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(level)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      BEGIN_KV_SERIALIZE_MAP()
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_SET_LOG_CATEGORIES
+  {
+    struct request_t
+    {
+      std::string categories;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(categories)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      std::string categories;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(categories)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT
+  {
+    struct request_t
+    {
+      uint32_t n_inputs;
+      uint32_t n_outputs;
+      uint32_t ring_size;
+      bool rct;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(n_inputs)
+        KV_SERIALIZE(n_outputs)
+        KV_SERIALIZE_OPT(ring_size, 0u)
+        KV_SERIALIZE_OPT(rct, true)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      uint64_t size;
+      uint64_t weight;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(size)
+        KV_SERIALIZE(weight)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_SETUP_BACKGROUND_SYNC
+  {
+    struct request_t
+    {
+      std::string background_sync_type;
+      std::string wallet_password;
+      std::string background_cache_password;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(background_sync_type)
+        KV_SERIALIZE(wallet_password)
+        KV_SERIALIZE_OPT(background_cache_password, (std::string)"")
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      BEGIN_KV_SERIALIZE_MAP()
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_START_BACKGROUND_SYNC
+  {
+    struct request_t
+    {
+      BEGIN_KV_SERIALIZE_MAP()
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      BEGIN_KV_SERIALIZE_MAP()
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_STOP_BACKGROUND_SYNC
+  {
+    struct request_t
+    {
+      std::string wallet_password;
+      std::string seed;
+      std::string seed_offset;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(wallet_password)
+        KV_SERIALIZE_OPT(seed, (std::string)"")
+        KV_SERIALIZE_OPT(seed_offset, (std::string)"")
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      BEGIN_KV_SERIALIZE_MAP()
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+}
 }
diff --git a/src/wallet/wallet_rpc_server_error_codes.h b/src/wallet/wallet_rpc_server_error_codes.h
index ca2de0cc6..97f9e81a3 100644
--- a/src/wallet/wallet_rpc_server_error_codes.h
+++ b/src/wallet/wallet_rpc_server_error_codes.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
@@ -81,3 +81,5 @@
 #define WALLET_RPC_ERROR_CODE_DISABLED               -48
 #define WALLET_RPC_ERROR_CODE_PROXY_ALREADY_DEFINED  -49
 #define WALLET_RPC_ERROR_CODE_NONZERO_UNLOCK_TIME    -50
+#define WALLET_RPC_ERROR_CODE_IS_BACKGROUND_WALLET   -51
+#define WALLET_RPC_ERROR_CODE_IS_BACKGROUND_SYNCING  -52
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 700039c90..dbd226476 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 # 
 # All rights reserved.
 # 
diff --git a/tests/README.md b/tests/README.md
index 216f2b63e..2c1589cd4 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -104,7 +104,7 @@ ctest
 
 To run the same tests on a release build, replace `debug` with `release`.
 
-To run specific hash test, you can use `ctest` `-R` parameter. For exmaple to run only `blake2b` hash tests:
+To run specific hash test, you can use `ctest` `-R` parameter. For example to run only `blake2b` hash tests:
 
 ```
 ctest -R hash-blake2b
diff --git a/tests/benchmark.cpp b/tests/benchmark.cpp
index 6843ad0d7..289368a62 100644
--- a/tests/benchmark.cpp
+++ b/tests/benchmark.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020-2023, The Monero Project
+// Copyright (c) 2020-2024, The Monero Project
 
 //
 // All rights reserved.
diff --git a/tests/block_weight/CMakeLists.txt b/tests/block_weight/CMakeLists.txt
index 6d88fb754..f622d5a3d 100644
--- a/tests/block_weight/CMakeLists.txt
+++ b/tests/block_weight/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/tests/block_weight/block_weight.cpp b/tests/block_weight/block_weight.cpp
index 676d56006..44ccf1e64 100644
--- a/tests/block_weight/block_weight.cpp
+++ b/tests/block_weight/block_weight.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2023, The Monero Project
+// Copyright (c) 2019-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/block_weight/block_weight.py b/tests/block_weight/block_weight.py
index cfd0900fb..a17e05241 100755
--- a/tests/block_weight/block_weight.py
+++ b/tests/block_weight/block_weight.py
@@ -2,7 +2,7 @@
 # Simulate a maximal block attack on the Monero network
 # This uses the scheme proposed by ArticMine
 # Written by Sarang Nother
-# Copyright (c) 2019-2023, The Monero Project
+# Copyright (c) 2019-2024, The Monero Project
 from __future__ import print_function
 import sys
 import math
diff --git a/tests/core_tests/CMakeLists.txt b/tests/core_tests/CMakeLists.txt
index debe0fa4f..4e94992ab 100644
--- a/tests/core_tests/CMakeLists.txt
+++ b/tests/core_tests/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/tests/core_tests/block_reward.cpp b/tests/core_tests/block_reward.cpp
index 7f75aea5b..78f732bef 100644
--- a/tests/core_tests/block_reward.cpp
+++ b/tests/core_tests/block_reward.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/core_tests/block_reward.h b/tests/core_tests/block_reward.h
index fa8751bf2..08e957e94 100644
--- a/tests/core_tests/block_reward.h
+++ b/tests/core_tests/block_reward.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/core_tests/block_validation.cpp b/tests/core_tests/block_validation.cpp
index fd5ab5a7f..27dd4846b 100644
--- a/tests/core_tests/block_validation.cpp
+++ b/tests/core_tests/block_validation.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/core_tests/block_validation.h b/tests/core_tests/block_validation.h
index a7a901279..71e554b99 100644
--- a/tests/core_tests/block_validation.h
+++ b/tests/core_tests/block_validation.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/core_tests/bulletproof_plus.cpp b/tests/core_tests/bulletproof_plus.cpp
index 742670063..d6376112b 100644
--- a/tests/core_tests/bulletproof_plus.cpp
+++ b/tests/core_tests/bulletproof_plus.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/core_tests/bulletproof_plus.h b/tests/core_tests/bulletproof_plus.h
index f23732554..2dfa0014b 100644
--- a/tests/core_tests/bulletproof_plus.h
+++ b/tests/core_tests/bulletproof_plus.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/core_tests/bulletproofs.cpp b/tests/core_tests/bulletproofs.cpp
index 278bf46a2..481e189cb 100644
--- a/tests/core_tests/bulletproofs.cpp
+++ b/tests/core_tests/bulletproofs.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/core_tests/bulletproofs.h b/tests/core_tests/bulletproofs.h
index 4fc8b60de..7f537fc83 100644
--- a/tests/core_tests/bulletproofs.h
+++ b/tests/core_tests/bulletproofs.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/core_tests/chain_split_1.cpp b/tests/core_tests/chain_split_1.cpp
index cffabb9c1..dadf87fdd 100644
--- a/tests/core_tests/chain_split_1.cpp
+++ b/tests/core_tests/chain_split_1.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/core_tests/chain_split_1.h b/tests/core_tests/chain_split_1.h
index dbf1178a8..a246c915c 100644
--- a/tests/core_tests/chain_split_1.h
+++ b/tests/core_tests/chain_split_1.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/core_tests/chain_switch_1.cpp b/tests/core_tests/chain_switch_1.cpp
index 2d9cdcbd8..16e85a289 100644
--- a/tests/core_tests/chain_switch_1.cpp
+++ b/tests/core_tests/chain_switch_1.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/core_tests/chain_switch_1.h b/tests/core_tests/chain_switch_1.h
index ebae72b7f..c813037e3 100644
--- a/tests/core_tests/chain_switch_1.h
+++ b/tests/core_tests/chain_switch_1.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/core_tests/chaingen.cpp b/tests/core_tests/chaingen.cpp
index 8b2c2a577..05a6ce1f9 100644
--- a/tests/core_tests/chaingen.cpp
+++ b/tests/core_tests/chaingen.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h
index 0a1eb232d..4cd969e9a 100644
--- a/tests/core_tests/chaingen.h
+++ b/tests/core_tests/chaingen.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/core_tests/chaingen001.cpp b/tests/core_tests/chaingen001.cpp
index 1dde16ff5..7affcc15b 100644
--- a/tests/core_tests/chaingen001.cpp
+++ b/tests/core_tests/chaingen001.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp
index dfcdfd8bd..9601a0ff5 100644
--- a/tests/core_tests/chaingen_main.cpp
+++ b/tests/core_tests/chaingen_main.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/core_tests/chaingen_serialization.h b/tests/core_tests/chaingen_serialization.h
index 5a43e970e..8fc3b4100 100644
--- a/tests/core_tests/chaingen_serialization.h
+++ b/tests/core_tests/chaingen_serialization.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/core_tests/chaingen_tests_list.h b/tests/core_tests/chaingen_tests_list.h
index db05f684f..595c80552 100644
--- a/tests/core_tests/chaingen_tests_list.h
+++ b/tests/core_tests/chaingen_tests_list.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/core_tests/double_spend.cpp b/tests/core_tests/double_spend.cpp
index 4bc854ae6..90c864f44 100644
--- a/tests/core_tests/double_spend.cpp
+++ b/tests/core_tests/double_spend.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/core_tests/double_spend.h b/tests/core_tests/double_spend.h
index 85c855bcf..798b926a3 100644
--- a/tests/core_tests/double_spend.h
+++ b/tests/core_tests/double_spend.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/core_tests/double_spend.inl b/tests/core_tests/double_spend.inl
index 5ccf4de98..8519ac7e4 100644
--- a/tests/core_tests/double_spend.inl
+++ b/tests/core_tests/double_spend.inl
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/core_tests/integer_overflow.cpp b/tests/core_tests/integer_overflow.cpp
index 2f48ec04a..12b7edaa4 100644
--- a/tests/core_tests/integer_overflow.cpp
+++ b/tests/core_tests/integer_overflow.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/core_tests/integer_overflow.h b/tests/core_tests/integer_overflow.h
index 16af6ef06..0c75d7e8c 100644
--- a/tests/core_tests/integer_overflow.h
+++ b/tests/core_tests/integer_overflow.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/core_tests/multisig.cpp b/tests/core_tests/multisig.cpp
index e33803325..8c479ed50 100644
--- a/tests/core_tests/multisig.cpp
+++ b/tests/core_tests/multisig.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/core_tests/multisig.h b/tests/core_tests/multisig.h
index f46558655..42cb963da 100644
--- a/tests/core_tests/multisig.h
+++ b/tests/core_tests/multisig.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/core_tests/rct.cpp b/tests/core_tests/rct.cpp
index 9ea7a8e3b..0252c4885 100644
--- a/tests/core_tests/rct.cpp
+++ b/tests/core_tests/rct.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/core_tests/rct.h b/tests/core_tests/rct.h
index eea2740ca..56485f5c3 100644
--- a/tests/core_tests/rct.h
+++ b/tests/core_tests/rct.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/core_tests/rct2.cpp b/tests/core_tests/rct2.cpp
index 28e97b06d..c4197fff4 100644
--- a/tests/core_tests/rct2.cpp
+++ b/tests/core_tests/rct2.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/core_tests/rct2.h b/tests/core_tests/rct2.h
index 7fc7ad49b..fcf559594 100644
--- a/tests/core_tests/rct2.h
+++ b/tests/core_tests/rct2.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/core_tests/ring_signature_1.cpp b/tests/core_tests/ring_signature_1.cpp
index 218747f21..b65fb1cd3 100644
--- a/tests/core_tests/ring_signature_1.cpp
+++ b/tests/core_tests/ring_signature_1.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/core_tests/ring_signature_1.h b/tests/core_tests/ring_signature_1.h
index 03120b95f..f86269aa2 100644
--- a/tests/core_tests/ring_signature_1.h
+++ b/tests/core_tests/ring_signature_1.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/core_tests/transaction_tests.cpp b/tests/core_tests/transaction_tests.cpp
index c27e6b375..02e1bd244 100644
--- a/tests/core_tests/transaction_tests.cpp
+++ b/tests/core_tests/transaction_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/core_tests/transaction_tests.h b/tests/core_tests/transaction_tests.h
index 1450e3b6f..c05be5087 100644
--- a/tests/core_tests/transaction_tests.h
+++ b/tests/core_tests/transaction_tests.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/core_tests/tx_pool.cpp b/tests/core_tests/tx_pool.cpp
index 1b8480526..1bee8efc1 100644
--- a/tests/core_tests/tx_pool.cpp
+++ b/tests/core_tests/tx_pool.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2023, The Monero Project
+// Copyright (c) 2019-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/tests/core_tests/tx_pool.h b/tests/core_tests/tx_pool.h
index ebf728422..315c1d679 100644
--- a/tests/core_tests/tx_pool.h
+++ b/tests/core_tests/tx_pool.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2023, The Monero Project
+// Copyright (c) 2019-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/tests/core_tests/tx_validation.cpp b/tests/core_tests/tx_validation.cpp
index a46b2a06a..48f2064b0 100644
--- a/tests/core_tests/tx_validation.cpp
+++ b/tests/core_tests/tx_validation.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/core_tests/tx_validation.h b/tests/core_tests/tx_validation.h
index 5c06bcd16..2e6e02c9e 100644
--- a/tests/core_tests/tx_validation.h
+++ b/tests/core_tests/tx_validation.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/core_tests/v2_tests.cpp b/tests/core_tests/v2_tests.cpp
index 9030f22ba..f868c6df8 100644
--- a/tests/core_tests/v2_tests.cpp
+++ b/tests/core_tests/v2_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/core_tests/v2_tests.h b/tests/core_tests/v2_tests.h
index a71b5b300..ef6eb0f31 100644
--- a/tests/core_tests/v2_tests.h
+++ b/tests/core_tests/v2_tests.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/core_tests/wallet_tools.h b/tests/core_tests/wallet_tools.h
index 94921e56e..fcaa6698c 100644
--- a/tests/core_tests/wallet_tools.h
+++ b/tests/core_tests/wallet_tools.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/tests/crypto/CMakeLists.txt b/tests/crypto/CMakeLists.txt
index df0f871f2..42d1c8da8 100644
--- a/tests/crypto/CMakeLists.txt
+++ b/tests/crypto/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/tests/crypto/cnv4-jit.c b/tests/crypto/cnv4-jit.c
index f1cb35a79..0c6e14005 100644
--- a/tests/crypto/cnv4-jit.c
+++ b/tests/crypto/cnv4-jit.c
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2023, The Monero Project
+// Copyright (c) 2019-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/crypto/crypto-ops-data.c b/tests/crypto/crypto-ops-data.c
index c1ebbb1f0..aa51da138 100644
--- a/tests/crypto/crypto-ops-data.c
+++ b/tests/crypto/crypto-ops-data.c
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/crypto/crypto-ops.c b/tests/crypto/crypto-ops.c
index fcb38200d..ea355ec65 100644
--- a/tests/crypto/crypto-ops.c
+++ b/tests/crypto/crypto-ops.c
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/crypto/crypto-tests.h b/tests/crypto/crypto-tests.h
index f0b2a8eaa..ed57317d7 100644
--- a/tests/crypto/crypto-tests.h
+++ b/tests/crypto/crypto-tests.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/crypto/crypto.cpp b/tests/crypto/crypto.cpp
index 68f8259e2..aeca2d756 100644
--- a/tests/crypto/crypto.cpp
+++ b/tests/crypto/crypto.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/crypto/hash.c b/tests/crypto/hash.c
index fc69c3a71..ff4ef1908 100644
--- a/tests/crypto/hash.c
+++ b/tests/crypto/hash.c
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/crypto/main.cpp b/tests/crypto/main.cpp
index 6f8aba597..668c04ea1 100644
--- a/tests/crypto/main.cpp
+++ b/tests/crypto/main.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/crypto/random.c b/tests/crypto/random.c
index d575b95a0..63b9e3ba3 100644
--- a/tests/crypto/random.c
+++ b/tests/crypto/random.c
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/difficulty/CMakeLists.txt b/tests/difficulty/CMakeLists.txt
index 109ed1d43..e03945092 100644
--- a/tests/difficulty/CMakeLists.txt
+++ b/tests/difficulty/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/tests/difficulty/difficulty.cpp b/tests/difficulty/difficulty.cpp
index 9cecadca8..e82e51a24 100644
--- a/tests/difficulty/difficulty.cpp
+++ b/tests/difficulty/difficulty.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/difficulty/generate-data b/tests/difficulty/generate-data
index cfec61a88..24926796a 100755
--- a/tests/difficulty/generate-data
+++ b/tests/difficulty/generate-data
@@ -1,6 +1,6 @@
 #!/usr/bin/python3
 
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 # 
 # All rights reserved.
 # 
diff --git a/tests/functional_tests/CMakeLists.txt b/tests/functional_tests/CMakeLists.txt
index 306eba073..62185a0db 100644
--- a/tests/functional_tests/CMakeLists.txt
+++ b/tests/functional_tests/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/tests/functional_tests/address_book.py b/tests/functional_tests/address_book.py
index d614fb24c..84365fe8c 100755
--- a/tests/functional_tests/address_book.py
+++ b/tests/functional_tests/address_book.py
@@ -1,7 +1,7 @@
 #!/usr/bin/env python3
 #encoding=utf-8
 
-# Copyright (c) 2019-2023, The Monero Project
+# Copyright (c) 2019-2024, The Monero Project
 # 
 # All rights reserved.
 # 
diff --git a/tests/functional_tests/bans.py b/tests/functional_tests/bans.py
index 71299b681..fa0a19664 100755
--- a/tests/functional_tests/bans.py
+++ b/tests/functional_tests/bans.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python3
 
-# Copyright (c) 2019-2023, The Monero Project
+# Copyright (c) 2019-2024, The Monero Project
 # 
 # All rights reserved.
 # 
diff --git a/tests/functional_tests/blockchain.py b/tests/functional_tests/blockchain.py
index 65378da4b..db0bd2eef 100755
--- a/tests/functional_tests/blockchain.py
+++ b/tests/functional_tests/blockchain.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python3
 
-# Copyright (c) 2018-2023, The Monero Project
+# Copyright (c) 2018-2024, The Monero Project
 
 # 
 # All rights reserved.
@@ -137,6 +137,7 @@ class BlockchainTest():
         assert res_getblocktemplate.reserved_offset > 0
         assert res_getblocktemplate.prev_hash == res_info.top_block_hash
         assert res_getblocktemplate.expected_reward >= 600000000000
+        assert res_getblocktemplate.cumulative_weight > 0
         assert len(res_getblocktemplate.blocktemplate_blob) > 0
         assert len(res_getblocktemplate.blockhashing_blob) > 0
         assert int(res_getblocktemplate.wide_difficulty, 16) == (res_getblocktemplate.difficulty_top64 << 64) + res_getblocktemplate.difficulty
diff --git a/tests/functional_tests/cold_signing.py b/tests/functional_tests/cold_signing.py
index be5710f1d..38e1baa26 100755
--- a/tests/functional_tests/cold_signing.py
+++ b/tests/functional_tests/cold_signing.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python3
 
-# Copyright (c) 2019-2023, The Monero Project
+# Copyright (c) 2019-2024, The Monero Project
 # 
 # All rights reserved.
 # 
diff --git a/tests/functional_tests/cpu_power_test.cpp b/tests/functional_tests/cpu_power_test.cpp
index 0ae48c527..623be90c1 100644
--- a/tests/functional_tests/cpu_power_test.cpp
+++ b/tests/functional_tests/cpu_power_test.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/functional_tests/daemon_info.py b/tests/functional_tests/daemon_info.py
index 901375bc8..9d645330d 100755
--- a/tests/functional_tests/daemon_info.py
+++ b/tests/functional_tests/daemon_info.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python3
 
-# Copyright (c) 2018-2023, The Monero Project
+# Copyright (c) 2018-2024, The Monero Project
 
 # 
 # All rights reserved.
diff --git a/tests/functional_tests/get_output_distribution.py b/tests/functional_tests/get_output_distribution.py
index 08019121a..54cb63035 100755
--- a/tests/functional_tests/get_output_distribution.py
+++ b/tests/functional_tests/get_output_distribution.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python3
 
-# Copyright (c) 2019-2023, The Monero Project
+# Copyright (c) 2019-2024, The Monero Project
 # 
 # All rights reserved.
 # 
diff --git a/tests/functional_tests/http_digest_auth.py b/tests/functional_tests/http_digest_auth.py
index 7c22f9f30..1311ae62d 100644
--- a/tests/functional_tests/http_digest_auth.py
+++ b/tests/functional_tests/http_digest_auth.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python3
 
-# Copyright (c) 2024, The Monero Project
+# Copyright (c) 2024-2024, The Monero Project
 # 
 # All rights reserved.
 # 
diff --git a/tests/functional_tests/integrated_address.py b/tests/functional_tests/integrated_address.py
index d2e81622f..912bee26a 100755
--- a/tests/functional_tests/integrated_address.py
+++ b/tests/functional_tests/integrated_address.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python3
 
-# Copyright (c) 2019-2023, The Monero Project
+# Copyright (c) 2019-2024, The Monero Project
 # 
 # All rights reserved.
 # 
diff --git a/tests/functional_tests/k_anonymity.py b/tests/functional_tests/k_anonymity.py
index ffa670b4c..1182d29c8 100755
--- a/tests/functional_tests/k_anonymity.py
+++ b/tests/functional_tests/k_anonymity.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python3
 
-# Copyright (c) 2023, The Monero Project
+# Copyright (c) 2023-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/tests/functional_tests/main.cpp b/tests/functional_tests/main.cpp
index f19fa2c44..5ec40203c 100644
--- a/tests/functional_tests/main.cpp
+++ b/tests/functional_tests/main.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/functional_tests/make_test_signature.cc b/tests/functional_tests/make_test_signature.cc
index cba989070..d92986da2 100644
--- a/tests/functional_tests/make_test_signature.cc
+++ b/tests/functional_tests/make_test_signature.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2023, The Monero Project
+// Copyright (c) 2019-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/functional_tests/mining.py b/tests/functional_tests/mining.py
index 242c58dbe..aa7e291b5 100755
--- a/tests/functional_tests/mining.py
+++ b/tests/functional_tests/mining.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python3
 
-# Copyright (c) 2018-2023, The Monero Project
+# Copyright (c) 2018-2024, The Monero Project
 
 # 
 # All rights reserved.
diff --git a/tests/functional_tests/multisig.py b/tests/functional_tests/multisig.py
index 73cc8d643..c29cd1322 100755
--- a/tests/functional_tests/multisig.py
+++ b/tests/functional_tests/multisig.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python3
 
-# Copyright (c) 2019-2023, The Monero Project
+# Copyright (c) 2019-2024, The Monero Project
 # 
 # All rights reserved.
 # 
diff --git a/tests/functional_tests/p2p.py b/tests/functional_tests/p2p.py
index 2c582cc8a..8d92318ce 100755
--- a/tests/functional_tests/p2p.py
+++ b/tests/functional_tests/p2p.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python3
 
-# Copyright (c) 2018-2023, The Monero Project
+# Copyright (c) 2018-2024, The Monero Project
 
 # 
 # All rights reserved.
diff --git a/tests/functional_tests/proofs.py b/tests/functional_tests/proofs.py
index 23cb858be..83869a063 100755
--- a/tests/functional_tests/proofs.py
+++ b/tests/functional_tests/proofs.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python3
 
-# Copyright (c) 2019-2023, The Monero Project
+# Copyright (c) 2019-2024, The Monero Project
 # 
 # All rights reserved.
 # 
diff --git a/tests/functional_tests/rpc_payment.py b/tests/functional_tests/rpc_payment.py
index bfd20bc1a..5b65b5af4 100755
--- a/tests/functional_tests/rpc_payment.py
+++ b/tests/functional_tests/rpc_payment.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python3
 
-# Copyright (c) 2019-2023, The Monero Project
+# Copyright (c) 2019-2024, The Monero Project
 # 
 # All rights reserved.
 # 
diff --git a/tests/functional_tests/sign_message.py b/tests/functional_tests/sign_message.py
index 1bfb6f666..9d6692cf8 100755
--- a/tests/functional_tests/sign_message.py
+++ b/tests/functional_tests/sign_message.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python3
 
-# Copyright (c) 2019-2023, The Monero Project
+# Copyright (c) 2019-2024, The Monero Project
 # 
 # All rights reserved.
 # 
diff --git a/tests/functional_tests/speed.py b/tests/functional_tests/speed.py
index cc971d83d..9f3514a0d 100755
--- a/tests/functional_tests/speed.py
+++ b/tests/functional_tests/speed.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python3
 
-# Copyright (c) 2018-2023, The Monero Project
+# Copyright (c) 2018-2024, The Monero Project
 
 # 
 # All rights reserved.
diff --git a/tests/functional_tests/transactions_flow_test.cpp b/tests/functional_tests/transactions_flow_test.cpp
index a15348bca..fd1a64673 100644
--- a/tests/functional_tests/transactions_flow_test.cpp
+++ b/tests/functional_tests/transactions_flow_test.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/functional_tests/transactions_flow_test.h b/tests/functional_tests/transactions_flow_test.h
index 568ce4d86..06a401e9b 100644
--- a/tests/functional_tests/transactions_flow_test.h
+++ b/tests/functional_tests/transactions_flow_test.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/functional_tests/transactions_generation_from_blockchain.cpp b/tests/functional_tests/transactions_generation_from_blockchain.cpp
index 9e33330a6..3abb13909 100644
--- a/tests/functional_tests/transactions_generation_from_blockchain.cpp
+++ b/tests/functional_tests/transactions_generation_from_blockchain.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/functional_tests/transactions_generation_from_blockchain.h b/tests/functional_tests/transactions_generation_from_blockchain.h
index 4758bd066..12e78579d 100644
--- a/tests/functional_tests/transactions_generation_from_blockchain.h
+++ b/tests/functional_tests/transactions_generation_from_blockchain.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/functional_tests/transfer.py b/tests/functional_tests/transfer.py
index 56a2514d9..2987ded89 100755
--- a/tests/functional_tests/transfer.py
+++ b/tests/functional_tests/transfer.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python3
 
-# Copyright (c) 2019-2023, The Monero Project
+# Copyright (c) 2019-2024, The Monero Project
 # 
 # All rights reserved.
 # 
@@ -30,6 +30,7 @@
 
 from __future__ import print_function
 import json
+import util_resources
 import pprint
 from deepdiff import DeepDiff
 pp = pprint.PrettyPrinter(indent=2)
@@ -46,6 +47,17 @@ seeds = [
     'dilute gutter certain antics pamphlet macro enjoy left slid guarded bogeys upload nineteen bomb jubilee enhanced irritate turnip eggs swung jukebox loudly reduce sedan slid',
 ]
 
+def diff_transfers(actual_transfers, expected_transfers, ignore_order = True):
+    # The payments containers aren't ordered; re-scanning can lead to diff orders
+    diff = DeepDiff(actual_transfers, expected_transfers, ignore_order = ignore_order)
+    if diff != {}:
+        pp.pprint(diff)
+    assert diff == {}
+
+def diff_incoming_transfers(actual_transfers, expected_transfers):
+    # wallet2 m_transfers container is ordered and order should be the same across rescans
+    diff_transfers(actual_transfers, expected_transfers, ignore_order = False)
+
 class TransferTest():
     def run_test(self):
         self.reset()
@@ -64,6 +76,8 @@ class TransferTest():
         self.check_multiple_submissions()
         self.check_scan_tx()
         self.check_subtract_fee_from_outputs()
+        self.check_background_sync()
+        self.check_background_sync_reorg_recovery()
 
     def reset(self):
         print('Resetting blockchain')
@@ -875,12 +889,6 @@ class TransferTest():
 
         print('Testing scan_tx')
 
-        def diff_transfers(actual_transfers, expected_transfers):
-            diff = DeepDiff(actual_transfers, expected_transfers)
-            if diff != {}:
-                pp.pprint(diff)
-            assert diff == {}
-
         # set up sender_wallet
         sender_wallet = self.wallet[0]
         try: sender_wallet.close_wallet()
@@ -1162,5 +1170,385 @@ class TransferTest():
         except AssertionError:
             pass
 
+    def check_background_sync(self):
+        daemon = Daemon()
+
+        print('Testing background sync')
+
+        # Some helper functions
+        def stop_with_wrong_inputs(wallet, wallet_password, seed = ''):
+            invalid = False
+            try: wallet.stop_background_sync(wallet_password = wallet_password, seed = seed)
+            except: invalid = True
+            assert invalid
+
+        def open_with_wrong_password(wallet, filename, password):
+            invalid_password = False
+            try: wallet.open_wallet(filename, password = password)
+            except: invalid_password = True
+            assert invalid_password
+
+        def restore_wallet(wallet, seed, filename = '', password = ''):
+            wallet.close_wallet()
+            if filename != '':
+                util_resources.remove_wallet_files(filename)
+            wallet.restore_deterministic_wallet(seed = seed, filename = filename, password = password)
+            wallet.auto_refresh(enable = False)
+            assert wallet.get_transfers() == {}
+
+        def assert_correct_transfers(wallet, expected_transfers, expected_inc_transfers, expected_balance):
+            diff_transfers(wallet.get_transfers(), expected_transfers)
+            diff_incoming_transfers(wallet.incoming_transfers(transfer_type = 'all'), expected_inc_transfers)
+            assert wallet.get_balance().balance == expected_balance
+
+        # Set up sender_wallet. Prepare to sweep single output to receiver.
+        # We're testing a sweep because it makes sure background sync can
+        # properly pick up txs which do not have a change output back to sender.
+        sender_wallet = self.wallet[0]
+        try: sender_wallet.close_wallet()
+        except: pass
+        sender_wallet.restore_deterministic_wallet(seed = seeds[0])
+        sender_wallet.auto_refresh(enable = False)
+        sender_wallet.refresh()
+        res = sender_wallet.incoming_transfers(transfer_type = 'available')
+        unlocked = [x for x in res.transfers if x.unlocked and x.amount > 0]
+        assert len(unlocked) > 0
+        ki = unlocked[0].key_image
+        amount = unlocked[0].amount
+        spent_txid = unlocked[0].tx_hash
+        sender_wallet.refresh()
+        res = sender_wallet.get_transfers()
+        out_len = 0 if 'out' not in res else len(res.out)
+        sender_starting_balance = sender_wallet.get_balance().balance
+
+        # Background sync type options
+        reuse_password = sender_wallet.background_sync_options.reuse_password
+        custom_password = sender_wallet.background_sync_options.custom_password
+
+        # set up receiver_wallet
+        receiver_wallet = self.wallet[1]
+        try: receiver_wallet.close_wallet()
+        except: pass
+        receiver_wallet.restore_deterministic_wallet(seed = seeds[1])
+        receiver_wallet.auto_refresh(enable = False)
+        receiver_wallet.refresh()
+        res = receiver_wallet.get_transfers()
+        in_len = 0 if 'in' not in res else len(res['in'])
+        receiver_starting_balance = receiver_wallet.get_balance().balance
+
+        # transfer from sender_wallet to receiver_wallet
+        dst = '44Kbx4sJ7JDRDV5aAhLJzQCjDz2ViLRduE3ijDZu3osWKBjMGkV1XPk4pfDUMqt1Aiezvephdqm6YD19GKFD9ZcXVUTp6BW'
+        res = sender_wallet.sweep_single(dst, key_image = ki)
+        assert len(res.tx_hash) == 32*2
+        txid = res.tx_hash
+        assert res.fee > 0
+        fee = res.fee
+        assert res.amount == amount - fee
+
+        expected_sender_balance = sender_starting_balance - amount
+        expected_receiver_balance = receiver_starting_balance + (amount - fee)
+
+        print('Checking background sync on outgoing wallet')
+        sender_wallet.setup_background_sync(background_sync_type = reuse_password)
+        sender_wallet.start_background_sync()
+        # Mine block to an uninvolved wallet
+        daemon.generateblocks('46r4nYSevkfBUMhuykdK3gQ98XDqDTYW1hNLaXNvjpsJaSbNtdXh1sKMsdVgqkaihChAzEy29zEDPMR3NHQvGoZCLGwTerK', 1)
+        # sender should still be able to scan the transfer normally because we
+        # spent an output that had a known key image
+        sender_wallet.refresh()
+        transfers = sender_wallet.get_transfers()
+        assert 'pending' not in transfers or len(transfers.pending) == 0
+        assert 'pool' not in transfers or len (transfers.pool) == 0
+        assert len(transfers.out) == out_len + 1
+        tx = [x for x in transfers.out if x.txid == txid]
+        assert len(tx) == 1
+        tx = tx[0]
+        assert tx.amount == amount - fee
+        assert tx.fee == fee
+        assert len(tx.destinations) == 1
+        assert tx.destinations[0].amount == amount - fee
+        assert tx.destinations[0].address == dst
+        incoming_transfers = sender_wallet.incoming_transfers(transfer_type = 'all')
+        assert len([x for x in incoming_transfers.transfers if x.tx_hash == spent_txid and x.key_image == ki and x.spent]) == 1
+        assert sender_wallet.get_balance().balance == expected_sender_balance
+
+        # Restore and check background syncing outgoing wallet
+        restore_wallet(sender_wallet, seeds[0])
+        sender_wallet.setup_background_sync(background_sync_type = reuse_password)
+        sender_wallet.start_background_sync()
+        sender_wallet.refresh()
+        for i, out_tx in enumerate(transfers.out):
+            if 'destinations' in out_tx:
+                del transfers.out[i]['destinations'] # destinations are not expected after wallet restore
+        # sender's balance should be higher because can't detect spends while
+        # background sync enabled, only receives
+        background_bal = sender_wallet.get_balance().balance
+        assert background_bal > expected_sender_balance
+        background_transfers = sender_wallet.get_transfers()
+        assert 'out' not in background_transfers or len(background_transfers.out) == 0
+        assert 'in' in background_transfers and len(background_transfers['in']) > 0
+        background_incoming_transfers = sender_wallet.incoming_transfers(transfer_type = 'all')
+        assert len(background_incoming_transfers) == len(incoming_transfers)
+        assert len([x for x in background_incoming_transfers.transfers if x.spent or x.key_image != '']) == 0
+        assert len([x for x in background_incoming_transfers.transfers if x.tx_hash == spent_txid]) == 1
+
+        # Try to stop background sync with the wrong seed
+        stop_with_wrong_inputs(sender_wallet, wallet_password = '', seed = seeds[1])
+
+        # Stop background sync and check transfers update correctly
+        sender_wallet.stop_background_sync(wallet_password = '', seed = seeds[0])
+        assert_correct_transfers(sender_wallet, transfers, incoming_transfers, expected_sender_balance)
+
+        # Check stopping a wallet with wallet files saved to disk
+        for background_sync_type in [reuse_password, custom_password]:
+            restore_wallet(sender_wallet, seeds[0], 'test1', 'test_password')
+            background_cache_password = None if background_sync_type == reuse_password else 'background_password'
+            sender_wallet.setup_background_sync(background_sync_type = background_sync_type, wallet_password = 'test_password', background_cache_password = background_cache_password)
+            sender_wallet.start_background_sync()
+            sender_wallet.refresh()
+            assert_correct_transfers(sender_wallet, background_transfers, background_incoming_transfers, background_bal)
+            stop_with_wrong_inputs(sender_wallet, 'wrong_password')
+            sender_wallet.stop_background_sync(wallet_password = 'test_password')
+            assert_correct_transfers(sender_wallet, transfers, incoming_transfers, expected_sender_balance)
+
+        # Close wallet while background syncing, then reopen
+        for background_sync_type in [reuse_password, custom_password]:
+            restore_wallet(sender_wallet, seeds[0], 'test1', 'test_password')
+            background_cache_password = None if background_sync_type == reuse_password else 'background_password'
+            sender_wallet.setup_background_sync(background_sync_type = background_sync_type, wallet_password = 'test_password', background_cache_password = background_cache_password)
+            sender_wallet.start_background_sync()
+            sender_wallet.refresh()
+            assert_correct_transfers(sender_wallet, background_transfers, background_incoming_transfers, background_bal)
+            sender_wallet.close_wallet()
+            open_with_wrong_password(sender_wallet, 'test1', 'wrong_password')
+            sender_wallet.open_wallet('test1', password = 'test_password')
+            # It should reopen with spend key loaded and correctly scan all transfers
+            assert_correct_transfers(sender_wallet, transfers, incoming_transfers, expected_sender_balance)
+
+        # Close wallet while syncing normally, then reopen
+        for background_sync_type in [reuse_password, custom_password]:
+            restore_wallet(sender_wallet, seeds[0], 'test1', 'test_password')
+            background_cache_password = None if background_sync_type == reuse_password else 'background_password'
+            sender_wallet.setup_background_sync(background_sync_type = background_sync_type, wallet_password = 'test_password', background_cache_password = background_cache_password)
+            sender_wallet.refresh()
+            assert_correct_transfers(sender_wallet, transfers, incoming_transfers, expected_sender_balance)
+            sender_wallet.close_wallet()
+            open_with_wrong_password(sender_wallet, 'test1', 'wrong_password')
+            sender_wallet.open_wallet('test1', password = 'test_password')
+            assert_correct_transfers(sender_wallet, transfers, incoming_transfers, expected_sender_balance)
+
+        # Create background cache using custom password, then use it to sync, then reopen main wallet
+        for background_cache_password in ['background_password', '']:
+            restore_wallet(sender_wallet, seeds[0], 'test1', 'test_password')
+            assert not util_resources.file_exists('test1.background')
+            assert not util_resources.file_exists('test1.background.keys')
+            sender_wallet.setup_background_sync(background_sync_type = custom_password, wallet_password = 'test_password', background_cache_password = background_cache_password)
+            assert util_resources.file_exists('test1.background')
+            assert util_resources.file_exists('test1.background.keys')
+            sender_wallet.close_wallet()
+            open_with_wrong_password(sender_wallet, 'test1.background', 'test_password')
+            sender_wallet.open_wallet('test1.background', password = background_cache_password)
+            sender_wallet.refresh()
+            assert_correct_transfers(sender_wallet, background_transfers, background_incoming_transfers, background_bal)
+            sender_wallet.close_wallet()
+            sender_wallet.open_wallet('test1', password = 'test_password')
+            assert_correct_transfers(sender_wallet, transfers, incoming_transfers, expected_sender_balance)
+
+        # Check that main wallet keeps background cache encrypted with custom password in sync
+        restore_wallet(sender_wallet, seeds[0], 'test1', 'test_password')
+        sender_wallet.setup_background_sync(background_sync_type = background_sync_type, wallet_password = 'test_password', background_cache_password = 'background_password')
+        sender_wallet.refresh()
+        assert_correct_transfers(sender_wallet, transfers, incoming_transfers, expected_sender_balance)
+        sender_wallet.close_wallet()
+        sender_wallet.open_wallet('test1.background', password = 'background_password')
+        assert_correct_transfers(sender_wallet, transfers, incoming_transfers, expected_sender_balance)
+
+        # Try using wallet password as custom background password
+        restore_wallet(sender_wallet, seeds[0], 'test1', 'test_password')
+        assert not util_resources.file_exists('test1.background')
+        assert not util_resources.file_exists('test1.background.keys')
+        same_password = False
+        try: sender_wallet.setup_background_sync(background_sync_type = custom_password, wallet_password = 'test_password', background_cache_password = 'test_password')
+        except: same_password = True
+        assert same_password
+        assert not util_resources.file_exists('test1.background')
+        assert not util_resources.file_exists('test1.background.keys')
+
+        # Turn off background sync
+        for background_sync_type in [reuse_password, custom_password]:
+            restore_wallet(sender_wallet, seeds[0], 'test1', 'test_password')
+            background_cache_password = None if background_sync_type == reuse_password else 'background_password'
+            sender_wallet.setup_background_sync(background_sync_type = background_sync_type, wallet_password = 'test_password', background_cache_password = background_cache_password)
+            if background_sync_type == custom_password:
+                assert util_resources.file_exists('test1.background')
+                assert util_resources.file_exists('test1.background.keys')
+                sender_wallet.close_wallet()
+                assert util_resources.file_exists('test1.background')
+                assert util_resources.file_exists('test1.background.keys')
+            else:
+                assert not util_resources.file_exists('test1.background')
+                assert not util_resources.file_exists('test1.background.keys')
+                sender_wallet.close_wallet()
+                assert not util_resources.file_exists('test1.background')
+                assert not util_resources.file_exists('test1.background.keys')
+            sender_wallet.open_wallet('test1', password = 'test_password')
+            sender_wallet.setup_background_sync(background_sync_type = sender_wallet.background_sync_options.off, wallet_password = 'test_password')
+            assert not util_resources.file_exists('test1.background')
+            assert not util_resources.file_exists('test1.background.keys')
+            sender_wallet.close_wallet()
+            assert not util_resources.file_exists('test1.background')
+            assert not util_resources.file_exists('test1.background.keys')
+            sender_wallet.open_wallet('test1', password = 'test_password')
+
+        # Sanity check against outgoing wallet restored at height 0
+        sender_wallet.close_wallet()
+        sender_wallet.restore_deterministic_wallet(seed = seeds[0], restore_height = 0)
+        sender_wallet.refresh()
+        assert_correct_transfers(sender_wallet, transfers, incoming_transfers, expected_sender_balance)
+
+        print('Checking background sync on incoming wallet')
+        receiver_wallet.setup_background_sync(background_sync_type = reuse_password)
+        receiver_wallet.start_background_sync()
+        receiver_wallet.refresh()
+        transfers = receiver_wallet.get_transfers()
+        assert 'pending' not in transfers or len(transfers.pending) == 0
+        assert 'pool' not in transfers or len (transfers.pool) == 0
+        assert len(transfers['in']) == in_len + 1
+        tx = [x for x in transfers['in'] if x.txid == txid]
+        assert len(tx) == 1
+        tx = tx[0]
+        assert tx.amount == amount - fee
+        assert tx.fee == fee
+        incoming_transfers = receiver_wallet.incoming_transfers(transfer_type = 'all')
+        assert len([x for x in incoming_transfers.transfers if x.tx_hash == txid and x.key_image == '' and not x.spent]) == 1
+        assert receiver_wallet.get_balance().balance == expected_receiver_balance
+
+        # Restore and check background syncing incoming wallet
+        restore_wallet(receiver_wallet, seeds[1])
+        receiver_wallet.setup_background_sync(background_sync_type = reuse_password)
+        receiver_wallet.start_background_sync()
+        receiver_wallet.refresh()
+        if 'out' in transfers:
+            for i, out_tx in enumerate(transfers.out):
+                if 'destinations' in out_tx:
+                    del transfers.out[i]['destinations'] # destinations are not expected after wallet restore
+        background_bal = receiver_wallet.get_balance().balance
+        assert background_bal >= expected_receiver_balance
+        background_transfers = receiver_wallet.get_transfers()
+        assert 'out' not in background_transfers or len(background_transfers.out) == 0
+        assert 'in' in background_transfers and len(background_transfers['in']) > 0
+        background_incoming_transfers = receiver_wallet.incoming_transfers(transfer_type = 'all')
+        assert len(background_incoming_transfers) == len(incoming_transfers)
+        assert len([x for x in background_incoming_transfers.transfers if x.spent or x.key_image != '']) == 0
+        assert len([x for x in background_incoming_transfers.transfers if x.tx_hash == txid]) == 1
+
+        # Stop background sync and check transfers update correctly
+        receiver_wallet.stop_background_sync(wallet_password = '', seed = seeds[1])
+        diff_transfers(receiver_wallet.get_transfers(), transfers)
+        incoming_transfers = receiver_wallet.incoming_transfers(transfer_type = 'all')
+        assert len(background_incoming_transfers) == len(incoming_transfers)
+        assert len([x for x in incoming_transfers.transfers if x.tx_hash == txid and x.key_image != '' and not x.spent]) == 1
+        assert receiver_wallet.get_balance().balance == expected_receiver_balance
+
+        # Check a fresh incoming wallet with wallet files saved to disk and encrypted with password
+        restore_wallet(receiver_wallet, seeds[1], 'test2', 'test_password')
+        receiver_wallet.setup_background_sync(background_sync_type = reuse_password, wallet_password = 'test_password')
+        receiver_wallet.start_background_sync()
+        receiver_wallet.refresh()
+        assert_correct_transfers(receiver_wallet, background_transfers, background_incoming_transfers, background_bal)
+        stop_with_wrong_inputs(receiver_wallet, 'wrong_password')
+        receiver_wallet.stop_background_sync(wallet_password = 'test_password')
+        assert_correct_transfers(receiver_wallet, transfers, incoming_transfers, expected_receiver_balance)
+
+        # Close receiver's wallet while background sync is enabled then reopen
+        restore_wallet(receiver_wallet, seeds[1], 'test2', 'test_password')
+        receiver_wallet.setup_background_sync(background_sync_type = reuse_password, wallet_password = 'test_password')
+        receiver_wallet.start_background_sync()
+        receiver_wallet.refresh()
+        diff_transfers(receiver_wallet.get_transfers(), background_transfers)
+        diff_incoming_transfers(receiver_wallet.incoming_transfers(transfer_type = 'all'), background_incoming_transfers)
+        assert receiver_wallet.get_balance().balance == background_bal
+        receiver_wallet.close_wallet()
+        receiver_wallet.open_wallet('test2', password = 'test_password')
+        # It should reopen with spend key loaded and correctly scan all transfers
+        assert_correct_transfers(receiver_wallet, transfers, incoming_transfers, expected_receiver_balance)
+
+        # Sanity check against incoming wallet restored at height 0
+        receiver_wallet.close_wallet()
+        receiver_wallet.restore_deterministic_wallet(seed = seeds[1], restore_height = 0)
+        receiver_wallet.refresh()
+        assert_correct_transfers(receiver_wallet, transfers, incoming_transfers, expected_receiver_balance)
+
+        # Clean up
+        util_resources.remove_wallet_files('test1')
+        util_resources.remove_wallet_files('test2')
+        for i in range(2):
+            self.wallet[i].close_wallet()
+            self.wallet[i].restore_deterministic_wallet(seed = seeds[i])
+
+    def check_background_sync_reorg_recovery(self):
+        daemon = Daemon()
+
+        print('Testing background sync reorg recovery')
+
+        # Disconnect daemon from peers
+        daemon.out_peers(0)
+
+        # Background sync type options
+        sender_wallet = self.wallet[0]
+        reuse_password = sender_wallet.background_sync_options.reuse_password
+        custom_password = sender_wallet.background_sync_options.custom_password
+
+        for background_sync_type in [reuse_password, custom_password]:
+            # Set up wallet saved to disk
+            sender_wallet.close_wallet()
+            util_resources.remove_wallet_files('test1')
+            sender_wallet.restore_deterministic_wallet(seed = seeds[0], filename = 'test1', password = '')
+            sender_wallet.auto_refresh(enable = False)
+            sender_wallet.refresh()
+            sender_starting_balance = sender_wallet.get_balance().balance
+
+            # Send tx and mine a block
+            amount = 1000000000000
+            assert sender_starting_balance > amount
+            dst = {'address': '44Kbx4sJ7JDRDV5aAhLJzQCjDz2ViLRduE3ijDZu3osWKBjMGkV1XPk4pfDUMqt1Aiezvephdqm6YD19GKFD9ZcXVUTp6BW', 'amount': amount}
+            res = sender_wallet.transfer([dst])
+            assert len(res.tx_hash) == 32*2
+            txid = res.tx_hash
+
+            daemon.generateblocks('46r4nYSevkfBUMhuykdK3gQ98XDqDTYW1hNLaXNvjpsJaSbNtdXh1sKMsdVgqkaihChAzEy29zEDPMR3NHQvGoZCLGwTerK', 1)
+
+            # Make sure the wallet can see the tx
+            sender_wallet.refresh()
+            transfers = sender_wallet.get_transfers()
+            assert 'pool' not in transfers or len (transfers.pool) == 0
+            tx = [x for x in transfers.out if x.txid == txid]
+            assert len(tx) == 1
+            tx = tx[0]
+            assert sender_wallet.get_balance().balance < (sender_starting_balance - amount)
+
+            # Pop the block while background syncing
+            background_cache_password = None if background_sync_type == reuse_password else 'background_password'
+            sender_wallet.setup_background_sync(background_sync_type = background_sync_type, wallet_password = '', background_cache_password = background_cache_password)
+            sender_wallet.start_background_sync()
+            daemon.pop_blocks(1)
+            daemon.flush_txpool()
+
+            daemon.generateblocks('46r4nYSevkfBUMhuykdK3gQ98XDqDTYW1hNLaXNvjpsJaSbNtdXh1sKMsdVgqkaihChAzEy29zEDPMR3NHQvGoZCLGwTerK', 1)
+
+            # Make sure the wallet can no longer see the tx
+            sender_wallet.refresh()
+            sender_wallet.stop_background_sync(wallet_password = '', seed = seeds[0])
+            transfers = sender_wallet.get_transfers()
+            no_tx = [x for x in transfers.out if x.txid == txid]
+            assert len(no_tx) == 0
+            assert sender_wallet.get_balance().balance == sender_starting_balance
+
+        # Clean up
+        daemon.out_peers(12)
+        util_resources.remove_wallet_files('test1')
+        self.wallet[0].close_wallet()
+        self.wallet[0].restore_deterministic_wallet(seed = seeds[0])
+
 if __name__ == '__main__':
     TransferTest().run_test()
diff --git a/tests/functional_tests/txpool.py b/tests/functional_tests/txpool.py
index 273337631..8e4d9c6eb 100755
--- a/tests/functional_tests/txpool.py
+++ b/tests/functional_tests/txpool.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python3
 
-# Copyright (c) 2019-2023, The Monero Project
+# Copyright (c) 2019-2024, The Monero Project
 # 
 # All rights reserved.
 # 
diff --git a/tests/functional_tests/uri.py b/tests/functional_tests/uri.py
index 1cbba14df..7d42c5ef8 100755
--- a/tests/functional_tests/uri.py
+++ b/tests/functional_tests/uri.py
@@ -1,7 +1,7 @@
 #!/usr/bin/env python3
 #encoding=utf-8
 
-# Copyright (c) 2019-2023, The Monero Project
+# Copyright (c) 2019-2024, The Monero Project
 # 
 # All rights reserved.
 # 
diff --git a/tests/functional_tests/util_resources.py b/tests/functional_tests/util_resources.py
index c12506146..9c879b27c 100755
--- a/tests/functional_tests/util_resources.py
+++ b/tests/functional_tests/util_resources.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python3
 
-# Copyright (c) 2021-2023, The Monero Project
+# Copyright (c) 2021-2024, The Monero Project
 
 #
 # All rights reserved.
@@ -37,6 +37,8 @@
 from __future__ import print_function
 import subprocess
 import psutil
+import os
+import errno
 
 def available_ram_gb():
     ram_bytes = psutil.virtual_memory().available
@@ -51,3 +53,26 @@ def get_time_pi_seconds(cores, app_dir='.'):
     miliseconds = int(decoded)
 
     return miliseconds / 1000.0
+
+def remove_file(name):
+    WALLET_DIRECTORY = os.environ['WALLET_DIRECTORY']
+    assert WALLET_DIRECTORY != ''
+    try:
+        os.unlink(WALLET_DIRECTORY + '/' + name)
+    except OSError as e:
+        if e.errno != errno.ENOENT:
+            raise
+
+def get_file_path(name):
+    WALLET_DIRECTORY = os.environ['WALLET_DIRECTORY']
+    assert WALLET_DIRECTORY != ''
+    return WALLET_DIRECTORY + '/' + name
+
+def remove_wallet_files(name):
+    for suffix in ['', '.keys', '.background', '.background.keys', '.address.txt']:
+        remove_file(name + suffix)
+
+def file_exists(name):
+    WALLET_DIRECTORY = os.environ['WALLET_DIRECTORY']
+    assert WALLET_DIRECTORY != ''
+    return os.path.isfile(WALLET_DIRECTORY + '/' + name)
diff --git a/tests/functional_tests/validate_address.py b/tests/functional_tests/validate_address.py
index 915308df5..c237ba0c2 100755
--- a/tests/functional_tests/validate_address.py
+++ b/tests/functional_tests/validate_address.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python3
 
-# Copyright (c) 2019-2023, The Monero Project
+# Copyright (c) 2019-2024, The Monero Project
 # 
 # All rights reserved.
 # 
diff --git a/tests/functional_tests/wallet.py b/tests/functional_tests/wallet.py
index 3bb4459d6..b55a07a70 100755
--- a/tests/functional_tests/wallet.py
+++ b/tests/functional_tests/wallet.py
@@ -1,7 +1,7 @@
 #!/usr/bin/env python3
 #encoding=utf-8
 
-# Copyright (c) 2019-2023, The Monero Project
+# Copyright (c) 2019-2024, The Monero Project
 # 
 # All rights reserved.
 # 
@@ -34,8 +34,7 @@
 
 from __future__ import print_function
 import sys
-import os
-import errno
+import util_resources
 
 from framework.wallet import Wallet
 from framework.daemon import Daemon
@@ -54,24 +53,6 @@ class WalletTest():
       self.change_password()
       self.store()
 
-    def remove_file(self, name):
-        WALLET_DIRECTORY = os.environ['WALLET_DIRECTORY']
-        assert WALLET_DIRECTORY != ''
-        try:
-            os.unlink(WALLET_DIRECTORY + '/' + name)
-        except OSError as e:
-            if e.errno != errno.ENOENT:
-                raise
-
-    def remove_wallet_files(self, name):
-        for suffix in ['', '.keys']:
-            self.remove_file(name + suffix)
-
-    def file_exists(self, name):
-        WALLET_DIRECTORY = os.environ['WALLET_DIRECTORY']
-        assert WALLET_DIRECTORY != ''
-        return os.path.isfile(WALLET_DIRECTORY + '/' + name)
-
     def reset(self):
         print('Resetting blockchain')
         daemon = Daemon()
@@ -333,7 +314,7 @@ class WalletTest():
         try: wallet.close_wallet()
         except: pass
 
-        self.remove_wallet_files('test1')
+        util_resources.remove_wallet_files('test1')
 
         seed = 'velvet lymph giddy number token physics poetry unquoted nibs useful sabotage limits benches lifestyle eden nitrogen anvil fewest avoid batch vials washing fences goat unquoted'
         res = wallet.restore_deterministic_wallet(seed = seed, filename = 'test1')
@@ -359,7 +340,7 @@ class WalletTest():
 
         wallet.close_wallet()
 
-        self.remove_wallet_files('test1')
+        util_resources.remove_wallet_files('test1')
 
     def store(self):
         print('Testing store')
@@ -369,22 +350,26 @@ class WalletTest():
         try: wallet.close_wallet()
         except: pass
 
-        self.remove_wallet_files('test1')
+        util_resources.remove_wallet_files('test1')
 
         seed = 'velvet lymph giddy number token physics poetry unquoted nibs useful sabotage limits benches lifestyle eden nitrogen anvil fewest avoid batch vials washing fences goat unquoted'
         res = wallet.restore_deterministic_wallet(seed = seed, filename = 'test1')
         assert res.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
         assert res.seed == seed
 
-        self.remove_file('test1')
-        assert self.file_exists('test1.keys')
-        assert not self.file_exists('test1')
+        util_resources.remove_file('test1')
+        assert util_resources.file_exists('test1.keys')
+        assert not util_resources.file_exists('test1')
         wallet.store()
-        assert self.file_exists('test1.keys')
-        assert self.file_exists('test1')
+        assert util_resources.file_exists('test1.keys')
+        assert util_resources.file_exists('test1')
 
         wallet.close_wallet()
-        self.remove_wallet_files('test1')
+
+        wallet.open_wallet(filename = 'test1', password = '')
+        wallet.close_wallet()
+
+        util_resources.remove_wallet_files('test1')
 
 
 if __name__ == '__main__':
diff --git a/tests/fuzz/CMakeLists.txt b/tests/fuzz/CMakeLists.txt
index 570ee78f9..cd517a4ae 100644
--- a/tests/fuzz/CMakeLists.txt
+++ b/tests/fuzz/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/tests/fuzz/base58.cpp b/tests/fuzz/base58.cpp
index 8cdce0921..90758e184 100644
--- a/tests/fuzz/base58.cpp
+++ b/tests/fuzz/base58.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/fuzz/block.cpp b/tests/fuzz/block.cpp
index 1da38fa44..fb7e788cc 100644
--- a/tests/fuzz/block.cpp
+++ b/tests/fuzz/block.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/fuzz/bulletproof.cpp b/tests/fuzz/bulletproof.cpp
index 210f444f1..eb705838f 100644
--- a/tests/fuzz/bulletproof.cpp
+++ b/tests/fuzz/bulletproof.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/fuzz/cold-outputs.cpp b/tests/fuzz/cold-outputs.cpp
index 79a43ddca..72847a260 100644
--- a/tests/fuzz/cold-outputs.cpp
+++ b/tests/fuzz/cold-outputs.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/fuzz/cold-transaction.cpp b/tests/fuzz/cold-transaction.cpp
index 96c84bf76..395de3fd5 100644
--- a/tests/fuzz/cold-transaction.cpp
+++ b/tests/fuzz/cold-transaction.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/fuzz/fuzzer.cpp b/tests/fuzz/fuzzer.cpp
index d9016866a..efe473bf0 100644
--- a/tests/fuzz/fuzzer.cpp
+++ b/tests/fuzz/fuzzer.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/fuzz/fuzzer.h b/tests/fuzz/fuzzer.h
index aecf44707..1a4baef08 100644
--- a/tests/fuzz/fuzzer.h
+++ b/tests/fuzz/fuzzer.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/fuzz/http-client.cpp b/tests/fuzz/http-client.cpp
index 629d012cb..2b4a032b1 100644
--- a/tests/fuzz/http-client.cpp
+++ b/tests/fuzz/http-client.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/fuzz/levin.cpp b/tests/fuzz/levin.cpp
index b3f64b03f..7ebb1bb18 100644
--- a/tests/fuzz/levin.cpp
+++ b/tests/fuzz/levin.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/fuzz/load_from_binary.cpp b/tests/fuzz/load_from_binary.cpp
index 476ca2955..a758c9427 100644
--- a/tests/fuzz/load_from_binary.cpp
+++ b/tests/fuzz/load_from_binary.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/fuzz/load_from_json.cpp b/tests/fuzz/load_from_json.cpp
index 7cdb55054..bcf0fe6df 100644
--- a/tests/fuzz/load_from_json.cpp
+++ b/tests/fuzz/load_from_json.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/fuzz/parse_url.cpp b/tests/fuzz/parse_url.cpp
index 7b68c1532..384f38fcb 100644
--- a/tests/fuzz/parse_url.cpp
+++ b/tests/fuzz/parse_url.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/fuzz/signature.cpp b/tests/fuzz/signature.cpp
index 92f4911bf..f6348b5f3 100644
--- a/tests/fuzz/signature.cpp
+++ b/tests/fuzz/signature.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/fuzz/transaction.cpp b/tests/fuzz/transaction.cpp
index 2f7f629ff..c829d9a2a 100644
--- a/tests/fuzz/transaction.cpp
+++ b/tests/fuzz/transaction.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/fuzz/tx-extra.cpp b/tests/fuzz/tx-extra.cpp
index b7b0fdec7..87dd1cea6 100644
--- a/tests/fuzz/tx-extra.cpp
+++ b/tests/fuzz/tx-extra.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020-2023, The Monero Project
+// Copyright (c) 2020-2024, The Monero Project
 
 // 
 // All rights reserved.
diff --git a/tests/fuzz/utf8.cpp b/tests/fuzz/utf8.cpp
index 3b6033e8b..8173fa262 100644
--- a/tests/fuzz/utf8.cpp
+++ b/tests/fuzz/utf8.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/hash-target.cpp b/tests/hash-target.cpp
index 440742666..15d41990f 100644
--- a/tests/hash-target.cpp
+++ b/tests/hash-target.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/hash/CMakeLists.txt b/tests/hash/CMakeLists.txt
index fe938c856..b8f719927 100644
--- a/tests/hash/CMakeLists.txt
+++ b/tests/hash/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/tests/hash/main.cpp b/tests/hash/main.cpp
index c1ad80c7c..f44aba2f6 100644
--- a/tests/hash/main.cpp
+++ b/tests/hash/main.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/io.h b/tests/io.h
index d6d82aaa2..8a0137ab4 100644
--- a/tests/io.h
+++ b/tests/io.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/libwallet_api_tests/CMakeLists.txt b/tests/libwallet_api_tests/CMakeLists.txt
index 9b081ab1d..a8be47d57 100644
--- a/tests/libwallet_api_tests/CMakeLists.txt
+++ b/tests/libwallet_api_tests/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/tests/libwallet_api_tests/main.cpp b/tests/libwallet_api_tests/main.cpp
index 9a9ccd5b7..e51e2fb9a 100644
--- a/tests/libwallet_api_tests/main.cpp
+++ b/tests/libwallet_api_tests/main.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/net_load_tests/CMakeLists.txt b/tests/net_load_tests/CMakeLists.txt
index cabbc741b..1a6108db3 100644
--- a/tests/net_load_tests/CMakeLists.txt
+++ b/tests/net_load_tests/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/tests/net_load_tests/clt.cpp b/tests/net_load_tests/clt.cpp
index 21b0359c1..ab5de976f 100644
--- a/tests/net_load_tests/clt.cpp
+++ b/tests/net_load_tests/clt.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/net_load_tests/net_load_tests.h b/tests/net_load_tests/net_load_tests.h
index 67d5605d7..f938ce083 100644
--- a/tests/net_load_tests/net_load_tests.h
+++ b/tests/net_load_tests/net_load_tests.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/net_load_tests/srv.cpp b/tests/net_load_tests/srv.cpp
index 990e54460..6abc39166 100644
--- a/tests/net_load_tests/srv.cpp
+++ b/tests/net_load_tests/srv.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/performance_tests/CMakeLists.txt b/tests/performance_tests/CMakeLists.txt
index 5dd278c6f..a1158fcec 100644
--- a/tests/performance_tests/CMakeLists.txt
+++ b/tests/performance_tests/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/tests/performance_tests/bulletproof.h b/tests/performance_tests/bulletproof.h
index 989b4ba00..d670e5372 100644
--- a/tests/performance_tests/bulletproof.h
+++ b/tests/performance_tests/bulletproof.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/performance_tests/bulletproof_plus.h b/tests/performance_tests/bulletproof_plus.h
index 152e66046..4dab9f829 100644
--- a/tests/performance_tests/bulletproof_plus.h
+++ b/tests/performance_tests/bulletproof_plus.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/performance_tests/check_hash.h b/tests/performance_tests/check_hash.h
index d786cc36a..f93c5acec 100644
--- a/tests/performance_tests/check_hash.h
+++ b/tests/performance_tests/check_hash.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2023, The Monero Project
+// Copyright (c) 2019-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/performance_tests/check_tx_signature.h b/tests/performance_tests/check_tx_signature.h
index 2ae4a2613..034148df5 100644
--- a/tests/performance_tests/check_tx_signature.h
+++ b/tests/performance_tests/check_tx_signature.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/performance_tests/cn_fast_hash.h b/tests/performance_tests/cn_fast_hash.h
index 8f6d2d6b8..f0e0bfb1e 100644
--- a/tests/performance_tests/cn_fast_hash.h
+++ b/tests/performance_tests/cn_fast_hash.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/performance_tests/cn_slow_hash.h b/tests/performance_tests/cn_slow_hash.h
index 26f4113de..2c5109ea3 100644
--- a/tests/performance_tests/cn_slow_hash.h
+++ b/tests/performance_tests/cn_slow_hash.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/performance_tests/construct_tx.h b/tests/performance_tests/construct_tx.h
index 14a603ca5..4bbd870e9 100644
--- a/tests/performance_tests/construct_tx.h
+++ b/tests/performance_tests/construct_tx.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/performance_tests/crypto_ops.h b/tests/performance_tests/crypto_ops.h
index 9753bbeb7..b8ebc85dc 100644
--- a/tests/performance_tests/crypto_ops.h
+++ b/tests/performance_tests/crypto_ops.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/performance_tests/derive_public_key.h b/tests/performance_tests/derive_public_key.h
index 29ab0aefa..55846b7a1 100644
--- a/tests/performance_tests/derive_public_key.h
+++ b/tests/performance_tests/derive_public_key.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/performance_tests/derive_secret_key.h b/tests/performance_tests/derive_secret_key.h
index 49b546599..3bd028a2e 100644
--- a/tests/performance_tests/derive_secret_key.h
+++ b/tests/performance_tests/derive_secret_key.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/performance_tests/derive_view_tag.h b/tests/performance_tests/derive_view_tag.h
index fed00b905..6c36251b4 100644
--- a/tests/performance_tests/derive_view_tag.h
+++ b/tests/performance_tests/derive_view_tag.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/tests/performance_tests/equality.h b/tests/performance_tests/equality.h
index 51b9cb609..0c1378b1f 100644
--- a/tests/performance_tests/equality.h
+++ b/tests/performance_tests/equality.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/performance_tests/ge_frombytes_vartime.h b/tests/performance_tests/ge_frombytes_vartime.h
index d3d717195..8eb2c5329 100644
--- a/tests/performance_tests/ge_frombytes_vartime.h
+++ b/tests/performance_tests/ge_frombytes_vartime.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/performance_tests/ge_tobytes.h b/tests/performance_tests/ge_tobytes.h
index 7adf1dcea..dd5769194 100644
--- a/tests/performance_tests/ge_tobytes.h
+++ b/tests/performance_tests/ge_tobytes.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/performance_tests/generate_key_derivation.h b/tests/performance_tests/generate_key_derivation.h
index dc1d7e3d0..b07c792ff 100644
--- a/tests/performance_tests/generate_key_derivation.h
+++ b/tests/performance_tests/generate_key_derivation.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/performance_tests/generate_key_image.h b/tests/performance_tests/generate_key_image.h
index 36d86a77c..afe168dee 100644
--- a/tests/performance_tests/generate_key_image.h
+++ b/tests/performance_tests/generate_key_image.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/performance_tests/generate_key_image_helper.h b/tests/performance_tests/generate_key_image_helper.h
index 03ef0138c..98e28983c 100644
--- a/tests/performance_tests/generate_key_image_helper.h
+++ b/tests/performance_tests/generate_key_image_helper.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/performance_tests/generate_keypair.h b/tests/performance_tests/generate_keypair.h
index 1ec76c0a6..fc023595d 100644
--- a/tests/performance_tests/generate_keypair.h
+++ b/tests/performance_tests/generate_keypair.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/performance_tests/is_out_to_acc.h b/tests/performance_tests/is_out_to_acc.h
index 017145aa9..c8676a5bc 100644
--- a/tests/performance_tests/is_out_to_acc.h
+++ b/tests/performance_tests/is_out_to_acc.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp
index 9cae5df1f..929eec590 100644
--- a/tests/performance_tests/main.cpp
+++ b/tests/performance_tests/main.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/performance_tests/multi_tx_test_base.h b/tests/performance_tests/multi_tx_test_base.h
index 23186a2e0..75e0120cf 100644
--- a/tests/performance_tests/multi_tx_test_base.h
+++ b/tests/performance_tests/multi_tx_test_base.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/performance_tests/multiexp.h b/tests/performance_tests/multiexp.h
index 14493d34f..5332d047f 100644
--- a/tests/performance_tests/multiexp.h
+++ b/tests/performance_tests/multiexp.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 // 
 // All rights reserved.
diff --git a/tests/performance_tests/out_can_be_to_acc.h b/tests/performance_tests/out_can_be_to_acc.h
index ac38c0f58..f09f08b5e 100644
--- a/tests/performance_tests/out_can_be_to_acc.h
+++ b/tests/performance_tests/out_can_be_to_acc.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/tests/performance_tests/performance_tests.h b/tests/performance_tests/performance_tests.h
index 68679a36c..0f6063fcf 100644
--- a/tests/performance_tests/performance_tests.h
+++ b/tests/performance_tests/performance_tests.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/performance_tests/performance_utils.h b/tests/performance_tests/performance_utils.h
index 013aa94cd..44d5eded8 100644
--- a/tests/performance_tests/performance_utils.h
+++ b/tests/performance_tests/performance_utils.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/performance_tests/range_proof.h b/tests/performance_tests/range_proof.h
index 702a73d53..8774b0439 100644
--- a/tests/performance_tests/range_proof.h
+++ b/tests/performance_tests/range_proof.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/performance_tests/rct_mlsag.h b/tests/performance_tests/rct_mlsag.h
index 034df3183..3d1732b7a 100644
--- a/tests/performance_tests/rct_mlsag.h
+++ b/tests/performance_tests/rct_mlsag.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/performance_tests/sc_check.h b/tests/performance_tests/sc_check.h
index 6df8e0d5d..10f225b45 100644
--- a/tests/performance_tests/sc_check.h
+++ b/tests/performance_tests/sc_check.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 // 
 // All rights reserved.
diff --git a/tests/performance_tests/sc_reduce32.h b/tests/performance_tests/sc_reduce32.h
index 5dadc4395..301096e8a 100644
--- a/tests/performance_tests/sc_reduce32.h
+++ b/tests/performance_tests/sc_reduce32.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/performance_tests/sig_clsag.h b/tests/performance_tests/sig_clsag.h
index 6c36819a5..e7c942c9b 100644
--- a/tests/performance_tests/sig_clsag.h
+++ b/tests/performance_tests/sig_clsag.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/performance_tests/sig_mlsag.h b/tests/performance_tests/sig_mlsag.h
index 54e89829d..53a97a2c8 100644
--- a/tests/performance_tests/sig_mlsag.h
+++ b/tests/performance_tests/sig_mlsag.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/performance_tests/signature.h b/tests/performance_tests/signature.h
index e8aa8f33b..79b1df9d1 100644
--- a/tests/performance_tests/signature.h
+++ b/tests/performance_tests/signature.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/performance_tests/single_tx_test_base.h b/tests/performance_tests/single_tx_test_base.h
index b0a3f30bc..d337a998a 100644
--- a/tests/performance_tests/single_tx_test_base.h
+++ b/tests/performance_tests/single_tx_test_base.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/performance_tests/subaddress_expand.h b/tests/performance_tests/subaddress_expand.h
index a154aba21..505e27ba2 100644
--- a/tests/performance_tests/subaddress_expand.h
+++ b/tests/performance_tests/subaddress_expand.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/trezor/CMakeLists.txt b/tests/trezor/CMakeLists.txt
index 14c25823b..70b1df19a 100644
--- a/tests/trezor/CMakeLists.txt
+++ b/tests/trezor/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/tests/trezor/daemon.cpp b/tests/trezor/daemon.cpp
index de4f9bc51..a6e1e172f 100644
--- a/tests/trezor/daemon.cpp
+++ b/tests/trezor/daemon.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/tests/trezor/daemon.h b/tests/trezor/daemon.h
index 3c0b6b78b..99f61ad6f 100644
--- a/tests/trezor/daemon.h
+++ b/tests/trezor/daemon.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/tests/trezor/tools.cpp b/tests/trezor/tools.cpp
index aa5664611..4e496e283 100644
--- a/tests/trezor/tools.cpp
+++ b/tests/trezor/tools.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/tests/trezor/tools.h b/tests/trezor/tools.h
index b628bbefa..de22d55f4 100644
--- a/tests/trezor/tools.h
+++ b/tests/trezor/tools.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/tests/trezor/trezor_tests.cpp b/tests/trezor/trezor_tests.cpp
index 0fcd10cef..56fb33357 100644
--- a/tests/trezor/trezor_tests.cpp
+++ b/tests/trezor/trezor_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/trezor/trezor_tests.h b/tests/trezor/trezor_tests.h
index 16d7641db..0b3136b68 100644
--- a/tests/trezor/trezor_tests.h
+++ b/tests/trezor/trezor_tests.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt
index 567ca8b00..8659b0ed0 100644
--- a/tests/unit_tests/CMakeLists.txt
+++ b/tests/unit_tests/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/tests/unit_tests/account.cpp b/tests/unit_tests/account.cpp
index 68fb63ebf..9dedea2c3 100644
--- a/tests/unit_tests/account.cpp
+++ b/tests/unit_tests/account.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/unit_tests/address_from_url.cpp b/tests/unit_tests/address_from_url.cpp
index d33502d6e..35ad28d08 100644
--- a/tests/unit_tests/address_from_url.cpp
+++ b/tests/unit_tests/address_from_url.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/unit_tests/aligned.cpp b/tests/unit_tests/aligned.cpp
index 9ae43aeb3..ff20c9843 100644
--- a/tests/unit_tests/aligned.cpp
+++ b/tests/unit_tests/aligned.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 // 
 // All rights reserved.
diff --git a/tests/unit_tests/apply_permutation.cpp b/tests/unit_tests/apply_permutation.cpp
index 829af4e8a..40f7dade4 100644
--- a/tests/unit_tests/apply_permutation.cpp
+++ b/tests/unit_tests/apply_permutation.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/unit_tests/base58.cpp b/tests/unit_tests/base58.cpp
index 4b897308f..f105a0458 100644
--- a/tests/unit_tests/base58.cpp
+++ b/tests/unit_tests/base58.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/unit_tests/block_queue.cpp b/tests/unit_tests/block_queue.cpp
index 61e1b050e..a643b877d 100644
--- a/tests/unit_tests/block_queue.cpp
+++ b/tests/unit_tests/block_queue.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/unit_tests/block_reward.cpp b/tests/unit_tests/block_reward.cpp
index c469fac2c..7cbf9bc29 100644
--- a/tests/unit_tests/block_reward.cpp
+++ b/tests/unit_tests/block_reward.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/unit_tests/blockchain_db.cpp b/tests/unit_tests/blockchain_db.cpp
index 0207a6e8f..66219322e 100644
--- a/tests/unit_tests/blockchain_db.cpp
+++ b/tests/unit_tests/blockchain_db.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/unit_tests/bootstrap_node_selector.cpp b/tests/unit_tests/bootstrap_node_selector.cpp
index 4c5929502..1ac9b915b 100644
--- a/tests/unit_tests/bootstrap_node_selector.cpp
+++ b/tests/unit_tests/bootstrap_node_selector.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020-2023, The Monero Project
+// Copyright (c) 2020-2024, The Monero Project
 
 // 
 // All rights reserved.
diff --git a/tests/unit_tests/bulletproofs.cpp b/tests/unit_tests/bulletproofs.cpp
index 9fa6887fc..12d8fd131 100644
--- a/tests/unit_tests/bulletproofs.cpp
+++ b/tests/unit_tests/bulletproofs.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/unit_tests/bulletproofs_plus.cpp b/tests/unit_tests/bulletproofs_plus.cpp
index 4bcfdb682..c1b5e1326 100644
--- a/tests/unit_tests/bulletproofs_plus.cpp
+++ b/tests/unit_tests/bulletproofs_plus.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/unit_tests/canonical_amounts.cpp b/tests/unit_tests/canonical_amounts.cpp
index e10a5bc8e..4a7b9d0c8 100644
--- a/tests/unit_tests/canonical_amounts.cpp
+++ b/tests/unit_tests/canonical_amounts.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/unit_tests/chacha.cpp b/tests/unit_tests/chacha.cpp
index 3041b02a2..6386e7ec6 100644
--- a/tests/unit_tests/chacha.cpp
+++ b/tests/unit_tests/chacha.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/unit_tests/checkpoints.cpp b/tests/unit_tests/checkpoints.cpp
index f047bd439..d860fabb4 100644
--- a/tests/unit_tests/checkpoints.cpp
+++ b/tests/unit_tests/checkpoints.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/unit_tests/command_line.cpp b/tests/unit_tests/command_line.cpp
index 7e2e5a18d..651c84bc3 100644
--- a/tests/unit_tests/command_line.cpp
+++ b/tests/unit_tests/command_line.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/tests/unit_tests/crypto.cpp b/tests/unit_tests/crypto.cpp
index 9f79ed60e..f25123563 100644
--- a/tests/unit_tests/crypto.cpp
+++ b/tests/unit_tests/crypto.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/tests/unit_tests/decompose_amount_into_digits.cpp b/tests/unit_tests/decompose_amount_into_digits.cpp
index e69eac232..f1b50baa0 100644
--- a/tests/unit_tests/decompose_amount_into_digits.cpp
+++ b/tests/unit_tests/decompose_amount_into_digits.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/unit_tests/device.cpp b/tests/unit_tests/device.cpp
index 6d8bb1492..4812251df 100644
--- a/tests/unit_tests/device.cpp
+++ b/tests/unit_tests/device.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 // 
 // All rights reserved.
diff --git a/tests/unit_tests/difficulty.cpp b/tests/unit_tests/difficulty.cpp
index ae4997dd1..c8e9ceefc 100644
--- a/tests/unit_tests/difficulty.cpp
+++ b/tests/unit_tests/difficulty.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2023, The Monero Project
+// Copyright (c) 2019-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/unit_tests/dns_resolver.cpp b/tests/unit_tests/dns_resolver.cpp
index 87957398e..3c890687b 100644
--- a/tests/unit_tests/dns_resolver.cpp
+++ b/tests/unit_tests/dns_resolver.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/unit_tests/epee_boosted_tcp_server.cpp b/tests/unit_tests/epee_boosted_tcp_server.cpp
index d68f3c9bb..3fff9af04 100644
--- a/tests/unit_tests/epee_boosted_tcp_server.cpp
+++ b/tests/unit_tests/epee_boosted_tcp_server.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/unit_tests/epee_levin_protocol_handler_async.cpp b/tests/unit_tests/epee_levin_protocol_handler_async.cpp
index 05cc6412f..e445466c0 100644
--- a/tests/unit_tests/epee_levin_protocol_handler_async.cpp
+++ b/tests/unit_tests/epee_levin_protocol_handler_async.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/unit_tests/epee_serialization.cpp b/tests/unit_tests/epee_serialization.cpp
index ecc26e971..e81f00cd7 100644
--- a/tests/unit_tests/epee_serialization.cpp
+++ b/tests/unit_tests/epee_serialization.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020-2023, The Monero Project
+// Copyright (c) 2020-2024, The Monero Project
 
 //
 // All rights reserved.
diff --git a/tests/unit_tests/epee_utils.cpp b/tests/unit_tests/epee_utils.cpp
index 3573f21ba..31bdc698d 100644
--- a/tests/unit_tests/epee_utils.cpp
+++ b/tests/unit_tests/epee_utils.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/tests/unit_tests/expect.cpp b/tests/unit_tests/expect.cpp
index d4cb8cf37..92d06fc1f 100644
--- a/tests/unit_tests/expect.cpp
+++ b/tests/unit_tests/expect.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 //
 // All rights reserved.
diff --git a/tests/unit_tests/get_xtype_from_string.cpp b/tests/unit_tests/get_xtype_from_string.cpp
index f499bca0d..4e61a651a 100644
--- a/tests/unit_tests/get_xtype_from_string.cpp
+++ b/tests/unit_tests/get_xtype_from_string.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/unit_tests/hardfork.cpp b/tests/unit_tests/hardfork.cpp
index e6602bdb3..56958a0d8 100644
--- a/tests/unit_tests/hardfork.cpp
+++ b/tests/unit_tests/hardfork.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
@@ -375,7 +375,7 @@ TEST(voting, threshold)
       db.add_block(mkblock(hf, h, v), 0, 0, 0, 0, 0, crypto::hash());
       bool ret = hf.add(db.get_block_from_height(h), h);
       if (h >= 8 && threshold == 87) {
-        // for threshold 87, we reach the treshold at height 7, so from height 8, hard fork to version 2, but 8 tries to add 1
+        // for threshold 87, we reach the threshold at height 7, so from height 8, hard fork to version 2, but 8 tries to add 1
         ASSERT_FALSE(ret);
       }
       else {
diff --git a/tests/unit_tests/hashchain.cpp b/tests/unit_tests/hashchain.cpp
index fc07a2986..aef46cca9 100644
--- a/tests/unit_tests/hashchain.cpp
+++ b/tests/unit_tests/hashchain.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/unit_tests/hmac_keccak.cpp b/tests/unit_tests/hmac_keccak.cpp
index 6f83b5a29..3ff1efe5d 100644
--- a/tests/unit_tests/hmac_keccak.cpp
+++ b/tests/unit_tests/hmac_keccak.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 // 
 // All rights reserved.
diff --git a/tests/unit_tests/http.cpp b/tests/unit_tests/http.cpp
index 72870aa18..12dcd0325 100644
--- a/tests/unit_tests/http.cpp
+++ b/tests/unit_tests/http.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 //
 // All rights reserved.
 // 
diff --git a/tests/unit_tests/json_serialization.cpp b/tests/unit_tests/json_serialization.cpp
index 9525d23ea..9ac019aec 100644
--- a/tests/unit_tests/json_serialization.cpp
+++ b/tests/unit_tests/json_serialization.cpp
@@ -124,6 +124,68 @@ TEST(JsonSerialization, InvalidVectorBytes)
     EXPECT_THROW(cryptonote::json::fromJsonValue(doc, out), cryptonote::json::BAD_INPUT);
 }
 
+TEST(JsonSerialization, DaemonInfo)
+{
+  cryptonote::rpc::DaemonInfo info{};
+  info.height = 154544;
+  info.target_height = 15345435;
+  info.top_block_height = 2344;
+  info.wide_difficulty = cryptonote::difficulty_type{"100000000000000000005443"};
+  info.difficulty = 200376420520695107;
+  info.target = 7657567;
+  info.tx_count = 355;
+  info.tx_pool_size = 45435;
+  info.alt_blocks_count = 43535;
+  info.outgoing_connections_count = 1444;
+  info.incoming_connections_count = 1444;
+  info.white_peerlist_size = 14550;
+  info.grey_peerlist_size = 34324;
+  info.mainnet = true;
+  info.testnet = true;
+  info.stagenet = true;
+  info.nettype = "main";
+  info.top_block_hash = crypto::hash{1};
+  info.wide_cumulative_difficulty = cryptonote::difficulty_type{"200000000000000000005543"};
+  info.cumulative_difficulty = 400752841041384871;
+  info.block_size_limit = 4324234;
+  info.block_weight_limit = 3434;
+  info.block_size_median = 3434;
+  info.adjusted_time = 4535;
+  info.block_weight_median = 43535;
+  info.start_time = 34535;
+  info.version = "1.0";
+
+  const auto info_copy = test_json(info);
+
+  EXPECT_EQ(info.height, info_copy.height);
+  EXPECT_EQ(info.target_height, info_copy.target_height);
+  EXPECT_EQ(info.top_block_height, info_copy.top_block_height);
+  EXPECT_EQ(info.wide_difficulty, info_copy.wide_difficulty);
+  EXPECT_EQ(info.difficulty, info_copy.difficulty);
+  EXPECT_EQ(info.target, info_copy.target);
+  EXPECT_EQ(info.tx_count, info_copy.tx_count);
+  EXPECT_EQ(info.tx_pool_size, info_copy.tx_pool_size);
+  EXPECT_EQ(info.alt_blocks_count, info_copy.alt_blocks_count);
+  EXPECT_EQ(info.outgoing_connections_count, info_copy.outgoing_connections_count);
+  EXPECT_EQ(info.incoming_connections_count, info_copy.incoming_connections_count);
+  EXPECT_EQ(info.white_peerlist_size, info_copy.white_peerlist_size);
+  EXPECT_EQ(info.grey_peerlist_size, info_copy.grey_peerlist_size);
+  EXPECT_EQ(info.mainnet, info_copy.mainnet);
+  EXPECT_EQ(info.testnet, info_copy.testnet);
+  EXPECT_EQ(info.stagenet, info_copy.stagenet);
+  EXPECT_EQ(info.nettype, info_copy.nettype);
+  EXPECT_EQ(info.top_block_hash, info_copy.top_block_hash);
+  EXPECT_EQ(info.wide_cumulative_difficulty, info_copy.wide_cumulative_difficulty);
+  EXPECT_EQ(info.cumulative_difficulty, info_copy.cumulative_difficulty);
+  EXPECT_EQ(info.block_size_limit, info_copy.block_size_limit);
+  EXPECT_EQ(info.block_weight_limit, info_copy.block_weight_limit);
+  EXPECT_EQ(info.block_size_median, info_copy.block_size_median);
+  EXPECT_EQ(info.adjusted_time, info_copy.adjusted_time);
+  EXPECT_EQ(info.block_weight_median, info_copy.block_weight_median);
+  EXPECT_EQ(info.start_time, info_copy.start_time);
+  EXPECT_EQ(info.version, info_copy.version);
+}
+
 TEST(JsonSerialization, MinerTransaction)
 {
     cryptonote::account_base acct;
diff --git a/tests/unit_tests/json_serialization.h b/tests/unit_tests/json_serialization.h
index 8aeac6fde..4362d1977 100644
--- a/tests/unit_tests/json_serialization.h
+++ b/tests/unit_tests/json_serialization.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2020-2023, The Monero Project
+// Copyright (c) 2020-2024, The Monero Project
 
 //
 // All rights reserved.
diff --git a/tests/unit_tests/keccak.cpp b/tests/unit_tests/keccak.cpp
index 780138615..6c7365dc6 100644
--- a/tests/unit_tests/keccak.cpp
+++ b/tests/unit_tests/keccak.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 // 
 // All rights reserved.
diff --git a/tests/unit_tests/levin.cpp b/tests/unit_tests/levin.cpp
index d686df87d..6ebed8494 100644
--- a/tests/unit_tests/levin.cpp
+++ b/tests/unit_tests/levin.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2023, The Monero Project
+// Copyright (c) 2019-2024, The Monero Project
 //
 // All rights reserved.
 //
@@ -2219,6 +2219,63 @@ TEST_F(levin_notify, fluff_multiple)
     }
 }
 
+TEST_F(levin_notify, fluff_with_duplicate)
+{
+    std::shared_ptr<cryptonote::levin::notify> notifier_ptr = make_notifier(0, true, false);
+    auto &notifier = *notifier_ptr;
+
+    for (unsigned count = 0; count < 10; ++count)
+        add_connection(count % 2 == 0);
+
+    {
+        const auto status = notifier.get_status();
+        EXPECT_FALSE(status.has_noise);
+        EXPECT_FALSE(status.connections_filled);
+        EXPECT_TRUE(status.has_outgoing);
+    }
+    notifier.new_out_connection();
+    io_service_.poll();
+
+    std::vector<cryptonote::blobdata> txs(9);
+    txs[0].resize(100, 'e');
+    txs[1].resize(100, 'e');
+    txs[2].resize(100, 'e');
+    txs[3].resize(100, 'e');
+    txs[4].resize(200, 'f');
+    txs[5].resize(200, 'f');
+    txs[6].resize(200, 'f');
+    txs[7].resize(200, 'f');
+    txs[8].resize(200, 'f');
+
+    ASSERT_EQ(10u, contexts_.size());
+    {
+        auto context = contexts_.begin();
+        EXPECT_TRUE(notifier.send_txs(txs, context->get_id(), cryptonote::relay_method::fluff));
+
+        io_service_.reset();
+        ASSERT_LT(0u, io_service_.poll());
+        notifier.run_fluff();
+        ASSERT_LT(0u, io_service_.poll());
+
+        EXPECT_EQ(0u, context->process_send_queue());
+        for (++context; context != contexts_.end(); ++context)
+            EXPECT_EQ(1u, context->process_send_queue());
+
+        EXPECT_EQ(txs, events_.take_relayed(cryptonote::relay_method::fluff));
+        std::sort(txs.begin(), txs.end());
+        ASSERT_EQ(9u, receiver_.notified_size());
+        for (unsigned count = 0; count < 9; ++count)
+        {
+            auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second;
+            EXPECT_NE(txs, notification.txs);
+            EXPECT_EQ(notification.txs.size(), 2);
+            EXPECT_TRUE(notification._.empty());
+            EXPECT_TRUE(notification.dandelionpp_fluff);
+        }
+    }
+
+}
+
 TEST_F(levin_notify, noise)
 {
     for (unsigned count = 0; count < 10; ++count)
diff --git a/tests/unit_tests/lmdb.cpp b/tests/unit_tests/lmdb.cpp
index c213577fb..5b7d35d10 100644
--- a/tests/unit_tests/lmdb.cpp
+++ b/tests/unit_tests/lmdb.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/unit_tests/logging.cpp b/tests/unit_tests/logging.cpp
index 20f1466b4..b13919831 100644
--- a/tests/unit_tests/logging.cpp
+++ b/tests/unit_tests/logging.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2016-2023, The Monero Project
+// Copyright (c) 2016-2024, The Monero Project
 // 
 // All rights reserved.
 // 
@@ -166,7 +166,7 @@ TEST(logging, glob_prefix)
 TEST(logging, last_precedence)
 {
   init();
-  mlog_set_categories("gobal:FATAL,glo*:DEBUG");
+  mlog_set_categories("global:FATAL,glo*:DEBUG");
   log();
   std::string str;
   ASSERT_TRUE(load_log_to_string(log_filename, str));
diff --git a/tests/unit_tests/long_term_block_weight.cpp b/tests/unit_tests/long_term_block_weight.cpp
index 5d954bf0c..f7ef262e6 100644
--- a/tests/unit_tests/long_term_block_weight.cpp
+++ b/tests/unit_tests/long_term_block_weight.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2023, The Monero Project
+// Copyright (c) 2019-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/unit_tests/main.cpp b/tests/unit_tests/main.cpp
index 4094d79b7..d613adb6c 100644
--- a/tests/unit_tests/main.cpp
+++ b/tests/unit_tests/main.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/unit_tests/memwipe.cpp b/tests/unit_tests/memwipe.cpp
index 343f66722..3b82677ee 100644
--- a/tests/unit_tests/memwipe.cpp
+++ b/tests/unit_tests/memwipe.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/unit_tests/mlocker.cpp b/tests/unit_tests/mlocker.cpp
index ea8d3de3f..e9947349d 100644
--- a/tests/unit_tests/mlocker.cpp
+++ b/tests/unit_tests/mlocker.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 // 
 // All rights reserved.
diff --git a/tests/unit_tests/mnemonics.cpp b/tests/unit_tests/mnemonics.cpp
index ca568380a..8e8886f55 100644
--- a/tests/unit_tests/mnemonics.cpp
+++ b/tests/unit_tests/mnemonics.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/unit_tests/mul_div.cpp b/tests/unit_tests/mul_div.cpp
index 800c42354..e78ee50c6 100644
--- a/tests/unit_tests/mul_div.cpp
+++ b/tests/unit_tests/mul_div.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/unit_tests/multiexp.cpp b/tests/unit_tests/multiexp.cpp
index 1e0100603..af938bebf 100644
--- a/tests/unit_tests/multiexp.cpp
+++ b/tests/unit_tests/multiexp.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 // 
 // All rights reserved.
diff --git a/tests/unit_tests/multisig.cpp b/tests/unit_tests/multisig.cpp
index c044591c1..75dfaf20c 100644
--- a/tests/unit_tests/multisig.cpp
+++ b/tests/unit_tests/multisig.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
@@ -149,6 +149,7 @@ static void check_results(const std::vector<std::string> &intermediate_infos,
   std::unordered_set<crypto::secret_key> unique_privkeys;
   rct::key composite_pubkey = rct::identity();
 
+  ASSERT_TRUE(wallets.size() > 0);
   wallets[0].decrypt_keys("");
   crypto::public_key spend_pubkey = wallets[0].get_account().get_keys().m_account_address.m_spend_public_key;
   crypto::secret_key view_privkey = wallets[0].get_account().get_keys().m_view_secret_key;
@@ -156,32 +157,48 @@ static void check_results(const std::vector<std::string> &intermediate_infos,
   EXPECT_TRUE(crypto::secret_key_to_public_key(view_privkey, view_pubkey));
   wallets[0].encrypt_keys("");
 
-  for (size_t i = 0; i < wallets.size(); ++i)
+  // at the end of multisig kex, all wallets should emit a post-kex message with the same two pubkeys
+  std::vector<crypto::public_key> post_kex_msg_pubkeys;
+  ASSERT_TRUE(intermediate_infos.size() == wallets.size());
+  for (const std::string &intermediate_info : intermediate_infos)
   {
-    EXPECT_TRUE(!intermediate_infos[i].empty());
-    const multisig::multisig_account_status ms_status{wallets[i].get_multisig_status()};
+    multisig::multisig_kex_msg post_kex_msg;
+    EXPECT_TRUE(!intermediate_info.empty());
+    EXPECT_NO_THROW(post_kex_msg = intermediate_info);
+
+    if (post_kex_msg_pubkeys.size() != 0)
+      EXPECT_TRUE(post_kex_msg_pubkeys == post_kex_msg.get_msg_pubkeys());  //assumes sorting is always the same
+    else
+      post_kex_msg_pubkeys = post_kex_msg.get_msg_pubkeys();
+
+    EXPECT_TRUE(post_kex_msg_pubkeys.size() == 2);
+  }
+
+  // the post-kex pubkeys should equal the account's public view and spend keys
+  EXPECT_TRUE(std::find(post_kex_msg_pubkeys.begin(), post_kex_msg_pubkeys.end(), spend_pubkey) != post_kex_msg_pubkeys.end());
+  EXPECT_TRUE(std::find(post_kex_msg_pubkeys.begin(), post_kex_msg_pubkeys.end(), view_pubkey) != post_kex_msg_pubkeys.end());
+
+  // each wallet should have the same state (private view key, public spend key), and the public spend key should be
+  //   reproducible from the private spend keys found in each account
+  for (tools::wallet2 &wallet : wallets)
+  {
+    wallet.decrypt_keys("");
+    const multisig::multisig_account_status ms_status{wallet.get_multisig_status()};
     EXPECT_TRUE(ms_status.multisig_is_active);
     EXPECT_TRUE(ms_status.kex_is_done);
     EXPECT_TRUE(ms_status.is_ready);
     EXPECT_TRUE(ms_status.threshold == M);
     EXPECT_TRUE(ms_status.total == wallets.size());
 
-    wallets[i].decrypt_keys("");
-
-    if (i != 0)
-    {
-      // "equals" is transitive relation so we need only to compare first wallet's address to each others' addresses.
-      // no need to compare 0's address with itself.
-      EXPECT_TRUE(wallets[0].get_account().get_public_address_str(cryptonote::TESTNET) ==
-        wallets[i].get_account().get_public_address_str(cryptonote::TESTNET));
-      
-      EXPECT_EQ(spend_pubkey, wallets[i].get_account().get_keys().m_account_address.m_spend_public_key);
-      EXPECT_EQ(view_privkey, wallets[i].get_account().get_keys().m_view_secret_key);
-      EXPECT_EQ(view_pubkey, wallets[i].get_account().get_keys().m_account_address.m_view_public_key);
-    }
+    EXPECT_TRUE(wallets[0].get_account().get_public_address_str(cryptonote::TESTNET) ==
+      wallet.get_account().get_public_address_str(cryptonote::TESTNET));
+    
+    EXPECT_EQ(spend_pubkey, wallet.get_account().get_keys().m_account_address.m_spend_public_key);
+    EXPECT_EQ(view_privkey, wallet.get_account().get_keys().m_view_secret_key);
+    EXPECT_EQ(view_pubkey, wallet.get_account().get_keys().m_account_address.m_view_public_key);
 
     // sum together unique multisig keys
-    for (const auto &privkey : wallets[i].get_account().get_keys().m_multisig_keys)
+    for (const auto &privkey : wallet.get_account().get_keys().m_multisig_keys)
     {
       EXPECT_NE(privkey, crypto::null_skey);
 
@@ -189,17 +206,17 @@ static void check_results(const std::vector<std::string> &intermediate_infos,
       {
         unique_privkeys.insert(privkey);
         crypto::public_key pubkey;
-        crypto::secret_key_to_public_key(privkey, pubkey);
+        EXPECT_TRUE(crypto::secret_key_to_public_key(privkey, pubkey));
         EXPECT_NE(privkey, crypto::null_skey);
         EXPECT_NE(pubkey, crypto::null_pkey);
         EXPECT_NE(pubkey, rct::rct2pk(rct::identity()));
         rct::addKeys(composite_pubkey, composite_pubkey, rct::pk2rct(pubkey));
       }
     }
-    wallets[i].encrypt_keys("");
+    wallet.encrypt_keys("");
   }
 
-  // final key via sums should equal the wallets' public spend key
+  // final key via sum of privkeys should equal the wallets' public spend key
   wallets[0].decrypt_keys("");
   EXPECT_EQ(wallets[0].get_account().get_keys().m_account_address.m_spend_public_key, rct::rct2pk(composite_pubkey));
   wallets[0].encrypt_keys("");
@@ -257,6 +274,104 @@ static void make_wallets(const unsigned int M, const unsigned int N, const bool
   check_results(intermediate_infos, wallets, M);
 }
 
+static void make_wallets_boosting(std::vector<tools::wallet2>& wallets, unsigned int M)
+{
+  ASSERT_TRUE(wallets.size() > 1 && wallets.size() <= KEYS_COUNT);
+  ASSERT_TRUE(M <= wallets.size());
+  std::uint32_t kex_rounds_required = multisig::multisig_kex_rounds_required(wallets.size(), M);
+  std::uint32_t rounds_required = multisig::multisig_setup_rounds_required(wallets.size(), M);
+  std::uint32_t rounds_complete{0};
+
+  // initialize wallets, get first round multisig kex msgs
+  std::vector<std::string> initial_infos(wallets.size());
+
+  for (size_t i = 0; i < wallets.size(); ++i)
+  {
+    make_wallet(i, wallets[i]);
+
+    wallets[i].decrypt_keys("");
+    initial_infos[i] = wallets[i].get_multisig_first_kex_msg();
+    wallets[i].encrypt_keys("");
+  }
+
+  // wallets should not be multisig yet
+  for (const auto &wallet: wallets)
+  {
+    const multisig::multisig_account_status ms_status{wallet.get_multisig_status()};
+    ASSERT_FALSE(ms_status.multisig_is_active);
+  }
+
+  // get round 2 booster messages for wallet0 (if appropriate)
+  auto initial_infos_truncated = initial_infos;
+  initial_infos_truncated.erase(initial_infos_truncated.begin());
+
+  std::vector<std::string> wallet0_booster_infos;
+  wallet0_booster_infos.reserve(wallets.size() - 1);
+
+  if (rounds_complete + 1 < kex_rounds_required)
+  {
+    for (size_t i = 1; i < wallets.size(); ++i)
+    {
+      wallet0_booster_infos.push_back(
+          wallets[i].get_multisig_key_exchange_booster("", initial_infos_truncated, M, wallets.size())
+        );
+    }
+  }
+
+  // make wallets multisig
+  std::vector<std::string> intermediate_infos(wallets.size());
+
+  for (size_t i = 0; i < wallets.size(); ++i)
+    intermediate_infos[i] = wallets[i].make_multisig("", initial_infos, M);
+
+  ++rounds_complete;
+
+  // perform all kex rounds
+  // boost wallet0 each round, so wallet0 is always 1 round ahead
+  std::string wallet0_intermediate_info;
+  std::vector<std::string> new_infos(intermediate_infos.size());
+  multisig::multisig_account_status ms_status{wallets[0].get_multisig_status()};
+  while (!ms_status.is_ready)
+  {
+    // use booster infos to update wallet0 'early'
+    if (rounds_complete < kex_rounds_required)
+      new_infos[0] = wallets[0].exchange_multisig_keys("", wallet0_booster_infos);
+    else
+    {
+      // force update the post-kex round with wallet0's post-kex message since wallet0 is 'ahead' of the other wallets
+      wallet0_booster_infos = {wallets[0].exchange_multisig_keys("", {})};
+      new_infos[0] = wallets[0].exchange_multisig_keys("", wallet0_booster_infos, true);
+    }
+
+    // get wallet0 booster infos for next round
+    if (rounds_complete + 1 < kex_rounds_required)
+    {
+      // remove wallet0 info for this round (so boosters have incomplete kex message set)
+      auto intermediate_infos_truncated = intermediate_infos;
+      intermediate_infos_truncated.erase(intermediate_infos_truncated.begin());
+
+      // obtain booster messages from all other wallets
+      for (size_t i = 1; i < wallets.size(); ++i)
+      {
+        wallet0_booster_infos[i-1] =
+          wallets[i].get_multisig_key_exchange_booster("", intermediate_infos_truncated, M, wallets.size());
+      }
+    }
+
+    // update other wallets
+    for (size_t i = 1; i < wallets.size(); ++i)
+        new_infos[i] = wallets[i].exchange_multisig_keys("", intermediate_infos);
+
+    intermediate_infos = new_infos;
+    ++rounds_complete;
+    ms_status = wallets[0].get_multisig_status();
+  }
+
+  EXPECT_EQ(rounds_required, rounds_complete);
+
+  check_results(intermediate_infos, wallets, M);
+}
+
 TEST(multisig, make_1_2)
 {
   make_wallets(1, 2, false);
@@ -293,6 +408,12 @@ TEST(multisig, make_2_4)
   make_wallets(2, 4, true);
 }
 
+TEST(multisig, make_2_4_boosting)
+{
+  std::vector<tools::wallet2> wallets(4);
+  make_wallets_boosting(wallets, 2);
+}
+
 TEST(multisig, multisig_kex_msg)
 {
   using namespace multisig;
diff --git a/tests/unit_tests/net.cpp b/tests/unit_tests/net.cpp
index c9099b99b..abdd2558d 100644
--- a/tests/unit_tests/net.cpp
+++ b/tests/unit_tests/net.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 //
 // All rights reserved.
diff --git a/tests/unit_tests/node_server.cpp b/tests/unit_tests/node_server.cpp
index 6f490d7b9..39178884c 100644
--- a/tests/unit_tests/node_server.cpp
+++ b/tests/unit_tests/node_server.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/unit_tests/notify.cpp b/tests/unit_tests/notify.cpp
index c724be8fa..2324809d7 100644
--- a/tests/unit_tests/notify.cpp
+++ b/tests/unit_tests/notify.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 // 
 // All rights reserved.
diff --git a/tests/unit_tests/output_distribution.cpp b/tests/unit_tests/output_distribution.cpp
index 038c19874..9445ac690 100644
--- a/tests/unit_tests/output_distribution.cpp
+++ b/tests/unit_tests/output_distribution.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 // 
 // All rights reserved.
diff --git a/tests/unit_tests/output_selection.cpp b/tests/unit_tests/output_selection.cpp
index ea6745fd6..dd12d07f3 100644
--- a/tests/unit_tests/output_selection.cpp
+++ b/tests/unit_tests/output_selection.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
@@ -236,7 +236,7 @@ TEST(select_outputs, exact_unlock_block)
   const uint64_t exact_block_offsets_stop_exclusive = *(first_block_too_young - 1);
 
   // if too low we may fail by not picking exact block
-  // if too high test is not as senstive as it could be
+  // if too high test is not as sensitive as it could be
   constexpr size_t NUM_PICK_TESTS = 1 << 20;
 
   bool picked_exact_unlock_block = false;
diff --git a/tests/unit_tests/parse_amount.cpp b/tests/unit_tests/parse_amount.cpp
index 37dd23be5..b8ae4c8da 100644
--- a/tests/unit_tests/parse_amount.cpp
+++ b/tests/unit_tests/parse_amount.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/unit_tests/pruning.cpp b/tests/unit_tests/pruning.cpp
index 14700d664..13153f8a1 100644
--- a/tests/unit_tests/pruning.cpp
+++ b/tests/unit_tests/pruning.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 // 
 // All rights reserved.
diff --git a/tests/unit_tests/random.cpp b/tests/unit_tests/random.cpp
index 0d8175445..5c22f8849 100644
--- a/tests/unit_tests/random.cpp
+++ b/tests/unit_tests/random.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 // 
 // All rights reserved.
diff --git a/tests/unit_tests/ringct.cpp b/tests/unit_tests/ringct.cpp
index d7d29fc54..9979fad9a 100644
--- a/tests/unit_tests/ringct.cpp
+++ b/tests/unit_tests/ringct.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/unit_tests/ringdb.cpp b/tests/unit_tests/ringdb.cpp
index d213bde55..cd403f9e2 100644
--- a/tests/unit_tests/ringdb.cpp
+++ b/tests/unit_tests/ringdb.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 // 
 // All rights reserved.
diff --git a/tests/unit_tests/rolling_median.cpp b/tests/unit_tests/rolling_median.cpp
index 363544caa..2a3b8f6a3 100644
--- a/tests/unit_tests/rolling_median.cpp
+++ b/tests/unit_tests/rolling_median.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2023, The Monero Project
+// Copyright (c) 2019-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/unit_tests/rpc_version_str.cpp b/tests/unit_tests/rpc_version_str.cpp
index 6cda8d3fd..a0162ec2f 100644
--- a/tests/unit_tests/rpc_version_str.cpp
+++ b/tests/unit_tests/rpc_version_str.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2023, The Monero Project
+// Copyright (c) 2019-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/unit_tests/scaling_2021.cpp b/tests/unit_tests/scaling_2021.cpp
index 59e036d10..d86ce5a66 100644
--- a/tests/unit_tests/scaling_2021.cpp
+++ b/tests/unit_tests/scaling_2021.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2023, The Monero Project
+// Copyright (c) 2019-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp
index 2af8012ed..9daa44351 100644
--- a/tests/unit_tests/serialization.cpp
+++ b/tests/unit_tests/serialization.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/unit_tests/sha256.cpp b/tests/unit_tests/sha256.cpp
index a2a65dfbf..17b33d1cc 100644
--- a/tests/unit_tests/sha256.cpp
+++ b/tests/unit_tests/sha256.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/unit_tests/slow_memmem.cpp b/tests/unit_tests/slow_memmem.cpp
index 1b86a0e46..4bff792e1 100644
--- a/tests/unit_tests/slow_memmem.cpp
+++ b/tests/unit_tests/slow_memmem.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/unit_tests/subaddress.cpp b/tests/unit_tests/subaddress.cpp
index 1df20e825..b7b09a851 100644
--- a/tests/unit_tests/subaddress.cpp
+++ b/tests/unit_tests/subaddress.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/unit_tests/test_notifier.cpp b/tests/unit_tests/test_notifier.cpp
index bfa7c3a54..0e0b4b421 100644
--- a/tests/unit_tests/test_notifier.cpp
+++ b/tests/unit_tests/test_notifier.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 // 
 // All rights reserved.
diff --git a/tests/unit_tests/test_peerlist.cpp b/tests/unit_tests/test_peerlist.cpp
index c5343b757..b4d050848 100644
--- a/tests/unit_tests/test_peerlist.cpp
+++ b/tests/unit_tests/test_peerlist.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/unit_tests/test_protocol_pack.cpp b/tests/unit_tests/test_protocol_pack.cpp
index f25dc7694..e6a97e6a7 100644
--- a/tests/unit_tests/test_protocol_pack.cpp
+++ b/tests/unit_tests/test_protocol_pack.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/unit_tests/test_tx_utils.cpp b/tests/unit_tests/test_tx_utils.cpp
index 176a9af09..8d622f015 100644
--- a/tests/unit_tests/test_tx_utils.cpp
+++ b/tests/unit_tests/test_tx_utils.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/unit_tests/threadpool.cpp b/tests/unit_tests/threadpool.cpp
index 0c4035f18..d89f16167 100644
--- a/tests/unit_tests/threadpool.cpp
+++ b/tests/unit_tests/threadpool.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 // 
 // All rights reserved.
diff --git a/tests/unit_tests/tx_proof.cpp b/tests/unit_tests/tx_proof.cpp
index 932b38792..b1052334a 100644
--- a/tests/unit_tests/tx_proof.cpp
+++ b/tests/unit_tests/tx_proof.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 // 
 // All rights reserved.
diff --git a/tests/unit_tests/unbound.cpp b/tests/unit_tests/unbound.cpp
index dc888cec3..a3e5afb80 100644
--- a/tests/unit_tests/unbound.cpp
+++ b/tests/unit_tests/unbound.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2016-2023, The Monero Project
+// Copyright (c) 2016-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/unit_tests/unit_tests_utils.h b/tests/unit_tests/unit_tests_utils.h
index e3c6c2521..65da7bf88 100644
--- a/tests/unit_tests/unit_tests_utils.h
+++ b/tests/unit_tests/unit_tests_utils.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/unit_tests/uri.cpp b/tests/unit_tests/uri.cpp
index 98bf80643..f1c2b694b 100644
--- a/tests/unit_tests/uri.cpp
+++ b/tests/unit_tests/uri.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2016-2023, The Monero Project
+// Copyright (c) 2016-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/unit_tests/util.cpp b/tests/unit_tests/util.cpp
index 9285d2000..7128aa8c3 100644
--- a/tests/unit_tests/util.cpp
+++ b/tests/unit_tests/util.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2023-2023, The Monero Project
+// Copyright (c) 2023-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/tests/unit_tests/variant.cpp b/tests/unit_tests/variant.cpp
index d7ded8e4b..21e1f2309 100644
--- a/tests/unit_tests/variant.cpp
+++ b/tests/unit_tests/variant.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2023, The Monero Project
+// Copyright (c) 2023-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/tests/unit_tests/varint.cpp b/tests/unit_tests/varint.cpp
index f0a499d0c..5deac16c1 100644
--- a/tests/unit_tests/varint.cpp
+++ b/tests/unit_tests/varint.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2023, The Monero Project
+// Copyright (c) 2014-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/unit_tests/ver_rct_non_semantics_simple_cached.cpp b/tests/unit_tests/ver_rct_non_semantics_simple_cached.cpp
index 118fb7c48..df542bdc0 100644
--- a/tests/unit_tests/ver_rct_non_semantics_simple_cached.cpp
+++ b/tests/unit_tests/ver_rct_non_semantics_simple_cached.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2023, The Monero Project
+// Copyright (c) 2023-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/tests/unit_tests/vercmp.cpp b/tests/unit_tests/vercmp.cpp
index 7565fbab5..31b55559d 100644
--- a/tests/unit_tests/vercmp.cpp
+++ b/tests/unit_tests/vercmp.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // 
 // All rights reserved.
 // 
diff --git a/tests/unit_tests/wallet_storage.cpp b/tests/unit_tests/wallet_storage.cpp
index c38839a1c..43c5954cd 100644
--- a/tests/unit_tests/wallet_storage.cpp
+++ b/tests/unit_tests/wallet_storage.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2023, The Monero Project
+// Copyright (c) 2023-2024, The Monero Project
 //
 // All rights reserved.
 //
diff --git a/tests/unit_tests/wipeable_string.cpp b/tests/unit_tests/wipeable_string.cpp
index f1bb90a41..65321654a 100644
--- a/tests/unit_tests/wipeable_string.cpp
+++ b/tests/unit_tests/wipeable_string.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2018-2023, The Monero Project
+// Copyright (c) 2018-2024, The Monero Project
 
 // 
 // All rights reserved.
@@ -211,3 +211,15 @@ TEST(wipeable_string, to_hex)
   ASSERT_TRUE(epee::to_hex::wipeable_string(epee::span<const uint8_t>((const uint8_t*)"", 0)) == epee::wipeable_string(""));
   ASSERT_TRUE(epee::to_hex::wipeable_string(epee::span<const uint8_t>((const uint8_t*)"abc", 3)) == epee::wipeable_string("616263"));
 }
+
+TEST(wipeable_string, to_string)
+{
+  // Converting a wipeable_string to a string defeats the purpose of wipeable_string,
+  // but nice to know this works
+  std::string str;
+  {
+    epee::wipeable_string wipeable_str("foo");
+    str = std::string(wipeable_str.data(), wipeable_str.size());
+  }
+  ASSERT_TRUE(str == std::string("foo"));
+}
diff --git a/tests/unit_tests/zmq_rpc.cpp b/tests/unit_tests/zmq_rpc.cpp
index 51b0ed014..240251d1b 100644
--- a/tests/unit_tests/zmq_rpc.cpp
+++ b/tests/unit_tests/zmq_rpc.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2020-2023, The Monero Project
+// Copyright (c) 2020-2024, The Monero Project
 
 // 
 // All rights reserved.
diff --git a/translations/CMakeLists.txt b/translations/CMakeLists.txt
index 8abebf673..77a42981f 100644
--- a/translations/CMakeLists.txt
+++ b/translations/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2017-2023, The Monero Project
+# Copyright (c) 2017-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/translations/generate_translations_header.c b/translations/generate_translations_header.c
index b9ea41988..19a1d69a3 100644
--- a/translations/generate_translations_header.c
+++ b/translations/generate_translations_header.c
@@ -1,5 +1,5 @@
 // Copyright (c) 2013, Sergey Lyubka
-// Copyright (c) 2017-2023, The Monero Project
+// Copyright (c) 2017-2024, The Monero Project
 // All rights reserved.
 // Released under the MIT license.
 
diff --git a/utils/gpg_keys/jeffro256.asc b/utils/gpg_keys/jeffro256.asc
index b99e8dbee..f9e83004a 100644
--- a/utils/gpg_keys/jeffro256.asc
+++ b/utils/gpg_keys/jeffro256.asc
@@ -9,16 +9,16 @@ XU1C9X7Ld9o+ftzzJlSAsKgrUAt8tx8570U90mTojA6Ed26uswqkn4DIZXwPQNW4
 1e7S1flq4250P0DSs62wKdLEjev1MVgxMU3uh1FyuCKLCNY6y46tjCS3AYGKYchF
 QvABmrwVHjrRjnXrPevqwcp+pDGX1w2lm3jsiB94K8MRKzsQSuxy/r6NgSd/uOY7
 IQjFRep5xaln1w8AEQEAAbQiamVmZnJvMjU2IDxqZWZmcm8yNTZAdHV0YW5vdGEu
-Y29tPokB1AQTAQoAPhYhBC6qyTDmuQywGcAegG95eXpuOSRCBQJieG5LAhsDBQkD
-w0FFBQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAAAoJEG95eXpuOSRCrK0L/AoCFk3/
-qES8yraiSZgKuvqgfejZGua93ebaZ1LjNDq3nsQMjqDcpeehFINGvnSObTy6vOu+
-PgJe5gGNbeLhEhcb7HwrESLChoodvIJjbiBRUdOmSQ95rxiXtALTsAHL3QTzOEkO
-Eo3lvt8dSwfW5Tf/Fertzs/LqHPz9zWZNe/xegRd6QbuGO0bG04yi0xlQ428kPUs
-MTZpE3zSpsF5WYseLP3YBbleli0/ijHf79yTf4n8Yv079Fy8qe2+9B+EhUzberCE
-GczBUA9vBZ808/UkX7CqXRYXQQRtWXRcSh8EhzSOfglY8d2I2MBIDZLZO7pPL8Jn
-faeF8o1SBg5xowQe+PQIAn10m9SfRs7VDczKMftK4yDeDkQvswUrC4C+vmUAsD7Q
-yLK+zFucpchl/yje8eotpjnIvHsbPlz0AeEOEmDQzEG5+RUfwXL6Yy04dZSr50IV
-i3G3iIZFW2dSwyhkSmXAVXvGPnK4LdqVbk1+XJaUdqMWnOhoRXLAh/Xil7kBjQRi
+Y29tPokB1AQTAQoAPgIbAwULCQgHAgYVCgkICwIEFgIDAQIeAQIXgBYhBC6qyTDm
+uQywGcAegG95eXpuOSRCBQJmQ6nwBQkFrG8lAAoJEG95eXpuOSRCpf8L/RtjMN9p
+RUSf4IYwGkiP9SbrREoxlpvvqjJlXDlNCaRzSqT1OM2lgxDdzILOBQ2IGf+JucWs
+/wtTwP4RDkna6bqxes++d5CzYRXMaP7a5A9mYWtIUSDI/T9SFMu9RY9k7o0MlXt7
+3rWzDbcqMCFYwajQ9k/zr8q9FaL/MDyRNeKoGlTQ9lPYAUlBWD7fpSo17Y5/JOgv
+AyZdG28XmzH1sWYvSDIjjzbu6ITAPqEZtOUngbKjinbXz0U30HWt/KX+7Z9IffPD
+lyioyxcV9XeALggiRF1GXaPKNKcsqWt2yJZ7rbsDRJX5m0mbqFTnluZtdMv2UKfG
+AjNYJmzZNMal7/73mMXP7H0FBNWisCd2C2uAQd7NLXddkeWwuOGuAJZ2tjial01h
+KDSKZqVmQW01mZFIKhZSNAJnlusx1Qca0I4E1kU6Kn8kh3xVoNzigdIJaHZ+cszi
+9wtCtGRe0fVP/tlx5xLL0wWP7RXGGZwdSyeEQztfzREy1vcsJhig5YZ8XLkBjQRi
 eG5LAQwAxmTy1EccAVpmeQNkmcoaGRgEKLbn8XMiJMB7ETVzIWGQpi2I8vvYfUd3
 16rm9uzYBMFKi+VSIhjaELDH10c0sQkonMSAiSjIYrqJ0yKBMjeGXoPJBCN3vi4e
 lyqnzz6AdJ/qhrPBaXHc3P4///9DD6uphnk5lcYSovZbdqlr/+M6uGrgrZ4aj25Z
@@ -27,15 +27,15 @@ XSVO8w5UezkjX/TqcXIyCkcplJVC45BzNETvusMUDKv1IFbbREzYb3sL6rGI4Euz
 VsR2afuNyxgW/cwwXRQp8SvxWevGkkKj9yuoobeJOwD9ipYNHltkwl5HS0xJcfaG
 gg7O3sRhtEfW44k6ic+pgrDOFleD4dDbPJr+Q6AkCuNoMVw7cVdJCt+Bw3iveC/W
 VYOzkfzl59SdU/PF+HKz1khTVCz8+vNwNMNZZU6OfShQLkzZfGKFl1usEzazIVxe
-KGBneEd3ABEBAAGJAbwEGAEKACYWIQQuqskw5rkMsBnAHoBveXl6bjkkQgUCYnhu
-SwIbDAUJA8NBRQAKCRBveXl6bjkkQiTZDACUroGwPkfAfDf13KBsN97vcQC8/PqT
-jJEqUUo1l6N+rFNok9z8oBR8/kc/uL7QxCVTgdmkdCrCHYbneSxvu4pR8fzpOzIC
-wVRQk2TkXM8HCx/QuuaATCWtD361XR0bfjZVIkH5YW447Su2A8ykIISU76Sz7Kpy
-tjhMHiaKVqgYhlgKP26BAdfiZhJR2ZwKCWhXO3yqGBQ8yQsx1jHZqZdgVSKUrq5a
-7JziXWgOBQyOOMKnXXaS9f0rqkV5QUK0YdI0iSLSaZOb9zM7Y9J7WGTRWcjMYDsq
-ojaAMKcHA5r/3lrEsGBzpWtsL5AdKnF6eghHPlzbh4vYdOQpwIOCM2fYpX2AxNSB
-5B75TzIOXwedK2JPUoNJW2kKvQgS+FAIuNPeui8+qACeQayiRagPYl2Jw6N8QvGu
-aKWDyrHYT83PmGVbVTGkl0gYZVUd9FFmmb8ES1g5BVlVv3d5IZOwMvPa9xstZZG+
-ZeTE2atjLCrJcplAywUoeF0sGp0FmOfgzDY=
-=zEHW
+KGBneEd3ABEBAAGJAbwEGAEKACYCGwwWIQQuqskw5rkMsBnAHoBveXl6bjkkQgUC
+ZkOqBgUJBaxvOwAKCRBveXl6bjkkQt5PDACPD1mZZ3itmmfgGFkJBgK6Op+rIcF0
+10h94BDmxqmh2TBX3uqPYfAwGJ3z++vNxvrAIFC1KpJ5P8cMVlNer27vaPJts8+g
+EPRM7sNyDDs9ejeWxzkrhmYV2XqoUQrsam7PLjYt1I+ubx9cDrhe0/zynfjnMNc5
+Q2TER2NeI+yA2EtcKjXXv54s6a5eRqzH5CuV1Dndwlfx0P4eOWpRhLaIPq9GFLYk
+kfe/BEseSaUX2UxRK9psZpdweMzcx28HY5yvKdZTkYnTv7Fm2a6e2MbPcHYn8D0U
+M77a6kN9z0fqVS8KPiY8vx01gU4M7FBkORp1wm/rGoJQhtx+OxvXaiBG92/UtaIS
+SnNDWynhhSkdstVxE09AuZC1LMcVahAJJ7uikII+frubF2Mjm1rXgBEe9UMaEJVy
+2atN+JaWg8BQRs3rPorlzMBijF6BUn9ZQHwectAGzz6V7rLm964E7O72MOodGoAd
+UWiER2OabVtGaD05n8+T+kjFGBwur1jhbjs=
+=Bo0L
 -----END PGP PUBLIC KEY BLOCK-----
diff --git a/utils/health/build-scripts/clang-build-time-analyzer-clone-build.sh b/utils/health/build-scripts/clang-build-time-analyzer-clone-build.sh
index f218ce5a5..3b4902b24 100755
--- a/utils/health/build-scripts/clang-build-time-analyzer-clone-build.sh
+++ b/utils/health/build-scripts/clang-build-time-analyzer-clone-build.sh
@@ -1,6 +1,6 @@
 #!/bin/bash -e
 
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/utils/health/clang-build-time-analyzer-run.sh b/utils/health/clang-build-time-analyzer-run.sh
index cbfb00f65..9d37a353b 100755
--- a/utils/health/clang-build-time-analyzer-run.sh
+++ b/utils/health/clang-build-time-analyzer-run.sh
@@ -1,6 +1,6 @@
 #!/bin/bash -e
 
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/utils/health/clang-include-what-you-use-run.sh b/utils/health/clang-include-what-you-use-run.sh
index aee10b9a9..5ea19737c 100755
--- a/utils/health/clang-include-what-you-use-run.sh
+++ b/utils/health/clang-include-what-you-use-run.sh
@@ -1,6 +1,6 @@
 #!/bin/bash -e
 
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/utils/health/clang-tidy-run-cc.sh b/utils/health/clang-tidy-run-cc.sh
index b43f575b2..e724d5c0f 100755
--- a/utils/health/clang-tidy-run-cc.sh
+++ b/utils/health/clang-tidy-run-cc.sh
@@ -1,6 +1,6 @@
 #!/bin/bash -e
 
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/utils/health/clang-tidy-run-common.sh b/utils/health/clang-tidy-run-common.sh
index fc34b7fe9..e7724843b 100755
--- a/utils/health/clang-tidy-run-common.sh
+++ b/utils/health/clang-tidy-run-common.sh
@@ -1,6 +1,6 @@
 #!/bin/bash -e
 
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/utils/health/clang-tidy-run-cpp.sh b/utils/health/clang-tidy-run-cpp.sh
index e8df05958..615a2abc6 100755
--- a/utils/health/clang-tidy-run-cpp.sh
+++ b/utils/health/clang-tidy-run-cpp.sh
@@ -1,6 +1,6 @@
 #!/bin/bash -e
 
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/utils/health/valgrind-tests.sh b/utils/health/valgrind-tests.sh
index 3248c8b91..ebfc32135 100755
--- a/utils/health/valgrind-tests.sh
+++ b/utils/health/valgrind-tests.sh
@@ -1,6 +1,6 @@
 #!/bin/bash -e
 
-# Copyright (c) 2014-2023, The Monero Project
+# Copyright (c) 2014-2024, The Monero Project
 #
 # All rights reserved.
 #
diff --git a/utils/python-rpc/framework/daemon.py b/utils/python-rpc/framework/daemon.py
index 4ac24332d..917e68c8e 100644
--- a/utils/python-rpc/framework/daemon.py
+++ b/utils/python-rpc/framework/daemon.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2018-2023, The Monero Project
+# Copyright (c) 2018-2024, The Monero Project
 
 # 
 # All rights reserved.
diff --git a/utils/python-rpc/framework/rpc.py b/utils/python-rpc/framework/rpc.py
index 567bdd78d..9eac53416 100644
--- a/utils/python-rpc/framework/rpc.py
+++ b/utils/python-rpc/framework/rpc.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2018-2023, The Monero Project
+# Copyright (c) 2018-2024, The Monero Project
 
 # 
 # All rights reserved.
diff --git a/utils/python-rpc/framework/wallet.py b/utils/python-rpc/framework/wallet.py
index 8fa3eaafd..0753b4fd4 100644
--- a/utils/python-rpc/framework/wallet.py
+++ b/utils/python-rpc/framework/wallet.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2018-2023, The Monero Project
+# Copyright (c) 2018-2024, The Monero Project
 
 # 
 # All rights reserved.
@@ -540,6 +540,20 @@ class Wallet(object):
         }
         return self.rpc.send_json_rpc_request(exchange_multisig_keys)
 
+    def get_multisig_key_exchange_booster(self, multisig_info, threshold, num_signers, password = ''):
+        exchange_multisig_keys = {
+            'method': 'get_multisig_key_exchange_booster',
+            'params' : {
+                'multisig_info': multisig_info,
+                'threshold': threshold,
+                'num_signers': num_signers,
+                'password': password,
+            },
+            'jsonrpc': '2.0', 
+            'id': '0'
+        }
+        return self.rpc.send_json_rpc_request(get_multisig_key_exchange_booster)
+
     def export_multisig_info(self):
         export_multisig_info = {
             'method': 'export_multisig_info',
@@ -1139,3 +1153,45 @@ class Wallet(object):
             'id': '0'
         }
         return self.rpc.send_json_rpc_request(frozen)
+
+    class BackgroundSyncOptions(object):
+        def __init__(self):
+            self.off = 'off'
+            self.reuse_password = 'reuse-wallet-password'
+            self.custom_password = 'custom-background-password'
+    background_sync_options = BackgroundSyncOptions()
+
+    def setup_background_sync(self, background_sync_type = background_sync_options.off, wallet_password = '', background_cache_password = ''):
+        setup_background_sync = {
+            'method': 'setup_background_sync',
+            'jsonrpc': '2.0',
+            'params' : {
+                'background_sync_type': background_sync_type,
+                'wallet_password': wallet_password,
+                'background_cache_password': background_cache_password,
+            },
+            'id': '0'
+        }
+        return self.rpc.send_json_rpc_request(setup_background_sync)
+
+    def start_background_sync(self):
+        start_background_sync = {
+            'method': 'start_background_sync',
+            'jsonrpc': '2.0',
+            'params' : {},
+            'id': '0'
+        }
+        return self.rpc.send_json_rpc_request(start_background_sync)
+
+    def stop_background_sync(self, wallet_password = '', seed = '', seed_offset = ''):
+        stop_background_sync = {
+            'method': 'stop_background_sync',
+            'jsonrpc': '2.0',
+            'params' : {
+                'wallet_password': wallet_password,
+                'seed': seed,
+                'seed_offset': seed_offset,
+            },
+            'id': '0'
+        }
+        return self.rpc.send_json_rpc_request(stop_background_sync)
diff --git a/utils/python-rpc/framework/zmq.py b/utils/python-rpc/framework/zmq.py
index 99d7be332..66adf35af 100644
--- a/utils/python-rpc/framework/zmq.py
+++ b/utils/python-rpc/framework/zmq.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2018-2023, The Monero Project
+# Copyright (c) 2018-2024, The Monero Project
 
 # 
 # All rights reserved.
diff --git a/utils/systemd/monerod.service b/utils/systemd/monerod.service
index 63daefa82..28b457d56 100644
--- a/utils/systemd/monerod.service
+++ b/utils/systemd/monerod.service
@@ -1,6 +1,6 @@
 [Unit]
 Description=Monero Full Node
-After=network.target
+After=network-online.target
 
 [Service]
 User=monero