upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/gui.cpp
diff options
context:
space:
mode:
authorBitflash <btf01@proton.me>2026-08-03 10:33:07 -0300
committerGitHub <noreply@github.com>2026-08-03 10:33:07 -0300
commit5e37647023463cec4331dc48dc360ba60b22c888 (patch)
tree5969cf8ad06ffbd9bd9d0a35cf60149774cf8a57 /src/gui.cpp
parent4b2416b7315d76a2abd1a0ac0b72b3d1ef0ff6c6 (diff)
gui: read the Finish condition once, before the button that clears it (#118)
Creating a recovery phrase from the window aborted with an ImGui assertion on the success path: Expression: (g.DisabledStackSize > 0) && "Calling EndDisabled() too many times!" File: imgui/imgui.cpp Line: 8435 The Finish button is wrapped in BeginDisabled/EndDisabled guarded by !g_phraseWrittenDown, and its own click handler sets g_phraseWrittenDown to false. So the pair was opened while the checkbox was ticked and closed after the handler had unticked it: BeginDisabled was never called, EndDisabled was, and ImGui asserted on the unmatched close. It fires on the only path that installs a seed -- tick the box, press Finish -- so every phrase created from the window has hit it since the feature shipped in 1.2.12. Three releases carried it because nothing tests the window. Reproduced on the published 1.2.14 binary and confirmed fixed on this build, driving the real window: Wallet Safety -> Create recovery phrase -> tick -> Finish. The old build raises the assertion; the fixed build closes the dialog, reports "This wallet has a recovery phrase", and the address changes to a derived one. The seed itself was already written before the assertion, so the wallet was never left half-installed -- and ImGui's IM_ASSERT_USER_ERROR_RET returns before touching its state, so answering Ignore was safe. Abort was not: it kills the process with no DBFlush. The restore dialog next door already does this correctly, copying the condition into a local first. This makes the create dialog match.
Diffstat (limited to 'src/gui.cpp')
-rw-r--r--src/gui.cpp12
1 files changed, 10 insertions, 2 deletions
diff --git a/src/gui.cpp b/src/gui.cpp
index e800244..e3a8499 100644
--- a/src/gui.cpp
+++ b/src/gui.cpp
@@ -1005,7 +1005,15 @@ static void DrawCreatePhraseDialog()
1005 ImGui::Checkbox("I have written these words down", &g_phraseWrittenDown); 1005 ImGui::Checkbox("I have written these words down", &g_phraseWrittenDown);
1006 ImGui::Spacing(); 1006 ImGui::Spacing();
1007 1007
1008 if (!g_phraseWrittenDown) 1008 // Read the condition once, before the button that changes it.
1009 //
1010 // Pressing Finish clears g_phraseWrittenDown, so testing it again after
1011 // the click asked BeginDisabled and EndDisabled two different questions:
1012 // the pair was opened while the checkbox was ticked and closed after the
1013 // handler had unticked it. ImGui asserts on the unmatched EndDisabled --
1014 // on the success path, so every phrase created from the window hit it.
1015 const bool fFinishDisabled = !g_phraseWrittenDown;
1016 if (fFinishDisabled)
1009 ImGui::BeginDisabled(); 1017 ImGui::BeginDisabled();
1010 if (ImGui::Button("Finish", ImVec2(120.0f, 0.0f))) 1018 if (ImGui::Button("Finish", ImVec2(120.0f, 0.0f)))
1011 { 1019 {
@@ -1024,7 +1032,7 @@ static void DrawCreatePhraseDialog()
1024 g_showCreatePhrase = false; 1032 g_showCreatePhrase = false;
1025 g_needRefresh = true; 1033 g_needRefresh = true;
1026 } 1034 }
1027 if (!g_phraseWrittenDown) 1035 if (fFinishDisabled)
1028 ImGui::EndDisabled(); 1036 ImGui::EndDisabled();
1029 1037
1030 ImGui::SameLine(); 1038 ImGui::SameLine();