review iterations

This commit is contained in:
Alain Brenzikofer
2026-09-26 14:29:40 +02:00
parent 143d073980
commit 7b4ac1bb8b
13 changed files with 161 additions and 134 deletions
+21 -14
View File
@@ -2,14 +2,14 @@
{-# LANGUAGE TemplateHaskell #-}
module Simplex.Chat.Wallet
( AccountIndex,
AccountKey,
( AccountKey,
WalletAddress (..),
WalletInfo (..),
WalletError (..),
newWalletMaster,
entropyFromMnemonic,
importWalletMaster,
seedMnemonic,
masterMnemonic,
deriveAccount,
accountSecret,
)
@@ -18,15 +18,16 @@ where
import Control.Concurrent.STM
import Crypto.Random (ChaChaDRG)
import qualified Data.Aeson.TH as JQ
import Data.Bifunctor (first)
import qualified Data.ByteArray.Encoding as BAE
import Data.Text (Text)
import Data.Text.Encoding (decodeLatin1)
import Data.Word (Word32)
import qualified Simplex.Messaging.Crypto.BIP32 as B32
import qualified Simplex.Messaging.Crypto.BIP39 as B39
import Simplex.Messaging.Crypto.BIP44 (AccountIndex, CoinType (..), bip44Path)
import qualified Simplex.Messaging.Crypto.Secp256k1 as S
import Simplex.Messaging.Encoding.String (strEncode)
import Simplex.Messaging.Eth.Address (addressFromPrivateKey)
import Simplex.Messaging.Eth.Address (Address, addressFromPrivateKey)
import Simplex.Messaging.Parsers (defaultJSON, dropPrefix, sumTypeJSON)
type AccountKey = S.Secp256k1PrivateKey
@@ -34,7 +35,13 @@ type AccountKey = S.Secp256k1PrivateKey
data WalletAddress = WalletAddress
{ accountIndex :: AccountIndex,
keyPath :: Text,
address :: Text
address :: Address
}
deriving (Show)
data WalletInfo = WalletInfo
{ accountIndexes :: [AccountIndex],
nextAccountIndex :: Maybe Word32
}
deriving (Show)
@@ -53,24 +60,22 @@ masterStrength :: B39.EntropyStrength
masterStrength = B39.ES256
newWalletMaster :: TVar ChaChaDRG -> IO B32.WalletMaster
newWalletMaster g = (`B32.mkWalletMaster` "") <$> atomically (B39.randomEntropy masterStrength g)
newWalletMaster g = B32.mkWalletMaster <$> atomically (B39.randomEntropy masterStrength g)
entropyFromMnemonic :: Text -> Either WalletError B39.WalletEntropy
entropyFromMnemonic phrase = case B39.parsePhrase phrase of
Right ent | B39.entropyWordCount ent == 24 -> Right ent
_ -> Left WEBadMnemonic
entropyFromMnemonic = first (const WEBadMnemonic) . B39.parsePhrase
importWalletMaster :: Text -> Either WalletError B32.WalletMaster
importWalletMaster phrase = (`B32.mkWalletMaster` "") <$> entropyFromMnemonic phrase
importWalletMaster phrase = B32.mkWalletMaster <$> entropyFromMnemonic phrase
seedMnemonic :: B32.WalletMaster -> Text
seedMnemonic = decodeLatin1 . B39.entropyPhrase . B32.masterEntropy
masterMnemonic :: B32.WalletMaster -> Text
masterMnemonic = decodeLatin1 . B39.entropyPhrase . B32.masterEntropy
deriveAccount :: TVar ChaChaDRG -> B32.WalletMaster -> AccountIndex -> IO (AccountKey, WalletAddress)
deriveAccount g master n = do
k <- B32.xkKey <$> B32.derivePath g (B32.walletMasterKey master) path
a <- addressFromPrivateKey g k
pure (k, WalletAddress {accountIndex = n, keyPath = decodeLatin1 $ B32.renderPath path, address = decodeLatin1 $ strEncode a})
pure (k, WalletAddress {accountIndex = n, keyPath = decodeLatin1 $ B32.renderPath path, address = a})
where
path = bip44Path Ethereum n
@@ -79,4 +84,6 @@ accountSecret k = "0x" <> decodeLatin1 (BAE.convertToBase BAE.Base16 $ S.unPriva
$(JQ.deriveJSON defaultJSON ''WalletAddress)
$(JQ.deriveJSON defaultJSON ''WalletInfo)
$(JQ.deriveJSON (sumTypeJSON $ dropPrefix "WE") ''WalletError)