monero_rust/
lib.rs

1use monero_serai_mirror::{
2    wallet::{
3        seed::{Seed, Language},
4        address::{AddressType, AddressMeta, AddressSpec, MoneroAddress, Network, SubaddressIndex},
5        ViewPair,
6    },
7};
8
9use rand_core::OsRng; // For generating a seed.
10use zeroize::{Zeroizing};
11use std::os::raw::{c_char};
12use std::ffi::CString;
13use std::ffi::CStr;
14
15use curve25519_dalek::{
16    edwards::EdwardsPoint,
17    scalar::Scalar,
18    constants::ED25519_BASEPOINT_TABLE,
19};
20
21use sha3::{Digest, Keccak256}; // For generating the view key.
22
23/// Generates a mnemonic in the specified language
24#[no_mangle]
25pub extern "C" fn generate_mnemonic(language: u8) -> *const c_char {
26    // Convert language int to Language struct.
27    let _language: Language = match language{
28        0=>Language::German,
29        1=>Language::English,
30        2=>Language::Spanish,
31        3=>Language::French,
32        4=>Language::Italian,
33        5=>Language::Dutch,
34        6=>Language::Portuguese,
35        7=>Language::Russian,
36        8=>Language::Chinese,
37        9=>Language::Japanese,
38        10=>Language::Esperanto,
39        11=>Language::Lojban,
40        12=>Language::EnglishOld,
41        _=>Language::English,
42    };
43
44    // Convert/cast and return.
45    let ptr: *const c_char = convert_zeroize_string_to_c_char_ptr(&Seed::to_string(&Seed::new(&mut OsRng, _language)));
46    ptr
47}
48
49
50
51/// Generates an address from a mnemonic
52#[no_mangle]
53pub extern "C" fn generate_address(
54    mnemonic: *const c_char,
55    network: u8,
56    account: u32,
57    index: u32,
58) -> *const c_char {
59    let seed = match Seed::from_string(Zeroizing::new(convert_c_char_ptr_to_string(mnemonic))) {
60        Ok(seed) => seed,
61        Err(_) => {
62            // Handle invalid mnemonic, return empty string or error code.
63            return CString::new("").unwrap().into_raw();
64        }
65    };
66
67    let _network: Network = match network{
68        0=>Network::Mainnet,
69        1=>Network::Testnet,
70        2=>Network::Stagenet,
71        _=>Network::Mainnet,
72        // etc...
73    };
74
75    // Calculate spend key and point.
76    let spend: [u8; 32] = *seed.entropy();
77    let spend_scalar: Scalar = Scalar::from_bytes_mod_order(spend);
78    let spend_point: EdwardsPoint = &spend_scalar * &ED25519_BASEPOINT_TABLE;
79
80    // Calculate view key and point.
81    let view: [u8; 32] = Keccak256::digest(&spend).into();
82    let view_scalar: Scalar = Scalar::from_bytes_mod_order(view);
83    let view_point: EdwardsPoint = &view_scalar * &ED25519_BASEPOINT_TABLE;
84
85    let address: MoneroAddress;
86    if (account == 0) && (index == 0) {
87        // Public wallet address.
88        address = MoneroAddress::new(
89            AddressMeta::new(_network, AddressType::Standard),
90            spend_point,
91            view_point,
92        );
93    } else {
94        // Public wallet subaddress at (account, index).
95        let view: ViewPair = ViewPair::new(spend_point, Zeroizing::new(view_scalar));
96        address = view.address(_network, AddressSpec::Subaddress(SubaddressIndex::new(account, index).unwrap()));
97    }
98
99    // Convert/cast.
100    let c_string = CString::new(address.to_string()).unwrap(); // TODO validate address
101    let ptr: *const c_char = c_string.as_ptr() as *const c_char;
102
103    // Do not clean memory; must be freed by Dart wrapper.
104    std::mem::forget(c_string); // Warning: memory leak! must free this memory once done with it.
105
106    // Return.
107    ptr
108}
109
110fn convert_zeroize_string_to_c_char_ptr(zeroized_string: &str) -> *const c_char {
111    // Convert the zeroized string to a normal string.
112    let rust_string = zeroized_string;
113
114    // Convert the string to a CString.
115    let c_string = CString::new(rust_string).expect("Failed to create CString");
116
117    // Convert the CString to a raw pointer.
118    let raw_ptr = c_string.into_raw();
119
120    // Return the raw pointer.
121    raw_ptr
122}
123
124
125fn convert_c_char_ptr_to_string(c_char_ptr: *const c_char) -> String {
126    // Make sure c_char_ptr isn't null.
127    let c_str: &CStr = unsafe {
128        assert!(!c_char_ptr.is_null());
129        CStr::from_ptr(c_char_ptr)
130    };
131
132    // Convert and return.
133    c_str.to_string_lossy().into_owned()
134}
135
136// TODO: Add tests.