Introduce monero-wallet crate

This is fairly bare-bones for now and only contains one piece of
functionality: choosing random key offsets.

More functionality for actually signing Monero transactions will
be added later.
This commit is contained in:
Thomas Eizinger 2021-04-29 13:44:35 +10:00
parent dd6bfd3bf4
commit 471baf7c49
No known key found for this signature in database
GPG Key ID: 651AC83A6C6C8B96
4 changed files with 128 additions and 1 deletions

15
Cargo.lock generated
View File

@ -2214,6 +2214,21 @@ dependencies = [
"tracing",
]
[[package]]
name = "monero-wallet"
version = "0.1.0"
dependencies = [
"anyhow",
"curve25519-dalek",
"monero",
"monero-harness",
"monero-rpc",
"rand 0.7.3",
"testcontainers 0.12.0",
"tokio",
"tracing-subscriber",
]
[[package]]
name = "multihash"
version = "0.13.2"

View File

@ -1,5 +1,5 @@
[workspace]
members = [ "monero-harness", "monero-rpc", "swap" ]
members = [ "monero-harness", "monero-rpc", "swap", "monero-wallet" ]
[patch.crates-io]
torut = { git = "https://github.com/bonomat/torut/", branch = "feature-flag-tor-secret-keys", default-features = false, features = [ "v3", "control" ] }

19
monero-wallet/Cargo.toml Normal file
View File

@ -0,0 +1,19 @@
[package]
name = "monero-wallet"
version = "0.1.0"
authors = [ "CoBloX Team <team@coblox.tech>" ]
edition = "2018"
[dependencies]
anyhow = "1"
monero = "0.12"
monero-rpc = { path = "../monero-rpc" }
rand = "0.7"
[dev-dependencies]
curve25519-dalek = "3"
monero-harness = { path = "../monero-harness" }
rand = "0.7"
testcontainers = "0.12"
tokio = { version = "1", features = [ "rt-multi-thread", "time", "macros", "sync", "process", "fs" ] }
tracing-subscriber = { version = "0.2", default-features = false, features = [ "fmt", "ansi", "env-filter", "chrono", "tracing-log" ] }

93
monero-wallet/src/lib.rs Normal file
View File

@ -0,0 +1,93 @@
use anyhow::{Context, Result};
use monero::consensus::encode::VarInt;
use monero::cryptonote::hash::Hashable;
use monero_rpc::monerod;
use monero_rpc::monerod::{GetBlockResponse, MonerodRpc as _};
use rand::Rng;
pub struct Wallet {
client: monerod::Client,
}
impl Wallet {
/// Chooses 10 random key offsets for use within a new confidential
/// transactions.
///
/// Choosing these offsets randomly is not ideal for privacy, instead they
/// should be chosen in a way that mimics a real spending pattern as much as
/// possible.
pub async fn choose_ten_random_key_offsets(&self) -> Result<[VarInt; 10]> {
let latest_block = self.client.get_block_count().await?;
let latest_spendable_block = latest_block.count - 10;
let block: GetBlockResponse = self.client.get_block(latest_spendable_block).await?;
let tx_hash = block
.blob
.tx_hashes
.first()
.copied()
.unwrap_or_else(|| block.blob.miner_tx.hash());
let indices = self.client.get_o_indexes(tx_hash).await?;
let last_index = indices
.o_indexes
.into_iter()
.max()
.context("Expected at least one output index")?;
let oldest_index = last_index - (last_index / 100) * 40; // oldest index must be within last 40% TODO: CONFIRM THIS
let mut rng = rand::thread_rng();
Ok([
VarInt(rng.gen_range(oldest_index, last_index)),
VarInt(rng.gen_range(oldest_index, last_index)),
VarInt(rng.gen_range(oldest_index, last_index)),
VarInt(rng.gen_range(oldest_index, last_index)),
VarInt(rng.gen_range(oldest_index, last_index)),
VarInt(rng.gen_range(oldest_index, last_index)),
VarInt(rng.gen_range(oldest_index, last_index)),
VarInt(rng.gen_range(oldest_index, last_index)),
VarInt(rng.gen_range(oldest_index, last_index)),
VarInt(rng.gen_range(oldest_index, last_index)),
])
}
}
#[cfg(test)]
mod tests {
use super::*;
use monero_harness::image::Monerod;
use monero_rpc::monerod::{Client, GetOutputsOut};
use testcontainers::clients::Cli;
use testcontainers::Docker;
#[tokio::test]
async fn get_outs_for_key_offsets() {
let cli = Cli::default();
let container = cli.run(Monerod::default());
let rpc_client = Client::localhost(container.get_host_port(18081).unwrap()).unwrap();
rpc_client.generateblocks(150, "498AVruCDWgP9Az9LjMm89VWjrBrSZ2W2K3HFBiyzzrRjUJWUcCVxvY1iitfuKoek2FdX6MKGAD9Qb1G1P8QgR5jPmmt3Vj".to_owned()).await.unwrap();
let wallet = Wallet {
client: rpc_client.clone(),
};
let key_offsets = wallet.choose_ten_random_key_offsets().await.unwrap();
let result = rpc_client
.get_outs(
key_offsets
.to_vec()
.into_iter()
.map(|varint| GetOutputsOut {
amount: 0,
index: varint.0,
})
.collect(),
)
.await
.unwrap();
assert_eq!(result.outs.len(), 10);
}
}