Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

fix: mitigate crashes associated with some upgradetohd edge cases #6116

Merged
merged 3 commits into from
Jul 19, 2024

Conversation

kwvg
Copy link
Collaborator

@kwvg kwvg commented Jul 15, 2024

Motivation

When filming demo footage for #6093, I realized that if I tried to create an encrypted blank legacy wallet and run upgradetohd [mnemonic], the client would crash.

dash@b9c6631a824d:/src/dash$ ./src/qt/dash-qt
QStandardPaths: XDG_RUNTIME_DIR not set, defaulting to '/tmp/runtime-dash'
dash-qt: wallet/scriptpubkeyman.cpp:399: void LegacyScriptPubKeyMan::GenerateNewCryptedHDChain(const SecureString &, const SecureString &, CKeyingMaterial): Assertion `res' failed.
Posix Signal: Aborted
No debug information available for stacktrace. You should add debug information and then run:
dash-qt -printcrashinfo=bvcgc43iinzgc43ijfxgm3ybaadwiyltnawxc5avkbxxg2lyebjwsz3omfwduicbmjxxe5dfmqaaa===

The expected set of operations when performing privileged operations is to first use walletpassphrase [passphrase] [time] to unlock the wallet and then perform the privileged operation. This routine that applies for almost all privileged RPCs doesn't apply here, the unlock state of the wallet has no bearing on constructing an encrypted HD chain as it needs to be encrypted with the master key stored in the wallet, which in turn is encrypted with a key derived from the passphrase (i.e., upgradetohd imports always need the passphrase, if encrypted).

You might have noticed that I used upgradetohd [mnemonic] instead of the correct syntax, upgradetohd [mnemonic] "" [passphrase] that is supposed to be used when supplying a mnemonic to an encrypted wallet, because when you run the former, you don't get told to enter the passphrase into the RPC command, you're told.

Error: Please enter the wallet passphrase with walletpassphrase first.

Which tells you to treat it like any other routine privileged operation and follow the routine as mentioned above. This is where insufficient validation starts rearing its head, we only validate the passphrase if we're supplied one even though we should be demanding one if the wallet is encrypted and it isn't supplied. We didn't supply a passphrase because we're following the normal routine, we unlocked the wallet so EnsureWalletIsUnlocked() is happy, so now the following happens.

upgradetohd()
  | Insufficient validation has allowed us to supply a blank passphrase
  | for an encrypted wallet
  |- CWallet::UpgradeToHD()
    |- CWallet::GenerateNewHDChainEncrypted()
     | We get our hands on vMasterKey by generating the key from our passphrase
     | and using it to unlock vCryptedMasterKey. 
     | 
     | There's one small problem, we don't know if the output of CCrypter::Decrypt
     | isn't just gibberish. Since we don't have a passphrase, whatever came from
     | CCrypter::SetKeyFromPassphrase isn't the decryption key, meaning, the 
     | vMasterKey we just got is gibberish
     |- LegacyScriptPubKeyMan::GenerateNewCryptedHDChain()
       |- res = LegacyScriptPubKeyMan::EncryptHDChain()
       | |- EncryptSecret()
       |   |- CCrypter::SetKey()
       |      This is where everything unravels, the gibberish key's size doesn't
       |      match WALLET_CRYPTO_KEY_SIZE, it's no good for encryption. We bail out.
       |- assert(res)
          We assume are inputs are safe so there's no real reason we should crash.
          Except our inputs aren't safe, so we crash. Welp! :c

This problem has existed for a while but didn't cause the client to crash, in v20.1.1 (1951298), trying to do the same thing would return you a vague error

Failed to generate encrypted HD wallet (code -4)

In the process of working on mitigating this crash, another edge case was discovered, where if the wallet was unlocked and an incorrect passphrase was provided to upgradetohd, the user would not receive any feedback that they entered the wrong passphrase and the client would similarly crash.

upgradetohd()
 | We've been supplied a passphrase, so we can try and validate it by
 | trying to unlock the wallet with it. If it fails, we know we got the
 | wrong passphrase.
 |- CWallet::Unlock()
 | | Before we bother unlocking the wallet, we should check if we're
 | | already unlocked, if we are, we can just say "unlock successful".
 | |- CWallet::IsLocked()
 | |  Wallet is indeed unlocked.
 | |- return true; 
 | The validation method we just tried to use has a bail-out mechanism
 | that we don't account for, the "unlock" succeded so I guess we have the
 | right passphrase.
 [...] (continue call chain as mentioned earlier)
       |- assert(res)
          Oh...

This pull request aims to resolve crashes caused by the above two edge cases.

Additional Information

As this PR was required me to add additional guardrails on GenerateNewCryptedHDChain() and GenerateNewHDChainEncrypted(), it was taken as an opportunity to resolve a TODO (source). The following mitigations have been implemented.

  • Validating vMasterKey size (any key not of WALLET_CRYPTO_KEY_SIZE size cannot be used for encryption and so, cannot be a valid key)
  • Validating secureWalletPassphrase's presence to catch attempts at passing a blank value (an encrypted wallet cannot have a blank passphrase)
  • Using Unlock() to validate the correctness of vMasterKey. (the two other instances of iterating through mapMasterKeys use Unlock(), see here and here)
    • Lock()'ing the wallet before Unlock()'ing the wallet to avoid the IsLocked() bail-out condition and then restoring to the previous lock state afterwards.
  • Add an IsCrypted() check to see if upgradetohd's walletpassphrase is allowed to be empty.

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have made corresponding changes to the documentation (note: N/A)
  • I have assigned this pull request to a milestone (for repository code-owners and collaborators only)

@kwvg kwvg changed the title Fix uhd crash fix: mitigate crashes associated with some upgradetohd edge cases Jul 15, 2024
@kwvg kwvg added this to the 21.1 milestone Jul 15, 2024
assert_raises_rpc_error(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.", node.upgradetohd, mnemonic)
assert_raises_rpc_error(-14, "The wallet passphrase entered was incorrect", node.upgradetohd, mnemonic, "", "wrongpass")
assert_raises_rpc_error(-4, "Wallet encrypted but passphrase not supplied. Please refer to \"help upgradetohd\" for guidance.", node.upgradetohd, mnemonic)
assert_raises_rpc_error(-1, "GenerateNewHDChain: supplied incorrect passphrase", node.upgradetohd, mnemonic, "", "wrongpass")
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think you should not remove the check EnsureWalletIsUnlocked

The error message GenerateNewHDChain: supplied incorrect passphrase is less friendly than The wallet passphrase entered was incorrect

From the user's perspective, he has no idea what is GenerateNewHDChain

Copy link
Collaborator

Choose a reason for hiding this comment

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

also all over all RPC the check EnsureWalletIsUnlocked is a general-use-practice, that is used for all other rpc which requires wallet to be unlocked, and don't really care how exactly wallet is unlocked - because it is a wrong passphrase or because it is not provided

Copy link
Collaborator Author

@kwvg kwvg Jul 15, 2024

Choose a reason for hiding this comment

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

EnsureWalletIsUnlocked() doesn't tell if you have the wrong passphrase (source), it tells you to use walletpassphrase (the RPC) if your wallet isn't unlocked.

As I mentioned in my PR description, that reenforces the idea that this is just like any other privileged command, unlocking your wallet let you perform the privileged operation, when this isn't the case. Having an unlocked wallet won't give us the passphrase needed so the unlock state doesn't matter. Under the hood, we're locking-unlocking the wallet anyways.

The incorrect passphrase error comes from within upgradetohd() (source) and I had thought about doing the passphrase validation in upgradetohd to keep around the friendlier error message and retain the error code but that would require doing the Lock()-Unlock() routine, only for GenerateNewHDChain() to do it again (and I'd prefer to keep it in GenerateNewHDChain() than only in upgradetohd()).

We could make the message more friendly by changing the text in GenerateNewHDChain() itself but that would mean bucking with error message convention for exceptions thrown from non-RPC function bodies of "[function name]: [error message]", thoughts?

EDIT: Exception message has been changed to mimic the error message that comes with RPC_WALLET_PASSPHRASE_INCORRECT

src/wallet/rpcwallet.cpp Outdated Show resolved Hide resolved
@kwvg kwvg requested a review from knst July 15, 2024 21:22
@kwvg kwvg marked this pull request as ready for review July 15, 2024 22:57
Copy link
Member

@PastaPastaPasta PastaPastaPasta left a comment

Choose a reason for hiding this comment

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

Generally LGTM

Copy link
Collaborator

@thephez thephez left a comment

Choose a reason for hiding this comment

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

utACK 👍

src/wallet/scriptpubkeyman.cpp Outdated Show resolved Hide resolved
Copy link
Collaborator

@knst knst left a comment

Choose a reason for hiding this comment

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

one more comment about exception error - Otherwise PR LGTM

@@ -5683,7 +5683,9 @@ bool CWallet::GenerateNewHDChain(const SecureString& secureMnemonic, const Secur

// We got a gibberish key...
if (vMasterKey.empty()) {
throw std::runtime_error(strprintf("%s: supplied incorrect passphrase", __func__));
// Mimicking the error message of RPC_WALLET_PASSPHRASE_INCORRECT as it's possible
Copy link
Collaborator

Choose a reason for hiding this comment

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

you can actually write try/catch inside rpc code and if the exception - is 'passphrase is incorrect' - re-throw it as an RPCError.

I think that would be the best from the rpc's user point of view.

Copy link
Collaborator Author

@kwvg kwvg Jul 17, 2024

Choose a reason for hiding this comment

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

Seems a bit convoluted, it'll help us retain the right error code but has catching-processing-rethrowing been done anywhere else?

Copy link
Collaborator

Choose a reason for hiding this comment

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

what's about:

+try {
      if (!GenerateNewHDChain(secureMnemonic, secureMnemonicPassphrase, secureWalletPassphrase)) {
        error = Untranslated("Failed to generate HD wallet");
        return false;
+ } catch (const std::runtime_error& e) {
+    error = Unstransalted(sprintf("Generatin new chain is failed due to : %s", e.what());
+    return false;
+}

(that's not working code, just a concept)

Copy link
Collaborator Author

@kwvg kwvg Jul 17, 2024

Choose a reason for hiding this comment

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

This makes the user-facing error look like "Generating new chain failed due to: GenerateNewHDChain: supplied incorrect passphrase" (assuming we restore the pre-RPC_WALLET_PASSPHRASE_INCORRECT error message).

If trying to ensure the incorrect passphrase error has the correct error code, it'd look a bit like this

    bool wallet_upgraded{false};
    try {
        wallet_upgraded = pwallet->UpgradeToHD(secureMnemonic, secureMnemonicPassphrase, secureWalletPassphrase, error);
    } catch (const std::runtime_error& e) {
        if (e.what() == std::string("supplied incorrect passphrase")) {
            throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect");
        }
        // No more exceptions to translate, rethrow them
        throw std::runtime_error(e.what());
    } // Let other exceptions remain unhandled by us

But this feels like excessive effort when the alternative (just mirroring the error message we expect from RPC_WALLET_PASSPHRASE_INCORRECT) seems fine.

kwvg added 3 commits July 17, 2024 16:31
most of the steps are overlapping except for the encryption sections,
which we can make contingent on supplying of the keying material. `res`
isn't used anywhere since we plan on hard crashing if they fail anyway,
so if we got that far, we've already succeeded.

we will validate vMasterKey's size before starting the encryption
routine to prevent a value outside of our control from hard-crashing
the client.
additionally, let's do the lock-unlock validation test *before* trying
to generate a new chain. as a bonus, it lets us make sure that we
encrypt the HD chain using a key that's actually capable of unlocking
the wallet.

also, `upgradetohd` never checked if the passphrase was handed to us
(which matters in encrypted blank wallets) before calling UpgradeToHD,
so let's check it for them.
earlier it was possible to make it all the way to `EncryptSecret`
without actually having the passphrase in hand until being told off
by `CCrypter::SetKey`, we should avoid that.

also, let's get rid of checks that `UpgradeToHD` is now taking
responsibility for. no point in checking if the wallet is unlocked
as it has no bearing on your ability to upgrade the wallet.
Copy link
Collaborator

@knst knst left a comment

Choose a reason for hiding this comment

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

utACK 69c37f4

Copy link

@UdjinM6 UdjinM6 left a comment

Choose a reason for hiding this comment

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

utACK 69c37f4

Copy link
Member

@PastaPastaPasta PastaPastaPasta left a comment

Choose a reason for hiding this comment

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

utACK 69c37f4

@PastaPastaPasta PastaPastaPasta merged commit f5bf5ce into dashpay:develop Jul 19, 2024
5 of 12 checks passed
PastaPastaPasta added a commit that referenced this pull request Jul 20, 2024
…side of `assert(...)`

121c032 fix: avoid calling functions that change wallet state inside of `assert(...)` (UdjinM6)

Pull request description:

  ## Issue being fixed or feature implemented
  Functions that change the state should not be called inside `assert`s

  kudos to @kwvg for noticing #6116 (comment)

  ## What was done?
  Move them out

  ## How Has This Been Tested?

  ## Breaking Changes
  n/a

  ## Checklist:
  - [x] I have performed a self-review of my own code
  - [ ] I have commented my code, particularly in hard-to-understand areas
  - [ ] I have added or updated relevant unit/integration/functional/e2e tests
  - [ ] I have made corresponding changes to the documentation
  - [ ] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_

ACKs for top commit:
  kwvg:
    LGTM, utACK 121c032
  PastaPastaPasta:
    utACK 121c032

Tree-SHA512: 42b6f55a62b05e3e632febf92f9d5ac63f32a7754fdd9dffa816d71cb0f72a32ac8315fba5ed96d8bc652728e768f2eb399e5ebb4aa39afe1546fa16e1046347
PastaPastaPasta added a commit to PastaPastaPasta/dash that referenced this pull request Jul 23, 2024
…etohd edge cases

69c37f4 rpc: make sure `upgradetohd` always has the passphrase for `UpgradeToHD` (Kittywhiskers Van Gogh)
619b640 wallet: unify HD chain generation in CWallet (Kittywhiskers Van Gogh)
163d318 wallet: unify HD chain generation in LegacyScriptPubKeyMan (Kittywhiskers Van Gogh)

Pull request description:

  ## Motivation

  When filming demo footage for dashpay#6093, I realized that if I tried to create an encrypted blank legacy wallet and run `upgradetohd [mnemonic]`, the client would crash.

  ```
  dash@b9c6631a824d:/src/dash$ ./src/qt/dash-qt
  QStandardPaths: XDG_RUNTIME_DIR not set, defaulting to '/tmp/runtime-dash'
  dash-qt: wallet/scriptpubkeyman.cpp:399: void LegacyScriptPubKeyMan::GenerateNewCryptedHDChain(const SecureString &, const SecureString &, CKeyingMaterial): Assertion `res' failed.
  Posix Signal: Aborted
  No debug information available for stacktrace. You should add debug information and then run:
  dash-qt -printcrashinfo=bvcgc43iinzgc43ijfxgm3ybaadwiyltnawxc5avkbxxg2lyebjwsz3omfwduicbmjxxe5dfmqaaa===
  ```

  The expected set of operations when performing privileged operations is to first use `walletpassphrase [passphrase] [time]` to unlock the wallet and then perform the privileged operation. This routine that applies for almost all privileged RPCs doesn't apply here, the unlock state of the wallet has no bearing on constructing an encrypted HD chain as it needs to be encrypted with the master key stored in the wallet, which in turn is encrypted with a key derived from the passphrase (i.e., `upgradetohd` imports **always** need the passphrase, if encrypted).

  You might have noticed that I used `upgradetohd [mnemonic]` instead of the correct syntax, `upgradetohd [mnemonic] "" [passphrase]` that is supposed to be used when supplying a mnemonic to an encrypted wallet, because when you run the former, you don't get told to enter the passphrase into the RPC command, you're told.

  ```
  Error: Please enter the wallet passphrase with walletpassphrase first.
  ```

  Which tells you to treat it like any other routine privileged operation and follow the routine as mentioned above. This is where insufficient validation starts rearing its head, we only validate the passphrase if we're supplied one even though we should be demanding one if the wallet is encrypted and it isn't supplied. We didn't supply a passphrase because we're following the normal routine, we unlocked the wallet so `EnsureWalletIsUnlocked()` is happy, so now the following happens.

  ```
  upgradetohd()
    | Insufficient validation has allowed us to supply a blank passphrase
    | for an encrypted wallet
    |- CWallet::UpgradeToHD()
      |- CWallet::GenerateNewHDChainEncrypted()
       | We get our hands on vMasterKey by generating the key from our passphrase
       | and using it to unlock vCryptedMasterKey.
       |
       | There's one small problem, we don't know if the output of CCrypter::Decrypt
       | isn't just gibberish. Since we don't have a passphrase, whatever came from
       | CCrypter::SetKeyFromPassphrase isn't the decryption key, meaning, the
       | vMasterKey we just got is gibberish
       |- LegacyScriptPubKeyMan::GenerateNewCryptedHDChain()
         |- res = LegacyScriptPubKeyMan::EncryptHDChain()
         | |- EncryptSecret()
         |   |- CCrypter::SetKey()
         |      This is where everything unravels, the gibberish key's size doesn't
         |      match WALLET_CRYPTO_KEY_SIZE, it's no good for encryption. We bail out.
         |- assert(res)
            We assume are inputs are safe so there's no real reason we should crash.
            Except our inputs aren't safe, so we crash. Welp! :c
  ```

  This problem has existed for a while but didn't cause the client to crash, in v20.1.1 (1951298), trying to do the same thing would return you a vague error

  ```
  Failed to generate encrypted HD wallet (code -4)
  ```

  In the process of working on mitigating this crash, another edge case was discovered, where if the wallet was unlocked and an incorrect passphrase was provided to `upgradetohd`, the user would not receive any feedback that they entered the wrong passphrase and the client would similarly crash.

  ```
  upgradetohd()
   | We've been supplied a passphrase, so we can try and validate it by
   | trying to unlock the wallet with it. If it fails, we know we got the
   | wrong passphrase.
   |- CWallet::Unlock()
   | | Before we bother unlocking the wallet, we should check if we're
   | | already unlocked, if we are, we can just say "unlock successful".
   | |- CWallet::IsLocked()
   | |  Wallet is indeed unlocked.
   | |- return true;
   | The validation method we just tried to use has a bail-out mechanism
   | that we don't account for, the "unlock" succeded so I guess we have the
   | right passphrase.
   [...] (continue call chain as mentioned earlier)
         |- assert(res)
            Oh...
  ```

  This pull request aims to resolve crashes caused by the above two edge cases.

  ## Additional Information

  As this PR was required me to add additional guardrails on `GenerateNewCryptedHDChain()` and `GenerateNewHDChainEncrypted()`, it was taken as an opportunity to resolve a TODO ([source](https://github.com/dashpay/dash/blob/9456d0761d8883cc293dffba11dacded517b5f8f/src/wallet/wallet.cpp#L5028-L5038)). The following mitigations have been implemented.

  * Validating `vMasterKey` size (any key not of `WALLET_CRYPTO_KEY_SIZE` size cannot be used for encryption and so, cannot be a valid key)
  * Validating `secureWalletPassphrase`'s presence to catch attempts at passing a blank value (an encrypted wallet cannot have a blank passphrase)
  * Using `Unlock()` to validate the correctness of `vMasterKey`. (the two other instances of iterating through `mapMasterKeys` use `Unlock()`, see [here](https://github.com/dashpay/dash/blob/1394c41c8d0afb8370726488a2888be30d238148/src/wallet/wallet.cpp#L5498-L5500) and [here](https://github.com/dashpay/dash/blob/1394c41c8d0afb8370726488a2888be30d238148/src/wallet/wallet.cpp#L429-L431))
    * `Lock()`'ing the wallet before `Unlock()`'ing the wallet to avoid the `IsLocked()` bail-out condition and then restoring to the previous lock state afterwards.
  * Add an `IsCrypted()` check to see if `upgradetohd`'s `walletpassphrase` is allowed to be empty.

  ## Checklist:

  - [x] I have performed a self-review of my own code
  - [x] I have commented my code, particularly in hard-to-understand areas
  - [x] I have added or updated relevant unit/integration/functional/e2e tests
  - [x] I have made corresponding changes to the documentation **(note: N/A)**
  - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_

ACKs for top commit:
  knst:
    utACK 69c37f4
  UdjinM6:
    utACK 69c37f4
  PastaPastaPasta:
    utACK 69c37f4

Tree-SHA512: 4bda1f7155511447d6672bbaa22b909f5e2fc7efd1fd8ae1c61e0cdbbf3f6c28f6e8c1a8fe2a270fdedff7279322c93bf0f8e01890aff556fb17288ef6907b3e
@UdjinM6 UdjinM6 modified the milestones: 21.1, 21 Jul 24, 2024
PastaPastaPasta added a commit that referenced this pull request Jul 25, 2024
cd0a3a6 Merge #6154: chore: remove trailing whitespaces in release notes (pasta)
6bc60a7 Merge #6151: chore: update seeds for v21 release (pasta)
88e949a Merge #6146: chore: bump assumevalid, minchainwork, checkpoints, chaintxdata (pasta)
cc14427 Merge #6144: docs: release notes for v21.0.0 (pasta)
0a8ece1 Merge #6122: chore: translations 2024-07 (pasta)
146d244 Merge #6140: feat: harden all sporks on mainnet to current values (pasta)
024d272 Merge #6126: feat: enable EHF activation of MN_RR on mainnet (pasta)
e780b3d Merge #6125: docs: update manpages for 21.0 (pasta)
5ede23c Merge #6118: docs: add release notes notifying change of default branch to `develop` (pasta)
1b6fe9c Merge #6117: docs: update supported versions in SECURITY.md (pasta)
27d20be Merge #6116: fix: mitigate crashes associated with some upgradetohd edge cases (pasta)

Pull request description:

  ## Issue being fixed or feature implemented
  Backports to v21 for rc.3; there are more PRs to be back ported here

  ## What was done?
  see commits

  ## How Has This Been Tested?

  ## Breaking Changes
  None

  ## Checklist:
    _Go over all the following points, and put an `x` in all the boxes that apply._
  - [x] I have performed a self-review of my own code
  - [ ] I have commented my code, particularly in hard-to-understand areas
  - [ ] I have added or updated relevant unit/integration/functional/e2e tests
  - [x] I have made corresponding changes to the documentation
  - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_

ACKs for top commit:
  UdjinM6:
    utACK cd0a3a6

Tree-SHA512: 0a8d027c5952fcb61317cc413a0de1f3152ae675c5690942400c3e4655cc6a803c702a7e1b18d8dac77aa0f483783f5cdbe29d6c0592165c4f0517d6f37b91a4
PastaPastaPasta added a commit that referenced this pull request Jul 29, 2024
98a3393 chore: set release to true (pasta)
cd0a3a6 Merge #6154: chore: remove trailing whitespaces in release notes (pasta)
6bc60a7 Merge #6151: chore: update seeds for v21 release (pasta)
88e949a Merge #6146: chore: bump assumevalid, minchainwork, checkpoints, chaintxdata (pasta)
cc14427 Merge #6144: docs: release notes for v21.0.0 (pasta)
0a8ece1 Merge #6122: chore: translations 2024-07 (pasta)
146d244 Merge #6140: feat: harden all sporks on mainnet to current values (pasta)
024d272 Merge #6126: feat: enable EHF activation of MN_RR on mainnet (pasta)
e780b3d Merge #6125: docs: update manpages for 21.0 (pasta)
5ede23c Merge #6118: docs: add release notes notifying change of default branch to `develop` (pasta)
1b6fe9c Merge #6117: docs: update supported versions in SECURITY.md (pasta)
27d20be Merge #6116: fix: mitigate crashes associated with some upgradetohd edge cases (pasta)
db82817 Merge #6106: feat: create new composite quorum-command platformsign (pasta)
a45e6df Merge #6104: fix: adjust incorrect parameter description that says there is a default that doesn't exist (pasta)
7330982 Merge #6100: feat: make whitelist works with composite commands for platform needs (pasta)
9998ffd Merge #6096: feat: split type of error in submitchainlock - return enum in CL verifying code (pasta)
cdf7a25 Merge #6095: fix: createwallet to require 'load_on_startup' for descriptor wallets (pasta)
c1c2c55 Merge #6092: fix: mixing for partially unlocked descriptor wallets (pasta)
1175486 Merge #6073: feat: add logging for RPC HTTP requests: command, user, http-code, time of running (pasta)

Pull request description:

  ## Issue being fixed or feature implemented
  Suppressed changes from be83865 so the diff is empty.

  ## What was done?

  ## How Has This Been Tested?

  ## Breaking Changes

  ## Checklist:
  - [x] I have performed a self-review of my own code
  - [ ] I have commented my code, particularly in hard-to-understand areas
  - [ ] I have added or updated relevant unit/integration/functional/e2e tests
  - [ ] I have made corresponding changes to the documentation
  - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_

ACKs for top commit:
  PastaPastaPasta:
    ACK 158cf86; no diff
  knst:
    ACK 158cf86

Tree-SHA512: 3310a39fbcb45bdf09f885fe77ba769c0a715869a3bb287eaf0f2cf54b35a7e1f832c88df3bd31097eabf2d375515c1b87ff05e0c3282cef642833a154c42bbe
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants