core: wallet review fixes, and show the derived addresses

Concurrency: the account index is incremented in SQL and read back in the
same transaction, so two profiles cannot be handed the same key. Importing
a phrase is one transaction and single_seed is UNIQUE, so a phrase cannot
be discarded in favour of a key created meanwhile, and a device cannot end
up with two keys.

Wallet commands are no longer forwarded to a remote host: the recovery
phrase must not leave the device, and the raw command is logged there.

/wallet delete removes the key, confirmed by the last word of the phrase,
so creating a key before importing your own is no longer a dead end.

/wallet now shows every profile on the key with the first two name
addresses each, to check derivation against other wallets. Hidden profiles
are left out, as they are by /users. A bad phrase no longer says which word
was wrong. /wallet export uses the profile's own key.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX
This commit is contained in:
Alain BrenzikoferandClaude Opus 5 committed 2026-09-09 09:05:47 +00:00
1 parent 257cdc02c8
commit ee11b368b6
13 files changed
+232 -152

No files matched your search

+7 -1
View File
@@ -421,6 +421,7 @@ data ChatCommand
| APIWalletCreate
| APIWalletImport {recoveryPhrase :: Text}
| APIWalletExport
| APIWalletDelete {confirmWord :: Text}
| APISendCallInvitation ContactId CallType
| SendCallInvitation ContactName CallType
| APIRejectCall ContactId
@@ -746,6 +747,11 @@ allowRemoteCommand = \case
DeleteRemoteCtrl _ -> False
ExecChatStoreSQL _ -> False
ExecAgentStoreSQL _ -> False
APIWallet -> False
APIWalletCreate -> False
APIWalletImport _ -> False
APIWalletExport -> False
APIWalletDelete _ -> False
_ -> True
data RelayConnectionResult = RelayConnectionResult
@@ -847,7 +853,7 @@ data ChatResponse
| CRContactRequestRejected {user :: User, contactRequest :: UserContactRequest, contact_ :: Maybe Contact}
| CRServiceResponse {user :: User, responseData :: J.Object}
| CRServiceReplyAccepted {user :: User, connectionId :: AgentConnId}
| CRWallet {user :: User, walletKeyExists :: Bool, walletAccount :: Maybe (AccountIndex, Text, Text)}
| CRWallet {user :: User, walletKeyExists :: Bool, walletAccounts :: [(Text, AccountIndex, Bool, [(Text, Text)])]}
| CRWalletPhrase {user :: User, recoveryPhrase :: Text}
| CRUserAcceptedGroupSent {user :: User, groupInfo :: GroupInfo, hostContact :: Maybe Contact}
| CRUserDeletedMembers {user :: User, groupInfo :: GroupInfo, members :: [GroupMember], withMessages :: Bool, msgSigned :: Bool}