diff --git a/.github/workflows/rust_integration_tests.yml b/.github/workflows/rust_integration_tests.yml new file mode 100644 index 0000000..f9fe4a3 --- /dev/null +++ b/.github/workflows/rust_integration_tests.yml @@ -0,0 +1,24 @@ +name: Rust Integration Tests + +on: + push: + branches: + - main + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@v2 + + - name: Set up Rust + uses: actions/setup-rust@v2 + with: + toolchain: stable + + - name: Build and Test + run: | + cargo build --verbose + cargo test --verbose \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 0c2c25a..acd6e07 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ repository = "https://github.com/monumexyz/libmonero" readme = "README.md" keywords = ["monero", "monero-library"] categories = ["monero", "monero-library"] +exclude = ["main.rs"] [dependencies] base58-monero = "2.0.0" diff --git a/README.md b/README.md index c78a8c8..835e872 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ > DISCLAIMER: This library is still in early development and doesn't have a stable version yet. It is not ready for production use and not cryptographically audited. We are working hard to make it as secure as possible. Please use it at your own risk. -libmonero is a library for the Monero cryptocurrency written in Rust. It is designed to be fast, safe and easy to use. +libmonero is a powerful, batteries-included library for the Monero cryptocurrency written in Rust. It is designed to be fast, safe and easy to use. [Why another library?](#why-another-library) @@ -28,7 +28,18 @@ And many more features... ([Roadmap](#roadmap)) Add the library to your project and use the functions: \ ```cargo add libmonero``` -For more details, please take a look at [docs](docs/start.md) +For more details, please take a look at [docs](https://docs.rs/libmonero). + +## Sponsoring + +You can sponsor this project for 20$/month. If you sponsor this project, you will get the following benefits: + +- Mention in the README.md (with your logo and link to your website if you want) +- Request features or bugfixes +- Priority support +- Special role in the matrix room + +If you want to sponsor this project, please contact us at the matrix room: [#monume:matrix.org](https://matrix.to/#/#monume:matrix.org) ## Licensing diff --git a/docs/examples.md b/docs/examples.md deleted file mode 100644 index ca90568..0000000 --- a/docs/examples.md +++ /dev/null @@ -1,28 +0,0 @@ -# Examples - -This file contains various examples of how to use the library. -Here is a list of the examples: - -- [Creating a new wallet](#creating-a-new-wallet) - -## Creating a new wallet - -You can create a new wallet using the `new` function in the `Wallet` struct. - -```rust -use libmonero::wallet::Wallet; - -fn main() { - // This will create a new original (25-word) english mnemonic wallet in Monero mainnet with a random seed. - let wallet = Wallet::new("en", "original", 0); - // Now, for example get the private spend key and print it. - println!("{}", wallet.priv_sk); -} -``` - -The above example will print something like this to console: \ -`059fd750b44f957d338f3a6aa09cd2eb669df6463c782217b22917f84b9b9109` - -You can use the generated wallet to access the wallet's keys, address, etc. - ---- \ No newline at end of file diff --git a/docs/functions.md b/docs/functions.md deleted file mode 100644 index bcf2e03..0000000 --- a/docs/functions.md +++ /dev/null @@ -1,125 +0,0 @@ -# Functions - -This is the documentation for all functions in the library. \ -Here is a list of all functions in this project: - -- [Utils](#utils) - - [is_valid_addr(address: &str) -> bool](#is_valid_addraddress-str---bool) -- [Keys](#keys) - - [generate_seed(language: &str, seed_type: &str) -> Vec](#generate_seedlanguage-str-seed_type-str---vecstring) - - [derive_hex_seed(mnemonic_seed: Vec) -> String](#derive_hex_seedmnemonic_seed-vecstring---string) - - [derive_priv_keys(hex_seed: String) -> Vec](#derive_priv_keyshex_seed-string---vecstring) - - [derive_priv_vk_from_priv_sk(private_spend_key: String) -> String](#derive_priv_vk_from_priv_skprivate_spend_key-string---string) - - [derive_pub_key(private_key: String) -> String](#derive_pub_keyprivate_key-string---string) - - [derive_address(public_spend_key: String, public_view_key: String, network: i8) -> String](#derive_addresspublic_spend_key-string-public_view_key-string-network-i8---string) - - -## Utils - -You can use these functions via `libmonero::utils::function_name` - -- ### `is_valid_addr(address: &str) -> bool` - -Checks if the given address is a valid Monero address \ -Returns true if valid, returns false if invalid \ -Example usage: -```rust -println!(keys::is_valid_addr("42wDfAgKWRYcdB7NtrZtabUx2d4jknPmZBT4KS9gxLP4VYBS4S8zH1nj3aByTHVQL1LRhKzoL1NDhKV3tXEt3KeKR5kR7uw")); -> true -println!(keys::is_valid_addr("42wDfAgKWRYcdB7NtrZtabUx2d4jknPmZBT4KS9gxLP4VYBS4S8zH1nj3aByTHVQL1LRhKzoL1NDhKV3tXEt3KeKR5kR7u")); -> false -``` - -## Keys - -You can use these functions via `libmonero::keys::function_name` - -- ### `generate_seed(language: &str, seed_type: &str) -> Vec` - -Generates a mnemonic phrase for given language and type \ -Available types for now: -- `original`: (25-word original type) - - `en` (English) - - `eo` (Esperanto) - - `fr` (French) - - `it` (Italian) - - `jp` (Japanese) (Works but not recommended) - - `lj` (Lojban) - - `pt` (Portuguese) - - `ru` (Russian) -- `mymonero`: (13-word MyMonero type) - - `en`, `eo`, `fr`, `it`, `jp`, `lj`, `pt`, `ru` (same as original) -- `polyseed` (TO BE IMPLEMENTED) -> DISCLAIMER: polyseed is not implemented yet - -Example usage: -```rust -let mnemonic = keys::generate_seed("en", "original"); -println!("{:?}", mnemonic); -> ["tissue", "raking", "haunted", "huts", "afraid", "volcano", "howls", "liar", "egotistic", "befit", "rounded", "older", "bluntly", "imbalance", "pivot", "exotic", "tuxedo", "amaze", "mostly", "lukewarm", "macro", "vocal", "hounded", "biplane", "rounded"] -``` - -- ### `derive_hex_seed(mnemonic_seed: Vec) -> String` - -Derives the hexadecimal seed from the given mnemonic seed \ -Example usage: -```rust -let mnemonic = vec!["tissue", "raking", "haunted", "huts", "afraid", "volcano", "howls", "liar", "egotistic", "befit", "rounded", "older", "bluntly", "imbalance", "pivot", "exotic", "tuxedo", "amaze", "mostly", "lukewarm", "macro", "vocal", "hounded", "biplane", "rounded"]; -let hex_seed = keys::derive_hex_seed(mnemonic); -println!("{}", hex_seed); -> "f7b3beabc9bd6ced864096c0891a8fdf94dc714178a09828775dba01b4df9ab8" -``` - -- ### `derive_priv_keys(hex_seed: String) -> Vec` - -Derives the private keys from the given hexadecimal seed \ -First one in the Vec is the private spend key, second one is the private view key \ -Example usage: -```rust -let hex_seed = "f7b3beabc9bd6ced864096c0891a8fdf94dc714178a09828775dba01b4df9ab8"; -let priv_keys = derive_priv_keys(hex_seed); -println!("{:?}", priv_keys); -> ["c8982eada77ba2245183f2bff85dfaf993dc714178a09828775dba01b4df9a08", "0d13a94c82d7a60abb54d2217d38935c3f715295e30378f8848a1ca1abc8d908"] -``` - -- ### `derive_priv_vk_from_priv_sk(private_spend_key: String) -> String` - -Derives the private view key from the given private spend key \ -Example usage: -```rust -let private_spend_key = "c8982eada77ba2245183f2bff85dfaf993dc714178a09828775dba01b4df9a08"; -let private_view_key = derive_priv_vk_from_priv_sk(private_spend_key); -println!("{}", private_view_key); -> "0d13a94c82d7a60abb54d2217d38935c3f715295e30378f8848a1ca1abc8d908" -``` - -- ### `derive_pub_key(private_key: String) -> String` - -Derives the public key from the given private key (spend or view) \ -Example usage: -```rust -let private_spend_key = "c8982eada77ba2245183f2bff85dfaf993dc714178a09828775dba01b4df9a08" -let private_view_key = "0d13a94c82d7a60abb54d2217d38935c3f715295e30378f8848a1ca1abc8d908" -let public_spend_key = derive_pub_key(private_spend_key); -let public_view_key = derive_pub_key(private_view_key); -println!("{}", public_spend_key); -> "e78d891dd2be407f24e6470caad956e1b746ae0b41cd8252f96684090bc05d95" -println!("{}", public_view_key); -> "157d278aa3aee4e11c5a8243a43a78527a2691009562b8c18654975f1347cb47" -``` - -- ### `derive_address(public_spend_key: String, public_view_key: String, network: i8) -> String` - -Derives the address from the given public spend and view keys and network \ -Networks: -- `0`: Monero Mainnet -- `1`: Monero Testnet - -Example usage: -```rust -let public_spend_key = "e78d891dd2be407f24e6470caad956e1b746ae0b41cd8252f96684090bc05d95"; -let public_view_key = "157d278aa3aee4e11c5a8243a43a78527a2691009562b8c18654975f1347cb47"; -let address = derive_address(public_spend_key, public_view_key, 0); -println!("{}", address); -> "4AQ3jTJg91yNGTXjo9iWr1ekjBGJ5mM6HEsxKqoKddHnRwJTVJYnyLXeerff6iTys5Eo8dyG87tfqZNS5CcSd7U694YiR8J" -``` \ No newline at end of file diff --git a/docs/start.md b/docs/start.md deleted file mode 100644 index 6245c7b..0000000 --- a/docs/start.md +++ /dev/null @@ -1,18 +0,0 @@ -# Getting Started - -Welcome, this is the documentation for Monume's library libmonero. Here you will find information on how to use the library, as well as some examples. - -To get started, you will need to install the library. You can do this by running the following command: -```bash -cargo add libmonero -``` -After that, you can import the library into your project by adding `use libmonero;` to the top of your file. For example: -```rust -use libmonero::keys::generate_seed; -``` - -# List Of Contents - -- [Structs And Implementations](structs.md) -- [Functions](functions.md) -- [Examples](examples.md) \ No newline at end of file diff --git a/docs/structs.md b/docs/structs.md deleted file mode 100644 index 1de765e..0000000 --- a/docs/structs.md +++ /dev/null @@ -1,42 +0,0 @@ -# Structs And Implementations - -This is the documentation for all public structs and implementations in the library. \ -Here is a list of all structs and implementations in this project: - -- [Wallet](#wallet) - - [new(language: &str, seed_type: &str, network: i8) -> Wallet](#newlanguage-str-seed_type-str-network-i8---wallet) - -## Wallet - -This struct is used to store all the information about a wallet. \ -It is used to store the mnemonic seed, the private spend key, the private view key, the public spend key, the public view key, and the address. \ -You can use these functions via `use libmonero::wallet` -> It can also be used as a normal wallet which can make transactions, but this is not implemented yet and will be implemented in the future. - ---- - -- ### `new(language: &str, seed_type: &str, network: i8) -> Wallet` - -Creates a new wallet with the given language, seed type, and network. \ -Available languages: `en`, `eo`, `fr`, `it`, `jp`, `lj`, `pt`, `ru`... (same as generate_seed) \ -Available seed types: `original`, `mymonero` (same as generate_seed) \ -Available networks: `0` (Mainnet), `1` (Testnet) (same as derive_address) \ - -Example usage: -```rust -let wallet = wallet::Wallet::new("en", "original", 0); -println!("mnemonic: {:?}", wallet.mnemonic); -> mnemonic: ["twofold", "misery", "idols", "worry", "intended", "dunes", "vain", "unwind", "bounced", "enigma", "mural", "asked", "degrees", "react", "nautical", "nomad", "utensils", "remedy", "hockey", "corrode", "eternal", "dizzy", "rally", "jabbed", "utensils"] -println!("hex_seed: {:?}", wallet.hex_seed); -> hex_seed: "dbfa4cde9e2890cd1878c83705a2f283cfc212dc84a006cb93ee1f1adc5814bc" -println!("priv_sk: {:?}", wallet.priv_sk); -> priv_sk: "acdfbcdf7ce6c504e3ba243774e55d9ecec212dc84a006cb93ee1f1adc58140c" -println!("priv_vk: {:?}", wallet.priv_vk); -> priv_vk: "7f65a5f4a4a68af77b3a97770c76d66dd3f8f959121ec8969ebcc2c24ea8e80f" -println!("pub_sk: {:?}", wallet.pub_sk); -> pub_sk: "1ecddc60ba4baacc12fcba53703cbecc8302b0e8703332bd69294fd85ba26ede" -println!("pub_vk: {:?}", wallet.pub_vk); -> pub_vk: "803844e0a71eda67272e2fec385dbc764b82412f1e66fb509c579f54c12b8106" -println!("address: {:?}", wallet.address); -> address: "42npBNptGRPb8mxLhiaYsPbD2B8Qs9z41YgXGf8jtzeHeDY9cndneC1JFiQB5dn291LncLYaje1WaEV2Si65FDsn1kqvwAy" -``` diff --git a/precommit.sh b/precommit.sh index 2eb3d84..5a11b43 100644 --- a/precommit.sh +++ b/precommit.sh @@ -1,3 +1,4 @@ #!/bin/sh cd scripts && python3 copyright.py -cargo clippy --fix --lib -p libmonero --allow-dirty # For fixing clippy warnings \ No newline at end of file +cargo clippy --fix --lib -p libmonero --allow-staged --allow-dirty # For fixing clippy warnings +cargo test \ No newline at end of file diff --git a/scripts/copyright.py b/scripts/copyright.py index b7a0d44..0bdb0cf 100644 --- a/scripts/copyright.py +++ b/scripts/copyright.py @@ -50,7 +50,9 @@ def add_text_to_files(folder_path, text, ignored_extensions=[]): file.write(text + '\n' + ''.join(lines)) def main(): + print("Starting copyright script") add_text_to_files(path, header, ignore) + print("Finished adding copyright headers") if __name__ == "__main__": main() \ No newline at end of file diff --git a/src/crypt/cryptonight/aesu.rs b/src/crypt/cryptonight/aesu.rs index 6c8e6e5..cb1413c 100644 --- a/src/crypt/cryptonight/aesu.rs +++ b/src/crypt/cryptonight/aesu.rs @@ -126,22 +126,4 @@ fn shift_rows(block: &mut [u8]) { block[col * 4 + 3] = block[col * 4 - 1]; } block[3] = tmp; -} - -pub fn aes_round16(blocks: &[u8; 16], round_keys: &[u8; 16]) -> [u8; 16] { - let mut block = *blocks; - let mut round_key = *round_keys; - - aes_round(&mut block, &round_key); - round_key.rotate_left(4); - - aes_round(&mut block, &round_key); - round_key.rotate_left(4); - - aes_round(&mut block, &round_key); - round_key.rotate_left(4); - - aes_round(&mut block, &round_key); - - block } \ No newline at end of file diff --git a/src/crypt/cryptonight/slow_hash.rs b/src/crypt/cryptonight/slow_hash.rs index 9189874..b42c282 100644 --- a/src/crypt/cryptonight/slow_hash.rs +++ b/src/crypt/cryptonight/slow_hash.rs @@ -14,8 +14,18 @@ use crate::crypt::cryptonight::aesu::{aes_round, xor}; const SCRATCHPAD_SIZE: usize = 2 * 1024 * 1024; // 2 MiB -/// Main CryptoNight function defined in: https://web.archive.org/web/20190911221902/https://cryptonote.org/cns/cns008.txt -/// It is used in previous block hash calculation and in PoW calculation +/// Main CryptoNight function defined in: +/// +/// Even though it's actually implemented in Rust for [Cuprate](https://github.com/Cuprate/cuprate), anyone can use it. +/// +/// Example: +/// ``` +/// use libmonero::crypt::cryptonight::slow_hash::cn_slow_hash; +/// +/// let input: &str = "This is a test"; +/// let output: String = cn_slow_hash(input.as_bytes()); +/// assert_eq!(output, "a084f01d1437a09c6985401b60d43554ae105802c5f5d8a9b3253649c0be6605".to_string()); +/// ``` pub fn cn_slow_hash(input: &[u8]) -> String { // CryptoNight Step 1: Initialization Of Scratchpad @@ -192,7 +202,6 @@ pub fn cn_slow_hash(input: &[u8]) -> String { // Step 3C: Use the first byte of the Keccak state to select a hash function let hash_function = keccak_hash[0] & 0x03; - println!("hash_function: {:?}", hash_function); let final_byte = match hash_function { 0 => blake256_hash(keccak_hash), 1 => groestl256_hash(keccak_hash), diff --git a/src/keys.rs b/src/keys.rs index 2063772..3112863 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -10,7 +10,7 @@ //! # Keys //! -//! This module is for everything related to keys, such as generating seeds, deriving keys from seeds, deriving public keys from private keys, and deriving addresses from public keys. +//! This module is for everything related to keys, such as generating seeds, deriving keys from seeds, deriving public keys from private keys, and deriving addresses from public keys etc. use crate::crypt::ed25519::sc_reduce32; use crate::wordsets::{WordsetOriginal, WORDSETSORIGINAL}; @@ -94,15 +94,38 @@ fn generate_mymonero_seed(language: &str) -> Vec<&str> { seed } -/// Creates a cryptographically secure seed of given type and language +/// Generates a cryptographically secure mnemonic phrase for given language and seed type +/// +/// Available seed types: +/// - `original` : (25-word) +/// - `en` (English) +/// - `eo` (Esperanto) +/// - `fr` (French) +/// - `it` (Italian) +/// - `jp` (Japanese) (Works but not recommended) +/// - `lj` (Lojban) +/// - `pt` (Portuguese) +/// - `ru` (Russian) +/// - `mymonero` : (13-word, MyMonero wallet type) +/// - `en`, `eo`, `fr`, `it`, `jp`, `lj`, `pt`, `ru` (same as original) +/// - `polyseed` : (TO BE IMPLEMENTED) +/// > DISCLAIMER: polyseed is not implemented yet +/// +/// Example: +/// ``` +/// use libmonero::keys::generate_seed; +/// +/// let mnemonic: Vec = generate_seed("en", "original"); +/// // Not equal to the example below because the seed is generated randomly, but the seed is valid +/// assert_ne!(mnemonic, vec!["tissue", "raking", "haunted", "huts", "afraid", "volcano", "howls", "liar", "egotistic", "befit", "rounded", "older", "bluntly", "imbalance", "pivot", "exotic", "tuxedo", "amaze", "mostly", "lukewarm", "macro", "vocal", "hounded", "biplane", "rounded"].iter().map(|&s| s.to_string()).collect::>()); +/// ``` pub fn generate_seed(language: &str, seed_type: &str) -> Vec { - let seed; - match seed_type { - "original" => seed = generate_original_seed(language), - "mymonero" => seed = generate_mymonero_seed(language), - "polyseed" => panic!("Polyseed not yet implemented yet"), + let seed = match seed_type { + "original" => generate_original_seed(language), + "mymonero" => generate_mymonero_seed(language), + "polyseed" => panic!("Polyseed not implemented yet"), _ => panic!("Invalid seed type"), - } + }; let mut seed_string: Vec = Vec::new(); for word in seed { seed_string.push(word.to_string()); @@ -115,7 +138,16 @@ fn swap_endian_4_byte(s: &str) -> String { format!("{}{}{}{}", &s[6..8], &s[4..6], &s[2..4], &s[0..2]) } -/// Derives hex seed from given mnemonic seed +/// Derives hexadecimal seed from the given mnemonic seed +/// +/// Example: +/// ``` +/// use libmonero::keys::derive_hex_seed; +/// +/// let mnemonic: Vec = vec!["tissue", "raking", "haunted", "huts", "afraid", "volcano", "howls", "liar", "egotistic", "befit", "rounded", "older", "bluntly", "imbalance", "pivot", "exotic", "tuxedo", "amaze", "mostly", "lukewarm", "macro", "vocal", "hounded", "biplane", "rounded"].iter().map(|s| s.to_string()).collect(); +/// let hex_seed: String = derive_hex_seed(mnemonic); +/// assert_eq!(hex_seed, "f7b3beabc9bd6ced864096c0891a8fdf94dc714178a09828775dba01b4df9ab8".to_string()); +/// ``` pub fn derive_hex_seed(mut mnemonic_seed: Vec) -> String { // Find the wordset for the given seed let mut the_wordset = &WordsetOriginal { @@ -210,8 +242,8 @@ fn derive_original_priv_keys(hex_seed: String) -> Vec { let mut priv_spend_key = String::new(); for i in (0..hex_bytes_array.len()).step_by(32) { let mut priv_key = String::new(); - for j in i..i + 32 { - priv_key.push_str(&format!("{:02x}", hex_bytes_array[j])); + for byte in hex_bytes_array.iter().skip(i).take(32) { + priv_key.push_str(&format!("{:02x}", byte)); } priv_spend_key.push_str(&priv_key); } @@ -225,8 +257,8 @@ fn derive_original_priv_keys(hex_seed: String) -> Vec { let mut priv_view_key = String::new(); for i in (0..priv_view_key_array.len()).step_by(32) { let mut priv_key = String::new(); - for j in i..i + 32 { - priv_key.push_str(&format!("{:02x}", priv_view_key_array[j])); + for byte in priv_view_key_array.iter().skip(i).take(32) { + priv_key.push_str(&format!("{:02x}", byte)); } priv_view_key.push_str(&priv_key); } @@ -245,8 +277,8 @@ fn derive_mymonero_priv_keys(hex_seed: String) -> Vec { let mut priv_spend_key = String::new(); for i in (0..priv_spend_key_array.len()).step_by(32) { let mut priv_key = String::new(); - for j in i..i + 32 { - priv_key.push_str(&format!("{:02x}", priv_spend_key_array[j])); + for item in priv_spend_key_array.iter().skip(i).take(32) { + priv_key.push_str(&format!("{:02x}", item)); } priv_spend_key.push_str(&priv_key); } @@ -262,8 +294,8 @@ fn derive_mymonero_priv_keys(hex_seed: String) -> Vec { let mut priv_view_key = String::new(); for i in (0..priv_view_key_array.len()).step_by(32) { let mut priv_key = String::new(); - for j in i..i + 32 { - priv_key.push_str(&format!("{:02x}", priv_view_key_array[j])); + for item in priv_view_key_array.iter().skip(i).take(32) { + priv_key.push_str(&format!("{:02x}", item)); } priv_view_key.push_str(&priv_key); } @@ -271,7 +303,18 @@ fn derive_mymonero_priv_keys(hex_seed: String) -> Vec { vec![priv_spend_key, priv_view_key] } -/// Derives private spend and view keys from given hex seed +/// Derives private keys from given hex seed +/// +/// Vector's first element is private spend key, second element is private view key +/// +/// Example: +/// ``` +/// use libmonero::keys::derive_priv_keys; +/// +/// let hex_seed: String = "f7b3beabc9bd6ced864096c0891a8fdf94dc714178a09828775dba01b4df9ab8".to_string(); +/// let priv_keys: Vec = derive_priv_keys(hex_seed); +/// assert_eq!(priv_keys, vec!["c8982eada77ba2245183f2bff85dfaf993dc714178a09828775dba01b4df9a08", "0d13a94c82d7a60abb54d2217d38935c3f715295e30378f8848a1ca1abc8d908"].iter().map(|&s| s.to_string()).collect::>()); +/// ``` pub fn derive_priv_keys(hex_seed: String) -> Vec { match hex_seed.len() { 32 => derive_mymonero_priv_keys(hex_seed), @@ -280,7 +323,16 @@ pub fn derive_priv_keys(hex_seed: String) -> Vec { } } -/// Derives private view key from private spend key +/// Derives private view key from given private spend key +/// +/// Example: +/// ``` +/// use libmonero::keys::derive_priv_vk_from_priv_sk; +/// +/// let private_spend_key: String = "c8982eada77ba2245183f2bff85dfaf993dc714178a09828775dba01b4df9a08".to_string(); +/// let private_view_key: String = derive_priv_vk_from_priv_sk(private_spend_key); +/// assert_eq!(private_view_key, "0d13a94c82d7a60abb54d2217d38935c3f715295e30378f8848a1ca1abc8d908".to_string()); +/// ``` pub fn derive_priv_vk_from_priv_sk(private_spend_key: String) -> String { // Turn private spend key into bytes and pass through Keccak256 function let priv_spend_key_bytes = hex::decode(private_spend_key.clone()).unwrap(); @@ -292,8 +344,8 @@ pub fn derive_priv_vk_from_priv_sk(private_spend_key: String) -> String { let mut priv_view_key = String::new(); for i in (0..priv_view_key_array.len()).step_by(32) { let mut priv_key = String::new(); - for j in i..i + 32 { - priv_key.push_str(&format!("{:02x}", priv_view_key_array[j])); + for item in priv_view_key_array.iter().skip(i).take(32) { + priv_key.push_str(&format!("{:02x}", item)); } priv_view_key.push_str(&priv_key); } @@ -306,7 +358,16 @@ fn ge_scalar_mult_base(scalar: &Scalar) -> EdwardsPoint { ED25519_BASEPOINT_TABLE.mul(scalar as &Scalar) } -/// Derives public key from given private key, can be either spend or view key +/// Derives public key from given private key (spend or view) +/// +/// Example: +/// ``` +/// use libmonero::keys::derive_pub_key; +/// +/// let private_spend_key: String = "c8982eada77ba2245183f2bff85dfaf993dc714178a09828775dba01b4df9a08".to_string(); +/// let public_spend_key: String = derive_pub_key(private_spend_key); +/// assert_eq!(public_spend_key, "e78d891dd2be407f24e6470caad956e1b746ae0b41cd8252f96684090bc05d95".to_string()); +/// ``` pub fn derive_pub_key(private_key: String) -> String { // Turn private key into bytes let private_key_bytes = hex::decode(private_key.clone()).unwrap(); @@ -320,8 +381,8 @@ pub fn derive_pub_key(private_key: String) -> String { let mut public_key = String::new(); for i in (0..public_key_bytes.len()).step_by(32) { let mut pub_key = String::new(); - for j in i..i + 32 { - pub_key.push_str(&format!("{:02x}", public_key_bytes[j])); + for item in public_key_bytes.iter().skip(i).take(32) { + pub_key.push_str(&format!("{:02x}", item)); } public_key.push_str(&pub_key); } @@ -329,7 +390,21 @@ pub fn derive_pub_key(private_key: String) -> String { public_key } -/// Derives public address from given public spend and view keys and network +/// Derives main public address from given public spend key, public view key and network +/// +/// Networks: +/// - `0` : Monero Mainnet +/// - `1` : Monero Testnet +/// +/// Example: +/// ``` +/// use libmonero::keys::derive_address; +/// +/// let public_spend_key: String = "e78d891dd2be407f24e6470caad956e1b746ae0b41cd8252f96684090bc05d95".to_string(); +/// let public_view_key: String = "157d278aa3aee4e11c5a8243a43a78527a2691009562b8c18654975f1347cb47".to_string(); +/// let public_address: String = derive_address(public_spend_key, public_view_key, 0); +/// assert_eq!(public_address, "4AQ3jTJg91yNGTXjo9iWr1ekjBGJ5mM6HEsxKqoKddHnRwJTVJYnyLXeerff6iTys5Eo8dyG87tfqZNS5CcSd7U694YiR8J".to_string()); +/// ``` pub fn derive_address(public_spend_key: String, public_view_key: String, network: u8) -> String { let network_byte = match network { 0 => vec![0x12], // Monero mainnet diff --git a/src/lib.rs b/src/lib.rs index 40a7d19..10ad08f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,9 +8,28 @@ * */ +//! Powerful, batteries-included Monero library. It is mainly function-oriented, but some structs are also included. +//! +//! You can get started by adding the "libmonero" crate in your project: \ +//! `cargo add libmonero` +//! +//! ## Functions +//! +//! - Utils +//! - [is_valid_addr(address: &str) -> bool](utils/fn.is_valid_addr.html) +//! - Keys +//! - [generate_seed(language: &str, seed_type: &str) -> Vec](keys/fn.generate_seed.html) +//! - [derive_hex_seed(mnemonic_seed: Vec) -> String](keys/fn.derive_hex_seed.html) +//! - [derive_priv_keys(hex_seed: String) -> Vec](keys/fn.derive_priv_keys.html) +//! - [derive_priv_vk_from_priv_sk(private_spend_key: String) -> String](keys/fn.derive_priv_vk_from_priv_sk.html) +//! - [derive_pub_key(private_key: String) -> String](keys/fn.derive_pub_key.html) +//! - [derive_address(public_spend_key: String, public_view_key: String, network: i8) -> String](keys/fn.derive_address.html) + +/// Utility functions like address validation pub mod utils; +/// All functions related to keys pub mod keys; -pub mod mnemonics { +pub(crate) mod mnemonics { pub mod original { pub mod wordsets; pub mod languages { @@ -29,18 +48,20 @@ pub mod mnemonics { } } } +/// Cryptographic functions pub mod crypt { - pub mod ed25519; + pub(crate) mod ed25519; + /// Cryptonight functions pub mod cryptonight { + /// Cryptonight's slow hash related functions pub mod slow_hash; - pub mod aesu; - pub mod otheru; + pub(crate) mod aesu; + pub(crate) mod otheru; } } -pub mod wallet; -pub use utils::*; -pub use keys::*; -pub use mnemonics::original::wordsets; -pub use wallet::*; -pub use crypt::cryptonight::slow_hash::*; \ No newline at end of file +// Will be added in the future +// pub mod wallet; +// pub use wallet::*; +pub use crypt::cryptonight::slow_hash::cn_slow_hash; +use mnemonics::original::wordsets; \ No newline at end of file diff --git a/src/utils.rs b/src/utils.rs index aa0272f..b41473c 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -10,12 +10,23 @@ //! # Utils //! -//! This module contains utility functions. +//! This module contains utility functions like address validation etc. use regex::Regex; -/// Returns true if given address is a valid Monero address +/// Checks if the given address is valid, returns true if it is, false otherwise +/// +/// Example: +/// ``` +/// use libmonero::utils::is_valid_addr; +/// +/// let result: bool = is_valid_addr("42XUaeqehJTfM1wpW5prsJiQYobDUQG5FfzVe47sYa8LZG3wPwybySuC6kwADuLJJDg86k8yfcp6h963Ck8NEfWdAjfJyVB"); +/// assert_eq!(result, true); +/// +/// let result_invalid: bool = is_valid_addr("12342XUaeqehJTfM1wpW5prsJiQYobDUQG5FfzVe47sYa8LZG3wPwybySuC6kwADuLJJDg86k8yfcp6h963Ck8NEfWdAjfJyVB"); +/// assert_eq!(result_invalid, false); +/// ``` pub fn is_valid_addr(address: &str) -> bool { - let r = Regex::new(r"^(4|8)[0-9a-zA-Z]{95}$").unwrap(); + let r = Regex::new(r"^[48][0-9AB][1-9A-HJ-NP-Za-km-z]{93}$").unwrap(); r.is_match(address) } \ No newline at end of file diff --git a/src/wallet.rs b/src/wallet.rs index 74d9133..d4edfd6 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -67,7 +67,6 @@ pub struct Wallet { pub sub_adresses: Vec, pub transactions: Vec, pub main_node: DaemonNode, - /// Record of processes, used internally, don't change manually unless specified pub processes: HashMap } diff --git a/tests/integration_test.rs b/tests/integration_test.rs new file mode 100644 index 0000000..298190f --- /dev/null +++ b/tests/integration_test.rs @@ -0,0 +1,41 @@ +#[cfg(test)] +mod tests { + use libmonero::keys::{derive_address, derive_hex_seed, derive_priv_keys, derive_pub_key, generate_seed}; + use libmonero::crypt::cryptonight::slow_hash::cn_slow_hash; + + #[test] + fn seed_generation() { + let seed = generate_seed("en", "original"); + assert_ne!(seed, vec!["tissue", "raking", "haunted", "huts", "afraid", "volcano", "howls", "liar", "egotistic", "befit", "rounded", "older", "bluntly", "imbalance", "pivot", "exotic", "tuxedo", "amaze", "mostly", "lukewarm", "macro", "vocal", "hounded", "biplane", "rounded"].iter().map(|&s| s.to_string()).collect::>()); + } + + #[test] + fn key_derivation() { + // five saved himself oust taunts pebbles fibula organs koala copy dying vein damp dauntless code gags copy roster geek toolbox joyous apart unlikely warped taunts + let mnemonic = ["five", "saved", "himself", "oust", "taunts", "pebbles", "fibula", "organs", "koala", "copy", "dying", "vein", "damp", "dauntless", "code", "gags", "copy", "roster", "geek", "toolbox", "joyous", "apart", "unlikely", "warped", "taunts"].to_vec().iter().map(|s| s.to_string()).collect::>(); + let hex_seed = derive_hex_seed(mnemonic); + assert_eq!(hex_seed.clone(), "6bdaf7a0a8f3f1ce4767d6d9c38b72b48ccc3ffa4f60be91389b1b96403ff20e".to_string()); + let priv_keys = derive_priv_keys(hex_seed); + let priv_sk = &priv_keys[0]; + let priv_vk = &priv_keys[1]; + assert_eq!(priv_sk, &"6bdaf7a0a8f3f1ce4767d6d9c38b72b48ccc3ffa4f60be91389b1b96403ff20e".to_string()); + assert_eq!(priv_vk, &"490447bf98677377923b4da400fa2b7e6dff6dff0ca24f7ae533a8207fd27c00".to_string()); + let pub_sk = derive_pub_key(priv_sk.clone()); + assert_eq!(pub_sk.clone(), "03970285bf0724d75e0f50bca9a9ea0e8db5091b69403dc944465f8936bde787".to_string()); + let pub_vk = derive_pub_key(priv_keys[1].clone()); + assert_eq!(pub_vk.clone(), "528a736a5079dc9536edb5b6fa0a5209ce820b9734fc0785024670b3d3ba4c69".to_string()); + let addr = derive_address(pub_sk, pub_vk, 0); + assert_eq!(addr, "41kztevQ9HVd2LMni56Ka13SBt6k9qFH6afYGWyXfWnJPdoEE86mHddRxZxPtAwdZb2e8wsZdiFyxPFMTtaWp14PCxPF3wT".to_string()); + } + + #[cfg(test)] + #[allow(warnings)] + fn hashing_cn_slow() { + let input = b"This is a test"; + let output = cn_slow_hash(input); + assert_eq!( + output, + "a084f01d1437a09c6985401b60d43554ae105802c5f5d8a9b3253649c0be6605".to_string() + ); + } +} \ No newline at end of file