Merge #434
434: Introduce monero-wallet crate r=thomaseizinger a=thomaseizinger This PR: 1. ~Introduce a crate for the epee binary serialization as a serde format~: Released here: https://github.com/comit-network/monero-epee-bin-serde 2. Extends the MoneroRPC client with two binary calls 3. Introduces a `monero-wallet` crate that for now just provides functionality for choosing random key offsets. Together with the the ability to produce bulletproofs and ring signatures, this should be enough for signing Monero transactions locally. (1) and (2) are a prerequisite for (3). Co-authored-by: Thomas Eizinger <thomas@eizinger.io>pull/468/head
commit
e7785d2c83
@ -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" ] }
|
||||
|
@ -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" ] }
|
@ -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);
|
||||
}
|
||||
}
|
Loading…
Reference in new issue