@@ -17,7 +17,7 @@ use std::str::FromStr;
1717use std:: sync:: Arc ;
1818use std:: time:: Duration ;
1919
20- pub use bip39:: Mnemonic ;
20+ use bip39:: Mnemonic as Bip39Mnemonic ;
2121use bitcoin:: hashes:: sha256:: Hash as Sha256 ;
2222use bitcoin:: hashes:: Hash ;
2323use bitcoin:: secp256k1:: PublicKey ;
@@ -1151,15 +1151,107 @@ uniffi::custom_type!(BlockHash, String, {
11511151 } ,
11521152} ) ;
11531153
1154- uniffi:: custom_type!( Mnemonic , String , {
1155- remote,
1156- try_lift: |val| {
1157- Ok ( Mnemonic :: from_str( & val) . map_err( |_| Error :: InvalidSecretKey ) ?)
1158- } ,
1159- lower: |obj| {
1160- obj. to_string( )
1161- } ,
1162- } ) ;
1154+ /// A syntactically and semantically valid BIP 39 mnemonic.
1155+ #[ derive( Debug , Clone , PartialEq , Eq , uniffi:: Object ) ]
1156+ #[ uniffi:: export( Debug , Display , Eq ) ]
1157+ pub struct Mnemonic {
1158+ pub ( crate ) inner : Bip39Mnemonic ,
1159+ }
1160+
1161+ #[ uniffi:: export]
1162+ impl Mnemonic {
1163+ /// Constructs a mnemonic from its BIP 39 phrase.
1164+ #[ uniffi:: constructor]
1165+ pub fn from_str ( mnemonic_str : & str ) -> Result < Self , Error > {
1166+ mnemonic_str. parse ( )
1167+ }
1168+
1169+ /// Constructs an English mnemonic from 128-256 bits of entropy.
1170+ ///
1171+ /// The entropy must be a multiple of 32 bits.
1172+ #[ uniffi:: constructor]
1173+ pub fn from_entropy ( entropy : & [ u8 ] ) -> Result < Self , Error > {
1174+ Bip39Mnemonic :: from_entropy ( entropy) . map ( Self :: from) . map_err ( |_| Error :: InvalidMnemonic )
1175+ }
1176+
1177+ /// Generates a random English mnemonic with the specified word count.
1178+ ///
1179+ /// Defaults to 24 words when no word count is specified.
1180+ #[ uniffi:: constructor]
1181+ pub fn generate ( word_count : Option < WordCount > ) -> Self {
1182+ let word_count = word_count. unwrap_or ( WordCount :: Words24 ) . word_count ( ) ;
1183+ let inner = Bip39Mnemonic :: generate ( word_count)
1184+ . expect ( "WordCount always maps to a valid BIP 39 word count" ) ;
1185+ Self { inner }
1186+ }
1187+
1188+ /// Returns the words in the mnemonic.
1189+ pub fn words ( & self ) -> Vec < String > {
1190+ self . inner . words ( ) . map ( String :: from) . collect ( )
1191+ }
1192+
1193+ /// Returns the indices of the mnemonic's words in the English BIP 39 word list.
1194+ pub fn word_indices ( & self ) -> Vec < u16 > {
1195+ self . inner . word_indices ( ) . map ( |index| index as u16 ) . collect ( )
1196+ }
1197+
1198+ /// Returns the number of words in the mnemonic.
1199+ pub fn word_count ( & self ) -> u8 {
1200+ self . inner . word_count ( ) as u8
1201+ }
1202+
1203+ /// Returns the entropy used to construct the mnemonic.
1204+ pub fn to_entropy ( & self ) -> Vec < u8 > {
1205+ self . inner . to_entropy ( )
1206+ }
1207+
1208+ /// Derives the 64-byte BIP 39 seed using the given passphrase.
1209+ pub fn to_seed ( & self , passphrase : & str ) -> Vec < u8 > {
1210+ self . inner . to_seed ( passphrase) . to_vec ( )
1211+ }
1212+
1213+ /// Returns the checksum encoded in the mnemonic's last word.
1214+ pub fn checksum ( & self ) -> u8 {
1215+ self . inner . checksum ( )
1216+ }
1217+ }
1218+
1219+ impl FromStr for Mnemonic {
1220+ type Err = Error ;
1221+
1222+ fn from_str ( mnemonic_str : & str ) -> Result < Self , Self :: Err > {
1223+ mnemonic_str
1224+ . parse :: < Bip39Mnemonic > ( )
1225+ . map ( |inner| Self { inner } )
1226+ . map_err ( |_| Error :: InvalidMnemonic )
1227+ }
1228+ }
1229+
1230+ impl From < Bip39Mnemonic > for Mnemonic {
1231+ fn from ( inner : Bip39Mnemonic ) -> Self {
1232+ Self { inner }
1233+ }
1234+ }
1235+
1236+ impl Deref for Mnemonic {
1237+ type Target = Bip39Mnemonic ;
1238+
1239+ fn deref ( & self ) -> & Self :: Target {
1240+ & self . inner
1241+ }
1242+ }
1243+
1244+ impl AsRef < Bip39Mnemonic > for Mnemonic {
1245+ fn as_ref ( & self ) -> & Bip39Mnemonic {
1246+ self . deref ( )
1247+ }
1248+ }
1249+
1250+ impl std:: fmt:: Display for Mnemonic {
1251+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
1252+ write ! ( f, "{}" , self . inner)
1253+ }
1254+ }
11631255
11641256uniffi:: custom_type!( SocketAddress , String , {
11651257 remote,
@@ -2887,6 +2979,39 @@ mod tests {
28872979 let hrn3 = hrn1;
28882980 assert_eq ! ( hrn1, hrn3) ;
28892981 }
2982+
2983+ #[ test]
2984+ fn test_mnemonic_traits ( ) {
2985+ let mnemonic_str = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about" ;
2986+ let mnemonic = Mnemonic :: from_str ( mnemonic_str) . unwrap ( ) ;
2987+ let bip39_mnemonic = Bip39Mnemonic :: from_str ( mnemonic_str) . unwrap ( ) ;
2988+
2989+ assert_eq ! ( mnemonic. as_ref( ) , & bip39_mnemonic) ;
2990+ assert_eq ! ( mnemonic. to_string( ) , mnemonic_str) ;
2991+ assert_eq ! ( mnemonic, Mnemonic :: from( bip39_mnemonic) ) ;
2992+ assert ! ( format!( "{:?}" , mnemonic) . contains( "Mnemonic" ) ) ;
2993+
2994+ let invalid_mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon" ;
2995+ assert_eq ! ( Mnemonic :: from_str( invalid_mnemonic) , Err ( Error :: InvalidMnemonic ) ) ;
2996+ }
2997+
2998+ #[ test]
2999+ fn test_mnemonic_functionality ( ) {
3000+ let entropy = [ 0 ; 16 ] ;
3001+ let mnemonic = Mnemonic :: from_entropy ( & entropy) . unwrap ( ) ;
3002+
3003+ assert_eq ! (
3004+ mnemonic. words( ) ,
3005+ [ vec![ "abandon" . to_string( ) ; 11 ] , vec![ "about" . to_string( ) ] ] . concat( )
3006+ ) ;
3007+ assert_eq ! ( mnemonic. word_indices( ) , [ vec![ 0 ; 11 ] , vec![ 3 ] ] . concat( ) ) ;
3008+ assert_eq ! ( mnemonic. word_count( ) , 12 ) ;
3009+ assert_eq ! ( mnemonic. to_entropy( ) , entropy) ;
3010+ assert_eq ! ( mnemonic. checksum( ) , 3 ) ;
3011+ assert_eq ! ( mnemonic. to_seed( "TREZOR" ) . len( ) , 64 ) ;
3012+ assert_eq ! ( Mnemonic :: generate( Some ( WordCount :: Words12 ) ) . word_count( ) , 12 ) ;
3013+ assert_eq ! ( Mnemonic :: from_entropy( & [ 0 ; 15 ] ) , Err ( Error :: InvalidMnemonic ) ) ;
3014+ }
28903015}
28913016
28923017/// An opaque token used to continue a paginated listing.
0 commit comments